@oh-my-pi/pi-coding-agent 17.2.4 → 17.2.5

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 (319) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/{CHANGELOG-bpmhv26t.md → CHANGELOG-q2w43aw3.md} +51 -0
  3. package/dist/cli.js +4148 -3717
  4. package/dist/{template-8vdv6xb4.js → template-c2hyaytt.js} +8 -26
  5. package/dist/types/cli/args.d.ts +4 -1
  6. package/dist/types/cli/browser-relay-cli.d.ts +15 -0
  7. package/dist/types/cli/command-help.d.ts +99 -0
  8. package/dist/types/cli/help-extra.d.ts +2 -0
  9. package/dist/types/cli/thinking-levels.d.ts +5 -0
  10. package/dist/types/cli/update-cli.d.ts +13 -0
  11. package/dist/types/commands/browser-relay.d.ts +38 -0
  12. package/dist/types/commands/commit.d.ts +3 -0
  13. package/dist/types/commands/complete.d.ts +1 -1
  14. package/dist/types/commands/completions.d.ts +6 -0
  15. package/dist/types/commands/grep.d.ts +3 -0
  16. package/dist/types/commands/join.d.ts +4 -0
  17. package/dist/types/commands/launch-help.d.ts +171 -0
  18. package/dist/types/commands/launch.d.ts +5 -1
  19. package/dist/types/commands/models.d.ts +3 -0
  20. package/dist/types/commands/say.d.ts +10 -0
  21. package/dist/types/commands/worktree.d.ts +3 -0
  22. package/dist/types/config/keybindings.d.ts +5 -0
  23. package/dist/types/config/model-discovery.d.ts +2 -2
  24. package/dist/types/config/models-config-schema-bundle.d.ts +3 -0
  25. package/dist/types/config/models-config-schema.d.ts +2 -0
  26. package/dist/types/config/models-config.d.ts +1 -0
  27. package/dist/types/config/service-tier.d.ts +2 -0
  28. package/dist/types/config/settings-schema.d.ts +69 -21
  29. package/dist/types/cursor-bridge-tools.d.ts +1 -1
  30. package/dist/types/cursor.d.ts +7 -4
  31. package/dist/types/edit/index.d.ts +3 -3
  32. package/dist/types/edit/modes/replace.d.ts +22 -15
  33. package/dist/types/edit/renderer.d.ts +1 -1
  34. package/dist/types/eval/py/kernel.d.ts +3 -1
  35. package/dist/types/eval/py/spawn-options.d.ts +12 -16
  36. package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +8 -1
  37. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -1
  38. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +2 -0
  39. package/dist/types/launch/broker.d.ts +1 -1
  40. package/dist/types/launch/client.d.ts +6 -3
  41. package/dist/types/launch/ensure.d.ts +8 -0
  42. package/dist/types/launch/paths.d.ts +1 -1
  43. package/dist/types/launch/protocol.d.ts +2 -2
  44. package/dist/types/lsp/client.d.ts +2 -0
  45. package/dist/types/lsp/mux/daemon.d.ts +15 -0
  46. package/dist/types/lsp/mux/protocol.d.ts +57 -0
  47. package/dist/types/lsp/mux/server.d.ts +18 -0
  48. package/dist/types/lsp/types.d.ts +22 -2
  49. package/dist/types/main.d.ts +2 -0
  50. package/dist/types/mnemopi/embed-client.d.ts +1 -1
  51. package/dist/types/mnemopi/state.d.ts +0 -23
  52. package/dist/types/modes/components/assistant-message.d.ts +2 -0
  53. package/dist/types/modes/components/custom-editor.d.ts +2 -1
  54. package/dist/types/modes/components/read-tool-group.d.ts +2 -0
  55. package/dist/types/modes/components/stripped-tool-calls-placeholder.d.ts +12 -0
  56. package/dist/types/modes/components/tool-execution.d.ts +2 -0
  57. package/dist/types/modes/controllers/input-controller.d.ts +1 -0
  58. package/dist/types/modes/interactive-mode.d.ts +1 -0
  59. package/dist/types/modes/types.d.ts +1 -0
  60. package/dist/types/modes/workflow.d.ts +2 -1
  61. package/dist/types/sdk.d.ts +3 -1
  62. package/dist/types/secrets/index.d.ts +1 -1
  63. package/dist/types/session/agent-session-types.d.ts +3 -1
  64. package/dist/types/session/indexed-session-storage.d.ts +1 -0
  65. package/dist/types/session/session-advisors.d.ts +1 -1
  66. package/dist/types/session/session-context.d.ts +2 -1
  67. package/dist/types/session/session-maintenance.d.ts +1 -0
  68. package/dist/types/session/session-manager.d.ts +16 -11
  69. package/dist/types/session/session-stats.d.ts +10 -0
  70. package/dist/types/session/session-storage.d.ts +15 -3
  71. package/dist/types/system-prompt.d.ts +4 -1
  72. package/dist/types/task/index.d.ts +2 -1
  73. package/dist/types/task/spawn-policy.d.ts +5 -0
  74. package/dist/types/thinking.d.ts +1 -7
  75. package/dist/types/tools/ast-grep.d.ts +1 -1
  76. package/dist/types/tools/browser/registry.d.ts +2 -1
  77. package/dist/types/tools/browser/relay/bridge.d.ts +35 -0
  78. package/dist/types/tools/browser/relay/daemon.d.ts +16 -0
  79. package/dist/types/tools/browser/relay/kind.d.ts +19 -0
  80. package/dist/types/tools/browser/relay/protocol.d.ts +97 -0
  81. package/dist/types/tools/browser/relay/server.d.ts +35 -0
  82. package/dist/types/tools/browser/render.d.ts +1 -0
  83. package/dist/types/tools/browser.d.ts +3 -0
  84. package/dist/types/tools/computer/exposure.d.ts +9 -7
  85. package/dist/types/tools/computer/protocol.d.ts +70 -17
  86. package/dist/types/tools/computer/supervisor.d.ts +23 -10
  87. package/dist/types/tools/computer/worker-entry.d.ts +1 -0
  88. package/dist/types/tools/computer/worker.d.ts +42 -7
  89. package/dist/types/tools/computer-renderer.d.ts +7 -5
  90. package/dist/types/tools/computer.d.ts +25 -44
  91. package/dist/types/tools/context.d.ts +3 -0
  92. package/dist/types/tools/glob.d.ts +1 -1
  93. package/dist/types/tools/grep.d.ts +1 -1
  94. package/dist/types/tools/render-utils.d.ts +2 -0
  95. package/dist/types/tools/{browser/run-cancellation.d.ts → run-scope.d.ts} +8 -8
  96. package/dist/types/tools/tool-timeouts.d.ts +5 -0
  97. package/dist/types/tools/xdev.d.ts +8 -0
  98. package/dist/types/utils/clipboard.d.ts +2 -2
  99. package/dist/types/web/kagi.d.ts +1 -0
  100. package/dist/types/web/search/index.d.ts +1 -1
  101. package/dist/types/web/search/providers/anthropic.d.ts +1 -0
  102. package/dist/types/web/search/providers/base.d.ts +2 -0
  103. package/dist/types/web/search/providers/brave.d.ts +1 -0
  104. package/dist/types/web/search/providers/browser-page.d.ts +1 -0
  105. package/dist/types/web/search/providers/codex.d.ts +1 -0
  106. package/dist/types/web/search/providers/exa.d.ts +1 -0
  107. package/dist/types/web/search/providers/firecrawl.d.ts +1 -0
  108. package/dist/types/web/search/providers/gemini.d.ts +1 -0
  109. package/dist/types/web/search/providers/jina.d.ts +1 -0
  110. package/dist/types/web/search/providers/kagi.d.ts +1 -0
  111. package/dist/types/web/search/providers/kimi.d.ts +1 -0
  112. package/dist/types/web/search/providers/parallel.d.ts +1 -0
  113. package/dist/types/web/search/providers/perplexity.d.ts +1 -0
  114. package/dist/types/web/search/providers/searxng.d.ts +1 -0
  115. package/dist/types/web/search/providers/tavily.d.ts +1 -0
  116. package/dist/types/web/search/providers/tinyfish.d.ts +1 -0
  117. package/dist/types/web/search/providers/utils.d.ts +6 -9
  118. package/dist/types/web/search/providers/zai.d.ts +1 -0
  119. package/dist/types/web/search/types.d.ts +4 -0
  120. package/package.json +12 -12
  121. package/src/cli/args.ts +7 -87
  122. package/src/cli/browser-relay-cli.ts +119 -0
  123. package/src/cli/command-help.ts +103 -0
  124. package/src/cli/completion-gen.ts +5 -5
  125. package/src/cli/flag-tables.ts +9 -0
  126. package/src/cli/help-extra.ts +89 -0
  127. package/src/cli/setup-cli.ts +13 -21
  128. package/src/cli/thinking-levels.ts +7 -0
  129. package/src/cli/update-cli.ts +26 -7
  130. package/src/cli-commands.ts +170 -33
  131. package/src/cli.ts +17 -5
  132. package/src/commands/acp.ts +3 -1
  133. package/src/commands/agents.ts +3 -2
  134. package/src/commands/auth-broker.ts +3 -2
  135. package/src/commands/auth-gateway.ts +3 -2
  136. package/src/commands/bench.ts +2 -3
  137. package/src/commands/browser-relay.ts +53 -0
  138. package/src/commands/cleanse.ts +2 -2
  139. package/src/commands/commit.ts +3 -2
  140. package/src/commands/complete.ts +2 -1
  141. package/src/commands/completions.ts +3 -2
  142. package/src/commands/config.ts +3 -2
  143. package/src/commands/dry-balance.ts +2 -2
  144. package/src/commands/gallery.ts +3 -2
  145. package/src/commands/gc.ts +3 -2
  146. package/src/commands/grep.ts +3 -2
  147. package/src/commands/grievances.ts +3 -2
  148. package/src/commands/install.ts +2 -2
  149. package/src/commands/join.ts +3 -2
  150. package/src/commands/launch-help.ts +116 -0
  151. package/src/commands/launch.ts +7 -191
  152. package/src/commands/models.ts +3 -2
  153. package/src/commands/plugin.ts +3 -2
  154. package/src/commands/read.ts +3 -2
  155. package/src/commands/say.ts +3 -2
  156. package/src/commands/setup.ts +3 -2
  157. package/src/commands/shell.ts +3 -2
  158. package/src/commands/ssh.ts +3 -2
  159. package/src/commands/stats.ts +3 -2
  160. package/src/commands/tiny-models.ts +2 -2
  161. package/src/commands/token.ts +2 -2
  162. package/src/commands/ttsr.ts +2 -2
  163. package/src/commands/update.ts +3 -2
  164. package/src/commands/usage.ts +3 -2
  165. package/src/commands/web-search.ts +3 -2
  166. package/src/commands/worktree.ts +3 -2
  167. package/src/config/keybindings.ts +5 -0
  168. package/src/config/model-discovery.ts +30 -13
  169. package/src/config/model-registry.ts +9 -5
  170. package/src/config/models-config-schema-bundle.ts +9 -0
  171. package/src/config/resolve-config-value.ts +2 -1
  172. package/src/config/service-tier.ts +5 -0
  173. package/src/config/settings-schema.ts +72 -20
  174. package/src/cursor-bridge-tools.ts +1 -1
  175. package/src/cursor.ts +10 -14
  176. package/src/edit/diff.ts +17 -8
  177. package/src/edit/index.ts +20 -7
  178. package/src/edit/modes/replace.ts +93 -40
  179. package/src/edit/renderer.ts +1 -1
  180. package/src/edit/streaming.ts +14 -17
  181. package/src/eval/py/kernel.ts +2 -1
  182. package/src/eval/py/spawn-options.ts +24 -29
  183. package/src/export/html/template.js +8 -26
  184. package/src/extensibility/extensions/wrapper.ts +1 -0
  185. package/src/extensibility/legacy-pi-ai-shim.ts +22 -1
  186. package/src/extensibility/legacy-pi-coding-agent-shim.ts +7 -6
  187. package/src/extensibility/plugins/legacy-pi-compat.ts +124 -57
  188. package/src/extensibility/skills.ts +11 -0
  189. package/src/extensibility/utils.ts +10 -2
  190. package/src/launch/broker.ts +1 -1
  191. package/src/launch/client.ts +32 -13
  192. package/src/launch/ensure.ts +73 -0
  193. package/src/launch/paths.ts +1 -1
  194. package/src/launch/protocol.ts +2 -2
  195. package/src/lsp/client.ts +23 -2
  196. package/src/lsp/index.ts +8 -0
  197. package/src/lsp/mux/daemon.ts +348 -0
  198. package/src/lsp/mux/protocol.ts +96 -0
  199. package/src/lsp/mux/server.ts +797 -0
  200. package/src/lsp/types.ts +28 -2
  201. package/src/main.ts +7 -2
  202. package/src/mcp/oauth-flow.ts +11 -1
  203. package/src/mnemopi/embed-client.ts +49 -2
  204. package/src/mnemopi/state.ts +57 -23
  205. package/src/modes/components/assistant-message.ts +11 -0
  206. package/src/modes/components/chat-transcript-builder.ts +4 -0
  207. package/src/modes/components/custom-editor.ts +9 -0
  208. package/src/modes/components/read-tool-group.ts +10 -0
  209. package/src/modes/components/status-line/component.ts +1 -1
  210. package/src/modes/components/stripped-tool-calls-placeholder.ts +35 -0
  211. package/src/modes/components/tool-execution.ts +8 -0
  212. package/src/modes/components/tree-selector.ts +46 -48
  213. package/src/modes/controllers/event-controller.ts +17 -0
  214. package/src/modes/controllers/input-controller.ts +39 -1
  215. package/src/modes/controllers/mcp-command-controller.ts +11 -5
  216. package/src/modes/controllers/selector-controller.ts +20 -0
  217. package/src/modes/interactive-mode.ts +2 -0
  218. package/src/modes/types.ts +1 -0
  219. package/src/modes/utils/hotkeys-markdown.ts +1 -0
  220. package/src/modes/utils/interactive-context-helpers.ts +1 -0
  221. package/src/modes/utils/ui-helpers.ts +6 -10
  222. package/src/modes/workflow.ts +8 -2
  223. package/src/prompts/agents/init.md +1 -1
  224. package/src/prompts/system/plan-mode-active.md +2 -2
  225. package/src/prompts/system/system-prompt.md +26 -52
  226. package/src/prompts/system/workflow-notice.md +3 -3
  227. package/src/prompts/tools/ast-grep.md +1 -1
  228. package/src/prompts/tools/bash.md +1 -1
  229. package/src/prompts/tools/browser.md +1 -0
  230. package/src/prompts/tools/computer.md +21 -21
  231. package/src/prompts/tools/glob.md +1 -1
  232. package/src/prompts/tools/grep.md +1 -1
  233. package/src/prompts/tools/replace.md +5 -4
  234. package/src/prompts/tools/task.md +4 -4
  235. package/src/sdk.ts +34 -6
  236. package/src/secrets/index.ts +1 -1
  237. package/src/session/agent-session-types.ts +3 -1
  238. package/src/session/agent-session.ts +13 -1
  239. package/src/session/indexed-session-storage.ts +9 -0
  240. package/src/session/prewalk.ts +24 -2
  241. package/src/session/session-advisors.ts +2 -2
  242. package/src/session/session-context.ts +16 -12
  243. package/src/session/session-dump-format.ts +1 -1
  244. package/src/session/session-maintenance.ts +38 -4
  245. package/src/session/session-manager.ts +102 -67
  246. package/src/session/session-paths.ts +118 -55
  247. package/src/session/session-stats.ts +48 -3
  248. package/src/session/session-storage.ts +30 -22
  249. package/src/session/stream-guards.ts +1 -1
  250. package/src/slash-commands/builtin-registry.ts +27 -24
  251. package/src/system-prompt.ts +8 -3
  252. package/src/task/index.ts +23 -8
  253. package/src/task/spawn-policy.ts +14 -0
  254. package/src/thinking.ts +2 -8
  255. package/src/tools/ast-grep.ts +10 -4
  256. package/src/tools/browser/cmux/cmux-tab.ts +5 -10
  257. package/src/tools/browser/cmux/rpc.ts +2 -8
  258. package/src/tools/browser/registry.ts +53 -2
  259. package/src/tools/browser/relay/bridge.ts +945 -0
  260. package/src/tools/browser/relay/daemon.ts +117 -0
  261. package/src/tools/browser/relay/extension-assets/background.js.txt +242 -0
  262. package/src/tools/browser/relay/extension-assets/manifest.json.txt +14 -0
  263. package/src/tools/browser/relay/extension-assets/options.html.txt +53 -0
  264. package/src/tools/browser/relay/extension-assets/options.js.txt +23 -0
  265. package/src/tools/browser/relay/kind.ts +41 -0
  266. package/src/tools/browser/relay/protocol.ts +54 -0
  267. package/src/tools/browser/relay/server.ts +141 -0
  268. package/src/tools/browser/render.ts +4 -1
  269. package/src/tools/browser/shared-daemon.ts +7 -58
  270. package/src/tools/browser/tab-supervisor.ts +5 -4
  271. package/src/tools/browser/tab-worker.ts +33 -12
  272. package/src/tools/browser.ts +26 -1
  273. package/src/tools/computer/exposure.ts +11 -35
  274. package/src/tools/computer/protocol.ts +51 -10
  275. package/src/tools/computer/supervisor.ts +227 -124
  276. package/src/tools/computer/worker-entry.ts +11 -6
  277. package/src/tools/computer/worker.ts +695 -81
  278. package/src/tools/computer-renderer.ts +104 -65
  279. package/src/tools/computer.ts +134 -428
  280. package/src/tools/context.ts +3 -0
  281. package/src/tools/glob.ts +9 -2
  282. package/src/tools/grep.ts +12 -6
  283. package/src/tools/inspect-image.ts +32 -6
  284. package/src/tools/render-utils.ts +2 -0
  285. package/src/tools/{browser/run-cancellation.ts → run-scope.ts} +11 -11
  286. package/src/tools/tool-timeouts.ts +1 -0
  287. package/src/tools/xdev.ts +29 -2
  288. package/src/utils/clipboard.ts +40 -14
  289. package/src/utils/command-args.ts +26 -28
  290. package/src/utils/tool-choice.ts +0 -13
  291. package/src/web/kagi.ts +2 -1
  292. package/src/web/search/index.ts +18 -2
  293. package/src/web/search/providers/anthropic.ts +4 -1
  294. package/src/web/search/providers/base.ts +2 -0
  295. package/src/web/search/providers/brave.ts +3 -1
  296. package/src/web/search/providers/browser-page.ts +6 -4
  297. package/src/web/search/providers/codex.ts +4 -1
  298. package/src/web/search/providers/duckduckgo.ts +2 -1
  299. package/src/web/search/providers/ecosia.ts +2 -1
  300. package/src/web/search/providers/exa.ts +4 -2
  301. package/src/web/search/providers/firecrawl.ts +3 -1
  302. package/src/web/search/providers/gemini.ts +8 -2
  303. package/src/web/search/providers/google.ts +2 -1
  304. package/src/web/search/providers/jina.ts +12 -2
  305. package/src/web/search/providers/kagi.ts +3 -0
  306. package/src/web/search/providers/kimi.ts +5 -1
  307. package/src/web/search/providers/mojeek.ts +2 -1
  308. package/src/web/search/providers/parallel.ts +5 -1
  309. package/src/web/search/providers/perplexity.ts +6 -3
  310. package/src/web/search/providers/public.ts +1 -1
  311. package/src/web/search/providers/searxng.ts +11 -5
  312. package/src/web/search/providers/startpage.ts +10 -4
  313. package/src/web/search/providers/synthetic.ts +11 -5
  314. package/src/web/search/providers/tavily.ts +3 -1
  315. package/src/web/search/providers/tinyfish.ts +3 -1
  316. package/src/web/search/providers/utils.ts +12 -10
  317. package/src/web/search/providers/xai.ts +2 -2
  318. package/src/web/search/providers/zai.ts +18 -3
  319. package/src/web/search/types.ts +6 -0
@@ -258,23 +258,41 @@ function formatPreviewWindow(lines: string[], centerIndex: number, options: Prev
258
258
  .join("\n");
259
259
  }
260
260
 
261
- function findExactMatchOutcome(content: string, target: string): MatchOutcome | undefined {
262
- const exactIndex = content.indexOf(target);
263
- if (exactIndex === -1) {
261
+ function findExactMatchOutcome(
262
+ content: string,
263
+ target: string,
264
+ excludedRanges: readonly { startIndex: number; endIndex: number }[],
265
+ ): MatchOutcome | undefined {
266
+ let firstIndex: number | undefined;
267
+ let occurrences = 0;
268
+ const recordedIndices: number[] = [];
269
+ let searchStart = 0;
270
+
271
+ while (searchStart <= content.length - target.length) {
272
+ const index = content.indexOf(target, searchStart);
273
+ if (index === -1) break;
274
+
275
+ const endIndex = index + target.length;
276
+ if (!excludedRanges.some(range => index < range.endIndex && endIndex > range.startIndex)) {
277
+ firstIndex ??= index;
278
+ occurrences++;
279
+ if (recordedIndices.length < MAX_RECORDED_MATCHES) {
280
+ recordedIndices.push(index);
281
+ }
282
+ }
283
+ searchStart = endIndex;
284
+ }
285
+
286
+ if (firstIndex === undefined) {
264
287
  return undefined;
265
288
  }
266
289
 
267
- const occurrences = content.split(target).length - 1;
268
290
  if (occurrences > 1) {
269
291
  const contentLines = content.split("\n");
270
292
  const occurrenceLines: number[] = [];
271
293
  const occurrencePreviews: string[] = [];
272
- let searchStart = 0;
273
-
274
- for (let i = 0; i < MAX_RECORDED_MATCHES; i++) {
275
- const idx = content.indexOf(target, searchStart);
276
- if (idx === -1) break;
277
- const lineNumber = content.slice(0, idx).split("\n").length;
294
+ for (const index of recordedIndices) {
295
+ const lineNumber = content.slice(0, index).split("\n").length;
278
296
  occurrenceLines.push(lineNumber);
279
297
  occurrencePreviews.push(
280
298
  formatPreviewWindow(contentLines, lineNumber - 1, {
@@ -282,17 +300,16 @@ function findExactMatchOutcome(content: string, target: string): MatchOutcome |
282
300
  maxLen: OCCURRENCE_PREVIEW_MAX_LEN,
283
301
  }),
284
302
  );
285
- searchStart = idx + 1;
286
303
  }
287
304
 
288
305
  return { occurrences, occurrenceLines, occurrencePreviews };
289
306
  }
290
307
 
291
- const startLine = content.slice(0, exactIndex).split("\n").length;
308
+ const startLine = content.slice(0, firstIndex).split("\n").length;
292
309
  return {
293
310
  match: {
294
311
  actualText: target,
295
- startIndex: exactIndex,
312
+ startIndex: firstIndex,
296
313
  startLine,
297
314
  confidence: 1,
298
315
  },
@@ -408,6 +425,7 @@ function findBestFuzzyMatchCore(
408
425
  offsets: number[],
409
426
  threshold: number,
410
427
  includeDepth: boolean,
428
+ excludedRanges: readonly { startIndex: number; endIndex: number }[],
411
429
  ): BestFuzzyMatchResult {
412
430
  const targetNormalized = normalizeLines(targetLines, includeDepth);
413
431
 
@@ -417,6 +435,12 @@ function findBestFuzzyMatchCore(
417
435
  let aboveThresholdCount = 0;
418
436
 
419
437
  for (let start = 0; start <= contentLines.length - targetLines.length; start++) {
438
+ const startIndex = offsets[start];
439
+ const endLine = start + targetLines.length - 1;
440
+ const endIndex = Math.max(startIndex + 1, offsets[endLine] + contentLines[endLine].length);
441
+ if (excludedRanges.some(range => startIndex < range.endIndex && endIndex > range.startIndex)) {
442
+ continue;
443
+ }
420
444
  const windowLines = contentLines.slice(start, start + targetLines.length);
421
445
  const windowNormalized = normalizeLines(windowLines, includeDepth);
422
446
  let score = 0;
@@ -434,7 +458,7 @@ function findBestFuzzyMatchCore(
434
458
  bestScore = score;
435
459
  best = {
436
460
  actualText: windowLines.join("\n"),
437
- startIndex: offsets[start],
461
+ startIndex,
438
462
  startLine: start + 1,
439
463
  confidence: score,
440
464
  };
@@ -446,7 +470,12 @@ function findBestFuzzyMatchCore(
446
470
  return { best, aboveThresholdCount, secondBestScore };
447
471
  }
448
472
 
449
- function findBestFuzzyMatch(content: string, target: string, threshold: number): BestFuzzyMatchResult {
473
+ function findBestFuzzyMatch(
474
+ content: string,
475
+ target: string,
476
+ threshold: number,
477
+ excludedRanges: readonly { startIndex: number; endIndex: number }[],
478
+ ): BestFuzzyMatchResult {
450
479
  const contentLines = content.split("\n");
451
480
  const targetLines = target.split("\n");
452
481
 
@@ -458,11 +487,18 @@ function findBestFuzzyMatch(content: string, target: string, threshold: number):
458
487
  }
459
488
 
460
489
  const offsets = computeLineOffsets(contentLines);
461
- let result = findBestFuzzyMatchCore(contentLines, targetLines, offsets, threshold, true);
490
+ let result = findBestFuzzyMatchCore(contentLines, targetLines, offsets, threshold, true, excludedRanges);
462
491
 
463
492
  // Retry without indent depth if match is close but below threshold
464
493
  if (result.best && result.best.confidence < threshold && result.best.confidence >= FALLBACK_THRESHOLD) {
465
- const noDepthResult = findBestFuzzyMatchCore(contentLines, targetLines, offsets, threshold, false);
494
+ const noDepthResult = findBestFuzzyMatchCore(
495
+ contentLines,
496
+ targetLines,
497
+ offsets,
498
+ threshold,
499
+ false,
500
+ excludedRanges,
501
+ );
466
502
  if (noDepthResult.best && noDepthResult.best.confidence > result.best.confidence) {
467
503
  result = noDepthResult;
468
504
  }
@@ -473,25 +509,35 @@ function findBestFuzzyMatch(content: string, target: string, threshold: number):
473
509
 
474
510
  /**
475
511
  * Find a match for target text within content.
476
- * Used primarily for replace-mode edits.
512
+ * Used primarily for replace-mode edits; excluded ranges remain invisible to exact and fuzzy matching.
477
513
  */
478
514
  export function findMatch(
479
515
  content: string,
480
516
  target: string,
481
- options: { allowFuzzy: boolean; threshold?: number },
517
+ options: {
518
+ allowFuzzy: boolean;
519
+ threshold?: number;
520
+ excludedRanges?: readonly { startIndex: number; endIndex: number }[];
521
+ },
482
522
  ): MatchOutcome {
483
523
  if (target.length === 0) {
484
524
  return {};
485
525
  }
526
+ const excludedRanges = options.excludedRanges ?? [];
486
527
 
487
- const exactMatch = findExactMatchOutcome(content, target);
528
+ const exactMatch = findExactMatchOutcome(content, target, excludedRanges);
488
529
  if (exactMatch) {
489
530
  return exactMatch;
490
531
  }
491
532
 
492
533
  // Try fuzzy match
493
534
  const threshold = options.threshold ?? DEFAULT_FUZZY_THRESHOLD;
494
- const { best, aboveThresholdCount, secondBestScore } = findBestFuzzyMatch(content, target, threshold);
535
+ const { best, aboveThresholdCount, secondBestScore } = findBestFuzzyMatch(
536
+ content,
537
+ target,
538
+ threshold,
539
+ excludedRanges,
540
+ );
495
541
 
496
542
  if (!best) {
497
543
  return {};
@@ -1012,24 +1058,31 @@ export function findContextLine(
1012
1058
  return { index: undefined, confidence: bestScore };
1013
1059
  }
1014
1060
 
1015
- export const replaceEditEntrySchema = type({
1016
- old_text: "string",
1017
- new_text: "string",
1018
- "all?": "boolean",
1019
- });
1020
-
1021
1061
  export const replaceEditSchema = type({
1022
1062
  path: "string",
1023
- edits: replaceEditEntrySchema.array(),
1063
+ old_string: "string",
1064
+ new_string: "string",
1065
+ "replace_all?": "boolean",
1024
1066
  });
1025
1067
 
1026
- export type ReplaceEditEntry = typeof replaceEditEntrySchema.infer;
1027
1068
  export type ReplaceParams = typeof replaceEditSchema.infer;
1028
1069
 
1029
- export interface ExecuteReplaceSingleOptions {
1070
+ /**
1071
+ * Internal batch form of {@link ReplaceParams}, produced only by the Cursor
1072
+ * exec bridge: a `pi_edit` frame carries several replacements against one
1073
+ * path but must run as a single tool lifecycle with one aggregate result.
1074
+ * Never advertised to the model — the agent loop validates model calls
1075
+ * against {@link replaceEditSchema}, which has no `edits` field.
1076
+ */
1077
+ export interface ReplaceBatchParams {
1078
+ path: string;
1079
+ edits: Omit<ReplaceParams, "path">[];
1080
+ }
1081
+
1082
+ export interface ExecuteReplaceOptions {
1030
1083
  session: ToolSession;
1031
1084
  path: string;
1032
- params: ReplaceEditEntry;
1085
+ params: Omit<ReplaceParams, "path">;
1033
1086
  signal?: AbortSignal;
1034
1087
  batchRequest?: LspBatchRequest;
1035
1088
  allowFuzzy: boolean;
@@ -1038,9 +1091,9 @@ export interface ExecuteReplaceSingleOptions {
1038
1091
  beginDeferredDiagnosticsForPath: (path: string) => WritethroughDeferredHandle;
1039
1092
  }
1040
1093
 
1041
- export async function executeReplaceSingle(
1042
- options: ExecuteReplaceSingleOptions,
1043
- ): Promise<AgentToolResult<EditToolDetails, ReplaceEditEntry>> {
1094
+ export async function executeReplace(
1095
+ options: ExecuteReplaceOptions,
1096
+ ): Promise<AgentToolResult<EditToolDetails, ReplaceParams>> {
1044
1097
  const {
1045
1098
  session,
1046
1099
  path,
@@ -1052,12 +1105,12 @@ export async function executeReplaceSingle(
1052
1105
  writethrough,
1053
1106
  beginDeferredDiagnosticsForPath,
1054
1107
  } = options;
1055
- const { old_text, new_text, all } = params;
1108
+ const { old_string, new_string, replace_all } = params;
1056
1109
 
1057
1110
  enforcePlanModeWrite(session, path);
1058
1111
 
1059
- if (old_text.length === 0) {
1060
- throw new Error("old_text must not be empty.");
1112
+ if (old_string.length === 0) {
1113
+ throw new Error("old_string must not be empty.");
1061
1114
  }
1062
1115
 
1063
1116
  const absolutePath = resolvePlanPath(session, path);
@@ -1065,12 +1118,12 @@ export async function executeReplaceSingle(
1065
1118
  const { bom, text: content } = stripBom(rawContent);
1066
1119
  const originalEnding = detectLineEnding(content);
1067
1120
  const normalizedContent = normalizeToLF(content);
1068
- const normalizedOldText = normalizeToLF(old_text);
1069
- const normalizedNewText = normalizeToLF(new_text);
1121
+ const normalizedOldText = normalizeToLF(old_string);
1122
+ const normalizedNewText = normalizeToLF(new_string);
1070
1123
 
1071
1124
  const result = replaceText(normalizedContent, normalizedOldText, normalizedNewText, {
1072
1125
  fuzzy: allowFuzzy,
1073
- all: all ?? false,
1126
+ all: replace_all ?? false,
1074
1127
  threshold: fuzzyThreshold,
1075
1128
  });
1076
1129
 
@@ -115,7 +115,7 @@ interface EditRenderArgs {
115
115
  patch?: string;
116
116
  input?: string;
117
117
  _input?: string;
118
- all?: boolean;
118
+ replace_all?: boolean;
119
119
  // Patch mode fields
120
120
  op?: Operation;
121
121
  rename?: unknown;
@@ -30,7 +30,6 @@ import { computeEditDiff, type DiffError, type DiffResult } from "./diff";
30
30
  import { computeHashlineDiff, computeHashlineSectionDiff } from "./hashline/diff";
31
31
  import { type ApplyPatchEntry, expandApplyPatchToEntries, expandApplyPatchToPreviewEntries } from "./modes/apply-patch";
32
32
  import { computePatchDiff, type PatchEditEntry } from "./modes/patch";
33
- import type { ReplaceEditEntry } from "./modes/replace";
34
33
 
35
34
  export interface PerFileDiffPreview {
36
35
  path: string;
@@ -333,27 +332,32 @@ function splitApplyPatchPerFile(input: string): EditMatcherEntry[] {
333
332
 
334
333
  interface ReplaceArgs {
335
334
  path?: string;
336
- edits?: ReplaceEditEntry[];
335
+ old_string?: string;
336
+ new_string?: string;
337
+ replace_all?: boolean;
337
338
  __partialJson?: string;
338
339
  }
339
340
 
340
341
  const replaceStrategy: EditStreamingStrategy<ReplaceArgs> = {
341
342
  extractCompleteEdits(args, partialJson) {
342
- if (!args?.edits) return args;
343
- return { ...args, edits: dropIncompleteLastEdit(args.edits, partialJson, "edits") };
343
+ // While args are still streaming, `old_string` is only trustworthy once
344
+ // the parser has moved past it — i.e. the `new_string` key has appeared.
345
+ // Previewing a half-streamed `old_string` would flash a bogus
346
+ // "no match" diff error until the rest of the value arrives.
347
+ if (!partialJson || partialJson.includes('"new_string"')) return args;
348
+ return { ...args, old_string: undefined, new_string: undefined };
344
349
  },
345
350
  async computeDiffPreview(args, ctx) {
346
351
  if (!args.path) return null;
347
- const first = args.edits?.[0];
348
- if (!first || first.old_text === undefined || first.new_text === undefined) return null;
352
+ if (args.old_string === undefined || args.new_string === undefined) return null;
349
353
  ctx.signal.throwIfAborted();
350
354
  const result = await computeEditDiff(
351
355
  args.path,
352
- first.old_text,
353
- first.new_text,
356
+ args.old_string,
357
+ args.new_string,
354
358
  ctx.cwd,
355
359
  ctx.allowFuzzy ?? true,
356
- first.all,
360
+ args.replace_all,
357
361
  ctx.fuzzyThreshold,
358
362
  );
359
363
  ctx.signal.throwIfAborted();
@@ -363,14 +367,7 @@ const replaceStrategy: EditStreamingStrategy<ReplaceArgs> = {
363
367
  return "";
364
368
  },
365
369
  matcherDigest(args) {
366
- const edits = args?.edits;
367
- if (!Array.isArray(edits)) return undefined;
368
- let digest: string | undefined;
369
- for (const edit of edits) {
370
- if (typeof edit?.new_text !== "string") continue;
371
- digest = digest === undefined ? edit.new_text : `${digest}\n${edit.new_text}`;
372
- }
373
- return digest;
370
+ return typeof args?.new_string === "string" ? args.new_string : undefined;
374
371
  },
375
372
  matcherPaths(args) {
376
373
  return typeof args?.path === "string" && args.path.length > 0 ? [args.path] : undefined;
@@ -82,8 +82,9 @@ const availabilityCache = new Map<string, Promise<PythonKernelAvailability>>();
82
82
  export async function checkPythonKernelAvailability(
83
83
  cwd: string,
84
84
  interpreter?: string,
85
+ options?: { forceProbe?: boolean },
85
86
  ): Promise<PythonKernelAvailability> {
86
- if (isBunTestRuntime() || $flag("PI_PYTHON_SKIP_CHECK")) {
87
+ if (!options?.forceProbe && (isBunTestRuntime() || $flag("PI_PYTHON_SKIP_CHECK"))) {
87
88
  return { ok: true };
88
89
  }
89
90
  const resolvedCwd = path.resolve(cwd);
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * Subprocess spawn-option helpers for the Python kernel.
3
3
  *
4
- * Pure helpers (`shouldHideKernelWindow`, `consoleAttachedViaTTY`) live here
5
- * so they can be unit-tested without dragging in the kernel's runtime
6
- * dependencies. The effectful `hostHasInheritableConsole` wraps a Win32 FFI
7
- * probe with a TTY fallback and is the function `kernel.ts` actually calls.
4
+ * Pure helpers (`shouldHideKernelWindow`, `consoleAttached`) live here so they
5
+ * can be unit-tested without dragging in the kernel's runtime dependencies.
6
+ * The effectful `hostHasInheritableConsole` combines a Win32 FFI probe with
7
+ * TTY evidence and is the function `kernel.ts` actually calls.
8
8
  */
9
9
  import { dlopen, FFIType } from "bun:ffi";
10
10
 
@@ -54,20 +54,21 @@ export function shouldDetachKernel(platform: NodeJS.Platform): boolean {
54
54
  }
55
55
 
56
56
  /**
57
- * TTY-based fallback used when the Win32 console probe is unavailable.
57
+ * Combine native Win32 and stdio TTY evidence of an inheritable console.
58
58
  *
59
- * Returns `true` if any of stdin/stdout/stderr is currently a TTY. This
60
- * correctly detects the common interactive launches and the partial-
61
- * redirection cases (`omp -p > out.txt`, `< in.txt`, `2> err.log`) where at
62
- * least one stream stays bound to the terminal. The all-stdio-redirected
63
- * case (`< in > out 2> err` from a console) is the reason we prefer the
64
- * Win32 probe over this fallback whenever possible.
59
+ * `GetConsoleWindow()` detects classic consoles even when every stdio stream
60
+ * is redirected. TTY detection covers ConPTY-backed terminals, where compiled
61
+ * hosts can receive a null HWND despite being attached to Windows Terminal.
62
+ * Either signal must preserve console inheritance: `CREATE_NO_WINDOW` can
63
+ * deadlock NumPy native-extension loading in the Python child.
65
64
  */
66
- export function consoleAttachedViaTTY(opts: {
65
+ export function consoleAttached(opts: {
66
+ nativeConsole?: boolean | null;
67
67
  stdinIsTTY: boolean;
68
68
  stdoutIsTTY: boolean;
69
69
  stderrIsTTY: boolean;
70
70
  }): boolean {
71
+ if (opts.nativeConsole === true) return true;
71
72
  return opts.stdinIsTTY || opts.stdoutIsTTY || opts.stderrIsTTY;
72
73
  }
73
74
 
@@ -75,10 +76,10 @@ export function consoleAttachedViaTTY(opts: {
75
76
  * Probe `kernel32.dll!GetConsoleWindow()` to detect whether the current
76
77
  * Windows process owns a console window.
77
78
  *
78
- * Returns `true` for a non-NULL HWND, `false` when NULL (no console true
79
- * service / `DETACHED_PROCESS` / GUI parent), and `null` when the probe
80
- * itself fails (off-Windows, FFI disabled, or unexpected kernel32 layout).
81
- * A `null` return means "don't trust me, use the TTY fallback".
79
+ * Returns `true` for a non-NULL HWND, `false` when NULL, and `null` when the
80
+ * probe itself fails (off-Windows, FFI disabled, or unexpected kernel32
81
+ * layout). A false result is not conclusive for ConPTY-backed terminals, so
82
+ * callers must also inspect the stdio TTY signals.
82
83
  *
83
84
  * Cached on first call because in practice the console attachment of a
84
85
  * long-lived OMP host never changes for the lifetime of the process, and
@@ -117,21 +118,15 @@ export function __resetWindowsConsoleProbeCache(): void {
117
118
  /**
118
119
  * Whether the host process owns a console its children can inherit.
119
120
  *
120
- * - On Windows, the authoritative signal is `GetConsoleWindow()`. It returns
121
- * a non-NULL HWND whenever the process has a console attached, regardless
122
- * of how the standard streams are redirected so an `omp -p ... < in.txt
123
- * > out.txt 2> err.log` launched from a real Windows Terminal session is
124
- * correctly classified as console-attached and the kernel keeps its
125
- * inheritable console.
126
- * - On any other platform, or if the FFI probe fails, fall back to the
127
- * TTY-OR heuristic. That still catches the common interactive cases.
121
+ * On Windows, `GetConsoleWindow()` detects classic consoles and all-stdio-
122
+ * redirected launches while stdio TTYs detect ConPTY-backed terminals. Either
123
+ * signal is sufficient. Other platforms use the same TTY evidence, although
124
+ * `windowsHide` is a no-op there.
128
125
  */
129
126
  export function hostHasInheritableConsole(): boolean {
130
- if (process.platform === "win32") {
131
- const native = probeWindowsConsoleWindow();
132
- if (native !== null) return native;
133
- }
134
- return consoleAttachedViaTTY({
127
+ const nativeConsole = process.platform === "win32" ? probeWindowsConsoleWindow() : null;
128
+ return consoleAttached({
129
+ nativeConsole,
135
130
  stdinIsTTY: !!process.stdin.isTTY,
136
131
  stdoutIsTTY: !!process.stdout.isTTY,
137
132
  stderrIsTTY: !!process.stderr.isTTY,
@@ -204,7 +204,7 @@
204
204
  containsActive.set(node, has);
205
205
  }
206
206
 
207
- // Stack: [node, indent, justBranched, showConnector, isLast, gutters, isVirtualRootChild]
207
+ // Stack: [node, indent, showConnector, isLast, gutters, isVirtualRootChild]
208
208
  const stack = [];
209
209
 
210
210
  // Add roots (prioritize branch containing active leaf)
@@ -213,11 +213,11 @@
213
213
  );
214
214
  for (let i = orderedRoots.length - 1; i >= 0; i--) {
215
215
  const isLast = i === orderedRoots.length - 1;
216
- stack.push([orderedRoots[i], multipleRoots ? 1 : 0, multipleRoots, multipleRoots, isLast, [], multipleRoots]);
216
+ stack.push([orderedRoots[i], multipleRoots ? 1 : 0, multipleRoots, isLast, [], multipleRoots]);
217
217
  }
218
218
 
219
219
  while (stack.length > 0) {
220
- const [node, indent, justBranched, showConnector, isLast, gutters, isVirtualRootChild] = stack.pop();
220
+ const [node, indent, showConnector, isLast, gutters, isVirtualRootChild] = stack.pop();
221
221
 
222
222
  result.push({ node, indent, showConnector, isLast, gutters, isVirtualRootChild, multipleRoots });
223
223
 
@@ -229,18 +229,10 @@
229
229
  Number(containsActive.get(b)) - Number(containsActive.get(a))
230
230
  );
231
231
 
232
- // Calculate child indent (matches tree-selector.ts)
233
- let childIndent;
234
- if (multipleChildren) {
235
- // Parent branches: children get +1
236
- childIndent = indent + 1;
237
- } else if (justBranched && indent > 0) {
238
- // First generation after a branch: +1 for visual grouping
239
- childIndent = indent + 1;
240
- } else {
241
- // Single-child chain: stay flat
242
- childIndent = indent;
243
- }
232
+ // Real branch points add visual depth, and a virtual root's direct
233
+ // children (the session roots) nest one level under the shared
234
+ // column-0 root. Linear continuations otherwise stay aligned.
235
+ const childIndent = multipleChildren || isVirtualRootChild ? indent + 1 : indent;
244
236
 
245
237
  // Build gutters for children
246
238
  const connectorDisplayed = showConnector && !isVirtualRootChild;
@@ -253,7 +245,7 @@
253
245
  // Add children in reverse order for stack
254
246
  for (let i = orderedChildren.length - 1; i >= 0; i--) {
255
247
  const childIsLast = i === orderedChildren.length - 1;
256
- stack.push([orderedChildren[i], childIndent, multipleChildren, multipleChildren, childIsLast, childGutters, false]);
248
+ stack.push([orderedChildren[i], childIndent, multipleChildren, childIsLast, childGutters, false]);
257
249
  }
258
250
  }
259
251
 
@@ -268,13 +260,6 @@
268
260
  const displayIndent = multipleRoots ? Math.max(0, indent - 1) : indent;
269
261
  const connector = showConnector && !isVirtualRootChild ? (isLast ? '└─ ' : '├─ ') : '';
270
262
  const connectorPosition = connector ? displayIndent - 1 : -1;
271
- // Chain rows (no connector of their own) under a last-sibling (`└─`)
272
- // branch stay anchored by a vertical drawn one level right of the
273
- // suppressed gutter — below the branch head's content — never in the
274
- // `└─` corner column itself (#2298, #2325). Chains under `├─` heads
275
- // are already anchored by the sibling line (`show: true` gutter).
276
- const nearestGutter = !connector ? gutters[gutters.length - 1] : undefined;
277
- const chainAnchorLevel = nearestGutter && !nearestGutter.show ? nearestGutter.position + 1 : -1;
278
263
 
279
264
  const totalChars = displayIndent * 3;
280
265
  const prefixChars = [];
@@ -287,9 +272,6 @@
287
272
  // Standard tree semantics: `│` only while more siblings continue
288
273
  // below (`show`), space below a `└─`.
289
274
  prefixChars.push(posInLevel === 0 && gutter.show ? '│' : ' ');
290
- } else if (level === chainAnchorLevel) {
291
- // Chain anchor for rows under a `└─` branch head.
292
- prefixChars.push(posInLevel === 0 ? '│' : ' ');
293
275
  } else if (connector && level === connectorPosition) {
294
276
  if (posInLevel === 0) {
295
277
  prefixChars.push(isLast ? '└' : '├');
@@ -239,6 +239,7 @@ export class ExtensionToolWrapper<TParameters extends TSchema = TSchema, TDetail
239
239
  // short-circuit above.
240
240
  const resolvedArgs = approvalArgs(effectiveParams, context);
241
241
  const resolved = resolveApproval(this.tool, resolvedArgs, approvalMode, userPolicies);
242
+ context?.xdevTierResolved?.(resolved.tier);
242
243
  if (resolved.policy === "deny") {
243
244
  throw new Error(
244
245
  `Tool "${this.tool.name}" is blocked by user policy.\n` +
@@ -19,7 +19,15 @@
19
19
  * `types.ts` via the `export *` below — pi-ai still exports both as types,
20
20
  * only the runtime `Type` builder and `StringEnum()` helper were removed.
21
21
  */
22
- import type { Api, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
22
+ import {
23
+ type Api,
24
+ type AssistantMessage,
25
+ type AssistantMessageEventStream,
26
+ type Context,
27
+ type Model,
28
+ type SimpleStreamOptions,
29
+ streamSimple,
30
+ } from "@oh-my-pi/pi-ai";
23
31
  import type { Effort } from "@oh-my-pi/pi-catalog/effort";
24
32
  import { clampThinkingLevelForModel } from "@oh-my-pi/pi-catalog/model-thinking";
25
33
  import {
@@ -121,6 +129,19 @@ export { calculateCost, getBundledModel, getBundledModels, getBundledProviders,
121
129
  export const getModel = getBundledModel;
122
130
  export const getModels = getBundledModels;
123
131
 
132
+ /**
133
+ * Stream OpenAI Responses through the historical simple-options contract.
134
+ *
135
+ * Legacy `/compat` callers pass {@link SimpleStreamOptions}; routing through
136
+ * `streamSimple` preserves option normalization before provider dispatch.
137
+ */
138
+ export function streamSimpleOpenAIResponses(
139
+ model: Model<"openai-responses">,
140
+ context: Context,
141
+ options?: SimpleStreamOptions,
142
+ ): AssistantMessageEventStream {
143
+ return streamSimple(model, context, options);
144
+ }
124
145
  /**
125
146
  * Compatibility re-exports for runtime helpers that upstream
126
147
  * `@earendil-works/pi-ai` exposed from its package root but omp's
@@ -1415,12 +1415,13 @@ export function getPackageDir(): string {
1415
1415
  return getOmpPackageDir() ?? (isCompiledBinary() ? path.dirname(process.execPath) : process.cwd());
1416
1416
  }
1417
1417
 
1418
- // Legacy pi's `@earendil-works/pi-coding-agent` re-exported `estimateTokens`
1419
- // and `compact` from its package root (via `./core/compaction/index.ts`). In
1420
- // omp both live in `@oh-my-pi/pi-agent-core/compaction`, and the coding-agent
1421
- // barrel below does not forward them, so legacy extensions importing either
1422
- // fail Bun's static export check during validation (issues #6583, #7174).
1423
- export { compact, estimateTokens } from "@oh-my-pi/pi-agent-core/compaction";
1418
+ // Legacy pi's `@earendil-works/pi-coding-agent` re-exported `estimateTokens`,
1419
+ // `compact`, and `serializeConversation` from its package root (via
1420
+ // `./core/compaction/index.ts`). In omp they live in
1421
+ // `@oh-my-pi/pi-agent-core/compaction`, and the coding-agent barrel below does
1422
+ // not forward them, so legacy extensions importing them fail Bun's static
1423
+ // export check during validation (issues #6583, #7174, #7403).
1424
+ export { compact, estimateTokens, serializeConversation } from "@oh-my-pi/pi-agent-core/compaction";
1424
1425
 
1425
1426
  // Same barrel gap for two more legacy package-root exports: pi re-exported the
1426
1427
  // `CONFIG_DIR_NAME` constant and the CLI parser `parseArgs`. In omp