mstro-app 0.4.52 → 0.5.1

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 (590) hide show
  1. package/LICENSE +129 -190
  2. package/PRIVACY.md +3 -3
  3. package/README.md +15 -6
  4. package/bin/commands/config.js +0 -1
  5. package/bin/mstro.js +1 -2
  6. package/bin/postinstall.js +0 -1
  7. package/dist/server/cli/headless/claude-invoker-process.d.ts.map +1 -1
  8. package/dist/server/cli/headless/claude-invoker-process.js +0 -1
  9. package/dist/server/cli/headless/claude-invoker-process.js.map +1 -1
  10. package/dist/server/cli/headless/claude-invoker-stall.d.ts.map +1 -1
  11. package/dist/server/cli/headless/claude-invoker-stall.js +7 -3
  12. package/dist/server/cli/headless/claude-invoker-stall.js.map +1 -1
  13. package/dist/server/cli/headless/claude-invoker-stream.d.ts.map +1 -1
  14. package/dist/server/cli/headless/claude-invoker-stream.js +0 -1
  15. package/dist/server/cli/headless/claude-invoker-stream.js.map +1 -1
  16. package/dist/server/cli/headless/claude-invoker-tools.d.ts.map +1 -1
  17. package/dist/server/cli/headless/claude-invoker-tools.js +0 -1
  18. package/dist/server/cli/headless/claude-invoker-tools.js.map +1 -1
  19. package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
  20. package/dist/server/cli/headless/claude-invoker.js +1 -2
  21. package/dist/server/cli/headless/claude-invoker.js.map +1 -1
  22. package/dist/server/cli/headless/haiku-assessments.d.ts.map +1 -1
  23. package/dist/server/cli/headless/haiku-assessments.js +0 -1
  24. package/dist/server/cli/headless/haiku-assessments.js.map +1 -1
  25. package/dist/server/cli/headless/headless-logger.d.ts.map +1 -1
  26. package/dist/server/cli/headless/headless-logger.js +0 -1
  27. package/dist/server/cli/headless/headless-logger.js.map +1 -1
  28. package/dist/server/cli/headless/index.d.ts.map +1 -1
  29. package/dist/server/cli/headless/index.js +0 -1
  30. package/dist/server/cli/headless/index.js.map +1 -1
  31. package/dist/server/cli/headless/native-timeout-detector.d.ts.map +1 -1
  32. package/dist/server/cli/headless/native-timeout-detector.js +0 -1
  33. package/dist/server/cli/headless/native-timeout-detector.js.map +1 -1
  34. package/dist/server/cli/headless/output-utils.d.ts.map +1 -1
  35. package/dist/server/cli/headless/output-utils.js +0 -1
  36. package/dist/server/cli/headless/output-utils.js.map +1 -1
  37. package/dist/server/cli/headless/prompt-utils.d.ts.map +1 -1
  38. package/dist/server/cli/headless/prompt-utils.js +0 -1
  39. package/dist/server/cli/headless/prompt-utils.js.map +1 -1
  40. package/dist/server/cli/headless/resilient-runner.d.ts.map +1 -1
  41. package/dist/server/cli/headless/resilient-runner.js +0 -1
  42. package/dist/server/cli/headless/resilient-runner.js.map +1 -1
  43. package/dist/server/cli/headless/retry-strategies.d.ts.map +1 -1
  44. package/dist/server/cli/headless/retry-strategies.js +0 -1
  45. package/dist/server/cli/headless/retry-strategies.js.map +1 -1
  46. package/dist/server/cli/headless/runner.d.ts.map +1 -1
  47. package/dist/server/cli/headless/runner.js +63 -68
  48. package/dist/server/cli/headless/runner.js.map +1 -1
  49. package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
  50. package/dist/server/cli/headless/stall-assessor.js +9 -5
  51. package/dist/server/cli/headless/stall-assessor.js.map +1 -1
  52. package/dist/server/cli/headless/tool-watchdog.d.ts.map +1 -1
  53. package/dist/server/cli/headless/tool-watchdog.js +0 -1
  54. package/dist/server/cli/headless/tool-watchdog.js.map +1 -1
  55. package/dist/server/cli/headless/types.d.ts.map +1 -1
  56. package/dist/server/cli/headless/types.js +0 -1
  57. package/dist/server/cli/headless/types.js.map +1 -1
  58. package/dist/server/cli/improvisation-attachments.d.ts.map +1 -1
  59. package/dist/server/cli/improvisation-attachments.js +0 -1
  60. package/dist/server/cli/improvisation-attachments.js.map +1 -1
  61. package/dist/server/cli/improvisation-history-store.d.ts +16 -0
  62. package/dist/server/cli/improvisation-history-store.d.ts.map +1 -0
  63. package/dist/server/cli/improvisation-history-store.js +51 -0
  64. package/dist/server/cli/improvisation-history-store.js.map +1 -0
  65. package/dist/server/cli/improvisation-movements.d.ts +31 -0
  66. package/dist/server/cli/improvisation-movements.d.ts.map +1 -0
  67. package/dist/server/cli/improvisation-movements.js +92 -0
  68. package/dist/server/cli/improvisation-movements.js.map +1 -0
  69. package/dist/server/cli/improvisation-output-queue.d.ts +13 -0
  70. package/dist/server/cli/improvisation-output-queue.d.ts.map +1 -0
  71. package/dist/server/cli/improvisation-output-queue.js +39 -0
  72. package/dist/server/cli/improvisation-output-queue.js.map +1 -0
  73. package/dist/server/cli/improvisation-retry.d.ts +21 -51
  74. package/dist/server/cli/improvisation-retry.d.ts.map +1 -1
  75. package/dist/server/cli/improvisation-retry.js +18 -434
  76. package/dist/server/cli/improvisation-retry.js.map +1 -1
  77. package/dist/server/cli/improvisation-session-manager.d.ts +10 -8
  78. package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
  79. package/dist/server/cli/improvisation-session-manager.js +53 -149
  80. package/dist/server/cli/improvisation-session-manager.js.map +1 -1
  81. package/dist/server/cli/improvisation-types.d.ts.map +1 -1
  82. package/dist/server/cli/improvisation-types.js +0 -1
  83. package/dist/server/cli/improvisation-types.js.map +1 -1
  84. package/dist/server/cli/retry/retry-best-result.d.ts +4 -0
  85. package/dist/server/cli/retry/retry-best-result.d.ts.map +1 -0
  86. package/dist/server/cli/retry/retry-best-result.js +60 -0
  87. package/dist/server/cli/retry/retry-best-result.js.map +1 -0
  88. package/dist/server/cli/retry/retry-context-loss.d.ts +6 -0
  89. package/dist/server/cli/retry/retry-context-loss.d.ts.map +1 -0
  90. package/dist/server/cli/retry/retry-context-loss.js +67 -0
  91. package/dist/server/cli/retry/retry-context-loss.js.map +1 -0
  92. package/dist/server/cli/retry/retry-premature-completion.d.ts +5 -0
  93. package/dist/server/cli/retry/retry-premature-completion.d.ts.map +1 -0
  94. package/dist/server/cli/retry/retry-premature-completion.js +80 -0
  95. package/dist/server/cli/retry/retry-premature-completion.js.map +1 -0
  96. package/dist/server/cli/retry/retry-recovery-strategies.d.ts +13 -0
  97. package/dist/server/cli/retry/retry-recovery-strategies.d.ts.map +1 -0
  98. package/dist/server/cli/retry/retry-recovery-strategies.js +165 -0
  99. package/dist/server/cli/retry/retry-recovery-strategies.js.map +1 -0
  100. package/dist/server/cli/retry/retry-resume-strategy.d.ts +12 -0
  101. package/dist/server/cli/retry/retry-resume-strategy.d.ts.map +1 -0
  102. package/dist/server/cli/retry/retry-resume-strategy.js +21 -0
  103. package/dist/server/cli/retry/retry-resume-strategy.js.map +1 -0
  104. package/dist/server/cli/retry/retry-runner-factory.d.ts +11 -0
  105. package/dist/server/cli/retry/retry-runner-factory.d.ts.map +1 -0
  106. package/dist/server/cli/retry/retry-runner-factory.js +59 -0
  107. package/dist/server/cli/retry/retry-runner-factory.js.map +1 -0
  108. package/dist/server/cli/retry/retry-tool-results.d.ts +9 -0
  109. package/dist/server/cli/retry/retry-tool-results.d.ts.map +1 -0
  110. package/dist/server/cli/retry/retry-tool-results.js +23 -0
  111. package/dist/server/cli/retry/retry-tool-results.js.map +1 -0
  112. package/dist/server/cli/retry/retry-types.d.ts +30 -0
  113. package/dist/server/cli/retry/retry-types.d.ts.map +1 -0
  114. package/dist/server/cli/retry/retry-types.js +3 -0
  115. package/dist/server/cli/retry/retry-types.js.map +1 -0
  116. package/dist/server/index.js +21 -110
  117. package/dist/server/index.js.map +1 -1
  118. package/dist/server/mcp/bouncer-cli.js +0 -1
  119. package/dist/server/mcp/bouncer-cli.js.map +1 -1
  120. package/dist/server/mcp/bouncer-haiku.d.ts.map +1 -1
  121. package/dist/server/mcp/bouncer-haiku.js +0 -1
  122. package/dist/server/mcp/bouncer-haiku.js.map +1 -1
  123. package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
  124. package/dist/server/mcp/bouncer-integration.js +0 -1
  125. package/dist/server/mcp/bouncer-integration.js.map +1 -1
  126. package/dist/server/mcp/security-analysis.d.ts.map +1 -1
  127. package/dist/server/mcp/security-analysis.js +0 -1
  128. package/dist/server/mcp/security-analysis.js.map +1 -1
  129. package/dist/server/mcp/security-audit.d.ts.map +1 -1
  130. package/dist/server/mcp/security-audit.js +0 -1
  131. package/dist/server/mcp/security-audit.js.map +1 -1
  132. package/dist/server/mcp/security-patterns.d.ts.map +1 -1
  133. package/dist/server/mcp/security-patterns.js +0 -1
  134. package/dist/server/mcp/security-patterns.js.map +1 -1
  135. package/dist/server/mcp/server.js +0 -1
  136. package/dist/server/mcp/server.js.map +1 -1
  137. package/dist/server/routes/files.d.ts.map +1 -1
  138. package/dist/server/routes/files.js +0 -1
  139. package/dist/server/routes/files.js.map +1 -1
  140. package/dist/server/routes/improvise.d.ts.map +1 -1
  141. package/dist/server/routes/improvise.js +0 -1
  142. package/dist/server/routes/improvise.js.map +1 -1
  143. package/dist/server/routes/index.d.ts.map +1 -1
  144. package/dist/server/routes/index.js +0 -1
  145. package/dist/server/routes/index.js.map +1 -1
  146. package/dist/server/routes/instances.d.ts.map +1 -1
  147. package/dist/server/routes/instances.js +0 -1
  148. package/dist/server/routes/instances.js.map +1 -1
  149. package/dist/server/routes/notifications.d.ts.map +1 -1
  150. package/dist/server/routes/notifications.js +0 -1
  151. package/dist/server/routes/notifications.js.map +1 -1
  152. package/dist/server/server-setup.d.ts +16 -1
  153. package/dist/server/server-setup.d.ts.map +1 -1
  154. package/dist/server/server-setup.js +107 -1
  155. package/dist/server/server-setup.js.map +1 -1
  156. package/dist/server/services/analytics.d.ts.map +1 -1
  157. package/dist/server/services/analytics.js +0 -1
  158. package/dist/server/services/analytics.js.map +1 -1
  159. package/dist/server/services/auth.d.ts.map +1 -1
  160. package/dist/server/services/auth.js +0 -1
  161. package/dist/server/services/auth.js.map +1 -1
  162. package/dist/server/services/client-id.d.ts.map +1 -1
  163. package/dist/server/services/client-id.js +0 -1
  164. package/dist/server/services/client-id.js.map +1 -1
  165. package/dist/server/services/file-explorer-ops.d.ts.map +1 -1
  166. package/dist/server/services/file-explorer-ops.js +0 -1
  167. package/dist/server/services/file-explorer-ops.js.map +1 -1
  168. package/dist/server/services/files.d.ts.map +1 -1
  169. package/dist/server/services/files.js +0 -1
  170. package/dist/server/services/files.js.map +1 -1
  171. package/dist/server/services/instances.d.ts.map +1 -1
  172. package/dist/server/services/instances.js +0 -1
  173. package/dist/server/services/instances.js.map +1 -1
  174. package/dist/server/services/pathUtils.d.ts.map +1 -1
  175. package/dist/server/services/pathUtils.js +0 -1
  176. package/dist/server/services/pathUtils.js.map +1 -1
  177. package/dist/server/services/plan/agent-loader.d.ts.map +1 -1
  178. package/dist/server/services/plan/agent-loader.js +0 -1
  179. package/dist/server/services/plan/agent-loader.js.map +1 -1
  180. package/dist/server/services/plan/board-config.d.ts +21 -0
  181. package/dist/server/services/plan/board-config.d.ts.map +1 -0
  182. package/dist/server/services/plan/board-config.js +111 -0
  183. package/dist/server/services/plan/board-config.js.map +1 -0
  184. package/dist/server/services/plan/composer.d.ts +1 -1
  185. package/dist/server/services/plan/composer.d.ts.map +1 -1
  186. package/dist/server/services/plan/composer.js +7 -6
  187. package/dist/server/services/plan/composer.js.map +1 -1
  188. package/dist/server/services/plan/config-installer.d.ts.map +1 -1
  189. package/dist/server/services/plan/config-installer.js +0 -1
  190. package/dist/server/services/plan/config-installer.js.map +1 -1
  191. package/dist/server/services/plan/dependency-resolver.d.ts.map +1 -1
  192. package/dist/server/services/plan/dependency-resolver.js +0 -1
  193. package/dist/server/services/plan/dependency-resolver.js.map +1 -1
  194. package/dist/server/services/plan/executor.d.ts +48 -48
  195. package/dist/server/services/plan/executor.d.ts.map +1 -1
  196. package/dist/server/services/plan/executor.js +202 -458
  197. package/dist/server/services/plan/executor.js.map +1 -1
  198. package/dist/server/services/plan/front-matter.d.ts.map +1 -1
  199. package/dist/server/services/plan/front-matter.js +0 -1
  200. package/dist/server/services/plan/front-matter.js.map +1 -1
  201. package/dist/server/services/plan/issue-classification.d.ts.map +1 -1
  202. package/dist/server/services/plan/issue-classification.js +0 -1
  203. package/dist/server/services/plan/issue-classification.js.map +1 -1
  204. package/dist/server/services/plan/issue-loader.d.ts +16 -0
  205. package/dist/server/services/plan/issue-loader.d.ts.map +1 -0
  206. package/dist/server/services/plan/issue-loader.js +45 -0
  207. package/dist/server/services/plan/issue-loader.js.map +1 -0
  208. package/dist/server/services/plan/issue-prompt-builder.d.ts.map +1 -1
  209. package/dist/server/services/plan/issue-prompt-builder.js +0 -1
  210. package/dist/server/services/plan/issue-prompt-builder.js.map +1 -1
  211. package/dist/server/services/plan/issue-retry.d.ts +3 -1
  212. package/dist/server/services/plan/issue-retry.d.ts.map +1 -1
  213. package/dist/server/services/plan/issue-retry.js +2 -1
  214. package/dist/server/services/plan/issue-retry.js.map +1 -1
  215. package/dist/server/services/plan/issue-writer.d.ts +34 -0
  216. package/dist/server/services/plan/issue-writer.d.ts.map +1 -0
  217. package/dist/server/services/plan/issue-writer.js +109 -0
  218. package/dist/server/services/plan/issue-writer.js.map +1 -0
  219. package/dist/server/services/plan/output-manager.js +2 -2
  220. package/dist/server/services/plan/output-manager.js.map +1 -1
  221. package/dist/server/services/plan/parser-core.d.ts.map +1 -1
  222. package/dist/server/services/plan/parser-core.js +0 -1
  223. package/dist/server/services/plan/parser-core.js.map +1 -1
  224. package/dist/server/services/plan/parser-migration.d.ts.map +1 -1
  225. package/dist/server/services/plan/parser-migration.js +0 -1
  226. package/dist/server/services/plan/parser-migration.js.map +1 -1
  227. package/dist/server/services/plan/parser.d.ts.map +1 -1
  228. package/dist/server/services/plan/parser.js +0 -1
  229. package/dist/server/services/plan/parser.js.map +1 -1
  230. package/dist/server/services/plan/progress-log.d.ts +11 -0
  231. package/dist/server/services/plan/progress-log.d.ts.map +1 -0
  232. package/dist/server/services/plan/progress-log.js +80 -0
  233. package/dist/server/services/plan/progress-log.js.map +1 -0
  234. package/dist/server/services/plan/prompt-builder.d.ts.map +1 -1
  235. package/dist/server/services/plan/prompt-builder.js +48 -32
  236. package/dist/server/services/plan/prompt-builder.js.map +1 -1
  237. package/dist/server/services/plan/readiness-planner.d.ts +15 -0
  238. package/dist/server/services/plan/readiness-planner.d.ts.map +1 -0
  239. package/dist/server/services/plan/readiness-planner.js +40 -0
  240. package/dist/server/services/plan/readiness-planner.js.map +1 -0
  241. package/dist/server/services/plan/review-gate.d.ts +31 -0
  242. package/dist/server/services/plan/review-gate.d.ts.map +1 -1
  243. package/dist/server/services/plan/review-gate.js +52 -3
  244. package/dist/server/services/plan/review-gate.js.map +1 -1
  245. package/dist/server/services/plan/state-reconciler.d.ts.map +1 -1
  246. package/dist/server/services/plan/state-reconciler.js +0 -1
  247. package/dist/server/services/plan/state-reconciler.js.map +1 -1
  248. package/dist/server/services/plan/types.d.ts.map +1 -1
  249. package/dist/server/services/plan/types.js +0 -1
  250. package/dist/server/services/plan/types.js.map +1 -1
  251. package/dist/server/services/plan/watcher.d.ts.map +1 -1
  252. package/dist/server/services/plan/watcher.js +0 -1
  253. package/dist/server/services/plan/watcher.js.map +1 -1
  254. package/dist/server/services/platform-credentials.d.ts.map +1 -1
  255. package/dist/server/services/platform-credentials.js +0 -1
  256. package/dist/server/services/platform-credentials.js.map +1 -1
  257. package/dist/server/services/platform-token-lifecycle.d.ts +70 -0
  258. package/dist/server/services/platform-token-lifecycle.d.ts.map +1 -0
  259. package/dist/server/services/platform-token-lifecycle.js +156 -0
  260. package/dist/server/services/platform-token-lifecycle.js.map +1 -0
  261. package/dist/server/services/platform.d.ts +25 -4
  262. package/dist/server/services/platform.d.ts.map +1 -1
  263. package/dist/server/services/platform.js +150 -92
  264. package/dist/server/services/platform.js.map +1 -1
  265. package/dist/server/services/sentry.d.ts.map +1 -1
  266. package/dist/server/services/sentry.js +0 -1
  267. package/dist/server/services/sentry.js.map +1 -1
  268. package/dist/server/services/settings.d.ts.map +1 -1
  269. package/dist/server/services/settings.js +0 -1
  270. package/dist/server/services/settings.js.map +1 -1
  271. package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
  272. package/dist/server/services/terminal/pty-manager.js +0 -1
  273. package/dist/server/services/terminal/pty-manager.js.map +1 -1
  274. package/dist/server/services/terminal/pty-utils.d.ts.map +1 -1
  275. package/dist/server/services/terminal/pty-utils.js +0 -1
  276. package/dist/server/services/terminal/pty-utils.js.map +1 -1
  277. package/dist/server/services/websocket/autocomplete.d.ts.map +1 -1
  278. package/dist/server/services/websocket/autocomplete.js +0 -1
  279. package/dist/server/services/websocket/autocomplete.js.map +1 -1
  280. package/dist/server/services/websocket/file-definition-handlers.d.ts.map +1 -1
  281. package/dist/server/services/websocket/file-definition-handlers.js +0 -1
  282. package/dist/server/services/websocket/file-definition-handlers.js.map +1 -1
  283. package/dist/server/services/websocket/file-download-handler.d.ts +17 -0
  284. package/dist/server/services/websocket/file-download-handler.d.ts.map +1 -0
  285. package/dist/server/services/websocket/file-download-handler.js +164 -0
  286. package/dist/server/services/websocket/file-download-handler.js.map +1 -0
  287. package/dist/server/services/websocket/file-explorer-handlers.d.ts.map +1 -1
  288. package/dist/server/services/websocket/file-explorer-handlers.js +0 -1
  289. package/dist/server/services/websocket/file-explorer-handlers.js.map +1 -1
  290. package/dist/server/services/websocket/file-search-handlers.d.ts.map +1 -1
  291. package/dist/server/services/websocket/file-search-handlers.js +0 -1
  292. package/dist/server/services/websocket/file-search-handlers.js.map +1 -1
  293. package/dist/server/services/websocket/file-upload-handler.d.ts +2 -3
  294. package/dist/server/services/websocket/file-upload-handler.d.ts.map +1 -1
  295. package/dist/server/services/websocket/file-upload-handler.js +4 -7
  296. package/dist/server/services/websocket/file-upload-handler.js.map +1 -1
  297. package/dist/server/services/websocket/file-utils.d.ts.map +1 -1
  298. package/dist/server/services/websocket/file-utils.js +0 -1
  299. package/dist/server/services/websocket/file-utils.js.map +1 -1
  300. package/dist/server/services/websocket/git-branch-handlers.d.ts.map +1 -1
  301. package/dist/server/services/websocket/git-branch-handlers.js +0 -1
  302. package/dist/server/services/websocket/git-branch-handlers.js.map +1 -1
  303. package/dist/server/services/websocket/git-diff-handlers.d.ts.map +1 -1
  304. package/dist/server/services/websocket/git-diff-handlers.js +0 -1
  305. package/dist/server/services/websocket/git-diff-handlers.js.map +1 -1
  306. package/dist/server/services/websocket/git-handlers.d.ts.map +1 -1
  307. package/dist/server/services/websocket/git-handlers.js +58 -6
  308. package/dist/server/services/websocket/git-handlers.js.map +1 -1
  309. package/dist/server/services/websocket/git-head-watcher.d.ts.map +1 -1
  310. package/dist/server/services/websocket/git-head-watcher.js +0 -1
  311. package/dist/server/services/websocket/git-head-watcher.js.map +1 -1
  312. package/dist/server/services/websocket/git-log-handlers.d.ts.map +1 -1
  313. package/dist/server/services/websocket/git-log-handlers.js +0 -1
  314. package/dist/server/services/websocket/git-log-handlers.js.map +1 -1
  315. package/dist/server/services/websocket/git-pr-handlers.d.ts.map +1 -1
  316. package/dist/server/services/websocket/git-pr-handlers.js +0 -1
  317. package/dist/server/services/websocket/git-pr-handlers.js.map +1 -1
  318. package/dist/server/services/websocket/git-tag-handlers.d.ts.map +1 -1
  319. package/dist/server/services/websocket/git-tag-handlers.js +0 -1
  320. package/dist/server/services/websocket/git-tag-handlers.js.map +1 -1
  321. package/dist/server/services/websocket/git-utils.d.ts +18 -3
  322. package/dist/server/services/websocket/git-utils.d.ts.map +1 -1
  323. package/dist/server/services/websocket/git-utils.js +58 -8
  324. package/dist/server/services/websocket/git-utils.js.map +1 -1
  325. package/dist/server/services/websocket/git-worktree-handlers.d.ts.map +1 -1
  326. package/dist/server/services/websocket/git-worktree-handlers.js +258 -16
  327. package/dist/server/services/websocket/git-worktree-handlers.js.map +1 -1
  328. package/dist/server/services/websocket/handler-context.d.ts +15 -0
  329. package/dist/server/services/websocket/handler-context.d.ts.map +1 -1
  330. package/dist/server/services/websocket/handler-context.js +0 -1
  331. package/dist/server/services/websocket/handler-context.js.map +1 -1
  332. package/dist/server/services/websocket/handler.d.ts +7 -0
  333. package/dist/server/services/websocket/handler.d.ts.map +1 -1
  334. package/dist/server/services/websocket/handler.js +76 -15
  335. package/dist/server/services/websocket/handler.js.map +1 -1
  336. package/dist/server/services/websocket/index.d.ts.map +1 -1
  337. package/dist/server/services/websocket/index.js +0 -1
  338. package/dist/server/services/websocket/index.js.map +1 -1
  339. package/dist/server/services/websocket/msg-id-tracker.d.ts +21 -0
  340. package/dist/server/services/websocket/msg-id-tracker.d.ts.map +1 -0
  341. package/dist/server/services/websocket/msg-id-tracker.js +76 -0
  342. package/dist/server/services/websocket/msg-id-tracker.js.map +1 -0
  343. package/dist/server/services/websocket/plan-board-handlers.d.ts.map +1 -1
  344. package/dist/server/services/websocket/plan-board-handlers.js +0 -1
  345. package/dist/server/services/websocket/plan-board-handlers.js.map +1 -1
  346. package/dist/server/services/websocket/plan-execution-handlers.d.ts.map +1 -1
  347. package/dist/server/services/websocket/plan-execution-handlers.js +6 -2
  348. package/dist/server/services/websocket/plan-execution-handlers.js.map +1 -1
  349. package/dist/server/services/websocket/plan-handlers.d.ts.map +1 -1
  350. package/dist/server/services/websocket/plan-handlers.js +0 -1
  351. package/dist/server/services/websocket/plan-handlers.js.map +1 -1
  352. package/dist/server/services/websocket/plan-helpers.d.ts.map +1 -1
  353. package/dist/server/services/websocket/plan-helpers.js +0 -1
  354. package/dist/server/services/websocket/plan-helpers.js.map +1 -1
  355. package/dist/server/services/websocket/plan-issue-handlers.d.ts.map +1 -1
  356. package/dist/server/services/websocket/plan-issue-handlers.js +0 -1
  357. package/dist/server/services/websocket/plan-issue-handlers.js.map +1 -1
  358. package/dist/server/services/websocket/plan-sprint-handlers.d.ts.map +1 -1
  359. package/dist/server/services/websocket/plan-sprint-handlers.js +0 -1
  360. package/dist/server/services/websocket/plan-sprint-handlers.js.map +1 -1
  361. package/dist/server/services/websocket/quality-complexity.d.ts.map +1 -1
  362. package/dist/server/services/websocket/quality-complexity.js +0 -1
  363. package/dist/server/services/websocket/quality-complexity.js.map +1 -1
  364. package/dist/server/services/websocket/quality-grading.d.ts +46 -0
  365. package/dist/server/services/websocket/quality-grading.d.ts.map +1 -0
  366. package/dist/server/services/websocket/quality-grading.js +482 -0
  367. package/dist/server/services/websocket/quality-grading.js.map +1 -0
  368. package/dist/server/services/websocket/quality-handlers.d.ts.map +1 -1
  369. package/dist/server/services/websocket/quality-handlers.js +15 -4
  370. package/dist/server/services/websocket/quality-handlers.js.map +1 -1
  371. package/dist/server/services/websocket/quality-linting.d.ts.map +1 -1
  372. package/dist/server/services/websocket/quality-linting.js +0 -1
  373. package/dist/server/services/websocket/quality-linting.js.map +1 -1
  374. package/dist/server/services/websocket/quality-persistence.d.ts +14 -0
  375. package/dist/server/services/websocket/quality-persistence.d.ts.map +1 -1
  376. package/dist/server/services/websocket/quality-persistence.js +28 -12
  377. package/dist/server/services/websocket/quality-persistence.js.map +1 -1
  378. package/dist/server/services/websocket/quality-review-agent.d.ts.map +1 -1
  379. package/dist/server/services/websocket/quality-review-agent.js +2 -3
  380. package/dist/server/services/websocket/quality-review-agent.js.map +1 -1
  381. package/dist/server/services/websocket/quality-service.d.ts +3 -1
  382. package/dist/server/services/websocket/quality-service.d.ts.map +1 -1
  383. package/dist/server/services/websocket/quality-service.js +53 -58
  384. package/dist/server/services/websocket/quality-service.js.map +1 -1
  385. package/dist/server/services/websocket/quality-tools.d.ts +1 -1
  386. package/dist/server/services/websocket/quality-tools.d.ts.map +1 -1
  387. package/dist/server/services/websocket/quality-tools.js +6 -3
  388. package/dist/server/services/websocket/quality-tools.js.map +1 -1
  389. package/dist/server/services/websocket/quality-types.d.ts +18 -2
  390. package/dist/server/services/websocket/quality-types.d.ts.map +1 -1
  391. package/dist/server/services/websocket/quality-types.js +0 -1
  392. package/dist/server/services/websocket/quality-types.js.map +1 -1
  393. package/dist/server/services/websocket/session-handlers.d.ts +48 -2
  394. package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
  395. package/dist/server/services/websocket/session-handlers.js +204 -66
  396. package/dist/server/services/websocket/session-handlers.js.map +1 -1
  397. package/dist/server/services/websocket/session-history.d.ts.map +1 -1
  398. package/dist/server/services/websocket/session-history.js +0 -1
  399. package/dist/server/services/websocket/session-history.js.map +1 -1
  400. package/dist/server/services/websocket/session-initialization.d.ts +2 -2
  401. package/dist/server/services/websocket/session-initialization.d.ts.map +1 -1
  402. package/dist/server/services/websocket/session-initialization.js +75 -18
  403. package/dist/server/services/websocket/session-initialization.js.map +1 -1
  404. package/dist/server/services/websocket/session-registry.d.ts +29 -1
  405. package/dist/server/services/websocket/session-registry.d.ts.map +1 -1
  406. package/dist/server/services/websocket/session-registry.js +53 -5
  407. package/dist/server/services/websocket/session-registry.js.map +1 -1
  408. package/dist/server/services/websocket/settings-handlers.d.ts.map +1 -1
  409. package/dist/server/services/websocket/settings-handlers.js +0 -1
  410. package/dist/server/services/websocket/settings-handlers.js.map +1 -1
  411. package/dist/server/services/websocket/skill-handlers.d.ts.map +1 -1
  412. package/dist/server/services/websocket/skill-handlers.js +0 -1
  413. package/dist/server/services/websocket/skill-handlers.js.map +1 -1
  414. package/dist/server/services/websocket/skill-watcher.d.ts.map +1 -1
  415. package/dist/server/services/websocket/skill-watcher.js +0 -1
  416. package/dist/server/services/websocket/skill-watcher.js.map +1 -1
  417. package/dist/server/services/websocket/tab-broadcast.d.ts +24 -0
  418. package/dist/server/services/websocket/tab-broadcast.d.ts.map +1 -0
  419. package/dist/server/services/websocket/tab-broadcast.js +12 -0
  420. package/dist/server/services/websocket/tab-broadcast.js.map +1 -0
  421. package/dist/server/services/websocket/tab-event-buffer.d.ts +103 -0
  422. package/dist/server/services/websocket/tab-event-buffer.d.ts.map +1 -0
  423. package/dist/server/services/websocket/tab-event-buffer.js +106 -0
  424. package/dist/server/services/websocket/tab-event-buffer.js.map +1 -0
  425. package/dist/server/services/websocket/tab-event-replay.d.ts +20 -0
  426. package/dist/server/services/websocket/tab-event-replay.d.ts.map +1 -0
  427. package/dist/server/services/websocket/tab-event-replay.js +20 -0
  428. package/dist/server/services/websocket/tab-event-replay.js.map +1 -0
  429. package/dist/server/services/websocket/tab-handlers.d.ts +0 -1
  430. package/dist/server/services/websocket/tab-handlers.d.ts.map +1 -1
  431. package/dist/server/services/websocket/tab-handlers.js +2 -10
  432. package/dist/server/services/websocket/tab-handlers.js.map +1 -1
  433. package/dist/server/services/websocket/terminal-handlers.d.ts.map +1 -1
  434. package/dist/server/services/websocket/terminal-handlers.js +39 -4
  435. package/dist/server/services/websocket/terminal-handlers.js.map +1 -1
  436. package/dist/server/services/websocket/types.d.ts +17 -8
  437. package/dist/server/services/websocket/types.d.ts.map +1 -1
  438. package/dist/server/services/websocket/types.js +8 -7
  439. package/dist/server/services/websocket/types.js.map +1 -1
  440. package/dist/server/utils/agent-manager.d.ts.map +1 -1
  441. package/dist/server/utils/agent-manager.js +0 -1
  442. package/dist/server/utils/agent-manager.js.map +1 -1
  443. package/dist/server/utils/paths.d.ts.map +1 -1
  444. package/dist/server/utils/paths.js +0 -1
  445. package/dist/server/utils/paths.js.map +1 -1
  446. package/dist/server/utils/port-manager.d.ts.map +1 -1
  447. package/dist/server/utils/port-manager.js +0 -1
  448. package/dist/server/utils/port-manager.js.map +1 -1
  449. package/dist/server/utils/port.d.ts.map +1 -1
  450. package/dist/server/utils/port.js +0 -1
  451. package/dist/server/utils/port.js.map +1 -1
  452. package/package.json +2 -2
  453. package/server/README.md +1 -1
  454. package/server/cli/headless/claude-invoker-process.ts +0 -1
  455. package/server/cli/headless/claude-invoker-stall.ts +7 -3
  456. package/server/cli/headless/claude-invoker-stream.ts +0 -1
  457. package/server/cli/headless/claude-invoker-tools.ts +0 -1
  458. package/server/cli/headless/claude-invoker.ts +1 -2
  459. package/server/cli/headless/haiku-assessments.ts +0 -1
  460. package/server/cli/headless/headless-logger.ts +0 -1
  461. package/server/cli/headless/index.ts +0 -1
  462. package/server/cli/headless/native-timeout-detector.ts +0 -1
  463. package/server/cli/headless/output-utils.ts +0 -1
  464. package/server/cli/headless/prompt-utils.ts +0 -1
  465. package/server/cli/headless/resilient-runner.ts +0 -1
  466. package/server/cli/headless/retry-strategies.ts +0 -1
  467. package/server/cli/headless/runner.ts +67 -73
  468. package/server/cli/headless/stall-assessor.ts +9 -5
  469. package/server/cli/headless/tool-watchdog.ts +0 -1
  470. package/server/cli/headless/types.ts +1 -2
  471. package/server/cli/improvisation-attachments.ts +0 -1
  472. package/server/cli/improvisation-history-store.ts +61 -0
  473. package/server/cli/improvisation-movements.ts +119 -0
  474. package/server/cli/improvisation-output-queue.ts +41 -0
  475. package/server/cli/improvisation-retry.ts +25 -601
  476. package/server/cli/improvisation-session-manager.ts +74 -161
  477. package/server/cli/improvisation-types.ts +0 -1
  478. package/server/cli/retry/retry-best-result.ts +69 -0
  479. package/server/cli/retry/retry-context-loss.ts +86 -0
  480. package/server/cli/retry/retry-premature-completion.ts +112 -0
  481. package/server/cli/retry/retry-recovery-strategies.ts +246 -0
  482. package/server/cli/retry/retry-resume-strategy.ts +32 -0
  483. package/server/cli/retry/retry-runner-factory.ts +69 -0
  484. package/server/cli/retry/retry-tool-results.ts +30 -0
  485. package/server/cli/retry/retry-types.ts +31 -0
  486. package/server/index.ts +37 -124
  487. package/server/mcp/bouncer-cli.ts +0 -1
  488. package/server/mcp/bouncer-haiku.ts +0 -1
  489. package/server/mcp/bouncer-integration.ts +0 -1
  490. package/server/mcp/security-analysis.ts +0 -1
  491. package/server/mcp/security-audit.ts +0 -1
  492. package/server/mcp/security-patterns.ts +0 -1
  493. package/server/mcp/server.ts +0 -1
  494. package/server/routes/files.ts +0 -1
  495. package/server/routes/improvise.ts +0 -1
  496. package/server/routes/index.ts +0 -1
  497. package/server/routes/instances.ts +0 -1
  498. package/server/routes/notifications.ts +0 -1
  499. package/server/server-setup.ts +126 -2
  500. package/server/services/analytics.ts +0 -1
  501. package/server/services/auth.ts +0 -1
  502. package/server/services/client-id.ts +0 -1
  503. package/server/services/file-explorer-ops.ts +0 -1
  504. package/server/services/files.ts +0 -1
  505. package/server/services/instances.ts +0 -1
  506. package/server/services/pathUtils.ts +0 -1
  507. package/server/services/plan/agent-loader.ts +0 -1
  508. package/server/services/plan/agents/assess-stall.md +11 -4
  509. package/server/services/plan/agents/code-review.md +13 -11
  510. package/server/services/plan/board-config.ts +121 -0
  511. package/server/services/plan/composer.ts +7 -6
  512. package/server/services/plan/config-installer.ts +0 -1
  513. package/server/services/plan/dependency-resolver.ts +0 -1
  514. package/server/services/plan/executor.ts +259 -470
  515. package/server/services/plan/front-matter.ts +0 -1
  516. package/server/services/plan/issue-classification.ts +0 -1
  517. package/server/services/plan/issue-loader.ts +63 -0
  518. package/server/services/plan/issue-prompt-builder.ts +0 -1
  519. package/server/services/plan/issue-retry.ts +5 -2
  520. package/server/services/plan/issue-writer.ts +136 -0
  521. package/server/services/plan/output-manager.ts +2 -2
  522. package/server/services/plan/parser-core.ts +0 -1
  523. package/server/services/plan/parser-migration.ts +0 -1
  524. package/server/services/plan/parser.ts +0 -1
  525. package/server/services/plan/progress-log.ts +91 -0
  526. package/server/services/plan/prompt-builder.ts +73 -36
  527. package/server/services/plan/readiness-planner.ts +49 -0
  528. package/server/services/plan/review-gate.ts +102 -3
  529. package/server/services/plan/state-reconciler.ts +0 -1
  530. package/server/services/plan/types.ts +0 -1
  531. package/server/services/plan/watcher.ts +0 -1
  532. package/server/services/platform-credentials.ts +0 -1
  533. package/server/services/platform-token-lifecycle.ts +171 -0
  534. package/server/services/platform.ts +168 -105
  535. package/server/services/sentry.ts +0 -1
  536. package/server/services/settings.ts +0 -1
  537. package/server/services/terminal/pty-manager.ts +0 -1
  538. package/server/services/terminal/pty-utils.ts +0 -1
  539. package/server/services/websocket/autocomplete.ts +0 -1
  540. package/server/services/websocket/file-definition-handlers.ts +0 -1
  541. package/server/services/websocket/file-download-handler.ts +190 -0
  542. package/server/services/websocket/file-explorer-handlers.ts +0 -1
  543. package/server/services/websocket/file-search-handlers.ts +0 -1
  544. package/server/services/websocket/file-upload-handler.ts +6 -5
  545. package/server/services/websocket/file-utils.ts +0 -1
  546. package/server/services/websocket/git-branch-handlers.ts +0 -1
  547. package/server/services/websocket/git-diff-handlers.ts +0 -1
  548. package/server/services/websocket/git-handlers.ts +66 -10
  549. package/server/services/websocket/git-head-watcher.ts +0 -1
  550. package/server/services/websocket/git-log-handlers.ts +0 -1
  551. package/server/services/websocket/git-pr-handlers.ts +0 -1
  552. package/server/services/websocket/git-tag-handlers.ts +0 -1
  553. package/server/services/websocket/git-utils.ts +69 -9
  554. package/server/services/websocket/git-worktree-handlers.ts +289 -19
  555. package/server/services/websocket/handler-context.ts +15 -1
  556. package/server/services/websocket/handler.ts +79 -16
  557. package/server/services/websocket/index.ts +0 -1
  558. package/server/services/websocket/msg-id-tracker.ts +83 -0
  559. package/server/services/websocket/plan-board-handlers.ts +0 -1
  560. package/server/services/websocket/plan-execution-handlers.ts +6 -2
  561. package/server/services/websocket/plan-handlers.ts +0 -1
  562. package/server/services/websocket/plan-helpers.ts +0 -1
  563. package/server/services/websocket/plan-issue-handlers.ts +0 -1
  564. package/server/services/websocket/plan-sprint-handlers.ts +0 -1
  565. package/server/services/websocket/quality-complexity.ts +0 -1
  566. package/server/services/websocket/quality-grading.ts +611 -0
  567. package/server/services/websocket/quality-handlers.ts +16 -4
  568. package/server/services/websocket/quality-linting.ts +0 -1
  569. package/server/services/websocket/quality-persistence.ts +30 -8
  570. package/server/services/websocket/quality-review-agent.ts +2 -3
  571. package/server/services/websocket/quality-service.ts +54 -55
  572. package/server/services/websocket/quality-tools.ts +11 -3
  573. package/server/services/websocket/quality-types.ts +21 -3
  574. package/server/services/websocket/session-handlers.ts +213 -69
  575. package/server/services/websocket/session-history.ts +0 -1
  576. package/server/services/websocket/session-initialization.ts +83 -20
  577. package/server/services/websocket/session-registry.ts +61 -5
  578. package/server/services/websocket/settings-handlers.ts +0 -1
  579. package/server/services/websocket/skill-handlers.ts +0 -1
  580. package/server/services/websocket/skill-watcher.ts +0 -1
  581. package/server/services/websocket/tab-broadcast.ts +37 -0
  582. package/server/services/websocket/tab-event-buffer.ts +158 -0
  583. package/server/services/websocket/tab-event-replay.ts +41 -0
  584. package/server/services/websocket/tab-handlers.ts +2 -10
  585. package/server/services/websocket/terminal-handlers.ts +39 -3
  586. package/server/services/websocket/types.ts +19 -7
  587. package/server/utils/agent-manager.ts +0 -1
  588. package/server/utils/paths.ts +0 -1
  589. package/server/utils/port-manager.ts +0 -1
  590. package/server/utils/port.ts +0 -1
@@ -1,5 +1,4 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
- // Licensed under the MIT License. See LICENSE file for details.
3
2
 
4
3
  /**
5
4
  * Review Gate — AI-powered quality gate for completed issues.
@@ -13,16 +12,19 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from
13
12
  import { join } from 'node:path';
14
13
  import { ResilientRunner } from '../../cli/headless/resilient-runner.js';
15
14
  import { loadAgentPrompt } from './agent-loader.js';
15
+ import { getBoardReviewCriteria, resolveActiveBoardId } from './board-config.js';
16
16
  import { resolveIsCodeTask } from './issue-classification.js';
17
+ import { appendCancellationNote, type WarnFn } from './issue-writer.js';
18
+ import { resolveOutputPath } from './output-manager.js';
17
19
  import type { Issue, ReviewCheck, ReviewResult } from './types.js';
18
20
 
19
21
  /** Max review attempts per issue per sprint before giving up */
20
22
  export const MAX_REVIEW_ATTEMPTS = 3;
21
23
 
22
- /** Review runner stall timeouts (ms) */
24
+ /** Review runner stall timeouts (ms) — hard cap is a backstop that only fires after stall signals flag the run */
23
25
  const REVIEW_STALL_WARNING_MS = 300_000; // 5 min
24
26
  const REVIEW_STALL_KILL_MS = 600_000; // 10 min
25
- const REVIEW_STALL_HARD_CAP_MS = 900_000; // 15 min
27
+ const REVIEW_STALL_HARD_CAP_MS = 2_700_000; // 45 min backstop
26
28
 
27
29
  export interface ReviewIssueOptions {
28
30
  workingDir: string;
@@ -244,3 +246,100 @@ function buildReviewPrompt(
244
246
  return `You are a reviewer. Review the work done for issue ${issue.id}: ${issue.title}.\n\n## Acceptance Criteria\n${criteriaStr}\n\nOutput EXACTLY one JSON object on its own line (no markdown fencing):\n{"passed": true, "checks": [{"name": "criteria_met", "passed": true, "details": "..."}]}`;
245
247
  }
246
248
 
249
+ // ── Review pipeline orchestration ───────────────────────────
250
+
251
+ /** Status messages emitted during the review pipeline. */
252
+ export type ReviewProgressStatus = 'reviewing' | 'passed' | 'failed' | 'max_attempts';
253
+
254
+ /** Callbacks for the executor to observe review pipeline events. */
255
+ export interface ReviewPipelineCallbacks {
256
+ /** Update an issue's front-matter status on disk. */
257
+ setStatus: (issuePath: string, status: string) => Promise<void>;
258
+ onOutput: (issueId: string, text: string) => void;
259
+ onReviewProgress: (issueId: string, status: ReviewProgressStatus) => void;
260
+ onIssueAbandoned: (issueId: string, reason: string, attempts: number) => void;
261
+ onIssueCompleted: (issue: Issue) => void;
262
+ onIssueError: (issueId: string, error: string) => void;
263
+ warn: WarnFn;
264
+ }
265
+
266
+ /** Configuration for running the full review pipeline for a single issue. */
267
+ export interface ReviewPipelineOptions {
268
+ issue: Issue;
269
+ pmDir: string;
270
+ workingDir: string;
271
+ executionDir: string | null;
272
+ boardDir: string | null;
273
+ boardId: string | null;
274
+ extraEnv?: Record<string, string>;
275
+ }
276
+
277
+ /**
278
+ * Full review pipeline for a completed issue: attempt-count guard, status
279
+ * transitions, AI review, persistence, and event emission through the
280
+ * caller's callbacks. Returns `true` when the review passes and the issue is
281
+ * marked `done`, `false` otherwise (reverted, cancelled, or errored).
282
+ */
283
+ export async function runReviewPipeline(
284
+ options: ReviewPipelineOptions,
285
+ callbacks: ReviewPipelineCallbacks,
286
+ ): Promise<boolean> {
287
+ const { issue, pmDir, workingDir, executionDir, boardDir, boardId, extraEnv } = options;
288
+ const reviewDir = boardDir ?? pmDir;
289
+ const attempts = getReviewAttemptCount(reviewDir, issue);
290
+
291
+ if (attempts >= MAX_REVIEW_ATTEMPTS) {
292
+ await callbacks.setStatus(issue.path, 'cancelled');
293
+ await appendCancellationNote(
294
+ pmDir,
295
+ issue,
296
+ `Cancelled after ${MAX_REVIEW_ATTEMPTS} failed reviews — issue may need restructuring`,
297
+ callbacks.warn,
298
+ );
299
+ callbacks.onReviewProgress(issue.id, 'max_attempts');
300
+ callbacks.onIssueAbandoned(
301
+ issue.id,
302
+ `Review failed ${MAX_REVIEW_ATTEMPTS} times — cancelled to unblock dependents`,
303
+ attempts,
304
+ );
305
+ callbacks.onOutput(issue.id, 'Review: max attempts reached, cancelling issue to unblock dependents');
306
+ return false;
307
+ }
308
+
309
+ await callbacks.setStatus(issue.path, 'in_review');
310
+ callbacks.onReviewProgress(issue.id, 'reviewing');
311
+
312
+ const outputPath = resolveOutputPath(issue, workingDir, boardDir);
313
+ const effectiveBoardId = boardId ?? resolveActiveBoardId(pmDir);
314
+ const reviewCriteria = await getBoardReviewCriteria(pmDir, effectiveBoardId, callbacks.warn);
315
+
316
+ const result = await reviewIssue({
317
+ workingDir: executionDir || workingDir,
318
+ issue,
319
+ pmDir,
320
+ outputPath,
321
+ onOutput: (text) => callbacks.onOutput(issue.id, text),
322
+ logDir: boardDir ? join(boardDir, 'logs') : undefined,
323
+ reviewCriteria,
324
+ boardDir,
325
+ extraEnv,
326
+ });
327
+ persistReviewResult(reviewDir, issue, result);
328
+
329
+ if (result.passed) {
330
+ await callbacks.setStatus(issue.path, 'done');
331
+ callbacks.onReviewProgress(issue.id, 'passed');
332
+ callbacks.onIssueCompleted(issue);
333
+ return true;
334
+ }
335
+
336
+ await callbacks.setStatus(issue.path, 'todo');
337
+ appendReviewFeedback(pmDir, issue, result);
338
+ callbacks.onReviewProgress(issue.id, 'failed');
339
+ callbacks.onIssueError(
340
+ issue.id,
341
+ `Review failed: ${result.checks.filter(c => !c.passed).map(c => c.name).join(', ')}`,
342
+ );
343
+ return false;
344
+ }
345
+
@@ -1,5 +1,4 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
- // Licensed under the MIT License. See LICENSE file for details.
3
2
 
4
3
  /**
5
4
  * State Reconciler — Recomputes STATE.md from individual issue files.
@@ -1,5 +1,4 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
- // Licensed under the MIT License. See LICENSE file for details.
3
2
 
4
3
  /**
5
4
  * Plan Types — Project Plan Spec (PPS) data structures
@@ -1,5 +1,4 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
- // Licensed under the MIT License. See LICENSE file for details.
3
2
 
4
3
  /**
5
4
  * Plan Watcher — Watches .mstro/pm/ directory for changes and broadcasts updates.
@@ -1,5 +1,4 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
- // Licensed under the MIT License. See LICENSE file for details.
3
2
 
4
3
  /**
5
4
  * Platform Credentials — Token management for device authentication.
@@ -0,0 +1,171 @@
1
+ // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
+
3
+ /**
4
+ * Token Lifecycle — refresh, verify, and auth-expired signaling for the
5
+ * platform device token. Extracted from PlatformConnection so the relay
6
+ * lifecycle (sockets, reconnect, heartbeat) and the auth lifecycle (token
7
+ * refresh, verify, expired-signal) can be reasoned about apart. The token
8
+ * lifecycle has its own clock (24h periodic check), its own remote endpoints
9
+ * (/refresh, /verify), and its own failure modes (revocation vs. transient
10
+ * network error) — none of which need to be in PlatformConnection.
11
+ */
12
+
13
+ import {
14
+ getCredentials,
15
+ shouldRefreshToken,
16
+ updateCredentials,
17
+ } from './platform-credentials.js'
18
+
19
+ const PERIODIC_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000
20
+
21
+ export interface TokenLifecycleDeps {
22
+ /** Platform base URL — used to compose /api/auth/device/{refresh,verify} */
23
+ platformUrl: string
24
+ /** Send a message upstream over the live relay socket (no-op if closed). */
25
+ send: (message: unknown) => void
26
+ /** Read the current relay connection ID at the moment auth-expired fires. */
27
+ getConnectionId: () => string | null
28
+ }
29
+
30
+ export class TokenLifecycle {
31
+ private intervalHandle: ReturnType<typeof setInterval> | null = null
32
+
33
+ constructor(private readonly deps: TokenLifecycleDeps) {}
34
+
35
+ /**
36
+ * Refresh the device token if it's older than the refresh interval.
37
+ * Returns `true` if the token is (still) valid after this call, `false`
38
+ * if refresh was attempted and rejected with an auth error — in which
39
+ * case the caller should surface an auth-expired signal to the web
40
+ * rather than silently reusing a dead token.
41
+ */
42
+ async maybeRefresh(): Promise<boolean> {
43
+ const creds = getCredentials()
44
+ if (!creds) return false
45
+ if (!shouldRefreshToken(creds)) return true
46
+
47
+ try {
48
+ const response = await fetch(`${this.deps.platformUrl}/api/auth/device/refresh`, {
49
+ method: 'POST',
50
+ headers: {
51
+ 'Authorization': `Bearer ${creds.token}`,
52
+ 'Content-Type': 'application/json'
53
+ }
54
+ })
55
+
56
+ if (response.ok) {
57
+ const data = await response.json() as { accessToken: string }
58
+ updateCredentials({
59
+ token: data.accessToken,
60
+ lastRefreshedAt: new Date().toISOString()
61
+ })
62
+ return true
63
+ }
64
+ if (response.status === 401 || response.status === 403) {
65
+ console.warn(`[Platform] Token refresh failed — auth is expired (${response.status}). Run \`mstro login --force\`.`)
66
+ this.notifyAuthExpired()
67
+ return false
68
+ }
69
+ console.warn(`[Platform] Token refresh failed with status ${response.status}, will retry later`)
70
+ return true
71
+ } catch (err) {
72
+ console.warn('[Platform] Token refresh error:', err)
73
+ return true
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Verify the current token against the platform. A rejection (401/403)
79
+ * means the token is permanently invalid (revoked, signing-key rotation,
80
+ * account deleted); the caller should stop looping reconnects and tell
81
+ * the user to run `mstro login --force`.
82
+ *
83
+ * Returns `true` when the token is valid or the verification endpoint
84
+ * is unreachable (we prefer false negatives to false positives — a
85
+ * network blip shouldn't force a re-login).
86
+ */
87
+ async verify(): Promise<boolean> {
88
+ const creds = getCredentials()
89
+ if (!creds?.token) return false
90
+ try {
91
+ const response = await fetch(`${this.deps.platformUrl}/api/auth/device/verify`, {
92
+ method: 'POST',
93
+ headers: { 'Authorization': `Bearer ${creds.token}` },
94
+ })
95
+ if (response.status === 401 || response.status === 403) {
96
+ console.warn(`[Platform] Token verify rejected (${response.status}) — auth is expired.`)
97
+ return false
98
+ }
99
+ return true
100
+ } catch {
101
+ // Network error: treat as "probably valid" so a flaky connection
102
+ // doesn't force users to re-login. The WebSocket open itself will
103
+ // catch a truly bad token via the 4001 path.
104
+ return true
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Surface an auth-expired condition to any paired web clients.
110
+ *
111
+ * Two cooperating paths deliver this signal — either alone is enough,
112
+ * both together cover every timing edge:
113
+ *
114
+ * 1. **CLI-initiated (this method):** we detected a 401 from the
115
+ * `/refresh` or `/verify` endpoint *while the relay socket is
116
+ * still open*. The injected `send` pushes the message upstream so
117
+ * the server relays it to paired webs before the CLI closes.
118
+ * A no-op if the socket is already closed.
119
+ *
120
+ * 2. **Server-initiated:** when the platform closes a CLI socket
121
+ * with 4001 or 4008, `handleAuthClose` in `clientHandlers.ts`
122
+ * broadcasts the same `clientAuthExpired` to paired webs. This
123
+ * covers cases where the CLI never had a chance to detect the
124
+ * rejection itself (e.g. token revoked while the socket was idle,
125
+ * server-side token rotation).
126
+ *
127
+ * IMPORTANT: never route this through PlatformConnection's
128
+ * `onRelayedMessage` callback — that callback feeds INCOMING web→CLI
129
+ * requests into the local handler, which would treat
130
+ * `clientAuthExpired` as an unknown inbound request.
131
+ */
132
+ notifyAuthExpired(): void {
133
+ this.deps.send({
134
+ type: 'clientAuthExpired',
135
+ data: {
136
+ connectionId: this.deps.getConnectionId(),
137
+ message: 'The CLI\'s device token is invalid — run `mstro login --force` on the machine.',
138
+ },
139
+ })
140
+ }
141
+
142
+ /**
143
+ * Best-effort token verification, fired in parallel with the socket
144
+ * open so a slow verify endpoint never delays reconnect.
145
+ *
146
+ * Only runs when the token is stale enough that we'd be about to
147
+ * refresh anyway — keeps the hot path free of an extra network call.
148
+ * A truly-revoked token that slips past this check still hits 4001
149
+ * on the WebSocket, which also triggers `notifyAuthExpired`.
150
+ */
151
+ maybeVerifyInParallel(): void {
152
+ const creds = getCredentials()
153
+ if (!creds || !shouldRefreshToken(creds)) return
154
+ this.verify().then((valid) => {
155
+ if (!valid) this.notifyAuthExpired()
156
+ }).catch(() => { /* network error — rely on 4001 close path */ })
157
+ }
158
+
159
+ startPeriodicCheck(): void {
160
+ this.intervalHandle = setInterval(() => {
161
+ this.maybeRefresh()
162
+ }, PERIODIC_CHECK_INTERVAL_MS)
163
+ }
164
+
165
+ stopPeriodicCheck(): void {
166
+ if (this.intervalHandle) {
167
+ clearInterval(this.intervalHandle)
168
+ this.intervalHandle = null
169
+ }
170
+ }
171
+ }
@@ -1,5 +1,4 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
- // Licensed under the MIT License. See LICENSE file for details.
3
2
 
4
3
  /**
5
4
  * Platform Connection Service
@@ -14,12 +13,8 @@ import { arch, hostname, type } from 'node:os'
14
13
  import { basename } from 'node:path'
15
14
  import { AnalyticsEvents, trackEvent } from './analytics.js'
16
15
  import { getClientId } from './client-id.js'
17
- import {
18
- CLI_VERSION,
19
- getCredentials,
20
- shouldRefreshToken,
21
- updateCredentials,
22
- } from './platform-credentials.js'
16
+ import { CLI_VERSION, getCredentials } from './platform-credentials.js'
17
+ import { TokenLifecycle } from './platform-token-lifecycle.js'
23
18
  import { captureException } from './sentry.js'
24
19
 
25
20
  /**
@@ -50,6 +45,27 @@ if (typeof WebSocket !== 'undefined') {
50
45
  // PLATFORM_URL is set via --server / --dev flag in mstro.js
51
46
  const DEFAULT_PLATFORM_URL = process.env.PLATFORM_URL || 'https://api.mstro.app'
52
47
 
48
+ /**
49
+ * Relay wire-format version this CLI speaks. Bumped by the server when the
50
+ * relay protocol changes incompatibly. Must match (or exceed) the server's
51
+ * `minProtocolVersion` in `server/src/relay/version-policy.ts` — when the
52
+ * server's floor moves above this value, the upgrade handshake returns 426
53
+ * and the CLI surfaces the upgrade message via `handleSocketClose`.
54
+ *
55
+ * This is a build-time constant, not user-configurable: a stale CLI must be
56
+ * told to upgrade rather than allowed to opt itself into compatibility.
57
+ */
58
+ const PROTOCOL_VERSION = 1
59
+
60
+ /**
61
+ * Long sentinel delay used after a 426 protocol-too-old close. The CLI
62
+ * auto-upgrades on the next `mstro` invocation, so a tight reconnect loop
63
+ * would just hammer the server with rejections that policy already decided
64
+ * to refuse. One hour is long enough to count as "stop", short enough that
65
+ * a forgotten foreground process eventually retries on its own.
66
+ */
67
+ const PROTOCOL_UPGRADE_RECONNECT_DELAY_MS = 60 * 60 * 1000
68
+
53
69
  interface ConnectionCallbacks {
54
70
  onConnected?: (connectionId: string) => void
55
71
  onDisconnected?: () => void
@@ -73,11 +89,12 @@ export class PlatformConnection {
73
89
  private callbacks: ConnectionCallbacks
74
90
  private connectionId: string | null = null
75
91
  private isConnected = false
76
- private tokenRefreshInterval: ReturnType<typeof setInterval> | null = null
77
92
  private heartbeatInterval: ReturnType<typeof setInterval> | null = null
78
93
  private missedPongs = 0
79
94
  private everConnected = false
95
+ private protocolUpgradeRequired = false
80
96
  private readonly startedAt: string
97
+ private readonly tokens: TokenLifecycle
81
98
 
82
99
  constructor(
83
100
  workingDirectory: string,
@@ -88,39 +105,11 @@ export class PlatformConnection {
88
105
  this.platformUrl = platformUrl || DEFAULT_PLATFORM_URL
89
106
  this.callbacks = callbacks
90
107
  this.startedAt = new Date().toISOString()
91
- }
92
-
93
- private async maybeRefreshToken(): Promise<void> {
94
- const creds = getCredentials()
95
- if (!creds || !shouldRefreshToken(creds)) return
96
-
97
- try {
98
- const response = await fetch(`${this.platformUrl}/api/auth/device/refresh`, {
99
- method: 'POST',
100
- headers: {
101
- 'Authorization': `Bearer ${creds.token}`,
102
- 'Content-Type': 'application/json'
103
- }
104
- })
105
-
106
- if (response.ok) {
107
- const data = await response.json() as { accessToken: string }
108
- updateCredentials({
109
- token: data.accessToken,
110
- lastRefreshedAt: new Date().toISOString()
111
- })
112
- } else {
113
- console.warn('[Platform] Token refresh failed, will retry later')
114
- }
115
- } catch (err) {
116
- console.warn('[Platform] Token refresh error:', err)
117
- }
118
- }
119
-
120
- private startTokenRefreshCheck(): void {
121
- this.tokenRefreshInterval = setInterval(() => {
122
- this.maybeRefreshToken()
123
- }, 24 * 60 * 60 * 1000)
108
+ this.tokens = new TokenLifecycle({
109
+ platformUrl: this.platformUrl,
110
+ send: (msg) => this.send(msg),
111
+ getConnectionId: () => this.connectionId,
112
+ })
124
113
  }
125
114
 
126
115
  private startHeartbeat(): void {
@@ -153,48 +142,18 @@ export class PlatformConnection {
153
142
  }
154
143
  }
155
144
 
156
- private stopTokenRefreshCheck(): void {
157
- if (this.tokenRefreshInterval) {
158
- clearInterval(this.tokenRefreshInterval)
159
- this.tokenRefreshInterval = null
160
- }
161
- }
162
-
163
145
  connect(): void {
164
146
  this.isIntentionallyClosed = false
165
- const name = basename(this.workingDirectory)
166
- const machineHostname = hostname()
167
- const clientId = getClientId()
168
- const machineId = getMachineIdentifier()
169
- const nodeVersion = process.version
170
- const osType = type().toLowerCase()
171
- const cpuArch = arch()
172
-
173
- const credentials = getCredentials()
174
- const authToken = credentials?.token
147
+ this.protocolUpgradeRequired = false
175
148
 
149
+ const authToken = getCredentials()?.token
176
150
  if (!authToken) {
177
151
  console.error('\n❌ Not logged in. Run `mstro login` first.\n')
178
152
  this.callbacks.onError?.('Not logged in - run `mstro login` first')
179
153
  return
180
154
  }
181
155
 
182
- const params = new URLSearchParams({
183
- name,
184
- workingDirectory: this.workingDirectory,
185
- machineHostname,
186
- clientId,
187
- machineId,
188
- nodeVersion,
189
- osType,
190
- cpuArch,
191
- cliVersion: CLI_VERSION,
192
- capabilities: JSON.stringify({}),
193
- startedAt: this.startedAt,
194
- })
195
-
196
- const wsUrl = `${this.platformUrl.replace(/^http/, 'ws')}/ws/client?${params}`
197
-
156
+ const wsUrl = this.buildConnectionUrl()
198
157
  try {
199
158
  this.ws = new WebSocketImpl(wsUrl)
200
159
  } catch (err) {
@@ -205,7 +164,31 @@ export class PlatformConnection {
205
164
  return
206
165
  }
207
166
 
208
- const connectionTimeout = setTimeout(() => {
167
+ const connectionTimeout = this.startConnectionTimeout()
168
+ this.attachSocketHandlers(this.ws, authToken, connectionTimeout)
169
+ this.tokens.maybeVerifyInParallel()
170
+ }
171
+
172
+ private buildConnectionUrl(): string {
173
+ const params = new URLSearchParams({
174
+ name: basename(this.workingDirectory),
175
+ workingDirectory: this.workingDirectory,
176
+ machineHostname: hostname(),
177
+ clientId: getClientId(),
178
+ machineId: getMachineIdentifier(),
179
+ nodeVersion: process.version,
180
+ osType: type().toLowerCase(),
181
+ cpuArch: arch(),
182
+ cliVersion: CLI_VERSION,
183
+ capabilities: JSON.stringify({}),
184
+ startedAt: this.startedAt,
185
+ protocolVersion: String(PROTOCOL_VERSION),
186
+ })
187
+ return `${this.platformUrl.replace(/^http/, 'ws')}/ws/client?${params}`
188
+ }
189
+
190
+ private startConnectionTimeout(): ReturnType<typeof setTimeout> {
191
+ return setTimeout(() => {
209
192
  const state = this.ws?.readyState
210
193
  if (this.ws && (state === 0 || state === undefined)) {
211
194
  console.error('\n❌ Connection timeout. The platform may have rejected your credentials.')
@@ -214,53 +197,133 @@ export class PlatformConnection {
214
197
  this.callbacks.onError?.('Connection timeout - run `mstro login --force`')
215
198
  }
216
199
  }, 10000)
200
+ }
217
201
 
218
- this.ws.onopen = () => {
202
+ private attachSocketHandlers(
203
+ ws: WebSocket,
204
+ authToken: string,
205
+ connectionTimeout: ReturnType<typeof setTimeout>,
206
+ ): void {
207
+ ws.onopen = () => {
219
208
  clearTimeout(connectionTimeout)
220
- this.ws!.send(JSON.stringify({ type: 'auth', token: authToken }))
221
- this.maybeRefreshToken()
222
- this.startTokenRefreshCheck()
209
+ ws.send(JSON.stringify({ type: 'auth', token: authToken }))
210
+ this.tokens.maybeRefresh()
211
+ this.tokens.startPeriodicCheck()
223
212
  this.reconnectAttempts = 0
224
213
  trackEvent(AnalyticsEvents.PLATFORM_CONNECTED)
225
214
  }
226
215
 
227
- this.ws.onmessage = (event) => {
216
+ ws.onmessage = (event) => {
228
217
  try {
229
- const message = JSON.parse(event.data.toString())
230
- this.handleMessage(message)
218
+ this.handleMessage(JSON.parse(event.data.toString()))
231
219
  } catch (err) {
232
220
  console.error('Failed to parse platform message:', err)
233
221
  }
234
222
  }
235
223
 
236
- this.ws.onclose = (event) => {
224
+ ws.onclose = (event) => {
237
225
  clearTimeout(connectionTimeout)
238
- this.stopHeartbeat()
239
- this.isConnected = false
240
-
241
- if (!this.isIntentionallyClosed) {
242
- const isAuthFailure = event.code === 4001 ||
243
- event.reason?.includes('Unauthorized') ||
244
- (event.code === 1006 && !this.everConnected)
245
-
246
- if (isAuthFailure) {
247
- console.error('\n❌ Authentication failed. Your device token may be invalid or expired.')
248
- console.error(' Run `mstro login --force` to re-authenticate.\n')
249
- this.callbacks.onError?.('Authentication failed - run `mstro login --force`')
250
- return
251
- }
252
-
253
- console.log('Disconnected, reconnecting...')
254
- this.callbacks.onDisconnected?.()
255
- trackEvent(AnalyticsEvents.PLATFORM_DISCONNECTED)
256
- this.scheduleReconnect()
257
- }
226
+ this.handleSocketClose(event)
258
227
  }
259
228
 
260
- this.ws.onerror = () => {
229
+ ws.onerror = () => {
261
230
  clearTimeout(connectionTimeout)
262
231
  // onclose will be called after this
263
232
  }
233
+
234
+ // The Node `ws` library exposes the raw HTTP upgrade response as an
235
+ // `unexpected-response` event when the server returns a non-101 status
236
+ // (for us: 426 Upgrade Required from the protocol-version gate). The
237
+ // global WebSocket in Bun / Node 21+ doesn't expose this — for those
238
+ // runtimes we rely on the close-code path below. Both paths feed into
239
+ // `protocolUpgradeRequired`, so `handleSocketClose` can decide once.
240
+ const wsWithEmitter = ws as unknown as {
241
+ on?: (event: string, listener: (...args: unknown[]) => void) => void
242
+ }
243
+ if (typeof wsWithEmitter.on === 'function') {
244
+ wsWithEmitter.on('unexpected-response', (...args: unknown[]) => {
245
+ const response = args[1] as { statusCode?: number } | undefined
246
+ if (response?.statusCode === 426) {
247
+ this.protocolUpgradeRequired = true
248
+ }
249
+ })
250
+ }
251
+ }
252
+
253
+ private handleSocketClose(event: CloseEvent): void {
254
+ this.stopHeartbeat()
255
+ this.isConnected = false
256
+
257
+ if (this.isIntentionallyClosed) return
258
+
259
+ if (this.isProtocolTooOldClose(event)) {
260
+ this.handleProtocolUpgradeRequired()
261
+ return
262
+ }
263
+
264
+ const isAuthFailure = event.code === 4001 ||
265
+ event.reason?.includes('Unauthorized') ||
266
+ (event.code === 1006 && !this.everConnected)
267
+
268
+ if (isAuthFailure) {
269
+ console.error('\n❌ Authentication failed. Your device token may be invalid or expired.')
270
+ console.error(' Run `mstro login --force` to re-authenticate.\n')
271
+ this.tokens.notifyAuthExpired()
272
+ this.callbacks.onError?.('Authentication failed - run `mstro login --force`')
273
+ return
274
+ }
275
+
276
+ console.log('Disconnected, reconnecting...')
277
+ this.callbacks.onDisconnected?.()
278
+ trackEvent(AnalyticsEvents.PLATFORM_DISCONNECTED)
279
+ this.scheduleReconnect()
280
+ }
281
+
282
+ /**
283
+ * The relay can refuse a CLI for being too old in two shapes:
284
+ *
285
+ * 1. **HTTP 426 during upgrade** — the `ws` library surfaces this via
286
+ * its `unexpected-response` event, which sets
287
+ * `protocolUpgradeRequired` before `onclose` fires. This is the path
288
+ * the current server takes (`checkProtocolVersionGate`).
289
+ *
290
+ * 2. **WS close 1002/1008 with `protocol-too-old` reason** — reserved
291
+ * for a future server variant that completes the upgrade and then
292
+ * closes (e.g. when the policy check moves into a post-handshake
293
+ * stage). The reason string is part of the contract with the server.
294
+ *
295
+ * Anything else (1006 race after a successful run, 1001 going-away on
296
+ * deploy) must fall through to the regular reconnect path so transient
297
+ * failures keep healing on their own.
298
+ */
299
+ private isProtocolTooOldClose(event: CloseEvent): boolean {
300
+ if (this.protocolUpgradeRequired) return true
301
+ const code = event.code
302
+ if ((code === 1002 || code === 1008) && event.reason?.includes('protocol-too-old')) {
303
+ return true
304
+ }
305
+ return false
306
+ }
307
+
308
+ private handleProtocolUpgradeRequired(): void {
309
+ const message = 'Mstro CLI is out of date for this orchestra. Run `mstro` again to upgrade and reconnect.'
310
+ console.error(`\n❌ ${message}\n`)
311
+ this.callbacks.onError?.(message)
312
+ this.callbacks.onDisconnected?.()
313
+
314
+ // Don't exit the process — terminal sessions and other local state stay
315
+ // alive while the user re-runs `mstro`. Schedule a single, far-future
316
+ // reconnect as a sentinel so we silently retry exactly once if the
317
+ // process is somehow still around an hour later. The existing
318
+ // exponential-backoff path is bypassed deliberately: hammering a server
319
+ // that has already decided to refuse on policy is pointless.
320
+ if (this.reconnectTimeout) {
321
+ clearTimeout(this.reconnectTimeout)
322
+ }
323
+ this.reconnectTimeout = setTimeout(() => {
324
+ this.reconnectTimeout = null
325
+ this.connect()
326
+ }, PROTOCOL_UPGRADE_RECONNECT_DELAY_MS)
264
327
  }
265
328
 
266
329
  private handleMessage(message: Record<string, unknown>): void {
@@ -325,7 +388,7 @@ export class PlatformConnection {
325
388
  disconnect(): void {
326
389
  this.isIntentionallyClosed = true
327
390
  this.stopHeartbeat()
328
- this.stopTokenRefreshCheck()
391
+ this.tokens.stopPeriodicCheck()
329
392
 
330
393
  if (this.reconnectTimeout) {
331
394
  clearTimeout(this.reconnectTimeout)