fix(subagent): validate direct depth boundaries

This commit is contained in:
Tianyi Cui
2026-07-12 10:48:20 +08:00
parent 875c3d62d8
commit d427478c44
13 changed files with 88 additions and 36 deletions

View File

@@ -21,6 +21,7 @@ import { AgentId, type Agent, type AgentHandle, type AgentOptions } from '@deeps
import { SessionId, snapshotJsonValue, type SessionEvent, type TurnEndReason } from '@deepseek-ai/dsh-session'
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import { assertSupportedOutputSchema, OutputSchemaError } from '@deepseek-ai/dsh-tools'
import { assertSubagentMaxDepth } from '@deepseek-ai/dsh-subagent'
import type { SubagentResult, SubagentRun, SubagentStartRequest, SubagentStopReason } from '@deepseek-ai/dsh-subagent'
import {
attachStructuredRuntime,
@@ -42,20 +43,26 @@ declare module '@deepseek-ai/dsh-agent' {
* (config/ACP-created) agent, parent depth + 1 for a subagent. Set by the
* in-process backends on every child they create so a nested spawn reads its
* parent's depth from `parent.options.subagentDepth` and the `depthLimit`
* capability can cap the tree. Merge-extensible field (the seam owns it; the
* loop neither sets nor reads it).
* capability can cap the tree. When present it is a non-negative safe
* integer. Merge-extensible field (the seam owns it; the loop neither sets
* nor reads it).
*/
subagentDepth?: number
}
}
/**
* Read an agent's delegation depth (absent ⇒ a top-level agent, depth 0).
* Read an agent's delegation depth (absent ⇒ a top-level agent, depth 0),
* rejecting a malformed stored value instead of letting it disable comparison.
* @param agent - the agent whose options may carry `subagentDepth`.
* @returns 0 for a top-level agent, its parent's depth + 1 for a subagent.
*/
export function depthOf(agent: Agent): number {
return agent.options.subagentDepth ?? 0
const depth = agent.options.subagentDepth ?? 0
if (!Number.isSafeInteger(depth) || depth < 0 || Object.is(depth, -0)) {
throw new TypeError('agent subagentDepth must be a non-negative safe integer')
}
return depth
}
/** Thrown when a spawn would exceed the request's `maxDepth` cap. */
@@ -139,6 +146,7 @@ export function startInProcessRun(
const inputPrompt = request.prompt
const inputAgentOptions = request.agentOptions
const inputSeed = options.seed
assertSubagentMaxDepth(inputMaxDepth)
const toolFilter = inputToolFilter === undefined ? undefined : snapshotJsonValue(inputToolFilter)
if (inputToolFilter !== undefined && toolFilter === undefined) {
throw new TypeError('subagent tool filter must be losslessly JSON-serializable')