@zhixuan92/multi-model-agent-core 4.5.3 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/README.md +8 -3
  2. package/dist/escalation/delegate-with-escalation.d.ts +2 -2
  3. package/dist/escalation/delegate-with-escalation.d.ts.map +1 -1
  4. package/dist/escalation/delegate-with-escalation.js +11 -17
  5. package/dist/escalation/delegate-with-escalation.js.map +1 -1
  6. package/dist/escalation/fallback-helpers.d.ts +5 -5
  7. package/dist/escalation/fallback-helpers.d.ts.map +1 -1
  8. package/dist/escalation/fallback-helpers.js +5 -3
  9. package/dist/escalation/fallback-helpers.js.map +1 -1
  10. package/dist/events/event-builder.d.ts +36 -5
  11. package/dist/events/event-builder.d.ts.map +1 -1
  12. package/dist/events/event-builder.js +175 -33
  13. package/dist/events/event-builder.js.map +1 -1
  14. package/dist/events/observability-events.d.ts +6 -0
  15. package/dist/events/observability-events.d.ts.map +1 -1
  16. package/dist/events/observability-events.js +5 -0
  17. package/dist/events/observability-events.js.map +1 -1
  18. package/dist/events/telemetry-types.d.ts +10 -0
  19. package/dist/events/telemetry-types.d.ts.map +1 -1
  20. package/dist/events/verbose-log-channel.d.ts +7 -7
  21. package/dist/events/verbose-log-channel.js +10 -10
  22. package/dist/index.d.ts +9 -3
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +8 -2
  25. package/dist/index.js.map +1 -1
  26. package/dist/lifecycle/annotate-parser.d.ts +9 -0
  27. package/dist/lifecycle/annotate-parser.d.ts.map +1 -0
  28. package/dist/lifecycle/annotate-parser.js +83 -0
  29. package/dist/lifecycle/annotate-parser.js.map +1 -0
  30. package/dist/lifecycle/annotate-prompts.d.ts +9 -0
  31. package/dist/lifecycle/annotate-prompts.d.ts.map +1 -0
  32. package/dist/lifecycle/annotate-prompts.js +86 -0
  33. package/dist/lifecycle/annotate-prompts.js.map +1 -0
  34. package/dist/lifecycle/auto-register-context-block.d.ts +2 -2
  35. package/dist/lifecycle/auto-register-context-block.d.ts.map +1 -1
  36. package/dist/lifecycle/auto-register-context-block.js.map +1 -1
  37. package/dist/lifecycle/build-cancelled-result.d.ts +11 -0
  38. package/dist/lifecycle/build-cancelled-result.d.ts.map +1 -0
  39. package/dist/lifecycle/build-cancelled-result.js +27 -0
  40. package/dist/lifecycle/build-cancelled-result.js.map +1 -0
  41. package/dist/lifecycle/executor-output-types.d.ts +2 -2
  42. package/dist/lifecycle/executor-output-types.d.ts.map +1 -1
  43. package/dist/lifecycle/fallback-report.d.ts +2 -2
  44. package/dist/lifecycle/fallback-report.d.ts.map +1 -1
  45. package/dist/lifecycle/fallback-report.js.map +1 -1
  46. package/dist/lifecycle/findings-parser.d.ts +2 -0
  47. package/dist/lifecycle/findings-parser.d.ts.map +1 -1
  48. package/dist/lifecycle/findings-parser.js +9 -4
  49. package/dist/lifecycle/findings-parser.js.map +1 -1
  50. package/dist/lifecycle/git-toplevel.d.ts +12 -0
  51. package/dist/lifecycle/git-toplevel.d.ts.map +1 -0
  52. package/dist/lifecycle/git-toplevel.js +49 -0
  53. package/dist/lifecycle/git-toplevel.js.map +1 -0
  54. package/dist/lifecycle/handlers/annotator.d.ts +2 -1
  55. package/dist/lifecycle/handlers/annotator.d.ts.map +1 -1
  56. package/dist/lifecycle/handlers/annotator.js +225 -7
  57. package/dist/lifecycle/handlers/annotator.js.map +1 -1
  58. package/dist/lifecycle/handlers/baseline-handlers.d.ts +10 -5
  59. package/dist/lifecycle/handlers/baseline-handlers.d.ts.map +1 -1
  60. package/dist/lifecycle/handlers/baseline-handlers.js +206 -316
  61. package/dist/lifecycle/handlers/baseline-handlers.js.map +1 -1
  62. package/dist/lifecycle/handlers/enrich-runtime-result.d.ts +3 -0
  63. package/dist/lifecycle/handlers/enrich-runtime-result.d.ts.map +1 -0
  64. package/dist/lifecycle/handlers/enrich-runtime-result.js +191 -0
  65. package/dist/lifecycle/handlers/enrich-runtime-result.js.map +1 -0
  66. package/dist/lifecycle/handlers/git-commit-handler.d.ts +3 -2
  67. package/dist/lifecycle/handlers/git-commit-handler.d.ts.map +1 -1
  68. package/dist/lifecycle/handlers/git-commit-handler.js +139 -110
  69. package/dist/lifecycle/handlers/git-commit-handler.js.map +1 -1
  70. package/dist/lifecycle/handlers/prepare-execution-context-handler.d.ts +7 -13
  71. package/dist/lifecycle/handlers/prepare-execution-context-handler.d.ts.map +1 -1
  72. package/dist/lifecycle/handlers/prepare-execution-context-handler.js +72 -49
  73. package/dist/lifecycle/handlers/prepare-execution-context-handler.js.map +1 -1
  74. package/dist/lifecycle/handlers/register-context-block-handlers.d.ts +2 -1
  75. package/dist/lifecycle/handlers/register-context-block-handlers.d.ts.map +1 -1
  76. package/dist/lifecycle/handlers/register-context-block-handlers.js +35 -14
  77. package/dist/lifecycle/handlers/register-context-block-handlers.js.map +1 -1
  78. package/dist/lifecycle/handlers/review-handler.d.ts +2 -1
  79. package/dist/lifecycle/handlers/review-handler.d.ts.map +1 -1
  80. package/dist/lifecycle/handlers/review-handler.js +152 -154
  81. package/dist/lifecycle/handlers/review-handler.js.map +1 -1
  82. package/dist/lifecycle/handlers/rework-handler.d.ts +2 -1
  83. package/dist/lifecycle/handlers/rework-handler.d.ts.map +1 -1
  84. package/dist/lifecycle/handlers/rework-handler.js +62 -14
  85. package/dist/lifecycle/handlers/rework-handler.js.map +1 -1
  86. package/dist/lifecycle/handlers/task-executor.d.ts +5 -4
  87. package/dist/lifecycle/handlers/task-executor.d.ts.map +1 -1
  88. package/dist/lifecycle/handlers/task-executor.js +153 -74
  89. package/dist/lifecycle/handlers/task-executor.js.map +1 -1
  90. package/dist/lifecycle/handlers/terminal-handlers.d.ts +22 -0
  91. package/dist/lifecycle/handlers/terminal-handlers.d.ts.map +1 -1
  92. package/dist/lifecycle/handlers/terminal-handlers.js +86 -5
  93. package/dist/lifecycle/handlers/terminal-handlers.js.map +1 -1
  94. package/dist/lifecycle/lifecycle-context.d.ts +31 -3
  95. package/dist/lifecycle/lifecycle-context.d.ts.map +1 -1
  96. package/dist/lifecycle/lifecycle-dispatcher.d.ts +18 -19
  97. package/dist/lifecycle/lifecycle-dispatcher.d.ts.map +1 -1
  98. package/dist/lifecycle/lifecycle-dispatcher.js +38 -22
  99. package/dist/lifecycle/lifecycle-dispatcher.js.map +1 -1
  100. package/dist/lifecycle/lifecycle-driver.d.ts +15 -8
  101. package/dist/lifecycle/lifecycle-driver.d.ts.map +1 -1
  102. package/dist/lifecycle/lifecycle-driver.js +110 -29
  103. package/dist/lifecycle/lifecycle-driver.js.map +1 -1
  104. package/dist/lifecycle/merge-stage-stats.d.ts +3 -3
  105. package/dist/lifecycle/merge-stage-stats.d.ts.map +1 -1
  106. package/dist/lifecycle/merge-stage-stats.js +2 -2
  107. package/dist/lifecycle/merge-stage-stats.js.map +1 -1
  108. package/dist/lifecycle/perform-implementation.d.ts +3 -0
  109. package/dist/lifecycle/perform-implementation.d.ts.map +1 -0
  110. package/dist/lifecycle/perform-implementation.js +264 -0
  111. package/dist/lifecycle/perform-implementation.js.map +1 -0
  112. package/dist/lifecycle/repo-hygiene.d.ts +14 -0
  113. package/dist/lifecycle/repo-hygiene.d.ts.map +1 -0
  114. package/dist/lifecycle/repo-hygiene.js +71 -0
  115. package/dist/lifecycle/repo-hygiene.js.map +1 -0
  116. package/dist/lifecycle/shared-compute.d.ts +4 -4
  117. package/dist/lifecycle/shared-compute.d.ts.map +1 -1
  118. package/dist/lifecycle/shared-compute.js +1 -1
  119. package/dist/lifecycle/shared-compute.js.map +1 -1
  120. package/dist/lifecycle/stage-io.d.ts +139 -0
  121. package/dist/lifecycle/stage-io.d.ts.map +1 -0
  122. package/dist/lifecycle/stage-io.js +20 -0
  123. package/dist/lifecycle/stage-io.js.map +1 -0
  124. package/dist/lifecycle/stage-plan-builder.d.ts +3 -3
  125. package/dist/lifecycle/stage-plan-builder.d.ts.map +1 -1
  126. package/dist/lifecycle/stage-plan-builder.js +157 -128
  127. package/dist/lifecycle/stage-plan-builder.js.map +1 -1
  128. package/dist/lifecycle/stage-plan-types.d.ts +5 -21
  129. package/dist/lifecycle/stage-plan-types.d.ts.map +1 -1
  130. package/dist/lifecycle/stage-progression.d.ts +3 -15
  131. package/dist/lifecycle/stage-progression.d.ts.map +1 -1
  132. package/dist/lifecycle/stage-progression.js +48 -91
  133. package/dist/lifecycle/stage-progression.js.map +1 -1
  134. package/dist/lifecycle/task-completion-summary.d.ts +2 -2
  135. package/dist/lifecycle/task-completion-summary.d.ts.map +1 -1
  136. package/dist/lifecycle/task-completion-summary.js +0 -6
  137. package/dist/lifecycle/task-completion-summary.js.map +1 -1
  138. package/dist/lifecycle/task-executor.d.ts +23 -0
  139. package/dist/lifecycle/task-executor.d.ts.map +1 -1
  140. package/dist/lifecycle/task-executor.js +77 -5
  141. package/dist/lifecycle/task-executor.js.map +1 -1
  142. package/dist/lifecycle/task-grouping.d.ts +19 -0
  143. package/dist/lifecycle/task-grouping.d.ts.map +1 -0
  144. package/dist/lifecycle/task-grouping.js +48 -0
  145. package/dist/lifecycle/task-grouping.js.map +1 -0
  146. package/dist/lifecycle/task-runner.d.ts +20 -7
  147. package/dist/lifecycle/task-runner.d.ts.map +1 -1
  148. package/dist/lifecycle/task-runner.js +42 -278
  149. package/dist/lifecycle/task-runner.js.map +1 -1
  150. package/dist/lifecycle/tool-config-types.d.ts +9 -0
  151. package/dist/lifecycle/tool-config-types.d.ts.map +1 -1
  152. package/dist/lifecycle/worker-output-contract.d.ts +9 -15
  153. package/dist/lifecycle/worker-output-contract.d.ts.map +1 -1
  154. package/dist/lifecycle/worker-output-contract.js +45 -30
  155. package/dist/lifecycle/worker-output-contract.js.map +1 -1
  156. package/dist/providers/assemble-run-result.d.ts +3 -3
  157. package/dist/providers/assemble-run-result.d.ts.map +1 -1
  158. package/dist/providers/assemble-run-result.js +20 -9
  159. package/dist/providers/assemble-run-result.js.map +1 -1
  160. package/dist/providers/claude-session.d.ts.map +1 -1
  161. package/dist/providers/claude-session.js +1 -0
  162. package/dist/providers/claude-session.js.map +1 -1
  163. package/dist/providers/claude.js.map +1 -1
  164. package/dist/providers/codex-cli-launch.js +1 -1
  165. package/dist/providers/codex-cli-launch.js.map +1 -1
  166. package/dist/providers/codex-cli-session.d.ts +4 -1
  167. package/dist/providers/codex-cli-session.d.ts.map +1 -1
  168. package/dist/providers/codex-cli-session.js +73 -46
  169. package/dist/providers/codex-cli-session.js.map +1 -1
  170. package/dist/providers/codex.js.map +1 -1
  171. package/dist/providers/index.d.ts +1 -0
  172. package/dist/providers/index.d.ts.map +1 -1
  173. package/dist/providers/index.js +1 -0
  174. package/dist/providers/index.js.map +1 -1
  175. package/dist/providers/normalize-claude.d.ts +2 -0
  176. package/dist/providers/normalize-claude.d.ts.map +1 -1
  177. package/dist/providers/normalize-claude.js +1 -0
  178. package/dist/providers/normalize-claude.js.map +1 -1
  179. package/dist/providers/run-annotator-turn.d.ts +24 -0
  180. package/dist/providers/run-annotator-turn.d.ts.map +1 -0
  181. package/dist/providers/run-annotator-turn.js +43 -0
  182. package/dist/providers/run-annotator-turn.js.map +1 -0
  183. package/dist/providers/run-worker-turn.d.ts +26 -0
  184. package/dist/providers/run-worker-turn.d.ts.map +1 -0
  185. package/dist/providers/run-worker-turn.js +58 -0
  186. package/dist/providers/run-worker-turn.js.map +1 -0
  187. package/dist/providers/runner-types.d.ts +1 -1
  188. package/dist/providers/runner-types.d.ts.map +1 -1
  189. package/dist/reporting/headline-composer.d.ts +3 -3
  190. package/dist/reporting/headline-composer.d.ts.map +1 -1
  191. package/dist/review/index.d.ts +4 -3
  192. package/dist/review/index.d.ts.map +1 -1
  193. package/dist/review/index.js +7 -4
  194. package/dist/review/index.js.map +1 -1
  195. package/dist/review/parse-review-report.d.ts +7 -1
  196. package/dist/review/parse-review-report.d.ts.map +1 -1
  197. package/dist/review/parse-review-report.js +13 -21
  198. package/dist/review/parse-review-report.js.map +1 -1
  199. package/dist/review/review-verdict-mapping.d.ts +3 -3
  200. package/dist/review/review-verdict-mapping.d.ts.map +1 -1
  201. package/dist/review/review-verdict-mapping.js +1 -1
  202. package/dist/review/review-verdict-mapping.js.map +1 -1
  203. package/dist/review/run-reviewer.d.ts +40 -0
  204. package/dist/review/run-reviewer.d.ts.map +1 -0
  205. package/dist/review/run-reviewer.js +54 -0
  206. package/dist/review/run-reviewer.js.map +1 -0
  207. package/dist/review/templates/quality-review.d.ts +5 -0
  208. package/dist/review/templates/quality-review.d.ts.map +1 -1
  209. package/dist/review/templates/quality-review.js +35 -25
  210. package/dist/review/templates/quality-review.js.map +1 -1
  211. package/dist/review/templates/spec-review.d.ts +6 -0
  212. package/dist/review/templates/spec-review.d.ts.map +1 -1
  213. package/dist/review/templates/spec-review.js +36 -8
  214. package/dist/review/templates/spec-review.js.map +1 -1
  215. package/dist/stores/batch-cache.d.ts +3 -3
  216. package/dist/stores/batch-cache.d.ts.map +1 -1
  217. package/dist/stores/batch-cache.js.map +1 -1
  218. package/dist/stores/batch-registry.d.ts +31 -0
  219. package/dist/stores/batch-registry.d.ts.map +1 -1
  220. package/dist/stores/batch-registry.js +12 -0
  221. package/dist/stores/batch-registry.js.map +1 -1
  222. package/dist/tools/debug/tool-config.js +1 -1
  223. package/dist/tools/debug/tool-config.js.map +1 -1
  224. package/dist/tools/delegate/tool-config.d.ts.map +1 -1
  225. package/dist/tools/delegate/tool-config.js +3 -1
  226. package/dist/tools/delegate/tool-config.js.map +1 -1
  227. package/dist/tools/execute-plan/tool-config.d.ts.map +1 -1
  228. package/dist/tools/execute-plan/tool-config.js +3 -1
  229. package/dist/tools/execute-plan/tool-config.js.map +1 -1
  230. package/dist/tools/investigate/tool-config.js +1 -1
  231. package/dist/tools/investigate/tool-config.js.map +1 -1
  232. package/dist/tools/retry/tool-config.js.map +1 -1
  233. package/dist/types/run-result.d.ts +164 -213
  234. package/dist/types/run-result.d.ts.map +1 -1
  235. package/dist/types/run-result.js +17 -0
  236. package/dist/types/run-result.js.map +1 -1
  237. package/dist/types.d.ts +1 -1
  238. package/dist/types.d.ts.map +1 -1
  239. package/package.json +1 -1
  240. package/dist/lifecycle/handlers/derive-terminal-status.d.ts +0 -10
  241. package/dist/lifecycle/handlers/derive-terminal-status.d.ts.map +0 -1
  242. package/dist/lifecycle/handlers/derive-terminal-status.js +0 -35
  243. package/dist/lifecycle/handlers/derive-terminal-status.js.map +0 -1
  244. package/dist/lifecycle/handlers/files-written-cross-check.d.ts +0 -27
  245. package/dist/lifecycle/handlers/files-written-cross-check.d.ts.map +0 -1
  246. package/dist/lifecycle/handlers/files-written-cross-check.js +0 -91
  247. package/dist/lifecycle/handlers/files-written-cross-check.js.map +0 -1
  248. package/dist/lifecycle/handlers/verify-stage.d.ts +0 -37
  249. package/dist/lifecycle/handlers/verify-stage.d.ts.map +0 -1
  250. package/dist/lifecycle/handlers/verify-stage.js +0 -208
  251. package/dist/lifecycle/handlers/verify-stage.js.map +0 -1
  252. package/dist/review/default-engines.d.ts +0 -3
  253. package/dist/review/default-engines.d.ts.map +0 -1
  254. package/dist/review/default-engines.js +0 -22
  255. package/dist/review/default-engines.js.map +0 -1
  256. package/dist/review/reviewer-engine.d.ts +0 -53
  257. package/dist/review/reviewer-engine.d.ts.map +0 -1
  258. package/dist/review/reviewer-engine.js +0 -47
  259. package/dist/review/reviewer-engine.js.map +0 -1
  260. package/dist/review/reviewer-output-parser.d.ts +0 -12
  261. package/dist/review/reviewer-output-parser.d.ts.map +0 -1
  262. package/dist/review/reviewer-output-parser.js +0 -213
  263. package/dist/review/reviewer-output-parser.js.map +0 -1
  264. package/dist/review/reviewer-prompt-builder.d.ts +0 -21
  265. package/dist/review/reviewer-prompt-builder.d.ts.map +0 -1
  266. package/dist/review/reviewer-prompt-builder.js +0 -22
  267. package/dist/review/reviewer-prompt-builder.js.map +0 -1
  268. package/dist/review/templates/annotate-completion.d.ts +0 -12
  269. package/dist/review/templates/annotate-completion.d.ts.map +0 -1
  270. package/dist/review/templates/annotate-completion.js +0 -72
  271. package/dist/review/templates/annotate-completion.js.map +0 -1
@@ -1,60 +1,83 @@
1
1
  import { DiffTracker } from '../diff-tracker.js';
2
2
  /**
3
- * StageHandler for row 2.5 (prepare_execution_context).
3
+ * Stage handler: row 2.5 prepare_execution_context.
4
4
  *
5
- * Reads from state:
6
- * - state.task / state.executionContext: idempotency guards. If callers
7
- * pre-populate them via DispatchInput.context (or a future intake step),
8
- * this handler is a structural acknowledgment.
9
- * - state.request: the rawRequest passed to dispatch. Used as a fallback
10
- * to surface the tasks list when callers haven't supplied state.task.
5
+ * Seeding: populates state.task, state.reviewPolicy, state.diffTracker.
6
+ * This handler is the canonical "state was set up correctly" acknowledgment
7
+ * for downstream handlers that read from these slots.
11
8
  *
12
- * Writes to state (only when slots are empty AND request is shaped for it):
13
- * - state.task: the first TaskSpec from a delegate-style request payload.
14
- * runTasks (in task-runner.ts) dispatches one StagePlan per TaskSpec,
15
- * so state.task is the per-dispatch task. This handler surfaces task[0]
16
- * as a fallback for downstream handlers that need a TaskSpec.
9
+ * §5.1 payload: null. Gate exists only for the per-stage telemetry slot.
17
10
  */
18
11
  export async function prepareExecutionContextHandler(state) {
19
- // Fallback: if rawRequest carries a TaskSpec[] and state.task is empty,
20
- // surface the first task so per-task handlers have something to read.
21
- if (!state.task) {
22
- const req = state.request;
23
- const first = req?.tasks?.[0];
24
- if (first)
25
- state.task = first;
26
- }
27
- // #45 Step 7e: per-task reviewPolicy lives on TaskSpec, not on rawRequest's
28
- // top-level. Override state.reviewPolicy from state.task when present so
29
- // the per-row runConditions (gating spec/quality/diff chains) see the
30
- // right value. This must run unconditionally callers via
31
- // runTaskViaDispatcher pre-populate state.task + state.executionContext but
32
- // the dispatcher's initialState defaults state.reviewPolicy to 'full' since
33
- // the per-task reviewPolicy isn't visible at top-level rawRequest.
34
- const task = state.task;
35
- if (task && task.reviewPolicy) {
36
- state.reviewPolicy = task.reviewPolicy;
37
- }
38
- // Tool sweep #6: snapshot the worker's declared filePaths BEFORE the
39
- // implementer runs so reviewer stages can produce a cumulative diff
40
- // against the pre-task baseline. Skip read-only routes (audit / review
41
- // / verify / debug / investigate / explore) they don't write files
42
- // by sandbox policy, so a tracker would just be empty noise.
43
- if (!state.diffTracker && task && state.toolCategory !== 'read_only') {
44
- const cwd = task.cwd;
45
- const filePaths = Array.isArray(task.filePaths) ? task.filePaths : [];
46
- if (cwd && filePaths.length > 0) {
47
- const tracker = new DiffTracker(cwd);
48
- try {
49
- await tracker.snapshot(filePaths);
50
- state.diffTracker = tracker;
51
- }
52
- catch {
53
- // Snapshot failures (permission, unreadable) shouldn't block the
54
- // task. Reviewer just sees an empty diff and falls back to the
55
- // worker-output-only path — degraded but not broken.
12
+ const t0 = Date.now();
13
+ try {
14
+ // Fallback: if rawRequest carries a TaskSpec[] and state.task is empty,
15
+ // surface the first task so per-task handlers have something to read.
16
+ if (!state.task) {
17
+ const req = state.request;
18
+ const first = req?.tasks?.[0];
19
+ if (first)
20
+ state.task = first;
21
+ }
22
+ // #45 Step 7e: per-task reviewPolicy lives on TaskSpec, not on rawRequest's
23
+ // top-level. Override state.reviewPolicy from state.task when present so
24
+ // the per-row runConditions (gating spec/quality/diff chains) see the
25
+ // right value.
26
+ const task = state.task;
27
+ if (task?.reviewPolicy) {
28
+ state.reviewPolicy = task.reviewPolicy;
29
+ }
30
+ // Tool sweep #6: snapshot the worker's declared filePaths BEFORE the
31
+ // implementer runs so reviewer stages can produce a cumulative diff
32
+ // against the pre-task baseline. Skip read-only routes (audit / review
33
+ // / verify / debug / investigate / explore) they don't write files
34
+ // by sandbox policy, so a tracker would just be empty noise.
35
+ if (!state.diffTracker && task && state.toolCategory !== 'read_only') {
36
+ const cwd = task.cwd;
37
+ const filePaths = Array.isArray(task.filePaths) ? task.filePaths : [];
38
+ if (cwd && filePaths.length > 0) {
39
+ const tracker = new DiffTracker(cwd);
40
+ try {
41
+ await tracker.snapshot(filePaths);
42
+ state.diffTracker = tracker;
43
+ }
44
+ catch {
45
+ // Snapshot failures (permission, unreadable) shouldn't block the
46
+ // task. Reviewer just sees an empty diff and falls back to the
47
+ // worker-output-only path degraded but not broken.
48
+ }
56
49
  }
57
50
  }
51
+ return {
52
+ outcome: 'advance',
53
+ payload: null,
54
+ telemetry: {
55
+ stageLabel: 'prepare',
56
+ durationMs: Date.now() - t0,
57
+ costUSD: 0,
58
+ turnsUsed: 0,
59
+ stopReason: 'normal',
60
+ },
61
+ };
62
+ }
63
+ catch (err) {
64
+ const msg = err instanceof Error ? err.message : String(err);
65
+ const comment = /brief schema|invalid brief/i.test(msg) ? `brief_invalid: ${msg}` :
66
+ /workspace|traversal|sandbox/i.test(msg) ? `workspace_violation: ${msg}` :
67
+ /context_block|missing/i.test(msg) ? `context_block_missing: ${msg}` :
68
+ `prepare_failed: ${msg}`;
69
+ return {
70
+ outcome: 'halt',
71
+ comment,
72
+ payload: null,
73
+ telemetry: {
74
+ stageLabel: 'prepare',
75
+ durationMs: Date.now() - t0,
76
+ costUSD: 0,
77
+ turnsUsed: 0,
78
+ stopReason: 'transport_error',
79
+ },
80
+ };
58
81
  }
59
82
  }
60
83
  //# sourceMappingURL=prepare-execution-context-handler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prepare-execution-context-handler.js","sourceRoot":"","sources":["../../../src/lifecycle/handlers/prepare-execution-context-handler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAAC,KAAqB;IACxE,wEAAwE;IACxE,sEAAsE;IACtE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAChB,MAAM,GAAG,GAAG,KAAK,CAAC,OAA6C,CAAC;QAChE,MAAM,KAAK,GAAG,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,KAAK;YAAE,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;IAChC,CAAC;IAED,4EAA4E;IAC5E,yEAAyE;IACzE,sEAAsE;IACtE,2DAA2D;IAC3D,4EAA4E;IAC5E,4EAA4E;IAC5E,mEAAmE;IACnE,MAAM,IAAI,GAAG,KAAK,CAAC,IAA4B,CAAC;IAChD,IAAI,IAAI,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;QAC9B,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;IACzC,CAAC;IAED,qEAAqE;IACrE,oEAAoE;IACpE,uEAAuE;IACvE,qEAAqE;IACrE,6DAA6D;IAC7D,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,IAAI,IAAI,KAAK,CAAC,YAAY,KAAK,WAAW,EAAE,CAAC;QACrE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACrB,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,IAAI,GAAG,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC;YACrC,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;gBAClC,KAAK,CAAC,WAAW,GAAG,OAAO,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,iEAAiE;gBACjE,+DAA+D;gBAC/D,qDAAqD;YACvD,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"prepare-execution-context-handler.js","sourceRoot":"","sources":["../../../src/lifecycle/handlers/prepare-execution-context-handler.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
1
  import type { LifecycleState } from '../stage-plan-types.js';
2
- export declare function registerToBlockStoreHandler(state: LifecycleState): void;
2
+ import type { StageGate, RegisterBlockPayload } from '../stage-io.js';
3
+ export declare function registerToBlockStoreHandler(state: LifecycleState): Promise<StageGate<RegisterBlockPayload>>;
3
4
  //# sourceMappingURL=register-context-block-handlers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"register-context-block-handlers.d.ts","sourceRoot":"","sources":["../../../src/lifecycle/handlers/register-context-block-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAG7D,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAevE"}
1
+ {"version":3,"file":"register-context-block-handlers.d.ts","sourceRoot":"","sources":["../../../src/lifecycle/handlers/register-context-block-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAItE,wBAAsB,2BAA2B,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC,CAqCjH"}
@@ -1,18 +1,39 @@
1
- export function registerToBlockStoreHandler(state) {
2
- if (state.terminal)
3
- return;
4
- const pc = state.projectContext;
1
+ const MAX_BYTES = 50 * 1024 * 1024;
2
+ export async function registerToBlockStoreHandler(state) {
3
+ const t0 = Date.now();
5
4
  const req = state.request;
6
- if (!pc || !req?.content) {
7
- state.terminal = true;
8
- state.errorCode = 'invalid_request';
9
- return;
5
+ const bytes = Buffer.byteLength(req?.content ?? '', 'utf-8');
6
+ if (bytes > MAX_BYTES) {
7
+ return {
8
+ outcome: 'halt',
9
+ comment: `payload_too_large: ${bytes} bytes`,
10
+ payload: { blockId: '', bytes },
11
+ telemetry: { stageLabel: 'register-block', durationMs: Date.now() - t0, costUSD: 0, turnsUsed: 0, stopReason: 'normal' },
12
+ };
13
+ }
14
+ try {
15
+ // Existing API: pc.contextBlocks.register(content) → RegisteredBlock { id, ... }
16
+ // See packages/core/src/lifecycle/handlers/register-context-block-handlers.ts:13
17
+ // and packages/core/src/stores/context-block-tool.ts:23 for the interface.
18
+ const pc = state.projectContext;
19
+ const registered = pc.contextBlocks.register(req?.content ?? '');
20
+ // v4 back-compat: compose_response reads state.blockRegistration to detect
21
+ // the register-context-block route. v5 emits the same data via the gate's
22
+ // payload; we keep this state slot populated for the existing compose path.
23
+ state.blockRegistration = { id: registered.id, size: bytes, ttlMs: pc.contextBlocks.ttlMs };
24
+ return {
25
+ outcome: 'advance',
26
+ payload: { blockId: registered.id, bytes },
27
+ telemetry: { stageLabel: 'register-block', durationMs: Date.now() - t0, costUSD: 0, turnsUsed: 0, stopReason: 'normal' },
28
+ };
29
+ }
30
+ catch (err) {
31
+ return {
32
+ outcome: 'halt',
33
+ comment: `store_write_failed: ${err instanceof Error ? err.message : String(err)}`,
34
+ payload: { blockId: '', bytes },
35
+ telemetry: { stageLabel: 'register-block', durationMs: Date.now() - t0, costUSD: 0, turnsUsed: 0, stopReason: 'transport_error' },
36
+ };
10
37
  }
11
- const registered = pc.contextBlocks.register(req.content);
12
- state.blockRegistration = {
13
- id: registered.id,
14
- size: Buffer.byteLength(req.content, 'utf8'),
15
- ttlMs: pc.contextBlocks.ttlMs,
16
- };
17
38
  }
18
39
  //# sourceMappingURL=register-context-block-handlers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"register-context-block-handlers.js","sourceRoot":"","sources":["../../../src/lifecycle/handlers/register-context-block-handlers.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,2BAA2B,CAAC,KAAqB;IAC/D,IAAI,KAAK,CAAC,QAAQ;QAAE,OAAO;IAC3B,MAAM,EAAE,GAAG,KAAK,CAAC,cAA4C,CAAC;IAC9D,MAAM,GAAG,GAAG,KAAK,CAAC,OAA0C,CAAC;IAC7D,IAAI,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC;QACzB,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;QACtB,KAAK,CAAC,SAAS,GAAG,iBAAiB,CAAC;QACpC,OAAO;IACT,CAAC;IACD,MAAM,UAAU,GAAG,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC1D,KAAK,CAAC,iBAAiB,GAAG;QACxB,EAAE,EAAE,UAAU,CAAC,EAAE;QACjB,IAAI,EAAE,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC;QAC5C,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,KAAK;KAC9B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"register-context-block-handlers.js","sourceRoot":"","sources":["../../../src/lifecycle/handlers/register-context-block-handlers.ts"],"names":[],"mappings":"AAGA,MAAM,SAAS,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAEnC,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,KAAqB;IACrE,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACtB,MAAM,GAAG,GAAG,KAAK,CAAC,OAA0C,CAAC;IAC7D,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;IAE7D,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;QACtB,OAAO;YACL,OAAO,EAAE,MAAM;YACf,OAAO,EAAE,sBAAsB,KAAK,QAAQ;YAC5C,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE;YAC/B,SAAS,EAAE,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE;SACzH,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,iFAAiF;QACjF,iFAAiF;QACjF,2EAA2E;QAC3E,MAAM,EAAE,GAAG,KAAK,CAAC,cAAqB,CAAC;QACvC,MAAM,UAAU,GAAG,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC;QACjE,2EAA2E;QAC3E,0EAA0E;QAC1E,4EAA4E;QAC3E,KAAa,CAAC,iBAAiB,GAAG,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QACrG,OAAO;YACL,OAAO,EAAE,SAAS;YAClB,OAAO,EAAE,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,EAAE,KAAK,EAAE;YAC1C,SAAS,EAAE,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE;SACzH,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,MAAM;YACf,OAAO,EAAE,uBAAuB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;YAClF,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE;YAC/B,SAAS,EAAE,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,iBAAiB,EAAE;SAClI,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -1,3 +1,4 @@
1
1
  import type { LifecycleState } from '../stage-plan-types.js';
2
- export declare function reviewHandler(state: LifecycleState): Promise<void>;
2
+ import type { StageGate, ReviewPayload } from '../stage-io.js';
3
+ export declare function reviewHandler(state: LifecycleState): Promise<StageGate<ReviewPayload>>;
3
4
  //# sourceMappingURL=review-handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"review-handler.d.ts","sourceRoot":"","sources":["../../../src/lifecycle/handlers/review-handler.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AA4C7D,wBAAsB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAsIxE"}
1
+ {"version":3,"file":"review-handler.d.ts","sourceRoot":"","sources":["../../../src/lifecycle/handlers/review-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAW,MAAM,gBAAgB,CAAC;AAQxE,wBAAsB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAiJ5F"}
@@ -1,169 +1,167 @@
1
- // v4.4.x Review stage.
2
- //
3
- // One complex session, two sequential turns: spec review, then quality
4
- // review. Same session = same cached prefix on the 2nd turn. Combined
5
- // verdict is `approved` only if BOTH reviewers approve; otherwise
6
- // `changes_required`. Combined `reviewConcerns` is the concat of both
7
- // reviewers' concerns.
8
- import { specLintTemplate } from '../../review/templates/spec-review.js';
9
- import { qualityLintTemplate } from '../../review/templates/quality-review.js';
1
+ import { specReviewPrompt } from '../../review/templates/spec-review.js';
2
+ import { qualityReviewPrompt } from '../../review/templates/quality-review.js';
10
3
  import { parseReviewReport } from '../../review/parse-review-report.js';
4
+ import { runReviewerTurn, invertedReviewerTier } from '../../review/run-reviewer.js';
11
5
  import { mergeStageStats } from '../merge-stage-stats.js';
12
- import { HUMAN_LABEL } from '../stage-labels.js';
13
- import { buildWarmFollowupMessage } from '../warm-followup.js';
14
- async function runOneReviewer(session, task, source, diff, workerOutput, isWarmFollowup) {
15
- const template = source === 'spec' ? specLintTemplate : qualityLintTemplate;
16
- const promptCtx = {
17
- brief: task.prompt ?? '',
18
- workerOutput,
19
- diff,
20
- planContext: task.planContext,
6
+ export async function reviewHandler(state) {
7
+ const t0 = Date.now();
8
+ const policy = state.reviewPolicy; // 'full' | 'quality_only' | 'diff_only' | 'none'
9
+ // v5 review-policy mapping (per spec §14 assumption 2 — adapted to v4 enum):
10
+ // 'full' → run BOTH spec + quality
11
+ // 'quality_only' run ONLY quality
12
+ // 'diff_only' → run ONLY spec (spec reviewer is the closest analog)
13
+ // 'none' → skip (handled upstream)
14
+ const runSpec = policy === 'full' || policy === 'diff_only';
15
+ const runQuality = policy === 'full' || policy === 'quality_only';
16
+ const impl = (state.gates?.['implement']?.payload ?? {});
17
+ const briefObj = (state.task ?? {});
18
+ const briefStr = briefObj.brief ?? '';
19
+ const context = {
20
+ brief: briefStr,
21
+ workerSummary: (impl?.summary ?? ""),
22
+ filesChanged: impl.filesChanged ?? [],
21
23
  };
22
- // Warm follow-up: the same reviewer session already loaded brief +
23
- // diff + planContext on turn 1. Send only the new instruction via
24
- // the standard preamble. systemPrompt is intentionally not
25
- // re-prepended it lives in the resumed session's history.
26
- // Cold open: full system prompt + brief + diff + planContext.
27
- const fullPrompt = isWarmFollowup && template.buildWarmFollowup
28
- ? buildWarmFollowupMessage(template.buildWarmFollowup(promptCtx))
29
- : template.systemPrompt + '\n\n' + template.buildUserPrompt(promptCtx);
30
- try {
31
- const turn = await session.send(fullPrompt, { stageLabel: HUMAN_LABEL.review });
32
- return { turn };
33
- }
34
- catch (err) {
35
- return { transportError: err instanceof Error ? err.message : String(err) };
24
+ const subResults = [];
25
+ // Cross-tier inversion (per design): reviewer runs on the opposite tier of
26
+ // the implementer. Read implementer tier from the executionContext; fall
27
+ // back to inferring from the implementing stage's gate payload, then
28
+ // 'standard' if neither is known (defensive matches legacy default).
29
+ const implementerTier = state.executionContext?.assignedTier
30
+ ?? (state.gates?.['implement']?.payload?.agentTier)
31
+ ?? 'standard';
32
+ const resolvedReviewerTier = invertedReviewerTier(implementerTier);
33
+ if (runSpec) {
34
+ const r = await runReviewerWithRetries(state, specReviewPrompt(context), 'spec', implementerTier);
35
+ subResults.push({
36
+ name: 'spec', result: r.parsed, cost: r.costUSD, ms: r.ms,
37
+ model: r.model,
38
+ inputTokens: r.inputTokens, outputTokens: r.outputTokens,
39
+ cachedReadTokens: r.cachedReadTokens, cachedNonReadTokens: r.cachedNonReadTokens,
40
+ turnsUsed: r.turnsUsed,
41
+ });
36
42
  }
37
- }
38
- export async function reviewHandler(state) {
39
- if (state.terminal)
40
- return;
41
- if (state.reviewVerdict !== undefined)
42
- return;
43
- const ctx = state.executionContext;
44
- const task = state.task;
45
- const last = state.lastRunResult;
46
- if (!ctx || !task || !last)
47
- return;
48
- if (state.reviewPolicy === 'none') {
49
- state.reviewVerdict = 'approved';
50
- state.reviewFindings = [];
51
- return;
43
+ if (runQuality) {
44
+ const r = await runReviewerWithRetries(state, qualityReviewPrompt(context), 'quality', implementerTier);
45
+ subResults.push({
46
+ name: 'quality', result: r.parsed, cost: r.costUSD, ms: r.ms,
47
+ model: r.model,
48
+ inputTokens: r.inputTokens, outputTokens: r.outputTokens,
49
+ cachedReadTokens: r.cachedReadTokens, cachedNonReadTokens: r.cachedNonReadTokens,
50
+ turnsUsed: r.turnsUsed,
51
+ });
52
52
  }
53
- let cumulativeDiff = '';
54
- if (state.diffTracker) {
55
- try {
56
- cumulativeDiff = await state.diffTracker.cumulativeDiff();
53
+ const succeeded = subResults.filter(s => s.result.verdict).map(s => s.name);
54
+ const errored = subResults.filter(s => !s.result.verdict).map(s => ({
55
+ reviewer: s.name, error: s.result.parseError ?? 'unknown',
56
+ }));
57
+ const findings = [];
58
+ let nextId = 1;
59
+ for (const s of subResults) {
60
+ for (const f of s.result.findings ?? []) {
61
+ findings.push({ ...f, id: `F${nextId++}`, source: 'reviewer' });
57
62
  }
58
- catch { /* tolerated */ }
59
- }
60
- const workerOutput = last.output ?? '';
61
- // v4.4.x: spec and quality run sequentially on the SAME complex
62
- // session so the second call benefits from the cached prefix.
63
- const reviewerTier = ctx.assignedTier === 'standard' ? 'complex' : 'standard';
64
- if (!ctx.providers[reviewerTier]) {
65
- state.reviewVerdict = 'changes_required';
66
- state.reviewError = `no provider available for tier ${reviewerTier}`;
67
- state.reviewFindings = [];
68
- return;
69
63
  }
70
- // Which sub-reviews to run, per reviewPolicy.
71
- const runSpec = state.reviewPolicy === 'full';
72
- const runQuality = state.reviewPolicy === 'full'
73
- || state.reviewPolicy === 'quality_only'
74
- || state.reviewPolicy === 'diff_only';
75
- const sources = [];
76
- if (runSpec)
77
- sources.push('spec');
78
- if (runQuality)
79
- sources.push('quality');
80
- if (sources.length === 0) {
81
- state.reviewVerdict = 'approved';
82
- state.reviewFindings = [];
83
- return;
64
+ // Combined verdict: approved iff EVERY configured reviewer returned approved.
65
+ let verdict;
66
+ if (succeeded.length === 0) {
67
+ verdict = 'changes_required';
68
+ findings.push({
69
+ id: `F${nextId++}`, severity: 'high', category: 'reviewer-availability',
70
+ claim: 'All configured reviewers failed to parse their output; defaulting to changes_required.',
71
+ evidence: errored.map(e => `${e.reviewer}: ${e.error}`).join(' | '),
72
+ source: 'reviewer',
73
+ });
84
74
  }
85
- const findings = [];
86
- const errors = [];
87
- let anyChangesRequired = false;
88
- let anySuccess = false;
89
- // Sequential — second turn hits the cached prefix on the same session.
90
- // The warm-follow-up form is valid ONLY when iteration > 0 AND the
91
- // session reference is the same as iteration 0. Today ctx.getSession
92
- // is idempotent on tier so both iterations see the same session; the
93
- // identity guard exists so a future change that rotates the reviewer
94
- // session mid-stage (escalation rotation, retry-after-failure) falls
95
- // back to cold-open automatically rather than sending a warm follow-up
96
- // into a fresh thread that lacks the prior history.
97
- const settled = [];
98
- let firstSession = null;
99
- for (let iteration = 0; iteration < sources.length; iteration++) {
100
- const source = sources[iteration];
101
- const currentSession = ctx.getSession(reviewerTier);
102
- if (iteration === 0)
103
- firstSession = currentSession;
104
- const isWarmFollowup = iteration > 0 && currentSession === firstSession;
105
- const outcome = await runOneReviewer(currentSession, task, source, cumulativeDiff, workerOutput, isWarmFollowup);
106
- settled.push({ source, outcome });
75
+ else if (subResults.some(s => s.result.verdict === 'changes_required')) {
76
+ verdict = 'changes_required';
107
77
  }
108
- let combinedInput = 0, combinedOutput = 0, combinedCached = 0, combinedNonRead = 0;
109
- let combinedTurns = 0;
110
- let combinedCost = null;
111
- let combinedDuration = 0;
112
- for (const { source, outcome } of settled) {
113
- if ('transportError' in outcome) {
114
- if (source === 'spec')
115
- state.specReviewError = outcome.transportError;
116
- else
117
- state.qualityReviewError = outcome.transportError;
118
- errors.push(`${source}: ${outcome.transportError}`);
119
- process.stderr.write(`[review-handler] ${source} transportError: ${outcome.transportError}\n`);
120
- continue;
78
+ else if (errored.length > 0) {
79
+ // Mixed: some approved, some errored.
80
+ verdict = 'changes_required';
81
+ for (const e of errored) {
82
+ findings.push({
83
+ id: `F${nextId++}`, severity: 'high', category: 'reviewer-availability',
84
+ claim: `Reviewer ${e.reviewer} failed to parse; treating as changes_required.`,
85
+ evidence: e.error.length >= 20 ? e.error : `parse error: ${e.error}`,
86
+ source: 'reviewer',
87
+ });
121
88
  }
122
- anySuccess = true;
123
- const parsed = parseReviewReport(outcome.turn.output ?? '');
124
- if (source === 'spec') {
125
- state.specReviewerNotes = outcome.turn.output;
126
- state.specReviewVerdict = parsed.verdict;
127
- }
128
- else {
129
- state.qualityReviewerNotes = outcome.turn.output;
130
- state.qualityReviewVerdict = parsed.verdict;
131
- }
132
- if (parsed.verdict === 'changes_required')
133
- anyChangesRequired = true;
134
- for (const dev of parsed.deviations)
135
- findings.push({ source, text: dev });
136
- combinedInput += outcome.turn.usage?.inputTokens ?? 0;
137
- combinedOutput += outcome.turn.usage?.outputTokens ?? 0;
138
- combinedCached += outcome.turn.usage?.cachedReadTokens ?? 0;
139
- combinedNonRead += outcome.turn.usage?.cachedNonReadTokens ?? 0;
140
- combinedTurns += outcome.turn.turns ?? 1;
141
- combinedDuration += outcome.turn.durationMs ?? 0;
142
- const c = outcome.turn.costUSD;
143
- if (c !== null && c !== undefined)
144
- combinedCost = (combinedCost ?? 0) + c;
145
89
  }
146
- state.reviewFindings = findings;
147
- state.reviewConcerns = findings.map((f) => f.text);
148
- if (!anySuccess) {
149
- state.reviewVerdict = 'changes_required';
150
- state.reviewError = errors.join(' | ');
151
- return;
90
+ else {
91
+ verdict = 'approved';
152
92
  }
153
- state.reviewVerdict = anyChangesRequired ? 'changes_required' : 'approved';
154
- mergeStageStats(state, 'review', {
155
- inputTokens: combinedInput,
156
- outputTokens: combinedOutput,
157
- cachedReadTokens: combinedCached,
158
- cachedNonReadTokens: combinedNonRead,
159
- turnCount: combinedTurns,
160
- toolCallCount: 0,
161
- costUSD: combinedCost,
162
- durationMs: combinedDuration || null,
163
- }, {
164
- tier: reviewerTier,
165
- model: ctx.providers[reviewerTier]?.config?.model ?? null,
166
- verdict: state.reviewVerdict,
93
+ const totalCost = subResults.reduce((s, x) => s + (x.cost ?? 0), 0);
94
+ const totalMs = subResults.reduce((s, x) => s + x.ms, 0);
95
+ const totalInput = subResults.reduce((s, x) => s + x.inputTokens, 0);
96
+ const totalOutput = subResults.reduce((s, x) => s + x.outputTokens, 0);
97
+ const totalCachedRead = subResults.reduce((s, x) => s + x.cachedReadTokens, 0);
98
+ const totalCachedNonRead = subResults.reduce((s, x) => s + x.cachedNonReadTokens, 0);
99
+ const totalTurns = subResults.reduce((s, x) => s + x.turnsUsed, 0);
100
+ // Both reviewers run on the standard tier through the same Session — same
101
+ // canonical model in practice. Pick the first non-null.
102
+ const reviewerModel = subResults.find(s => s.model !== null)?.model ?? null;
103
+ // Write reviewer stage stats so event-builder.buildReviewStage(rr.stageStats?.review)
104
+ // produces a real stage entry instead of returning null. Without this, the
105
+ // review stage was silently invisible in telemetry for reviewPolicy=full runs.
106
+ if (subResults.length > 0) {
107
+ mergeStageStats(state, 'review', {
108
+ inputTokens: totalInput,
109
+ outputTokens: totalOutput,
110
+ cachedReadTokens: totalCachedRead,
111
+ cachedNonReadTokens: totalCachedNonRead,
112
+ turnCount: totalTurns,
113
+ toolCallCount: 0,
114
+ costUSD: totalCost,
115
+ durationMs: Math.max(totalMs, Date.now() - t0),
116
+ filesReadCount: 0,
117
+ filesWrittenCount: 0,
118
+ }, { tier: resolvedReviewerTier, model: reviewerModel });
119
+ }
120
+ return {
121
+ outcome: 'advance',
122
+ payload: { verdict, findings, reviewersSucceeded: succeeded, reviewersErrored: errored },
123
+ telemetry: {
124
+ stageLabel: 'review',
125
+ durationMs: Math.max(totalMs, Date.now() - t0),
126
+ costUSD: totalCost > 0 ? totalCost : null,
127
+ turnsUsed: subResults.length,
128
+ stopReason: 'normal',
129
+ },
130
+ };
131
+ }
132
+ async function runReviewerWithRetries(state, prompt, name, implementerTier) {
133
+ const turn = await runReviewerTurn({
134
+ prompt,
135
+ ctx: state.executionContext,
136
+ reviewer: name,
137
+ implementerTier,
167
138
  });
139
+ if (turn.kind === 'transport_error') {
140
+ // Final failure surfaces as a parse-failure shape so the aggregator treats
141
+ // this reviewer as errored — same downstream effect as an unparseable response.
142
+ return {
143
+ parsed: { verdict: undefined, findings: [], parseError: turn.message },
144
+ costUSD: null,
145
+ ms: turn.ms,
146
+ model: null,
147
+ inputTokens: 0,
148
+ outputTokens: 0,
149
+ cachedReadTokens: 0,
150
+ cachedNonReadTokens: 0,
151
+ turnsUsed: 0,
152
+ };
153
+ }
154
+ const parsed = parseReviewReport(turn.text);
155
+ return {
156
+ parsed,
157
+ costUSD: turn.costUSD,
158
+ ms: turn.ms,
159
+ model: turn.model,
160
+ inputTokens: turn.inputTokens,
161
+ outputTokens: turn.outputTokens,
162
+ cachedReadTokens: turn.cachedReadTokens,
163
+ cachedNonReadTokens: turn.cachedNonReadTokens,
164
+ turnsUsed: turn.turnsUsed,
165
+ };
168
166
  }
169
167
  //# sourceMappingURL=review-handler.js.map