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:
@@ -334,7 +334,7 @@ describe('runOneShot and executeCli', () => {
|
||||
const { ctx, agent, persistenceRoot } = await harness([textResponse('final answer')])
|
||||
const output = await invoke(ctx, ['task'])
|
||||
expect(output).toEqual({ code: 0, stdout: 'final answer\n', stderr: '' })
|
||||
expect(agent.status).toBe('disposed')
|
||||
expect(agent.status).toBe('idle')
|
||||
const files = await readdir(persistenceRoot, { recursive: true })
|
||||
expect(files.some(file => file.endsWith('.jsonl.zstd'))).toBe(true)
|
||||
})
|
||||
@@ -387,18 +387,20 @@ describe('runOneShot and executeCli', () => {
|
||||
ctx.on('agent/inbox/enqueue', (subject) => {
|
||||
if (subject !== agent || injected) return
|
||||
injected = true
|
||||
agent.inject([{ type: 'text', text: 'startup injection' }], { source: { kind: 'plugin', plugin: 'test' } })
|
||||
agent.inject({ content: [{ type: 'text', text: 'startup injection' }], source: { kind: 'plugin', plugin: 'test' } })
|
||||
other.append('turn/start', { turn: 1, trigger: { kind: 'injection', source: { kind: 'plugin', plugin: 'test' } } })
|
||||
other.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
|
||||
})
|
||||
const output = await invoke(ctx, ['--output-format', 'stream-json', 'task'])
|
||||
const lines = output.stdout.trimEnd().split('\n').map(line => JSON.parse(line) as Record<string, unknown>)
|
||||
const events = lines.slice(0, -1).map(line => line['event'] as SessionEvent)
|
||||
expect(lines.at(-1)).toMatchObject({ type: 'result', success: true, turn: 2, result: 'streamed' })
|
||||
expect(events[0]).toMatchObject({ type: 'turn/start', data: { turn: 2, trigger: { kind: 'message' } } })
|
||||
expect(events.at(-1)).toMatchObject({ type: 'turn/end', data: { turn: 2 } })
|
||||
expect(lines.at(-1)).toMatchObject({ type: 'result', success: true, turn: 1, result: 'streamed' })
|
||||
expect(events[0]).toMatchObject({ type: 'turn/start', data: { turn: 1, trigger: { kind: 'message' } } })
|
||||
expect(events.at(-1)).toMatchObject({ type: 'turn/end', data: { turn: 1 } })
|
||||
expect(lines.slice(0, -1).every(line => line['sessionId'] === agent.session.id)).toBe(true)
|
||||
expect(events.some(event => event.type === 'user/message' && event.data.source.kind !== 'user')).toBe(false)
|
||||
expect(events.some(event => event.type === 'user/message'
|
||||
&& event.data.source.kind === 'plugin'
|
||||
&& event.data.source.plugin === 'test')).toBe(false)
|
||||
})
|
||||
|
||||
it('emits partial data and a diagnostic for non-completed turns', async () => {
|
||||
@@ -424,7 +426,7 @@ describe('runOneShot and executeCli', () => {
|
||||
expect(JSON.parse(output.stdout)).toMatchObject({ success: false, reason: { kind: 'aborted' } })
|
||||
expect(output.code).toBe(1)
|
||||
expect(output.stderr).toContain('turn 1 was aborted')
|
||||
expect(agent.status).toBe('disposed')
|
||||
expect(agent.status).toBe('idle')
|
||||
})
|
||||
|
||||
it('contains stream-writer failures, cancels, flushes, and returns the output error', async () => {
|
||||
@@ -459,7 +461,7 @@ describe('runOneShot and executeCli', () => {
|
||||
expect(output.code).toBe(1)
|
||||
expect(output.stdout).toBe('')
|
||||
expect(output.stderr).toContain('stdout closed')
|
||||
expect(final.agent.status).toBe('disposed')
|
||||
expect(final.agent.status).toBe('idle')
|
||||
|
||||
const disposal = await harness([textResponse('answer')])
|
||||
const disposalOutput = await invoke(disposal.ctx, ['task'], { failDispose: true })
|
||||
@@ -486,7 +488,7 @@ describe('runOneShot and executeCli', () => {
|
||||
startup.ctx.on('session/event', (session, event) => {
|
||||
if (session === startup.agent.session && event.type === 'assistant/chunk') started()
|
||||
})
|
||||
startup.agent.followup([{ type: 'text', text: 'first' }])
|
||||
startup.agent.followup({ content: [{ type: 'text', text: 'first' }], source: { kind: 'user' } })
|
||||
await running
|
||||
const startupAbort = new AbortController()
|
||||
const waiting = runOneShot(startup.ctx, { task: 'second', signal: startupAbort.signal })
|
||||
@@ -514,7 +516,6 @@ describe('formatTurnFailure', () => {
|
||||
[{ kind: 'error', step: 3, failure: { message: 'provider bad', code: 'SERVER' } }, 'failed at step 3: provider bad'],
|
||||
[{ kind: 'disposed' }, 'was disposed'],
|
||||
[{ kind: 'max-tokens' }, 'output-token limit'],
|
||||
[{ kind: 'rejected', reason: 'policy' }, 'was rejected: policy'],
|
||||
[{ kind: 'interrupted' }, 'persistence recovery'],
|
||||
]
|
||||
for (const [reason, expected] of cases) expect(formatTurnFailure(reason)).toContain(expected)
|
||||
|
||||
Reference in New Issue
Block a user