@getpaseo/server 0.1.2

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 (475) hide show
  1. package/.env.example +20 -0
  2. package/README.md +107 -0
  3. package/agent-prompt.md +339 -0
  4. package/dist/scripts/daemon-runner.js +32 -0
  5. package/dist/scripts/daemon-runner.js.map +1 -0
  6. package/dist/scripts/dev-runner.js +19 -0
  7. package/dist/scripts/dev-runner.js.map +1 -0
  8. package/dist/scripts/mcp-stdio-socket-bridge-cli.mjs +62 -0
  9. package/dist/scripts/supervisor.js +95 -0
  10. package/dist/scripts/supervisor.js.map +1 -0
  11. package/dist/server/client/daemon-client.d.ts +383 -0
  12. package/dist/server/client/daemon-client.d.ts.map +1 -0
  13. package/dist/server/client/daemon-client.js +2443 -0
  14. package/dist/server/client/daemon-client.js.map +1 -0
  15. package/dist/server/server/agent/activity-curator.d.ts +8 -0
  16. package/dist/server/server/agent/activity-curator.d.ts.map +1 -0
  17. package/dist/server/server/agent/activity-curator.js +228 -0
  18. package/dist/server/server/agent/activity-curator.js.map +1 -0
  19. package/dist/server/server/agent/agent-management-mcp.d.ts +34 -0
  20. package/dist/server/server/agent/agent-management-mcp.d.ts.map +1 -0
  21. package/dist/server/server/agent/agent-management-mcp.js +619 -0
  22. package/dist/server/server/agent/agent-management-mcp.js.map +1 -0
  23. package/dist/server/server/agent/agent-manager.d.ts +182 -0
  24. package/dist/server/server/agent/agent-manager.d.ts.map +1 -0
  25. package/dist/server/server/agent/agent-manager.js +1066 -0
  26. package/dist/server/server/agent/agent-manager.js.map +1 -0
  27. package/dist/server/server/agent/agent-metadata-generator.d.ts +29 -0
  28. package/dist/server/server/agent/agent-metadata-generator.d.ts.map +1 -0
  29. package/dist/server/server/agent/agent-metadata-generator.js +157 -0
  30. package/dist/server/server/agent/agent-metadata-generator.js.map +1 -0
  31. package/dist/server/server/agent/agent-projections.d.ts +12 -0
  32. package/dist/server/server/agent/agent-projections.d.ts.map +1 -0
  33. package/dist/server/server/agent/agent-projections.js +238 -0
  34. package/dist/server/server/agent/agent-projections.js.map +1 -0
  35. package/dist/server/server/agent/agent-response-loop.d.ts +32 -0
  36. package/dist/server/server/agent/agent-response-loop.d.ts.map +1 -0
  37. package/dist/server/server/agent/agent-response-loop.js +224 -0
  38. package/dist/server/server/agent/agent-response-loop.js.map +1 -0
  39. package/dist/server/server/agent/agent-sdk-types.d.ts +360 -0
  40. package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -0
  41. package/dist/server/server/agent/agent-sdk-types.js +2 -0
  42. package/dist/server/server/agent/agent-sdk-types.js.map +1 -0
  43. package/dist/server/server/agent/agent-storage.d.ts +187 -0
  44. package/dist/server/server/agent/agent-storage.d.ts.map +1 -0
  45. package/dist/server/server/agent/agent-storage.js +328 -0
  46. package/dist/server/server/agent/agent-storage.js.map +1 -0
  47. package/dist/server/server/agent/audio-utils.d.ts +3 -0
  48. package/dist/server/server/agent/audio-utils.d.ts.map +1 -0
  49. package/dist/server/server/agent/audio-utils.js +19 -0
  50. package/dist/server/server/agent/audio-utils.js.map +1 -0
  51. package/dist/server/server/agent/dictation-debug.d.ts +13 -0
  52. package/dist/server/server/agent/dictation-debug.d.ts.map +1 -0
  53. package/dist/server/server/agent/dictation-debug.js +50 -0
  54. package/dist/server/server/agent/dictation-debug.js.map +1 -0
  55. package/dist/server/server/agent/llm-openai.d.ts +7 -0
  56. package/dist/server/server/agent/llm-openai.d.ts.map +1 -0
  57. package/dist/server/server/agent/llm-openai.js +8 -0
  58. package/dist/server/server/agent/llm-openai.js.map +1 -0
  59. package/dist/server/server/agent/mcp-server.d.ts +26 -0
  60. package/dist/server/server/agent/mcp-server.d.ts.map +1 -0
  61. package/dist/server/server/agent/mcp-server.js +762 -0
  62. package/dist/server/server/agent/mcp-server.js.map +1 -0
  63. package/dist/server/server/agent/model-resolver.d.ts +11 -0
  64. package/dist/server/server/agent/model-resolver.d.ts.map +1 -0
  65. package/dist/server/server/agent/model-resolver.js +21 -0
  66. package/dist/server/server/agent/model-resolver.js.map +1 -0
  67. package/dist/server/server/agent/orchestrator-instructions.d.ts +7 -0
  68. package/dist/server/server/agent/orchestrator-instructions.d.ts.map +1 -0
  69. package/dist/server/server/agent/orchestrator-instructions.js +51 -0
  70. package/dist/server/server/agent/orchestrator-instructions.js.map +1 -0
  71. package/dist/server/server/agent/orchestrator.d.ts +12 -0
  72. package/dist/server/server/agent/orchestrator.d.ts.map +1 -0
  73. package/dist/server/server/agent/orchestrator.js +12 -0
  74. package/dist/server/server/agent/orchestrator.js.map +1 -0
  75. package/dist/server/server/agent/pcm16-resampler.d.ts +14 -0
  76. package/dist/server/server/agent/pcm16-resampler.d.ts.map +1 -0
  77. package/dist/server/server/agent/pcm16-resampler.js +63 -0
  78. package/dist/server/server/agent/pcm16-resampler.js.map +1 -0
  79. package/dist/server/server/agent/provider-launch-config.d.ts +139 -0
  80. package/dist/server/server/agent/provider-launch-config.d.ts.map +1 -0
  81. package/dist/server/server/agent/provider-launch-config.js +83 -0
  82. package/dist/server/server/agent/provider-launch-config.js.map +1 -0
  83. package/dist/server/server/agent/provider-manifest.d.ts +20 -0
  84. package/dist/server/server/agent/provider-manifest.d.ts.map +1 -0
  85. package/dist/server/server/agent/provider-manifest.js +97 -0
  86. package/dist/server/server/agent/provider-manifest.js.map +1 -0
  87. package/dist/server/server/agent/provider-registry.d.ts +18 -0
  88. package/dist/server/server/agent/provider-registry.d.ts.map +1 -0
  89. package/dist/server/server/agent/provider-registry.js +45 -0
  90. package/dist/server/server/agent/provider-registry.js.map +1 -0
  91. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts +3 -0
  92. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts.map +1 -0
  93. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js +42 -0
  94. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js.map +1 -0
  95. package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts +16 -0
  96. package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts.map +1 -0
  97. package/dist/server/server/agent/providers/claude/tool-call-mapper.js +73 -0
  98. package/dist/server/server/agent/providers/claude/tool-call-mapper.js.map +1 -0
  99. package/dist/server/server/agent/providers/claude-agent.d.ts +35 -0
  100. package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -0
  101. package/dist/server/server/agent/providers/claude-agent.js +2056 -0
  102. package/dist/server/server/agent/providers/claude-agent.js.map +1 -0
  103. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.d.ts +13 -0
  104. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.d.ts.map +1 -0
  105. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.js +67 -0
  106. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.js.map +1 -0
  107. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts +15 -0
  108. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts.map +1 -0
  109. package/dist/server/server/agent/providers/codex/tool-call-mapper.js +640 -0
  110. package/dist/server/server/agent/providers/codex/tool-call-mapper.js.map +1 -0
  111. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +34 -0
  112. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -0
  113. package/dist/server/server/agent/providers/codex-app-server-agent.js +2476 -0
  114. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -0
  115. package/dist/server/server/agent/providers/codex-rollout-timeline.d.ts +9 -0
  116. package/dist/server/server/agent/providers/codex-rollout-timeline.d.ts.map +1 -0
  117. package/dist/server/server/agent/providers/codex-rollout-timeline.js +486 -0
  118. package/dist/server/server/agent/providers/codex-rollout-timeline.js.map +1 -0
  119. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts +3 -0
  120. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts.map +1 -0
  121. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js +33 -0
  122. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js.map +1 -0
  123. package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts +13 -0
  124. package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts.map +1 -0
  125. package/dist/server/server/agent/providers/opencode/tool-call-mapper.js +75 -0
  126. package/dist/server/server/agent/providers/opencode/tool-call-mapper.js.map +1 -0
  127. package/dist/server/server/agent/providers/opencode-agent.d.ts +37 -0
  128. package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -0
  129. package/dist/server/server/agent/providers/opencode-agent.js +822 -0
  130. package/dist/server/server/agent/providers/opencode-agent.js.map +1 -0
  131. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +1363 -0
  132. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -0
  133. package/dist/server/server/agent/providers/tool-call-detail-primitives.js +534 -0
  134. package/dist/server/server/agent/providers/tool-call-detail-primitives.js.map +1 -0
  135. package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts +18 -0
  136. package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts.map +1 -0
  137. package/dist/server/server/agent/providers/tool-call-mapper-utils.js +119 -0
  138. package/dist/server/server/agent/providers/tool-call-mapper-utils.js.map +1 -0
  139. package/dist/server/server/agent/recordings-debug.d.ts +3 -0
  140. package/dist/server/server/agent/recordings-debug.d.ts.map +1 -0
  141. package/dist/server/server/agent/recordings-debug.js +19 -0
  142. package/dist/server/server/agent/recordings-debug.js.map +1 -0
  143. package/dist/server/server/agent/stt-debug.d.ts +10 -0
  144. package/dist/server/server/agent/stt-debug.d.ts.map +1 -0
  145. package/dist/server/server/agent/stt-debug.js +33 -0
  146. package/dist/server/server/agent/stt-debug.js.map +1 -0
  147. package/dist/server/server/agent/stt-manager.d.ts +32 -0
  148. package/dist/server/server/agent/stt-manager.d.ts.map +1 -0
  149. package/dist/server/server/agent/stt-manager.js +231 -0
  150. package/dist/server/server/agent/stt-manager.js.map +1 -0
  151. package/dist/server/server/agent/system-prompt.d.ts +3 -0
  152. package/dist/server/server/agent/system-prompt.d.ts.map +1 -0
  153. package/dist/server/server/agent/system-prompt.js +19 -0
  154. package/dist/server/server/agent/system-prompt.js.map +1 -0
  155. package/dist/server/server/agent/tool-name-normalization.d.ts +7 -0
  156. package/dist/server/server/agent/tool-name-normalization.d.ts.map +1 -0
  157. package/dist/server/server/agent/tool-name-normalization.js +45 -0
  158. package/dist/server/server/agent/tool-name-normalization.js.map +1 -0
  159. package/dist/server/server/agent/tts-debug.d.ts +8 -0
  160. package/dist/server/server/agent/tts-debug.d.ts.map +1 -0
  161. package/dist/server/server/agent/tts-debug.js +24 -0
  162. package/dist/server/server/agent/tts-debug.js.map +1 -0
  163. package/dist/server/server/agent/tts-manager.d.ts +33 -0
  164. package/dist/server/server/agent/tts-manager.d.ts.map +1 -0
  165. package/dist/server/server/agent/tts-manager.js +261 -0
  166. package/dist/server/server/agent/tts-manager.js.map +1 -0
  167. package/dist/server/server/agent/wait-for-agent-tracker.d.ts +15 -0
  168. package/dist/server/server/agent/wait-for-agent-tracker.d.ts.map +1 -0
  169. package/dist/server/server/agent/wait-for-agent-tracker.js +53 -0
  170. package/dist/server/server/agent/wait-for-agent-tracker.js.map +1 -0
  171. package/dist/server/server/allowed-hosts.d.ts +13 -0
  172. package/dist/server/server/allowed-hosts.d.ts.map +1 -0
  173. package/dist/server/server/allowed-hosts.js +94 -0
  174. package/dist/server/server/allowed-hosts.js.map +1 -0
  175. package/dist/server/server/bootstrap.d.ts +49 -0
  176. package/dist/server/server/bootstrap.d.ts.map +1 -0
  177. package/dist/server/server/bootstrap.js +483 -0
  178. package/dist/server/server/bootstrap.js.map +1 -0
  179. package/dist/server/server/config.d.ts +13 -0
  180. package/dist/server/server/config.d.ts.map +1 -0
  181. package/dist/server/server/config.js +84 -0
  182. package/dist/server/server/config.js.map +1 -0
  183. package/dist/server/server/connection-offer.d.ts +19 -0
  184. package/dist/server/server/connection-offer.d.ts.map +1 -0
  185. package/dist/server/server/connection-offer.js +60 -0
  186. package/dist/server/server/connection-offer.js.map +1 -0
  187. package/dist/server/server/daemon-keypair.d.ts +8 -0
  188. package/dist/server/server/daemon-keypair.d.ts.map +1 -0
  189. package/dist/server/server/daemon-keypair.js +40 -0
  190. package/dist/server/server/daemon-keypair.js.map +1 -0
  191. package/dist/server/server/dictation/dictation-stream-manager.d.ts +76 -0
  192. package/dist/server/server/dictation/dictation-stream-manager.d.ts.map +1 -0
  193. package/dist/server/server/dictation/dictation-stream-manager.js +481 -0
  194. package/dist/server/server/dictation/dictation-stream-manager.js.map +1 -0
  195. package/dist/server/server/exports.d.ts +11 -0
  196. package/dist/server/server/exports.d.ts.map +1 -0
  197. package/dist/server/server/exports.js +11 -0
  198. package/dist/server/server/exports.js.map +1 -0
  199. package/dist/server/server/file-download/token-store.d.ts +25 -0
  200. package/dist/server/server/file-download/token-store.d.ts.map +1 -0
  201. package/dist/server/server/file-download/token-store.js +40 -0
  202. package/dist/server/server/file-download/token-store.js.map +1 -0
  203. package/dist/server/server/file-explorer/service.d.ts +41 -0
  204. package/dist/server/server/file-explorer/service.d.ts.map +1 -0
  205. package/dist/server/server/file-explorer/service.js +163 -0
  206. package/dist/server/server/file-explorer/service.js.map +1 -0
  207. package/dist/server/server/index.d.ts +2 -0
  208. package/dist/server/server/index.d.ts.map +1 -0
  209. package/dist/server/server/index.js +90 -0
  210. package/dist/server/server/index.js.map +1 -0
  211. package/dist/server/server/json-utils.d.ts +11 -0
  212. package/dist/server/server/json-utils.d.ts.map +1 -0
  213. package/dist/server/server/json-utils.js +45 -0
  214. package/dist/server/server/json-utils.js.map +1 -0
  215. package/dist/server/server/logger.d.ts +12 -0
  216. package/dist/server/server/logger.d.ts.map +1 -0
  217. package/dist/server/server/logger.js +29 -0
  218. package/dist/server/server/logger.js.map +1 -0
  219. package/dist/server/server/messages.d.ts +9 -0
  220. package/dist/server/server/messages.d.ts.map +1 -0
  221. package/dist/server/server/messages.js +29 -0
  222. package/dist/server/server/messages.js.map +1 -0
  223. package/dist/server/server/pairing-offer.d.ts +16 -0
  224. package/dist/server/server/pairing-offer.d.ts.map +1 -0
  225. package/dist/server/server/pairing-offer.js +45 -0
  226. package/dist/server/server/pairing-offer.js.map +1 -0
  227. package/dist/server/server/pairing-qr.d.ts +7 -0
  228. package/dist/server/server/pairing-qr.d.ts.map +1 -0
  229. package/dist/server/server/pairing-qr.js +45 -0
  230. package/dist/server/server/pairing-qr.js.map +1 -0
  231. package/dist/server/server/paseo-home.d.ts +2 -0
  232. package/dist/server/server/paseo-home.d.ts.map +1 -0
  233. package/dist/server/server/paseo-home.js +19 -0
  234. package/dist/server/server/paseo-home.js.map +1 -0
  235. package/dist/server/server/path-utils.d.ts +3 -0
  236. package/dist/server/server/path-utils.d.ts.map +1 -0
  237. package/dist/server/server/path-utils.js +20 -0
  238. package/dist/server/server/path-utils.js.map +1 -0
  239. package/dist/server/server/persisted-config.d.ts +500 -0
  240. package/dist/server/server/persisted-config.d.ts.map +1 -0
  241. package/dist/server/server/persisted-config.js +212 -0
  242. package/dist/server/server/persisted-config.js.map +1 -0
  243. package/dist/server/server/persistence-hooks.d.ts +24 -0
  244. package/dist/server/server/persistence-hooks.d.ts.map +1 -0
  245. package/dist/server/server/persistence-hooks.js +60 -0
  246. package/dist/server/server/persistence-hooks.js.map +1 -0
  247. package/dist/server/server/pid-lock.d.ts +19 -0
  248. package/dist/server/server/pid-lock.d.ts.map +1 -0
  249. package/dist/server/server/pid-lock.js +115 -0
  250. package/dist/server/server/pid-lock.js.map +1 -0
  251. package/dist/server/server/push/push-service.d.ts +21 -0
  252. package/dist/server/server/push/push-service.d.ts.map +1 -0
  253. package/dist/server/server/push/push-service.js +68 -0
  254. package/dist/server/server/push/push-service.js.map +1 -0
  255. package/dist/server/server/push/token-store.d.ts +18 -0
  256. package/dist/server/server/push/token-store.d.ts.map +1 -0
  257. package/dist/server/server/push/token-store.js +70 -0
  258. package/dist/server/server/push/token-store.js.map +1 -0
  259. package/dist/server/server/relay-transport.d.ts +22 -0
  260. package/dist/server/server/relay-transport.d.ts.map +1 -0
  261. package/dist/server/server/relay-transport.js +374 -0
  262. package/dist/server/server/relay-transport.js.map +1 -0
  263. package/dist/server/server/server-id.d.ts +17 -0
  264. package/dist/server/server/server-id.d.ts.map +1 -0
  265. package/dist/server/server/server-id.js +63 -0
  266. package/dist/server/server/server-id.js.map +1 -0
  267. package/dist/server/server/session.d.ts +360 -0
  268. package/dist/server/server/session.d.ts.map +1 -0
  269. package/dist/server/server/session.js +4615 -0
  270. package/dist/server/server/session.js.map +1 -0
  271. package/dist/server/server/speech/audio.d.ts +10 -0
  272. package/dist/server/server/speech/audio.d.ts.map +1 -0
  273. package/dist/server/server/speech/audio.js +101 -0
  274. package/dist/server/server/speech/audio.js.map +1 -0
  275. package/dist/server/server/speech/providers/local/config.d.ts +26 -0
  276. package/dist/server/server/speech/providers/local/config.d.ts.map +1 -0
  277. package/dist/server/server/speech/providers/local/config.js +93 -0
  278. package/dist/server/server/speech/providers/local/config.js.map +1 -0
  279. package/dist/server/server/speech/providers/local/models.d.ts +12 -0
  280. package/dist/server/server/speech/providers/local/models.d.ts.map +1 -0
  281. package/dist/server/server/speech/providers/local/models.js +18 -0
  282. package/dist/server/server/speech/providers/local/models.js.map +1 -0
  283. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.d.ts +24 -0
  284. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.d.ts.map +1 -0
  285. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.js +422 -0
  286. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.js.map +1 -0
  287. package/dist/server/server/speech/providers/local/runtime.d.ts +30 -0
  288. package/dist/server/server/speech/providers/local/runtime.d.ts.map +1 -0
  289. package/dist/server/server/speech/providers/local/runtime.js +254 -0
  290. package/dist/server/server/speech/providers/local/runtime.js.map +1 -0
  291. package/dist/server/server/speech/providers/local/sherpa/model-catalog.d.ts +117 -0
  292. package/dist/server/server/speech/providers/local/sherpa/model-catalog.d.ts.map +1 -0
  293. package/dist/server/server/speech/providers/local/sherpa/model-catalog.js +166 -0
  294. package/dist/server/server/speech/providers/local/sherpa/model-catalog.js.map +1 -0
  295. package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts +17 -0
  296. package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts.map +1 -0
  297. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js +151 -0
  298. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js.map +1 -0
  299. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.d.ts +28 -0
  300. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.d.ts.map +1 -0
  301. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js +68 -0
  302. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js.map +1 -0
  303. package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.d.ts +37 -0
  304. package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.d.ts.map +1 -0
  305. package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.js +79 -0
  306. package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.js.map +1 -0
  307. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-loader.d.ts +7 -0
  308. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-loader.d.ts.map +1 -0
  309. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-loader.js +11 -0
  310. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-loader.js.map +1 -0
  311. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.d.ts +7 -0
  312. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.d.ts.map +1 -0
  313. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.js +44 -0
  314. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.js.map +1 -0
  315. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.d.ts +28 -0
  316. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.d.ts.map +1 -0
  317. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js +131 -0
  318. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js.map +1 -0
  319. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.d.ts +21 -0
  320. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.d.ts.map +1 -0
  321. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js +132 -0
  322. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js.map +1 -0
  323. package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.d.ts +23 -0
  324. package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.d.ts.map +1 -0
  325. package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.js +112 -0
  326. package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.js.map +1 -0
  327. package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.d.ts +23 -0
  328. package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.d.ts.map +1 -0
  329. package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.js +140 -0
  330. package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.js.map +1 -0
  331. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.d.ts +21 -0
  332. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.d.ts.map +1 -0
  333. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.js +95 -0
  334. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.js.map +1 -0
  335. package/dist/server/server/speech/providers/openai/config.d.ts +22 -0
  336. package/dist/server/server/speech/providers/openai/config.d.ts.map +1 -0
  337. package/dist/server/server/speech/providers/openai/config.js +94 -0
  338. package/dist/server/server/speech/providers/openai/config.js.map +1 -0
  339. package/dist/server/server/speech/providers/openai/realtime-transcription-session.d.ts +42 -0
  340. package/dist/server/server/speech/providers/openai/realtime-transcription-session.d.ts.map +1 -0
  341. package/dist/server/server/speech/providers/openai/realtime-transcription-session.js +165 -0
  342. package/dist/server/server/speech/providers/openai/realtime-transcription-session.js.map +1 -0
  343. package/dist/server/server/speech/providers/openai/runtime.d.ts +27 -0
  344. package/dist/server/server/speech/providers/openai/runtime.d.ts.map +1 -0
  345. package/dist/server/server/speech/providers/openai/runtime.js +103 -0
  346. package/dist/server/server/speech/providers/openai/runtime.js.map +1 -0
  347. package/dist/server/server/speech/providers/openai/stt.d.ts +22 -0
  348. package/dist/server/server/speech/providers/openai/stt.d.ts.map +1 -0
  349. package/dist/server/server/speech/providers/openai/stt.js +208 -0
  350. package/dist/server/server/speech/providers/openai/stt.js.map +1 -0
  351. package/dist/server/server/speech/providers/openai/tts.d.ts +18 -0
  352. package/dist/server/server/speech/providers/openai/tts.d.ts.map +1 -0
  353. package/dist/server/server/speech/providers/openai/tts.js +46 -0
  354. package/dist/server/server/speech/providers/openai/tts.js.map +1 -0
  355. package/dist/server/server/speech/speech-config-resolver.d.ts +11 -0
  356. package/dist/server/server/speech/speech-config-resolver.d.ts.map +1 -0
  357. package/dist/server/server/speech/speech-config-resolver.js +64 -0
  358. package/dist/server/server/speech/speech-config-resolver.js.map +1 -0
  359. package/dist/server/server/speech/speech-provider.d.ts +59 -0
  360. package/dist/server/server/speech/speech-provider.d.ts.map +1 -0
  361. package/dist/server/server/speech/speech-provider.js +2 -0
  362. package/dist/server/server/speech/speech-provider.js.map +1 -0
  363. package/dist/server/server/speech/speech-runtime.d.ts +20 -0
  364. package/dist/server/server/speech/speech-runtime.d.ts.map +1 -0
  365. package/dist/server/server/speech/speech-runtime.js +119 -0
  366. package/dist/server/server/speech/speech-runtime.js.map +1 -0
  367. package/dist/server/server/speech/speech-types.d.ts +20 -0
  368. package/dist/server/server/speech/speech-types.d.ts.map +1 -0
  369. package/dist/server/server/speech/speech-types.js +7 -0
  370. package/dist/server/server/speech/speech-types.js.map +1 -0
  371. package/dist/server/server/terminal-mcp/index.d.ts +4 -0
  372. package/dist/server/server/terminal-mcp/index.d.ts.map +1 -0
  373. package/dist/server/server/terminal-mcp/index.js +3 -0
  374. package/dist/server/server/terminal-mcp/index.js.map +1 -0
  375. package/dist/server/server/terminal-mcp/server.d.ts +10 -0
  376. package/dist/server/server/terminal-mcp/server.d.ts.map +1 -0
  377. package/dist/server/server/terminal-mcp/server.js +217 -0
  378. package/dist/server/server/terminal-mcp/server.js.map +1 -0
  379. package/dist/server/server/terminal-mcp/terminal-manager.d.ts +123 -0
  380. package/dist/server/server/terminal-mcp/terminal-manager.d.ts.map +1 -0
  381. package/dist/server/server/terminal-mcp/terminal-manager.js +351 -0
  382. package/dist/server/server/terminal-mcp/terminal-manager.js.map +1 -0
  383. package/dist/server/server/terminal-mcp/tmux.d.ts +207 -0
  384. package/dist/server/server/terminal-mcp/tmux.d.ts.map +1 -0
  385. package/dist/server/server/terminal-mcp/tmux.js +924 -0
  386. package/dist/server/server/terminal-mcp/tmux.js.map +1 -0
  387. package/dist/server/server/types.d.ts +5 -0
  388. package/dist/server/server/types.d.ts.map +1 -0
  389. package/dist/server/server/types.js +3 -0
  390. package/dist/server/server/types.js.map +1 -0
  391. package/dist/server/server/utils/diff-highlighter.d.ts +52 -0
  392. package/dist/server/server/utils/diff-highlighter.d.ts.map +1 -0
  393. package/dist/server/server/utils/diff-highlighter.js +244 -0
  394. package/dist/server/server/utils/diff-highlighter.js.map +1 -0
  395. package/dist/server/server/utils/syntax-highlighter.d.ts +10 -0
  396. package/dist/server/server/utils/syntax-highlighter.d.ts.map +1 -0
  397. package/dist/server/server/utils/syntax-highlighter.js +141 -0
  398. package/dist/server/server/utils/syntax-highlighter.js.map +1 -0
  399. package/dist/server/server/voice-config.d.ts +14 -0
  400. package/dist/server/server/voice-config.d.ts.map +1 -0
  401. package/dist/server/server/voice-config.js +51 -0
  402. package/dist/server/server/voice-config.js.map +1 -0
  403. package/dist/server/server/voice-mcp-bridge-command.d.ts +17 -0
  404. package/dist/server/server/voice-mcp-bridge-command.d.ts.map +1 -0
  405. package/dist/server/server/voice-mcp-bridge-command.js +31 -0
  406. package/dist/server/server/voice-mcp-bridge-command.js.map +1 -0
  407. package/dist/server/server/voice-mcp-bridge.d.ts +18 -0
  408. package/dist/server/server/voice-mcp-bridge.d.ts.map +1 -0
  409. package/dist/server/server/voice-mcp-bridge.js +109 -0
  410. package/dist/server/server/voice-mcp-bridge.js.map +1 -0
  411. package/dist/server/server/voice-permission-policy.d.ts +4 -0
  412. package/dist/server/server/voice-permission-policy.d.ts.map +1 -0
  413. package/dist/server/server/voice-permission-policy.js +13 -0
  414. package/dist/server/server/voice-permission-policy.js.map +1 -0
  415. package/dist/server/server/voice-types.d.ts +17 -0
  416. package/dist/server/server/voice-types.d.ts.map +1 -0
  417. package/dist/server/server/voice-types.js +2 -0
  418. package/dist/server/server/voice-types.js.map +1 -0
  419. package/dist/server/server/websocket-server.d.ts +80 -0
  420. package/dist/server/server/websocket-server.d.ts.map +1 -0
  421. package/dist/server/server/websocket-server.js +447 -0
  422. package/dist/server/server/websocket-server.js.map +1 -0
  423. package/dist/server/shared/agent-lifecycle.d.ts +3 -0
  424. package/dist/server/shared/agent-lifecycle.d.ts.map +1 -0
  425. package/dist/server/shared/agent-lifecycle.js +8 -0
  426. package/dist/server/shared/agent-lifecycle.js.map +1 -0
  427. package/dist/server/shared/connection-offer.d.ts +62 -0
  428. package/dist/server/shared/connection-offer.d.ts.map +1 -0
  429. package/dist/server/shared/connection-offer.js +17 -0
  430. package/dist/server/shared/connection-offer.js.map +1 -0
  431. package/dist/server/shared/daemon-endpoints.d.ts +19 -0
  432. package/dist/server/shared/daemon-endpoints.d.ts.map +1 -0
  433. package/dist/server/shared/daemon-endpoints.js +98 -0
  434. package/dist/server/shared/daemon-endpoints.js.map +1 -0
  435. package/dist/server/shared/messages.d.ts +36729 -0
  436. package/dist/server/shared/messages.d.ts.map +1 -0
  437. package/dist/server/shared/messages.js +1666 -0
  438. package/dist/server/shared/messages.js.map +1 -0
  439. package/dist/server/shared/path-utils.d.ts +2 -0
  440. package/dist/server/shared/path-utils.d.ts.map +1 -0
  441. package/dist/server/shared/path-utils.js +16 -0
  442. package/dist/server/shared/path-utils.js.map +1 -0
  443. package/dist/server/shared/tool-call-display.d.ts +11 -0
  444. package/dist/server/shared/tool-call-display.d.ts.map +1 -0
  445. package/dist/server/shared/tool-call-display.js +82 -0
  446. package/dist/server/shared/tool-call-display.js.map +1 -0
  447. package/dist/server/terminal/terminal-manager.d.ts +14 -0
  448. package/dist/server/terminal/terminal-manager.d.ts.map +1 -0
  449. package/dist/server/terminal/terminal-manager.js +67 -0
  450. package/dist/server/terminal/terminal-manager.js.map +1 -0
  451. package/dist/server/terminal/terminal.d.ts +67 -0
  452. package/dist/server/terminal/terminal.d.ts.map +1 -0
  453. package/dist/server/terminal/terminal.js +190 -0
  454. package/dist/server/terminal/terminal.js.map +1 -0
  455. package/dist/server/utils/checkout-git.d.ts +138 -0
  456. package/dist/server/utils/checkout-git.d.ts.map +1 -0
  457. package/dist/server/utils/checkout-git.js +1079 -0
  458. package/dist/server/utils/checkout-git.js.map +1 -0
  459. package/dist/server/utils/path.d.ts +5 -0
  460. package/dist/server/utils/path.d.ts.map +1 -0
  461. package/dist/server/utils/path.js +15 -0
  462. package/dist/server/utils/path.js.map +1 -0
  463. package/dist/server/utils/project-icon.d.ts +39 -0
  464. package/dist/server/utils/project-icon.d.ts.map +1 -0
  465. package/dist/server/utils/project-icon.js +391 -0
  466. package/dist/server/utils/project-icon.js.map +1 -0
  467. package/dist/server/utils/worktree-metadata.d.ts +21 -0
  468. package/dist/server/utils/worktree-metadata.d.ts.map +1 -0
  469. package/dist/server/utils/worktree-metadata.js +74 -0
  470. package/dist/server/utils/worktree-metadata.js.map +1 -0
  471. package/dist/server/utils/worktree.d.ts +95 -0
  472. package/dist/server/utils/worktree.d.ts.map +1 -0
  473. package/dist/server/utils/worktree.js +568 -0
  474. package/dist/server/utils/worktree.js.map +1 -0
  475. package/package.json +108 -0
@@ -0,0 +1,2056 @@
1
+ import { execSync, spawn } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
3
+ import fs from "node:fs";
4
+ import { promises } from "node:fs";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ import { query, } from "@anthropic-ai/claude-agent-sdk";
8
+ import { mapClaudeCanceledToolCall, mapClaudeCompletedToolCall, mapClaudeFailedToolCall, mapClaudeRunningToolCall, } from "./claude/tool-call-mapper.js";
9
+ import { applyProviderEnv, isProviderCommandAvailable, } from "../provider-launch-config.js";
10
+ import { getOrchestratorModeInstructions } from "../orchestrator-instructions.js";
11
+ const fsPromises = promises;
12
+ function normalizeClaudeModelLabel(model) {
13
+ const fallback = model.displayName?.trim() || model.value;
14
+ const prefix = model.description?.split(/[·•]/)[0]?.trim() || "";
15
+ if (!prefix)
16
+ return fallback;
17
+ // Prefer concrete versioned labels from description (e.g. "Opus 4.6",
18
+ // "Sonnet 4.5"), especially when displayName is generic like
19
+ // "Default (recommended)".
20
+ if (/\d/.test(prefix)) {
21
+ return prefix;
22
+ }
23
+ return fallback;
24
+ }
25
+ const CLAUDE_CAPABILITIES = {
26
+ supportsStreaming: true,
27
+ supportsSessionPersistence: true,
28
+ supportsDynamicModes: true,
29
+ supportsMcpServers: true,
30
+ supportsReasoningStream: true,
31
+ supportsToolInvocations: true,
32
+ };
33
+ const DEFAULT_MODES = [
34
+ {
35
+ id: "default",
36
+ label: "Always Ask",
37
+ description: "Prompts for permission the first time a tool is used",
38
+ },
39
+ {
40
+ id: "acceptEdits",
41
+ label: "Accept File Edits",
42
+ description: "Automatically approves edit-focused tools without prompting",
43
+ },
44
+ {
45
+ id: "plan",
46
+ label: "Plan Mode",
47
+ description: "Analyze the codebase without executing tools or edits",
48
+ },
49
+ {
50
+ id: "bypassPermissions",
51
+ label: "Bypass",
52
+ description: "Skip all permission prompts (use with caution)",
53
+ },
54
+ ];
55
+ const VALID_CLAUDE_MODES = new Set(DEFAULT_MODES.map((mode) => mode.id));
56
+ function resolveClaudeBinary() {
57
+ try {
58
+ const claudePath = execSync("which claude", { encoding: "utf8" }).trim();
59
+ if (claudePath) {
60
+ return claudePath;
61
+ }
62
+ }
63
+ catch {
64
+ // fall through
65
+ }
66
+ throw new Error("Claude CLI not found. Install claude or configure agents.providers.claude.command.mode='replace'.");
67
+ }
68
+ function resolveClaudeSpawnCommand(spawnOptions, runtimeSettings) {
69
+ const commandConfig = runtimeSettings?.command;
70
+ if (!commandConfig || commandConfig.mode === "default") {
71
+ return {
72
+ command: spawnOptions.command,
73
+ args: [...spawnOptions.args],
74
+ };
75
+ }
76
+ if (commandConfig.mode === "append") {
77
+ return {
78
+ command: spawnOptions.command,
79
+ args: [...(commandConfig.args ?? []), ...spawnOptions.args],
80
+ };
81
+ }
82
+ return {
83
+ command: commandConfig.argv[0],
84
+ args: [...commandConfig.argv.slice(1), ...spawnOptions.args],
85
+ };
86
+ }
87
+ export function extractUserMessageText(content) {
88
+ if (typeof content === "string") {
89
+ const normalized = content.trim();
90
+ return normalized.length > 0 ? normalized : null;
91
+ }
92
+ if (!Array.isArray(content)) {
93
+ return null;
94
+ }
95
+ const parts = [];
96
+ for (const block of content) {
97
+ if (!block || typeof block !== "object") {
98
+ continue;
99
+ }
100
+ const text = typeof block.text === "string" ? block.text : undefined;
101
+ if (text && text.trim()) {
102
+ parts.push(text.trim());
103
+ continue;
104
+ }
105
+ const input = typeof block.input === "string" ? block.input : undefined;
106
+ if (input && input.trim()) {
107
+ parts.push(input.trim());
108
+ }
109
+ }
110
+ if (parts.length === 0) {
111
+ return null;
112
+ }
113
+ const combined = parts.join("\n\n").trim();
114
+ return combined.length > 0 ? combined : null;
115
+ }
116
+ const DEFAULT_PERMISSION_TIMEOUT_MS = 120000;
117
+ function isMetadata(value) {
118
+ return typeof value === "object" && value !== null;
119
+ }
120
+ function isMcpServerConfig(value) {
121
+ if (!isMetadata(value)) {
122
+ return false;
123
+ }
124
+ const type = value.type;
125
+ if (type === "stdio") {
126
+ return typeof value.command === "string";
127
+ }
128
+ if (type === "http" || type === "sse") {
129
+ return typeof value.url === "string";
130
+ }
131
+ return false;
132
+ }
133
+ function isMcpServersRecord(value) {
134
+ if (!isMetadata(value)) {
135
+ return false;
136
+ }
137
+ for (const config of Object.values(value)) {
138
+ if (!isMcpServerConfig(config)) {
139
+ return false;
140
+ }
141
+ }
142
+ return true;
143
+ }
144
+ function isPermissionMode(value) {
145
+ return typeof value === "string" && VALID_CLAUDE_MODES.has(value);
146
+ }
147
+ function coerceSessionMetadata(metadata) {
148
+ if (!isMetadata(metadata)) {
149
+ return {};
150
+ }
151
+ const result = {};
152
+ if (metadata.provider === "claude" || metadata.provider === "codex") {
153
+ result.provider = metadata.provider;
154
+ }
155
+ if (typeof metadata.cwd === "string") {
156
+ result.cwd = metadata.cwd;
157
+ }
158
+ if (typeof metadata.modeId === "string") {
159
+ result.modeId = metadata.modeId;
160
+ }
161
+ if (typeof metadata.model === "string") {
162
+ result.model = metadata.model;
163
+ }
164
+ if (typeof metadata.title === "string" || metadata.title === null) {
165
+ result.title = metadata.title;
166
+ }
167
+ if (typeof metadata.approvalPolicy === "string") {
168
+ result.approvalPolicy = metadata.approvalPolicy;
169
+ }
170
+ if (typeof metadata.sandboxMode === "string") {
171
+ result.sandboxMode = metadata.sandboxMode;
172
+ }
173
+ if (typeof metadata.networkAccess === "boolean") {
174
+ result.networkAccess = metadata.networkAccess;
175
+ }
176
+ if (typeof metadata.webSearch === "boolean") {
177
+ result.webSearch = metadata.webSearch;
178
+ }
179
+ if (isMetadata(metadata.extra)) {
180
+ const extra = {};
181
+ if (isMetadata(metadata.extra.codex)) {
182
+ extra.codex = metadata.extra.codex;
183
+ }
184
+ if (isClaudeExtra(metadata.extra.claude)) {
185
+ extra.claude = metadata.extra.claude;
186
+ }
187
+ if (extra.codex || extra.claude) {
188
+ result.extra = extra;
189
+ }
190
+ }
191
+ if (typeof metadata.systemPrompt === "string") {
192
+ result.systemPrompt = metadata.systemPrompt;
193
+ }
194
+ if (isMcpServersRecord(metadata.mcpServers)) {
195
+ result.mcpServers = metadata.mcpServers;
196
+ }
197
+ return result;
198
+ }
199
+ function toClaudeSdkMcpConfig(config) {
200
+ switch (config.type) {
201
+ case "stdio":
202
+ return {
203
+ type: "stdio",
204
+ command: config.command,
205
+ args: config.args,
206
+ env: config.env,
207
+ };
208
+ case "http":
209
+ return {
210
+ type: "http",
211
+ url: config.url,
212
+ headers: config.headers,
213
+ };
214
+ case "sse":
215
+ return {
216
+ type: "sse",
217
+ url: config.url,
218
+ headers: config.headers,
219
+ };
220
+ }
221
+ }
222
+ function isClaudeContentChunk(value) {
223
+ return isMetadata(value) && typeof value.type === "string";
224
+ }
225
+ function isClaudeExtra(value) {
226
+ return isMetadata(value);
227
+ }
228
+ function isPermissionUpdate(value) {
229
+ if (!isMetadata(value)) {
230
+ return false;
231
+ }
232
+ const type = value.type;
233
+ if (type !== "addRules" && type !== "replaceRules" && type !== "removeRules") {
234
+ return false;
235
+ }
236
+ const rules = value.rules;
237
+ const behavior = value.behavior;
238
+ const destination = value.destination;
239
+ return Array.isArray(rules) && typeof behavior === "string" && typeof destination === "string";
240
+ }
241
+ function resolvePermissionKind(toolName, input) {
242
+ if (toolName === "ExitPlanMode")
243
+ return "plan";
244
+ if (toolName === "AskUserQuestion" && Array.isArray(input.questions)) {
245
+ return "question";
246
+ }
247
+ return "tool";
248
+ }
249
+ export class ClaudeAgentClient {
250
+ constructor(options) {
251
+ this.provider = "claude";
252
+ this.capabilities = CLAUDE_CAPABILITIES;
253
+ this.defaults = options.defaults;
254
+ this.logger = options.logger.child({ module: "agent", provider: "claude" });
255
+ this.runtimeSettings = options.runtimeSettings;
256
+ try {
257
+ this.claudePath = execSync("which claude", { encoding: "utf8" }).trim() || null;
258
+ }
259
+ catch {
260
+ this.claudePath = null;
261
+ }
262
+ }
263
+ applyRuntimeSettings(options) {
264
+ const hasEnvOverrides = Object.keys(this.runtimeSettings?.env ?? {}).length > 0;
265
+ const commandMode = this.runtimeSettings?.command?.mode;
266
+ const needsCustomSpawn = hasEnvOverrides || commandMode === "append" || commandMode === "replace";
267
+ if (!needsCustomSpawn) {
268
+ return options;
269
+ }
270
+ return {
271
+ ...options,
272
+ spawnClaudeCodeProcess: (spawnOptions) => {
273
+ const resolved = resolveClaudeSpawnCommand(spawnOptions, this.runtimeSettings);
274
+ return spawn(resolved.command, resolved.args, {
275
+ cwd: spawnOptions.cwd,
276
+ env: applyProviderEnv(spawnOptions.env, this.runtimeSettings),
277
+ signal: spawnOptions.signal,
278
+ stdio: ["pipe", "pipe", "pipe"],
279
+ });
280
+ },
281
+ };
282
+ }
283
+ async createSession(config) {
284
+ const claudeConfig = this.assertConfig(config);
285
+ return new ClaudeAgentSession(claudeConfig, {
286
+ defaults: this.defaults,
287
+ claudePath: this.claudePath,
288
+ runtimeSettings: this.runtimeSettings,
289
+ logger: this.logger,
290
+ });
291
+ }
292
+ async resumeSession(handle, overrides) {
293
+ const metadata = coerceSessionMetadata(handle.metadata);
294
+ const merged = { ...metadata, ...overrides };
295
+ if (!merged.cwd) {
296
+ throw new Error("Claude resume requires the original working directory in metadata");
297
+ }
298
+ const mergedConfig = { ...merged, provider: "claude", cwd: merged.cwd };
299
+ const claudeConfig = this.assertConfig(mergedConfig);
300
+ return new ClaudeAgentSession(claudeConfig, {
301
+ defaults: this.defaults,
302
+ claudePath: this.claudePath,
303
+ runtimeSettings: this.runtimeSettings,
304
+ handle,
305
+ logger: this.logger,
306
+ });
307
+ }
308
+ async listModels(options) {
309
+ const prompt = (async function* empty() { })();
310
+ const claudeOptions = {
311
+ cwd: options?.cwd ?? process.cwd(),
312
+ permissionMode: "plan",
313
+ includePartialMessages: false,
314
+ ...(this.claudePath ? { pathToClaudeCodeExecutable: this.claudePath } : {}),
315
+ };
316
+ const claudeQuery = query({
317
+ prompt,
318
+ options: this.applyRuntimeSettings(claudeOptions),
319
+ });
320
+ try {
321
+ const models = await claudeQuery.supportedModels();
322
+ return models.map((model) => ({
323
+ provider: "claude",
324
+ id: model.value,
325
+ label: normalizeClaudeModelLabel(model),
326
+ description: model.description,
327
+ thinkingOptions: [
328
+ { id: "off", label: "Off", isDefault: true },
329
+ { id: "on", label: "On" },
330
+ ],
331
+ defaultThinkingOptionId: "off",
332
+ metadata: {
333
+ description: model.description,
334
+ },
335
+ }));
336
+ }
337
+ finally {
338
+ if (typeof claudeQuery.return === "function") {
339
+ try {
340
+ await claudeQuery.return();
341
+ }
342
+ catch {
343
+ // ignore shutdown errors
344
+ }
345
+ }
346
+ }
347
+ }
348
+ async listPersistedAgents(options) {
349
+ const configDir = process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude");
350
+ const projectsRoot = path.join(configDir, "projects");
351
+ if (!(await pathExists(projectsRoot))) {
352
+ return [];
353
+ }
354
+ const limit = options?.limit ?? 20;
355
+ const candidates = await collectRecentClaudeSessions(projectsRoot, limit * 3);
356
+ const descriptors = [];
357
+ for (const candidate of candidates) {
358
+ const descriptor = await parseClaudeSessionDescriptor(candidate.path, candidate.mtime);
359
+ if (descriptor) {
360
+ descriptors.push(descriptor);
361
+ }
362
+ if (descriptors.length >= limit) {
363
+ break;
364
+ }
365
+ }
366
+ return descriptors;
367
+ }
368
+ async isAvailable() {
369
+ const commandConfig = this.runtimeSettings?.command;
370
+ if (commandConfig?.mode === "replace") {
371
+ return isProviderCommandAvailable(commandConfig, resolveClaudeBinary);
372
+ }
373
+ return this.claudePath !== null;
374
+ }
375
+ assertConfig(config) {
376
+ if (config.provider !== "claude") {
377
+ throw new Error(`ClaudeAgentClient received config for provider '${config.provider}'`);
378
+ }
379
+ return { ...config, provider: "claude" };
380
+ }
381
+ }
382
+ class ClaudeAgentSession {
383
+ constructor(config, options) {
384
+ this.provider = "claude";
385
+ this.capabilities = CLAUDE_CAPABILITIES;
386
+ this.query = null;
387
+ this.input = null;
388
+ this.availableModes = DEFAULT_MODES;
389
+ this.toolUseCache = new Map();
390
+ this.toolUseIndexToId = new Map();
391
+ this.toolUseInputBuffers = new Map();
392
+ this.pendingPermissions = new Map();
393
+ this.eventQueue = null;
394
+ this.persistedHistory = [];
395
+ this.historyPending = false;
396
+ this.turnCancelRequested = false;
397
+ // NOTE: streamedAssistantTextThisTurn and streamedReasoningThisTurn were removed
398
+ // These flags are now tracked per-turn via TurnContext to prevent race conditions
399
+ // when multiple stream() calls overlap (e.g., interrupt + new message)
400
+ this.cancelCurrentTurn = null;
401
+ // Track the pending interrupt promise so we can await it in processPrompt
402
+ // This ensures the interrupt's response is consumed before we call query.next()
403
+ this.pendingInterruptPromise = null;
404
+ // Track the current turn ID and active turn promise to serialize concurrent stream() calls
405
+ // and prevent race conditions where two processPrompt() loops run against the same query
406
+ this.currentTurnId = 0;
407
+ this.activeTurnPromise = null;
408
+ this.cachedRuntimeInfo = null;
409
+ this.lastOptionsModel = null;
410
+ this.activeSidechains = new Map();
411
+ this.compacting = false;
412
+ this.queryRestartNeeded = false;
413
+ this.handlePermissionRequest = async (toolName, input, options) => {
414
+ const requestId = `permission-${randomUUID()}`;
415
+ const kind = resolvePermissionKind(toolName, input);
416
+ const metadata = {};
417
+ if (options.toolUseID) {
418
+ metadata.toolUseId = options.toolUseID;
419
+ }
420
+ if (toolName === "ExitPlanMode" && typeof input.plan === "string") {
421
+ metadata.planText = input.plan;
422
+ }
423
+ const detail = kind === "tool"
424
+ ? mapClaudeRunningToolCall({
425
+ name: toolName,
426
+ callId: options.toolUseID ?? requestId,
427
+ input,
428
+ output: null,
429
+ }).detail
430
+ : undefined;
431
+ const request = {
432
+ id: requestId,
433
+ provider: "claude",
434
+ name: toolName,
435
+ kind,
436
+ input,
437
+ detail,
438
+ suggestions: options.suggestions?.map((suggestion) => ({ ...suggestion })),
439
+ metadata: Object.keys(metadata).length ? metadata : undefined,
440
+ };
441
+ this.pushEvent({ type: "permission_requested", provider: "claude", request });
442
+ return await new Promise((resolve, reject) => {
443
+ const cleanupFns = [];
444
+ const cleanup = () => {
445
+ while (cleanupFns.length) {
446
+ const fn = cleanupFns.pop();
447
+ try {
448
+ fn?.();
449
+ }
450
+ catch {
451
+ // ignore cleanup errors
452
+ }
453
+ }
454
+ };
455
+ const timeout = setTimeout(() => {
456
+ this.pendingPermissions.delete(requestId);
457
+ cleanup();
458
+ const error = new Error("Permission request timed out");
459
+ this.pushEvent({
460
+ type: "permission_resolved",
461
+ provider: "claude",
462
+ requestId,
463
+ resolution: { behavior: "deny", message: "timeout" },
464
+ });
465
+ reject(error);
466
+ }, DEFAULT_PERMISSION_TIMEOUT_MS);
467
+ cleanupFns.push(() => clearTimeout(timeout));
468
+ const abortHandler = () => {
469
+ this.pendingPermissions.delete(requestId);
470
+ cleanup();
471
+ reject(new Error("Permission request aborted"));
472
+ };
473
+ if (options?.signal) {
474
+ if (options.signal.aborted) {
475
+ abortHandler();
476
+ return;
477
+ }
478
+ options.signal.addEventListener("abort", abortHandler, { once: true });
479
+ cleanupFns.push(() => options.signal?.removeEventListener("abort", abortHandler));
480
+ }
481
+ this.pendingPermissions.set(requestId, {
482
+ request,
483
+ resolve,
484
+ reject,
485
+ cleanup,
486
+ });
487
+ });
488
+ };
489
+ this.config = config;
490
+ this.defaults = options.defaults;
491
+ this.claudePath = options.claudePath;
492
+ this.runtimeSettings = options.runtimeSettings;
493
+ this.logger = options.logger;
494
+ const handle = options.handle;
495
+ if (handle) {
496
+ if (!handle.sessionId) {
497
+ throw new Error("Cannot resume: persistence handle has no sessionId");
498
+ }
499
+ this.claudeSessionId = handle.sessionId;
500
+ this.persistence = handle;
501
+ this.loadPersistedHistory(handle.sessionId);
502
+ }
503
+ else {
504
+ this.claudeSessionId = null;
505
+ this.persistence = null;
506
+ }
507
+ // Validate mode if provided
508
+ if (config.modeId && !VALID_CLAUDE_MODES.has(config.modeId)) {
509
+ const validModesList = Array.from(VALID_CLAUDE_MODES).join(", ");
510
+ throw new Error(`Invalid mode '${config.modeId}' for Claude provider. Valid modes: ${validModesList}`);
511
+ }
512
+ this.currentMode = isPermissionMode(config.modeId) ? config.modeId : "default";
513
+ }
514
+ get id() {
515
+ return this.claudeSessionId;
516
+ }
517
+ async getRuntimeInfo() {
518
+ if (this.cachedRuntimeInfo) {
519
+ return { ...this.cachedRuntimeInfo };
520
+ }
521
+ const info = {
522
+ provider: "claude",
523
+ sessionId: this.claudeSessionId,
524
+ model: this.lastOptionsModel,
525
+ modeId: this.currentMode ?? null,
526
+ };
527
+ this.cachedRuntimeInfo = info;
528
+ return { ...info };
529
+ }
530
+ async run(prompt, options) {
531
+ const events = this.stream(prompt, options);
532
+ const timeline = [];
533
+ let finalText = "";
534
+ let usage;
535
+ for await (const event of events) {
536
+ if (event.type === "timeline") {
537
+ timeline.push(event.item);
538
+ if (event.item.type === "assistant_message") {
539
+ if (!finalText) {
540
+ finalText = event.item.text;
541
+ }
542
+ else if (event.item.text.startsWith(finalText)) {
543
+ finalText = event.item.text;
544
+ }
545
+ else {
546
+ finalText += event.item.text;
547
+ }
548
+ }
549
+ }
550
+ else if (event.type === "turn_completed") {
551
+ usage = event.usage;
552
+ }
553
+ else if (event.type === "turn_failed") {
554
+ throw new Error(event.error);
555
+ }
556
+ }
557
+ this.cachedRuntimeInfo = {
558
+ provider: "claude",
559
+ sessionId: this.claudeSessionId,
560
+ model: this.lastOptionsModel,
561
+ modeId: this.currentMode ?? null,
562
+ };
563
+ if (!this.claudeSessionId) {
564
+ throw new Error("Session ID not set after run completed");
565
+ }
566
+ return {
567
+ sessionId: this.claudeSessionId,
568
+ finalText,
569
+ usage,
570
+ timeline,
571
+ };
572
+ }
573
+ async *stream(prompt, options) {
574
+ void options;
575
+ // Increment turn ID to invalidate any in-flight processPrompt() loops from previous turns.
576
+ // This prevents race conditions where an interrupted turn's events get mixed with the new turn.
577
+ const turnId = ++this.currentTurnId;
578
+ // Cancel the previous turn if one exists. The caller of interrupt() is responsible
579
+ // for awaiting completion - the new turn just signals cancellation and proceeds.
580
+ if (this.cancelCurrentTurn) {
581
+ this.cancelCurrentTurn();
582
+ }
583
+ // Reset cancel flag at the start of each turn to prevent stale state from previous turns
584
+ this.turnCancelRequested = false;
585
+ const sdkMessage = this.toSdkUserMessage(prompt);
586
+ const queue = new Pushable();
587
+ this.eventQueue = queue;
588
+ let finishedNaturally = false;
589
+ let cancelIssued = false;
590
+ const requestCancel = () => {
591
+ if (cancelIssued) {
592
+ return;
593
+ }
594
+ cancelIssued = true;
595
+ this.turnCancelRequested = true;
596
+ // Store the interrupt promise so processPrompt can await it before calling query.next()
597
+ this.pendingInterruptPromise = this.interruptActiveTurn().catch((error) => {
598
+ this.logger.warn({ err: error }, "Failed to interrupt during cancel");
599
+ });
600
+ this.flushPendingToolCalls();
601
+ // Push turn_canceled before ending the queue so consumers get proper lifecycle signals
602
+ queue.push({
603
+ type: "turn_canceled",
604
+ provider: "claude",
605
+ reason: "Interrupted",
606
+ });
607
+ queue.end();
608
+ };
609
+ this.cancelCurrentTurn = requestCancel;
610
+ if (this.historyPending && this.persistedHistory.length > 0) {
611
+ for (const item of this.persistedHistory) {
612
+ queue.push({ type: "timeline", item, provider: "claude" });
613
+ }
614
+ this.historyPending = false;
615
+ this.persistedHistory = [];
616
+ }
617
+ // Start forwarding events and track the promise so future turns can wait for completion
618
+ const forwardPromise = this.forwardPromptEvents(sdkMessage, queue, turnId);
619
+ this.activeTurnPromise = forwardPromise;
620
+ forwardPromise.catch((error) => {
621
+ this.logger.error({ err: error }, "Unexpected error in forwardPromptEvents");
622
+ });
623
+ try {
624
+ for await (const event of queue) {
625
+ yield event;
626
+ if (event.type === "turn_completed" ||
627
+ event.type === "turn_failed" ||
628
+ event.type === "turn_canceled") {
629
+ finishedNaturally = true;
630
+ break;
631
+ }
632
+ }
633
+ }
634
+ finally {
635
+ if (!finishedNaturally && !cancelIssued) {
636
+ requestCancel();
637
+ }
638
+ if (this.eventQueue === queue) {
639
+ this.eventQueue = null;
640
+ }
641
+ if (this.cancelCurrentTurn === requestCancel) {
642
+ this.cancelCurrentTurn = null;
643
+ }
644
+ // Clear the active turn promise if it's still ours
645
+ if (this.activeTurnPromise === forwardPromise) {
646
+ this.activeTurnPromise = null;
647
+ }
648
+ }
649
+ }
650
+ async interrupt() {
651
+ this.cancelCurrentTurn?.();
652
+ }
653
+ async *streamHistory() {
654
+ if (!this.historyPending || this.persistedHistory.length === 0) {
655
+ return;
656
+ }
657
+ const history = this.persistedHistory;
658
+ this.persistedHistory = [];
659
+ this.historyPending = false;
660
+ for (const item of history) {
661
+ yield { type: "timeline", item, provider: "claude" };
662
+ }
663
+ }
664
+ async getAvailableModes() {
665
+ return this.availableModes;
666
+ }
667
+ async getCurrentMode() {
668
+ return this.currentMode ?? null;
669
+ }
670
+ async setMode(modeId) {
671
+ // Validate mode
672
+ if (!VALID_CLAUDE_MODES.has(modeId)) {
673
+ const validModesList = Array.from(VALID_CLAUDE_MODES).join(", ");
674
+ throw new Error(`Invalid mode '${modeId}' for Claude provider. Valid modes: ${validModesList}`);
675
+ }
676
+ const normalized = isPermissionMode(modeId) ? modeId : "default";
677
+ const query = await this.ensureQuery();
678
+ await query.setPermissionMode(normalized);
679
+ this.currentMode = normalized;
680
+ }
681
+ async setModel(modelId) {
682
+ const normalizedModelId = typeof modelId === "string" && modelId.trim().length > 0 ? modelId : null;
683
+ const query = await this.ensureQuery();
684
+ await query.setModel(normalizedModelId ?? undefined);
685
+ this.config.model = normalizedModelId ?? undefined;
686
+ this.lastOptionsModel = normalizedModelId ?? this.lastOptionsModel;
687
+ this.cachedRuntimeInfo = null;
688
+ // Model change affects persistence metadata, so invalidate cached handle.
689
+ this.persistence = null;
690
+ }
691
+ async setThinkingOption(thinkingOptionId) {
692
+ const normalizedThinkingOptionId = typeof thinkingOptionId === "string" && thinkingOptionId.trim().length > 0
693
+ ? thinkingOptionId
694
+ : null;
695
+ if (!normalizedThinkingOptionId || normalizedThinkingOptionId === "default") {
696
+ this.config.thinkingOptionId = undefined;
697
+ }
698
+ else if (normalizedThinkingOptionId === "on") {
699
+ this.config.thinkingOptionId = "on";
700
+ }
701
+ else if (normalizedThinkingOptionId === "off") {
702
+ this.config.thinkingOptionId = "off";
703
+ }
704
+ else {
705
+ throw new Error(`Unknown thinking option: ${normalizedThinkingOptionId}`);
706
+ }
707
+ this.queryRestartNeeded = true;
708
+ }
709
+ getPendingPermissions() {
710
+ return Array.from(this.pendingPermissions.values()).map((entry) => entry.request);
711
+ }
712
+ async respondToPermission(requestId, response) {
713
+ const pending = this.pendingPermissions.get(requestId);
714
+ if (!pending) {
715
+ throw new Error(`No pending permission request with id '${requestId}'`);
716
+ }
717
+ this.pendingPermissions.delete(requestId);
718
+ pending.cleanup?.();
719
+ if (response.behavior === "allow") {
720
+ if (pending.request.kind === "plan") {
721
+ await this.setMode("acceptEdits");
722
+ this.pushToolCall(mapClaudeCompletedToolCall({
723
+ name: "plan_approval",
724
+ callId: pending.request.id,
725
+ input: pending.request.input ?? null,
726
+ output: { approved: true },
727
+ }));
728
+ }
729
+ const result = {
730
+ behavior: "allow",
731
+ updatedInput: response.updatedInput ?? pending.request.input ?? {},
732
+ updatedPermissions: this.normalizePermissionUpdates(response.updatedPermissions),
733
+ };
734
+ pending.resolve(result);
735
+ }
736
+ else {
737
+ if (pending.request.kind === "tool") {
738
+ this.pushToolCall(mapClaudeFailedToolCall({
739
+ name: pending.request.name,
740
+ callId: (typeof pending.request.metadata?.toolUseId === "string"
741
+ ? pending.request.metadata.toolUseId
742
+ : null) ?? pending.request.id,
743
+ input: pending.request.input ?? null,
744
+ output: null,
745
+ error: { message: response.message ?? "Permission denied" },
746
+ }));
747
+ }
748
+ const result = {
749
+ behavior: "deny",
750
+ message: response.message ?? "Permission request denied",
751
+ interrupt: response.interrupt,
752
+ };
753
+ pending.resolve(result);
754
+ }
755
+ this.pushEvent({
756
+ type: "permission_resolved",
757
+ provider: "claude",
758
+ requestId,
759
+ resolution: response,
760
+ });
761
+ }
762
+ describePersistence() {
763
+ if (this.persistence) {
764
+ return this.persistence;
765
+ }
766
+ if (!this.claudeSessionId) {
767
+ return null;
768
+ }
769
+ this.persistence = {
770
+ provider: "claude",
771
+ sessionId: this.claudeSessionId,
772
+ nativeHandle: this.claudeSessionId,
773
+ metadata: this.config,
774
+ };
775
+ return this.persistence;
776
+ }
777
+ async close() {
778
+ this.rejectAllPendingPermissions(new Error("Claude session closed"));
779
+ this.input?.end();
780
+ await this.query?.interrupt?.();
781
+ await this.query?.return?.();
782
+ this.query = null;
783
+ this.input = null;
784
+ }
785
+ async listCommands() {
786
+ const q = await this.ensureQuery();
787
+ const commands = await q.supportedCommands();
788
+ return commands.map((cmd) => ({
789
+ name: cmd.name,
790
+ description: cmd.description,
791
+ argumentHint: cmd.argumentHint,
792
+ }));
793
+ }
794
+ async executeCommand(commandName, args) {
795
+ const commandPrompt = args ? `/${commandName} ${args}` : `/${commandName}`;
796
+ // Commands return their output in a user message with <local-command-stdout> tags,
797
+ // NOT as an assistant message. We need to extract that specifically.
798
+ const events = this.stream(commandPrompt);
799
+ const timeline = [];
800
+ let commandOutput = "";
801
+ let usage;
802
+ for await (const event of events) {
803
+ if (event.type === "timeline") {
804
+ timeline.push(event.item);
805
+ // Check for command output in user messages
806
+ if (event.item.type === "user_message") {
807
+ const text = event.item.text;
808
+ const match = text.match(/<local-command-stdout>([\s\S]*?)<\/local-command-stdout>/);
809
+ if (match) {
810
+ commandOutput = match[1].trim();
811
+ }
812
+ }
813
+ // Also capture assistant messages (some commands may produce both)
814
+ if (event.item.type === "assistant_message" && !commandOutput) {
815
+ commandOutput = event.item.text;
816
+ }
817
+ }
818
+ else if (event.type === "turn_completed") {
819
+ usage = event.usage;
820
+ }
821
+ else if (event.type === "turn_failed") {
822
+ throw new Error(event.error);
823
+ }
824
+ }
825
+ return {
826
+ text: commandOutput,
827
+ timeline,
828
+ usage,
829
+ };
830
+ }
831
+ async ensureQuery() {
832
+ if (this.query && !this.queryRestartNeeded) {
833
+ return this.query;
834
+ }
835
+ if (this.queryRestartNeeded && this.query) {
836
+ this.input?.end();
837
+ try {
838
+ await this.query.return?.();
839
+ }
840
+ catch { /* ignore */ }
841
+ this.query = null;
842
+ this.input = null;
843
+ this.queryRestartNeeded = false;
844
+ }
845
+ const input = new Pushable();
846
+ const options = this.buildOptions();
847
+ this.logger.debug({ options }, "claude query");
848
+ this.input = input;
849
+ this.query = query({ prompt: input, options });
850
+ await this.query.setPermissionMode(this.currentMode);
851
+ return this.query;
852
+ }
853
+ buildOptions() {
854
+ const configuredThinkingOptionId = this.config.thinkingOptionId;
855
+ const thinkingOptionId = configuredThinkingOptionId && configuredThinkingOptionId !== "default"
856
+ ? configuredThinkingOptionId
857
+ : "off";
858
+ let maxThinkingTokens;
859
+ if (thinkingOptionId === "on") {
860
+ maxThinkingTokens = 10000;
861
+ }
862
+ else if (thinkingOptionId === "off") {
863
+ maxThinkingTokens = 0;
864
+ }
865
+ const appendedSystemPrompt = [
866
+ getOrchestratorModeInstructions(),
867
+ this.config.systemPrompt?.trim(),
868
+ ]
869
+ .filter((entry) => typeof entry === "string" && entry.length > 0)
870
+ .join("\n\n");
871
+ const base = {
872
+ cwd: this.config.cwd,
873
+ includePartialMessages: true,
874
+ permissionMode: this.currentMode,
875
+ agents: this.defaults?.agents,
876
+ canUseTool: this.handlePermissionRequest,
877
+ ...(this.claudePath ? { pathToClaudeCodeExecutable: this.claudePath } : {}),
878
+ // Use Claude Code preset system prompt and load CLAUDE.md files
879
+ // Append provider-agnostic system prompt and orchestrator instructions for agents.
880
+ systemPrompt: {
881
+ type: "preset",
882
+ preset: "claude_code",
883
+ append: appendedSystemPrompt,
884
+ },
885
+ settingSources: ["user", "project"],
886
+ stderr: (data) => {
887
+ this.logger.error({ stderr: data.trim() }, "Claude Agent SDK stderr");
888
+ },
889
+ env: {
890
+ ...process.env,
891
+ // Increase MCP timeouts for long-running tool calls (10 minutes)
892
+ MCP_TIMEOUT: "600000",
893
+ MCP_TOOL_TIMEOUT: "600000",
894
+ },
895
+ // If we have a session ID from a previous query (e.g., after interrupt),
896
+ // resume that session to continue the conversation history.
897
+ ...(this.claudeSessionId ? { resume: this.claudeSessionId } : {}),
898
+ ...(maxThinkingTokens !== undefined ? { maxThinkingTokens } : {}),
899
+ ...this.config.extra?.claude,
900
+ };
901
+ if (this.config.mcpServers) {
902
+ base.mcpServers = this.normalizeMcpServers(this.config.mcpServers);
903
+ }
904
+ if (this.config.model) {
905
+ base.model = this.config.model;
906
+ }
907
+ this.lastOptionsModel = base.model ?? null;
908
+ if (this.claudeSessionId) {
909
+ base.resume = this.claudeSessionId;
910
+ }
911
+ return this.applyRuntimeSettings(base);
912
+ }
913
+ applyRuntimeSettings(options) {
914
+ const hasEnvOverrides = Object.keys(this.runtimeSettings?.env ?? {}).length > 0;
915
+ const commandMode = this.runtimeSettings?.command?.mode;
916
+ const needsCustomSpawn = hasEnvOverrides || commandMode === "append" || commandMode === "replace";
917
+ if (!needsCustomSpawn) {
918
+ return options;
919
+ }
920
+ return {
921
+ ...options,
922
+ spawnClaudeCodeProcess: (spawnOptions) => {
923
+ const resolved = resolveClaudeSpawnCommand(spawnOptions, this.runtimeSettings);
924
+ return spawn(resolved.command, resolved.args, {
925
+ cwd: spawnOptions.cwd,
926
+ env: applyProviderEnv(spawnOptions.env, this.runtimeSettings),
927
+ signal: spawnOptions.signal,
928
+ stdio: ["pipe", "pipe", "pipe"],
929
+ });
930
+ },
931
+ };
932
+ }
933
+ normalizeMcpServers(servers) {
934
+ const result = {};
935
+ for (const [name, config] of Object.entries(servers)) {
936
+ result[name] = toClaudeSdkMcpConfig(config);
937
+ }
938
+ return result;
939
+ }
940
+ toSdkUserMessage(prompt) {
941
+ const content = [];
942
+ if (Array.isArray(prompt)) {
943
+ for (const chunk of prompt) {
944
+ if (chunk.type === "text") {
945
+ content.push({ type: "text", text: chunk.text });
946
+ }
947
+ else if (chunk.type === "image") {
948
+ content.push({
949
+ type: "image",
950
+ source: {
951
+ type: "base64",
952
+ media_type: chunk.mimeType,
953
+ data: chunk.data,
954
+ },
955
+ });
956
+ }
957
+ }
958
+ }
959
+ else {
960
+ content.push({ type: "text", text: prompt });
961
+ }
962
+ return {
963
+ type: "user",
964
+ message: {
965
+ role: "user",
966
+ content,
967
+ },
968
+ parent_tool_use_id: null,
969
+ session_id: this.claudeSessionId ?? "",
970
+ };
971
+ }
972
+ async *processPrompt(sdkMessage, turnId) {
973
+ // If there's a pending interrupt, await it BEFORE calling ensureQuery().
974
+ // interruptActiveTurn() clears this.query after interrupt() returns,
975
+ // so we must wait for it to complete before we try to get the query.
976
+ if (this.pendingInterruptPromise) {
977
+ await this.pendingInterruptPromise;
978
+ this.pendingInterruptPromise = null;
979
+ }
980
+ // Check if we were superseded while waiting for the interrupt
981
+ if (this.currentTurnId !== turnId) {
982
+ return;
983
+ }
984
+ const query = await this.ensureQuery();
985
+ if (!this.input) {
986
+ throw new Error("Claude session input stream not initialized");
987
+ }
988
+ this.input.push(sdkMessage);
989
+ while (true) {
990
+ // Check if this turn has been superseded by a new one.
991
+ if (this.currentTurnId !== turnId) {
992
+ break;
993
+ }
994
+ const { value, done } = await query.next();
995
+ if (done) {
996
+ break;
997
+ }
998
+ if (!value) {
999
+ continue;
1000
+ }
1001
+ // Double-check turn ID after awaiting, in case a new turn started while we waited
1002
+ if (this.currentTurnId !== turnId) {
1003
+ break;
1004
+ }
1005
+ yield value;
1006
+ if (value.type === "result") {
1007
+ break;
1008
+ }
1009
+ }
1010
+ }
1011
+ async forwardPromptEvents(message, queue, turnId) {
1012
+ // Create a turn-local context to track streaming state.
1013
+ // This prevents race conditions when a new stream() call interrupts a running one.
1014
+ const turnContext = {
1015
+ streamedAssistantTextThisTurn: false,
1016
+ streamedReasoningThisTurn: false,
1017
+ };
1018
+ let completedNormally = false;
1019
+ try {
1020
+ for await (const sdkEvent of this.processPrompt(message, turnId)) {
1021
+ // Check if this turn has been superseded before pushing events
1022
+ if (this.currentTurnId !== turnId) {
1023
+ break;
1024
+ }
1025
+ const events = this.translateMessageToEvents(sdkEvent, turnContext);
1026
+ for (const event of events) {
1027
+ queue.push(event);
1028
+ if (event.type === "turn_completed") {
1029
+ completedNormally = true;
1030
+ }
1031
+ }
1032
+ }
1033
+ }
1034
+ catch (error) {
1035
+ if (!this.turnCancelRequested && this.currentTurnId === turnId) {
1036
+ queue.push({
1037
+ type: "turn_failed",
1038
+ provider: "claude",
1039
+ error: error instanceof Error ? error.message : "Claude stream failed",
1040
+ });
1041
+ }
1042
+ }
1043
+ finally {
1044
+ // Emit terminal event for superseded turns so consumers get proper lifecycle signals.
1045
+ // Use turn_canceled (not turn_failed) to distinguish intentional interruption from errors.
1046
+ // Only emit if not already emitted by requestCancel() (indicated by turnCancelRequested).
1047
+ const wasSuperseded = this.currentTurnId !== turnId;
1048
+ if (wasSuperseded && !completedNormally && !this.turnCancelRequested) {
1049
+ this.flushPendingToolCalls();
1050
+ queue.push({
1051
+ type: "turn_canceled",
1052
+ provider: "claude",
1053
+ reason: "Interrupted by new message",
1054
+ });
1055
+ }
1056
+ this.turnCancelRequested = false;
1057
+ queue.end();
1058
+ }
1059
+ }
1060
+ async interruptActiveTurn() {
1061
+ const queryToInterrupt = this.query;
1062
+ if (!queryToInterrupt || typeof queryToInterrupt.interrupt !== "function") {
1063
+ this.logger.info("interruptActiveTurn: no query to interrupt");
1064
+ return;
1065
+ }
1066
+ try {
1067
+ this.logger.info("interruptActiveTurn: calling query.interrupt()...");
1068
+ const t0 = Date.now();
1069
+ await queryToInterrupt.interrupt();
1070
+ this.logger.info({ durationMs: Date.now() - t0 }, "interruptActiveTurn: query.interrupt() returned");
1071
+ // After interrupt(), the query iterator is done (returns done: true).
1072
+ // Clear it so ensureQuery() creates a fresh query for the next turn.
1073
+ // Also end the input stream and call return() to clean up the SDK process.
1074
+ this.input?.end();
1075
+ this.logger.info("interruptActiveTurn: calling query.return()...");
1076
+ const t1 = Date.now();
1077
+ await queryToInterrupt.return?.();
1078
+ this.logger.info({ durationMs: Date.now() - t1 }, "interruptActiveTurn: query.return() returned");
1079
+ this.query = null;
1080
+ this.input = null;
1081
+ this.queryRestartNeeded = false;
1082
+ }
1083
+ catch (error) {
1084
+ this.logger.warn({ err: error }, "Failed to interrupt active turn");
1085
+ }
1086
+ }
1087
+ handleSidechainMessage(message, parentToolUseId) {
1088
+ let toolName;
1089
+ if (message.type === "assistant") {
1090
+ const content = message.message?.content;
1091
+ if (Array.isArray(content)) {
1092
+ for (const block of content) {
1093
+ if (isClaudeContentChunk(block) &&
1094
+ (block.type === "tool_use" || block.type === "mcp_tool_use" || block.type === "server_tool_use") &&
1095
+ typeof block.name === "string") {
1096
+ toolName = block.name;
1097
+ break;
1098
+ }
1099
+ }
1100
+ }
1101
+ }
1102
+ else if (message.type === "stream_event") {
1103
+ const event = message.event;
1104
+ if (event.type === "content_block_start") {
1105
+ const cb = isClaudeContentChunk(event.content_block) ? event.content_block : null;
1106
+ if (cb?.type === "tool_use" && typeof cb.name === "string") {
1107
+ toolName = cb.name;
1108
+ }
1109
+ }
1110
+ }
1111
+ else if (message.type === "tool_progress") {
1112
+ toolName = message.tool_name;
1113
+ }
1114
+ if (!toolName) {
1115
+ return [];
1116
+ }
1117
+ const prev = this.activeSidechains.get(parentToolUseId);
1118
+ if (prev === toolName) {
1119
+ return [];
1120
+ }
1121
+ this.activeSidechains.set(parentToolUseId, toolName);
1122
+ return [
1123
+ {
1124
+ type: "timeline",
1125
+ item: mapClaudeRunningToolCall({
1126
+ name: "Task",
1127
+ callId: parentToolUseId,
1128
+ input: null,
1129
+ output: null,
1130
+ metadata: { subAgentActivity: toolName },
1131
+ }),
1132
+ provider: "claude",
1133
+ },
1134
+ ];
1135
+ }
1136
+ translateMessageToEvents(message, turnContext) {
1137
+ const parentToolUseId = "parent_tool_use_id" in message
1138
+ ? message.parent_tool_use_id
1139
+ : null;
1140
+ if (parentToolUseId) {
1141
+ return this.handleSidechainMessage(message, parentToolUseId);
1142
+ }
1143
+ const events = [];
1144
+ switch (message.type) {
1145
+ case "system":
1146
+ if (message.subtype === "init") {
1147
+ this.handleSystemMessage(message);
1148
+ }
1149
+ else if (message.subtype === "status") {
1150
+ const status = message.status;
1151
+ if (status === "compacting") {
1152
+ this.compacting = true;
1153
+ events.push({
1154
+ type: "timeline",
1155
+ item: { type: "compaction", status: "loading" },
1156
+ provider: "claude",
1157
+ });
1158
+ }
1159
+ }
1160
+ else if (message.subtype === "compact_boundary") {
1161
+ const meta = message.compact_metadata;
1162
+ events.push({
1163
+ type: "timeline",
1164
+ item: {
1165
+ type: "compaction",
1166
+ status: "completed",
1167
+ trigger: meta?.trigger === "manual" ? "manual" : "auto",
1168
+ preTokens: meta?.pre_tokens,
1169
+ },
1170
+ provider: "claude",
1171
+ });
1172
+ }
1173
+ break;
1174
+ case "user": {
1175
+ if (this.compacting) {
1176
+ this.compacting = false;
1177
+ break;
1178
+ }
1179
+ const content = message.message?.content;
1180
+ if (typeof content === "string" && content.length > 0) {
1181
+ // String content from user messages (e.g., local command output)
1182
+ events.push({
1183
+ type: "timeline",
1184
+ item: { type: "user_message", text: content },
1185
+ provider: "claude",
1186
+ });
1187
+ }
1188
+ else if (Array.isArray(content)) {
1189
+ const timelineItems = this.mapBlocksToTimeline(content, { turnContext });
1190
+ for (const item of timelineItems) {
1191
+ events.push({ type: "timeline", item, provider: "claude" });
1192
+ }
1193
+ }
1194
+ break;
1195
+ }
1196
+ case "assistant": {
1197
+ const timelineItems = this.mapBlocksToTimeline(message.message.content, {
1198
+ turnContext,
1199
+ suppressAssistantText: turnContext.streamedAssistantTextThisTurn,
1200
+ suppressReasoning: turnContext.streamedReasoningThisTurn,
1201
+ });
1202
+ for (const item of timelineItems) {
1203
+ events.push({ type: "timeline", item, provider: "claude" });
1204
+ }
1205
+ break;
1206
+ }
1207
+ case "stream_event": {
1208
+ const timelineItems = this.mapPartialEvent(message.event, turnContext);
1209
+ for (const item of timelineItems) {
1210
+ events.push({ type: "timeline", item, provider: "claude" });
1211
+ }
1212
+ break;
1213
+ }
1214
+ case "result": {
1215
+ const usage = this.convertUsage(message);
1216
+ if (message.subtype === "success") {
1217
+ events.push({ type: "turn_completed", provider: "claude", usage });
1218
+ }
1219
+ else {
1220
+ const errorMessage = "errors" in message && Array.isArray(message.errors) && message.errors.length > 0
1221
+ ? message.errors.join("\n")
1222
+ : "Claude run failed";
1223
+ events.push({ type: "turn_failed", provider: "claude", error: errorMessage });
1224
+ }
1225
+ break;
1226
+ }
1227
+ default:
1228
+ break;
1229
+ }
1230
+ return events;
1231
+ }
1232
+ handleSystemMessage(message) {
1233
+ if (message.subtype !== "init") {
1234
+ return;
1235
+ }
1236
+ const msg = message;
1237
+ const newSessionIdRaw = typeof msg.session_id === "string"
1238
+ ? msg.session_id
1239
+ : typeof msg.sessionId === "string"
1240
+ ? msg.sessionId
1241
+ : typeof msg.session?.id === "string"
1242
+ ? msg.session.id
1243
+ : "";
1244
+ const newSessionId = newSessionIdRaw.trim();
1245
+ if (!newSessionId) {
1246
+ return;
1247
+ }
1248
+ const existingSessionId = this.claudeSessionId;
1249
+ if (existingSessionId === null) {
1250
+ // First time setting session ID (empty → filled) - this is expected
1251
+ this.claudeSessionId = newSessionId;
1252
+ this.logger.debug({ sessionId: newSessionId }, "Claude session ID set for the first time");
1253
+ }
1254
+ else if (existingSessionId === newSessionId) {
1255
+ // Same session ID - no-op, but log for visibility
1256
+ this.logger.debug({ sessionId: newSessionId }, "Claude session ID unchanged (same value)");
1257
+ }
1258
+ else {
1259
+ // CRITICAL: Session ID is being overwritten with a different value
1260
+ // This should NEVER happen and indicates a serious bug
1261
+ throw new Error(`CRITICAL: Claude session ID overwrite detected! ` +
1262
+ `Existing: ${existingSessionId}, New: ${newSessionId}. ` +
1263
+ `This indicates a session identity corruption bug.`);
1264
+ }
1265
+ this.availableModes = DEFAULT_MODES;
1266
+ this.currentMode = message.permissionMode;
1267
+ this.persistence = null;
1268
+ // Capture actual model from SDK init message (not just the configured model)
1269
+ if (message.model) {
1270
+ this.logger.debug({ model: message.model }, "Captured model from SDK init");
1271
+ this.lastOptionsModel = message.model;
1272
+ // Invalidate cached runtime info so it picks up the new model
1273
+ this.cachedRuntimeInfo = null;
1274
+ }
1275
+ }
1276
+ convertUsage(message) {
1277
+ if (!message.usage) {
1278
+ return undefined;
1279
+ }
1280
+ return {
1281
+ inputTokens: message.usage.input_tokens,
1282
+ cachedInputTokens: message.usage.cache_read_input_tokens,
1283
+ outputTokens: message.usage.output_tokens,
1284
+ totalCostUsd: message.total_cost_usd,
1285
+ };
1286
+ }
1287
+ enqueueTimeline(item) {
1288
+ this.pushEvent({ type: "timeline", item, provider: "claude" });
1289
+ }
1290
+ flushPendingToolCalls() {
1291
+ for (const [id, entry] of this.toolUseCache) {
1292
+ if (entry.started) {
1293
+ this.pushToolCall(mapClaudeCanceledToolCall({
1294
+ name: entry.name,
1295
+ callId: id,
1296
+ input: entry.input ?? null,
1297
+ output: null,
1298
+ }));
1299
+ }
1300
+ }
1301
+ this.toolUseCache.clear();
1302
+ }
1303
+ pushToolCall(item, target) {
1304
+ if (target) {
1305
+ target.push(item);
1306
+ return;
1307
+ }
1308
+ this.enqueueTimeline(item);
1309
+ }
1310
+ pushEvent(event) {
1311
+ if (this.eventQueue) {
1312
+ this.eventQueue.push(event);
1313
+ }
1314
+ }
1315
+ normalizePermissionUpdates(updates) {
1316
+ if (!updates || updates.length === 0) {
1317
+ return undefined;
1318
+ }
1319
+ const normalized = updates.filter(isPermissionUpdate);
1320
+ return normalized.length > 0 ? normalized : undefined;
1321
+ }
1322
+ rejectAllPendingPermissions(error) {
1323
+ for (const [id, pending] of this.pendingPermissions) {
1324
+ pending.cleanup?.();
1325
+ pending.reject(error);
1326
+ this.pendingPermissions.delete(id);
1327
+ }
1328
+ }
1329
+ loadPersistedHistory(sessionId) {
1330
+ try {
1331
+ const historyPath = this.resolveHistoryPath(sessionId);
1332
+ if (!historyPath || !fs.existsSync(historyPath)) {
1333
+ return;
1334
+ }
1335
+ const content = fs.readFileSync(historyPath, "utf8");
1336
+ const timeline = [];
1337
+ for (const line of content.split(/\n+/)) {
1338
+ const trimmed = line.trim();
1339
+ if (!trimmed)
1340
+ continue;
1341
+ try {
1342
+ const entry = JSON.parse(trimmed);
1343
+ if (entry.isSidechain) {
1344
+ continue;
1345
+ }
1346
+ const items = this.convertHistoryEntry(entry);
1347
+ if (items.length > 0) {
1348
+ timeline.push(...items);
1349
+ }
1350
+ }
1351
+ catch (error) {
1352
+ // ignore malformed history line
1353
+ }
1354
+ }
1355
+ if (timeline.length > 0) {
1356
+ this.persistedHistory = timeline;
1357
+ this.historyPending = true;
1358
+ }
1359
+ }
1360
+ catch (error) {
1361
+ // ignore history load failures
1362
+ }
1363
+ }
1364
+ resolveHistoryPath(sessionId) {
1365
+ const cwd = this.config.cwd;
1366
+ if (!cwd)
1367
+ return null;
1368
+ // Match Claude CLI's path sanitization: replace slashes, dots, and underscores with dashes
1369
+ const sanitized = cwd.replace(/[\\/\.]/g, "-").replace(/_/g, "-");
1370
+ const configDir = process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude");
1371
+ const dir = path.join(configDir, "projects", sanitized);
1372
+ return path.join(dir, `${sessionId}.jsonl`);
1373
+ }
1374
+ convertHistoryEntry(entry) {
1375
+ return convertClaudeHistoryEntry(entry, (content) => this.mapBlocksToTimeline(content, { context: "history" }));
1376
+ }
1377
+ mapBlocksToTimeline(content, options) {
1378
+ const context = options?.context ?? "live";
1379
+ const turnContext = options?.turnContext;
1380
+ const suppressAssistant = options?.suppressAssistantText ?? false;
1381
+ const suppressReasoning = options?.suppressReasoning ?? false;
1382
+ if (typeof content === "string") {
1383
+ if (!content || content === "[Request interrupted by user for tool use]") {
1384
+ return [];
1385
+ }
1386
+ if (context === "live" && turnContext) {
1387
+ turnContext.streamedAssistantTextThisTurn = true;
1388
+ }
1389
+ if (suppressAssistant) {
1390
+ return [];
1391
+ }
1392
+ return [{ type: "assistant_message", text: content }];
1393
+ }
1394
+ const items = [];
1395
+ for (const block of content) {
1396
+ switch (block.type) {
1397
+ case "text":
1398
+ case "text_delta":
1399
+ if (block.text && block.text !== "[Request interrupted by user for tool use]") {
1400
+ if (context === "live" && turnContext) {
1401
+ turnContext.streamedAssistantTextThisTurn = true;
1402
+ }
1403
+ if (!suppressAssistant) {
1404
+ items.push({ type: "assistant_message", text: block.text });
1405
+ }
1406
+ }
1407
+ break;
1408
+ case "thinking":
1409
+ case "thinking_delta":
1410
+ if (block.thinking) {
1411
+ if (context === "live" && turnContext) {
1412
+ turnContext.streamedReasoningThisTurn = true;
1413
+ }
1414
+ if (!suppressReasoning) {
1415
+ items.push({ type: "reasoning", text: block.thinking });
1416
+ }
1417
+ }
1418
+ break;
1419
+ case "tool_use":
1420
+ case "server_tool_use":
1421
+ case "mcp_tool_use": {
1422
+ this.handleToolUseStart(block, items);
1423
+ break;
1424
+ }
1425
+ case "tool_result":
1426
+ case "mcp_tool_result":
1427
+ case "web_fetch_tool_result":
1428
+ case "web_search_tool_result":
1429
+ case "code_execution_tool_result":
1430
+ case "bash_code_execution_tool_result":
1431
+ case "text_editor_code_execution_tool_result": {
1432
+ this.handleToolResult(block, items);
1433
+ break;
1434
+ }
1435
+ default:
1436
+ break;
1437
+ }
1438
+ }
1439
+ return items;
1440
+ }
1441
+ handleToolUseStart(block, items) {
1442
+ const entry = this.upsertToolUseEntry(block);
1443
+ if (!entry) {
1444
+ return;
1445
+ }
1446
+ if (entry.started) {
1447
+ return;
1448
+ }
1449
+ entry.started = true;
1450
+ this.toolUseCache.set(entry.id, entry);
1451
+ this.pushToolCall(mapClaudeRunningToolCall({
1452
+ name: entry.name,
1453
+ callId: entry.id,
1454
+ input: entry.input ?? this.normalizeToolInput(block.input) ?? null,
1455
+ output: null,
1456
+ }), items);
1457
+ }
1458
+ handleToolResult(block, items) {
1459
+ const entry = typeof block.tool_use_id === "string" ? this.toolUseCache.get(block.tool_use_id) : undefined;
1460
+ const toolName = entry?.name ?? block.tool_name ?? "tool";
1461
+ const callId = typeof block.tool_use_id === "string" && block.tool_use_id.length > 0
1462
+ ? block.tool_use_id
1463
+ : entry?.id ?? null;
1464
+ // Extract output from block.content (SDK always returns content in string form)
1465
+ const output = this.buildToolOutput(block, entry);
1466
+ if (block.is_error) {
1467
+ this.pushToolCall(mapClaudeFailedToolCall({
1468
+ name: toolName,
1469
+ callId,
1470
+ input: entry?.input ?? null,
1471
+ output: output ?? null,
1472
+ error: block,
1473
+ }), items);
1474
+ }
1475
+ else {
1476
+ this.pushToolCall(mapClaudeCompletedToolCall({
1477
+ name: toolName,
1478
+ callId,
1479
+ input: entry?.input ?? null,
1480
+ output: output ?? null,
1481
+ }), items);
1482
+ }
1483
+ if (typeof block.tool_use_id === "string") {
1484
+ this.toolUseCache.delete(block.tool_use_id);
1485
+ }
1486
+ }
1487
+ buildToolOutput(block, entry) {
1488
+ if (block.is_error) {
1489
+ return undefined;
1490
+ }
1491
+ const server = entry?.server ?? block.server ?? "tool";
1492
+ const tool = entry?.name ?? block.tool_name ?? "tool";
1493
+ const content = typeof block.content === "string" ? block.content : "";
1494
+ const input = entry?.input;
1495
+ // Build structured result based on tool type
1496
+ const structured = this.buildStructuredToolResult(server, tool, content, input);
1497
+ if (structured) {
1498
+ return structured;
1499
+ }
1500
+ // Fallback format - try to parse JSON first
1501
+ const result = {};
1502
+ if (content.length > 0) {
1503
+ try {
1504
+ // If content is a JSON string, parse it
1505
+ result.output = JSON.parse(content);
1506
+ }
1507
+ catch {
1508
+ // If not JSON, return unchanged (no extra wrapping)
1509
+ result.output = content;
1510
+ }
1511
+ }
1512
+ // Preserve file changes tracked during tool execution
1513
+ if (entry?.files?.length) {
1514
+ result.files = entry.files;
1515
+ }
1516
+ return Object.keys(result).length > 0 ? result : undefined;
1517
+ }
1518
+ buildStructuredToolResult(server, tool, output, input) {
1519
+ const normalizedServer = server.toLowerCase();
1520
+ const normalizedTool = tool.toLowerCase();
1521
+ // Command execution tools
1522
+ if (normalizedServer.includes("bash") ||
1523
+ normalizedServer.includes("shell") ||
1524
+ normalizedServer.includes("command") ||
1525
+ normalizedTool.includes("bash") ||
1526
+ normalizedTool.includes("shell") ||
1527
+ normalizedTool.includes("command") ||
1528
+ (input && (typeof input.command === "string" || Array.isArray(input.command)))) {
1529
+ const command = this.extractCommandText(input ?? {}) ?? "command";
1530
+ return {
1531
+ type: "command",
1532
+ command,
1533
+ output,
1534
+ cwd: typeof input?.cwd === "string" ? input.cwd : undefined,
1535
+ };
1536
+ }
1537
+ // File write tools (new files or complete replacements)
1538
+ if (normalizedTool.includes("write") ||
1539
+ normalizedTool === "write_file" ||
1540
+ normalizedTool === "create_file") {
1541
+ if (input && typeof input.file_path === "string") {
1542
+ return {
1543
+ type: "file_write",
1544
+ filePath: input.file_path,
1545
+ oldContent: "",
1546
+ newContent: typeof input.content === "string" ? input.content : output,
1547
+ };
1548
+ }
1549
+ }
1550
+ // File edit/patch tools
1551
+ if (normalizedTool.includes("edit") ||
1552
+ normalizedTool.includes("patch") ||
1553
+ normalizedTool === "apply_patch" ||
1554
+ normalizedTool === "apply_diff") {
1555
+ if (input && typeof input.file_path === "string") {
1556
+ // Support both old_str/new_str and old_string/new_string parameter names
1557
+ const oldContent = typeof input.old_str === "string" ? input.old_str : typeof input.old_string === "string" ? input.old_string : undefined;
1558
+ const newContent = typeof input.new_str === "string" ? input.new_str : typeof input.new_string === "string" ? input.new_string : undefined;
1559
+ return {
1560
+ type: "file_edit",
1561
+ filePath: input.file_path,
1562
+ diff: typeof input.patch === "string" ? input.patch : typeof input.diff === "string" ? input.diff : undefined,
1563
+ oldContent,
1564
+ newContent,
1565
+ };
1566
+ }
1567
+ }
1568
+ // File read tools
1569
+ if (normalizedTool.includes("read") ||
1570
+ normalizedTool === "read_file" ||
1571
+ normalizedTool === "view_file") {
1572
+ if (input && typeof input.file_path === "string") {
1573
+ return {
1574
+ type: "file_read",
1575
+ filePath: input.file_path,
1576
+ content: output,
1577
+ };
1578
+ }
1579
+ }
1580
+ return undefined;
1581
+ }
1582
+ mapPartialEvent(event, turnContext) {
1583
+ if (event.type === "content_block_start") {
1584
+ const block = isClaudeContentChunk(event.content_block) ? event.content_block : null;
1585
+ if (block?.type === "tool_use" && typeof event.index === "number" && typeof block.id === "string") {
1586
+ this.toolUseIndexToId.set(event.index, block.id);
1587
+ this.toolUseInputBuffers.delete(block.id);
1588
+ }
1589
+ }
1590
+ else if (event.type === "content_block_delta") {
1591
+ const delta = isClaudeContentChunk(event.delta) ? event.delta : null;
1592
+ if (delta?.type === "input_json_delta") {
1593
+ const partialJson = typeof delta.partial_json === "string" ? delta.partial_json : undefined;
1594
+ this.handleToolInputDelta(event.index, partialJson);
1595
+ return [];
1596
+ }
1597
+ }
1598
+ else if (event.type === "content_block_stop" && typeof event.index === "number") {
1599
+ const toolId = this.toolUseIndexToId.get(event.index);
1600
+ if (toolId) {
1601
+ this.toolUseIndexToId.delete(event.index);
1602
+ this.toolUseInputBuffers.delete(toolId);
1603
+ }
1604
+ }
1605
+ switch (event.type) {
1606
+ case "content_block_start":
1607
+ return isClaudeContentChunk(event.content_block)
1608
+ ? this.mapBlocksToTimeline([event.content_block], { turnContext })
1609
+ : [];
1610
+ case "content_block_delta":
1611
+ return isClaudeContentChunk(event.delta) ? this.mapBlocksToTimeline([event.delta], { turnContext }) : [];
1612
+ default:
1613
+ return [];
1614
+ }
1615
+ }
1616
+ upsertToolUseEntry(block) {
1617
+ const id = typeof block.id === "string" ? block.id : undefined;
1618
+ if (!id) {
1619
+ return null;
1620
+ }
1621
+ const existing = this.toolUseCache.get(id) ??
1622
+ {
1623
+ id,
1624
+ name: typeof block.name === "string" && block.name.length > 0 ? block.name : "tool",
1625
+ server: typeof block.server === "string" && block.server.length > 0
1626
+ ? block.server
1627
+ : typeof block.name === "string" && block.name.length > 0
1628
+ ? block.name
1629
+ : "tool",
1630
+ classification: "generic",
1631
+ started: false,
1632
+ };
1633
+ if (typeof block.name === "string" && block.name.length > 0) {
1634
+ existing.name = block.name;
1635
+ }
1636
+ if (typeof block.server === "string" && block.server.length > 0) {
1637
+ existing.server = block.server;
1638
+ }
1639
+ else if (!existing.server) {
1640
+ existing.server = existing.name;
1641
+ }
1642
+ if (block.type === "tool_use" || block.type === "mcp_tool_use" || block.type === "server_tool_use") {
1643
+ const input = this.normalizeToolInput(block.input);
1644
+ if (input) {
1645
+ this.applyToolInput(existing, input);
1646
+ }
1647
+ }
1648
+ this.toolUseCache.set(id, existing);
1649
+ return existing;
1650
+ }
1651
+ handleToolInputDelta(index, partialJson) {
1652
+ if (typeof index !== "number" || typeof partialJson !== "string") {
1653
+ return;
1654
+ }
1655
+ const toolId = this.toolUseIndexToId.get(index);
1656
+ if (!toolId) {
1657
+ return;
1658
+ }
1659
+ const buffer = (this.toolUseInputBuffers.get(toolId) ?? "") + partialJson;
1660
+ this.toolUseInputBuffers.set(toolId, buffer);
1661
+ let parsed;
1662
+ try {
1663
+ parsed = JSON.parse(buffer);
1664
+ }
1665
+ catch {
1666
+ return;
1667
+ }
1668
+ const entry = this.toolUseCache.get(toolId);
1669
+ const normalized = this.normalizeToolInput(parsed);
1670
+ if (!entry || !normalized) {
1671
+ return;
1672
+ }
1673
+ this.applyToolInput(entry, normalized);
1674
+ this.toolUseCache.set(toolId, entry);
1675
+ this.pushToolCall(mapClaudeRunningToolCall({
1676
+ name: entry.name,
1677
+ callId: toolId,
1678
+ input: normalized,
1679
+ output: null,
1680
+ }));
1681
+ }
1682
+ normalizeToolInput(input) {
1683
+ if (!isMetadata(input)) {
1684
+ return null;
1685
+ }
1686
+ return input;
1687
+ }
1688
+ applyToolInput(entry, input) {
1689
+ entry.input = input;
1690
+ if (this.isCommandTool(entry.name, input)) {
1691
+ entry.classification = "command";
1692
+ entry.commandText = this.extractCommandText(input) ?? entry.commandText;
1693
+ }
1694
+ else {
1695
+ const files = this.extractFileChanges(input);
1696
+ if (files?.length) {
1697
+ entry.classification = "file_change";
1698
+ entry.files = files;
1699
+ }
1700
+ }
1701
+ }
1702
+ isCommandTool(name, input) {
1703
+ const normalized = name.toLowerCase();
1704
+ if (normalized.includes("bash") || normalized.includes("shell") || normalized.includes("terminal") || normalized.includes("command")) {
1705
+ return true;
1706
+ }
1707
+ if (typeof input.command === "string" || Array.isArray(input.command)) {
1708
+ return true;
1709
+ }
1710
+ return false;
1711
+ }
1712
+ extractCommandText(input) {
1713
+ const command = input.command;
1714
+ if (typeof command === "string" && command.length > 0) {
1715
+ return command;
1716
+ }
1717
+ if (Array.isArray(command)) {
1718
+ const tokens = command.filter((value) => typeof value === "string");
1719
+ if (tokens.length > 0) {
1720
+ return tokens.join(" ");
1721
+ }
1722
+ }
1723
+ if (typeof input.description === "string" && input.description.length > 0) {
1724
+ return input.description;
1725
+ }
1726
+ return undefined;
1727
+ }
1728
+ extractFileChanges(input) {
1729
+ if (typeof input.file_path === "string" && input.file_path.length > 0) {
1730
+ const relative = this.relativizePath(input.file_path);
1731
+ if (relative) {
1732
+ return [{ path: relative, kind: this.detectFileKind(input.file_path) }];
1733
+ }
1734
+ }
1735
+ if (typeof input.patch === "string" && input.patch.length > 0) {
1736
+ const files = this.parsePatchFileList(input.patch);
1737
+ if (files.length > 0) {
1738
+ return files.map((entry) => ({
1739
+ path: this.relativizePath(entry.path) ?? entry.path,
1740
+ kind: entry.kind,
1741
+ }));
1742
+ }
1743
+ }
1744
+ if (Array.isArray(input.files)) {
1745
+ const files = [];
1746
+ for (const value of input.files) {
1747
+ if (typeof value === "string" && value.length > 0) {
1748
+ files.push({ path: this.relativizePath(value) ?? value, kind: this.detectFileKind(value) });
1749
+ }
1750
+ }
1751
+ if (files.length > 0) {
1752
+ return files;
1753
+ }
1754
+ }
1755
+ return undefined;
1756
+ }
1757
+ detectFileKind(filePath) {
1758
+ try {
1759
+ return fs.existsSync(filePath) ? "update" : "add";
1760
+ }
1761
+ catch {
1762
+ return "update";
1763
+ }
1764
+ }
1765
+ relativizePath(target) {
1766
+ if (!target) {
1767
+ return undefined;
1768
+ }
1769
+ const cwd = this.config.cwd;
1770
+ if (cwd && target.startsWith(cwd)) {
1771
+ const relative = path.relative(cwd, target);
1772
+ return relative.length > 0 ? relative : path.basename(target);
1773
+ }
1774
+ return target;
1775
+ }
1776
+ parsePatchFileList(patch) {
1777
+ const files = [];
1778
+ const seen = new Set();
1779
+ for (const line of patch.split(/\r?\n/)) {
1780
+ const trimmed = line.trim();
1781
+ let kind = null;
1782
+ let parsedPath = null;
1783
+ if (trimmed.startsWith("*** Add File:")) {
1784
+ kind = "add";
1785
+ parsedPath = trimmed.replace("*** Add File:", "").trim();
1786
+ }
1787
+ else if (trimmed.startsWith("*** Delete File:")) {
1788
+ kind = "delete";
1789
+ parsedPath = trimmed.replace("*** Delete File:", "").trim();
1790
+ }
1791
+ else if (trimmed.startsWith("*** Update File:")) {
1792
+ kind = "update";
1793
+ parsedPath = trimmed.replace("*** Update File:", "").trim();
1794
+ }
1795
+ if (kind && parsedPath && !seen.has(`${kind}:${parsedPath}`)) {
1796
+ seen.add(`${kind}:${parsedPath}`);
1797
+ files.push({ path: parsedPath, kind });
1798
+ }
1799
+ }
1800
+ return files;
1801
+ }
1802
+ }
1803
+ function hasToolLikeBlock(block) {
1804
+ if (!block || typeof block !== "object") {
1805
+ return false;
1806
+ }
1807
+ const type = typeof block.type === "string" ? block.type.toLowerCase() : "";
1808
+ return type.includes("tool");
1809
+ }
1810
+ function normalizeHistoryBlocks(content) {
1811
+ if (Array.isArray(content)) {
1812
+ const blocks = content.filter((entry) => isClaudeContentChunk(entry));
1813
+ return blocks.length > 0 ? blocks : null;
1814
+ }
1815
+ if (isClaudeContentChunk(content)) {
1816
+ return [content];
1817
+ }
1818
+ return null;
1819
+ }
1820
+ export function convertClaudeHistoryEntry(entry, mapBlocks) {
1821
+ if (entry.type === "system" && entry.subtype === "compact_boundary") {
1822
+ return [{
1823
+ type: "compaction",
1824
+ status: "completed",
1825
+ trigger: entry.compactMetadata?.trigger === "manual" ? "manual" : "auto",
1826
+ preTokens: entry.compactMetadata?.preTokens,
1827
+ }];
1828
+ }
1829
+ if (entry.isCompactSummary) {
1830
+ return [];
1831
+ }
1832
+ const message = entry?.message;
1833
+ if (!message || !("content" in message)) {
1834
+ return [];
1835
+ }
1836
+ const content = message.content;
1837
+ const normalizedBlocks = normalizeHistoryBlocks(content);
1838
+ const contentValue = typeof content === "string"
1839
+ ? content
1840
+ : normalizedBlocks;
1841
+ const hasToolBlock = normalizedBlocks?.some((block) => hasToolLikeBlock(block)) ?? false;
1842
+ const timeline = [];
1843
+ if (entry.type === "user") {
1844
+ const text = extractUserMessageText(content);
1845
+ if (text) {
1846
+ timeline.push({
1847
+ type: "user_message",
1848
+ text,
1849
+ });
1850
+ }
1851
+ }
1852
+ if (hasToolBlock && normalizedBlocks) {
1853
+ const mapped = mapBlocks(normalizedBlocks);
1854
+ if (entry.type === "user") {
1855
+ const toolItems = mapped.filter((item) => item.type === "tool_call");
1856
+ return timeline.length ? [...timeline, ...toolItems] : toolItems;
1857
+ }
1858
+ return mapped;
1859
+ }
1860
+ if (entry.type === "assistant" && contentValue) {
1861
+ return mapBlocks(contentValue);
1862
+ }
1863
+ return timeline;
1864
+ }
1865
+ class Pushable {
1866
+ constructor() {
1867
+ this.queue = [];
1868
+ this.resolvers = [];
1869
+ this.closed = false;
1870
+ }
1871
+ push(item) {
1872
+ if (this.closed) {
1873
+ return;
1874
+ }
1875
+ if (this.resolvers.length > 0) {
1876
+ const resolve = this.resolvers.shift();
1877
+ resolve({ value: item, done: false });
1878
+ }
1879
+ else {
1880
+ this.queue.push(item);
1881
+ }
1882
+ }
1883
+ end() {
1884
+ this.closed = true;
1885
+ while (this.resolvers.length > 0) {
1886
+ const resolve = this.resolvers.shift();
1887
+ resolve({ value: undefined, done: true });
1888
+ }
1889
+ }
1890
+ [Symbol.asyncIterator]() {
1891
+ return {
1892
+ next: () => {
1893
+ if (this.queue.length > 0) {
1894
+ const value = this.queue.shift();
1895
+ if (value !== undefined) {
1896
+ return Promise.resolve({ value, done: false });
1897
+ }
1898
+ }
1899
+ if (this.closed) {
1900
+ return Promise.resolve({ value: undefined, done: true });
1901
+ }
1902
+ return new Promise((resolve) => {
1903
+ this.resolvers.push(resolve);
1904
+ });
1905
+ },
1906
+ };
1907
+ }
1908
+ }
1909
+ async function pathExists(target) {
1910
+ try {
1911
+ await fsPromises.access(target);
1912
+ return true;
1913
+ }
1914
+ catch {
1915
+ return false;
1916
+ }
1917
+ }
1918
+ async function collectRecentClaudeSessions(root, limit) {
1919
+ let projectDirs;
1920
+ try {
1921
+ projectDirs = await fsPromises.readdir(root);
1922
+ }
1923
+ catch {
1924
+ return [];
1925
+ }
1926
+ const candidates = [];
1927
+ for (const dirName of projectDirs) {
1928
+ const projectPath = path.join(root, dirName);
1929
+ let stats;
1930
+ try {
1931
+ stats = await fsPromises.stat(projectPath);
1932
+ }
1933
+ catch {
1934
+ continue;
1935
+ }
1936
+ if (!stats.isDirectory()) {
1937
+ continue;
1938
+ }
1939
+ let files;
1940
+ try {
1941
+ files = await fsPromises.readdir(projectPath);
1942
+ }
1943
+ catch {
1944
+ continue;
1945
+ }
1946
+ for (const file of files) {
1947
+ if (!file.endsWith(".jsonl")) {
1948
+ continue;
1949
+ }
1950
+ const fullPath = path.join(projectPath, file);
1951
+ try {
1952
+ const fileStats = await fsPromises.stat(fullPath);
1953
+ candidates.push({ path: fullPath, mtime: fileStats.mtime });
1954
+ }
1955
+ catch {
1956
+ // ignore stat errors for individual files
1957
+ }
1958
+ }
1959
+ }
1960
+ return candidates
1961
+ .sort((a, b) => b.mtime.getTime() - a.mtime.getTime())
1962
+ .slice(0, limit);
1963
+ }
1964
+ async function parseClaudeSessionDescriptor(filePath, mtime) {
1965
+ let content;
1966
+ try {
1967
+ content = await fsPromises.readFile(filePath, "utf8");
1968
+ }
1969
+ catch {
1970
+ return null;
1971
+ }
1972
+ let sessionId = null;
1973
+ let cwd = null;
1974
+ let title = null;
1975
+ const timeline = [];
1976
+ for (const rawLine of content.split(/\r?\n/)) {
1977
+ const line = rawLine.trim();
1978
+ if (!line)
1979
+ continue;
1980
+ let entry;
1981
+ try {
1982
+ entry = JSON.parse(line);
1983
+ }
1984
+ catch {
1985
+ continue;
1986
+ }
1987
+ if (entry?.isSidechain) {
1988
+ continue;
1989
+ }
1990
+ if (!sessionId && typeof entry.sessionId === "string") {
1991
+ sessionId = entry.sessionId;
1992
+ }
1993
+ if (!cwd && typeof entry.cwd === "string") {
1994
+ cwd = entry.cwd;
1995
+ }
1996
+ if (entry.type === "user" && entry.message) {
1997
+ const text = extractClaudeUserText(entry.message);
1998
+ if (text) {
1999
+ if (!title) {
2000
+ title = text;
2001
+ }
2002
+ timeline.push({ type: "user_message", text });
2003
+ }
2004
+ }
2005
+ else if (entry.type === "assistant" && entry.message) {
2006
+ const text = extractClaudeUserText(entry.message);
2007
+ if (text) {
2008
+ timeline.push({ type: "assistant_message", text });
2009
+ }
2010
+ }
2011
+ if (sessionId && cwd && title) {
2012
+ break;
2013
+ }
2014
+ }
2015
+ if (!sessionId || !cwd) {
2016
+ return null;
2017
+ }
2018
+ const persistence = {
2019
+ provider: "claude",
2020
+ sessionId,
2021
+ nativeHandle: sessionId,
2022
+ metadata: {
2023
+ provider: "claude",
2024
+ cwd,
2025
+ },
2026
+ };
2027
+ return {
2028
+ provider: "claude",
2029
+ sessionId,
2030
+ cwd,
2031
+ title: (title ?? "").trim() || `Claude session ${sessionId.slice(0, 8)}`,
2032
+ lastActivityAt: mtime,
2033
+ persistence,
2034
+ timeline,
2035
+ };
2036
+ }
2037
+ function extractClaudeUserText(message) {
2038
+ if (!message) {
2039
+ return null;
2040
+ }
2041
+ if (typeof message.content === "string") {
2042
+ return message.content.trim();
2043
+ }
2044
+ if (typeof message.text === "string") {
2045
+ return message.text.trim();
2046
+ }
2047
+ if (Array.isArray(message.content)) {
2048
+ for (const block of message.content) {
2049
+ if (block && typeof block.text === "string") {
2050
+ return block.text.trim();
2051
+ }
2052
+ }
2053
+ }
2054
+ return null;
2055
+ }
2056
+ //# sourceMappingURL=claude-agent.js.map