wicked-crew-api-types 0.40.0 → 0.73.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.
- package/index.d.ts +2570 -23
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -84,7 +84,25 @@ export interface AuditEntry {
|
|
|
84
84
|
[k: string]: unknown;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* `GET /audit`'s query (api-types 0.43.0 adds `since`). Every filter is optional and they
|
|
89
|
+
* compose (AND); an empty value is absent. A malformed `limit` or `since` answers 400.
|
|
90
|
+
*/
|
|
91
|
+
export interface AuditQuery {
|
|
92
|
+
runId?: string;
|
|
93
|
+
action?: string;
|
|
94
|
+
/** Max entries returned, newest first. Default 200, capped at 1000. */
|
|
95
|
+
limit?: number;
|
|
96
|
+
/**
|
|
97
|
+
* Inclusive lower bound on {@link AuditEntry.ts} (unix MILLIS): keep entries with
|
|
98
|
+
* `ts >= since`. Applied before `limit`. The "while you were away" read: a skin passes the
|
|
99
|
+
* operator's last-visit instant and gets what happened since, including `system` actors'
|
|
100
|
+
* actions (the stall watchdog's `run.stall.*` entries).
|
|
101
|
+
*/
|
|
102
|
+
since?: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** `GET /audit` — newest first, filterable by {@link AuditQuery} (`?runId=` / `?action=` / `?limit=` / `?since=`). */
|
|
88
106
|
export interface AuditPage {
|
|
89
107
|
entries: AuditEntry[];
|
|
90
108
|
}
|
|
@@ -166,6 +184,8 @@ export type RoutingInfo =
|
|
|
166
184
|
export interface HealthResponse {
|
|
167
185
|
status: string;
|
|
168
186
|
version: string;
|
|
187
|
+
/** The engine actor's answer (`"ok"`), or `"busy"` when the actor did not answer within ~1 s
|
|
188
|
+
* (crew#471): the daemon serves while the engine is occupied — a large launch is planning. */
|
|
169
189
|
ping: string;
|
|
170
190
|
capabilities?: HealthCapabilities;
|
|
171
191
|
/**
|
|
@@ -211,6 +231,12 @@ export interface HealthCapabilities {
|
|
|
211
231
|
* on such a daemon.
|
|
212
232
|
*/
|
|
213
233
|
seatChipOnCreate?: boolean;
|
|
234
|
+
/**
|
|
235
|
+
* `AgentSession.chat_id` is served on every run launched from a chat, terminal ones included
|
|
236
|
+
* (C1; crew ≥ the release carrying api-types 0.71.0). ABSENT on a daemon before the field — read
|
|
237
|
+
* as `false`: group such a daemon's runs one session per run.
|
|
238
|
+
*/
|
|
239
|
+
runChatId?: boolean;
|
|
214
240
|
}
|
|
215
241
|
|
|
216
242
|
/** One `GET /health.warnings[]` entry (additive; wicked-core#411 / wicked-crew#497). */
|
|
@@ -289,12 +315,18 @@ export interface AgentSession {
|
|
|
289
315
|
*
|
|
290
316
|
* - `'delivered'` — a PR was opened for this run (by the deliver phase, or post-hoc via
|
|
291
317
|
* `POST /runs/:id/deliver`); `deliverUrl` carries the PR URL.
|
|
318
|
+
* - `'pushed'` — (api-types 0.69.0, N1) the run's branch was PUSHED and that is the whole
|
|
319
|
+
* delivery: no pull request could be opened because the origin is not a GitHub host gh can
|
|
320
|
+
* resolve (a local path, SSH, GitLab, ADO, Gitea…). `deliverBranch` + `deliverRemote` say
|
|
321
|
+
* what is where. Nothing is left to lift, so no post-hoc deliver is offered (`POST
|
|
322
|
+
* /runs/:id/deliver` answers 409); opening the pull/merge request is the forge's, by hand.
|
|
292
323
|
* - `'stranded'` — reviewable work nobody lifted, reached two ways: a COMPLETED repo-scoped
|
|
293
324
|
* run with no recorded PR whose worktree still exists on disk AND carries work (derived
|
|
294
325
|
* honestly for OLD runs too — records written before this field existed strand the same
|
|
295
|
-
* way); OR (crew#418) a run whose deliver phase hit a LIFT collision — a rebase conflict
|
|
296
|
-
*
|
|
297
|
-
*
|
|
326
|
+
* way); OR (crew#418) a run whose deliver phase hit a LIFT collision — a rebase conflict
|
|
327
|
+
* (a push the remote REFUSED no longer strands: since 0.69.0 it parks the run at the
|
|
328
|
+
* engine's deliver-refusal gate, N4) — which the daemon reinterprets from the engine's
|
|
329
|
+
* `failed` to `completed`+`stranded`: its work is committed on the `wicked/<id>` branch (the engine
|
|
298
330
|
* reaps the now-clean worktree, but never the branch), recoverable via `POST /runs/:id/
|
|
299
331
|
* deliver`, which stands a throwaway worktree back up from that branch to lift it.
|
|
300
332
|
* - `'vacuous'` — a COMPLETED repo-scoped run whose surviving worktree carries NO
|
|
@@ -317,7 +349,7 @@ export interface AgentSession {
|
|
|
317
349
|
* FAILURE of a deliver phase is not spelled here; it is `units[].status === 'rejected'`
|
|
318
350
|
* plus `denial_reason` (crew#318's message), both already on the list wire.
|
|
319
351
|
*/
|
|
320
|
-
delivery?: 'delivered' | 'stranded' | 'vacuous' | 'none';
|
|
352
|
+
delivery?: 'delivered' | 'pushed' | 'stranded' | 'vacuous' | 'none';
|
|
321
353
|
/**
|
|
322
354
|
* The delivered PR's URL (crew#393; api-types 0.18.0) — present exactly when
|
|
323
355
|
* `delivery === 'delivered'`; absent otherwise (absence is the one spelling, never `null`).
|
|
@@ -328,6 +360,13 @@ export interface AgentSession {
|
|
|
328
360
|
* idempotent against the existing branch) and `'none'` after it is cleaned up.
|
|
329
361
|
*/
|
|
330
362
|
deliverUrl?: string;
|
|
363
|
+
/** The pushed run branch (api-types 0.69.0, N1) — present exactly when `delivery === 'pushed'`. */
|
|
364
|
+
deliverBranch?: string;
|
|
365
|
+
/**
|
|
366
|
+
* The remote the branch was pushed to (api-types 0.69.0, N1), with any URL userinfo removed —
|
|
367
|
+
* present exactly when `delivery === 'pushed'`. May be a filesystem path (a local bare origin).
|
|
368
|
+
*/
|
|
369
|
+
deliverRemote?: string;
|
|
331
370
|
/**
|
|
332
371
|
* The campaign this AD-HOC run was attached to at launch (`LaunchRunBody.campaignId`;
|
|
333
372
|
* wicked-studio#27, api-types 0.19.0) — daemon-joined at DTO assembly on `GET /runs` and
|
|
@@ -419,6 +458,15 @@ export interface AgentSession {
|
|
|
419
458
|
/** Wave 6 (F-7R2-006): the seats BENCHED for this run — never convened, never a failover or
|
|
420
459
|
* judge target. Absent (never `[]`) when none / on an older engine. */
|
|
421
460
|
benched_seats?: BenchedSeat[];
|
|
461
|
+
/**
|
|
462
|
+
* The run's APPROVED INTENT AMENDMENTS, in the order a human decided them (wicked-core#555;
|
|
463
|
+
* additive, engine ≥ the core-ts release after 0.7.30) — the `amend_intent` arm of a gate
|
|
464
|
+
* ({@link GateDecision.action}). Each amendment is appended to every unit at or after the
|
|
465
|
+
* gate's cursor, so the acceptance list each LATER EVALUATOR is handed changed with the
|
|
466
|
+
* decision. Render them beside the run's original intent: this is what was withdrawn or
|
|
467
|
+
* changed, and when. ABSENT (never `[]`) on an unamended run and on an older engine.
|
|
468
|
+
*/
|
|
469
|
+
intent_amendments?: IntentAmendment[];
|
|
422
470
|
/** crew#661: the declared skills this run's phases ran WITHOUT (the published skills snapshot did not
|
|
423
471
|
* hold them when the launching subsystem armed) — the run proceeded DEGRADED, and says so here.
|
|
424
472
|
* ABSENT (never `[]`) when the run launched fully armed, and on a daemon before this field. */
|
|
@@ -450,6 +498,85 @@ export interface AgentSession {
|
|
|
450
498
|
* this field.
|
|
451
499
|
*/
|
|
452
500
|
chat_grounded?: boolean;
|
|
501
|
+
/**
|
|
502
|
+
* The chat this run was launched from (`LaunchRunBody.chatId`; C1, api-types 0.71.0) — on EVERY
|
|
503
|
+
* run, terminal ones included, and after a daemon restart (read back from the `run.launched`
|
|
504
|
+
* trail entry). ABSENT — never `null` — when the run was not launched from a chat, or on a daemon
|
|
505
|
+
* before the field: check `GET /health.capabilities.runChatId` before grouping runs by chat.
|
|
506
|
+
*/
|
|
507
|
+
chat_id?: string;
|
|
508
|
+
/**
|
|
509
|
+
* The engine's plan state (wicked-core `TeamPlanState`, DES-TEAMING-002 §8.4-§8.6; api-types
|
|
510
|
+
* 0.46.0 types the engine field) — present on a run launched from a PLAN or a PRESET, ABSENT on
|
|
511
|
+
* every other run (a registered def, the free-text planner) and on a team run before its launch
|
|
512
|
+
* is scored. Engine-computed; typed here only as far as a skin reads it. Read the run's kind from
|
|
513
|
+
* {@link AgentSession.run_identity}, which is resolved from this.
|
|
514
|
+
*/
|
|
515
|
+
team_plan?: TeamPlanState;
|
|
516
|
+
/**
|
|
517
|
+
* What this run IS (seam X2; api-types 0.46.0) — daemon-resolved at DTO assembly on BOTH
|
|
518
|
+
* `GET /runs` and `GET /runs/:id` from the engine's own record (the plan state above, else the
|
|
519
|
+
* launch's recorded `sessionStarted`), never from the run's phase sequence. Always present on a
|
|
520
|
+
* 0.46.0+ daemon; ABSENT from an older one. `system` replaces a skin's own list of system
|
|
521
|
+
* workflow ids.
|
|
522
|
+
*/
|
|
523
|
+
run_identity?: RunIdentity;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* The fields of the engine's `TeamPlanState` a skin reads (the engine sends more — every field is
|
|
528
|
+
* engine-computed and additive).
|
|
529
|
+
*/
|
|
530
|
+
export interface TeamPlanState {
|
|
531
|
+
/** The last plan rev assigned (accepted or held). `0` = none yet. */
|
|
532
|
+
rev: number;
|
|
533
|
+
/** The last ACCEPTED plan rev. `0` = no plan accepted yet. */
|
|
534
|
+
accepted_rev: number;
|
|
535
|
+
/** The preset the launch named. ABSENT on a user plan. */
|
|
536
|
+
preset?: string;
|
|
537
|
+
/**
|
|
538
|
+
* (wicked-core#633, DES-TEAMING-002 X1; api-types 0.47.0) PRESENT while the run's PA is scoping
|
|
539
|
+
* its launch plan — a creator plan or preset that declared no `touch` set. Until the `pa-scope`
|
|
540
|
+
* unit's boundary decides the plan, the run's only unit is `pa-scope` (plan rev 1): what the run
|
|
541
|
+
* will contain is not known yet. ABSENT once the plan is scoped (or when none needed scoping).
|
|
542
|
+
*/
|
|
543
|
+
scope?: TeamPlanScopeHold;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/** The launch plan the PA is scoping (the engine's `ScopeHold`). */
|
|
547
|
+
export interface TeamPlanScopeHold {
|
|
548
|
+
/** The launch plan as authored (the preset's steps or the user's plan), without `pa-scope`. */
|
|
549
|
+
plan: { steps: TeamPlanStep[] } & Record<string, unknown>;
|
|
550
|
+
/** The run has no repo: the PA rates the risk (`RISK`) instead of declaring a touch set. */
|
|
551
|
+
unbound: boolean;
|
|
552
|
+
/** The scope step's `SCOPE` / `RISK` lines once its turn folds; ABSENT until then. */
|
|
553
|
+
answer?: { ord: number; attempt: number; by: string; lines: string };
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* What a run is (seam X2; api-types 0.46.0), resolved by the daemon from the engine's record.
|
|
558
|
+
*
|
|
559
|
+
* - `'preset'` — launched from a preset; `name` is the preset.
|
|
560
|
+
* - `'user_plan'` — launched from a user-composed plan (the engine's per-run `<run>:plan-<rev>`
|
|
561
|
+
* def); `name` is `null`.
|
|
562
|
+
* - `'workflow'` — launched on a registered workflow def; `name` is its id (a crew-composed
|
|
563
|
+
* per-run delivery/floor def reads as its base).
|
|
564
|
+
* - `'free_text'` — no workflow and no plan: the free-text planner; `name` is `null`.
|
|
565
|
+
* - `'unknown'` — the daemon has no record of what the launch named (a run predating the
|
|
566
|
+
* engine's event log, or an engine without the log read); `name` is `null`.
|
|
567
|
+
*/
|
|
568
|
+
export interface RunIdentity {
|
|
569
|
+
kind: 'preset' | 'user_plan' | 'workflow' | 'free_text' | 'unknown';
|
|
570
|
+
/** The preset or workflow name — `null` unless `kind` is `'preset'` or `'workflow'`. */
|
|
571
|
+
name: string | null;
|
|
572
|
+
/** `kind === 'user_plan'`, spelled out. */
|
|
573
|
+
user_plan: boolean;
|
|
574
|
+
/**
|
|
575
|
+
* A machine-owned run (chat, onboarding, the interactive document and video seams, …): skins keep
|
|
576
|
+
* it off delivery surfaces. Keyed by `name` against the daemon's ONE list — the same list that
|
|
577
|
+
* stamps `WorkflowDef.is_system` on `GET /workflows`.
|
|
578
|
+
*/
|
|
579
|
+
system: boolean;
|
|
453
580
|
}
|
|
454
581
|
|
|
455
582
|
/** An ordered unit of work within a run (`WorkUnit`). */
|
|
@@ -494,6 +621,34 @@ export interface WorkUnit {
|
|
|
494
621
|
* planned before the field existed, and on an older engine.
|
|
495
622
|
*/
|
|
496
623
|
base_skill_ref?: string | null;
|
|
624
|
+
/**
|
|
625
|
+
* The phase-catalog entry this unit instantiates (`build`, `review`, …) when it was planned from
|
|
626
|
+
* a catalog-composed plan — a preset or a user plan (wicked-core `WorkUnit.catalog`,
|
|
627
|
+
* DES-TEAMING-002 T3; api-types 0.46.0 types the engine field). ABSENT on every other unit.
|
|
628
|
+
*/
|
|
629
|
+
catalog?: string;
|
|
630
|
+
/** TRUE on a unit of a TEAM run (planned from the per-run plan def); ABSENT otherwise. Engine field. */
|
|
631
|
+
team_run?: boolean;
|
|
632
|
+
/**
|
|
633
|
+
* TRUE when this unit's phase declared `requires_capture_report` (wicked-core#535 / BC-80) —
|
|
634
|
+
* carried from the def at plan time. ABSENT on every other unit and on an older engine.
|
|
635
|
+
*/
|
|
636
|
+
requires_capture_report?: boolean;
|
|
637
|
+
/**
|
|
638
|
+
* What the capture phase REPORTED, read from its output marker by the gate fold
|
|
639
|
+
* (wicked-core#535 / BC-80): `derived` learnings, `submitted` proposals, `failed` submissions.
|
|
640
|
+
* Present once a capture unit emitted a well-formed marker — whether it passed or denied, so the
|
|
641
|
+
* counts are readable beside the denial. ABSENT on every other unit, on a capture unit whose
|
|
642
|
+
* marker was MISSING (that denies; the denial is the record), and on an older engine.
|
|
643
|
+
*/
|
|
644
|
+
capture_report?: CaptureReport;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/** The counts a capture phase reported (`WorkUnit.capture_report`; wicked-core#535). */
|
|
648
|
+
export interface CaptureReport {
|
|
649
|
+
derived: number;
|
|
650
|
+
submitted: number;
|
|
651
|
+
failed: number;
|
|
497
652
|
}
|
|
498
653
|
|
|
499
654
|
/** A run plus its ordered units (`SessionView`) — the shape `GET /runs` returns. */
|
|
@@ -626,9 +781,29 @@ export interface RosterSeat {
|
|
|
626
781
|
* while every ballot failed. Absent = the probe decided (and since 0.36.0 the probe itself needs
|
|
627
782
|
* a credential-SHAPED file, never mere presence).
|
|
628
783
|
*/
|
|
629
|
-
auth_source?: 'seat-stderr';
|
|
630
|
-
/** Present with `auth_source: 'seat-stderr'
|
|
784
|
+
auth_source?: 'seat-stderr' | 'probe';
|
|
785
|
+
/** Present with `auth_source: 'seat-stderr'`, and with `probe` when it read signed out: the
|
|
786
|
+
* seat's own words, bounded. */
|
|
631
787
|
auth_evidence?: string;
|
|
788
|
+
/**
|
|
789
|
+
* Present with `auth_source: 'probe'` (crew#630): ISO-8601 of the seat's own auth-status check
|
|
790
|
+
* (`claude auth status`, `codex login status`) under the seat's configuration home, which then
|
|
791
|
+
* decided `auth` and `signed_in` over the credential file. The daemon re-checks in the
|
|
792
|
+
* background (a signed-in answer after 5 minutes, any other after 30 s). A probed seat whose
|
|
793
|
+
* check has not answered yet reads `unknown`, never `signed_in` off its file.
|
|
794
|
+
*/
|
|
795
|
+
probed_at?: string;
|
|
796
|
+
/**
|
|
797
|
+
* How far `auth` was VERIFIED (crew#645): `live` — the seat answered an authenticated request, or
|
|
798
|
+
* refused one for want of a credential (a status command reads the stored login, so an expired
|
|
799
|
+
* OAuth login still reads signed in there; the daemon makes one short live request when it says
|
|
800
|
+
* signed in); `status` — only the status command answered (the live request could not tell);
|
|
801
|
+
* `unverified` — nothing asked the seat (it has no status command, or its check has not answered
|
|
802
|
+
* or could not tell). Optional on the wire: a daemon before it sends none.
|
|
803
|
+
*/
|
|
804
|
+
login_check?: 'live' | 'status' | 'unverified';
|
|
805
|
+
/** Present unless `login_check` is `live`: why, in the operator's words ("login unverified — …"). */
|
|
806
|
+
login_note?: string;
|
|
632
807
|
/** Present when `auth` is `not_required`: the free tier the seat answers on. */
|
|
633
808
|
free_tier?: string;
|
|
634
809
|
/**
|
|
@@ -659,9 +834,33 @@ export interface RosterSeat {
|
|
|
659
834
|
* a reader must tolerate absence today.
|
|
660
835
|
*/
|
|
661
836
|
council_bench?: RosterSeatCouncilBench;
|
|
837
|
+
/**
|
|
838
|
+
* (core#581) Present on a seat whose ACP config pins `verified_version`, once the daemon's
|
|
839
|
+
* probe has answered: the build the seat's input-governance admission was proven against and
|
|
840
|
+
* what the binary reports now. `matched: false` with `governanceClaimed: true` means the engine
|
|
841
|
+
* fails the seat's ACP governance closed at every spawn.
|
|
842
|
+
*/
|
|
843
|
+
version_pin?: SeatVersionPin;
|
|
662
844
|
[k: string]: unknown;
|
|
663
845
|
}
|
|
664
846
|
|
|
847
|
+
/**
|
|
848
|
+
* One pinned seat's `--version` reading (core#581): `RosterSeat.version_pin` and
|
|
849
|
+
* `AcpCliDiagnostics.versionPin`. Read the way the engine's spawn-time check reads it: the pin
|
|
850
|
+
* matches only the first line of `--version`, trimmed; `observed` is the first non-empty line,
|
|
851
|
+
* `null` when the binary did not answer.
|
|
852
|
+
*/
|
|
853
|
+
export interface SeatVersionPin {
|
|
854
|
+
cli: string;
|
|
855
|
+
binary: string;
|
|
856
|
+
pinned: string;
|
|
857
|
+
observed: string | null;
|
|
858
|
+
matched: boolean;
|
|
859
|
+
governanceClaimed: boolean;
|
|
860
|
+
/** The operator-facing reason when a governed seat's pin does not hold; else `null`. */
|
|
861
|
+
disclosure: string | null;
|
|
862
|
+
}
|
|
863
|
+
|
|
665
864
|
/** `RosterSeat.council_bench` (api-types 0.35.0).
|
|
666
865
|
* @deprecated ABSENT from crew 0.7.36 (see {@link RosterSeat.council_bench}); removed one minor later. */
|
|
667
866
|
export interface RosterSeatCouncilBench {
|
|
@@ -679,6 +878,62 @@ export interface RosterSeatCouncilBench {
|
|
|
679
878
|
window_ms: number;
|
|
680
879
|
}
|
|
681
880
|
|
|
881
|
+
/**
|
|
882
|
+
* `GET /roster/record?days=N` (api-types 0.52.0): each seat's record over the last N days (default 7,
|
|
883
|
+
* max 30), folded from the runs' durable event logs (studio's weekly 1:1 per agent). Nothing new is
|
|
884
|
+
* stored; the window is by event time. A unit's seat is `unitDistributed.cli` (moved by
|
|
885
|
+
* `unitReassigned.newCli`), else the unit row's `assigned_cli`. 503 when the engine has no event-log
|
|
886
|
+
* read binding (a missing binding is not an empty week).
|
|
887
|
+
*/
|
|
888
|
+
export interface SeatRecordResponse {
|
|
889
|
+
days: number;
|
|
890
|
+
/** Window start, unix MILLIS. */
|
|
891
|
+
since: number;
|
|
892
|
+
/** Window end (the read time), unix MILLIS. */
|
|
893
|
+
until: number;
|
|
894
|
+
/** Runs whose event log was read. */
|
|
895
|
+
runsRead: number;
|
|
896
|
+
/** True when the daemon's per-read run cap cut the read (the oldest runs in the window were skipped). */
|
|
897
|
+
truncated: boolean;
|
|
898
|
+
/** Seats with any record in the window, by key. A roster seat absent here had no units and no bench. */
|
|
899
|
+
seats: SeatRecord[];
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/** One seat's week (`SeatRecordResponse.seats[]`). */
|
|
903
|
+
export interface SeatRecord {
|
|
904
|
+
/** The roster key. */
|
|
905
|
+
cli: string;
|
|
906
|
+
/** Distinct units the seat was dispatched on in the window. */
|
|
907
|
+
units: number;
|
|
908
|
+
/** Units of this seat with a gate evaluation in the window. */
|
|
909
|
+
gated: number;
|
|
910
|
+
/** Of `gated`: the FIRST evaluation passed (`gateEvaluated.combined` and no `denial`). */
|
|
911
|
+
firstPass: number;
|
|
912
|
+
/** Units the seat ran first that were dispatched again (a retry or a send-back) or given a `unitReworkAmended`. */
|
|
913
|
+
rework: number;
|
|
914
|
+
/** Distinct units that went quiet (`workerStalled`, engine or daemon watchdog) while the seat ran them. */
|
|
915
|
+
stalls: number;
|
|
916
|
+
/** Runs in the window whose `benched_seats` names the seat. */
|
|
917
|
+
benched: number;
|
|
918
|
+
/** Bench reason → runs. */
|
|
919
|
+
benchReasons: Record<string, number>;
|
|
920
|
+
/** Summed `cliUsage.costUsd` where a price was known; `null` when no usage frame carried one (never `0` for "unknown"). */
|
|
921
|
+
costUsd: number | null;
|
|
922
|
+
/** Usage frames that carried a price. */
|
|
923
|
+
costedUsage: number;
|
|
924
|
+
/** Phase (`phase_ref`, else `stage`) → the same counts, so a move can name the phase. */
|
|
925
|
+
byPhase: Record<string, SeatPhaseRecord>;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/** One phase's slice of a {@link SeatRecord}. */
|
|
929
|
+
export interface SeatPhaseRecord {
|
|
930
|
+
units: number;
|
|
931
|
+
gated: number;
|
|
932
|
+
firstPass: number;
|
|
933
|
+
rework: number;
|
|
934
|
+
stalls: number;
|
|
935
|
+
}
|
|
936
|
+
|
|
682
937
|
/** A seat's auth reading (`RosterSeat.auth`; api-types 0.35.0). */
|
|
683
938
|
export type SeatAuth = 'signed_in' | 'signed_out' | 'not_required' | 'unknown';
|
|
684
939
|
|
|
@@ -774,8 +1029,39 @@ export interface GateDecision {
|
|
|
774
1029
|
* `unitDispatched{attempt: last + 1}`); requires `approve: false` — a disagreement between
|
|
775
1030
|
* `action` and `approve` answers 400, a gate with no creator phase before it 409. An older
|
|
776
1031
|
* daemon's strict schema rejects the key with a 400 — omit it against such servers.
|
|
1032
|
+
*
|
|
1033
|
+
* The ESCALATION arms (additive; wicked-core#469 / #467, engine ≥ the core-ts release after
|
|
1034
|
+
* 0.7.30): all require `approve: true` and take no `amend` / `amendScope` / `plan` (a 400
|
|
1035
|
+
* otherwise). `'extend'` | `'targeted'` | `'accept_partial'` answer the escalation gate of a
|
|
1036
|
+
* repo-checks floor that did not finish (`gateEscalated.denialSource: 'repo_checks_timeout'`):
|
|
1037
|
+
* the engine re-runs the floor on the tree as it stands — the seat does not run again — with
|
|
1038
|
+
* every check under 2× its bound, with the repo's declared `test_targeted` in place of the full
|
|
1039
|
+
* test set (no targeted command ⇒ the test set is waived), or with the checks that did not
|
|
1040
|
+
* finish waived; the re-run goes through the ordinary gate and waived checks are named in
|
|
1041
|
+
* `gateEvaluated.floorNote`. `'accept_suggestion'` answers an evaluator's worktree-guard denial
|
|
1042
|
+
* whose edit was restored and pinned (`gateEscalated.suggestionRef`): the engine applies the
|
|
1043
|
+
* pinned edit and rewinds to the creator (`unitReworkAmended{scope: 'accept_suggestion'}`).
|
|
1044
|
+
* The engine refuses each at any other gate — a 409 — and the gate stays open.
|
|
1045
|
+
*
|
|
1046
|
+
* `'amend_intent'` (additive; wicked-core#555, engine ≥ the core-ts release after 0.7.30)
|
|
1047
|
+
* approves the gate AND AMENDS THE RUN'S INTENT: `amend` carries what the acceptance list now
|
|
1048
|
+
* says (required, non-empty), `approve: true`, and no `amendScope` — the scope IS every unit at
|
|
1049
|
+
* or after the cursor, which is what makes it reach the LATER EVALUATOR. Before it, nothing
|
|
1050
|
+
* amended the list an evaluator is handed, so a mid-run descope could only end in a relaunch.
|
|
1051
|
+
* The engine appends the text to those units' descriptions, records it on the run
|
|
1052
|
+
* (`AgentSession.intent_amendments`) and emits `intentAmended`. Refused at a plan or team gate,
|
|
1053
|
+
* and with empty text — a 409 with the gate left open.
|
|
777
1054
|
*/
|
|
778
|
-
action?:
|
|
1055
|
+
action?:
|
|
1056
|
+
| 'approve'
|
|
1057
|
+
| 'request_changes'
|
|
1058
|
+
| 'reject'
|
|
1059
|
+
| 'edit_plan'
|
|
1060
|
+
| 'extend'
|
|
1061
|
+
| 'targeted'
|
|
1062
|
+
| 'accept_partial'
|
|
1063
|
+
| 'accept_suggestion'
|
|
1064
|
+
| 'amend_intent';
|
|
779
1065
|
/**
|
|
780
1066
|
* Where an approve's `amend` lands (api-types 0.38.0, additive; same release as `action`).
|
|
781
1067
|
* Absent = `'cursor'` (today: the gated unit's description). `'creator'` — the first creator
|
|
@@ -784,6 +1070,38 @@ export interface GateDecision {
|
|
|
784
1070
|
* Identical text already present on the target is not appended twice.
|
|
785
1071
|
*/
|
|
786
1072
|
amendScope?: 'cursor' | 'creator';
|
|
1073
|
+
/**
|
|
1074
|
+
* Approve a `plan_approval` gate WITH AN EDIT (DES-TEAMING-002 §8.6, seam T3; additive; needs an
|
|
1075
|
+
* engine whose addon carries `Core.supportsPlanLaunch`). The edited plan: the engine publishes
|
|
1076
|
+
* `plan.proposed{by:"human", kind:"edit"}`, floor-fills it (phases below the floor are ADDED,
|
|
1077
|
+
* never refused) and accepts it as the next `plan_rev` — or refuses it (`plan.refused`) and
|
|
1078
|
+
* re-opens the gate under a new `gate_id`, answering `awaiting_human`. Requires `approve: true`
|
|
1079
|
+
* and `action` omitted or `'edit_plan'`; `amend` / `amendScope` do not apply. A plain approve
|
|
1080
|
+
* (no `plan`) releases the held plan as it is; `approve: false` rejects (cancels).
|
|
1081
|
+
*/
|
|
1082
|
+
plan?: LaunchPlan;
|
|
1083
|
+
/**
|
|
1084
|
+
* The gate this decision answers — the unit `ord` its open gate paused before, as `GET
|
|
1085
|
+
* /runs/:id/gate` served it (api-types 0.44.0, additive). When present the daemon compares it to
|
|
1086
|
+
* the run's open gate and answers **409** `{ error, code: 'gate_changed', openOrd }` on a
|
|
1087
|
+
* mismatch, confirming nothing: the gate was answered elsewhere or replaced while the decision
|
|
1088
|
+
* waited (a skin's undo window). When the daemon cannot tell which gate is open it answers
|
|
1089
|
+
* **409** `{ error, code: 'gate_unknown' }` — refresh the gate and decide again. Absent = today's
|
|
1090
|
+
* behaviour (no gate check).
|
|
1091
|
+
*/
|
|
1092
|
+
ord?: number;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/**
|
|
1096
|
+
* A USER-COMPOSED plan (DES-TEAMING-002 §8.4, seam T3): ordered `steps[]` over the phase catalog,
|
|
1097
|
+
* the predicted `touch` set (the intent score's input: a plan with a creator step and no `touch`
|
|
1098
|
+
* scores 100, "no declared scope") and an optional MANUAL-mode floor `override` (refused in auto
|
|
1099
|
+
* mode). A step's `id` defaults to its catalog id. The ENGINE validates every key.
|
|
1100
|
+
*/
|
|
1101
|
+
export interface LaunchPlan {
|
|
1102
|
+
steps: Array<{ catalog: string; id?: string | undefined; [k: string]: unknown }>;
|
|
1103
|
+
touch?: string[];
|
|
1104
|
+
override?: TeamPlanOverride;
|
|
787
1105
|
}
|
|
788
1106
|
|
|
789
1107
|
/**
|
|
@@ -1100,6 +1418,22 @@ export type WorkerStallEscalatedFrame = {
|
|
|
1100
1418
|
escalations?: number;
|
|
1101
1419
|
/** outcome `failed`: bounded excerpt of what the recovery call threw. */
|
|
1102
1420
|
error?: string;
|
|
1421
|
+
/**
|
|
1422
|
+
* Why the watchdog did not do a plain reassign (crew#638 / #581 / #580; additive, absent on older
|
|
1423
|
+
* daemons): `tool_unit` — the cursor is the engine's own command, nothing to fail over to;
|
|
1424
|
+
* `evaluating` — the worker returned and its gate evaluation is in flight, a reassign would start
|
|
1425
|
+
* a second creator; `evaluator_distinct` — every other seat is one evaluator ≠ creator forbids
|
|
1426
|
+
* (see `avoided`), so the unit was left for a human; `no_output` (outcome `exhausted`) — the
|
|
1427
|
+
* reassigned seat produced nothing past a startup banner.
|
|
1428
|
+
*/
|
|
1429
|
+
reason?: 'tool_unit' | 'evaluating' | 'evaluator_distinct' | 'no_output';
|
|
1430
|
+
/**
|
|
1431
|
+
* The seats the failover pick excluded under evaluator ≠ creator (crew#638; additive): a
|
|
1432
|
+
* creator cursor avoids the run's evaluator seats, an evaluator cursor the seats that built its
|
|
1433
|
+
* work, an `evaluator_distinct`-routed unit the seat it was routed away from. Present on an `ok`
|
|
1434
|
+
* reassign that skipped one and on a `reason: 'evaluator_distinct'` notify; absent otherwise.
|
|
1435
|
+
*/
|
|
1436
|
+
avoided?: string[];
|
|
1103
1437
|
};
|
|
1104
1438
|
|
|
1105
1439
|
export interface RecordedEvent extends CoreEvent {
|
|
@@ -1998,10 +2332,12 @@ export interface UnitReworkAmendedEvent {
|
|
|
1998
2332
|
* 0.7.27): `'cursor'` — the gated unit, today's approve-with-steer (`GateDecision.amendScope`
|
|
1999
2333
|
* absent or `'cursor'`); `'creator'` — the first creator phase at/after the cursor
|
|
2000
2334
|
* (`amendScope: 'creator'`); `'request_changes'` — the creator phase the gate rewound to, and
|
|
2001
|
-
* `amendment` then carries the evaluator's full findings + the operator's note
|
|
2002
|
-
*
|
|
2335
|
+
* `amendment` then carries the evaluator's full findings + the operator's note;
|
|
2336
|
+
* `'accept_suggestion'` (wicked-core#467) — the same rewind after the operator adopted the
|
|
2337
|
+
* evaluator's pinned edit, and `amendment` names it. ABSENT on an engine before the field — read
|
|
2338
|
+
* as `'cursor'`.
|
|
2003
2339
|
*/
|
|
2004
|
-
scope?: 'cursor' | 'creator' | 'request_changes';
|
|
2340
|
+
scope?: 'cursor' | 'creator' | 'request_changes' | 'accept_suggestion';
|
|
2005
2341
|
}
|
|
2006
2342
|
|
|
2007
2343
|
/** P2 — a worker's ApplyStepResult arrived and output is ready to be gated. Fires before GateDecided.
|
|
@@ -2119,6 +2455,16 @@ export interface UnitDistributedEvent {
|
|
|
2119
2455
|
degraded_reason?: string | null;
|
|
2120
2456
|
}
|
|
2121
2457
|
|
|
2458
|
+
/** One APPROVED INTENT AMENDMENT (`AgentSession.intent_amendments`; wicked-core#555). */
|
|
2459
|
+
export interface IntentAmendment {
|
|
2460
|
+
/** The human's own words — what the run's acceptance list now says. */
|
|
2461
|
+
text: string;
|
|
2462
|
+
/** The gate's cursor unit `ord`: the first unit the amendment reached. */
|
|
2463
|
+
ord: number;
|
|
2464
|
+
/** When it was decided, unix millis. */
|
|
2465
|
+
at: number;
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2122
2468
|
/** One seat the wave-6 engine BENCHED for a run (`AgentSession.benched_seats`, F-7R2-006): never
|
|
2123
2469
|
* convened, never a failover or judge target, named in `unitDistributed.degradedReason`. */
|
|
2124
2470
|
export interface BenchedSeat {
|
|
@@ -2618,6 +2964,30 @@ export interface SkillsManifestResponse {
|
|
|
2618
2964
|
* `recorded.reasons` is `null` for a row written before per-reason portability (0.34.0). */
|
|
2619
2965
|
drift?: SnapshotRowDrift[];
|
|
2620
2966
|
} | null;
|
|
2967
|
+
/**
|
|
2968
|
+
* The wicked-garden plugin INSTALLED on this host right now (wicked-studio#388) — so a surface
|
|
2969
|
+
* can say when the daemon's baseline, and therefore every snapshot published from it, is behind
|
|
2970
|
+
* the operator's install. `null` = no plugin is installed (the unseeded case the 503 covers);
|
|
2971
|
+
* absent on a daemon older than the release carrying it. Compare `installed.baseline` with
|
|
2972
|
+
* `manifest.baseline`: different = Refresh baseline, then Publish.
|
|
2973
|
+
*/
|
|
2974
|
+
installed?: InstalledPlugin | null;
|
|
2975
|
+
}
|
|
2976
|
+
|
|
2977
|
+
/** `SkillsManifestResponse.installed` — the live plugin's identity (wicked-studio#388). */
|
|
2978
|
+
export interface InstalledPlugin {
|
|
2979
|
+
source: { kind: SkillSourceKind; path: string; plugin_version: string };
|
|
2980
|
+
/** HEAD sha for a `checkout` source; `null` otherwise (or when git could not answer). */
|
|
2981
|
+
git_sha: string | null;
|
|
2982
|
+
/**
|
|
2983
|
+
* The content hash of the INSTALLED bundle, comparable with `SkillManifest.baseline` — the same
|
|
2984
|
+
* identity `POST /skills/refresh-baseline` decides on, never the version string (two installs of
|
|
2985
|
+
* one version with different bytes are two baselines). `null` when the bundle could not be read,
|
|
2986
|
+
* and then `unreadable` says why: a comparison crew could not make is stated, not reported as
|
|
2987
|
+
* agreement.
|
|
2988
|
+
*/
|
|
2989
|
+
baseline: string | null;
|
|
2990
|
+
unreadable: string | null;
|
|
2621
2991
|
}
|
|
2622
2992
|
|
|
2623
2993
|
/** The identity of a portability-rule table (api-types 0.36.0, F-083): its version and the
|
|
@@ -3247,6 +3617,14 @@ export interface TestingReconResponse {
|
|
|
3247
3617
|
/** Present only when the fan was campaign-registered with a `projectId` and filing a sibling
|
|
3248
3618
|
* into the project failed — the runs are LIVE; re-attach via POST /projects/:id/members. */
|
|
3249
3619
|
projectAttachError?: string;
|
|
3620
|
+
/**
|
|
3621
|
+
* `true` on a **202**: the engine was still launching the fan when the daemon answered (crew#471 —
|
|
3622
|
+
* it plans every node's units before it replies, which held the POST for minutes). The `runIds`
|
|
3623
|
+
* are final (deterministic node ids) and can be followed now; the trail entries and project
|
|
3624
|
+
* filing land when the engine answers, and a late refusal is audited as `campaign.launch_failed`.
|
|
3625
|
+
* Absent on a 201.
|
|
3626
|
+
*/
|
|
3627
|
+
launching?: true;
|
|
3250
3628
|
}
|
|
3251
3629
|
|
|
3252
3630
|
// ── The governed test-authoring launch (wave 6; api-types 0.36.0) ──────────────────────────────
|
|
@@ -3421,6 +3799,21 @@ export interface LaunchRunBody {
|
|
|
3421
3799
|
repoRef?: string;
|
|
3422
3800
|
/** Built-in workflow id (`feature` | `bug` | `migration`); omit for free-text single-unit mode. */
|
|
3423
3801
|
workflow?: string;
|
|
3802
|
+
/**
|
|
3803
|
+
* A USER-COMPOSED plan (DES-TEAMING-002 §8.4, seam T3; additive; needs an engine whose addon
|
|
3804
|
+
* carries `Core.supportsPlanLaunch` — an older one is refused with a 501, never run unplanned).
|
|
3805
|
+
* The ENGINE publishes `plan.proposed{by:"human"}` before any unit runs, scores and floor-fills
|
|
3806
|
+
* it, and holds it at a `plan_approval` gate (`awaiting_human{gate_kind:"plan_approval"}`) when
|
|
3807
|
+
* the approval matrix says so: always in manual mode, and in auto mode when high risk (band
|
|
3808
|
+
* 70-100 or destructive). Mutually exclusive with `workflow` (a plan or a preset).
|
|
3809
|
+
* A creator plan with NO `touch` set — and every creator preset — is scoped by the run's PA
|
|
3810
|
+
* first (wicked-core#633, X1): rev 1 is the read-only `pa-scope` step alone (unit ord 1), the
|
|
3811
|
+
* scored plan is rev 2 proposed by the PA seat (`plan.proposed{by:<PA seat>}`), and its
|
|
3812
|
+
* `plan_approval` gate, when one opens, is at ord 2. Declare `touch` to be scored at launch.
|
|
3813
|
+
* `deliver: "pr"` with a plan (api-types 0.42.0, T8) hands the engine the deliver step, which it
|
|
3814
|
+
* appends to the plan and puts in the floor; `deliver` omitted with a plan means `"none"`.
|
|
3815
|
+
*/
|
|
3816
|
+
plan?: LaunchPlan;
|
|
3424
3817
|
/**
|
|
3425
3818
|
* The project to file this run into (DES-PROJECT-001 §2.2). The `crew.run` membership is
|
|
3426
3819
|
* attached atomically with the launch record; an unknown or archived project fails the launch
|
|
@@ -3532,6 +3925,29 @@ export interface LaunchRunBody {
|
|
|
3532
3925
|
actor?: string;
|
|
3533
3926
|
}
|
|
3534
3927
|
|
|
3928
|
+
/**
|
|
3929
|
+
* One issue a launch's intent linked (`#N`, `owner/repo#N`, an issue URL) and whether the daemon
|
|
3930
|
+
* could read it (crew#627, additive). A governed worker cannot read GitHub, so for a workflow launch
|
|
3931
|
+
* the daemon reads each linked issue under its own identity (`gh issue view`, at most 5) and appends
|
|
3932
|
+
* the title, state, body and comments to the problem the engine plans from, inside a marked block.
|
|
3933
|
+
* `resolved: false` names why (`error`) — shown, never skipped.
|
|
3934
|
+
*/
|
|
3935
|
+
export interface LinkedIssue {
|
|
3936
|
+
ref: string;
|
|
3937
|
+
resolved: boolean;
|
|
3938
|
+
/** Present when `resolved`. */
|
|
3939
|
+
title?: string;
|
|
3940
|
+
/** Present when not `resolved`: why, in one line. */
|
|
3941
|
+
error?: string;
|
|
3942
|
+
}
|
|
3943
|
+
|
|
3944
|
+
/** The `POST /runs` 201 body. `linkedIssues` (crew#627, additive) is present when the intent of a
|
|
3945
|
+
* workflow launch named at least one issue; absent otherwise and on an older daemon. */
|
|
3946
|
+
export interface LaunchRunResponse {
|
|
3947
|
+
runId: string;
|
|
3948
|
+
linkedIssues?: LinkedIssue[];
|
|
3949
|
+
}
|
|
3950
|
+
|
|
3535
3951
|
/**
|
|
3536
3952
|
* Body for `PUT /runs/:id/guidance` (DES-UX-002 §7.2, CREW-UX-7; api-types 0.9.0) — upsert the
|
|
3537
3953
|
* run's ONE durable operator guidance note. `text: ''` CLEARS the note (the DTO field returns
|
|
@@ -3663,12 +4079,20 @@ export interface PhaseDef {
|
|
|
3663
4079
|
* run `cmd` directly. */
|
|
3664
4080
|
executor?: PhaseExecutor;
|
|
3665
4081
|
/** Per-phase agent instructions folded into the unit prompt by the engine (core PhaseDef.instructions,
|
|
3666
|
-
* Option<String>). Absent = no extra instruction. Mirrors of core drop-ins that carry it
|
|
3667
|
-
*
|
|
4082
|
+
* Option<String>). Absent = no extra instruction. Mirrors of core drop-ins that carry it MUST
|
|
4083
|
+
* reproduce it verbatim or the runtime def diverges from core's — FINDING-011. */
|
|
3668
4084
|
instructions?: string | null;
|
|
3669
4085
|
gate_type: GateType | null;
|
|
3670
4086
|
gate: GateSpec;
|
|
3671
4087
|
executes_code: boolean;
|
|
4088
|
+
/**
|
|
4089
|
+
* The phase must REPORT what it captured (wicked-core#535 / BC-80; additive, engine ≥ the
|
|
4090
|
+
* release carrying it — an older engine's strict def parser refuses the key). Its output must
|
|
4091
|
+
* carry `wicked-capture-report {"derived": N, "submitted": M, "failed": K}`; a missing marker, a
|
|
4092
|
+
* failed submission and proposals derived but never submitted each deny the unit into the human
|
|
4093
|
+
* gate. An honest `0/0/0` passes. Absent/false ⇒ not parsed (every phase but `capture`).
|
|
4094
|
+
*/
|
|
4095
|
+
requires_capture_report?: boolean;
|
|
3672
4096
|
verified_evidence: boolean;
|
|
3673
4097
|
required_deliverables: string[];
|
|
3674
4098
|
depends_on: string[];
|
|
@@ -3695,6 +4119,29 @@ export interface WorkflowDef {
|
|
|
3695
4119
|
base_skill_ref?: string | null;
|
|
3696
4120
|
}
|
|
3697
4121
|
|
|
4122
|
+
/** A drop-in workflow definition the ENGINE refused, with the engine's own reason (wicked-crew#718). */
|
|
4123
|
+
export interface RefusedWorkflow {
|
|
4124
|
+
/** The def's id, as its file named it. */
|
|
4125
|
+
id: string;
|
|
4126
|
+
/**
|
|
4127
|
+
* wicked-core's refusal, verbatim — e.g. `gate evaluates nothing: write-a-note — the phase
|
|
4128
|
+
* declares executes_code but pins no validator and has no human gate, so its gate would approve
|
|
4129
|
+
* with nothing checked.`
|
|
4130
|
+
*/
|
|
4131
|
+
reason: string;
|
|
4132
|
+
}
|
|
4133
|
+
|
|
4134
|
+
/**
|
|
4135
|
+
* `GET /workflows` (wicked-crew#718). `workflows` is what the ENGINE accepted — the only defs a
|
|
4136
|
+
* launch can name. `unavailable` is the drop-ins it refused, with its reason, so a def that cannot
|
|
4137
|
+
* be launched is NAMED rather than offered and then 400'd `unknown workflow`. Absent on a daemon
|
|
4138
|
+
* older than the release carrying it (read it as `[]`).
|
|
4139
|
+
*/
|
|
4140
|
+
export interface WorkflowsResponse {
|
|
4141
|
+
workflows: WorkflowDef[];
|
|
4142
|
+
unavailable?: RefusedWorkflow[];
|
|
4143
|
+
}
|
|
4144
|
+
|
|
3698
4145
|
/** Top-level requirements_graph.json artifact (schema 1.0.0). */
|
|
3699
4146
|
export interface DomainGraph {
|
|
3700
4147
|
metadata: { schema_version: string; migration_mode: string; source?: string };
|
|
@@ -3768,6 +4215,24 @@ export interface CodeGraphData {
|
|
|
3768
4215
|
totals?: { nodes: number; edges: number; files: number };
|
|
3769
4216
|
}
|
|
3770
4217
|
|
|
4218
|
+
/**
|
|
4219
|
+
* `GET /api/v1/repos/:id/deliver-target` (R3) — where a delivering launch on this repo would push,
|
|
4220
|
+
* read by the same origin preflight the deliver gate card uses (crew#730). `sentence` is the gate's
|
|
4221
|
+
* own target sentence for a run not yet started ("the run branch"); show it verbatim.
|
|
4222
|
+
* - `github` — a github.com origin; `githubRepo` is its `owner/repo` on github.com or
|
|
4223
|
+
* ssh.github.com, and `null` on another `*.github.com` host (gist, api);
|
|
4224
|
+
* - `local` — a filesystem path or `file://` URL: no pull request can be opened against it;
|
|
4225
|
+
* - `other` — another host: a pull request opens only if gh resolves it as a GitHub host;
|
|
4226
|
+
* - `none` — the checkout has no `origin` remote: the push will fail;
|
|
4227
|
+
* - `unknown` — the origin could not be read; the sentence claims nothing either way.
|
|
4228
|
+
*/
|
|
4229
|
+
export interface DeliverTargetResponse {
|
|
4230
|
+
repo: string;
|
|
4231
|
+
origin: 'github' | 'local' | 'other' | 'none' | 'unknown';
|
|
4232
|
+
githubRepo: string | null;
|
|
4233
|
+
sentence: string;
|
|
4234
|
+
}
|
|
4235
|
+
|
|
3771
4236
|
/**
|
|
3772
4237
|
* `GET /repos/:id/graph` → 200 (api-types 0.35.0, F-2R2-005). `graph: null` = the repo's code
|
|
3773
4238
|
* graph has not been built (nothing at its registered graph path) — and `reason` says WHY, so a
|
|
@@ -3800,6 +4265,12 @@ export interface GitContributor {
|
|
|
3800
4265
|
export interface SystemSettings {
|
|
3801
4266
|
/** Max nodes returned by wicked-estate graph-view (default 150). */
|
|
3802
4267
|
graphNodeLimit: number;
|
|
4268
|
+
/**
|
|
4269
|
+
* The watch registry's operator settings (TR-W5a, api-types 0.73.0): only `enabled` and
|
|
4270
|
+
* `threshold` of a shipped entry, and the LLM lane switch. A `watch` patch needs a human actor
|
|
4271
|
+
* (a worker's bearer is refused 403); the daemon stamps who changed what and when.
|
|
4272
|
+
*/
|
|
4273
|
+
watch?: WatchSettings;
|
|
3803
4274
|
/**
|
|
3804
4275
|
* Base directory for the ENGINE-OWNED worker CLI config homes (the claude worker home is
|
|
3805
4276
|
* `<root>/claude`). The daemon applies it as env `WICKED_WORKER_HOME` at boot and on every
|
|
@@ -3847,6 +4318,18 @@ export interface SystemSettings {
|
|
|
3847
4318
|
* `base_skill_refused`); the finding is an `error`.
|
|
3848
4319
|
*/
|
|
3849
4320
|
baseSkillPolicy?: 'warn' | 'require';
|
|
4321
|
+
/**
|
|
4322
|
+
* The DELIVER IDENTITY (crew#549; additive): the GitHub login the deliver phase must push as.
|
|
4323
|
+
* The deliver script reads `gh api user` AND asks git which credential it would use for the
|
|
4324
|
+
* remote's host, and REFUSES — before anything is staged, committed or pushed — when either
|
|
4325
|
+
* disagrees with this login (or, with nothing configured, when those two disagree with each
|
|
4326
|
+
* other). `''`/absent ⇒ the `GH_ACCOUNT` env var decides, and unset there means the phase
|
|
4327
|
+
* pushes as whatever login gh holds, said aloud on the gate card.
|
|
4328
|
+
*
|
|
4329
|
+
* A LOGIN ONLY — never a token. The credential itself stays in gh's keyring or in the daemon's
|
|
4330
|
+
* `GH_TOKEN`; `GET /settings` returns this login and no secret ever.
|
|
4331
|
+
*/
|
|
4332
|
+
deliverIdentityLogin?: string;
|
|
3850
4333
|
/**
|
|
3851
4334
|
* The stall watchdog's DETECTION threshold (crew#287; api-types 0.18.0 — previously a
|
|
3852
4335
|
* daemon-local extension): minutes a run in `executing` may go without ANY engine event on the
|
|
@@ -3992,6 +4475,60 @@ export interface ElicitationResponse {
|
|
|
3992
4475
|
content?: { response: string };
|
|
3993
4476
|
}
|
|
3994
4477
|
|
|
4478
|
+
// ── Presets (DES-TEAMING-002 §8.4, seam C2) — saved phase selections ──────────
|
|
4479
|
+
|
|
4480
|
+
/**
|
|
4481
|
+
* One plan step: the catalog entry it instantiates (`understand`, `build`, …), its phase id,
|
|
4482
|
+
* and the step fields the catalog lets a step set (`gate`, `validator_pin`, `depends_on`, …).
|
|
4483
|
+
* The engine validates it (`plan::compose`); unknown keys are refused there.
|
|
4484
|
+
*/
|
|
4485
|
+
export interface PresetStep {
|
|
4486
|
+
catalog: string;
|
|
4487
|
+
id: string;
|
|
4488
|
+
[k: string]: unknown;
|
|
4489
|
+
}
|
|
4490
|
+
|
|
4491
|
+
/**
|
|
4492
|
+
* A preset (`GET /presets`, `PUT /presets/:name`): a named phase selection stored in the core
|
|
4493
|
+
* store. A launch's `workflow` names one (`POST /runs {workflow}`); a project-scoped preset
|
|
4494
|
+
* shadows a global one of the same name for that project. Built-ins carry
|
|
4495
|
+
* `created_by: "builtin"` and cannot be deleted.
|
|
4496
|
+
*/
|
|
4497
|
+
export interface Preset {
|
|
4498
|
+
name: string;
|
|
4499
|
+
/** `"global"` or `"project:<id>"`. */
|
|
4500
|
+
scope: string;
|
|
4501
|
+
steps: PresetStep[];
|
|
4502
|
+
/** `"builtin"` for a seeded built-in; otherwise the writing surface. */
|
|
4503
|
+
created_by: string;
|
|
4504
|
+
/** Unix millis. */
|
|
4505
|
+
updated_at: number;
|
|
4506
|
+
/**
|
|
4507
|
+
* A machine-owned preset (`chat`, `onboarding`, …): keep it off delivery surfaces and the work-mode
|
|
4508
|
+
* selector. The preset's half of the one name-keyed classification that stamps
|
|
4509
|
+
* `WorkflowDef.is_system` and `run_identity.system` (DES-TEAMING-002 §11.3). The daemon stamps it
|
|
4510
|
+
* on every preset it serves (api-types 0.48.0); absent from an older daemon.
|
|
4511
|
+
*/
|
|
4512
|
+
system?: boolean;
|
|
4513
|
+
[k: string]: unknown;
|
|
4514
|
+
}
|
|
4515
|
+
|
|
4516
|
+
/** `PUT /presets/:name` body. `projectId` absent ⇒ a global preset. */
|
|
4517
|
+
export interface PutPresetBody {
|
|
4518
|
+
steps: PresetStep[];
|
|
4519
|
+
projectId?: string;
|
|
4520
|
+
}
|
|
4521
|
+
|
|
4522
|
+
/** `GET /presets` (`?projectId=`) — the presets a launch in that project sees, by name. */
|
|
4523
|
+
export interface PresetListResponse {
|
|
4524
|
+
presets: Preset[];
|
|
4525
|
+
}
|
|
4526
|
+
|
|
4527
|
+
/** `GET /presets/:name`, `PUT /presets/:name`. */
|
|
4528
|
+
export interface PresetResponse {
|
|
4529
|
+
preset: Preset;
|
|
4530
|
+
}
|
|
4531
|
+
|
|
3995
4532
|
// ── Projects (DES-PROJECT-001) — the experience-plane keystone ─────────────────
|
|
3996
4533
|
|
|
3997
4534
|
/** Project lifecycle: `active ⇄ archived`, no hard delete (ADR §1.3). */
|
|
@@ -4627,6 +5164,60 @@ export type ChatSeatRefusedFrame = {
|
|
|
4627
5164
|
project_id?: string;
|
|
4628
5165
|
};
|
|
4629
5166
|
|
|
5167
|
+
// ── Chat citations (crew#561, F-RC1-117; api-types 0.67.0) ───────────────────
|
|
5168
|
+
//
|
|
5169
|
+
// A seat's answer cites paths, `path:line` / `path:symbol` refs and commit SHAs, and nothing used
|
|
5170
|
+
// to check them: on the RC1 Phase 6 re-run 2 of 26 cited SHAs existed in no repo and three line
|
|
5171
|
+
// refs were off, rendered as plain text a reader could not tell apart from the real ones. The
|
|
5172
|
+
// daemon verifies each citation against the very read roots it handed the seats and publishes the
|
|
5173
|
+
// verdicts; the seat's TEXT is never edited — a skin MARKS it from these items.
|
|
5174
|
+
|
|
5175
|
+
/** What class of thing a citation is. */
|
|
5176
|
+
export type ChatCitationKind = 'path' | 'line' | 'symbol' | 'sha';
|
|
5177
|
+
|
|
5178
|
+
/**
|
|
5179
|
+
* The verdict on one citation.
|
|
5180
|
+
* - `verified` — it exists, exactly as cited;
|
|
5181
|
+
* - `corrected` — it exists, but not where the reply said (`resolved` is the real place);
|
|
5182
|
+
* - `unverified` — it is not in the chat's scope (the fabricated-SHA case);
|
|
5183
|
+
* - `unchecked` — the bounded pass did not reach it. NOT a claim either way.
|
|
5184
|
+
*/
|
|
5185
|
+
export type ChatCitationStatus = 'verified' | 'corrected' | 'unverified' | 'unchecked';
|
|
5186
|
+
|
|
5187
|
+
/** One citation as verified. `raw` is the token EXACTLY as it appears in the reply, so a skin can
|
|
5188
|
+
* find it in the rendered text without re-parsing. */
|
|
5189
|
+
export interface ChatCitationItem {
|
|
5190
|
+
raw: string;
|
|
5191
|
+
kind: ChatCitationKind;
|
|
5192
|
+
status: ChatCitationStatus;
|
|
5193
|
+
/** Where the thing actually is: `alpha/src/foo.ts:2069` for a corrected line ref, the repo name
|
|
5194
|
+
* for a SHA, the defining line for a symbol. Absent when there is nothing to add. */
|
|
5195
|
+
resolved?: string;
|
|
5196
|
+
/** Why, in one short phrase — a skin's hover text. */
|
|
5197
|
+
note?: string;
|
|
5198
|
+
}
|
|
5199
|
+
|
|
5200
|
+
/**
|
|
5201
|
+
* DAEMON-SYNTHETIC (like {@link ChatSeatRefusedFrame}): ONE frame per terminal `chatReply` of a
|
|
5202
|
+
* chat that has read roots, broadcast AFTER that reply (verification never delays an answer) and
|
|
5203
|
+
* stamped with the reply's own `turn_id`. ABSENT — never empty — when the reply cites nothing, when
|
|
5204
|
+
* the chat has no read roots to verify against, and for a not-ok reply (a refusal or eviction line
|
|
5205
|
+
* cites nothing a reader would paste). The counts match `items`; `unverifiable` counts `unverified`
|
|
5206
|
+
* only, so `unchecked` is never read as a fabrication.
|
|
5207
|
+
*/
|
|
5208
|
+
export type ChatCitationsFrame = {
|
|
5209
|
+
type: 'chatCitations';
|
|
5210
|
+
chat: string;
|
|
5211
|
+
cliKey: string;
|
|
5212
|
+
turn_id?: string;
|
|
5213
|
+
verified: number;
|
|
5214
|
+
unverifiable: number;
|
|
5215
|
+
corrected: number;
|
|
5216
|
+
unchecked: number;
|
|
5217
|
+
items: ChatCitationItem[];
|
|
5218
|
+
project_id?: string;
|
|
5219
|
+
};
|
|
5220
|
+
|
|
4630
5221
|
/** One live chat on `GET /chats` (FINDING-027 gap 4; scope fields with crew#502). */
|
|
4631
5222
|
export interface ChatSummary {
|
|
4632
5223
|
chatId: string;
|
|
@@ -4670,7 +5261,25 @@ export interface ChatUsage {
|
|
|
4670
5261
|
*/
|
|
4671
5262
|
export type ChatTranscriptRecord =
|
|
4672
5263
|
| { at: number; turnId: string; kind: 'user'; text: string; seats: string[] }
|
|
4673
|
-
| { at: number; turnId: string; kind: 'seat'; cliKey: string; text: string; ok: boolean; usage: ChatUsage | null }
|
|
5264
|
+
| { at: number; turnId: string; kind: 'seat'; cliKey: string; text: string; ok: boolean; usage: ChatUsage | null }
|
|
5265
|
+
/**
|
|
5266
|
+
* The citation verdicts of the `seat` record with the same `turnId` + `cliKey` (api-types 0.68.0,
|
|
5267
|
+
* crew#561) — a SEPARATE record because the transcript is append-only and verification finishes
|
|
5268
|
+
* after the reply is stored. A reader folds it onto that reply; a skin that does not know this
|
|
5269
|
+
* kind ignores it (the `kind` discriminant is the extension point). Present only for a reply that
|
|
5270
|
+
* cited something; the live frame is {@link ChatCitationsFrame}, with the same fields.
|
|
5271
|
+
*/
|
|
5272
|
+
| {
|
|
5273
|
+
at: number;
|
|
5274
|
+
turnId: string;
|
|
5275
|
+
kind: 'citations';
|
|
5276
|
+
cliKey: string;
|
|
5277
|
+
verified: number;
|
|
5278
|
+
unverifiable: number;
|
|
5279
|
+
corrected: number;
|
|
5280
|
+
unchecked: number;
|
|
5281
|
+
items: ChatCitationItem[];
|
|
5282
|
+
};
|
|
4674
5283
|
|
|
4675
5284
|
/** `GET /chats/:id` → 200. */
|
|
4676
5285
|
export interface ChatDetailResponse {
|
|
@@ -5045,8 +5654,12 @@ export interface Campaign {
|
|
|
5045
5654
|
* `delivery === 'delivered'`.
|
|
5046
5655
|
*/
|
|
5047
5656
|
export interface CampaignNodeDelivery {
|
|
5048
|
-
delivery: 'delivered' | 'stranded' | 'vacuous' | 'none';
|
|
5657
|
+
delivery: 'delivered' | 'pushed' | 'stranded' | 'vacuous' | 'none';
|
|
5049
5658
|
deliverUrl?: string;
|
|
5659
|
+
/** Present exactly when `delivery === 'pushed'` (api-types 0.69.0, N1). */
|
|
5660
|
+
deliverBranch?: string;
|
|
5661
|
+
/** Present exactly when `delivery === 'pushed'`; userinfo removed. */
|
|
5662
|
+
deliverRemote?: string;
|
|
5050
5663
|
}
|
|
5051
5664
|
|
|
5052
5665
|
/**
|
|
@@ -5059,8 +5672,12 @@ export interface CampaignNodeDelivery {
|
|
|
5059
5672
|
export interface AttachedRunView {
|
|
5060
5673
|
runId: string;
|
|
5061
5674
|
status: SessionStatus;
|
|
5062
|
-
delivery: 'delivered' | 'stranded' | 'vacuous' | 'none';
|
|
5675
|
+
delivery: 'delivered' | 'pushed' | 'stranded' | 'vacuous' | 'none';
|
|
5063
5676
|
deliverUrl?: string;
|
|
5677
|
+
/** Present exactly when `delivery === 'pushed'` (api-types 0.69.0, N1). */
|
|
5678
|
+
deliverBranch?: string;
|
|
5679
|
+
/** Present exactly when `delivery === 'pushed'`; userinfo removed. */
|
|
5680
|
+
deliverRemote?: string;
|
|
5064
5681
|
}
|
|
5065
5682
|
|
|
5066
5683
|
/**
|
|
@@ -5248,6 +5865,9 @@ export interface AcpCliDiagnostics {
|
|
|
5248
5865
|
lastStartedTs: number | null;
|
|
5249
5866
|
/** Epoch ms of the newest `acpFallback`, or `null` when none recorded. */
|
|
5250
5867
|
lastFallbackTs: number | null;
|
|
5868
|
+
/** (core#581) The seat's ACP version pin, probed now — present only for a pinned seat, which
|
|
5869
|
+
* lists here even with no ACP events yet (zero counts). */
|
|
5870
|
+
versionPin?: SeatVersionPin;
|
|
5251
5871
|
}
|
|
5252
5872
|
|
|
5253
5873
|
/** The ACP fold of `GET /diagnostics` — keyed by `cliKey` (`claude`, `pi`, `codex`, …). */
|
|
@@ -5364,6 +5984,36 @@ export interface DiagnosticsGovernance {
|
|
|
5364
5984
|
findings: DiagnosticsGovernanceFinding[];
|
|
5365
5985
|
}
|
|
5366
5986
|
|
|
5987
|
+
/** `POST /governance/deadletters/replay` body (api-types 0.51.0): replay THIS daemon's dead-letter
|
|
5988
|
+
* outbox into its own governance store. `dryRun: true` folds the outbox and moves nothing. */
|
|
5989
|
+
export interface GovernanceReplayBody {
|
|
5990
|
+
dryRun?: boolean;
|
|
5991
|
+
}
|
|
5992
|
+
|
|
5993
|
+
/** `POST /governance/deadletters/replay` answer (api-types 0.51.0) — the `governance replay` CLI's
|
|
5994
|
+
* outcome. A dry run carries `fold` (what the outbox holds) and `blocker`: why a real replay could
|
|
5995
|
+
* not run on this daemon (in-memory store, an engine without the binding), `null` when it can. A
|
|
5996
|
+
* real replay reports what landed (`replayed`), what an earlier replay had already landed
|
|
5997
|
+
* (`alreadyPresent`, `null` on an engine that does not say) and what did not (`failed` — those
|
|
5998
|
+
* entries are back on the live outbox and stay dead letters). 409: no governance store / a replay
|
|
5999
|
+
* already running / a blocker; 501: the engine cannot replay. */
|
|
6000
|
+
export interface GovernanceReplayOutcome {
|
|
6001
|
+
outbox: string;
|
|
6002
|
+
store: { path: string; source: DiagnosticsGovernanceStoreSource };
|
|
6003
|
+
/** Where a real replay archived the drained outbox; `null` on a dry run. */
|
|
6004
|
+
archive: string | null;
|
|
6005
|
+
read: number;
|
|
6006
|
+
replayed: number;
|
|
6007
|
+
alreadyPresent: number | null;
|
|
6008
|
+
failed: number;
|
|
6009
|
+
dryRun: boolean;
|
|
6010
|
+
/** The same-event conflation caveat the CLI prints, or `null`. */
|
|
6011
|
+
note: string | null;
|
|
6012
|
+
/** Dry run only: the outbox fold (the `/diagnostics` dead-letter block minus the legacy pointer). */
|
|
6013
|
+
fold?: Omit<DiagnosticsGovernanceDeadletters, 'legacyOutbox'>;
|
|
6014
|
+
blocker: string | null;
|
|
6015
|
+
}
|
|
6016
|
+
|
|
5367
6017
|
/** The skills seam's state as `GET /diagnostics` reports it (skills keystone, api-types 0.28.0). */
|
|
5368
6018
|
export type DiagnosticsSkillsState = 'published' | 'fallback' | 'blocked' | 'config-error' | 'disabled';
|
|
5369
6019
|
|
|
@@ -5670,9 +6320,219 @@ export interface PolicyLandingResult {
|
|
|
5670
6320
|
* predating the policy→steering landing (forward-additive, DES-MEM-FACETED-001 §5.2).
|
|
5671
6321
|
*/
|
|
5672
6322
|
export type ApproveProposalResponse =
|
|
5673
|
-
| { outcome: 'promoted'; active_id: string }
|
|
6323
|
+
| { outcome: 'promoted'; active_id: string; edited?: ProposalEdit }
|
|
5674
6324
|
| { outcome: 'handed_off'; payload: unknown; landing?: PolicyLandingResult };
|
|
5675
6325
|
|
|
6326
|
+
/**
|
|
6327
|
+
* `POST /proposals/:id/approve` body (api-types 0.49.0) — present only for an accept WITH an edit;
|
|
6328
|
+
* no body (or `{}`) is the plain approve. estate has no proposal edit, so the daemon submits the
|
|
6329
|
+
* edited copy to the same queue (`payload.edited_from = <id>`), approves it, then rejects the
|
|
6330
|
+
* original. Memory proposals only (400 otherwise; 404 when `id` is not pending; 409 while another
|
|
6331
|
+
* accept of the same `id` is in flight).
|
|
6332
|
+
*/
|
|
6333
|
+
export interface ApproveProposalBody {
|
|
6334
|
+
/** The edited memory text (replaces `payload.content`). */
|
|
6335
|
+
content?: string;
|
|
6336
|
+
/** `"pattern"`: the copy drops its `project`/`repo` facets, so it may be recalled on ANY project
|
|
6337
|
+
* (the human act that lets a pattern cross projects; refused on anything but a captured memory, `payload.capture: "memory"`).
|
|
6338
|
+
* `"project"`: it stays scoped to its project — gaining the proposing run's project as its
|
|
6339
|
+
* `project` facet when the worker filed none (400 when no project files that run). An accept
|
|
6340
|
+
* that changes nothing the store keeps approves the original as filed (no copy, no `edited`). */
|
|
6341
|
+
reach?: 'project' | 'pattern';
|
|
6342
|
+
}
|
|
6343
|
+
|
|
6344
|
+
/**
|
|
6345
|
+
* `POST /proposals` body (api-types 0.54.0) — a skin files ONE preference into the review queue
|
|
6346
|
+
* (studio's takes: "prefers takes like X", filed when a person picks one of two candidate outputs).
|
|
6347
|
+
* The daemon files it as `kind_type: "memory"` with `payload {content, tier: "semantic",
|
|
6348
|
+
* capture: "preference", source?}`; the kind is never the caller's (unknown keys are a 400).
|
|
6349
|
+
* Nothing is learned until the person accepts it on `/proposals`.
|
|
6350
|
+
*/
|
|
6351
|
+
export interface FileProposalBody {
|
|
6352
|
+
/** The preference in words (1–4000 chars, trimmed). */
|
|
6353
|
+
content: string;
|
|
6354
|
+
/** Becomes the `project` facet, so the preference is recalled on that project only. */
|
|
6355
|
+
project?: string;
|
|
6356
|
+
/** Where the pick was made, e.g. `doc:<docId>@v3` (kept on the payload). */
|
|
6357
|
+
source?: string;
|
|
6358
|
+
}
|
|
6359
|
+
|
|
6360
|
+
/** `POST /proposals` → 201: the pending proposal's id. */
|
|
6361
|
+
export interface FileProposalResponse {
|
|
6362
|
+
id: string;
|
|
6363
|
+
}
|
|
6364
|
+
|
|
6365
|
+
/** An accept-with-edit's trail: the original proposal (now rejected) and the approved copy. */
|
|
6366
|
+
export interface ProposalEdit {
|
|
6367
|
+
from: string;
|
|
6368
|
+
to: string;
|
|
6369
|
+
/** The copy was approved but rejecting the original failed: it is still pending, for the person
|
|
6370
|
+
* to reject. Absent when the original was rejected. */
|
|
6371
|
+
originalPending?: true;
|
|
6372
|
+
}
|
|
6373
|
+
|
|
6374
|
+
// ── Capture (Studio OS behaviour 8, api-types 0.49.0) ─────────────────────────────
|
|
6375
|
+
|
|
6376
|
+
/** One dropped text file (a transcript, meeting notes). */
|
|
6377
|
+
export interface CaptureTextFile {
|
|
6378
|
+
name: string;
|
|
6379
|
+
text: string;
|
|
6380
|
+
}
|
|
6381
|
+
|
|
6382
|
+
/** One dropped photo, read by a vision-capable seat. `mediaType` is image/png, image/jpeg,
|
|
6383
|
+
* image/gif or image/webp. */
|
|
6384
|
+
export interface CaptureImageFile {
|
|
6385
|
+
name: string;
|
|
6386
|
+
mediaType: string;
|
|
6387
|
+
dataBase64: string;
|
|
6388
|
+
}
|
|
6389
|
+
|
|
6390
|
+
/**
|
|
6391
|
+
* `POST /projects/:id/capture` body: notes and/or files (at least one). The daemon lands them in a
|
|
6392
|
+
* per-run inbox and launches a repo-less run filed to the project (through `POST /runs`, so its
|
|
6393
|
+
* refusals are that route's). The run's output is proposals in the `/proposals` queue — each with
|
|
6394
|
+
* `kind_type: "memory"` (or `"policy:<type>"` for a rule), `payload.capture` = `intent` |
|
|
6395
|
+
* `decision` | `memory` | `rule`, `provenance.run_id` = the returned `runId`, and `facets.project`
|
|
6396
|
+
* unless the team marked a memory `payload.reach: "pattern"` (the user decides on accept).
|
|
6397
|
+
*/
|
|
6398
|
+
export interface CaptureBody {
|
|
6399
|
+
notes?: string;
|
|
6400
|
+
files?: Array<CaptureTextFile | CaptureImageFile>;
|
|
6401
|
+
}
|
|
6402
|
+
|
|
6403
|
+
/** `POST /projects/:id/capture` → 201. */
|
|
6404
|
+
export interface CaptureResponse {
|
|
6405
|
+
runId: string;
|
|
6406
|
+
}
|
|
6407
|
+
|
|
6408
|
+
// ── The Demo experience (wicked-studio#373, api-types 0.61.0) ────────────────────────────────
|
|
6409
|
+
|
|
6410
|
+
/**
|
|
6411
|
+
* `POST /projects/:id/demo` body: the app to film, who the demo is for and what to show. The daemon
|
|
6412
|
+
* mints the run's demo root, writes the brief there and launches the built-in `demo` preset (the
|
|
6413
|
+
* wicked-garden demo skill's plan → record → review) through `POST /runs`, so its refusals are that
|
|
6414
|
+
* route's. The demo root is the run's only extra write root; a caller never names it.
|
|
6415
|
+
*/
|
|
6416
|
+
export interface DemoLaunchBody {
|
|
6417
|
+
/** The running app (http or https). The recorder is read-only against it. */
|
|
6418
|
+
url: string;
|
|
6419
|
+
audience: string;
|
|
6420
|
+
show: string;
|
|
6421
|
+
/** The roster to seat the team from, as `POST /runs` takes it. Omit for the daemon's roster. */
|
|
6422
|
+
clisJson?: string;
|
|
6423
|
+
}
|
|
6424
|
+
|
|
6425
|
+
/** `POST /projects/:id/demo` → 201. */
|
|
6426
|
+
export interface DemoLaunchResponse {
|
|
6427
|
+
runId: string;
|
|
6428
|
+
}
|
|
6429
|
+
|
|
6430
|
+
/** `PUT /runs/:id/demo/script` body: the presenter's edit of `script.md` (plan gate only; ≤ 256 KB). */
|
|
6431
|
+
export interface DemoScriptBody {
|
|
6432
|
+
content: string;
|
|
6433
|
+
}
|
|
6434
|
+
|
|
6435
|
+
/**
|
|
6436
|
+
* Where a demo run stands. `preparing` covers the PA's scope step and any phase the floor added before
|
|
6437
|
+
* `plan`; `team_gate` is the team plan's approval gate (`plan_approval`: the run holds a plan rev past
|
|
6438
|
+
* the accepted one), which opens before `plan` runs and is answered like any gate; `plan_gate` and `review_gate` are the run's human gates (answered through `POST /runs/:id/gate`:
|
|
6439
|
+
* approve, or `action: 'request_changes'` with the note as `amend` — at the plan gate it re-runs `plan`,
|
|
6440
|
+
* at the review gate it rewinds to `record`). The review gate is the one after `review` passed, or the
|
|
6441
|
+
* engine's escalation gate when the review did not pass (`review.rejected`).
|
|
6442
|
+
*/
|
|
6443
|
+
export type DemoStage =
|
|
6444
|
+
| 'preparing'
|
|
6445
|
+
| 'team_gate'
|
|
6446
|
+
| 'planning'
|
|
6447
|
+
| 'plan_gate'
|
|
6448
|
+
| 'recording'
|
|
6449
|
+
| 'reviewing'
|
|
6450
|
+
| 'review_gate'
|
|
6451
|
+
| 'done'
|
|
6452
|
+
| 'failed';
|
|
6453
|
+
|
|
6454
|
+
/** One chapter of the plan's `chapters.json`, with whether its segment has been recorded. */
|
|
6455
|
+
export interface DemoChapter {
|
|
6456
|
+
/** `NN-slug` — the segment key record.mjs re-records by. */
|
|
6457
|
+
key: string;
|
|
6458
|
+
title: string;
|
|
6459
|
+
blurb: string;
|
|
6460
|
+
tags: string[];
|
|
6461
|
+
resets: string[];
|
|
6462
|
+
recorded: boolean;
|
|
6463
|
+
}
|
|
6464
|
+
|
|
6465
|
+
/** A chapter marker of the stitched MP4 (record.mjs's `chapters.md`). */
|
|
6466
|
+
export interface DemoMarker {
|
|
6467
|
+
/** `m:ss`. */
|
|
6468
|
+
at: string;
|
|
6469
|
+
sec: number;
|
|
6470
|
+
title: string;
|
|
6471
|
+
}
|
|
6472
|
+
|
|
6473
|
+
/** One finding of the reviewer's verdict block. */
|
|
6474
|
+
export interface DemoFinding {
|
|
6475
|
+
at: string;
|
|
6476
|
+
chapter: string;
|
|
6477
|
+
issue: string;
|
|
6478
|
+
verdict: 're-encode' | 're-record' | 'fix-app';
|
|
6479
|
+
}
|
|
6480
|
+
|
|
6481
|
+
/** `GET /runs/:id/demo` → 200 (404 for a run that is not a demo run). */
|
|
6482
|
+
export interface DemoView {
|
|
6483
|
+
runId: string;
|
|
6484
|
+
/** From the brief the daemon wrote. */
|
|
6485
|
+
url: string | null;
|
|
6486
|
+
audience: string | null;
|
|
6487
|
+
stage: DemoStage;
|
|
6488
|
+
/** `script.md` — the presenter script (null until `plan` writes it). */
|
|
6489
|
+
script: string | null;
|
|
6490
|
+
chapters: DemoChapter[];
|
|
6491
|
+
markers: DemoMarker[];
|
|
6492
|
+
/** Root-relative paths for `GET /runs/:id/demo/file?path=`. */
|
|
6493
|
+
sheets: Array<{ name: 'chapters' | 'joins' | 'end'; path: string }>;
|
|
6494
|
+
video: { path: string; bytes: number } | null;
|
|
6495
|
+
/** From the recorder's `recording.json`: true only when every stitched segment was recorded read-only;
|
|
6496
|
+
* null before a stitch (or from a recorder without the guard). */
|
|
6497
|
+
recording: { readOnly: boolean | null };
|
|
6498
|
+
/**
|
|
6499
|
+
* The reviewer's last output and its parsed verdict block (empty until `review` has finished).
|
|
6500
|
+
* `rejected`: the engine judged the review NOT PASS, so the review gate is its escalation gate — a
|
|
6501
|
+
* failed review cannot be accepted: approve re-runs the reviewer, `request_changes` sends the note back
|
|
6502
|
+
* to `record`, reject cancels the run.
|
|
6503
|
+
*/
|
|
6504
|
+
review: { verdict: 'accept' | 'changes' | null; findings: DemoFinding[]; text: string | null; rejected: boolean };
|
|
6505
|
+
/** The seats that recorded and reviewed — evaluator ≠ creator puts them apart. */
|
|
6506
|
+
seats: { recorder: string | null; reviewer: string | null };
|
|
6507
|
+
/** The script says which data is synthetic. */
|
|
6508
|
+
syntheticLabelled: boolean;
|
|
6509
|
+
}
|
|
6510
|
+
|
|
6511
|
+
/** What `POST /runs/:id/demo/export` makes (EP-C3; api-types 0.72.0). The MP4 itself needs no export: download `video.path`. */
|
|
6512
|
+
export type DemoExportFormat = 'gif' | 'poster';
|
|
6513
|
+
|
|
6514
|
+
/**
|
|
6515
|
+
* `POST /runs/:id/demo/export` body (EP-C3; api-types 0.72.0). `gif` encodes the stitched MP4 into
|
|
6516
|
+
* `demo-video/demo.gif` (two-pass palette, 10 fps, 720 px wide); `poster` writes one frame to
|
|
6517
|
+
* `demo-video/poster.jpg`. The daemon encodes with ffmpeg off its event loop and a timeout (GIF 180 s,
|
|
6518
|
+
* poster 30 s), only at the review gate or once the run has ended. Refusals: 400 a bad body, 404 not a demo run, 409 no stitched MP4 yet / the run is still recording / `demo-video/` changed mid-encode, 422 no frame
|
|
6519
|
+
* at `atSec`, 502 ffmpeg failed, 503 no ffmpeg on the daemon host (`hint` says how to install it),
|
|
6520
|
+
* 504 the encode ran past its timeout. A second identical request while one encodes joins it.
|
|
6521
|
+
*/
|
|
6522
|
+
export interface DemoExportBody {
|
|
6523
|
+
format: DemoExportFormat;
|
|
6524
|
+
/** Poster only: the frame, in seconds from the start (0 … 86400). Default: the first chapter marker + 1 s, or 1 s with no markers. */
|
|
6525
|
+
atSec?: number;
|
|
6526
|
+
}
|
|
6527
|
+
|
|
6528
|
+
/** `POST /runs/:id/demo/export` → 200: the file, ready for `GET /runs/:id/demo/file?path=`. */
|
|
6529
|
+
export interface DemoExportResponse {
|
|
6530
|
+
format: DemoExportFormat;
|
|
6531
|
+
/** `demo-video/demo.gif` or `demo-video/poster.jpg` (root-relative). */
|
|
6532
|
+
path: string;
|
|
6533
|
+
bytes: number;
|
|
6534
|
+
}
|
|
6535
|
+
|
|
5676
6536
|
/** `POST /proposals/:id/reject` → 200. */
|
|
5677
6537
|
export interface RejectProposalResponse {
|
|
5678
6538
|
ok: true;
|
|
@@ -5774,16 +6634,1703 @@ export interface MemoryCoverageResponse {
|
|
|
5774
6634
|
}
|
|
5775
6635
|
|
|
5776
6636
|
/**
|
|
5777
|
-
* `POST /memory/retire` body.
|
|
5778
|
-
*
|
|
5779
|
-
*
|
|
5780
|
-
*
|
|
6637
|
+
* `POST /memory/retire` body. This retire is SUBTREE-scoped: estate `memory.erase` hard-deletes
|
|
6638
|
+
* EVERY memory whose scope equals or descends from `scope_prefix`. `scope_prefix` is required and
|
|
6639
|
+
* must be non-empty (estate refuses a total wipe) — the surface must show the operator the whole
|
|
6640
|
+
* subtree it will remove. To remove ONE row, use {@link RetireMemoryItemBody}.
|
|
5781
6641
|
*/
|
|
5782
6642
|
export interface RetireMemoryBody {
|
|
5783
6643
|
scope_prefix: string;
|
|
5784
6644
|
}
|
|
5785
6645
|
|
|
5786
|
-
/**
|
|
5787
|
-
|
|
6646
|
+
/**
|
|
6647
|
+
* `POST /memory/retire-item` body (studio#206; api-types 0.60.0): exactly ONE memory, by the
|
|
6648
|
+
* `memory_id` `GET /memory` listed. 404 when no memory has that id; 501 `estate_upgrade_required`
|
|
6649
|
+
* when the estate predates erase-by-id (wicked-estate#188) — nothing is deleted then.
|
|
6650
|
+
*/
|
|
6651
|
+
export interface RetireMemoryItemBody {
|
|
6652
|
+
memory_id: string;
|
|
6653
|
+
}
|
|
6654
|
+
|
|
6655
|
+
/** `POST /memory/retire` / `POST /memory/retire-item` → 200. How many memories were deleted
|
|
6656
|
+
* (always 1 for `retire-item`). */
|
|
6657
|
+
export interface RetireMemoryResponse {
|
|
5788
6658
|
erased: number;
|
|
5789
6659
|
}
|
|
6660
|
+
|
|
6661
|
+
// ── Team events on the bus (DES-TEAMING-002 §6, wicked-core T1; api-types 0.41.0) ──────────────
|
|
6662
|
+
//
|
|
6663
|
+
// The `wicked.team.*` facts wicked-core publishes on the cross-product bus (`domain`
|
|
6664
|
+
// `wicked-core`, `subdomain` `core.team`), one owner per type (DES-002 §7: E engine, S supervisor,
|
|
6665
|
+
// R attempt runner). Crew publishes none of them: it relays and reads. Mirrors wicked-core
|
|
6666
|
+
// `src/team/events.rs`; `packages/crew/tests/team-events.test.ts` pins every type against the
|
|
6667
|
+
// engine's own round-trip fixtures by value. Payload keys are snake_case and every key is
|
|
6668
|
+
// present (an absent value is `null`), except a plan step's optional fields, which a plan omits
|
|
6669
|
+
// when unset. The embedded {@link TeamLedger} keeps DES-001 §7's camelCase.
|
|
6670
|
+
|
|
6671
|
+
/** The 25 `wicked.team.<noun>.<verb>` event types. */
|
|
6672
|
+
export type TeamEventType =
|
|
6673
|
+
| 'wicked.team.path.started'
|
|
6674
|
+
| 'wicked.team.path.scored'
|
|
6675
|
+
| 'wicked.team.plan.proposed'
|
|
6676
|
+
| 'wicked.team.plan.revised'
|
|
6677
|
+
| 'wicked.team.plan.accepted'
|
|
6678
|
+
| 'wicked.team.plan.refused'
|
|
6679
|
+
| 'wicked.team.member.joined'
|
|
6680
|
+
| 'wicked.team.member.left'
|
|
6681
|
+
| 'wicked.team.step.claimed'
|
|
6682
|
+
| 'wicked.team.checkpoint.reached'
|
|
6683
|
+
| 'wicked.team.finding.raised'
|
|
6684
|
+
| 'wicked.team.advice.delivered'
|
|
6685
|
+
| 'wicked.team.advice.answered'
|
|
6686
|
+
| 'wicked.team.help.requested'
|
|
6687
|
+
| 'wicked.team.help.answered'
|
|
6688
|
+
| 'wicked.team.change.requested'
|
|
6689
|
+
| 'wicked.team.step.completed'
|
|
6690
|
+
| 'wicked.team.step.reviewed'
|
|
6691
|
+
| 'wicked.team.finding.settled'
|
|
6692
|
+
| 'wicked.team.council.called'
|
|
6693
|
+
| 'wicked.team.council.ruled'
|
|
6694
|
+
| 'wicked.team.ledger.folded'
|
|
6695
|
+
| 'wicked.team.gate.opened'
|
|
6696
|
+
| 'wicked.team.gate.decided'
|
|
6697
|
+
| 'wicked.team.path.ended';
|
|
6698
|
+
|
|
6699
|
+
/** The fields every team payload carries. */
|
|
6700
|
+
export interface TeamEnvelope {
|
|
6701
|
+
run_id: string;
|
|
6702
|
+
ord: number | null;
|
|
6703
|
+
attempt: number | null;
|
|
6704
|
+
/**
|
|
6705
|
+
* Who acted: a seated CLI instance (the PA, a member or the authoring monitor, e.g.
|
|
6706
|
+
* `'claude#1'`, `'claude#2'`, `'codex'`), `'engine'`, `'human'` or `'council:<task id>'`.
|
|
6707
|
+
*/
|
|
6708
|
+
by: string;
|
|
6709
|
+
/** Epoch milliseconds at the producer. */
|
|
6710
|
+
at: number;
|
|
6711
|
+
/** The row this one answers, e.g. `'finding.raised#4'`. */
|
|
6712
|
+
re: string | null;
|
|
6713
|
+
}
|
|
6714
|
+
|
|
6715
|
+
/** A plan step: a catalog entry plus the step fields a plan may set (omitted when unset). */
|
|
6716
|
+
export interface TeamPlanStep {
|
|
6717
|
+
catalog: string;
|
|
6718
|
+
id: string;
|
|
6719
|
+
instructions?: string;
|
|
6720
|
+
owner?: 'pa' | 'team' | (string & {});
|
|
6721
|
+
depends_on?: string[];
|
|
6722
|
+
/** A raised gate, as the workflow's externally tagged `GateSpec`. */
|
|
6723
|
+
gate?: unknown;
|
|
6724
|
+
added_by?: 'plan' | 'floor' | (string & {});
|
|
6725
|
+
floor_reason?: string;
|
|
6726
|
+
late?: boolean;
|
|
6727
|
+
}
|
|
6728
|
+
|
|
6729
|
+
export interface TeamPlanOverride {
|
|
6730
|
+
remove: string[];
|
|
6731
|
+
reason: string;
|
|
6732
|
+
}
|
|
6733
|
+
|
|
6734
|
+
export type TeamPathStartedPayload = TeamEnvelope & {
|
|
6735
|
+
cli: string;
|
|
6736
|
+
selection: 'chosen' | 'random' | (string & {});
|
|
6737
|
+
roster: string[];
|
|
6738
|
+
request: string;
|
|
6739
|
+
workflow: string | null;
|
|
6740
|
+
plan: boolean;
|
|
6741
|
+
};
|
|
6742
|
+
|
|
6743
|
+
export type TeamPathScoredPayload = TeamEnvelope & {
|
|
6744
|
+
/** `'intent:<proposal_id>'` | `'diff:<ord>:<attempt>:<rescore_seq>'`. */
|
|
6745
|
+
score_source: string;
|
|
6746
|
+
basis: 'intent' | 'diff' | (string & {});
|
|
6747
|
+
score: number;
|
|
6748
|
+
deterministic: number;
|
|
6749
|
+
reasons: string[];
|
|
6750
|
+
model: { add: number; rationale: string } | null;
|
|
6751
|
+
signals: {
|
|
6752
|
+
changed_symbols: number;
|
|
6753
|
+
dependents: number;
|
|
6754
|
+
products: number;
|
|
6755
|
+
contract_change: boolean;
|
|
6756
|
+
test_gap: number;
|
|
6757
|
+
critical: boolean;
|
|
6758
|
+
destructive: boolean;
|
|
6759
|
+
truncated: boolean;
|
|
6760
|
+
} | null;
|
|
6761
|
+
plan: { monitors: number; depth: string; post_hoc_reviewer: boolean; post_hoc_other_cli: boolean };
|
|
6762
|
+
tree: string | null;
|
|
6763
|
+
};
|
|
6764
|
+
|
|
6765
|
+
export type TeamPlanProposedPayload = TeamEnvelope & {
|
|
6766
|
+
proposal_id: string;
|
|
6767
|
+
base_rev: number | null;
|
|
6768
|
+
kind: 'initial' | 'change' | 'edit' | (string & {});
|
|
6769
|
+
preset: string | null;
|
|
6770
|
+
steps: TeamPlanStep[];
|
|
6771
|
+
monitors: { asked: number };
|
|
6772
|
+
asks: string[];
|
|
6773
|
+
touch: string[];
|
|
6774
|
+
override: TeamPlanOverride | null;
|
|
6775
|
+
rationale: string;
|
|
6776
|
+
};
|
|
6777
|
+
|
|
6778
|
+
export type TeamPlanRevisedPayload = TeamEnvelope & {
|
|
6779
|
+
plan_rev: number;
|
|
6780
|
+
proposal_id: string | null;
|
|
6781
|
+
reason: 'floor_raised' | 'pa_added' | 'member_request' | (string & {});
|
|
6782
|
+
from_band: string;
|
|
6783
|
+
to_band: string;
|
|
6784
|
+
high_risk: boolean;
|
|
6785
|
+
added: TeamPlanStep[];
|
|
6786
|
+
};
|
|
6787
|
+
|
|
6788
|
+
export type TeamPlanAcceptedPayload = TeamEnvelope & {
|
|
6789
|
+
plan_rev: number;
|
|
6790
|
+
workflow_id: string;
|
|
6791
|
+
band: string;
|
|
6792
|
+
high_risk: boolean;
|
|
6793
|
+
mode: 'auto' | 'manual' | (string & {});
|
|
6794
|
+
steps: TeamPlanStep[];
|
|
6795
|
+
override: TeamPlanOverride | null;
|
|
6796
|
+
proposal_id: string | null;
|
|
6797
|
+
};
|
|
6798
|
+
|
|
6799
|
+
export type TeamPlanRefusedPayload = TeamEnvelope & {
|
|
6800
|
+
proposal_id: string;
|
|
6801
|
+
base_rev: number | null;
|
|
6802
|
+
reason: string;
|
|
6803
|
+
};
|
|
6804
|
+
|
|
6805
|
+
export type TeamMemberJoinedPayload = TeamEnvelope & {
|
|
6806
|
+
member_id: string;
|
|
6807
|
+
open_seq: number;
|
|
6808
|
+
seat: string;
|
|
6809
|
+
role: 'monitor' | (string & {});
|
|
6810
|
+
status: 'attached' | 'failed' | (string & {});
|
|
6811
|
+
reason: string;
|
|
6812
|
+
error: string | null;
|
|
6813
|
+
};
|
|
6814
|
+
|
|
6815
|
+
export type TeamMemberLeftPayload = TeamEnvelope & {
|
|
6816
|
+
member_id: string;
|
|
6817
|
+
open_seq: number;
|
|
6818
|
+
seat: string;
|
|
6819
|
+
status: 'completed' | 'budget_exhausted' | 'failed' | 'timed_out' | (string & {});
|
|
6820
|
+
batches: number;
|
|
6821
|
+
error: string | null;
|
|
6822
|
+
};
|
|
6823
|
+
|
|
6824
|
+
export type TeamStepClaimedPayload = TeamEnvelope & {
|
|
6825
|
+
step_id: string;
|
|
6826
|
+
role: string;
|
|
6827
|
+
kind: string;
|
|
6828
|
+
phase: string;
|
|
6829
|
+
criterion: string;
|
|
6830
|
+
baseline_tree: string | null;
|
|
6831
|
+
repo: { workdir: string; git_dir: string } | null;
|
|
6832
|
+
code_graph_db: string | null;
|
|
6833
|
+
};
|
|
6834
|
+
|
|
6835
|
+
export type TeamCheckpointReachedPayload = TeamEnvelope & {
|
|
6836
|
+
seq: number;
|
|
6837
|
+
tool_call_id: string;
|
|
6838
|
+
kind: string;
|
|
6839
|
+
title: string;
|
|
6840
|
+
status: 'completed' | 'failed' | (string & {});
|
|
6841
|
+
paths: string[];
|
|
6842
|
+
};
|
|
6843
|
+
|
|
6844
|
+
export type TeamFindingRaisedPayload = TeamEnvelope & {
|
|
6845
|
+
raise_seq: number;
|
|
6846
|
+
finding_id: string;
|
|
6847
|
+
member_id: string;
|
|
6848
|
+
line_key: string | null;
|
|
6849
|
+
anchor: string | null;
|
|
6850
|
+
anchor_source: 'graph' | 'hunk' | 'none' | (string & {}) | null;
|
|
6851
|
+
severity: 'high' | 'medium' | (string & {});
|
|
6852
|
+
path: string;
|
|
6853
|
+
line: number;
|
|
6854
|
+
evidence: string;
|
|
6855
|
+
claim: string;
|
|
6856
|
+
suggestion: string | null;
|
|
6857
|
+
tree: string;
|
|
6858
|
+
in_diff: boolean;
|
|
6859
|
+
corroborated_by: string[];
|
|
6860
|
+
};
|
|
6861
|
+
|
|
6862
|
+
export type TeamAdviceDeliveredPayload = TeamEnvelope & {
|
|
6863
|
+
raise_seq: number;
|
|
6864
|
+
finding_id: string;
|
|
6865
|
+
/** The steer id | `'boundary:<step_id>:<attempt>'` | `'end:<attempt>'`. */
|
|
6866
|
+
delivery_id: string;
|
|
6867
|
+
steer_id: string | null;
|
|
6868
|
+
channel: 'acp_steering' | 'boundary' | 'none' | (string & {});
|
|
6869
|
+
outcome: 'injected' | 'turn_ended' | 'refused' | 'not_delivered' | (string & {});
|
|
6870
|
+
detail: string | null;
|
|
6871
|
+
};
|
|
6872
|
+
|
|
6873
|
+
export type TeamAdviceAnsweredPayload = TeamEnvelope & {
|
|
6874
|
+
raise_seq: number;
|
|
6875
|
+
/** `'<step_id>:<attempt>'`. */
|
|
6876
|
+
answered_in: string;
|
|
6877
|
+
finding_id: string;
|
|
6878
|
+
disposition: 'accepted' | 'declined' | (string & {});
|
|
6879
|
+
reason: string;
|
|
6880
|
+
};
|
|
6881
|
+
|
|
6882
|
+
export type TeamHelpRequestedPayload = TeamEnvelope & {
|
|
6883
|
+
help_id: string;
|
|
6884
|
+
help_seq: number;
|
|
6885
|
+
question: string;
|
|
6886
|
+
context: string;
|
|
6887
|
+
};
|
|
6888
|
+
|
|
6889
|
+
export type TeamHelpAnsweredPayload = TeamEnvelope & {
|
|
6890
|
+
help_id: string;
|
|
6891
|
+
answer_id: string;
|
|
6892
|
+
answer: string;
|
|
6893
|
+
evidence: string[];
|
|
6894
|
+
};
|
|
6895
|
+
|
|
6896
|
+
export type TeamChangeRequestedPayload = TeamEnvelope & {
|
|
6897
|
+
change_id: string;
|
|
6898
|
+
change_seq: number;
|
|
6899
|
+
steps: TeamPlanStep[];
|
|
6900
|
+
reason: string;
|
|
6901
|
+
};
|
|
6902
|
+
|
|
6903
|
+
export type TeamStepCompletedPayload = TeamEnvelope & {
|
|
6904
|
+
step_id: string;
|
|
6905
|
+
status: 'ok' | 'failed' | 'cancelled' | 'elicitation_failed' | 'timed_out' | (string & {});
|
|
6906
|
+
tree: string | null;
|
|
6907
|
+
output_bytes: number;
|
|
6908
|
+
output_ref: string;
|
|
6909
|
+
};
|
|
6910
|
+
|
|
6911
|
+
export type TeamStepReviewedPayload = TeamEnvelope & {
|
|
6912
|
+
step_id: string;
|
|
6913
|
+
verdict: 'accepted' | 'rejected' | (string & {});
|
|
6914
|
+
to: 'member' | 'pa' | (string & {}) | null;
|
|
6915
|
+
reason: string;
|
|
6916
|
+
};
|
|
6917
|
+
|
|
6918
|
+
export type TeamFindingSettledPayload = TeamEnvelope & {
|
|
6919
|
+
raise_seq: number;
|
|
6920
|
+
finding_id: string;
|
|
6921
|
+
status: 'held' | 'withdrawn' | 'superseded' | (string & {});
|
|
6922
|
+
reason: string;
|
|
6923
|
+
final_line: number | null;
|
|
6924
|
+
};
|
|
6925
|
+
|
|
6926
|
+
export type TeamCouncilCalledPayload = TeamEnvelope & {
|
|
6927
|
+
/** `'finding:<raise_seq>'` | `'step:<step_id>:<attempt>'`. */
|
|
6928
|
+
subject: string;
|
|
6929
|
+
finding_id: string | null;
|
|
6930
|
+
trigger: 'unresolved_high' | 'member_step' | (string & {});
|
|
6931
|
+
question: string;
|
|
6932
|
+
positions: { by: string; position: string; reason: string }[];
|
|
6933
|
+
evidence: string;
|
|
6934
|
+
excluded_seats: string[];
|
|
6935
|
+
transcript: number[];
|
|
6936
|
+
};
|
|
6937
|
+
|
|
6938
|
+
export type TeamCouncilRuledPayload = TeamEnvelope & {
|
|
6939
|
+
subject: string;
|
|
6940
|
+
verdict: 'yes' | 'no' | 'no_verdict' | (string & {});
|
|
6941
|
+
reason: 'no_quorum' | 'seats_benched' | 'error' | 'timeout' | 'cap' | (string & {}) | null;
|
|
6942
|
+
task_id: string | null;
|
|
6943
|
+
consensus: boolean;
|
|
6944
|
+
agreement_pct: number;
|
|
6945
|
+
dissent: string[];
|
|
6946
|
+
returned: number;
|
|
6947
|
+
seated: number;
|
|
6948
|
+
};
|
|
6949
|
+
|
|
6950
|
+
/** DES-001 §7 `teamLedger.findings[]` (camelCase). */
|
|
6951
|
+
export interface TeamLedgerFinding {
|
|
6952
|
+
findingId: string;
|
|
6953
|
+
monitorId: string;
|
|
6954
|
+
seat: string;
|
|
6955
|
+
severity: 'high' | 'medium' | (string & {});
|
|
6956
|
+
path: string;
|
|
6957
|
+
line: number;
|
|
6958
|
+
evidence: string;
|
|
6959
|
+
claim: string;
|
|
6960
|
+
suggestion: string | null;
|
|
6961
|
+
tree: string;
|
|
6962
|
+
inDiff: boolean;
|
|
6963
|
+
finalLine: number | null;
|
|
6964
|
+
corroboratedBy: string[];
|
|
6965
|
+
delivery: 'injected' | 'not_delivered' | (string & {});
|
|
6966
|
+
status: 'accepted' | 'declined' | 'withdrawn' | 'unanswered' | 'superseded' | (string & {});
|
|
6967
|
+
workerReason: string | null;
|
|
6968
|
+
monitorReply: { kind: 'hold' | 'withdraw' | (string & {}); reason: string } | null;
|
|
6969
|
+
dispute: {
|
|
6970
|
+
verdict: 'yes' | 'no' | 'no_verdict' | (string & {});
|
|
6971
|
+
agreementPct: number | null;
|
|
6972
|
+
dissent: number | null;
|
|
6973
|
+
seats: string[];
|
|
6974
|
+
reason: string | null;
|
|
6975
|
+
} | null;
|
|
6976
|
+
/**
|
|
6977
|
+
* (T6, api-types 0.42.0) The enclosing location the finding id is minted over (git's funcname
|
|
6978
|
+
* heuristic). Omitted when file-level.
|
|
6979
|
+
*/
|
|
6980
|
+
anchor?: string;
|
|
6981
|
+
/** (T6, api-types 0.42.0) The attempt that first raised it, when it was carried into a redriven attempt. Omitted otherwise. */
|
|
6982
|
+
carriedFromAttempt?: number;
|
|
6983
|
+
}
|
|
6984
|
+
|
|
6985
|
+
/**
|
|
6986
|
+
* (T6, DES-002 §8.8; api-types 0.42.0) The PA's review of a member's step, as the attempt that
|
|
6987
|
+
* carried the review recorded it.
|
|
6988
|
+
*/
|
|
6989
|
+
export interface TeamStepReview {
|
|
6990
|
+
stepId: string;
|
|
6991
|
+
/** The member attempt the review is about. */
|
|
6992
|
+
reviewedAttempt: number;
|
|
6993
|
+
verdict: 'accepted' | 'rejected' | (string & {});
|
|
6994
|
+
to: 'member' | 'pa' | (string & {}) | null;
|
|
6995
|
+
reason: string;
|
|
6996
|
+
/** On a rejection: `true` the member held (`HOLD`), `false` it accepted, `null` no answer on record. */
|
|
6997
|
+
held: boolean | null;
|
|
6998
|
+
memberReason: string | null;
|
|
6999
|
+
/** The one-off council a hold convened. */
|
|
7000
|
+
dispute: TeamLedgerFinding['dispute'];
|
|
7001
|
+
}
|
|
7002
|
+
|
|
7003
|
+
/** DES-001 §7 `teamLedger` without its envelope: the fold of one attempt's team stream. */
|
|
7004
|
+
export interface TeamLedger {
|
|
7005
|
+
finalPass: 'completed' | 'timed_out' | 'skipped' | 'stream_gap' | (string & {});
|
|
7006
|
+
renderedToJudge: boolean;
|
|
7007
|
+
teamPause: boolean;
|
|
7008
|
+
monitors: {
|
|
7009
|
+
monitorId: string;
|
|
7010
|
+
seat: string;
|
|
7011
|
+
batches: number;
|
|
7012
|
+
status: 'completed' | 'budget_exhausted' | 'failed' | 'timed_out' | (string & {});
|
|
7013
|
+
error: string | null;
|
|
7014
|
+
}[];
|
|
7015
|
+
findings: TeamLedgerFinding[];
|
|
7016
|
+
rejected: { malformed: number; belowBar: number; unconfirmed: number; duplicate: number };
|
|
7017
|
+
/** (T6, api-types 0.42.0) The PA's reviews of member steps this attempt carried. Omitted when none. */
|
|
7018
|
+
stepReviews?: TeamStepReview[];
|
|
7019
|
+
}
|
|
7020
|
+
|
|
7021
|
+
export type TeamLedgerFoldedPayload = TeamEnvelope & {
|
|
7022
|
+
final_pass: 'completed' | 'timed_out' | 'skipped' | 'stream_gap' | (string & {});
|
|
7023
|
+
ledger: TeamLedger;
|
|
7024
|
+
transport: 'bus' | 'none' | (string & {});
|
|
7025
|
+
transcript: {
|
|
7026
|
+
from_event_id: number;
|
|
7027
|
+
to_event_id: number;
|
|
7028
|
+
count: number;
|
|
7029
|
+
truncated: boolean;
|
|
7030
|
+
events: { event_id: number; event_type: string; payload: unknown }[];
|
|
7031
|
+
};
|
|
7032
|
+
};
|
|
7033
|
+
|
|
7034
|
+
/** `gate.opened`'s kind-specific fields, discriminated on `kind`. */
|
|
7035
|
+
export type TeamGateOpenedPayload = TeamEnvelope & { gate_id: string } & (
|
|
7036
|
+
| { kind: 'unit_review'; ledger_ref: string | null; ledger_source: 'folded' | 'synthesized' | 'no_bus' | (string & {}) }
|
|
7037
|
+
| {
|
|
7038
|
+
kind: 'plan_approval';
|
|
7039
|
+
reviewing_ord: number;
|
|
7040
|
+
plan_rev: number;
|
|
7041
|
+
band: string;
|
|
7042
|
+
high_risk: boolean;
|
|
7043
|
+
mode: 'auto' | 'manual' | (string & {});
|
|
7044
|
+
reason: 'manual_mode' | 'high_risk' | 'into_high_risk' | 'override' | (string & {});
|
|
7045
|
+
diff: { from_rev: number | null; added: string[] };
|
|
7046
|
+
}
|
|
7047
|
+
| { kind: 'team_dispute'; finding_ids: string[] }
|
|
7048
|
+
| { kind: 'team_transport'; fact: string; reason: string }
|
|
7049
|
+
);
|
|
7050
|
+
|
|
7051
|
+
export type TeamGateDecidedPayload = TeamEnvelope & {
|
|
7052
|
+
gate_id: string;
|
|
7053
|
+
kind: 'unit_review' | 'plan_approval' | 'team_dispute' | 'team_transport' | (string & {});
|
|
7054
|
+
decision:
|
|
7055
|
+
| 'allow'
|
|
7056
|
+
| 'deny'
|
|
7057
|
+
| 'paused'
|
|
7058
|
+
| 'human_approved'
|
|
7059
|
+
| 'human_amended'
|
|
7060
|
+
| 'human_rejected'
|
|
7061
|
+
| (string & {});
|
|
7062
|
+
combined: boolean | null;
|
|
7063
|
+
team_pause: boolean;
|
|
7064
|
+
unresolved: string[];
|
|
7065
|
+
};
|
|
7066
|
+
|
|
7067
|
+
export type TeamPathEndedPayload = TeamEnvelope & {
|
|
7068
|
+
status: 'completed' | 'failed' | 'cancelled' | (string & {});
|
|
7069
|
+
};
|
|
7070
|
+
|
|
7071
|
+
/** Each team event type's payload. */
|
|
7072
|
+
export interface TeamEventPayloads {
|
|
7073
|
+
'wicked.team.path.started': TeamPathStartedPayload;
|
|
7074
|
+
'wicked.team.path.scored': TeamPathScoredPayload;
|
|
7075
|
+
'wicked.team.plan.proposed': TeamPlanProposedPayload;
|
|
7076
|
+
'wicked.team.plan.revised': TeamPlanRevisedPayload;
|
|
7077
|
+
'wicked.team.plan.accepted': TeamPlanAcceptedPayload;
|
|
7078
|
+
'wicked.team.plan.refused': TeamPlanRefusedPayload;
|
|
7079
|
+
'wicked.team.member.joined': TeamMemberJoinedPayload;
|
|
7080
|
+
'wicked.team.member.left': TeamMemberLeftPayload;
|
|
7081
|
+
'wicked.team.step.claimed': TeamStepClaimedPayload;
|
|
7082
|
+
'wicked.team.checkpoint.reached': TeamCheckpointReachedPayload;
|
|
7083
|
+
'wicked.team.finding.raised': TeamFindingRaisedPayload;
|
|
7084
|
+
'wicked.team.advice.delivered': TeamAdviceDeliveredPayload;
|
|
7085
|
+
'wicked.team.advice.answered': TeamAdviceAnsweredPayload;
|
|
7086
|
+
'wicked.team.help.requested': TeamHelpRequestedPayload;
|
|
7087
|
+
'wicked.team.help.answered': TeamHelpAnsweredPayload;
|
|
7088
|
+
'wicked.team.change.requested': TeamChangeRequestedPayload;
|
|
7089
|
+
'wicked.team.step.completed': TeamStepCompletedPayload;
|
|
7090
|
+
'wicked.team.step.reviewed': TeamStepReviewedPayload;
|
|
7091
|
+
'wicked.team.finding.settled': TeamFindingSettledPayload;
|
|
7092
|
+
'wicked.team.council.called': TeamCouncilCalledPayload;
|
|
7093
|
+
'wicked.team.council.ruled': TeamCouncilRuledPayload;
|
|
7094
|
+
'wicked.team.ledger.folded': TeamLedgerFoldedPayload;
|
|
7095
|
+
'wicked.team.gate.opened': TeamGateOpenedPayload;
|
|
7096
|
+
'wicked.team.gate.decided': TeamGateDecidedPayload;
|
|
7097
|
+
'wicked.team.path.ended': TeamPathEndedPayload;
|
|
7098
|
+
}
|
|
7099
|
+
|
|
7100
|
+
/** One team bus row, discriminated on `event_type`. */
|
|
7101
|
+
export type TeamBusEvent = {
|
|
7102
|
+
[K in TeamEventType]: { event_type: K; payload: TeamEventPayloads[K] };
|
|
7103
|
+
}[TeamEventType];
|
|
7104
|
+
|
|
7105
|
+
// ── The crew team surface (DES-TEAMING-002 T8; api-types 0.42.0) ─────────────────────────────
|
|
7106
|
+
//
|
|
7107
|
+
// Commands go through the API, facts go on the bus (§4.0). Crew RELAYS the engine's
|
|
7108
|
+
// `wicked.team.*` rows onto `/ws`, READS them back per run, and POSTs commands to the engine. It
|
|
7109
|
+
// publishes no team row.
|
|
7110
|
+
|
|
7111
|
+
/**
|
|
7112
|
+
* A `/ws` frame carrying one `wicked.team.*` bus row, relayed verbatim (the `interactiveEvent`
|
|
7113
|
+
* pattern). `project_id` is set when the run's membership files it, as on every other frame.
|
|
7114
|
+
*/
|
|
7115
|
+
export interface TeamEventFrame {
|
|
7116
|
+
type: 'teamEvent';
|
|
7117
|
+
/** The whole bus row as wicked-bus delivers it. */
|
|
7118
|
+
event: TeamBusEvent & { event_id: number; [k: string]: unknown };
|
|
7119
|
+
project_id?: string;
|
|
7120
|
+
}
|
|
7121
|
+
|
|
7122
|
+
// ── The watch registry wire contract (DES-TRIGGER-REGISTRY-001 §4.5; TR-W4; api-types 0.70.0) ──
|
|
7123
|
+
//
|
|
7124
|
+
// The registry is advisory by construction: a watch row can never allow, approve or block. Its
|
|
7125
|
+
// facts are two bus types owned by crew (`wicked.crew.watch_finding.{raised,cleared}`), relayed
|
|
7126
|
+
// onto `/ws` as {@link WatchEventFrame} by a bus tap (never broadcast directly, so the socket and
|
|
7127
|
+
// the record cannot disagree). Types only: the runtime lands in TR-W5a, and a daemon without it
|
|
7128
|
+
// simply never sends these frames or serves these routes.
|
|
7129
|
+
|
|
7130
|
+
/** The two bus types the watch registry publishes. */
|
|
7131
|
+
export type WatchEventType = 'wicked.crew.watch_finding.raised' | 'wicked.crew.watch_finding.cleared';
|
|
7132
|
+
|
|
7133
|
+
/** How a watch row is meant: a contradiction in the work's own evidence, a look-worthy flag, or a candidate rule for the review queue. */
|
|
7134
|
+
export type WatchEmitAs = 'finding' | 'flag' | 'proposal';
|
|
7135
|
+
export type WatchSeverity = 'high' | 'medium' | 'info';
|
|
7136
|
+
/** The Watchtower's kind column. There is no `needs` kind: needs-you stays studio's own fold. */
|
|
7137
|
+
export type WatchKind = 'problem' | 'decision' | 'done' | 'quiet' | 'delivery';
|
|
7138
|
+
/** `p0` is never shed; `p1` is shed after `p2` when the push ring overflows. */
|
|
7139
|
+
export type WatchPriority = 'p0' | 'p1' | 'p2';
|
|
7140
|
+
|
|
7141
|
+
/** Where in the run a watch row points: "Jump in" scrolls to the nearest event at or after `at`. */
|
|
7142
|
+
export interface WatchAnchor {
|
|
7143
|
+
run_id: string;
|
|
7144
|
+
ord: number | null;
|
|
7145
|
+
attempt: number | null;
|
|
7146
|
+
/** Unix millis of the source event. */
|
|
7147
|
+
at: number;
|
|
7148
|
+
}
|
|
7149
|
+
|
|
7150
|
+
/** One piece of evidence a watch row cites. `run_event` names a persisted CoreEvent; `bus_row` a bus row. */
|
|
7151
|
+
export interface WatchEvidenceRef {
|
|
7152
|
+
kind: 'run_event' | 'bus_row' | (string & {});
|
|
7153
|
+
type: string;
|
|
7154
|
+
ord?: number | null;
|
|
7155
|
+
attempt?: number | null;
|
|
7156
|
+
/** For `bus_row`: the cited row's `event_id`. */
|
|
7157
|
+
event_id?: number;
|
|
7158
|
+
}
|
|
7159
|
+
|
|
7160
|
+
/**
|
|
7161
|
+
* The payload of `wicked.crew.watch_finding.raised`. Its first six fields are the team
|
|
7162
|
+
* envelope's (`DES-TEAMING-002` §4.4). `watch_id` is `"w-"` + the deterministic key over
|
|
7163
|
+
* (`run_id | "-"`, `entry_id`, `entry_version`, `subject`), so a replay or a redelivery resolves to
|
|
7164
|
+
* the same row.
|
|
7165
|
+
*/
|
|
7166
|
+
export interface WatchFinding {
|
|
7167
|
+
/** `null` for a row no run owns (a registry-internal flag such as `registry-lagging`). */
|
|
7168
|
+
run_id: string | null;
|
|
7169
|
+
ord: number | null;
|
|
7170
|
+
attempt: number | null;
|
|
7171
|
+
/** `"watch:<entry_id>@<entry_version>"`. */
|
|
7172
|
+
by: string;
|
|
7173
|
+
/** Unix millis the row was produced. */
|
|
7174
|
+
at: number;
|
|
7175
|
+
/** What it answers: `"<CoreEvent type>#<ord>:<attempt>"`, or a bus source row's idempotency key. */
|
|
7176
|
+
re: string;
|
|
7177
|
+
watch_id: string;
|
|
7178
|
+
entry_id: string;
|
|
7179
|
+
entry_version: number;
|
|
7180
|
+
/** `"deterministic:<name>"` or `"llm:<prompt-ref>@<n>"`. */
|
|
7181
|
+
check: string;
|
|
7182
|
+
kind: WatchEmitAs;
|
|
7183
|
+
severity: WatchSeverity;
|
|
7184
|
+
watch_kind: WatchKind;
|
|
7185
|
+
/** `"gate"`: render as one line on the open gate's existing row, never a new row. */
|
|
7186
|
+
attach: 'gate' | null;
|
|
7187
|
+
/** The run's project membership, as the relay tags frames; absent when the run is unfiled. */
|
|
7188
|
+
project_id?: string;
|
|
7189
|
+
/** The one plain sentence the Watchtower shows. */
|
|
7190
|
+
sentence: string;
|
|
7191
|
+
/** Entry-specific facts, at most 4 KB, redacted. */
|
|
7192
|
+
facts: Record<string, unknown>;
|
|
7193
|
+
anchor: WatchAnchor | null;
|
|
7194
|
+
evidence: WatchEvidenceRef[];
|
|
7195
|
+
/** Set for an `llm:*` check only. */
|
|
7196
|
+
model: { seat: string; model: string; latency_ms: number } | null;
|
|
7197
|
+
/** How many later rows this roll-up stands for (`0` on an ordinary row). */
|
|
7198
|
+
rolled_up: number;
|
|
7199
|
+
}
|
|
7200
|
+
|
|
7201
|
+
/** The fields every `wicked.crew.watch_finding.cleared` payload carries. */
|
|
7202
|
+
export interface WatchFindingClearedBase {
|
|
7203
|
+
run_id: string | null;
|
|
7204
|
+
ord: number | null;
|
|
7205
|
+
attempt: number | null;
|
|
7206
|
+
by: string;
|
|
7207
|
+
at: number;
|
|
7208
|
+
re: string;
|
|
7209
|
+
/** The raised row this clears. */
|
|
7210
|
+
watch_id: string;
|
|
7211
|
+
entry_id: string;
|
|
7212
|
+
entry_version: number;
|
|
7213
|
+
project_id?: string;
|
|
7214
|
+
}
|
|
7215
|
+
|
|
7216
|
+
/**
|
|
7217
|
+
* The payload of `wicked.crew.watch_finding.cleared`, discriminated on `reason`: the raised row
|
|
7218
|
+
* `watch_id` names is resolved (a later attempt passed, the worker produced output), dismissed by
|
|
7219
|
+
* the operator ("Seen, not a problem": `dismissed_by` says who), or replaced by a roll-up
|
|
7220
|
+
* (`replaced_by` names it). The set is closed on purpose: no clearing reason is allow-like.
|
|
7221
|
+
*/
|
|
7222
|
+
export type WatchFindingCleared =
|
|
7223
|
+
| (WatchFindingClearedBase & { reason: 'resolved' })
|
|
7224
|
+
| (WatchFindingClearedBase & { reason: 'dismissed'; dismissed_by: string })
|
|
7225
|
+
| (WatchFindingClearedBase & { reason: 'rolled_up'; replaced_by: string });
|
|
7226
|
+
|
|
7227
|
+
export interface WatchEventPayloads {
|
|
7228
|
+
'wicked.crew.watch_finding.raised': WatchFinding;
|
|
7229
|
+
'wicked.crew.watch_finding.cleared': WatchFindingCleared;
|
|
7230
|
+
}
|
|
7231
|
+
|
|
7232
|
+
/** One watch bus row, discriminated on `event_type`. */
|
|
7233
|
+
export type WatchBusEvent = {
|
|
7234
|
+
[K in WatchEventType]: { event_type: K; payload: WatchEventPayloads[K] };
|
|
7235
|
+
}[WatchEventType];
|
|
7236
|
+
|
|
7237
|
+
/**
|
|
7238
|
+
* A `/ws` frame carrying one `wicked.crew.watch_finding.*` bus row, relayed verbatim (the
|
|
7239
|
+
* {@link TeamEventFrame} pattern). `project_id` is set when the run's membership files it.
|
|
7240
|
+
*/
|
|
7241
|
+
export interface WatchEventFrame {
|
|
7242
|
+
type: 'watchEvent';
|
|
7243
|
+
/** The whole bus row as wicked-bus delivers it. */
|
|
7244
|
+
event: WatchBusEvent & { event_id: number; [k: string]: unknown };
|
|
7245
|
+
project_id?: string;
|
|
7246
|
+
}
|
|
7247
|
+
|
|
7248
|
+
/**
|
|
7249
|
+
* What one check can see on one run. Studio never renders an empty list as "all clear" when an entry
|
|
7250
|
+
* was `not_checked`; a `not_checked` entry always says why, in plain words ("no declared scope", "the
|
|
7251
|
+
* daemon was down").
|
|
7252
|
+
*/
|
|
7253
|
+
export type WatchCoverage =
|
|
7254
|
+
| { entry_id: string; state: 'checked' }
|
|
7255
|
+
| { entry_id: string; state: 'not_checked'; reason: string };
|
|
7256
|
+
|
|
7257
|
+
/** `GET /watch?project=&kind=&run=&since=&limit=`: the feed fold, newest first. */
|
|
7258
|
+
export interface WatchFeedResponse {
|
|
7259
|
+
findings: WatchFinding[];
|
|
7260
|
+
/**
|
|
7261
|
+
* The clearing rows of the findings on this page (a late join folds them as the live
|
|
7262
|
+
* `watch_finding.cleared` frames would: Fixed, Dismissed, rolled up). api-types 0.73.0.
|
|
7263
|
+
*/
|
|
7264
|
+
cleared?: WatchFindingCleared[];
|
|
7265
|
+
/** Present with `run=` only. */
|
|
7266
|
+
coverage?: WatchCoverage[];
|
|
7267
|
+
}
|
|
7268
|
+
|
|
7269
|
+
/**
|
|
7270
|
+
* Where an entry's key point comes from. `core`: a CoreEvent from the daemon fan-in. `bus`: a pulled
|
|
7271
|
+
* 4-segment bus type. `watchdog`: a stall-watchdog frame (`workerStalled`, …). `internal`: the
|
|
7272
|
+
* registry's own tick or check-failure report (`registry-lagging`, `registry-check-failed`).
|
|
7273
|
+
* (api-types 0.73.0 adds `watchdog` and `internal`.)
|
|
7274
|
+
*/
|
|
7275
|
+
export type WatchEntrySource = 'core' | 'bus' | 'watchdog' | 'internal';
|
|
7276
|
+
|
|
7277
|
+
/** Who changed one operator setting of an entry, and when (stamped by the daemon, never the caller). */
|
|
7278
|
+
export interface WatchSettingStamp {
|
|
7279
|
+
by: string;
|
|
7280
|
+
/** Unix millis. */
|
|
7281
|
+
at: number;
|
|
7282
|
+
}
|
|
7283
|
+
|
|
7284
|
+
/** One entry's operator override in `SystemSettings.watch.entries`. */
|
|
7285
|
+
export interface WatchEntryOverride {
|
|
7286
|
+
enabled?: boolean;
|
|
7287
|
+
/** Merged over the shipped threshold, validated by the entry's check. */
|
|
7288
|
+
threshold?: Record<string, unknown>;
|
|
7289
|
+
/** Daemon-stamped; a caller cannot set these. */
|
|
7290
|
+
enabled_changed?: WatchSettingStamp;
|
|
7291
|
+
threshold_changed?: WatchSettingStamp;
|
|
7292
|
+
}
|
|
7293
|
+
|
|
7294
|
+
/** `SystemSettings.watch`. */
|
|
7295
|
+
export interface WatchSettings {
|
|
7296
|
+
entries?: Record<string, WatchEntryOverride>;
|
|
7297
|
+
/** The LLM lane (off by default; TR-W9). */
|
|
7298
|
+
llm?: boolean;
|
|
7299
|
+
}
|
|
7300
|
+
|
|
7301
|
+
/**
|
|
7302
|
+
* One registry entry (`packages/crew/watch/entries/<id>.json`), as `GET /watch/entries` serves it:
|
|
7303
|
+
* the effective entry (the operator's `enabled` / `threshold` override applied) plus its threshold
|
|
7304
|
+
* in plain words. The schema has no allow-like `emit.as` value.
|
|
7305
|
+
*/
|
|
7306
|
+
export interface WatchEntry {
|
|
7307
|
+
/** Stable, `[a-z0-9-]{3,48}`; equals the file name. */
|
|
7308
|
+
id: string;
|
|
7309
|
+
/** Bumped on any semantic change; part of every finding key. */
|
|
7310
|
+
version: number;
|
|
7311
|
+
/** See {@link WatchEntrySource}. A `bus` type is a 4-segment `wicked.<domain>.<noun>.<verb>` type. */
|
|
7312
|
+
on: { source: WatchEntrySource; type: string };
|
|
7313
|
+
/** JSON-path equality, AND of keys; an array means "any of". */
|
|
7314
|
+
filter: Record<string, unknown>;
|
|
7315
|
+
check: string;
|
|
7316
|
+
params: Record<string, unknown>;
|
|
7317
|
+
threshold: Record<string, unknown>;
|
|
7318
|
+
emit: {
|
|
7319
|
+
as: WatchEmitAs;
|
|
7320
|
+
severity: WatchSeverity;
|
|
7321
|
+
watch_kind: WatchKind;
|
|
7322
|
+
attach: 'gate' | null;
|
|
7323
|
+
/** Past `per_run`, one roll-up replaces the rest. */
|
|
7324
|
+
rate: { per_run: number };
|
|
7325
|
+
};
|
|
7326
|
+
priority: WatchPriority;
|
|
7327
|
+
enabled: boolean;
|
|
7328
|
+
/** The threshold in plain words, for "When Studio speaks up". Always set on `GET /watch/entries`. */
|
|
7329
|
+
threshold_text: string;
|
|
7330
|
+
}
|
|
7331
|
+
|
|
7332
|
+
/** Who changed an operator-adjustable entry setting, and when (read from the `settings.updated` audit entry). */
|
|
7333
|
+
export interface WatchEntryChange {
|
|
7334
|
+
id: string;
|
|
7335
|
+
by: string;
|
|
7336
|
+
/** Unix millis. */
|
|
7337
|
+
at: number;
|
|
7338
|
+
}
|
|
7339
|
+
|
|
7340
|
+
/** `GET /watch/health`. Every turned-off entry and every changed threshold is listed with who and when. */
|
|
7341
|
+
export interface WatchHealth {
|
|
7342
|
+
/** The registry is running. `false` ⇒ `reason` says why. */
|
|
7343
|
+
armed: boolean;
|
|
7344
|
+
reason: string | null;
|
|
7345
|
+
sources: {
|
|
7346
|
+
bus: 'ok' | 'lagging' | 'down' | (string & {});
|
|
7347
|
+
core: 'ok' | 'lagging' | 'down' | (string & {});
|
|
7348
|
+
watchdog: 'ok' | 'lagging' | 'down' | (string & {});
|
|
7349
|
+
};
|
|
7350
|
+
queue: { depth: number; hwm: number; shed_by_priority: Record<WatchPriority, number> };
|
|
7351
|
+
tail_lag_ms: number;
|
|
7352
|
+
emit: { failed: number };
|
|
7353
|
+
entries: {
|
|
7354
|
+
loaded: number;
|
|
7355
|
+
refused: Array<{ id: string; reason: string }>;
|
|
7356
|
+
off: WatchEntryChange[];
|
|
7357
|
+
thresholds_changed: WatchEntryChange[];
|
|
7358
|
+
};
|
|
7359
|
+
llm: { enabled: boolean; inflight: number; timeouts: number; skipped_no_seat: number };
|
|
7360
|
+
/** `core_gap: true`: runs that started and ended while the daemon was down were not evaluated this boot. */
|
|
7361
|
+
replay?: { core_gap: boolean };
|
|
7362
|
+
}
|
|
7363
|
+
|
|
7364
|
+
/** One `wicked.team.*` bus row as `GET /runs/:id/team` returns it. */
|
|
7365
|
+
export type TeamRow = TeamBusEvent & {
|
|
7366
|
+
event_id: number;
|
|
7367
|
+
/** wicked-bus `emitted_at` (Unix millis). */
|
|
7368
|
+
emitted_at: number;
|
|
7369
|
+
/**
|
|
7370
|
+
* Set on a `wicked.team.ledger.folded` row the unit's gate did NOT read: a `gate.opened
|
|
7371
|
+
* {kind:"unit_review"}` for the same ord and attempt names another ledger (`ledger_ref` null,
|
|
7372
|
+
* a synthesized ledger). `gate.opened.ledger_ref` is the only authority on what the gate read.
|
|
7373
|
+
*/
|
|
7374
|
+
unused?: true;
|
|
7375
|
+
};
|
|
7376
|
+
|
|
7377
|
+
/**
|
|
7378
|
+
* One unit's team state in {@link RunTeamResponse}: the engine's persisted snapshot
|
|
7379
|
+
* (`Core.runTeam`; the last five fields are `null` until the unit's attempt folded) plus the
|
|
7380
|
+
* unit's bus rows.
|
|
7381
|
+
*/
|
|
7382
|
+
export interface RunTeamUnit {
|
|
7383
|
+
ord: number;
|
|
7384
|
+
/** `null` until the unit dispatched. */
|
|
7385
|
+
transport: 'bus' | 'none' | (string & {}) | null;
|
|
7386
|
+
reason: string | null;
|
|
7387
|
+
/** Where the gate's ledger came from: folded by the supervisor, synthesized by the worker on the final-pass timeout, or local (no bus). */
|
|
7388
|
+
ledgerSource: 'folded' | 'synthesized' | 'no_bus' | (string & {}) | null;
|
|
7389
|
+
/** The supervisor row the gate read (`"ledger.folded#<ord>:<attempt>"`); `null` for a synthesized or local ledger. */
|
|
7390
|
+
ledgerRef: string | null;
|
|
7391
|
+
finalPass: 'completed' | 'timed_out' | 'skipped' | 'stream_gap' | (string & {}) | null;
|
|
7392
|
+
/** An unresolved HIGH (or an incomplete record) stands without a council YES: the run pauses `team_dispute`. */
|
|
7393
|
+
teamPause: boolean | null;
|
|
7394
|
+
/** How many findings the attempt's ledger holds. */
|
|
7395
|
+
findings: number | null;
|
|
7396
|
+
/** The unit's bus rows (`payload.ord` = this ord), every attempt, ordered by `event_id`. Empty when the bus has none. */
|
|
7397
|
+
rows: TeamRow[];
|
|
7398
|
+
/**
|
|
7399
|
+
* The folded ledger the gate read (the `ledger.folded` row `ledgerRef` names), or `null`: not
|
|
7400
|
+
* folded yet, synthesized or local (read `UnitEvidence.team` on the evidence route), or the bus
|
|
7401
|
+
* no longer has the row (the snapshot fields above still stand).
|
|
7402
|
+
*/
|
|
7403
|
+
ledger: TeamLedger | null;
|
|
7404
|
+
}
|
|
7405
|
+
|
|
7406
|
+
/**
|
|
7407
|
+
* `GET /runs/:id/team`. The run's team transport and per-unit team state. A run that is not a team
|
|
7408
|
+
* run (free text, a registered def: bug, chat, onboarding, …) answers `teamed: false`,
|
|
7409
|
+
* `transport: null` and `units: []` — nothing to render, and no outage.
|
|
7410
|
+
*/
|
|
7411
|
+
export interface RunTeamResponse {
|
|
7412
|
+
runId: string;
|
|
7413
|
+
/** Whether the run is a team run at all. `false` ⇒ `transport` and `reason` are `null`. */
|
|
7414
|
+
teamed: boolean;
|
|
7415
|
+
/**
|
|
7416
|
+
* For a team run: `"bus"`; `"none"` (the teamed run fell back to no team transport: render the
|
|
7417
|
+
* "team transport unavailable" banner with `reason`); `"pending"` (not decided yet);
|
|
7418
|
+
* `"unavailable"` (this daemon has no bus: the run is paused `team_transport` until the operator
|
|
7419
|
+
* answers it). `null` for a run that is not a team run.
|
|
7420
|
+
*/
|
|
7421
|
+
transport: 'bus' | 'none' | 'pending' | 'unavailable' | (string & {}) | null;
|
|
7422
|
+
reason: string | null;
|
|
7423
|
+
/** The first bus event id of the run's stream. */
|
|
7424
|
+
streamFloor: number | null;
|
|
7425
|
+
planRev: number | null;
|
|
7426
|
+
/** The required fact the run waits on (its event type), if any. */
|
|
7427
|
+
pending: string | null;
|
|
7428
|
+
/** The run is terminal and its end is on record (a run that is not a team run: its status is terminal). */
|
|
7429
|
+
ended: boolean;
|
|
7430
|
+
units: RunTeamUnit[];
|
|
7431
|
+
/** The run-level rows (`payload.ord` null: `path.*`, `plan.*`), ordered by `event_id`. */
|
|
7432
|
+
rows: TeamRow[];
|
|
7433
|
+
}
|
|
7434
|
+
|
|
7435
|
+
/**
|
|
7436
|
+
* `POST /runs/:id/plan` body: a plan edit. At a `plan_approval` gate the edit IS the gate answer
|
|
7437
|
+
* (the engine proposes, floor-fills and accepts it, or refuses it and re-opens the gate) and the
|
|
7438
|
+
* route answers `{ status }`. Anywhere else on a live planned run (running, or paused at another
|
|
7439
|
+
* gate) it is a MID-RUN edit (`Core.proposePlan`): `plan.steps` are the steps to ADD, held and
|
|
7440
|
+
* applied at the run's next step boundary — its author approved it, so no gate opens — and the
|
|
7441
|
+
* route answers {@link PlanProposalResponse}. A mid-run edit carries no `touch` / `override`.
|
|
7442
|
+
*/
|
|
7443
|
+
export interface EditPlanBody {
|
|
7444
|
+
plan: LaunchPlan;
|
|
7445
|
+
/**
|
|
7446
|
+
* The caller's id for this edit: the engine's idempotency key for a mid-run edit, so a retried
|
|
7447
|
+
* POST carrying the same id proposes once (`duplicate: true`). Omitted ⇒ the daemon mints a
|
|
7448
|
+
* fresh UUID for that POST, so an omitted-id retry is a SECOND edit — send one to retry safely.
|
|
7449
|
+
* A gate edit is keyed on its gate, not on this id.
|
|
7450
|
+
*/
|
|
7451
|
+
requestId?: string;
|
|
7452
|
+
}
|
|
7453
|
+
|
|
7454
|
+
/** `POST /runs/:id/plan` at a `plan_approval` gate: the run's status after the gate answer. */
|
|
7455
|
+
export interface EditPlanGateResponse {
|
|
7456
|
+
status: SessionStatus;
|
|
7457
|
+
}
|
|
7458
|
+
|
|
7459
|
+
/**
|
|
7460
|
+
* `POST /runs/:id/plan` mid-run: the engine's `PlanProposal` (`Core.proposePlan`), what the held
|
|
7461
|
+
* edit does to the run — read off the dry run it was validated by, since the author's own edit
|
|
7462
|
+
* opens no gate. Refusals (a plan awaiting approval — edit it at the gate —, a started deliver
|
|
7463
|
+
* step, a finished or unplanned run, an edit the revision refuses) answer 409 with the engine
|
|
7464
|
+
* reason.
|
|
7465
|
+
*/
|
|
7466
|
+
export interface PlanProposalResponse {
|
|
7467
|
+
/** The `plan.proposed` id the edit is published under. */
|
|
7468
|
+
proposal_id: string;
|
|
7469
|
+
/** `true`: this `requestId` was already taken — nothing new is held or published. */
|
|
7470
|
+
duplicate: boolean;
|
|
7471
|
+
/** The floor band of the rev the edit makes (`"40-69"`, …); `null` for a duplicate. */
|
|
7472
|
+
band: string | null;
|
|
7473
|
+
/** §8.5's high-risk rule on that rev: `true` = the edit moved the run into high risk. `null` for a duplicate. */
|
|
7474
|
+
high_risk: boolean | null;
|
|
7475
|
+
/** The floor phase types (catalog ids) the edit adds; empty for a duplicate. */
|
|
7476
|
+
floor_added: string[];
|
|
7477
|
+
}
|
|
7478
|
+
|
|
7479
|
+
/** `POST /runs/:id/plan`: a gate answer (`status`) or a mid-run proposal (`proposal_id`). */
|
|
7480
|
+
export type EditPlanResponse = EditPlanGateResponse | PlanProposalResponse;
|
|
7481
|
+
|
|
7482
|
+
/** `POST /team/outbox/replay`: the engine's replay of `<state home>/team-outbox.ndjson`. */
|
|
7483
|
+
export interface TeamOutboxReplayReport {
|
|
7484
|
+
/** `[idempotency key, bus event id]` of every fact that reached the bus, in publish order. */
|
|
7485
|
+
published: Array<[string, number]>;
|
|
7486
|
+
superseded: number;
|
|
7487
|
+
invalid: number;
|
|
7488
|
+
remaining: number;
|
|
7489
|
+
/** `[run id, reason]` per lane that stopped. */
|
|
7490
|
+
failures: Array<[string, string]>;
|
|
7491
|
+
}
|
|
7492
|
+
|
|
7493
|
+
/** One phase type of the engine's catalog (`GET /catalog`, `Core.catalog`), in catalog order —
|
|
7494
|
+
* the row studio's phase picker renders. */
|
|
7495
|
+
export interface CatalogEntry {
|
|
7496
|
+
id: string;
|
|
7497
|
+
kind: StageKindPhase;
|
|
7498
|
+
role: PhaseRole;
|
|
7499
|
+
gate: GateSpec;
|
|
7500
|
+
gate_type: GateType | null;
|
|
7501
|
+
executes_code: boolean;
|
|
7502
|
+
/** `"tool"`: a step of this entry supplies its own command (`deliver`); else `"agent"`. */
|
|
7503
|
+
executor: 'agent' | 'tool';
|
|
7504
|
+
validator_pin: string | null;
|
|
7505
|
+
/** The entry carries a validator pin (a step may not remove or swap it). */
|
|
7506
|
+
pinned: boolean;
|
|
7507
|
+
/** That pin is the evidence floor. */
|
|
7508
|
+
evidence_floor: boolean;
|
|
7509
|
+
/**
|
|
7510
|
+
* The entry declares re-verified evidence: a step of it is an acceptance requirement of the run
|
|
7511
|
+
* that contains it (`test`, `domain_coverage`). ABSENT from an engine before the flag (api-types
|
|
7512
|
+
* 0.46.0) — the daemon then fails a plan run's acceptance closed rather than guess.
|
|
7513
|
+
*/
|
|
7514
|
+
verified_evidence?: boolean;
|
|
7515
|
+
skill_ref: string | null;
|
|
7516
|
+
/** The entry's one-line description; `null` when it has none. */
|
|
7517
|
+
description: string | null;
|
|
7518
|
+
}
|
|
7519
|
+
|
|
7520
|
+
/** `GET /catalog`. */
|
|
7521
|
+
export interface CatalogResponse {
|
|
7522
|
+
entries: CatalogEntry[];
|
|
7523
|
+
}
|
|
7524
|
+
|
|
7525
|
+
/** `POST /plans/preview` body: a draft plan, previewed as the `POST /runs {plan}` launch with the
|
|
7526
|
+
* same fields would decide it. */
|
|
7527
|
+
export interface PlanPreviewBody {
|
|
7528
|
+
plan: LaunchPlan;
|
|
7529
|
+
/** Only matters to a preset name, so it does not change a plan's preview. */
|
|
7530
|
+
projectId?: string;
|
|
7531
|
+
/** The launch's gate mode (`humanConfirm` on `POST /runs`); absent = auto. */
|
|
7532
|
+
humanConfirm?: string;
|
|
7533
|
+
/** The registered repo the launch would run on: a behavioural `touch` set is scored against its
|
|
7534
|
+
* code graph at the base the launch would start from (read locally, no fetch). An unregistered
|
|
7535
|
+
* repo is a 400. Absent ⇒ the fail-closed score (`graph: "unavailable"`). */
|
|
7536
|
+
repoRef?: string;
|
|
7537
|
+
/** The launch's delivery (`deliver` on `POST /runs`): `"pr"` hands the engine the launch's
|
|
7538
|
+
* deliver step, so the preview's floor carries `deliver` as the launch's would. Absent or
|
|
7539
|
+
* `"none"` ⇒ no deliver step (a plan launch's own default). */
|
|
7540
|
+
deliver?: 'pr' | 'none';
|
|
7541
|
+
}
|
|
7542
|
+
|
|
7543
|
+
/**
|
|
7544
|
+
* `POST /plans/preview`: the engine's `PlanPreview` (`Core.previewPlan`) — what the launch would
|
|
7545
|
+
* compute (precheck, intent score, floor fill, approval matrix, planning checks), persisting and
|
|
7546
|
+
* publishing nothing. A refusal (the launch's own, an unregistered repo) is a 400.
|
|
7547
|
+
*/
|
|
7548
|
+
export interface PlanPreviewResponse {
|
|
7549
|
+
/** The intent score (§8.2), its deterministic part, one reason per contribution. */
|
|
7550
|
+
score: number;
|
|
7551
|
+
deterministic: number;
|
|
7552
|
+
reasons: string[];
|
|
7553
|
+
/** The destructive signal the floor fill reads. */
|
|
7554
|
+
destructive: boolean;
|
|
7555
|
+
/** The floor band the score lands in (`"40-69"`, `"70-100"`, …). */
|
|
7556
|
+
band: string;
|
|
7557
|
+
high_risk: boolean;
|
|
7558
|
+
/** The floor phase types the plan owes, in order. The steps the floor ADDED are the `steps`
|
|
7559
|
+
* with `added_by: "floor"`. */
|
|
7560
|
+
floor: string[];
|
|
7561
|
+
/** The floor override as recorded (manual mode only). */
|
|
7562
|
+
floor_override: TeamPlanOverride | null;
|
|
7563
|
+
/** The floor-filled steps; each carries `added_by`, and `floor_reason` when the floor added it. */
|
|
7564
|
+
steps: TeamPlanStep[];
|
|
7565
|
+
/** The def the run would plan from (`compose` of `steps`). */
|
|
7566
|
+
def: WorkflowDef;
|
|
7567
|
+
/** `true` ⇒ the launch would pause at a `plan_approval` gate before its first unit. */
|
|
7568
|
+
pauses: boolean;
|
|
7569
|
+
pause_reason: 'manual_mode' | 'high_risk' | 'override' | (string & {}) | null;
|
|
7570
|
+
/** `"ready"`: the score read the repo's code graph. `"not_needed"`: a docs-only touch set (or no
|
|
7571
|
+
* creator and no touch set). `"unavailable"`: the fail-closed score (no repo, no or a stale
|
|
7572
|
+
* graph — `reasons` says which). `"pending_pa_scope"` (wicked-core#633, DES-TEAMING-002 X1;
|
|
7573
|
+
* api-types 0.47.0): a creator plan with no `touch` set — the launch runs the PA's read-only
|
|
7574
|
+
* `pa-scope` step first and scores the plan from its answer, so `score`, `band` and the floor
|
|
7575
|
+
* shown are the baseline's with `pa-scope` first, NOT the plan's final risk, and `pauses` is
|
|
7576
|
+
* manual mode's alone (an auto-mode launch may still pause once the PA's scope lands high). */
|
|
7577
|
+
graph: 'ready' | 'not_needed' | 'unavailable' | 'pending_pa_scope';
|
|
7578
|
+
}
|
|
7579
|
+
|
|
7580
|
+
// ── Standing orders (Studio OS behaviour 10; api-types 0.50.0) ─────────────────────────────────
|
|
7581
|
+
|
|
7582
|
+
/**
|
|
7583
|
+
* A standing order's structured rule — parsed from plain words by a seat and CONFIRMED by the
|
|
7584
|
+
* operator before it is stored. `trigger.phase` is the phase the gate reviews (the reviewed unit's
|
|
7585
|
+
* `phase_ref`) or `*`. The invariant: `approve` never answers the deliver gate, a plan approval,
|
|
7586
|
+
* an escalation, a failure or a triage gate, and never a finding (the create route refuses a rule
|
|
7587
|
+
* that approves `deliver`, `plan_approval` or a finding with a 400 `order_refused`).
|
|
7588
|
+
*/
|
|
7589
|
+
export interface StandingOrderRule {
|
|
7590
|
+
scope: { kind: 'all' } | { kind: 'project'; projectId: string };
|
|
7591
|
+
/** `band` (api-types 0.53.0, optional): the order matches only a run whose accepted plan landed in
|
|
7592
|
+
* that band (`"0-19"`, …); a run with no scored plan never matches a band-scoped order.
|
|
7593
|
+
* `preset` (api-types 0.56.0, optional): only a run launched from that preset (`team_plan.preset`).
|
|
7594
|
+
* `phase: "plan_approval"` with `action: "approve"` is a TRUST RECEIPT (0.56.0): accepted only
|
|
7595
|
+
* with `band: "0-19"`, a `preset` and a `project` scope, and it answers a run's plan gate only
|
|
7596
|
+
* when that open gate itself reports band 0-19 and no high risk. `deliver` is never approvable. */
|
|
7597
|
+
trigger: { kind: 'gate'; phase: string; band?: string | undefined; preset?: string | undefined } | { kind: 'finding'; severity: 'high' | 'medium' | '*' };
|
|
7598
|
+
action: 'approve' | 'hold' | 'notify';
|
|
7599
|
+
activeWhen: 'away' | 'always';
|
|
7600
|
+
}
|
|
7601
|
+
|
|
7602
|
+
export interface StandingOrder {
|
|
7603
|
+
id: string;
|
|
7604
|
+
/** The operator's own words. */
|
|
7605
|
+
text: string;
|
|
7606
|
+
rule: StandingOrderRule;
|
|
7607
|
+
/** Unix millis. */
|
|
7608
|
+
createdAt: number;
|
|
7609
|
+
}
|
|
7610
|
+
|
|
7611
|
+
/** An outbound message a `notify` order wanted sent. Only ever QUEUED — the daemon sends nothing. */
|
|
7612
|
+
export interface QueuedStandingMessage {
|
|
7613
|
+
id: string;
|
|
7614
|
+
orderId: string;
|
|
7615
|
+
orderText: string;
|
|
7616
|
+
runId: string | null;
|
|
7617
|
+
text: string;
|
|
7618
|
+
/** Unix millis. */
|
|
7619
|
+
at: number;
|
|
7620
|
+
status: 'queued';
|
|
7621
|
+
}
|
|
7622
|
+
|
|
7623
|
+
/** `GET /standing-orders`, and the answer to `PUT /standing-orders/away`. */
|
|
7624
|
+
export interface StandingOrdersState {
|
|
7625
|
+
away: boolean;
|
|
7626
|
+
/** Unix millis the flag turned on; `null` while present. */
|
|
7627
|
+
awaySince: number | null;
|
|
7628
|
+
orders: StandingOrder[];
|
|
7629
|
+
outbox: QueuedStandingMessage[];
|
|
7630
|
+
}
|
|
7631
|
+
|
|
7632
|
+
/** `PUT /standing-orders/away`. */
|
|
7633
|
+
export interface StandingAwayBody {
|
|
7634
|
+
away: boolean;
|
|
7635
|
+
}
|
|
7636
|
+
|
|
7637
|
+
/** `POST /standing-orders/parse` — the plain words one seat reads. */
|
|
7638
|
+
export interface ParseStandingOrderBody {
|
|
7639
|
+
text: string;
|
|
7640
|
+
}
|
|
7641
|
+
|
|
7642
|
+
/** `POST /standing-orders/parse`'s 200: the rule to show back for confirmation. `refused` is set
|
|
7643
|
+
* when storing it would be refused (the invariant), in plain words. A 422 carries the seat's own
|
|
7644
|
+
* `answer` when it could not produce a rule; 409 = no seat can take a turn; 502 = the seat failed. */
|
|
7645
|
+
export interface ParsedStandingOrder {
|
|
7646
|
+
rule: StandingOrderRule;
|
|
7647
|
+
seat: string;
|
|
7648
|
+
refused?: string;
|
|
7649
|
+
}
|
|
7650
|
+
|
|
7651
|
+
/** `POST /standing-orders` — the confirmed rule. 201 `{ order }`. */
|
|
7652
|
+
export interface CreateStandingOrderBody {
|
|
7653
|
+
text: string;
|
|
7654
|
+
rule: StandingOrderRule;
|
|
7655
|
+
}
|
|
7656
|
+
|
|
7657
|
+
// ── Decided-gate history (api-types 0.53.0) ────────────────────────────────────────────────────
|
|
7658
|
+
|
|
7659
|
+
/**
|
|
7660
|
+
* One decided gate — `GET /gates/decided?since=<unix ms>` (newest first, at most 1000). One row per
|
|
7661
|
+
* `gate.decided` audit line, joined to the engine's gate row for the same `(run, ord)` and to the
|
|
7662
|
+
* run. What a skin needs for a seat's track record on a step, and for the preview of a proposed
|
|
7663
|
+
* standing order (`phase` and `orderApprovable` are the order evaluator's own readings).
|
|
7664
|
+
*/
|
|
7665
|
+
export interface DecidedGate {
|
|
7666
|
+
runId: string;
|
|
7667
|
+
/** The unit the gate paused before; `null` when the decision named none (a plan edit). */
|
|
7668
|
+
ord: number | null;
|
|
7669
|
+
/** Unix millis. */
|
|
7670
|
+
decidedAt: number;
|
|
7671
|
+
decision: 'approve' | 'request_changes' | 'reject' | 'edit_plan';
|
|
7672
|
+
/** Who decided: a person's actor id, or `standing-order:<id>`. */
|
|
7673
|
+
actor: string;
|
|
7674
|
+
/** A standing order decided it (the actor is `standing-order:<id>`). */
|
|
7675
|
+
byOrder: boolean;
|
|
7676
|
+
/** The engine's gate kind (`def`, `run_level`, `terminal`, `deliver`, `plan_approval`, …); `null` when unknown. */
|
|
7677
|
+
gateKind: string | null;
|
|
7678
|
+
/** The phase an order's trigger matches (`intake`, or the reviewed unit's phase); `null` when unknown. */
|
|
7679
|
+
phase: string | null;
|
|
7680
|
+
projectId: string | null;
|
|
7681
|
+
/** The band the run's accepted plan landed in; `null` for an unscored run. */
|
|
7682
|
+
band: string | null;
|
|
7683
|
+
/** The unit whose work the gate judged (the newest creator before the gate) and its seat; `null` when none. */
|
|
7684
|
+
creator: { seat: string; phase: string | null; ord: number } | null;
|
|
7685
|
+
/** A standing order could have approved this gate (a phase-review gate of a run that lands no doctrine). */
|
|
7686
|
+
orderApprovable: boolean;
|
|
7687
|
+
}
|
|
7688
|
+
|
|
7689
|
+
/** `GET /gates/decided`'s 200. A malformed `since` answers 400. */
|
|
7690
|
+
export interface DecidedGatesResponse {
|
|
7691
|
+
gates: DecidedGate[];
|
|
7692
|
+
}
|
|
7693
|
+
|
|
7694
|
+
// ── Freeze deliveries (api-types 0.55.0) ───────────────────────────────────────────────────────
|
|
7695
|
+
|
|
7696
|
+
/**
|
|
7697
|
+
* `GET|PUT /deliveries/freeze` — the one switch that holds every deliver gate. While `frozen`, an
|
|
7698
|
+
* approve of a gate that would run a deliver unit answers 409 `{code: "deliveries_frozen"}` and
|
|
7699
|
+
* `POST /runs/:id/deliver` refuses the same way; the gate stays open, so unfreezing lets the same
|
|
7700
|
+
* approve through. A run launched with `deliverGate: "auto"` has no gate and is not held.
|
|
7701
|
+
* Audited as `deliveries.frozen {reason?}` / `deliveries.unfrozen`; a restart keeps the freeze.
|
|
7702
|
+
*/
|
|
7703
|
+
export interface DeliveryFreezeState {
|
|
7704
|
+
frozen: boolean;
|
|
7705
|
+
/** ISO time it was turned on; `null` while thawed. */
|
|
7706
|
+
since: string | null;
|
|
7707
|
+
/** The actor id that turned it on; `null` while thawed. */
|
|
7708
|
+
by: string | null;
|
|
7709
|
+
reason: string | null;
|
|
7710
|
+
}
|
|
7711
|
+
|
|
7712
|
+
/** `PUT /deliveries/freeze`. `reason` (1–500 chars) is kept only when freezing. */
|
|
7713
|
+
export interface PutDeliveryFreezeBody {
|
|
7714
|
+
frozen: boolean;
|
|
7715
|
+
reason?: string;
|
|
7716
|
+
}
|
|
7717
|
+
|
|
7718
|
+
// ── MCP tools registry (DES-MCP-TOOLS-001 §5, §8, slice S2; api-types 0.58.0) ─────────────────
|
|
7719
|
+
|
|
7720
|
+
/**
|
|
7721
|
+
* How crew reaches an upstream: an MCP server over stdio or streamable HTTP, or a plain REST API
|
|
7722
|
+
* wrapped as tools from its OpenAPI document (`rest`, api-types 0.64.0).
|
|
7723
|
+
*/
|
|
7724
|
+
export type McpUpstreamKind = 'mcp-stdio' | 'mcp-http' | 'rest';
|
|
7725
|
+
/**
|
|
7726
|
+
* A tool's class, from its own `tools/list` annotations (never from a carrier): `read` when
|
|
7727
|
+
* `readOnlyHint` is true; else `destructive` unless `destructiveHint` is false; else `write`. A tool
|
|
7728
|
+
* with no annotations, or with neither `readOnlyHint` nor `destructiveHint`, is `write`. An
|
|
7729
|
+
* operator's `classOverride` wins.
|
|
7730
|
+
*/
|
|
7731
|
+
export type McpToolClass = 'read' | 'write' | 'destructive';
|
|
7732
|
+
/**
|
|
7733
|
+
* `registered`: the schema the operator saved is the one the server lists. `unregistered`: the
|
|
7734
|
+
* schema changed, or the server added the tool after the save; it is denied until previewed and
|
|
7735
|
+
* saved again. `gone`: the server no longer lists it (kept so old records resolve).
|
|
7736
|
+
*/
|
|
7737
|
+
export type McpToolStatus = 'registered' | 'unregistered' | 'gone';
|
|
7738
|
+
/** `failing` after 3 consecutive failed probes; `never` = no probe has succeeded yet. */
|
|
7739
|
+
export type McpHealthState = 'ok' | 'failing' | 'never';
|
|
7740
|
+
/** `none` = the server needs no secret; `missing` = its reference resolves to nothing. */
|
|
7741
|
+
export type McpAuthState = 'none' | 'set' | 'missing';
|
|
7742
|
+
|
|
7743
|
+
/** The spec's tool annotations, only the fields it defines. */
|
|
7744
|
+
export interface McpToolAnnotations {
|
|
7745
|
+
title?: string;
|
|
7746
|
+
readOnlyHint?: boolean;
|
|
7747
|
+
destructiveHint?: boolean;
|
|
7748
|
+
idempotentHint?: boolean;
|
|
7749
|
+
openWorldHint?: boolean;
|
|
7750
|
+
}
|
|
7751
|
+
|
|
7752
|
+
/**
|
|
7753
|
+
* Where a server's secret comes from and where crew injects it. The registry stores this
|
|
7754
|
+
* reference only, never a value. `ref` is `keychain:wicked-mcp/<name>` (written by
|
|
7755
|
+
* `PUT /mcp/servers/:name/secret`) or `env:<NAME>` (the daemon's env). An `mcp-stdio` server gets
|
|
7756
|
+
* the value in env variable `env`; an `mcp-http` or `rest` server in header `header`, after `prefix`.
|
|
7757
|
+
*/
|
|
7758
|
+
export interface McpAuthConfig {
|
|
7759
|
+
ref: string;
|
|
7760
|
+
env?: string;
|
|
7761
|
+
header?: string;
|
|
7762
|
+
prefix?: string;
|
|
7763
|
+
}
|
|
7764
|
+
|
|
7765
|
+
/**
|
|
7766
|
+
* `POST /mcp/servers/preview`. `mcp-stdio` takes `command` (+ `args`); `mcp-http` takes `url`.
|
|
7767
|
+
* `rest` takes `url`, the base URL every call of the API is pinned to (its scheme, host and port,
|
|
7768
|
+
* and its path as a prefix), plus its OpenAPI 3 document: `openapiUrl` (fetched by crew) or
|
|
7769
|
+
* `openapi` (the document itself, pasted), never both. `operations` picks which operations become
|
|
7770
|
+
* tools (by `operationId` or tool name); absent = all of them.
|
|
7771
|
+
*/
|
|
7772
|
+
export interface McpServerConfigBody {
|
|
7773
|
+
/** 1–63 of `a-z 0-9 _ -`; the `<server>` of the subject `mcp:<server>/<tool>`. */
|
|
7774
|
+
name: string;
|
|
7775
|
+
kind: McpUpstreamKind;
|
|
7776
|
+
command?: string;
|
|
7777
|
+
args?: string[];
|
|
7778
|
+
url?: string;
|
|
7779
|
+
auth?: McpAuthConfig | null;
|
|
7780
|
+
/** `rest` only: where crew fetches the OpenAPI 3 document (JSON or YAML). */
|
|
7781
|
+
openapiUrl?: string;
|
|
7782
|
+
/** `rest` only: the OpenAPI 3 document itself (instead of `openapiUrl`); at most 1 MB as JSON. */
|
|
7783
|
+
openapi?: Record<string, unknown>;
|
|
7784
|
+
/** `rest` only: the operations to wrap, by `operationId` or tool name; absent = all. */
|
|
7785
|
+
operations?: string[];
|
|
7786
|
+
/**
|
|
7787
|
+
* The secret VALUE this server's keychain entry will hold (wicked-crew#719). The preview is
|
|
7788
|
+
* probed with it and it is held with the preview; `POST /mcp/servers` writes it to the OS store
|
|
7789
|
+
* as part of the save, so the secret and the registry row commit together — studio used to write
|
|
7790
|
+
* the keychain itself first, and a failure between the two writes left a keychain entry with no
|
|
7791
|
+
* server, or a server whose secret never landed. Requires `auth.ref` =
|
|
7792
|
+
* `keychain:wicked-mcp/<name>`; `501` when the platform has no OS secret store. It is in no
|
|
7793
|
+
* response, log, audit entry or file.
|
|
7794
|
+
*/
|
|
7795
|
+
secret?: string;
|
|
7796
|
+
}
|
|
7797
|
+
|
|
7798
|
+
/**
|
|
7799
|
+
* How a `rest` tool turns its arguments into one HTTP request (api-types 0.64.0). The arguments are
|
|
7800
|
+
* an ALLOWLIST: an argument that no map names is dropped, never sent (its name is disclosed in the
|
|
7801
|
+
* result's `_meta["wicked/rest"].droppedArgs`). The request goes only to the server's base URL: a
|
|
7802
|
+
* request that would leave its scheme, host, port or base path, or a redirect to another host, is
|
|
7803
|
+
* refused before (or instead of) being followed, and recorded as a `guard_error`.
|
|
7804
|
+
*/
|
|
7805
|
+
export interface McpRestMapping {
|
|
7806
|
+
method: 'GET' | 'HEAD' | 'OPTIONS' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
7807
|
+
/** The OpenAPI path, e.g. `/issues/{id}`; joined onto the base URL's path. */
|
|
7808
|
+
pathTemplate: string;
|
|
7809
|
+
/** argument → `{variable}` of `pathTemplate`. */
|
|
7810
|
+
pathMap: Record<string, string>;
|
|
7811
|
+
/** argument → query parameter. */
|
|
7812
|
+
queryMap: Record<string, string>;
|
|
7813
|
+
/** argument → request header (never the auth header, `Host`, `Cookie` or a hop-by-hop header). */
|
|
7814
|
+
headerMap: Record<string, string>;
|
|
7815
|
+
/** argument → top-level key of the JSON request body; `null` when the body is one argument or none. */
|
|
7816
|
+
bodyMap: Record<string, string> | null;
|
|
7817
|
+
/** The argument that is the whole JSON request body; `null` when the body is mapped by key or absent. */
|
|
7818
|
+
bodyArg: string | null;
|
|
7819
|
+
/** Every argument that is sent: the union of the maps and `bodyArg`. */
|
|
7820
|
+
argAllowlist: string[];
|
|
7821
|
+
timeoutMs: number;
|
|
7822
|
+
}
|
|
7823
|
+
|
|
7824
|
+
export interface McpHealth {
|
|
7825
|
+
state: McpHealthState;
|
|
7826
|
+
consecutiveFailures: number;
|
|
7827
|
+
checkedAt: string | null;
|
|
7828
|
+
/** The last probe's error, with any secret scrubbed out. */
|
|
7829
|
+
lastError: string | null;
|
|
7830
|
+
}
|
|
7831
|
+
|
|
7832
|
+
export interface McpTool {
|
|
7833
|
+
name: string;
|
|
7834
|
+
/** A `rest` tool's request mapping (api-types 0.64.0); `null` for an MCP server's tool. */
|
|
7835
|
+
rest?: McpRestMapping | null;
|
|
7836
|
+
/** The policy token: `mcp:<server>/<tool>`. */
|
|
7837
|
+
subject: string;
|
|
7838
|
+
description: string | null;
|
|
7839
|
+
annotations: McpToolAnnotations | null;
|
|
7840
|
+
inputSchema: Record<string, unknown> | null;
|
|
7841
|
+
derivedClass: McpToolClass;
|
|
7842
|
+
classOverride: McpToolClass | null;
|
|
7843
|
+
/** `classOverride ?? derivedClass`: what a policy sees. */
|
|
7844
|
+
class: McpToolClass;
|
|
7845
|
+
enabled: boolean;
|
|
7846
|
+
status: McpToolStatus;
|
|
7847
|
+
/** The saved schema's hash; `null` = never saved. */
|
|
7848
|
+
schemaHash: string | null;
|
|
7849
|
+
/** The hash the last probe saw. */
|
|
7850
|
+
observedSchemaHash: string;
|
|
7851
|
+
}
|
|
7852
|
+
|
|
7853
|
+
export interface McpServer {
|
|
7854
|
+
name: string;
|
|
7855
|
+
kind: McpUpstreamKind;
|
|
7856
|
+
command: string | null;
|
|
7857
|
+
args: string[];
|
|
7858
|
+
url: string | null;
|
|
7859
|
+
auth: McpAuthConfig | null;
|
|
7860
|
+
/** `rest` only (api-types 0.64.0): the OpenAPI URL, `null` when the document was pasted. */
|
|
7861
|
+
openapiUrl?: string | null;
|
|
7862
|
+
/** `rest` only: the operations that were picked; `null` = all. */
|
|
7863
|
+
operations?: string[] | null;
|
|
7864
|
+
authState: McpAuthState;
|
|
7865
|
+
enabled: boolean;
|
|
7866
|
+
health: McpHealth;
|
|
7867
|
+
registeredAt: string;
|
|
7868
|
+
updatedAt: string;
|
|
7869
|
+
tools: McpTool[];
|
|
7870
|
+
/** Over the tools that are not `gone`. */
|
|
7871
|
+
counts: { total: number; enabled: number; registered: number; read: number; write: number; destructive: number };
|
|
7872
|
+
}
|
|
7873
|
+
|
|
7874
|
+
/**
|
|
7875
|
+
* A server configured in a CLI's own home, by name only (never its command, URL or env).
|
|
7876
|
+
* `origin: 'worker'` is a server in a seat's worker home, which no wicked-handed server may be.
|
|
7877
|
+
*/
|
|
7878
|
+
export interface McpDiscoveredServer {
|
|
7879
|
+
name: string;
|
|
7880
|
+
cli: string;
|
|
7881
|
+
origin: 'operator' | 'worker';
|
|
7882
|
+
/** The config file, relative to `~` or `<worker home>`. */
|
|
7883
|
+
source: string;
|
|
7884
|
+
/** Whether a registered server has the same name. */
|
|
7885
|
+
managed: boolean;
|
|
7886
|
+
}
|
|
7887
|
+
|
|
7888
|
+
/** `GET /mcp/servers`. */
|
|
7889
|
+
export interface McpServersResponse {
|
|
7890
|
+
servers: McpServer[];
|
|
7891
|
+
discovered: McpDiscoveredServer[];
|
|
7892
|
+
}
|
|
7893
|
+
|
|
7894
|
+
/** The difference between the registered tools and a probe, by tool name. */
|
|
7895
|
+
export interface McpToolDiff {
|
|
7896
|
+
added: string[];
|
|
7897
|
+
removed: string[];
|
|
7898
|
+
changed: string[];
|
|
7899
|
+
unchanged: string[];
|
|
7900
|
+
}
|
|
7901
|
+
|
|
7902
|
+
export interface McpPreviewTool {
|
|
7903
|
+
name: string;
|
|
7904
|
+
/** A `rest` tool's request mapping (api-types 0.64.0); `null` for an MCP server's tool. */
|
|
7905
|
+
rest?: McpRestMapping | null;
|
|
7906
|
+
subject: string;
|
|
7907
|
+
description: string | null;
|
|
7908
|
+
annotations: McpToolAnnotations | null;
|
|
7909
|
+
inputSchema: Record<string, unknown> | null;
|
|
7910
|
+
class: McpToolClass;
|
|
7911
|
+
schemaHash: string;
|
|
7912
|
+
}
|
|
7913
|
+
|
|
7914
|
+
/**
|
|
7915
|
+
* `POST /mcp/servers/preview`'s 200. Save it with `POST /mcp/servers {previewHash}` before
|
|
7916
|
+
* `expiresAt`. `diff` compares with the registered server of the same name, `null` when none.
|
|
7917
|
+
* A server that does not answer is a 502 `probe_failed`.
|
|
7918
|
+
*/
|
|
7919
|
+
export interface McpPreviewResponse {
|
|
7920
|
+
previewHash: string;
|
|
7921
|
+
expiresAt: string;
|
|
7922
|
+
server: {
|
|
7923
|
+
name: string;
|
|
7924
|
+
kind: McpUpstreamKind;
|
|
7925
|
+
command: string | null;
|
|
7926
|
+
args: string[];
|
|
7927
|
+
url: string | null;
|
|
7928
|
+
auth: McpAuthConfig | null;
|
|
7929
|
+
openapiUrl?: string | null;
|
|
7930
|
+
operations?: string[] | null;
|
|
7931
|
+
};
|
|
7932
|
+
serverInfo: { name: string; version: string } | null;
|
|
7933
|
+
tools: McpPreviewTool[];
|
|
7934
|
+
diff: McpToolDiff | null;
|
|
7935
|
+
/** `rest` only (api-types 0.64.0): operations that could not be wrapped, each with why. */
|
|
7936
|
+
skipped?: string[];
|
|
7937
|
+
/**
|
|
7938
|
+
* Each tool's decision per phase role × seat × mode under the current policies and approvals,
|
|
7939
|
+
* judged as if this preview were saved now (api-types 0.63.0); see `withdrawOnSave`. `null` when
|
|
7940
|
+
* the engine cannot preview.
|
|
7941
|
+
*/
|
|
7942
|
+
policies?: McpPolicyPreviewResponse | null;
|
|
7943
|
+
}
|
|
7944
|
+
|
|
7945
|
+
/** `POST /mcp/servers`. No `previewHash`, or one that is unknown or expired, is a 409. */
|
|
7946
|
+
export interface SaveMcpServerBody {
|
|
7947
|
+
previewHash?: string;
|
|
7948
|
+
}
|
|
7949
|
+
|
|
7950
|
+
/** `PATCH /mcp/servers/:name`. */
|
|
7951
|
+
export interface PatchMcpServerBody {
|
|
7952
|
+
enabled: boolean;
|
|
7953
|
+
}
|
|
7954
|
+
|
|
7955
|
+
/** `PATCH /mcp/tools/:subject` (subject URL-encoded). At least one field. */
|
|
7956
|
+
export interface PatchMcpToolBody {
|
|
7957
|
+
enabled?: boolean;
|
|
7958
|
+
classOverride?: McpToolClass | null;
|
|
7959
|
+
}
|
|
7960
|
+
|
|
7961
|
+
/** `POST /mcp/servers/:name/test`'s 200: a failed probe is `ok: false` and counts toward `failing`. */
|
|
7962
|
+
export interface McpServerTestResponse {
|
|
7963
|
+
ok: boolean;
|
|
7964
|
+
error: string | null;
|
|
7965
|
+
/** `null` when the probe failed. */
|
|
7966
|
+
diff: McpToolDiff | null;
|
|
7967
|
+
server: McpServer;
|
|
7968
|
+
}
|
|
7969
|
+
|
|
7970
|
+
/** `PUT /mcp/servers/:name/secret`: one line, 8–8192 characters. */
|
|
7971
|
+
export interface PutMcpSecretBody {
|
|
7972
|
+
value: string;
|
|
7973
|
+
}
|
|
7974
|
+
|
|
7975
|
+
/** The value is never echoed; `ref` goes into the server's `auth.ref`. 501 = no OS secret store. */
|
|
7976
|
+
export interface McpSecretResponse {
|
|
7977
|
+
ref: string;
|
|
7978
|
+
set: true;
|
|
7979
|
+
}
|
|
7980
|
+
|
|
7981
|
+
// ── MCP broker call path (DES-MCP-TOOLS-001 §6, §8 `POST /mcp/call`; slice S3) ─────────────────
|
|
7982
|
+
|
|
7983
|
+
/** `POST /mcp/call`: what the garden shim sends. `token` is the worker's `WICKED_MCP_TOKEN`. */
|
|
7984
|
+
export interface McpCallBody {
|
|
7985
|
+
token: string;
|
|
7986
|
+
/** `mcp:<server>/<tool>`. */
|
|
7987
|
+
subject: string;
|
|
7988
|
+
/** The tool's arguments (a JSON object); absent = `{}`. */
|
|
7989
|
+
args?: Record<string, unknown>;
|
|
7990
|
+
}
|
|
7991
|
+
|
|
7992
|
+
/** How a brokered call ended. Every outcome but `ok` means the result was not handed back. */
|
|
7993
|
+
export type McpCallOutcome =
|
|
7994
|
+
/** The call ran; `result` is the scrubbed upstream result (it may itself be a tool error, `isError`). */
|
|
7995
|
+
| 'ok'
|
|
7996
|
+
/** Refused by a policy or an engine gate before anything ran (403). The worker continues. */
|
|
7997
|
+
| 'denied'
|
|
7998
|
+
/** Waits for the operator's approval (first use of a server, or the run's mode) (409). */
|
|
7999
|
+
| 'pending_approval'
|
|
8000
|
+
/** The call ran, but the output policy withheld its result (403). */
|
|
8001
|
+
| 'withheld'
|
|
8002
|
+
/** The unit's call budget is spent (429). */
|
|
8003
|
+
| 'budget_exhausted'
|
|
8004
|
+
/** The tool failed 5 times in a row and is refused for 60 s (503). */
|
|
8005
|
+
| 'breaker_open'
|
|
8006
|
+
/** The upstream failed (502) or timed out (504). */
|
|
8007
|
+
| 'upstream_error'
|
|
8008
|
+
/**
|
|
8009
|
+
* The call could not be judged, scrubbed or recorded, or (a `rest` tool) its request would have
|
|
8010
|
+
* left the API's pinned host (`status.errorClass` `rest_host_escape`): refused, fail closed (500).
|
|
8011
|
+
*/
|
|
8012
|
+
| 'guard_error';
|
|
8013
|
+
|
|
8014
|
+
/** Who decided a call: an engine gate (`engine:mcp-*`), a steering rule, or the broker itself. */
|
|
8015
|
+
export type McpDecisionBy = string;
|
|
8016
|
+
|
|
8017
|
+
/** `POST /mcp/call`'s answer, on every status. Never carries a secret; `result` only on `ok`. */
|
|
8018
|
+
export interface McpCallResponse {
|
|
8019
|
+
outcome: McpCallOutcome;
|
|
8020
|
+
subject: string;
|
|
8021
|
+
/** The call record's span id (`GET /mcp/usage` and the run's Governance panel resolve it). */
|
|
8022
|
+
callId: string | null;
|
|
8023
|
+
/** The scrubbed `tools/call` result — only when `outcome` is `ok`. */
|
|
8024
|
+
result?: Record<string, unknown>;
|
|
8025
|
+
/** The rules or gates that decided (deny, ask, withheld). */
|
|
8026
|
+
ruleIds: string[];
|
|
8027
|
+
/** Why the result is not handed back; absent on `ok`. */
|
|
8028
|
+
reason?: string;
|
|
8029
|
+
/** What the worker can do instead. */
|
|
8030
|
+
remedy?: string;
|
|
8031
|
+
/** `pending_approval` only: the subject the operator is asked to approve. */
|
|
8032
|
+
pending_approval?: string;
|
|
8033
|
+
/** `denied` / `withheld`: the call was refused (the unit continues). */
|
|
8034
|
+
denied?: true;
|
|
8035
|
+
}
|
|
8036
|
+
|
|
8037
|
+
export type McpCallDecision = 'allow' | 'ask' | 'deny' | 'guard_error';
|
|
8038
|
+
|
|
8039
|
+
/**
|
|
8040
|
+
* One call record (`<state home>/mcp/calls.ndjson`), OTel span shaped. Arguments and results are
|
|
8041
|
+
* NEVER captured: only the subject, the decision, timings and the byte count.
|
|
8042
|
+
*/
|
|
8043
|
+
export interface McpCallRecord {
|
|
8044
|
+
/** The run id, or `null` when the token resolved to no unit (a guard error before judging). */
|
|
8045
|
+
traceId: string | null;
|
|
8046
|
+
spanId: string;
|
|
8047
|
+
/** The unit, `<ord>:<attempt>`; `null` with `traceId`. */
|
|
8048
|
+
parentSpanId: string | null;
|
|
8049
|
+
name: 'mcp.tool.call';
|
|
8050
|
+
start: string;
|
|
8051
|
+
end: string;
|
|
8052
|
+
ms: number;
|
|
8053
|
+
status: { code: 'ok' | 'error'; errorClass: string | null };
|
|
8054
|
+
decision: { decision: McpCallDecision; by: McpDecisionBy; ruleIds: string[]; claimId: string | null };
|
|
8055
|
+
outcome: McpCallOutcome;
|
|
8056
|
+
attrs: {
|
|
8057
|
+
'mcp.subject': string;
|
|
8058
|
+
'mcp.class': McpToolClass | null;
|
|
8059
|
+
'mcp.kind': McpUpstreamKind | null;
|
|
8060
|
+
'wicked.seat': string | null;
|
|
8061
|
+
'wicked.phase': string | null;
|
|
8062
|
+
'wicked.carrier': 'shim';
|
|
8063
|
+
'bytes.out': number;
|
|
8064
|
+
retries: number;
|
|
8065
|
+
};
|
|
8066
|
+
}
|
|
8067
|
+
|
|
8068
|
+
/** The `/ws` frame each call record rides (additive CoreEvent contract: skins that don't know it ignore it). */
|
|
8069
|
+
export interface McpCallCompletedFrame {
|
|
8070
|
+
type: 'mcpCallCompleted';
|
|
8071
|
+
record: McpCallRecord;
|
|
8072
|
+
}
|
|
8073
|
+
|
|
8074
|
+
// ── The unit's tool list (DES-MCP-TOOLS-001 §8, slice S4; api-types 0.65.0) ─────────────────────
|
|
8075
|
+
|
|
8076
|
+
/**
|
|
8077
|
+
* `POST /mcp/tools`: what the garden shim's `list` sends. The token travels in the body, never the
|
|
8078
|
+
* query string, so it stays out of request logs.
|
|
8079
|
+
*/
|
|
8080
|
+
export interface McpToolsBody {
|
|
8081
|
+
token: string;
|
|
8082
|
+
}
|
|
8083
|
+
|
|
8084
|
+
/** One tool the token's unit may try, judged now with no arguments. A certain deny is left out. */
|
|
8085
|
+
export interface McpVisibleTool {
|
|
8086
|
+
/** `mcp:<server>/<tool>`. */
|
|
8087
|
+
subject: string;
|
|
8088
|
+
class: McpToolClass;
|
|
8089
|
+
/** `allow` = a call runs; `ask` = it waits for the operator's approval. */
|
|
8090
|
+
decision: 'allow' | 'ask';
|
|
8091
|
+
ruleIds: string[];
|
|
8092
|
+
/** Why a call would ask. */
|
|
8093
|
+
reason?: string;
|
|
8094
|
+
/** The tool's description, from the server's `tools/list`. */
|
|
8095
|
+
description: string | null;
|
|
8096
|
+
/** The tool's argument JSON schema, from `tools/list`. */
|
|
8097
|
+
inputSchema: Record<string, unknown> | null;
|
|
8098
|
+
}
|
|
8099
|
+
|
|
8100
|
+
/** `POST /mcp/tools`'s answer: the unit the token is bound to and its tools. */
|
|
8101
|
+
export interface McpToolsResponse {
|
|
8102
|
+
unit: { runId: string; ord: number; attempt: number; phase: string; seat: string };
|
|
8103
|
+
tools: McpVisibleTool[];
|
|
8104
|
+
}
|
|
8105
|
+
|
|
8106
|
+
// ── MCP policies: the preview matrix and approvals (DES-MCP-TOOLS-001 §4.5, §4.7, §8, slice S6; api-types 0.63.0) ──
|
|
8107
|
+
|
|
8108
|
+
/** A policy decision: `ask` = the call waits for the operator's approval (it does not run). */
|
|
8109
|
+
export type McpDecision = 'allow' | 'ask' | 'deny';
|
|
8110
|
+
/**
|
|
8111
|
+
* The phase role a matrix row stands for: `creator` = a creator phase with full write posture;
|
|
8112
|
+
* `evaluator` = an evaluator phase (read-only); `recon` = a neutral phase with a read-only posture.
|
|
8113
|
+
*/
|
|
8114
|
+
export type McpPhaseRole = 'creator' | 'evaluator' | 'recon';
|
|
8115
|
+
/** The run's mode (studio's launch autonomy): Gate every step / Gate by risk / Auto. */
|
|
8116
|
+
export type McpRunMode = 'ask' | 'balanced' | 'autonomous';
|
|
8117
|
+
|
|
8118
|
+
/** One matrix cell: what a call from a unit of this shape would get now. Nothing is recorded. */
|
|
8119
|
+
export interface McpPolicyCell {
|
|
8120
|
+
role: McpPhaseRole;
|
|
8121
|
+
seat: string;
|
|
8122
|
+
mode: McpRunMode;
|
|
8123
|
+
phaseId?: string;
|
|
8124
|
+
decision: McpDecision;
|
|
8125
|
+
/** The class the engine judged (the tool's override, else its annotations; none = `write`). */
|
|
8126
|
+
class: McpToolClass;
|
|
8127
|
+
/** The rules that decided: `engine:*` gates first, then steering rule ids (each a Steering row). */
|
|
8128
|
+
ruleIds: string[];
|
|
8129
|
+
obligations: string[];
|
|
8130
|
+
reason: string | null;
|
|
8131
|
+
}
|
|
8132
|
+
|
|
8133
|
+
/** Whether a tool is approved in each ledger, and by which token (`server` = `mcp:<server>`). */
|
|
8134
|
+
export interface McpApprovalState {
|
|
8135
|
+
/** `MCP-FIRST-USE`: the first use of a server asks in every mode until approved. */
|
|
8136
|
+
firstUse: 'server' | 'tool' | null;
|
|
8137
|
+
/** `MCP-POSTURE-WRITE`: in balanced mode a write asks until approved. */
|
|
8138
|
+
write: 'server' | 'tool' | null;
|
|
8139
|
+
}
|
|
8140
|
+
|
|
8141
|
+
export interface McpPolicyPreviewTool {
|
|
8142
|
+
subject: string;
|
|
8143
|
+
server: string;
|
|
8144
|
+
tool: string;
|
|
8145
|
+
class: McpToolClass;
|
|
8146
|
+
status: McpToolStatus;
|
|
8147
|
+
/** The server and the tool are both enabled. */
|
|
8148
|
+
enabled: boolean;
|
|
8149
|
+
/** What the broker sends the engine: enabled and `registered`; otherwise D-5 denies it. */
|
|
8150
|
+
registered: boolean;
|
|
8151
|
+
approval: McpApprovalState;
|
|
8152
|
+
/** One per role × seat × mode, in `roles`, `seats`, `modes` order. */
|
|
8153
|
+
cells: McpPolicyCell[];
|
|
8154
|
+
}
|
|
8155
|
+
|
|
8156
|
+
/** `POST /mcp/policies/preview`. Every field narrows; none = every tool, role, seat and mode. */
|
|
8157
|
+
export interface McpPolicyPreviewBody {
|
|
8158
|
+
/** One tool: `mcp:<server>/<tool>`. */
|
|
8159
|
+
subject?: string;
|
|
8160
|
+
server?: string;
|
|
8161
|
+
phaseRole?: McpPhaseRole;
|
|
8162
|
+
seat?: string;
|
|
8163
|
+
mode?: McpRunMode;
|
|
8164
|
+
/** A workflow phase id a policy may name (`review`, `build`, ...). */
|
|
8165
|
+
phaseId?: string;
|
|
8166
|
+
}
|
|
8167
|
+
|
|
8168
|
+
/** `POST /mcp/policies/preview`'s 200, and `McpPreviewResponse.policies`. */
|
|
8169
|
+
export interface McpPolicyPreviewResponse {
|
|
8170
|
+
roles: McpPhaseRole[];
|
|
8171
|
+
seats: string[];
|
|
8172
|
+
modes: McpRunMode[];
|
|
8173
|
+
phaseId: string | null;
|
|
8174
|
+
/**
|
|
8175
|
+
* Server previews only (`[]` elsewhere): the approvals saving this preview withdraws, because a
|
|
8176
|
+
* saved tool's schema changed. The cells are judged under the CURRENT approvals, so a tool listed
|
|
8177
|
+
* here (or every tool, when `mcp:<server>` is listed) asks again after the save.
|
|
8178
|
+
*/
|
|
8179
|
+
withdrawOnSave: string[];
|
|
8180
|
+
tools: McpPolicyPreviewTool[];
|
|
8181
|
+
}
|
|
8182
|
+
|
|
8183
|
+
/** An approved subject and the ledgers that hold it. */
|
|
8184
|
+
export interface McpApproval {
|
|
8185
|
+
subject: string;
|
|
8186
|
+
scope: 'server' | 'tool';
|
|
8187
|
+
firstUse: boolean;
|
|
8188
|
+
write: boolean;
|
|
8189
|
+
}
|
|
8190
|
+
|
|
8191
|
+
/** A registered, enabled tool that would ask in balanced mode, and why. */
|
|
8192
|
+
export interface McpPendingApproval {
|
|
8193
|
+
subject: string;
|
|
8194
|
+
server: string;
|
|
8195
|
+
tool: string;
|
|
8196
|
+
class: McpToolClass;
|
|
8197
|
+
needs: Array<'first-use' | 'write'>;
|
|
8198
|
+
}
|
|
8199
|
+
|
|
8200
|
+
export interface McpLedgerState {
|
|
8201
|
+
id: string;
|
|
8202
|
+
/** `false` = the mcp-defaults pack was not seeded; approvals are refused (503). */
|
|
8203
|
+
present: boolean;
|
|
8204
|
+
/** A retired first-use ledger still asks: the engine reads it either way. */
|
|
8205
|
+
retired: boolean;
|
|
8206
|
+
}
|
|
8207
|
+
|
|
8208
|
+
/** `GET /mcp/approvals`. */
|
|
8209
|
+
export interface McpApprovalsResponse {
|
|
8210
|
+
approved: McpApproval[];
|
|
8211
|
+
pending: McpPendingApproval[];
|
|
8212
|
+
ledgers: { firstUse: McpLedgerState; write: McpLedgerState };
|
|
8213
|
+
}
|
|
8214
|
+
|
|
8215
|
+
/**
|
|
8216
|
+
* `POST /mcp/approvals`, an audited edit of the ledger rules' `excludes`. `mcp:<server>` approves
|
|
8217
|
+
* the server's FIRST USE (`MCP-FIRST-USE`); its write tools still ask in balanced mode.
|
|
8218
|
+
* `mcp:<server>/<tool>` approves that tool's first use and its writes (`MCP-POSTURE-WRITE` too).
|
|
8219
|
+
* An approval never lifts an engine gate or an explicit deny rule.
|
|
8220
|
+
*/
|
|
8221
|
+
export interface McpApprovalBody {
|
|
8222
|
+
subject: string;
|
|
8223
|
+
}
|
|
8224
|
+
|
|
8225
|
+
/** `POST /mcp/approvals` and `DELETE /mcp/approvals/:subject`: the ledger rules the edit changed. */
|
|
8226
|
+
export interface McpApprovalResponse {
|
|
8227
|
+
subject: string;
|
|
8228
|
+
approved: boolean;
|
|
8229
|
+
rulesChanged: string[];
|
|
8230
|
+
}
|
|
8231
|
+
|
|
8232
|
+
// ── MCP usage: the fold over the call records (DES-MCP-TOOLS-001 §7 Usage, §8 `GET /mcp/usage`; slice S7; api-types 0.66.0) ──
|
|
8233
|
+
|
|
8234
|
+
/** Every call record in a window, split by the broker's decision. */
|
|
8235
|
+
export interface McpDecisionCounts {
|
|
8236
|
+
allow: number;
|
|
8237
|
+
ask: number;
|
|
8238
|
+
deny: number;
|
|
8239
|
+
guard_error: number;
|
|
8240
|
+
}
|
|
8241
|
+
|
|
8242
|
+
/**
|
|
8243
|
+
* The numbers a set of call records folds to. `ran` counts the calls the broker let through
|
|
8244
|
+
* (`decision: allow`); the error rate and the latency percentiles are over those calls only, since
|
|
8245
|
+
* a denied or asked call never reached an upstream. Percentiles are nearest-rank: the value at
|
|
8246
|
+
* position `ceil(q * n)` of the sorted durations. `null` = no call ran.
|
|
8247
|
+
*/
|
|
8248
|
+
export interface McpUsageStats {
|
|
8249
|
+
calls: number;
|
|
8250
|
+
decisions: McpDecisionCounts;
|
|
8251
|
+
ran: number;
|
|
8252
|
+
/** Ran calls whose status is `error` (an upstream failure or a tool error, `isError`). */
|
|
8253
|
+
errors: number;
|
|
8254
|
+
errorRate: number | null;
|
|
8255
|
+
p50Ms: number | null;
|
|
8256
|
+
p95Ms: number | null;
|
|
8257
|
+
p99Ms: number | null;
|
|
8258
|
+
}
|
|
8259
|
+
|
|
8260
|
+
/** One tool's row of the per-tool table. */
|
|
8261
|
+
export interface McpUsageTool extends McpUsageStats {
|
|
8262
|
+
subject: string;
|
|
8263
|
+
server: string;
|
|
8264
|
+
tool: string;
|
|
8265
|
+
/** The class the engine judged on the newest record; `null` = never judged (a guard error). */
|
|
8266
|
+
class: McpToolClass | null;
|
|
8267
|
+
/** The seats that called it, sorted. */
|
|
8268
|
+
seats: string[];
|
|
8269
|
+
lastCall: string;
|
|
8270
|
+
}
|
|
8271
|
+
|
|
8272
|
+
/** One server's totals: its row's "last used" and "calls over the window". */
|
|
8273
|
+
export interface McpUsageServer {
|
|
8274
|
+
server: string;
|
|
8275
|
+
calls: number;
|
|
8276
|
+
decisions: McpDecisionCounts;
|
|
8277
|
+
lastCall: string;
|
|
8278
|
+
}
|
|
8279
|
+
|
|
8280
|
+
/** The drill-down: one tool × seat × run. `runId: null` = calls the broker refused before judging. */
|
|
8281
|
+
export interface McpUsageRun {
|
|
8282
|
+
subject: string;
|
|
8283
|
+
seat: string | null;
|
|
8284
|
+
runId: string | null;
|
|
8285
|
+
calls: number;
|
|
8286
|
+
decisions: McpDecisionCounts;
|
|
8287
|
+
/** Ran calls whose status is `error`, as in {@link McpUsageStats}. */
|
|
8288
|
+
errors: number;
|
|
8289
|
+
lastCall: string;
|
|
8290
|
+
}
|
|
8291
|
+
|
|
8292
|
+
/**
|
|
8293
|
+
* A tool chain: `from` then `to`, two consecutive calls of one unit attempt (by start time), with
|
|
8294
|
+
* different subjects. `count` = how often; `runs` = in how many runs.
|
|
8295
|
+
*/
|
|
8296
|
+
export interface McpUsageChain {
|
|
8297
|
+
from: string;
|
|
8298
|
+
to: string;
|
|
8299
|
+
count: number;
|
|
8300
|
+
runs: number;
|
|
8301
|
+
}
|
|
8302
|
+
|
|
8303
|
+
/** One UTC day the window touches (its first day is partial), days with no calls included. */
|
|
8304
|
+
export interface McpUsageDay {
|
|
8305
|
+
day: string;
|
|
8306
|
+
calls: number;
|
|
8307
|
+
decisions: McpDecisionCounts;
|
|
8308
|
+
}
|
|
8309
|
+
|
|
8310
|
+
/**
|
|
8311
|
+
* `GET /mcp/usage?days&subject&seat&decision`. `days` is 1-30 (default 7); the window is the last
|
|
8312
|
+
* `days` × 24 h. Every filter narrows every section, except `chains`: a chain is folded from the
|
|
8313
|
+
* unit's calls under the seat and decision filters, then kept when either end is `subject`.
|
|
8314
|
+
* Records older than 30 days are folded into `calls-daily.ndjson` and are not in any window.
|
|
8315
|
+
*/
|
|
8316
|
+
export interface McpUsageResponse {
|
|
8317
|
+
days: number;
|
|
8318
|
+
since: string;
|
|
8319
|
+
until: string;
|
|
8320
|
+
filters: { subject: string | null; seat: string | null; decision: McpCallDecision | null };
|
|
8321
|
+
totals: McpUsageStats;
|
|
8322
|
+
/** Most calls first, then by subject. */
|
|
8323
|
+
tools: McpUsageTool[];
|
|
8324
|
+
/** Most calls first, then by name. */
|
|
8325
|
+
servers: McpUsageServer[];
|
|
8326
|
+
/** Newest first, at most 200. */
|
|
8327
|
+
runs: McpUsageRun[];
|
|
8328
|
+
/** Most frequent first, at most 20. */
|
|
8329
|
+
chains: McpUsageChain[];
|
|
8330
|
+
/** Oldest first. */
|
|
8331
|
+
daily: McpUsageDay[];
|
|
8332
|
+
/** The seats seen in the window before any filter (the seat filter's options), sorted. */
|
|
8333
|
+
seats: string[];
|
|
8334
|
+
/** Lines of `calls.ndjson` that did not parse as a call record. */
|
|
8335
|
+
skipped: number;
|
|
8336
|
+
}
|