wicked-crew 0.6.0 → 0.7.1

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 (117) hide show
  1. package/README.md +103 -0
  2. package/dist/api/audit.d.ts +13 -0
  3. package/dist/api/audit.d.ts.map +1 -1
  4. package/dist/api/audit.js +18 -2
  5. package/dist/api/audit.js.map +1 -1
  6. package/dist/api/endpoint-manifest-live.d.ts +29 -0
  7. package/dist/api/endpoint-manifest-live.d.ts.map +1 -0
  8. package/dist/api/endpoint-manifest-live.js +72 -0
  9. package/dist/api/endpoint-manifest-live.js.map +1 -0
  10. package/dist/api/endpoint-manifest.d.ts +107 -0
  11. package/dist/api/endpoint-manifest.d.ts.map +1 -0
  12. package/dist/api/endpoint-manifest.js +108 -0
  13. package/dist/api/endpoint-manifest.js.map +1 -0
  14. package/dist/api/guidance-index.d.ts +39 -0
  15. package/dist/api/guidance-index.d.ts.map +1 -0
  16. package/dist/api/guidance-index.js +67 -0
  17. package/dist/api/guidance-index.js.map +1 -0
  18. package/dist/api/open-path.d.ts +16 -0
  19. package/dist/api/open-path.d.ts.map +1 -1
  20. package/dist/api/open-path.js +22 -0
  21. package/dist/api/open-path.js.map +1 -1
  22. package/dist/api/requirements.d.ts +7 -0
  23. package/dist/api/requirements.d.ts.map +1 -1
  24. package/dist/api/requirements.js +23 -2
  25. package/dist/api/requirements.js.map +1 -1
  26. package/dist/api/retry-index.d.ts +30 -0
  27. package/dist/api/retry-index.d.ts.map +1 -0
  28. package/dist/api/retry-index.js +45 -0
  29. package/dist/api/retry-index.js.map +1 -0
  30. package/dist/api/routes.d.ts +53 -1
  31. package/dist/api/routes.d.ts.map +1 -1
  32. package/dist/api/routes.js +399 -30
  33. package/dist/api/routes.js.map +1 -1
  34. package/dist/api/run-files.d.ts +63 -0
  35. package/dist/api/run-files.d.ts.map +1 -0
  36. package/dist/api/run-files.js +271 -0
  37. package/dist/api/run-files.js.map +1 -0
  38. package/dist/api/server.d.ts +79 -0
  39. package/dist/api/server.d.ts.map +1 -1
  40. package/dist/api/server.js +183 -8
  41. package/dist/api/server.js.map +1 -1
  42. package/dist/api/stall-watchdog.d.ts +62 -0
  43. package/dist/api/stall-watchdog.d.ts.map +1 -0
  44. package/dist/api/stall-watchdog.js +138 -0
  45. package/dist/api/stall-watchdog.js.map +1 -0
  46. package/dist/cli/index.js +89 -15
  47. package/dist/cli/index.js.map +1 -1
  48. package/dist/core/adapter.d.ts +36 -10
  49. package/dist/core/adapter.d.ts.map +1 -1
  50. package/dist/core/adapter.js +245 -31
  51. package/dist/core/adapter.js.map +1 -1
  52. package/dist/core/bridge-reaper.d.ts +134 -0
  53. package/dist/core/bridge-reaper.d.ts.map +1 -0
  54. package/dist/core/bridge-reaper.js +286 -0
  55. package/dist/core/bridge-reaper.js.map +1 -0
  56. package/dist/core/deliver.d.ts +118 -0
  57. package/dist/core/deliver.d.ts.map +1 -0
  58. package/dist/core/deliver.js +241 -0
  59. package/dist/core/deliver.js.map +1 -0
  60. package/dist/core/deliverable-floor.d.ts +155 -0
  61. package/dist/core/deliverable-floor.d.ts.map +1 -0
  62. package/dist/core/deliverable-floor.js +248 -0
  63. package/dist/core/deliverable-floor.js.map +1 -0
  64. package/dist/core/exec.d.ts +2 -0
  65. package/dist/core/exec.d.ts.map +1 -1
  66. package/dist/core/exec.js.map +1 -1
  67. package/dist/core/types.d.ts +79 -1
  68. package/dist/core/types.d.ts.map +1 -1
  69. package/dist/core/types.js +3 -0
  70. package/dist/core/types.js.map +1 -1
  71. package/dist/interactive/bridge-pool.d.ts +52 -0
  72. package/dist/interactive/bridge-pool.d.ts.map +1 -1
  73. package/dist/interactive/bridge-pool.js +93 -12
  74. package/dist/interactive/bridge-pool.js.map +1 -1
  75. package/dist/interactive/chat-events.d.ts +207 -0
  76. package/dist/interactive/chat-events.d.ts.map +1 -0
  77. package/dist/interactive/chat-events.js +769 -0
  78. package/dist/interactive/chat-events.js.map +1 -0
  79. package/dist/interactive/demo-events.d.ts +283 -0
  80. package/dist/interactive/demo-events.d.ts.map +1 -0
  81. package/dist/interactive/demo-events.js +889 -0
  82. package/dist/interactive/demo-events.js.map +1 -0
  83. package/dist/interactive/draft-events.d.ts +87 -7
  84. package/dist/interactive/draft-events.d.ts.map +1 -1
  85. package/dist/interactive/draft-events.js +352 -49
  86. package/dist/interactive/draft-events.js.map +1 -1
  87. package/dist/interactive/edit-events.d.ts +25 -2
  88. package/dist/interactive/edit-events.d.ts.map +1 -1
  89. package/dist/interactive/edit-events.js +88 -9
  90. package/dist/interactive/edit-events.js.map +1 -1
  91. package/dist/interactive/repo-snapshot.d.ts +100 -0
  92. package/dist/interactive/repo-snapshot.d.ts.map +1 -0
  93. package/dist/interactive/repo-snapshot.js +289 -0
  94. package/dist/interactive/repo-snapshot.js.map +1 -0
  95. package/dist/projects/graph-paths.d.ts +122 -0
  96. package/dist/projects/graph-paths.d.ts.map +1 -0
  97. package/dist/projects/graph-paths.js +175 -0
  98. package/dist/projects/graph-paths.js.map +1 -0
  99. package/dist/projects/graph.d.ts +191 -0
  100. package/dist/projects/graph.d.ts.map +1 -0
  101. package/dist/projects/graph.js +829 -0
  102. package/dist/projects/graph.js.map +1 -0
  103. package/dist/projects/routes.d.ts +17 -0
  104. package/dist/projects/routes.d.ts.map +1 -1
  105. package/dist/projects/routes.js +139 -0
  106. package/dist/projects/routes.js.map +1 -1
  107. package/dist/qe/ledger.d.ts +3 -2
  108. package/dist/qe/ledger.d.ts.map +1 -1
  109. package/dist/qe/ledger.js +5 -4
  110. package/dist/qe/ledger.js.map +1 -1
  111. package/dist/studio/assets/index-D-BFUYnY.js +530 -0
  112. package/dist/studio/assets/index-D6S9zUtO.css +32 -0
  113. package/dist/studio/index.html +5 -3
  114. package/endpoint-manifest.json +624 -0
  115. package/package.json +8 -5
  116. package/dist/studio/assets/index-CCwXa1cn.js +0 -428
  117. package/dist/studio/assets/index-HWxo0h41.css +0 -32
package/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # wicked-crew
2
+
3
+ **The harness for your agent harnesses** — run the coding-agent CLIs you already use as governed
4
+ workers through durable, multi-agent workflows. Intent in, verified work out: the evaluator is
5
+ structurally not the creator, gates are deny-dominates, and "done" is re-derived from evidence
6
+ instead of asserted.
7
+
8
+ Local-first: loopback only, no accounts, no billing, no model reselling. Crew drives the agents
9
+ *you* already pay for, on *your* auth and *your* plan.
10
+
11
+ ## Install
12
+
13
+ Requires Node.js ≥ 22 and at least one coding-agent CLI on your machine (e.g. Claude Code, Codex).
14
+
15
+ ```bash
16
+ npm install -g wicked-crew
17
+ wicked-crew serve
18
+ ```
19
+
20
+ Or without installing:
21
+
22
+ ```bash
23
+ npx wicked-crew serve
24
+ ```
25
+
26
+ Or use the family installer — [`npx wicked-installer`](https://www.npmjs.com/package/wicked-installer)
27
+ installs/updates the whole wicked-\* family, crew included.
28
+
29
+ `serve` starts the daemon on `http://127.0.0.1:7701` (override with `--port` or `CREW_PORT`) and
30
+ serves the bundled **wicked-studio** browser console same-origin — open the URL and you have the
31
+ control room: launch and steer runs, answer human gates, browse projects and evidence, watch live
32
+ engine events. Durable state (runs, evidence, event log) lives in `~/.wicked-crew/` (`--db`
33
+ overrides).
34
+
35
+ ## Quickstart
36
+
37
+ ```bash
38
+ # 1. Start the daemon (leave it running)
39
+ wicked-crew serve
40
+
41
+ # 2. Launch a governed run — from the console at http://127.0.0.1:7701,
42
+ # or headless over the API:
43
+ curl -X POST http://127.0.0.1:7701/api/v1/runs \
44
+ -H 'content-type: application/json' \
45
+ -d '{"problem": "Add input validation to the signup form", "workflow": "feature"}'
46
+
47
+ # 3. Watch it move through the workflow's phases; approve or reject at the gates.
48
+ ```
49
+
50
+ One run token moves through the workflow's phases with a gate between every one — e.g. for
51
+ `feature`: clarify → design → build → adversarial-review → test → review. Each phase is executed
52
+ by a versioned skill on an assigned CLI worker; the gates decide, and every decision is audited.
53
+
54
+ ## The governed-run model
55
+
56
+ - **You own the workflow; the agent owns the work.** The orchestrator sequences phases and holds
57
+ the gates; the agent does the coding inside a phase.
58
+ - **Workflows are data.** `feature` / `bug` / `migration` ship built-in; new ones are drop-in JSON
59
+ files, not code.
60
+ - **Gates are real, not self-graded.** Phase transitions resolve **deny-dominates** on a
61
+ deterministic structural floor, with an independent evaluator seat that reads cold evidence
62
+ only — a model may fail a gate, never solely approve one.
63
+ - **Evidence, not assertion.** "Done" is re-derived from evidence at the gate, never claimed by
64
+ the agent that did the work.
65
+
66
+ The engine underneath is [wicked-core](https://github.com/mikeparcewski/wicked-core) (Rust,
67
+ single-writer), embedded via the `wicked-core-ts` napi bridge — the daemon is a thin REST+WS
68
+ layer over it.
69
+
70
+ ## The acceptance gate
71
+
72
+ `GET /api/v1/runs/:id/acceptance` answers "does the QE evidence ledger accept this run's work?" —
73
+ crew's machine gate, absorbed from the retired wicked-testing product. <!-- historical --> It reads the repo's
74
+ evidence ledger (a wicked-ledger store at `<repo>/.wicked-qe/`, written by wicked-garden's QE
75
+ skills; legacy `.wicked-testing/` ledgers are still read) and resolves the workflow's acceptance
76
+ requirement **deny-dominates**: only a `PASS` verdict satisfies it. `FAIL`, `CONDITIONAL`,
77
+ `PARTIAL`, `INCONCLUSIVE`, a missing ledger, or a missing verdict each deny with their own named
78
+ reason — no evidence is never a pass. The route always returns 200 for a known run ("no verdict"
79
+ is a real answer about the gate, not an error); `?qeRun=<id>` pins the read to one QE run.
80
+
81
+ ## CLI
82
+
83
+ ```
84
+ wicked-crew serve|start|resume|gate|status|mcp
85
+ ```
86
+
87
+ | command | what it does |
88
+ |---|---|
89
+ | `serve` | run the daemon: REST `/api/v1` + WS `/ws` + the bundled studio console |
90
+ | `start` | boot and launch a run headless (`--problem`, `--workflow`, `--repo`) |
91
+ | `resume` | resume a persisted run (`--session <id>`) |
92
+ | `gate` | answer a pending human gate from the terminal |
93
+ | `status` | inspect run state |
94
+ | `mcp` | stdio MCP server — crew-as-a-tool for coding agents |
95
+
96
+ ## Links
97
+
98
+ - Repo: <https://github.com/mikeparcewski/wicked-crew>
99
+ - Site & docs: <https://wc.wickedagile.com>
100
+ - Wire contract (every `/api/v1` + `/ws` shape, types-only): [`wicked-crew-api-types`](https://www.npmjs.com/package/wicked-crew-api-types)
101
+ - Engine: [wicked-core](https://github.com/mikeparcewski/wicked-core) · Console: [wicked-studio](https://github.com/mikeparcewski/wicked-studio)
102
+
103
+ MIT
@@ -21,6 +21,8 @@ export interface AuditReadFilter {
21
21
  /** Max entries returned (newest first). Default 200, capped at 1000. */
22
22
  limit?: number;
23
23
  }
24
+ /** `readAll`'s filter: no limit — the whole point is that nothing gets trimmed. */
25
+ export type AuditScanFilter = Omit<AuditReadFilter, 'limit'>;
24
26
  export declare class AuditLog {
25
27
  readonly path: string;
26
28
  private readonly warn;
@@ -53,5 +55,16 @@ export declare class AuditLog {
53
55
  * still answers.
54
56
  */
55
57
  read(filter?: AuditReadFilter): Promise<AuditEntry[]>;
58
+ /**
59
+ * Read the trail EXHAUSTIVELY, newest first — no read cap. The hydrate path
60
+ * for the boot-time indexes (`RetryIndex`, `GuidanceIndex`): durable facts
61
+ * like retry lineage must not vanish because 1000+ newer launches landed on
62
+ * top of them (BRIEF-UX-002 C5). Same cost class as `read` — that already
63
+ * parses the whole file and only trims the returned slice — so this is a
64
+ * one-time full-file scan at boot, measured at ~19ms on a 19k-line live trail.
65
+ */
66
+ readAll(filter?: AuditScanFilter): Promise<AuditEntry[]>;
67
+ /** The shared full-file parse: every matching entry, newest first, uncapped. */
68
+ private scan;
56
69
  }
57
70
  //# sourceMappingURL=audit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/api/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE1D,qDAAqD;AACrD,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAE7E;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,QAAQ;IAMjB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANvB,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;gBAGd,IAAI,GAAE,MAA2B,EACzB,IAAI,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAA6B;IAGvE;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,IAAI,QAAQ;IAMvB;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,IAAI;IAyBzG,wCAAwC;IACxC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB;;;;OAIG;IACG,IAAI,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;CA2B5D"}
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/api/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE1D,qDAAqD;AACrD,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAE7E;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,mFAAmF;AACnF,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;AAE7D,qBAAa,QAAQ;IAMjB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANvB,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;gBAGd,IAAI,GAAE,MAA2B,EACzB,IAAI,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAA6B;IAGvE;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,IAAI,QAAQ;IAMvB;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,IAAI;IAyBzG,wCAAwC;IACxC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB;;;;OAIG;IACG,IAAI,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAM3D;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAIxD,gFAAgF;YAClE,IAAI;CA0BnB"}
package/dist/api/audit.js CHANGED
@@ -77,6 +77,23 @@ export class AuditLog {
77
77
  * still answers.
78
78
  */
79
79
  async read(filter) {
80
+ const entries = await this.scan(filter);
81
+ const limit = Math.min(Math.max(filter?.limit ?? 200, 1), 1000);
82
+ return entries.slice(0, limit);
83
+ }
84
+ /**
85
+ * Read the trail EXHAUSTIVELY, newest first — no read cap. The hydrate path
86
+ * for the boot-time indexes (`RetryIndex`, `GuidanceIndex`): durable facts
87
+ * like retry lineage must not vanish because 1000+ newer launches landed on
88
+ * top of them (BRIEF-UX-002 C5). Same cost class as `read` — that already
89
+ * parses the whole file and only trims the returned slice — so this is a
90
+ * one-time full-file scan at boot, measured at ~19ms on a 19k-line live trail.
91
+ */
92
+ readAll(filter) {
93
+ return this.scan(filter);
94
+ }
95
+ /** The shared full-file parse: every matching entry, newest first, uncapped. */
96
+ async scan(filter) {
80
97
  if (this.disabled)
81
98
  return [];
82
99
  await this.flush();
@@ -108,8 +125,7 @@ export class AuditLog {
108
125
  }
109
126
  }
110
127
  entries.reverse(); // file order is append order; the API answers newest first
111
- const limit = Math.min(Math.max(filter?.limit ?? 200, 1), 1000);
112
- return entries.slice(0, limit);
128
+ return entries;
113
129
  }
114
130
  }
115
131
  //# sourceMappingURL=audit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/api/audit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/api/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Build the LIVE endpoint manifest by booting the real server assembly (TH-11).
3
+ *
4
+ * A separate module from endpoint-manifest.ts on purpose: this imports `createServer` while
5
+ * server.ts imports the hook installer — folding both into one file would make the cycle
6
+ * structural instead of avoiding it.
7
+ *
8
+ * # Why boot `createServer` rather than re-list routes by hand
9
+ *
10
+ * The manifest's whole value is that it cannot drift from the daemon: it is read from the same
11
+ * `onRoute` hook, over the same registration calls (`registerRoutes`, project routes, terminal
12
+ * WS, `/ws`, interactive event routes), that the served daemon runs. A hand-maintained list would
13
+ * be a second spelling of the route table — the exact thing the manifest exists to end.
14
+ *
15
+ * # What is faked, and why it is safe
16
+ *
17
+ * Route REGISTRATION never calls the engine — only handlers do, and no request is ever injected
18
+ * here. The adapter stub answers the three things `createServer` itself asks at boot:
19
+ * `getSettings()` (worker-config root export), `projectsSupported()` → false (skips membership
20
+ * hydration), and `stub: true` (any answering seam that were ever armed would refuse rather than
21
+ * subscribe). Every optional seam is disabled explicitly, the audit trail goes to a temp file
22
+ * (never `~/.wicked-crew/audit.log`), and `studioRoot` points at a nonexistent directory so the
23
+ * server boots HEADLESS — deliberately: the static wildcard route depends on whether a studio
24
+ * bundle is installed on the generating machine, and a committed manifest must not vary by
25
+ * install state. The manifest documents the API + WS surface.
26
+ */
27
+ import { type EndpointManifest } from './endpoint-manifest.js';
28
+ export declare function collectLiveEndpointManifest(): Promise<EndpointManifest>;
29
+ //# sourceMappingURL=endpoint-manifest-live.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endpoint-manifest-live.d.ts","sourceRoot":"","sources":["../../src/api/endpoint-manifest-live.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAQH,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAEtF,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAmC7E"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Build the LIVE endpoint manifest by booting the real server assembly (TH-11).
3
+ *
4
+ * A separate module from endpoint-manifest.ts on purpose: this imports `createServer` while
5
+ * server.ts imports the hook installer — folding both into one file would make the cycle
6
+ * structural instead of avoiding it.
7
+ *
8
+ * # Why boot `createServer` rather than re-list routes by hand
9
+ *
10
+ * The manifest's whole value is that it cannot drift from the daemon: it is read from the same
11
+ * `onRoute` hook, over the same registration calls (`registerRoutes`, project routes, terminal
12
+ * WS, `/ws`, interactive event routes), that the served daemon runs. A hand-maintained list would
13
+ * be a second spelling of the route table — the exact thing the manifest exists to end.
14
+ *
15
+ * # What is faked, and why it is safe
16
+ *
17
+ * Route REGISTRATION never calls the engine — only handlers do, and no request is ever injected
18
+ * here. The adapter stub answers the three things `createServer` itself asks at boot:
19
+ * `getSettings()` (worker-config root export), `projectsSupported()` → false (skips membership
20
+ * hydration), and `stub: true` (any answering seam that were ever armed would refuse rather than
21
+ * subscribe). Every optional seam is disabled explicitly, the audit trail goes to a temp file
22
+ * (never `~/.wicked-crew/audit.log`), and `studioRoot` points at a nonexistent directory so the
23
+ * server boots HEADLESS — deliberately: the static wildcard route depends on whether a studio
24
+ * bundle is installed on the generating machine, and a committed manifest must not vary by
25
+ * install state. The manifest documents the API + WS surface.
26
+ */
27
+ import { mkdtempSync, rmSync } from 'node:fs';
28
+ import { tmpdir } from 'node:os';
29
+ import { join } from 'node:path';
30
+ import { createServer } from './server.js';
31
+ import { buildEndpointManifest } from './endpoint-manifest.js';
32
+ export async function collectLiveEndpointManifest() {
33
+ // Deterministic boot: auth mode is pinned OFF via options (no env/file resolution), and the
34
+ // logger is silenced for the duration — this runs inside test output and CI logs.
35
+ const savedLogLevel = process.env['LOG_LEVEL'];
36
+ process.env['LOG_LEVEL'] = 'silent';
37
+ const scratch = mkdtempSync(join(tmpdir(), 'crew-endpoint-manifest-'));
38
+ const adapter = {
39
+ stub: true,
40
+ projectsSupported: () => false,
41
+ getSettings: async () => ({}),
42
+ // The daemon's single CoreEvent fan-out subscribes at boot; no event ever arrives here.
43
+ onEvent: () => () => { },
44
+ };
45
+ try {
46
+ const app = await createServer(adapter, {
47
+ auth: { mode: 'off' },
48
+ auditPath: join(scratch, 'audit.log'),
49
+ projectEvents: { disabled: true },
50
+ interactiveWsRelay: { disabled: true },
51
+ seatHealthProbe: { enabled: false },
52
+ stallWatchdog: { enabled: false },
53
+ // Nonexistent on purpose — headless boot; see the module header.
54
+ studioRoot: join(scratch, 'no-studio-bundle'),
55
+ });
56
+ try {
57
+ await app.ready();
58
+ return buildEndpointManifest(app.endpointManifest ?? []);
59
+ }
60
+ finally {
61
+ await app.close();
62
+ }
63
+ }
64
+ finally {
65
+ if (savedLogLevel === undefined)
66
+ delete process.env['LOG_LEVEL'];
67
+ else
68
+ process.env['LOG_LEVEL'] = savedLogLevel;
69
+ rmSync(scratch, { recursive: true, force: true });
70
+ }
71
+ }
72
+ //# sourceMappingURL=endpoint-manifest-live.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endpoint-manifest-live.js","sourceRoot":"","sources":["../../src/api/endpoint-manifest-live.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAyB,MAAM,wBAAwB,CAAC;AAEtF,MAAM,CAAC,KAAK,UAAU,2BAA2B;IAC/C,4FAA4F;IAC5F,kFAAkF;IAClF,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,QAAQ,CAAC;IACpC,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,yBAAyB,CAAC,CAAC,CAAC;IACvE,MAAM,OAAO,GAAG;QACd,IAAI,EAAE,IAAI;QACV,iBAAiB,EAAE,GAAG,EAAE,CAAC,KAAK;QAC9B,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QAC7B,wFAAwF;QACxF,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,GAAE,CAAC;KACE,CAAC;IAC5B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE;YACtC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;YACrB,SAAS,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC;YACrC,aAAa,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;YACjC,kBAAkB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;YACtC,eAAe,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;YACnC,aAAa,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;YACjC,iEAAiE;YACjE,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC;SAC9C,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;YAClB,OAAO,qBAAqB,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;YAAS,CAAC;QACT,IAAI,aAAa,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;;YAC5D,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,aAAa,CAAC;QAC9C,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * The endpoint manifest (TH-11) — crew's route table as a MACHINE-READABLE build artifact.
3
+ *
4
+ * # Why this exists
5
+ *
6
+ * routes.ts declares zero fastify schemas (verified: grep `schema:` = 0), no OpenAPI spec exists
7
+ * anywhere in crew, and `wicked-crew-api-types` is types-only with no endpoint-to-type binding —
8
+ * so nothing machine-readable said which methods+paths this daemon serves, what a request body is
9
+ * called, or which status codes a route can answer. Request validation is NOT a vacuum (13
10
+ * `.strict()` zod objects guard the bodies), but zod objects are route-internal: a test harness,
11
+ * a drift check, or an API-test generator had nothing to read. With no fastify schemas declared,
12
+ * `@fastify/swagger` has nothing to generate from either — the `onRoute` hook IS the cheap path.
13
+ *
14
+ * # How it works
15
+ *
16
+ * {@link installEndpointManifestHook} registers a fastify `onRoute` hook (added by `createServer`
17
+ * BEFORE any route registration, so every route the daemon serves is seen) that accumulates one
18
+ * {@link EndpointEntry} per method+path. Two declaration channels feed the type/status fields:
19
+ *
20
+ * 1. `config.manifest` on the route options ({@link ManifestRouteConfig}) — the explicit
21
+ * channel. Names should reference `wicked-crew-api-types` exports where one exists
22
+ * (`LaunchRunBody`, `GateDecision`, …) so the manifest binds endpoints to the published
23
+ * wire contract; a structural spelling (`'{ runId: string }'`) is the honest fallback for
24
+ * wire shapes the contract has no name for.
25
+ * 2. `schema.response` keys, when fastify schemas start existing (the longer-term
26
+ * zod-to-json-schema path) — status codes are folded in automatically, so migrating a route
27
+ * to real fastify schemas enriches the manifest without touching this module.
28
+ *
29
+ * Routes that declare neither still land in the manifest with `requestType`/`responseType` null
30
+ * and `statusCodes: []` — the manifest records "where declared", never invents.
31
+ *
32
+ * # What consumes it
33
+ *
34
+ * - `scripts/generate-endpoint-manifest.ts` (npm run manifest:endpoints) writes the committed
35
+ * `endpoint-manifest.json` at the package root — the build artifact.
36
+ * - `tests/endpoint-manifest.test.ts` rebuilds the live route table and fails on ANY drift from
37
+ * the committed file, so an added/removed/renamed endpoint fails CI until the manifest is
38
+ * regenerated and the diff reviewed. The manifest diff IS the API regression trigger.
39
+ * - `scripts/generate-api-tests.ts` derives positive + 400/404/409 negative API tests from the
40
+ * committed manifest (see `tests/generated/`).
41
+ *
42
+ * HEAD entries are dropped: fastify v5 auto-exposes a HEAD twin for every GET
43
+ * (`exposeHeadRoutes`), which would double the GET surface with rows nobody declared.
44
+ */
45
+ import type { FastifyInstance } from 'fastify';
46
+ /** The explicit per-route declaration channel — `{ config: { manifest: {...} } }` on a route. */
47
+ export interface ManifestRouteConfig {
48
+ /** Request-body type name — a `wicked-crew-api-types` export where one exists. */
49
+ requestType?: string;
50
+ /** Success-response type name, or a structural spelling when the contract has no name for it. */
51
+ responseType?: string;
52
+ /** Every status code this route answers on purpose (success + named error codes). */
53
+ statusCodes?: number[];
54
+ }
55
+ declare module 'fastify' {
56
+ interface FastifyContextConfig {
57
+ /** TH-11 — this route's row in the committed endpoint manifest. */
58
+ manifest?: ManifestRouteConfig;
59
+ }
60
+ interface FastifyInstance {
61
+ /** Accumulated by `createServer`'s onRoute hook; read after `app.ready()`. */
62
+ endpointManifest?: EndpointEntry[];
63
+ }
64
+ }
65
+ /** One method+path the daemon serves. */
66
+ export interface EndpointEntry {
67
+ method: string;
68
+ path: string;
69
+ /** Declared request-body type name, or null — "where declared", never invented. */
70
+ requestType: string | null;
71
+ /** Declared response type name, or null. */
72
+ responseType: string | null;
73
+ /** Declared status codes (config.manifest + fastify schema.response keys), sorted. */
74
+ statusCodes: number[];
75
+ /** Present (true) only on websocket routes (`/ws`, `/ws/terminals/:id`). */
76
+ websocket?: boolean;
77
+ }
78
+ /** The committed artifact's shape (endpoint-manifest.json). */
79
+ export interface EndpointManifest {
80
+ version: 1;
81
+ /**
82
+ * The `wicked-crew-api-types` version this route table was generated against — the published
83
+ * wire contract the type names bind to. Evidence manifests that cite endpoints should carry
84
+ * this (recon R8/R11: api-types drift is live; studio pinned ^0.8.0 against 0.10.0).
85
+ */
86
+ apiTypesVersion: string;
87
+ /** Sorted by path, then method — a stable order so drift diffs are readable. */
88
+ endpoints: EndpointEntry[];
89
+ }
90
+ /**
91
+ * Register the accumulating `onRoute` hook. MUST run before any route is registered — fastify
92
+ * only replays `onRoute` for routes added after the hook exists. Returns the live array the hook
93
+ * appends to; `createServer` exposes it as `app.endpointManifest`.
94
+ */
95
+ export declare function installEndpointManifestHook(app: FastifyInstance): EndpointEntry[];
96
+ /**
97
+ * The published wire contract's version, read from the actually-installed dependency.
98
+ *
99
+ * NOT `require('wicked-crew-api-types/package.json')`: that package's exports map exposes only
100
+ * `.` (types-only, no runtime condition), so both the subpath and a bare resolve are refused.
101
+ * Walking the resolver's candidate directories and reading the file with fs sidesteps the
102
+ * exports map while still honoring the real resolution order (workspace link included).
103
+ */
104
+ export declare function apiTypesVersion(): string;
105
+ /** Entries → the committed artifact: stable order, contract version stamped. */
106
+ export declare function buildEndpointManifest(entries: EndpointEntry[]): EndpointManifest;
107
+ //# sourceMappingURL=endpoint-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endpoint-manifest.d.ts","sourceRoot":"","sources":["../../src/api/endpoint-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,SAAS,CAAC;AAE7D,iGAAiG;AACjG,MAAM,WAAW,mBAAmB;IAClC,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iGAAiG;IACjG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,oBAAoB;QAC5B,mEAAmE;QACnE,QAAQ,CAAC,EAAE,mBAAmB,CAAC;KAChC;IACD,UAAU,eAAe;QACvB,8EAA8E;QAC9E,gBAAgB,CAAC,EAAE,aAAa,EAAE,CAAC;KACpC;CACF;AAED,yCAAyC;AACzC,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,4CAA4C;IAC5C,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,sFAAsF;IACtF,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,+DAA+D;AAC/D,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,CAAC,CAAC;IACX;;;;OAIG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,gFAAgF;IAChF,SAAS,EAAE,aAAa,EAAE,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,eAAe,GAAG,aAAa,EAAE,CAgCjF;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAWxC;AAED,gFAAgF;AAChF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,gBAAgB,CAKhF"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * The endpoint manifest (TH-11) — crew's route table as a MACHINE-READABLE build artifact.
3
+ *
4
+ * # Why this exists
5
+ *
6
+ * routes.ts declares zero fastify schemas (verified: grep `schema:` = 0), no OpenAPI spec exists
7
+ * anywhere in crew, and `wicked-crew-api-types` is types-only with no endpoint-to-type binding —
8
+ * so nothing machine-readable said which methods+paths this daemon serves, what a request body is
9
+ * called, or which status codes a route can answer. Request validation is NOT a vacuum (13
10
+ * `.strict()` zod objects guard the bodies), but zod objects are route-internal: a test harness,
11
+ * a drift check, or an API-test generator had nothing to read. With no fastify schemas declared,
12
+ * `@fastify/swagger` has nothing to generate from either — the `onRoute` hook IS the cheap path.
13
+ *
14
+ * # How it works
15
+ *
16
+ * {@link installEndpointManifestHook} registers a fastify `onRoute` hook (added by `createServer`
17
+ * BEFORE any route registration, so every route the daemon serves is seen) that accumulates one
18
+ * {@link EndpointEntry} per method+path. Two declaration channels feed the type/status fields:
19
+ *
20
+ * 1. `config.manifest` on the route options ({@link ManifestRouteConfig}) — the explicit
21
+ * channel. Names should reference `wicked-crew-api-types` exports where one exists
22
+ * (`LaunchRunBody`, `GateDecision`, …) so the manifest binds endpoints to the published
23
+ * wire contract; a structural spelling (`'{ runId: string }'`) is the honest fallback for
24
+ * wire shapes the contract has no name for.
25
+ * 2. `schema.response` keys, when fastify schemas start existing (the longer-term
26
+ * zod-to-json-schema path) — status codes are folded in automatically, so migrating a route
27
+ * to real fastify schemas enriches the manifest without touching this module.
28
+ *
29
+ * Routes that declare neither still land in the manifest with `requestType`/`responseType` null
30
+ * and `statusCodes: []` — the manifest records "where declared", never invents.
31
+ *
32
+ * # What consumes it
33
+ *
34
+ * - `scripts/generate-endpoint-manifest.ts` (npm run manifest:endpoints) writes the committed
35
+ * `endpoint-manifest.json` at the package root — the build artifact.
36
+ * - `tests/endpoint-manifest.test.ts` rebuilds the live route table and fails on ANY drift from
37
+ * the committed file, so an added/removed/renamed endpoint fails CI until the manifest is
38
+ * regenerated and the diff reviewed. The manifest diff IS the API regression trigger.
39
+ * - `scripts/generate-api-tests.ts` derives positive + 400/404/409 negative API tests from the
40
+ * committed manifest (see `tests/generated/`).
41
+ *
42
+ * HEAD entries are dropped: fastify v5 auto-exposes a HEAD twin for every GET
43
+ * (`exposeHeadRoutes`), which would double the GET surface with rows nobody declared.
44
+ */
45
+ import { existsSync, readFileSync } from 'node:fs';
46
+ import { createRequire } from 'node:module';
47
+ import { join } from 'node:path';
48
+ /**
49
+ * Register the accumulating `onRoute` hook. MUST run before any route is registered — fastify
50
+ * only replays `onRoute` for routes added after the hook exists. Returns the live array the hook
51
+ * appends to; `createServer` exposes it as `app.endpointManifest`.
52
+ */
53
+ export function installEndpointManifestHook(app) {
54
+ const entries = [];
55
+ app.addHook('onRoute', (route) => {
56
+ const methods = Array.isArray(route.method) ? route.method : [route.method];
57
+ const declared = route.config?.manifest;
58
+ // The future-proofing channel: fastify response schemas keyed by status code. None exist
59
+ // today (routes.ts declares zero), but the fold means the zod→fastify-schema migration
60
+ // enriches the manifest for free.
61
+ const schemaResponse = route.schema
62
+ ?.response;
63
+ const schemaCodes = Object.keys(schemaResponse ?? {})
64
+ .map(Number)
65
+ .filter((n) => Number.isInteger(n) && n >= 100 && n <= 599);
66
+ const statusCodes = [...new Set([...(declared?.statusCodes ?? []), ...schemaCodes])].sort((a, b) => a - b);
67
+ const websocket = route.websocket === true;
68
+ for (const method of methods) {
69
+ // Fastify v5 auto-exposes a HEAD twin per GET; recording it would double the GET surface
70
+ // with rows nobody declared and no client calls on purpose.
71
+ if (method === 'HEAD')
72
+ continue;
73
+ entries.push({
74
+ method,
75
+ path: route.url,
76
+ requestType: declared?.requestType ?? null,
77
+ responseType: declared?.responseType ?? null,
78
+ statusCodes,
79
+ ...(websocket ? { websocket: true } : {}),
80
+ });
81
+ }
82
+ });
83
+ return entries;
84
+ }
85
+ /**
86
+ * The published wire contract's version, read from the actually-installed dependency.
87
+ *
88
+ * NOT `require('wicked-crew-api-types/package.json')`: that package's exports map exposes only
89
+ * `.` (types-only, no runtime condition), so both the subpath and a bare resolve are refused.
90
+ * Walking the resolver's candidate directories and reading the file with fs sidesteps the
91
+ * exports map while still honoring the real resolution order (workspace link included).
92
+ */
93
+ export function apiTypesVersion() {
94
+ const require = createRequire(import.meta.url);
95
+ for (const dir of require.resolve.paths('wicked-crew-api-types') ?? []) {
96
+ const pkgPath = join(dir, 'wicked-crew-api-types', 'package.json');
97
+ if (existsSync(pkgPath)) {
98
+ return JSON.parse(readFileSync(pkgPath, 'utf8')).version;
99
+ }
100
+ }
101
+ throw new Error('wicked-crew-api-types is not resolvable from packages/crew — the manifest cannot stamp the wire-contract version');
102
+ }
103
+ /** Entries → the committed artifact: stable order, contract version stamped. */
104
+ export function buildEndpointManifest(entries) {
105
+ const endpoints = [...entries].sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method));
106
+ return { version: 1, apiTypesVersion: apiTypesVersion(), endpoints };
107
+ }
108
+ //# sourceMappingURL=endpoint-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endpoint-manifest.js","sourceRoot":"","sources":["../../src/api/endpoint-manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The run→operator-guidance index (DES-UX-002 §7.2, CREW-UX-7).
3
+ *
4
+ * NAMING NOTE: DES-UX-002 §7.2 labels this slice CREW-UX-4, but that id was already spent on an
5
+ * unrelated merged slice (crew#308) — this implementation is CREW-UX-7; the doc's §7.2 spec is
6
+ * what it implements.
7
+ *
8
+ * A durable pre-gate annotation: ONE operator guidance note per run, upserted via
9
+ * `PUT /runs/:id/guidance` and echoed as `AgentSession.guidance` on the run DTOs. The engine's
10
+ * run record carries no such field (wicked-core-ts 0.6.x), so — exactly like retry lineage
11
+ * (CREW-UX-3) and the actor trail (task #88) — the durable record is the audit trail: every
12
+ * write lands as a `guidance.set` entry whose `detail.text` is the full note (empty string =
13
+ * cleared), and this map is the read-side latency layer that lets the run DTOs echo it without
14
+ * a trail scan per request.
15
+ *
16
+ * Mirrors `RetryIndex`'s posture: hydrated once at server start (best-effort — a missing or
17
+ * unreadable trail leaves guidance blank, the pre-CREW-UX-7 behavior, not an error) and updated
18
+ * at the same post-commit point that writes the audit entry, so the map can only lag by a
19
+ * failed hydrate, never diverge silently.
20
+ */
21
+ import type { AuditLog } from './audit.js';
22
+ export declare class GuidanceIndex {
23
+ private readonly runToGuidance;
24
+ /**
25
+ * Load guidance from EVERY `guidance.set` entry in the trail — exhaustively, not capped
26
+ * (BRIEF-UX-002 C5, same defect as `RetryIndex.hydrate`: a durable note must not vanish
27
+ * because 1000+ newer writes landed on top of it). The trail answers newest first, so the
28
+ * FIRST entry seen per run is the current note — later (older) entries for the same run
29
+ * are superseded writes and are skipped, which is also what keeps a cleared note (newest
30
+ * entry has `text: ''`) from being resurrected by an older non-empty one. Still best-effort,
31
+ * like `RetryIndex.hydrate`; cost is one full-file scan at boot.
32
+ */
33
+ hydrate(audit: AuditLog, log?: (msg: string) => void): Promise<void>;
34
+ /** Upsert the note; the empty string CLEARS it (the DTO field goes back to absent). */
35
+ set(runId: string, text: string): void;
36
+ /** The note for this run, or `undefined` (the DTO spells that as an ABSENT field). */
37
+ guidanceFor(runId: string): string | undefined;
38
+ }
39
+ //# sourceMappingURL=guidance-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guidance-index.d.ts","sourceRoot":"","sources":["../../src/api/guidance-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAE3D;;;;;;;;OAQG;IACG,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB1E,uFAAuF;IACvF,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKtC,sFAAsF;IACtF,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;CAG/C"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The run→operator-guidance index (DES-UX-002 §7.2, CREW-UX-7).
3
+ *
4
+ * NAMING NOTE: DES-UX-002 §7.2 labels this slice CREW-UX-4, but that id was already spent on an
5
+ * unrelated merged slice (crew#308) — this implementation is CREW-UX-7; the doc's §7.2 spec is
6
+ * what it implements.
7
+ *
8
+ * A durable pre-gate annotation: ONE operator guidance note per run, upserted via
9
+ * `PUT /runs/:id/guidance` and echoed as `AgentSession.guidance` on the run DTOs. The engine's
10
+ * run record carries no such field (wicked-core-ts 0.6.x), so — exactly like retry lineage
11
+ * (CREW-UX-3) and the actor trail (task #88) — the durable record is the audit trail: every
12
+ * write lands as a `guidance.set` entry whose `detail.text` is the full note (empty string =
13
+ * cleared), and this map is the read-side latency layer that lets the run DTOs echo it without
14
+ * a trail scan per request.
15
+ *
16
+ * Mirrors `RetryIndex`'s posture: hydrated once at server start (best-effort — a missing or
17
+ * unreadable trail leaves guidance blank, the pre-CREW-UX-7 behavior, not an error) and updated
18
+ * at the same post-commit point that writes the audit entry, so the map can only lag by a
19
+ * failed hydrate, never diverge silently.
20
+ */
21
+ export class GuidanceIndex {
22
+ runToGuidance = new Map();
23
+ /**
24
+ * Load guidance from EVERY `guidance.set` entry in the trail — exhaustively, not capped
25
+ * (BRIEF-UX-002 C5, same defect as `RetryIndex.hydrate`: a durable note must not vanish
26
+ * because 1000+ newer writes landed on top of it). The trail answers newest first, so the
27
+ * FIRST entry seen per run is the current note — later (older) entries for the same run
28
+ * are superseded writes and are skipped, which is also what keeps a cleared note (newest
29
+ * entry has `text: ''`) from being resurrected by an older non-empty one. Still best-effort,
30
+ * like `RetryIndex.hydrate`; cost is one full-file scan at boot.
31
+ */
32
+ async hydrate(audit, log) {
33
+ try {
34
+ const superseded = new Set();
35
+ for (const entry of await audit.readAll({ action: 'guidance.set' })) {
36
+ if (typeof entry.runId !== 'string')
37
+ continue;
38
+ if (superseded.has(entry.runId))
39
+ continue;
40
+ // The NEWEST entry for a run decides — even when its text is malformed. Marking the
41
+ // run seen BEFORE the text check keeps a corrupt newest write from resurrecting an
42
+ // older superseded note (Copilot, #312): unknowable current note = no note.
43
+ superseded.add(entry.runId);
44
+ const text = entry.detail?.['text'];
45
+ if (typeof text !== 'string')
46
+ continue;
47
+ if (text !== '')
48
+ this.runToGuidance.set(entry.runId, text);
49
+ }
50
+ }
51
+ catch (err) {
52
+ log?.(`[runs] guidance-index hydrate failed (prior runs read as no-guidance until restart): ${err instanceof Error ? err.message : String(err)}`);
53
+ }
54
+ }
55
+ /** Upsert the note; the empty string CLEARS it (the DTO field goes back to absent). */
56
+ set(runId, text) {
57
+ if (text === '')
58
+ this.runToGuidance.delete(runId);
59
+ else
60
+ this.runToGuidance.set(runId, text);
61
+ }
62
+ /** The note for this run, or `undefined` (the DTO spells that as an ABSENT field). */
63
+ guidanceFor(runId) {
64
+ return this.runToGuidance.get(runId);
65
+ }
66
+ }
67
+ //# sourceMappingURL=guidance-index.js.map