Merge remote-tracking branch 'origin/master' into codex/tool-json-schema-dsl

# Conflicts:
#	docs/event-producer-consumer.md
#	packages/cordis/tool-cordis/src/api-catalog.ts
This commit is contained in:
Tianyi Cui
2026-07-21 19:44:49 +08:00
509 changed files with 12824 additions and 2596 deletions

View File

@@ -11,11 +11,16 @@
"types": "./lib/types/index.d.ts",
"default": "./lib/index.js"
},
"./invariant": {
"types": "./lib/types/invariant.d.ts",
"default": "./lib/invariant.js"
},
"./src/*": "./src/*",
"./package.json": "./package.json"
},
"files": [
"lib/index.js",
"lib/invariant.js",
"lib/types/**/*.d.ts",
"lib/types/**/*.d.ts.map",
"src"
@@ -23,12 +28,13 @@
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/dsh-agent": "^0.0.1",
"@deepseek-ai/dsh-user-approval": "^0.0.1",
"@deepseek-ai/dsh-code-runtime": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-llm": "^0.0.1",
"@deepseek-ai/dsh-scope": "^0.0.1",
"@deepseek-ai/dsh-session": "^0.0.1",
"@deepseek-ai/dsh-system-prompt": "^0.0.1",
"@deepseek-ai/dsh-user-approval": "^0.0.1",
"cordis": "^4.0.0-rc.7"
},
"dependencies": {
@@ -36,12 +42,13 @@
},
"devDependencies": {
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-user-approval": "workspace:^",
"@deepseek-ai/dsh-code-runtime": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-scope": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-system-prompt": "workspace:^",
"@deepseek-ai/dsh-user-approval": "workspace:^",
"cordis": "^4.0.0-rc.7"
}
}

View File

@@ -0,0 +1,69 @@
/** Package-owned tool-pipeline invariants. @module @deepseek-ai/dsh-tools/invariant */
import type { Context } from 'cordis'
import type { InvariantFailure, InvariantInstaller } from '@deepseek-ai/dsh-invariants'
import type { ToolExecution, ToolExecutionResult } from './index.ts'
const PACKAGE_NAME = '@deepseek-ai/dsh-tools'
/** Cordis companion plugin name. */
export const name = 'tools-invariant'
/** Service required before the companion can reserve package ownership. */
export const inject = ['invariants']
type ToolStage = 'pre' | 'execute' | 'post'
/** Validate the immutable final execution/result snapshot. */
function validateResult(
exec: Readonly<ToolExecution>,
result: Readonly<ToolExecutionResult>,
fail: InvariantFailure,
): void {
if (!Object.isFrozen(exec)) fail('tools/result execution must be frozen before publication')
if (!Object.isFrozen(result) || !Object.isFrozen(result.content)) {
fail('tools/result outcome and content must be frozen before publication')
}
if (exec.name.length === 0 || String(exec.callId).length === 0) {
fail('tools/result execution must carry non-empty name and callId')
}
}
/** Install monotonic pipeline and final-snapshot checks. */
const install: InvariantInstaller = (ctx, fail) => {
const stages = new WeakMap<object, ToolStage>()
ctx.on('internal/dispatch', (_mode, eventName, args) => {
if (eventName === 'tools/pre-execute') {
const exec = args[0] as ToolExecution
if (stages.has(exec)) fail('tools/pre-execute repeated for one execution')
stages.set(exec, 'pre')
return
}
if (eventName === 'tools/execute') {
const exec = args[0] as ToolExecution
if (stages.get(exec) !== 'pre') fail('tools/execute must follow tools/pre-execute')
stages.set(exec, 'execute')
return
}
if (eventName === 'tools/post-execute') {
const exec = args[0] as ToolExecution
const previous = stages.get(exec)
if (previous !== 'pre' && previous !== 'execute') {
fail('tools/post-execute must follow tools/pre-execute or tools/execute')
}
stages.set(exec, 'post')
return
}
if (eventName !== 'tools/result') return
const [exec, result] = args as [Readonly<ToolExecution>, Readonly<ToolExecutionResult>]
validateResult(exec, result, fail)
stages.delete(exec)
}, { global: true })
}
/**
* Register the tools invariant companion.
* @param ctx - Cordis context carrying the invariant service.
* @returns the installed registration's disposer after setup succeeds.
*/
export const apply = (ctx: Context): Promise<() => void> =>
Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))

View File

@@ -0,0 +1,84 @@
import { describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import { scopeTarget } from '@deepseek-ai/dsh-scope'
import { CallId } from '@deepseek-ai/dsh-llm'
import type { ToolExecution, ToolExecutionResult, ToolExecutionToken } from '@deepseek-ai/dsh-tools'
import * as ToolsInvariant from '@deepseek-ai/dsh-tools/invariant'
import InvariantService from '@deepseek-ai/dsh-invariants'
async function setup(): Promise<Context> {
const ctx = new Context()
await ctx.plugin(InvariantService)
await ctx.plugin(ToolsInvariant)
return ctx
}
const execution = (overrides: Partial<ToolExecution> = {}): ToolExecution => ({
token: Symbol('tool') as ToolExecutionToken,
callId: CallId('call-1'),
name: 'echo',
arguments: Object.freeze({ text: 'hi' }),
...overrides,
})
const outcome = (): ToolExecutionResult => Object.freeze({
content: Object.freeze([{ type: 'text' as const, text: 'ok' }]) as never,
isError: false,
})
function emitResult(ctx: Context, exec: ToolExecution, result: ToolExecutionResult): void {
ctx.emit(scopeTarget(ctx as never, undefined), 'tools/result', exec, result)
}
async function stage(ctx: Context, name: 'tools/pre-execute' | 'tools/execute', exec: ToolExecution): Promise<void> {
if (name === 'tools/pre-execute') {
await ctx.waterfall(ctx as never, name, exec, () => Promise.resolve({ kind: 'allow' as const }))
} else {
await ctx.waterfall(ctx as never, name, exec, () => Promise.resolve(outcome()))
}
}
describe('tool-pipeline invariants', () => {
it('accepts dispatch and denial stage orders with frozen results', async () => {
const ctx = await setup()
const dispatched = execution()
await stage(ctx, 'tools/pre-execute', dispatched)
await stage(ctx, 'tools/execute', dispatched)
await ctx.waterfall(ctx as never, 'tools/post-execute', dispatched, outcome(), () => Promise.resolve({ kind: 'accept' as const }))
Object.freeze(dispatched)
emitResult(ctx, dispatched, outcome())
const denied = execution({ callId: CallId('call-2') })
await stage(ctx, 'tools/pre-execute', denied)
await ctx.waterfall(ctx as never, 'tools/post-execute', denied, outcome(), () => Promise.resolve({ kind: 'accept' as const }))
Object.freeze(denied)
emitResult(ctx, denied, outcome())
ctx.emit('tools/change')
})
it('rejects repeated and out-of-order pipeline stages', async () => {
const ctx = await setup()
const exec = execution()
await stage(ctx, 'tools/pre-execute', exec)
await expect(stage(ctx, 'tools/pre-execute', exec)).rejects.toThrow(/repeated/)
const noPre = execution({ callId: CallId('call-2') })
await expect(stage(ctx, 'tools/execute', noPre)).rejects.toThrow(/must follow tools\/pre-execute/)
expect(() => ctx.waterfall(
ctx as never, 'tools/post-execute', noPre, outcome(),
() => Promise.resolve({ kind: 'accept' as const }),
)).toThrow(/must follow tools\/pre-execute or tools\/execute/)
})
it('rejects mutable or anonymous final snapshots', async () => {
const ctx = await setup()
expect(() => { emitResult(ctx, execution(), outcome()) }).toThrow(/execution must be frozen/)
const exec = Object.freeze(execution())
expect(() => { emitResult(ctx, exec, { content: [], isError: false }) })
.toThrow(/outcome and content must be frozen/)
const anonymous = Object.freeze(execution({ name: '' }))
expect(() => { emitResult(ctx, anonymous, outcome()) }).toThrow(/non-empty name and callId/)
})
})

View File

@@ -34,6 +34,9 @@
},
{
"path": "../../ui/user-approval"
},
{
"path": "../../support/invariants"
}
]
}