@caupulican/pi-agent-core 0.91.4 → 0.93.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-loop.d.ts +2 -1
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +52 -96
- package/dist/agent-loop.js.map +1 -1
- package/dist/agent.d.ts +4 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +2 -0
- package/dist/agent.js.map +1 -1
- package/dist/provider-request-planner.d.ts +2 -0
- package/dist/provider-request-planner.d.ts.map +1 -1
- package/dist/provider-request-planner.js +7 -1
- package/dist/provider-request-planner.js.map +1 -1
- package/dist/tool-failure-recovery-protocol.d.ts +0 -7
- package/dist/tool-failure-recovery-protocol.d.ts.map +1 -1
- package/dist/tool-failure-recovery-protocol.js +1 -25
- package/dist/tool-failure-recovery-protocol.js.map +1 -1
- package/dist/types.d.ts +26 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -1
- package/dist/types.js.map +1 -1
- package/package.json +22 -2
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA0OA;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AA4U1C,MAAM,qCAAqC,GAAG,MAAM,CAAC,mCAAmC,CAAC,CAAC;AAO1F,mGAAmG;AACnG,MAAM,UAAU,uCAAuC;IACtD,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,qCAAqC,CAAC,EAAE,IAAa,EAAE,CAAC,CAAC;AAClF,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,mCAAmC,CAAC,KAAc;IACjE,OAAO,CACN,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACpB,KAA+D,CAAC,qCAAqC,CAAC,KAAK,IAAI,CAChH,CAAC;AACH,CAAC","sourcesContent":["import type {\n\tApi,\n\tAssistantMessage,\n\tAssistantMessageEvent,\n\tContext,\n\tImageContent,\n\tMessage,\n\tModel,\n\tSimpleStreamOptions,\n\tstreamSimple,\n\tTextContent,\n\tTool,\n\tToolArgumentValidationTelemetryEvent,\n\tToolResultMessage,\n\tUsage,\n} from \"@caupulican/pi-ai\";\nimport type { Static, TSchema } from \"typebox\";\n\n/**\n * Stream function used by the agent loop.\n *\n * Contract:\n * - Must not throw or return a rejected promise for request/model/runtime failures.\n * - Must return an AssistantMessageEventStream.\n * - Failures must be encoded in the returned stream via protocol events and a\n * final AssistantMessage with stopReason \"error\" or \"aborted\" and errorMessage.\n */\nexport type StreamFn = (\n\t...args: Parameters<typeof streamSimple>\n) => ReturnType<typeof streamSimple> | Promise<ReturnType<typeof streamSimple>>;\n\n/**\n * Configuration for how tool calls from a single assistant message are executed.\n *\n * - \"sequential\": each tool call is prepared, executed, and finalized before the next one starts.\n * - \"parallel\": tool calls are prepared and executed in bounded concurrent accounting waves.\n * Each wave updates failure-recovery state before later calls launch. `tool_execution_end` is\n * emitted in completion order within each wave, while tool-result artifacts remain in source order.\n */\nexport type ToolExecutionMode = \"sequential\" | \"parallel\";\n\n/**\n * Controls how many queued user messages are injected when the agent loop reaches a queue drain point.\n *\n * - \"all\": drain and inject every queued message at that point.\n * - \"one-at-a-time\": drain and inject only the oldest queued message, leaving the rest queued for later drain points.\n */\nexport type QueueMode = \"all\" | \"one-at-a-time\";\n\n/** A single tool call content block emitted by an assistant message. */\nexport type AgentToolCall = Extract<AssistantMessage[\"content\"][number], { type: \"toolCall\" }>;\n\n/**\n * Result returned from `beforeToolCall`.\n *\n * Returning `{ block: true }` prevents the tool from executing. The loop emits an error tool result instead.\n * `reason` becomes the text shown in that error result. If omitted, a default blocked message is used.\n */\nexport interface BeforeToolCallResult {\n\tblock?: boolean;\n\treason?: string;\n}\n\n/**\n * Partial override returned from `afterToolCall`.\n *\n * Merge semantics are field-by-field:\n * - `content`: if provided, replaces the tool result content array in full\n * - `details`: if provided, replaces the tool result details value in full\n * - `usage`: if provided, replaces provider usage reported by the tool\n * - `isError`: if provided, replaces the tool result error flag\n * - `terminate`: if provided, replaces the early-termination hint\n *\n * Omitted fields keep the original executed tool result values.\n * There is no deep merge for `content` or `details`.\n */\nexport interface AfterToolCallResult {\n\tcontent?: (TextContent | ImageContent)[];\n\tdetails?: unknown;\n\tusage?: Usage;\n\tisError?: boolean;\n\t/**\n\t * Hint that the agent should stop after the current tool batch.\n\t * Early termination only happens when every finalized tool result in the batch sets this to true.\n\t */\n\tterminate?: boolean;\n}\n\n/** Context passed to `beforeToolCall`. */\nexport interface BeforeToolCallContext {\n\t/** The assistant message that requested the tool call. */\n\tassistantMessage: AssistantMessage;\n\t/** The raw tool call block from `assistantMessage.content`. */\n\ttoolCall: AgentToolCall;\n\t/** Validated tool arguments for the target tool schema. */\n\targs: unknown;\n\t/** Current agent context at the time the tool call is prepared. */\n\tcontext: AgentContext;\n}\n\n/** Context passed to `afterToolCall`. */\nexport interface AfterToolCallContext {\n\t/** The assistant message that requested the tool call. */\n\tassistantMessage: AssistantMessage;\n\t/** The raw tool call block from `assistantMessage.content`. */\n\ttoolCall: AgentToolCall;\n\t/** Validated tool arguments for the target tool schema. */\n\targs: unknown;\n\t/** The executed tool result before any `afterToolCall` overrides are applied. */\n\tresult: AgentToolResult<any>;\n\t/** Whether the executed tool result is currently treated as an error. */\n\tisError: boolean;\n\t/** Current agent context at the time the tool call is finalized. */\n\tcontext: AgentContext;\n}\n\n/** Policy-finalized result of a tool call that outlived its foreground turn. */\nexport interface BackgroundToolCallCompletion {\n\t/** Original tool call identity. */\n\ttoolCall: AgentToolCall;\n\t/** Result after the normal `afterToolCall` policy boundary has run. */\n\tresult: AgentToolResult<any>;\n\t/** Final error classification after policy overrides. */\n\tisError: boolean;\n}\n\n/** Context offered to a host when a prepared tool call crosses its foreground latency budget. */\nexport interface BackgroundToolCallContext extends BeforeToolCallContext {\n\t/** Configured foreground latency budget that elapsed. */\n\telapsedMs: number;\n\t/** Event-driven terminal signal for the real, policy-finalized execution. */\n\tcompletion: Promise<BackgroundToolCallCompletion>;\n\t/** Abort only this detached execution. */\n\tcancel(): void;\n}\n\n/** Immediate foreground result returned when the host accepts ownership of a slow tool call. */\nexport interface BackgroundToolCallHandoff {\n\t/** Bounded result telling the model how to address the session-owned task. */\n\tresult: AgentToolResult<any>;\n\t/** Optional foreground error classification. Defaults to `result.isError === true`. */\n\tisError?: boolean;\n}\n\n/** Context passed to `shouldStopAfterTurn`. */\nexport interface ShouldStopAfterTurnContext {\n\t/** The assistant message that completed the turn. */\n\tmessage: AssistantMessage;\n\t/** Tool result messages passed to the preceding `turn_end` event. */\n\ttoolResults: ToolResultMessage[];\n\t/** Current agent context after the turn's assistant message and tool results have been appended. */\n\tcontext: AgentContext;\n\t/** Messages that this loop invocation will return if it exits at this point. Prompt runs include the initial prompt messages; continuation runs do not include pre-existing context messages. */\n\tnewMessages: AgentMessage[];\n}\n\n/** Replacement runtime state used by the agent loop before starting another provider request. */\nexport interface AgentLoopTurnUpdate {\n\t/** Context for the next provider request. */\n\tcontext?: AgentContext;\n\t/** Model for the next provider request. */\n\tmodel?: Model<any>;\n\t/** Thinking level for the next provider request. */\n\tthinkingLevel?: ThinkingLevel;\n}\n\nexport interface ToolValidationEscalationEvent {\n\ttool: string;\n\tsignature: string;\n\trepeats: number;\n\tmodel: string;\n\tprovider: string;\n}\n\nexport interface PrepareNextTurnContext extends ShouldStopAfterTurnContext {}\n\n/** Input for one replay-safe context-planning attempt. */\nexport interface AgentContextPlanRequest {\n\t/** Sanitized durable history used as the compactable portion of this request. */\n\tmessages: AgentMessage[];\n\t/** Zero-based admission generation; freshness-only retries repeat the same value. */\n\tattempt: number;\n}\n\n/**\n * Replay-safe context plan. `messages` is compactable history; `transientMessages` is mandatory,\n * request-local context that compaction must never summarize or drop.\n */\nexport interface AgentContextPlan {\n\tmessages: AgentMessage[];\n\ttransientMessages?: AgentMessage[];\n\t/** Cheap freshness check immediately before admission/commit. */\n\tisCurrent?: () => boolean;\n\t/**\n\t * Pure final validation for expensive projections. Return false to discard and replan; do not\n\t * mutate durable state here.\n\t */\n\tprepareCommit?: () => boolean;\n\t/**\n\t * Apply lifecycle side effects after every composed validator passed. Synchronous, infallible by\n\t * contract, and must not change the planned payload.\n\t */\n\tcommit?: () => void;\n\t/** Release request-local planning resources when a plan is not accepted. */\n\tdiscard?: () => void;\n}\n\n/** Provider-ready request inspected after full materialization and immediately before transport. */\nexport interface RequestPreflightContext {\n\tmodel: Model<Api>;\n\tcontext: Context;\n\t/** Current owner-selected output cap before request-local narrowing. */\n\tmaxTokens?: number;\n}\n\n/** Request-local limits. A returned output cap can only narrow the current owner/model limit. */\nexport interface RequestPreflightResult {\n\tmaxTokens?: number;\n}\n\n/** Exact materialization offered to the host-owned compaction/admission gate. */\nexport interface ProviderRequestAdmissionContext extends RequestPreflightContext {\n\t/** Agent-level request snapshot from which this materialization was planned. */\n\tsourceContext: AgentContext;\n\t/** Provider context containing only the non-compactable system/tool/transient envelope. */\n\tnonCompactableContext: Context;\n\t/** Zero-based admission generation; increments only after an accepted history replan. */\n\tattempt: number;\n}\n\nexport type ProviderRequestAdmissionResult =\n\t| { action: \"send\"; maxTokens?: number }\n\t| { action: \"replan\"; context: AgentContext };\n\n/**\n * Default runaway-loop backstop: a single identical tool-call signature recurring this many times\n * within a sliding window (2×) stops the loop. Generous enough that legitimate long/varied work never\n * trips it, but bounds the cost of a model wedged repeating one failing call forever.\n */\nexport const DEFAULT_MAX_STALL_TURNS = 12;\n\nexport interface AgentLoopConfig extends SimpleStreamOptions {\n\tmodel: Model<any>;\n\n\t/**\n\t * Converts AgentMessage[] to LLM-compatible Message[] before each LLM call.\n\t *\n\t * Each AgentMessage must be converted to a UserMessage, AssistantMessage, or ToolResultMessage\n\t * that the LLM can understand. AgentMessages that cannot be converted (e.g., UI-only notifications,\n\t * status messages) should be filtered out.\n\t *\n\t * Contract: must not throw or reject. Return a safe fallback value instead.\n\t * Throwing interrupts the low-level agent loop without producing a normal event sequence.\n\t *\n\t * @example\n\t * ```typescript\n\t * convertToLlm: (messages) => messages.flatMap(m => {\n\t * if (m.role === \"custom\") {\n\t * // Convert custom message to user message\n\t * return [{ role: \"user\", content: m.content, timestamp: m.timestamp }];\n\t * }\n\t * if (m.role === \"notification\") {\n\t * // Filter out UI-only messages\n\t * return [];\n\t * }\n\t * // Pass through standard LLM messages\n\t * return [m];\n\t * })\n\t * ```\n\t */\n\tconvertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\n\t/**\n\t * Optional transform applied to the context before `convertToLlm`.\n\t *\n\t * Use this for operations that work at the AgentMessage level:\n\t * - Context window management (pruning old messages)\n\t * - Injecting context from external sources\n\t *\n\t * Contract: must not throw or reject. Return the original messages or another\n\t * safe fallback value instead.\n\t *\n\t * @example\n\t * ```typescript\n\t * transformContext: async (messages) => {\n\t * if (estimateTokens(messages) > MAX_TOKENS) {\n\t * return pruneOldMessages(messages);\n\t * }\n\t * return messages;\n\t * }\n\t * ```\n\t */\n\ttransformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\n\t/**\n\t * Preferred two-phase replacement for `transformContext`. Planning is replay-safe and may run\n\t * again after compaction or invalidation; only the accepted plan's `commit` is invoked.\n\t */\n\tplanContext?: (request: AgentContextPlanRequest, signal?: AbortSignal) => Promise<AgentContextPlan>;\n\n\t/**\n\t * Host-owned admission gate over the complete provider-visible materialization. It may accept the\n\t * request or compact durable history and return a replacement source context for replanning.\n\t */\n\tadmitProviderRequest?: (\n\t\trequest: ProviderRequestAdmissionContext,\n\t\tsignal?: AbortSignal,\n\t) => ProviderRequestAdmissionResult | Promise<ProviderRequestAdmissionResult>;\n\n\t/**\n\t * Runs after admission against the exact transport-ready context, immediately before every provider request.\n\t *\n\t * Use this for request-local budget/authority checks whose state can change between tool turns.\n\t * Throwing prevents transport. A returned `maxTokens` must be a positive safe integer and can\n\t * only narrow the current owner/model output limit; it never mutates the persistent loop config.\n\t */\n\trequestPreflight?: (\n\t\tcontext: RequestPreflightContext,\n\t\tsignal?: AbortSignal,\n\t) => RequestPreflightResult | undefined | Promise<RequestPreflightResult | undefined>;\n\n\t/**\n\t * Resolve the reasoning effort after context transformation and immediately before the provider\n\t * request. This supports request-local policy decisions that must not mutate persisted agent state.\n\t */\n\tresolveRequestReasoning?: (\n\t\treasoning: SimpleStreamOptions[\"reasoning\"],\n\t\trequest: { model: Model<Api>; context: Context; maxTokens?: number },\n\t) => SimpleStreamOptions[\"reasoning\"];\n\n\t/**\n\t * Resolves an API key dynamically for each LLM call.\n\t *\n\t * Useful for short-lived OAuth tokens (e.g., GitHub Copilot) that may expire\n\t * during long-running tool execution phases.\n\t *\n\t * Contract: must not throw or reject. Return undefined when no key is available.\n\t */\n\tgetApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\n\t/**\n\t * Called after each turn fully completes and `turn_end` has been emitted.\n\t *\n\t * If it returns true, the loop emits `agent_end` and exits before polling steering or follow-up queues,\n\t * without starting another LLM call. The current assistant response and any tool executions finish normally.\n\t *\n\t * Use this to request a graceful stop after the current turn, e.g. before context gets too full.\n\t *\n\t * Contract: must not throw or reject. Throwing interrupts the low-level agent loop without producing a normal event sequence.\n\t */\n\tshouldStopAfterTurn?: (context: ShouldStopAfterTurnContext) => boolean | Promise<boolean>;\n\n\t/**\n\t * Runaway-loop backstop. A model stuck repeating the SAME tool call (identical name + arguments) —\n\t * because a tool keeps erroring, or it is confused/oscillating — makes no progress yet keeps\n\t * consuming tokens indefinitely (history grows every turn). This bounds that cost: if one tool-call\n\t * signature recurs at least this many times within a sliding window (2×), the loop stops gracefully\n\t * (emits `agent_end`). It counts ONLY turns that issued tool calls and keys on exact name+arguments,\n\t * so legitimate long or varied agentic work never trips it. `0` disables the backstop.\n\t * Default: {@link DEFAULT_MAX_STALL_TURNS}.\n\t */\n\tmaxStallTurns?: number;\n\n\t/**\n\t * Observability hook fired once if the {@link maxStallTurns} runaway backstop trips, just before the\n\t * loop stops. Lets the host surface/log why the run ended. Must not throw.\n\t */\n\tonRunawayStop?: (info: { signature: string; repeats: number }) => void;\n\n\t/**\n\t * Called after `turn_end` and before the loop decides whether another provider request should start.\n\t * Return replacement context/model/thinking state to affect the next turn in this run.\n\t * Return undefined to keep using the current context/config.\n\t */\n\tprepareNextTurn?: (\n\t\tcontext: PrepareNextTurnContext,\n\t) => AgentLoopTurnUpdate | undefined | Promise<AgentLoopTurnUpdate | undefined>;\n\n\t/**\n\t * Returns steering messages to inject into the conversation mid-run.\n\t *\n\t * Called after the current assistant turn finishes executing its tool calls, unless `shouldStopAfterTurn` exits first.\n\t * If messages are returned, they are added to the context before the next LLM call.\n\t * Tool calls from the current assistant message are not skipped.\n\t *\n\t * Use this for \"steering\" the agent while it's working.\n\t *\n\t * Contract: must not throw or reject. Return [] when no steering messages are available.\n\t */\n\tgetSteeringMessages?: () => Promise<AgentMessage[]>;\n\n\t/**\n\t * Returns follow-up messages to process after the agent would otherwise stop.\n\t *\n\t * Called when the agent has no more tool calls and no steering messages.\n\t * If messages are returned, they're added to the context and the agent\n\t * continues with another turn.\n\t *\n\t * Use this for follow-up messages that should wait until the agent finishes.\n\t *\n\t * Contract: must not throw or reject. Return [] when no follow-up messages are available.\n\t */\n\tgetFollowUpMessages?: () => Promise<AgentMessage[]>;\n\n\t/**\n\t * Tool execution mode.\n\t * - \"sequential\": execute tool calls one by one\n\t * - \"parallel\": preflight and execute tool calls in bounded concurrent accounting waves;\n\t * update recovery state between waves, emit `tool_execution_end` in completion order within\n\t * each wave, then emit tool-result message artifacts in assistant source order\n\t *\n\t * Default: \"parallel\"\n\t */\n\ttoolExecution?: ToolExecutionMode;\n\n\t/** Disable in-band tool repair teaching notes. Default: enabled. */\n\ttoolArgumentTeachEnabled?: boolean;\n\n\t/**\n\t * Observe tool argument validation outcomes. Events contain only shape metadata\n\t * (outcome, model/provider/tool, failure modes, repairs) and never argument values.\n\t */\n\tonToolArgumentValidation?: (event: ToolArgumentValidationTelemetryEvent) => void;\n\n\t/**\n\t * Number of consecutive identical validation bounces before adding full schema/example feedback\n\t * and notifying the host. Set to 0 to disable. Default: 3.\n\t */\n\ttoolValidationEscalationThreshold?: number;\n\n\t/**\n\t * Fired when a repeated identical tool validation failure reaches the escalation threshold.\n\t * Hosts with model routers can use this signal to move the next turn off a cheap route.\n\t */\n\tonToolValidationEscalation?: (event: ToolValidationEscalationEvent) => void;\n\n\t/**\n\t * Called before a tool is executed, after arguments have been validated.\n\t *\n\t * Return `{ block: true }` to prevent execution. The loop emits an error tool result instead.\n\t * The hook receives the agent abort signal and is responsible for honoring it.\n\t */\n\tbeforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;\n\n\t/**\n\t * Called after a tool finishes executing, before `tool_execution_end` and tool-result message events are emitted.\n\t *\n\t * Return an `AfterToolCallResult` to override parts of the executed tool result:\n\t * - `content` replaces the full content array\n\t * - `details` replaces the full details payload\n\t * - `isError` replaces the error flag\n\t * - `terminate` replaces the early-termination hint\n\t *\n\t * Any omitted fields keep their original values. No deep merge is performed.\n\t * The hook receives the agent abort signal and is responsible for honoring it.\n\t */\n\tafterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;\n\n\t/**\n\t * Foreground latency budget for prepared tool calls. When it elapses, `handoffToolCall` may\n\t * transfer the still-running execution to a host-owned task. Disabled unless both fields exist.\n\t */\n\tbackgroundToolCallAfterMs?: number;\n\n\t/**\n\t * Synchronously accept ownership of a slow call. Returning a handoff lets the provider loop\n\t * continue with its bounded placeholder while `completion` still crosses `afterToolCall` once.\n\t * Returning `undefined` keeps waiting in the foreground.\n\t */\n\thandoffToolCall?: (context: BackgroundToolCallContext) => BackgroundToolCallHandoff | undefined;\n\n\t/**\n\t * Register a one-shot host request that asks an in-flight foreground call to cross the same\n\t * `handoffToolCall` boundary before its automatic latency budget elapses.\n\t */\n\tsubscribeToolCallHandoffRequest?: (toolCallId: string, request: () => void) => () => void;\n}\n\n/**\n * Thinking/reasoning level for models that support it.\n * Note: \"xhigh\", \"max\", and \"ultra\" are only supported by selected model families. \"ultra\" maps\n * to the model's maximum provider effort and reinforces proactive orchestration in capable hosts;\n * delegation can remain available at lower levels. Use model thinking-level metadata from\n * @caupulican/pi-ai to detect support for a concrete model.\n */\nexport type ThinkingLevel = \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\" | \"ultra\";\n\n/**\n * Extensible interface for custom app messages.\n * Apps can extend via declaration merging:\n *\n * @example\n * ```typescript\n * declare module \"@mariozechner/agent\" {\n * interface CustomAgentMessages {\n * artifact: ArtifactMessage;\n * notification: NotificationMessage;\n * }\n * }\n * ```\n */\nexport interface CustomAgentMessages {\n\t// Empty by default - apps extend via declaration merging\n}\n\n/**\n * AgentMessage: Union of LLM messages + custom messages.\n * This abstraction allows apps to add custom message types while maintaining\n * type safety and compatibility with the base LLM messages.\n */\nexport type AgentMessage = Message | CustomAgentMessages[keyof CustomAgentMessages];\n\n/**\n * Public agent state.\n *\n * `tools` and `messages` use accessor properties so implementations can copy\n * assigned arrays before storing them.\n */\nexport interface AgentState {\n\t/** System prompt sent with each model request. */\n\tsystemPrompt: string;\n\t/** Active model used for future turns. */\n\tmodel: Model<any>;\n\t/** Requested reasoning level for future turns. */\n\tthinkingLevel: ThinkingLevel;\n\t/** Available tools. Assigning a new array copies the top-level array. */\n\tset tools(tools: AgentTool<any>[]);\n\tget tools(): AgentTool<any>[];\n\t/** Conversation transcript. Assigning a new array copies the top-level array. */\n\tset messages(messages: AgentMessage[]);\n\tget messages(): AgentMessage[];\n\t/**\n\t * True while the agent is processing a prompt or continuation.\n\t *\n\t * This remains true until awaited `agent_end` listeners settle.\n\t */\n\treadonly isStreaming: boolean;\n\t/** Partial assistant message for the current streamed response, if any. */\n\treadonly streamingMessage?: AgentMessage;\n\t/** Tool call ids currently executing. */\n\treadonly pendingToolCalls: ReadonlySet<string>;\n\t/** Error message from the most recent failed or aborted assistant turn, if any. */\n\treadonly errorMessage?: string;\n}\n\n/** Final or partial result produced by a tool. */\nexport interface AgentToolResult<T> {\n\t/** Text or image content returned to the model. */\n\tcontent: (TextContent | ImageContent)[];\n\t/** Arbitrary structured details for logs or UI rendering. */\n\tdetails: T;\n\t/**\n\t * Marks a completed execution as a failure without throwing.\n\t *\n\t * The agent loop preserves the result long enough for `afterToolCall` to\n\t * inspect it, then converts the bounded diagnostic into its durable failure\n\t * record. Throwing remains valid for exceptional execution failures.\n\t */\n\tisError?: boolean;\n\t/** Provider usage spent inside this tool, for durable budget and cost accounting. */\n\tusage?: Usage;\n\t/**\n\t * Hint that the agent should stop after the current tool batch.\n\t * Early termination only happens when every finalized tool result in the batch sets this to true.\n\t */\n\tterminate?: boolean;\n}\n\n/** Callback used by tools to stream partial execution updates. */\nexport type AgentToolUpdateCallback<T = any> = (partialResult: AgentToolResult<T>) => void;\n\nconst AGENT_TOOL_FAILURE_RECOVERY_AUTHORITY = Symbol(\"AgentToolFailureRecoveryAuthority\");\n\n/** Opaque identity shared only by tool instances that act on the same authoritative backend. */\nexport interface AgentToolFailureRecoveryAuthority {\n\treadonly [AGENT_TOOL_FAILURE_RECOVERY_AUTHORITY]: true;\n}\n\n/** Create an unforgeable, process-local recovery authority for intentionally cooperating tools. */\nexport function createAgentToolFailureRecoveryAuthority(): AgentToolFailureRecoveryAuthority {\n\treturn Object.freeze({ [AGENT_TOOL_FAILURE_RECOVERY_AUTHORITY]: true as const });\n}\n\n/** Validate recovery authority values supplied by tool-owned contracts. */\nexport function isAgentToolFailureRecoveryAuthority(value: unknown): value is AgentToolFailureRecoveryAuthority {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t!Array.isArray(value) &&\n\t\t(value as { [AGENT_TOOL_FAILURE_RECOVERY_AUTHORITY]?: unknown })[AGENT_TOOL_FAILURE_RECOVERY_AUTHORITY] === true\n\t);\n}\n\n/** Exact, opaque state requirement shared only by tools that intentionally cooperate on recovery. */\nexport interface AgentToolFailureRecoveryTarget {\n\t/** Backend identity; equality is object identity and the harness never serializes it. */\n\tauthority: AgentToolFailureRecoveryAuthority;\n\t/** Stable semantic namespace owned by the declaring tools. The harness never interprets it. */\n\tkind: string;\n\t/** Exact resource/state identity within `kind`. The harness compares it byte-for-byte. */\n\tscope: string;\n}\n\n/** Bounded failure identity supplied to a failed tool's recovery contract. */\nexport interface AgentToolFailureRecoveryContext {\n\tfailureCode: string;\n}\n\n/**\n * One action a tool can actually perform for a declared failure target.\n *\n * A `correct` action teaches a materially changed operation and never unlocks an unchanged retry.\n * A `repair` action must emit exact evidence after success; only that evidence may unlock one probe.\n */\nexport type AgentToolFailureRecoveryAction<TParameters extends TSchema, TDetails> =\n\t| {\n\t\t\tkind: \"correct\";\n\t\t\tauthority: AgentToolFailureRecoveryAuthority;\n\t\t\ttargetKind: string;\n\t\t\tinstruction: string;\n\t }\n\t| {\n\t\t\tkind: \"repair\";\n\t\t\tauthority: AgentToolFailureRecoveryAuthority;\n\t\t\ttargetKind: string;\n\t\t\tinstruction: string;\n\t\t\tgetEvidence: (params: Static<TParameters>, result: AgentToolResult<TDetails>) => readonly string[];\n\t };\n\n/** Tool-owned failure targets and recovery actions. Undeclared behavior has no recovery authority. */\nexport interface AgentToolFailureRecoveryContract<TParameters extends TSchema, TDetails> {\n\t/** Derive exact recovery requirements from validated arguments and a classified failure. */\n\tgetFailureTargets?: (\n\t\tparams: Static<TParameters>,\n\t\tfailure: AgentToolFailureRecoveryContext,\n\t) => readonly AgentToolFailureRecoveryTarget[];\n\t/** Actions this tool can perform when it is present in the active tool surface. */\n\tactions?: readonly AgentToolFailureRecoveryAction<TParameters, TDetails>[];\n}\n\n/** Tool definition used by the agent runtime. */\nexport interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any> extends Tool<TParameters> {\n\t/** Human-readable label for UI display. */\n\tlabel: string;\n\t/** Compact provider-facing capability description. Execution keeps the full `description`. */\n\tproviderDescription?: string;\n\t/**\n\t * Optional compatibility shim for raw tool-call arguments before schema validation.\n\t * Must return an object that matches `TParameters`.\n\t */\n\tprepareArguments?: (args: unknown) => Static<TParameters>;\n\t/** Explicit failure-recovery authority; the agent loop never infers recovery from argument text. */\n\tfailureRecovery?: AgentToolFailureRecoveryContract<TParameters, TDetails>;\n\t/**\n\t * Execute the tool call. Throw for exceptional execution failures, or return\n\t * `{ isError: true }` with bounded diagnostic content for an expected\n\t * operation failure such as a non-zero subprocess exit.\n\t */\n\texecute: (\n\t\ttoolCallId: string,\n\t\tparams: Static<TParameters>,\n\t\tsignal?: AbortSignal,\n\t\tonUpdate?: AgentToolUpdateCallback<TDetails>,\n\t) => Promise<AgentToolResult<TDetails>>;\n\t/**\n\t * Per-tool execution mode override.\n\t * - \"sequential\": this tool must execute one at a time with other tool calls.\n\t * - \"parallel\": this tool can execute concurrently with other tool calls.\n\t *\n\t * If omitted, the default execution mode applies.\n\t */\n\texecutionMode?: ToolExecutionMode;\n}\n\n/** Context snapshot passed into the low-level agent loop. */\nexport interface AgentContext {\n\t/** System prompt included with the request. */\n\tsystemPrompt: string;\n\t/** Transcript visible to the model. */\n\tmessages: AgentMessage[];\n\t/** Tools available for this run. */\n\ttools?: AgentTool<any>[];\n}\n\n/**\n * Events emitted by the Agent for UI updates.\n *\n * `agent_end` is the last event emitted for a run, but awaited `Agent.subscribe()`\n * listeners for that event are still part of run settlement. The agent becomes\n * idle only after those listeners finish.\n */\nexport interface ToolCallRepairInfo {\n\trepaired: true;\n\trawArguments?: Record<string, unknown>;\n\tnotes?: string[];\n}\n\nexport type AgentEvent =\n\t// Agent lifecycle\n\t| { type: \"agent_start\" }\n\t| { type: \"agent_end\"; messages: AgentMessage[] }\n\t// Turn lifecycle - a turn is one assistant response + any tool calls/results\n\t| { type: \"turn_start\" }\n\t| { type: \"turn_end\"; message: AgentMessage; toolResults: ToolResultMessage[] }\n\t// Message lifecycle - emitted for user, assistant, and toolResult messages\n\t| { type: \"message_start\"; message: AgentMessage }\n\t// Only emitted for assistant messages during streaming\n\t| { type: \"message_update\"; message: AgentMessage; assistantMessageEvent: AssistantMessageEvent }\n\t| { type: \"message_end\"; message: AgentMessage }\n\t// Tool execution lifecycle\n\t| { type: \"tool_execution_start\"; toolCallId: string; toolName: string; args: any; repair?: ToolCallRepairInfo }\n\t| {\n\t\t\ttype: \"tool_execution_update\";\n\t\t\ttoolCallId: string;\n\t\t\ttoolName: string;\n\t\t\targs: any;\n\t\t\tpartialResult: any;\n\t\t\trepair?: ToolCallRepairInfo;\n\t }\n\t| {\n\t\t\ttype: \"tool_execution_end\";\n\t\t\ttoolCallId: string;\n\t\t\ttoolName: string;\n\t\t\tresult: any;\n\t\t\tisError: boolean;\n\t\t\trepair?: ToolCallRepairInfo;\n\t };\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAsPA;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAC1C,kGAAkG;AAClG,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,CAAC;AAoV7C,MAAM,qCAAqC,GAAG,MAAM,CAAC,mCAAmC,CAAC,CAAC;AAO1F,mGAAmG;AACnG,MAAM,UAAU,uCAAuC;IACtD,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,qCAAqC,CAAC,EAAE,IAAa,EAAE,CAAC,CAAC;AAClF,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,mCAAmC,CAAC,KAAc;IACjE,OAAO,CACN,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACpB,KAA+D,CAAC,qCAAqC,CAAC,KAAK,IAAI,CAChH,CAAC;AACH,CAAC","sourcesContent":["import type {\n\tApi,\n\tAssistantMessage,\n\tAssistantMessageEvent,\n\tContext,\n\tImageContent,\n\tMessage,\n\tModel,\n\tSimpleStreamOptions,\n\tstreamSimple,\n\tTextContent,\n\tTool,\n\tToolArgumentValidationTelemetryEvent,\n\tToolResultMessage,\n\tUsage,\n} from \"@caupulican/pi-ai\";\nimport type { Static, TSchema } from \"typebox\";\n\n/**\n * Stream function used by the agent loop.\n *\n * Contract:\n * - Must not throw or return a rejected promise for request/model/runtime failures.\n * - Must return an AssistantMessageEventStream.\n * - Failures must be encoded in the returned stream via protocol events and a\n * final AssistantMessage with stopReason \"error\" or \"aborted\" and errorMessage.\n */\nexport type StreamFn = (\n\t...args: Parameters<typeof streamSimple>\n) => ReturnType<typeof streamSimple> | Promise<ReturnType<typeof streamSimple>>;\n\n/**\n * Configuration for how tool calls from a single assistant message are executed.\n *\n * - \"sequential\": each tool call is prepared, executed, and finalized before the next one starts.\n * - \"parallel\": tool calls are prepared and executed in bounded concurrent accounting waves.\n * Each wave updates failure-recovery state before later calls launch. `tool_execution_end` is\n * emitted in completion order within each wave, while tool-result artifacts remain in source order.\n */\nexport type ToolExecutionMode = \"sequential\" | \"parallel\";\n\n/**\n * Controls how many queued user messages are injected when the agent loop reaches a queue drain point.\n *\n * - \"all\": drain and inject every queued message at that point.\n * - \"one-at-a-time\": drain and inject only the oldest queued message, leaving the rest queued for later drain points.\n */\nexport type QueueMode = \"all\" | \"one-at-a-time\";\n\n/** A single tool call content block emitted by an assistant message. */\nexport type AgentToolCall = Extract<AssistantMessage[\"content\"][number], { type: \"toolCall\" }>;\n\n/**\n * Result returned from `beforeToolCall`.\n *\n * Returning `{ block: true }` prevents the tool from executing. The loop emits an error tool result instead.\n * `reason` becomes the text shown in that error result. If omitted, a default blocked message is used.\n */\nexport interface BeforeToolCallResult {\n\tblock?: boolean;\n\treason?: string;\n}\n\n/**\n * Partial override returned from `afterToolCall`.\n *\n * Merge semantics are field-by-field:\n * - `content`: if provided, replaces the tool result content array in full\n * - `details`: if provided, replaces the tool result details value in full\n * - `usage`: if provided, replaces provider usage reported by the tool\n * - `isError`: if provided, replaces the tool result error flag\n * - `terminate`: if provided, replaces the early-termination hint\n *\n * Omitted fields keep the original executed tool result values.\n * There is no deep merge for `content` or `details`.\n */\nexport interface AfterToolCallResult {\n\tcontent?: (TextContent | ImageContent)[];\n\tdetails?: unknown;\n\tusage?: Usage;\n\tisError?: boolean;\n\t/**\n\t * Hint that the agent should stop after the current tool batch.\n\t * Early termination only happens when every finalized tool result in the batch sets this to true.\n\t */\n\tterminate?: boolean;\n}\n\n/** Context passed to `beforeToolCall`. */\nexport interface BeforeToolCallContext {\n\t/** The assistant message that requested the tool call. */\n\tassistantMessage: AssistantMessage;\n\t/** The raw tool call block from `assistantMessage.content`. */\n\ttoolCall: AgentToolCall;\n\t/** Validated tool arguments for the target tool schema. */\n\targs: unknown;\n\t/** Current agent context at the time the tool call is prepared. */\n\tcontext: AgentContext;\n}\n\n/** Context passed to `afterToolCall`. */\nexport interface AfterToolCallContext {\n\t/** The assistant message that requested the tool call. */\n\tassistantMessage: AssistantMessage;\n\t/** The raw tool call block from `assistantMessage.content`. */\n\ttoolCall: AgentToolCall;\n\t/** Validated tool arguments for the target tool schema. */\n\targs: unknown;\n\t/** The executed tool result before any `afterToolCall` overrides are applied. */\n\tresult: AgentToolResult<any>;\n\t/** Whether the executed tool result is currently treated as an error. */\n\tisError: boolean;\n\t/** Current agent context at the time the tool call is finalized. */\n\tcontext: AgentContext;\n}\n\n/** Policy-finalized result of a tool call that outlived its foreground turn. */\nexport interface BackgroundToolCallCompletion {\n\t/** Original tool call identity. */\n\ttoolCall: AgentToolCall;\n\t/** Result after the normal `afterToolCall` policy boundary has run. */\n\tresult: AgentToolResult<any>;\n\t/** Final error classification after policy overrides. */\n\tisError: boolean;\n}\n\n/** Context offered to a host when a prepared tool call crosses its foreground latency budget. */\nexport interface BackgroundToolCallContext extends BeforeToolCallContext {\n\t/** Configured foreground latency budget that elapsed. */\n\telapsedMs: number;\n\t/** Event-driven terminal signal for the real, policy-finalized execution. */\n\tcompletion: Promise<BackgroundToolCallCompletion>;\n\t/** Abort only this detached execution. */\n\tcancel(): void;\n}\n\n/** Immediate foreground result returned when the host accepts ownership of a slow tool call. */\nexport interface BackgroundToolCallHandoff {\n\t/** Bounded result telling the model how to address the session-owned task. */\n\tresult: AgentToolResult<any>;\n\t/** Optional foreground error classification. Defaults to `result.isError === true`. */\n\tisError?: boolean;\n}\n\n/** Context passed to `shouldStopAfterTurn`. */\nexport interface ShouldStopAfterTurnContext {\n\t/** The assistant message that completed the turn. */\n\tmessage: AssistantMessage;\n\t/** Tool result messages passed to the preceding `turn_end` event. */\n\ttoolResults: ToolResultMessage[];\n\t/** Current agent context after the turn's assistant message and tool results have been appended. */\n\tcontext: AgentContext;\n\t/** Messages that this loop invocation will return if it exits at this point. Prompt runs include the initial prompt messages; continuation runs do not include pre-existing context messages. */\n\tnewMessages: AgentMessage[];\n}\n\n/** Replacement runtime state used by the agent loop before starting another provider request. */\nexport interface AgentLoopTurnUpdate {\n\t/** Context for the next provider request. */\n\tcontext?: AgentContext;\n\t/** Model for the next provider request. */\n\tmodel?: Model<any>;\n\t/** Thinking level for the next provider request. */\n\tthinkingLevel?: ThinkingLevel;\n}\n\nexport type AgentRunawayStopReason = \"repeated_tool_call\" | \"provider_turn_limit\";\n\n/** Semantic cause and evidence for a host-enforced runaway/cost stop. */\nexport interface AgentRunawayStopInfo {\n\treason: AgentRunawayStopReason;\n\tsignature: string;\n\trepeats: number;\n}\n\nexport interface ToolValidationEscalationEvent {\n\ttool: string;\n\tsignature: string;\n\trepeats: number;\n\tmodel: string;\n\tprovider: string;\n}\n\nexport interface PrepareNextTurnContext extends ShouldStopAfterTurnContext {}\n\n/** Input for one replay-safe context-planning attempt. */\nexport interface AgentContextPlanRequest {\n\t/** Sanitized durable history used as the compactable portion of this request. */\n\tmessages: AgentMessage[];\n\t/** Zero-based admission generation; freshness-only retries repeat the same value. */\n\tattempt: number;\n}\n\n/**\n * Replay-safe context plan. `messages` is compactable history. `transientMessages` and\n * `transientSystemPrompt` are mandatory request-local context that compaction must never summarize\n * or drop.\n */\nexport interface AgentContextPlan {\n\tmessages: AgentMessage[];\n\ttransientMessages?: AgentMessage[];\n\t/** Host-owned instructions appended to the system channel for this request only. */\n\ttransientSystemPrompt?: string;\n\t/** Cheap freshness check immediately before admission/commit. */\n\tisCurrent?: () => boolean;\n\t/**\n\t * Pure final validation for expensive projections. Return false to discard and replan; do not\n\t * mutate durable state here.\n\t */\n\tprepareCommit?: () => boolean;\n\t/**\n\t * Apply lifecycle side effects after every composed validator passed. Synchronous, infallible by\n\t * contract, and must not change the planned payload.\n\t */\n\tcommit?: () => void;\n\t/** Release request-local planning resources when a plan is not accepted. */\n\tdiscard?: () => void;\n}\n\n/** Provider-ready request inspected after full materialization and immediately before transport. */\nexport interface RequestPreflightContext {\n\tmodel: Model<Api>;\n\tcontext: Context;\n\t/** Current owner-selected output cap before request-local narrowing. */\n\tmaxTokens?: number;\n}\n\n/** Request-local limits. A returned output cap can only narrow the current owner/model limit. */\nexport interface RequestPreflightResult {\n\tmaxTokens?: number;\n}\n\n/** Exact materialization offered to the host-owned compaction/admission gate. */\nexport interface ProviderRequestAdmissionContext extends RequestPreflightContext {\n\t/** Agent-level request snapshot from which this materialization was planned. */\n\tsourceContext: AgentContext;\n\t/** Provider context containing only the non-compactable system/tool/transient envelope. */\n\tnonCompactableContext: Context;\n\t/** Zero-based admission generation; increments only after an accepted history replan. */\n\tattempt: number;\n}\n\nexport type ProviderRequestAdmissionResult =\n\t| { action: \"send\"; maxTokens?: number }\n\t| { action: \"replan\"; context: AgentContext };\n\n/**\n * Default runaway-loop backstop: a single identical tool-call signature recurring this many times\n * within a sliding window (4×) stops the loop. Generous enough that legitimate long/varied work never\n * trips it, but bounds the cost of a model wedged repeating one failing call forever.\n */\nexport const DEFAULT_MAX_STALL_TURNS = 12;\n/** Maximum paid provider turns in one logical prompt before the local cost fuse stops the run. */\nexport const DEFAULT_MAX_PROVIDER_TURNS = 20;\n\nexport interface AgentLoopConfig extends SimpleStreamOptions {\n\tmodel: Model<any>;\n\n\t/**\n\t * Converts AgentMessage[] to LLM-compatible Message[] before each LLM call.\n\t *\n\t * Each AgentMessage must be converted to a UserMessage, AssistantMessage, or ToolResultMessage\n\t * that the LLM can understand. AgentMessages that cannot be converted (e.g., UI-only notifications,\n\t * status messages) should be filtered out.\n\t *\n\t * Contract: must not throw or reject. Return a safe fallback value instead.\n\t * Throwing interrupts the low-level agent loop without producing a normal event sequence.\n\t *\n\t * @example\n\t * ```typescript\n\t * convertToLlm: (messages) => messages.flatMap(m => {\n\t * if (m.role === \"custom\") {\n\t * // Convert custom message to user message\n\t * return [{ role: \"user\", content: m.content, timestamp: m.timestamp }];\n\t * }\n\t * if (m.role === \"notification\") {\n\t * // Filter out UI-only messages\n\t * return [];\n\t * }\n\t * // Pass through standard LLM messages\n\t * return [m];\n\t * })\n\t * ```\n\t */\n\tconvertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\n\t/**\n\t * Optional transform applied to the context before `convertToLlm`.\n\t *\n\t * Use this for operations that work at the AgentMessage level:\n\t * - Context window management (pruning old messages)\n\t * - Injecting context from external sources\n\t *\n\t * Contract: must not throw or reject. Return the original messages or another\n\t * safe fallback value instead.\n\t *\n\t * @example\n\t * ```typescript\n\t * transformContext: async (messages) => {\n\t * if (estimateTokens(messages) > MAX_TOKENS) {\n\t * return pruneOldMessages(messages);\n\t * }\n\t * return messages;\n\t * }\n\t * ```\n\t */\n\ttransformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\n\t/**\n\t * Preferred two-phase replacement for `transformContext`. Planning is replay-safe and may run\n\t * again after compaction or invalidation; only the accepted plan's `commit` is invoked.\n\t */\n\tplanContext?: (request: AgentContextPlanRequest, signal?: AbortSignal) => Promise<AgentContextPlan>;\n\n\t/**\n\t * Host-owned admission gate over the complete provider-visible materialization. It may accept the\n\t * request or compact durable history and return a replacement source context for replanning.\n\t */\n\tadmitProviderRequest?: (\n\t\trequest: ProviderRequestAdmissionContext,\n\t\tsignal?: AbortSignal,\n\t) => ProviderRequestAdmissionResult | Promise<ProviderRequestAdmissionResult>;\n\n\t/**\n\t * Runs after admission against the exact transport-ready context, immediately before every provider request.\n\t *\n\t * Use this for request-local budget/authority checks whose state can change between tool turns.\n\t * Throwing prevents transport. A returned `maxTokens` must be a positive safe integer and can\n\t * only narrow the current owner/model output limit; it never mutates the persistent loop config.\n\t */\n\trequestPreflight?: (\n\t\tcontext: RequestPreflightContext,\n\t\tsignal?: AbortSignal,\n\t) => RequestPreflightResult | undefined | Promise<RequestPreflightResult | undefined>;\n\n\t/**\n\t * Resolve the reasoning effort after context transformation and immediately before the provider\n\t * request. This supports request-local policy decisions that must not mutate persisted agent state.\n\t */\n\tresolveRequestReasoning?: (\n\t\treasoning: SimpleStreamOptions[\"reasoning\"],\n\t\trequest: { model: Model<Api>; context: Context; maxTokens?: number },\n\t) => SimpleStreamOptions[\"reasoning\"];\n\n\t/**\n\t * Resolves an API key dynamically for each LLM call.\n\t *\n\t * Useful for short-lived OAuth tokens (e.g., GitHub Copilot) that may expire\n\t * during long-running tool execution phases.\n\t *\n\t * Contract: must not throw or reject. Return undefined when no key is available.\n\t */\n\tgetApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\n\t/**\n\t * Called after each turn fully completes and `turn_end` has been emitted.\n\t *\n\t * If it returns true, the loop emits `agent_end` and exits before polling steering or follow-up queues,\n\t * without starting another LLM call. The current assistant response and any tool executions finish normally.\n\t *\n\t * Use this to request a graceful stop after the current turn, e.g. before context gets too full.\n\t *\n\t * Contract: must not throw or reject. Throwing interrupts the low-level agent loop without producing a normal event sequence.\n\t */\n\tshouldStopAfterTurn?: (context: ShouldStopAfterTurnContext) => boolean | Promise<boolean>;\n\n\t/**\n\t * Runaway-loop backstop. A model stuck repeating the SAME tool call (identical name + arguments) —\n\t * because a tool keeps erroring, or it is confused/oscillating — makes no progress yet keeps\n\t * consuming tokens indefinitely (history grows every turn). This bounds that cost: if one tool-call\n\t * signature recurs at least this many times within a sliding window (4×), the loop stops gracefully\n\t * (emits `agent_end`). It counts ONLY turns that issued tool calls and keys on exact name+arguments,\n\t * so legitimate long or varied agentic work never trips it. `0` disables the backstop.\n\t * Default: {@link DEFAULT_MAX_STALL_TURNS}.\n\t */\n\tmaxStallTurns?: number;\n\n\t/**\n\t * Absolute provider-request cost fuse for one logical prompt, including host continuations.\n\t * Unlike {@link maxStallTurns}, this also catches varied tool churn that never repeats an exact\n\t * signature. The loop emits a local terminal diagnostic before another provider request. `0`\n\t * disables the fuse. Default: {@link DEFAULT_MAX_PROVIDER_TURNS}.\n\t */\n\tmaxProviderTurns?: number;\n\n\t/**\n\t * Observability hook fired once if either the repeated-call backstop or provider-turn cost fuse trips,\n\t * just before the loop stops. Lets the host surface/log the exact cause. Must not throw.\n\t */\n\tonRunawayStop?: (info: AgentRunawayStopInfo) => void;\n\n\t/**\n\t * Called after `turn_end` and before the loop decides whether another provider request should start.\n\t * Return replacement context/model/thinking state to affect the next turn in this run.\n\t * Return undefined to keep using the current context/config.\n\t */\n\tprepareNextTurn?: (\n\t\tcontext: PrepareNextTurnContext,\n\t) => AgentLoopTurnUpdate | undefined | Promise<AgentLoopTurnUpdate | undefined>;\n\n\t/**\n\t * Returns steering messages to inject into the conversation mid-run.\n\t *\n\t * Called after the current assistant turn finishes executing its tool calls, unless `shouldStopAfterTurn` exits first.\n\t * If messages are returned, they are added to the context before the next LLM call.\n\t * Tool calls from the current assistant message are not skipped.\n\t *\n\t * Use this for \"steering\" the agent while it's working.\n\t *\n\t * Contract: must not throw or reject. Return [] when no steering messages are available.\n\t */\n\tgetSteeringMessages?: () => Promise<AgentMessage[]>;\n\n\t/**\n\t * Returns follow-up messages to process after the agent would otherwise stop.\n\t *\n\t * Called when the agent has no more tool calls and no steering messages.\n\t * If messages are returned, they're added to the context and the agent\n\t * continues with another turn.\n\t *\n\t * Use this for follow-up messages that should wait until the agent finishes.\n\t *\n\t * Contract: must not throw or reject. Return [] when no follow-up messages are available.\n\t */\n\tgetFollowUpMessages?: () => Promise<AgentMessage[]>;\n\n\t/**\n\t * Tool execution mode.\n\t * - \"sequential\": execute tool calls one by one\n\t * - \"parallel\": preflight and execute tool calls in bounded concurrent accounting waves;\n\t * update recovery state between waves, emit `tool_execution_end` in completion order within\n\t * each wave, then emit tool-result message artifacts in assistant source order\n\t *\n\t * Default: \"parallel\"\n\t */\n\ttoolExecution?: ToolExecutionMode;\n\n\t/** Disable in-band tool repair teaching notes. Default: enabled. */\n\ttoolArgumentTeachEnabled?: boolean;\n\n\t/**\n\t * Observe tool argument validation outcomes. Events contain only shape metadata\n\t * (outcome, model/provider/tool, failure modes, repairs) and never argument values.\n\t */\n\tonToolArgumentValidation?: (event: ToolArgumentValidationTelemetryEvent) => void;\n\n\t/**\n\t * Number of consecutive identical validation bounces before adding full schema/example feedback\n\t * and notifying the host. Set to 0 to disable. Default: 3.\n\t */\n\ttoolValidationEscalationThreshold?: number;\n\n\t/**\n\t * Fired when a repeated identical tool validation failure reaches the escalation threshold.\n\t * Hosts with model routers can use this signal to move the next turn off a cheap route.\n\t */\n\tonToolValidationEscalation?: (event: ToolValidationEscalationEvent) => void;\n\n\t/**\n\t * Called before a tool is executed, after arguments have been validated.\n\t *\n\t * Return `{ block: true }` to prevent execution. The loop emits an error tool result instead.\n\t * The hook receives the agent abort signal and is responsible for honoring it.\n\t */\n\tbeforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;\n\n\t/**\n\t * Called after a tool finishes executing, before `tool_execution_end` and tool-result message events are emitted.\n\t *\n\t * Return an `AfterToolCallResult` to override parts of the executed tool result:\n\t * - `content` replaces the full content array\n\t * - `details` replaces the full details payload\n\t * - `isError` replaces the error flag\n\t * - `terminate` replaces the early-termination hint\n\t *\n\t * Any omitted fields keep their original values. No deep merge is performed.\n\t * The hook receives the agent abort signal and is responsible for honoring it.\n\t */\n\tafterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;\n\n\t/**\n\t * Foreground latency budget for prepared tool calls. When it elapses, `handoffToolCall` may\n\t * transfer the still-running execution to a host-owned task. Disabled unless both fields exist.\n\t */\n\tbackgroundToolCallAfterMs?: number;\n\n\t/**\n\t * Synchronously accept ownership of a slow call. Returning a handoff lets the provider loop\n\t * continue with its bounded placeholder while `completion` still crosses `afterToolCall` once.\n\t * Returning `undefined` keeps waiting in the foreground.\n\t */\n\thandoffToolCall?: (context: BackgroundToolCallContext) => BackgroundToolCallHandoff | undefined;\n\n\t/**\n\t * Register a one-shot host request that asks an in-flight foreground call to cross the same\n\t * `handoffToolCall` boundary before its automatic latency budget elapses.\n\t */\n\tsubscribeToolCallHandoffRequest?: (toolCallId: string, request: () => void) => () => void;\n}\n\n/**\n * Thinking/reasoning level for models that support it.\n * Note: \"xhigh\", \"max\", and \"ultra\" are only supported by selected model families. \"ultra\" maps\n * to the model's maximum provider effort and reinforces proactive orchestration in capable hosts;\n * delegation can remain available at lower levels. Use model thinking-level metadata from\n * @caupulican/pi-ai to detect support for a concrete model.\n */\nexport type ThinkingLevel = \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\" | \"ultra\";\n\n/**\n * Extensible interface for custom app messages.\n * Apps can extend via declaration merging:\n *\n * @example\n * ```typescript\n * declare module \"@mariozechner/agent\" {\n * interface CustomAgentMessages {\n * artifact: ArtifactMessage;\n * notification: NotificationMessage;\n * }\n * }\n * ```\n */\nexport interface CustomAgentMessages {\n\t// Empty by default - apps extend via declaration merging\n}\n\n/**\n * AgentMessage: Union of LLM messages + custom messages.\n * This abstraction allows apps to add custom message types while maintaining\n * type safety and compatibility with the base LLM messages.\n */\nexport type AgentMessage = Message | CustomAgentMessages[keyof CustomAgentMessages];\n\n/**\n * Public agent state.\n *\n * `tools` and `messages` use accessor properties so implementations can copy\n * assigned arrays before storing them.\n */\nexport interface AgentState {\n\t/** System prompt sent with each model request. */\n\tsystemPrompt: string;\n\t/** Active model used for future turns. */\n\tmodel: Model<any>;\n\t/** Requested reasoning level for future turns. */\n\tthinkingLevel: ThinkingLevel;\n\t/** Available tools. Assigning a new array copies the top-level array. */\n\tset tools(tools: AgentTool<any>[]);\n\tget tools(): AgentTool<any>[];\n\t/** Conversation transcript. Assigning a new array copies the top-level array. */\n\tset messages(messages: AgentMessage[]);\n\tget messages(): AgentMessage[];\n\t/**\n\t * True while the agent is processing a prompt or continuation.\n\t *\n\t * This remains true until awaited `agent_end` listeners settle.\n\t */\n\treadonly isStreaming: boolean;\n\t/** Partial assistant message for the current streamed response, if any. */\n\treadonly streamingMessage?: AgentMessage;\n\t/** Tool call ids currently executing. */\n\treadonly pendingToolCalls: ReadonlySet<string>;\n\t/** Error message from the most recent failed or aborted assistant turn, if any. */\n\treadonly errorMessage?: string;\n}\n\n/** Final or partial result produced by a tool. */\nexport interface AgentToolResult<T> {\n\t/** Text or image content returned to the model. */\n\tcontent: (TextContent | ImageContent)[];\n\t/** Arbitrary structured details for logs or UI rendering. */\n\tdetails: T;\n\t/**\n\t * Marks a completed execution as a failure without throwing.\n\t *\n\t * The agent loop preserves the result long enough for `afterToolCall` to\n\t * inspect it, then converts the bounded diagnostic into its durable failure\n\t * record. Throwing remains valid for exceptional execution failures.\n\t */\n\tisError?: boolean;\n\t/** Provider usage spent inside this tool, for durable budget and cost accounting. */\n\tusage?: Usage;\n\t/**\n\t * Hint that the agent should stop after the current tool batch.\n\t * Early termination only happens when every finalized tool result in the batch sets this to true.\n\t */\n\tterminate?: boolean;\n}\n\n/** Callback used by tools to stream partial execution updates. */\nexport type AgentToolUpdateCallback<T = any> = (partialResult: AgentToolResult<T>) => void;\n\nconst AGENT_TOOL_FAILURE_RECOVERY_AUTHORITY = Symbol(\"AgentToolFailureRecoveryAuthority\");\n\n/** Opaque identity shared only by tool instances that act on the same authoritative backend. */\nexport interface AgentToolFailureRecoveryAuthority {\n\treadonly [AGENT_TOOL_FAILURE_RECOVERY_AUTHORITY]: true;\n}\n\n/** Create an unforgeable, process-local recovery authority for intentionally cooperating tools. */\nexport function createAgentToolFailureRecoveryAuthority(): AgentToolFailureRecoveryAuthority {\n\treturn Object.freeze({ [AGENT_TOOL_FAILURE_RECOVERY_AUTHORITY]: true as const });\n}\n\n/** Validate recovery authority values supplied by tool-owned contracts. */\nexport function isAgentToolFailureRecoveryAuthority(value: unknown): value is AgentToolFailureRecoveryAuthority {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t!Array.isArray(value) &&\n\t\t(value as { [AGENT_TOOL_FAILURE_RECOVERY_AUTHORITY]?: unknown })[AGENT_TOOL_FAILURE_RECOVERY_AUTHORITY] === true\n\t);\n}\n\n/** Exact, opaque state requirement shared only by tools that intentionally cooperate on recovery. */\nexport interface AgentToolFailureRecoveryTarget {\n\t/** Backend identity; equality is object identity and the harness never serializes it. */\n\tauthority: AgentToolFailureRecoveryAuthority;\n\t/** Stable semantic namespace owned by the declaring tools. The harness never interprets it. */\n\tkind: string;\n\t/** Exact resource/state identity within `kind`. The harness compares it byte-for-byte. */\n\tscope: string;\n}\n\n/** Bounded failure identity supplied to a failed tool's recovery contract. */\nexport interface AgentToolFailureRecoveryContext {\n\tfailureCode: string;\n}\n\n/**\n * One action a tool can actually perform for a declared failure target.\n *\n * A `correct` action teaches a materially changed operation and never unlocks an unchanged retry.\n * A `repair` action must emit exact evidence after success; only that evidence may unlock one probe.\n */\nexport type AgentToolFailureRecoveryAction<TParameters extends TSchema, TDetails> =\n\t| {\n\t\t\tkind: \"correct\";\n\t\t\tauthority: AgentToolFailureRecoveryAuthority;\n\t\t\ttargetKind: string;\n\t\t\tinstruction: string;\n\t }\n\t| {\n\t\t\tkind: \"repair\";\n\t\t\tauthority: AgentToolFailureRecoveryAuthority;\n\t\t\ttargetKind: string;\n\t\t\tinstruction: string;\n\t\t\tgetEvidence: (params: Static<TParameters>, result: AgentToolResult<TDetails>) => readonly string[];\n\t };\n\n/** Tool-owned failure targets and recovery actions. Undeclared behavior has no recovery authority. */\nexport interface AgentToolFailureRecoveryContract<TParameters extends TSchema, TDetails> {\n\t/** Derive exact recovery requirements from validated arguments and a classified failure. */\n\tgetFailureTargets?: (\n\t\tparams: Static<TParameters>,\n\t\tfailure: AgentToolFailureRecoveryContext,\n\t) => readonly AgentToolFailureRecoveryTarget[];\n\t/** Actions this tool can perform when it is present in the active tool surface. */\n\tactions?: readonly AgentToolFailureRecoveryAction<TParameters, TDetails>[];\n}\n\n/** Tool definition used by the agent runtime. */\nexport interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any> extends Tool<TParameters> {\n\t/** Human-readable label for UI display. */\n\tlabel: string;\n\t/** Compact provider-facing capability description. Execution keeps the full `description`. */\n\tproviderDescription?: string;\n\t/**\n\t * Optional compatibility shim for raw tool-call arguments before schema validation.\n\t * Must return an object that matches `TParameters`.\n\t */\n\tprepareArguments?: (args: unknown) => Static<TParameters>;\n\t/** Explicit failure-recovery authority; the agent loop never infers recovery from argument text. */\n\tfailureRecovery?: AgentToolFailureRecoveryContract<TParameters, TDetails>;\n\t/**\n\t * Execute the tool call. Throw for exceptional execution failures, or return\n\t * `{ isError: true }` with bounded diagnostic content for an expected\n\t * operation failure such as a non-zero subprocess exit.\n\t */\n\texecute: (\n\t\ttoolCallId: string,\n\t\tparams: Static<TParameters>,\n\t\tsignal?: AbortSignal,\n\t\tonUpdate?: AgentToolUpdateCallback<TDetails>,\n\t) => Promise<AgentToolResult<TDetails>>;\n\t/**\n\t * Per-tool execution mode override.\n\t * - \"sequential\": this tool must execute one at a time with other tool calls.\n\t * - \"parallel\": this tool can execute concurrently with other tool calls.\n\t *\n\t * If omitted, the default execution mode applies.\n\t */\n\texecutionMode?: ToolExecutionMode;\n}\n\n/** Context snapshot passed into the low-level agent loop. */\nexport interface AgentContext {\n\t/** System prompt included with the request. */\n\tsystemPrompt: string;\n\t/** Transcript visible to the model. */\n\tmessages: AgentMessage[];\n\t/** Tools available for this run. */\n\ttools?: AgentTool<any>[];\n}\n\n/**\n * Events emitted by the Agent for UI updates.\n *\n * `agent_end` is the last event emitted for a run, but awaited `Agent.subscribe()`\n * listeners for that event are still part of run settlement. The agent becomes\n * idle only after those listeners finish.\n */\nexport interface ToolCallRepairInfo {\n\trepaired: true;\n\trawArguments?: Record<string, unknown>;\n\tnotes?: string[];\n}\n\nexport type AgentEvent =\n\t// Agent lifecycle\n\t| { type: \"agent_start\" }\n\t| { type: \"agent_end\"; messages: AgentMessage[] }\n\t// Turn lifecycle - a turn is one assistant response + any tool calls/results\n\t| { type: \"turn_start\" }\n\t| { type: \"turn_end\"; message: AgentMessage; toolResults: ToolResultMessage[] }\n\t// Message lifecycle - emitted for user, assistant, and toolResult messages\n\t| { type: \"message_start\"; message: AgentMessage }\n\t// Only emitted for assistant messages during streaming\n\t| { type: \"message_update\"; message: AgentMessage; assistantMessageEvent: AssistantMessageEvent }\n\t| { type: \"message_end\"; message: AgentMessage }\n\t// Tool execution lifecycle\n\t| { type: \"tool_execution_start\"; toolCallId: string; toolName: string; args: any; repair?: ToolCallRepairInfo }\n\t| {\n\t\t\ttype: \"tool_execution_update\";\n\t\t\ttoolCallId: string;\n\t\t\ttoolName: string;\n\t\t\targs: any;\n\t\t\tpartialResult: any;\n\t\t\trepair?: ToolCallRepairInfo;\n\t }\n\t| {\n\t\t\ttype: \"tool_execution_end\";\n\t\t\ttoolCallId: string;\n\t\t\ttoolName: string;\n\t\t\tresult: any;\n\t\t\tisError: boolean;\n\t\t\trepair?: ToolCallRepairInfo;\n\t };\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@caupulican/pi-agent-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.93.0",
|
|
4
4
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -61,6 +61,21 @@
|
|
|
61
61
|
"pi-source": "./src/messages.ts",
|
|
62
62
|
"import": "./dist/messages.js"
|
|
63
63
|
},
|
|
64
|
+
"./provider-request-estimator": {
|
|
65
|
+
"types": "./dist/provider-request-estimator.d.ts",
|
|
66
|
+
"pi-source": "./src/provider-request-estimator.ts",
|
|
67
|
+
"import": "./dist/provider-request-estimator.js"
|
|
68
|
+
},
|
|
69
|
+
"./provider-request-planner": {
|
|
70
|
+
"types": "./dist/provider-request-planner.d.ts",
|
|
71
|
+
"pi-source": "./src/provider-request-planner.ts",
|
|
72
|
+
"import": "./dist/provider-request-planner.js"
|
|
73
|
+
},
|
|
74
|
+
"./provider-tool-projection": {
|
|
75
|
+
"types": "./dist/provider-tool-projection.d.ts",
|
|
76
|
+
"pi-source": "./src/provider-tool-projection.ts",
|
|
77
|
+
"import": "./dist/provider-tool-projection.js"
|
|
78
|
+
},
|
|
64
79
|
"./paths": {
|
|
65
80
|
"types": "./dist/utils/paths.d.ts",
|
|
66
81
|
"pi-source": "./src/utils/paths.ts",
|
|
@@ -91,6 +106,11 @@
|
|
|
91
106
|
"pi-source": "./src/tool-failure-memory.ts",
|
|
92
107
|
"import": "./dist/tool-failure-memory.js"
|
|
93
108
|
},
|
|
109
|
+
"./tool-protocol-residue": {
|
|
110
|
+
"types": "./dist/tool-protocol-residue.d.ts",
|
|
111
|
+
"pi-source": "./src/tool-protocol-residue.ts",
|
|
112
|
+
"import": "./dist/tool-protocol-residue.js"
|
|
113
|
+
},
|
|
94
114
|
"./truncate": {
|
|
95
115
|
"types": "./dist/utils/truncate.d.ts",
|
|
96
116
|
"pi-source": "./src/utils/truncate.ts",
|
|
@@ -121,7 +141,7 @@
|
|
|
121
141
|
"prepublishOnly": "npm run clean && npm run build"
|
|
122
142
|
},
|
|
123
143
|
"dependencies": {
|
|
124
|
-
"@caupulican/pi-ai": "^0.
|
|
144
|
+
"@caupulican/pi-ai": "^0.93.0",
|
|
125
145
|
"ignore": "7.0.5",
|
|
126
146
|
"typebox": "1.1.38",
|
|
127
147
|
"yaml": "2.9.0"
|