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:
Tianyi Cui
2026-07-21 23:39:03 +08:00
194 changed files with 3387 additions and 1222 deletions

View File

@@ -18,6 +18,8 @@ import {
WEB_SEARCH_MAX_RESULTS,
} from '@deepseek-ai/dsh-tool-web'
const testToolSignal = new AbortController().signal
const available = true
function searchProvider(result: WebSearchResult, isAvailable = available): WebSearchProvider {
@@ -39,7 +41,7 @@ async function mountTools(opts: {
if (opts.fetchProvider) ctx.web.registerFetchProvider(opts.fetchProvider)
const fiber = await ctx.plugin(ToolWeb, opts.config ?? {})
let counter = 0
const call = (name: string, args: unknown) => ctx.tools.execute({ callId: CallId(`call-${++counter}`), name, arguments: args })
const call = (name: string, args: unknown) => ctx.tools.execute({ signal: testToolSignal, callId: CallId(`call-${++counter}`), name, arguments: args })
return { ctx, fiber, call }
}
@@ -166,9 +168,9 @@ describe('tool-web registration', () => {
const names = ctx.tools.schemas().map(s => s.name)
expect(names).toContain('web_search')
expect(names).toContain('web_fetch')
expect(ctx.tools.executionMode({ callId: CallId('search-safe'), name: 'web_search', arguments: { query: 'q' } }))
expect(ctx.tools.executionMode({ signal: testToolSignal, callId: CallId('search-safe'), name: 'web_search', arguments: { query: 'q' } }))
.toEqual({ kind: 'parallel' })
expect(ctx.tools.executionMode({ callId: CallId('fetch-safe'), name: 'web_fetch', arguments: { url: 'https://a.test' } }))
expect(ctx.tools.executionMode({ signal: testToolSignal, callId: CallId('fetch-safe'), name: 'web_fetch', arguments: { url: 'https://a.test' } }))
.toEqual({ kind: 'parallel' })
await fiber.dispose()
expect(ctx.tools.schemas().map(s => s.name)).not.toContain('web_search')
@@ -281,7 +283,7 @@ describe('tool-web execution through the real registry', () => {
await fiber.dispose()
})
it('executes web_fetch with no caller signal (forwards undefined to the seam)', async () => {
it('forwards the required caller signal to web_fetch', async () => {
const seen: { signal?: AbortSignal | undefined; passedSignal?: boolean } = {}
const fetchProvider = {
id: 'stub-fetch',
@@ -293,8 +295,7 @@ describe('tool-web execution through the real registry', () => {
},
}
const { ctx, fiber } = await mountTools({ webConfig: { fetchProvider: 'stub-fetch' }, fetchProvider })
// No signal on the execution: the tool passes `undefined`.
const out = await ctx.tools.execute({ callId: CallId('fetch-2'), name: 'web_fetch', arguments: { url: 'https://a.test' } })
const out = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('fetch-2'), name: 'web_fetch', arguments: { url: 'https://a.test' } })
expect(out.isError).toBe(false)
expect(out.value).toEqual({
url: 'https://a.test',
@@ -302,8 +303,8 @@ describe('tool-web execution through the real registry', () => {
body: { kind: 'text', content: 'ok' },
truncated: false,
})
expect(seen.passedSignal).toBe(false)
expect(seen.signal).toBeUndefined()
expect(seen.passedSignal).toBe(true)
expect(seen.signal).toBe(testToolSignal)
await fiber.dispose()
})