Merge remote-tracking branch 'origin/master' into worktree/pr628-merge-20260727

# Conflicts:
#	.agents/notes/implemented/architecture/2026-06-21-bounded-llm-request-recovery.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-21-bounded-llm-request-recovery.md
#	.agents/notes/implemented/architecture/2026-06-21-bounded-llm-request-recovery.zh.md
#	docs/architecture.i18n.yaml
#	docs/architecture.md
#	docs/architecture.zh.md
#	docs/cordis-catalog/events.md
#	docs/core-data-structures/llm-streaming.i18n.yaml
#	docs/core-data-structures/llm-streaming.md
#	docs/core-data-structures/llm-streaming.zh.md
#	docs/event-producer-consumer.md
#	examples/acp-agent/tests/snapshots/empty-response-retry/session.jsonl
#	packages/compact/compact-basic/src/index.ts
#	packages/compact/compact-basic/tests/compact-basic.spec.ts
#	packages/cordis/tool-cordis/src/api-catalog.ts
#	packages/core/agent-loop/README.i18n.yaml
#	packages/core/agent-loop/README.md
#	packages/core/agent-loop/README.zh.md
#	packages/core/agent-loop/src/loop.ts
#	packages/core/agent-loop/tests/request-recovery.spec.ts
#	packages/core/agent/src/types.ts
#	packages/core/scope/tests/invariant.spec.ts
#	packages/llm/llm-retry/README.i18n.yaml
#	packages/llm/llm-retry/README.md
#	packages/llm/llm-retry/README.zh.md
#	packages/llm/llm-retry/src/index.ts
#	packages/llm/llm-retry/src/invariant.ts
#	packages/llm/llm-retry/tests/invariant.spec.ts
#	packages/llm/llm-retry/tests/retry.spec.ts
#	packages/plan/plan-mode/src/index.ts
#	packages/plan/plan-mode/tests/integration.spec.ts
#	packages/plan/plan-mode/tests/plan-mode.spec.ts
This commit is contained in:
Tianyi Cui
2026-07-27 23:29:26 +08:00
461 changed files with 8294 additions and 10405 deletions
@@ -35,12 +35,9 @@ declare module '@deepseek-ai/dsh-tasks' {
}
async function composePrefix(ctx: Context, cwd: string): Promise<Message[]> {
const agent = { session: { header: { cwd } } } as unknown as Agent
const empty: Message[] = []
return await agentEvents(ctx, agent).waterfall(
'agent/session-prefix', empty, new AbortController().signal,
() => Promise.resolve(empty),
)
const agent = ctx.agentLoop.create(SessionId('agent-spine-prefix'), {}, { cwd })
await agentEvents(ctx, agent).serial('agent/step', 1, 1, new AbortController().signal)
return agent.session.deriveMessages()
}
/**
@@ -108,15 +105,8 @@ async function withIsolatedSkillHomes<T>(run: () => Promise<T>): Promise<T> {
}
}
function waitForIdle(ctx: Context, target: Agent): Promise<void> {
return new Promise((resolve) => {
const dispose = ctx.on('agent/status', (agent, status) => {
if (agent === target && status === 'idle') {
dispose()
resolve()
}
})
})
function waitForIdle(_ctx: Context, target: Agent): Promise<void> {
return target.whenIdle()
}
function messageText(message: Message | undefined): string {
@@ -245,7 +235,7 @@ describe('dsh-agent-spine-demo bundle', () => {
agentOptions: { provider: 'mock', model: 'mock' },
})
handle.agent.followup([{ type: 'text', text: 'recover' }])
handle.agent.followup({ content: [{ type: 'text', text: 'recover' }], source: { kind: 'user' } })
await waitForIdle(ctx, handle.agent)
expect(adapter.requests).toBe(2)
@@ -345,7 +335,7 @@ describe('dsh-agent-spine-demo bundle', () => {
})
const agent = handle.agent
agent.followup([{ type: 'text', text: 'hi' }])
agent.followup({ content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' } })
await waitForIdle(ctx, agent)
const sentText = adapter.requests[0]?.messages.map(messageText).join('\n')
@@ -374,7 +364,7 @@ describe('dsh-agent-spine-demo bundle', () => {
agentOptions: { provider: 'mock', model: 'mock' },
})
handle.agent.followup([{ type: 'text', text: 'hi' }])
handle.agent.followup({ content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' } })
await waitForIdle(ctx, handle.agent)
expect(adapter.requests[0]?.messages).toEqual([{ role: 'user', content: [{ type: 'text', text: 'hi' }] }])
@@ -464,11 +454,11 @@ describe('dsh-agent-spine-demo bundle', () => {
agentOptions: { provider: 'mock', model: 'mock' },
})
handle.agent.followup([{ type: 'text', text: 'hi' }])
handle.agent.followup({ content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' } })
await waitForIdle(ctx, handle.agent)
expect(messageText(adapter.requests[0]?.messages[0])).toContain('workspace rule before skills')
expect(messageText(adapter.requests[0]?.messages[1])).toContain('prefix-order-skill')
expect(messageText(adapter.requests[0]?.messages[1])).toContain('workspace rule before skills')
expect(messageText(adapter.requests[0]?.messages[2])).toContain('prefix-order-skill')
await handle.dispose()
await ctx.fiber.dispose()
} finally {