@cat-factory/server 0.214.1 → 0.216.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 (77) hide show
  1. package/dist/agents/ContainerAgentExecutor.d.ts +5 -1
  2. package/dist/agents/ContainerAgentExecutor.d.ts.map +1 -1
  3. package/dist/agents/ContainerAgentExecutor.js +6 -13
  4. package/dist/agents/ContainerAgentExecutor.js.map +1 -1
  5. package/dist/agents/toolTrajectory.d.ts +40 -0
  6. package/dist/agents/toolTrajectory.d.ts.map +1 -0
  7. package/dist/agents/toolTrajectory.js +67 -0
  8. package/dist/agents/toolTrajectory.js.map +1 -0
  9. package/dist/app.d.ts.map +1 -1
  10. package/dist/app.js +7 -0
  11. package/dist/app.js.map +1 -1
  12. package/dist/http/cors.d.ts +7 -0
  13. package/dist/http/cors.d.ts.map +1 -1
  14. package/dist/http/cors.js +8 -0
  15. package/dist/http/cors.js.map +1 -1
  16. package/dist/http/loopback.d.ts +23 -0
  17. package/dist/http/loopback.d.ts.map +1 -0
  18. package/dist/http/loopback.js +53 -0
  19. package/dist/http/loopback.js.map +1 -0
  20. package/dist/modules/publicApi/PublicDebugController.d.ts.map +1 -1
  21. package/dist/modules/publicApi/PublicDebugController.js +43 -1
  22. package/dist/modules/publicApi/PublicDebugController.js.map +1 -1
  23. package/dist/modules/publicApi/PublicDecisionController.d.ts.map +1 -1
  24. package/dist/modules/publicApi/PublicDecisionController.js +41 -322
  25. package/dist/modules/publicApi/PublicDecisionController.js.map +1 -1
  26. package/dist/modules/publicApi/PublicMcpController.d.ts +5 -0
  27. package/dist/modules/publicApi/PublicMcpController.d.ts.map +1 -0
  28. package/dist/modules/publicApi/PublicMcpController.js +74 -0
  29. package/dist/modules/publicApi/PublicMcpController.js.map +1 -0
  30. package/dist/modules/publicApi/decisions/approvalRoutes.d.ts +4 -0
  31. package/dist/modules/publicApi/decisions/approvalRoutes.d.ts.map +1 -0
  32. package/dist/modules/publicApi/decisions/approvalRoutes.js +96 -0
  33. package/dist/modules/publicApi/decisions/approvalRoutes.js.map +1 -0
  34. package/dist/modules/publicApi/decisions/dialogueRoutes.d.ts +5 -0
  35. package/dist/modules/publicApi/decisions/dialogueRoutes.d.ts.map +1 -0
  36. package/dist/modules/publicApi/decisions/dialogueRoutes.js +161 -0
  37. package/dist/modules/publicApi/decisions/dialogueRoutes.js.map +1 -0
  38. package/dist/modules/publicApi/decisions/gateRoutes.d.ts +5 -0
  39. package/dist/modules/publicApi/decisions/gateRoutes.d.ts.map +1 -0
  40. package/dist/modules/publicApi/decisions/gateRoutes.js +120 -0
  41. package/dist/modules/publicApi/decisions/gateRoutes.js.map +1 -0
  42. package/dist/modules/publicApi/decisions/projection.d.ts +62 -0
  43. package/dist/modules/publicApi/decisions/projection.d.ts.map +1 -0
  44. package/dist/modules/publicApi/decisions/projection.js +489 -0
  45. package/dist/modules/publicApi/decisions/projection.js.map +1 -0
  46. package/dist/modules/publicApi/decisions/requirementsRoutes.d.ts +11 -0
  47. package/dist/modules/publicApi/decisions/requirementsRoutes.d.ts.map +1 -0
  48. package/dist/modules/publicApi/decisions/requirementsRoutes.js +91 -0
  49. package/dist/modules/publicApi/decisions/requirementsRoutes.js.map +1 -0
  50. package/dist/modules/publicApi/decisions/scope.d.ts +107 -0
  51. package/dist/modules/publicApi/decisions/scope.d.ts.map +1 -0
  52. package/dist/modules/publicApi/decisions/scope.js +142 -0
  53. package/dist/modules/publicApi/decisions/scope.js.map +1 -0
  54. package/dist/modules/publicApi/publicApiAdmission.d.ts +9 -5
  55. package/dist/modules/publicApi/publicApiAdmission.d.ts.map +1 -1
  56. package/dist/modules/publicApi/publicApiAdmission.js +13 -5
  57. package/dist/modules/publicApi/publicApiAdmission.js.map +1 -1
  58. package/dist/modules/publicApi/publicApiAuth.d.ts +22 -0
  59. package/dist/modules/publicApi/publicApiAuth.d.ts.map +1 -1
  60. package/dist/modules/publicApi/publicApiAuth.js +39 -2
  61. package/dist/modules/publicApi/publicApiAuth.js.map +1 -1
  62. package/dist/modules/telemetry/TelemetryIngestController.d.ts.map +1 -1
  63. package/dist/modules/telemetry/TelemetryIngestController.js +13 -3
  64. package/dist/modules/telemetry/TelemetryIngestController.js.map +1 -1
  65. package/dist/persistence/rpc-allowlist.d.ts +1 -1
  66. package/dist/persistence/rpc-allowlist.d.ts.map +1 -1
  67. package/dist/persistence/rpc-allowlist.js +1 -0
  68. package/dist/persistence/rpc-allowlist.js.map +1 -1
  69. package/dist/telemetry/machineTelemetry.d.ts +8 -1
  70. package/dist/telemetry/machineTelemetry.d.ts.map +1 -1
  71. package/dist/telemetry/machineTelemetry.js +6 -0
  72. package/dist/telemetry/machineTelemetry.js.map +1 -1
  73. package/dist/telemetry/machineTelemetryRead.d.ts +36 -1
  74. package/dist/telemetry/machineTelemetryRead.d.ts.map +1 -1
  75. package/dist/telemetry/machineTelemetryRead.js +29 -0
  76. package/dist/telemetry/machineTelemetryRead.js.map +1 -1
  77. package/package.json +9 -8
@@ -0,0 +1,489 @@
1
+ import { ARCHITECTURE_BRAINSTORM_AGENT_KIND, CLARITY_REVIEW_AGENT_KIND, REQUIREMENTS_BRAINSTORM_AGENT_KIND, } from '@cat-factory/agents';
2
+ import { dedicatedParkSurface } from '@cat-factory/orchestration';
3
+ // The run → `PublicDecisionList` projection: what a parked run is currently asking a human, as
4
+ // external resources. One `to*Decision` + one `isLive*` predicate per park kind, and the assembly
5
+ // that decides which reads a given run even needs.
6
+ //
7
+ // Two rules hold across every kind here:
8
+ //
9
+ // 1. **A settled park is not a decision.** A predicate lists the park's own waiting state plus
10
+ // the in-flight states either side of it, so a poller sees progress rather than an empty list
11
+ // that reads as "nothing is happening" — but never a terminal state, which would put a
12
+ // question in front of a caller with nothing left to answer.
13
+ // 2. **Project as data, never as prose.** Every one of these carries model-authored text
14
+ // (a proposal, a finding, an agent's question). It crosses the wire as its own field so a
15
+ // consumer can render it in its own surface; nothing here composes it into markdown.
16
+ /** Project a live requirements review onto the external decision resource. */
17
+ export function toRequirementsDecision(review) {
18
+ return {
19
+ kind: 'requirements-review',
20
+ reviewId: review.id,
21
+ taskId: review.blockId,
22
+ status: review.status,
23
+ iteration: review.iteration ?? 1,
24
+ maxIterations: review.maxIterations ?? 1,
25
+ findings: review.items.map(toFinding),
26
+ incorporatedRequirements: review.incorporatedRequirements,
27
+ };
28
+ }
29
+ /** Project a live clarity (bug-triage) review — the requirements twin over its own document. */
30
+ export function toClarityDecision(review) {
31
+ return {
32
+ kind: 'clarity-review',
33
+ reviewId: review.id,
34
+ taskId: review.blockId,
35
+ status: review.status,
36
+ iteration: review.iteration ?? 1,
37
+ maxIterations: review.maxIterations ?? 1,
38
+ findings: review.items.map(toFinding),
39
+ clarifiedReport: review.clarifiedReport,
40
+ };
41
+ }
42
+ /** Project a live brainstorm session for one stage. */
43
+ export function toBrainstormDecision(session) {
44
+ return {
45
+ kind: 'brainstorm',
46
+ sessionId: session.id,
47
+ stage: session.stage,
48
+ taskId: session.blockId,
49
+ status: session.status,
50
+ iteration: session.iteration ?? 1,
51
+ maxIterations: session.maxIterations ?? 1,
52
+ options: session.items.map(toFinding),
53
+ convergedDirection: session.convergedDirection,
54
+ };
55
+ }
56
+ /**
57
+ * The shared item projection for all three iterative-review kinds. They persist the SAME item
58
+ * shape (`requirementReviewItemSchema`) on purpose, so one projection is the honest expression of
59
+ * that rather than three copies waiting to drift. The internal `autoAnswerable` classification and
60
+ * the Requirement-Writer recommendation machinery stay off it: both drive in-app affordances.
61
+ */
62
+ function toFinding(item) {
63
+ return {
64
+ itemId: item.id,
65
+ category: item.category,
66
+ severity: item.severity,
67
+ title: item.title,
68
+ detail: item.detail,
69
+ status: item.status,
70
+ reply: item.reply,
71
+ };
72
+ }
73
+ /** Project a run's fork-decision step state onto the external decision resource. */
74
+ export function toForkDecision(state) {
75
+ return {
76
+ kind: 'fork',
77
+ status: state.status,
78
+ seamSummary: state.seamSummary ?? null,
79
+ forks: state.forks ?? [],
80
+ };
81
+ }
82
+ /**
83
+ * Project a run's JUDGE step state onto the external decision resource. The rubric body is
84
+ * deliberately omitted (see `publicJudgeDecisionSchema`) — a caller answers the findings.
85
+ */
86
+ export function toJudgeDecision(stepKind, state) {
87
+ return {
88
+ kind: 'judge',
89
+ stepKind,
90
+ status: state.status,
91
+ rubricId: state.rubricId ?? null,
92
+ rubricName: state.rubricName ?? null,
93
+ threshold: state.threshold ?? null,
94
+ verdict: state.verdict ?? null,
95
+ bounces: state.bounces ?? 0,
96
+ maxBounces: state.maxBounces ?? 0,
97
+ };
98
+ }
99
+ /**
100
+ * Project a step's APPROVAL GATE. `exceeded` is read off the step's companion state rather than
101
+ * the approval itself, because that is where the engine records it — and it is what tells a caller
102
+ * to answer with `resolve-exceeded` instead of `approve`. Reading it as a plain boolean (rather
103
+ * than only when a companion exists) means an ordinary pipeline gate reports `false`, which is the
104
+ * true statement, not an omission.
105
+ */
106
+ export function toApprovalDecision(step, stepIndex, approval) {
107
+ return {
108
+ kind: 'approval-gate',
109
+ approvalId: approval.id,
110
+ stepKind: step.agentKind,
111
+ stepIndex,
112
+ status: approval.status,
113
+ proposal: approval.proposal,
114
+ feedback: approval.feedback ?? null,
115
+ exceeded: step.companion?.exceeded === true,
116
+ };
117
+ }
118
+ /** Project a decision an agent raised mid-work. */
119
+ export function toAgentDecision(step, decision) {
120
+ return {
121
+ kind: 'agent-decision',
122
+ decisionId: decision.id,
123
+ stepKind: step.agentKind,
124
+ question: decision.question,
125
+ options: decision.options,
126
+ };
127
+ }
128
+ /**
129
+ * Project a parked PR deep review's curation state.
130
+ *
131
+ * The slices and findings go through their own projections rather than crossing the wire as the
132
+ * engine holds them: the internal shapes are mid-evolution (per-slice resume reports) while the
133
+ * published ones must not move, and the `optional | null` internals collapse to always-present
134
+ * nullables so four generated clients have one shape to check.
135
+ */
136
+ export function toPrReviewDecision(state) {
137
+ return {
138
+ kind: 'pr-review',
139
+ status: state.status,
140
+ summary: state.summary ?? null,
141
+ prUrl: state.prUrl ?? null,
142
+ slices: (state.slices ?? []).map(toPrReviewSlice),
143
+ findings: (state.findings ?? []).map(toPrReviewFinding),
144
+ selectedFindingIds: state.selectedFindingIds ?? [],
145
+ };
146
+ }
147
+ /** One reviewed slice, externally. */
148
+ function toPrReviewSlice(slice) {
149
+ return {
150
+ sliceId: slice.id,
151
+ title: slice.title,
152
+ rationale: slice.rationale,
153
+ paths: slice.paths,
154
+ };
155
+ }
156
+ /**
157
+ * One review finding, externally. The challenge is projected rather than passed through so a
158
+ * caller sees the VERDICT and its justification without the engine's job bookkeeping.
159
+ */
160
+ function toPrReviewFinding(finding) {
161
+ const challenge = finding.challenge;
162
+ return {
163
+ findingId: finding.id,
164
+ sliceId: finding.sliceId ?? null,
165
+ path: finding.path,
166
+ line: finding.line ?? null,
167
+ side: finding.side ?? null,
168
+ severity: finding.severity,
169
+ category: finding.category,
170
+ title: finding.title,
171
+ detail: finding.detail,
172
+ suggestedFix: finding.suggestedFix ?? null,
173
+ challenge: challenge
174
+ ? {
175
+ status: challenge.status,
176
+ question: challenge.question ?? null,
177
+ justification: challenge.justification ?? null,
178
+ }
179
+ : null,
180
+ };
181
+ }
182
+ /** Project a parked human-test gate: what to test against, and how much fix budget is left. */
183
+ export function toHumanTestDecision(state) {
184
+ const env = state.environment;
185
+ return {
186
+ kind: 'human-test',
187
+ phase: state.phase,
188
+ environment: env
189
+ ? { url: env.url, status: env.status, expiresAt: env.expiresAt ?? null }
190
+ : null,
191
+ degradedReason: state.degradedReason ?? null,
192
+ attempts: state.attempts,
193
+ maxAttempts: state.maxAttempts,
194
+ };
195
+ }
196
+ /** Project a parked visual-confirmation gate: the pairings awaiting a verdict. */
197
+ export function toVisualConfirmDecision(state) {
198
+ return {
199
+ kind: 'visual-confirmation',
200
+ phase: state.phase,
201
+ pairs: (state.pairs ?? []).map((pair) => ({
202
+ view: pair.view,
203
+ actualArtifactId: pair.actualArtifactId ?? null,
204
+ referenceArtifactId: pair.referenceArtifactId ?? null,
205
+ })),
206
+ degradedReason: state.degradedReason ?? null,
207
+ attempts: state.attempts,
208
+ maxAttempts: state.maxAttempts,
209
+ };
210
+ }
211
+ /**
212
+ * Whether a judge state is something the caller can still act on. `awaiting_decision` is the
213
+ * park; `evaluating`/`bouncing` are in flight and worth surfacing so a poller sees progress
214
+ * rather than an empty list that reads as "nothing is happening". A `passed`/`failed`/`skipped`
215
+ * judge is settled and carries no question.
216
+ */
217
+ function isLiveJudge(state) {
218
+ return (state.status === 'awaiting_decision' ||
219
+ state.status === 'evaluating' ||
220
+ state.status === 'bouncing');
221
+ }
222
+ /**
223
+ * Whether an iterative review (requirements / clarity / brainstorm — one lifecycle, one
224
+ * predicate) is something the caller can still act on. A settled review (`incorporated`) is
225
+ * history: it is the document downstream agents implement, not a question, so it is dropped from
226
+ * the list rather than presented as answerable. `incorporating`/`reviewing` ARE listed: the driver
227
+ * is mid-cycle and the caller should see that its answers are in flight.
228
+ */
229
+ function isLiveReview(review) {
230
+ return review.status !== 'incorporated';
231
+ }
232
+ /**
233
+ * Whether the fork state is something the caller can still act on. `awaiting_choice` is the park;
234
+ * `proposing`/`answering` are in-flight and worth surfacing so a poller sees progress. A `chosen`,
235
+ * `single_path` or `skipped` state is settled and carries no question.
236
+ */
237
+ function isLiveFork(state) {
238
+ return (state.status === 'awaiting_choice' ||
239
+ state.status === 'proposing' ||
240
+ state.status === 'answering');
241
+ }
242
+ /**
243
+ * Whether a PR deep review still wants a human. `awaiting_selection` is the park; `reviewing`,
244
+ * `challenging`, `fixing` and `posting` are work in flight a poller should see. `done`/`skipped`
245
+ * are settled.
246
+ */
247
+ function isLivePrReview(state) {
248
+ return state.status !== 'done' && state.status !== 'skipped';
249
+ }
250
+ /**
251
+ * Whether a human-test gate still wants a person. `awaiting_human` is the park; `provisioning`,
252
+ * `fixing` and `resolving_conflicts` are work in flight a poller should see; `passed` is settled.
253
+ *
254
+ * An EXHAUSTIVE switch rather than `phase !== 'passed'`, because the interesting direction is the
255
+ * one a negative test gets silently wrong: a phase added later (a `failed`, an `expired`) is
256
+ * terminal more often than not, and `!== 'passed'` would list it as a live question forever with
257
+ * nothing failing. Routed through {@link unclassifiedPhase} so the build breaks until somebody
258
+ * says which side the new phase is on.
259
+ */
260
+ function isLiveHumanTest(state) {
261
+ switch (state.phase) {
262
+ case 'awaiting_human':
263
+ case 'provisioning':
264
+ case 'fixing':
265
+ case 'resolving_conflicts':
266
+ return true;
267
+ case 'passed':
268
+ return false;
269
+ default:
270
+ return unclassifiedPhase(state.phase);
271
+ }
272
+ }
273
+ /**
274
+ * Whether a visual-confirmation gate still wants a person. `awaiting_human` is the park, `fixing`
275
+ * is in flight, `approved` is settled. Exhaustive for the reason {@link isLiveHumanTest} is, and
276
+ * the pending phase is not hypothetical here: the internal schema's own comment records a
277
+ * `capturing` phase deferred until the re-capture loop is wired.
278
+ */
279
+ function isLiveVisualConfirm(state) {
280
+ switch (state.phase) {
281
+ case 'awaiting_human':
282
+ case 'fixing':
283
+ return true;
284
+ case 'approved':
285
+ return false;
286
+ default:
287
+ return unclassifiedPhase(state.phase);
288
+ }
289
+ }
290
+ /**
291
+ * A gate phase this projection has no verdict for. Unreachable by TYPE: reaching it means a
292
+ * member was added to the picklist without being classified above, which is a build failure at the
293
+ * call site rather than a runtime branch.
294
+ *
295
+ * It still answers at runtime, because the type is total against the SCHEMA and only partial
296
+ * against the DATA: a row persisted by a newer build lands here. `false` is the safe answer, as a
297
+ * phase nobody classified is not something to put in front of a caller as an open question.
298
+ */
299
+ function unclassifiedPhase(_phase) {
300
+ return false;
301
+ }
302
+ /**
303
+ * Project the PRE-DISPATCH INPUT GATE's verdict for an external caller. The issue CODES are the same
304
+ * closed vocabulary the SPA renders, so an integration maps them to its own copy (or hands them to
305
+ * whoever filed the ticket) rather than parsing our prose.
306
+ */
307
+ export function toInputGateDecision(gate) {
308
+ return {
309
+ kind: 'input-gate',
310
+ status: gate.status,
311
+ mode: gate.mode,
312
+ issues: gate.issues,
313
+ checkedAt: gate.checkedAt,
314
+ };
315
+ }
316
+ /**
317
+ * Whether the gate's verdict is something the caller still has to act on. ONLY `blocked`, which is
318
+ * narrower than the other predicates here on purpose: `off`, `not_applicable` and `passed`
319
+ * are recorded facts about a run that was never held up, and `overridden` is a park somebody
320
+ * already answered. Listing any of them would put a decision in front of a caller with nothing to
321
+ * decide, and `parked` is read off the run's own status rather than from this list, so a settled
322
+ * verdict has nothing to add to it.
323
+ */
324
+ function isLiveInputGate(gate) {
325
+ return gate.status === 'blocked';
326
+ }
327
+ /**
328
+ * Build the run's decision list: whatever it is currently asking a human, plus the run status so a
329
+ * caller can distinguish "parked, answer me" from "still working".
330
+ *
331
+ * The run is RE-READ rather than taken from the `ScopedRun` the caller was gated with, because
332
+ * every mutating route builds its response through here AFTER acting: a `proceed` that advanced the
333
+ * run, or an `incorporate` that flipped it out of `blocked`, must not report the pre-action status.
334
+ * A run that vanished between the gate and here (a concurrent delete) falls back to the gated
335
+ * snapshot rather than 500-ing on an action that already succeeded.
336
+ *
337
+ * Everything that rides the run's STEPS is read off the instance already in hand; only the
338
+ * separately-stored iterative reviews cost a round-trip, and those are bounded by what the run's
339
+ * own step chain can produce (see {@link liveDialogueDecisions}).
340
+ */
341
+ export async function buildDecisionList(c, workspaceId, scoped) {
342
+ const container = c.get('container');
343
+ const execution = (await container.executionRepository.get(workspaceId, scoped.execution.id)) ?? scoped.execution;
344
+ // The dialogue reads and the fork read are INDEPENDENT point lookups in separate stores, so they
345
+ // are issued together: this projection is on the poll path AND rebuilt after every answer, and
346
+ // awaiting them in sequence made its latency the sum of every park kind a run could carry rather
347
+ // than the slowest one. The concatenation order below is still deterministic.
348
+ const [dialogue, fork] = await Promise.all([
349
+ liveDialogueDecisions(c, workspaceId, scoped.blockId, execution),
350
+ liveForkDecisions(c, workspaceId, execution),
351
+ ]);
352
+ const decisions = [
353
+ ...dialogue,
354
+ ...fork,
355
+ ...liveStepDecisions(execution, container.agentKindRegistry),
356
+ ];
357
+ // The input gate parks BEFORE the first dispatch, so when it is live it is the only thing the
358
+ // run is asking. Listed first for that reason: it is the earliest question, and the steps whose
359
+ // decisions follow have not run.
360
+ if (execution.inputGate && isLiveInputGate(execution.inputGate)) {
361
+ decisions.unshift(toInputGateDecision(execution.inputGate));
362
+ }
363
+ return {
364
+ runId: execution.id,
365
+ taskId: scoped.blockId,
366
+ status: execution.status,
367
+ // `blocked` IS the parked state — the run is waiting on a human and will not move until one
368
+ // of these decisions is answered. Read from the run itself rather than inferred from the
369
+ // decisions, so a run parked on a surface this projection doesn't model (`human-review`,
370
+ // whose answer is a person approving the PR on the VCS host) still reports `parked: true`
371
+ // with an empty list rather than silently claiming all is well.
372
+ parked: execution.status === 'blocked',
373
+ decisions,
374
+ };
375
+ }
376
+ /**
377
+ * The block-scoped iterative reviews a run can be parked on: requirements, clarity, and a
378
+ * brainstorm session per stage.
379
+ *
380
+ * Each of these lives in its OWN store, so each is a point read. Clarity and the brainstorms are
381
+ * fetched only when the run's step chain actually carries the kind that creates them: a run with
382
+ * no `clarity-review` step cannot be parked on a clarity review, and paying for the read on every
383
+ * poll of every run would make the cost of this endpoint a function of how many park kinds exist
384
+ * rather than of what the run can ask.
385
+ *
386
+ * Requirements is deliberately NOT step-gated. It has been read unconditionally since this surface
387
+ * shipped, which also serves a review run off-path from the block inspector; narrowing it now
388
+ * would take capability away from a live integration, and `/api/v1` does not do that.
389
+ */
390
+ async function liveDialogueDecisions(c, workspaceId, blockId, execution) {
391
+ const container = c.get('container');
392
+ const kinds = new Set(execution.steps.map((s) => s.agentKind));
393
+ const { requirements, clarity, brainstorm } = container;
394
+ // Each read is against a different store with nothing to say to the others, so they are issued
395
+ // together rather than chained. The RESULT order is fixed by the tuple, not by which store
396
+ // answered first, so a caller's decision list does not reshuffle between two polls of one run.
397
+ const [requirementsReview, clarityReview, brainstormSessions] = await Promise.all([
398
+ requirements ? requirements.service.getForBlock(workspaceId, blockId) : null,
399
+ clarity && kinds.has(CLARITY_REVIEW_AGENT_KIND)
400
+ ? clarity.service.getForBlock(workspaceId, blockId)
401
+ : null,
402
+ brainstorm
403
+ ? Promise.all(liveBrainstormStages(kinds).map((stage) => brainstorm.services[stage].getForBlock(workspaceId, blockId)))
404
+ : [],
405
+ ]);
406
+ const decisions = [];
407
+ if (requirementsReview && isLiveReview(requirementsReview)) {
408
+ decisions.push(toRequirementsDecision(requirementsReview));
409
+ }
410
+ if (clarityReview && isLiveReview(clarityReview)) {
411
+ decisions.push(toClarityDecision(clarityReview));
412
+ }
413
+ for (const session of brainstormSessions) {
414
+ if (session && isLiveReview(session))
415
+ decisions.push(toBrainstormDecision(session));
416
+ }
417
+ return decisions;
418
+ }
419
+ /**
420
+ * The brainstorm stages this run's step chain can park on. A block may hold one live session per
421
+ * stage at once, so this returns a LIST: a pipeline running both dialogues puts two brainstorm
422
+ * decisions in front of the caller, and collapsing them to one would hide whichever came second.
423
+ */
424
+ function liveBrainstormStages(kinds) {
425
+ const stages = [];
426
+ if (kinds.has(REQUIREMENTS_BRAINSTORM_AGENT_KIND))
427
+ stages.push('requirements');
428
+ if (kinds.has(ARCHITECTURE_BRAINSTORM_AGENT_KIND))
429
+ stages.push('architecture');
430
+ return stages;
431
+ }
432
+ /** The run's implementation-fork park, which the engine stores behind its own service read. */
433
+ async function liveForkDecisions(c, workspaceId, execution) {
434
+ const fork = await c.get('container').executionService.getForkDecision(workspaceId, execution.id);
435
+ return fork && isLiveFork(fork) ? [toForkDecision(fork)] : [];
436
+ }
437
+ /**
438
+ * Every park that rides the run's own STEPS — no repository round-trip, since all of this state
439
+ * lives on the instance already in hand (the whole reason none of it has a side table).
440
+ *
441
+ * Scanned over the WHOLE step chain rather than `currentStep` alone: the engine parks a run by
442
+ * leaving the step waiting, and which index that is depends on the park (a companion gate sits on
443
+ * the companion's step, an approval gate on the producer's), so keying off the cursor would report
444
+ * an empty list for a run that is plainly stopped.
445
+ */
446
+ function liveStepDecisions(execution, registry) {
447
+ const decisions = [];
448
+ execution.steps.forEach((step, index) => {
449
+ const approval = step.approval;
450
+ if (approval?.status === 'pending' && isGenericApprovalPark(execution, step, registry)) {
451
+ decisions.push(toApprovalDecision(step, index, approval));
452
+ }
453
+ if (step.decision && step.decision.chosen == null) {
454
+ decisions.push(toAgentDecision(step, step.decision));
455
+ }
456
+ if (step.judge && isLiveJudge(step.judge)) {
457
+ decisions.push(toJudgeDecision(step.agentKind, step.judge));
458
+ }
459
+ if (step.prReview && isLivePrReview(step.prReview)) {
460
+ decisions.push(toPrReviewDecision(step.prReview));
461
+ }
462
+ if (step.humanTest && isLiveHumanTest(step.humanTest)) {
463
+ decisions.push(toHumanTestDecision(step.humanTest));
464
+ }
465
+ if (step.visualConfirm && isLiveVisualConfirm(step.visualConfirm)) {
466
+ decisions.push(toVisualConfirmDecision(step.visualConfirm));
467
+ }
468
+ });
469
+ return decisions;
470
+ }
471
+ /**
472
+ * Whether a step's pending approval is an ORDINARY approval gate — the one the approve /
473
+ * request-changes / reject routes answer.
474
+ *
475
+ * `step.approval` is the engine's generic parking mechanism, so a review gate, a brainstorm, a
476
+ * human-verdict gate and a fork choice all carry a pending approval too. Reporting those as
477
+ * `approval-gate` would be the worst kind of wrong here: the caller would be offered verbs the
478
+ * engine refuses (`assertNotIterativeGate`), so a well-behaved integration would sit in a 409 loop
479
+ * against a park it was told it could answer. The classifier is the ENGINE's own, so what this
480
+ * surface offers and what the engine accepts cannot disagree.
481
+ *
482
+ * `companion-cap` is admitted rather than excluded: it IS answered here, by `resolve-exceeded`,
483
+ * and the projection flags it as `exceeded` so a caller reaches for that verb instead of approve.
484
+ */
485
+ function isGenericApprovalPark(execution, step, registry) {
486
+ const surface = dedicatedParkSurface(execution, step, registry);
487
+ return surface === null || surface === 'companion-cap';
488
+ }
489
+ //# sourceMappingURL=projection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projection.js","sourceRoot":"","sources":["../../../../src/modules/publicApi/decisions/projection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Hono } from 'hono';
2
+ import type { AppEnv } from '../../../http/env.js';
3
+ /**
4
+ * The REQUIREMENTS review loop. Each route is the external twin of a `RequirementReviewController`
5
+ * route, calling the same service method. The item-level routes are addressed by ITEM id (not
6
+ * review id): a headless caller reads the findings from `GET .../decisions`, and making it also
7
+ * thread a review id through would be ceremony over a value it never chose. The live review is
8
+ * resolved from the run's block, exactly as the SPA's block-scoped routes do.
9
+ */
10
+ export declare function registerRequirementsDecisionRoutes(app: Hono<AppEnv>): void;
11
+ //# sourceMappingURL=requirementsRoutes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requirementsRoutes.d.ts","sourceRoot":"","sources":["../../../../src/modules/publicApi/decisions/requirementsRoutes.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAChC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAIlD;;;;;;GAMG;AACH,wBAAgB,kCAAkC,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAqG1E"}
@@ -0,0 +1,91 @@
1
+ import { incorporatePublicRunRequirementsContract, proceedPublicRunRequirementsContract, replyPublicRunFindingContract, reReviewPublicRunRequirementsContract, resolvePublicRunRequirementsExceededContract, setPublicRunFindingStatusContract, } from '@cat-factory/contracts';
2
+ import { buildHonoRoute } from '@toad-contracts/hono';
3
+ import { buildDecisionList } from './projection.js';
4
+ import { failureBody, gateRequirementsAction } from './scope.js';
5
+ /**
6
+ * The REQUIREMENTS review loop. Each route is the external twin of a `RequirementReviewController`
7
+ * route, calling the same service method. The item-level routes are addressed by ITEM id (not
8
+ * review id): a headless caller reads the findings from `GET .../decisions`, and making it also
9
+ * thread a review id through would be ceremony over a value it never chose. The live review is
10
+ * resolved from the run's block, exactly as the SPA's block-scoped routes do.
11
+ */
12
+ export function registerRequirementsDecisionRoutes(app) {
13
+ // Answer one finding.
14
+ buildHonoRoute(app, replyPublicRunFindingContract, async (c) => {
15
+ const { runId, itemId } = c.req.valid('param');
16
+ const gated = await gateRequirementsAction(c, runId);
17
+ if ('fail' in gated) {
18
+ return c.json(failureBody(gated.fail), gated.fail.status);
19
+ }
20
+ const { workspaceId, scoped, requirements, review } = gated;
21
+ await requirements.service.replyToItem(workspaceId, review.id, itemId, c.req.valid('json').reply);
22
+ return c.json(await buildDecisionList(c, workspaceId, scoped), 200);
23
+ });
24
+ // Dismiss a finding as not applicable, or reopen one dismissed by mistake.
25
+ buildHonoRoute(app, setPublicRunFindingStatusContract, async (c) => {
26
+ const { runId, itemId } = c.req.valid('param');
27
+ const gated = await gateRequirementsAction(c, runId);
28
+ if ('fail' in gated) {
29
+ return c.json(failureBody(gated.fail), gated.fail.status);
30
+ }
31
+ const { workspaceId, scoped, requirements, review } = gated;
32
+ await requirements.service.setItemStatus(workspaceId, review.id, itemId, c.req.valid('json').status);
33
+ return c.json(await buildDecisionList(c, workspaceId, scoped), 200);
34
+ });
35
+ // Fold the recorded answers into the standardized document. ASYNCHRONOUS: it records the intent
36
+ // on the parked step and signals the durable driver, which folds + re-reviews in the background —
37
+ // so the returned list shows the review `incorporating`, and the caller learns the outcome from
38
+ // the SSE stream or a follow-up read, exactly as the SPA does.
39
+ buildHonoRoute(app, incorporatePublicRunRequirementsContract, async (c) => {
40
+ const { runId } = c.req.valid('param');
41
+ const gated = await gateRequirementsAction(c, runId);
42
+ if ('fail' in gated) {
43
+ return c.json(failureBody(gated.fail), gated.fail.status);
44
+ }
45
+ const { workspaceId, scoped } = gated;
46
+ await c
47
+ .get('container')
48
+ .executionService.requirementsReview.incorporate(workspaceId, scoped.blockId, c.req.valid('json').feedback);
49
+ return c.json(await buildDecisionList(c, workspaceId, scoped), 200);
50
+ });
51
+ // One more reviewer pass over the incorporated document. On convergence the parked run advances.
52
+ buildHonoRoute(app, reReviewPublicRunRequirementsContract, async (c) => {
53
+ const { runId } = c.req.valid('param');
54
+ const gated = await gateRequirementsAction(c, runId);
55
+ if ('fail' in gated) {
56
+ return c.json(failureBody(gated.fail), gated.fail.status);
57
+ }
58
+ const { workspaceId, scoped } = gated;
59
+ await c
60
+ .get('container')
61
+ .executionService.requirementsReview.reReview(workspaceId, scoped.blockId);
62
+ return c.json(await buildDecisionList(c, workspaceId, scoped), 200);
63
+ });
64
+ // Settle the requirements phase and advance the parked run.
65
+ buildHonoRoute(app, proceedPublicRunRequirementsContract, async (c) => {
66
+ const { runId } = c.req.valid('param');
67
+ const gated = await gateRequirementsAction(c, runId);
68
+ if ('fail' in gated) {
69
+ return c.json(failureBody(gated.fail), gated.fail.status);
70
+ }
71
+ const { workspaceId, scoped } = gated;
72
+ await c
73
+ .get('container')
74
+ .executionService.requirementsReview.proceed(workspaceId, scoped.blockId);
75
+ return c.json(await buildDecisionList(c, workspaceId, scoped), 200);
76
+ });
77
+ // Resolve a review that hit its iteration cap (extra round / proceed / stop and reset).
78
+ buildHonoRoute(app, resolvePublicRunRequirementsExceededContract, async (c) => {
79
+ const { runId } = c.req.valid('param');
80
+ const gated = await gateRequirementsAction(c, runId);
81
+ if ('fail' in gated) {
82
+ return c.json(failureBody(gated.fail), gated.fail.status);
83
+ }
84
+ const { workspaceId, scoped } = gated;
85
+ await c
86
+ .get('container')
87
+ .executionService.requirementsReview.resolveExceeded(workspaceId, scoped.blockId, c.req.valid('json').choice);
88
+ return c.json(await buildDecisionList(c, workspaceId, scoped), 200);
89
+ });
90
+ }
91
+ //# sourceMappingURL=requirementsRoutes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requirementsRoutes.js","sourceRoot":"","sources":["../../../../src/modules/publicApi/decisions/requirementsRoutes.ts"],"names":[],"mappings":"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"}