feat(web): split context injection into producer-declared forms
Injected context reached the transcript as one anonymous shape whose expanded body was the whole message serialized as inline JSON, so the escaping collapsed the only readable part — the model-facing prose — into a single line. `MessageSource` gains an optional producer-declared `form`, a small semantic vocabulary of information shapes independent of `kind`: kind says who produced the context, form says what shape it is, so several producers may share one presentation. Two values ship. `instructions` (workspace-context) lists the reconciled files above their text and keeps the `<system-reminder>` framing verbatim, because the framing is part of what the model read. `catalog` moves dsh-tool-skill off the shared plugin kind onto a `skill-catalog` source carrying the published name/description entries, and the body lists those instead of re-parsing `<available_skills>` out of the prose. Catalog identity moves with it: the republish digest now covers the durable entries, deleting the text-slicing that recovered them from a logged message. Everything else renders the opaque body — the model-facing text with its real line breaks, then the remaining provenance as fields. That is the documented default, not a leftover: a resumed, forked, or foreign log must render whether or not its producer is mounted here, which is why the classification lives in the durable source rather than a client-side table keyed by producer.
This commit is contained in:
@@ -219,6 +219,7 @@ function workspaceChangeContext(scope: string, digest: string): UserMessage {
|
||||
content: [{ type: 'text', text: `instructions for ${scope}` }],
|
||||
source: {
|
||||
kind: 'workspace-instructions',
|
||||
form: 'instructions',
|
||||
changes: [{ action: 'set', scope, path: `${scope}/AGENTS.md`, digest }],
|
||||
},
|
||||
})
|
||||
@@ -974,6 +975,7 @@ describe('workspace context request injection', () => {
|
||||
role: 'user',
|
||||
source: {
|
||||
kind: 'workspace-instructions',
|
||||
form: 'instructions',
|
||||
baseline: true,
|
||||
changes: [{ action: 'set', scope: sk('.', 'AGENTS.md'), path: 'AGENTS.md' }],
|
||||
},
|
||||
@@ -1951,6 +1953,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
expect(workspaceContextOf(result)?.source).toMatchObject({ kind: 'workspace-instructions' })
|
||||
expect(workspaceContextOf(result)?.source).toMatchObject({
|
||||
kind: 'workspace-instructions',
|
||||
form: 'instructions',
|
||||
changes: [{
|
||||
action: 'set',
|
||||
scope: sk('pkg', 'AGENTS.md'),
|
||||
@@ -2262,6 +2265,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
expect(workspaceContextOf(changed)?.source).toMatchObject({
|
||||
kind: 'workspace-instructions',
|
||||
form: 'instructions',
|
||||
changes: [{ action: 'replace', scope: sk('pkg', 'AGENTS.md'), path: join('pkg', 'AGENTS.md') }],
|
||||
})
|
||||
expect(blocksText(workspaceContextOf(changed)?.content)).toBe([
|
||||
@@ -2487,6 +2491,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
expect(workspaceContextOf(removed)?.source).toEqual({
|
||||
kind: 'workspace-instructions',
|
||||
form: 'instructions',
|
||||
changes: [{ action: 'remove', scope: sk('pkg', 'AGENTS.md'), path: join('pkg', 'AGENTS.md') }],
|
||||
})
|
||||
expect(blocksText(workspaceContextOf(removed)?.content)).toBe([
|
||||
@@ -2887,6 +2892,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
],
|
||||
source: {
|
||||
kind: 'workspace-instructions',
|
||||
form: 'instructions',
|
||||
changes: [
|
||||
null,
|
||||
{ action: 'unknown', scope: 'pkg', path: join('pkg', 'AGENTS.md') },
|
||||
@@ -3073,6 +3079,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
expect(workspaceContextOf(result)?.source).toMatchObject({ kind: 'workspace-instructions' })
|
||||
expect(workspaceContextOf(result)?.source).toMatchObject({
|
||||
kind: 'workspace-instructions',
|
||||
form: 'instructions',
|
||||
changes: [{ action: 'set', scope: sk('pkg', 'AGENTS.md'), path: join('pkg', 'AGENTS.md') }],
|
||||
})
|
||||
expect(blocksText(workspaceContextOf(result)?.content)).toContain('nested package rule')
|
||||
@@ -3505,7 +3512,7 @@ describe('workspace context pending state', () => {
|
||||
// must not mint per-session pending state.
|
||||
expect(commitPendingInstructionContexts(agent, [createUserMessage({
|
||||
content: [],
|
||||
source: { kind: 'workspace-instructions', changes: [] },
|
||||
source: { kind: 'workspace-instructions', form: 'instructions', changes: [] },
|
||||
})], pending)).toEqual([])
|
||||
expect(pending.has(agent.session)).toBe(false)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user