wicked-crew-api-types 0.18.0 → 0.20.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 (2) hide show
  1. package/index.d.ts +173 -19
  2. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -197,9 +197,14 @@ export interface AgentSession {
197
197
  *
198
198
  * - `'delivered'` — a PR was opened for this run (by the deliver phase, or post-hoc via
199
199
  * `POST /runs/:id/deliver`); `deliverUrl` carries the PR URL.
200
- * - `'stranded'` — a COMPLETED repo-scoped run with no recorded PR whose worktree still
201
- * exists on disk AND carries work: reviewable work nobody lifted. Derived honestly for
202
- * OLD runs too — records written before this field existed strand exactly the same way.
200
+ * - `'stranded'` — reviewable work nobody lifted, reached two ways: a COMPLETED repo-scoped
201
+ * run with no recorded PR whose worktree still exists on disk AND carries work (derived
202
+ * honestly for OLD runs too — records written before this field existed strand the same
203
+ * way); OR (crew#418) a run whose deliver phase hit a LIFT collision — a rebase conflict or
204
+ * non-fast-forward push — which the daemon reinterprets from the engine's `failed` to
205
+ * `completed`+`stranded`: its work is committed on the `wicked/<id>` branch (the engine
206
+ * reaps the now-clean worktree, but never the branch), recoverable via `POST /runs/:id/
207
+ * deliver`, which stands a throwaway worktree back up from that branch to lift it.
203
208
  * - `'vacuous'` — a COMPLETED repo-scoped run whose surviving worktree carries NO
204
209
  * contribution at all (crew#311: nothing uncommitted, no run-branch commit — units all
205
210
  * reached "done" while producing nothing). There is nothing to deliver; the recovery is a
@@ -231,6 +236,25 @@ export interface AgentSession {
231
236
  * idempotent against the existing branch) and `'none'` after it is cleaned up.
232
237
  */
233
238
  deliverUrl?: string;
239
+ /**
240
+ * The campaign this AD-HOC run was attached to at launch (`LaunchRunBody.campaignId`;
241
+ * wicked-studio#27, api-types 0.19.0) — daemon-joined at DTO assembly on `GET /runs` and
242
+ * `GET /runs/:id` from the launch record (audit trail), so it survives a daemon restart.
243
+ * ABSENT — never `null` — when the run was not attached (absence is the one spelling).
244
+ *
245
+ * NOT set on a campaign's own DAG-node runs: those are correlated through
246
+ * {@link Campaign.node_run_id} (their run ids are `{campaign}:{node}:a{attempt}`), and a
247
+ * caller-chosen `sessionId` may legally contain `:`, so id-shape parsing is unsound — read
248
+ * membership from the campaign, not from this field or the id.
249
+ */
250
+ campaign_id?: string;
251
+ /**
252
+ * The ad-hoc group label this run was launched under (`LaunchRunBody.groupLabel`;
253
+ * wicked-studio#27, api-types 0.19.0) — daemon-joined like {@link AgentSession.campaign_id}.
254
+ * ABSENT when the run is ungrouped. Runs sharing a label form one `RunGroup` on
255
+ * `GET /campaigns`.
256
+ */
257
+ group_label?: string;
234
258
  }
235
259
 
236
260
  /** An ordered unit of work within a run (`WorkUnit`). */
@@ -406,6 +430,13 @@ export interface GateInfo {
406
430
  prompt: string;
407
431
  lifecycle: string;
408
432
  receivedAt: string;
433
+ /**
434
+ * Refusal-detection warning (issue #419, api-types 0.20.0). Present ONLY when the gate prompt
435
+ * reads as a pure sandbox/tool refusal — the worker reported it could not act, with no sign of
436
+ * productive work — so an operator does not approve a refusal as if it were work. Additive and
437
+ * advisory: it never gates a decision, and is omitted entirely on a normal gate.
438
+ */
439
+ refusal?: { matched: boolean; reason: string };
409
440
  }
410
441
 
411
442
  /** Approve / reject payload for the steering gate (`POST /runs/:id/gate`). On a
@@ -512,7 +543,15 @@ export interface CoreEvent {
512
543
  updatedDescription?: string;
513
544
  // unitOutputCaptured (EVT-013)
514
545
  outputBytes?: number;
515
- stepStatus?: 'ok' | 'failed' | 'cancelled';
546
+ /**
547
+ * How the worker step finished. `'elicitation_failed'` (DES-002) and `'timed_out'` (perf#4 —
548
+ * the ENGINE's own turn ceiling, `WICKED_UNIT_TIMEOUT_SECS`) widen the original trio.
549
+ * `'timed_out'` vs `'cancelled'` is THE wire distinction between the platform's deadline and
550
+ * an operator cancel: automation may key recovery on `'timed_out'`, and must treat
551
+ * `'cancelled'` as final. Older engines never send the newer values — consumers keying on
552
+ * them degrade to doing nothing, which is the safe direction.
553
+ */
554
+ stepStatus?: 'ok' | 'failed' | 'cancelled' | 'elicitation_failed' | 'timed_out';
516
555
  /** `unitOutputDelta` (api-types 0.5.1): one streamed chunk of a worker's live output text. */
517
556
  text?: string;
518
557
  // unitPlanned enrichment fields — the wire spelling is camelCase (event_to_json);
@@ -630,13 +669,15 @@ export type WorkerStalledFrame = {
630
669
 
631
670
  /**
632
671
  * The stall watchdog's ESCALATION frame (crew#341; api-types 0.18.0). DAEMON-SYNTHETIC, emitted
633
- * only when escalation is armed (`SystemSettings.workerStallEscalateMinutes` > 0 — OFF by
634
- * default) and a detected stall stays silent past that threshold. Reports what the watchdog DID
635
- * and how it ended, one escalation per quiet period:
672
+ * only when escalation is armed (`SystemSettings.workerStallEscalateMinutes` > 0 — ON by default
673
+ * as of perf#4, default 30 minutes; an explicit `0` disarms) and a detected stall stays silent
674
+ * past that threshold. Reports what the watchdog DID and how it ended, one escalation per quiet
675
+ * period:
636
676
  *
637
- * - `action: 'reassign'` — the wedged cursor unit was recycled in place (engine `reassignUnit`:
638
- * the stale turn is superseded, the session closed, the unit re-dispatched; queued operator
639
- * injects survive into the fresh turn). `outcome: 'ok' | 'failed' | 'exhausted'`.
677
+ * - `action: 'reassign'` — the wedged cursor unit was recycled (engine `reassignUnit`: the stale
678
+ * turn is superseded, the session closed, the unit re-dispatched; queued operator injects
679
+ * survive into the fresh turn), routed to a DIFFERENT seat from the run's pool when one is
680
+ * available (perf#4) and in place otherwise. `outcome: 'ok' | 'failed' | 'exhausted'`.
640
681
  * - `action: 'notify'` — surface loudly without touching the run (the fail-loud rung).
641
682
  *
642
683
  * `needsYou: true` marks the frames a human should act on (failed / exhausted / notify).
@@ -656,8 +697,14 @@ export type WorkerStallEscalatedFrame = {
656
697
  outcome: 'ok' | 'failed' | 'exhausted';
657
698
  /** True when a human should look; false = the platform recovered on its own. */
658
699
  needsYou: boolean;
659
- /** action `reassign`: the seat the unit was re-dispatched to, when known. */
700
+ /** action `reassign`: the seat the unit was re-dispatched to, when known. As of perf#4 this
701
+ * is the failover TARGET — a different seat from the run's pool when one exists, else the
702
+ * stalled seat recycled in place. */
660
703
  cli?: string;
704
+ /** action `reassign` (perf#4, additive): the seat that was stalled and reassigned AWAY from,
705
+ * when the watchdog knew it. Equal to `cli` on a single-seat in-place recycle. Absent on
706
+ * frames from daemons predating the field. */
707
+ previousCli?: string;
661
708
  /** action `reassign`: automatic reassigns consumed for this run, this one included. */
662
709
  escalations?: number;
663
710
  /** outcome `failed`: bounded excerpt of what the recovery call threw. */
@@ -920,8 +967,10 @@ export interface UnitReworkAmendedEvent {
920
967
  }
921
968
 
922
969
  /** P2 — a worker's ApplyStepResult arrived and output is ready to be gated. Fires before GateDecided.
923
- * `outputBytes` is the byte length of the worker's output; `stepStatus` is "ok"/"failed"/"cancelled";
924
- * `governed` reflects whether the runner armed input governance for this unit. */
970
+ * `outputBytes` is the byte length of the worker's output; `stepStatus` is "ok" / "failed" /
971
+ * "cancelled" / "elicitation_failed" (DES-002) / "timed_out" (perf#4 — the engine's own turn
972
+ * ceiling, kept apart from "cancelled" so an operator cancel is never mistaken for a platform
973
+ * timeout); `governed` reflects whether the runner armed input governance for this unit. */
925
974
  export interface UnitOutputCapturedEvent {
926
975
  type: 'unitOutputCaptured';
927
976
  session: string;
@@ -929,7 +978,7 @@ export interface UnitOutputCapturedEvent {
929
978
  attempt: number;
930
979
  /** Byte length of the worker's raw output — distinguishes 0-byte from 8 MB truncated. */
931
980
  outputBytes: number;
932
- stepStatus: 'ok' | 'failed' | 'cancelled';
981
+ stepStatus: 'ok' | 'failed' | 'cancelled' | 'elicitation_failed' | 'timed_out';
933
982
  governed: boolean;
934
983
  }
935
984
 
@@ -1518,6 +1567,28 @@ export interface LaunchRunBody {
1518
1567
  * Omit when the launch is not a retry.
1519
1568
  */
1520
1569
  retryOf?: string;
1570
+ /**
1571
+ * Ad-hoc campaign attach (wicked-studio#27; api-types 0.19.0): file this run onto an EXISTING
1572
+ * campaign's surface. Validated at launch, loudly: an unknown campaign id fails the launch
1573
+ * with a 404 naming it (nothing is launched); a deployment whose engine addon lacks the
1574
+ * campaign bindings answers 501 ("upgrade the engine", never "fix your request").
1575
+ *
1576
+ * The attach is daemon-side provenance ONLY (recorded in the `run.launched` audit entry,
1577
+ * restart-durable, echoed as `AgentSession.campaign_id`): the run executes byte-identically
1578
+ * to an unattached launch — it does NOT become a DAG node, is never scheduled, gated, or
1579
+ * cancelled by the campaign, and the campaign's own lifecycle ignores it. It appears on the
1580
+ * campaigns surface as a `Campaign.attached_runs` entry. Mutually exclusive with
1581
+ * `groupLabel` (both ⇒ 400). Omit both for pre-0.19 behavior, byte for byte.
1582
+ */
1583
+ campaignId?: string;
1584
+ /**
1585
+ * Ad-hoc label grouping, created on first use (wicked-studio#27; api-types 0.19.0): runs
1586
+ * launched with the same label form one group — no pre-registration, no scheduler, no DAG,
1587
+ * no shared gates; pure provenance for "these sibling launches are one effort". 1–200
1588
+ * chars. Served as a `RunGroup` row beside the campaigns on `GET /campaigns` and echoed as
1589
+ * `AgentSession.group_label`. Mutually exclusive with `campaignId` (both ⇒ 400).
1590
+ */
1591
+ groupLabel?: string;
1521
1592
  }
1522
1593
 
1523
1594
  /**
@@ -1785,16 +1856,23 @@ export interface SystemSettings {
1785
1856
  /**
1786
1857
  * The stall watchdog's ESCALATION threshold (crew#341; api-types 0.18.0): quiet minutes before
1787
1858
  * the watchdog ACTS on a detected stall (see {@link WorkerStallEscalatedFrame}). Integer
1788
- * 0..1440; **absent or 0 = escalation OFF — the shipped default** (the issue's design: automatic
1789
- * recovery on a run that is merely slow would be worse than the wedge). Values below
1859
+ * 0..1440; `0` = escalation OFF. **Absent = the daemon default (30 — armed)**: as of perf#4 the
1860
+ * ladder is ON by default (run 616c8661 burned a full 2h turn ceiling with the watchdog
1861
+ * detection-only; the engine's `reassignUnit` supersedes the wedged turn safely, so acting is
1862
+ * strictly better than watching). 30 leaves ~55% headroom over the slowest legitimate
1863
+ * time-to-first-output observed in the field (~19.4 min) while still recovering ~4x faster
1864
+ * than the 2h ceiling. Opt out with an explicit `0`. Values below
1790
1865
  * `workerStallMinutes` escalate at the detection threshold — the ladder never acts before it
1791
1866
  * has notified.
1792
1867
  */
1793
1868
  workerStallEscalateMinutes?: number;
1794
1869
  /**
1795
1870
  * What an armed escalation DOES (crew#341): `'reassign'` (default) recycles the wedged cursor
1796
- * unit in place — the engine supersedes the stale turn, closes the worker session, bumps the
1797
- * attempt, and re-dispatches (queued operator injects survive into the fresh turn);
1871
+ * unit — the engine supersedes the stale turn, closes the worker session, bumps the
1872
+ * attempt, and re-dispatches (queued operator injects survive into the fresh turn). As of
1873
+ * perf#4 the watchdog routes the re-dispatch to a DIFFERENT seat from the run's own pool when
1874
+ * one is available (a seat that just sat silent past the escalation threshold is the last
1875
+ * seat to hand the retry to), falling back to an in-place recycle on a single-seat pool.
1798
1876
  * `'notify'` is the fail-loud rung — a `needsYou` `workerStallEscalated` frame + an audit
1799
1877
  * entry, the run untouched.
1800
1878
  */
@@ -2408,7 +2486,17 @@ export interface Campaign {
2408
2486
  /** The full definition, embedded so a resume needs no second store. */
2409
2487
  def: CampaignDef;
2410
2488
  node_status: Record<string, CampaignNodeStatus>;
2411
- /** node_id -> live Run id, always `{campaign}:{node}:a{attempt}` (attempt-keyed, idempotent). */
2489
+ /**
2490
+ * node_id -> live Run id, always `{campaign}:{node}:a{attempt}` (attempt-keyed, idempotent).
2491
+ *
2492
+ * These values ARE the engine session ids: a node's run is an ordinary governed run, so it is
2493
+ * served by `GET /runs` / `GET /runs/:id` under exactly this id, and every session-scoped
2494
+ * CoreEvent frame on `/ws` carries it as `session` (the `campaignNodeStarted` /
2495
+ * `campaignNodeAwaitingHuman` frames additionally spell it as `runId`). Live narration for a
2496
+ * campaign card is therefore a pure client-side correlation:
2497
+ * `frame.session === campaign.node_run_id[node]` — no extra wire, no per-node fetch
2498
+ * (wicked-studio#27).
2499
+ */
2412
2500
  node_run_id: Record<string, string>;
2413
2501
  /** node_id -> 0-based attempt counter (a `human_gate_on_failure` Retry bumps it). */
2414
2502
  node_attempt: Record<string, number>;
@@ -2418,6 +2506,72 @@ export interface Campaign {
2418
2506
  pending_failure_gates: string[];
2419
2507
  /** Whether a fail-fast (or Abort) tripped — finalization lands on `failed`. */
2420
2508
  fail_fast_tripped: boolean;
2509
+ /**
2510
+ * Per-node delivery (wicked-studio#27; api-types 0.19.0) — DAEMON-JOINED at DTO assembly on
2511
+ * `GET /campaigns` and `GET /campaigns/:id`, never engine-persisted: the exact same
2512
+ * `delivery`/`deliverUrl` derivation every run already carries on the runs wire
2513
+ * (`AgentSession.delivery`, crew#393/#311), keyed by node_id like {@link Campaign.node_status}.
2514
+ * A node appears here once its current-attempt run (`node_run_id[node]`) exists on the store —
2515
+ * a still-pending node has no entry. With `node_run_id` + `node_status`, this is everything a
2516
+ * "N of M delivered" rollup needs from ONE campaigns fetch — no per-node run fetch, and no
2517
+ * dependence on the runs list's archived-run filtering. ABSENT only from a pre-0.19 daemon.
2518
+ */
2519
+ node_delivery?: Record<string, CampaignNodeDelivery>;
2520
+ /**
2521
+ * Ad-hoc runs attached to this campaign at launch (`LaunchRunBody.campaignId`;
2522
+ * wicked-studio#27, api-types 0.19.0) — DAEMON-JOINED like {@link Campaign.node_delivery},
2523
+ * launch order. These are NOT DAG nodes: the campaign's scheduler and lifecycle ignore them;
2524
+ * each entry is an independent governed run that an operator filed onto this campaign's
2525
+ * surface. Absent from a pre-0.19 daemon; `[]` when none. Narration correlation is the same
2526
+ * as for nodes: each `runId` is the engine session id on `/ws` frames (`frame.session`).
2527
+ */
2528
+ attached_runs?: AttachedRunView[];
2529
+ }
2530
+
2531
+ /**
2532
+ * One member's delivery snapshot on the campaigns surface (wicked-studio#27; api-types 0.19.0)
2533
+ * — the same tri-state + URL contract as `AgentSession.delivery`/`deliverUrl` (crew#393/#311),
2534
+ * derived by the same code at DTO assembly. `deliverUrl` present exactly when
2535
+ * `delivery === 'delivered'`.
2536
+ */
2537
+ export interface CampaignNodeDelivery {
2538
+ delivery: 'delivered' | 'stranded' | 'vacuous' | 'none';
2539
+ deliverUrl?: string;
2540
+ }
2541
+
2542
+ /**
2543
+ * An ad-hoc run on the campaigns surface (wicked-studio#27; api-types 0.19.0) — a member of
2544
+ * `Campaign.attached_runs` or `RunGroup.runs`. `runId` is the engine session id: the run is
2545
+ * served in full by `GET /runs/:id`, and live `/ws` CoreEvent frames for it carry it as
2546
+ * `session`. `status`/`delivery`/`deliverUrl` are snapshots of the same fields the run wire
2547
+ * serves, so a rollup needs no second fetch.
2548
+ */
2549
+ export interface AttachedRunView {
2550
+ runId: string;
2551
+ status: SessionStatus;
2552
+ delivery: 'delivered' | 'stranded' | 'vacuous' | 'none';
2553
+ deliverUrl?: string;
2554
+ }
2555
+
2556
+ /**
2557
+ * An ad-hoc label group (wicked-studio#27; api-types 0.19.0): the runs launched with the same
2558
+ * `LaunchRunBody.groupLabel`, in launch order. NOT an engine campaign — no DAG, no scheduler,
2559
+ * no status of its own (derive a rollup from the members); it exists the moment the first run
2560
+ * is launched under the label. Served beside the campaigns in `CampaignsListResponse.groups`.
2561
+ */
2562
+ export interface RunGroup {
2563
+ label: string;
2564
+ runs: AttachedRunView[];
2565
+ }
2566
+
2567
+ /**
2568
+ * `GET /campaigns` 200 body (api-types 0.19.0 — `groups` is ADDITIVE: a pre-0.19 daemon sends
2569
+ * only `campaigns`). One fetch answers the whole grouping surface: engine campaigns (each
2570
+ * carrying its own per-node rollup fields) plus the ad-hoc label groups.
2571
+ */
2572
+ export interface CampaignsListResponse {
2573
+ campaigns: Campaign[];
2574
+ groups: RunGroup[];
2421
2575
  }
2422
2576
 
2423
2577
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wicked-crew-api-types",
3
- "version": "0.18.0",
3
+ "version": "0.20.0",
4
4
  "description": "The wire contract of the wicked-crew daemon's /api/v1 REST surface and /ws CoreEvent frames \u2014 types only, zero runtime",
5
5
  "type": "module",
6
6
  "types": "./index.d.ts",