feat(subagent): add explicit child reports
This commit is contained in:
94
packages/subagent/tool-subagent-report/src/index.ts
Normal file
94
packages/subagent/tool-subagent-report/src/index.ts
Normal 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))
|
||||
}
|
||||
30
packages/subagent/tool-subagent-report/src/invariant.ts
Normal file
30
packages/subagent/tool-subagent-report/src/invariant.ts
Normal 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 */
|
||||
Reference in New Issue
Block a user