@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
@@ -12,34 +12,41 @@
12
12
  *
13
13
  * Modes use this class and add their own I/O layer on top.
14
14
  */
15
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
15
+ import { readFileSync } from "node:fs";
16
16
  import { basename, dirname } from "node:path";
17
+ import { runToolCall, } from "@earendil-works/pi-agent-core";
17
18
  import { contentText, getCurrentSystemMessage, retryDelayMs } from "@earendil-works/pi-ai";
18
19
  import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, isRecoverableLength, isRetryableAssistantError, modelsAreEqual, resetApiProviders, streamSimple, } from "@earendil-works/pi-ai/compat";
19
20
  import { getThemeByName, theme } from "../modes/interactive/theme/theme.js";
20
21
  import { stripFrontmatter } from "../utils/frontmatter.js";
21
- import { resolvePath } from "../utils/paths.js";
22
+ import { processImage } from "../utils/image-process.js";
22
23
  import { sleep } from "../utils/sleep.js";
23
24
  import { normalizeAssistantThinkingTags } from "../utils/thinking-tags.js";
25
+ import { normalizeToolResultImages } from "../utils/tool-result-images.js";
24
26
  import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth-guidance.js";
25
27
  import { executeBashWithOperations } from "./bash-executor.js";
26
28
  import { generateBugReportSummary } from "./bug-report.js";
27
- import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, estimateTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
29
+ import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, estimateProjectedContextTokens, estimateTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
28
30
  import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "./defaults.js";
29
31
  import { exportSessionToHtml } from "./export-html/index.js";
30
32
  import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
31
- import { AUTO_HANDOFF_GOAL } from "./handoff.js";
32
33
  import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
33
34
  import { emitSessionShutdownEvent } from "./extensions/runner.js";
35
+ import { createToolNameMatcher, isMcpToolName } from "./mcp-servers.js";
36
+ import { convertToLlm } from "./messages.js";
34
37
  import { ModelRegistry } from "./model-registry.js";
38
+ import { NestedToolCallRunner } from "./nested-tool-calls.js";
35
39
  import { expandPromptTemplate } from "./prompt-templates.js";
36
- import { CURRENT_SESSION_VERSION, getLatestCompactionEntry } from "./session-manager.js";
37
- import { createSyntheticSourceInfo } from "./source-info.js";
40
+ import { exportSessionToJsonl } from "./session-export.js";
41
+ import { getLatestCompactionEntry, SessionManager, } from "./session-manager.js";
42
+ import { applyToolModifiers, DEFAULT_TOOL_NAMES, } from "./settings-manager.js";
43
+ import { BUILTIN_PATH_PREFIX, createSyntheticSourceInfo, isSyntheticPath } from "./source-info.js";
38
44
  import { buildSystemPrompt, buildSystemPromptSections, diffSystemPromptSections, normalizeBuildSystemPromptOptions, } from "./system-prompt.js";
39
45
  import { createLocalBashOperations } from "./tools/bash.js";
40
46
  import { createAllToolDefinitions } from "./tools/index.js";
41
47
  import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
42
- import { addUsageToTotals, createUsageTotals } from "./usage-totals.js";
48
+ import { addUsageToTotals, combineUsage, createUsageTotals } from "./usage-totals.js";
49
+ import { findLatestResponse, getBranchSelection, getVirtualModelState, isVirtualModel, VIRTUAL_MODEL_STATE_ENTRY, } from "./virtual-models.js";
43
50
  import { captionImageWithModel } from "./vision-caption.js";
44
51
  /** Parse consecutive skill blocks at the start of a user message. */
45
52
  export function parseSkillBlocks(text) {
@@ -120,6 +127,11 @@ export class AgentSession {
120
127
  // Retry state
121
128
  _retryAbortController = undefined;
122
129
  _retryAttempt = 0;
130
+ /**
131
+ * Failed response that the next request repeats, set by auto-retry and overflow recovery. The
132
+ * retry is routed with it as `failed`, since the context no longer contains it.
133
+ */
134
+ _failedResponse;
123
135
  // Length-stop continuation state (consecutive output-token-limit continuations)
124
136
  _lengthContinuations = 0;
125
137
  // Bash execution state
@@ -128,14 +140,37 @@ export class AgentSession {
128
140
  // Extension system
129
141
  _extensionRunner;
130
142
  _turnIndex = 0;
143
+ _entryIdsByMessage = new WeakMap();
144
+ _boundaryDispatchedMessages = new WeakSet();
145
+ _lastAssistantMessage;
146
+ _lastAssistantToolResults = [];
147
+ _lastActivityOutcome = "completed";
148
+ _isBeforeSettle = false;
149
+ _abortDuringBeforeSettle = false;
150
+ _isEmittingAgentSettled = false;
151
+ _deferredSettledActions = [];
131
152
  _resourceLoader;
132
153
  _customTools;
133
154
  _baseToolDefinitions = new Map();
134
155
  _cwd;
135
156
  _extensionRunnerRef;
136
157
  _initialActiveToolNames;
137
- _allowedToolNames;
138
- _excludedToolNames;
158
+ /**
159
+ * Tools of the restored or reloaded loadout that are not registered yet, such as tools of MCP
160
+ * servers that are still connecting. They are activated when they are registered, and dropped when
161
+ * `setActiveToolsByName()` deactivates a tool or the next agent run starts.
162
+ */
163
+ _pendingToolNames = new Set();
164
+ _usesDefaultTools;
165
+ _defaultToolModifiers;
166
+ /** Matches the `--tools` entries: tool names or patterns. */
167
+ _allowedTools;
168
+ /**
169
+ * Whether the allowlist filters MCP tools: it is empty (`--no-tools`) or names an MCP tool
170
+ * (`mcp__*`). Otherwise it keeps MCP tools registered for codemode and tool_search.
171
+ */
172
+ _allowlistFiltersMcp = false;
173
+ _excludedTools;
139
174
  _baseToolsOverride;
140
175
  _sessionStartEvent;
141
176
  _extensionUIContext;
@@ -149,6 +184,10 @@ export class AgentSession {
149
184
  _cacheWarmer;
150
185
  // Tool registry for extension getTools/setTools
151
186
  _toolRegistry = new Map();
187
+ /** Created on the first `ctx.executeTool()` call. */
188
+ _nestedToolCalls;
189
+ /** Declared tools whose declarations requests leave out, from `prepareLoadout` hooks. */
190
+ _hiddenDeclarations = new Set();
152
191
  _toolDefinitions = new Map();
153
192
  _toolPromptSnippets = new Map();
154
193
  _toolPromptGuidelines = new Map();
@@ -170,8 +209,14 @@ export class AgentSession {
170
209
  }
171
210
  this._extensionRunnerRef = config.extensionRunnerRef;
172
211
  this._initialActiveToolNames = config.initialActiveToolNames;
173
- this._allowedToolNames = config.allowedToolNames ? new Set(config.allowedToolNames) : undefined;
174
- this._excludedToolNames = config.excludedToolNames ? new Set(config.excludedToolNames) : undefined;
212
+ this._usesDefaultTools = config.usesDefaultTools ?? false;
213
+ this._defaultToolModifiers = config.defaultToolModifiers ?? [];
214
+ if (config.allowedToolNames) {
215
+ this._allowedTools = createToolNameMatcher(config.allowedToolNames);
216
+ this._allowlistFiltersMcp =
217
+ config.allowedToolNames.length === 0 || config.allowedToolNames.some((entry) => entry.startsWith("mcp__"));
218
+ }
219
+ this._excludedTools = config.excludedToolNames ? createToolNameMatcher(config.excludedToolNames) : undefined;
175
220
  this._baseToolsOverride = config.baseToolsOverride;
176
221
  this._sessionStartEvent = config.sessionStartEvent ?? { type: "session_start", reason: "startup" };
177
222
  // Always subscribe to agent events for internal handling
@@ -179,6 +224,9 @@ export class AgentSession {
179
224
  this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
180
225
  this._installAgentToolHooks();
181
226
  this._installAgentNextTurnRefresh();
227
+ this._installAgentRequestProjection();
228
+ this._installAgentBoundaryHooks();
229
+ this._installHiddenDeclarationsProjection();
182
230
  this._installAgentForcedPromptProjection();
183
231
  this._buildRuntime({
184
232
  activeToolNames: this._initialActiveToolNames,
@@ -219,28 +267,71 @@ export class AgentSession {
219
267
  }
220
268
  throw new Error(formatNoApiKeyFoundMessage(model.provider));
221
269
  }
222
- async _getSummarizationRequestAuth(model, signal) {
270
+ async _getSummarizationRequestAuth(selectedModel, signal) {
271
+ // Route a virtual model first: summaries size their input and output from the model they get.
272
+ const { model, thinkingLevel } = isVirtualModel(selectedModel)
273
+ ? await this._modelRuntime.resolveModel(selectedModel, convertToLlm(this.messages), {
274
+ reason: "direct",
275
+ thinkingLevel: this.thinkingLevel,
276
+ signal,
277
+ })
278
+ : { model: selectedModel, thinkingLevel: this.thinkingLevel };
223
279
  if (this.agent.streamFunction === streamSimple) {
224
- return this._getRequiredRequestAuth(model, signal);
280
+ return { ...(await this._getRequiredRequestAuth(model, signal)), thinkingLevel };
225
281
  }
226
282
  try {
227
283
  const result = await this._modelRuntime.getAuth(model, { signal });
228
284
  if (!result)
229
- return { model };
285
+ return { model, thinkingLevel };
230
286
  const requestModel = result.auth.baseUrl ? { ...model, baseUrl: result.auth.baseUrl } : model;
231
287
  return {
232
288
  model: requestModel,
233
289
  apiKey: result.auth.apiKey,
234
290
  headers: withoutDeletedHeaders(result.auth.headers),
235
291
  env: result.env,
292
+ thinkingLevel,
236
293
  };
237
294
  }
238
295
  catch (error) {
239
296
  if (signal?.aborted)
240
297
  throw error;
241
- return { model };
298
+ return { model, thinkingLevel };
242
299
  }
243
300
  }
301
+ /**
302
+ * The model whose limits apply to `message`, or undefined when the message came from another
303
+ * model. Under a virtual selection, that is the physical model that produced it.
304
+ */
305
+ _modelForMessage(message) {
306
+ const model = this.model;
307
+ if (model && isVirtualModel(model))
308
+ return this._modelRuntime.getPhysicalModel(message.provider, message.model);
309
+ return model?.provider === message.provider && model.id === message.model ? model : undefined;
310
+ }
311
+ /**
312
+ * Record the selection on the current branch when the branch implies another one, so a resume
313
+ * restores it. Tree navigation can leave the latest `model_change` on another branch; responses
314
+ * cannot record a virtual selection because they name physical models. Responses do record a
315
+ * physical selection unless the branch holds a virtual one; checking a physical selection against
316
+ * responses would record it on every prompt while `prepareRequest` redirects to another model.
317
+ */
318
+ _recordSelection() {
319
+ const model = this.model;
320
+ if (!model)
321
+ return;
322
+ const getModel = (provider, modelId) => this._modelRuntime.getModel(provider, modelId);
323
+ const recorded = getBranchSelection(this.sessionManager.getBranch(), getModel);
324
+ if (!recorded || (recorded.provider === model.provider && recorded.modelId === model.id))
325
+ return;
326
+ const recordedModel = getModel(recorded.provider, recorded.modelId);
327
+ if (!isVirtualModel(model) && !(recordedModel && isVirtualModel(recordedModel)))
328
+ return;
329
+ this.sessionManager.appendModelChange(model.provider, model.id);
330
+ }
331
+ /** The model whose limits apply to the conversation. */
332
+ _limitsModel() {
333
+ return this.routedModel?.model ?? this.model;
334
+ }
244
335
  /**
245
336
  * Install tool hooks once on the Agent instance.
246
337
  *
@@ -250,65 +341,217 @@ export class AgentSession {
250
341
  * happens here instead of in wrappers.
251
342
  */
252
343
  _installAgentToolHooks() {
253
- this.agent.beforeToolCall = async ({ toolCall, args }) => {
254
- const runner = this._extensionRunner;
255
- if (!runner.hasHandlers("tool_call")) {
256
- return undefined;
257
- }
258
- try {
259
- return await runner.emitToolCall({
260
- type: "tool_call",
261
- toolName: toolCall.name,
262
- toolCallId: toolCall.id,
263
- input: args,
264
- });
265
- }
266
- catch (err) {
267
- if (err instanceof Error) {
268
- throw err;
269
- }
270
- throw new Error(`Extension failed, blocking execution: ${String(err)}`);
271
- }
272
- };
273
- this.agent.afterToolCall = async ({ toolCall, args, result, isError }) => {
274
- const runner = this._extensionRunner;
275
- if (!runner.hasHandlers("tool_result")) {
276
- return undefined;
344
+ this.agent.beforeToolCall = (context) => this._beforeToolCall(context);
345
+ this.agent.afterToolCall = (context) => this._afterToolCall(context);
346
+ }
347
+ /** `tool_call` handlers. `parentToolCallId` is set for calls another tool made. */
348
+ async _beforeToolCall({ toolCall, args }, parentToolCallId) {
349
+ const runner = this._extensionRunner;
350
+ if (!runner.hasHandlers("tool_call")) {
351
+ return undefined;
352
+ }
353
+ try {
354
+ return await runner.emitToolCall({
355
+ type: "tool_call",
356
+ toolName: toolCall.name,
357
+ toolCallId: toolCall.id,
358
+ ...(parentToolCallId ? { parentToolCallId } : {}),
359
+ input: args,
360
+ });
361
+ }
362
+ catch (err) {
363
+ if (err instanceof Error) {
364
+ throw err;
277
365
  }
278
- const hookResult = await runner.emitToolResult({
366
+ throw new Error(`Extension failed, blocking execution: ${String(err)}`);
367
+ }
368
+ }
369
+ /** `tool_result` handlers and image normalization. `parentToolCallId` is set for calls another tool made. */
370
+ async _afterToolCall({ toolCall, args, result, isError }, parentToolCallId) {
371
+ const runner = this._extensionRunner;
372
+ const hookResult = runner.hasHandlers("tool_result")
373
+ ? await runner.emitToolResult({
279
374
  type: "tool_result",
280
375
  toolName: toolCall.name,
281
376
  toolCallId: toolCall.id,
377
+ ...(parentToolCallId ? { parentToolCallId } : {}),
282
378
  input: args,
283
379
  content: result.content,
284
380
  details: result.details,
381
+ ...(result.structuredContent === undefined ? {} : { structuredContent: result.structuredContent }),
285
382
  isError,
286
383
  usage: result.usage,
287
- });
288
- if (!hookResult) {
289
- return undefined;
290
- }
291
- return {
292
- content: hookResult.content,
293
- details: hookResult.details,
294
- isError: hookResult.isError ?? isError,
295
- usage: hookResult.usage,
296
- terminate: hookResult.terminate,
297
- };
384
+ })
385
+ : undefined;
386
+ const content = hookResult?.content ?? result.content ?? [];
387
+ // Runs after the extension hook so images injected or replaced by extensions are normalized too.
388
+ const resizeOptions = this._limitsModel()?.inputLimits?.images?.resize;
389
+ const normalizedContent = await normalizeToolResultImages(content, {
390
+ autoResizeImages: this.settingsManager.getImageAutoResize(),
391
+ ...(resizeOptions ? { resizeOptions } : {}),
392
+ });
393
+ if (!hookResult && normalizedContent === content) {
394
+ return undefined;
395
+ }
396
+ // The hook result already dropped structured content that replaced content no longer matches.
397
+ return {
398
+ content: normalizedContent,
399
+ details: hookResult?.details,
400
+ structuredContent: hookResult ? hookResult.structuredContent : result.structuredContent,
401
+ isError: hookResult?.isError ?? isError,
402
+ usage: hookResult?.usage,
403
+ terminate: hookResult?.terminate,
298
404
  };
299
405
  }
406
+ /**
407
+ * Run a call that the tool call `parentToolCallId` made through `ctx.executeTool()`. It goes
408
+ * through the agent's tool pipeline with the session's hooks, against the callable tools.
409
+ */
410
+ async _executeNestedToolCall(parentToolCallId, name, args, options) {
411
+ this._nestedToolCalls ??= new NestedToolCallRunner({
412
+ getTools: () => this._getCallableTools(),
413
+ isSequential: () => this.agent.toolExecution === "sequential",
414
+ runToolCall: (toolCall, parentId, signal, onUpdate) => {
415
+ const assistantMessage = this._findLastAssistantMessage();
416
+ if (!assistantMessage) {
417
+ return Promise.resolve({
418
+ toolCall,
419
+ result: { content: [{ type: "text", text: "No assistant message issued this call" }], details: {} },
420
+ isError: true,
421
+ });
422
+ }
423
+ return runToolCall(toolCall, {
424
+ tools: this._getCallableTools(),
425
+ assistantMessage,
426
+ context: { messages: this.agent.state.messages, tools: this.agent.state.tools },
427
+ beforeToolCall: (context) => this._beforeToolCall(context, parentId),
428
+ afterToolCall: (context) => this._afterToolCall(context, parentId),
429
+ signal,
430
+ onUpdate,
431
+ });
432
+ },
433
+ emit: async (event) => {
434
+ await this._extensionRunner.emit(event);
435
+ this._emit(event);
436
+ },
437
+ });
438
+ return this._nestedToolCalls.execute(parentToolCallId, name, args, options);
439
+ }
440
+ /** Whether `projection`, the current session projection, exceeds the compaction threshold of `model`. */
441
+ _exceedsCompactionThreshold(model, projection) {
442
+ if (model.contextWindow <= 0)
443
+ return false;
444
+ return shouldCompact(estimateProjectedContextTokens(projection, this.sessionManager.getBranch()).tokens, model.contextWindow, this.settingsManager.getCompactionSettings(this.model));
445
+ }
300
446
  async _compactBeforeNextAssistantResponse(context) {
447
+ const projection = this.sessionManager.buildSessionProjection();
448
+ // A virtual selection is checked in prepareRequest, against the model the request is routed to.
301
449
  const model = this.model;
302
- const settings = this.settingsManager.getCompactionSettings(model);
303
- if (!model ||
304
- model.contextWindow <= 0 ||
305
- !shouldCompact(estimateContextTokens(context.messages).tokens, model.contextWindow, settings)) {
306
- return context;
450
+ if (!model || isVirtualModel(model) || !this._exceedsCompactionThreshold(model, projection)) {
451
+ return { ...context, messages: projection.messages };
307
452
  }
308
453
  await this._runAutoCompaction("threshold", false);
309
- return {
310
- ...context,
311
- messages: this.agent.state.messages.slice(),
454
+ return { ...context, messages: this.sessionManager.buildSessionProjection().messages };
455
+ }
456
+ _installAgentRequestProjection() {
457
+ const previousPrepareRequest = this.agent.prepareRequest;
458
+ this.agent.prepareRequest = async (request, signal) => {
459
+ const failed = this._failedResponse;
460
+ this._failedResponse = undefined;
461
+ const prepare = async () => {
462
+ const projection = this.sessionManager.buildSessionProjection();
463
+ const canonicalContext = {
464
+ ...request.context,
465
+ messages: projection.messages,
466
+ // Messages declare the provider-visible loadout; context.tools keeps executable implementations.
467
+ tools: this.agent.state.tools.slice(),
468
+ };
469
+ const previous = await previousPrepareRequest?.({
470
+ ...request,
471
+ context: canonicalContext,
472
+ model: this.agent.state.model,
473
+ thinkingLevel: this.agent.state.thinkingLevel,
474
+ }, signal);
475
+ return { previous, context: previous?.context ?? canonicalContext, projection };
476
+ };
477
+ let { previous, context, projection } = await prepare();
478
+ const model = previous?.model ?? this.agent.state.model;
479
+ const thinkingLevel = previous?.thinkingLevel ?? this.agent.state.thinkingLevel;
480
+ if (!isVirtualModel(model))
481
+ return { ...previous, context, model, thinkingLevel };
482
+ // The selection stays in agent state; only this request uses the routed model. A routing
483
+ // failure rejects, which ends the run with an error response. Only messages the user wrote
484
+ // start a turn; extension messages can follow them, e.g. from before_agent_start.
485
+ const lastResponse = context.messages.findLastIndex((message) => message.role === "assistant");
486
+ const userTurn = context.messages.slice(lastResponse + 1).some((message) => message.role === "user");
487
+ const state = getVirtualModelState(this.sessionManager.getBranch(), model.provider, model.id);
488
+ const route = await this._modelRuntime.resolveModel(model, convertToLlm(context.messages), {
489
+ reason: failed ? "retry" : userTurn ? "user" : "continuation",
490
+ thinkingLevel,
491
+ signal,
492
+ failed,
493
+ state,
494
+ });
495
+ if (route.state !== undefined && route.state !== state) {
496
+ const data = { provider: model.provider, modelId: model.id, state: route.state };
497
+ const entry = this.sessionManager.getEntry(this.sessionManager.appendCustomEntry(VIRTUAL_MODEL_STATE_ENTRY, data));
498
+ if (entry)
499
+ this._emit({ type: "entry_appended", entry });
500
+ }
501
+ // The route stands: the router already decided this request. The state entry does not change
502
+ // the projection.
503
+ if (this._exceedsCompactionThreshold(route.model, projection)) {
504
+ await this._runAutoCompaction("threshold", false);
505
+ ({ previous, context } = await prepare());
506
+ }
507
+ return { ...previous, context, model: route.model, thinkingLevel: route.thinkingLevel };
508
+ };
509
+ }
510
+ async _dispatchTurnEndBoundary(message, toolResults) {
511
+ this._lastActivityOutcome =
512
+ message.stopReason === "aborted" ? "aborted" : message.stopReason === "error" ? "error" : "completed";
513
+ const messageEntryId = this._findPersistedMessageEntryId(message);
514
+ if (!this._extensionRunner.hasHandlers("turn_end"))
515
+ return false;
516
+ if (!messageEntryId) {
517
+ this._extensionRunner.emitError({
518
+ extensionPath: "<boundary>",
519
+ event: "turn_end",
520
+ error: "turn_end could not resolve the persisted assistant entry ID",
521
+ });
522
+ return false;
523
+ }
524
+ const toolResultEntryIds = toolResults.flatMap((result) => {
525
+ const entryId = this._findPersistedMessageEntryId(result);
526
+ return entryId ? [entryId] : [];
527
+ });
528
+ const boundary = await this._extensionRunner.emitBoundary({
529
+ type: "turn_end",
530
+ turnIndex: this._turnIndex,
531
+ message,
532
+ toolResults,
533
+ messageEntryId,
534
+ toolResultEntryIds,
535
+ outcome: this._lastActivityOutcome,
536
+ }, (entries) => this._buildBoundaryContext(entries, "turn_end"));
537
+ this._commitBoundaryDrafts(boundary.entries);
538
+ if (boundary.continue && !this._buildBoundaryContext([], "turn_end").canContinue) {
539
+ this._reportInvalidBoundaryContinuation("turn_end");
540
+ return false;
541
+ }
542
+ return boundary.continue;
543
+ }
544
+ _installAgentBoundaryHooks() {
545
+ const previousFinishTurn = this.agent.finishTurn;
546
+ this.agent.finishTurn = async (turn, signal) => {
547
+ this._boundaryDispatchedMessages.add(turn.message);
548
+ const extensionContinue = await this._dispatchTurnEndBoundary(turn.message, turn.toolResults);
549
+ const previousDecision = await previousFinishTurn?.(turn, signal);
550
+ if (previousDecision?.action === "end")
551
+ return previousDecision;
552
+ if (extensionContinue || previousDecision?.action === "continue")
553
+ return { action: "continue" };
554
+ return undefined;
312
555
  };
313
556
  }
314
557
  _installAgentNextTurnRefresh() {
@@ -347,6 +590,84 @@ export class AgentSession {
347
590
  // =========================================================================
348
591
  // Event Subscription
349
592
  // =========================================================================
593
+ _refreshFinalizedContext() {
594
+ const projection = this.sessionManager.buildSessionProjection();
595
+ for (const entry of projection.entries) {
596
+ for (const message of entry.messages)
597
+ this._entryIdsByMessage.set(message, entry.sourceEntry.id);
598
+ }
599
+ this.agent.state.messages = projection.messages;
600
+ }
601
+ _applyBoundaryDrafts(manager, drafts) {
602
+ const appended = [];
603
+ for (const draft of drafts) {
604
+ let entryId;
605
+ switch (draft.type) {
606
+ case "custom":
607
+ entryId = manager.appendCustomEntry(draft.customType, draft.data);
608
+ break;
609
+ case "custom_message":
610
+ entryId = manager.appendCustomMessageEntry(draft.customType, draft.content, draft.display, draft.details);
611
+ break;
612
+ case "context_edit":
613
+ entryId = manager.appendContextEdit(draft.targetId, draft.replacement);
614
+ break;
615
+ case "compaction": {
616
+ const tokensBefore = estimateProjectedContextTokens(manager.buildSessionProjection(), manager.getBranch()).tokens;
617
+ entryId = manager.appendCompaction(draft.summary, draft.firstKeptEntryId, tokensBefore, draft.details, true, draft.usage);
618
+ break;
619
+ }
620
+ }
621
+ const entry = manager.getEntry(entryId);
622
+ if (entry)
623
+ appended.push(entry);
624
+ }
625
+ return appended;
626
+ }
627
+ _createBoundaryPreviewManager(drafts) {
628
+ const header = this.sessionManager.getHeader();
629
+ if (!header)
630
+ throw new Error("Session header is missing");
631
+ const manager = SessionManager.inMemory(this._cwd, undefined, [header, ...this.sessionManager.getBranch()]);
632
+ this._applyBoundaryDrafts(manager, drafts);
633
+ return manager;
634
+ }
635
+ _getPendingBoundaryMessages() {
636
+ return [...this.agent.peekQueuedMessages(), ...this._pendingCustomMessages];
637
+ }
638
+ _buildBoundaryContext(drafts, boundary) {
639
+ const projection = this._createBoundaryPreviewManager(drafts).buildSessionProjection();
640
+ const pendingMessages = this._getPendingBoundaryMessages();
641
+ const llmMessages = convertToLlm(projection.messages);
642
+ const finalRole = llmMessages[llmMessages.length - 1]?.role;
643
+ const hasNonSystemContext = llmMessages.some((message) => message.role !== "system");
644
+ const contextCanContinue = hasNonSystemContext && finalRole !== "assistant";
645
+ const pendingCustomContext = this._pendingCustomMessages.length > 0;
646
+ return {
647
+ contextEntries: projection.entries,
648
+ contextMessages: projection.messages,
649
+ llmMessages,
650
+ pendingMessages,
651
+ canContinue: contextCanContinue ||
652
+ pendingCustomContext ||
653
+ (boundary === "turn_end"
654
+ ? this.agent.hasQueuedMessages()
655
+ : finalRole === "assistant" && this.agent.hasQueuedMessages()),
656
+ };
657
+ }
658
+ _commitBoundaryDrafts(drafts) {
659
+ const appended = this._applyBoundaryDrafts(this.sessionManager, drafts);
660
+ this._refreshFinalizedContext();
661
+ for (const entry of appended)
662
+ this._emit({ type: "entry_appended", entry });
663
+ }
664
+ _reportInvalidBoundaryContinuation(event) {
665
+ this._extensionRunner.emitError({
666
+ extensionPath: "<boundary>",
667
+ event,
668
+ error: `${event} requested continuation without runnable model context`,
669
+ });
670
+ }
350
671
  /** Emit an event to all listeners */
351
672
  _emit(event) {
352
673
  for (const l of this._eventListeners) {
@@ -385,56 +706,45 @@ export class AgentSession {
385
706
  async _emitAgentSettled() {
386
707
  this._cacheWarmer?.onAgentSettled();
387
708
  this._isAgentRunActive = false;
709
+ this._isEmittingAgentSettled = true;
388
710
  try {
389
- await this._extensionRunner.emit({ type: "agent_settled" });
390
- this._emit({ type: "agent_settled" });
391
- await this._checkAutoHandoff();
711
+ const aborted = this._agentRunAbortRequested;
712
+ await this._extensionRunner.emit({ type: "agent_settled", aborted });
713
+ this._emit({ type: "agent_settled", aborted });
392
714
  }
393
715
  finally {
394
- this._resolveIdleWaitIfIdle();
716
+ this._isEmittingAgentSettled = false;
395
717
  }
396
- }
397
- _autoHandoffTriggered = false;
398
- async _checkAutoHandoff() {
399
- if (!this.settingsManager.getAutoHandoffEnabled())
400
- return;
401
- const usage = this.getContextUsage();
402
- if (!usage || usage.tokens === null)
403
- return;
404
- const threshold = this.settingsManager.getAutoHandoffThresholdTokens();
405
- if (usage.tokens < threshold) {
406
- this._autoHandoffTriggered = false;
407
- return;
408
- }
409
- if (this._autoHandoffTriggered)
410
- return;
411
- // Auto-handoff is TUI-only: it runs at agent_settled, and headless/print
412
- // modes should not churn sessions on their own. The handoff-new command
413
- // itself now also works in non-TUI modes (direct generation, no loader).
414
- if (this._extensionMode !== "tui")
415
- return;
416
- const command = this._extensionRunner.getCommand("handoff-new");
417
- if (!command)
718
+ const deferred = this._deferredSettledActions.splice(0);
719
+ if (deferred.length > 0) {
720
+ try {
721
+ for (const action of deferred)
722
+ await action();
723
+ }
724
+ finally {
725
+ this._resolveIdleWaitIfIdle();
726
+ }
418
727
  return;
419
- this._autoHandoffTriggered = true;
420
- const ctx = this._extensionRunner.createCommandContext();
421
- try {
422
- await command.handler(AUTO_HANDOFF_GOAL, ctx);
423
- }
424
- catch (err) {
425
- // Retry on the next settled turn while the session remains above the threshold.
426
- this._autoHandoffTriggered = false;
427
- this._extensionRunner.emitError({
428
- extensionPath: "command:handoff-new",
429
- event: "auto-handoff",
430
- error: err instanceof Error ? err.message : String(err),
431
- });
432
728
  }
729
+ this._resolveIdleWaitIfIdle();
433
730
  }
434
- // Track last assistant message for auto-compaction check
435
- _lastAssistantMessage = undefined;
436
731
  /** Internal handler for agent events - shared by subscribe and reconnect */
437
732
  _handleAgentEvent = async (event) => {
733
+ // Record the calls a tool made through ctx.executeTool() and their usage on its result message.
734
+ if (this._nestedToolCalls) {
735
+ if (event.type === "message_start" && event.message.role === "toolResult") {
736
+ const message = event.message;
737
+ const summary = this._nestedToolCalls.takeRecord(message.toolCallId);
738
+ if (summary?.calls)
739
+ message.nestedCalls = summary.calls;
740
+ if (summary?.usage) {
741
+ message.usage = message.usage ? combineUsage(message.usage, summary.usage) : summary.usage;
742
+ }
743
+ }
744
+ else if (event.type === "agent_end") {
745
+ this._nestedToolCalls.clear();
746
+ }
747
+ }
438
748
  // When a user message starts, check if it's from either queue and remove it BEFORE emitting
439
749
  // This ensures the UI sees the updated queue state
440
750
  if (event.type === "message_start" && event.message.role === "user") {
@@ -457,29 +767,30 @@ export class AgentSession {
457
767
  }
458
768
  }
459
769
  }
460
- // Emit to extensions first
770
+ // Emit to extensions first, then notify public listeners.
461
771
  await this._emitExtensionEvent(event);
462
- // Notify all listeners
463
772
  this._emit(event.type === "agent_end" ? { ...event, willRetry: this._willRetryAfterAgentEnd(event) } : event);
464
773
  // Handle session persistence
465
774
  if (event.type === "message_end") {
775
+ let entryId;
466
776
  // Check if this is a custom message from extensions
467
777
  if (event.message.role === "custom") {
468
778
  // Persist as CustomMessageEntry
469
- this.sessionManager.appendCustomMessageEntry(event.message.customType, event.message.content, event.message.display, event.message.details);
779
+ entryId = this.sessionManager.appendCustomMessageEntry(event.message.customType, event.message.content, event.message.display, event.message.details);
470
780
  }
471
781
  else if (event.message.role === "system" ||
472
782
  event.message.role === "user" ||
473
783
  event.message.role === "assistant" ||
474
784
  event.message.role === "toolResult") {
475
785
  // Regular LLM message - persist as SessionMessageEntry
476
- this.sessionManager.appendMessage(event.message);
786
+ entryId = this.sessionManager.appendMessage(event.message);
477
787
  }
788
+ if (entryId)
789
+ this._entryIdsByMessage.set(event.message, entryId);
478
790
  // Other message types (bashExecution, compactionSummary, branchSummary) are persisted elsewhere
479
- // Track assistant message for auto-compaction (checked on agent_end)
480
791
  if (event.message.role === "assistant") {
481
- this._lastAssistantMessage = event.message;
482
792
  const assistantMsg = event.message;
793
+ this._lastAssistantMessage = assistantMsg;
483
794
  // A length stop is itself a recovery trigger, so it must not clear the
484
795
  // one-shot overflow-recovery guard before the retry has run.
485
796
  if (assistantMsg.stopReason !== "error" && assistantMsg.stopReason !== "length") {
@@ -508,6 +819,7 @@ export class AgentSession {
508
819
  // extension and listener dispatch above also picks up messages that turn_end
509
820
  // handlers queued.
510
821
  if (event.type === "turn_end") {
822
+ this._lastAssistantToolResults = event.toolResults;
511
823
  this._flushPendingCustomMessages();
512
824
  }
513
825
  };
@@ -526,6 +838,47 @@ export class AgentSession {
526
838
  }
527
839
  return false;
528
840
  }
841
+ _findPersistedMessageEntryId(message) {
842
+ const mapped = this._entryIdsByMessage.get(message);
843
+ if (mapped)
844
+ return mapped;
845
+ for (const entry of [...this.sessionManager.getBranch()].reverse()) {
846
+ if (entry.type === "message" && entry.message === message)
847
+ return entry.id;
848
+ }
849
+ const messageIndex = this.agent.state.messages.indexOf(message);
850
+ if (messageIndex < 0)
851
+ return undefined;
852
+ const projection = this.sessionManager.buildSessionProjection();
853
+ let projectedIndex = 0;
854
+ for (const entry of projection.entries) {
855
+ for (let i = 0; i < entry.messages.length; i++) {
856
+ if (projectedIndex === messageIndex) {
857
+ this._entryIdsByMessage.set(message, entry.sourceEntry.id);
858
+ return entry.sourceEntry.id;
859
+ }
860
+ projectedIndex++;
861
+ }
862
+ }
863
+ return undefined;
864
+ }
865
+ _omitRecoveryAttempt(message, toolResults = []) {
866
+ const targets = [message, ...toolResults];
867
+ const targetIds = targets.map((target) => this._findPersistedMessageEntryId(target));
868
+ const unresolvedProjectedTarget = targets.some((target, index) => targetIds[index] === undefined && this.agent.state.messages.includes(target));
869
+ if (unresolvedProjectedTarget) {
870
+ throw new Error("Cannot persist recovery omission because a projected message has no source entry");
871
+ }
872
+ for (const targetId of targetIds) {
873
+ if (!targetId)
874
+ continue;
875
+ const editId = this.sessionManager.appendContextEdit(targetId, null);
876
+ const entry = this.sessionManager.getEntry(editId);
877
+ if (entry)
878
+ this._emit({ type: "entry_appended", entry });
879
+ }
880
+ this._refreshFinalizedContext();
881
+ }
529
882
  /** Find the last assistant message in agent state (including aborted ones) */
530
883
  _findLastAssistantMessage() {
531
884
  const messages = this.agent.state.messages;
@@ -575,13 +928,9 @@ export class AgentSession {
575
928
  await this._extensionRunner.emit(extensionEvent);
576
929
  }
577
930
  else if (event.type === "turn_end") {
578
- const extensionEvent = {
579
- type: "turn_end",
580
- turnIndex: this._turnIndex,
581
- message: event.message,
582
- toolResults: event.toolResults,
583
- };
584
- await this._extensionRunner.emit(extensionEvent);
931
+ if (event.message.role === "assistant" && !this._boundaryDispatchedMessages.delete(event.message)) {
932
+ await this._dispatchTurnEndBoundary(event.message, event.toolResults);
933
+ }
585
934
  this._turnIndex++;
586
935
  }
587
936
  else if (event.type === "message_start") {
@@ -645,6 +994,7 @@ export class AgentSession {
645
994
  toolName: event.toolName,
646
995
  result: event.result,
647
996
  isError: event.isError,
997
+ ...(event.durationMs === undefined ? {} : { durationMs: event.durationMs }),
648
998
  };
649
999
  await this._extensionRunner.emit(extensionEvent);
650
1000
  }
@@ -664,26 +1014,13 @@ export class AgentSession {
664
1014
  }
665
1015
  };
666
1016
  }
667
- /**
668
- * Temporarily disconnect from agent events.
669
- * User listeners are preserved and will receive events again after resubscribe().
670
- * Used internally during operations that need to pause event processing.
671
- */
1017
+ /** Disconnect from agent events during disposal. */
672
1018
  _disconnectFromAgent() {
673
1019
  if (this._unsubscribeAgent) {
674
1020
  this._unsubscribeAgent();
675
1021
  this._unsubscribeAgent = undefined;
676
1022
  }
677
1023
  }
678
- /**
679
- * Reconnect to agent events after _disconnectFromAgent().
680
- * Preserves all existing listeners.
681
- */
682
- _reconnectToAgent() {
683
- if (this._unsubscribeAgent)
684
- return; // Already connected
685
- this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
686
- }
687
1024
  /**
688
1025
  * Remove all listeners and disconnect from agent.
689
1026
  * Call this when completely done with the session.
@@ -711,6 +1048,10 @@ export class AgentSession {
711
1048
  // =========================================================================
712
1049
  // Read-only State Access
713
1050
  // =========================================================================
1051
+ /** Refresh the public finalized transcript from the canonical session projection. */
1052
+ refreshContext() {
1053
+ this._refreshFinalizedContext();
1054
+ }
714
1055
  /** Full agent state */
715
1056
  get state() {
716
1057
  return this.agent.state;
@@ -732,6 +1073,14 @@ export class AgentSession {
732
1073
  get thinkingLevel() {
733
1074
  return this.agent.state.thinkingLevel;
734
1075
  }
1076
+ /** Under a virtual selection, the physical model and thinking level of the latest successful response. */
1077
+ get routedModel() {
1078
+ if (!this.model || !isVirtualModel(this.model))
1079
+ return undefined;
1080
+ const latest = findLatestResponse(this.agent.state.messages);
1081
+ const model = latest && this._modelRuntime.getPhysicalModel(latest.provider, latest.model);
1082
+ return model && { model, thinkingLevel: latest?.thinkingLevel };
1083
+ }
735
1084
  /** Whether the session is currently processing an agent run or post-run continuation. */
736
1085
  get isStreaming() {
737
1086
  return this._isAgentRunActive;
@@ -749,12 +1098,16 @@ export class AgentSession {
749
1098
  return this._retryAttempt;
750
1099
  }
751
1100
  /**
752
- * Get the names of currently active tools.
753
- * Returns the names of tools currently set on the agent.
1101
+ * Get the names of currently active tools, which are the tools declared to the model.
1102
+ * Tools with `codemode` or `deferred` exposure are callable from other tools without being active.
754
1103
  */
755
1104
  getActiveToolNames() {
756
1105
  return this.agent.state.tools.map((t) => t.name);
757
1106
  }
1107
+ /** Get the names of the tools that tools can call through `ctx.executeTool()`. */
1108
+ getCallableToolNames() {
1109
+ return this._getCallableTools().map((t) => t.name);
1110
+ }
758
1111
  /**
759
1112
  * Get all configured tools with name, description, parameter schema, prompt guidelines, and source metadata.
760
1113
  */
@@ -764,7 +1117,9 @@ export class AgentSession {
764
1117
  description: definition.description,
765
1118
  parameters: definition.parameters,
766
1119
  promptGuidelines: definition.promptGuidelines,
767
- discovery: definition.discovery,
1120
+ exposure: this._getToolExposure(definition.name),
1121
+ ...(definition.namespace ? { namespace: definition.namespace } : {}),
1122
+ ...(definition.annotations ? { annotations: { ...definition.annotations } } : {}),
768
1123
  sourceInfo,
769
1124
  }));
770
1125
  }
@@ -773,22 +1128,113 @@ export class AgentSession {
773
1128
  }
774
1129
  /**
775
1130
  * Set active tools by name.
776
- * Only tools in the registry can be enabled. Unknown tool names are ignored.
1131
+ * Only tools in the registry can be enabled. Unknown and hidden tool names are ignored.
777
1132
  * Also rebuilds the system prompt to reflect the new tool set.
778
1133
  * Changes take effect on the next agent turn.
779
1134
  */
780
1135
  setActiveToolsByName(toolNames) {
781
- const tools = [];
782
- const validToolNames = [];
783
- for (const name of toolNames) {
1136
+ const previous = this.getActiveToolNames();
1137
+ this._setActiveTools(toolNames);
1138
+ // A loadout that deactivates a tool replaces the restored one, whose pending tools are dropped.
1139
+ // One that only adds tools, like activating tool_search, keeps them.
1140
+ const active = new Set(this.getActiveToolNames());
1141
+ if (previous.some((name) => !active.has(name)))
1142
+ this._pendingToolNames.clear();
1143
+ }
1144
+ _setActiveTools(toolNames) {
1145
+ const tools = this._applyToolLoadout(toolNames);
1146
+ for (const tool of tools)
1147
+ this._pendingToolNames.delete(tool.name);
1148
+ this._rebuildSystemPrompt(tools.map((tool) => tool.name));
1149
+ }
1150
+ /**
1151
+ * Whether `--tools` and `--exclude-tools` keep the tool registered. MCP tools stay registered
1152
+ * unless the allowlist filters them (see `_allowlistFiltersMcp`).
1153
+ */
1154
+ _isAllowedTool(name) {
1155
+ if (this._excludedTools?.(name))
1156
+ return false;
1157
+ if (!this._allowedTools || this._allowedTools(name))
1158
+ return true;
1159
+ return !this._allowlistFiltersMcp && isMcpToolName(name);
1160
+ }
1161
+ /**
1162
+ * Whether the tool may be active, which declares it to the model. MCP tools the allowlist keeps
1163
+ * without matching them are only for codemode and tool_search: they may be declared only when
1164
+ * tool_search can load them (non-`direct` exposure and tool_search registered). This also applies
1165
+ * to tools restored from the transcript or set by extensions.
1166
+ */
1167
+ _isActivatable(name) {
1168
+ if (!this._allowedTools || this._allowedTools(name) || !isMcpToolName(name))
1169
+ return true;
1170
+ return this._getToolExposure(name) !== "direct" && this._toolRegistry.has("tool_search");
1171
+ }
1172
+ _getToolExposure(name) {
1173
+ return this._toolDefinitions.get(name)?.definition.exposure ?? "direct";
1174
+ }
1175
+ /**
1176
+ * Tools callable through `ctx.executeTool()`: the active `direct` tools and every registered
1177
+ * `codemode` or `deferred` tool.
1178
+ */
1179
+ _getCallableTools(active = new Set(this.getActiveToolNames())) {
1180
+ return [...this._toolRegistry.values()].filter((tool) => {
1181
+ const exposure = this._getToolExposure(tool.name);
1182
+ return exposure === "codemode" || exposure === "deferred" || (exposure === "direct" && active.has(tool.name));
1183
+ });
1184
+ }
1185
+ /**
1186
+ * Set the agent's tools for the given active tool names and return them. The active tools are
1187
+ * the registered, non-hidden ones; they are declared to the model. Active tools with a
1188
+ * `prepareLoadout` hook can change the declared descriptions and hide declarations from
1189
+ * requests (see {@link _installHiddenDeclarationsProjection}).
1190
+ */
1191
+ _applyToolLoadout(toolNames) {
1192
+ const tools = [...new Set(toolNames)].flatMap((name) => {
784
1193
  const tool = this._toolRegistry.get(name);
785
- if (tool) {
786
- tools.push(tool);
787
- validToolNames.push(name);
1194
+ return tool && this._getToolExposure(name) !== "hidden" && this._isActivatable(name) ? [tool] : [];
1195
+ });
1196
+ const hooks = tools.flatMap((tool) => {
1197
+ const entry = this._toolDefinitions.get(tool.name);
1198
+ return entry?.definition.prepareLoadout ? [entry] : [];
1199
+ });
1200
+ const hidden = new Set();
1201
+ let declared = tools;
1202
+ if (hooks.length > 0) {
1203
+ const loadout = {
1204
+ declared: tools,
1205
+ callable: this._getCallableTools(new Set(tools.map((tool) => tool.name))),
1206
+ registered: [...this._toolRegistry.values()],
1207
+ getExposure: (name) => this._getToolExposure(name),
1208
+ getNamespace: (name) => this._toolDefinitions.get(name)?.definition.namespace,
1209
+ getPromptGuidelines: (name) => this._toolPromptGuidelines.get(name) ?? [],
1210
+ };
1211
+ const descriptions = new Map();
1212
+ for (const { definition, sourceInfo } of hooks) {
1213
+ try {
1214
+ const changes = definition.prepareLoadout?.(loadout);
1215
+ for (const [name, description] of Object.entries(changes?.descriptions ?? {})) {
1216
+ descriptions.set(name, description);
1217
+ }
1218
+ for (const name of changes?.hiddenDeclarations ?? [])
1219
+ hidden.add(name);
1220
+ }
1221
+ catch (error) {
1222
+ this._extensionRunner.emitError({
1223
+ extensionPath: sourceInfo.path,
1224
+ event: "prepare_loadout",
1225
+ error: error instanceof Error ? error.message : String(error),
1226
+ stack: error instanceof Error ? error.stack : undefined,
1227
+ });
1228
+ }
788
1229
  }
1230
+ declared = tools.map((tool) => {
1231
+ const description = descriptions.get(tool.name);
1232
+ return description === undefined ? tool : { ...tool, description };
1233
+ });
789
1234
  }
790
- this.agent.state.tools = tools;
791
- this._rebuildSystemPrompt(validToolNames);
1235
+ this._hiddenDeclarations = hidden;
1236
+ this.agent.state.tools = declared;
1237
+ return declared;
792
1238
  }
793
1239
  /** Whether compaction or branch summarization is currently running */
794
1240
  get isCompacting() {
@@ -859,6 +1305,7 @@ export class AgentSession {
859
1305
  const toolSnippets = {};
860
1306
  for (const name of this._toolRegistry.keys()) {
861
1307
  const snippet = this._toolPromptSnippets.get(name);
1308
+ // Tools without a snippet are not listed.
862
1309
  if (snippet)
863
1310
  toolSnippets[name] = snippet;
864
1311
  }
@@ -876,6 +1323,7 @@ export class AgentSession {
876
1323
  customPrompt: loaderSystemPrompt,
877
1324
  appendSystemPrompt,
878
1325
  selectedTools: validToolNames,
1326
+ hiddenTools: [...this._hiddenDeclarations],
879
1327
  toolSnippets,
880
1328
  toolGuidelines: Object.fromEntries(this._toolPromptGuidelines),
881
1329
  });
@@ -891,11 +1339,9 @@ export class AgentSession {
891
1339
  * {@link _installAgentForcedPromptProjection}.
892
1340
  */
893
1341
  _preparePromptAndToolLoadout(options, messages = this.agent.state.messages) {
894
- options.selectedTools = [...new Set(options.selectedTools)].filter((name) => this._toolRegistry.has(name));
895
- this.agent.state.tools = options.selectedTools.flatMap((name) => {
896
- const tool = this._toolRegistry.get(name);
897
- return tool ? [tool] : [];
898
- });
1342
+ options.selectedTools = this._applyToolLoadout(options.selectedTools).map((tool) => tool.name);
1343
+ // The tool list and rules must match the declarations the request carries.
1344
+ options.hiddenTools = [...this._hiddenDeclarations];
899
1345
  const sections = diffSystemPromptSections(getCurrentSystemMessage(messages)?.sections ?? {}, buildSystemPromptSections(options));
900
1346
  return sections ? { role: "system", content: "", sections, timestamp: Date.now() } : undefined;
901
1347
  }
@@ -909,6 +1355,32 @@ export class AgentSession {
909
1355
  * collapse into one head holding the forced text and the current tools. Runs after the
910
1356
  * `context` extension handlers.
911
1357
  */
1358
+ /**
1359
+ * Remove the declarations that `prepareLoadout` hooks hide from every request. The whole
1360
+ * transcript is filtered with the current set, so the projected declarations stay consistent
1361
+ * across requests and only change when the loadout does.
1362
+ */
1363
+ _installHiddenDeclarationsProjection() {
1364
+ const previousTransformContext = this.agent.transformContext;
1365
+ this.agent.transformContext = async (messages, signal) => {
1366
+ const transformed = previousTransformContext ? await previousTransformContext(messages, signal) : messages;
1367
+ const hidden = this._hiddenDeclarations;
1368
+ if (hidden.size === 0)
1369
+ return transformed;
1370
+ return transformed.map((message) => {
1371
+ if (message.role !== "system" || (!message.toolsAdded && !message.toolsRemoved))
1372
+ return message;
1373
+ const { toolsAdded, toolsRemoved, ...rest } = message;
1374
+ const added = toolsAdded?.filter((tool) => !hidden.has(tool.name)) ?? [];
1375
+ const removed = toolsRemoved?.filter((tool) => !hidden.has(tool.name)) ?? [];
1376
+ return {
1377
+ ...rest,
1378
+ ...(added.length > 0 ? { toolsAdded: added } : {}),
1379
+ ...(removed.length > 0 ? { toolsRemoved: removed } : {}),
1380
+ };
1381
+ });
1382
+ };
1383
+ }
912
1384
  _installAgentForcedPromptProjection() {
913
1385
  const previousTransformContext = this.agent.transformContext;
914
1386
  this.agent.transformContext = async (messages, signal) => {
@@ -926,29 +1398,43 @@ export class AgentSession {
926
1398
  return [head, ...transformed.filter((message) => message.role !== "system")];
927
1399
  };
928
1400
  }
929
- /** Restore the active tool loadout declared by the session transcript, if it declares one. */
1401
+ /**
1402
+ * Restore the active tool loadout declared by the session transcript, if it declares one.
1403
+ * Tools reachable only from other tools are never declared, but they do not depend on the active
1404
+ * set, so the transcript's declarations are the whole loadout.
1405
+ */
930
1406
  _restoreToolsFromTranscript() {
1407
+ this._pendingToolNames.clear();
931
1408
  const current = getCurrentSystemMessage(this.sessionManager.buildSessionContext().messages);
932
1409
  if (!current)
933
1410
  return;
934
- const toolNames = (current.toolsAdded ?? [])
935
- .map((tool) => tool.name)
936
- .filter((name) => this._toolRegistry.has(name));
937
- this.agent.state.tools = toolNames.flatMap((name) => {
938
- const registered = this._toolRegistry.get(name);
939
- return registered ? [registered] : [];
940
- });
941
- this._rebuildSystemPrompt(toolNames);
1411
+ const names = (current.toolsAdded ?? []).map((tool) => tool.name);
1412
+ this._pendingToolNames = new Set(names.filter((name) => this._isAllowedTool(name)));
1413
+ this._setActiveTools(names);
942
1414
  }
943
1415
  // =========================================================================
944
1416
  // Prompting
945
1417
  // =========================================================================
946
1418
  async _runAgentPrompt(messages) {
947
1419
  this._agentRunAbortRequested = false;
1420
+ // Compaction before the prompt may have scheduled a retry; the new prompt replaces it.
1421
+ this._failedResponse = undefined;
1422
+ this._recordSelection();
1423
+ // The run records the loadout in the transcript; restored tools that did not register by now
1424
+ // are dropped, so a tool that never registers does not stay pending.
1425
+ this._pendingToolNames.clear();
948
1426
  this._isAgentRunActive = true;
949
1427
  try {
950
1428
  await this.agent.prompt(messages);
951
- while (await this._handlePostAgentRun()) {
1429
+ while (!this._agentRunAbortRequested) {
1430
+ if (await this._handlePostAgentRun()) {
1431
+ if (this._agentRunAbortRequested)
1432
+ break;
1433
+ await this.agent.continue();
1434
+ continue;
1435
+ }
1436
+ if (this._agentRunAbortRequested || !(await this._runBeforeSettleBoundary()))
1437
+ break;
952
1438
  if (this._agentRunAbortRequested)
953
1439
  break;
954
1440
  await this.agent.continue();
@@ -957,6 +1443,7 @@ export class AgentSession {
957
1443
  finally {
958
1444
  if (this._agentRunAbortRequested)
959
1445
  this._finishCancelledRetry();
1446
+ this._failedResponse = undefined;
960
1447
  this._runSystemPromptOptions = undefined;
961
1448
  this._flushPendingBashMessages();
962
1449
  this._flushPendingCustomMessages();
@@ -964,43 +1451,52 @@ export class AgentSession {
964
1451
  }
965
1452
  }
966
1453
  async _handlePostAgentRun() {
967
- const msg = this._lastAssistantMessage;
1454
+ const message = this._lastAssistantMessage;
1455
+ const toolResults = this._lastAssistantToolResults;
968
1456
  this._lastAssistantMessage = undefined;
1457
+ this._lastAssistantToolResults = [];
969
1458
  if (this._agentRunAbortRequested) {
970
1459
  this._finishCancelledRetry();
971
1460
  return false;
972
1461
  }
973
- if (!msg) {
974
- return false;
975
- }
976
- if (this._isRetryableError(msg) && (await this._prepareRetry(msg))) {
1462
+ if (!message)
1463
+ return this.agent.hasQueuedMessages();
1464
+ if (this._isRetryableError(message) && (await this._prepareRetry(message))) {
977
1465
  if (this._agentRunAbortRequested)
978
1466
  this._finishCancelledRetry();
1467
+ this._failedResponse = message;
979
1468
  return !this._agentRunAbortRequested;
980
1469
  }
981
1470
  if (this._agentRunAbortRequested) {
982
1471
  this._finishCancelledRetry();
983
1472
  return false;
984
1473
  }
985
- if (msg.stopReason === "error" && this._retryAttempt > 0) {
1474
+ if (message.stopReason === "error" && this._retryAttempt > 0) {
986
1475
  this._emit({
987
1476
  type: "auto_retry_end",
988
1477
  success: false,
989
1478
  attempt: this._retryAttempt,
990
- finalError: msg.errorMessage,
1479
+ finalError: message.errorMessage,
991
1480
  });
992
1481
  this._retryAttempt = 0;
993
1482
  }
994
- if (await this._checkCompaction(msg)) {
1483
+ if (await this._checkCompaction(message, true, toolResults)) {
995
1484
  return !this._agentRunAbortRequested;
996
1485
  }
997
1486
  // A length stop means the model hit the output token limit mid-response.
998
1487
  // The agent loop already re-prompts when truncated tool calls are present;
999
1488
  // here the loop stopped on truncated text, so queue a follow-up to continue.
1000
1489
  // Compaction runs first: a length stop caused by context pressure (input
1001
- // filling the window) should compact instead of blindly continuing.
1002
- if (msg.stopReason === "length" &&
1003
- !msg.content.some((c) => c.type === "toolCall") &&
1490
+ // filling the window) should compact instead of blindly continuing. Once
1491
+ // durable recovery omits an attempt, a failed compaction must not fall
1492
+ // through to continuation with that attempt absent from canonical context.
1493
+ const lengthEntryId = message.stopReason === "length" ? this._findPersistedMessageEntryId(message) : undefined;
1494
+ const lengthResponseRetained = lengthEntryId === undefined ||
1495
+ this.sessionManager.buildSessionProjection().entries.some((entry) => entry.sourceEntry.id === lengthEntryId &&
1496
+ entry.messages.some((projected) => projected.role === "assistant"));
1497
+ if (message.stopReason === "length" &&
1498
+ lengthResponseRetained &&
1499
+ !message.content.some((c) => c.type === "toolCall") &&
1004
1500
  this._lengthContinuations < MAX_LENGTH_CONTINUATIONS) {
1005
1501
  this._lengthContinuations++;
1006
1502
  await this._queueFollowUp(LENGTH_CONTINUATION_MESSAGE);
@@ -1010,6 +1506,30 @@ export class AgentSession {
1010
1506
  // here were queued by agent_end extension handlers and need a continuation.
1011
1507
  return !this._agentRunAbortRequested && this.agent.hasQueuedMessages();
1012
1508
  }
1509
+ async _runBeforeSettleBoundary() {
1510
+ if (!this._extensionRunner.hasHandlers("agent_before_settle"))
1511
+ return this.agent.hasQueuedMessages();
1512
+ this._isBeforeSettle = true;
1513
+ this._abortDuringBeforeSettle = false;
1514
+ try {
1515
+ const result = await this._extensionRunner.emitBoundary({ type: "agent_before_settle", outcome: this._lastActivityOutcome }, (entries) => this._buildBoundaryContext(entries, "agent_before_settle"));
1516
+ this._commitBoundaryDrafts(result.entries);
1517
+ this._flushPendingCustomMessages();
1518
+ const finalContext = this._buildBoundaryContext([], "agent_before_settle");
1519
+ if (this._abortDuringBeforeSettle)
1520
+ return false;
1521
+ const shouldContinue = result.continue || this.agent.hasQueuedMessages();
1522
+ if (shouldContinue && !finalContext.canContinue) {
1523
+ if (result.continue)
1524
+ this._reportInvalidBoundaryContinuation("agent_before_settle");
1525
+ return false;
1526
+ }
1527
+ return shouldContinue;
1528
+ }
1529
+ finally {
1530
+ this._isBeforeSettle = false;
1531
+ }
1532
+ }
1013
1533
  async _runInputHandlers(text, images, source, streamingBehavior) {
1014
1534
  if (!this._extensionRunner.hasHandlers("input")) {
1015
1535
  return { text, images };
@@ -1023,6 +1543,25 @@ export class AgentSession {
1023
1543
  }
1024
1544
  return { text, images };
1025
1545
  }
1546
+ async _normalizePromptImages(images) {
1547
+ if (!images)
1548
+ return { images: [], hints: [] };
1549
+ const normalizedImages = [];
1550
+ const hints = [];
1551
+ for (const image of images) {
1552
+ const processed = await processImage(Buffer.from(image.data, "base64"), image.mimeType, {
1553
+ autoResizeImages: this.settingsManager.getImageAutoResize(),
1554
+ resizeOptions: this._limitsModel()?.inputLimits?.images?.resize,
1555
+ });
1556
+ if (!processed.ok) {
1557
+ hints.push(processed.message);
1558
+ continue;
1559
+ }
1560
+ normalizedImages.push({ type: "image", data: processed.data, mimeType: processed.mimeType });
1561
+ hints.push(...processed.hints);
1562
+ }
1563
+ return { images: normalizedImages, hints };
1564
+ }
1026
1565
  /**
1027
1566
  * Send a prompt to the agent.
1028
1567
  * - Handles extension commands (registered via pi.registerCommand) immediately, even during streaming
@@ -1033,149 +1572,137 @@ export class AgentSession {
1033
1572
  * @throws Error if no model selected or no API key available (when not streaming)
1034
1573
  */
1035
1574
  async prompt(text, options) {
1575
+ if (this._isEmittingAgentSettled) {
1576
+ this._deferredSettledActions.push(async () => await this.prompt(text, options));
1577
+ return;
1578
+ }
1036
1579
  const expandPromptTemplates = options?.expandPromptTemplates ?? true;
1037
1580
  const preflightResult = options?.preflightResult;
1038
- let messages;
1039
- try {
1040
- // Handle extension commands first (execute immediately, even during streaming)
1041
- // Extension commands manage their own LLM interaction via pi.sendMessage()
1042
- if (expandPromptTemplates && text.startsWith("/")) {
1043
- const handled = await this._tryExecuteExtensionCommand(text);
1044
- if (handled) {
1045
- // Extension command executed, no prompt to send
1046
- preflightResult?.(true);
1047
- return;
1048
- }
1049
- }
1050
- if (this._compactionAbortController !== undefined) {
1051
- throw new Error("Cannot submit a prompt while compaction is in progress. Wait for compaction to finish and retry.");
1052
- }
1053
- // Emit input event for extension interception (before skill/template expansion)
1054
- const processedInput = await this._runInputHandlers(text, options?.images, options?.source ?? "interactive", this.isStreaming ? options?.streamingBehavior : undefined);
1055
- if (!processedInput) {
1056
- preflightResult?.(true);
1581
+ // Handle extension commands first (execute immediately, even during streaming)
1582
+ // Extension commands manage their own LLM interaction via pi.sendMessage()
1583
+ if (expandPromptTemplates && text.startsWith("/")) {
1584
+ const handled = await this._tryExecuteExtensionCommand(text);
1585
+ if (handled) {
1586
+ // Extension command executed, no prompt to send
1587
+ preflightResult?.("handled");
1057
1588
  return;
1058
1589
  }
1059
- const { text: currentText, images: currentImages } = processedInput;
1060
- // Expand skill commands (/skill:name args) and prompt templates (/template args)
1061
- let expandedText = currentText;
1062
- if (expandPromptTemplates) {
1063
- expandedText = this._expandSkillCommand(expandedText);
1064
- expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
1065
- }
1066
- // Vision relay: when the active model cannot accept images, describe
1067
- // attached images with the configured caption model (images.imageCaptionModel)
1068
- // so the main model can work from the description text.
1069
- if (currentImages && currentImages.length > 0) {
1070
- const caption = await this._captionImagesForCurrentModel(currentImages, currentText);
1071
- if (caption) {
1072
- expandedText += `\n\n${caption}`;
1073
- }
1074
- }
1075
- // If streaming, queue via steer() or followUp() based on option
1076
- if (this.isStreaming) {
1077
- if (!options?.streamingBehavior) {
1078
- throw new Error("Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.");
1079
- }
1080
- if (options.streamingBehavior === "followUp") {
1081
- await this._queueFollowUp(expandedText, currentImages);
1082
- }
1083
- else {
1084
- await this._queueSteer(expandedText, currentImages);
1085
- }
1086
- preflightResult?.(true);
1087
- return;
1590
+ }
1591
+ if (this._compactionAbortController !== undefined) {
1592
+ throw new Error("Cannot submit a prompt while compaction is in progress. Wait for compaction to finish and retry.");
1593
+ }
1594
+ // Emit input event for extension interception (before skill/template expansion)
1595
+ const processedInput = await this._runInputHandlers(text, options?.images, options?.source ?? "interactive", this.isStreaming ? options?.streamingBehavior : undefined);
1596
+ if (!processedInput) {
1597
+ preflightResult?.("handled");
1598
+ return;
1599
+ }
1600
+ const { text: currentText, images: currentImages } = processedInput;
1601
+ // Expand skill commands (/skill:name args) and prompt templates (/template args)
1602
+ let expandedText = currentText;
1603
+ if (expandPromptTemplates) {
1604
+ expandedText = this._expandSkillCommand(expandedText);
1605
+ expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
1606
+ }
1607
+ // Fork vision relay: non-vision models receive the configured caption as text.
1608
+ if (currentImages && currentImages.length > 0) {
1609
+ const caption = await this._captionImagesForCurrentModel(currentImages, currentText);
1610
+ if (caption)
1611
+ expandedText += `\n\n${caption}`;
1612
+ }
1613
+ // If streaming, queue via steer() or followUp() based on option
1614
+ if (this.isStreaming) {
1615
+ if (!options?.streamingBehavior) {
1616
+ throw new Error("Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.");
1088
1617
  }
1089
- // Flush any pending bash and custom messages before the new prompt
1090
- this._flushPendingBashMessages();
1091
- this._flushPendingCustomMessages();
1092
- // Validate model
1093
- if (!this.model) {
1094
- throw new Error(formatNoModelSelectedMessage());
1618
+ if (options.streamingBehavior === "followUp") {
1619
+ await this._queueFollowUp(expandedText, currentImages);
1095
1620
  }
1096
- const hasConfiguredAuth = this._modelRuntime.hasConfiguredAuth(this.model.provider) ||
1097
- (await this._modelRuntime.checkAuth(this.model.provider)) !== undefined;
1098
- if (!hasConfiguredAuth) {
1099
- const isOAuth = this._modelRuntime.isUsingOAuth(this.model.provider);
1100
- if (isOAuth) {
1101
- throw new Error(`Authentication failed for "${this.model.provider}". ` +
1102
- `Credentials may have expired or network is unavailable. ` +
1103
- `Run '/login ${this.model.provider}' to re-authenticate.`);
1104
- }
1105
- throw new Error(formatNoApiKeyFoundMessage(this.model.provider));
1621
+ else {
1622
+ await this._queueSteer(expandedText, currentImages);
1106
1623
  }
1107
- // Check if we need to compact before sending (catches aborted responses).
1108
- // The user's new prompt is sent below, so do not call agent.continue() here.
1109
- const lastAssistant = this._findLastAssistantMessage();
1110
- if (lastAssistant) {
1111
- await this._checkCompaction(lastAssistant, false);
1624
+ preflightResult?.("queued");
1625
+ return;
1626
+ }
1627
+ // Flush any pending bash and custom messages before the new prompt
1628
+ this._flushPendingBashMessages();
1629
+ this._flushPendingCustomMessages();
1630
+ // Validate model
1631
+ if (!this.model) {
1632
+ throw new Error(formatNoModelSelectedMessage());
1633
+ }
1634
+ const hasConfiguredAuth = this._modelRuntime.hasConfiguredAuth(this.model.provider) ||
1635
+ (await this._modelRuntime.checkAuth(this.model.provider)) !== undefined;
1636
+ if (!hasConfiguredAuth) {
1637
+ const isOAuth = this._modelRuntime.isUsingOAuth(this.model.provider);
1638
+ if (isOAuth) {
1639
+ throw new Error(`Authentication failed for "${this.model.provider}". ` +
1640
+ `Credentials may have expired or network is unavailable. ` +
1641
+ `Run '/login ${this.model.provider}' to re-authenticate.`);
1642
+ }
1643
+ throw new Error(formatNoApiKeyFoundMessage(this.model.provider));
1644
+ }
1645
+ // Check if we need to compact before sending (catches aborted responses).
1646
+ // The user's new prompt is sent below, so do not call agent.continue() here.
1647
+ const lastAssistant = this._findLastAssistantMessage();
1648
+ if (lastAssistant) {
1649
+ await this._checkCompaction(lastAssistant, false);
1650
+ }
1651
+ // Emit before_agent_start before normalizing images so extension-driven model
1652
+ // selection determines the resize profile used for the request and history.
1653
+ const selectedToolsBefore = this._baseSystemPromptOptions.selectedTools;
1654
+ const result = await this._extensionRunner.emitBeforeAgentStart(expandedText, currentImages, this._baseSystemPromptOptions);
1655
+ // Handlers may edit event.systemPromptOptions.selectedTools or call setActiveTools(),
1656
+ // which updates the live loadout instead. An explicit edit wins; otherwise the live
1657
+ // loadout is authoritative, so a setActiveTools() call is not undone here.
1658
+ const handlerEditedTools = result.systemPromptOptions.selectedTools.length !== selectedToolsBefore.length ||
1659
+ result.systemPromptOptions.selectedTools.some((name, index) => name !== selectedToolsBefore[index]);
1660
+ if (!handlerEditedTools)
1661
+ result.systemPromptOptions.selectedTools = this.getActiveToolNames();
1662
+ const normalized = await this._normalizePromptImages(currentImages);
1663
+ const userText = normalized.hints.length > 0 ? `${expandedText}\n\n${normalized.hints.join("\n")}` : expandedText;
1664
+ // Another prompt can start while input/caption/auth/hooks preflight awaits.
1665
+ // Queue instead of racing it; do not consume pending next-turn messages yet.
1666
+ if (this.isStreaming) {
1667
+ if (options?.streamingBehavior === "steer") {
1668
+ await this._queueSteer(expandedText, currentImages);
1112
1669
  }
1113
- // Build messages array (custom message if any, then user message)
1114
- messages = [];
1115
- // Add user message
1116
- const userContent = [{ type: "text", text: expandedText }];
1117
- if (currentImages) {
1118
- userContent.push(...currentImages);
1670
+ else {
1671
+ await this._queueFollowUp(expandedText, currentImages);
1119
1672
  }
1673
+ preflightResult?.("queued");
1674
+ return;
1675
+ }
1676
+ // Build messages only after hooks and image normalization have completed.
1677
+ const messages = [];
1678
+ const userContent = [{ type: "text", text: userText }];
1679
+ userContent.push(...normalized.images);
1680
+ messages.push({
1681
+ role: "user",
1682
+ content: userContent,
1683
+ timestamp: Date.now(),
1684
+ });
1685
+ // Inject any pending "nextTurn" messages as context alongside the user message
1686
+ for (const msg of this._pendingNextTurnMessages) {
1687
+ messages.push(msg);
1688
+ }
1689
+ this._pendingNextTurnMessages = [];
1690
+ for (const msg of result.messages) {
1120
1691
  messages.push({
1121
- role: "user",
1122
- content: userContent,
1692
+ role: "custom",
1693
+ customType: msg.customType,
1694
+ // Untyped extensions can pass null/missing content; normalize at ingestion.
1695
+ content: msg.content ?? [],
1696
+ display: msg.display,
1697
+ details: msg.details,
1123
1698
  timestamp: Date.now(),
1124
1699
  });
1125
- // Inject any pending "nextTurn" messages as context alongside the user message
1126
- for (const msg of this._pendingNextTurnMessages) {
1127
- messages.push(msg);
1128
- }
1129
- this._pendingNextTurnMessages = [];
1130
- // Emit before_agent_start extension event
1131
- const selectedToolsBefore = this._baseSystemPromptOptions.selectedTools;
1132
- const result = await this._extensionRunner.emitBeforeAgentStart(expandedText, currentImages, this._baseSystemPromptOptions);
1133
- // Handlers may edit event.systemPromptOptions.selectedTools or call setActiveTools(),
1134
- // which updates the live loadout instead. An explicit edit wins; otherwise the live
1135
- // loadout is authoritative, so a setActiveTools() call is not undone here.
1136
- const handlerEditedTools = result.systemPromptOptions.selectedTools.length !== selectedToolsBefore.length ||
1137
- result.systemPromptOptions.selectedTools.some((name, index) => name !== selectedToolsBefore[index]);
1138
- if (!handlerEditedTools)
1139
- result.systemPromptOptions.selectedTools = this.getActiveToolNames();
1140
- for (const msg of result.messages) {
1141
- messages.push({
1142
- role: "custom",
1143
- customType: msg.customType,
1144
- // Untyped extensions can pass null/missing content; normalize at ingestion.
1145
- content: msg.content ?? [],
1146
- display: msg.display,
1147
- details: msg.details,
1148
- timestamp: Date.now(),
1149
- });
1150
- }
1151
- // Another prompt may have started while this one was preflighting (auth
1152
- // checks, compaction, and extension events can take seconds). The agent is
1153
- // now streaming, so queue instead of racing the active run: the RPC layer
1154
- // has already reported preflight success, so throwing here would silently
1155
- // drop the message. Default to a follow-up so it runs after the current turn.
1156
- if (this.isStreaming) {
1157
- if (options?.streamingBehavior === "steer") {
1158
- await this._queueSteer(expandedText, currentImages);
1159
- }
1160
- else {
1161
- await this._queueFollowUp(expandedText, currentImages);
1162
- }
1163
- preflightResult?.(true);
1164
- return;
1165
- }
1166
- const updateMessage = this._preparePromptAndToolLoadout(result.systemPromptOptions);
1167
- this._runSystemPromptOptions = result.systemPromptOptions;
1168
- if (updateMessage)
1169
- messages.unshift(updateMessage);
1170
- }
1171
- catch (error) {
1172
- preflightResult?.(false);
1173
- throw error;
1174
- }
1175
- if (!messages) {
1176
- return;
1177
1700
  }
1178
- preflightResult?.(true);
1701
+ const updateMessage = this._preparePromptAndToolLoadout(result.systemPromptOptions);
1702
+ this._runSystemPromptOptions = result.systemPromptOptions;
1703
+ if (updateMessage)
1704
+ messages.unshift(updateMessage);
1705
+ preflightResult?.("started");
1179
1706
  await this._runAgentPrompt(messages);
1180
1707
  }
1181
1708
  /**
@@ -1241,7 +1768,7 @@ export class AgentSession {
1241
1768
  }
1242
1769
  const processedInput = await this._runInputHandlers(text, images, source, this.isStreaming ? behavior : undefined);
1243
1770
  if (!processedInput)
1244
- return;
1771
+ return "handled";
1245
1772
  let expandedText = this._expandSkillCommand(processedInput.text);
1246
1773
  expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
1247
1774
  if (behavior === "steer") {
@@ -1250,6 +1777,7 @@ export class AgentSession {
1250
1777
  else {
1251
1778
  await this._queueFollowUp(expandedText, processedInput.images);
1252
1779
  }
1780
+ return "queued";
1253
1781
  }
1254
1782
  /**
1255
1783
  * Queue a steering message while the agent is running.
@@ -1261,7 +1789,7 @@ export class AgentSession {
1261
1789
  * @throws Error if text is an extension command
1262
1790
  */
1263
1791
  async steer(text, images, options) {
1264
- await this._queueUserInput(text, images, "steer", options?.source ?? "interactive");
1792
+ return this._queueUserInput(text, images, "steer", options?.source ?? "interactive");
1265
1793
  }
1266
1794
  /**
1267
1795
  * Queue a follow-up message to be processed after the agent finishes.
@@ -1272,7 +1800,7 @@ export class AgentSession {
1272
1800
  * @throws Error if text is an extension command
1273
1801
  */
1274
1802
  async followUp(text, images, options) {
1275
- await this._queueUserInput(text, images, "followUp", options?.source ?? "interactive");
1803
+ return this._queueUserInput(text, images, "followUp", options?.source ?? "interactive");
1276
1804
  }
1277
1805
  /**
1278
1806
  * Internal: Queue a steering message (already expanded, no extension command check).
@@ -1300,11 +1828,7 @@ export class AgentSession {
1300
1828
  if (images) {
1301
1829
  content.push(...images);
1302
1830
  }
1303
- this.agent.followUp({
1304
- role: "user",
1305
- content,
1306
- timestamp: Date.now(),
1307
- });
1831
+ this.agent.followUp({ role: "user", content, timestamp: Date.now() });
1308
1832
  }
1309
1833
  /**
1310
1834
  * Throw an error if the text is an extension command.
@@ -1352,6 +1876,10 @@ export class AgentSession {
1352
1876
  }
1353
1877
  }
1354
1878
  else if (options?.triggerTurn) {
1879
+ if (this._isEmittingAgentSettled) {
1880
+ this._deferredSettledActions.push(async () => await this._runAgentPrompt(appMessage));
1881
+ return;
1882
+ }
1355
1883
  await this._runAgentPrompt(appMessage);
1356
1884
  }
1357
1885
  else if (this.isStreaming) {
@@ -1366,8 +1894,8 @@ export class AgentSession {
1366
1894
  }
1367
1895
  }
1368
1896
  _appendCustomMessage(appMessage) {
1369
- this.agent.state.messages.push(appMessage);
1370
1897
  this.sessionManager.appendCustomMessageEntry(appMessage.customType, appMessage.content, appMessage.display, appMessage.details);
1898
+ this._refreshFinalizedContext();
1371
1899
  this._emit({ type: "message_start", message: appMessage });
1372
1900
  this._emit({ type: "message_end", message: appMessage });
1373
1901
  }
@@ -1541,6 +2069,7 @@ export class AgentSession {
1541
2069
  *
1542
2070
  * @param content User message content (string or content array)
1543
2071
  * @param options.deliverAs Delivery mode when streaming: "steer" or "followUp"
2072
+ * @param options.expandPromptTemplates Whether to dispatch extension commands and expand skill commands and prompt templates. Default: false.
1544
2073
  */
1545
2074
  async sendUserMessage(content, options) {
1546
2075
  // Normalize content to text string + optional images
@@ -1564,7 +2093,6 @@ export class AgentSession {
1564
2093
  if (images.length === 0)
1565
2094
  images = undefined;
1566
2095
  }
1567
- // Use prompt() with expandPromptTemplates to control command handling and template expansion
1568
2096
  await this.prompt(text, {
1569
2097
  expandPromptTemplates: options?.expandPromptTemplates ?? false,
1570
2098
  streamingBehavior: options?.deliverAs,
@@ -1622,6 +2150,8 @@ export class AgentSession {
1622
2150
  this.abortRetry();
1623
2151
  this.abortCompaction();
1624
2152
  this.abortBranchSummary();
2153
+ if (this._isBeforeSettle)
2154
+ this._abortDuringBeforeSettle = true;
1625
2155
  this.agent.abort();
1626
2156
  await this.waitForIdle();
1627
2157
  }
@@ -1646,7 +2176,8 @@ export class AgentSession {
1646
2176
  }
1647
2177
  /**
1648
2178
  * Set model directly.
1649
- * Validates that auth is configured, saves to session and settings.
2179
+ * Validates that auth is configured and saves to the session transcript.
2180
+ * Persists to global defaults only when options.persist is true.
1650
2181
  * @throws Error if no auth is configured for the model
1651
2182
  */
1652
2183
  async setModel(model, options = {}) {
@@ -1661,6 +2192,9 @@ export class AgentSession {
1661
2192
  this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
1662
2193
  this._addPersistedDefaultToNonEmptyScope(model);
1663
2194
  }
2195
+ // Apply thinking level for the new model.
2196
+ // Per-model thinking level overrides take priority over the global default.
2197
+ // Model persistence does not implicitly rewrite the global thinking default.
1664
2198
  this.setThinkingLevel(thinkingLevel);
1665
2199
  await this._emitModelSelect(model, previousModel, "set");
1666
2200
  }
@@ -1691,11 +2225,8 @@ export class AgentSession {
1691
2225
  return this._cycleAvailableModel(direction, options);
1692
2226
  }
1693
2227
  async _cycleScopedModel(direction, options) {
1694
- const checks = await Promise.all(this._scopedModels.map(async (scoped) => ({
1695
- scoped,
1696
- auth: await this._modelRuntime.checkAuth(scoped.model.provider),
1697
- })));
1698
- const scopedModels = checks.filter(({ auth }) => auth !== undefined).map(({ scoped }) => scoped);
2228
+ const availableIds = new Set(this._modelRuntime.getAvailableSnapshot().map((model) => `${model.provider}\0${model.id}`));
2229
+ const scopedModels = this._scopedModels.filter((scoped) => availableIds.has(`${scoped.model.provider}\0${scoped.model.id}`));
1699
2230
  if (scopedModels.length <= 1)
1700
2231
  return undefined;
1701
2232
  const currentModel = this.model;
@@ -1713,16 +2244,17 @@ export class AgentSession {
1713
2244
  this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
1714
2245
  this._addPersistedDefaultToNonEmptyScope(next.model);
1715
2246
  }
1716
- // Apply thinking level.
1717
- // - Explicit scoped model thinking level overrides current session level
1718
- // - Undefined scoped model thinking level inherits the current session preference
2247
+ // Apply thinking level for the new model.
2248
+ // - Explicit scoped model thinking level overrides defaults
2249
+ // - Per-model thinking level overrides take priority over the global default
1719
2250
  // setThinkingLevel clamps to model capabilities.
2251
+ // Model persistence does not implicitly rewrite the global thinking default.
1720
2252
  this.setThinkingLevel(thinkingLevel);
1721
2253
  await this._emitModelSelect(next.model, currentModel, "cycle");
1722
2254
  return { model: next.model, thinkingLevel: this.thinkingLevel, isScoped: true };
1723
2255
  }
1724
2256
  async _cycleAvailableModel(direction, options) {
1725
- const availableModels = await this._modelRuntime.getAvailable();
2257
+ const availableModels = this._modelRuntime.getAvailableSnapshot();
1726
2258
  if (availableModels.length <= 1)
1727
2259
  return undefined;
1728
2260
  const currentModel = this.model;
@@ -1739,7 +2271,8 @@ export class AgentSession {
1739
2271
  this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
1740
2272
  this._addPersistedDefaultToNonEmptyScope(nextModel);
1741
2273
  }
1742
- // Re-clamp thinking level for new model's capabilities
2274
+ // Apply thinking level for the new model.
2275
+ // Model persistence does not implicitly rewrite the global thinking default.
1743
2276
  this.setThinkingLevel(thinkingLevel);
1744
2277
  await this._emitModelSelect(nextModel, currentModel, "cycle");
1745
2278
  return { model: nextModel, thinkingLevel: this.thinkingLevel, isScoped: false };
@@ -1750,7 +2283,8 @@ export class AgentSession {
1750
2283
  /**
1751
2284
  * Set thinking level.
1752
2285
  * Clamps to model capabilities based on available thinking levels.
1753
- * Saves to session and settings only if the level actually changes.
2286
+ * Saves the clamped level to the session transcript only if the level actually changes.
2287
+ * Persists the requested level to global defaults only when options.persist is true.
1754
2288
  */
1755
2289
  setThinkingLevel(level, options = {}) {
1756
2290
  const availableLevels = this.getAvailableThinkingLevels();
@@ -1805,10 +2339,12 @@ export class AgentSession {
1805
2339
  if (explicitLevel !== undefined) {
1806
2340
  return explicitLevel;
1807
2341
  }
2342
+ // Per-model default takes priority when switching to a model that has one
1808
2343
  if (targetModel) {
1809
2344
  const perModel = this.settingsManager.getModelThinkingLevel(targetModel.provider, targetModel.id);
1810
- if (perModel !== undefined)
2345
+ if (perModel !== undefined) {
1811
2346
  return perModel;
2347
+ }
1812
2348
  }
1813
2349
  return this.settingsManager.getDefaultThinkingLevel() ?? this.thinkingLevel ?? DEFAULT_THINKING_LEVEL;
1814
2350
  }
@@ -1841,9 +2377,11 @@ export class AgentSession {
1841
2377
  // =========================================================================
1842
2378
  // Compaction
1843
2379
  // =========================================================================
1844
- /** Generate the built-in compaction summary for manual and automatic compaction. */
1845
- async _runDefaultCompaction(preparation, requestModel, apiKey, headers, customInstructions, signal, env, reason) {
1846
- return compact(preparation, requestModel, apiKey, headers, customInstructions, signal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }), undefined);
2380
+ /** Generate Pi's built-in compaction summary for manual and automatic compaction. */
2381
+ async _runDefaultCompaction(preparation, model, customInstructions, signal, reason) {
2382
+ // Resolve the request only when Pi summarizes itself: routing may call models or fail.
2383
+ const request = await this._getSummarizationRequestAuth(model, signal);
2384
+ return compact(preparation, request.model, request.apiKey, request.headers, customInstructions, signal, request.thinkingLevel, this.agent.streamFunction, request.env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }), undefined);
1847
2385
  }
1848
2386
  _clearManualCompactionState() {
1849
2387
  this._compactionAbortController = undefined;
@@ -1851,11 +2389,20 @@ export class AgentSession {
1851
2389
  }
1852
2390
  /**
1853
2391
  * Manually compact the session context.
1854
- * Aborts current agent operation first.
2392
+ *
2393
+ * This is the manual entry point used by `/compact`, RPC, and extensions. It is
2394
+ * separate from automatic threshold/overflow compaction, which enters through
2395
+ * `_checkCompaction()` and `_runAutoCompaction()`. After preparation and the
2396
+ * `session_before_compact` hook, both paths call the lower-level `compact()`
2397
+ * function imported from `./compaction/index.ts`, unless the hook cancels or
2398
+ * supplies a custom result.
2399
+ *
2400
+ * Aborts the current agent operation first. Manual compaction never retries or
2401
+ * continues the interrupted agent turn.
2402
+ *
1855
2403
  * @param customInstructions Optional instructions for the compaction summary
1856
2404
  */
1857
2405
  async compact(customInstructions) {
1858
- this._disconnectFromAgent();
1859
2406
  await this.abort();
1860
2407
  this._compactionAbortController = new AbortController();
1861
2408
  this._emit({ type: "compaction_start", reason: "manual" });
@@ -1867,7 +2414,6 @@ export class AgentSession {
1867
2414
  throw new Error(formatNoModelSelectedMessage());
1868
2415
  }
1869
2416
  const settings = this.settingsManager.getCompactionSettings(model);
1870
- const { model: requestModel, apiKey, headers, env, } = await this._getSummarizationRequestAuth(model, this._compactionAbortController.signal);
1871
2417
  const pathEntries = this.sessionManager.getBranch();
1872
2418
  const preparation = prepareCompaction(pathEntries, settings);
1873
2419
  if (!preparation) {
@@ -1879,7 +2425,6 @@ export class AgentSession {
1879
2425
  throw new Error("Nothing to compact (session too small)");
1880
2426
  }
1881
2427
  let extensionCompaction;
1882
- let fromExtension = false;
1883
2428
  if (this._extensionRunner.hasHandlers("session_before_compact")) {
1884
2429
  const result = (await this._extensionRunner.emit({
1885
2430
  type: "session_before_compact",
@@ -1914,7 +2459,7 @@ export class AgentSession {
1914
2459
  }
1915
2460
  else {
1916
2461
  // Shared default summary generator, also used by automatic compaction.
1917
- const result = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, env, "manual");
2462
+ const result = await this._runDefaultCompaction(preparation, model, customInstructions, this._compactionAbortController.signal, "manual");
1918
2463
  summary = result.summary;
1919
2464
  firstKeptEntryId = result.firstKeptEntryId;
1920
2465
  tokensBefore = result.tokensBefore;
@@ -1926,9 +2471,8 @@ export class AgentSession {
1926
2471
  }
1927
2472
  this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
1928
2473
  const newEntries = this.sessionManager.getEntries();
1929
- const sessionContext = this.sessionManager.buildSessionContext();
1930
- this.agent.state.messages = sessionContext.messages;
1931
- const estimatedTokensAfter = estimateMessagesTokens(sessionContext.messages);
2474
+ this._refreshFinalizedContext();
2475
+ const estimatedTokensAfter = estimateMessagesTokens(this.sessionManager.buildSessionProjection().messages);
1932
2476
  // Get the saved compaction entry for the extension event
1933
2477
  const savedCompactionEntry = newEntries.find((e) => e.type === "compaction" && e.summary === summary);
1934
2478
  if (this._extensionRunner && savedCompactionEntry) {
@@ -1977,13 +2521,12 @@ export class AgentSession {
1977
2521
  errorMessage,
1978
2522
  aborted,
1979
2523
  willRetry: false,
1980
- fromExtension: false,
2524
+ fromExtension,
1981
2525
  });
1982
2526
  throw error;
1983
2527
  }
1984
2528
  finally {
1985
2529
  this._clearManualCompactionState();
1986
- this._reconnectToAgent();
1987
2530
  }
1988
2531
  }
1989
2532
  /**
@@ -2000,29 +2543,41 @@ export class AgentSession {
2000
2543
  this._branchSummaryAbortController?.abort();
2001
2544
  }
2002
2545
  /**
2003
- * Check if compaction is needed and run it.
2004
- * Called after agent_end and before prompt submission.
2546
+ * Dispatch automatic compaction after `agent_end` or before prompt submission.
2547
+ * Manual compaction does not call this method; it enters through `compact()`.
2548
+ *
2549
+ * Automatic cases:
2550
+ * 1. Overflow with retry: a context-overflow error or recoverable length stop;
2551
+ * remove the failed assistant message, compact, and retry the turn once.
2552
+ * 2. Overflow without retry: a successful response exceeded the configured
2553
+ * context window; compact but preserve the completed response.
2554
+ * 3. Threshold without retry: valid or estimated context usage crossed the
2555
+ * configured threshold; compact without retrying the completed response.
2005
2556
  *
2006
- * Two cases:
2007
- * 1. Overflow: LLM returned context overflow error, remove error message from agent state, compact, auto-retry
2008
- * 2. Threshold: Context over threshold, compact, NO auto-retry (user continues manually)
2557
+ * Each case calls `_runAutoCompaction()`. After preparation and the
2558
+ * `session_before_compact` hook, that method calls the lower-level `compact()`
2559
+ * function imported from `./compaction/index.ts`, unless the hook cancels or
2560
+ * supplies a custom result.
2009
2561
  *
2010
2562
  * @param assistantMessage The assistant message to check
2011
2563
  * @param skipAbortedCheck If false, include aborted messages (for pre-prompt check). Default: true
2564
+ * @returns Whether the post-run loop should call `agent.continue()` for overflow recovery or queued messages
2012
2565
  */
2013
- async _checkCompaction(assistantMessage, skipAbortedCheck = true) {
2566
+ async _checkCompaction(assistantMessage, skipAbortedCheck = true, toolResults = []) {
2014
2567
  const settings = this.settingsManager.getCompactionSettings(this.model);
2015
2568
  if (!settings.enabled)
2016
2569
  return false;
2017
2570
  // Skip if message was aborted (user cancelled) - unless skipAbortedCheck is false
2018
2571
  if (skipAbortedCheck && assistantMessage.stopReason === "aborted")
2019
2572
  return false;
2020
- const contextWindow = this.model?.contextWindow ?? 0;
2021
2573
  // Skip overflow check if the message came from a different model.
2022
2574
  // This handles the case where user switched from a smaller-context model (e.g. opus)
2023
2575
  // to a larger-context model (e.g. codex) - the overflow error from the old model
2024
- // shouldn't trigger compaction for the new model.
2025
- const sameModel = this.model && assistantMessage.provider === this.model.provider && assistantMessage.model === this.model.id;
2576
+ // shouldn't trigger compaction for the new model. Under a virtual selection, the
2577
+ // physical model that produced the message supplies the limits.
2578
+ const messageModel = this._modelForMessage(assistantMessage);
2579
+ const sameModel = messageModel !== undefined;
2580
+ const contextWindow = (messageModel ?? this.model)?.contextWindow ?? 0;
2026
2581
  // Skip compaction checks if this assistant message is older than the latest
2027
2582
  // compaction boundary. This prevents a stale pre-compaction usage/error
2028
2583
  // from retriggering compaction on the first prompt after compaction.
@@ -2034,54 +2589,89 @@ export class AgentSession {
2034
2589
  // Automatic cases 1 and 2: context overflow.
2035
2590
  // A length stop is recoverable when output ended below the model's original desired limit,
2036
2591
  // independent of the configured context size or any context-clamped provider request limit.
2037
- const contextOverflow = sameModel && isContextOverflow(assistantMessage, contextWindow);
2038
- const recoverableLength = sameModel && isRecoverableLength(assistantMessage, this.model?.maxTokens ?? 0);
2592
+ const currentProjection = this.sessionManager.buildSessionProjection();
2593
+ const assistantEntryId = this._findPersistedMessageEntryId(assistantMessage);
2594
+ const assistantIsProjected = assistantEntryId === undefined ||
2595
+ currentProjection.entries.some((entry) => entry.sourceEntry.id === assistantEntryId &&
2596
+ entry.messages.some((message) => message.role === "assistant"));
2597
+ const branch = this.sessionManager.getBranch();
2598
+ const assistantIndex = assistantEntryId ? branch.findIndex((entry) => entry.id === assistantEntryId) : -1;
2599
+ const entriesAfterAssistant = assistantIndex >= 0 ? branch.slice(assistantIndex + 1) : [];
2600
+ const hasPostAssistantContextEdit = entriesAfterAssistant.some((entry) => entry.type === "context_edit");
2601
+ const latestAssistantEdit = entriesAfterAssistant
2602
+ .filter((entry) => entry.type === "context_edit" && entry.targetId === assistantEntryId)
2603
+ .at(-1);
2604
+ const assistantRetainedForExplicitRecovery = assistantEntryId === undefined ||
2605
+ (!entriesAfterAssistant.some((entry) => entry.type === "compaction") &&
2606
+ latestAssistantEdit?.replacement !== null);
2607
+ const assistantUsageMatchesProjection = assistantIsProjected && !hasPostAssistantContextEdit;
2608
+ const explicitOverflow = assistantMessage.stopReason === "error" && isContextOverflow(assistantMessage);
2609
+ const contextOverflow = sameModel &&
2610
+ ((explicitOverflow && assistantRetainedForExplicitRecovery) ||
2611
+ (assistantUsageMatchesProjection && isContextOverflow(assistantMessage, contextWindow)));
2612
+ const recoverableLength = sameModel && assistantIsProjected && isRecoverableLength(assistantMessage, messageModel.maxTokens);
2039
2613
  if (contextOverflow || recoverableLength) {
2040
2614
  const willRetry = assistantMessage.stopReason !== "stop";
2615
+ // Case 2: the response completed successfully. Compact, but do not retry because
2616
+ // agent.continue() cannot continue from a completed assistant response.
2041
2617
  if (!willRetry) {
2042
2618
  return await this._runAutoCompaction("overflow", false);
2043
2619
  }
2044
2620
  if (this._overflowRecoveryAttempted) {
2621
+ const errorMessage = contextOverflow
2622
+ ? "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model."
2623
+ : "Truncated response recovery failed after one compact-and-retry attempt.";
2045
2624
  this._emit({
2046
2625
  type: "compaction_end",
2047
2626
  reason: "overflow",
2048
2627
  result: undefined,
2049
2628
  aborted: false,
2050
2629
  willRetry: false,
2051
- errorMessage: contextOverflow
2052
- ? "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model."
2053
- : "Truncated response recovery failed after one compact-and-retry attempt.",
2630
+ errorMessage,
2631
+ });
2632
+ await this._emitSessionCompactFailed({
2633
+ reason: "overflow",
2634
+ errorMessage,
2635
+ aborted: false,
2636
+ willRetry: false,
2637
+ fromExtension: false,
2054
2638
  });
2055
2639
  return false;
2056
2640
  }
2641
+ // Persistently omit the selected final attempt before post-run recovery compaction.
2057
2642
  this._overflowRecoveryAttempted = true;
2058
- // Remove the error message from agent state (it IS saved to session for history,
2059
- // but we don't want it in context for the retry)
2060
- const messages = this.agent.state.messages;
2061
- if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
2062
- this.agent.state.messages = messages.slice(0, -1);
2063
- }
2064
- return await this._runAutoCompaction("overflow", willRetry);
2643
+ this._omitRecoveryAttempt(assistantMessage, toolResults);
2644
+ const retry = await this._runAutoCompaction("overflow", willRetry);
2645
+ if (retry)
2646
+ this._failedResponse = assistantMessage;
2647
+ return retry;
2065
2648
  }
2066
- // Case 2: Threshold - context is getting large
2649
+ // Case 3: threshold compaction without retry.
2067
2650
  // For error messages or all-zero usage messages, estimate from the last valid response.
2068
2651
  // This ensures sessions that hit persistent API errors (e.g. 529) or malformed zero-usage
2069
2652
  // responses can still compact and do not reset context accounting.
2070
2653
  let contextTokens;
2654
+ const projection = currentProjection;
2655
+ const hasContextEdits = projection.entries.some((entry) => entry.sourceEntry.type === "context_edit");
2071
2656
  const directContextTokens = assistantMessage.usage ? calculateContextTokens(assistantMessage.usage) : 0;
2072
- if (assistantMessage.stopReason === "error" || directContextTokens === 0) {
2657
+ if (hasContextEdits) {
2658
+ contextTokens = estimateProjectedContextTokens(projection, branch).tokens;
2659
+ }
2660
+ else if (assistantMessage.stopReason === "error" || directContextTokens === 0) {
2073
2661
  const messages = this.agent.state.messages;
2074
2662
  const estimate = estimateContextTokens(messages);
2075
- if (estimate.lastUsageIndex === null)
2076
- return false; // No usage data at all
2077
- // Verify the usage source is post-compaction. Kept pre-compaction messages
2078
- // have stale usage reflecting the old (larger) context and would falsely
2079
- // trigger compaction right after one just finished.
2080
- const usageMsg = messages[estimate.lastUsageIndex];
2081
- if (compactionEntry &&
2082
- usageMsg.role === "assistant" &&
2083
- usageMsg.timestamp <= new Date(compactionEntry.timestamp).getTime()) {
2084
- return false;
2663
+ // Without provider usage, estimate.tokens is the pure message-size estimate.
2664
+ // Only usage-backed estimates need the stale pre-compaction check.
2665
+ if (estimate.lastUsageIndex !== null) {
2666
+ // Verify the usage source is post-compaction. Kept pre-compaction messages
2667
+ // have stale usage reflecting the old (larger) context and would falsely
2668
+ // trigger compaction right after one just finished.
2669
+ const usageMsg = messages[estimate.lastUsageIndex];
2670
+ if (compactionEntry &&
2671
+ usageMsg.role === "assistant" &&
2672
+ usageMsg.timestamp <= new Date(compactionEntry.timestamp).getTime()) {
2673
+ return false;
2674
+ }
2085
2675
  }
2086
2676
  contextTokens = estimate.tokens;
2087
2677
  }
@@ -2094,7 +2684,14 @@ export class AgentSession {
2094
2684
  return false;
2095
2685
  }
2096
2686
  /**
2097
- * Internal: Run auto-compaction with events.
2687
+ * Execute threshold or overflow compaction. Manual compaction uses
2688
+ * `AgentSession.compact()` instead. Both paths call the lower-level `compact()`
2689
+ * function imported from `./compaction/index.ts` after preparation and extension
2690
+ * interception.
2691
+ *
2692
+ * @param reason Automatic trigger selected by `_checkCompaction()`
2693
+ * @param willRetry Whether to continue the interrupted turn after overflow compaction
2694
+ * @returns Whether the post-run loop should call `agent.continue()`
2098
2695
  */
2099
2696
  async _runAutoCompaction(reason, willRetry) {
2100
2697
  const model = this.model;
@@ -2117,8 +2714,6 @@ export class AgentSession {
2117
2714
  started = true;
2118
2715
  this._emit({ type: "compaction_start", reason });
2119
2716
  abortController.signal.throwIfAborted();
2120
- const { model: requestModel, apiKey, headers, env, } = await this._getSummarizationRequestAuth(model, abortController.signal);
2121
- abortController.signal.throwIfAborted();
2122
2717
  let extensionCompaction;
2123
2718
  if (this._extensionRunner.hasHandlers("session_before_compact")) {
2124
2719
  const extensionResult = (await this._extensionRunner.emit({
@@ -2155,7 +2750,7 @@ export class AgentSession {
2155
2750
  }
2156
2751
  else {
2157
2752
  // Shared default summary generator, also used by manual compaction.
2158
- const compactResult = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, undefined, abortController.signal, env, reason);
2753
+ const compactResult = await this._runDefaultCompaction(preparation, model, undefined, abortController.signal, reason);
2159
2754
  summary = compactResult.summary;
2160
2755
  firstKeptEntryId = compactResult.firstKeptEntryId;
2161
2756
  tokensBefore = compactResult.tokensBefore;
@@ -2165,9 +2760,8 @@ export class AgentSession {
2165
2760
  abortController.signal.throwIfAborted();
2166
2761
  this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
2167
2762
  const newEntries = this.sessionManager.getEntries();
2168
- const sessionContext = this.sessionManager.buildSessionContext();
2169
- this.agent.state.messages = sessionContext.messages;
2170
- const estimatedTokensAfter = estimateMessagesTokens(sessionContext.messages);
2763
+ this._refreshFinalizedContext();
2764
+ const estimatedTokensAfter = estimateMessagesTokens(this.sessionManager.buildSessionProjection().messages);
2171
2765
  // Get the saved compaction entry for the extension event
2172
2766
  const savedCompactionEntry = newEntries.find((e) => e.type === "compaction" && e.summary === summary);
2173
2767
  if (this._extensionRunner && savedCompactionEntry) {
@@ -2188,18 +2782,8 @@ export class AgentSession {
2188
2782
  details,
2189
2783
  };
2190
2784
  this._emit({ type: "compaction_end", reason, result, aborted: false, willRetry });
2191
- if (willRetry) {
2192
- const messages = this.agent.state.messages;
2193
- const lastMsg = messages[messages.length - 1];
2194
- // The overflow response was persisted on message_end before _checkCompaction() removed it
2195
- // from agent state. Rebuilding state from the new compaction can restore that kept entry,
2196
- // leaving an assistant as the final message. agent.continue() rejects that state, so remove
2197
- // the retriable error or truncated-length response again before continuing the interrupted turn.
2198
- if (lastMsg?.role === "assistant" && (lastMsg.stopReason === "error" || lastMsg.stopReason === "length")) {
2199
- this.agent.state.messages = messages.slice(0, -1);
2200
- }
2785
+ if (willRetry)
2201
2786
  return true;
2202
- }
2203
2787
  // Auto-compaction can complete while follow-up/steering/custom messages are waiting.
2204
2788
  // Continue once so queued messages are delivered.
2205
2789
  return this.agent.hasQueuedMessages();
@@ -2248,12 +2832,6 @@ export class AgentSession {
2248
2832
  get autoCompactionEnabled() {
2249
2833
  return this.settingsManager.getCompactionEnabled();
2250
2834
  }
2251
- get autoHandoffEnabled() {
2252
- return this.settingsManager.getAutoHandoffEnabled();
2253
- }
2254
- get autoHandoffThresholdTokens() {
2255
- return this.settingsManager.getAutoHandoffThresholdTokens();
2256
- }
2257
2835
  async bindExtensions(bindings) {
2258
2836
  if (bindings.uiContext !== undefined) {
2259
2837
  this._extensionUIContext = bindings.uiContext;
@@ -2275,6 +2853,7 @@ export class AgentSession {
2275
2853
  }
2276
2854
  this._applyExtensionBindings(this._extensionRunner);
2277
2855
  await this._extensionRunner.emit(this._sessionStartEvent);
2856
+ this._extensionRunner.reportUnhandledMcpServers();
2278
2857
  await this.extendResourcesFromExtensions(this._sessionStartEvent.reason === "reload" ? "reload" : "startup");
2279
2858
  }
2280
2859
  async extendResourcesFromExtensions(reason) {
@@ -2296,7 +2875,7 @@ export class AgentSession {
2296
2875
  buildExtensionResourcePaths(entries) {
2297
2876
  return entries.map((entry) => {
2298
2877
  const source = this.getExtensionSourceLabel(entry.extensionPath);
2299
- const baseDir = entry.extensionPath.startsWith("<") ? undefined : dirname(entry.extensionPath);
2878
+ const baseDir = isSyntheticPath(entry.extensionPath) ? undefined : dirname(entry.extensionPath);
2300
2879
  return {
2301
2880
  path: entry.path,
2302
2881
  metadata: {
@@ -2309,7 +2888,7 @@ export class AgentSession {
2309
2888
  });
2310
2889
  }
2311
2890
  getExtensionSourceLabel(extensionPath) {
2312
- if (extensionPath.startsWith("<")) {
2891
+ if (isSyntheticPath(extensionPath)) {
2313
2892
  return `extension:${extensionPath.replace(/[<>]/g, "")}`;
2314
2893
  }
2315
2894
  const base = basename(extensionPath);
@@ -2394,6 +2973,7 @@ export class AgentSession {
2394
2973
  },
2395
2974
  getActiveTools: () => this.getActiveToolNames(),
2396
2975
  getAllTools: () => this.getAllTools(),
2976
+ getSettings: () => this.settingsManager.getSettings(),
2397
2977
  setActiveTools: (toolNames) => this.setActiveToolsByName(toolNames),
2398
2978
  getResolvedSkills: () => this._resourceLoader.getResolvedSkills().map((resource) => {
2399
2979
  const skill = this._resourceLoader.getSkills().skills.find((s) => s.filePath === resource.path);
@@ -2452,6 +3032,8 @@ export class AgentSession {
2452
3032
  },
2453
3033
  getSystemPrompt: () => this.systemPrompt,
2454
3034
  getSystemPromptOptions: () => this._baseSystemPromptOptions,
3035
+ executeTool: (callerId, name, args, options) => this._executeNestedToolCall(callerId, name, args, options),
3036
+ getCallableTools: () => this._getCallableTools(),
2455
3037
  }, {
2456
3038
  registerProvider: (name, config) => {
2457
3039
  this._modelRuntime.registerProvider(name, config);
@@ -2465,14 +3047,22 @@ export class AgentSession {
2465
3047
  this._modelRuntime.unregisterProvider(name);
2466
3048
  this._refreshCurrentModelFromRegistry();
2467
3049
  },
3050
+ registerVirtualModel: (definition) => {
3051
+ this._modelRuntime.registerVirtualModel(definition);
3052
+ this._refreshCurrentModelFromRegistry();
3053
+ },
3054
+ unregisterVirtualModel: (provider, id) => {
3055
+ this._modelRuntime.unregisterVirtualModel(provider, id);
3056
+ this._refreshCurrentModelFromRegistry();
3057
+ },
2468
3058
  });
2469
3059
  }
2470
3060
  _refreshToolRegistry(options) {
2471
- const previousRegistryNames = new Set(this._toolRegistry.keys());
3061
+ // Tools that were already activated on registration. A tool whose exposure changes to
3062
+ // `direct` or `model-only` (for example from `hidden`) is activated like a new tool.
3063
+ const previousActivatedOnRegistration = new Set([...this._toolRegistry.keys()].filter((name) => this._isActivatedOnRegistration(name)));
2472
3064
  const previousActiveToolNames = this.getActiveToolNames();
2473
- const allowedToolNames = this._allowedToolNames;
2474
- const excludedToolNames = this._excludedToolNames;
2475
- const isAllowedTool = (name) => (!allowedToolNames || allowedToolNames.has(name)) && !excludedToolNames?.has(name);
3065
+ const allowedTools = this._allowedTools;
2476
3066
  const registeredTools = this._extensionRunner.getAllRegisteredTools();
2477
3067
  const allCustomTools = [
2478
3068
  ...registeredTools,
@@ -2480,14 +3070,14 @@ export class AgentSession {
2480
3070
  definition,
2481
3071
  sourceInfo: createSyntheticSourceInfo(`<sdk:${definition.name}>`, { source: "sdk" }),
2482
3072
  })),
2483
- ].filter((tool) => isAllowedTool(tool.definition.name));
3073
+ ].filter((tool) => this._isAllowedTool(tool.definition.name));
2484
3074
  const definitionRegistry = new Map(Array.from(this._baseToolDefinitions.entries())
2485
- .filter(([name]) => isAllowedTool(name))
3075
+ .filter(([name]) => this._isAllowedTool(name))
2486
3076
  .map(([name, definition]) => [
2487
3077
  name,
2488
3078
  {
2489
3079
  definition,
2490
- sourceInfo: createSyntheticSourceInfo(`<builtin:${name}>`, { source: "builtin" }),
3080
+ sourceInfo: createSyntheticSourceInfo(`${BUILTIN_PATH_PREFIX}${name}`, { source: "builtin" }),
2491
3081
  },
2492
3082
  ]));
2493
3083
  for (const tool of allCustomTools) {
@@ -2512,37 +3102,53 @@ export class AgentSession {
2512
3102
  const runner = this._extensionRunner;
2513
3103
  const wrappedExtensionTools = wrapRegisteredTools(allCustomTools, runner);
2514
3104
  const wrappedBuiltInTools = wrapRegisteredTools(Array.from(this._baseToolDefinitions.values())
2515
- .filter((definition) => isAllowedTool(definition.name))
3105
+ .filter((definition) => this._isAllowedTool(definition.name))
2516
3106
  .map((definition) => ({
2517
3107
  definition,
2518
- sourceInfo: createSyntheticSourceInfo(`<builtin:${definition.name}>`, { source: "builtin" }),
3108
+ sourceInfo: createSyntheticSourceInfo(`${BUILTIN_PATH_PREFIX}${definition.name}`, {
3109
+ source: "builtin",
3110
+ }),
2519
3111
  })), runner);
2520
3112
  const toolRegistry = new Map(wrappedBuiltInTools.map((tool) => [tool.name, tool]));
2521
3113
  for (const tool of wrappedExtensionTools) {
2522
3114
  toolRegistry.set(tool.name, tool);
2523
3115
  }
2524
3116
  this._toolRegistry = toolRegistry;
2525
- const nextActiveToolNames = (options?.activeToolNames ? [...options.activeToolNames] : [...previousActiveToolNames]).filter((name) => isAllowedTool(name));
2526
- if (allowedToolNames) {
3117
+ const nextActiveToolNames = (options?.activeToolNames ? [...options.activeToolNames] : [...previousActiveToolNames]).filter((name) => this._isAllowedTool(name));
3118
+ if (allowedTools) {
2527
3119
  for (const toolName of this._toolRegistry.keys()) {
2528
- if (allowedToolNames.has(toolName)) {
3120
+ // Naming or matching a tool activates it even when it is not active by default. MCP tools
3121
+ // kept registered without being named stay inactive.
3122
+ if (allowedTools(toolName) && this._isDeclarable(toolName)) {
2529
3123
  nextActiveToolNames.push(toolName);
2530
3124
  }
2531
3125
  }
2532
3126
  }
2533
3127
  else if (options?.includeAllExtensionTools) {
2534
3128
  for (const tool of wrappedExtensionTools) {
2535
- nextActiveToolNames.push(tool.name);
3129
+ if (this._isActivatedOnRegistration(tool.name))
3130
+ nextActiveToolNames.push(tool.name);
2536
3131
  }
2537
3132
  }
2538
3133
  else if (!options?.activeToolNames) {
2539
3134
  for (const toolName of this._toolRegistry.keys()) {
2540
- if (!previousRegistryNames.has(toolName)) {
3135
+ if (!previousActivatedOnRegistration.has(toolName) && this._isActivatedOnRegistration(toolName)) {
2541
3136
  nextActiveToolNames.push(toolName);
2542
3137
  }
2543
3138
  }
2544
3139
  }
2545
- this.setActiveToolsByName([...new Set(nextActiveToolNames)]);
3140
+ // Pending tools that are registered now become active.
3141
+ nextActiveToolNames.push(...this._pendingToolNames);
3142
+ this._setActiveTools([...new Set(nextActiveToolNames)]);
3143
+ }
3144
+ /** Whether activating the tool declares it to the model. */
3145
+ _isDeclarable(name) {
3146
+ const exposure = this._getToolExposure(name);
3147
+ return exposure === "direct" || exposure === "model-only";
3148
+ }
3149
+ /** Whether registering the tool activates it, which declares it to the model. */
3150
+ _isActivatedOnRegistration(name) {
3151
+ return this._isDeclarable(name) && this._toolDefinitions.get(name)?.definition.defaultActive !== false;
2546
3152
  }
2547
3153
  _buildRuntime(options) {
2548
3154
  const autoResizeImages = this.settingsManager.getImageAutoResize();
@@ -2581,15 +3187,28 @@ export class AgentSession {
2581
3187
  });
2582
3188
  }
2583
3189
  async reload(options) {
2584
- const previousFlagValues = this._extensionRunner.getFlagValues();
2585
- await emitSessionShutdownEvent(this._extensionRunner, { type: "session_shutdown", reason: "reload" });
3190
+ const oldRunner = this._extensionRunner;
3191
+ const previousFlagValues = oldRunner.getFlagValues();
3192
+ await emitSessionShutdownEvent(oldRunner, { type: "session_shutdown", reason: "reload" });
3193
+ oldRunner.invalidate();
3194
+ const getDefaultTools = () => this._usesDefaultTools
3195
+ ? applyToolModifiers(this.settingsManager.getDefaultTools() ?? DEFAULT_TOOL_NAMES, this._defaultToolModifiers)
3196
+ : [];
3197
+ const previousDefaultTools = new Set(getDefaultTools());
2586
3198
  await this.settingsManager.reload();
2587
3199
  this.syncQueueModesFromSettings();
2588
3200
  resetApiProviders();
2589
3201
  await this._resourceLoader.reload();
3202
+ // Activate tools newly added to defaultTools. Removed ones stay active, and tools disabled
3203
+ // during the session stay disabled unless the setting newly adds them.
3204
+ const addedDefaultTools = getDefaultTools().filter((name) => !previousDefaultTools.has(name));
3205
+ // Tools the new extensions register later, such as MCP tools, are pending until then.
3206
+ for (const name of this.getActiveToolNames())
3207
+ this._pendingToolNames.add(name);
2590
3208
  this._buildRuntime({
2591
- activeToolNames: this.getActiveToolNames(),
3209
+ activeToolNames: [...this.getActiveToolNames(), ...addedDefaultTools],
2592
3210
  flagValues: previousFlagValues,
3211
+ includeAllExtensionTools: true,
2593
3212
  });
2594
3213
  const hasBindings = this._extensionUIContext ||
2595
3214
  this._extensionCommandContextActions ||
@@ -2598,6 +3217,7 @@ export class AgentSession {
2598
3217
  if (hasBindings) {
2599
3218
  await options?.beforeSessionStart?.();
2600
3219
  await this._extensionRunner.emit({ type: "session_start", reason: "reload" });
3220
+ this._extensionRunner.reportUnhandledMcpServers();
2601
3221
  await this.extendResourcesFromExtensions("reload");
2602
3222
  }
2603
3223
  }
@@ -2610,7 +3230,7 @@ export class AgentSession {
2610
3230
  */
2611
3231
  _isRetryableError(message) {
2612
3232
  // Context overflow is handled by compaction, not retry.
2613
- if (isContextOverflow(message, this.model?.contextWindow ?? 0))
3233
+ if (isContextOverflow(message, (this._modelForMessage(message) ?? this.model)?.contextWindow ?? 0))
2614
3234
  return false;
2615
3235
  return isRetryableAssistantError(message);
2616
3236
  }
@@ -2677,11 +3297,8 @@ export class AgentSession {
2677
3297
  delayMs,
2678
3298
  errorMessage: message.errorMessage || "Unknown error",
2679
3299
  });
2680
- // Remove error message from agent state (keep in session for history)
2681
- const messages = this.agent.state.messages;
2682
- if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
2683
- this.agent.state.messages = messages.slice(0, -1);
2684
- }
3300
+ // Keep the failed attempt in raw history while durably omitting it from model projection.
3301
+ this._omitRecoveryAttempt(message);
2685
3302
  // Wait with exponential backoff (abortable)
2686
3303
  this._retryAbortController = new AbortController();
2687
3304
  try {
@@ -2773,10 +3390,8 @@ export class AgentSession {
2773
3390
  this._pendingBashMessages.push(bashMessage);
2774
3391
  }
2775
3392
  else {
2776
- // Add to agent state immediately
2777
- this.agent.state.messages.push(bashMessage);
2778
- // Save to session
2779
3393
  this.sessionManager.appendMessage(bashMessage);
3394
+ this._refreshFinalizedContext();
2780
3395
  }
2781
3396
  }
2782
3397
  /**
@@ -2803,12 +3418,10 @@ export class AgentSession {
2803
3418
  if (this._pendingBashMessages.length === 0)
2804
3419
  return;
2805
3420
  for (const bashMessage of this._pendingBashMessages) {
2806
- // Add to agent state
2807
- this.agent.state.messages.push(bashMessage);
2808
- // Save to session
2809
3421
  this.sessionManager.appendMessage(bashMessage);
2810
3422
  }
2811
3423
  this._pendingBashMessages = [];
3424
+ this._refreshFinalizedContext();
2812
3425
  }
2813
3426
  // =========================================================================
2814
3427
  // Session Management
@@ -2818,9 +3431,9 @@ export class AgentSession {
2818
3431
  */
2819
3432
  setSessionName(name) {
2820
3433
  this.sessionManager.appendSessionInfo(name);
2821
- const sessionName = this.sessionManager.getSessionName();
2822
- this._emit({ type: "session_info_changed", name: sessionName });
2823
- void this._extensionRunner.emit({ type: "session_info_changed", name: sessionName });
3434
+ const event = { type: "session_info_changed", name: this.sessionManager.getSessionName() };
3435
+ this._emit(event);
3436
+ void this._extensionRunner.emit(event);
2824
3437
  }
2825
3438
  // =========================================================================
2826
3439
  // Tree Navigation
@@ -2907,15 +3520,11 @@ export class AgentSession {
2907
3520
  let summaryDetails;
2908
3521
  let summaryUsage;
2909
3522
  if (options.summarize && entriesToSummarize.length > 0 && !extensionSummary) {
2910
- const model = this.model;
2911
- const { apiKey, headers, env } = await this._getSummarizationRequestAuth(model);
3523
+ const signal = this._branchSummaryAbortController.signal;
2912
3524
  const branchSummarySettings = this.settingsManager.getBranchSummarySettings();
2913
3525
  const result = await generateBranchSummary(entriesToSummarize, {
2914
- model,
2915
- apiKey,
2916
- headers,
2917
- env,
2918
- signal: this._branchSummaryAbortController.signal,
3526
+ ...(await this._getSummarizationRequestAuth(this.model, signal)),
3527
+ signal,
2919
3528
  customInstructions,
2920
3529
  replaceInstructions,
2921
3530
  reserveTokens: branchSummarySettings.reserveTokens,
@@ -2982,9 +3591,8 @@ export class AgentSession {
2982
3591
  if (label && !summaryText) {
2983
3592
  this.sessionManager.appendLabelChange(targetId, label);
2984
3593
  }
2985
- // Update agent state
2986
- const sessionContext = this.sessionManager.buildSessionContext();
2987
- this.agent.state.messages = sessionContext.messages;
3594
+ // Update finalized context from the canonical session projection.
3595
+ this._refreshFinalizedContext();
2988
3596
  this._restoreToolsFromTranscript();
2989
3597
  // Emit session_tree event (extension runners + RPC stdout subscribers)
2990
3598
  const treeEvent = {
@@ -3083,7 +3691,7 @@ export class AgentSession {
3083
3691
  };
3084
3692
  }
3085
3693
  getContextUsage() {
3086
- const model = this.model;
3694
+ const model = this._limitsModel();
3087
3695
  if (!model)
3088
3696
  return undefined;
3089
3697
  const contextWindow = model.contextWindow ?? 0;
@@ -3092,30 +3700,24 @@ export class AgentSession {
3092
3700
  // After compaction, the last assistant usage reflects pre-compaction context size.
3093
3701
  // We can only trust usage from an assistant that responded after the latest compaction.
3094
3702
  // If no such assistant exists, context token count is unknown until the next LLM response.
3095
- const branchEntries = this.sessionManager.getBranch();
3096
- const latestCompaction = getLatestCompactionEntry(branchEntries);
3703
+ const projection = this.sessionManager.buildSessionProjection();
3704
+ const branch = this.sessionManager.getBranch();
3705
+ const latestCompaction = getLatestCompactionEntry(branch);
3097
3706
  if (latestCompaction) {
3098
- // Check if there's a valid assistant usage after the compaction boundary
3099
- const compactionIndex = branchEntries.lastIndexOf(latestCompaction);
3100
- let hasPostCompactionUsage = false;
3101
- for (let i = branchEntries.length - 1; i > compactionIndex; i--) {
3102
- const entry = branchEntries[i];
3103
- if (entry.type === "message" && entry.message.role === "assistant") {
3104
- const assistant = entry.message;
3105
- if (assistant.stopReason !== "aborted" && assistant.stopReason !== "error") {
3106
- const contextTokens = calculateContextTokens(assistant.usage);
3107
- if (contextTokens > 0) {
3108
- hasPostCompactionUsage = true;
3109
- break;
3110
- }
3111
- }
3112
- }
3113
- }
3114
- if (!hasPostCompactionUsage) {
3707
+ const projectedAssistants = new Set(projection.entries.flatMap((entry) => entry.messages.some((message) => message.role === "assistant" &&
3708
+ message.stopReason !== "aborted" &&
3709
+ message.stopReason !== "error" &&
3710
+ calculateContextTokens(message.usage) > 0)
3711
+ ? [entry.sourceEntry.id]
3712
+ : []));
3713
+ const compactionIndex = branch.findIndex((entry) => entry.id === latestCompaction.id);
3714
+ const hasPostCompactionUsage = branch
3715
+ .slice(compactionIndex + 1)
3716
+ .some((entry) => projectedAssistants.has(entry.id));
3717
+ if (!hasPostCompactionUsage)
3115
3718
  return { tokens: null, contextWindow, percent: null };
3116
- }
3117
3719
  }
3118
- const estimate = estimateContextTokens(this.messages);
3720
+ const estimate = estimateProjectedContextTokens(projection, branch);
3119
3721
  const percent = (estimate.tokens / contextWindow) * 100;
3120
3722
  return {
3121
3723
  tokens: estimate.tokens,
@@ -3126,13 +3728,14 @@ export class AgentSession {
3126
3728
  /**
3127
3729
  * Export session to HTML.
3128
3730
  * @param outputPath Optional output path (defaults to session directory)
3731
+ * @param options Optional export presentation settings
3129
3732
  * @returns Path to exported file
3130
3733
  */
3131
3734
  async exportToHtml(outputPath, options = {}) {
3132
3735
  const themeName = [options.themeName, this.settingsManager.getTheme()].find((candidate) => candidate !== undefined && getThemeByName(candidate) !== undefined);
3133
3736
  // Create tool renderer if we have an extension runner (for custom tool HTML rendering)
3134
3737
  const toolRenderer = createToolHtmlRenderer({
3135
- getToolDefinition: (name) => this.getToolDefinition(name),
3738
+ getToolRenderers: (name) => this._extensionRunner.resolveToolRenderers(name, () => this.getToolDefinition(name)),
3136
3739
  theme,
3137
3740
  cwd: this.sessionManager.getCwd(),
3138
3741
  });
@@ -3149,29 +3752,7 @@ export class AgentSession {
3149
3752
  * @returns The resolved output file path.
3150
3753
  */
3151
3754
  exportToJsonl(outputPath) {
3152
- const filePath = resolvePath(outputPath ?? `session-${new Date().toISOString().replace(/[:.]/g, "-")}.jsonl`, process.cwd());
3153
- const dir = dirname(filePath);
3154
- if (!existsSync(dir)) {
3155
- mkdirSync(dir, { recursive: true });
3156
- }
3157
- const header = {
3158
- type: "session",
3159
- version: CURRENT_SESSION_VERSION,
3160
- id: this.sessionManager.getSessionId(),
3161
- timestamp: new Date().toISOString(),
3162
- cwd: this.sessionManager.getCwd(),
3163
- };
3164
- const branchEntries = this.sessionManager.getBranch();
3165
- const lines = [JSON.stringify(header)];
3166
- // Re-chain parentIds to form a linear sequence
3167
- let prevId = null;
3168
- for (const entry of branchEntries) {
3169
- const linear = { ...entry, parentId: prevId };
3170
- lines.push(JSON.stringify(linear));
3171
- prevId = entry.id;
3172
- }
3173
- writeFileSync(filePath, `${lines.join("\n")}\n`);
3174
- return filePath;
3755
+ return exportSessionToJsonl(this.sessionManager, outputPath);
3175
3756
  }
3176
3757
  /**
3177
3758
  * Ask the current model to describe what went wrong in this session for a bug report.
@@ -3182,16 +3763,11 @@ export class AgentSession {
3182
3763
  if (!model) {
3183
3764
  throw new Error("No model selected");
3184
3765
  }
3185
- const { model: requestModel, apiKey, headers, env } = await this._getSummarizationRequestAuth(model);
3186
3766
  return generateBugReportSummary({
3767
+ ...(await this._getSummarizationRequestAuth(model, options.signal)),
3187
3768
  messages: this.messages,
3188
3769
  hint: options.hint,
3189
- model: requestModel,
3190
- apiKey,
3191
- headers,
3192
- env,
3193
3770
  signal: options.signal,
3194
- thinkingLevel: this.thinkingLevel,
3195
3771
  streamFn: this.agent.streamFunction,
3196
3772
  retry: this.settingsManager.getRetrySettings(),
3197
3773
  sessionId: this.sessionId,