@oh-my-pi/pi-coding-agent 15.3.2 → 15.4.2

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 (193) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/dist/types/cli/file-processor.d.ts +1 -1
  3. package/dist/types/config/settings-schema.d.ts +45 -3
  4. package/dist/types/config/settings.d.ts +1 -1
  5. package/dist/types/debug/raw-sse.d.ts +2 -0
  6. package/dist/types/edit/file-read-cache.d.ts +15 -4
  7. package/dist/types/edit/index.d.ts +3 -8
  8. package/dist/types/edit/renderer.d.ts +1 -2
  9. package/dist/types/eval/__tests__/shared-executors.test.d.ts +1 -0
  10. package/dist/types/eval/js/shared/local-module-loader.d.ts +16 -0
  11. package/dist/types/eval/js/shared/rewrite-imports.d.ts +4 -0
  12. package/dist/types/eval/js/shared/runtime.d.ts +14 -8
  13. package/dist/types/eval/py/executor.d.ts +1 -2
  14. package/dist/types/eval/py/kernel.d.ts +6 -0
  15. package/dist/types/eval/py/tool-bridge.d.ts +1 -5
  16. package/dist/types/eval/session-id.d.ts +3 -0
  17. package/dist/types/extensibility/extensions/types.d.ts +1 -3
  18. package/dist/types/hashline/anchors.d.ts +15 -9
  19. package/dist/types/hashline/constants.d.ts +0 -2
  20. package/dist/types/hashline/diff.d.ts +1 -2
  21. package/dist/types/hashline/executor.d.ts +52 -0
  22. package/dist/types/hashline/hash.d.ts +44 -93
  23. package/dist/types/hashline/index.d.ts +2 -1
  24. package/dist/types/hashline/input.d.ts +2 -9
  25. package/dist/types/hashline/recovery.d.ts +3 -9
  26. package/dist/types/hashline/tokenizer.d.ts +91 -0
  27. package/dist/types/hashline/types.d.ts +5 -7
  28. package/dist/types/modes/components/extensions/types.d.ts +0 -4
  29. package/dist/types/modes/types.d.ts +1 -0
  30. package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
  31. package/dist/types/sdk.d.ts +2 -0
  32. package/dist/types/session/agent-session.d.ts +11 -15
  33. package/dist/types/session/agent-storage.d.ts +11 -10
  34. package/dist/types/slash-commands/acp-builtins.d.ts +3 -3
  35. package/dist/types/slash-commands/types.d.ts +0 -5
  36. package/dist/types/task/executor.d.ts +2 -0
  37. package/dist/types/tool-discovery/tool-index.d.ts +0 -50
  38. package/dist/types/tools/index.d.ts +2 -8
  39. package/dist/types/tools/match-line-format.d.ts +4 -4
  40. package/dist/types/tools/output-schema-validator.d.ts +64 -0
  41. package/dist/types/tools/review.d.ts +13 -0
  42. package/dist/types/tools/search-tool-bm25.d.ts +1 -1
  43. package/dist/types/tools/search.d.ts +4 -3
  44. package/dist/types/utils/edit-mode.d.ts +1 -1
  45. package/dist/types/web/kagi.d.ts +4 -2
  46. package/dist/types/web/parallel.d.ts +4 -3
  47. package/dist/types/web/scrapers/types.d.ts +2 -1
  48. package/dist/types/web/search/index.d.ts +12 -4
  49. package/dist/types/web/search/provider.d.ts +2 -1
  50. package/dist/types/web/search/providers/anthropic.d.ts +9 -4
  51. package/dist/types/web/search/providers/base.d.ts +34 -2
  52. package/dist/types/web/search/providers/brave.d.ts +8 -1
  53. package/dist/types/web/search/providers/codex.d.ts +13 -9
  54. package/dist/types/web/search/providers/exa.d.ts +10 -1
  55. package/dist/types/web/search/providers/gemini.d.ts +20 -23
  56. package/dist/types/web/search/providers/jina.d.ts +2 -1
  57. package/dist/types/web/search/providers/kagi.d.ts +4 -1
  58. package/dist/types/web/search/providers/kimi.d.ts +10 -1
  59. package/dist/types/web/search/providers/parallel.d.ts +3 -2
  60. package/dist/types/web/search/providers/perplexity.d.ts +5 -2
  61. package/dist/types/web/search/providers/searxng.d.ts +2 -1
  62. package/dist/types/web/search/providers/synthetic.d.ts +5 -8
  63. package/dist/types/web/search/providers/tavily.d.ts +11 -4
  64. package/dist/types/web/search/providers/utils.d.ts +8 -6
  65. package/dist/types/web/search/providers/zai.d.ts +12 -3
  66. package/package.json +7 -7
  67. package/src/cli/file-processor.ts +12 -2
  68. package/src/cli.ts +0 -8
  69. package/src/commands/commit.ts +8 -8
  70. package/src/config/prompt-templates.ts +6 -6
  71. package/src/config/settings-schema.ts +47 -3
  72. package/src/config/settings.ts +5 -5
  73. package/src/debug/raw-sse.ts +68 -3
  74. package/src/edit/file-read-cache.ts +68 -25
  75. package/src/edit/index.ts +6 -37
  76. package/src/edit/renderer.ts +9 -47
  77. package/src/edit/streaming.ts +43 -56
  78. package/src/eval/__tests__/shared-executors.test.ts +520 -0
  79. package/src/eval/js/context-manager.ts +64 -53
  80. package/src/eval/js/shared/local-module-loader.ts +265 -0
  81. package/src/eval/js/shared/prelude.txt +4 -0
  82. package/src/eval/js/shared/rewrite-imports.ts +85 -0
  83. package/src/eval/js/shared/runtime.ts +129 -86
  84. package/src/eval/js/worker-core.ts +23 -38
  85. package/src/eval/py/executor.ts +155 -84
  86. package/src/eval/py/kernel.ts +10 -1
  87. package/src/eval/py/prelude.py +22 -24
  88. package/src/eval/py/runner.py +203 -85
  89. package/src/eval/py/tool-bridge.ts +17 -10
  90. package/src/eval/session-id.ts +8 -0
  91. package/src/exec/bash-executor.ts +27 -16
  92. package/src/extensibility/extensions/runner.ts +0 -1
  93. package/src/extensibility/extensions/types.ts +1 -3
  94. package/src/hashline/anchors.ts +56 -65
  95. package/src/hashline/apply.ts +29 -31
  96. package/src/hashline/constants.ts +0 -3
  97. package/src/hashline/diff-preview.ts +4 -5
  98. package/src/hashline/diff.ts +30 -4
  99. package/src/hashline/execute.ts +91 -26
  100. package/src/hashline/executor.ts +239 -0
  101. package/src/hashline/grammar.lark +12 -10
  102. package/src/hashline/hash.ts +69 -114
  103. package/src/hashline/index.ts +2 -1
  104. package/src/hashline/input.ts +48 -41
  105. package/src/hashline/prefixes.ts +21 -11
  106. package/src/hashline/recovery.ts +63 -71
  107. package/src/hashline/stream.ts +2 -2
  108. package/src/hashline/tokenizer.ts +467 -0
  109. package/src/hashline/types.ts +6 -8
  110. package/src/internal-urls/docs-index.generated.ts +7 -7
  111. package/src/modes/components/extensions/types.ts +0 -5
  112. package/src/modes/components/session-observer-overlay.ts +11 -2
  113. package/src/modes/components/settings-selector.ts +10 -1
  114. package/src/modes/components/tree-selector.ts +10 -2
  115. package/src/modes/controllers/command-controller.ts +1 -3
  116. package/src/modes/controllers/extension-ui-controller.ts +10 -11
  117. package/src/modes/controllers/selector-controller.ts +5 -5
  118. package/src/modes/theme/theme.ts +4 -2
  119. package/src/modes/types.ts +4 -1
  120. package/src/modes/utils/ui-helpers.ts +4 -0
  121. package/src/prompts/agents/explore.md +1 -1
  122. package/src/prompts/tools/ast-edit.md +1 -1
  123. package/src/prompts/tools/ast-grep.md +1 -1
  124. package/src/prompts/tools/eval.md +1 -1
  125. package/src/prompts/tools/hashline.md +73 -94
  126. package/src/prompts/tools/read.md +4 -4
  127. package/src/prompts/tools/search.md +3 -3
  128. package/src/sdk.ts +33 -26
  129. package/src/session/agent-session.ts +59 -66
  130. package/src/session/agent-storage.ts +13 -14
  131. package/src/slash-commands/acp-builtins.ts +3 -3
  132. package/src/slash-commands/types.ts +0 -6
  133. package/src/task/executor.ts +26 -57
  134. package/src/task/index.ts +8 -4
  135. package/src/tool-discovery/tool-index.ts +0 -134
  136. package/src/tools/ast-edit.ts +36 -13
  137. package/src/tools/ast-grep.ts +45 -4
  138. package/src/tools/browser/tab-worker.ts +3 -2
  139. package/src/tools/eval.ts +2 -1
  140. package/src/tools/fetch.ts +23 -14
  141. package/src/tools/index.ts +2 -8
  142. package/src/tools/irc.ts +59 -5
  143. package/src/tools/match-line-format.ts +5 -7
  144. package/src/tools/output-schema-validator.ts +132 -0
  145. package/src/tools/read.ts +142 -31
  146. package/src/tools/review.ts +23 -0
  147. package/src/tools/search-tool-bm25.ts +3 -30
  148. package/src/tools/search.ts +48 -16
  149. package/src/tools/write.ts +3 -3
  150. package/src/tools/yield.ts +32 -41
  151. package/src/utils/edit-mode.ts +1 -2
  152. package/src/utils/file-mentions.ts +2 -2
  153. package/src/web/kagi.ts +15 -6
  154. package/src/web/parallel.ts +9 -6
  155. package/src/web/scrapers/types.ts +7 -1
  156. package/src/web/scrapers/youtube.ts +13 -7
  157. package/src/web/search/index.ts +37 -11
  158. package/src/web/search/provider.ts +5 -3
  159. package/src/web/search/providers/anthropic.ts +30 -21
  160. package/src/web/search/providers/base.ts +35 -2
  161. package/src/web/search/providers/brave.ts +4 -4
  162. package/src/web/search/providers/codex.ts +118 -89
  163. package/src/web/search/providers/exa.ts +3 -2
  164. package/src/web/search/providers/gemini.ts +58 -155
  165. package/src/web/search/providers/jina.ts +4 -4
  166. package/src/web/search/providers/kagi.ts +17 -11
  167. package/src/web/search/providers/kimi.ts +29 -13
  168. package/src/web/search/providers/parallel.ts +171 -23
  169. package/src/web/search/providers/perplexity.ts +38 -37
  170. package/src/web/search/providers/searxng.ts +3 -1
  171. package/src/web/search/providers/synthetic.ts +16 -19
  172. package/src/web/search/providers/tavily.ts +23 -18
  173. package/src/web/search/providers/utils.ts +11 -17
  174. package/src/web/search/providers/zai.ts +16 -8
  175. package/dist/types/hashline/parser.d.ts +0 -7
  176. package/dist/types/mcp/discoverable-tool-metadata.d.ts +0 -7
  177. package/dist/types/tools/vim.d.ts +0 -58
  178. package/dist/types/vim/buffer.d.ts +0 -41
  179. package/dist/types/vim/commands.d.ts +0 -6
  180. package/dist/types/vim/engine.d.ts +0 -47
  181. package/dist/types/vim/parser.d.ts +0 -3
  182. package/dist/types/vim/render.d.ts +0 -25
  183. package/dist/types/vim/types.d.ts +0 -182
  184. package/src/hashline/parser.ts +0 -246
  185. package/src/mcp/discoverable-tool-metadata.ts +0 -24
  186. package/src/prompts/tools/vim.md +0 -98
  187. package/src/tools/vim.ts +0 -949
  188. package/src/vim/buffer.ts +0 -309
  189. package/src/vim/commands.ts +0 -382
  190. package/src/vim/engine.ts +0 -2409
  191. package/src/vim/parser.ts +0 -134
  192. package/src/vim/render.ts +0 -252
  193. package/src/vim/types.ts +0 -197
package/CHANGELOG.md CHANGED
@@ -2,6 +2,114 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [15.4.2] - 2026-05-26
6
+
7
+ ### Fixed
8
+
9
+ - Fixed plan-mode subagents being unable to terminate because `yield` was registered but missing from the active tool set when `requireYieldTool` was combined with an explicit `toolNames` list ([#1408](https://github.com/can1357/oh-my-pi/issues/1408))
10
+
11
+ ## [15.4.1] - 2026-05-26
12
+
13
+ ### Breaking Changes
14
+
15
+ - The `vim` edit mode option is no longer available; configurations using `edit.mode: vim` will be automatically mapped to `hashline` mode
16
+ - Hashline payload semantics are now strictly inline-first: the first payload line is whatever follows the sigil on the op line itself, and subsequent lines append after it. A newline immediately after `↑`/`↓`/`:` is no longer a free separator — it produces a blank first payload line. Use `LINE↓content` for a one-line insert, `LINE↓firstline\nsecondline` for two lines; bare `LINE↓` / `LINE↑` / `LINE:` (no inline payload) still insert/replace with one blank line as before.
17
+
18
+ ### Added
19
+
20
+ - Added `irc.timeoutMs` setting to configure IRC message timeout duration with a default of 120 seconds
21
+ - Added timeout enforcement for IRC send operations to prevent indefinite hangs when recipients are unresponsive
22
+ - Added evaluator state inheritance for `task`-spawned subagents so JavaScript and Python variables are visible between a parent agent and its child sessions
23
+ - Added `hashline-per` edit mode to restore the legacy per-line hashline dialect alongside the default file-hash dialect
24
+ - Added file-hash computation and validation for hashline sections to detect stale edits
25
+ - Added file-read snapshot caching with multi-snapshot ring per path for recovery from agent's own writes
26
+ - Added delete operation (`!`) support to hashline grammar for explicit line deletion
27
+ - Added structural bracket/brace balance warnings when deleting lines with unclosed constructs
28
+ - Added file-hash computation and validation for hashline sections to detect stale edits
29
+ - Added file-read snapshot caching with multi-snapshot ring per path for recovery from agent's own writes
30
+ - Added delete operation (`!`) support to hashline grammar for explicit line deletion
31
+ - Added structural bracket/brace balance warnings when deleting lines with unclosed constructs
32
+
33
+ ### Changed
34
+
35
+ - Changed Python shared eval sessions to be keyed by `sessionId` and `cwd` so code state no longer leaks across different directories when reusing a session
36
+ - Changed shared JavaScript and Python startup to deduplicate concurrent first-time session initialization so parallel first calls share one warm session
37
+ - Changed shared JavaScript and Python execution output handling so interleaved async runs keep their `display` output scoped to the originating run
38
+ - Changed Python tool bridge to use per-run identifiers alongside session IDs for correct routing of tool responses and output in concurrent evaluations
39
+ - Changed JavaScript and Python `eval` execution to allow overlapping asynchronous cells on the same session ID to run concurrently instead of being strictly queued
40
+ - Updated the edit mode option set to support `replace`, `patch`, `hashline`, and `apply_patch` variants
41
+ - Bare `A:` / `A-B:` (no payload, no inline body) now replaces the line/range with a single blank line, symmetric with bare `A↑` / `A↓` inserting a blank line; previously rejected as ambiguous
42
+ - Simplified hashline anchor format from `LINE+HASH` to bare `LINE` numbers in edit operations
43
+ - Updated hashline file headers to include 4-hex file hash: `¶PATH#HASH` format for anchored edits
44
+ - Changed hashline line separator from `|` to `:` in editable output (e.g., `42:content` instead of `42ab|content`)
45
+ - Removed per-line hash validation; file-level hash now validates entire section integrity
46
+ - Updated read/search output to emit file-hash headers (`¶PATH#HASH`) followed by numbered lines for hashline mode
47
+ - Modified hashline grammar to accept optional file hash in headers and removed hash requirements from line anchors
48
+ - Changed hashline diff preview format to use `LINE:content` instead of `LINE+HASH|content`
49
+ - Updated prompt documentation to reflect new `¶PATH#HASH` header and bare line-number syntax
50
+ - Bare `A:` / `A-B:` (no payload, no inline body) now replaces the line/range with a single blank line, symmetric with bare `A↑` / `A↓` inserting a blank line; previously rejected as ambiguous
51
+ - Simplified hashline anchor format from `LINE+HASH` to bare `LINE` numbers in edit operations
52
+ - Updated hashline file headers to include 4-hex file hash: `¶PATH#HASH` format for anchored edits
53
+ - Changed hashline line separator from `|` to `:` in editable output (e.g., `42:content` instead of `42ab|content`)
54
+ - Removed per-line hash validation; file-level hash now validates entire section integrity
55
+ - Updated read/search output to emit file-hash headers (`¶PATH#HASH`) followed by numbered lines for hashline mode
56
+ - Modified hashline grammar to accept optional file hash in headers and removed hash requirements from line anchors
57
+ - Changed hashline diff preview format to use `LINE:content` instead of `LINE+HASH|content`
58
+ - Updated prompt documentation to reflect new `¶PATH#HASH` header and bare line-number syntax
59
+
60
+ ### Removed
61
+
62
+ - Removed the `installH2Fetch()` activation from CLI startup; HTTPS fetches now use Bun's default transport
63
+ - Removed the `vim` edit mode along with the `VimTool` module, prompt, and supporting buffer/engine/renderer stack
64
+ - Removed per-line hash anchors (2-letter bigram hashes) from hashline format
65
+ - Removed `RANGE_INTERIOR_HASH` constant; multi-line ranges no longer use `**` filler
66
+ - Removed `HashMismatch` type and hash mismatch error reporting; replaced with file-level validation
67
+ - Removed per-line hash anchors (2-letter bigram hashes) from hashline format
68
+ - Removed `RANGE_INTERIOR_HASH` constant; multi-line ranges no longer use `**` filler
69
+ - Removed `HashMismatch` type and hash mismatch error reporting; replaced with file-level validation
70
+
71
+ ### Fixed
72
+
73
+ - Fixed JavaScript module reloading to refresh local re-exports when transitive dependency files are edited
74
+ - Fixed Python tool calls in warm kernels to initialize once bridge environment variables appear after startup and to return a clear `tool bridge is unavailable` error when missing
75
+ - Fixed IRC `send` handling to preserve recipient incoming messages when auto-reply timeouts instead of dropping them
76
+ - Fixed Python session disposal to cancel all concurrent active executions in a shared kernel
77
+ - Fixed JavaScript `eval` imports to preserve module-level singletons across re-imports of unchanged local files and reload them only after edits
78
+ - Fixed concurrent Python evaluator tool calls to use per-run identifiers so tool responses and output are routed to the correct execution
79
+ - Fixed the `search` tool argument validation to accept a single string `paths` value as a one-path search.
80
+
81
+ ## [15.4.0] - 2026-05-26
82
+
83
+ ### Breaking Changes
84
+
85
+ - Replaced the hashline patch format from `§`, `«`, `»`, `≔`, and `..` to `¶`, `↑`, `↓`, `→` with range separators written as `A-B`, requiring users to migrate hashline edit inputs
86
+
87
+ ### Added
88
+
89
+ - Added `codex` and `gemini` to the web search provider settings so users can configure OpenAI and Gemini web search directly from provider selection
90
+ - Added OpenAI (`codex`) and Gemini web search options with updated setup descriptions for `omp /login openai-codex` and Gemini OAuth login
91
+ - Added pretty-printing for wide JSON `data:` payloads in the raw provider-stream debug viewer so streamed event bodies expand across multiple `data:` lines instead of getting clipped by the per-line truncator, and updated the viewer header to read `raw provider stream (SSE + WS)` now that Codex WebSocket frames also flow through the buffer
92
+
93
+ ### Changed
94
+
95
+ - Updated hashline operation syntax to support inline payload text on the same op line for insert and replace (`ANCHOR↑/↓/...→`) while still accepting payload lines that follow
96
+ - Updated hashline anchor parsing so copied `|TEXT` decorations remain cosmetic and payload must be provided on or after the operator
97
+ - Unified subagent output-schema validation into a single shared module (`tools/output-schema-validator.ts`) used by both the in-process `yield` tool (validates before the subagent yields) and the executor's post-mortem `finalizeSubprocessOutput` path (validates after subprocess exit). Previously each side ran its own `normalizeSchema` → `jtdToJsonSchema` → `validateJsonSchemaValue` chain in parallel, which was semantically equivalent but invited drift: a future tweak on one side could silently disagree with the other and cause yields that pass in-tool to fail post-mortem (or vice versa). The unification preserves both call sites' existing behavior (yield throws an actionable per-issue error for the model; executor produces a `schema_violation` outcome with the first issue and missing-required fields) by exposing two output formatters (`formatAllValidationIssues` for retries, `formatValidationIssueHeadline` for headlines).
98
+ - Changed web search provider credential lookup to use the shared `AuthStorage` pipeline (`getApiKey`/`getOAuthAccess`) for API-key and OAuth auth instead of direct `AgentStorage` access
99
+ - Changed the `codex` web search provider display label from `Codex` to `OpenAI`
100
+ - Updated `anthropic` and `openai`/`gemini` web search option descriptions to reflect their native `web_search`/OAuth requirements
101
+
102
+ ### Fixed
103
+
104
+ - Fixed hashline inline payload parsing so same-line payloads containing whitespace, including tab-indented text, are preserved instead of being rejected
105
+ - Fixed Bun HTTP/2 transport errors (`HTTP2StreamReset`, `HTTP2RefusedStream`, and `HTTP2EnhanceYourCalm`) to be treated as transient so the assistant now retries automatically instead of stopping on these recoverable failures
106
+ - Fixed web search OAuth-backed providers (including Codex and Gemini) to use broker-managed token retrieval and account metadata, avoiding direct token-store refresh behavior that could cause search authentication failures
107
+ - Updated Tavily missing-credential feedback to prompt users to configure an API-key provider setting instead of referencing `agent.db` directly
108
+ - Refreshed expired OpenAI Codex OAuth tokens during `web_search` execution and persisted the updated credentials so searches continue working after token expiry
109
+ - Fixed built-in `explore` agent failing every invocation with `schema_violation: files.0.ref: must not be present` on releases prior to 15.3.2 by renaming the `files[].ref` property to `files[].path` in the agent's output schema; `ref` is a JTD-reserved keyword (RFC 8927) and collides with JSON Type Definition's schema-reference form, so the converter previously dropped it from the generated JSON Schema. Defense-in-depth alongside the 15.3.2 converter fix ([#1379](https://github.com/can1357/oh-my-pi/issues/1379)).
110
+ - Increased the `yield` tool's schema-validation retry budget from 1 to 3 so subagents whose first structured-output attempt mismatches the declared output schema get up to three retries before the parent's post-mortem `schema_violation` check hard-fails the task. The tool now also surfaces remaining retry attempts and an explicit "call yield again with the corrected shape" directive in each rejection message, giving the model the context it needs to converge — particularly helpful for models like GLM that tend to invent per-element field names instead of following the declared schema.
111
+ - Fixed CLI PDF file arguments being decoded as raw bytes for local vision models; `.pdf` and other supported document files now go through the same Markit conversion path as the `read` tool before entering the prompt ([#1401](https://github.com/can1357/oh-my-pi/issues/1401)).
112
+
5
113
  ## [15.3.2] - 2026-05-25
6
114
  ### Added
7
115
 
@@ -22,6 +130,8 @@
22
130
  - Added `OMP_NO_WEBP` environment variable to disable WebP encoding in image resize, fixing HTTP 400 errors when attaching browser snapshots to vision models running on local llama.cpp (which uses STB library that lacks WebP support)
23
131
  - Fixed loop mode submitting the next prompt while a background async-job delivery turn (idle flush) was still pending, which could cause the job result to be silently dropped and make the session appear to keep firing while work was ongoing ([#1294](https://github.com/can1357/oh-my-pi/issues/1294))
24
132
  - Fixed clipboard image paste (Ctrl+V) silently failing on WSL2 by routing image reads through a `powershell.exe` bridge when WSL interop is detected, since `arboard` returns `ContentNotAvailable` under WSLg ([#1280](https://github.com/can1357/oh-my-pi/issues/1280))
133
+ - Fixed `bash` tool timeout and ESC cancellation getting stuck when native shell cleanup stalls; the JavaScript-side deadline now returns the tool result on schedule while native cleanup continues in the background ([#1347](https://github.com/can1357/oh-my-pi/issues/1347))
134
+ - Fixed reviewer agent always failing JTD validation with `findings.0.priority: expected number, received string` whenever `report_finding` surfaced a finding; the tool's `"P0"`-`"P3"` priority is now coerced to its numeric ordinal before populating the auto-injected `findings[]` ([#1350](https://github.com/can1357/oh-my-pi/issues/1350))
25
135
  - Fixed config-only marketplace LSP plugins such as `csharp-lsp` not registering servers with the CLI when the plugin cache has only marketplace metadata and no package code ([#1352](https://github.com/can1357/oh-my-pi/issues/1352)).
26
136
  - Fixed JTD-to-JSON-Schema conversion treating user-named properties as nested JTD forms when their keys collided with JTD keywords like `ref`, which broke the built-in explore agent's output validator with `schema_violation: files.0.ref: must not be present` ([#1345](https://github.com/can1357/oh-my-pi/issues/1345))
27
137
  - Fixed extension `ctx.ui.notify()` messages emitted during `session_start` being cleared before the first interactive render ([#1316](https://github.com/can1357/oh-my-pi/issues/1316)).
@@ -7,5 +7,5 @@ export interface ProcessFileOptions {
7
7
  /** Whether to auto-resize images to 2000x2000 max. Default: true */
8
8
  autoResizeImages?: boolean;
9
9
  }
10
- /** Process @file arguments into text content and image attachments */
10
+ /** Process @file arguments into text, document content, and image attachments */
11
11
  export declare function processFileArguments(fileArgs: string[], options?: ProcessFileOptions): Promise<ProcessedFiles>;
@@ -1871,7 +1871,7 @@ export declare const SETTINGS_SCHEMA: {
1871
1871
  readonly ui: {
1872
1872
  readonly tab: "editing";
1873
1873
  readonly label: "Edit Mode";
1874
- readonly description: "Select the edit tool variant (replace, patch, hashline, vim, or apply_patch)";
1874
+ readonly description: "Select the edit tool variant (replace, patch, hashline, or apply_patch)";
1875
1875
  };
1876
1876
  };
1877
1877
  readonly "edit.fuzzyMatch": {
@@ -1951,7 +1951,7 @@ export declare const SETTINGS_SCHEMA: {
1951
1951
  readonly ui: {
1952
1952
  readonly tab: "editing";
1953
1953
  readonly label: "Hash Lines";
1954
- readonly description: "Include line hashes in read output for hashline edit mode (LINE+ID|content)";
1954
+ readonly description: "Include file-hash headers and line numbers in read output for hashline edit mode (\u00B6PATH#hash plus LINE:content)";
1955
1955
  };
1956
1956
  };
1957
1957
  readonly "read.defaultLimit": {
@@ -2282,6 +2282,31 @@ export declare const SETTINGS_SCHEMA: {
2282
2282
  readonly description: "Enable agent-to-agent IRC messaging via the irc tool";
2283
2283
  };
2284
2284
  };
2285
+ readonly "irc.timeoutMs": {
2286
+ readonly type: "number";
2287
+ readonly default: 120000;
2288
+ readonly ui: {
2289
+ readonly tab: "tools";
2290
+ readonly label: "IRC Timeout";
2291
+ readonly description: "Drop IRC messages whose recipient does not respond within this many milliseconds (0 disables the timeout)";
2292
+ readonly options: readonly [{
2293
+ readonly value: "0";
2294
+ readonly label: "Disabled";
2295
+ }, {
2296
+ readonly value: "30000";
2297
+ readonly label: "30 seconds";
2298
+ }, {
2299
+ readonly value: "60000";
2300
+ readonly label: "1 minute";
2301
+ }, {
2302
+ readonly value: "120000";
2303
+ readonly label: "2 minutes";
2304
+ }, {
2305
+ readonly value: "300000";
2306
+ readonly label: "5 minutes";
2307
+ }];
2308
+ };
2309
+ };
2285
2310
  readonly "renderMermaid.enabled": {
2286
2311
  readonly type: "boolean";
2287
2312
  readonly default: false;
@@ -2762,6 +2787,15 @@ export declare const SETTINGS_SCHEMA: {
2762
2787
  }];
2763
2788
  };
2764
2789
  };
2790
+ readonly "task.enableLsp": {
2791
+ readonly type: "boolean";
2792
+ readonly default: false;
2793
+ readonly ui: {
2794
+ readonly tab: "tasks";
2795
+ readonly label: "LSP in Subagents";
2796
+ readonly description: "Allow subagents spawned via the task tool to use the lsp tool. Off by default to keep subagents cheap; enable when LSP-aware delegation is worth the extra tokens.";
2797
+ };
2798
+ };
2765
2799
  readonly "task.maxRecursionDepth": {
2766
2800
  readonly type: "number";
2767
2801
  readonly default: 2;
@@ -2978,7 +3012,15 @@ export declare const SETTINGS_SCHEMA: {
2978
3012
  }, {
2979
3013
  readonly value: "anthropic";
2980
3014
  readonly label: "Anthropic";
2981
- readonly description: "Uses Anthropic web search";
3015
+ readonly description: "Claude's native web_search tool (uses Anthropic OAuth or ANTHROPIC_API_KEY)";
3016
+ }, {
3017
+ readonly value: "codex";
3018
+ readonly label: "OpenAI";
3019
+ readonly description: "OpenAI's native web_search (uses ChatGPT OAuth via /login openai-codex)";
3020
+ }, {
3021
+ readonly value: "gemini";
3022
+ readonly label: "Gemini";
3023
+ readonly description: "Google Search grounding via Gemini (uses google-gemini-cli or google-antigravity OAuth)";
2982
3024
  }, {
2983
3025
  readonly value: "zai";
2984
3026
  readonly label: "Z.AI";
@@ -88,7 +88,7 @@ export declare class Settings {
88
88
  getGroup<G extends GroupPrefix>(prefix: G): GroupTypeMap[G];
89
89
  /**
90
90
  * Get the edit variant for a specific model.
91
- * Returns "patch", "replace", "hashline", "vim", "apply_patch", or null (use global default).
91
+ * Returns "patch", "replace", "hashline", "apply_patch", or null (use global default).
92
92
  */
93
93
  getEditVariantForModel(model: string | undefined): EditMode | null;
94
94
  /**
@@ -1,5 +1,7 @@
1
1
  import { type Component } from "@oh-my-pi/pi-tui";
2
2
  import { type RawSseDebugBuffer } from "./raw-sse-buffer";
3
+ /** @internal Exported for tests. */
4
+ export declare function expandPrettyDataLines(raw: readonly string[]): string[];
3
5
  export interface RawSseViewerOptions {
4
6
  buffer: RawSseDebugBuffer;
5
7
  terminalRows: number;
@@ -2,19 +2,29 @@ import type { ToolSession } from "../tools";
2
2
  export interface FileReadSnapshot {
3
3
  /** 1-indexed line number → exact line content as observed by `read`/`search`. */
4
4
  lines: Map<number, string>;
5
+ /** Full normalized text when the read path observed the whole file. */
6
+ fullText?: string;
7
+ /** 4-hex hash of `fullText`, or a sparse snapshot hash supplied by search. */
8
+ fileHash?: string;
5
9
  recordedAt: number;
6
10
  }
11
+ interface FileReadSnapshotMetadata {
12
+ fullText?: string;
13
+ fileHash?: string;
14
+ }
7
15
  export declare class FileReadCache {
8
16
  #private;
9
17
  /** Look up the most recent snapshot for `absPath`, or `null` if absent. */
10
18
  get(absPath: string): FileReadSnapshot | null;
19
+ /** Look up the most recent snapshot for `absPath` whose file hash matches. */
20
+ getByHash(absPath: string, fileHash: string): FileReadSnapshot | null;
11
21
  /** Record a contiguous run of lines (e.g. from a `read` tool). `startLine` is 1-indexed. */
12
- recordContiguous(absPath: string, startLine: number, lines: readonly string[]): void;
22
+ recordContiguous(absPath: string, startLine: number, lines: readonly string[], metadata?: FileReadSnapshotMetadata): void;
13
23
  /** Record sparse `(lineNumber, content)` pairs (e.g. `search` matches plus context). */
14
- recordSparse(absPath: string, entries: Iterable<readonly [number, string]>): void;
15
- /** Drop the snapshot for a single path. */
24
+ recordSparse(absPath: string, entries: Iterable<readonly [number, string]>, metadata?: FileReadSnapshotMetadata): void;
25
+ /** Drop the snapshot history for a single path. */
16
26
  invalidate(absPath: string): void;
17
- /** Drop every snapshot. */
27
+ /** Drop every snapshot history. */
18
28
  clear(): void;
19
29
  }
20
30
  /**
@@ -23,3 +33,4 @@ export declare class FileReadCache {
23
33
  * the session itself.
24
34
  */
25
35
  export declare function getFileReadCache(session: ToolSession): FileReadCache;
36
+ export {};
@@ -1,10 +1,7 @@
1
1
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
2
- import type * as z from "zod/v4";
3
2
  import { type HashlineParams, hashlineEditParamsSchema } from "../hashline";
4
3
  import type { ToolSession } from "../tools";
5
- import { vimSchema } from "../tools/vim";
6
4
  import { type EditMode } from "../utils/edit-mode";
7
- import type { VimToolDetails } from "../vim/types";
8
5
  import { type ApplyPatchParams, applyPatchSchema } from "./modes/apply-patch";
9
6
  import { type PatchParams, patchEditSchema } from "./modes/patch";
10
7
  import { type ReplaceParams, replaceEditSchema } from "./modes/replace";
@@ -20,10 +17,8 @@ export * from "./modes/replace";
20
17
  export * from "./normalize";
21
18
  export * from "./renderer";
22
19
  export * from "./streaming";
23
- type TInput = typeof replaceEditSchema | typeof patchEditSchema | typeof hashlineEditParamsSchema | typeof vimSchema | typeof applyPatchSchema;
24
- type VimParams = z.infer<typeof vimSchema>;
25
- type EditParams = ReplaceParams | PatchParams | HashlineParams | VimParams | ApplyPatchParams;
26
- type EditToolResultDetails = EditToolDetails | VimToolDetails;
20
+ type TInput = typeof replaceEditSchema | typeof patchEditSchema | typeof hashlineEditParamsSchema | typeof applyPatchSchema;
21
+ type EditParams = ReplaceParams | PatchParams | HashlineParams | ApplyPatchParams;
27
22
  export declare class EditTool implements AgentTool<TInput> {
28
23
  #private;
29
24
  private readonly session;
@@ -54,5 +49,5 @@ export declare class EditTool implements AgentTool<TInput> {
54
49
  * internal `name` and `customWireName`, so returned calls route correctly.
55
50
  */
56
51
  get customWireName(): string | undefined;
57
- execute(_toolCallId: string, params: EditParams, signal?: AbortSignal, onUpdate?: AgentToolUpdateCallback<EditToolResultDetails, TInput>, context?: AgentToolContext): Promise<AgentToolResult<EditToolResultDetails, TInput>>;
52
+ execute(_toolCallId: string, params: EditParams, signal?: AbortSignal, onUpdate?: AgentToolUpdateCallback<EditToolDetails, TInput>, context?: AgentToolContext): Promise<AgentToolResult<EditToolDetails, TInput>>;
58
53
  }
@@ -7,7 +7,6 @@ import type { FileDiagnosticsResult } from "../lsp";
7
7
  import { type Theme } from "../modes/theme/theme";
8
8
  import type { OutputMeta } from "../tools/output-meta";
9
9
  import { getLspBatchRequest, type LspBatchRequest } from "../tools/render-utils";
10
- import { type VimRenderArgs } from "../tools/vim";
11
10
  import type { EditMode } from "../utils/edit-mode";
12
11
  import type { DiffError, DiffResult } from "./diff";
13
12
  import type { Operation } from "./modes/patch";
@@ -94,7 +93,7 @@ export interface EditRenderContext {
94
93
  }
95
94
  export declare const editToolRenderer: {
96
95
  mergeCallAndResult: boolean;
97
- renderCall(args: EditRenderArgs | VimRenderArgs, options: RenderResultOptions & {
96
+ renderCall(args: EditRenderArgs, options: RenderResultOptions & {
98
97
  renderContext?: EditRenderContext;
99
98
  }, uiTheme: Theme): Component;
100
99
  renderResult(result: {
@@ -0,0 +1,16 @@
1
+ export type LocalImportResolution = {
2
+ mode: "local";
3
+ value: unknown;
4
+ } | {
5
+ mode: "external";
6
+ target: string;
7
+ };
8
+ export declare class LocalModuleLoader {
9
+ #private;
10
+ constructor(sessionId: string);
11
+ resolveForRun(cwd: string, source: string): Promise<LocalImportResolution>;
12
+ resolveForModule(moduleUrl: string, source: string, cwd: string): Promise<LocalImportResolution>;
13
+ requireForFile(moduleUrlOrPath: string | undefined, cwd: string): NodeJS.Require;
14
+ filenameForUrl(moduleUrlOrPath: string | undefined): string | null;
15
+ dirnameForUrl(moduleUrlOrPath: string | undefined, cwd: string): string;
16
+ }
@@ -1,4 +1,8 @@
1
1
  export declare function rewriteImports(code: string): string;
2
+ export declare function collectModuleSourceSpecifiers(code: string): string[];
3
+ export declare function rewriteModuleSourceSpecifiers(code: string, replacer: (source: string) => string): string;
4
+ export declare function rewriteDynamicImports(code: string, callee?: string): string;
5
+ export declare function stripTypeScriptSyntax(code: string): string;
2
6
  export declare function wrapCode(code: string): {
3
7
  source: string;
4
8
  asyncWrapped: boolean;
@@ -1,21 +1,24 @@
1
1
  import { type HelperBundle } from "./helpers";
2
2
  import type { JsDisplayOutput } from "./types";
3
3
  /**
4
- * Per-run callbacks. Returned by `getHooks()` on each helper/tool/display invocation so
5
- * the embedding worker can route emissions to the currently active run. Returning `null`
6
- * makes status/display/tool calls reject with an error — useful for guarding against
7
- * helpers being invoked outside a run window.
4
+ * Per-run callbacks. Runtime globals resolve these from AsyncLocalStorage so
5
+ * overlapping async cells can route output/tool calls back to their own run.
8
6
  */
9
7
  export interface RuntimeHooks {
10
8
  onText(chunk: string): void;
11
9
  onDisplay(output: JsDisplayOutput): void;
12
10
  callTool(name: string, args: unknown): Promise<unknown>;
13
11
  }
12
+ export interface RunContext {
13
+ runId: string;
14
+ hooks: RuntimeHooks;
15
+ cwd: string;
16
+ finalExpressionSet: boolean;
17
+ finalExpressionValue: unknown;
18
+ }
14
19
  export interface RuntimeOptions {
15
20
  initialCwd: string;
16
21
  sessionId: string;
17
- /** Resolve hooks for the run currently in flight, or `null` if nothing is active. */
18
- getHooks(): RuntimeHooks | null;
19
22
  /**
20
23
  * Extra globals installed alongside `__omp_helpers__` / prelude. Use for stable, lifetime-
21
24
  * of-the-worker bindings (e.g. browser's `page`, `browser`). Per-run scope should be set
@@ -42,6 +45,9 @@ export declare class JsRuntime {
42
45
  * cleanup it wants.
43
46
  */
44
47
  setRunScope(scope: Record<string, unknown>): void;
45
- run(code: string, filename?: string): Promise<unknown>;
46
- displayValue(value: unknown): void;
48
+ run(code: string, filename: string | undefined, hooks: RuntimeHooks, options?: {
49
+ runId?: string;
50
+ cwd?: string;
51
+ }): Promise<unknown>;
52
+ displayValue(value: unknown, hooks?: RuntimeHooks | undefined): void;
47
53
  }
@@ -1,7 +1,6 @@
1
1
  import type { ToolSession } from "../../tools";
2
2
  import type { JsStatusEvent } from "../js/shared/types";
3
- import type { KernelDisplayOutput } from "./display";
4
- import { type KernelExecuteOptions, type KernelExecuteResult } from "./kernel";
3
+ import { type KernelDisplayOutput, type KernelExecuteOptions, type KernelExecuteResult } from "./kernel";
5
4
  export type PythonKernelMode = "session" | "per-call";
6
5
  export interface PythonExecutorOptions {
7
6
  /** Working directory for command execution */
@@ -1,7 +1,13 @@
1
1
  import { type KernelDisplayOutput } from "./display";
2
2
  export type { KernelDisplayOutput, PythonStatusEvent } from "./display";
3
3
  export { renderKernelDisplay } from "./display";
4
+ export type KernelRuntimeEnv = Record<string, string | null>;
4
5
  export interface KernelExecuteOptions {
6
+ id?: string;
7
+ /** Runtime working directory applied immediately before this request executes. */
8
+ cwd?: string;
9
+ /** Managed runtime environment variables applied immediately before this request executes. */
10
+ env?: KernelRuntimeEnv;
5
11
  signal?: AbortSignal;
6
12
  onChunk?: (text: string) => Promise<void> | void;
7
13
  onDisplay?: (output: KernelDisplayOutput) => Promise<void> | void;
@@ -11,10 +11,6 @@ export interface PyToolBridgeInfo {
11
11
  }
12
12
  /** Starts the bridge server lazily and returns its connection info. */
13
13
  export declare function ensurePyToolBridge(): Promise<PyToolBridgeInfo>;
14
- /**
15
- * Register a tool session for the duration of one execution. The returned
16
- * function MUST be called to remove the entry once execution finishes.
17
- */
18
- export declare function registerPyToolBridge(sessionId: string, entry: PyToolBridgeEntry): () => void;
14
+ export declare function registerPyToolBridge(sessionId: string, runId: string, entry: PyToolBridgeEntry): () => void;
19
15
  /** Stop the bridge and clear registrations. Test-only / shutdown helper. */
20
16
  export declare function disposePyToolBridge(): Promise<void>;
@@ -0,0 +1,3 @@
1
+ import type { ToolSession } from "../tools";
2
+ export type EvalSessionSource = Pick<ToolSession, "cwd" | "getSessionFile">;
3
+ export declare function defaultEvalSessionId(session: EvalSessionSource): string;
@@ -179,8 +179,6 @@ export interface ExtensionContext {
179
179
  shutdown(): void;
180
180
  /** Get the current effective system prompt. */
181
181
  getSystemPrompt(): string[];
182
- /** @deprecated Use hasPendingMessages() instead */
183
- hasQueuedMessages(): boolean;
184
182
  }
185
183
  /**
186
184
  * Extended context for command handlers.
@@ -605,7 +603,7 @@ export interface ExtensionAPI {
605
603
  triggerTurn?: boolean;
606
604
  deliverAs?: "steer" | "followUp" | "nextTurn";
607
605
  }): void;
608
- /** Send a user message to the agent. Always triggers a turn. */
606
+ /** Send a user message to the agent, or queue it when deliverAs is set. */
609
607
  sendUserMessage(content: string | (TextContent | ImageContent)[], options?: {
610
608
  deliverAs?: "steer" | "followUp";
611
609
  }): void;
@@ -1,20 +1,26 @@
1
- import type { HashMismatch } from "./types";
2
1
  export declare function formatFullAnchorRequirement(raw?: string): string;
3
2
  export declare function parseTag(ref: string): {
4
3
  line: number;
5
- hash: string;
6
4
  };
5
+ export interface HashlineMismatchDetails {
6
+ path?: string;
7
+ expectedFileHash: string;
8
+ actualFileHash: string;
9
+ fileLines: string[];
10
+ anchorLines?: readonly number[];
11
+ }
7
12
  export declare class HashlineMismatchError extends Error {
8
- readonly mismatches: HashMismatch[];
13
+ readonly path: string | undefined;
14
+ readonly expectedFileHash: string;
15
+ readonly actualFileHash: string;
9
16
  readonly fileLines: string[];
10
- readonly remaps: ReadonlyMap<string, string>;
11
- constructor(mismatches: HashMismatch[], fileLines: string[]);
17
+ readonly anchorLines: readonly number[];
18
+ constructor(details: HashlineMismatchDetails);
12
19
  get displayMessage(): string;
13
- private static rejectionHeader;
14
- static formatDisplayMessage(mismatches: HashMismatch[], fileLines: string[]): string;
15
- static formatMessage(mismatches: HashMismatch[], fileLines: string[]): string;
20
+ static rejectionHeader(details: HashlineMismatchDetails): string[];
21
+ static formatDisplayMessage(details: HashlineMismatchDetails): string;
22
+ static formatMessage(details: HashlineMismatchDetails): string;
16
23
  }
17
24
  export declare function validateLineRef(ref: {
18
25
  line: number;
19
- hash: string;
20
26
  }, fileLines: string[]): void;
@@ -1,7 +1,5 @@
1
1
  /** Lines of context shown either side of a hash mismatch. */
2
2
  export declare const MISMATCH_CONTEXT = 2;
3
- /** Filler hash used for the interior of a multi-line range; not validated. */
4
- export declare const RANGE_INTERIOR_HASH = "**";
5
3
  /** Optional patch envelope start marker; silently consumed when present. */
6
4
  export declare const BEGIN_PATCH_MARKER = "*** Begin Patch";
7
5
  /** Optional patch envelope end marker; terminates parsing when encountered. */
@@ -1,5 +1,4 @@
1
- import { type HashlineInputSection } from "./input";
2
- import type { HashlineApplyOptions } from "./types";
1
+ import type { HashlineApplyOptions, HashlineInputSection } from "./types";
3
2
  export declare function computeHashlineSectionDiff(section: HashlineInputSection, cwd: string, options?: HashlineApplyOptions): Promise<{
4
3
  diff: string;
5
4
  firstChangedLine: number | undefined;
@@ -0,0 +1,52 @@
1
+ import { type HashlineToken } from "./tokenizer";
2
+ import type { HashlineEdit } from "./types";
3
+ /**
4
+ * Token-driven state machine that turns a stream of {@link HashlineToken}s
5
+ * into the flat list of {@link HashlineEdit}s applied downstream by the
6
+ * apply/diff layers.
7
+ *
8
+ * The executor owns:
9
+ * - the running edit index (kept monotonic across pending flushes),
10
+ * - the pending-payload buffer (lines accumulated for the most recently
11
+ * opened insert/replace op),
12
+ * - all parse-time diagnostics (range order, "delete with payload",
13
+ * orphan payload, unrecognized op),
14
+ * - the {@link terminated} flag set by `envelope-end`/`abort`.
15
+ *
16
+ * Tokens are dispatched in the order they arrive; the matching tokenizer
17
+ * supplies the line numbers carried inside each token so diagnostics line
18
+ * up with the source.
19
+ */
20
+ export declare class HashlineExecutor {
21
+ #private;
22
+ /** True once an `envelope-end` or `abort` token has been observed. */
23
+ get terminated(): boolean;
24
+ /**
25
+ * Consume one token. After `terminated` flips true subsequent feeds
26
+ * are silently ignored so callers can keep draining their tokenizer
27
+ * without explicit early-exit guards.
28
+ */
29
+ feed(token: HashlineToken): void;
30
+ /**
31
+ * Flush any open pending op (including its trailing blank lines, which
32
+ * are payload-significant) and return the accumulated edits and
33
+ * warnings. The executor is single-use; reset() is required for reuse.
34
+ */
35
+ end(): {
36
+ edits: HashlineEdit[];
37
+ warnings: string[];
38
+ };
39
+ /** Reset to a fresh state so the same instance can drive another parse. */
40
+ reset(): void;
41
+ }
42
+ /**
43
+ * Drive a full hashline diff through the tokenizer + executor pipeline and
44
+ * return the resulting edits plus any parse-time warnings. This is the
45
+ * convenience entry point most callers want; reach for {@link
46
+ * HashlineTokenizer}/{@link HashlineExecutor} directly only when you need
47
+ * streaming feeds, cross-section state, or custom token handling.
48
+ */
49
+ export declare function parseHashline(diff: string): {
50
+ edits: HashlineEdit[];
51
+ warnings: string[];
52
+ };