@getpaseo/server 0.1.62 → 0.1.63

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 (464) hide show
  1. package/README.md +4 -0
  2. package/dist/server/client/daemon-client-runtime-metrics.d.ts +6 -6
  3. package/dist/server/client/daemon-client-runtime-metrics.d.ts.map +1 -1
  4. package/dist/server/client/daemon-client-transport-types.d.ts +13 -13
  5. package/dist/server/client/daemon-client-transport-types.d.ts.map +1 -1
  6. package/dist/server/client/daemon-client-websocket-transport.d.ts +1 -1
  7. package/dist/server/client/daemon-client-websocket-transport.d.ts.map +1 -1
  8. package/dist/server/client/daemon-client-websocket-transport.js +5 -4
  9. package/dist/server/client/daemon-client-websocket-transport.js.map +1 -1
  10. package/dist/server/client/daemon-client.d.ts +59 -37
  11. package/dist/server/client/daemon-client.d.ts.map +1 -1
  12. package/dist/server/client/daemon-client.js +62 -17
  13. package/dist/server/client/daemon-client.js.map +1 -1
  14. package/dist/server/server/agent/agent-loading.d.ts.map +1 -1
  15. package/dist/server/server/agent/agent-loading.js +5 -3
  16. package/dist/server/server/agent/agent-loading.js.map +1 -1
  17. package/dist/server/server/agent/agent-manager.d.ts +45 -19
  18. package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
  19. package/dist/server/server/agent/agent-manager.js +393 -290
  20. package/dist/server/server/agent/agent-manager.js.map +1 -1
  21. package/dist/server/server/agent/agent-metadata-generator.d.ts +6 -6
  22. package/dist/server/server/agent/agent-metadata-generator.d.ts.map +1 -1
  23. package/dist/server/server/agent/agent-metadata-generator.js +46 -38
  24. package/dist/server/server/agent/agent-metadata-generator.js.map +1 -1
  25. package/dist/server/server/agent/agent-projections.d.ts +4 -6
  26. package/dist/server/server/agent/agent-projections.d.ts.map +1 -1
  27. package/dist/server/server/agent/agent-projections.js +59 -65
  28. package/dist/server/server/agent/agent-projections.js.map +1 -1
  29. package/dist/server/server/agent/agent-response-loop.d.ts +4 -4
  30. package/dist/server/server/agent/agent-response-loop.d.ts.map +1 -1
  31. package/dist/server/server/agent/agent-response-loop.js +58 -45
  32. package/dist/server/server/agent/agent-response-loop.js.map +1 -1
  33. package/dist/server/server/agent/agent-sdk-types.d.ts +43 -40
  34. package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
  35. package/dist/server/server/agent/agent-sdk-types.js.map +1 -1
  36. package/dist/server/server/agent/agent-storage.d.ts +2 -2
  37. package/dist/server/server/agent/agent-storage.d.ts.map +1 -1
  38. package/dist/server/server/agent/agent-storage.js +29 -36
  39. package/dist/server/server/agent/agent-storage.js.map +1 -1
  40. package/dist/server/server/agent/agent-stream-coalescer.d.ts +6 -6
  41. package/dist/server/server/agent/agent-stream-coalescer.d.ts.map +1 -1
  42. package/dist/server/server/agent/agent-timeline-store-types.d.ts +10 -10
  43. package/dist/server/server/agent/agent-timeline-store-types.d.ts.map +1 -1
  44. package/dist/server/server/agent/agent-timeline-store.d.ts +2 -2
  45. package/dist/server/server/agent/agent-timeline-store.d.ts.map +1 -1
  46. package/dist/server/server/agent/agent-timeline-store.js +85 -64
  47. package/dist/server/server/agent/agent-timeline-store.js.map +1 -1
  48. package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
  49. package/dist/server/server/agent/mcp-server.js +185 -148
  50. package/dist/server/server/agent/mcp-server.js.map +1 -1
  51. package/dist/server/server/agent/mcp-shared.d.ts +9 -2
  52. package/dist/server/server/agent/mcp-shared.d.ts.map +1 -1
  53. package/dist/server/server/agent/mcp-shared.js +2 -0
  54. package/dist/server/server/agent/mcp-shared.js.map +1 -1
  55. package/dist/server/server/agent/model-resolver.d.ts +2 -2
  56. package/dist/server/server/agent/model-resolver.d.ts.map +1 -1
  57. package/dist/server/server/agent/model-resolver.js +9 -5
  58. package/dist/server/server/agent/model-resolver.js.map +1 -1
  59. package/dist/server/server/agent/provider-launch-config.d.ts +28 -17
  60. package/dist/server/server/agent/provider-launch-config.d.ts.map +1 -1
  61. package/dist/server/server/agent/provider-launch-config.js +20 -9
  62. package/dist/server/server/agent/provider-launch-config.js.map +1 -1
  63. package/dist/server/server/agent/provider-registry.d.ts +4 -2
  64. package/dist/server/server/agent/provider-registry.d.ts.map +1 -1
  65. package/dist/server/server/agent/provider-registry.js +24 -21
  66. package/dist/server/server/agent/provider-registry.js.map +1 -1
  67. package/dist/server/server/agent/provider-snapshot-manager.d.ts +6 -5
  68. package/dist/server/server/agent/provider-snapshot-manager.d.ts.map +1 -1
  69. package/dist/server/server/agent/provider-snapshot-manager.js +40 -31
  70. package/dist/server/server/agent/provider-snapshot-manager.js.map +1 -1
  71. package/dist/server/server/agent/providers/acp-agent.d.ts +11 -12
  72. package/dist/server/server/agent/providers/acp-agent.d.ts.map +1 -1
  73. package/dist/server/server/agent/providers/acp-agent.js +148 -122
  74. package/dist/server/server/agent/providers/acp-agent.js.map +1 -1
  75. package/dist/server/server/agent/providers/claude/sidechain-tracker.d.ts +2 -0
  76. package/dist/server/server/agent/providers/claude/sidechain-tracker.d.ts.map +1 -1
  77. package/dist/server/server/agent/providers/claude/sidechain-tracker.js +47 -45
  78. package/dist/server/server/agent/providers/claude/sidechain-tracker.js.map +1 -1
  79. package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +2 -2
  80. package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts.map +1 -1
  81. package/dist/server/server/agent/providers/claude/task-notification-tool-call.js +10 -5
  82. package/dist/server/server/agent/providers/claude/task-notification-tool-call.js.map +1 -1
  83. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts.map +1 -1
  84. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js +11 -2
  85. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js.map +1 -1
  86. package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts +2 -2
  87. package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts.map +1 -1
  88. package/dist/server/server/agent/providers/claude/tool-call-mapper.js +20 -13
  89. package/dist/server/server/agent/providers/claude/tool-call-mapper.js.map +1 -1
  90. package/dist/server/server/agent/providers/claude-agent.d.ts +20 -8
  91. package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
  92. package/dist/server/server/agent/providers/claude-agent.js +610 -460
  93. package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
  94. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.d.ts +2 -2
  95. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.d.ts.map +1 -1
  96. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts +2 -2
  97. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts.map +1 -1
  98. package/dist/server/server/agent/providers/codex/tool-call-mapper.js +49 -44
  99. package/dist/server/server/agent/providers/codex/tool-call-mapper.js.map +1 -1
  100. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +27 -8
  101. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
  102. package/dist/server/server/agent/providers/codex-app-server-agent.js +564 -492
  103. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
  104. package/dist/server/server/agent/providers/codex-rollout-timeline.d.ts +2 -2
  105. package/dist/server/server/agent/providers/codex-rollout-timeline.d.ts.map +1 -1
  106. package/dist/server/server/agent/providers/codex-rollout-timeline.js +58 -47
  107. package/dist/server/server/agent/providers/codex-rollout-timeline.js.map +1 -1
  108. package/dist/server/server/agent/providers/copilot-acp-agent.d.ts +2 -2
  109. package/dist/server/server/agent/providers/copilot-acp-agent.d.ts.map +1 -1
  110. package/dist/server/server/agent/providers/diagnostic-utils.d.ts +3 -3
  111. package/dist/server/server/agent/providers/diagnostic-utils.d.ts.map +1 -1
  112. package/dist/server/server/agent/providers/diagnostic-utils.js +82 -9
  113. package/dist/server/server/agent/providers/diagnostic-utils.js.map +1 -1
  114. package/dist/server/server/agent/providers/generic-acp-agent.d.ts +2 -2
  115. package/dist/server/server/agent/providers/generic-acp-agent.d.ts.map +1 -1
  116. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts.map +1 -1
  117. package/dist/server/server/agent/providers/mock-load-test-agent.js.map +1 -1
  118. package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts +2 -2
  119. package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts.map +1 -1
  120. package/dist/server/server/agent/providers/opencode-agent.d.ts +2 -2
  121. package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
  122. package/dist/server/server/agent/providers/opencode-agent.js +385 -360
  123. package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
  124. package/dist/server/server/agent/providers/pi-direct-agent.d.ts +1 -0
  125. package/dist/server/server/agent/providers/pi-direct-agent.d.ts.map +1 -1
  126. package/dist/server/server/agent/providers/pi-direct-agent.js +109 -140
  127. package/dist/server/server/agent/providers/pi-direct-agent.js.map +1 -1
  128. package/dist/server/server/agent/providers/test-utils/session-stream-adapter.d.ts.map +1 -1
  129. package/dist/server/server/agent/providers/test-utils/session-stream-adapter.js +3 -1
  130. package/dist/server/server/agent/providers/test-utils/session-stream-adapter.js.map +1 -1
  131. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +3 -3
  132. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
  133. package/dist/server/server/agent/providers/tool-call-detail-primitives.js +102 -73
  134. package/dist/server/server/agent/providers/tool-call-detail-primitives.js.map +1 -1
  135. package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts +2 -2
  136. package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts.map +1 -1
  137. package/dist/server/server/agent/stt-manager.d.ts.map +1 -1
  138. package/dist/server/server/agent/stt-manager.js +63 -53
  139. package/dist/server/server/agent/stt-manager.js.map +1 -1
  140. package/dist/server/server/agent/timeline-projection.d.ts +6 -6
  141. package/dist/server/server/agent/timeline-projection.d.ts.map +1 -1
  142. package/dist/server/server/agent/timeline-projection.js +11 -6
  143. package/dist/server/server/agent/timeline-projection.js.map +1 -1
  144. package/dist/server/server/agent/tts-manager.d.ts.map +1 -1
  145. package/dist/server/server/agent/tts-manager.js +1 -0
  146. package/dist/server/server/agent/tts-manager.js.map +1 -1
  147. package/dist/server/server/agent-attention-policy.d.ts +2 -2
  148. package/dist/server/server/agent-attention-policy.d.ts.map +1 -1
  149. package/dist/server/server/bootstrap.d.ts +4 -4
  150. package/dist/server/server/bootstrap.d.ts.map +1 -1
  151. package/dist/server/server/bootstrap.js +493 -485
  152. package/dist/server/server/bootstrap.js.map +1 -1
  153. package/dist/server/server/chat/chat-service.d.ts +1 -1
  154. package/dist/server/server/chat/chat-service.d.ts.map +1 -1
  155. package/dist/server/server/chat/chat-service.js +3 -3
  156. package/dist/server/server/chat/chat-service.js.map +1 -1
  157. package/dist/server/server/checkout-diff-manager.d.ts +2 -2
  158. package/dist/server/server/checkout-diff-manager.d.ts.map +1 -1
  159. package/dist/server/server/checkout-git-utils.d.ts +5 -3
  160. package/dist/server/server/checkout-git-utils.d.ts.map +1 -1
  161. package/dist/server/server/checkout-git-utils.js +1 -2
  162. package/dist/server/server/checkout-git-utils.js.map +1 -1
  163. package/dist/server/server/config.d.ts.map +1 -1
  164. package/dist/server/server/config.js +68 -39
  165. package/dist/server/server/config.js.map +1 -1
  166. package/dist/server/server/connection-offer.d.ts +2 -2
  167. package/dist/server/server/connection-offer.d.ts.map +1 -1
  168. package/dist/server/server/daemon-config-store.d.ts +5 -3
  169. package/dist/server/server/daemon-config-store.d.ts.map +1 -1
  170. package/dist/server/server/daemon-config-store.js +26 -0
  171. package/dist/server/server/daemon-config-store.js.map +1 -1
  172. package/dist/server/server/daemon-keypair.d.ts +2 -2
  173. package/dist/server/server/daemon-keypair.d.ts.map +1 -1
  174. package/dist/server/server/editor-targets.d.ts +4 -4
  175. package/dist/server/server/editor-targets.d.ts.map +1 -1
  176. package/dist/server/server/editor-targets.js +11 -15
  177. package/dist/server/server/editor-targets.js.map +1 -1
  178. package/dist/server/server/exports.d.ts +3 -3
  179. package/dist/server/server/exports.d.ts.map +1 -1
  180. package/dist/server/server/exports.js +1 -3
  181. package/dist/server/server/exports.js.map +1 -1
  182. package/dist/server/server/file-download/token-store.d.ts +4 -4
  183. package/dist/server/server/file-download/token-store.d.ts.map +1 -1
  184. package/dist/server/server/index.js +16 -12
  185. package/dist/server/server/index.js.map +1 -1
  186. package/dist/server/server/logger.d.ts +4 -4
  187. package/dist/server/server/logger.d.ts.map +1 -1
  188. package/dist/server/server/logger.js +26 -20
  189. package/dist/server/server/logger.js.map +1 -1
  190. package/dist/server/server/loop/rpc-schemas.d.ts +52 -52
  191. package/dist/server/server/loop-service.d.ts +13 -12
  192. package/dist/server/server/loop-service.d.ts.map +1 -1
  193. package/dist/server/server/loop-service.js +22 -18
  194. package/dist/server/server/loop-service.js.map +1 -1
  195. package/dist/server/server/package-version.d.ts +2 -2
  196. package/dist/server/server/package-version.d.ts.map +1 -1
  197. package/dist/server/server/package-version.js +19 -17
  198. package/dist/server/server/package-version.js.map +1 -1
  199. package/dist/server/server/pairing-offer.d.ts +2 -2
  200. package/dist/server/server/pairing-offer.d.ts.map +1 -1
  201. package/dist/server/server/paseo-env.d.ts +9 -0
  202. package/dist/server/server/paseo-env.d.ts.map +1 -0
  203. package/dist/server/server/paseo-env.js +70 -0
  204. package/dist/server/server/paseo-env.js.map +1 -0
  205. package/dist/server/server/paseo-worktree-archive-service.d.ts +4 -4
  206. package/dist/server/server/paseo-worktree-archive-service.d.ts.map +1 -1
  207. package/dist/server/server/paseo-worktree-archive-service.js +11 -11
  208. package/dist/server/server/paseo-worktree-archive-service.js.map +1 -1
  209. package/dist/server/server/persisted-config.d.ts +62 -62
  210. package/dist/server/server/persisted-config.d.ts.map +1 -1
  211. package/dist/server/server/persisted-config.js +4 -4
  212. package/dist/server/server/persisted-config.js.map +1 -1
  213. package/dist/server/server/persistence-hooks.d.ts +8 -9
  214. package/dist/server/server/persistence-hooks.d.ts.map +1 -1
  215. package/dist/server/server/persistence-hooks.js +4 -12
  216. package/dist/server/server/persistence-hooks.js.map +1 -1
  217. package/dist/server/server/pid-lock.js.map +1 -1
  218. package/dist/server/server/push/push-service.d.ts.map +1 -1
  219. package/dist/server/server/push/push-service.js +1 -3
  220. package/dist/server/server/push/push-service.js.map +1 -1
  221. package/dist/server/server/relay-transport.d.ts +8 -8
  222. package/dist/server/server/relay-transport.d.ts.map +1 -1
  223. package/dist/server/server/relay-transport.js +27 -16
  224. package/dist/server/server/relay-transport.js.map +1 -1
  225. package/dist/server/server/schedule/service.d.ts.map +1 -1
  226. package/dist/server/server/schedule/service.js +2 -2
  227. package/dist/server/server/schedule/service.js.map +1 -1
  228. package/dist/server/server/script-health-monitor.d.ts.map +1 -1
  229. package/dist/server/server/script-health-monitor.js +7 -6
  230. package/dist/server/server/script-health-monitor.js.map +1 -1
  231. package/dist/server/server/script-proxy.js +1 -1
  232. package/dist/server/server/script-proxy.js.map +1 -1
  233. package/dist/server/server/script-status-projection.d.ts +4 -4
  234. package/dist/server/server/script-status-projection.d.ts.map +1 -1
  235. package/dist/server/server/script-status-projection.js +54 -44
  236. package/dist/server/server/script-status-projection.js.map +1 -1
  237. package/dist/server/server/server-id.d.ts +4 -4
  238. package/dist/server/server/server-id.d.ts.map +1 -1
  239. package/dist/server/server/session.d.ts +45 -14
  240. package/dist/server/server/session.d.ts.map +1 -1
  241. package/dist/server/server/session.js +1098 -761
  242. package/dist/server/server/session.js.map +1 -1
  243. package/dist/server/server/speech/audio.js +1 -1
  244. package/dist/server/server/speech/audio.js.map +1 -1
  245. package/dist/server/server/speech/providers/local/config.d.ts +6 -6
  246. package/dist/server/server/speech/providers/local/config.d.ts.map +1 -1
  247. package/dist/server/server/speech/providers/local/config.js +41 -16
  248. package/dist/server/server/speech/providers/local/config.js.map +1 -1
  249. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.d.ts +2 -2
  250. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.d.ts.map +1 -1
  251. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.js +42 -19
  252. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.js.map +1 -1
  253. package/dist/server/server/speech/providers/local/runtime.d.ts +4 -4
  254. package/dist/server/server/speech/providers/local/runtime.d.ts.map +1 -1
  255. package/dist/server/server/speech/providers/local/runtime.js +108 -77
  256. package/dist/server/server/speech/providers/local/runtime.js.map +1 -1
  257. package/dist/server/server/speech/providers/local/sherpa/model-catalog.d.ts +2 -2
  258. package/dist/server/server/speech/providers/local/sherpa/model-catalog.d.ts.map +1 -1
  259. package/dist/server/server/speech/providers/local/sherpa/model-catalog.js +1 -4
  260. package/dist/server/server/speech/providers/local/sherpa/model-catalog.js.map +1 -1
  261. package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts +2 -2
  262. package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts.map +1 -1
  263. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js +19 -19
  264. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js.map +1 -1
  265. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.d.ts +28 -7
  266. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.d.ts.map +1 -1
  267. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js.map +1 -1
  268. package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.d.ts +23 -4
  269. package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.d.ts.map +1 -1
  270. package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.js +35 -28
  271. package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.js.map +1 -1
  272. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-loader.d.ts +5 -5
  273. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-loader.d.ts.map +1 -1
  274. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.d.ts +7 -7
  275. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.d.ts.map +1 -1
  276. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.js +5 -0
  277. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.js.map +1 -1
  278. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.d.ts.map +1 -1
  279. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js +3 -1
  280. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js.map +1 -1
  281. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.d.ts +2 -2
  282. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.d.ts.map +1 -1
  283. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js +3 -1
  284. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js.map +1 -1
  285. package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.d.ts.map +1 -1
  286. package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.js +10 -4
  287. package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.js.map +1 -1
  288. package/dist/server/server/speech/providers/local/sherpa/sherpa-runtime-env.d.ts +2 -2
  289. package/dist/server/server/speech/providers/local/sherpa/sherpa-runtime-env.d.ts.map +1 -1
  290. package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.d.ts +2 -2
  291. package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.d.ts.map +1 -1
  292. package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.js +4 -1
  293. package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.js.map +1 -1
  294. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.d.ts +2 -2
  295. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.d.ts.map +1 -1
  296. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.js +18 -11
  297. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.js.map +1 -1
  298. package/dist/server/server/speech/providers/openai/config.d.ts +2 -2
  299. package/dist/server/server/speech/providers/openai/config.d.ts.map +1 -1
  300. package/dist/server/server/speech/providers/openai/config.js +58 -31
  301. package/dist/server/server/speech/providers/openai/config.js.map +1 -1
  302. package/dist/server/server/speech/providers/openai/realtime-transcription-session.d.ts.map +1 -1
  303. package/dist/server/server/speech/providers/openai/realtime-transcription-session.js +2 -2
  304. package/dist/server/server/speech/providers/openai/realtime-transcription-session.js.map +1 -1
  305. package/dist/server/server/speech/providers/openai/runtime.d.ts +4 -4
  306. package/dist/server/server/speech/providers/openai/runtime.d.ts.map +1 -1
  307. package/dist/server/server/speech/providers/openai/runtime.js +37 -32
  308. package/dist/server/server/speech/providers/openai/runtime.js.map +1 -1
  309. package/dist/server/server/speech/providers/openai/stt.d.ts.map +1 -1
  310. package/dist/server/server/speech/providers/openai/stt.js +4 -3
  311. package/dist/server/server/speech/providers/openai/stt.js.map +1 -1
  312. package/dist/server/server/speech/providers/openai/tts.d.ts.map +1 -1
  313. package/dist/server/server/speech/providers/openai/tts.js +3 -2
  314. package/dist/server/server/speech/providers/openai/tts.js.map +1 -1
  315. package/dist/server/server/speech/speech-config-resolver.d.ts.map +1 -1
  316. package/dist/server/server/speech/speech-config-resolver.js +46 -17
  317. package/dist/server/server/speech/speech-config-resolver.js.map +1 -1
  318. package/dist/server/server/speech/speech-provider.d.ts +2 -2
  319. package/dist/server/server/speech/speech-provider.d.ts.map +1 -1
  320. package/dist/server/server/speech/speech-runtime.d.ts +6 -6
  321. package/dist/server/server/speech/speech-runtime.d.ts.map +1 -1
  322. package/dist/server/server/speech/speech-runtime.js +17 -17
  323. package/dist/server/server/speech/speech-runtime.js.map +1 -1
  324. package/dist/server/server/speech/speech-types.d.ts +2 -2
  325. package/dist/server/server/speech/speech-types.d.ts.map +1 -1
  326. package/dist/server/server/speech/turn-detection-provider.d.ts +2 -2
  327. package/dist/server/server/speech/turn-detection-provider.d.ts.map +1 -1
  328. package/dist/server/server/utils/diff-highlighter.d.ts +0 -3
  329. package/dist/server/server/utils/diff-highlighter.d.ts.map +1 -1
  330. package/dist/server/server/utils/diff-highlighter.js +67 -66
  331. package/dist/server/server/utils/diff-highlighter.js.map +1 -1
  332. package/dist/server/server/voice/voice-turn-controller.d.ts.map +1 -1
  333. package/dist/server/server/voice/voice-turn-controller.js +1 -0
  334. package/dist/server/server/voice/voice-turn-controller.js.map +1 -1
  335. package/dist/server/server/voice-types.d.ts +2 -2
  336. package/dist/server/server/voice-types.d.ts.map +1 -1
  337. package/dist/server/server/websocket-server.d.ts +31 -21
  338. package/dist/server/server/websocket-server.d.ts.map +1 -1
  339. package/dist/server/server/websocket-server.js +299 -197
  340. package/dist/server/server/websocket-server.js.map +1 -1
  341. package/dist/server/server/workspace-git-metadata.d.ts +2 -2
  342. package/dist/server/server/workspace-git-metadata.d.ts.map +1 -1
  343. package/dist/server/server/workspace-git-metadata.js +2 -32
  344. package/dist/server/server/workspace-git-metadata.js.map +1 -1
  345. package/dist/server/server/workspace-git-service.d.ts +8 -4
  346. package/dist/server/server/workspace-git-service.d.ts.map +1 -1
  347. package/dist/server/server/workspace-git-service.js +163 -115
  348. package/dist/server/server/workspace-git-service.js.map +1 -1
  349. package/dist/server/server/workspace-reconciliation-service.d.ts +5 -4
  350. package/dist/server/server/workspace-reconciliation-service.d.ts.map +1 -1
  351. package/dist/server/server/workspace-reconciliation-service.js +82 -82
  352. package/dist/server/server/workspace-reconciliation-service.js.map +1 -1
  353. package/dist/server/server/workspace-registry-bootstrap.d.ts.map +1 -1
  354. package/dist/server/server/workspace-registry-bootstrap.js +40 -33
  355. package/dist/server/server/workspace-registry-bootstrap.js.map +1 -1
  356. package/dist/server/server/workspace-registry-model.d.ts +19 -6
  357. package/dist/server/server/workspace-registry-model.d.ts.map +1 -1
  358. package/dist/server/server/workspace-registry-model.js +35 -21
  359. package/dist/server/server/workspace-registry-model.js.map +1 -1
  360. package/dist/server/server/workspace-registry.d.ts +2 -2
  361. package/dist/server/server/workspace-script-runtime-store.d.ts +2 -2
  362. package/dist/server/server/workspace-script-runtime-store.d.ts.map +1 -1
  363. package/dist/server/server/workspace-service-env.js +3 -3
  364. package/dist/server/server/workspace-service-env.js.map +1 -1
  365. package/dist/server/server/worktree-bootstrap.d.ts +4 -4
  366. package/dist/server/server/worktree-bootstrap.d.ts.map +1 -1
  367. package/dist/server/server/worktree-bootstrap.js +95 -67
  368. package/dist/server/server/worktree-bootstrap.js.map +1 -1
  369. package/dist/server/server/worktree-session.d.ts +8 -8
  370. package/dist/server/server/worktree-session.d.ts.map +1 -1
  371. package/dist/server/server/worktree-session.js +27 -19
  372. package/dist/server/server/worktree-session.js.map +1 -1
  373. package/dist/server/services/github-service.d.ts +1 -7
  374. package/dist/server/services/github-service.d.ts.map +1 -1
  375. package/dist/server/services/github-service.js +123 -143
  376. package/dist/server/services/github-service.js.map +1 -1
  377. package/dist/server/shared/agent-attention-notification.d.ts +9 -8
  378. package/dist/server/shared/agent-attention-notification.d.ts.map +1 -1
  379. package/dist/server/shared/agent-attention-notification.js +27 -17
  380. package/dist/server/shared/agent-attention-notification.js.map +1 -1
  381. package/dist/server/shared/daemon-endpoints.d.ts +2 -2
  382. package/dist/server/shared/daemon-endpoints.d.ts.map +1 -1
  383. package/dist/server/shared/daemon-endpoints.js +17 -2
  384. package/dist/server/shared/daemon-endpoints.js.map +1 -1
  385. package/dist/server/shared/messages.d.ts +21962 -3049
  386. package/dist/server/shared/messages.d.ts.map +1 -1
  387. package/dist/server/shared/messages.js +79 -2
  388. package/dist/server/shared/messages.js.map +1 -1
  389. package/dist/server/shared/terminal-stream-protocol.d.ts +2 -2
  390. package/dist/server/shared/terminal-stream-protocol.d.ts.map +1 -1
  391. package/dist/server/shared/tool-call-display.d.ts +2 -2
  392. package/dist/server/shared/tool-call-display.d.ts.map +1 -1
  393. package/dist/server/terminal/terminal-manager.d.ts.map +1 -1
  394. package/dist/server/terminal/terminal-manager.js +1 -3
  395. package/dist/server/terminal/terminal-manager.js.map +1 -1
  396. package/dist/server/terminal/terminal-output-coalescer.d.ts +6 -6
  397. package/dist/server/terminal/terminal-output-coalescer.d.ts.map +1 -1
  398. package/dist/server/terminal/terminal.d.ts +3 -2
  399. package/dist/server/terminal/terminal.d.ts.map +1 -1
  400. package/dist/server/terminal/terminal.js +57 -19
  401. package/dist/server/terminal/terminal.js.map +1 -1
  402. package/dist/server/utils/checkout-git.d.ts +13 -12
  403. package/dist/server/utils/checkout-git.d.ts.map +1 -1
  404. package/dist/server/utils/checkout-git.js +351 -281
  405. package/dist/server/utils/checkout-git.js.map +1 -1
  406. package/dist/server/utils/directory-suggestions.js +12 -33
  407. package/dist/server/utils/directory-suggestions.js.map +1 -1
  408. package/dist/server/utils/executable.d.ts +1 -14
  409. package/dist/server/utils/executable.d.ts.map +1 -1
  410. package/dist/server/utils/executable.js +13 -49
  411. package/dist/server/utils/executable.js.map +1 -1
  412. package/dist/server/utils/github-remote.d.ts +13 -0
  413. package/dist/server/utils/github-remote.d.ts.map +1 -0
  414. package/dist/server/utils/github-remote.js +128 -0
  415. package/dist/server/utils/github-remote.js.map +1 -0
  416. package/dist/server/utils/paseo-config-file.d.ts +30 -0
  417. package/dist/server/utils/paseo-config-file.d.ts.map +1 -0
  418. package/dist/server/utils/paseo-config-file.js +90 -0
  419. package/dist/server/utils/paseo-config-file.js.map +1 -0
  420. package/dist/server/utils/paseo-config-schema.d.ts +290 -0
  421. package/dist/server/utils/paseo-config-schema.d.ts.map +1 -0
  422. package/dist/server/utils/paseo-config-schema.js +60 -0
  423. package/dist/server/utils/paseo-config-schema.js.map +1 -0
  424. package/dist/server/utils/project-icon.d.ts.map +1 -1
  425. package/dist/server/utils/project-icon.js +84 -109
  426. package/dist/server/utils/project-icon.js.map +1 -1
  427. package/dist/server/utils/promise-timeout.d.ts +2 -2
  428. package/dist/server/utils/promise-timeout.d.ts.map +1 -1
  429. package/dist/server/utils/run-git-command.d.ts +3 -1
  430. package/dist/server/utils/run-git-command.d.ts.map +1 -1
  431. package/dist/server/utils/run-git-command.js +10 -1
  432. package/dist/server/utils/run-git-command.js.map +1 -1
  433. package/dist/server/utils/script-hostname.d.ts +2 -2
  434. package/dist/server/utils/script-hostname.d.ts.map +1 -1
  435. package/dist/server/utils/spawn.d.ts +10 -3
  436. package/dist/server/utils/spawn.d.ts.map +1 -1
  437. package/dist/server/utils/spawn.js +30 -5
  438. package/dist/server/utils/spawn.js.map +1 -1
  439. package/dist/server/utils/windows-command.d.ts +15 -0
  440. package/dist/server/utils/windows-command.d.ts.map +1 -0
  441. package/dist/server/utils/windows-command.js +37 -0
  442. package/dist/server/utils/windows-command.js.map +1 -0
  443. package/dist/server/utils/worktree.d.ts +10 -7
  444. package/dist/server/utils/worktree.d.ts.map +1 -1
  445. package/dist/server/utils/worktree.js +64 -55
  446. package/dist/server/utils/worktree.js.map +1 -1
  447. package/dist/src/server/pid-lock.js.map +1 -1
  448. package/package.json +15 -20
  449. package/dist/server/server/agent/llm-openai.d.ts +0 -7
  450. package/dist/server/server/agent/llm-openai.d.ts.map +0 -1
  451. package/dist/server/server/agent/llm-openai.js +0 -8
  452. package/dist/server/server/agent/llm-openai.js.map +0 -1
  453. package/dist/server/server/agent/orchestrator.d.ts +0 -12
  454. package/dist/server/server/agent/orchestrator.d.ts.map +0 -1
  455. package/dist/server/server/agent/orchestrator.js +0 -12
  456. package/dist/server/server/agent/orchestrator.js.map +0 -1
  457. package/dist/server/server/types.d.ts +0 -5
  458. package/dist/server/server/types.d.ts.map +0 -1
  459. package/dist/server/server/types.js +0 -3
  460. package/dist/server/server/types.js.map +0 -1
  461. package/dist/server/server/workspace-registry.test-helpers.d.ts +0 -37
  462. package/dist/server/server/workspace-registry.test-helpers.d.ts.map +0 -1
  463. package/dist/server/server/workspace-registry.test-helpers.js +0 -121
  464. package/dist/server/server/workspace-registry.test-helpers.js.map +0 -1
@@ -2,7 +2,8 @@ import equal from "fast-deep-equal";
2
2
  import { v4 as uuidv4 } from "uuid";
3
3
  import { TTLCache } from "@isaacs/ttlcache";
4
4
  import pMemoize from "p-memoize";
5
- import { resolve, sep } from "path";
5
+ import { realpathSync } from "node:fs";
6
+ import { basename, resolve, sep } from "path";
6
7
  import { homedir } from "node:os";
7
8
  import { z } from "zod";
8
9
  import { isLegacyEditorTargetId, serializeAgentStreamEvent, } from "./messages.js";
@@ -16,7 +17,7 @@ import { isPaseoDictationDebugEnabled } from "./agent/recordings-debug.js";
16
17
  import { listAvailableEditorTargets, openInEditorTarget } from "./editor-targets.js";
17
18
  import { DictationStreamManager, } from "./dictation/dictation-stream-manager.js";
18
19
  import { createVoiceTurnController, } from "./voice/voice-turn-controller.js";
19
- import { buildConfigOverrides, extractTimestamps, toAgentPersistenceHandle, } from "./persistence-hooks.js";
20
+ import { buildConfigOverrides, extractTimestamps, isStoredAgentProviderAvailable, toAgentPersistenceHandle, } from "./persistence-hooks.js";
20
21
  import { ensureAgentLoaded } from "./agent/agent-loading.js";
21
22
  import { sendPromptToAgent, unarchiveAgentState } from "./agent/mcp-shared.js";
22
23
  import { experimental_createMCPClient } from "ai";
@@ -24,6 +25,7 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
24
25
  import { buildWorkspaceScriptPayloads } from "./script-status-projection.js";
25
26
  import { deriveProjectSlug } from "./workspace-git-metadata.js";
26
27
  import { spawnWorkspaceScript } from "./worktree-bootstrap.js";
28
+ import { applyMutableProviderConfigToOverrides } from "./daemon-config-store.js";
27
29
  import { buildProviderRegistry } from "./agent/provider-registry.js";
28
30
  import { resolveSnapshotCwd } from "./agent/provider-snapshot-manager.js";
29
31
  import { scheduleAgentMetadataGeneration } from "./agent/agent-metadata-generator.js";
@@ -32,11 +34,12 @@ import { MAX_EXPLICIT_AGENT_TITLE_CHARS } from "./agent/agent-title-limits.js";
32
34
  import { appendTimelineItemIfAgentKnown, emitLiveTimelineItemIfAgentKnown, } from "./agent/timeline-append.js";
33
35
  import { projectTimelineRows, selectTimelineWindowByProjectedLimit, } from "./agent/timeline-projection.js";
34
36
  import { DEFAULT_STRUCTURED_GENERATION_PROVIDERS, StructuredAgentFallbackError, StructuredAgentResponseError, generateStructuredAgentResponseWithFallback, } from "./agent/agent-response-loop.js";
35
- import { checkoutLiteFromGitSnapshot, normalizeWorkspaceId as normalizePersistedWorkspaceId, deriveProjectGroupingName, deriveWorkspaceId, deriveProjectRootPath, deriveProjectKind, deriveWorkspaceKind, deriveWorkspaceDisplayName, buildProjectPlacementForCwd as buildProjectPlacementForCwdStandalone, } from "./workspace-registry-model.js";
37
+ import { checkoutLiteFromGitSnapshot, normalizeWorkspaceId as normalizePersistedWorkspaceId, deriveProjectGroupingName, classifyDirectoryForProjectMembership, deriveWorkspaceDisplayName, } from "./workspace-registry-model.js";
36
38
  import { createPersistedProjectRecord, createPersistedWorkspaceRecord, } from "./workspace-registry.js";
37
39
  import { buildVoiceModeSystemPrompt, stripVoiceModeSystemPrompt, wrapSpokenInput, } from "./voice-config.js";
38
40
  import { isVoicePermissionAllowed } from "./voice-permission-policy.js";
39
41
  import { listDirectoryEntries, readExplorerFile, getDownloadableFileInfo, } from "./file-explorer/service.js";
42
+ import { readPaseoConfigForEdit, writePaseoConfigForEdit, } from "../utils/paseo-config-file.js";
40
43
  import { runAsyncWorktreeBootstrap } from "./worktree-bootstrap.js";
41
44
  import { archivePersistedWorkspaceRecord } from "./workspace-archive-service.js";
42
45
  import { WorkspaceReconciliationService } from "./workspace-reconciliation-service.js";
@@ -58,12 +61,97 @@ import { killTerminalsUnderPath as killWorktreeTerminalsUnderPath } from "./pase
58
61
  import { toWorktreeWireError } from "./worktree-errors.js";
59
62
  const MAX_INITIAL_AGENT_TITLE_CHARS = Math.min(60, MAX_EXPLICIT_AGENT_TITLE_CHARS);
60
63
  const WORKSPACE_GIT_WATCH_REMOVED_STATE_KEY = "__removed__";
64
+ async function resolveKnownProjectRootForConfig(input) {
65
+ const requestedRoot = canonicalizeConfigRoot(input.repoRoot);
66
+ const projects = await input.projectRegistry.list();
67
+ for (const project of projects) {
68
+ if (project.archivedAt !== null) {
69
+ continue;
70
+ }
71
+ const projectRoot = canonicalizeConfigRoot(project.rootPath);
72
+ if (requestedRoot === projectRoot) {
73
+ return projectRoot;
74
+ }
75
+ }
76
+ return null;
77
+ }
78
+ function canonicalizeConfigRoot(repoRoot) {
79
+ const resolved = resolve(repoRoot);
80
+ try {
81
+ return stripTrailingPathSeparators(realpathSync(resolved));
82
+ }
83
+ catch {
84
+ return stripTrailingPathSeparators(resolved);
85
+ }
86
+ }
87
+ function stripTrailingPathSeparators(path) {
88
+ let normalized = path;
89
+ while (normalized.length > 1 && normalized.endsWith(sep)) {
90
+ normalized = normalized.slice(0, -1);
91
+ }
92
+ return normalized;
93
+ }
61
94
  // TODO: Remove once all app store clients are on >=0.1.45 and understand arbitrary provider strings.
62
95
  // Clients before 0.1.45 validate providers with z.enum(["claude", "codex", "opencode"]) and reject
63
96
  // the entire session message if they encounter an unknown provider.
64
97
  const LEGACY_PROVIDER_IDS = new Set(["claude", "codex", "opencode"]);
65
98
  const MIN_VERSION_ALL_PROVIDERS = "0.1.45";
66
99
  const MIN_VERSION_FLEXIBLE_EDITOR_IDS = "0.1.50";
100
+ function errorToFriendlyMessage(error) {
101
+ if (error instanceof Error)
102
+ return error.message;
103
+ if (typeof error === "string")
104
+ return error;
105
+ return "Unknown error";
106
+ }
107
+ function resolveSubscriptionId(subscribe, requestedSubscriptionId) {
108
+ if (!subscribe)
109
+ return null;
110
+ if (requestedSubscriptionId && requestedSubscriptionId.length > 0) {
111
+ return requestedSubscriptionId;
112
+ }
113
+ return uuidv4();
114
+ }
115
+ function diffChangeTypeFor(file) {
116
+ if (file.isNew)
117
+ return "A";
118
+ if (file.isDeleted)
119
+ return "D";
120
+ return "M";
121
+ }
122
+ function buildWorkspaceCheckout(workspace, project) {
123
+ if (project.kind !== "git") {
124
+ return {
125
+ cwd: workspace.cwd,
126
+ isGit: false,
127
+ currentBranch: null,
128
+ remoteUrl: null,
129
+ worktreeRoot: null,
130
+ isPaseoOwnedWorktree: false,
131
+ mainRepoRoot: null,
132
+ };
133
+ }
134
+ if (workspace.kind === "worktree") {
135
+ return {
136
+ cwd: workspace.cwd,
137
+ isGit: true,
138
+ currentBranch: workspace.displayName,
139
+ remoteUrl: null,
140
+ worktreeRoot: workspace.cwd,
141
+ isPaseoOwnedWorktree: true,
142
+ mainRepoRoot: project.rootPath,
143
+ };
144
+ }
145
+ return {
146
+ cwd: workspace.cwd,
147
+ isGit: true,
148
+ currentBranch: workspace.displayName,
149
+ remoteUrl: null,
150
+ worktreeRoot: workspace.cwd,
151
+ isPaseoOwnedWorktree: false,
152
+ mainRepoRoot: null,
153
+ };
154
+ }
67
155
  function isAppVersionAtLeast(appVersion, minVersion) {
68
156
  if (!appVersion)
69
157
  return false;
@@ -119,6 +207,50 @@ export function resolveCreateAgentTitles(options) {
119
207
  provisionalTitle,
120
208
  };
121
209
  }
210
+ function parseFetchWorkspacesCursorSort(raw) {
211
+ const cursorSort = [];
212
+ for (const item of raw) {
213
+ if (!item ||
214
+ typeof item !== "object" ||
215
+ typeof item.key !== "string" ||
216
+ typeof item.direction !== "string") {
217
+ throw new SessionRequestError("invalid_cursor", "Invalid fetch_workspaces cursor");
218
+ }
219
+ const key = item.key;
220
+ const direction = item.direction;
221
+ if ((key !== "status_priority" &&
222
+ key !== "activity_at" &&
223
+ key !== "name" &&
224
+ key !== "project_id") ||
225
+ (direction !== "asc" && direction !== "desc")) {
226
+ throw new SessionRequestError("invalid_cursor", "Invalid fetch_workspaces cursor");
227
+ }
228
+ cursorSort.push({ key, direction });
229
+ }
230
+ return cursorSort;
231
+ }
232
+ function parseFetchAgentsCursorSort(raw) {
233
+ const cursorSort = [];
234
+ for (const item of raw) {
235
+ if (!item ||
236
+ typeof item !== "object" ||
237
+ typeof item.key !== "string" ||
238
+ typeof item.direction !== "string") {
239
+ throw new SessionRequestError("invalid_cursor", "Invalid fetch_agents cursor");
240
+ }
241
+ const key = item.key;
242
+ const direction = item.direction;
243
+ if ((key !== "status_priority" &&
244
+ key !== "created_at" &&
245
+ key !== "updated_at" &&
246
+ key !== "title") ||
247
+ (direction !== "asc" && direction !== "desc")) {
248
+ throw new SessionRequestError("invalid_cursor", "Invalid fetch_agents cursor");
249
+ }
250
+ cursorSort.push({ key, direction });
251
+ }
252
+ return cursorSort;
253
+ }
122
254
  export function resolveWaitForFinishError(options) {
123
255
  if (options.status !== "error") {
124
256
  return null;
@@ -292,53 +424,13 @@ export class Session {
292
424
  this.getDaemonTcpPort = getDaemonTcpPort ?? null;
293
425
  this.getDaemonTcpHost = getDaemonTcpHost ?? null;
294
426
  this.resolveScriptHealth = resolveScriptHealth ?? null;
295
- if (this.terminalManager) {
296
- this.unsubscribeTerminalsChanged = this.terminalManager.subscribeTerminalsChanged((event) => this.handleTerminalsChanged(event));
297
- }
298
- if (this.providerSnapshotManager) {
299
- const handleProviderSnapshotChange = (entries, cwd) => {
300
- // COMPAT(providersSnapshot): keep provider visibility gating for older clients.
301
- const visibleEntries = entries.filter((entry) => this.isProviderVisibleToClient(entry.provider));
302
- this.emit({
303
- type: "providers_snapshot_update",
304
- payload: {
305
- cwd,
306
- entries: visibleEntries,
307
- generatedAt: new Date().toISOString(),
308
- },
309
- });
310
- };
311
- this.providerSnapshotManager.on("change", handleProviderSnapshotChange);
312
- this.unsubscribeProviderSnapshotEvents = () => {
313
- this.providerSnapshotManager?.off("change", handleProviderSnapshotChange);
314
- };
315
- }
316
- this.resolveVoiceTurnDetection = toResolver(voice?.turnDetection ?? null);
317
- this.registerVoiceSpeakHandler = voiceBridge?.registerVoiceSpeakHandler;
318
- this.unregisterVoiceSpeakHandler = voiceBridge?.unregisterVoiceSpeakHandler;
319
- this.registerVoiceCallerContext = voiceBridge?.registerVoiceCallerContext;
320
- this.unregisterVoiceCallerContext = voiceBridge?.unregisterVoiceCallerContext;
321
- this.getSpeechReadiness = dictation?.getSpeechReadiness;
427
+ this.subscribeToOptionalManagers();
428
+ this.bindVoiceBridges({ voice, voiceBridge, dictation });
322
429
  this.agentProviderRuntimeSettings = agentProviderRuntimeSettings;
323
430
  this.providerOverrides = providerOverrides;
324
431
  this.isDev = isDev === true;
325
432
  this.abortController = new AbortController();
326
- this.providerRegistry = buildProviderRegistry(this.sessionLogger, {
327
- runtimeSettings: this.agentProviderRuntimeSettings,
328
- providerOverrides: this.providerOverrides,
329
- workspaceGitService: this.workspaceGitService,
330
- isDev: this.isDev,
331
- });
332
- // Initialize per-session managers
333
- this.ttsManager = new TTSManager(this.sessionId, this.sessionLogger, tts);
334
- this.sttManager = new STTManager(this.sessionId, this.sessionLogger, stt);
335
- this.dictationStreamManager = new DictationStreamManager({
336
- logger: this.sessionLogger,
337
- sessionId: this.sessionId,
338
- emit: (msg) => this.handleDictationManagerMessage(msg),
339
- stt: dictation?.stt ?? null,
340
- finalTimeoutMs: dictation?.finalTimeoutMs,
341
- });
433
+ this.initializePerSessionManagers({ tts, stt, dictation });
342
434
  // Initialize agent MCP client asynchronously
343
435
  void this.initializeAgentMcp();
344
436
  this.subscribeToAgentEvents();
@@ -427,16 +519,11 @@ export class Session {
427
519
  return;
428
520
  }
429
521
  this.sessionLogger.debug({ agentId, lifecycle: snapshot.lifecycle, hasInFlightRun }, "interruptAgentIfRunning: interrupting");
430
- try {
431
- const t0 = Date.now();
432
- const cancelled = await this.agentManager.cancelAgentRun(agentId);
433
- this.sessionLogger.debug({ agentId, cancelled, durationMs: Date.now() - t0 }, "interruptAgentIfRunning: cancelAgentRun completed");
434
- if (!cancelled) {
435
- this.sessionLogger.warn({ agentId }, "interruptAgentIfRunning: reported running but no active run was cancelled");
436
- }
437
- }
438
- catch (error) {
439
- throw error;
522
+ const t0 = Date.now();
523
+ const cancelled = await this.agentManager.cancelAgentRun(agentId);
524
+ this.sessionLogger.debug({ agentId, cancelled, durationMs: Date.now() - t0 }, "interruptAgentIfRunning: cancelAgentRun completed");
525
+ if (!cancelled) {
526
+ this.sessionLogger.warn({ agentId }, "interruptAgentIfRunning: reported running but no active run was cancelled");
440
527
  }
441
528
  }
442
529
  hasActiveAgentRun(agentId) {
@@ -464,12 +551,7 @@ export class Session {
464
551
  }
465
552
  catch (error) {
466
553
  this.handleAgentRunError(agentId, error, "Failed to start agent run");
467
- const message = error instanceof Error
468
- ? error.message
469
- : typeof error === "string"
470
- ? error
471
- : "Unknown error";
472
- return { ok: false, error: message };
554
+ return { ok: false, error: errorToFriendlyMessage(error) };
473
555
  }
474
556
  void (async () => {
475
557
  try {
@@ -486,7 +568,7 @@ export class Session {
486
568
  return { ok: true };
487
569
  }
488
570
  handleAgentRunError(agentId, error, context) {
489
- const message = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
571
+ const message = errorToFriendlyMessage(error);
490
572
  this.sessionLogger.error({ err: error, agentId, context }, `${context} for agent ${agentId}`);
491
573
  this.emit({
492
574
  type: "activity_log",
@@ -522,6 +604,50 @@ export class Session {
522
604
  /**
523
605
  * Subscribe to AgentManager events and forward them to the client
524
606
  */
607
+ subscribeToOptionalManagers() {
608
+ if (this.terminalManager) {
609
+ this.unsubscribeTerminalsChanged = this.terminalManager.subscribeTerminalsChanged((event) => this.handleTerminalsChanged(event));
610
+ }
611
+ if (this.providerSnapshotManager) {
612
+ const handleProviderSnapshotChange = (entries, cwd) => {
613
+ // COMPAT(providersSnapshot): keep provider visibility gating for older clients.
614
+ const visibleEntries = entries.filter((entry) => this.isProviderVisibleToClient(entry.provider));
615
+ this.emit({
616
+ type: "providers_snapshot_update",
617
+ payload: {
618
+ cwd,
619
+ entries: visibleEntries,
620
+ generatedAt: new Date().toISOString(),
621
+ },
622
+ });
623
+ };
624
+ this.providerSnapshotManager.on("change", handleProviderSnapshotChange);
625
+ this.unsubscribeProviderSnapshotEvents = () => {
626
+ this.providerSnapshotManager?.off("change", handleProviderSnapshotChange);
627
+ };
628
+ }
629
+ }
630
+ bindVoiceBridges(params) {
631
+ const { voice, voiceBridge, dictation } = params;
632
+ this.resolveVoiceTurnDetection = toResolver(voice?.turnDetection ?? null);
633
+ this.registerVoiceSpeakHandler = voiceBridge?.registerVoiceSpeakHandler;
634
+ this.unregisterVoiceSpeakHandler = voiceBridge?.unregisterVoiceSpeakHandler;
635
+ this.registerVoiceCallerContext = voiceBridge?.registerVoiceCallerContext;
636
+ this.unregisterVoiceCallerContext = voiceBridge?.unregisterVoiceCallerContext;
637
+ this.getSpeechReadiness = dictation?.getSpeechReadiness;
638
+ }
639
+ initializePerSessionManagers(params) {
640
+ const { tts, stt, dictation } = params;
641
+ this.ttsManager = new TTSManager(this.sessionId, this.sessionLogger, tts);
642
+ this.sttManager = new STTManager(this.sessionId, this.sessionLogger, stt);
643
+ this.dictationStreamManager = new DictationStreamManager({
644
+ logger: this.sessionLogger,
645
+ sessionId: this.sessionId,
646
+ emit: (msg) => this.handleDictationManagerMessage(msg),
647
+ stt: dictation?.stt ?? null,
648
+ finalTimeoutMs: dictation?.finalTimeoutMs,
649
+ });
650
+ }
525
651
  subscribeToAgentEvents() {
526
652
  if (this.unsubscribeAgentEvents) {
527
653
  this.unsubscribeAgentEvents();
@@ -618,8 +744,19 @@ export class Session {
618
744
  payload.archivedAt = storedRecord?.archivedAt ?? null;
619
745
  return payload;
620
746
  }
621
- buildStoredAgentPayload(record) {
622
- return buildStoredAgentPayload(record, this.providerRegistry, this.sessionLogger);
747
+ getProviderRegistry() {
748
+ return buildProviderRegistry(this.sessionLogger, {
749
+ runtimeSettings: this.agentProviderRuntimeSettings,
750
+ providerOverrides: applyMutableProviderConfigToOverrides(this.providerOverrides, this.daemonConfigStore.get().providers),
751
+ workspaceGitService: this.workspaceGitService,
752
+ isDev: this.isDev,
753
+ });
754
+ }
755
+ getRegisteredProviderIds() {
756
+ return Object.keys(this.getProviderRegistry());
757
+ }
758
+ buildStoredAgentPayload(record, registeredProviderIds = this.getRegisteredProviderIds()) {
759
+ return buildStoredAgentPayload(record, registeredProviderIds);
623
760
  }
624
761
  isProviderVisibleToClient(provider) {
625
762
  if (clientSupportsAllProviders(this.appVersion)) {
@@ -633,44 +770,34 @@ export class Session {
633
770
  }
634
771
  return editors.filter((editor) => isLegacyEditorTargetId(editor.id));
635
772
  }
636
- matchesAgentFilter(options) {
637
- const { agent, project, filter } = options;
638
- if (filter?.labels) {
639
- const matchesLabels = Object.entries(filter.labels).every(([key, value]) => agent.labels[key] === value);
640
- if (!matchesLabels) {
641
- return false;
642
- }
643
- }
644
- const includeArchived = filter?.includeArchived ?? false;
645
- if (!includeArchived && agent.archivedAt) {
646
- return false;
773
+ agentThinkingOptionMatchesFilter(agent, filter) {
774
+ if (filter.thinkingOptionId === undefined) {
775
+ return true;
647
776
  }
648
- if (filter?.thinkingOptionId !== undefined) {
649
- const expectedThinkingOptionId = resolveEffectiveThinkingOptionId({
650
- configuredThinkingOptionId: filter.thinkingOptionId ?? null,
777
+ const expectedThinkingOptionId = resolveEffectiveThinkingOptionId({
778
+ configuredThinkingOptionId: filter.thinkingOptionId ?? null,
779
+ });
780
+ const resolvedThinkingOptionId = agent.effectiveThinkingOptionId ??
781
+ resolveEffectiveThinkingOptionId({
782
+ runtimeInfo: agent.runtimeInfo,
783
+ configuredThinkingOptionId: agent.thinkingOptionId ?? null,
651
784
  });
652
- const resolvedThinkingOptionId = agent.effectiveThinkingOptionId ??
653
- resolveEffectiveThinkingOptionId({
654
- runtimeInfo: agent.runtimeInfo,
655
- configuredThinkingOptionId: agent.thinkingOptionId ?? null,
656
- });
657
- if (resolvedThinkingOptionId !== expectedThinkingOptionId) {
658
- return false;
659
- }
660
- }
661
- if (filter?.statuses && filter.statuses.length > 0) {
785
+ return resolvedThinkingOptionId === expectedThinkingOptionId;
786
+ }
787
+ matchesAgentStructuralFilter(agent, project, filter) {
788
+ if (filter.statuses && filter.statuses.length > 0) {
662
789
  const statuses = new Set(filter.statuses);
663
790
  if (!statuses.has(agent.status)) {
664
791
  return false;
665
792
  }
666
793
  }
667
- if (typeof filter?.requiresAttention === "boolean") {
794
+ if (typeof filter.requiresAttention === "boolean") {
668
795
  const requiresAttention = agent.requiresAttention ?? false;
669
796
  if (requiresAttention !== filter.requiresAttention) {
670
797
  return false;
671
798
  }
672
799
  }
673
- if (filter?.projectKeys && filter.projectKeys.length > 0) {
800
+ if (filter.projectKeys && filter.projectKeys.length > 0) {
674
801
  const projectKeys = new Set(filter.projectKeys.filter((item) => item.trim().length > 0));
675
802
  if (projectKeys.size > 0 && !projectKeys.has(project.projectKey)) {
676
803
  return false;
@@ -678,6 +805,26 @@ export class Session {
678
805
  }
679
806
  return true;
680
807
  }
808
+ matchesAgentFilter(options) {
809
+ const { agent, project, filter } = options;
810
+ if (filter?.labels) {
811
+ const matchesLabels = Object.entries(filter.labels).every(([key, value]) => agent.labels[key] === value);
812
+ if (!matchesLabels) {
813
+ return false;
814
+ }
815
+ }
816
+ const includeArchived = filter?.includeArchived ?? false;
817
+ if (!includeArchived && agent.archivedAt) {
818
+ return false;
819
+ }
820
+ if (filter && !this.agentThinkingOptionMatchesFilter(agent, filter)) {
821
+ return false;
822
+ }
823
+ if (filter && !this.matchesAgentStructuralFilter(agent, project, filter)) {
824
+ return false;
825
+ }
826
+ return true;
827
+ }
681
828
  getAgentUpdateTargetId(update) {
682
829
  return update.kind === "remove" ? update.agentId : update.agent.id;
683
830
  }
@@ -724,54 +871,26 @@ export class Session {
724
871
  const workspaceId = this.resolveRegisteredWorkspaceIdForCwd(normalizedCwd, workspaces);
725
872
  return workspaces.find((workspace) => workspace.workspaceId === workspaceId) ?? null;
726
873
  }
874
+ async findExactWorkspaceByDirectory(cwd, options) {
875
+ const normalizedCwd = await this.resolveWorkspaceDirectory(cwd, options);
876
+ const workspaces = await this.workspaceRegistry.list();
877
+ return workspaces.find((workspace) => workspace.cwd === normalizedCwd) ?? null;
878
+ }
727
879
  async resolveWorkspaceDirectory(cwd, options) {
728
880
  const normalizedCwd = normalizePersistedWorkspaceId(cwd);
729
881
  if (options?.refreshGit === false) {
730
882
  const snapshot = this.workspaceGitService.peekSnapshot(normalizedCwd);
731
883
  return normalizePersistedWorkspaceId(snapshot?.git.repoRoot ?? normalizedCwd);
732
884
  }
733
- try {
734
- const snapshot = await this.workspaceGitService.getSnapshot(normalizedCwd);
735
- return normalizePersistedWorkspaceId(snapshot.git.repoRoot ?? normalizedCwd);
736
- }
737
- catch {
738
- return normalizedCwd;
739
- }
885
+ const checkout = await this.workspaceGitService.getCheckout(normalizedCwd);
886
+ return normalizePersistedWorkspaceId(checkout.worktreeRoot ?? normalizedCwd);
740
887
  }
741
888
  async buildProjectPlacementForWorkspace(workspace, projectRecord) {
742
889
  const project = projectRecord ?? (await this.projectRegistry.get(workspace.projectId));
743
890
  if (!project) {
744
891
  throw new Error(`Project not found for workspace ${workspace.workspaceId}`);
745
892
  }
746
- const checkout = project.kind !== "git"
747
- ? {
748
- cwd: workspace.cwd,
749
- isGit: false,
750
- currentBranch: null,
751
- remoteUrl: null,
752
- worktreeRoot: null,
753
- isPaseoOwnedWorktree: false,
754
- mainRepoRoot: null,
755
- }
756
- : workspace.kind === "worktree"
757
- ? {
758
- cwd: workspace.cwd,
759
- isGit: true,
760
- currentBranch: workspace.displayName,
761
- remoteUrl: null,
762
- worktreeRoot: workspace.cwd,
763
- isPaseoOwnedWorktree: true,
764
- mainRepoRoot: project.rootPath,
765
- }
766
- : {
767
- cwd: workspace.cwd,
768
- isGit: true,
769
- currentBranch: workspace.displayName,
770
- remoteUrl: null,
771
- worktreeRoot: workspace.cwd,
772
- isPaseoOwnedWorktree: false,
773
- mainRepoRoot: null,
774
- };
893
+ const checkout = buildWorkspaceCheckout(workspace, project);
775
894
  return {
776
895
  projectKey: project.projectId,
777
896
  projectName: project.displayName,
@@ -851,360 +970,7 @@ export class Session {
851
970
  try {
852
971
  this.sessionLogger.trace({ messageType: msg.type, payloadBytes: JSON.stringify(msg).length }, "inbound message");
853
972
  try {
854
- switch (msg.type) {
855
- case "voice_audio_chunk":
856
- await this.handleAudioChunk(msg);
857
- break;
858
- case "abort_request":
859
- await this.handleAbort();
860
- break;
861
- case "audio_played":
862
- this.handleAudioPlayed(msg.id);
863
- break;
864
- case "fetch_agents_request":
865
- await this.handleFetchAgents(msg);
866
- break;
867
- case "fetch_agent_history_request":
868
- await this.handleFetchAgentHistory(msg);
869
- break;
870
- case "fetch_workspaces_request":
871
- await this.handleFetchWorkspacesRequest(msg);
872
- break;
873
- case "fetch_agent_request":
874
- await this.handleFetchAgent(msg.agentId, msg.requestId);
875
- break;
876
- case "delete_agent_request":
877
- await this.handleDeleteAgentRequest(msg.agentId, msg.requestId);
878
- break;
879
- case "archive_agent_request":
880
- await this.handleArchiveAgentRequest(msg.agentId, msg.requestId);
881
- break;
882
- case "close_items_request":
883
- await this.handleCloseItemsRequest(msg);
884
- break;
885
- case "update_agent_request":
886
- await this.handleUpdateAgentRequest(msg.agentId, msg.name, msg.labels, msg.requestId);
887
- break;
888
- case "set_voice_mode":
889
- await this.handleSetVoiceMode(msg.enabled, msg.agentId, msg.requestId);
890
- break;
891
- case "send_agent_message_request":
892
- await this.handleSendAgentMessageRequest(msg);
893
- break;
894
- case "wait_for_finish_request":
895
- await this.handleWaitForFinish(msg.agentId, msg.requestId, msg.timeoutMs);
896
- break;
897
- case "get_daemon_config_request":
898
- this.emit({
899
- type: "get_daemon_config_response",
900
- payload: {
901
- requestId: msg.requestId,
902
- config: this.daemonConfigStore.get(),
903
- },
904
- });
905
- break;
906
- case "set_daemon_config_request":
907
- this.emit({
908
- type: "set_daemon_config_response",
909
- payload: {
910
- requestId: msg.requestId,
911
- config: this.daemonConfigStore.patch(msg.config),
912
- },
913
- });
914
- break;
915
- case "dictation_stream_start":
916
- {
917
- const unavailable = this.resolveVoiceFeatureUnavailableContext("dictation");
918
- if (unavailable) {
919
- this.emit({
920
- type: "dictation_stream_error",
921
- payload: {
922
- dictationId: msg.dictationId,
923
- error: unavailable.message,
924
- retryable: unavailable.retryable,
925
- reasonCode: unavailable.reasonCode,
926
- missingModelIds: unavailable.missingModelIds,
927
- },
928
- });
929
- break;
930
- }
931
- }
932
- await this.dictationStreamManager.handleStart(msg.dictationId, msg.format);
933
- break;
934
- case "dictation_stream_chunk":
935
- await this.dictationStreamManager.handleChunk({
936
- dictationId: msg.dictationId,
937
- seq: msg.seq,
938
- audioBase64: msg.audio,
939
- format: msg.format,
940
- });
941
- break;
942
- case "dictation_stream_finish":
943
- await this.dictationStreamManager.handleFinish(msg.dictationId, msg.finalSeq);
944
- break;
945
- case "dictation_stream_cancel":
946
- this.dictationStreamManager.handleCancel(msg.dictationId);
947
- break;
948
- case "create_agent_request":
949
- await this.handleCreateAgentRequest(msg);
950
- break;
951
- case "resume_agent_request":
952
- await this.handleResumeAgentRequest(msg);
953
- break;
954
- case "refresh_agent_request":
955
- await this.handleRefreshAgentRequest(msg);
956
- break;
957
- case "cancel_agent_request":
958
- await this.handleCancelAgentRequest(msg.agentId, msg.requestId);
959
- break;
960
- case "restart_server_request":
961
- await this.handleRestartServerRequest(msg.requestId, msg.reason);
962
- break;
963
- case "shutdown_server_request":
964
- await this.handleShutdownServerRequest(msg.requestId);
965
- break;
966
- case "fetch_agent_timeline_request":
967
- await this.handleFetchAgentTimelineRequest(msg);
968
- break;
969
- case "set_agent_mode_request":
970
- await this.handleSetAgentModeRequest(msg.agentId, msg.modeId, msg.requestId);
971
- break;
972
- case "set_agent_model_request":
973
- await this.handleSetAgentModelRequest(msg.agentId, msg.modelId, msg.requestId);
974
- break;
975
- case "set_agent_feature_request":
976
- await this.handleSetAgentFeatureRequest(msg.agentId, msg.featureId, msg.value, msg.requestId);
977
- break;
978
- case "set_agent_thinking_request":
979
- await this.handleSetAgentThinkingRequest(msg.agentId, msg.thinkingOptionId, msg.requestId);
980
- break;
981
- case "agent_permission_response":
982
- await this.handleAgentPermissionResponse(msg.agentId, msg.requestId, msg.response);
983
- break;
984
- case "checkout_status_request":
985
- await this.handleCheckoutStatusRequest(msg);
986
- break;
987
- case "validate_branch_request":
988
- await this.handleValidateBranchRequest(msg);
989
- break;
990
- case "branch_suggestions_request":
991
- await this.handleBranchSuggestionsRequest(msg);
992
- break;
993
- case "directory_suggestions_request":
994
- await this.handleDirectorySuggestionsRequest(msg);
995
- break;
996
- case "subscribe_checkout_diff_request":
997
- await this.handleSubscribeCheckoutDiffRequest(msg);
998
- break;
999
- case "unsubscribe_checkout_diff_request":
1000
- this.handleUnsubscribeCheckoutDiffRequest(msg);
1001
- break;
1002
- case "checkout_switch_branch_request":
1003
- await this.handleCheckoutSwitchBranchRequest(msg);
1004
- break;
1005
- case "stash_save_request":
1006
- await this.handleStashSaveRequest(msg);
1007
- break;
1008
- case "stash_pop_request":
1009
- await this.handleStashPopRequest(msg);
1010
- break;
1011
- case "stash_list_request":
1012
- await this.handleStashListRequest(msg);
1013
- break;
1014
- case "checkout_commit_request":
1015
- await this.handleCheckoutCommitRequest(msg);
1016
- break;
1017
- case "checkout_merge_request":
1018
- await this.handleCheckoutMergeRequest(msg);
1019
- break;
1020
- case "checkout_merge_from_base_request":
1021
- await this.handleCheckoutMergeFromBaseRequest(msg);
1022
- break;
1023
- case "checkout_pull_request":
1024
- await this.handleCheckoutPullRequest(msg);
1025
- break;
1026
- case "checkout_push_request":
1027
- await this.handleCheckoutPushRequest(msg);
1028
- break;
1029
- case "checkout_pr_create_request":
1030
- await this.handleCheckoutPrCreateRequest(msg);
1031
- break;
1032
- case "checkout_pr_status_request":
1033
- await this.handleCheckoutPrStatusRequest(msg);
1034
- break;
1035
- case "pull_request_timeline_request":
1036
- await this.handlePullRequestTimelineRequest(msg);
1037
- break;
1038
- case "github_search_request":
1039
- await this.handleGitHubSearchRequest(msg);
1040
- break;
1041
- case "paseo_worktree_list_request":
1042
- await this.handlePaseoWorktreeListRequest(msg);
1043
- break;
1044
- case "paseo_worktree_archive_request":
1045
- await this.handlePaseoWorktreeArchiveRequest(msg);
1046
- break;
1047
- case "create_paseo_worktree_request":
1048
- await this.handleCreatePaseoWorktreeRequest(msg);
1049
- break;
1050
- case "workspace_setup_status_request":
1051
- await this.handleWorkspaceSetupStatusRequest(msg);
1052
- break;
1053
- case "list_available_editors_request":
1054
- await this.handleListAvailableEditorsRequest(msg);
1055
- break;
1056
- case "open_in_editor_request":
1057
- await this.handleOpenInEditorRequest(msg);
1058
- break;
1059
- case "open_project_request":
1060
- await this.handleOpenProjectRequest(msg);
1061
- break;
1062
- case "archive_workspace_request":
1063
- await this.handleArchiveWorkspaceRequest(msg);
1064
- break;
1065
- case "file_explorer_request":
1066
- await this.handleFileExplorerRequest(msg);
1067
- break;
1068
- case "project_icon_request":
1069
- await this.handleProjectIconRequest(msg);
1070
- break;
1071
- case "file_download_token_request":
1072
- await this.handleFileDownloadTokenRequest(msg);
1073
- break;
1074
- case "list_provider_models_request":
1075
- await this.handleListProviderModelsRequest(msg);
1076
- break;
1077
- case "list_provider_modes_request":
1078
- await this.handleListProviderModesRequest(msg);
1079
- break;
1080
- case "list_provider_features_request":
1081
- await this.handleListProviderFeaturesRequest(msg);
1082
- break;
1083
- case "list_available_providers_request":
1084
- await this.handleListAvailableProvidersRequest(msg);
1085
- break;
1086
- case "get_providers_snapshot_request":
1087
- await this.handleGetProvidersSnapshotRequest(msg);
1088
- break;
1089
- case "refresh_providers_snapshot_request":
1090
- await this.handleRefreshProvidersSnapshotRequest(msg);
1091
- break;
1092
- case "provider_diagnostic_request":
1093
- await this.handleProviderDiagnosticRequest(msg);
1094
- break;
1095
- case "clear_agent_attention":
1096
- await this.handleClearAgentAttention(msg.agentId, msg.requestId);
1097
- break;
1098
- case "client_heartbeat":
1099
- this.handleClientHeartbeat(msg);
1100
- break;
1101
- case "ping": {
1102
- const now = Date.now();
1103
- this.emit({
1104
- type: "pong",
1105
- payload: {
1106
- requestId: msg.requestId,
1107
- clientSentAt: msg.clientSentAt,
1108
- serverReceivedAt: now,
1109
- serverSentAt: now,
1110
- },
1111
- });
1112
- break;
1113
- }
1114
- case "list_commands_request":
1115
- await this.handleListCommandsRequest(msg);
1116
- break;
1117
- case "register_push_token":
1118
- this.handleRegisterPushToken(msg.token);
1119
- break;
1120
- case "subscribe_terminals_request":
1121
- this.handleSubscribeTerminalsRequest(msg);
1122
- break;
1123
- case "unsubscribe_terminals_request":
1124
- this.handleUnsubscribeTerminalsRequest(msg);
1125
- break;
1126
- case "list_terminals_request":
1127
- await this.handleListTerminalsRequest(msg);
1128
- break;
1129
- case "create_terminal_request":
1130
- await this.handleCreateTerminalRequest(msg);
1131
- break;
1132
- case "start_workspace_script_request":
1133
- await this.handleStartWorkspaceScriptRequest(msg);
1134
- break;
1135
- case "subscribe_terminal_request":
1136
- await this.handleSubscribeTerminalRequest(msg);
1137
- break;
1138
- case "unsubscribe_terminal_request":
1139
- this.handleUnsubscribeTerminalRequest(msg);
1140
- break;
1141
- case "terminal_input":
1142
- this.handleTerminalInput(msg);
1143
- break;
1144
- case "kill_terminal_request":
1145
- await this.handleKillTerminalRequest(msg);
1146
- break;
1147
- case "capture_terminal_request":
1148
- await this.handleCaptureTerminalRequest(msg);
1149
- break;
1150
- case "chat/create":
1151
- await this.handleChatCreateRequest(msg);
1152
- break;
1153
- case "chat/list":
1154
- await this.handleChatListRequest(msg);
1155
- break;
1156
- case "chat/inspect":
1157
- await this.handleChatInspectRequest(msg);
1158
- break;
1159
- case "chat/delete":
1160
- await this.handleChatDeleteRequest(msg);
1161
- break;
1162
- case "chat/post":
1163
- await this.handleChatPostRequest(msg);
1164
- break;
1165
- case "chat/read":
1166
- await this.handleChatReadRequest(msg);
1167
- break;
1168
- case "chat/wait":
1169
- await this.handleChatWaitRequest(msg);
1170
- break;
1171
- case "schedule/create":
1172
- await this.handleScheduleCreateRequest(msg);
1173
- break;
1174
- case "schedule/list":
1175
- await this.handleScheduleListRequest(msg);
1176
- break;
1177
- case "schedule/inspect":
1178
- await this.handleScheduleInspectRequest(msg);
1179
- break;
1180
- case "schedule/logs":
1181
- await this.handleScheduleLogsRequest(msg);
1182
- break;
1183
- case "schedule/pause":
1184
- await this.handleSchedulePauseRequest(msg);
1185
- break;
1186
- case "schedule/resume":
1187
- await this.handleScheduleResumeRequest(msg);
1188
- break;
1189
- case "schedule/delete":
1190
- await this.handleScheduleDeleteRequest(msg);
1191
- break;
1192
- case "loop/run":
1193
- await this.handleLoopRunRequest(msg);
1194
- break;
1195
- case "loop/list":
1196
- await this.handleLoopListRequest(msg);
1197
- break;
1198
- case "loop/inspect":
1199
- await this.handleLoopInspectRequest(msg);
1200
- break;
1201
- case "loop/logs":
1202
- await this.handleLoopLogsRequest(msg);
1203
- break;
1204
- case "loop/stop":
1205
- await this.handleLoopStopRequest(msg);
1206
- break;
1207
- }
973
+ await this.dispatchInboundMessage(msg);
1208
974
  }
1209
975
  catch (error) {
1210
976
  const err = error instanceof Error ? error : new Error(String(error));
@@ -1241,6 +1007,418 @@ export class Session {
1241
1007
  this.inflightRequests--;
1242
1008
  }
1243
1009
  }
1010
+ async dispatchInboundMessage(msg) {
1011
+ const promise = this.dispatchVoiceAndControlMessage(msg) ??
1012
+ this.dispatchAgentLifecycleMessage(msg) ??
1013
+ this.dispatchAgentConfigMessage(msg) ??
1014
+ this.dispatchCheckoutMessage(msg) ??
1015
+ this.dispatchWorkspaceAndProjectMessage(msg) ??
1016
+ this.dispatchProviderMessage(msg) ??
1017
+ this.dispatchTerminalMessage(msg) ??
1018
+ this.dispatchChatScheduleLoopMessage(msg) ??
1019
+ this.dispatchMiscMessage(msg);
1020
+ if (promise)
1021
+ await promise;
1022
+ }
1023
+ dispatchVoiceAndControlMessage(msg) {
1024
+ switch (msg.type) {
1025
+ case "voice_audio_chunk":
1026
+ return this.handleAudioChunk(msg);
1027
+ case "abort_request":
1028
+ return this.handleAbort();
1029
+ case "audio_played":
1030
+ this.handleAudioPlayed(msg.id);
1031
+ return undefined;
1032
+ case "set_voice_mode":
1033
+ return this.handleSetVoiceMode(msg.enabled, msg.agentId, msg.requestId);
1034
+ case "dictation_stream_start":
1035
+ return this.handleDictationStreamStart(msg);
1036
+ case "dictation_stream_chunk":
1037
+ return this.dictationStreamManager.handleChunk({
1038
+ dictationId: msg.dictationId,
1039
+ seq: msg.seq,
1040
+ audioBase64: msg.audio,
1041
+ format: msg.format,
1042
+ });
1043
+ case "dictation_stream_finish":
1044
+ return this.dictationStreamManager.handleFinish(msg.dictationId, msg.finalSeq);
1045
+ case "dictation_stream_cancel":
1046
+ this.dictationStreamManager.handleCancel(msg.dictationId);
1047
+ return undefined;
1048
+ case "restart_server_request":
1049
+ return this.handleRestartServerRequest(msg.requestId, msg.reason);
1050
+ case "shutdown_server_request":
1051
+ return this.handleShutdownServerRequest(msg.requestId);
1052
+ case "client_heartbeat":
1053
+ this.handleClientHeartbeat(msg);
1054
+ return undefined;
1055
+ case "ping": {
1056
+ const now = Date.now();
1057
+ this.emit({
1058
+ type: "pong",
1059
+ payload: {
1060
+ requestId: msg.requestId,
1061
+ clientSentAt: msg.clientSentAt,
1062
+ serverReceivedAt: now,
1063
+ serverSentAt: now,
1064
+ },
1065
+ });
1066
+ return undefined;
1067
+ }
1068
+ default:
1069
+ return undefined;
1070
+ }
1071
+ }
1072
+ async handleDictationStreamStart(msg) {
1073
+ const unavailable = this.resolveVoiceFeatureUnavailableContext("dictation");
1074
+ if (unavailable) {
1075
+ this.emit({
1076
+ type: "dictation_stream_error",
1077
+ payload: {
1078
+ dictationId: msg.dictationId,
1079
+ error: unavailable.message,
1080
+ retryable: unavailable.retryable,
1081
+ reasonCode: unavailable.reasonCode,
1082
+ missingModelIds: unavailable.missingModelIds,
1083
+ },
1084
+ });
1085
+ return;
1086
+ }
1087
+ await this.dictationStreamManager.handleStart(msg.dictationId, msg.format);
1088
+ }
1089
+ dispatchAgentLifecycleMessage(msg) {
1090
+ switch (msg.type) {
1091
+ case "fetch_agents_request":
1092
+ return this.handleFetchAgents(msg);
1093
+ case "fetch_agent_history_request":
1094
+ return this.handleFetchAgentHistory(msg);
1095
+ case "fetch_agent_request":
1096
+ return this.handleFetchAgent(msg.agentId, msg.requestId);
1097
+ case "delete_agent_request":
1098
+ return this.handleDeleteAgentRequest(msg.agentId, msg.requestId);
1099
+ case "archive_agent_request":
1100
+ return this.handleArchiveAgentRequest(msg.agentId, msg.requestId);
1101
+ case "close_items_request":
1102
+ return this.handleCloseItemsRequest(msg);
1103
+ case "update_agent_request":
1104
+ return this.handleUpdateAgentRequest(msg.agentId, msg.name, msg.labels, msg.requestId);
1105
+ case "send_agent_message_request":
1106
+ return this.handleSendAgentMessageRequest(msg);
1107
+ case "wait_for_finish_request":
1108
+ return this.handleWaitForFinish(msg.agentId, msg.requestId, msg.timeoutMs);
1109
+ case "create_agent_request":
1110
+ return this.handleCreateAgentRequest(msg);
1111
+ case "resume_agent_request":
1112
+ return this.handleResumeAgentRequest(msg);
1113
+ case "refresh_agent_request":
1114
+ return this.handleRefreshAgentRequest(msg);
1115
+ case "cancel_agent_request":
1116
+ return this.handleCancelAgentRequest(msg.agentId, msg.requestId);
1117
+ case "fetch_agent_timeline_request":
1118
+ return this.handleFetchAgentTimelineRequest(msg);
1119
+ case "agent_permission_response":
1120
+ return this.handleAgentPermissionResponse(msg.agentId, msg.requestId, msg.response);
1121
+ case "clear_agent_attention":
1122
+ return this.handleClearAgentAttention(msg.agentId, msg.requestId);
1123
+ default:
1124
+ return undefined;
1125
+ }
1126
+ }
1127
+ dispatchAgentConfigMessage(msg) {
1128
+ switch (msg.type) {
1129
+ case "set_agent_mode_request":
1130
+ return this.handleSetAgentModeRequest(msg.agentId, msg.modeId, msg.requestId);
1131
+ case "set_agent_model_request":
1132
+ return this.handleSetAgentModelRequest(msg.agentId, msg.modelId, msg.requestId);
1133
+ case "set_agent_feature_request":
1134
+ return this.handleSetAgentFeatureRequest(msg.agentId, msg.featureId, msg.value, msg.requestId);
1135
+ case "set_agent_thinking_request":
1136
+ return this.handleSetAgentThinkingRequest(msg.agentId, msg.thinkingOptionId, msg.requestId);
1137
+ case "get_daemon_config_request":
1138
+ this.emit({
1139
+ type: "get_daemon_config_response",
1140
+ payload: { requestId: msg.requestId, config: this.daemonConfigStore.get() },
1141
+ });
1142
+ return undefined;
1143
+ case "set_daemon_config_request":
1144
+ this.emit({
1145
+ type: "set_daemon_config_response",
1146
+ payload: {
1147
+ requestId: msg.requestId,
1148
+ config: this.daemonConfigStore.patch(msg.config),
1149
+ },
1150
+ });
1151
+ return undefined;
1152
+ case "read_project_config_request":
1153
+ return this.handleReadProjectConfigRequest(msg);
1154
+ case "write_project_config_request":
1155
+ return this.handleWriteProjectConfigRequest(msg);
1156
+ default:
1157
+ return undefined;
1158
+ }
1159
+ }
1160
+ async handleReadProjectConfigRequest(msg) {
1161
+ const repoRoot = await resolveKnownProjectRootForConfig({
1162
+ repoRoot: msg.repoRoot,
1163
+ projectRegistry: this.projectRegistry,
1164
+ });
1165
+ if (!repoRoot) {
1166
+ this.emitProjectConfigReadFailure(msg, { code: "project_not_found" });
1167
+ return;
1168
+ }
1169
+ const result = readPaseoConfigForEdit(repoRoot);
1170
+ if (!result.ok) {
1171
+ this.sessionLogger.warn({ repoRoot, requestId: msg.requestId, outcome: result.error.code }, "Failed to read project config");
1172
+ this.emitProjectConfigReadFailure(msg, result.error, repoRoot);
1173
+ return;
1174
+ }
1175
+ if (result.config === null) {
1176
+ this.sessionLogger.debug({ repoRoot, requestId: msg.requestId, outcome: "missing_project_config" }, "Project config missing");
1177
+ }
1178
+ this.emit({
1179
+ type: "read_project_config_response",
1180
+ payload: {
1181
+ requestId: msg.requestId,
1182
+ repoRoot,
1183
+ ok: true,
1184
+ config: result.config,
1185
+ revision: result.revision,
1186
+ },
1187
+ });
1188
+ }
1189
+ async handleWriteProjectConfigRequest(msg) {
1190
+ const repoRoot = await resolveKnownProjectRootForConfig({
1191
+ repoRoot: msg.repoRoot,
1192
+ projectRegistry: this.projectRegistry,
1193
+ });
1194
+ if (!repoRoot) {
1195
+ this.emitProjectConfigWriteFailure(msg, { code: "project_not_found" });
1196
+ return;
1197
+ }
1198
+ this.sessionLogger.debug({ repoRoot, requestId: msg.requestId, outcome: "write_attempt" }, "Writing project config");
1199
+ const result = writePaseoConfigForEdit({
1200
+ repoRoot,
1201
+ config: msg.config,
1202
+ expectedRevision: msg.expectedRevision,
1203
+ });
1204
+ if (!result.ok) {
1205
+ this.sessionLogger.debug({ repoRoot, requestId: msg.requestId, outcome: result.error.code }, "Project config write did not complete");
1206
+ this.emitProjectConfigWriteFailure(msg, result.error, repoRoot);
1207
+ return;
1208
+ }
1209
+ this.sessionLogger.debug({ repoRoot, requestId: msg.requestId, outcome: "written" }, "Project config written");
1210
+ this.emit({
1211
+ type: "write_project_config_response",
1212
+ payload: {
1213
+ requestId: msg.requestId,
1214
+ repoRoot,
1215
+ ok: true,
1216
+ config: result.config,
1217
+ revision: result.revision,
1218
+ },
1219
+ });
1220
+ }
1221
+ emitProjectConfigReadFailure(msg, error, repoRoot = msg.repoRoot) {
1222
+ this.emit({
1223
+ type: "read_project_config_response",
1224
+ payload: {
1225
+ requestId: msg.requestId,
1226
+ repoRoot,
1227
+ ok: false,
1228
+ error,
1229
+ },
1230
+ });
1231
+ }
1232
+ emitProjectConfigWriteFailure(msg, error, repoRoot = msg.repoRoot) {
1233
+ this.emit({
1234
+ type: "write_project_config_response",
1235
+ payload: {
1236
+ requestId: msg.requestId,
1237
+ repoRoot,
1238
+ ok: false,
1239
+ error,
1240
+ },
1241
+ });
1242
+ }
1243
+ dispatchCheckoutMessage(msg) {
1244
+ switch (msg.type) {
1245
+ case "checkout_status_request":
1246
+ return this.handleCheckoutStatusRequest(msg);
1247
+ case "validate_branch_request":
1248
+ return this.handleValidateBranchRequest(msg);
1249
+ case "branch_suggestions_request":
1250
+ return this.handleBranchSuggestionsRequest(msg);
1251
+ case "directory_suggestions_request":
1252
+ return this.handleDirectorySuggestionsRequest(msg);
1253
+ case "subscribe_checkout_diff_request":
1254
+ return this.handleSubscribeCheckoutDiffRequest(msg);
1255
+ case "unsubscribe_checkout_diff_request":
1256
+ this.handleUnsubscribeCheckoutDiffRequest(msg);
1257
+ return undefined;
1258
+ case "checkout_switch_branch_request":
1259
+ return this.handleCheckoutSwitchBranchRequest(msg);
1260
+ case "stash_save_request":
1261
+ return this.handleStashSaveRequest(msg);
1262
+ case "stash_pop_request":
1263
+ return this.handleStashPopRequest(msg);
1264
+ case "stash_list_request":
1265
+ return this.handleStashListRequest(msg);
1266
+ case "checkout_commit_request":
1267
+ return this.handleCheckoutCommitRequest(msg);
1268
+ case "checkout_merge_request":
1269
+ return this.handleCheckoutMergeRequest(msg);
1270
+ case "checkout_merge_from_base_request":
1271
+ return this.handleCheckoutMergeFromBaseRequest(msg);
1272
+ case "checkout_pull_request":
1273
+ return this.handleCheckoutPullRequest(msg);
1274
+ case "checkout_push_request":
1275
+ return this.handleCheckoutPushRequest(msg);
1276
+ case "checkout_pr_create_request":
1277
+ return this.handleCheckoutPrCreateRequest(msg);
1278
+ case "checkout_pr_status_request":
1279
+ return this.handleCheckoutPrStatusRequest(msg);
1280
+ case "pull_request_timeline_request":
1281
+ return this.handlePullRequestTimelineRequest(msg);
1282
+ case "github_search_request":
1283
+ return this.handleGitHubSearchRequest(msg);
1284
+ default:
1285
+ return undefined;
1286
+ }
1287
+ }
1288
+ dispatchWorkspaceAndProjectMessage(msg) {
1289
+ switch (msg.type) {
1290
+ case "fetch_workspaces_request":
1291
+ return this.handleFetchWorkspacesRequest(msg);
1292
+ case "paseo_worktree_list_request":
1293
+ return this.handlePaseoWorktreeListRequest(msg);
1294
+ case "paseo_worktree_archive_request":
1295
+ return this.handlePaseoWorktreeArchiveRequest(msg);
1296
+ case "create_paseo_worktree_request":
1297
+ return this.handleCreatePaseoWorktreeRequest(msg);
1298
+ case "workspace_setup_status_request":
1299
+ return this.handleWorkspaceSetupStatusRequest(msg);
1300
+ case "list_available_editors_request":
1301
+ return this.handleListAvailableEditorsRequest(msg);
1302
+ case "open_in_editor_request":
1303
+ return this.handleOpenInEditorRequest(msg);
1304
+ case "open_project_request":
1305
+ return this.handleOpenProjectRequest(msg);
1306
+ case "archive_workspace_request":
1307
+ return this.handleArchiveWorkspaceRequest(msg);
1308
+ case "file_explorer_request":
1309
+ return this.handleFileExplorerRequest(msg);
1310
+ case "project_icon_request":
1311
+ return this.handleProjectIconRequest(msg);
1312
+ case "file_download_token_request":
1313
+ return this.handleFileDownloadTokenRequest(msg);
1314
+ default:
1315
+ return undefined;
1316
+ }
1317
+ }
1318
+ dispatchProviderMessage(msg) {
1319
+ switch (msg.type) {
1320
+ case "list_provider_models_request":
1321
+ return this.handleListProviderModelsRequest(msg);
1322
+ case "list_provider_modes_request":
1323
+ return this.handleListProviderModesRequest(msg);
1324
+ case "list_provider_features_request":
1325
+ return this.handleListProviderFeaturesRequest(msg);
1326
+ case "list_available_providers_request":
1327
+ return this.handleListAvailableProvidersRequest(msg);
1328
+ case "get_providers_snapshot_request":
1329
+ return this.handleGetProvidersSnapshotRequest(msg);
1330
+ case "refresh_providers_snapshot_request":
1331
+ return this.handleRefreshProvidersSnapshotRequest(msg);
1332
+ case "provider_diagnostic_request":
1333
+ return this.handleProviderDiagnosticRequest(msg);
1334
+ default:
1335
+ return undefined;
1336
+ }
1337
+ }
1338
+ dispatchTerminalMessage(msg) {
1339
+ switch (msg.type) {
1340
+ case "subscribe_terminals_request":
1341
+ this.handleSubscribeTerminalsRequest(msg);
1342
+ return undefined;
1343
+ case "unsubscribe_terminals_request":
1344
+ this.handleUnsubscribeTerminalsRequest(msg);
1345
+ return undefined;
1346
+ case "list_terminals_request":
1347
+ return this.handleListTerminalsRequest(msg);
1348
+ case "create_terminal_request":
1349
+ return this.handleCreateTerminalRequest(msg);
1350
+ case "start_workspace_script_request":
1351
+ return this.handleStartWorkspaceScriptRequest(msg);
1352
+ case "subscribe_terminal_request":
1353
+ return this.handleSubscribeTerminalRequest(msg);
1354
+ case "unsubscribe_terminal_request":
1355
+ this.handleUnsubscribeTerminalRequest(msg);
1356
+ return undefined;
1357
+ case "terminal_input":
1358
+ this.handleTerminalInput(msg);
1359
+ return undefined;
1360
+ case "kill_terminal_request":
1361
+ return this.handleKillTerminalRequest(msg);
1362
+ case "capture_terminal_request":
1363
+ return this.handleCaptureTerminalRequest(msg);
1364
+ default:
1365
+ return undefined;
1366
+ }
1367
+ }
1368
+ dispatchChatScheduleLoopMessage(msg) {
1369
+ switch (msg.type) {
1370
+ case "chat/create":
1371
+ return this.handleChatCreateRequest(msg);
1372
+ case "chat/list":
1373
+ return this.handleChatListRequest(msg);
1374
+ case "chat/inspect":
1375
+ return this.handleChatInspectRequest(msg);
1376
+ case "chat/delete":
1377
+ return this.handleChatDeleteRequest(msg);
1378
+ case "chat/post":
1379
+ return this.handleChatPostRequest(msg);
1380
+ case "chat/read":
1381
+ return this.handleChatReadRequest(msg);
1382
+ case "chat/wait":
1383
+ return this.handleChatWaitRequest(msg);
1384
+ case "schedule/create":
1385
+ return this.handleScheduleCreateRequest(msg);
1386
+ case "schedule/list":
1387
+ return this.handleScheduleListRequest(msg);
1388
+ case "schedule/inspect":
1389
+ return this.handleScheduleInspectRequest(msg);
1390
+ case "schedule/logs":
1391
+ return this.handleScheduleLogsRequest(msg);
1392
+ case "schedule/pause":
1393
+ return this.handleSchedulePauseRequest(msg);
1394
+ case "schedule/resume":
1395
+ return this.handleScheduleResumeRequest(msg);
1396
+ case "schedule/delete":
1397
+ return this.handleScheduleDeleteRequest(msg);
1398
+ case "loop/run":
1399
+ return this.handleLoopRunRequest(msg);
1400
+ case "loop/list":
1401
+ return this.handleLoopListRequest(msg);
1402
+ case "loop/inspect":
1403
+ return this.handleLoopInspectRequest(msg);
1404
+ case "loop/logs":
1405
+ return this.handleLoopLogsRequest(msg);
1406
+ case "loop/stop":
1407
+ return this.handleLoopStopRequest(msg);
1408
+ default:
1409
+ return undefined;
1410
+ }
1411
+ }
1412
+ async dispatchMiscMessage(msg) {
1413
+ switch (msg.type) {
1414
+ case "list_commands_request":
1415
+ await this.handleListCommandsRequest(msg);
1416
+ return;
1417
+ case "register_push_token":
1418
+ this.handleRegisterPushToken(msg.token);
1419
+ return;
1420
+ }
1421
+ }
1244
1422
  resetPeakInflight() {
1245
1423
  this.peakInflightRequests = this.inflightRequests;
1246
1424
  }
@@ -1441,13 +1619,15 @@ export class Session {
1441
1619
  return { agentId, archivedAt: archivedRecord.archivedAt };
1442
1620
  }
1443
1621
  async handleCloseItemsRequest(msg) {
1622
+ const archiveResults = await Promise.allSettled(msg.agentIds.map((agentId) => this.archiveAgentForClose(agentId)));
1444
1623
  const agents = [];
1445
- for (const agentId of msg.agentIds) {
1446
- try {
1447
- agents.push(await this.archiveAgentForClose(agentId));
1624
+ for (let i = 0; i < archiveResults.length; i += 1) {
1625
+ const result = archiveResults[i];
1626
+ if (result.status === "fulfilled") {
1627
+ agents.push(result.value);
1448
1628
  }
1449
- catch (error) {
1450
- this.sessionLogger.warn({ err: error, agentId, requestId: msg.requestId }, "Failed to archive agent during close_items batch");
1629
+ else {
1630
+ this.sessionLogger.warn({ err: result.reason, agentId: msg.agentIds[i], requestId: msg.requestId }, "Failed to archive agent during close_items batch");
1451
1631
  }
1452
1632
  }
1453
1633
  const terminals = [];
@@ -1529,7 +1709,9 @@ export class Session {
1529
1709
  requestId,
1530
1710
  agentId,
1531
1711
  accepted: false,
1532
- error: error?.message ? String(error.message) : "Failed to update agent",
1712
+ error: error?.message
1713
+ ? String(error.message)
1714
+ : "Failed to update agent",
1533
1715
  },
1534
1716
  });
1535
1717
  }
@@ -1899,24 +2081,15 @@ export class Session {
1899
2081
  initialPrompt: trimmedPrompt,
1900
2082
  });
1901
2083
  await this.forwardAgentUpdate(snapshot);
1902
- if (trimmedPrompt || (images?.length ?? 0) > 0 || (attachments?.length ?? 0) > 0) {
1903
- scheduleAgentMetadataGeneration({
1904
- agentManager: this.agentManager,
1905
- agentId: snapshot.id,
1906
- cwd: snapshot.cwd,
1907
- initialPrompt: trimmedPrompt,
1908
- explicitTitle,
1909
- paseoHome: this.paseoHome,
1910
- logger: this.sessionLogger,
1911
- deps: {
1912
- workspaceGitService: this.workspaceGitService,
1913
- },
1914
- });
1915
- const started = await this.handleSendAgentMessage(snapshot.id, trimmedPrompt || "", resolveClientMessageId(clientMessageId), images, attachments, outputSchema ? { outputSchema } : undefined);
1916
- if (!started.ok) {
1917
- throw new Error(started.error);
1918
- }
1919
- }
2084
+ await this.sendInitialCreateAgentPrompt({
2085
+ snapshot,
2086
+ trimmedPrompt,
2087
+ images,
2088
+ attachments,
2089
+ clientMessageId,
2090
+ outputSchema,
2091
+ explicitTitle,
2092
+ });
1920
2093
  if (requestId) {
1921
2094
  const agentPayload = await this.buildAgentPayload(snapshot);
1922
2095
  this.emit({
@@ -1975,6 +2148,31 @@ export class Session {
1975
2148
  });
1976
2149
  }
1977
2150
  }
2151
+ async sendInitialCreateAgentPrompt(params) {
2152
+ const { snapshot, trimmedPrompt, images, attachments, clientMessageId, outputSchema } = params;
2153
+ const hasPrompt = Boolean(trimmedPrompt);
2154
+ const hasImages = (images?.length ?? 0) > 0;
2155
+ const hasAttachments = (attachments?.length ?? 0) > 0;
2156
+ if (!hasPrompt && !hasImages && !hasAttachments) {
2157
+ return;
2158
+ }
2159
+ scheduleAgentMetadataGeneration({
2160
+ agentManager: this.agentManager,
2161
+ agentId: snapshot.id,
2162
+ cwd: snapshot.cwd,
2163
+ initialPrompt: trimmedPrompt,
2164
+ explicitTitle: params.explicitTitle,
2165
+ paseoHome: this.paseoHome,
2166
+ logger: this.sessionLogger,
2167
+ deps: {
2168
+ workspaceGitService: this.workspaceGitService,
2169
+ },
2170
+ });
2171
+ const started = await this.handleSendAgentMessage(snapshot.id, trimmedPrompt || "", resolveClientMessageId(clientMessageId), images, attachments, outputSchema ? { outputSchema } : undefined);
2172
+ if (!started.ok) {
2173
+ throw new Error(started.error);
2174
+ }
2175
+ }
1978
2176
  async handleResumeAgentRequest(msg) {
1979
2177
  const { handle, overrides, requestId } = msg;
1980
2178
  if (!handle) {
@@ -2041,7 +2239,11 @@ export class Session {
2041
2239
  if (!record) {
2042
2240
  throw new Error(`Agent not found: ${agentId}`);
2043
2241
  }
2044
- const handle = toAgentPersistenceHandle(this.sessionLogger, this.providerRegistry, record.persistence);
2242
+ const providerRegistry = this.getProviderRegistry();
2243
+ if (!isStoredAgentProviderAvailable(record, Object.keys(providerRegistry))) {
2244
+ throw new Error(`Agent ${agentId} references unavailable provider '${record.provider}'`);
2245
+ }
2246
+ const handle = toAgentPersistenceHandle(providerRegistry, record.persistence);
2045
2247
  if (!handle) {
2046
2248
  throw new Error(`Agent ${agentId} cannot be refreshed because it lacks persistence`);
2047
2249
  }
@@ -2107,13 +2309,32 @@ export class Session {
2107
2309
  github: this.github,
2108
2310
  }, config, gitOptions, legacyWorktreeName, attachments);
2109
2311
  }
2312
+ emitProviderDisabledResponse(kind, provider, requestId, fetchedAt) {
2313
+ const payload = {
2314
+ provider,
2315
+ error: `Provider ${provider} is disabled`,
2316
+ fetchedAt,
2317
+ requestId,
2318
+ };
2319
+ if (kind === "models") {
2320
+ this.emit({ type: "list_provider_models_response", payload });
2321
+ }
2322
+ else {
2323
+ this.emit({ type: "list_provider_modes_response", payload });
2324
+ }
2325
+ }
2110
2326
  async handleListProviderModelsRequest(msg) {
2111
2327
  const cwd = resolveSnapshotCwd(msg.cwd ? expandTilde(msg.cwd) : undefined);
2112
2328
  const fetchedAt = new Date().toISOString();
2113
2329
  const manager = this.providerSnapshotManager;
2114
2330
  if (!manager) {
2115
2331
  try {
2116
- const models = await this.providerRegistry[msg.provider].fetchModels({
2332
+ const definition = this.getProviderRegistry()[msg.provider];
2333
+ if (!definition.enabled) {
2334
+ this.emitProviderDisabledResponse("models", msg.provider, msg.requestId, fetchedAt);
2335
+ return;
2336
+ }
2337
+ const models = await definition.fetchModels({
2117
2338
  cwd,
2118
2339
  force: false,
2119
2340
  });
@@ -2155,6 +2376,10 @@ export class Session {
2155
2376
  });
2156
2377
  return;
2157
2378
  }
2379
+ if (!entry.enabled) {
2380
+ this.emitProviderDisabledResponse("models", msg.provider, msg.requestId, fetchedAt);
2381
+ return;
2382
+ }
2158
2383
  if (entry.status === "ready") {
2159
2384
  this.emit({
2160
2385
  type: "list_provider_models_response",
@@ -2199,6 +2424,10 @@ export class Session {
2199
2424
  });
2200
2425
  return;
2201
2426
  }
2427
+ if (!entry.enabled) {
2428
+ this.emitProviderDisabledResponse("modes", msg.provider, msg.requestId, fetchedAt);
2429
+ return;
2430
+ }
2202
2431
  if (entry.status === "ready") {
2203
2432
  this.emit({
2204
2433
  type: "list_provider_modes_response",
@@ -2227,7 +2456,12 @@ export class Session {
2227
2456
  return;
2228
2457
  }
2229
2458
  try {
2230
- const modes = await this.providerRegistry[msg.provider].fetchModes({
2459
+ const definition = this.getProviderRegistry()[msg.provider];
2460
+ if (!definition.enabled) {
2461
+ this.emitProviderDisabledResponse("modes", msg.provider, msg.requestId, fetchedAt);
2462
+ return;
2463
+ }
2464
+ const modes = await definition.fetchModes({
2231
2465
  cwd,
2232
2466
  force: false,
2233
2467
  });
@@ -2262,6 +2496,9 @@ export class Session {
2262
2496
  }
2263
2497
  const findEntry = () => manager.getSnapshot(cwd).find((candidate) => candidate.provider === provider);
2264
2498
  let entry = findEntry();
2499
+ if (entry && !entry.enabled) {
2500
+ return entry;
2501
+ }
2265
2502
  if (!entry || entry.status === "loading") {
2266
2503
  // Awaits the in-flight warmup (deduped per-cwd) so old clients still get
2267
2504
  // a resolved answer rather than a loading placeholder.
@@ -2374,7 +2611,7 @@ export class Session {
2374
2611
  }
2375
2612
  async handleProviderDiagnosticRequest(msg) {
2376
2613
  try {
2377
- const client = this.providerRegistry[msg.provider].createClient(this.sessionLogger);
2614
+ const client = this.getProviderRegistry()[msg.provider].createClient(this.sessionLogger);
2378
2615
  const diagnostic = client.getDiagnostic
2379
2616
  ? (await client.getDiagnostic()).diagnostic
2380
2617
  : "No diagnostic available for this provider.";
@@ -2431,7 +2668,7 @@ export class Session {
2431
2668
  ? [
2432
2669
  "Files changed:",
2433
2670
  ...diff.structured.map((file) => {
2434
- const changeType = file.isNew ? "A" : file.isDeleted ? "D" : "M";
2671
+ const changeType = diffChangeTypeFor(file);
2435
2672
  const status = file.status && file.status !== "ok" ? ` [${file.status}]` : "";
2436
2673
  return `${changeType}\t${file.path}\t(+${file.additions} -${file.deletions})${status}`;
2437
2674
  }),
@@ -2487,7 +2724,7 @@ export class Session {
2487
2724
  ? [
2488
2725
  "Files changed:",
2489
2726
  ...diff.structured.map((file) => {
2490
- const changeType = file.isNew ? "A" : file.isDeleted ? "D" : "M";
2727
+ const changeType = diffChangeTypeFor(file);
2491
2728
  const status = file.status && file.status !== "ok" ? ` [${file.status}]` : "";
2492
2729
  return `${changeType}\t${file.path}\t(+${file.additions} -${file.deletions})${status}`;
2493
2730
  }),
@@ -2543,7 +2780,9 @@ export class Session {
2543
2780
  return snapshot.git.isDirty === true;
2544
2781
  }
2545
2782
  catch (error) {
2546
- throw new Error(`Unable to inspect git status for ${cwd}: ${error.message}`);
2783
+ throw new Error(`Unable to inspect git status for ${cwd}: ${error.message}`, {
2784
+ cause: error,
2785
+ });
2547
2786
  }
2548
2787
  }
2549
2788
  async checkoutExistingBranch(cwd, branch) {
@@ -2623,7 +2862,9 @@ export class Session {
2623
2862
  requestId,
2624
2863
  agentId,
2625
2864
  accepted: false,
2626
- error: error?.message ? String(error.message) : "Failed to set agent mode",
2865
+ error: error?.message
2866
+ ? String(error.message)
2867
+ : "Failed to set agent mode",
2627
2868
  },
2628
2869
  });
2629
2870
  }
@@ -2655,7 +2896,9 @@ export class Session {
2655
2896
  requestId,
2656
2897
  agentId,
2657
2898
  accepted: false,
2658
- error: error?.message ? String(error.message) : "Failed to set agent model",
2899
+ error: error?.message
2900
+ ? String(error.message)
2901
+ : "Failed to set agent model",
2659
2902
  },
2660
2903
  });
2661
2904
  }
@@ -2687,7 +2930,9 @@ export class Session {
2687
2930
  requestId,
2688
2931
  agentId,
2689
2932
  accepted: false,
2690
- error: error?.message ? String(error.message) : "Failed to set agent feature",
2933
+ error: error?.message
2934
+ ? String(error.message)
2935
+ : "Failed to set agent feature",
2691
2936
  },
2692
2937
  });
2693
2938
  }
@@ -2719,7 +2964,9 @@ export class Session {
2719
2964
  requestId,
2720
2965
  agentId,
2721
2966
  accepted: false,
2722
- error: error?.message ? String(error.message) : "Failed to set agent thinking option",
2967
+ error: error?.message
2968
+ ? String(error.message)
2969
+ : "Failed to set agent thinking option",
2723
2970
  },
2724
2971
  });
2725
2972
  }
@@ -3232,6 +3479,7 @@ export class Session {
3232
3479
  cwd,
3233
3480
  isGit: true,
3234
3481
  repoRoot: snapshot.git.repoRoot,
3482
+ mainRepoRoot: snapshot.git.mainRepoRoot,
3235
3483
  currentBranch: snapshot.git.currentBranch ?? null,
3236
3484
  isDirty: snapshot.git.isDirty,
3237
3485
  baseRef: snapshot.git.baseRef ?? null,
@@ -3322,7 +3570,9 @@ export class Session {
3322
3570
  const message = branchLabel
3323
3571
  ? `${Session.PASEO_STASH_PREFIX} ${branchLabel}`
3324
3572
  : `${Session.PASEO_STASH_PREFIX} unnamed`;
3325
- await execCommand("git", ["stash", "push", "--include-untracked", "-m", message], { cwd });
3573
+ await execCommand("git", ["stash", "push", "--include-untracked", "-m", message], {
3574
+ cwd,
3575
+ });
3326
3576
  await this.notifyGitMutation(cwd, "stash-push");
3327
3577
  this.checkoutDiffManager.scheduleRefreshForCwd(cwd);
3328
3578
  this.emit({
@@ -3340,7 +3590,9 @@ export class Session {
3340
3590
  async handleStashPopRequest(msg) {
3341
3591
  const { cwd, stashIndex, requestId } = msg;
3342
3592
  try {
3343
- await execCommand("git", ["stash", "pop", `stash@{${stashIndex}}`], { cwd });
3593
+ await execCommand("git", ["stash", "pop", `stash@{${stashIndex}}`], {
3594
+ cwd,
3595
+ });
3344
3596
  await this.notifyGitMutation(cwd, "stash-pop");
3345
3597
  this.checkoutDiffManager.scheduleRefreshForCwd(cwd);
3346
3598
  this.emit({
@@ -3568,7 +3820,7 @@ export class Session {
3568
3820
  title,
3569
3821
  body,
3570
3822
  base: msg.baseRef,
3571
- }, this.github, this.workspaceGitService);
3823
+ }, this.github);
3572
3824
  await this.notifyGitMutation(cwd, "create-pr", { invalidateGithub: true });
3573
3825
  this.emit({
3574
3826
  type: "checkout_pr_create_response",
@@ -3912,9 +4164,12 @@ export class Session {
3912
4164
  // (excluding internal agents which are for ephemeral system tasks)
3913
4165
  const registryRecords = await this.agentStorage.list();
3914
4166
  const liveIds = new Set(agentSnapshots.map((a) => a.id));
4167
+ const registeredProviderIds = this.getRegisteredProviderIds();
3915
4168
  const persistedAgents = registryRecords
3916
4169
  .filter((record) => !liveIds.has(record.id) && !record.internal)
3917
- .map((record) => this.buildStoredAgentPayload(record));
4170
+ .filter((record) => filter?.includeUnavailablePersisted === true ||
4171
+ isStoredAgentProviderAvailable(record, registeredProviderIds))
4172
+ .map((record) => this.buildStoredAgentPayload(record, registeredProviderIds));
3918
4173
  let agents = [...liveAgents, ...persistedAgents];
3919
4174
  agents = agents.filter((agent) => this.isProviderVisibleToClient(agent.provider));
3920
4175
  // Filter by labels if filter provided
@@ -4095,25 +4350,7 @@ export class Session {
4095
4350
  if (!payload.values || typeof payload.values !== "object") {
4096
4351
  throw new SessionRequestError("invalid_cursor", "Invalid fetch_agents cursor");
4097
4352
  }
4098
- const cursorSort = [];
4099
- for (const item of payload.sort) {
4100
- if (!item ||
4101
- typeof item !== "object" ||
4102
- typeof item.key !== "string" ||
4103
- typeof item.direction !== "string") {
4104
- throw new SessionRequestError("invalid_cursor", "Invalid fetch_agents cursor");
4105
- }
4106
- const key = item.key;
4107
- const direction = item.direction;
4108
- if ((key !== "status_priority" &&
4109
- key !== "created_at" &&
4110
- key !== "updated_at" &&
4111
- key !== "title") ||
4112
- (direction !== "asc" && direction !== "desc")) {
4113
- throw new SessionRequestError("invalid_cursor", "Invalid fetch_agents cursor");
4114
- }
4115
- cursorSort.push({ key, direction });
4116
- }
4353
+ const cursorSort = parseFetchAgentsCursorSort(payload.sort);
4117
4354
  if (cursorSort.length !== sort.length ||
4118
4355
  cursorSort.some((entry, index) => entry.key !== sort[index]?.key || entry.direction !== sort[index]?.direction)) {
4119
4356
  throw new SessionRequestError("invalid_cursor", "fetch_agents cursor does not match current sort");
@@ -4145,18 +4382,49 @@ export class Session {
4145
4382
  .filter((project) => !project.archivedAt)
4146
4383
  .map((project) => [project.projectId, project]));
4147
4384
  const placementsByCwd = new Map();
4148
- for (const workspace of persistedWorkspaces) {
4149
- if (workspace.archivedAt) {
4150
- continue;
4151
- }
4385
+ const pairs = persistedWorkspaces.flatMap((workspace) => {
4386
+ if (workspace.archivedAt)
4387
+ return [];
4152
4388
  const project = activeProjects.get(workspace.projectId);
4153
- if (!project) {
4154
- continue;
4155
- }
4156
- placementsByCwd.set(normalizePersistedWorkspaceId(workspace.cwd), await this.buildProjectPlacementForWorkspace(workspace, project));
4389
+ if (!project)
4390
+ return [];
4391
+ return [{ workspace, project }];
4392
+ });
4393
+ const placements = await Promise.all(pairs.map(({ workspace, project }) => this.buildProjectPlacementForWorkspace(workspace, project)));
4394
+ for (let i = 0; i < pairs.length; i += 1) {
4395
+ placementsByCwd.set(normalizePersistedWorkspaceId(pairs[i].workspace.cwd), placements[i]);
4157
4396
  }
4158
4397
  return placementsByCwd;
4159
4398
  }
4399
+ async collectFetchAgentsEntries(params) {
4400
+ const { candidates, limit, getPlacement, filter } = params;
4401
+ const matchedEntries = [];
4402
+ const batchSize = 25;
4403
+ for (let start = 0; start < candidates.length && matchedEntries.length <= limit; start += batchSize) {
4404
+ const batch = candidates.slice(start, start + batchSize);
4405
+ const batchEntries = await Promise.all(batch.map(async (agent) => {
4406
+ const project = await getPlacement(agent.cwd);
4407
+ return project ? { agent, project } : null;
4408
+ }));
4409
+ for (const entry of batchEntries) {
4410
+ if (!entry) {
4411
+ continue;
4412
+ }
4413
+ if (!this.matchesAgentFilter({
4414
+ agent: entry.agent,
4415
+ project: entry.project,
4416
+ filter,
4417
+ })) {
4418
+ continue;
4419
+ }
4420
+ matchedEntries.push(entry);
4421
+ if (matchedEntries.length > limit) {
4422
+ break;
4423
+ }
4424
+ }
4425
+ }
4426
+ return matchedEntries;
4427
+ }
4160
4428
  async listFetchAgentsEntries(request) {
4161
4429
  const filter = request.type === "fetch_agent_history_request" &&
4162
4430
  request.filter?.includeArchived === undefined
@@ -4166,6 +4434,7 @@ export class Session {
4166
4434
  const sort = this.normalizeFetchAgentsSort(request.sort);
4167
4435
  let agents = await this.listAgentPayloads({
4168
4436
  labels: filter?.labels,
4437
+ includeUnavailablePersisted: request.type === "fetch_agent_history_request",
4169
4438
  });
4170
4439
  const activePlacementsByCwd = scope === "active" ? await this.buildActiveProjectPlacementsByWorkspaceCwd() : null;
4171
4440
  if (activePlacementsByCwd) {
@@ -4192,31 +4461,12 @@ export class Session {
4192
4461
  candidates = candidates.filter((agent) => this.compareAgentWithCursor(agent, cursor, sort) > 0);
4193
4462
  }
4194
4463
  const limit = request.page?.limit ?? 200;
4195
- const matchedEntries = [];
4196
- const batchSize = 25;
4197
- for (let start = 0; start < candidates.length && matchedEntries.length <= limit; start += batchSize) {
4198
- const batch = candidates.slice(start, start + batchSize);
4199
- const batchEntries = await Promise.all(batch.map(async (agent) => {
4200
- const project = await getPlacement(agent.cwd);
4201
- return project ? { agent, project } : null;
4202
- }));
4203
- for (const entry of batchEntries) {
4204
- if (!entry) {
4205
- continue;
4206
- }
4207
- if (!this.matchesAgentFilter({
4208
- agent: entry.agent,
4209
- project: entry.project,
4210
- filter,
4211
- })) {
4212
- continue;
4213
- }
4214
- matchedEntries.push(entry);
4215
- if (matchedEntries.length > limit) {
4216
- break;
4217
- }
4218
- }
4219
- }
4464
+ const matchedEntries = await this.collectFetchAgentsEntries({
4465
+ candidates,
4466
+ limit,
4467
+ getPlacement,
4468
+ filter,
4469
+ });
4220
4470
  const pagedEntries = matchedEntries.slice(0, limit);
4221
4471
  const hasMore = matchedEntries.length > limit;
4222
4472
  const nextCursor = hasMore && pagedEntries.length > 0
@@ -4277,6 +4527,11 @@ export class Session {
4277
4527
  resolveHealth: this.resolveScriptHealth ?? undefined,
4278
4528
  })
4279
4529
  : [],
4530
+ ...(resolvedProjectRecord
4531
+ ? {
4532
+ project: await this.buildProjectPlacementForWorkspace(workspace, resolvedProjectRecord),
4533
+ }
4534
+ : {}),
4280
4535
  };
4281
4536
  }
4282
4537
  buildWorkspaceGitRuntimePayload(snapshot) {
@@ -4363,16 +4618,14 @@ export class Session {
4363
4618
  const descriptorsByWorkspaceId = new Map();
4364
4619
  const workspaceIds = options.workspaceIds ? new Set(options.workspaceIds) : null;
4365
4620
  const workspaceIdsByDirectory = new Map(activeRecords.map((workspace) => [normalizePersistedWorkspaceId(workspace.cwd), workspace.workspaceId]));
4366
- for (const workspace of activeRecords) {
4367
- if (workspaceIds && !workspaceIds.has(workspace.workspaceId)) {
4368
- continue;
4369
- }
4370
- const projectRecord = activeProjects.get(workspace.projectId) ?? null;
4371
- descriptorsByWorkspaceId.set(workspace.workspaceId, await this.buildWorkspaceDescriptor({
4372
- workspace,
4373
- projectRecord,
4374
- includeGitData: options.includeGitData,
4375
- }));
4621
+ const includedWorkspaces = activeRecords.filter((workspace) => !workspaceIds || workspaceIds.has(workspace.workspaceId));
4622
+ const workspaceDescriptors = await Promise.all(includedWorkspaces.map((workspace) => this.buildWorkspaceDescriptor({
4623
+ workspace,
4624
+ projectRecord: activeProjects.get(workspace.projectId) ?? null,
4625
+ includeGitData: options.includeGitData,
4626
+ })));
4627
+ for (let i = 0; i < includedWorkspaces.length; i += 1) {
4628
+ descriptorsByWorkspaceId.set(includedWorkspaces[i].workspaceId, workspaceDescriptors[i]);
4376
4629
  }
4377
4630
  for (const agent of agents) {
4378
4631
  if (agent.archivedAt) {
@@ -4402,8 +4655,13 @@ export class Session {
4402
4655
  if (exact) {
4403
4656
  return exact.workspaceId;
4404
4657
  }
4658
+ const userHome = homedir();
4405
4659
  let bestMatch = null;
4406
4660
  for (const workspace of workspaces) {
4661
+ if (workspace.cwd === userHome)
4662
+ continue;
4663
+ if (workspace.archivedAt)
4664
+ continue;
4407
4665
  const prefix = workspace.cwd.endsWith(sep) ? workspace.cwd : `${workspace.cwd}${sep}`;
4408
4666
  if (!normalizedCwd.startsWith(prefix)) {
4409
4667
  continue;
@@ -4488,25 +4746,7 @@ export class Session {
4488
4746
  if (!payload.values || typeof payload.values !== "object") {
4489
4747
  throw new SessionRequestError("invalid_cursor", "Invalid fetch_workspaces cursor");
4490
4748
  }
4491
- const cursorSort = [];
4492
- for (const item of payload.sort) {
4493
- if (!item ||
4494
- typeof item !== "object" ||
4495
- typeof item.key !== "string" ||
4496
- typeof item.direction !== "string") {
4497
- throw new SessionRequestError("invalid_cursor", "Invalid fetch_workspaces cursor");
4498
- }
4499
- const key = item.key;
4500
- const direction = item.direction;
4501
- if ((key !== "status_priority" &&
4502
- key !== "activity_at" &&
4503
- key !== "name" &&
4504
- key !== "project_id") ||
4505
- (direction !== "asc" && direction !== "desc")) {
4506
- throw new SessionRequestError("invalid_cursor", "Invalid fetch_workspaces cursor");
4507
- }
4508
- cursorSort.push({ key, direction });
4509
- }
4749
+ const cursorSort = parseFetchWorkspacesCursorSort(payload.sort);
4510
4750
  if (cursorSort.length !== sort.length ||
4511
4751
  cursorSort.some((entry, index) => entry.key !== sort[index]?.key || entry.direction !== sort[index]?.direction)) {
4512
4752
  throw new SessionRequestError("invalid_cursor", "fetch_workspaces cursor does not match current sort");
@@ -4633,41 +4873,121 @@ export class Session {
4633
4873
  }
4634
4874
  }
4635
4875
  async findOrCreateWorkspaceForDirectory(cwd) {
4876
+ const inputCwd = normalizePersistedWorkspaceId(cwd);
4636
4877
  const normalizedCwd = await this.resolveWorkspaceDirectory(cwd);
4637
- const existingWorkspace = await this.findWorkspaceByDirectory(normalizedCwd);
4878
+ const existingWorkspace = await this.findExactWorkspaceByDirectory(normalizedCwd, {
4879
+ refreshGit: false,
4880
+ });
4638
4881
  if (existingWorkspace) {
4639
- return this.ensureWorkspaceRecordUnarchived(existingWorkspace);
4882
+ if (existingWorkspace.archivedAt && inputCwd !== normalizedCwd) {
4883
+ const timestamp = new Date().toISOString();
4884
+ const displayName = basename(inputCwd) || inputCwd;
4885
+ const projectRecord = createPersistedProjectRecord({
4886
+ projectId: inputCwd,
4887
+ rootPath: inputCwd,
4888
+ kind: "non_git",
4889
+ displayName,
4890
+ createdAt: timestamp,
4891
+ updatedAt: timestamp,
4892
+ });
4893
+ await this.projectRegistry.upsert(projectRecord);
4894
+ const workspaceRecord = createPersistedWorkspaceRecord({
4895
+ workspaceId: inputCwd,
4896
+ projectId: projectRecord.projectId,
4897
+ cwd: inputCwd,
4898
+ kind: "directory",
4899
+ displayName,
4900
+ createdAt: timestamp,
4901
+ updatedAt: timestamp,
4902
+ });
4903
+ await this.workspaceRegistry.upsert(workspaceRecord);
4904
+ return workspaceRecord;
4905
+ }
4906
+ return this.reclassifyOrUnarchiveWorkspaceForDirectory({
4907
+ workspace: existingWorkspace,
4908
+ project: await this.projectRegistry.get(existingWorkspace.projectId),
4909
+ cwd: normalizedCwd,
4910
+ });
4640
4911
  }
4641
- const placement = await buildProjectPlacementForCwdStandalone({
4642
- cwd: normalizedCwd,
4643
- workspaceGitService: this.workspaceGitService,
4644
- });
4645
- const workspaceId = deriveWorkspaceId(normalizedCwd, placement.checkout);
4912
+ return this.createWorkspaceForDirectory(normalizedCwd);
4913
+ }
4914
+ async createWorkspaceForDirectory(cwd) {
4915
+ const checkout = await this.workspaceGitService.getCheckout(cwd);
4916
+ const membership = classifyDirectoryForProjectMembership({ cwd, checkout });
4646
4917
  const timestamp = new Date().toISOString();
4647
- const projectRecord = createPersistedProjectRecord({
4648
- projectId: placement.projectKey,
4649
- rootPath: deriveProjectRootPath({ cwd: normalizedCwd, checkout: placement.checkout }),
4650
- kind: deriveProjectKind(placement.checkout),
4651
- displayName: placement.projectName,
4652
- createdAt: timestamp,
4653
- updatedAt: timestamp,
4918
+ const projectRecord = await this.resolveProjectRecordForPlacement({
4919
+ membership,
4920
+ timestamp,
4654
4921
  });
4655
4922
  await this.projectRegistry.upsert(projectRecord);
4656
4923
  const workspaceRecord = createPersistedWorkspaceRecord({
4657
- workspaceId,
4658
- projectId: placement.projectKey,
4659
- cwd: normalizedCwd,
4660
- kind: deriveWorkspaceKind(placement.checkout),
4661
- displayName: deriveWorkspaceDisplayName({
4662
- cwd: normalizedCwd,
4663
- checkout: placement.checkout,
4664
- }),
4924
+ workspaceId: membership.workspaceId,
4925
+ projectId: projectRecord.projectId,
4926
+ cwd,
4927
+ kind: membership.workspaceKind,
4928
+ displayName: membership.workspaceDisplayName,
4665
4929
  createdAt: timestamp,
4666
4930
  updatedAt: timestamp,
4667
4931
  });
4668
4932
  await this.workspaceRegistry.upsert(workspaceRecord);
4669
4933
  return workspaceRecord;
4670
4934
  }
4935
+ async reclassifyOrUnarchiveWorkspaceForDirectory(input) {
4936
+ const checkout = await this.workspaceGitService.getCheckout(input.cwd);
4937
+ const membership = classifyDirectoryForProjectMembership({ cwd: input.cwd, checkout });
4938
+ const timestamp = new Date().toISOString();
4939
+ const projectRecord = await this.resolveProjectRecordForPlacement({
4940
+ membership,
4941
+ timestamp,
4942
+ });
4943
+ const projectId = projectRecord.projectId;
4944
+ const kind = membership.workspaceKind;
4945
+ const displayName = membership.workspaceDisplayName;
4946
+ if (input.workspace.workspaceId === membership.workspaceId &&
4947
+ input.workspace.projectId === projectId &&
4948
+ input.workspace.kind === kind &&
4949
+ input.workspace.displayName === displayName) {
4950
+ return this.ensureWorkspaceRecordUnarchived(input.workspace);
4951
+ }
4952
+ await this.projectRegistry.upsert(projectRecord);
4953
+ const nextWorkspace = {
4954
+ ...input.workspace,
4955
+ workspaceId: membership.workspaceId,
4956
+ projectId,
4957
+ cwd: input.cwd,
4958
+ kind,
4959
+ displayName,
4960
+ archivedAt: null,
4961
+ updatedAt: timestamp,
4962
+ };
4963
+ await this.workspaceRegistry.upsert(nextWorkspace);
4964
+ return nextWorkspace;
4965
+ }
4966
+ async resolveProjectRecordForPlacement(input) {
4967
+ const rootPath = input.membership.projectRootPath;
4968
+ const kind = input.membership.projectKind;
4969
+ const projects = await this.projectRegistry.list();
4970
+ const existingProject = projects.find((project) => !project.archivedAt && project.rootPath === rootPath) ??
4971
+ projects.find((project) => project.rootPath === rootPath) ??
4972
+ null;
4973
+ if (!existingProject) {
4974
+ return createPersistedProjectRecord({
4975
+ projectId: input.membership.projectKey,
4976
+ rootPath,
4977
+ kind,
4978
+ displayName: input.membership.projectName,
4979
+ createdAt: input.timestamp,
4980
+ updatedAt: input.timestamp,
4981
+ });
4982
+ }
4983
+ return {
4984
+ ...existingProject,
4985
+ rootPath,
4986
+ kind,
4987
+ archivedAt: null,
4988
+ updatedAt: input.timestamp,
4989
+ };
4990
+ }
4671
4991
  async ensureWorkspaceRecordUnarchived(workspace) {
4672
4992
  const project = await this.projectRegistry.get(workspace.projectId);
4673
4993
  if (!workspace.archivedAt && (!project || !project.archivedAt)) {
@@ -4749,7 +5069,7 @@ export class Session {
4749
5069
  const result = await service.runOnce();
4750
5070
  const changedWorkspaceIds = new Set();
4751
5071
  const changedProjectIds = new Set();
4752
- for (const change of result.changesApplied) {
5072
+ await Promise.all(result.changesApplied.map(async (change) => {
4753
5073
  switch (change.kind) {
4754
5074
  case "workspace_archived":
4755
5075
  await this.removeWorkspaceGitWatchTarget(change.directory);
@@ -4765,7 +5085,7 @@ export class Session {
4765
5085
  changedProjectIds.add(change.projectId);
4766
5086
  break;
4767
5087
  }
4768
- }
5088
+ }));
4769
5089
  if (changedProjectIds.size > 0) {
4770
5090
  for (const workspace of await this.workspaceRegistry.list()) {
4771
5091
  if (changedProjectIds.has(workspace.projectId)) {
@@ -4844,11 +5164,7 @@ export class Session {
4844
5164
  }
4845
5165
  async handleFetchAgents(request) {
4846
5166
  const requestedSubscriptionId = request.subscribe?.subscriptionId?.trim();
4847
- const subscriptionId = request.subscribe
4848
- ? requestedSubscriptionId && requestedSubscriptionId.length > 0
4849
- ? requestedSubscriptionId
4850
- : uuidv4()
4851
- : null;
5167
+ const subscriptionId = resolveSubscriptionId(request.subscribe, requestedSubscriptionId);
4852
5168
  try {
4853
5169
  if (subscriptionId) {
4854
5170
  this.agentUpdatesSubscription = {
@@ -4924,11 +5240,7 @@ export class Session {
4924
5240
  }
4925
5241
  async handleFetchWorkspacesRequest(request) {
4926
5242
  const requestedSubscriptionId = request.subscribe?.subscriptionId?.trim();
4927
- const subscriptionId = request.subscribe
4928
- ? requestedSubscriptionId && requestedSubscriptionId.length > 0
4929
- ? requestedSubscriptionId
4930
- : uuidv4()
4931
- : null;
5243
+ const subscriptionId = resolveSubscriptionId(request.subscribe, requestedSubscriptionId);
4932
5244
  try {
4933
5245
  this.sessionLogger.debug({
4934
5246
  requestId: request.requestId,
@@ -4997,8 +5309,9 @@ export class Session {
4997
5309
  async handleOpenProjectRequest(request) {
4998
5310
  try {
4999
5311
  const workspace = await this.findOrCreateWorkspaceForDirectory(request.cwd);
5312
+ await this.syncWorkspaceGitObserverForWorkspace(workspace);
5313
+ const descriptor = await this.describeWorkspaceRecord(workspace);
5000
5314
  await this.emitWorkspaceUpdateForCwd(workspace.cwd);
5001
- const descriptor = await this.describeWorkspaceRecordWithGitData(workspace);
5002
5315
  this.emit({
5003
5316
  type: "open_project_response",
5004
5317
  payload: {
@@ -5007,6 +5320,15 @@ export class Session {
5007
5320
  error: null,
5008
5321
  },
5009
5322
  });
5323
+ void this.workspaceGitService
5324
+ .getSnapshot(workspace.cwd, {
5325
+ force: true,
5326
+ includeGitHub: true,
5327
+ reason: "open_project",
5328
+ })
5329
+ .catch((error) => {
5330
+ this.sessionLogger.warn({ err: error, cwd: workspace.cwd }, "Background snapshot refresh failed after open_project");
5331
+ });
5010
5332
  }
5011
5333
  catch (error) {
5012
5334
  const message = error instanceof Error ? error.message : "Failed to open project";
@@ -5274,6 +5596,55 @@ export class Session {
5274
5596
  payload: { requestId, agent, project, error: null },
5275
5597
  });
5276
5598
  }
5599
+ loadProjectedTimelineWindow(params) {
5600
+ const { agentId, direction, cursor, requestedLimit, provider } = params;
5601
+ let timeline = params.timeline;
5602
+ const projectedLimit = Math.max(1, Math.floor(requestedLimit));
5603
+ let fetchLimit = projectedLimit;
5604
+ let projectedWindow = selectTimelineWindowByProjectedLimit({
5605
+ rows: timeline.rows,
5606
+ provider,
5607
+ direction,
5608
+ limit: projectedLimit,
5609
+ collapseToolLifecycle: false,
5610
+ });
5611
+ while (timeline.hasOlder) {
5612
+ const needsMoreProjectedEntries = projectedWindow.projectedEntries.length < projectedLimit;
5613
+ const firstLoadedRow = timeline.rows[0];
5614
+ const firstSelectedRow = projectedWindow.selectedRows[0];
5615
+ const startsAtLoadedBoundary = firstLoadedRow != null &&
5616
+ firstSelectedRow != null &&
5617
+ firstSelectedRow.seq === firstLoadedRow.seq;
5618
+ const boundaryIsAssistantChunk = startsAtLoadedBoundary && firstLoadedRow.item.type === "assistant_message";
5619
+ if (!needsMoreProjectedEntries && !boundaryIsAssistantChunk) {
5620
+ break;
5621
+ }
5622
+ const maxRows = Math.max(0, timeline.window.maxSeq - timeline.window.minSeq + 1);
5623
+ const nextFetchLimit = Math.min(maxRows, fetchLimit * 2);
5624
+ if (nextFetchLimit <= fetchLimit) {
5625
+ break;
5626
+ }
5627
+ fetchLimit = nextFetchLimit;
5628
+ timeline = this.agentManager.fetchTimeline(agentId, {
5629
+ direction,
5630
+ cursor,
5631
+ limit: fetchLimit,
5632
+ });
5633
+ projectedWindow = selectTimelineWindowByProjectedLimit({
5634
+ rows: timeline.rows,
5635
+ provider,
5636
+ direction,
5637
+ limit: projectedLimit,
5638
+ collapseToolLifecycle: false,
5639
+ });
5640
+ }
5641
+ return {
5642
+ timeline,
5643
+ selectedRows: projectedWindow.selectedRows,
5644
+ minSeq: projectedWindow.minSeq,
5645
+ maxSeq: projectedWindow.maxSeq,
5646
+ };
5647
+ }
5277
5648
  async handleFetchAgentTimelineRequest(msg) {
5278
5649
  const direction = msg.direction ?? (msg.cursor ? "after" : "tail");
5279
5650
  const projection = msg.projection ?? "projected";
@@ -5309,51 +5680,20 @@ export class Session {
5309
5680
  let endCursor = null;
5310
5681
  let entries;
5311
5682
  if (shouldLimitByProjectedWindow) {
5312
- const projectedLimit = Math.max(1, Math.floor(requestedLimit));
5313
- let fetchLimit = projectedLimit;
5314
- let projectedWindow = selectTimelineWindowByProjectedLimit({
5315
- rows: timeline.rows,
5316
- provider: snapshot.provider,
5683
+ const projectedResult = this.loadProjectedTimelineWindow({
5684
+ agentId: msg.agentId,
5317
5685
  direction,
5318
- limit: projectedLimit,
5319
- collapseToolLifecycle: false,
5686
+ cursor,
5687
+ requestedLimit,
5688
+ provider: snapshot.provider,
5689
+ timeline,
5320
5690
  });
5321
- while (timeline.hasOlder) {
5322
- const needsMoreProjectedEntries = projectedWindow.projectedEntries.length < projectedLimit;
5323
- const firstLoadedRow = timeline.rows[0];
5324
- const firstSelectedRow = projectedWindow.selectedRows[0];
5325
- const startsAtLoadedBoundary = firstLoadedRow != null &&
5326
- firstSelectedRow != null &&
5327
- firstSelectedRow.seq === firstLoadedRow.seq;
5328
- const boundaryIsAssistantChunk = startsAtLoadedBoundary && firstLoadedRow.item.type === "assistant_message";
5329
- if (!needsMoreProjectedEntries && !boundaryIsAssistantChunk) {
5330
- break;
5331
- }
5332
- const maxRows = Math.max(0, timeline.window.maxSeq - timeline.window.minSeq + 1);
5333
- const nextFetchLimit = Math.min(maxRows, fetchLimit * 2);
5334
- if (nextFetchLimit <= fetchLimit) {
5335
- break;
5336
- }
5337
- fetchLimit = nextFetchLimit;
5338
- timeline = this.agentManager.fetchTimeline(msg.agentId, {
5339
- direction,
5340
- cursor,
5341
- limit: fetchLimit,
5342
- });
5343
- projectedWindow = selectTimelineWindowByProjectedLimit({
5344
- rows: timeline.rows,
5345
- provider: snapshot.provider,
5346
- direction,
5347
- limit: projectedLimit,
5348
- collapseToolLifecycle: false,
5349
- });
5350
- }
5351
- const selectedRows = projectedWindow.selectedRows;
5352
- entries = projectTimelineRows(selectedRows, snapshot.provider, projection);
5353
- if (projectedWindow.minSeq !== null && projectedWindow.maxSeq !== null) {
5354
- startCursor = { epoch: timeline.epoch, seq: projectedWindow.minSeq };
5355
- endCursor = { epoch: timeline.epoch, seq: projectedWindow.maxSeq };
5356
- hasOlder = projectedWindow.minSeq > timeline.window.minSeq;
5691
+ timeline = projectedResult.timeline;
5692
+ entries = projectTimelineRows(projectedResult.selectedRows, snapshot.provider, projection);
5693
+ if (projectedResult.minSeq !== null && projectedResult.maxSeq !== null) {
5694
+ startCursor = { epoch: timeline.epoch, seq: projectedResult.minSeq };
5695
+ endCursor = { epoch: timeline.epoch, seq: projectedResult.maxSeq };
5696
+ hasOlder = projectedResult.minSeq > timeline.window.minSeq;
5357
5697
  hasNewer = false;
5358
5698
  }
5359
5699
  }
@@ -5461,18 +5801,13 @@ export class Session {
5461
5801
  await this.agentManager.waitForAgentRunStart(agentId, { signal: startAbort.signal });
5462
5802
  }
5463
5803
  catch (error) {
5464
- const message = error instanceof Error
5465
- ? error.message
5466
- : typeof error === "string"
5467
- ? error
5468
- : "Unknown error";
5469
5804
  this.emit({
5470
5805
  type: "send_agent_message_response",
5471
5806
  payload: {
5472
5807
  requestId: msg.requestId,
5473
5808
  agentId,
5474
5809
  accepted: false,
5475
- error: message,
5810
+ error: errorToFriendlyMessage(error),
5476
5811
  },
5477
5812
  });
5478
5813
  return;
@@ -5491,18 +5826,13 @@ export class Session {
5491
5826
  });
5492
5827
  }
5493
5828
  catch (error) {
5494
- const message = error instanceof Error
5495
- ? error.message
5496
- : typeof error === "string"
5497
- ? error
5498
- : "Unknown error";
5499
5829
  this.emit({
5500
5830
  type: "send_agent_message_response",
5501
5831
  payload: {
5502
5832
  requestId: msg.requestId,
5503
5833
  agentId: resolved.agentId,
5504
5834
  accepted: false,
5505
- error: message,
5835
+ error: errorToFriendlyMessage(error),
5506
5836
  },
5507
5837
  });
5508
5838
  }
@@ -5540,11 +5870,16 @@ export class Session {
5540
5870
  return;
5541
5871
  }
5542
5872
  const final = this.buildStoredAgentPayload(record);
5543
- const status = record.attentionReason === "permission"
5544
- ? "permission"
5545
- : record.lastStatus === "error"
5546
- ? "error"
5547
- : "idle";
5873
+ let status;
5874
+ if (record.attentionReason === "permission") {
5875
+ status = "permission";
5876
+ }
5877
+ else if (record.lastStatus === "error") {
5878
+ status = "error";
5879
+ }
5880
+ else {
5881
+ status = "idle";
5882
+ }
5548
5883
  const error = resolveWaitForFinishError({ status, final });
5549
5884
  this.emit({
5550
5885
  type: "wait_for_finish_response",
@@ -5568,11 +5903,16 @@ export class Session {
5568
5903
  if (!final) {
5569
5904
  throw new Error(`Agent ${agentId} disappeared while waiting`);
5570
5905
  }
5571
- let status = result.permission
5572
- ? "permission"
5573
- : result.status === "error"
5574
- ? "error"
5575
- : "idle";
5906
+ let status;
5907
+ if (result.permission) {
5908
+ status = "permission";
5909
+ }
5910
+ else if (result.status === "error") {
5911
+ status = "error";
5912
+ }
5913
+ else {
5914
+ status = "idle";
5915
+ }
5576
5916
  const error = resolveWaitForFinishError({ status, final });
5577
5917
  this.emit({
5578
5918
  type: "wait_for_finish_response",
@@ -5583,11 +5923,7 @@ export class Session {
5583
5923
  const isAbort = error instanceof Error &&
5584
5924
  (error.name === "AbortError" || error.message.toLowerCase().includes("aborted"));
5585
5925
  if (!isAbort) {
5586
- const message = error instanceof Error
5587
- ? error.message
5588
- : typeof error === "string"
5589
- ? error
5590
- : "Unknown error";
5926
+ const message = errorToFriendlyMessage(error);
5591
5927
  this.sessionLogger.error({ err: error, agentId }, "wait_for_finish_request failed");
5592
5928
  const final = await this.getAgentPayloadById(agentId);
5593
5929
  this.emit({
@@ -5604,7 +5940,7 @@ export class Session {
5604
5940
  }
5605
5941
  const final = await this.getAgentPayloadById(agentId);
5606
5942
  if (!final) {
5607
- throw new Error(`Agent ${agentId} disappeared while waiting`);
5943
+ throw new Error(`Agent ${agentId} disappeared while waiting`, { cause: error });
5608
5944
  }
5609
5945
  this.emit({
5610
5946
  type: "wait_for_finish_response",
@@ -5620,44 +5956,7 @@ export class Session {
5620
5956
  /**
5621
5957
  * Handle audio chunk for buffering and transcription
5622
5958
  */
5623
- async handleAudioChunk(msg) {
5624
- if (!this.isVoiceMode) {
5625
- this.sessionLogger.warn("Received voice_audio_chunk while voice mode is disabled; transcript will be emitted but voice assistant turn is skipped");
5626
- }
5627
- const chunkFormat = msg.format || "audio/wav";
5628
- if (this.isVoiceMode) {
5629
- if (!this.voiceTurnController) {
5630
- throw new Error("Voice mode is enabled but the voice turn controller is not running");
5631
- }
5632
- const chunkBytes = Buffer.byteLength(msg.audio, "base64");
5633
- this.voiceInputChunkCount += 1;
5634
- this.voiceInputBytes += chunkBytes;
5635
- if (this.voiceInputChunkCount === 1) {
5636
- this.sessionLogger.info({
5637
- format: chunkFormat,
5638
- audioBytes: chunkBytes,
5639
- }, "Received first voice_audio_chunk for active voice mode");
5640
- }
5641
- const now = Date.now();
5642
- if (this.voiceInputChunkCount % 50 === 0 || now - this.voiceInputWindowStartedAt >= 1000) {
5643
- this.sessionLogger.info({
5644
- chunkCount: this.voiceInputChunkCount,
5645
- audioBytes: this.voiceInputBytes,
5646
- windowMs: now - this.voiceInputWindowStartedAt,
5647
- format: chunkFormat,
5648
- }, "Voice input chunk summary");
5649
- this.voiceInputWindowStartedAt = now;
5650
- this.voiceInputChunkCount = 0;
5651
- this.voiceInputBytes = 0;
5652
- }
5653
- await this.voiceTurnController.appendClientChunk({
5654
- audioBase64: msg.audio,
5655
- format: chunkFormat,
5656
- });
5657
- return;
5658
- }
5659
- const chunkBuffer = Buffer.from(msg.audio, "base64");
5660
- const isPCMChunk = chunkFormat.toLowerCase().includes("pcm");
5959
+ async ensureAudioBufferForFormat(chunkFormat, isPCMChunk) {
5661
5960
  if (!this.audioBuffer) {
5662
5961
  this.audioBuffer = {
5663
5962
  chunks: [],
@@ -5665,8 +5964,8 @@ export class Session {
5665
5964
  isPCM: isPCMChunk,
5666
5965
  totalPCMBytes: 0,
5667
5966
  };
5967
+ return this.audioBuffer;
5668
5968
  }
5669
- // If the format changes mid-stream, flush what we have first
5670
5969
  if (this.audioBuffer.isPCM !== isPCMChunk) {
5671
5970
  this.sessionLogger.debug({
5672
5971
  oldFormat: this.audioBuffer.isPCM ? "pcm" : this.audioBuffer.format,
@@ -5682,19 +5981,61 @@ export class Session {
5682
5981
  isPCM: isPCMChunk,
5683
5982
  totalPCMBytes: 0,
5684
5983
  };
5984
+ return this.audioBuffer;
5685
5985
  }
5686
- else if (!this.audioBuffer.isPCM) {
5687
- // Keep latest format info for non-PCM blobs
5986
+ if (!this.audioBuffer.isPCM) {
5688
5987
  this.audioBuffer.format = chunkFormat;
5689
5988
  }
5690
- this.audioBuffer.chunks.push(chunkBuffer);
5691
- if (this.audioBuffer.isPCM) {
5692
- this.audioBuffer.totalPCMBytes += chunkBuffer.length;
5989
+ return this.audioBuffer;
5990
+ }
5991
+ async forwardAudioChunkToVoiceTurn(msg, chunkFormat) {
5992
+ if (!this.voiceTurnController) {
5993
+ throw new Error("Voice mode is enabled but the voice turn controller is not running");
5994
+ }
5995
+ const chunkBytes = Buffer.byteLength(msg.audio, "base64");
5996
+ this.voiceInputChunkCount += 1;
5997
+ this.voiceInputBytes += chunkBytes;
5998
+ if (this.voiceInputChunkCount === 1) {
5999
+ this.sessionLogger.info({
6000
+ format: chunkFormat,
6001
+ audioBytes: chunkBytes,
6002
+ }, "Received first voice_audio_chunk for active voice mode");
6003
+ }
6004
+ const now = Date.now();
6005
+ if (this.voiceInputChunkCount % 50 === 0 || now - this.voiceInputWindowStartedAt >= 1000) {
6006
+ this.sessionLogger.info({
6007
+ chunkCount: this.voiceInputChunkCount,
6008
+ audioBytes: this.voiceInputBytes,
6009
+ windowMs: now - this.voiceInputWindowStartedAt,
6010
+ format: chunkFormat,
6011
+ }, "Voice input chunk summary");
6012
+ this.voiceInputWindowStartedAt = now;
6013
+ this.voiceInputChunkCount = 0;
6014
+ this.voiceInputBytes = 0;
6015
+ }
6016
+ await this.voiceTurnController.appendClientChunk({
6017
+ audioBase64: msg.audio,
6018
+ format: chunkFormat,
6019
+ });
6020
+ }
6021
+ async handleAudioChunk(msg) {
6022
+ if (!this.isVoiceMode) {
6023
+ this.sessionLogger.warn("Received voice_audio_chunk while voice mode is disabled; transcript will be emitted but voice assistant turn is skipped");
6024
+ }
6025
+ const chunkFormat = msg.format || "audio/wav";
6026
+ if (this.isVoiceMode) {
6027
+ await this.forwardAudioChunkToVoiceTurn(msg, chunkFormat);
6028
+ return;
6029
+ }
6030
+ const chunkBuffer = Buffer.from(msg.audio, "base64");
6031
+ const isPCMChunk = chunkFormat.toLowerCase().includes("pcm");
6032
+ const buffer = await this.ensureAudioBufferForFormat(chunkFormat, isPCMChunk);
6033
+ buffer.chunks.push(chunkBuffer);
6034
+ if (buffer.isPCM) {
6035
+ buffer.totalPCMBytes += chunkBuffer.length;
5693
6036
  }
5694
6037
  // In non-voice mode, use streaming threshold to process chunks
5695
- const reachedStreamingThreshold = !this.isVoiceMode &&
5696
- this.audioBuffer.isPCM &&
5697
- this.audioBuffer.totalPCMBytes >= MIN_STREAMING_SEGMENT_BYTES;
6038
+ const reachedStreamingThreshold = !this.isVoiceMode && buffer.isPCM && buffer.totalPCMBytes >= MIN_STREAMING_SEGMENT_BYTES;
5698
6039
  if (!msg.isLast && reachedStreamingThreshold) {
5699
6040
  return;
5700
6041
  }
@@ -6261,7 +6602,7 @@ export class Session {
6261
6602
  async handleChatPostRequest(request) {
6262
6603
  try {
6263
6604
  const authorAgentId = request.authorAgentId?.trim() || this.clientId;
6264
- const message = await this.chatService.postMessage({
6605
+ const message = await this.chatService.dispatchMessage({
6265
6606
  room: request.room,
6266
6607
  authorAgentId,
6267
6608
  body: request.body,
@@ -6611,11 +6952,7 @@ export class Session {
6611
6952
  }
6612
6953
  this.emitTerminalsChangedSnapshot({
6613
6954
  cwd: event.cwd,
6614
- terminals: this.filterStandaloneTerminals(event.terminals).map((terminal) => ({
6615
- id: terminal.id,
6616
- name: terminal.name,
6617
- ...(terminal.title ? { title: terminal.title } : {}),
6618
- })),
6955
+ terminals: this.filterStandaloneTerminals(event.terminals).map((terminal) => Object.assign({ id: terminal.id, name: terminal.name }, terminal.title ? { title: terminal.title } : {})),
6619
6956
  });
6620
6957
  }
6621
6958
  handleSubscribeTerminalsRequest(msg) {
@@ -7037,7 +7374,7 @@ export class Session {
7037
7374
  return true;
7038
7375
  }
7039
7376
  disposeTerminalSubscriptions() {
7040
- for (const terminalId of [...this.terminalIdToSlot.keys()]) {
7377
+ for (const terminalId of Array.from(this.terminalIdToSlot.keys())) {
7041
7378
  this.detachTerminalStream(terminalId, { emitExit: false });
7042
7379
  }
7043
7380
  }