wicked-crew-api-types 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +31 -0
  2. package/index.d.ts +1148 -0
  3. package/package.json +43 -0
package/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # wicked-crew-api-types
2
+
3
+ The **wire contract** of the wicked-crew daemon: one definition of every shape that crosses the
4
+ `/api/v1` REST surface and the `/ws` CoreEvent stream. Both sides compile against it —
5
+
6
+ - the daemon's route layer, via `packages/crew/src/core/types.ts` (a re-export), and
7
+ - the [wicked-studio](https://github.com/mikeparcewski/wicked-studio) SPA, via its
8
+ `src/api/types.ts` (likewise a re-export)
9
+
10
+ — replacing the hand-copied mirror the studio used to carry (task #84). Published to npm as
11
+ [`wicked-crew-api-types`](https://www.npmjs.com/package/wicked-crew-api-types) so out-of-repo
12
+ clients (studio first among them) can pin the contract as a normal dependency; crew's in-repo
13
+ consumers keep the workspace reference.
14
+
15
+ ## Rules
16
+
17
+ - **Wire shapes only.** Engine-internal types (`LaunchRunInput`, `RepoOnboardRef`, runtime
18
+ constants) stay in the daemon.
19
+ - **Zero runtime.** This is a `.d.ts`-only package: no JavaScript, nothing to build. Consumers
20
+ declare it as a devDependency (the published `wicked-crew` tarball never depends on it at
21
+ runtime). Import it with `import type` only — the `exports` map offers just a `types`
22
+ condition, so a value import fails loudly.
23
+ - **Forward-additive.** Optional/index-signature fields keep the shapes additive: a newer daemon
24
+ that adds fields still parses in an older studio (DES-STUDIO-001 §5.1).
25
+
26
+ ## Drift guard
27
+
28
+ `packages/crew/tests/wire-contract.test.ts` holds compile-time assertions, run by
29
+ `npm run -w packages/crew typecheck` (and CI): every response type the daemon produces must satisfy
30
+ this contract, and every request body the contract allows must be accepted by the daemon's zod
31
+ schemas. Change the wire shape in one place and the typecheck fails everywhere it matters.
package/index.d.ts ADDED
@@ -0,0 +1,1148 @@
1
+ /**
2
+ * wicked-crew-api-types — the wire contract of the wicked-crew daemon.
3
+ *
4
+ * One definition of every shape that crosses the daemon's HTTP/WS boundary:
5
+ * the `/api/v1` JSON responses and request bodies, and the CoreEvent frames
6
+ * delivered verbatim over `/ws`. BOTH sides compile against this package —
7
+ * the daemon's route layer (via `packages/crew/src/core/types.ts`, which
8
+ * re-exports it) and the studio SPA (via `packages/studio/src/api/types.ts`,
9
+ * likewise a re-export) — replacing the hand-copied mirror the studio used to
10
+ * carry, which could drift from the daemon silently (task #84).
11
+ *
12
+ * Rules of this package:
13
+ * - WIRE SHAPES ONLY. Engine-internal types (`LaunchRunInput`,
14
+ * `RepoOnboardRef`, runtime constants) stay in `packages/crew/src/core/types.ts`.
15
+ * - ZERO RUNTIME. This is a `.d.ts`-only package with no JavaScript; import it
16
+ * with `import type` exclusively. The `exports` map deliberately offers only
17
+ * a `types` condition, so a value import fails loudly at resolution time.
18
+ * - FORWARD-ADDITIVE. Optional/index-signature fields keep the shapes additive:
19
+ * a newer daemon that adds fields still parses in an older studio, and new
20
+ * CoreEvent variants pass through event switches untouched (DES-STUDIO-001
21
+ * §5.1). No `any` at the boundary — unknown-typed fields are narrowed at use.
22
+ *
23
+ * The shapes mirror wicked-core's serde representation (wicked-core/src/domain.rs,
24
+ * repo.rs, scope.rs) — the daemon passes most of them through verbatim.
25
+ * Drift guard: `packages/crew/tests/wire-contract.test.ts` fails to compile if
26
+ * the daemon's produced types stop satisfying this contract.
27
+ */
28
+
29
+ /** Run-level lifecycle status (`SessionStatus`, snake_case serde token). */
30
+ export type SessionStatus =
31
+ | 'planning'
32
+ | 'distributing'
33
+ | 'executing'
34
+ | 'awaiting_human'
35
+ | 'completed'
36
+ | 'cancelled'
37
+ | 'failed';
38
+
39
+ /** Per-unit lifecycle status (`UnitStatus`). */
40
+ export type UnitStatus = 'pending' | 'distributed' | 'done' | 'rejected';
41
+
42
+ /** The methodology stage badge on a unit (`StageKind`). */
43
+ export type StageKind = 'recon' | 'build' | 'review' | 'test';
44
+
45
+ /** Collection-scope mode (`EntityMode`). */
46
+ export type EntityMode = 'shared' | 'isolated';
47
+
48
+ /** Human-confirm gate policy. serde: `None`→"none", `All`→"all", `Before(n)`→{ before: n }. */
49
+ export type HumanConfirm = 'none' | 'all' | { before: number };
50
+
51
+ /** Why a CLI was assigned to a unit (`RoutingInfo`, internally tagged on `method`). */
52
+ export type RoutingInfo =
53
+ /**
54
+ * `seated` is the seats CONVENED — the denominator `returned` must be read against.
55
+ * Unknown on a run recorded by an engine older than the quorum fix; unknown means exactly that,
56
+ * never "equal to `returned`" (FINDING-026 D).
57
+ *
58
+ * `number | null` AND optional because unknown arrives in both shapes and neither is under this
59
+ * file's control: the engine serializes the artifact's `Option<u32>` as an explicit `null` (no
60
+ * `skip_serializing_if`, so the routing artifact and the event stream agree on one spelling of
61
+ * unknown), while a payload from before the field existed at all simply has no key. Read it with
62
+ * `== null`, which covers both — `=== undefined` silently misses the null case, which is the one
63
+ * the live API actually sends.
64
+ */
65
+ | {
66
+ method: 'council';
67
+ winner: string;
68
+ agreement_pct: number;
69
+ returned: number;
70
+ seated?: number | null;
71
+ dissent: number;
72
+ }
73
+ | { method: 'degraded'; reason: string }
74
+ | { method: 'evaluator_distinct'; winner: string; was: string }
75
+ | { method: 'tool' };
76
+
77
+ /** A run (`AgentSession`). */
78
+ export interface AgentSession {
79
+ id: string;
80
+ workflow_id: string;
81
+ problem: string;
82
+ entity_mode: EntityMode;
83
+ collection_scope: string | null;
84
+ clis: string[];
85
+ status: SessionStatus;
86
+ human_confirm: HumanConfirm;
87
+ unit_ix: number;
88
+ attempt: number;
89
+ workdir: string | null;
90
+ repo_ref: string | null;
91
+ }
92
+
93
+ /** An ordered unit of work within a run (`WorkUnit`). */
94
+ export interface WorkUnit {
95
+ id: string;
96
+ session_id: string;
97
+ ord: number;
98
+ description: string;
99
+ stage: StageKind;
100
+ assigned_cli: string | null;
101
+ assigned_invocation: string | null;
102
+ council_task_ref: string | null;
103
+ routing: RoutingInfo | null;
104
+ denial_reason: string | null;
105
+ phase_ref: string | null;
106
+ conformance_ref: string | null;
107
+ phase_status: string | null;
108
+ collection_scope: string | null;
109
+ status: UnitStatus;
110
+ skill_ref?: string | null;
111
+ /** The command this unit runs directly (Tool-executor phases). `null`/absent for Agent units. */
112
+ tool_cmd?: string[] | null;
113
+ /** Evaluator≠creator role. Present on def-driven units; absent on legacy/free-text. */
114
+ role?: PhaseRole;
115
+ /** Gate policy for this phase. Present on def-driven units; absent on legacy/free-text. */
116
+ gate?: GateSpec | string;
117
+ /** True when a pinned deterministic validator is attached to this unit. */
118
+ has_validator_pin?: boolean;
119
+ }
120
+
121
+ /** A run plus its ordered units (`SessionView`) — the shape `GET /runs` returns. */
122
+ export interface SessionView {
123
+ session: AgentSession;
124
+ units: WorkUnit[];
125
+ }
126
+
127
+ /** A registered git repository (`RepoEntry`). */
128
+ export interface RepoEntry {
129
+ id: string;
130
+ name: string;
131
+ root_path: string;
132
+ default_branch: string;
133
+ registered_at: number;
134
+ /** Remote git URL the repo was cloned from (undefined for locally-registered repos). */
135
+ git_url?: string;
136
+ /**
137
+ * ABSOLUTE path of this repo's code graph, resolved by the engine (wicked-core#170).
138
+ *
139
+ * Optional in the TYPE, mandatory in practice: an addon predating the field omits it, and
140
+ * `codeGraphDb()` in the daemon's `repoPaths.ts` is where that turns into a loud error. Never
141
+ * join this path yourself — six independent spellings is what FINDING-069 was.
142
+ */
143
+ code_graph_db?: string;
144
+ }
145
+
146
+ /** The run id of the onboarding run launched when a repo was registered (`GET /repos/:id/onboard`). */
147
+ export interface OnboardRef {
148
+ runId: string | null;
149
+ }
150
+
151
+ /**
152
+ * A council seat (`AgenticCli`) as returned by `GET /roster`. Only the fields
153
+ * the launch form uses are named; the index signature keeps the (large) rest of
154
+ * the seat intact so it round-trips verbatim into `clisJson` on launch.
155
+ */
156
+ export interface RosterSeat {
157
+ key: string;
158
+ display_name: string;
159
+ binary: string;
160
+ enabled_for_council: boolean;
161
+ category?: string;
162
+ [k: string]: unknown;
163
+ }
164
+
165
+ /** The daemon's cached open-gate record (`GET /runs/:id/gate`, DES-STUDIO-001 §3.3). */
166
+ export interface GateInfo {
167
+ runId: string;
168
+ ord: number;
169
+ prompt: string;
170
+ lifecycle: string;
171
+ receivedAt: string;
172
+ }
173
+
174
+ /** Approve / reject payload for the steering gate (`POST /runs/:id/gate`). */
175
+ export interface GateDecision {
176
+ approve: boolean;
177
+ amend?: string;
178
+ }
179
+
180
+ /**
181
+ * A CoreEvent frame as delivered verbatim over `/ws` — a tagged-JSON object
182
+ * discriminated on `type` (wicked-core-ts `event_to_json`). The named optional
183
+ * fields cover the frames the daemon and the studio inspect; the index
184
+ * signature keeps the shape additive-safe so new variants pass through
185
+ * untouched (DES-STUDIO-001 §2.1, §5.1).
186
+ */
187
+ export interface CoreEvent {
188
+ type: string;
189
+ session?: string;
190
+ ord?: number;
191
+ prompt?: string;
192
+ chunk?: string;
193
+ allow?: boolean;
194
+ cli?: string;
195
+ description?: string;
196
+ problem?: string;
197
+ message?: string;
198
+ /** `elicitationCreated` (DES-002): the id minted by the actor for this elicitation round. */
199
+ elicitationId?: string;
200
+ /** `elicitationCreated`: an ordered set of valid responses; `null` / absent = free-text. */
201
+ options?: string[] | null;
202
+ /** PTY terminal frames (`terminalOpened`/`terminalOutput`/`terminalExited`): the terminal id. */
203
+ id?: string;
204
+ /**
205
+ * A monotonic counter — but of WHAT depends on where the event came from, and the two never
206
+ * appear together, so there is exactly one right reading per event:
207
+ *
208
+ * - **live `/ws` frame** — `terminalOutput` only, and it counts within that one terminal
209
+ * (`actor.rs` `TerminalState::next_seq`). Two terminals both start at 0; comparing their `seq`
210
+ * values means nothing.
211
+ * - **entry from `runEvents`** — the durable log's own envelope counter, process-wide and stamped
212
+ * at emit (`event_log.rs` `SEQ`), which is what `read_run` sorts on. It totals-orders a run,
213
+ * which `ts` alone cannot: a burst from one actor turn shares a millisecond.
214
+ *
215
+ * They cannot collide, because `terminalOutput` is classed high-volume and is **never written to
216
+ * the log** — so no payload ever carries both meanings. What that does forbid is merging the two
217
+ * sources and sorting the result on this field: a live per-terminal 3 is not comparable to a
218
+ * replayed envelope 4700. Sort a replay by `seq`; order live frames by arrival, per terminal id.
219
+ */
220
+ seq?: number;
221
+ /** Capture time, epoch millis. Present ONLY on entries replayed from the durable event log
222
+ * (`runEvents` / `GET /runs/:id/events`); live `/ws` frames are not stamped, because for those
223
+ * the arrival IS the time. */
224
+ ts?: number;
225
+ /** `terminalOutput`: raw PTY bytes, base64-encoded (decode → send to the owning socket). */
226
+ bytesB64?: string;
227
+ /** `terminalOpened`: the working directory the PTY was opened in. */
228
+ cwd?: string;
229
+ // ── DES-STUDIO-COCKPIT-001 §3 B-events (Phase B insight wires) ──
230
+ /** `unitDispatched`/`cliUsage`: 0-based dispatch attempt (`>0` = a re-dispatch / rework). */
231
+ attempt?: number;
232
+ /** `cliUsage`: prompt/input tokens for the unit run. */
233
+ inputTokens?: number;
234
+ /** `cliUsage`: completion/output tokens for the unit run. */
235
+ outputTokens?: number;
236
+ /** `cliUsage`: dollar cost when the CLI reports it (claude) or a price table resolves it; else `null`. */
237
+ costUsd?: number | null;
238
+ /** `dataUsed`: the data files the unit's CLI touched (`tool_use` file paths). */
239
+ files?: string[];
240
+ /** `gateEvaluated`: the gated criterion — `null` when the phase was UNGATED (no deterministic floor). */
241
+ criterion?: string | null;
242
+ /** `gateEvaluated`: `true` iff a pinned validator gated this unit (else the phase is ungated). */
243
+ hasDeterministicFloor?: boolean;
244
+ /** `gateEvaluated`: whether the deterministic (layer-1) floor passed (vacuous when no floor). */
245
+ deterministicPass?: boolean;
246
+ /** `gateEvaluated`: the agent (layer-2) judge's verdict when one ran, else `null`. */
247
+ agentVerdict?: string | null;
248
+ /** `gateEvaluated`: the agent judge's reasoning when one ran, else `null`. */
249
+ agentReasoning?: string | null;
250
+ /** `gateEvaluated`: the evaluator≠creator second-pass result — `null` when that layer did not run. */
251
+ evaluatorPass?: boolean | null;
252
+ /**
253
+ * `gateEvaluated`: policy ids the second pass applied. EMPTY alongside `evaluatorPass: true`
254
+ * means nothing applied, so the pass is a vacuous default-allow rather than an enforced
255
+ * approval (FINDING-025). The layer-3 analogue of `hasDeterministicFloor`.
256
+ */
257
+ evaluatorPolicies?: string[];
258
+ /** `gateEvaluated`: the WINNING denial's reason when `combined === false`, else `null`. */
259
+ denialReason?: string | null;
260
+ /** `gateEvaluated`: the final deny-dominant decision over all layers (mirrors `gateDecided.allow`). */
261
+ combined?: boolean;
262
+ // sessionStarted enrichment fields (snake_case — serde wire names)
263
+ workflow_id?: string | null;
264
+ cli_count?: number;
265
+ governed?: boolean;
266
+ entity_mode?: string;
267
+ // workflowSelected (EVT-001) — camelCase per event_to_json
268
+ workflowId?: string;
269
+ unitCount?: number;
270
+ // unitReworkAmended (EVT-012)
271
+ amendment?: string;
272
+ updatedDescription?: string;
273
+ // unitOutputCaptured (EVT-013)
274
+ outputBytes?: number;
275
+ stepStatus?: 'ok' | 'failed' | 'cancelled';
276
+ // unitPlanned enrichment fields — the wire spelling is camelCase (event_to_json);
277
+ // the snake_case variants are kept for older daemons.
278
+ stage?: string;
279
+ skill_ref?: string | null;
280
+ skillRef?: string | null;
281
+ has_validator_pin?: boolean;
282
+ hasValidatorPin?: boolean;
283
+ executor_type?: string;
284
+ executorType?: string;
285
+ // unitDistributed enrichment fields
286
+ routing_method?: string;
287
+ agreement_pct?: number | null;
288
+ returned?: number | null;
289
+ dissent?: number | null;
290
+ degraded_reason?: string | null;
291
+ // councilConvened / councilDeliberated / councilVoted (live deliberation) — camelCase
292
+ // per event_to_json
293
+ clis?: string[];
294
+ consensus?: boolean;
295
+ agreementPct?: number;
296
+ votes?: number;
297
+ /** councilDeliberated: the completed ballot number (1-based). */
298
+ round?: number;
299
+ /** councilDeliberated: the approval bar the council must reach, as a percent. */
300
+ neededPct?: number;
301
+ /**
302
+ * councilSeatFailed: the seat's exit code, when it ran far enough to have one.
303
+ *
304
+ * The branch itself arrives on the shared `kind` field — `spawn_failed`, `non_zero_exit`,
305
+ * `timed_out`, `pty_unsupported`, `invocation_empty`, `workdir_unavailable`,
306
+ * `prompt_write_failed`, `wait_failed`, `unreported`. Every seat that does not vote reports
307
+ * one; without it the council's quorum shrinks silently.
308
+ */
309
+ exitCode?: number | null;
310
+ /** councilSeatFailed: bounded capture of what the seat wrote to stderr (≤4 KiB). */
311
+ stderr?: string;
312
+ /** councilSeatFailed: how long the seat burned before failing — separates a spawn error from a timeout. */
313
+ latencyMs?: number;
314
+ /** workerStalled: silent seconds before the stall event fired. */
315
+ stalledSecs?: number;
316
+ /** failureTriaged: the triage judge's decision. */
317
+ decision?: string;
318
+ /** failureTriaged: the judge's bounded reasoning. */
319
+ analysis?: string;
320
+ // assumptionRecorded (external-transform convention) — camelCase per event_to_json
321
+ kind?: string;
322
+ library?: string;
323
+ transform?: string;
324
+ known?: boolean;
325
+ detail?: string;
326
+ [k: string]: unknown;
327
+ }
328
+
329
+ /**
330
+ * A frame read back from core's DURABLE per-run event log (`GET /runs/:id/events`):
331
+ * the same tagged object the live stream carries, plus the two fields the log
332
+ * adds when it records one.
333
+ *
334
+ * Same shape as the live frame on purpose — one mapping in core
335
+ * (`CoreEvent::to_json`) serializes both, so an event named here is the event
336
+ * named live, and a bundle assembled after a run cannot describe it in different
337
+ * words than the operator watched it happen in. (FINDING-014)
338
+ */
339
+ export interface RecordedEvent extends CoreEvent {
340
+ /** Capture time, epoch millis — when core emitted the frame, not when it was read. */
341
+ ts: number;
342
+ /**
343
+ * Monotonic ordering across THIS RUN's whole event trail, so frames sharing a `ts` are still
344
+ * ordered.
345
+ *
346
+ * Not the same `seq` as `CoreEvent`'s, which counts chunks within one terminal's output. This one
347
+ * is required and spans every frame of the run; that one is optional and scoped to a stream. The
348
+ * name is inherited from the wire and narrowed here rather than renamed, because the wire is what
349
+ * `runEvents` returns.
350
+ */
351
+ seq: number;
352
+ }
353
+
354
+ /**
355
+ * DES-STUDIO-COCKPIT-001 §3 B-events — the 4 tagged-JSON insight frames, as a discriminated
356
+ * union for consumers that narrow on `type`. Each mirrors a wicked-core `CoreEvent` variant
357
+ * (`event_to_json`, camelCase). They also flow through the permissive {@link CoreEvent} above; the
358
+ * union just gives Phase-B panels exact field types.
359
+ */
360
+ export type InsightEvent =
361
+ | UnitDispatchedEvent
362
+ | CliUsageEvent
363
+ | DataUsedEvent
364
+ | GateEvaluatedEvent;
365
+
366
+ /** §3 B2 — a unit was dispatched (initial + each re-dispatch); `attempt>0` = rework. */
367
+ export interface UnitDispatchedEvent {
368
+ type: 'unitDispatched';
369
+ session: string;
370
+ ord: number;
371
+ attempt: number;
372
+ }
373
+
374
+ /** §3 B3 — token/cost burn for one unit run. `costUsd` is `null` when no cost is known. */
375
+ export interface CliUsageEvent {
376
+ type: 'cliUsage';
377
+ session: string;
378
+ ord: number;
379
+ attempt: number;
380
+ inputTokens: number;
381
+ outputTokens: number;
382
+ costUsd: number | null;
383
+ }
384
+
385
+ /** §3 B4 — the data files a unit's CLI touched. */
386
+ export interface DataUsedEvent {
387
+ type: 'dataUsed';
388
+ session: string;
389
+ ord: number;
390
+ files: string[];
391
+ }
392
+
393
+ /** §3 B1 — the gate's decision depth, emitted alongside `gateDecided`. */
394
+ export interface GateEvaluatedEvent {
395
+ type: 'gateEvaluated';
396
+ session: string;
397
+ ord: number;
398
+ criterion: string | null;
399
+ hasDeterministicFloor: boolean;
400
+ deterministicPass: boolean;
401
+ agentVerdict: string | null;
402
+ agentReasoning: string | null;
403
+ evaluatorPass: boolean | null;
404
+ denialReason: string | null;
405
+ combined: boolean;
406
+ }
407
+
408
+ /** Foundation wave: session started with enriched context. */
409
+ export interface SessionStartedEvent {
410
+ type: 'sessionStarted';
411
+ session: string;
412
+ problem: string;
413
+ workflow_id: string | null;
414
+ cli_count: number;
415
+ governed: boolean;
416
+ entity_mode: 'shared' | 'isolated';
417
+ }
418
+
419
+ // ── P1 observability events ─────────────────────────────────────────────────
420
+
421
+ /** P1 — a worker failure halted this unit; `detail` is a bounded excerpt of raw output. */
422
+ export interface StepFailedEvent {
423
+ type: 'stepFailed';
424
+ session: string;
425
+ ord: number;
426
+ attempt: number;
427
+ detail: string;
428
+ failureKind: string;
429
+ }
430
+
431
+ /** P1 — the engine restarted and is re-dispatching this unit; `attempt` is the NEW attempt number. */
432
+ export interface CrashRecoveryRedriveEvent {
433
+ type: 'crashRecoveryRedrive';
434
+ session: string;
435
+ ord: number;
436
+ attempt: number;
437
+ }
438
+
439
+ /** P1 — a PTY worker session opened for the run; `terminalId` matches the terminal event stream. */
440
+ export interface WorkerSessionStartedEvent {
441
+ type: 'workerSessionStarted';
442
+ session: string;
443
+ terminalId: string;
444
+ cliKey: string;
445
+ }
446
+
447
+ /** P1 — an ACP persistent session opened for a CLI; `acpSessionId` is the protocol session handle. */
448
+ export interface AcpSessionStartedEvent {
449
+ type: 'acpSessionStarted';
450
+ session: string;
451
+ cliKey: string;
452
+ acpSessionId: string;
453
+ }
454
+
455
+ /** P1 — ACP unavailable or failed for a CLI; the run continues with single-shot fallback. */
456
+ export interface AcpFallbackEvent {
457
+ type: 'acpFallback';
458
+ session: string;
459
+ cliKey: string;
460
+ reason: string;
461
+ fallbackKind: string;
462
+ }
463
+
464
+ // ── P2 observability events ─────────────────────────────────────────────────
465
+
466
+ /**
467
+ * P2 — one prior unit's context contributed to a cross-CLI injection.
468
+ * Mirrors `wicked_core::InjectedContext`: identity + size only, no raw content.
469
+ */
470
+ export interface InjectedContextRecord {
471
+ ord: number;
472
+ label: string;
473
+ outputBytes: number;
474
+ }
475
+
476
+ /** P2 — an existing PTY session was reused for a subsequent unit in the same run (EVT-003). */
477
+ export interface WorkerSessionReusedEvent {
478
+ type: 'workerSessionReused';
479
+ session: string;
480
+ terminalId: string;
481
+ ord: number;
482
+ }
483
+
484
+ /**
485
+ * P2 — a PTY worker session closed (EVT-004).
486
+ * `reason`: `"run_complete"` (normal end-of-run) | `"error"` (PTY write failure).
487
+ */
488
+ export interface WorkerSessionClosedEvent {
489
+ type: 'workerSessionClosed';
490
+ session: string;
491
+ terminalId: string;
492
+ reason: 'run_complete' | 'error';
493
+ }
494
+
495
+ /**
496
+ * P2 — prior cross-CLI unit outputs were injected into a unit's context before dispatch (EVT-007).
497
+ * Carries identity + byte-size only — raw output content is not included.
498
+ */
499
+ export interface UnitContextInjectedEvent {
500
+ type: 'unitContextInjected';
501
+ session: string;
502
+ ord: number;
503
+ recipientCli: string;
504
+ priorUnits: InjectedContextRecord[];
505
+ }
506
+
507
+ // ── P2 governance-deep observability events (wicked-core#89) ────────────────
508
+
509
+ /** P2 — governance hook fired for a tool call; records the per-call allow/deny decision. */
510
+ export interface GovernanceHookFiredEvent {
511
+ type: 'governanceHookFired';
512
+ session: string;
513
+ ord: number;
514
+ attempt: number;
515
+ toolName: string;
516
+ decision: 'allow' | 'deny';
517
+ denyingPolicy: string | null;
518
+ }
519
+
520
+ /** P2 — a pinned deterministic validator was attached; confirms the governance floor is armed. */
521
+ export interface ValidationPinAttachedEvent {
522
+ type: 'validationPinAttached';
523
+ session: string;
524
+ ord: number;
525
+ pin: string;
526
+ criterion: string;
527
+ }
528
+
529
+ /** P2 — a HumanConfirmIf gate escalated to human review. */
530
+ export interface GateEscalatedEvent {
531
+ type: 'gateEscalated';
532
+ session: string;
533
+ ord: number;
534
+ condition: string;
535
+ verdictSummary: string;
536
+ }
537
+
538
+ /** P2 — a tool-executor command was dispatched (non-agent unit). */
539
+ export interface ToolExecutorDispatchedEvent {
540
+ type: 'toolExecutorDispatched';
541
+ session: string;
542
+ ord: number;
543
+ cmd: string[];
544
+ workdir: string | null;
545
+ }
546
+
547
+ /** P2 — governance context was confirmed armed for a unit. */
548
+ export interface GovernanceContextArmedEvent {
549
+ type: 'governanceContextArmed';
550
+ session: string;
551
+ ord: number;
552
+ attempt: number;
553
+ path: 'wrapped_cli' | 'acp';
554
+ dbPath: string;
555
+ }
556
+
557
+ /** P2 — a unit the workflow declared GOVERNED ran with its tool calls unchecked, because the CLI it
558
+ * was routed to has no gate-hook adapter (injection is claude-only). Distinct from a unit that was
559
+ * never governed at all: both report `governed: false` on unitOutputCaptured, and only this event
560
+ * separates "not asked for" from "asked for and not applied" (FINDING-063). */
561
+ export interface GovernanceUnenforcedEvent {
562
+ type: 'governanceUnenforced';
563
+ session: string;
564
+ ord: number;
565
+ attempt: number;
566
+ cli: string;
567
+ reason: string;
568
+ }
569
+
570
+ // ── P2 decisions-full observability events (wicked-core EVT-001/012/013) ────
571
+
572
+ /** P2 — a structured workflow def was selected; fires once per session, after SessionStarted and before
573
+ * the first UnitPlanned. Not emitted for free-text runs. `unitCount` is the number of phases. */
574
+ export interface WorkflowSelectedEvent {
575
+ type: 'workflowSelected';
576
+ session: string;
577
+ workflowId: string;
578
+ unitCount: number;
579
+ }
580
+
581
+ /** P2 — a human approved a gate with an amendment text that was injected into the unit's description
582
+ * before re-dispatch. Fires after the amendment is persisted, before Resumed. Only emitted when
583
+ * the amendment text is non-empty. The canonical record for the HITL paper trail. */
584
+ export interface UnitReworkAmendedEvent {
585
+ type: 'unitReworkAmended';
586
+ session: string;
587
+ ord: number;
588
+ /** The raw amendment text supplied by the operator. */
589
+ amendment: string;
590
+ /** The unit's description after the amendment was injected. */
591
+ updatedDescription: string;
592
+ }
593
+
594
+ /** P2 — a worker's ApplyStepResult arrived and output is ready to be gated. Fires before GateDecided.
595
+ * `outputBytes` is the byte length of the worker's output; `stepStatus` is "ok"/"failed"/"cancelled";
596
+ * `governed` reflects whether the runner armed input governance for this unit. */
597
+ export interface UnitOutputCapturedEvent {
598
+ type: 'unitOutputCaptured';
599
+ session: string;
600
+ ord: number;
601
+ attempt: number;
602
+ /** Byte length of the worker's raw output — distinguishes 0-byte from 8 MB truncated. */
603
+ outputBytes: number;
604
+ stepStatus: 'ok' | 'failed' | 'cancelled';
605
+ governed: boolean;
606
+ }
607
+
608
+ /** Foundation wave: a unit was planned with full phase metadata. */
609
+ export interface UnitPlannedEvent {
610
+ type: 'unitPlanned';
611
+ session: string;
612
+ ord: number;
613
+ description: string;
614
+ stage: StageKind;
615
+ role: PhaseRole;
616
+ gate: 'auto' | 'human_confirm' | 'human_confirm_if';
617
+ skill_ref: string | null;
618
+ has_validator_pin: boolean;
619
+ executor_type: 'agent' | 'tool';
620
+ }
621
+
622
+ /** Foundation wave: a unit was distributed with full routing detail. */
623
+ export interface UnitDistributedEvent {
624
+ type: 'unitDistributed';
625
+ session: string;
626
+ ord: number;
627
+ cli: string;
628
+ routing_method: 'council' | 'degraded' | 'evaluator_distinct' | 'tool';
629
+ agreement_pct: number | null;
630
+ returned: number | null;
631
+ dissent: number | null;
632
+ degraded_reason: string | null;
633
+ }
634
+
635
+ // ── Worker injection + reassignment events (core#93) ──────────────────────────
636
+
637
+ /** An operator message was injected into active worker sessions mid-run. */
638
+ export interface WorkerMessageInjectedEvent {
639
+ type: 'workerMessageInjected';
640
+ session: string;
641
+ message: string;
642
+ /** `"all"` or the cli_key that was targeted. */
643
+ target: string;
644
+ }
645
+
646
+ /** A unit was stopped and re-dispatched to a different CLI (or re-routed via council). */
647
+ export interface UnitReassignedEvent {
648
+ type: 'unitReassigned';
649
+ session: string;
650
+ ord: number;
651
+ attempt: number;
652
+ previousCli: string;
653
+ /** `null` means the council was re-convened and its choice is the new assignment. */
654
+ newCli: string | null;
655
+ }
656
+
657
+ // ── Governance types (crew#40/42/43) ───────────────────────────────────────────
658
+
659
+ /**
660
+ * A registered governance policy (`wicked-governance::Policy`).
661
+ * `effect`: `deny` | `allow_with_conditions` | `allow`.
662
+ * `severity`: `high` | `medium` | `low`.
663
+ */
664
+ export interface GovernancePolicy {
665
+ id: string;
666
+ kind: string;
667
+ applies_to: string[];
668
+ effect: 'deny' | 'allow_with_conditions' | 'allow';
669
+ trigger: { contains?: string };
670
+ obligations: string[];
671
+ criteria: string;
672
+ severity: 'high' | 'medium' | 'low';
673
+ rule: string;
674
+ /**
675
+ * Withdrawn from enforcement (FINDING-038). Still listed — the record survives so past decisions
676
+ * citing it stay explicable — but it can no longer decide a gate. Absent on policies written
677
+ * before the field existed, which read as active.
678
+ */
679
+ retired?: boolean;
680
+ }
681
+
682
+ /**
683
+ * A prescriptive conformance rule (`wicked-governance::ConformanceRule`).
684
+ * `rule_type`: `pattern` | `policy`. `severity`: `info` | `warn` | `error` | `critical`.
685
+ */
686
+ export interface ConformanceRule {
687
+ id: string;
688
+ rule_type: 'pattern' | 'policy';
689
+ statement: string;
690
+ severity: 'info' | 'warn' | 'error' | 'critical';
691
+ confidence: number;
692
+ targets: { language?: string; layer?: string; framework?: string };
693
+ symbol_ref?: string;
694
+ compliance?: { framework: string; control_id: string };
695
+ provenance: { source: string; ref?: string; source_kinds: string[] };
696
+ /** Withdrawn from recall. Same contract as {@link GovernancePolicy.retired}. */
697
+ retired?: boolean;
698
+ }
699
+
700
+ /** Facet query for `GET /governance/rules/preview`. All fields are optional. */
701
+ export interface RulePreviewQuery {
702
+ language?: string;
703
+ layer?: string;
704
+ framework?: string;
705
+ severity?: string;
706
+ rule_type?: string;
707
+ }
708
+
709
+ // ── Governance claims (crew#40/43) ─────────────────────────────────────────────
710
+
711
+ /**
712
+ * A recorded governance decision from the conformance store (`wicked-apps-core::ConformanceClaim`).
713
+ * `decision` values: `allow` | `deny` | `allow_with_conditions`.
714
+ */
715
+ export interface GovernanceClaim {
716
+ claim_id: string;
717
+ scope: string;
718
+ phase: string;
719
+ policy_ids: string[];
720
+ decision: 'allow' | 'deny' | 'allow_with_conditions';
721
+ obligations: string[];
722
+ evaluated_context_ref: string;
723
+ criteria: string;
724
+ evaluator_identity: string;
725
+ /** Unix-seconds timestamp. */
726
+ evaluated_at: number;
727
+ }
728
+
729
+ /** The launch-run request body (`POST /runs`). */
730
+ export interface LaunchRunBody {
731
+ problem: string;
732
+ sessionId?: string;
733
+ clisJson?: string;
734
+ entityMode?: EntityMode;
735
+ humanConfirm?: string;
736
+ repoRef?: string;
737
+ /** Built-in workflow id (`feature` | `bug` | `migration`); omit for free-text single-unit mode. */
738
+ workflow?: string;
739
+ /**
740
+ * The project to file this run into (DES-PROJECT-001 §2.2). The `crew.run` membership is
741
+ * attached atomically with the launch record; an unknown or archived project fails the launch
742
+ * (4xx) — never a silent unfiled run. Omit for an unfiled run (the synthesized `default`).
743
+ */
744
+ projectId?: string;
745
+ }
746
+
747
+ // ── Governance types (crew#40/41) ──────────────────────────────────────────────
748
+
749
+ /** Per-app breakdown within a `CoverageReport`. */
750
+ export interface CoveragePerApp {
751
+ app: string;
752
+ behavior_bearing: number;
753
+ resolved: number;
754
+ risk_flagged: number;
755
+ unaccounted: number;
756
+ coverage: number;
757
+ }
758
+
759
+ /** A behavior-bearing node without a coverage annotation (a coverage hole). */
760
+ export interface UnaccountedNode {
761
+ symbol_id: string;
762
+ name?: string;
763
+ kind?: string;
764
+ file?: string;
765
+ app?: string;
766
+ }
767
+
768
+ /**
769
+ * Front-half coverage gate report (`wicked-governance::CoverageReport`).
770
+ * `null` when the graph store has no nodes.
771
+ */
772
+ export interface CoverageReport {
773
+ total: number;
774
+ behavior_bearing: number;
775
+ resolved: number;
776
+ risk_flagged: number;
777
+ unaccounted: number;
778
+ coverage: number;
779
+ resolved_rate: number;
780
+ mean_confidence: number;
781
+ resolve_threshold: number;
782
+ per_app: CoveragePerApp[];
783
+ unaccounted_nodes: UnaccountedNode[];
784
+ }
785
+
786
+ /** One entry of a repo's code-graph summary — a node kind and how many the graph holds (#122). */
787
+ export interface GraphKind {
788
+ kind: string;
789
+ count: number;
790
+ }
791
+
792
+ // ── Workflow viewer + domain-model browser types (crew#44) ──────────────────
793
+
794
+ /** Gate position in the value→strategy→execution ladder. */
795
+ export type GateType = 'value' | 'strategy' | 'execution';
796
+
797
+ /** Human-confirm spec for a phase gate (serde flattened from Rust enum). */
798
+ export type GateSpec =
799
+ | 'auto'
800
+ | { human_confirm: { unconditional: boolean } }
801
+ | { human_confirm_if: 'verdict_not_pass' };
802
+
803
+ /** Evaluator≠creator role for a phase. */
804
+ export type PhaseRole = 'neutral' | 'creator' | 'evaluator';
805
+
806
+ /** Methodology stage for a phase (same tokens as {@link StageKind}). */
807
+ export type StageKindPhase = 'recon' | 'build' | 'review' | 'test';
808
+
809
+ /** How a phase executes — agent (council-routed CLI) or tool (direct command). */
810
+ export type PhaseExecutor =
811
+ | { type: 'agent' }
812
+ | { type: 'tool'; cmd: string[] };
813
+
814
+ /** One ordered phase of a workflow — pure data. */
815
+ export interface PhaseDef {
816
+ id: string;
817
+ kind: StageKindPhase;
818
+ /** How the phase executes. Omitted = Agent (engine default). Tool phases bypass the council and
819
+ * run `cmd` directly. */
820
+ executor?: PhaseExecutor;
821
+ /** Per-phase agent instructions folded into the unit prompt by the engine (core PhaseDef.instructions,
822
+ * Option<String>). Absent = no extra instruction. Mirrors of core drop-ins that carry it (e.g.
823
+ * survey-repo) MUST reproduce it verbatim or the runtime def diverges from core's — FINDING-011. */
824
+ instructions?: string | null;
825
+ gate_type: GateType | null;
826
+ gate: GateSpec;
827
+ executes_code: boolean;
828
+ verified_evidence: boolean;
829
+ required_deliverables: string[];
830
+ depends_on: string[];
831
+ role: PhaseRole;
832
+ skill_ref: string | null;
833
+ allowed_skills: string[];
834
+ validator_pin: string | null;
835
+ }
836
+
837
+ /** A workflow — id + ordered phases. */
838
+ export interface WorkflowDef {
839
+ id: string;
840
+ phases: PhaseDef[];
841
+ /** True for built-in workflows that have dedicated entry points and must not appear in the work-mode selector. */
842
+ is_system?: boolean;
843
+ }
844
+
845
+ /** Top-level requirements_graph.json artifact (schema 1.0.0). */
846
+ export interface DomainGraph {
847
+ metadata: { schema_version: string; migration_mode: string; source?: string };
848
+ domains: Record<string, DomainGraphDomain>;
849
+ }
850
+
851
+ /** A capability domain in the requirements graph. */
852
+ export interface DomainGraphDomain {
853
+ description?: string;
854
+ cluster_id?: number;
855
+ requirements: Record<string, DomainGraphRequirement>;
856
+ entities: Record<string, { description?: string }>;
857
+ }
858
+
859
+ /** A requirement in a domain. */
860
+ export interface DomainGraphRequirement {
861
+ title: string;
862
+ description: string;
863
+ status?: string;
864
+ disposition?: string;
865
+ business_rules: Array<{ id: string; statement: string; confidence: number; provenance: { source: string } }>;
866
+ validations: Array<{ id: string; statement: string; confidence?: number }>;
867
+ error_paths: Array<{ id: string; statement: string }>;
868
+ }
869
+
870
+ /** Coverage stats from `wicked-core coverage --json` — present when code is indexed but domain not yet annotated. */
871
+ export interface DomainCoverage {
872
+ coverage: number;
873
+ total: number;
874
+ behavior_bearing: number;
875
+ resolved: number;
876
+ }
877
+
878
+ /** The open-terminal request body (`POST /terminals`, DES-TERMINAL-001 §6). */
879
+ export interface OpenTerminalBody {
880
+ /** Working directory the PTY opens in. */
881
+ cwd: string;
882
+ /** Command to run; omit for the user's login shell. */
883
+ cmd?: string[];
884
+ cols: number;
885
+ rows: number;
886
+ /**
887
+ * Omit for the safe governed default; `false` is the loud, opt-in UNGOVERNED
888
+ * operator shell (surfaced as ungoverned in the UI, DES-TERMINAL-001 §7).
889
+ */
890
+ governed?: boolean;
891
+ }
892
+
893
+ /** Symbol-level code graph from estate (GET /repos/:id/graph). */
894
+ export interface CodeGraphNode {
895
+ id: string; // estate symbol ID (primary key)
896
+ name: string; // short display name (function/class/type name)
897
+ kind: string; // function | class | struct | interface | type_alias | method | enum
898
+ file: string; // source file path
899
+ inDeg: number; // incoming call/import count (hotspot indicator)
900
+ outDeg: number; // outgoing call/import count
901
+ lang: string; // language (typescript, rust, python, etc.)
902
+ }
903
+ export interface CodeGraphEdge { src: string; tgt: string; }
904
+ export interface CodeGraphData {
905
+ nodes: CodeGraphNode[];
906
+ edges: CodeGraphEdge[];
907
+ stats: { nodeCount: number; edgeCount: number; fileCount: number };
908
+ }
909
+
910
+ export interface GitCommit {
911
+ sha: string;
912
+ shortSha: string;
913
+ message: string;
914
+ author: string;
915
+ date: string;
916
+ }
917
+
918
+ export interface GitContributor {
919
+ commits: number;
920
+ name: string;
921
+ email: string;
922
+ }
923
+
924
+ /** Daemon-persisted system settings (~/.config/wicked-core/settings.json). */
925
+ export interface SystemSettings {
926
+ /** Max nodes returned by wicked-estate graph-view (default 150). */
927
+ graphNodeLimit: number;
928
+ }
929
+
930
+ // ── Requirements management (server-side search + overrides; crew api/requirements.ts) ──
931
+ export interface RequirementSummary {
932
+ key: string;
933
+ domain: string;
934
+ reqId: string;
935
+ title: string;
936
+ category: 'functional' | 'config-data';
937
+ statement: string;
938
+ status: string;
939
+ risk: boolean;
940
+ riskSource: 'operator' | 'data' | null;
941
+ edited: boolean;
942
+ }
943
+
944
+ export interface RequirementDetail extends RequirementSummary {
945
+ description: string;
946
+ notes: string;
947
+ sourceTitle: string;
948
+ ruleCount: number;
949
+ componentCount: number;
950
+ validationCount: number;
951
+ errorPathCount: number;
952
+ businessRules: unknown[];
953
+ legacyComponents: unknown[];
954
+ }
955
+
956
+ export interface RequirementsPage {
957
+ total: number;
958
+ corpus: number;
959
+ offset: number;
960
+ limit: number;
961
+ items: RequirementSummary[];
962
+ /** Which source served the corpus: the live estate store, or the evidence-gated
963
+ * `requirements_graph.json` snapshot, which can lag it by hours (FINDING-065). */
964
+ source: 'store' | 'artifact';
965
+ }
966
+
967
+ export interface RequirementPatch {
968
+ title?: string;
969
+ notes?: string;
970
+ status?: 'active' | 'deprecated' | 'review';
971
+ risk?: boolean;
972
+ }
973
+
974
+ // ── Blast radius (wicked-estate blast-radius --json via crew) ──
975
+ export interface BlastRadius {
976
+ target: string;
977
+ dependents: Array<{ id: string; name: string; kind: string; file: string; line: number }>;
978
+ /** Unresolved call-sites referencing the target — absence of dependents never means "safe". */
979
+ unresolved: number;
980
+ }
981
+
982
+ /** An open elicitation as `GET /runs/:id/elicitation` returns it (DES-002). */
983
+ export interface ElicitationInfo {
984
+ runId: string;
985
+ elicitationId: string;
986
+ message: string;
987
+ /** Ordered set of valid responses; `null` means free-text. */
988
+ options: string[] | null;
989
+ receivedAt: string;
990
+ }
991
+
992
+ /** `POST /runs/:id/elicitation` body. `content` is required for accept, absent otherwise. */
993
+ export interface ElicitationResponse {
994
+ elicitationId: string;
995
+ action: 'accept' | 'decline' | 'cancel';
996
+ content?: { response: string };
997
+ }
998
+
999
+ // ── Projects (DES-PROJECT-001) — the experience-plane keystone ─────────────────
1000
+
1001
+ /** Project lifecycle: `active ⇄ archived`, no hard delete (ADR §1.3). */
1002
+ export type ProjectLifecycle = 'active' | 'archived';
1003
+
1004
+ /**
1005
+ * A named, control-plane-owned container whose members are work units from any
1006
+ * plane (`GET /projects`, `GET /projects/:id`). Mirrors wicked-core's serde
1007
+ * shape (`project.rs`) — Rust `Option` fields arrive as `null`, never absent.
1008
+ * The synthesized `default` project ("Unfiled") appears in lists with
1009
+ * `id: "default"`, `scope: ""`, and zero timestamps; it rejects PATCH/attach.
1010
+ */
1011
+ export interface Project {
1012
+ /** `proj_<sortable>` — minted by control at create; or the reserved `default`. */
1013
+ id: string;
1014
+ name: string;
1015
+ description: string | null;
1016
+ status: ProjectLifecycle;
1017
+ /** The estate scope path of this project's record (`project:<id>`; `""` for `default`). */
1018
+ scope: string;
1019
+ /** Unix millis. */
1020
+ created_at: number;
1021
+ /** Unix millis. */
1022
+ updated_at: number;
1023
+ [k: string]: unknown;
1024
+ }
1025
+
1026
+ /**
1027
+ * A typed, opaque membership reference (ADR §1.2). `member_kind` is the open
1028
+ * `<product>.<noun>` grammar (`crew.run`, `crew.chat`, `crew.repo`,
1029
+ * `crew.workflow`, `interactive.doc`, reserved `studio.session`).
1030
+ */
1031
+ export interface ProjectMember {
1032
+ /** Derived from `(project_id, member_kind, member_ref)` — the UNIQUE constraint. */
1033
+ id: string;
1034
+ project_id: string;
1035
+ member_kind: string;
1036
+ /** Opaque to the engine; `crew.*` refs are checked at the API layer at attach time. */
1037
+ member_ref: string;
1038
+ /** Skin hints (doc root, display title, …) as JSON text; `null` when none. */
1039
+ meta: string | null;
1040
+ /** Unix millis. */
1041
+ attached_at: number;
1042
+ /** The attaching surface: `studio` | `interactive` | `cli` | `api`. */
1043
+ attached_by: string;
1044
+ [k: string]: unknown;
1045
+ }
1046
+
1047
+ /** `GET /projects/:id` — detail + members (ADR §5.2). */
1048
+ export interface ProjectDetail {
1049
+ project: Project;
1050
+ members: ProjectMember[];
1051
+ }
1052
+
1053
+ /** `POST /projects` body. */
1054
+ export interface CreateProjectBody {
1055
+ /** 1–120 chars; unique among ACTIVE projects (409 on collision). */
1056
+ name: string;
1057
+ description?: string;
1058
+ }
1059
+
1060
+ /** `PATCH /projects/:id` body — rename / describe / archive / restore. */
1061
+ export interface UpdateProjectBody {
1062
+ name?: string;
1063
+ /** `""` clears the description. */
1064
+ description?: string;
1065
+ status?: ProjectLifecycle;
1066
+ }
1067
+
1068
+ /** `POST /projects/:id/members` body. */
1069
+ export interface AttachMemberBody {
1070
+ /** `<product>.<noun>` (open grammar). */
1071
+ kind: string;
1072
+ ref: string;
1073
+ /** Skin hints, carried opaquely. */
1074
+ meta?: Record<string, unknown>;
1075
+ /** The attaching surface; defaults to `api`. */
1076
+ attachedBy?: 'studio' | 'interactive' | 'cli' | 'api';
1077
+ }
1078
+
1079
+ /**
1080
+ * One normalized entry of the merged project activity feed
1081
+ * (`GET /projects/:id/activity`, ADR §5.2): core events of member runs/chats
1082
+ * ∪ bus `wicked.interactive.*` events carrying this `project_id`.
1083
+ */
1084
+ export interface ActivityEntry {
1085
+ /** Stable entry id (`crew:<run>:<seq>` | `bus:<event_id>`) — the cursor tiebreaker. */
1086
+ id: string;
1087
+ /** Unix millis. */
1088
+ ts: number;
1089
+ source: 'crew' | 'interactive';
1090
+ /** The event type (`awaitingHuman`, `wicked.interactive.version.created`, …). */
1091
+ kind: string;
1092
+ /** The member the entry belongs to (run id, doc name). */
1093
+ ref: string;
1094
+ summary: string;
1095
+ /** The original frame/payload, verbatim. */
1096
+ raw: unknown;
1097
+ }
1098
+
1099
+ /** `GET /projects/:id/activity` — newest-first, cursor-paginated on `(ts, id)`. */
1100
+ export interface ActivityPage {
1101
+ projectId: string;
1102
+ entries: ActivityEntry[];
1103
+ /** Opaque; pass back as `?cursor=`. `null` ⇒ no older entries. */
1104
+ nextCursor: string | null;
1105
+ }
1106
+
1107
+ /**
1108
+ * A durable interaction request (DES-PROJECT-001 §5.3) as the engine persists
1109
+ * it — written in the same transaction as the run's `awaiting_human`
1110
+ * transition, resolved in the same transaction as the gate decision. The
1111
+ * prompt inbox (`GET /projects/:id/prompts`) returns the OPEN ones across the
1112
+ * project's member runs.
1113
+ */
1114
+ export interface InteractionRequest {
1115
+ /** `ir_<derived>` — stable across re-pauses of the same `(run, kind, ord)`. */
1116
+ id: string;
1117
+ /** The owning run. */
1118
+ session_id: string;
1119
+ kind: 'gate' | 'elicitation';
1120
+ /** The unit ordinal the gate pauses before; `null` when not unit-bound. */
1121
+ ord: number | null;
1122
+ /** The already-run unit the gate reviews, when attributable. */
1123
+ reviewing_ord: number | null;
1124
+ prompt: string;
1125
+ status: 'open' | 'answered' | 'expired' | 'cancelled';
1126
+ /** The decision payload (JSON text, e.g. `{"approve":true,"amend":null}`) once resolved. */
1127
+ answer: string | null;
1128
+ /** Unix millis. */
1129
+ created_at: number;
1130
+ /** Unix millis; `null` while open. */
1131
+ resolved_at: number | null;
1132
+ [k: string]: unknown;
1133
+ }
1134
+
1135
+ /** `GET /projects/:id/prompts` — the open prompt inbox across member runs. */
1136
+ export interface ProjectPrompts {
1137
+ projectId: string;
1138
+ prompts: InteractionRequest[];
1139
+ }
1140
+
1141
+ /** `POST /chats` body (gains `projectId` with DES-PROJECT-001). */
1142
+ export interface ChatOpenBody {
1143
+ chatId?: string;
1144
+ clis?: string[];
1145
+ repoRef?: string;
1146
+ /** File the chat into a project (`crew.chat` membership, attached on open). */
1147
+ projectId?: string;
1148
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "wicked-crew-api-types",
3
+ "version": "0.1.0",
4
+ "description": "The wire contract of the wicked-crew daemon's /api/v1 REST surface and /ws CoreEvent frames — types only, zero runtime",
5
+ "type": "module",
6
+ "types": "./index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./index.d.ts"
10
+ }
11
+ },
12
+ "files": [
13
+ "index.d.ts"
14
+ ],
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/mikeparcewski/wicked-crew.git",
18
+ "directory": "packages/crew-api-types"
19
+ },
20
+ "homepage": "https://github.com/mikeparcewski/wicked-crew/tree/main/packages/crew-api-types#readme",
21
+ "bugs": {
22
+ "url": "https://github.com/mikeparcewski/wicked-crew/issues"
23
+ },
24
+ "keywords": [
25
+ "wicked-crew",
26
+ "api-types",
27
+ "wire-contract",
28
+ "typescript"
29
+ ],
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "scripts": {
34
+ "typecheck": "tsc -p tsconfig.json"
35
+ },
36
+ "devDependencies": {
37
+ "typescript": "^5.5.0"
38
+ },
39
+ "engines": {
40
+ "node": ">=22.0.0"
41
+ },
42
+ "license": "MIT"
43
+ }