@selesai/code 0.13.40 → 0.14.3

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 (1360) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/dist/bun/runtime-setup.js +4 -1
  3. package/dist/cli/args.d.ts +3 -1
  4. package/dist/cli/args.js +76 -33
  5. package/dist/cli/auth-check.d.ts +18 -0
  6. package/dist/cli/auth-check.js +49 -0
  7. package/dist/cli/credential-print.d.ts +5 -15
  8. package/dist/cli/credential-print.js +42 -82
  9. package/dist/cli/file-processor.d.ts +1 -1
  10. package/dist/cli/file-processor.js +2 -1
  11. package/dist/cli/list-models.d.ts +1 -1
  12. package/dist/cli/list-models.js +2 -2
  13. package/dist/cli/startup-ui.js +38 -30
  14. package/dist/config.d.ts +45 -7
  15. package/dist/config.js +134 -18
  16. package/dist/core/agent-session-custom-tools.test.js +64 -0
  17. package/dist/core/agent-session-length-continuation.test.js +7 -3
  18. package/dist/core/agent-session-runtime.js +2 -2
  19. package/dist/core/agent-session-services.d.ts +1 -0
  20. package/dist/core/agent-session-services.js +14 -0
  21. package/dist/core/agent-session.d.ts +180 -40
  22. package/dist/core/agent-session.js +1061 -485
  23. package/dist/core/auth-storage.d.ts +25 -15
  24. package/dist/core/auth-storage.js +242 -66
  25. package/dist/core/bash-executor.js +42 -52
  26. package/dist/core/bug-report.js +1 -1
  27. package/dist/core/cache-warmer.d.ts +1 -0
  28. package/dist/core/cache-warmer.js +15 -1
  29. package/dist/core/compaction/branch-summarization.js +3 -0
  30. package/dist/core/compaction/compaction.d.ts +3 -1
  31. package/dist/core/compaction/compaction.js +156 -79
  32. package/dist/core/compaction/utils.d.ts +2 -1
  33. package/dist/core/compaction/utils.js +25 -18
  34. package/dist/core/crash-log.d.ts +5 -0
  35. package/dist/core/crash-log.js +69 -0
  36. package/dist/core/defaults.d.ts +1 -1
  37. package/dist/core/defaults.js +1 -1
  38. package/dist/core/export-html/template.css +10 -0
  39. package/dist/core/export-html/template.js +72 -18
  40. package/dist/core/export-html/tool-renderer.d.ts +3 -3
  41. package/dist/core/export-html/tool-renderer.js +5 -3
  42. package/dist/core/extensions/index.d.ts +2 -2
  43. package/dist/core/extensions/index.js +1 -1
  44. package/dist/core/extensions/loader.js +138 -55
  45. package/dist/core/extensions/runner.d.ts +38 -5
  46. package/dist/core/extensions/runner.js +220 -7
  47. package/dist/core/extensions/types.d.ts +438 -51
  48. package/dist/core/extensions/types.js +3 -0
  49. package/dist/core/extensions/virtual-modules.js +6 -0
  50. package/dist/core/extensions/wrapper.d.ts +2 -2
  51. package/dist/core/extensions/wrapper.js +3 -3
  52. package/dist/core/footer-data-provider.d.ts +1 -1
  53. package/dist/core/footer-data-provider.js +1 -1
  54. package/dist/core/handoff.d.ts +0 -1
  55. package/dist/core/handoff.js +0 -1
  56. package/dist/core/http-dispatcher.js +5 -0
  57. package/dist/core/index.d.ts +1 -1
  58. package/dist/core/keybindings.d.ts +25 -24
  59. package/dist/core/keybindings.js +27 -6
  60. package/dist/core/mcp-servers.d.ts +134 -0
  61. package/dist/core/mcp-servers.js +223 -0
  62. package/dist/core/model-config.d.ts +122 -5
  63. package/dist/core/model-config.js +35 -1
  64. package/dist/core/model-registry.d.ts +23 -15
  65. package/dist/core/model-registry.js +46 -22
  66. package/dist/core/model-resolver.d.ts +6 -5
  67. package/dist/core/model-resolver.js +51 -21
  68. package/dist/core/model-runtime.d.ts +73 -14
  69. package/dist/core/model-runtime.js +398 -104
  70. package/dist/core/models-store.d.ts +12 -7
  71. package/dist/core/models-store.js +85 -15
  72. package/dist/core/nested-tool-calls.d.ts +102 -0
  73. package/dist/core/nested-tool-calls.js +178 -0
  74. package/dist/core/package-manager.d.ts +8 -1
  75. package/dist/core/package-manager.js +144 -61
  76. package/dist/core/package-manager.test.js +5 -5
  77. package/dist/core/pi-manifest.d.ts +9 -0
  78. package/dist/core/pi-manifest.js +25 -0
  79. package/dist/core/prompt-templates.d.ts +6 -1
  80. package/dist/core/prompt-templates.js +61 -35
  81. package/dist/core/provider-composer.d.ts +44 -19
  82. package/dist/core/provider-composer.js +166 -64
  83. package/dist/core/radius.d.ts +2 -0
  84. package/dist/core/radius.js +2 -0
  85. package/dist/core/remote-catalog-provider.d.ts +8 -1
  86. package/dist/core/remote-catalog-provider.js +25 -13
  87. package/dist/core/resource-loader.d.ts +9 -1
  88. package/dist/core/resource-loader.js +179 -45
  89. package/dist/core/runtime-credentials.d.ts +5 -5
  90. package/dist/core/runtime-credentials.js +11 -8
  91. package/dist/core/sdk.d.ts +16 -7
  92. package/dist/core/sdk.js +57 -24
  93. package/dist/core/session-manager.d.ts +45 -9
  94. package/dist/core/session-manager.js +124 -31
  95. package/dist/core/settings-diagnostics.d.ts +8 -0
  96. package/dist/core/settings-diagnostics.js +20 -0
  97. package/dist/core/settings-manager.d.ts +62 -32
  98. package/dist/core/settings-manager.js +147 -69
  99. package/dist/core/skills.d.ts +5 -2
  100. package/dist/core/skills.js +58 -35
  101. package/dist/core/slash-commands.js +2 -1
  102. package/dist/core/source-info.d.ts +8 -0
  103. package/dist/core/source-info.js +16 -0
  104. package/dist/core/system-prompt.d.ts +9 -3
  105. package/dist/core/system-prompt.js +9 -12
  106. package/dist/core/system-prompt.test.js +2 -7
  107. package/dist/core/tools/bash.d.ts +12 -0
  108. package/dist/core/tools/bash.js +33 -2
  109. package/dist/core/tools/edit-diff.d.ts +0 -5
  110. package/dist/core/tools/edit-diff.js +2 -5
  111. package/dist/core/tools/edit.d.ts +4 -0
  112. package/dist/core/tools/edit.js +31 -11
  113. package/dist/core/tools/find.d.ts +7 -0
  114. package/dist/core/tools/find.js +16 -19
  115. package/dist/core/tools/grep.d.ts +4 -0
  116. package/dist/core/tools/grep.js +5 -1
  117. package/dist/core/tools/index.d.ts +1 -1
  118. package/dist/core/tools/index.js +11 -2
  119. package/dist/core/tools/ls.d.ts +4 -0
  120. package/dist/core/tools/ls.js +5 -1
  121. package/dist/core/tools/output-accumulator.d.ts +11 -0
  122. package/dist/core/tools/output-accumulator.js +40 -10
  123. package/dist/core/tools/read.d.ts +17 -2
  124. package/dist/core/tools/read.js +23 -2
  125. package/dist/core/tools/render-utils.d.ts +6 -0
  126. package/dist/core/tools/render-utils.js +26 -0
  127. package/dist/core/tools/renderers/bash.js +19 -35
  128. package/dist/core/tools/renderers/edit.js +5 -4
  129. package/dist/core/tools/renderers/index.d.ts +2 -2
  130. package/dist/core/tools/renderers/read.js +3 -2
  131. package/dist/core/tools/tool-definition-wrapper.d.ts +5 -3
  132. package/dist/core/tools/tool-definition-wrapper.js +3 -1
  133. package/dist/core/tools/truncate.d.ts +14 -0
  134. package/dist/core/tools/truncate.js +29 -0
  135. package/dist/core/tools/write.d.ts +4 -0
  136. package/dist/core/tools/write.js +6 -2
  137. package/dist/core/trust-manager.js +3 -1
  138. package/dist/core/usage-totals.d.ts +2 -0
  139. package/dist/core/usage-totals.js +23 -0
  140. package/dist/core/virtual-models.d.ts +116 -0
  141. package/dist/core/virtual-models.js +123 -0
  142. package/dist/defaults/models.json +32 -0
  143. package/dist/defaults/settings.json +1 -9
  144. package/dist/extensions/codemode/execute.d.ts +15 -0
  145. package/dist/extensions/codemode/execute.js +645 -0
  146. package/dist/extensions/codemode/execute.lazy.d.ts +2 -0
  147. package/dist/extensions/codemode/execute.lazy.js +2 -0
  148. package/dist/extensions/codemode/index.d.ts +20 -0
  149. package/dist/extensions/codemode/index.js +31 -0
  150. package/dist/extensions/codemode/renderer.d.ts +11 -0
  151. package/dist/extensions/codemode/renderer.js +136 -0
  152. package/dist/extensions/codemode/tool.d.ts +129 -0
  153. package/dist/extensions/codemode/tool.js +275 -0
  154. package/dist/extensions/context-compaction-reminder.ts +1 -1
  155. package/dist/extensions/grep-app/index.ts +2 -10
  156. package/dist/extensions/herdr-agent-state.ts +1 -1
  157. package/dist/extensions/{pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/index.d.ts → index.d.ts} +0 -1
  158. package/dist/extensions/index.js +12 -0
  159. package/dist/extensions/jev/classifier-context.ts +54 -0
  160. package/dist/extensions/jev/classifier.test.ts +150 -0
  161. package/dist/extensions/jev/classifier.ts +191 -0
  162. package/dist/extensions/jev/decisions.test.ts +56 -47
  163. package/dist/extensions/jev/decisions.ts +94 -109
  164. package/dist/extensions/jev/test-support.ts +33 -38
  165. package/dist/extensions/jev-advisory-lifecycle.test.ts +12 -12
  166. package/dist/extensions/jev-advisory-memory.test.ts +9 -9
  167. package/dist/extensions/jev-advisory-recommendations.test.ts +8 -8
  168. package/dist/extensions/jev-ask-tool.test.ts +41 -45
  169. package/dist/extensions/jev-ask-tool.ts +11 -39
  170. package/dist/extensions/jev-find-source.test.ts +26 -0
  171. package/dist/extensions/jev-find-source.ts +58 -19
  172. package/dist/extensions/jev-tool-ranker.test.ts +185 -0
  173. package/dist/extensions/jev-tool-ranker.ts +169 -0
  174. package/dist/{core → extensions}/llama/client.d.ts +2 -0
  175. package/dist/{core → extensions}/llama/client.js +7 -3
  176. package/dist/{core → extensions}/llama/index.js +13 -2
  177. package/dist/{core → extensions}/llama/provider.d.ts +1 -1
  178. package/dist/extensions/llama/provider.js +235 -0
  179. package/dist/extensions/mcp/cli.d.ts +20 -0
  180. package/dist/extensions/mcp/cli.js +496 -0
  181. package/dist/extensions/mcp/cli.lazy.d.ts +2 -0
  182. package/dist/extensions/mcp/cli.lazy.js +2 -0
  183. package/dist/extensions/mcp/config.d.ts +83 -0
  184. package/dist/extensions/mcp/config.js +193 -0
  185. package/dist/extensions/mcp/index.d.ts +78 -0
  186. package/dist/extensions/mcp/index.js +1201 -0
  187. package/dist/extensions/mcp/log.d.ts +15 -0
  188. package/dist/extensions/mcp/log.js +65 -0
  189. package/dist/extensions/mcp/oauth.d.ts +97 -0
  190. package/dist/extensions/mcp/oauth.js +401 -0
  191. package/dist/extensions/mcp/resources.d.ts +41 -0
  192. package/dist/extensions/mcp/resources.js +260 -0
  193. package/dist/extensions/mcp/runtime.d.ts +96 -0
  194. package/dist/extensions/mcp/runtime.js +412 -0
  195. package/dist/extensions/mcp/runtime.lazy.d.ts +2 -0
  196. package/dist/extensions/mcp/runtime.lazy.js +2 -0
  197. package/dist/extensions/mcp/tools.d.ts +86 -0
  198. package/dist/extensions/mcp/tools.js +252 -0
  199. package/dist/extensions/mcp/ui.d.ts +59 -0
  200. package/dist/extensions/mcp/ui.js +185 -0
  201. package/dist/extensions/package.json +2 -1
  202. package/dist/extensions/pi-graft/index.ts +4 -19
  203. package/dist/extensions/pi-graft/lifecycle.test.ts +32 -25
  204. package/dist/extensions/pi-graft/tools.ts +6 -0
  205. package/dist/extensions/pi-hermes-memory/CHANGELOG.md +25 -0
  206. package/dist/extensions/pi-hermes-memory/README.md +140 -4
  207. package/dist/extensions/pi-hermes-memory/package-lock.json +624 -2720
  208. package/dist/extensions/pi-hermes-memory/package.json +9 -7
  209. package/dist/extensions/pi-hermes-memory/scripts/benchmark-memory-startup.mjs +72 -0
  210. package/dist/extensions/pi-hermes-memory/scripts/check-production-install.mjs +107 -0
  211. package/dist/extensions/pi-hermes-memory/src/auto-consolidation-warning.ts +2 -1
  212. package/dist/extensions/pi-hermes-memory/src/config.ts +35 -0
  213. package/dist/extensions/pi-hermes-memory/src/constants.ts +62 -15
  214. package/dist/extensions/pi-hermes-memory/src/handlers/auto-consolidate.ts +374 -50
  215. package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +7 -4
  216. package/dist/extensions/pi-hermes-memory/src/handlers/correction-detector.ts +19 -14
  217. package/dist/extensions/pi-hermes-memory/src/handlers/insights.ts +75 -1
  218. package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +322 -22
  219. package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +8 -0
  220. package/dist/extensions/pi-hermes-memory/src/handlers/session-flush.ts +193 -54
  221. package/dist/extensions/pi-hermes-memory/src/handlers/skills-command.ts +6 -2
  222. package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +33 -5
  223. package/dist/extensions/pi-hermes-memory/src/index.ts +116 -47
  224. package/dist/extensions/pi-hermes-memory/src/memory-initialization.ts +101 -0
  225. package/dist/extensions/pi-hermes-memory/src/store/atomic-lock-coordinator.ts +10 -2
  226. package/dist/extensions/pi-hermes-memory/src/store/db.ts +34 -9
  227. package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +44 -8
  228. package/dist/extensions/pi-hermes-memory/src/store/schema.ts +3 -1
  229. package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +141 -0
  230. package/dist/extensions/pi-hermes-memory/src/store/standing-instructions.ts +17 -6
  231. package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +20 -10
  232. package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +1 -17
  233. package/dist/extensions/pi-hermes-memory/src/tools/session-search-tool.ts +2 -10
  234. package/dist/extensions/pi-hermes-memory/src/tools/skill-tool.ts +4 -8
  235. package/dist/extensions/pi-hermes-memory/src/types.ts +48 -0
  236. package/dist/extensions/pi-hermes-memory/tests/config.test.ts +108 -0
  237. package/dist/extensions/pi-hermes-memory/tests/handlers/auto-consolidate.test.ts +1185 -42
  238. package/dist/extensions/pi-hermes-memory/tests/handlers/insights.test.ts +57 -1
  239. package/dist/extensions/pi-hermes-memory/tests/handlers/lazy-memory-hooks.test.ts +128 -0
  240. package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +844 -1
  241. package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +16 -0
  242. package/dist/extensions/pi-hermes-memory/tests/handlers/session-flush.test.ts +512 -3
  243. package/dist/extensions/pi-hermes-memory/tests/handlers/skills-command.test.ts +38 -0
  244. package/dist/extensions/pi-hermes-memory/tests/handlers/sync-markdown-memories.test.ts +183 -2
  245. package/dist/extensions/pi-hermes-memory/tests/handlers/system-prompt.test.ts +1 -3
  246. package/dist/extensions/pi-hermes-memory/tests/index.test.ts +17 -0
  247. package/dist/extensions/pi-hermes-memory/tests/integration/corruption-recovery-integration.test.ts +21 -0
  248. package/dist/extensions/pi-hermes-memory/tests/lazy-startup.test.ts +313 -0
  249. package/dist/extensions/pi-hermes-memory/tests/memory-initialization.test.ts +182 -0
  250. package/dist/extensions/pi-hermes-memory/tests/prompt-injection.test.ts +184 -0
  251. package/dist/extensions/pi-hermes-memory/tests/skill-discovery-contract.test.ts +60 -0
  252. package/dist/extensions/pi-hermes-memory/tests/store/atomic-lock-lazy-probe.test.ts +80 -0
  253. package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +2 -0
  254. package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +79 -0
  255. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +268 -0
  256. package/dist/extensions/pi-hermes-memory/tests/store/standing-instructions.test.ts +29 -0
  257. package/dist/extensions/pi-hermes-memory/tests/store/usage-tracking.test.ts +170 -0
  258. package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +80 -1
  259. package/dist/extensions/pi-hermes-memory/tests/tools/skill-tool.test.ts +51 -0
  260. package/dist/extensions/pi-intercom/CHANGELOG.md +66 -0
  261. package/dist/extensions/pi-intercom/README.md +100 -12
  262. package/dist/extensions/pi-intercom/broker/broker.ts +28 -12
  263. package/dist/extensions/pi-intercom/broker/client.ts +3 -0
  264. package/dist/extensions/pi-intercom/broker/cross-machine-provenance.test.ts +63 -0
  265. package/dist/extensions/pi-intercom/broker/protocol.ts +64 -0
  266. package/dist/extensions/pi-intercom/broker/spawn.test.ts +58 -167
  267. package/dist/extensions/pi-intercom/broker/spawn.ts +40 -87
  268. package/dist/extensions/pi-intercom/cli.mjs +6 -0
  269. package/dist/extensions/pi-intercom/cli.test.ts +428 -0
  270. package/dist/extensions/pi-intercom/cli.ts +343 -0
  271. package/dist/extensions/pi-intercom/config.test.ts +61 -1
  272. package/dist/extensions/pi-intercom/config.ts +53 -2
  273. package/dist/extensions/pi-intercom/cross-machine-discovery.test.ts +218 -0
  274. package/dist/extensions/pi-intercom/cross-machine-discovery.ts +140 -0
  275. package/dist/extensions/pi-intercom/cross-machine-envelope.test.ts +106 -0
  276. package/dist/extensions/pi-intercom/cross-machine-envelope.ts +92 -0
  277. package/dist/extensions/pi-intercom/cross-machine-integration.test.ts +25 -0
  278. package/dist/extensions/pi-intercom/cross-machine-transport.test.ts +106 -0
  279. package/dist/extensions/pi-intercom/cross-machine-transport.ts +110 -0
  280. package/dist/extensions/pi-intercom/extension-api.ts +14 -0
  281. package/dist/extensions/pi-intercom/handover.test.ts +88 -0
  282. package/dist/extensions/pi-intercom/handover.ts +115 -0
  283. package/dist/extensions/pi-intercom/herdr-location.test.ts +173 -0
  284. package/dist/extensions/pi-intercom/herdr-location.ts +137 -0
  285. package/dist/extensions/pi-intercom/human-priority.pi.test.ts +489 -0
  286. package/dist/extensions/pi-intercom/index.ts +582 -164
  287. package/dist/extensions/pi-intercom/intercom.integration.test.ts +590 -22
  288. package/dist/extensions/pi-intercom/package.json +12 -2
  289. package/dist/extensions/pi-intercom/project-agent.ts +1 -1
  290. package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +26 -2
  291. package/dist/extensions/pi-intercom/test/handover-picker.test.ts +134 -0
  292. package/dist/extensions/pi-intercom/test/inline-message.test.ts +43 -0
  293. package/dist/extensions/pi-intercom/types.d.ts +247 -0
  294. package/dist/extensions/pi-intercom/types.js +2 -0
  295. package/dist/extensions/pi-intercom/types.ts +39 -1
  296. package/dist/extensions/pi-intercom/ui/handover-picker.ts +318 -0
  297. package/dist/extensions/pi-intercom/ui/inline-message.ts +10 -3
  298. package/dist/extensions/pi-intercom/ui/session-list.ts +28 -15
  299. package/dist/extensions/pi-subagents/.github/workflows/release.yml +5 -1
  300. package/dist/extensions/pi-subagents/.github/workflows/test.yml +188 -18
  301. package/dist/extensions/pi-subagents/CHANGELOG.md +514 -1
  302. package/dist/extensions/pi-subagents/README.md +7 -5
  303. package/dist/extensions/pi-subagents/VISION.md +6 -0
  304. package/dist/extensions/pi-subagents/agents/reviewer.md +7 -5
  305. package/dist/extensions/pi-subagents/agents/worker.md +1 -1
  306. package/dist/extensions/pi-subagents/docs/agents.md +75 -35
  307. package/dist/extensions/pi-subagents/docs/configuration.md +156 -25
  308. package/dist/extensions/pi-subagents/docs/extension-api.md +113 -29
  309. package/dist/extensions/pi-subagents/docs/missions.md +46 -14
  310. package/dist/extensions/pi-subagents/docs/models.md +22 -84
  311. package/dist/extensions/pi-subagents/docs/observability.md +49 -24
  312. package/dist/extensions/pi-subagents/docs/standalone-background.md +59 -0
  313. package/dist/extensions/pi-subagents/docs/tool-reference.md +167 -116
  314. package/dist/extensions/pi-subagents/docs/watchdog.md +41 -10
  315. package/dist/extensions/pi-subagents/docs/workflows.md +239 -163
  316. package/dist/extensions/pi-subagents/examples/typed-gate/.pi/agents/classifier.md +14 -0
  317. package/dist/extensions/pi-subagents/examples/typed-gate/README.md +42 -0
  318. package/dist/extensions/pi-subagents/examples/typed-gate/classify +38 -0
  319. package/dist/extensions/pi-subagents/examples/typed-gate/workflow.js +32 -0
  320. package/dist/extensions/pi-subagents/index.ts +11 -3
  321. package/dist/extensions/pi-subagents/inspector-runner.mjs +3 -3
  322. package/dist/extensions/pi-subagents/package-lock.json +517 -1129
  323. package/dist/extensions/pi-subagents/package.json +22 -11
  324. package/dist/extensions/pi-subagents/prompts/council.md +7 -4
  325. package/dist/extensions/pi-subagents/prompts/parallel-review.md +2 -2
  326. package/dist/extensions/pi-subagents/prompts/review-loop.md +2 -2
  327. package/dist/extensions/pi-subagents/runner-peer-loader.mjs +21 -0
  328. package/dist/extensions/pi-subagents/runner-peer-preload.mjs +30 -0
  329. package/dist/extensions/pi-subagents/scripts/build-package.mjs +79 -0
  330. package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +2 -2
  331. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +46 -27
  332. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +11 -7
  333. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +78 -73
  334. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +4 -5
  335. package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +10 -2
  336. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +69 -67
  337. package/dist/extensions/pi-subagents/skills/pi-subagents/references/review-and-validation.md +3 -3
  338. package/dist/extensions/pi-subagents/src/agents/advertised-agent-prompt.ts +21 -10
  339. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +113 -113
  340. package/dist/extensions/pi-subagents/src/agents/agent-memory.ts +91 -4
  341. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +15 -9
  342. package/dist/extensions/pi-subagents/src/agents/agents.ts +432 -193
  343. package/dist/extensions/pi-subagents/src/agents/chain-serializer.ts +6 -1
  344. package/dist/extensions/pi-subagents/src/agents/global-npm-root.ts +58 -0
  345. package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +5 -2
  346. package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +22 -18
  347. package/dist/extensions/pi-subagents/src/agents/skills.ts +43 -10
  348. package/dist/extensions/pi-subagents/src/api/delegation.ts +20 -1
  349. package/dist/extensions/pi-subagents/src/api/inspectors.ts +27 -0
  350. package/dist/extensions/pi-subagents/src/api/preflight.ts +140 -62
  351. package/dist/extensions/pi-subagents/src/api/required-child-extensions.ts +6 -0
  352. package/dist/extensions/pi-subagents/src/api/shared-types.ts +2 -0
  353. package/dist/extensions/pi-subagents/src/extension/config.ts +24 -40
  354. package/dist/extensions/pi-subagents/src/extension/control-notices.ts +4 -1
  355. package/dist/extensions/pi-subagents/src/extension/doctor.ts +18 -15
  356. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +87 -13
  357. package/dist/extensions/pi-subagents/src/extension/herdr-pi-bridge.ts +162 -0
  358. package/dist/extensions/pi-subagents/src/extension/index.ts +280 -268
  359. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +76 -37
  360. package/dist/extensions/pi-subagents/src/extension/reply-workflow-script.ts +67 -0
  361. package/dist/extensions/pi-subagents/src/extension/rpc.ts +34 -11
  362. package/dist/extensions/pi-subagents/src/extension/schemas.ts +116 -205
  363. package/dist/extensions/pi-subagents/src/extension/subagent-guide.ts +13 -5
  364. package/dist/extensions/pi-subagents/src/extension/tool-activation.ts +146 -0
  365. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +77 -90
  366. package/dist/extensions/pi-subagents/src/extension/upgrade-notice.ts +40 -0
  367. package/dist/extensions/pi-subagents/src/inspectors/actions.ts +148 -0
  368. package/dist/extensions/pi-subagents/src/inspectors/ghostty/actions.ts +74 -0
  369. package/dist/extensions/pi-subagents/src/inspectors/ghostty/plugin.ts +29 -0
  370. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +100 -180
  371. package/dist/extensions/pi-subagents/src/inspectors/herdr/plugin.ts +20 -0
  372. package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +2 -2
  373. package/dist/extensions/pi-subagents/src/inspectors/{herdr/inspector-runner.ts → inspector-runner.ts} +13 -13
  374. package/dist/extensions/pi-subagents/src/inspectors/plugins.ts +75 -0
  375. package/dist/extensions/pi-subagents/src/inspectors/{herdr/session-roots-codec.ts → session-roots-codec.ts} +4 -15
  376. package/dist/extensions/pi-subagents/src/inspectors/{herdr/shell-command.ts → shell-command.ts} +1 -1
  377. package/dist/extensions/pi-subagents/src/inspectors/types.ts +51 -0
  378. package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +38 -53
  379. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +53 -9
  380. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +69 -55
  381. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +7 -2
  382. package/dist/extensions/pi-subagents/src/missions/goal-driver.ts +45 -32
  383. package/dist/extensions/pi-subagents/src/missions/store.ts +11 -2
  384. package/dist/extensions/pi-subagents/src/policy/authority.ts +5 -1
  385. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +30 -18
  386. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +59 -9
  387. package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +18 -2
  388. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +463 -248
  389. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +106 -66
  390. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +33 -20
  391. package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +47 -17
  392. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -5
  393. package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +24 -11
  394. package/dist/extensions/pi-subagents/src/runs/background/await-async-run.ts +78 -0
  395. package/dist/extensions/pi-subagents/src/runs/background/binary-bootstrap.ts +66 -0
  396. package/dist/extensions/pi-subagents/src/runs/background/calendar-schedule.ts +90 -0
  397. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +2 -2
  398. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +19 -38
  399. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +62 -3
  400. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +82 -10
  401. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +259 -21
  402. package/dist/extensions/pi-subagents/src/runs/background/owned-process-tree.ts +30 -3
  403. package/dist/extensions/pi-subagents/src/runs/background/pid-namespace.ts +18 -0
  404. package/dist/extensions/pi-subagents/src/runs/background/process-terminal-candidate.ts +98 -0
  405. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +18 -97
  406. package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +14 -5
  407. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +66 -12
  408. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +4 -3
  409. package/dist/extensions/pi-subagents/src/runs/background/run-child-session.ts +126 -45
  410. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +11 -6
  411. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +18 -7
  412. package/dist/extensions/pi-subagents/src/runs/background/runner-aliases.ts +43 -51
  413. package/dist/extensions/pi-subagents/src/runs/background/runner-child-launch.ts +8 -1
  414. package/dist/extensions/pi-subagents/src/runs/background/runner-child-sessions.ts +5 -4
  415. package/dist/extensions/pi-subagents/src/runs/background/runner-http-dispatcher.ts +119 -0
  416. package/dist/extensions/pi-subagents/src/runs/background/runner-startup-failure.ts +56 -0
  417. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +233 -69
  418. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +67 -16
  419. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +21 -3
  420. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner-bootstrap.ts +276 -0
  421. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +592 -573
  422. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +177 -29
  423. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +77 -28
  424. package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +10 -4
  425. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +7 -13
  426. package/dist/extensions/pi-subagents/src/runs/background/workflow-terminal-proof.ts +41 -23
  427. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +19 -8
  428. package/dist/extensions/pi-subagents/src/runs/foreground/async-stop-action.ts +130 -7
  429. package/dist/extensions/pi-subagents/src/runs/foreground/command-action.ts +41 -0
  430. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +267 -385
  431. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +4 -2
  432. package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +6 -10
  433. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1158 -413
  434. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +3 -1
  435. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +2 -1
  436. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +200 -74
  437. package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +133 -34
  438. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +6 -1
  439. package/dist/extensions/pi-subagents/src/runs/shared/child-commands.ts +228 -0
  440. package/dist/extensions/pi-subagents/src/runs/shared/child-hooks.ts +18 -18
  441. package/dist/extensions/pi-subagents/src/runs/shared/child-launch-plan.ts +16 -4
  442. package/dist/extensions/pi-subagents/src/runs/shared/child-launch.ts +55 -12
  443. package/dist/extensions/pi-subagents/src/runs/shared/child-lifecycle.ts +6 -3
  444. package/dist/extensions/pi-subagents/src/runs/shared/child-runtime-config.ts +11 -1
  445. package/dist/extensions/pi-subagents/src/runs/shared/child-session.ts +367 -74
  446. package/dist/extensions/pi-subagents/src/runs/shared/child-tool-plan.ts +50 -86
  447. package/dist/extensions/pi-subagents/src/runs/shared/claude-code-adapter.ts +288 -0
  448. package/dist/extensions/pi-subagents/src/runs/shared/codex-exec-adapter.ts +129 -0
  449. package/dist/extensions/pi-subagents/src/runs/shared/cursor-agent-adapter.ts +114 -0
  450. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +5 -5
  451. package/dist/extensions/pi-subagents/src/runs/shared/effective-system-prompt.ts +33 -0
  452. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-contract.ts +49 -10
  453. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +61 -9
  454. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +16 -9
  455. package/dist/extensions/pi-subagents/src/runs/shared/external-job-bridge.ts +55 -1
  456. package/dist/extensions/pi-subagents/src/runs/shared/git-environment.ts +1 -1
  457. package/dist/extensions/pi-subagents/src/runs/shared/herdr-connection.ts +134 -0
  458. package/dist/extensions/pi-subagents/src/runs/shared/herdr-external-adapters.ts +169 -0
  459. package/dist/extensions/pi-subagents/src/runs/shared/herdr-machine.ts +279 -0
  460. package/dist/extensions/pi-subagents/src/runs/shared/herdr-pi-protocol.ts +59 -0
  461. package/dist/extensions/pi-subagents/src/runs/shared/herdr-placed-run.ts +263 -0
  462. package/dist/extensions/pi-subagents/src/runs/shared/mcp-config-sources.ts +6 -2
  463. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +108 -13
  464. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-grant.ts +23 -7
  465. package/dist/extensions/pi-subagents/src/runs/shared/model-resolution-diagnostic.ts +76 -0
  466. package/dist/extensions/pi-subagents/src/runs/shared/{model-fallback.ts → model-resolution.ts} +65 -245
  467. package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +30 -11
  468. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +24 -3
  469. package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +5 -4
  470. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +55 -7
  471. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +14 -4
  472. package/dist/extensions/pi-subagents/src/runs/shared/partial-output.ts +48 -0
  473. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +12 -4
  474. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +88 -1
  475. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +12 -3
  476. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +160 -9
  477. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +35 -34
  478. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +58 -19
  479. package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +2 -2
  480. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +21 -0
  481. package/dist/extensions/pi-subagents/src/runs/shared/usage-reconciliation.ts +73 -0
  482. package/dist/extensions/pi-subagents/src/runs/shared/workflow-async-child-guidance.ts +18 -0
  483. package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-apply.ts +160 -0
  484. package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +42 -20
  485. package/dist/extensions/pi-subagents/src/runs/shared/worktree-lock.ts +29 -0
  486. package/dist/extensions/pi-subagents/src/runs/shared/worktree-setup-command.ts +27 -4
  487. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +94 -26
  488. package/dist/extensions/pi-subagents/src/shared/child-cache-retention.ts +43 -0
  489. package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +5 -3
  490. package/dist/extensions/pi-subagents/src/shared/disabled-features.ts +123 -0
  491. package/dist/extensions/pi-subagents/src/shared/extension-context.ts +14 -1
  492. package/dist/extensions/pi-subagents/src/shared/file-lease.ts +74 -0
  493. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +27 -77
  494. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +70 -10
  495. package/dist/extensions/pi-subagents/src/shared/model-info.ts +10 -1
  496. package/dist/extensions/pi-subagents/src/shared/opencode-session-headers.ts +30 -0
  497. package/dist/extensions/pi-subagents/src/shared/package-version.ts +17 -0
  498. package/dist/extensions/pi-subagents/src/shared/parent-wake.ts +66 -0
  499. package/dist/extensions/pi-subagents/src/shared/pruned-fork.ts +71 -58
  500. package/dist/extensions/pi-subagents/src/shared/required-child-extensions.ts +115 -0
  501. package/dist/extensions/pi-subagents/src/shared/settings-file-lease.ts +36 -0
  502. package/dist/extensions/pi-subagents/src/shared/settings.ts +7 -4
  503. package/dist/extensions/pi-subagents/src/shared/shortcuts.ts +1 -5
  504. package/dist/extensions/pi-subagents/src/shared/types.ts +146 -48
  505. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +28 -2
  506. package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +15 -1
  507. package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +19 -4
  508. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +13 -2
  509. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +15 -15
  510. package/dist/extensions/pi-subagents/src/slash/subagent-cost.ts +46 -9
  511. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +29 -16
  512. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +201 -26
  513. package/dist/extensions/pi-subagents/src/tui/fleet.ts +33 -16
  514. package/dist/extensions/pi-subagents/src/tui/render.ts +334 -103
  515. package/dist/extensions/pi-subagents/src/tui/running-tone.ts +28 -0
  516. package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +27 -28
  517. package/dist/extensions/pi-subagents/src/watchdog/diff-tool.ts +47 -12
  518. package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +2 -2
  519. package/dist/extensions/pi-subagents/src/watchdog/model-selection.ts +22 -2
  520. package/dist/extensions/pi-subagents/src/watchdog/permission-arbiter.ts +17 -17
  521. package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +24 -8
  522. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +43 -39
  523. package/dist/extensions/pi-subagents/src/watchdog/render.ts +2 -2
  524. package/dist/extensions/pi-subagents/src/watchdog/review.ts +127 -78
  525. package/dist/extensions/pi-subagents/src/watchdog/rules.ts +1 -1
  526. package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +107 -31
  527. package/dist/extensions/pi-subagents/src/watchdog/scope.ts +1 -1
  528. package/dist/extensions/pi-subagents/src/watchdog/settings.ts +22 -14
  529. package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +14 -13
  530. package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +35 -6
  531. package/dist/extensions/pi-subagents/src/watchdog/types.ts +6 -4
  532. package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +2 -2
  533. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -3
  534. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +500 -65
  535. package/dist/extensions/pi-subagents/src/workflows/structured-workflow-scripts.ts +177 -0
  536. package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +8 -2
  537. package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +5 -3
  538. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +70 -10
  539. package/dist/extensions/pi-subagents/src/workflows/workflow-resources.ts +57 -18
  540. package/dist/extensions/pi-subagents/src/workflows/workflow-reuse.ts +120 -0
  541. package/dist/extensions/pi-subagents/src/workflows/workflow-revival.ts +123 -0
  542. package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +5 -2
  543. package/dist/extensions/pi-subagents/test/fixtures/cwd-workflow-child.ts +37 -0
  544. package/dist/extensions/pi-subagents/test/fixtures/herdr-allocation-lock-child.ts +16 -0
  545. package/dist/extensions/pi-subagents/test/fixtures/herdr-provision-child.ts +15 -0
  546. package/dist/extensions/pi-subagents/test/fixtures/native-nested-wait-provider.ts +76 -0
  547. package/dist/extensions/pi-subagents/test/fixtures/runner-bootstrap-harness.ts +36 -0
  548. package/dist/extensions/pi-subagents/test/fixtures/schedule-history-writer.mjs +48 -0
  549. package/dist/extensions/pi-subagents/test/fixtures/settings-race-writer.mjs +57 -0
  550. package/dist/extensions/pi-subagents/test/fixtures/workflow-cwd-access-failure.ts +33 -0
  551. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +40 -7
  552. package/dist/extensions/pi-subagents/test/integration/async-attention-clearing.test.ts +9 -8
  553. package/dist/extensions/pi-subagents/test/integration/async-execution.part-1.test.ts +435 -276
  554. package/dist/extensions/pi-subagents/test/integration/async-execution.part-2.test.ts +435 -397
  555. package/dist/extensions/pi-subagents/test/integration/async-execution.part-3.test.ts +918 -497
  556. package/dist/extensions/pi-subagents/test/integration/async-execution.part-4.test.ts +264 -17
  557. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +185 -22
  558. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +3 -2
  559. package/dist/extensions/pi-subagents/test/integration/claude-code-dispatch.test.ts +127 -0
  560. package/dist/extensions/pi-subagents/test/integration/deadline-checkpoint.test.ts +126 -0
  561. package/dist/extensions/pi-subagents/test/integration/doctor-executor.test.ts +29 -1
  562. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +88 -0
  563. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +450 -13
  564. package/dist/extensions/pi-subagents/test/integration/foreign-workflow-steering.test.ts +10 -6
  565. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +105 -86
  566. package/dist/extensions/pi-subagents/test/integration/in-process-child.test.ts +403 -18
  567. package/dist/extensions/pi-subagents/test/integration/inline-workflow-visibility.test.ts +351 -0
  568. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +195 -10
  569. package/dist/extensions/pi-subagents/test/integration/model-resolution-diagnostic.test.ts +108 -0
  570. package/dist/extensions/pi-subagents/test/integration/nested-async-wait.test.ts +61 -0
  571. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +5 -4
  572. package/dist/extensions/pi-subagents/test/integration/recovered-assistant-error.test.ts +1 -1
  573. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +6 -4
  574. package/dist/extensions/pi-subagents/test/integration/render-widget-mouse.test.ts +84 -0
  575. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +242 -16
  576. package/dist/extensions/pi-subagents/test/integration/result-publication.test.ts +2 -2
  577. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +158 -4
  578. package/dist/extensions/pi-subagents/test/integration/single-execution.part-1.test.ts +1435 -248
  579. package/dist/extensions/pi-subagents/test/integration/single-execution.part-2.test.ts +454 -515
  580. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +140 -21
  581. package/dist/extensions/pi-subagents/test/integration/structured-workflows.test.ts +105 -0
  582. package/dist/extensions/pi-subagents/test/integration/workflow-result-publication.test.ts +90 -11
  583. package/dist/extensions/pi-subagents/test/integration/workflow-steer-inbox.test.ts +18 -15
  584. package/dist/extensions/pi-subagents/test/integration/workflow-worktree-admission.test.ts +100 -0
  585. package/dist/extensions/pi-subagents/test/smoke/clean-install.mjs +106 -0
  586. package/dist/extensions/pi-subagents/test/smoke/npm-background.mjs +43 -0
  587. package/dist/extensions/pi-subagents/test/smoke/packed-runner-selection.mjs +42 -0
  588. package/dist/extensions/pi-subagents/test/smoke/runner-loader-capability.mjs +50 -0
  589. package/dist/extensions/pi-subagents/test/smoke/sdk-child.ts +57 -0
  590. package/dist/extensions/pi-subagents/test/smoke/{pi085-extension.ts → sdk-extension.ts} +2 -2
  591. package/dist/extensions/pi-subagents/test/smoke/standalone-background.mjs +140 -0
  592. package/dist/extensions/pi-subagents/test/smoke/standalone-fault.ts +43 -0
  593. package/dist/extensions/pi-subagents/test/smoke/standalone-matrix.mjs +65 -0
  594. package/dist/extensions/pi-subagents/test/smoke/standalone-observer.ts +8 -0
  595. package/dist/extensions/pi-subagents/test/smoke/standalone-parent.ts +220 -0
  596. package/dist/extensions/pi-subagents/test/smoke/standalone-provider.ts +58 -0
  597. package/dist/extensions/pi-subagents/test/smoke/standalone-release.json +8 -0
  598. package/dist/extensions/pi-subagents/test/smoke/standalone-revival.ts +109 -0
  599. package/dist/extensions/pi-subagents/test/smoke/standalone-shared.ts +79 -0
  600. package/dist/extensions/pi-subagents/test/smoke/tool-activation.test.ts +128 -0
  601. package/dist/extensions/pi-subagents/test/support/async-execution-fixture.ts +6 -25
  602. package/dist/extensions/pi-subagents/test/support/async-result-timeout-evidence.ts +61 -0
  603. package/dist/extensions/pi-subagents/test/support/fake-child-session.ts +122 -9
  604. package/dist/extensions/pi-subagents/test/support/foreign-workflow-steer-host.mjs +15 -7
  605. package/dist/extensions/pi-subagents/test/support/helpers.ts +11 -14
  606. package/dist/extensions/pi-subagents/test/support/isolated-temp-root.mjs +21 -3
  607. package/dist/extensions/pi-subagents/test/support/native-peer-loader.mjs +15 -0
  608. package/dist/extensions/pi-subagents/test/support/node-command.ts +29 -6
  609. package/dist/extensions/pi-subagents/test/support/notify-diagnostics-fixture.ts +5 -3
  610. package/dist/extensions/pi-subagents/test/support/single-execution-fixture.ts +7 -15
  611. package/dist/extensions/pi-subagents/test/support/watchdog-stable-host-loader.mjs +23 -0
  612. package/dist/extensions/pi-subagents/test/unit/acceptance-compaction.test.ts +3 -3
  613. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +158 -155
  614. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +52 -1
  615. package/dist/extensions/pi-subagents/test/unit/advertised-agent-prompt.test.ts +21 -17
  616. package/dist/extensions/pi-subagents/test/unit/advertised-agent-refresh.test.ts +105 -36
  617. package/dist/extensions/pi-subagents/test/unit/advertisement-barrier.test.ts +151 -0
  618. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +1 -2
  619. package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +33 -3
  620. package/dist/extensions/pi-subagents/test/unit/agent-end-auto-drain.test.ts +149 -0
  621. package/dist/extensions/pi-subagents/test/unit/agent-exclude-dirs.test.ts +246 -0
  622. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +62 -87
  623. package/dist/extensions/pi-subagents/test/unit/agent-global-root-override.test.ts +86 -0
  624. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +398 -48
  625. package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +126 -1
  626. package/dist/extensions/pi-subagents/test/unit/agent-name-resolution.test.ts +75 -0
  627. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +94 -42
  628. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +73 -3
  629. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +428 -2
  630. package/dist/extensions/pi-subagents/test/unit/async-permission-session.test.ts +4 -4
  631. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +112 -1
  632. package/dist/extensions/pi-subagents/test/unit/async-result-timeout-evidence.test.ts +53 -0
  633. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +92 -4
  634. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +58 -6
  635. package/dist/extensions/pi-subagents/test/unit/async-run-history.test.ts +123 -0
  636. package/dist/extensions/pi-subagents/test/unit/async-spawn-package-dir.test.ts +203 -0
  637. package/dist/extensions/pi-subagents/test/unit/async-spawn-preload.test.ts +26 -35
  638. package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +185 -0
  639. package/dist/extensions/pi-subagents/test/unit/authority-policy.test.ts +9 -1
  640. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +106 -1
  641. package/dist/extensions/pi-subagents/test/unit/await-async-run.test.ts +202 -0
  642. package/dist/extensions/pi-subagents/test/unit/binary-async-spawn.test.ts +115 -0
  643. package/dist/extensions/pi-subagents/test/unit/binary-pi-spawn.test.ts +27 -0
  644. package/dist/extensions/pi-subagents/test/unit/calendar-schedule.test.ts +70 -0
  645. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +38 -0
  646. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +10 -2
  647. package/dist/extensions/pi-subagents/test/unit/changelog-format.test.ts +11 -0
  648. package/dist/extensions/pi-subagents/test/unit/child-cache-retention.test.ts +87 -0
  649. package/dist/extensions/pi-subagents/test/unit/child-commands.test.ts +243 -0
  650. package/dist/extensions/pi-subagents/test/unit/child-external-job-bridge.test.ts +126 -0
  651. package/dist/extensions/pi-subagents/test/unit/child-launch-plan.test.ts +12 -1
  652. package/dist/extensions/pi-subagents/test/unit/child-lifecycle.test.ts +36 -0
  653. package/dist/extensions/pi-subagents/test/unit/child-runtime-config.test.ts +24 -0
  654. package/dist/extensions/pi-subagents/test/unit/child-session-commands.test.ts +162 -0
  655. package/dist/extensions/pi-subagents/test/unit/child-session-host-sdk.test.ts +226 -0
  656. package/dist/extensions/pi-subagents/test/unit/child-session-parent-providers.test.ts +200 -0
  657. package/dist/extensions/pi-subagents/test/unit/child-session-queued.test.ts +260 -0
  658. package/dist/extensions/pi-subagents/test/unit/child-tool-plan-permission-system.test.ts +3 -98
  659. package/dist/extensions/pi-subagents/test/unit/child-tool-plan.test.ts +222 -1
  660. package/dist/extensions/pi-subagents/test/unit/claude-code-adapter.test.ts +314 -0
  661. package/dist/extensions/pi-subagents/test/unit/claude-code-dispatch.test.ts +182 -0
  662. package/dist/extensions/pi-subagents/test/unit/codex-exec-adapter.test.ts +157 -0
  663. package/dist/extensions/pi-subagents/test/unit/command-action.test.ts +50 -0
  664. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +25 -11
  665. package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +3 -1
  666. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +18 -0
  667. package/dist/extensions/pi-subagents/test/unit/control-notices.test.ts +7 -1
  668. package/dist/extensions/pi-subagents/test/unit/cursor-agent-adapter.test.ts +220 -0
  669. package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +104 -32
  670. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +138 -1
  671. package/dist/extensions/pi-subagents/test/unit/disabled-features.test.ts +227 -0
  672. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +48 -9
  673. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +7 -4
  674. package/dist/extensions/pi-subagents/test/unit/extension-lazy-loading.test.ts +21 -0
  675. package/dist/extensions/pi-subagents/test/unit/extension-maintenance-lifecycle.test.ts +66 -0
  676. package/dist/extensions/pi-subagents/test/unit/extension-module-preload.test.ts +49 -0
  677. package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +104 -3
  678. package/dist/extensions/pi-subagents/test/unit/file-lease.test.ts +17 -0
  679. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +316 -0
  680. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +58 -7
  681. package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +25 -96
  682. package/dist/extensions/pi-subagents/test/unit/ghostty-inspector.test.ts +92 -0
  683. package/dist/extensions/pi-subagents/test/unit/global-npm-root.test.ts +108 -0
  684. package/dist/extensions/pi-subagents/test/unit/herdr-connection.test.ts +43 -0
  685. package/dist/extensions/pi-subagents/test/unit/herdr-external-adapters.test.ts +89 -0
  686. package/dist/extensions/pi-subagents/test/unit/herdr-inspector-bootstrap.test.ts +11 -20
  687. package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +113 -19
  688. package/dist/extensions/pi-subagents/test/unit/herdr-machine.test.ts +161 -0
  689. package/dist/extensions/pi-subagents/test/unit/herdr-native-placement.test.ts +187 -0
  690. package/dist/extensions/pi-subagents/test/unit/herdr-session-roots-codec.test.ts +7 -12
  691. package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +2 -2
  692. package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +182 -87
  693. package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +68 -49
  694. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +295 -135
  695. package/dist/extensions/pi-subagents/test/unit/inspect-rpc.test.ts +3 -3
  696. package/dist/extensions/pi-subagents/test/unit/inspector-authority-policy.test.ts +144 -0
  697. package/dist/extensions/pi-subagents/test/unit/inspector-registration.test.ts +128 -0
  698. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +49 -1
  699. package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-grant.test.ts +74 -1
  700. package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-resolution.test.ts +112 -0
  701. package/dist/extensions/pi-subagents/test/unit/mission-global-projection.test.ts +215 -0
  702. package/dist/extensions/pi-subagents/test/unit/mission-goal-driver.test.ts +65 -1
  703. package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +2 -1
  704. package/dist/extensions/pi-subagents/test/unit/model-resolution-diagnostic.test.ts +78 -0
  705. package/dist/extensions/pi-subagents/test/unit/model-resolution.test.ts +153 -0
  706. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +43 -1
  707. package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +1 -21
  708. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +279 -2
  709. package/dist/extensions/pi-subagents/test/unit/nested-control-fanout.test.ts +73 -0
  710. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +297 -3
  711. package/dist/extensions/pi-subagents/test/unit/notify-diagnostics.test.ts +1 -1
  712. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +431 -23
  713. package/dist/extensions/pi-subagents/test/unit/opencode-session-headers.test.ts +32 -0
  714. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +89 -21
  715. package/dist/extensions/pi-subagents/test/unit/owned-process-tree.test.ts +31 -1
  716. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +18 -24
  717. package/dist/extensions/pi-subagents/test/unit/parent-wake.test.ts +206 -0
  718. package/dist/extensions/pi-subagents/test/unit/partial-output.test.ts +53 -0
  719. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +216 -32
  720. package/dist/extensions/pi-subagents/test/unit/pi-spawn-host-root.test.ts +82 -0
  721. package/dist/extensions/pi-subagents/test/unit/pi-subagents-policy-guidance.test.ts +28 -0
  722. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +193 -104
  723. package/dist/extensions/pi-subagents/test/unit/proactive-skills.test.ts +16 -1
  724. package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +17 -1
  725. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +89 -9
  726. package/dist/extensions/pi-subagents/test/unit/prompt-audit-optional-peers.test.ts +6 -0
  727. package/dist/extensions/pi-subagents/test/unit/prompt-audit-provider-routing.test.ts +69 -0
  728. package/dist/extensions/pi-subagents/test/unit/prompt-template-bridge.test.ts +2 -2
  729. package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +23 -1
  730. package/dist/extensions/pi-subagents/test/unit/pruned-fork.test.ts +117 -3
  731. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +35 -14
  732. package/dist/extensions/pi-subagents/test/unit/readonly-drain-observation.test.ts +3 -2
  733. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +261 -9
  734. package/dist/extensions/pi-subagents/test/unit/reply-workflow-script.test.ts +37 -0
  735. package/dist/extensions/pi-subagents/test/unit/required-child-extensions.test.ts +114 -0
  736. package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +11 -1
  737. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +22 -2
  738. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +125 -18
  739. package/dist/extensions/pi-subagents/test/unit/run-child-session.test.ts +230 -0
  740. package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +50 -1
  741. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +301 -4
  742. package/dist/extensions/pi-subagents/test/unit/runner-bootstrap.test.ts +182 -0
  743. package/dist/extensions/pi-subagents/test/unit/runner-http-dispatcher.test.ts +136 -0
  744. package/dist/extensions/pi-subagents/test/unit/runner-peer-loader.test.ts +68 -0
  745. package/dist/extensions/pi-subagents/test/unit/runner-peer-symlinks.test.ts +50 -0
  746. package/dist/extensions/pi-subagents/test/unit/running-tone.test.ts +13 -0
  747. package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +155 -4
  748. package/dist/extensions/pi-subagents/test/unit/schedule-claim-initialization.test.ts +71 -0
  749. package/dist/extensions/pi-subagents/test/unit/schedule-claim-preservation.test.ts +98 -0
  750. package/dist/extensions/pi-subagents/test/unit/schedule-history-transactions.test.ts +201 -0
  751. package/dist/extensions/pi-subagents/test/unit/scheduled-mission.test.ts +317 -0
  752. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +412 -11
  753. package/dist/extensions/pi-subagents/test/unit/scheduled-store-root.test.ts +2 -8
  754. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +70 -71
  755. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +748 -5
  756. package/dist/extensions/pi-subagents/test/unit/session-liveness-delivery.test.ts +337 -0
  757. package/dist/extensions/pi-subagents/test/unit/settings-atomic-save.test.ts +419 -0
  758. package/dist/extensions/pi-subagents/test/unit/settings-transactions.test.ts +88 -0
  759. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +13 -1
  760. package/dist/extensions/pi-subagents/test/unit/skills-disable-model-invocation.test.ts +164 -0
  761. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +35 -2
  762. package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +2 -2
  763. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +334 -9
  764. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +170 -0
  765. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +34 -5
  766. package/dist/extensions/pi-subagents/test/unit/structured-output-validation.test.ts +199 -0
  767. package/dist/extensions/pi-subagents/test/unit/structured-workflow-resources.test.ts +241 -0
  768. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +47 -38
  769. package/dist/extensions/pi-subagents/test/unit/subagent-cost.test.ts +97 -0
  770. package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +10 -0
  771. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +343 -44
  772. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +410 -11
  773. package/dist/extensions/pi-subagents/test/unit/supervisor-ask-registration.test.ts +459 -22
  774. package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +43 -3
  775. package/dist/extensions/pi-subagents/test/unit/timeout-defaults.test.ts +11 -1
  776. package/dist/extensions/pi-subagents/test/unit/tool-activation-stale-peer.test.ts +32 -0
  777. package/dist/extensions/pi-subagents/test/unit/tool-activation.test.ts +313 -0
  778. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +14 -0
  779. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +76 -89
  780. package/dist/extensions/pi-subagents/test/unit/typed-gate-output.test.ts +141 -0
  781. package/dist/extensions/pi-subagents/test/unit/upgrade-notice.test.ts +31 -0
  782. package/dist/extensions/pi-subagents/test/unit/usage-reconciliation-owners.test.ts +66 -0
  783. package/dist/extensions/pi-subagents/test/unit/usage-reconciliation.test.ts +23 -0
  784. package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +195 -1
  785. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +3 -1
  786. package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +48 -45
  787. package/dist/extensions/pi-subagents/test/unit/watchdog-compaction-scope.test.ts +94 -0
  788. package/dist/extensions/pi-subagents/test/unit/watchdog-diff-tool.test.ts +48 -3
  789. package/dist/extensions/pi-subagents/test/unit/watchdog-native-scheduling.test.ts +107 -0
  790. package/dist/extensions/pi-subagents/test/unit/watchdog-permission-arbiter.test.ts +95 -7
  791. package/dist/extensions/pi-subagents/test/unit/watchdog-register-child.test.ts +79 -0
  792. package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +7 -4
  793. package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +111 -24
  794. package/dist/extensions/pi-subagents/test/unit/watchdog-rules.test.ts +1 -1
  795. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +283 -2
  796. package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +3 -0
  797. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +12 -1
  798. package/dist/extensions/pi-subagents/test/unit/watchdog-stable-host-layout.test.ts +23 -0
  799. package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +30 -0
  800. package/dist/extensions/pi-subagents/test/unit/watchdog-turn-delta.test.ts +39 -2
  801. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +18 -1
  802. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +8 -4
  803. package/dist/extensions/pi-subagents/test/unit/workflow-child-summary.test.ts +10 -0
  804. package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +6 -3
  805. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +44 -1
  806. package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +28 -1
  807. package/dist/extensions/pi-subagents/test/unit/workflow-revival.test.ts +85 -0
  808. package/dist/extensions/pi-subagents/test/unit/workflow-scripts-feature.test.ts +177 -0
  809. package/dist/extensions/pi-subagents/test/unit/workflow-terminal-proof.test.ts +8 -0
  810. package/dist/extensions/pi-subagents/test/unit/workflow-worktree-admission.test.ts +29 -0
  811. package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +343 -4
  812. package/dist/extensions/pi-subagents/test/unit/worktree-lock.test.ts +123 -0
  813. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +252 -22
  814. package/dist/extensions/pi-subagents/tsconfig.build.json +13 -0
  815. package/dist/extensions/pi-tool-display/tests/custom-tool-overrides.test.ts +1 -1
  816. package/dist/extensions/pi-tool-display/tests/tool-overrides-config.test.ts +1 -1
  817. package/dist/extensions/pi-tool-display/tests/tool-overrides-mcp.test.ts +24 -1
  818. package/dist/extensions/pi-tool-display/tests/tool-overrides-registration.test.ts +6 -2
  819. package/dist/extensions/pi-web-agent/src/extension.ts +1 -5
  820. package/dist/extensions/pi-zentui/.github/workflows/ci.yml +67 -4
  821. package/dist/extensions/pi-zentui/AGENTS.md +3 -2
  822. package/dist/extensions/pi-zentui/README.md +60 -32
  823. package/dist/extensions/pi-zentui/docs/configuration.md +662 -51
  824. package/dist/extensions/pi-zentui/docs/footer-format.md +174 -5
  825. package/dist/extensions/pi-zentui/extensions/zentui/accent-rail-editor.ts +15 -4
  826. package/dist/extensions/pi-zentui/extensions/zentui/average-token-rate.ts +146 -0
  827. package/dist/extensions/pi-zentui/extensions/zentui/codex-quota-display.ts +34 -0
  828. package/dist/extensions/pi-zentui/extensions/zentui/codex-quota.ts +298 -0
  829. package/dist/extensions/pi-zentui/extensions/zentui/component-colors.ts +172 -0
  830. package/dist/extensions/pi-zentui/extensions/zentui/component-settings.ts +243 -0
  831. package/dist/extensions/pi-zentui/extensions/zentui/config.ts +851 -114
  832. package/dist/extensions/pi-zentui/extensions/zentui/custom-value-colors.ts +93 -0
  833. package/dist/extensions/pi-zentui/extensions/zentui/custom-variable-demand.ts +61 -0
  834. package/dist/extensions/pi-zentui/extensions/zentui/custom-variable-format.ts +80 -0
  835. package/dist/extensions/pi-zentui/extensions/zentui/custom-variables.ts +152 -0
  836. package/dist/extensions/pi-zentui/extensions/zentui/editor-metadata-format.ts +138 -23
  837. package/dist/extensions/pi-zentui/extensions/zentui/editor-mouse.ts +89 -0
  838. package/dist/extensions/pi-zentui/extensions/zentui/extension-status-controller.ts +148 -0
  839. package/dist/extensions/pi-zentui/extensions/zentui/extension-status-settings.ts +40 -0
  840. package/dist/extensions/pi-zentui/extensions/zentui/extension-status.ts +109 -1
  841. package/dist/extensions/pi-zentui/extensions/zentui/footer-format.ts +203 -23
  842. package/dist/extensions/pi-zentui/extensions/zentui/footer-layout.ts +80 -3
  843. package/dist/extensions/pi-zentui/extensions/zentui/footer-text.ts +1 -1
  844. package/dist/extensions/pi-zentui/extensions/zentui/footer.ts +432 -125
  845. package/dist/extensions/pi-zentui/extensions/zentui/format.ts +92 -4
  846. package/dist/extensions/pi-zentui/extensions/zentui/git.ts +47 -30
  847. package/dist/extensions/pi-zentui/extensions/zentui/github-status.ts +575 -0
  848. package/dist/extensions/pi-zentui/extensions/zentui/host-project-changes.ts +25 -0
  849. package/dist/extensions/pi-zentui/extensions/zentui/host-template-values.ts +29 -0
  850. package/dist/extensions/pi-zentui/extensions/zentui/icons.ts +53 -13
  851. package/dist/extensions/pi-zentui/extensions/zentui/index.ts +778 -258
  852. package/dist/extensions/pi-zentui/extensions/zentui/interaction-summary.ts +70 -5
  853. package/dist/extensions/pi-zentui/extensions/zentui/layered-editor.ts +55 -0
  854. package/dist/extensions/pi-zentui/extensions/zentui/live-context.ts +6 -1
  855. package/dist/extensions/pi-zentui/extensions/zentui/live-metadata-demand.ts +72 -0
  856. package/dist/extensions/pi-zentui/extensions/zentui/live-metadata-display.ts +21 -0
  857. package/dist/extensions/pi-zentui/extensions/zentui/live-metadata.ts +255 -0
  858. package/dist/extensions/pi-zentui/extensions/zentui/minimalist-editor.ts +476 -36
  859. package/dist/extensions/pi-zentui/extensions/zentui/minimalist-settings.ts +118 -0
  860. package/dist/extensions/pi-zentui/extensions/zentui/minimalist-template.ts +177 -0
  861. package/dist/extensions/pi-zentui/extensions/zentui/omp-editor-frame.ts +148 -0
  862. package/dist/extensions/pi-zentui/extensions/zentui/omp-statusline.ts +569 -0
  863. package/dist/extensions/pi-zentui/extensions/zentui/omp-template-lookups.ts +344 -0
  864. package/dist/extensions/pi-zentui/extensions/zentui/omp-template-metrics.ts +91 -0
  865. package/dist/extensions/pi-zentui/extensions/zentui/omp-template-modes.ts +323 -0
  866. package/dist/extensions/pi-zentui/extensions/zentui/omp-ui.ts +210 -0
  867. package/dist/extensions/pi-zentui/extensions/zentui/package-version.ts +30 -12
  868. package/dist/extensions/pi-zentui/extensions/zentui/project-demand.ts +46 -0
  869. package/dist/extensions/pi-zentui/extensions/zentui/project-discovery.ts +58 -0
  870. package/dist/extensions/pi-zentui/extensions/zentui/project-refresh.ts +10 -3
  871. package/dist/extensions/pi-zentui/extensions/zentui/project-state.ts +37 -0
  872. package/dist/extensions/pi-zentui/extensions/zentui/repository-root.ts +1 -0
  873. package/dist/extensions/pi-zentui/extensions/zentui/runtime.ts +119 -43
  874. package/dist/extensions/pi-zentui/extensions/zentui/selector-border.ts +40 -29
  875. package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +1268 -722
  876. package/dist/extensions/pi-zentui/extensions/zentui/settings-keys.ts +59 -0
  877. package/dist/extensions/pi-zentui/extensions/zentui/settings-list-selection.ts +13 -0
  878. package/dist/extensions/pi-zentui/extensions/zentui/settings-previews.ts +11 -4
  879. package/dist/extensions/pi-zentui/extensions/zentui/state.ts +24 -11
  880. package/dist/extensions/pi-zentui/extensions/zentui/style.ts +6 -0
  881. package/dist/extensions/pi-zentui/extensions/zentui/telemetry.ts +113 -11
  882. package/dist/extensions/pi-zentui/extensions/zentui/thinking-experimental.ts +234 -46
  883. package/dist/extensions/pi-zentui/extensions/zentui/thinking-steps.ts +20 -5
  884. package/dist/extensions/pi-zentui/extensions/zentui/token-rate.ts +313 -0
  885. package/dist/extensions/pi-zentui/extensions/zentui/turn-summary-format.ts +59 -0
  886. package/dist/extensions/pi-zentui/extensions/zentui/turn-summary-rate.ts +52 -0
  887. package/dist/extensions/pi-zentui/extensions/zentui/ui.ts +640 -172
  888. package/dist/extensions/pi-zentui/extensions/zentui/user-message-styles.ts +72 -34
  889. package/dist/extensions/pi-zentui/extensions/zentui/user-message.ts +219 -34
  890. package/dist/extensions/pi-zentui/extensions/zentui/working-line.ts +495 -119
  891. package/dist/extensions/pi-zentui/omp.ts +157 -0
  892. package/dist/extensions/pi-zentui/package-lock.json +3762 -1507
  893. package/dist/extensions/pi-zentui/package.json +50 -12
  894. package/dist/extensions/pi-zentui/test/accent-rail-editor.test.ts +13 -2
  895. package/dist/extensions/pi-zentui/test/average-token-rate.test.ts +297 -0
  896. package/dist/extensions/pi-zentui/test/behavior-compatibility.mjs +89 -0
  897. package/dist/extensions/pi-zentui/test/codex-quota-lifecycle.test.ts +284 -0
  898. package/dist/extensions/pi-zentui/test/codex-quota-rendering.test.ts +466 -0
  899. package/dist/extensions/pi-zentui/test/codex-quota.test.ts +440 -0
  900. package/dist/extensions/pi-zentui/test/component-colors.test.ts +554 -0
  901. package/dist/extensions/pi-zentui/test/component-settings.test.ts +364 -0
  902. package/dist/extensions/pi-zentui/test/config-component-independence.test.ts +402 -0
  903. package/dist/extensions/pi-zentui/test/config-extension-status-choice.test.ts +233 -0
  904. package/dist/extensions/pi-zentui/test/config-hidden-extension-status-colors.test.ts +118 -0
  905. package/dist/extensions/pi-zentui/test/config-hidden-extension-status-placement.test.ts +142 -0
  906. package/dist/extensions/pi-zentui/test/config-minimalist-templates.test.ts +240 -0
  907. package/dist/extensions/pi-zentui/test/config-template-variables.test.ts +85 -0
  908. package/dist/extensions/pi-zentui/test/config.test.ts +80 -17
  909. package/dist/extensions/pi-zentui/test/custom-value-colors-integration.test.ts +453 -0
  910. package/dist/extensions/pi-zentui/test/custom-value-colors.test.ts +399 -0
  911. package/dist/extensions/pi-zentui/test/custom-variable-alias-prototypes.test.ts +177 -0
  912. package/dist/extensions/pi-zentui/test/custom-variable-consumers.test.ts +194 -0
  913. package/dist/extensions/pi-zentui/test/custom-variable-demand.test.ts +169 -0
  914. package/dist/extensions/pi-zentui/test/custom-variable-probe-reservation.test.ts +141 -0
  915. package/dist/extensions/pi-zentui/test/custom-variables-lifecycle.integration.test.ts +365 -0
  916. package/dist/extensions/pi-zentui/test/custom-variables.test.ts +330 -0
  917. package/dist/extensions/pi-zentui/test/editor-metadata-format.test.ts +121 -0
  918. package/dist/extensions/pi-zentui/test/editor-mouse.test.ts +507 -0
  919. package/dist/extensions/pi-zentui/test/editor-viewport-indicators.test.ts +247 -20
  920. package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +204 -260
  921. package/dist/extensions/pi-zentui/test/extension-status-controller.test.ts +323 -0
  922. package/dist/extensions/pi-zentui/test/extension-status-lifecycle.integration.test.ts +466 -0
  923. package/dist/extensions/pi-zentui/test/extension-status-settings.test.ts +79 -0
  924. package/dist/extensions/pi-zentui/test/footer-alignment.test.ts +135 -0
  925. package/dist/extensions/pi-zentui/test/footer-authority.test.ts +69 -0
  926. package/dist/extensions/pi-zentui/test/footer-auto-compaction.test.ts +148 -0
  927. package/dist/extensions/pi-zentui/test/footer-format-cache.test.ts +186 -0
  928. package/dist/extensions/pi-zentui/test/footer-format.test.ts +154 -4
  929. package/dist/extensions/pi-zentui/test/footer-hidden.test.ts +312 -0
  930. package/dist/extensions/pi-zentui/test/footer-hyperlink.test.ts +22 -0
  931. package/dist/extensions/pi-zentui/test/footer-layout.test.ts +80 -0
  932. package/dist/extensions/pi-zentui/test/footer-thinking-level.test.ts +123 -0
  933. package/dist/extensions/pi-zentui/test/format.test.ts +75 -1
  934. package/dist/extensions/pi-zentui/test/git-probes.test.ts +182 -0
  935. package/dist/extensions/pi-zentui/test/github-status.test.ts +931 -0
  936. package/dist/extensions/pi-zentui/test/host-template-surfaces.test.ts +287 -0
  937. package/dist/extensions/pi-zentui/test/icons.test.ts +86 -16
  938. package/dist/extensions/pi-zentui/test/layered-editor.test.ts +53 -0
  939. package/dist/extensions/pi-zentui/test/live-context-integration.test.ts +154 -7
  940. package/dist/extensions/pi-zentui/test/live-context.test.ts +45 -0
  941. package/dist/extensions/pi-zentui/test/live-metadata-lifecycle.integration.test.ts +963 -0
  942. package/dist/extensions/pi-zentui/test/live-metadata.test.ts +767 -0
  943. package/dist/extensions/pi-zentui/test/minimalist-cache-hit-colors.test.ts +183 -0
  944. package/dist/extensions/pi-zentui/test/minimalist-editor.test.ts +222 -4
  945. package/dist/extensions/pi-zentui/test/minimalist-settings.test.ts +103 -0
  946. package/dist/extensions/pi-zentui/test/minimalist-templates.test.ts +489 -0
  947. package/dist/extensions/pi-zentui/test/omp-editor-composers.test.ts +324 -0
  948. package/dist/extensions/pi-zentui/test/omp-statusline.test.ts +579 -0
  949. package/dist/extensions/pi-zentui/test/omp-template-lookups.test.ts +630 -0
  950. package/dist/extensions/pi-zentui/test/omp-template-metrics.test.ts +72 -0
  951. package/dist/extensions/pi-zentui/test/omp-template-modes.test.ts +557 -0
  952. package/dist/extensions/pi-zentui/test/omp-ui.test.ts +93 -0
  953. package/dist/extensions/pi-zentui/test/package-contents.mjs +1 -0
  954. package/dist/extensions/pi-zentui/test/pi-version-policy.test.ts +37 -0
  955. package/dist/extensions/pi-zentui/test/project-demand-integration.test.ts +542 -0
  956. package/dist/extensions/pi-zentui/test/project-discovery.test.ts +412 -0
  957. package/dist/extensions/pi-zentui/test/project-refresh.test.ts +44 -0
  958. package/dist/extensions/pi-zentui/test/project-state.test.ts +38 -1
  959. package/dist/extensions/pi-zentui/test/responsive-dependencies.test.ts +7 -7
  960. package/dist/extensions/pi-zentui/test/runtime.test.ts +127 -2
  961. package/dist/extensions/pi-zentui/test/selesai-v030-gate.json +939 -0
  962. package/dist/extensions/pi-zentui/test/settings-command.test.ts +1330 -82
  963. package/dist/extensions/pi-zentui/test/settings-keys.test.ts +41 -0
  964. package/dist/extensions/pi-zentui/test/settings-list-selection.test.ts +88 -0
  965. package/dist/extensions/pi-zentui/test/settings-previews.test.ts +4 -1
  966. package/dist/extensions/pi-zentui/test/state.test.ts +15 -1
  967. package/dist/extensions/pi-zentui/test/telemetry-lifecycle-integration.test.ts +214 -12
  968. package/dist/extensions/pi-zentui/test/telemetry-snapshot.test.ts +246 -0
  969. package/dist/extensions/pi-zentui/test/thinking-experimental-cache.test.ts +246 -0
  970. package/dist/extensions/pi-zentui/test/thinking-experimental-rows.test.ts +22 -0
  971. package/dist/extensions/pi-zentui/test/thinking-experimental-tui.mjs +113 -51
  972. package/dist/extensions/pi-zentui/test/thinking-experimental.test.ts +576 -76
  973. package/dist/extensions/pi-zentui/test/thinking-steps.test.ts +23 -0
  974. package/dist/extensions/pi-zentui/test/token-rate.test.ts +450 -0
  975. package/dist/extensions/pi-zentui/test/turn-summary-format.test.ts +207 -0
  976. package/dist/extensions/pi-zentui/test/turn-summary-lifecycle.integration.test.ts +72 -5
  977. package/dist/extensions/pi-zentui/test/turn-summary-rate.test.ts +158 -0
  978. package/dist/extensions/pi-zentui/test/usage-lifecycle-integration.test.ts +421 -0
  979. package/dist/extensions/pi-zentui/test/usage-snapshot.test.ts +154 -0
  980. package/dist/extensions/pi-zentui/test/user-message-cache.test.ts +239 -0
  981. package/dist/extensions/pi-zentui/test/user-message-native-reduced-capabilities.test.ts +61 -0
  982. package/dist/extensions/pi-zentui/test/user-message-native.test.ts +447 -0
  983. package/dist/extensions/pi-zentui/test/user-message-styles.test.ts +2 -2
  984. package/dist/extensions/pi-zentui/test/working-line-editor-placement.test.ts +317 -0
  985. package/dist/extensions/pi-zentui/test/working-line-generation-cache.test.ts +238 -0
  986. package/dist/extensions/pi-zentui/test/working-line-lifecycle.integration.test.ts +674 -179
  987. package/dist/extensions/pi-zentui/test/working-line-message-ownership.test.ts +239 -0
  988. package/dist/extensions/pi-zentui/test/working-line-metric-animation.test.ts +284 -0
  989. package/dist/extensions/pi-zentui/test/working-line-placement.test.ts +298 -0
  990. package/dist/extensions/pi-zentui/test/working-line.test.ts +386 -18
  991. package/dist/extensions/pi-zentui/test/wrapped-editor-consumers.integration.test.ts +579 -0
  992. package/dist/extensions/pi-zentui/test/wrapped-editor-usage.integration.test.ts +298 -0
  993. package/dist/extensions/pi-zentui/tsconfig.json +1 -1
  994. package/dist/extensions/pi-zentui/vitest.host.config.ts +33 -0
  995. package/dist/extensions/question/index.ts +1 -5
  996. package/dist/extensions/rtk.test.ts +28 -15
  997. package/dist/extensions/rtk.ts +22 -22
  998. package/dist/extensions/skills-index/index.test.ts +99 -0
  999. package/dist/extensions/skills-index/index.ts +105 -0
  1000. package/dist/extensions/skills-index/package.json +9 -0
  1001. package/dist/extensions/tokenin-onboarding.ts +48 -208
  1002. package/dist/extensions/tool-search/index.d.ts +11 -0
  1003. package/dist/extensions/tool-search/index.js +14 -0
  1004. package/dist/extensions/tool-search/tool.d.ts +91 -0
  1005. package/dist/extensions/tool-search/tool.js +214 -0
  1006. package/dist/index.d.ts +17 -9
  1007. package/dist/index.js +11 -5
  1008. package/dist/main.d.ts +4 -0
  1009. package/dist/main.js +106 -42
  1010. package/dist/migrations.js +4 -3
  1011. package/dist/modes/index.d.ts +1 -0
  1012. package/dist/modes/interactive/bug-report.js +4 -0
  1013. package/dist/modes/interactive/components/armin.d.ts +3 -0
  1014. package/dist/modes/interactive/components/armin.js +7 -5
  1015. package/dist/modes/interactive/components/assistant-message.js +1 -1
  1016. package/dist/modes/interactive/components/auth-url.d.ts +13 -0
  1017. package/dist/modes/interactive/components/auth-url.js +36 -0
  1018. package/dist/modes/interactive/components/bash-execution.d.ts +5 -1
  1019. package/dist/modes/interactive/components/bash-execution.js +17 -14
  1020. package/dist/modes/interactive/components/branch-summary-message.d.ts +2 -1
  1021. package/dist/modes/interactive/components/branch-summary-message.js +5 -2
  1022. package/dist/modes/interactive/components/compaction-summary-message.d.ts +2 -1
  1023. package/dist/modes/interactive/components/compaction-summary-message.js +5 -2
  1024. package/dist/modes/interactive/components/config-selector.js +13 -3
  1025. package/dist/modes/interactive/components/custom-editor.js +7 -0
  1026. package/dist/modes/interactive/components/custom-entry.d.ts +3 -1
  1027. package/dist/modes/interactive/components/custom-entry.js +8 -2
  1028. package/dist/modes/interactive/components/custom-message.js +1 -0
  1029. package/dist/modes/interactive/components/first-time-setup.d.ts +4 -4
  1030. package/dist/modes/interactive/components/first-time-setup.js +8 -2
  1031. package/dist/modes/interactive/components/footer.d.ts +7 -0
  1032. package/dist/modes/interactive/components/footer.js +44 -4
  1033. package/dist/modes/interactive/components/index.d.ts +0 -1
  1034. package/dist/modes/interactive/components/index.js +0 -1
  1035. package/dist/modes/interactive/components/login-dialog.d.ts +2 -0
  1036. package/dist/modes/interactive/components/login-dialog.js +11 -5
  1037. package/dist/modes/interactive/components/mermaid.d.ts +10 -0
  1038. package/dist/modes/interactive/components/mermaid.js +74 -0
  1039. package/dist/modes/interactive/components/model-selector.d.ts +14 -9
  1040. package/dist/modes/interactive/components/model-selector.js +79 -31
  1041. package/dist/modes/interactive/components/oauth-selector.d.ts +8 -2
  1042. package/dist/modes/interactive/components/oauth-selector.js +24 -21
  1043. package/dist/modes/interactive/components/pi-logo.d.ts +18 -0
  1044. package/dist/modes/interactive/components/pi-logo.js +36 -0
  1045. package/dist/modes/interactive/components/radius-login-selector.d.ts +14 -0
  1046. package/dist/modes/interactive/components/radius-login-selector.js +84 -0
  1047. package/dist/modes/interactive/components/scoped-models-selector.d.ts +4 -0
  1048. package/dist/modes/interactive/components/scoped-models-selector.js +26 -0
  1049. package/dist/modes/interactive/components/settings-selector.d.ts +8 -11
  1050. package/dist/modes/interactive/components/settings-selector.js +60 -102
  1051. package/dist/modes/interactive/components/skill-invocation-message.d.ts +2 -1
  1052. package/dist/modes/interactive/components/skill-invocation-message.js +5 -2
  1053. package/dist/modes/interactive/components/themed-text.d.ts +16 -0
  1054. package/dist/modes/interactive/components/themed-text.js +28 -0
  1055. package/dist/modes/interactive/components/tool-execution.d.ts +11 -18
  1056. package/dist/modes/interactive/components/tool-execution.js +34 -47
  1057. package/dist/modes/interactive/components/tree-selector.js +7 -0
  1058. package/dist/modes/interactive/components/user-message.d.ts +3 -1
  1059. package/dist/modes/interactive/components/user-message.js +14 -6
  1060. package/dist/modes/interactive/components/visual-truncate.d.ts +27 -3
  1061. package/dist/modes/interactive/components/visual-truncate.js +39 -6
  1062. package/dist/modes/interactive/external-editor.js +2 -1
  1063. package/dist/modes/interactive/interactive-mode.d.ts +41 -8
  1064. package/dist/modes/interactive/interactive-mode.js +854 -492
  1065. package/dist/modes/interactive/program-status-reporter.d.ts +33 -0
  1066. package/dist/modes/interactive/program-status-reporter.js +109 -0
  1067. package/dist/modes/interactive/theme/dark.json +59 -72
  1068. package/dist/modes/interactive/theme/light.json +57 -69
  1069. package/dist/modes/interactive/theme/system-theme.d.ts +54 -0
  1070. package/dist/modes/interactive/theme/system-theme.js +545 -0
  1071. package/dist/modes/interactive/theme/theme-controller.d.ts +46 -6
  1072. package/dist/modes/interactive/theme/theme-controller.js +127 -47
  1073. package/dist/modes/interactive/theme/theme-json.d.ts +1 -0
  1074. package/dist/modes/interactive/theme/theme-json.js +3 -1
  1075. package/dist/modes/interactive/theme/theme-schema.json +7 -2
  1076. package/dist/modes/interactive/theme/theme.d.ts +66 -39
  1077. package/dist/modes/interactive/theme/theme.js +243 -320
  1078. package/dist/modes/interactive/tui-renderer.d.ts +2 -1
  1079. package/dist/modes/interactive/tui-renderer.js +1 -0
  1080. package/dist/modes/json-event.d.ts +33 -0
  1081. package/dist/modes/json-event.js +28 -0
  1082. package/dist/modes/print-mode.js +12 -2
  1083. package/dist/modes/rpc/rpc-client.d.ts +11 -19
  1084. package/dist/modes/rpc/rpc-client.js +52 -35
  1085. package/dist/modes/rpc/rpc-mode.js +11 -26
  1086. package/dist/modes/rpc/rpc-types.d.ts +12 -23
  1087. package/dist/package-manager-cli.js +16 -5
  1088. package/dist/rpc-entry.js +1 -0
  1089. package/dist/skills/pi-subagents/SKILL.md +109 -35
  1090. package/dist/skills/pi-subagents/references/constraints-and-recipes.md +38 -238
  1091. package/dist/skills/pi-subagents/references/execution-controls.md +181 -83
  1092. package/dist/skills/pi-subagents/references/management-authoring-rpc.md +9 -9
  1093. package/dist/skills/pi-subagents/references/multi-lane-orchestration.md +24 -2
  1094. package/dist/skills/pi-subagents/references/prompting-and-roles.md +121 -91
  1095. package/dist/skills/pi-subagents/references/review-and-validation.md +73 -0
  1096. package/dist/utils/ansi.d.ts +8 -0
  1097. package/dist/utils/ansi.js +30 -11
  1098. package/dist/utils/clipboard-image.js +14 -17
  1099. package/dist/utils/clipboard.d.ts +2 -0
  1100. package/dist/utils/clipboard.js +17 -11
  1101. package/dist/utils/frontmatter.js +2 -1
  1102. package/dist/utils/image-convert.d.ts +12 -0
  1103. package/dist/utils/image-convert.js +45 -6
  1104. package/dist/utils/image-resize-core.d.ts +12 -0
  1105. package/dist/utils/image-resize-core.js +5 -0
  1106. package/dist/utils/image-resize-worker.js +3 -2
  1107. package/dist/utils/image-resize.js +9 -10
  1108. package/dist/utils/mime.js +1 -1
  1109. package/dist/utils/output-files.d.ts +14 -0
  1110. package/dist/utils/output-files.js +29 -0
  1111. package/dist/utils/paths.d.ts +6 -3
  1112. package/dist/utils/paths.js +28 -5
  1113. package/dist/utils/shell.d.ts +1 -3
  1114. package/dist/utils/shell.js +3 -30
  1115. package/dist/utils/syntax-highlight.d.ts +1 -0
  1116. package/dist/utils/syntax-highlight.js +68 -2
  1117. package/dist/utils/tool-result-images.d.ts +20 -0
  1118. package/dist/utils/tool-result-images.js +47 -0
  1119. package/dist/utils/tools-manager.d.ts +5 -0
  1120. package/dist/utils/tools-manager.js +6 -6
  1121. package/dist/utils/version-check.d.ts +4 -0
  1122. package/dist/utils/version-check.js +20 -2
  1123. package/docs/cli-integration.md +104 -0
  1124. package/docs/cli.md +333 -0
  1125. package/docs/codemode.md +230 -0
  1126. package/docs/configuration.md +47 -0
  1127. package/docs/environment-variables.md +5 -0
  1128. package/docs/extensions.md +107 -30
  1129. package/docs/json.md +8 -4
  1130. package/docs/keybindings.md +10 -10
  1131. package/docs/mcp.md +262 -0
  1132. package/docs/models.md +104 -3
  1133. package/docs/providers.md +20 -2
  1134. package/docs/rpc.md +13 -12
  1135. package/docs/sdk.md +41 -33
  1136. package/docs/security.md +21 -20
  1137. package/docs/settings.md +18 -46
  1138. package/docs/terminal-setup.md +14 -0
  1139. package/docs/virtual-models.md +114 -0
  1140. package/examples/extensions/jev-router.ts +113 -0
  1141. package/examples/extensions/tokenin-usage-widget.ts +124 -0
  1142. package/examples/rpc-client.ts +35 -0
  1143. package/examples/sdk/14-codemode-mcp.ts +67 -0
  1144. package/package.json +15 -8
  1145. package/dist/core/agent-session-auto-handoff.test.js +0 -195
  1146. package/dist/core/built-in-extensions.d.ts +0 -2
  1147. package/dist/core/built-in-extensions.js +0 -2
  1148. package/dist/core/llama/provider.js +0 -143
  1149. package/dist/core/settings-manager-auto-handoff.test.d.ts +0 -1
  1150. package/dist/core/settings-manager-auto-handoff.test.js +0 -53
  1151. package/dist/extensions/capability-gateway/catalog.ts +0 -177
  1152. package/dist/extensions/capability-gateway/index.test.ts +0 -226
  1153. package/dist/extensions/capability-gateway/index.ts +0 -669
  1154. package/dist/extensions/capability-gateway/integration.test.ts +0 -803
  1155. package/dist/extensions/capability-gateway/package.json +0 -16
  1156. package/dist/extensions/capability-gateway/routing.test.ts +0 -574
  1157. package/dist/extensions/capability-gateway/routing.ts +0 -403
  1158. package/dist/extensions/pi-subagents/runner-server-preload.mjs +0 -13
  1159. package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +0 -89
  1160. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +0 -259
  1161. package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +0 -312
  1162. package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +0 -374
  1163. package/dist/extensions/pi-subagents/src/runs/shared/readonly-model-continuation.ts +0 -69
  1164. package/dist/extensions/pi-subagents/src/runs/shared/readonly-session-evidence.ts +0 -307
  1165. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +0 -211
  1166. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/cli.d.ts +0 -3
  1167. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/register-bedrock.d.ts +0 -2
  1168. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/restore-sandbox-env.d.ts +0 -17
  1169. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/args.d.ts +0 -57
  1170. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/config-selector.d.ts +0 -16
  1171. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/file-processor.d.ts +0 -15
  1172. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/initial-message.d.ts +0 -18
  1173. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/list-models.d.ts +0 -9
  1174. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/project-trust.d.ts +0 -10
  1175. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/session-picker.d.ts +0 -10
  1176. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/startup-ui.d.ts +0 -20
  1177. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli.d.ts +0 -3
  1178. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/config.d.ts +0 -96
  1179. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-runtime.d.ts +0 -119
  1180. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-services.d.ts +0 -85
  1181. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session.d.ts +0 -620
  1182. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-guidance.d.ts +0 -5
  1183. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-storage.d.ts +0 -56
  1184. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/bash-executor.d.ts +0 -32
  1185. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/cache-stats.d.ts +0 -49
  1186. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/branch-summarization.d.ts +0 -93
  1187. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/compaction.d.ts +0 -128
  1188. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/index.d.ts +0 -7
  1189. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/utils.d.ts +0 -38
  1190. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/defaults.d.ts +0 -3
  1191. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/diagnostics.d.ts +0 -15
  1192. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/event-bus.d.ts +0 -9
  1193. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/exec.d.ts +0 -29
  1194. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/experimental.d.ts +0 -2
  1195. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/ansi-to-html.d.ts +0 -22
  1196. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/index.d.ts +0 -37
  1197. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/tool-renderer.d.ts +0 -34
  1198. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/index.d.ts +0 -12
  1199. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/loader.d.ts +0 -23
  1200. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/runner.d.ts +0 -171
  1201. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +0 -1261
  1202. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/wrapper.d.ts +0 -20
  1203. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/footer-data-provider.d.ts +0 -54
  1204. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/http-dispatcher.d.ts +0 -22
  1205. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/index.d.ts +0 -13
  1206. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/keybindings.d.ts +0 -358
  1207. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/messages.d.ts +0 -77
  1208. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-config.d.ts +0 -512
  1209. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-registry.d.ts +0 -43
  1210. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-resolver.d.ts +0 -121
  1211. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-runtime.d.ts +0 -82
  1212. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/models-store.d.ts +0 -17
  1213. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/output-guard.d.ts +0 -7
  1214. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/package-manager.d.ts +0 -210
  1215. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/project-trust.d.ts +0 -15
  1216. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/prompt-templates.d.ts +0 -54
  1217. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-attribution.d.ts +0 -4
  1218. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-composer.d.ts +0 -55
  1219. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/radius.d.ts +0 -2
  1220. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/remote-catalog-provider.d.ts +0 -5
  1221. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resolve-config-value.d.ts +0 -30
  1222. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resource-loader.d.ts +0 -206
  1223. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/runtime-credentials.d.ts +0 -15
  1224. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/sdk.d.ts +0 -106
  1225. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-cwd.d.ts +0 -19
  1226. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-manager.d.ts +0 -356
  1227. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/settings-manager.d.ts +0 -297
  1228. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/skills.d.ts +0 -60
  1229. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/slash-commands.d.ts +0 -15
  1230. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/source-info.d.ts +0 -18
  1231. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/system-prompt.d.ts +0 -28
  1232. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/telemetry.d.ts +0 -3
  1233. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/timings.d.ts +0 -10
  1234. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/bash.d.ts +0 -68
  1235. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit-diff.d.ts +0 -106
  1236. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit.d.ts +0 -51
  1237. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/file-mutation-queue.d.ts +0 -6
  1238. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/find.d.ts +0 -35
  1239. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/grep.d.ts +0 -37
  1240. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/index.d.ts +0 -40
  1241. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/ls.d.ts +0 -37
  1242. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/output-accumulator.d.ts +0 -52
  1243. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/path-utils.d.ts +0 -10
  1244. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/read.d.ts +0 -35
  1245. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/render-utils.d.ts +0 -24
  1246. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/tool-definition-wrapper.d.ts +0 -14
  1247. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/truncate.d.ts +0 -70
  1248. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/write.d.ts +0 -26
  1249. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/trust-manager.d.ts +0 -36
  1250. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/usage-totals.d.ts +0 -19
  1251. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/client.d.ts +0 -61
  1252. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/huggingface.d.ts +0 -23
  1253. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/index.d.ts +0 -3
  1254. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/provider.d.ts +0 -10
  1255. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/ui.d.ts +0 -42
  1256. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/index.d.ts +0 -35
  1257. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/main.d.ts +0 -12
  1258. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/migrations.d.ts +0 -33
  1259. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/index.d.ts +0 -9
  1260. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/armin.d.ts +0 -34
  1261. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/assistant-message.d.ts +0 -22
  1262. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bash-execution.d.ts +0 -34
  1263. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bordered-loader.d.ts +0 -16
  1264. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/branch-summary-message.d.ts +0 -16
  1265. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/compaction-summary-message.d.ts +0 -16
  1266. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/config-selector.d.ts +0 -102
  1267. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/countdown-timer.d.ts +0 -14
  1268. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-editor.d.ts +0 -21
  1269. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-entry.d.ts +0 -19
  1270. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-message.d.ts +0 -20
  1271. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/daxnuts.d.ts +0 -23
  1272. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/diff.d.ts +0 -12
  1273. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/dynamic-border.d.ts +0 -15
  1274. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/earendil-announcement.d.ts +0 -5
  1275. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-editor.d.ts +0 -22
  1276. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-input.d.ts +0 -23
  1277. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-selector.d.ts +0 -26
  1278. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/first-time-setup.d.ts +0 -25
  1279. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/footer.d.ts +0 -32
  1280. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/index.d.ts +0 -34
  1281. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/keybinding-hints.d.ts +0 -13
  1282. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/login-dialog.d.ts +0 -52
  1283. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/model-selector.d.ts +0 -54
  1284. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/oauth-selector.d.ts +0 -33
  1285. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/scoped-models-selector.d.ts +0 -42
  1286. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector-search.d.ts +0 -23
  1287. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector.d.ts +0 -95
  1288. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/settings-selector.d.ts +0 -77
  1289. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/show-images-selector.d.ts +0 -10
  1290. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/skill-invocation-message.d.ts +0 -17
  1291. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/status-indicator.d.ts +0 -28
  1292. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/theme-selector.d.ts +0 -11
  1293. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/thinking-selector.d.ts +0 -11
  1294. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tool-execution.d.ts +0 -63
  1295. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tree-selector.d.ts +0 -94
  1296. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/trust-selector.d.ts +0 -23
  1297. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message-selector.d.ts +0 -30
  1298. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message.d.ts +0 -14
  1299. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/visual-truncate.d.ts +0 -24
  1300. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/interactive-mode.d.ts +0 -394
  1301. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/model-search.d.ts +0 -12
  1302. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme-controller.d.ts +0 -29
  1303. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme.d.ts +0 -120
  1304. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/print-mode.d.ts +0 -28
  1305. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/jsonl.d.ts +0 -17
  1306. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-client.d.ts +0 -246
  1307. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-mode.d.ts +0 -20
  1308. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-types.d.ts +0 -457
  1309. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/package-manager-cli.d.ts +0 -8
  1310. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/rpc-entry.d.ts +0 -3
  1311. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/ansi.d.ts +0 -2
  1312. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/changelog.d.ts +0 -22
  1313. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/child-process.d.ts +0 -18
  1314. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-image.d.ts +0 -11
  1315. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-native.d.ts +0 -11
  1316. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard.d.ts +0 -4
  1317. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/deprecation.d.ts +0 -4
  1318. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/exif-orientation.d.ts +0 -5
  1319. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/frontmatter.d.ts +0 -8
  1320. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/fs-watch.d.ts +0 -5
  1321. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/git.d.ts +0 -26
  1322. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/html.d.ts +0 -7
  1323. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-convert.d.ts +0 -10
  1324. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-process.d.ts +0 -18
  1325. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-core.d.ts +0 -30
  1326. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-worker.d.ts +0 -2
  1327. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize.d.ts +0 -16
  1328. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/json.d.ts +0 -3
  1329. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/mime.d.ts +0 -3
  1330. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/open-browser.d.ts +0 -9
  1331. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/paths.d.ts +0 -31
  1332. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/photon.d.ts +0 -21
  1333. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/pi-user-agent.d.ts +0 -2
  1334. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/shell.d.ts +0 -31
  1335. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/sleep.d.ts +0 -5
  1336. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/syntax-highlight.d.ts +0 -12
  1337. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/tools-manager.d.ts +0 -3
  1338. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/version-check.d.ts +0 -15
  1339. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/windows-self-update.d.ts +0 -3
  1340. package/dist/extensions/pi-subagents/test/smoke/pi085-child.ts +0 -31
  1341. package/dist/extensions/pi-subagents/test/smoke/pi085-clean-install.mjs +0 -74
  1342. package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +0 -150
  1343. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +0 -766
  1344. package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +0 -327
  1345. package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +0 -309
  1346. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +0 -1093
  1347. package/dist/extensions/pi-subagents/test/unit/readonly-model-continuation.test.ts +0 -94
  1348. package/dist/extensions/pi-subagents/test/unit/readonly-session-evidence.test.ts +0 -1329
  1349. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +0 -153
  1350. package/dist/extensions/pi-zentui/extensions/zentui/accent-rail-layout-patch.ts +0 -457
  1351. package/dist/extensions/pi-zentui/test/accent-rail-layout-patch.test.ts +0 -669
  1352. package/dist/extensions/pi-zentui/test/thinking-experimental-docs.test.ts +0 -51
  1353. package/dist/modes/interactive/components/daxnuts.d.ts +0 -22
  1354. package/dist/modes/interactive/components/daxnuts.js +0 -139
  1355. /package/dist/core/{agent-session-auto-handoff.test.d.ts → agent-session-custom-tools.test.d.ts} +0 -0
  1356. /package/dist/{core → extensions}/llama/huggingface.d.ts +0 -0
  1357. /package/dist/{core → extensions}/llama/huggingface.js +0 -0
  1358. /package/dist/{core → extensions}/llama/index.d.ts +0 -0
  1359. /package/dist/{core → extensions}/llama/ui.d.ts +0 -0
  1360. /package/dist/{core → extensions}/llama/ui.js +0 -0
@@ -2,27 +2,30 @@
2
2
  * Interactive mode for the coding agent.
3
3
  * Handles TUI rendering and user interaction, delegating business logic to AgentSession.
4
4
  */
5
+ import * as crypto from "node:crypto";
5
6
  import * as fs from "node:fs";
6
7
  import * as os from "node:os";
7
8
  import * as path from "node:path";
8
9
  import { isRetryableAssistantError, } from "@earendil-works/pi-ai/compat";
9
10
  import * as TuiLayouts from "@earendil-works/pi-tui";
10
- import { CombinedAutocompleteProvider, Container, fuzzyFilter, Markdown, matchesKey, Spacer, setCapabilityOverrides, setKeybindings, Text, TruncatedText, TuiAltScreen, TuiMainScreen, visibleWidth, } from "@earendil-works/pi-tui";
11
+ import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey, Spacer, setCapabilityOverrides, setKeybindings, Text, TruncatedText, TuiAltScreen, TuiMainScreen, visibleWidth, } from "@earendil-works/pi-tui";
11
12
  import chalk from "chalk";
12
- import { spawn, spawnSync } from "child_process";
13
- import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getBundledDefaultsDir, getDebugLogPath, getDocsPath, getShareViewerUrl, getUpdateInstruction, PACKAGE_NAME, VERSION, } from "../../config.js";
13
+ import { spawn } from "child_process";
14
+ import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, detectInstallChange, getAgentDir, getAuthPath, getBundledDefaultsDir, getDebugLogPath, getDocsPath, getUpdateInstruction, PACKAGE_NAME, VERSION, } from "../../config.js";
14
15
  import { parseSkillBlocks } from "../../core/agent-session.js";
15
16
  import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
16
17
  import { CACHE_TTL_MS, collectCacheMisses, computeCacheWaste, detectCacheMiss, } from "../../core/cache-stats.js";
17
18
  import { formatCacheWarmingStatus, formatCacheWarmingUsage } from "../../core/cache-warmer.js";
18
- import { recordCrash, takeUnnotifiedCrash } from "../../core/crash-log.js";
19
+ import { findExtensionStackMatches, recordCrash, takeUnnotifiedCrash } from "../../core/crash-log.js";
19
20
  import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "../../core/defaults.js";
20
21
  import { FooterDataProvider } from "../../core/footer-data-provider.js";
21
22
  import { configureHttpDispatcher, formatHttpIdleTimeoutMs } from "../../core/http-dispatcher.js";
22
23
  import { KeybindingsManager } from "../../core/keybindings.js";
23
- import { createCompactionSummaryMessage } from "../../core/messages.js";
24
- import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScope, resolveModelScopeWithDiagnostics, } from "../../core/model-resolver.js";
24
+ import { createCompactionSummaryMessage, createCustomMessage } from "../../core/messages.js";
25
+ import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScopeFromModels, } from "../../core/model-resolver.js";
26
+ import { CredentialSynchronizationError } from "../../core/model-runtime.js";
25
27
  import { DefaultPackageManager } from "../../core/package-manager.js";
28
+ import { RADIUS_MCP_URL, RADIUS_PROVIDER_ID } from "../../core/radius.js";
26
29
  import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../core/session-cwd.js";
27
30
  import { SessionManager, sessionEntryToContextMessages, } from "../../core/session-manager.js";
28
31
  import { buildGitCommandPrompt, GIT_COMMAND_MODES } from "../../core/git-command.js";
@@ -31,14 +34,16 @@ import { isInstallTelemetryEnabled } from "../../core/telemetry.js";
31
34
  import { withBuiltInRenderers } from "../../core/tools/renderers/index.js";
32
35
  import { hasTrustRequiringProjectResources, ProjectTrustStore } from "../../core/trust-manager.js";
33
36
  import { formatCost, getUsageCostBreakdown } from "../../core/usage-totals.js";
37
+ import { addMcpServerConfig, loadMcpConfig } from "../../extensions/mcp/config.js";
34
38
  import { getChangelogPath, getNewEntries, normalizeChangelogLinks, parseChangelog } from "../../utils/changelog.js";
35
- import { copyToClipboard, readClipboardText } from "../../utils/clipboard.js";
39
+ import { copyToClipboard, readClipboardFilePaths, readClipboardText } from "../../utils/clipboard.js";
36
40
  import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
37
41
  import { parseGitUrl } from "../../utils/git.js";
38
- import { formatDimensionNote, resizeImage } from "../../utils/image-resize.js";
42
+ import { ensurePngTranscoder } from "../../utils/image-convert.js";
39
43
  import { getCwdRelativePath } from "../../utils/paths.js";
40
44
  import { getPiUserAgent } from "../../utils/pi-user-agent.js";
41
45
  import { killTrackedDetachedChildren } from "../../utils/shell.js";
46
+ import { loadAllHighlightLanguages } from "../../utils/syntax-highlight.js";
42
47
  import { ensureTool } from "../../utils/tools-manager.js";
43
48
  import { checkForNewPiVersion } from "../../utils/version-check.js";
44
49
  import { reportBug } from "./bug-report.js";
@@ -46,13 +51,11 @@ import { createChatViewport } from "./chat-viewport.js";
46
51
  import { ArminComponent } from "./components/armin.js";
47
52
  import { AssistantMessageComponent } from "./components/assistant-message.js";
48
53
  import { BashExecutionComponent } from "./components/bash-execution.js";
49
- import { BorderedLoader } from "./components/bordered-loader.js";
50
54
  import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js";
51
55
  import { CompactionSummaryMessageComponent } from "./components/compaction-summary-message.js";
52
56
  import { CustomEditor } from "./components/custom-editor.js";
53
57
  import { CustomEntryComponent } from "./components/custom-entry.js";
54
58
  import { CustomMessageComponent } from "./components/custom-message.js";
55
- import { DaxnutsComponent } from "./components/daxnuts.js";
56
59
  import { DynamicBorder } from "./components/dynamic-border.js";
57
60
  import { StartupBox } from "./components/startup-box.js";
58
61
  import { EarendilAnnouncementComponent } from "./components/earendil-announcement.js";
@@ -62,22 +65,28 @@ import { ExtensionSelectorComponent } from "./components/extension-selector.js";
62
65
  import { FooterComponent, formatTokens } from "./components/footer.js";
63
66
  import { formatKeyText, keyDisplayText, keyHint, keyText, rawKeyHint } from "./components/keybinding-hints.js";
64
67
  import { LoginDialogComponent } from "./components/login-dialog.js";
68
+ import { createMermaidMarkdownTransformer } from "./components/mermaid.js";
65
69
  import { ModelSelectorComponent } from "./components/model-selector.js";
66
- import { formatAuthSelectorProviderType, OAuthSelectorComponent, } from "./components/oauth-selector.js";
70
+ import { formatAuthSelectorProviderStatus, formatAuthSelectorProviderType, OAuthSelectorComponent, } from "./components/oauth-selector.js";
71
+ import { createLoginMenuSelector } from "./components/radius-login-selector.js";
67
72
  import { ScopedModelsSelectorComponent } from "./components/scoped-models-selector.js";
68
73
  import { SessionSelectorComponent } from "./components/session-selector.js";
69
74
  import { SettingsSelectorComponent } from "./components/settings-selector.js";
70
75
  import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
71
76
  import { BranchSummaryStatusIndicator, CompactionStatusIndicator, IdleStatus, ImageCaptioningStatusIndicator, RetryStatusIndicator, WorkingStatusIndicator, } from "./components/status-indicator.js";
77
+ import { ThemedText } from "./components/themed-text.js";
78
+ import { ThinkingSelectorComponent } from "./components/thinking-selector.js";
72
79
  import { ToolExecutionComponent } from "./components/tool-execution.js";
73
80
  import { TreeSelectorComponent } from "./components/tree-selector.js";
74
81
  import { TrustSelectorComponent } from "./components/trust-selector.js";
75
82
  import { UserMessageComponent } from "./components/user-message.js";
76
83
  import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
77
84
  import { editInExternalEditor } from "./external-editor.js";
78
- import { getModelSearchText } from "./model-search.js";
79
85
  import { refreshModelCatalogs } from "./model-catalog-refresh.js";
80
- import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
86
+ import { getModelSearchText } from "./model-search.js";
87
+ import { ProgramStatusReporter } from "./program-status-reporter.js";
88
+ import { shareSession } from "./session-share.js";
89
+ import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, SYSTEM_THEME_NAME, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
81
90
  import { InteractiveThemeController } from "./theme/theme-controller.js";
82
91
  import { createInteractiveTui, createInteractiveTuiReference } from "./tui-renderer.js";
83
92
  export { createInteractiveTui, createInteractiveTuiReference } from "./tui-renderer.js";
@@ -90,16 +99,26 @@ function isWorkingStatusEditor(editor) {
90
99
  function isExpandable(obj) {
91
100
  return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
92
101
  }
93
- class ExpandableText extends Text {
94
- getCollapsedText;
95
- getExpandedText;
102
+ class ExpandableText extends ThemedText {
103
+ state;
96
104
  constructor(getCollapsedText, getExpandedText, expanded = false, paddingX = 0, paddingY = 0) {
97
- super(expanded ? getExpandedText() : getCollapsedText(), paddingX, paddingY);
98
- this.getCollapsedText = getCollapsedText;
99
- this.getExpandedText = getExpandedText;
105
+ const state = { expanded };
106
+ super(() => (state.expanded ? getExpandedText() : getCollapsedText()), paddingX, paddingY);
107
+ this.state = state;
100
108
  }
101
109
  setExpanded(expanded) {
102
- this.setText(expanded ? this.getExpandedText() : this.getCollapsedText());
110
+ this.state.expanded = expanded;
111
+ this.invalidate();
112
+ }
113
+ }
114
+ /** The built-in header. Clicking its logo (the first two lines, after one column of padding) plays an easter egg. */
115
+ class BuiltInHeader extends ExpandableText {
116
+ onLogoClick;
117
+ handleMouse(event) {
118
+ if (event.type !== "click" || event.y > 1 || event.x < 1 || event.x > 4 || !this.onLogoClick)
119
+ return undefined;
120
+ this.onLogoClick(event.screenX - event.x + 1, event.screenY - event.y);
121
+ return { handled: true };
103
122
  }
104
123
  }
105
124
  function isCustomSessionEntry(item) {
@@ -111,7 +130,9 @@ function isCompactionCostNotice(item) {
111
130
  function isUsageSessionEntry(item) {
112
131
  return "type" in item && item.type === "usage";
113
132
  }
114
- const DEAD_TERMINAL_ERROR_CODES = new Set(["EIO", "EPIPE", "ENOTCONN"]);
133
+ // EIO: tty reads/ioctls from an orphaned background process group, or writes after hangup.
134
+ // ENOTTY: the tty was revoked (macOS) and stdin is no longer a terminal.
135
+ const DEAD_TERMINAL_ERROR_CODES = new Set(["EIO", "EPIPE", "ENOTCONN", "ENOTTY"]);
115
136
  function isDeadTerminalError(error) {
116
137
  if (!error || typeof error !== "object" || !("code" in error)) {
117
138
  return false;
@@ -119,6 +140,22 @@ function isDeadTerminalError(error) {
119
140
  const code = error.code;
120
141
  return code !== undefined && DEAD_TERMINAL_ERROR_CODES.has(code);
121
142
  }
143
+ export function formatCrashExtensionHint(extensionMatches) {
144
+ const matches = Array.isArray(extensionMatches)
145
+ ? extensionMatches.filter((match) => typeof match === "string" && match.length > 0)
146
+ : [];
147
+ if (matches.length === 0)
148
+ return undefined;
149
+ const quoted = matches.map((match) => `\`${match}\``);
150
+ const labels = quoted.length === 1
151
+ ? quoted[0]
152
+ : quoted.length === 2
153
+ ? quoted.join(" and ")
154
+ : `${quoted.slice(0, -1).join(", ")}, and ${quoted[quoted.length - 1]}`;
155
+ const noun = matches.length === 1 ? "extension" : "extensions";
156
+ const pronoun = matches.length === 1 ? "it" : "them";
157
+ return `A stack frame came from loaded ${noun} ${labels}, which may be involved. Try disabling ${pronoun} with \`${APP_NAME} config\`, or run \`${APP_NAME} -ne\` to confirm.`;
158
+ }
122
159
  const ANTHROPIC_SUBSCRIPTION_AUTH_WARNING = "Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage. Disable this warning in /settings.";
123
160
  const SELESAI_LOGO = [
124
161
  " ____ ____ __ ____ ____ ______ ______ ",
@@ -179,6 +216,7 @@ function llamaCppPostLoginGuidance(actionLabel, loadedModelCount) {
179
216
  ? `${actionLabel}. No llama.cpp models are loaded. Use /llama to load a model, then /model to select it.`
180
217
  : `${actionLabel}. Use /model to select a loaded llama.cpp model, or /llama to manage models.`;
181
218
  }
219
+ const RADIUS_LOGIN_INTRO = "Radius is a service crafted for Pi by the builders of Pi, Earendil Works";
182
220
  const AUTH_TYPE_ORDER = { oauth: 0, api_key: 1 };
183
221
  function createFuzzyAutocompleteItems(items, prefix, getSearchText, toAutocompleteItem) {
184
222
  const filtered = fuzzyFilter(items, prefix, getSearchText);
@@ -201,18 +239,21 @@ function getLoginProviderCompletionOptions(providerOptions) {
201
239
  id: provider.id,
202
240
  name: provider.name,
203
241
  authTypes: [provider.authType],
242
+ subscription: provider.subscription,
204
243
  });
205
244
  }
206
245
  return Array.from(byId.values()).sort((a, b) => a.name.localeCompare(b.name));
207
246
  }
208
247
  function getLoginProviderSearchText(provider) {
209
248
  const authTypes = provider.authTypes
210
- .map((authType) => `${authType} ${formatAuthSelectorProviderType(authType)}`)
249
+ .map((authType) => `${authType} ${formatAuthSelectorProviderType(authType, provider.subscription)}`)
211
250
  .join(" ");
212
251
  return `${provider.id} ${provider.name} ${authTypes}`;
213
252
  }
214
253
  function formatLoginProviderCompletionDescription(provider) {
215
- const authTypes = provider.authTypes.map(formatAuthSelectorProviderType).join("/");
254
+ const authTypes = provider.authTypes
255
+ .map((authType) => formatAuthSelectorProviderType(authType, provider.subscription))
256
+ .join("/");
216
257
  return provider.name === provider.id ? authTypes : `${provider.name} · ${authTypes}`;
217
258
  }
218
259
  export class InteractiveMode {
@@ -220,13 +261,13 @@ export class InteractiveMode {
220
261
  renderer;
221
262
  ui;
222
263
  mainScreenRenderState;
223
- documentContainer;
224
264
  loadedResourcesContainer;
225
265
  chatContainer;
226
- pendingMessagesContainer;
227
- statusContainer;
266
+ documentContainer;
228
267
  transcriptScrollView;
229
268
  fullscreenLayoutRoot;
269
+ pendingMessagesContainer;
270
+ statusContainer;
230
271
  defaultEditor;
231
272
  editor;
232
273
  editorComponentFactory;
@@ -234,6 +275,8 @@ export class InteractiveMode {
234
275
  autocompleteProviderWrappers = [];
235
276
  fdPath;
236
277
  editorContainer;
278
+ activeSelectorToken;
279
+ activeSelectorDispose;
237
280
  footer;
238
281
  footerContainer;
239
282
  footerDataProvider;
@@ -261,16 +304,22 @@ export class InteractiveMode {
261
304
  // Status line tracking (for mutating immediately-sequential status updates)
262
305
  lastStatusSpacer = undefined;
263
306
  lastStatusText = undefined;
307
+ lastStatusMessage = "";
308
+ managedToolStatusStarted = false;
264
309
  // Streaming message tracking
265
310
  streamingComponent = undefined;
311
+ entriesRenderedByBoundaryCompaction = new Set();
266
312
  streamingMessage = undefined;
267
313
  // Tool execution tracking: toolCallId -> component
268
314
  pendingTools = new Map();
269
315
  // Tool output expansion state
270
316
  toolOutputExpanded = false;
271
- managedToolStatusStarted = false;
272
317
  // Thinking block visibility state
273
318
  hideThinkingBlock = false;
319
+ mermaidMarkdownTransformer = createMermaidMarkdownTransformer({
320
+ getMode: () => this.settingsManager.getMermaidRenderingMode(),
321
+ theme,
322
+ });
274
323
  outputPad = 1;
275
324
  // Agent subscription unsubscribe function
276
325
  unsubscribe;
@@ -289,15 +338,16 @@ export class InteractiveMode {
289
338
  compactionQueuedMessages = [];
290
339
  // Shutdown state
291
340
  shutdownRequested = false;
341
+ /** Reports working, blocked, done, and error states to terminals that support OSC 7501. */
342
+ programStatus = new ProgramStatusReporter(() => this.ui.terminal, () => this.sessionManager.getSessionName());
292
343
  /** The `/bug` hint is shown at most once per session so error output stays readable. */
293
344
  bugReportHintShown = false;
345
+ installChangeWarningShown = false;
294
346
  // Extension UI state
295
347
  extensionSelector = undefined;
296
348
  extensionInput = undefined;
297
349
  extensionEditor = undefined;
298
- // Extension terminal input listeners, keyed by handler so they can be
299
- // re-registered on the active renderer after a TUI mode switch.
300
- extensionTerminalInputUnsubscribers = new Map();
350
+ extensionTerminalInputSubscriptions = new Set();
301
351
  // Extension widgets (components rendered above/below the editor)
302
352
  extensionWidgetsAbove = new Map();
303
353
  extensionWidgetsBelow = new Map();
@@ -343,6 +393,7 @@ export class InteractiveMode {
343
393
  });
344
394
  this.runtimeHost.setRebindSession(async () => {
345
395
  await this.rebindCurrentSession({ renderBeforeBind: true });
396
+ this.themeController.applyFromSettings();
346
397
  });
347
398
  this.version = VERSION;
348
399
  this.renderer = createInteractiveTui({
@@ -352,13 +403,14 @@ export class InteractiveMode {
352
403
  terminal: options.terminal,
353
404
  onRightClickPaste: this.onRightClickPaste,
354
405
  fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
406
+ fullscreenWheelScrollLines: this.settingsManager.getFullscreenWheelScrollLines(),
355
407
  });
356
408
  this.ui = createInteractiveTuiReference(() => this.renderer);
357
409
  this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
358
- this.documentContainer = new Container();
359
410
  this.headerContainer = new Container();
360
411
  this.loadedResourcesContainer = new Container();
361
412
  this.chatContainer = new Container();
413
+ this.documentContainer = new Container();
362
414
  this.documentContainer.addChild(this.headerContainer);
363
415
  this.documentContainer.addChild(this.loadedResourcesContainer);
364
416
  this.documentContainer.addChild(this.chatContainer);
@@ -396,7 +448,8 @@ export class InteractiveMode {
396
448
  });
397
449
  }
398
450
  getAutocompleteSourceTag(sourceInfo) {
399
- if (!sourceInfo) {
451
+ // Built-in extension commands are untagged, like built-in commands.
452
+ if (!sourceInfo || sourceInfo.source === "builtin") {
400
453
  return undefined;
401
454
  }
402
455
  const scopePrefix = sourceInfo.scope === "user" ? "u" : sourceInfo.scope === "project" ? "p" : "t";
@@ -443,11 +496,10 @@ export class InteractiveMode {
443
496
  }));
444
497
  const modelCommand = slashCommands.find((command) => command.name === "model");
445
498
  if (modelCommand) {
446
- modelCommand.getArgumentCompletions = async (prefix) => {
447
- // Get available models (scoped or from registry)
499
+ modelCommand.getArgumentCompletions = (prefix) => {
448
500
  const models = this.session.scopedModels.length > 0
449
501
  ? this.session.scopedModels.map((s) => s.model)
450
- : await this.session.modelRuntime.getAvailable();
502
+ : this.session.modelRuntime.getAvailableSnapshot();
451
503
  if (models.length === 0)
452
504
  return null;
453
505
  // Create items with provider/id format
@@ -464,6 +516,15 @@ export class InteractiveMode {
464
516
  }));
465
517
  };
466
518
  }
519
+ const thinkingCommand = slashCommands.find((command) => command.name === "thinking");
520
+ if (thinkingCommand) {
521
+ thinkingCommand.getArgumentCompletions = (prefix) => {
522
+ return createFuzzyAutocompleteItems(this.session.getAvailableThinkingLevels(), prefix, (level) => level, (level) => ({
523
+ value: level,
524
+ label: level,
525
+ }));
526
+ };
527
+ }
467
528
  const gitCommand = slashCommands.find((command) => command.name === "git");
468
529
  if (gitCommand) {
469
530
  gitCommand.getArgumentCompletions = (prefix) => {
@@ -538,7 +599,7 @@ export class InteractiveMode {
538
599
  this.chatContainer.addChild(new Text(condensedText, 1, 0));
539
600
  }
540
601
  else {
541
- this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
602
+ this.chatContainer.addChild(new ThemedText(() => theme.bold(theme.fg("accent", "What's New")), 1, 0));
542
603
  this.chatContainer.addChild(new Spacer(1));
543
604
  this.chatContainer.addChild(new Markdown(this.changelogMarkdown.trim(), 1, 0, this.getMarkdownThemeWithSettings()));
544
605
  this.chatContainer.addChild(new Spacer(1));
@@ -590,6 +651,7 @@ export class InteractiveMode {
590
651
  terminal,
591
652
  onRightClickPaste: this.onRightClickPaste,
592
653
  fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
654
+ fullscreenWheelScrollLines: this.settingsManager.getFullscreenWheelScrollLines(),
593
655
  });
594
656
  nextUi.setClearOnShrink(clearOnShrink);
595
657
  nextUi.onDebug = onDebug;
@@ -619,7 +681,7 @@ export class InteractiveMode {
619
681
  this.registerSignalHandlers();
620
682
  // Load changelog (only show new entries, skip for resumed sessions)
621
683
  this.changelogMarkdown = this.getChangelogForDisplay();
622
- if (this.session.scopedModels.length > 0 && (this.options.verbose || !this.settingsManager.getQuietStartup())) {
684
+ if (this.session.scopedModels.length > 0 && this.shouldShowStartupDetails()) {
623
685
  const modelList = this.session.scopedModels
624
686
  .map((sm) => {
625
687
  const thinkingStr = sm.thinkingLevel ? `:${sm.thinkingLevel}` : "";
@@ -665,38 +727,45 @@ export class InteractiveMode {
665
727
  // Start the UI before initializing extensions so session_start handlers can use interactive dialogs
666
728
  this.ui.start();
667
729
  this.isInitialized = true;
668
- await this.themeController.applyFromSettings();
669
- // Brand banner always appears; quiet startup only hides keybinding help.
670
- const logo = `${gradientLogo(SELESAI_LOGO, "#ff6b2c", "#fb923c")}\n${theme.fg("dim", ` ${APP_NAME} v${this.version} · /help for commands`)}`;
671
- // Build startup instructions using keybinding hint helpers
672
- const hint = (keybinding, description) => keyHint(keybinding, description);
673
- const expandedInstructions = [
674
- hint("app.interrupt", "to interrupt"),
675
- hint("app.clear", "to clear"),
676
- rawKeyHint(`${keyText("app.clear")} twice`, "to exit"),
677
- hint("app.exit", "to exit (empty)"),
678
- hint("app.suspend", "to suspend"),
679
- keyHint("tui.editor.deleteToLineEnd", "to delete to end"),
680
- hint("app.thinking.cycle", "to cycle thinking level"),
681
- rawKeyHint(`${keyText("app.model.cycleForward")}/${keyText("app.model.cycleBackward")}`, "to cycle models"),
682
- hint("app.model.select", "to select model"),
683
- hint("app.tools.expand", "to expand tools"),
684
- hint("app.thinking.toggle", "to expand thinking"),
685
- hint("app.editor.external", "for external editor"),
686
- rawKeyHint("/", "for commands"),
687
- rawKeyHint("!", "to run bash"),
688
- rawKeyHint("!!", "to run bash (no context)"),
689
- hint("app.message.followUp", "to queue follow-up"),
690
- hint("app.message.dequeue", "to edit all queued messages"),
691
- hint("app.clipboard.pasteImage", "to paste image (with text fallback)"),
692
- rawKeyHint("drop files", "to attach"),
693
- ].join("\n");
694
- this.builtInHeader = new ExpandableText(() => logo, () => `${logo}\n\n${expandedInstructions}`, this.getStartupExpansionState(), 1, 0);
695
- // Setup UI layout
696
- this.headerContainer.addChild(new Spacer(1));
697
- this.headerContainer.addChild(this.builtInHeader);
698
- this.headerContainer.addChild(new Spacer(1));
699
- this.ui.requestRender();
730
+ this.programStatus.report();
731
+ this.ensurePngTranscoder();
732
+ this.themeController.applyFromSettings();
733
+ // The header and startup notices bake theme colors into their text, so build them once the terminal
734
+ // reported its colors. This ends at the terminal's DA1 reply, or after 100 ms if it answers nothing.
735
+ await this.themeController.waitForTerminalColors();
736
+ if (this.shouldShowStartupHeader()) {
737
+ // Keep the Selesai banner; upstream quietStartup controls header visibility.
738
+ const logo = () => `${gradientLogo(SELESAI_LOGO, "#ff6b2c", "#fb923c")}\n${theme.fg("dim", ` ${APP_NAME} v${this.version} · /help for commands`)}`;
739
+ // Build startup instructions using keybinding hint helpers
740
+ const hint = (keybinding, description) => keyHint(keybinding, description);
741
+ const expandedInstructions = () => [
742
+ hint("app.interrupt", "to interrupt"),
743
+ hint("app.clear", "to clear"),
744
+ rawKeyHint(`${keyText("app.clear")} twice`, "to exit"),
745
+ hint("app.exit", "to exit (empty)"),
746
+ hint("app.suspend", "to suspend"),
747
+ keyHint("tui.editor.deleteToLineEnd", "to delete to end"),
748
+ hint("app.thinking.cycle", "to cycle thinking level"),
749
+ rawKeyHint(`${keyText("app.model.cycleForward")}/${keyText("app.model.cycleBackward")}`, "to cycle models"),
750
+ hint("app.model.select", "to select model"),
751
+ hint("app.tools.expand", "to expand tools"),
752
+ hint("app.thinking.toggle", "to expand thinking"),
753
+ hint("app.editor.external", "for external editor"),
754
+ rawKeyHint("/", "for commands"),
755
+ rawKeyHint("!", "to run bash"),
756
+ rawKeyHint("!!", "to run bash (no context)"),
757
+ hint("app.message.followUp", "to queue follow-up"),
758
+ hint("app.message.dequeue", "to edit all queued messages"),
759
+ hint("app.clipboard.pasteImage", "to paste image (with text fallback)"),
760
+ rawKeyHint("drop files", "to attach"),
761
+ ].join("\n");
762
+ this.builtInHeader = new ExpandableText(() => logo(), () => `${logo()}\n\n${expandedInstructions()}`, this.getStartupExpansionState(), 1, 0);
763
+ // Setup UI layout
764
+ this.headerContainer.addChild(new Spacer(1));
765
+ this.headerContainer.addChild(this.builtInHeader);
766
+ this.headerContainer.addChild(new Spacer(1));
767
+ this.ui.requestRender();
768
+ }
700
769
  // Ensure fd and rg are available after mounting the TUI (downloads if missing, adds to PATH via getBinDir)
701
770
  // so slow downloads do not make startup appear frozen.
702
771
  // Both are needed: fd for autocomplete, rg for grep tool and bash commands.
@@ -725,6 +794,14 @@ export class InteractiveMode {
725
794
  });
726
795
  // Initialize available provider count for footer display
727
796
  await this.updateAvailableProviderCount();
797
+ // Flush the completed startup state before loading the remaining syntax grammars.
798
+ this.ui.renderNow();
799
+ void loadAllHighlightLanguages().then(() => {
800
+ if (!this.isInitialized)
801
+ return;
802
+ this.ui.invalidate();
803
+ this.ui.requestRender();
804
+ });
728
805
  }
729
806
  /**
730
807
  * Update terminal title with session name and cwd.
@@ -753,11 +830,15 @@ export class InteractiveMode {
753
830
  .catch(() => { })
754
831
  .finally(() => clearTimeout(timeout));
755
832
  }
756
- // Start version check asynchronously
757
- checkForNewPiVersion(this.version, PACKAGE_NAME).then((newRelease) => {
833
+ // Update checks are best-effort: a network failure must not end the session.
834
+ void checkForNewPiVersion(this.version, PACKAGE_NAME)
835
+ .then((newRelease) => {
758
836
  if (newRelease) {
759
837
  this.showNewVersionNotification(newRelease);
760
838
  }
839
+ })
840
+ .catch(() => {
841
+ this.showWarning(`Could not check for updates. The network may be unavailable or the request timed out. Your session is still active; try again later with \`${APP_NAME} update --self\`.`);
761
842
  });
762
843
  // Start package update check asynchronously
763
844
  this.checkForPackageUpdates()
@@ -780,7 +861,18 @@ export class InteractiveMode {
780
861
  }
781
862
  });
782
863
  // Show startup warnings
783
- const { migratedProviders, modelFallbackMessage, initialMessage, initialImages, initialMessages } = this.options;
864
+ const { migratedProviders, startupDiagnostics, modelFallbackMessage, initialMessage, initialImages, initialMessages, } = this.options;
865
+ for (const diagnostic of startupDiagnostics ?? []) {
866
+ if (diagnostic.type === "error") {
867
+ this.showError(diagnostic.message);
868
+ }
869
+ else if (diagnostic.type === "warning") {
870
+ this.showWarning(diagnostic.message);
871
+ }
872
+ else {
873
+ this.showStatus(diagnostic.message);
874
+ }
875
+ }
784
876
  if (migratedProviders && migratedProviders.length > 0) {
785
877
  this.showWarning(`Migrated credentials to auth.json: ${migratedProviders.join(", ")}`);
786
878
  }
@@ -965,6 +1057,14 @@ export class InteractiveMode {
965
1057
  getStartupExpansionState() {
966
1058
  return this.options.verbose || this.toolOutputExpanded;
967
1059
  }
1060
+ /** Startup header (logo, version, key hints). Hidden only by quietStartup: true. */
1061
+ shouldShowStartupHeader() {
1062
+ return this.options.verbose === true || this.settingsManager.getQuietStartup() !== true;
1063
+ }
1064
+ /** Startup details (model scope, loaded resources). Hidden by quietStartup: true or "header". */
1065
+ shouldShowStartupDetails() {
1066
+ return this.options.verbose === true || this.settingsManager.getQuietStartup() === false;
1067
+ }
968
1068
  /**
969
1069
  * Get a short path relative to the package root for display.
970
1070
  */
@@ -1228,7 +1328,7 @@ export class InteractiveMode {
1228
1328
  if (this.startupBannerDismissed)
1229
1329
  return;
1230
1330
  this.loadedResourcesContainer.clear();
1231
- const showListing = options?.force || !this.startupBannerDismissed || this.options.verbose || !this.settingsManager.getQuietStartup();
1331
+ const showListing = options?.force || this.shouldShowStartupDetails();
1232
1332
  const showDiagnostics = showListing || options?.showDiagnosticsWhenQuiet === true;
1233
1333
  if (!showListing && !showDiagnostics) {
1234
1334
  return;
@@ -1281,22 +1381,23 @@ export class InteractiveMode {
1281
1381
  if (showDiagnostics) {
1282
1382
  const skillDiagnostics = skillsResult.diagnostics;
1283
1383
  if (skillDiagnostics.length > 0) {
1284
- const warningLines = this.formatDiagnostics(skillDiagnostics, sourceInfos);
1285
- this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Skill conflicts]")}\n${warningLines}`, 0, 0));
1384
+ const warningLines = () => this.formatDiagnostics(skillDiagnostics, sourceInfos);
1385
+ this.loadedResourcesContainer.addChild(new ThemedText(() => `${theme.fg("warning", "[Skill conflicts]")}\n${warningLines()}`, 0, 0));
1286
1386
  this.loadedResourcesContainer.addChild(new Spacer(1));
1287
1387
  }
1288
1388
  const promptDiagnostics = promptsResult.diagnostics;
1289
1389
  if (promptDiagnostics.length > 0) {
1290
- const warningLines = this.formatDiagnostics(promptDiagnostics, sourceInfos);
1291
- this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Prompt conflicts]")}\n${warningLines}`, 0, 0));
1390
+ const warningLines = () => this.formatDiagnostics(promptDiagnostics, sourceInfos);
1391
+ this.loadedResourcesContainer.addChild(new ThemedText(() => `${theme.fg("warning", "[Prompt conflicts]")}\n${warningLines()}`, 0, 0));
1292
1392
  this.loadedResourcesContainer.addChild(new Spacer(1));
1293
1393
  }
1294
1394
  const extensionDiagnostics = [];
1295
- const extensionErrors = this.session.resourceLoader.getExtensions().errors;
1296
- if (extensionErrors.length > 0) {
1297
- for (const error of extensionErrors) {
1298
- extensionDiagnostics.push({ type: "error", message: error.error, path: error.path });
1299
- }
1395
+ const extensionsResult = this.session.resourceLoader.getExtensions();
1396
+ for (const error of extensionsResult.errors) {
1397
+ extensionDiagnostics.push({ type: "error", message: error.error, path: error.path });
1398
+ }
1399
+ for (const warning of extensionsResult.warnings ?? []) {
1400
+ extensionDiagnostics.push({ type: "warning", message: warning.warning, path: warning.path });
1300
1401
  }
1301
1402
  const commandDiagnostics = this.session.extensionRunner.getCommandDiagnostics();
1302
1403
  extensionDiagnostics.push(...commandDiagnostics);
@@ -1304,14 +1405,14 @@ export class InteractiveMode {
1304
1405
  const shortcutDiagnostics = this.session.extensionRunner.getShortcutDiagnostics();
1305
1406
  extensionDiagnostics.push(...shortcutDiagnostics);
1306
1407
  if (extensionDiagnostics.length > 0) {
1307
- const warningLines = this.formatDiagnostics(extensionDiagnostics, sourceInfos);
1308
- this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Extension issues]")}\n${warningLines}`, 0, 0));
1408
+ const warningLines = () => this.formatDiagnostics(extensionDiagnostics, sourceInfos);
1409
+ this.loadedResourcesContainer.addChild(new ThemedText(() => `${theme.fg("warning", "[Extension issues]")}\n${warningLines()}`, 0, 0));
1309
1410
  this.loadedResourcesContainer.addChild(new Spacer(1));
1310
1411
  }
1311
1412
  const themeDiagnostics = themesResult.diagnostics;
1312
1413
  if (themeDiagnostics.length > 0) {
1313
- const warningLines = this.formatDiagnostics(themeDiagnostics, sourceInfos);
1314
- this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Theme conflicts]")}\n${warningLines}`, 0, 0));
1414
+ const warningLines = () => this.formatDiagnostics(themeDiagnostics, sourceInfos);
1415
+ this.loadedResourcesContainer.addChild(new ThemedText(() => `${theme.fg("warning", "[Theme conflicts]")}\n${warningLines()}`, 0, 0));
1315
1416
  this.loadedResourcesContainer.addChild(new Spacer(1));
1316
1417
  }
1317
1418
  }
@@ -1403,11 +1504,24 @@ export class InteractiveMode {
1403
1504
  this.showLoadedResources({ force: false, showDiagnosticsWhenQuiet: true });
1404
1505
  this.showStartupNoticesIfNeeded();
1405
1506
  }
1507
+ applyFullscreenScrollbarSetting() {
1508
+ this.transcriptScrollView?.setScrollbar(this.settingsManager.getFullscreenScrollbar());
1509
+ }
1510
+ /** Lets extension images use the PNG transcoder; tool results register it themselves. */
1511
+ ensurePngTranscoder() {
1512
+ ensurePngTranscoder(() => {
1513
+ this.ui.invalidate();
1514
+ this.ui.requestRender();
1515
+ });
1516
+ }
1406
1517
  applyRuntimeSettings() {
1407
1518
  setCapabilityOverrides(this.settingsManager.getTerminalCapabilityOverrides());
1519
+ this.ensurePngTranscoder();
1408
1520
  configureHttpDispatcher(this.settingsManager.getHttpIdleTimeoutMs());
1521
+ this.applyFullscreenScrollbarSetting();
1409
1522
  if (this.renderer instanceof TuiAltScreen) {
1410
1523
  this.renderer.setCopyOnSelect(this.settingsManager.getFullscreenCopyOnSelect());
1524
+ this.renderer.setWheelScrollLines(this.settingsManager.getFullscreenWheelScrollLines());
1411
1525
  }
1412
1526
  this.footer.setSession(this.session);
1413
1527
  this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
@@ -1433,6 +1547,7 @@ export class InteractiveMode {
1433
1547
  const session = this.session;
1434
1548
  this.unsubscribe?.();
1435
1549
  this.unsubscribe = undefined;
1550
+ this.programStatus.reset();
1436
1551
  this.applyRuntimeSettings();
1437
1552
  if (options.renderBeforeBind) {
1438
1553
  this.renderCurrentSessionState();
@@ -1452,13 +1567,26 @@ export class InteractiveMode {
1452
1567
  async handleFatalRuntimeError(prefix, error) {
1453
1568
  const message = error instanceof Error ? error.message : String(error);
1454
1569
  this.showError(`${prefix}: ${message}`);
1570
+ const extensionHint = this.getCrashExtensionHint(error);
1571
+ if (extensionHint) {
1572
+ this.chatContainer.addChild(new ThemedText(() => theme.fg("warning", extensionHint), this.outputPad, 0));
1573
+ }
1455
1574
  if (this.recordCrash("fatal_error", error)) {
1456
- this.chatContainer.addChild(new Text(theme.fg("muted", this.crashReportInstructions()), this.outputPad, 0));
1575
+ const instructions = this.crashReportInstructions();
1576
+ this.chatContainer.addChild(new ThemedText(() => theme.fg("muted", instructions), this.outputPad, 0));
1457
1577
  }
1458
1578
  stopThemeWatcher();
1459
1579
  this.stop("transcript");
1460
1580
  process.exit(1);
1461
1581
  }
1582
+ getCrashExtensionHint(error) {
1583
+ try {
1584
+ return formatCrashExtensionHint(findExtensionStackMatches(error instanceof Error ? error.stack : undefined, this.session.resourceLoader.getExtensions().extensions));
1585
+ }
1586
+ catch {
1587
+ return undefined;
1588
+ }
1589
+ }
1462
1590
  /** Persist a crash so the next start can point the user at `/bug`. Returns false when nothing was written. */
1463
1591
  recordCrash(kind, error) {
1464
1592
  try {
@@ -1481,7 +1609,7 @@ export class InteractiveMode {
1481
1609
  if (this.bugReportHintShown)
1482
1610
  return;
1483
1611
  this.bugReportHintShown = true;
1484
- this.chatContainer.addChild(new Text(theme.fg("muted", `If this looks like a ${APP_NAME} bug, /bug sends a report to the developers.`), this.outputPad, 0));
1612
+ this.chatContainer.addChild(new ThemedText(() => theme.fg("muted", `If this looks like a ${APP_NAME} bug, /bug sends a report to the developers.`), this.outputPad, 0));
1485
1613
  this.ui.requestRender();
1486
1614
  }
1487
1615
  maybeSuggestBugReport(message) {
@@ -1489,8 +1617,32 @@ export class InteractiveMode {
1489
1617
  return;
1490
1618
  if (/\b(?:abort(?:ed)?|cancel(?:l?ed)?)\b/i.test(message.errorMessage ?? ""))
1491
1619
  return;
1620
+ if (this.maybeShowInstallChangeWarning())
1621
+ return;
1492
1622
  this.suggestBugReport();
1493
1623
  }
1624
+ /**
1625
+ * After an error, check whether an update replaced or removed this install while the session ran.
1626
+ * Code loaded on demand then fails with missing modules until restart (#10439). Returns true when
1627
+ * the install changed.
1628
+ */
1629
+ maybeShowInstallChangeWarning() {
1630
+ if (this.installChangeWarningShown)
1631
+ return true;
1632
+ const change = detectInstallChange();
1633
+ if (!change)
1634
+ return false;
1635
+ this.installChangeWarningShown = true;
1636
+ const cause = change.kind === "updated"
1637
+ ? `${APP_NAME} was updated to ${change.version} while this session was running (${VERSION})`
1638
+ : `The ${APP_NAME} installation this session runs from was removed or replaced`;
1639
+ const resumeCommand = formatResumeCommand(this.sessionManager);
1640
+ const restart = resumeCommand
1641
+ ? `Restart with \`${resumeCommand}\` to continue this session.`
1642
+ : `Restart ${APP_NAME}.`;
1643
+ this.showWarning(`${cause}. Features that load code on demand can fail until restart. ${restart}`);
1644
+ return true;
1645
+ }
1494
1646
  renderCurrentSessionState() {
1495
1647
  this.loadedResourcesContainer.clear();
1496
1648
  this.chatContainer.clear();
@@ -1509,7 +1661,10 @@ export class InteractiveMode {
1509
1661
  * whatever this returns, so they never reach into the tool registry themselves.
1510
1662
  */
1511
1663
  getRegisteredToolDefinition(toolName) {
1512
- return withBuiltInRenderers(toolName, this.session.getToolDefinition(toolName));
1664
+ return this.session.extensionRunner.resolveToolRenderers(toolName, () => withBuiltInRenderers(toolName, this.session.getToolDefinition(toolName)));
1665
+ }
1666
+ getMarkdownTransformers() {
1667
+ return [this.mermaidMarkdownTransformer, ...this.session.extensionRunner.getMarkdownTransformers()];
1513
1668
  }
1514
1669
  /**
1515
1670
  * Set up keyboard shortcuts registered by extensions.
@@ -1675,7 +1830,7 @@ export class InteractiveMode {
1675
1830
  container.addChild(new Text(line, 1, 0));
1676
1831
  }
1677
1832
  if (content.length > InteractiveMode.MAX_WIDGET_LINES) {
1678
- container.addChild(new Text(theme.fg("muted", "... (widget truncated)"), 1, 0));
1833
+ container.addChild(new ThemedText(() => theme.fg("muted", "... (widget truncated)"), 1, 0));
1679
1834
  }
1680
1835
  component = container;
1681
1836
  }
@@ -1818,23 +1973,23 @@ export class InteractiveMode {
1818
1973
  this.ui.requestRender();
1819
1974
  }
1820
1975
  addExtensionTerminalInputListener(handler) {
1821
- const unsubscribe = this.ui.addInputListener(handler);
1822
- this.extensionTerminalInputUnsubscribers.set(handler, unsubscribe);
1976
+ const subscription = { handler, unsubscribe: this.ui.addInputListener(handler) };
1977
+ this.extensionTerminalInputSubscriptions.add(subscription);
1823
1978
  return () => {
1824
- unsubscribe();
1825
- this.extensionTerminalInputUnsubscribers.delete(handler);
1979
+ subscription.unsubscribe();
1980
+ this.extensionTerminalInputSubscriptions.delete(subscription);
1826
1981
  };
1827
1982
  }
1828
1983
  rebindExtensionTerminalInputListeners() {
1829
- for (const handler of this.extensionTerminalInputUnsubscribers.keys()) {
1830
- this.extensionTerminalInputUnsubscribers.set(handler, this.ui.addInputListener(handler));
1984
+ for (const subscription of this.extensionTerminalInputSubscriptions) {
1985
+ subscription.unsubscribe();
1986
+ subscription.unsubscribe = this.ui.addInputListener(subscription.handler);
1831
1987
  }
1832
1988
  }
1833
1989
  clearExtensionTerminalInputListeners() {
1834
- for (const unsubscribe of this.extensionTerminalInputUnsubscribers.values()) {
1835
- unsubscribe();
1836
- }
1837
- this.extensionTerminalInputUnsubscribers.clear();
1990
+ for (const subscription of this.extensionTerminalInputSubscriptions)
1991
+ subscription.unsubscribe();
1992
+ this.extensionTerminalInputSubscriptions.clear();
1838
1993
  }
1839
1994
  /**
1840
1995
  * Create the ExtensionUIContext for extensions.
@@ -1909,7 +2064,7 @@ export class InteractiveMode {
1909
2064
  /**
1910
2065
  * Show a selector for extensions.
1911
2066
  */
1912
- showExtensionSelector(title, options, opts) {
2067
+ showExtensionSelector(title, options, opts, blocked = { kind: "question", message: title }) {
1913
2068
  return new Promise((resolve) => {
1914
2069
  if (opts?.signal?.aborted) {
1915
2070
  resolve(undefined);
@@ -1929,9 +2084,12 @@ export class InteractiveMode {
1929
2084
  this.hideExtensionSelector();
1930
2085
  resolve(undefined);
1931
2086
  }, { tui: this.ui, timeout: opts?.timeout, onToggleToolsExpanded: () => this.toggleToolOutputExpansion() });
2087
+ this.disposeActiveSelector();
1932
2088
  this.editorContainer.clear();
1933
2089
  this.editorContainer.addChild(this.extensionSelector);
1934
2090
  this.ui.setFocus(this.extensionSelector);
2091
+ // Extension dialogs share the editor slot: opening one replaces the status of a displaced one.
2092
+ this.programStatus.setBlocked("extension-dialog", blocked);
1935
2093
  this.ui.requestRender();
1936
2094
  });
1937
2095
  }
@@ -1943,6 +2101,7 @@ export class InteractiveMode {
1943
2101
  this.editorContainer.clear();
1944
2102
  this.editorContainer.addChild(this.editor);
1945
2103
  this.extensionSelector = undefined;
2104
+ this.programStatus.setBlocked("extension-dialog", undefined);
1946
2105
  this.ui.setFocus(this.editor);
1947
2106
  this.ui.requestRender();
1948
2107
  }
@@ -1950,7 +2109,10 @@ export class InteractiveMode {
1950
2109
  * Show a confirmation dialog for extensions.
1951
2110
  */
1952
2111
  async showExtensionConfirm(title, message, opts) {
1953
- const result = await this.showExtensionSelector(`${title}\n${message}`, ["Yes", "No"], opts);
2112
+ const result = await this.showExtensionSelector(`${title}\n${message}`, ["Yes", "No"], opts, {
2113
+ kind: "permission",
2114
+ message: title,
2115
+ });
1954
2116
  return result === "Yes";
1955
2117
  }
1956
2118
  async promptForMissingSessionCwd(error) {
@@ -1980,9 +2142,11 @@ export class InteractiveMode {
1980
2142
  this.hideExtensionInput();
1981
2143
  resolve(undefined);
1982
2144
  }, { tui: this.ui, timeout: opts?.timeout });
2145
+ this.disposeActiveSelector();
1983
2146
  this.editorContainer.clear();
1984
2147
  this.editorContainer.addChild(this.extensionInput);
1985
2148
  this.ui.setFocus(this.extensionInput);
2149
+ this.programStatus.setBlocked("extension-dialog", { kind: "question", message: title });
1986
2150
  this.ui.requestRender();
1987
2151
  });
1988
2152
  }
@@ -1994,6 +2158,7 @@ export class InteractiveMode {
1994
2158
  this.editorContainer.clear();
1995
2159
  this.editorContainer.addChild(this.editor);
1996
2160
  this.extensionInput = undefined;
2161
+ this.programStatus.setBlocked("extension-dialog", undefined);
1997
2162
  this.ui.setFocus(this.editor);
1998
2163
  this.ui.requestRender();
1999
2164
  }
@@ -2009,9 +2174,11 @@ export class InteractiveMode {
2009
2174
  this.hideExtensionEditor();
2010
2175
  resolve(undefined);
2011
2176
  }, undefined, this.settingsManager.getExternalEditorCommand());
2177
+ this.disposeActiveSelector();
2012
2178
  this.editorContainer.clear();
2013
2179
  this.editorContainer.addChild(this.extensionEditor);
2014
2180
  this.ui.setFocus(this.extensionEditor);
2181
+ this.programStatus.setBlocked("extension-dialog", { kind: "question", message: title });
2015
2182
  this.ui.requestRender();
2016
2183
  });
2017
2184
  }
@@ -2022,6 +2189,7 @@ export class InteractiveMode {
2022
2189
  this.editorContainer.clear();
2023
2190
  this.editorContainer.addChild(this.editor);
2024
2191
  this.extensionEditor = undefined;
2192
+ this.programStatus.setBlocked("extension-dialog", undefined);
2025
2193
  this.ui.setFocus(this.editor);
2026
2194
  this.ui.requestRender();
2027
2195
  }
@@ -2033,6 +2201,7 @@ export class InteractiveMode {
2033
2201
  this.editorComponentFactory = factory;
2034
2202
  // Save text from current editor before switching
2035
2203
  const currentText = this.editor.getText();
2204
+ this.disposeActiveSelector();
2036
2205
  this.editorContainer.clear();
2037
2206
  if (factory) {
2038
2207
  // Create the custom editor with tui, theme, and keybindings
@@ -2049,6 +2218,9 @@ export class InteractiveMode {
2049
2218
  if (newEditor.setPaddingX !== undefined) {
2050
2219
  newEditor.setPaddingX(this.defaultEditor.getPaddingX());
2051
2220
  }
2221
+ if (newEditor.setAutocompleteMaxVisible !== undefined) {
2222
+ newEditor.setAutocompleteMaxVisible(this.defaultEditor.getAutocompleteMaxVisible());
2223
+ }
2052
2224
  // Set autocomplete if supported
2053
2225
  if (newEditor.setAutocompleteProvider && this.autocompleteProvider) {
2054
2226
  newEditor.setAutocompleteProvider(this.autocompleteProvider);
@@ -2160,6 +2332,7 @@ export class InteractiveMode {
2160
2332
  options?.onHandle?.(handle);
2161
2333
  }
2162
2334
  else {
2335
+ this.disposeActiveSelector();
2163
2336
  this.editorContainer.clear();
2164
2337
  this.editorContainer.addChild(component);
2165
2338
  this.ui.setFocus(component);
@@ -2180,17 +2353,14 @@ export class InteractiveMode {
2180
2353
  */
2181
2354
  showExtensionError(extensionPath, error, stack) {
2182
2355
  const errorMsg = `Extension "${extensionPath}" error: ${error}`;
2183
- const errorText = new Text(theme.fg("error", errorMsg), 1, 0);
2356
+ const errorText = new ThemedText(() => theme.fg("error", errorMsg), 1, 0);
2184
2357
  this.chatContainer.addChild(errorText);
2185
2358
  if (stack) {
2186
2359
  // Show stack trace in dim color, indented
2187
- const stackLines = stack
2188
- .split("\n")
2189
- .slice(1) // Skip first line (duplicates error message)
2190
- .map((line) => theme.fg("dim", ` ${line.trim()}`))
2191
- .join("\n");
2192
- if (stackLines) {
2193
- this.chatContainer.addChild(new Text(stackLines, 1, 0));
2360
+ const stackLines = stack.split("\n").slice(1); // Skip first line (duplicates error message)
2361
+ if (stackLines.length > 0) {
2362
+ const renderStack = () => stackLines.map((line) => theme.fg("dim", ` ${line.trim()}`)).join("\n");
2363
+ this.chatContainer.addChild(new ThemedText(renderStack, 1, 0));
2194
2364
  }
2195
2365
  }
2196
2366
  this.ui.requestRender();
@@ -2260,8 +2430,8 @@ export class InteractiveMode {
2260
2430
  this.updateEditorBorderColor();
2261
2431
  }
2262
2432
  };
2263
- // Handle clipboard paste (triggered on Ctrl+V). Images are attached by path;
2264
- // otherwise, paste plain text from the system clipboard.
2433
+ // Handle clipboard paste (triggered on Ctrl+V). Copied files use their original paths,
2434
+ // images are attached via temporary files, and plain text is the final fallback.
2265
2435
  this.defaultEditor.onPasteImage = () => {
2266
2436
  void this.handleClipboardPaste();
2267
2437
  };
@@ -2284,32 +2454,37 @@ export class InteractiveMode {
2284
2454
  }
2285
2455
  async handleClipboardPaste() {
2286
2456
  try {
2457
+ const filePaths = await readClipboardFilePaths();
2458
+ if (filePaths) {
2459
+ if (filePaths.some((filePath) => /\p{Cc}/u.test(filePath))) {
2460
+ throw new Error("Clipboard file path contains control characters");
2461
+ }
2462
+ const paths = this.isBashMode ? filePaths.map(quoteIfNeeded).join(" ") : filePaths.join("\n");
2463
+ const cursor = this.editor.getCursor?.();
2464
+ const currentLine = cursor ? (this.editor.getText().split("\n")[cursor.line] ?? "") : "";
2465
+ const characterBeforeCursor = cursor && cursor.col > 0 ? currentLine[cursor.col - 1] : "";
2466
+ const characterAfterCursor = cursor ? currentLine[cursor.col] : "";
2467
+ const leadingSpace = characterBeforeCursor && !/\s/.test(characterBeforeCursor) ? " " : "";
2468
+ const trailingSpace = characterAfterCursor && !/\s/.test(characterAfterCursor) ? " " : "";
2469
+ this.editor.insertTextAtCursor?.(`${leadingSpace}${paths}${trailingSpace}`);
2470
+ this.ui.requestRender();
2471
+ return;
2472
+ }
2287
2473
  const image = await readClipboardImage();
2288
2474
  if (image) {
2289
2475
  const tmpDir = os.tmpdir();
2290
2476
  const ext = extensionForImageMimeType(image.mimeType) ?? "png";
2291
- const fileName = `selesai-clipboard-${Date.now()}-${this.pendingInputImages.length + 1}.${ext}`;
2477
+ const fileName = `selesai-clipboard-${crypto.randomUUID()}.${ext}`;
2292
2478
  const filePath = path.join(tmpDir, fileName);
2293
2479
  fs.writeFileSync(filePath, Buffer.from(image.bytes));
2294
- let attachment;
2295
- let dimensionNote;
2296
- const autoResizeImages = this.settingsManager.getImageAutoResize();
2297
- if (autoResizeImages) {
2298
- const resized = await resizeImage(image.bytes, image.mimeType).catch(() => null);
2299
- if (!resized) {
2300
- this.showWarning("Clipboard image attached without resize");
2301
- attachment = { type: "image", mimeType: image.mimeType, data: Buffer.from(image.bytes).toString("base64") };
2302
- }
2303
- else {
2304
- dimensionNote = formatDimensionNote(resized);
2305
- attachment = { type: "image", mimeType: resized.mimeType, data: resized.data };
2306
- }
2307
- }
2308
- else {
2309
- attachment = { type: "image", mimeType: image.mimeType, data: Buffer.from(image.bytes).toString("base64") };
2310
- }
2311
- this.pendingInputImages.push(attachment);
2312
- this.editor.insertTextAtCursor?.(dimensionNote ? `${filePath} ${dimensionNote}` : filePath);
2480
+ // Keep the fork caption attachment. AgentSession normalizes it after hooks
2481
+ // select the request model; resizing here would discard source resolution.
2482
+ this.pendingInputImages.push({
2483
+ type: "image",
2484
+ mimeType: image.mimeType,
2485
+ data: Buffer.from(image.bytes).toString("base64"),
2486
+ });
2487
+ this.editor.insertTextAtCursor?.(filePath);
2313
2488
  this.ui.requestRender();
2314
2489
  return;
2315
2490
  }
@@ -2320,7 +2495,7 @@ export class InteractiveMode {
2320
2495
  }
2321
2496
  }
2322
2497
  catch (error) {
2323
- this.showWarning(`Failed to paste clipboard: ${error instanceof Error ? error.message : String(error)}`);
2498
+ this.showError(`Failed to paste from clipboard: ${error instanceof Error ? error.message : String(error)}`);
2324
2499
  }
2325
2500
  }
2326
2501
  consumePendingInputImages() {
@@ -2379,6 +2554,12 @@ export class InteractiveMode {
2379
2554
  await this.handleModelCommand(searchTerm);
2380
2555
  return;
2381
2556
  }
2557
+ if (text === "/thinking" || text.startsWith("/thinking ")) {
2558
+ const searchTerm = text.startsWith("/thinking ") ? text.slice(10).trim() : undefined;
2559
+ this.editor.setText("");
2560
+ this.handleThinkingCommand(searchTerm);
2561
+ return;
2562
+ }
2382
2563
  if (text === "/export" || text.startsWith("/export ")) {
2383
2564
  await this.handleExportCommand(text);
2384
2565
  this.editor.setText("");
@@ -2568,6 +2749,7 @@ export class InteractiveMode {
2568
2749
  await this.init();
2569
2750
  }
2570
2751
  this.footer.invalidate();
2752
+ this.programStatus.handleEvent(event);
2571
2753
  switch (event.type) {
2572
2754
  case "agent_start":
2573
2755
  this.pendingTools.clear();
@@ -2597,6 +2779,8 @@ export class InteractiveMode {
2597
2779
  this.ui.requestRender();
2598
2780
  break;
2599
2781
  case "entry_appended":
2782
+ if (this.entriesRenderedByBoundaryCompaction.delete(event.entry.id))
2783
+ break;
2600
2784
  if (event.entry.type === "custom") {
2601
2785
  this.addCustomEntryToChat(event.entry);
2602
2786
  this.ui.requestRender();
@@ -2605,6 +2789,34 @@ export class InteractiveMode {
2605
2789
  this.addCacheWarmingUsage(event.entry);
2606
2790
  this.ui.requestRender();
2607
2791
  }
2792
+ else if (event.entry.type === "custom_message" && event.entry.display) {
2793
+ this.addMessageToChat(createCustomMessage(event.entry.customType, event.entry.content, event.entry.display, event.entry.details, event.entry.timestamp));
2794
+ this.ui.requestRender();
2795
+ }
2796
+ else if (event.entry.type === "compaction") {
2797
+ const entries = this.sessionManager.buildContextEntries();
2798
+ if (entries[0]?.id !== event.entry.id)
2799
+ break;
2800
+ this.chatContainer.clear();
2801
+ const branch = this.sessionManager.getBranch();
2802
+ const compactionIndex = branch.findIndex((entry) => entry.id === event.entry.id);
2803
+ const entriesAfterCompaction = new Set(branch.slice(compactionIndex + 1).map((entry) => entry.id));
2804
+ const retainedEntries = entries.slice(1);
2805
+ this.renderSessionEntries(retainedEntries.filter((entry) => !entriesAfterCompaction.has(entry.id)));
2806
+ this.addMessageToChat(createCompactionSummaryMessage(event.entry.summary, event.entry.tokensBefore, event.entry.timestamp));
2807
+ if (event.entry.usage) {
2808
+ this.addCompactionCostNotice({
2809
+ type: "compaction_cost",
2810
+ kind: "compaction",
2811
+ usage: event.entry.usage,
2812
+ });
2813
+ }
2814
+ this.renderSessionEntries(retainedEntries.filter((entry) => entriesAfterCompaction.has(entry.id)));
2815
+ for (const entryId of entriesAfterCompaction)
2816
+ this.entriesRenderedByBoundaryCompaction.add(entryId);
2817
+ this.footer.invalidate();
2818
+ this.ui.requestRender();
2819
+ }
2608
2820
  break;
2609
2821
  case "session_info_changed":
2610
2822
  this.updateTerminalTitle();
@@ -2626,23 +2838,24 @@ export class InteractiveMode {
2626
2838
  this.ui.requestRender();
2627
2839
  }
2628
2840
  else if (event.message.role === "assistant") {
2629
- this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad);
2841
+ this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.getMarkdownTransformers());
2630
2842
  this.streamingMessage = event.message;
2631
2843
  this.chatContainer.addChild(this.streamingComponent);
2632
- this.streamingComponent.updateContent(this.streamingMessage);
2844
+ this.streamingComponent.updateContent(this.streamingMessage, true);
2633
2845
  this.ui.requestRender();
2634
2846
  }
2635
2847
  break;
2636
2848
  case "message_update":
2637
2849
  if (this.streamingComponent && event.message.role === "assistant") {
2638
2850
  this.streamingMessage = event.message;
2639
- this.streamingComponent.updateContent(this.streamingMessage);
2851
+ this.streamingComponent.updateContent(this.streamingMessage, true);
2640
2852
  for (const content of this.streamingMessage.content) {
2641
2853
  if (content.type === "toolCall") {
2642
2854
  if (!this.pendingTools.has(content.id)) {
2643
2855
  const component = new ToolExecutionComponent(content.name, content.id, content.arguments, {
2644
2856
  showImages: this.settingsManager.getShowImages(),
2645
2857
  imageWidthCells: this.settingsManager.getImageWidthCells(),
2858
+ outputPad: this.outputPad,
2646
2859
  }, this.getRegisteredToolDefinition(content.name), this.ui, this.sessionManager.getCwd());
2647
2860
  component.setExpanded(this.toolOutputExpanded);
2648
2861
  this.chatContainer.addChild(component);
@@ -2673,7 +2886,7 @@ export class InteractiveMode {
2673
2886
  : "Operation aborted";
2674
2887
  this.streamingMessage.errorMessage = errorMessage;
2675
2888
  }
2676
- this.streamingComponent.updateContent(this.streamingMessage);
2889
+ this.streamingComponent.updateContent(this.streamingMessage, false);
2677
2890
  if (this.streamingMessage.stopReason === "aborted" || this.streamingMessage.stopReason === "error") {
2678
2891
  if (!errorMessage) {
2679
2892
  errorMessage = this.streamingMessage.errorMessage || "Error";
@@ -2705,11 +2918,15 @@ export class InteractiveMode {
2705
2918
  // The bash execution callback handles TUI output rendering.
2706
2919
  break;
2707
2920
  case "tool_execution_start": {
2921
+ // Nested calls (from codemode scripts) are shown inside their parent's row.
2922
+ if (event.parentToolCallId)
2923
+ break;
2708
2924
  let component = this.pendingTools.get(event.toolCallId);
2709
2925
  if (!component) {
2710
2926
  component = new ToolExecutionComponent(event.toolName, event.toolCallId, event.args, {
2711
2927
  showImages: this.settingsManager.getShowImages(),
2712
2928
  imageWidthCells: this.settingsManager.getImageWidthCells(),
2929
+ outputPad: this.outputPad,
2713
2930
  }, this.getRegisteredToolDefinition(event.toolName), this.ui, this.sessionManager.getCwd());
2714
2931
  component.setExpanded(this.toolOutputExpanded);
2715
2932
  this.chatContainer.addChild(component);
@@ -2729,9 +2946,11 @@ export class InteractiveMode {
2729
2946
  break;
2730
2947
  }
2731
2948
  case "tool_execution_end": {
2949
+ if (event.isError)
2950
+ this.maybeShowInstallChangeWarning();
2732
2951
  const component = this.pendingTools.get(event.toolCallId);
2733
2952
  if (component) {
2734
- component.updateResult({ ...event.result, isError: event.isError });
2953
+ component.updateResult({ ...event.result, isError: event.isError, durationMs: event.durationMs });
2735
2954
  this.pendingTools.delete(event.toolCallId);
2736
2955
  this.ui.requestRender();
2737
2956
  }
@@ -2795,9 +3014,21 @@ export class InteractiveMode {
2795
3014
  }
2796
3015
  }
2797
3016
  else if (event.result) {
3017
+ const entries = this.sessionManager.buildContextEntries();
3018
+ if (entries[0]?.type !== "compaction") {
3019
+ throw new Error("Completed compaction is missing from the session context");
3020
+ }
2798
3021
  this.chatContainer.clear();
2799
- this.rebuildChatFromMessages();
3022
+ // The latest compaction is prepended for model context; append it below at its chronological position.
3023
+ this.renderSessionEntries(entries.slice(1));
2800
3024
  this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString()));
3025
+ if (event.result.usage) {
3026
+ this.addCompactionCostNotice({
3027
+ type: "compaction_cost",
3028
+ kind: "compaction",
3029
+ usage: event.result.usage,
3030
+ });
3031
+ }
2801
3032
  this.footer.invalidate();
2802
3033
  }
2803
3034
  else if (event.errorMessage) {
@@ -2806,7 +3037,8 @@ export class InteractiveMode {
2806
3037
  }
2807
3038
  else {
2808
3039
  this.chatContainer.addChild(new Spacer(1));
2809
- this.chatContainer.addChild(new Text(theme.fg("error", event.errorMessage), 1, 0));
3040
+ const errorMessage = event.errorMessage;
3041
+ this.chatContainer.addChild(new ThemedText(() => theme.fg("error", errorMessage), 1, 0));
2810
3042
  }
2811
3043
  }
2812
3044
  void this.flushCompactionQueue({ willRetry: event.willRetry });
@@ -2878,7 +3110,7 @@ export class InteractiveMode {
2878
3110
  }
2879
3111
  const message = status.type === "warning" ? `Warning: ${status.message}` : status.message;
2880
3112
  const color = status.type === "warning" ? "warning" : "dim";
2881
- this.chatContainer.addChild(new Text(theme.fg(color, message), 1, 0));
3113
+ this.chatContainer.addChild(new ThemedText(() => theme.fg(color, message), 1, 0));
2882
3114
  this.lastStatusSpacer = undefined;
2883
3115
  this.lastStatusText = undefined;
2884
3116
  this.ui.requestRender();
@@ -2894,12 +3126,14 @@ export class InteractiveMode {
2894
3126
  const last = children.length > 0 ? children[children.length - 1] : undefined;
2895
3127
  const secondLast = children.length > 1 ? children[children.length - 2] : undefined;
2896
3128
  if (last && secondLast && last === this.lastStatusText && secondLast === this.lastStatusSpacer) {
2897
- this.lastStatusText.setText(theme.fg("dim", message));
3129
+ this.lastStatusMessage = message;
3130
+ this.lastStatusText.invalidate();
2898
3131
  this.ui.requestRender();
2899
3132
  return;
2900
3133
  }
2901
3134
  const spacer = new Spacer(1);
2902
- const text = new Text(theme.fg("dim", message), 1, 0);
3135
+ this.lastStatusMessage = message;
3136
+ const text = new ThemedText(() => theme.fg("dim", this.lastStatusMessage), 1, 0);
2903
3137
  this.chatContainer.addChild(spacer);
2904
3138
  this.chatContainer.addChild(text);
2905
3139
  this.lastStatusSpacer = spacer;
@@ -2911,7 +3145,7 @@ export class InteractiveMode {
2911
3145
  if (!renderer) {
2912
3146
  return;
2913
3147
  }
2914
- const component = new CustomEntryComponent(entry, renderer);
3148
+ const component = new CustomEntryComponent(entry, renderer, this.outputPad);
2915
3149
  component.setExpanded(this.toolOutputExpanded);
2916
3150
  if (!component.hasContent()) {
2917
3151
  return;
@@ -2928,7 +3162,7 @@ export class InteractiveMode {
2928
3162
  addMessageToChat(message, options) {
2929
3163
  switch (message.role) {
2930
3164
  case "bashExecution": {
2931
- const component = new BashExecutionComponent(message.command, this.ui, message.excludeFromContext);
3165
+ const component = new BashExecutionComponent(message.command, this.ui, message.excludeFromContext, this.outputPad);
2932
3166
  if (message.output) {
2933
3167
  component.appendOutput(message.output);
2934
3168
  }
@@ -2947,14 +3181,14 @@ export class InteractiveMode {
2947
3181
  }
2948
3182
  case "compactionSummary": {
2949
3183
  this.chatContainer.addChild(new Spacer(1));
2950
- const component = new CompactionSummaryMessageComponent(message, this.getMarkdownThemeWithSettings());
3184
+ const component = new CompactionSummaryMessageComponent(message, this.getMarkdownThemeWithSettings(), this.outputPad);
2951
3185
  component.setExpanded(this.toolOutputExpanded);
2952
3186
  this.chatContainer.addChild(component);
2953
3187
  break;
2954
3188
  }
2955
3189
  case "branchSummary": {
2956
3190
  this.chatContainer.addChild(new Spacer(1));
2957
- const component = new BranchSummaryMessageComponent(message, this.getMarkdownThemeWithSettings());
3191
+ const component = new BranchSummaryMessageComponent(message, this.getMarkdownThemeWithSettings(), this.outputPad);
2958
3192
  component.setExpanded(this.toolOutputExpanded);
2959
3193
  this.chatContainer.addChild(component);
2960
3194
  break;
@@ -2970,18 +3204,18 @@ export class InteractiveMode {
2970
3204
  const skillBlocks = parseSkillBlocks(textContent);
2971
3205
  if (skillBlocks) {
2972
3206
  for (const skillBlock of skillBlocks.skills) {
2973
- const component = new SkillInvocationMessageComponent(skillBlock, this.getMarkdownThemeWithSettings());
3207
+ const component = new SkillInvocationMessageComponent(skillBlock, this.getMarkdownThemeWithSettings(), this.outputPad);
2974
3208
  component.setExpanded(this.toolOutputExpanded);
2975
3209
  this.chatContainer.addChild(component);
2976
3210
  }
2977
3211
  if (skillBlocks.userMessage) {
2978
3212
  this.chatContainer.addChild(new Spacer(1));
2979
- const userComponent = new UserMessageComponent(skillBlocks.userMessage, this.getMarkdownThemeWithSettings(), this.outputPad);
3213
+ const userComponent = new UserMessageComponent(skillBlocks.userMessage, this.getMarkdownThemeWithSettings(), this.outputPad, this.getMarkdownTransformers());
2980
3214
  this.chatContainer.addChild(userComponent);
2981
3215
  }
2982
3216
  }
2983
3217
  else {
2984
- const userComponent = new UserMessageComponent(textContent, this.getMarkdownThemeWithSettings(), this.outputPad);
3218
+ const userComponent = new UserMessageComponent(textContent, this.getMarkdownThemeWithSettings(), this.outputPad, this.getMarkdownTransformers());
2985
3219
  this.chatContainer.addChild(userComponent);
2986
3220
  }
2987
3221
  if (options?.populateHistory) {
@@ -2991,7 +3225,7 @@ export class InteractiveMode {
2991
3225
  break;
2992
3226
  }
2993
3227
  case "assistant": {
2994
- const assistantComponent = new AssistantMessageComponent(message, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad);
3228
+ const assistantComponent = new AssistantMessageComponent(message, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.getMarkdownTransformers());
2995
3229
  this.chatContainer.addChild(assistantComponent);
2996
3230
  break;
2997
3231
  }
@@ -3043,6 +3277,7 @@ export class InteractiveMode {
3043
3277
  const component = new ToolExecutionComponent(content.name, content.id, content.arguments, {
3044
3278
  showImages: this.settingsManager.getShowImages(),
3045
3279
  imageWidthCells: this.settingsManager.getImageWidthCells(),
3280
+ outputPad: this.outputPad,
3046
3281
  }, this.getRegisteredToolDefinition(content.name), this.ui, this.sessionManager.getCwd());
3047
3282
  component.setExpanded(this.toolOutputExpanded);
3048
3283
  this.chatContainer.addChild(component);
@@ -3096,11 +3331,18 @@ export class InteractiveMode {
3096
3331
  * @param options.populateHistory Add user messages to editor history
3097
3332
  */
3098
3333
  renderSessionEntries(entries, options = {}) {
3334
+ // Selection coordinates point into the transcript being replaced (#9311).
3335
+ if (this.renderer instanceof TuiAltScreen)
3336
+ this.renderer.resetTextSelection();
3099
3337
  const items = entries.flatMap((entry) => {
3100
3338
  if (entry.type === "custom" || (entry.type === "usage" && entry.kind === "cache_warm")) {
3101
3339
  return [entry];
3102
3340
  }
3103
- return sessionEntryToContextMessages(entry);
3341
+ const messages = sessionEntryToContextMessages(entry);
3342
+ if ((entry.type === "compaction" || entry.type === "branch_summary") && entry.usage && messages.length > 0) {
3343
+ return [...messages, { type: "compaction_cost", kind: entry.type, usage: entry.usage }];
3344
+ }
3345
+ return messages;
3104
3346
  });
3105
3347
  this.renderSessionItems(items, options);
3106
3348
  }
@@ -3108,7 +3350,8 @@ export class InteractiveMode {
3108
3350
  if (!this.settingsManager.getShowCacheMissNotices())
3109
3351
  return;
3110
3352
  this.chatContainer.addChild(new Spacer(1));
3111
- this.chatContainer.addChild(new Text(theme.fg("dim", formatCacheWarmingUsage(entry)), 1, 0));
3353
+ const usage = formatCacheWarmingUsage(entry);
3354
+ this.chatContainer.addChild(new ThemedText(() => theme.fg("dim", usage), 1, 0));
3112
3355
  }
3113
3356
  /**
3114
3357
  * Render billing usage for a compaction or branch summary. The notice is derived
@@ -3122,7 +3365,7 @@ export class InteractiveMode {
3122
3365
  const cost = usage.cost.total >= 0.01 ? ` (~$${usage.cost.total.toFixed(2)})` : "";
3123
3366
  const label = notice.kind === "compaction" ? "Compaction" : "Branch summary";
3124
3367
  this.chatContainer.addChild(new Spacer(1));
3125
- this.chatContainer.addChild(new Text(theme.fg("warning", `${label}: ${formatTokens(tokens)} tokens billed${cost}`), 1, 0));
3368
+ this.chatContainer.addChild(new ThemedText(() => theme.fg("warning", `${label}: ${formatTokens(tokens)} tokens billed${cost}`), 1, 0));
3126
3369
  }
3127
3370
  static countDroppedThinkingBlocks(message) {
3128
3371
  let count = 0;
@@ -3159,7 +3402,7 @@ export class InteractiveMode {
3159
3402
  return;
3160
3403
  const noun = droppedCount === 1 ? "thinking block" : "thinking blocks";
3161
3404
  this.chatContainer.addChild(new Spacer(1));
3162
- this.chatContainer.addChild(new Text(theme.fg("warning", `Anthropic dropped ${droppedCount} ${noun} (details in session)`), 1, 0));
3405
+ this.chatContainer.addChild(new ThemedText(() => theme.fg("warning", `Anthropic dropped ${droppedCount} ${noun} (details in session)`), 1, 0));
3163
3406
  }
3164
3407
  /**
3165
3408
  * Show a transcript notice when a completed assistant message paid for a
@@ -3186,9 +3429,8 @@ export class InteractiveMode {
3186
3429
  else if (miss.idleMs >= CACHE_TTL_MS) {
3187
3430
  label = `Cache miss after ${Math.round(miss.idleMs / 60_000)}m idle`;
3188
3431
  }
3189
- const text = theme.fg("warning", `${label}: ${reBilled}`);
3190
3432
  this.chatContainer.addChild(new Spacer(1));
3191
- this.chatContainer.addChild(new Text(text, 1, 0));
3433
+ this.chatContainer.addChild(new ThemedText(() => theme.fg("warning", `${label}: ${reBilled}`), 1, 0));
3192
3434
  }
3193
3435
  renderInitialMessages() {
3194
3436
  const entries = this.sessionManager.buildContextEntries();
@@ -3216,7 +3458,7 @@ export class InteractiveMode {
3216
3458
  if (this.chatContainer.children.length > 0) {
3217
3459
  this.chatContainer.addChild(new Spacer(1));
3218
3460
  }
3219
- this.chatContainer.addChild(new Text(theme.fg("warning", `This project is not trusted. Project ${CONFIG_DIR_NAME} resources and packages are ignored. Use /trust to save a trust decision, then restart pi.`), 1, 0));
3461
+ this.chatContainer.addChild(new ThemedText(() => theme.fg("warning", `This project is not trusted. Project ${CONFIG_DIR_NAME} resources and packages are ignored. Use /trust to save a trust decision, then restart pi.`), 1, 0));
3220
3462
  }
3221
3463
  async getUserInput() {
3222
3464
  const queuedInput = this.pendingUserInputs.shift();
@@ -3313,6 +3555,10 @@ export class InteractiveMode {
3313
3555
  * paste / Kitty / modifyOtherKeys sequences.
3314
3556
  */
3315
3557
  uncaughtCrash(error) {
3558
+ // A dead terminal is not a pi crash. Do not try to restore it or record it.
3559
+ if (isDeadTerminalError(error)) {
3560
+ this.emergencyTerminalExit();
3561
+ }
3316
3562
  if (this.isShuttingDown) {
3317
3563
  process.exit(1);
3318
3564
  }
@@ -3331,6 +3577,9 @@ export class InteractiveMode {
3331
3577
  catch { }
3332
3578
  console.error(`${APP_NAME} exiting due to uncaughtException:`);
3333
3579
  console.error(error);
3580
+ const extensionHint = this.getCrashExtensionHint(error);
3581
+ if (extensionHint)
3582
+ console.error(`\n${extensionHint}`);
3334
3583
  if (this.recordCrash("uncaught_exception", error)) {
3335
3584
  console.error(`\n${this.crashReportInstructions()}`);
3336
3585
  }
@@ -3368,10 +3617,13 @@ export class InteractiveMode {
3368
3617
  }
3369
3618
  throw error;
3370
3619
  };
3371
- process.stdout.on("error", terminalErrorHandler);
3372
- process.stderr.on("error", terminalErrorHandler);
3373
- this.signalCleanupHandlers.push(() => process.stdout.off("error", terminalErrorHandler));
3374
- this.signalCleanupHandlers.push(() => process.stderr.off("error", terminalErrorHandler));
3620
+ // stdin needs the handler too: once the terminal is gone, reads and setRawMode
3621
+ // fail with EIO (orphaned background process group) or ENOTTY (revoked tty).
3622
+ // Node emits these as stream errors, which are uncaught without a listener.
3623
+ for (const stream of [process.stdin, process.stdout, process.stderr]) {
3624
+ stream.on("error", terminalErrorHandler);
3625
+ this.signalCleanupHandlers.push(() => stream.off("error", terminalErrorHandler));
3626
+ }
3375
3627
  // Restore the terminal before the process dies on any uncaught throw.
3376
3628
  // Without this, an unhandled exception from extension code (or anywhere
3377
3629
  // in pi) leaves the terminal in raw mode with no cursor.
@@ -3504,6 +3756,8 @@ export class InteractiveMode {
3504
3756
  this.setToolsExpanded(!this.toolOutputExpanded);
3505
3757
  }
3506
3758
  setToolsExpanded(expanded) {
3759
+ if (expanded === this.toolOutputExpanded)
3760
+ return;
3507
3761
  this.toolOutputExpanded = expanded;
3508
3762
  const activeHeader = this.customHeader ?? this.builtInHeader;
3509
3763
  if (isExpandable(activeHeader)) {
@@ -3561,20 +3815,20 @@ export class InteractiveMode {
3561
3815
  }
3562
3816
  showError(errorMessage) {
3563
3817
  this.chatContainer.addChild(new Spacer(1));
3564
- this.chatContainer.addChild(new Text(theme.fg("error", `Error: ${errorMessage}`), 1, 0));
3818
+ this.chatContainer.addChild(new ThemedText(() => theme.fg("error", `Error: ${errorMessage}`), this.outputPad, 0));
3565
3819
  this.ui.requestRender();
3566
3820
  }
3567
3821
  showWarning(warningMessage) {
3568
3822
  this.chatContainer.addChild(new Spacer(1));
3569
- this.chatContainer.addChild(new Text(theme.fg("warning", `Warning: ${warningMessage}`), 1, 0));
3823
+ this.chatContainer.addChild(new ThemedText(() => theme.fg("warning", `Warning: ${warningMessage}`), 1, 0));
3570
3824
  this.ui.requestRender();
3571
3825
  }
3572
3826
  showNewVersionNotification(release) {
3573
- const updateInstruction = `New version ${release.version} is available. ${getUpdateInstruction(PACKAGE_NAME)}`;
3827
+ const updateInstruction = () => `New version ${release.version} is available. ${getUpdateInstruction(PACKAGE_NAME)}`;
3574
3828
  const note = release.note?.trim();
3575
3829
  this.chatContainer.addChild(new Spacer(1));
3576
3830
  this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
3577
- this.chatContainer.addChild(new Text(`${theme.bold(theme.fg("warning", "Update Available"))}\n${updateInstruction}`, 1, 0));
3831
+ this.chatContainer.addChild(new ThemedText(() => `${theme.bold(theme.fg("warning", "Update Available"))}\n${updateInstruction()}`, 1, 0));
3578
3832
  if (note) {
3579
3833
  this.chatContainer.addChild(new Spacer(1));
3580
3834
  this.chatContainer.addChild(new Markdown(note, 1, 0, this.getMarkdownThemeWithSettings(), {
@@ -3585,12 +3839,12 @@ export class InteractiveMode {
3585
3839
  this.ui.requestRender();
3586
3840
  }
3587
3841
  showPackageUpdateNotification(packages) {
3588
- const action = theme.fg("accent", `${APP_NAME} update --extensions`);
3589
- const updateInstruction = theme.fg("muted", "Package updates are available. Run ") + action;
3842
+ const updateInstruction = () => theme.fg("muted", "Package updates are available. Run ") +
3843
+ theme.fg("accent", `${APP_NAME} update --extensions`);
3590
3844
  const packageLines = packages.map((pkg) => `- ${pkg}`).join("\n");
3591
3845
  this.chatContainer.addChild(new Spacer(1));
3592
3846
  this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
3593
- this.chatContainer.addChild(new Text(`${theme.bold(theme.fg("warning", "Package Updates Available"))}\n${updateInstruction}\n${theme.fg("muted", "Packages:")}\n${packageLines}`, 1, 0));
3847
+ this.chatContainer.addChild(new ThemedText(() => `${theme.bold(theme.fg("warning", "Package Updates Available"))}\n${updateInstruction()}\n${theme.fg("muted", "Packages:")}\n${packageLines}`, 1, 0));
3594
3848
  this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
3595
3849
  this.ui.requestRender();
3596
3850
  }
@@ -3763,29 +4017,43 @@ export class InteractiveMode {
3763
4017
  // =========================================================================
3764
4018
  // Selectors
3765
4019
  // =========================================================================
4020
+ disposeActiveSelector() {
4021
+ const dispose = this.activeSelectorDispose;
4022
+ this.activeSelectorToken = undefined;
4023
+ this.activeSelectorDispose = undefined;
4024
+ dispose?.();
4025
+ }
3766
4026
  /**
3767
4027
  * Shows a selector component in place of the editor.
3768
4028
  * @param create Factory that receives a `done` callback and returns the component and focus target
3769
4029
  */
3770
4030
  showSelector(create) {
4031
+ const token = {};
4032
+ let dispose;
3771
4033
  const done = () => {
4034
+ dispose?.();
4035
+ if (this.activeSelectorToken !== token)
4036
+ return;
4037
+ this.activeSelectorToken = undefined;
4038
+ this.activeSelectorDispose = undefined;
3772
4039
  this.editorContainer.clear();
3773
4040
  this.editorContainer.addChild(this.editor);
3774
4041
  this.ui.setFocus(this.editor);
3775
4042
  };
3776
- const { component, focus } = create(done);
4043
+ const created = create(done);
4044
+ dispose = created.dispose;
4045
+ this.disposeActiveSelector();
4046
+ this.activeSelectorToken = token;
4047
+ this.activeSelectorDispose = dispose;
3777
4048
  this.editorContainer.clear();
3778
- this.editorContainer.addChild(component);
3779
- this.ui.setFocus(focus);
4049
+ this.editorContainer.addChild(created.component);
4050
+ this.ui.setFocus(created.focus);
3780
4051
  this.ui.requestRender();
3781
4052
  }
3782
4053
  showSettingsSelector() {
3783
4054
  this.showSelector((done) => {
3784
4055
  const selector = new SettingsSelectorComponent({
3785
4056
  autoCompact: this.session.autoCompactionEnabled,
3786
- autoHandoffEnabled: this.settingsManager.getAutoHandoffEnabled(),
3787
- autoHandoffThresholdTokens: this.settingsManager.getAutoHandoffThresholdTokens(),
3788
- capabilityGatewayJevEnabled: this.settingsManager.getCapabilityGatewayJevEnabled(),
3789
4057
  defaultModel: (() => {
3790
4058
  const defaultProvider = this.settingsManager.getDefaultProvider();
3791
4059
  const defaultModelId = this.settingsManager.getDefaultModel();
@@ -3803,11 +4071,11 @@ export class InteractiveMode {
3803
4071
  visionModelSelector: (currentValue, selectorDone) => {
3804
4072
  const slash = currentValue.indexOf("/");
3805
4073
  const currentModel = slash > 0 ? this.session.modelRuntime.getModel(currentValue.slice(0, slash), currentValue.slice(slash + 1)) : undefined;
3806
- return new ModelSelectorComponent(this.ui, currentModel, this.settingsManager, this.session.modelRuntime, this.session.scopedModels, (model) => {
4074
+ return new ModelSelectorComponent(this.ui, currentModel, this.session.modelRuntime, this.session.scopedModels, (model) => {
3807
4075
  this.settingsManager.setImageCaptionModel(model ? `${model.provider}/${model.id}` : undefined);
3808
4076
  selectorDone(model ? `${model.provider}/${model.id}` : "off");
3809
4077
  this.showStatus(model ? `Vision caption model: ${model.provider}/${model.id}` : "Vision captioning disabled");
3810
- }, selectorDone, undefined, { modelFilter: (model) => model.input.includes("image"), allowOff: true, persistSelection: false });
4078
+ }, selectorDone, undefined, undefined, undefined, { modelFilter: (model) => model.input.includes("image"), allowOff: true });
3811
4079
  },
3812
4080
  steeringMode: this.session.steeringMode,
3813
4081
  followUpMode: this.session.followUpMode,
@@ -3817,10 +4085,11 @@ export class InteractiveMode {
3817
4085
  thinkingLevel: this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
3818
4086
  availableThinkingLevels: [...THINKING_LEVEL_OPTIONS],
3819
4087
  modelThinkingLevels: this.settingsManager.getAllModelThinkingLevels(),
3820
- currentTheme: this.themeController.getThemeSelection() || "dark",
4088
+ currentTheme: this.themeController.getThemeSelection() || SYSTEM_THEME_NAME,
3821
4089
  terminalTheme: this.themeController.getTerminalTheme(),
3822
4090
  availableThemes: getAvailableThemes(),
3823
4091
  hideThinkingBlock: this.hideThinkingBlock,
4092
+ mermaidRenderingMode: this.settingsManager.getMermaidRenderingMode(),
3824
4093
  collapseChangelog: this.settingsManager.getCollapseChangelog(),
3825
4094
  enableInstallTelemetry: this.settingsManager.getEnableInstallTelemetry(),
3826
4095
  doubleEscapeAction: this.settingsManager.getDoubleEscapeAction(),
@@ -3838,6 +4107,7 @@ export class InteractiveMode {
3838
4107
  fullscreenExitOutput: this.settingsManager.getFullscreenExitOutput(),
3839
4108
  fullscreenScrollbar: this.settingsManager.getFullscreenScrollbar(),
3840
4109
  fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
4110
+ fullscreenWheelScrollLines: this.settingsManager.getFullscreenWheelScrollLines(),
3841
4111
  warnings: this.settingsManager.getWarnings(),
3842
4112
  skills: this.buildSkillToggleItems(),
3843
4113
  }, {
@@ -3845,29 +4115,6 @@ export class InteractiveMode {
3845
4115
  this.session.setAutoCompactionEnabled(enabled);
3846
4116
  this.footer.setAutoCompactEnabled(enabled);
3847
4117
  },
3848
- onAutoHandoffEnabledChange: (enabled) => {
3849
- this.settingsManager.setAutoHandoffEnabled(enabled);
3850
- },
3851
- onAutoHandoffThresholdTokensChange: (tokens) => {
3852
- this.settingsManager.setAutoHandoffThresholdTokens(tokens);
3853
- },
3854
- onCapabilityGatewayJevChange: (enabled) => {
3855
- if (enabled && !this.session.modelRuntime.getProviderAuthStatus("tokenin").configured) {
3856
- this.showStatus("Token-In is required for Jev routing; starting /tokenin add");
3857
- done();
3858
- void this.session
3859
- .prompt("/tokenin add")
3860
- .then(() => {
3861
- if (this.session.modelRuntime.getProviderAuthStatus("tokenin").configured) {
3862
- this.settingsManager.setCapabilityGatewayJevEnabled(true);
3863
- }
3864
- })
3865
- .catch((error) => this.showError(`Could not start Token-In setup: ${String(error)}`));
3866
- return false;
3867
- }
3868
- this.settingsManager.setCapabilityGatewayJevEnabled(enabled);
3869
- return true;
3870
- },
3871
4118
  onShowImagesChange: (enabled) => {
3872
4119
  this.settingsManager.setShowImages(enabled);
3873
4120
  for (const child of this.chatContainer.children) {
@@ -3919,6 +4166,7 @@ export class InteractiveMode {
3919
4166
  },
3920
4167
  onModelThinkingLevelChange: (provider, modelId, level) => {
3921
4168
  this.settingsManager.setModelThinkingLevel(provider, modelId, level);
4169
+ // If the override is for the current model, apply it to the session too
3922
4170
  const current = this.session.model;
3923
4171
  if (current && current.provider === provider && current.id === modelId) {
3924
4172
  this.session.setThinkingLevel(level);
@@ -3928,6 +4176,7 @@ export class InteractiveMode {
3928
4176
  },
3929
4177
  onModelThinkingLevelRemove: (provider, modelId) => {
3930
4178
  this.settingsManager.removeModelThinkingLevel(provider, modelId);
4179
+ // If the override was for the current model, revert to global default
3931
4180
  const current = this.session.model;
3932
4181
  if (current && current.provider === provider && current.id === modelId) {
3933
4182
  const globalDefault = this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
@@ -3936,14 +4185,9 @@ export class InteractiveMode {
3936
4185
  this.updateEditorBorderColor();
3937
4186
  }
3938
4187
  },
3939
- onThinkingLevelChange: (level) => {
3940
- this.session.setThinkingLevel(level);
3941
- this.footer.invalidate();
3942
- this.updateEditorBorderColor();
3943
- },
3944
4188
  onThemeChange: (themeSetting) => {
3945
4189
  this.settingsManager.setTheme(themeSetting);
3946
- void this.themeController.setThemeSetting(themeSetting);
4190
+ this.themeController.setThemeSetting(themeSetting);
3947
4191
  },
3948
4192
  onThemePreview: (themeName) => this.themeController.preview(themeName),
3949
4193
  onHideThinkingBlockChange: (hidden) => {
@@ -3951,6 +4195,11 @@ export class InteractiveMode {
3951
4195
  this.settingsManager.setHideThinkingBlock(hidden);
3952
4196
  this.updateThinkingBlockVisibility();
3953
4197
  },
4198
+ onMermaidRenderingModeChange: (mode) => {
4199
+ this.settingsManager.setMermaidRenderingMode(mode);
4200
+ this.chatContainer.invalidate();
4201
+ this.ui.requestRender();
4202
+ },
3954
4203
  onShowCacheMissNoticesChange: (shown) => {
3955
4204
  this.settingsManager.setShowCacheMissNotices(shown);
3956
4205
  this.rebuildChatFromMessages();
@@ -3961,8 +4210,8 @@ export class InteractiveMode {
3961
4210
  onEnableInstallTelemetryChange: (enabled) => {
3962
4211
  this.settingsManager.setEnableInstallTelemetry(enabled);
3963
4212
  },
3964
- onQuietStartupChange: (enabled) => {
3965
- this.settingsManager.setQuietStartup(enabled);
4213
+ onQuietStartupChange: (quiet) => {
4214
+ this.settingsManager.setQuietStartup(quiet);
3966
4215
  },
3967
4216
  onDefaultProjectTrustChange: (defaultProjectTrust) => {
3968
4217
  this.settingsManager.setDefaultProjectTrust(defaultProjectTrust);
@@ -3987,21 +4236,14 @@ export class InteractiveMode {
3987
4236
  onOutputPadChange: (padding) => {
3988
4237
  this.settingsManager.setOutputPad(padding);
3989
4238
  this.outputPad = padding;
3990
- if (this.streamingComponent || this.session.isStreaming) {
3991
- for (const child of this.chatContainer.children) {
3992
- if (child instanceof AssistantMessageComponent ||
3993
- child instanceof CustomMessageComponent ||
3994
- child instanceof UserMessageComponent) {
4239
+ for (const container of [this.chatContainer, this.pendingMessagesContainer]) {
4240
+ for (const child of container.children) {
4241
+ if ("setOutputPad" in child && typeof child.setOutputPad === "function") {
3995
4242
  child.setOutputPad(padding);
3996
4243
  }
3997
4244
  }
3998
- if (this.streamingComponent) {
3999
- this.streamingComponent.setOutputPad(padding);
4000
- }
4001
- this.ui.requestRender();
4002
- return;
4003
4245
  }
4004
- this.rebuildChatFromMessages();
4246
+ this.ui.requestRender();
4005
4247
  },
4006
4248
  onAutocompleteMaxVisibleChange: (maxVisible) => {
4007
4249
  this.settingsManager.setAutocompleteMaxVisible(maxVisible);
@@ -4044,6 +4286,11 @@ export class InteractiveMode {
4044
4286
  if (this.renderer instanceof TuiAltScreen)
4045
4287
  this.renderer.setCopyOnSelect(enabled);
4046
4288
  },
4289
+ onFullscreenWheelScrollLinesChange: (lines) => {
4290
+ this.settingsManager.setFullscreenWheelScrollLines(lines);
4291
+ if (this.renderer instanceof TuiAltScreen)
4292
+ this.renderer.setWheelScrollLines(lines);
4293
+ },
4047
4294
  onWarningsChange: (warnings) => {
4048
4295
  this.settingsManager.setWarnings(warnings);
4049
4296
  },
@@ -4061,6 +4308,44 @@ export class InteractiveMode {
4061
4308
  return { component: selector, focus: selector.getSettingsList() };
4062
4309
  });
4063
4310
  }
4311
+ handleThinkingCommand(searchTerm) {
4312
+ const availableLevels = this.session.getAvailableThinkingLevels();
4313
+ if (!searchTerm) {
4314
+ this.showThinkingSelector();
4315
+ return;
4316
+ }
4317
+ const normalized = searchTerm.trim().toLowerCase();
4318
+ const level = availableLevels.find((candidate) => candidate.toLowerCase() === normalized);
4319
+ if (!level) {
4320
+ this.showError(`Unknown thinking level "${searchTerm}". Available levels: ${availableLevels.join(", ")}.`);
4321
+ return;
4322
+ }
4323
+ this.selectThinkingLevel(level, false);
4324
+ }
4325
+ selectThinkingLevel(level, persist) {
4326
+ try {
4327
+ this.session.setThinkingLevel(level, { persist });
4328
+ this.footer.invalidate();
4329
+ this.updateEditorBorderColor();
4330
+ this.showStatus(persist ? `Default thinking level: ${level}` : `Thinking level: ${level}`);
4331
+ }
4332
+ catch (error) {
4333
+ this.showError(error instanceof Error ? error.message : String(error));
4334
+ }
4335
+ }
4336
+ showThinkingSelector() {
4337
+ this.showSelector((done) => {
4338
+ const selectLevel = (level, persist) => {
4339
+ this.selectThinkingLevel(level, persist);
4340
+ done();
4341
+ };
4342
+ const selector = new ThinkingSelectorComponent(this.session.thinkingLevel ?? DEFAULT_THINKING_LEVEL, this.session.getAvailableThinkingLevels(), (level) => selectLevel(level, false), () => {
4343
+ done();
4344
+ this.ui.requestRender();
4345
+ }, (level) => selectLevel(level, true), this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);
4346
+ return { component: selector, focus: selector };
4347
+ });
4348
+ }
4064
4349
  getVisionModels() {
4065
4350
  try {
4066
4351
  const models = this.session.scopedModels.length > 0
@@ -4136,12 +4421,11 @@ export class InteractiveMode {
4136
4421
  const model = await this.findExactModelMatch(searchTerm);
4137
4422
  if (model) {
4138
4423
  try {
4139
- await this.session.setModel(model);
4424
+ await this.session.setModel(model, { persist: false });
4140
4425
  this.footer.invalidate();
4141
4426
  this.updateEditorBorderColor();
4142
4427
  this.showStatus(`Model: ${model.id}`);
4143
4428
  void this.maybeWarnAboutAnthropicSubscriptionAuth(model);
4144
- this.checkDaxnutsEasterEgg(model);
4145
4429
  }
4146
4430
  catch (error) {
4147
4431
  this.showError(error instanceof Error ? error.message : String(error));
@@ -4151,21 +4435,37 @@ export class InteractiveMode {
4151
4435
  this.showModelSelector(searchTerm);
4152
4436
  }
4153
4437
  async findExactModelMatch(searchTerm) {
4154
- const models = await this.getModelCandidates();
4155
- return findExactModelReferenceMatch(searchTerm, models);
4156
- }
4157
- async getModelCandidates() {
4158
- if (this.session.scopedModels.length > 0) {
4159
- return this.session.scopedModels.map((scoped) => scoped.model);
4160
- }
4438
+ const cachedModels = this.session.scopedModels.length > 0
4439
+ ? this.session.scopedModels.map((scoped) => scoped.model)
4440
+ : [...this.session.modelRuntime.getAvailableSnapshot()];
4441
+ const cachedMatch = findExactModelReferenceMatch(searchTerm, cachedModels);
4442
+ if (cachedMatch || this.session.scopedModels.length > 0)
4443
+ return cachedMatch;
4444
+ this.showStatus("Refreshing model catalogs…");
4445
+ const controller = new AbortController();
4446
+ let timedOut = false;
4447
+ const timeout = setTimeout(() => {
4448
+ timedOut = true;
4449
+ controller.abort();
4450
+ }, 15_000);
4161
4451
  try {
4162
- const controller = new AbortController();
4163
- await refreshModelCatalogs(this.session.modelRuntime, controller.signal);
4164
- return [...(await this.session.modelRuntime.getAvailable())];
4452
+ const result = await refreshModelCatalogs(this.session.modelRuntime, controller.signal);
4453
+ if (result.aborted && timedOut) {
4454
+ this.showWarning("Model refresh timed out; searching cached models.");
4455
+ }
4456
+ else if (result.errors.size > 0) {
4457
+ this.showWarning(`Could not refresh ${[...result.errors.keys()].join(", ")}; searching cached models.`);
4458
+ }
4165
4459
  }
4166
- catch {
4167
- return [];
4460
+ catch (error) {
4461
+ this.showWarning(timedOut
4462
+ ? "Model refresh timed out; searching cached models."
4463
+ : `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`);
4168
4464
  }
4465
+ finally {
4466
+ clearTimeout(timeout);
4467
+ }
4468
+ return findExactModelReferenceMatch(searchTerm, [...this.session.modelRuntime.getAvailableSnapshot()]);
4169
4469
  }
4170
4470
  /** Update the footer's available provider count from the current snapshot without refreshing catalogs. */
4171
4471
  updateAvailableProviderCount() {
@@ -4249,95 +4549,91 @@ export class InteractiveMode {
4249
4549
  }
4250
4550
  showModelSelector(initialSearchInput) {
4251
4551
  this.showSelector((done) => {
4252
- const selector = new ModelSelectorComponent(this.ui, this.session.model, this.settingsManager, this.session.modelRuntime, this.session.scopedModels, async (model) => {
4253
- if (!model)
4254
- return;
4552
+ const selectModel = async (model, persist) => {
4255
4553
  try {
4256
- await this.session.setModel(model);
4554
+ await this.session.setModel(model, { persist });
4257
4555
  this.updateAvailableProviderCount();
4258
4556
  this.footer.invalidate();
4259
4557
  this.updateEditorBorderColor();
4260
4558
  done();
4261
- this.showStatus(`Model: ${model.id}`);
4559
+ this.showStatus(persist ? `Default model: ${model.provider}/${model.id}` : `Model: ${model.id}`);
4262
4560
  void this.maybeWarnAboutAnthropicSubscriptionAuth(model);
4263
- this.checkDaxnutsEasterEgg(model);
4264
4561
  }
4265
4562
  catch (error) {
4266
4563
  done();
4267
4564
  this.showError(error instanceof Error ? error.message : String(error));
4268
4565
  }
4566
+ };
4567
+ const defaultProvider = this.settingsManager.getDefaultProvider();
4568
+ const defaultModel = this.settingsManager.getDefaultModel();
4569
+ const selector = new ModelSelectorComponent(this.ui, this.session.model, this.session.modelRuntime, this.session.scopedModels, (model) => {
4570
+ if (model)
4571
+ selectModel(model, false);
4269
4572
  }, () => {
4270
4573
  done();
4271
4574
  this.ui.requestRender();
4272
- }, initialSearchInput);
4273
- return { component: selector, focus: selector };
4575
+ }, initialSearchInput, (model) => selectModel(model, true), defaultProvider && defaultModel ? { provider: defaultProvider, id: defaultModel } : undefined);
4576
+ return { component: selector, focus: selector, dispose: () => selector.dispose() };
4274
4577
  });
4275
4578
  }
4276
- async showModelsSelector() {
4277
- // Get all available models
4278
- const controller = new AbortController();
4279
- await refreshModelCatalogs(this.session.modelRuntime, controller.signal);
4280
- const allModels = [...(await this.session.modelRuntime.getAvailable())];
4281
- const allModelIds = new Set(allModels.map((model) => `${model.provider}/${model.id}`));
4579
+ showModelsSelector() {
4580
+ let availableModels = [...this.session.modelRuntime.getAvailableSnapshot()];
4581
+ let availableModelIds = new Set(availableModels.map((model) => `${model.provider}/${model.id}`));
4282
4582
  const configuredPatterns = this.settingsManager.getEnabledModels();
4283
4583
  const sessionScopedModels = this.session.scopedModels;
4284
- if (allModels.length === 0 && !configuredPatterns?.length && sessionScopedModels.length === 0) {
4285
- this.showStatus("No models available");
4286
- return;
4287
- }
4288
- const configuredScope = configuredPatterns?.length
4289
- ? await resolveModelScopeWithDiagnostics(configuredPatterns, this.session.modelRuntime)
4290
- : undefined;
4291
- // Check if session has scoped models (from previous session-only changes or CLI --models)
4292
- const hasSessionScope = sessionScopedModels.length > 0;
4293
- // Build enabled model IDs from session state or settings
4294
- let currentEnabledIds = null;
4295
- if (hasSessionScope) {
4296
- // Use current session's scoped models
4297
- currentEnabledIds = sessionScopedModels.map((scoped) => `${scoped.model.provider}/${scoped.model.id}`);
4298
- }
4299
- else if (configuredScope) {
4300
- currentEnabledIds = configuredScope.scopedModels.map((scoped) => `${scoped.model.provider}/${scoped.model.id}`);
4301
- }
4302
- for (const diagnostic of configuredScope?.diagnostics ?? []) {
4303
- if (diagnostic.code !== "no-match")
4304
- continue;
4305
- currentEnabledIds ??= [];
4306
- if (!currentEnabledIds.includes(diagnostic.pattern))
4307
- currentEnabledIds.push(diagnostic.pattern);
4308
- }
4309
- // Helper to update session's scoped models (session-only, no persist)
4310
- const updateSessionModels = async (enabledIds) => {
4584
+ const configuredEnabledIds = (models) => {
4585
+ if (!configuredPatterns?.length)
4586
+ return null;
4587
+ const resolved = resolveModelScopeFromModels(configuredPatterns, models);
4588
+ const ids = resolved.scopedModels.map((scoped) => `${scoped.model.provider}/${scoped.model.id}`);
4589
+ for (const diagnostic of resolved.diagnostics) {
4590
+ if (diagnostic.code === "no-match" && !ids.includes(diagnostic.pattern))
4591
+ ids.push(diagnostic.pattern);
4592
+ }
4593
+ return ids;
4594
+ };
4595
+ let currentEnabledIds = sessionScopedModels.length > 0
4596
+ ? sessionScopedModels.map((scoped) => `${scoped.model.provider}/${scoped.model.id}`)
4597
+ : configuredEnabledIds(availableModels);
4598
+ let selectionChanged = false;
4599
+ const updateSessionModels = (enabledIds) => {
4311
4600
  currentEnabledIds = enabledIds === null ? null : [...enabledIds];
4312
- const hasEnabledAvailableModel = enabledIds?.some((id) => allModelIds.has(id)) ?? false;
4313
- const allAvailableModelsEnabled = enabledIds !== null && [...allModelIds].every((id) => enabledIds.includes(id));
4601
+ const hasEnabledAvailableModel = enabledIds?.some((id) => availableModelIds.has(id)) ?? false;
4602
+ const allAvailableModelsEnabled = enabledIds !== null && [...availableModelIds].every((id) => enabledIds.includes(id));
4314
4603
  if (enabledIds && hasEnabledAvailableModel && !allAvailableModelsEnabled) {
4315
- const newScopedModels = await resolveModelScope(enabledIds, this.session.modelRuntime);
4316
- this.session.setScopedModels(newScopedModels.map((sm) => ({
4317
- model: sm.model,
4318
- thinkingLevel: sm.thinkingLevel,
4604
+ const newScopedModels = resolveModelScopeFromModels(enabledIds, availableModels).scopedModels;
4605
+ this.session.setScopedModels(newScopedModels.map((scoped) => ({
4606
+ model: scoped.model,
4607
+ thinkingLevel: scoped.thinkingLevel,
4319
4608
  })));
4320
4609
  }
4321
4610
  else {
4322
- // All enabled or none enabled = no filter
4323
4611
  this.session.setScopedModels([]);
4324
4612
  }
4325
- await this.updateAvailableProviderCount();
4613
+ this.updateAvailableProviderCount();
4326
4614
  this.ui.requestRender();
4327
4615
  };
4328
4616
  this.showSelector((done) => {
4617
+ let disposed = false;
4618
+ let timedOut = false;
4619
+ const controller = new AbortController();
4620
+ const timeout = setTimeout(() => {
4621
+ timedOut = true;
4622
+ controller.abort();
4623
+ }, 15_000);
4329
4624
  const selector = new ScopedModelsSelectorComponent({
4330
- allModels,
4625
+ allModels: availableModels,
4331
4626
  enabledModelIds: currentEnabledIds,
4627
+ refreshStatus: "Refreshing model catalogs…",
4332
4628
  }, {
4333
- onChange: async (enabledIds) => {
4334
- await updateSessionModels(enabledIds);
4629
+ onChange: (enabledIds) => {
4630
+ selectionChanged = true;
4631
+ updateSessionModels(enabledIds);
4335
4632
  },
4336
4633
  onPersist: (enabledIds) => {
4337
- // Persist to settings
4338
4634
  const allEnabled = enabledIds !== null &&
4339
- enabledIds.length === allModels.length &&
4340
- enabledIds.every((id) => allModelIds.has(id));
4635
+ enabledIds.length === availableModels.length &&
4636
+ enabledIds.every((id) => availableModelIds.has(id));
4341
4637
  const newPatterns = enabledIds === null || allEnabled ? undefined : enabledIds;
4342
4638
  this.settingsManager.setEnabledModels(newPatterns ? [...newPatterns] : undefined);
4343
4639
  this.showStatus("Model selection saved to settings");
@@ -4347,7 +4643,50 @@ export class InteractiveMode {
4347
4643
  this.ui.requestRender();
4348
4644
  },
4349
4645
  });
4350
- return { component: selector, focus: selector };
4646
+ void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
4647
+ .then((result) => {
4648
+ if (disposed)
4649
+ return;
4650
+ availableModels = [...this.session.modelRuntime.getAvailableSnapshot()];
4651
+ availableModelIds = new Set(availableModels.map((model) => `${model.provider}/${model.id}`));
4652
+ if (!selectionChanged && sessionScopedModels.length === 0) {
4653
+ currentEnabledIds = configuredEnabledIds(availableModels);
4654
+ selector.updateModels(availableModels, currentEnabledIds);
4655
+ }
4656
+ else {
4657
+ selector.updateModels(availableModels);
4658
+ }
4659
+ if (currentEnabledIds !== null)
4660
+ updateSessionModels(currentEnabledIds);
4661
+ if (result.aborted && timedOut) {
4662
+ selector.setRefreshStatus("Model refresh timed out; showing cached models.", "warning");
4663
+ }
4664
+ else if (result.errors.size > 0) {
4665
+ selector.setRefreshStatus(`Could not refresh ${[...result.errors.keys()].join(", ")}; showing cached models.`, "warning");
4666
+ }
4667
+ else {
4668
+ selector.setRefreshStatus("Model catalogs refreshed.", "success");
4669
+ }
4670
+ this.ui.requestRender();
4671
+ })
4672
+ .catch((error) => {
4673
+ if (disposed)
4674
+ return;
4675
+ selector.setRefreshStatus(timedOut
4676
+ ? "Model refresh timed out; showing cached models."
4677
+ : `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`, "warning");
4678
+ this.ui.requestRender();
4679
+ })
4680
+ .finally(() => clearTimeout(timeout));
4681
+ return {
4682
+ component: selector,
4683
+ focus: selector,
4684
+ dispose: () => {
4685
+ disposed = true;
4686
+ clearTimeout(timeout);
4687
+ controller.abort();
4688
+ },
4689
+ };
4351
4690
  });
4352
4691
  }
4353
4692
  showUserMessageSelector() {
@@ -4592,6 +4931,7 @@ export class InteractiveMode {
4592
4931
  source: authStatus.label ?? authStatus.source,
4593
4932
  }
4594
4933
  : undefined;
4934
+ const subscription = provider.auth.oauth?.isSubscription === true;
4595
4935
  if ((!authType || authType === "oauth") && provider.auth.oauth) {
4596
4936
  options.push({
4597
4937
  id: provider.id,
@@ -4599,6 +4939,7 @@ export class InteractiveMode {
4599
4939
  authType: "oauth",
4600
4940
  method: provider.auth.oauth,
4601
4941
  status,
4942
+ subscription,
4602
4943
  });
4603
4944
  }
4604
4945
  if ((!authType || authType === "api_key") && provider.auth.apiKey) {
@@ -4608,19 +4949,24 @@ export class InteractiveMode {
4608
4949
  authType: "api_key",
4609
4950
  method: provider.auth.apiKey,
4610
4951
  status,
4952
+ subscription,
4611
4953
  });
4612
4954
  }
4613
4955
  }
4614
4956
  return options.sort((a, b) => a.name.localeCompare(b.name));
4615
4957
  }
4616
4958
  async getLogoutProviderOptions() {
4617
- return (await this.session.modelRuntime.listCredentials())
4618
- .map(({ providerId, type }) => ({
4619
- id: providerId,
4620
- name: this.session.modelRuntime.getProvider(providerId)?.name ?? providerId,
4621
- authType: type,
4622
- status: { type, source: "stored credential" },
4623
- }))
4959
+ return (await this.session.modelRuntime.listCredentials({ signal: AbortSignal.timeout(15_000) }))
4960
+ .map(({ providerId, type }) => {
4961
+ const provider = this.session.modelRuntime.getProvider(providerId);
4962
+ return {
4963
+ id: providerId,
4964
+ name: provider?.name ?? providerId,
4965
+ authType: type,
4966
+ status: { type, source: "stored credential" },
4967
+ subscription: provider?.auth.oauth?.isSubscription === true,
4968
+ };
4969
+ })
4624
4970
  .sort((a, b) => a.name.localeCompare(b.name));
4625
4971
  }
4626
4972
  findLoginProviderOptions(providerRef) {
@@ -4632,7 +4978,6 @@ export class InteractiveMode {
4632
4978
  provider.name.toLowerCase() === normalizedProviderRef);
4633
4979
  }
4634
4980
  async handleLoginCommand(providerRef) {
4635
- await this.session.modelRuntime.getAvailable();
4636
4981
  if (!providerRef) {
4637
4982
  this.showLoginAuthTypeSelector();
4638
4983
  return;
@@ -4651,18 +4996,25 @@ export class InteractiveMode {
4651
4996
  }
4652
4997
  this.showLoginProviderSelector(undefined, providerRef);
4653
4998
  }
4654
- async startProviderLogin(providerOption) {
4999
+ /** `onBack` reopens the selector the login was started from when the user cancels it. */
5000
+ async startProviderLogin(providerOption, onBack) {
4655
5001
  if (providerOption.authType === "oauth") {
4656
- await this.showLoginDialog(providerOption.id, providerOption.name);
5002
+ await this.showLoginDialog(providerOption.id, providerOption.name, onBack);
4657
5003
  }
4658
5004
  else if (providerOption.method?.login) {
4659
- await this.showApiKeyLoginDialog(providerOption.id, providerOption.name);
5005
+ await this.showApiKeyLoginDialog(providerOption.id, providerOption.name, onBack);
4660
5006
  }
4661
5007
  else {
4662
- this.showAmbientAuthDialog(providerOption);
5008
+ this.showAmbientAuthDialog(providerOption, onBack);
4663
5009
  }
4664
5010
  }
4665
5011
  showLoginAuthTypeSelector(providerOptions) {
5012
+ // The top-level selector offers Radius directly, as its last option.
5013
+ const radiusOption = providerOptions
5014
+ ? undefined
5015
+ : this.getLoginProviderOptions("oauth").find((provider) => provider.id === RADIUS_PROVIDER_ID);
5016
+ const radiusText = radiusOption ? `Sign in with ${radiusOption.name}` : undefined;
5017
+ const radiusLabel = radiusOption ? `${radiusText}${formatAuthSelectorProviderStatus(radiusOption)}` : undefined;
4666
5018
  const oauthProvider = providerOptions?.find((provider) => provider.authType === "oauth");
4667
5019
  const oauthLoginLabel = oauthProvider?.method && "loginLabel" in oauthProvider.method ? oauthProvider.method.loginLabel : undefined;
4668
5020
  const subscriptionLabel = oauthLoginLabel ?? "Sign in with an account";
@@ -4677,6 +5029,8 @@ export class InteractiveMode {
4677
5029
  if (availableAuthTypes.has("api_key")) {
4678
5030
  options.push(apiKeyLabel);
4679
5031
  }
5032
+ if (radiusLabel)
5033
+ options.push(radiusLabel);
4680
5034
  if (options.length === 0) {
4681
5035
  this.showStatus("No login methods available.");
4682
5036
  return;
@@ -4692,29 +5046,37 @@ export class InteractiveMode {
4692
5046
  ? `Select authentication method for ${providerOptions[0].name}:`
4693
5047
  : "Select authentication method:";
4694
5048
  this.showSelector((done) => {
4695
- const selector = new ExtensionSelectorComponent(title, options, (option) => {
5049
+ const onSelect = (option) => {
4696
5050
  done();
5051
+ if (radiusOption && option === radiusLabel) {
5052
+ void this.startProviderLogin(radiusOption, () => this.showLoginAuthTypeSelector());
5053
+ return;
5054
+ }
4697
5055
  const authType = option === subscriptionLabel ? "oauth" : "api_key";
4698
5056
  if (providerOptions) {
4699
5057
  const providerOption = providerOptions.find((provider) => provider.authType === authType);
4700
5058
  if (providerOption) {
4701
- void this.startProviderLogin(providerOption);
5059
+ void this.startProviderLogin(providerOption, () => this.showLoginAuthTypeSelector(providerOptions));
4702
5060
  }
4703
5061
  return;
4704
5062
  }
4705
5063
  this.showLoginProviderSelector(authType);
4706
- }, () => {
5064
+ };
5065
+ const onCancel = () => {
4707
5066
  done();
4708
5067
  this.ui.requestRender();
4709
- });
4710
- return { component: selector, focus: selector };
5068
+ };
5069
+ const selector = radiusLabel && radiusText
5070
+ ? createLoginMenuSelector(this.ui, title, options, { label: radiusLabel, text: radiusText }, onSelect, onCancel)
5071
+ : new ExtensionSelectorComponent(title, options, onSelect, onCancel);
5072
+ return { component: selector, focus: selector, dispose: () => selector.dispose() };
4711
5073
  });
4712
5074
  }
4713
5075
  showLoginProviderSelector(authType, initialSearchInput) {
4714
5076
  const providerOptions = this.getLoginProviderOptions(authType);
4715
5077
  if (providerOptions.length === 0) {
4716
5078
  const message = authType === "oauth"
4717
- ? "No subscription providers available."
5079
+ ? "No account providers available."
4718
5080
  : authType === "api_key"
4719
5081
  ? "No API key providers available."
4720
5082
  : "No login providers available.";
@@ -4728,7 +5090,7 @@ export class InteractiveMode {
4728
5090
  if (!providerOption) {
4729
5091
  return;
4730
5092
  }
4731
- await this.startProviderLogin(providerOption);
5093
+ await this.startProviderLogin(providerOption, () => this.showLoginProviderSelector(authType, initialSearchInput));
4732
5094
  }, () => {
4733
5095
  done();
4734
5096
  if (authType) {
@@ -4746,7 +5108,14 @@ export class InteractiveMode {
4746
5108
  this.showLoginAuthTypeSelector();
4747
5109
  return;
4748
5110
  }
4749
- const providerOptions = await this.getLogoutProviderOptions();
5111
+ let providerOptions;
5112
+ try {
5113
+ providerOptions = await this.getLogoutProviderOptions();
5114
+ }
5115
+ catch (error) {
5116
+ this.showError(`Could not read stored credentials: ${error instanceof Error ? error.message : String(error)}`);
5117
+ return;
5118
+ }
4750
5119
  if (providerOptions.length === 0) {
4751
5120
  this.showStatus("No stored credentials to remove. /logout only removes credentials saved by /login; environment variables and models.json config are unchanged.");
4752
5121
  return;
@@ -4759,7 +5128,9 @@ export class InteractiveMode {
4759
5128
  return;
4760
5129
  }
4761
5130
  try {
4762
- await this.session.modelRuntime.logout(providerOption.id);
5131
+ await this.session.modelRuntime.logout(providerOption.id, {
5132
+ signal: AbortSignal.timeout(15_000),
5133
+ });
4763
5134
  await this.updateAvailableProviderCount();
4764
5135
  const message = providerOption.authType === "oauth"
4765
5136
  ? `Logged out of ${providerOption.name}`
@@ -4767,7 +5138,10 @@ export class InteractiveMode {
4767
5138
  this.showStatus(message);
4768
5139
  }
4769
5140
  catch (error) {
4770
- this.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);
5141
+ const message = error instanceof Error ? error.message : String(error);
5142
+ this.showError(error instanceof CredentialSynchronizationError
5143
+ ? `Credentials removed for ${providerOption.name}, but local model state could not be synchronized: ${message}`
5144
+ : `Logout failed: ${message}`);
4771
5145
  }
4772
5146
  }, () => {
4773
5147
  done();
@@ -4777,8 +5151,8 @@ export class InteractiveMode {
4777
5151
  });
4778
5152
  }
4779
5153
  async completeProviderAuthentication(providerId, providerName, authType, previousModel) {
4780
- const session = this.session;
4781
5154
  const actionLabel = authType === "oauth" ? `Logged in to ${providerName}` : `Saved API key for ${providerName}`;
5155
+ const session = this.session;
4782
5156
  // Dynamic catalogs may be empty until the first authenticated network refresh.
4783
5157
  const deferSelection = isUnknownModel(previousModel) &&
4784
5158
  hasDefaultModelProvider(providerId) &&
@@ -4789,10 +5163,9 @@ export class InteractiveMode {
4789
5163
  let selectedModel;
4790
5164
  let selectionError;
4791
5165
  if (isUnknownModel(previousModel)) {
4792
- const availableModels = await this.session.modelRuntime.getAvailable();
5166
+ const availableModels = this.session.modelRuntime.getAvailableSnapshot();
4793
5167
  const providerModels = availableModels.filter((model) => model.provider === providerId);
4794
- // Matches LLAMA_PROVIDER_ID from core/llama/provider.ts; kept inline to avoid
4795
- // coupling interactive mode to the built-in extension.
5168
+ // Matches LLAMA_PROVIDER_ID from extensions/llama/provider.ts; kept inline to avoid coupling interactive mode to the built-in extension.
4796
5169
  if (providerId === "llama.cpp") {
4797
5170
  selectionError = llamaCppPostLoginGuidance(actionLabel, providerModels.length);
4798
5171
  }
@@ -4829,7 +5202,6 @@ export class InteractiveMode {
4829
5202
  if (selectedModel) {
4830
5203
  this.showStatus(`${actionLabel}. Selected ${selectedModel.id}. Credentials saved to ${getAuthPath()}`);
4831
5204
  void this.maybeWarnAboutAnthropicSubscriptionAuth(selectedModel);
4832
- this.checkDaxnutsEasterEgg(selectedModel);
4833
5205
  }
4834
5206
  else {
4835
5207
  this.showStatus(`${actionLabel}. Credentials saved to ${getAuthPath()}`);
@@ -4871,21 +5243,24 @@ export class InteractiveMode {
4871
5243
  })
4872
5244
  .finally(() => clearTimeout(timeout));
4873
5245
  }
4874
- showAmbientAuthDialog(providerOption) {
5246
+ showAmbientAuthDialog(providerOption, onBack) {
4875
5247
  const restoreEditor = () => {
4876
5248
  this.editorContainer.clear();
4877
5249
  this.editorContainer.addChild(this.editor);
4878
5250
  this.ui.setFocus(this.editor);
4879
5251
  this.ui.requestRender();
4880
5252
  };
4881
- const dialog = new LoginDialogComponent(this.ui, providerOption.id, () => restoreEditor(), providerOption.name, `${providerOption.name} setup`);
5253
+ const dialog = new LoginDialogComponent(this.ui, providerOption.id, () => {
5254
+ restoreEditor();
5255
+ onBack?.();
5256
+ }, providerOption.name, `${providerOption.name} setup`);
4882
5257
  dialog.showInfo(`${providerOption.method?.name ?? "Authentication"} is configured outside ${APP_NAME}.`, [], true);
4883
5258
  this.editorContainer.clear();
4884
5259
  this.editorContainer.addChild(dialog);
4885
5260
  this.ui.setFocus(dialog);
4886
5261
  this.ui.requestRender();
4887
5262
  }
4888
- async showApiKeyLoginDialog(providerId, providerName) {
5263
+ async showApiKeyLoginDialog(providerId, providerName, onBack) {
4889
5264
  const previousModel = this.session.model;
4890
5265
  const dialog = new LoginDialogComponent(this.ui, providerId, (_success, _message) => {
4891
5266
  // Completion handled below
@@ -4908,19 +5283,25 @@ export class InteractiveMode {
4908
5283
  this.ui.requestRender();
4909
5284
  };
4910
5285
  try {
4911
- await this.loginProvider(dialog, providerId, "api_key");
5286
+ await this.loginProvider(dialog, providerId, providerName, "api_key");
4912
5287
  restoreEditor();
4913
5288
  await this.completeProviderAuthentication(providerId, providerName, "api_key", previousModel);
4914
5289
  }
4915
5290
  catch (error) {
4916
5291
  restoreEditor();
4917
5292
  const errorMsg = error instanceof Error ? error.message : String(error);
4918
- if (errorMsg !== "Login cancelled") {
5293
+ if (error instanceof CredentialSynchronizationError) {
5294
+ this.showError(`Saved API key for ${providerName}, but local model state could not be synchronized: ${errorMsg}`);
5295
+ }
5296
+ else if (errorMsg === "Login cancelled") {
5297
+ onBack?.();
5298
+ }
5299
+ else {
4919
5300
  this.showError(`Failed to save API key for ${providerName}: ${errorMsg}`);
4920
5301
  }
4921
5302
  }
4922
5303
  }
4923
- showAuthSelect(dialog, prompt) {
5304
+ showAuthSelect(dialog, prompt, providerId) {
4924
5305
  return new Promise((resolve, reject) => {
4925
5306
  const restoreDialog = () => {
4926
5307
  this.editorContainer.clear();
@@ -4939,17 +5320,17 @@ export class InteractiveMode {
4939
5320
  }, () => {
4940
5321
  restoreDialog();
4941
5322
  reject(new Error("Login cancelled"));
4942
- });
5323
+ }, { description: providerId === RADIUS_PROVIDER_ID ? RADIUS_LOGIN_INTRO : undefined });
4943
5324
  this.editorContainer.clear();
4944
5325
  this.editorContainer.addChild(selector);
4945
5326
  this.ui.setFocus(selector);
4946
5327
  this.ui.requestRender();
4947
5328
  });
4948
5329
  }
4949
- async showAuthPrompt(dialog, prompt) {
5330
+ async showAuthPrompt(dialog, prompt, providerId) {
4950
5331
  let response;
4951
5332
  if (prompt.type === "select") {
4952
- response = this.showAuthSelect(dialog, prompt);
5333
+ response = this.showAuthSelect(dialog, prompt, providerId);
4953
5334
  }
4954
5335
  else if (prompt.type === "manual_code") {
4955
5336
  response = dialog.showManualInput(prompt.message);
@@ -4990,14 +5371,20 @@ export class InteractiveMode {
4990
5371
  dialog.showProgress(event.message);
4991
5372
  }
4992
5373
  }
4993
- async loginProvider(dialog, providerId, method) {
4994
- await this.session.modelRuntime.login(providerId, method, {
4995
- signal: dialog.signal,
4996
- prompt: (prompt) => this.showAuthPrompt(dialog, prompt),
4997
- notify: (event) => this.notifyAuthDialog(dialog, event),
4998
- });
5374
+ async loginProvider(dialog, providerId, providerName, method) {
5375
+ this.programStatus.setBlocked("login", { kind: "auth", message: `Log in to ${providerName}` });
5376
+ try {
5377
+ await this.session.modelRuntime.login(providerId, method, {
5378
+ signal: dialog.signal,
5379
+ prompt: (prompt) => this.showAuthPrompt(dialog, prompt, providerId),
5380
+ notify: (event) => this.notifyAuthDialog(dialog, event),
5381
+ }, { getDeviceId: () => this.settingsManager.getOrCreateDeviceId() });
5382
+ }
5383
+ finally {
5384
+ this.programStatus.setBlocked("login", undefined);
5385
+ }
4999
5386
  }
5000
- async showLoginDialog(providerId, providerName) {
5387
+ async showLoginDialog(providerId, providerName, onBack) {
5001
5388
  const previousModel = this.session.model;
5002
5389
  const dialog = new LoginDialogComponent(this.ui, providerId, (_success, _message) => { }, providerName);
5003
5390
  this.editorContainer.clear();
@@ -5011,18 +5398,70 @@ export class InteractiveMode {
5011
5398
  this.ui.requestRender();
5012
5399
  };
5013
5400
  try {
5014
- await this.loginProvider(dialog, providerId, "oauth");
5401
+ await this.loginProvider(dialog, providerId, providerName, "oauth");
5015
5402
  restoreEditor();
5016
5403
  await this.completeProviderAuthentication(providerId, providerName, "oauth", previousModel);
5404
+ if (providerId === RADIUS_PROVIDER_ID)
5405
+ this.offerRadiusMcpServer(providerId, providerName);
5017
5406
  }
5018
5407
  catch (error) {
5019
5408
  restoreEditor();
5020
5409
  const errorMsg = error instanceof Error ? error.message : String(error);
5021
- if (errorMsg !== "Login cancelled") {
5410
+ if (error instanceof CredentialSynchronizationError) {
5411
+ this.showError(`Logged in to ${providerName}, but local model state could not be synchronized: ${errorMsg}`);
5412
+ }
5413
+ else if (errorMsg === "Login cancelled") {
5414
+ onBack?.();
5415
+ }
5416
+ else {
5022
5417
  this.showError(`Failed to login to ${providerName}: ${errorMsg}`);
5023
5418
  }
5024
5419
  }
5025
5420
  }
5421
+ /**
5422
+ * Offer to point the Radius MCP server in the global mcp.json at the Radius login, adding the server
5423
+ * when missing. Nothing is asked when a global server already uses this login.
5424
+ */
5425
+ offerRadiusMcpServer(providerId, providerName) {
5426
+ const mcpPath = path.join(getAgentDir(), "mcp.json");
5427
+ const normalizeUrl = (url) => url.replace(/\/+$/u, "");
5428
+ const { servers } = loadMcpConfig({
5429
+ agentDir: getAgentDir(),
5430
+ cwd: this.sessionManager.getCwd(),
5431
+ projectTrusted: false,
5432
+ });
5433
+ const existing = servers.find((server) => "url" in server.config && normalizeUrl(server.config.url) === normalizeUrl(RADIUS_MCP_URL));
5434
+ if (existing && "url" in existing.config && existing.config.auth?.provider === providerId)
5435
+ return;
5436
+ let name = existing?.name ?? "radius";
5437
+ if (!existing && servers.some((server) => server.name === name))
5438
+ name = "radius-mcp";
5439
+ const config = existing && "url" in existing.config
5440
+ ? { ...existing.config, auth: { provider: providerId } }
5441
+ : { url: RADIUS_MCP_URL, auth: { provider: providerId } };
5442
+ // `auth` replaces the MCP OAuth sign-in.
5443
+ delete config.oauth;
5444
+ this.showSelector((done) => {
5445
+ const selector = new ExtensionSelectorComponent(`Configure ${providerName} MCP in ${mcpPath}?`, ["Yes", "No"], (option) => {
5446
+ done();
5447
+ if (option !== "Yes")
5448
+ return;
5449
+ try {
5450
+ addMcpServerConfig(mcpPath, name, config);
5451
+ }
5452
+ catch (error) {
5453
+ this.showError(`Could not update ${mcpPath}: ${error instanceof Error ? error.message : String(error)}`);
5454
+ return;
5455
+ }
5456
+ // The MCP extension reads mcp.json when the session starts.
5457
+ void this.handleReloadCommand();
5458
+ }, () => {
5459
+ done();
5460
+ this.ui.requestRender();
5461
+ });
5462
+ return { component: selector, focus: selector };
5463
+ });
5464
+ }
5026
5465
  // =========================================================================
5027
5466
  // Command handlers
5028
5467
  // =========================================================================
@@ -5082,7 +5521,7 @@ export class InteractiveMode {
5082
5521
  const borderColor = (s) => theme.fg("border", s);
5083
5522
  reloadBox.addChild(new DynamicBorder(borderColor));
5084
5523
  reloadBox.addChild(new Spacer(1));
5085
- reloadBox.addChild(new Text(theme.fg("muted", "Reloading keybindings, extensions, skills, prompts, themes, and context files..."), 1, 0));
5524
+ reloadBox.addChild(new ThemedText(() => theme.fg("muted", "Reloading keybindings, extensions, skills, prompts, themes, and context files..."), 1, 0));
5086
5525
  reloadBox.addChild(new Spacer(1));
5087
5526
  reloadBox.addChild(new DynamicBorder(borderColor));
5088
5527
  const previousEditor = this.editor;
@@ -5119,7 +5558,7 @@ export class InteractiveMode {
5119
5558
  }
5120
5559
  setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
5121
5560
  this.applyRuntimeSettings();
5122
- await this.themeController.applyFromSettings();
5561
+ this.themeController.applyFromSettings();
5123
5562
  this.setupAutocompleteProvider();
5124
5563
  const runner = this.session.extensionRunner;
5125
5564
  this.setupExtensionShortcuts(runner);
@@ -5231,91 +5670,14 @@ export class InteractiveMode {
5231
5670
  }
5232
5671
  }
5233
5672
  async handleShareCommand() {
5234
- // Check if gh is available and logged in
5235
- try {
5236
- const authResult = spawnSync("gh", ["auth", "status"], { encoding: "utf-8" });
5237
- if (authResult.status !== 0) {
5238
- this.showError("GitHub CLI is not logged in. Run 'gh auth login' first.");
5239
- return;
5240
- }
5241
- }
5242
- catch {
5243
- this.showError("GitHub CLI (gh) is not installed. Install it from https://cli.github.com/");
5244
- return;
5245
- }
5246
- // Export to a temp file
5247
- const tmpFile = path.join(os.tmpdir(), "session.html");
5248
- try {
5249
- await this.session.exportToHtml(tmpFile, { themeName: theme.name });
5250
- }
5251
- catch (error) {
5252
- this.showError(`Failed to export session: ${error instanceof Error ? error.message : "Unknown error"}`);
5253
- return;
5254
- }
5255
- // Show cancellable loader, replacing the editor
5256
- const loader = new BorderedLoader(this.ui, theme, "Creating gist...");
5257
- this.editorContainer.clear();
5258
- this.editorContainer.addChild(loader);
5259
- this.ui.setFocus(loader);
5260
- this.ui.requestRender();
5261
- const restoreEditor = () => {
5262
- loader.dispose();
5263
- this.editorContainer.clear();
5264
- this.editorContainer.addChild(this.editor);
5265
- this.ui.setFocus(this.editor);
5266
- try {
5267
- fs.unlinkSync(tmpFile);
5268
- }
5269
- catch {
5270
- // Ignore cleanup errors
5271
- }
5272
- };
5273
- // Create a secret gist asynchronously
5274
- let proc = null;
5275
- loader.onAbort = () => {
5276
- proc?.kill();
5277
- restoreEditor();
5278
- this.showStatus("Share cancelled");
5279
- };
5280
- try {
5281
- const result = await new Promise((resolve) => {
5282
- proc = spawn("gh", ["gist", "create", "--public=false", tmpFile]);
5283
- let stdout = "";
5284
- let stderr = "";
5285
- proc.stdout?.on("data", (data) => {
5286
- stdout += data.toString();
5287
- });
5288
- proc.stderr?.on("data", (data) => {
5289
- stderr += data.toString();
5290
- });
5291
- proc.on("close", (code) => resolve({ stdout, stderr, code }));
5292
- });
5293
- if (loader.signal.aborted)
5294
- return;
5295
- restoreEditor();
5296
- if (result.code !== 0) {
5297
- const errorMsg = result.stderr?.trim() || "Unknown error";
5298
- this.showError(`Failed to create gist: ${errorMsg}`);
5299
- return;
5300
- }
5301
- // Extract gist ID from the URL returned by gh
5302
- // gh returns something like: https://gist.github.com/username/GIST_ID
5303
- const gistUrl = result.stdout?.trim();
5304
- const gistId = gistUrl?.split("/").pop();
5305
- if (!gistId) {
5306
- this.showError("Failed to parse gist ID from gh output");
5307
- return;
5308
- }
5309
- // Create the preview URL
5310
- const previewUrl = getShareViewerUrl(gistId);
5311
- this.showStatus(`Share URL: ${previewUrl}\nGist: ${gistUrl}`);
5312
- }
5313
- catch (error) {
5314
- if (!loader.signal.aborted) {
5315
- restoreEditor();
5316
- this.showError(`Failed to create gist: ${error instanceof Error ? error.message : "Unknown error"}`);
5317
- }
5318
- }
5673
+ await shareSession({
5674
+ session: this.session,
5675
+ ui: this.ui,
5676
+ editorContainer: this.editorContainer,
5677
+ editor: this.editor,
5678
+ showStatus: (message) => this.showStatus(message),
5679
+ showError: (message) => this.showError(message),
5680
+ });
5319
5681
  }
5320
5682
  async handleBugCommand(hint) {
5321
5683
  await reportBug({
@@ -5360,7 +5722,7 @@ export class InteractiveMode {
5360
5722
  const currentName = this.sessionManager.getSessionName();
5361
5723
  if (currentName) {
5362
5724
  this.chatContainer.addChild(new Spacer(1));
5363
- this.chatContainer.addChild(new Text(theme.fg("dim", `Session name: ${currentName}`), 1, 0));
5725
+ this.chatContainer.addChild(new ThemedText(() => theme.fg("dim", `Session name: ${currentName}`), 1, 0));
5364
5726
  }
5365
5727
  else {
5366
5728
  this.showWarning("Usage: /name <name>");
@@ -5374,7 +5736,8 @@ export class InteractiveMode {
5374
5736
  this.showWarning(`Session name was normalized from ${JSON.stringify(name)} to ${JSON.stringify(sessionName)}`);
5375
5737
  }
5376
5738
  this.chatContainer.addChild(new Spacer(1));
5377
- this.chatContainer.addChild(new Text(theme.fg("dim", `Session name set: ${sessionName ?? name}`), 1, 0));
5739
+ const displayName = sessionName ?? name;
5740
+ this.chatContainer.addChild(new ThemedText(() => theme.fg("dim", `Session name set: ${displayName}`), 1, 0));
5378
5741
  this.ui.requestRender();
5379
5742
  }
5380
5743
  handleSessionCommand() {
@@ -5386,61 +5749,69 @@ export class InteractiveMode {
5386
5749
  // resolves to a concrete responseModel). Usage without model attribution is
5387
5750
  // grouped separately so the breakdown reconciles with the session total.
5388
5751
  const usageBreakdown = getUsageCostBreakdown(entries);
5389
- let info = `${theme.bold("Session Info")}\n\n`;
5390
- if (sessionName) {
5391
- info += `${theme.fg("dim", "Name:")} ${sessionName}\n`;
5392
- }
5393
- info += `${theme.fg("dim", "File:")} ${stats.sessionFile ?? "In-memory"}\n`;
5394
- info += `${theme.fg("dim", "ID:")} ${stats.sessionId}\n\n`;
5395
- info += `${theme.bold("Messages")}\n`;
5396
- info += `${theme.fg("dim", "Total:")} ${stats.totalMessages}\n`;
5397
- info += `${theme.fg("dim", "User:")} ${stats.userMessages}\n`;
5398
- info += `${theme.fg("dim", "Assistant:")} ${stats.assistantMessages}\n`;
5399
- info += `${theme.fg("dim", "Tools:")} ${stats.toolCalls} calls, ${stats.toolResults} results\n\n`;
5400
- info += `${theme.bold("Tokens")}\n`;
5401
- // "Input" is the full prompt volume. With cache activity, split it into
5402
- // cached (served from cache) vs uncached (everything else) - the only
5403
- // provider-independent split. Cache writes, where reported, are a detail
5404
- // of the uncached portion.
5405
- const { input, cacheRead, cacheWrite } = stats.tokens;
5406
- const promptTokens = input + cacheRead + cacheWrite;
5407
- info += `${theme.fg("dim", "Input:")} ${promptTokens.toLocaleString()}\n`;
5408
- if (promptTokens > 0 && (cacheRead > 0 || cacheWrite > 0)) {
5409
- const hitRate = theme.fg("dim", `(${((cacheRead / promptTokens) * 100).toFixed(1)}%)`);
5410
- info += ` ${theme.fg("dim", "Cached:")} ${cacheRead.toLocaleString()} ${hitRate}\n`;
5411
- const written = cacheWrite > 0 ? ` ${theme.fg("dim", `(${cacheWrite.toLocaleString()} written to cache)`)}` : "";
5412
- info += ` ${theme.fg("dim", "Uncached:")} ${(input + cacheWrite).toLocaleString()}${written}\n`;
5413
- }
5414
- info += `${theme.fg("dim", "Output:")} ${stats.tokens.output.toLocaleString()}\n`;
5415
- info += `${theme.fg("dim", "Total:")} ${stats.tokens.total.toLocaleString()}\n`;
5752
+ // Snapshot the stats; the text is built on demand so it follows theme changes.
5416
5753
  const cacheWarmingStatus = this.session.cacheWarmingStatus;
5417
- info += `\n${theme.bold("Cache Warming")}\n`;
5418
- info += `${theme.fg("dim", "Mode:")} ${this.settingsManager.getCacheWarmingMode()}\n`;
5419
- info += `${theme.fg("dim", "Status:")} ${cacheWarmingStatus ? formatCacheWarmingStatus(cacheWarmingStatus) : "Inactive (cache warming unavailable)"}\n`;
5420
- const decision = cacheWarmingStatus?.decision;
5421
- if (decision?.economicsAvailable) {
5422
- info += `${theme.fg("dim", "Cache miss penalty:")} $${decision.missCost.toFixed(3)}\n`;
5423
- info += `${theme.fg("dim", "Refresh cost:")} $${decision.warmCost.toFixed(3)}\n`;
5424
- }
5425
- if (stats.cost > 0 || cacheWaste.missedTokens > 0) {
5426
- info += `\n${theme.bold("Cost")}\n`;
5427
- info += `${theme.fg("dim", "Total:")} ${formatCost(stats.cost)}`;
5428
- if (usageBreakdown.length > 1) {
5429
- for (const entry of usageBreakdown) {
5430
- info += `\n ${theme.fg("dim", `${entry.key}:`)} ${formatCost(entry.cost)} ${theme.fg("dim", `(${formatTokens(entry.tokens)} tokens)`)}`;
5754
+ const cacheWarmingMode = this.settingsManager.getCacheWarmingMode();
5755
+ const model = this.session.model;
5756
+ const selectedModelKey = `${model?.provider}/${model?.id}`;
5757
+ const renderInfo = () => {
5758
+ let info = `${theme.bold("Session Info")}\n\n`;
5759
+ if (sessionName) {
5760
+ info += `${theme.fg("dim", "Name:")} ${sessionName}\n`;
5761
+ }
5762
+ info += `${theme.fg("dim", "File:")} ${stats.sessionFile ?? "In-memory"}\n`;
5763
+ info += `${theme.fg("dim", "ID:")} ${stats.sessionId}\n\n`;
5764
+ info += `${theme.bold("Messages")}\n`;
5765
+ info += `${theme.fg("dim", "Total:")} ${stats.totalMessages}\n`;
5766
+ info += `${theme.fg("dim", "User:")} ${stats.userMessages}\n`;
5767
+ info += `${theme.fg("dim", "Assistant:")} ${stats.assistantMessages}\n`;
5768
+ info += `${theme.fg("dim", "Tools:")} ${stats.toolCalls} calls, ${stats.toolResults} results\n\n`;
5769
+ info += `${theme.bold("Tokens")}\n`;
5770
+ // "Input" is the full prompt volume. With cache activity, split it into
5771
+ // cached (served from cache) vs uncached (everything else) - the only
5772
+ // provider-independent split. Cache writes, where reported, are a detail
5773
+ // of the uncached portion.
5774
+ const { input, cacheRead, cacheWrite } = stats.tokens;
5775
+ const promptTokens = input + cacheRead + cacheWrite;
5776
+ info += `${theme.fg("dim", "Input:")} ${promptTokens.toLocaleString()}\n`;
5777
+ if (promptTokens > 0 && (cacheRead > 0 || cacheWrite > 0)) {
5778
+ const hitRate = theme.fg("dim", `(${((cacheRead / promptTokens) * 100).toFixed(1)}%)`);
5779
+ info += ` ${theme.fg("dim", "Cached:")} ${cacheRead.toLocaleString()} ${hitRate}\n`;
5780
+ const written = cacheWrite > 0 ? ` ${theme.fg("dim", `(${cacheWrite.toLocaleString()} written to cache)`)}` : "";
5781
+ info += ` ${theme.fg("dim", "Uncached:")} ${(input + cacheWrite).toLocaleString()}${written}\n`;
5782
+ }
5783
+ info += `${theme.fg("dim", "Output:")} ${stats.tokens.output.toLocaleString()}\n`;
5784
+ info += `${theme.fg("dim", "Total:")} ${stats.tokens.total.toLocaleString()}\n`;
5785
+ info += `\n${theme.bold("Cache Warming")}\n`;
5786
+ info += `${theme.fg("dim", "Mode:")} ${cacheWarmingMode}\n`;
5787
+ info += `${theme.fg("dim", "Status:")} ${cacheWarmingStatus ? formatCacheWarmingStatus(cacheWarmingStatus) : "Inactive (cache warming unavailable)"}\n`;
5788
+ const decision = cacheWarmingStatus?.decision;
5789
+ if (decision?.economicsAvailable) {
5790
+ info += `${theme.fg("dim", "Cache miss penalty:")} ${formatCost(decision.missCost)}\n`;
5791
+ info += `${theme.fg("dim", "Refresh cost:")} ${formatCost(decision.warmCost)}\n`;
5792
+ }
5793
+ if (stats.cost > 0 || cacheWaste.missedTokens > 0) {
5794
+ info += `\n${theme.bold("Cost")}\n`;
5795
+ info += `${theme.fg("dim", "Total:")} ${formatCost(stats.cost)}`;
5796
+ // A single entry repeats the total, unless it names a model other than the selected one.
5797
+ if (usageBreakdown.length > 1 || usageBreakdown[0]?.key !== selectedModelKey) {
5798
+ for (const entry of usageBreakdown) {
5799
+ info += `\n ${theme.fg("dim", `${entry.key}:`)} ${formatCost(entry.cost)} ${theme.fg("dim", `(${formatTokens(entry.tokens)} tokens)`)}`;
5800
+ }
5801
+ }
5802
+ if (cacheWaste.missedTokens > 0) {
5803
+ const missLabel = cacheWaste.missCount === 1 ? "1 miss" : `${cacheWaste.missCount} misses`;
5804
+ const detail = `${cacheWaste.missedTokens.toLocaleString()} tokens, ${missLabel}`;
5805
+ info +=
5806
+ cacheWaste.missedCost >= 0.0001
5807
+ ? `\n${theme.fg("dim", "Cache Re-billed:")} ${formatCost(cacheWaste.missedCost)} ${theme.fg("dim", `(${detail})`)}`
5808
+ : `\n${theme.fg("dim", "Cache Re-billed:")} ${detail}`;
5431
5809
  }
5432
5810
  }
5433
- if (cacheWaste.missedTokens > 0) {
5434
- const missLabel = cacheWaste.missCount === 1 ? "1 miss" : `${cacheWaste.missCount} misses`;
5435
- const detail = `${cacheWaste.missedTokens.toLocaleString()} tokens, ${missLabel}`;
5436
- info +=
5437
- cacheWaste.missedCost >= 0.0001
5438
- ? `\n${theme.fg("dim", "Cache Re-billed:")} ${formatCost(cacheWaste.missedCost)} ${theme.fg("dim", `(${detail})`)}`
5439
- : `\n${theme.fg("dim", "Cache Re-billed:")} ${detail}`;
5440
- }
5441
- }
5811
+ return info;
5812
+ };
5442
5813
  this.chatContainer.addChild(new Spacer(1));
5443
- this.chatContainer.addChild(new Text(info, 1, 0));
5814
+ this.chatContainer.addChild(new ThemedText(renderInfo, 1, 0));
5444
5815
  this.ui.requestRender();
5445
5816
  }
5446
5817
  handleChangelogCommand() {
@@ -5454,7 +5825,7 @@ export class InteractiveMode {
5454
5825
  : "No changelog entries found.";
5455
5826
  this.chatContainer.addChild(new Spacer(1));
5456
5827
  this.chatContainer.addChild(new DynamicBorder());
5457
- this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
5828
+ this.chatContainer.addChild(new ThemedText(() => theme.bold(theme.fg("accent", "What's New")), 1, 0));
5458
5829
  this.chatContainer.addChild(new Spacer(1));
5459
5830
  this.chatContainer.addChild(new Markdown(changelogMarkdown, 1, 1, this.getMarkdownThemeWithSettings()));
5460
5831
  this.chatContainer.addChild(new DynamicBorder());
@@ -5555,7 +5926,7 @@ export class InteractiveMode {
5555
5926
  | \`${copyMessage}\` | Copy selection or last assistant message |
5556
5927
  | \`${followUp}\` | Queue follow-up message |
5557
5928
  | \`${dequeue}\` | Restore queued messages |
5558
- | \`${pasteImage}\` | Paste image or text from clipboard |
5929
+ | \`${pasteImage}\` | Paste files on macOS, images, or text from clipboard |
5559
5930
  | \`/\` | Slash commands |
5560
5931
  | \`!\` | Run bash command |
5561
5932
  | \`!!\` | Run bash command (excluded from context) |
@@ -5577,7 +5948,7 @@ export class InteractiveMode {
5577
5948
  }
5578
5949
  this.chatContainer.addChild(new Spacer(1));
5579
5950
  this.chatContainer.addChild(new DynamicBorder());
5580
- this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "Keyboard Shortcuts")), 1, 0));
5951
+ this.chatContainer.addChild(new ThemedText(() => theme.bold(theme.fg("accent", "Keyboard Shortcuts")), 1, 0));
5581
5952
  this.chatContainer.addChild(new Spacer(1));
5582
5953
  this.chatContainer.addChild(new Markdown(hotkeys.trim(), 1, 1, this.getMarkdownThemeWithSettings()));
5583
5954
  this.chatContainer.addChild(new DynamicBorder());
@@ -5591,7 +5962,7 @@ export class InteractiveMode {
5591
5962
  return;
5592
5963
  }
5593
5964
  this.chatContainer.addChild(new Spacer(1));
5594
- this.chatContainer.addChild(new Text(`${theme.fg("accent", "✓ New session started")}`, 1, 1));
5965
+ this.chatContainer.addChild(new ThemedText(() => theme.fg("accent", "✓ New session started"), 1, 1));
5595
5966
  this.ui.requestRender();
5596
5967
  }
5597
5968
  catch (error) {
@@ -5622,7 +5993,7 @@ export class InteractiveMode {
5622
5993
  fs.mkdirSync(path.dirname(debugLogPath), { recursive: true });
5623
5994
  fs.writeFileSync(debugLogPath, debugData);
5624
5995
  this.chatContainer.addChild(new Spacer(1));
5625
- this.chatContainer.addChild(new Text(`${theme.fg("accent", "✓ Debug log written")}\n${theme.fg("muted", debugLogPath)}`, 1, 1));
5996
+ this.chatContainer.addChild(new ThemedText(() => `${theme.fg("accent", "✓ Debug log written")}\n${theme.fg("muted", debugLogPath)}`, 1, 1));
5626
5997
  this.ui.requestRender();
5627
5998
  }
5628
5999
  handleArminSaysHi() {
@@ -5635,16 +6006,6 @@ export class InteractiveMode {
5635
6006
  this.chatContainer.addChild(new EarendilAnnouncementComponent());
5636
6007
  this.ui.requestRender();
5637
6008
  }
5638
- handleDaxnuts() {
5639
- this.chatContainer.addChild(new Spacer(1));
5640
- this.chatContainer.addChild(new DaxnutsComponent(this.ui));
5641
- this.ui.requestRender();
5642
- }
5643
- checkDaxnutsEasterEgg(model) {
5644
- if (model.provider === "opencode" && model.id.toLowerCase().includes("kimi-k2.5")) {
5645
- this.handleDaxnuts();
5646
- }
5647
- }
5648
6009
  async handleBashCommand(command, excludeFromContext = false) {
5649
6010
  const extensionRunner = this.session.extensionRunner;
5650
6011
  // Emit user_bash event to let extensions intercept
@@ -5665,7 +6026,7 @@ export class InteractiveMode {
5665
6026
  if (eventResult?.result) {
5666
6027
  const result = eventResult.result;
5667
6028
  // Create UI component for display
5668
- this.bashComponent = new BashExecutionComponent(command, this.ui, excludeFromContext);
6029
+ this.bashComponent = new BashExecutionComponent(command, this.ui, excludeFromContext, this.outputPad);
5669
6030
  if (this.session.isStreaming) {
5670
6031
  this.pendingMessagesContainer.addChild(this.bashComponent);
5671
6032
  this.pendingBashComponents.push(this.bashComponent);
@@ -5686,7 +6047,7 @@ export class InteractiveMode {
5686
6047
  }
5687
6048
  // Normal execution path (possibly with custom operations)
5688
6049
  const isDeferred = this.session.isStreaming;
5689
- this.bashComponent = new BashExecutionComponent(command, this.ui, excludeFromContext);
6050
+ this.bashComponent = new BashExecutionComponent(command, this.ui, excludeFromContext, this.outputPad);
5690
6051
  if (isDeferred) {
5691
6052
  // Show in pending area when agent is streaming
5692
6053
  this.pendingMessagesContainer.addChild(this.bashComponent);
@@ -5727,6 +6088,7 @@ export class InteractiveMode {
5727
6088
  }
5728
6089
  }
5729
6090
  stop(fullscreenExitOutput = this.settingsManager.getFullscreenExitOutput()) {
6091
+ this.disposeActiveSelector();
5730
6092
  if (this.settingsManager.getShowTerminalProgress()) {
5731
6093
  this.ui.terminal.setProgress(false);
5732
6094
  }