import { type Static, Type } from "typebox"; import { Check } from "typebox/value"; import { isSingleLineText, singleLineTextSchema, } from "../../shared/text-contracts"; import { invocationElapsedSeconds, type SubagentFeedback } from "./domain"; import { sanitizePublicSubagentErrorMessage } from "./public-error"; const UNICODE_WHITE_SPACE_CODE_POINT = /^\p{White_Space}$/u; /** Names the complete public subagent tool set. */ export const SUBAGENT_TOOL_NAMES = [ "subagent_start", "subagent_steer", "subagent_wait", "subagent_query", ] as const; /** Opens the available-subagent contribution for model context and diagnostics. */ export const AVAILABLE_SUBAGENTS_PROMPT_OPENING_TAG = ''; /** Closes the available-subagent contribution in model context. */ export const AVAILABLE_SUBAGENTS_PROMPT_CLOSING_TAG = ""; /** Lists stable failed-tool codes. */ const SUBAGENT_FAILED_CODES = [ "invalid_request", "agent_unavailable", "unknown_session", "not_owner", "message_rejected", "start_failed", "query_failed", "wait_already_active", ] as const; /** Identifies one public failed-tool code. */ export type SubagentFailedCode = (typeof SUBAGENT_FAILED_CODES)[number]; /** Carries machine-readable failed-tool details. */ export interface SubagentFailureDetails { readonly code: SubagentFailedCode; readonly message: string; } /** Declares the exact public start request boundary. */ export const SubagentStartParameters = Type.Object( { agentId: singleLineTextSchema({ description: "Subagent ID listed in ", minLength: 1, }), taskName: singleLineTextSchema({ description: "Short task name for subagent session", minLength: 3, maxLength: 60, }), prompt: Type.String({ description: "Instructions for subagent", minLength: 1, maxLength: 32768, }), }, { additionalProperties: false }, ); /** Declares the exact public steer request boundary. */ export const SubagentSteerParameters = Type.Object( { sessionId: Type.Integer({ description: "Session ID returned by subagent_start", minimum: 1, }), prompt: Type.String({ description: "Instructions to send to existing subagent session", minLength: 1, maxLength: 32768, }), }, { additionalProperties: false }, ); /** Declares the exact public query request boundary. */ export const SubagentQueryParameters = Type.Object( { sessionId: Type.Integer({ description: "Session ID returned by subagent_start", minimum: 1, }), question: Type.String({ description: "Question about specified subagent session", minLength: 1, maxLength: 4096, }), }, { additionalProperties: false }, ); /** Declares the exact public wait request boundary. */ export const SubagentWaitParameters = Type.Object( { sessionIds: Type.Array(Type.Integer({ minimum: 1 }), { description: "List of unique session IDs returned by subagent_start", minItems: 1, maxItems: 64, uniqueItems: true, }), timeout: Type.Integer({ description: "Maximum wait time in seconds", minimum: 1, maximum: 3600, }), }, { additionalProperties: false }, ); /** Describes a validated start request. */ export type SubagentStartRequest = Static; /** Describes a validated steer request. */ export type SubagentSteerRequest = Static; /** Describes a validated query request. */ export type SubagentQueryRequest = Static; /** Describes a validated wait request. */ export type SubagentWaitRequest = Static; /** Lists public normal results. */ export type SubagentNormalResult = | { readonly outcome: "accepted"; readonly sessionId: number } | ({ readonly outcome: "feedback"; readonly sessionId: number; readonly elapsedSeconds: number; } & ( | { readonly status: "success"; readonly output: string } | { readonly status: "failure" | "abort"; readonly error: string } )) | { readonly outcome: "timeout" } | { readonly outcome: "no_active_sessions" }; /** Carries one public failed-tool result through Pi's throwing tool channel. */ export class SubagentToolError extends Error { public readonly details: SubagentFailureDetails; public readonly code: SubagentFailedCode; /** Creates a stable code while leaving message wording non-contractual. */ public constructor(code: SubagentFailedCode, message: string) { const safeMessage = sanitizePublicSubagentErrorMessage(message); super(`[${code}] ${safeMessage}`); this.name = "SubagentToolError"; this.code = code; this.details = { code, message: safeMessage }; } } /** Validates and narrows an unknown start request once at the tool boundary. */ export function parseSubagentStartRequest( value: unknown, ): SubagentStartRequest { if (!isExactRecord(value, ["agentId", "taskName", "prompt"])) { throw invalidRequest( "subagent_start requires agentId, taskName, and prompt", ); } const agentId = value["agentId"]; const taskName = value["taskName"]; const prompt = value["prompt"]; if ( !isSingleLineText(agentId) || !isSingleLineText(taskName) || typeof prompt !== "string" || !hasNonWhitespaceCodePoint(prompt) ) { throw invalidRequest("subagent_start request fields are invalid"); } return { agentId, taskName, prompt }; } /** Validates and narrows an unknown steer request once at the tool boundary. */ export function parseSubagentSteerRequest( value: unknown, ): SubagentSteerRequest { if (!isExactRecord(value, ["sessionId", "prompt"])) { throw invalidRequest("subagent_steer requires sessionId and prompt"); } const sessionId = value["sessionId"]; const prompt = value["prompt"]; if ( typeof sessionId !== "number" || !Number.isInteger(sessionId) || sessionId <= 0 || typeof prompt !== "string" || !hasNonWhitespaceCodePoint(prompt) ) { throw invalidRequest("subagent_steer request fields are invalid"); } return { sessionId, prompt }; } /** Validates and narrows an unknown query request once at the tool boundary. */ export function parseSubagentQueryRequest( value: unknown, ): SubagentQueryRequest { if (!isExactRecord(value, ["sessionId", "question"])) { throw invalidRequest("subagent_query requires sessionId and question"); } const sessionId = value["sessionId"]; const question = value["question"]; if ( typeof sessionId !== "number" || !Number.isInteger(sessionId) || sessionId <= 0 || typeof question !== "string" || !hasNonWhitespaceCodePoint(question) ) { throw invalidRequest("subagent_query request fields are invalid"); } return { sessionId, question }; } /** Validates and narrows an unknown wait request once at the tool boundary. */ export function parseSubagentWaitRequest(value: unknown): SubagentWaitRequest { if (!Check(SubagentWaitParameters, value)) { throw invalidRequest("subagent_wait request fields are invalid"); } return value; } /** Validates one normal result returned through the runtime IPC bridge. */ export function parseSubagentNormalResult( value: unknown, ): SubagentNormalResult { const outcome = typeof value === "object" && value !== null ? Reflect.get(value, "outcome") : undefined; if (isOutcomeOnlyResult(value, outcome)) { return { outcome }; } const sessionId = readPositiveInteger(value, "sessionId"); if ( outcome === "accepted" && sessionId !== undefined && isExactRecord(value, ["outcome", "sessionId"]) ) { return { outcome, sessionId }; } const status = readString(value, "status"); const elapsedSeconds = readPositiveInteger(value, "elapsedSeconds"); if ( outcome === "feedback" && sessionId !== undefined && elapsedSeconds !== undefined && status === "success" && isExactRecord(value, [ "outcome", "sessionId", "status", "elapsedSeconds", "output", ]) ) { const output = readString(value, "output"); if (output !== undefined) { return { outcome, sessionId, status, elapsedSeconds, output }; } } if ( outcome === "feedback" && sessionId !== undefined && elapsedSeconds !== undefined && (status === "failure" || status === "abort") && isExactRecord(value, [ "outcome", "sessionId", "status", "elapsedSeconds", "error", ]) ) { const error = readString(value, "error"); if (error !== undefined) { return { outcome, sessionId, status, elapsedSeconds, error: sanitizePublicSubagentErrorMessage(error), }; } } throw new SubagentToolError( "start_failed", "Subagent returned an invalid response", ); } /** Converts terminal feedback to its exact public wait result. */ export function feedbackResult( feedback: SubagentFeedback, ): SubagentNormalResult { const sessionId = feedback.sessionKey.ownerLocalSessionId; const elapsedSeconds = invocationElapsedSeconds( feedback.presentation.invocationMetadata, ); return feedback.status === "success" ? { outcome: "feedback", sessionId, status: "success", elapsedSeconds, output: feedback.output, } : { outcome: "feedback", sessionId, status: feedback.status, elapsedSeconds, error: sanitizePublicSubagentErrorMessage(feedback.error), }; } /** Reads one non-empty string from untrusted result data. */ function readString(value: unknown, key: string): string | undefined { const field = typeof value === "object" && value !== null ? Reflect.get(value, key) : undefined; return typeof field === "string" ? field : undefined; } /** Reads one positive integer from untrusted result data. */ function readPositiveInteger(value: unknown, key: string): number | undefined { const field = typeof value === "object" && value !== null ? Reflect.get(value, key) : undefined; return typeof field === "number" && Number.isInteger(field) && field > 0 ? field : undefined; } /** Returns a failed-tool error for structural request violations. */ function invalidRequest(message: string): SubagentToolError { return new SubagentToolError("invalid_request", message); } /** Identifies closed normal results whose outcome is their only field. */ function isOutcomeOnlyResult( value: unknown, outcome: unknown, ): outcome is "timeout" | "no_active_sessions" { return ( (outcome === "timeout" || outcome === "no_active_sessions") && isExactRecord(value, ["outcome"]) ); } /** Requires at least one Unicode code point outside the whitespace class. */ function hasNonWhitespaceCodePoint(value: string): boolean { return Array.from(value).some( (codePoint) => !UNICODE_WHITE_SPACE_CODE_POINT.test(codePoint), ); } /** Rejects unknown object keys before any semantic validation. */ function isExactRecord( value: unknown, keys: readonly string[], ): value is Record { if (typeof value !== "object" || value === null || Array.isArray(value)) { return false; } const actualKeys = Object.keys(value); return ( actualKeys.length === keys.length && actualKeys.every((key) => keys.includes(key)) ); }