import type { Message, SimpleStreamOptions, ThinkingBudgets, Transport } from '@earendil-works/pi-ai'; import { z } from 'zod/v4'; import type { IStreamLogger } from './PSAgentLogger'; import { PSAgentTask } from './PSAgentTask'; import { PSAgentTool } from './PSAgentTool'; import type { AfterToolCallContext, AfterToolCallResult, AgentEvent, AgentLoopTurnUpdate, AgentMessage, BeforeToolCallContext, BeforeToolCallResult, QueueMode, StreamFn, ToolExecutionMode } from './agent-core/src/types.js'; /** * Configuration for `PSAgent` running in **remote** mode (default). * * This is the base config used by all consumers. It does not expose any * local-mode options — those live exclusively on {@link PSAgentLocalConfig}. */ export interface PSAgentConfig { agent_class?: 'LlmAgent' | 'LoopAgent' | 'ParallelAgent' | 'SequentialAgent'; name: string; description: string; instruction: string; sub_agents?: PSAgentConfig[]; additional_properties?: Record; examples?: string[]; model?: string; llm_config?: { max_completion_tokens?: number; context_window?: number; /** @default 50 */ max_specific_tool_calls?: number; reasoning_effort?: 'minimal' | 'low' | 'medium' | 'high'; /** 0–1 */ temperature?: number; /** 0–1 */ top_p?: number; verbosity?: 'low' | 'medium' | 'high'; /** Enable prompt caching. @default true */ enable_prompt_caching?: boolean; /** Prompt caching control for local LLM calls. `enabled` defaults to true and `ttl` defaults to '1h'. */ prompt_caching?: { enabled?: boolean; ttl?: '5m' | '1h'; }; /** Cache the system prompt. @default true when enable_prompt_caching is true */ cache_system_prompt?: boolean; /** Message indices to set cache breakpoints at (Anthropic models). */ cache_breakpoints?: number[]; } & Record; input_schema?: z.core.JSONSchema.BaseSchema; output_schema?: z.core.JSONSchema.BaseSchema; show_plan?: boolean; hitl_for_plan?: boolean; tools?: PSAgentTool[]; tasks?: PSAgentTask[]; token?: string; getToken?: () => Promise; /** * Callable invoked before every LLM call in local mode to supply fresh non-auth headers. * Called once per LLM request — suitable for short-lived headers such as app tokens and * trace IDs that expire faster than the agent's lifetime. * * **`Authorization` must NOT be returned from this callable.** Auth is handled exclusively * by `getToken`/`getApiKey` so that per-call bearer-token refresh works correctly. * Returning `Authorization` here would re-introduce the defaultHeaders override bug. * * Errors thrown by this callable are caught and treated as `{}` (non-fatal) so the * LLM call proceeds with the headers already present on `model.headers`. * * Only used when `local: true`. Has no effect in remote mode. */ getHeaders?: () => Promise>; additional_headers?: Record; evaluation?: { run_evaluator: boolean; reference_correlation_ids: string[] | null; }; /** Optional logger forwarded to execution. */ log?: IStreamLogger; /** A2A server port. @default 41241 */ port?: number; } /** * Additional options available exclusively when `local: true` is set. * * Every field mirrors its counterpart in `AgentOptions` from `agent-core` * so callers using the local path never need a nested options bag. * * These fields are **not** present on {@link PSAgentConfig} and will not * appear in intellisense for remote-mode consumers. */ export interface PSAgentLocalOptions { /** Override the stream function used by the local Agent. Defaults to `streamSimple`. */ streamFn?: StreamFn; /** Preferred transport forwarded to the stream function. */ transport?: Transport; /** Convert AgentMessages to the provider Message format before each LLM call. */ convertToLlm?: (messages: AgentMessage[]) => Message[] | Promise; /** Transform the full context (e.g. apply RAG, summarise) before each LLM call. */ transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise; /** Resolve an API key for a given provider at runtime. */ getApiKey?: (provider: string) => Promise | string | undefined; /** Called with each raw streaming payload from the provider. */ onPayload?: SimpleStreamOptions['onPayload']; /** Called once the full provider response object is available. */ onResponse?: SimpleStreamOptions['onResponse']; /** Called before every tool execution; can override or block the call. */ beforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise; /** Called after every tool execution; can override the result. */ afterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise; /** Called before each new agent loop turn; can inject messages or mutate state. */ prepareNextTurn?: (signal?: AbortSignal) => Promise | AgentLoopTurnUpdate | undefined; /** Session identifier forwarded to providers for cache-aware backends. */ sessionId?: string; /** Per-level thinking token budgets forwarded to the stream function. */ thinkingBudgets?: ThinkingBudgets; /** Optional cap for provider-requested retry delays (ms). */ maxRetryDelayMs?: number; /** How to execute multiple tool calls in one assistant turn. @default "parallel" */ toolExecution?: ToolExecutionMode; /** How queued steering messages are drained. @default "one-at-a-time" */ steeringMode?: QueueMode; /** How queued follow-up messages are drained. @default "one-at-a-time" */ followUpMode?: QueueMode; /** * Listeners subscribed to the local Agent's lifecycle events. * Each entry is registered via `agent.subscribe(listener)` before the first prompt. */ localListeners?: Array<(event: AgentEvent, signal: AbortSignal) => Promise | void>; } /** * Configuration for `PSAgent` running in **local** mode (`local: true`). * * Extends {@link PSAgentConfig} with all local-only options from * {@link PSAgentLocalOptions}. The `local` discriminant is required and * must be `true`. * * Remote consumers who do not set `local: true` will never see these * additional fields in their config type. */ export type PSAgentLocalConfig = PSAgentConfig & PSAgentLocalOptions & { /** Must be `true` to enable local in-process execution via `agent-core`. */ local: true; }; /** * The full config type accepted by the `PSAgent` constructor. * * - When `local` is absent or `false`, only {@link PSAgentConfig} fields are valid. * - When `local: true`, all {@link PSAgentLocalConfig} fields become available. * * Use this type when writing helpers or utilities that accept either mode. */ export type PSAgentConfigWithLocal = PSAgentConfig | PSAgentLocalConfig; /** * @deprecated Use {@link PSAgentLocalOptions} instead. * Kept for backward compatibility with existing imports. */ export type LocalAgentOptions = PSAgentLocalOptions & { local?: boolean; };