@getpaseo/server 0.1.29 → 0.1.32

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