charness 0.0.0-stage → 3.0.0

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 (988) hide show
  1. package/.env.example +88 -0
  2. package/DEVELOPMENT.md +110 -0
  3. package/LICENSE +21 -0
  4. package/MIGRATION_1_TO_2.md +24 -0
  5. package/README.md +1024 -2
  6. package/dist/accessControl.d.ts +71 -0
  7. package/dist/accessControl.d.ts.map +1 -0
  8. package/dist/accessControl.js +108 -0
  9. package/dist/accessControl.js.map +1 -0
  10. package/dist/adapters/adapterEventRouting.d.ts +51 -0
  11. package/dist/adapters/adapterEventRouting.d.ts.map +1 -0
  12. package/dist/adapters/adapterEventRouting.js +74 -0
  13. package/dist/adapters/adapterEventRouting.js.map +1 -0
  14. package/dist/adapters/adapterNames.d.ts +19 -0
  15. package/dist/adapters/adapterNames.d.ts.map +1 -0
  16. package/dist/adapters/adapterNames.js +25 -0
  17. package/dist/adapters/adapterNames.js.map +1 -0
  18. package/dist/adapters/claudeCliAdapter.d.ts +1336 -0
  19. package/dist/adapters/claudeCliAdapter.d.ts.map +1 -0
  20. package/dist/adapters/claudeCliAdapter.js +4204 -0
  21. package/dist/adapters/claudeCliAdapter.js.map +1 -0
  22. package/dist/adapters/claudeJsonStreamAdapter.d.ts +548 -0
  23. package/dist/adapters/claudeJsonStreamAdapter.d.ts.map +1 -0
  24. package/dist/adapters/claudeJsonStreamAdapter.js +1810 -0
  25. package/dist/adapters/claudeJsonStreamAdapter.js.map +1 -0
  26. package/dist/adapters/claudePlatformFlags.d.ts +35 -0
  27. package/dist/adapters/claudePlatformFlags.d.ts.map +1 -0
  28. package/dist/adapters/claudePlatformFlags.js +46 -0
  29. package/dist/adapters/claudePlatformFlags.js.map +1 -0
  30. package/dist/adapters/createAdapter.d.ts +252 -0
  31. package/dist/adapters/createAdapter.d.ts.map +1 -0
  32. package/dist/adapters/createAdapter.js +475 -0
  33. package/dist/adapters/createAdapter.js.map +1 -0
  34. package/dist/adapters/openCodeAdapter.d.ts +1446 -0
  35. package/dist/adapters/openCodeAdapter.d.ts.map +1 -0
  36. package/dist/adapters/openCodeAdapter.js +4540 -0
  37. package/dist/adapters/openCodeAdapter.js.map +1 -0
  38. package/dist/adapters/sessionLaunchDefaults.d.ts +28 -0
  39. package/dist/adapters/sessionLaunchDefaults.d.ts.map +1 -0
  40. package/dist/adapters/sessionLaunchDefaults.js +15 -0
  41. package/dist/adapters/sessionLaunchDefaults.js.map +1 -0
  42. package/dist/adapters/terminalAdapter.d.ts +68 -0
  43. package/dist/adapters/terminalAdapter.d.ts.map +1 -0
  44. package/dist/adapters/terminalAdapter.js +371 -0
  45. package/dist/adapters/terminalAdapter.js.map +1 -0
  46. package/dist/agentLogin/agentLogin.d.ts +31 -0
  47. package/dist/agentLogin/agentLogin.d.ts.map +1 -0
  48. package/dist/agentLogin/agentLogin.js +493 -0
  49. package/dist/agentLogin/agentLogin.js.map +1 -0
  50. package/dist/agentTrigger.d.ts +28 -0
  51. package/dist/agentTrigger.d.ts.map +1 -0
  52. package/dist/agentTrigger.js +53 -0
  53. package/dist/agentTrigger.js.map +1 -0
  54. package/dist/apiErrorRetry.d.ts +209 -0
  55. package/dist/apiErrorRetry.d.ts.map +1 -0
  56. package/dist/apiErrorRetry.js +351 -0
  57. package/dist/apiErrorRetry.js.map +1 -0
  58. package/dist/apiRetryKick.d.ts +148 -0
  59. package/dist/apiRetryKick.d.ts.map +1 -0
  60. package/dist/apiRetryKick.js +158 -0
  61. package/dist/apiRetryKick.js.map +1 -0
  62. package/dist/bootClassifier.d.ts +46 -0
  63. package/dist/bootClassifier.d.ts.map +1 -0
  64. package/dist/bootClassifier.js +43 -0
  65. package/dist/bootClassifier.js.map +1 -0
  66. package/dist/bot.d.ts +207 -0
  67. package/dist/bot.d.ts.map +1 -0
  68. package/dist/bot.js +10611 -0
  69. package/dist/bot.js.map +1 -0
  70. package/dist/botFileStorage.d.ts +60 -0
  71. package/dist/botFileStorage.d.ts.map +1 -0
  72. package/dist/botFileStorage.js +205 -0
  73. package/dist/botFileStorage.js.map +1 -0
  74. package/dist/cli/applyDnsFix.d.ts +7 -0
  75. package/dist/cli/applyDnsFix.d.ts.map +1 -0
  76. package/dist/cli/applyDnsFix.js +46 -0
  77. package/dist/cli/applyDnsFix.js.map +1 -0
  78. package/dist/cli/bot.d.ts +27 -0
  79. package/dist/cli/bot.d.ts.map +1 -0
  80. package/dist/cli/bot.js +98 -0
  81. package/dist/cli/bot.js.map +1 -0
  82. package/dist/cli/botEntry.d.ts +3 -0
  83. package/dist/cli/botEntry.d.ts.map +1 -0
  84. package/dist/cli/botEntry.js +11 -0
  85. package/dist/cli/botEntry.js.map +1 -0
  86. package/dist/cli/connectorGuards.d.ts +31 -0
  87. package/dist/cli/connectorGuards.d.ts.map +1 -0
  88. package/dist/cli/connectorGuards.js +159 -0
  89. package/dist/cli/connectorGuards.js.map +1 -0
  90. package/dist/cli/envLoader.d.ts +34 -0
  91. package/dist/cli/envLoader.d.ts.map +1 -0
  92. package/dist/cli/envLoader.js +132 -0
  93. package/dist/cli/envLoader.js.map +1 -0
  94. package/dist/cli/hot.d.ts +82 -0
  95. package/dist/cli/hot.d.ts.map +1 -0
  96. package/dist/cli/hot.js +323 -0
  97. package/dist/cli/hot.js.map +1 -0
  98. package/dist/cli.d.ts +3 -0
  99. package/dist/cli.d.ts.map +1 -0
  100. package/dist/cli.js +130 -0
  101. package/dist/cli.js.map +1 -0
  102. package/dist/connectors/jira/adf.d.ts +72 -0
  103. package/dist/connectors/jira/adf.d.ts.map +1 -0
  104. package/dist/connectors/jira/adf.js +407 -0
  105. package/dist/connectors/jira/adf.js.map +1 -0
  106. package/dist/connectors/jira/agentBinaries.d.ts +14 -0
  107. package/dist/connectors/jira/agentBinaries.d.ts.map +1 -0
  108. package/dist/connectors/jira/agentBinaries.js +75 -0
  109. package/dist/connectors/jira/agentBinaries.js.map +1 -0
  110. package/dist/connectors/jira/answerSink.d.ts +52 -0
  111. package/dist/connectors/jira/answerSink.d.ts.map +1 -0
  112. package/dist/connectors/jira/answerSink.js +220 -0
  113. package/dist/connectors/jira/answerSink.js.map +1 -0
  114. package/dist/connectors/jira/attachmentDownload.d.ts +41 -0
  115. package/dist/connectors/jira/attachmentDownload.d.ts.map +1 -0
  116. package/dist/connectors/jira/attachmentDownload.js +92 -0
  117. package/dist/connectors/jira/attachmentDownload.js.map +1 -0
  118. package/dist/connectors/jira/attachmentTool.d.ts +43 -0
  119. package/dist/connectors/jira/attachmentTool.d.ts.map +1 -0
  120. package/dist/connectors/jira/attachmentTool.js +150 -0
  121. package/dist/connectors/jira/attachmentTool.js.map +1 -0
  122. package/dist/connectors/jira/client.d.ts +490 -0
  123. package/dist/connectors/jira/client.d.ts.map +1 -0
  124. package/dist/connectors/jira/client.js +453 -0
  125. package/dist/connectors/jira/client.js.map +1 -0
  126. package/dist/connectors/jira/config.d.ts +117 -0
  127. package/dist/connectors/jira/config.d.ts.map +1 -0
  128. package/dist/connectors/jira/config.js +350 -0
  129. package/dist/connectors/jira/config.js.map +1 -0
  130. package/dist/connectors/jira/configFile.d.ts +10 -0
  131. package/dist/connectors/jira/configFile.d.ts.map +1 -0
  132. package/dist/connectors/jira/configFile.js +50 -0
  133. package/dist/connectors/jira/configFile.js.map +1 -0
  134. package/dist/connectors/jira/connector.d.ts +68 -0
  135. package/dist/connectors/jira/connector.d.ts.map +1 -0
  136. package/dist/connectors/jira/connector.js +257 -0
  137. package/dist/connectors/jira/connector.js.map +1 -0
  138. package/dist/connectors/jira/contextLedger.d.ts +59 -0
  139. package/dist/connectors/jira/contextLedger.d.ts.map +1 -0
  140. package/dist/connectors/jira/contextLedger.js +194 -0
  141. package/dist/connectors/jira/contextLedger.js.map +1 -0
  142. package/dist/connectors/jira/contextPreamble.d.ts +18 -0
  143. package/dist/connectors/jira/contextPreamble.d.ts.map +1 -0
  144. package/dist/connectors/jira/contextPreamble.js +31 -0
  145. package/dist/connectors/jira/contextPreamble.js.map +1 -0
  146. package/dist/connectors/jira/inbound.d.ts +132 -0
  147. package/dist/connectors/jira/inbound.d.ts.map +1 -0
  148. package/dist/connectors/jira/inbound.js +316 -0
  149. package/dist/connectors/jira/inbound.js.map +1 -0
  150. package/dist/connectors/jira/issueBlocks.d.ts +51 -0
  151. package/dist/connectors/jira/issueBlocks.d.ts.map +1 -0
  152. package/dist/connectors/jira/issueBlocks.js +182 -0
  153. package/dist/connectors/jira/issueBlocks.js.map +1 -0
  154. package/dist/connectors/jira/issueContext.d.ts +25 -0
  155. package/dist/connectors/jira/issueContext.d.ts.map +1 -0
  156. package/dist/connectors/jira/issueContext.js +53 -0
  157. package/dist/connectors/jira/issueContext.js.map +1 -0
  158. package/dist/connectors/jira/issueDelta.d.ts +35 -0
  159. package/dist/connectors/jira/issueDelta.d.ts.map +1 -0
  160. package/dist/connectors/jira/issueDelta.js +50 -0
  161. package/dist/connectors/jira/issueDelta.js.map +1 -0
  162. package/dist/connectors/jira/mediaPlaceholders.d.ts +20 -0
  163. package/dist/connectors/jira/mediaPlaceholders.d.ts.map +1 -0
  164. package/dist/connectors/jira/mediaPlaceholders.js +92 -0
  165. package/dist/connectors/jira/mediaPlaceholders.js.map +1 -0
  166. package/dist/connectors/jira/outbound.d.ts +6 -0
  167. package/dist/connectors/jira/outbound.d.ts.map +1 -0
  168. package/dist/connectors/jira/outbound.js +38 -0
  169. package/dist/connectors/jira/outbound.js.map +1 -0
  170. package/dist/connectors/jira/prompt.d.ts +37 -0
  171. package/dist/connectors/jira/prompt.d.ts.map +1 -0
  172. package/dist/connectors/jira/prompt.js +99 -0
  173. package/dist/connectors/jira/prompt.js.map +1 -0
  174. package/dist/connectors/jira/promptSpill.d.ts +50 -0
  175. package/dist/connectors/jira/promptSpill.d.ts.map +1 -0
  176. package/dist/connectors/jira/promptSpill.js +157 -0
  177. package/dist/connectors/jira/promptSpill.js.map +1 -0
  178. package/dist/connectors/jira/promptText.d.ts +7 -0
  179. package/dist/connectors/jira/promptText.d.ts.map +1 -0
  180. package/dist/connectors/jira/promptText.js +26 -0
  181. package/dist/connectors/jira/promptText.js.map +1 -0
  182. package/dist/connectors/jira/sessionKeyCodec.d.ts +16 -0
  183. package/dist/connectors/jira/sessionKeyCodec.d.ts.map +1 -0
  184. package/dist/connectors/jira/sessionKeyCodec.js +101 -0
  185. package/dist/connectors/jira/sessionKeyCodec.js.map +1 -0
  186. package/dist/connectors/jira/trigger.d.ts +50 -0
  187. package/dist/connectors/jira/trigger.d.ts.map +1 -0
  188. package/dist/connectors/jira/trigger.js +93 -0
  189. package/dist/connectors/jira/trigger.js.map +1 -0
  190. package/dist/connectors/jira/triggerLog.d.ts +51 -0
  191. package/dist/connectors/jira/triggerLog.d.ts.map +1 -0
  192. package/dist/connectors/jira/triggerLog.js +78 -0
  193. package/dist/connectors/jira/triggerLog.js.map +1 -0
  194. package/dist/connectors/jira/unconfirmedPosts.d.ts +58 -0
  195. package/dist/connectors/jira/unconfirmedPosts.d.ts.map +1 -0
  196. package/dist/connectors/jira/unconfirmedPosts.js +105 -0
  197. package/dist/connectors/jira/unconfirmedPosts.js.map +1 -0
  198. package/dist/connectors/telegram/answerSink.d.ts +46 -0
  199. package/dist/connectors/telegram/answerSink.d.ts.map +1 -0
  200. package/dist/connectors/telegram/answerSink.js +71 -0
  201. package/dist/connectors/telegram/answerSink.js.map +1 -0
  202. package/dist/connectors/telegram/commands/autoContinueLimits.d.ts +31 -0
  203. package/dist/connectors/telegram/commands/autoContinueLimits.d.ts.map +1 -0
  204. package/dist/connectors/telegram/commands/autoContinueLimits.js +142 -0
  205. package/dist/connectors/telegram/commands/autoContinueLimits.js.map +1 -0
  206. package/dist/connectors/telegram/commands/botCore.d.ts +46 -0
  207. package/dist/connectors/telegram/commands/botCore.d.ts.map +1 -0
  208. package/dist/connectors/telegram/commands/botCore.js +3 -0
  209. package/dist/connectors/telegram/commands/botCore.js.map +1 -0
  210. package/dist/connectors/telegram/commands/callbackMessage.d.ts +12 -0
  211. package/dist/connectors/telegram/commands/callbackMessage.d.ts.map +1 -0
  212. package/dist/connectors/telegram/commands/callbackMessage.js +13 -0
  213. package/dist/connectors/telegram/commands/callbackMessage.js.map +1 -0
  214. package/dist/connectors/telegram/commands/compaction.d.ts +47 -0
  215. package/dist/connectors/telegram/commands/compaction.d.ts.map +1 -0
  216. package/dist/connectors/telegram/commands/compaction.js +868 -0
  217. package/dist/connectors/telegram/commands/compaction.js.map +1 -0
  218. package/dist/connectors/telegram/commands/displayModes.d.ts +13 -0
  219. package/dist/connectors/telegram/commands/displayModes.d.ts.map +1 -0
  220. package/dist/connectors/telegram/commands/displayModes.js +335 -0
  221. package/dist/connectors/telegram/commands/displayModes.js.map +1 -0
  222. package/dist/connectors/telegram/commands/modelProviders.d.ts +95 -0
  223. package/dist/connectors/telegram/commands/modelProviders.d.ts.map +1 -0
  224. package/dist/connectors/telegram/commands/modelProviders.js +841 -0
  225. package/dist/connectors/telegram/commands/modelProviders.js.map +1 -0
  226. package/dist/connectors/telegram/commands/reminders.d.ts +54 -0
  227. package/dist/connectors/telegram/commands/reminders.d.ts.map +1 -0
  228. package/dist/connectors/telegram/commands/reminders.js +624 -0
  229. package/dist/connectors/telegram/commands/reminders.js.map +1 -0
  230. package/dist/connectors/telegram/fileIntake.d.ts +166 -0
  231. package/dist/connectors/telegram/fileIntake.d.ts.map +1 -0
  232. package/dist/connectors/telegram/fileIntake.js +304 -0
  233. package/dist/connectors/telegram/fileIntake.js.map +1 -0
  234. package/dist/connectors/telegram/fileSendGateway.d.ts +57 -0
  235. package/dist/connectors/telegram/fileSendGateway.d.ts.map +1 -0
  236. package/dist/connectors/telegram/fileSendGateway.js +221 -0
  237. package/dist/connectors/telegram/fileSendGateway.js.map +1 -0
  238. package/dist/connectors/telegram/foreignKeyFallbacks.d.ts +52 -0
  239. package/dist/connectors/telegram/foreignKeyFallbacks.d.ts.map +1 -0
  240. package/dist/connectors/telegram/foreignKeyFallbacks.js +71 -0
  241. package/dist/connectors/telegram/foreignKeyFallbacks.js.map +1 -0
  242. package/dist/connectors/telegram/inbound.d.ts +131 -0
  243. package/dist/connectors/telegram/inbound.d.ts.map +1 -0
  244. package/dist/connectors/telegram/inbound.js +278 -0
  245. package/dist/connectors/telegram/inbound.js.map +1 -0
  246. package/dist/connectors/telegram/languagePicker.d.ts +32 -0
  247. package/dist/connectors/telegram/languagePicker.d.ts.map +1 -0
  248. package/dist/connectors/telegram/languagePicker.js +55 -0
  249. package/dist/connectors/telegram/languagePicker.js.map +1 -0
  250. package/dist/connectors/telegram/messageSplit.d.ts +62 -0
  251. package/dist/connectors/telegram/messageSplit.d.ts.map +1 -0
  252. package/dist/connectors/telegram/messageSplit.js +119 -0
  253. package/dist/connectors/telegram/messageSplit.js.map +1 -0
  254. package/dist/connectors/telegram/outbound.d.ts +62 -0
  255. package/dist/connectors/telegram/outbound.d.ts.map +1 -0
  256. package/dist/connectors/telegram/outbound.js +116 -0
  257. package/dist/connectors/telegram/outbound.js.map +1 -0
  258. package/dist/connectors/telegram/output/createOutputTransport.d.ts +34 -0
  259. package/dist/connectors/telegram/output/createOutputTransport.d.ts.map +1 -0
  260. package/dist/connectors/telegram/output/createOutputTransport.js +77 -0
  261. package/dist/connectors/telegram/output/createOutputTransport.js.map +1 -0
  262. package/dist/connectors/telegram/output/dmOutputTransport.d.ts +47 -0
  263. package/dist/connectors/telegram/output/dmOutputTransport.d.ts.map +1 -0
  264. package/dist/connectors/telegram/output/dmOutputTransport.js +412 -0
  265. package/dist/connectors/telegram/output/dmOutputTransport.js.map +1 -0
  266. package/dist/connectors/telegram/outputBacklogGlue.d.ts +38 -0
  267. package/dist/connectors/telegram/outputBacklogGlue.d.ts.map +1 -0
  268. package/dist/connectors/telegram/outputBacklogGlue.js +49 -0
  269. package/dist/connectors/telegram/outputBacklogGlue.js.map +1 -0
  270. package/dist/connectors/telegram/outputFlushPlan.d.ts +93 -0
  271. package/dist/connectors/telegram/outputFlushPlan.d.ts.map +1 -0
  272. package/dist/connectors/telegram/outputFlushPlan.js +120 -0
  273. package/dist/connectors/telegram/outputFlushPlan.js.map +1 -0
  274. package/dist/connectors/telegram/questionKeyboards.d.ts +30 -0
  275. package/dist/connectors/telegram/questionKeyboards.d.ts.map +1 -0
  276. package/dist/connectors/telegram/questionKeyboards.js +37 -0
  277. package/dist/connectors/telegram/questionKeyboards.js.map +1 -0
  278. package/dist/connectors/telegram/renderAgentHtml.d.ts +27 -0
  279. package/dist/connectors/telegram/renderAgentHtml.d.ts.map +1 -0
  280. package/dist/connectors/telegram/renderAgentHtml.js +97 -0
  281. package/dist/connectors/telegram/renderAgentHtml.js.map +1 -0
  282. package/dist/connectors/telegram/sessionKeyCodec.d.ts +28 -0
  283. package/dist/connectors/telegram/sessionKeyCodec.d.ts.map +1 -0
  284. package/dist/connectors/telegram/sessionKeyCodec.js +107 -0
  285. package/dist/connectors/telegram/sessionKeyCodec.js.map +1 -0
  286. package/dist/connectors/telegram/telegramCallGuard.d.ts +19 -0
  287. package/dist/connectors/telegram/telegramCallGuard.d.ts.map +1 -0
  288. package/dist/connectors/telegram/telegramCallGuard.js +35 -0
  289. package/dist/connectors/telegram/telegramCallGuard.js.map +1 -0
  290. package/dist/connectors/telegram/timezonePicker.d.ts +85 -0
  291. package/dist/connectors/telegram/timezonePicker.d.ts.map +1 -0
  292. package/dist/connectors/telegram/timezonePicker.js +142 -0
  293. package/dist/connectors/telegram/timezonePicker.js.map +1 -0
  294. package/dist/connectors/telegram/updateDispatcher.d.ts +85 -0
  295. package/dist/connectors/telegram/updateDispatcher.d.ts.map +1 -0
  296. package/dist/connectors/telegram/updateDispatcher.js +159 -0
  297. package/dist/connectors/telegram/updateDispatcher.js.map +1 -0
  298. package/dist/connectors/test/connector.d.ts +52 -0
  299. package/dist/connectors/test/connector.d.ts.map +1 -0
  300. package/dist/connectors/test/connector.js +143 -0
  301. package/dist/connectors/test/connector.js.map +1 -0
  302. package/dist/connectors/test/sessionKeyCodec.d.ts +25 -0
  303. package/dist/connectors/test/sessionKeyCodec.d.ts.map +1 -0
  304. package/dist/connectors/test/sessionKeyCodec.js +50 -0
  305. package/dist/connectors/test/sessionKeyCodec.js.map +1 -0
  306. package/dist/diagLog.d.ts +6 -0
  307. package/dist/diagLog.d.ts.map +1 -0
  308. package/dist/diagLog.js +58 -0
  309. package/dist/diagLog.js.map +1 -0
  310. package/dist/effortLevels.d.ts +68 -0
  311. package/dist/effortLevels.d.ts.map +1 -0
  312. package/dist/effortLevels.js +114 -0
  313. package/dist/effortLevels.js.map +1 -0
  314. package/dist/folderName.d.ts +42 -0
  315. package/dist/folderName.d.ts.map +1 -0
  316. package/dist/folderName.js +51 -0
  317. package/dist/folderName.js.map +1 -0
  318. package/dist/i18n/de.d.ts +2 -0
  319. package/dist/i18n/de.d.ts.map +1 -0
  320. package/dist/i18n/de.js +537 -0
  321. package/dist/i18n/de.js.map +1 -0
  322. package/dist/i18n/en.d.ts +7 -0
  323. package/dist/i18n/en.d.ts.map +1 -0
  324. package/dist/i18n/en.js +566 -0
  325. package/dist/i18n/en.js.map +1 -0
  326. package/dist/i18n/es.d.ts +2 -0
  327. package/dist/i18n/es.d.ts.map +1 -0
  328. package/dist/i18n/es.js +537 -0
  329. package/dist/i18n/es.js.map +1 -0
  330. package/dist/i18n/fr.d.ts +2 -0
  331. package/dist/i18n/fr.d.ts.map +1 -0
  332. package/dist/i18n/fr.js +537 -0
  333. package/dist/i18n/fr.js.map +1 -0
  334. package/dist/i18n/hi.d.ts +2 -0
  335. package/dist/i18n/hi.d.ts.map +1 -0
  336. package/dist/i18n/hi.js +537 -0
  337. package/dist/i18n/hi.js.map +1 -0
  338. package/dist/i18n/ja.d.ts +2 -0
  339. package/dist/i18n/ja.d.ts.map +1 -0
  340. package/dist/i18n/ja.js +537 -0
  341. package/dist/i18n/ja.js.map +1 -0
  342. package/dist/i18n/ka.d.ts +2 -0
  343. package/dist/i18n/ka.d.ts.map +1 -0
  344. package/dist/i18n/ka.js +537 -0
  345. package/dist/i18n/ka.js.map +1 -0
  346. package/dist/i18n/pt.d.ts +2 -0
  347. package/dist/i18n/pt.d.ts.map +1 -0
  348. package/dist/i18n/pt.js +537 -0
  349. package/dist/i18n/pt.js.map +1 -0
  350. package/dist/i18n/ru.d.ts +6 -0
  351. package/dist/i18n/ru.d.ts.map +1 -0
  352. package/dist/i18n/ru.js +540 -0
  353. package/dist/i18n/ru.js.map +1 -0
  354. package/dist/i18n/uk.d.ts +8 -0
  355. package/dist/i18n/uk.d.ts.map +1 -0
  356. package/dist/i18n/uk.js +542 -0
  357. package/dist/i18n/uk.js.map +1 -0
  358. package/dist/i18n/uz.d.ts +2 -0
  359. package/dist/i18n/uz.d.ts.map +1 -0
  360. package/dist/i18n/uz.js +537 -0
  361. package/dist/i18n/uz.js.map +1 -0
  362. package/dist/i18n/zh.d.ts +2 -0
  363. package/dist/i18n/zh.d.ts.map +1 -0
  364. package/dist/i18n/zh.js +537 -0
  365. package/dist/i18n/zh.js.map +1 -0
  366. package/dist/i18n.d.ts +115 -0
  367. package/dist/i18n.d.ts.map +1 -0
  368. package/dist/i18n.js +233 -0
  369. package/dist/i18n.js.map +1 -0
  370. package/dist/index.d.ts +3 -0
  371. package/dist/index.d.ts.map +1 -0
  372. package/dist/index.js +10 -0
  373. package/dist/index.js.map +1 -0
  374. package/dist/installManager.d.ts +99 -0
  375. package/dist/installManager.d.ts.map +1 -0
  376. package/dist/installManager.js +943 -0
  377. package/dist/installManager.js.map +1 -0
  378. package/dist/instanceLock.d.ts +161 -0
  379. package/dist/instanceLock.d.ts.map +1 -0
  380. package/dist/instanceLock.js +388 -0
  381. package/dist/instanceLock.js.map +1 -0
  382. package/dist/mcpConfig.d.ts +76 -0
  383. package/dist/mcpConfig.d.ts.map +1 -0
  384. package/dist/mcpConfig.js +270 -0
  385. package/dist/mcpConfig.js.map +1 -0
  386. package/dist/openCodeOpenQuestion.d.ts +44 -0
  387. package/dist/openCodeOpenQuestion.d.ts.map +1 -0
  388. package/dist/openCodeOpenQuestion.js +64 -0
  389. package/dist/openCodeOpenQuestion.js.map +1 -0
  390. package/dist/openCodeQuestionFlow.d.ts +105 -0
  391. package/dist/openCodeQuestionFlow.d.ts.map +1 -0
  392. package/dist/openCodeQuestionFlow.js +143 -0
  393. package/dist/openCodeQuestionFlow.js.map +1 -0
  394. package/dist/openCodeSessionRouting.d.ts +133 -0
  395. package/dist/openCodeSessionRouting.d.ts.map +1 -0
  396. package/dist/openCodeSessionRouting.js +253 -0
  397. package/dist/openCodeSessionRouting.js.map +1 -0
  398. package/dist/openCodeSessionTitle.d.ts +50 -0
  399. package/dist/openCodeSessionTitle.d.ts.map +1 -0
  400. package/dist/openCodeSessionTitle.js +84 -0
  401. package/dist/openCodeSessionTitle.js.map +1 -0
  402. package/dist/outputTrace.d.ts +108 -0
  403. package/dist/outputTrace.d.ts.map +1 -0
  404. package/dist/outputTrace.js +430 -0
  405. package/dist/outputTrace.js.map +1 -0
  406. package/dist/pendingQuestionRepost.d.ts +47 -0
  407. package/dist/pendingQuestionRepost.d.ts.map +1 -0
  408. package/dist/pendingQuestionRepost.js +29 -0
  409. package/dist/pendingQuestionRepost.js.map +1 -0
  410. package/dist/pinnedStatus.d.ts +52 -0
  411. package/dist/pinnedStatus.d.ts.map +1 -0
  412. package/dist/pinnedStatus.js +36 -0
  413. package/dist/pinnedStatus.js.map +1 -0
  414. package/dist/platform/answerSink.d.ts +93 -0
  415. package/dist/platform/answerSink.d.ts.map +1 -0
  416. package/dist/platform/answerSink.js +31 -0
  417. package/dist/platform/answerSink.js.map +1 -0
  418. package/dist/platform/capabilityFallback.d.ts +39 -0
  419. package/dist/platform/capabilityFallback.d.ts.map +1 -0
  420. package/dist/platform/capabilityFallback.js +55 -0
  421. package/dist/platform/capabilityFallback.js.map +1 -0
  422. package/dist/platform/commandRouter.d.ts +51 -0
  423. package/dist/platform/commandRouter.d.ts.map +1 -0
  424. package/dist/platform/commandRouter.js +53 -0
  425. package/dist/platform/commandRouter.js.map +1 -0
  426. package/dist/platform/connectorSet.d.ts +39 -0
  427. package/dist/platform/connectorSet.d.ts.map +1 -0
  428. package/dist/platform/connectorSet.js +55 -0
  429. package/dist/platform/connectorSet.js.map +1 -0
  430. package/dist/platform/inbound.d.ts +147 -0
  431. package/dist/platform/inbound.d.ts.map +1 -0
  432. package/dist/platform/inbound.js +18 -0
  433. package/dist/platform/inbound.js.map +1 -0
  434. package/dist/platform/outbound.d.ts +225 -0
  435. package/dist/platform/outbound.d.ts.map +1 -0
  436. package/dist/platform/outbound.js +30 -0
  437. package/dist/platform/outbound.js.map +1 -0
  438. package/dist/platform/unservedStateGuard.d.ts +27 -0
  439. package/dist/platform/unservedStateGuard.d.ts.map +1 -0
  440. package/dist/platform/unservedStateGuard.js +42 -0
  441. package/dist/platform/unservedStateGuard.js.map +1 -0
  442. package/dist/postToSession.d.ts +94 -0
  443. package/dist/postToSession.d.ts.map +1 -0
  444. package/dist/postToSession.js +56 -0
  445. package/dist/postToSession.js.map +1 -0
  446. package/dist/progressLine.d.ts +180 -0
  447. package/dist/progressLine.d.ts.map +1 -0
  448. package/dist/progressLine.js +293 -0
  449. package/dist/progressLine.js.map +1 -0
  450. package/dist/rateLimiter.d.ts +246 -0
  451. package/dist/rateLimiter.d.ts.map +1 -0
  452. package/dist/rateLimiter.js +555 -0
  453. package/dist/rateLimiter.js.map +1 -0
  454. package/dist/requests/answerRequest.d.ts +57 -0
  455. package/dist/requests/answerRequest.d.ts.map +1 -0
  456. package/dist/requests/answerRequest.js +96 -0
  457. package/dist/requests/answerRequest.js.map +1 -0
  458. package/dist/requests/answerTail.d.ts +83 -0
  459. package/dist/requests/answerTail.d.ts.map +1 -0
  460. package/dist/requests/answerTail.js +102 -0
  461. package/dist/requests/answerTail.js.map +1 -0
  462. package/dist/requests/limitWaitAnswer.d.ts +87 -0
  463. package/dist/requests/limitWaitAnswer.d.ts.map +1 -0
  464. package/dist/requests/limitWaitAnswer.js +83 -0
  465. package/dist/requests/limitWaitAnswer.js.map +1 -0
  466. package/dist/requests/requestGroup.d.ts +44 -0
  467. package/dist/requests/requestGroup.d.ts.map +1 -0
  468. package/dist/requests/requestGroup.js +63 -0
  469. package/dist/requests/requestGroup.js.map +1 -0
  470. package/dist/requests/requestHeader.d.ts +70 -0
  471. package/dist/requests/requestHeader.d.ts.map +1 -0
  472. package/dist/requests/requestHeader.js +83 -0
  473. package/dist/requests/requestHeader.js.map +1 -0
  474. package/dist/requests/requestLedger.d.ts +217 -0
  475. package/dist/requests/requestLedger.d.ts.map +1 -0
  476. package/dist/requests/requestLedger.js +468 -0
  477. package/dist/requests/requestLedger.js.map +1 -0
  478. package/dist/requests/sessionTurnProbe.d.ts +52 -0
  479. package/dist/requests/sessionTurnProbe.d.ts.map +1 -0
  480. package/dist/requests/sessionTurnProbe.js +29 -0
  481. package/dist/requests/sessionTurnProbe.js.map +1 -0
  482. package/dist/requests/types.d.ts +181 -0
  483. package/dist/requests/types.d.ts.map +1 -0
  484. package/dist/requests/types.js +9 -0
  485. package/dist/requests/types.js.map +1 -0
  486. package/dist/requests/wakeUpEngine.d.ts +160 -0
  487. package/dist/requests/wakeUpEngine.d.ts.map +1 -0
  488. package/dist/requests/wakeUpEngine.js +399 -0
  489. package/dist/requests/wakeUpEngine.js.map +1 -0
  490. package/dist/requests/wakeUpRules.d.ts +151 -0
  491. package/dist/requests/wakeUpRules.d.ts.map +1 -0
  492. package/dist/requests/wakeUpRules.js +167 -0
  493. package/dist/requests/wakeUpRules.js.map +1 -0
  494. package/dist/resumeContext.d.ts +87 -0
  495. package/dist/resumeContext.d.ts.map +1 -0
  496. package/dist/resumeContext.js +114 -0
  497. package/dist/resumeContext.js.map +1 -0
  498. package/dist/scheduler/checkRun.d.ts +95 -0
  499. package/dist/scheduler/checkRun.d.ts.map +1 -0
  500. package/dist/scheduler/checkRun.js +172 -0
  501. package/dist/scheduler/checkRun.js.map +1 -0
  502. package/dist/scheduler/delivery.d.ts +99 -0
  503. package/dist/scheduler/delivery.d.ts.map +1 -0
  504. package/dist/scheduler/delivery.js +212 -0
  505. package/dist/scheduler/delivery.js.map +1 -0
  506. package/dist/scheduler/deliveryKind.d.ts +29 -0
  507. package/dist/scheduler/deliveryKind.d.ts.map +1 -0
  508. package/dist/scheduler/deliveryKind.js +39 -0
  509. package/dist/scheduler/deliveryKind.js.map +1 -0
  510. package/dist/scheduler/directoryThreads.d.ts +19 -0
  511. package/dist/scheduler/directoryThreads.d.ts.map +1 -0
  512. package/dist/scheduler/directoryThreads.js +28 -0
  513. package/dist/scheduler/directoryThreads.js.map +1 -0
  514. package/dist/scheduler/engine.d.ts +77 -0
  515. package/dist/scheduler/engine.d.ts.map +1 -0
  516. package/dist/scheduler/engine.js +337 -0
  517. package/dist/scheduler/engine.js.map +1 -0
  518. package/dist/scheduler/injection.d.ts +102 -0
  519. package/dist/scheduler/injection.d.ts.map +1 -0
  520. package/dist/scheduler/injection.js +122 -0
  521. package/dist/scheduler/injection.js.map +1 -0
  522. package/dist/scheduler/mcpBoot.d.ts +65 -0
  523. package/dist/scheduler/mcpBoot.d.ts.map +1 -0
  524. package/dist/scheduler/mcpBoot.js +60 -0
  525. package/dist/scheduler/mcpBoot.js.map +1 -0
  526. package/dist/scheduler/mcpSurface.d.ts +355 -0
  527. package/dist/scheduler/mcpSurface.d.ts.map +1 -0
  528. package/dist/scheduler/mcpSurface.js +1257 -0
  529. package/dist/scheduler/mcpSurface.js.map +1 -0
  530. package/dist/scheduler/rebindResume.d.ts +27 -0
  531. package/dist/scheduler/rebindResume.d.ts.map +1 -0
  532. package/dist/scheduler/rebindResume.js +17 -0
  533. package/dist/scheduler/rebindResume.js.map +1 -0
  534. package/dist/scheduler/recurrence.d.ts +58 -0
  535. package/dist/scheduler/recurrence.d.ts.map +1 -0
  536. package/dist/scheduler/recurrence.js +210 -0
  537. package/dist/scheduler/recurrence.js.map +1 -0
  538. package/dist/scheduler/runLedger.d.ts +59 -0
  539. package/dist/scheduler/runLedger.d.ts.map +1 -0
  540. package/dist/scheduler/runLedger.js +54 -0
  541. package/dist/scheduler/runLedger.js.map +1 -0
  542. package/dist/scheduler/store.d.ts +97 -0
  543. package/dist/scheduler/store.d.ts.map +1 -0
  544. package/dist/scheduler/store.js +124 -0
  545. package/dist/scheduler/store.js.map +1 -0
  546. package/dist/scheduler/timezoneRecompute.d.ts +61 -0
  547. package/dist/scheduler/timezoneRecompute.d.ts.map +1 -0
  548. package/dist/scheduler/timezoneRecompute.js +46 -0
  549. package/dist/scheduler/timezoneRecompute.js.map +1 -0
  550. package/dist/scheduler/types.d.ts +139 -0
  551. package/dist/scheduler/types.d.ts.map +1 -0
  552. package/dist/scheduler/types.js +8 -0
  553. package/dist/scheduler/types.js.map +1 -0
  554. package/dist/sendErrorClassifier.d.ts +45 -0
  555. package/dist/sendErrorClassifier.d.ts.map +1 -0
  556. package/dist/sendErrorClassifier.js +65 -0
  557. package/dist/sendErrorClassifier.js.map +1 -0
  558. package/dist/sessionKey.d.ts +136 -0
  559. package/dist/sessionKey.d.ts.map +1 -0
  560. package/dist/sessionKey.js +153 -0
  561. package/dist/sessionKey.js.map +1 -0
  562. package/dist/sessionPick.d.ts +42 -0
  563. package/dist/sessionPick.d.ts.map +1 -0
  564. package/dist/sessionPick.js +39 -0
  565. package/dist/sessionPick.js.map +1 -0
  566. package/dist/shutdown.d.ts +126 -0
  567. package/dist/shutdown.d.ts.map +1 -0
  568. package/dist/shutdown.js +164 -0
  569. package/dist/shutdown.js.map +1 -0
  570. package/dist/startupPromptBuffer.d.ts +85 -0
  571. package/dist/startupPromptBuffer.d.ts.map +1 -0
  572. package/dist/startupPromptBuffer.js +123 -0
  573. package/dist/startupPromptBuffer.js.map +1 -0
  574. package/dist/state.d.ts +1245 -0
  575. package/dist/state.d.ts.map +1 -0
  576. package/dist/state.js +1830 -0
  577. package/dist/state.js.map +1 -0
  578. package/dist/threadContextPreamble.d.ts +89 -0
  579. package/dist/threadContextPreamble.d.ts.map +1 -0
  580. package/dist/threadContextPreamble.js +102 -0
  581. package/dist/threadContextPreamble.js.map +1 -0
  582. package/dist/threadRouting.d.ts +164 -0
  583. package/dist/threadRouting.d.ts.map +1 -0
  584. package/dist/threadRouting.js +182 -0
  585. package/dist/threadRouting.js.map +1 -0
  586. package/dist/types.d.ts +1015 -0
  587. package/dist/types.d.ts.map +1 -0
  588. package/dist/types.js +3 -0
  589. package/dist/types.js.map +1 -0
  590. package/dist/utils/abortableFifo.d.ts +10 -0
  591. package/dist/utils/abortableFifo.d.ts.map +1 -0
  592. package/dist/utils/abortableFifo.js +55 -0
  593. package/dist/utils/abortableFifo.js.map +1 -0
  594. package/dist/utils/adoptedToolList.d.ts +27 -0
  595. package/dist/utils/adoptedToolList.d.ts.map +1 -0
  596. package/dist/utils/adoptedToolList.js +22 -0
  597. package/dist/utils/adoptedToolList.js.map +1 -0
  598. package/dist/utils/agentEnvironment.d.ts +42 -0
  599. package/dist/utils/agentEnvironment.d.ts.map +1 -0
  600. package/dist/utils/agentEnvironment.js +122 -0
  601. package/dist/utils/agentEnvironment.js.map +1 -0
  602. package/dist/utils/ansiClean.d.ts +11 -0
  603. package/dist/utils/ansiClean.d.ts.map +1 -0
  604. package/dist/utils/ansiClean.js +139 -0
  605. package/dist/utils/ansiClean.js.map +1 -0
  606. package/dist/utils/autoContinueOnLimit.d.ts +49 -0
  607. package/dist/utils/autoContinueOnLimit.d.ts.map +1 -0
  608. package/dist/utils/autoContinueOnLimit.js +69 -0
  609. package/dist/utils/autoContinueOnLimit.js.map +1 -0
  610. package/dist/utils/canonicalPathContainment.d.ts +7 -0
  611. package/dist/utils/canonicalPathContainment.d.ts.map +1 -0
  612. package/dist/utils/canonicalPathContainment.js +48 -0
  613. package/dist/utils/canonicalPathContainment.js.map +1 -0
  614. package/dist/utils/claudeAuthLogin.d.ts +71 -0
  615. package/dist/utils/claudeAuthLogin.d.ts.map +1 -0
  616. package/dist/utils/claudeAuthLogin.js +93 -0
  617. package/dist/utils/claudeAuthLogin.js.map +1 -0
  618. package/dist/utils/claudeChunkClassifier.d.ts +97 -0
  619. package/dist/utils/claudeChunkClassifier.d.ts.map +1 -0
  620. package/dist/utils/claudeChunkClassifier.js +254 -0
  621. package/dist/utils/claudeChunkClassifier.js.map +1 -0
  622. package/dist/utils/claudeCodeVersion.d.ts +31 -0
  623. package/dist/utils/claudeCodeVersion.d.ts.map +1 -0
  624. package/dist/utils/claudeCodeVersion.js +62 -0
  625. package/dist/utils/claudeCodeVersion.js.map +1 -0
  626. package/dist/utils/claudeCompactHook.d.ts +64 -0
  627. package/dist/utils/claudeCompactHook.d.ts.map +1 -0
  628. package/dist/utils/claudeCompactHook.js +98 -0
  629. package/dist/utils/claudeCompactHook.js.map +1 -0
  630. package/dist/utils/claudeLivenessAction.d.ts +208 -0
  631. package/dist/utils/claudeLivenessAction.d.ts.map +1 -0
  632. package/dist/utils/claudeLivenessAction.js +135 -0
  633. package/dist/utils/claudeLivenessAction.js.map +1 -0
  634. package/dist/utils/claudeMcpHeal.d.ts +92 -0
  635. package/dist/utils/claudeMcpHeal.d.ts.map +1 -0
  636. package/dist/utils/claudeMcpHeal.js +91 -0
  637. package/dist/utils/claudeMcpHeal.js.map +1 -0
  638. package/dist/utils/claudeRelayRouting.d.ts +86 -0
  639. package/dist/utils/claudeRelayRouting.d.ts.map +1 -0
  640. package/dist/utils/claudeRelayRouting.js +159 -0
  641. package/dist/utils/claudeRelayRouting.js.map +1 -0
  642. package/dist/utils/claudeRuntimeInfo.d.ts +26 -0
  643. package/dist/utils/claudeRuntimeInfo.d.ts.map +1 -0
  644. package/dist/utils/claudeRuntimeInfo.js +151 -0
  645. package/dist/utils/claudeRuntimeInfo.js.map +1 -0
  646. package/dist/utils/claudeScrapeShapes.d.ts +256 -0
  647. package/dist/utils/claudeScrapeShapes.d.ts.map +1 -0
  648. package/dist/utils/claudeScrapeShapes.js +290 -0
  649. package/dist/utils/claudeScrapeShapes.js.map +1 -0
  650. package/dist/utils/claudeSessionTitle.d.ts +27 -0
  651. package/dist/utils/claudeSessionTitle.d.ts.map +1 -0
  652. package/dist/utils/claudeSessionTitle.js +90 -0
  653. package/dist/utils/claudeSessionTitle.js.map +1 -0
  654. package/dist/utils/claudeStreamJson.d.ts +202 -0
  655. package/dist/utils/claudeStreamJson.d.ts.map +1 -0
  656. package/dist/utils/claudeStreamJson.js +351 -0
  657. package/dist/utils/claudeStreamJson.js.map +1 -0
  658. package/dist/utils/claudeSubagentTail.d.ts +93 -0
  659. package/dist/utils/claudeSubagentTail.d.ts.map +1 -0
  660. package/dist/utils/claudeSubagentTail.js +136 -0
  661. package/dist/utils/claudeSubagentTail.js.map +1 -0
  662. package/dist/utils/clearThreadOutputQueues.d.ts +43 -0
  663. package/dist/utils/clearThreadOutputQueues.d.ts.map +1 -0
  664. package/dist/utils/clearThreadOutputQueues.js +39 -0
  665. package/dist/utils/clearThreadOutputQueues.js.map +1 -0
  666. package/dist/utils/compactCommandRoute.d.ts +45 -0
  667. package/dist/utils/compactCommandRoute.d.ts.map +1 -0
  668. package/dist/utils/compactCommandRoute.js +36 -0
  669. package/dist/utils/compactCommandRoute.js.map +1 -0
  670. package/dist/utils/compactOnIdle.d.ts +329 -0
  671. package/dist/utils/compactOnIdle.d.ts.map +1 -0
  672. package/dist/utils/compactOnIdle.js +379 -0
  673. package/dist/utils/compactOnIdle.js.map +1 -0
  674. package/dist/utils/consoleFileTap.d.ts +51 -0
  675. package/dist/utils/consoleFileTap.d.ts.map +1 -0
  676. package/dist/utils/consoleFileTap.js +86 -0
  677. package/dist/utils/consoleFileTap.js.map +1 -0
  678. package/dist/utils/displayVerbosity.d.ts +44 -0
  679. package/dist/utils/displayVerbosity.d.ts.map +1 -0
  680. package/dist/utils/displayVerbosity.js +75 -0
  681. package/dist/utils/displayVerbosity.js.map +1 -0
  682. package/dist/utils/download.d.ts +72 -0
  683. package/dist/utils/download.d.ts.map +1 -0
  684. package/dist/utils/download.js +216 -0
  685. package/dist/utils/download.js.map +1 -0
  686. package/dist/utils/draftFinalize.d.ts +110 -0
  687. package/dist/utils/draftFinalize.d.ts.map +1 -0
  688. package/dist/utils/draftFinalize.js +100 -0
  689. package/dist/utils/draftFinalize.js.map +1 -0
  690. package/dist/utils/draftId.d.ts +36 -0
  691. package/dist/utils/draftId.d.ts.map +1 -0
  692. package/dist/utils/draftId.js +46 -0
  693. package/dist/utils/draftId.js.map +1 -0
  694. package/dist/utils/draftPacer.d.ts +72 -0
  695. package/dist/utils/draftPacer.d.ts.map +1 -0
  696. package/dist/utils/draftPacer.js +53 -0
  697. package/dist/utils/draftPacer.js.map +1 -0
  698. package/dist/utils/effortStartupKeystroke.d.ts +20 -0
  699. package/dist/utils/effortStartupKeystroke.d.ts.map +1 -0
  700. package/dist/utils/effortStartupKeystroke.js +28 -0
  701. package/dist/utils/effortStartupKeystroke.js.map +1 -0
  702. package/dist/utils/ensureSessionPlan.d.ts +70 -0
  703. package/dist/utils/ensureSessionPlan.d.ts.map +1 -0
  704. package/dist/utils/ensureSessionPlan.js +54 -0
  705. package/dist/utils/ensureSessionPlan.js.map +1 -0
  706. package/dist/utils/fileSendPlan.d.ts +217 -0
  707. package/dist/utils/fileSendPlan.d.ts.map +1 -0
  708. package/dist/utils/fileSendPlan.js +291 -0
  709. package/dist/utils/fileSendPlan.js.map +1 -0
  710. package/dist/utils/fileSendService.d.ts +94 -0
  711. package/dist/utils/fileSendService.d.ts.map +1 -0
  712. package/dist/utils/fileSendService.js +387 -0
  713. package/dist/utils/fileSendService.js.map +1 -0
  714. package/dist/utils/groupFinalizePlan.d.ts +38 -0
  715. package/dist/utils/groupFinalizePlan.d.ts.map +1 -0
  716. package/dist/utils/groupFinalizePlan.js +35 -0
  717. package/dist/utils/groupFinalizePlan.js.map +1 -0
  718. package/dist/utils/identicalOutputGuard.d.ts +83 -0
  719. package/dist/utils/identicalOutputGuard.d.ts.map +1 -0
  720. package/dist/utils/identicalOutputGuard.js +99 -0
  721. package/dist/utils/identicalOutputGuard.js.map +1 -0
  722. package/dist/utils/isoTimestamp.d.ts +16 -0
  723. package/dist/utils/isoTimestamp.d.ts.map +1 -0
  724. package/dist/utils/isoTimestamp.js +34 -0
  725. package/dist/utils/isoTimestamp.js.map +1 -0
  726. package/dist/utils/jsonStreamBusyWatchdog.d.ts +130 -0
  727. package/dist/utils/jsonStreamBusyWatchdog.d.ts.map +1 -0
  728. package/dist/utils/jsonStreamBusyWatchdog.js +122 -0
  729. package/dist/utils/jsonStreamBusyWatchdog.js.map +1 -0
  730. package/dist/utils/jsonStreamHost.d.ts +227 -0
  731. package/dist/utils/jsonStreamHost.d.ts.map +1 -0
  732. package/dist/utils/jsonStreamHost.js +417 -0
  733. package/dist/utils/jsonStreamHost.js.map +1 -0
  734. package/dist/utils/keyedTransitionQueue.d.ts +18 -0
  735. package/dist/utils/keyedTransitionQueue.d.ts.map +1 -0
  736. package/dist/utils/keyedTransitionQueue.js +34 -0
  737. package/dist/utils/keyedTransitionQueue.js.map +1 -0
  738. package/dist/utils/limitEpisodeRecovery.d.ts +92 -0
  739. package/dist/utils/limitEpisodeRecovery.d.ts.map +1 -0
  740. package/dist/utils/limitEpisodeRecovery.js +133 -0
  741. package/dist/utils/limitEpisodeRecovery.js.map +1 -0
  742. package/dist/utils/limitHeldPromptQueue.d.ts +48 -0
  743. package/dist/utils/limitHeldPromptQueue.d.ts.map +1 -0
  744. package/dist/utils/limitHeldPromptQueue.js +59 -0
  745. package/dist/utils/limitHeldPromptQueue.js.map +1 -0
  746. package/dist/utils/limitHeldPrompts.d.ts +58 -0
  747. package/dist/utils/limitHeldPrompts.d.ts.map +1 -0
  748. package/dist/utils/limitHeldPrompts.js +65 -0
  749. package/dist/utils/limitHeldPrompts.js.map +1 -0
  750. package/dist/utils/linkPreviewSuppression.d.ts +18 -0
  751. package/dist/utils/linkPreviewSuppression.d.ts.map +1 -0
  752. package/dist/utils/linkPreviewSuppression.js +37 -0
  753. package/dist/utils/linkPreviewSuppression.js.map +1 -0
  754. package/dist/utils/localClock.d.ts +15 -0
  755. package/dist/utils/localClock.d.ts.map +1 -0
  756. package/dist/utils/localClock.js +29 -0
  757. package/dist/utils/localClock.js.map +1 -0
  758. package/dist/utils/markdownTableRender.d.ts +83 -0
  759. package/dist/utils/markdownTableRender.d.ts.map +1 -0
  760. package/dist/utils/markdownTableRender.js +298 -0
  761. package/dist/utils/markdownTableRender.js.map +1 -0
  762. package/dist/utils/mediaGroupCollector.d.ts +51 -0
  763. package/dist/utils/mediaGroupCollector.d.ts.map +1 -0
  764. package/dist/utils/mediaGroupCollector.js +90 -0
  765. package/dist/utils/mediaGroupCollector.js.map +1 -0
  766. package/dist/utils/messageSendService.d.ts +136 -0
  767. package/dist/utils/messageSendService.d.ts.map +1 -0
  768. package/dist/utils/messageSendService.js +165 -0
  769. package/dist/utils/messageSendService.js.map +1 -0
  770. package/dist/utils/minutesOverride.d.ts +7 -0
  771. package/dist/utils/minutesOverride.d.ts.map +1 -0
  772. package/dist/utils/minutesOverride.js +16 -0
  773. package/dist/utils/minutesOverride.js.map +1 -0
  774. package/dist/utils/modelPickerPlan.d.ts +119 -0
  775. package/dist/utils/modelPickerPlan.d.ts.map +1 -0
  776. package/dist/utils/modelPickerPlan.js +171 -0
  777. package/dist/utils/modelPickerPlan.js.map +1 -0
  778. package/dist/utils/modelSetReplyDecision.d.ts +67 -0
  779. package/dist/utils/modelSetReplyDecision.d.ts.map +1 -0
  780. package/dist/utils/modelSetReplyDecision.js +27 -0
  781. package/dist/utils/modelSetReplyDecision.js.map +1 -0
  782. package/dist/utils/openCodeAuthLogin.d.ts +181 -0
  783. package/dist/utils/openCodeAuthLogin.d.ts.map +1 -0
  784. package/dist/utils/openCodeAuthLogin.js +393 -0
  785. package/dist/utils/openCodeAuthLogin.js.map +1 -0
  786. package/dist/utils/openCodeCompactPlugin.d.ts +81 -0
  787. package/dist/utils/openCodeCompactPlugin.d.ts.map +1 -0
  788. package/dist/utils/openCodeCompactPlugin.js +146 -0
  789. package/dist/utils/openCodeCompactPlugin.js.map +1 -0
  790. package/dist/utils/openCodeTurnActivity.d.ts +67 -0
  791. package/dist/utils/openCodeTurnActivity.d.ts.map +1 -0
  792. package/dist/utils/openCodeTurnActivity.js +27 -0
  793. package/dist/utils/openCodeTurnActivity.js.map +1 -0
  794. package/dist/utils/outputDebounce.d.ts +24 -0
  795. package/dist/utils/outputDebounce.d.ts.map +1 -0
  796. package/dist/utils/outputDebounce.js +30 -0
  797. package/dist/utils/outputDebounce.js.map +1 -0
  798. package/dist/utils/outputFlushTiming.d.ts +54 -0
  799. package/dist/utils/outputFlushTiming.d.ts.map +1 -0
  800. package/dist/utils/outputFlushTiming.js +48 -0
  801. package/dist/utils/outputFlushTiming.js.map +1 -0
  802. package/dist/utils/paginateList.d.ts +25 -0
  803. package/dist/utils/paginateList.d.ts.map +1 -0
  804. package/dist/utils/paginateList.js +32 -0
  805. package/dist/utils/paginateList.js.map +1 -0
  806. package/dist/utils/paneDiff.d.ts +55 -0
  807. package/dist/utils/paneDiff.d.ts.map +1 -0
  808. package/dist/utils/paneDiff.js +88 -0
  809. package/dist/utils/paneDiff.js.map +1 -0
  810. package/dist/utils/paneResizeGuard.d.ts +69 -0
  811. package/dist/utils/paneResizeGuard.d.ts.map +1 -0
  812. package/dist/utils/paneResizeGuard.js +91 -0
  813. package/dist/utils/paneResizeGuard.js.map +1 -0
  814. package/dist/utils/persistAdapterSessionIds.d.ts +39 -0
  815. package/dist/utils/persistAdapterSessionIds.d.ts.map +1 -0
  816. package/dist/utils/persistAdapterSessionIds.js +28 -0
  817. package/dist/utils/persistAdapterSessionIds.js.map +1 -0
  818. package/dist/utils/pinnedBannerSkipDecision.d.ts +44 -0
  819. package/dist/utils/pinnedBannerSkipDecision.d.ts.map +1 -0
  820. package/dist/utils/pinnedBannerSkipDecision.js +20 -0
  821. package/dist/utils/pinnedBannerSkipDecision.js.map +1 -0
  822. package/dist/utils/pollBackoff.d.ts +45 -0
  823. package/dist/utils/pollBackoff.d.ts.map +1 -0
  824. package/dist/utils/pollBackoff.js +44 -0
  825. package/dist/utils/pollBackoff.js.map +1 -0
  826. package/dist/utils/promptDelivery.d.ts +46 -0
  827. package/dist/utils/promptDelivery.d.ts.map +1 -0
  828. package/dist/utils/promptDelivery.js +49 -0
  829. package/dist/utils/promptDelivery.js.map +1 -0
  830. package/dist/utils/providerDisconnectPlan.d.ts +82 -0
  831. package/dist/utils/providerDisconnectPlan.d.ts.map +1 -0
  832. package/dist/utils/providerDisconnectPlan.js +110 -0
  833. package/dist/utils/providerDisconnectPlan.js.map +1 -0
  834. package/dist/utils/rateLimitLog.d.ts +48 -0
  835. package/dist/utils/rateLimitLog.d.ts.map +1 -0
  836. package/dist/utils/rateLimitLog.js +43 -0
  837. package/dist/utils/rateLimitLog.js.map +1 -0
  838. package/dist/utils/recentRelayWindow.d.ts +165 -0
  839. package/dist/utils/recentRelayWindow.d.ts.map +1 -0
  840. package/dist/utils/recentRelayWindow.js +284 -0
  841. package/dist/utils/recentRelayWindow.js.map +1 -0
  842. package/dist/utils/recvPreviewRedaction.d.ts +23 -0
  843. package/dist/utils/recvPreviewRedaction.d.ts.map +1 -0
  844. package/dist/utils/recvPreviewRedaction.js +39 -0
  845. package/dist/utils/recvPreviewRedaction.js.map +1 -0
  846. package/dist/utils/reminderFlow.d.ts +148 -0
  847. package/dist/utils/reminderFlow.d.ts.map +1 -0
  848. package/dist/utils/reminderFlow.js +122 -0
  849. package/dist/utils/reminderFlow.js.map +1 -0
  850. package/dist/utils/reminderScheduleText.d.ts +87 -0
  851. package/dist/utils/reminderScheduleText.d.ts.map +1 -0
  852. package/dist/utils/reminderScheduleText.js +135 -0
  853. package/dist/utils/reminderScheduleText.js.map +1 -0
  854. package/dist/utils/reminderWizard.d.ts +710 -0
  855. package/dist/utils/reminderWizard.d.ts.map +1 -0
  856. package/dist/utils/reminderWizard.js +1164 -0
  857. package/dist/utils/reminderWizard.js.map +1 -0
  858. package/dist/utils/replyQuote.d.ts +111 -0
  859. package/dist/utils/replyQuote.d.ts.map +1 -0
  860. package/dist/utils/replyQuote.js +122 -0
  861. package/dist/utils/replyQuote.js.map +1 -0
  862. package/dist/utils/resolveBinary.d.ts +25 -0
  863. package/dist/utils/resolveBinary.d.ts.map +1 -0
  864. package/dist/utils/resolveBinary.js +99 -0
  865. package/dist/utils/resolveBinary.js.map +1 -0
  866. package/dist/utils/retryAfterHeader.d.ts +7 -0
  867. package/dist/utils/retryAfterHeader.d.ts.map +1 -0
  868. package/dist/utils/retryAfterHeader.js +17 -0
  869. package/dist/utils/retryAfterHeader.js.map +1 -0
  870. package/dist/utils/rotatingJsonlFile.d.ts +40 -0
  871. package/dist/utils/rotatingJsonlFile.d.ts.map +1 -0
  872. package/dist/utils/rotatingJsonlFile.js +106 -0
  873. package/dist/utils/rotatingJsonlFile.js.map +1 -0
  874. package/dist/utils/rotatingLogFile.d.ts +25 -0
  875. package/dist/utils/rotatingLogFile.d.ts.map +1 -0
  876. package/dist/utils/rotatingLogFile.js +74 -0
  877. package/dist/utils/rotatingLogFile.js.map +1 -0
  878. package/dist/utils/sendRateTracker.d.ts +59 -0
  879. package/dist/utils/sendRateTracker.d.ts.map +1 -0
  880. package/dist/utils/sendRateTracker.js +144 -0
  881. package/dist/utils/sendRateTracker.js.map +1 -0
  882. package/dist/utils/serialQueue.d.ts +5 -0
  883. package/dist/utils/serialQueue.d.ts.map +1 -0
  884. package/dist/utils/serialQueue.js +14 -0
  885. package/dist/utils/serialQueue.js.map +1 -0
  886. package/dist/utils/sseStreamLifecycle.d.ts +32 -0
  887. package/dist/utils/sseStreamLifecycle.d.ts.map +1 -0
  888. package/dist/utils/sseStreamLifecycle.js +41 -0
  889. package/dist/utils/sseStreamLifecycle.js.map +1 -0
  890. package/dist/utils/startupReadiness.d.ts +119 -0
  891. package/dist/utils/startupReadiness.d.ts.map +1 -0
  892. package/dist/utils/startupReadiness.js +148 -0
  893. package/dist/utils/startupReadiness.js.map +1 -0
  894. package/dist/utils/statusFlushDecision.d.ts +18 -0
  895. package/dist/utils/statusFlushDecision.d.ts.map +1 -0
  896. package/dist/utils/statusFlushDecision.js +53 -0
  897. package/dist/utils/statusFlushDecision.js.map +1 -0
  898. package/dist/utils/subagentRender.d.ts +52 -0
  899. package/dist/utils/subagentRender.d.ts.map +1 -0
  900. package/dist/utils/subagentRender.js +66 -0
  901. package/dist/utils/subagentRender.js.map +1 -0
  902. package/dist/utils/subagentStatusRender.d.ts +90 -0
  903. package/dist/utils/subagentStatusRender.d.ts.map +1 -0
  904. package/dist/utils/subagentStatusRender.js +124 -0
  905. package/dist/utils/subagentStatusRender.js.map +1 -0
  906. package/dist/utils/telegramError.d.ts +2 -0
  907. package/dist/utils/telegramError.d.ts.map +1 -0
  908. package/dist/utils/telegramError.js +16 -0
  909. package/dist/utils/telegramError.js.map +1 -0
  910. package/dist/utils/terminalEmitPlan.d.ts +74 -0
  911. package/dist/utils/terminalEmitPlan.d.ts.map +1 -0
  912. package/dist/utils/terminalEmitPlan.js +86 -0
  913. package/dist/utils/terminalEmitPlan.js.map +1 -0
  914. package/dist/utils/thinkingRender.d.ts +74 -0
  915. package/dist/utils/thinkingRender.d.ts.map +1 -0
  916. package/dist/utils/thinkingRender.js +85 -0
  917. package/dist/utils/thinkingRender.js.map +1 -0
  918. package/dist/utils/threadStatusReport.d.ts +55 -0
  919. package/dist/utils/threadStatusReport.d.ts.map +1 -0
  920. package/dist/utils/threadStatusReport.js +65 -0
  921. package/dist/utils/threadStatusReport.js.map +1 -0
  922. package/dist/utils/threadToggle.d.ts +29 -0
  923. package/dist/utils/threadToggle.d.ts.map +1 -0
  924. package/dist/utils/threadToggle.js +39 -0
  925. package/dist/utils/threadToggle.js.map +1 -0
  926. package/dist/utils/timezone.d.ts +125 -0
  927. package/dist/utils/timezone.d.ts.map +1 -0
  928. package/dist/utils/timezone.js +278 -0
  929. package/dist/utils/timezone.js.map +1 -0
  930. package/dist/utils/tmuxExec.d.ts +61 -0
  931. package/dist/utils/tmuxExec.d.ts.map +1 -0
  932. package/dist/utils/tmuxExec.js +109 -0
  933. package/dist/utils/tmuxExec.js.map +1 -0
  934. package/dist/utils/tmuxSessionName.d.ts +34 -0
  935. package/dist/utils/tmuxSessionName.d.ts.map +1 -0
  936. package/dist/utils/tmuxSessionName.js +51 -0
  937. package/dist/utils/tmuxSessionName.js.map +1 -0
  938. package/dist/utils/toolResultRender.d.ts +61 -0
  939. package/dist/utils/toolResultRender.d.ts.map +1 -0
  940. package/dist/utils/toolResultRender.js +62 -0
  941. package/dist/utils/toolResultRender.js.map +1 -0
  942. package/dist/utils/topicRequest.d.ts +61 -0
  943. package/dist/utils/topicRequest.d.ts.map +1 -0
  944. package/dist/utils/topicRequest.js +68 -0
  945. package/dist/utils/topicRequest.js.map +1 -0
  946. package/dist/utils/topicView.d.ts +55 -0
  947. package/dist/utils/topicView.d.ts.map +1 -0
  948. package/dist/utils/topicView.js +56 -0
  949. package/dist/utils/topicView.js.map +1 -0
  950. package/dist/utils/transcribeAudio.d.ts +89 -0
  951. package/dist/utils/transcribeAudio.d.ts.map +1 -0
  952. package/dist/utils/transcribeAudio.js +249 -0
  953. package/dist/utils/transcribeAudio.js.map +1 -0
  954. package/dist/utils/transientFrames.d.ts +52 -0
  955. package/dist/utils/transientFrames.d.ts.map +1 -0
  956. package/dist/utils/transientFrames.js +41 -0
  957. package/dist/utils/transientFrames.js.map +1 -0
  958. package/dist/utils/typingActive.d.ts +35 -0
  959. package/dist/utils/typingActive.d.ts.map +1 -0
  960. package/dist/utils/typingActive.js +22 -0
  961. package/dist/utils/typingActive.js.map +1 -0
  962. package/dist/utils/typingLoopBackstop.d.ts +64 -0
  963. package/dist/utils/typingLoopBackstop.d.ts.map +1 -0
  964. package/dist/utils/typingLoopBackstop.js +68 -0
  965. package/dist/utils/typingLoopBackstop.js.map +1 -0
  966. package/dist/utils/verbosityRender.d.ts +18 -0
  967. package/dist/utils/verbosityRender.d.ts.map +1 -0
  968. package/dist/utils/verbosityRender.js +17 -0
  969. package/dist/utils/verbosityRender.js.map +1 -0
  970. package/dist/utils/wedgeRecovery.d.ts +57 -0
  971. package/dist/utils/wedgeRecovery.d.ts.map +1 -0
  972. package/dist/utils/wedgeRecovery.js +24 -0
  973. package/dist/utils/wedgeRecovery.js.map +1 -0
  974. package/dist/utils.d.ts +30 -0
  975. package/dist/utils.d.ts.map +1 -0
  976. package/dist/utils.js +61 -0
  977. package/dist/utils.js.map +1 -0
  978. package/dist/validation.d.ts +113 -0
  979. package/dist/validation.d.ts.map +1 -0
  980. package/dist/validation.js +226 -0
  981. package/dist/validation.js.map +1 -0
  982. package/dist/voiceQueue.d.ts +7 -0
  983. package/dist/voiceQueue.d.ts.map +1 -0
  984. package/dist/voiceQueue.js +33 -0
  985. package/dist/voiceQueue.js.map +1 -0
  986. package/examples/.env.example +17 -0
  987. package/examples/docker-compose.yml +36 -0
  988. package/package.json +83 -4
@@ -0,0 +1,1810 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ClaudeJsonStreamAdapter = exports.claudeJsonStreamUsageLogPrefix = void 0;
37
+ exports.readLatestCompactSummaryFromTranscript = readLatestCompactSummaryFromTranscript;
38
+ const crypto_1 = require("crypto");
39
+ const events_1 = require("events");
40
+ const fs = __importStar(require("fs"));
41
+ const sessionKey_1 = require("../sessionKey");
42
+ const apiErrorRetry_1 = require("../apiErrorRetry");
43
+ const installManager_1 = require("../installManager");
44
+ const mcpConfig_1 = require("../mcpConfig");
45
+ const claudeCompactHook_1 = require("../utils/claudeCompactHook");
46
+ const claudePlatformFlags_1 = require("./claudePlatformFlags");
47
+ const state_1 = require("../state");
48
+ const resolveBinary_1 = require("../utils/resolveBinary");
49
+ const displayVerbosity_1 = require("../utils/displayVerbosity");
50
+ const i18n_1 = require("../i18n");
51
+ const resumeContext_1 = require("../resumeContext");
52
+ const effortLevels_1 = require("../effortLevels");
53
+ const claudeCliAdapter_1 = require("./claudeCliAdapter");
54
+ const claudeStreamJson_1 = require("../utils/claudeStreamJson");
55
+ const claudeMcpHeal_1 = require("../utils/claudeMcpHeal");
56
+ const tmuxExec_1 = require("../utils/tmuxExec");
57
+ const pollBackoff_1 = require("../utils/pollBackoff");
58
+ const claudeRuntimeInfo_1 = require("../utils/claudeRuntimeInfo");
59
+ const jsonStreamBusyWatchdog_1 = require("../utils/jsonStreamBusyWatchdog");
60
+ const jsonStreamHost_1 = require("../utils/jsonStreamHost");
61
+ const adapterNames_1 = require("./adapterNames");
62
+ const claudeCodeVersion_1 = require("../utils/claudeCodeVersion");
63
+ const sessionLaunchDefaults_1 = require("./sessionLaunchDefaults");
64
+ /**
65
+ * In-flight explicit stops, per key — a second stop (or a start's implicit stop)
66
+ * AWAITS the first instead of racing it: the delayed first `tmux kill-session`
67
+ * could otherwise land AFTER a fresh same-name spawn and kill the new session.
68
+ * MODULE-level, shared by the idle and the per-turn adapter instances: both host
69
+ * a conversation under the SAME tmux name and dir, and a `/claude_mode` switch
70
+ * stops the process on one instance while the other spawns the next.
71
+ */
72
+ const stopsInFlightByKey = new Map();
73
+ /**
74
+ * @description Coalesce window for streamed answer/thinking deltas before an
75
+ * `output`/`thinking` emit — the CLI streams per-token, so raw per-delta emits
76
+ * would flood Telegram with edits. Mirrors OpenCode's `sseOutputBatchMs` (500).
77
+ */
78
+ const streamOutputBatchMs = 350;
79
+ /** Prefix of the per-turn token accounting line (L-D11); the process-level e2e
80
+ * and the live cache check read it. */
81
+ exports.claudeJsonStreamUsageLogPrefix = '[ClaudeJson] usage ';
82
+ /** How long to wait for the `initialize` control-response handshake before
83
+ * proceeding without the interactive control channel (questions unavailable). */
84
+ const initializeHandshakeTimeoutMs = 15000;
85
+ /**
86
+ * @description SECOND Claude backend: drives the `claude` CLI over its
87
+ * documented `--input-format stream-json --output-format stream-json` protocol
88
+ * (typed events, no TUI scrape) as an EXTERNAL tmux-hosted process per
89
+ * {@link SessionKey} (plan 2026-07-05-jsonstream-restart-isolation): a `#!/bin/sh`
90
+ * wrapper backgrounds claude with stdin on a FIFO the process itself holds
91
+ * `0<>` and stdout appended to a `stdout.jsonl` the adapter TAILS — so a bot
92
+ * restart (every hot reload) neither EOFs claude's stdin nor loses its output;
93
+ * the restarted bot re-adopts the tmux session and replays the missed tail.
94
+ * Same structured events, on the SAME subscription billing (proven
95
+ * `apiKeySource:"none"` + `seven_day` rate-limit event; the wrapper runs
96
+ * `env -u ANTHROPIC_API_KEY`, never `--bare`).
97
+ *
98
+ * Structural template is {@link import('./openCodeAdapter').OpenCodeAdapter}
99
+ * (event-driven, own process, per-key session map); the Claude on-disk
100
+ * transcript readers are reused for `/sessions` / resume / recap.
101
+ *
102
+ * Interactive questions (`AskUserQuestion`) reach Telegram through the same
103
+ * `question` event + pin flow as OpenCode: the CLI surfaces them as a
104
+ * `control_request` (`subtype:"can_use_tool"`, `tool_name:"AskUserQuestion"`)
105
+ * on stdout, answered with a `control_response` carrying
106
+ * `updatedInput.answers` on stdin — a wire protocol reverse-engineered from a
107
+ * live capture + the open-source `@anthropic-ai/claude-agent-sdk`. Ordinary
108
+ * tool permissions are auto-allowed (operator trusts their own agent — parity
109
+ * with the tmux backend's `bypassPermissions`), so only questions block.
110
+ *
111
+ * A topic flips between it and the tmux-scrape backend via `/claude_mode`
112
+ * (they share the on-disk transcript, so the switch resumes the same
113
+ * conversation). The DEFAULT Claude backend (`getDefaultClaudeBackendName`):
114
+ * `/login` is handled out-of-band by the bot (`claude auth login` in a pty),
115
+ * so it no longer needs a TUI to sign in.
116
+ */
117
+ class ClaudeJsonStreamAdapter extends events_1.EventEmitter {
118
+ name;
119
+ label;
120
+ /**
121
+ * `idle` — the process stays between turns and is stopped at the idle mark (L3);
122
+ * `perTurn` — the process is stopped right after each turn (L5, L-D3), by the
123
+ * bot on the `turnEnded` event. Same host, same session layout: one process per
124
+ * conversation at a time whichever lifecycle runs it.
125
+ */
126
+ lifecycle;
127
+ constructor(options = { lifecycle: 'idle' }) {
128
+ super();
129
+ this.lifecycle = options.lifecycle;
130
+ this.name = options.lifecycle === 'perTurn' ? adapterNames_1.claudePerTurnAdapterName : adapterNames_1.claudeJsonStreamAdapterName;
131
+ this.label = options.lifecycle === 'perTurn' ? 'Claude Code (per-turn)' : 'Claude Code (stream)';
132
+ }
133
+ /** Emits incremental text deltas (like the tmux scrape) → the transports
134
+ * synthesise continuation (`getDmDraftContinuation` / group delta path). */
135
+ outputsDeltas = true;
136
+ /** No TUI banner → keep the bot's `agent.ready` cue + one-shot boot ping (L2). */
137
+ selfGreetsOnStart = false;
138
+ sessions = new Map();
139
+ claudePath = (0, resolveBinary_1.resolveClaudeBinary)();
140
+ displayPrefsReader = null;
141
+ seenWatermarkWriter = null;
142
+ jsonStreamTailWriter = null;
143
+ sessionLaunchDefaultsReader = null;
144
+ /** The stops in flight — the module-level map shared by both lifecycles' instances (see {@link stopsInFlightByKey}). */
145
+ stopsInFlight = stopsInFlightByKey;
146
+ setDisplayPrefsReader(reader) {
147
+ this.displayPrefsReader = reader;
148
+ }
149
+ setSeenWatermarkWriter(writer) {
150
+ this.seenWatermarkWriter = writer;
151
+ }
152
+ setJsonStreamTailWriter(writer) {
153
+ this.jsonStreamTailWriter = writer;
154
+ }
155
+ setSessionLaunchDefaultsReader(reader) {
156
+ this.sessionLaunchDefaultsReader = reader;
157
+ }
158
+ getLaunchOptions(key) {
159
+ return (0, sessionLaunchDefaults_1.getSessionLaunchOptions)({
160
+ savedEffort: this.getEffort(key),
161
+ defaults: this.sessionLaunchDefaultsReader?.(key) ?? null,
162
+ botDefaultEffort: effortLevels_1.defaultEffortLevel,
163
+ });
164
+ }
165
+ getDisplayPrefs(key) {
166
+ return this.displayPrefsReader?.(key) ?? (0, displayVerbosity_1.getDefaultDisplayPrefs)();
167
+ }
168
+ advanceSeenWatermark(key, watermark) {
169
+ this.seenWatermarkWriter?.(key, watermark);
170
+ }
171
+ // ─────────────────────────────────────────────────────────────────────────
172
+ // Lifecycle (S1)
173
+ // ─────────────────────────────────────────────────────────────────────────
174
+ async startSession(key, workDir, _args, sessionId) {
175
+ await this.stopSessionInternal(key);
176
+ const id = sessionId && (0, claudeCliAdapter_1.checkIsValidUuid)(sessionId) ? sessionId : (0, crypto_1.randomUUID)();
177
+ await this.spawnSession(key, workDir, id, { ...this.getLaunchOptions(key), resume: false });
178
+ }
179
+ async resumeSession(key, workDir, sessionId, options) {
180
+ await this.stopSessionInternal(key);
181
+ if (!(0, claudeCliAdapter_1.checkIsValidUuid)(sessionId))
182
+ throw new Error(`Invalid sessionId: ${sessionId}`);
183
+ await this.spawnSession(key, workDir, sessionId, { ...this.getLaunchOptions(key), resume: true });
184
+ // Post the short last-N-turn context block ONLY on the explicit user resume
185
+ // (`/sessions` pick) — a silent re-attach stays quiet (ResumeSessionOptions).
186
+ if (options?.isWithRecentContext) {
187
+ try {
188
+ const turns = await this.getRecentTurns(key, workDir, sessionId, resumeContext_1.resumeContextTurnLimit);
189
+ const rendered = (0, resumeContext_1.formatResumeContext)(turns);
190
+ if (rendered)
191
+ this.emit('output', key, rendered, { isComplete: true });
192
+ }
193
+ catch (e) {
194
+ console.warn(`[ClaudeJson] resume context block failed:`, e instanceof Error ? e.message : e);
195
+ }
196
+ }
197
+ }
198
+ /**
199
+ * @description Spawn (or re-spawn) the CLI process for a thread as an
200
+ * EXTERNAL tmux-hosted process (wrapper + FIFO stdin + stdout file — the
201
+ * probe-proven layout in `utils/jsonStreamHost.ts`), start the stdout tail
202
+ * poller, and complete the `initialize` control handshake (which enables the
203
+ * `AskUserQuestion` control channel). Rejects — with the wrapper's exit code
204
+ * and a stderr excerpt — if the process fails to come up. Reused by start /
205
+ * resume / effort-respawn.
206
+ */
207
+ async spawnSession(key, workDir, sessionId, opts) {
208
+ if (!(0, installManager_1.checkIsInstalled)('claude')) {
209
+ this.emit('output', key, 'Installing Claude Code...');
210
+ await (0, installManager_1.installTool)('claude');
211
+ }
212
+ const mcpFlags = await (0, mcpConfig_1.prepareMcpFlags)({ key, dataDir: (0, state_1.resolveDataDir)() });
213
+ const args = [
214
+ '-p',
215
+ // Followed by an option on purpose: `--disallowedTools` takes several values.
216
+ ...(0, claudePlatformFlags_1.getClaudePlatformFlags)(key),
217
+ '--input-format', 'stream-json',
218
+ '--output-format', 'stream-json',
219
+ '--include-partial-messages',
220
+ '--verbose',
221
+ '--replay-user-messages',
222
+ // Bypass ALL permission checks (tmux-backend parity) so the operator's own
223
+ // agent never stalls on a permission prompt. `--dangerously-skip-permissions`
224
+ // + `bypassPermissions` means regular tools (Bash/Read/…) never route through
225
+ // the stdio prompt tool at all. `--permission-prompt-tool stdio` STAYS: it is
226
+ // what keeps `AskUserQuestion` in the tool list AND still routes it through the
227
+ // control channel even under bypass (verified live on v2.1.201 — bypass does
228
+ // NOT suppress AskUserQuestion, and `apiKeySource` stays `none`). The residual
229
+ // circuit-breakers claude never bypasses (`rm -rf /`, `rm -rf ~`) still arrive
230
+ // as `can_use_tool` and are answered by the generic auto-allow below.
231
+ '--dangerously-skip-permissions',
232
+ '--permission-mode', 'bypassPermissions',
233
+ '--permission-prompt-tool', 'stdio',
234
+ ];
235
+ if (opts.resume)
236
+ args.push('--resume', sessionId);
237
+ else
238
+ args.push('--session-id', sessionId);
239
+ if (opts.model)
240
+ args.push('--model', opts.model);
241
+ if (opts.effort)
242
+ args.push('--effort', opts.effort);
243
+ args.push(...mcpFlags);
244
+ // Overflow-triggered compaction gets the bot's summary guidance (see the module).
245
+ args.push(...(0, claudeCompactHook_1.prepareClaudeCompactHookFlags)((0, state_1.resolveDataDir)()));
246
+ // Subscription billing: the wrapper runs `env -u ANTHROPIC_API_KEY` (a set
247
+ // key would meter the API); a tracker conversation's wrapper starts claude
248
+ // with `env -i` and the agent allowlist instead (R32) — no key either. The CLI
249
+ // reads the OAuth login from ~/.claude — `apiKeySource:"none"` (probe-verified
250
+ // under the wrapper).
251
+ const sessionDir = (0, jsonStreamHost_1.resolveJsonStreamSessionDir)((0, state_1.resolveDataDir)(), key);
252
+ const paths = (0, jsonStreamHost_1.getJsonStreamSessionPaths)(sessionDir);
253
+ const tmuxName = (0, jsonStreamHost_1.buildJsonStreamTmuxSessionName)(key);
254
+ console.log(`[ClaudeJson] spawn ${(0, sessionKey_1.keyToString)(key)} session=${sessionId} resume=${opts.resume} effort=${opts.effort} model=${opts.model ?? 'default'} (tmux ${tmuxName})`);
255
+ let pid;
256
+ let fifoFd;
257
+ try {
258
+ // Fresh host layout — a stale dir/fifo from a crashed run must not be reused.
259
+ fs.rmSync(sessionDir, { recursive: true, force: true });
260
+ fs.mkdirSync(sessionDir, { recursive: true, mode: 0o700 });
261
+ await (0, tmuxExec_1.execFilePromise)('mkfifo', [paths.stdinFifo]);
262
+ fs.writeFileSync(paths.wrapperFile, (0, jsonStreamHost_1.buildWrapperScript)(this.claudePath, args, workDir, paths, (0, claudePlatformFlags_1.getClaudePlatformEnvironment)(key)), { mode: 0o755 });
263
+ // A stale same-name tmux session (crashed bot / pre-adopt build) would make
264
+ // `new-session` fail — and must not keep running unowned next to ours.
265
+ await (0, tmuxExec_1.tmuxAsync)('kill-session', '-t', (0, tmuxExec_1.getTmuxSessionTarget)(tmuxName));
266
+ await (0, tmuxExec_1.tmuxOrThrowAsync)('new-session', '-d', '-s', tmuxName, paths.wrapperFile);
267
+ const pidRead = await (0, jsonStreamHost_1.waitForPidFile)(paths.pidFile);
268
+ if (pidRead === null)
269
+ throw new Error(`claude never wrote its pid file${describeSpawnFailure(paths)}`);
270
+ pid = pidRead;
271
+ const fdRead = await (0, jsonStreamHost_1.openFifoWriterNonBlocking)(paths.stdinFifo);
272
+ if (fdRead === null)
273
+ throw new Error(`claude is not holding its stdin FIFO${describeSpawnFailure(paths)}`);
274
+ fifoFd = fdRead;
275
+ }
276
+ catch (e) {
277
+ (0, mcpConfig_1.cleanupMcpTempFiles)({ key, dataDir: (0, state_1.resolveDataDir)() });
278
+ await (0, tmuxExec_1.tmuxAsync)('kill-session', '-t', (0, tmuxExec_1.getTmuxSessionTarget)(tmuxName));
279
+ try {
280
+ fs.rmSync(sessionDir, { recursive: true, force: true });
281
+ }
282
+ catch { /* best-effort */ }
283
+ throw new Error(`Failed to start Claude stream session: ${e instanceof Error ? e.message : String(e)}`);
284
+ }
285
+ const session = {
286
+ key, workDir, sessionId,
287
+ pid, paths, fifoFd,
288
+ stdinWriteChain: Promise.resolve(),
289
+ tail: (0, jsonStreamHost_1.createStdoutTailState)(0),
290
+ pollTimer: null, pollDelayMs: pollBackoff_1.basePollIntervalMs, unchangedStreak: 0,
291
+ isOversizeWarned: false, lastPersistedTailOffset: 0,
292
+ reader: new claudeStreamJson_1.ClaudeStreamLineReader(),
293
+ isActive: true, isStopping: false, isSuspending: false, isRespawning: false, isBusy: false, unconsumedInputCount: 0,
294
+ lastStdoutActivityAt: Date.now(), outstandingToolUseIds: new Set(),
295
+ model: opts.model, reportedModel: null, effort: opts.effort,
296
+ currentResponseText: '', emittedLength: 0, outputTimer: null,
297
+ reasoningText: '', reasoningStartedAt: null, reasoningTimer: null, reasoningActive: false,
298
+ toolNamesById: new Map(), questionToolUseIds: new Set(),
299
+ subagentActive: false, childResponseText: '', childEmittedLength: 0, childOutputTimer: null,
300
+ pendingInitResolve: null, initRequestId: null, pendingControlRequests: new Map(),
301
+ compactionInProgress: false, pendingCompaction: null,
302
+ pendingQuestion: null, apiErrorFired: false, swallowNextAbortError: false,
303
+ lastWatermarkOffset: -1,
304
+ backgroundTaskIds: new Set(), claudeCodeVersion: null, applyingChunk: null,
305
+ adoptCatchUpOffset: null, adoptCatchUpResolvers: [],
306
+ };
307
+ this.sessions.set((0, sessionKey_1.keyToString)(key), session);
308
+ // A fresh spawn starts a fresh stdout file — reset the persisted tail offset
309
+ // so a later adopt never resumes from a previous run's position.
310
+ this.jsonStreamTailWriter?.(key, buildTailRecord(session, 0));
311
+ this.schedulePoll(session, pollBackoff_1.basePollIntervalMs);
312
+ // Initialize handshake: declares the interactive control channel (permission
313
+ // prompts + AskUserQuestion route to stdio). Without it AskUserQuestion is not
314
+ // even in the tool list (verified). Best-effort — proceed on timeout so a
315
+ // handshake hiccup never blocks a plain-output session. The response arrives
316
+ // through the tail poller started above.
317
+ await this.performInitialize(session);
318
+ this.emit('started', key);
319
+ }
320
+ // ─────────────────────────────────────────────────────────────────────────
321
+ // External-process transport: stdout tail poll + exit detection
322
+ // ─────────────────────────────────────────────────────────────────────────
323
+ /** Arm the next tail poll (single-flight `setTimeout` chain, mirroring the
324
+ * scrape backend's `schedulePoll` — never overlapping ticks). */
325
+ schedulePoll(session, delayMs) {
326
+ if (!session.isActive)
327
+ return;
328
+ if (session.pollTimer)
329
+ clearTimeout(session.pollTimer);
330
+ session.pollDelayMs = delayMs;
331
+ session.pollTimer = setTimeout(() => {
332
+ session.pollTimer = null;
333
+ this.pollTailTick(session);
334
+ }, delayMs);
335
+ }
336
+ /** Snap the tail cadence back to base — called on every stdin write, when
337
+ * fresh output is expected shortly (mirrors the scrape backend's write snap). */
338
+ snapPollToBase(session) {
339
+ if (!session.isActive)
340
+ return;
341
+ session.unchangedStreak = 0;
342
+ this.schedulePoll(session, pollBackoff_1.basePollIntervalMs);
343
+ }
344
+ pollTailTick(session) {
345
+ if (!session.isActive || this.sessions.get((0, sessionKey_1.keyToString)(session.key)) !== session)
346
+ return;
347
+ let isChanged = false;
348
+ try {
349
+ isChanged = this.drainStdoutTail(session);
350
+ }
351
+ catch (e) {
352
+ console.warn(`[ClaudeJson] stdout tail read failed for ${(0, sessionKey_1.keyToString)(session.key)}:`, e instanceof Error ? e.message : e);
353
+ }
354
+ // Exit detection (locked decision): the wrapper writes `exitcode` when
355
+ // claude exits; pid-alive covers a hard-killed wrapper that never wrote it.
356
+ const exitCode = (0, jsonStreamHost_1.readExitCodeFile)(session.paths.exitCodeFile);
357
+ if (exitCode !== null || !(0, jsonStreamHost_1.checkIsPidAlive)(session.pid)) {
358
+ // Final drain — bytes flushed at process exit may postdate the read above.
359
+ try {
360
+ this.drainStdoutTail(session);
361
+ }
362
+ catch { /* already reported above */ }
363
+ this.finalizeExternalExit(session, exitCode ?? (0, jsonStreamHost_1.readExitCodeFile)(session.paths.exitCodeFile));
364
+ return;
365
+ }
366
+ this.settleAdoptCatchUp(session, false);
367
+ // Backstop: a busy session gone silent with nothing in flight lost its
368
+ // terminal `result` — clear the stuck flag so the typing indicator can't hang.
369
+ this.maybeClearBusyOnIdle(session);
370
+ const next = (0, pollBackoff_1.getNextPollDelay)({ isChanged, currentDelayMs: session.pollDelayMs, unchangedStreak: session.unchangedStreak });
371
+ session.unchangedStreak = next.unchangedStreak;
372
+ this.schedulePoll(session, next.delayMs);
373
+ }
374
+ /**
375
+ * @description Idle watchdog (bounded safety net). `isBusy` is cleared in
376
+ * exactly one place — a processed terminal `result` (`handleTurnEnd`) — so a
377
+ * single missed `result` (an aborted `interrupt`, a process gone quiet after
378
+ * the answer, a future CLI dropping the line) hangs the native typing
379
+ * indicator indefinitely. When the session is busy but stdout has been silent
380
+ * past {@link busyIdleWatchdogMs} and nothing is genuinely in flight, the turn
381
+ * has really ended: clear the flag. The pure decision + its rationale live in
382
+ * `utils/jsonStreamBusyWatchdog` — every in-flight signal (tool / sub-agent /
383
+ * question / batched answer) vetoes the clear, so a legitimately long-running
384
+ * turn is never truncated (a working agent never goes silent with nothing in
385
+ * flight — it streams deltas or waits on a tool/sub-agent/user).
386
+ */
387
+ maybeClearBusyOnIdle(session) {
388
+ const shouldClear = (0, jsonStreamBusyWatchdog_1.checkShouldClearBusyOnIdle)({
389
+ isBusy: session.isBusy,
390
+ msSinceStdoutActivity: Date.now() - session.lastStdoutActivityAt,
391
+ idleTimeoutMs: jsonStreamBusyWatchdog_1.busyIdleWatchdogMs,
392
+ outstandingToolCount: session.outstandingToolUseIds.size,
393
+ subagentActive: session.subagentActive,
394
+ hasPendingQuestion: session.pendingQuestion !== null,
395
+ hasUnflushedAnswer: session.currentResponseText.length !== session.emittedLength,
396
+ });
397
+ if (!shouldClear)
398
+ return;
399
+ console.warn(`[ClaudeJson] busy-idle watchdog: ${(0, sessionKey_1.keyToString)(session.key)} idle ${jsonStreamBusyWatchdog_1.busyIdleWatchdogMs}ms with nothing in flight — clearing stuck busy (missed terminal result?)`);
400
+ session.isBusy = false;
401
+ this.finishReasoning(session);
402
+ // The record must not keep saying "turn in flight": an adopt would restore the cleared flag.
403
+ this.jsonStreamTailWriter?.(session.key, buildTailRecord(session, session.lastPersistedTailOffset));
404
+ }
405
+ /**
406
+ * @description Read whatever `stdout.jsonl` grew since the last poll and feed
407
+ * it through the SAME decode → line-reader → classifier pipeline the stdio
408
+ * pipe used to feed. Returns whether anything new was consumed (drives the
409
+ * adaptive poll backoff). After consuming, persists the line-boundary tail
410
+ * offset so a bot restart resumes exactly where processing stopped.
411
+ */
412
+ drainStdoutTail(session) {
413
+ const size = (0, jsonStreamHost_1.getFileSize)(session.paths.stdoutFile);
414
+ if (size === null)
415
+ return false; // not created yet (claude still booting)
416
+ if (size > jsonStreamHost_1.stdoutOversizeWarnBytes && !session.isOversizeWarned) {
417
+ session.isOversizeWarned = true;
418
+ console.warn(`[ClaudeJson] stdout log for ${(0, sessionKey_1.keyToString)(session.key)} exceeds ${jsonStreamHost_1.stdoutOversizeWarnBytes} bytes (no rotation in v1)`);
419
+ }
420
+ const decision = (0, jsonStreamHost_1.getStdoutTailDecision)(session.tail, size);
421
+ if (decision.kind === 'reseed') {
422
+ // External truncation — the retained partial line died with the old file.
423
+ session.reader = new claudeStreamJson_1.ClaudeStreamLineReader();
424
+ return false;
425
+ }
426
+ if (decision.kind === 'none')
427
+ return false;
428
+ const chunk = (0, jsonStreamHost_1.readFileByteRange)(session.paths.stdoutFile, decision.startOffset, decision.endOffset);
429
+ if (chunk.length === 0)
430
+ return false;
431
+ const text = (0, jsonStreamHost_1.decodeStdoutTailChunk)(session.tail, chunk);
432
+ if (text) {
433
+ // Any new stdout — even a `tool_progress` frame the classifier ignores —
434
+ // means the process is alive and working; feed the idle watchdog's clock.
435
+ session.lastStdoutActivityAt = Date.now();
436
+ this.onStdout(session, text);
437
+ }
438
+ // Persist the consumption boundary ONLY when nothing sits in the answer /
439
+ // child batchers: the offset means "everything before here has LEFT the
440
+ // adapter", so text still waiting in a 350ms batch must hold it back —
441
+ // otherwise a reload in that window skips the batched text on replay
442
+ // (live seam-loss 2026-07-05: lines 216–221 were consumed
443
+ // into the batch, the offset moved past them, and the kill dropped them).
444
+ // A deferred persist happens in `flushAnswer` / the child flush, right
445
+ // after the batched text is emitted.
446
+ if (checkIsEmitCaughtUp(session))
447
+ this.persistTailOffset(session);
448
+ return true;
449
+ }
450
+ /** Persist the tail's line-boundary offset (monotonic; inert until the
451
+ * writer is registered — same idiom as the seen-watermark writer). */
452
+ persistTailOffset(session) {
453
+ if (session.applyingChunk) {
454
+ session.applyingChunk.isPersistDeferred = true;
455
+ return;
456
+ }
457
+ const boundary = (0, jsonStreamHost_1.getStdoutLineBoundaryOffset)(session.tail, session.reader.pending);
458
+ if (boundary <= session.lastPersistedTailOffset)
459
+ return;
460
+ session.lastPersistedTailOffset = boundary;
461
+ this.jsonStreamTailWriter?.(session.key, buildTailRecord(session, boundary));
462
+ }
463
+ /**
464
+ * @description Single teardown convergence point for the external process —
465
+ * both the explicit stop (`isStopping` → `stopped`) and the poll tick's exit
466
+ * detection (→ `closed`, with the wrapper-reported exit code and a stderr
467
+ * excerpt). Kills any tmux leftovers, removes the host dir, and cleans the
468
+ * MCP temp files. A respawn owns its own transition (`isRespawning`).
469
+ */
470
+ finalizeExternalExit(session, exitCode) {
471
+ if (session.isRespawning)
472
+ return; // an effort/model re-spawn owns the transition
473
+ const key = session.key;
474
+ // Only act if this session is still the current one for the key.
475
+ if (this.sessions.get((0, sessionKey_1.keyToString)(key)) !== session)
476
+ return;
477
+ this.clearTimers(session);
478
+ session.isActive = false;
479
+ this.sessions.delete((0, sessionKey_1.keyToString)(key));
480
+ this.settleAdoptCatchUp(session, true);
481
+ this.closeFifo(session);
482
+ const stderrTail = session.isStopping ? '' : (0, jsonStreamHost_1.readStderrTail)(session.paths.stderrFile);
483
+ void (0, tmuxExec_1.tmuxAsync)('kill-session', '-t', (0, tmuxExec_1.getTmuxSessionTarget)((0, jsonStreamHost_1.buildJsonStreamTmuxSessionName)(key)));
484
+ try {
485
+ fs.rmSync(session.paths.dir, { recursive: true, force: true });
486
+ }
487
+ catch { /* best-effort */ }
488
+ (0, mcpConfig_1.cleanupMcpTempFiles)({ key, dataDir: (0, state_1.resolveDataDir)() });
489
+ if (session.isSuspending) {
490
+ // The session id stays persisted: the next trigger resumes the conversation (L3).
491
+ console.log(`[ClaudeJson] session ${(0, sessionKey_1.keyToString)(key)} suspended (code=${exitCode})`);
492
+ this.emit('suspended', key);
493
+ }
494
+ else if (session.isStopping) {
495
+ console.log(`[ClaudeJson] session ${(0, sessionKey_1.keyToString)(key)} stopped (code=${exitCode})`);
496
+ this.emit('stopped', key);
497
+ }
498
+ else {
499
+ console.warn(`[ClaudeJson] session ${(0, sessionKey_1.keyToString)(key)} exited unexpectedly (code=${exitCode})${stderrTail ? ` stderr: ${stderrTail}` : ''}`);
500
+ this.emit('closed', key);
501
+ }
502
+ }
503
+ /** Close the FIFO fd AFTER any queued writes settle, so an in-flight write
504
+ * never hits a closed fd (EBADF noise on every stop). */
505
+ closeFifo(session) {
506
+ const fd = session.fifoFd;
507
+ session.stdinWriteChain = session.stdinWriteChain
508
+ .catch(() => { })
509
+ .then(() => { try {
510
+ fs.closeSync(fd);
511
+ }
512
+ catch { /* already closed */ } });
513
+ }
514
+ // ─────────────────────────────────────────────────────────────────────────
515
+ // Reattach at boot (S3) — adopt the external process, resume the tail
516
+ // ─────────────────────────────────────────────────────────────────────────
517
+ /** Scan tmux for `cjson-…` sessions that outlived the bot (mirror of the
518
+ * scrape backend's scan). The bot decides adopt-vs-orphan per key — this
519
+ * method only lists. */
520
+ async listExistingTmuxSessions() {
521
+ const raw = await (0, tmuxExec_1.tmuxAsync)('list-sessions', '-F', '#{session_name}');
522
+ if (!raw)
523
+ return [];
524
+ const result = [];
525
+ for (const name of raw.split('\n').map((s) => s.trim()).filter(Boolean)) {
526
+ const key = (0, jsonStreamHost_1.parseJsonStreamTmuxSessionName)(name);
527
+ if (key)
528
+ result.push({ key, sessionName: name });
529
+ }
530
+ return result;
531
+ }
532
+ /** Kill an orphan `cjson-…` tmux session AND its host dir (the dir is
533
+ * per-thread, derived from the parsed name; an unparseable name only
534
+ * kills the tmux session). */
535
+ async killOrphanTmuxSession(sessionName) {
536
+ console.log(`[ClaudeJson] kill orphan tmux session: ${sessionName}`);
537
+ await (0, tmuxExec_1.tmuxAsync)('kill-session', '-t', (0, tmuxExec_1.getTmuxSessionTarget)(sessionName));
538
+ const key = (0, jsonStreamHost_1.parseJsonStreamTmuxSessionName)(sessionName);
539
+ if (!key)
540
+ return;
541
+ try {
542
+ fs.rmSync((0, jsonStreamHost_1.resolveJsonStreamSessionDir)((0, state_1.resolveDataDir)(), key), { recursive: true, force: true });
543
+ }
544
+ catch { /* best-effort */ }
545
+ }
546
+ /**
547
+ * @description Adopt an external json-stream process that survived a bot
548
+ * restart: reopen the stdin FIFO writer and resume the stdout tail from the
549
+ * persisted line-boundary offset — everything claude produced during the
550
+ * downtime replays through the normal pipeline, so the in-flight turn is
551
+ * delivered end-to-end (which is why the bot posts NO reattach recap for an
552
+ * adopt). An unknown / foreign-session offset seeds to the CURRENT EOF (no
553
+ * backlog flood — the first-migration case). No initialize handshake — the
554
+ * surviving process completed it at spawn; a pending question is restored
555
+ * from the sidecar so its buttons stay answerable. Returns `false` — after
556
+ * killing the leftovers — when the process is dead (exitcode present / pid
557
+ * gone / FIFO unheld): the caller falls back to the dead-process `--resume`
558
+ * reopen.
559
+ */
560
+ async adoptExistingTmuxSession(key, sessionName, workDir, claudeSessionId, persistedTail) {
561
+ const names = await (0, tmuxExec_1.tmuxAsync)('list-sessions', '-F', '#{session_name}');
562
+ if (!names.split('\n').includes(sessionName)) {
563
+ console.log(`[ClaudeJson] adopt: tmux session ${sessionName} no longer exists`);
564
+ return false;
565
+ }
566
+ const k = (0, sessionKey_1.keyToString)(key);
567
+ if (this.sessions.has(k)) {
568
+ console.log(`[ClaudeJson] adopt: already tracking ${k}, skipping`);
569
+ return true;
570
+ }
571
+ const sessionDir = (0, jsonStreamHost_1.resolveJsonStreamSessionDir)((0, state_1.resolveDataDir)(), key);
572
+ const paths = (0, jsonStreamHost_1.getJsonStreamSessionPaths)(sessionDir);
573
+ const pid = (0, jsonStreamHost_1.readPidFile)(paths.pidFile);
574
+ const exitCode = (0, jsonStreamHost_1.readExitCodeFile)(paths.exitCodeFile);
575
+ if (pid === null || exitCode !== null || !(0, jsonStreamHost_1.checkIsPidAlive)(pid)) {
576
+ console.log(`[ClaudeJson] adopt: ${sessionName} process is dead (pid=${pid} exit=${exitCode}), cleaning up`);
577
+ await this.killOrphanTmuxSession(sessionName);
578
+ return false;
579
+ }
580
+ const fifoFd = await (0, jsonStreamHost_1.openFifoWriterNonBlocking)(paths.stdinFifo);
581
+ if (fifoFd === null) {
582
+ console.log(`[ClaudeJson] adopt: ${sessionName} is not holding its stdin FIFO, cleaning up`);
583
+ await this.killOrphanTmuxSession(sessionName);
584
+ return false;
585
+ }
586
+ const size = (0, jsonStreamHost_1.getFileSize)(paths.stdoutFile) ?? 0;
587
+ const { startOffset, backgroundTaskIds, isTurnInFlight, outstandingToolUseIds, claudeCodeVersion } = (0, jsonStreamHost_1.resolveAdoptedTail)(persistedTail, claudeSessionId, size);
588
+ console.log(`[ClaudeJson] adopt: re-attaching to ${sessionName} in ${workDir} (pid=${pid}, tail=${startOffset}/${size})`);
589
+ const session = {
590
+ key, workDir, sessionId: claudeSessionId,
591
+ pid, paths, fifoFd,
592
+ stdinWriteChain: Promise.resolve(),
593
+ tail: (0, jsonStreamHost_1.createStdoutTailState)(startOffset),
594
+ pollTimer: null, pollDelayMs: pollBackoff_1.basePollIntervalMs, unchangedStreak: 0,
595
+ isOversizeWarned: false, lastPersistedTailOffset: startOffset,
596
+ reader: new claudeStreamJson_1.ClaudeStreamLineReader(),
597
+ // isBusy starts from the PERSISTED turn-in-flight flag: a turn in a long, silent
598
+ // tool call leaves no frame after the tail offset to rebuild it from. The
599
+ // replayed/live events then keep it right (deltas/toolUse set it, `result`
600
+ // clears it) — see `applyAction`.
601
+ isActive: true, isStopping: false, isSuspending: false, isRespawning: false, isBusy: isTurnInFlight, unconsumedInputCount: 0,
602
+ // The outstanding tools come from the record too: they veto the busy-idle watchdog, which would
603
+ // otherwise clear the restored turn after its silence window and let the stale-tool stop kill it.
604
+ lastStdoutActivityAt: Date.now(), outstandingToolUseIds: new Set(outstandingToolUseIds),
605
+ model: null, reportedModel: null, effort: null,
606
+ currentResponseText: '', emittedLength: 0, outputTimer: null,
607
+ reasoningText: '', reasoningStartedAt: null, reasoningTimer: null, reasoningActive: false,
608
+ toolNamesById: new Map(), questionToolUseIds: new Set(),
609
+ subagentActive: false, childResponseText: '', childEmittedLength: 0, childOutputTimer: null,
610
+ pendingInitResolve: null, initRequestId: null, pendingControlRequests: new Map(),
611
+ compactionInProgress: false, pendingCompaction: null,
612
+ pendingQuestion: this.readQuestionSidecar(paths),
613
+ apiErrorFired: false, swallowNextAbortError: false,
614
+ lastWatermarkOffset: -1,
615
+ // The frames that built the list and reported the version lie before the
616
+ // tail offset (not replayed): both come from the persisted record.
617
+ backgroundTaskIds: new Set(backgroundTaskIds), claudeCodeVersion, applyingChunk: null,
618
+ // Nothing to catch up on when the tail already sits at EOF.
619
+ adoptCatchUpOffset: startOffset < size ? size : null, adoptCatchUpResolvers: [],
620
+ };
621
+ if (session.pendingQuestion) {
622
+ // The external process is still blocked on this question — busy, and the
623
+ // question's internal tool_result echo must stay suppressed after answer.
624
+ session.isBusy = true;
625
+ session.questionToolUseIds.add(session.pendingQuestion.toolUseId);
626
+ }
627
+ this.sessions.set(k, session);
628
+ this.schedulePoll(session, pollBackoff_1.basePollIntervalMs);
629
+ this.emit('started', key);
630
+ return true;
631
+ }
632
+ /**
633
+ * @description Resolves once an ADOPTED session's tail has consumed what its
634
+ * stdout held at the adopt — the replayed frames have rebuilt the live state
635
+ * (busy, tasks, input) — or once the session is gone. Immediate for a session
636
+ * that was not adopted or whose tail already sat at EOF. The stale-tool-list
637
+ * stop (L4) looks only after this: a look before it would read a turn in flight
638
+ * as idle.
639
+ */
640
+ whenAdoptReplayed(key) {
641
+ const session = this.sessions.get((0, sessionKey_1.keyToString)(key));
642
+ if (!session || session.adoptCatchUpOffset === null)
643
+ return Promise.resolve();
644
+ return new Promise((resolve) => { session.adoptCatchUpResolvers.push(resolve); });
645
+ }
646
+ /** Settle the adopt catch-up once the tail consumed up to the adopt-time EOF (or the session ended). */
647
+ settleAdoptCatchUp(session, isSessionGone) {
648
+ if (session.adoptCatchUpOffset === null)
649
+ return;
650
+ if (!isSessionGone && session.tail.consumedBytes < session.adoptCatchUpOffset)
651
+ return;
652
+ session.adoptCatchUpOffset = null;
653
+ const resolvers = session.adoptCatchUpResolvers.splice(0);
654
+ for (const resolve of resolvers)
655
+ resolve();
656
+ }
657
+ /** Restore a pending question persisted by a previous bot life (see
658
+ * {@link writeQuestionSidecar}). Parse-boundary narrowing; the questions
659
+ * array re-runs the same validator as a live control_request, so a corrupt
660
+ * sidecar yields `null`, never a malformed pending question. */
661
+ readQuestionSidecar(paths) {
662
+ let parsed;
663
+ try {
664
+ parsed = JSON.parse(fs.readFileSync(paths.questionFile, 'utf8'));
665
+ }
666
+ catch {
667
+ return null; // absent (the common case) or corrupt
668
+ }
669
+ if (!(0, claudeStreamJson_1.checkIsStreamRecord)(parsed))
670
+ return null;
671
+ const { requestId, toolUseId, rawInput, questions } = parsed;
672
+ if (typeof requestId !== 'string' || typeof toolUseId !== 'string')
673
+ return null;
674
+ if (!(0, claudeStreamJson_1.checkIsStreamRecord)(rawInput) || !Array.isArray(questions))
675
+ return null;
676
+ const revalidated = this.parseQuestions({ questions });
677
+ if (revalidated.length === 0)
678
+ return null;
679
+ return { requestId, toolUseId, rawInput, questions: revalidated };
680
+ }
681
+ performInitialize(session) {
682
+ const requestId = 'init_' + (0, crypto_1.randomUUID)();
683
+ session.initRequestId = requestId;
684
+ const request = {
685
+ type: 'control_request',
686
+ request_id: requestId,
687
+ request: { subtype: 'initialize', supportedDialogKinds: ['refusal_fallback_prompt'] },
688
+ };
689
+ return new Promise((resolve) => {
690
+ let settled = false;
691
+ const done = () => { if (!settled) {
692
+ settled = true;
693
+ session.pendingInitResolve = null;
694
+ resolve();
695
+ } };
696
+ session.pendingInitResolve = done;
697
+ this.writeStdin(session, request);
698
+ setTimeout(() => {
699
+ if (!settled)
700
+ console.warn(`[ClaudeJson] initialize handshake timed out for ${(0, sessionKey_1.keyToString)(session.key)} — questions may be unavailable`);
701
+ done();
702
+ }, initializeHandshakeTimeoutMs);
703
+ });
704
+ }
705
+ stopSession(key) {
706
+ void this.stopSessionInternal(key);
707
+ }
708
+ /**
709
+ * @description Stop the process but KEEP the session resumable (lifecycle plan
710
+ * L3): the same teardown as a stop (SIGTERM, tmux kill, host dir removed —
711
+ * L-D13), converging on `suspended` instead of `stopped`, so the bot keeps the
712
+ * persisted id and the retry / compaction state the stop would drop. The caller
713
+ * checks `checkIsWorking` first — a suspend kills a background task like any
714
+ * stop. Joins a stop already in flight.
715
+ */
716
+ async suspendSession(key) {
717
+ await this.stopSessionInternal(key, { isSuspend: true });
718
+ }
719
+ async stopSessionInternal(key, options = { isSuspend: false }) {
720
+ const k = (0, sessionKey_1.keyToString)(key);
721
+ const inFlight = this.stopsInFlight.get(k);
722
+ if (inFlight)
723
+ return inFlight; // join the running stop instead of racing it
724
+ const session = this.sessions.get(k);
725
+ if (!session)
726
+ return;
727
+ const stopPromise = this.performStop(session, options).finally(() => { this.stopsInFlight.delete(k); });
728
+ this.stopsInFlight.set(k, stopPromise);
729
+ return stopPromise;
730
+ }
731
+ /** Explicit stop = hard-kill the EXTERNAL process (released sessions are
732
+ * never adopted — locked decision): SIGTERM the pid, kill the tmux session,
733
+ * then converge through {@link finalizeExternalExit} (dir removal + `stopped`,
734
+ * or `suspended` for an idle stop that keeps the session resumable). */
735
+ async performStop(session, options) {
736
+ // Reject a pending question server-side before teardown so the model's
737
+ // AskUserQuestion turn is unblocked (deny), mirroring OpenCode's reject.
738
+ this.rejectQuestion(session.key);
739
+ session.isStopping = true;
740
+ session.isSuspending = options.isSuspend;
741
+ // Let the reject (and any queued frame) reach the FIFO before the kill.
742
+ await session.stdinWriteChain.catch(() => { });
743
+ try {
744
+ process.kill(session.pid, 'SIGTERM');
745
+ }
746
+ catch { /* already gone */ }
747
+ await (0, tmuxExec_1.tmuxAsync)('kill-session', '-t', (0, tmuxExec_1.getTmuxSessionTarget)((0, jsonStreamHost_1.buildJsonStreamTmuxSessionName)(session.key)));
748
+ this.finalizeExternalExit(session, null);
749
+ }
750
+ checkIsActive(key) {
751
+ const session = this.sessions.get((0, sessionKey_1.keyToString)(key));
752
+ return session?.isActive === true;
753
+ }
754
+ checkIsBusy(key) {
755
+ return this.sessions.get((0, sessionKey_1.keyToString)(key))?.isBusy === true;
756
+ }
757
+ checkHasUnconsumedInput(key) {
758
+ return (this.sessions.get((0, sessionKey_1.keyToString)(key))?.unconsumedInputCount ?? 0) > 0;
759
+ }
760
+ /**
761
+ * @description Whether the process is WORKING and must not be stopped (L-D2):
762
+ * a running turn, a background task the CLI still runs (background Bash /
763
+ * Monitor / sub-agent — a stop kills them, and a finished one starts its own
764
+ * turn), input written but not yet taken in, or a bot-issued compaction in
765
+ * flight. An unknown / inactive session is not working.
766
+ */
767
+ checkIsWorking(key) {
768
+ const session = this.sessions.get((0, sessionKey_1.keyToString)(key));
769
+ if (!session?.isActive)
770
+ return false;
771
+ return session.isBusy || this.checkHasBackgroundWork(key);
772
+ }
773
+ /** Background tasks, input not yet taken in, a compaction in flight — "working" minus the turn (L-D2). */
774
+ checkHasBackgroundWork(key) {
775
+ const session = this.sessions.get((0, sessionKey_1.keyToString)(key));
776
+ if (!session?.isActive)
777
+ return false;
778
+ return session.backgroundTaskIds.size > 0
779
+ || session.unconsumedInputCount > 0
780
+ || session.compactionInProgress;
781
+ }
782
+ /** The CLI version the live process reported on `system/init`, `null` until then (L-D10 gate input). */
783
+ getClaudeCodeVersion(key) {
784
+ return this.sessions.get((0, sessionKey_1.keyToString)(key))?.claudeCodeVersion ?? null;
785
+ }
786
+ /** L-D10: only a CLI that reports the background-task list may be stopped automatically. */
787
+ checkIsAutoStopSupported(key) {
788
+ return (0, claudeCodeVersion_1.checkIsClaudeAutoStopSupported)(this.getClaudeCodeVersion(key));
789
+ }
790
+ async getRuntimeInfo(key) {
791
+ const session = this.sessions.get((0, sessionKey_1.keyToString)(key));
792
+ if (!session?.isActive) {
793
+ return { version: null, model: null, contextWindowTokens: null, contextUsedTokens: null };
794
+ }
795
+ const filePath = (0, claudeCliAdapter_1.getClaudeTranscriptPath)(session.workDir, session.sessionId);
796
+ try {
797
+ return await (0, claudeRuntimeInfo_1.readClaudeRuntimeInfo)(filePath);
798
+ }
799
+ catch {
800
+ return { version: null, model: null, contextWindowTokens: null, contextUsedTokens: null };
801
+ }
802
+ }
803
+ getClaudeSessionId(key) {
804
+ return this.sessions.get((0, sessionKey_1.keyToString)(key))?.sessionId ?? null;
805
+ }
806
+ /**
807
+ * @description Real, CONFIRMED compaction on the json-stream backend (F3):
808
+ * send `/compact` (or `/compact <instruction>` for F2's closing section) as a
809
+ * stream-json user turn — the CLI parses the slash command, compacts, and
810
+ * reports the outcome on a `compact_boundary` (success, with token counts) /
811
+ * a failing `compact_status` frame. The whole turn's own output is suppressed
812
+ * (`compactionInProgress`) so only the bot's confirmation shows. Resolves the
813
+ * {@link CompactionResult} — a success carrying the boundary frame's pre/post
814
+ * token counts (`null` when only `compact_status success` was seen) — awaiting
815
+ * the terminal signal so the caller can post its notice AFTER the context was
816
+ * really compacted.
817
+ */
818
+ async compactContext(key, instruction) {
819
+ const session = this.sessions.get((0, sessionKey_1.keyToString)(key));
820
+ if (!session?.isActive)
821
+ return { ok: false, error: (0, i18n_1.t)('compact.start_agent_first') };
822
+ if (session.pendingCompaction) {
823
+ return { ok: false, error: (0, i18n_1.t)('compact.failed', { reason: 'a compaction is already running' }) };
824
+ }
825
+ if (session.isBusy)
826
+ return { ok: false, error: (0, i18n_1.t)('compact.busy') };
827
+ const trimmed = instruction?.trim();
828
+ const text = trimmed ? `/compact ${trimmed}` : '/compact';
829
+ const startedAt = Date.now();
830
+ const result = await new Promise((resolve) => {
831
+ // Bounded by SILENCE, not by elapsed time. A flat 3-minute cap on total
832
+ // elapsed time is what made idle compaction look mute: a real 3 min 15.6 s
833
+ // compaction (`compact_metadata.duration_ms: 195649`) was declared failed
834
+ // while the CLI was succeeding, so the notice was never posted. Summarising
835
+ // scales with the context being summarised, and compact-on-idle exists FOR
836
+ // big contexts — any fixed cap fails exactly where the feature matters.
837
+ // While it works the CLI heartbeats `system/status status:"compacting"`
838
+ // every few seconds, so real silence is the honest death signal.
839
+ const timer = setInterval(() => {
840
+ const verdict = (0, jsonStreamBusyWatchdog_1.getCompactionWaitVerdict)({
841
+ msSinceStdoutActivity: Date.now() - session.lastStdoutActivityAt,
842
+ msSinceCompactionStarted: Date.now() - startedAt,
843
+ silenceTimeoutMs: jsonStreamBusyWatchdog_1.compactionSilenceTimeoutMs,
844
+ absoluteTimeoutMs: jsonStreamBusyWatchdog_1.compactionAbsoluteTimeoutMs,
845
+ });
846
+ if (verdict === 'keepWaiting')
847
+ return;
848
+ const outcome = (0, jsonStreamBusyWatchdog_1.getCompactionTimeoutOutcome)({
849
+ verdict,
850
+ sawSuccess: session.pendingCompaction?.sawSuccess === true,
851
+ });
852
+ if (outcome.kind === 'succeededWithoutTokenCounts') {
853
+ this.resolveCompaction(session, { ok: true, preTokens: null, postTokens: null });
854
+ return;
855
+ }
856
+ this.resolveCompaction(session, {
857
+ ok: false,
858
+ error: outcome.reason === 'silent'
859
+ ? `the CLI went silent for ${Math.round(jsonStreamBusyWatchdog_1.compactionSilenceTimeoutMs / 1000)}s without confirming the compaction`
860
+ : `the compaction did not finish within ${Math.round(jsonStreamBusyWatchdog_1.compactionAbsoluteTimeoutMs / 60_000)} min`,
861
+ });
862
+ }, jsonStreamBusyWatchdog_1.compactionWaitPollMs);
863
+ timer.unref?.();
864
+ session.pendingCompaction = { resolve, timer, sawSuccess: false };
865
+ session.compactionInProgress = true;
866
+ // Fresh turn: reset the answer cursor + idle-watchdog clock, mark busy.
867
+ session.currentResponseText = '';
868
+ session.emittedLength = 0;
869
+ session.isBusy = true;
870
+ session.lastStdoutActivityAt = Date.now();
871
+ // Not counted as unconsumed input: a bot-issued `/compact` is a control
872
+ // turn whose echo is not guaranteed, and it only runs on an idle session.
873
+ this.writeStdin(session, { type: 'user', message: { role: 'user', content: text } });
874
+ });
875
+ if (result.ok) {
876
+ console.log(`[ClaudeJson] compacted ${(0, sessionKey_1.keyToString)(key)} (pre=${result.preTokens ?? '?'} post=${result.postTokens ?? '?'})`);
877
+ return result;
878
+ }
879
+ console.warn(`[ClaudeJson] compaction failed for ${(0, sessionKey_1.keyToString)(key)}: ${result.error}`);
880
+ // The raw internal reason is wrapped into the user-facing template HERE (not at
881
+ // the resolve sites) so every failure path reads the same way in the topic.
882
+ return { ok: false, error: (0, i18n_1.t)('compact.failed', { reason: result.error }) };
883
+ }
884
+ /**
885
+ * @description Read the newest compaction summary from the on-disk transcript
886
+ * (the `isCompactSummary:true` message) so F2 can lift its appended closing
887
+ * section into the idle-compaction notice. Returns `null` when the session is
888
+ * inactive, the transcript is unreadable, or no summary is present.
889
+ */
890
+ async getLatestCompactionSummary(key) {
891
+ const session = this.sessions.get((0, sessionKey_1.keyToString)(key));
892
+ if (!session?.isActive)
893
+ return null;
894
+ return readLatestCompactSummaryFromTranscript((0, claudeCliAdapter_1.getClaudeTranscriptPath)(session.workDir, session.sessionId));
895
+ }
896
+ // ─────────────────────────────────────────────────────────────────────────
897
+ // Input (S3)
898
+ // ─────────────────────────────────────────────────────────────────────────
899
+ sendInput(key, input) {
900
+ const session = this.sessions.get((0, sessionKey_1.keyToString)(key));
901
+ if (!session?.isActive) {
902
+ console.warn(`[ClaudeJson] sendInput to inactive session ${(0, sessionKey_1.keyToString)(key)} dropped`);
903
+ return;
904
+ }
905
+ // A fresh user turn begins → reset the answer cursor so its first tail is a
906
+ // new message (the bot also flips `needsNewMessage` for delta adapters).
907
+ session.currentResponseText = '';
908
+ session.emittedLength = 0;
909
+ session.apiErrorFired = false;
910
+ session.isBusy = true;
911
+ // A fresh turn resets the idle-watchdog clock: without this a session that
912
+ // sat idle for minutes (busy=false, clock stale) would trip the watchdog on
913
+ // its very next poll — before claude has a chance to emit the first token.
914
+ session.lastStdoutActivityAt = Date.now();
915
+ // The turn in flight is persisted at once (same offset, flipped flag): a restart
916
+ // during a long, silent tool call finds no frame after the offset to rebuild it.
917
+ this.jsonStreamTailWriter?.(key, buildTailRecord(session, session.lastPersistedTailOffset));
918
+ this.writeUserMessage(session, input);
919
+ }
920
+ sendSignal(key, _signal) {
921
+ this.sendInterrupt(key);
922
+ }
923
+ sendEscape(key) {
924
+ this.sendInterrupt(key);
925
+ }
926
+ sendInterrupt(key) {
927
+ const session = this.sessions.get((0, sessionKey_1.keyToString)(key));
928
+ if (!session?.isActive)
929
+ return;
930
+ this.writeStdin(session, {
931
+ type: 'control_request',
932
+ request_id: 'interrupt_' + (0, crypto_1.randomUUID)(),
933
+ request: { subtype: 'interrupt' },
934
+ });
935
+ // An interrupt ABORTS the current turn. The CLI does not reliably emit a
936
+ // terminal `result` for an aborted turn, so clearing `isBusy` here (rather
937
+ // than waiting on the idle watchdog) makes the explicit stop deterministic —
938
+ // the typing indicator drops immediately. If output resumes, a delta re-arms
939
+ // busy; if a `result` does arrive, `handleTurnEnd` is a harmless no-op.
940
+ session.isBusy = false;
941
+ session.outstandingToolUseIds.clear();
942
+ // When the CLI DOES emit a terminal `result{is_error}` for this abort, it
943
+ // carries no text → `claudeStreamJson` labels it the literal `'API error'`.
944
+ // Arm the one-shot so `handleTurnEnd` swallows it instead of relaying a bogus
945
+ // "Claude error: API error" (the reported triple-message on question-cancel,
946
+ // and any bare `/esc` mid-turn). Consumed by the next `handleTurnEnd`.
947
+ session.swallowNextAbortError = true;
948
+ }
949
+ /** Write one user turn, counting it until Claude echoes it back (it has read it). */
950
+ writeUserMessage(session, content) {
951
+ session.unconsumedInputCount += 1;
952
+ this.writeStdin(session, { type: 'user', message: { role: 'user', content } });
953
+ }
954
+ /** Enqueue one stream-json frame onto the stdin FIFO. Fire-and-forget for
955
+ * callers (sync signature preserved); the per-session chain keeps wire
956
+ * order and absorbs transient `EAGAIN` (see `writeFifoText`). */
957
+ writeStdin(session, obj) {
958
+ const line = JSON.stringify(obj) + '\n';
959
+ session.stdinWriteChain = session.stdinWriteChain
960
+ .then(() => (0, jsonStreamHost_1.writeFifoText)(session.fifoFd, line))
961
+ .catch((e) => console.error(`[ClaudeJson] stdin write failed for ${(0, sessionKey_1.keyToString)(session.key)}:`, e instanceof Error ? e.message : e));
962
+ // New input → output expected shortly; snap the tail poll back to base.
963
+ this.snapPollToBase(session);
964
+ }
965
+ // ─────────────────────────────────────────────────────────────────────────
966
+ // Output parsing (S2) + control channel (S4)
967
+ // ─────────────────────────────────────────────────────────────────────────
968
+ onStdout(session, chunk) {
969
+ const applyingChunk = { isPersistDeferred: false };
970
+ session.applyingChunk = applyingChunk;
971
+ try {
972
+ this.applyStdoutLines(session, session.reader.push(chunk));
973
+ }
974
+ finally {
975
+ session.applyingChunk = null;
976
+ }
977
+ // A persist a handler asked for mid-chunk (see `applyingChunk`) runs now,
978
+ // under the same emit-caught-up gate the drain applies.
979
+ if (applyingChunk.isPersistDeferred && checkIsEmitCaughtUp(session))
980
+ this.persistTailOffset(session);
981
+ }
982
+ applyStdoutLines(session, lines) {
983
+ for (const line of lines) {
984
+ const msg = (0, claudeStreamJson_1.parseStreamJsonLine)(line);
985
+ if (!msg)
986
+ continue;
987
+ for (const action of (0, claudeStreamJson_1.classifyClaudeStreamMessage)(msg)) {
988
+ this.applyAction(session, action);
989
+ }
990
+ // S7: advance the seen-watermark as each PARENT assistant message settles
991
+ // (is relayed), not only at turn end. The external process normally
992
+ // SURVIVES a bot restart now (adopt replays the stdout tail — no recap),
993
+ // but when the process itself DIES mid-turn the dead-process `--resume`
994
+ // fallback recaps from this watermark: without the per-message advance
995
+ // the aborted turn's already-relayed assistant messages would re-count
996
+ // as a false "missed N" (same shared recap reader as the tmux backend,
997
+ // the 2026-07-04 bug). A settled `assistant` message is on disk here, so
998
+ // the transcript EOF includes it. A CHILD (sub-agent) message
999
+ // (`parent_tool_use_id` set) must never advance the watermark.
1000
+ if (msg.type === 'assistant' && msg.parent_tool_use_id == null) {
1001
+ this.advanceWatermarkFromTranscript(session);
1002
+ }
1003
+ }
1004
+ }
1005
+ /**
1006
+ * @description Settle the CLI's reply to a control_request the BOT wrote,
1007
+ * matched by `request_id`: the spawn-time `initialize` handshake, plus the MCP
1008
+ * heal's `mcp_status` / `mcp_reconnect` round-trips. A response for an id we
1009
+ * are not waiting on belongs to nothing of ours — ignore it rather than guess
1010
+ * which awaiter it might settle.
1011
+ */
1012
+ handleControlResponse(session, action) {
1013
+ if (session.pendingInitResolve && action.requestId === session.initRequestId) {
1014
+ session.pendingInitResolve();
1015
+ return;
1016
+ }
1017
+ const pending = session.pendingControlRequests.get(action.requestId);
1018
+ if (!pending)
1019
+ return;
1020
+ session.pendingControlRequests.delete(action.requestId);
1021
+ clearTimeout(pending.timer);
1022
+ pending.resolve({ isSuccess: action.isSuccess, payload: action.payload });
1023
+ }
1024
+ /**
1025
+ * @description Write ONE bot-issued control_request and resolve with its
1026
+ * `control_response`, or `null` when none arrives within
1027
+ * {@link mcpControlRequestTimeoutMs}. The reply comes back through the normal
1028
+ * stdout-tail → classifier path (the CLI answers on the same stdio control
1029
+ * channel `can_use_tool` uses), so the bounded timer is what keeps a silent
1030
+ * CLI from hanging the caller. Never rejects: an unanswered request is an
1031
+ * unknown, not an error.
1032
+ *
1033
+ * A session that is no longer the live one for its key gets NOTHING written:
1034
+ * its fd is closed (and its number reusable), so a write after teardown could
1035
+ * land in an unrelated file — and no reply could ever settle the awaiter
1036
+ * anyway. Same identity guard as the poll tick and the exit finalizer.
1037
+ */
1038
+ requestControl(session, requestId, frame) {
1039
+ if (!session.isActive || this.sessions.get((0, sessionKey_1.keyToString)(session.key)) !== session)
1040
+ return Promise.resolve(null);
1041
+ return new Promise((resolve) => {
1042
+ const timer = setTimeout(() => {
1043
+ // Only the entry still parked here is ours to time out — a response that
1044
+ // landed first already removed it (and resolved).
1045
+ if (!session.pendingControlRequests.delete(requestId))
1046
+ return;
1047
+ resolve(null);
1048
+ }, claudeMcpHeal_1.mcpControlRequestTimeoutMs);
1049
+ timer.unref?.();
1050
+ session.pendingControlRequests.set(requestId, { resolve, timer });
1051
+ this.writeStdin(session, frame);
1052
+ });
1053
+ }
1054
+ /**
1055
+ * @description Heal the bot's own injected MCP server inside this LIVE session:
1056
+ * ask `mcp_status`, and reconnect the server ONLY when it reports `failed`
1057
+ * (the latched state a bot restart leaves behind — the CLI never retries it,
1058
+ * so the bot's tools stay gone for the rest of the session). `connected` is
1059
+ * left strictly alone, and any other status (`needs-auth`, an unknown future
1060
+ * one) is skipped rather than guessed at — see `decideMcpHeal`.
1061
+ *
1062
+ * Control frames only: nothing written here can be read as a user turn, the
1063
+ * session is never marked busy, and the pending-request map is keyed by
1064
+ * `request_id` so the `can_use_tool` / `interrupt` paths are untouched.
1065
+ *
1066
+ * `healed` is reported off the reconnect's own ack, and that ack is MEASURED to
1067
+ * be both synchronous and honest — so no confirming third round-trip is needed
1068
+ * and adding one would only be ceremony. Against a still-dead endpoint the
1069
+ * reconnect answers `{subtype:'error', error:'ECONNREFUSED…'}` (never a hollow
1070
+ * success), and after a successful one an IMMEDIATELY following `mcp_status`
1071
+ * already reads `connected`. Do not re-litigate this as a missing verification.
1072
+ */
1073
+ async healMcpServer(key, serverName) {
1074
+ const session = this.sessions.get((0, sessionKey_1.keyToString)(key));
1075
+ if (!session?.isActive)
1076
+ return 'unavailable';
1077
+ const statusRequestId = 'mcp_status_' + (0, crypto_1.randomUUID)();
1078
+ const status = await this.requestControl(session, statusRequestId, (0, claudeMcpHeal_1.buildMcpStatusControlRequest)(statusRequestId));
1079
+ // No answer (timeout) or an `error` verdict: nothing is known about the
1080
+ // server, so nothing may be done to it.
1081
+ if (!status?.isSuccess)
1082
+ return 'unavailable';
1083
+ const decision = (0, claudeMcpHeal_1.decideMcpHeal)((0, claudeMcpHeal_1.getMcpServerStatus)(status.payload, serverName));
1084
+ if (decision === 'healthy')
1085
+ return 'healthy';
1086
+ if (decision === 'skip')
1087
+ return 'skipped';
1088
+ const reconnectRequestId = 'mcp_reconnect_' + (0, crypto_1.randomUUID)();
1089
+ const reconnect = await this.requestControl(session, reconnectRequestId, (0, claudeMcpHeal_1.buildMcpReconnectControlRequest)(reconnectRequestId, serverName));
1090
+ // `mcp_reconnect` answers a bare success ack (no inner record); anything
1091
+ // else means the reconnect did not land, so the server is still broken.
1092
+ return reconnect?.isSuccess === true ? 'healed' : 'unavailable';
1093
+ }
1094
+ applyAction(session, action) {
1095
+ // A bot-issued `/compact` turn produces its own noise ("Not enough messages
1096
+ // to compact.", the summarisation itself) — suppress ALL of it so only the
1097
+ // bot's confirmation shows. The compact_* frames + turn end still drive the
1098
+ // awaiter; a stray provider error still surfaces. (A CLI-side AUTO compaction
1099
+ // during a normal turn is NOT gated here — `compactionInProgress` is only set
1100
+ // by our own `compactContext`, and `pendingCompaction` guards the handlers.)
1101
+ if (session.compactionInProgress) {
1102
+ switch (action.kind) {
1103
+ case 'turnEnd':
1104
+ this.handleTurnEnd(session, action);
1105
+ return;
1106
+ case 'compactStatus':
1107
+ this.handleCompactStatus(session, action);
1108
+ return;
1109
+ case 'compactBoundary':
1110
+ this.handleCompactBoundary(session, action);
1111
+ return;
1112
+ case 'init':
1113
+ this.handleInit(session, action);
1114
+ return;
1115
+ case 'backgroundTasks':
1116
+ this.handleBackgroundTasks(session, action);
1117
+ return;
1118
+ case 'apiRetry':
1119
+ this.maybeEmitApiError(session, action.text);
1120
+ return;
1121
+ // A reply to one of OUR control requests is never the compaction turn's
1122
+ // output — its awaiter must settle whatever else is running.
1123
+ case 'controlResponse':
1124
+ this.handleControlResponse(session, action);
1125
+ return;
1126
+ default: return; // swallow text / thinking / tool / toolResult / control / echo
1127
+ }
1128
+ }
1129
+ switch (action.kind) {
1130
+ case 'init':
1131
+ // Per-turn `init` re-emits; the id is fixed by our `--session-id`, so the
1132
+ // id itself is nothing to act on. The MODEL it carries is load-bearing:
1133
+ // it is the only report of what claude actually runs. Without capturing
1134
+ // it `/status` and the pinned banner showed no model at all on a default
1135
+ // start / resume / adopt (nothing ever set the `--model` pick).
1136
+ this.handleInit(session, action);
1137
+ return;
1138
+ case 'backgroundTasks':
1139
+ this.handleBackgroundTasks(session, action);
1140
+ return;
1141
+ case 'textDelta':
1142
+ // Mid-turn activity marks the session busy: `sendInput` normally set it,
1143
+ // but an ADOPTED session (bot restart mid-turn) reconstructs the busy
1144
+ // state purely from the replayed/live events; `turnEnd` clears it.
1145
+ session.isBusy = true;
1146
+ if (action.isSubagent)
1147
+ this.handleSubagentText(session, action.text);
1148
+ else
1149
+ this.appendAnswerDelta(session, action.text);
1150
+ return;
1151
+ case 'thinkingDelta':
1152
+ session.isBusy = true;
1153
+ if (action.isSubagent)
1154
+ return; // child reasoning never rendered (parity)
1155
+ this.appendThinkingDelta(session, action.text);
1156
+ return;
1157
+ case 'toolUse':
1158
+ session.isBusy = true;
1159
+ this.handleToolUse(session, action);
1160
+ return;
1161
+ case 'toolResult':
1162
+ this.handleToolResult(session, action);
1163
+ return;
1164
+ case 'turnEnd':
1165
+ this.handleTurnEnd(session, action);
1166
+ return;
1167
+ case 'controlRequest':
1168
+ this.handleControlRequest(session, action);
1169
+ return;
1170
+ case 'controlResponse':
1171
+ this.handleControlResponse(session, action);
1172
+ return;
1173
+ case 'apiRetry':
1174
+ this.maybeEmitApiError(session, action.text);
1175
+ return;
1176
+ case 'compactStatus':
1177
+ // A CLI-side auto-compaction (no bot-issued `/compact`) — no awaiter to
1178
+ // resolve, so this is a no-op; the guard lives in the handler.
1179
+ this.handleCompactStatus(session, action);
1180
+ return;
1181
+ case 'compactBoundary':
1182
+ this.handleCompactBoundary(session, action);
1183
+ return;
1184
+ case 'rateLimit':
1185
+ // Subscription usage-window signal; not surfaced to the topic (parity).
1186
+ return;
1187
+ case 'userEcho':
1188
+ // Claude has read one of our user messages. A message written mid-turn
1189
+ // is taken into the RUNNING turn, so the session is busy from here until
1190
+ // the turn's `result`, even if an earlier `result` already cleared it.
1191
+ session.unconsumedInputCount = Math.max(0, session.unconsumedInputCount - 1);
1192
+ session.isBusy = true;
1193
+ return;
1194
+ }
1195
+ }
1196
+ handleInit(session, action) {
1197
+ if (action.model)
1198
+ session.reportedModel = action.model;
1199
+ if (action.claudeCodeVersion)
1200
+ session.claudeCodeVersion = action.claudeCodeVersion;
1201
+ }
1202
+ /** Replace the live background-task list with the frame's (it is re-sent
1203
+ * WHOLE on every change, so no diffing). The tail-offset persist that follows
1204
+ * this frame's drain carries the new list to `state.json`. */
1205
+ handleBackgroundTasks(session, action) {
1206
+ session.backgroundTaskIds = new Set(action.taskIds);
1207
+ }
1208
+ // — compaction (F3) —
1209
+ handleCompactStatus(session, action) {
1210
+ const pending = session.pendingCompaction;
1211
+ if (!pending)
1212
+ return;
1213
+ if (action.result === 'success') {
1214
+ // Wait for the boundary frame (it carries the pre/post token counts).
1215
+ pending.sawSuccess = true;
1216
+ return;
1217
+ }
1218
+ // Any non-success result (e.g. "not enough messages") is a failure.
1219
+ this.resolveCompaction(session, {
1220
+ ok: false,
1221
+ error: action.error ?? action.result ?? 'compaction did not run',
1222
+ });
1223
+ }
1224
+ handleCompactBoundary(session, action) {
1225
+ if (!session.pendingCompaction) {
1226
+ // The CLI's own compaction (a context overflow mid-turn, or one met while replaying the downtime tail):
1227
+ // nobody waits for it, but the conversation's context was replaced all the same.
1228
+ this.emit('contextCompacted', session.key, action.trigger);
1229
+ return;
1230
+ }
1231
+ this.resolveCompaction(session, { ok: true, preTokens: action.preTokens, postTokens: action.postTokens }, action.trigger);
1232
+ }
1233
+ /**
1234
+ * Resolve (and clear) the in-flight compaction awaiter exactly once. Every way a bot-issued compaction can
1235
+ * succeed (its boundary, its turn end, the timeout that saw its success) ends here, so a success is told
1236
+ * once as `contextCompacted`.
1237
+ */
1238
+ resolveCompaction(session, result, trigger = null) {
1239
+ const pending = session.pendingCompaction;
1240
+ if (!pending)
1241
+ return;
1242
+ session.pendingCompaction = null;
1243
+ session.compactionInProgress = false;
1244
+ if (pending.timer)
1245
+ clearInterval(pending.timer);
1246
+ pending.resolve(result);
1247
+ if (result.ok)
1248
+ this.emit('contextCompacted', session.key, trigger);
1249
+ }
1250
+ // — answer text —
1251
+ appendAnswerDelta(session, text) {
1252
+ // Reasoning is over the moment the answer starts (Claude closes the thinking
1253
+ // block before the text block).
1254
+ this.finishReasoning(session);
1255
+ session.currentResponseText += text;
1256
+ if (session.outputTimer)
1257
+ return; // a flush is already scheduled
1258
+ session.outputTimer = setTimeout(() => {
1259
+ session.outputTimer = null;
1260
+ this.flushAnswer(session, false);
1261
+ }, streamOutputBatchMs);
1262
+ }
1263
+ flushAnswer(session, isFinal) {
1264
+ if (session.outputTimer) {
1265
+ clearTimeout(session.outputTimer);
1266
+ session.outputTimer = null;
1267
+ }
1268
+ const tail = session.currentResponseText.slice(session.emittedLength);
1269
+ if (tail) {
1270
+ session.emittedLength = session.currentResponseText.length;
1271
+ const meta = isFinal ? { isFinal: true } : {};
1272
+ // NO isContinuation: `outputsDeltas` adapters let the transports synthesise it.
1273
+ this.emit('output', session.key, tail, meta);
1274
+ }
1275
+ // The answer batcher is drained — the held-back consumption boundary is now
1276
+ // safe to persist (see `drainStdoutTail`).
1277
+ if (checkIsEmitCaughtUp(session))
1278
+ this.persistTailOffset(session);
1279
+ }
1280
+ // — reasoning / thinking —
1281
+ appendThinkingDelta(session, text) {
1282
+ if (!session.reasoningActive) {
1283
+ session.reasoningActive = true;
1284
+ session.reasoningStartedAt = Date.now();
1285
+ session.reasoningText = '';
1286
+ }
1287
+ session.reasoningText += text;
1288
+ if (session.reasoningTimer)
1289
+ return;
1290
+ session.reasoningTimer = setTimeout(() => {
1291
+ session.reasoningTimer = null;
1292
+ this.emit('thinking', session.key, { phase: 'live', text: session.reasoningText });
1293
+ }, streamOutputBatchMs);
1294
+ }
1295
+ finishReasoning(session) {
1296
+ if (!session.reasoningActive)
1297
+ return;
1298
+ if (session.reasoningTimer) {
1299
+ clearTimeout(session.reasoningTimer);
1300
+ session.reasoningTimer = null;
1301
+ }
1302
+ const durationMs = session.reasoningStartedAt ? Date.now() - session.reasoningStartedAt : undefined;
1303
+ this.emit('thinking', session.key, { phase: 'done', text: session.reasoningText, durationMs });
1304
+ session.reasoningActive = false;
1305
+ session.reasoningStartedAt = null;
1306
+ }
1307
+ // — tools —
1308
+ handleToolUse(session, action) {
1309
+ // AskUserQuestion never renders as a tool (it is intercepted on the control
1310
+ // channel); guard defensively.
1311
+ if (action.tool === 'AskUserQuestion')
1312
+ return;
1313
+ if (action.isSubagent) {
1314
+ this.markSubagentActive(session);
1315
+ return;
1316
+ }
1317
+ session.toolNamesById.set(action.toolUseId, action.tool);
1318
+ // Track it as in flight until its tool_result returns — a busy session with
1319
+ // an outstanding tool (e.g. a long silent Bash) must NOT trip the idle
1320
+ // watchdog even though stdout goes quiet while the tool runs.
1321
+ session.outstandingToolUseIds.add(action.toolUseId);
1322
+ // Transient status only; the completed body arrives as a toolResult.
1323
+ this.emit('status', session.key, `🔧 ${action.tool}`);
1324
+ }
1325
+ handleToolResult(session, action) {
1326
+ // The tool returned — no longer in flight (do this before any early return so
1327
+ // the outstanding-tool set can never leak and pin the idle watchdog open).
1328
+ session.outstandingToolUseIds.delete(action.toolUseId);
1329
+ // Skip AskUserQuestion's internal "questions have been answered" tool_result.
1330
+ if (session.questionToolUseIds.has(action.toolUseId))
1331
+ return;
1332
+ const tool = session.toolNamesById.get(action.toolUseId) ?? 'tool';
1333
+ if (!action.output.trim())
1334
+ return;
1335
+ // Mode-agnostic emit — the bot applies the per-thread `/tool_results` pref.
1336
+ this.emit('toolResult', session.key, { tool, output: action.output });
1337
+ }
1338
+ // — sub-agent (v1) —
1339
+ markSubagentActive(session) {
1340
+ session.subagentActive = true;
1341
+ this.emit('subagentStatus', session.key, { active: true, title: null });
1342
+ }
1343
+ handleSubagentText(session, text) {
1344
+ if (this.getDisplayPrefs(session.key).subagent === 'full') {
1345
+ session.childResponseText += text;
1346
+ if (session.childOutputTimer)
1347
+ return;
1348
+ session.childOutputTimer = setTimeout(() => {
1349
+ session.childOutputTimer = null;
1350
+ const tail = session.childResponseText.slice(session.childEmittedLength);
1351
+ if (!tail.trim())
1352
+ return;
1353
+ session.childEmittedLength = session.childResponseText.length;
1354
+ this.emit('output', session.key, tail, { isSubagent: true });
1355
+ // Child batcher drained — release the held-back tail offset (see
1356
+ // `drainStdoutTail`).
1357
+ if (checkIsEmitCaughtUp(session))
1358
+ this.persistTailOffset(session);
1359
+ }, streamOutputBatchMs);
1360
+ }
1361
+ else {
1362
+ this.markSubagentActive(session);
1363
+ }
1364
+ }
1365
+ clearSubagent(session) {
1366
+ if (session.childOutputTimer) {
1367
+ clearTimeout(session.childOutputTimer);
1368
+ session.childOutputTimer = null;
1369
+ }
1370
+ session.childResponseText = '';
1371
+ session.childEmittedLength = 0;
1372
+ if (session.subagentActive) {
1373
+ session.subagentActive = false;
1374
+ this.emit('subagentStatus', session.key, { active: false, title: null });
1375
+ }
1376
+ }
1377
+ // — turn end —
1378
+ /** Every terminal `result` — a normal turn, a compaction turn, an error — ends with the `turnEnded` event. */
1379
+ handleTurnEnd(session, action) {
1380
+ this.applyTurnEnd(session, action);
1381
+ // After the handlers above: the bot's per-turn stop (L5) reads the session's state as settled here.
1382
+ this.emit('turnEnded', session.key);
1383
+ }
1384
+ applyTurnEnd(session, action) {
1385
+ // L-D11: every turn's accounting, the compaction turn included — the
1386
+ // cross-process prompt-cache check reads these lines, so they must be real
1387
+ // and repeatable, never reconstructed from a transcript afterwards.
1388
+ if (action.usage) {
1389
+ const { inputTokens, cacheReadTokens, cacheWriteTokens, outputTokens } = action.usage;
1390
+ console.log(`${exports.claudeJsonStreamUsageLogPrefix}${(0, sessionKey_1.keyToString)(session.key)}: input=${inputTokens} cacheRead=${cacheReadTokens} cacheWrite=${cacheWriteTokens} output=${outputTokens}`);
1391
+ }
1392
+ session.isBusy = false;
1393
+ session.outstandingToolUseIds.clear();
1394
+ this.finishReasoning(session);
1395
+ this.clearSubagent(session);
1396
+ // A bot-issued `/compact` turn ends here. Emit NOTHING (the summary text /
1397
+ // "not enough" message are suppressed) and settle the awaiter: a still-pending
1398
+ // compaction with no failure signal is a clean success (fallback for a build
1399
+ // that skipped the boundary frame); an errored turn is a failure.
1400
+ if (session.compactionInProgress) {
1401
+ session.swallowNextAbortError = false;
1402
+ if (session.pendingCompaction) {
1403
+ const sawSuccess = session.pendingCompaction.sawSuccess;
1404
+ this.resolveCompaction(session, sawSuccess || !action.isError
1405
+ ? { ok: true, preTokens: null, postTokens: null }
1406
+ : { ok: false, error: action.errorText ?? 'compaction failed' });
1407
+ }
1408
+ else {
1409
+ session.compactionInProgress = false;
1410
+ }
1411
+ return;
1412
+ }
1413
+ // One-shot: consume the interrupt's swallow flag for THIS terminal result
1414
+ // (read-and-clear so it can never leak onto a later, unrelated turn).
1415
+ const swallowAbortError = session.swallowNextAbortError;
1416
+ session.swallowNextAbortError = false;
1417
+ if (action.isError && action.errorText) {
1418
+ // Our own interrupt produced this error result — relay nothing (no bogus
1419
+ // "Claude error", no spurious retry). A REAL provider error arriving
1420
+ // WITHOUT a preceding interrupt still classifies / surfaces normally.
1421
+ if (swallowAbortError) {
1422
+ console.log(`[ClaudeJson] swallowed interrupt-aborted turn error for ${(0, sessionKey_1.keyToString)(session.key)}: ${action.errorText}`);
1423
+ return;
1424
+ }
1425
+ const emitted = this.maybeEmitApiError(session, action.errorText);
1426
+ if (!emitted)
1427
+ this.emit('output', session.key, `Claude error: ${action.errorText}`, { isFinal: true });
1428
+ return;
1429
+ }
1430
+ // Reconciliation backstop: if the delta stream under-delivered vs the final
1431
+ // `result` text, append the gap so the answer is never short.
1432
+ if (action.resultText && action.resultText.length > session.currentResponseText.length
1433
+ && action.resultText.startsWith(session.currentResponseText)) {
1434
+ session.currentResponseText = action.resultText;
1435
+ }
1436
+ this.flushAnswer(session, true);
1437
+ // Advance the seen watermark to the transcript's current byte size so a bot
1438
+ // restart can count assistant messages produced while it was down. Also runs
1439
+ // per settled parent message (see `onStdout`); this turn-end call is the
1440
+ // safety-net catch-up for the final assistant line.
1441
+ this.advanceWatermarkFromTranscript(session);
1442
+ }
1443
+ /**
1444
+ * @description Advance the persisted seen-watermark to the transcript's current
1445
+ * byte size when it grew past the last write (S7). A cheap `fs.statSync().size`
1446
+ * (NOT a whole-file read — this runs per settled parent assistant message, not
1447
+ * only at turn end) plus a monotonic guard, mirroring the tmux backend's
1448
+ * {@link import('./claudeCliAdapter').ClaudeCliAdapter} advance so both Claude
1449
+ * paths track "relayed up to here". Best-effort — a transcript not yet on disk
1450
+ * leaves the watermark where it was; the next relayed message re-tries.
1451
+ */
1452
+ advanceWatermarkFromTranscript(session) {
1453
+ try {
1454
+ const filePath = (0, claudeCliAdapter_1.getClaudeTranscriptPath)(session.workDir, session.sessionId);
1455
+ const eof = fs.statSync(filePath).size;
1456
+ if (eof <= session.lastWatermarkOffset)
1457
+ return; // monotonic: only real growth writes
1458
+ session.lastWatermarkOffset = eof;
1459
+ this.advanceSeenWatermark(session.key, { sessionId: session.sessionId, claudeTranscriptOffset: eof });
1460
+ }
1461
+ catch {
1462
+ // Transcript not yet on disk (UUID just born) / stat failed — retry next relay.
1463
+ }
1464
+ }
1465
+ // — api error —
1466
+ /** Classify + emit an `apiError` once per episode; returns whether it fired. */
1467
+ maybeEmitApiError(session, text) {
1468
+ if (session.apiErrorFired)
1469
+ return false;
1470
+ const apiError = (0, apiErrorRetry_1.classifyAgentApiError)(text, Date.now());
1471
+ if (!apiError)
1472
+ return false;
1473
+ session.apiErrorFired = true;
1474
+ this.emit('apiError', session.key, apiError);
1475
+ return true;
1476
+ }
1477
+ // ─────────────────────────────────────────────────────────────────────────
1478
+ // Questions & permissions — control channel (S4)
1479
+ // ─────────────────────────────────────────────────────────────────────────
1480
+ handleControlRequest(session, action) {
1481
+ if (action.subtype === 'can_use_tool') {
1482
+ if (action.toolName === 'AskUserQuestion' && action.input && action.toolUseId) {
1483
+ session.questionToolUseIds.add(action.toolUseId);
1484
+ this.surfaceQuestion(session, action.requestId, action.toolUseId, action.input);
1485
+ return;
1486
+ }
1487
+ // Any other tool → auto-allow (operator trusts their own agent; parity with
1488
+ // the tmux backend's bypassPermissions). Under bypass this fires only for the
1489
+ // residual circuit-breakers claude never bypasses (`rm -rf /`, `rm -rf ~`).
1490
+ // `buildCanUseToolAllow` guarantees the schema-required `updatedInput`.
1491
+ this.writeControlResponse(session, action.requestId, (0, claudeStreamJson_1.buildCanUseToolAllow)(action.input, action.toolUseId));
1492
+ return;
1493
+ }
1494
+ if (action.subtype === 'request_user_dialog') {
1495
+ // No dialog UI in Telegram → cancel so the CLI falls back gracefully.
1496
+ this.writeControlResponse(session, action.requestId, { behavior: 'cancelled' });
1497
+ return;
1498
+ }
1499
+ // Unknown control request → generic success so the CLI is never left hanging.
1500
+ this.writeControlResponse(session, action.requestId, {});
1501
+ }
1502
+ surfaceQuestion(session, requestId, toolUseId, rawInput) {
1503
+ const questions = this.parseQuestions(rawInput);
1504
+ if (questions.length === 0) {
1505
+ // Nothing to ask — allow with the raw input so the turn continues.
1506
+ this.writeControlResponse(session, requestId, (0, claudeStreamJson_1.buildCanUseToolAllow)(rawInput, toolUseId));
1507
+ return;
1508
+ }
1509
+ session.pendingQuestion = { requestId, toolUseId, rawInput, questions };
1510
+ session.isBusy = true;
1511
+ this.writeQuestionSidecar(session);
1512
+ const payload = { requestId, questions };
1513
+ this.emit('question', session.key, payload);
1514
+ }
1515
+ /**
1516
+ * @description Persist the pending question to the host dir. The external
1517
+ * claude process stays BLOCKED on its `can_use_tool` across a bot restart,
1518
+ * but the control_request line lies BEFORE the persisted tail offset (it was
1519
+ * consumed pre-restart), so tail replay alone would never resurface it — the
1520
+ * sidecar is what lets an adopting bot still answer over the FIFO. Removed
1521
+ * the moment the question resolves (answer / reject); dies with the dir on
1522
+ * stop.
1523
+ */
1524
+ writeQuestionSidecar(session) {
1525
+ if (!session.pendingQuestion)
1526
+ return;
1527
+ try {
1528
+ fs.writeFileSync(session.paths.questionFile, JSON.stringify(session.pendingQuestion));
1529
+ }
1530
+ catch (e) {
1531
+ console.warn(`[ClaudeJson] cannot persist pending question for ${(0, sessionKey_1.keyToString)(session.key)}:`, e instanceof Error ? e.message : e);
1532
+ }
1533
+ }
1534
+ clearQuestionSidecar(session) {
1535
+ try {
1536
+ fs.unlinkSync(session.paths.questionFile);
1537
+ }
1538
+ catch { /* absent */ }
1539
+ }
1540
+ /** Map the AskUserQuestion `input.questions` to the shared question shape
1541
+ * (dropping option `preview`, which breaks in the Telegram relay). */
1542
+ parseQuestions(rawInput) {
1543
+ const rawQuestions = Array.isArray(rawInput.questions) ? rawInput.questions : [];
1544
+ const result = [];
1545
+ for (const raw of rawQuestions) {
1546
+ if (typeof raw !== 'object' || raw === null)
1547
+ continue;
1548
+ const q = raw;
1549
+ if (typeof q.question !== 'string')
1550
+ continue;
1551
+ const rawOptions = Array.isArray(q.options) ? q.options : [];
1552
+ const options = rawOptions
1553
+ .map((o) => {
1554
+ if (typeof o !== 'object' || o === null)
1555
+ return null;
1556
+ const oo = o;
1557
+ if (typeof oo.label !== 'string')
1558
+ return null;
1559
+ return typeof oo.description === 'string' ? { label: oo.label, description: oo.description } : { label: oo.label };
1560
+ })
1561
+ .filter((o) => o !== null);
1562
+ if (options.length === 0)
1563
+ continue;
1564
+ const question = { question: q.question, options };
1565
+ if (typeof q.header === 'string')
1566
+ question.header = q.header;
1567
+ if (q.multiSelect === true)
1568
+ question.multiple = true;
1569
+ result.push(question);
1570
+ }
1571
+ return result;
1572
+ }
1573
+ answerQuestion(key, answers) {
1574
+ const session = this.sessions.get((0, sessionKey_1.keyToString)(key));
1575
+ if (!session?.isActive || !session.pendingQuestion)
1576
+ return;
1577
+ const pending = session.pendingQuestion;
1578
+ session.pendingQuestion = null;
1579
+ this.clearQuestionSidecar(session);
1580
+ // The turn resumes now; reset the idle-watchdog clock so the (possibly long)
1581
+ // wait for the user's answer doesn't instantly trip it before output resumes.
1582
+ session.lastStdoutActivityAt = Date.now();
1583
+ // Build the answers map: question text → selected label(s) (comma-joined for
1584
+ // multi-select), the exact shape AskUserQuestion expects.
1585
+ const answersMap = {};
1586
+ pending.questions.forEach((q, i) => {
1587
+ const selected = answers[i] ?? [];
1588
+ answersMap[q.question] = selected.join(',');
1589
+ });
1590
+ this.writeControlResponse(session, pending.requestId, (0, claudeStreamJson_1.buildCanUseToolAllow)({ ...pending.rawInput, answers: answersMap }, pending.toolUseId));
1591
+ }
1592
+ rejectQuestion(key) {
1593
+ const session = this.sessions.get((0, sessionKey_1.keyToString)(key));
1594
+ if (!session?.pendingQuestion)
1595
+ return;
1596
+ const pending = session.pendingQuestion;
1597
+ session.pendingQuestion = null;
1598
+ this.clearQuestionSidecar(session);
1599
+ this.writeControlResponse(session, pending.requestId, (0, claudeStreamJson_1.buildCanUseToolDeny)('User declined to answer the question.', pending.toolUseId));
1600
+ }
1601
+ writeControlResponse(session, requestId, response) {
1602
+ this.writeStdin(session, { type: 'control_response', response: { subtype: 'success', request_id: requestId, response } });
1603
+ }
1604
+ // ─────────────────────────────────────────────────────────────────────────
1605
+ // Session history / resume (S5) — reuse the shared Claude transcript readers
1606
+ // ─────────────────────────────────────────────────────────────────────────
1607
+ async getSessions(_key, workDir) {
1608
+ return (0, claudeCliAdapter_1.listClaudeSessionsForWorkDir)((0, claudeCliAdapter_1.getClaudeProjectsRoot)(), workDir);
1609
+ }
1610
+ async getRecentTurns(_key, workDir, sessionId, limit) {
1611
+ const filePath = (0, claudeCliAdapter_1.getClaudeTranscriptPath)(workDir, sessionId);
1612
+ return (0, claudeCliAdapter_1.readRecentClaudeTurns)(filePath, limit);
1613
+ }
1614
+ async getReattachRecap(key, workDir, sessionId, watermark) {
1615
+ const filePath = (0, claudeCliAdapter_1.getClaudeTranscriptPath)(workDir, sessionId);
1616
+ const offset = watermark?.claudeTranscriptOffset;
1617
+ const isWatermarkKnown = typeof offset === 'number' && watermark?.sessionId === sessionId;
1618
+ const { missedCount, turns, headOffset } = (0, claudeCliAdapter_1.readClaudeReattachTranscript)(filePath, offset ?? 0, resumeContext_1.resumeContextTurnLimit);
1619
+ const headWatermark = headOffset === undefined ? undefined : { sessionId, claudeTranscriptOffset: headOffset };
1620
+ return { missedCount: isWatermarkKnown ? missedCount : 0, turns, isWatermarkKnown, isActive: this.checkIsBusy(key), headWatermark };
1621
+ }
1622
+ // ─────────────────────────────────────────────────────────────────────────
1623
+ // Model / effort (S6) — spawn flags; a live change re-spawns with --resume
1624
+ // ─────────────────────────────────────────────────────────────────────────
1625
+ async setModel(key, modelId) {
1626
+ const session = this.sessions.get((0, sessionKey_1.keyToString)(key));
1627
+ if (!session?.isActive)
1628
+ return (0, i18n_1.t)('model.start_agent_first');
1629
+ await this.respawnWithChange(session, { model: modelId, effort: session.effort });
1630
+ return null;
1631
+ }
1632
+ /**
1633
+ * @description The model the session is RUNNING. Claude's own `system/init`
1634
+ * report wins over the pinned `--model` pick: it is the resolved id (an alias
1635
+ * like `opus` resolves to its snapshot) and it is the only value present when
1636
+ * no pick was ever made. The pick is the fallback for the window between a
1637
+ * `/model` re-spawn and its first `init`, so the label never blanks out
1638
+ * mid-switch.
1639
+ */
1640
+ getCurrentModel(key) {
1641
+ const session = this.sessions.get((0, sessionKey_1.keyToString)(key));
1642
+ return session?.reportedModel ?? session?.model ?? null;
1643
+ }
1644
+ async getAvailableModels() {
1645
+ // The CLI `--model` accepts these aliases (resolved to the latest snapshot).
1646
+ return ['sonnet', 'opus', 'haiku'];
1647
+ }
1648
+ async setEffort(key, level) {
1649
+ if (!(0, effortLevels_1.checkIsClaudeEffortLevel)(level)) {
1650
+ return (0, i18n_1.t)('effort.invalid_level', { level, valid: (0, effortLevels_1.getClaudeAvailableLevels)().join(', ') });
1651
+ }
1652
+ (0, claudeCliAdapter_1.saveEffortPref)(key, level);
1653
+ const session = this.sessions.get((0, sessionKey_1.keyToString)(key));
1654
+ if (!session?.isActive)
1655
+ return (0, i18n_1.t)('effort.start_agent_first');
1656
+ await this.respawnWithChange(session, { model: session.model, effort: level });
1657
+ return null;
1658
+ }
1659
+ getEffort(key) {
1660
+ return (0, claudeCliAdapter_1.loadEffortPrefs)()[(0, sessionKey_1.keyToString)(key)] ?? null;
1661
+ }
1662
+ async getAvailableEffortLevels(_key) {
1663
+ return (0, effortLevels_1.getClaudeAvailableLevels)();
1664
+ }
1665
+ /**
1666
+ * @description Apply a model/effort change by re-spawning the child with
1667
+ * `--resume <sessionId>` (the on-disk session persists, so only an in-flight
1668
+ * streaming reply is lost — same trade-off as OpenCode's server restart). The
1669
+ * intermediate exit is suppressed (`isRespawning`) so the bot sees no
1670
+ * closed/stopped churn.
1671
+ */
1672
+ async respawnWithChange(session, change) {
1673
+ const { key, workDir, sessionId } = session;
1674
+ session.isRespawning = true;
1675
+ this.rejectQuestion(key);
1676
+ await session.stdinWriteChain.catch(() => { });
1677
+ this.clearTimers(session);
1678
+ session.isActive = false;
1679
+ this.closeFifo(session);
1680
+ try {
1681
+ process.kill(session.pid, 'SIGTERM');
1682
+ }
1683
+ catch { /* gone */ }
1684
+ await (0, tmuxExec_1.tmuxAsync)('kill-session', '-t', (0, tmuxExec_1.getTmuxSessionTarget)((0, jsonStreamHost_1.buildJsonStreamTmuxSessionName)(key)));
1685
+ // spawnSession lays the host dir out fresh and replaces the map entry.
1686
+ await this.spawnSession(key, workDir, sessionId, { effort: change.effort, model: change.model, resume: true });
1687
+ }
1688
+ clearTimers(session) {
1689
+ if (session.pollTimer) {
1690
+ clearTimeout(session.pollTimer);
1691
+ session.pollTimer = null;
1692
+ }
1693
+ if (session.outputTimer) {
1694
+ clearTimeout(session.outputTimer);
1695
+ session.outputTimer = null;
1696
+ }
1697
+ if (session.reasoningTimer) {
1698
+ clearTimeout(session.reasoningTimer);
1699
+ session.reasoningTimer = null;
1700
+ }
1701
+ if (session.childOutputTimer) {
1702
+ clearTimeout(session.childOutputTimer);
1703
+ session.childOutputTimer = null;
1704
+ }
1705
+ // An in-flight compaction dies with the session: the process that was going
1706
+ // to confirm it is gone, so settle the caller now instead of leaving it
1707
+ // parked until the silence watchdog notices a session that no longer exists.
1708
+ this.resolveCompaction(session, { ok: false, error: 'the session ended before the compaction confirmed' });
1709
+ // Same reasoning for bot-issued control requests: the process they were
1710
+ // talking to is going away (stop / exit / model-effort respawn), so their
1711
+ // replies can never arrive. Settle each as an unknown (`null`) and drop the
1712
+ // map, rather than leaving a caller parked on the round-trip timeout and a
1713
+ // timer armed against a dead session.
1714
+ const pendingControls = [...session.pendingControlRequests.values()];
1715
+ session.pendingControlRequests.clear();
1716
+ for (const pending of pendingControls) {
1717
+ clearTimeout(pending.timer);
1718
+ pending.resolve(null);
1719
+ }
1720
+ }
1721
+ }
1722
+ exports.ClaudeJsonStreamAdapter = ClaudeJsonStreamAdapter;
1723
+ /** The tail record persisted for a session at `offsetBytes` — the offset and the working
1724
+ * state in force there (task list, turn in flight, outstanding tools) travel together
1725
+ * (see `JsonStreamTailOffset`). */
1726
+ function buildTailRecord(session, offsetBytes) {
1727
+ return {
1728
+ sessionId: session.sessionId,
1729
+ offsetBytes,
1730
+ backgroundTaskIds: [...session.backgroundTaskIds],
1731
+ isTurnInFlight: session.isBusy,
1732
+ outstandingToolUseIds: [...session.outstandingToolUseIds],
1733
+ // Only once known: the record never claims a version the process did not report.
1734
+ ...(session.claudeCodeVersion !== null ? { claudeCodeVersion: session.claudeCodeVersion } : {}),
1735
+ };
1736
+ }
1737
+ /**
1738
+ * @description Whether every consumed event has LEFT the adapter — nothing is
1739
+ * waiting in the answer / sub-agent 350ms batchers. Gates the tail-offset
1740
+ * persist: batched-but-unemitted text must keep the persisted offset BEHIND
1741
+ * it, so a restart replays (never skips) it. Live thinking text is
1742
+ * deliberately ignored — it renders as a transient frame, so a lost batch of
1743
+ * it at a reload seam costs nothing permanent.
1744
+ */
1745
+ function checkIsEmitCaughtUp(session) {
1746
+ return session.currentResponseText.length === session.emittedLength
1747
+ && session.childResponseText.length === session.childEmittedLength;
1748
+ }
1749
+ /**
1750
+ * @description Extract the newest compaction summary from a Claude on-disk
1751
+ * transcript: the last JSONL line with `isCompactSummary === true`, whose
1752
+ * `message.content` (a string, or `{type:'text'}` blocks) holds the summary the
1753
+ * `/compact` turn produced. Returns `null` for an unreadable file or when no
1754
+ * summary line is present. Reads the whole file — cheap enough for the
1755
+ * once-per-idle-compaction call.
1756
+ */
1757
+ function readLatestCompactSummaryFromTranscript(filePath) {
1758
+ let raw;
1759
+ try {
1760
+ raw = fs.readFileSync(filePath, 'utf8');
1761
+ }
1762
+ catch {
1763
+ return null;
1764
+ }
1765
+ let summary = null;
1766
+ for (const line of raw.split('\n')) {
1767
+ const trimmed = line.trim();
1768
+ if (!trimmed)
1769
+ continue;
1770
+ let obj;
1771
+ try {
1772
+ obj = JSON.parse(trimmed);
1773
+ }
1774
+ catch {
1775
+ continue;
1776
+ }
1777
+ if (!(0, claudeStreamJson_1.checkIsStreamRecord)(obj) || obj.isCompactSummary !== true)
1778
+ continue;
1779
+ const message = (0, claudeStreamJson_1.checkIsStreamRecord)(obj.message) ? obj.message : null;
1780
+ const content = message?.content;
1781
+ if (typeof content === 'string' && content.trim()) {
1782
+ summary = content;
1783
+ }
1784
+ else if (Array.isArray(content)) {
1785
+ const chunks = [];
1786
+ for (const block of content) {
1787
+ if ((0, claudeStreamJson_1.checkIsStreamRecord)(block) && block.type === 'text' && typeof block.text === 'string') {
1788
+ chunks.push(block.text);
1789
+ }
1790
+ }
1791
+ if (chunks.join('').trim())
1792
+ summary = chunks.join('');
1793
+ }
1794
+ }
1795
+ return summary;
1796
+ }
1797
+ /** Spawn-fail diagnostics: the wrapper's exit code + a stderr excerpt (the
1798
+ * passive `stderr.log` is read ONLY here and at unexpected exit — locked
1799
+ * decision, no live stderr consumer). */
1800
+ function describeSpawnFailure(paths) {
1801
+ const exitCode = (0, jsonStreamHost_1.readExitCodeFile)(paths.exitCodeFile);
1802
+ const stderrTail = (0, jsonStreamHost_1.readStderrTail)(paths.stderrFile);
1803
+ const parts = [];
1804
+ if (exitCode !== null)
1805
+ parts.push(`exit=${exitCode}`);
1806
+ if (stderrTail)
1807
+ parts.push(`stderr: ${stderrTail}`);
1808
+ return parts.length > 0 ? ` (${parts.join(', ')})` : '';
1809
+ }
1810
+ //# sourceMappingURL=claudeJsonStreamAdapter.js.map