@oh-my-pi-zen/pi-agent-core 16.3.6-zen.1

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.
Files changed (66) hide show
  1. package/CHANGELOG.md +1031 -0
  2. package/README.md +473 -0
  3. package/dist/types/agent-loop.d.ts +89 -0
  4. package/dist/types/agent.d.ts +427 -0
  5. package/dist/types/append-only-context.d.ts +133 -0
  6. package/dist/types/compaction/branch-summarization.d.ts +101 -0
  7. package/dist/types/compaction/compaction-v2-streaming.d.ts +82 -0
  8. package/dist/types/compaction/compaction.d.ts +283 -0
  9. package/dist/types/compaction/entries.d.ts +110 -0
  10. package/dist/types/compaction/errors.d.ts +26 -0
  11. package/dist/types/compaction/index.d.ts +12 -0
  12. package/dist/types/compaction/messages.d.ts +77 -0
  13. package/dist/types/compaction/openai.d.ts +77 -0
  14. package/dist/types/compaction/pruning.d.ts +105 -0
  15. package/dist/types/compaction/shake.d.ts +92 -0
  16. package/dist/types/compaction/tool-protection.d.ts +17 -0
  17. package/dist/types/compaction/utils.d.ts +58 -0
  18. package/dist/types/compaction.d.ts +1 -0
  19. package/dist/types/index.d.ts +12 -0
  20. package/dist/types/proxy.d.ts +85 -0
  21. package/dist/types/replay-policy.d.ts +5 -0
  22. package/dist/types/run-collector.d.ts +196 -0
  23. package/dist/types/telemetry.d.ts +590 -0
  24. package/dist/types/thinking.d.ts +17 -0
  25. package/dist/types/tokenizer.d.ts +1 -0
  26. package/dist/types/types.d.ts +648 -0
  27. package/dist/types/utils/yield.d.ts +71 -0
  28. package/package.json +78 -0
  29. package/src/agent-loop.ts +2262 -0
  30. package/src/agent.ts +1374 -0
  31. package/src/append-only-context.ts +348 -0
  32. package/src/compaction/branch-summarization.ts +370 -0
  33. package/src/compaction/compaction-v2-streaming.ts +724 -0
  34. package/src/compaction/compaction.ts +1553 -0
  35. package/src/compaction/entries.ts +142 -0
  36. package/src/compaction/errors.ts +31 -0
  37. package/src/compaction/index.ts +13 -0
  38. package/src/compaction/messages.ts +237 -0
  39. package/src/compaction/openai.ts +586 -0
  40. package/src/compaction/prompts/auto-handoff-threshold-focus.md +1 -0
  41. package/src/compaction/prompts/branch-summary-context.md +5 -0
  42. package/src/compaction/prompts/branch-summary-preamble.md +2 -0
  43. package/src/compaction/prompts/branch-summary.md +30 -0
  44. package/src/compaction/prompts/compaction-short-summary.md +9 -0
  45. package/src/compaction/prompts/compaction-summary-context.md +5 -0
  46. package/src/compaction/prompts/compaction-summary.md +38 -0
  47. package/src/compaction/prompts/compaction-turn-prefix.md +17 -0
  48. package/src/compaction/prompts/compaction-update-summary.md +45 -0
  49. package/src/compaction/prompts/file-operations.md +5 -0
  50. package/src/compaction/prompts/handoff-document.md +49 -0
  51. package/src/compaction/prompts/snapcompact-archive-context.md +3 -0
  52. package/src/compaction/prompts/summarization-system.md +3 -0
  53. package/src/compaction/pruning.ts +424 -0
  54. package/src/compaction/shake.ts +429 -0
  55. package/src/compaction/tool-protection.ts +55 -0
  56. package/src/compaction/utils.ts +323 -0
  57. package/src/compaction.ts +1 -0
  58. package/src/index.ts +24 -0
  59. package/src/proxy.ts +376 -0
  60. package/src/replay-policy.ts +13 -0
  61. package/src/run-collector.ts +631 -0
  62. package/src/telemetry.ts +2034 -0
  63. package/src/thinking.ts +19 -0
  64. package/src/tokenizer.ts +17 -0
  65. package/src/types.ts +726 -0
  66. package/src/utils/yield.ts +183 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,1031 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ ## [16.3.3] - 2026-07-02
6
+
7
+ ### Changed
8
+
9
+ - Enabled dynamic model resolution to support seamless mid-run model switching.
10
+
11
+ ### Fixed
12
+
13
+ - Fixed an issue in the Cursor agent where assistant messages containing native tool calls could duplicate text blocks on replay.
14
+ - Fixed a bug where Cursor agent exec-channel tools (such as bash, write, and delete) were executed a second time after server-side execution.
15
+ - Improved error handling for tool calls interrupted by upstream provider stream errors, distinguishing transport/provider failures from local tool execution failures in the CLI, events, and messages.
16
+
17
+ ## [16.3.0] - 2026-07-02
18
+
19
+ ### Added
20
+
21
+ - Added support for Anthropic fallback content blocks in agent-loop assistant messages, ensuring they are preserved across session persistence and event fanout.
22
+
23
+ ### Fixed
24
+
25
+ - Fixed an issue where legacy steering messages were prematurely consumed and dropped during in-flight tool execution polls.
26
+ - Fixed an issue where skipped tool results in queued messages were incorrectly treated as completed, preventing necessary retries.
27
+ - Improved branch summaries to preserve informative tool results from abandoned branches while filtering out redundant output.
28
+ - Fixed interruptible tool waits to properly abort on host-provided IRC interrupts in addition to user steering.
29
+ - Fixed schema validation errors for closed union tools by correctly injecting intent tracing into each variant.
30
+ - Fixed token compaction reserve-budget logic to honor explicit reserveTokens values equal to the built-in default, and clamped the fallback reserve to at least one token for very small context windows.
31
+
32
+ ## [16.2.4] - 2026-06-28
33
+
34
+ ### Changed
35
+
36
+ - Improved the reliability of remote compaction by introducing transient error retries, configurable timeouts, and immediate termination upon user-initiated aborts.
37
+
38
+ ### Fixed
39
+
40
+ - Fixed an issue where assistant responses and encrypted reasoning could be lost during local history trimming prior to remote compaction.
41
+ - Fixed type compatibility for hosts with title audit entries by adding support for `title_change` session metadata.
42
+ - Fixed an issue where transient stream read failures after a completed tool call were treated as terminal errors, allowing the agent to successfully execute the tool and continue the turn.
43
+
44
+ ## [16.2.3] - 2026-06-28
45
+
46
+ ### Changed
47
+
48
+ - Enabled V2 streaming remote compaction by default for compatible AI and OpenAI-compatible models, which forwards full conversation history to the provider and supports session routing, prompt caching, provider-native tool history replay, transient error retries, and configurable timeouts.
49
+
50
+ ### Fixed
51
+
52
+ - Fixed an issue where assistant responses and encrypted reasoning could be lost during local history trimming.
53
+ - Added `title_change` session metadata to the compaction entry type union to maintain type compatibility for hosts with title audit entries.
54
+
55
+ ## [16.2.2] - 2026-06-27
56
+
57
+ ### Added
58
+
59
+ - Added optional AgentTool.matcherPaths(args) and AgentTool.matcherEntries(args) hooks to allow tools to surface target file paths and isolate file evaluations for path-scoped stream matchers (e.g., when handling multi-file payloads or embedded paths in streamed arguments).
60
+
61
+ ### Removed
62
+
63
+ - Removed support for Pi dialect integration.
64
+
65
+ ## [16.2.0] - 2026-06-27
66
+
67
+ ### Added
68
+
69
+ - Added an optional `cwdResolver` to `Agent` and `getCwd` to `AgentLoopConfig` to dynamically resolve the working directory per LLM call, allowing workspace-scoped provider discovery (such as GitLab Duo Agent) to follow live directory changes without reconstructing the agent.
70
+
71
+ ### Fixed
72
+
73
+ - Fixed an issue where API-level provider refusals were replayed as assistant dialogue on subsequent requests, preventing repeated refusals after a single blocked turn.
74
+ - Fixed a bug where internal streaming state (`partialJson`) could leak onto the final `AssistantMessage` if a stream ended without a `toolcall_end` event.
75
+ - Fixed `Agent` to correctly forward the working directory (`cwd`) into provider stream options, enabling providers like GitLab Duo Agent to scope local tool execution to the workspace.
76
+ - Enabled custom OpenAI-compatible providers to use native remote compaction instead of falling back to local summarization.
77
+
78
+ ## [16.1.23] - 2026-06-26
79
+
80
+ ### Changed
81
+
82
+ - Changed `AgentLoopConfig.onTurnEnd` and `Agent.setOnTurnEnd` callbacks to receive whether the loop will continue with another provider request.
83
+
84
+ ### Fixed
85
+
86
+ - Fixed stale snapcompact archive frames leaking into context-full compaction after `compaction.strategy` was switched from `snapcompact` to `context-full`. Switching strategy left the latest compaction entry's `preserveData.snapcompact` in place, so context-full kept rebuilding context with old image frames attached — inflating context/token usage and making sessions appear to compact early (around ~60% apparent window use). The first context-full compaction after the switch now folds the prior archive's plaintext into the LLM summary input and strips `preserveData.snapcompact` from the new entry; legacy frame-only archives (no plaintext to migrate) are stripped outright. ([#3561](https://github.com/cagedbird043/oh-my-pi-zen/pull/3561) by [@serverinspector](https://github.com/serverinspector))
87
+
88
+ ## [16.1.18] - 2026-06-25
89
+
90
+ ### Fixed
91
+
92
+ - Fixed `AppendOnlyContextManager.syncMessages` clearing the entire log on any in-place rewrite of an already-synced message. Per-turn tool-output pruning, image stripping, or any `transformContext` re-render that touched a single message used to drop every prior turn out of the append-only log and re-send the conversation from scratch, forcing local backends (llama.cpp / Ollama / LM Studio) to re-prefill tens of thousands of tokens every few turns. `syncMessages` now finds the longest byte-stable prefix between the previously-synced messages and the new ones, truncates the log to that prefix, and only re-appends the diverged tail — so the provider's KV cache stays warm up to the divergence point. ([#3406](https://github.com/cagedbird043/oh-my-pi-zen/issues/3406))
93
+
94
+ ## [16.1.17] - 2026-06-24
95
+
96
+ ### Fixed
97
+
98
+ - Hardened the agent-loop cooperative yield against backward wall-clock jumps. A stale future timestamp left in the shared yield gate (NTP step, or a fake-timer test mocking `Date.now`) could make `yieldIfDue()` gate forever and stop yielding to the event loop; the gate now treats a backward clock delta as due and re-anchors. The gate is exposed as an injectable `YieldGate` (with `yieldIfDue()` retained as the shared singleton) so it can be exercised without mocking process-global timers.
99
+
100
+ ## [16.1.16] - 2026-06-23
101
+
102
+ ### Added
103
+
104
+ - Added `generateHandoffFromContext(context, model, options)` to `@oh-my-pi-zen/pi-agent-core/compaction`: runs the handoff oneshot against a fully-built provider `Context` (system prompt, normalized tools, transformed history, trailing handoff prompt) with `streamOptions` mirroring the live turn's cache routing, so a host that owns the transform pipeline can make the handoff request share the prompt cache the main turn populated. `generateHandoff(messages, …)` is unchanged and now delegates to it.
105
+ - Added an optional `systemPrompt` argument to `Agent.buildSideRequestContext(llmMessages, systemPrompt?)`, defaulting to the live agent prompt; callers can pin a different prompt (e.g. handoff generation, which uses the base prompt rather than a per-turn `before_agent_start` hook override).
106
+
107
+ ### Changed
108
+
109
+ - Updated `buildSideRequestContext` to allow pinning custom system prompts
110
+
111
+ ## [16.1.10] - 2026-06-21
112
+
113
+ ### Fixed
114
+
115
+ - Fixed labeled user interrupts retaining incomplete streamed tool calls before `toolcall_end`, which could persist malformed tool-call IDs into replay.
116
+
117
+ ## [16.1.8] - 2026-06-20
118
+
119
+ ### Breaking Changes
120
+
121
+ - Changed `transformProviderContext` and `buildSideRequestContext` to return a Promise
122
+
123
+ ### Added
124
+
125
+ - Added `buildSideRequestContext` to the `Agent` class to build prompt-cache-friendly provider Contexts for side-channels or ephemeral requests.
126
+ - Added `compactionContextTokens(providerContextTokens, storedConversationEstimate)`: floors the provider-reported context tokens by a local estimate of the stored conversation for the compaction decision, so a `before_provider_request` payload transform (a compression extension, obfuscator, or inline snapcompact) that shrinks the request can no longer deflate provider usage below the true history size and suppress auto-compaction.
127
+
128
+ ### Changed
129
+
130
+ - Exported helper functions `normalizeMessagesForProvider` and `resolveOwnedDialectFromEnv` from `packages/agent/src/agent-loop.ts`.
131
+
132
+ ## [16.1.5] - 2026-06-19
133
+
134
+ ### Fixed
135
+
136
+ - Wire-encoded `normalizeTools` parameters unconditionally so tools whose `intent` resolves to `"omit"` (function intent or `intent: "omit"`, e.g. builtin `eval` / `resolve`) no longer leak raw arktype/zod schema objects in `parameters` ([#3074](https://github.com/cagedbird043/oh-my-pi-zen/issues/3074))
137
+
138
+ ## [16.1.2] - 2026-06-19
139
+
140
+ ### Fixed
141
+
142
+ - Prevented sensitive raw JSON payloads from leaking into agent events during tool validation
143
+ - Ensured tool validation errors are handled correctly for malformed JSON parse inputs
144
+ - Ensure deep-cloning of tool-call arguments respects own enumerable properties
145
+ - Prevent direct object references between agent message snapshots and streaming events
146
+
147
+ ## [16.1.0] - 2026-06-19
148
+
149
+ ### Added
150
+
151
+ - Added `SoftToolRequirement` support to `getToolChoice`: a host can require a tool by returning a soft requirement instead of a hard `ToolChoice`. The loop injects the supplied reminder once (leaving `tool_choice` on auto), and escalates to a one-turn forced choice — skipping any detour tool batch — only if the model fails to call the required tool, avoiding the provider message-cache invalidation of forcing every turn.
152
+ - Added `pruneToolDescriptions` option to reduce token usage by stripping tool descriptions from provider-bound specs
153
+
154
+ ### Fixed
155
+
156
+ - Improved token estimation accuracy for compaction summaries containing multi-block content
157
+
158
+ ## [16.0.11] - 2026-06-19
159
+
160
+ ### Changed
161
+
162
+ - Updated the display format for truncated file operation summaries
163
+
164
+ ## [16.0.8] - 2026-06-18
165
+
166
+ ### Fixed
167
+
168
+ - Stopped the compaction `<files>` summary from tracking `scheme://` URLs — internal URIs (`conflict://`, `artifact://`, `local://`, `history://`, …) and web URLs are no longer recorded as files, and legacy entries rehydrated from older compaction summaries are dropped.
169
+
170
+ ## [16.0.6] - 2026-06-18
171
+
172
+ ### Added
173
+
174
+ - Added `transformAssistantMessage` hook to `AgentOptions` and `Agent` to allow mutating the finalized assistant message before UI emission, context appending, or tool dispatch
175
+
176
+ ## [16.0.5] - 2026-06-17
177
+
178
+ ### Breaking Changes
179
+
180
+ - Changed `AgentOptions.getApiKey` and `AgentLoopConfig.getApiKey` to receive the active `Model` and return an API key or `ApiKeyResolver`, so credential routing stays model-scoped and retry context is no longer exposed through the agent-core API
181
+
182
+ ### Added
183
+
184
+ - Added agent-loop deadline support for graceful wall-clock session stops.
185
+
186
+ ### Changed
187
+
188
+ - Changed Gemini repetition-loop detection to live in the pi-ai stream layer instead of the agent loop. The agent no longer runs its own Gemini-gated verbatim repetition check (`detectRepetition`/`truncateRepetition`); loops now surface as a retryable transient stream error that the standard auto-retry path discards and re-samples, rather than a committed contentful error message.
189
+
190
+ ### Fixed
191
+
192
+ - Fixed `PI_DIALECT=minimax` being ignored by the owned tool-calling env selector. ([#2759](https://github.com/cagedbird043/oh-my-pi-zen/issues/2759))
193
+
194
+ ## [16.0.1] - 2026-06-15
195
+
196
+ ### Fixed
197
+
198
+ - Fixed transient provider errors after streamed tool-call arguments so incomplete tool calls are marked as interrupted output instead of eligible for automatic retry ([#2683](https://github.com/cagedbird043/oh-my-pi-zen/issues/2683)).
199
+ - Fixed `@oh-my-pi-zen/pi-agent-core` telemetry content capture crashing every chat turn with `TypeError: systemPrompt.map is not a function` when `captureMessageContent` is enabled (`OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`). `ChatRequestSnapshot.systemPrompt` now accepts `string | readonly string[]` and the telemetry serializers normalize a bare string to a single-element array — previously the full-system serializer called `.map` on a string (the `.length` guard passed, so it threw) and the request-message serializer iterated the string into one `system` message per character.
200
+
201
+ ## [16.0.0] - 2026-06-15
202
+
203
+ ### Breaking Changes
204
+
205
+ - Renamed owned tool-calling options from `toolCallSyntax`/`exampleSyntax` to `dialect`/`exampleDialect`.
206
+ - Changed compaction conversation serialization to use the target model's native dialect turn, thinking, tool-call, and tool-result envelopes when a dialect is selected.
207
+ - Renamed the owned dialect environment variable from `PI_OWNED_TOOLS` to `PI_DIALECT`.
208
+
209
+ ### Added
210
+
211
+ - Added `onTurnEnd` hook support (`setOnTurnEnd`/`onTurnEnd`) to run awaited per-turn bookkeeping with current messages before the next model request and skip callback execution for aborted or error turns
212
+
213
+ ### Changed
214
+
215
+ - Renamed `toolCallSyntax` option to `dialect` in AgentOptions and AgentLoopConfig
216
+ - Updated conversation serialization to use dialect's native transcript rendering when a dialect is selected
217
+ - Changed internal references from `ToolCallSyntax` type to `Dialect` type across agent loop and compaction modules
218
+
219
+ ## [15.13.3] - 2026-06-15
220
+
221
+ ### Added
222
+
223
+ - Added the `interruptible` tool field: when set, the agent loop may abort the tool mid-execution to deliver a queued steering message (honored only in `immediate` interrupt mode).
224
+ - Added support for `gemini` and `gemma` as valid owned tool syntax values in environment configuration
225
+
226
+ ### Fixed
227
+
228
+ - Fixed `pruneToolOutputs` blanking tiny tool results during overflow pruning: results below `50` tokens (`MIN_PRUNE_TOKENS`) are no longer replaced with the `[Output truncated - N tokens]` placeholder, which cost more tokens than the result itself and churned the prompt cache for zero savings.
229
+
230
+ ## [15.13.2] - 2026-06-15
231
+
232
+ ### Breaking Changes
233
+
234
+ - Removed `harmony-leak` exports from the `@oh-my-pi-zen/pi-agent-core` package entrypoint
235
+ - Replaced the experimental `promptToolCalls` agent/loop option with `toolCallSyntax`, selecting an explicit in-band tool-call grammar instead of a boolean GLM-only mode.
236
+
237
+ ### Added
238
+
239
+ - Added support for selecting owned in-band tool-call syntax via `PI_OWNED_TOOLS=<syntax>` (for example `hermes` or `qwen3`) while preserving legacy `PI_OWNED_TOOLS=1/true` as GLM mode
240
+ - Added owned in-band tool calling for multiple syntaxes (`glm`, `hermes`, `kimi`, `xml`, `anthropic`, `deepseek`, `harmony`, `pi-native`, `qwen3`). Owned mode sends no native provider tools, appends a syntax-specific prompt/catalog, re-encodes prior tool calls/results as grammar-owned text, and parses streamed model output back into canonical tool calls.
241
+ - Added tool-example folding to `normalizeTools`: when given a model's affinity syntax (resolved via `preferredToolSyntax`), it renders each tool's `examples` into an `<examples>` block in that native syntax and appends it to the wire description. Wired through both context paths (fresh build and append-only `takeSnapshot`/`build` via a new `exampleSyntax` build option), with the `_i` intent-field placeholder added to examples when intent tracing injects it.
242
+ - Added the `abortOnFabricatedToolResult` option to `AgentOptions`/`AgentLoopConfig` (default `true`): when owned tool calling is active and the model fabricates a tool result mid-turn, `true` aborts the provider request immediately while `false` lets it finish and discards the fabricated continuation.
243
+
244
+ ### Changed
245
+
246
+ - Added owned in-band syntax support to `Agent` loop configuration resolution by selecting syntax from `toolCallSyntax` or `PI_OWNED_TOOLS` when present
247
+
248
+ ### Fixed
249
+
250
+ - Fixed append-only context cache fingerprinting to account for `exampleSyntax`, so switching tool-call syntax rebuilds cached prompts with the correct injected tool examples
251
+ - Fixed owned in-band tool-calling requests to omit `toolChoice` after stripping native tools, preventing invalid tool-choice requests
252
+ - Fixed owned tool calling letting the model fabricate tool results by treating grammar-owned tool-result markers in assistant text as a hard turn boundary: calls before the fabrication are kept, fabricated results and dependent calls are dropped, and the real result is fed back on the next turn.
253
+
254
+ ## [15.13.1] - 2026-06-15
255
+
256
+ ### Added
257
+
258
+ - Added repetition-loop detection to the streaming agent loop for Gemini-family providers. A runaway run of a repeated text or thinking unit is detected mid-stream from a bounded rolling tail (O(1) per delta), the provider request is aborted, the repeated tail is collapsed to a single representative copy, and the turn ends gracefully with an `error` stop reason. Legitimate all-numeric/whitespace/punctuation runs (hexdumps, zero-fills, numeric tables) are not misclassified as loops ([#2549](https://github.com/cagedbird043/oh-my-pi-zen/pull/2549) by [@usr-bin-roygbiv](https://github.com/usr-bin-roygbiv)).
259
+
260
+ ### Fixed
261
+
262
+ - Fixed repetition loop handling to collapse repeated `thinking` blocks to a single representative copy when a loop is detected
263
+ - Fixed repetition-loop detection to ignore repeats that contain only digits, whitespace, or punctuation so legitimate numeric outputs no longer stop with a repetition-loop error
264
+ - Fixed false-positive repetition-loop checks across `text` and `thinking` stream boundaries by tracking loop detection per block type
265
+
266
+ ## [15.12.6] - 2026-06-14
267
+
268
+ ### Fixed
269
+
270
+ - Fixed dynamic forced tool choices from queue hooks being filtered against the active per-turn tool set before provider dispatch. ([#1701](https://github.com/cagedbird043/oh-my-pi-zen/issues/1701))
271
+
272
+ ## [15.12.4] - 2026-06-13
273
+
274
+ ### Fixed
275
+
276
+ - Fixed remote compaction input trimming to use unlimited context when `model.contextWindow` is unset
277
+
278
+ ## [15.12.1] - 2026-06-12
279
+
280
+ ### Breaking Changes
281
+
282
+ - Changed `pruneSupersededToolResults` to allow `supersedeKey` to be omitted so useless-result pruning can run without read-style supersede grouping
283
+
284
+ ### Added
285
+
286
+ - Added `pruneUseless` controls to `PruneConfig` and `SupersedePruneConfig` so callers can toggle compaction of `toolResult` entries marked `useless`
287
+ - Added the ability to disable useless-result pruning by setting `pruneUseless` to false
288
+ - Tools can flag a result contextually useless (`AgentToolResult.useless`; overridable via `AfterToolCallResult.useless`): the agent loop copies the flag onto the persisted `ToolResultMessage` (errors always win), and compaction consumes it — the cache-aware supersede pass and the threshold prune blank flagged results to the exact `USELESS_NOTICE` placeholder (bypassing the protect window, skipping results smaller than the notice), shake collects them inside the protect-recent window, and `serializeConversation` drops the whole tool call/result pair from summarizer input
289
+
290
+ ### Changed
291
+
292
+ - Changed `pruneSupersededToolResults` to allow omitted `supersedeKey` when `pruneUseless` is enabled, so useless-result pruning can run without read-style supersede grouping
293
+
294
+ ## [15.11.4] - 2026-06-12
295
+
296
+ ### Added
297
+
298
+ - Added `hasSteeringMessages` to `AgentLoopConfig` (wired by `Agent` to its steering queue): a peek used by the immediate-interrupt poll during tool execution, so the loop can detect queued steering without dequeuing and the queue keeps owning its messages until the injection boundary
299
+ - The agent loop now re-samples after a non-terminal stop (`stopReason: "stop"` with `stopDetails: { type: "pause_turn" }`, emitted by the Codex providers for `end_turn: false` commentary-only responses): the assistant message is committed to history and the model is called again without ending the turn. Consecutive pause continuations without an intervening tool call are capped at 8 to bound a backend that never stops pausing.
300
+
301
+ ### Changed
302
+
303
+ - Changed steering handling so queued steering messages are now dequeued only at injection boundaries, with immediate mid-batch interrupt polling using `hasSteeringMessages`. Consumers constructing `AgentLoopConfig` directly with only `getSteeringMessages` no longer get mid-batch interrupts — steering degrades to boundary-only delivery until they also supply `hasSteeringMessages`
304
+ - Compaction, handoff, short-summary, and branch-summarization helpers now accept an `ApiKey` (static string or resolver) instead of a pre-resolved string, so a 401 mid-compaction force-refreshes and rotates the credential through the central auth-retry policy before any model-level fallback. The remote OpenAI compaction request is wrapped in `withAuth` and its HTTP failures now carry `.status`, so the retry classifier actually fires on remote-compaction 401s.
305
+ - `transformProviderContext` now receives the dispatch model as a second argument (`(context, model) => Context`), so per-request transforms can gate on model capabilities (vision input, provider, API family). Existing single-argument implementations keep working unchanged.
306
+ - Remote-compaction and summarization failures now throw pi-ai's typed `ProviderHttpError` instead of mutating plain `Error`s with a `.status` property; the generic `requestRemoteCompaction` error now carries `.status` (and response headers) too.
307
+
308
+ ### Fixed
309
+
310
+ - Fixed a regression where steering messages could be injected into history during an aborted in-flight tool batch, leaving them hidden from queue consumers for post-abort continue
311
+
312
+ ## [15.11.2] - 2026-06-11
313
+
314
+ ### Added
315
+
316
+ - `AgentTool.concurrency` now also accepts a per-call resolver function `(args) => "shared" | "exclusive"`, letting tools pick the scheduling mode from the call's arguments (a throwing resolver falls back to `"exclusive"`)
317
+
318
+ ### Fixed
319
+
320
+ - Fixed whitespace-only error tool results so Anthropic requests no longer 400 with `tool_result: content cannot be empty if is_error is true` and wedge the session on every subsequent turn
321
+
322
+ ## [15.11.0] - 2026-06-10
323
+
324
+ ### Breaking Changes
325
+
326
+ - Removed `compaction/index.ts` re-export of snapcompact helpers, so snapcompact utilities are no longer available from the agent compaction barrel and should be imported from `@oh-my-pi-zen/snapcompact`
327
+ - Removed the `convertToLlm` alias export from `compaction/messages` — it duplicated `defaultConvertToLlm` under a second name. Import `defaultConvertToLlm` (array form) or the new `convertMessageToLlm` (single-message form) instead
328
+
329
+ ### Added
330
+
331
+ - Added `convertMessageToLlm()`: the single-message core transformer behind `defaultConvertToLlm()`. Embedders with app-specific message roles should handle their own roles and delegate every core role (`user`/`developer`/`assistant`/`toolResult`/`custom`/`hookMessage`/`branchSummary`/`compactionSummary`) to it instead of duplicating the conversion — a duplicated `compactionSummary` case is how snapcompact frames once silently dropped off provider requests
332
+ - Added `pruneSupersededToolResults()` and the opt-in `PruneConfig.supersedeKey` hook so harnesses can prune stale tool results superseded by a newer read of the same file; superseded results are pruned ahead of age-based victims during overflow pruning and replaced with a `[Superseded by a newer read of this file]` placeholder. Without the new config, `pruneToolOutputs()` behavior is unchanged.
333
+ - Added `readToolSupersedeKey()` implementing the read-tool path/selector grammar (selector-free reads supersede range reads of the same file; URL-scheme paths exempt). Pruning honors prompt-cache economics: per-turn prunes only fire when the post-candidate suffix is small or the cache is cold (idle gap).
334
+ - Added the `snapcompact` compaction strategy via `@oh-my-pi-zen/snapcompact`: instead of an LLM summary, discarded history is printed onto dense bitmap frames and re-attached to the compaction summary message as image blocks. `CompactionSummaryMessage` gains an optional `images` field, `estimateTokens()` charges per attached frame, and frames persist under `preserveData.snapcompact` with an 8-frame middle-out eviction budget.
335
+ - Snapcompact frames are now rendered in a provider-aware shape (`SNAPCOMPACT_SHAPES` + `resolveSnapcompactShape(api)`), following the snapcompact 200k-token monolithic evals: Anthropic-family and unknown APIs get `8x8r-bw` (unscii-8 square cells, black ink, every line printed twice with the copy on a pale highlight band — read at F1 parity with raw text at ~2x lower cost and the most refusal-robust), Google gets `8x8r-sent` (sentence-hue ink, ~2.9x cheaper), and OpenAI gets `6x6u-sent` (unscii Lanczos-stretched to 6x6 cells — OpenAI bills a flat ~2.9k tokens per image, so frame count is the only cost lever) with `detail: "original"` on the frame images. `snapcompactCompact()` accepts `model`/`shape` options, frames persist their shape metadata, mixed-shape archives (provider switches, legacy 5x8 frames) are flagged in the reading instructions, and `snapcompactGeometry()`/`renderSnapcompactFrame()` now take a shape
336
+
337
+ ### Changed
338
+
339
+ - Compaction and branch-summary file lists are now a single `<files>` tag instead of `<read-files>`/`<modified-files>`: paths render as the grouped, prefix-folded directory tree the find/search tools emit (`# dir/` headers, bare basenames), each annotated `(Read)`, `(Write)`, or `(RW)` — modified files that were also read get `(RW)`. Legacy tags in summaries written by earlier versions are still stripped and self-heal on the next compaction
340
+
341
+ ### Fixed
342
+
343
+ - Fixed queued steering messages being drained into an externally aborted run: interrupting mid-tool execution (e.g. Enter with a pending steer) dequeued the steer into the dying run — it landed in history without a response and the post-abort resume saw an empty queue, so the agent stopped instead of continuing. Steering/follow-up/aside queue polls are now skipped once the run's abort signal fires, leaving the queue intact for `Agent.continue()`.
344
+ - Fixed `<read-files>` compaction lists recording the same file once per line-range/raw selector (`src/foo.ts:50-200`, `:raw`, `:1-50:raw`, …): read-tool selectors are now stripped before tracking, so reads dedupe to the base path and match their write/edit path when splitting read-only vs modified lists. Selector-polluted lists stored by earlier compactions self-heal on the next compaction. `readToolSupersedeKey()` now shares the same splitter (`splitReadSelector()`), gaining the `..` range alias and `L`-prefix forms it previously missed.
345
+ - Fixed `estimateTokens()` undercounting thinking-heavy assistant messages on replay: `thinkingSignature` payloads (OpenAI Responses encrypted reasoning items, Anthropic signed thinking blocks, etc.) and `redactedThinking.data` are now charged alongside the visible thinking text, so the local estimate tracks provider-reported usage instead of straddling the threshold on every turn ([#2275](https://github.com/cagedbird043/oh-my-pi-zen/issues/2275)).
346
+
347
+ ## [15.10.12] - 2026-06-10
348
+
349
+ ### Added
350
+
351
+ - Added `AgentLoopConfig.getDisableReasoning` so callers can override `disableReasoning` per LLM call, mirroring `getReasoning`.
352
+ - Added `transformProviderContext` to `AgentOptions`/`AgentLoopConfig`: an optional hook applied to the assembled provider context after conversion, normalization, and append-only handling, but before telemetry capture and provider send.
353
+
354
+ ### Fixed
355
+
356
+ - Fixed `Agent` runs so explicit reasoning disablement is forwarded to provider stream options and re-resolved per continuation, keeping mid-run thinking-off changes in sync with the next provider request.
357
+
358
+ ## [15.10.11] - 2026-06-10
359
+
360
+ ### Changed
361
+
362
+ - Editorial pass over the compaction prompts: fixed garbled grammar and missing articles, RFC-keyed prohibitions, deduped restated instructions; parsed markers (`<read-files>`/`<modified-files>`/`<previous-summary>`) and all output-format headings left byte-identical
363
+ - Catalog imports moved to the new `@oh-my-pi-zen/pi-catalog` package: subpath imports (`calculateCost`, Codex wire constants) plus catalog values previously taken from the `@oh-my-pi-zen/pi-ai` root (`getBundledModel`, `clampThinkingLevelForModel`), which pi-ai no longer re-exports; type-only `Model`/`Api`/`Effort` imports from pi-ai are unchanged
364
+
365
+ ## [15.10.8] - 2026-06-09
366
+
367
+ ### Added
368
+
369
+ - Added optional `fetch` overrides to `SummaryOptions` and `compact`/`generateSummary` so remote compaction can use custom HTTP clients
370
+ - Added optional `fetch` option to `ProxyStreamOptions` to control the HTTP request used by `streamProxy`
371
+ - Added optional `fetch` overrides to `requestOpenAiRemoteCompaction` and `requestRemoteCompaction` for injectable HTTP transport
372
+ - Added the upstream provider that served a request (`AssistantMessage.upstreamProvider`, e.g. OpenRouter's routed provider) as a `pi.gen_ai.response.upstream_provider` chat-span telemetry attribute, alongside the existing response id and time-to-first-chunk.
373
+
374
+ ## [15.10.5] - 2026-06-08
375
+
376
+ ### Removed
377
+
378
+ - Removed the `maxToolCallsPerTurn` option from `AgentOptions` and `AgentLoopConfig`, so assistant turns are no longer capped after a configured number of completed tool calls
379
+
380
+ ### Fixed
381
+
382
+ - Fixed stalled aborted assistant responses so the run now stops without waiting for provider iterator cleanup and returns the aborted message promptly
383
+ - Fixed `afterToolCall` handling so it now runs for completed tool executions even after a run is aborted so tool post-processing still applies
384
+ - Fixed `agentLoopDetailed().detailed()` so run telemetry and coverage are captured before `stream.result()` resolves.
385
+ - Fixed agent-loop stream invariants so `agentLoopContinue` no longer mutates the caller's message array, emitted assistant events snapshot mutable provider content, terminal provider events win over late abort signals, transformed tool arguments are reflected consistently in hooks/events, and successful run-end telemetry fires from the same finalization path as failures.
386
+ - Fixed tool result parsing to mark assistant tool outputs with unsupported content block shapes as errors and include a diagnostic text block
387
+ - Fixed GPT-5 Harmony leakage handling by recovering valid leaked tool calls when possible and discarding leaked partial assistant output before retrying
388
+ - Fixed tool-call cancellation handling so aborted tools are marked aborted with an explicit reason and do not report generic errors
389
+ - Fixed tool-call completion so assistant messages on abort keep only completed tool-call blocks and continue processing tool calls when a length stop still included results
390
+ - Fixed deliberate aborts (TTSR rule matches, user-interrupt labels) so a mid-stream tool-call block that never reached `toolcall_end` is retained on the aborted assistant message and paired with a placeholder result labeled by the abort reason, instead of being dropped; anonymous aborts (bare `abort()`) still drop incomplete tool calls whose partial arguments are unsafe to replay
391
+ - Fixed runs that stopped with reason `length` after returning tool results so execution continues to handle additional tool calls
392
+
393
+ ## [15.10.3] - 2026-06-08
394
+
395
+ ### Added
396
+
397
+ - Added a non-interrupting "aside" message channel to the agent loop (`AgentLoopConfig.getAsideMessages` / `Agent.setAsideMessageProvider`). Asides are drained at each step boundary (after a tool batch, before the next model call) and at the yield check, so passive notifications (e.g. background-job completions, late LSP diagnostics) reach the model *between requests* without waiting for the agent to stop and without aborting in-flight tools the way steering does.
398
+
399
+ ### Changed
400
+
401
+ - Changed core custom and hook messages to convert to `developer` messages for provider context.
402
+
403
+ ### Fixed
404
+
405
+ - Fixed the compaction spinner freezing (only repainting on a terminal resize) when compacting very large codex/OpenAI contexts. `buildOpenAiNativeHistory` re-collected the full known/custom tool-call id sets on every history-bearing message, rescanning the entire growing native history each time — O(N²) in history items — which blocked the event loop for seconds and starved the loader's animation timer and render scheduler. The sets are now maintained incrementally (linear), so building the compaction request no longer monopolizes the main thread.
406
+
407
+ ### Removed
408
+
409
+ - Removed the now-dead `<turn-aborted>` marker from the OpenAI compaction output user-message filter, since `transformMessages` no longer emits that note.
410
+ - Removed stale synthetic user-message tag filters from OpenAI remote compaction output preservation; developer messages are now dropped by role instead.
411
+ - Tool executions now receive the active turn `AbortSignal` unconditionally.
412
+
413
+ ## [15.10.2] - 2026-06-08
414
+
415
+ ### Fixed
416
+
417
+ - Fixed proxy stream silently returning a zero-token success response when the server disconnects without sending a `done` or `error` terminal SSE event. The stream now throws an error, surfacing the disconnect as an `error` event with `stopReason: "error"` and resolving `finalResultPromise`, instead of defaulting to `stopReason: "stop"` with empty content and leaving `stream.result()` callers hanging indefinitely.
418
+
419
+ ## [15.10.1] - 2026-06-07
420
+
421
+ ### Added
422
+
423
+ - Added optional `promptCacheKey` support to `AgentOptions` and `Agent` via a new `promptCacheKey` property so providers can receive a caller-provided prompt cache key
424
+ - Added optional `ApiKeyResolveContext` parameter to `getApiKey` in `AgentOptions` and `AgentLoopConfig` so key resolvers can receive retry context
425
+
426
+ ### Changed
427
+
428
+ - Enabled streaming API calls to re-resolve credentials through the `getApiKey` callback when retries occur after authentication-related errors
429
+ - `Agent.abort(reason?)` now forwards `reason` to the underlying `AbortController`, and the synthesized aborted assistant message carries that reason on `errorMessage` (string or non-`AbortError` `Error` message) instead of always defaulting to `"Request was aborted"`. Bare `abort()` is unchanged.
430
+
431
+ ### Fixed
432
+
433
+ - Fixed handling of short-lived API keys so that expired tokens are retried with a refreshed value during 401/usage-limit failures
434
+ - Ensured fallback API key resolution uses the initially configured static `apiKey` when `getApiKey` is present
435
+ - Wrapped oneshot LLM completions (`instrumentedCompleteSimple`: handoff, compaction/branch summaries) in an `EventLoopKeepalive`. These run outside the agent `#runLoop`, so without the keepalive Bun's event loop stopped servicing timers while parked on the completion promise — freezing host spinners (e.g. the `/handoff` loader) until an unrelated terminal resize poked the loop into rendering again.
436
+
437
+ ## [15.9.5] - 2026-06-05
438
+
439
+ ### Fixed
440
+
441
+ - Surfaced Anthropic stream failures whose message starts with `Output blocked by conten` as normal assistant error lifecycle events, so interactive clients render content-filter blocks instead of silently dropping the streaming bubble at `agent_end`.
442
+
443
+ ## [15.8.3] - 2026-06-03
444
+
445
+ ### Added
446
+
447
+ - Added `getReadToolPath(context)` to `@oh-my-pi-zen/pi-agent-core/compaction/tool-protection` to extract a paired `read` tool call's `path` for embedders building read-targeted protection matchers
448
+ - Added `getReadToolPath(context)` to `@oh-my-pi-zen/pi-agent-core/compaction/tool-protection`: the shared primitive that extracts a paired `read` tool call's `path` argument, so embedders can build their own read-targeted compaction protection matchers (e.g. plan-file reads) the same way `isSkillReadToolResult` does.
449
+
450
+ ## [15.8.2] - 2026-06-03
451
+
452
+ ### Added
453
+
454
+ - Added optional `AgentTool.matcherDigest(args)` hook: tools whose streamed arguments encode content in a wire grammar (patch formats, escaped strings) can expose the real content they introduce, so stream-content matchers (e.g. TTSR rules) run against plain source text instead of the wire format.
455
+
456
+ ### Fixed
457
+
458
+ - Fixed the agent loop wedging the model when a `write`/`edit` tool call is truncated by `stop_reason: length` (e.g. an OpenCode Zen / Claude-3.5-Haiku turn that emits >~1000 lines of code, blowing past the 8K `max_tokens` output cap). The skipped tool result now surfaces an actionable hint — naming `stop_reason: length` and telling the model to split the payload into multiple smaller calls — instead of the generic "Tool call was not executed because the assistant ended its turn" placeholder, which left the auto-continue loop re-emitting the same oversized payload until the user gave up. Tools are still NOT executed when the arguments are truncated. ([#1785](https://github.com/cagedbird043/oh-my-pi-zen/issues/1785))
459
+
460
+ ## [15.8.0] - 2026-06-02
461
+
462
+ ### Fixed
463
+
464
+ - Engaged GPT-5 Harmony leak detection on the committed assistant message (openai-codex only). `detectHarmonyLeakInAssistantMessage` now runs on the streamed `done`/`error` result and the trailing fallback, so a leaked final response is aborted-and-retried by the existing mitigation instead of being committed as-is. Tool-argument (`tool_arg`) scanning is gated on the trailing-garbage `T` co-signal and only fires when a caller supplies a parse boundary via `detectHarmonyLeakInAssistantMessage`'s new optional `toolArgParseEnd` resolver. The agent loop passes none — it cannot bound a streamed tool DSL — so that surface stays inert and a legitimate codex tool call whose content legitimately carries `to=functions.*` next to a channel word or non-Latin script (e.g. editing the harmony fixtures) is never hard-aborted.
465
+
466
+ ## [15.7.4] - 2026-05-31
467
+
468
+ ### Removed
469
+
470
+ - Removed the local-model `summarizeShakeRegions` compressor and related shake-summary prompt/types; shake now only provides mechanical artifact-backed elision primitives.
471
+
472
+ ## [15.7.3] - 2026-05-31
473
+
474
+ ### Added
475
+
476
+ - Added `shake` compaction primitives (`collectShakeRegions`, `applyShakeRegion`, `applyShakeRegions`, `summarizeShakeRegions`, `DEFAULT_SHAKE_CONFIG`, `AGGRESSIVE_SHAKE_CONFIG`, plus the `ShakeRegion`/`ShakeConfig`/`ShakeSummaryItem`/`ShakeSummaryComplete`/`ProtectedToolMatcher` types) under `@oh-my-pi-zen/pi-agent-core/compaction`. These detect heavy context regions — whole tool-call results plus large fenced/XML blocks — and either elide them with placeholders or extractively compress them through an injected completion backend (no LLM summary cut-point). The compressor is provider-agnostic: callers wire it to a local on-device model. Pure detection/mutation; no I/O.
477
+
478
+ ### Fixed
479
+
480
+ - Fixed tool-output pruning and shake protection for `read`: ordinary file/URL reads are now eligible for compaction, while `read` calls whose `path` starts with `skill://` remain protected like native `skill` results.
481
+
482
+ ## [15.5.15] - 2026-05-30
483
+
484
+ ### Added
485
+
486
+ - Added `maxToolCallsPerTurn` to `AgentLoopConfig`/`AgentOptions`, allowing callers to cut a streamed assistant turn after a completed tool-call batch and execute the runnable partial turn instead of waiting for the provider to yield.
487
+
488
+ ### Fixed
489
+
490
+ - Normalized `maxToolCallsPerTurn` to accept only positive integer limits, with non-finite or non-positive values treated as disabled
491
+
492
+ ## [15.5.14] - 2026-05-29
493
+
494
+ ### Fixed
495
+
496
+ - Fixed the agent loop abandoning tool calls that Anthropic adaptive/interleaved-thinking models (e.g. Opus) emit under `stop_reason: "end_turn"`. The previous gate only ran tools when `stopReason === "toolUse"`, so an `end_turn`+tool_use turn produced "Tool call was not executed because the assistant ended its turn" placeholders, made no progress, and could trap the model in a re-emit/abandon loop. `stop_reason` is never replayed on the wire and (verified against the live Anthropic Messages API) does not gate continuation validity, so `stop`/`end_turn` turns carrying tool_use blocks are now executed and the loop continues — exactly like `toolUse`. Only `length` (max_tokens truncation) still abandons, since the trailing tool call may have incomplete arguments. The continuation stays valid because `transformMessages` strips the now-untrustworthy thinking signature and the encoder downgrades the block to text.
497
+
498
+ ## [15.5.10] - 2026-05-28
499
+
500
+ ### Fixed
501
+
502
+ - Fixed compaction summarizer throws losing the provider's HTTP status. `generateSummary`, `generateHandoff`, `generateShortSummary`, and `generateTurnPrefixSummary` now route their `stopReason === "error"` throws through a `createSummarizationError` helper that copies `AssistantMessage.errorStatus` onto the thrown `Error` as `.status`, letting downstream consumers (e.g. `AgentSession.#isCompactionAuthFailure` in `@oh-my-pi-zen/pi-coding-agent`) branch on real provider 401/403s without regex-scraping the message body.
503
+
504
+ ## [15.5.0] - 2026-05-26
505
+
506
+ ### Added
507
+
508
+ - Added `approval` support to `AgentTool` declarations with the new `ToolTier` and `ToolApproval` APIs, allowing tools to declare capability tiers (`read`, `write`, or `exec`) and optional override/reason metadata for approval gating
509
+ - Added `formatApprovalDetails` on `AgentTool` to append custom detail text or lines to approval prompts
510
+ - Added exported `ToolTier` and `ToolApproval` type aliases for tool approval declarations
511
+
512
+ ### Fixed
513
+
514
+ - Fixed chat-request telemetry storing the raw scoped `serviceTier` value (`"openai-only"`/`"claude-only"`) in `OpenAIAttr.RequestServiceTier` instead of the resolved wire value (`"priority"`). Dashboards and alerts filtering on the concrete tier name (`service_tier == "priority"`) were broken by the scoped placeholder; `buildChatRequestAttributes` now runs the tier through `resolveServiceTier(serviceTier, provider)` before recording, keeping the `shouldSendServiceTier` gate intact so non-OpenAI providers continue to omit the attribute entirely.
515
+
516
+ ## [15.3.0] - 2026-05-25
517
+
518
+ ### Fixed
519
+
520
+ - Fixed `transformContext` receiving the loop config object as the `signal` argument instead of the actual `AbortSignal`, so hooks that check `signal.aborted` or call `signal.addEventListener` now work correctly under abort/timeout conditions
521
+ - Fixed `appendOnlyContext` not being re-evaluated after `setModel()` — the mode was decided once at session construction based on the initial model's provider, so switching from/to DeepSeek (or changing `provider.appendOnlyContext`) mid-session produced incorrect mode behavior
522
+
523
+ ## [15.2.3] - 2026-05-22
524
+
525
+ ### Added
526
+
527
+ - Added `onBeforeYield` hook support so user code can run right before the agent loop checks for follow-up messages
528
+
529
+ ## [15.1.3] - 2026-05-17
530
+
531
+ ### Added
532
+
533
+ - Added optional `telemetry` support to `generateSummary`, `generateHandoff`, `generateBranchSummary`, and `compact` options so compaction, handoff, and branch summary one-shot LLM calls can emit OpenTelemetry chat telemetry when enabled
534
+ - Added shared oneshot telemetry instrumentation for compaction, handoff, and branch summary calls, tagging spans with `pi.gen_ai.oneshot.kind` values such as `compaction_summary`, `compaction_short_summary`, `compaction_turn_prefix`, `handoff`, and `branch_summary`
535
+
536
+ ## [15.1.2] - 2026-05-15
537
+
538
+ ### Added
539
+
540
+ - Added `responseHeaders` to `ChatUsageEvent` and `ManualChatTelemetryOptions` so telemetry hooks receive captured lowercase upstream response headers for each chat span
541
+ - Added automatic gateway/proxy detection from response headers (`litellm`, `helicone`, `portkey`, `openrouter`) and stamped `pi.gen_ai.gateway.*` span attributes for detected routing metadata
542
+ - Added exported `detectGatewayFromHeaders` API for header-based gateway detection
543
+
544
+ ## [15.1.0] - 2026-05-15
545
+
546
+ ### Breaking Changes
547
+
548
+ - Removed the `@oh-my-pi-zen/pi-agent-core/compaction/handoff` exports from the package surface, including `extractHandoffDocument`, `createHandoffContext`, and `createHandoffFileName`
549
+ - Removed legacy telemetry constants from the public enum surface (including `AGGREGATE_ATTR`, `GenAIAttr.System`, and old `gen_ai.*` extension keys such as `gen_ai.request.service_tier`/cost/tool status/handoff fields) and replaced them with `OpenAIAttr`, `PiGenAIAttr`, and `PiGenAIAggregateAttr`
550
+
551
+ ### Added
552
+
553
+ - Added `generateHandoff(messages, model, apiKey, options)` to `@oh-my-pi-zen/pi-agent-core/compaction` to generate a handoff document by calling the model directly, using live system/tool context and optional metadata
554
+ - Added generation filtering so the returned handoff document now includes only text content blocks from the model output
555
+ - Added support for defining `AgentTool` schemas with Zod, with legacy TypeBox schemas still supported when generating tool schemas for model calls
556
+ - Added `OpenAIAttr`, `PiGenAIAttr`, and `PiGenAIAggregateAttr` exports so consumers can reference the new `openai.*` and `pi.gen_ai.*` telemetry attribute keys directly
557
+ - Added `onChatUsage` to `AgentTelemetryConfig`, an always-fired hook receiving a `ChatUsageEvent` for every chat step that produced usage. The event carries the chat `span`, `agent`, `conversationId`, `stepNumber`, `model`, `provider`, `serviceTier`, `usage`, optional `cost`, and resolved dynamic `attributes` — independent of whether a `costEstimator` is configured.
558
+ - Added `agentLoopDetailed(...)` and `agentLoopContinueDetailed(...)` helpers that return the same event stream plus a `detailed()` result with run `telemetry` and `coverage`
559
+ - Added `onRunEnd` to `AgentTelemetryConfig` to receive `AgentRunSummary` and `AgentRunCoverage` at the end of each invocation
560
+ - Added run-level telemetry and coverage types/helpers (for example `AgentRunSummary`, `AgentRunCoverage`, `aggregateAgentRunSummaries`, and `aggregateAgentRunCoverage`) to package exports
561
+ - Added generic telemetry extension hooks for dynamic span attributes, provider/agent-name normalization, per-step cost deltas, warning callbacks, bounded summary content capture, and manual chat telemetry for non-loop model calls.
562
+ - Added opt-in OpenTelemetry instrumentation on the agent loop. Pass `telemetry: {}` (or a richer `AgentTelemetryConfig`) on `AgentLoopConfig` / `AgentOptions` / `createAgentSession({ telemetry })` to emit GenAI-semantic-convention spans plus `pi.gen_ai.*` extension attributes:
563
+ - `invoke_agent {agent.name}` wraps each `agentLoop` invocation with `gen_ai.operation.name=invoke_agent`, agent identity, conversation id, and `pi.gen_ai.agent.step.count`.
564
+ - `chat {model}` per provider call, parented under `invoke_agent`, with OTEL request/response/usage attributes (`gen_ai.request.{model,stream,temperature,top_p,top_k,max_tokens,presence_penalty,stop_sequences}`, `gen_ai.response.{model,id,finish_reasons,time_to_first_chunk}`, `gen_ai.usage.{input_tokens,output_tokens,cache_read.input_tokens,cache_creation.input_tokens,reasoning.output_tokens}`) and project extensions for reasoning effort, tool choice, available tools, usage totals, and cost.
565
+ - `execute_tool {tool.name}` per tool call, parented under `invoke_agent`, with `gen_ai.tool.{name,call.id,description,type}` plus the active context so user/MCP/provider spans created inside `tool.execute()` attach as children.
566
+ - One-shot `handoff` span available via the public `recordHandoff(...)` helper for agent-to-agent transitions.
567
+ - Added `AgentTelemetryConfig` hooks (`onSpanStart`, `onSpanEnd`, `costEstimator`), `agent` identity, `attributes` envelope merged onto every span, `captureMessageContent` toggle (defaults to the `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` env var) emitting OTEL-shaped `gen_ai.input.messages` / `gen_ai.output.messages` / `gen_ai.system_instructions` / `gen_ai.tool.call.arguments` / `gen_ai.tool.call.result`, and tracer/tracerName override surfaces.
568
+ - Added `Agent#setTelemetry(config)` so consumers can swap or disable instrumentation between invocations.
569
+ - Added `@opentelemetry/api` as a runtime dependency; SDK setup (exporters, samplers, processors) remains the host's responsibility per standard OTEL conventions. When no SDK is registered, helpers fall through to no-op spans with zero overhead.
570
+ - Added compaction APIs under `@oh-my-pi-zen/pi-agent-core/compaction`, including context compaction, branch summarization, handoff prompt/context helpers, pruning, token budgeting, prompt templates, and OpenAI `/responses/compact` helpers.
571
+
572
+ ### Changed
573
+
574
+ - Changed handoff document generation to force `toolChoice: "none"` when calling the model so tool invocation is disabled during generation
575
+ - Changed `chat` spans to emit normalized provider identifiers in `gen_ai.provider.name` via OTEL-style values (for example `google` to `gcp.gemini`) instead of the legacy `gen_ai.system` label
576
+ - Changed service-tier telemetry to emit `openai.request.service_tier`/`openai.response.service_tier` only when supported by provider via `shouldSendServiceTier`, rather than always using `gen_ai.request.service_tier`
577
+ - Changed captured message payloads so full capture now records OTEL-structured message parts with `pi.gen_ai.request.messages`, `pi.gen_ai.system_instructions`, and `gen_ai.output.messages` including assistant `finish_reason`
578
+ - Changed the `agent_end` event payload to include optional `telemetry` and `coverage` fields when telemetry is enabled, while keeping the legacy payload shape when disabled
579
+ - Changed `invoke_agent` spans to include aggregate `pi.gen_ai.agent.*` attributes for chat/tool counts, latency, usage, cost, errors, and tool coverage
580
+
581
+ ### Fixed
582
+
583
+ - Fixed intent-field injection for tool schemas defined with Zod by converting them to wire schema before mutation
584
+ - Fixed token accounting in `ChatUsageEvent` and usage summaries so `inputTokens` and `totalTokens` now include cached read/write input tokens
585
+ - Fixed `execute_tool` span attributes so `pi.gen_ai.tool.status` and `error.type` now reflect run-level tool outcomes (`ok`, `error`, `skipped`, `blocked`, `timeout`, `aborted`) instead of mapping all non-ok cases the same way
586
+ - Fixed `onRunEnd` callbacks to be safe and idempotent by invoking them once per run and swallowing thrown callback errors so they cannot fail or duplicate successful runs
587
+ - Fixed run telemetry to count interrupted, blocked, or otherwise skipped tool calls so run coverage and tool counters now include those paths
588
+ - Fixed chat failure handling so failed chat steps are still represented in run summaries when provider streaming throws before yielding an assistant message
589
+ - Fixed double-counting of interrupted tool calls in run summaries: the `runTool` early-return on a queued steering interrupt now defers to the post-batch tail sweep so each call is recorded exactly once
590
+ - Fixed `coverage.toolsInvoked` and run-summary tool counters under-reporting tool calls embedded in an aborted/errored assistant message — those calls now record a collector orphan with status `aborted` or `error`
591
+ - Fixed `AgentRunSummary.usage.inputTokens` so it now includes `cache_read` and `cache_write` input tokens, matching `ChatUsageEvent.inputTokens`
592
+ - Fixed span lifecycle hooks (`onSpanStart`, `onSpanEnd`) so a thrown user callback is caught and surfaced via `onTelemetryWarning` (`on_span_start_failed` / `on_span_end_failed`) instead of leaking and aborting the surrounding span
593
+ - Fixed unbounded recursion in summary content capture when a captured value contains a cyclic or deeply nested array — array recursion now respects the same depth cap as plain-object recursion and replaces back-references with `"[Circular]"`
594
+
595
+ ## [15.0.1] - 2026-05-14
596
+
597
+ ### Breaking Changes
598
+
599
+ - Raised the minimum required Bun version from >=1.3.7 to >=1.3.14
600
+
601
+ ## [14.9.5] - 2026-05-12
602
+
603
+ ### Added
604
+
605
+ - Added an `isError?: boolean` field on `AgentToolResult` so tools can flag a non-throwing failure (e.g. an aggregator that catches per-entry errors). `coerceToolResult` preserves the flag and the agent loop surfaces it as a tool error on the wire.
606
+
607
+ ## [14.9.3] - 2026-05-10
608
+
609
+ ### Added
610
+
611
+ - Added `onHarmonyLeak` option on `Agent`/loop config to receive GPT-5 Harmony leak audit callbacks
612
+ - Added harmony-leak detection and audit exports to the package index for programmatic leak detection and recovery hooks
613
+
614
+ ### Changed
615
+
616
+ - Changed OpenAI Codex model runs to detect GPT-5 Harmony protocol leakage during streaming and automatically retry or recover tool calls instead of sending contaminated arguments downstream
617
+
618
+ ### Security
619
+
620
+ - Hardened tool-call handling against leaked `to=functions.*` protocol tails by truncating or retrying before execution
621
+ - Hardened failure handling so repeated GPT-5 Harmony leak mitigation is retried only up to two times before escalating to an explicit error
622
+
623
+ ## [14.9.0] - 2026-05-10
624
+
625
+ ### Added
626
+
627
+ - Added `Agent#metadata` field forwarded to every API request; callers can set arbitrary provider metadata (e.g. `metadata.user_id`) once and have it applied to all subsequent stream calls without modifying per-call options
628
+ - Added `Agent#setMetadataResolver(fn)` for installing a function that resolves request metadata at call time. The `metadata` getter dispatches through the resolver on every read (including the snapshot taken per `prompt()`), so callers reflect mutable external state (e.g. live OAuth account UUID after a token refresh) without manual re-syncs. Plain `agent.metadata = …` continues to set a static value and clears any installed resolver.
629
+ - Added an `onSseEvent` agent option and loop config forwarding path for raw provider SSE diagnostics.
630
+
631
+ ## [14.7.6] - 2026-05-07
632
+
633
+ ### Added
634
+
635
+ - Added `hideThinkingSummary` option/getter/setter on `Agent` and `AgentLoopConfig`. Forwarded to the underlying stream call so providers can omit reasoning/thinking summaries on demand.
636
+
637
+ ## [14.7.2] - 2026-05-06
638
+
639
+ ### Added
640
+
641
+ - Added `loadMode` option to `AgentTool` to mark built-in tools as `essential` for initial loading or `discoverable` for search activation
642
+ - Added optional `summary` field to `AgentTool` definitions for one-line text used in tool discovery indexes
643
+
644
+ ## [14.7.0] - 2026-05-04
645
+
646
+ ### Breaking Changes
647
+
648
+ - Changed `Agent` API types so `systemPrompt` is now a list of prompt strings, requiring callers to pass and update system prompts via string arrays
649
+
650
+ ### Changed
651
+
652
+ - Removed automatic project-context injection into each model call from loop logic
653
+
654
+ ### Removed
655
+
656
+ - Removed the `projectPrompt` field from agent state/context and the `setProjectPrompt` mutator
657
+
658
+ ## [14.6.2] - 2026-05-03
659
+
660
+ ### Fixed
661
+
662
+ - Fixed unhandled promise rejection when `getApiKey` or any other async error occurs during `streamAssistantResponse`: agent loop IIFEs now catch and route errors through `EventStream.fail()`, which terminates the `for await` loop and lets `Agent#runLoop`'s catch block create a proper error assistant message instead of crashing
663
+
664
+ ## [14.6.0] - 2026-05-02
665
+
666
+ ### Fixed
667
+
668
+ - Fixed request cancellation before provider events by emitting an aborted assistant message and ending the stream with `stopReason: "aborted"`
669
+
670
+ ## [14.5.10] - 2026-04-30
671
+
672
+ ### Added
673
+
674
+ - Added an `onResponse` stream option for observing provider response metadata after response headers arrive.
675
+
676
+ ## [14.2.0] - 2026-04-23
677
+
678
+ ### Changed
679
+
680
+ - Changed tool dispatch to match model-returned tool calls by either internal tool name or custom wire name, enabling custom OpenAI tool names such as `apply_patch`.
681
+
682
+ ## [14.0.1] - 2026-04-08
683
+
684
+ ### Added
685
+
686
+ - Added `onAssistantMessageEvent` callback option to inspect assistant streaming events before they are emitted, enabling abort decisions before buffered events continue flowing
687
+ - Added `setAssistantMessageEventInterceptor()` method to dynamically set or update the assistant message event interceptor
688
+
689
+ ## [13.13.0] - 2026-03-18
690
+
691
+ ### Added
692
+
693
+ - Added `startup.checkUpdate` setting, set to `true` by default, can be disabled to skip the update check on agent initialization
694
+
695
+ ## [13.12.7] - 2026-03-16
696
+
697
+ ### Added
698
+
699
+ - Added overload for `prompt()` method accepting a string input with optional options parameter
700
+
701
+ ### Fixed
702
+
703
+ - Fixed stale forced toolChoice being passed to provider after tools are refreshed mid-turn
704
+
705
+ ## [13.9.16] - 2026-03-10
706
+
707
+ ### Added
708
+
709
+ - Added `onPayload` option to `AgentOptions` to inspect or replace provider payloads before they are sent
710
+
711
+ ## [13.9.3] - 2026-03-07
712
+
713
+ ### Added
714
+
715
+ - Exported `ThinkingLevel` selector constants and types for configuring agent reasoning behavior
716
+ - Added `inherit` thinking level option to defer reasoning configuration to higher-level selectors
717
+ - Added `serviceTier` option to configure service tier for agent requests
718
+
719
+ ### Changed
720
+
721
+ - Changed `thinkingLevel` from required string to optional `Effort` type, allowing undefined state
722
+ - Updated `setThinkingLevel()` method to accept `Effort | undefined` instead of `ThinkingLevel` string
723
+
724
+ ## [13.4.0] - 2026-03-01
725
+
726
+ ### Added
727
+
728
+ - Added `getToolChoice` option to dynamically override tool choice per LLM call
729
+
730
+ ## [13.3.8] - 2026-02-28
731
+
732
+ ### Changed
733
+
734
+ - Changed intent field name from `agent__intent` to `_i` in tool schemas
735
+
736
+ ### Fixed
737
+
738
+ - Fixed synthetic tool result text formatting so aborted/error tool results no longer emit `Tool execution was aborted.: Request was aborted` style punctuation.
739
+
740
+ ## [13.3.7] - 2026-02-27
741
+
742
+ ### Added
743
+
744
+ - Added `lenientArgValidation` option to tools to allow graceful handling of argument validation errors by passing raw arguments to execute() instead of returning an error to the LLM
745
+
746
+ ## [13.3.1] - 2026-02-26
747
+
748
+ ### Added
749
+
750
+ - Added `topP`, `topK`, `minP`, `presencePenalty`, and `repetitionPenalty` options to `AgentOptions` for fine-grained sampling control
751
+ - Added getter and setter properties for sampling parameters on the `Agent` class to allow runtime configuration
752
+
753
+ ## [13.1.0] - 2026-02-23
754
+
755
+ ### Changed
756
+
757
+ - Removed per-tool `agent__intent` field description from injected schema to reduce token usage; intent format is now documented once in the system prompt instead of repeated in every tool definition
758
+
759
+ ## [12.19.0] - 2026-02-22
760
+
761
+ ### Changed
762
+
763
+ - Updated tool result messages to include error details when tool execution fails
764
+
765
+ ## [12.14.0] - 2026-02-19
766
+
767
+ ### Added
768
+
769
+ - Added `intentTracing` option to enable intent goal extraction from tool calls, allowing models to specify high-level goals via a required `_intent` field that is automatically injected into tool schemas and stripped from arguments before execution
770
+
771
+ ## [12.11.0] - 2026-02-19
772
+
773
+ ### Added
774
+
775
+ - Exported `AgentBusyError` exception class for handling concurrent agent operations
776
+
777
+ ### Changed
778
+
779
+ - Agent now throws `AgentBusyError` instead of generic `Error` when attempting concurrent operations
780
+
781
+ ## [12.8.0] - 2026-02-16
782
+
783
+ ### Added
784
+
785
+ - Added `transformToolCallArguments` option to `AgentOptions` and `AgentLoopConfig` for transforming tool call arguments before execution (e.g. secret deobfuscation)
786
+
787
+ ## [12.2.0] - 2026-02-13
788
+
789
+ ### Added
790
+
791
+ - Added `providerSessionState` option to share provider state map for session-scoped transport and session caches
792
+ - Added `preferWebsockets` option to hint that websocket transport should be preferred when supported by the provider implementation
793
+
794
+ ## [11.10.0] - 2026-02-10
795
+
796
+ ### Added
797
+
798
+ - Added `temperature` option to `AgentOptions` to control LLM sampling temperature
799
+ - Added `temperature` getter and setter to `Agent` class for runtime configuration
800
+
801
+ ## [11.6.0] - 2026-02-07
802
+
803
+ ### Added
804
+
805
+ - Added `hasQueuedMessages()` method to check for pending steering/follow-up messages
806
+ - Resume queued steering and follow-up messages from `continue()` after auto-compaction
807
+
808
+ ### Changed
809
+
810
+ - Extracted `dequeueSteeringMessages()` and `dequeueFollowUpMessages()` from inline config callbacks
811
+ - Added `skipInitialSteeringPoll` option to `_runLoop()` for correct queue resume ordering
812
+
813
+ ## [11.3.0] - 2026-02-06
814
+
815
+ ### Added
816
+
817
+ - Added `maxRetryDelayMs` option to AgentOptions to cap server-requested retry delays, allowing higher-level retry logic to handle long waits with user visibility
818
+
819
+ ### Changed
820
+
821
+ - Updated ThinkingLevel documentation to include support for gpt-5.3 and gpt-5.3-codex models with 'xhigh' thinking level
822
+
823
+ ## [11.2.0] - 2026-02-05
824
+
825
+ ### Fixed
826
+
827
+ - Fixed handling of aborted requests to properly throw abort errors when stream terminates without a terminal event
828
+
829
+ ## [10.5.0] - 2026-02-04
830
+
831
+ ### Added
832
+
833
+ - Added `concurrency` option to `AgentTool` to control tool scheduling: "shared" (default, runs in parallel) or "exclusive" (runs alone)
834
+ - Implemented parallel execution of shared tools within a single agent turn for improved performance
835
+
836
+ ### Changed
837
+
838
+ - Refactored tool execution to support concurrent scheduling with proper interrupt handling and steering message checks
839
+
840
+ ## [9.2.2] - 2026-01-31
841
+
842
+ ### Added
843
+
844
+ - Added toolChoice option to AgentPromptOptions for controlling tool selection
845
+
846
+ ## [8.2.0] - 2026-01-24
847
+
848
+ ### Changed
849
+
850
+ - Updated TypeScript configuration for better publish-time configuration handling with tsconfig.publish.json
851
+
852
+ ## [8.0.0] - 2026-01-23
853
+
854
+ ### Added
855
+
856
+ - Added `nonAbortable` option to tools to ignore abort signals during execution
857
+
858
+ ## [6.8.0] - 2026-01-20
859
+
860
+ ### Changed
861
+
862
+ - Updated proxy stream processing to use utility function for reading lines
863
+
864
+ ## [6.2.0] - 2026-01-19
865
+
866
+ ### Added
867
+
868
+ - Enhanced getToolContext to receive tool call batch information including batchId, index, total count, and tool call details
869
+
870
+ ## [5.6.7] - 2026-01-18
871
+
872
+ ### Fixed
873
+
874
+ - Added proper tool result messages for tool calls that are aborted or error out
875
+ - Ensured tool_use/tool_result pairing is maintained when tool execution fails
876
+
877
+ ## [4.6.0] - 2026-01-12
878
+
879
+ ### Changed
880
+
881
+ - Modified assistant message handling to split messages around tool results for improved readability when using Cursor tools
882
+
883
+ ### Fixed
884
+
885
+ - Fixed tool result ordering in Cursor mode by buffering results and emitting them at the correct position within assistant messages
886
+
887
+ ## [4.3.0] - 2026-01-11
888
+
889
+ ### Added
890
+
891
+ - Added `cursorExecHandlers` and `cursorOnToolResult` options for local tool execution with cursor-based streaming
892
+ - Added `emitExternalEvent` method to allow external event injection into the agent state
893
+
894
+ ## [4.0.0] - 2026-01-10
895
+
896
+ ### Added
897
+
898
+ - Added `popLastSteer()` and `popLastFollowUp()` methods to remove and return the last queued message (LIFO) for dequeue operations
899
+ - `thinkingBudgets` option on `Agent` and `AgentOptions` to customize token budgets per thinking level
900
+ - `sessionId` option on `Agent` to forward session identifiers to LLM providers for session-based caching
901
+
902
+ ### Fixed
903
+
904
+ - `minimal` thinking level now maps to `minimal` reasoning effort instead of being treated as `low`
905
+
906
+ ## [3.33.0] - 2026-01-08
907
+
908
+ ### Fixed
909
+
910
+ - Ensured aborted assistant responses always include an error message for callers.
911
+ - Filtered thinking blocks from Cerebras request context to keep multi-turn prompts compatible.
912
+
913
+ ## [3.21.0] - 2026-01-06
914
+
915
+ ### Changed
916
+
917
+ - Switched from local `@oh-my-pi-zen/pi-ai` to upstream `@mariozechner/pi-ai` package
918
+
919
+ ### Added
920
+
921
+ - Added `sessionId` option for provider caching (e.g., OpenAI Codex session-based prompt caching)
922
+ - Added `sessionId` getter/setter on Agent class for runtime session switching
923
+
924
+ ## [3.20.0] - 2026-01-06
925
+
926
+ ### Breaking Changes
927
+
928
+ - Replaced `queueMessage`/`queueMode` with steering + follow-up queues: use `steer`, `setSteeringMode`, and `getSteeringMode` for mid-run interruptions, and `followUp`, `setFollowUpMode`, and `getFollowUpMode` for post-turn messages
929
+ - Agent loop callbacks now use `getSteeringMessages` and `getFollowUpMessages` instead of `getQueuedMessages`
930
+
931
+ ### Added
932
+
933
+ - Added follow-up message queue support so new user messages can continue a run after the agent would otherwise stop
934
+ - Added `RenderResultOptions.spinnerFrame` for animated tool-result rendering
935
+
936
+ ### Changed
937
+
938
+ - `prompt()` and `continue()` now throw when the agent is already streaming; use steering or follow-up queues instead
939
+
940
+ ## [3.4.1337] - 2026-01-03
941
+
942
+ ### Added
943
+
944
+ - Added `popMessage()` method to Agent class for removing and retrieving the last message
945
+ - Added abort signal checks during response streaming for faster interruption handling
946
+
947
+ ### Fixed
948
+
949
+ - Fixed abort handling to properly return aborted message state when stream is interrupted mid-response
950
+
951
+ ## [1.341.0] - 2026-01-03
952
+
953
+ ### Added
954
+
955
+ - Added `interruptMode` option to control when queued messages interrupt tool execution.
956
+ - Implemented "immediate" mode (default) to check queue after each tool and interrupt remaining tools.
957
+ - Implemented "wait" mode to defer queue processing until the entire turn completes.
958
+ - Added getter and setter methods for `interruptMode` on Agent class.
959
+
960
+ ## [1.337.1] - 2026-01-02
961
+
962
+ ### Changed
963
+
964
+ - Forked to @oh-my-pi scope with unified versioning across all packages
965
+
966
+ ## [1.337.0] - 2026-01-02
967
+
968
+ Initial release under @oh-my-pi scope. See previous releases at [badlogic/pi-mono](https://github.com/badlogic/pi-mono).
969
+
970
+ ## [0.38.0] - 2026-01-08
971
+
972
+ ### Added
973
+
974
+ - `thinkingBudgets` option on `Agent` and `AgentOptions` to customize token budgets per thinking level ([#529](https://github.com/badlogic/pi-mono/pull/529) by [@melihmucuk](https://github.com/melihmucuk))
975
+
976
+ ## [0.37.3] - 2026-01-06
977
+
978
+ ### Added
979
+
980
+ - `sessionId` option on `Agent` to forward session identifiers to LLM providers for session-based caching.
981
+
982
+ ## [0.37.0] - 2026-01-05
983
+
984
+ ### Fixed
985
+
986
+ - `minimal` thinking level now maps to `minimal` reasoning effort instead of being treated as `low`.
987
+
988
+ ## [0.32.0] - 2026-01-03
989
+
990
+ ### Breaking Changes
991
+
992
+ - **Queue API replaced with steer/followUp**: The `queueMessage()` method has been split into two methods with different delivery semantics ([#403](https://github.com/badlogic/pi-mono/issues/403)):
993
+ - `steer(msg)`: Interrupts the agent mid-run. Delivered after current tool execution, skips remaining tools.
994
+ - `followUp(msg)`: Waits until the agent finishes. Delivered only when there are no more tool calls or steering messages.
995
+ - **Queue mode renamed**: `queueMode` option renamed to `steeringMode`. Added new `followUpMode` option. Both control whether messages are delivered one-at-a-time or all at once.
996
+ - **AgentLoopConfig callbacks renamed**: `getQueuedMessages` split into `getSteeringMessages` and `getFollowUpMessages`.
997
+ - **Agent methods renamed**:
998
+ - `queueMessage()` → `steer()` and `followUp()`
999
+ - `clearMessageQueue()` → `clearSteeringQueue()`, `clearFollowUpQueue()`, `clearAllQueues()`
1000
+ - `setQueueMode()`/`getQueueMode()` → `setSteeringMode()`/`getSteeringMode()` and `setFollowUpMode()`/`getFollowUpMode()`
1001
+
1002
+ ### Fixed
1003
+
1004
+ - `prompt()` and `continue()` now throw if called while the agent is already streaming, preventing race conditions and corrupted state. Use `steer()` or `followUp()` to queue messages during streaming, or `await` the previous call.
1005
+
1006
+ ## [0.31.0] - 2026-01-02
1007
+
1008
+ ### Breaking Changes
1009
+
1010
+ - **Transport abstraction removed**: `ProviderTransport`, `AppTransport`, and `AgentTransport` interface have been removed. Use the `streamFn` option directly for custom streaming implementations.
1011
+ - **Agent options renamed**:
1012
+ - `transport` → removed (use `streamFn` instead)
1013
+ - `messageTransformer` → `convertToLlm`
1014
+ - `preprocessor` → `transformContext`
1015
+ - **`AppMessage` renamed to `AgentMessage`**: All references to `AppMessage` have been renamed to `AgentMessage` for consistency.
1016
+ - **`CustomMessages` renamed to `CustomAgentMessages`**: The declaration merging interface has been renamed.
1017
+ - **`UserMessageWithAttachments` and `Attachment` types removed**: Attachment handling is now the responsibility of the `convertToLlm` function.
1018
+ - **Agent loop moved from `@oh-my-pi-zen/pi-ai`**: The `agentLoop`, `agentLoopContinue`, and related types have moved to this package. Import from `@oh-my-pi-zen/pi-agent` instead.
1019
+
1020
+ ### Added
1021
+
1022
+ - `streamFn` option on `Agent` for custom stream implementations. Default uses `streamSimple` from pi-ai.
1023
+ - `streamProxy()` utility function for browser apps that need to proxy LLM calls through a backend server. Replaces the removed `AppTransport`.
1024
+ - `getApiKey` option for dynamic API key resolution (useful for expiring OAuth tokens like GitHub Copilot).
1025
+ - `agentLoop()` and `agentLoopContinue()` low-level functions for running the agent loop without the `Agent` class wrapper.
1026
+ - New exported types: `AgentLoopConfig`, `AgentContext`, `AgentTool`, `AgentToolResult`, `AgentToolUpdateCallback`, `StreamFn`.
1027
+
1028
+ ### Changed
1029
+
1030
+ - `Agent` constructor now has all options optional (empty options use defaults).
1031
+ - `queueMessage()` is now synchronous (no longer returns a Promise).