skills: render catalog locators by authority (#27591)

## Why

Hosted skills introduced by #27388 use opaque `skill://` resource
identifiers, but the skills catalog rendered every locator as a `file`
and told the model that every skill body lived on disk. That can send
the model toward filesystem tools for a resource that must instead be
read through its owning authority.

The catalog should describe how each source is accessed without changing
the underlying discovery or invocation behavior.

## What changed

- Render host skills as `file`, executor-owned skills as `environment
resource`, orchestrator-owned skills as `orchestrator resource`, and
custom-provider skills as `custom resource`.
- Update the shared no-alias guidance to describe source locators rather
than assuming every skill is stored on the host filesystem.
- Direct orchestrator resources through `skills.list` and `skills.read`,
and explicitly tell the model not to treat `skill://` identifiers as
filesystem paths.
- Preserve the existing filesystem and alias behavior for local skills.

## Scope

This PR changes only model-visible catalog rendering and guidance. It
does not change skill discovery, selection, prompt injection, provider
routing, catalog caching or refresh behavior, resource validation, or
the `skills.*` tool contract.

## Verification

- Extended skills-extension coverage for host-file and executor-resource
labels.
- Extended the no-executor app-server flow to assert
orchestrator-resource wording and non-filesystem guidance.
This commit is contained in:
jif
2026-06-11 13:51:04 +02:00
committed by GitHub
parent dd46ef1aac
commit 4322e01d2b
4 changed files with 40 additions and 13 deletions
@@ -217,7 +217,9 @@ async fn orchestrator_skill_can_read_referenced_resource_without_an_executor() -
assert!(first_request.tool_by_name("skills", "search").is_none());
let developer_messages = first_request.message_input_texts("developer");
let catalog_line = format!("- {SKILL_NAME}: {SKILL_DESCRIPTION} (file: {SKILL_RESOURCE_URI})");
let catalog_line = format!(
"- {SKILL_NAME}: {SKILL_DESCRIPTION} (orchestrator resource: {SKILL_RESOURCE_URI})"
);
assert_eq!(
1,
developer_messages
@@ -230,6 +232,11 @@ async fn orchestrator_skill_can_read_referenced_resource_without_an_executor() -
.iter()
.all(|text| !text.contains("ignored-plugin:ignored"))
);
assert!(
developer_messages
.iter()
.any(|text| text.contains("do not treat `skill://` identifiers as filesystem paths"))
);
let skill_fragments = first_request
.message_input_texts("user")
.into_iter()