@cat-factory/server 0.323.0 → 0.325.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 (57) hide show
  1. package/dist/app.d.ts.map +1 -1
  2. package/dist/app.js +5 -0
  3. package/dist/app.js.map +1 -1
  4. package/dist/modules/publicApi/PublicApiController.d.ts.map +1 -1
  5. package/dist/modules/publicApi/PublicApiController.js +5 -292
  6. package/dist/modules/publicApi/PublicApiController.js.map +1 -1
  7. package/dist/modules/publicApi/PublicDecisionController.d.ts.map +1 -1
  8. package/dist/modules/publicApi/PublicDecisionController.js +2 -0
  9. package/dist/modules/publicApi/PublicDecisionController.js.map +1 -1
  10. package/dist/modules/publicApi/PublicFragmentController.d.ts +4 -0
  11. package/dist/modules/publicApi/PublicFragmentController.d.ts.map +1 -0
  12. package/dist/modules/publicApi/PublicFragmentController.js +61 -0
  13. package/dist/modules/publicApi/PublicFragmentController.js.map +1 -0
  14. package/dist/modules/publicApi/boardProjection.d.ts.map +1 -1
  15. package/dist/modules/publicApi/boardProjection.js +4 -0
  16. package/dist/modules/publicApi/boardProjection.js.map +1 -1
  17. package/dist/modules/publicApi/decisions/bugFishingProjection.d.ts +19 -0
  18. package/dist/modules/publicApi/decisions/bugFishingProjection.d.ts.map +1 -0
  19. package/dist/modules/publicApi/decisions/bugFishingProjection.js +130 -0
  20. package/dist/modules/publicApi/decisions/bugFishingProjection.js.map +1 -0
  21. package/dist/modules/publicApi/decisions/bugFishingRoutes.d.ts +4 -0
  22. package/dist/modules/publicApi/decisions/bugFishingRoutes.d.ts.map +1 -0
  23. package/dist/modules/publicApi/decisions/bugFishingRoutes.js +73 -0
  24. package/dist/modules/publicApi/decisions/bugFishingRoutes.js.map +1 -0
  25. package/dist/modules/publicApi/decisions/projection.d.ts +21 -0
  26. package/dist/modules/publicApi/decisions/projection.d.ts.map +1 -1
  27. package/dist/modules/publicApi/decisions/projection.js +65 -11
  28. package/dist/modules/publicApi/decisions/projection.js.map +1 -1
  29. package/dist/modules/publicApi/fragmentCatalog.d.ts +47 -0
  30. package/dist/modules/publicApi/fragmentCatalog.d.ts.map +1 -0
  31. package/dist/modules/publicApi/fragmentCatalog.js +106 -0
  32. package/dist/modules/publicApi/fragmentCatalog.js.map +1 -0
  33. package/dist/modules/publicApi/openapiDocument.generated.d.ts +1 -1
  34. package/dist/modules/publicApi/openapiDocument.generated.d.ts.map +1 -1
  35. package/dist/modules/publicApi/openapiDocument.generated.js +1 -1
  36. package/dist/modules/publicApi/openapiDocument.generated.js.map +1 -1
  37. package/dist/modules/publicApi/publicApiAdmission.d.ts +1 -1
  38. package/dist/modules/publicApi/publicApiAdmission.d.ts.map +1 -1
  39. package/dist/modules/publicApi/publicApiAdmission.js +5 -1
  40. package/dist/modules/publicApi/publicApiAdmission.js.map +1 -1
  41. package/dist/modules/publicApi/publicApiStream.d.ts +50 -1
  42. package/dist/modules/publicApi/publicApiStream.d.ts.map +1 -1
  43. package/dist/modules/publicApi/publicApiStream.js +83 -5
  44. package/dist/modules/publicApi/publicApiStream.js.map +1 -1
  45. package/dist/modules/publicApi/publicApiStreamRoutes.d.ts +30 -0
  46. package/dist/modules/publicApi/publicApiStreamRoutes.d.ts.map +1 -0
  47. package/dist/modules/publicApi/publicApiStreamRoutes.js +275 -0
  48. package/dist/modules/publicApi/publicApiStreamRoutes.js.map +1 -0
  49. package/dist/modules/publicApi/runProjection.d.ts +30 -0
  50. package/dist/modules/publicApi/runProjection.d.ts.map +1 -0
  51. package/dist/modules/publicApi/runProjection.js +127 -0
  52. package/dist/modules/publicApi/runProjection.js.map +1 -0
  53. package/dist/modules/publicApi/taskCreation.d.ts +2 -0
  54. package/dist/modules/publicApi/taskCreation.d.ts.map +1 -1
  55. package/dist/modules/publicApi/taskCreation.js +20 -12
  56. package/dist/modules/publicApi/taskCreation.js.map +1 -1
  57. package/package.json +8 -8
@@ -1 +1 @@
1
- {"version":3,"file":"openapiDocument.generated.js","sourceRoot":"","sources":["../../../src/modules/publicApi/openapiDocument.generated.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,EAAE;AACF,0FAA0F;AAC1F,8FAA8F;AAC9F,8FAA8F;AAC9F,8EAA8E;AAE9E,MAAM,CAAC,MAAM,YAAY,GAAG,8y9vBAA8y9vB,CAAA"}
1
+ {"version":3,"file":"openapiDocument.generated.js","sourceRoot":"","sources":["../../../src/modules/publicApi/openapiDocument.generated.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,EAAE;AACF,0FAA0F;AAC1F,8FAA8F;AAC9F,8FAA8F;AAC9F,8EAA8E;AAE9E,MAAM,CAAC,MAAM,YAAY,GAAG,yizxBAAyizxB,CAAA"}
@@ -98,7 +98,7 @@ export declare const PUBLIC_TASK_STOP_PATH = "POST /api/v1/tasks/:taskId/stop";
98
98
  * decision surface admits it cannot answer are one decision. See
99
99
  * `backend/docs/adr/0043-public-decision-surface.md`.
100
100
  */
101
- export declare const PUBLICLY_ANSWERABLE_PARK_SURFACES: Map<string, "agent-decision" | "approval-gate" | "brainstorm" | "clarity-review" | "follow-ups" | "fork" | "human-test" | "input-gate" | "interview" | "judge" | "pr-review" | "requirements-review" | "visual-confirmation">;
101
+ export declare const PUBLICLY_ANSWERABLE_PARK_SURFACES: Map<string, "agent-decision" | "approval-gate" | "brainstorm" | "bug-fishing" | "clarity-review" | "follow-ups" | "fork" | "human-test" | "input-gate" | "interview" | "judge" | "pr-review" | "requirements-review" | "visual-confirmation">;
102
102
  /** The pipeline shape admission reasons about: the step chain plus its parallel flag arrays. */
103
103
  export interface AdmissiblePipelineShape {
104
104
  agentKinds: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"publicApiAdmission.d.ts","sourceRoot":"","sources":["../../../src/modules/publicApi/publicApiAdmission.ts"],"names":[],"mappings":"AAaA,OAAO,EACL,KAAK,iBAAiB,EAUvB,MAAM,qBAAqB,CAAA;AAG5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAEvD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,oBAAoB,aAK/B,CAAA;AASF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,eAAe,CAAA;AAuBnD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,6BAA6B,sBAAsB,CAAA;AAkDhE;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,iCAAiC,CAAA;AACpE,eAAO,MAAM,qBAAqB,oCAAoC,CAAA;AAEtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,iCAAiC,+NAW5C,CAAA;AAEF,gGAAgG;AAChG,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,6FAA6F;IAC7F,OAAO,CAAC,EAAE,OAAO,EAAE,CAAA;IACnB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,EAAE,CAAA;IACjB;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;QAAE,YAAY,CAAC,EAAE;YAAE,UAAU,CAAC,EAAE,OAAO,CAAA;SAAE,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAA;CACjG;AASD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,uBAAuB,EACjC,QAAQ,EAAE,iBAAiB,GAC1B,OAAO,CAIT;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,0EAA0E;IAC1E,UAAU,EAAE,iBAAiB,CAAA;IAC7B,2GAA2G;IAC3G,KAAK,EAAE,YAAY,CAAA;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,YAAY,EAAE,YAAY,CAAA;CAC3B,GAAG,mBAAmB,CAEtB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,uBAAuB,EACjC,UAAU,EAAE,mBAAmB,GAC9B,OAAO,CAET;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,uBAAuB,EACjC,UAAU,EAAE,mBAAmB,GAC9B,MAAM,EAAE,CAwBV;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CA2BjG;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,uBAAuB,EACjC,UAAU,EAAE,mBAAmB,EAC/B,OAAO,EAAE;IAAE,eAAe,EAAE,OAAO,CAAA;CAAE,GACpC,MAAM,EAAE,CAKV;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,uBAAuB,EACjC,UAAU,EAAE,mBAAmB,GAC9B,OAAO,CAIT"}
1
+ {"version":3,"file":"publicApiAdmission.d.ts","sourceRoot":"","sources":["../../../src/modules/publicApi/publicApiAdmission.ts"],"names":[],"mappings":"AAaA,OAAO,EACL,KAAK,iBAAiB,EAWvB,MAAM,qBAAqB,CAAA;AAG5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAEvD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,oBAAoB,aAK/B,CAAA;AASF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,eAAe,CAAA;AAuBnD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,6BAA6B,sBAAsB,CAAA;AAkDhE;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,iCAAiC,CAAA;AACpE,eAAO,MAAM,qBAAqB,oCAAoC,CAAA;AAEtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,iCAAiC,+OAe5C,CAAA;AAEF,gGAAgG;AAChG,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,6FAA6F;IAC7F,OAAO,CAAC,EAAE,OAAO,EAAE,CAAA;IACnB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,EAAE,CAAA;IACjB;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;QAAE,YAAY,CAAC,EAAE;YAAE,UAAU,CAAC,EAAE,OAAO,CAAA;SAAE,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAA;CACjG;AASD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,uBAAuB,EACjC,QAAQ,EAAE,iBAAiB,GAC1B,OAAO,CAIT;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,0EAA0E;IAC1E,UAAU,EAAE,iBAAiB,CAAA;IAC7B,2GAA2G;IAC3G,KAAK,EAAE,YAAY,CAAA;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,YAAY,EAAE,YAAY,CAAA;CAC3B,GAAG,mBAAmB,CAEtB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,uBAAuB,EACjC,UAAU,EAAE,mBAAmB,GAC9B,OAAO,CAET;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,uBAAuB,EACjC,UAAU,EAAE,mBAAmB,GAC9B,MAAM,EAAE,CAwBV;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CA2BjG;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,uBAAuB,EACjC,UAAU,EAAE,mBAAmB,EAC/B,OAAO,EAAE;IAAE,eAAe,EAAE,OAAO,CAAA;CAAE,GACpC,MAAM,EAAE,CAKV;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,uBAAuB,EACjC,UAAU,EAAE,mBAAmB,GAC9B,OAAO,CAIT"}
@@ -10,7 +10,7 @@
10
10
  // cross-runtime conformance suite.
11
11
  //
12
12
  // See backend/docs/adr/0047-headless-clarification-loop.md (D1).
13
- import { ARCHITECTURE_BRAINSTORM_AGENT_KIND, CLARITY_REVIEW_AGENT_KIND, CURATION_GATE_TRAIT, INTERVIEW_GATE_TRAIT, isInlineModelStep, PR_REVIEWER_KIND, REQUIREMENTS_BRAINSTORM_AGENT_KIND, REQUIREMENTS_REVIEW_AGENT_KIND, traitsFor, } from '@cat-factory/agents';
13
+ import { ARCHITECTURE_BRAINSTORM_AGENT_KIND, BUG_FISHER_KIND, CLARITY_REVIEW_AGENT_KIND, CURATION_GATE_TRAIT, INTERVIEW_GATE_TRAIT, isInlineModelStep, PR_REVIEWER_KIND, REQUIREMENTS_BRAINSTORM_AGENT_KIND, REQUIREMENTS_REVIEW_AGENT_KIND, traitsFor, } from '@cat-factory/agents';
14
14
  /**
15
15
  * Inline agent kinds that PARK a run on a human/gate decision. This MUST list every
16
16
  * inline-and-parking kind: the two review gates AND the two brainstorm dialogues (all four set the
@@ -197,6 +197,10 @@ export const PUBLICLY_ANSWERABLE_PARK_SURFACES = new Map([
197
197
  // The PR deep review's finding curation: `…/decisions/pr-review/resolve` (finish / fix / post),
198
198
  // plus per-finding dismiss, challenge and resume.
199
199
  [PR_REVIEWER_KIND, 'pr-review'],
200
+ // The bug-fishing expedition's triage: `…/decisions/bug-fishing/address` (one bug-fix task per
201
+ // marked finding), plus per-finding dismiss and `…/resolve` to finish. The second curating kind
202
+ // to gain verbs, which is why `curationWait` now names only a deployment's OWN curating kinds.
203
+ [BUG_FISHER_KIND, 'bug-fishing'],
200
204
  ]);
201
205
  /** The enabled steps of a pipeline, paired with their ORIGINAL index (gates are index-aligned). */
202
206
  function enabledSteps(pipeline) {
@@ -1 +1 @@
1
- {"version":3,"file":"publicApiAdmission.js","sourceRoot":"","sources":["../../../src/modules/publicApi/publicApiAdmission.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"publicApiAdmission.js","sourceRoot":"","sources":["../../../src/modules/publicApi/publicApiAdmission.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { ExecutionStatus, PublicRun } from '@cat-factory/contracts';
1
+ import type { ExecutionStatus, PublicDecisionList, PublicRun } from '@cat-factory/contracts';
2
2
  /**
3
3
  * Whether a run is PARKED: waiting on a human decision and unable to move until one is answered.
4
4
  *
@@ -21,6 +21,25 @@ export declare function createParkAnnouncer(): {
21
21
  /** Feed each poll's status; true exactly on the tick a NEW park should be announced. */
22
22
  shouldAnnounce: (status: ExecutionStatus) => boolean;
23
23
  };
24
+ /**
25
+ * Tracks the last DECISION payload written, so a `decision-state` frame is emitted only when the
26
+ * run's decision list actually moved.
27
+ *
28
+ * The counterpart of {@link createParkAnnouncer}, and it fails the same two invisible ways. A
29
+ * detector that always fires turns a park into an unbounded stream of identical payloads, since a
30
+ * park lasts as long as a human takes and the projection is rebuilt every tick. One that latches
31
+ * and never re-arms delivers the first state and then goes quiet, which is the failure the channel
32
+ * exists to prevent: a review whose slices report one by one would report the first and nothing
33
+ * after it, and a caller cannot tell that from a reviewer that stopped.
34
+ *
35
+ * Compared on the SERIALIZED payload rather than a field of it, for the reason the progress frames
36
+ * beside it are: what counts as "moved" is decided by the whole projection, so a decision kind that
37
+ * grows a field is covered with no edit here.
38
+ */
39
+ export declare function createDecisionAnnouncer(): {
40
+ /** Feed each tick's serialized decision list; true exactly when it should be written. */
41
+ shouldAnnounce: (payload: string) => boolean;
42
+ };
24
43
  /**
25
44
  * Per-step character cap on the deliverable a STREAM frame carries, applied to `output` and to
26
45
  * `data`'s serialized size alike.
@@ -49,4 +68,34 @@ export declare const STREAM_DELIVERABLE_PREVIEW_CHARS = 2000;
49
68
  * then absent in a way that reads as never written.
50
69
  */
51
70
  export declare function reduceRunForStream(run: PublicRun): PublicRun;
71
+ /**
72
+ * Per-string character cap on the model-authored text a DECISION frame carries.
73
+ *
74
+ * Smaller than {@link STREAM_DELIVERABLE_PREVIEW_CHARS}, which bounds the same class of content on
75
+ * the run streams, and the difference is what the cap is paid PER. The run stream pays it once per
76
+ * step, so a pipeline's length bounds the frame; a decision list pays it once per string in a list
77
+ * the RUN sizes (a deep review parks with one finding per issue it found, each carrying a detail,
78
+ * an evidence quote and a suggested fix), so the same number would still let one frame reach
79
+ * hundreds of kilobytes and be re-sent on every change.
80
+ *
81
+ * Sized as a preview a caller can act on: enough to recognise what a finding is about and decide
82
+ * whether to fetch it whole, which is what `GET /api/v1/runs/{runId}/decisions` is for.
83
+ */
84
+ export declare const STREAM_DECISION_TEXT_PREVIEW_CHARS = 1000;
85
+ /**
86
+ * Reduce a decision list for an SSE frame: clip every over-long string to a preview and report the
87
+ * clip on the list's own `truncated` flag.
88
+ *
89
+ * Kind-AGNOSTIC by construction, for the same reason {@link createDecisionAnnouncer} compares the
90
+ * serialized payload rather than a field of it: what a decision kind carries is decided by that
91
+ * kind, so a rule written per kind is one a new kind (or a new field on an old one) silently
92
+ * escapes. Clipping by LENGTH wherever the text sits covers all fourteen with no edit, and the ids,
93
+ * statuses, counts and enums a caller routes on are short by construction, so nothing it acts on
94
+ * is what gets clipped.
95
+ *
96
+ * What is NEVER reduced is the list itself. Every decision the run is asking is in every frame,
97
+ * because an empty `decisions` that means "this payload was narrowed" and one that means "nothing
98
+ * is being asked" are opposite facts, and telling them apart is the whole job of this surface.
99
+ */
100
+ export declare function reduceDecisionsForStream(list: PublicDecisionList): PublicDecisionList;
52
101
  //# sourceMappingURL=publicApiStream.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"publicApiStream.d.ts","sourceRoot":"","sources":["../../../src/modules/publicApi/publicApiStream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAiB,MAAM,wBAAwB,CAAA;AAUvF;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAEzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,IAAI;IACrC,wFAAwF;IACxF,cAAc,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,OAAO,CAAA;CACrD,CAaA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,gCAAgC,OAAQ,CAAA;AAErD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,SAAS,GAAG,SAAS,CAE5D"}
1
+ {"version":3,"file":"publicApiStream.d.ts","sourceRoot":"","sources":["../../../src/modules/publicApi/publicApiStream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAClB,SAAS,EAEV,MAAM,wBAAwB,CAAA;AAa/B;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAEzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,IAAI;IACrC,wFAAwF;IACxF,cAAc,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,OAAO,CAAA;CACrD,CAaA;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,IAAI;IACzC,yFAAyF;IACzF,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAA;CAC7C,CASA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,gCAAgC,OAAQ,CAAA;AAErD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,SAAS,GAAG,SAAS,CAE5D;AAkBD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kCAAkC,OAAQ,CAAA;AAEvD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,kBAAkB,GAAG,kBAAkB,CAiBrF"}
@@ -1,9 +1,12 @@
1
- // Park announcement for the public SSE streams (`/api/v1/jobs/:id/events` and the board task
2
- // stream). Extracted from `PublicApiController` because BOTH streams need identical behaviour and
3
- // the state is exactly the kind that drifts when copy-pasted: one loop re-arming on resume and the
4
- // other not is invisible in review and only shows up as a second park nobody was told about.
1
+ // The pure decisions the public SSE streams share: when a park is announced, when a decision list
2
+ // counts as moved, and how a frame is reduced for the wire. The loops themselves are in
3
+ // `publicApiStreamRoutes.ts`.
5
4
  //
6
- // It is also the only part of the stream loop that is unit-testable at all everything around it
5
+ // Split out because BOTH streams need identical behaviour and this is exactly the kind of state
6
+ // that drifts when copy-pasted: one loop re-arming on resume and the other not is invisible in
7
+ // review and only shows up as a second park nobody was told about.
8
+ //
9
+ // It is also the only part of the stream loop that is unit-testable at all: everything around it
7
10
  // is a live poll over the store behind a hijacked response.
8
11
  /**
9
12
  * Whether a run is PARKED: waiting on a human decision and unable to move until one is answered.
@@ -40,6 +43,32 @@ export function createParkAnnouncer() {
40
43
  },
41
44
  };
42
45
  }
46
+ /**
47
+ * Tracks the last DECISION payload written, so a `decision-state` frame is emitted only when the
48
+ * run's decision list actually moved.
49
+ *
50
+ * The counterpart of {@link createParkAnnouncer}, and it fails the same two invisible ways. A
51
+ * detector that always fires turns a park into an unbounded stream of identical payloads, since a
52
+ * park lasts as long as a human takes and the projection is rebuilt every tick. One that latches
53
+ * and never re-arms delivers the first state and then goes quiet, which is the failure the channel
54
+ * exists to prevent: a review whose slices report one by one would report the first and nothing
55
+ * after it, and a caller cannot tell that from a reviewer that stopped.
56
+ *
57
+ * Compared on the SERIALIZED payload rather than a field of it, for the reason the progress frames
58
+ * beside it are: what counts as "moved" is decided by the whole projection, so a decision kind that
59
+ * grows a field is covered with no edit here.
60
+ */
61
+ export function createDecisionAnnouncer() {
62
+ let last = null;
63
+ return {
64
+ shouldAnnounce(payload) {
65
+ if (payload === last)
66
+ return false;
67
+ last = payload;
68
+ return true;
69
+ },
70
+ };
71
+ }
43
72
  /**
44
73
  * Per-step character cap on the deliverable a STREAM frame carries, applied to `output` and to
45
74
  * `data`'s serialized size alike.
@@ -85,4 +114,53 @@ function reduceStepForStream(step) {
85
114
  truncated: true,
86
115
  };
87
116
  }
117
+ /**
118
+ * Per-string character cap on the model-authored text a DECISION frame carries.
119
+ *
120
+ * Smaller than {@link STREAM_DELIVERABLE_PREVIEW_CHARS}, which bounds the same class of content on
121
+ * the run streams, and the difference is what the cap is paid PER. The run stream pays it once per
122
+ * step, so a pipeline's length bounds the frame; a decision list pays it once per string in a list
123
+ * the RUN sizes (a deep review parks with one finding per issue it found, each carrying a detail,
124
+ * an evidence quote and a suggested fix), so the same number would still let one frame reach
125
+ * hundreds of kilobytes and be re-sent on every change.
126
+ *
127
+ * Sized as a preview a caller can act on: enough to recognise what a finding is about and decide
128
+ * whether to fetch it whole, which is what `GET /api/v1/runs/{runId}/decisions` is for.
129
+ */
130
+ export const STREAM_DECISION_TEXT_PREVIEW_CHARS = 1_000;
131
+ /**
132
+ * Reduce a decision list for an SSE frame: clip every over-long string to a preview and report the
133
+ * clip on the list's own `truncated` flag.
134
+ *
135
+ * Kind-AGNOSTIC by construction, for the same reason {@link createDecisionAnnouncer} compares the
136
+ * serialized payload rather than a field of it: what a decision kind carries is decided by that
137
+ * kind, so a rule written per kind is one a new kind (or a new field on an old one) silently
138
+ * escapes. Clipping by LENGTH wherever the text sits covers all fourteen with no edit, and the ids,
139
+ * statuses, counts and enums a caller routes on are short by construction, so nothing it acts on
140
+ * is what gets clipped.
141
+ *
142
+ * What is NEVER reduced is the list itself. Every decision the run is asking is in every frame,
143
+ * because an empty `decisions` that means "this payload was narrowed" and one that means "nothing
144
+ * is being asked" are opposite facts, and telling them apart is the whole job of this surface.
145
+ */
146
+ export function reduceDecisionsForStream(list) {
147
+ let clipped = false;
148
+ const clip = (value) => {
149
+ if (typeof value === 'string') {
150
+ if (value.length <= STREAM_DECISION_TEXT_PREVIEW_CHARS)
151
+ return value;
152
+ clipped = true;
153
+ return value.slice(0, STREAM_DECISION_TEXT_PREVIEW_CHARS);
154
+ }
155
+ if (Array.isArray(value))
156
+ return value.map(clip);
157
+ if (value && typeof value === 'object') {
158
+ return Object.fromEntries(Object.entries(value).map(([key, v]) => [key, clip(v)]));
159
+ }
160
+ return value;
161
+ };
162
+ const decisions = clip(list.decisions);
163
+ const unanswerable = clip(list.unanswerable);
164
+ return { ...list, decisions, unanswerable, truncated: clipped };
165
+ }
88
166
  //# sourceMappingURL=publicApiStream.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"publicApiStream.js","sourceRoot":"","sources":["../../../src/modules/publicApi/publicApiStream.ts"],"names":[],"mappings":"AAEA,6FAA6F;AAC7F,kGAAkG;AAClG,mGAAmG;AACnG,6FAA6F;AAC7F,EAAE;AACF,kGAAkG;AAClG,4DAA4D;AAE5D;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAuB;IAC9C,OAAO,MAAM,KAAK,SAAS,CAAA;AAC7B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB;IAIjC,IAAI,SAAS,GAAG,KAAK,CAAA;IACrB,OAAO;QACL,cAAc,CAAC,MAAuB;YACpC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACtB,SAAS,GAAG,KAAK,CAAA;gBACjB,OAAO,KAAK,CAAA;YACd,CAAC;YACD,IAAI,SAAS;gBAAE,OAAO,KAAK,CAAA;YAC3B,SAAS,GAAG,IAAI,CAAA;YAChB,OAAO,IAAI,CAAA;QACb,CAAC;KACF,CAAA;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,KAAK,CAAA;AAErD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAc;IAC/C,OAAO,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,mBAAmB,CAAC,EAAE,CAAA;AAC9D,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAmB;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;IAC1B,MAAM,OAAO,GAAG,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,MAAM,GAAG,gCAAgC,CAAA;IACnF,6FAA6F;IAC7F,mEAAmE;IACnE,MAAM,QAAQ,GACZ,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,gCAAgC,CAAA;IAC1F,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAA;IACtC,OAAO;QACL,GAAG,IAAI;QACP,MAAM,EAAE,OAAO,IAAI,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,gCAAgC,CAAC,CAAC,CAAC,CAAC,MAAM;QAC/F,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI;QACjC,SAAS,EAAE,IAAI;KAChB,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"publicApiStream.js","sourceRoot":"","sources":["../../../src/modules/publicApi/publicApiStream.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { Hono } from 'hono';
2
+ import type { AppEnv } from '../../http/env.js';
3
+ /**
4
+ * `GET /api/v1/runs/:runId/decision-events`: the run's whole `PublicDecisionList`, pushed as a
5
+ * `decision-state` frame whenever it changes.
6
+ *
7
+ * This is what makes a chunked operation legible without polling. A run's progress lives on the
8
+ * execution, but what a PR deep review or a bug-fishing expedition is DOING lives on state the run
9
+ * projection does not carry (`step.prReview`, `step.bugFishing`): its own status, how many slices
10
+ * have reported, a challenge verdict landing. None of that moves `publicRun`, so on the two run
11
+ * streams beside this one a review emits no frame at all for its entire duration and then a single
12
+ * `decision` at the park. The documentation told a caller to poll `/runs/:runId/decisions` for
13
+ * exactly the progress those streams could not give it; this serves the same payload by push.
14
+ *
15
+ * ITS OWN ROUTE rather than a flag on the run streams, and the reason is the published SDKs rather
16
+ * than taste. A query parameter added to an existing operation is emitted as a positional argument
17
+ * ahead of the trailing options bag, so `stream(id, options)` becomes `stream(id, query, options)`
18
+ * in TypeScript and `Stream(ctx, id)` grows an argument in Go: an in-place retype of four released
19
+ * clients, which this surface does not do. A NEW operation is the additive shape, and it turns out
20
+ * to be the better one anyway. It is keyed by RUN like the decision list it streams, so one route
21
+ * serves a board task and a headless job where a flag needed adding to two; and the run streams
22
+ * stay what they are, which is progress channels.
23
+ *
24
+ * `read` scope, matching `GET /api/v1/runs/:runId/decisions`: knowing what a run is waiting on is a
25
+ * monitoring concern, and answering is what needs `decide`.
26
+ */
27
+ export declare function registerRunDecisionStreamRoute(app: Hono<AppEnv>): void;
28
+ export declare function registerJobStreamRoute(app: Hono<AppEnv>): void;
29
+ export declare function registerTaskRunStreamRoute(app: Hono<AppEnv>): void;
30
+ //# sourceMappingURL=publicApiStreamRoutes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publicApiStreamRoutes.d.ts","sourceRoot":"","sources":["../../../src/modules/publicApi/publicApiStreamRoutes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAEhC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAoC/C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAgEtE;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAqF9D;AAED,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAgFlE"}
@@ -0,0 +1,275 @@
1
+ import { streamSSE } from 'hono/streaming';
2
+ import { projectDecisionList } from './decisions/projection.js';
3
+ import { loadScopedRun } from './decisions/scope.js';
4
+ import { authorize } from './publicApiAuth.js';
5
+ import { createDecisionAnnouncer, createParkAnnouncer, isParked, reduceDecisionsForStream, reduceRunForStream, } from './publicApiStream.js';
6
+ import { loadPublicJob, toPublicJob, toPublicRun } from './runProjection.js';
7
+ // The public SSE routes: the two RUN streams (`GET /api/v1/jobs/:id/events` and
8
+ // `GET /api/v1/tasks/:taskId/events`) and the DECISION stream beside them
9
+ // (`GET /api/v1/runs/:runId/decision-events`).
10
+ //
11
+ // Both are BOUNDED POLLS over the persisted run rather than a subscription to an event hub, which
12
+ // is what makes them runtime-symmetric by construction: there is nothing per-facade to wire, so
13
+ // the Worker and Node serve the identical stream. Frames are de-duplicated on the serialized
14
+ // payload, so a quiet run produces a quiet stream.
15
+ //
16
+ // Neither is a JSON route contract (the response is `text/event-stream`), so both are raw Hono
17
+ // routes documented by hand in `scripts/generate-openapi.mjs`. Their `read` scope literal lives
18
+ // here and is restated there; keep the two in step.
19
+ //
20
+ // They live in their own module because they are the bulk of what `PublicApiController` used to
21
+ // be and they share the decision channel below, which neither of that controller's other route
22
+ // groups has anything to do with.
23
+ /** How often a stream re-reads the run, and the hard cap on how long the connection stays open. */
24
+ const SSE_POLL_MS = 1000;
25
+ const SSE_MAX_MS = 5 * 60 * 1000;
26
+ /** Re-verify the caller's key at most this often on a live stream, so a mid-stream revoke cuts it. */
27
+ const SSE_REAUTH_MS = 5000;
28
+ /**
29
+ * `GET /api/v1/runs/:runId/decision-events`: the run's whole `PublicDecisionList`, pushed as a
30
+ * `decision-state` frame whenever it changes.
31
+ *
32
+ * This is what makes a chunked operation legible without polling. A run's progress lives on the
33
+ * execution, but what a PR deep review or a bug-fishing expedition is DOING lives on state the run
34
+ * projection does not carry (`step.prReview`, `step.bugFishing`): its own status, how many slices
35
+ * have reported, a challenge verdict landing. None of that moves `publicRun`, so on the two run
36
+ * streams beside this one a review emits no frame at all for its entire duration and then a single
37
+ * `decision` at the park. The documentation told a caller to poll `/runs/:runId/decisions` for
38
+ * exactly the progress those streams could not give it; this serves the same payload by push.
39
+ *
40
+ * ITS OWN ROUTE rather than a flag on the run streams, and the reason is the published SDKs rather
41
+ * than taste. A query parameter added to an existing operation is emitted as a positional argument
42
+ * ahead of the trailing options bag, so `stream(id, options)` becomes `stream(id, query, options)`
43
+ * in TypeScript and `Stream(ctx, id)` grows an argument in Go: an in-place retype of four released
44
+ * clients, which this surface does not do. A NEW operation is the additive shape, and it turns out
45
+ * to be the better one anyway. It is keyed by RUN like the decision list it streams, so one route
46
+ * serves a board task and a headless job where a flag needed adding to two; and the run streams
47
+ * stay what they are, which is progress channels.
48
+ *
49
+ * `read` scope, matching `GET /api/v1/runs/:runId/decisions`: knowing what a run is waiting on is a
50
+ * monitoring concern, and answering is what needs `decide`.
51
+ */
52
+ export function registerRunDecisionStreamRoute(app) {
53
+ app.get('/api/v1/runs/:runId/decision-events', async (c) => {
54
+ const gate = await authorize(c, 'read');
55
+ if ('fail' in gate) {
56
+ return c.json({ error: { code: gate.fail.code, message: gate.fail.message } }, gate.fail.status);
57
+ }
58
+ const { auth } = gate;
59
+ const runId = c.req.param('runId');
60
+ const container = c.get('container');
61
+ // The same resolution the decision ROUTES use, so this streams exactly the population they
62
+ // answer: a board task run or a headless job anchor in the key's workspace, and anything else
63
+ // is a 404 indistinguishable from a run that never existed.
64
+ const scoped = await loadScopedRun(c, auth.workspaceId, runId);
65
+ if (!scoped) {
66
+ return c.json({ error: { code: 'not_found', message: 'Run not found' } }, 404);
67
+ }
68
+ const { blockId } = scoped;
69
+ const keys = container.publicApiKeys;
70
+ return streamSSE(c, async (stream) => {
71
+ const startedAt = Date.now();
72
+ let lastAuthCheck = Date.now();
73
+ // Change-detected on the serialized payload, like the progress frames on the run streams, so
74
+ // a park that lasts an hour costs one frame rather than 3,600 identical ones.
75
+ const changed = createDecisionAnnouncer();
76
+ for (;;) {
77
+ if (stream.aborted)
78
+ break;
79
+ if (keys && Date.now() - lastAuthCheck > SSE_REAUTH_MS) {
80
+ if (!(await keys.isActive(auth.keyId)))
81
+ break;
82
+ lastAuthCheck = Date.now();
83
+ }
84
+ const execution = await container.executionRepository.get(auth.workspaceId, runId);
85
+ if (!execution)
86
+ break;
87
+ const list = await projectDecisionList(c, auth.workspaceId, blockId, execution);
88
+ // Reduced for the same reason `reduceRunForStream` reduces beside it: the frame carries the
89
+ // WHOLE list and is re-sent whenever any part of it moves, so a park holding eighty
90
+ // model-authored findings would repeat all of them for the rest of the run. The list itself
91
+ // is never narrowed: only the prose inside it is clipped, and `truncated` says so.
92
+ const data = JSON.stringify(reduceDecisionsForStream(list));
93
+ if (changed.shouldAnnounce(data)) {
94
+ await stream.writeSSE({ event: 'decision-state', data });
95
+ }
96
+ // A finished run asks nothing, so the stream ends rather than holding a connection open
97
+ // over a list that can no longer move. The LAST frame is written above first, which is what
98
+ // makes the close readable: a caller sees the decisions empty out and then the terminal.
99
+ if (execution.status === 'done' || execution.status === 'failed') {
100
+ await stream.writeSSE({ event: 'done', data });
101
+ break;
102
+ }
103
+ if (Date.now() - startedAt > SSE_MAX_MS) {
104
+ await stream.writeSSE({ event: 'timeout', data: '{}' });
105
+ break;
106
+ }
107
+ await stream.sleep(SSE_POLL_MS);
108
+ }
109
+ });
110
+ });
111
+ }
112
+ export function registerJobStreamRoute(app) {
113
+ // Stream a job's progress + terminal completion over SSE. Authenticated by the API key header
114
+ // (an external client can set headers, unlike a browser `EventSource`).
115
+ app.get('/api/v1/jobs/:id/events', async (c) => {
116
+ const gate = await authorize(c, 'read');
117
+ if ('fail' in gate) {
118
+ return c.json({ error: { code: gate.fail.code, message: gate.fail.message } }, gate.fail.status);
119
+ }
120
+ const { auth } = gate;
121
+ const id = c.req.param('id');
122
+ const container = c.get('container');
123
+ // Same headless-job scoping as the poll read: only a headless run this surface created.
124
+ const initial = await loadPublicJob(c, auth.workspaceId, id);
125
+ if (!initial) {
126
+ return c.json({ error: { code: 'not_found', message: 'Job not found' } }, 404);
127
+ }
128
+ const keys = container.publicApiKeys;
129
+ return streamSSE(c, async (stream) => {
130
+ const startedAt = Date.now();
131
+ let lastAuthCheck = Date.now();
132
+ let last = '';
133
+ // Emit the initial state immediately, then poll until terminal / client-gone / revoked /
134
+ // timeout. A `blocked` run is a PARK awaiting a human decision, no longer a dead end, since
135
+ // a `decide`-scope caller can answer it over `/api/v1/runs/:runId/decisions`, so the stream
136
+ // announces the park and keeps watching rather than closing on it.
137
+ const parks = createParkAnnouncer();
138
+ for (;;) {
139
+ if (stream.aborted)
140
+ break;
141
+ // Re-verify the key periodically so a mid-stream revoke cuts the connection (the key was
142
+ // only proven once, at open). Cheap non-hashing revocation check, throttled.
143
+ if (keys && Date.now() - lastAuthCheck > SSE_REAUTH_MS) {
144
+ if (!(await keys.isActive(auth.keyId)))
145
+ break;
146
+ lastAuthCheck = Date.now();
147
+ }
148
+ const execution = await container.executionRepository.get(auth.workspaceId, id);
149
+ if (!execution)
150
+ break;
151
+ const job = toPublicJob(execution, auth.externalIdentity);
152
+ const data = JSON.stringify(job);
153
+ if (data !== last) {
154
+ await stream.writeSSE({
155
+ event: job.status === 'succeeded' ? 'done' : job.status === 'failed' ? 'error' : 'progress',
156
+ data,
157
+ });
158
+ last = data;
159
+ }
160
+ // A `blocked` run has PARKED on a human decision. Announce it once (so a caller learns of
161
+ // the park by push rather than by polling the decisions endpoint on a timer) and keep the
162
+ // stream open: the park is answerable, and answering resumes the very run being watched,
163
+ // which the caller should see through the SAME connection. Announced once per park rather
164
+ // than every tick, so a long park doesn't spam identical frames; re-armed when the run
165
+ // resumes, so a second park later in the pipeline is announced too.
166
+ if (parks.shouldAnnounce(execution.status)) {
167
+ await stream.writeSSE({ event: 'decision', data });
168
+ }
169
+ // A `paused` run is NOT terminal: the spend gate pauses a run when the workspace budget
170
+ // is exhausted and RESUMES it once budget frees up (ExecutionService.evaluateStep), so keep
171
+ // polling (bounded by SSE_MAX_MS below) rather than signalling a false terminal stop.
172
+ // `blocked` is likewise non-terminal now (see above).
173
+ if (execution.status !== 'running' &&
174
+ execution.status !== 'paused' &&
175
+ !isParked(execution.status)) {
176
+ // The run has stopped. When it ended in a terminal public status (succeeded/failed) the
177
+ // event above already carried `done`/`error`. A raw status that maps to `running` but is
178
+ // neither terminal nor a park (a `cancelled` stop lands as `failed`, so this is the
179
+ // belt-and-braces arm) would otherwise close the stream after a `progress` frame, leaving
180
+ // the client unable to tell "terminal" from "connection dropped". Emit an explicit
181
+ // terminal `stopped` frame so every close is unambiguous.
182
+ if (job.status === 'running')
183
+ await stream.writeSSE({ event: 'stopped', data });
184
+ break;
185
+ }
186
+ if (Date.now() - startedAt > SSE_MAX_MS) {
187
+ // Bound the connection; the client can reconnect to keep watching.
188
+ await stream.writeSSE({ event: 'timeout', data: '{}' });
189
+ break;
190
+ }
191
+ await stream.sleep(SSE_POLL_MS);
192
+ }
193
+ });
194
+ });
195
+ }
196
+ export function registerTaskRunStreamRoute(app) {
197
+ // Stream a task's run over SSE: the same bounded-poll pattern as the jobs stream, re-reading the
198
+ // persisted run + its block each tick so a mid-run PR-open surfaces. Terminal on `done`/`failed`;
199
+ // a parked `blocked`/`paused` keeps polling until the run resumes or the connection hits
200
+ // SSE_MAX_MS.
201
+ app.get('/api/v1/tasks/:taskId/events', async (c) => {
202
+ const gate = await authorize(c, 'read');
203
+ if ('fail' in gate) {
204
+ return c.json({ error: { code: gate.fail.code, message: gate.fail.message } }, gate.fail.status);
205
+ }
206
+ const { auth } = gate;
207
+ const taskId = c.req.param('taskId');
208
+ const container = c.get('container');
209
+ const found = await container.boardService.getServiceTask(auth.workspaceId, taskId);
210
+ if (!found) {
211
+ return c.json({ error: { code: 'not_found', message: 'Task not found' } }, 404);
212
+ }
213
+ const run = await container.executionRepository.getByBlock(auth.workspaceId, taskId);
214
+ if (!run) {
215
+ return c.json({ error: { code: 'no_run', message: 'Task has not been started' } }, 404);
216
+ }
217
+ const runId = run.id;
218
+ const keys = container.publicApiKeys;
219
+ return streamSSE(c, async (stream) => {
220
+ const startedAt = Date.now();
221
+ let lastAuthCheck = Date.now();
222
+ let last = '';
223
+ // Announce a park once per park (see the jobs stream for the rationale); re-armed on resume
224
+ // so a later step's park is announced too.
225
+ const parks = createParkAnnouncer();
226
+ for (;;) {
227
+ if (stream.aborted)
228
+ break;
229
+ if (keys && Date.now() - lastAuthCheck > SSE_REAUTH_MS) {
230
+ if (!(await keys.isActive(auth.keyId)))
231
+ break;
232
+ lastAuthCheck = Date.now();
233
+ }
234
+ const execution = await container.executionRepository.get(auth.workspaceId, runId);
235
+ // Re-read the block for the current PR/branch: the run opens the PR mid-flight, so
236
+ // the block (not the execution) carries it.
237
+ const block = await container.boardService.getServiceTask(auth.workspaceId, taskId);
238
+ if (!execution || !block)
239
+ break;
240
+ // Reduced for the wire: the frame carries the whole run, so an oversized step deliverable
241
+ // would be re-sent on every change for the rest of the run. See `reduceRunForStream`.
242
+ const runView = reduceRunForStream(toPublicRun(execution, block.block, auth.externalIdentity));
243
+ const data = JSON.stringify(runView);
244
+ if (data !== last) {
245
+ await stream.writeSSE({
246
+ event: runView.status === 'done'
247
+ ? 'done'
248
+ : runView.status === 'failed'
249
+ ? 'error'
250
+ : 'progress',
251
+ data,
252
+ });
253
+ last = data;
254
+ }
255
+ // A `blocked` run has PARKED on a human decision (a requirements/clarity review, a
256
+ // brainstorm, an approval gate, a fork choice). Push it as a distinct `decision` frame so
257
+ // the caller reacts to the park instead of inferring it from a `progress` payload whose
258
+ // status happens to read `blocked`; `GET /api/v1/runs/:runId/decisions` then carries what
259
+ // is actually being asked. The stream stays open across the park: answering resumes this
260
+ // very run, and the caller should see that on the same connection.
261
+ if (parks.shouldAnnounce(execution.status)) {
262
+ await stream.writeSSE({ event: 'decision', data });
263
+ }
264
+ if (execution.status === 'done' || execution.status === 'failed')
265
+ break;
266
+ if (Date.now() - startedAt > SSE_MAX_MS) {
267
+ await stream.writeSSE({ event: 'timeout', data: '{}' });
268
+ break;
269
+ }
270
+ await stream.sleep(SSE_POLL_MS);
271
+ }
272
+ });
273
+ });
274
+ }
275
+ //# sourceMappingURL=publicApiStreamRoutes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publicApiStreamRoutes.js","sourceRoot":"","sources":["../../../src/modules/publicApi/publicApiStreamRoutes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { Block, ExecutionInstance, PublicJob, PublicRun } from '@cat-factory/contracts';
2
+ import type { Context } from 'hono';
3
+ import type { AppEnv } from '../../http/env.js';
4
+ /**
5
+ * Project a persisted execution onto the external job resource (no block/board internals).
6
+ *
7
+ * Takes the READING key's own identity because the run's pinned one is not visible to every key
8
+ * (`runIdentityVisibility.ts` holds the rule and why there is one). Passed as a parameter rather
9
+ * than read off the context here, so the projection stays a pure function of the run plus the
10
+ * caller and the SSE loops can render frame after frame without re-reading anything.
11
+ */
12
+ export declare function toPublicJob(execution: ExecutionInstance, readerIdentity: string | null): PublicJob;
13
+ /**
14
+ * Project a task's persisted run + its block onto the RICH external run resource: per-step
15
+ * state/progress/subtasks, the failure kind+message, and the PR (url + branch). The run's
16
+ * `status` is the raw execution status (`running`/`blocked`/`paused`/`done`/`failed`): the
17
+ * public run view deliberately surfaces the parked states (unlike the coarse `publicJob`), so
18
+ * a caller can tell an awaiting-a-human `blocked` from a still-`running` step. The PR branch
19
+ * lives on the BLOCK (`block.pullRequest`), not the run, so both are joined here.
20
+ */
21
+ export declare function toPublicRun(execution: ExecutionInstance, block: Block, readerIdentity: string | null): PublicRun;
22
+ /**
23
+ * Load a public JOB by id for an authenticated key: the persisted execution, but ONLY when it is
24
+ * anchored on a HEADLESS internal block (a run this public surface created). Returns null when no
25
+ * such run exists in the key's workspace OR the id points at a normal board execution, so an
26
+ * external key can never read an arbitrary in-workspace run's output, only its own headless jobs.
27
+ * Runtime-symmetric: one `executionRepository.get` + one `boardService.getInternalTask` point-read.
28
+ */
29
+ export declare function loadPublicJob<E extends AppEnv>(c: Context<E>, workspaceId: string, id: string): Promise<ExecutionInstance | null>;
30
+ //# sourceMappingURL=runProjection.d.ts.map