@hyperdrive.bot/paseo-server 0.2.6

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 (804) hide show
  1. package/.env.example +31 -0
  2. package/README.md +111 -0
  3. package/dist/scripts/dev-runner.js +37 -0
  4. package/dist/scripts/mcp-stdio-socket-bridge-cli.mjs +62 -0
  5. package/dist/scripts/supervisor-entrypoint.js +141 -0
  6. package/dist/scripts/supervisor-log-config.js +31 -0
  7. package/dist/scripts/supervisor.js +252 -0
  8. package/dist/server/executable-resolution/executable-resolution.d.ts +10 -0
  9. package/dist/server/executable-resolution/executable-resolution.js +118 -0
  10. package/dist/server/executable-resolution/windows.d.ts +18 -0
  11. package/dist/server/executable-resolution/windows.js +65 -0
  12. package/dist/server/extensions/daemon-backend.d.ts +11 -0
  13. package/dist/server/extensions/daemon-backend.js +146 -0
  14. package/dist/server/extensions/extension-host-manager.d.ts +48 -0
  15. package/dist/server/extensions/extension-host-manager.js +141 -0
  16. package/dist/server/extensions/extension-loader.d.ts +11 -0
  17. package/dist/server/extensions/extension-loader.js +47 -0
  18. package/dist/server/extensions/extension-service.d.ts +17 -0
  19. package/dist/server/extensions/extension-service.js +76 -0
  20. package/dist/server/server/agent/activity-curator.d.ts +28 -0
  21. package/dist/server/server/agent/activity-curator.js +227 -0
  22. package/dist/server/server/agent/agent-archive.d.ts +11 -0
  23. package/dist/server/server/agent/agent-archive.js +16 -0
  24. package/dist/server/server/agent/agent-loading.d.ts +12 -0
  25. package/dist/server/server/agent/agent-loading.js +55 -0
  26. package/dist/server/server/agent/agent-manager.d.ts +462 -0
  27. package/dist/server/server/agent/agent-manager.js +2955 -0
  28. package/dist/server/server/agent/agent-projections.d.ts +26 -0
  29. package/dist/server/server/agent/agent-projections.js +404 -0
  30. package/dist/server/server/agent/agent-prompt.d.ts +76 -0
  31. package/dist/server/server/agent/agent-prompt.js +230 -0
  32. package/dist/server/server/agent/agent-response-loop.d.ts +67 -0
  33. package/dist/server/server/agent/agent-response-loop.js +326 -0
  34. package/dist/server/server/agent/agent-routes.d.ts +18 -0
  35. package/dist/server/server/agent/agent-routes.js +107 -0
  36. package/dist/server/server/agent/agent-sdk-types.d.ts +677 -0
  37. package/dist/server/server/agent/agent-sdk-types.js +22 -0
  38. package/dist/server/server/agent/agent-storage.d.ts +152 -0
  39. package/dist/server/server/agent/agent-storage.js +342 -0
  40. package/dist/server/server/agent/agent-stream-coalescer.d.ts +41 -0
  41. package/dist/server/server/agent/agent-stream-coalescer.js +166 -0
  42. package/dist/server/server/agent/agent-timeline-store-types.d.ts +50 -0
  43. package/dist/server/server/agent/agent-timeline-store-types.js +2 -0
  44. package/dist/server/server/agent/agent-timeline-store.d.ts +27 -0
  45. package/dist/server/server/agent/agent-timeline-store.js +230 -0
  46. package/dist/server/server/agent/audio-utils.d.ts +3 -0
  47. package/dist/server/server/agent/audio-utils.js +19 -0
  48. package/dist/server/server/agent/create-agent/create.d.ts +83 -0
  49. package/dist/server/server/agent/create-agent/create.js +289 -0
  50. package/dist/server/server/agent/create-agent-lifecycle-dispatch.d.ts +54 -0
  51. package/dist/server/server/agent/create-agent-lifecycle-dispatch.js +152 -0
  52. package/dist/server/server/agent/create-agent-mode.d.ts +13 -0
  53. package/dist/server/server/agent/create-agent-mode.js +62 -0
  54. package/dist/server/server/agent/create-agent-title.d.ts +10 -0
  55. package/dist/server/server/agent/create-agent-title.js +34 -0
  56. package/dist/server/server/agent/dictation-debug.d.ts +13 -0
  57. package/dist/server/server/agent/dictation-debug.js +50 -0
  58. package/dist/server/server/agent/foreground-run-state.d.ts +50 -0
  59. package/dist/server/server/agent/foreground-run-state.js +160 -0
  60. package/dist/server/server/agent/import-sessions.d.ts +47 -0
  61. package/dist/server/server/agent/import-sessions.js +145 -0
  62. package/dist/server/server/agent/lifecycle-command.d.ts +73 -0
  63. package/dist/server/server/agent/lifecycle-command.js +92 -0
  64. package/dist/server/server/agent/mcp-server.d.ts +22 -0
  65. package/dist/server/server/agent/mcp-server.js +544 -0
  66. package/dist/server/server/agent/mcp-shared.d.ts +193 -0
  67. package/dist/server/server/agent/mcp-shared.js +197 -0
  68. package/dist/server/server/agent/pcm16-resampler.d.ts +14 -0
  69. package/dist/server/server/agent/pcm16-resampler.js +63 -0
  70. package/dist/server/server/agent/permission-response.d.ts +15 -0
  71. package/dist/server/server/agent/permission-response.js +14 -0
  72. package/dist/server/server/agent/prompt-attachments.d.ts +7 -0
  73. package/dist/server/server/agent/prompt-attachments.js +90 -0
  74. package/dist/server/server/agent/provider-history-timestamps.d.ts +2 -0
  75. package/dist/server/server/agent/provider-history-timestamps.js +16 -0
  76. package/dist/server/server/agent/provider-launch-config.d.ts +51 -0
  77. package/dist/server/server/agent/provider-launch-config.js +183 -0
  78. package/dist/server/server/agent/provider-notices.d.ts +3 -0
  79. package/dist/server/server/agent/provider-notices.js +5 -0
  80. package/dist/server/server/agent/provider-registry.d.ts +49 -0
  81. package/dist/server/server/agent/provider-registry.js +578 -0
  82. package/dist/server/server/agent/provider-session-import.d.ts +10 -0
  83. package/dist/server/server/agent/provider-session-import.js +49 -0
  84. package/dist/server/server/agent/provider-snapshot-manager.d.ts +137 -0
  85. package/dist/server/server/agent/provider-snapshot-manager.js +694 -0
  86. package/dist/server/server/agent/providers/acp-agent.d.ts +354 -0
  87. package/dist/server/server/agent/providers/acp-agent.js +2438 -0
  88. package/dist/server/server/agent/providers/claude/agent.d.ts +341 -0
  89. package/dist/server/server/agent/providers/claude/agent.js +4606 -0
  90. package/dist/server/server/agent/providers/claude/background-shell-kill-keys.d.ts +32 -0
  91. package/dist/server/server/agent/providers/claude/background-shell-kill-keys.js +34 -0
  92. package/dist/server/server/agent/providers/claude/background-task-tracker.d.ts +49 -0
  93. package/dist/server/server/agent/providers/claude/background-task-tracker.js +119 -0
  94. package/dist/server/server/agent/providers/claude/feature-definitions.d.ts +8 -0
  95. package/dist/server/server/agent/providers/claude/feature-definitions.js +24 -0
  96. package/dist/server/server/agent/providers/claude/hook-handler.d.ts +37 -0
  97. package/dist/server/server/agent/providers/claude/hook-handler.js +193 -0
  98. package/dist/server/server/agent/providers/claude/hooks/event-translator.d.ts +49 -0
  99. package/dist/server/server/agent/providers/claude/hooks/event-translator.js +151 -0
  100. package/dist/server/server/agent/providers/claude/image-compression.d.ts +10 -0
  101. package/dist/server/server/agent/providers/claude/image-compression.js +32 -0
  102. package/dist/server/server/agent/providers/claude/model-manifest.d.ts +104 -0
  103. package/dist/server/server/agent/providers/claude/model-manifest.js +221 -0
  104. package/dist/server/server/agent/providers/claude/models.d.ts +11 -0
  105. package/dist/server/server/agent/providers/claude/models.js +97 -0
  106. package/dist/server/server/agent/providers/claude/partial-json.d.ts +5 -0
  107. package/dist/server/server/agent/providers/claude/partial-json.js +306 -0
  108. package/dist/server/server/agent/providers/claude/project-dir.d.ts +6 -0
  109. package/dist/server/server/agent/providers/claude/project-dir.js +57 -0
  110. package/dist/server/server/agent/providers/claude/pty-session-launcher.d.ts +41 -0
  111. package/dist/server/server/agent/providers/claude/pty-session-launcher.js +131 -0
  112. package/dist/server/server/agent/providers/claude/resume-source.d.ts +11 -0
  113. package/dist/server/server/agent/providers/claude/resume-source.js +37 -0
  114. package/dist/server/server/agent/providers/claude/rewind.d.ts +30 -0
  115. package/dist/server/server/agent/providers/claude/rewind.js +30 -0
  116. package/dist/server/server/agent/providers/claude/sidechain-tracker.d.ts +22 -0
  117. package/dist/server/server/agent/providers/claude/sidechain-tracker.js +232 -0
  118. package/dist/server/server/agent/providers/claude/spawn-config-writer.d.ts +70 -0
  119. package/dist/server/server/agent/providers/claude/spawn-config-writer.js +229 -0
  120. package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +47 -0
  121. package/dist/server/server/agent/providers/claude/task-notification-tool-call.js +292 -0
  122. package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.d.ts +24 -0
  123. package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.js +23 -0
  124. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts +3 -0
  125. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js +130 -0
  126. package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts +16 -0
  127. package/dist/server/server/agent/providers/claude/tool-call-mapper.js +129 -0
  128. package/dist/server/server/agent/providers/claude/transport/claude-paths.d.ts +31 -0
  129. package/dist/server/server/agent/providers/claude/transport/claude-paths.js +63 -0
  130. package/dist/server/server/agent/providers/claude/transport/continue-flag.d.ts +31 -0
  131. package/dist/server/server/agent/providers/claude/transport/continue-flag.js +42 -0
  132. package/dist/server/server/agent/providers/claude/transport/pty-query.d.ts +62 -0
  133. package/dist/server/server/agent/providers/claude/transport/pty-query.js +265 -0
  134. package/dist/server/server/agent/providers/claude/transport/pty.d.ts +50 -0
  135. package/dist/server/server/agent/providers/claude/transport/pty.js +211 -0
  136. package/dist/server/server/agent/providers/claude/transport/redact.d.ts +2 -0
  137. package/dist/server/server/agent/providers/claude/transport/redact.js +5 -0
  138. package/dist/server/server/agent/providers/claude/transport/sdk.d.ts +63 -0
  139. package/dist/server/server/agent/providers/claude/transport/sdk.js +207 -0
  140. package/dist/server/server/agent/providers/claude/transport/transcript-sdk-reader.d.ts +27 -0
  141. package/dist/server/server/agent/providers/claude/transport/transcript-sdk-reader.js +162 -0
  142. package/dist/server/server/agent/providers/claude/transport/transcript-tailer.d.ts +28 -0
  143. package/dist/server/server/agent/providers/claude/transport/transcript-tailer.js +166 -0
  144. package/dist/server/server/agent/providers/claude/transport/truncate-session.d.ts +41 -0
  145. package/dist/server/server/agent/providers/claude/transport/truncate-session.js +86 -0
  146. package/dist/server/server/agent/providers/claude/transport/types.d.ts +47 -0
  147. package/dist/server/server/agent/providers/claude/transport/types.js +2 -0
  148. package/dist/server/server/agent/providers/codex/app-server-transport.d.ts +90 -0
  149. package/dist/server/server/agent/providers/codex/app-server-transport.js +262 -0
  150. package/dist/server/server/agent/providers/codex/rewind.d.ts +21 -0
  151. package/dist/server/server/agent/providers/codex/rewind.js +46 -0
  152. package/dist/server/server/agent/providers/codex/test-utils/fake-app-server.d.ts +36 -0
  153. package/dist/server/server/agent/providers/codex/test-utils/fake-app-server.js +217 -0
  154. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.d.ts +12 -0
  155. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.js +104 -0
  156. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts +24 -0
  157. package/dist/server/server/agent/providers/codex/tool-call-mapper.js +830 -0
  158. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +308 -0
  159. package/dist/server/server/agent/providers/codex-app-server-agent.js +4514 -0
  160. package/dist/server/server/agent/providers/codex-feature-definitions.d.ts +11 -0
  161. package/dist/server/server/agent/providers/codex-feature-definitions.js +45 -0
  162. package/dist/server/server/agent/providers/copilot-acp-agent.d.ts +26 -0
  163. package/dist/server/server/agent/providers/copilot-acp-agent.js +187 -0
  164. package/dist/server/server/agent/providers/cursor-acp-agent.d.ts +15 -0
  165. package/dist/server/server/agent/providers/cursor-acp-agent.js +18 -0
  166. package/dist/server/server/agent/providers/diagnostic-utils.d.ts +36 -0
  167. package/dist/server/server/agent/providers/diagnostic-utils.js +355 -0
  168. package/dist/server/server/agent/providers/generic-acp-agent.d.ts +42 -0
  169. package/dist/server/server/agent/providers/generic-acp-agent.js +161 -0
  170. package/dist/server/server/agent/providers/kiro-acp-agent.d.ts +17 -0
  171. package/dist/server/server/agent/providers/kiro-acp-agent.js +78 -0
  172. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +85 -0
  173. package/dist/server/server/agent/providers/mock-load-test-agent.js +1010 -0
  174. package/dist/server/server/agent/providers/mock-slow-provider.d.ts +14 -0
  175. package/dist/server/server/agent/providers/mock-slow-provider.js +39 -0
  176. package/dist/server/server/agent/providers/opencode/paths.d.ts +2 -0
  177. package/dist/server/server/agent/providers/opencode/paths.js +7 -0
  178. package/dist/server/server/agent/providers/opencode/rewind.d.ts +18 -0
  179. package/dist/server/server/agent/providers/opencode/rewind.js +14 -0
  180. package/dist/server/server/agent/providers/opencode/server-manager.d.ts +91 -0
  181. package/dist/server/server/agent/providers/opencode/server-manager.js +425 -0
  182. package/dist/server/server/agent/providers/opencode/test-server-manager.d.ts +25 -0
  183. package/dist/server/server/agent/providers/opencode/test-server-manager.js +39 -0
  184. package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-harness.d.ts +92 -0
  185. package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-harness.js +268 -0
  186. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts +3 -0
  187. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js +256 -0
  188. package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts +13 -0
  189. package/dist/server/server/agent/providers/opencode/tool-call-mapper.js +53 -0
  190. package/dist/server/server/agent/providers/opencode-agent.d.ts +286 -0
  191. package/dist/server/server/agent/providers/opencode-agent.js +2857 -0
  192. package/dist/server/server/agent/providers/pi/agent.d.ts +137 -0
  193. package/dist/server/server/agent/providers/pi/agent.js +1650 -0
  194. package/dist/server/server/agent/providers/pi/cli-runtime.d.ts +21 -0
  195. package/dist/server/server/agent/providers/pi/cli-runtime.js +233 -0
  196. package/dist/server/server/agent/providers/pi/history-mapper.d.ts +9 -0
  197. package/dist/server/server/agent/providers/pi/history-mapper.js +140 -0
  198. package/dist/server/server/agent/providers/pi/rewind.d.ts +8 -0
  199. package/dist/server/server/agent/providers/pi/rewind.js +8 -0
  200. package/dist/server/server/agent/providers/pi/rpc-types.d.ts +208 -0
  201. package/dist/server/server/agent/providers/pi/rpc-types.js +2 -0
  202. package/dist/server/server/agent/providers/pi/runtime.d.ts +57 -0
  203. package/dist/server/server/agent/providers/pi/runtime.js +56 -0
  204. package/dist/server/server/agent/providers/pi/session-descriptor.d.ts +16 -0
  205. package/dist/server/server/agent/providers/pi/session-descriptor.js +346 -0
  206. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts +83 -0
  207. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js +184 -0
  208. package/dist/server/server/agent/providers/pi/tool-call-mapper.d.ts +112 -0
  209. package/dist/server/server/agent/providers/pi/tool-call-mapper.js +284 -0
  210. package/dist/server/server/agent/providers/provider-image-output.d.ts +25 -0
  211. package/dist/server/server/agent/providers/provider-image-output.js +111 -0
  212. package/dist/server/server/agent/providers/provider-runner.d.ts +27 -0
  213. package/dist/server/server/agent/providers/provider-runner.js +81 -0
  214. package/dist/server/server/agent/providers/test-utils/session-stream-adapter.d.ts +3 -0
  215. package/dist/server/server/agent/providers/test-utils/session-stream-adapter.js +60 -0
  216. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +884 -0
  217. package/dist/server/server/agent/providers/tool-call-detail-primitives.js +778 -0
  218. package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts +25 -0
  219. package/dist/server/server/agent/providers/tool-call-mapper-utils.js +208 -0
  220. package/dist/server/server/agent/recordings-debug.d.ts +3 -0
  221. package/dist/server/server/agent/recordings-debug.js +19 -0
  222. package/dist/server/server/agent/render-file-attachment.d.ts +29 -0
  223. package/dist/server/server/agent/render-file-attachment.js +41 -0
  224. package/dist/server/server/agent/resolve-resume-cwd.d.ts +14 -0
  225. package/dist/server/server/agent/resolve-resume-cwd.js +49 -0
  226. package/dist/server/server/agent/rewind/rewind.d.ts +10 -0
  227. package/dist/server/server/agent/rewind/rewind.js +29 -0
  228. package/dist/server/server/agent/rewind/test-rewind-session.d.ts +48 -0
  229. package/dist/server/server/agent/rewind/test-rewind-session.js +88 -0
  230. package/dist/server/server/agent/runtime-mcp-config.d.ts +14 -0
  231. package/dist/server/server/agent/runtime-mcp-config.js +53 -0
  232. package/dist/server/server/agent/session-digest-generator.d.ts +37 -0
  233. package/dist/server/server/agent/session-digest-generator.js +115 -0
  234. package/dist/server/server/agent/session-digest.d.ts +35 -0
  235. package/dist/server/server/agent/session-digest.js +104 -0
  236. package/dist/server/server/agent/structured-generation-providers.d.ts +29 -0
  237. package/dist/server/server/agent/structured-generation-providers.js +236 -0
  238. package/dist/server/server/agent/stt-debug.d.ts +10 -0
  239. package/dist/server/server/agent/stt-debug.js +33 -0
  240. package/dist/server/server/agent/stt-manager.d.ts +38 -0
  241. package/dist/server/server/agent/stt-manager.js +246 -0
  242. package/dist/server/server/agent/system-prompt.d.ts +2 -0
  243. package/dist/server/server/agent/system-prompt.js +8 -0
  244. package/dist/server/server/agent/timeline-append.d.ts +10 -0
  245. package/dist/server/server/agent/timeline-append.js +27 -0
  246. package/dist/server/server/agent/timeline-projection.d.ts +71 -0
  247. package/dist/server/server/agent/timeline-projection.js +373 -0
  248. package/dist/server/server/agent/tools/paseo-tools.d.ts +54 -0
  249. package/dist/server/server/agent/tools/paseo-tools.js +2197 -0
  250. package/dist/server/server/agent/tools/types.d.ts +36 -0
  251. package/dist/server/server/agent/tools/types.js +2 -0
  252. package/dist/server/server/agent/tts-debug.d.ts +8 -0
  253. package/dist/server/server/agent/tts-debug.js +24 -0
  254. package/dist/server/server/agent/tts-manager.d.ts +41 -0
  255. package/dist/server/server/agent/tts-manager.js +375 -0
  256. package/dist/server/server/agent/wait-for-agent-tracker.d.ts +15 -0
  257. package/dist/server/server/agent/wait-for-agent-tracker.js +53 -0
  258. package/dist/server/server/agent-attention-policy.d.ts +29 -0
  259. package/dist/server/server/agent-attention-policy.js +36 -0
  260. package/dist/server/server/atomic-file.d.ts +3 -0
  261. package/dist/server/server/atomic-file.js +19 -0
  262. package/dist/server/server/auth.d.ts +38 -0
  263. package/dist/server/server/auth.js +143 -0
  264. package/dist/server/server/auto-archive-on-merge/archive-if-safe.d.ts +40 -0
  265. package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +92 -0
  266. package/dist/server/server/auto-archive-on-merge/index.d.ts +8 -0
  267. package/dist/server/server/auto-archive-on-merge/index.js +15 -0
  268. package/dist/server/server/bootstrap.d.ts +126 -0
  269. package/dist/server/server/bootstrap.js +1193 -0
  270. package/dist/server/server/browser-tools/broker.d.ts +45 -0
  271. package/dist/server/server/browser-tools/broker.js +390 -0
  272. package/dist/server/server/browser-tools/errors.d.ts +22 -0
  273. package/dist/server/server/browser-tools/errors.js +27 -0
  274. package/dist/server/server/browser-tools/policy.d.ts +10 -0
  275. package/dist/server/server/browser-tools/policy.js +16 -0
  276. package/dist/server/server/browser-tools/tools.d.ts +16 -0
  277. package/dist/server/server/browser-tools/tools.js +845 -0
  278. package/dist/server/server/chat/chat-mentions.d.ts +47 -0
  279. package/dist/server/server/chat/chat-mentions.js +113 -0
  280. package/dist/server/server/chat/chat-service.d.ts +78 -0
  281. package/dist/server/server/chat/chat-service.js +337 -0
  282. package/dist/server/server/checkout/status-projection.d.ts +19 -0
  283. package/dist/server/server/checkout/status-projection.js +102 -0
  284. package/dist/server/server/checkout-diff-manager.d.ts +41 -0
  285. package/dist/server/server/checkout-diff-manager.js +189 -0
  286. package/dist/server/server/checkout-git-utils.d.ts +11 -0
  287. package/dist/server/server/checkout-git-utils.js +34 -0
  288. package/dist/server/server/client-message-id.d.ts +3 -0
  289. package/dist/server/server/client-message-id.js +12 -0
  290. package/dist/server/server/config.d.ts +17 -0
  291. package/dist/server/server/config.js +320 -0
  292. package/dist/server/server/connection-offer.d.ts +20 -0
  293. package/dist/server/server/connection-offer.js +59 -0
  294. package/dist/server/server/daemon-config-store.d.ts +25 -0
  295. package/dist/server/server/daemon-config-store.js +194 -0
  296. package/dist/server/server/daemon-keypair.d.ts +8 -0
  297. package/dist/server/server/daemon-keypair.js +42 -0
  298. package/dist/server/server/daemon-version.d.ts +5 -0
  299. package/dist/server/server/daemon-version.js +22 -0
  300. package/dist/server/server/daemon-worker.d.ts +2 -0
  301. package/dist/server/server/daemon-worker.js +251 -0
  302. package/dist/server/server/dictation/dictation-stream-manager.d.ts +87 -0
  303. package/dist/server/server/dictation/dictation-stream-manager.js +572 -0
  304. package/dist/server/server/editor-targets.d.ts +18 -0
  305. package/dist/server/server/editor-targets.js +109 -0
  306. package/dist/server/server/exports.d.ts +24 -0
  307. package/dist/server/server/exports.js +24 -0
  308. package/dist/server/server/file-download/token-store.d.ts +24 -0
  309. package/dist/server/server/file-download/token-store.js +40 -0
  310. package/dist/server/server/file-explorer/file-download-frames.d.ts +27 -0
  311. package/dist/server/server/file-explorer/file-download-frames.js +47 -0
  312. package/dist/server/server/file-explorer/service.d.ts +75 -0
  313. package/dist/server/server/file-explorer/service.js +308 -0
  314. package/dist/server/server/file-upload/index.d.ts +27 -0
  315. package/dist/server/server/file-upload/index.js +158 -0
  316. package/dist/server/server/hostnames.d.ts +13 -0
  317. package/dist/server/server/hostnames.js +94 -0
  318. package/dist/server/server/json-utils.d.ts +11 -0
  319. package/dist/server/server/json-utils.js +45 -0
  320. package/dist/server/server/lifecycle-reasons.d.ts +4 -0
  321. package/dist/server/server/lifecycle-reasons.js +6 -0
  322. package/dist/server/server/logger.d.ts +29 -0
  323. package/dist/server/server/logger.js +125 -0
  324. package/dist/server/server/loop-service.d.ts +234 -0
  325. package/dist/server/server/loop-service.js +769 -0
  326. package/dist/server/server/managed-processes/managed-processes.d.ts +76 -0
  327. package/dist/server/server/managed-processes/managed-processes.js +326 -0
  328. package/dist/server/server/meetings/capture-http.d.ts +3 -0
  329. package/dist/server/server/meetings/capture-http.js +71 -0
  330. package/dist/server/server/meetings/capture-orchestrator.d.ts +123 -0
  331. package/dist/server/server/meetings/capture-orchestrator.js +531 -0
  332. package/dist/server/server/meetings/capture-stream-ws.d.ts +42 -0
  333. package/dist/server/server/meetings/capture-stream-ws.js +68 -0
  334. package/dist/server/server/meetings/live-transcription.d.ts +57 -0
  335. package/dist/server/server/meetings/live-transcription.js +280 -0
  336. package/dist/server/server/meetings/meetings-http.d.ts +42 -0
  337. package/dist/server/server/meetings/meetings-http.js +266 -0
  338. package/dist/server/server/messages.d.ts +9 -0
  339. package/dist/server/server/messages.js +29 -0
  340. package/dist/server/server/migrations/backfill-workspace-id.migration.d.ts +9 -0
  341. package/dist/server/server/migrations/backfill-workspace-id.migration.js +64 -0
  342. package/dist/server/server/monitoring/telemetry.d.ts +13 -0
  343. package/dist/server/server/monitoring/telemetry.js +21 -0
  344. package/dist/server/server/package-version.d.ts +19 -0
  345. package/dist/server/server/package-version.js +60 -0
  346. package/dist/server/server/pagination/cursor.d.ts +16 -0
  347. package/dist/server/server/pagination/cursor.js +62 -0
  348. package/dist/server/server/pagination/sortable-pager.d.ts +24 -0
  349. package/dist/server/server/pagination/sortable-pager.js +68 -0
  350. package/dist/server/server/pairing-offer.d.ts +18 -0
  351. package/dist/server/server/pairing-offer.js +47 -0
  352. package/dist/server/server/pairing-qr.d.ts +7 -0
  353. package/dist/server/server/pairing-qr.js +45 -0
  354. package/dist/server/server/paseo-env.d.ts +13 -0
  355. package/dist/server/server/paseo-env.js +53 -0
  356. package/dist/server/server/paseo-home.d.ts +2 -0
  357. package/dist/server/server/paseo-home.js +19 -0
  358. package/dist/server/server/paseo-worktree-service.d.ts +52 -0
  359. package/dist/server/server/paseo-worktree-service.js +294 -0
  360. package/dist/server/server/path-utils.d.ts +5 -0
  361. package/dist/server/server/path-utils.js +34 -0
  362. package/dist/server/server/persisted-config.d.ts +333 -0
  363. package/dist/server/server/persisted-config.js +402 -0
  364. package/dist/server/server/persistence-hooks.d.ts +32 -0
  365. package/dist/server/server/persistence-hooks.js +97 -0
  366. package/dist/server/server/pid-lock.d.ts +32 -0
  367. package/dist/server/server/pid-lock.js +170 -0
  368. package/dist/server/server/private-files.d.ts +6 -0
  369. package/dist/server/server/private-files.js +37 -0
  370. package/dist/server/server/process-diagnostics.d.ts +17 -0
  371. package/dist/server/server/process-diagnostics.js +22 -0
  372. package/dist/server/server/push/categories.d.ts +35 -0
  373. package/dist/server/server/push/categories.js +40 -0
  374. package/dist/server/server/push/expo-worker.d.ts +51 -0
  375. package/dist/server/server/push/expo-worker.js +149 -0
  376. package/dist/server/server/push/index.d.ts +39 -0
  377. package/dist/server/server/push/index.js +45 -0
  378. package/dist/server/server/push/notifications.d.ts +9 -0
  379. package/dist/server/server/push/notifications.js +15 -0
  380. package/dist/server/server/push/push-service.d.ts +20 -0
  381. package/dist/server/server/push/push-service.js +66 -0
  382. package/dist/server/server/push/routes.d.ts +28 -0
  383. package/dist/server/server/push/routes.js +74 -0
  384. package/dist/server/server/push/subscriptions.d.ts +42 -0
  385. package/dist/server/server/push/subscriptions.js +112 -0
  386. package/dist/server/server/push/token-store.d.ts +29 -0
  387. package/dist/server/server/push/token-store.js +78 -0
  388. package/dist/server/server/push/triggers.d.ts +68 -0
  389. package/dist/server/server/push/triggers.js +103 -0
  390. package/dist/server/server/push/vapid-keys.d.ts +15 -0
  391. package/dist/server/server/push/vapid-keys.js +52 -0
  392. package/dist/server/server/push/web-worker.d.ts +44 -0
  393. package/dist/server/server/push/web-worker.js +99 -0
  394. package/dist/server/server/relay-transport.d.ts +31 -0
  395. package/dist/server/server/relay-transport.js +514 -0
  396. package/dist/server/server/resolve-workspace-id-for-path.d.ts +3 -0
  397. package/dist/server/server/resolve-workspace-id-for-path.js +41 -0
  398. package/dist/server/server/resolve-worktree-creation-intent.d.ts +32 -0
  399. package/dist/server/server/resolve-worktree-creation-intent.js +121 -0
  400. package/dist/server/server/schedule/cron.d.ts +4 -0
  401. package/dist/server/server/schedule/cron.js +81 -0
  402. package/dist/server/server/schedule/service.d.ts +52 -0
  403. package/dist/server/server/schedule/service.js +628 -0
  404. package/dist/server/server/schedule/store.d.ts +13 -0
  405. package/dist/server/server/schedule/store.js +57 -0
  406. package/dist/server/server/script-health-monitor.d.ts +39 -0
  407. package/dist/server/server/script-health-monitor.js +159 -0
  408. package/dist/server/server/script-proxy.d.ts +3 -0
  409. package/dist/server/server/script-proxy.js +2 -0
  410. package/dist/server/server/script-route-branch-handler.d.ts +10 -0
  411. package/dist/server/server/script-route-branch-handler.js +11 -0
  412. package/dist/server/server/script-status-projection.d.ts +36 -0
  413. package/dist/server/server/script-status-projection.js +200 -0
  414. package/dist/server/server/server-id.d.ts +17 -0
  415. package/dist/server/server/server-id.js +68 -0
  416. package/dist/server/server/service-proxy.d.ts +237 -0
  417. package/dist/server/server/service-proxy.js +714 -0
  418. package/dist/server/server/session/agent-config/agent-config-session.d.ts +50 -0
  419. package/dist/server/server/session/agent-config/agent-config-session.js +98 -0
  420. package/dist/server/server/session/agent-updates/agent-updates-service.d.ts +59 -0
  421. package/dist/server/server/session/agent-updates/agent-updates-service.js +220 -0
  422. package/dist/server/server/session/chat/chat-schedule-loop-session.d.ts +120 -0
  423. package/dist/server/server/session/chat/chat-schedule-loop-session.js +489 -0
  424. package/dist/server/server/session/checkout/checkout-session.d.ts +140 -0
  425. package/dist/server/server/session/checkout/checkout-session.js +927 -0
  426. package/dist/server/server/session/checkout/git-metadata-generator.d.ts +53 -0
  427. package/dist/server/server/session/checkout/git-metadata-generator.js +159 -0
  428. package/dist/server/server/session/daemon/daemon-self-update-session-controller.d.ts +32 -0
  429. package/dist/server/server/session/daemon/daemon-self-update-session-controller.js +88 -0
  430. package/dist/server/server/session/daemon/daemon-self-updater.d.ts +32 -0
  431. package/dist/server/server/session/daemon/daemon-self-updater.js +56 -0
  432. package/dist/server/server/session/daemon/daemon-session.d.ts +76 -0
  433. package/dist/server/server/session/daemon/daemon-session.js +148 -0
  434. package/dist/server/server/session/daemon/diagnostics.d.ts +41 -0
  435. package/dist/server/server/session/daemon/diagnostics.js +431 -0
  436. package/dist/server/server/session/daemon/install-origin.d.ts +7 -0
  437. package/dist/server/server/session/daemon/install-origin.js +64 -0
  438. package/dist/server/server/session/daemon/npm-global-cli.d.ts +29 -0
  439. package/dist/server/server/session/daemon/npm-global-cli.js +98 -0
  440. package/dist/server/server/session/files/workspace-files-session.d.ts +43 -0
  441. package/dist/server/server/session/files/workspace-files-session.js +218 -0
  442. package/dist/server/server/session/git-mutation/git-mutation-service.d.ts +34 -0
  443. package/dist/server/server/session/git-mutation/git-mutation-service.js +71 -0
  444. package/dist/server/server/session/project-config/project-config-session.d.ts +34 -0
  445. package/dist/server/server/session/project-config/project-config-session.js +125 -0
  446. package/dist/server/server/session/provider/provider-catalog-session.d.ts +74 -0
  447. package/dist/server/server/session/provider/provider-catalog-session.js +343 -0
  448. package/dist/server/server/session/session-rename-route.d.ts +4 -0
  449. package/dist/server/server/session/session-rename-route.js +71 -0
  450. package/dist/server/server/session/session-rename-service.d.ts +25 -0
  451. package/dist/server/server/session/session-rename-service.js +76 -0
  452. package/dist/server/server/session/voice/voice-session.d.ts +166 -0
  453. package/dist/server/server/session/voice/voice-session.js +893 -0
  454. package/dist/server/server/session/voice/voice-turn-controller.d.ts +38 -0
  455. package/dist/server/server/session/voice/voice-turn-controller.js +393 -0
  456. package/dist/server/server/session/workspace-git-observer/workspace-git-observer-service.d.ts +36 -0
  457. package/dist/server/server/session/workspace-git-observer/workspace-git-observer-service.js +134 -0
  458. package/dist/server/server/session/workspace-provisioning/workspace-provisioning-service.d.ts +34 -0
  459. package/dist/server/server/session/workspace-provisioning/workspace-provisioning-service.js +190 -0
  460. package/dist/server/server/session/workspace-scripts/workspace-scripts-service.d.ts +41 -0
  461. package/dist/server/server/session/workspace-scripts/workspace-scripts-service.js +100 -0
  462. package/dist/server/server/session.d.ts +562 -0
  463. package/dist/server/server/session.js +5787 -0
  464. package/dist/server/server/speech/audio.d.ts +10 -0
  465. package/dist/server/server/speech/audio.js +101 -0
  466. package/dist/server/server/speech/provider-resolver.d.ts +3 -0
  467. package/dist/server/server/speech/provider-resolver.js +7 -0
  468. package/dist/server/server/speech/providers/local/config.d.ts +30 -0
  469. package/dist/server/server/speech/providers/local/config.js +134 -0
  470. package/dist/server/server/speech/providers/local/models.d.ts +11 -0
  471. package/dist/server/server/speech/providers/local/models.js +17 -0
  472. package/dist/server/server/speech/providers/local/runtime.d.ts +31 -0
  473. package/dist/server/server/speech/providers/local/runtime.js +135 -0
  474. package/dist/server/server/speech/providers/local/sherpa/assets/silero_vad.onnx +0 -0
  475. package/dist/server/server/speech/providers/local/sherpa/model-catalog.d.ts +56 -0
  476. package/dist/server/server/speech/providers/local/sherpa/model-catalog.js +74 -0
  477. package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts +15 -0
  478. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js +142 -0
  479. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.d.ts +49 -0
  480. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js +73 -0
  481. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.d.ts +9 -0
  482. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.js +107 -0
  483. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.d.ts +28 -0
  484. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js +137 -0
  485. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.d.ts +21 -0
  486. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js +133 -0
  487. package/dist/server/server/speech/providers/local/sherpa/sherpa-runtime-env.d.ts +18 -0
  488. package/dist/server/server/speech/providers/local/sherpa/sherpa-runtime-env.js +84 -0
  489. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.d.ts +21 -0
  490. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.js +101 -0
  491. package/dist/server/server/speech/providers/local/sherpa/silero-vad-provider.d.ts +19 -0
  492. package/dist/server/server/speech/providers/local/sherpa/silero-vad-provider.js +49 -0
  493. package/dist/server/server/speech/providers/local/sherpa/silero-vad-session.d.ts +38 -0
  494. package/dist/server/server/speech/providers/local/sherpa/silero-vad-session.js +176 -0
  495. package/dist/server/server/speech/providers/local/worker-bytes.d.ts +4 -0
  496. package/dist/server/server/speech/providers/local/worker-bytes.js +9 -0
  497. package/dist/server/server/speech/providers/local/worker-client.d.ts +93 -0
  498. package/dist/server/server/speech/providers/local/worker-client.js +611 -0
  499. package/dist/server/server/speech/providers/local/worker-process.d.ts +2 -0
  500. package/dist/server/server/speech/providers/local/worker-process.js +270 -0
  501. package/dist/server/server/speech/providers/local/worker-protocol.d.ts +95 -0
  502. package/dist/server/server/speech/providers/local/worker-protocol.js +2 -0
  503. package/dist/server/server/speech/providers/openai/config.d.ts +19 -0
  504. package/dist/server/server/speech/providers/openai/config.js +148 -0
  505. package/dist/server/server/speech/providers/openai/runtime.d.ts +29 -0
  506. package/dist/server/server/speech/providers/openai/runtime.js +102 -0
  507. package/dist/server/server/speech/providers/openai/stt.d.ts +23 -0
  508. package/dist/server/server/speech/providers/openai/stt.js +209 -0
  509. package/dist/server/server/speech/providers/openai/tts.d.ts +19 -0
  510. package/dist/server/server/speech/providers/openai/tts.js +48 -0
  511. package/dist/server/server/speech/speech-config-resolver.d.ts +11 -0
  512. package/dist/server/server/speech/speech-config-resolver.js +132 -0
  513. package/dist/server/server/speech/speech-provider.d.ts +59 -0
  514. package/dist/server/server/speech/speech-provider.js +2 -0
  515. package/dist/server/server/speech/speech-runtime.d.ts +45 -0
  516. package/dist/server/server/speech/speech-runtime.js +562 -0
  517. package/dist/server/server/speech/speech-types.d.ts +22 -0
  518. package/dist/server/server/speech/speech-types.js +8 -0
  519. package/dist/server/server/speech/turn-detection-provider.d.ts +23 -0
  520. package/dist/server/server/speech/turn-detection-provider.js +2 -0
  521. package/dist/server/server/uploads/file-attachment-validation.d.ts +61 -0
  522. package/dist/server/server/uploads/file-attachment-validation.js +56 -0
  523. package/dist/server/server/uploads/staging-service.d.ts +91 -0
  524. package/dist/server/server/uploads/staging-service.js +144 -0
  525. package/dist/server/server/utils/diff-highlighter.d.ts +57 -0
  526. package/dist/server/server/utils/diff-highlighter.js +279 -0
  527. package/dist/server/server/voice/voice-turn-controller.d.ts +38 -0
  528. package/dist/server/server/voice/voice-turn-controller.js +393 -0
  529. package/dist/server/server/voice-config.d.ts +15 -0
  530. package/dist/server/server/voice-config.js +54 -0
  531. package/dist/server/server/voice-permission-policy.d.ts +4 -0
  532. package/dist/server/server/voice-permission-policy.js +13 -0
  533. package/dist/server/server/voice-types.d.ts +12 -0
  534. package/dist/server/server/voice-types.js +2 -0
  535. package/dist/server/server/watch/composer-dictate.d.ts +42 -0
  536. package/dist/server/server/watch/composer-dictate.js +37 -0
  537. package/dist/server/server/watch/notification-action.d.ts +38 -0
  538. package/dist/server/server/watch/notification-action.js +54 -0
  539. package/dist/server/server/watch/notification-registry.d.ts +16 -0
  540. package/dist/server/server/watch/notification-registry.js +2 -0
  541. package/dist/server/server/web-ui.d.ts +10 -0
  542. package/dist/server/server/web-ui.js +205 -0
  543. package/dist/server/server/websocket/runtime-metrics.d.ts +94 -0
  544. package/dist/server/server/websocket/runtime-metrics.js +148 -0
  545. package/dist/server/server/websocket-server.d.ts +193 -0
  546. package/dist/server/server/websocket-server.js +1768 -0
  547. package/dist/server/server/workflow/workflow-feature.d.ts +2 -0
  548. package/dist/server/server/workflow/workflow-feature.js +8 -0
  549. package/dist/server/server/workflow/workflow-http-routes.d.ts +27 -0
  550. package/dist/server/server/workflow/workflow-http-routes.js +97 -0
  551. package/dist/server/server/workflow/workflow-manager.d.ts +228 -0
  552. package/dist/server/server/workflow/workflow-manager.js +444 -0
  553. package/dist/server/server/workflow/workflow-progress.d.ts +95 -0
  554. package/dist/server/server/workflow/workflow-progress.js +178 -0
  555. package/dist/server/server/workflow/workflow-storage.d.ts +36 -0
  556. package/dist/server/server/workflow/workflow-storage.js +143 -0
  557. package/dist/server/server/workspace/all-sessions-route.d.ts +32 -0
  558. package/dist/server/server/workspace/all-sessions-route.js +138 -0
  559. package/dist/server/server/workspace/session-openable.d.ts +16 -0
  560. package/dist/server/server/workspace/session-openable.js +48 -0
  561. package/dist/server/server/workspace/sessions-route.d.ts +15 -0
  562. package/dist/server/server/workspace/sessions-route.js +108 -0
  563. package/dist/server/server/workspace/sessions-search-route.d.ts +68 -0
  564. package/dist/server/server/workspace/sessions-search-route.js +203 -0
  565. package/dist/server/server/workspace-archive-service.d.ts +65 -0
  566. package/dist/server/server/workspace-archive-service.js +229 -0
  567. package/dist/server/server/workspace-auto-name.d.ts +55 -0
  568. package/dist/server/server/workspace-auto-name.js +128 -0
  569. package/dist/server/server/workspace-bootstrap-dedupe.d.ts +34 -0
  570. package/dist/server/server/workspace-bootstrap-dedupe.js +23 -0
  571. package/dist/server/server/workspace-directory.d.ts +93 -0
  572. package/dist/server/server/workspace-directory.js +422 -0
  573. package/dist/server/server/workspace-git-metadata.d.ts +24 -0
  574. package/dist/server/server/workspace-git-metadata.js +55 -0
  575. package/dist/server/server/workspace-git-service.d.ts +257 -0
  576. package/dist/server/server/workspace-git-service.js +1386 -0
  577. package/dist/server/server/workspace-reconciliation-service.d.ts +57 -0
  578. package/dist/server/server/workspace-reconciliation-service.js +278 -0
  579. package/dist/server/server/workspace-registry-bootstrap.d.ts +13 -0
  580. package/dist/server/server/workspace-registry-bootstrap.js +121 -0
  581. package/dist/server/server/workspace-registry-model.d.ts +55 -0
  582. package/dist/server/server/workspace-registry-model.js +190 -0
  583. package/dist/server/server/workspace-registry.d.ts +117 -0
  584. package/dist/server/server/workspace-registry.js +194 -0
  585. package/dist/server/server/workspace-script-runtime-store.d.ts +28 -0
  586. package/dist/server/server/workspace-script-runtime-store.js +78 -0
  587. package/dist/server/server/workspace-service-env.d.ts +18 -0
  588. package/dist/server/server/workspace-service-env.js +85 -0
  589. package/dist/server/server/workspace-service-port-registry.d.ts +19 -0
  590. package/dist/server/server/workspace-service-port-registry.js +59 -0
  591. package/dist/server/server/worktree/commands.d.ts +55 -0
  592. package/dist/server/server/worktree/commands.js +107 -0
  593. package/dist/server/server/worktree-bootstrap.d.ts +76 -0
  594. package/dist/server/server/worktree-bootstrap.js +720 -0
  595. package/dist/server/server/worktree-branch-name-generator.d.ts +35 -0
  596. package/dist/server/server/worktree-branch-name-generator.js +91 -0
  597. package/dist/server/server/worktree-core.d.ts +31 -0
  598. package/dist/server/server/worktree-core.js +105 -0
  599. package/dist/server/server/worktree-errors.d.ts +12 -0
  600. package/dist/server/server/worktree-errors.js +28 -0
  601. package/dist/server/server/worktree-session.d.ts +152 -0
  602. package/dist/server/server/worktree-session.js +466 -0
  603. package/dist/server/services/github-service.d.ts +337 -0
  604. package/dist/server/services/github-service.js +2101 -0
  605. package/dist/server/services/quota-fetcher/manifest.d.ts +4 -0
  606. package/dist/server/services/quota-fetcher/manifest.js +52 -0
  607. package/dist/server/services/quota-fetcher/provider.d.ts +17 -0
  608. package/dist/server/services/quota-fetcher/provider.js +2 -0
  609. package/dist/server/services/quota-fetcher/providers/claude.d.ts +26 -0
  610. package/dist/server/services/quota-fetcher/providers/claude.js +217 -0
  611. package/dist/server/services/quota-fetcher/providers/codex.d.ts +23 -0
  612. package/dist/server/services/quota-fetcher/providers/codex.js +211 -0
  613. package/dist/server/services/quota-fetcher/providers/copilot.d.ts +17 -0
  614. package/dist/server/services/quota-fetcher/providers/copilot.js +75 -0
  615. package/dist/server/services/quota-fetcher/providers/cursor.d.ts +17 -0
  616. package/dist/server/services/quota-fetcher/providers/cursor.js +123 -0
  617. package/dist/server/services/quota-fetcher/providers/grok.d.ts +18 -0
  618. package/dist/server/services/quota-fetcher/providers/grok.js +89 -0
  619. package/dist/server/services/quota-fetcher/providers/kimi.d.ts +20 -0
  620. package/dist/server/services/quota-fetcher/providers/kimi.js +89 -0
  621. package/dist/server/services/quota-fetcher/providers/minimax.d.ts +29 -0
  622. package/dist/server/services/quota-fetcher/providers/minimax.js +227 -0
  623. package/dist/server/services/quota-fetcher/providers/zai.d.ts +17 -0
  624. package/dist/server/services/quota-fetcher/providers/zai.js +58 -0
  625. package/dist/server/services/quota-fetcher/service.d.ts +28 -0
  626. package/dist/server/services/quota-fetcher/service.js +58 -0
  627. package/dist/server/services/quota-fetcher/usage.d.ts +22 -0
  628. package/dist/server/services/quota-fetcher/usage.js +49 -0
  629. package/dist/server/terminal/activity/terminal-activity-tracker.d.ts +20 -0
  630. package/dist/server/terminal/activity/terminal-activity-tracker.js +59 -0
  631. package/dist/server/terminal/agent-hooks/agent-hook-installer.d.ts +62 -0
  632. package/dist/server/terminal/agent-hooks/agent-hook-installer.js +117 -0
  633. package/dist/server/terminal/agent-hooks/claude/claude-settings.d.ts +7 -0
  634. package/dist/server/terminal/agent-hooks/claude/claude-settings.js +88 -0
  635. package/dist/server/terminal/agent-hooks/claude/claude.d.ts +4 -0
  636. package/dist/server/terminal/agent-hooks/claude/claude.js +47 -0
  637. package/dist/server/terminal/agent-hooks/codex/codex-settings.d.ts +7 -0
  638. package/dist/server/terminal/agent-hooks/codex/codex-settings.js +99 -0
  639. package/dist/server/terminal/agent-hooks/codex/codex.d.ts +4 -0
  640. package/dist/server/terminal/agent-hooks/codex/codex.js +30 -0
  641. package/dist/server/terminal/agent-hooks/opencode/opencode-plugin.d.ts +4 -0
  642. package/dist/server/terminal/agent-hooks/opencode/opencode-plugin.js +46 -0
  643. package/dist/server/terminal/agent-hooks/opencode/opencode.d.ts +3 -0
  644. package/dist/server/terminal/agent-hooks/opencode/opencode.js +23 -0
  645. package/dist/server/terminal/agent-hooks/provider-registry.d.ts +24 -0
  646. package/dist/server/terminal/agent-hooks/provider-registry.js +36 -0
  647. package/dist/server/terminal/agent-hooks/terminal-agent-hook-setting.d.ts +10 -0
  648. package/dist/server/terminal/agent-hooks/terminal-agent-hook-setting.js +26 -0
  649. package/dist/server/terminal/shell-integration/zsh/.zshenv +17 -0
  650. package/dist/server/terminal/shell-integration/zsh/paseo-integration.zsh +32 -0
  651. package/dist/server/terminal/terminal-capture.d.ts +12 -0
  652. package/dist/server/terminal/terminal-capture.js +43 -0
  653. package/dist/server/terminal/terminal-manager-factory.d.ts +6 -0
  654. package/dist/server/terminal/terminal-manager-factory.js +5 -0
  655. package/dist/server/terminal/terminal-manager.d.ts +78 -0
  656. package/dist/server/terminal/terminal-manager.js +319 -0
  657. package/dist/server/terminal/terminal-output-coalescer.d.ts +34 -0
  658. package/dist/server/terminal/terminal-output-coalescer.js +73 -0
  659. package/dist/server/terminal/terminal-restore.d.ts +18 -0
  660. package/dist/server/terminal/terminal-restore.js +52 -0
  661. package/dist/server/terminal/terminal-session-controller.d.ts +85 -0
  662. package/dist/server/terminal/terminal-session-controller.js +819 -0
  663. package/dist/server/terminal/terminal-ts-loader.mjs +20 -0
  664. package/dist/server/terminal/terminal-worker-process.d.ts +2 -0
  665. package/dist/server/terminal/terminal-worker-process.js +304 -0
  666. package/dist/server/terminal/terminal-worker-protocol.d.ts +119 -0
  667. package/dist/server/terminal/terminal-worker-protocol.js +2 -0
  668. package/dist/server/terminal/terminal.d.ts +151 -0
  669. package/dist/server/terminal/terminal.js +1161 -0
  670. package/dist/server/terminal/worker-terminal-manager.d.ts +20 -0
  671. package/dist/server/terminal/worker-terminal-manager.js +688 -0
  672. package/dist/server/utils/build-metadata-prompt.d.ts +19 -0
  673. package/dist/server/utils/build-metadata-prompt.js +29 -0
  674. package/dist/server/utils/checkout-git.d.ts +253 -0
  675. package/dist/server/utils/checkout-git.js +2238 -0
  676. package/dist/server/utils/directory-suggestions.d.ts +26 -0
  677. package/dist/server/utils/directory-suggestions.js +830 -0
  678. package/dist/server/utils/git-rev-parse-path.d.ts +3 -0
  679. package/dist/server/utils/git-rev-parse-path.js +21 -0
  680. package/dist/server/utils/github-remote.d.ts +9 -0
  681. package/dist/server/utils/github-remote.js +62 -0
  682. package/dist/server/utils/paseo-config-file.d.ts +30 -0
  683. package/dist/server/utils/paseo-config-file.js +90 -0
  684. package/dist/server/utils/path.d.ts +18 -0
  685. package/dist/server/utils/path.js +123 -0
  686. package/dist/server/utils/project-icon.d.ts +39 -0
  687. package/dist/server/utils/project-icon.js +364 -0
  688. package/dist/server/utils/promise-timeout.d.ts +9 -0
  689. package/dist/server/utils/promise-timeout.js +25 -0
  690. package/dist/server/utils/run-git-command.d.ts +37 -0
  691. package/dist/server/utils/run-git-command.js +232 -0
  692. package/dist/server/utils/spawn.d.ts +33 -0
  693. package/dist/server/utils/spawn.js +71 -0
  694. package/dist/server/utils/string-command-shell.d.ts +10 -0
  695. package/dist/server/utils/string-command-shell.js +21 -0
  696. package/dist/server/utils/tree-kill.d.ts +18 -0
  697. package/dist/server/utils/tree-kill.js +77 -0
  698. package/dist/server/utils/windows-command.d.ts +15 -0
  699. package/dist/server/utils/windows-command.js +41 -0
  700. package/dist/server/utils/worktree-metadata.d.ts +39 -0
  701. package/dist/server/utils/worktree-metadata.js +174 -0
  702. package/dist/server/utils/worktree.d.ts +205 -0
  703. package/dist/server/utils/worktree.js +1119 -0
  704. package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css +1 -0
  705. package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.br +0 -0
  706. package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.gz +0 -0
  707. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-127b8d280bb901a810b0c9fa094d4945.js +5 -0
  708. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-127b8d280bb901a810b0c9fa094d4945.js.br +0 -0
  709. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-127b8d280bb901a810b0c9fa094d4945.js.gz +0 -0
  710. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-127b8d280bb901a810b0c9fa094d4945.js.map.br +0 -0
  711. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-127b8d280bb901a810b0c9fa094d4945.js.map.gz +0 -0
  712. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-0e6ed782245f4dda6e0a73171a133d45.js +3 -0
  713. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-0e6ed782245f4dda6e0a73171a133d45.js.br +0 -0
  714. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-0e6ed782245f4dda6e0a73171a133d45.js.gz +0 -0
  715. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-0e6ed782245f4dda6e0a73171a133d45.js.map.br +0 -0
  716. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-0e6ed782245f4dda6e0a73171a133d45.js.map.gz +0 -0
  717. package/dist/server/web-ui/_expo/static/js/web/index-b31c4d6fb6383454381e43410736cb89.js +16533 -0
  718. package/dist/server/web-ui/_expo/static/js/web/index-b31c4d6fb6383454381e43410736cb89.js.br +0 -0
  719. package/dist/server/web-ui/_expo/static/js/web/index-b31c4d6fb6383454381e43410736cb89.js.gz +0 -0
  720. package/dist/server/web-ui/_expo/static/js/web/index-b31c4d6fb6383454381e43410736cb89.js.map.br +0 -0
  721. package/dist/server/web-ui/_expo/static/js/web/index-b31c4d6fb6383454381e43410736cb89.js.map.gz +0 -0
  722. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-0d03551c4ea3780f5b81db6a7aebb017.js +3 -0
  723. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-0d03551c4ea3780f5b81db6a7aebb017.js.br +0 -0
  724. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-0d03551c4ea3780f5b81db6a7aebb017.js.gz +0 -0
  725. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-0d03551c4ea3780f5b81db6a7aebb017.js.map.br +0 -0
  726. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-0d03551c4ea3780f5b81db6a7aebb017.js.map.gz +0 -0
  727. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-feda55adc8031781db083743ca73cbc3.js +5 -0
  728. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-feda55adc8031781db083743ca73cbc3.js.br +0 -0
  729. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-feda55adc8031781db083743ca73cbc3.js.gz +0 -0
  730. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-feda55adc8031781db083743ca73cbc3.js.map.br +0 -0
  731. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-feda55adc8031781db083743ca73cbc3.js.map.gz +0 -0
  732. package/dist/server/web-ui/_expo/static/js/web/profiler-593c9481537cb12f4b6c17ef48bcba1c.js +11 -0
  733. package/dist/server/web-ui/_expo/static/js/web/profiler-593c9481537cb12f4b6c17ef48bcba1c.js.br +0 -0
  734. package/dist/server/web-ui/_expo/static/js/web/profiler-593c9481537cb12f4b6c17ef48bcba1c.js.gz +0 -0
  735. package/dist/server/web-ui/_expo/static/js/web/profiler-593c9481537cb12f4b6c17ef48bcba1c.js.map.br +0 -0
  736. package/dist/server/web-ui/_expo/static/js/web/profiler-593c9481537cb12f4b6c17ef48bcba1c.js.map.gz +0 -0
  737. package/dist/server/web-ui/_expo/static/js/web/startRecording-533a4fe1acfc37460ac2912610062d3a.js +60 -0
  738. package/dist/server/web-ui/_expo/static/js/web/startRecording-533a4fe1acfc37460ac2912610062d3a.js.br +0 -0
  739. package/dist/server/web-ui/_expo/static/js/web/startRecording-533a4fe1acfc37460ac2912610062d3a.js.gz +0 -0
  740. package/dist/server/web-ui/_expo/static/js/web/startRecording-533a4fe1acfc37460ac2912610062d3a.js.map.br +0 -0
  741. package/dist/server/web-ui/_expo/static/js/web/startRecording-533a4fe1acfc37460ac2912610062d3a.js.map.gz +0 -0
  742. package/dist/server/web-ui/apple-touch-icon.png +0 -0
  743. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon-mask.0a328cd9c1afd0afe8e3b1ec5165b1b4.png +0 -0
  744. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon.35ba0eaec5a4f5ed12ca16fabeae451d.png +0 -0
  745. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55.png +0 -0
  746. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@2x.png +0 -0
  747. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@3x.png +0 -0
  748. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@4x.png +0 -0
  749. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7.png +0 -0
  750. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@2x.png +0 -0
  751. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@3x.png +0 -0
  752. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@4x.png +0 -0
  753. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/search-icon.286d67d3f74808a60a78d3ebf1a5fb57.png +0 -0
  754. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/arrow_down.017bc6ba3fc25503e5eb5e53826d48a8.png +0 -0
  755. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/error.d1ea1496f9057eb392d5bbf3732a61b7.png +0 -0
  756. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/file.19eeb73b9593a38f8e9f418337fc7d10.png +0 -0
  757. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/forward.d8b800c443b8972542883e0b9de2bdc6.png +0 -0
  758. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/pkg.ab19f4cbc543357183a20571f68380a3.png +0 -0
  759. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/sitemap.412dd9275b6b48ad28f5e3d81bb1f626.png +0 -0
  760. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/unmatched.20e71bdf79e3a97bf55fd9e164041578.png +0 -0
  761. package/dist/server/web-ui/assets/assets/images/editor-apps/antigravity.6e91a685c33435e0b466a56db86cf141.png +0 -0
  762. package/dist/server/web-ui/assets/assets/images/editor-apps/cursor.c31d6bce4fe9aadc3fe59962f4c4fcf3.png +0 -0
  763. package/dist/server/web-ui/assets/assets/images/editor-apps/file-explorer.3e15e8f72c825c85ce336bcb0cdef776.png +0 -0
  764. package/dist/server/web-ui/assets/assets/images/editor-apps/finder.7f68fc2c475621a672e1be09309d5567.png +0 -0
  765. package/dist/server/web-ui/assets/assets/images/editor-apps/vscode.832bdb4c685d930f1c864c793703600b.png +0 -0
  766. package/dist/server/web-ui/assets/assets/images/editor-apps/webstorm.aa5dc2cd8c20cc0a155c4c5c5ab3c5f5.png +0 -0
  767. package/dist/server/web-ui/assets/assets/images/editor-apps/zed.f3a670b7f9aa226da4fe53fb86f1abbd.png +0 -0
  768. package/dist/server/web-ui/assets/assets/images/favicon-dark-attention.882b3a27dcb2073e9e31b334f9ed9728.png +0 -0
  769. package/dist/server/web-ui/assets/assets/images/favicon-dark-running.8112342ff0d39e047a7f8d4fad9402f3.png +0 -0
  770. package/dist/server/web-ui/assets/assets/images/favicon-dark.8005ed36ac07a5a7c60de25780897bd4.png +0 -0
  771. package/dist/server/web-ui/assets/assets/images/favicon-light-attention.882b3a27dcb2073e9e31b334f9ed9728.png +0 -0
  772. package/dist/server/web-ui/assets/assets/images/favicon-light-running.8112342ff0d39e047a7f8d4fad9402f3.png +0 -0
  773. package/dist/server/web-ui/assets/assets/images/favicon-light.8005ed36ac07a5a7c60de25780897bd4.png +0 -0
  774. package/dist/server/web-ui/assets/assets/images/notification-icon.3bf81d33ddbf380606bdd248ba83e158.png +0 -0
  775. package/dist/server/web-ui/favicon.ico +0 -0
  776. package/dist/server/web-ui/index.html +90 -0
  777. package/dist/server/web-ui/index.html.br +0 -0
  778. package/dist/server/web-ui/index.html.gz +0 -0
  779. package/dist/server/web-ui/manifest.json +27 -0
  780. package/dist/server/web-ui/manifest.json.br +0 -0
  781. package/dist/server/web-ui/manifest.json.gz +0 -0
  782. package/dist/server/web-ui/metadata.json +1 -0
  783. package/dist/server/web-ui/metadata.json.br +1 -0
  784. package/dist/server/web-ui/metadata.json.gz +0 -0
  785. package/dist/server/web-ui/pwa-icon-192.png +0 -0
  786. package/dist/server/web-ui/pwa-icon-512.png +0 -0
  787. package/dist/server/web-ui/robots.txt +2 -0
  788. package/dist/server/web-ui/service-worker.js +87 -0
  789. package/dist/server/web-ui/service-worker.js.br +0 -0
  790. package/dist/server/web-ui/service-worker.js.gz +0 -0
  791. package/dist/src/executable-resolution/executable-resolution.js +118 -0
  792. package/dist/src/executable-resolution/windows.js +65 -0
  793. package/dist/src/server/agent/provider-launch-config.js +183 -0
  794. package/dist/src/server/paseo-env.js +53 -0
  795. package/dist/src/server/paseo-home.js +19 -0
  796. package/dist/src/server/persisted-config.js +402 -0
  797. package/dist/src/server/pid-lock.js +170 -0
  798. package/dist/src/server/private-files.js +37 -0
  799. package/dist/src/server/speech/providers/local/sherpa/sherpa-runtime-env.js +84 -0
  800. package/dist/src/terminal/shell-integration/zsh/.zshenv +17 -0
  801. package/dist/src/terminal/shell-integration/zsh/paseo-integration.zsh +32 -0
  802. package/dist/src/utils/spawn.js +71 -0
  803. package/dist/src/utils/windows-command.js +41 -0
  804. package/package.json +143 -0
@@ -0,0 +1,2955 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { resolve } from "node:path";
3
+ import { stat } from "node:fs/promises";
4
+ import { AGENT_LIFECYCLE_STATUSES, } from "@hyperdrive.bot/paseo-protocol/agent-lifecycle";
5
+ import { getParentAgentIdFromLabels, isDelegatedAgent, PARENT_AGENT_ID_LABEL, } from "@hyperdrive.bot/paseo-protocol/agent-labels";
6
+ import { z } from "zod";
7
+ import { getAgentStreamEventTurnId, } from "./agent-sdk-types.js";
8
+ import { buildArchivedAgentRecord } from "./agent-archive.js";
9
+ import { InMemoryAgentTimelineStore, } from "./agent-timeline-store.js";
10
+ import { AGENT_STREAM_COALESCE_DEFAULT_WINDOW_MS, AgentStreamCoalescer, } from "./agent-stream-coalescer.js";
11
+ import { ForegroundRunState } from "./foreground-run-state.js";
12
+ import { getAgentProviderDefinition } from "@hyperdrive.bot/paseo-protocol/provider-manifest";
13
+ import { invokeRewindCapability } from "./rewind/rewind.js";
14
+ import { isSystemInjectedEnvelope } from "./agent-prompt.js";
15
+ import { stripInternalPaseoMcpServer, withRuntimePaseoMcpServer } from "./runtime-mcp-config.js";
16
+ import { resolveCreateAgentTitles } from "./create-agent-title.js";
17
+ const RELOAD_SESSION_CLOSE_TIMEOUT_MS = 3000;
18
+ const INTERRUPT_SESSION_TIMEOUT_MS = 2000;
19
+ const STORED_AGENT_CAPABILITIES = {
20
+ supportsStreaming: false,
21
+ supportsSessionPersistence: true,
22
+ supportsDynamicModes: false,
23
+ supportsMcpServers: false,
24
+ supportsReasoningStream: false,
25
+ supportsToolInvocations: true,
26
+ supportsRewindConversation: false,
27
+ supportsRewindFiles: false,
28
+ supportsRewindBoth: false,
29
+ };
30
+ function formatProviderList(providers) {
31
+ return providers.length > 0 ? providers.join(", ") : "none";
32
+ }
33
+ function buildStoredAgentConfig(record) {
34
+ const config = {
35
+ provider: record.provider,
36
+ cwd: record.cwd,
37
+ };
38
+ if (!record.config) {
39
+ return config;
40
+ }
41
+ if (record.config.modeId != null)
42
+ config.modeId = record.config.modeId;
43
+ if (record.config.model != null)
44
+ config.model = record.config.model;
45
+ if (record.config.thinkingOptionId != null) {
46
+ config.thinkingOptionId = record.config.thinkingOptionId;
47
+ }
48
+ if (record.config.featureValues != null) {
49
+ config.featureValues = record.config.featureValues;
50
+ }
51
+ if (record.config.extra != null)
52
+ config.extra = record.config.extra;
53
+ if (record.config.systemPrompt != null) {
54
+ config.systemPrompt = record.config.systemPrompt;
55
+ }
56
+ if (record.config.mcpServers != null)
57
+ config.mcpServers = record.config.mcpServers;
58
+ return stripInternalPaseoMcpServer(config);
59
+ }
60
+ export { AGENT_LIFECYCLE_STATUSES };
61
+ function resolveInitialAttention(input) {
62
+ if (input == null || !input.requiresAttention) {
63
+ return { requiresAttention: false };
64
+ }
65
+ return {
66
+ requiresAttention: true,
67
+ attentionReason: input.attentionReason,
68
+ attentionTimestamp: new Date(input.attentionTimestamp),
69
+ };
70
+ }
71
+ const SYSTEM_ERROR_PREFIX = "[System Error]";
72
+ function attachPersistenceCwd(handle, cwd) {
73
+ if (!handle) {
74
+ return null;
75
+ }
76
+ return {
77
+ ...handle,
78
+ metadata: {
79
+ ...handle.metadata,
80
+ cwd,
81
+ },
82
+ };
83
+ }
84
+ const BUSY_STATUSES = new Set(["initializing", "running"]);
85
+ const AgentIdSchema = z.guid();
86
+ function isAgentBusy(status) {
87
+ return BUSY_STATUSES.has(status);
88
+ }
89
+ function isTurnTerminalEvent(event) {
90
+ return (event.type === "turn_completed" ||
91
+ event.type === "turn_failed" ||
92
+ event.type === "turn_canceled");
93
+ }
94
+ function abortMessage(reason, fallbackMessage) {
95
+ if (typeof reason === "string")
96
+ return reason;
97
+ if (reason instanceof Error)
98
+ return reason.message;
99
+ return fallbackMessage;
100
+ }
101
+ function createAbortError(signal, fallbackMessage) {
102
+ const message = abortMessage(signal?.reason, fallbackMessage);
103
+ return Object.assign(new Error(message), { name: "AbortError" });
104
+ }
105
+ function validateAgentId(agentId, source) {
106
+ const result = AgentIdSchema.safeParse(agentId);
107
+ if (!result.success) {
108
+ throw new Error(`${source}: agentId must be a UUID`);
109
+ }
110
+ return result.data;
111
+ }
112
+ function applyLabelPatch(labels, patch) {
113
+ const nextLabels = { ...labels };
114
+ for (const [key, value] of Object.entries(patch)) {
115
+ if (value === null) {
116
+ delete nextLabels[key];
117
+ }
118
+ else {
119
+ nextLabels[key] = value;
120
+ }
121
+ }
122
+ return nextLabels;
123
+ }
124
+ function buildExplicitTimelineSeedForRegister(now, options) {
125
+ const hasTimeline = Boolean(options?.timeline?.length);
126
+ const hasTimelineRows = Boolean(options?.timelineRows?.length);
127
+ const hasTimelineNextSeq = options?.timelineNextSeq !== undefined;
128
+ if (!hasTimeline && !hasTimelineRows && !hasTimelineNextSeq) {
129
+ return null;
130
+ }
131
+ return {
132
+ items: options?.timeline,
133
+ rows: options?.timelineRows,
134
+ nextSeq: options?.timelineNextSeq,
135
+ timestamp: (options?.updatedAt ?? options?.createdAt ?? now).toISOString(),
136
+ };
137
+ }
138
+ function buildImportedTimelineRows(entries) {
139
+ const rows = [];
140
+ for (const entry of entries) {
141
+ if (entry.item.type === "user_message" && isSystemInjectedEnvelope(entry.item.text)) {
142
+ continue;
143
+ }
144
+ rows.push({
145
+ seq: rows.length + 1,
146
+ timestamp: entry.timestamp ?? new Date().toISOString(),
147
+ item: entry.item,
148
+ });
149
+ }
150
+ return rows;
151
+ }
152
+ function resolveImportedAgentTitle(config, timelineRows) {
153
+ const initialPrompt = getFirstUserMessageTextFromRows(timelineRows);
154
+ if (!initialPrompt) {
155
+ return null;
156
+ }
157
+ const { explicitTitle, provisionalTitle } = resolveCreateAgentTitles({
158
+ configTitle: config.title,
159
+ initialPrompt,
160
+ });
161
+ return explicitTitle ?? provisionalTitle ?? null;
162
+ }
163
+ function getFirstUserMessageTextFromRows(rows) {
164
+ for (const row of rows) {
165
+ const item = row.item;
166
+ if (item.type !== "user_message") {
167
+ continue;
168
+ }
169
+ const text = item.text.trim();
170
+ if (text) {
171
+ return text;
172
+ }
173
+ }
174
+ return null;
175
+ }
176
+ export class AgentManager {
177
+ constructor(options) {
178
+ this.clients = new Map();
179
+ this.providerEnabled = new Map();
180
+ /**
181
+ * Per-provider hot-swap metadata indexed by provider id. Populated by
182
+ * {@link updateProviderRegistry} from the daemon's ProviderEnabledMap.
183
+ * Empty when callers (mostly tests) pass only the bare `{enabled}` shape.
184
+ */
185
+ this.providerSwapMetadata = new Map();
186
+ this.agents = new Map();
187
+ this.timelineStore = new InMemoryAgentTimelineStore();
188
+ this.agentsAwaitingInitialSnapshotPersist = new Set();
189
+ this.sessionEventTails = new Map();
190
+ this.foregroundRuns = new ForegroundRunState();
191
+ this.subscribers = new Set();
192
+ this.previousStatuses = new Map();
193
+ this.backgroundTasks = new Set();
194
+ this.attentionListeners = new Set();
195
+ this.paseoToolsEnabled = true;
196
+ this.paseoToolCatalogFactory = null;
197
+ // Restored 2026-06-03: appendSystemPrompt and onAgentArchived references
198
+ // survived in this file's methods but the field declarations were dropped
199
+ // during the OSS hot-swap merge.
200
+ this.appendSystemPrompt = "";
201
+ this.idFactory = options?.idFactory ?? (() => randomUUID());
202
+ this.registry = options?.registry;
203
+ this.durableTimelineStore = options?.durableTimelineStore;
204
+ if (options?.onAgentAttention) {
205
+ this.attentionListeners.add(options.onAgentAttention);
206
+ }
207
+ this.onWorkspaceStateMayHaveChanged = options?.onWorkspaceStateMayHaveChanged;
208
+ this.mcpBaseUrl = options?.mcpBaseUrl ?? null;
209
+ this.mcpAuthToken = options?.mcpAuthToken ?? null;
210
+ this.configurePaseoTools(options);
211
+ this.appendSystemPrompt = options.appendSystemPrompt ?? "";
212
+ this.logger = options.logger.child({ module: "agent", component: "agent-manager" });
213
+ this.rescueTimeouts = {
214
+ reloadSessionCloseMs: options.rescueTimeouts?.reloadSessionCloseMs ?? RELOAD_SESSION_CLOSE_TIMEOUT_MS,
215
+ interruptSessionMs: options.rescueTimeouts?.interruptSessionMs ?? INTERRUPT_SESSION_TIMEOUT_MS,
216
+ };
217
+ this.agentStreamCoalescer = new AgentStreamCoalescer({
218
+ windowMs: options.agentStreamCoalesceWindowMs ?? AGENT_STREAM_COALESCE_DEFAULT_WINDOW_MS,
219
+ timers: { setTimeout, clearTimeout },
220
+ onFlush: ({ agentId, item, provider, turnId }) => {
221
+ const event = this.recordAndDispatchTimelineItem(agentId, item, provider, turnId);
222
+ this.notifyForegroundTurnWaiters(agentId, event);
223
+ },
224
+ });
225
+ this.updateProviderRegistry({
226
+ providerDefinitions: options.providerDefinitions ?? {},
227
+ clients: options.clients ?? {},
228
+ });
229
+ }
230
+ configurePaseoTools(options) {
231
+ this.paseoToolsEnabled = options.paseoToolsEnabled ?? true;
232
+ this.paseoToolCatalogFactory = options.paseoToolCatalogFactory ?? null;
233
+ }
234
+ registerClient(provider, client) {
235
+ this.clients.set(provider, client);
236
+ }
237
+ updateProviderRegistry(input) {
238
+ for (const [provider, definition] of Object.entries(input.providerDefinitions)) {
239
+ if (definition) {
240
+ this.providerEnabled.set(provider, definition.enabled);
241
+ this.providerSwapMetadata.set(provider, {
242
+ wireFamily: definition.wireFamily,
243
+ compatibleProviders: definition.compatibleProviders ?? [],
244
+ });
245
+ }
246
+ }
247
+ for (const [provider, client] of Object.entries(input.clients)) {
248
+ if (client) {
249
+ this.clients.set(provider, client);
250
+ }
251
+ }
252
+ }
253
+ getRegisteredProviderIds() {
254
+ return Array.from(this.clients.keys());
255
+ }
256
+ /**
257
+ * @deprecated Use {@link addAgentAttentionListener} for multi-subscriber registration.
258
+ * Retained for back-compat: replaces all currently-registered listeners with `callback`.
259
+ */
260
+ setAgentAttentionCallback(callback) {
261
+ this.attentionListeners.clear();
262
+ this.attentionListeners.add(callback);
263
+ }
264
+ /**
265
+ * Register an additional attention listener. Returns an unsubscribe function.
266
+ * Multiple subscribers are supported (e.g. WebSocketServer + pushTrigger emitter).
267
+ */
268
+ addAgentAttentionListener(listener) {
269
+ this.attentionListeners.add(listener);
270
+ return () => {
271
+ this.attentionListeners.delete(listener);
272
+ };
273
+ }
274
+ setAgentArchivedCallback(callback) {
275
+ this.onAgentArchived = callback;
276
+ }
277
+ setMcpBaseUrl(url) {
278
+ this.mcpBaseUrl = url;
279
+ }
280
+ setPaseoToolsEnabled(enabled) {
281
+ this.paseoToolsEnabled = enabled;
282
+ }
283
+ setPaseoToolCatalogFactory(factory) {
284
+ this.paseoToolCatalogFactory = factory;
285
+ }
286
+ /**
287
+ * Capability token the daemon's own MCP clients must present to the Agent MCP
288
+ * endpoint when a daemon password is configured. Read by the per-client
289
+ * session to authenticate its own MCP connection. Stays in the daemon — never
290
+ * sent to remote clients.
291
+ */
292
+ getMcpAuthToken() {
293
+ return this.mcpAuthToken;
294
+ }
295
+ setAppendSystemPrompt(prompt) {
296
+ this.appendSystemPrompt = prompt ?? "";
297
+ }
298
+ getMetricsSnapshot() {
299
+ const byLifecycle = {};
300
+ let withActiveForegroundTurn = 0;
301
+ let totalItems = 0;
302
+ let maxItemsPerAgent = 0;
303
+ for (const agent of this.agents.values()) {
304
+ byLifecycle[agent.lifecycle] = (byLifecycle[agent.lifecycle] ?? 0) + 1;
305
+ if (agent.activeForegroundTurnId !== null) {
306
+ withActiveForegroundTurn++;
307
+ }
308
+ if (!this.timelineStore.has(agent.id)) {
309
+ continue;
310
+ }
311
+ const len = this.timelineStore.getItems(agent.id).length;
312
+ totalItems += len;
313
+ if (len > maxItemsPerAgent) {
314
+ maxItemsPerAgent = len;
315
+ }
316
+ }
317
+ return {
318
+ total: this.agents.size,
319
+ byLifecycle,
320
+ withActiveForegroundTurn,
321
+ timelineStats: {
322
+ totalItems,
323
+ maxItemsPerAgent,
324
+ },
325
+ };
326
+ }
327
+ touchUpdatedAt(agent) {
328
+ const nowMs = Date.now();
329
+ const previousMs = agent.updatedAt.getTime();
330
+ const nextMs = nowMs > previousMs ? nowMs : previousMs + 1;
331
+ const next = new Date(nextMs);
332
+ agent.updatedAt = next;
333
+ return next;
334
+ }
335
+ nextStoredUpdatedAt(record) {
336
+ const previousMs = Date.parse(record.updatedAt);
337
+ const nowMs = Date.now();
338
+ const nextMs = nowMs > previousMs ? nowMs : previousMs + 1;
339
+ return new Date(nextMs).toISOString();
340
+ }
341
+ hasInFlightRun(agentId) {
342
+ const agent = this.agents.get(agentId);
343
+ if (!agent) {
344
+ return false;
345
+ }
346
+ return (agent.lifecycle === "running" ||
347
+ Boolean(agent.activeForegroundTurnId) ||
348
+ this.foregroundRuns.hasPendingRun(agentId));
349
+ }
350
+ subscribe(callback, options) {
351
+ const targetAgentId = options?.agentId == null ? null : validateAgentId(options.agentId, "subscribe");
352
+ const record = {
353
+ callback,
354
+ agentId: targetAgentId,
355
+ };
356
+ this.subscribers.add(record);
357
+ if (options?.replayState !== false) {
358
+ if (record.agentId) {
359
+ const agent = this.agents.get(record.agentId);
360
+ if (agent) {
361
+ callback({
362
+ type: "agent_state",
363
+ agent: { ...agent },
364
+ });
365
+ }
366
+ }
367
+ else {
368
+ // For global subscribers, skip internal agents during replay
369
+ for (const agent of this.agents.values()) {
370
+ if (agent.internal) {
371
+ continue;
372
+ }
373
+ callback({
374
+ type: "agent_state",
375
+ agent: { ...agent },
376
+ });
377
+ }
378
+ }
379
+ }
380
+ return () => {
381
+ this.subscribers.delete(record);
382
+ };
383
+ }
384
+ listAgents() {
385
+ return Array.from(this.agents.values())
386
+ .filter((agent) => !agent.internal)
387
+ .map((agent) => Object.assign({}, agent));
388
+ }
389
+ async listImportableSessions(options) {
390
+ const providerEntries = Array.from(this.clients.entries()).filter(([provider, client]) => client.capabilities.supportsSessionListing &&
391
+ !!client.listImportableSessions &&
392
+ this.isProviderImportable(provider, options?.providerFilter));
393
+ const sessionLists = await Promise.all(providerEntries.map(async ([provider, client]) => {
394
+ try {
395
+ return (await client.listImportableSessions({
396
+ limit: options?.limit,
397
+ cwd: options?.cwd,
398
+ })).map((session) => Object.assign(session, { provider }));
399
+ }
400
+ catch (error) {
401
+ this.logger.warn({ err: error, provider }, "Failed to list importable sessions for provider");
402
+ return [];
403
+ }
404
+ }));
405
+ const sessions = sessionLists.flat();
406
+ const limit = options?.limit ?? 20;
407
+ return sessions
408
+ .sort((a, b) => b.lastActivityAt.getTime() - a.lastActivityAt.getTime())
409
+ .slice(0, limit);
410
+ }
411
+ isProviderImportable(provider, providerFilter) {
412
+ if (this.providerEnabled.get(provider) === false) {
413
+ return false;
414
+ }
415
+ if (providerFilter && !providerFilter.has(provider)) {
416
+ return false;
417
+ }
418
+ return true;
419
+ }
420
+ async listProviderAvailability() {
421
+ return Promise.all(Array.from(this.clients.keys()).map((provider) => this.getProviderAvailability(provider)));
422
+ }
423
+ async getProviderAvailability(provider) {
424
+ const client = this.clients.get(provider);
425
+ if (!client) {
426
+ return {
427
+ provider,
428
+ available: false,
429
+ error: `No client registered for provider '${provider}'`,
430
+ };
431
+ }
432
+ try {
433
+ const available = await client.isAvailable();
434
+ return {
435
+ provider,
436
+ available,
437
+ error: null,
438
+ };
439
+ }
440
+ catch (error) {
441
+ const message = error instanceof Error ? error.message : String(error);
442
+ this.logger.warn({ err: error, provider }, "Failed to check provider availability");
443
+ return {
444
+ provider,
445
+ available: false,
446
+ error: message,
447
+ };
448
+ }
449
+ }
450
+ async listDraftCommands(config) {
451
+ const normalizedConfig = await this.normalizeConfig(config, { resolveDefaultModel: false });
452
+ const client = this.requireClient(normalizedConfig.provider);
453
+ if (!normalizedConfig.model) {
454
+ return [];
455
+ }
456
+ const available = await client.isAvailable();
457
+ if (!available) {
458
+ throw new Error(`Provider '${normalizedConfig.provider}' is not available. Please ensure the CLI is installed.`);
459
+ }
460
+ if (client.listCommands) {
461
+ return await client.listCommands(normalizedConfig);
462
+ }
463
+ const session = await client.createSession(normalizedConfig);
464
+ try {
465
+ if (!session.listCommands) {
466
+ throw new Error(`Provider '${normalizedConfig.provider}' does not support listing commands`);
467
+ }
468
+ return await session.listCommands();
469
+ }
470
+ finally {
471
+ try {
472
+ await session.close();
473
+ }
474
+ catch (error) {
475
+ this.logger.warn({ err: error, provider: normalizedConfig.provider }, "Failed to close draft command listing session");
476
+ }
477
+ }
478
+ }
479
+ async listDraftFeatures(config) {
480
+ const normalizedConfig = await this.normalizeConfig(config, { resolveDefaultModel: false });
481
+ const client = this.requireClient(normalizedConfig.provider);
482
+ if (!normalizedConfig.model) {
483
+ return [];
484
+ }
485
+ const available = await client.isAvailable();
486
+ if (!available) {
487
+ throw new Error(`Provider '${normalizedConfig.provider}' is not available. Please ensure the CLI is installed.`);
488
+ }
489
+ if (client.listFeatures) {
490
+ return await client.listFeatures(normalizedConfig);
491
+ }
492
+ const session = await client.createSession(normalizedConfig);
493
+ try {
494
+ return session.features ?? [];
495
+ }
496
+ finally {
497
+ try {
498
+ await session.close();
499
+ }
500
+ catch (error) {
501
+ this.logger.warn({ err: error, provider: normalizedConfig.provider }, "Failed to close draft feature listing session");
502
+ }
503
+ }
504
+ }
505
+ getAgent(id) {
506
+ const agent = this.agents.get(id);
507
+ return agent ? { ...agent } : null;
508
+ }
509
+ getTimeline(id) {
510
+ this.requireAgent(id);
511
+ return this.timelineStore.getItems(id);
512
+ }
513
+ async getTimelineRows(id) {
514
+ this.requireAgent(id);
515
+ if (this.durableTimelineStore) {
516
+ return await this.durableTimelineStore.getCommittedRows(id);
517
+ }
518
+ return this.timelineStore.getRows(id);
519
+ }
520
+ fetchTimeline(id, options) {
521
+ this.requireAgent(id);
522
+ return this.timelineStore.fetch(id, options);
523
+ }
524
+ async createAgent(config, agentId, options) {
525
+ const resolvedAgentId = validateAgentId(agentId ?? this.idFactory(), "createAgent");
526
+ const { storedConfig, launchConfig } = await this.prepareSessionConfig(config, resolvedAgentId);
527
+ this.requireEnabledProvider(storedConfig.provider);
528
+ const client = await this.requireAvailableClient({
529
+ provider: storedConfig.provider,
530
+ });
531
+ const launchContext = await this.buildLaunchContext(resolvedAgentId, client, options?.env);
532
+ const providerLaunchConfig = this.resolveProviderLaunchConfig(launchConfig, launchContext);
533
+ const createOptions = this.buildCreateSessionOptions(options);
534
+ const session = await client.createSession(providerLaunchConfig, launchContext, createOptions);
535
+ return this.registerSession(session, storedConfig, resolvedAgentId, {
536
+ labels: options?.labels,
537
+ digest: options?.digest,
538
+ initialTitle: options?.initialTitle,
539
+ workspaceId: options?.workspaceId,
540
+ });
541
+ }
542
+ buildCreateSessionOptions(options) {
543
+ if (options?.persistSession === undefined && !options?.resumeSessionId) {
544
+ return undefined;
545
+ }
546
+ return {
547
+ ...(options.persistSession !== undefined ? { persistSession: options.persistSession } : {}),
548
+ ...(options.resumeSessionId ? { resumeSessionId: options.resumeSessionId } : {}),
549
+ };
550
+ }
551
+ // Reconstruct an agent from provider persistence. Callers should explicitly
552
+ // hydrate timeline history after resume.
553
+ async resumeAgentFromPersistence(handle, overrides, agentId, options) {
554
+ const resolvedAgentId = validateAgentId(agentId ?? this.idFactory(), "resumeAgentFromPersistence");
555
+ const metadata = (handle.metadata ?? {});
556
+ const mergedConfig = {
557
+ ...metadata,
558
+ ...overrides,
559
+ provider: handle.provider,
560
+ };
561
+ const { storedConfig, launchConfig } = await this.prepareSessionConfig(mergedConfig, resolvedAgentId);
562
+ const client = this.requireClient(handle.provider);
563
+ const available = await client.isAvailable();
564
+ if (!available) {
565
+ throw new Error(`Provider '${handle.provider}' is not available. Please ensure the CLI is installed.`);
566
+ }
567
+ const launchContext = await this.buildLaunchContext(resolvedAgentId, client);
568
+ const providerLaunchConfig = this.resolveProviderLaunchConfig(launchConfig, launchContext);
569
+ const session = await client.resumeSession(handle, providerLaunchConfig, launchContext);
570
+ return this.registerSession(session, storedConfig, resolvedAgentId, options);
571
+ }
572
+ async importProviderSession(input) {
573
+ const resolvedAgentId = validateAgentId(this.idFactory(), "importProviderSession");
574
+ this.requireEnabledProvider(input.provider);
575
+ const client = await this.requireAvailableClient({ provider: input.provider });
576
+ if (!client.importSession) {
577
+ throw new Error(`Provider '${input.provider}' does not support importing sessions`);
578
+ }
579
+ const { storedConfig, launchConfig } = await this.prepareSessionConfig({
580
+ provider: input.provider,
581
+ cwd: input.cwd,
582
+ }, resolvedAgentId);
583
+ const launchContext = await this.buildLaunchContext(resolvedAgentId, client);
584
+ const providerLaunchConfig = this.resolveProviderLaunchConfig(launchConfig, launchContext);
585
+ const imported = await client.importSession({
586
+ providerHandleId: input.providerHandleId,
587
+ cwd: input.cwd,
588
+ }, { config: providerLaunchConfig, storedConfig, launchContext });
589
+ const importedConfig = await this.normalizeConfig(stripInternalPaseoMcpServer(imported.config));
590
+ const timelineRows = buildImportedTimelineRows(imported.timeline);
591
+ const initialTitle = resolveImportedAgentTitle(importedConfig, timelineRows);
592
+ return this.registerSession(imported.session, importedConfig, resolvedAgentId, {
593
+ labels: input.labels,
594
+ workspaceId: input.workspaceId,
595
+ timelineRows,
596
+ timelineNextSeq: timelineRows.length + 1,
597
+ persistence: imported.persistence,
598
+ historyPrimed: true,
599
+ initialTitle,
600
+ publishWhenReady: true,
601
+ });
602
+ }
603
+ /**
604
+ * Hot-swap a running agent's provider to a compatible alternative
605
+ * (e.g. Claude → Claude (Pool)). Compatibility is determined by the
606
+ * provider registry's effective `compatibleProviders` set — the swap is
607
+ * rejected if the target is not declared compatible.
608
+ *
609
+ * Preserves the session timeline, persistence handle, labels, and lifecycle
610
+ * metadata. Any in-flight turn is canceled first (best effort). On success,
611
+ * the session is recreated against the new provider's client using
612
+ * `resumeSession` with the existing persistence handle rewritten to the new
613
+ * provider, plus an optional model/mode override.
614
+ *
615
+ * Throws if the target provider has no registered client, is disabled, or
616
+ * is not in the current provider's compatible list.
617
+ */
618
+ async swapAgentProvider(agentId, newProviderId, overrides) {
619
+ const existing = this.requireSessionAgent(agentId);
620
+ const currentProvider = existing.provider;
621
+ if (currentProvider === newProviderId) {
622
+ throw new Error(`Agent '${agentId}' is already on provider '${newProviderId}' — nothing to swap.`);
623
+ }
624
+ // Validate compatibility via the daemon-wide swap metadata (sourced from
625
+ // provider-registry, which already resolved derived providers + ran
626
+ // symmetry validation).
627
+ const currentMeta = this.providerSwapMetadata.get(currentProvider);
628
+ const newMeta = this.providerSwapMetadata.get(newProviderId);
629
+ if (!currentMeta) {
630
+ throw new Error(`Cannot swap: current provider '${currentProvider}' is not registered.`);
631
+ }
632
+ if (!newMeta) {
633
+ throw new Error(`Cannot swap: target provider '${newProviderId}' is not registered.`);
634
+ }
635
+ if (currentMeta.wireFamily &&
636
+ newMeta.wireFamily &&
637
+ currentMeta.wireFamily !== newMeta.wireFamily) {
638
+ throw new Error(`Cannot swap '${currentProvider}' → '${newProviderId}': wireFamily mismatch ` +
639
+ `(${currentMeta.wireFamily} vs ${newMeta.wireFamily}).`);
640
+ }
641
+ const compatible = currentMeta.compatibleProviders;
642
+ if (!compatible.includes(newProviderId)) {
643
+ throw new Error(`Cannot swap '${currentProvider}' → '${newProviderId}': not in compatibleProviders list. ` +
644
+ `Allowed: [${compatible.join(", ") || "none"}].`);
645
+ }
646
+ this.requireEnabledProvider(newProviderId);
647
+ const newClient = this.requireClient(newProviderId);
648
+ const available = await newClient.isAvailable();
649
+ if (!available) {
650
+ throw new Error(`Cannot swap to '${newProviderId}': provider is not available. Please ensure the CLI is installed.`);
651
+ }
652
+ // Cancel any in-flight turn — best effort, do not fail the swap if cancel times out.
653
+ if (this.hasInFlightRun(agentId)) {
654
+ try {
655
+ await this.cancelAgentRun(agentId);
656
+ }
657
+ catch (error) {
658
+ this.logger.warn({ err: error, agentId, currentProvider, newProviderId }, "swapAgentProvider: cancelAgentRun failed, proceeding anyway");
659
+ }
660
+ }
661
+ const refreshedExisting = this.requireSessionAgent(agentId);
662
+ const handle = refreshedExisting.persistence;
663
+ if (!handle) {
664
+ throw new Error(`Cannot swap provider for agent '${agentId}': no persistence handle. ` +
665
+ `Only agents with session persistence can be swapped.`);
666
+ }
667
+ const newPersistenceHandle = {
668
+ ...handle,
669
+ provider: newProviderId,
670
+ };
671
+ // Build new config: copy from existing, override provider + (optional)
672
+ // model/modeId. Inherit everything else (cwd, env, mcp servers, etc.).
673
+ const baseConfig = {
674
+ ...refreshedExisting.config,
675
+ provider: newProviderId,
676
+ ...(overrides?.model !== undefined ? { model: overrides.model ?? undefined } : {}),
677
+ ...(overrides?.modeId !== undefined ? { modeId: overrides.modeId } : {}),
678
+ };
679
+ const { storedConfig, launchConfig } = await this.prepareSessionConfig(baseConfig, agentId);
680
+ const launchContext = await this.buildLaunchContext(agentId, newClient);
681
+ const providerLaunchConfig = this.resolveProviderLaunchConfig(launchConfig, launchContext);
682
+ // Preserve fields we want to carry across the swap.
683
+ const preservedHistoryPrimed = refreshedExisting.historyPrimed;
684
+ const preservedLastUsage = refreshedExisting.lastUsage;
685
+ const preservedLastError = refreshedExisting.lastError;
686
+ const preservedAttention = refreshedExisting.attention;
687
+ const oldSession = refreshedExisting.session;
688
+ const session = await newClient.resumeSession(newPersistenceHandle, providerLaunchConfig, launchContext);
689
+ this.agentStreamCoalescer.flushAndDiscard(agentId);
690
+ this.agents.delete(agentId);
691
+ if (refreshedExisting.unsubscribeSession) {
692
+ refreshedExisting.unsubscribeSession();
693
+ refreshedExisting.unsubscribeSession = null;
694
+ }
695
+ this.foregroundRuns.clearAgent(agentId, refreshedExisting);
696
+ await this.closeReloadedSession(oldSession, agentId);
697
+ const managed = await this.registerSession(session, storedConfig, agentId, {
698
+ labels: refreshedExisting.labels,
699
+ digest: refreshedExisting.digest,
700
+ createdAt: refreshedExisting.createdAt,
701
+ updatedAt: refreshedExisting.updatedAt,
702
+ lastUserMessageAt: refreshedExisting.lastUserMessageAt,
703
+ historyPrimed: preservedHistoryPrimed,
704
+ lastUsage: preservedLastUsage,
705
+ lastError: preservedLastError,
706
+ attention: preservedAttention,
707
+ });
708
+ this.logger.info({ agentId, oldProvider: currentProvider, newProvider: newProviderId }, "swapAgentProvider: completed");
709
+ return managed;
710
+ }
711
+ // Hot-reload an active agent session with config overrides while preserving
712
+ // in-memory timeline state.
713
+ async reloadAgentSession(agentId, overrides, options) {
714
+ let existing = this.requireSessionAgent(agentId);
715
+ if (this.hasInFlightRun(agentId)) {
716
+ await this.cancelAgentRun(agentId);
717
+ existing = this.requireSessionAgent(agentId);
718
+ }
719
+ const rehydrateFromDisk = options?.rehydrateFromDisk ?? false;
720
+ const preservedHistoryPrimed = existing.historyPrimed;
721
+ const preservedLastUsage = existing.lastUsage;
722
+ const preservedLastError = existing.lastError;
723
+ const preservedAttention = existing.attention;
724
+ const handle = existing.persistence;
725
+ const provider = handle?.provider ?? existing.provider;
726
+ const client = this.requireClient(provider);
727
+ const refreshConfig = {
728
+ ...existing.config,
729
+ ...overrides,
730
+ provider,
731
+ };
732
+ const { storedConfig, launchConfig } = await this.prepareSessionConfig(refreshConfig, agentId);
733
+ const launchContext = await this.buildLaunchContext(agentId, client);
734
+ const providerLaunchConfig = this.resolveProviderLaunchConfig(launchConfig, launchContext);
735
+ const session = handle
736
+ ? await client.resumeSession(handle, providerLaunchConfig, launchContext)
737
+ : await client.createSession(providerLaunchConfig, launchContext);
738
+ this.agentStreamCoalescer.flushAndDiscard(agentId);
739
+ // Remove the existing agent entry before swapping sessions
740
+ this.agents.delete(agentId);
741
+ if (existing.unsubscribeSession) {
742
+ existing.unsubscribeSession();
743
+ existing.unsubscribeSession = null;
744
+ }
745
+ this.foregroundRuns.clearAgent(agentId, existing);
746
+ await this.closeReloadedSession(existing.session, agentId);
747
+ if (rehydrateFromDisk) {
748
+ // Wipe both durable and in-memory timeline so registerSession mints a
749
+ // new epoch and hydrateTimelineFromProvider re-streams the freshly read
750
+ // provider history into an empty timeline.
751
+ await this.deleteCommittedTimeline(agentId);
752
+ this.timelineStore.delete(agentId);
753
+ }
754
+ // Preserve existing labels and timeline during reload.
755
+ return this.registerSession(session, storedConfig, agentId, {
756
+ labels: existing.labels,
757
+ workspaceId: existing.workspaceId,
758
+ digest: existing.digest,
759
+ createdAt: existing.createdAt,
760
+ updatedAt: existing.updatedAt,
761
+ lastUserMessageAt: existing.lastUserMessageAt,
762
+ historyPrimed: rehydrateFromDisk ? false : preservedHistoryPrimed,
763
+ lastUsage: preservedLastUsage,
764
+ lastError: preservedLastError,
765
+ attention: preservedAttention,
766
+ });
767
+ }
768
+ async closeReloadedSession(session, agentId) {
769
+ try {
770
+ const result = await this.waitWithTimeout({
771
+ operation: session.close(),
772
+ timeoutMs: this.rescueTimeouts.reloadSessionCloseMs,
773
+ onLateError: (error) => {
774
+ this.logger.warn({ err: error, agentId }, "Previous session close failed after refresh timeout");
775
+ },
776
+ });
777
+ if (result === "timed_out") {
778
+ this.logger.warn({ agentId, timeoutMs: this.rescueTimeouts.reloadSessionCloseMs }, "Timed out closing previous session during refresh");
779
+ }
780
+ }
781
+ catch (error) {
782
+ this.logger.warn({ err: error, agentId }, "Failed to close previous session during refresh");
783
+ }
784
+ }
785
+ async waitWithTimeout(options) {
786
+ let didTimeOut = false;
787
+ let timer = null;
788
+ const operation = options.operation
789
+ .then(() => "completed")
790
+ .catch((error) => {
791
+ if (didTimeOut) {
792
+ options.onLateError?.(error);
793
+ return "timed_out";
794
+ }
795
+ throw error;
796
+ });
797
+ try {
798
+ return await Promise.race([
799
+ operation,
800
+ new Promise((resolvePromise) => {
801
+ timer = setTimeout(() => {
802
+ didTimeOut = true;
803
+ resolvePromise("timed_out");
804
+ }, options.timeoutMs);
805
+ }),
806
+ ]);
807
+ }
808
+ finally {
809
+ if (timer) {
810
+ clearTimeout(timer);
811
+ }
812
+ }
813
+ }
814
+ async closeAgent(agentId) {
815
+ const agent = this.requireAgent(agentId);
816
+ this.logger.trace({
817
+ agentId,
818
+ provider: agent.provider,
819
+ sessionId: agent.persistence?.sessionId ?? undefined,
820
+ turnId: agent.activeForegroundTurnId ?? undefined,
821
+ lifecycle: agent.lifecycle,
822
+ activeForegroundTurnId: agent.activeForegroundTurnId,
823
+ pendingPermissions: agent.pendingPermissions.size,
824
+ }, "agent.manager.close.start");
825
+ const closedAgent = this.prepareAgentForClosure(agent, "agent closed");
826
+ await agent.session.close();
827
+ this.timelineStore.delete(agentId);
828
+ await this.persistSnapshot(closedAgent);
829
+ this.emitClosedAgent(closedAgent, { persist: false });
830
+ this.logger.trace({
831
+ agentId,
832
+ provider: closedAgent.provider,
833
+ sessionId: closedAgent.persistence?.sessionId ?? undefined,
834
+ }, "agent.manager.close.complete");
835
+ }
836
+ async archiveAgent(agentId) {
837
+ const agent = this.requireAgent(agentId);
838
+ if (!this.registry) {
839
+ throw new Error("Agent storage is not configured");
840
+ }
841
+ await this.registry.applySnapshot(agent, {
842
+ internal: agent.internal,
843
+ });
844
+ const stored = await this.registry.get(agentId);
845
+ if (!stored) {
846
+ throw new Error(`Agent ${agentId} not found in storage after snapshot`);
847
+ }
848
+ const { archivedAt } = await this.markRecordArchived(stored);
849
+ agent.updatedAt = new Date(archivedAt);
850
+ await this.closeAgent(agentId);
851
+ await this.cascadeArchiveChildren(agentId);
852
+ return { archivedAt };
853
+ }
854
+ // Children created via the MCP `create_agent` tool carry the parent-agent-id
855
+ // label pointing back at the caller. Archiving the parent cascades to those
856
+ // children so subagent fleets don't outlive their orchestrator. Detached
857
+ // handoff agents omit this label, so they stand outside the cascade.
858
+ async cascadeArchiveChildren(parentAgentId) {
859
+ const registry = this.registry;
860
+ if (!registry) {
861
+ return;
862
+ }
863
+ const records = await registry.list();
864
+ for (const record of records) {
865
+ if (record.archivedAt) {
866
+ continue;
867
+ }
868
+ if (record.labels?.[PARENT_AGENT_ID_LABEL] !== parentAgentId) {
869
+ continue;
870
+ }
871
+ if (this.agents.has(record.id)) {
872
+ await this.archiveAgent(record.id);
873
+ }
874
+ else {
875
+ await this.markRecordArchived(record);
876
+ await this.cascadeArchiveChildren(record.id);
877
+ }
878
+ }
879
+ }
880
+ async markRecordArchived(record) {
881
+ const registry = this.requireRegistry();
882
+ const archivedAt = new Date().toISOString();
883
+ const archivedRecord = buildArchivedAgentRecord(record, { archivedAt, updatedAt: archivedAt });
884
+ await registry.upsert(archivedRecord);
885
+ await this.archiveNativeSessionBestEffort(record.provider, record.persistence);
886
+ if (this.agents.has(record.id)) {
887
+ this.notifyAgentState(record.id);
888
+ }
889
+ else if (!archivedRecord.internal) {
890
+ this.dispatchArchivedStoredAgent(archivedRecord);
891
+ }
892
+ await this.fireAgentArchived(record.id);
893
+ return archivedRecord;
894
+ }
895
+ async fireAgentArchived(agentId) {
896
+ const callback = this.onAgentArchived;
897
+ if (!callback) {
898
+ return;
899
+ }
900
+ try {
901
+ await callback(agentId);
902
+ }
903
+ catch (error) {
904
+ this.logger.warn({ err: error, agentId }, "onAgentArchived callback failed");
905
+ }
906
+ }
907
+ dispatchArchivedStoredAgent(record) {
908
+ const updatedAt = new Date(record.updatedAt);
909
+ this.dispatch({
910
+ type: "agent_state",
911
+ agent: {
912
+ id: record.id,
913
+ provider: record.provider,
914
+ cwd: record.cwd,
915
+ workspaceId: record.workspaceId,
916
+ session: null,
917
+ capabilities: STORED_AGENT_CAPABILITIES,
918
+ config: buildStoredAgentConfig(record),
919
+ runtimeInfo: undefined,
920
+ lifecycle: "closed",
921
+ createdAt: new Date(record.createdAt),
922
+ updatedAt,
923
+ availableModes: [],
924
+ features: record.features,
925
+ currentModeId: record.lastModeId ?? null,
926
+ pendingPermissions: new Map(),
927
+ bufferedPermissionResolutions: new Map(),
928
+ inFlightPermissionResponses: new Set(),
929
+ pendingReplacement: false,
930
+ activeForegroundTurnId: null,
931
+ foregroundTurnWaiters: new Set(),
932
+ finalizedForegroundTurnIds: new Set(),
933
+ unsubscribeSession: null,
934
+ persistence: record.persistence ?? null,
935
+ historyPrimed: true,
936
+ lastUserMessageAt: record.lastUserMessageAt ? new Date(record.lastUserMessageAt) : null,
937
+ lastUsage: undefined,
938
+ lastError: record.lastError ?? undefined,
939
+ attention: { requiresAttention: false },
940
+ internal: record.internal,
941
+ labels: record.labels,
942
+ digest: record.digest,
943
+ },
944
+ });
945
+ }
946
+ async setAgentMode(agentId, modeId) {
947
+ const agent = this.requireSessionAgent(agentId);
948
+ const notice = (await agent.session.setMode(modeId)) ?? null;
949
+ const currentMode = (await agent.session.getCurrentMode()) ?? modeId;
950
+ agent.config.modeId = currentMode ?? undefined;
951
+ agent.currentModeId = currentMode;
952
+ // Update runtimeInfo to reflect the new mode
953
+ if (agent.runtimeInfo) {
954
+ agent.runtimeInfo = { ...agent.runtimeInfo, modeId: currentMode };
955
+ }
956
+ this.touchUpdatedAt(agent);
957
+ this.emitState(agent);
958
+ return notice;
959
+ }
960
+ async setAgentModel(agentId, modelId) {
961
+ const agent = this.requireSessionAgent(agentId);
962
+ const normalizedModelId = typeof modelId === "string" && modelId.trim().length > 0 ? modelId : null;
963
+ if (agent.session.setModel) {
964
+ await agent.session.setModel(normalizedModelId);
965
+ }
966
+ agent.config.model = normalizedModelId ?? undefined;
967
+ if (agent.runtimeInfo) {
968
+ agent.runtimeInfo = { ...agent.runtimeInfo, model: normalizedModelId };
969
+ }
970
+ this.touchUpdatedAt(agent);
971
+ this.emitState(agent);
972
+ }
973
+ async setAgentThinkingOption(agentId, thinkingOptionId) {
974
+ const agent = this.requireSessionAgent(agentId);
975
+ const normalizedThinkingOptionId = typeof thinkingOptionId === "string" && thinkingOptionId.trim().length > 0
976
+ ? thinkingOptionId
977
+ : null;
978
+ let notice = null;
979
+ if (agent.session.setThinkingOption) {
980
+ notice = (await agent.session.setThinkingOption(normalizedThinkingOptionId)) ?? null;
981
+ }
982
+ agent.config.thinkingOptionId = normalizedThinkingOptionId ?? undefined;
983
+ if (agent.runtimeInfo) {
984
+ agent.runtimeInfo = {
985
+ ...agent.runtimeInfo,
986
+ thinkingOptionId: normalizedThinkingOptionId,
987
+ };
988
+ }
989
+ this.touchUpdatedAt(agent);
990
+ this.emitState(agent);
991
+ return notice;
992
+ }
993
+ async setAgentFeature(agentId, featureId, value) {
994
+ const agent = this.requireAgent(agentId);
995
+ if (!agent.session.setFeature) {
996
+ throw new Error("Agent session does not support setting features");
997
+ }
998
+ await agent.session.setFeature(featureId, value);
999
+ agent.config.featureValues = { ...agent.config.featureValues, [featureId]: value };
1000
+ this.touchUpdatedAt(agent);
1001
+ this.emitState(agent);
1002
+ }
1003
+ async setTitle(agentId, title) {
1004
+ const agent = this.requireAgent(agentId);
1005
+ const normalizedTitle = title.trim();
1006
+ if (!normalizedTitle) {
1007
+ return;
1008
+ }
1009
+ if (this.agentsAwaitingInitialSnapshotPersist.has(agent.id) &&
1010
+ this.registry &&
1011
+ (await this.registry.get(agent.id)) === null) {
1012
+ return;
1013
+ }
1014
+ this.touchUpdatedAt(agent);
1015
+ await this.persistSnapshot(agent, { title: normalizedTitle });
1016
+ this.emitState(agent, { persist: false });
1017
+ }
1018
+ async setLabels(agentId, labels) {
1019
+ const agent = this.requireAgent(agentId);
1020
+ await this.writeLabels(agent.id, labels);
1021
+ }
1022
+ async writeLabels(agentId, patch) {
1023
+ const liveAgent = this.agents.get(agentId);
1024
+ if (liveAgent) {
1025
+ liveAgent.labels = applyLabelPatch(liveAgent.labels, patch);
1026
+ this.touchUpdatedAt(liveAgent);
1027
+ await this.persistSnapshot(liveAgent);
1028
+ this.emitState(liveAgent, { persist: false });
1029
+ const record = this.registry ? await this.registry.get(agentId) : null;
1030
+ return { record, live: true };
1031
+ }
1032
+ const nextRecord = await this.writeStoredMetadata(agentId, { labels: patch });
1033
+ return { record: nextRecord, live: false };
1034
+ }
1035
+ async writeStoredMetadata(agentId, patch) {
1036
+ const registry = this.requireRegistry();
1037
+ const record = await registry.get(agentId);
1038
+ if (!record) {
1039
+ throw new Error(`Agent not found: ${agentId}`);
1040
+ }
1041
+ const nextRecord = {
1042
+ ...record,
1043
+ ...(patch.title ? { title: patch.title } : {}),
1044
+ ...(patch.labels ? { labels: applyLabelPatch(record.labels, patch.labels) } : {}),
1045
+ ...(patch.digest ? { digest: patch.digest } : {}),
1046
+ updatedAt: this.nextStoredUpdatedAt(record),
1047
+ };
1048
+ await registry.upsert(nextRecord);
1049
+ return nextRecord;
1050
+ }
1051
+ async detachAgent(agentId) {
1052
+ const registry = this.requireRegistry();
1053
+ const liveAgent = this.agents.get(agentId);
1054
+ if (liveAgent) {
1055
+ const previousParentAgentId = getParentAgentIdFromLabels(liveAgent.labels);
1056
+ if (!previousParentAgentId) {
1057
+ await this.persistSnapshot(liveAgent);
1058
+ const record = await registry.get(agentId);
1059
+ if (!record) {
1060
+ throw new Error(`Agent not found in storage after detach: ${agentId}`);
1061
+ }
1062
+ return { record, live: true, previousParentAgentId: null };
1063
+ }
1064
+ const { record } = await this.writeLabels(agentId, { [PARENT_AGENT_ID_LABEL]: null });
1065
+ if (!record) {
1066
+ throw new Error(`Agent not found in storage after detach: ${agentId}`);
1067
+ }
1068
+ return { record, live: true, previousParentAgentId };
1069
+ }
1070
+ const record = await registry.get(agentId);
1071
+ if (!record) {
1072
+ throw new Error(`Agent not found: ${agentId}`);
1073
+ }
1074
+ const previousParentAgentId = getParentAgentIdFromLabels(record.labels);
1075
+ if (!previousParentAgentId) {
1076
+ return { record, live: false, previousParentAgentId: null };
1077
+ }
1078
+ const result = await this.writeLabels(agentId, { [PARENT_AGENT_ID_LABEL]: null });
1079
+ if (!result.record) {
1080
+ throw new Error(`Agent not found in storage after detach: ${agentId}`);
1081
+ }
1082
+ return { record: result.record, live: false, previousParentAgentId };
1083
+ }
1084
+ /**
1085
+ * Replace the session digest wholesale. Callers (the set_session_digest MCP
1086
+ * tool and the daemon digest refresher) compute the merged digest and pass
1087
+ * the full object. See docs/session-digest.md.
1088
+ */
1089
+ async setDigest(agentId, digest) {
1090
+ const agent = this.requireAgent(agentId);
1091
+ agent.digest = digest;
1092
+ // Targeted digest-only persist (mirrors setGeneratedTitle): the digest
1093
+ // refresh runs asynchronously on running→idle, so a full-record
1094
+ // persistSnapshot here would race with and revert a concurrent title/label
1095
+ // write. Updating only the digest field is race-safe. See docs/session-digest.md §5.
1096
+ const registry = this.requireRegistry();
1097
+ const persisted = await registry.setDigest(agentId, digest);
1098
+ agent.updatedAt = new Date(persisted.updatedAt);
1099
+ this.emitState(agent, { persist: false });
1100
+ }
1101
+ notifyAgentState(agentId) {
1102
+ const agent = this.agents.get(agentId);
1103
+ if (!agent || agent.internal) {
1104
+ return;
1105
+ }
1106
+ this.touchUpdatedAt(agent);
1107
+ this.emitState(agent);
1108
+ }
1109
+ async clearAgentAttention(agentId) {
1110
+ const agent = this.requireAgent(agentId);
1111
+ if (agent.attention.requiresAttention) {
1112
+ agent.attention = { requiresAttention: false };
1113
+ await this.persistSnapshot(agent);
1114
+ this.emitState(agent, { persist: false });
1115
+ }
1116
+ }
1117
+ async archiveSnapshot(agentId, archivedAt) {
1118
+ const registry = this.requireRegistry();
1119
+ const liveAgent = this.getAgent(agentId);
1120
+ if (liveAgent) {
1121
+ await this.persistSnapshot(liveAgent, {
1122
+ internal: liveAgent.internal,
1123
+ });
1124
+ }
1125
+ const record = await registry.get(agentId);
1126
+ if (!record) {
1127
+ throw new Error(`Agent not found: ${agentId}`);
1128
+ }
1129
+ const nextRecord = buildArchivedAgentRecord(record, { archivedAt });
1130
+ await registry.upsert(nextRecord);
1131
+ await this.archiveNativeSessionBestEffort(record.provider, record.persistence);
1132
+ if (this.agents.has(agentId)) {
1133
+ this.notifyAgentState(agentId);
1134
+ }
1135
+ else if (!nextRecord.internal) {
1136
+ this.dispatchArchivedStoredAgent(nextRecord);
1137
+ }
1138
+ await this.fireAgentArchived(agentId);
1139
+ return nextRecord;
1140
+ }
1141
+ async unarchiveSnapshot(agentId) {
1142
+ const registry = this.requireRegistry();
1143
+ const record = await registry.get(agentId);
1144
+ if (!record || !record.archivedAt) {
1145
+ return false;
1146
+ }
1147
+ await this.unarchiveNativeSession(record.provider, record.persistence);
1148
+ await registry.upsert({
1149
+ ...record,
1150
+ archivedAt: null,
1151
+ updatedAt: new Date().toISOString(),
1152
+ });
1153
+ if (this.getAgent(agentId)) {
1154
+ this.notifyAgentState(agentId);
1155
+ }
1156
+ return true;
1157
+ }
1158
+ async unarchiveSnapshotByHandle(handle) {
1159
+ const registry = this.requireRegistry();
1160
+ const records = await registry.list();
1161
+ const matched = records.find((record) => record.persistence?.provider === handle.provider &&
1162
+ record.persistence?.sessionId === handle.sessionId);
1163
+ if (!matched) {
1164
+ return;
1165
+ }
1166
+ await this.unarchiveSnapshot(matched.id);
1167
+ }
1168
+ async updateAgentMetadata(agentId, updates) {
1169
+ const liveAgent = this.getAgent(agentId);
1170
+ if (liveAgent) {
1171
+ if (updates.title) {
1172
+ await this.setTitle(agentId, updates.title);
1173
+ }
1174
+ if (updates.labels) {
1175
+ await this.writeLabels(agentId, updates.labels);
1176
+ }
1177
+ if (updates.digest) {
1178
+ await this.setDigest(agentId, updates.digest);
1179
+ }
1180
+ return;
1181
+ }
1182
+ await this.writeStoredMetadata(agentId, updates);
1183
+ }
1184
+ async runAgent(agentId, prompt, options) {
1185
+ const events = this.streamAgent(agentId, prompt, options);
1186
+ const timeline = [];
1187
+ let finalText = "";
1188
+ let usage;
1189
+ let canceled = false;
1190
+ for await (const event of events) {
1191
+ if (event.type === "timeline") {
1192
+ timeline.push(event.item);
1193
+ }
1194
+ else if (event.type === "turn_completed") {
1195
+ usage = event.usage;
1196
+ }
1197
+ else if (event.type === "turn_failed") {
1198
+ throw new Error(this.formatTurnFailedMessage(event));
1199
+ }
1200
+ else if (event.type === "turn_canceled") {
1201
+ canceled = true;
1202
+ }
1203
+ }
1204
+ finalText = this.getLastAssistantMessageFromTimeline(timeline) ?? "";
1205
+ const agent = this.requireAgent(agentId);
1206
+ const sessionId = agent.persistence?.sessionId;
1207
+ if (!sessionId) {
1208
+ throw new Error(`Agent ${agentId} has no persistence.sessionId after run completed`);
1209
+ }
1210
+ return {
1211
+ sessionId,
1212
+ finalText,
1213
+ usage,
1214
+ timeline,
1215
+ canceled,
1216
+ };
1217
+ }
1218
+ /**
1219
+ * Try to run a prompt out-of-band — i.e. without allocating a foreground turn
1220
+ * and without canceling any active turn. Returns true when the session
1221
+ * accepted the prompt as a side-effect command (e.g. /goal pause). Events
1222
+ * emitted by the handler flow through dispatchStream so they persist and
1223
+ * broadcast like normal timeline events.
1224
+ */
1225
+ tryRunOutOfBand(agentId, prompt) {
1226
+ const agent = this.requireSessionAgent(agentId);
1227
+ const handler = agent.session.tryHandleOutOfBand?.(prompt);
1228
+ if (!handler) {
1229
+ return false;
1230
+ }
1231
+ const dispatch = (event) => {
1232
+ // Persist timeline items so they show up in fetchAgentTimeline; broadcast
1233
+ // for live subscribers. Other event types are broadcast only.
1234
+ if (event.type === "timeline") {
1235
+ this.touchUpdatedAt(agent);
1236
+ const row = this.recordTimeline(agent.id, event.item);
1237
+ this.dispatchStream(agent.id, event, {
1238
+ seq: row.seq,
1239
+ epoch: this.timelineStore.getEpoch(agent.id),
1240
+ timestamp: row.timestamp,
1241
+ });
1242
+ return;
1243
+ }
1244
+ this.dispatchStream(agent.id, event, { timestamp: new Date().toISOString() });
1245
+ };
1246
+ void (async () => {
1247
+ try {
1248
+ await handler.run({ emit: dispatch });
1249
+ }
1250
+ catch (error) {
1251
+ const text = error instanceof Error ? error.message : "Out-of-band command failed";
1252
+ dispatch({
1253
+ type: "timeline",
1254
+ provider: agent.provider,
1255
+ item: { type: "assistant_message", text: `[Error] ${text}` },
1256
+ });
1257
+ }
1258
+ })();
1259
+ return true;
1260
+ }
1261
+ async appendTimelineItem(agentId, item) {
1262
+ const agent = this.requireAgent(agentId);
1263
+ this.touchUpdatedAt(agent);
1264
+ const row = this.recordTimeline(agentId, item);
1265
+ this.dispatchStream(agentId, {
1266
+ type: "timeline",
1267
+ item,
1268
+ provider: agent.provider,
1269
+ }, {
1270
+ seq: row.seq,
1271
+ epoch: this.timelineStore.getEpoch(agentId),
1272
+ timestamp: row.timestamp,
1273
+ });
1274
+ await this.persistSnapshot(agent);
1275
+ }
1276
+ async emitLiveTimelineItem(agentId, item) {
1277
+ const agent = this.requireAgent(agentId);
1278
+ this.touchUpdatedAt(agent);
1279
+ this.dispatchStream(agentId, {
1280
+ type: "timeline",
1281
+ item,
1282
+ provider: agent.provider,
1283
+ });
1284
+ }
1285
+ streamAgent(agentId, prompt, options) {
1286
+ const existingAgent = this.requireSessionAgent(agentId);
1287
+ this.logger.trace({
1288
+ agentId,
1289
+ provider: existingAgent.provider,
1290
+ sessionId: existingAgent.persistence?.sessionId ?? undefined,
1291
+ turnId: existingAgent.activeForegroundTurnId ?? undefined,
1292
+ lifecycle: existingAgent.lifecycle,
1293
+ activeForegroundTurnId: existingAgent.activeForegroundTurnId,
1294
+ hasPendingForegroundRun: this.foregroundRuns.hasPendingRun(agentId),
1295
+ promptType: typeof prompt === "string" ? "string" : "structured",
1296
+ hasRunOptions: Boolean(options),
1297
+ }, "agent.manager.stream.request");
1298
+ if (existingAgent.activeForegroundTurnId || this.foregroundRuns.hasPendingRun(agentId)) {
1299
+ this.logger.trace({
1300
+ agentId,
1301
+ provider: existingAgent.provider,
1302
+ sessionId: existingAgent.persistence?.sessionId ?? undefined,
1303
+ turnId: existingAgent.activeForegroundTurnId ?? undefined,
1304
+ lifecycle: existingAgent.lifecycle,
1305
+ hasPendingForegroundRun: this.foregroundRuns.hasPendingRun(agentId),
1306
+ }, "agent.manager.stream.reject");
1307
+ throw new Error(`Agent ${agentId} already has an active run`);
1308
+ }
1309
+ const agent = existingAgent;
1310
+ agent.pendingReplacement = false;
1311
+ agent.lastError = undefined;
1312
+ const pendingRun = this.foregroundRuns.createPendingRun(agentId);
1313
+ const streamForwarder = async function* streamForwarder() {
1314
+ let turnId;
1315
+ let turnStream = null;
1316
+ try {
1317
+ const result = await agent.session.startTurn(prompt, options);
1318
+ turnId = result.turnId;
1319
+ }
1320
+ catch (error) {
1321
+ const errorMsg = error instanceof Error ? error.message : "Failed to start turn";
1322
+ await this.handleStreamEvent(agent, {
1323
+ type: "turn_failed",
1324
+ provider: agent.provider,
1325
+ error: errorMsg,
1326
+ });
1327
+ this.finalizeForegroundTurn(agent);
1328
+ this.foregroundRuns.settlePendingRun(agentId, pendingRun.token);
1329
+ throw error;
1330
+ }
1331
+ pendingRun.started = true;
1332
+ agent.activeForegroundTurnId = turnId;
1333
+ agent.lifecycle = "running";
1334
+ this.touchUpdatedAt(agent);
1335
+ this.emitState(agent);
1336
+ this.logger.trace({
1337
+ agentId,
1338
+ provider: agent.provider,
1339
+ sessionId: agent.persistence?.sessionId ?? undefined,
1340
+ turnId,
1341
+ lifecycle: agent.lifecycle,
1342
+ activeForegroundTurnId: agent.activeForegroundTurnId,
1343
+ }, "agent.manager.stream.start");
1344
+ turnStream = this.foregroundRuns.createTurnStream(turnId);
1345
+ this.foregroundRuns.addWaiter(agent, turnStream.waiter);
1346
+ try {
1347
+ for await (const event of turnStream.events(isTurnTerminalEvent)) {
1348
+ yield event;
1349
+ }
1350
+ }
1351
+ finally {
1352
+ if (turnStream) {
1353
+ this.foregroundRuns.deleteWaiter(agent, turnStream.waiter);
1354
+ }
1355
+ this.foregroundRuns.settlePendingRun(agentId, pendingRun.token);
1356
+ if (!agent.activeForegroundTurnId) {
1357
+ await this.refreshRuntimeInfo(agent);
1358
+ }
1359
+ }
1360
+ }.call(this);
1361
+ return streamForwarder;
1362
+ }
1363
+ finalizeForegroundTurn(agent, turnId) {
1364
+ const mutableAgent = agent;
1365
+ if (turnId) {
1366
+ this.foregroundRuns.rememberFinalizedTurn(mutableAgent, turnId);
1367
+ }
1368
+ mutableAgent.activeForegroundTurnId = null;
1369
+ const terminalError = mutableAgent.lastError;
1370
+ const shouldHoldBusyForReplacement = mutableAgent.pendingReplacement && !terminalError;
1371
+ let nextLifecycle;
1372
+ if (shouldHoldBusyForReplacement) {
1373
+ nextLifecycle = "running";
1374
+ }
1375
+ else if (terminalError) {
1376
+ nextLifecycle = "error";
1377
+ }
1378
+ else {
1379
+ nextLifecycle = "idle";
1380
+ }
1381
+ mutableAgent.lifecycle = nextLifecycle;
1382
+ const persistenceHandle = mutableAgent.session.describePersistence() ??
1383
+ (mutableAgent.runtimeInfo?.sessionId
1384
+ ? { provider: mutableAgent.provider, sessionId: mutableAgent.runtimeInfo.sessionId }
1385
+ : null);
1386
+ if (persistenceHandle) {
1387
+ mutableAgent.persistence = attachPersistenceCwd(persistenceHandle, mutableAgent.cwd);
1388
+ }
1389
+ this.logger.trace({
1390
+ agentId: agent.id,
1391
+ provider: agent.provider,
1392
+ sessionId: mutableAgent.persistence?.sessionId ?? undefined,
1393
+ turnId,
1394
+ lifecycle: mutableAgent.lifecycle,
1395
+ terminalError,
1396
+ pendingReplacement: mutableAgent.pendingReplacement,
1397
+ }, "agent.manager.finalize");
1398
+ if (!shouldHoldBusyForReplacement) {
1399
+ this.touchUpdatedAt(mutableAgent);
1400
+ this.emitState(mutableAgent);
1401
+ }
1402
+ }
1403
+ replaceAgentRun(agentId, prompt, options) {
1404
+ const snapshot = this.requireAgent(agentId);
1405
+ if (snapshot.lifecycle !== "running" &&
1406
+ !snapshot.activeForegroundTurnId &&
1407
+ !this.foregroundRuns.hasPendingRun(agentId)) {
1408
+ return this.streamAgent(agentId, prompt, options);
1409
+ }
1410
+ const agent = this.requireSessionAgent(agentId);
1411
+ agent.pendingReplacement = true;
1412
+ agent.lifecycle = "running";
1413
+ this.touchUpdatedAt(agent);
1414
+ this.emitState(agent);
1415
+ return async function* replaceRunForwarder() {
1416
+ try {
1417
+ await this.cancelAgentRun(agentId);
1418
+ const nextRun = this.streamAgent(agentId, prompt, options);
1419
+ for await (const event of nextRun) {
1420
+ yield event;
1421
+ }
1422
+ }
1423
+ catch (error) {
1424
+ const latest = this.agents.get(agentId);
1425
+ if (latest) {
1426
+ const latestActive = latest;
1427
+ latestActive.pendingReplacement = false;
1428
+ if (!latestActive.activeForegroundTurnId && latestActive.lifecycle === "running") {
1429
+ latestActive.lifecycle = "idle";
1430
+ this.touchUpdatedAt(latestActive);
1431
+ this.emitState(latestActive);
1432
+ }
1433
+ }
1434
+ throw error;
1435
+ }
1436
+ }.call(this);
1437
+ }
1438
+ async waitForAgentRunStart(agentId, options) {
1439
+ const snapshot = this.getAgent(agentId);
1440
+ if (!snapshot) {
1441
+ throw new Error(`Agent ${agentId} not found`);
1442
+ }
1443
+ const pendingRun = this.foregroundRuns.getPendingRun(agentId);
1444
+ if ((snapshot.lifecycle === "running" || pendingRun?.started) && !snapshot.pendingReplacement) {
1445
+ return;
1446
+ }
1447
+ if (!snapshot.activeForegroundTurnId && !pendingRun && !snapshot.pendingReplacement) {
1448
+ throw new Error(`Agent ${agentId} has no pending run`);
1449
+ }
1450
+ if (options?.signal?.aborted) {
1451
+ throw createAbortError(options.signal, "wait_for_agent_start aborted");
1452
+ }
1453
+ await new Promise((resolvePromise, reject) => {
1454
+ if (options?.signal?.aborted) {
1455
+ reject(createAbortError(options.signal, "wait_for_agent_start aborted"));
1456
+ return;
1457
+ }
1458
+ let unsubscribe = null;
1459
+ let abortHandler = null;
1460
+ const cleanup = () => {
1461
+ if (unsubscribe) {
1462
+ try {
1463
+ unsubscribe();
1464
+ }
1465
+ catch {
1466
+ // ignore cleanup errors
1467
+ }
1468
+ unsubscribe = null;
1469
+ }
1470
+ if (abortHandler && options?.signal) {
1471
+ try {
1472
+ options.signal.removeEventListener("abort", abortHandler);
1473
+ }
1474
+ catch {
1475
+ // ignore cleanup errors
1476
+ }
1477
+ abortHandler = null;
1478
+ }
1479
+ };
1480
+ const finishOk = () => {
1481
+ cleanup();
1482
+ resolvePromise();
1483
+ };
1484
+ const finishErr = (error) => {
1485
+ cleanup();
1486
+ reject(error);
1487
+ };
1488
+ if (options?.signal) {
1489
+ abortHandler = () => finishErr(createAbortError(options.signal, "wait_for_agent_start aborted"));
1490
+ options.signal.addEventListener("abort", abortHandler, { once: true });
1491
+ }
1492
+ const checkCurrentState = () => {
1493
+ const current = this.getAgent(agentId);
1494
+ if (!current) {
1495
+ finishErr(new Error(`Agent ${agentId} not found`));
1496
+ return true;
1497
+ }
1498
+ const currentPendingRun = this.foregroundRuns.getPendingRun(agentId);
1499
+ if ((current.lifecycle === "running" || currentPendingRun?.started) &&
1500
+ !current.pendingReplacement) {
1501
+ finishOk();
1502
+ return true;
1503
+ }
1504
+ if (current.lifecycle === "error" && !currentPendingRun?.started) {
1505
+ finishErr(new Error(current.lastError ?? `Agent ${agentId} failed to start`));
1506
+ return true;
1507
+ }
1508
+ if (!currentPendingRun && !current.activeForegroundTurnId && !current.pendingReplacement) {
1509
+ finishErr(new Error(`Agent ${agentId} run finished before starting`));
1510
+ return true;
1511
+ }
1512
+ return false;
1513
+ };
1514
+ unsubscribe = this.subscribe((event) => {
1515
+ if (event.type !== "agent_state" || event.agent.id !== agentId) {
1516
+ return;
1517
+ }
1518
+ checkCurrentState();
1519
+ }, { agentId, replayState: false });
1520
+ checkCurrentState();
1521
+ });
1522
+ }
1523
+ async respondToPermission(agentId, requestId, response) {
1524
+ const agent = this.requireAgent(agentId);
1525
+ agent.inFlightPermissionResponses.add(requestId);
1526
+ try {
1527
+ const result = await agent.session.respondToPermission(requestId, response);
1528
+ agent.pendingPermissions.delete(requestId);
1529
+ try {
1530
+ await this.refreshSessionState(agent);
1531
+ }
1532
+ catch {
1533
+ // Ignore refresh errors - state sync after permission approval is best effort.
1534
+ }
1535
+ this.touchUpdatedAt(agent);
1536
+ await this.persistSnapshot(agent);
1537
+ this.emitState(agent);
1538
+ const bufferedResolution = agent.bufferedPermissionResolutions.get(requestId);
1539
+ if (bufferedResolution) {
1540
+ agent.bufferedPermissionResolutions.delete(requestId);
1541
+ this.dispatchStream(agent.id, bufferedResolution, { timestamp: new Date().toISOString() });
1542
+ }
1543
+ return result;
1544
+ }
1545
+ finally {
1546
+ agent.inFlightPermissionResponses.delete(requestId);
1547
+ agent.bufferedPermissionResolutions.delete(requestId);
1548
+ }
1549
+ }
1550
+ /**
1551
+ * Dismiss a live background shell for an agent. Delegates to the provider
1552
+ * session, which (for Claude Code over PTY) kills the shell inside the CLI
1553
+ * via its own keybinding. Returns false if the provider doesn't support it.
1554
+ */
1555
+ async dismissBackgroundTask(agentId, taskId) {
1556
+ const agent = this.requireSessionAgent(agentId);
1557
+ if (typeof agent.session.dismissBackgroundTask !== "function") {
1558
+ return false;
1559
+ }
1560
+ return agent.session.dismissBackgroundTask(taskId);
1561
+ }
1562
+ async cancelAgentRun(agentId) {
1563
+ const agent = this.requireSessionAgent(agentId);
1564
+ const pendingRun = this.foregroundRuns.getPendingRun(agentId);
1565
+ const foregroundTurnId = agent.activeForegroundTurnId;
1566
+ const hasForegroundTurn = Boolean(foregroundTurnId);
1567
+ const isAutonomousRunning = agent.lifecycle === "running" && !hasForegroundTurn && !pendingRun;
1568
+ if (!hasForegroundTurn && !isAutonomousRunning && !pendingRun) {
1569
+ return false;
1570
+ }
1571
+ await this.interruptSession(agent.session, agentId);
1572
+ // The interrupt will produce a turn_canceled/turn_failed event via subscribe(),
1573
+ // which flows through the session event dispatcher and settles the foreground turn waiter.
1574
+ // Wait briefly for the event to propagate if there's an active foreground turn.
1575
+ if (foregroundTurnId) {
1576
+ const waiter = Array.from(agent.foregroundTurnWaiters).find((candidate) => candidate.turnId === foregroundTurnId);
1577
+ const timeout = new Promise((resolvePromise) => setTimeout(resolvePromise, 2000));
1578
+ if (waiter) {
1579
+ await Promise.race([waiter.settledPromise, timeout]);
1580
+ }
1581
+ else if (agent.activeForegroundTurnId === foregroundTurnId) {
1582
+ await Promise.race([
1583
+ new Promise((resolvePromise) => {
1584
+ const unsubscribe = this.subscribe((event) => {
1585
+ if (event.type === "agent_state" &&
1586
+ event.agent.id === agentId &&
1587
+ !event.agent.activeForegroundTurnId) {
1588
+ unsubscribe();
1589
+ resolvePromise();
1590
+ }
1591
+ }, { agentId, replayState: false });
1592
+ }),
1593
+ timeout,
1594
+ ]);
1595
+ }
1596
+ // The waiter settling wakes up the streamForwarder generator, but its
1597
+ // finally block (which deletes the pendingForegroundRun) runs asynchronously.
1598
+ // Wait for the pending run to be fully cleaned up so the next streamAgent
1599
+ // call doesn't see a stale entry and reject with "already has an active run".
1600
+ if (pendingRun && !pendingRun.settled) {
1601
+ await Promise.race([pendingRun.settledPromise, timeout]);
1602
+ }
1603
+ }
1604
+ else if (pendingRun) {
1605
+ const timeout = new Promise((resolvePromise) => setTimeout(resolvePromise, 2000));
1606
+ await Promise.race([pendingRun.settledPromise, timeout]);
1607
+ }
1608
+ // If the foreground turn is still stuck after the timeout, force-dispatch a
1609
+ // synthetic turn_canceled so the normal event pipeline cleans up
1610
+ // activeForegroundTurnId, settles waiters, and unblocks the streamForwarder.
1611
+ if (foregroundTurnId && agent.activeForegroundTurnId === foregroundTurnId) {
1612
+ this.logger.warn({ agentId, foregroundTurnId }, "cancelAgentRun: foreground turn still active after timeout, force-canceling");
1613
+ void this.dispatchSessionEvent(agent, {
1614
+ type: "turn_canceled",
1615
+ provider: agent.provider,
1616
+ reason: "interrupted",
1617
+ turnId: foregroundTurnId,
1618
+ });
1619
+ // The synthetic event unblocks the streamForwarder generator, whose finally
1620
+ // block settles the pending foreground run asynchronously. Wait for it.
1621
+ const staleRun = this.foregroundRuns.getPendingRun(agentId);
1622
+ if (staleRun && !staleRun.settled) {
1623
+ await staleRun.settledPromise;
1624
+ }
1625
+ }
1626
+ // Clear any pending permissions that weren't cleaned up by handleStreamEvent.
1627
+ if (agent.pendingPermissions.size > 0) {
1628
+ for (const [requestId] of agent.pendingPermissions) {
1629
+ this.dispatchStream(agent.id, {
1630
+ type: "permission_resolved",
1631
+ provider: agent.provider,
1632
+ requestId,
1633
+ resolution: { behavior: "deny", message: "Interrupted" },
1634
+ }, { timestamp: new Date().toISOString() });
1635
+ }
1636
+ agent.pendingPermissions.clear();
1637
+ this.touchUpdatedAt(agent);
1638
+ this.emitState(agent);
1639
+ }
1640
+ return true;
1641
+ }
1642
+ async interruptSession(session, agentId) {
1643
+ try {
1644
+ const result = await this.waitWithTimeout({
1645
+ operation: session.interrupt(),
1646
+ timeoutMs: this.rescueTimeouts.interruptSessionMs,
1647
+ onLateError: (error) => {
1648
+ this.logger.warn({ err: error, agentId }, "Session interrupt failed after timeout during cancel");
1649
+ },
1650
+ });
1651
+ if (result === "timed_out") {
1652
+ this.logger.warn({ agentId, timeoutMs: this.rescueTimeouts.interruptSessionMs }, "Timed out interrupting session during cancel");
1653
+ }
1654
+ }
1655
+ catch (error) {
1656
+ this.logger.error({ err: error, agentId }, "Failed to interrupt session");
1657
+ }
1658
+ }
1659
+ getPendingPermissions(agentId) {
1660
+ const agent = this.requireSessionAgent(agentId);
1661
+ return Array.from(agent.pendingPermissions.values());
1662
+ }
1663
+ peekPendingPermission(agent) {
1664
+ const iterator = agent.pendingPermissions.values().next();
1665
+ return iterator.done ? null : iterator.value;
1666
+ }
1667
+ /**
1668
+ * Hydrates the timeline from provider history if the agent's durable
1669
+ * timeline is empty (e.g., imported agents that have provider history
1670
+ * on disk but no persisted timeline rows). No-ops if already hydrated.
1671
+ */
1672
+ async hydrateTimelineFromProvider(agentId, options) {
1673
+ const agent = this.requireSessionAgent(agentId);
1674
+ await this.hydrateTimelineFromLegacyProviderHistory(agent, options);
1675
+ }
1676
+ async rewind(agentId, messageId, mode) {
1677
+ const agent = this.requireSessionAgent(agentId);
1678
+ const hadActiveRun = Boolean(agent.activeForegroundTurnId) || this.foregroundRuns.hasPendingRun(agentId);
1679
+ if (hadActiveRun) {
1680
+ await this.cancelAgentRun(agentId);
1681
+ }
1682
+ const lock = this.foregroundRuns.createPendingRun(agentId);
1683
+ try {
1684
+ this.logger.info({ agentId, provider: agent.provider, messageId, mode }, "agent.rewind.start");
1685
+ await invokeRewindCapability(agent.session, { messageId, mode });
1686
+ if (mode !== "files") {
1687
+ await this.hydrateTimelineFromProvider(agentId, { force: true, broadcast: true });
1688
+ }
1689
+ await this.refreshRuntimeInfo(agent);
1690
+ await this.persistSnapshot(agent);
1691
+ this.logger.info({ agentId, provider: agent.provider, messageId, mode }, "agent.rewind.complete");
1692
+ }
1693
+ catch (error) {
1694
+ this.logger.warn({ err: error, agentId, provider: agent.provider, messageId, mode }, "agent.rewind.failed");
1695
+ throw error;
1696
+ }
1697
+ finally {
1698
+ this.foregroundRuns.settlePendingRun(agentId, lock.token);
1699
+ }
1700
+ }
1701
+ async deleteCommittedTimeline(agentId) {
1702
+ if (!this.durableTimelineStore) {
1703
+ return;
1704
+ }
1705
+ await this.durableTimelineStore.deleteAgent(agentId);
1706
+ }
1707
+ async getLastAssistantMessage(agentId) {
1708
+ const agent = this.agents.get(agentId);
1709
+ if (!agent) {
1710
+ return null;
1711
+ }
1712
+ return await this.getLastAssistantMessageFromStores(agentId);
1713
+ }
1714
+ getLastAssistantMessageFromTimeline(timeline) {
1715
+ return this.getLastAssistantMessageSegmentFromTimeline(timeline)?.text ?? null;
1716
+ }
1717
+ getLastAssistantMessageSegmentFromTimeline(timeline) {
1718
+ // Collect the last contiguous assistant messages (Claude streams chunks)
1719
+ const chunks = [];
1720
+ let startsAtBeginning = false;
1721
+ for (let i = timeline.length - 1; i >= 0; i--) {
1722
+ const item = timeline[i];
1723
+ if (item.type !== "assistant_message") {
1724
+ if (chunks.length) {
1725
+ break;
1726
+ }
1727
+ continue;
1728
+ }
1729
+ chunks.push(item.text);
1730
+ startsAtBeginning = i === 0;
1731
+ }
1732
+ if (!chunks.length) {
1733
+ return null;
1734
+ }
1735
+ return {
1736
+ text: chunks.toReversed().join(""),
1737
+ startsAtBeginning,
1738
+ };
1739
+ }
1740
+ async getLastAssistantMessageFromStores(agentId) {
1741
+ const liveTimeline = this.timelineStore.getItems(agentId);
1742
+ const liveSegment = this.getLastAssistantMessageSegmentFromTimeline(liveTimeline);
1743
+ if (!this.durableTimelineStore) {
1744
+ return liveSegment?.text ?? null;
1745
+ }
1746
+ if (!liveSegment) {
1747
+ return await this.durableTimelineStore.getLastAssistantMessage(agentId);
1748
+ }
1749
+ if (!liveSegment.startsAtBeginning) {
1750
+ return liveSegment.text;
1751
+ }
1752
+ const lastDurableItem = await this.durableTimelineStore.getLastItem(agentId);
1753
+ if (lastDurableItem?.type !== "assistant_message") {
1754
+ return liveSegment.text;
1755
+ }
1756
+ const durableMessage = await this.durableTimelineStore.getLastAssistantMessage(agentId);
1757
+ return durableMessage ? `${durableMessage}${liveSegment.text}` : liveSegment.text;
1758
+ }
1759
+ async getLastItemFromStores(agentId) {
1760
+ const lastLiveItem = this.timelineStore.getLastItem(agentId);
1761
+ if (lastLiveItem) {
1762
+ return lastLiveItem;
1763
+ }
1764
+ if (!this.durableTimelineStore) {
1765
+ return null;
1766
+ }
1767
+ return await this.durableTimelineStore.getLastItem(agentId);
1768
+ }
1769
+ async waitForAgentEvent(agentId, options) {
1770
+ const snapshot = this.getAgent(agentId);
1771
+ if (!snapshot) {
1772
+ throw new Error(`Agent ${agentId} not found`);
1773
+ }
1774
+ const pendingForegroundRun = this.foregroundRuns.getPendingRun(agentId);
1775
+ const hasForegroundTurn = Boolean(snapshot.activeForegroundTurnId) || Boolean(pendingForegroundRun);
1776
+ const immediatePermission = this.peekPendingPermission(snapshot);
1777
+ if (immediatePermission) {
1778
+ return {
1779
+ status: snapshot.lifecycle,
1780
+ permission: immediatePermission,
1781
+ lastMessage: await this.getLastAssistantMessage(agentId),
1782
+ };
1783
+ }
1784
+ const initialStatus = snapshot.lifecycle;
1785
+ const initialBusy = isAgentBusy(initialStatus) || hasForegroundTurn;
1786
+ const waitForActive = options?.waitForActive ?? false;
1787
+ if (!waitForActive && !initialBusy) {
1788
+ return {
1789
+ status: initialStatus,
1790
+ permission: null,
1791
+ lastMessage: await this.getLastAssistantMessage(agentId),
1792
+ };
1793
+ }
1794
+ if (waitForActive && !initialBusy && !hasForegroundTurn) {
1795
+ return {
1796
+ status: initialStatus,
1797
+ permission: null,
1798
+ lastMessage: await this.getLastAssistantMessage(agentId),
1799
+ };
1800
+ }
1801
+ if (options?.signal?.aborted) {
1802
+ throw createAbortError(options.signal, "wait_for_agent aborted");
1803
+ }
1804
+ return await new Promise((resolvePromise, reject) => {
1805
+ // Bug #1 Fix: Check abort signal AGAIN inside Promise constructor
1806
+ // to avoid race condition between pre-Promise check and abort listener registration
1807
+ if (options?.signal?.aborted) {
1808
+ reject(createAbortError(options.signal, "wait_for_agent aborted"));
1809
+ return;
1810
+ }
1811
+ let currentStatus = initialStatus;
1812
+ let hasStarted = isAgentBusy(initialStatus) ||
1813
+ Boolean(snapshot.activeForegroundTurnId) ||
1814
+ Boolean(pendingForegroundRun?.started);
1815
+ let terminalStatusOverride = null;
1816
+ let finished = false;
1817
+ // Bug #3 Fix: Declare unsubscribe and abortHandler upfront so cleanup can reference them
1818
+ let unsubscribe = null;
1819
+ let abortHandler = null;
1820
+ const cleanup = () => {
1821
+ // Clean up subscription
1822
+ if (unsubscribe) {
1823
+ try {
1824
+ unsubscribe();
1825
+ }
1826
+ catch {
1827
+ // ignore cleanup errors
1828
+ }
1829
+ unsubscribe = null;
1830
+ }
1831
+ // Clean up abort listener
1832
+ if (abortHandler && options?.signal) {
1833
+ try {
1834
+ options.signal.removeEventListener("abort", abortHandler);
1835
+ }
1836
+ catch {
1837
+ // ignore cleanup errors
1838
+ }
1839
+ abortHandler = null;
1840
+ }
1841
+ };
1842
+ const finish = (permission) => {
1843
+ if (finished) {
1844
+ return;
1845
+ }
1846
+ finished = true;
1847
+ cleanup();
1848
+ void this.getLastAssistantMessage(agentId)
1849
+ .then((lastMessage) => {
1850
+ resolvePromise({
1851
+ status: currentStatus,
1852
+ permission,
1853
+ lastMessage,
1854
+ });
1855
+ return;
1856
+ })
1857
+ .catch(reject);
1858
+ };
1859
+ // Bug #3 Fix: Set up abort handler BEFORE subscription
1860
+ // to ensure cleanup handlers exist before callback can fire
1861
+ if (options?.signal) {
1862
+ abortHandler = () => {
1863
+ cleanup();
1864
+ reject(createAbortError(options.signal, "wait_for_agent aborted"));
1865
+ };
1866
+ options.signal.addEventListener("abort", abortHandler, { once: true });
1867
+ }
1868
+ // Bug #3 Fix: Now subscribe with cleanup handlers already in place
1869
+ // This prevents race condition if callback fires synchronously with replayState: true
1870
+ unsubscribe = this.subscribe((event) => {
1871
+ if (event.type === "agent_state") {
1872
+ currentStatus = event.agent.lifecycle;
1873
+ const pending = this.peekPendingPermission(event.agent);
1874
+ if (pending) {
1875
+ finish(pending);
1876
+ return;
1877
+ }
1878
+ if (isAgentBusy(event.agent.lifecycle)) {
1879
+ hasStarted = true;
1880
+ return;
1881
+ }
1882
+ if (!waitForActive || hasStarted) {
1883
+ if (terminalStatusOverride) {
1884
+ currentStatus = terminalStatusOverride;
1885
+ }
1886
+ finish(null);
1887
+ }
1888
+ return;
1889
+ }
1890
+ if (event.type === "agent_stream") {
1891
+ if (event.event.type === "permission_requested") {
1892
+ finish(event.event.request);
1893
+ return;
1894
+ }
1895
+ if (event.event.type === "turn_failed") {
1896
+ hasStarted = true;
1897
+ terminalStatusOverride = "error";
1898
+ return;
1899
+ }
1900
+ if (event.event.type === "turn_completed") {
1901
+ hasStarted = true;
1902
+ }
1903
+ if (event.event.type === "turn_canceled") {
1904
+ hasStarted = true;
1905
+ }
1906
+ }
1907
+ }, { agentId, replayState: true });
1908
+ });
1909
+ }
1910
+ async registerSession(session, config, agentId, options) {
1911
+ const resolvedAgentId = validateAgentId(agentId, "registerSession");
1912
+ if (this.agents.has(resolvedAgentId)) {
1913
+ throw new Error(`Agent with id ${resolvedAgentId} already exists`);
1914
+ }
1915
+ const initialPersistedTitle = await this.resolveInitialPersistedTitle(resolvedAgentId, config, options?.initialTitle ?? null);
1916
+ const now = new Date();
1917
+ const { durableTimelineHasRows } = await this.initializeAgentTimelineForRegister({
1918
+ agentId: resolvedAgentId,
1919
+ now,
1920
+ options,
1921
+ });
1922
+ const managed = this.buildManagedAgentForRegister({
1923
+ resolvedAgentId,
1924
+ session,
1925
+ config,
1926
+ now,
1927
+ durableTimelineHasRows,
1928
+ options,
1929
+ });
1930
+ this.agents.set(resolvedAgentId, managed);
1931
+ // Initialize previousStatus to track transitions
1932
+ this.previousStatuses.set(resolvedAgentId, managed.lifecycle);
1933
+ await this.refreshRuntimeInfo(managed, { emit: !options?.publishWhenReady });
1934
+ await this.persistSnapshot(managed, {
1935
+ title: initialPersistedTitle,
1936
+ });
1937
+ if (!options?.publishWhenReady) {
1938
+ this.emitState(managed, { persist: false });
1939
+ }
1940
+ await this.refreshSessionState(managed, { emit: !options?.publishWhenReady });
1941
+ managed.lifecycle = "idle";
1942
+ await this.persistSnapshot(managed);
1943
+ this.emitState(managed, { persist: false });
1944
+ this.subscribeToSession(managed);
1945
+ return { ...managed };
1946
+ }
1947
+ async initializeAgentTimelineForRegister(params) {
1948
+ const { agentId, now, options } = params;
1949
+ const explicitTimelineSeed = buildExplicitTimelineSeedForRegister(now, options);
1950
+ const shouldSeedFromDurable = !explicitTimelineSeed &&
1951
+ !this.timelineStore.has(agentId) &&
1952
+ this.durableTimelineStore !== undefined;
1953
+ const durableTimelineSeed = shouldSeedFromDurable
1954
+ ? await this.loadCommittedTimelineSeed(agentId, now)
1955
+ : null;
1956
+ const durableTimelineHasRows = durableTimelineSeed != null && (durableTimelineSeed.nextSeq ?? 1) > 1;
1957
+ const timelineSeed = explicitTimelineSeed ?? durableTimelineSeed;
1958
+ if (timelineSeed || !this.timelineStore.has(agentId)) {
1959
+ this.timelineStore.initialize(agentId, timelineSeed ?? { timestamp: now.toISOString() });
1960
+ }
1961
+ if (options?.timelineRows?.length) {
1962
+ this.enqueueDurableTimelineBulkInsert(agentId, options.timelineRows);
1963
+ }
1964
+ return { durableTimelineHasRows };
1965
+ }
1966
+ buildManagedAgentForRegister(params) {
1967
+ const { resolvedAgentId, session, config, now, durableTimelineHasRows, options } = params;
1968
+ return {
1969
+ id: resolvedAgentId,
1970
+ provider: config.provider,
1971
+ cwd: config.cwd,
1972
+ workspaceId: options?.workspaceId,
1973
+ session,
1974
+ capabilities: session.capabilities,
1975
+ config,
1976
+ runtimeInfo: undefined,
1977
+ lifecycle: "initializing",
1978
+ createdAt: options?.createdAt ?? now,
1979
+ updatedAt: options?.updatedAt ?? now,
1980
+ availableModes: [],
1981
+ currentModeId: null,
1982
+ pendingPermissions: new Map(),
1983
+ bufferedPermissionResolutions: new Map(),
1984
+ inFlightPermissionResponses: new Set(),
1985
+ pendingReplacement: false,
1986
+ activeForegroundTurnId: null,
1987
+ foregroundTurnWaiters: new Set(),
1988
+ finalizedForegroundTurnIds: new Set(),
1989
+ unsubscribeSession: null,
1990
+ persistence: attachPersistenceCwd(options?.persistence ?? session.describePersistence(), config.cwd),
1991
+ historyPrimed: options?.historyPrimed ?? durableTimelineHasRows,
1992
+ lastUserMessageAt: options?.lastUserMessageAt ?? null,
1993
+ lastUsage: options?.lastUsage,
1994
+ lastError: options?.lastError,
1995
+ attention: resolveInitialAttention(options?.attention),
1996
+ internal: config.internal ?? false,
1997
+ labels: options?.labels ?? {},
1998
+ digest: options?.digest,
1999
+ };
2000
+ }
2001
+ async loadCommittedTimelineSeed(agentId, now) {
2002
+ if (!this.durableTimelineStore) {
2003
+ return { timestamp: now.toISOString() };
2004
+ }
2005
+ return {
2006
+ nextSeq: (await this.durableTimelineStore.getLatestCommittedSeq(agentId)) + 1,
2007
+ timestamp: now.toISOString(),
2008
+ };
2009
+ }
2010
+ prepareAgentForClosure(agent, cancelReason) {
2011
+ this.agentStreamCoalescer.flushAndDiscard(agent.id);
2012
+ this.agents.delete(agent.id);
2013
+ this.previousStatuses.delete(agent.id);
2014
+ if (agent.unsubscribeSession) {
2015
+ agent.unsubscribeSession();
2016
+ agent.unsubscribeSession = null;
2017
+ }
2018
+ this.foregroundRuns.cancelWaiters(agent, (turnId) => ({
2019
+ type: "turn_canceled",
2020
+ provider: agent.provider,
2021
+ reason: cancelReason,
2022
+ turnId,
2023
+ }));
2024
+ this.foregroundRuns.settlePendingRun(agent.id);
2025
+ return {
2026
+ ...agent,
2027
+ lifecycle: "closed",
2028
+ session: null,
2029
+ activeForegroundTurnId: null,
2030
+ };
2031
+ }
2032
+ emitClosedAgent(agent, options) {
2033
+ this.emitState(agent, options);
2034
+ }
2035
+ subscribeToSession(agent) {
2036
+ if (agent.unsubscribeSession) {
2037
+ return;
2038
+ }
2039
+ const agentId = agent.id;
2040
+ const unsubscribe = agent.session.subscribe((event) => {
2041
+ this.enqueueSessionEvent(agentId, event);
2042
+ });
2043
+ agent.unsubscribeSession = unsubscribe;
2044
+ }
2045
+ enqueueSessionEvent(agentId, event) {
2046
+ this.logger.trace({
2047
+ agentId,
2048
+ provider: event.provider,
2049
+ sessionId: this.agents.get(agentId)?.persistence?.sessionId ?? undefined,
2050
+ turnId: getAgentStreamEventTurnId(event),
2051
+ event,
2052
+ }, "agent.manager.enqueue");
2053
+ const previous = this.sessionEventTails.get(agentId) ?? Promise.resolve();
2054
+ const next = previous
2055
+ .catch(() => undefined)
2056
+ .then(async () => {
2057
+ const current = this.agents.get(agentId);
2058
+ if (!current) {
2059
+ return;
2060
+ }
2061
+ if (current.session == null) {
2062
+ return;
2063
+ }
2064
+ this.logger.trace({
2065
+ agentId,
2066
+ provider: event.provider,
2067
+ sessionId: current.persistence?.sessionId ?? undefined,
2068
+ turnId: getAgentStreamEventTurnId(event),
2069
+ event,
2070
+ }, "agent.manager.dequeue");
2071
+ await this.dispatchSessionEvent(current, event);
2072
+ return;
2073
+ })
2074
+ .catch((err) => {
2075
+ this.logger.error({ err, agentId, eventType: event.type }, "Failed to process session event");
2076
+ });
2077
+ this.sessionEventTails.set(agentId, next);
2078
+ this.trackBackgroundTask(next);
2079
+ void next.finally(() => {
2080
+ if (this.sessionEventTails.get(agentId) === next) {
2081
+ this.sessionEventTails.delete(agentId);
2082
+ }
2083
+ });
2084
+ }
2085
+ async dispatchSessionEvent(agent, event) {
2086
+ const turnId = getAgentStreamEventTurnId(event);
2087
+ const matchingWaiters = this.foregroundRuns.getMatchingWaiters(agent, turnId);
2088
+ this.logger.trace({
2089
+ agentId: agent.id,
2090
+ provider: event.provider,
2091
+ sessionId: agent.persistence?.sessionId ?? undefined,
2092
+ turnId,
2093
+ matchingWaiterCount: matchingWaiters.length,
2094
+ event,
2095
+ }, "agent.manager.dispatch_session_event");
2096
+ const shouldNotifyWaiters = await this.handleStreamEvent(agent, event);
2097
+ if (!shouldNotifyWaiters) {
2098
+ return;
2099
+ }
2100
+ this.foregroundRuns.notifyWaiters(matchingWaiters, event, {
2101
+ terminal: isTurnTerminalEvent(event),
2102
+ });
2103
+ this.logger.trace({
2104
+ agentId: agent.id,
2105
+ provider: event.provider,
2106
+ sessionId: agent.persistence?.sessionId ?? undefined,
2107
+ turnId,
2108
+ notifiedWaiterCount: matchingWaiters.length,
2109
+ terminal: isTurnTerminalEvent(event),
2110
+ event,
2111
+ }, "agent.manager.notify_waiters");
2112
+ }
2113
+ async resolveInitialPersistedTitle(agentId, config, fallbackTitle) {
2114
+ const existing = await this.registry?.get(agentId);
2115
+ if (existing) {
2116
+ return existing.title ?? null;
2117
+ }
2118
+ const explicitTitle = typeof config.title === "string" && config.title.trim().length > 0
2119
+ ? config.title.trim()
2120
+ : null;
2121
+ return explicitTitle ?? fallbackTitle;
2122
+ }
2123
+ async persistSnapshot(agent, options) {
2124
+ if (!this.registry) {
2125
+ return;
2126
+ }
2127
+ // Don't persist internal agents - they're ephemeral system tasks
2128
+ if (agent.internal) {
2129
+ return;
2130
+ }
2131
+ await this.registry.applySnapshot(agent, options);
2132
+ }
2133
+ requireRegistry() {
2134
+ if (!this.registry) {
2135
+ throw new Error("Agent storage unavailable");
2136
+ }
2137
+ return this.registry;
2138
+ }
2139
+ async refreshSessionState(agent, options) {
2140
+ try {
2141
+ const modes = await agent.session.getAvailableModes();
2142
+ agent.availableModes = modes;
2143
+ }
2144
+ catch {
2145
+ agent.availableModes = [];
2146
+ }
2147
+ try {
2148
+ agent.currentModeId = await agent.session.getCurrentMode();
2149
+ }
2150
+ catch {
2151
+ agent.currentModeId = null;
2152
+ }
2153
+ try {
2154
+ const pending = agent.session.getPendingPermissions();
2155
+ agent.pendingPermissions = new Map(pending.map((request) => [request.id, request]));
2156
+ }
2157
+ catch {
2158
+ agent.pendingPermissions.clear();
2159
+ }
2160
+ this.syncFeaturesFromSession(agent);
2161
+ await this.refreshRuntimeInfo(agent, options);
2162
+ }
2163
+ async refreshRuntimeInfo(agent, options) {
2164
+ try {
2165
+ const newInfo = await agent.session.getRuntimeInfo();
2166
+ const changed = newInfo.model !== agent.runtimeInfo?.model ||
2167
+ newInfo.thinkingOptionId !== agent.runtimeInfo?.thinkingOptionId ||
2168
+ newInfo.sessionId !== agent.runtimeInfo?.sessionId ||
2169
+ newInfo.modeId !== agent.runtimeInfo?.modeId;
2170
+ agent.runtimeInfo = newInfo;
2171
+ if (!agent.persistence && newInfo.sessionId) {
2172
+ agent.persistence = attachPersistenceCwd({ provider: agent.provider, sessionId: newInfo.sessionId }, agent.cwd);
2173
+ }
2174
+ // Emit state if runtimeInfo changed so clients get the updated model
2175
+ if (changed && options?.emit !== false) {
2176
+ this.emitState(agent);
2177
+ }
2178
+ }
2179
+ catch {
2180
+ // Keep existing runtimeInfo if refresh fails.
2181
+ }
2182
+ }
2183
+ async hydrateTimelineFromLegacyProviderHistory(agent, options) {
2184
+ if (agent.historyPrimed && !options?.force) {
2185
+ return;
2186
+ }
2187
+ if (options?.force) {
2188
+ const historyEvents = [];
2189
+ for await (const event of agent.session.streamHistory()) {
2190
+ if (event.type === "timeline") {
2191
+ if (event.item.type === "user_message" && isSystemInjectedEnvelope(event.item.text)) {
2192
+ continue;
2193
+ }
2194
+ historyEvents.push(event);
2195
+ }
2196
+ }
2197
+ this.agentStreamCoalescer.flushAndDiscard(agent.id);
2198
+ await this.deleteCommittedTimeline(agent.id);
2199
+ this.timelineStore.delete(agent.id);
2200
+ this.timelineStore.initialize(agent.id, { timestamp: new Date().toISOString() });
2201
+ agent.historyPrimed = true;
2202
+ for (const event of historyEvents) {
2203
+ const row = this.recordTimeline(agent.id, event.item, event.timestamp ? { timestamp: event.timestamp } : undefined);
2204
+ if (options?.broadcast) {
2205
+ this.dispatchStream(agent.id, event, {
2206
+ seq: row.seq,
2207
+ epoch: this.timelineStore.getEpoch(agent.id),
2208
+ timestamp: row.timestamp,
2209
+ });
2210
+ }
2211
+ }
2212
+ this.touchUpdatedAt(agent);
2213
+ this.emitState(agent);
2214
+ return;
2215
+ }
2216
+ agent.historyPrimed = true;
2217
+ try {
2218
+ for await (const event of agent.session.streamHistory()) {
2219
+ if (event.type !== "timeline") {
2220
+ continue;
2221
+ }
2222
+ if (event.item.type === "user_message" && isSystemInjectedEnvelope(event.item.text)) {
2223
+ continue;
2224
+ }
2225
+ this.recordTimeline(agent.id, event.item, event.timestamp ? { timestamp: event.timestamp } : undefined);
2226
+ }
2227
+ }
2228
+ catch {
2229
+ // ignore history failures
2230
+ }
2231
+ }
2232
+ notifyForegroundTurnWaiters(agentId, event) {
2233
+ const turnId = getAgentStreamEventTurnId(event);
2234
+ if (turnId == null) {
2235
+ return;
2236
+ }
2237
+ const agent = this.agents.get(agentId);
2238
+ if (!agent) {
2239
+ return;
2240
+ }
2241
+ this.foregroundRuns.notifyAgentWaiters(agent, event);
2242
+ this.logger.trace({
2243
+ agentId,
2244
+ provider: event.provider,
2245
+ sessionId: agent.persistence?.sessionId ?? undefined,
2246
+ turnId,
2247
+ event,
2248
+ }, "agent.manager.notify_waiters.coalesced");
2249
+ }
2250
+ async handleStreamEvent(agent, event, options) {
2251
+ const eventTurnId = getAgentStreamEventTurnId(event);
2252
+ const isForegroundEvent = Boolean(eventTurnId && agent.activeForegroundTurnId === eventTurnId);
2253
+ this.traceHandleStreamEventStart(agent, event, eventTurnId, isForegroundEvent);
2254
+ if (eventTurnId &&
2255
+ isTurnTerminalEvent(event) &&
2256
+ this.foregroundRuns.hasFinalizedTurn(agent, eventTurnId)) {
2257
+ return false;
2258
+ }
2259
+ // Only update timestamp for live events, not history replay
2260
+ if (!options?.fromHistory) {
2261
+ this.touchUpdatedAt(agent);
2262
+ if (this.agentStreamCoalescer.handle(agent.id, event)) {
2263
+ this.traceCoalescerBuffered(agent, event, eventTurnId);
2264
+ return false;
2265
+ }
2266
+ this.agentStreamCoalescer.flushFor(agent.id);
2267
+ }
2268
+ const flags = { shouldDispatchEvent: true, shouldNotifyWaiters: true };
2269
+ const dispatchPromise = this.dispatchStreamEventByType({
2270
+ agent,
2271
+ event,
2272
+ options,
2273
+ isForegroundEvent,
2274
+ eventTurnId,
2275
+ flags,
2276
+ });
2277
+ if (dispatchPromise) {
2278
+ await dispatchPromise;
2279
+ }
2280
+ if (!options?.fromHistory && isForegroundEvent && isTurnTerminalEvent(event)) {
2281
+ this.finalizeForegroundTurn(agent, eventTurnId);
2282
+ }
2283
+ if (!options?.fromHistory && flags.shouldDispatchEvent) {
2284
+ this.dispatchStream(agent.id, event, { timestamp: new Date().toISOString() });
2285
+ }
2286
+ this.traceHandleStreamEventEnd(agent, event, eventTurnId, flags);
2287
+ return flags.shouldNotifyWaiters;
2288
+ }
2289
+ traceHandleStreamEventStart(agent, event, turnId, isForegroundEvent) {
2290
+ this.logger.trace({
2291
+ agentId: agent.id,
2292
+ provider: event.provider,
2293
+ sessionId: agent.persistence?.sessionId ?? undefined,
2294
+ turnId,
2295
+ lifecycle: agent.lifecycle,
2296
+ activeForegroundTurnId: agent.activeForegroundTurnId,
2297
+ isForegroundEvent,
2298
+ event,
2299
+ }, "agent.manager.handle_stream_event.start");
2300
+ }
2301
+ traceCoalescerBuffered(agent, event, turnId) {
2302
+ this.logger.trace({
2303
+ agentId: agent.id,
2304
+ provider: event.provider,
2305
+ sessionId: agent.persistence?.sessionId ?? undefined,
2306
+ turnId,
2307
+ event,
2308
+ }, "agent.manager.coalescer.buffer");
2309
+ }
2310
+ traceHandleStreamEventEnd(agent, event, turnId, flags) {
2311
+ this.logger.trace({
2312
+ agentId: agent.id,
2313
+ provider: event.provider,
2314
+ sessionId: agent.persistence?.sessionId ?? undefined,
2315
+ turnId,
2316
+ lifecycle: agent.lifecycle,
2317
+ activeForegroundTurnId: agent.activeForegroundTurnId,
2318
+ shouldDispatchEvent: flags.shouldDispatchEvent,
2319
+ shouldNotifyWaiters: flags.shouldNotifyWaiters,
2320
+ event,
2321
+ }, "agent.manager.handle_stream_event.end");
2322
+ }
2323
+ dispatchStreamEventByType(params) {
2324
+ const { agent, event, options, isForegroundEvent, eventTurnId, flags } = params;
2325
+ switch (event.type) {
2326
+ case "thread_started":
2327
+ this.onStreamThreadStarted(agent);
2328
+ return undefined;
2329
+ case "usage_updated":
2330
+ agent.lastUsage = event.usage;
2331
+ this.emitState(agent);
2332
+ return undefined;
2333
+ case "mode_changed":
2334
+ agent.currentModeId = event.currentModeId;
2335
+ agent.availableModes = event.availableModes;
2336
+ if (agent.runtimeInfo) {
2337
+ agent.runtimeInfo = { ...agent.runtimeInfo, modeId: event.currentModeId };
2338
+ }
2339
+ flags.shouldDispatchEvent = false;
2340
+ this.emitState(agent);
2341
+ return undefined;
2342
+ case "model_changed":
2343
+ agent.runtimeInfo = event.runtimeInfo;
2344
+ if (!agent.persistence && event.runtimeInfo.sessionId) {
2345
+ agent.persistence = attachPersistenceCwd({ provider: agent.provider, sessionId: event.runtimeInfo.sessionId }, agent.cwd);
2346
+ }
2347
+ agent.currentModeId = event.runtimeInfo.modeId ?? agent.currentModeId;
2348
+ flags.shouldDispatchEvent = false;
2349
+ this.emitState(agent);
2350
+ return undefined;
2351
+ case "thinking_option_changed":
2352
+ if (agent.runtimeInfo) {
2353
+ agent.runtimeInfo = {
2354
+ ...agent.runtimeInfo,
2355
+ thinkingOptionId: event.thinkingOptionId,
2356
+ };
2357
+ }
2358
+ flags.shouldDispatchEvent = false;
2359
+ this.emitState(agent);
2360
+ return undefined;
2361
+ case "timeline":
2362
+ return this.onStreamTimelineEvent({ agent, event, options, isForegroundEvent, flags });
2363
+ case "turn_completed":
2364
+ this.onStreamTurnCompleted({ agent, event, eventTurnId, isForegroundEvent });
2365
+ return undefined;
2366
+ case "turn_failed":
2367
+ return this.onStreamTurnFailed({
2368
+ agent,
2369
+ event,
2370
+ eventTurnId,
2371
+ isForegroundEvent,
2372
+ options,
2373
+ });
2374
+ case "turn_canceled":
2375
+ this.onStreamTurnCanceled({ agent, event, eventTurnId, isForegroundEvent, options });
2376
+ return undefined;
2377
+ case "turn_started":
2378
+ this.onStreamTurnStarted({ agent, eventTurnId, isForegroundEvent });
2379
+ return undefined;
2380
+ case "permission_requested":
2381
+ this.onStreamPermissionRequested(agent, event);
2382
+ return undefined;
2383
+ case "permission_resolved":
2384
+ this.onStreamPermissionResolved({ agent, event, options, flags });
2385
+ return undefined;
2386
+ default:
2387
+ return undefined;
2388
+ }
2389
+ }
2390
+ onStreamThreadStarted(agent) {
2391
+ const previousSessionId = agent.persistence?.sessionId ?? null;
2392
+ const handle = agent.session.describePersistence();
2393
+ if (handle) {
2394
+ agent.persistence = attachPersistenceCwd(handle, agent.cwd);
2395
+ if (agent.persistence?.sessionId !== previousSessionId) {
2396
+ this.emitState(agent);
2397
+ }
2398
+ }
2399
+ void this.refreshRuntimeInfo(agent);
2400
+ }
2401
+ async onStreamTimelineEvent(params) {
2402
+ const { agent, event, options, flags } = params;
2403
+ if (event.item.type === "user_message" && isSystemInjectedEnvelope(event.item.text)) {
2404
+ flags.shouldDispatchEvent = false;
2405
+ flags.shouldNotifyWaiters = false;
2406
+ return;
2407
+ }
2408
+ if (options?.fromHistory) {
2409
+ this.recordTimeline(agent.id, event.item, event.timestamp ? { timestamp: event.timestamp } : undefined);
2410
+ flags.shouldDispatchEvent = false;
2411
+ flags.shouldNotifyWaiters = false;
2412
+ return;
2413
+ }
2414
+ this.recordAndDispatchTimelineItem(agent.id, event.item, event.provider, event.turnId);
2415
+ if (event.item.type === "user_message") {
2416
+ agent.lastUserMessageAt = new Date();
2417
+ this.emitState(agent);
2418
+ }
2419
+ flags.shouldDispatchEvent = false;
2420
+ flags.shouldNotifyWaiters = true;
2421
+ }
2422
+ onStreamTurnCompleted(params) {
2423
+ const { agent, event, eventTurnId, isForegroundEvent } = params;
2424
+ this.logger.trace({
2425
+ agentId: agent.id,
2426
+ provider: agent.provider,
2427
+ sessionId: agent.persistence?.sessionId ?? undefined,
2428
+ turnId: eventTurnId,
2429
+ lifecycle: agent.lifecycle,
2430
+ activeForegroundTurnId: agent.activeForegroundTurnId,
2431
+ }, "agent.manager.turn.completed");
2432
+ agent.lastUsage = event.usage;
2433
+ agent.lastError = undefined;
2434
+ if (!isForegroundEvent && agent.lifecycle !== "idle" && !agent.pendingReplacement) {
2435
+ agent.lifecycle = "idle";
2436
+ this.emitState(agent);
2437
+ }
2438
+ void this.refreshRuntimeInfo(agent);
2439
+ }
2440
+ async onStreamTurnFailed(params) {
2441
+ const { agent, event, eventTurnId, isForegroundEvent, options } = params;
2442
+ this.logger.warn({
2443
+ agentId: agent.id,
2444
+ provider: agent.provider,
2445
+ sessionId: agent.persistence?.sessionId ?? undefined,
2446
+ turnId: eventTurnId,
2447
+ lifecycle: agent.lifecycle,
2448
+ activeForegroundTurnId: agent.activeForegroundTurnId,
2449
+ eventTurnId,
2450
+ error: event.error,
2451
+ code: event.code,
2452
+ diagnostic: event.diagnostic,
2453
+ }, "handleStreamEvent: turn_failed");
2454
+ if (!isForegroundEvent) {
2455
+ agent.lifecycle = "error";
2456
+ }
2457
+ agent.lastError = event.error;
2458
+ await this.appendSystemErrorTimelineMessage(agent, event.provider, this.formatTurnFailedMessage(event), options);
2459
+ this.resolvePendingPermissionsForAgent(agent, event.provider, options, "Turn failed");
2460
+ if (!isForegroundEvent) {
2461
+ this.emitState(agent);
2462
+ }
2463
+ }
2464
+ onStreamTurnCanceled(params) {
2465
+ const { agent, event, eventTurnId, isForegroundEvent, options } = params;
2466
+ this.logger.trace({
2467
+ agentId: agent.id,
2468
+ provider: agent.provider,
2469
+ sessionId: agent.persistence?.sessionId ?? undefined,
2470
+ turnId: eventTurnId,
2471
+ lifecycle: agent.lifecycle,
2472
+ activeForegroundTurnId: agent.activeForegroundTurnId,
2473
+ eventTurnId,
2474
+ }, "agent.manager.turn.canceled");
2475
+ if (!isForegroundEvent && !agent.pendingReplacement) {
2476
+ agent.lifecycle = "idle";
2477
+ }
2478
+ agent.lastError = undefined;
2479
+ this.resolvePendingPermissionsForAgent(agent, event.provider, options, "Interrupted");
2480
+ if (!isForegroundEvent) {
2481
+ this.emitState(agent);
2482
+ }
2483
+ }
2484
+ onStreamTurnStarted(params) {
2485
+ const { agent, eventTurnId, isForegroundEvent } = params;
2486
+ this.logger.trace({
2487
+ agentId: agent.id,
2488
+ provider: agent.provider,
2489
+ sessionId: agent.persistence?.sessionId ?? undefined,
2490
+ turnId: eventTurnId,
2491
+ lifecycle: agent.lifecycle,
2492
+ activeForegroundTurnId: agent.activeForegroundTurnId,
2493
+ }, "agent.manager.turn.started");
2494
+ if (!isForegroundEvent) {
2495
+ agent.lifecycle = "running";
2496
+ this.emitState(agent);
2497
+ }
2498
+ }
2499
+ onStreamPermissionRequested(agent, event) {
2500
+ const hadPendingPermissions = agent.pendingPermissions.size > 0;
2501
+ agent.pendingPermissions.set(event.request.id, event.request);
2502
+ if (!hadPendingPermissions && !agent.internal) {
2503
+ this.broadcastAgentAttention(agent, "permission");
2504
+ }
2505
+ this.emitState(agent);
2506
+ }
2507
+ onStreamPermissionResolved(params) {
2508
+ const { agent, event, options, flags } = params;
2509
+ agent.pendingPermissions.delete(event.requestId);
2510
+ if (!options?.fromHistory && agent.inFlightPermissionResponses.has(event.requestId)) {
2511
+ agent.bufferedPermissionResolutions.set(event.requestId, event);
2512
+ flags.shouldDispatchEvent = false;
2513
+ return;
2514
+ }
2515
+ this.emitState(agent);
2516
+ }
2517
+ resolvePendingPermissionsForAgent(agent, provider, options, message) {
2518
+ for (const [requestId] of agent.pendingPermissions) {
2519
+ agent.pendingPermissions.delete(requestId);
2520
+ if (!options?.fromHistory) {
2521
+ this.dispatchStream(agent.id, {
2522
+ type: "permission_resolved",
2523
+ provider,
2524
+ requestId,
2525
+ resolution: { behavior: "deny", message },
2526
+ });
2527
+ }
2528
+ }
2529
+ }
2530
+ recordAndDispatchTimelineItem(agentId, item, provider, turnId) {
2531
+ const row = this.recordTimeline(agentId, item);
2532
+ const event = {
2533
+ type: "timeline",
2534
+ item,
2535
+ provider,
2536
+ ...(turnId !== undefined ? { turnId } : {}),
2537
+ };
2538
+ this.dispatchStream(agentId, event, {
2539
+ seq: row.seq,
2540
+ epoch: this.timelineStore.getEpoch(agentId),
2541
+ timestamp: row.timestamp,
2542
+ });
2543
+ if (item.type === "tool_call" &&
2544
+ item.status === "completed" &&
2545
+ item.detail?.type === "shell" &&
2546
+ commandMayHaveChangedExternalState(item.detail.command)) {
2547
+ const agent = this.agents.get(agentId);
2548
+ if (agent) {
2549
+ this.onWorkspaceStateMayHaveChanged?.({ cwd: agent.cwd });
2550
+ }
2551
+ }
2552
+ return event;
2553
+ }
2554
+ async appendSystemErrorTimelineMessage(agent, provider, message, options) {
2555
+ if (options?.fromHistory) {
2556
+ return;
2557
+ }
2558
+ const normalized = message.trim();
2559
+ if (!normalized) {
2560
+ return;
2561
+ }
2562
+ const text = `${SYSTEM_ERROR_PREFIX} ${normalized}`;
2563
+ const lastItem = await this.getLastItemFromStores(agent.id);
2564
+ if (lastItem?.type === "assistant_message" && lastItem.text === text) {
2565
+ return;
2566
+ }
2567
+ const item = { type: "assistant_message", text };
2568
+ const row = this.recordTimeline(agent.id, item);
2569
+ this.dispatchStream(agent.id, {
2570
+ type: "timeline",
2571
+ item,
2572
+ provider,
2573
+ }, {
2574
+ seq: row.seq,
2575
+ epoch: this.timelineStore.getEpoch(agent.id),
2576
+ timestamp: row.timestamp,
2577
+ });
2578
+ }
2579
+ formatTurnFailedMessage(event) {
2580
+ const base = event.error.trim();
2581
+ const parts = [base.length > 0 ? base : "Provider run failed"];
2582
+ const code = event.code?.trim();
2583
+ if (code) {
2584
+ parts.push(`code: ${code}`);
2585
+ }
2586
+ const diagnostic = event.diagnostic?.trim();
2587
+ if (diagnostic && diagnostic !== base) {
2588
+ parts.push(diagnostic);
2589
+ }
2590
+ return parts.join("\n\n");
2591
+ }
2592
+ recordTimeline(agentId, item, options) {
2593
+ const row = this.timelineStore.append(agentId, item, options);
2594
+ this.enqueueDurableTimelineAppend(agentId, row);
2595
+ return row;
2596
+ }
2597
+ emitState(agent, options) {
2598
+ // Keep attention as an edge-triggered unread signal, not a level signal.
2599
+ this.checkAndSetAttention(agent);
2600
+ if (options?.persist !== false) {
2601
+ this.enqueueBackgroundPersist(agent);
2602
+ }
2603
+ this.syncFeaturesFromSession(agent);
2604
+ this.logger.trace({
2605
+ agentId: agent.id,
2606
+ provider: agent.provider,
2607
+ sessionId: agent.persistence?.sessionId ?? undefined,
2608
+ turnId: agent.activeForegroundTurnId ?? undefined,
2609
+ lifecycle: agent.lifecycle,
2610
+ activeForegroundTurnId: agent.activeForegroundTurnId,
2611
+ pendingPermissions: agent.pendingPermissions.size,
2612
+ persist: options?.persist !== false,
2613
+ }, "agent.manager.emit_state");
2614
+ this.dispatch({
2615
+ type: "agent_state",
2616
+ agent: { ...agent },
2617
+ });
2618
+ }
2619
+ syncFeaturesFromSession(agent) {
2620
+ if ("session" in agent && agent.session?.features) {
2621
+ agent.features = agent.session.features;
2622
+ }
2623
+ }
2624
+ checkAndSetAttention(agent) {
2625
+ const previousStatus = this.previousStatuses.get(agent.id);
2626
+ const currentStatus = agent.lifecycle;
2627
+ // Track the new status
2628
+ this.previousStatuses.set(agent.id, currentStatus);
2629
+ // Skip attention tracking for internal agents
2630
+ if (agent.internal) {
2631
+ return;
2632
+ }
2633
+ // Skip if already requires attention
2634
+ if (agent.attention.requiresAttention) {
2635
+ return;
2636
+ }
2637
+ // Check if agent transitioned from running to idle (finished)
2638
+ if (previousStatus === "running" && currentStatus === "idle") {
2639
+ agent.attention = {
2640
+ requiresAttention: true,
2641
+ attentionReason: "finished",
2642
+ attentionTimestamp: new Date(),
2643
+ };
2644
+ this.broadcastAgentAttention(agent, "finished");
2645
+ return;
2646
+ }
2647
+ // Check if agent entered error state
2648
+ if (previousStatus !== "error" && currentStatus === "error") {
2649
+ agent.attention = {
2650
+ requiresAttention: true,
2651
+ attentionReason: "error",
2652
+ attentionTimestamp: new Date(),
2653
+ };
2654
+ this.broadcastAgentAttention(agent, "error");
2655
+ return;
2656
+ }
2657
+ }
2658
+ enqueueBackgroundPersist(agent) {
2659
+ const task = this.persistSnapshot(agent).catch((err) => {
2660
+ this.logger.error({ err, agentId: agent.id }, "Failed to persist agent snapshot");
2661
+ });
2662
+ this.trackBackgroundTask(task);
2663
+ }
2664
+ enqueueDurableTimelineAppend(agentId, row) {
2665
+ if (!this.durableTimelineStore) {
2666
+ return;
2667
+ }
2668
+ const task = this.durableTimelineStore
2669
+ .bulkInsert(agentId, [row])
2670
+ .then(() => undefined)
2671
+ .catch((err) => {
2672
+ this.logger.error({ err, agentId, seq: row.seq, itemType: row.item.type }, "Failed to append timeline row to durable store");
2673
+ });
2674
+ this.trackBackgroundTask(task);
2675
+ }
2676
+ enqueueDurableTimelineBulkInsert(agentId, rows) {
2677
+ if (!this.durableTimelineStore || rows.length === 0) {
2678
+ return;
2679
+ }
2680
+ const task = this.durableTimelineStore.bulkInsert(agentId, rows).catch((err) => {
2681
+ this.logger.error({ err, agentId, rowCount: rows.length }, "Failed to seed durable timeline store");
2682
+ });
2683
+ this.trackBackgroundTask(task);
2684
+ }
2685
+ trackBackgroundTask(task) {
2686
+ this.backgroundTasks.add(task);
2687
+ void task.finally(() => {
2688
+ this.backgroundTasks.delete(task);
2689
+ });
2690
+ }
2691
+ /**
2692
+ * Flush any background persistence work (best-effort).
2693
+ * Used by daemon shutdown paths to avoid unhandled rejections after cleanup.
2694
+ */
2695
+ async flush() {
2696
+ this.agentStreamCoalescer.flushAll();
2697
+ // Drain tasks, including tasks spawned while awaiting.
2698
+ while (this.backgroundTasks.size > 0) {
2699
+ const pending = Array.from(this.backgroundTasks);
2700
+ await Promise.allSettled(pending);
2701
+ }
2702
+ }
2703
+ broadcastAgentAttention(agent, reason) {
2704
+ if (isDelegatedAgent(agent)) {
2705
+ return;
2706
+ }
2707
+ if (this.attentionListeners.size === 0) {
2708
+ return;
2709
+ }
2710
+ const params = {
2711
+ agentId: agent.id,
2712
+ provider: agent.provider,
2713
+ reason,
2714
+ };
2715
+ for (const listener of this.attentionListeners) {
2716
+ try {
2717
+ listener(params);
2718
+ }
2719
+ catch (err) {
2720
+ this.logger.warn({ err, agentId: agent.id }, "Attention listener threw");
2721
+ }
2722
+ }
2723
+ }
2724
+ dispatchStream(agentId, event, metadata) {
2725
+ const agent = this.agents.get(agentId);
2726
+ this.logger.trace({
2727
+ agentId,
2728
+ provider: event.provider,
2729
+ sessionId: agent?.persistence?.sessionId ?? undefined,
2730
+ turnId: getAgentStreamEventTurnId(event),
2731
+ metadata,
2732
+ event,
2733
+ }, "agent.manager.dispatch_stream");
2734
+ this.dispatch({ type: "agent_stream", agentId, event, ...metadata });
2735
+ }
2736
+ dispatch(event) {
2737
+ for (const subscriber of this.subscribers) {
2738
+ if (subscriber.agentId &&
2739
+ event.type === "agent_stream" &&
2740
+ subscriber.agentId !== event.agentId) {
2741
+ continue;
2742
+ }
2743
+ if (subscriber.agentId &&
2744
+ event.type === "agent_state" &&
2745
+ subscriber.agentId !== event.agent.id) {
2746
+ continue;
2747
+ }
2748
+ // Skip internal agents for global subscribers (those without a specific agentId)
2749
+ if (!subscriber.agentId) {
2750
+ if (event.type === "agent_state" && event.agent.internal) {
2751
+ continue;
2752
+ }
2753
+ if (event.type === "agent_stream") {
2754
+ const agent = this.agents.get(event.agentId);
2755
+ if (agent?.internal) {
2756
+ continue;
2757
+ }
2758
+ }
2759
+ }
2760
+ subscriber.callback(event);
2761
+ }
2762
+ }
2763
+ async normalizeConfig(config, options = {}) {
2764
+ const normalized = { ...config };
2765
+ // Always resolve cwd to absolute path for consistent history file lookup
2766
+ if (normalized.cwd) {
2767
+ normalized.cwd = resolve(normalized.cwd);
2768
+ try {
2769
+ const cwdStats = await stat(normalized.cwd);
2770
+ if (!cwdStats.isDirectory()) {
2771
+ throw new Error(`Working directory is not a directory: ${normalized.cwd}`);
2772
+ }
2773
+ }
2774
+ catch (error) {
2775
+ if (error instanceof Error &&
2776
+ "code" in error &&
2777
+ error.code === "ENOENT") {
2778
+ throw new Error(`Working directory does not exist: ${normalized.cwd}`, { cause: error });
2779
+ }
2780
+ if (error instanceof Error) {
2781
+ throw error;
2782
+ }
2783
+ throw new Error(`Failed to access working directory: ${normalized.cwd}`, { cause: error });
2784
+ }
2785
+ }
2786
+ if (typeof normalized.model === "string") {
2787
+ const trimmed = normalized.model.trim();
2788
+ normalized.model = trimmed.length > 0 && trimmed !== "default" ? trimmed : undefined;
2789
+ }
2790
+ const shouldResolveDefaultModel = options.resolveDefaultModel ?? true;
2791
+ if (shouldResolveDefaultModel && !normalized.model) {
2792
+ const defaultModelId = await this.resolveDefaultModelId(normalized);
2793
+ if (defaultModelId) {
2794
+ normalized.model = defaultModelId;
2795
+ }
2796
+ }
2797
+ if (!normalized.modeId) {
2798
+ try {
2799
+ normalized.modeId =
2800
+ getAgentProviderDefinition(normalized.provider).defaultModeId ?? undefined;
2801
+ }
2802
+ catch {
2803
+ // Unknown provider
2804
+ }
2805
+ }
2806
+ return normalized;
2807
+ }
2808
+ async resolveDefaultModelId(config) {
2809
+ const client = this.clients.get(config.provider);
2810
+ if (!client) {
2811
+ return undefined;
2812
+ }
2813
+ try {
2814
+ const catalog = await client.fetchCatalog({
2815
+ scope: "workspace",
2816
+ cwd: config.cwd,
2817
+ force: false,
2818
+ });
2819
+ return (catalog.models.find((model) => model.isDefault) ?? catalog.models[0])?.id;
2820
+ }
2821
+ catch {
2822
+ // Provider may not support model listing — leave model undefined.
2823
+ return undefined;
2824
+ }
2825
+ }
2826
+ async prepareSessionConfig(config, agentId) {
2827
+ const storedConfig = await this.normalizeConfig(stripInternalPaseoMcpServer(config));
2828
+ const launchConfig = this.applyDaemonAppendSystemPrompt(withRuntimePaseoMcpServer({
2829
+ config: storedConfig,
2830
+ agentId,
2831
+ mcpBaseUrl: this.mcpBaseUrl,
2832
+ mcpAuthToken: this.mcpAuthToken,
2833
+ }));
2834
+ return { storedConfig, launchConfig };
2835
+ }
2836
+ applyDaemonAppendSystemPrompt(config) {
2837
+ const daemonAppendSystemPrompt = this.appendSystemPrompt.trim();
2838
+ const next = { ...config };
2839
+ delete next.daemonAppendSystemPrompt;
2840
+ return daemonAppendSystemPrompt
2841
+ ? {
2842
+ ...next,
2843
+ daemonAppendSystemPrompt,
2844
+ }
2845
+ : next;
2846
+ }
2847
+ async buildLaunchContext(agentId, client, env) {
2848
+ const context = {
2849
+ agentId,
2850
+ env: {
2851
+ ...env,
2852
+ PASEO_AGENT_ID: agentId,
2853
+ },
2854
+ };
2855
+ if (this.paseoToolsEnabled &&
2856
+ client.capabilities.supportsNativePaseoTools &&
2857
+ this.paseoToolCatalogFactory) {
2858
+ context.paseoTools = await this.paseoToolCatalogFactory({ callerAgentId: agentId });
2859
+ }
2860
+ return context;
2861
+ }
2862
+ resolveProviderLaunchConfig(launchConfig, launchContext) {
2863
+ return launchContext.paseoTools ? stripInternalPaseoMcpServer(launchConfig) : launchConfig;
2864
+ }
2865
+ async requireAvailableClient(options) {
2866
+ const client = this.clients.get(options.provider);
2867
+ if (!client) {
2868
+ const configuredProviders = this.getConfiguredProviderIds();
2869
+ throw new Error(`Unknown provider '${options.provider}'. Configured providers: ${formatProviderList(configuredProviders)}.`);
2870
+ }
2871
+ let unavailableReason = null;
2872
+ try {
2873
+ const available = await client.isAvailable();
2874
+ if (available) {
2875
+ return client;
2876
+ }
2877
+ }
2878
+ catch (error) {
2879
+ unavailableReason = error instanceof Error ? error.message : String(error);
2880
+ }
2881
+ const availableProviders = (await this.listProviderAvailability())
2882
+ .filter((entry) => entry.available)
2883
+ .map((entry) => entry.provider);
2884
+ const providerList = formatProviderList(availableProviders);
2885
+ const reason = unavailableReason ? ` Reason: ${unavailableReason}.` : "";
2886
+ throw new Error(`Provider '${options.provider}' is not available.${reason} Available providers: ${providerList}. Use one of those providers, or install/configure '${options.provider}'.`);
2887
+ }
2888
+ requireEnabledProvider(provider) {
2889
+ if (this.providerEnabled.get(provider) === false) {
2890
+ throw new Error(`Provider '${provider}' is disabled`);
2891
+ }
2892
+ }
2893
+ getConfiguredProviderIds() {
2894
+ return Array.from(new Set([...this.providerEnabled.keys(), ...this.clients.keys()]));
2895
+ }
2896
+ requireClient(provider) {
2897
+ const client = this.clients.get(provider);
2898
+ if (!client) {
2899
+ throw new Error(`No client registered for provider '${provider}'`);
2900
+ }
2901
+ return client;
2902
+ }
2903
+ async archiveNativeSessionBestEffort(provider, persistence) {
2904
+ if (!persistence)
2905
+ return;
2906
+ const client = this.clients.get(provider);
2907
+ if (!client?.archiveNativeSession)
2908
+ return;
2909
+ try {
2910
+ await client.archiveNativeSession(persistence);
2911
+ }
2912
+ catch (error) {
2913
+ this.logger.warn({ error, provider, sessionId: persistence.sessionId }, "Failed to archive native session (best-effort)");
2914
+ }
2915
+ }
2916
+ async unarchiveNativeSession(provider, persistence) {
2917
+ if (!persistence)
2918
+ return;
2919
+ const client = this.clients.get(provider);
2920
+ if (!client?.unarchiveNativeSession)
2921
+ return;
2922
+ await client.unarchiveNativeSession(persistence);
2923
+ }
2924
+ requireAgent(id) {
2925
+ const normalizedId = validateAgentId(id, "requireAgent");
2926
+ const agent = this.agents.get(normalizedId);
2927
+ if (!agent) {
2928
+ throw new Error(`Unknown agent '${normalizedId}'`);
2929
+ }
2930
+ return agent;
2931
+ }
2932
+ requireSessionAgent(id) {
2933
+ const agent = this.requireAgent(id);
2934
+ if (agent.session === null) {
2935
+ throw new Error(`Agent '${agent.id}' has no managed session`);
2936
+ }
2937
+ return agent;
2938
+ }
2939
+ }
2940
+ export function commandMayHaveChangedExternalState(command) {
2941
+ const normalized = command.toLowerCase();
2942
+ // Commands that operate on remote state and do NOT trigger local file
2943
+ // watchers. Local git mutations (commit, checkout, merge, rebase, reset,
2944
+ // pull) are already caught by watchers on .git/HEAD and refs/heads/.
2945
+ return (
2946
+ // GitHub PR operations (merge, close, create, edit, comment, review)
2947
+ /\bgh\s+pr\s+(merge|close|create|edit|comment|review)\b/.test(normalized) ||
2948
+ // Pushes to remote — local refs unchanged, but remote state (PR checks,
2949
+ // mergeable status) may shift immediately after.
2950
+ /\bgit\s+push\b/.test(normalized) ||
2951
+ // Fetches update refs/remotes/ which our watchers do not watch, so
2952
+ // ahead/behind counts can drift stale until the next refresh.
2953
+ /\bgit\s+fetch\b/.test(normalized));
2954
+ }
2955
+ //# sourceMappingURL=agent-manager.js.map