@minionry/minion 0.7.36

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 (476) hide show
  1. package/LICENSE +14 -0
  2. package/PRIVACY.md +357 -0
  3. package/README.md +57 -0
  4. package/THIRD_PARTY_NOTICES.md +245 -0
  5. package/bin/branding.js +4 -0
  6. package/bin/commands/browser-install.js +483 -0
  7. package/bin/commands/create-sandbox.js +542 -0
  8. package/bin/commands/fingerprint.js +101 -0
  9. package/bin/commands/login.js +400 -0
  10. package/bin/commands/logout.js +93 -0
  11. package/bin/commands/minions.js +211 -0
  12. package/bin/commands/sandboxes.js +309 -0
  13. package/bin/commands/status.js +298 -0
  14. package/bin/commands/whoami.js +153 -0
  15. package/bin/launch-env.js +35 -0
  16. package/bin/machine-identity.js +221 -0
  17. package/bin/machine-layout.js +200 -0
  18. package/bin/minion.js +839 -0
  19. package/bin/platform-endpoints.js +8 -0
  20. package/bin/postinstall.js +52 -0
  21. package/bin/pty-doctor.js +326 -0
  22. package/bin/pty-locator.js +110 -0
  23. package/bin/pty-provision.js +186 -0
  24. package/bin/sandbox/Dockerfile +78 -0
  25. package/bin/sandbox/entrypoint.sh +14 -0
  26. package/bin/sandbox/install-repo.sh +53 -0
  27. package/bin/sandbox/seccomp-userns.json +883 -0
  28. package/bin/sandbox-config.js +306 -0
  29. package/dist/bin/machine-identity.js +221 -0
  30. package/dist/bin/machine-layout.js +200 -0
  31. package/dist/electron-host/agent-lease.js +141 -0
  32. package/dist/electron-host/dom-driver.js +642 -0
  33. package/dist/electron-host/ipc-protocol.js +89 -0
  34. package/dist/electron-host/js-driver.js +216 -0
  35. package/dist/electron-host/view-manager.js +254 -0
  36. package/dist/server/cli/engine-resume.js +49 -0
  37. package/dist/server/cli/eta-estimator.js +278 -0
  38. package/dist/server/cli/headless/background-activity.js +109 -0
  39. package/dist/server/cli/headless/claude-invoker-process.js +614 -0
  40. package/dist/server/cli/headless/claude-invoker-stall.js +150 -0
  41. package/dist/server/cli/headless/claude-invoker-stream.js +555 -0
  42. package/dist/server/cli/headless/claude-invoker-tools.js +148 -0
  43. package/dist/server/cli/headless/claude-invoker.js +151 -0
  44. package/dist/server/cli/headless/engine-availability.js +92 -0
  45. package/dist/server/cli/headless/engine-notice.js +185 -0
  46. package/dist/server/cli/headless/haiku-assessments.js +293 -0
  47. package/dist/server/cli/headless/headless-logger.js +75 -0
  48. package/dist/server/cli/headless/index.js +9 -0
  49. package/dist/server/cli/headless/mcp-config.js +314 -0
  50. package/dist/server/cli/headless/native-timeout-detector.js +72 -0
  51. package/dist/server/cli/headless/output-utils.js +71 -0
  52. package/dist/server/cli/headless/prompt-utils.js +82 -0
  53. package/dist/server/cli/headless/resilient-runner.js +326 -0
  54. package/dist/server/cli/headless/retry-strategies.js +292 -0
  55. package/dist/server/cli/headless/runner.js +245 -0
  56. package/dist/server/cli/headless/stall-assessor.js +295 -0
  57. package/dist/server/cli/headless/tool-watchdog.js +447 -0
  58. package/dist/server/cli/headless/types.js +18 -0
  59. package/dist/server/cli/history-portability.js +85 -0
  60. package/dist/server/cli/improvisation-attachments.js +99 -0
  61. package/dist/server/cli/improvisation-history-store.js +121 -0
  62. package/dist/server/cli/improvisation-movements.js +137 -0
  63. package/dist/server/cli/improvisation-output-queue.js +34 -0
  64. package/dist/server/cli/improvisation-retry.js +8 -0
  65. package/dist/server/cli/improvisation-session-manager.js +1016 -0
  66. package/dist/server/cli/improvisation-types.js +31 -0
  67. package/dist/server/cli/plan-lifecycle.js +72 -0
  68. package/dist/server/cli/prompt-builders.js +371 -0
  69. package/dist/server/cli/retry/retry-best-result.js +28 -0
  70. package/dist/server/cli/retry/retry-context-loss.js +75 -0
  71. package/dist/server/cli/retry/retry-recovery-strategies.js +213 -0
  72. package/dist/server/cli/retry/retry-resume-strategy.js +20 -0
  73. package/dist/server/cli/retry/retry-runner-factory.js +92 -0
  74. package/dist/server/cli/retry/retry-tool-results.js +22 -0
  75. package/dist/server/cli/retry/retry-types.js +3 -0
  76. package/dist/server/engines/EngineEvent.js +57 -0
  77. package/dist/server/engines/claude/ClaudeCodeEngine.js +316 -0
  78. package/dist/server/engines/claude/claude-command.js +32 -0
  79. package/dist/server/engines/codex/CodexEngine.js +469 -0
  80. package/dist/server/engines/codex/codex-auth.js +203 -0
  81. package/dist/server/engines/codex/codex-invoker.js +205 -0
  82. package/dist/server/engines/codex/codex-provision.js +215 -0
  83. package/dist/server/engines/credentials/credential-probe.js +88 -0
  84. package/dist/server/engines/credentials/model-profiles.js +249 -0
  85. package/dist/server/engines/credentials/provider-credentials.js +146 -0
  86. package/dist/server/engines/engine-event-stream.js +52 -0
  87. package/dist/server/engines/execution/engine-session.js +380 -0
  88. package/dist/server/engines/factory.js +42 -0
  89. package/dist/server/engines/hermes/HermesEngine.js +628 -0
  90. package/dist/server/engines/hermes/hermes-auth.js +159 -0
  91. package/dist/server/engines/hermes/hermes-gateway-home.js +271 -0
  92. package/dist/server/engines/hermes/hermes-invoker.js +350 -0
  93. package/dist/server/engines/hermes/hermes-provision.js +318 -0
  94. package/dist/server/engines/read-only.js +20 -0
  95. package/dist/server/engines/types.js +27 -0
  96. package/dist/server/handlers/company-execution-registry.js +67 -0
  97. package/dist/server/handlers/company-handlers.js +514 -0
  98. package/dist/server/handlers/company-triggers.js +189 -0
  99. package/dist/server/handlers/company-types.js +3 -0
  100. package/dist/server/index.js +244 -0
  101. package/dist/server/mcp/app-tools-bridge.js +110 -0
  102. package/dist/server/mcp/app-tools-mcp-server.js +99 -0
  103. package/dist/server/mcp/bouncer-integration.js +744 -0
  104. package/dist/server/mcp/browser-mcp-server.js +915 -0
  105. package/dist/server/mcp/browser-redaction.js +103 -0
  106. package/dist/server/mcp/classifier/BouncerClassifier.js +3 -0
  107. package/dist/server/mcp/classifier/ClaudeBouncerClassifier.js +212 -0
  108. package/dist/server/mcp/classifier/factory.js +124 -0
  109. package/dist/server/mcp/classifier/health.js +54 -0
  110. package/dist/server/mcp/classifier/telemetry.js +0 -0
  111. package/dist/server/mcp/company-patterns.js +104 -0
  112. package/dist/server/mcp/department-profiles.js +133 -0
  113. package/dist/server/mcp/quality-bridge.js +153 -0
  114. package/dist/server/mcp/quality-mcp-server.js +105 -0
  115. package/dist/server/mcp/security-analysis.js +384 -0
  116. package/dist/server/mcp/security-audit.js +152 -0
  117. package/dist/server/mcp/security-patterns.js +314 -0
  118. package/dist/server/mcp/server.js +194 -0
  119. package/dist/server/mcp/test-utils/agent-simulator.js +301 -0
  120. package/dist/server/mcp/test-utils/e2e-agent.js +103 -0
  121. package/dist/server/mcp/test-utils/e2e-capture.js +92 -0
  122. package/dist/server/mcp/test-utils/e2e-client.js +116 -0
  123. package/dist/server/mcp/test-utils/e2e-fixtures.js +87 -0
  124. package/dist/server/mcp/test-utils/e2e-gating.js +8 -0
  125. package/dist/server/mcp/test-utils/e2e-harness.js +8 -0
  126. package/dist/server/mcp/test-utils/e2e-scenarios.js +105 -0
  127. package/dist/server/routes/files.js +18 -0
  128. package/dist/server/routes/improvise.js +64 -0
  129. package/dist/server/routes/index.js +7 -0
  130. package/dist/server/routes/instances.js +41 -0
  131. package/dist/server/routes/internal.js +900 -0
  132. package/dist/server/routes/notifications.js +110 -0
  133. package/dist/server/server-setup.js +282 -0
  134. package/dist/server/services/agent-push.js +148 -0
  135. package/dist/server/services/analytics.js +197 -0
  136. package/dist/server/services/apps/build.js +249 -0
  137. package/dist/server/services/apps/greenlight.js +199 -0
  138. package/dist/server/services/apps/install.js +101 -0
  139. package/dist/server/services/apps/layout.js +35 -0
  140. package/dist/server/services/apps/serve.js +665 -0
  141. package/dist/server/services/apps/space-apps.js +248 -0
  142. package/dist/server/services/auth.js +48 -0
  143. package/dist/server/services/browser/chrome-sandbox.js +245 -0
  144. package/dist/server/services/browser/chromium-launch-spec.js +51 -0
  145. package/dist/server/services/browser/claude-reasoner.js +669 -0
  146. package/dist/server/services/browser/debug-log.js +3 -0
  147. package/dist/server/services/browser/driver-state.js +546 -0
  148. package/dist/server/services/browser/electron-provision.js +256 -0
  149. package/dist/server/services/browser/host-internal.js +245 -0
  150. package/dist/server/services/browser/host-mock.js +334 -0
  151. package/dist/server/services/browser/host-types.js +3 -0
  152. package/dist/server/services/browser/host.js +1067 -0
  153. package/dist/server/services/browser/index.js +4 -0
  154. package/dist/server/services/browser/ipc-client.js +354 -0
  155. package/dist/server/services/browser/pipewire-sink.js +199 -0
  156. package/dist/server/services/browser/preflight.js +590 -0
  157. package/dist/server/services/browser/reasoning-agent.js +659 -0
  158. package/dist/server/services/browser/sandbox-policy.js +169 -0
  159. package/dist/server/services/browser/stderr-noise-filter.js +26 -0
  160. package/dist/server/services/browser/telemetry.js +107 -0
  161. package/dist/server/services/browser/xvfb-supervisor.js +139 -0
  162. package/dist/server/services/browser-frame-policy.js +56 -0
  163. package/dist/server/services/client-id.js +32 -0
  164. package/dist/server/services/collab/registry.js +408 -0
  165. package/dist/server/services/collab/session.js +489 -0
  166. package/dist/server/services/collab/text-diff.js +28 -0
  167. package/dist/server/services/collab/types.js +19 -0
  168. package/dist/server/services/e2ee-group-key.js +197 -0
  169. package/dist/server/services/e2ee-identity.js +203 -0
  170. package/dist/server/services/file-explorer-ops.js +328 -0
  171. package/dist/server/services/file-transfer-zip.js +188 -0
  172. package/dist/server/services/files.js +111 -0
  173. package/dist/server/services/git/branch-ops.js +62 -0
  174. package/dist/server/services/git/command.js +68 -0
  175. package/dist/server/services/git/commit-message.js +74 -0
  176. package/dist/server/services/git/diff-ops.js +56 -0
  177. package/dist/server/services/git/haiku.js +76 -0
  178. package/dist/server/services/git/head-watcher.js +226 -0
  179. package/dist/server/services/git/index.js +17 -0
  180. package/dist/server/services/git/log-ops.js +98 -0
  181. package/dist/server/services/git/merge-ops.js +314 -0
  182. package/dist/server/services/git/parsers.js +401 -0
  183. package/dist/server/services/git/remote-ops.js +192 -0
  184. package/dist/server/services/git/status-ops.js +105 -0
  185. package/dist/server/services/git/suggestion-ops.js +80 -0
  186. package/dist/server/services/git/tag-ops.js +36 -0
  187. package/dist/server/services/git/types.js +4 -0
  188. package/dist/server/services/git/worktree-ops.js +114 -0
  189. package/dist/server/services/instances.js +337 -0
  190. package/dist/server/services/model-catalog.js +247 -0
  191. package/dist/server/services/observability/posthog.js +153 -0
  192. package/dist/server/services/pathUtils.js +114 -0
  193. package/dist/server/services/plan/agent-loader.js +97 -0
  194. package/dist/server/services/plan/agent-manager.js +363 -0
  195. package/dist/server/services/plan/agent-resolver.js +74 -0
  196. package/dist/server/services/plan/agents/assess-stall.md +33 -0
  197. package/dist/server/services/plan/agents/browser-planner.md +53 -0
  198. package/dist/server/services/plan/agents/browser-reasoner.md +114 -0
  199. package/dist/server/services/plan/agents/check-injection.md +95 -0
  200. package/dist/server/services/plan/agents/classify-error.md +30 -0
  201. package/dist/server/services/plan/agents/code-review.md +142 -0
  202. package/dist/server/services/plan/agents/commit-message.md +28 -0
  203. package/dist/server/services/plan/agents/detect-context-loss.md +30 -0
  204. package/dist/server/services/plan/agents/execute-issue.md +58 -0
  205. package/dist/server/services/plan/agents/fix-quality.md +25 -0
  206. package/dist/server/services/plan/agents/plan-coordinator.md +72 -0
  207. package/dist/server/services/plan/agents/pr-description.md +29 -0
  208. package/dist/server/services/plan/agents/retry-task.md +27 -0
  209. package/dist/server/services/plan/agents/review-code.md +38 -0
  210. package/dist/server/services/plan/agents/review-quality.md +45 -0
  211. package/dist/server/services/plan/agents/triage-findings.md +78 -0
  212. package/dist/server/services/plan/agents/verify-review.md +57 -0
  213. package/dist/server/services/plan/approval-evaluator.js +143 -0
  214. package/dist/server/services/plan/board-archive.js +102 -0
  215. package/dist/server/services/plan/board-config.js +108 -0
  216. package/dist/server/services/plan/board-export.js +214 -0
  217. package/dist/server/services/plan/composer-prompt.js +115 -0
  218. package/dist/server/services/plan/composer.js +664 -0
  219. package/dist/server/services/plan/config-installer.js +103 -0
  220. package/dist/server/services/plan/dependency-resolver.js +137 -0
  221. package/dist/server/services/plan/executor.js +696 -0
  222. package/dist/server/services/plan/front-matter.js +43 -0
  223. package/dist/server/services/plan/issue-classification.js +10 -0
  224. package/dist/server/services/plan/issue-effort.js +182 -0
  225. package/dist/server/services/plan/issue-loader.js +35 -0
  226. package/dist/server/services/plan/issue-prompt-builder.js +152 -0
  227. package/dist/server/services/plan/issue-retry.js +42 -0
  228. package/dist/server/services/plan/issue-writer.js +102 -0
  229. package/dist/server/services/plan/metrics-collector.js +272 -0
  230. package/dist/server/services/plan/output-manager.js +77 -0
  231. package/dist/server/services/plan/parser-core.js +435 -0
  232. package/dist/server/services/plan/parser-migration.js +108 -0
  233. package/dist/server/services/plan/parser.js +258 -0
  234. package/dist/server/services/plan/plan-runner-factory.js +68 -0
  235. package/dist/server/services/plan/pm-timeline.js +320 -0
  236. package/dist/server/services/plan/progress-log.js +101 -0
  237. package/dist/server/services/plan/prompt-builder.js +142 -0
  238. package/dist/server/services/plan/quality-delta.js +274 -0
  239. package/dist/server/services/plan/readiness-planner.js +34 -0
  240. package/dist/server/services/plan/review-gate.js +409 -0
  241. package/dist/server/services/plan/review-report.js +134 -0
  242. package/dist/server/services/plan/review-verdict-stream.js +161 -0
  243. package/dist/server/services/plan/state-reconciler.js +238 -0
  244. package/dist/server/services/plan/template-diff.js +175 -0
  245. package/dist/server/services/plan/template-instantiator.js +516 -0
  246. package/dist/server/services/plan/template-manifest.js +56 -0
  247. package/dist/server/services/plan/template-types.js +3 -0
  248. package/dist/server/services/plan/trigger-evaluator.js +211 -0
  249. package/dist/server/services/plan/types.js +3 -0
  250. package/dist/server/services/plan/watcher.js +73 -0
  251. package/dist/server/services/platform-credentials.js +56 -0
  252. package/dist/server/services/platform-e2ee.js +421 -0
  253. package/dist/server/services/platform-envelope.js +102 -0
  254. package/dist/server/services/platform-heartbeat.js +88 -0
  255. package/dist/server/services/platform-reconnect.js +60 -0
  256. package/dist/server/services/platform-token-lifecycle.js +95 -0
  257. package/dist/server/services/platform-url.js +17 -0
  258. package/dist/server/services/platform.js +656 -0
  259. package/dist/server/services/runtime-info.js +9 -0
  260. package/dist/server/services/schedule/agent-schedule-store.js +232 -0
  261. package/dist/server/services/schedule/agent-scheduler.js +323 -0
  262. package/dist/server/services/schedule/daily-timing.js +105 -0
  263. package/dist/server/services/schedule/quality-schedule-launch.js +11 -0
  264. package/dist/server/services/schedule/quality-schedule-store.js +211 -0
  265. package/dist/server/services/schedule/quality-scheduler.js +215 -0
  266. package/dist/server/services/schedule/run-bootstrap.js +329 -0
  267. package/dist/server/services/schedule/schedule-store.js +238 -0
  268. package/dist/server/services/schedule/schedule-wire.js +77 -0
  269. package/dist/server/services/schedule/scheduler.js +310 -0
  270. package/dist/server/services/schedule/template-source.js +97 -0
  271. package/dist/server/services/sdk/agent-sessions.js +264 -0
  272. package/dist/server/services/sdk/agents.js +557 -0
  273. package/dist/server/services/sdk/app-endpoints.js +48 -0
  274. package/dist/server/services/sdk/app-tools.js +303 -0
  275. package/dist/server/services/sdk/audit.js +48 -0
  276. package/dist/server/services/sdk/browser-transport.js +188 -0
  277. package/dist/server/services/sdk/browser.js +605 -0
  278. package/dist/server/services/sdk/composer.js +129 -0
  279. package/dist/server/services/sdk/connect.js +273 -0
  280. package/dist/server/services/sdk/consent-cooldown.js +31 -0
  281. package/dist/server/services/sdk/endpoint-url.js +141 -0
  282. package/dist/server/services/sdk/endpoints.js +314 -0
  283. package/dist/server/services/sdk/file-change-notify.js +36 -0
  284. package/dist/server/services/sdk/files-app.js +163 -0
  285. package/dist/server/services/sdk/files-search.js +131 -0
  286. package/dist/server/services/sdk/files-transfer.js +279 -0
  287. package/dist/server/services/sdk/files-workspace.js +233 -0
  288. package/dist/server/services/sdk/git.js +520 -0
  289. package/dist/server/services/sdk/grant-store.js +318 -0
  290. package/dist/server/services/sdk/host-ops.js +84 -0
  291. package/dist/server/services/sdk/inference-claude.js +214 -0
  292. package/dist/server/services/sdk/inference.js +247 -0
  293. package/dist/server/services/sdk/live-registry.js +42 -0
  294. package/dist/server/services/sdk/local-app-bindings.js +71 -0
  295. package/dist/server/services/sdk/pipeline.js +190 -0
  296. package/dist/server/services/sdk/pm-execute.js +228 -0
  297. package/dist/server/services/sdk/pm-planning-tap.js +29 -0
  298. package/dist/server/services/sdk/pm-schedule.js +165 -0
  299. package/dist/server/services/sdk/pm.js +634 -0
  300. package/dist/server/services/sdk/quality-change-notify.js +20 -0
  301. package/dist/server/services/sdk/quality-schedule.js +165 -0
  302. package/dist/server/services/sdk/quality.js +602 -0
  303. package/dist/server/services/sdk/rate-limits.js +198 -0
  304. package/dist/server/services/sdk/registry.js +597 -0
  305. package/dist/server/services/sdk/revoke.js +86 -0
  306. package/dist/server/services/sdk/run-profile.js +41 -0
  307. package/dist/server/services/sdk/sandbox.js +89 -0
  308. package/dist/server/services/sdk/storage.js +110 -0
  309. package/dist/server/services/sdk/streams.js +147 -0
  310. package/dist/server/services/sdk/terminal.js +201 -0
  311. package/dist/server/services/sdk/test-utils/workshop-browser-app.js +466 -0
  312. package/dist/server/services/sentry.js +99 -0
  313. package/dist/server/services/settings.js +374 -0
  314. package/dist/server/services/snippets/expander.js +87 -0
  315. package/dist/server/services/snippets/index.js +6 -0
  316. package/dist/server/services/snippets/loader.js +119 -0
  317. package/dist/server/services/snippets/parser.js +52 -0
  318. package/dist/server/services/snippets/types.js +3 -0
  319. package/dist/server/services/snippets/watcher.js +60 -0
  320. package/dist/server/services/space-layout.js +80 -0
  321. package/dist/server/services/system-prompts.js +219 -0
  322. package/dist/server/services/terminal/pty-manager.js +206 -0
  323. package/dist/server/services/terminal/pty-utils.js +150 -0
  324. package/dist/server/services/timeline/assembler.js +890 -0
  325. package/dist/server/services/timeline/contract.js +196 -0
  326. package/dist/server/services/timeline/index.js +4 -0
  327. package/dist/server/services/websocket/agent-schedule-handlers.js +217 -0
  328. package/dist/server/services/websocket/apps-handlers.js +399 -0
  329. package/dist/server/services/websocket/ask-user-question-bridge.js +112 -0
  330. package/dist/server/services/websocket/autocomplete-index.js +415 -0
  331. package/dist/server/services/websocket/autocomplete-match.js +243 -0
  332. package/dist/server/services/websocket/autocomplete.js +407 -0
  333. package/dist/server/services/websocket/browser-agent-runtime.js +208 -0
  334. package/dist/server/services/websocket/browser-device-profile.js +46 -0
  335. package/dist/server/services/websocket/browser-handlers.js +377 -0
  336. package/dist/server/services/websocket/browser-host-broadcast.js +179 -0
  337. package/dist/server/services/websocket/browser-ice-config.js +121 -0
  338. package/dist/server/services/websocket/browser-payloads.js +222 -0
  339. package/dist/server/services/websocket/browser-viewer-lifecycle.js +84 -0
  340. package/dist/server/services/websocket/browser-viewer-registry.js +118 -0
  341. package/dist/server/services/websocket/collab-broadcast.js +11 -0
  342. package/dist/server/services/websocket/collab-handlers.js +116 -0
  343. package/dist/server/services/websocket/commit-suggestion-service.js +132 -0
  344. package/dist/server/services/websocket/debug-log.js +38 -0
  345. package/dist/server/services/websocket/dispatch-util.js +10 -0
  346. package/dist/server/services/websocket/engine-auth-handlers.js +323 -0
  347. package/dist/server/services/websocket/engine-credential-watcher.js +83 -0
  348. package/dist/server/services/websocket/engine-profile-handlers.js +92 -0
  349. package/dist/server/services/websocket/file-definition-handlers.js +180 -0
  350. package/dist/server/services/websocket/file-download-handler.js +291 -0
  351. package/dist/server/services/websocket/file-explorer-handlers.js +291 -0
  352. package/dist/server/services/websocket/file-search-handlers.js +267 -0
  353. package/dist/server/services/websocket/file-share-handlers.js +328 -0
  354. package/dist/server/services/websocket/file-transfer-http.js +361 -0
  355. package/dist/server/services/websocket/file-upload-handler.js +267 -0
  356. package/dist/server/services/websocket/file-utils.js +346 -0
  357. package/dist/server/services/websocket/git-branch-handlers.js +90 -0
  358. package/dist/server/services/websocket/git-diff-handlers.js +59 -0
  359. package/dist/server/services/websocket/git-handlers.js +212 -0
  360. package/dist/server/services/websocket/git-head-watcher.js +85 -0
  361. package/dist/server/services/websocket/git-log-handlers.js +104 -0
  362. package/dist/server/services/websocket/git-pr-handlers.js +56 -0
  363. package/dist/server/services/websocket/git-tag-handlers.js +53 -0
  364. package/dist/server/services/websocket/git-utils.js +11 -0
  365. package/dist/server/services/websocket/git-worktree-handlers.js +521 -0
  366. package/dist/server/services/websocket/handler-context.js +113 -0
  367. package/dist/server/services/websocket/handler.js +1197 -0
  368. package/dist/server/services/websocket/index.js +5 -0
  369. package/dist/server/services/websocket/movement-written-files.js +26 -0
  370. package/dist/server/services/websocket/msg-id-tracker.js +48 -0
  371. package/dist/server/services/websocket/plan-board-handlers.js +410 -0
  372. package/dist/server/services/websocket/plan-execution-handlers.js +244 -0
  373. package/dist/server/services/websocket/plan-handlers.js +67 -0
  374. package/dist/server/services/websocket/plan-helpers.js +161 -0
  375. package/dist/server/services/websocket/plan-issue-handlers.js +168 -0
  376. package/dist/server/services/websocket/plan-sprint-handlers.js +190 -0
  377. package/dist/server/services/websocket/plan-transfer-handlers.js +310 -0
  378. package/dist/server/services/websocket/pm-timeline-broadcast.js +28 -0
  379. package/dist/server/services/websocket/post-session-scan.js +132 -0
  380. package/dist/server/services/websocket/prompt-queue-handlers.js +96 -0
  381. package/dist/server/services/websocket/quality-agent-cost.js +229 -0
  382. package/dist/server/services/websocket/quality-agent-notes.js +113 -0
  383. package/dist/server/services/websocket/quality-change-activity.js +187 -0
  384. package/dist/server/services/websocket/quality-complexity.js +345 -0
  385. package/dist/server/services/websocket/quality-dependencies.js +445 -0
  386. package/dist/server/services/websocket/quality-duplication.js +162 -0
  387. package/dist/server/services/websocket/quality-eta.js +65 -0
  388. package/dist/server/services/websocket/quality-fingerprint.js +104 -0
  389. package/dist/server/services/websocket/quality-grading.js +575 -0
  390. package/dist/server/services/websocket/quality-handlers.js +48 -0
  391. package/dist/server/services/websocket/quality-linting.js +270 -0
  392. package/dist/server/services/websocket/quality-operations.js +54 -0
  393. package/dist/server/services/websocket/quality-persistence.js +410 -0
  394. package/dist/server/services/websocket/quality-review-agent.js +526 -0
  395. package/dist/server/services/websocket/quality-run.js +220 -0
  396. package/dist/server/services/websocket/quality-secrets.js +0 -0
  397. package/dist/server/services/websocket/quality-service.js +775 -0
  398. package/dist/server/services/websocket/quality-tools.js +383 -0
  399. package/dist/server/services/websocket/quality-triage-agent.js +101 -0
  400. package/dist/server/services/websocket/quality-types.js +149 -0
  401. package/dist/server/services/websocket/recommended-answers.js +28 -0
  402. package/dist/server/services/websocket/schedule-handlers.js +197 -0
  403. package/dist/server/services/websocket/sdk-agent-host.js +569 -0
  404. package/dist/server/services/websocket/sdk-browser-host.js +459 -0
  405. package/dist/server/services/websocket/sdk-composer-host.js +10 -0
  406. package/dist/server/services/websocket/sdk-git-host.js +115 -0
  407. package/dist/server/services/websocket/sdk-handlers.js +242 -0
  408. package/dist/server/services/websocket/sdk-pm-execution-host.js +63 -0
  409. package/dist/server/services/websocket/sdk-pm-schedule-host.js +21 -0
  410. package/dist/server/services/websocket/sdk-quality-schedule-host.js +34 -0
  411. package/dist/server/services/websocket/sdk-terminal-host.js +140 -0
  412. package/dist/server/services/websocket/session-handlers.js +565 -0
  413. package/dist/server/services/websocket/session-history.js +335 -0
  414. package/dist/server/services/websocket/session-initialization.js +315 -0
  415. package/dist/server/services/websocket/session-registry.js +340 -0
  416. package/dist/server/services/websocket/session-role-directory.js +39 -0
  417. package/dist/server/services/websocket/settings-handlers.js +276 -0
  418. package/dist/server/services/websocket/skill-handlers.js +324 -0
  419. package/dist/server/services/websocket/skill-watcher.js +83 -0
  420. package/dist/server/services/websocket/snippet-handlers.js +261 -0
  421. package/dist/server/services/websocket/tab-broadcast.js +35 -0
  422. package/dist/server/services/websocket/tab-event-buffer.js +110 -0
  423. package/dist/server/services/websocket/tab-event-replay.js +47 -0
  424. package/dist/server/services/websocket/tab-handlers.js +211 -0
  425. package/dist/server/services/websocket/terminal-handlers.js +268 -0
  426. package/dist/server/services/websocket/types.js +96 -0
  427. package/dist/server/types/company-messages.js +102 -0
  428. package/dist/server/utils/agent-manager.js +202 -0
  429. package/dist/server/utils/console-timestamps.js +24 -0
  430. package/dist/server/utils/file-store.js +112 -0
  431. package/dist/server/utils/paths.js +28 -0
  432. package/dist/server/utils/port-manager.js +43 -0
  433. package/dist/server/utils/port.js +204 -0
  434. package/electron-host/dist/agent-lease.js +141 -0
  435. package/electron-host/dist/capture-recovery.js +77 -0
  436. package/electron-host/dist/codec-policy.js +37 -0
  437. package/electron-host/dist/controller-channel.js +60 -0
  438. package/electron-host/dist/controller-handlers.js +153 -0
  439. package/electron-host/dist/device-emulator.js +106 -0
  440. package/electron-host/dist/dom-driver.js +642 -0
  441. package/electron-host/dist/domain-registry.js +159 -0
  442. package/electron-host/dist/domain-validators.js +308 -0
  443. package/electron-host/dist/editable-focus-reporter.js +143 -0
  444. package/electron-host/dist/fake-peer.test-util.js +105 -0
  445. package/electron-host/dist/host-log.js +31 -0
  446. package/electron-host/dist/host-wire.js +11 -0
  447. package/electron-host/dist/input-router.js +421 -0
  448. package/electron-host/dist/ipc-protocol.js +89 -0
  449. package/electron-host/dist/ipc-server.js +193 -0
  450. package/electron-host/dist/js-driver.js +216 -0
  451. package/electron-host/dist/main-guards.js +70 -0
  452. package/electron-host/dist/main.js +716 -0
  453. package/electron-host/dist/roster-sync.js +87 -0
  454. package/electron-host/dist/rpc-context.js +1 -0
  455. package/electron-host/dist/rpc-tab-methods.js +138 -0
  456. package/electron-host/dist/rpc-webrtc-methods.js +121 -0
  457. package/electron-host/dist/screen-capture-cache.js +147 -0
  458. package/electron-host/dist/sdk-injector.js +166 -0
  459. package/electron-host/dist/sdp.js +102 -0
  460. package/electron-host/dist/session-surface.js +78 -0
  461. package/electron-host/dist/surface-sizing.js +59 -0
  462. package/electron-host/dist/view-manager.js +254 -0
  463. package/electron-host/dist/viewer-registry.js +46 -0
  464. package/electron-host/dist/viewer-session.js +288 -0
  465. package/electron-host/dist/webrtc-channels.js +14 -0
  466. package/electron-host/dist/webrtc-controller.js +57 -0
  467. package/electron-host/dist/webrtc-ice-filter.js +13 -0
  468. package/electron-host/dist/webrtc-peer.js +45 -0
  469. package/electron-host/package.json +12 -0
  470. package/electron-host/tsconfig.json +23 -0
  471. package/electron-host/webrtc-controller-preload.cjs +3 -0
  472. package/electron-host/webrtc-controller.html +14 -0
  473. package/package.json +85 -0
  474. package/vendor/e2ee/LICENSE +14 -0
  475. package/vendor/e2ee/package.json +14 -0
  476. package/vendor/e2ee/src/index.js +310 -0
@@ -0,0 +1,696 @@
1
+ // Copyright (c) 2025-present Minionry, Inc.
2
+ // SPDX-License-Identifier: LicenseRef-Minionry-Software
3
+ import { EventEmitter } from 'node:events';
4
+ import { readFile } from 'node:fs/promises';
5
+ import { join, resolve } from 'node:path';
6
+ import { etaTargetKey, getEtaProfileCached } from '../../cli/eta-estimator.js';
7
+ import { describeDispatchBlocker, engineAvailability } from '../../cli/headless/engine-availability.js';
8
+ import { describeParkUntil } from '../../cli/headless/engine-notice.js';
9
+ import { runWithFileLogger } from '../../cli/headless/headless-logger.js';
10
+ import { resolveHistoryPaths } from '../../cli/improvisation-history-store.js';
11
+ import { legacyThinkingChunk } from '../timeline/index.js';
12
+ import { movementWrittenFiles } from '../websocket/movement-written-files.js';
13
+ import { persistTranscriptSession } from '../websocket/session-history.js';
14
+ import { DEFAULT_MAX_PARALLEL_AGENTS, getBoardMaxParallelAgents, getBoardQualityGate, resolveActiveBoardId, resolveBoardDir, resolveBoardWorktreePath, tryCompleteBoardIfDone, } from './board-config.js';
15
+ import { ConfigInstaller } from './config-installer.js';
16
+ import { resolveReadyToWork } from './dependency-resolver.js';
17
+ import { loadBoardIssues, loadProjectIssues } from './issue-loader.js';
18
+ import { buildIssuePrompt } from './issue-prompt-builder.js';
19
+ import { runIssueWithRetry } from './issue-retry.js';
20
+ import { appendCancellationNote, extractIssueStatus, recoverStaleIssues, revertIncompleteIssues, updateIssueFrontMatter, validateIssuePath, } from './issue-writer.js';
21
+ import { listExistingDocs, publishOutputs, resolveOutputPath } from './output-manager.js';
22
+ import { NO_SPECS_DIR_ERROR, resolvePmDir } from './parser.js';
23
+ import { resolveIssueExecutionTarget, toExecutionTargetInfo } from './plan-runner-factory.js';
24
+ import { appendProgressEntry, appendProgressNote, ensureOutputDirs } from './progress-log.js';
25
+ import { captureQualityBaseline, computeQualityDelta, persistQualityDelta, qualityGateSkipReason, unavailableQualityDelta, WORKTREE_NOT_CHECKED_REASON, } from './quality-delta.js';
26
+ import { buildCompletionReason, detectDeadState } from './readiness-planner.js';
27
+ import { REVIEW_GATE_EFFORT_LEVEL, REVIEW_GATE_ENGINE, REVIEW_GATE_MODEL, runReviewPipeline, } from './review-gate.js';
28
+ import { renderReviewResultMarkdown } from './review-report.js';
29
+ import { reconcileState } from './state-reconciler.js';
30
+ export const MAX_DISPATCHES_PER_ISSUE = 3;
31
+ const PARK_POLL_MS = 5_000;
32
+ function classifyIssueOutcome(result) {
33
+ if (result.engineNotice?.kind === 'limit')
34
+ return 'limit';
35
+ if (result.engineNotice?.kind === 'auth')
36
+ return 'auth';
37
+ if (result.completed && !result.error)
38
+ return 'completed';
39
+ if (result.error)
40
+ return 'error';
41
+ return 'incomplete';
42
+ }
43
+ const MAX_CONSECUTIVE_EMPTY_WAVES = 3;
44
+ function reviewLine(issueId, status) {
45
+ switch (status) {
46
+ case 'reviewing': return `${issueId} implemented, reviewing.`;
47
+ case 'passed': return `${issueId} passed review.`;
48
+ case 'failed': return `${issueId} failed review, retrying.`;
49
+ case 'max_attempts': return `${issueId} failed review.`;
50
+ }
51
+ }
52
+ const ISSUE_STALL_WARNING_MS = 900_000;
53
+ const ISSUE_STALL_KILL_MS = 1_800_000;
54
+ const ISSUE_STALL_HARD_CAP_MS = 14_400_000;
55
+ const ISSUE_STALL_MAX_EXTENSIONS = 10;
56
+ export class PlanExecutor extends EventEmitter {
57
+ status = 'idle';
58
+ workingDir;
59
+ extraEnv;
60
+ shouldStop = false;
61
+ shouldPause = false;
62
+ waveAbortController = null;
63
+ context;
64
+ lastStartOptions = {};
65
+ configInstaller;
66
+ waveBrowserActivity = new Map();
67
+ aggregateSessionId = null;
68
+ waveTranscripts = [];
69
+ waveEtaProfiles = new Map();
70
+ waveQualityGate = 'advise';
71
+ waveQualityBaselines = new Map();
72
+ etaProfileFor(target) {
73
+ return this.waveEtaProfiles.get(etaTargetKey(target));
74
+ }
75
+ attemptLedger = new Map();
76
+ waveEngineBlock = null;
77
+ metrics = {
78
+ issuesCompleted: 0,
79
+ issuesAttempted: 0,
80
+ totalDuration: 0,
81
+ currentIssueId: null,
82
+ currentWaveIds: [],
83
+ };
84
+ constructor(workingDir, options) {
85
+ super();
86
+ this.workingDir = workingDir;
87
+ this.extraEnv = options?.extraEnv;
88
+ this.configInstaller = new ConfigInstaller(workingDir);
89
+ this.context = this.buildContext({});
90
+ }
91
+ getStatus() { return this.status; }
92
+ getMetrics() { return { ...this.metrics }; }
93
+ getExecutionSnapshot() {
94
+ return { status: this.status, boardId: this.context.boardId };
95
+ }
96
+ startEpic(epicPath) {
97
+ return this.runStart({ epic: epicPath });
98
+ }
99
+ startBoard(boardId, executionDir) {
100
+ return this.runStart({ board: boardId, executionDir });
101
+ }
102
+ start(options = {}) {
103
+ return this.runStart(options);
104
+ }
105
+ pause() { this.shouldPause = true; }
106
+ stop() {
107
+ this.shouldStop = true;
108
+ this.status = 'stopping';
109
+ this.emit('statusChanged', this.status);
110
+ this.waveAbortController?.abort();
111
+ }
112
+ resume() {
113
+ this.shouldPause = false;
114
+ if (this.status !== 'paused')
115
+ return Promise.resolve();
116
+ return this.runStart(this.lastStartOptions);
117
+ }
118
+ async runStart(options) {
119
+ if (this.status === 'executing' || this.status === 'starting')
120
+ return;
121
+ this.lastStartOptions = options;
122
+ this.shouldStop = false;
123
+ this.shouldPause = false;
124
+ this.status = 'starting';
125
+ this.emit('statusChanged', this.status);
126
+ this.context = this.buildContext(options);
127
+ this.attemptLedger = new Map();
128
+ this.waveEngineBlock = null;
129
+ const startTime = Date.now();
130
+ this.aggregateSessionId = `improv-${startTime}-pm-${this.effectiveBoardId() ?? 'run'}`;
131
+ this.status = 'executing';
132
+ this.emit('statusChanged', this.status);
133
+ await this.runStaleRecovery();
134
+ const stallResult = await this.runWaveLoop();
135
+ this.metrics.totalDuration = Date.now() - startTime;
136
+ if (stallResult === 'stalled' || stallResult === 'dead' || stallResult === 'auth') {
137
+ this.status = 'error';
138
+ if (stallResult === 'stalled') {
139
+ this.emit('error', `Execution stalled: ${MAX_CONSECUTIVE_EMPTY_WAVES} consecutive waves completed zero issues. Issues may be stuck in review or failing repeatedly.`);
140
+ }
141
+ }
142
+ else if (this.shouldPause) {
143
+ this.status = 'paused';
144
+ }
145
+ else if (this.shouldStop) {
146
+ this.status = 'idle';
147
+ this.emit('complete', 'Stopped by user');
148
+ }
149
+ else {
150
+ this.status = 'complete';
151
+ }
152
+ this.emit('statusChanged', this.status);
153
+ }
154
+ buildContext(options) {
155
+ const pmDir = resolvePmDir(this.workingDir);
156
+ const boardId = options.board ?? null;
157
+ return {
158
+ workingDir: this.workingDir,
159
+ extraEnv: this.extraEnv,
160
+ epicScope: options.epic ?? null,
161
+ boardId,
162
+ executionDir: options.executionDir ?? null,
163
+ pmDir,
164
+ boardDir: resolveBoardDir(pmDir, boardId),
165
+ };
166
+ }
167
+ emitWarn = (message, issueId) => {
168
+ this.emit('output', { issueId: issueId ?? 'system', text: message, boardId: this.context.boardId ?? null });
169
+ };
170
+ narrate(text) {
171
+ this.emit('output', { issueId: 'system', text });
172
+ }
173
+ async setIssueStatus(issuePath, newStatus) {
174
+ const { pmDir } = this.context;
175
+ if (!pmDir)
176
+ return;
177
+ await updateIssueFrontMatter(pmDir, issuePath, newStatus, this.emitWarn);
178
+ }
179
+ async runWaveLoop() {
180
+ let consecutiveZeroCompletions = 0;
181
+ while (!this.shouldStop && !this.shouldPause) {
182
+ const gate = await this.waitOutEngineBlock();
183
+ if (gate === 'auth')
184
+ return 'auth';
185
+ if (gate === 'interrupted')
186
+ break;
187
+ const { ready, disposition } = await this.pickReadyIssues();
188
+ if (disposition !== 'work')
189
+ return disposition === 'dead' ? 'dead' : 'done';
190
+ if (ready.length === 0)
191
+ continue;
192
+ const maxParallel = await getBoardMaxParallelAgents(this.context.pmDir, this.effectiveBoardId(), this.emitWarn);
193
+ this.waveEngineBlock = null;
194
+ const completedCount = await this.executeWave(ready.slice(0, maxParallel));
195
+ if (completedCount > 0) {
196
+ consecutiveZeroCompletions = 0;
197
+ continue;
198
+ }
199
+ if (this.waveEngineBlock)
200
+ continue;
201
+ consecutiveZeroCompletions++;
202
+ if (consecutiveZeroCompletions >= MAX_CONSECUTIVE_EMPTY_WAVES)
203
+ return 'stalled';
204
+ }
205
+ return 'done';
206
+ }
207
+ async waitOutEngineBlock() {
208
+ const blocker = engineAvailability.automaticDispatchBlocker();
209
+ if (!blocker)
210
+ return 'proceed';
211
+ if (blocker.kind === 'auth') {
212
+ const message = `Execution stopped: ${describeDispatchBlocker(blocker)}.`;
213
+ this.narrate(message);
214
+ this.emit('error', message);
215
+ await appendProgressNote(this.context.pmDir, this.context.boardDir, `**Stopped** — ${describeDispatchBlocker(blocker)}`, this.emitWarn);
216
+ return 'auth';
217
+ }
218
+ return this.parkUntilReset(blocker);
219
+ }
220
+ async parkUntilReset(blocker) {
221
+ const until = blocker.until ?? Date.now() + PARK_POLL_MS;
222
+ this.narrate(`Usage limit reached — waves paused until ${describeParkUntil(until)}.`);
223
+ await appendProgressNote(this.context.pmDir, this.context.boardDir, `**Paused** until ${new Date(until).toISOString()} — ${blocker.notice.text.slice(0, 200)}`, this.emitWarn);
224
+ while (!this.shouldStop && !this.shouldPause) {
225
+ if (!engineAvailability.automaticDispatchBlocker()) {
226
+ this.narrate('Usage limit reset — resuming waves.');
227
+ await appendProgressNote(this.context.pmDir, this.context.boardDir, '**Resumed** after usage limit reset', this.emitWarn);
228
+ return 'proceed';
229
+ }
230
+ await new Promise((resolve) => setTimeout(resolve, PARK_POLL_MS));
231
+ }
232
+ return 'interrupted';
233
+ }
234
+ attemptsFor(issueId) {
235
+ return this.attemptLedger.get(issueId) ?? [];
236
+ }
237
+ recordAttempt(issueId, attempt) {
238
+ const attempts = this.attemptsFor(issueId);
239
+ attempts.push(attempt);
240
+ this.attemptLedger.set(issueId, attempts);
241
+ }
242
+ recordReviewOutcome(issueId, result) {
243
+ this.emit('output', { issueId, text: renderReviewResultMarkdown(result), phase: 'review' });
244
+ const attempts = this.attemptsFor(issueId);
245
+ const last = attempts[attempts.length - 1];
246
+ if (!last)
247
+ return;
248
+ const failures = result.checks.filter((c) => !c.passed).map((c) => ({ name: c.name, details: c.details }));
249
+ if (failures.length > 0)
250
+ last.reviewFailures = failures;
251
+ }
252
+ async escalateCappedIssue(issue) {
253
+ const { pmDir } = this.context;
254
+ const attempts = this.attemptsFor(issue.id).length;
255
+ const reason = `Dispatched ${attempts} times in this run without completing — automatic re-dispatch stopped; review its logs and output before running again`;
256
+ await this.setIssueStatus(issue.path, 'cancelled');
257
+ if (pmDir)
258
+ await appendCancellationNote(pmDir, issue, reason, this.emitWarn);
259
+ this.emit('issueAbandoned', { issueId: issue.id, reason, attempts });
260
+ this.emit('issueError', { issueId: issue.id, error: reason });
261
+ this.narrate(`${issue.id} abandoned after ${attempts} dispatches.`);
262
+ await appendProgressNote(this.context.pmDir, this.context.boardDir, `**${issue.id}** abandoned — ${reason}`, this.emitWarn);
263
+ }
264
+ effectiveBoardId() {
265
+ return this.context.boardId ?? resolveActiveBoardId(this.context.pmDir);
266
+ }
267
+ async executeWave(issues) {
268
+ if (this.shouldStop)
269
+ return 0;
270
+ const waveStart = Date.now();
271
+ const waveIds = issues.map(i => i.id);
272
+ const waveLabel = `wave[${waveIds.join(',')}]`;
273
+ this.metrics.currentWaveIds = waveIds;
274
+ this.metrics.issuesAttempted += issues.length;
275
+ this.waveBrowserActivity = new Map();
276
+ this.waveTranscripts = [];
277
+ this.waveQualityBaselines = new Map();
278
+ this.waveQualityGate = await getBoardQualityGate(this.context.pmDir, this.effectiveBoardId(), this.emitWarn);
279
+ this.emit('waveStarted', { issueIds: waveIds });
280
+ this.waveAbortController = new AbortController();
281
+ const historyDir = resolveHistoryPaths(this.workingDir, '').improviseDir;
282
+ const waveTargets = new Map();
283
+ for (const issue of issues) {
284
+ const t = resolveIssueExecutionTarget(issue);
285
+ const target = { engine: t.engineId, model: t.model, fastMode: t.fastMode };
286
+ waveTargets.set(etaTargetKey(target), target);
287
+ }
288
+ const reviewTarget = { engine: REVIEW_GATE_ENGINE, model: REVIEW_GATE_MODEL };
289
+ waveTargets.set(etaTargetKey(reviewTarget), reviewTarget);
290
+ this.waveEtaProfiles = new Map();
291
+ await Promise.all([...waveTargets].map(async ([key, target]) => {
292
+ const profile = await getEtaProfileCached(historyDir, target).catch(() => null);
293
+ if (profile)
294
+ this.waveEtaProfiles.set(key, profile);
295
+ }));
296
+ await ensureOutputDirs(this.context.pmDir, this.context.boardDir);
297
+ this.configInstaller.installPermissions();
298
+ for (const issue of issues) {
299
+ await this.setIssueStatus(issue.path, 'in_progress');
300
+ }
301
+ const existingDocs = listExistingDocs(this.workingDir, this.context.boardDir);
302
+ const { pmDir } = this.context;
303
+ let completedCount = 0;
304
+ try {
305
+ const abortSignal = this.waveAbortController?.signal;
306
+ const runnerPromises = issues.map(issue => this.runSingleIssue(issue, pmDir, existingDocs, waveLabel, abortSignal));
307
+ const results = await Promise.allSettled(runnerPromises);
308
+ for (let i = 0; i < results.length; i++) {
309
+ const result = results[i];
310
+ if (result.status === 'rejected') {
311
+ this.emit('issueError', {
312
+ issueId: issues[i].id,
313
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
314
+ });
315
+ }
316
+ }
317
+ completedCount = await this.reconcileWaveResults(issues, abortSignal);
318
+ }
319
+ catch (error) {
320
+ this.emit('waveError', {
321
+ issueIds: waveIds,
322
+ error: error instanceof Error ? error.message : String(error),
323
+ });
324
+ if (pmDir)
325
+ await revertIncompleteIssues(pmDir, issues, this.emitWarn);
326
+ }
327
+ finally {
328
+ this.configInstaller.uninstallPermissions();
329
+ }
330
+ this.waveAbortController = null;
331
+ await this.finalizeWave(issues, waveStart, waveLabel);
332
+ this.metrics.currentWaveIds = [];
333
+ return completedCount;
334
+ }
335
+ async runSingleIssue(issue, pmDir, existingDocs, waveLabel, abortSignal) {
336
+ const { executionDir, boardDir, workingDir } = this.context;
337
+ const effectiveDir = executionDir || workingDir;
338
+ const outputPath = resolveOutputPath(issue, workingDir, boardDir);
339
+ const previousAttempts = this.attemptsFor(issue.id);
340
+ const prompt = buildIssuePrompt({
341
+ issue,
342
+ workingDir: effectiveDir,
343
+ pmDir,
344
+ boardDir,
345
+ existingDocs,
346
+ outputPath,
347
+ ...(previousAttempts.length > 0 ? { previousAttempts } : {}),
348
+ qualityGate: this.waveQualityGate,
349
+ });
350
+ const target = resolveIssueExecutionTarget(issue);
351
+ this.emit('issueStarted', issue);
352
+ const issueEtaProfile = this.etaProfileFor({ engine: target.engineId, model: target.model, fastMode: target.fastMode });
353
+ this.emit('issuePrompt', {
354
+ issueId: issue.id,
355
+ prompt,
356
+ executionTarget: toExecutionTargetInfo(target),
357
+ ...(issueEtaProfile ? { etaProfile: issueEtaProfile } : {}),
358
+ });
359
+ const boardLogDir = boardDir ? join(boardDir, 'logs') : undefined;
360
+ const emitOutput = (text) => this.emit('output', { issueId: issue.id, text });
361
+ const qualityBaseline = await this.captureQualityBaseline(issue, emitOutput, abortSignal);
362
+ const execStart = Date.now();
363
+ const result = await runWithFileLogger(`pm-issue-${issue.id}`, () => runIssueWithRetry({
364
+ workingDir: effectiveDir,
365
+ prompt,
366
+ tabId: `${this.context.boardId ?? 'pm'}::${issue.id}`,
367
+ engineId: target.engineId,
368
+ model: target.model,
369
+ effortLevel: target.effortLevel,
370
+ fastMode: target.fastMode,
371
+ stallWarningMs: ISSUE_STALL_WARNING_MS,
372
+ stallKillMs: ISSUE_STALL_KILL_MS,
373
+ stallHardCapMs: ISSUE_STALL_HARD_CAP_MS,
374
+ stallMaxExtensions: ISSUE_STALL_MAX_EXTENSIONS,
375
+ outputCallback: emitOutput,
376
+ thinkingCallback: (text) => emitOutput(legacyThinkingChunk(text)),
377
+ toolUseCallback: (event) => {
378
+ this.emit('toolUse', { issueId: issue.id, event });
379
+ this.recordBrowserToolUse(issue.id, event);
380
+ },
381
+ tokenUsageCallback: (usage) => {
382
+ this.emit('tokenUsage', { issueId: issue.id, usage });
383
+ },
384
+ extraEnv: this.extraEnv,
385
+ abortSignal,
386
+ ...(pmDir ? {
387
+ appendHistory: {
388
+ pmDir,
389
+ issuePath: issue.path,
390
+ issueId: issue.id,
391
+ issueTitle: issue.title,
392
+ execStart,
393
+ },
394
+ } : {}),
395
+ }), boardLogDir);
396
+ this.persistIssueTranscript(issue, prompt, result, execStart, target);
397
+ this.waveTranscripts.push({ issueId: issue.id, title: issue.title, prompt, result, execStart, fastMode: issue.fastMode ?? undefined, engine: target.engineId, model: target.model });
398
+ this.recordDispatch(issue, result, execStart, outputPath);
399
+ if (qualityBaseline) {
400
+ this.waveQualityBaselines.set(issue.id, { baseline: qualityBaseline, writtenFiles: movementWrittenFiles(result) });
401
+ }
402
+ if (!result.completed || result.error) {
403
+ this.emit('output', { issueId: waveLabel, text: `Issue ${issue.id}: ${result.error || 'did not complete'}` });
404
+ }
405
+ }
406
+ persistIssueTranscript(issue, prompt, result, execStart, target) {
407
+ const transcriptSessionId = `improv-${execStart}-pm-${issue.id}`;
408
+ const transcriptPath = persistTranscriptSession(this.context.workingDir, {
409
+ sessionId: transcriptSessionId,
410
+ userPrompt: prompt,
411
+ result,
412
+ execStart,
413
+ fastMode: issue.fastMode ?? undefined,
414
+ engine: target.engineId,
415
+ model: target.model,
416
+ });
417
+ if (transcriptPath) {
418
+ this.emit('issueTranscript', {
419
+ issueId: issue.id,
420
+ tabId: `${this.context.boardId ?? 'pm'}::${issue.id}`,
421
+ sessionId: transcriptSessionId,
422
+ });
423
+ }
424
+ }
425
+ recordDispatch(issue, result, execStart, outputPath) {
426
+ const outcome = classifyIssueOutcome(result);
427
+ this.recordAttempt(issue.id, {
428
+ startedAt: execStart,
429
+ durationMs: Date.now() - execStart,
430
+ outcome,
431
+ ...(result.error ? { error: result.error } : {}),
432
+ ...(result.assistantResponse ? { tail: result.assistantResponse.slice(-800) } : {}),
433
+ ...(result.toolUseHistory?.length ? { toolNames: Array.from(new Set(result.toolUseHistory.map((t) => t.toolName))) } : {}),
434
+ outFile: outputPath,
435
+ });
436
+ if (outcome === 'limit' || outcome === 'auth') {
437
+ this.waveEngineBlock = outcome;
438
+ }
439
+ }
440
+ recordBrowserToolUse(issueId, event) {
441
+ if (event.type !== 'tool_start' && event.type !== 'tool_complete')
442
+ return;
443
+ const name = event.toolName ?? '';
444
+ const isBrowserTool = name.startsWith('browser_') ||
445
+ name.startsWith('minionry-browser__browser_') ||
446
+ name.startsWith('mcp__minionry-browser__browser_');
447
+ if (!isBrowserTool)
448
+ return;
449
+ const entry = this.waveBrowserActivity.get(issueId) ?? { issueId, calls: 0, tools: [] };
450
+ entry.calls += 1;
451
+ if (!entry.tools.includes(name))
452
+ entry.tools.push(name);
453
+ if (event.type === 'tool_start' && event.completeInput) {
454
+ const url = event.completeInput.url;
455
+ if (typeof url === 'string' && url.length > 0) {
456
+ entry.finalUrl = url;
457
+ }
458
+ }
459
+ this.waveBrowserActivity.set(issueId, entry);
460
+ }
461
+ async finalizeWave(issues, waveStart, waveLabel) {
462
+ const { pmDir, boardDir, boardId } = this.context;
463
+ try {
464
+ reconcileState(this.workingDir, boardId ?? undefined);
465
+ this.emit('stateUpdated');
466
+ }
467
+ catch (err) {
468
+ this.emit('output', {
469
+ issueId: waveLabel,
470
+ text: `Warning: state reconciliation failed: ${err instanceof Error ? err.message : String(err)}`,
471
+ });
472
+ }
473
+ try {
474
+ publishOutputs(issues, this.workingDir, boardDir, {
475
+ onWarning: (issueId, text) => this.emit('output', { issueId, text: `Warning: ${text}` }),
476
+ });
477
+ }
478
+ catch (err) {
479
+ this.emit('output', {
480
+ issueId: waveLabel,
481
+ text: `Warning: output publishing failed: ${err instanceof Error ? err.message : String(err)}`,
482
+ });
483
+ }
484
+ try {
485
+ const browserActivity = new Map(this.waveBrowserActivity);
486
+ await appendProgressEntry(pmDir, boardDir, issues, waveStart, this.emitWarn, browserActivity);
487
+ }
488
+ catch (err) {
489
+ this.emit('output', {
490
+ issueId: waveLabel,
491
+ text: `Warning: progress log update failed: ${err instanceof Error ? err.message : String(err)}`,
492
+ });
493
+ }
494
+ this.persistAggregateTranscripts(waveLabel);
495
+ }
496
+ persistAggregateTranscripts(waveLabel) {
497
+ if (!this.aggregateSessionId)
498
+ return;
499
+ try {
500
+ for (const t of this.waveTranscripts) {
501
+ persistTranscriptSession(this.workingDir, {
502
+ sessionId: this.aggregateSessionId,
503
+ userPrompt: t.prompt,
504
+ result: t.result,
505
+ execStart: t.execStart,
506
+ fastMode: t.fastMode,
507
+ engine: t.engine,
508
+ model: t.model,
509
+ });
510
+ }
511
+ }
512
+ catch (err) {
513
+ this.emit('output', {
514
+ issueId: waveLabel,
515
+ text: `Warning: aggregate history persistence failed: ${err instanceof Error ? err.message : String(err)}`,
516
+ });
517
+ }
518
+ }
519
+ async reconcileWaveResults(issues, abortSignal) {
520
+ const { pmDir } = this.context;
521
+ if (!pmDir)
522
+ return 0;
523
+ let completed = 0;
524
+ for (const issue of issues) {
525
+ if (this.shouldStop)
526
+ break;
527
+ const fullPath = validateIssuePath(issue.path, pmDir);
528
+ try {
529
+ const content = await readFile(fullPath, 'utf-8');
530
+ const currentStatus = extractIssueStatus(content) ?? 'unknown';
531
+ if (currentStatus === 'in_review' || currentStatus === 'done') {
532
+ if (await this.finalizeCompletedIssue(issue, pmDir, abortSignal))
533
+ completed++;
534
+ }
535
+ else {
536
+ await this.setIssueStatus(issue.path, issue.status);
537
+ this.emit('issueError', {
538
+ issueId: issue.id,
539
+ error: 'Issue did not complete during wave execution',
540
+ });
541
+ }
542
+ }
543
+ catch {
544
+ this.emit('issueError', { issueId: issue.id, error: 'Could not read issue file after wave' });
545
+ }
546
+ }
547
+ return completed;
548
+ }
549
+ async finalizeCompletedIssue(issue, pmDir, abortSignal) {
550
+ if (issue.reviewGate === 'none') {
551
+ await this.setIssueStatus(issue.path, 'done');
552
+ this.metrics.issuesCompleted++;
553
+ this.emit('issueCompleted', issue);
554
+ this.narrate(`${issue.id} done.`);
555
+ return true;
556
+ }
557
+ const qualityDelta = await this.recordQualityDelta(issue);
558
+ const passed = await runReviewPipeline({
559
+ issue,
560
+ pmDir,
561
+ workingDir: this.workingDir,
562
+ executionDir: this.context.executionDir,
563
+ boardDir: this.context.boardDir,
564
+ extraEnv: this.extraEnv,
565
+ abortSignal,
566
+ qualityDelta,
567
+ }, {
568
+ setStatus: (path, status) => this.setIssueStatus(path, status),
569
+ onReviewPrompt: (issueId, prompt) => {
570
+ const reviewEtaProfile = this.etaProfileFor({ engine: REVIEW_GATE_ENGINE, model: REVIEW_GATE_MODEL });
571
+ this.emit('issuePrompt', {
572
+ issueId,
573
+ prompt,
574
+ phase: 'review',
575
+ executionTarget: { engine: REVIEW_GATE_ENGINE, model: REVIEW_GATE_MODEL, effortLevel: REVIEW_GATE_EFFORT_LEVEL },
576
+ ...(reviewEtaProfile ? { etaProfile: reviewEtaProfile } : {}),
577
+ });
578
+ },
579
+ onOutput: (issueId, text) => this.emit('output', { issueId, text, phase: 'review' }),
580
+ onReviewProgress: (issueId, status) => { this.emit('reviewProgress', { issueId, status }); this.narrate(reviewLine(issueId, status)); },
581
+ onIssueAbandoned: (issueId, reason, attempts) => { this.emit('issueAbandoned', { issueId, reason, attempts }); this.narrate(`${issueId} abandoned after ${attempts} attempt${attempts === 1 ? '' : 's'}.`); },
582
+ onIssueCompleted: (completedIssue) => this.emit('issueCompleted', completedIssue),
583
+ onIssueError: (issueId, error) => this.emit('issueError', { issueId, error, phase: 'review' }),
584
+ onReviewResult: (issueId, result) => this.recordReviewOutcome(issueId, result),
585
+ warn: this.emitWarn,
586
+ });
587
+ if (passed)
588
+ this.metrics.issuesCompleted++;
589
+ return passed;
590
+ }
591
+ runsInWorktree() {
592
+ const { executionDir, pmDir } = this.context;
593
+ if (!executionDir)
594
+ return false;
595
+ const worktree = resolveBoardWorktreePath(pmDir, this.effectiveBoardId());
596
+ return worktree !== null && resolve(worktree) === resolve(executionDir);
597
+ }
598
+ async captureQualityBaseline(issue, emitOutput, abortSignal) {
599
+ if (qualityGateSkipReason(issue, this.waveQualityGate) !== null)
600
+ return null;
601
+ if (this.runsInWorktree())
602
+ return { available: false, reason: WORKTREE_NOT_CHECKED_REASON };
603
+ const outcome = await captureQualityBaseline({ spaceRoot: this.workingDir, signal: abortSignal }, issue);
604
+ if (outcome.available) {
605
+ const { fingerprints, checked, unchecked } = outcome.baseline;
606
+ const skipped = unchecked.length > 0 ? `, ${unchecked.length} unchecked` : '';
607
+ emitOutput(`Quality baseline: ${fingerprints.length} active finding(s) in ${checked.length} file(s)${skipped}.\n`);
608
+ }
609
+ else {
610
+ emitOutput(`Quality baseline unavailable: ${outcome.reason}\n`);
611
+ }
612
+ return outcome;
613
+ }
614
+ async recordQualityDelta(issue) {
615
+ const gate = this.waveQualityGate;
616
+ if (gate === 'off' || qualityGateSkipReason(issue, gate) !== null)
617
+ return null;
618
+ const { boardDir } = this.context;
619
+ if (!boardDir)
620
+ return null;
621
+ const kept = this.waveQualityBaselines.get(issue.id);
622
+ const record = this.runsInWorktree()
623
+ ? unavailableQualityDelta(issue.id, gate, WORKTREE_NOT_CHECKED_REASON)
624
+ : await computeQualityDelta({ spaceRoot: this.workingDir }, {
625
+ issue,
626
+ gate,
627
+ baseline: kept?.baseline ?? { available: false, reason: 'no baseline was captured before this issue ran' },
628
+ writtenFiles: kept?.writtenFiles ?? [],
629
+ });
630
+ try {
631
+ persistQualityDelta(boardDir, record);
632
+ }
633
+ catch (err) {
634
+ this.emitWarn(`Warning: failed to persist quality delta for ${issue.id}: ${err instanceof Error ? err.message : String(err)}`, issue.id);
635
+ }
636
+ const line = record.available
637
+ ? `Quality delta: ${record.introducedCount} introduced finding(s) in ${record.checked.length} file(s)${record.unchecked.length > 0 ? `, ${record.unchecked.length} unchecked` : ''}.\n`
638
+ : `Quality delta unavailable: ${record.reason}\n`;
639
+ this.emit('output', { issueId: issue.id, text: line });
640
+ return record;
641
+ }
642
+ async runStaleRecovery() {
643
+ const { pmDir } = this.context;
644
+ if (!pmDir)
645
+ return;
646
+ const { issues } = await this.loadScopedIssues(pmDir);
647
+ if (!issues)
648
+ return;
649
+ const recovered = await recoverStaleIssues(pmDir, issues, this.emitWarn);
650
+ if (recovered.length > 0) {
651
+ this.emit('output', {
652
+ issueId: 'recovery',
653
+ text: `Recovered ${recovered.length} issue${recovered.length > 1 ? 's' : ''} from previous interrupted execution: ${recovered.join(', ')}`,
654
+ });
655
+ this.emit('stateUpdated');
656
+ }
657
+ }
658
+ async pickReadyIssues() {
659
+ const { pmDir, epicScope } = this.context;
660
+ if (!pmDir) {
661
+ this.emit('error', NO_SPECS_DIR_ERROR);
662
+ return { ready: [], disposition: 'dead' };
663
+ }
664
+ const { issues, boardId } = await this.loadScopedIssues(pmDir);
665
+ if (!issues)
666
+ return { ready: [], disposition: 'dead' };
667
+ const readyAll = resolveReadyToWork(issues, epicScope ?? undefined);
668
+ const capped = readyAll.filter((i) => this.attemptsFor(i.id).length >= MAX_DISPATCHES_PER_ISSUE);
669
+ for (const issue of capped)
670
+ await this.escalateCappedIssue(issue);
671
+ const ready = readyAll.filter((i) => !capped.includes(i));
672
+ if (readyAll.length > 0)
673
+ return { ready, disposition: 'work' };
674
+ const deadState = detectDeadState(issues);
675
+ if (deadState) {
676
+ this.emit('error', deadState);
677
+ return { ready: [], disposition: 'dead' };
678
+ }
679
+ this.emit('complete', buildCompletionReason(issues, epicScope));
680
+ if (boardId) {
681
+ await tryCompleteBoardIfDone(pmDir, boardId, issues, this.emitWarn);
682
+ }
683
+ return { ready: [], disposition: 'done' };
684
+ }
685
+ async loadScopedIssues(pmDir) {
686
+ const boardId = this.effectiveBoardId();
687
+ const issues = boardId
688
+ ? await loadBoardIssues(pmDir, boardId, {
689
+ onError: msg => this.emit('error', msg),
690
+ warn: this.emitWarn,
691
+ })
692
+ : loadProjectIssues(this.workingDir, { onError: msg => this.emit('error', msg) });
693
+ return { issues, boardId };
694
+ }
695
+ }
696
+ export { DEFAULT_MAX_PARALLEL_AGENTS };