feat(subagent): add explicit child reports

This commit is contained in:
Dudu-0223
2026-07-31 22:45:21 +08:00
committed by Tianyi Cui
parent a1b3bebb61
commit 431fb4b035
76 changed files with 3831 additions and 143 deletions

View File

@@ -0,0 +1,94 @@
/**
* The child-scoped `report` tool, installed into every continuable in-process
* child's unpublished context. Roots, one-shot children, remote providers, and
* agentless executions never see the registration.
*
* @module @deepseek-ai/dsh-tool-subagent-report
*/
import type { Context } from 'cordis'
import z from 'schemastery'
import type { Agent } from '@deepseek-ai/dsh-agent'
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import type { SubagentReportDelivery } from '@deepseek-ai/dsh-subagent'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'tool-subagent-report'
// The contribution registers only through childCtx.tools, but declaring tools
// makes Loader ordering fail at load instead of the next child materialization.
export const inject = ['subagents', 'tools']
/** Config: how accepted reports are scheduled on the parent. */
export interface Config {
/**
* Parent scheduling (default `quiet`). `quiet` adds context without waking;
* `wakeup` creates one ordinary later parent turn.
*/
reportDelivery?: SubagentReportDelivery
}
export const Config: z<Config> = z.object({
reportDelivery: z.union(['quiet', 'wakeup'] as const).default('quiet'),
})
/**
* Install `report` into one continuable child's scope.
* @param childCtx - child-scoped context receiving the tool.
* @param ctx - service context used for delivery.
* @param delivery - resolved deployment scheduling policy.
* @returns disposer for this one registration.
*/
export function installReportTool(
childCtx: Context,
ctx: Context,
delivery: SubagentReportDelivery,
): () => void {
return childCtx.tools.register(defineTool({
name: 'report',
description:
'Report selected content to the agent that started you. Call this zero or more times for progress, '
+ 'findings, or a final answer. Reporting does not end your turn or finish your work, and only your '
+ 'direct parent receives it. A failed call may still have arrived, so do not blindly repeat it.',
parameters: {
output: {
type: 'string',
required: true,
description: 'Self-contained content for your parent; it does not see your private work.',
},
},
output: {
schema: {
type: 'object',
additionalProperties: false,
properties: {
messageId: { type: 'string', required: true },
},
},
render: (_args, value) => [{
type: 'text',
text: `report accepted by the agent that started you as message ${value.messageId}`,
}],
},
async execute(args, exec) {
const content: ContentBlock[] = [{ type: 'text', text: args.output }]
// Scope-local resolution guarantees an Agent. The service still verifies
// its exact live Activation identity at the authority boundary.
const messageId = await ctx.subagents.reportFrom(exec.agent as Agent, content, {
delivery,
signal: exec.signal,
})
return { messageId }
},
}))
}
/**
* Register the continuable-child contribution.
* @param ctx - context carrying tools and the subagent service.
* @param config - deployment scheduling policy.
*/
export function apply(ctx: Context, config: Config = {}): void {
const { reportDelivery = 'quiet' } = Config(config)
ctx.subagents.registerContinuableSetup(childCtx =>
installReportTool(childCtx, ctx, reportDelivery))
}

View File

@@ -0,0 +1,30 @@
/**
* Package-owned invariant companion for `@deepseek-ai/dsh-tool-subagent-report`.
* @module @deepseek-ai/dsh-tool-subagent-report/invariant
*/
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-tool-subagent-report'
/** Cordis companion plugin name. */
export const name = 'tool-subagent-report-invariant'
/** Service required before the companion can reserve package ownership. */
export const inject = ['invariants']
/**
* No runtime invariant: this adapter has no independent lifecycle stream;
* sender authorization and delivery relations belong to the subagent service.
*/
const install: InvariantInstaller = () => {}
/**
* Register this package's invariant companion.
* @param ctx - context carrying the invariant service.
* @returns the registration disposer after setup succeeds.
*/
export const apply = (ctx: Context): Promise<() => void> =>
Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))
/* jscpd:ignore-end */