refactor(agent): unify sourced message delivery

This commit is contained in:
_Kerman
2026-07-24 22:38:50 +08:00
parent 009d113e0e
commit 992cf894af
197 changed files with 1890 additions and 2132 deletions

View File

@@ -4,11 +4,11 @@ The model-facing skill catalog and `skill` tool.
Requires `ctx.tools` and `ctx.skills` (`inject: ['tools', 'skills']`).
## Session-prefix catalog
## Session catalog
The plugin contributes one user-role `<system-reminder>` catalog through `agent/session-prefix`. It resolves skills for the calling session's cwd, forwards the prefix abort signal to discovery, and lists only sorted `name` and `description` entries; skill bodies, paths, sources, providers, and `whenToUse` hints remain outside the catalog. The catalog is omitted when no model-invocable skills are available, and also when that agent's tool view restricts away the shipped `skill` tool or resolves a same-name scoped shadow instead. This exact-definition check keeps prompt guidance, the model-visible schema, and executable dispatch aligned.
The plugin injects one durable user-role `<system-reminder>` catalog at the first `agent/step` of a live session. It resolves skills for the calling session's cwd, forwards the step abort signal to discovery, and lists only sorted `name` and `description` entries; skill bodies, paths, sources, providers, and `whenToUse` hints remain outside the catalog. The catalog is omitted when no model-invocable skills are available, and also when that agent's tool view restricts away the shipped `skill` tool or resolves a same-name scoped shadow instead. This exact-definition check keeps prompt guidance, the model-visible schema, and executable dispatch aligned.
`catalogDescriptionMaxLength` controls normalized, XML-escaped catalog descriptions. Its default is `500` and values must be integers of at least `3`, which reserves room for a truncation ellipsis. The [session-prefix Agent Note](../../../.agents/notes/implemented/feature/2026-07-07-session-prefix.md) defines the request-only, header-logged lifecycle of this message.
`catalogDescriptionMaxLength` controls normalized, XML-escaped catalog descriptions. Its default is `500` and values must be integers of at least `3`, which reserves room for a truncation ellipsis. The catalog is a sourced `user/message` injected before the first request and retained in ordinary session history.
## Tool: `skill`
@@ -26,11 +26,11 @@ The tool does not call `agent.inject()` in v1. Its result is already recorded as
## Model Experience
### Session prefix
### Session catalog
#### What the model sees
If model-invocable skills exist and this exact `skill` tool is visible, the agent receives the catalog template below, with one data-dependent entry per sorted skill. The catalog is a frozen user-role session prefix.
If model-invocable skills exist and this exact `skill` tool is visible, the agent receives the catalog template below, with one data-dependent entry per sorted skill. The catalog is one durable user-role message.
##### Skill catalog template
@@ -52,7 +52,7 @@ Repeated input cost scales with skill count and `catalogDescriptionMaxLength`; n
#### KV Cache effect
Prefix-stable within a loop instance once the session prefix is composed. A new or resumed instance with different providers, skills, descriptions, visibility, or catalog limits may invalidate reuse from the first changed catalog token.
Append-only after the existing reusable prefix. A new or resumed instance with different providers, skills, descriptions, visibility, or catalog limits may affect cache reuse from the newly appended catalog position.
### Tool schema

View File

@@ -29,7 +29,7 @@ export const Config: z<Config> = z.object({
/**
* Register the model-facing skill loader and its visibility-matched
* session-prefix catalog. The catalog is emitted only when the calling agent
* durable session catalog. The catalog is emitted only when the calling agent
* resolves this plugin's exact tool registration; a restriction or scoped
* same-name shadow therefore removes both the schema and its call guidance.
*/
@@ -126,7 +126,7 @@ export function apply(ctx: Context, config: Config = {}): void {
const skills = await ctx.skills.list({ cwd: agent.session.header.cwd, signal })
if (skills.length > 0) {
const catalog = renderCatalogMessage(skills, catalogDescriptionMaxLength)
agent.inject(catalog.content, { source: { kind: 'plugin', plugin: 'dsh-tool-skill' } })
agent.inject({ content: catalog.content, source: { kind: 'plugin', plugin: 'dsh-tool-skill' } })
}
catalogLoaded.add(agent.session)
})

View File

@@ -48,11 +48,8 @@ function agentForCwd(cwd: string): Agent {
send: () => AgentMessageId('stub'),
followup: () => AgentMessageId('stub'),
steer: () => AgentMessageId('stub'),
inject(content, options) {
session.append('user/message', {
content,
source: options?.source ?? { kind: 'user' },
}, { surfaceOp: 'append' })
inject(input) {
session.append('user/message', input, { surfaceOp: 'append' })
return AgentMessageId('stub')
},
cancel() {},
@@ -147,9 +144,7 @@ describe('dsh-tool-skill', () => {
content: 'A body.',
})
ctx.on('agent/step', (agent) => {
agent.inject([{ type: 'text', text: 'later contribution' }], {
source: { kind: 'plugin', plugin: 'later-contribution' },
})
agent.inject({ content: [{ type: 'text', text: 'later contribution' }], source: { kind: 'plugin', plugin: 'later-contribution' } })
})
const prefix = await composePrefix(ctx, '/workspace')