Merge remote-tracking branch 'origin/master' into codex/project-instruction-files
# Conflicts: # docs/config-catalog.md # docs/cordis-catalog/events.md # docs/cordis-catalog/services.md # docs/core-data-structures/core.md # docs/event-producer-consumer.md # docs/persistence-catalog.md # docs/rfc/implemented/feature/2026-06-15-code-mode.md # docs/rfc/implemented/feature/2026-06-30-interception-seams.md # docs/rfc/proposed/simplification/2026-07-04-prune-dead-core-spine-surface.md # docs/tool-execution-pipeline.md # packages/cordis/tool-cordis/src/api-catalog.ts # packages/core/agent-core/tests/agent-core.spec.ts # packages/core/agent-loop/src/agent.ts # packages/core/agent/README.md # packages/core/session/src/index.ts # packages/core/tools/README.md # packages/core/tools/src/index.ts # pnpm-lock.yaml # scripts/gen-doc-graphs.ts # scripts/type-equiv.manifest.json
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { createScope } from '@deepseek-ai/dsh-scope'
|
||||
import type { Scope } from '@deepseek-ai/dsh-scope'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import { CodeRuntime } from '@deepseek-ai/dsh-code-runtime'
|
||||
import type { CodeRunRequest, CodeRunResult } from '@deepseek-ai/dsh-code-runtime'
|
||||
import ToolRegistry, { CodeRunFailedError, RUN_CODE_NAME, defineTool } from '@deepseek-ai/dsh-tools'
|
||||
import type { Config, PostToolDecision, ToolExecutionResult } from '@deepseek-ai/dsh-tools'
|
||||
import { AgentId } from '@deepseek-ai/dsh-agent'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type { SessionEventMap } from '@deepseek-ai/dsh-session'
|
||||
@@ -54,6 +57,15 @@ async function setup(options: SetupOptions = {}) {
|
||||
return { ctx, tools: ctx.tools, systemPrompt: ctx.systemPrompt, runtime: runtime! }
|
||||
}
|
||||
|
||||
/** Mint one production-shaped agent scope that can register scoped tool policy. */
|
||||
async function mintAgentScope(ctx: Context, name = 'scoped'): Promise<{ scope: Scope; agent: Agent }> {
|
||||
const agent = { id: AgentId(name) } as Agent
|
||||
let scope!: Scope
|
||||
await ctx.plugin(Object.assign((inner: Context) => { scope = createScope(inner, agent) },
|
||||
{ inject: ['tools', 'systemPrompt'] }))
|
||||
return { scope, agent }
|
||||
}
|
||||
|
||||
/** Register a trivial echo tool; returns the calls it received. */
|
||||
function registerEcho(ctx: Context, name = 'echo'): unknown[] {
|
||||
const calls: unknown[] = []
|
||||
@@ -112,6 +124,35 @@ describe('mode-aware wire contribution', () => {
|
||||
expect(sdk?.text).not.toContain('run_code(args:')
|
||||
})
|
||||
|
||||
it.each(['code', 'both'] as const)('treats expert assembly output as authoritative in mode %s', async (mode) => {
|
||||
const { ctx, systemPrompt } = await setup({ mode })
|
||||
registerEcho(ctx)
|
||||
ctx.on('system-prompt/assemble', async (_assembly, _context, next) => {
|
||||
const assembly = await next()
|
||||
return {
|
||||
...assembly,
|
||||
sections: assembly.sections.filter(section => section.name !== 'tools:sdk'),
|
||||
tools: assembly.tools.filter(tool => tool.name !== RUN_CODE_NAME),
|
||||
}
|
||||
}, { prepend: true })
|
||||
|
||||
const assembly = await systemPrompt.assemble()
|
||||
expect(assembly.sections.some(section => section.name === 'tools:sdk')).toBe(false)
|
||||
expect(assembly.tools.some(tool => tool.name === RUN_CODE_NAME)).toBe(false)
|
||||
})
|
||||
|
||||
it.each(['code', 'both'] as const)('lets one scope shadow the default SDK section in mode %s', async (mode) => {
|
||||
const { ctx, systemPrompt } = await setup({ mode })
|
||||
registerEcho(ctx)
|
||||
const { scope, agent } = await mintAgentScope(ctx)
|
||||
scope.ctx.systemPrompt.section({ name: 'tools:sdk', order: 150, text: 'SCOPED SDK' })
|
||||
|
||||
const scoped = await systemPrompt.assemble({ scope: agent })
|
||||
const global = await systemPrompt.assemble()
|
||||
expect(scoped.sections.find(section => section.name === 'tools:sdk')?.text).toBe('SCOPED SDK')
|
||||
expect(global.sections.find(section => section.name === 'tools:sdk')?.text).toContain('declare const tools:')
|
||||
})
|
||||
|
||||
it("mode 'both' contributes every native schema plus run_code, and the SDK section", async () => {
|
||||
const { ctx, systemPrompt } = await setup({ mode: 'both' })
|
||||
registerEcho(ctx)
|
||||
@@ -120,6 +161,107 @@ describe('mode-aware wire contribution', () => {
|
||||
expect(assembly.sections.some(section => section.name === 'tools:sdk')).toBe(true)
|
||||
})
|
||||
|
||||
it.each(['code', 'both'] as const)('keeps the run_code transport outside scoped allow-list filtering in mode %s', async (mode) => {
|
||||
const { ctx, systemPrompt, runtime } = await setup({ mode })
|
||||
registerEcho(ctx, 'echo')
|
||||
registerEcho(ctx, 'hidden')
|
||||
const { scope, agent } = await mintAgentScope(ctx)
|
||||
const lift = scope.ctx.tools.restrict({ allow: ['echo'] })
|
||||
|
||||
const assembly = await systemPrompt.assemble({ scope: agent })
|
||||
expect(assembly.tools.map(tool => tool.name)).toEqual(mode === 'code'
|
||||
? [RUN_CODE_NAME]
|
||||
: ['echo', RUN_CODE_NAME])
|
||||
const sdk = assembly.sections.find(section => section.name === 'tools:sdk')?.text
|
||||
expect(sdk).toContain('echo(args:')
|
||||
expect(sdk).not.toContain('hidden(args:')
|
||||
|
||||
runtime.behavior = request => Promise.resolve({
|
||||
logs: [],
|
||||
value: Object.keys(request.bindings[0]!.functions).sort().join(','),
|
||||
})
|
||||
const result = await runCode(ctx, 'return Object.keys(tools)', { agent })
|
||||
expect(result.isError).toBe(false)
|
||||
expect(result.content).toEqual([{ type: 'text', text: 'echo' }])
|
||||
|
||||
lift()
|
||||
const unrestricted = await systemPrompt.assemble({ scope: agent })
|
||||
expect(unrestricted.tools.map(tool => tool.name)).toEqual(mode === 'code'
|
||||
? [RUN_CODE_NAME]
|
||||
: ['echo', 'hidden', RUN_CODE_NAME])
|
||||
})
|
||||
|
||||
it.each(['code', 'both'] as const)('keeps the run_code transport outside scoped deny-list filtering in mode %s', async (mode) => {
|
||||
const { ctx, systemPrompt, runtime } = await setup({ mode })
|
||||
registerEcho(ctx, 'denied')
|
||||
registerEcho(ctx, 'kept')
|
||||
const { scope, agent } = await mintAgentScope(ctx)
|
||||
scope.ctx.tools.restrict({ deny: ['denied'] })
|
||||
|
||||
const assembly = await systemPrompt.assemble({ scope: agent })
|
||||
expect(assembly.tools.map(tool => tool.name)).toEqual(mode === 'code'
|
||||
? [RUN_CODE_NAME]
|
||||
: ['kept', RUN_CODE_NAME])
|
||||
const sdk = assembly.sections.find(section => section.name === 'tools:sdk')?.text
|
||||
expect(sdk).not.toContain('denied(args:')
|
||||
expect(sdk).toContain('kept(args:')
|
||||
|
||||
runtime.behavior = request => Promise.resolve({
|
||||
logs: [],
|
||||
value: Object.keys(request.bindings[0]!.functions).sort().join(','),
|
||||
})
|
||||
const result = await runCode(ctx, 'return Object.keys(tools)', { agent })
|
||||
expect(result.isError).toBe(false)
|
||||
expect(result.content).toEqual([{ type: 'text', text: 'kept' }])
|
||||
})
|
||||
|
||||
it.each(['code', 'both'] as const)('reserves run_code against scoped shadows and explicit restrictions in mode %s', async (mode) => {
|
||||
const { ctx, systemPrompt } = await setup({ mode })
|
||||
const { scope, agent } = await mintAgentScope(ctx)
|
||||
const impostor = defineTool({
|
||||
name: RUN_CODE_NAME,
|
||||
description: 'Scoped impostor.',
|
||||
parameters: {},
|
||||
execute: () => Promise.resolve([{ type: 'text' as const, text: 'impostor' }]),
|
||||
})
|
||||
|
||||
expect(() => scope.ctx.tools.register(impostor)).toThrow(/reserved for the Code Mode presentation transport/)
|
||||
expect(() => ctx.tools.register(impostor)).toThrow(/reserved for the Code Mode presentation transport/)
|
||||
expect(() => scope.ctx.tools.restrict({ allow: [RUN_CODE_NAME] })).toThrow(/cannot name reserved Code Mode presentation transport/)
|
||||
expect(() => scope.ctx.tools.restrict({ deny: [RUN_CODE_NAME] })).toThrow(/cannot name reserved Code Mode presentation transport/)
|
||||
scope.ctx.systemPrompt.section({ name: 'scoped-note', order: 149, text: 'safe note' })
|
||||
scope.ctx.tools.register(defineTool({
|
||||
name: 'scoped_safe',
|
||||
description: 'Safe scoped tool.',
|
||||
parameters: {},
|
||||
execute: () => Promise.resolve([{ type: 'text' as const, text: 'safe' }]),
|
||||
}))
|
||||
|
||||
const assembly = await systemPrompt.assemble({ scope: agent })
|
||||
const transports = assembly.tools.filter(tool => tool.name === RUN_CODE_NAME)
|
||||
expect(transports).toHaveLength(1)
|
||||
expect(transports[0]?.description).toContain('Execute a TypeScript program')
|
||||
expect(assembly.sections.find(section => section.name === 'scoped-note')?.text).toBe('safe note')
|
||||
expect(assembly.sections.find(section => section.name === 'tools:sdk')?.text).toContain('scoped_safe(args:')
|
||||
expect(ctx.tools.get(RUN_CODE_NAME, agent)).toBe(ctx.tools.get(RUN_CODE_NAME))
|
||||
const result = await runCode(ctx, 'return 1', { agent })
|
||||
expect(result.content).toEqual([{ type: 'text', text: '(run_code completed with no output)' }])
|
||||
})
|
||||
|
||||
it.each(['code', 'both'] as const)('keeps run_code in the toolOrder universe without exposing it as a restriction target in mode %s', async (mode) => {
|
||||
const { ctx, systemPrompt } = await setup({
|
||||
mode,
|
||||
toolOrder: [RUN_CODE_NAME, '<unlisted-tools>'],
|
||||
})
|
||||
registerEcho(ctx)
|
||||
const { agent } = await mintAgentScope(ctx)
|
||||
|
||||
const assembly = await systemPrompt.assemble({ scope: agent })
|
||||
expect(assembly.tools.map(tool => tool.name)).toEqual(mode === 'code'
|
||||
? [RUN_CODE_NAME]
|
||||
: [RUN_CODE_NAME, 'echo'])
|
||||
})
|
||||
|
||||
it("never exposes run_code to programs, even under mode 'both' (no recursive dispatch path)", async () => {
|
||||
const { ctx, runtime } = await setup({ mode: 'both' })
|
||||
registerEcho(ctx)
|
||||
@@ -201,6 +343,36 @@ describe('the run_code dispatch bridge', () => {
|
||||
expect(result.meta).toEqual({ logs: [{ source: 'console', level: 'log', text: 'saw echo:one' }], dispatches: 2 })
|
||||
})
|
||||
|
||||
it('exposes only an opaque parent token to nested result observers', async () => {
|
||||
const { ctx, runtime } = await setup({ mode: 'code' })
|
||||
registerEcho(ctx)
|
||||
runtime.behavior = async (request) => {
|
||||
await request.bindings[0]!.functions.echo!({ value: 'nested' })
|
||||
return { logs: [], value: 'done' }
|
||||
}
|
||||
|
||||
// Model a timeout-style outer wrapper: it temporarily installs a signal,
|
||||
// delegates, then restores the exact prior shape. A nested result observer
|
||||
// is observe-only and must not receive the live outer execution object;
|
||||
// freezing the correlation value it sees therefore cannot break restore.
|
||||
ctx.on('tools/execute', async (exec, next) => {
|
||||
if (exec.name !== RUN_CODE_NAME) return next()
|
||||
const previous = exec.signal
|
||||
exec.signal = new AbortController().signal
|
||||
const result = await next()
|
||||
if (previous === undefined) delete exec.signal
|
||||
else exec.signal = previous
|
||||
return result
|
||||
})
|
||||
ctx.on('tools/result', (exec) => {
|
||||
if (exec.parent !== undefined) Object.freeze(exec.parent)
|
||||
})
|
||||
|
||||
const result = await runCode(ctx, 'await tools.echo({ value: "nested" })')
|
||||
expect(result.isError).toBe(false)
|
||||
expect(result.content).toEqual([{ type: 'text', text: 'done' }])
|
||||
})
|
||||
|
||||
it('serializes Promise.all dispatches: tool executions never overlap, in submission order', async () => {
|
||||
const { ctx, runtime } = await setup({ mode: 'code' })
|
||||
const intervals: [string, string][] = []
|
||||
@@ -624,16 +796,17 @@ describe('the run_code dispatch bridge', () => {
|
||||
expect(events.filter(event => event.type === 'tool/code-dispatch')).toEqual([])
|
||||
})
|
||||
|
||||
it('logs the value the tool RECEIVED even when the tool mutates its arguments', async () => {
|
||||
it('gives the tool and durable log the same immutable argument value', async () => {
|
||||
const { ctx, runtime } = await setup({ mode: 'code' })
|
||||
const { agent, events } = fakeAgent()
|
||||
let mutationSucceeded: boolean | undefined
|
||||
ctx.tools.register(defineTool({
|
||||
name: 'mutator',
|
||||
description: 'Mutates its own args object.',
|
||||
description: 'Attempts to mutate its args object.',
|
||||
parameters: { list: { type: 'array', required: true } },
|
||||
execute(args) {
|
||||
args.list.push('injected-by-tool')
|
||||
return Promise.resolve([{ type: 'text' as const, text: 'mutated' }])
|
||||
mutationSucceeded = Reflect.set(args.list, 1, 'injected-by-tool')
|
||||
return Promise.resolve([{ type: 'text' as const, text: 'protected' }])
|
||||
},
|
||||
}))
|
||||
runtime.behavior = async (request) => {
|
||||
@@ -642,6 +815,7 @@ describe('the run_code dispatch bridge', () => {
|
||||
}
|
||||
const result = await runCode(ctx, 'program', { agent })
|
||||
expect(result.isError).toBe(false)
|
||||
expect(mutationSucceeded).toBe(false)
|
||||
const dispatch = events.find(event => event.type === 'tool/code-dispatch')?.data as SessionEventMap['tool/code-dispatch']
|
||||
expect(dispatch.arguments).toEqual({ list: ['original'] })
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user