- Fixed context token estimates to count user image attachments consistently with tool result images (#4983).
- Changed source syntax to avoid TypeScript constructs that require JavaScript emit, keeping the package compatible with Node.js strip-only TypeScript checks.
- Removed the package-level development watch script now that the root TypeScript check validates strip-only-compatible sources.
- Fixed tool-call preflight to stop preparing sibling tool calls after the run is aborted (#4276).
- Fixed tail truncation for oversized single-line output that ends with a trailing newline (#4715).
- Fixed Windows Node execution environment command spawns to hide helper console windows from background processes (#4699).
- Raised the minimum supported Node.js version to 22.19.0.
- Changed the default agent transport to
autoso providers can use their best available transport by default (#4083).
- Added
shouldStopAfterTurnto the low-level agent loop config for gracefully exiting after a completed turn before polling queued messages or starting another LLM call.
- Migrated public TypeBox-facing types and examples from
@sinclair/typebox0.34.x totypebox1.x. Install and import fromtypeboxinstead of relying on@sinclair/typeboxtransitively (#3112)
- Added
terminate: truetool-result hints to skip the automatic follow-up LLM call when every finalized tool result in the current batch opts into early termination (#3525)
- Fixed
streamProxy()to preserve the proxy-safe serializable subset of stream options, including session, transport, retry-delay, metadata, header, cache-retention, and thinking-budget settings (#3512) - Fixed parallel tool execution to emit
tool_execution_endas soon as each tool is finalized, while still emitting persisted tool-result messages in assistant source order (#3503)
- Clarified parallel tool execution ordering docs to specify that final tool lifecycle and tool-result artifacts are emitted in tool completion order.
- Fixed parallel tool-call finalization to convert
afterToolCallhook throws into error tool results instead of aborting the batch (#3084)
AgentStatehas been reshaped:streamMessagewas renamed tostreamingMessageerrorwas renamed toerrorMessageisStreaming,streamingMessage,pendingToolCalls, anderrorMessageare now readonly in the public APIpendingToolCallsis now typed asReadonlySet<string>toolsandmessagesare now accessor properties, and assigning either field copies the provided top-level array instead of preserving array identity
AgentOptions.initialStateno longer accepts runtime-owned fields. RemoveisStreaming,streamingMessage,pendingToolCalls, anderrorMessagefrominitialStatevalues.- Removed
Agentmutator methods in favor of direct property access:agent.setSystemPrompt(value)->agent.state.systemPrompt = valueagent.setModel(model)->agent.state.model = modelagent.setThinkingLevel(level)->agent.state.thinkingLevel = levelagent.setTools(tools)->agent.state.tools = toolsagent.replaceMessages(messages)->agent.state.messages = messagesagent.appendMessage(message)->agent.state.messages.push(message)agent.clearMessages()->agent.state.messages = []agent.setToolExecution(mode)->agent.toolExecution = modeagent.setBeforeToolCall(fn)->agent.beforeToolCall = fnagent.setAfterToolCall(fn)->agent.afterToolCall = fnagent.setTransport(transport)->agent.transport = transport
- Removed queue mode getter/setter methods in favor of properties:
agent.setSteeringMode(mode)->agent.steeringMode = modeagent.getSteeringMode()->agent.steeringModeagent.setFollowUpMode(mode)->agent.followUpMode = modeagent.getFollowUpMode()->agent.followUpMode
Agent.subscribe()listeners are now awaited and receive the activeAbortSignal:agent.subscribe((event) => { ... })->agent.subscribe(async (event, signal) => { ... })agent_endis now the final emitted event for a run, but not the idle boundaryagent.waitForIdle(),agent.prompt(...), andagent.continue()now settle only after awaitedagent_endlisteners finishagent.state.isStreamingremainstrueuntil that settlement completes
- Added
AgentTool.prepareArgumentshook to prepare raw tool call arguments before schema validation, enabling compatibility shims for resumed sessions with outdated tool schemas
- Added
Agent.signalto expose the active abort signal for the current turn, allowing callers to forward cancellation into nested async work (#2660)
- Fixed steering messages to wait until the current assistant message's tool-call batch fully finishes instead of skipping pending tool calls.
- Added
beforeToolCallandafterToolCallhooks toAgentOptionsandAgentLoopConfigfor preflight blocking and post-execution tool result mutation.
- Added configurable tool execution mode to
AgentandagentLoopviatoolExecution: "parallel" | "sequential", withparallelas the default. Parallel mode preflights tool calls sequentially, executes allowed tools concurrently, and emits final tool results in assistant source order.
- Added
transporttoAgentOptionsandAgentLoopConfigforwarding, allowing stream transport preference ("sse","websocket","auto") to flow into provider calls.
- Fixed
continue()to resume queued steering/follow-up messages when context currently ends in an assistant message, and preserved one-at-a-time steering ordering during assistant-tail resumes (#1312 by @ferologics)
- Added
maxRetryDelayMsoption toAgentOptionsto cap server-requested retry delays. Passed through to the underlying stream function. (#1123)
thinkingBudgetsoption onAgentandAgentOptionsto customize token budgets per thinking level (#529 by @melihmucuk)
sessionIdoption onAgentto forward session identifiers to LLM providers for session-based caching.
minimalthinking level now maps tominimalreasoning effort instead of being treated aslow.
- Queue API replaced with steer/followUp: The
queueMessage()method has been split into two methods with different delivery semantics (#403):steer(msg): Interrupts the agent mid-run. Delivered after current tool execution, skips remaining tools.followUp(msg): Waits until the agent finishes. Delivered only when there are no more tool calls or steering messages.
- Queue mode renamed:
queueModeoption renamed tosteeringMode. Added newfollowUpModeoption. Both control whether messages are delivered one-at-a-time or all at once. - AgentLoopConfig callbacks renamed:
getQueuedMessagessplit intogetSteeringMessagesandgetFollowUpMessages. - Agent methods renamed:
queueMessage()→steer()andfollowUp()clearMessageQueue()→clearSteeringQueue(),clearFollowUpQueue(),clearAllQueues()setQueueMode()/getQueueMode()→setSteeringMode()/getSteeringMode()andsetFollowUpMode()/getFollowUpMode()
prompt()andcontinue()now throw if called while the agent is already streaming, preventing race conditions and corrupted state. Usesteer()orfollowUp()to queue messages during streaming, orawaitthe previous call.
-
Transport abstraction removed:
ProviderTransport,AppTransport, andAgentTransportinterface have been removed. Use thestreamFnoption directly for custom streaming implementations. -
Agent options renamed:
transport→ removed (usestreamFninstead)messageTransformer→convertToLlmpreprocessor→transformContext
-
AppMessagerenamed toAgentMessage: All references toAppMessagehave been renamed toAgentMessagefor consistency. -
CustomMessagesrenamed toCustomAgentMessages: The declaration merging interface has been renamed. -
UserMessageWithAttachmentsandAttachmenttypes removed: Attachment handling is now the responsibility of theconvertToLlmfunction. -
Agent loop moved from
@mariozechner/pi-ai: TheagentLoop,agentLoopContinue, and related types have moved to this package. Import from@mariozechner/pi-agent-coreinstead.
-
streamFnoption onAgentfor custom stream implementations. Default usesstreamSimplefrom pi-ai. -
streamProxy()utility function for browser apps that need to proxy LLM calls through a backend server. Replaces the removedAppTransport. -
getApiKeyoption for dynamic API key resolution (useful for expiring OAuth tokens like GitHub Copilot). -
agentLoop()andagentLoopContinue()low-level functions for running the agent loop without theAgentclass wrapper. -
New exported types:
AgentLoopConfig,AgentContext,AgentTool,AgentToolResult,AgentToolUpdateCallback,StreamFn.
-
Agentconstructor now has all options optional (empty options use defaults). -
queueMessage()is now synchronous (no longer returns a Promise).