@getpaseo/server 0.1.30 → 0.1.33

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 (499) 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 +488 -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 +10 -10
  154. package/dist/server/server/persisted-config.d.ts.map +1 -1
  155. package/dist/server/server/persisted-config.js.map +1 -1
  156. package/dist/server/server/persistence-hooks.d.ts.map +1 -1
  157. package/dist/server/server/persistence-hooks.js.map +1 -1
  158. package/dist/server/server/pid-lock.d.ts.map +1 -1
  159. package/dist/server/server/pid-lock.js.map +1 -1
  160. package/dist/server/server/push/push-service.d.ts.map +1 -1
  161. package/dist/server/server/push/push-service.js.map +1 -1
  162. package/dist/server/server/relay-transport.d.ts.map +1 -1
  163. package/dist/server/server/relay-transport.js +6 -2
  164. package/dist/server/server/relay-transport.js.map +1 -1
  165. package/dist/server/server/session.d.ts +49 -37
  166. package/dist/server/server/session.d.ts.map +1 -1
  167. package/dist/server/server/session.js +1240 -998
  168. package/dist/server/server/session.js.map +1 -1
  169. package/dist/server/server/speech/audio.d.ts.map +1 -1
  170. package/dist/server/server/speech/audio.js.map +1 -1
  171. package/dist/server/server/speech/providers/local/config.d.ts.map +1 -1
  172. package/dist/server/server/speech/providers/local/config.js +5 -14
  173. package/dist/server/server/speech/providers/local/config.js.map +1 -1
  174. package/dist/server/server/speech/providers/local/models.d.ts.map +1 -1
  175. package/dist/server/server/speech/providers/local/models.js +1 -1
  176. package/dist/server/server/speech/providers/local/models.js.map +1 -1
  177. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.d.ts.map +1 -1
  178. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.js +21 -7
  179. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.js.map +1 -1
  180. package/dist/server/server/speech/providers/local/runtime.d.ts.map +1 -1
  181. package/dist/server/server/speech/providers/local/runtime.js +1 -23
  182. package/dist/server/server/speech/providers/local/runtime.js.map +1 -1
  183. package/dist/server/server/speech/providers/local/sherpa/model-catalog.d.ts.map +1 -1
  184. package/dist/server/server/speech/providers/local/sherpa/model-catalog.js.map +1 -1
  185. package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts.map +1 -1
  186. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js.map +1 -1
  187. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.d.ts.map +1 -1
  188. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js +9 -4
  189. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js.map +1 -1
  190. package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.d.ts.map +1 -1
  191. package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.js +7 -2
  192. package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.js.map +1 -1
  193. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.d.ts.map +1 -1
  194. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js +5 -1
  195. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js.map +1 -1
  196. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.d.ts.map +1 -1
  197. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js +2 -4
  198. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js.map +1 -1
  199. package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.d.ts.map +1 -1
  200. package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.js +1 -3
  201. package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.js.map +1 -1
  202. package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.d.ts.map +1 -1
  203. package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.js +2 -4
  204. package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.js.map +1 -1
  205. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.d.ts.map +1 -1
  206. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.js +4 -1
  207. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.js.map +1 -1
  208. package/dist/server/server/speech/providers/local/sherpa/silero-vad-provider.d.ts.map +1 -1
  209. package/dist/server/server/speech/providers/local/sherpa/silero-vad-provider.js +1 -1
  210. package/dist/server/server/speech/providers/local/sherpa/silero-vad-provider.js.map +1 -1
  211. package/dist/server/server/speech/providers/local/sherpa/silero-vad-session.d.ts.map +1 -1
  212. package/dist/server/server/speech/providers/local/sherpa/silero-vad-session.js.map +1 -1
  213. package/dist/server/server/speech/providers/openai/config.d.ts.map +1 -1
  214. package/dist/server/server/speech/providers/openai/config.js +5 -24
  215. package/dist/server/server/speech/providers/openai/config.js.map +1 -1
  216. package/dist/server/server/speech/providers/openai/realtime-transcription-session.d.ts.map +1 -1
  217. package/dist/server/server/speech/providers/openai/realtime-transcription-session.js +6 -3
  218. package/dist/server/server/speech/providers/openai/realtime-transcription-session.js.map +1 -1
  219. package/dist/server/server/speech/providers/openai/runtime.d.ts.map +1 -1
  220. package/dist/server/server/speech/providers/openai/runtime.js +2 -6
  221. package/dist/server/server/speech/providers/openai/runtime.js.map +1 -1
  222. package/dist/server/server/speech/providers/openai/stt.d.ts.map +1 -1
  223. package/dist/server/server/speech/providers/openai/stt.js +1 -3
  224. package/dist/server/server/speech/providers/openai/stt.js.map +1 -1
  225. package/dist/server/server/speech/providers/openai/tts.d.ts.map +1 -1
  226. package/dist/server/server/speech/providers/openai/tts.js.map +1 -1
  227. package/dist/server/server/speech/speech-config-resolver.d.ts.map +1 -1
  228. package/dist/server/server/speech/speech-config-resolver.js +3 -7
  229. package/dist/server/server/speech/speech-config-resolver.js.map +1 -1
  230. package/dist/server/server/speech/speech-provider.d.ts.map +1 -1
  231. package/dist/server/server/speech/speech-runtime.d.ts.map +1 -1
  232. package/dist/server/server/speech/speech-runtime.js.map +1 -1
  233. package/dist/server/server/speech/turn-detection-provider.d.ts.map +1 -1
  234. package/dist/server/server/terminal-mcp/server.d.ts.map +1 -1
  235. package/dist/server/server/terminal-mcp/server.js +3 -11
  236. package/dist/server/server/terminal-mcp/server.js.map +1 -1
  237. package/dist/server/server/terminal-mcp/terminal-manager.d.ts.map +1 -1
  238. package/dist/server/server/terminal-mcp/terminal-manager.js +2 -14
  239. package/dist/server/server/terminal-mcp/terminal-manager.js.map +1 -1
  240. package/dist/server/server/terminal-mcp/tmux.d.ts +1 -1
  241. package/dist/server/server/terminal-mcp/tmux.d.ts.map +1 -1
  242. package/dist/server/server/terminal-mcp/tmux.js +20 -123
  243. package/dist/server/server/terminal-mcp/tmux.js.map +1 -1
  244. package/dist/server/server/utils/diff-highlighter.d.ts +11 -3
  245. package/dist/server/server/utils/diff-highlighter.d.ts.map +1 -1
  246. package/dist/server/server/utils/diff-highlighter.js +49 -36
  247. package/dist/server/server/utils/diff-highlighter.js.map +1 -1
  248. package/dist/server/server/voice/fixed-duration-pcm-ring-buffer.d.ts.map +1 -1
  249. package/dist/server/server/voice/fixed-duration-pcm-ring-buffer.js.map +1 -1
  250. package/dist/server/server/voice/voice-turn-controller.d.ts.map +1 -1
  251. package/dist/server/server/voice/voice-turn-controller.js +1 -3
  252. package/dist/server/server/voice/voice-turn-controller.js.map +1 -1
  253. package/dist/server/server/voice-config.d.ts.map +1 -1
  254. package/dist/server/server/voice-config.js.map +1 -1
  255. package/dist/server/server/voice-mcp-bridge.d.ts.map +1 -1
  256. package/dist/server/server/voice-mcp-bridge.js.map +1 -1
  257. package/dist/server/server/websocket-server.d.ts +1 -0
  258. package/dist/server/server/websocket-server.d.ts.map +1 -1
  259. package/dist/server/server/websocket-server.js +20 -22
  260. package/dist/server/server/websocket-server.js.map +1 -1
  261. package/dist/server/server/workspace-registry-bootstrap.d.ts +3 -3
  262. package/dist/server/server/workspace-registry-bootstrap.d.ts.map +1 -1
  263. package/dist/server/server/workspace-registry-bootstrap.js +6 -6
  264. package/dist/server/server/workspace-registry-bootstrap.js.map +1 -1
  265. package/dist/server/server/workspace-registry-model.d.ts +14 -3
  266. package/dist/server/server/workspace-registry-model.d.ts.map +1 -1
  267. package/dist/server/server/workspace-registry-model.js +40 -15
  268. package/dist/server/server/workspace-registry-model.js.map +1 -1
  269. package/dist/server/server/workspace-registry.d.ts +5 -5
  270. package/dist/server/server/workspace-registry.d.ts.map +1 -1
  271. package/dist/server/server/workspace-registry.js +16 -13
  272. package/dist/server/server/workspace-registry.js.map +1 -1
  273. package/dist/server/server/worktree-bootstrap.d.ts.map +1 -1
  274. package/dist/server/server/worktree-bootstrap.js +17 -6
  275. package/dist/server/server/worktree-bootstrap.js.map +1 -1
  276. package/dist/server/shared/agent-attention-notification.d.ts.map +1 -1
  277. package/dist/server/shared/agent-attention-notification.js.map +1 -1
  278. package/dist/server/shared/agent-lifecycle.d.ts.map +1 -1
  279. package/dist/server/shared/daemon-endpoints.d.ts +1 -0
  280. package/dist/server/shared/daemon-endpoints.d.ts.map +1 -1
  281. package/dist/server/shared/daemon-endpoints.js +11 -2
  282. package/dist/server/shared/daemon-endpoints.js.map +1 -1
  283. package/dist/server/shared/messages.d.ts +1228 -2982
  284. package/dist/server/shared/messages.d.ts.map +1 -1
  285. package/dist/server/shared/messages.js +330 -302
  286. package/dist/server/shared/messages.js.map +1 -1
  287. package/dist/server/shared/terminal-stream-protocol.d.ts +36 -0
  288. package/dist/server/shared/terminal-stream-protocol.d.ts.map +1 -0
  289. package/dist/server/shared/terminal-stream-protocol.js +99 -0
  290. package/dist/server/shared/terminal-stream-protocol.js.map +1 -0
  291. package/dist/server/shared/tool-call-display.d.ts.map +1 -1
  292. package/dist/server/shared/tool-call-display.js +6 -3
  293. package/dist/server/shared/tool-call-display.js.map +1 -1
  294. package/dist/server/terminal/terminal.d.ts +9 -48
  295. package/dist/server/terminal/terminal.d.ts.map +1 -1
  296. package/dist/server/terminal/terminal.js +49 -126
  297. package/dist/server/terminal/terminal.js.map +1 -1
  298. package/dist/server/utils/checkout-git.d.ts +1 -0
  299. package/dist/server/utils/checkout-git.d.ts.map +1 -1
  300. package/dist/server/utils/checkout-git.js +111 -120
  301. package/dist/server/utils/checkout-git.js.map +1 -1
  302. package/dist/server/utils/directory-suggestions.d.ts +1 -1
  303. package/dist/server/utils/directory-suggestions.d.ts.map +1 -1
  304. package/dist/server/utils/directory-suggestions.js +40 -40
  305. package/dist/server/utils/directory-suggestions.js.map +1 -1
  306. package/dist/server/utils/project-icon.d.ts.map +1 -1
  307. package/dist/server/utils/project-icon.js +2 -11
  308. package/dist/server/utils/project-icon.js.map +1 -1
  309. package/dist/server/utils/worktree.d.ts +2 -0
  310. package/dist/server/utils/worktree.d.ts.map +1 -1
  311. package/dist/server/utils/worktree.js +22 -19
  312. package/dist/server/utils/worktree.js.map +1 -1
  313. package/dist/src/server/agent/activity-curator.js +5 -4
  314. package/dist/src/server/agent/activity-curator.js.map +1 -1
  315. package/dist/src/server/agent/agent-manager.js +26 -26
  316. package/dist/src/server/agent/agent-manager.js.map +1 -1
  317. package/dist/src/server/agent/agent-metadata-generator.js +1 -3
  318. package/dist/src/server/agent/agent-metadata-generator.js.map +1 -1
  319. package/dist/src/server/agent/agent-projections.js +4 -12
  320. package/dist/src/server/agent/agent-projections.js.map +1 -1
  321. package/dist/src/server/agent/agent-response-loop.js +6 -6
  322. package/dist/src/server/agent/agent-response-loop.js.map +1 -1
  323. package/dist/src/server/agent/agent-sdk-types.js.map +1 -1
  324. package/dist/src/server/agent/agent-storage.js +2 -4
  325. package/dist/src/server/agent/agent-storage.js.map +1 -1
  326. package/dist/src/server/agent/dictation-debug.js.map +1 -1
  327. package/dist/src/server/agent/mcp-server.js +19 -27
  328. package/dist/src/server/agent/mcp-server.js.map +1 -1
  329. package/dist/src/server/agent/pcm16-resampler.js.map +1 -1
  330. package/dist/src/server/agent/provider-launch-config.js +4 -2
  331. package/dist/src/server/agent/provider-launch-config.js.map +1 -1
  332. package/dist/src/server/agent/provider-manifest.js +63 -9
  333. package/dist/src/server/agent/provider-manifest.js.map +1 -1
  334. package/dist/src/server/agent/provider-registry.js +1 -1
  335. package/dist/src/server/agent/provider-registry.js.map +1 -1
  336. package/dist/src/server/agent/providers/claude/model-catalog.js +10 -10
  337. package/dist/src/server/agent/providers/claude/model-catalog.js.map +1 -1
  338. package/dist/src/server/agent/providers/claude/partial-json.js +4 -4
  339. package/dist/src/server/agent/providers/claude/partial-json.js.map +1 -1
  340. package/dist/src/server/agent/providers/claude/sidechain-tracker.js +230 -0
  341. package/dist/src/server/agent/providers/claude/sidechain-tracker.js.map +1 -0
  342. package/dist/src/server/agent/providers/claude/task-notification-tool-call.js +37 -20
  343. package/dist/src/server/agent/providers/claude/task-notification-tool-call.js.map +1 -1
  344. package/dist/src/server/agent/providers/claude/tool-call-detail-parser.js +21 -11
  345. package/dist/src/server/agent/providers/claude/tool-call-detail-parser.js.map +1 -1
  346. package/dist/src/server/agent/providers/claude/tool-call-mapper.js +23 -11
  347. package/dist/src/server/agent/providers/claude/tool-call-mapper.js.map +1 -1
  348. package/dist/src/server/agent/providers/claude-agent.js +488 -1134
  349. package/dist/src/server/agent/providers/claude-agent.js.map +1 -1
  350. package/dist/src/server/agent/providers/codex/tool-call-detail-parser.js +2 -2
  351. package/dist/src/server/agent/providers/codex/tool-call-detail-parser.js.map +1 -1
  352. package/dist/src/server/agent/providers/codex/tool-call-mapper.js +14 -11
  353. package/dist/src/server/agent/providers/codex/tool-call-mapper.js.map +1 -1
  354. package/dist/src/server/agent/providers/codex-app-server-agent.js +347 -163
  355. package/dist/src/server/agent/providers/codex-app-server-agent.js.map +1 -1
  356. package/dist/src/server/agent/providers/codex-rollout-timeline.js +21 -32
  357. package/dist/src/server/agent/providers/codex-rollout-timeline.js.map +1 -1
  358. package/dist/src/server/agent/providers/opencode/tool-call-detail-parser.js +2 -2
  359. package/dist/src/server/agent/providers/opencode/tool-call-detail-parser.js.map +1 -1
  360. package/dist/src/server/agent/providers/opencode/tool-call-mapper.js +2 -9
  361. package/dist/src/server/agent/providers/opencode/tool-call-mapper.js.map +1 -1
  362. package/dist/src/server/agent/providers/opencode-agent.js +5 -5
  363. package/dist/src/server/agent/providers/opencode-agent.js.map +1 -1
  364. package/dist/src/server/agent/providers/tool-call-detail-primitives.js +149 -15
  365. package/dist/src/server/agent/providers/tool-call-detail-primitives.js.map +1 -1
  366. package/dist/src/server/agent/providers/tool-call-mapper-utils.js +1 -3
  367. package/dist/src/server/agent/providers/tool-call-mapper-utils.js.map +1 -1
  368. package/dist/src/server/agent/stt-manager.js +1 -2
  369. package/dist/src/server/agent/stt-manager.js.map +1 -1
  370. package/dist/src/server/agent/timeline-projection.js.map +1 -1
  371. package/dist/src/server/agent/tts-manager.js +27 -9
  372. package/dist/src/server/agent/tts-manager.js.map +1 -1
  373. package/dist/src/server/agent/wait-for-agent-tracker.js.map +1 -1
  374. package/dist/src/server/agent-attention-policy.js.map +1 -1
  375. package/dist/src/server/allowed-hosts.js.map +1 -1
  376. package/dist/src/server/bootstrap.js +46 -5
  377. package/dist/src/server/bootstrap.js.map +1 -1
  378. package/dist/src/server/config.js +4 -11
  379. package/dist/src/server/config.js.map +1 -1
  380. package/dist/src/server/connection-offer.js +2 -3
  381. package/dist/src/server/connection-offer.js.map +1 -1
  382. package/dist/src/server/daemon-version.js +1 -1
  383. package/dist/src/server/daemon-version.js.map +1 -1
  384. package/dist/src/server/dictation/dictation-stream-manager.js +4 -1
  385. package/dist/src/server/dictation/dictation-stream-manager.js.map +1 -1
  386. package/dist/src/server/file-explorer/service.js +5 -8
  387. package/dist/src/server/file-explorer/service.js.map +1 -1
  388. package/dist/src/server/messages.js.map +1 -1
  389. package/dist/src/server/package-version.js +1 -2
  390. package/dist/src/server/package-version.js.map +1 -1
  391. package/dist/src/server/pairing-offer.js +45 -0
  392. package/dist/src/server/pairing-offer.js.map +1 -0
  393. package/dist/src/server/pairing-qr.js +45 -0
  394. package/dist/src/server/pairing-qr.js.map +1 -0
  395. package/dist/src/server/persisted-config.js.map +1 -1
  396. package/dist/src/server/persistence-hooks.js.map +1 -1
  397. package/dist/src/server/pid-lock.js.map +1 -1
  398. package/dist/src/server/push/push-service.js.map +1 -1
  399. package/dist/src/server/relay-transport.js +6 -2
  400. package/dist/src/server/relay-transport.js.map +1 -1
  401. package/dist/src/server/session.js +1240 -998
  402. package/dist/src/server/session.js.map +1 -1
  403. package/dist/src/server/speech/audio.js.map +1 -1
  404. package/dist/src/server/speech/providers/local/config.js +5 -14
  405. package/dist/src/server/speech/providers/local/config.js.map +1 -1
  406. package/dist/src/server/speech/providers/local/models.js +1 -1
  407. package/dist/src/server/speech/providers/local/models.js.map +1 -1
  408. package/dist/src/server/speech/providers/local/pocket/pocket-tts-onnx.js +21 -7
  409. package/dist/src/server/speech/providers/local/pocket/pocket-tts-onnx.js.map +1 -1
  410. package/dist/src/server/speech/providers/local/runtime.js +1 -23
  411. package/dist/src/server/speech/providers/local/runtime.js.map +1 -1
  412. package/dist/src/server/speech/providers/local/sherpa/model-catalog.js.map +1 -1
  413. package/dist/src/server/speech/providers/local/sherpa/model-downloader.js.map +1 -1
  414. package/dist/src/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js +9 -4
  415. package/dist/src/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js.map +1 -1
  416. package/dist/src/server/speech/providers/local/sherpa/sherpa-online-recognizer.js +7 -2
  417. package/dist/src/server/speech/providers/local/sherpa/sherpa-online-recognizer.js.map +1 -1
  418. package/dist/src/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js +5 -1
  419. package/dist/src/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js.map +1 -1
  420. package/dist/src/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js +2 -4
  421. package/dist/src/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js.map +1 -1
  422. package/dist/src/server/speech/providers/local/sherpa/sherpa-realtime-session.js +1 -3
  423. package/dist/src/server/speech/providers/local/sherpa/sherpa-realtime-session.js.map +1 -1
  424. package/dist/src/server/speech/providers/local/sherpa/sherpa-stt.js +2 -4
  425. package/dist/src/server/speech/providers/local/sherpa/sherpa-stt.js.map +1 -1
  426. package/dist/src/server/speech/providers/local/sherpa/sherpa-tts.js +4 -1
  427. package/dist/src/server/speech/providers/local/sherpa/sherpa-tts.js.map +1 -1
  428. package/dist/src/server/speech/providers/local/sherpa/silero-vad-provider.js +1 -1
  429. package/dist/src/server/speech/providers/local/sherpa/silero-vad-provider.js.map +1 -1
  430. package/dist/src/server/speech/providers/local/sherpa/silero-vad-session.js.map +1 -1
  431. package/dist/src/server/speech/providers/openai/config.js +5 -24
  432. package/dist/src/server/speech/providers/openai/config.js.map +1 -1
  433. package/dist/src/server/speech/providers/openai/realtime-transcription-session.js +6 -3
  434. package/dist/src/server/speech/providers/openai/realtime-transcription-session.js.map +1 -1
  435. package/dist/src/server/speech/providers/openai/runtime.js +2 -6
  436. package/dist/src/server/speech/providers/openai/runtime.js.map +1 -1
  437. package/dist/src/server/speech/providers/openai/stt.js +1 -3
  438. package/dist/src/server/speech/providers/openai/stt.js.map +1 -1
  439. package/dist/src/server/speech/providers/openai/tts.js.map +1 -1
  440. package/dist/src/server/speech/speech-config-resolver.js +3 -7
  441. package/dist/src/server/speech/speech-config-resolver.js.map +1 -1
  442. package/dist/src/server/speech/speech-runtime.js.map +1 -1
  443. package/dist/src/server/utils/diff-highlighter.js +49 -36
  444. package/dist/src/server/utils/diff-highlighter.js.map +1 -1
  445. package/dist/src/server/voice/fixed-duration-pcm-ring-buffer.js.map +1 -1
  446. package/dist/src/server/voice/voice-turn-controller.js +1 -3
  447. package/dist/src/server/voice/voice-turn-controller.js.map +1 -1
  448. package/dist/src/server/voice-config.js.map +1 -1
  449. package/dist/src/server/voice-mcp-bridge.js.map +1 -1
  450. package/dist/src/server/websocket-server.js +20 -22
  451. package/dist/src/server/websocket-server.js.map +1 -1
  452. package/dist/src/server/workspace-registry-bootstrap.js +6 -6
  453. package/dist/src/server/workspace-registry-bootstrap.js.map +1 -1
  454. package/dist/src/server/workspace-registry-model.js +40 -15
  455. package/dist/src/server/workspace-registry-model.js.map +1 -1
  456. package/dist/src/server/workspace-registry.js +16 -13
  457. package/dist/src/server/workspace-registry.js.map +1 -1
  458. package/dist/src/server/worktree-bootstrap.js +17 -6
  459. package/dist/src/server/worktree-bootstrap.js.map +1 -1
  460. package/dist/src/shared/agent-attention-notification.js.map +1 -1
  461. package/dist/src/shared/daemon-endpoints.js +11 -2
  462. package/dist/src/shared/daemon-endpoints.js.map +1 -1
  463. package/dist/src/shared/messages.js +330 -302
  464. package/dist/src/shared/messages.js.map +1 -1
  465. package/dist/src/shared/terminal-stream-protocol.js +99 -0
  466. package/dist/src/shared/terminal-stream-protocol.js.map +1 -0
  467. package/dist/src/shared/tool-call-display.js +6 -3
  468. package/dist/src/shared/tool-call-display.js.map +1 -1
  469. package/dist/src/terminal/terminal.js +49 -126
  470. package/dist/src/terminal/terminal.js.map +1 -1
  471. package/dist/src/utils/checkout-git.js +111 -120
  472. package/dist/src/utils/checkout-git.js.map +1 -1
  473. package/dist/src/utils/directory-suggestions.js +40 -40
  474. package/dist/src/utils/directory-suggestions.js.map +1 -1
  475. package/dist/src/utils/project-icon.js +2 -11
  476. package/dist/src/utils/project-icon.js.map +1 -1
  477. package/dist/src/utils/worktree.js +22 -19
  478. package/dist/src/utils/worktree.js.map +1 -1
  479. package/package.json +3 -11
  480. package/dist/server/client/daemon-client-terminal-stream-manager.d.ts +0 -43
  481. package/dist/server/client/daemon-client-terminal-stream-manager.d.ts.map +0 -1
  482. package/dist/server/client/daemon-client-terminal-stream-manager.js +0 -134
  483. package/dist/server/client/daemon-client-terminal-stream-manager.js.map +0 -1
  484. package/dist/server/server/utils/syntax-highlighter.d.ts +0 -10
  485. package/dist/server/server/utils/syntax-highlighter.d.ts.map +0 -1
  486. package/dist/server/server/utils/syntax-highlighter.js +0 -145
  487. package/dist/server/server/utils/syntax-highlighter.js.map +0 -1
  488. package/dist/server/shared/binary-mux.d.ts +0 -31
  489. package/dist/server/shared/binary-mux.d.ts.map +0 -1
  490. package/dist/server/shared/binary-mux.js +0 -114
  491. package/dist/server/shared/binary-mux.js.map +0 -1
  492. package/dist/server/shared/terminal-key-input.d.ts +0 -9
  493. package/dist/server/shared/terminal-key-input.d.ts.map +0 -1
  494. package/dist/server/shared/terminal-key-input.js +0 -132
  495. package/dist/server/shared/terminal-key-input.js.map +0 -1
  496. package/dist/src/server/utils/syntax-highlighter.js +0 -145
  497. package/dist/src/server/utils/syntax-highlighter.js.map +0 -1
  498. package/dist/src/shared/binary-mux.js +0 -114
  499. 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,13 +1286,14 @@ 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();
1296
+ this.query?.close();
1474
1297
  await this.awaitWithTimeout(this.query?.interrupt?.(), "close query interrupt");
1475
1298
  await this.awaitWithTimeout(this.query?.return?.(), "close query return");
1476
1299
  this.query = null;
@@ -1512,15 +1335,11 @@ class ClaudeAgentSession {
1512
1335
  }
1513
1336
  const withoutPrefix = trimmed.slice(1);
1514
1337
  const firstWhitespaceIdx = withoutPrefix.search(/\s/);
1515
- const commandName = firstWhitespaceIdx === -1
1516
- ? withoutPrefix
1517
- : withoutPrefix.slice(0, firstWhitespaceIdx);
1338
+ const commandName = firstWhitespaceIdx === -1 ? withoutPrefix : withoutPrefix.slice(0, firstWhitespaceIdx);
1518
1339
  if (!commandName || commandName.includes("/")) {
1519
1340
  return null;
1520
1341
  }
1521
- const rawArgs = firstWhitespaceIdx === -1
1522
- ? ""
1523
- : withoutPrefix.slice(firstWhitespaceIdx + 1).trim();
1342
+ const rawArgs = firstWhitespaceIdx === -1 ? "" : withoutPrefix.slice(firstWhitespaceIdx + 1).trim();
1524
1343
  return rawArgs.length > 0
1525
1344
  ? { commandName, args: rawArgs, rawInput: trimmed }
1526
1345
  : { commandName, rawInput: trimmed };
@@ -1552,9 +1371,7 @@ class ClaudeAgentSession {
1552
1371
  yield {
1553
1372
  type: "turn_failed",
1554
1373
  provider: "claude",
1555
- error: error instanceof Error
1556
- ? error.message
1557
- : "Failed to rewind tracked files",
1374
+ error: error instanceof Error ? error.message : "Failed to rewind tracked files",
1558
1375
  };
1559
1376
  }
1560
1377
  }
@@ -1592,8 +1409,7 @@ class ClaudeAgentSession {
1592
1409
  }
1593
1410
  return {
1594
1411
  messageId: null,
1595
- error: rewindResult.error ??
1596
- `No file checkpoint found for message ${candidate}.`,
1412
+ error: rewindResult.error ?? `No file checkpoint found for message ${candidate}.`,
1597
1413
  };
1598
1414
  }
1599
1415
  const candidates = this.getRewindCandidateUserMessageIds();
@@ -1615,16 +1431,12 @@ class ClaudeAgentSession {
1615
1431
  }
1616
1432
  }
1617
1433
  catch (error) {
1618
- lastError =
1619
- error instanceof Error
1620
- ? error.message
1621
- : "Failed to rewind tracked files.";
1434
+ lastError = error instanceof Error ? error.message : "Failed to rewind tracked files.";
1622
1435
  }
1623
1436
  }
1624
1437
  return {
1625
1438
  messageId: null,
1626
- error: lastError ??
1627
- "No rewind checkpoints are currently available for this session.",
1439
+ error: lastError ?? "No rewind checkpoints are currently available for this session.",
1628
1440
  };
1629
1441
  }
1630
1442
  async rewindFilesOnce(messageId) {
@@ -1648,9 +1460,7 @@ class ClaudeAgentSession {
1648
1460
  getRewindCandidateUserMessageIds() {
1649
1461
  const candidates = [];
1650
1462
  const pushUnique = (value) => {
1651
- if (typeof value === "string" &&
1652
- value.length > 0 &&
1653
- !candidates.includes(value)) {
1463
+ if (typeof value === "string" && value.length > 0 && !candidates.includes(value)) {
1654
1464
  candidates.push(value);
1655
1465
  }
1656
1466
  };
@@ -1716,10 +1526,13 @@ class ClaudeAgentSession {
1716
1526
  }
1717
1527
  if (this.queryRestartNeeded && this.query) {
1718
1528
  this.input?.end();
1529
+ this.query.close();
1719
1530
  try {
1720
1531
  await this.query.return?.();
1721
1532
  }
1722
- catch { /* ignore */ }
1533
+ catch {
1534
+ /* ignore */
1535
+ }
1723
1536
  this.query = null;
1724
1537
  this.input = null;
1725
1538
  this.queryRestartNeeded = false;
@@ -1851,7 +1664,6 @@ class ClaudeAgentSession {
1851
1664
  }
1852
1665
  const messageId = randomUUID();
1853
1666
  this.rememberUserMessageId(messageId);
1854
- this.localUserMessageIds.add(messageId);
1855
1667
  return {
1856
1668
  type: "user",
1857
1669
  message: {
@@ -1863,24 +1675,6 @@ class ClaudeAgentSession {
1863
1675
  session_id: this.claudeSessionId ?? "",
1864
1676
  };
1865
1677
  }
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
1678
  transitionTurnState(next, reason) {
1885
1679
  if (this.turnState === next) {
1886
1680
  return;
@@ -1888,20 +1682,17 @@ class ClaudeAgentSession {
1888
1682
  this.logger.debug({ from: this.turnState, to: next, reason }, "Claude turn state transition");
1889
1683
  this.turnState = next;
1890
1684
  }
1891
- transitionTurnStateFromActiveRuns(reason) {
1892
- if (this.runTracker.hasActiveRuns("foreground")) {
1685
+ syncTurnState(reason) {
1686
+ if (this.activeForegroundTurn) {
1893
1687
  this.transitionTurnState("foreground", reason);
1894
1688
  return;
1895
1689
  }
1896
- if (this.runTracker.hasActiveRuns("autonomous")) {
1690
+ if (this.autonomousTurn) {
1897
1691
  this.transitionTurnState("autonomous", reason);
1898
1692
  return;
1899
1693
  }
1900
1694
  this.transitionTurnState("idle", reason);
1901
1695
  }
1902
- failRun(run, errorMessage) {
1903
- this.emitRunEvent(run, this.buildTurnFailedEvent(errorMessage));
1904
- }
1905
1696
  buildTurnFailedEvent(errorMessage) {
1906
1697
  const normalized = errorMessage.trim() || "Claude run failed";
1907
1698
  const exitCodeMatch = normalized.match(/\bcode\s+(\d+)\b/i);
@@ -1927,274 +1718,106 @@ class ClaudeAgentSession {
1927
1718
  this.recentStderr = "";
1928
1719
  }
1929
1720
  getRecentStderrDiagnostic() {
1930
- const text = this.recentStderr.trim();
1931
- return text.length > 0 ? text : undefined;
1721
+ return this.recentStderr.trim() || undefined;
1932
1722
  }
1933
- cancelRun(run, event) {
1934
- this.flushPendingToolCalls();
1935
- this.emitRunEvent(run, event);
1723
+ createTurnId(owner) {
1724
+ return `${owner}-turn-${this.nextTurnOrdinal++}`;
1936
1725
  }
1937
- emitRunEvent(run, event) {
1938
- if (event.type === "turn_started" ||
1939
- event.type === "turn_completed" ||
1726
+ isTerminalTurnEvent(event) {
1727
+ return (event.type === "turn_completed" ||
1940
1728
  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);
1729
+ event.type === "turn_canceled");
1962
1730
  }
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;
1731
+ shouldRecoverInterruptedQueryAbort(error, consecutiveRecoveries) {
1732
+ if (consecutiveRecoveries >= 3) {
1733
+ return false;
1975
1734
  }
1976
- if (this.activeForegroundTurn?.runId === run.id) {
1977
- this.activeForegroundTurn = null;
1978
- this.preReplayMetadataSeen = false;
1735
+ const message = typeof error === "string"
1736
+ ? error
1737
+ : error instanceof Error
1738
+ ? `${error.message}\n${error.stack ?? ""}`
1739
+ : JSON.stringify(error);
1740
+ return message.toLowerCase().includes("request was aborted");
1741
+ }
1742
+ finishForegroundTurn(event) {
1743
+ if (event.type === "turn_failed" || event.type === "turn_canceled") {
1744
+ this.flushPendingToolCalls();
1979
1745
  }
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`);
1746
+ this.dispatchForegroundEvents([event]);
1988
1747
  }
1989
- async transitionAutonomousToForeground() {
1990
- const autonomousRuns = this.runTracker.listActiveRuns("autonomous");
1991
- if (autonomousRuns.length === 0) {
1992
- this.transitionTurnStateFromActiveRuns("no autonomous runs to transition");
1748
+ dispatchForegroundEvents(events) {
1749
+ const foregroundTurn = this.activeForegroundTurn;
1750
+ if (!foregroundTurn) {
1751
+ this.dispatchLiveEvents(events);
1993
1752
  return;
1994
1753
  }
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;
1754
+ let terminalSeen = false;
1755
+ for (const event of events) {
1756
+ foregroundTurn.queue.push(event);
1757
+ terminalSeen || (terminalSeen = this.isTerminalTurnEvent(event));
2108
1758
  }
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;
1759
+ if (!terminalSeen) {
1760
+ return;
2127
1761
  }
2128
- if (run.state === "finalizing" &&
2129
- (message.type === "assistant" || message.type === "stream_event")) {
2130
- return false;
1762
+ foregroundTurn.queue.end();
1763
+ if (this.activeForegroundTurn === foregroundTurn) {
1764
+ this.activeForegroundTurn = null;
2131
1765
  }
2132
- return true;
1766
+ this.syncTurnState("foreground turn terminal");
2133
1767
  }
2134
- isToolUseBoundaryStreamEvent(message) {
2135
- if (message.type !== "stream_event") {
2136
- return false;
1768
+ dispatchLiveEvents(events) {
1769
+ let terminalSeen = false;
1770
+ for (const event of events) {
1771
+ this.liveEventQueue.push(event);
1772
+ terminalSeen || (terminalSeen = this.isTerminalTurnEvent(event));
2137
1773
  }
2138
- const event = message.event;
2139
- if (!event || event.type !== "message_delta") {
2140
- return false;
1774
+ if (terminalSeen && this.autonomousTurn) {
1775
+ this.autonomousTurn = null;
1776
+ this.syncTurnState("autonomous turn terminal");
2141
1777
  }
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
1778
  }
2147
- notePreReplayMetadata(message) {
2148
- if (this.turnState !== "foreground") {
1779
+ startAutonomousTurn() {
1780
+ if (this.autonomousTurn) {
2149
1781
  return;
2150
1782
  }
2151
- const foregroundRun = this.activeForegroundTurn
2152
- ? this.runTracker.getRun(this.activeForegroundTurn.runId)
2153
- : null;
2154
- if (!foregroundRun || foregroundRun.promptReplaySeen) {
1783
+ this.autonomousTurn = {
1784
+ id: this.createTurnId("autonomous"),
1785
+ };
1786
+ this.liveEventQueue.push({ type: "turn_started", provider: "claude" });
1787
+ this.syncTurnState("autonomous turn started");
1788
+ }
1789
+ completeAutonomousTurn() {
1790
+ if (!this.autonomousTurn) {
2155
1791
  return;
2156
1792
  }
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") {
1793
+ this.autonomousTurn = null;
1794
+ this.liveEventQueue.push({ type: "turn_completed", provider: "claude" });
1795
+ this.syncTurnState("autonomous turn completed");
1796
+ }
1797
+ cancelAutonomousTurn(reason) {
1798
+ if (!this.autonomousTurn) {
2163
1799
  return;
2164
1800
  }
2165
- this.preReplayMetadataSeen = true;
2166
- }
2167
- reserveAutonomousWake(reason) {
2168
- this.pendingAutonomousWakeReservations += 1;
2169
- this.logger.debug({
1801
+ this.flushPendingToolCalls();
1802
+ this.autonomousTurn = null;
1803
+ this.liveEventQueue.push({
1804
+ type: "turn_canceled",
1805
+ provider: "claude",
2170
1806
  reason,
2171
- pendingAutonomousWakeReservations: this.pendingAutonomousWakeReservations,
2172
- }, "Reserved autonomous wake");
1807
+ });
1808
+ this.syncTurnState("autonomous turn canceled");
2173
1809
  }
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;
1810
+ failActiveTurns(errorMessage) {
1811
+ const failure = this.buildTurnFailedEvent(errorMessage);
1812
+ if (this.activeForegroundTurn) {
1813
+ this.flushPendingToolCalls();
1814
+ this.dispatchForegroundEvents([failure]);
1815
+ return;
2186
1816
  }
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;
1817
+ if (this.autonomousTurn) {
1818
+ this.flushPendingToolCalls();
1819
+ this.dispatchLiveEvents([failure]);
2191
1820
  }
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
1821
  }
2199
1822
  startQueryPump() {
2200
1823
  if (this.closed || this.queryPumpPromise) {
@@ -2211,139 +1834,83 @@ class ClaudeAgentSession {
2211
1834
  });
2212
1835
  }
2213
1836
  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;
1837
+ let activeQuery;
1838
+ try {
1839
+ activeQuery = await this.ensureQuery();
1840
+ }
1841
+ catch (error) {
1842
+ this.logger.trace({ err: error }, "Failed to initialize Claude query pump");
1843
+ this.failActiveTurns(error instanceof Error ? error.message : "Claude stream failed");
1844
+ return;
1845
+ }
1846
+ let consecutiveInterruptAbortRecoveries = 0;
1847
+ try {
1848
+ while (!this.closed && this.query === activeQuery) {
1849
+ try {
1850
+ for await (const message of activeQuery) {
1851
+ consecutiveInterruptAbortRecoveries = 0;
1852
+ if (await this.handleMissingResumedConversation(message, activeQuery)) {
1853
+ return;
1854
+ }
1855
+ this.routeSdkMessageFromPump(message);
1856
+ }
1857
+ if (!this.closed && this.query === activeQuery) {
1858
+ this.failActiveTurns("Claude stream ended before terminal result");
1859
+ }
1860
+ return;
2271
1861
  }
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");
1862
+ catch (error) {
1863
+ if (!this.closed &&
1864
+ this.query === activeQuery &&
1865
+ this.shouldRecoverInterruptedQueryAbort(error, consecutiveInterruptAbortRecoveries)) {
1866
+ consecutiveInterruptAbortRecoveries += 1;
1867
+ this.logger.debug({ recoveries: consecutiveInterruptAbortRecoveries }, "Recovering Claude query pump after interrupt abort");
1868
+ continue;
2276
1869
  }
1870
+ if (!this.closed && this.query === activeQuery) {
1871
+ this.failActiveTurns(error instanceof Error ? error.message : "Claude stream failed");
1872
+ }
1873
+ return;
2277
1874
  }
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
1875
  }
2300
- catch (error) {
2301
- this.logger.trace({ err: error }, "Failed to route Claude SDK message from query pump");
1876
+ }
1877
+ finally {
1878
+ if (this.query === activeQuery) {
1879
+ this.query = null;
1880
+ this.input = null;
2302
1881
  }
2303
1882
  }
2304
1883
  }
2305
1884
  routeSdkMessageFromPump(message) {
2306
- if (this.shouldSuppressLocalReplayActivity(message)) {
1885
+ const routeToForeground = Boolean(this.activeForegroundTurn);
1886
+ const assistantishMessage = message.type === "assistant" ||
1887
+ message.type === "stream_event" ||
1888
+ message.type === "tool_progress";
1889
+ if (!routeToForeground && assistantishMessage) {
1890
+ this.startAutonomousTurn();
1891
+ }
1892
+ if (!routeToForeground && !this.autonomousTurn && message.type === "result") {
2307
1893
  return;
2308
1894
  }
1895
+ const turnId = this.activeForegroundTurn?.id ?? this.autonomousTurn?.id ?? null;
2309
1896
  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
1897
  this.logger.trace({
2321
1898
  claudeSessionId: this.claudeSessionId,
2322
1899
  messageType: message.type,
2323
- routeReason: route.reason,
2324
- runId: route.run?.id ?? null,
2325
- runOwner: route.run?.owner ?? null,
2326
- suppressTerminalEvents,
2327
- metadataOnly,
1900
+ routedTo: routeToForeground ? "foreground_queue" : "live_queue",
1901
+ turnId,
2328
1902
  }, "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
1903
  const messageEvents = this.translateMessageToEvents(message, {
2337
1904
  suppressAssistantText: true,
2338
1905
  suppressReasoning: true,
2339
- suppressTerminalEvents,
2340
1906
  });
2341
- const assistantTimelineItems = this.timelineAssembler.consume({
1907
+ const assistantTimelineEvents = this.timelineAssembler
1908
+ .consume({
2342
1909
  message,
2343
- runId: route.run?.id ?? null,
1910
+ runId: turnId,
2344
1911
  messageIdHint: identifiers.messageId,
2345
- });
2346
- const assistantTimelineEvents = assistantTimelineItems.map((item) => ({
1912
+ })
1913
+ .map((item) => ({
2347
1914
  type: "timeline",
2348
1915
  item,
2349
1916
  provider: "claude",
@@ -2352,16 +1919,26 @@ class ClaudeAgentSession {
2352
1919
  if (events.length === 0) {
2353
1920
  return;
2354
1921
  }
2355
- if (!route.run) {
2356
- if (route.dispatchWithoutRun === false) {
2357
- return;
2358
- }
2359
- this.dispatchMetadataEvents(events);
1922
+ if (this.pendingInterruptAbort &&
1923
+ message.type === "result" &&
1924
+ events.some((event) => event.type === "turn_completed" || event.type === "turn_failed") &&
1925
+ (!this.activeForegroundTurn || !this.activeForegroundTurn.hasVisibleActivity)) {
1926
+ this.pendingInterruptAbort = false;
1927
+ this.logger.debug("Suppressing stale Claude interrupt terminal result");
2360
1928
  return;
2361
1929
  }
2362
- for (const event of events) {
2363
- this.emitRunEvent(route.run, event);
1930
+ if (this.activeForegroundTurn &&
1931
+ events.some((event) => event.type === "timeline" ||
1932
+ event.type === "permission_requested" ||
1933
+ event.type === "permission_resolved")) {
1934
+ this.activeForegroundTurn.hasVisibleActivity = true;
1935
+ this.pendingInterruptAbort = false;
1936
+ }
1937
+ if (routeToForeground) {
1938
+ this.dispatchForegroundEvents(events);
1939
+ return;
2364
1940
  }
1941
+ this.dispatchLiveEvents(events);
2365
1942
  }
2366
1943
  async handleMissingResumedConversation(message, query) {
2367
1944
  const staleResumeError = this.readMissingResumedConversationError(message);
@@ -2372,10 +1949,7 @@ class ClaudeAgentSession {
2372
1949
  claudeSessionId: this.claudeSessionId,
2373
1950
  error: staleResumeError,
2374
1951
  }, "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");
1952
+ this.failActiveTurns(staleResumeError);
2379
1953
  this.input?.end();
2380
1954
  await this.awaitWithTimeout(query.return?.(), "query pump return on missing resumed conversation");
2381
1955
  if (this.query === query) {
@@ -2386,373 +1960,36 @@ class ClaudeAgentSession {
2386
1960
  this.persistence = null;
2387
1961
  this.persistedHistory = [];
2388
1962
  this.historyPending = false;
1963
+ this.historyOffsetSessionId = null;
1964
+ this.historyReadOffsetBytes = 0;
1965
+ this.historyLineFragment = "";
2389
1966
  this.cachedRuntimeInfo = null;
2390
1967
  this.queryRestartNeeded = false;
1968
+ this.autonomousTurn = null;
1969
+ this.activeForegroundTurn = null;
1970
+ this.syncTurnState("missing resumed conversation");
2391
1971
  return true;
2392
1972
  }
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
1973
  async interruptActiveTurn() {
2509
1974
  const queryToInterrupt = this.query;
2510
1975
  if (!queryToInterrupt || typeof queryToInterrupt.interrupt !== "function") {
2511
1976
  this.logger.trace("interruptActiveTurn: no query to interrupt");
2512
1977
  return;
2513
1978
  }
1979
+ this.pendingInterruptAbort = true;
2514
1980
  try {
2515
- this.logger.trace("interruptActiveTurn: calling query.interrupt()...");
2516
1981
  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
1982
  }
2524
1983
  catch (error) {
2525
1984
  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
1985
  }
2721
1986
  }
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
1987
  translateMessageToEvents(message, options) {
2751
1988
  const parentToolUseId = "parent_tool_use_id" in message
2752
1989
  ? message.parent_tool_use_id
2753
1990
  : null;
2754
1991
  if (parentToolUseId) {
2755
- return this.handleSidechainMessage(message, parentToolUseId);
1992
+ return this.sidechainTracker.handleMessage(message, parentToolUseId);
2756
1993
  }
2757
1994
  const events = [];
2758
1995
  const fallbackThreadSessionId = this.captureSessionIdFromMessage(message);
@@ -2818,9 +2055,7 @@ class ClaudeAgentSession {
2818
2055
  this.compacting = false;
2819
2056
  break;
2820
2057
  }
2821
- const messageId = typeof message.uuid === "string" && message.uuid.length > 0
2822
- ? message.uuid
2823
- : undefined;
2058
+ const messageId = typeof message.uuid === "string" && message.uuid.length > 0 ? message.uuid : undefined;
2824
2059
  this.rememberUserMessageId(messageId);
2825
2060
  const content = message.message?.content;
2826
2061
  const taskNotificationItem = mapTaskNotificationUserContentToToolCall({
@@ -2836,19 +2071,22 @@ class ClaudeAgentSession {
2836
2071
  break;
2837
2072
  }
2838
2073
  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
- });
2074
+ if (!isClaudeTranscriptNoiseText(content)) {
2075
+ events.push({
2076
+ type: "timeline",
2077
+ item: {
2078
+ type: "user_message",
2079
+ text: content,
2080
+ ...(messageId ? { messageId } : {}),
2081
+ },
2082
+ provider: "claude",
2083
+ });
2084
+ }
2849
2085
  }
2850
2086
  else if (Array.isArray(content)) {
2851
- const timelineItems = this.mapBlocksToTimeline(content);
2087
+ const timelineItems = this.mapBlocksToTimeline(content, {
2088
+ textMessageType: "user_message",
2089
+ });
2852
2090
  for (const item of timelineItems) {
2853
2091
  if (item.type === "user_message" && messageId && !item.messageId) {
2854
2092
  events.push({
@@ -2884,9 +2122,6 @@ class ClaudeAgentSession {
2884
2122
  break;
2885
2123
  }
2886
2124
  case "result": {
2887
- if (options?.suppressTerminalEvents) {
2888
- break;
2889
- }
2890
2125
  const usage = this.convertUsage(message);
2891
2126
  if (message.subtype === "success") {
2892
2127
  events.push({ type: "turn_completed", provider: "claude", usage });
@@ -2948,18 +2183,14 @@ class ClaudeAgentSession {
2948
2183
  const existingSessionId = this.claudeSessionId;
2949
2184
  let threadStartedSessionId = null;
2950
2185
  if (existingSessionId === null) {
2951
- // First time setting session ID (empty → filled) - this is expected
2952
2186
  this.claudeSessionId = newSessionId;
2953
2187
  threadStartedSessionId = newSessionId;
2954
2188
  this.logger.debug({ sessionId: newSessionId }, "Claude session ID set for the first time");
2955
2189
  }
2956
2190
  else if (existingSessionId === newSessionId) {
2957
- // Same session ID - no-op, but log for visibility
2958
2191
  this.logger.debug({ sessionId: newSessionId }, "Claude session ID unchanged (same value)");
2959
2192
  }
2960
2193
  else {
2961
- // CRITICAL: Session ID is being overwritten with a different value
2962
- // This should NEVER happen and indicates a serious bug
2963
2194
  throw new Error(`CRITICAL: Claude session ID overwrite detected! ` +
2964
2195
  `Existing: ${existingSessionId}, New: ${newSessionId}. ` +
2965
2196
  `This indicates a session identity corruption bug.`);
@@ -2967,7 +2198,6 @@ class ClaudeAgentSession {
2967
2198
  this.availableModes = DEFAULT_MODES;
2968
2199
  this.currentMode = message.permissionMode;
2969
2200
  this.persistence = null;
2970
- // Capture actual model from SDK init message (not just the configured model)
2971
2201
  if (message.model) {
2972
2202
  const normalizedModel = normalizeClaudeRuntimeModelId({
2973
2203
  runtimeModelId: message.model,
@@ -2978,7 +2208,6 @@ class ClaudeAgentSession {
2978
2208
  });
2979
2209
  this.logger.debug({ model: message.model, normalizedModel }, "Captured model from SDK init");
2980
2210
  this.lastOptionsModel = normalizedModel;
2981
- // Invalidate cached runtime info so it picks up the new model
2982
2211
  this.cachedRuntimeInfo = null;
2983
2212
  }
2984
2213
  return threadStartedSessionId;
@@ -3031,7 +2260,7 @@ class ClaudeAgentSession {
3031
2260
  }
3032
2261
  }
3033
2262
  this.toolUseCache.clear();
3034
- this.activeSidechains.clear();
2263
+ this.sidechainTracker.clear();
3035
2264
  }
3036
2265
  pushToolCall(item, target) {
3037
2266
  if (!item) {
@@ -3046,15 +2275,8 @@ class ClaudeAgentSession {
3046
2275
  pushEvent(event) {
3047
2276
  const foregroundTurn = this.activeForegroundTurn;
3048
2277
  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;
2278
+ foregroundTurn.queue.push(event);
2279
+ return;
3058
2280
  }
3059
2281
  this.liveEventQueue.push(event);
3060
2282
  }
@@ -3072,47 +2294,125 @@ class ClaudeAgentSession {
3072
2294
  this.pendingPermissions.delete(id);
3073
2295
  }
3074
2296
  }
3075
- waitForLiveHistoryPoll() {
3076
- return new Promise((resolve) => setTimeout(resolve, 250));
3077
- }
3078
- loadPersistedHistory(sessionId) {
2297
+ loadPersistedHistory(sessionId, options) {
3079
2298
  try {
3080
2299
  const historyPath = this.resolveHistoryPath(sessionId);
3081
2300
  if (!historyPath || !fs.existsSync(historyPath)) {
3082
2301
  return;
3083
2302
  }
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
- }
2303
+ if (this.historyOffsetSessionId !== sessionId) {
2304
+ this.historyOffsetSessionId = sessionId;
2305
+ this.historyReadOffsetBytes = 0;
2306
+ this.historyLineFragment = "";
3106
2307
  }
3107
- if (timeline.length > 0) {
3108
- this.persistedHistory = timeline;
3109
- this.historyPending = true;
2308
+ const content = fs.readFileSync(historyPath);
2309
+ if (content.byteLength < this.historyReadOffsetBytes) {
2310
+ this.historyReadOffsetBytes = 0;
2311
+ this.historyLineFragment = "";
3110
2312
  }
2313
+ if (content.byteLength === this.historyReadOffsetBytes) {
2314
+ return;
2315
+ }
2316
+ const unreadChunk = content.subarray(this.historyReadOffsetBytes).toString("utf8");
2317
+ this.historyReadOffsetBytes = content.byteLength;
2318
+ this.ingestPersistedHistoryChunk(unreadChunk, {
2319
+ dispatchLive: options?.dispatchLive ?? false,
2320
+ });
3111
2321
  }
3112
2322
  catch (error) {
3113
2323
  // ignore history load failures
3114
2324
  }
3115
2325
  }
2326
+ ingestPersistedHistoryChunk(chunk, options) {
2327
+ if (!chunk) {
2328
+ return;
2329
+ }
2330
+ const combined = `${this.historyLineFragment}${chunk}`;
2331
+ this.historyLineFragment = "";
2332
+ const lines = combined.split(/\r?\n/);
2333
+ const trailing = lines.pop() ?? "";
2334
+ const timeline = [];
2335
+ for (const line of lines) {
2336
+ this.ingestPersistedHistoryLine(line, {
2337
+ dispatchLive: options.dispatchLive,
2338
+ timeline,
2339
+ });
2340
+ }
2341
+ if (trailing.trim().length > 0) {
2342
+ const handled = this.ingestPersistedHistoryLine(trailing, {
2343
+ dispatchLive: options.dispatchLive,
2344
+ timeline,
2345
+ });
2346
+ if (!handled) {
2347
+ this.historyLineFragment = trailing;
2348
+ }
2349
+ }
2350
+ if (!options.dispatchLive && timeline.length > 0) {
2351
+ this.persistedHistory = [...this.persistedHistory, ...timeline];
2352
+ this.historyPending = true;
2353
+ }
2354
+ }
2355
+ ingestPersistedHistoryLine(line, options) {
2356
+ const trimmed = line.trim();
2357
+ if (!trimmed) {
2358
+ return true;
2359
+ }
2360
+ let entry;
2361
+ try {
2362
+ entry = JSON.parse(trimmed);
2363
+ }
2364
+ catch {
2365
+ return false;
2366
+ }
2367
+ if (entry.isSidechain) {
2368
+ return true;
2369
+ }
2370
+ if (entry.type === "user" && typeof entry.uuid === "string") {
2371
+ this.rememberUserMessageId(entry.uuid);
2372
+ }
2373
+ if (options.dispatchLive) {
2374
+ this.dispatchPersistedHistoryEntry(entry);
2375
+ return true;
2376
+ }
2377
+ const items = this.convertHistoryEntry(entry);
2378
+ if (items.length > 0) {
2379
+ options.timeline.push(...items);
2380
+ }
2381
+ return true;
2382
+ }
2383
+ dispatchPersistedHistoryEntry(entry) {
2384
+ const liveMessage = this.normalizePersistedHistoryEntryToLiveMessage(entry);
2385
+ if (liveMessage) {
2386
+ this.routeSdkMessageFromPump(liveMessage);
2387
+ return;
2388
+ }
2389
+ const items = this.convertHistoryEntry(entry);
2390
+ for (const item of items) {
2391
+ this.pushEvent({
2392
+ type: "timeline",
2393
+ item,
2394
+ provider: "claude",
2395
+ });
2396
+ }
2397
+ }
2398
+ normalizePersistedHistoryEntryToLiveMessage(entry) {
2399
+ const taskNotificationMessage = coerceTaskNotificationHistoryRecordToSystemMessage(entry);
2400
+ if (taskNotificationMessage) {
2401
+ return taskNotificationMessage;
2402
+ }
2403
+ const type = readTrimmedString(entry.type);
2404
+ switch (type) {
2405
+ case "assistant":
2406
+ case "result":
2407
+ case "stream_event":
2408
+ case "system":
2409
+ case "tool_progress":
2410
+ case "user":
2411
+ return entry;
2412
+ default:
2413
+ return null;
2414
+ }
2415
+ }
3116
2416
  resolveHistoryPath(sessionId) {
3117
2417
  const cwd = this.config.cwd;
3118
2418
  if (!cwd)
@@ -3126,25 +2426,50 @@ class ClaudeAgentSession {
3126
2426
  convertHistoryEntry(entry) {
3127
2427
  return convertClaudeHistoryEntry(entry, (content) => this.mapBlocksToTimeline(content));
3128
2428
  }
2429
+ // Maps Claude content blocks into AgentTimelineItems.
2430
+ //
2431
+ // textMessageType controls what type text blocks emit:
2432
+ // - "assistant_message" (default): one item per text block (streaming granularity)
2433
+ // - "user_message": coalesces all text blocks into a single user_message
2434
+ // (matches extractUserMessageText semantics: trim each block, join with "\n\n")
2435
+ //
2436
+ // suppressAssistantText only applies when textMessageType is "assistant_message" — user text
2437
+ // must never be suppressed since the TimelineAssembler only handles assistant text.
2438
+ //
2439
+ // NOTE: convertClaudeHistoryEntry uses extractUserMessageText directly instead of this function
2440
+ // for user entries. Both paths must produce equivalent user_message items.
3129
2441
  mapBlocksToTimeline(content, options) {
3130
- const suppressAssistant = options?.suppressAssistantText ?? false;
2442
+ const textMessageType = options?.textMessageType ?? "assistant_message";
2443
+ const suppressText = textMessageType === "assistant_message" && (options?.suppressAssistantText ?? false);
3131
2444
  const suppressReasoning = options?.suppressReasoning ?? false;
3132
2445
  if (typeof content === "string") {
3133
- if (!content || content === INTERRUPT_TOOL_USE_PLACEHOLDER) {
2446
+ if (!content ||
2447
+ content === INTERRUPT_TOOL_USE_PLACEHOLDER ||
2448
+ isClaudeTranscriptNoiseText(content)) {
3134
2449
  return [];
3135
2450
  }
3136
- if (suppressAssistant) {
2451
+ if (suppressText) {
3137
2452
  return [];
3138
2453
  }
3139
- return [{ type: "assistant_message", text: content }];
2454
+ return [{ type: textMessageType, text: content }];
3140
2455
  }
3141
2456
  const items = [];
2457
+ // User SDK entries can arrive as multiple text blocks, but Paseo treats them as one message.
2458
+ const userTextParts = [];
3142
2459
  for (const block of content) {
3143
2460
  switch (block.type) {
3144
2461
  case "text":
3145
2462
  case "text_delta":
3146
- if (block.text && block.text !== INTERRUPT_TOOL_USE_PLACEHOLDER) {
3147
- if (!suppressAssistant) {
2463
+ if (block.text &&
2464
+ block.text !== INTERRUPT_TOOL_USE_PLACEHOLDER &&
2465
+ !isClaudeTranscriptNoiseText(block.text)) {
2466
+ if (textMessageType === "user_message") {
2467
+ const trimmed = block.text.trim();
2468
+ if (trimmed) {
2469
+ userTextParts.push(trimmed);
2470
+ }
2471
+ }
2472
+ else if (!suppressText) {
3148
2473
  items.push({ type: "assistant_message", text: block.text });
3149
2474
  }
3150
2475
  }
@@ -3177,6 +2502,12 @@ class ClaudeAgentSession {
3177
2502
  break;
3178
2503
  }
3179
2504
  }
2505
+ if (textMessageType === "user_message" && userTextParts.length > 0) {
2506
+ items.unshift({
2507
+ type: "user_message",
2508
+ text: userTextParts.join("\n\n"),
2509
+ });
2510
+ }
3180
2511
  return items;
3181
2512
  }
3182
2513
  handleToolUseStart(block, items) {
@@ -3201,7 +2532,7 @@ class ClaudeAgentSession {
3201
2532
  const toolName = entry?.name ?? block.tool_name ?? "tool";
3202
2533
  const callId = typeof block.tool_use_id === "string" && block.tool_use_id.length > 0
3203
2534
  ? block.tool_use_id
3204
- : entry?.id ?? null;
2535
+ : (entry?.id ?? null);
3205
2536
  // Extract output from block.content (SDK always returns content in string form)
3206
2537
  const output = this.buildToolOutput(block, entry);
3207
2538
  if (block.is_error) {
@@ -3223,7 +2554,7 @@ class ClaudeAgentSession {
3223
2554
  }
3224
2555
  if (typeof block.tool_use_id === "string") {
3225
2556
  this.toolUseCache.delete(block.tool_use_id);
3226
- this.activeSidechains.delete(block.tool_use_id);
2557
+ this.sidechainTracker.delete(block.tool_use_id);
3227
2558
  }
3228
2559
  }
3229
2560
  buildToolOutput(block, entry) {
@@ -3296,12 +2627,24 @@ class ClaudeAgentSession {
3296
2627
  normalizedTool === "apply_diff") {
3297
2628
  if (input && typeof input.file_path === "string") {
3298
2629
  // 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;
2630
+ const oldContent = typeof input.old_str === "string"
2631
+ ? input.old_str
2632
+ : typeof input.old_string === "string"
2633
+ ? input.old_string
2634
+ : undefined;
2635
+ const newContent = typeof input.new_str === "string"
2636
+ ? input.new_str
2637
+ : typeof input.new_string === "string"
2638
+ ? input.new_string
2639
+ : undefined;
3301
2640
  return {
3302
2641
  type: "file_edit",
3303
2642
  filePath: input.file_path,
3304
- diff: typeof input.patch === "string" ? input.patch : typeof input.diff === "string" ? input.diff : undefined,
2643
+ diff: typeof input.patch === "string"
2644
+ ? input.patch
2645
+ : typeof input.diff === "string"
2646
+ ? input.diff
2647
+ : undefined,
3305
2648
  oldContent,
3306
2649
  newContent,
3307
2650
  };
@@ -3324,7 +2667,9 @@ class ClaudeAgentSession {
3324
2667
  mapPartialEvent(event, options) {
3325
2668
  if (event.type === "content_block_start") {
3326
2669
  const block = isClaudeContentChunk(event.content_block) ? event.content_block : null;
3327
- if (block?.type === "tool_use" && typeof event.index === "number" && typeof block.id === "string") {
2670
+ if (block?.type === "tool_use" &&
2671
+ typeof event.index === "number" &&
2672
+ typeof block.id === "string") {
3328
2673
  this.toolUseIndexToId.set(event.index, block.id);
3329
2674
  this.toolUseInputBuffers.delete(block.id);
3330
2675
  }
@@ -3389,7 +2734,9 @@ class ClaudeAgentSession {
3389
2734
  else if (!existing.server) {
3390
2735
  existing.server = existing.name;
3391
2736
  }
3392
- if (block.type === "tool_use" || block.type === "mcp_tool_use" || block.type === "server_tool_use") {
2737
+ if (block.type === "tool_use" ||
2738
+ block.type === "mcp_tool_use" ||
2739
+ block.type === "server_tool_use") {
3393
2740
  const input = this.normalizeToolInput(block.input);
3394
2741
  if (input) {
3395
2742
  this.applyToolInput(existing, input);
@@ -3465,7 +2812,10 @@ class ClaudeAgentSession {
3465
2812
  }
3466
2813
  isCommandTool(name, input) {
3467
2814
  const normalized = name.toLowerCase();
3468
- if (normalized.includes("bash") || normalized.includes("shell") || normalized.includes("terminal") || normalized.includes("command")) {
2815
+ if (normalized.includes("bash") ||
2816
+ normalized.includes("shell") ||
2817
+ normalized.includes("terminal") ||
2818
+ normalized.includes("command")) {
3469
2819
  return true;
3470
2820
  }
3471
2821
  if (typeof input.command === "string" || Array.isArray(input.command)) {
@@ -3509,7 +2859,10 @@ class ClaudeAgentSession {
3509
2859
  const files = [];
3510
2860
  for (const value of input.files) {
3511
2861
  if (typeof value === "string" && value.length > 0) {
3512
- files.push({ path: this.relativizePath(value) ?? value, kind: this.detectFileKind(value) });
2862
+ files.push({
2863
+ path: this.relativizePath(value) ?? value,
2864
+ kind: this.detectFileKind(value),
2865
+ });
3513
2866
  }
3514
2867
  }
3515
2868
  if (files.length > 0) {
@@ -3572,11 +2925,7 @@ function hasToolLikeBlock(block) {
3572
2925
  return type.includes("tool");
3573
2926
  }
3574
2927
  function readCompactionMetadata(source) {
3575
- const candidates = [
3576
- source.compact_metadata,
3577
- source.compactMetadata,
3578
- source.compactionMetadata,
3579
- ];
2928
+ const candidates = [source.compact_metadata, source.compactMetadata, source.compactionMetadata];
3580
2929
  for (const candidate of candidates) {
3581
2930
  if (!candidate || typeof candidate !== "object") {
3582
2931
  continue;
@@ -3602,18 +2951,18 @@ function normalizeHistoryBlocks(content) {
3602
2951
  export function convertClaudeHistoryEntry(entry, mapBlocks) {
3603
2952
  if (entry.type === "system" && entry.subtype === "compact_boundary") {
3604
2953
  const compactMetadata = readCompactionMetadata(entry);
3605
- return [{
2954
+ return [
2955
+ {
3606
2956
  type: "compaction",
3607
2957
  status: "completed",
3608
2958
  trigger: compactMetadata?.trigger === "manual" ? "manual" : "auto",
3609
2959
  preTokens: compactMetadata?.preTokens,
3610
- }];
2960
+ },
2961
+ ];
3611
2962
  }
3612
- if (entry.type === "system") {
3613
- const taskNotificationItem = mapTaskNotificationSystemRecordToToolCall(entry);
3614
- if (taskNotificationItem) {
3615
- return [taskNotificationItem];
3616
- }
2963
+ const taskNotificationItem = mapTaskNotificationSystemRecordToToolCall(entry);
2964
+ if (taskNotificationItem) {
2965
+ return [taskNotificationItem];
3617
2966
  }
3618
2967
  if (entry.isCompactSummary) {
3619
2968
  return [];
@@ -3626,10 +2975,12 @@ export function convertClaudeHistoryEntry(entry, mapBlocks) {
3626
2975
  return [];
3627
2976
  }
3628
2977
  const content = message.content;
2978
+ if ((entry.type === "user" || entry.type === "assistant") &&
2979
+ isClaudeTranscriptNoiseContent(content)) {
2980
+ return [];
2981
+ }
3629
2982
  const normalizedBlocks = normalizeHistoryBlocks(content);
3630
- const contentValue = typeof content === "string"
3631
- ? content
3632
- : normalizedBlocks;
2983
+ const contentValue = typeof content === "string" ? content : normalizedBlocks;
3633
2984
  const hasToolBlock = normalizedBlocks?.some((block) => hasToolLikeBlock(block)) ?? false;
3634
2985
  const userMessageId = entry.type === "user" && typeof entry.uuid === "string" && entry.uuid.length > 0
3635
2986
  ? entry.uuid
@@ -3762,9 +3113,7 @@ async function collectRecentClaudeSessions(root, limit) {
3762
3113
  }
3763
3114
  }
3764
3115
  }
3765
- return candidates
3766
- .sort((a, b) => b.mtime.getTime() - a.mtime.getTime())
3767
- .slice(0, limit);
3116
+ return candidates.sort((a, b) => b.mtime.getTime() - a.mtime.getTime()).slice(0, limit);
3768
3117
  }
3769
3118
  async function parseClaudeSessionDescriptor(filePath, mtime) {
3770
3119
  let content;
@@ -3847,15 +3196,20 @@ function extractClaudeUserText(message) {
3847
3196
  return null;
3848
3197
  }
3849
3198
  if (typeof message.content === "string") {
3850
- return message.content.trim();
3199
+ const normalized = message.content.trim();
3200
+ return normalized && !isClaudeTranscriptNoiseText(normalized) ? normalized : null;
3851
3201
  }
3852
3202
  if (typeof message.text === "string") {
3853
- return message.text.trim();
3203
+ const normalized = message.text.trim();
3204
+ return normalized && !isClaudeTranscriptNoiseText(normalized) ? normalized : null;
3854
3205
  }
3855
3206
  if (Array.isArray(message.content)) {
3856
3207
  for (const block of message.content) {
3857
3208
  if (block && typeof block.text === "string") {
3858
- return block.text.trim();
3209
+ const normalized = block.text.trim();
3210
+ if (normalized && !isClaudeTranscriptNoiseText(normalized)) {
3211
+ return normalized;
3212
+ }
3859
3213
  }
3860
3214
  }
3861
3215
  }