@oh-my-pi/pi-coding-agent 17.0.9 → 17.1.1

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 (349) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +1 -1
  3. package/dist/cli.js +4685 -4614
  4. package/dist/types/async/job-manager.d.ts +37 -1
  5. package/dist/types/cli/args.d.ts +2 -0
  6. package/dist/types/cli/bench-cli.d.ts +41 -0
  7. package/dist/types/commands/bench.d.ts +15 -3
  8. package/dist/types/commands/launch.d.ts +4 -0
  9. package/dist/types/config/model-registry.d.ts +3 -1
  10. package/dist/types/config/models-config-schema.d.ts +18 -6
  11. package/dist/types/config/models-config.d.ts +14 -5
  12. package/dist/types/config/provider-globals.d.ts +2 -2
  13. package/dist/types/config/service-tier.d.ts +5 -1
  14. package/dist/types/config/settings-schema.d.ts +374 -47
  15. package/dist/types/config/settings.d.ts +7 -0
  16. package/dist/types/dap/client.d.ts +20 -0
  17. package/dist/types/exec/bash-executor.d.ts +38 -0
  18. package/dist/types/exec/direnv.d.ts +33 -0
  19. package/dist/types/export/html/args.d.ts +15 -0
  20. package/dist/types/export/html/index.d.ts +11 -26
  21. package/dist/types/export/html/web-palette.d.ts +55 -116
  22. package/dist/types/extensibility/extensions/loader.d.ts +3 -0
  23. package/dist/types/extensibility/extensions/types.d.ts +17 -2
  24. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +3 -0
  25. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +13 -0
  26. package/dist/types/live/attestation.d.ts +2 -0
  27. package/dist/types/live/controller.d.ts +48 -0
  28. package/dist/types/live/protocol.d.ts +85 -0
  29. package/dist/types/live/protocol.test.d.ts +1 -0
  30. package/dist/types/live/transport.d.ts +35 -0
  31. package/dist/types/live/visualizer.d.ts +30 -0
  32. package/dist/types/lsp/client.d.ts +16 -1
  33. package/dist/types/mcp/config-writer.test.d.ts +1 -0
  34. package/dist/types/mcp/oauth-discovery.d.ts +2 -0
  35. package/dist/types/mcp/smithery-auth.d.ts +1 -1
  36. package/dist/types/mcp/smithery-auth.test.d.ts +1 -0
  37. package/dist/types/memory-backend/tool-names.d.ts +2 -0
  38. package/dist/types/modes/components/assistant-message.d.ts +1 -0
  39. package/dist/types/modes/components/custom-message.d.ts +1 -1
  40. package/dist/types/modes/components/login-dialog.d.ts +2 -2
  41. package/dist/types/modes/components/message-frame.d.ts +8 -4
  42. package/dist/types/modes/components/plan-review-overlay.d.ts +26 -0
  43. package/dist/types/modes/components/session-account-selector.d.ts +11 -0
  44. package/dist/types/modes/components/settings-defs.d.ts +7 -1
  45. package/dist/types/modes/components/status-line/component.jj-cache.test.d.ts +1 -0
  46. package/dist/types/modes/components/tool-execution.d.ts +9 -1
  47. package/dist/types/modes/controllers/command-controller.d.ts +1 -1
  48. package/dist/types/modes/controllers/event-controller.d.ts +6 -1
  49. package/dist/types/modes/controllers/live-command-controller.d.ts +14 -0
  50. package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
  51. package/dist/types/modes/interactive-mode.d.ts +6 -0
  52. package/dist/types/modes/theme/theme.d.ts +1 -1
  53. package/dist/types/modes/types.d.ts +4 -1
  54. package/dist/types/modes/utils/context-usage.d.ts +12 -2
  55. package/dist/types/plan-mode/plan-files.d.ts +10 -0
  56. package/dist/types/registry/agent-lifecycle.d.ts +21 -9
  57. package/dist/types/registry/agent-registry.d.ts +11 -4
  58. package/dist/types/sdk.d.ts +15 -1
  59. package/dist/types/secrets/index.d.ts +15 -2
  60. package/dist/types/secrets/obfuscator.d.ts +113 -15
  61. package/dist/types/session/acp-permission-gate.d.ts +19 -0
  62. package/dist/types/session/agent-session-events.d.ts +84 -0
  63. package/dist/types/session/agent-session-types.d.ts +321 -0
  64. package/dist/types/session/agent-session.d.ts +135 -753
  65. package/dist/types/session/async-job-delivery.d.ts +28 -0
  66. package/dist/types/session/auth-storage.d.ts +1 -1
  67. package/dist/types/session/bash-runner.d.ts +75 -0
  68. package/dist/types/session/blob-store.d.ts +11 -1
  69. package/dist/types/session/blob-store.test.d.ts +1 -0
  70. package/dist/types/session/checkpoint-entries.d.ts +28 -0
  71. package/dist/types/session/eval-runner.d.ts +51 -0
  72. package/dist/types/session/irc-bridge.d.ts +46 -0
  73. package/dist/types/session/messages.d.ts +24 -0
  74. package/dist/types/session/model-controls.d.ts +180 -0
  75. package/dist/types/session/prewalk.d.ts +46 -0
  76. package/dist/types/session/queued-messages.d.ts +22 -0
  77. package/dist/types/session/redis-session-storage.d.ts +1 -0
  78. package/dist/types/session/retry-fallback-chains.d.ts +74 -0
  79. package/dist/types/session/role-models.d.ts +15 -0
  80. package/dist/types/session/session-advisors.d.ts +228 -0
  81. package/dist/types/session/session-entries.d.ts +8 -0
  82. package/dist/types/session/session-handoff.d.ts +70 -0
  83. package/dist/types/session/session-history-format.d.ts +6 -1
  84. package/dist/types/session/session-maintenance.d.ts +246 -0
  85. package/dist/types/session/session-manager.d.ts +41 -0
  86. package/dist/types/session/session-memory.d.ts +56 -0
  87. package/dist/types/session/session-provider-boundary.d.ts +62 -0
  88. package/dist/types/session/session-stats.d.ts +48 -0
  89. package/dist/types/session/session-tools.d.ts +158 -0
  90. package/dist/types/session/session-workspace.d.ts +32 -0
  91. package/dist/types/session/stream-guards.d.ts +44 -0
  92. package/dist/types/session/streaming-output.d.ts +8 -0
  93. package/dist/types/session/todo-tracker.d.ts +56 -0
  94. package/dist/types/session/ttsr-coordinator.d.ts +51 -0
  95. package/dist/types/session/turn-recovery.d.ts +225 -0
  96. package/dist/types/slash-commands/helpers/session-pin.d.ts +9 -0
  97. package/dist/types/stt/index.d.ts +0 -2
  98. package/dist/types/stt/stt-controller.d.ts +7 -0
  99. package/dist/types/system-prompt.d.ts +2 -0
  100. package/dist/types/task/executor.d.ts +4 -1
  101. package/dist/types/task/output-manager.d.ts +3 -1
  102. package/dist/types/tiny/title-client.d.ts +10 -0
  103. package/dist/types/tools/approval.d.ts +1 -0
  104. package/dist/types/tools/browser/launch.d.ts +5 -0
  105. package/dist/types/tools/builtin-names.d.ts +1 -1
  106. package/dist/types/tools/computer/protocol.d.ts +43 -0
  107. package/dist/types/tools/computer/supervisor.d.ts +32 -0
  108. package/dist/types/tools/computer/worker-entry.d.ts +1 -0
  109. package/dist/types/tools/computer/worker.d.ts +15 -0
  110. package/dist/types/tools/computer-renderer.d.ts +22 -0
  111. package/dist/types/tools/computer.d.ts +71 -0
  112. package/dist/types/tools/context.d.ts +2 -0
  113. package/dist/types/tools/default-renderer.d.ts +21 -0
  114. package/dist/types/tools/image-gen.d.ts +4 -4
  115. package/dist/types/tools/image-providers.d.ts +38 -0
  116. package/dist/types/tools/index.d.ts +7 -0
  117. package/dist/types/tools/path-utils.d.ts +8 -0
  118. package/dist/types/tools/todo.d.ts +10 -4
  119. package/dist/types/tools/xdev.d.ts +5 -1
  120. package/dist/types/tts/streaming-player.d.ts +10 -29
  121. package/dist/types/tts/vocalizer.d.ts +5 -0
  122. package/dist/types/utils/jj.d.ts +29 -0
  123. package/dist/types/utils/lang-from-path.d.ts +1 -0
  124. package/dist/types/utils/title-generator.d.ts +30 -0
  125. package/dist/types/utils/tools-manager.d.ts +1 -2
  126. package/dist/types/vibe/runtime.d.ts +36 -8
  127. package/dist/types/web/search/index.d.ts +1 -1
  128. package/dist/types/web/search/provider.d.ts +15 -5
  129. package/dist/types/web/search/types.d.ts +94 -0
  130. package/package.json +12 -12
  131. package/scripts/generate-share-viewer.ts +4 -6
  132. package/src/advisor/__tests__/advisor.test.ts +643 -0
  133. package/src/advisor/runtime.ts +144 -43
  134. package/src/async/job-manager.ts +90 -2
  135. package/src/cli/args.ts +3 -0
  136. package/src/cli/auth-gateway-cli.ts +18 -3
  137. package/src/cli/bench-cli.ts +395 -29
  138. package/src/cli/flag-tables.ts +3 -0
  139. package/src/cli/gc-cli.ts +2 -2
  140. package/src/cli/setup-cli.ts +2 -14
  141. package/src/cli/usage-cli.ts +10 -1
  142. package/src/cli.ts +8 -0
  143. package/src/collab/host.ts +3 -2
  144. package/src/commands/bench.ts +18 -4
  145. package/src/commands/launch.ts +4 -0
  146. package/src/config/model-registry.ts +16 -2
  147. package/src/config/models-config-schema.ts +15 -4
  148. package/src/config/provider-globals.ts +9 -9
  149. package/src/config/service-tier.ts +26 -1
  150. package/src/config/settings-schema.ts +270 -51
  151. package/src/config/settings.ts +81 -2
  152. package/src/dap/client.ts +37 -4
  153. package/src/edit/index.ts +49 -12
  154. package/src/eval/executor-base.ts +1 -0
  155. package/src/eval/js/executor.ts +2 -0
  156. package/src/eval/py/executor.ts +103 -33
  157. package/src/exec/bash-executor.ts +90 -6
  158. package/src/exec/direnv.ts +145 -0
  159. package/src/export/html/args.ts +20 -0
  160. package/src/export/html/index.ts +45 -47
  161. package/src/export/html/template.css +19 -1
  162. package/src/export/html/template.html +6 -0
  163. package/src/export/html/template.js +21 -0
  164. package/src/export/html/tool-views.generated.js +31 -31
  165. package/src/export/html/web-palette.ts +116 -132
  166. package/src/export/share.ts +249 -49
  167. package/src/extensibility/extensions/loader.ts +29 -1
  168. package/src/extensibility/extensions/runner.ts +6 -0
  169. package/src/extensibility/extensions/types.ts +33 -2
  170. package/src/extensibility/extensions/wrapper.ts +68 -12
  171. package/src/extensibility/legacy-pi-coding-agent-shim.ts +7 -1
  172. package/src/extensibility/plugins/legacy-pi-compat.ts +19 -1
  173. package/src/hindsight/state.ts +64 -6
  174. package/src/internal-urls/local-protocol.ts +2 -1
  175. package/src/internal-urls/memory-protocol.ts +2 -2
  176. package/src/internal-urls/skill-protocol.ts +2 -2
  177. package/src/internal-urls/ssh-protocol.ts +2 -1
  178. package/src/internal-urls/vault-protocol.ts +2 -1
  179. package/src/live/attestation.ts +91 -0
  180. package/src/live/controller.ts +517 -0
  181. package/src/live/prompts/agent-final-message.md +3 -0
  182. package/src/live/prompts/live-instructions.md +23 -0
  183. package/src/live/protocol.test.ts +140 -0
  184. package/src/live/protocol.ts +233 -0
  185. package/src/live/transport.ts +422 -0
  186. package/src/live/visualizer.ts +214 -0
  187. package/src/lsp/client.ts +29 -9
  188. package/src/lsp/index.ts +19 -4
  189. package/src/main.ts +8 -1
  190. package/src/mcp/config-writer.test.ts +43 -0
  191. package/src/mcp/config-writer.ts +109 -82
  192. package/src/mcp/oauth-discovery.ts +10 -2
  193. package/src/mcp/smithery-auth.test.ts +29 -0
  194. package/src/mcp/smithery-auth.ts +3 -2
  195. package/src/memories/index.ts +40 -20
  196. package/src/memory-backend/tool-names.ts +2 -0
  197. package/src/mnemopi/backend.ts +24 -7
  198. package/src/modes/acp/acp-agent.ts +56 -10
  199. package/src/modes/acp/acp-event-mapper.ts +8 -1
  200. package/src/modes/components/agent-hub.ts +1 -1
  201. package/src/modes/components/assistant-message.ts +7 -2
  202. package/src/modes/components/custom-message.ts +4 -1
  203. package/src/modes/components/login-dialog.ts +14 -4
  204. package/src/modes/components/message-frame.ts +14 -8
  205. package/src/modes/components/plan-review-overlay.ts +350 -35
  206. package/src/modes/components/session-account-selector.ts +62 -0
  207. package/src/modes/components/settings-defs.ts +23 -1
  208. package/src/modes/components/settings-selector.ts +169 -1
  209. package/src/modes/components/status-line/component.jj-cache.test.ts +229 -0
  210. package/src/modes/components/status-line/component.ts +123 -2
  211. package/src/modes/components/tool-execution.ts +28 -112
  212. package/src/modes/controllers/command-controller.ts +56 -15
  213. package/src/modes/controllers/event-controller.ts +111 -16
  214. package/src/modes/controllers/extension-ui-controller.test.ts +1 -0
  215. package/src/modes/controllers/extension-ui-controller.ts +20 -4
  216. package/src/modes/controllers/input-controller.ts +47 -39
  217. package/src/modes/controllers/live-command-controller.ts +255 -0
  218. package/src/modes/controllers/mcp-command-controller.ts +10 -1
  219. package/src/modes/controllers/omfg-controller.ts +44 -40
  220. package/src/modes/controllers/selector-controller.ts +80 -10
  221. package/src/modes/controllers/session-focus-controller.ts +6 -1
  222. package/src/modes/interactive-mode.ts +176 -38
  223. package/src/modes/runtime-init.ts +2 -0
  224. package/src/modes/setup-wizard/scenes/web-search.ts +17 -9
  225. package/src/modes/theme/theme.ts +1 -1
  226. package/src/modes/types.ts +4 -1
  227. package/src/modes/utils/context-usage.ts +23 -7
  228. package/src/modes/utils/ui-helpers.ts +10 -5
  229. package/src/plan-mode/plan-files.ts +40 -0
  230. package/src/prompts/bench/cache-prefix-chunk.md +1 -0
  231. package/src/prompts/bench/cache-prefix.md +3 -0
  232. package/src/prompts/bench/cache-suffix.md +1 -0
  233. package/src/prompts/system/computer-safety.md +14 -0
  234. package/src/prompts/system/custom-system-prompt.md +1 -1
  235. package/src/prompts/system/project-prompt.md +8 -1
  236. package/src/prompts/system/subagent-async-pending.md +6 -0
  237. package/src/prompts/system/system-prompt.md +1 -1
  238. package/src/prompts/system/workflow-notice.md +48 -40
  239. package/src/prompts/system/xdev-mount-notice.md +4 -0
  240. package/src/prompts/tools/computer.md +26 -0
  241. package/src/prompts/tools/todo.md +3 -1
  242. package/src/registry/agent-lifecycle.ts +116 -46
  243. package/src/registry/agent-registry.ts +35 -8
  244. package/src/registry/persisted-agents.ts +26 -2
  245. package/src/sdk.ts +215 -136
  246. package/src/secrets/index.ts +127 -4
  247. package/src/secrets/obfuscator.ts +2328 -105
  248. package/src/session/acp-permission-gate.ts +165 -0
  249. package/src/session/agent-session-error-log.test.ts +1 -1
  250. package/src/session/agent-session-events.ts +66 -0
  251. package/src/session/agent-session-types.ts +343 -0
  252. package/src/session/agent-session.ts +1940 -12218
  253. package/src/session/artifacts.ts +6 -5
  254. package/src/session/async-job-delivery.ts +74 -0
  255. package/src/session/auth-storage.ts +1 -0
  256. package/src/session/bash-runner.ts +326 -0
  257. package/src/session/blob-store.test.ts +56 -0
  258. package/src/session/blob-store.ts +18 -2
  259. package/src/session/checkpoint-entries.ts +81 -0
  260. package/src/session/eval-runner.ts +217 -0
  261. package/src/session/indexed-session-storage.ts +9 -2
  262. package/src/session/irc-bridge.ts +203 -0
  263. package/src/session/messages.ts +266 -1
  264. package/src/session/model-controls.ts +714 -0
  265. package/src/session/prewalk.ts +252 -0
  266. package/src/session/queued-messages.ts +93 -0
  267. package/src/session/redis-session-storage.ts +51 -11
  268. package/src/session/retry-fallback-chains.ts +455 -0
  269. package/src/session/role-models.ts +85 -0
  270. package/src/session/session-advisors.ts +1679 -0
  271. package/src/session/session-context.ts +6 -3
  272. package/src/session/session-entries.ts +8 -0
  273. package/src/session/session-handoff.ts +308 -0
  274. package/src/session/session-history-format.ts +20 -9
  275. package/src/session/session-loader.ts +0 -46
  276. package/src/session/session-maintenance.ts +2983 -0
  277. package/src/session/session-manager.ts +390 -30
  278. package/src/session/session-memory.ts +222 -0
  279. package/src/session/session-provider-boundary.ts +307 -0
  280. package/src/session/session-stats.ts +293 -0
  281. package/src/session/session-tools.ts +980 -0
  282. package/src/session/session-workspace.ts +53 -0
  283. package/src/session/stream-guards.ts +417 -0
  284. package/src/session/streaming-output.ts +52 -5
  285. package/src/session/todo-tracker.ts +380 -0
  286. package/src/session/ttsr-coordinator.ts +496 -0
  287. package/src/session/turn-recovery.ts +1629 -0
  288. package/src/slash-commands/builtin-registry.ts +257 -20
  289. package/src/slash-commands/helpers/session-pin.ts +44 -0
  290. package/src/slash-commands/helpers/usage-report.ts +22 -3
  291. package/src/stt/downloader.ts +0 -2
  292. package/src/stt/index.ts +0 -2
  293. package/src/stt/stt-controller.ts +57 -146
  294. package/src/system-prompt.ts +45 -17
  295. package/src/task/executor.ts +285 -70
  296. package/src/task/index.ts +98 -105
  297. package/src/task/output-manager.ts +25 -3
  298. package/src/task/persisted-revive.ts +4 -3
  299. package/src/task/structured-subagent.ts +1 -0
  300. package/src/tiny/title-client.ts +22 -0
  301. package/src/tools/approval.ts +55 -10
  302. package/src/tools/bash-interactive.ts +116 -86
  303. package/src/tools/bash-skill-urls.ts +28 -2
  304. package/src/tools/bash.ts +339 -123
  305. package/src/tools/browser/launch.ts +9 -1
  306. package/src/tools/browser/tab-supervisor.ts +8 -5
  307. package/src/tools/browser.ts +94 -47
  308. package/src/tools/builtin-names.ts +1 -0
  309. package/src/tools/computer/protocol.ts +28 -0
  310. package/src/tools/computer/supervisor.ts +258 -0
  311. package/src/tools/computer/worker-entry.ts +25 -0
  312. package/src/tools/computer/worker.ts +135 -0
  313. package/src/tools/computer-renderer.ts +108 -0
  314. package/src/tools/computer.ts +433 -0
  315. package/src/tools/context.ts +2 -0
  316. package/src/tools/default-renderer.ts +139 -0
  317. package/src/tools/essential-tools.ts +1 -0
  318. package/src/tools/image-gen.ts +20 -28
  319. package/src/tools/image-providers.ts +50 -0
  320. package/src/tools/index.ts +14 -0
  321. package/src/tools/path-utils.ts +49 -2
  322. package/src/tools/read.ts +156 -88
  323. package/src/tools/renderers.ts +9 -1
  324. package/src/tools/todo.ts +101 -11
  325. package/src/tools/vibe.ts +1 -2
  326. package/src/tools/write.ts +51 -1
  327. package/src/tools/xdev.ts +121 -29
  328. package/src/tts/streaming-player.ts +82 -283
  329. package/src/tts/vocalizer.ts +18 -1
  330. package/src/utils/clipboard.ts +1 -30
  331. package/src/utils/jj.ts +125 -4
  332. package/src/utils/lang-from-path.ts +7 -0
  333. package/src/utils/mac-file-urls.applescript +37 -0
  334. package/src/utils/title-generator.ts +137 -1
  335. package/src/utils/tool-choice.ts +14 -0
  336. package/src/utils/tools-manager.ts +1 -19
  337. package/src/vibe/runtime.ts +852 -72
  338. package/src/web/search/index.ts +3 -6
  339. package/src/web/search/provider.ts +31 -16
  340. package/src/web/search/providers/xai.ts +7 -1
  341. package/src/web/search/types.ts +3 -0
  342. package/dist/types/stt/recorder.d.ts +0 -30
  343. package/dist/types/stt/transcriber.d.ts +0 -14
  344. package/dist/types/stt/wav.d.ts +0 -29
  345. package/dist/types/tts/player.d.ts +0 -32
  346. package/src/stt/recorder.ts +0 -551
  347. package/src/stt/transcriber.ts +0 -60
  348. package/src/stt/wav.ts +0 -173
  349. package/src/tts/player.ts +0 -137
package/src/sdk.ts CHANGED
@@ -14,6 +14,7 @@ import type {
14
14
  CredentialDisabledEvent,
15
15
  Message,
16
16
  Model,
17
+ ModelUsageHealth,
17
18
  ProviderSessionState,
18
19
  SimpleStreamOptions,
19
20
  } from "@oh-my-pi/pi-ai";
@@ -32,7 +33,7 @@ import {
32
33
  formatActiveRepoWatchdogPrompt,
33
34
  formatAdvisorContextPrompt,
34
35
  } from "./advisor";
35
- import { type AsyncJob, AsyncJobManager } from "./async";
36
+ import { AsyncJobManager } from "./async";
36
37
  import { AutoLearnController, buildAutoLearnInstructions } from "./autolearn/controller";
37
38
  import { createAutoresearchExtension } from "./autoresearch";
38
39
  import { loadCapability } from "./capability";
@@ -55,6 +56,7 @@ import {
55
56
  resolveModelRoleValue,
56
57
  } from "./config/model-resolver";
57
58
  import { loadPromptTemplates as loadPromptTemplatesInternal, type PromptTemplate } from "./config/prompt-templates";
59
+ import { applyProviderGlobalsFromSettings } from "./config/provider-globals";
58
60
  import { buildServiceTierByFamily } from "./config/service-tier";
59
61
  import { Settings, type SkillsSettings } from "./config/settings";
60
62
  import { CursorExecHandlers } from "./cursor";
@@ -105,19 +107,22 @@ import {
105
107
  } from "./mcp";
106
108
  import { MCP_CONNECTION_STATUS_EVENT_CHANNEL, type McpConnectionStatusEvent } from "./mcp/startup-events";
107
109
  import { createSessionMemoryRuntimeContext, resolveMemoryBackend } from "./memory-backend";
110
+ import { MEMORY_BACKEND_TOOL_NAMES } from "./memory-backend/tool-names";
108
111
  import type { MnemopiSessionState } from "./mnemopi/state";
109
- import asyncResultTemplate from "./prompts/tools/async-result.md" with { type: "text" };
110
112
  import lateDiagnosticTemplate from "./prompts/tools/lsp-late-diagnostic.md" with { type: "text" };
111
113
  import { AgentLifecycleManager } from "./registry/agent-lifecycle";
112
- import { AgentRegistry, MAIN_AGENT_ID } from "./registry/agent-registry";
114
+ import { type AgentRef, AgentRegistry, MAIN_AGENT_ID } from "./registry/agent-registry";
113
115
  import {
114
116
  collectEnvSecrets,
115
117
  deobfuscateSessionContext,
116
118
  deobfuscateToolArguments,
119
+ getExistingSecretPlaceholderKey,
120
+ getSecretPlaceholderKey,
117
121
  loadSecrets,
118
122
  obfuscateMessages,
119
123
  obfuscateProviderContext,
120
124
  SecretObfuscator,
125
+ secretEntriesNeedPlaceholderKey,
121
126
  } from "./secrets";
122
127
  import { AgentSession, type InitialRetryFallbackState, type PlanYolo, type Prewalk } from "./session/agent-session";
123
128
  import { discoverAuthStorage as discoverAuthStorageFromConfig } from "./session/auth-broker-config";
@@ -132,6 +137,12 @@ import {
132
137
  wrapSteeringForModel,
133
138
  } from "./session/messages";
134
139
  import { clampProviderContextImages } from "./session/provider-image-budget";
140
+ import {
141
+ expandDefaultRetryFallbackChains,
142
+ findRetryFallbackCandidates,
143
+ type RetryFallbackResolutionContext,
144
+ resolveRetryFallbackChainKey,
145
+ } from "./session/retry-fallback-chains";
135
146
  import { getRestorableSessionModels } from "./session/session-context";
136
147
  import { SessionManager } from "./session/session-manager";
137
148
  import { createSettingsAwareStreamFn } from "./session/settings-stream-fn";
@@ -173,15 +184,10 @@ import {
173
184
  GrepTool,
174
185
  getSearchTools,
175
186
  HIDDEN_TOOLS,
176
- isImageProviderPreference,
177
187
  isMountableUnderXdev,
178
- isSearchProviderId,
179
- isSearchProviderPreference,
180
188
  type LspStartupServerInfo,
181
189
  ReadTool,
182
- setExcludedSearchProviders,
183
- setPreferredImageProvider,
184
- setPreferredSearchProvider,
190
+ releaseComputerSessionsForOwner,
185
191
  type Tool,
186
192
  type ToolSession,
187
193
  WebSearchTool,
@@ -199,62 +205,14 @@ import { ttsTool } from "./tools/tts";
199
205
  import { resolveActiveRepoContext } from "./utils/active-repo-context";
200
206
  import { EventBus } from "./utils/event-bus";
201
207
  import { buildNamedToolChoice } from "./utils/tool-choice";
208
+ import { VibeSessionRegistry } from "./vibe/runtime";
202
209
  import { buildWorkspaceTree, type WorkspaceTree } from "./workspace-tree";
203
210
 
204
- type AsyncResultEntry = {
205
- jobId: string;
206
- result: string;
207
- job: AsyncJob | undefined;
208
- durationMs: number | undefined;
209
- };
210
-
211
- type AsyncResultJobDetails = {
212
- jobId: string;
213
- type?: "bash" | "task";
214
- label?: string;
215
- durationMs?: number;
216
- };
217
-
218
- type AsyncResultDetails = {
219
- jobs: AsyncResultJobDetails[];
220
- };
221
-
222
211
  type McpNotificationEntry = {
223
212
  serverName: string;
224
213
  uri: string;
225
214
  };
226
215
 
227
- function buildAsyncResultBatchMessage(entries: AsyncResultEntry[]): CustomMessage<AsyncResultDetails> | null {
228
- if (entries.length === 0) return null;
229
- const jobs = entries.map(entry => ({
230
- jobId: entry.jobId,
231
- result: entry.result,
232
- type: entry.job?.type,
233
- label: entry.job?.label,
234
- durationMs: entry.durationMs,
235
- }));
236
- const details: AsyncResultDetails = {
237
- jobs: jobs.map(job => ({
238
- jobId: job.jobId,
239
- type: job.type,
240
- label: job.label,
241
- durationMs: job.durationMs,
242
- })),
243
- };
244
- return {
245
- role: "custom",
246
- customType: "async-result",
247
- content: prompt.render(asyncResultTemplate, {
248
- multiple: jobs.length > 1,
249
- jobs,
250
- }),
251
- display: true,
252
- attribution: "agent",
253
- details,
254
- timestamp: Date.now(),
255
- };
256
- }
257
-
258
216
  type LateDiagnosticsDetails = {
259
217
  files: Array<{ path: string; summary: string; errored: boolean; messages: string[] }>;
260
218
  };
@@ -371,6 +329,8 @@ function applyMCPEnvironment(result: { exaApiKeys: string[] }): void {
371
329
  export interface CreateAgentSessionOptions {
372
330
  /** Working directory for project-local discovery. Default: getProjectDir() */
373
331
  cwd?: string;
332
+ /** Additional workspace directories beyond cwd (multi-root), absolute or cwd-relative. */
333
+ additionalDirectories?: string[];
374
334
  /** Global config directory. Default: ~/.omp/agent */
375
335
  agentDir?: string;
376
336
  /** Spawns to allow. Default: "*" */
@@ -525,6 +485,13 @@ export interface CreateAgentSessionOptions {
525
485
  agentDisplayName?: string;
526
486
  /** Optional shared agent registry for IRC routing. Default: AgentRegistry.global(). */
527
487
  agentRegistry?: AgentRegistry;
488
+ /**
489
+ * Registry generation authorized for this creation. `null` requires the id
490
+ * to be absent; an AgentRef allows a parked revival to reuse only that ref.
491
+ * Undefined preserves legacy unconditional registration for external SDK callers.
492
+ * @internal
493
+ */
494
+ expectedAgentRef?: AgentRef | null;
528
495
  /** Parent task ID prefix for nested artifact naming (e.g., "Extensions") */
529
496
  parentTaskPrefix?: string;
530
497
  /**
@@ -554,6 +521,11 @@ export interface CreateAgentSessionOptions {
554
521
 
555
522
  /** Whether UI is available (enables interactive tools like ask). Default: false */
556
523
  hasUI?: boolean;
524
+ /**
525
+ * Defer `confirm` reserve-policy fallback until AgentSession prompt-time UI is configured.
526
+ * ACP uses this while capabilities are negotiated without enabling UI-only tools.
527
+ */
528
+ deferUsageReserveConfirmation?: boolean;
557
529
 
558
530
  /**
559
531
  * Opt-in OpenTelemetry instrumentation forwarded to the underlying Agent.
@@ -1292,26 +1264,22 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1292
1264
  discoveredSkillsPromise?.catch(() => {});
1293
1265
 
1294
1266
  // Initialize provider preferences from settings
1295
- const excludedWebSearchProviders = settings.get("providers.webSearchExclude");
1296
- if (Array.isArray(excludedWebSearchProviders)) {
1297
- setExcludedSearchProviders(excludedWebSearchProviders.filter(isSearchProviderId));
1298
- }
1299
-
1300
- const webSearchProvider = settings.get("providers.webSearch");
1301
- if (typeof webSearchProvider === "string" && isSearchProviderPreference(webSearchProvider)) {
1302
- setPreferredSearchProvider(webSearchProvider);
1303
- }
1304
-
1305
- const imageProvider = settings.get("providers.image");
1306
- if (isImageProviderPreference(imageProvider)) {
1307
- setPreferredImageProvider(imageProvider);
1308
- }
1267
+ applyProviderGlobalsFromSettings(settings);
1309
1268
 
1310
1269
  const sessionManager =
1311
1270
  options.sessionManager ??
1312
1271
  logger.time("sessionManager", () =>
1313
1272
  SessionManager.create(cwd, SessionManager.getDefaultSessionDir(cwd, agentDir)),
1314
1273
  );
1274
+ const configuredDirs = options.additionalDirectories
1275
+ ? options.additionalDirectories
1276
+ : settings.get("workspace.additionalDirectories");
1277
+ if (configuredDirs.length > 0) {
1278
+ // Merge with any roots restored from the session header (resume/fork), not replace.
1279
+ const existing = sessionManager.getAdditionalDirectories();
1280
+ const merged = [...new Set([...existing, ...configuredDirs])];
1281
+ await sessionManager.setAdditionalDirectories(merged);
1282
+ }
1315
1283
  const providerSessionId = options.providerSessionId ?? sessionManager.getSessionId();
1316
1284
  const forkCacheShapeChanged =
1317
1285
  options.model !== undefined ||
@@ -1349,8 +1317,30 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1349
1317
  const fileEntries = await logger.time("loadSecrets", loadSecrets, cwd, agentDir);
1350
1318
  const envEntries = collectEnvSecrets();
1351
1319
  const allEntries = [...envEntries, ...fileEntries];
1320
+ const needsPlaceholderKey = secretEntriesNeedPlaceholderKey(allEntries);
1321
+ const placeholderKey = needsPlaceholderKey
1322
+ ? await getSecretPlaceholderKey(agentDir)
1323
+ : await getExistingSecretPlaceholderKey(agentDir);
1352
1324
  if (allEntries.length > 0) {
1353
- obfuscator = new SecretObfuscator(allEntries);
1325
+ // The persisted placeholder key — and creating its key file under the
1326
+ // configured agentDir — is only needed for reversible obfuscate-mode
1327
+ // placeholders, or for a default (no custom `replacement`) replace-mode
1328
+ // regex whose key-derived idempotent fallback marker needs a stable key
1329
+ // across restarts (see `secretEntryNeedsPlaceholderKey`). A replace-only
1330
+ // secrets set with no such regex must not require the key; otherwise a
1331
+ // headless run with an unwritable default config root fails startup for a
1332
+ // feature it does not use.
1333
+ obfuscator = new SecretObfuscator(allEntries, placeholderKey);
1334
+ }
1335
+ if (obfuscator?.hasSecrets() !== true && placeholderKey !== undefined) {
1336
+ // No configured entry produced an active secret (e.g. only ignored short
1337
+ // plain entries, or no entries at all), but a persisted key exists. Build a
1338
+ // redaction-only obfuscator so a tool read of the key file does not ship the
1339
+ // reusable HMAC key to the provider.
1340
+ obfuscator = new SecretObfuscator(
1341
+ [{ type: "plain", mode: "replace", content: placeholderKey }],
1342
+ placeholderKey,
1343
+ );
1354
1344
  }
1355
1345
  }
1356
1346
  const secretsEnabled = obfuscator?.hasSecrets() === true;
@@ -1566,28 +1556,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1566
1556
  const restrictToolNames = options.restrictToolNames === true;
1567
1557
  const enableLsp = !restrictToolNames && (options.enableLsp ?? true);
1568
1558
  const asyncMaxJobs = Math.min(100, Math.max(1, settings.get("async.maxJobs") ?? 100));
1569
- const ASYNC_INLINE_RESULT_MAX_CHARS = 12_000;
1570
- const ASYNC_PREVIEW_MAX_CHARS = 4_000;
1571
- const formatAsyncResultForFollowUp = async (result: string): Promise<string> => {
1572
- if (result.length <= ASYNC_INLINE_RESULT_MAX_CHARS) {
1573
- return result;
1574
- }
1575
-
1576
- const preview = `${result.slice(0, ASYNC_PREVIEW_MAX_CHARS)}\n\n[Output truncated. Showing first ${ASYNC_PREVIEW_MAX_CHARS.toLocaleString()} characters.]`;
1577
- try {
1578
- const { path: artifactPath, id: artifactId } = await sessionManager.allocateArtifactPath("async");
1579
- if (artifactPath && artifactId) {
1580
- await Bun.write(artifactPath, result);
1581
- return `${preview}\nFull output: artifact://${artifactId}`;
1582
- }
1583
- } catch (error) {
1584
- logger.warn("Failed to persist async follow-up artifact", {
1585
- error: error instanceof Error ? error.message : String(error),
1586
- });
1587
- }
1588
-
1589
- return preview;
1590
- };
1591
1559
  // Only the first top-level session in a process owns an AsyncJobManager.
1592
1560
  // Subagents inherit the parent's manager via `AsyncJobManager.instance()`
1593
1561
  // (set below), and any additional top-level session spun up in-process
@@ -1596,24 +1564,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1596
1564
  // owning session's manager and break the `task`/`bash` async paths
1597
1565
  // (issue #1923). The `instance()` guard means later sessions also skip
1598
1566
  // constructing an orphaned manager that nothing would ever route to.
1567
+ // Delivery is owner-routed: every AgentSession registers its own sink
1568
+ // (see session/async-job-delivery.ts), so the manager takes no default
1569
+ // onJobComplete here.
1599
1570
  const asyncJobManager =
1600
1571
  !options.parentTaskPrefix && !AsyncJobManager.instance()
1601
- ? new AsyncJobManager({
1602
- maxRunningJobs: asyncMaxJobs,
1603
- onJobComplete: async (jobId, result, job) => {
1604
- if (!session || asyncJobManager!.isDeliverySuppressed(jobId)) return;
1605
- const formattedResult = await formatAsyncResultForFollowUp(result);
1606
- if (asyncJobManager!.isDeliverySuppressed(jobId)) return;
1607
-
1608
- const durationMs = job ? Math.max(0, Date.now() - job.startTime) : undefined;
1609
- session.yieldQueue.enqueue<AsyncResultEntry>("async-result", {
1610
- jobId,
1611
- result: formattedResult,
1612
- job,
1613
- durationMs,
1614
- });
1615
- },
1616
- })
1572
+ ? new AsyncJobManager({ maxRunningJobs: asyncMaxJobs })
1617
1573
  : undefined;
1618
1574
 
1619
1575
  const scopedAsyncJobManager = asyncJobManager ?? (options.parentTaskPrefix ? AsyncJobManager.instance() : undefined);
@@ -1623,15 +1579,18 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1623
1579
  const resolvedAgentDisplayName =
1624
1580
  options.agentDisplayName ?? ((options.taskDepth ?? 0) > 0 || options.parentTaskPrefix ? "sub" : "main");
1625
1581
  const agentKind = (options.taskDepth ?? 0) > 0 || options.parentTaskPrefix ? ("sub" as const) : ("main" as const);
1582
+ let registeredAgentRef: AgentRef | undefined;
1626
1583
  /**
1627
1584
  * Forget the agent ref on teardown — unless the agent is being parked (or is
1628
1585
  * already parked). Parking disposes the session but keeps the ref addressable
1629
1586
  * (history://, revive); only process teardown / explicit kill unregisters.
1630
1587
  */
1631
1588
  const unregisterUnlessParked = (): void => {
1632
- if (agentRegistry.get(resolvedAgentId)?.status === "parked") return;
1633
- if (AgentLifecycleManager.global().isParking(resolvedAgentId)) return;
1634
- agentRegistry.unregister(resolvedAgentId);
1589
+ const ref = registeredAgentRef;
1590
+ if (!ref || agentRegistry.get(resolvedAgentId) !== ref) return;
1591
+ if (ref.status === "parked") return;
1592
+ if (AgentLifecycleManager.global().isParking(resolvedAgentId, ref)) return;
1593
+ agentRegistry.unregister(resolvedAgentId, ref);
1635
1594
  };
1636
1595
  const evalKernelOwnerId = `agent-session:${Snowflake.next()}`;
1637
1596
 
@@ -1660,6 +1619,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1660
1619
  isToolActive: name => activeToolNames.has(name),
1661
1620
  setActiveToolNames,
1662
1621
  hasUI: options.hasUI ?? false,
1622
+ get additionalDirectories() {
1623
+ return sessionManager.getAdditionalDirectories();
1624
+ },
1663
1625
  enableLsp,
1664
1626
  enableIrc: restrictToolNames ? false : options.enableIrc,
1665
1627
  restrictToolNames,
@@ -1684,6 +1646,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1684
1646
  prewalkArmed: options.prewalk !== undefined,
1685
1647
  taskDepth: options.taskDepth ?? 0,
1686
1648
  getSessionFile: () => sessionManager.getSessionFile() ?? null,
1649
+ sessionManager,
1687
1650
  getEvalKernelOwnerId: () => evalKernelOwnerId,
1688
1651
  getEvalSessionId: () =>
1689
1652
  session?.getEvalSessionId() ?? options.parentEvalSessionId ?? defaultEvalSessionId(toolSession),
@@ -2141,27 +2104,38 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2141
2104
  if (!resolved.configuredRole || !settings.get("retry.modelFallback")) {
2142
2105
  return primaryPatterns;
2143
2106
  }
2144
- const fallbackChains = settings.get("retry.fallbackChains");
2145
- const roleFallbacks =
2146
- fallbackChains[resolved.configuredRole] ??
2147
- (resolved.configuredRole === "default" ? undefined : fallbackChains.default);
2148
- if (!Array.isArray(roleFallbacks)) return primaryPatterns;
2107
+ const fallbackContext: RetryFallbackResolutionContext = {
2108
+ chains: expandDefaultRetryFallbackChains(settings.get("retry.fallbackChains"), [
2109
+ ...Object.keys(settings.getModelRoles()),
2110
+ resolved.configuredRole,
2111
+ ]),
2112
+ getModelRole: role => settings.getModelRole(role),
2113
+ modelLookup: modelRegistry,
2114
+ };
2149
2115
  const originalSelector = resolved.configuredPatterns[0];
2116
+ const originalModel = parseModelPattern(originalSelector, availableModels, matchPreferences).model;
2117
+ const chainKey = resolveRetryFallbackChainKey(
2118
+ fallbackContext,
2119
+ originalSelector,
2120
+ originalModel,
2121
+ resolved.configuredRole,
2122
+ );
2123
+ if (!chainKey) return primaryPatterns;
2150
2124
  const parsedOriginal = parseModelString(originalSelector, {
2151
2125
  allowMaxSuffix: true,
2152
2126
  allowAutoAlias: true,
2153
2127
  isLiteralModelId: (provider, id) => modelRegistry.find(provider, id) !== undefined,
2154
2128
  });
2155
2129
  const retryFallback: InitialRetryFallbackState = {
2156
- role: resolved.configuredRole,
2130
+ role: chainKey,
2157
2131
  originalSelector,
2158
2132
  originalThinkingLevel: parsedOriginal?.thinkingLevel,
2159
2133
  };
2160
2134
  return [
2161
2135
  ...primaryPatterns,
2162
- ...roleFallbacks
2163
- .filter((pattern): pattern is string => typeof pattern === "string")
2164
- .map(pattern => ({ pattern, retryFallback })),
2136
+ ...findRetryFallbackCandidates(fallbackContext, chainKey, originalSelector, originalModel, {
2137
+ allowMissingPrimary: true,
2138
+ }).map(candidate => ({ pattern: candidate.raw, retryFallback })),
2165
2139
  ];
2166
2140
  }
2167
2141
  if (resolved.model) {
@@ -2181,10 +2155,68 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2181
2155
  }));
2182
2156
  }),
2183
2157
  );
2158
+ let usageFallbackTriggered = false;
2184
2159
  for (let patternIndex = 0; patternIndex < expandedModelPatterns.length; patternIndex += 1) {
2185
2160
  const { pattern, retryFallback } = expandedModelPatterns[patternIndex];
2186
2161
  const primary = parseModelPattern(pattern, availableModels, matchPreferences);
2187
2162
  if (!primary.model || (retryFallback && !hasModelAuth(primary.model))) continue;
2163
+ let hasUsageFallbackCandidate = false;
2164
+ for (
2165
+ let candidateIndex = patternIndex + 1;
2166
+ candidateIndex < expandedModelPatterns.length;
2167
+ candidateIndex += 1
2168
+ ) {
2169
+ const candidate = parseModelPattern(
2170
+ expandedModelPatterns[candidateIndex].pattern,
2171
+ availableModels,
2172
+ matchPreferences,
2173
+ );
2174
+ if (candidate.model && hasModelAuth(candidate.model)) {
2175
+ hasUsageFallbackCandidate = true;
2176
+ break;
2177
+ }
2178
+ }
2179
+ const usageReservePolicy = settings.get("retry.usageReservePolicy");
2180
+ if (
2181
+ (hasUsageFallbackCandidate || usageReservePolicy === "fail-closed") &&
2182
+ settings.get("retry.modelFallback") &&
2183
+ settings.get("retry.usageAwareFallback")
2184
+ ) {
2185
+ let usageHealth: ModelUsageHealth | undefined;
2186
+ try {
2187
+ usageHealth = await modelRegistry.authStorage.getModelUsageHealth(primary.model.provider, {
2188
+ modelId: primary.model.id,
2189
+ baseUrl: primary.model.baseUrl,
2190
+ reserveFraction: settings.get("retry.usageReservePct") / 100,
2191
+ });
2192
+ } catch (error) {
2193
+ logger.debug("Usage-aware model preflight failed open", {
2194
+ provider: primary.model.provider,
2195
+ model: primary.model.id,
2196
+ error: String(error),
2197
+ });
2198
+ }
2199
+ if (usageHealth?.state === "depleted") {
2200
+ if (usageReservePolicy === "fail-closed") {
2201
+ throw new Error(
2202
+ `Usage depleted for ${primary.model.provider}/${primary.model.id}; reserve policy is fail-closed.`,
2203
+ );
2204
+ }
2205
+ usageFallbackTriggered = true;
2206
+ continue;
2207
+ }
2208
+ if (usageHealth?.state === "reserve") {
2209
+ if (usageReservePolicy === "fail-closed") {
2210
+ throw new Error(
2211
+ `Usage reserve reached for ${primary.model.provider}/${primary.model.id}; reserve policy is fail-closed.`,
2212
+ );
2213
+ }
2214
+ if (usageReservePolicy === "auto" || (!options.hasUI && !options.deferUsageReserveConfirmation)) {
2215
+ usageFallbackTriggered = true;
2216
+ continue;
2217
+ }
2218
+ }
2219
+ }
2188
2220
  let selectedModel = primary.model;
2189
2221
  let selectedThinkingLevel = primary.thinkingLevel;
2190
2222
  let selectedExplicitThinkingLevel = primary.explicitThinkingLevel;
@@ -2264,7 +2296,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2264
2296
  }
2265
2297
  }
2266
2298
  model = selectedModel;
2267
- initialRetryFallback = retryFallback;
2299
+ initialRetryFallback =
2300
+ retryFallback && usageFallbackTriggered ? { ...retryFallback, pinned: true } : retryFallback;
2268
2301
  modelFallbackMessage = undefined;
2269
2302
  if (selectedExplicitThinkingLevel) {
2270
2303
  restoredSessionThinkingLevel = selectedThinkingLevel;
@@ -2644,8 +2677,13 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2644
2677
  }
2645
2678
  const defaultPrompt = await buildSystemPromptInternal({
2646
2679
  cwd,
2680
+ additionalWorkspaceRoots: sessionManager.getAdditionalDirectories(),
2647
2681
  xdevTools: toolSession.xdevRegistry?.entries() ?? [],
2648
- xdevDocs: toolSession.xdevRegistry?.docsAll() ?? "",
2682
+ xdevDocs:
2683
+ toolSession.xdevRegistry?.docsAll(
2684
+ settings.get("tools.xdevDocs"),
2685
+ settings.get("tools.xdevInlineDevices"),
2686
+ ) ?? "",
2649
2687
  autoQaEnabled: !restrictToolNames && isAutoQaEnabled(settings),
2650
2688
  resolvedCustomPrompt: options.customSystemPrompt,
2651
2689
  skills: session?.skills ?? skills,
@@ -2750,16 +2788,25 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2750
2788
  // so that subagents launched in the same parallel batch can see each other in
2751
2789
  // their initial `# IRC Peers` block (rendered inside `rebuildSystemPrompt`).
2752
2790
  // The session reference is attached after construction below.
2753
- agentRegistry.register({
2791
+ const registrationInput = {
2754
2792
  id: resolvedAgentId,
2755
2793
  displayName: resolvedAgentDisplayName,
2756
2794
  kind: agentKind,
2757
2795
  parentId: options.parentAgentId,
2758
2796
  session: null,
2759
2797
  sessionFile: sessionManager.getSessionFile() ?? null,
2760
- status: "running",
2761
- });
2762
- hasRegistered = true;
2798
+ status: "running" as const,
2799
+ };
2800
+ registeredAgentRef =
2801
+ options.expectedAgentRef === undefined
2802
+ ? agentRegistry.register(registrationInput)
2803
+ : agentRegistry.registerIfAvailable(registrationInput, options.expectedAgentRef);
2804
+ if (!registeredAgentRef) {
2805
+ throw new Error(`Agent "${resolvedAgentId}" is already owned by another session generation.`);
2806
+ }
2807
+ // A reused parked ref remains parked until the new AgentSession is fully
2808
+ // constructed and attached. Startup failure therefore leaves it revivable.
2809
+ hasRegistered = options.expectedAgentRef === undefined || options.expectedAgentRef === null;
2763
2810
 
2764
2811
  // Partition the initial enabled set for the xd:// transport: ambient
2765
2812
  // discoverable tools become mounted devices, while explicitly requested
@@ -3070,6 +3117,19 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
3070
3117
  skillsSettings: settings.getGroup("skills"),
3071
3118
  modelRegistry,
3072
3119
  toolRegistry,
3120
+ memoryAgentDir: agentDir,
3121
+ memoryTaskDepth: taskDepth,
3122
+ createMemoryTools: restrictToolNames
3123
+ ? undefined
3124
+ : async () => {
3125
+ const tools = await Promise.all(
3126
+ MEMORY_BACKEND_TOOL_NAMES.map(name => BUILTIN_TOOLS[name](toolSession)),
3127
+ );
3128
+ return tools.filter((tool): tool is AgentTool => tool !== null);
3129
+ },
3130
+ createComputerTool: restrictToolNames
3131
+ ? undefined
3132
+ : async () => (await BUILTIN_TOOLS.computer(toolSession)) ?? null,
3073
3133
  createVibeTools:
3074
3134
  (options.taskDepth ?? 0) === 0 && !options.parentTaskPrefix
3075
3135
  ? () => createVibeTools(toolSession)
@@ -3116,12 +3176,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
3116
3176
  titleSystemPrompt: options.titleSystemPrompt,
3117
3177
  });
3118
3178
  hasSession = true;
3119
- if (asyncJobManager) {
3120
- session.yieldQueue.register<AsyncResultEntry>("async-result", {
3121
- isStale: entry => asyncJobManager.isDeliverySuppressed(entry.jobId),
3122
- build: buildAsyncResultBatchMessage,
3123
- });
3124
- }
3125
3179
  session.yieldQueue.register<McpNotificationEntry>("mcp-notification", {
3126
3180
  build: buildMcpNotificationBatchMessage,
3127
3181
  });
@@ -3133,7 +3187,19 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
3133
3187
  // Attach the live session to the pre-registered ref so peers can route IRC
3134
3188
  // messages here. Refresh sessionFile in case it was unavailable at pre-register
3135
3189
  // time. The dispose wrapper below unregisters on teardown (unless parked).
3136
- agentRegistry.attachSession(resolvedAgentId, session, sessionManager.getSessionFile() ?? null);
3190
+ if (
3191
+ !registeredAgentRef ||
3192
+ !agentRegistry.attachSession(
3193
+ resolvedAgentId,
3194
+ session,
3195
+ sessionManager.getSessionFile() ?? null,
3196
+ registeredAgentRef,
3197
+ ) ||
3198
+ !agentRegistry.setStatus(resolvedAgentId, "running", registeredAgentRef)
3199
+ ) {
3200
+ throw new Error(`Agent "${resolvedAgentId}" was replaced during session initialization.`);
3201
+ }
3202
+ hasRegistered = true;
3137
3203
  {
3138
3204
  const originalDispose = session.dispose.bind(session);
3139
3205
  session.dispose = async () => {
@@ -3147,6 +3213,17 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
3147
3213
  // adopted subagent sessions, revivers. Tear it down while shared
3148
3214
  // resources (kernels, MCP, LSP) are still live. Subagent disposal
3149
3215
  // must NOT touch the global lifecycle.
3216
+ const vibeRegistry = VibeSessionRegistry.global();
3217
+ const vibeParentSession = {
3218
+ getAgentId: () => resolvedAgentId,
3219
+ getSessionId: () => sessionManager.getSessionId(),
3220
+ getSessionFile: () => sessionManager.getSessionFile() ?? null,
3221
+ sessionManager,
3222
+ asyncJobManager: scopedAsyncJobManager,
3223
+ settings,
3224
+ getActiveModelString,
3225
+ };
3226
+ await vibeRegistry.suspendScope(vibeRegistry.ownerScope(vibeParentSession), scopedAsyncJobManager);
3150
3227
  await AgentLifecycleManager.global().dispose();
3151
3228
  }
3152
3229
  await originalDispose();
@@ -3388,6 +3465,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
3388
3465
  try {
3389
3466
  if (hasSession) {
3390
3467
  await session.dispose();
3468
+ if (hasRegistered) unregisterUnlessParked();
3391
3469
  } else {
3392
3470
  if (hasRegistered) unregisterUnlessParked();
3393
3471
  if (asyncJobManager) {
@@ -3396,6 +3474,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
3396
3474
  }
3397
3475
  await asyncJobManager.dispose({ timeoutMs: 3_000 });
3398
3476
  }
3477
+ await releaseComputerSessionsForOwner(evalKernelOwnerId);
3399
3478
  await disposeKernelSessionsByOwner(evalKernelOwnerId);
3400
3479
  await disposeRubyKernelSessionsByOwner(evalKernelOwnerId);
3401
3480
  await disposeJuliaKernelSessionsByOwner(evalKernelOwnerId);