@mariozechner/pi-coding-agent 0.61.1 → 0.63.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.
Files changed (214) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/README.md +2 -2
  3. package/dist/cli/file-processor.d.ts.map +1 -1
  4. package/dist/cli/file-processor.js +4 -0
  5. package/dist/cli/file-processor.js.map +1 -1
  6. package/dist/core/agent-session.d.ts +15 -6
  7. package/dist/core/agent-session.d.ts.map +1 -1
  8. package/dist/core/agent-session.js +94 -90
  9. package/dist/core/agent-session.js.map +1 -1
  10. package/dist/core/auth-storage.d.ts +3 -1
  11. package/dist/core/auth-storage.d.ts.map +1 -1
  12. package/dist/core/auth-storage.js +5 -2
  13. package/dist/core/auth-storage.js.map +1 -1
  14. package/dist/core/compaction/branch-summarization.d.ts +2 -0
  15. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  16. package/dist/core/compaction/branch-summarization.js +2 -2
  17. package/dist/core/compaction/branch-summarization.js.map +1 -1
  18. package/dist/core/compaction/compaction.d.ts +2 -2
  19. package/dist/core/compaction/compaction.d.ts.map +1 -1
  20. package/dist/core/compaction/compaction.js +9 -9
  21. package/dist/core/compaction/compaction.js.map +1 -1
  22. package/dist/core/export-html/index.d.ts +2 -2
  23. package/dist/core/export-html/index.d.ts.map +1 -1
  24. package/dist/core/export-html/index.js +7 -6
  25. package/dist/core/export-html/index.js.map +1 -1
  26. package/dist/core/export-html/tool-renderer.d.ts +2 -2
  27. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  28. package/dist/core/export-html/tool-renderer.js +41 -16
  29. package/dist/core/export-html/tool-renderer.js.map +1 -1
  30. package/dist/core/extensions/index.d.ts +3 -2
  31. package/dist/core/extensions/index.d.ts.map +1 -1
  32. package/dist/core/extensions/index.js.map +1 -1
  33. package/dist/core/extensions/loader.d.ts.map +1 -1
  34. package/dist/core/extensions/loader.js +12 -2
  35. package/dist/core/extensions/loader.js.map +1 -1
  36. package/dist/core/extensions/runner.d.ts +4 -7
  37. package/dist/core/extensions/runner.d.ts.map +1 -1
  38. package/dist/core/extensions/runner.js +27 -38
  39. package/dist/core/extensions/runner.js.map +1 -1
  40. package/dist/core/extensions/types.d.ts +44 -9
  41. package/dist/core/extensions/types.d.ts.map +1 -1
  42. package/dist/core/extensions/types.js.map +1 -1
  43. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  44. package/dist/core/extensions/wrapper.js +2 -8
  45. package/dist/core/extensions/wrapper.js.map +1 -1
  46. package/dist/core/index.d.ts +1 -0
  47. package/dist/core/index.d.ts.map +1 -1
  48. package/dist/core/index.js +1 -0
  49. package/dist/core/index.js.map +1 -1
  50. package/dist/core/model-registry.d.ts +18 -2
  51. package/dist/core/model-registry.d.ts.map +1 -1
  52. package/dist/core/model-registry.js +83 -69
  53. package/dist/core/model-registry.js.map +1 -1
  54. package/dist/core/model-resolver.d.ts.map +1 -1
  55. package/dist/core/model-resolver.js +4 -4
  56. package/dist/core/model-resolver.js.map +1 -1
  57. package/dist/core/output-guard.d.ts +6 -0
  58. package/dist/core/output-guard.d.ts.map +1 -0
  59. package/dist/core/output-guard.js +59 -0
  60. package/dist/core/output-guard.js.map +1 -0
  61. package/dist/core/package-manager.d.ts +1 -0
  62. package/dist/core/package-manager.d.ts.map +1 -1
  63. package/dist/core/package-manager.js +77 -10
  64. package/dist/core/package-manager.js.map +1 -1
  65. package/dist/core/prompt-templates.d.ts +2 -1
  66. package/dist/core/prompt-templates.d.ts.map +1 -1
  67. package/dist/core/prompt-templates.js +30 -32
  68. package/dist/core/prompt-templates.js.map +1 -1
  69. package/dist/core/resolve-config-value.d.ts +6 -0
  70. package/dist/core/resolve-config-value.d.ts.map +1 -1
  71. package/dist/core/resolve-config-value.js +37 -5
  72. package/dist/core/resolve-config-value.js.map +1 -1
  73. package/dist/core/resource-loader.d.ts +6 -5
  74. package/dist/core/resource-loader.d.ts.map +1 -1
  75. package/dist/core/resource-loader.js +136 -108
  76. package/dist/core/resource-loader.js.map +1 -1
  77. package/dist/core/sdk.d.ts +2 -2
  78. package/dist/core/sdk.d.ts.map +1 -1
  79. package/dist/core/sdk.js +13 -22
  80. package/dist/core/sdk.js.map +1 -1
  81. package/dist/core/settings-manager.d.ts +2 -0
  82. package/dist/core/settings-manager.d.ts.map +1 -1
  83. package/dist/core/settings-manager.js +3 -0
  84. package/dist/core/settings-manager.js.map +1 -1
  85. package/dist/core/skills.d.ts +2 -1
  86. package/dist/core/skills.d.ts.map +1 -1
  87. package/dist/core/skills.js +25 -1
  88. package/dist/core/skills.js.map +1 -1
  89. package/dist/core/slash-commands.d.ts +2 -3
  90. package/dist/core/slash-commands.d.ts.map +1 -1
  91. package/dist/core/slash-commands.js.map +1 -1
  92. package/dist/core/source-info.d.ts +18 -0
  93. package/dist/core/source-info.d.ts.map +1 -0
  94. package/dist/core/source-info.js +19 -0
  95. package/dist/core/source-info.js.map +1 -0
  96. package/dist/core/system-prompt.d.ts.map +1 -1
  97. package/dist/core/system-prompt.js +3 -38
  98. package/dist/core/system-prompt.js.map +1 -1
  99. package/dist/core/timings.d.ts +1 -0
  100. package/dist/core/timings.d.ts.map +1 -1
  101. package/dist/core/timings.js +6 -0
  102. package/dist/core/timings.js.map +1 -1
  103. package/dist/core/tools/bash.d.ts +19 -9
  104. package/dist/core/tools/bash.d.ts.map +1 -1
  105. package/dist/core/tools/bash.js +151 -59
  106. package/dist/core/tools/bash.js.map +1 -1
  107. package/dist/core/tools/edit-diff.d.ts +23 -1
  108. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  109. package/dist/core/tools/edit-diff.js +100 -32
  110. package/dist/core/tools/edit-diff.js.map +1 -1
  111. package/dist/core/tools/edit.d.ts +30 -6
  112. package/dist/core/tools/edit.d.ts.map +1 -1
  113. package/dist/core/tools/edit.js +172 -59
  114. package/dist/core/tools/edit.js.map +1 -1
  115. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  116. package/dist/core/tools/file-mutation-queue.js +4 -4
  117. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  118. package/dist/core/tools/find.d.ts +11 -4
  119. package/dist/core/tools/find.d.ts.map +1 -1
  120. package/dist/core/tools/find.js +76 -27
  121. package/dist/core/tools/find.js.map +1 -1
  122. package/dist/core/tools/grep.d.ts +15 -4
  123. package/dist/core/tools/grep.d.ts.map +1 -1
  124. package/dist/core/tools/grep.js +83 -29
  125. package/dist/core/tools/grep.js.map +1 -1
  126. package/dist/core/tools/index.d.ts +67 -21
  127. package/dist/core/tools/index.d.ts.map +1 -1
  128. package/dist/core/tools/index.js +50 -26
  129. package/dist/core/tools/index.js.map +1 -1
  130. package/dist/core/tools/ls.d.ts +9 -3
  131. package/dist/core/tools/ls.d.ts.map +1 -1
  132. package/dist/core/tools/ls.js +67 -13
  133. package/dist/core/tools/ls.js.map +1 -1
  134. package/dist/core/tools/read.d.ts +10 -3
  135. package/dist/core/tools/read.d.ts.map +1 -1
  136. package/dist/core/tools/read.js +110 -51
  137. package/dist/core/tools/read.js.map +1 -1
  138. package/dist/core/tools/render-utils.d.ts +21 -0
  139. package/dist/core/tools/render-utils.d.ts.map +1 -0
  140. package/dist/core/tools/render-utils.js +49 -0
  141. package/dist/core/tools/render-utils.js.map +1 -0
  142. package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  143. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
  144. package/dist/core/tools/tool-definition-wrapper.js +30 -0
  145. package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
  146. package/dist/core/tools/write.d.ts +9 -3
  147. package/dist/core/tools/write.d.ts.map +1 -1
  148. package/dist/core/tools/write.js +162 -27
  149. package/dist/core/tools/write.js.map +1 -1
  150. package/dist/index.d.ts +3 -2
  151. package/dist/index.d.ts.map +1 -1
  152. package/dist/index.js +2 -1
  153. package/dist/index.js.map +1 -1
  154. package/dist/main.d.ts.map +1 -1
  155. package/dist/main.js +56 -18
  156. package/dist/main.js.map +1 -1
  157. package/dist/modes/interactive/components/bash-execution.d.ts +0 -1
  158. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  159. package/dist/modes/interactive/components/bash-execution.js +18 -5
  160. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  161. package/dist/modes/interactive/components/tool-execution.d.ts +15 -40
  162. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  163. package/dist/modes/interactive/components/tool-execution.js +126 -679
  164. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  165. package/dist/modes/interactive/interactive-mode.d.ts +4 -11
  166. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  167. package/dist/modes/interactive/interactive-mode.js +146 -93
  168. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  169. package/dist/modes/interactive/theme/theme.d.ts +3 -0
  170. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  171. package/dist/modes/interactive/theme/theme.js +14 -0
  172. package/dist/modes/interactive/theme/theme.js.map +1 -1
  173. package/dist/modes/print-mode.d.ts +1 -1
  174. package/dist/modes/print-mode.d.ts.map +1 -1
  175. package/dist/modes/print-mode.js +84 -78
  176. package/dist/modes/print-mode.js.map +1 -1
  177. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  178. package/dist/modes/rpc/rpc-mode.js +27 -20
  179. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  180. package/dist/modes/rpc/rpc-types.d.ts +3 -4
  181. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  182. package/dist/modes/rpc/rpc-types.js.map +1 -1
  183. package/dist/utils/image-resize.d.ts +5 -5
  184. package/dist/utils/image-resize.d.ts.map +1 -1
  185. package/dist/utils/image-resize.js +45 -94
  186. package/dist/utils/image-resize.js.map +1 -1
  187. package/docs/development.md +3 -1
  188. package/docs/extensions.md +74 -33
  189. package/docs/models.md +6 -0
  190. package/docs/rpc.md +11 -2
  191. package/docs/settings.md +12 -0
  192. package/docs/tui.md +2 -2
  193. package/examples/extensions/built-in-tool-renderer.ts +8 -8
  194. package/examples/extensions/commands.ts +3 -3
  195. package/examples/extensions/custom-compaction.ts +17 -4
  196. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  197. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  198. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  199. package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
  200. package/examples/extensions/handoff.ts +5 -2
  201. package/examples/extensions/minimal-mode.ts +14 -14
  202. package/examples/extensions/qna.ts +5 -2
  203. package/examples/extensions/question.ts +2 -2
  204. package/examples/extensions/questionnaire.ts +2 -2
  205. package/examples/extensions/subagent/index.ts +2 -2
  206. package/examples/extensions/summarize.ts +15 -4
  207. package/examples/extensions/todo.ts +2 -2
  208. package/examples/extensions/truncated-tool.ts +2 -2
  209. package/examples/extensions/with-deps/package-lock.json +2 -2
  210. package/examples/extensions/with-deps/package.json +1 -1
  211. package/examples/sdk/04-skills.ts +8 -2
  212. package/examples/sdk/08-prompt-templates.ts +2 -1
  213. package/examples/sdk/12-full-control.ts +0 -1
  214. package/package.json +5 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,112 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.63.0] - 2026-03-27
4
+
5
+ ### Breaking Changes
6
+
7
+ - `ModelRegistry.getApiKey(model)` has been replaced by `getApiKeyAndHeaders(model)` because `models.json` auth and header values can now resolve dynamically on every request. Extensions and SDK integrations that previously fetched only an API key must now fetch request auth per call and forward both `apiKey` and `headers`. Use `getApiKeyForProvider(provider)` only when you explicitly want provider-level API key lookup without model headers or `authHeader` handling ([#1835](https://github.com/badlogic/pi-mono/issues/1835))
8
+ - Removed deprecated direct `minimax` and `minimax-cn` model IDs, keeping only `MiniMax-M2.7` and `MiniMax-M2.7-highspeed`. Update pinned model IDs to one of those supported direct MiniMax models, or use another provider route that still exposes the older IDs ([#2596](https://github.com/badlogic/pi-mono/pull/2596) by [@liyuan97](https://github.com/liyuan97))
9
+
10
+ #### Migration Notes
11
+
12
+ Before:
13
+
14
+ ```ts
15
+ const apiKey = await ctx.modelRegistry.getApiKey(model);
16
+ return streamSimple(model, messages, { apiKey });
17
+ ```
18
+
19
+ After:
20
+
21
+ ```ts
22
+ const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
23
+ if (!auth.ok) throw new Error(auth.error);
24
+ return streamSimple(model, messages, {
25
+ apiKey: auth.apiKey,
26
+ headers: auth.headers,
27
+ });
28
+ ```
29
+
30
+ ### Added
31
+
32
+ - Added `sessionDir` setting support in global and project `settings.json` so session storage can be configured without passing `--session-dir` on every invocation ([#2598](https://github.com/badlogic/pi-mono/pull/2598) by [@smcllns](https://github.com/smcllns))
33
+ - Added a startup onboarding hint in the interactive header telling users pi can explain its own features and documentation ([#2620](https://github.com/badlogic/pi-mono/pull/2620) by [@ferologics](https://github.com/ferologics))
34
+ - Added `edit` tool multi-edit support so one call can update multiple separate, disjoint regions in the same file while matching all replacements against the original file content
35
+ - Added support for `PI_TUI_WRITE_LOG` directory paths, creating a unique log file (`tui-<timestamp>-<pid>.log`) per instance for easier debugging of multiple pi sessions ([#2508](https://github.com/badlogic/pi-mono/pull/2508) by [@mrexodia](https://github.com/mrexodia))
36
+
37
+ ### Changed
38
+
39
+ ### Fixed
40
+
41
+ - Fixed file mutation queue ordering so concurrent `edit` and `write` operations targeting the same file stay serialized in request order instead of being reordered during queue-key resolution
42
+ - Fixed `models.json` shell-command auth and headers to resolve at request time instead of being cached into long-lived model state. pi now leaves TTL, caching, and recovery policy to user-provided wrapper commands because arbitrary shell commands need provider-specific strategies ([#1835](https://github.com/badlogic/pi-mono/issues/1835))
43
+ - Fixed Google and Vertex cost calculation to subtract cached prompt tokens from billable input tokens instead of double-counting them when providers report `cachedContentTokenCount` ([#2588](https://github.com/badlogic/pi-mono/pull/2588) by [@sparkleMing](https://github.com/sparkleMing))
44
+ - Added missing `ajv` direct dependency; previously relied on transitive install via `@mariozechner/pi-ai` which broke standalone installs ([#2252](https://github.com/badlogic/pi-mono/issues/2252))
45
+ - Fixed `/export` HTML backgrounds to honor `theme.export.pageBg`, `cardBg`, and `infoBg` instead of always deriving them from `userMessageBg` ([#2565](https://github.com/badlogic/pi-mono/issues/2565))
46
+ - Fixed interactive bash execution collapsed previews to recompute visual line wrapping at render time, so previews respect the current terminal width after resizes and split-pane width changes ([#2569](https://github.com/badlogic/pi-mono/issues/2569))
47
+ - Fixed RPC `get_session_stats` to expose `contextUsage`, so headless clients can read actual current context-window usage instead of deriving it from token totals ([#2550](https://github.com/badlogic/pi-mono/issues/2550))
48
+ - Fixed `pi update` for git packages to fetch only the tracked target branch with `--no-tags`, reducing unrelated branch and tag noise while preserving force-push-safe updates ([#2548](https://github.com/badlogic/pi-mono/issues/2548))
49
+ - Fixed print and JSON modes to emit `session_shutdown` before exit, so extensions can release long-lived resources and non-interactive runs terminate cleanly ([#2576](https://github.com/badlogic/pi-mono/issues/2576))
50
+ - Fixed GitHub Copilot OpenAI Responses requests to omit the `reasoning` field entirely when no reasoning effort is requested, avoiding `400` errors from Copilot `gpt-5-mini` rejecting `reasoning: { effort: "none" }` during internal summary calls ([#2567](https://github.com/badlogic/pi-mono/issues/2567))
51
+ - Fixed blockquote text color breaking after inline links (and other inline elements) due to missing style restoration prefix
52
+ - Fixed slash-command Tab completion from immediately chaining into argument autocomplete after completing the command name, restoring flows like `/model` that submit into a selector dialog ([#2577](https://github.com/badlogic/pi-mono/issues/2577))
53
+ - Fixed stale content and incorrect viewport tracking after TUI content shrinks or transient components inflate the working area ([#2126](https://github.com/badlogic/pi-mono/pull/2126) by [@Perlence](https://github.com/Perlence))
54
+ - Fixed `@` autocomplete to debounce editor-triggered searches, cancel in-flight `fd` lookups cleanly, and keep suggestions visible while results refresh ([#1278](https://github.com/badlogic/pi-mono/issues/1278))
55
+
56
+ ## [0.62.0] - 2026-03-23
57
+
58
+ ### New Features
59
+
60
+ - Built-in tools as extensible ToolDefinitions. Extension authors can now override rendering of built-in read/write/edit/bash/grep/find/ls tools with custom `renderCall`/`renderResult` components. See [docs/extensions.md](docs/extensions.md).
61
+ - Unified source provenance via `sourceInfo`. All resources, commands, tools, skills, and prompt templates now carry structured `sourceInfo` with path, scope, and source metadata. Visible in autocomplete, RPC discovery, and SDK introspection. See [docs/extensions.md](docs/extensions.md).
62
+ - AWS Bedrock cost allocation tagging. New `requestMetadata` option on `BedrockOptions` forwards key-value pairs to the Bedrock Converse API for AWS Cost Explorer split cost allocation.
63
+
64
+ ### Breaking Changes
65
+
66
+ - Changed `ToolDefinition.renderCall` and `renderResult` semantics. Fallback rendering now happens only when a renderer is not defined for that slot. If `renderCall` or `renderResult` is defined, it must return a `Component`.
67
+ - Changed slash command provenance to use `sourceInfo` consistently. RPC `get_commands`, `RpcSlashCommand`, and SDK `SlashCommandInfo` no longer expose `location` or `path`. Use `sourceInfo` instead ([#1734](https://github.com/badlogic/pi-mono/issues/1734))
68
+ - Removed legacy `source` fields from `Skill` and `PromptTemplate`. Use `sourceInfo.source` for provenance instead ([#1734](https://github.com/badlogic/pi-mono/issues/1734))
69
+ - Removed `ResourceLoader.getPathMetadata()`. Resource provenance is now attached directly to loaded resources via `sourceInfo` ([#1734](https://github.com/badlogic/pi-mono/issues/1734))
70
+ - Removed `extensionPath` from `RegisteredCommand` and `RegisteredTool`. Use `sourceInfo.path` for provenance instead ([#1734](https://github.com/badlogic/pi-mono/issues/1734))
71
+
72
+ #### Migration Notes
73
+
74
+ Resource, command, and tool provenance now use `sourceInfo` consistently.
75
+
76
+ Common updates:
77
+ - RPC `get_commands`: replace `path` and `location` with `sourceInfo.path`, `sourceInfo.scope`, and `sourceInfo.source`
78
+ - `SlashCommandInfo`: replace `command.path` and `command.location` with `command.sourceInfo`
79
+ - `Skill` and `PromptTemplate`: replace `.source` with `.sourceInfo.source`
80
+ - `RegisteredCommand` and `RegisteredTool`: replace `.extensionPath` with `.sourceInfo.path`
81
+ - Custom `ResourceLoader` implementations: remove `getPathMetadata()` and read provenance from loaded resources directly
82
+
83
+ Examples:
84
+ - `command.path` -> `command.sourceInfo.path`
85
+ - `command.location === "user"` -> `command.sourceInfo.scope === "user"`
86
+ - `skill.source` -> `skill.sourceInfo.source`
87
+ - `tool.extensionPath` -> `tool.sourceInfo.path`
88
+
89
+ ### Changed
90
+
91
+ - Built-in tools now work like custom tools in extensions. To get built-in tool definitions, import `readToolDefinition` / `createReadToolDefinition()` and the equivalent `bash`, `edit`, `write`, `grep`, `find`, and `ls` exports from `@mariozechner/pi-coding-agent`.
92
+ - Cleaned up `buildSystemPrompt()` so built-in tool snippets and tool-local guidelines come from built-in `ToolDefinition` metadata, while cross-tool and global prompt rules stay in system prompt construction.
93
+ - Added structured `sourceInfo` to `pi.getAllTools()` results for built-in, SDK, and extension tools ([#1734](https://github.com/badlogic/pi-mono/issues/1734))
94
+
95
+ ### Fixed
96
+
97
+ - Fixed extension command name conflicts so extensions with duplicate command names can load together. Conflicting extension commands now get numeric invocation suffixes in load order, for example `/review:1` and `/review:2` ([#1061](https://github.com/badlogic/pi-mono/issues/1061))
98
+ - Fixed slash command source attribution for extension commands, prompt templates, and skills in autocomplete and command discovery ([#1734](https://github.com/badlogic/pi-mono/issues/1734))
99
+ - Fixed auto-resized image handling to enforce the inline image size limit on the final base64 payload, return text-only fallbacks when resizing cannot produce a safe image, and avoid falling back to the original image in `read` and `@file` auto-resize paths ([#2055](https://github.com/badlogic/pi-mono/issues/2055))
100
+ - Fixed `pi update` for git packages to skip destructive reset, clean, and reinstall steps when the fetched target already matches the local checkout ([#2503](https://github.com/badlogic/pi-mono/issues/2503))
101
+ - Fixed print and JSON mode to take over stdout during non-interactive startup, keeping package-manager and other incidental chatter off protocol/output stdout ([#2482](https://github.com/badlogic/pi-mono/issues/2482))
102
+ - Fixed cli-highlight auto-detection for languageless code blocks that misidentified prose as programming languages and colored random English words as keywords
103
+ - Fixed Anthropic thinking disable handling to send `thinking: { type: "disabled" }` for reasoning-capable models when thinking is explicitly off ([#2022](https://github.com/badlogic/pi-mono/issues/2022))
104
+ - Fixed explicit thinking disable handling across Google, Google Vertex, Gemini CLI, OpenAI Responses, Azure OpenAI Responses, and OpenRouter-backed OpenAI-compatible completions ([#2490](https://github.com/badlogic/pi-mono/issues/2490))
105
+ - Fixed OpenAI Responses replay for foreign tool-call item IDs by hashing foreign IDs into bounded `fc_<hash>` IDs
106
+ - Fixed OpenAI-compatible completions streams to ignore null chunks instead of crashing ([#2466](https://github.com/badlogic/pi-mono/pull/2466) by [@Cheng-Zi-Qing](https://github.com/Cheng-Zi-Qing))
107
+ - Fixed `truncateToWidth()` performance for very large strings by streaming truncation ([#2447](https://github.com/badlogic/pi-mono/issues/2447))
108
+ - Fixed markdown heading styling being lost after inline code spans within headings
109
+
3
110
  ## [0.61.1] - 2026-03-20
4
111
 
5
112
  ### New Features
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  <!-- OSS_WEEKEND_START -->
2
2
  # 🏖️ OSS Weekend
3
3
 
4
- **Issue tracker reopens Monday, March 23, 2026.**
4
+ **Issue tracker reopens Monday, March 30, 2026.**
5
5
 
6
- OSS weekend runs Friday, March 20, 2026 through Monday, March 23, 2026. New issues are auto-closed during this time. For support, join [Discord](https://discord.com/invite/3cU7Bz4UPx).
6
+ OSS weekend runs Sunday, March 22, 2026 through Monday, March 30, 2026. New issues are auto-closed during this time. For support, join [Discord](https://discord.com/invite/3cU7Bz4UPx).
7
7
  <!-- OSS_WEEKEND_END -->
8
8
 
9
9
  ---
@@ -1 +1 @@
1
- {"version":3,"file":"file-processor.d.ts","sourceRoot":"","sources":["../../src/cli/file-processor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAOxD,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IAClC,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,sEAAsE;AACtE,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,cAAc,CAAC,CAwEpH","sourcesContent":["/**\n * Process @file CLI arguments into text content and image attachments\n */\n\nimport { access, readFile, stat } from \"node:fs/promises\";\nimport type { ImageContent } from \"@mariozechner/pi-ai\";\nimport chalk from \"chalk\";\nimport { resolve } from \"path\";\nimport { resolveReadPath } from \"../core/tools/path-utils.js\";\nimport { formatDimensionNote, resizeImage } from \"../utils/image-resize.js\";\nimport { detectSupportedImageMimeTypeFromFile } from \"../utils/mime.js\";\n\nexport interface ProcessedFiles {\n\ttext: string;\n\timages: ImageContent[];\n}\n\nexport interface ProcessFileOptions {\n\t/** Whether to auto-resize images to 2000x2000 max. Default: true */\n\tautoResizeImages?: boolean;\n}\n\n/** Process @file arguments into text content and image attachments */\nexport async function processFileArguments(fileArgs: string[], options?: ProcessFileOptions): Promise<ProcessedFiles> {\n\tconst autoResizeImages = options?.autoResizeImages ?? true;\n\tlet text = \"\";\n\tconst images: ImageContent[] = [];\n\n\tfor (const fileArg of fileArgs) {\n\t\t// Expand and resolve path (handles ~ expansion and macOS screenshot Unicode spaces)\n\t\tconst absolutePath = resolve(resolveReadPath(fileArg, process.cwd()));\n\n\t\t// Check if file exists\n\t\ttry {\n\t\t\tawait access(absolutePath);\n\t\t} catch {\n\t\t\tconsole.error(chalk.red(`Error: File not found: ${absolutePath}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Check if file is empty\n\t\tconst stats = await stat(absolutePath);\n\t\tif (stats.size === 0) {\n\t\t\t// Skip empty files\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst mimeType = await detectSupportedImageMimeTypeFromFile(absolutePath);\n\n\t\tif (mimeType) {\n\t\t\t// Handle image file\n\t\t\tconst content = await readFile(absolutePath);\n\t\t\tconst base64Content = content.toString(\"base64\");\n\n\t\t\tlet attachment: ImageContent;\n\t\t\tlet dimensionNote: string | undefined;\n\n\t\t\tif (autoResizeImages) {\n\t\t\t\tconst resized = await resizeImage({ type: \"image\", data: base64Content, mimeType });\n\t\t\t\tdimensionNote = formatDimensionNote(resized);\n\t\t\t\tattachment = {\n\t\t\t\t\ttype: \"image\",\n\t\t\t\t\tmimeType: resized.mimeType,\n\t\t\t\t\tdata: resized.data,\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tattachment = {\n\t\t\t\t\ttype: \"image\",\n\t\t\t\t\tmimeType,\n\t\t\t\t\tdata: base64Content,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\timages.push(attachment);\n\n\t\t\t// Add text reference to image with optional dimension note\n\t\t\tif (dimensionNote) {\n\t\t\t\ttext += `<file name=\"${absolutePath}\">${dimensionNote}</file>\\n`;\n\t\t\t} else {\n\t\t\t\ttext += `<file name=\"${absolutePath}\"></file>\\n`;\n\t\t\t}\n\t\t} else {\n\t\t\t// Handle text file\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(absolutePath, \"utf-8\");\n\t\t\t\ttext += `<file name=\"${absolutePath}\">\\n${content}\\n</file>\\n`;\n\t\t\t} catch (error: unknown) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tconsole.error(chalk.red(`Error: Could not read file ${absolutePath}: ${message}`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { text, images };\n}\n"]}
1
+ {"version":3,"file":"file-processor.d.ts","sourceRoot":"","sources":["../../src/cli/file-processor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAOxD,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IAClC,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,sEAAsE;AACtE,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,cAAc,CAAC,CA4EpH","sourcesContent":["/**\n * Process @file CLI arguments into text content and image attachments\n */\n\nimport { access, readFile, stat } from \"node:fs/promises\";\nimport type { ImageContent } from \"@mariozechner/pi-ai\";\nimport chalk from \"chalk\";\nimport { resolve } from \"path\";\nimport { resolveReadPath } from \"../core/tools/path-utils.js\";\nimport { formatDimensionNote, resizeImage } from \"../utils/image-resize.js\";\nimport { detectSupportedImageMimeTypeFromFile } from \"../utils/mime.js\";\n\nexport interface ProcessedFiles {\n\ttext: string;\n\timages: ImageContent[];\n}\n\nexport interface ProcessFileOptions {\n\t/** Whether to auto-resize images to 2000x2000 max. Default: true */\n\tautoResizeImages?: boolean;\n}\n\n/** Process @file arguments into text content and image attachments */\nexport async function processFileArguments(fileArgs: string[], options?: ProcessFileOptions): Promise<ProcessedFiles> {\n\tconst autoResizeImages = options?.autoResizeImages ?? true;\n\tlet text = \"\";\n\tconst images: ImageContent[] = [];\n\n\tfor (const fileArg of fileArgs) {\n\t\t// Expand and resolve path (handles ~ expansion and macOS screenshot Unicode spaces)\n\t\tconst absolutePath = resolve(resolveReadPath(fileArg, process.cwd()));\n\n\t\t// Check if file exists\n\t\ttry {\n\t\t\tawait access(absolutePath);\n\t\t} catch {\n\t\t\tconsole.error(chalk.red(`Error: File not found: ${absolutePath}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Check if file is empty\n\t\tconst stats = await stat(absolutePath);\n\t\tif (stats.size === 0) {\n\t\t\t// Skip empty files\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst mimeType = await detectSupportedImageMimeTypeFromFile(absolutePath);\n\n\t\tif (mimeType) {\n\t\t\t// Handle image file\n\t\t\tconst content = await readFile(absolutePath);\n\t\t\tconst base64Content = content.toString(\"base64\");\n\n\t\t\tlet attachment: ImageContent;\n\t\t\tlet dimensionNote: string | undefined;\n\n\t\t\tif (autoResizeImages) {\n\t\t\t\tconst resized = await resizeImage({ type: \"image\", data: base64Content, mimeType });\n\t\t\t\tif (!resized) {\n\t\t\t\t\ttext += `<file name=\"${absolutePath}\">[Image omitted: could not be resized below the inline image size limit.]</file>\\n`;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tdimensionNote = formatDimensionNote(resized);\n\t\t\t\tattachment = {\n\t\t\t\t\ttype: \"image\",\n\t\t\t\t\tmimeType: resized.mimeType,\n\t\t\t\t\tdata: resized.data,\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tattachment = {\n\t\t\t\t\ttype: \"image\",\n\t\t\t\t\tmimeType,\n\t\t\t\t\tdata: base64Content,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\timages.push(attachment);\n\n\t\t\t// Add text reference to image with optional dimension note\n\t\t\tif (dimensionNote) {\n\t\t\t\ttext += `<file name=\"${absolutePath}\">${dimensionNote}</file>\\n`;\n\t\t\t} else {\n\t\t\t\ttext += `<file name=\"${absolutePath}\"></file>\\n`;\n\t\t\t}\n\t\t} else {\n\t\t\t// Handle text file\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(absolutePath, \"utf-8\");\n\t\t\t\ttext += `<file name=\"${absolutePath}\">\\n${content}\\n</file>\\n`;\n\t\t\t} catch (error: unknown) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tconsole.error(chalk.red(`Error: Could not read file ${absolutePath}: ${message}`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { text, images };\n}\n"]}
@@ -38,6 +38,10 @@ export async function processFileArguments(fileArgs, options) {
38
38
  let dimensionNote;
39
39
  if (autoResizeImages) {
40
40
  const resized = await resizeImage({ type: "image", data: base64Content, mimeType });
41
+ if (!resized) {
42
+ text += `<file name="${absolutePath}">[Image omitted: could not be resized below the inline image size limit.]</file>\n`;
43
+ continue;
44
+ }
41
45
  dimensionNote = formatDimensionNote(resized);
42
46
  attachment = {
43
47
  type: "image",
@@ -1 +1 @@
1
- {"version":3,"file":"file-processor.js","sourceRoot":"","sources":["../../src/cli/file-processor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Process @file CLI arguments into text content and image attachments\n */\n\nimport { access, readFile, stat } from \"node:fs/promises\";\nimport type { ImageContent } from \"@mariozechner/pi-ai\";\nimport chalk from \"chalk\";\nimport { resolve } from \"path\";\nimport { resolveReadPath } from \"../core/tools/path-utils.js\";\nimport { formatDimensionNote, resizeImage } from \"../utils/image-resize.js\";\nimport { detectSupportedImageMimeTypeFromFile } from \"../utils/mime.js\";\n\nexport interface ProcessedFiles {\n\ttext: string;\n\timages: ImageContent[];\n}\n\nexport interface ProcessFileOptions {\n\t/** Whether to auto-resize images to 2000x2000 max. Default: true */\n\tautoResizeImages?: boolean;\n}\n\n/** Process @file arguments into text content and image attachments */\nexport async function processFileArguments(fileArgs: string[], options?: ProcessFileOptions): Promise<ProcessedFiles> {\n\tconst autoResizeImages = options?.autoResizeImages ?? true;\n\tlet text = \"\";\n\tconst images: ImageContent[] = [];\n\n\tfor (const fileArg of fileArgs) {\n\t\t// Expand and resolve path (handles ~ expansion and macOS screenshot Unicode spaces)\n\t\tconst absolutePath = resolve(resolveReadPath(fileArg, process.cwd()));\n\n\t\t// Check if file exists\n\t\ttry {\n\t\t\tawait access(absolutePath);\n\t\t} catch {\n\t\t\tconsole.error(chalk.red(`Error: File not found: ${absolutePath}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Check if file is empty\n\t\tconst stats = await stat(absolutePath);\n\t\tif (stats.size === 0) {\n\t\t\t// Skip empty files\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst mimeType = await detectSupportedImageMimeTypeFromFile(absolutePath);\n\n\t\tif (mimeType) {\n\t\t\t// Handle image file\n\t\t\tconst content = await readFile(absolutePath);\n\t\t\tconst base64Content = content.toString(\"base64\");\n\n\t\t\tlet attachment: ImageContent;\n\t\t\tlet dimensionNote: string | undefined;\n\n\t\t\tif (autoResizeImages) {\n\t\t\t\tconst resized = await resizeImage({ type: \"image\", data: base64Content, mimeType });\n\t\t\t\tdimensionNote = formatDimensionNote(resized);\n\t\t\t\tattachment = {\n\t\t\t\t\ttype: \"image\",\n\t\t\t\t\tmimeType: resized.mimeType,\n\t\t\t\t\tdata: resized.data,\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tattachment = {\n\t\t\t\t\ttype: \"image\",\n\t\t\t\t\tmimeType,\n\t\t\t\t\tdata: base64Content,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\timages.push(attachment);\n\n\t\t\t// Add text reference to image with optional dimension note\n\t\t\tif (dimensionNote) {\n\t\t\t\ttext += `<file name=\"${absolutePath}\">${dimensionNote}</file>\\n`;\n\t\t\t} else {\n\t\t\t\ttext += `<file name=\"${absolutePath}\"></file>\\n`;\n\t\t\t}\n\t\t} else {\n\t\t\t// Handle text file\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(absolutePath, \"utf-8\");\n\t\t\t\ttext += `<file name=\"${absolutePath}\">\\n${content}\\n</file>\\n`;\n\t\t\t} catch (error: unknown) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tconsole.error(chalk.red(`Error: Could not read file ${absolutePath}: ${message}`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { text, images };\n}\n"]}
1
+ {"version":3,"file":"file-processor.js","sourceRoot":"","sources":["../../src/cli/file-processor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Process @file CLI arguments into text content and image attachments\n */\n\nimport { access, readFile, stat } from \"node:fs/promises\";\nimport type { ImageContent } from \"@mariozechner/pi-ai\";\nimport chalk from \"chalk\";\nimport { resolve } from \"path\";\nimport { resolveReadPath } from \"../core/tools/path-utils.js\";\nimport { formatDimensionNote, resizeImage } from \"../utils/image-resize.js\";\nimport { detectSupportedImageMimeTypeFromFile } from \"../utils/mime.js\";\n\nexport interface ProcessedFiles {\n\ttext: string;\n\timages: ImageContent[];\n}\n\nexport interface ProcessFileOptions {\n\t/** Whether to auto-resize images to 2000x2000 max. Default: true */\n\tautoResizeImages?: boolean;\n}\n\n/** Process @file arguments into text content and image attachments */\nexport async function processFileArguments(fileArgs: string[], options?: ProcessFileOptions): Promise<ProcessedFiles> {\n\tconst autoResizeImages = options?.autoResizeImages ?? true;\n\tlet text = \"\";\n\tconst images: ImageContent[] = [];\n\n\tfor (const fileArg of fileArgs) {\n\t\t// Expand and resolve path (handles ~ expansion and macOS screenshot Unicode spaces)\n\t\tconst absolutePath = resolve(resolveReadPath(fileArg, process.cwd()));\n\n\t\t// Check if file exists\n\t\ttry {\n\t\t\tawait access(absolutePath);\n\t\t} catch {\n\t\t\tconsole.error(chalk.red(`Error: File not found: ${absolutePath}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Check if file is empty\n\t\tconst stats = await stat(absolutePath);\n\t\tif (stats.size === 0) {\n\t\t\t// Skip empty files\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst mimeType = await detectSupportedImageMimeTypeFromFile(absolutePath);\n\n\t\tif (mimeType) {\n\t\t\t// Handle image file\n\t\t\tconst content = await readFile(absolutePath);\n\t\t\tconst base64Content = content.toString(\"base64\");\n\n\t\t\tlet attachment: ImageContent;\n\t\t\tlet dimensionNote: string | undefined;\n\n\t\t\tif (autoResizeImages) {\n\t\t\t\tconst resized = await resizeImage({ type: \"image\", data: base64Content, mimeType });\n\t\t\t\tif (!resized) {\n\t\t\t\t\ttext += `<file name=\"${absolutePath}\">[Image omitted: could not be resized below the inline image size limit.]</file>\\n`;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tdimensionNote = formatDimensionNote(resized);\n\t\t\t\tattachment = {\n\t\t\t\t\ttype: \"image\",\n\t\t\t\t\tmimeType: resized.mimeType,\n\t\t\t\t\tdata: resized.data,\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tattachment = {\n\t\t\t\t\ttype: \"image\",\n\t\t\t\t\tmimeType,\n\t\t\t\t\tdata: base64Content,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\timages.push(attachment);\n\n\t\t\t// Add text reference to image with optional dimension note\n\t\t\tif (dimensionNote) {\n\t\t\t\ttext += `<file name=\"${absolutePath}\">${dimensionNote}</file>\\n`;\n\t\t\t} else {\n\t\t\t\ttext += `<file name=\"${absolutePath}\"></file>\\n`;\n\t\t\t}\n\t\t} else {\n\t\t\t// Handle text file\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(absolutePath, \"utf-8\");\n\t\t\t\ttext += `<file name=\"${absolutePath}\">\\n${content}\\n</file>\\n`;\n\t\t\t} catch (error: unknown) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tconsole.error(chalk.red(`Error: Could not read file ${absolutePath}: ${message}`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { text, images };\n}\n"]}
@@ -78,7 +78,12 @@ export interface AgentSessionConfig {
78
78
  modelRegistry: ModelRegistry;
79
79
  /** Initial active built-in tool names. Default: [read, bash, edit, write] */
80
80
  initialActiveToolNames?: string[];
81
- /** Override base tools (useful for custom runtimes). */
81
+ /**
82
+ * Override base tools (useful for custom runtimes).
83
+ *
84
+ * These are synthesized into minimal ToolDefinitions internally so AgentSession can keep
85
+ * a definition-first registry even when callers provide plain AgentTool instances.
86
+ */
82
87
  baseToolsOverride?: Record<string, AgentTool>;
83
88
  /** Mutable ref used by Agent to access the current ExtensionRunner */
84
89
  extensionRunnerRef?: {
@@ -126,6 +131,7 @@ export interface SessionStats {
126
131
  total: number;
127
132
  };
128
133
  cost: number;
134
+ contextUsage?: ContextUsage;
129
135
  }
130
136
  export declare class AgentSession {
131
137
  readonly agent: Agent;
@@ -155,7 +161,7 @@ export declare class AgentSession {
155
161
  private _turnIndex;
156
162
  private _resourceLoader;
157
163
  private _customTools;
158
- private _baseToolRegistry;
164
+ private _baseToolDefinitions;
159
165
  private _cwd;
160
166
  private _extensionRunnerRef?;
161
167
  private _initialActiveToolNames?;
@@ -167,12 +173,14 @@ export declare class AgentSession {
167
173
  private _extensionErrorUnsubscriber?;
168
174
  private _modelRegistry;
169
175
  private _toolRegistry;
176
+ private _toolDefinitions;
170
177
  private _toolPromptSnippets;
171
178
  private _toolPromptGuidelines;
172
179
  private _baseSystemPrompt;
173
180
  constructor(config: AgentSessionConfig);
174
181
  /** Model registry for API key resolution and model discovery */
175
182
  get modelRegistry(): ModelRegistry;
183
+ private _getRequiredRequestAuth;
176
184
  /**
177
185
  * Install tool hooks once on the Agent instance.
178
186
  *
@@ -237,9 +245,10 @@ export declare class AgentSession {
237
245
  */
238
246
  getActiveToolNames(): string[];
239
247
  /**
240
- * Get all configured tools with name, description, and parameter schema.
248
+ * Get all configured tools with name, description, parameter schema, and source metadata.
241
249
  */
242
250
  getAllTools(): ToolInfo[];
251
+ getToolDefinition(name: string): ToolDefinition | undefined;
243
252
  /**
244
253
  * Set active tools by name.
245
254
  * Only tools in the registry can be enabled. Unknown tool names are ignored.
@@ -377,8 +386,8 @@ export declare class AgentSession {
377
386
  private _emitModelSelect;
378
387
  /**
379
388
  * Set model directly.
380
- * Validates API key, saves to session and settings.
381
- * @throws Error if no API key available for the model
389
+ * Validates that auth is configured, saves to session and settings.
390
+ * @throws Error if no auth is configured for the model
382
391
  */
383
392
  setModel(model: Model<any>): Promise<void>;
384
393
  /**
@@ -388,7 +397,7 @@ export declare class AgentSession {
388
397
  * @returns The new model info, or undefined if only one model available
389
398
  */
390
399
  cycleModel(direction?: "forward" | "backward"): Promise<ModelCycleResult | undefined>;
391
- private _getScopedModelsWithApiKey;
400
+ private _getScopedModelsWithAuth;
392
401
  private _cycleScopedModel;
393
402
  private _cycleAvailableModel;
394
403
  /**