/** * TypeBox schemas for subagent tool parameters */ import { Type } from "typebox"; // Note: Using Type.Any() for Google API compatibility (doesn't support anyOf) const SkillOverride = Type.Any({ description: "Skill name(s) to inject (comma-separated), array of strings, or boolean (false disables, true uses default)", }); export const TaskItem = Type.Object({ agent: Type.String(), cwd: Type.Optional(Type.String()), model: Type.Optional( Type.String({ description: "Override model for this task (e.g. 'google/gemini-3-pro')", }), ), skill: Type.Optional(SkillOverride), task: Type.String(), systemPrompt: Type.Optional( Type.String({ description: "System prompt for creating this agent on-the-fly if 'agent' name doesn't match a pre-defined agent", }), ), }); // Sequential chain step (single agent) export const SequentialStepSchema = Type.Object({ agent: Type.String(), cwd: Type.Optional(Type.String()), model: Type.Optional(Type.String({ description: "Override model for this step" })), output: Type.Optional( Type.Any({ description: "Output filename to write in {chain_dir} (string), or false to disable file output", }), ), progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })), reads: Type.Optional( Type.Any({ description: "Files to read from {chain_dir} before running (array of filenames), or false to disable", }), ), skill: Type.Optional(SkillOverride), task: Type.Optional( Type.String({ description: "Task template with variables: {task}=original request, {previous}=prior step's text response, {chain_dir}=shared folder. Required for first step, defaults to '{previous}' for subsequent steps.", }), ), systemPrompt: Type.Optional( Type.String({ description: "System prompt for creating this agent on-the-fly if 'agent' name doesn't match a pre-defined agent", }), ), }); // Parallel task item (within a parallel step) export const ParallelTaskSchema = Type.Object({ agent: Type.String(), cwd: Type.Optional(Type.String()), model: Type.Optional(Type.String({ description: "Override model for this task" })), output: Type.Optional( Type.Any({ description: "Output filename to write in {chain_dir} (string), or false to disable file output", }), ), progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })), reads: Type.Optional( Type.Any({ description: "Files to read from {chain_dir} before running (array of filenames), or false to disable", }), ), skill: Type.Optional(SkillOverride), task: Type.Optional( Type.String({ description: "Task template with {task}, {previous}, {chain_dir} variables. Defaults to {previous}.", }), ), systemPrompt: Type.Optional( Type.String({ description: "System prompt for creating this agent on-the-fly if 'agent' name doesn't match a pre-defined agent", }), ), }); // Parallel chain step (multiple agents running concurrently) export const ParallelStepSchema = Type.Object({ concurrency: Type.Optional(Type.Number({ description: "Max concurrent tasks (default: 4)" })), failFast: Type.Optional(Type.Boolean({ description: "Stop on first failure (default: false)" })), continueOnError: Type.Optional( Type.Boolean({ description: "Collect partial results when individual tasks fail (default: false, overrides failFast when true)", }), ), parallel: Type.Array(ParallelTaskSchema, { minItems: 1, description: "Tasks to run in parallel", }), }); // Chain item can be either sequential or parallel // Note: Using Type.Any() for Google API compatibility (doesn't support anyOf) export const ChainItem = Type.Any({ description: "Chain step: either {agent, task?, ...} for sequential or {parallel: [...]} for concurrent execution", }); export const MaxOutputSchema = Type.Optional( Type.Object({ bytes: Type.Optional(Type.Number({ description: "Max bytes (default: 204800)" })), lines: Type.Optional(Type.Number({ description: "Max lines (default: 5000)" })), }), ); export const SubagentParams = Type.Object({ agent: Type.Optional( Type.String({ description: "Agent name (SINGLE mode) or target for management get/update/delete", }), ), task: Type.Optional(Type.String({ description: "Task (SINGLE mode)" })), systemPrompt: Type.Optional( Type.String({ description: "System prompt for creating agent on-the-fly when the named agent doesn't exist. Use this instead of pre-defining agents. Supports {cwd} variable for current working directory.", }), ), modelOverride: Type.Optional( Type.String({ description: "Model override for the dynamically created agent (e.g. 'anthropic/claude-sonnet-4')", }), ), toolsOverride: Type.Optional( Type.String({ description: "Comma-separated tool names to enable for the dynamically created agent", }), ), skillsOverride: Type.Optional( Type.String({ description: "Comma-separated skill names to inject for the dynamically created agent", }), ), thinkingOverride: Type.Optional( Type.String({ description: "Thinking level suffix for the dynamically created agent (e.g. 'medium', 'high')", }), ), // Management action (when present, tool operates in management mode) action: Type.Optional( Type.String({ description: "Management action: 'list' (discover agents/chains), 'get' (full detail), 'create', 'update', 'delete'. Omit for execution mode.", }), ), // Chain identifier for management (can't reuse 'chain': that's the execution array) chainName: Type.Optional( Type.String({ description: "Chain name for get/update/delete management actions", }), ), // Agent/chain configuration for create/update (nested to avoid conflicts with execution fields) config: Type.Optional( Type.Any({ description: "Agent or chain config for create/update. Agent: name, description, scope ('user'|'project', default 'user'), systemPrompt, model, category, tools (comma-separated), extensions (comma-separated), skills (comma-separated), thinking, output, reads, progress. Chain: name, description, scope, steps (array of {agent, task?, output?, reads?, model?, skills?, progress?}). Presence of 'steps' creates a chain instead of an agent.", }), ), tasks: Type.Optional( Type.Array(TaskItem, { description: "PARALLEL mode: [{agent, task}, ...]", }), ), chain: Type.Optional( Type.Array(ChainItem, { description: "CHAIN mode: sequential pipeline where each step's response becomes {previous} for the next. Use {task}, {previous}, {chain_dir} in task templates.", }), ), chainDir: Type.Optional( Type.String({ description: "Persistent directory for chain artifacts. Default: /pi-chain-runs/ (auto-cleaned after 24h)", }), ), async: Type.Optional( Type.Boolean({ description: "Run in background (default: false, or per config)", }), ), agentScope: Type.Optional( Type.String({ description: "Agent discovery scope: 'user', 'project', or 'both' (default: 'both'; project wins on name collisions)", }), ), cwd: Type.Optional(Type.String()), maxOutput: MaxOutputSchema, artifacts: Type.Optional(Type.Boolean({ description: "Write debug artifacts (default: true)" })), includeProgress: Type.Optional( Type.Boolean({ description: "Include full progress in result (default: false)", }), ), share: Type.Optional( Type.Boolean({ description: "Upload session to GitHub Gist for sharing (default: false)", }), ), sessionDir: Type.Optional( Type.String({ description: "Directory to store session logs (default: temp; enables sessions even if share=false)", }), ), // Clarification TUI clarify: Type.Optional( Type.Boolean({ description: "Show TUI to preview/edit before execution (default: true for chains, false for single/parallel). Implies sync mode.", }), ), // Solo agent overrides output: Type.Optional( Type.Any({ description: "Override output file for single agent (string), or false to disable (uses agent default if omitted). Absolute paths are used as-is; relative paths resolve against cwd.", }), ), skill: Type.Optional(SkillOverride), model: Type.Optional( Type.String({ description: "Override model for single agent (e.g. 'anthropic/claude-sonnet-4')", }), ), }); export const StatusParams = Type.Object({ dir: Type.Optional(Type.String({ description: "Async run directory (overrides id search)" })), id: Type.Optional(Type.String({ description: "Async run id or prefix" })), });