wicked-crew 0.3.2 → 0.5.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/dist/api/api-prefix.d.ts +13 -0
- package/dist/api/api-prefix.d.ts.map +1 -0
- package/dist/api/api-prefix.js +13 -0
- package/dist/api/api-prefix.js.map +1 -0
- package/dist/api/elicitation-cache.d.ts +93 -0
- package/dist/api/elicitation-cache.d.ts.map +1 -0
- package/dist/api/elicitation-cache.js +128 -0
- package/dist/api/elicitation-cache.js.map +1 -0
- package/dist/api/evidence.d.ts +48 -38
- package/dist/api/evidence.d.ts.map +1 -1
- package/dist/api/evidence.js +48 -39
- package/dist/api/evidence.js.map +1 -1
- package/dist/api/gate-cache.d.ts +29 -13
- package/dist/api/gate-cache.d.ts.map +1 -1
- package/dist/api/gate-cache.js +82 -36
- package/dist/api/gate-cache.js.map +1 -1
- package/dist/api/requirements.d.ts +12 -3
- package/dist/api/requirements.d.ts.map +1 -1
- package/dist/api/requirements.js +34 -37
- package/dist/api/requirements.js.map +1 -1
- package/dist/api/routes.d.ts +3 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +468 -51
- package/dist/api/routes.js.map +1 -1
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +13 -6
- package/dist/api/server.js.map +1 -1
- package/dist/api/unit-output.d.ts +62 -0
- package/dist/api/unit-output.d.ts.map +1 -0
- package/dist/api/unit-output.js +108 -0
- package/dist/api/unit-output.js.map +1 -0
- package/dist/core/adapter.d.ts +136 -6
- package/dist/core/adapter.d.ts.map +1 -1
- package/dist/core/adapter.js +512 -89
- package/dist/core/adapter.js.map +1 -1
- package/dist/core/bridge-path.d.ts +17 -6
- package/dist/core/bridge-path.d.ts.map +1 -1
- package/dist/core/bridge-path.js +17 -6
- package/dist/core/bridge-path.js.map +1 -1
- package/dist/core/exec.d.ts +37 -0
- package/dist/core/exec.d.ts.map +1 -0
- package/dist/core/exec.js +97 -0
- package/dist/core/exec.js.map +1 -0
- package/dist/core/repoPaths.d.ts +37 -0
- package/dist/core/repoPaths.d.ts.map +1 -0
- package/dist/core/repoPaths.js +48 -0
- package/dist/core/repoPaths.js.map +1 -0
- package/dist/core/types.d.ts +90 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/studio/assets/index-DaaUU8Ep.css +32 -0
- package/dist/studio/assets/index-Fu5DRC00.js +423 -0
- package/dist/studio/index.html +2 -2
- package/package.json +14 -13
- package/dist/studio/assets/index-CjUiA3ex.css +0 -32
- package/dist/studio/assets/index-DlHYzFvv.js +0 -420
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single source of truth for the HTTP API's version prefix (FINDING-006).
|
|
3
|
+
*
|
|
4
|
+
* Lives in its own leaf module — not in routes.ts — on purpose. Both `routes.ts` (which mounts
|
|
5
|
+
* every path under this prefix) and `unit-output.ts` (which builds an operator-facing evidence URL
|
|
6
|
+
* that must match those paths) need this constant. Exporting it from routes.ts, as the first cut
|
|
7
|
+
* did (Copilot review on #217), created a cycle: routes.ts imports unit-output.ts, so importing
|
|
8
|
+
* API_PREFIX back from routes.ts made unit-output.ts pull in routes.ts's whole module
|
|
9
|
+
* initialization (which reads package.json) and risked an ESM temporal-dead-zone trap. A leaf
|
|
10
|
+
* constant that neither side's logic depends on breaks that cycle cleanly.
|
|
11
|
+
*/
|
|
12
|
+
export declare const API_PREFIX = "/api/v1";
|
|
13
|
+
//# sourceMappingURL=api-prefix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-prefix.d.ts","sourceRoot":"","sources":["../../src/api/api-prefix.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,YAAY,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single source of truth for the HTTP API's version prefix (FINDING-006).
|
|
3
|
+
*
|
|
4
|
+
* Lives in its own leaf module — not in routes.ts — on purpose. Both `routes.ts` (which mounts
|
|
5
|
+
* every path under this prefix) and `unit-output.ts` (which builds an operator-facing evidence URL
|
|
6
|
+
* that must match those paths) need this constant. Exporting it from routes.ts, as the first cut
|
|
7
|
+
* did (Copilot review on #217), created a cycle: routes.ts imports unit-output.ts, so importing
|
|
8
|
+
* API_PREFIX back from routes.ts made unit-output.ts pull in routes.ts's whole module
|
|
9
|
+
* initialization (which reads package.json) and risked an ESM temporal-dead-zone trap. A leaf
|
|
10
|
+
* constant that neither side's logic depends on breaks that cycle cleanly.
|
|
11
|
+
*/
|
|
12
|
+
export const API_PREFIX = '/api/v1';
|
|
13
|
+
//# sourceMappingURL=api-prefix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-prefix.js","sourceRoot":"","sources":["../../src/api/api-prefix.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC"}
|
|
@@ -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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"}
|
package/dist/api/evidence.d.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import type { AgentSession,
|
|
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
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* which
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
-
/**
|
|
20
|
-
|
|
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,36 @@ 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
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* the
|
|
74
|
-
*
|
|
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
|
|
72
|
+
export type EventLoader = (runId: string) => Promise<RecordedEvent[] | null>;
|
|
77
73
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
74
|
+
* The id core keys the transcript store by. Unit ids are already `<run>:<suffix>`;
|
|
75
|
+
* fall back to the free-text `u<ord>` form for any unit that lacks the prefix.
|
|
76
|
+
*
|
|
77
|
+
* EXPORTED so `/runs/:id/units/:unitKey/output` calls THIS rather than re-deriving the
|
|
78
|
+
* key from the caller's path segment. It used to be private and the route built its own
|
|
79
|
+
* `<run>:<suffix>` string from whatever the operator typed, which meant the two surfaces
|
|
80
|
+
* could disagree about which unit a key names — and did: a key the bundle resolved
|
|
81
|
+
* answered `null` on the endpoint (FINDING-006). One derivation, from the unit record.
|
|
82
|
+
*/
|
|
83
|
+
export declare function coreUnitId(runId: string, unit: WorkUnit): string;
|
|
84
|
+
/**
|
|
85
|
+
* Assemble a run's evidence bundle.
|
|
86
|
+
*
|
|
87
|
+
* Every read degrades in place rather than failing the export: an unreadable
|
|
88
|
+
* transcript becomes `transcript: null` + `transcriptError` on that unit, and an
|
|
89
|
+
* unreadable event history becomes `events: []` + `eventsUnavailable`. A partial
|
|
90
|
+
* bundle that says which part is missing beats no bundle at all — but it must say
|
|
91
|
+
* so, or the gap reads as a fact about the run.
|
|
82
92
|
*/
|
|
83
|
-
export declare function buildEvidenceBundle(view: SessionView, loadTranscript: TranscriptLoader): Promise<EvidenceBundle>;
|
|
93
|
+
export declare function buildEvidenceBundle(view: SessionView, loadTranscript: TranscriptLoader, loadEvents: EventLoader): Promise<EvidenceBundle>;
|
|
84
94
|
/**
|
|
85
95
|
* Parse external-transform markers from one transcript — mirrors wicked-core's
|
|
86
96
|
* `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,
|
|
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;AAE7E;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CAEhE;AAsBD;;;;;;;;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"}
|
package/dist/api/evidence.js
CHANGED
|
@@ -1,50 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The id core keys the transcript store by. Unit ids are already `<run>:<suffix>`;
|
|
3
|
-
* fall back to the free-text `u<ord>` form for any unit that lacks the prefix
|
|
4
|
-
*
|
|
3
|
+
* fall back to the free-text `u<ord>` form for any unit that lacks the prefix.
|
|
4
|
+
*
|
|
5
|
+
* EXPORTED so `/runs/:id/units/:unitKey/output` calls THIS rather than re-deriving the
|
|
6
|
+
* key from the caller's path segment. It used to be private and the route built its own
|
|
7
|
+
* `<run>:<suffix>` string from whatever the operator typed, which meant the two surfaces
|
|
8
|
+
* could disagree about which unit a key names — and did: a key the bundle resolved
|
|
9
|
+
* answered `null` on the endpoint (FINDING-006). One derivation, from the unit record.
|
|
5
10
|
*/
|
|
6
|
-
function coreUnitId(runId, unit) {
|
|
11
|
+
export function coreUnitId(runId, unit) {
|
|
7
12
|
return unit.id.startsWith(`${runId}:`) ? unit.id : `${runId}:u${unit.ord}`;
|
|
8
13
|
}
|
|
9
14
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* yet, and an evidence bundle must not imply one).
|
|
15
|
+
* Why a run's `events` came back empty. Both cases are reported, never conflated:
|
|
16
|
+
* a read that failed is an operator problem, a run with no recorded history is a
|
|
17
|
+
* statement about that run.
|
|
14
18
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
}
|
|
19
|
+
const NO_EVENTS_RECORDED = 'core recorded no events for this run — it ran before the durable event log existed ' +
|
|
20
|
+
'(FINDING-014), or its log was removed. This is not a run that made no decisions.';
|
|
41
21
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
22
|
+
* The third cause, and a different kind of problem from the other two: the engine BUILD in use
|
|
23
|
+
* exposes no `runEvents` binding, so no run on this daemon has a readable trail. Reporting that as
|
|
24
|
+
* "this run recorded nothing" would blame the run for a missing capability — the FINDING-050 shape,
|
|
25
|
+
* distinct causes wearing one message.
|
|
46
26
|
*/
|
|
47
|
-
|
|
27
|
+
const NO_EVENT_LOG_BINDING = 'the running wicked-core build exposes no durable event log (runEvents), so no decision trail ' +
|
|
28
|
+
'can be read for any run on this daemon — rebuild/reinstall wicked-core-ts. This says nothing ' +
|
|
29
|
+
'about what this run decided.';
|
|
30
|
+
/**
|
|
31
|
+
* Assemble a run's evidence bundle.
|
|
32
|
+
*
|
|
33
|
+
* Every read degrades in place rather than failing the export: an unreadable
|
|
34
|
+
* transcript becomes `transcript: null` + `transcriptError` on that unit, and an
|
|
35
|
+
* unreadable event history becomes `events: []` + `eventsUnavailable`. A partial
|
|
36
|
+
* bundle that says which part is missing beats no bundle at all — but it must say
|
|
37
|
+
* so, or the gap reads as a fact about the run.
|
|
38
|
+
*/
|
|
39
|
+
export async function buildEvidenceBundle(view, loadTranscript, loadEvents) {
|
|
48
40
|
const ordered = [...view.units].sort((a, b) => a.ord - b.ord);
|
|
49
41
|
const units = await Promise.all(ordered.map(async (unit) => {
|
|
50
42
|
try {
|
|
@@ -58,11 +50,28 @@ export async function buildEvidenceBundle(view, loadTranscript) {
|
|
|
58
50
|
};
|
|
59
51
|
}
|
|
60
52
|
}));
|
|
53
|
+
let events = [];
|
|
54
|
+
let eventsUnavailable;
|
|
55
|
+
try {
|
|
56
|
+
const loaded = await loadEvents(view.session.id);
|
|
57
|
+
if (loaded === null) {
|
|
58
|
+
eventsUnavailable = NO_EVENT_LOG_BINDING;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
events = loaded;
|
|
62
|
+
if (events.length === 0)
|
|
63
|
+
eventsUnavailable = NO_EVENTS_RECORDED;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
eventsUnavailable = `could not read the run's event history: ${err instanceof Error ? err.message : String(err)}`;
|
|
68
|
+
}
|
|
61
69
|
return {
|
|
62
70
|
exportedAt: new Date().toISOString(),
|
|
63
71
|
session: view.session,
|
|
64
72
|
units,
|
|
65
|
-
events
|
|
73
|
+
events,
|
|
74
|
+
...(eventsUnavailable === undefined ? {} : { eventsUnavailable }),
|
|
66
75
|
assumptions: units.flatMap((u) => parseAssumptions(u.ord, u.transcript)),
|
|
67
76
|
};
|
|
68
77
|
}
|
package/dist/api/evidence.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../src/api/evidence.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../src/api/evidence.ts"],"names":[],"mappings":"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"}
|
package/dist/api/gate-cache.d.ts
CHANGED
|
@@ -11,25 +11,41 @@ export interface GateCacheEntry {
|
|
|
11
11
|
receivedAt: string;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Gate-prompt cache over a durable log (DES-STUDIO-001 §3.3).
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
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
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
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;
|
|
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"}
|