import type { AgentToolResult, ThinkingLevel } from "@earendil-works/pi-agent-core"; import type { Model, TextContent } from "@earendil-works/pi-ai/compat"; import type { TSchema, Static } from "typebox"; import type { WorkflowWriteLock } from "./locks"; export type ModelSelector = string | Model; export interface WorkflowLimits { maxDepth?: number; maxAgents?: number; concurrency?: number; timeoutMs?: number; maxOutputChars?: number; maxBashOutputChars?: number; } export interface WorkflowPlan { goal?: string; strategy?: "parallel" | "sequential"; agents: WorkflowAgentSpec[]; limits?: WorkflowLimits; synthesis?: false | WorkflowSynthesisSpec; } export interface WorkflowSynthesisSpec { enabled?: boolean; class?: string; task?: string; model?: ModelSelector; } export interface WorkflowAgentSpec { id?: string; class: string; task: string; model?: ModelSelector; tools?: string[]; context?: unknown; dependsOn?: string[]; canSpawn?: boolean; availableAgentClasses?: string[]; timeoutMs?: number; maxOutputChars?: number; metadata?: Record; } export interface AgentClassDefinition { name: string; description?: string; systemPrompt?: string | ((ctx: AgentPromptContext) => string); model?: ModelSelector; allowedModels?: string[]; tools?: string[]; canSpawn?: boolean; allowedChildClasses?: string[]; maxDepth?: number; thinkingLevel?: ThinkingLevel; temperature?: number; maxTokens?: number; resultInstructions?: string; } export interface AgentPromptContext { runId: string; agentId: string; parentAgentId?: string; depth: number; goal?: string; task: string; agentClass: AgentClassDefinition; availableTools: string[]; canSpawn: boolean; } export interface WorkflowToolContext { engine: WorkflowEngineLike; run: WorkflowRunState; agent: WorkflowAgentRuntime; signal?: AbortSignal; cwd: string; send(message: Omit): WorkflowMessage; receive(filter?: WorkflowReceiveFilter): WorkflowMessage[]; appendBlackboard(entry: Omit): WorkflowBlackboardEntry; readBlackboard(filter?: WorkflowBlackboardFilter): WorkflowBlackboardEntry[]; bash(command: string, options?: WorkflowBashOptions): Promise; acquireWriteLocks(paths: string[], options?: WorkflowLockOptions): Promise; getWriteLocks(): WorkflowWriteLock[]; spawn(plan: WorkflowPlan | string): Promise; } export interface WorkflowEngineLike { runPlan(plan: WorkflowPlan | string, host: WorkflowHostContext, parent?: WorkflowAgentRuntime): Promise; getRun(runId: string): WorkflowRunState | undefined; } export interface WorkflowToolDefinition { name: string; label?: string; description: string; parameters: TParams; executionMode?: "parallel" | "sequential"; execute(params: Static, ctx: WorkflowToolContext): Promise> | string | AgentToolResult; } export interface WorkflowHostContext { cwd: string; signal?: AbortSignal; modelRegistry?: { getAll(): Model[]; getAvailable(): Model[]; find(provider: string, modelId: string): Model | undefined; getApiKeyAndHeaders?(model: Model): Promise< | { ok: true; apiKey?: string; headers?: Record; env?: Record } | { ok: false; error: string } >; }; currentModel?: Model; sessionId?: string; workflowSettings?: WorkflowModelSettings; inheritedSkillContext?: string; exec?: (command: string, args: string[], options?: { cwd?: string; timeout?: number; signal?: AbortSignal }) => Promise; } export interface WorkflowModelSettings { fastModel?: string; defaultModel?: string; } export interface WorkflowEngineOptions { agentClasses?: AgentClassDefinition[]; tools?: WorkflowToolDefinition[]; defaultModel?: ModelSelector; fastModel?: ModelSelector; defaultLimits?: WorkflowLimits; } export interface WorkflowAgentRuntime { id: string; className: string; task: string; parentAgentId?: string; runId: string; depth: number; model?: Model; status: "pending" | "running" | "completed" | "failed" | "cancelled"; startedAt?: number; completedAt?: number; summary?: string; error?: string; usage?: WorkflowUsage; allowedChildClasses?: string[]; metadata?: Record; } export interface WorkflowRunState { id: string; goal?: string; status: "running" | "completed" | "failed" | "cancelled"; createdAt: number; completedAt?: number; parentRunId?: string; rootAgentId?: string; sessionId?: string; agents: Map; messages: WorkflowMessage[]; blackboard: WorkflowBlackboardEntry[]; events: WorkflowEvent[]; usage?: WorkflowUsage; result?: WorkflowRunResult; error?: string; } export interface WorkflowRunResult { runId: string; status: WorkflowRunState["status"]; goal?: string; results: WorkflowAgentResult[]; synthesis?: string; messages: WorkflowMessage[]; blackboard: WorkflowBlackboardEntry[]; events: WorkflowEvent[]; usage?: WorkflowUsage; error?: string; } export interface WorkflowAgentResult { id: string; className: string; task: string; status: WorkflowAgentRuntime["status"]; model?: string; output?: string; error?: string; usage?: WorkflowUsage; startedAt?: number; completedAt?: number; } export interface WorkflowUsage { input: number; output: number; cacheRead: number; cacheWrite: number; totalTokens: number; cost?: number; } export interface WorkflowMessage { id: string; runId: string; from: string; to?: string; channel?: string; text: string; data?: unknown; createdAt: number; } export interface WorkflowReceiveFilter { to?: string; from?: string; channel?: string; sinceId?: string; limit?: number; includeBroadcast?: boolean; } export interface WorkflowBlackboardEntry { id: string; runId: string; agentId: string; kind: string; text: string; data?: unknown; createdAt: number; } export interface WorkflowBlackboardFilter { kind?: string; agentId?: string; sinceId?: string; limit?: number; } export interface WorkflowEvent { id: string; runId: string; agentId?: string; type: string; message: string; data?: unknown; createdAt: number; } export interface WorkflowLockOptions { cwd?: string; timeoutMs?: number; reason?: string; } export interface WorkflowBashOptions { cwd?: string; timeoutMs?: number; maxOutputChars?: number; /** Paths this command may create, modify, or delete. Read-only commands should omit this. */ writePaths?: string[]; /** How long to wait for conflicting write locks before failing. Defaults to failing immediately. */ lockTimeoutMs?: number; } export interface WorkflowBashResult { stdout: string; stderr: string; code: number; killed?: boolean; } export function defineAgentClass(definition: AgentClassDefinition): AgentClassDefinition { return definition; } export function defineWorkflowTool( definition: WorkflowToolDefinition, ): WorkflowToolDefinition { return definition; } export function textResult(text: string, details?: TDetails): AgentToolResult { return { content: [{ type: "text", text } satisfies TextContent], details: details as TDetails, }; }