Merge branch 'stack/agent-profiles-5-web-ui' into stack/agent-profiles-8-authoring

# Conflicts:
#	docs/module-graph.i18n.yaml
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	docs/subsystems/tools.i18n.yaml
#	docs/subsystems/tools.md
#	docs/subsystems/tools.zh.md
#	examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl
This commit is contained in:
Yichen Jiang
2026-08-09 21:38:26 +08:00
266 changed files with 12118 additions and 4857 deletions

View File

@@ -14,41 +14,7 @@ import type { JsonValue } from '@deepseek-ai/dsh-session'
import { defineTool, parameterSchemaSpecToJsonSchema } from './schema.ts'
import { TOOL_REGISTRY_SCHEDULER } from './index.ts'
import type { CodeDispatchLog, ToolDefinition, ToolExecutionResult, ToolRegistry, ToolRunContext } from './index.ts'
declare module '@deepseek-ai/dsh-session' {
interface SessionEventMap {
/**
* One sub-dispatch STARTING inside a `run_code` program: the parent
* `run_code` call id, the deterministic sub-call id (`<parent>:code:<n>`,
* numbered in submission order), and the tool `name` with its
* JSON-normalized `arguments` — the exact value dispatched, normalized
* BEFORE dispatch, so this append can never fail on payload shape.
* Appended when the scheduler actually starts the call (not at
* submission), so a start means the tool body pipeline was entered; a
* call abandoned in the queue logs nothing. Log-only: `deriveMessages()`
* ignores it; UIs use it for live per-sub-call running state and pair it
* with `tool/code-dispatch` by `subCallId` (timing = the two events'
* `time` fields).
*/
'tool/code-dispatch-start': { parentCallId: CallId; subCallId: CallId; name: string; arguments: unknown }
/**
* One bridged sub-dispatch SETTLING: the pairing ids (matching the
* `tool/code-dispatch-start` with the same `subCallId`), the tool `name`
* with the same JSON-normalized `arguments`, and the sub-call's complete
* model-facing outcome in `tool/result`'s own vocabulary
* (`content` + `isError`), so UIs render a sub-call through the exact
* code path that renders a native call. Every started sub-call settles
* with exactly one of these (abort included: the aborted pipeline result
* is an `isError` outcome).
* Log-only: `deriveMessages()` ignores it, so sub-calls never re-enter
* model context; persistence and UIs get every call. Appended inside the
* parent `run_code`'s execution (the bridge drains in-flight dispatches
* before returning), so its execution-enclosure relation holds by
* construction.
*/
'tool/code-dispatch': { parentCallId: CallId; subCallId: CallId; name: string; arguments: unknown; isError: boolean; content: ContentBlock[] }
}
}
import type {} from './types.ts'
/** The model-facing name of the Code Mode tool. */
export const RUN_CODE_NAME = 'run_code'
@@ -502,6 +468,7 @@ export function createRunCodeTool(registry: ToolRegistry, options: RunCodeBridge
const subCallId = CallId(`${String(exec.callId)}:code:${n}`)
const input = {
callId: subCallId,
rootCallId: exec.rootCallId,
name,
arguments: normalized.dispatched,
...exec.agent ? { agent: exec.agent } : {},
@@ -539,6 +506,7 @@ export function createRunCodeTool(registry: ToolRegistry, options: RunCodeBridge
content: result.content,
})
agent.session.append('tool/code-dispatch', {
rootCallId: exec.rootCallId,
parentCallId: exec.callId,
subCallId,
name,
@@ -563,6 +531,7 @@ export function createRunCodeTool(registry: ToolRegistry, options: RunCodeBridge
},
async start(): Promise<void> {
exec.agent?.session.append('tool/code-dispatch-start', {
rootCallId: exec.rootCallId,
parentCallId: exec.callId,
subCallId,
name,

View File

@@ -85,6 +85,7 @@ export {
} from './json-schema.ts'
export type { JsonValue } from '@deepseek-ai/dsh-session'
export type { CodeDispatchEventData, CodeDispatchStartEventData } from './types.ts'
export { CodeRunFailedError, RUN_CODE_NAME } from './code-mode.ts'
export { jsonSchemaToTs, renderToolsSdk } from './ts-types.ts'
@@ -297,6 +298,11 @@ export type ToolExecutionToken = symbol & { readonly [toolExecutionTokenBrand]:
*/
export interface ToolExecutionInput {
readonly callId: CallId
/**
* Root model-requested call owning this execution tree. Callers omit it for
* a root execution; nested dispatchers propagate the enclosing value.
*/
readonly rootCallId?: CallId
readonly name: string
/** Losslessly JSON-serializable parsed arguments (tools validate their own schema). */
readonly arguments: unknown
@@ -352,6 +358,8 @@ export interface CodeDispatchLog {
* observers run.
*/
export interface ToolExecution extends ToolExecutionInput {
/** Root model-requested call, resolved for every root and nested execution. */
readonly rootCallId: CallId
/** Registry-assigned identity shared with nested calls only as their opaque `parent` token. */
readonly token: ToolExecutionToken
}
@@ -1233,6 +1241,7 @@ export class ToolRegistry extends Service {
const deferredContexts: UserMessage[] = []
const token = createExecutionToken()
const callId = exec.callId
const rootCallId = exec.rootCallId ?? callId
const name = exec.name
const agent = exec.agent
const parent = exec.parent
@@ -1243,6 +1252,7 @@ export class ToolRegistry extends Service {
const base = {
token,
callId,
rootCallId,
name,
signal,
...agent !== undefined ? { agent } : {},

View File

@@ -33,9 +33,34 @@ function validateResult(
const install: InvariantInstaller = Object.assign((ctx: Context, fail: InvariantFailure) => {
const stages = new WeakMap<object, ToolStage>()
const openTurns = new WeakMap<Session, number | null>()
const dispatchRoots = new WeakMap<Session, Map<string, string>>()
const validateDispatch = (session: Session, event: SessionEvent): void => {
if (event.type !== 'tool/code-dispatch-start' && event.type !== 'tool/code-dispatch') return
const root = String(event.data.rootCallId)
const parent = String(event.data.parentCallId)
const child = String(event.data.subCallId)
if (root.length === 0 || parent.length === 0 || child.length === 0) {
fail(`${event.type} must carry non-empty rootCallId, parentCallId, and subCallId`)
return
}
const roots = dispatchRoots.get(session)
const known = roots?.get(child)
if (known !== undefined && known !== root) fail(`${event.type} changed rootCallId for subCallId ${child}`)
if (parent !== root && roots?.get(parent) !== root) {
fail(`${event.type} parentCallId ${parent} does not belong to rootCallId ${root}`)
}
}
const commitDispatch = (session: Session, event: SessionEvent): void => {
if (event.type !== 'tool/code-dispatch-start' && event.type !== 'tool/code-dispatch') return
const roots = dispatchRoots.get(session) as Map<string, string>
roots.set(String(event.data.subCallId), String(event.data.rootCallId))
}
const seed = (session: Session): number | null => {
let openTurn: number | null = null
dispatchRoots.set(session, new Map())
for (const event of session.events) {
validateDispatch(session, event)
commitDispatch(session, event)
if (event.type === 'turn/start') openTurn = event.data.turn
else if (event.type === 'turn/end') openTurn = null
else if ((event.type === 'tool/code-dispatch-start' || event.type === 'tool/code-dispatch')
@@ -51,12 +76,15 @@ const install: InvariantInstaller = Object.assign((ctx: Context, fail: Invariant
for (const session of ctx.sessions.list()) seed(session)
ctx.on('session/created', (session) => { seed(session) }, { global: true })
ctx.on('session/event', (session, event) => {
validateDispatch(session, event)
commitDispatch(session, event)
if (event.type === 'turn/start') openTurns.set(session, event.data.turn)
else if (event.type === 'turn/end') openTurns.set(session, null)
}, { global: true })
ctx.on('internal/dispatch', (_mode, eventName, args) => {
if (eventName === 'session/event') {
const [session, event] = args as [Session, SessionEvent]
validateDispatch(session, event)
if ((event.type === 'tool/code-dispatch-start' || event.type === 'tool/code-dispatch')
&& openTurnFor(session) === null) {
fail(`${event.type} appended outside any open turn`)

View File

@@ -0,0 +1,58 @@
/**
* Durable Tool event vocabulary shared with type-only consumers.
*
* @module @deepseek-ai/dsh-tools/types
*/
import type { CallId } from '@deepseek-ai/dsh-llm/brand'
import type { ContentBlock } from '@deepseek-ai/dsh-llm/types'
/** Payload recorded when one nested Code Mode Tool dispatch starts. */
export interface CodeDispatchStartEventData {
rootCallId: CallId
parentCallId: CallId
subCallId: CallId
name: string
arguments: unknown
}
/** Payload recorded when one nested Code Mode Tool dispatch settles. */
export interface CodeDispatchEventData extends CodeDispatchStartEventData {
isError: boolean
content: ContentBlock[]
}
declare module '@deepseek-ai/dsh-session/types' {
interface SessionEventMap {
/**
* One sub-dispatch STARTING inside a `run_code` program: the parent
* `run_code` call id, the deterministic sub-call id (`<parent>:code:<n>`,
* numbered in submission order), and the tool `name` with its
* JSON-normalized `arguments` — the exact value dispatched, normalized
* BEFORE dispatch, so this append can never fail on payload shape.
* Appended when the scheduler actually starts the call (not at
* submission), so a start means the tool body pipeline was entered; a
* call abandoned in the queue logs nothing. Log-only: `deriveMessages()`
* ignores it; UIs use it for live per-sub-call running state and pair it
* with `tool/code-dispatch` by `subCallId` (timing = the two events'
* `time` fields).
*/
'tool/code-dispatch-start': CodeDispatchStartEventData
/**
* One bridged sub-dispatch SETTLING: the pairing ids (matching the
* `tool/code-dispatch-start` with the same `subCallId`), the tool `name`
* with the same JSON-normalized `arguments`, and the sub-call's complete
* model-facing outcome in `tool/result`'s own vocabulary
* (`content` + `isError`), so UIs render a sub-call through the exact
* code path that renders a native call. Every started sub-call settles
* with exactly one of these (abort included: the aborted pipeline result
* is an `isError` outcome).
* Log-only: `deriveMessages()` ignores it, so sub-calls never re-enter
* model context; persistence and UIs get every call. Appended inside the
* parent `run_code`'s execution (the bridge drains in-flight dispatches
* before returning), so its execution-enclosure relation holds by
* construction.
*/
'tool/code-dispatch': CodeDispatchEventData
}
}