wicked-crew 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/api/elicitation-cache.d.ts +93 -0
  2. package/dist/api/elicitation-cache.d.ts.map +1 -0
  3. package/dist/api/elicitation-cache.js +128 -0
  4. package/dist/api/elicitation-cache.js.map +1 -0
  5. package/dist/api/evidence.d.ts +37 -38
  6. package/dist/api/evidence.d.ts.map +1 -1
  7. package/dist/api/evidence.js +40 -36
  8. package/dist/api/evidence.js.map +1 -1
  9. package/dist/api/gate-cache.d.ts +29 -13
  10. package/dist/api/gate-cache.d.ts.map +1 -1
  11. package/dist/api/gate-cache.js +82 -36
  12. package/dist/api/gate-cache.js.map +1 -1
  13. package/dist/api/requirements.d.ts +66 -0
  14. package/dist/api/requirements.d.ts.map +1 -0
  15. package/dist/api/requirements.js +328 -0
  16. package/dist/api/requirements.js.map +1 -0
  17. package/dist/api/routes.d.ts +2 -1
  18. package/dist/api/routes.d.ts.map +1 -1
  19. package/dist/api/routes.js +494 -34
  20. package/dist/api/routes.js.map +1 -1
  21. package/dist/api/server.d.ts.map +1 -1
  22. package/dist/api/server.js +13 -6
  23. package/dist/api/server.js.map +1 -1
  24. package/dist/core/adapter.d.ts +94 -6
  25. package/dist/core/adapter.d.ts.map +1 -1
  26. package/dist/core/adapter.js +386 -86
  27. package/dist/core/adapter.js.map +1 -1
  28. package/dist/core/exec.d.ts +37 -0
  29. package/dist/core/exec.d.ts.map +1 -0
  30. package/dist/core/exec.js +97 -0
  31. package/dist/core/exec.js.map +1 -0
  32. package/dist/core/repoPaths.d.ts +37 -0
  33. package/dist/core/repoPaths.d.ts.map +1 -0
  34. package/dist/core/repoPaths.js +48 -0
  35. package/dist/core/repoPaths.js.map +1 -0
  36. package/dist/core/types.d.ts +81 -2
  37. package/dist/core/types.d.ts.map +1 -1
  38. package/dist/core/types.js.map +1 -1
  39. package/dist/studio/assets/index-Ci_R6ARr.js +423 -0
  40. package/dist/studio/assets/index-Dio_c1Q3.css +32 -0
  41. package/dist/studio/index.html +2 -2
  42. package/package.json +14 -13
  43. package/dist/studio/assets/index-BCcaLGxk.js +0 -420
  44. package/dist/studio/assets/index-vsyAOtPq.css +0 -32
@@ -0,0 +1,93 @@
1
+ import type { CoreEvent, SessionView } from '../core/types.js';
2
+ /**
3
+ * One pending elicitation for a run.
4
+ *
5
+ * Keyed by `runId`; there is at most one pending elicitation per run at a time
6
+ * (the bridge cancels any prior slot before opening a new one).
7
+ */
8
+ export interface ElicitationEntry {
9
+ runId: string;
10
+ elicitationId: string;
11
+ message: string;
12
+ /** Ordered set of valid responses; `null` means free-text. */
13
+ options: string[] | null;
14
+ /** When the daemon observed the elicitation — ISO-8601. */
15
+ receivedAt: string;
16
+ }
17
+ /**
18
+ * Pending-elicitation display store (DES-002 §4 P-2 / §6.3).
19
+ *
20
+ * Keyed by `runId`. The generation counter (one per run) guards against zombie
21
+ * entries: any mutation that makes a previously-taken entry stale bumps the
22
+ * counter, so `restoreIfUnchanged` is a no-op after a terminal event or a new
23
+ * elicitation arrived in the window between `take()` and a failed POST.
24
+ *
25
+ * # Concurrency model
26
+ *
27
+ * This runs on Node's single event-loop thread — all operations are synchronous
28
+ * and therefore atomic at the JS level. `take()` atomically removes the entry
29
+ * and returns the generation snapshot; `restoreIfUnchanged` only writes back if
30
+ * the generation is unchanged (no intervening mutation).
31
+ */
32
+ export declare class ElicitationCache {
33
+ private readonly entries;
34
+ /** Monotone generation counter per run. Starts at 0; every mutation increments. */
35
+ private readonly gens;
36
+ private bumpGen;
37
+ /**
38
+ * Store a new elicitation for a run, replacing any previous one.
39
+ *
40
+ * Caller supplies all fields except `receivedAt`, which is stamped here.
41
+ */
42
+ create(entry: Omit<ElicitationEntry, 'receivedAt'>): void;
43
+ /** The current pending elicitation for a run, if any. Does not mutate. */
44
+ get(runId: string): ElicitationEntry | undefined;
45
+ /**
46
+ * Atomically remove and return the entry for a run, together with the
47
+ * generation snapshot at the moment of removal.
48
+ *
49
+ * Returns `undefined` when there is no pending elicitation for the run.
50
+ * The generation counter is NOT incremented by `take` itself — only
51
+ * subsequent mutations (new elicitation, terminal event, reconcile) bump it.
52
+ * This is intentional: it lets `restoreIfUnchanged` detect exactly those
53
+ * mutations without counting the take itself as a change.
54
+ */
55
+ take(runId: string): {
56
+ entry: ElicitationEntry;
57
+ gen: number;
58
+ } | undefined;
59
+ /**
60
+ * Restore a previously taken entry if, and only if, the generation has not
61
+ * changed since `take()`.
62
+ *
63
+ * Returns `true` when the entry was actually restored (and bumps the gen).
64
+ * Returns `false` and is a no-op when any intervening mutation changed the gen.
65
+ *
66
+ * Intended for the POST /runs/:id/elicitation error path: if the adapter call
67
+ * fails, attempt to restore the prompt so the UI can retry — but only if no
68
+ * terminal event or superseding elicitation arrived in the meantime.
69
+ */
70
+ restoreIfUnchanged(runId: string, entry: ElicitationEntry, gen: number): boolean;
71
+ /**
72
+ * Fold one live CoreEvent into the cache.
73
+ *
74
+ * `elicitationCreated` → store the new prompt (replaces any prior one).
75
+ * Terminal events → delete the entry and bump the generation.
76
+ * All other events → no-op.
77
+ */
78
+ ingest(event: CoreEvent): void;
79
+ /**
80
+ * Drop entries whose run is in a terminal status and bump their generation.
81
+ *
82
+ * Called on every GET /runs reconcile. Unlike `GateCache.reconcile` (which
83
+ * only prunes entries that exist), this ALWAYS bumps the generation for
84
+ * terminal-status runs, even when no entry is currently present.
85
+ *
86
+ * Why: a POST may have taken the entry just before reconcile runs. If the
87
+ * run is already terminal, the subsequent `restoreIfUnchanged` in the POST
88
+ * error path must be a no-op — the generation bump here ensures that,
89
+ * regardless of whether the entry was present at reconcile time (F8).
90
+ */
91
+ reconcile(views: SessionView[]): void;
92
+ }
93
+ //# sourceMappingURL=elicitation-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elicitation-cache.d.ts","sourceRoot":"","sources":["../../src/api/elicitation-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACzB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;CACpB;AAQD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,mFAAmF;IACnF,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6B;IAElD,OAAO,CAAC,OAAO;IAIf;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,gBAAgB,EAAE,YAAY,CAAC,GAAG,IAAI;IAWzD,0EAA0E;IAC1E,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAIhD;;;;;;;;;OASG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,gBAAgB,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS;IAQzE;;;;;;;;;;OAUG;IACH,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAQhF;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAiB9B;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,IAAI;CAQtC"}
@@ -0,0 +1,128 @@
1
+ /** Session statuses that mean the run will never elicit again. */
2
+ const TERMINAL_STATUSES = new Set(['completed', 'cancelled', 'failed']);
3
+ /** CoreEvent types that signal a run is terminal (matching TERMINAL_STATUSES). */
4
+ const TERMINAL_EVENTS = new Set(['sessionCompleted', 'runCancelled', 'sessionFailed']);
5
+ /**
6
+ * Pending-elicitation display store (DES-002 §4 P-2 / §6.3).
7
+ *
8
+ * Keyed by `runId`. The generation counter (one per run) guards against zombie
9
+ * entries: any mutation that makes a previously-taken entry stale bumps the
10
+ * counter, so `restoreIfUnchanged` is a no-op after a terminal event or a new
11
+ * elicitation arrived in the window between `take()` and a failed POST.
12
+ *
13
+ * # Concurrency model
14
+ *
15
+ * This runs on Node's single event-loop thread — all operations are synchronous
16
+ * and therefore atomic at the JS level. `take()` atomically removes the entry
17
+ * and returns the generation snapshot; `restoreIfUnchanged` only writes back if
18
+ * the generation is unchanged (no intervening mutation).
19
+ */
20
+ export class ElicitationCache {
21
+ entries = new Map();
22
+ /** Monotone generation counter per run. Starts at 0; every mutation increments. */
23
+ gens = new Map();
24
+ bumpGen(runId) {
25
+ this.gens.set(runId, (this.gens.get(runId) ?? 0) + 1);
26
+ }
27
+ /**
28
+ * Store a new elicitation for a run, replacing any previous one.
29
+ *
30
+ * Caller supplies all fields except `receivedAt`, which is stamped here.
31
+ */
32
+ create(entry) {
33
+ this.entries.set(entry.runId, {
34
+ runId: entry.runId,
35
+ elicitationId: entry.elicitationId,
36
+ message: entry.message,
37
+ options: entry.options ?? null,
38
+ receivedAt: new Date().toISOString(),
39
+ });
40
+ this.bumpGen(entry.runId);
41
+ }
42
+ /** The current pending elicitation for a run, if any. Does not mutate. */
43
+ get(runId) {
44
+ return this.entries.get(runId);
45
+ }
46
+ /**
47
+ * Atomically remove and return the entry for a run, together with the
48
+ * generation snapshot at the moment of removal.
49
+ *
50
+ * Returns `undefined` when there is no pending elicitation for the run.
51
+ * The generation counter is NOT incremented by `take` itself — only
52
+ * subsequent mutations (new elicitation, terminal event, reconcile) bump it.
53
+ * This is intentional: it lets `restoreIfUnchanged` detect exactly those
54
+ * mutations without counting the take itself as a change.
55
+ */
56
+ take(runId) {
57
+ const entry = this.entries.get(runId);
58
+ if (!entry)
59
+ return undefined;
60
+ const gen = this.gens.get(runId) ?? 0;
61
+ this.entries.delete(runId);
62
+ return { entry, gen };
63
+ }
64
+ /**
65
+ * Restore a previously taken entry if, and only if, the generation has not
66
+ * changed since `take()`.
67
+ *
68
+ * Returns `true` when the entry was actually restored (and bumps the gen).
69
+ * Returns `false` and is a no-op when any intervening mutation changed the gen.
70
+ *
71
+ * Intended for the POST /runs/:id/elicitation error path: if the adapter call
72
+ * fails, attempt to restore the prompt so the UI can retry — but only if no
73
+ * terminal event or superseding elicitation arrived in the meantime.
74
+ */
75
+ restoreIfUnchanged(runId, entry, gen) {
76
+ const current = this.gens.get(runId) ?? 0;
77
+ if (current !== gen)
78
+ return false;
79
+ this.entries.set(runId, entry);
80
+ this.bumpGen(runId);
81
+ return true;
82
+ }
83
+ /**
84
+ * Fold one live CoreEvent into the cache.
85
+ *
86
+ * `elicitationCreated` → store the new prompt (replaces any prior one).
87
+ * Terminal events → delete the entry and bump the generation.
88
+ * All other events → no-op.
89
+ */
90
+ ingest(event) {
91
+ const runId = event.session;
92
+ if (typeof runId !== 'string')
93
+ return;
94
+ if (event.type === 'elicitationCreated') {
95
+ this.create({
96
+ runId,
97
+ elicitationId: String(event.elicitationId ?? ''),
98
+ message: String(event.message ?? ''),
99
+ options: Array.isArray(event.options) ? event.options : null,
100
+ });
101
+ }
102
+ else if (TERMINAL_EVENTS.has(event.type)) {
103
+ this.entries.delete(runId);
104
+ this.bumpGen(runId);
105
+ }
106
+ }
107
+ /**
108
+ * Drop entries whose run is in a terminal status and bump their generation.
109
+ *
110
+ * Called on every GET /runs reconcile. Unlike `GateCache.reconcile` (which
111
+ * only prunes entries that exist), this ALWAYS bumps the generation for
112
+ * terminal-status runs, even when no entry is currently present.
113
+ *
114
+ * Why: a POST may have taken the entry just before reconcile runs. If the
115
+ * run is already terminal, the subsequent `restoreIfUnchanged` in the POST
116
+ * error path must be a no-op — the generation bump here ensures that,
117
+ * regardless of whether the entry was present at reconcile time (F8).
118
+ */
119
+ reconcile(views) {
120
+ for (const view of views) {
121
+ if (TERMINAL_STATUSES.has(view.session.status)) {
122
+ this.entries.delete(view.session.id);
123
+ this.bumpGen(view.session.id);
124
+ }
125
+ }
126
+ }
127
+ }
128
+ //# sourceMappingURL=elicitation-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elicitation-cache.js","sourceRoot":"","sources":["../../src/api/elicitation-cache.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,20 @@
1
- import type { AgentSession, RoutingInfo, SessionView, WorkUnit } from '../core/types.js';
1
+ import type { AgentSession, RecordedEvent, SessionView, WorkUnit } from '../core/types.js';
2
+ export type { RecordedEvent };
2
3
  /**
3
4
  * The run evidence bundle — everything an operator needs to audit a finished run,
4
5
  * in one downloadable JSON document (`GET /runs/:id/evidence`).
5
6
  *
6
- * The daemon persists no event log of its own (CoreEvent frames are fanned out
7
- * over `/ws` and dropped), so the decision trail is RE-DERIVED from the run DTO,
8
- * which is where core durably records it: `routing` carries the council/routing
9
- * provenance and `status`/`denial_reason`/`phase_status` carry the gate outcome.
10
- * Nothing here is invented — every field traces to a field on the run.
7
+ * `events` is core's DURABLE EVENT LOG for the run, read back verbatim. It used to
8
+ * be re-derived here from the unit records, because the daemon persisted no log of
9
+ * its own — which meant an exported bundle carried a handful of pseudo-events in a
10
+ * vocabulary this file invented (`routingDecided`) instead of what actually
11
+ * happened. Re-derivation cannot recover what it never saw: council deliberation
12
+ * and votes, gate depth, escalations, fallbacks, and every timestamp were simply
13
+ * absent, and nothing in the bundle said so. (FINDING-014)
14
+ *
15
+ * Nothing was lost by dropping the derived events: `routing`, `assigned_cli`,
16
+ * `denial_reason`, `phase_status` and `conformance_ref` were their only inputs and
17
+ * they ride in `units` verbatim already.
11
18
  */
12
19
  export interface EvidenceBundle {
13
20
  /** When the bundle was assembled (ISO-8601). */
@@ -16,8 +23,17 @@ export interface EvidenceBundle {
16
23
  session: AgentSession;
17
24
  /** The run's units in `ord` order, each with its captured transcript. */
18
25
  units: EvidenceUnit[];
19
- /** The derived decision trail, in unit order (routing before that unit's gate). */
20
- events: EvidenceEvent[];
26
+ /**
27
+ * The run's recorded event history, oldest first — the same frames `/ws` carried
28
+ * live, each with a capture-time `ts` and an ordering `seq`.
29
+ */
30
+ events: RecordedEvent[];
31
+ /**
32
+ * Why `events` is empty, present ONLY when it is. An empty trail must never be
33
+ * indistinguishable from a run that genuinely decided nothing — the same posture
34
+ * `transcriptError` takes for a unit.
35
+ */
36
+ eventsUnavailable?: string;
21
37
  /**
22
38
  * Structured assumptions re-parsed from the transcripts (the external-transform
23
39
  * convention): third-party payload transformations agents relied on. Entries with
@@ -45,42 +61,25 @@ export interface EvidenceUnit extends WorkUnit {
45
61
  */
46
62
  transcriptError?: string;
47
63
  }
48
- /** A gate decision, re-derived from the unit's persisted outcome. */
49
- export interface GateDecisionEvent {
50
- type: 'gateDecided';
51
- unitId: string;
52
- ord: number;
53
- /** `true` when the unit passed its gate (`done`), `false` when it was `rejected`. */
54
- allow: boolean;
55
- denialReason: string | null;
56
- phaseStatus: string | null;
57
- conformanceRef: string | null;
58
- }
59
- /** Council / routing provenance, re-derived from the unit's `routing`. */
60
- export interface RoutingDecidedEvent {
61
- type: 'routingDecided';
62
- unitId: string;
63
- ord: number;
64
- assignedCli: string | null;
65
- routing: RoutingInfo;
66
- }
67
- export type EvidenceEvent = GateDecisionEvent | RoutingDecidedEvent;
68
64
  /** Loads one unit's captured transcript (the adapter's `workOutput`). */
69
65
  export type TranscriptLoader = (coreUnitId: string) => Promise<string | null>;
70
66
  /**
71
- * Re-derive the decision trail from the units. A unit contributes its routing
72
- * provenance (when the council/router recorded one) and its gate decision (once
73
- * the gate has actually resolved — a `pending`/`distributed` unit has no verdict
74
- * yet, and an evidence bundle must not imply one).
67
+ * Loads a run's recorded event history (the adapter's `runEvents`).
68
+ *
69
+ * `null` means the engine has no event-log binding at all — a capability gap, not an empty run.
70
+ * The adapter returns it rather than `[]` so this module can say which of the two happened.
75
71
  */
76
- export declare function evidenceEvents(units: WorkUnit[]): EvidenceEvent[];
72
+ export type EventLoader = (runId: string) => Promise<RecordedEvent[] | null>;
77
73
  /**
78
- * Assemble a run's evidence bundle. Transcripts are read per unit; a single
79
- * unreadable transcript degrades that unit to `transcript: null` + a
80
- * `transcriptError` rather than failing the whole export — a partial bundle that
81
- * says which part is missing beats no bundle at all.
74
+ * Assemble a run's evidence bundle.
75
+ *
76
+ * Every read degrades in place rather than failing the export: an unreadable
77
+ * transcript becomes `transcript: null` + `transcriptError` on that unit, and an
78
+ * unreadable event history becomes `events: []` + `eventsUnavailable`. A partial
79
+ * bundle that says which part is missing beats no bundle at all — but it must say
80
+ * so, or the gap reads as a fact about the run.
82
81
  */
83
- export declare function buildEvidenceBundle(view: SessionView, loadTranscript: TranscriptLoader): Promise<EvidenceBundle>;
82
+ export declare function buildEvidenceBundle(view: SessionView, loadTranscript: TranscriptLoader, loadEvents: EventLoader): Promise<EvidenceBundle>;
84
83
  /**
85
84
  * Parse external-transform markers from one transcript — mirrors wicked-core's
86
85
  * `assumptions::parse` (same grammar, same malformed→needs-review posture) so the
@@ -1 +1 @@
1
- {"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../../src/api/evidence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEzF;;;;;;;;;GASG;AACH,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,OAAO,EAAE,YAAY,CAAC;IACtB,yEAAyE;IACzE,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,mFAAmF;IACnF,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB;;;;OAIG;IACH,WAAW,EAAE,kBAAkB,EAAE,CAAC;CACnC;AAED,uEAAuE;AACvE,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,oBAAoB,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,2FAA2F;AAC3F,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC5C,yEAAyE;IACzE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,qEAAqE;AACrE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,qFAAqF;IACrF,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,0EAA0E;AAC1E,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,MAAM,aAAa,GAAG,iBAAiB,GAAG,mBAAmB,CAAC;AAEpE,yEAAyE;AACzE,MAAM,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAW9E;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,aAAa,EAAE,CAyBjE;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,WAAW,EACjB,cAAc,EAAE,gBAAgB,GAC/B,OAAO,CAAC,cAAc,CAAC,CAsBzB;AAKD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,kBAAkB,EAAE,CA0D7F;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGtD"}
1
+ {"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../../src/api/evidence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE3F,YAAY,EAAE,aAAa,EAAE,CAAC;AAE9B;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,OAAO,EAAE,YAAY,CAAC;IACtB,yEAAyE;IACzE,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB;;;OAGG;IACH,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,WAAW,EAAE,kBAAkB,EAAE,CAAC;CACnC;AAED,uEAAuE;AACvE,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,oBAAoB,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,2FAA2F;AAC3F,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC5C,yEAAyE;IACzE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,yEAAyE;AACzE,MAAM,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAE9E;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,CAAC;AA+B7E;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,WAAW,EACjB,cAAc,EAAE,gBAAgB,EAChC,UAAU,EAAE,WAAW,GACtB,OAAO,CAAC,cAAc,CAAC,CAwCzB;AAKD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,kBAAkB,EAAE,CA0D7F;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGtD"}
@@ -7,44 +7,31 @@ function coreUnitId(runId, unit) {
7
7
  return unit.id.startsWith(`${runId}:`) ? unit.id : `${runId}:u${unit.ord}`;
8
8
  }
9
9
  /**
10
- * Re-derive the decision trail from the units. A unit contributes its routing
11
- * provenance (when the council/router recorded one) and its gate decision (once
12
- * the gate has actually resolved — a `pending`/`distributed` unit has no verdict
13
- * yet, and an evidence bundle must not imply one).
10
+ * Why a run's `events` came back empty. Both cases are reported, never conflated:
11
+ * a read that failed is an operator problem, a run with no recorded history is a
12
+ * statement about that run.
14
13
  */
15
- export function evidenceEvents(units) {
16
- const events = [];
17
- for (const unit of [...units].sort((a, b) => a.ord - b.ord)) {
18
- if (unit.routing) {
19
- events.push({
20
- type: 'routingDecided',
21
- unitId: unit.id,
22
- ord: unit.ord,
23
- assignedCli: unit.assigned_cli,
24
- routing: unit.routing,
25
- });
26
- }
27
- if (unit.status === 'done' || unit.status === 'rejected') {
28
- events.push({
29
- type: 'gateDecided',
30
- unitId: unit.id,
31
- ord: unit.ord,
32
- allow: unit.status === 'done',
33
- denialReason: unit.denial_reason,
34
- phaseStatus: unit.phase_status,
35
- conformanceRef: unit.conformance_ref,
36
- });
37
- }
38
- }
39
- return events;
40
- }
14
+ const NO_EVENTS_RECORDED = 'core recorded no events for this run — it ran before the durable event log existed ' +
15
+ '(FINDING-014), or its log was removed. This is not a run that made no decisions.';
41
16
  /**
42
- * Assemble a run's evidence bundle. Transcripts are read per unit; a single
43
- * unreadable transcript degrades that unit to `transcript: null` + a
44
- * `transcriptError` rather than failing the whole export — a partial bundle that
45
- * says which part is missing beats no bundle at all.
17
+ * The third cause, and a different kind of problem from the other two: the engine BUILD in use
18
+ * exposes no `runEvents` binding, so no run on this daemon has a readable trail. Reporting that as
19
+ * "this run recorded nothing" would blame the run for a missing capability — the FINDING-050 shape,
20
+ * distinct causes wearing one message.
46
21
  */
47
- export async function buildEvidenceBundle(view, loadTranscript) {
22
+ const NO_EVENT_LOG_BINDING = 'the running wicked-core build exposes no durable event log (runEvents), so no decision trail ' +
23
+ 'can be read for any run on this daemon — rebuild/reinstall wicked-core-ts. This says nothing ' +
24
+ 'about what this run decided.';
25
+ /**
26
+ * Assemble a run's evidence bundle.
27
+ *
28
+ * Every read degrades in place rather than failing the export: an unreadable
29
+ * transcript becomes `transcript: null` + `transcriptError` on that unit, and an
30
+ * unreadable event history becomes `events: []` + `eventsUnavailable`. A partial
31
+ * bundle that says which part is missing beats no bundle at all — but it must say
32
+ * so, or the gap reads as a fact about the run.
33
+ */
34
+ export async function buildEvidenceBundle(view, loadTranscript, loadEvents) {
48
35
  const ordered = [...view.units].sort((a, b) => a.ord - b.ord);
49
36
  const units = await Promise.all(ordered.map(async (unit) => {
50
37
  try {
@@ -58,11 +45,28 @@ export async function buildEvidenceBundle(view, loadTranscript) {
58
45
  };
59
46
  }
60
47
  }));
48
+ let events = [];
49
+ let eventsUnavailable;
50
+ try {
51
+ const loaded = await loadEvents(view.session.id);
52
+ if (loaded === null) {
53
+ eventsUnavailable = NO_EVENT_LOG_BINDING;
54
+ }
55
+ else {
56
+ events = loaded;
57
+ if (events.length === 0)
58
+ eventsUnavailable = NO_EVENTS_RECORDED;
59
+ }
60
+ }
61
+ catch (err) {
62
+ eventsUnavailable = `could not read the run's event history: ${err instanceof Error ? err.message : String(err)}`;
63
+ }
61
64
  return {
62
65
  exportedAt: new Date().toISOString(),
63
66
  session: view.session,
64
67
  units,
65
- events: evidenceEvents(ordered),
68
+ events,
69
+ ...(eventsUnavailable === undefined ? {} : { eventsUnavailable }),
66
70
  assumptions: units.flatMap((u) => parseAssumptions(u.ord, u.transcript)),
67
71
  };
68
72
  }
@@ -1 +1 @@
1
- {"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../src/api/evidence.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../src/api/evidence.ts"],"names":[],"mappings":"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"}
@@ -11,25 +11,41 @@ export interface GateCacheEntry {
11
11
  receivedAt: string;
12
12
  }
13
13
  /**
14
- * Self-healing gate-prompt cache (DES-STUDIO-001 §3.3).
14
+ * Gate-prompt cache over a durable log (DES-STUDIO-001 §3.3).
15
15
  *
16
- * Core does NOT persist the gate prompt — it exists only on the transient
17
- * `awaitingHuman` CoreEvent (`AgentSession` has no prompt field). So the daemon
18
- * event-sources it here, keyed by runId (a paused run has exactly one open gate,
19
- * before `unit_ix`). Entries are pruned on any resumed/terminal event for the
20
- * run, and re-checked against `sessionsDetail()` on every list reconcile so the
21
- * cache can neither leak nor go stale ("re-merged on reconcile, pruned to
22
- * still-paused — can't leak or blank").
16
+ * The prompt is keyed by runId — a paused run has exactly one open gate, before `unit_ix`. Entries
17
+ * are pruned on any resumed/terminal event for the run, and re-checked against `sessionsDetail()`
18
+ * on every list reconcile, so the cache can neither leak nor go stale.
23
19
  *
24
- * NOTE: the full HITL lifecycle from §3.3 (advisory / hard-expiry timers,
25
- * append-only message log, auto-cancel-on-expiry, synthesized `gateLifecycle`
26
- * frames) is a deliberate follow-on. This increment caches only the prompt so a
27
- * late-joining browser can render the gate; `lifecycle` is always `open`.
20
+ * That self-healing claim was true and beside the point until FINDING-051: every path here was
21
+ * prune-only, so a daemon restart dropped the map and **nothing could put an entry back**. A parked
22
+ * run then kept holding the operator without being able to say what it was asking. The prompt was
23
+ * never actually lost — core records `awaitingHuman` to the event log (core#139) — it was simply
24
+ * never read. `rebuild` is that read, which demotes this map to what it was always meant to be: a
25
+ * latency optimisation, not the system of record.
26
+ *
27
+ * NOTE: the full HITL lifecycle from §3.3 (advisory / hard-expiry timers, append-only message log,
28
+ * auto-cancel-on-expiry, synthesized `gateLifecycle` frames) is a deliberate follow-on. This
29
+ * increment caches only the prompt so a late-joining browser can render the gate; `lifecycle` is
30
+ * always `open`.
28
31
  */
29
32
  export declare class GateCache {
30
33
  private readonly entries;
31
- /** Fold one CoreEvent into the cache. */
34
+ /** Fold one live CoreEvent into the cache. */
32
35
  ingest(event: CoreEvent): void;
36
+ /**
37
+ * Rebuild one run's gate from its durable event history and adopt the result (FINDING-051).
38
+ *
39
+ * `events` is the run's log oldest-first. Folding all of it yields the last `awaitingHuman` not
40
+ * followed by a resumed/terminal event — i.e. an open gate, or nothing.
41
+ *
42
+ * Deliberately does NOT cross-check the run's live status first: that costs a `sessionsDetail()`
43
+ * on a read path, and the write side is already guarded — approving a run that has moved on is
44
+ * refused by the `status === 'awaiting_human'` check on `POST /runs/:id/gate`. So the worst a
45
+ * stale replay can do is show a prompt that then declines to be approved, and `reconcile` drops
46
+ * it on the next list. The failure mode is a confusing read, never a bad write.
47
+ */
48
+ rebuild(runId: string, events: CoreEvent[]): GateCacheEntry | undefined;
33
49
  /** The cached open gate for a run, if any. */
34
50
  get(runId: string): GateCacheEntry | undefined;
35
51
  /** Drop cache entries whose run is no longer `awaiting_human` (self-healing on reconcile). */
@@ -1 +1 @@
1
- {"version":3,"file":"gate-cache.d.ts","sourceRoot":"","sources":["../../src/api/gate-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/D,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;IACf,sFAAsF;IACtF,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAE7D,yCAAyC;IACzC,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAyB9B,8CAA8C;IAC9C,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAI9C,8FAA8F;IAC9F,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,IAAI;CAQtC"}
1
+ {"version":3,"file":"gate-cache.d.ts","sourceRoot":"","sources":["../../src/api/gate-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/D,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;IACf,sFAAsF;IACtF,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;CACpB;AA+CD;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAE7D,8CAA8C;IAC9C,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAI9B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,cAAc,GAAG,SAAS;IAQvE,8CAA8C;IAC9C,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAI9C,8FAA8F;IAC9F,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,IAAI;CAQtC"}