@oh-my-pi/pi-coding-agent 15.10.9 → 15.10.11

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 (352) hide show
  1. package/CHANGELOG.md +117 -0
  2. package/dist/cli.js +23087 -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 +1 -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/commands/launch.d.ts +1 -1
  11. package/dist/types/commands/read.d.ts +1 -1
  12. package/dist/types/commands/usage.d.ts +25 -0
  13. package/dist/types/config/append-only-context-mode.d.ts +2 -1
  14. package/dist/types/config/model-discovery.d.ts +55 -0
  15. package/dist/types/config/model-registry.d.ts +20 -219
  16. package/dist/types/config/model-resolver.d.ts +16 -10
  17. package/dist/types/config/model-roles.d.ts +28 -0
  18. package/dist/types/config/models-config-schema.d.ts +523 -42
  19. package/dist/types/config/models-config.d.ts +385 -0
  20. package/dist/types/config/settings-schema.d.ts +12 -16
  21. package/dist/types/config/settings.d.ts +1 -1
  22. package/dist/types/debug/log-viewer.d.ts +1 -1
  23. package/dist/types/debug/raw-sse.d.ts +1 -1
  24. package/dist/types/debug/terminal-info.d.ts +0 -1
  25. package/dist/types/eval/backend.d.ts +0 -2
  26. package/dist/types/eval/idle-timeout.d.ts +0 -4
  27. package/dist/types/eval/js/shared/rewrite-imports.d.ts +6 -6
  28. package/dist/types/export/html/template.generated.d.ts +1 -1
  29. package/dist/types/extensibility/extensions/types.d.ts +3 -3
  30. package/dist/types/hindsight/mental-models.d.ts +17 -8
  31. package/dist/types/internal-urls/artifact-protocol.d.ts +2 -2
  32. package/dist/types/internal-urls/types.d.ts +1 -1
  33. package/dist/types/lsp/edits.d.ts +9 -0
  34. package/dist/types/lsp/index.d.ts +2 -2
  35. package/dist/types/lsp/types.d.ts +2 -0
  36. package/dist/types/lsp/utils.d.ts +3 -0
  37. package/dist/types/mcp/json-rpc.d.ts +5 -0
  38. package/dist/types/mnemopi/state.d.ts +11 -1
  39. package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
  40. package/dist/types/modes/components/assistant-message.d.ts +3 -1
  41. package/dist/types/modes/components/bash-execution.d.ts +1 -1
  42. package/dist/types/modes/components/copy-selector.d.ts +1 -1
  43. package/dist/types/modes/components/dynamic-border.d.ts +1 -1
  44. package/dist/types/modes/components/extensions/extension-dashboard.d.ts +1 -1
  45. package/dist/types/modes/components/extensions/extension-list.d.ts +1 -1
  46. package/dist/types/modes/components/extensions/inspector-panel.d.ts +1 -1
  47. package/dist/types/modes/components/footer.d.ts +1 -1
  48. package/dist/types/modes/components/hook-editor.d.ts +5 -0
  49. package/dist/types/modes/components/hook-input.d.ts +4 -0
  50. package/dist/types/modes/components/hook-selector.d.ts +1 -1
  51. package/dist/types/modes/components/model-selector.d.ts +1 -1
  52. package/dist/types/modes/components/plan-review-overlay.d.ts +1 -1
  53. package/dist/types/modes/components/session-observer-overlay.d.ts +1 -1
  54. package/dist/types/modes/components/session-selector.d.ts +1 -1
  55. package/dist/types/modes/components/status-line/component.d.ts +1 -1
  56. package/dist/types/modes/components/tiny-title-download-progress.d.ts +1 -1
  57. package/dist/types/modes/components/transcript-container.d.ts +31 -26
  58. package/dist/types/modes/components/tree-selector.d.ts +1 -1
  59. package/dist/types/modes/components/user-message-selector.d.ts +1 -1
  60. package/dist/types/modes/components/user-message.d.ts +2 -1
  61. package/dist/types/modes/components/visual-truncate.d.ts +1 -1
  62. package/dist/types/modes/components/welcome.d.ts +19 -3
  63. package/dist/types/modes/controllers/mcp-command-controller.d.ts +1 -1
  64. package/dist/types/modes/controllers/streaming-reveal.d.ts +1 -1
  65. package/dist/types/modes/interactive-mode.d.ts +1 -1
  66. package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +1 -1
  67. package/dist/types/modes/setup-wizard/scenes/types.d.ts +1 -1
  68. package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +1 -1
  69. package/dist/types/modes/setup-wizard/wizard-overlay.d.ts +1 -1
  70. package/dist/types/modes/types.d.ts +2 -1
  71. package/dist/types/session/agent-session.d.ts +1 -1
  72. package/dist/types/session/auth-broker-config.d.ts +4 -0
  73. package/dist/types/session/session-manager.d.ts +1 -1
  74. package/dist/types/slash-commands/helpers/stats-dashboard.d.ts +13 -0
  75. package/dist/types/ssh/connection-manager.d.ts +8 -0
  76. package/dist/types/task/discovery.d.ts +1 -2
  77. package/dist/types/task/parallel.d.ts +2 -2
  78. package/dist/types/task/worktree.d.ts +2 -0
  79. package/dist/types/tiny/title-client.d.ts +1 -1
  80. package/dist/types/tools/ask.d.ts +4 -0
  81. package/dist/types/tools/conflict-detect.d.ts +16 -0
  82. package/dist/types/tools/github-cache.d.ts +7 -0
  83. package/dist/types/tools/sqlite-reader.d.ts +3 -0
  84. package/dist/types/tools/todo.d.ts +2 -0
  85. package/dist/types/tui/output-block.d.ts +3 -3
  86. package/dist/types/utils/changelog.d.ts +8 -0
  87. package/dist/types/web/scrapers/readthedocs.d.ts +3 -0
  88. package/dist/types/web/scrapers/types.d.ts +12 -0
  89. package/dist/types/web/search/providers/codex.d.ts +1 -1
  90. package/dist/types/web/search/providers/gemini.d.ts +1 -1
  91. package/examples/extensions/tools.ts +5 -4
  92. package/package.json +14 -11
  93. package/scripts/build-binary.ts +18 -23
  94. package/scripts/bundle-dist.ts +81 -0
  95. package/scripts/{dev-launch → omp} +1 -1
  96. package/scripts/{dev-launch-preload.ts → omp.ts} +1 -1
  97. package/src/async/job-manager.ts +57 -3
  98. package/src/autoresearch/dashboard.ts +1 -1
  99. package/src/autoresearch/prompt-setup.md +6 -6
  100. package/src/autoresearch/prompt.md +6 -6
  101. package/src/capability/fs.ts +10 -0
  102. package/src/cli/args.ts +1 -1
  103. package/src/cli/auth-gateway-cli.ts +1 -3
  104. package/src/cli/dry-balance-cli.ts +1 -1
  105. package/src/cli/gallery-cli.ts +1 -1
  106. package/src/cli/gallery-fixtures/fs.ts +1 -1
  107. package/src/cli/gallery-fixtures/types.ts +5 -1
  108. package/src/cli/list-models.ts +7 -12
  109. package/src/cli/usage-cli.ts +603 -0
  110. package/src/cli-commands.ts +1 -0
  111. package/src/cli.ts +69 -5
  112. package/src/commands/complete.ts +1 -1
  113. package/src/commands/launch.ts +1 -1
  114. package/src/commands/read.ts +6 -3
  115. package/src/commands/usage.ts +35 -0
  116. package/src/commit/agentic/agent.ts +1 -1
  117. package/src/commit/model-selection.ts +1 -1
  118. package/src/config/append-only-context-mode.ts +6 -12
  119. package/src/config/model-discovery.ts +554 -0
  120. package/src/config/model-registry.ts +308 -1025
  121. package/src/config/model-resolver.ts +113 -156
  122. package/src/config/model-roles.ts +74 -0
  123. package/src/config/models-config-schema.ts +57 -8
  124. package/src/config/models-config.ts +129 -0
  125. package/src/config/settings-schema.ts +18 -14
  126. package/src/config/settings.ts +37 -1
  127. package/src/dap/client.ts +124 -37
  128. package/src/dap/session.ts +259 -158
  129. package/src/debug/log-viewer.ts +1 -1
  130. package/src/debug/raw-sse.ts +1 -1
  131. package/src/debug/terminal-info.ts +0 -3
  132. package/src/edit/diff.ts +95 -18
  133. package/src/edit/hashline/block-resolver.ts +20 -1
  134. package/src/edit/hashline/diff.ts +36 -1
  135. package/src/edit/hashline/execute.ts +8 -2
  136. package/src/edit/index.ts +16 -1
  137. package/src/edit/modes/patch.ts +52 -0
  138. package/src/edit/modes/replace.ts +56 -22
  139. package/src/edit/notebook.ts +22 -2
  140. package/src/edit/renderer.ts +36 -10
  141. package/src/eval/__tests__/completion-bridge.test.ts +1 -1
  142. package/src/eval/backend.ts +0 -2
  143. package/src/eval/completion-bridge.ts +2 -1
  144. package/src/eval/idle-timeout.ts +2 -9
  145. package/src/eval/js/context-manager.ts +6 -8
  146. package/src/eval/js/executor.ts +6 -2
  147. package/src/eval/js/index.ts +0 -2
  148. package/src/eval/js/shared/helpers.ts +5 -6
  149. package/src/eval/js/shared/local-module-loader.ts +1 -1
  150. package/src/eval/js/shared/prelude.txt +62 -1
  151. package/src/eval/js/shared/rewrite-imports.ts +49 -23
  152. package/src/eval/js/shared/runtime.ts +1 -1
  153. package/src/eval/py/index.ts +0 -2
  154. package/src/eval/py/kernel.ts +19 -0
  155. package/src/eval/py/runner.py +107 -3
  156. package/src/exec/bash-executor.ts +3 -1
  157. package/src/export/html/template.generated.ts +1 -1
  158. package/src/export/html/template.js +3 -1
  159. package/src/extensibility/extensions/types.ts +3 -2
  160. package/src/extensibility/plugins/legacy-pi-compat.ts +20 -3
  161. package/src/hindsight/mental-models.ts +59 -12
  162. package/src/hindsight/state.ts +6 -1
  163. package/src/internal-urls/artifact-protocol.ts +11 -2
  164. package/src/internal-urls/docs-index.generated.ts +10 -10
  165. package/src/internal-urls/issue-pr-protocol.ts +12 -5
  166. package/src/internal-urls/router.ts +1 -1
  167. package/src/internal-urls/types.ts +1 -1
  168. package/src/lib/xai-http.ts +1 -1
  169. package/src/lsp/client.ts +118 -38
  170. package/src/lsp/clients/biome-client.ts +101 -39
  171. package/src/lsp/edits.ts +143 -95
  172. package/src/lsp/index.ts +31 -22
  173. package/src/lsp/render.ts +1 -1
  174. package/src/lsp/types.ts +2 -0
  175. package/src/lsp/utils.ts +28 -10
  176. package/src/main.ts +165 -17
  177. package/src/mcp/json-rpc.ts +35 -5
  178. package/src/mcp/transports/stdio.ts +7 -1
  179. package/src/memories/index.ts +2 -1
  180. package/src/mnemopi/backend.ts +25 -3
  181. package/src/mnemopi/state.ts +38 -2
  182. package/src/modes/components/agent-dashboard.ts +10 -7
  183. package/src/modes/components/assistant-message.ts +19 -13
  184. package/src/modes/components/bash-execution.ts +1 -1
  185. package/src/modes/components/copy-selector.ts +1 -1
  186. package/src/modes/components/diff.ts +13 -2
  187. package/src/modes/components/dynamic-border.ts +12 -3
  188. package/src/modes/components/extensions/extension-dashboard.ts +8 -5
  189. package/src/modes/components/extensions/extension-list.ts +1 -1
  190. package/src/modes/components/extensions/inspector-panel.ts +1 -1
  191. package/src/modes/components/footer.ts +1 -1
  192. package/src/modes/components/history-search.ts +1 -1
  193. package/src/modes/components/hook-editor.ts +8 -0
  194. package/src/modes/components/hook-input.ts +8 -0
  195. package/src/modes/components/hook-selector.ts +2 -2
  196. package/src/modes/components/model-selector.ts +66 -54
  197. package/src/modes/components/plan-review-overlay.ts +1 -1
  198. package/src/modes/components/session-observer-overlay.ts +2 -2
  199. package/src/modes/components/session-selector.ts +1 -1
  200. package/src/modes/components/settings-selector.ts +5 -1
  201. package/src/modes/components/status-line/component.ts +1 -1
  202. package/src/modes/components/tiny-title-download-progress.ts +1 -1
  203. package/src/modes/components/transcript-container.ts +373 -141
  204. package/src/modes/components/tree-selector.ts +3 -3
  205. package/src/modes/components/user-message-selector.ts +1 -1
  206. package/src/modes/components/user-message.ts +17 -5
  207. package/src/modes/components/visual-truncate.ts +1 -1
  208. package/src/modes/components/welcome.ts +108 -26
  209. package/src/modes/controllers/command-controller.ts +10 -3
  210. package/src/modes/controllers/event-controller.ts +73 -49
  211. package/src/modes/controllers/input-controller.ts +5 -5
  212. package/src/modes/controllers/mcp-command-controller.ts +1 -1
  213. package/src/modes/controllers/selector-controller.ts +1 -5
  214. package/src/modes/controllers/streaming-reveal.ts +85 -18
  215. package/src/modes/interactive-mode.ts +5 -19
  216. package/src/modes/setup-wizard/scenes/glyph.ts +1 -1
  217. package/src/modes/setup-wizard/scenes/providers.ts +1 -1
  218. package/src/modes/setup-wizard/scenes/sign-in.ts +1 -1
  219. package/src/modes/setup-wizard/scenes/theme.ts +1 -1
  220. package/src/modes/setup-wizard/scenes/types.ts +1 -1
  221. package/src/modes/setup-wizard/scenes/web-search.ts +1 -1
  222. package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
  223. package/src/modes/types.ts +2 -1
  224. package/src/prompts/agents/explore.md +2 -2
  225. package/src/prompts/agents/librarian.md +1 -2
  226. package/src/prompts/agents/oracle.md +1 -1
  227. package/src/prompts/agents/plan.md +5 -5
  228. package/src/prompts/agents/task.md +5 -5
  229. package/src/prompts/ci-green-request.md +5 -7
  230. package/src/prompts/goals/goal-budget-limit.md +2 -2
  231. package/src/prompts/goals/goal-continuation.md +4 -4
  232. package/src/prompts/goals/goal-mode-active.md +1 -1
  233. package/src/prompts/memories/read-path.md +1 -1
  234. package/src/prompts/memories/stage_one_system.md +2 -2
  235. package/src/prompts/review-custom-request.md +1 -1
  236. package/src/prompts/system/agent-creation-architect.md +2 -2
  237. package/src/prompts/system/auto-continue.md +1 -1
  238. package/src/prompts/system/background-tan-dispatch.md +1 -1
  239. package/src/prompts/system/btw-user.md +2 -2
  240. package/src/prompts/system/commit-message-system.md +13 -1
  241. package/src/prompts/system/custom-system-prompt.md +1 -1
  242. package/src/prompts/system/eager-todo.md +2 -2
  243. package/src/prompts/system/irc-incoming.md +1 -1
  244. package/src/prompts/system/manual-continue.md +1 -1
  245. package/src/prompts/system/omfg-user.md +3 -4
  246. package/src/prompts/system/orchestrate-notice.md +9 -9
  247. package/src/prompts/system/plan-mode-active.md +4 -4
  248. package/src/prompts/system/plan-mode-subagent.md +4 -5
  249. package/src/prompts/system/plan-mode-tool-decision-reminder.md +1 -1
  250. package/src/prompts/system/project-prompt.md +2 -2
  251. package/src/prompts/system/subagent-system-prompt.md +4 -4
  252. package/src/prompts/system/system-prompt.md +15 -26
  253. package/src/prompts/system/title-system.md +2 -2
  254. package/src/prompts/system/ttsr-tool-reminder.md +1 -1
  255. package/src/prompts/system/workflow-notice.md +1 -1
  256. package/src/prompts/tools/ast-edit.md +1 -1
  257. package/src/prompts/tools/ast-grep.md +2 -2
  258. package/src/prompts/tools/bash.md +8 -10
  259. package/src/prompts/tools/browser.md +7 -7
  260. package/src/prompts/tools/debug.md +1 -1
  261. package/src/prompts/tools/eval.md +3 -3
  262. package/src/prompts/tools/find.md +0 -1
  263. package/src/prompts/tools/github.md +8 -7
  264. package/src/prompts/tools/goal.md +1 -1
  265. package/src/prompts/tools/image-gen.md +1 -1
  266. package/src/prompts/tools/inspect-image-system.md +1 -1
  267. package/src/prompts/tools/irc.md +15 -15
  268. package/src/prompts/tools/lsp.md +2 -2
  269. package/src/prompts/tools/patch.md +2 -2
  270. package/src/prompts/tools/read.md +3 -4
  271. package/src/prompts/tools/recall.md +1 -1
  272. package/src/prompts/tools/reflect.md +1 -1
  273. package/src/prompts/tools/render-mermaid.md +2 -2
  274. package/src/prompts/tools/replace.md +4 -10
  275. package/src/prompts/tools/rewind.md +2 -2
  276. package/src/prompts/tools/search-tool-bm25.md +1 -9
  277. package/src/prompts/tools/search.md +0 -1
  278. package/src/prompts/tools/ssh.md +0 -4
  279. package/src/prompts/tools/task.md +2 -3
  280. package/src/prompts/tools/todo.md +6 -2
  281. package/src/sdk.ts +23 -10
  282. package/src/session/agent-session.ts +44 -10
  283. package/src/session/auth-broker-config.ts +30 -1
  284. package/src/session/session-manager.ts +2 -2
  285. package/src/session/streaming-output.ts +23 -2
  286. package/src/slash-commands/builtin-registry.ts +20 -0
  287. package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
  288. package/src/ssh/connection-manager.ts +27 -0
  289. package/src/task/commands.ts +2 -1
  290. package/src/task/discovery.ts +17 -24
  291. package/src/task/executor.ts +61 -53
  292. package/src/task/index.ts +137 -60
  293. package/src/task/parallel.ts +3 -3
  294. package/src/task/render.ts +2 -2
  295. package/src/task/worktree.ts +64 -56
  296. package/src/thinking.ts +2 -1
  297. package/src/tiny/title-client.ts +32 -14
  298. package/src/tools/archive-reader.ts +30 -2
  299. package/src/tools/ask.ts +104 -21
  300. package/src/tools/ast-edit.ts +25 -5
  301. package/src/tools/auto-generated-guard.ts +20 -3
  302. package/src/tools/bash-interactive.ts +27 -7
  303. package/src/tools/bash.ts +54 -13
  304. package/src/tools/browser/launch.ts +11 -2
  305. package/src/tools/browser/readable.ts +19 -2
  306. package/src/tools/browser/registry.ts +4 -1
  307. package/src/tools/browser/render.ts +2 -2
  308. package/src/tools/browser/tab-supervisor.ts +55 -16
  309. package/src/tools/conflict-detect.ts +50 -4
  310. package/src/tools/debug.ts +1 -1
  311. package/src/tools/eval-render.ts +5 -5
  312. package/src/tools/eval.ts +0 -2
  313. package/src/tools/fetch.ts +33 -10
  314. package/src/tools/gh-cache-invalidation.ts +63 -8
  315. package/src/tools/gh-renderer.ts +1 -1
  316. package/src/tools/gh.ts +172 -29
  317. package/src/tools/github-cache.ts +70 -6
  318. package/src/tools/image-gen.ts +3 -9
  319. package/src/tools/irc.ts +5 -1
  320. package/src/tools/job.ts +1 -1
  321. package/src/tools/read.ts +202 -61
  322. package/src/tools/render-utils.ts +3 -3
  323. package/src/tools/resolve.ts +1 -1
  324. package/src/tools/search.ts +92 -29
  325. package/src/tools/sqlite-reader.ts +17 -5
  326. package/src/tools/ssh.ts +8 -8
  327. package/src/tools/todo.ts +51 -12
  328. package/src/tools/write.ts +118 -18
  329. package/src/tui/output-block.ts +4 -4
  330. package/src/utils/changelog.ts +27 -1
  331. package/src/utils/file-mentions.ts +2 -1
  332. package/src/web/scrapers/arxiv.ts +1 -1
  333. package/src/web/scrapers/go-pkg.ts +1 -1
  334. package/src/web/scrapers/iacr.ts +1 -1
  335. package/src/web/scrapers/readthedocs.ts +1 -1
  336. package/src/web/scrapers/twitter.ts +2 -1
  337. package/src/web/scrapers/types.ts +87 -8
  338. package/src/web/scrapers/wikipedia.ts +1 -1
  339. package/src/web/scrapers/youtube.ts +6 -1
  340. package/src/web/search/index.ts +1 -1
  341. package/src/web/search/providers/anthropic.ts +8 -2
  342. package/src/web/search/providers/codex.ts +2 -1
  343. package/src/web/search/providers/gemini.ts +2 -3
  344. package/src/web/search/render.ts +8 -6
  345. package/dist/types/config/model-equivalence.d.ts +0 -24
  346. package/dist/types/config/model-id-affixes.d.ts +0 -12
  347. package/dist/types/config/model-provider-priority.d.ts +0 -1
  348. package/dist/types/exec/idle-timeout-watchdog.d.ts +0 -18
  349. package/src/config/model-equivalence.ts +0 -875
  350. package/src/config/model-id-affixes.ts +0 -81
  351. package/src/config/model-provider-priority.ts +0 -56
  352. package/src/exec/idle-timeout-watchdog.ts +0 -126
@@ -4,30 +4,30 @@ Sends short text messages to other live agents in this process and receives thei
4
4
  - The main agent is addressable as `Main`. Subagents reuse their task id (e.g. `AuthLoader`, or `AuthLoader-2` when the name repeats).
5
5
  - `op: "list"` returns the current set of visible peers. Use it before sending if you are not sure who is live.
6
6
  - `op: "send"` delivers `message` to `to`. `to` may be a specific id or `"all"` to broadcast.
7
- - The recipient generates the reply via an ephemeral side-channel turn that uses their current model, system prompt, and history — it does **not** wait for the recipient's main loop to be free, so it is safe to IRC an agent that is currently inside a long-running tool call.
8
- - The exchange (incoming question + auto-reply) is queued for injection into the recipient's persisted history; the recipient sees it on its next turn and can follow up if needed.
7
+ - Replies are generated on a side channel that does not wait for the recipient's main loop, so it is safe to IRC an agent that is mid tool call.
8
+ - The exchange (question + auto-reply) is injected into the recipient's history; they see it on their next turn and can follow up.
9
9
  </instruction>
10
10
 
11
11
  <when_to_use>
12
12
  You SHOULD reach for `irc` proactively when continuing alone is wasteful or wrong. When in doubt, prefer messaging.
13
- - **Unexpected state.** You hit something the original task did not describe — a missing file, a config that contradicts the assignment, an API behaving differently than you were told, a tool failing in a way that suggests the spec is wrong. DM `Main` (or the spawning agent) for guidance instead of guessing.
14
- - **Blocked by another agent.** A peer holds the file/branch/resource you need, has already started the change you are about to make, or owns a decision you depend on. DM that peer (or broadcast to discover who) before duplicating or stepping on work.
15
- - **Decision points outside your scope.** A genuine fork in the road that the assignment did not pre-decide (e.g. which of two viable APIs to use, whether to refactor adjacent code). Ask the requester rather than picking unilaterally.
16
- - **Coordination opportunities.** You realize a peer's in-flight work would benefit from yours, or vice-versa.
13
+ - **Unexpected state.** The task did not describe what you found — missing file, config contradicting the assignment, API or tool behaving differently than told. DM `Main` (or the spawning agent) instead of guessing.
14
+ - **Blocked by another agent.** A peer holds the file/branch/resource you need, started the change you are about to make, or owns a decision you depend on. DM that peer (or broadcast to discover who) before duplicating work.
15
+ - **Decision points outside your scope.** A genuine fork the assignment did not pre-decide (e.g. which of two viable APIs, whether to refactor adjacent code). Ask the requester rather than picking unilaterally.
16
+ - **Coordination opportunities.** A peer's in-flight work would benefit from yours, or vice-versa.
17
17
 
18
- Do **not** use `irc` for: routine progress updates, things you can verify with a tool call, or questions whose answer is already in your assignment / repo / docs.
18
+ NEVER use `irc` for: routine progress updates, things a tool call can verify, or questions already answered by your assignment / repo / docs.
19
19
  </when_to_use>
20
20
 
21
21
  <etiquette>
22
22
  These rules apply to both sending and replying.
23
- - **Plain prose only.** Do not send structured JSON status payloads (e.g. `{"type":"task_completed",…}`). Write a normal sentence: "Done with the auth refactor — left a TODO in `src/server/auth.ts` for the rate limiter."
24
- - **Do not quote the message you are replying to.** The sender already saw it; the TUI already renders it. Lead with the answer.
25
- - **Use IRC, not terminal tools, to learn about peers.** Do not `grep` artifacts, read other sessions' JSONL files, or shell-poke around to figure out what another agent is doing. DM them — they have the live answer and you do not.
26
- - **One round-trip is enough.** Replies arrive synchronously when the recipient is reachable. Do not follow up with "did you get my message?" — they did. If `delivered` is empty or the result was `failed`, the peer is unavailable; move on or report the blocker, do not retry in a loop.
27
- - **Stay terse.** A DM is a chat message, not a memo. One question per send when you can. Share file paths and artifacts via `local://` / `memory://` / `artifact://` URLs instead of pasting blobs.
28
- - **Address peers by id.** Use the exact id from `op: "list"` (e.g. `AuthLoader`, `Main`). Do not invent friendly names.
29
- - **Do not IRC for things a tool would answer.** If a `read`, `grep`, or build command would resolve the question, do that first.
30
- - **When you receive an IRC message, answer it before continuing.** The recipient injects the question + your auto-reply into your history; address it directly, do not repeat it back to the user.
23
+ - **Plain prose only.** NEVER send structured JSON status payloads (e.g. `{"type":"task_completed",…}`). Write a normal sentence: "Done with the auth refactor — left a TODO in `src/server/auth.ts` for the rate limiter."
24
+ - **NEVER quote the message you are replying to.** Lead with the answer.
25
+ - **Use IRC, not terminal tools, to learn about peers.** NEVER `grep` artifacts, read other sessions' JSONL files, or shell-poke to figure out what another agent is doing. DM them.
26
+ - **One round-trip is enough.** Replies arrive synchronously when the recipient is reachable. NEVER follow up with "did you get my message?". If `delivered` is empty or the result was `failed`, the peer is unavailable move on or report the blocker; NEVER retry in a loop.
27
+ - **Stay terse.** A DM is a chat message, not a memo. One question per send. Share file paths and artifacts via `local://` / `memory://` / `artifact://` URLs instead of pasting blobs.
28
+ - **Address peers by id.** Use the exact id from `op: "list"` (e.g. `AuthLoader`, `Main`). NEVER invent friendly names.
29
+ - **NEVER IRC for things a tool would answer.** If a `read`, `grep`, or build command resolves the question, do that first.
30
+ - **Answer incoming IRC messages before continuing.** Address the question directly; do not repeat it back to the user.
31
31
  </etiquette>
32
32
 
33
33
  <output>
@@ -37,6 +37,6 @@ Interacts with Language Server Protocol servers for code intelligence.
37
37
 
38
38
  <critical>
39
39
  - You MUST use `lsp` for symbol-aware operations (rename, find references, go to definition/implementation, code actions) whenever a language server is available — it is safer and more accurate than text-based alternatives.
40
- - You NEVER perform cross-file renames with `ast_edit`, `sed`, `rsed`, or manual edits when `lsp` `rename` can do it. Text-based renames miss shadowing, re-exports, and usages in other files.
41
- - Prefer `lsp` `code_actions` for imports, quick-fixes, and refactors the language server already knows how to apply.
40
+ - You NEVER perform cross-file renames with `ast_edit`, `sed`, or manual edits when `lsp` `rename` can do it. Text-based renames miss shadowing, re-exports, and usages in other files.
41
+ - You SHOULD use `lsp` `code_actions` for imports, quick-fixes, and refactors the language server already knows how to apply.
42
42
  </critical>
@@ -5,7 +5,7 @@ Patches files given diff hunks. Primary tool for existing-file edits.
5
5
  - `@@` — bare header when context lines unique
6
6
  - `@@ $ANCHOR` — anchor copied verbatim from file (full line or unique substring)
7
7
  **Anchor Selection:**
8
- 1. Otherwise choose highly specific anchor copied from file:
8
+ 1. Prefer bare `@@` when context lines alone are unique; otherwise choose highly specific anchor copied from file:
9
9
  - full function signature
10
10
  - class declaration
11
11
  - unique string literal/error message
@@ -47,7 +47,7 @@ Returns success/failure; on failure, error message indicates:
47
47
  - You NEVER use anchors as comments (no line numbers, location labels, placeholders like `@@ @@`)
48
48
  - You NEVER place new lines outside the intended block
49
49
  - If edit fails or breaks structure, you MUST re-read the file and produce a new patch from current content — you NEVER retry the same diff
50
- - NEVER use edit to fix indentation, whitespace, or reformat code. Formatting is a single command run once at the end (`bun fmt`, `cargo fmt`, `prettier write`, etc.)—not N individual edits. If you see inconsistent indentation after an edit, leave it; the formatter will fix all of it in one pass.
50
+ - NEVER use edit to fix indentation, whitespace, or reformat code. Formatting is a single command run once at the end (`bun fmt`, `cargo fmt`, `prettier --write`, etc.) not N individual edits. If you see inconsistent indentation after an edit, leave it; the formatter will fix all of it in one pass.
51
51
  </critical>
52
52
 
53
53
  <examples>
@@ -8,7 +8,7 @@ Read files, directories, archives, SQLite databases, images, documents, internal
8
8
 
9
9
  ## Parameters
10
10
 
11
- - `path` — required. Local path, internal URI (`skill://`, `agent://`, `artifact://`, `memory://`, `rule://`, `local://`, `vault://`, `mcp://`), or URL. Append `:<sel>` for line ranges, raw mode, or special modes (e.g. `src/foo.ts:50-200`, `src/foo.ts:raw`, `db.sqlite:users:42`).
11
+ - `path` — required. Local path, internal URI (`skill://`, `agent://`, `artifact://`, `memory://`, `rule://`, `local://`, `vault://`, `mcp://`, `omp://`, `issue://`, `pr://`), or URL. Append `:<sel>` for line ranges, raw mode, or special modes (e.g. `src/foo.ts:50-200`, `src/foo.ts:raw`, `db.sqlite:users:42`).
12
12
 
13
13
  ## Selectors
14
14
 
@@ -74,12 +74,11 @@ For `.sqlite`, `.sqlite3`, `.db`, `.db3`:
74
74
 
75
75
  # Internal URIs
76
76
 
77
- `skill://<name>`, `agent://<id>`, `artifact://<id>`, `memory://root`, `rule://<name>`, `local://<name>.md`, `vault://<vault>/<path>`, `mcp://<uri>` resolve transparently and accept the same line selectors as filesystem paths. Use `artifact://<id>` to recover full output that a previous bash/eval/tool result spilled or truncated.
77
+ `skill://<name>`, `agent://<id>`, `artifact://<id>`, `memory://root`, `rule://<name>`, `local://<name>.md`, `vault://<vault>/<path>`, `mcp://<uri>`, `omp://<doc>.md`, `issue://<N>`, and `pr://<N>` resolve transparently and accept the same line selectors as filesystem paths. Use `artifact://<id>` to recover full output that a previous bash/eval/tool result spilled or truncated.
78
78
 
79
79
  <critical>
80
80
  - You MUST use `read` for every file, directory, archive, and URL inspection. `cat`, `head`, `tail`, `less`, `more`, `ls`, `tar`, `unzip`, `curl`, `wget` are FORBIDDEN — any such bash call is a bug, regardless of how short or convenient it looks.
81
81
  - You MUST prefer `read` over a browser/puppeteer tool for URL content; only reach for a browser when `read` cannot deliver reasonable content.
82
82
  - For line ranges, append the selector to `path` (`path="src/foo.ts:50-200"`, `path="src/foo.ts:50+150"`). NEVER substitute `sed -n`, `awk NR`, or `head`/`tail` pipelines.
83
- - Summary footer says `read <path>:raw …`? Re-issue the exact selector it names. NEVER guess what's inside `..` / `…` markers — they carry no content.
84
- - You MAY combine selectors with URL reads and internal URIs; both paginate the cached resolved output.
83
+ - Summary footer names ranges to re-read? Re-issue ONLY the ranges you need via the multi-range selector. NEVER guess what's inside `..` / `…` markers — they carry no content.
85
84
  </critical>
@@ -2,4 +2,4 @@ Search long-term memory for relevant information. Returns raw matching entries r
2
2
 
3
3
  Use proactively — before answering questions about past conversations, user preferences, project decisions, or any topic where prior context would help accuracy. When in doubt, recall first.
4
4
 
5
- Prefer `recall` when you need specific facts or entries. Use `reflect` instead when you need a synthesised answer across many memories.
5
+ Prefer `recall` when you need specific facts or entries. Use `reflect` instead when you need a synthesized answer across many memories.
@@ -1,4 +1,4 @@
1
- Generate a synthesised answer by reasoning over long-term memory. Unlike `recall`, `reflect` blends relevant memories into a coherent response.
1
+ Generate a synthesized answer by reasoning over long-term memory. Unlike `recall`, `reflect` blends relevant memories into a coherent response.
2
2
 
3
3
  Use for open-ended questions spanning many stored facts: "What do you know about this user?", "Summarize project decisions.", "What are my preferences for X?"
4
4
 
@@ -3,7 +3,7 @@ Convert Mermaid graph source into ASCII diagram output.
3
3
  Parameters:
4
4
  - `mermaid` (required): Mermaid graph text to render.
5
5
  - `config` (optional): JSON render configuration (spacing and layout options).
6
+
6
7
  Behavior:
7
8
  - Returns ASCII diagram text.
8
- - Saves full output to `artifact://<id>` when storage available.
9
- - Returns error when Mermaid input invalid or rendering fails.
9
+ - Saves full output to `artifact://<id>`.
@@ -16,21 +16,15 @@ Returns success/failure status. On success, file modified in place with replacem
16
16
  </critical>
17
17
 
18
18
  <bash-alternatives>
19
- Replace for content-addressed changes—you identify \_what* to change by its text.
19
+ Replace is content-addressed — you identify *what* to change by its text.
20
20
 
21
- For position-addressed or pattern-addressed changes, bash more efficient:
21
+ For pattern-addressed bulk changes, bash is more efficient:
22
22
 
23
23
  |Operation|Command|
24
24
  |---|---|
25
- |Append to file|`cat >> file <<'EOF'`…`EOF`|
26
- |Prepend to file|`{ cat - file; } <<'EOF' > tmp && mv tmp file`|
27
- |Delete lines N-M|`sed -i 'N,Md' file`|
28
- |Insert after line N|`sed -i 'Na\text' file`|
29
25
  |Regex replace|`sd 'pattern' 'replacement' file`|
30
26
  |Bulk replace across files|`sd 'pattern' 'replacement' **/*.ts`|
31
- |Copy lines N-M to another file|`sed -n 'N,Mp' src >> dest`|
32
- |Move lines N-M to another file|`sed -n 'N,Mp' src >> dest && sed -i 'N,Md' src`|
33
27
 
34
- Use Replace when _content itself_ identifies location.
35
- Use bash when _position_ or _pattern_ identifies what to change.
28
+ Use Replace when _content itself_ identifies location; use `ast_edit` for structure-aware codemods.
29
+ NEVER use `sed -i`/`perl -i`/heredoc redirection for edits those calls are blocked; use this tool or `write`.
36
30
  </bash-alternatives>
@@ -3,9 +3,9 @@ End an active checkpoint. Rewind context to it, replacing intermediate explorati
3
3
  Call immediately after `checkpoint`-started investigative work.
4
4
 
5
5
  Requirements:
6
- - `report` is REQUIRED and must be concise, factual, and actionable.
6
+ - `report` is REQUIRED and MUST be concise, factual, and actionable.
7
7
  - Include key findings, decisions, and any unresolved risks.
8
- - Do not include raw scratch logs unless essential.
8
+ - AVOID raw scratch logs unless essential.
9
9
  - You MUST call this before yielding if a checkpoint is active.
10
10
 
11
11
  Behavior:
@@ -15,21 +15,13 @@ Input:
15
15
  - `limit` — optional maximum number of tools to return and activate (default `8`)
16
16
 
17
17
  Behavior:
18
- - Searches hidden tool metadata using BM25-style relevance ranking
19
18
  - Matches against tool name, label, server name, description/summary, and input schema keys
20
19
  - Activates the top matching tools for the rest of the current session
21
20
  - Repeated searches add to the active tool set; they do not remove earlier selections
22
21
  - Newly activated tools become available before the next model call in the same overall turn
23
22
 
24
23
  Notes:
25
- Start with `limit` 5–10 if unsure.
26
- - `query` is matched against tool metadata fields:
27
- - `name`
28
- - `label`
29
- - `server_name` (MCP tools)
30
- - `mcp_tool_name` (MCP tools)
31
- - `description` / `summary`
32
- - input schema property keys (`schema_keys`)
24
+ - Start with `limit` 5–10 if unsure.
33
25
 
34
26
  Not for repository/file/code search. Tool discovery only.
35
27
 
@@ -20,6 +20,5 @@ Searches files using powerful regex matching.
20
20
  <critical>
21
21
  - You MUST use the built-in `search` tool for any content search. NEVER shell out to `grep`, `rg`, `ripgrep`, `ag`, `ack`, `git grep`, `awk`, `sed`-for-search, or any other CLI search via Bash — even for a single match, even "just to check quickly", even piped through other commands.
22
22
  - Bash `grep`/`rg` loses `.gitignore` semantics, bypasses result limits, and wastes tokens. The `search` tool is faster, structured, and already wired into the workspace — there is no scenario where Bash search is preferable.
23
- - If you catch yourself typing `grep`, `rg`, or `| grep` in a Bash command, stop and re-issue the lookup through the `search` tool instead.
24
23
  - If the search is open-ended, requiring multiple rounds, you MUST use the Task tool with the explore subagent instead of chaining `search` calls yourself.
25
24
  </critical>
@@ -1,9 +1,5 @@
1
1
  Runs commands on remote hosts.
2
2
 
3
- <instruction>
4
- You MUST build commands from the reference below
5
- </instruction>
6
-
7
3
  <commands>
8
4
  **linux/bash, linux/zsh, macos/bash, macos/zsh** — Unix-like:
9
5
  - Files: `ls`, `cat`, `head`, `tail`, `grep`, `find`
@@ -31,10 +31,9 @@ Subagents have no conversation history. Every fact, file path, and direction the
31
31
 
32
32
  <rules>
33
33
  - **Maximize batch width.** Spawn the widest parallel set the work decomposes into. NEVER spawn a single-task batch for divisible work, or defer work that could have been concurrent.
34
- - NEVER assign tasks to run project-wide build/test/lint. Caller verifies after the batch.
35
- - **Subagents do not verify, lint, or format.** Every assignment MUST instruct the subagent to skip all gates and formatters. You run them once at the end across the union of changed files — avoids redundant runs and racing formatter passes.
34
+ - **Subagents do not verify, lint, or format.** Every assignment MUST instruct the subagent to skip all gates, formatters, and project-wide build/test/lint. You run them once at the end across the union of changed files — avoids redundant runs and racing formatter passes.
36
35
  - No globs, no "update all", no package-wide scope. Fan out.
37
- - Do not concern yourself with how agents might overlap on certain actions. Never use it as an excuse to go slower: they can resolve collisions in real-time with the harness facilities.
36
+ - NEVER slow down or serialize because tasks might overlap on some files. Agents resolve collisions among themselves in real time.
38
37
  - Pass large payloads via `local://<path>` URIs, not inline. {{#if contextEnabled}} (other than the context){{/if}}
39
38
  {{#if contextEnabled}}- Put shared constraints in `context` once; do not duplicate across assignments.{{/if}}
40
39
  - Prefer agents that investigate **and** edit in one pass; only spin a read-only discovery step when affected files are genuinely unknown.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Manages a phased task list. Pass `ops`: a flat array of operations.
4
4
  The next pending task is auto-promoted to `in_progress` after each completion.
5
- Allowed `op` values are only `init`, `start`, `done`, `drop`, `rm`, `append`, and `note`. `pending` is a task status, not an `op`; leave not-yet-started tasks implicit in `init`/`append` lists.
5
+ Allowed `op` values are only `init`, `start`, `done`, `drop`, `rm`, `append`, `note`, and `view`. `pending` is a task status, not an `op`; leave not-yet-started tasks implicit in `init`/`append` lists.
6
6
 
7
7
  ## Operations
8
8
 
@@ -12,9 +12,10 @@ Allowed `op` values are only `init`, `start`, `done`, `drop`, `rm`, `append`, an
12
12
  |`start`|`task`|Mark in progress|
13
13
  |`done`|`task` or `phase`|Mark completed|
14
14
  |`drop`|`task` or `phase`|Mark abandoned|
15
- |`rm`|`task` or `phase`|Remove|
15
+ |`rm`|`task` or `phase` (optional)|Remove task or phase's tasks; omit both to clear the entire list|
16
16
  |`append`|`phase`, `items: string[]`|Append tasks to `phase`; lazily creates phase|
17
17
  |`note`|`task`, `text`|Append a note to a task. Reminders for future-you only.|
18
+ |`view`|—|Read-only: echo the current list without modifying it|
18
19
 
19
20
  ## Anatomy
20
21
  - **Task content**: 5–10 words, what is being done, not how. Used as the task identifier — unique.
@@ -25,6 +26,7 @@ Allowed `op` values are only `init`, `start`, `done`, `drop`, `rm`, `append`, an
25
26
  - Complete phases in order.
26
27
  - On blockers, `append` a new task to the active phase to unblock yourself, or `drop`.
27
28
  - `task` and `phase` fields reference content/name verbatim; keep them stable once introduced.
29
+ - Lost track of exact task text? `view` echoes the full list — NEVER guess content from memory; a mismatched `task` string is an error.
28
30
 
29
31
  ## When to create a list
30
32
  - Task requires 3+ distinct steps
@@ -35,6 +37,8 @@ Allowed `op` values are only `init`, `start`, `done`, `drop`, `rm`, `append`, an
35
37
  <examples>
36
38
  # Initial setup (multi-phase)
37
39
  `{"ops":[{"op":"init","list":[{"phase":"Foundation","items":["Scaffold crate","Wire workspace"]},{"phase":"Auth","items":["Port credential store","Wire OAuth providers"]},{"phase":"Verification","items":["Run cargo test"]}]}]}`
40
+ # View current state (read-only)
41
+ `{"ops":[{"op":"view"}]}`
38
42
  # Initial setup (single phase)
39
43
  `{"ops":[{"op":"init","list":[{"phase":"Implementation","items":["Apply fix","Run tests"]}]}]}`
40
44
  # Complete one task
package/src/sdk.ts CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  getOpenAICodexTransportDetails,
20
20
  prewarmOpenAICodexResponses,
21
21
  } from "@oh-my-pi/pi-ai/providers/openai-codex-responses";
22
+ import { DEFAULT_MODEL_PER_PROVIDER } from "@oh-my-pi/pi-catalog/provider-models";
22
23
  import type { Component } from "@oh-my-pi/pi-tui";
23
24
  import {
24
25
  $env,
@@ -41,7 +42,6 @@ import { createApiKeyResolver } from "./config/api-key-resolver";
41
42
  import { shouldEnableAppendOnlyContext } from "./config/append-only-context-mode";
42
43
  import { ModelRegistry } from "./config/model-registry";
43
44
  import {
44
- defaultModelPerProvider,
45
45
  formatModelString,
46
46
  getModelMatchPreferences,
47
47
  parseModelPattern,
@@ -531,11 +531,18 @@ function resolveSnapshotTtlMs(): number {
531
531
  * override to re-mint access tokens when needed.
532
532
  */
533
533
  export async function discoverAuthStorage(agentDir: string = getDefaultAgentDir()): Promise<AuthStorage> {
534
- const brokerConfig = await resolveAuthBrokerConfig();
534
+ const brokerConfigPromise = resolveAuthBrokerConfig();
535
+ const cachePath = getAuthBrokerSnapshotCachePath();
536
+ // Warm the encrypted snapshot cache into the page cache while the broker
537
+ // config resolves (it may shell out for a `!command` token). Decryption
538
+ // needs the resolved token, so the real cache read cannot start earlier.
539
+ void Bun.file(cachePath)
540
+ .arrayBuffer()
541
+ .catch(() => undefined);
542
+ const brokerConfig = await brokerConfigPromise;
535
543
  if (brokerConfig) {
536
544
  const client = new AuthBrokerClient({ url: brokerConfig.url, token: brokerConfig.token });
537
545
  const ttlMs = resolveSnapshotTtlMs();
538
- const cachePath = getAuthBrokerSnapshotCachePath();
539
546
  const persist =
540
547
  ttlMs > 0
541
548
  ? (snapshot: SnapshotResponse): void => {
@@ -1730,7 +1737,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1730
1737
  // the winning provider (e.g. anthropic's claude-3-5-sonnet-20240620)
1731
1738
  // instead of the intended provider default (claude-sonnet-4-6). Mirrors
1732
1739
  // findInitialModel's precedence.
1733
- for (const [provider, defaultId] of Object.entries(defaultModelPerProvider)) {
1740
+ for (const [provider, defaultId] of Object.entries(DEFAULT_MODEL_PER_PROVIDER)) {
1734
1741
  const preferred = fallbackCandidates.find(
1735
1742
  candidate => candidate.provider === provider && candidate.id === defaultId,
1736
1743
  );
@@ -2342,7 +2349,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2342
2349
  }
2343
2350
 
2344
2351
  if (model?.api === "openai-codex-responses") {
2345
- const codexModel = model;
2352
+ // `.api` equality doesn't narrow the generic; the guard makes this cast sound.
2353
+ const codexModel = model as Model<"openai-codex-responses">;
2346
2354
  const codexTransport = getOpenAICodexTransportDetails(codexModel, {
2347
2355
  sessionId: providerSessionId,
2348
2356
  baseUrl: codexModel.baseUrl,
@@ -2373,12 +2381,17 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2373
2381
  }
2374
2382
 
2375
2383
  // Start LSP warmup in the background so startup does not block on language server initialization.
2376
- // Print/script invocations (`hasUI=false`) don't render the warmup status indicator AND typically
2377
- // finish before LSP servers would have stabilizedwarming them just spends CPU parsing big
2378
- // `initialize` responses concurrently with the LLM stream consumer, jittering perceived latency.
2379
- // Tools that need an LSP server still spin one up on demand through `getOrCreateClient`.
2384
+ // With `lsp.lazy` (the default) the warmup is skipped: recognized servers are still discovered and
2385
+ // surfaced in the UI as "available", but cold-start on first use the lsp tool or an edit/write
2386
+ // touching a matching file type through `getOrCreateClient`.
2387
+ // Print/script invocations (`hasUI=false`) skip it regardless: they don't render the warmup status
2388
+ // indicator AND typically finish before LSP servers would have stabilized — warming them just spends
2389
+ // CPU parsing big `initialize` responses concurrently with the LLM stream consumer, jittering
2390
+ // perceived latency.
2380
2391
  let lspServers: CreateAgentSessionResult["lspServers"];
2381
- if (enableLsp && options.hasUI && settings.get("lsp.diagnosticsOnWrite")) {
2392
+ if (enableLsp && options.hasUI && settings.get("lsp.lazy")) {
2393
+ lspServers = discoverStartupLspServers(cwd, "available");
2394
+ } else if (enableLsp && options.hasUI) {
2382
2395
  lspServers = discoverStartupLspServers(cwd);
2383
2396
  if (lspServers.length > 0) {
2384
2397
  void (async () => {
@@ -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,7 +105,7 @@ 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
111
  formatModelSelectorValue,
@@ -115,6 +115,7 @@ import {
115
115
  type ResolvedModelRoleValue,
116
116
  resolveModelRoleValue,
117
117
  } from "../config/model-resolver";
118
+ import { MODEL_ROLE_IDS } from "../config/model-roles";
118
119
  import { expandPromptTemplate, type PromptTemplate } from "../config/prompt-templates";
119
120
  import type { Settings, SkillsSettings } from "../config/settings";
120
121
  import { onAppendOnlyModeChanged } from "../config/settings";
@@ -287,6 +288,20 @@ export type AgentSessionEventListener = (event: AgentSessionEvent) => void;
287
288
  export type AsyncJobSnapshotItem = Pick<AsyncJob, "id" | "type" | "status" | "label" | "startTime">;
288
289
 
289
290
  const EMPTY_STOP_MAX_RETRIES = 3;
291
+ const RETRY_BACKOFF_MAX_DELAY_MS = 8_000;
292
+ const RETRY_BACKOFF_JITTER_RATIO = 0.25;
293
+
294
+ function calculateRetryBackoffDelayMs(baseDelayMs: number, attempt: number): number {
295
+ const cappedDelayMs = Math.min(Math.max(0, baseDelayMs) * 2 ** Math.max(0, attempt - 1), RETRY_BACKOFF_MAX_DELAY_MS);
296
+ const jitter = 1 - Math.random() * RETRY_BACKOFF_JITTER_RATIO;
297
+ return cappedDelayMs * jitter;
298
+ }
299
+
300
+ /**
301
+ * Slack added past a sibling credential's block expiry before retrying, so
302
+ * the next getApiKey lands after the block has actually lapsed.
303
+ */
304
+ const SIBLING_UNBLOCK_BUFFER_MS = 1_000;
290
305
  const NON_WHITESPACE_RE = /\S/;
291
306
 
292
307
  function hasNonWhitespace(value: string): boolean {
@@ -8307,13 +8322,16 @@ export class AgentSession {
8307
8322
 
8308
8323
  const errorMessage = message.errorMessage || "Unknown error";
8309
8324
  const parsedRetryAfterMs = this.#parseRetryAfterMsFromError(errorMessage);
8310
- let delayMs = retrySettings.baseDelayMs * 2 ** (this.#retryAttempt - 1);
8325
+ let delayMs = calculateRetryBackoffDelayMs(retrySettings.baseDelayMs, this.#retryAttempt);
8311
8326
  let switchedCredential = false;
8312
8327
  let switchedModel = false;
8328
+ // Set when a usage-limit error pinned the wait to credential
8329
+ // availability — suppresses the generic retry-after bump below.
8330
+ let usageLimitWaitMs: number | undefined;
8313
8331
 
8314
8332
  if (this.model && isUsageLimitError(errorMessage)) {
8315
8333
  const retryAfterMs = parsedRetryAfterMs ?? calculateRateLimitBackoffMs(parseRateLimitReason(errorMessage));
8316
- const switched = await this.#modelRegistry.authStorage.markUsageLimitReached(
8334
+ const outcome = await this.#modelRegistry.authStorage.markUsageLimitReached(
8317
8335
  this.model.provider,
8318
8336
  this.sessionId,
8319
8337
  {
@@ -8321,12 +8339,28 @@ export class AgentSession {
8321
8339
  baseUrl: this.model.baseUrl,
8322
8340
  },
8323
8341
  );
8324
- if (switched) {
8342
+ if (outcome.switched) {
8325
8343
  switchedCredential = true;
8326
8344
  delayMs = 0;
8327
- } else if (retryAfterMs > delayMs) {
8328
- // No more accounts to switch to wait out the backoff
8329
- delayMs = retryAfterMs;
8345
+ } else {
8346
+ // No sibling credential is usable right now. Wait for whichever
8347
+ // comes first: the provider's retry-after window for the current
8348
+ // account, or the earliest moment a temporarily blocked sibling
8349
+ // frees up (e.g. a 60s post-401 block or a 5-min usage-probe
8350
+ // block) — the next attempt's getApiKey re-ranks and picks it up.
8351
+ // Without this, one short-lived sibling block escalates a
8352
+ // recoverable situation into the provider's multi-hour wait and
8353
+ // trips the fail-fast cap below.
8354
+ usageLimitWaitMs = retryAfterMs;
8355
+ if (outcome.retryAtMs !== undefined) {
8356
+ const siblingWaitMs = Math.max(0, outcome.retryAtMs - Date.now()) + SIBLING_UNBLOCK_BUFFER_MS;
8357
+ if (siblingWaitMs < usageLimitWaitMs) {
8358
+ usageLimitWaitMs = siblingWaitMs;
8359
+ }
8360
+ }
8361
+ if (usageLimitWaitMs > delayMs) {
8362
+ delayMs = usageLimitWaitMs;
8363
+ }
8330
8364
  }
8331
8365
  }
8332
8366
 
@@ -8338,7 +8372,7 @@ export class AgentSession {
8338
8372
  }
8339
8373
  if (switchedModel) {
8340
8374
  delayMs = 0;
8341
- } else if (parsedRetryAfterMs && parsedRetryAfterMs > delayMs) {
8375
+ } else if (usageLimitWaitMs === undefined && parsedRetryAfterMs && parsedRetryAfterMs > delayMs) {
8342
8376
  delayMs = parsedRetryAfterMs;
8343
8377
  }
8344
8378
  }
@@ -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);
@@ -650,6 +650,7 @@ export class OutputSink {
650
650
  #sawData = false;
651
651
  #truncated = false;
652
652
  #lastChunkTime = 0;
653
+ #pendingChunk = "";
653
654
 
654
655
  // Per-line column cap streaming state (persists across `push` calls so a
655
656
  // long line split across chunks still trips the same trigger).
@@ -701,14 +702,20 @@ export class OutputSink {
701
702
  push(chunk: string): void {
702
703
  chunk = sanitizeWithOptionalSixelPassthrough(chunk, sanitizeText);
703
704
 
704
- // Throttled onChunk: only call the callback when enough time has passed.
705
+ // Throttled onChunk: coalesce chunks arriving inside the throttle window
706
+ // and flush the buffered concatenation on the next eligible tick (plus a
707
+ // final flush in dump()) so the preview never has silent gaps.
705
708
  // Live preview gets the raw (pre-cap) chunk so the TUI never lags behind
706
709
  // what reached the sink — the column cap is for the persisted LLM view.
707
710
  if (this.#onChunk) {
708
711
  const now = Date.now();
709
712
  if (now - this.#lastChunkTime >= this.#chunkThrottleMs) {
710
713
  this.#lastChunkTime = now;
711
- this.#onChunk(chunk);
714
+ const merged = this.#pendingChunk + chunk;
715
+ this.#pendingChunk = "";
716
+ this.#onChunk(merged);
717
+ } else {
718
+ this.#pendingChunk += chunk;
712
719
  }
713
720
  }
714
721
 
@@ -880,6 +887,11 @@ export class OutputSink {
880
887
  const sink = Bun.file(this.#artifactPath).writer();
881
888
  this.#file = { path: this.#artifactPath, artifactId: this.#artifactId, sink };
882
889
 
890
+ // Head-retained bytes precede the rolling tail buffer in the capture.
891
+ if (this.#head.length > 0) {
892
+ sink.write(this.#head);
893
+ }
894
+
883
895
  // Flush existing buffer to file BEFORE it gets trimmed further.
884
896
  if (this.#buffer.length > 0) {
885
897
  sink.write(this.#buffer);
@@ -946,10 +958,19 @@ export class OutputSink {
946
958
  this.#columnEllipsisAdded = false;
947
959
  this.#columnDroppedBytes = 0;
948
960
  this.#columnTruncatedLines = 0;
961
+ this.#pendingChunk = "";
949
962
  }
950
963
 
951
964
  async dump(notice?: string): Promise<OutputSummary> {
952
965
  const noticeLine = notice ? `[${notice}]\n` : "";
966
+
967
+ // Flush any chunk still held back by the throttle so the live preview
968
+ // ends with the complete stream.
969
+ if (this.#onChunk && this.#pendingChunk.length > 0) {
970
+ const pending = this.#pendingChunk;
971
+ this.#pendingChunk = "";
972
+ this.#onChunk(pending);
973
+ }
953
974
  const totalLines = this.#sawData ? this.#totalLines + 1 : 0;
954
975
 
955
976
  if (this.#file) await this.#file.sink.end();
@@ -30,6 +30,7 @@ import { createMarketplaceManager } from "./helpers/marketplace-manager";
30
30
  import { handleMcpAcp } from "./helpers/mcp";
31
31
  import { commandConsumed, errorMessage, parseSlashCommand, parseSubcommand, usage } from "./helpers/parse";
32
32
  import { handleSshAcp } from "./helpers/ssh";
33
+ import { launchStatsDashboard, parseStatsDashboardArgs } from "./helpers/stats-dashboard";
33
34
  import { handleTodoAcp } from "./helpers/todo";
34
35
  import { buildUsageReportText } from "./helpers/usage-report";
35
36
  import { parseMarketplaceInstallArgs, parsePluginScopeArgs } from "./marketplace-install-parser";
@@ -542,6 +543,25 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
542
543
  runtime.ctx.editor.setText("");
543
544
  },
544
545
  },
546
+ {
547
+ name: "stats",
548
+ description: "Launch the local stats dashboard",
549
+ inlineHint: "[--port <port>]",
550
+ allowArgs: true,
551
+ handle: async (command, runtime) => {
552
+ const parsed = parseStatsDashboardArgs(command.args);
553
+ if ("error" in parsed) return usage(parsed.error, runtime);
554
+
555
+ await runtime.output("Syncing session files...");
556
+ try {
557
+ const result = await launchStatsDashboard(parsed);
558
+ await runtime.output(result.message);
559
+ } catch (error) {
560
+ await runtime.output(`Stats dashboard failed: ${errorMessage(error)}`);
561
+ }
562
+ return commandConsumed();
563
+ },
564
+ },
545
565
  {
546
566
  name: "changelog",
547
567
  description: "Show changelog entries",