@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,409 @@
1
+ // Copyright (c) 2025-present Minionry, Inc.
2
+ // SPDX-License-Identifier: LicenseRef-Minionry-Software
3
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { persistTranscriptSession } from '../websocket/session-history.js';
6
+ import { loadAgentPrompt } from './agent-loader.js';
7
+ import { resolveIsCodeTask } from './issue-classification.js';
8
+ import { appendCancellationNote, appendOutputHistoryEntry } from './issue-writer.js';
9
+ import { resolveOutputPath } from './output-manager.js';
10
+ import { createPlanRunner } from './plan-runner-factory.js';
11
+ import { QUALITY_DELTA_MAX_FINDINGS, qualityDeltaDir } from './quality-delta.js';
12
+ import { REVIEW_INCOMPLETE_CHECK } from './review-report.js';
13
+ import { balancedObjectEnd, createVerdictStream, parseVerdictObject } from './review-verdict-stream.js';
14
+ import { MAX_REVIEW_ATTEMPTS } from './types.js';
15
+ export { MAX_REVIEW_ATTEMPTS };
16
+ export const REVIEW_GATE_ENGINE = 'claude-code';
17
+ export const REVIEW_GATE_MODEL = 'fable';
18
+ export const REVIEW_GATE_EFFORT_LEVEL = 'max';
19
+ export async function reviewIssue(options) {
20
+ const { workingDir, issue, pmDir, outputPath, onOutput, onPrompt, onComplete, logDir, boardDir, qualityDelta } = options;
21
+ const isCodeTask = resolveIsCodeTask(issue);
22
+ const issueType = isCodeTask ? 'code' : 'non-code';
23
+ try {
24
+ const prompt = buildReviewPrompt(issue, pmDir, outputPath, isCodeTask, boardDir, workingDir, qualityDelta);
25
+ onPrompt?.(prompt);
26
+ if (issue.reviewModel && issue.reviewModel !== REVIEW_GATE_MODEL) {
27
+ onOutput?.(`Review model '${issue.reviewModel}' overridden — the review gate is pinned to ${REVIEW_GATE_MODEL} at ${REVIEW_GATE_EFFORT_LEVEL} effort.\n`);
28
+ }
29
+ const stream = createVerdictStream(onOutput);
30
+ const runner = createPlanRunner('review', {
31
+ workingDir,
32
+ prompt,
33
+ engineId: REVIEW_GATE_ENGINE,
34
+ model: REVIEW_GATE_MODEL,
35
+ effortLevel: REVIEW_GATE_EFFORT_LEVEL,
36
+ verbose: true,
37
+ outputCallback: (text) => stream.write(text),
38
+ extraEnv: options.extraEnv,
39
+ abortSignal: options.abortSignal,
40
+ logLabel: 'pm-review',
41
+ logDir,
42
+ });
43
+ const result = await runner.run().finally(() => stream.flush());
44
+ await onComplete?.(result, prompt);
45
+ if (result.completed && result.assistantResponse) {
46
+ const verdict = applyQualityGate(await resolveVerdict(options, issueType, result.assistantResponse), qualityDelta);
47
+ const blocked = verdict.checks.find((c) => c.name === QUALITY_DELTA_CHECK && !c.passed);
48
+ if (blocked)
49
+ onOutput?.(`\nQuality gate (block): ${blocked.details}\n`);
50
+ return verdict;
51
+ }
52
+ return incompleteReviewResult(issue.id, issueType, options.abortSignal?.aborted ? 'Review aborted by user stop' : 'Review runner did not complete');
53
+ }
54
+ catch (err) {
55
+ return incompleteReviewResult(issue.id, issueType, options.abortSignal?.aborted
56
+ ? 'Review aborted by user stop'
57
+ : `Review threw an error: ${err instanceof Error ? err.message : String(err)}`);
58
+ }
59
+ }
60
+ async function resolveVerdict(options, issueType, reviewerOutput) {
61
+ const parsed = parseReviewOutput(options.issue.id, issueType, reviewerOutput);
62
+ if (!hasFailedChecksWithoutDetails(parsed))
63
+ return parsed;
64
+ options.onOutput?.('\nReview verdict listed failed checks without details — asking the reviewer for reasons.\n');
65
+ const followUp = await runDetailsFollowUp(options, issueType, parsed, reviewerOutput);
66
+ if (followUp && !hasFailedChecksWithoutDetails(followUp))
67
+ return followUp;
68
+ return incompleteReviewResult(options.issue.id, issueType, 'Reviewer reported failed checks without details');
69
+ }
70
+ export function getReviewAttemptCount(boardOrSandboxDir, issue) {
71
+ if (!boardOrSandboxDir)
72
+ return 0;
73
+ const reviewsDir = join(boardOrSandboxDir, 'reviews');
74
+ if (!existsSync(reviewsDir))
75
+ return 0;
76
+ try {
77
+ return readdirSync(reviewsDir).filter(f => f.startsWith(issue.id) && f.endsWith('.json')).length;
78
+ }
79
+ catch {
80
+ return 0;
81
+ }
82
+ }
83
+ export function persistReviewResult(boardOrSandboxDir, issue, result) {
84
+ if (!boardOrSandboxDir)
85
+ return;
86
+ const reviewsDir = join(boardOrSandboxDir, 'reviews');
87
+ if (!existsSync(reviewsDir))
88
+ mkdirSync(reviewsDir, { recursive: true });
89
+ try {
90
+ writeFileSync(join(reviewsDir, `${issue.id}-${Date.now()}.json`), JSON.stringify(result, null, 2), 'utf-8');
91
+ }
92
+ catch { }
93
+ }
94
+ export function appendReviewFeedback(pmDir, issue, result) {
95
+ const fullPath = join(pmDir, issue.path);
96
+ try {
97
+ let content = readFileSync(fullPath, 'utf-8');
98
+ const failedChecks = result.checks.filter(c => !c.passed);
99
+ const feedback = failedChecks.map(c => ` - ${c.name}: ${c.details}`).join('\n');
100
+ const entry = `- Review failed (${new Date().toISOString().split('T')[0]}): ${failedChecks.length} check(s) failed\n${feedback}`;
101
+ if (content.includes('## Activity')) {
102
+ content = content.replace(/## Activity/, `## Activity\n${entry}`);
103
+ }
104
+ else {
105
+ content += `\n\n## Activity\n${entry}`;
106
+ }
107
+ writeFileSync(fullPath, content, 'utf-8');
108
+ }
109
+ catch { }
110
+ }
111
+ function balancedObjectAt(text, start) {
112
+ const end = balancedObjectEnd(text, start);
113
+ return end === -1 ? null : text.slice(start, end);
114
+ }
115
+ export function extractReviewVerdict(output) {
116
+ return verdictFromFences(output) ?? lastTopLevelVerdict(output);
117
+ }
118
+ function verdictFromFences(output) {
119
+ const fenced = /```(?:json)?\s*([\s\S]*?)```/g;
120
+ let found = null;
121
+ for (let match = fenced.exec(output); match; match = fenced.exec(output)) {
122
+ const body = match[1].trim();
123
+ found = parseVerdictObject(body) ?? verdictInText(body) ?? found;
124
+ }
125
+ return found;
126
+ }
127
+ function verdictInText(text) {
128
+ const braced = text.indexOf('{');
129
+ if (braced === -1)
130
+ return null;
131
+ const obj = balancedObjectAt(text, braced);
132
+ return obj ? parseVerdictObject(obj) : null;
133
+ }
134
+ function lastTopLevelVerdict(output) {
135
+ let last = null;
136
+ let i = output.indexOf('{');
137
+ while (i !== -1) {
138
+ const obj = balancedObjectAt(output, i);
139
+ const parsed = obj?.includes('"passed"') ? parseVerdictObject(obj) : null;
140
+ if (obj && parsed) {
141
+ last = parsed;
142
+ i = output.indexOf('{', i + obj.length);
143
+ continue;
144
+ }
145
+ i = output.indexOf('{', i + 1);
146
+ }
147
+ return last;
148
+ }
149
+ export function parseReviewOutput(issueId, issueType, output) {
150
+ const parsed = extractReviewVerdict(output);
151
+ if (parsed) {
152
+ return {
153
+ issueId,
154
+ issueType,
155
+ passed: !!parsed.passed,
156
+ checks: Array.isArray(parsed.checks) ? parsed.checks.map((c) => ({
157
+ name: String(c.name ?? 'unknown'),
158
+ passed: !!c.passed,
159
+ details: String(c.details ?? ''),
160
+ })) : [],
161
+ reviewedAt: new Date().toISOString(),
162
+ };
163
+ }
164
+ return incompleteReviewResult(issueId, issueType, 'Could not parse review output');
165
+ }
166
+ export function hasFailedChecksWithoutDetails(result) {
167
+ if (result.passed || isIncompleteReview(result))
168
+ return false;
169
+ return result.checks.some((c) => !c.passed && c.details.trim().length === 0);
170
+ }
171
+ async function runDetailsFollowUp(options, issueType, verdict, previousOutput) {
172
+ const failedNames = verdict.checks.filter((c) => !c.passed).map((c) => c.name);
173
+ const prompt = [
174
+ `You reviewed issue ${options.issue.id} (${options.issue.title}) and returned this verdict:`,
175
+ '```',
176
+ previousOutput.slice(-4000),
177
+ '```',
178
+ '',
179
+ `The failed checks ${failedNames.join(', ')} carry no details. The implementing agent cannot act on a bare label.`,
180
+ 'Restate the SAME verdict as one JSON object with, for every failed check, a specific `details` string naming what is wrong and what would satisfy the check (file, criterion, behaviour). Do not change which checks passed or failed.',
181
+ 'Output EXACTLY one JSON object on its own line (no markdown fencing):',
182
+ '{"passed": false, "checks": [{"name": "criteria_met", "passed": false, "details": "..."}]}',
183
+ ].join('\n');
184
+ try {
185
+ const stream = createVerdictStream(options.onOutput);
186
+ const runner = createPlanRunner('review', {
187
+ workingDir: options.workingDir,
188
+ prompt,
189
+ engineId: REVIEW_GATE_ENGINE,
190
+ model: REVIEW_GATE_MODEL,
191
+ effortLevel: REVIEW_GATE_EFFORT_LEVEL,
192
+ verbose: true,
193
+ inferenceOnly: true,
194
+ policy: 'NONE',
195
+ outputCallback: (text) => stream.write(text),
196
+ extraEnv: options.extraEnv,
197
+ abortSignal: options.abortSignal,
198
+ logLabel: 'pm-review-details',
199
+ logDir: options.logDir,
200
+ });
201
+ const result = await runner.run().finally(() => stream.flush());
202
+ if (!result.completed || !result.assistantResponse)
203
+ return null;
204
+ const parsed = parseReviewOutput(options.issue.id, issueType, result.assistantResponse);
205
+ return isIncompleteReview(parsed) ? null : parsed;
206
+ }
207
+ catch {
208
+ return null;
209
+ }
210
+ }
211
+ export function incompleteReviewResult(issueId, issueType, reason) {
212
+ return {
213
+ issueId,
214
+ issueType,
215
+ passed: false,
216
+ checks: [{ name: REVIEW_INCOMPLETE_CHECK, passed: false, details: reason }],
217
+ reviewedAt: new Date().toISOString(),
218
+ };
219
+ }
220
+ export function isIncompleteReview(result) {
221
+ return !result.passed && result.checks.length === 1 && result.checks[0]?.name === REVIEW_INCOMPLETE_CHECK;
222
+ }
223
+ export const QUALITY_DELTA_CHECK = 'quality_delta';
224
+ const BLOCKING_SEVERITIES = new Set(['critical', 'high']);
225
+ const BLOCKING_CATEGORIES = new Set(['security', 'secrets', 'dependencies']);
226
+ export const QUALITY_DELTA_PROMPT_MAX_FINDINGS = 25;
227
+ export const QUALITY_DELTA_PROMPT_MAX_UNCHECKED = 20;
228
+ export const QUALITY_DELTA_PROMPT_MAX_CHARS = 6_000;
229
+ const QUALITY_DELTA_PROMPT_RESERVE = 800;
230
+ const QUALITY_DELTA_PROMPT_UNCHECKED_SHARE = 1_200;
231
+ const QUALITY_DELTA_PROMPT_CLIP = 200;
232
+ const QUALITY_DELTA_CHECK_MAX_FINDINGS = 10;
233
+ export function isBlockingQualityFinding(finding) {
234
+ return BLOCKING_SEVERITIES.has(finding.severity) || BLOCKING_CATEGORIES.has(finding.category);
235
+ }
236
+ function blockingQualityFindings(delta) {
237
+ if (!delta?.available || delta.gate !== 'block')
238
+ return [];
239
+ return delta.introduced.filter(isBlockingQualityFinding);
240
+ }
241
+ function clip(text, max) {
242
+ return text.length > max ? `${text.slice(0, max - 1)}…` : text;
243
+ }
244
+ function describeQualityFinding(finding) {
245
+ const where = finding.line === null ? finding.path : `${finding.path}:${finding.line}`;
246
+ return `[${finding.severity}] ${finding.category} ${where} — ${clip(finding.title, QUALITY_DELTA_PROMPT_CLIP)}`;
247
+ }
248
+ export function applyQualityGate(result, delta) {
249
+ if (isIncompleteReview(result))
250
+ return result;
251
+ const blockers = blockingQualityFindings(delta);
252
+ if (blockers.length === 0)
253
+ return result;
254
+ const listed = blockers.slice(0, QUALITY_DELTA_CHECK_MAX_FINDINGS).map(describeQualityFinding).join('; ');
255
+ const more = blockers.length > QUALITY_DELTA_CHECK_MAX_FINDINGS ? `; and ${blockers.length - QUALITY_DELTA_CHECK_MAX_FINDINGS} more` : '';
256
+ const details = `${blockers.length} introduced finding(s) fail the board's quality gate (block: critical or high severity, or category security / secrets / dependencies): ${listed}${more}. Fix each before the next review.`;
257
+ return {
258
+ ...result,
259
+ passed: false,
260
+ checks: [...result.checks.filter((c) => c.name !== QUALITY_DELTA_CHECK), { name: QUALITY_DELTA_CHECK, passed: false, details }],
261
+ };
262
+ }
263
+ function takeWithinBudget(items, render, budget) {
264
+ const lines = [];
265
+ for (const item of items) {
266
+ const line = render(item);
267
+ if (line.length + 1 > budget.left)
268
+ break;
269
+ lines.push(line);
270
+ budget.left -= line.length + 1;
271
+ }
272
+ return lines;
273
+ }
274
+ export function renderQualityDeltaSection(delta, boardDir) {
275
+ if (!delta) {
276
+ return "Not recorded for this issue — the board's `quality_gate` is `off`, or this run has no board. That is not evidence either way.";
277
+ }
278
+ if (!delta.available) {
279
+ return `Gate mode: ${delta.gate}. Unavailable — ${delta.reason}. No file was checked, so this is not a clean result: weigh the code on your own reading. An unavailable delta never fails a review.`;
280
+ }
281
+ const blocking = new Set(blockingQualityFindings(delta));
282
+ const budget = { left: QUALITY_DELTA_PROMPT_MAX_CHARS - QUALITY_DELTA_PROMPT_RESERVE - QUALITY_DELTA_PROMPT_UNCHECKED_SHARE };
283
+ const findingLines = takeWithinBudget(delta.introduced.slice(0, QUALITY_DELTA_PROMPT_MAX_FINDINGS), (finding) => {
284
+ const mark = blocking.has(finding) ? ' (BLOCKING)' : '';
285
+ const suggestion = finding.suggestion ? `\n suggestion: ${clip(finding.suggestion, QUALITY_DELTA_PROMPT_CLIP)}` : '';
286
+ return `- ${describeQualityFinding(finding)}${mark}${suggestion}`;
287
+ }, budget);
288
+ budget.left += QUALITY_DELTA_PROMPT_UNCHECKED_SHARE;
289
+ const uncheckedLines = takeWithinBudget(delta.unchecked.slice(0, QUALITY_DELTA_PROMPT_MAX_UNCHECKED), (entry) => `- ${entry.path} — ${entry.reason}`, budget);
290
+ const record = boardDir ? join(qualityDeltaDir(boardDir), `${delta.issueId}-*.json`) : `<board>/quality/${delta.issueId}-*.json`;
291
+ const created = delta.createdFiles.length > 0 ? `, ${delta.createdFiles.length} created file(s) (every finding in a created file counts as introduced)` : '';
292
+ const lines = [
293
+ `Gate mode: ${delta.gate}. Baseline captured ${delta.baselineAt}; ${delta.checked.length} file(s) checked, ${delta.introducedCount} introduced finding(s), ${delta.unchecked.length} unchecked file(s)${created}.`,
294
+ ];
295
+ if (delta.introducedCount === 0) {
296
+ lines.push('No introduced findings.');
297
+ }
298
+ else {
299
+ if (delta.gate === 'block') {
300
+ lines.push(blocking.size > 0
301
+ ? `${blocking.size} of them fail this review whatever your verdict (critical or high severity, or category security / secrets / dependencies) — marked BLOCKING below.`
302
+ : 'None is critical or high, or in category security / secrets / dependencies, so the gate does not fail this review on its own.');
303
+ }
304
+ lines.push('', `Introduced findings, most severe first — showing ${findingLines.length} of ${delta.introducedCount} (this section is capped at ${QUALITY_DELTA_PROMPT_MAX_FINDINGS} findings and ${QUALITY_DELTA_PROMPT_MAX_CHARS} characters; the record at ${record} lists up to ${QUALITY_DELTA_MAX_FINDINGS}):`, ...findingLines);
305
+ }
306
+ if (delta.unchecked.length > 0) {
307
+ lines.push('', `Unchecked files — no analyzer read these, so they are unchecked, not clean (showing ${uncheckedLines.length} of ${delta.unchecked.length}):`, ...uncheckedLines);
308
+ }
309
+ return lines.join('\n');
310
+ }
311
+ function buildReviewPrompt(issue, pmDir, outputPath, isCodeTask, boardDir, workingDir, qualityDelta) {
312
+ const criteria = issue.acceptanceCriteria
313
+ .map(c => `- [${c.checked ? 'x' : ' '}] ${c.text}`)
314
+ .join('\n');
315
+ const criteriaStr = criteria || 'No specific criteria defined.';
316
+ const filesModified = issue.filesToModify.map(f => `- ${f}`).join('\n');
317
+ const issueSpecPath = join(pmDir, issue.path);
318
+ if (isCodeTask) {
319
+ const result = loadAgentPrompt('review-code', {
320
+ issue_id: issue.id,
321
+ issue_title: issue.title,
322
+ files_modified: filesModified,
323
+ acceptance_criteria: criteriaStr,
324
+ output_path: outputPath,
325
+ quality_delta: renderQualityDeltaSection(qualityDelta, boardDir),
326
+ }, boardDir, workingDir);
327
+ if (result)
328
+ return result;
329
+ }
330
+ const result = loadAgentPrompt('review-quality', {
331
+ issue_id: issue.id,
332
+ issue_title: issue.title,
333
+ output_path: outputPath,
334
+ issue_spec_path: issueSpecPath,
335
+ acceptance_criteria: criteriaStr,
336
+ }, boardDir, workingDir);
337
+ if (result)
338
+ return result;
339
+ 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": "..."}]}`;
340
+ }
341
+ export async function runReviewPipeline(options, callbacks) {
342
+ const { issue, pmDir, workingDir, executionDir, boardDir, extraEnv, abortSignal, qualityDelta } = options;
343
+ const reviewDir = boardDir ?? pmDir;
344
+ const attempts = getReviewAttemptCount(reviewDir, issue);
345
+ if (attempts >= MAX_REVIEW_ATTEMPTS) {
346
+ await callbacks.setStatus(issue.path, 'cancelled');
347
+ await appendCancellationNote(pmDir, issue, `Cancelled after ${MAX_REVIEW_ATTEMPTS} failed reviews — issue may need restructuring`, callbacks.warn);
348
+ callbacks.onReviewProgress(issue.id, 'max_attempts');
349
+ callbacks.onIssueAbandoned(issue.id, `Review failed ${MAX_REVIEW_ATTEMPTS} times — cancelled to unblock dependents`, attempts);
350
+ callbacks.onOutput(issue.id, 'Review: max attempts reached, cancelling issue to unblock dependents');
351
+ return false;
352
+ }
353
+ await callbacks.setStatus(issue.path, 'in_review');
354
+ callbacks.onReviewProgress(issue.id, 'reviewing');
355
+ const outputPath = resolveOutputPath(issue, workingDir, boardDir);
356
+ const reviewStart = Date.now();
357
+ const reviewSessionId = `improv-${reviewStart}-pm-${issue.id}-review`;
358
+ const result = await reviewIssue({
359
+ workingDir: executionDir || workingDir,
360
+ issue,
361
+ pmDir,
362
+ outputPath,
363
+ onOutput: (text) => callbacks.onOutput(issue.id, text),
364
+ onPrompt: (prompt) => callbacks.onReviewPrompt(issue.id, prompt),
365
+ onComplete: async (sessionResult, prompt) => {
366
+ persistTranscriptSession(workingDir, {
367
+ sessionId: reviewSessionId,
368
+ userPrompt: prompt,
369
+ result: sessionResult,
370
+ execStart: reviewStart,
371
+ engine: REVIEW_GATE_ENGINE,
372
+ model: REVIEW_GATE_MODEL,
373
+ });
374
+ await appendOutputHistoryEntry(pmDir, issue.path, {
375
+ sessionId: reviewSessionId,
376
+ title: `${issue.id} review`,
377
+ timestamp: new Date(reviewStart).toISOString(),
378
+ status: sessionResult.completed && !sessionResult.error ? 'done' : 'failed',
379
+ }, callbacks.warn);
380
+ },
381
+ logDir: boardDir ? join(boardDir, 'logs') : undefined,
382
+ boardDir,
383
+ extraEnv,
384
+ abortSignal,
385
+ qualityDelta,
386
+ });
387
+ const reviewFinish = Date.now();
388
+ if (!isIncompleteReview(result)) {
389
+ const stamped = { ...result, startedAt: new Date(reviewStart).toISOString(), finishedAt: new Date(reviewFinish).toISOString() };
390
+ persistReviewResult(reviewDir, issue, stamped);
391
+ callbacks.onReviewResult?.(issue.id, stamped);
392
+ }
393
+ if (result.passed) {
394
+ await callbacks.setStatus(issue.path, 'done');
395
+ callbacks.onReviewProgress(issue.id, 'passed');
396
+ callbacks.onIssueCompleted(issue);
397
+ return true;
398
+ }
399
+ if (isIncompleteReview(result)) {
400
+ callbacks.onOutput(issue.id, `Review: ${result.checks[0]?.details ?? 'incomplete'} — leaving for retry.`);
401
+ callbacks.onIssueError(issue.id, `Review incomplete: ${result.checks[0]?.details ?? 'no verdict'}`);
402
+ return false;
403
+ }
404
+ await callbacks.setStatus(issue.path, 'todo');
405
+ appendReviewFeedback(pmDir, issue, result);
406
+ callbacks.onReviewProgress(issue.id, 'failed');
407
+ callbacks.onIssueError(issue.id, `Review failed: ${result.checks.filter(c => !c.passed).map(c => c.name).join(', ')}`);
408
+ return false;
409
+ }
@@ -0,0 +1,134 @@
1
+ // Copyright (c) 2025-present Minionry, Inc.
2
+ // SPDX-License-Identifier: LicenseRef-Minionry-Software
3
+ export const REVIEW_INCOMPLETE_CHECK = 'review_incomplete';
4
+ const CHECK_LABELS = {
5
+ criteria_met: 'Acceptance criteria',
6
+ code_quality: 'Code quality',
7
+ no_obvious_bugs: 'No obvious bugs',
8
+ output_quality: 'Output quality',
9
+ completeness: 'Completeness',
10
+ [REVIEW_INCOMPLETE_CHECK]: 'Review incomplete',
11
+ };
12
+ export function reviewCheckLabel(name) {
13
+ const known = CHECK_LABELS[name];
14
+ if (known)
15
+ return known;
16
+ const words = name.replace(/[_-]+/g, ' ').trim();
17
+ if (!words)
18
+ return 'Check';
19
+ return words.charAt(0).toUpperCase() + words.slice(1);
20
+ }
21
+ export function toReviewReport(result) {
22
+ const checks = Array.isArray(result.checks) ? result.checks : [];
23
+ const verdict = verdictOf(result, checks);
24
+ const sections = verdict === 'incomplete' ? [] : failuresFirst(checks).map(toSection);
25
+ return {
26
+ issueId: result.issueId,
27
+ issueType: result.issueType,
28
+ verdict,
29
+ passedCount: checks.filter(c => c.passed).length,
30
+ totalCount: checks.length,
31
+ failedLabels: sections.filter(s => !s.passed).map(s => s.label),
32
+ sections,
33
+ reviewedAt: result.reviewedAt,
34
+ autoPass: result.autoPass === true,
35
+ incompleteReason: verdict === 'incomplete' ? (checks[0]?.details ?? '').trim() : '',
36
+ };
37
+ }
38
+ const VERDICT_TITLES = {
39
+ passed: '✅ Review passed',
40
+ failed: '❌ Review failed',
41
+ incomplete: '⚠️ Review incomplete',
42
+ };
43
+ export class MarkdownReviewReportRenderer {
44
+ render(report) {
45
+ const lines = ['', '', '---', '', `### ${VERDICT_TITLES[report.verdict]} — ${report.issueId}`, '', subtitle(report), ''];
46
+ if (report.verdict === 'incomplete') {
47
+ lines.push(report.incompleteReason || 'The reviewer returned no verdict.', '', 'Nothing was marked done — the issue stays in review for a retry.', '');
48
+ return lines.join('\n');
49
+ }
50
+ for (const section of report.sections)
51
+ lines.push(...renderSection(section));
52
+ return lines.join('\n');
53
+ }
54
+ }
55
+ const defaultRenderer = new MarkdownReviewReportRenderer();
56
+ export function renderReviewResultMarkdown(result, renderer = defaultRenderer) {
57
+ return renderer.render(toReviewReport(result));
58
+ }
59
+ function verdictOf(result, checks) {
60
+ if (result.passed)
61
+ return 'passed';
62
+ if (checks.length === 1 && checks[0]?.name === REVIEW_INCOMPLETE_CHECK)
63
+ return 'incomplete';
64
+ return 'failed';
65
+ }
66
+ function failuresFirst(checks) {
67
+ return [...checks.filter(c => !c.passed), ...checks.filter(c => c.passed)];
68
+ }
69
+ function toSection(check) {
70
+ return {
71
+ name: check.name,
72
+ label: reviewCheckLabel(check.name),
73
+ passed: check.passed,
74
+ details: (check.details ?? '').trim(),
75
+ };
76
+ }
77
+ function subtitle(report) {
78
+ const parts = [];
79
+ if (report.verdict === 'passed') {
80
+ parts.push(`**${report.passedCount} of ${report.totalCount} checks passed**`);
81
+ }
82
+ else if (report.verdict === 'failed') {
83
+ const failed = report.totalCount - report.passedCount;
84
+ const named = report.failedLabels.length > 0 ? ` — ${report.failedLabels.join(', ')}` : '';
85
+ parts.push(`**${failed} of ${report.totalCount} checks failed**${named}`);
86
+ }
87
+ parts.push(report.issueType === 'code' ? 'code review' : 'quality review');
88
+ if (report.autoPass)
89
+ parts.push('_auto-passed on an infrastructure failure_');
90
+ const when = formatReviewedAt(report.reviewedAt);
91
+ if (when)
92
+ parts.push(when);
93
+ return parts.join(' · ');
94
+ }
95
+ function renderSection(section) {
96
+ const head = `#### ${section.passed ? '✅' : '❌'} ${section.label}`;
97
+ const body = section.details ? renderDetailBody(section.details) : ['_The reviewer gave no reason._'];
98
+ return [head, '', ...body, ''];
99
+ }
100
+ export function renderDetailBody(details) {
101
+ const facets = splitEnumeratedFacets(details);
102
+ if (!facets)
103
+ return [details];
104
+ const out = [];
105
+ if (facets.preamble)
106
+ out.push(facets.preamble, '');
107
+ for (const item of facets.items)
108
+ out.push(`- **${item.label}** — ${item.text}`);
109
+ return out;
110
+ }
111
+ export function splitEnumeratedFacets(details) {
112
+ const marker = /(^|[\s([])(AC\s?\d+)\s*[:.]\s+/g;
113
+ const marks = [];
114
+ for (let m = marker.exec(details); m; m = marker.exec(details)) {
115
+ marks.push({ start: m.index + m[1].length, end: m.index + m[0].length, label: m[2].replace(/\s+/g, '') });
116
+ }
117
+ if (marks.length < 2)
118
+ return null;
119
+ const items = marks
120
+ .map((mark, i) => ({
121
+ label: mark.label,
122
+ text: details.slice(mark.end, i + 1 < marks.length ? marks[i + 1].start : details.length).trim(),
123
+ }))
124
+ .filter(item => item.text.length > 0);
125
+ if (items.length < 2)
126
+ return null;
127
+ return { preamble: details.slice(0, marks[0].start).trim(), items };
128
+ }
129
+ function formatReviewedAt(iso) {
130
+ const at = new Date(iso);
131
+ if (Number.isNaN(at.getTime()))
132
+ return '';
133
+ return `${at.toISOString().replace('T', ' ').slice(0, 16)} UTC`;
134
+ }