@oh-my-pi/pi-coding-agent 15.10.10 → 15.10.12

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 (415) hide show
  1. package/CHANGELOG.md +142 -7
  2. package/dist/cli.js +23108 -0
  3. package/dist/tokenizers.linux-x64-gnu-xcjh3jwk.node +0 -0
  4. package/dist/types/async/job-manager.d.ts +18 -0
  5. package/dist/types/cli/args.d.ts +2 -1
  6. package/dist/types/cli/dry-balance-cli.d.ts +1 -1
  7. package/dist/types/cli/gallery-cli.d.ts +1 -1
  8. package/dist/types/cli/gallery-fixtures/types.d.ts +1 -1
  9. package/dist/types/cli/usage-cli.d.ts +72 -0
  10. package/dist/types/cli-commands.d.ts +12 -0
  11. package/dist/types/commands/launch.d.ts +5 -1
  12. package/dist/types/commands/read.d.ts +1 -1
  13. package/dist/types/commands/usage.d.ts +25 -0
  14. package/dist/types/config/api-key-resolver.d.ts +3 -0
  15. package/dist/types/config/append-only-context-mode.d.ts +2 -1
  16. package/dist/types/config/model-discovery.d.ts +55 -0
  17. package/dist/types/config/model-registry.d.ts +8 -219
  18. package/dist/types/config/model-resolver.d.ts +34 -10
  19. package/dist/types/config/model-roles.d.ts +28 -0
  20. package/dist/types/config/models-config-schema.d.ts +523 -42
  21. package/dist/types/config/models-config.d.ts +385 -0
  22. package/dist/types/config/settings-schema.d.ts +41 -8
  23. package/dist/types/config/settings.d.ts +8 -1
  24. package/dist/types/debug/log-viewer.d.ts +1 -1
  25. package/dist/types/debug/raw-sse.d.ts +1 -1
  26. package/dist/types/edit/hashline/noop-loop-guard.d.ts +72 -0
  27. package/dist/types/eval/backend.d.ts +0 -2
  28. package/dist/types/eval/idle-timeout.d.ts +0 -4
  29. package/dist/types/eval/js/shared/rewrite-imports.d.ts +6 -6
  30. package/dist/types/eval/py/executor.d.ts +5 -0
  31. package/dist/types/eval/py/kernel.d.ts +6 -1
  32. package/dist/types/eval/py/runtime.d.ts +9 -0
  33. package/dist/types/exec/bash-executor.d.ts +2 -0
  34. package/dist/types/export/html/template.generated.d.ts +1 -1
  35. package/dist/types/extensibility/extensions/runner.d.ts +3 -2
  36. package/dist/types/extensibility/extensions/types.d.ts +6 -3
  37. package/dist/types/hindsight/mental-models.d.ts +17 -8
  38. package/dist/types/internal-urls/artifact-protocol.d.ts +2 -2
  39. package/dist/types/internal-urls/types.d.ts +1 -1
  40. package/dist/types/lsp/edits.d.ts +9 -0
  41. package/dist/types/lsp/index.d.ts +2 -2
  42. package/dist/types/lsp/types.d.ts +2 -0
  43. package/dist/types/lsp/utils.d.ts +3 -0
  44. package/dist/types/mcp/json-rpc.d.ts +5 -0
  45. package/dist/types/memory-backend/index.d.ts +1 -0
  46. package/dist/types/memory-backend/runtime.d.ts +4 -0
  47. package/dist/types/memory-backend/types.d.ts +66 -1
  48. package/dist/types/mnemopi/state.d.ts +11 -1
  49. package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
  50. package/dist/types/modes/components/assistant-message.d.ts +3 -1
  51. package/dist/types/modes/components/bash-execution.d.ts +1 -1
  52. package/dist/types/modes/components/copy-selector.d.ts +1 -1
  53. package/dist/types/modes/components/dynamic-border.d.ts +1 -1
  54. package/dist/types/modes/components/extensions/extension-dashboard.d.ts +1 -1
  55. package/dist/types/modes/components/extensions/extension-list.d.ts +1 -1
  56. package/dist/types/modes/components/extensions/inspector-panel.d.ts +1 -1
  57. package/dist/types/modes/components/footer.d.ts +1 -1
  58. package/dist/types/modes/components/hook-editor.d.ts +5 -0
  59. package/dist/types/modes/components/hook-input.d.ts +4 -0
  60. package/dist/types/modes/components/hook-selector.d.ts +1 -1
  61. package/dist/types/modes/components/model-selector.d.ts +1 -1
  62. package/dist/types/modes/components/plan-review-overlay.d.ts +1 -1
  63. package/dist/types/modes/components/session-observer-overlay.d.ts +1 -1
  64. package/dist/types/modes/components/session-selector.d.ts +1 -1
  65. package/dist/types/modes/components/status-line/component.d.ts +1 -1
  66. package/dist/types/modes/components/tiny-title-download-progress.d.ts +1 -1
  67. package/dist/types/modes/components/transcript-container.d.ts +25 -6
  68. package/dist/types/modes/components/tree-selector.d.ts +1 -1
  69. package/dist/types/modes/components/user-message-selector.d.ts +1 -1
  70. package/dist/types/modes/components/user-message.d.ts +2 -1
  71. package/dist/types/modes/components/visual-truncate.d.ts +1 -1
  72. package/dist/types/modes/components/welcome.d.ts +19 -3
  73. package/dist/types/modes/controllers/mcp-command-controller.d.ts +1 -1
  74. package/dist/types/modes/controllers/streaming-reveal.d.ts +1 -1
  75. package/dist/types/modes/index.d.ts +3 -3
  76. package/dist/types/modes/interactive-mode.d.ts +8 -3
  77. package/dist/types/modes/oauth-manual-input.d.ts +7 -0
  78. package/dist/types/modes/rpc/rpc-client.d.ts +39 -2
  79. package/dist/types/modes/rpc/rpc-mode.d.ts +31 -2
  80. package/dist/types/modes/rpc/rpc-subagents.d.ts +24 -0
  81. package/dist/types/modes/rpc/rpc-types.d.ts +75 -1
  82. package/dist/types/modes/setup-wizard/index.d.ts +5 -1
  83. package/dist/types/modes/setup-wizard/lazy.d.ts +2 -0
  84. package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +1 -1
  85. package/dist/types/modes/setup-wizard/scenes/types.d.ts +1 -1
  86. package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +1 -1
  87. package/dist/types/modes/setup-wizard/wizard-overlay.d.ts +1 -1
  88. package/dist/types/modes/types.d.ts +4 -1
  89. package/dist/types/secrets/index.d.ts +1 -1
  90. package/dist/types/secrets/obfuscator.d.ts +8 -2
  91. package/dist/types/session/agent-session.d.ts +15 -3
  92. package/dist/types/session/auth-broker-config.d.ts +4 -0
  93. package/dist/types/session/session-manager.d.ts +1 -1
  94. package/dist/types/session/streaming-output.d.ts +23 -0
  95. package/dist/types/slash-commands/acp-builtins.d.ts +16 -0
  96. package/dist/types/slash-commands/builtin-registry.d.ts +1 -0
  97. package/dist/types/slash-commands/helpers/stats-dashboard.d.ts +13 -0
  98. package/dist/types/slash-commands/types.d.ts +1 -1
  99. package/dist/types/ssh/connection-manager.d.ts +8 -0
  100. package/dist/types/system-prompt.d.ts +2 -0
  101. package/dist/types/task/executor.d.ts +1 -0
  102. package/dist/types/task/index.d.ts +2 -2
  103. package/dist/types/task/parallel.d.ts +2 -2
  104. package/dist/types/task/types.d.ts +8 -0
  105. package/dist/types/task/worktree.d.ts +2 -0
  106. package/dist/types/thinking.d.ts +4 -0
  107. package/dist/types/tiny/title-client.d.ts +11 -0
  108. package/dist/types/tiny/title-protocol.d.ts +1 -0
  109. package/dist/types/tools/ask.d.ts +4 -0
  110. package/dist/types/tools/conflict-detect.d.ts +16 -0
  111. package/dist/types/tools/github-cache.d.ts +7 -0
  112. package/dist/types/tools/index.d.ts +6 -0
  113. package/dist/types/tools/sqlite-reader.d.ts +3 -0
  114. package/dist/types/tui/output-block.d.ts +3 -3
  115. package/dist/types/utils/changelog.d.ts +8 -0
  116. package/dist/types/utils/git.d.ts +15 -2
  117. package/dist/types/utils/title-generator.d.ts +3 -2
  118. package/dist/types/web/scrapers/readthedocs.d.ts +3 -0
  119. package/dist/types/web/scrapers/types.d.ts +12 -0
  120. package/dist/types/web/search/providers/codex.d.ts +1 -1
  121. package/dist/types/web/search/providers/gemini.d.ts +1 -1
  122. package/examples/extensions/tools.ts +5 -4
  123. package/package.json +14 -11
  124. package/scripts/build-binary.ts +18 -23
  125. package/scripts/bundle-dist.ts +81 -0
  126. package/scripts/{dev-launch → omp} +1 -1
  127. package/scripts/{dev-launch-preload.ts → omp.ts} +1 -1
  128. package/src/async/job-manager.ts +57 -3
  129. package/src/auto-thinking/classifier.ts +1 -0
  130. package/src/autoresearch/dashboard.ts +1 -1
  131. package/src/autoresearch/prompt-setup.md +6 -6
  132. package/src/autoresearch/prompt.md +6 -6
  133. package/src/capability/fs.ts +10 -0
  134. package/src/cli/args.ts +4 -1
  135. package/src/cli/auth-gateway-cli.ts +1 -3
  136. package/src/cli/dry-balance-cli.ts +1 -1
  137. package/src/cli/gallery-cli.ts +1 -1
  138. package/src/cli/gallery-fixtures/fs.ts +1 -1
  139. package/src/cli/gallery-fixtures/types.ts +5 -1
  140. package/src/cli/list-models.ts +2 -1
  141. package/src/cli/usage-cli.ts +603 -0
  142. package/src/cli-commands.ts +30 -0
  143. package/src/cli.ts +76 -13
  144. package/src/commands/complete.ts +1 -1
  145. package/src/commands/launch.ts +5 -1
  146. package/src/commands/read.ts +6 -3
  147. package/src/commands/usage.ts +35 -0
  148. package/src/commit/agentic/agent.ts +1 -1
  149. package/src/commit/model-selection.ts +4 -3
  150. package/src/config/api-key-resolver.ts +8 -6
  151. package/src/config/append-only-context-mode.ts +6 -12
  152. package/src/config/model-discovery.ts +554 -0
  153. package/src/config/model-registry.ts +320 -1041
  154. package/src/config/model-resolver.ts +173 -156
  155. package/src/config/model-roles.ts +74 -0
  156. package/src/config/models-config-schema.ts +57 -8
  157. package/src/config/models-config.ts +129 -0
  158. package/src/config/settings-schema.ts +61 -19
  159. package/src/config/settings.ts +98 -4
  160. package/src/dap/client.ts +124 -37
  161. package/src/dap/session.ts +259 -158
  162. package/src/debug/log-viewer.ts +1 -1
  163. package/src/debug/raw-sse.ts +1 -1
  164. package/src/edit/diff.ts +47 -3
  165. package/src/edit/hashline/block-resolver.ts +20 -1
  166. package/src/edit/hashline/diff.ts +36 -1
  167. package/src/edit/hashline/execute.ts +47 -4
  168. package/src/edit/hashline/noop-loop-guard.ts +99 -0
  169. package/src/edit/index.ts +16 -1
  170. package/src/edit/modes/patch.ts +52 -0
  171. package/src/edit/modes/replace.ts +56 -22
  172. package/src/edit/notebook.ts +22 -2
  173. package/src/edit/renderer.ts +36 -10
  174. package/src/eval/__tests__/completion-bridge.test.ts +1 -1
  175. package/src/eval/backend.ts +0 -2
  176. package/src/eval/completion-bridge.ts +3 -1
  177. package/src/eval/idle-timeout.ts +2 -9
  178. package/src/eval/js/context-manager.ts +6 -8
  179. package/src/eval/js/executor.ts +6 -2
  180. package/src/eval/js/index.ts +0 -2
  181. package/src/eval/js/shared/helpers.ts +5 -6
  182. package/src/eval/js/shared/local-module-loader.ts +1 -1
  183. package/src/eval/js/shared/prelude.txt +62 -1
  184. package/src/eval/js/shared/rewrite-imports.ts +40 -22
  185. package/src/eval/js/shared/runtime.ts +1 -1
  186. package/src/eval/py/executor.ts +29 -7
  187. package/src/eval/py/index.ts +6 -3
  188. package/src/eval/py/kernel.ts +43 -4
  189. package/src/eval/py/runner.py +107 -3
  190. package/src/eval/py/runtime.ts +37 -0
  191. package/src/exec/bash-executor.ts +85 -4
  192. package/src/export/html/template.generated.ts +1 -1
  193. package/src/export/html/template.js +3 -1
  194. package/src/extensibility/extensions/get-commands-handler.ts +2 -1
  195. package/src/extensibility/extensions/runner.ts +6 -1
  196. package/src/extensibility/extensions/types.ts +6 -2
  197. package/src/extensibility/plugins/legacy-pi-compat.ts +20 -3
  198. package/src/hindsight/bank.ts +17 -2
  199. package/src/hindsight/mental-models.ts +59 -12
  200. package/src/hindsight/state.ts +6 -1
  201. package/src/internal-urls/artifact-protocol.ts +11 -2
  202. package/src/internal-urls/docs-index.generated.ts +11 -11
  203. package/src/internal-urls/issue-pr-protocol.ts +12 -5
  204. package/src/internal-urls/router.ts +1 -1
  205. package/src/internal-urls/types.ts +1 -1
  206. package/src/lib/xai-http.ts +1 -1
  207. package/src/lsp/client.ts +118 -38
  208. package/src/lsp/clients/biome-client.ts +101 -39
  209. package/src/lsp/edits.ts +143 -95
  210. package/src/lsp/index.ts +31 -22
  211. package/src/lsp/render.ts +1 -1
  212. package/src/lsp/types.ts +2 -0
  213. package/src/lsp/utils.ts +28 -10
  214. package/src/main.ts +183 -23
  215. package/src/mcp/json-rpc.ts +35 -5
  216. package/src/mcp/transports/stdio.ts +7 -1
  217. package/src/memories/index.ts +4 -1
  218. package/src/memory-backend/index.ts +1 -0
  219. package/src/memory-backend/local-backend.ts +9 -0
  220. package/src/memory-backend/off-backend.ts +9 -0
  221. package/src/memory-backend/runtime.ts +66 -0
  222. package/src/memory-backend/types.ts +81 -1
  223. package/src/mnemopi/backend.ts +176 -7
  224. package/src/mnemopi/state.ts +38 -2
  225. package/src/modes/acp/acp-agent.ts +119 -11
  226. package/src/modes/components/agent-dashboard.ts +10 -7
  227. package/src/modes/components/assistant-message.ts +32 -28
  228. package/src/modes/components/bash-execution.ts +1 -1
  229. package/src/modes/components/copy-selector.ts +1 -1
  230. package/src/modes/components/diff.ts +13 -2
  231. package/src/modes/components/dynamic-border.ts +12 -3
  232. package/src/modes/components/extensions/extension-dashboard.ts +8 -5
  233. package/src/modes/components/extensions/extension-list.ts +1 -1
  234. package/src/modes/components/extensions/inspector-panel.ts +1 -1
  235. package/src/modes/components/footer.ts +4 -2
  236. package/src/modes/components/history-search.ts +1 -1
  237. package/src/modes/components/hook-editor.ts +8 -0
  238. package/src/modes/components/hook-input.ts +8 -0
  239. package/src/modes/components/hook-selector.ts +2 -2
  240. package/src/modes/components/model-selector.ts +4 -2
  241. package/src/modes/components/plan-review-overlay.ts +1 -1
  242. package/src/modes/components/session-observer-overlay.ts +2 -2
  243. package/src/modes/components/session-selector.ts +1 -1
  244. package/src/modes/components/settings-selector.ts +5 -1
  245. package/src/modes/components/status-line/component.ts +119 -35
  246. package/src/modes/components/tiny-title-download-progress.ts +1 -1
  247. package/src/modes/components/transcript-container.ts +258 -53
  248. package/src/modes/components/tree-selector.ts +3 -3
  249. package/src/modes/components/user-message-selector.ts +1 -1
  250. package/src/modes/components/user-message.ts +17 -5
  251. package/src/modes/components/visual-truncate.ts +1 -1
  252. package/src/modes/components/welcome.ts +108 -26
  253. package/src/modes/controllers/command-controller.ts +11 -4
  254. package/src/modes/controllers/event-controller.ts +73 -4
  255. package/src/modes/controllers/input-controller.ts +2 -1
  256. package/src/modes/controllers/mcp-command-controller.ts +39 -4
  257. package/src/modes/controllers/selector-controller.ts +1 -1
  258. package/src/modes/controllers/streaming-reveal.ts +85 -18
  259. package/src/modes/index.ts +3 -21
  260. package/src/modes/interactive-mode.ts +42 -18
  261. package/src/modes/oauth-manual-input.ts +30 -3
  262. package/src/modes/rpc/rpc-client.ts +154 -3
  263. package/src/modes/rpc/rpc-mode.ts +97 -12
  264. package/src/modes/rpc/rpc-subagents.ts +265 -0
  265. package/src/modes/rpc/rpc-types.ts +81 -1
  266. package/src/modes/setup-wizard/index.ts +12 -2
  267. package/src/modes/setup-wizard/lazy.ts +16 -0
  268. package/src/modes/setup-wizard/scenes/glyph.ts +1 -1
  269. package/src/modes/setup-wizard/scenes/providers.ts +1 -1
  270. package/src/modes/setup-wizard/scenes/sign-in.ts +1 -1
  271. package/src/modes/setup-wizard/scenes/theme.ts +1 -1
  272. package/src/modes/setup-wizard/scenes/types.ts +1 -1
  273. package/src/modes/setup-wizard/scenes/web-search.ts +1 -1
  274. package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
  275. package/src/modes/types.ts +4 -1
  276. package/src/prompts/agents/explore.md +2 -2
  277. package/src/prompts/agents/librarian.md +1 -2
  278. package/src/prompts/agents/oracle.md +1 -1
  279. package/src/prompts/agents/plan.md +5 -5
  280. package/src/prompts/agents/task.md +5 -5
  281. package/src/prompts/ci-green-request.md +5 -7
  282. package/src/prompts/goals/goal-budget-limit.md +2 -2
  283. package/src/prompts/goals/goal-continuation.md +4 -4
  284. package/src/prompts/goals/goal-mode-active.md +1 -1
  285. package/src/prompts/memories/read-path.md +1 -1
  286. package/src/prompts/memories/stage_one_system.md +2 -2
  287. package/src/prompts/review-custom-request.md +1 -1
  288. package/src/prompts/system/agent-creation-architect.md +2 -2
  289. package/src/prompts/system/auto-continue.md +1 -1
  290. package/src/prompts/system/background-tan-dispatch.md +1 -1
  291. package/src/prompts/system/btw-user.md +2 -2
  292. package/src/prompts/system/commit-message-system.md +13 -1
  293. package/src/prompts/system/custom-system-prompt.md +1 -1
  294. package/src/prompts/system/eager-todo.md +2 -2
  295. package/src/prompts/system/irc-incoming.md +1 -1
  296. package/src/prompts/system/manual-continue.md +1 -1
  297. package/src/prompts/system/omfg-user.md +3 -4
  298. package/src/prompts/system/orchestrate-notice.md +9 -9
  299. package/src/prompts/system/plan-mode-active.md +4 -4
  300. package/src/prompts/system/plan-mode-subagent.md +4 -5
  301. package/src/prompts/system/plan-mode-tool-decision-reminder.md +1 -1
  302. package/src/prompts/system/project-prompt.md +2 -2
  303. package/src/prompts/system/subagent-system-prompt.md +4 -4
  304. package/src/prompts/system/system-prompt.md +13 -24
  305. package/src/prompts/system/title-system.md +2 -2
  306. package/src/prompts/system/ttsr-tool-reminder.md +1 -1
  307. package/src/prompts/system/workflow-notice.md +1 -1
  308. package/src/prompts/tools/ast-edit.md +1 -1
  309. package/src/prompts/tools/ast-grep.md +2 -2
  310. package/src/prompts/tools/bash.md +5 -7
  311. package/src/prompts/tools/browser.md +7 -7
  312. package/src/prompts/tools/debug.md +1 -1
  313. package/src/prompts/tools/eval.md +3 -3
  314. package/src/prompts/tools/find.md +0 -1
  315. package/src/prompts/tools/github.md +8 -7
  316. package/src/prompts/tools/goal.md +1 -1
  317. package/src/prompts/tools/image-gen.md +1 -1
  318. package/src/prompts/tools/inspect-image-system.md +1 -1
  319. package/src/prompts/tools/irc.md +15 -15
  320. package/src/prompts/tools/lsp.md +2 -2
  321. package/src/prompts/tools/patch.md +2 -2
  322. package/src/prompts/tools/read.md +3 -4
  323. package/src/prompts/tools/recall.md +1 -1
  324. package/src/prompts/tools/reflect.md +1 -1
  325. package/src/prompts/tools/render-mermaid.md +2 -2
  326. package/src/prompts/tools/replace.md +4 -10
  327. package/src/prompts/tools/rewind.md +2 -2
  328. package/src/prompts/tools/search-tool-bm25.md +1 -9
  329. package/src/prompts/tools/search.md +0 -1
  330. package/src/prompts/tools/ssh.md +0 -4
  331. package/src/prompts/tools/task.md +2 -3
  332. package/src/prompts/tools/todo.md +1 -1
  333. package/src/sdk.ts +31 -11
  334. package/src/secrets/index.ts +8 -1
  335. package/src/secrets/obfuscator.ts +39 -18
  336. package/src/session/agent-session.ts +223 -64
  337. package/src/session/auth-broker-config.ts +30 -1
  338. package/src/session/session-manager.ts +2 -2
  339. package/src/session/streaming-output.ts +188 -11
  340. package/src/slash-commands/acp-builtins.ts +24 -0
  341. package/src/slash-commands/builtin-registry.ts +40 -0
  342. package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
  343. package/src/slash-commands/types.ts +1 -1
  344. package/src/ssh/connection-manager.ts +27 -0
  345. package/src/system-prompt.ts +14 -0
  346. package/src/task/commands.ts +2 -1
  347. package/src/task/executor.ts +74 -65
  348. package/src/task/index.ts +146 -68
  349. package/src/task/parallel.ts +3 -3
  350. package/src/task/render.ts +20 -5
  351. package/src/task/types.ts +9 -0
  352. package/src/task/worktree.ts +64 -56
  353. package/src/thinking.ts +9 -1
  354. package/src/tiny/title-client.ts +60 -16
  355. package/src/tiny/title-protocol.ts +1 -1
  356. package/src/tiny/worker.ts +6 -4
  357. package/src/tools/archive-reader.ts +30 -2
  358. package/src/tools/ask.ts +104 -21
  359. package/src/tools/ast-edit.ts +25 -5
  360. package/src/tools/auto-generated-guard.ts +20 -3
  361. package/src/tools/bash-interactive.ts +27 -7
  362. package/src/tools/bash.ts +100 -18
  363. package/src/tools/browser/launch.ts +11 -2
  364. package/src/tools/browser/readable.ts +19 -2
  365. package/src/tools/browser/registry.ts +4 -1
  366. package/src/tools/browser/render.ts +2 -2
  367. package/src/tools/browser/tab-supervisor.ts +55 -16
  368. package/src/tools/conflict-detect.ts +50 -4
  369. package/src/tools/debug.ts +1 -1
  370. package/src/tools/eval-render.ts +5 -5
  371. package/src/tools/eval.ts +0 -2
  372. package/src/tools/fetch.ts +33 -10
  373. package/src/tools/gh-cache-invalidation.ts +63 -8
  374. package/src/tools/gh-renderer.ts +1 -1
  375. package/src/tools/gh.ts +172 -29
  376. package/src/tools/github-cache.ts +70 -6
  377. package/src/tools/image-gen.ts +14 -13
  378. package/src/tools/index.ts +13 -1
  379. package/src/tools/inspect-image.ts +1 -0
  380. package/src/tools/irc.ts +5 -1
  381. package/src/tools/job.ts +1 -1
  382. package/src/tools/read.ts +202 -61
  383. package/src/tools/render-utils.ts +3 -3
  384. package/src/tools/resolve.ts +1 -1
  385. package/src/tools/search.ts +92 -29
  386. package/src/tools/sqlite-reader.ts +17 -5
  387. package/src/tools/ssh.ts +8 -8
  388. package/src/tools/todo.ts +38 -8
  389. package/src/tools/write.ts +118 -18
  390. package/src/tui/output-block.ts +4 -4
  391. package/src/utils/changelog.ts +27 -1
  392. package/src/utils/commit-message-generator.ts +1 -0
  393. package/src/utils/file-mentions.ts +2 -1
  394. package/src/utils/git.ts +267 -13
  395. package/src/utils/title-generator.ts +24 -5
  396. package/src/web/scrapers/arxiv.ts +1 -1
  397. package/src/web/scrapers/go-pkg.ts +1 -1
  398. package/src/web/scrapers/iacr.ts +1 -1
  399. package/src/web/scrapers/readthedocs.ts +1 -1
  400. package/src/web/scrapers/twitter.ts +2 -1
  401. package/src/web/scrapers/types.ts +87 -8
  402. package/src/web/scrapers/wikipedia.ts +1 -1
  403. package/src/web/scrapers/youtube.ts +6 -1
  404. package/src/web/search/index.ts +1 -1
  405. package/src/web/search/providers/codex.ts +2 -1
  406. package/src/web/search/providers/gemini.ts +2 -3
  407. package/src/web/search/render.ts +8 -6
  408. package/dist/types/config/model-equivalence.d.ts +0 -24
  409. package/dist/types/config/model-id-affixes.d.ts +0 -12
  410. package/dist/types/config/model-provider-priority.d.ts +0 -1
  411. package/dist/types/exec/idle-timeout-watchdog.d.ts +0 -18
  412. package/src/config/model-equivalence.ts +0 -875
  413. package/src/config/model-id-affixes.ts +0 -81
  414. package/src/config/model-provider-priority.ts +0 -56
  415. package/src/exec/idle-timeout-watchdog.ts +0 -126
@@ -79,14 +79,14 @@ import {
79
79
  clearAnthropicFastModeFallback,
80
80
  deriveClaudeDeviceId,
81
81
  Effort,
82
- getSupportedEfforts,
83
82
  isContextOverflow,
84
83
  isUsageLimitError,
85
- modelsAreEqual,
86
84
  parseRateLimitReason,
87
85
  resolveServiceTier,
88
86
  streamSimple,
89
87
  } from "@oh-my-pi/pi-ai";
88
+ import { getSupportedEfforts } from "@oh-my-pi/pi-catalog/model-thinking";
89
+ import { modelsAreEqual } from "@oh-my-pi/pi-catalog/models";
90
90
  import { countTokens, MacOSPowerAssertion } from "@oh-my-pi/pi-natives";
91
91
  import {
92
92
  extractRetryHint,
@@ -105,9 +105,10 @@ import { classifyDifficulty } from "../auto-thinking/classifier";
105
105
  import { reset as resetCapabilities } from "../capability";
106
106
  import type { Rule } from "../capability/rule";
107
107
  import { shouldEnableAppendOnlyContext } from "../config/append-only-context-mode";
108
- import { MODEL_ROLE_IDS, type ModelRegistry } from "../config/model-registry";
108
+ import type { ModelRegistry } from "../config/model-registry";
109
109
  import {
110
110
  extractExplicitThinkingSelector,
111
+ filterAvailableModelsByEnabledPatterns,
111
112
  formatModelSelectorValue,
112
113
  formatModelString,
113
114
  getModelMatchPreferences,
@@ -115,6 +116,7 @@ import {
115
116
  type ResolvedModelRoleValue,
116
117
  resolveModelRoleValue,
117
118
  } from "../config/model-resolver";
119
+ import { MODEL_ROLE_IDS } from "../config/model-roles";
118
120
  import { expandPromptTemplate, type PromptTemplate } from "../config/prompt-templates";
119
121
  import type { Settings, SkillsSettings } from "../config/settings";
120
122
  import { onAppendOnlyModeChanged } from "../config/settings";
@@ -183,7 +185,12 @@ import planModeToolDecisionReminderPrompt from "../prompts/system/plan-mode-tool
183
185
  import ttsrInterruptTemplate from "../prompts/system/ttsr-interrupt.md" with { type: "text" };
184
186
  import ttsrToolReminderTemplate from "../prompts/system/ttsr-tool-reminder.md" with { type: "text" };
185
187
  import { type AgentRegistry, MAIN_AGENT_ID } from "../registry/agent-registry";
186
- import { deobfuscateSessionContext, type SecretObfuscator } from "../secrets/obfuscator";
188
+ import {
189
+ deobfuscateSessionContext,
190
+ obfuscateProviderContext,
191
+ obfuscateProviderTools,
192
+ type SecretObfuscator,
193
+ } from "../secrets/obfuscator";
187
194
  import { invalidateHostMetadata } from "../ssh/connection-manager";
188
195
  import {
189
196
  AUTO_THINKING,
@@ -191,6 +198,7 @@ import {
191
198
  clampAutoThinkingEffort,
192
199
  resolveProvisionalAutoLevel,
193
200
  resolveThinkingLevelForModel,
201
+ shouldDisableReasoning,
194
202
  toReasoningEffort,
195
203
  } from "../thinking";
196
204
  import { shutdownTinyTitleClient } from "../tiny/title-client";
@@ -287,6 +295,20 @@ export type AgentSessionEventListener = (event: AgentSessionEvent) => void;
287
295
  export type AsyncJobSnapshotItem = Pick<AsyncJob, "id" | "type" | "status" | "label" | "startTime">;
288
296
 
289
297
  const EMPTY_STOP_MAX_RETRIES = 3;
298
+ const RETRY_BACKOFF_MAX_DELAY_MS = 8_000;
299
+ const RETRY_BACKOFF_JITTER_RATIO = 0.25;
300
+
301
+ function calculateRetryBackoffDelayMs(baseDelayMs: number, attempt: number): number {
302
+ const cappedDelayMs = Math.min(Math.max(0, baseDelayMs) * 2 ** Math.max(0, attempt - 1), RETRY_BACKOFF_MAX_DELAY_MS);
303
+ const jitter = 1 - Math.random() * RETRY_BACKOFF_JITTER_RATIO;
304
+ return cappedDelayMs * jitter;
305
+ }
306
+
307
+ /**
308
+ * Slack added past a sibling credential's block expiry before retrying, so
309
+ * the next getApiKey lands after the block has actually lapsed.
310
+ */
311
+ const SIBLING_UNBLOCK_BUFFER_MS = 1_000;
290
312
  const NON_WHITESPACE_RE = /\S/;
291
313
 
292
314
  function hasNonWhitespace(value: string): boolean {
@@ -309,6 +331,8 @@ export interface AgentSessionConfig {
309
331
  agent: Agent;
310
332
  sessionManager: SessionManager;
311
333
  settings: Settings;
334
+ /** Whether the caller explicitly requested yolo/auto-approve behavior for this session. */
335
+ autoApprove?: boolean;
312
336
  /** Models to cycle through with Ctrl+P (from --models flag) */
313
337
  scopedModels?: Array<{ model: Model; thinkingLevel?: ThinkingLevel }>;
314
338
  /** Initial session thinking selector. */
@@ -824,6 +848,7 @@ export class AgentSession {
824
848
  readonly settings: Settings;
825
849
  readonly yieldQueue: YieldQueue;
826
850
  fileSnapshotStore?: InMemorySnapshotStore;
851
+ #autoApprove: boolean;
827
852
 
828
853
  #powerAssertion: MacOSPowerAssertion | undefined;
829
854
 
@@ -1103,6 +1128,7 @@ export class AgentSession {
1103
1128
  this.agent = config.agent;
1104
1129
  this.sessionManager = config.sessionManager;
1105
1130
  this.settings = config.settings;
1131
+ this.#autoApprove = config.autoApprove === true;
1106
1132
  // Power assertions are taken per turn (see #beginInFlight); nothing acquired here.
1107
1133
  this.#evalKernelOwnerId = config.evalKernelOwnerId ?? `agent-session:${Snowflake.next()}`;
1108
1134
  this.#parentEvalSessionId = config.parentEvalSessionId;
@@ -1118,6 +1144,7 @@ export class AgentSession {
1118
1144
  } else {
1119
1145
  this.#thinkingLevel = config.thinkingLevel;
1120
1146
  }
1147
+ this.#applyThinkingLevelToAgent(this.#thinkingLevel);
1121
1148
  this.#promptTemplates = config.promptTemplates ?? [];
1122
1149
  this.#slashCommands = config.slashCommands ?? [];
1123
1150
  this.#extensionRunner = config.extensionRunner;
@@ -3514,12 +3541,26 @@ export class AgentSession {
3514
3541
  * Wrap a tool with a permission-gate proxy when an ACP client is connected.
3515
3542
  * Only wraps tools whose name is in PERMISSION_REQUIRED_TOOLS and only when
3516
3543
  * the bridge exposes `requestPermission`. No-ops for all other cases.
3544
+ *
3545
+ * When the user has explicitly opted into `yolo` / auto-approve behavior (via
3546
+ * the SDK/CLI `autoApprove` flag or a configured `tools.approvalMode: yolo`),
3547
+ * skips the gate unless the per-tool policy explicitly requires a prompt or
3548
+ * deny. The schema default is also `yolo`, so an explicit configuration or
3549
+ * explicit session flag is required: default-config ACP sessions keep the
3550
+ * client-side permission gate.
3517
3551
  */
3518
3552
  #wrapToolForAcpPermission<T extends AgentTool>(tool: T): T {
3519
3553
  const bridge = this.#clientBridge;
3520
3554
  // Match the capability+method gating pattern used by read/write/bash.
3521
3555
  if (!bridge?.capabilities.requestPermission || !bridge.requestPermission) return tool;
3522
3556
  if (!PERMISSION_REQUIRED_TOOLS.has(tool.name)) return tool;
3557
+ // Skip the gate only on explicit yolo opt-in; honour per-tool policies
3558
+ // that require a prompt or deny (matching the normal approval wrapper).
3559
+ if (this.#isExplicitAutoApproveMode()) {
3560
+ const userPolicies = (this.settings.get("tools.approval") ?? {}) as Record<string, unknown>;
3561
+ const toolPolicy = userPolicies[tool.name];
3562
+ if (!toolPolicy || toolPolicy === "allow") return tool;
3563
+ }
3523
3564
  return new Proxy(tool, {
3524
3565
  get: (target, prop) => {
3525
3566
  if (prop !== "execute") return Reflect.get(target, prop, target);
@@ -3607,6 +3648,13 @@ export class AgentSession {
3607
3648
  }) as T;
3608
3649
  }
3609
3650
 
3651
+ #isExplicitAutoApproveMode(): boolean {
3652
+ return (
3653
+ this.#autoApprove ||
3654
+ (this.settings.isConfigured("tools.approvalMode") && this.settings.get("tools.approvalMode") === "yolo")
3655
+ );
3656
+ }
3657
+
3610
3658
  async #applyActiveToolsByName(
3611
3659
  toolNames: string[],
3612
3660
  options?: { persistMCPSelection?: boolean; previousSelectedMCPToolNames?: string[] },
@@ -3984,6 +4032,47 @@ export class AgentSession {
3984
4032
  return deobfuscateSessionContext(this.sessionManager.buildSessionContext(), this.#obfuscator);
3985
4033
  }
3986
4034
 
4035
+ #obfuscateForProvider<T>(value: T): T {
4036
+ if (!this.#obfuscator?.hasSecrets()) return value;
4037
+ return this.#obfuscator.obfuscateObject(value);
4038
+ }
4039
+
4040
+ #obfuscateTextForProvider(text: string | undefined): string | undefined {
4041
+ if (!text || !this.#obfuscator?.hasSecrets()) return text;
4042
+ return this.#obfuscator.obfuscate(text);
4043
+ }
4044
+
4045
+ #obfuscatePreparationForProvider(preparation: CompactionPreparation): CompactionPreparation {
4046
+ if (!this.#obfuscator?.hasSecrets()) return preparation;
4047
+ if (!preparation.previousSummary && !preparation.previousPreserveData) return preparation;
4048
+ return {
4049
+ ...preparation,
4050
+ previousSummary: preparation.previousSummary
4051
+ ? this.#obfuscator.obfuscate(preparation.previousSummary)
4052
+ : preparation.previousSummary,
4053
+ previousPreserveData: preparation.previousPreserveData
4054
+ ? this.#obfuscator.obfuscateObject(preparation.previousPreserveData)
4055
+ : preparation.previousPreserveData,
4056
+ };
4057
+ }
4058
+
4059
+ #deobfuscateFromProvider(text: string): string {
4060
+ if (!this.#obfuscator?.hasSecrets()) return text;
4061
+ return this.#obfuscator.deobfuscate(text);
4062
+ }
4063
+
4064
+ #deobfuscatedProviderTextReadyForDelta(text: string): string {
4065
+ const deobfuscated = this.#deobfuscateFromProvider(text);
4066
+ if (!this.#obfuscator?.hasSecrets()) return deobfuscated;
4067
+ const pendingPlaceholderStart = deobfuscated.match(/#[A-Z0-9]{0,4}$/);
4068
+ if (pendingPlaceholderStart?.index === undefined) return deobfuscated;
4069
+ return deobfuscated.slice(0, pendingPlaceholderStart.index);
4070
+ }
4071
+
4072
+ #convertToLlmForSideRequest(messages: AgentMessage[]): Message[] {
4073
+ return this.#obfuscateForProvider(convertToLlm(messages));
4074
+ }
4075
+
3987
4076
  /** Convert session messages using the same pre-LLM pipeline as the active session. */
3988
4077
  async convertMessagesToLlm(messages: AgentMessage[], signal?: AbortSignal): Promise<Message[]> {
3989
4078
  const transformedMessages = await this.#transformContext(messages, signal);
@@ -4383,21 +4472,28 @@ export class AgentSession {
4383
4472
  * @throws Error if streaming and no streamingBehavior specified
4384
4473
  * @throws Error if no model selected or no API key available (when not streaming)
4385
4474
  */
4386
- async prompt(text: string, options?: PromptOptions): Promise<void> {
4475
+ /**
4476
+ * Returns `false` when the command was fully handled locally (extension or
4477
+ * custom-TS command consumed without calling the LLM). Returns `true` when
4478
+ * the prompt was forwarded to the agent — either directly or queued as a
4479
+ * steer/follow-up. Callers that render a UI or manage turn lifecycle (e.g.
4480
+ * the ACP agent) use this to know whether to expect an `agent_end` event.
4481
+ */
4482
+ async prompt(text: string, options?: PromptOptions): Promise<boolean> {
4387
4483
  const expandPromptTemplates = options?.expandPromptTemplates ?? true;
4388
4484
 
4389
4485
  // Handle extension commands first (execute immediately, even during streaming)
4390
4486
  if (expandPromptTemplates && text.startsWith("/")) {
4391
4487
  const handled = await this.#tryExecuteExtensionCommand(text);
4392
4488
  if (handled) {
4393
- return;
4489
+ return false;
4394
4490
  }
4395
4491
 
4396
4492
  // Try custom commands (TypeScript slash commands)
4397
4493
  const customResult = await this.#tryExecuteCustomCommand(text);
4398
4494
  if (customResult !== null) {
4399
4495
  if (customResult === "") {
4400
- return;
4496
+ return false;
4401
4497
  }
4402
4498
  text = customResult;
4403
4499
  }
@@ -4431,7 +4527,7 @@ export class AgentSession {
4431
4527
  for (const notice of keywordNotices) {
4432
4528
  await this.sendCustomMessage(notice, { deliverAs: options.streamingBehavior });
4433
4529
  }
4434
- return;
4530
+ return true;
4435
4531
  }
4436
4532
 
4437
4533
  // Skip eager todo prelude when the user has already queued a directive
@@ -4471,6 +4567,7 @@ export class AgentSession {
4471
4567
  if (!options?.synthetic) {
4472
4568
  await this.#enforcePlanModeToolDecision();
4473
4569
  }
4570
+ return true;
4474
4571
  }
4475
4572
 
4476
4573
  async promptCustomMessage<T = unknown>(
@@ -5679,16 +5776,25 @@ export class AgentSession {
5679
5776
  }
5680
5777
 
5681
5778
  /**
5682
- * Get all available models with valid API keys.
5779
+ * Get all available models with valid API keys, filtered by `enabledModels` when configured.
5780
+ * See {@link filterAvailableModelsByEnabledPatterns} for supported pattern forms and limitations.
5683
5781
  */
5684
5782
  getAvailableModels(): Model[] {
5685
- return this.#modelRegistry.getAvailable();
5783
+ const all = this.#modelRegistry.getAvailable();
5784
+ const patterns = this.settings.get("enabledModels");
5785
+ if (!patterns || patterns.length === 0) return all;
5786
+ return filterAvailableModelsByEnabledPatterns(all, patterns, this.#modelRegistry);
5686
5787
  }
5687
5788
 
5688
5789
  // =========================================================================
5689
5790
  // Thinking Level Management
5690
5791
  // =========================================================================
5691
5792
 
5793
+ #applyThinkingLevelToAgent(level: ThinkingLevel | undefined): void {
5794
+ this.agent.setThinkingLevel(toReasoningEffort(level));
5795
+ this.agent.setDisableReasoning(shouldDisableReasoning(level));
5796
+ }
5797
+
5692
5798
  /**
5693
5799
  * Set the thinking level. `auto` enables per-turn classification; the selector
5694
5800
  * itself is never written to the session log, but resolved concrete levels are
@@ -5702,7 +5808,7 @@ export class AgentSession {
5702
5808
  this.#autoThinking = true;
5703
5809
  this.#autoResolvedLevel = undefined;
5704
5810
  this.#thinkingLevel = provisional;
5705
- this.agent.setThinkingLevel(toReasoningEffort(provisional));
5811
+ this.#applyThinkingLevelToAgent(provisional);
5706
5812
  if (persist) {
5707
5813
  this.settings.set("defaultThinkingLevel", AUTO_THINKING);
5708
5814
  }
@@ -5718,7 +5824,7 @@ export class AgentSession {
5718
5824
  const isChanging = effectiveLevel !== this.#thinkingLevel;
5719
5825
 
5720
5826
  this.#thinkingLevel = effectiveLevel;
5721
- this.agent.setThinkingLevel(toReasoningEffort(effectiveLevel));
5827
+ this.#applyThinkingLevelToAgent(effectiveLevel);
5722
5828
 
5723
5829
  if (isChanging) {
5724
5830
  this.sessionManager.appendThinkingLevelChange(effectiveLevel);
@@ -5808,7 +5914,7 @@ export class AgentSession {
5808
5914
  const shouldPersistResolution = this.#autoResolvedLevel !== effort;
5809
5915
  this.#autoResolvedLevel = effort;
5810
5916
  this.#thinkingLevel = effort;
5811
- this.agent.setThinkingLevel(toReasoningEffort(effort));
5917
+ this.#applyThinkingLevelToAgent(effort);
5812
5918
  if (shouldPersistResolution) {
5813
5919
  this.sessionManager.appendThinkingLevelChange(effort);
5814
5920
  }
@@ -6162,10 +6268,10 @@ export class AgentSession {
6162
6268
  customInstructions,
6163
6269
  compactionAbortController.signal,
6164
6270
  {
6165
- promptOverride: compactionPrep.hookPrompt,
6166
- extraContext: compactionPrep.hookContext,
6167
- remoteInstructions: this.#baseSystemPrompt.join("\n\n"),
6168
- convertToLlm,
6271
+ promptOverride: this.#obfuscateTextForProvider(compactionPrep.hookPrompt),
6272
+ extraContext: this.#obfuscateForProvider(compactionPrep.hookContext),
6273
+ remoteInstructions: this.#obfuscateForProvider(this.#baseSystemPrompt.join("\n\n")),
6274
+ convertToLlm: messages => this.#convertToLlmForSideRequest(messages),
6169
6275
  },
6170
6276
  );
6171
6277
  summary = result.summary;
@@ -6348,15 +6454,15 @@ export class AgentSession {
6348
6454
  throw new Error(`No API key for ${model.provider}`);
6349
6455
  }
6350
6456
 
6351
- const handoffText = await generateHandoff(
6457
+ const rawHandoffText = await generateHandoff(
6352
6458
  this.agent.state.messages,
6353
6459
  model,
6354
6460
  apiKey,
6355
6461
  {
6356
- systemPrompt: this.#baseSystemPrompt,
6357
- tools: this.agent.state.tools,
6358
- customInstructions,
6359
- convertToLlm,
6462
+ systemPrompt: this.#obfuscateForProvider(this.#baseSystemPrompt),
6463
+ tools: obfuscateProviderTools(this.#obfuscator, this.agent.state.tools),
6464
+ customInstructions: this.#obfuscateTextForProvider(customInstructions),
6465
+ convertToLlm: messages => this.#convertToLlmForSideRequest(messages),
6360
6466
  initiatorOverride: "agent",
6361
6467
  metadata: this.agent.metadataForProvider(model.provider),
6362
6468
  telemetry: resolveTelemetry(this.agent.telemetry, this.sessionId),
@@ -6368,6 +6474,7 @@ export class AgentSession {
6368
6474
  },
6369
6475
  handoffSignal,
6370
6476
  );
6477
+ const handoffText = this.#deobfuscateFromProvider(rawHandoffText);
6371
6478
 
6372
6479
  if (handoffSignal.aborted) {
6373
6480
  throw new Error("Handoff cancelled");
@@ -7329,17 +7436,24 @@ export class AgentSession {
7329
7436
  if (!apiKey) continue;
7330
7437
 
7331
7438
  try {
7332
- return await compact(preparation, candidate, apiKey, customInstructions, signal, {
7333
- ...options,
7334
- metadata: this.agent.metadataForProvider(candidate.provider),
7335
- convertToLlm,
7336
- telemetry,
7337
- // Honor the user's /model thinking selection (incl. `off`) on
7338
- // the manual `/compact` path. Clamped per-model inside compact()
7339
- // via resolveCompactionEffort so unsupported-effort models
7340
- // (xai-oauth/grok-build) don't trip requireSupportedEffort.
7341
- thinkingLevel: this.thinkingLevel,
7342
- });
7439
+ return await compact(
7440
+ this.#obfuscatePreparationForProvider(preparation),
7441
+ candidate,
7442
+ apiKey,
7443
+ this.#obfuscateTextForProvider(customInstructions),
7444
+ signal,
7445
+ {
7446
+ ...options,
7447
+ metadata: this.agent.metadataForProvider(candidate.provider),
7448
+ convertToLlm: messages => this.#convertToLlmForSideRequest(messages),
7449
+ telemetry,
7450
+ // Honor the user's /model thinking selection (incl. `off`) on
7451
+ // the manual `/compact` path. Clamped per-model inside compact()
7452
+ // via resolveCompactionEffort so unsupported-effort models
7453
+ // (xai-oauth/grok-build) don't trip requireSupportedEffort.
7454
+ thinkingLevel: this.thinkingLevel,
7455
+ },
7456
+ );
7343
7457
  } catch (error) {
7344
7458
  if (!this.#isCompactionAuthFailure(error)) {
7345
7459
  throw error;
@@ -7619,20 +7733,27 @@ export class AgentSession {
7619
7733
  let attempt = 0;
7620
7734
  while (true) {
7621
7735
  try {
7622
- compactResult = await compact(preparation, candidate, apiKey, undefined, autoCompactionSignal, {
7623
- promptOverride: compactionPrep.hookPrompt,
7624
- extraContext: compactionPrep.hookContext,
7625
- remoteInstructions: this.#baseSystemPrompt.join("\n\n"),
7626
- metadata: this.agent.metadataForProvider(candidate.provider),
7627
- initiatorOverride: "agent",
7628
- convertToLlm,
7629
- telemetry,
7630
- // Honor the user's /model thinking selection on the
7631
- // auto-compaction path — the most-fired compaction
7632
- // site. Clamped per-model inside compact() via
7633
- // resolveCompactionEffort.
7634
- thinkingLevel: this.thinkingLevel,
7635
- });
7736
+ compactResult = await compact(
7737
+ this.#obfuscatePreparationForProvider(preparation),
7738
+ candidate,
7739
+ apiKey,
7740
+ undefined,
7741
+ autoCompactionSignal,
7742
+ {
7743
+ promptOverride: this.#obfuscateTextForProvider(compactionPrep.hookPrompt),
7744
+ extraContext: this.#obfuscateForProvider(compactionPrep.hookContext),
7745
+ remoteInstructions: this.#obfuscateForProvider(this.#baseSystemPrompt.join("\n\n")),
7746
+ metadata: this.agent.metadataForProvider(candidate.provider),
7747
+ initiatorOverride: "agent",
7748
+ convertToLlm: messages => this.#convertToLlmForSideRequest(messages),
7749
+ telemetry,
7750
+ // Honor the user's /model thinking selection on the
7751
+ // auto-compaction path — the most-fired compaction
7752
+ // site. Clamped per-model inside compact() via
7753
+ // resolveCompactionEffort.
7754
+ thinkingLevel: this.thinkingLevel,
7755
+ },
7756
+ );
7636
7757
  break;
7637
7758
  } catch (error) {
7638
7759
  if (autoCompactionSignal.aborted) {
@@ -8307,26 +8428,46 @@ export class AgentSession {
8307
8428
 
8308
8429
  const errorMessage = message.errorMessage || "Unknown error";
8309
8430
  const parsedRetryAfterMs = this.#parseRetryAfterMsFromError(errorMessage);
8310
- let delayMs = retrySettings.baseDelayMs * 2 ** (this.#retryAttempt - 1);
8431
+ let delayMs = calculateRetryBackoffDelayMs(retrySettings.baseDelayMs, this.#retryAttempt);
8311
8432
  let switchedCredential = false;
8312
8433
  let switchedModel = false;
8434
+ // Set when a usage-limit error pinned the wait to credential
8435
+ // availability — suppresses the generic retry-after bump below.
8436
+ let usageLimitWaitMs: number | undefined;
8313
8437
 
8314
8438
  if (this.model && isUsageLimitError(errorMessage)) {
8315
8439
  const retryAfterMs = parsedRetryAfterMs ?? calculateRateLimitBackoffMs(parseRateLimitReason(errorMessage));
8316
- const switched = await this.#modelRegistry.authStorage.markUsageLimitReached(
8440
+ const outcome = await this.#modelRegistry.authStorage.markUsageLimitReached(
8317
8441
  this.model.provider,
8318
8442
  this.sessionId,
8319
8443
  {
8320
8444
  retryAfterMs,
8321
8445
  baseUrl: this.model.baseUrl,
8446
+ modelId: this.model.id,
8322
8447
  },
8323
8448
  );
8324
- if (switched) {
8449
+ if (outcome.switched) {
8325
8450
  switchedCredential = true;
8326
8451
  delayMs = 0;
8327
- } else if (retryAfterMs > delayMs) {
8328
- // No more accounts to switch to wait out the backoff
8329
- delayMs = retryAfterMs;
8452
+ } else {
8453
+ // No sibling credential is usable right now. Wait for whichever
8454
+ // comes first: the provider's retry-after window for the current
8455
+ // account, or the earliest moment a temporarily blocked sibling
8456
+ // frees up (e.g. a 60s post-401 block or a 5-min usage-probe
8457
+ // block) — the next attempt's getApiKey re-ranks and picks it up.
8458
+ // Without this, one short-lived sibling block escalates a
8459
+ // recoverable situation into the provider's multi-hour wait and
8460
+ // trips the fail-fast cap below.
8461
+ usageLimitWaitMs = retryAfterMs;
8462
+ if (outcome.retryAtMs !== undefined) {
8463
+ const siblingWaitMs = Math.max(0, outcome.retryAtMs - Date.now()) + SIBLING_UNBLOCK_BUFFER_MS;
8464
+ if (siblingWaitMs < usageLimitWaitMs) {
8465
+ usageLimitWaitMs = siblingWaitMs;
8466
+ }
8467
+ }
8468
+ if (usageLimitWaitMs > delayMs) {
8469
+ delayMs = usageLimitWaitMs;
8470
+ }
8330
8471
  }
8331
8472
  }
8332
8473
 
@@ -8338,7 +8479,7 @@ export class AgentSession {
8338
8479
  }
8339
8480
  if (switchedModel) {
8340
8481
  delayMs = 0;
8341
- } else if (parsedRetryAfterMs && parsedRetryAfterMs > delayMs) {
8482
+ } else if (usageLimitWaitMs === undefined && parsedRetryAfterMs && parsedRetryAfterMs > delayMs) {
8342
8483
  delayMs = parsedRetryAfterMs;
8343
8484
  }
8344
8485
  }
@@ -8499,11 +8640,12 @@ export class AgentSession {
8499
8640
  * @param command The bash command to execute
8500
8641
  * @param onChunk Optional streaming callback for output
8501
8642
  * @param options.excludeFromContext If true, command output won't be sent to LLM (!! prefix)
8643
+ * @param options.useUserShell If true, allow caller to request configured user-shell routing
8502
8644
  */
8503
8645
  async executeBash(
8504
8646
  command: string,
8505
8647
  onChunk?: (chunk: string) => void,
8506
- options?: { excludeFromContext?: boolean },
8648
+ options?: { excludeFromContext?: boolean; useUserShell?: boolean },
8507
8649
  ): Promise<BashResult> {
8508
8650
  const excludeFromContext = options?.excludeFromContext === true;
8509
8651
  const cwd = this.sessionManager.getCwd();
@@ -8531,6 +8673,7 @@ export class AgentSession {
8531
8673
  sessionKey: this.sessionId,
8532
8674
  timeout: clampTimeout("bash") * 1000,
8533
8675
  onMinimizedSave: originalText => this.#saveBashOriginalArtifact(originalText),
8676
+ useUserShell: options?.useUserShell,
8534
8677
  });
8535
8678
 
8536
8679
  this.recordBashResult(command, result, options);
@@ -8656,6 +8799,7 @@ export class AgentSession {
8656
8799
  sessionId: namespacePythonSessionId(sessionId),
8657
8800
  kernelOwnerId: this.#evalKernelOwnerId,
8658
8801
  kernelMode: this.settings.get("python.kernelMode"),
8802
+ interpreter: this.settings.get("python.interpreter")?.trim() || undefined,
8659
8803
  onChunk,
8660
8804
  signal: abortController.signal,
8661
8805
  });
@@ -8948,6 +9092,7 @@ export class AgentSession {
8948
9092
  promptCacheKey: cacheSessionId,
8949
9093
  preferWebsockets: false,
8950
9094
  reasoning: toReasoningEffort(this.thinkingLevel),
9095
+ disableReasoning: shouldDisableReasoning(this.thinkingLevel),
8951
9096
  hideThinkingSummary: this.agent.hideThinkingSummary,
8952
9097
  serviceTier: this.serviceTier,
8953
9098
  signal: args.signal,
@@ -8956,17 +9101,27 @@ export class AgentSession {
8956
9101
  model.provider,
8957
9102
  );
8958
9103
 
8959
- let replyText = "";
9104
+ let providerReplyText = "";
9105
+ let emittedReplyText = "";
8960
9106
  let assistantMessage: AssistantMessage | undefined;
8961
- const stream = streamSimple(model, context, options);
9107
+ const stream = streamSimple(model, obfuscateProviderContext(this.#obfuscator, context), options);
8962
9108
  for await (const event of stream) {
8963
9109
  if (event.type === "text_delta") {
8964
- replyText += event.delta;
8965
- if (args.onTextDelta) args.onTextDelta(event.delta);
9110
+ providerReplyText += event.delta;
9111
+ if (args.onTextDelta) {
9112
+ const readyText = this.#deobfuscatedProviderTextReadyForDelta(providerReplyText);
9113
+ if (readyText.length > emittedReplyText.length) {
9114
+ const delta = readyText.slice(emittedReplyText.length);
9115
+ emittedReplyText = readyText;
9116
+ args.onTextDelta(delta);
9117
+ }
9118
+ }
8966
9119
  continue;
8967
9120
  }
8968
9121
  if (event.type === "done") {
8969
- assistantMessage = event.message;
9122
+ assistantMessage = this.#obfuscator?.hasSecrets()
9123
+ ? { ...event.message, content: this.#obfuscator.deobfuscateObject(event.message.content) }
9124
+ : event.message;
8970
9125
  break;
8971
9126
  }
8972
9127
  if (event.type === "error") {
@@ -8977,6 +9132,10 @@ export class AgentSession {
8977
9132
  if (!assistantMessage) {
8978
9133
  throw new Error("Ephemeral turn ended without a final message");
8979
9134
  }
9135
+ const replyText = this.#deobfuscateFromProvider(providerReplyText);
9136
+ if (args.onTextDelta && replyText.length > emittedReplyText.length) {
9137
+ args.onTextDelta(replyText.slice(emittedReplyText.length));
9138
+ }
8980
9139
  return {
8981
9140
  replyText: args.dedupeReply === false ? replyText.trim() : dedupeIrcReply(replyText.trim()),
8982
9141
  assistantMessage,
@@ -9236,7 +9395,7 @@ export class AgentSession {
9236
9395
  this.#autoResolvedLevel = undefined;
9237
9396
  this.#thinkingLevel = resolveThinkingLevelForModel(this.model, restoredThinkingLevel);
9238
9397
  }
9239
- this.agent.setThinkingLevel(toReasoningEffort(this.#thinkingLevel));
9398
+ this.#applyThinkingLevelToAgent(this.#thinkingLevel);
9240
9399
  this.agent.serviceTier = hasServiceTierEntry
9241
9400
  ? sessionContext.serviceTier
9242
9401
  : configuredServiceTier === "none"
@@ -9293,7 +9452,7 @@ export class AgentSession {
9293
9452
  this.#thinkingLevel = previousThinkingLevel;
9294
9453
  this.#autoThinking = previousAutoThinking;
9295
9454
  this.#autoResolvedLevel = previousAutoResolvedLevel;
9296
- this.agent.setThinkingLevel(toReasoningEffort(previousThinkingLevel));
9455
+ this.#applyThinkingLevelToAgent(previousThinkingLevel);
9297
9456
  this.agent.serviceTier = previousServiceTier;
9298
9457
  this.#syncTodoPhasesFromBranch();
9299
9458
  this.#reconnectToAgent();
@@ -9477,10 +9636,10 @@ export class AgentSession {
9477
9636
  model,
9478
9637
  apiKey,
9479
9638
  signal: this.#branchSummaryAbortController.signal,
9480
- customInstructions: options.customInstructions,
9639
+ customInstructions: this.#obfuscateTextForProvider(options.customInstructions),
9481
9640
  reserveTokens: branchSummarySettings.reserveTokens,
9482
9641
  metadata: this.agent.metadataForProvider(model.provider),
9483
- convertToLlm,
9642
+ convertToLlm: messages => this.#convertToLlmForSideRequest(messages),
9484
9643
  telemetry: resolveTelemetry(this.agent.telemetry, this.sessionId),
9485
9644
  });
9486
9645
  this.#branchSummaryAbortController = undefined;
@@ -65,13 +65,42 @@ async function readConfigYaml(): Promise<ConfigSnapshot> {
65
65
  }
66
66
  }
67
67
 
68
+ /**
69
+ * Process-lifetime memo for {@link resolveAuthBrokerConfig}. Keyed on the env
70
+ * inputs (plus agent dir, which decides which config.yml is read) so tests
71
+ * that flip `OMP_AUTH_BROKER_*` between cases still observe the change, while
72
+ * repeated resolution within one CLI invocation (startup, subagent sessions)
73
+ * skips the config.yml read and any `!command` token resolution.
74
+ */
75
+ let cachedConfigKey: string | null = null;
76
+ let cachedConfigPromise: Promise<AuthBrokerClientConfig | null> | null = null;
77
+
68
78
  /**
69
79
  * Read broker configuration. Returns null when the URL is missing
70
80
  * (broker disabled — local store is used). Throws when URL is set but no
71
81
  * token is available — the caller cannot fall back silently because the
72
82
  * user explicitly asked to use the broker.
83
+ *
84
+ * Successful resolutions (including "no broker configured") are memoized for
85
+ * the process lifetime; failures are not, so a missing token can be fixed and
86
+ * retried. Concurrent callers share one in-flight resolution.
73
87
  */
74
- export async function resolveAuthBrokerConfig(): Promise<AuthBrokerClientConfig | null> {
88
+ export function resolveAuthBrokerConfig(): Promise<AuthBrokerClientConfig | null> {
89
+ const key = `${process.env.OMP_AUTH_BROKER_URL ?? ""}\u0000${process.env.OMP_AUTH_BROKER_TOKEN ?? ""}\u0000${getAgentDir()}`;
90
+ if (cachedConfigPromise && cachedConfigKey === key) return cachedConfigPromise;
91
+ const promise = resolveAuthBrokerConfigUncached();
92
+ cachedConfigKey = key;
93
+ cachedConfigPromise = promise;
94
+ promise.catch(() => {
95
+ if (cachedConfigPromise === promise) {
96
+ cachedConfigPromise = null;
97
+ cachedConfigKey = null;
98
+ }
99
+ });
100
+ return promise;
101
+ }
102
+
103
+ async function resolveAuthBrokerConfigUncached(): Promise<AuthBrokerClientConfig | null> {
75
104
  const envUrl = process.env.OMP_AUTH_BROKER_URL;
76
105
  const envToken = process.env.OMP_AUTH_BROKER_TOKEN;
77
106
 
@@ -1516,10 +1516,10 @@ class NdjsonFileWriter {
1516
1516
  }
1517
1517
  }
1518
1518
 
1519
- /** Get recent sessions for display in welcome screen */
1519
+ /** Get recent sessions for display in welcome screen (which reserves WELCOME_SESSION_SLOTS rows) */
1520
1520
  export async function getRecentSessions(
1521
1521
  sessionDir: string,
1522
- limit = 3,
1522
+ limit = 4,
1523
1523
  storage: SessionStorage = new FileSessionStorage(),
1524
1524
  ): Promise<RecentSessionInfo[]> {
1525
1525
  const sessions = await getSortedSessions(sessionDir, storage);