@getpaseo/server 0.1.30 → 0.1.32

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 (498) hide show
  1. package/dist/scripts/daemon-runner.js +1 -1
  2. package/dist/scripts/daemon-runner.js.map +1 -1
  3. package/dist/scripts/dev-runner.js +1 -1
  4. package/dist/scripts/dev-runner.js.map +1 -1
  5. package/dist/server/client/daemon-client-relay-e2ee-transport.d.ts.map +1 -1
  6. package/dist/server/client/daemon-client-relay-e2ee-transport.js.map +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.map +1 -1
  9. package/dist/server/client/daemon-client.d.ts +108 -103
  10. package/dist/server/client/daemon-client.d.ts.map +1 -1
  11. package/dist/server/client/daemon-client.js +417 -407
  12. package/dist/server/client/daemon-client.js.map +1 -1
  13. package/dist/server/server/agent/activity-curator.d.ts.map +1 -1
  14. package/dist/server/server/agent/activity-curator.js +5 -4
  15. package/dist/server/server/agent/activity-curator.js.map +1 -1
  16. package/dist/server/server/agent/agent-management-mcp.d.ts.map +1 -1
  17. package/dist/server/server/agent/agent-management-mcp.js +13 -17
  18. package/dist/server/server/agent/agent-management-mcp.js.map +1 -1
  19. package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
  20. package/dist/server/server/agent/agent-manager.js +26 -26
  21. package/dist/server/server/agent/agent-manager.js.map +1 -1
  22. package/dist/server/server/agent/agent-metadata-generator.d.ts.map +1 -1
  23. package/dist/server/server/agent/agent-metadata-generator.js +1 -3
  24. package/dist/server/server/agent/agent-metadata-generator.js.map +1 -1
  25. package/dist/server/server/agent/agent-projections.d.ts.map +1 -1
  26. package/dist/server/server/agent/agent-projections.js +4 -12
  27. package/dist/server/server/agent/agent-projections.js.map +1 -1
  28. package/dist/server/server/agent/agent-response-loop.d.ts.map +1 -1
  29. package/dist/server/server/agent/agent-response-loop.js +6 -6
  30. package/dist/server/server/agent/agent-response-loop.js.map +1 -1
  31. package/dist/server/server/agent/agent-sdk-types.d.ts +23 -0
  32. package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
  33. package/dist/server/server/agent/agent-sdk-types.js.map +1 -1
  34. package/dist/server/server/agent/agent-storage.d.ts.map +1 -1
  35. package/dist/server/server/agent/agent-storage.js +2 -4
  36. package/dist/server/server/agent/agent-storage.js.map +1 -1
  37. package/dist/server/server/agent/dictation-debug.d.ts.map +1 -1
  38. package/dist/server/server/agent/dictation-debug.js.map +1 -1
  39. package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
  40. package/dist/server/server/agent/mcp-server.js +19 -27
  41. package/dist/server/server/agent/mcp-server.js.map +1 -1
  42. package/dist/server/server/agent/pcm16-resampler.d.ts.map +1 -1
  43. package/dist/server/server/agent/pcm16-resampler.js.map +1 -1
  44. package/dist/server/server/agent/provider-launch-config.d.ts.map +1 -1
  45. package/dist/server/server/agent/provider-launch-config.js +4 -2
  46. package/dist/server/server/agent/provider-launch-config.js.map +1 -1
  47. package/dist/server/server/agent/provider-manifest.d.ts +2 -2
  48. package/dist/server/server/agent/provider-manifest.d.ts.map +1 -1
  49. package/dist/server/server/agent/provider-manifest.js +63 -9
  50. package/dist/server/server/agent/provider-manifest.js.map +1 -1
  51. package/dist/server/server/agent/provider-registry.d.ts +2 -2
  52. package/dist/server/server/agent/provider-registry.d.ts.map +1 -1
  53. package/dist/server/server/agent/provider-registry.js +1 -1
  54. package/dist/server/server/agent/provider-registry.js.map +1 -1
  55. package/dist/server/server/agent/providers/claude/model-catalog.js +10 -10
  56. package/dist/server/server/agent/providers/claude/model-catalog.js.map +1 -1
  57. package/dist/server/server/agent/providers/claude/partial-json.d.ts.map +1 -1
  58. package/dist/server/server/agent/providers/claude/partial-json.js +4 -4
  59. package/dist/server/server/agent/providers/claude/partial-json.js.map +1 -1
  60. package/dist/server/server/agent/providers/claude/sidechain-tracker.d.ts +20 -0
  61. package/dist/server/server/agent/providers/claude/sidechain-tracker.d.ts.map +1 -0
  62. package/dist/server/server/agent/providers/claude/sidechain-tracker.js +230 -0
  63. package/dist/server/server/agent/providers/claude/sidechain-tracker.js.map +1 -0
  64. package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +11 -0
  65. package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts.map +1 -1
  66. package/dist/server/server/agent/providers/claude/task-notification-tool-call.js +37 -20
  67. package/dist/server/server/agent/providers/claude/task-notification-tool-call.js.map +1 -1
  68. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts.map +1 -1
  69. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js +21 -11
  70. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js.map +1 -1
  71. package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts.map +1 -1
  72. package/dist/server/server/agent/providers/claude/tool-call-mapper.js +23 -11
  73. package/dist/server/server/agent/providers/claude/tool-call-mapper.js.map +1 -1
  74. package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
  75. package/dist/server/server/agent/providers/claude-agent.js +486 -1134
  76. package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
  77. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.d.ts.map +1 -1
  78. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.js +2 -2
  79. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.js.map +1 -1
  80. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts.map +1 -1
  81. package/dist/server/server/agent/providers/codex/tool-call-mapper.js +14 -11
  82. package/dist/server/server/agent/providers/codex/tool-call-mapper.js.map +1 -1
  83. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
  84. package/dist/server/server/agent/providers/codex-app-server-agent.js +347 -163
  85. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
  86. package/dist/server/server/agent/providers/codex-rollout-timeline.d.ts.map +1 -1
  87. package/dist/server/server/agent/providers/codex-rollout-timeline.js +21 -32
  88. package/dist/server/server/agent/providers/codex-rollout-timeline.js.map +1 -1
  89. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts.map +1 -1
  90. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js +2 -2
  91. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js.map +1 -1
  92. package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts.map +1 -1
  93. package/dist/server/server/agent/providers/opencode/tool-call-mapper.js +2 -9
  94. package/dist/server/server/agent/providers/opencode/tool-call-mapper.js.map +1 -1
  95. package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
  96. package/dist/server/server/agent/providers/opencode-agent.js +5 -5
  97. package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
  98. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +277 -1
  99. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
  100. package/dist/server/server/agent/providers/tool-call-detail-primitives.js +149 -15
  101. package/dist/server/server/agent/providers/tool-call-detail-primitives.js.map +1 -1
  102. package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts.map +1 -1
  103. package/dist/server/server/agent/providers/tool-call-mapper-utils.js +1 -3
  104. package/dist/server/server/agent/providers/tool-call-mapper-utils.js.map +1 -1
  105. package/dist/server/server/agent/stt-manager.d.ts.map +1 -1
  106. package/dist/server/server/agent/stt-manager.js +1 -2
  107. package/dist/server/server/agent/stt-manager.js.map +1 -1
  108. package/dist/server/server/agent/system-prompt.js +5 -5
  109. package/dist/server/server/agent/timeline-projection.d.ts.map +1 -1
  110. package/dist/server/server/agent/timeline-projection.js.map +1 -1
  111. package/dist/server/server/agent/tts-manager.d.ts.map +1 -1
  112. package/dist/server/server/agent/tts-manager.js +27 -9
  113. package/dist/server/server/agent/tts-manager.js.map +1 -1
  114. package/dist/server/server/agent/wait-for-agent-tracker.d.ts.map +1 -1
  115. package/dist/server/server/agent/wait-for-agent-tracker.js.map +1 -1
  116. package/dist/server/server/agent-attention-policy.d.ts.map +1 -1
  117. package/dist/server/server/agent-attention-policy.js.map +1 -1
  118. package/dist/server/server/allowed-hosts.d.ts.map +1 -1
  119. package/dist/server/server/allowed-hosts.js.map +1 -1
  120. package/dist/server/server/bootstrap.d.ts.map +1 -1
  121. package/dist/server/server/bootstrap.js +46 -5
  122. package/dist/server/server/bootstrap.js.map +1 -1
  123. package/dist/server/server/config.d.ts.map +1 -1
  124. package/dist/server/server/config.js +4 -11
  125. package/dist/server/server/config.js.map +1 -1
  126. package/dist/server/server/connection-offer.d.ts +1 -1
  127. package/dist/server/server/connection-offer.d.ts.map +1 -1
  128. package/dist/server/server/connection-offer.js +2 -3
  129. package/dist/server/server/connection-offer.js.map +1 -1
  130. package/dist/server/server/daemon-version.d.ts.map +1 -1
  131. package/dist/server/server/daemon-version.js +1 -1
  132. package/dist/server/server/daemon-version.js.map +1 -1
  133. package/dist/server/server/dictation/dictation-stream-manager.d.ts.map +1 -1
  134. package/dist/server/server/dictation/dictation-stream-manager.js +4 -1
  135. package/dist/server/server/dictation/dictation-stream-manager.js.map +1 -1
  136. package/dist/server/server/exports.d.ts +1 -1
  137. package/dist/server/server/exports.d.ts.map +1 -1
  138. package/dist/server/server/exports.js +1 -1
  139. package/dist/server/server/exports.js.map +1 -1
  140. package/dist/server/server/file-explorer/service.d.ts +1 -1
  141. package/dist/server/server/file-explorer/service.d.ts.map +1 -1
  142. package/dist/server/server/file-explorer/service.js +5 -8
  143. package/dist/server/server/file-explorer/service.js.map +1 -1
  144. package/dist/server/server/index.js +1 -1
  145. package/dist/server/server/index.js.map +1 -1
  146. package/dist/server/server/logger.d.ts.map +1 -1
  147. package/dist/server/server/logger.js.map +1 -1
  148. package/dist/server/server/messages.d.ts.map +1 -1
  149. package/dist/server/server/messages.js.map +1 -1
  150. package/dist/server/server/package-version.d.ts.map +1 -1
  151. package/dist/server/server/package-version.js +1 -2
  152. package/dist/server/server/package-version.js.map +1 -1
  153. package/dist/server/server/persisted-config.d.ts.map +1 -1
  154. package/dist/server/server/persisted-config.js.map +1 -1
  155. package/dist/server/server/persistence-hooks.d.ts.map +1 -1
  156. package/dist/server/server/persistence-hooks.js.map +1 -1
  157. package/dist/server/server/pid-lock.d.ts.map +1 -1
  158. package/dist/server/server/pid-lock.js.map +1 -1
  159. package/dist/server/server/push/push-service.d.ts.map +1 -1
  160. package/dist/server/server/push/push-service.js.map +1 -1
  161. package/dist/server/server/relay-transport.d.ts.map +1 -1
  162. package/dist/server/server/relay-transport.js +6 -2
  163. package/dist/server/server/relay-transport.js.map +1 -1
  164. package/dist/server/server/session.d.ts +49 -37
  165. package/dist/server/server/session.d.ts.map +1 -1
  166. package/dist/server/server/session.js +1234 -998
  167. package/dist/server/server/session.js.map +1 -1
  168. package/dist/server/server/speech/audio.d.ts.map +1 -1
  169. package/dist/server/server/speech/audio.js.map +1 -1
  170. package/dist/server/server/speech/providers/local/config.d.ts.map +1 -1
  171. package/dist/server/server/speech/providers/local/config.js +5 -14
  172. package/dist/server/server/speech/providers/local/config.js.map +1 -1
  173. package/dist/server/server/speech/providers/local/models.d.ts.map +1 -1
  174. package/dist/server/server/speech/providers/local/models.js +1 -1
  175. package/dist/server/server/speech/providers/local/models.js.map +1 -1
  176. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.d.ts.map +1 -1
  177. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.js +21 -7
  178. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.js.map +1 -1
  179. package/dist/server/server/speech/providers/local/runtime.d.ts.map +1 -1
  180. package/dist/server/server/speech/providers/local/runtime.js +1 -23
  181. package/dist/server/server/speech/providers/local/runtime.js.map +1 -1
  182. package/dist/server/server/speech/providers/local/sherpa/model-catalog.d.ts.map +1 -1
  183. package/dist/server/server/speech/providers/local/sherpa/model-catalog.js.map +1 -1
  184. package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts.map +1 -1
  185. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js.map +1 -1
  186. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.d.ts.map +1 -1
  187. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js +9 -4
  188. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js.map +1 -1
  189. package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.d.ts.map +1 -1
  190. package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.js +7 -2
  191. package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.js.map +1 -1
  192. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.d.ts.map +1 -1
  193. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js +5 -1
  194. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js.map +1 -1
  195. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.d.ts.map +1 -1
  196. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js +2 -4
  197. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js.map +1 -1
  198. package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.d.ts.map +1 -1
  199. package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.js +1 -3
  200. package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.js.map +1 -1
  201. package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.d.ts.map +1 -1
  202. package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.js +2 -4
  203. package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.js.map +1 -1
  204. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.d.ts.map +1 -1
  205. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.js +4 -1
  206. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.js.map +1 -1
  207. package/dist/server/server/speech/providers/local/sherpa/silero-vad-provider.d.ts.map +1 -1
  208. package/dist/server/server/speech/providers/local/sherpa/silero-vad-provider.js +1 -1
  209. package/dist/server/server/speech/providers/local/sherpa/silero-vad-provider.js.map +1 -1
  210. package/dist/server/server/speech/providers/local/sherpa/silero-vad-session.d.ts.map +1 -1
  211. package/dist/server/server/speech/providers/local/sherpa/silero-vad-session.js.map +1 -1
  212. package/dist/server/server/speech/providers/openai/config.d.ts.map +1 -1
  213. package/dist/server/server/speech/providers/openai/config.js +5 -24
  214. package/dist/server/server/speech/providers/openai/config.js.map +1 -1
  215. package/dist/server/server/speech/providers/openai/realtime-transcription-session.d.ts.map +1 -1
  216. package/dist/server/server/speech/providers/openai/realtime-transcription-session.js +6 -3
  217. package/dist/server/server/speech/providers/openai/realtime-transcription-session.js.map +1 -1
  218. package/dist/server/server/speech/providers/openai/runtime.d.ts.map +1 -1
  219. package/dist/server/server/speech/providers/openai/runtime.js +2 -6
  220. package/dist/server/server/speech/providers/openai/runtime.js.map +1 -1
  221. package/dist/server/server/speech/providers/openai/stt.d.ts.map +1 -1
  222. package/dist/server/server/speech/providers/openai/stt.js +1 -3
  223. package/dist/server/server/speech/providers/openai/stt.js.map +1 -1
  224. package/dist/server/server/speech/providers/openai/tts.d.ts.map +1 -1
  225. package/dist/server/server/speech/providers/openai/tts.js.map +1 -1
  226. package/dist/server/server/speech/speech-config-resolver.d.ts.map +1 -1
  227. package/dist/server/server/speech/speech-config-resolver.js +3 -7
  228. package/dist/server/server/speech/speech-config-resolver.js.map +1 -1
  229. package/dist/server/server/speech/speech-provider.d.ts.map +1 -1
  230. package/dist/server/server/speech/speech-runtime.d.ts.map +1 -1
  231. package/dist/server/server/speech/speech-runtime.js.map +1 -1
  232. package/dist/server/server/speech/turn-detection-provider.d.ts.map +1 -1
  233. package/dist/server/server/terminal-mcp/server.d.ts.map +1 -1
  234. package/dist/server/server/terminal-mcp/server.js +3 -11
  235. package/dist/server/server/terminal-mcp/server.js.map +1 -1
  236. package/dist/server/server/terminal-mcp/terminal-manager.d.ts.map +1 -1
  237. package/dist/server/server/terminal-mcp/terminal-manager.js +2 -14
  238. package/dist/server/server/terminal-mcp/terminal-manager.js.map +1 -1
  239. package/dist/server/server/terminal-mcp/tmux.d.ts +1 -1
  240. package/dist/server/server/terminal-mcp/tmux.d.ts.map +1 -1
  241. package/dist/server/server/terminal-mcp/tmux.js +20 -123
  242. package/dist/server/server/terminal-mcp/tmux.js.map +1 -1
  243. package/dist/server/server/utils/diff-highlighter.d.ts +11 -3
  244. package/dist/server/server/utils/diff-highlighter.d.ts.map +1 -1
  245. package/dist/server/server/utils/diff-highlighter.js +49 -36
  246. package/dist/server/server/utils/diff-highlighter.js.map +1 -1
  247. package/dist/server/server/voice/fixed-duration-pcm-ring-buffer.d.ts.map +1 -1
  248. package/dist/server/server/voice/fixed-duration-pcm-ring-buffer.js.map +1 -1
  249. package/dist/server/server/voice/voice-turn-controller.d.ts.map +1 -1
  250. package/dist/server/server/voice/voice-turn-controller.js +1 -3
  251. package/dist/server/server/voice/voice-turn-controller.js.map +1 -1
  252. package/dist/server/server/voice-config.d.ts.map +1 -1
  253. package/dist/server/server/voice-config.js.map +1 -1
  254. package/dist/server/server/voice-mcp-bridge.d.ts.map +1 -1
  255. package/dist/server/server/voice-mcp-bridge.js.map +1 -1
  256. package/dist/server/server/websocket-server.d.ts +1 -0
  257. package/dist/server/server/websocket-server.d.ts.map +1 -1
  258. package/dist/server/server/websocket-server.js +20 -22
  259. package/dist/server/server/websocket-server.js.map +1 -1
  260. package/dist/server/server/workspace-registry-bootstrap.d.ts +3 -3
  261. package/dist/server/server/workspace-registry-bootstrap.d.ts.map +1 -1
  262. package/dist/server/server/workspace-registry-bootstrap.js +6 -6
  263. package/dist/server/server/workspace-registry-bootstrap.js.map +1 -1
  264. package/dist/server/server/workspace-registry-model.d.ts +14 -3
  265. package/dist/server/server/workspace-registry-model.d.ts.map +1 -1
  266. package/dist/server/server/workspace-registry-model.js +40 -15
  267. package/dist/server/server/workspace-registry-model.js.map +1 -1
  268. package/dist/server/server/workspace-registry.d.ts +5 -5
  269. package/dist/server/server/workspace-registry.d.ts.map +1 -1
  270. package/dist/server/server/workspace-registry.js +16 -13
  271. package/dist/server/server/workspace-registry.js.map +1 -1
  272. package/dist/server/server/worktree-bootstrap.d.ts.map +1 -1
  273. package/dist/server/server/worktree-bootstrap.js +17 -6
  274. package/dist/server/server/worktree-bootstrap.js.map +1 -1
  275. package/dist/server/shared/agent-attention-notification.d.ts.map +1 -1
  276. package/dist/server/shared/agent-attention-notification.js.map +1 -1
  277. package/dist/server/shared/agent-lifecycle.d.ts.map +1 -1
  278. package/dist/server/shared/daemon-endpoints.d.ts +1 -0
  279. package/dist/server/shared/daemon-endpoints.d.ts.map +1 -1
  280. package/dist/server/shared/daemon-endpoints.js +11 -2
  281. package/dist/server/shared/daemon-endpoints.js.map +1 -1
  282. package/dist/server/shared/messages.d.ts +1228 -2982
  283. package/dist/server/shared/messages.d.ts.map +1 -1
  284. package/dist/server/shared/messages.js +330 -302
  285. package/dist/server/shared/messages.js.map +1 -1
  286. package/dist/server/shared/terminal-stream-protocol.d.ts +36 -0
  287. package/dist/server/shared/terminal-stream-protocol.d.ts.map +1 -0
  288. package/dist/server/shared/terminal-stream-protocol.js +99 -0
  289. package/dist/server/shared/terminal-stream-protocol.js.map +1 -0
  290. package/dist/server/shared/tool-call-display.d.ts.map +1 -1
  291. package/dist/server/shared/tool-call-display.js +6 -3
  292. package/dist/server/shared/tool-call-display.js.map +1 -1
  293. package/dist/server/terminal/terminal.d.ts +5 -48
  294. package/dist/server/terminal/terminal.d.ts.map +1 -1
  295. package/dist/server/terminal/terminal.js +44 -98
  296. package/dist/server/terminal/terminal.js.map +1 -1
  297. package/dist/server/utils/checkout-git.d.ts +1 -0
  298. package/dist/server/utils/checkout-git.d.ts.map +1 -1
  299. package/dist/server/utils/checkout-git.js +111 -120
  300. package/dist/server/utils/checkout-git.js.map +1 -1
  301. package/dist/server/utils/directory-suggestions.d.ts +1 -1
  302. package/dist/server/utils/directory-suggestions.d.ts.map +1 -1
  303. package/dist/server/utils/directory-suggestions.js +40 -40
  304. package/dist/server/utils/directory-suggestions.js.map +1 -1
  305. package/dist/server/utils/project-icon.d.ts.map +1 -1
  306. package/dist/server/utils/project-icon.js +2 -11
  307. package/dist/server/utils/project-icon.js.map +1 -1
  308. package/dist/server/utils/worktree.d.ts +2 -0
  309. package/dist/server/utils/worktree.d.ts.map +1 -1
  310. package/dist/server/utils/worktree.js +22 -19
  311. package/dist/server/utils/worktree.js.map +1 -1
  312. package/dist/src/server/agent/activity-curator.js +5 -4
  313. package/dist/src/server/agent/activity-curator.js.map +1 -1
  314. package/dist/src/server/agent/agent-manager.js +26 -26
  315. package/dist/src/server/agent/agent-manager.js.map +1 -1
  316. package/dist/src/server/agent/agent-metadata-generator.js +1 -3
  317. package/dist/src/server/agent/agent-metadata-generator.js.map +1 -1
  318. package/dist/src/server/agent/agent-projections.js +4 -12
  319. package/dist/src/server/agent/agent-projections.js.map +1 -1
  320. package/dist/src/server/agent/agent-response-loop.js +6 -6
  321. package/dist/src/server/agent/agent-response-loop.js.map +1 -1
  322. package/dist/src/server/agent/agent-sdk-types.js.map +1 -1
  323. package/dist/src/server/agent/agent-storage.js +2 -4
  324. package/dist/src/server/agent/agent-storage.js.map +1 -1
  325. package/dist/src/server/agent/dictation-debug.js.map +1 -1
  326. package/dist/src/server/agent/mcp-server.js +19 -27
  327. package/dist/src/server/agent/mcp-server.js.map +1 -1
  328. package/dist/src/server/agent/pcm16-resampler.js.map +1 -1
  329. package/dist/src/server/agent/provider-launch-config.js +4 -2
  330. package/dist/src/server/agent/provider-launch-config.js.map +1 -1
  331. package/dist/src/server/agent/provider-manifest.js +63 -9
  332. package/dist/src/server/agent/provider-manifest.js.map +1 -1
  333. package/dist/src/server/agent/provider-registry.js +1 -1
  334. package/dist/src/server/agent/provider-registry.js.map +1 -1
  335. package/dist/src/server/agent/providers/claude/model-catalog.js +10 -10
  336. package/dist/src/server/agent/providers/claude/model-catalog.js.map +1 -1
  337. package/dist/src/server/agent/providers/claude/partial-json.js +4 -4
  338. package/dist/src/server/agent/providers/claude/partial-json.js.map +1 -1
  339. package/dist/src/server/agent/providers/claude/sidechain-tracker.js +230 -0
  340. package/dist/src/server/agent/providers/claude/sidechain-tracker.js.map +1 -0
  341. package/dist/src/server/agent/providers/claude/task-notification-tool-call.js +37 -20
  342. package/dist/src/server/agent/providers/claude/task-notification-tool-call.js.map +1 -1
  343. package/dist/src/server/agent/providers/claude/tool-call-detail-parser.js +21 -11
  344. package/dist/src/server/agent/providers/claude/tool-call-detail-parser.js.map +1 -1
  345. package/dist/src/server/agent/providers/claude/tool-call-mapper.js +23 -11
  346. package/dist/src/server/agent/providers/claude/tool-call-mapper.js.map +1 -1
  347. package/dist/src/server/agent/providers/claude-agent.js +486 -1134
  348. package/dist/src/server/agent/providers/claude-agent.js.map +1 -1
  349. package/dist/src/server/agent/providers/codex/tool-call-detail-parser.js +2 -2
  350. package/dist/src/server/agent/providers/codex/tool-call-detail-parser.js.map +1 -1
  351. package/dist/src/server/agent/providers/codex/tool-call-mapper.js +14 -11
  352. package/dist/src/server/agent/providers/codex/tool-call-mapper.js.map +1 -1
  353. package/dist/src/server/agent/providers/codex-app-server-agent.js +347 -163
  354. package/dist/src/server/agent/providers/codex-app-server-agent.js.map +1 -1
  355. package/dist/src/server/agent/providers/codex-rollout-timeline.js +21 -32
  356. package/dist/src/server/agent/providers/codex-rollout-timeline.js.map +1 -1
  357. package/dist/src/server/agent/providers/opencode/tool-call-detail-parser.js +2 -2
  358. package/dist/src/server/agent/providers/opencode/tool-call-detail-parser.js.map +1 -1
  359. package/dist/src/server/agent/providers/opencode/tool-call-mapper.js +2 -9
  360. package/dist/src/server/agent/providers/opencode/tool-call-mapper.js.map +1 -1
  361. package/dist/src/server/agent/providers/opencode-agent.js +5 -5
  362. package/dist/src/server/agent/providers/opencode-agent.js.map +1 -1
  363. package/dist/src/server/agent/providers/tool-call-detail-primitives.js +149 -15
  364. package/dist/src/server/agent/providers/tool-call-detail-primitives.js.map +1 -1
  365. package/dist/src/server/agent/providers/tool-call-mapper-utils.js +1 -3
  366. package/dist/src/server/agent/providers/tool-call-mapper-utils.js.map +1 -1
  367. package/dist/src/server/agent/stt-manager.js +1 -2
  368. package/dist/src/server/agent/stt-manager.js.map +1 -1
  369. package/dist/src/server/agent/timeline-projection.js.map +1 -1
  370. package/dist/src/server/agent/tts-manager.js +27 -9
  371. package/dist/src/server/agent/tts-manager.js.map +1 -1
  372. package/dist/src/server/agent/wait-for-agent-tracker.js.map +1 -1
  373. package/dist/src/server/agent-attention-policy.js.map +1 -1
  374. package/dist/src/server/allowed-hosts.js.map +1 -1
  375. package/dist/src/server/bootstrap.js +46 -5
  376. package/dist/src/server/bootstrap.js.map +1 -1
  377. package/dist/src/server/config.js +4 -11
  378. package/dist/src/server/config.js.map +1 -1
  379. package/dist/src/server/connection-offer.js +2 -3
  380. package/dist/src/server/connection-offer.js.map +1 -1
  381. package/dist/src/server/daemon-version.js +1 -1
  382. package/dist/src/server/daemon-version.js.map +1 -1
  383. package/dist/src/server/dictation/dictation-stream-manager.js +4 -1
  384. package/dist/src/server/dictation/dictation-stream-manager.js.map +1 -1
  385. package/dist/src/server/file-explorer/service.js +5 -8
  386. package/dist/src/server/file-explorer/service.js.map +1 -1
  387. package/dist/src/server/messages.js.map +1 -1
  388. package/dist/src/server/package-version.js +1 -2
  389. package/dist/src/server/package-version.js.map +1 -1
  390. package/dist/src/server/pairing-offer.js +45 -0
  391. package/dist/src/server/pairing-offer.js.map +1 -0
  392. package/dist/src/server/pairing-qr.js +45 -0
  393. package/dist/src/server/pairing-qr.js.map +1 -0
  394. package/dist/src/server/persisted-config.js.map +1 -1
  395. package/dist/src/server/persistence-hooks.js.map +1 -1
  396. package/dist/src/server/pid-lock.js.map +1 -1
  397. package/dist/src/server/push/push-service.js.map +1 -1
  398. package/dist/src/server/relay-transport.js +6 -2
  399. package/dist/src/server/relay-transport.js.map +1 -1
  400. package/dist/src/server/session.js +1234 -998
  401. package/dist/src/server/session.js.map +1 -1
  402. package/dist/src/server/speech/audio.js.map +1 -1
  403. package/dist/src/server/speech/providers/local/config.js +5 -14
  404. package/dist/src/server/speech/providers/local/config.js.map +1 -1
  405. package/dist/src/server/speech/providers/local/models.js +1 -1
  406. package/dist/src/server/speech/providers/local/models.js.map +1 -1
  407. package/dist/src/server/speech/providers/local/pocket/pocket-tts-onnx.js +21 -7
  408. package/dist/src/server/speech/providers/local/pocket/pocket-tts-onnx.js.map +1 -1
  409. package/dist/src/server/speech/providers/local/runtime.js +1 -23
  410. package/dist/src/server/speech/providers/local/runtime.js.map +1 -1
  411. package/dist/src/server/speech/providers/local/sherpa/model-catalog.js.map +1 -1
  412. package/dist/src/server/speech/providers/local/sherpa/model-downloader.js.map +1 -1
  413. package/dist/src/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js +9 -4
  414. package/dist/src/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js.map +1 -1
  415. package/dist/src/server/speech/providers/local/sherpa/sherpa-online-recognizer.js +7 -2
  416. package/dist/src/server/speech/providers/local/sherpa/sherpa-online-recognizer.js.map +1 -1
  417. package/dist/src/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js +5 -1
  418. package/dist/src/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js.map +1 -1
  419. package/dist/src/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js +2 -4
  420. package/dist/src/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js.map +1 -1
  421. package/dist/src/server/speech/providers/local/sherpa/sherpa-realtime-session.js +1 -3
  422. package/dist/src/server/speech/providers/local/sherpa/sherpa-realtime-session.js.map +1 -1
  423. package/dist/src/server/speech/providers/local/sherpa/sherpa-stt.js +2 -4
  424. package/dist/src/server/speech/providers/local/sherpa/sherpa-stt.js.map +1 -1
  425. package/dist/src/server/speech/providers/local/sherpa/sherpa-tts.js +4 -1
  426. package/dist/src/server/speech/providers/local/sherpa/sherpa-tts.js.map +1 -1
  427. package/dist/src/server/speech/providers/local/sherpa/silero-vad-provider.js +1 -1
  428. package/dist/src/server/speech/providers/local/sherpa/silero-vad-provider.js.map +1 -1
  429. package/dist/src/server/speech/providers/local/sherpa/silero-vad-session.js.map +1 -1
  430. package/dist/src/server/speech/providers/openai/config.js +5 -24
  431. package/dist/src/server/speech/providers/openai/config.js.map +1 -1
  432. package/dist/src/server/speech/providers/openai/realtime-transcription-session.js +6 -3
  433. package/dist/src/server/speech/providers/openai/realtime-transcription-session.js.map +1 -1
  434. package/dist/src/server/speech/providers/openai/runtime.js +2 -6
  435. package/dist/src/server/speech/providers/openai/runtime.js.map +1 -1
  436. package/dist/src/server/speech/providers/openai/stt.js +1 -3
  437. package/dist/src/server/speech/providers/openai/stt.js.map +1 -1
  438. package/dist/src/server/speech/providers/openai/tts.js.map +1 -1
  439. package/dist/src/server/speech/speech-config-resolver.js +3 -7
  440. package/dist/src/server/speech/speech-config-resolver.js.map +1 -1
  441. package/dist/src/server/speech/speech-runtime.js.map +1 -1
  442. package/dist/src/server/utils/diff-highlighter.js +49 -36
  443. package/dist/src/server/utils/diff-highlighter.js.map +1 -1
  444. package/dist/src/server/voice/fixed-duration-pcm-ring-buffer.js.map +1 -1
  445. package/dist/src/server/voice/voice-turn-controller.js +1 -3
  446. package/dist/src/server/voice/voice-turn-controller.js.map +1 -1
  447. package/dist/src/server/voice-config.js.map +1 -1
  448. package/dist/src/server/voice-mcp-bridge.js.map +1 -1
  449. package/dist/src/server/websocket-server.js +20 -22
  450. package/dist/src/server/websocket-server.js.map +1 -1
  451. package/dist/src/server/workspace-registry-bootstrap.js +6 -6
  452. package/dist/src/server/workspace-registry-bootstrap.js.map +1 -1
  453. package/dist/src/server/workspace-registry-model.js +40 -15
  454. package/dist/src/server/workspace-registry-model.js.map +1 -1
  455. package/dist/src/server/workspace-registry.js +16 -13
  456. package/dist/src/server/workspace-registry.js.map +1 -1
  457. package/dist/src/server/worktree-bootstrap.js +17 -6
  458. package/dist/src/server/worktree-bootstrap.js.map +1 -1
  459. package/dist/src/shared/agent-attention-notification.js.map +1 -1
  460. package/dist/src/shared/daemon-endpoints.js +11 -2
  461. package/dist/src/shared/daemon-endpoints.js.map +1 -1
  462. package/dist/src/shared/messages.js +330 -302
  463. package/dist/src/shared/messages.js.map +1 -1
  464. package/dist/src/shared/terminal-stream-protocol.js +99 -0
  465. package/dist/src/shared/terminal-stream-protocol.js.map +1 -0
  466. package/dist/src/shared/tool-call-display.js +6 -3
  467. package/dist/src/shared/tool-call-display.js.map +1 -1
  468. package/dist/src/terminal/terminal.js +44 -98
  469. package/dist/src/terminal/terminal.js.map +1 -1
  470. package/dist/src/utils/checkout-git.js +111 -120
  471. package/dist/src/utils/checkout-git.js.map +1 -1
  472. package/dist/src/utils/directory-suggestions.js +40 -40
  473. package/dist/src/utils/directory-suggestions.js.map +1 -1
  474. package/dist/src/utils/project-icon.js +2 -11
  475. package/dist/src/utils/project-icon.js.map +1 -1
  476. package/dist/src/utils/worktree.js +22 -19
  477. package/dist/src/utils/worktree.js.map +1 -1
  478. package/package.json +3 -11
  479. package/dist/server/client/daemon-client-terminal-stream-manager.d.ts +0 -43
  480. package/dist/server/client/daemon-client-terminal-stream-manager.d.ts.map +0 -1
  481. package/dist/server/client/daemon-client-terminal-stream-manager.js +0 -134
  482. package/dist/server/client/daemon-client-terminal-stream-manager.js.map +0 -1
  483. package/dist/server/server/utils/syntax-highlighter.d.ts +0 -10
  484. package/dist/server/server/utils/syntax-highlighter.d.ts.map +0 -1
  485. package/dist/server/server/utils/syntax-highlighter.js +0 -145
  486. package/dist/server/server/utils/syntax-highlighter.js.map +0 -1
  487. package/dist/server/shared/binary-mux.d.ts +0 -31
  488. package/dist/server/shared/binary-mux.d.ts.map +0 -1
  489. package/dist/server/shared/binary-mux.js +0 -114
  490. package/dist/server/shared/binary-mux.js.map +0 -1
  491. package/dist/server/shared/terminal-key-input.d.ts +0 -9
  492. package/dist/server/shared/terminal-key-input.d.ts.map +0 -1
  493. package/dist/server/shared/terminal-key-input.js +0 -132
  494. package/dist/server/shared/terminal-key-input.js.map +0 -1
  495. package/dist/src/server/utils/syntax-highlighter.js +0 -145
  496. package/dist/src/server/utils/syntax-highlighter.js.map +0 -1
  497. package/dist/src/shared/binary-mux.js +0 -114
  498. package/dist/src/shared/binary-mux.js.map +0 -1
@@ -6,35 +6,14 @@ import os from "node:os";
6
6
  import path from "node:path";
7
7
  import { query, } from "@anthropic-ai/claude-agent-sdk";
8
8
  import { mapClaudeCanceledToolCall, mapClaudeCompletedToolCall, mapClaudeFailedToolCall, mapClaudeRunningToolCall, } from "./claude/tool-call-mapper.js";
9
- import { isTaskNotificationUserContent, mapTaskNotificationSystemRecordToToolCall, mapTaskNotificationUserContentToToolCall, } from "./claude/task-notification-tool-call.js";
9
+ import { coerceTaskNotificationHistoryRecordToSystemMessage, mapTaskNotificationSystemRecordToToolCall, mapTaskNotificationUserContentToToolCall, } from "./claude/task-notification-tool-call.js";
10
10
  import { buildClaudeModelFamilyAliases, buildClaudeSelectableModelIds, listClaudeCatalogModels, } from "./claude/model-catalog.js";
11
11
  import { parsePartialJsonObject } from "./claude/partial-json.js";
12
- import { buildToolCallDisplayModel } from "../../../shared/tool-call-display.js";
13
- import { applyProviderEnv, } from "../provider-launch-config.js";
12
+ import { ClaudeSidechainTracker } from "./claude/sidechain-tracker.js";
13
+ import { applyProviderEnv } from "../provider-launch-config.js";
14
14
  import { getOrchestratorModeInstructions } from "../orchestrator-instructions.js";
15
- /*
16
- * Routing invariant:
17
- * While a foreground Claude turn is active, identifier-less assistant/stream/result
18
- * events must stay attached to that foreground run unless we have explicit evidence
19
- * that a distinct autonomous run has started.
20
- *
21
- * We previously allowed task_notification metadata to reserve autonomous ownership,
22
- * then consumed that reservation on the next unbound chunk. In practice Claude often
23
- * emits task_notification records and foreground tool-use stream chunks interleaved
24
- * within the same turn. That let a foreground turn's terminal result get misrouted
25
- * into an autonomous side run, which stranded the agent in "running" because the
26
- * foreground stream never received turn_completed.
27
- *
28
- * The rule below is intentionally conservative: foreground turns get first claim on
29
- * same-turn traffic, and autonomous wake reservations are only consumed once no
30
- * foreground turn is active. This keeps task_notification advisory instead of letting
31
- * it steal ownership from the active user turn.
32
- */
33
15
  const fsPromises = promises;
34
- const CLAUDE_SETTING_SOURCES = [
35
- "user",
36
- "project",
37
- ];
16
+ const CLAUDE_SETTING_SOURCES = ["user", "project"];
38
17
  function normalizeModelIdCandidate(modelId) {
39
18
  if (typeof modelId !== "string") {
40
19
  return null;
@@ -175,6 +154,8 @@ const REWIND_COMMAND = {
175
154
  argumentHint: "[user_message_uuid]",
176
155
  };
177
156
  const INTERRUPT_TOOL_USE_PLACEHOLDER = "[Request interrupted by user for tool use]";
157
+ const INTERRUPT_PLACEHOLDER_PATTERN = /^\[Request interrupted by user(?:[^\]]*)\]$/;
158
+ const NO_RESPONSE_REQUESTED_PLACEHOLDER = "No response requested.";
178
159
  const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
179
160
  function resolveClaudeSpawnCommand(spawnOptions, runtimeSettings) {
180
161
  const commandConfig = runtimeSettings?.command;
@@ -203,9 +184,7 @@ function applyRuntimeSettingsToClaudeOptions(options, runtimeSettings) {
203
184
  // The SDK defaults to spawning "node" via PATH lookup, which fails when
204
185
  // running from the managed runtime bundle where node isn't in PATH.
205
186
  // Always use process.execPath — the actual node binary running the daemon.
206
- const command = resolved.command === spawnOptions.command
207
- ? process.execPath
208
- : resolved.command;
187
+ const command = resolved.command === spawnOptions.command ? process.execPath : resolved.command;
209
188
  return spawn(command, resolved.args, {
210
189
  cwd: spawnOptions.cwd,
211
190
  env: applyProviderEnv(spawnOptions.env, runtimeSettings),
@@ -228,32 +207,20 @@ function summarizeClaudeOptionsForLog(options) {
228
207
  const prompt = systemPromptRaw;
229
208
  const promptType = typeof prompt.type === "string" ? prompt.type : "custom";
230
209
  return {
231
- mode: promptType === "preset"
232
- ? "preset"
233
- : "custom",
234
- preset: typeof prompt.preset === "string" && prompt.preset.length > 0
235
- ? prompt.preset
236
- : null,
210
+ mode: promptType === "preset" ? "preset" : "custom",
211
+ preset: typeof prompt.preset === "string" && prompt.preset.length > 0 ? prompt.preset : null,
237
212
  };
238
213
  })();
239
- const mcpServerNames = options.mcpServers
240
- ? Object.keys(options.mcpServers).sort()
241
- : [];
214
+ const mcpServerNames = options.mcpServers ? Object.keys(options.mcpServers).sort() : [];
242
215
  return {
243
216
  cwd: typeof options.cwd === "string" ? options.cwd : null,
244
- permissionMode: typeof options.permissionMode === "string"
245
- ? options.permissionMode
246
- : null,
217
+ permissionMode: typeof options.permissionMode === "string" ? options.permissionMode : null,
247
218
  model: typeof options.model === "string" ? options.model : null,
248
219
  includePartialMessages: options.includePartialMessages === true,
249
- settingSources: Array.isArray(options.settingSources)
250
- ? options.settingSources
251
- : [],
220
+ settingSources: Array.isArray(options.settingSources) ? options.settingSources : [],
252
221
  enableFileCheckpointing: options.enableFileCheckpointing === true,
253
222
  hasResume: typeof options.resume === "string" && options.resume.length > 0,
254
- maxThinkingTokens: typeof options.maxThinkingTokens === "number"
255
- ? options.maxThinkingTokens
256
- : null,
223
+ maxThinkingTokens: typeof options.maxThinkingTokens === "number" ? options.maxThinkingTokens : null,
257
224
  hasEnv: !!options.env,
258
225
  envKeyCount: Object.keys(options.env ?? {}).length,
259
226
  hasMcpServers: mcpServerNames.length > 0,
@@ -338,10 +305,59 @@ function coerceToolResultContentToString(content) {
338
305
  }
339
306
  return deterministicStringify(content);
340
307
  }
308
+ function normalizeClaudeTranscriptText(value) {
309
+ if (typeof value !== "string") {
310
+ return null;
311
+ }
312
+ const normalized = value.trim();
313
+ return normalized.length > 0 ? normalized : null;
314
+ }
315
+ function isClaudeInterruptPlaceholderText(value) {
316
+ const normalized = normalizeClaudeTranscriptText(value);
317
+ return normalized !== null && INTERRUPT_PLACEHOLDER_PATTERN.test(normalized);
318
+ }
319
+ function isClaudeNoResponsePlaceholderText(value) {
320
+ return normalizeClaudeTranscriptText(value) === NO_RESPONSE_REQUESTED_PLACEHOLDER;
321
+ }
322
+ function isClaudeTranscriptNoiseText(value) {
323
+ return isClaudeInterruptPlaceholderText(value) || isClaudeNoResponsePlaceholderText(value);
324
+ }
325
+ function collectClaudeTextContentParts(content) {
326
+ if (typeof content === "string") {
327
+ const normalized = normalizeClaudeTranscriptText(content);
328
+ return normalized ? [normalized] : [];
329
+ }
330
+ if (!Array.isArray(content)) {
331
+ return [];
332
+ }
333
+ const parts = [];
334
+ for (const block of content) {
335
+ if (!block || typeof block !== "object") {
336
+ continue;
337
+ }
338
+ const text = normalizeClaudeTranscriptText(block.text);
339
+ if (text) {
340
+ parts.push(text);
341
+ continue;
342
+ }
343
+ const input = normalizeClaudeTranscriptText(block.input);
344
+ if (input) {
345
+ parts.push(input);
346
+ }
347
+ }
348
+ return parts;
349
+ }
350
+ function isClaudeTranscriptNoiseContent(content) {
351
+ const parts = collectClaudeTextContentParts(content);
352
+ return parts.length > 0 && parts.every((part) => isClaudeTranscriptNoiseText(part));
353
+ }
341
354
  export function extractUserMessageText(content) {
342
355
  if (typeof content === "string") {
343
356
  const normalized = content.trim();
344
- return normalized.length > 0 ? normalized : null;
357
+ if (!normalized || isClaudeTranscriptNoiseText(normalized)) {
358
+ return null;
359
+ }
360
+ return normalized;
345
361
  }
346
362
  if (!Array.isArray(content)) {
347
363
  return null;
@@ -353,12 +369,18 @@ export function extractUserMessageText(content) {
353
369
  }
354
370
  const text = typeof block.text === "string" ? block.text : undefined;
355
371
  if (text && text.trim()) {
356
- parts.push(text.trim());
372
+ const trimmed = text.trim();
373
+ if (!isClaudeTranscriptNoiseText(trimmed)) {
374
+ parts.push(trimmed);
375
+ }
357
376
  continue;
358
377
  }
359
378
  const input = typeof block.input === "string" ? block.input : undefined;
360
379
  if (input && input.trim()) {
361
- parts.push(input.trim());
380
+ const trimmed = input.trim();
381
+ if (!isClaudeTranscriptNoiseText(trimmed)) {
382
+ parts.push(trimmed);
383
+ }
362
384
  }
363
385
  }
364
386
  if (parts.length === 0) {
@@ -367,8 +389,6 @@ export function extractUserMessageText(content) {
367
389
  const combined = parts.join("\n\n").trim();
368
390
  return combined.length > 0 ? combined : null;
369
391
  }
370
- const MAX_SUB_AGENT_LOG_ENTRIES = 200;
371
- const MAX_SUB_AGENT_SUMMARY_CHARS = 160;
372
392
  function isMetadata(value) {
373
393
  return typeof value === "object" && value !== null;
374
394
  }
@@ -508,179 +528,10 @@ function resolvePermissionKind(toolName, input) {
508
528
  }
509
529
  return "tool";
510
530
  }
511
- const ACTIVE_RUN_STATES = new Set([
512
- "queued",
513
- "awaiting_response",
514
- "streaming",
515
- "finalizing",
516
- ]);
517
- class RunTracker {
518
- constructor() {
519
- this.runs = new Map();
520
- this.runByTaskId = new Map();
521
- this.runByParentMessageId = new Map();
522
- this.runByMessageId = new Map();
523
- }
524
- createRun(input) {
525
- const run = {
526
- id: input.id,
527
- owner: input.owner,
528
- queue: input.queue,
529
- state: "queued",
530
- promptReplaySeen: input.promptReplaySeen ?? true,
531
- taskIds: new Set(),
532
- parentMessageIds: new Set(),
533
- messageIds: new Set(),
534
- };
535
- this.runs.set(run.id, run);
536
- return run;
537
- }
538
- getRun(runId) {
539
- return this.runs.get(runId) ?? null;
540
- }
541
- getForegroundRun() {
542
- for (const run of this.runs.values()) {
543
- if (run.owner === "foreground" && this.isActive(run.state)) {
544
- return run;
545
- }
546
- }
547
- return null;
548
- }
549
- listActiveRuns(owner) {
550
- const runs = [];
551
- for (const run of this.runs.values()) {
552
- if (!this.isActive(run.state)) {
553
- continue;
554
- }
555
- if (owner && run.owner !== owner) {
556
- continue;
557
- }
558
- runs.push(run);
559
- }
560
- return runs;
561
- }
562
- hasActiveRuns(owner) {
563
- for (const run of this.runs.values()) {
564
- if (!this.isActive(run.state)) {
565
- continue;
566
- }
567
- if (owner && run.owner !== owner) {
568
- continue;
569
- }
570
- return true;
571
- }
572
- return false;
573
- }
574
- getLatestActiveRun(owner) {
575
- let latest = null;
576
- for (const run of this.runs.values()) {
577
- if (!this.isActive(run.state)) {
578
- continue;
579
- }
580
- if (owner && run.owner !== owner) {
581
- continue;
582
- }
583
- latest = run;
584
- }
585
- return latest;
586
- }
587
- isRunActive(run) {
588
- if (!run) {
589
- return false;
590
- }
591
- return this.isActive(run.state);
592
- }
593
- resolveByIdentifiers(identifiers) {
594
- if (identifiers.taskId) {
595
- const run = this.resolveMappedRun(this.runByTaskId, identifiers.taskId);
596
- if (run) {
597
- return { run, reason: "task_id" };
598
- }
599
- }
600
- if (identifiers.parentMessageId) {
601
- const run = this.resolveMappedRun(this.runByParentMessageId, identifiers.parentMessageId);
602
- if (run) {
603
- return { run, reason: "parent_message_id" };
604
- }
605
- }
606
- if (identifiers.messageId) {
607
- const run = this.resolveMappedRun(this.runByMessageId, identifiers.messageId);
608
- if (run) {
609
- return { run, reason: "message_id" };
610
- }
611
- }
612
- return { run: null, reason: "metadata" };
613
- }
614
- bindIdentifiers(run, identifiers) {
615
- if (identifiers.taskId) {
616
- run.taskIds.add(identifiers.taskId);
617
- this.runByTaskId.set(identifiers.taskId, run.id);
618
- }
619
- if (identifiers.parentMessageId) {
620
- run.parentMessageIds.add(identifiers.parentMessageId);
621
- this.runByParentMessageId.set(identifiers.parentMessageId, run.id);
622
- }
623
- if (identifiers.messageId) {
624
- run.messageIds.add(identifiers.messageId);
625
- this.runByMessageId.set(identifiers.messageId, run.id);
626
- }
627
- }
628
- transition(run, nextState) {
629
- run.state = nextState;
630
- }
631
- complete(run, terminalState) {
632
- run.state = terminalState;
633
- this.clearRunIndex(run);
634
- }
635
- deriveLifecycle(pendingPermissionCount) {
636
- for (const run of this.runs.values()) {
637
- if (this.isActive(run.state)) {
638
- return "running";
639
- }
640
- }
641
- if (pendingPermissionCount > 0) {
642
- return "permission";
643
- }
644
- for (const run of this.runs.values()) {
645
- if (run.state === "error") {
646
- return "error";
647
- }
648
- }
649
- return "idle";
650
- }
651
- resolveMappedRun(mapping, identifier) {
652
- const runId = mapping.get(identifier);
653
- if (!runId) {
654
- return null;
655
- }
656
- const run = this.runs.get(runId);
657
- if (!run || !this.isActive(run.state)) {
658
- mapping.delete(identifier);
659
- return null;
660
- }
661
- return run;
662
- }
663
- clearRunIndex(run) {
664
- for (const taskId of run.taskIds) {
665
- this.runByTaskId.delete(taskId);
666
- }
667
- for (const parentMessageId of run.parentMessageIds) {
668
- this.runByParentMessageId.delete(parentMessageId);
669
- }
670
- for (const messageId of run.messageIds) {
671
- this.runByMessageId.delete(messageId);
672
- }
673
- run.taskIds.clear();
674
- run.parentMessageIds.clear();
675
- run.messageIds.clear();
676
- }
677
- isActive(state) {
678
- return ACTIVE_RUN_STATES.has(state);
679
- }
680
- }
681
531
  class TimelineAssembler {
682
532
  constructor() {
683
533
  this.messages = new Map();
534
+ this.finalizedMessageIds = new Set();
684
535
  this.activeMessageByRun = new Map();
685
536
  this.syntheticMessageCounter = 0;
686
537
  }
@@ -700,6 +551,9 @@ class TimelineAssembler {
700
551
  if (!messageId) {
701
552
  return [];
702
553
  }
554
+ if (this.finalizedMessageIds.has(messageId)) {
555
+ return [];
556
+ }
703
557
  const state = this.ensureMessageState(messageId, runId);
704
558
  const fragments = this.extractFragments(message.message?.content);
705
559
  return this.applyAbsoluteFragments(state, fragments);
@@ -799,13 +653,16 @@ class TimelineAssembler {
799
653
  if (runId && this.activeMessageByRun.get(runId) === messageId) {
800
654
  this.activeMessageByRun.delete(runId);
801
655
  }
656
+ this.finalizedMessageIds.add(messageId);
657
+ this.messages.delete(messageId);
802
658
  return items;
803
659
  }
804
660
  emitNewContent(state) {
805
661
  const items = [];
806
662
  const nextAssistantText = state.assistantText.slice(state.emittedAssistantLength);
807
663
  if (nextAssistantText.length > 0 &&
808
- nextAssistantText !== INTERRUPT_TOOL_USE_PLACEHOLDER) {
664
+ nextAssistantText !== INTERRUPT_TOOL_USE_PLACEHOLDER &&
665
+ !isClaudeTranscriptNoiseText(nextAssistantText)) {
809
666
  state.emittedAssistantLength = state.assistantText.length;
810
667
  items.push({ type: "assistant_message", text: nextAssistantText });
811
668
  }
@@ -886,28 +743,12 @@ class TimelineAssembler {
886
743
  }
887
744
  readMessageIdFromAssistantMessage(message) {
888
745
  const candidate = message;
889
- return readTrimmedString(candidate.message_id) ??
890
- readTrimmedString(candidate.message?.id) ??
891
- null;
746
+ return (readTrimmedString(candidate.message_id) ?? readTrimmedString(candidate.message?.id) ?? null);
892
747
  }
893
748
  readMessageIdFromStreamEvent(event) {
894
749
  const message = event.message;
895
- return (readTrimmedString(event.message_id) ??
896
- readTrimmedString(message?.id) ??
897
- null);
898
- }
899
- }
900
- function isMetadataOnlySdkMessage(message) {
901
- if (message.type === "system") {
902
- return true;
903
- }
904
- if (message.type !== "user") {
905
- return false;
906
- }
907
- if (isSyntheticUserEntry(message)) {
908
- return true;
750
+ return readTrimmedString(event.message_id) ?? readTrimmedString(message?.id) ?? null;
909
751
  }
910
- return isTaskNotificationUserContent(message.message?.content);
911
752
  }
912
753
  function isSyntheticUserEntry(entry) {
913
754
  if (!entry || typeof entry !== "object") {
@@ -1025,29 +866,30 @@ class ClaudeAgentSession {
1025
866
  this.toolUseInputBuffers = new Map();
1026
867
  this.pendingPermissions = new Map();
1027
868
  this.activeForegroundTurn = null;
869
+ this.autonomousTurn = null;
1028
870
  this.liveEventQueue = new Pushable();
1029
- this.runTracker = new RunTracker();
1030
871
  this.timelineAssembler = new TimelineAssembler();
872
+ this.sidechainTracker = new ClaudeSidechainTracker({
873
+ getToolInput: (toolUseId) => this.toolUseCache.get(toolUseId)?.input ?? null,
874
+ });
1031
875
  this.persistedHistory = [];
1032
876
  this.historyPending = false;
877
+ this.historyOffsetSessionId = null;
878
+ this.historyReadOffsetBytes = 0;
879
+ this.historyLineFragment = "";
1033
880
  this.turnState = "idle";
1034
- this.preReplayMetadataSeen = false;
1035
- this.pendingAutonomousWakeReservations = 0;
1036
- this.nextRunOrdinal = 1;
881
+ this.nextTurnOrdinal = 1;
1037
882
  this.cancelCurrentTurn = null;
1038
- this.pendingInterruptPromise = null;
1039
883
  this.activeTurnPromise = null;
1040
884
  this.cachedRuntimeInfo = null;
1041
885
  this.lastOptionsModel = null;
1042
886
  this.selectableModelIds = buildClaudeSelectableModelIds();
1043
887
  this.selectableModelFamilyAliases = buildClaudeModelFamilyAliases();
1044
- this.activeSidechains = new Map();
1045
888
  this.compacting = false;
1046
889
  this.queryPumpPromise = null;
1047
890
  this.queryRestartNeeded = false;
891
+ this.pendingInterruptAbort = false;
1048
892
  this.userMessageIds = [];
1049
- this.localUserMessageIds = new Set();
1050
- this.suppressLocalReplayActivity = false;
1051
893
  this.recentStderr = "";
1052
894
  this.closed = false;
1053
895
  this.handlePermissionRequest = async (toolName, input, options) => {
@@ -1202,34 +1044,25 @@ class ClaudeAgentSession {
1202
1044
  if (this.cancelCurrentTurn) {
1203
1045
  this.cancelCurrentTurn();
1204
1046
  }
1205
- this.suppressLocalReplayActivity = false;
1206
- this.pendingAutonomousWakeReservations = 0;
1207
1047
  const slashCommand = this.resolveSlashCommandInvocation(prompt);
1208
1048
  if (slashCommand?.commandName === REWIND_COMMAND_NAME) {
1209
1049
  yield* this.streamRewindCommand(slashCommand);
1210
1050
  return;
1211
1051
  }
1212
- await this.awaitPendingInterruptPromise();
1213
- if (this.turnState === "autonomous" &&
1214
- this.runTracker.hasActiveRuns("autonomous")) {
1215
- await this.transitionAutonomousToForeground();
1052
+ if (this.autonomousTurn) {
1053
+ this.completeAutonomousTurn();
1216
1054
  }
1217
1055
  const sdkMessage = this.toSdkUserMessage(prompt);
1218
1056
  const queue = new Pushable();
1219
- const run = this.createRun("foreground", queue);
1220
- this.runTracker.bindIdentifiers(run, {
1221
- taskId: null,
1222
- parentMessageId: null,
1223
- messageId: typeof sdkMessage.uuid === "string" ? sdkMessage.uuid : null,
1224
- });
1225
1057
  const foregroundTurn = {
1226
- runId: run.id,
1058
+ id: this.createTurnId("foreground"),
1227
1059
  queue,
1060
+ hasVisibleActivity: false,
1228
1061
  };
1229
1062
  this.activeForegroundTurn = foregroundTurn;
1230
- this.preReplayMetadataSeen = false;
1231
1063
  this.transitionTurnState("foreground", "foreground stream started");
1232
1064
  this.clearRecentStderr();
1065
+ queue.push({ type: "turn_started", provider: "claude" });
1233
1066
  let finishedNaturally = false;
1234
1067
  let cancelIssued = false;
1235
1068
  let queueDrainedWithoutTerminal = false;
@@ -1240,19 +1073,16 @@ class ClaudeAgentSession {
1240
1073
  return;
1241
1074
  }
1242
1075
  cancelIssued = true;
1243
- if (this.activeForegroundTurn?.runId === run.id) {
1244
- this.activeForegroundTurn = null;
1245
- }
1246
1076
  if (this.cancelCurrentTurn === requestCancel) {
1247
1077
  this.cancelCurrentTurn = null;
1248
1078
  }
1249
1079
  this.rejectAllPendingPermissions(new Error("Permission request aborted"));
1250
- this.cancelRun(run, {
1080
+ this.finishForegroundTurn({
1251
1081
  type: "turn_canceled",
1252
1082
  provider: "claude",
1253
1083
  reason: "Interrupted",
1254
1084
  });
1255
- this.pendingInterruptPromise = this.interruptActiveTurn().catch((error) => {
1085
+ void this.interruptActiveTurn().catch((error) => {
1256
1086
  this.logger.warn({ err: error }, "Failed to interrupt during cancel");
1257
1087
  });
1258
1088
  };
@@ -1266,7 +1096,7 @@ class ClaudeAgentSession {
1266
1096
  this.input.push(sdkMessage);
1267
1097
  }
1268
1098
  catch (error) {
1269
- this.failRun(run, error instanceof Error ? error.message : "Claude stream failed");
1099
+ this.finishForegroundTurn(this.buildTurnFailedEvent(error instanceof Error ? error.message : "Claude stream failed"));
1270
1100
  finishedNaturally = true;
1271
1101
  }
1272
1102
  try {
@@ -1306,16 +1136,8 @@ class ClaudeAgentSession {
1306
1136
  this.cancelCurrentTurn();
1307
1137
  return;
1308
1138
  }
1309
- const autonomousRuns = this.runTracker.listActiveRuns("autonomous");
1310
- if (autonomousRuns.length > 0) {
1311
- this.flushPendingToolCalls();
1312
- for (const run of autonomousRuns) {
1313
- this.emitRunEvent(run, {
1314
- type: "turn_canceled",
1315
- provider: "claude",
1316
- reason: "Interrupted",
1317
- });
1318
- }
1139
+ if (this.autonomousTurn) {
1140
+ this.cancelAutonomousTurn("Interrupted");
1319
1141
  }
1320
1142
  await this.interruptActiveTurn();
1321
1143
  }
@@ -1464,12 +1286,12 @@ class ClaudeAgentSession {
1464
1286
  this.cancelCurrentTurn?.();
1465
1287
  this.activeForegroundTurn?.queue.end();
1466
1288
  this.activeForegroundTurn = null;
1289
+ this.autonomousTurn = null;
1467
1290
  this.cancelCurrentTurn = null;
1468
1291
  this.turnState = "idle";
1469
- this.suppressLocalReplayActivity = false;
1470
- this.pendingAutonomousWakeReservations = 0;
1471
1292
  this.liveEventQueue.end();
1472
1293
  this.activeTurnPromise = null;
1294
+ this.sidechainTracker.clear();
1473
1295
  this.input?.end();
1474
1296
  await this.awaitWithTimeout(this.query?.interrupt?.(), "close query interrupt");
1475
1297
  await this.awaitWithTimeout(this.query?.return?.(), "close query return");
@@ -1512,15 +1334,11 @@ class ClaudeAgentSession {
1512
1334
  }
1513
1335
  const withoutPrefix = trimmed.slice(1);
1514
1336
  const firstWhitespaceIdx = withoutPrefix.search(/\s/);
1515
- const commandName = firstWhitespaceIdx === -1
1516
- ? withoutPrefix
1517
- : withoutPrefix.slice(0, firstWhitespaceIdx);
1337
+ const commandName = firstWhitespaceIdx === -1 ? withoutPrefix : withoutPrefix.slice(0, firstWhitespaceIdx);
1518
1338
  if (!commandName || commandName.includes("/")) {
1519
1339
  return null;
1520
1340
  }
1521
- const rawArgs = firstWhitespaceIdx === -1
1522
- ? ""
1523
- : withoutPrefix.slice(firstWhitespaceIdx + 1).trim();
1341
+ const rawArgs = firstWhitespaceIdx === -1 ? "" : withoutPrefix.slice(firstWhitespaceIdx + 1).trim();
1524
1342
  return rawArgs.length > 0
1525
1343
  ? { commandName, args: rawArgs, rawInput: trimmed }
1526
1344
  : { commandName, rawInput: trimmed };
@@ -1552,9 +1370,7 @@ class ClaudeAgentSession {
1552
1370
  yield {
1553
1371
  type: "turn_failed",
1554
1372
  provider: "claude",
1555
- error: error instanceof Error
1556
- ? error.message
1557
- : "Failed to rewind tracked files",
1373
+ error: error instanceof Error ? error.message : "Failed to rewind tracked files",
1558
1374
  };
1559
1375
  }
1560
1376
  }
@@ -1592,8 +1408,7 @@ class ClaudeAgentSession {
1592
1408
  }
1593
1409
  return {
1594
1410
  messageId: null,
1595
- error: rewindResult.error ??
1596
- `No file checkpoint found for message ${candidate}.`,
1411
+ error: rewindResult.error ?? `No file checkpoint found for message ${candidate}.`,
1597
1412
  };
1598
1413
  }
1599
1414
  const candidates = this.getRewindCandidateUserMessageIds();
@@ -1615,16 +1430,12 @@ class ClaudeAgentSession {
1615
1430
  }
1616
1431
  }
1617
1432
  catch (error) {
1618
- lastError =
1619
- error instanceof Error
1620
- ? error.message
1621
- : "Failed to rewind tracked files.";
1433
+ lastError = error instanceof Error ? error.message : "Failed to rewind tracked files.";
1622
1434
  }
1623
1435
  }
1624
1436
  return {
1625
1437
  messageId: null,
1626
- error: lastError ??
1627
- "No rewind checkpoints are currently available for this session.",
1438
+ error: lastError ?? "No rewind checkpoints are currently available for this session.",
1628
1439
  };
1629
1440
  }
1630
1441
  async rewindFilesOnce(messageId) {
@@ -1648,9 +1459,7 @@ class ClaudeAgentSession {
1648
1459
  getRewindCandidateUserMessageIds() {
1649
1460
  const candidates = [];
1650
1461
  const pushUnique = (value) => {
1651
- if (typeof value === "string" &&
1652
- value.length > 0 &&
1653
- !candidates.includes(value)) {
1462
+ if (typeof value === "string" && value.length > 0 && !candidates.includes(value)) {
1654
1463
  candidates.push(value);
1655
1464
  }
1656
1465
  };
@@ -1719,7 +1528,9 @@ class ClaudeAgentSession {
1719
1528
  try {
1720
1529
  await this.query.return?.();
1721
1530
  }
1722
- catch { /* ignore */ }
1531
+ catch {
1532
+ /* ignore */
1533
+ }
1723
1534
  this.query = null;
1724
1535
  this.input = null;
1725
1536
  this.queryRestartNeeded = false;
@@ -1851,7 +1662,6 @@ class ClaudeAgentSession {
1851
1662
  }
1852
1663
  const messageId = randomUUID();
1853
1664
  this.rememberUserMessageId(messageId);
1854
- this.localUserMessageIds.add(messageId);
1855
1665
  return {
1856
1666
  type: "user",
1857
1667
  message: {
@@ -1863,24 +1673,6 @@ class ClaudeAgentSession {
1863
1673
  session_id: this.claudeSessionId ?? "",
1864
1674
  };
1865
1675
  }
1866
- async awaitPendingInterruptPromise() {
1867
- if (!this.pendingInterruptPromise) {
1868
- return;
1869
- }
1870
- await this.pendingInterruptPromise;
1871
- this.pendingInterruptPromise = null;
1872
- }
1873
- createRun(owner, queue) {
1874
- const runId = `${owner}-run-${this.nextRunOrdinal++}`;
1875
- const run = this.runTracker.createRun({
1876
- id: runId,
1877
- owner,
1878
- queue,
1879
- promptReplaySeen: owner === "autonomous",
1880
- });
1881
- this.logger.debug({ runId, owner, state: run.state }, "Created Claude run");
1882
- return run;
1883
- }
1884
1676
  transitionTurnState(next, reason) {
1885
1677
  if (this.turnState === next) {
1886
1678
  return;
@@ -1888,20 +1680,17 @@ class ClaudeAgentSession {
1888
1680
  this.logger.debug({ from: this.turnState, to: next, reason }, "Claude turn state transition");
1889
1681
  this.turnState = next;
1890
1682
  }
1891
- transitionTurnStateFromActiveRuns(reason) {
1892
- if (this.runTracker.hasActiveRuns("foreground")) {
1683
+ syncTurnState(reason) {
1684
+ if (this.activeForegroundTurn) {
1893
1685
  this.transitionTurnState("foreground", reason);
1894
1686
  return;
1895
1687
  }
1896
- if (this.runTracker.hasActiveRuns("autonomous")) {
1688
+ if (this.autonomousTurn) {
1897
1689
  this.transitionTurnState("autonomous", reason);
1898
1690
  return;
1899
1691
  }
1900
1692
  this.transitionTurnState("idle", reason);
1901
1693
  }
1902
- failRun(run, errorMessage) {
1903
- this.emitRunEvent(run, this.buildTurnFailedEvent(errorMessage));
1904
- }
1905
1694
  buildTurnFailedEvent(errorMessage) {
1906
1695
  const normalized = errorMessage.trim() || "Claude run failed";
1907
1696
  const exitCodeMatch = normalized.match(/\bcode\s+(\d+)\b/i);
@@ -1927,274 +1716,106 @@ class ClaudeAgentSession {
1927
1716
  this.recentStderr = "";
1928
1717
  }
1929
1718
  getRecentStderrDiagnostic() {
1930
- const text = this.recentStderr.trim();
1931
- return text.length > 0 ? text : undefined;
1719
+ return this.recentStderr.trim() || undefined;
1932
1720
  }
1933
- cancelRun(run, event) {
1934
- this.flushPendingToolCalls();
1935
- this.emitRunEvent(run, event);
1721
+ createTurnId(owner) {
1722
+ return `${owner}-turn-${this.nextTurnOrdinal++}`;
1936
1723
  }
1937
- emitRunEvent(run, event) {
1938
- if (event.type === "turn_started" ||
1939
- event.type === "turn_completed" ||
1724
+ isTerminalTurnEvent(event) {
1725
+ return (event.type === "turn_completed" ||
1940
1726
  event.type === "turn_failed" ||
1941
- event.type === "turn_canceled") {
1942
- this.logger.trace({
1943
- runId: run.id,
1944
- owner: run.owner,
1945
- runState: run.state,
1946
- eventType: event.type,
1947
- routedTo: run.owner === "foreground" && run.queue ? "foreground_queue" : "live_queue",
1948
- }, "Claude run event emitted");
1949
- }
1950
- if (run.owner === "foreground" && run.queue) {
1951
- run.queue.push(event);
1952
- if (event.type === "turn_completed" ||
1953
- event.type === "turn_failed" ||
1954
- event.type === "turn_canceled") {
1955
- run.queue.end();
1956
- }
1957
- }
1958
- else {
1959
- this.liveEventQueue.push(event);
1960
- }
1961
- this.handleRunTerminalEvent(run, event);
1727
+ event.type === "turn_canceled");
1962
1728
  }
1963
- handleRunTerminalEvent(run, event) {
1964
- if (event.type === "turn_completed") {
1965
- this.runTracker.complete(run, "completed");
1966
- }
1967
- else if (event.type === "turn_failed") {
1968
- this.runTracker.complete(run, "error");
1969
- }
1970
- else if (event.type === "turn_canceled") {
1971
- this.runTracker.complete(run, "interrupted");
1972
- }
1973
- else {
1974
- return;
1729
+ shouldRecoverInterruptedQueryAbort(error, consecutiveRecoveries) {
1730
+ if (consecutiveRecoveries >= 3) {
1731
+ return false;
1975
1732
  }
1976
- if (this.activeForegroundTurn?.runId === run.id) {
1977
- this.activeForegroundTurn = null;
1978
- this.preReplayMetadataSeen = false;
1733
+ const message = typeof error === "string"
1734
+ ? error
1735
+ : error instanceof Error
1736
+ ? `${error.message}\n${error.stack ?? ""}`
1737
+ : JSON.stringify(error);
1738
+ return message.toLowerCase().includes("request was aborted");
1739
+ }
1740
+ finishForegroundTurn(event) {
1741
+ if (event.type === "turn_failed" || event.type === "turn_canceled") {
1742
+ this.flushPendingToolCalls();
1979
1743
  }
1980
- this.logger.trace({
1981
- runId: run.id,
1982
- owner: run.owner,
1983
- eventType: event.type,
1984
- runState: run.state,
1985
- hasActiveForegroundTurn: Boolean(this.activeForegroundTurn),
1986
- }, "Claude run terminal event handled");
1987
- this.transitionTurnStateFromActiveRuns(`run ${run.id} terminal`);
1744
+ this.dispatchForegroundEvents([event]);
1988
1745
  }
1989
- async transitionAutonomousToForeground() {
1990
- const autonomousRuns = this.runTracker.listActiveRuns("autonomous");
1991
- if (autonomousRuns.length === 0) {
1992
- this.transitionTurnStateFromActiveRuns("no autonomous runs to transition");
1746
+ dispatchForegroundEvents(events) {
1747
+ const foregroundTurn = this.activeForegroundTurn;
1748
+ if (!foregroundTurn) {
1749
+ this.dispatchLiveEvents(events);
1993
1750
  return;
1994
1751
  }
1995
- this.logger.debug({ runIds: autonomousRuns.map((run) => run.id) }, "Transitioning autonomous runs to foreground ownership");
1996
- this.flushPendingToolCalls();
1997
- for (const run of autonomousRuns) {
1998
- this.emitRunEvent(run, {
1999
- type: "turn_canceled",
2000
- provider: "claude",
2001
- reason: "Interrupted by foreground prompt",
2002
- });
2003
- }
2004
- this.pendingInterruptPromise = this.interruptActiveTurn().catch((error) => {
2005
- this.logger.warn({ err: error }, "Failed to interrupt autonomous run during foreground transition");
2006
- });
2007
- await this.awaitPendingInterruptPromise();
2008
- this.transitionTurnStateFromActiveRuns("autonomous interrupted for foreground");
2009
- }
2010
- routeMessage(normalized) {
2011
- if (normalized.metadataOnly) {
2012
- if ((normalized.message.type === "user" &&
2013
- isTaskNotificationUserContent(normalized.message.message?.content)) ||
2014
- (normalized.message.type === "system" &&
2015
- normalized.message.subtype === "task_notification")) {
2016
- this.reserveAutonomousWake("task_notification");
2017
- }
2018
- this.notePreReplayMetadata(normalized.message);
2019
- return { run: null, reason: "metadata" };
2020
- }
2021
- const hasIdentifiers = Boolean(normalized.identifiers.taskId ||
2022
- normalized.identifiers.parentMessageId ||
2023
- normalized.identifiers.messageId);
2024
- const byIdentifiers = this.runTracker.resolveByIdentifiers(normalized.identifiers);
2025
- if (byIdentifiers.run) {
2026
- return byIdentifiers;
2027
- }
2028
- const foregroundRun = this.activeForegroundTurn
2029
- ? this.runTracker.getRun(this.activeForegroundTurn.runId)
2030
- : null;
2031
- // A previously unseen task_id during foreground ownership is deterministic
2032
- // evidence of a distinct autonomous wake/run, not foreground response text.
2033
- if (this.turnState === "foreground" &&
2034
- foregroundRun &&
2035
- normalized.identifiers.taskId) {
2036
- const incomingTaskId = normalized.identifiers.taskId;
2037
- // Foreground must claim its first task_id; otherwise early foreground
2038
- // result events can be misrouted to autonomous fallback runs.
2039
- if (foregroundRun.taskIds.size === 0) {
2040
- if (foregroundRun.state !== "finalizing") {
2041
- return { run: foregroundRun, reason: "foreground" };
2042
- }
2043
- }
2044
- else if (foregroundRun.taskIds.has(incomingTaskId)) {
2045
- return { run: foregroundRun, reason: "foreground" };
2046
- }
2047
- const autonomousRun = this.createRun("autonomous", null);
2048
- this.emitRunEvent(autonomousRun, { type: "turn_started", provider: "claude" });
2049
- return { run: autonomousRun, reason: "task_id_new" };
2050
- }
2051
- if (this.turnState === "foreground" &&
2052
- foregroundRun &&
2053
- this.shouldPreferForegroundRun({
2054
- run: foregroundRun,
2055
- message: normalized.message,
2056
- })) {
2057
- return { run: foregroundRun, reason: "foreground" };
2058
- }
2059
- if (this.pendingAutonomousWakeReservations > 0 &&
2060
- !normalized.identifiers.taskId &&
2061
- !normalized.identifiers.parentMessageId &&
2062
- !normalized.identifiers.messageId) {
2063
- const reservedAutonomousRun = this.claimOrCreateAutonomousRun("reservation_unbound");
2064
- return {
2065
- run: reservedAutonomousRun,
2066
- reason: "reserved_autonomous",
2067
- };
2068
- }
2069
- if (!hasIdentifiers) {
2070
- const activeAutonomousRun = this.runTracker.getLatestActiveRun("autonomous");
2071
- if (activeAutonomousRun) {
2072
- return { run: activeAutonomousRun, reason: "unbound_autonomous" };
2073
- }
2074
- if (!foregroundRun &&
2075
- (normalized.message.type === "assistant" ||
2076
- normalized.message.type === "stream_event" ||
2077
- normalized.message.type === "result" ||
2078
- normalized.message.type === "tool_progress")) {
2079
- const autonomousRun = this.claimOrCreateAutonomousRun("unbound_implicit");
2080
- return { run: autonomousRun, reason: "unbound_autonomous" };
2081
- }
2082
- }
2083
- if (this.pendingAutonomousWakeReservations > 0) {
2084
- const reservedAutonomousRun = this.claimOrCreateAutonomousRun("reservation_fallback");
2085
- return { run: reservedAutonomousRun, reason: "reserved_autonomous" };
2086
- }
2087
- this.logger.debug({
2088
- messageType: normalized.message.type,
2089
- hasIdentifiers,
2090
- taskId: normalized.identifiers.taskId,
2091
- parentMessageId: normalized.identifiers.parentMessageId,
2092
- messageId: normalized.identifiers.messageId,
2093
- turnState: this.turnState,
2094
- pendingAutonomousWakeReservations: this.pendingAutonomousWakeReservations,
2095
- }, "Ignoring unmatched Claude SDK message without explicit run start signal");
2096
- return {
2097
- run: null,
2098
- reason: "ignored_unmatched",
2099
- dispatchWithoutRun: false,
2100
- };
2101
- }
2102
- shouldPreferForegroundRun(input) {
2103
- const { run, message } = input;
2104
- if (run.state === "completed" ||
2105
- run.state === "interrupted" ||
2106
- run.state === "error") {
2107
- return false;
1752
+ let terminalSeen = false;
1753
+ for (const event of events) {
1754
+ foregroundTurn.queue.push(event);
1755
+ terminalSeen || (terminalSeen = this.isTerminalTurnEvent(event));
2108
1756
  }
2109
- // Before prompt replay is observed, prefer foreground by default so the
2110
- // first turn cannot be stranded in autonomous fallback. If metadata churn
2111
- // was observed pre-replay, stay conservative and wait for replay.
2112
- if (!run.promptReplaySeen) {
2113
- if (this.isToolUseBoundaryStreamEvent(input.message)) {
2114
- return true;
2115
- }
2116
- // Keep pre-replay result events with the foreground run so stale result
2117
- // bursts cannot consume autonomous wake reservations.
2118
- if (message.type === "result") {
2119
- return true;
2120
- }
2121
- if (message.type === "assistant" ||
2122
- message.type === "stream_event" ||
2123
- message.type === "tool_progress") {
2124
- return !this.preReplayMetadataSeen;
2125
- }
2126
- return true;
1757
+ if (!terminalSeen) {
1758
+ return;
2127
1759
  }
2128
- if (run.state === "finalizing" &&
2129
- (message.type === "assistant" || message.type === "stream_event")) {
2130
- return false;
1760
+ foregroundTurn.queue.end();
1761
+ if (this.activeForegroundTurn === foregroundTurn) {
1762
+ this.activeForegroundTurn = null;
2131
1763
  }
2132
- return true;
1764
+ this.syncTurnState("foreground turn terminal");
2133
1765
  }
2134
- isToolUseBoundaryStreamEvent(message) {
2135
- if (message.type !== "stream_event") {
2136
- return false;
1766
+ dispatchLiveEvents(events) {
1767
+ let terminalSeen = false;
1768
+ for (const event of events) {
1769
+ this.liveEventQueue.push(event);
1770
+ terminalSeen || (terminalSeen = this.isTerminalTurnEvent(event));
2137
1771
  }
2138
- const event = message.event;
2139
- if (!event || event.type !== "message_delta") {
2140
- return false;
1772
+ if (terminalSeen && this.autonomousTurn) {
1773
+ this.autonomousTurn = null;
1774
+ this.syncTurnState("autonomous turn terminal");
2141
1775
  }
2142
- const delta = "delta" in event && event.delta && typeof event.delta === "object"
2143
- ? event.delta
2144
- : null;
2145
- return delta?.stop_reason === "tool_use";
2146
1776
  }
2147
- notePreReplayMetadata(message) {
2148
- if (this.turnState !== "foreground") {
1777
+ startAutonomousTurn() {
1778
+ if (this.autonomousTurn) {
2149
1779
  return;
2150
1780
  }
2151
- const foregroundRun = this.activeForegroundTurn
2152
- ? this.runTracker.getRun(this.activeForegroundTurn.runId)
2153
- : null;
2154
- if (!foregroundRun || foregroundRun.promptReplaySeen) {
1781
+ this.autonomousTurn = {
1782
+ id: this.createTurnId("autonomous"),
1783
+ };
1784
+ this.liveEventQueue.push({ type: "turn_started", provider: "claude" });
1785
+ this.syncTurnState("autonomous turn started");
1786
+ }
1787
+ completeAutonomousTurn() {
1788
+ if (!this.autonomousTurn) {
2155
1789
  return;
2156
1790
  }
2157
- // Most system metadata (init/hook callbacks/etc.) can precede the first prompt
2158
- // replay for a legitimate foreground run. Treating all of it as churn strands
2159
- // one-shot helper runs. task_notification is the exception: it represents
2160
- // background agent activity and should suppress pre-replay foreground routing.
2161
- if (message.type === "system" &&
2162
- message.subtype !== "task_notification") {
1791
+ this.autonomousTurn = null;
1792
+ this.liveEventQueue.push({ type: "turn_completed", provider: "claude" });
1793
+ this.syncTurnState("autonomous turn completed");
1794
+ }
1795
+ cancelAutonomousTurn(reason) {
1796
+ if (!this.autonomousTurn) {
2163
1797
  return;
2164
1798
  }
2165
- this.preReplayMetadataSeen = true;
2166
- }
2167
- reserveAutonomousWake(reason) {
2168
- this.pendingAutonomousWakeReservations += 1;
2169
- this.logger.debug({
1799
+ this.flushPendingToolCalls();
1800
+ this.autonomousTurn = null;
1801
+ this.liveEventQueue.push({
1802
+ type: "turn_canceled",
1803
+ provider: "claude",
2170
1804
  reason,
2171
- pendingAutonomousWakeReservations: this.pendingAutonomousWakeReservations,
2172
- }, "Reserved autonomous wake");
1805
+ });
1806
+ this.syncTurnState("autonomous turn canceled");
2173
1807
  }
2174
- claimOrCreateAutonomousRun(reason) {
2175
- const existing = this.runTracker.getLatestActiveRun("autonomous");
2176
- if (existing) {
2177
- if (this.pendingAutonomousWakeReservations > 0) {
2178
- this.pendingAutonomousWakeReservations -= 1;
2179
- }
2180
- this.logger.debug({
2181
- reason,
2182
- runId: existing.id,
2183
- pendingAutonomousWakeReservations: this.pendingAutonomousWakeReservations,
2184
- }, "Claimed autonomous wake reservation on existing run");
2185
- return existing;
1808
+ failActiveTurns(errorMessage) {
1809
+ const failure = this.buildTurnFailedEvent(errorMessage);
1810
+ if (this.activeForegroundTurn) {
1811
+ this.flushPendingToolCalls();
1812
+ this.dispatchForegroundEvents([failure]);
1813
+ return;
2186
1814
  }
2187
- const run = this.createRun("autonomous", null);
2188
- this.emitRunEvent(run, { type: "turn_started", provider: "claude" });
2189
- if (this.pendingAutonomousWakeReservations > 0) {
2190
- this.pendingAutonomousWakeReservations -= 1;
1815
+ if (this.autonomousTurn) {
1816
+ this.flushPendingToolCalls();
1817
+ this.dispatchLiveEvents([failure]);
2191
1818
  }
2192
- this.logger.debug({
2193
- reason,
2194
- runId: run.id,
2195
- pendingAutonomousWakeReservations: this.pendingAutonomousWakeReservations,
2196
- }, "Claimed autonomous wake reservation with new run");
2197
- return run;
2198
1819
  }
2199
1820
  startQueryPump() {
2200
1821
  if (this.closed || this.queryPumpPromise) {
@@ -2211,139 +1832,83 @@ class ClaudeAgentSession {
2211
1832
  });
2212
1833
  }
2213
1834
  async runQueryPump() {
2214
- while (!this.closed) {
2215
- if (!this.claudeSessionId && !this.activeForegroundTurn && !this.query) {
2216
- await this.waitForLiveHistoryPoll();
2217
- continue;
2218
- }
2219
- let q;
2220
- try {
2221
- q = await this.ensureQuery();
2222
- }
2223
- catch (error) {
2224
- this.logger.trace({ err: error }, "Failed to initialize Claude query pump");
2225
- await this.waitForLiveHistoryPoll();
2226
- continue;
2227
- }
2228
- let next;
2229
- try {
2230
- next = await q.next();
2231
- this.logger.trace({ claudeSessionId: this.claudeSessionId, next }, "Claude query pump raw next()");
2232
- }
2233
- catch (error) {
2234
- if (this.query !== q) {
2235
- this.logger.trace({ err: error, staleQuery: true }, "Ignoring Claude query pump next() failure from replaced query");
2236
- await this.awaitWithTimeout(q.return?.(), "query pump return after stale failure");
2237
- continue;
2238
- }
2239
- this.logger.trace({ err: error }, "Claude query pump next() failed");
2240
- for (const run of this.runTracker.listActiveRuns()) {
2241
- this.failRun(run, error instanceof Error ? error.message : "Claude stream failed");
2242
- }
2243
- this.input?.end();
2244
- await this.awaitWithTimeout(q.return?.(), "query pump return after failure");
2245
- if (this.query === q) {
2246
- this.query = null;
2247
- this.input = null;
2248
- }
2249
- await this.waitForLiveHistoryPoll();
2250
- continue;
2251
- }
2252
- if (next.done) {
2253
- if (this.query !== q) {
2254
- this.logger.trace({
2255
- claudeSessionId: this.claudeSessionId,
2256
- activeRunCount: this.runTracker.listActiveRuns().length,
2257
- staleQuery: true,
2258
- }, "Ignoring replaced Claude query pump completion");
2259
- await this.awaitWithTimeout(q.return?.(), "query pump return on stale done");
2260
- continue;
2261
- }
2262
- this.logger.trace({
2263
- claudeSessionId: this.claudeSessionId,
2264
- activeRunCount: this.runTracker.listActiveRuns().length,
2265
- }, "Claude query pump next() returned done");
2266
- this.input?.end();
2267
- await this.awaitWithTimeout(q.return?.(), "query pump return on done");
2268
- if (this.query === q) {
2269
- this.query = null;
2270
- this.input = null;
1835
+ let activeQuery;
1836
+ try {
1837
+ activeQuery = await this.ensureQuery();
1838
+ }
1839
+ catch (error) {
1840
+ this.logger.trace({ err: error }, "Failed to initialize Claude query pump");
1841
+ this.failActiveTurns(error instanceof Error ? error.message : "Claude stream failed");
1842
+ return;
1843
+ }
1844
+ let consecutiveInterruptAbortRecoveries = 0;
1845
+ try {
1846
+ while (!this.closed && this.query === activeQuery) {
1847
+ try {
1848
+ for await (const message of activeQuery) {
1849
+ consecutiveInterruptAbortRecoveries = 0;
1850
+ if (await this.handleMissingResumedConversation(message, activeQuery)) {
1851
+ return;
1852
+ }
1853
+ this.routeSdkMessageFromPump(message);
1854
+ }
1855
+ if (!this.closed && this.query === activeQuery) {
1856
+ this.failActiveTurns("Claude stream ended before terminal result");
1857
+ }
1858
+ return;
2271
1859
  }
2272
- const activeRuns = this.runTracker.listActiveRuns();
2273
- if (activeRuns.length > 0) {
2274
- for (const run of activeRuns) {
2275
- this.failRun(run, "Claude stream ended before terminal result");
1860
+ catch (error) {
1861
+ if (!this.closed &&
1862
+ this.query === activeQuery &&
1863
+ this.shouldRecoverInterruptedQueryAbort(error, consecutiveInterruptAbortRecoveries)) {
1864
+ consecutiveInterruptAbortRecoveries += 1;
1865
+ this.logger.debug({ recoveries: consecutiveInterruptAbortRecoveries }, "Recovering Claude query pump after interrupt abort");
1866
+ continue;
2276
1867
  }
1868
+ if (!this.closed && this.query === activeQuery) {
1869
+ this.failActiveTurns(error instanceof Error ? error.message : "Claude stream failed");
1870
+ }
1871
+ return;
2277
1872
  }
2278
- await this.waitForLiveHistoryPoll();
2279
- continue;
2280
- }
2281
- const sdkMessage = next.value;
2282
- if (!sdkMessage) {
2283
- continue;
2284
- }
2285
- if (this.query !== q) {
2286
- this.logger.trace({
2287
- claudeSessionId: this.claudeSessionId,
2288
- messageType: sdkMessage.type,
2289
- staleQuery: true,
2290
- }, "Ignoring Claude SDK message from replaced query");
2291
- await this.awaitWithTimeout(q.return?.(), "query pump return on stale message");
2292
- continue;
2293
- }
2294
- if (await this.handleMissingResumedConversation(sdkMessage, q)) {
2295
- continue;
2296
- }
2297
- try {
2298
- this.routeSdkMessageFromPump(sdkMessage);
2299
1873
  }
2300
- catch (error) {
2301
- this.logger.trace({ err: error }, "Failed to route Claude SDK message from query pump");
1874
+ }
1875
+ finally {
1876
+ if (this.query === activeQuery) {
1877
+ this.query = null;
1878
+ this.input = null;
2302
1879
  }
2303
1880
  }
2304
1881
  }
2305
1882
  routeSdkMessageFromPump(message) {
2306
- if (this.shouldSuppressLocalReplayActivity(message)) {
1883
+ const routeToForeground = Boolean(this.activeForegroundTurn);
1884
+ const assistantishMessage = message.type === "assistant" ||
1885
+ message.type === "stream_event" ||
1886
+ message.type === "tool_progress";
1887
+ if (!routeToForeground && assistantishMessage) {
1888
+ this.startAutonomousTurn();
1889
+ }
1890
+ if (!routeToForeground && !this.autonomousTurn && message.type === "result") {
2307
1891
  return;
2308
1892
  }
1893
+ const turnId = this.activeForegroundTurn?.id ?? this.autonomousTurn?.id ?? null;
2309
1894
  const identifiers = readEventIdentifiers(message);
2310
- const metadataOnly = isMetadataOnlySdkMessage(message);
2311
- const route = this.routeMessage({
2312
- message,
2313
- identifiers,
2314
- metadataOnly,
2315
- });
2316
- const suppressTerminalEvents = this.shouldSuppressReplayResultTerminal({
2317
- run: route.run,
2318
- message,
2319
- });
2320
1895
  this.logger.trace({
2321
1896
  claudeSessionId: this.claudeSessionId,
2322
1897
  messageType: message.type,
2323
- routeReason: route.reason,
2324
- runId: route.run?.id ?? null,
2325
- runOwner: route.run?.owner ?? null,
2326
- suppressTerminalEvents,
2327
- metadataOnly,
1898
+ routedTo: routeToForeground ? "foreground_queue" : "live_queue",
1899
+ turnId,
2328
1900
  }, "Claude query pump routed SDK message");
2329
- if (route.run) {
2330
- this.transitionTurnStateFromActiveRuns(`routed via ${route.reason}`);
2331
- this.runTracker.bindIdentifiers(route.run, identifiers);
2332
- if (!suppressTerminalEvents) {
2333
- this.updateRunLifecycleForMessage(route.run, message, identifiers);
2334
- }
2335
- }
2336
1901
  const messageEvents = this.translateMessageToEvents(message, {
2337
1902
  suppressAssistantText: true,
2338
1903
  suppressReasoning: true,
2339
- suppressTerminalEvents,
2340
1904
  });
2341
- const assistantTimelineItems = this.timelineAssembler.consume({
1905
+ const assistantTimelineEvents = this.timelineAssembler
1906
+ .consume({
2342
1907
  message,
2343
- runId: route.run?.id ?? null,
1908
+ runId: turnId,
2344
1909
  messageIdHint: identifiers.messageId,
2345
- });
2346
- const assistantTimelineEvents = assistantTimelineItems.map((item) => ({
1910
+ })
1911
+ .map((item) => ({
2347
1912
  type: "timeline",
2348
1913
  item,
2349
1914
  provider: "claude",
@@ -2352,16 +1917,26 @@ class ClaudeAgentSession {
2352
1917
  if (events.length === 0) {
2353
1918
  return;
2354
1919
  }
2355
- if (!route.run) {
2356
- if (route.dispatchWithoutRun === false) {
2357
- return;
2358
- }
2359
- this.dispatchMetadataEvents(events);
1920
+ if (this.pendingInterruptAbort &&
1921
+ message.type === "result" &&
1922
+ events.some((event) => event.type === "turn_completed" || event.type === "turn_failed") &&
1923
+ (!this.activeForegroundTurn || !this.activeForegroundTurn.hasVisibleActivity)) {
1924
+ this.pendingInterruptAbort = false;
1925
+ this.logger.debug("Suppressing stale Claude interrupt terminal result");
2360
1926
  return;
2361
1927
  }
2362
- for (const event of events) {
2363
- this.emitRunEvent(route.run, event);
1928
+ if (this.activeForegroundTurn &&
1929
+ events.some((event) => event.type === "timeline" ||
1930
+ event.type === "permission_requested" ||
1931
+ event.type === "permission_resolved")) {
1932
+ this.activeForegroundTurn.hasVisibleActivity = true;
1933
+ this.pendingInterruptAbort = false;
1934
+ }
1935
+ if (routeToForeground) {
1936
+ this.dispatchForegroundEvents(events);
1937
+ return;
2364
1938
  }
1939
+ this.dispatchLiveEvents(events);
2365
1940
  }
2366
1941
  async handleMissingResumedConversation(message, query) {
2367
1942
  const staleResumeError = this.readMissingResumedConversationError(message);
@@ -2372,10 +1947,7 @@ class ClaudeAgentSession {
2372
1947
  claudeSessionId: this.claudeSessionId,
2373
1948
  error: staleResumeError,
2374
1949
  }, "Claude resumed session no longer exists; invalidating persisted session");
2375
- for (const run of this.runTracker.listActiveRuns()) {
2376
- this.failRun(run, staleResumeError);
2377
- }
2378
- this.transitionTurnStateFromActiveRuns("missing resumed conversation");
1950
+ this.failActiveTurns(staleResumeError);
2379
1951
  this.input?.end();
2380
1952
  await this.awaitWithTimeout(query.return?.(), "query pump return on missing resumed conversation");
2381
1953
  if (this.query === query) {
@@ -2386,373 +1958,36 @@ class ClaudeAgentSession {
2386
1958
  this.persistence = null;
2387
1959
  this.persistedHistory = [];
2388
1960
  this.historyPending = false;
1961
+ this.historyOffsetSessionId = null;
1962
+ this.historyReadOffsetBytes = 0;
1963
+ this.historyLineFragment = "";
2389
1964
  this.cachedRuntimeInfo = null;
2390
1965
  this.queryRestartNeeded = false;
1966
+ this.autonomousTurn = null;
1967
+ this.activeForegroundTurn = null;
1968
+ this.syncTurnState("missing resumed conversation");
2391
1969
  return true;
2392
1970
  }
2393
- shouldSuppressReplayResultTerminal(input) {
2394
- const { run, message } = input;
2395
- if (!run || run.owner !== "foreground" || message.type !== "result") {
2396
- return false;
2397
- }
2398
- if (run.promptReplaySeen) {
2399
- return false;
2400
- }
2401
- if (run.state === "streaming" || run.state === "finalizing") {
2402
- return false;
2403
- }
2404
- const resultSubtype = "subtype" in message && typeof message.subtype === "string"
2405
- ? message.subtype
2406
- : null;
2407
- // Pre-replay success results are stale in practice (leftover from an
2408
- // earlier query segment) and must not end the current foreground run.
2409
- if (resultSubtype === "success") {
2410
- this.logger.trace({
2411
- runId: run.id,
2412
- runOwner: run.owner,
2413
- runState: run.state,
2414
- promptReplaySeen: run.promptReplaySeen,
2415
- resultSubtype,
2416
- }, "Suppressing pre-replay foreground success result terminal event");
2417
- return true;
2418
- }
2419
- // For non-success results, keep the metadata-churn guard to avoid
2420
- // suppressing legitimate hard failures.
2421
- return this.preReplayMetadataSeen;
2422
- }
2423
- dispatchMetadataEvents(events) {
2424
- for (const event of events) {
2425
- this.pushEvent(event);
2426
- }
2427
- }
2428
- updateRunLifecycleForMessage(run, message, identifiers) {
2429
- const previousState = run.state;
2430
- if (message.type === "user" &&
2431
- identifiers.messageId &&
2432
- run.messageIds.has(identifiers.messageId)) {
2433
- run.promptReplaySeen = true;
2434
- this.preReplayMetadataSeen = false;
2435
- }
2436
- if (run.state === "queued") {
2437
- this.runTracker.transition(run, "awaiting_response");
2438
- }
2439
- if (message.type === "assistant" ||
2440
- message.type === "stream_event" ||
2441
- message.type === "tool_progress") {
2442
- this.runTracker.transition(run, "streaming");
2443
- return;
2444
- }
2445
- if (message.type === "result") {
2446
- this.runTracker.transition(run, "finalizing");
2447
- }
2448
- else {
2449
- return;
2450
- }
2451
- if (run.state !== previousState) {
2452
- this.logger.trace({
2453
- runId: run.id,
2454
- owner: run.owner,
2455
- messageType: message.type,
2456
- previousState,
2457
- nextState: run.state,
2458
- taskId: identifiers.taskId,
2459
- parentMessageId: identifiers.parentMessageId,
2460
- messageId: identifiers.messageId,
2461
- }, "Updated Claude run lifecycle from SDK message");
2462
- }
2463
- }
2464
- shouldSuppressLocalReplayActivity(message) {
2465
- const localReplay = this.isLocalReplayUserMessage(message);
2466
- if (!this.activeForegroundTurn && localReplay) {
2467
- this.suppressLocalReplayActivity = true;
2468
- this.logger.debug({ uuid: message.uuid }, "Suppressing local replay user message from live pump");
2469
- return true;
2470
- }
2471
- if (!this.suppressLocalReplayActivity) {
2472
- return false;
2473
- }
2474
- // Suppress only replay scaffolding. Do not suppress autonomous
2475
- // assistant/result events; otherwise task-notification replies can be dropped.
2476
- if (localReplay) {
2477
- return true;
2478
- }
2479
- if (message.type === "system") {
2480
- return true;
2481
- }
2482
- const identifiers = readEventIdentifiers(message);
2483
- const hasIdentifiers = Boolean(identifiers.taskId || identifiers.parentMessageId || identifiers.messageId);
2484
- if (message.type !== "user" && !hasIdentifiers) {
2485
- if (this.pendingAutonomousWakeReservations > 0) {
2486
- this.suppressLocalReplayActivity = false;
2487
- return false;
2488
- }
2489
- return true;
2490
- }
2491
- if (message.type === "user") {
2492
- this.suppressLocalReplayActivity = false;
2493
- return false;
2494
- }
2495
- this.suppressLocalReplayActivity = false;
2496
- return false;
2497
- }
2498
- isLocalReplayUserMessage(message) {
2499
- if (message.type !== "user") {
2500
- return false;
2501
- }
2502
- const uuid = readTrimmedString(message.uuid);
2503
- if (!uuid) {
2504
- return false;
2505
- }
2506
- return this.localUserMessageIds.has(uuid);
2507
- }
2508
1971
  async interruptActiveTurn() {
2509
1972
  const queryToInterrupt = this.query;
2510
1973
  if (!queryToInterrupt || typeof queryToInterrupt.interrupt !== "function") {
2511
1974
  this.logger.trace("interruptActiveTurn: no query to interrupt");
2512
1975
  return;
2513
1976
  }
1977
+ this.pendingInterruptAbort = true;
2514
1978
  try {
2515
- this.logger.trace("interruptActiveTurn: calling query.interrupt()...");
2516
1979
  await this.awaitWithTimeout(queryToInterrupt.interrupt(), "interruptActiveTurn query.interrupt()");
2517
- this.input?.end();
2518
- this.logger.trace("interruptActiveTurn: calling query.return()...");
2519
- await this.awaitWithTimeout(queryToInterrupt.return?.(), "interruptActiveTurn query.return()");
2520
- this.query = null;
2521
- this.input = null;
2522
- this.queryRestartNeeded = false;
2523
1980
  }
2524
1981
  catch (error) {
2525
1982
  this.logger.warn({ err: error }, "Failed to interrupt active turn");
2526
- this.input?.end();
2527
- if (this.query === queryToInterrupt) {
2528
- this.query = null;
2529
- this.input = null;
2530
- }
2531
- // Try to force-close the iterator to unblock the pump's q.next() call.
2532
- this.awaitWithTimeout(queryToInterrupt.return?.(), "interruptActiveTurn force return after failure").catch(() => { });
2533
- // Disown the current pump and start a fresh one immediately so
2534
- // autonomous wakes are not lost while waiting for the next user turn.
2535
- this.queryPumpPromise = null;
2536
- this.startQueryPump();
2537
- }
2538
- }
2539
- handleSidechainMessage(message, parentToolUseId) {
2540
- const state = this.activeSidechains.get(parentToolUseId) ??
2541
- {
2542
- actions: [],
2543
- actionKeys: [],
2544
- nextActionIndex: 1,
2545
- actionIndexByKey: new Map(),
2546
- };
2547
- this.activeSidechains.set(parentToolUseId, state);
2548
- const contextUpdated = this.updateSubAgentContextFromTaskInput(state, parentToolUseId);
2549
- const actionCandidates = this.extractSubAgentActionCandidates(message);
2550
- let actionUpdated = false;
2551
- for (const action of actionCandidates) {
2552
- if (this.appendSubAgentAction(state, action)) {
2553
- actionUpdated = true;
2554
- }
2555
- }
2556
- if (!contextUpdated && !actionUpdated) {
2557
- return [];
2558
- }
2559
- const toolCall = mapClaudeRunningToolCall({
2560
- name: "Task",
2561
- callId: parentToolUseId,
2562
- input: null,
2563
- output: null,
2564
- });
2565
- if (!toolCall) {
2566
- return [];
2567
- }
2568
- const detail = {
2569
- type: "sub_agent",
2570
- ...(state.subAgentType ? { subAgentType: state.subAgentType } : {}),
2571
- ...(state.description ? { description: state.description } : {}),
2572
- log: state.actions
2573
- .map((action) => action.summary
2574
- ? `[${action.toolName}] ${action.summary}`
2575
- : `[${action.toolName}]`)
2576
- .join("\n"),
2577
- actions: state.actions.map((action) => ({
2578
- index: action.index,
2579
- toolName: action.toolName,
2580
- ...(action.summary ? { summary: action.summary } : {}),
2581
- })),
2582
- };
2583
- return [
2584
- {
2585
- type: "timeline",
2586
- item: {
2587
- ...toolCall,
2588
- detail,
2589
- },
2590
- provider: "claude",
2591
- },
2592
- ];
2593
- }
2594
- updateSubAgentContextFromTaskInput(state, parentToolUseId) {
2595
- const taskInput = this.toolUseCache.get(parentToolUseId)?.input;
2596
- const nextSubAgentType = this.normalizeSubAgentText(taskInput?.subagent_type);
2597
- const nextDescription = this.normalizeSubAgentText(taskInput?.description);
2598
- let changed = false;
2599
- if (nextSubAgentType && nextSubAgentType !== state.subAgentType) {
2600
- state.subAgentType = nextSubAgentType;
2601
- changed = true;
2602
- }
2603
- if (nextDescription && nextDescription !== state.description) {
2604
- state.description = nextDescription;
2605
- changed = true;
2606
- }
2607
- return changed;
2608
- }
2609
- normalizeSubAgentText(value) {
2610
- const normalized = readTrimmedString(value)?.replace(/\s+/g, " ");
2611
- if (!normalized) {
2612
- return undefined;
2613
- }
2614
- if (normalized.length <= MAX_SUB_AGENT_SUMMARY_CHARS) {
2615
- return normalized;
2616
- }
2617
- return `${normalized.slice(0, MAX_SUB_AGENT_SUMMARY_CHARS)}...`;
2618
- }
2619
- extractSubAgentActionCandidates(message) {
2620
- if (message.type === "assistant") {
2621
- const content = message.message?.content;
2622
- if (!Array.isArray(content)) {
2623
- return [];
2624
- }
2625
- const actions = [];
2626
- for (const block of content) {
2627
- if (!isClaudeContentChunk(block) ||
2628
- !(block.type === "tool_use" ||
2629
- block.type === "mcp_tool_use" ||
2630
- block.type === "server_tool_use") ||
2631
- typeof block.name !== "string") {
2632
- continue;
2633
- }
2634
- const key = readTrimmedString(block.id) ??
2635
- `assistant:${block.name}:${actions.length}`;
2636
- actions.push({
2637
- key,
2638
- toolName: block.name,
2639
- input: block.input ?? null,
2640
- });
2641
- }
2642
- return actions;
2643
- }
2644
- if (message.type === "stream_event") {
2645
- const event = message.event;
2646
- if (event.type !== "content_block_start") {
2647
- return [];
2648
- }
2649
- const block = isClaudeContentChunk(event.content_block)
2650
- ? event.content_block
2651
- : null;
2652
- if (!block ||
2653
- !(block.type === "tool_use" ||
2654
- block.type === "mcp_tool_use" ||
2655
- block.type === "server_tool_use") ||
2656
- typeof block.name !== "string") {
2657
- return [];
2658
- }
2659
- const key = readTrimmedString(block.id) ??
2660
- `stream:${block.name}:${typeof event.index === "number" ? event.index : 0}`;
2661
- return [
2662
- {
2663
- key,
2664
- toolName: block.name,
2665
- input: block.input ?? null,
2666
- },
2667
- ];
2668
- }
2669
- if (message.type === "tool_progress") {
2670
- const toolName = readTrimmedString(message.tool_name);
2671
- if (!toolName) {
2672
- return [];
2673
- }
2674
- const key = readTrimmedString(message.tool_use_id) ?? `progress:${toolName}`;
2675
- return [{ key, toolName, input: null }];
2676
- }
2677
- return [];
2678
- }
2679
- appendSubAgentAction(state, candidate) {
2680
- const normalizedToolName = readTrimmedString(candidate.toolName);
2681
- if (!normalizedToolName) {
2682
- return false;
2683
- }
2684
- const summary = this.deriveSubAgentActionSummary(normalizedToolName, candidate.input);
2685
- const existingIndex = state.actionIndexByKey.get(candidate.key);
2686
- if (existingIndex !== undefined) {
2687
- const existing = state.actions[existingIndex];
2688
- if (!existing) {
2689
- return false;
2690
- }
2691
- const nextSummary = existing.summary ?? summary;
2692
- const unchanged = existing.toolName === normalizedToolName &&
2693
- existing.summary === nextSummary;
2694
- if (unchanged) {
2695
- return false;
2696
- }
2697
- state.actions[existingIndex] = {
2698
- ...existing,
2699
- toolName: normalizedToolName,
2700
- ...(nextSummary ? { summary: nextSummary } : {}),
2701
- };
2702
- return true;
2703
- }
2704
- const nextEntry = {
2705
- index: state.nextActionIndex,
2706
- toolName: normalizedToolName,
2707
- ...(summary ? { summary } : {}),
2708
- };
2709
- state.nextActionIndex += 1;
2710
- state.actions.push(nextEntry);
2711
- state.actionKeys.push(candidate.key);
2712
- this.trimSubAgentTail(state);
2713
- this.rebuildSubAgentActionIndex(state);
2714
- return true;
2715
- }
2716
- trimSubAgentTail(state) {
2717
- while (state.actions.length > MAX_SUB_AGENT_LOG_ENTRIES) {
2718
- state.actions.shift();
2719
- state.actionKeys.shift();
2720
1983
  }
2721
1984
  }
2722
- rebuildSubAgentActionIndex(state) {
2723
- state.actionIndexByKey.clear();
2724
- for (let index = 0; index < state.actionKeys.length; index += 1) {
2725
- const key = state.actionKeys[index];
2726
- if (key) {
2727
- state.actionIndexByKey.set(key, index);
2728
- }
2729
- }
2730
- }
2731
- deriveSubAgentActionSummary(toolName, input) {
2732
- const runningToolCall = mapClaudeRunningToolCall({
2733
- name: toolName,
2734
- callId: `sub-agent-summary-${toolName}`,
2735
- input,
2736
- output: null,
2737
- });
2738
- if (!runningToolCall) {
2739
- return undefined;
2740
- }
2741
- const display = buildToolCallDisplayModel({
2742
- name: runningToolCall.name,
2743
- status: runningToolCall.status,
2744
- error: runningToolCall.error,
2745
- detail: runningToolCall.detail,
2746
- metadata: runningToolCall.metadata,
2747
- });
2748
- return this.normalizeSubAgentText(display.summary);
2749
- }
2750
1985
  translateMessageToEvents(message, options) {
2751
1986
  const parentToolUseId = "parent_tool_use_id" in message
2752
1987
  ? message.parent_tool_use_id
2753
1988
  : null;
2754
1989
  if (parentToolUseId) {
2755
- return this.handleSidechainMessage(message, parentToolUseId);
1990
+ return this.sidechainTracker.handleMessage(message, parentToolUseId);
2756
1991
  }
2757
1992
  const events = [];
2758
1993
  const fallbackThreadSessionId = this.captureSessionIdFromMessage(message);
@@ -2818,9 +2053,7 @@ class ClaudeAgentSession {
2818
2053
  this.compacting = false;
2819
2054
  break;
2820
2055
  }
2821
- const messageId = typeof message.uuid === "string" && message.uuid.length > 0
2822
- ? message.uuid
2823
- : undefined;
2056
+ const messageId = typeof message.uuid === "string" && message.uuid.length > 0 ? message.uuid : undefined;
2824
2057
  this.rememberUserMessageId(messageId);
2825
2058
  const content = message.message?.content;
2826
2059
  const taskNotificationItem = mapTaskNotificationUserContentToToolCall({
@@ -2836,19 +2069,22 @@ class ClaudeAgentSession {
2836
2069
  break;
2837
2070
  }
2838
2071
  if (typeof content === "string" && content.length > 0) {
2839
- // String content from user messages (e.g., local command output)
2840
- events.push({
2841
- type: "timeline",
2842
- item: {
2843
- type: "user_message",
2844
- text: content,
2845
- ...(messageId ? { messageId } : {}),
2846
- },
2847
- provider: "claude",
2848
- });
2072
+ if (!isClaudeTranscriptNoiseText(content)) {
2073
+ events.push({
2074
+ type: "timeline",
2075
+ item: {
2076
+ type: "user_message",
2077
+ text: content,
2078
+ ...(messageId ? { messageId } : {}),
2079
+ },
2080
+ provider: "claude",
2081
+ });
2082
+ }
2849
2083
  }
2850
2084
  else if (Array.isArray(content)) {
2851
- const timelineItems = this.mapBlocksToTimeline(content);
2085
+ const timelineItems = this.mapBlocksToTimeline(content, {
2086
+ textMessageType: "user_message",
2087
+ });
2852
2088
  for (const item of timelineItems) {
2853
2089
  if (item.type === "user_message" && messageId && !item.messageId) {
2854
2090
  events.push({
@@ -2884,9 +2120,6 @@ class ClaudeAgentSession {
2884
2120
  break;
2885
2121
  }
2886
2122
  case "result": {
2887
- if (options?.suppressTerminalEvents) {
2888
- break;
2889
- }
2890
2123
  const usage = this.convertUsage(message);
2891
2124
  if (message.subtype === "success") {
2892
2125
  events.push({ type: "turn_completed", provider: "claude", usage });
@@ -2948,18 +2181,14 @@ class ClaudeAgentSession {
2948
2181
  const existingSessionId = this.claudeSessionId;
2949
2182
  let threadStartedSessionId = null;
2950
2183
  if (existingSessionId === null) {
2951
- // First time setting session ID (empty → filled) - this is expected
2952
2184
  this.claudeSessionId = newSessionId;
2953
2185
  threadStartedSessionId = newSessionId;
2954
2186
  this.logger.debug({ sessionId: newSessionId }, "Claude session ID set for the first time");
2955
2187
  }
2956
2188
  else if (existingSessionId === newSessionId) {
2957
- // Same session ID - no-op, but log for visibility
2958
2189
  this.logger.debug({ sessionId: newSessionId }, "Claude session ID unchanged (same value)");
2959
2190
  }
2960
2191
  else {
2961
- // CRITICAL: Session ID is being overwritten with a different value
2962
- // This should NEVER happen and indicates a serious bug
2963
2192
  throw new Error(`CRITICAL: Claude session ID overwrite detected! ` +
2964
2193
  `Existing: ${existingSessionId}, New: ${newSessionId}. ` +
2965
2194
  `This indicates a session identity corruption bug.`);
@@ -2967,7 +2196,6 @@ class ClaudeAgentSession {
2967
2196
  this.availableModes = DEFAULT_MODES;
2968
2197
  this.currentMode = message.permissionMode;
2969
2198
  this.persistence = null;
2970
- // Capture actual model from SDK init message (not just the configured model)
2971
2199
  if (message.model) {
2972
2200
  const normalizedModel = normalizeClaudeRuntimeModelId({
2973
2201
  runtimeModelId: message.model,
@@ -2978,7 +2206,6 @@ class ClaudeAgentSession {
2978
2206
  });
2979
2207
  this.logger.debug({ model: message.model, normalizedModel }, "Captured model from SDK init");
2980
2208
  this.lastOptionsModel = normalizedModel;
2981
- // Invalidate cached runtime info so it picks up the new model
2982
2209
  this.cachedRuntimeInfo = null;
2983
2210
  }
2984
2211
  return threadStartedSessionId;
@@ -3031,7 +2258,7 @@ class ClaudeAgentSession {
3031
2258
  }
3032
2259
  }
3033
2260
  this.toolUseCache.clear();
3034
- this.activeSidechains.clear();
2261
+ this.sidechainTracker.clear();
3035
2262
  }
3036
2263
  pushToolCall(item, target) {
3037
2264
  if (!item) {
@@ -3046,15 +2273,8 @@ class ClaudeAgentSession {
3046
2273
  pushEvent(event) {
3047
2274
  const foregroundTurn = this.activeForegroundTurn;
3048
2275
  if (foregroundTurn) {
3049
- const run = this.runTracker.getRun(foregroundTurn.runId);
3050
- if (run &&
3051
- run.owner === "foreground" &&
3052
- run.queue === foregroundTurn.queue &&
3053
- this.runTracker.isRunActive(run)) {
3054
- foregroundTurn.queue.push(event);
3055
- return;
3056
- }
3057
- this.activeForegroundTurn = null;
2276
+ foregroundTurn.queue.push(event);
2277
+ return;
3058
2278
  }
3059
2279
  this.liveEventQueue.push(event);
3060
2280
  }
@@ -3072,47 +2292,125 @@ class ClaudeAgentSession {
3072
2292
  this.pendingPermissions.delete(id);
3073
2293
  }
3074
2294
  }
3075
- waitForLiveHistoryPoll() {
3076
- return new Promise((resolve) => setTimeout(resolve, 250));
3077
- }
3078
- loadPersistedHistory(sessionId) {
2295
+ loadPersistedHistory(sessionId, options) {
3079
2296
  try {
3080
2297
  const historyPath = this.resolveHistoryPath(sessionId);
3081
2298
  if (!historyPath || !fs.existsSync(historyPath)) {
3082
2299
  return;
3083
2300
  }
3084
- const content = fs.readFileSync(historyPath, "utf8");
3085
- const timeline = [];
3086
- for (const line of content.split(/\n+/)) {
3087
- const trimmed = line.trim();
3088
- if (!trimmed)
3089
- continue;
3090
- try {
3091
- const entry = JSON.parse(trimmed);
3092
- if (entry.isSidechain) {
3093
- continue;
3094
- }
3095
- if (entry.type === "user" && typeof entry.uuid === "string") {
3096
- this.rememberUserMessageId(entry.uuid);
3097
- }
3098
- const items = this.convertHistoryEntry(entry);
3099
- if (items.length > 0) {
3100
- timeline.push(...items);
3101
- }
3102
- }
3103
- catch (error) {
3104
- // ignore malformed history line
3105
- }
2301
+ if (this.historyOffsetSessionId !== sessionId) {
2302
+ this.historyOffsetSessionId = sessionId;
2303
+ this.historyReadOffsetBytes = 0;
2304
+ this.historyLineFragment = "";
3106
2305
  }
3107
- if (timeline.length > 0) {
3108
- this.persistedHistory = timeline;
3109
- this.historyPending = true;
2306
+ const content = fs.readFileSync(historyPath);
2307
+ if (content.byteLength < this.historyReadOffsetBytes) {
2308
+ this.historyReadOffsetBytes = 0;
2309
+ this.historyLineFragment = "";
3110
2310
  }
2311
+ if (content.byteLength === this.historyReadOffsetBytes) {
2312
+ return;
2313
+ }
2314
+ const unreadChunk = content.subarray(this.historyReadOffsetBytes).toString("utf8");
2315
+ this.historyReadOffsetBytes = content.byteLength;
2316
+ this.ingestPersistedHistoryChunk(unreadChunk, {
2317
+ dispatchLive: options?.dispatchLive ?? false,
2318
+ });
3111
2319
  }
3112
2320
  catch (error) {
3113
2321
  // ignore history load failures
3114
2322
  }
3115
2323
  }
2324
+ ingestPersistedHistoryChunk(chunk, options) {
2325
+ if (!chunk) {
2326
+ return;
2327
+ }
2328
+ const combined = `${this.historyLineFragment}${chunk}`;
2329
+ this.historyLineFragment = "";
2330
+ const lines = combined.split(/\r?\n/);
2331
+ const trailing = lines.pop() ?? "";
2332
+ const timeline = [];
2333
+ for (const line of lines) {
2334
+ this.ingestPersistedHistoryLine(line, {
2335
+ dispatchLive: options.dispatchLive,
2336
+ timeline,
2337
+ });
2338
+ }
2339
+ if (trailing.trim().length > 0) {
2340
+ const handled = this.ingestPersistedHistoryLine(trailing, {
2341
+ dispatchLive: options.dispatchLive,
2342
+ timeline,
2343
+ });
2344
+ if (!handled) {
2345
+ this.historyLineFragment = trailing;
2346
+ }
2347
+ }
2348
+ if (!options.dispatchLive && timeline.length > 0) {
2349
+ this.persistedHistory = [...this.persistedHistory, ...timeline];
2350
+ this.historyPending = true;
2351
+ }
2352
+ }
2353
+ ingestPersistedHistoryLine(line, options) {
2354
+ const trimmed = line.trim();
2355
+ if (!trimmed) {
2356
+ return true;
2357
+ }
2358
+ let entry;
2359
+ try {
2360
+ entry = JSON.parse(trimmed);
2361
+ }
2362
+ catch {
2363
+ return false;
2364
+ }
2365
+ if (entry.isSidechain) {
2366
+ return true;
2367
+ }
2368
+ if (entry.type === "user" && typeof entry.uuid === "string") {
2369
+ this.rememberUserMessageId(entry.uuid);
2370
+ }
2371
+ if (options.dispatchLive) {
2372
+ this.dispatchPersistedHistoryEntry(entry);
2373
+ return true;
2374
+ }
2375
+ const items = this.convertHistoryEntry(entry);
2376
+ if (items.length > 0) {
2377
+ options.timeline.push(...items);
2378
+ }
2379
+ return true;
2380
+ }
2381
+ dispatchPersistedHistoryEntry(entry) {
2382
+ const liveMessage = this.normalizePersistedHistoryEntryToLiveMessage(entry);
2383
+ if (liveMessage) {
2384
+ this.routeSdkMessageFromPump(liveMessage);
2385
+ return;
2386
+ }
2387
+ const items = this.convertHistoryEntry(entry);
2388
+ for (const item of items) {
2389
+ this.pushEvent({
2390
+ type: "timeline",
2391
+ item,
2392
+ provider: "claude",
2393
+ });
2394
+ }
2395
+ }
2396
+ normalizePersistedHistoryEntryToLiveMessage(entry) {
2397
+ const taskNotificationMessage = coerceTaskNotificationHistoryRecordToSystemMessage(entry);
2398
+ if (taskNotificationMessage) {
2399
+ return taskNotificationMessage;
2400
+ }
2401
+ const type = readTrimmedString(entry.type);
2402
+ switch (type) {
2403
+ case "assistant":
2404
+ case "result":
2405
+ case "stream_event":
2406
+ case "system":
2407
+ case "tool_progress":
2408
+ case "user":
2409
+ return entry;
2410
+ default:
2411
+ return null;
2412
+ }
2413
+ }
3116
2414
  resolveHistoryPath(sessionId) {
3117
2415
  const cwd = this.config.cwd;
3118
2416
  if (!cwd)
@@ -3126,25 +2424,50 @@ class ClaudeAgentSession {
3126
2424
  convertHistoryEntry(entry) {
3127
2425
  return convertClaudeHistoryEntry(entry, (content) => this.mapBlocksToTimeline(content));
3128
2426
  }
2427
+ // Maps Claude content blocks into AgentTimelineItems.
2428
+ //
2429
+ // textMessageType controls what type text blocks emit:
2430
+ // - "assistant_message" (default): one item per text block (streaming granularity)
2431
+ // - "user_message": coalesces all text blocks into a single user_message
2432
+ // (matches extractUserMessageText semantics: trim each block, join with "\n\n")
2433
+ //
2434
+ // suppressAssistantText only applies when textMessageType is "assistant_message" — user text
2435
+ // must never be suppressed since the TimelineAssembler only handles assistant text.
2436
+ //
2437
+ // NOTE: convertClaudeHistoryEntry uses extractUserMessageText directly instead of this function
2438
+ // for user entries. Both paths must produce equivalent user_message items.
3129
2439
  mapBlocksToTimeline(content, options) {
3130
- const suppressAssistant = options?.suppressAssistantText ?? false;
2440
+ const textMessageType = options?.textMessageType ?? "assistant_message";
2441
+ const suppressText = textMessageType === "assistant_message" && (options?.suppressAssistantText ?? false);
3131
2442
  const suppressReasoning = options?.suppressReasoning ?? false;
3132
2443
  if (typeof content === "string") {
3133
- if (!content || content === INTERRUPT_TOOL_USE_PLACEHOLDER) {
2444
+ if (!content ||
2445
+ content === INTERRUPT_TOOL_USE_PLACEHOLDER ||
2446
+ isClaudeTranscriptNoiseText(content)) {
3134
2447
  return [];
3135
2448
  }
3136
- if (suppressAssistant) {
2449
+ if (suppressText) {
3137
2450
  return [];
3138
2451
  }
3139
- return [{ type: "assistant_message", text: content }];
2452
+ return [{ type: textMessageType, text: content }];
3140
2453
  }
3141
2454
  const items = [];
2455
+ // User SDK entries can arrive as multiple text blocks, but Paseo treats them as one message.
2456
+ const userTextParts = [];
3142
2457
  for (const block of content) {
3143
2458
  switch (block.type) {
3144
2459
  case "text":
3145
2460
  case "text_delta":
3146
- if (block.text && block.text !== INTERRUPT_TOOL_USE_PLACEHOLDER) {
3147
- if (!suppressAssistant) {
2461
+ if (block.text &&
2462
+ block.text !== INTERRUPT_TOOL_USE_PLACEHOLDER &&
2463
+ !isClaudeTranscriptNoiseText(block.text)) {
2464
+ if (textMessageType === "user_message") {
2465
+ const trimmed = block.text.trim();
2466
+ if (trimmed) {
2467
+ userTextParts.push(trimmed);
2468
+ }
2469
+ }
2470
+ else if (!suppressText) {
3148
2471
  items.push({ type: "assistant_message", text: block.text });
3149
2472
  }
3150
2473
  }
@@ -3177,6 +2500,12 @@ class ClaudeAgentSession {
3177
2500
  break;
3178
2501
  }
3179
2502
  }
2503
+ if (textMessageType === "user_message" && userTextParts.length > 0) {
2504
+ items.unshift({
2505
+ type: "user_message",
2506
+ text: userTextParts.join("\n\n"),
2507
+ });
2508
+ }
3180
2509
  return items;
3181
2510
  }
3182
2511
  handleToolUseStart(block, items) {
@@ -3201,7 +2530,7 @@ class ClaudeAgentSession {
3201
2530
  const toolName = entry?.name ?? block.tool_name ?? "tool";
3202
2531
  const callId = typeof block.tool_use_id === "string" && block.tool_use_id.length > 0
3203
2532
  ? block.tool_use_id
3204
- : entry?.id ?? null;
2533
+ : (entry?.id ?? null);
3205
2534
  // Extract output from block.content (SDK always returns content in string form)
3206
2535
  const output = this.buildToolOutput(block, entry);
3207
2536
  if (block.is_error) {
@@ -3223,7 +2552,7 @@ class ClaudeAgentSession {
3223
2552
  }
3224
2553
  if (typeof block.tool_use_id === "string") {
3225
2554
  this.toolUseCache.delete(block.tool_use_id);
3226
- this.activeSidechains.delete(block.tool_use_id);
2555
+ this.sidechainTracker.delete(block.tool_use_id);
3227
2556
  }
3228
2557
  }
3229
2558
  buildToolOutput(block, entry) {
@@ -3296,12 +2625,24 @@ class ClaudeAgentSession {
3296
2625
  normalizedTool === "apply_diff") {
3297
2626
  if (input && typeof input.file_path === "string") {
3298
2627
  // Support both old_str/new_str and old_string/new_string parameter names
3299
- const oldContent = typeof input.old_str === "string" ? input.old_str : typeof input.old_string === "string" ? input.old_string : undefined;
3300
- const newContent = typeof input.new_str === "string" ? input.new_str : typeof input.new_string === "string" ? input.new_string : undefined;
2628
+ const oldContent = typeof input.old_str === "string"
2629
+ ? input.old_str
2630
+ : typeof input.old_string === "string"
2631
+ ? input.old_string
2632
+ : undefined;
2633
+ const newContent = typeof input.new_str === "string"
2634
+ ? input.new_str
2635
+ : typeof input.new_string === "string"
2636
+ ? input.new_string
2637
+ : undefined;
3301
2638
  return {
3302
2639
  type: "file_edit",
3303
2640
  filePath: input.file_path,
3304
- diff: typeof input.patch === "string" ? input.patch : typeof input.diff === "string" ? input.diff : undefined,
2641
+ diff: typeof input.patch === "string"
2642
+ ? input.patch
2643
+ : typeof input.diff === "string"
2644
+ ? input.diff
2645
+ : undefined,
3305
2646
  oldContent,
3306
2647
  newContent,
3307
2648
  };
@@ -3324,7 +2665,9 @@ class ClaudeAgentSession {
3324
2665
  mapPartialEvent(event, options) {
3325
2666
  if (event.type === "content_block_start") {
3326
2667
  const block = isClaudeContentChunk(event.content_block) ? event.content_block : null;
3327
- if (block?.type === "tool_use" && typeof event.index === "number" && typeof block.id === "string") {
2668
+ if (block?.type === "tool_use" &&
2669
+ typeof event.index === "number" &&
2670
+ typeof block.id === "string") {
3328
2671
  this.toolUseIndexToId.set(event.index, block.id);
3329
2672
  this.toolUseInputBuffers.delete(block.id);
3330
2673
  }
@@ -3389,7 +2732,9 @@ class ClaudeAgentSession {
3389
2732
  else if (!existing.server) {
3390
2733
  existing.server = existing.name;
3391
2734
  }
3392
- if (block.type === "tool_use" || block.type === "mcp_tool_use" || block.type === "server_tool_use") {
2735
+ if (block.type === "tool_use" ||
2736
+ block.type === "mcp_tool_use" ||
2737
+ block.type === "server_tool_use") {
3393
2738
  const input = this.normalizeToolInput(block.input);
3394
2739
  if (input) {
3395
2740
  this.applyToolInput(existing, input);
@@ -3465,7 +2810,10 @@ class ClaudeAgentSession {
3465
2810
  }
3466
2811
  isCommandTool(name, input) {
3467
2812
  const normalized = name.toLowerCase();
3468
- if (normalized.includes("bash") || normalized.includes("shell") || normalized.includes("terminal") || normalized.includes("command")) {
2813
+ if (normalized.includes("bash") ||
2814
+ normalized.includes("shell") ||
2815
+ normalized.includes("terminal") ||
2816
+ normalized.includes("command")) {
3469
2817
  return true;
3470
2818
  }
3471
2819
  if (typeof input.command === "string" || Array.isArray(input.command)) {
@@ -3509,7 +2857,10 @@ class ClaudeAgentSession {
3509
2857
  const files = [];
3510
2858
  for (const value of input.files) {
3511
2859
  if (typeof value === "string" && value.length > 0) {
3512
- files.push({ path: this.relativizePath(value) ?? value, kind: this.detectFileKind(value) });
2860
+ files.push({
2861
+ path: this.relativizePath(value) ?? value,
2862
+ kind: this.detectFileKind(value),
2863
+ });
3513
2864
  }
3514
2865
  }
3515
2866
  if (files.length > 0) {
@@ -3572,11 +2923,7 @@ function hasToolLikeBlock(block) {
3572
2923
  return type.includes("tool");
3573
2924
  }
3574
2925
  function readCompactionMetadata(source) {
3575
- const candidates = [
3576
- source.compact_metadata,
3577
- source.compactMetadata,
3578
- source.compactionMetadata,
3579
- ];
2926
+ const candidates = [source.compact_metadata, source.compactMetadata, source.compactionMetadata];
3580
2927
  for (const candidate of candidates) {
3581
2928
  if (!candidate || typeof candidate !== "object") {
3582
2929
  continue;
@@ -3602,18 +2949,18 @@ function normalizeHistoryBlocks(content) {
3602
2949
  export function convertClaudeHistoryEntry(entry, mapBlocks) {
3603
2950
  if (entry.type === "system" && entry.subtype === "compact_boundary") {
3604
2951
  const compactMetadata = readCompactionMetadata(entry);
3605
- return [{
2952
+ return [
2953
+ {
3606
2954
  type: "compaction",
3607
2955
  status: "completed",
3608
2956
  trigger: compactMetadata?.trigger === "manual" ? "manual" : "auto",
3609
2957
  preTokens: compactMetadata?.preTokens,
3610
- }];
2958
+ },
2959
+ ];
3611
2960
  }
3612
- if (entry.type === "system") {
3613
- const taskNotificationItem = mapTaskNotificationSystemRecordToToolCall(entry);
3614
- if (taskNotificationItem) {
3615
- return [taskNotificationItem];
3616
- }
2961
+ const taskNotificationItem = mapTaskNotificationSystemRecordToToolCall(entry);
2962
+ if (taskNotificationItem) {
2963
+ return [taskNotificationItem];
3617
2964
  }
3618
2965
  if (entry.isCompactSummary) {
3619
2966
  return [];
@@ -3626,10 +2973,12 @@ export function convertClaudeHistoryEntry(entry, mapBlocks) {
3626
2973
  return [];
3627
2974
  }
3628
2975
  const content = message.content;
2976
+ if ((entry.type === "user" || entry.type === "assistant") &&
2977
+ isClaudeTranscriptNoiseContent(content)) {
2978
+ return [];
2979
+ }
3629
2980
  const normalizedBlocks = normalizeHistoryBlocks(content);
3630
- const contentValue = typeof content === "string"
3631
- ? content
3632
- : normalizedBlocks;
2981
+ const contentValue = typeof content === "string" ? content : normalizedBlocks;
3633
2982
  const hasToolBlock = normalizedBlocks?.some((block) => hasToolLikeBlock(block)) ?? false;
3634
2983
  const userMessageId = entry.type === "user" && typeof entry.uuid === "string" && entry.uuid.length > 0
3635
2984
  ? entry.uuid
@@ -3762,9 +3111,7 @@ async function collectRecentClaudeSessions(root, limit) {
3762
3111
  }
3763
3112
  }
3764
3113
  }
3765
- return candidates
3766
- .sort((a, b) => b.mtime.getTime() - a.mtime.getTime())
3767
- .slice(0, limit);
3114
+ return candidates.sort((a, b) => b.mtime.getTime() - a.mtime.getTime()).slice(0, limit);
3768
3115
  }
3769
3116
  async function parseClaudeSessionDescriptor(filePath, mtime) {
3770
3117
  let content;
@@ -3847,15 +3194,20 @@ function extractClaudeUserText(message) {
3847
3194
  return null;
3848
3195
  }
3849
3196
  if (typeof message.content === "string") {
3850
- return message.content.trim();
3197
+ const normalized = message.content.trim();
3198
+ return normalized && !isClaudeTranscriptNoiseText(normalized) ? normalized : null;
3851
3199
  }
3852
3200
  if (typeof message.text === "string") {
3853
- return message.text.trim();
3201
+ const normalized = message.text.trim();
3202
+ return normalized && !isClaudeTranscriptNoiseText(normalized) ? normalized : null;
3854
3203
  }
3855
3204
  if (Array.isArray(message.content)) {
3856
3205
  for (const block of message.content) {
3857
3206
  if (block && typeof block.text === "string") {
3858
- return block.text.trim();
3207
+ const normalized = block.text.trim();
3208
+ if (normalized && !isClaudeTranscriptNoiseText(normalized)) {
3209
+ return normalized;
3210
+ }
3859
3211
  }
3860
3212
  }
3861
3213
  }