Merge remote-tracking branch 'origin/master' into worktree/context-source-cards

# Conflicts:
#	.agents/notes/implemented/simplification/2026-07-31-web-ui-no-steer-entry-or-interjection-chrome.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-31-web-ui-no-steer-entry-or-interjection-chrome.md
#	.agents/notes/implemented/simplification/2026-07-31-web-ui-no-steer-entry-or-interjection-chrome.zh.md
#	apps/web/tests/seeded-history.e2e.ts
#	apps/web/tests/snapshots/queue-actions/layout.expected.md
#	docs/core-data-structures/core.i18n.yaml
#	examples/acp-agent/tests/snapshots/code-mode-workspace-context/session.jsonl
#	examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl
#	examples/acp-agent/tests/snapshots/skill-load/session.jsonl
#	examples/acp-agent/tests/snapshots/workspace-context/session.jsonl
#	examples/headless-agent/tests/snapshots/advanced-toolchain/session.1.jsonl
#	examples/headless-agent/tests/snapshots/advanced-toolchain/session.2.jsonl
#	examples/headless-agent/tests/snapshots/advanced-toolchain/session.jsonl
#	examples/headless-agent/tests/snapshots/pty-tools/session.jsonl
#	packages/client/runtime/README.i18n.yaml
#	packages/client/runtime/tests/history-fold.spec.ts
#	packages/client/ui-conversation/README.i18n.yaml
#	packages/client/ui-conversation/src/client/chat/MessageItem.tsx
#	packages/client/ui-conversation/tests/chat-branch-tails.spec.tsx
#	packages/context/workspace-context/src/index.ts
#	packages/context/workspace-context/tests/workspace-context.spec.ts
#	packages/skill/tool-skill/README.i18n.yaml
#	packages/skill/tool-skill/README.md
#	packages/skill/tool-skill/README.zh.md
#	packages/skill/tool-skill/src/index.ts
#	packages/skill/tool-skill/tests/tool-skill.spec.ts
This commit is contained in:
creatixchu
2026-08-06 11:28:48 +08:00
823 changed files with 18562 additions and 14742 deletions
@@ -39,7 +39,16 @@ declare module '@deepseek-ai/dsh-tasks' {
async function composePrefix(ctx: Context, cwd: string): Promise<Message[]> {
const agent = ctx.agentLoop.create(SessionId('agent-spine-prefix'), {}, { cwd })
await agentEvents(ctx, agent).serial('agent/step', 1, 1, new AbortController().signal)
const signal = new AbortController().signal
const decision = await agentEvents(ctx, agent).waterfall(
'agent/pre-step', [], { turn: 1, step: 1, signal },
() => Promise.resolve({ kind: 'enter', messages: [] }),
)
if (decision.kind === 'enter') {
for (const message of decision.messages) {
agent.session.append('user/message', message, { surfaceOp: 'append' })
}
}
return agent.session.deriveMessages()
}
@@ -166,7 +175,6 @@ describe('dsh-agent-spine-demo bundle', () => {
const session = ctx.sessions.create(SessionId('configured-title-limits'))
session.append('turn/start', {
turn: 1,
trigger: { kind: 'message', source: { kind: 'user' } },
})
session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'One two three four' }],
@@ -209,8 +217,8 @@ describe('dsh-agent-spine-demo bundle', () => {
it('mounts package companions and forwards invariant selection config', async () => {
const nestedTurn = (ctx: Context): void => {
const session = ctx.sessions.create()
session.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } })
session.append('turn/start', { turn: 2, trigger: { kind: 'message', source: { kind: 'user' } } })
session.append('turn/start', { turn: 1 })
session.append('turn/start', { turn: 2 })
}
const enabled = await mount({ workspaceContext: false })
@@ -327,7 +335,7 @@ describe('dsh-agent-spine-demo bundle', () => {
try {
await mkdir(join(root, '.git'), { recursive: true })
await writeFile(join(root, 'AGENTS.md'), 'bundled project rule')
const adapter = new MockAdapter([textResponse('ok')])
const adapter = new MockAdapter([textResponse('first')])
const ctx = await mount({ workspaceContext: { maxBytes: 65536 } })
await ctx.plugin(LocalFileSystem, { cwd: '/' })
ctx.llm.registerAdapter(['mock'], adapter)
@@ -341,9 +349,10 @@ describe('dsh-agent-spine-demo bundle', () => {
agent.followup(createUserMessage({ content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' } }))
await waitForIdle(ctx, agent)
const sentText = adapter.requests[0]?.messages.map(messageText).join('\n')
expect(sentText).toContain('hi')
expect(sentText).toContain('bundled project rule')
expect(adapter.requests).toHaveLength(1)
const firstRequestText = adapter.requests[0]?.messages.map(messageText).join('\n')
expect(firstRequestText).toContain('hi')
expect(firstRequestText).toContain('bundled project rule')
expect(adapter.requests[0]?.system).toContain('You are an AI agent powered by the DeepSeek Harness SDK.')
expect(adapter.requests[0]?.system).not.toContain('bundled project rule')
await handle.dispose()
@@ -581,12 +590,12 @@ describe('dsh-agent-spine-demo bundle', () => {
}).toThrow('agent-spine-demo: dshHome and skills.local.dshHome must resolve to the same directory')
})
it('places workspace instructions before the skill catalog in the session prefix', async () => {
it('delivers workspace instructions ahead of the first-step skill catalog', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-agent-spine-demo-prefix-order-'))
try {
await mkdir(join(root, '.git'), { recursive: true })
await writeFile(join(root, 'AGENTS.md'), 'workspace rule before skills')
const adapter = new MockAdapter([textResponse('ok')])
const adapter = new MockAdapter([textResponse('first')])
const ctx = await mount({ workspaceContext: { maxBytes: 65536 } })
await ctx.plugin(LocalFileSystem, { cwd: '/' })
ctx.llm.registerAdapter(['mock'], adapter)
@@ -605,8 +614,16 @@ describe('dsh-agent-spine-demo bundle', () => {
handle.agent.followup(createUserMessage({ content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' } }))
await waitForIdle(ctx, handle.agent)
expect(messageText(adapter.requests[0]?.messages[1])).toContain('workspace rule before skills')
expect(messageText(adapter.requests[0]?.messages[2])).toContain('prefix-order-skill')
expect(adapter.requests).toHaveLength(1)
const workspaceIndex = adapter.requests[0]!.messages.findIndex(
message => messageText(message).includes('workspace rule before skills'),
)
const catalogIndex = adapter.requests[0]!.messages.findIndex(
message => messageText(message).includes('prefix-order-skill'),
)
expect(workspaceIndex).toBeGreaterThanOrEqual(0)
expect(catalogIndex).toBeGreaterThanOrEqual(0)
expect(workspaceIndex).toBeLessThan(catalogIndex)
await handle.dispose()
await ctx.fiber.dispose()
} finally {