Merge branch 'codex/tool-json-schema-dsl' into codex/canonical-tool-output
# Conflicts: # .agents/notes/implemented/feature/2026-06-30-interception-seams.md # docs/config-catalog.md # docs/cookbook/adding-a-tool.i18n.yaml # docs/cookbook/adding-a-tool.md # docs/cookbook/adding-a-tool.zh.md # docs/cordis-catalog/events.md # docs/cordis-catalog/services.md # docs/core-data-structures/tools.md # docs/event-producer-consumer.md # docs/persistence-catalog.md # examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl # examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/stdout.expected.jsonl # packages/bash/tool-bash/src/index.ts # packages/core/agent-loop/src/tool-calls.ts # packages/core/agent-loop/tests/cancel.spec.ts # packages/core/agent-loop/tests/contract-regressions.spec.ts # packages/core/agent-loop/tests/tool-calls.spec.ts # packages/core/tools/README.md # packages/core/tools/src/index.ts # packages/core/tools/tests/code-mode.spec.ts # packages/core/tools/tests/tools.spec.ts # packages/fs/tool-fs-search/tests/integration.spec.ts # packages/fs/tool-fs-search/tests/tools.spec.ts # packages/fs/tool-fs/tests/integration.spec.ts # packages/mcp/mcp-client/src/tools.ts # packages/timeout/timeout-policy/tests/timeout-policy.spec.ts # packages/web/tool-web/tests/integration.spec.ts # packages/web/tool-web/tests/tool-web.spec.ts
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
import { Context } from 'cordis'
|
||||
import type { Terminal } from '@earendil-works/pi-tui'
|
||||
import AgentRegistry, { type Agent, type AgentOptions, type AgentStatus } from '@deepseek-ai/dsh-agent'
|
||||
import AgentRegistry, {
|
||||
type Agent,
|
||||
type AgentCancelCause,
|
||||
type AgentOptions,
|
||||
type AgentStatus,
|
||||
} from '@deepseek-ai/dsh-agent'
|
||||
import type { ContentBlock, LlmModelContext, LlmModelInfo, LlmProviderInfo } from '@deepseek-ai/dsh-llm'
|
||||
import CommandService from '@deepseek-ai/dsh-commands'
|
||||
import SessionStore, { SessionId, type Session } from '@deepseek-ai/dsh-session'
|
||||
@@ -13,7 +18,7 @@ interface FakeAgent extends Agent {
|
||||
status: AgentStatus
|
||||
sent: ContentBlock[][]
|
||||
steered: ContentBlock[][]
|
||||
cancelled: string[]
|
||||
cancelled: AgentCancelCause[]
|
||||
}
|
||||
|
||||
export interface TuiHarnessOptions {
|
||||
@@ -110,7 +115,7 @@ export async function createTuiTestHarness<TerminalType extends Terminal, Exit e
|
||||
options.beforeMount?.(session)
|
||||
const sent: ContentBlock[][] = []
|
||||
const steered: ContentBlock[][] = []
|
||||
const cancelled: string[] = []
|
||||
const cancelled: AgentCancelCause[] = []
|
||||
const agent: FakeAgent = {
|
||||
id: sessionId,
|
||||
options: options.agentOptions ?? { provider: 'deepseek', model: 'deepseek-v4-flash' },
|
||||
@@ -127,8 +132,8 @@ export async function createTuiTestHarness<TerminalType extends Terminal, Exit e
|
||||
steered.push(content)
|
||||
},
|
||||
inject() {},
|
||||
cancel(reason) {
|
||||
cancelled.push(reason ?? '')
|
||||
cancel(cause = { kind: 'user' }) {
|
||||
cancelled.push(cause)
|
||||
},
|
||||
whenIdle() {
|
||||
return Promise.resolve()
|
||||
|
||||
@@ -34,8 +34,8 @@ buffer
|
||||
11| " Retrying model request (1/2) in 1000ms: temporary transport failure "
|
||||
style 1-67 fg=yellow
|
||||
12| <blank>
|
||||
13| " cancelled during retry delay "
|
||||
style 1-28 fg=yellow
|
||||
13| " Turn cancelled. "
|
||||
style 1-15 fg=yellow
|
||||
14| "────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-95 dim
|
||||
15| " "
|
||||
|
||||
@@ -280,7 +280,7 @@ describe('TUI terminal-state snapshots', () => {
|
||||
})
|
||||
harness.session.append('turn/end', {
|
||||
turn: 1,
|
||||
reason: { kind: 'aborted', reason: 'cancelled during retry delay' },
|
||||
reason: { kind: 'aborted' },
|
||||
})
|
||||
})
|
||||
await checkpoint('retry-cancelled', harness.terminal, { includeScrollback: true })
|
||||
|
||||
@@ -500,7 +500,7 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
result.terminal.send('\x0f')
|
||||
result.terminal.send('/cancel')
|
||||
result.terminal.send('\r')
|
||||
expect(result.agent.cancelled).toContain('cancelled from terminal')
|
||||
expect(result.agent.cancelled).toContainEqual({ kind: 'user' })
|
||||
|
||||
result.agent.status = 'idle'
|
||||
for (const command of ['/help', '/reasoning', '/tools', '/redraw']) {
|
||||
@@ -605,7 +605,7 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
expect(assembly.variables).toMatchObject({ provider: 'beta', model: 'b1' })
|
||||
const seed: LlmCallConfig = { provider: 'alpha', model: 'a1', temperature: 0.2 }
|
||||
const request = await agentEvents(result.ctx, result.agent).waterfall(
|
||||
'agent/request', 1, 0, seed, () => Promise.resolve(seed),
|
||||
'agent/request', 1, 0, seed, new AbortController().signal, () => Promise.resolve(seed),
|
||||
)
|
||||
expect(request).toEqual({ provider: 'beta', model: 'b1', temperature: 0.2 })
|
||||
await dispose(result)
|
||||
@@ -657,7 +657,7 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
expect(assembly.variables).toEqual({})
|
||||
const seed: LlmCallConfig = { provider: 'fallback', model: 'fallback' }
|
||||
await expect(agentEvents(empty.ctx, empty.agent).waterfall(
|
||||
'agent/request', 1, 0, seed, () => Promise.resolve(seed),
|
||||
'agent/request', 1, 0, seed, new AbortController().signal, () => Promise.resolve(seed),
|
||||
)).resolves.toBe(seed)
|
||||
await dispose(empty)
|
||||
|
||||
@@ -833,7 +833,7 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
result.terminal.send('/exit')
|
||||
result.terminal.send('\r')
|
||||
await tick()
|
||||
expect(result.agent.cancelled).toContain('terminal exit requested')
|
||||
expect(result.agent.cancelled).toContainEqual({ kind: 'user' })
|
||||
expect(result.exit).toHaveBeenCalledWith(0)
|
||||
|
||||
const events = await setup()
|
||||
@@ -850,7 +850,7 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
events.session.append('turn/start', { turn: 2, trigger: { kind: 'message', source: { kind: 'user' } } })
|
||||
events.session.append('turn/end', { turn: 2, reason: { kind: 'error', step: 1, message: 'durable failure' } })
|
||||
events.session.append('turn/start', { turn: 3, trigger: { kind: 'message', source: { kind: 'user' } } })
|
||||
events.session.append('turn/end', { turn: 3, reason: { kind: 'aborted', reason: 'stopped' } })
|
||||
events.session.append('turn/end', { turn: 3, reason: { kind: 'aborted' } })
|
||||
events.session.append('turn/start', { turn: 4, trigger: { kind: 'message', source: { kind: 'user' } } })
|
||||
events.session.append('turn/end', { turn: 4, reason: { kind: 'max-tokens' } })
|
||||
events.session.append('turn/start', { turn: 5, trigger: { kind: 'message', source: { kind: 'user' } } })
|
||||
@@ -866,8 +866,8 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
await tick()
|
||||
expect(events.terminal.output).toContain('live failure')
|
||||
expect(events.terminal.output).toContain('durable failure')
|
||||
expect(events.terminal.output).toContain('Turn cancelled')
|
||||
expect(events.terminal.output).toContain('structured provider failure')
|
||||
expect(events.terminal.output).toContain('stopped')
|
||||
expect(events.terminal.output).toContain('output-token limit')
|
||||
expect(events.terminal.output).toContain('Turn rejected')
|
||||
expect(events.terminal.output).toContain('previous process ended')
|
||||
|
||||
Reference in New Issue
Block a user