mstro-app 0.4.2 → 0.4.4

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 (342) hide show
  1. package/bin/mstro.js +119 -40
  2. package/dist/server/cli/headless/claude-invoker-process.d.ts +11 -0
  3. package/dist/server/cli/headless/claude-invoker-process.d.ts.map +1 -0
  4. package/dist/server/cli/headless/claude-invoker-process.js +140 -0
  5. package/dist/server/cli/headless/claude-invoker-process.js.map +1 -0
  6. package/dist/server/cli/headless/claude-invoker-stall.d.ts +40 -0
  7. package/dist/server/cli/headless/claude-invoker-stall.d.ts.map +1 -0
  8. package/dist/server/cli/headless/claude-invoker-stall.js +98 -0
  9. package/dist/server/cli/headless/claude-invoker-stall.js.map +1 -0
  10. package/dist/server/cli/headless/claude-invoker-stream.d.ts +44 -0
  11. package/dist/server/cli/headless/claude-invoker-stream.d.ts.map +1 -0
  12. package/dist/server/cli/headless/claude-invoker-stream.js +276 -0
  13. package/dist/server/cli/headless/claude-invoker-stream.js.map +1 -0
  14. package/dist/server/cli/headless/claude-invoker-tools.d.ts +21 -0
  15. package/dist/server/cli/headless/claude-invoker-tools.d.ts.map +1 -0
  16. package/dist/server/cli/headless/claude-invoker-tools.js +137 -0
  17. package/dist/server/cli/headless/claude-invoker-tools.js.map +1 -0
  18. package/dist/server/cli/headless/claude-invoker.d.ts +6 -4
  19. package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
  20. package/dist/server/cli/headless/claude-invoker.js +10 -804
  21. package/dist/server/cli/headless/claude-invoker.js.map +1 -1
  22. package/dist/server/cli/headless/haiku-assessments.d.ts +62 -0
  23. package/dist/server/cli/headless/haiku-assessments.d.ts.map +1 -0
  24. package/dist/server/cli/headless/haiku-assessments.js +281 -0
  25. package/dist/server/cli/headless/haiku-assessments.js.map +1 -0
  26. package/dist/server/cli/headless/headless-logger.d.ts +3 -2
  27. package/dist/server/cli/headless/headless-logger.d.ts.map +1 -1
  28. package/dist/server/cli/headless/headless-logger.js +28 -5
  29. package/dist/server/cli/headless/headless-logger.js.map +1 -1
  30. package/dist/server/cli/headless/native-timeout-detector.d.ts +44 -0
  31. package/dist/server/cli/headless/native-timeout-detector.d.ts.map +1 -0
  32. package/dist/server/cli/headless/native-timeout-detector.js +99 -0
  33. package/dist/server/cli/headless/native-timeout-detector.js.map +1 -0
  34. package/dist/server/cli/headless/stall-assessor.d.ts +2 -110
  35. package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
  36. package/dist/server/cli/headless/stall-assessor.js +65 -457
  37. package/dist/server/cli/headless/stall-assessor.js.map +1 -1
  38. package/dist/server/cli/headless/types.d.ts +4 -1
  39. package/dist/server/cli/headless/types.d.ts.map +1 -1
  40. package/dist/server/cli/improvisation-attachments.d.ts +21 -0
  41. package/dist/server/cli/improvisation-attachments.d.ts.map +1 -0
  42. package/dist/server/cli/improvisation-attachments.js +116 -0
  43. package/dist/server/cli/improvisation-attachments.js.map +1 -0
  44. package/dist/server/cli/improvisation-retry.d.ts +52 -0
  45. package/dist/server/cli/improvisation-retry.d.ts.map +1 -0
  46. package/dist/server/cli/improvisation-retry.js +434 -0
  47. package/dist/server/cli/improvisation-retry.js.map +1 -0
  48. package/dist/server/cli/improvisation-session-manager.d.ts +10 -266
  49. package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
  50. package/dist/server/cli/improvisation-session-manager.js +117 -1079
  51. package/dist/server/cli/improvisation-session-manager.js.map +1 -1
  52. package/dist/server/cli/improvisation-types.d.ts +86 -0
  53. package/dist/server/cli/improvisation-types.d.ts.map +1 -0
  54. package/dist/server/cli/improvisation-types.js +10 -0
  55. package/dist/server/cli/improvisation-types.js.map +1 -0
  56. package/dist/server/cli/prompt-builders.d.ts +68 -0
  57. package/dist/server/cli/prompt-builders.d.ts.map +1 -0
  58. package/dist/server/cli/prompt-builders.js +312 -0
  59. package/dist/server/cli/prompt-builders.js.map +1 -0
  60. package/dist/server/index.js +33 -212
  61. package/dist/server/index.js.map +1 -1
  62. package/dist/server/mcp/bouncer-haiku.d.ts +10 -0
  63. package/dist/server/mcp/bouncer-haiku.d.ts.map +1 -0
  64. package/dist/server/mcp/bouncer-haiku.js +152 -0
  65. package/dist/server/mcp/bouncer-haiku.js.map +1 -0
  66. package/dist/server/mcp/bouncer-integration.d.ts +3 -4
  67. package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
  68. package/dist/server/mcp/bouncer-integration.js +50 -196
  69. package/dist/server/mcp/bouncer-integration.js.map +1 -1
  70. package/dist/server/mcp/security-analysis.d.ts +38 -0
  71. package/dist/server/mcp/security-analysis.d.ts.map +1 -0
  72. package/dist/server/mcp/security-analysis.js +183 -0
  73. package/dist/server/mcp/security-analysis.js.map +1 -0
  74. package/dist/server/mcp/security-audit.d.ts +1 -1
  75. package/dist/server/mcp/security-audit.d.ts.map +1 -1
  76. package/dist/server/mcp/security-patterns.d.ts +1 -25
  77. package/dist/server/mcp/security-patterns.d.ts.map +1 -1
  78. package/dist/server/mcp/security-patterns.js +55 -260
  79. package/dist/server/mcp/security-patterns.js.map +1 -1
  80. package/dist/server/server-setup.d.ts +22 -0
  81. package/dist/server/server-setup.d.ts.map +1 -0
  82. package/dist/server/server-setup.js +101 -0
  83. package/dist/server/server-setup.js.map +1 -0
  84. package/dist/server/services/file-explorer-ops.d.ts +24 -0
  85. package/dist/server/services/file-explorer-ops.d.ts.map +1 -0
  86. package/dist/server/services/file-explorer-ops.js +211 -0
  87. package/dist/server/services/file-explorer-ops.js.map +1 -0
  88. package/dist/server/services/files.d.ts +2 -85
  89. package/dist/server/services/files.d.ts.map +1 -1
  90. package/dist/server/services/files.js +7 -427
  91. package/dist/server/services/files.js.map +1 -1
  92. package/dist/server/services/plan/composer.d.ts +1 -1
  93. package/dist/server/services/plan/composer.d.ts.map +1 -1
  94. package/dist/server/services/plan/composer.js +118 -32
  95. package/dist/server/services/plan/composer.js.map +1 -1
  96. package/dist/server/services/plan/config-installer.d.ts +25 -0
  97. package/dist/server/services/plan/config-installer.d.ts.map +1 -0
  98. package/dist/server/services/plan/config-installer.js +182 -0
  99. package/dist/server/services/plan/config-installer.js.map +1 -0
  100. package/dist/server/services/plan/dependency-resolver.d.ts +1 -1
  101. package/dist/server/services/plan/dependency-resolver.d.ts.map +1 -1
  102. package/dist/server/services/plan/dependency-resolver.js +4 -1
  103. package/dist/server/services/plan/dependency-resolver.js.map +1 -1
  104. package/dist/server/services/plan/executor.d.ts +38 -74
  105. package/dist/server/services/plan/executor.d.ts.map +1 -1
  106. package/dist/server/services/plan/executor.js +274 -460
  107. package/dist/server/services/plan/executor.js.map +1 -1
  108. package/dist/server/services/plan/front-matter.d.ts +18 -0
  109. package/dist/server/services/plan/front-matter.d.ts.map +1 -0
  110. package/dist/server/services/plan/front-matter.js +44 -0
  111. package/dist/server/services/plan/front-matter.js.map +1 -0
  112. package/dist/server/services/plan/output-manager.d.ts +22 -0
  113. package/dist/server/services/plan/output-manager.d.ts.map +1 -0
  114. package/dist/server/services/plan/output-manager.js +97 -0
  115. package/dist/server/services/plan/output-manager.js.map +1 -0
  116. package/dist/server/services/plan/parser-core.d.ts +20 -0
  117. package/dist/server/services/plan/parser-core.d.ts.map +1 -0
  118. package/dist/server/services/plan/parser-core.js +350 -0
  119. package/dist/server/services/plan/parser-core.js.map +1 -0
  120. package/dist/server/services/plan/parser-migration.d.ts +5 -0
  121. package/dist/server/services/plan/parser-migration.d.ts.map +1 -0
  122. package/dist/server/services/plan/parser-migration.js +124 -0
  123. package/dist/server/services/plan/parser-migration.js.map +1 -0
  124. package/dist/server/services/plan/parser.d.ts +11 -3
  125. package/dist/server/services/plan/parser.d.ts.map +1 -1
  126. package/dist/server/services/plan/parser.js +184 -369
  127. package/dist/server/services/plan/parser.js.map +1 -1
  128. package/dist/server/services/plan/prompt-builder.d.ts +17 -0
  129. package/dist/server/services/plan/prompt-builder.d.ts.map +1 -0
  130. package/dist/server/services/plan/prompt-builder.js +137 -0
  131. package/dist/server/services/plan/prompt-builder.js.map +1 -0
  132. package/dist/server/services/plan/review-gate.d.ts +28 -0
  133. package/dist/server/services/plan/review-gate.d.ts.map +1 -0
  134. package/dist/server/services/plan/review-gate.js +191 -0
  135. package/dist/server/services/plan/review-gate.js.map +1 -0
  136. package/dist/server/services/plan/state-reconciler.d.ts +1 -1
  137. package/dist/server/services/plan/state-reconciler.d.ts.map +1 -1
  138. package/dist/server/services/plan/state-reconciler.js +59 -7
  139. package/dist/server/services/plan/state-reconciler.js.map +1 -1
  140. package/dist/server/services/plan/types.d.ts +68 -0
  141. package/dist/server/services/plan/types.d.ts.map +1 -1
  142. package/dist/server/services/platform-credentials.d.ts +24 -0
  143. package/dist/server/services/platform-credentials.d.ts.map +1 -0
  144. package/dist/server/services/platform-credentials.js +68 -0
  145. package/dist/server/services/platform-credentials.js.map +1 -0
  146. package/dist/server/services/platform.d.ts +1 -31
  147. package/dist/server/services/platform.d.ts.map +1 -1
  148. package/dist/server/services/platform.js +11 -109
  149. package/dist/server/services/platform.js.map +1 -1
  150. package/dist/server/services/terminal/pty-manager.d.ts +7 -97
  151. package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
  152. package/dist/server/services/terminal/pty-manager.js +53 -266
  153. package/dist/server/services/terminal/pty-manager.js.map +1 -1
  154. package/dist/server/services/terminal/pty-utils.d.ts +57 -0
  155. package/dist/server/services/terminal/pty-utils.d.ts.map +1 -0
  156. package/dist/server/services/terminal/pty-utils.js +141 -0
  157. package/dist/server/services/terminal/pty-utils.js.map +1 -0
  158. package/dist/server/services/websocket/file-definition-handlers.d.ts +4 -0
  159. package/dist/server/services/websocket/file-definition-handlers.d.ts.map +1 -0
  160. package/dist/server/services/websocket/file-definition-handlers.js +153 -0
  161. package/dist/server/services/websocket/file-definition-handlers.js.map +1 -0
  162. package/dist/server/services/websocket/file-explorer-handlers.d.ts.map +1 -1
  163. package/dist/server/services/websocket/file-explorer-handlers.js +52 -391
  164. package/dist/server/services/websocket/file-explorer-handlers.js.map +1 -1
  165. package/dist/server/services/websocket/file-search-handlers.d.ts +5 -0
  166. package/dist/server/services/websocket/file-search-handlers.d.ts.map +1 -0
  167. package/dist/server/services/websocket/file-search-handlers.js +238 -0
  168. package/dist/server/services/websocket/file-search-handlers.js.map +1 -0
  169. package/dist/server/services/websocket/file-utils.js +3 -3
  170. package/dist/server/services/websocket/file-utils.js.map +1 -1
  171. package/dist/server/services/websocket/git-branch-handlers.d.ts +7 -0
  172. package/dist/server/services/websocket/git-branch-handlers.d.ts.map +1 -0
  173. package/dist/server/services/websocket/git-branch-handlers.js +110 -0
  174. package/dist/server/services/websocket/git-branch-handlers.js.map +1 -0
  175. package/dist/server/services/websocket/git-diff-handlers.d.ts +6 -0
  176. package/dist/server/services/websocket/git-diff-handlers.d.ts.map +1 -0
  177. package/dist/server/services/websocket/git-diff-handlers.js +123 -0
  178. package/dist/server/services/websocket/git-diff-handlers.js.map +1 -0
  179. package/dist/server/services/websocket/git-handlers.d.ts +2 -31
  180. package/dist/server/services/websocket/git-handlers.d.ts.map +1 -1
  181. package/dist/server/services/websocket/git-handlers.js +35 -541
  182. package/dist/server/services/websocket/git-handlers.js.map +1 -1
  183. package/dist/server/services/websocket/git-log-handlers.d.ts +6 -0
  184. package/dist/server/services/websocket/git-log-handlers.d.ts.map +1 -0
  185. package/dist/server/services/websocket/git-log-handlers.js +128 -0
  186. package/dist/server/services/websocket/git-log-handlers.js.map +1 -0
  187. package/dist/server/services/websocket/git-pr-handlers.d.ts.map +1 -1
  188. package/dist/server/services/websocket/git-pr-handlers.js +13 -53
  189. package/dist/server/services/websocket/git-pr-handlers.js.map +1 -1
  190. package/dist/server/services/websocket/git-tag-handlers.d.ts +6 -0
  191. package/dist/server/services/websocket/git-tag-handlers.d.ts.map +1 -0
  192. package/dist/server/services/websocket/git-tag-handlers.js +76 -0
  193. package/dist/server/services/websocket/git-tag-handlers.js.map +1 -0
  194. package/dist/server/services/websocket/git-utils.d.ts +43 -0
  195. package/dist/server/services/websocket/git-utils.d.ts.map +1 -0
  196. package/dist/server/services/websocket/git-utils.js +201 -0
  197. package/dist/server/services/websocket/git-utils.js.map +1 -0
  198. package/dist/server/services/websocket/handler.d.ts +2 -0
  199. package/dist/server/services/websocket/handler.d.ts.map +1 -1
  200. package/dist/server/services/websocket/handler.js +37 -112
  201. package/dist/server/services/websocket/handler.js.map +1 -1
  202. package/dist/server/services/websocket/plan-board-handlers.d.ts +11 -0
  203. package/dist/server/services/websocket/plan-board-handlers.d.ts.map +1 -0
  204. package/dist/server/services/websocket/plan-board-handlers.js +218 -0
  205. package/dist/server/services/websocket/plan-board-handlers.js.map +1 -0
  206. package/dist/server/services/websocket/plan-execution-handlers.d.ts +9 -0
  207. package/dist/server/services/websocket/plan-execution-handlers.d.ts.map +1 -0
  208. package/dist/server/services/websocket/plan-execution-handlers.js +142 -0
  209. package/dist/server/services/websocket/plan-execution-handlers.js.map +1 -0
  210. package/dist/server/services/websocket/plan-handlers.d.ts +7 -2
  211. package/dist/server/services/websocket/plan-handlers.d.ts.map +1 -1
  212. package/dist/server/services/websocket/plan-handlers.js +21 -462
  213. package/dist/server/services/websocket/plan-handlers.js.map +1 -1
  214. package/dist/server/services/websocket/plan-helpers.d.ts +19 -0
  215. package/dist/server/services/websocket/plan-helpers.d.ts.map +1 -0
  216. package/dist/server/services/websocket/plan-helpers.js +199 -0
  217. package/dist/server/services/websocket/plan-helpers.js.map +1 -0
  218. package/dist/server/services/websocket/plan-issue-handlers.d.ts +12 -0
  219. package/dist/server/services/websocket/plan-issue-handlers.d.ts.map +1 -0
  220. package/dist/server/services/websocket/plan-issue-handlers.js +162 -0
  221. package/dist/server/services/websocket/plan-issue-handlers.js.map +1 -0
  222. package/dist/server/services/websocket/plan-sprint-handlers.d.ts +7 -0
  223. package/dist/server/services/websocket/plan-sprint-handlers.d.ts.map +1 -0
  224. package/dist/server/services/websocket/plan-sprint-handlers.js +206 -0
  225. package/dist/server/services/websocket/plan-sprint-handlers.js.map +1 -0
  226. package/dist/server/services/websocket/quality-complexity.d.ts +14 -0
  227. package/dist/server/services/websocket/quality-complexity.d.ts.map +1 -0
  228. package/dist/server/services/websocket/quality-complexity.js +262 -0
  229. package/dist/server/services/websocket/quality-complexity.js.map +1 -0
  230. package/dist/server/services/websocket/quality-fix-agent.d.ts +16 -0
  231. package/dist/server/services/websocket/quality-fix-agent.d.ts.map +1 -0
  232. package/dist/server/services/websocket/quality-fix-agent.js +140 -0
  233. package/dist/server/services/websocket/quality-fix-agent.js.map +1 -0
  234. package/dist/server/services/websocket/quality-handlers.d.ts.map +1 -1
  235. package/dist/server/services/websocket/quality-handlers.js +34 -346
  236. package/dist/server/services/websocket/quality-handlers.js.map +1 -1
  237. package/dist/server/services/websocket/quality-linting.d.ts +9 -0
  238. package/dist/server/services/websocket/quality-linting.d.ts.map +1 -0
  239. package/dist/server/services/websocket/quality-linting.js +178 -0
  240. package/dist/server/services/websocket/quality-linting.js.map +1 -0
  241. package/dist/server/services/websocket/quality-review-agent.d.ts +19 -0
  242. package/dist/server/services/websocket/quality-review-agent.d.ts.map +1 -0
  243. package/dist/server/services/websocket/quality-review-agent.js +206 -0
  244. package/dist/server/services/websocket/quality-review-agent.js.map +1 -0
  245. package/dist/server/services/websocket/quality-service.d.ts +3 -51
  246. package/dist/server/services/websocket/quality-service.d.ts.map +1 -1
  247. package/dist/server/services/websocket/quality-service.js +9 -651
  248. package/dist/server/services/websocket/quality-service.js.map +1 -1
  249. package/dist/server/services/websocket/quality-tools.d.ts +23 -0
  250. package/dist/server/services/websocket/quality-tools.d.ts.map +1 -0
  251. package/dist/server/services/websocket/quality-tools.js +208 -0
  252. package/dist/server/services/websocket/quality-tools.js.map +1 -0
  253. package/dist/server/services/websocket/quality-types.d.ts +59 -0
  254. package/dist/server/services/websocket/quality-types.d.ts.map +1 -0
  255. package/dist/server/services/websocket/quality-types.js +101 -0
  256. package/dist/server/services/websocket/quality-types.js.map +1 -0
  257. package/dist/server/services/websocket/session-handlers.d.ts +3 -4
  258. package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
  259. package/dist/server/services/websocket/session-handlers.js +3 -378
  260. package/dist/server/services/websocket/session-handlers.js.map +1 -1
  261. package/dist/server/services/websocket/session-history.d.ts +4 -0
  262. package/dist/server/services/websocket/session-history.d.ts.map +1 -0
  263. package/dist/server/services/websocket/session-history.js +208 -0
  264. package/dist/server/services/websocket/session-history.js.map +1 -0
  265. package/dist/server/services/websocket/session-initialization.d.ts +5 -0
  266. package/dist/server/services/websocket/session-initialization.d.ts.map +1 -0
  267. package/dist/server/services/websocket/session-initialization.js +163 -0
  268. package/dist/server/services/websocket/session-initialization.js.map +1 -0
  269. package/dist/server/services/websocket/types.d.ts +12 -2
  270. package/dist/server/services/websocket/types.d.ts.map +1 -1
  271. package/package.json +1 -2
  272. package/server/cli/headless/claude-invoker-process.ts +204 -0
  273. package/server/cli/headless/claude-invoker-stall.ts +164 -0
  274. package/server/cli/headless/claude-invoker-stream.ts +353 -0
  275. package/server/cli/headless/claude-invoker-tools.ts +187 -0
  276. package/server/cli/headless/claude-invoker.ts +15 -1092
  277. package/server/cli/headless/haiku-assessments.ts +365 -0
  278. package/server/cli/headless/headless-logger.ts +26 -5
  279. package/server/cli/headless/native-timeout-detector.ts +117 -0
  280. package/server/cli/headless/stall-assessor.ts +65 -618
  281. package/server/cli/headless/types.ts +4 -1
  282. package/server/cli/improvisation-attachments.ts +148 -0
  283. package/server/cli/improvisation-retry.ts +602 -0
  284. package/server/cli/improvisation-session-manager.ts +140 -1349
  285. package/server/cli/improvisation-types.ts +98 -0
  286. package/server/cli/prompt-builders.ts +370 -0
  287. package/server/index.ts +35 -246
  288. package/server/mcp/bouncer-haiku.ts +182 -0
  289. package/server/mcp/bouncer-integration.ts +87 -248
  290. package/server/mcp/security-analysis.ts +217 -0
  291. package/server/mcp/security-audit.ts +1 -1
  292. package/server/mcp/security-patterns.ts +60 -283
  293. package/server/server-setup.ts +114 -0
  294. package/server/services/file-explorer-ops.ts +293 -0
  295. package/server/services/files.ts +20 -532
  296. package/server/services/plan/composer.ts +140 -35
  297. package/server/services/plan/config-installer.ts +187 -0
  298. package/server/services/plan/dependency-resolver.ts +4 -1
  299. package/server/services/plan/executor.ts +281 -488
  300. package/server/services/plan/front-matter.ts +48 -0
  301. package/server/services/plan/output-manager.ts +113 -0
  302. package/server/services/plan/parser-core.ts +406 -0
  303. package/server/services/plan/parser-migration.ts +128 -0
  304. package/server/services/plan/parser.ts +188 -394
  305. package/server/services/plan/prompt-builder.ts +161 -0
  306. package/server/services/plan/review-gate.ts +212 -0
  307. package/server/services/plan/state-reconciler.ts +68 -7
  308. package/server/services/plan/types.ts +101 -1
  309. package/server/services/platform-credentials.ts +83 -0
  310. package/server/services/platform.ts +16 -131
  311. package/server/services/terminal/pty-manager.ts +66 -313
  312. package/server/services/terminal/pty-utils.ts +176 -0
  313. package/server/services/websocket/file-definition-handlers.ts +165 -0
  314. package/server/services/websocket/file-explorer-handlers.ts +37 -452
  315. package/server/services/websocket/file-search-handlers.ts +291 -0
  316. package/server/services/websocket/file-utils.ts +3 -3
  317. package/server/services/websocket/git-branch-handlers.ts +130 -0
  318. package/server/services/websocket/git-diff-handlers.ts +140 -0
  319. package/server/services/websocket/git-handlers.ts +40 -625
  320. package/server/services/websocket/git-log-handlers.ts +149 -0
  321. package/server/services/websocket/git-pr-handlers.ts +17 -62
  322. package/server/services/websocket/git-tag-handlers.ts +91 -0
  323. package/server/services/websocket/git-utils.ts +230 -0
  324. package/server/services/websocket/handler.ts +39 -112
  325. package/server/services/websocket/plan-board-handlers.ts +277 -0
  326. package/server/services/websocket/plan-execution-handlers.ts +184 -0
  327. package/server/services/websocket/plan-handlers.ts +23 -544
  328. package/server/services/websocket/plan-helpers.ts +215 -0
  329. package/server/services/websocket/plan-issue-handlers.ts +204 -0
  330. package/server/services/websocket/plan-sprint-handlers.ts +252 -0
  331. package/server/services/websocket/quality-complexity.ts +294 -0
  332. package/server/services/websocket/quality-fix-agent.ts +181 -0
  333. package/server/services/websocket/quality-handlers.ts +36 -404
  334. package/server/services/websocket/quality-linting.ts +187 -0
  335. package/server/services/websocket/quality-review-agent.ts +246 -0
  336. package/server/services/websocket/quality-service.ts +11 -762
  337. package/server/services/websocket/quality-tools.ts +209 -0
  338. package/server/services/websocket/quality-types.ts +169 -0
  339. package/server/services/websocket/session-handlers.ts +5 -437
  340. package/server/services/websocket/session-history.ts +222 -0
  341. package/server/services/websocket/session-initialization.ts +209 -0
  342. package/server/services/websocket/types.ts +46 -2
@@ -0,0 +1,353 @@
1
+ // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file for details.
3
+
4
+ import { hlog } from './headless-logger.js';
5
+ import type { NativeTimeoutDetector } from './native-timeout-detector.js';
6
+ import { classifyError } from './stall-assessor.js';
7
+ import type { ResolvedHeadlessConfig, ToolUseAccumulator } from './types.js';
8
+
9
+ // biome-ignore lint/suspicious/noExplicitAny: external CLI stream JSON with heterogeneous shapes
10
+ type StreamJson = any;
11
+
12
+ export interface StreamHandlerContext {
13
+ config: ResolvedHeadlessConfig;
14
+ accumulatedAssistantResponse: string;
15
+ accumulatedThinking: string;
16
+ accumulatedToolUse: ToolUseAccumulator[];
17
+ toolInputBuffers: Map<number, { name: string; id: string; inputJson: string; startTime: number }>;
18
+ nativeTimeoutDetector: NativeTimeoutDetector;
19
+ /** When true, assistant text is buffered instead of forwarded to outputCallback.
20
+ * Active during resume mode until thinking/tool activity confirms Claude has context. */
21
+ resumeAssessmentActive: boolean;
22
+ /** Buffered assistant text during resume assessment */
23
+ resumeAssessmentBuffer: string;
24
+ /** Cumulative API token usage from message_start/message_delta events */
25
+ apiTokenUsage: { inputTokens: number; outputTokens: number };
26
+ /** Tracks cumulative output_tokens within the current step (message_delta is cumulative per-step) */
27
+ currentStepOutputTokens: number;
28
+ /** Timestamp of the last token usage change (tokens still flowing = process alive) */
29
+ lastTokenActivityTime: number;
30
+ /** Claude Code result event stop_reason (e.g., 'end_turn', 'max_tokens') */
31
+ stopReason?: string;
32
+ }
33
+
34
+ /** Log messages when verbose mode is enabled */
35
+ export function verboseLog(verbose: boolean | undefined, ...msgs: string[]): void {
36
+ if (verbose) {
37
+ for (const msg of msgs) hlog(msg);
38
+ }
39
+ }
40
+
41
+ // ========== Stream Event Handlers ==========
42
+
43
+ function handleSessionCapture(
44
+ parsed: StreamJson,
45
+ captured: { claudeSessionId?: string }
46
+ ): void {
47
+ if (parsed.type === 'system' && parsed.subtype === 'init' && parsed.session_id) {
48
+ captured.claudeSessionId = parsed.session_id;
49
+ }
50
+ if (parsed.type === 'result' && parsed.session_id && !captured.claudeSessionId) {
51
+ captured.claudeSessionId = parsed.session_id;
52
+ }
53
+ }
54
+
55
+ function handleThinkingDelta(event: StreamJson, ctx: StreamHandlerContext): string {
56
+ if (
57
+ event.type !== 'content_block_delta' ||
58
+ event.delta?.type !== 'thinking_delta' ||
59
+ !event.delta?.thinking
60
+ ) {
61
+ return ctx.accumulatedThinking;
62
+ }
63
+
64
+ if (ctx.resumeAssessmentActive) {
65
+ ctx.resumeAssessmentActive = false;
66
+ if (ctx.resumeAssessmentBuffer) {
67
+ ctx.config.outputCallback?.(ctx.resumeAssessmentBuffer);
68
+ ctx.resumeAssessmentBuffer = '';
69
+ }
70
+ }
71
+
72
+ const thinking = event.delta.thinking;
73
+ const updated = ctx.accumulatedThinking + thinking;
74
+
75
+ if (ctx.config.thinkingCallback) {
76
+ ctx.config.thinkingCallback(thinking);
77
+ } else if (ctx.config.outputCallback) {
78
+ ctx.config.outputCallback(thinking);
79
+ } else {
80
+ process.stdout.write(thinking);
81
+ }
82
+
83
+ return updated;
84
+ }
85
+
86
+ function handleTextDelta(event: StreamJson, ctx: StreamHandlerContext): string {
87
+ if (
88
+ event.type !== 'content_block_delta' ||
89
+ event.delta?.type !== 'text_delta' ||
90
+ !event.delta?.text
91
+ ) {
92
+ return ctx.accumulatedAssistantResponse;
93
+ }
94
+
95
+ const text = event.delta.text;
96
+ const updated = ctx.accumulatedAssistantResponse + text;
97
+
98
+ const { passthrough, timeouts } = ctx.nativeTimeoutDetector.processChunk(text);
99
+
100
+ for (const timeout of timeouts) {
101
+ ctx.config.outputCallback?.(
102
+ `\n[[MSTRO_NATIVE_TIMEOUT]] ${timeout.toolName} timed out \u2014 ${timeout.action} with ${timeout.preservedCount} results preserved\n`
103
+ );
104
+ }
105
+
106
+ if (ctx.resumeAssessmentActive) {
107
+ if (passthrough) {
108
+ ctx.resumeAssessmentBuffer += passthrough;
109
+ }
110
+ return updated;
111
+ }
112
+
113
+ if (passthrough && ctx.config.outputCallback) {
114
+ ctx.config.outputCallback(passthrough);
115
+ }
116
+
117
+ return updated;
118
+ }
119
+
120
+ /** Accumulate input tokens from a message_start event */
121
+ function handleMessageStartTokens(event: StreamJson, ctx: StreamHandlerContext): boolean {
122
+ if (event.type !== 'message_start' || !event.message?.usage) return false;
123
+ const usage = event.message.usage;
124
+ ctx.currentStepOutputTokens = 0;
125
+ let changed = false;
126
+ if (typeof usage.input_tokens === 'number') {
127
+ ctx.apiTokenUsage.inputTokens += usage.input_tokens;
128
+ changed = true;
129
+ }
130
+ if (typeof usage.cache_creation_input_tokens === 'number') {
131
+ ctx.apiTokenUsage.inputTokens += usage.cache_creation_input_tokens;
132
+ changed = true;
133
+ }
134
+ if (typeof usage.cache_read_input_tokens === 'number') {
135
+ ctx.apiTokenUsage.inputTokens += usage.cache_read_input_tokens;
136
+ changed = true;
137
+ }
138
+ verboseLog(ctx.config.verbose,
139
+ `[TOKENS] message_start: input=${usage.input_tokens ?? 0} cache_create=${usage.cache_creation_input_tokens ?? 0} cache_read=${usage.cache_read_input_tokens ?? 0} → total_input=${ctx.apiTokenUsage.inputTokens}`);
140
+ return changed;
141
+ }
142
+
143
+ /** Accumulate output tokens from a message_delta event (cumulative tracking) */
144
+ function handleMessageDeltaTokens(event: StreamJson, ctx: StreamHandlerContext): boolean {
145
+ if (event.type !== 'message_delta' || !event.usage) return false;
146
+ if (typeof event.usage.output_tokens !== 'number') return false;
147
+ const increment = event.usage.output_tokens - ctx.currentStepOutputTokens;
148
+ verboseLog(ctx.config.verbose,
149
+ `[TOKENS] message_delta: output=${event.usage.output_tokens} (step_prev=${ctx.currentStepOutputTokens} increment=${increment}) → total_output=${ctx.apiTokenUsage.outputTokens + Math.max(increment, 0)}`);
150
+ if (increment <= 0) return false;
151
+ ctx.apiTokenUsage.outputTokens += increment;
152
+ ctx.currentStepOutputTokens = event.usage.output_tokens;
153
+ return true;
154
+ }
155
+
156
+ function handleTokenUsage(event: StreamJson, ctx: StreamHandlerContext): void {
157
+ const changed = handleMessageStartTokens(event, ctx) || handleMessageDeltaTokens(event, ctx);
158
+ if (changed) {
159
+ ctx.lastTokenActivityTime = Date.now();
160
+ ctx.config.tokenUsageCallback?.({ ...ctx.apiTokenUsage });
161
+ }
162
+ }
163
+
164
+ /** Extract definitive token usage from the result event */
165
+ function handleResultTokenUsage(parsed: StreamJson, ctx: StreamHandlerContext): void {
166
+ if (!parsed.usage) return;
167
+ const u = parsed.usage;
168
+ const input = (typeof u.input_tokens === 'number' ? u.input_tokens : 0)
169
+ + (typeof u.cache_creation_input_tokens === 'number' ? u.cache_creation_input_tokens : 0)
170
+ + (typeof u.cache_read_input_tokens === 'number' ? u.cache_read_input_tokens : 0);
171
+ const output = typeof u.output_tokens === 'number' ? u.output_tokens : 0;
172
+
173
+ if (input > 0 || output > 0) {
174
+ verboseLog(ctx.config.verbose,
175
+ `[TOKENS] Result event usage: input=${input} output=${output} ` +
176
+ `(stream accumulated: input=${ctx.apiTokenUsage.inputTokens} output=${ctx.apiTokenUsage.outputTokens})`);
177
+ ctx.apiTokenUsage = { inputTokens: input, outputTokens: output };
178
+ ctx.lastTokenActivityTime = Date.now();
179
+ ctx.config.tokenUsageCallback?.({ ...ctx.apiTokenUsage });
180
+ }
181
+ }
182
+
183
+ function handleToolResult(parsed: StreamJson, ctx: StreamHandlerContext): void {
184
+ if (parsed.type !== 'user' || !parsed.message?.content) {
185
+ return;
186
+ }
187
+
188
+ for (const content of parsed.message.content) {
189
+ if (content.type !== 'tool_result') {
190
+ continue;
191
+ }
192
+
193
+ const toolId = content.tool_use_id;
194
+ const result = content.content;
195
+ const isError = content.is_error || false;
196
+ const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
197
+
198
+ const toolEntry = ctx.accumulatedToolUse.find(t => t.toolId === toolId);
199
+ if (toolEntry) {
200
+ toolEntry.result = resultStr;
201
+ toolEntry.isError = isError;
202
+ toolEntry.duration = Date.now() - toolEntry.startTime;
203
+ }
204
+
205
+ if (ctx.config.toolUseCallback) {
206
+ ctx.config.toolUseCallback({ type: 'tool_result', toolId, result: resultStr, isError });
207
+ }
208
+ }
209
+ }
210
+
211
+ // ========== Stream Processing ==========
212
+
213
+ export function processStreamEvent(parsed: StreamJson, ctx: StreamHandlerContext): void {
214
+ if (parsed.type === 'error') {
215
+ const errorMessage = parsed.error?.message || parsed.message || JSON.stringify(parsed);
216
+ ctx.config.outputCallback?.(`\n[[MSTRO_ERROR:CLAUDE_ERROR]] ${errorMessage}\n`);
217
+ return;
218
+ }
219
+
220
+ if (parsed.type === 'result') {
221
+ handleResultTokenUsage(parsed, ctx);
222
+ if (parsed.stop_reason) {
223
+ ctx.stopReason = parsed.stop_reason;
224
+ }
225
+ if (parsed.is_error) {
226
+ const errorMessage = parsed.error || parsed.result || 'Unknown error in result';
227
+ ctx.config.outputCallback?.(`\n[[MSTRO_ERROR:CLAUDE_RESULT_ERROR]] ${errorMessage}\n`);
228
+ return;
229
+ }
230
+ }
231
+
232
+ if (parsed.type === 'stream_event' && parsed.event) {
233
+ const event = parsed.event;
234
+ ctx.accumulatedThinking = handleThinkingDelta(event, ctx);
235
+ ctx.accumulatedAssistantResponse = handleTextDelta(event, ctx);
236
+ handleToolStreamEvents(event, ctx);
237
+ handleTokenUsage(event, ctx);
238
+ }
239
+ handleToolResult(parsed, ctx);
240
+ }
241
+
242
+ /** Handle tool_use content_block_start */
243
+ function handleToolStart(event: StreamJson, ctx: StreamHandlerContext): void {
244
+ if (event.type !== 'content_block_start' || event.content_block?.type !== 'tool_use') return;
245
+
246
+ if (ctx.resumeAssessmentActive) {
247
+ ctx.resumeAssessmentActive = false;
248
+ if (ctx.resumeAssessmentBuffer) {
249
+ ctx.config.outputCallback?.(ctx.resumeAssessmentBuffer);
250
+ ctx.resumeAssessmentBuffer = '';
251
+ }
252
+ }
253
+
254
+ const toolName = event.content_block.name;
255
+ const toolId = event.content_block.id;
256
+ const index = event.index;
257
+
258
+ ctx.toolInputBuffers.set(index, { name: toolName, id: toolId, inputJson: '', startTime: Date.now() });
259
+ ctx.config.toolUseCallback?.({ type: 'tool_start', toolName, toolId, index });
260
+ }
261
+
262
+ /** Handle input_json_delta for tool input streaming */
263
+ function handleToolInputDelta(event: StreamJson, ctx: StreamHandlerContext): void {
264
+ if (event.type !== 'content_block_delta' || event.delta?.type !== 'input_json_delta') return;
265
+
266
+ const index = event.index;
267
+ const partialJson = event.delta.partial_json;
268
+ const toolBuffer = ctx.toolInputBuffers.get(index);
269
+ if (toolBuffer) toolBuffer.inputJson += partialJson;
270
+ ctx.config.toolUseCallback?.({ type: 'tool_input_delta', partialJson, index });
271
+ }
272
+
273
+ /** Handle content_block_stop — finalize tool input and emit tool_complete */
274
+ function handleToolComplete(event: StreamJson, ctx: StreamHandlerContext): void {
275
+ if (event.type !== 'content_block_stop') return;
276
+
277
+ const index = event.index;
278
+ const toolBuffer = ctx.toolInputBuffers.get(index);
279
+ if (!toolBuffer) return;
280
+
281
+ let completeInput: Record<string, unknown> = {};
282
+ try { completeInput = JSON.parse(toolBuffer.inputJson); } catch { /* incomplete JSON */ }
283
+
284
+ ctx.accumulatedToolUse.push({
285
+ toolName: toolBuffer.name, toolId: toolBuffer.id,
286
+ toolInput: completeInput, startTime: toolBuffer.startTime
287
+ });
288
+ ctx.toolInputBuffers.delete(index);
289
+
290
+ ctx.config.toolUseCallback?.({
291
+ type: 'tool_complete', toolName: toolBuffer.name, toolId: toolBuffer.id,
292
+ index, completeInput
293
+ });
294
+ }
295
+
296
+ /** Handle tool-specific stream events (start, input delta, complete) */
297
+ function handleToolStreamEvents(event: StreamJson, ctx: StreamHandlerContext): void {
298
+ handleToolStart(event, ctx);
299
+ handleToolInputDelta(event, ctx);
300
+ handleToolComplete(event, ctx);
301
+ }
302
+
303
+ export function processStreamLines(
304
+ buffer: string,
305
+ sessionCapture: { claudeSessionId?: string },
306
+ ctx: StreamHandlerContext
307
+ ): string {
308
+ const lines = buffer.split('\n');
309
+ const remainder = lines.pop() || '';
310
+
311
+ for (const line of lines) {
312
+ if (!line.trim()) continue;
313
+ try {
314
+ const parsed = JSON.parse(line);
315
+ handleSessionCapture(parsed, sessionCapture);
316
+ processStreamEvent(parsed, ctx);
317
+ } catch { /* Ignore parse errors */ }
318
+ }
319
+
320
+ return remainder;
321
+ }
322
+
323
+ /** Flush native timeout detector buffers and return post-timeout output if any */
324
+ export function flushNativeTimeoutBuffers(ctx: StreamHandlerContext): string | undefined {
325
+ const remaining = ctx.nativeTimeoutDetector.flush();
326
+ const buffered = ctx.nativeTimeoutDetector.bufferedPostTimeoutOutput;
327
+ const postTimeout = (buffered + remaining) || undefined;
328
+
329
+ if (!postTimeout && remaining) {
330
+ ctx.config.outputCallback?.(remaining);
331
+ }
332
+
333
+ return postTimeout;
334
+ }
335
+
336
+ /** Classify unmatched stderr via Haiku when process exits with error */
337
+ export async function classifyUnmatchedStderr(
338
+ stderr: string,
339
+ errorAlreadySurfaced: boolean,
340
+ code: number | null,
341
+ config: ResolvedHeadlessConfig,
342
+ ): Promise<void> {
343
+ if (!stderr || errorAlreadySurfaced || code === 0) return;
344
+
345
+ try {
346
+ const classified = await classifyError(stderr, config.claudeCommand, config.verbose);
347
+ if (classified) {
348
+ config.outputCallback?.(`\n[[MSTRO_ERROR:${classified.errorCode}]] ${classified.message}\n`);
349
+ }
350
+ } catch {
351
+ // Haiku classification failed — proceed without it
352
+ }
353
+ }
@@ -0,0 +1,187 @@
1
+ // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file for details.
3
+
4
+ import type { ChildProcess } from 'node:child_process';
5
+ import type { StallState } from './claude-invoker-stall.js';
6
+ import type { StreamHandlerContext } from './claude-invoker-stream.js';
7
+ import { verboseLog } from './claude-invoker-stream.js';
8
+ import { killProcessGroup } from './runner.js';
9
+ import { assessToolTimeout } from './stall-assessor.js';
10
+ import { ToolWatchdog } from './tool-watchdog.js';
11
+ import type { ResolvedHeadlessConfig, ToolUseEvent } from './types.js';
12
+
13
+ // ========== Tool Tracking ==========
14
+
15
+ /** Summarize a tool's input for stall assessment context */
16
+ function summarizeToolInput(input: Record<string, unknown>): string | undefined {
17
+ try {
18
+ if (input.description) return String(input.description).slice(0, 200);
19
+ if (input.prompt) return String(input.prompt).slice(0, 200);
20
+ if (input.command) return String(input.command).slice(0, 200);
21
+ if (input.pattern) return `pattern: ${String(input.pattern).slice(0, 100)}`;
22
+ return JSON.stringify(input).slice(0, 200);
23
+ } catch {
24
+ return undefined;
25
+ }
26
+ }
27
+
28
+ /** Shared mutable state for tool event handlers */
29
+ interface ToolTrackingState {
30
+ pendingTools: Map<string, string>;
31
+ counters: { lastToolInputSummary: string | undefined; totalToolCalls: number };
32
+ toolIdToName: Map<string, string>;
33
+ toolIdToInput: Map<string, Record<string, unknown>>;
34
+ watchdog: ToolWatchdog | null;
35
+ stallState: StallState;
36
+ ctx: StreamHandlerContext;
37
+ onTimeout: (hungToolId: string) => void;
38
+ }
39
+
40
+ export interface ToolTrackingResult {
41
+ pendingTools: Map<string, string>;
42
+ watchdog: ToolWatchdog | null;
43
+ toolWatchdogActive: boolean;
44
+ counters: { lastToolInputSummary: string | undefined; totalToolCalls: number };
45
+ /** Must be called after stallCheckInterval is created, to wire up the kill handler */
46
+ setKillContext: (claudeProcess: ChildProcess, stallCheckInterval: ReturnType<typeof setInterval>) => void;
47
+ }
48
+
49
+ function onToolStart(event: ToolUseEvent, s: ToolTrackingState): void {
50
+ const id = event.toolId!;
51
+ s.pendingTools.set(id, event.toolName!);
52
+ s.counters.totalToolCalls++;
53
+ s.toolIdToName.set(id, event.toolName!);
54
+ if (s.watchdog) {
55
+ s.watchdog.startWatch(id, event.toolName!, {}, () => { s.onTimeout(id); });
56
+ }
57
+ }
58
+
59
+ function onToolComplete(event: ToolUseEvent, s: ToolTrackingState): void {
60
+ const id = event.toolId!;
61
+ const input = event.completeInput ?? {};
62
+ s.counters.lastToolInputSummary = summarizeToolInput(input);
63
+ s.toolIdToInput.set(id, input);
64
+ if (!s.watchdog) return;
65
+ const toolName = s.toolIdToName.get(id);
66
+ if (toolName) {
67
+ s.watchdog.startWatch(id, toolName, input, () => { s.onTimeout(id); });
68
+ }
69
+ }
70
+
71
+ function onToolResult(event: ToolUseEvent, s: ToolTrackingState): void {
72
+ const id = event.toolId!;
73
+ s.pendingTools.delete(id);
74
+ s.stallState.stallWarningEmitted = false;
75
+ s.stallState.lastActivityTime = Date.now();
76
+ const toolEntry = s.ctx.accumulatedToolUse.find(t => t.toolId === id);
77
+ if (!s.watchdog || !toolEntry) return;
78
+ const toolName = s.toolIdToName.get(id);
79
+ if (toolName && toolEntry.duration) {
80
+ s.watchdog.recordCompletion(toolName, toolEntry.duration);
81
+ }
82
+ s.watchdog.clearWatch(id);
83
+ }
84
+
85
+ /** Resolve a display URL from tool input for timeout messages */
86
+ function resolveToolUrl(toolInput: Record<string, unknown>): string | undefined {
87
+ if (toolInput.url) return String(toolInput.url);
88
+ if (toolInput.query) return String(toolInput.query);
89
+ return undefined;
90
+ }
91
+
92
+ /** Handle a tool timeout by building a checkpoint and killing the process */
93
+ function executeToolTimeout(
94
+ hungToolId: string,
95
+ watchdog: ToolWatchdog,
96
+ killCtx: { claudeProcess: ChildProcess; stallCheckInterval: ReturnType<typeof setInterval> },
97
+ s: ToolTrackingState,
98
+ config: ResolvedHeadlessConfig,
99
+ prompt: string,
100
+ sessionCapture: { claudeSessionId?: string },
101
+ perfStart: number,
102
+ ): void {
103
+ const checkpoint = watchdog.buildCheckpoint(
104
+ prompt, s.ctx.accumulatedAssistantResponse, s.ctx.accumulatedThinking,
105
+ s.ctx.accumulatedToolUse, hungToolId, sessionCapture.claudeSessionId, perfStart,
106
+ );
107
+
108
+ const toolName = s.toolIdToName.get(hungToolId) || 'unknown';
109
+ const toolInput = s.toolIdToInput.get(hungToolId) || {};
110
+ const timeoutMs = watchdog.getTimeout(toolName);
111
+ const url = resolveToolUrl(toolInput);
112
+
113
+ config.outputCallback?.(
114
+ `\n[[MSTRO_TOOL_TIMEOUT]] ${toolName} timed out after ${Math.round(timeoutMs / 1000)}s${url ? ` fetching: ${url.slice(0, 100)}` : ''}. ${s.ctx.accumulatedToolUse.filter(t => t.result !== undefined).length} completed results preserved.\n`
115
+ );
116
+
117
+ if (checkpoint) {
118
+ config.onToolTimeout?.(checkpoint);
119
+ }
120
+
121
+ verboseLog(config.verbose, `[WATCHDOG] Killing process due to ${toolName} timeout`);
122
+ watchdog.clearAll();
123
+ clearInterval(killCtx.stallCheckInterval);
124
+ if (killCtx.claudeProcess.pid) killProcessGroup(killCtx.claudeProcess.pid, 'SIGTERM');
125
+ const proc = killCtx.claudeProcess;
126
+ setTimeout(() => { if (!proc.killed && proc.pid) killProcessGroup(proc.pid, 'SIGKILL'); }, 5000);
127
+ }
128
+
129
+ /** Set up tool activity tracking and watchdog */
130
+ export function setupToolTracking(
131
+ config: ResolvedHeadlessConfig,
132
+ stallState: StallState,
133
+ ctx: StreamHandlerContext,
134
+ sessionCapture: { claudeSessionId?: string },
135
+ prompt: string,
136
+ perfStart: number,
137
+ ): ToolTrackingResult {
138
+ const pendingTools = new Map<string, string>();
139
+ const counters = { lastToolInputSummary: undefined as string | undefined, totalToolCalls: 0 };
140
+
141
+ const toolWatchdogActive = config.enableToolWatchdog !== false;
142
+ const watchdog = toolWatchdogActive
143
+ ? new ToolWatchdog({
144
+ profiles: config.toolTimeoutProfiles,
145
+ verbose: config.verbose,
146
+ onTiebreaker: async (toolName, toolInput, elapsedMs, tokenSilenceMs) => {
147
+ return assessToolTimeout(toolName, toolInput, elapsedMs, config.claudeCommand, config.verbose, tokenSilenceMs);
148
+ },
149
+ getTokenSilenceMs: () => {
150
+ const last = ctx.lastTokenActivityTime;
151
+ return last > 0 ? Date.now() - last : undefined;
152
+ },
153
+ })
154
+ : null;
155
+
156
+ let killCtx: { claudeProcess: ChildProcess; stallCheckInterval: ReturnType<typeof setInterval> } | null = null;
157
+
158
+ const trackingState: ToolTrackingState = {
159
+ pendingTools, counters,
160
+ toolIdToName: new Map(), toolIdToInput: new Map(),
161
+ watchdog, stallState, ctx,
162
+ onTimeout: (hungToolId) => {
163
+ if (!watchdog || !killCtx) return;
164
+ executeToolTimeout(hungToolId, watchdog, killCtx, trackingState, config, prompt, sessionCapture, perfStart);
165
+ },
166
+ };
167
+
168
+ const origToolUseCallback = config.toolUseCallback;
169
+
170
+ config.toolUseCallback = (event) => {
171
+ if (event.type === 'tool_start' && event.toolName && event.toolId) {
172
+ onToolStart(event, trackingState);
173
+ } else if (event.type === 'tool_complete' && event.completeInput && event.toolId) {
174
+ onToolComplete(event, trackingState);
175
+ } else if (event.type === 'tool_result' && event.toolId) {
176
+ onToolResult(event, trackingState);
177
+ }
178
+ origToolUseCallback?.(event);
179
+ };
180
+
181
+ return {
182
+ pendingTools, watchdog, toolWatchdogActive, counters,
183
+ setKillContext: (claudeProcess, stallCheckInterval) => {
184
+ killCtx = { claudeProcess, stallCheckInterval };
185
+ },
186
+ };
187
+ }