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
@@ -0,0 +1,248 @@
1
+ /**
2
+ * THE DELIVERABLE FLOOR (crew#311) — "done" is re-derived from the artifact, never asserted.
3
+ *
4
+ * ## The defect
5
+ *
6
+ * A governed unit that produced NOTHING still passed its execution gate on the strength of a
7
+ * prose reply. The engine's only universal floor is the SUBSTANCE floor
8
+ * (wicked-core `actor.rs`, "phase produced no reviewable substance"): a governed
9
+ * Creator/Neutral unit is rejected only when its output carries BOTH under 200 trimmed chars
10
+ * of prose AND no worktree change. So ~200 characters of narration — "I'll read the design
11
+ * spec, let me check the API types…" — clears it, with no file anywhere. Prose length stood in
12
+ * for evidence.
13
+ *
14
+ * The engine has two real evidence instruments, and neither reaches a run of this shape:
15
+ *
16
+ * 1. `validator_pin` → the built-in EVIDENCE floor (`builtin_floors::EVIDENCE_FLOOR_PIN`,
17
+ * criterion "the run left a change in its worktree (done is re-derived from the diff, never
18
+ * asserted)"). It re-verifies a git DIFF, and is fail-closed on a run with no worktree — so
19
+ * an UNBOUND run (`repoRef` omitted, `workdir: null`) can never satisfy it. Every crew
20
+ * interactive seam launches unbound on purpose (wicked-core#293/#294), and their deliverable
21
+ * is a FILE in a declared write root, invisible to a diff.
22
+ * 2. `required_deliverables` → enforced by wicked-core as of FINDING-101, but only in the
23
+ * WRAPPED-CLI runner (`execute_wrapped.rs`), only against the unit's own cwd, and any
24
+ * absolute or `..`-escaping path is counted MISSING by construction. The ACP runner — the
25
+ * path a real seat takes — never consults it. So for a run whose deliverable is an absolute
26
+ * path under `extraWriteRoots`, declaring it there would fail every run on one runner and
27
+ * check nothing on the other. That gap is wicked-core's to close
28
+ * (wicked-core#297, items 1 and 3); once it is, this phase can retire in favour of the
29
+ * engine field. Crew must not paper over it meanwhile by declaring something unverifiable.
30
+ *
31
+ * ## What this module does
32
+ *
33
+ * It gives crew the instrument it can honestly own: a deterministic TOOL phase, appended to a
34
+ * PER-RUN copy of the workflow def (the exact mechanism `core/deliver.ts` proved for
35
+ * `deliver: "pr"` — the shared def is never mutated, nothing lands in the overlay dir, the
36
+ * catalog never grows a per-run entry), which asserts that the artifacts the launcher declared
37
+ * EXIST, are non-empty, and were written by THIS run (crew#320 — existence alone re-derives
38
+ * nothing at a path a prior run already populated). A Tool phase's failure surface is its exit
39
+ * code: wicked-core's
40
+ * `run_tool_cmd` maps a non-zero exit to `StepStatus::Failed`, which fails the unit and the run
41
+ * through the unchanged completion path. No LLM, no worktree, no judgement about the content —
42
+ * only "the phase promised this file; is it there".
43
+ *
44
+ * That is deliberately a FLOOR and not a review, in exactly the sense `builtin_floors` documents:
45
+ * it proves an artifact exists. It says nothing about whether the artifact is correct, or even
46
+ * related to the task. What it removes is the failure mode crew#311 recorded — a unit whose
47
+ * `Write` was policy-denied, which therefore produced no file at all, reporting done.
48
+ *
49
+ * ## The report
50
+ *
51
+ * The script prints what was EXPECTED and what was FOUND before it exits, on both branches, so
52
+ * the failure is legible from the unit output alone: which paths were declared, which exist
53
+ * (with their size), and which are missing and why (absent / empty file / empty directory).
54
+ * An operator reading a failed run must never have to guess what the phase was supposed to
55
+ * produce.
56
+ */
57
+ /** The id of the appended floor phase — also the collision probe when a def already carries one. */
58
+ export const DELIVERABLE_FLOOR_PHASE_ID = 'verify-deliverables';
59
+ /** The marker line the failing branch prints. Asserted by tests and greppable in a unit output. */
60
+ export const DELIVERABLE_FLOOR_FAILURE_MARKER = '[wicked-crew] DELIVERABLE FLOOR FAILED';
61
+ /**
62
+ * Slack allowed on the freshness comparison, in milliseconds (crew#320).
63
+ *
64
+ * The launch timestamp comes from the daemon's `Date.now()` (millisecond resolution); the
65
+ * artifact timestamp comes from the FILESYSTEM, and not every filesystem keeps milliseconds —
66
+ * HFS+ and ext3 truncate mtime to whole seconds, and some SMB/NFS mounts are coarser still. A
67
+ * file genuinely written 400 ms after a launch at `…:07.600Z` can therefore report an mtime of
68
+ * `…:07.000Z` and read as older than the launch that produced it.
69
+ *
70
+ * One second absorbs that truncation without weakening the check this exists to make: the stale
71
+ * artifact crew#320 describes is the PREVIOUS run's output, which predates this run's launch by
72
+ * that run's whole remaining lifetime plus the turnaround before the next one — orders of
73
+ * magnitude more than a timestamp-granularity rounding error.
74
+ */
75
+ export const DELIVERABLE_FLOOR_MTIME_SLACK_MS = 1_000;
76
+ /**
77
+ * The floor program, run as `node -e <script> <launchedAtMs> <path...>`.
78
+ *
79
+ * Node rather than a shell: this must work identically on macOS, Linux, and Windows, and the
80
+ * daemon already IS node, so `process.execPath` is a guaranteed-present absolute interpreter
81
+ * (see {@link deliverableFloorPhase}). `-e` runs CommonJS, so `require` is available; the launch
82
+ * timestamp and the declared paths arrive as `process.argv.slice(1)`, passed as argv rather than
83
+ * interpolated into a shell string so a path containing a space, a quote, or a `$` cannot be
84
+ * re-parsed.
85
+ *
86
+ * A path counts as PRODUCED when it exists, carries bytes, and was written by THIS RUN:
87
+ *
88
+ * - a non-empty file whose `mtimeMs` is at or after the launch timestamp;
89
+ * - a directory holding at least one direct entry whose `mtimeMs` is at or after it (a phase may
90
+ * declare a directory of outputs; the directory's own mtime is not enough, because a directory
91
+ * left over from a prior run keeps its entries and its mtime).
92
+ *
93
+ * A zero-byte file is NOT evidence — it is the same "nothing was produced" this exists to catch,
94
+ * and the draft/chat seams already treated an empty deliverable as a failure at their own
95
+ * post-hoc check.
96
+ *
97
+ * ## Why the timestamp (crew#320)
98
+ *
99
+ * Existence alone re-derives nothing when the path can be pre-populated. The interactive seams
100
+ * copy their deliverable to `outPath` and never remove it, and demo/draft/edit run directories
101
+ * are keyed by DOCUMENT ID (edit by `doc:version`) — so a second run over the same key starts
102
+ * with the previous run's file already sitting at the declared path. The floor would find it,
103
+ * find bytes, and pass: the artifact exists, but this run did not produce it. "Done" would be
104
+ * asserted by a leftover rather than derived from this run's work — the exact failure shape one
105
+ * level in from the one crew#311 closed.
106
+ *
107
+ * The comparison is the stronger of the two available fixes. Clearing `outPath` at launch would
108
+ * also work, but only for as long as every seam remembers to clear it; the mtime check survives
109
+ * a seam that forgets, and it survives a seam that has not been written yet.
110
+ *
111
+ * A launch timestamp that does not parse FAILS CLOSED. An armed floor that cannot judge freshness
112
+ * is not a floor, and passing on the grounds that the check itself is broken is precisely the
113
+ * "assert done" move this module exists to refuse.
114
+ */
115
+ export function deliverableFloorScript() {
116
+ return [
117
+ 'const fs=require("node:fs");',
118
+ 'const pathmod=require("node:path");',
119
+ 'const launch=Number(process.argv[1]);',
120
+ 'const want=process.argv.slice(2);',
121
+ 'const iso=(t)=>{try{return new Date(t).toISOString()}catch(e){return String(t)}};',
122
+ 'if(!Number.isFinite(launch)){',
123
+ `console.log(${JSON.stringify(DELIVERABLE_FLOOR_FAILURE_MARKER)}+" — the floor was armed without a parseable launch timestamp ("+JSON.stringify(process.argv[1])+"), so it cannot tell this run's artifacts from a prior run's leftovers. Failing closed (crew#320).");`,
124
+ 'process.exit(1)}',
125
+ `const floorAt=launch-${DELIVERABLE_FLOOR_MTIME_SLACK_MS};`,
126
+ 'const fresh=(t)=>t>=floorAt;',
127
+ 'const found=[],missing=[];',
128
+ 'for(const p of want){',
129
+ 'let s=null;',
130
+ 'try{s=fs.statSync(p)}catch(e){s=null}',
131
+ 'if(s===null){missing.push(p+" (does not exist)");continue}',
132
+ 'if(s.isDirectory()){',
133
+ 'let names=[];try{names=fs.readdirSync(p)}catch(e){names=[]}',
134
+ 'if(names.length===0){missing.push(p+" (directory is empty)");continue}',
135
+ 'let n=0;',
136
+ 'for(const e of names){let es=null;try{es=fs.statSync(pathmod.join(p,e))}catch(err){es=null}if(es!==null&&fresh(es.mtimeMs)){n++}}',
137
+ 'if(n===0){missing.push(p+" (directory, "+names.length+" entries, but NONE written by this run — every entry predates the launch at "+iso(launch)+", so they are a PRIOR run\'s output)")}',
138
+ 'else{found.push(p+" (directory, "+names.length+" entries, "+n+" written by this run)")}',
139
+ 'continue}',
140
+ 'if(s.size===0){missing.push(p+" (file is empty, 0 bytes)");continue}',
141
+ 'if(!fresh(s.mtimeMs)){missing.push(p+" (STALE: last modified "+iso(s.mtimeMs)+", before this run launched at "+iso(launch)+" — it is a PRIOR run\'s artifact, not this run\'s)");continue}',
142
+ 'found.push(p+" ("+s.size+" bytes)");',
143
+ '}',
144
+ 'console.log("[wicked-crew] deliverable floor: this phase declared "+want.length+" artifact(s); this run launched at "+iso(launch)+".");',
145
+ 'console.log("[wicked-crew] EXPECTED: "+(want.length?want.join(", "):"(none)"));',
146
+ 'console.log("[wicked-crew] FOUND: "+(found.length?found.join(", "):"(nothing)"));',
147
+ 'if(missing.length===0){',
148
+ 'console.log("[wicked-crew] every declared deliverable exists, carries bytes, and was written by THIS run — done is re-derived from the artifact, not asserted.");',
149
+ 'process.exit(0)}',
150
+ 'console.log("[wicked-crew] MISSING: "+missing.join(", "));',
151
+ `console.log(${JSON.stringify(DELIVERABLE_FLOOR_FAILURE_MARKER)}+" — the run reported done without producing the artifact(s) it was launched to produce. A prose reply is not a deliverable (crew#311), and a prior run's leftover file is not this run's evidence (crew#320).");`,
152
+ 'process.exit(1);',
153
+ ].join('');
154
+ }
155
+ /**
156
+ * The floor PhaseDef for `paths`, judged against `launchedAtMs`.
157
+ *
158
+ * `launchedAtMs` is positional and required rather than defaulted (crew#320): a default would let
159
+ * a new caller arm a floor that checks existence only, and the resulting run would report done on
160
+ * a leftover with nothing in its output saying the freshness half was never applied. The compiler
161
+ * asking the question is the point.
162
+ *
163
+ * `cmd[0]` is `process.execPath` — the ABSOLUTE path of the node binary already running the
164
+ * daemon — not the bare name `node`. wicked-core preflights every Tool phase at launch
165
+ * (`workflow::preflight_tool_phases`) and REFUSES to start a run whose tool binary does not
166
+ * resolve; a bare `node` resolves only if the engine's PATH happens to carry one, which is
167
+ * exactly the kind of environment-dependent gate that fails in production and passes in tests.
168
+ * The composed def is per-run and hot-registered only, so baking a machine-local path into it
169
+ * costs nothing: it never reaches the catalog or the overlay dir.
170
+ *
171
+ * `role: 'neutral'` + `executes_code: false` + `governed`-free Tool dispatch: this phase is
172
+ * deterministic tooling, so the substance floor, the agent judge, and the input-governance fold
173
+ * do not apply to it — its verdict IS its exit code.
174
+ */
175
+ export function deliverableFloorPhase(paths, launchedAtMs, dependsOn = []) {
176
+ return {
177
+ id: DELIVERABLE_FLOOR_PHASE_ID,
178
+ kind: 'test',
179
+ executor: {
180
+ type: 'tool',
181
+ // The timestamp rides ahead of the paths as its own argv slot: a path is arbitrary user
182
+ // text and could itself look like a number, so position — not parsing — separates them.
183
+ cmd: [process.execPath, '-e', deliverableFloorScript(), String(launchedAtMs), ...paths],
184
+ },
185
+ gate_type: 'execution',
186
+ gate: 'auto',
187
+ executes_code: false,
188
+ // NOT `verified_evidence: true`: wicked-core arms any `verified_evidence` phase that names no
189
+ // pin of its own with the worktree-DIFF floor (FINDING-055), which is fail-closed on the
190
+ // repo-less runs this phase exists to serve — it would deny every one of them for the wrong
191
+ // reason. This phase carries its own re-verification in its exit code.
192
+ verified_evidence: false,
193
+ // NOT the declared paths either: core checks `required_deliverables` relative to the unit's
194
+ // cwd and counts every absolute path as missing (see the module doc; wicked-core#297).
195
+ // Declaring them here would fail the run on the wrapped-CLI path for the wrong reason.
196
+ required_deliverables: [],
197
+ depends_on: dependsOn,
198
+ role: 'neutral',
199
+ skill_ref: null,
200
+ allowed_skills: [],
201
+ validator_pin: null,
202
+ };
203
+ }
204
+ /**
205
+ * Compose a PER-RUN def: `base`'s phases (untouched — the shared def is never mutated) plus the
206
+ * deliverable floor appended last, under a run-scoped id. Mirrors `composeDeliverWorkflow`'s
207
+ * contract exactly, including the id charset/length rules `registerWorkflow` enforces and the
208
+ * deliberate absence of `is_system` (core's register schema rejects unknown fields on this path,
209
+ * and a composed def is engine input, not catalog data).
210
+ *
211
+ * Throws on an empty `paths` list — a floor over nothing would pass vacuously, which is the
212
+ * defect, not the fix. Throws on a def that already carries the floor phase, for the same
213
+ * ambiguity reason `composeDeliverWorkflow` does.
214
+ *
215
+ * `launchedAtMs` defaults to composition time, which is the correct instant on every path that
216
+ * exists: composition is the last thing that happens before `core.launchRun`, so nothing this run
217
+ * produces can predate it, and a nonsense value is rejected here rather than being discovered by
218
+ * the tool phase minutes later (crew#320).
219
+ */
220
+ export function composeDeliverableFloor(base, runId, paths, launchedAtMs = Date.now()) {
221
+ if (paths.length === 0) {
222
+ throw new Error('requireDeliverables: at least one path must be declared — a floor over nothing passes vacuously');
223
+ }
224
+ for (const p of paths) {
225
+ if (p.trim().length === 0) {
226
+ throw new Error('requireDeliverables: a declared deliverable path must not be blank');
227
+ }
228
+ }
229
+ if (!Number.isFinite(launchedAtMs) || launchedAtMs <= 0) {
230
+ throw new Error(`requireDeliverables: launchedAtMs must be a positive epoch-millisecond timestamp, got ` +
231
+ `${String(launchedAtMs)} — the floor compares it against each artifact's mtime to prove ` +
232
+ `THIS run produced them`);
233
+ }
234
+ if (base.phases.some((p) => p.id === DELIVERABLE_FLOOR_PHASE_ID)) {
235
+ throw new Error(`workflow '${base.id}' already has a '${DELIVERABLE_FLOOR_PHASE_ID}' phase — launch it without requireDeliverables`);
236
+ }
237
+ const last = base.phases[base.phases.length - 1];
238
+ const safeRunId = runId.replace(/[^a-zA-Z0-9._-]/g, '_');
239
+ const composedId = `${base.id}-verified-${safeRunId}`.slice(0, 128);
240
+ return {
241
+ id: composedId,
242
+ phases: [
243
+ ...base.phases,
244
+ deliverableFloorPhase(paths, launchedAtMs, last !== undefined ? [last.id] : []),
245
+ ],
246
+ };
247
+ }
248
+ //# sourceMappingURL=deliverable-floor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deliverable-floor.js","sourceRoot":"","sources":["../../src/core/deliverable-floor.ts"],"names":[],"mappings":"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"}
@@ -18,6 +18,8 @@ type ExecOpts = {
18
18
  timeout?: number;
19
19
  cwd?: string;
20
20
  env?: NodeJS.ProcessEnv;
21
+ /** Suppress the console window a child would open on Windows (git, etc.). */
22
+ windowsHide?: boolean;
21
23
  /** `number | undefined` on purpose: `exactOptionalPropertyTypes` would otherwise reject the very
22
24
  * shape this module defends against, making the guard below untypeable and untestable. */
23
25
  maxBuffer?: number | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/core/exec.ts"],"names":[],"mappings":"AA+BA;;sFAEsF;AACtF,eAAO,MAAM,qBAAqB,QAAmB,CAAC;AAUtD;;;;oDAIoD;AACpD,qBAAa,kBAAmB,SAAQ,KAAK;IAEzC,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM;gBADlB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM;CAS9B;AAED,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAMtD;AAED,KAAK,QAAQ,GAAG;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB;+FAC2F;IAC3F,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAEF;;kFAEkF;AAClF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAEvD;AAED;;kEAEkE;AAClE,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,GAAE,QAAa,GAClB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAoB7C"}
1
+ {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/core/exec.ts"],"names":[],"mappings":"AA+BA;;sFAEsF;AACtF,eAAO,MAAM,qBAAqB,QAAmB,CAAC;AAUtD;;;;oDAIoD;AACpD,qBAAa,kBAAmB,SAAQ,KAAK;IAEzC,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM;gBADlB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM;CAS9B;AAED,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAMtD;AAED,KAAK,QAAQ,GAAG;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;+FAC2F;IAC3F,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAEF;;kFAEkF;AAClF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAEvD;AAED;;kEAEkE;AAClE,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,GAAE,QAAa,GAClB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAoB7C"}
@@ -1 +1 @@
1
- {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/core/exec.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,EAAE;AACF,eAAe;AACf,EAAE;AACF,oGAAoG;AACpG,iGAAiG;AACjG,gGAAgG;AAChG,2FAA2F;AAC3F,EAAE;AACF,kGAAkG;AAClG,kGAAkG;AAClG,qCAAqC;AACrC,EAAE;AACF,wDAAwD;AACxD,EAAE;AACF,gGAAgG;AAChG,8FAA8F;AAC9F,mGAAmG;AACnG,0EAA0E;AAC1E,EAAE;AACF,0BAA0B;AAC1B,EAAE;AACF,8FAA8F;AAC9F,6FAA6F;AAC7F,0BAA0B;AAE1B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C;;sFAEsF;AACtF,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAEtD;kFACkF;AAClF,SAAS,UAAU,CAAC,CAAS;IAC3B,IAAI,CAAC,IAAI,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC;IACpE,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IACpD,OAAO,GAAG,CAAC,QAAQ,CAAC;AACtB,CAAC;AAED;;;;oDAIoD;AACpD,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAEhC;IACA;IAFX,YACW,IAAY,EACZ,UAAkB;QAE3B,KAAK,CACH,GAAG,IAAI,uBAAuB,UAAU,CAAC,UAAU,CAAC,wCAAwC;YAC1F,0CAA0C,IAAI,8CAA8C;YAC5F,wDAAwD,CAC3D,CAAC;QAPO,SAAI,GAAJ,IAAI,CAAQ;QACZ,eAAU,GAAV,UAAU,CAAQ;QAO3B,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAED,iEAAiE;AACjE,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ;QACvB,GAAG,KAAK,IAAI;QACX,GAA0B,CAAC,IAAI,KAAK,mCAAmC,CACzE,CAAC;AACJ,CAAC;AAWD;;kFAEkF;AAClF,MAAM,UAAU,gBAAgB,CAAC,IAAc;IAC7C,OAAO,IAAI,CAAC,SAAS,IAAI,qBAAqB,CAAC;AACjD,CAAC;AAED;;kEAEkE;AAClE,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,IAAY,EACZ,IAAc,EACd,OAAiB,EAAE;IAEnB,gGAAgG;IAChG,6FAA6F;IAC7F,2FAA2F;IAC3F,iCAAiC;IACjC,EAAE;IACF,sFAAsF;IACtF,6EAA6E;IAC7E,8DAA8D;IAC9D,EAAE;IACF,8FAA8F;IAC9F,uFAAuF;IACvF,4CAA4C;IAC5C,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,CAAC;QACH,OAAO,MAAM,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;IACjE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,gBAAgB,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,kBAAkB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACzE,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/core/exec.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,EAAE;AACF,eAAe;AACf,EAAE;AACF,oGAAoG;AACpG,iGAAiG;AACjG,gGAAgG;AAChG,2FAA2F;AAC3F,EAAE;AACF,kGAAkG;AAClG,kGAAkG;AAClG,qCAAqC;AACrC,EAAE;AACF,wDAAwD;AACxD,EAAE;AACF,gGAAgG;AAChG,8FAA8F;AAC9F,mGAAmG;AACnG,0EAA0E;AAC1E,EAAE;AACF,0BAA0B;AAC1B,EAAE;AACF,8FAA8F;AAC9F,6FAA6F;AAC7F,0BAA0B;AAE1B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C;;sFAEsF;AACtF,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAEtD;kFACkF;AAClF,SAAS,UAAU,CAAC,CAAS;IAC3B,IAAI,CAAC,IAAI,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC;IACpE,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IACpD,OAAO,GAAG,CAAC,QAAQ,CAAC;AACtB,CAAC;AAED;;;;oDAIoD;AACpD,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAEhC;IACA;IAFX,YACW,IAAY,EACZ,UAAkB;QAE3B,KAAK,CACH,GAAG,IAAI,uBAAuB,UAAU,CAAC,UAAU,CAAC,wCAAwC;YAC1F,0CAA0C,IAAI,8CAA8C;YAC5F,wDAAwD,CAC3D,CAAC;QAPO,SAAI,GAAJ,IAAI,CAAQ;QACZ,eAAU,GAAV,UAAU,CAAQ;QAO3B,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAED,iEAAiE;AACjE,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ;QACvB,GAAG,KAAK,IAAI;QACX,GAA0B,CAAC,IAAI,KAAK,mCAAmC,CACzE,CAAC;AACJ,CAAC;AAaD;;kFAEkF;AAClF,MAAM,UAAU,gBAAgB,CAAC,IAAc;IAC7C,OAAO,IAAI,CAAC,SAAS,IAAI,qBAAqB,CAAC;AACjD,CAAC;AAED;;kEAEkE;AAClE,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,IAAY,EACZ,IAAc,EACd,OAAiB,EAAE;IAEnB,gGAAgG;IAChG,6FAA6F;IAC7F,2FAA2F;IAC3F,iCAAiC;IACjC,EAAE;IACF,sFAAsF;IACtF,6EAA6E;IAC7E,8DAA8D;IAC9D,EAAE;IACF,8FAA8F;IAC9F,uFAAuF;IACvF,4CAA4C;IAC5C,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,CAAC;QACH,OAAO,MAAM,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;IACjE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,gBAAgB,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,kBAAkB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACzE,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -55,6 +55,84 @@ export interface LaunchRunInput {
55
55
  * fails the launch with no session persisted. Omit for runs that deliver inside their workdir.
56
56
  */
57
57
  extraWriteRoots?: string[];
58
+ /**
59
+ * The PROJECT's co-located code graph this run's governed workers should query instead of the
60
+ * run repo's own — resolved by `projects/graph.ts::resolveProjectGraphBinding`, which is the ONE
61
+ * thing that knows where a project graph lives and what each repo is labelled inside it.
62
+ *
63
+ * `repoLabel` is optional in the TYPE because a repo-LESS run legitimately has none — there is
64
+ * no own repo to name. It is not optional in MEANING for a repo-bound run: the engine uses it to
65
+ * confirm the co-located graph actually holds this run's repo.
66
+ *
67
+ * TypeScript cannot catch a missing label here — the invariant ties this field to `repoRef`, a
68
+ * sibling field — so `CoreAdapter.launchRun` enforces it at RUNTIME. Two failure modes, and they
69
+ * are NOT the same thing:
70
+ *
71
+ * - **crew refuses the launch outright.** Passing `projectGraph` without `projectId`, or with
72
+ * `repoRef` set and no `repoLabel`, THROWS: no session is created and nothing runs. These are
73
+ * caller bugs with no sensible degraded reading, so they fail before anything is persisted.
74
+ * - **the ENGINE declines the binding.** A well-formed binding whose graph turns out not to hold
75
+ * the named label is refused engine-side and the run proceeds against its own repo graph — a
76
+ * real launch, quietly narrower. That is the fallback; it is not what a missing `repoLabel`
77
+ * gets you.
78
+ *
79
+ * Omit the whole field for the per-repo behaviour.
80
+ */
81
+ projectGraph?: {
82
+ dbPath: string;
83
+ repoLabel?: string;
84
+ };
85
+ /**
86
+ * Delivery mode (crew#293). `"pr"` ⇒ the adapter appends the hardened deliver Tool phase
87
+ * (push the run branch + `gh pr create`, see `core/deliver.ts`) to a PER-RUN copy of the
88
+ * selected workflow def and launches that copy — the shared def is never mutated. Requires
89
+ * `workflow`; a free-text run has no def to append to. Omit ⇒ no delivery phase (default).
90
+ */
91
+ deliver?: 'pr';
92
+ /**
93
+ * THE DELIVERABLE FLOOR (crew#311) — absolute paths this run MUST have produced by the time
94
+ * it finishes. When non-empty (and `workflow` is set), the adapter appends a deterministic
95
+ * verification Tool phase to a PER-RUN copy of the def (see `core/deliverable-floor.ts`)
96
+ * which re-derives "done" from the artifacts: every declared path must exist and carry bytes,
97
+ * or the phase exits non-zero and the RUN FAILS, naming what was expected and what was found.
98
+ *
99
+ * This is the instrument for runs whose deliverable is a FILE in a declared write root rather
100
+ * than a worktree diff — every crew interactive seam. The engine's own two floors do not
101
+ * reach them: the built-in evidence floor re-verifies a git diff and is fail-closed on a
102
+ * repo-less run, and `required_deliverables` is checked only by the wrapped-CLI runner and
103
+ * only against the unit's cwd (an absolute path counts as missing there by construction).
104
+ *
105
+ * Omit for runs whose evidence is the worktree diff — those are the engine's to floor.
106
+ */
107
+ requireDeliverables?: string[];
108
+ }
109
+ /** Default `workerStallMinutes` (crew#287): silent minutes before the stall watchdog fires. */
110
+ export declare const DEFAULT_WORKER_STALL_MINUTES = 15;
111
+ /**
112
+ * LOCAL extension of the published `SystemSettings` (wicked-crew-api-types 0.6.0): the crew#287
113
+ * stall-watchdog knob is daemon-owned until the contract picks it up. NOTE for the next
114
+ * api-types release: fold `workerStallMinutes` into `SystemSettings` (and the synthetic
115
+ * `workerStalled` /ws frame — see `api/stall-watchdog.ts` `WorkerStalledFrame` — into the
116
+ * event documentation). Extra fields are forward-additive on the wire (DES-STUDIO-001 §5.1),
117
+ * so shipping it daemon-side first breaks no consumer.
118
+ */
119
+ export interface CrewSystemSettings extends SystemSettings {
120
+ /**
121
+ * Minutes a run in `executing` may go without ANY engine event on the daemon's relay before
122
+ * a synthetic `workerStalled` frame is broadcast on /ws (detection only; default 15).
123
+ */
124
+ workerStallMinutes?: number;
125
+ /**
126
+ * Skin-owned preference blobs, round-tripped verbatim (crew#323). RESTATED here rather than
127
+ * merely inherited from `SystemSettings` so the daemon's own type SAYS the settings store is
128
+ * shared with the experience plane — the fact that used to live only in a comment in the
129
+ * studio's client, which is how `studio.appearance` / `studio.notifications` could be
130
+ * silently dropped by `PUT /settings` without either side's types objecting.
131
+ *
132
+ * The daemon never interprets a value here; it enforces only "JSON-serializable, under the
133
+ * per-key byte cap" at the PUT boundary (`api/routes.ts` `STUDIO_SETTINGS_MAX_BYTES`).
134
+ */
135
+ [key: `studio.${string}`]: unknown;
58
136
  }
59
- export declare const DEFAULT_SETTINGS: SystemSettings;
137
+ export declare const DEFAULT_SETTINGS: CrewSystemSettings;
60
138
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,mBAAmB,uBAAuB,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,8FAA8F;AAC9F,MAAM,WAAW,cAAc;IAC7B,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,eAAO,MAAM,gBAAgB,EAAE,cAE9B,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,mBAAmB,uBAAuB,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,8FAA8F;AAC9F,MAAM,WAAW,cAAc;IAC7B,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,YAAY,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD;;;;;OAKG;IACH,OAAO,CAAC,EAAE,IAAI,CAAC;IACf;;;;;;;;;;;;;;OAcG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,+FAA+F;AAC/F,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;OASG;IACH,CAAC,GAAG,EAAE,UAAU,MAAM,EAAE,GAAG,OAAO,CAAC;CACpC;AAED,eAAO,MAAM,gBAAgB,EAAE,kBAG9B,CAAC"}
@@ -18,7 +18,10 @@
18
18
  * daemon speaks these daemon-owned shapes, which is what quarantines the
19
19
  * FINALIZING core-ts `subscribe` seam to a single file (DES-STUDIO-001 §5.2).
20
20
  */
21
+ /** Default `workerStallMinutes` (crew#287): silent minutes before the stall watchdog fires. */
22
+ export const DEFAULT_WORKER_STALL_MINUTES = 15;
21
23
  export const DEFAULT_SETTINGS = {
22
24
  graphNodeLimit: 150,
25
+ workerStallMinutes: DEFAULT_WORKER_STALL_MINUTES,
23
26
  };
24
27
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA4CH,MAAM,CAAC,MAAM,gBAAgB,GAAmB;IAC9C,cAAc,EAAE,GAAG;CACpB,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA2FH,+FAA+F;AAC/F,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,CAAC;AA6B/C,MAAM,CAAC,MAAM,gBAAgB,GAAuB;IAClD,cAAc,EAAE,GAAG;IACnB,kBAAkB,EAAE,4BAA4B;CACjD,CAAC"}
@@ -19,9 +19,39 @@
19
19
  * live pid earns three 1.5 s attempts before the bridge is declared dead. Identity is checked
20
20
  * too: `/api/health` must report THIS root, or a recycled port belonging to some other service
21
21
  * would be proxied as if it were ours.
22
+ *
23
+ * On start/adopt the pool also records the daemon's own origin with the bridge (crew#298,
24
+ * `POST /api/studio-origin`, interactive ≥ 0.8.0) so the bridge's `GET /` redirects a direct
25
+ * visitor into studio instead of its API-only fallback page. Fire-and-forget, once per pooled
26
+ * bridge: recording can never fail — or slow down — a proxied request.
22
27
  */
23
28
  import { type ChildProcess } from 'node:child_process';
29
+ import type { AddressInfo } from 'node:net';
24
30
  export declare const LOCK_NAME = ".wi-serve.json";
31
+ /**
32
+ * The wicked-interactive range crew will start, as an npm spec.
33
+ *
34
+ * The spawn used to be a bare `npx --yes wicked-interactive`, which resolves whatever the public
35
+ * registry calls `latest` AT RUNTIME — no lockfile entry, no integrity hash, no floor. Three things
36
+ * follow from that, and none of them are theoretical:
37
+ *
38
+ * - a breaking wicked-interactive release changes crew's behaviour with no crew change, no review,
39
+ * and nothing in crew's history to point at during the incident;
40
+ * - crew has no way to REQUIRE a route it depends on. The learned-theme readback
41
+ * (`GET /d/:docId/api/theme/learned`, interactive#181) shipped in 0.8.1; against 0.8.0 the
42
+ * brand-learn surface polls forever and degrades silently. A floor turns that into a resolvable
43
+ * version, not a mystery;
44
+ * - the resolution is a network fetch from a public registry into a long-lived daemon.
45
+ *
46
+ * A caret floor is the smallest fix that closes the first two: it pins the MAJOR-compatible range so
47
+ * a 0.9.0 cannot arrive unannounced, and it states the minimum crew actually needs. It does NOT make
48
+ * the fetch reproducible — that needs a real dependency with a lockfile entry, which is the
49
+ * follow-up this comment exists to keep visible.
50
+ *
51
+ * Bump this when crew starts depending on a newer interactive route, and say which route in the
52
+ * commit.
53
+ */
54
+ export declare const INTERACTIVE_SPEC = "wicked-interactive@^0.8.1";
25
55
  /** ADR-0025: 1.5 s × 3 while the pid lives. */
26
56
  export declare const HEALTH_TIMEOUT_MS = 1500;
27
57
  export declare const HEALTH_ATTEMPTS = 3;
@@ -45,7 +75,21 @@ export interface BridgePoolIo {
45
75
  startTimeoutMs?: number;
46
76
  healthTimeoutMs?: number;
47
77
  log?: (msg: string) => void;
78
+ /** Lower-severity channel for EXPECTED skips (a pre-0.8.0 bridge without the endpoint, #298). */
79
+ debug?: (msg: string) => void;
80
+ /**
81
+ * The daemon's own origin (`http://<bound host>:<bound port>`), resolved LAZILY — the pool is
82
+ * built before `listen`, but only consulted while serving a request, i.e. after the address is
83
+ * bound. Null (or absent) means "nothing to record" and the pool never POSTs (#298).
84
+ */
85
+ studioOrigin?: () => string | null;
48
86
  }
87
+ /**
88
+ * The daemon's own http origin from its bound server address, or null before `listen`. Wildcard
89
+ * binds (`0.0.0.0` / `::`) are not dialable from a browser, so they normalize to loopback — the
90
+ * bridge is local-only anyway, so whoever hits its port can reach the daemon at 127.0.0.1 too.
91
+ */
92
+ export declare function boundOrigin(addr: AddressInfo | string | null): string | null;
49
93
  /** `<root>/.wi-serve.json`, or null when absent/unparseable/incomplete. */
50
94
  export declare function readLock(root: string): LiveBridge | null;
51
95
  /** Signal 0 probes existence without delivering: EPERM means alive-but-not-ours. */
@@ -64,6 +108,14 @@ export declare class InteractiveBridgePool {
64
108
  /** Live bridges, for tests and future operator introspection. */
65
109
  keys(): string[];
66
110
  private resolveOrStart;
111
+ /**
112
+ * POST the daemon's own origin to the bridge's `/api/studio-origin` (#298), so the bridge's
113
+ * `GET /` redirects a direct visitor into studio instead of the API-only fallback page.
114
+ * At most one attempt per pooled bridge per process; 404/405 means the bridge predates the
115
+ * endpoint (interactive < 0.8.0) and is an expected skip, any other failure is a warn — never
116
+ * an error, because origin recording must never fail a proxy request.
117
+ */
118
+ private recordStudioOrigin;
67
119
  /** The lockfile points at a bridge that is alive, answering, and serving THIS root. */
68
120
  private healthy;
69
121
  private start;
@@ -1 +1 @@
1
- {"version":3,"file":"bridge-pool.d.ts","sourceRoot":"","sources":["../../src/interactive/bridge-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAI3E,eAAO,MAAM,SAAS,mBAAmB,CAAC;AAC1C,+CAA+C;AAC/C,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,6FAA6F;AAC7F,eAAO,MAAM,gBAAgB,QAAS,CAAC;AAEvC,yDAAyD;AACzD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,sFAAsF;AACtF,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,qFAAqF;IACrF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAK1C;AAOD,gGAAgG;AAChG,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,YAAY,CAAC;IACvC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAQxD;AAED,oFAAoF;AACpF,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ7C;AAkBD,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAe;IAClC,oGAAoG;IACpG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiC;IACtD,6FAA6F;IAC7F,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0C;gBAEvD,EAAE,GAAE,YAAiB;IAIjC,qGAAqG;IAC/F,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAc/C,uFAAuF;IACvF,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI9B,iEAAiE;IACjE,IAAI,IAAI,MAAM,EAAE;YAIF,cAAc;IAW5B,uFAAuF;YACzE,OAAO;YAeP,KAAK;CAsCpB"}
1
+ {"version":3,"file":"bridge-pool.d.ts","sourceRoot":"","sources":["../../src/interactive/bridge-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,eAAO,MAAM,SAAS,mBAAmB,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,gBAAgB,8BAA8B,CAAC;AAC5D,+CAA+C;AAC/C,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,6FAA6F;AAC7F,eAAO,MAAM,gBAAgB,QAAS,CAAC;AAEvC,yDAAyD;AACzD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,sFAAsF;AACtF,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,qFAAqF;IACrF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAK1C;AAOD,gGAAgG;AAChG,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,YAAY,CAAC;IACvC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,iGAAiG;IACjG,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CACpC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAS5E;AAED,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAQxD;AAED,oFAAoF;AACpF,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ7C;AAyBD,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAe;IAClC,oGAAoG;IACpG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmC;IACxD,6FAA6F;IAC7F,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0C;gBAEvD,EAAE,GAAE,YAAiB;IAIjC,qGAAqG;IAC/F,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAc/C,uFAAuF;IACvF,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI9B,iEAAiE;IACjE,IAAI,IAAI,MAAM,EAAE;YAIF,cAAc;IAW5B;;;;;;OAMG;IACH,OAAO,CAAC,kBAAkB;IA+B1B,uFAAuF;YACzE,OAAO;YAeP,KAAK;CAsCpB"}
@@ -19,11 +19,40 @@
19
19
  * live pid earns three 1.5 s attempts before the bridge is declared dead. Identity is checked
20
20
  * too: `/api/health` must report THIS root, or a recycled port belonging to some other service
21
21
  * would be proxied as if it were ours.
22
+ *
23
+ * On start/adopt the pool also records the daemon's own origin with the bridge (crew#298,
24
+ * `POST /api/studio-origin`, interactive ≥ 0.8.0) so the bridge's `GET /` redirects a direct
25
+ * visitor into studio instead of its API-only fallback page. Fire-and-forget, once per pooled
26
+ * bridge: recording can never fail — or slow down — a proxied request.
22
27
  */
23
28
  import { spawn as nodeSpawn } from 'node:child_process';
24
29
  import { mkdirSync, readFileSync } from 'node:fs';
25
30
  import { join, resolve } from 'node:path';
26
31
  export const LOCK_NAME = '.wi-serve.json';
32
+ /**
33
+ * The wicked-interactive range crew will start, as an npm spec.
34
+ *
35
+ * The spawn used to be a bare `npx --yes wicked-interactive`, which resolves whatever the public
36
+ * registry calls `latest` AT RUNTIME — no lockfile entry, no integrity hash, no floor. Three things
37
+ * follow from that, and none of them are theoretical:
38
+ *
39
+ * - a breaking wicked-interactive release changes crew's behaviour with no crew change, no review,
40
+ * and nothing in crew's history to point at during the incident;
41
+ * - crew has no way to REQUIRE a route it depends on. The learned-theme readback
42
+ * (`GET /d/:docId/api/theme/learned`, interactive#181) shipped in 0.8.1; against 0.8.0 the
43
+ * brand-learn surface polls forever and degrades silently. A floor turns that into a resolvable
44
+ * version, not a mystery;
45
+ * - the resolution is a network fetch from a public registry into a long-lived daemon.
46
+ *
47
+ * A caret floor is the smallest fix that closes the first two: it pins the MAJOR-compatible range so
48
+ * a 0.9.0 cannot arrive unannounced, and it states the minimum crew actually needs. It does NOT make
49
+ * the fetch reproducible — that needs a real dependency with a lockfile entry, which is the
50
+ * follow-up this comment exists to keep visible.
51
+ *
52
+ * Bump this when crew starts depending on a newer interactive route, and say which route in the
53
+ * commit.
54
+ */
55
+ export const INTERACTIVE_SPEC = 'wicked-interactive@^0.8.1';
27
56
  /** ADR-0025: 1.5 s × 3 while the pid lives. */
28
57
  export const HEALTH_TIMEOUT_MS = 1500;
29
58
  export const HEALTH_ATTEMPTS = 3;
@@ -41,7 +70,22 @@ export class BridgeUnavailableError extends Error {
41
70
  }
42
71
  /** The one command that reproduces a failed start in a terminal, where its output is visible. */
43
72
  function serveCommand(root) {
44
- return `npx wicked-interactive serve --root ${root}`;
73
+ return `npx ${INTERACTIVE_SPEC} serve --root ${root}`;
74
+ }
75
+ /**
76
+ * The daemon's own http origin from its bound server address, or null before `listen`. Wildcard
77
+ * binds (`0.0.0.0` / `::`) are not dialable from a browser, so they normalize to loopback — the
78
+ * bridge is local-only anyway, so whoever hits its port can reach the daemon at 127.0.0.1 too.
79
+ */
80
+ export function boundOrigin(addr) {
81
+ if (addr === null || typeof addr === 'string')
82
+ return null;
83
+ const host = addr.address === '' || addr.address === '0.0.0.0' || addr.address === '::'
84
+ ? '127.0.0.1'
85
+ : addr.address.includes(':')
86
+ ? `[${addr.address}]`
87
+ : addr.address;
88
+ return `http://${host}:${addr.port}`;
45
89
  }
46
90
  /** `<root>/.wi-serve.json`, or null when absent/unparseable/incomplete. */
47
91
  export function readLock(root) {
@@ -97,8 +141,8 @@ export class InteractiveBridgePool {
97
141
  // Fast path: we started/adopted it and its pid is still alive. A full health round-trip on
98
142
  // every proxied request would put a 1.5 s timeout budget in front of every asset fetch.
99
143
  const cached = this.live.get(root);
100
- if (cached && pidAlive(cached.pid))
101
- return cached;
144
+ if (cached && pidAlive(cached.bridge.pid))
145
+ return cached.bridge;
102
146
  this.live.delete(root);
103
147
  const pending = this.inflight.get(root);
104
148
  if (pending)
@@ -116,14 +160,51 @@ export class InteractiveBridgePool {
116
160
  return [...this.live.keys()];
117
161
  }
118
162
  async resolveOrStart(root) {
119
- const adopted = await this.healthy(root);
120
- if (adopted) {
121
- this.live.set(root, adopted);
122
- return adopted;
123
- }
124
- const started = await this.start(root);
125
- this.live.set(root, started);
126
- return started;
163
+ // Adopt-or-start; either way the bridge just answered `/api/health` for this root, which is
164
+ // exactly the moment #298 wants the studio origin recorded — fire-and-forget, so recording
165
+ // can never delay (let alone fail) the proxied request that triggered the resolution.
166
+ const bridge = (await this.healthy(root)) ?? (await this.start(root));
167
+ const entry = { bridge, originRecorded: false };
168
+ this.live.set(root, entry);
169
+ this.recordStudioOrigin(entry);
170
+ return bridge;
171
+ }
172
+ /**
173
+ * POST the daemon's own origin to the bridge's `/api/studio-origin` (#298), so the bridge's
174
+ * `GET /` redirects a direct visitor into studio instead of the API-only fallback page.
175
+ * At most one attempt per pooled bridge per process; 404/405 means the bridge predates the
176
+ * endpoint (interactive < 0.8.0) and is an expected skip, any other failure is a warn — never
177
+ * an error, because origin recording must never fail a proxy request.
178
+ */
179
+ recordStudioOrigin(entry) {
180
+ if (entry.originRecorded)
181
+ return;
182
+ entry.originRecorded = true;
183
+ const origin = this.io.studioOrigin?.() ?? null;
184
+ if (origin === null)
185
+ return; // no origin to record (pool not bootstrapped from a listening daemon)
186
+ const { host, port } = entry.bridge;
187
+ const timeout = this.io.healthTimeoutMs ?? HEALTH_TIMEOUT_MS;
188
+ void (async () => {
189
+ try {
190
+ const res = await fetch(`http://${host}:${port}/api/studio-origin`, {
191
+ method: 'POST',
192
+ headers: { 'content-type': 'application/json' },
193
+ body: JSON.stringify({ origin }),
194
+ signal: AbortSignal.timeout(timeout),
195
+ });
196
+ await res.arrayBuffer().catch(() => undefined); // drain, so the connection is released
197
+ if (res.status === 404 || res.status === 405) {
198
+ this.io.debug?.(`bridge at ${host}:${port} has no /api/studio-origin (interactive < 0.8.0) — skipping origin record`);
199
+ }
200
+ else if (!res.ok) {
201
+ this.io.log?.(`recording studio origin ${origin} with the bridge at ${host}:${port} failed: HTTP ${res.status}`);
202
+ }
203
+ }
204
+ catch (err) {
205
+ this.io.log?.(`recording studio origin ${origin} with the bridge at ${host}:${port} failed: ${err.message}`);
206
+ }
207
+ })();
127
208
  }
128
209
  /** The lockfile points at a bridge that is alive, answering, and serving THIS root. */
129
210
  async healthy(root) {
@@ -178,7 +259,7 @@ export class InteractiveBridgePool {
178
259
  function defaultSpawn(root) {
179
260
  // `--yes` is load-bearing: without it npx PROMPTS when the package is not installed, and a
180
261
  // daemon has no tty to answer with — the request would hang instead of failing to a 503.
181
- return nodeSpawn('npx', ['--yes', 'wicked-interactive', 'serve', '--root', root], {
262
+ return nodeSpawn('npx', ['--yes', INTERACTIVE_SPEC, 'serve', '--root', root], {
182
263
  cwd: root,
183
264
  detached: true,
184
265
  stdio: 'ignore',