wicked-crew 0.7.1 → 0.7.3
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/delivery-index.d.ts +61 -0
- package/dist/api/delivery-index.d.ts.map +1 -0
- package/dist/api/delivery-index.js +87 -0
- package/dist/api/delivery-index.js.map +1 -0
- package/dist/api/governance-steering.d.ts +139 -0
- package/dist/api/governance-steering.d.ts.map +1 -0
- package/dist/api/governance-steering.js +302 -0
- package/dist/api/governance-steering.js.map +1 -0
- package/dist/api/governance-wiki.d.ts +28 -0
- package/dist/api/governance-wiki.d.ts.map +1 -0
- package/dist/api/governance-wiki.js +97 -0
- package/dist/api/governance-wiki.js.map +1 -0
- package/dist/api/routes.d.ts +21 -16
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +196 -10
- package/dist/api/routes.js.map +1 -1
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +56 -1
- package/dist/api/server.js.map +1 -1
- package/dist/api/testing.d.ts +117 -0
- package/dist/api/testing.d.ts.map +1 -0
- package/dist/api/testing.js +152 -0
- package/dist/api/testing.js.map +1 -0
- package/dist/campaign/supervision.d.ts +296 -0
- package/dist/campaign/supervision.d.ts.map +1 -0
- package/dist/campaign/supervision.js +515 -0
- package/dist/campaign/supervision.js.map +1 -0
- package/dist/campaigns/plan.d.ts +64 -0
- package/dist/campaigns/plan.d.ts.map +1 -0
- package/dist/campaigns/plan.js +201 -0
- package/dist/campaigns/plan.js.map +1 -0
- package/dist/campaigns/routes.d.ts +28 -0
- package/dist/campaigns/routes.d.ts.map +1 -0
- package/dist/campaigns/routes.js +177 -0
- package/dist/campaigns/routes.js.map +1 -0
- package/dist/core/adapter.d.ts +160 -1
- package/dist/core/adapter.d.ts.map +1 -1
- package/dist/core/adapter.js +300 -1
- package/dist/core/adapter.js.map +1 -1
- package/dist/projects/routes.d.ts +4 -4
- package/dist/qe/acceptance.d.ts +20 -1
- package/dist/qe/acceptance.d.ts.map +1 -1
- package/dist/qe/acceptance.js +32 -0
- package/dist/qe/acceptance.js.map +1 -1
- package/dist/qe/conformance.d.ts +158 -0
- package/dist/qe/conformance.d.ts.map +1 -0
- package/dist/qe/conformance.js +244 -0
- package/dist/qe/conformance.js.map +1 -0
- package/dist/studio/assets/index-BAW9rqNT.css +32 -0
- package/dist/studio/assets/index-UahdRwF7.js +532 -0
- package/dist/studio/index.html +2 -2
- package/dist/studio/testid-inventory.json +4387 -0
- package/endpoint-manifest.json +154 -9
- package/package.json +3 -3
- package/dist/studio/assets/index-D-BFUYnY.js +0 -530
- package/dist/studio/assets/index-D6S9zUtO.css +0 -32
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
// Campaign budget/runtime governance (TH-20 / test-R22): the crew-side supervision layer
|
|
2
|
+
// that makes campaign spend BOUNDED and every abort VISIBLE.
|
|
3
|
+
//
|
|
4
|
+
// Why this exists: core's `CampaignDef` carries exactly one resource knob — `max_concurrency`
|
|
5
|
+
// (DES-CAMPAIGN-001 §2) — and the engine's per-unit wall-clock default is 2 HOURS
|
|
6
|
+
// (wicked-core `execute_wrapped.rs` `unit_timeout()`, override `WICKED_UNIT_TIMEOUT_SECS`).
|
|
7
|
+
// A nightly 20+-node governed campaign with no ceiling is an unbounded spend commitment.
|
|
8
|
+
//
|
|
9
|
+
// KNOB PLACEMENT — the P-9 interim decision, recorded here on purpose:
|
|
10
|
+
// Budget/timeout/kill knobs live CREW-SIDE (this module), NOT as `CampaignDef` fields in
|
|
11
|
+
// core. Crew supervision ships without a core release and rides the cancel path that
|
|
12
|
+
// already exists (`CancelRun` via the adapter). The accepted cost: supervision state does
|
|
13
|
+
// not survive an ENGINE-ONLY resume — resume a campaign without the crew daemon and
|
|
14
|
+
// nothing enforces the budget. Moving the knobs into `CampaignDef` (where they would
|
|
15
|
+
// survive resume) stays open as P-9's core-maintainer call; this module is written so the
|
|
16
|
+
// policy type can migrate onto the def without changing its semantics.
|
|
17
|
+
// Doctrine + operator docs: `docs/campaign-budgets.md`.
|
|
18
|
+
//
|
|
19
|
+
// What the supervisor enforces, sweep-driven like `api/stall-watchdog.ts` (same
|
|
20
|
+
// deps-injected/fake-clock idiom) but ENFORCING where the watchdog is detection-only:
|
|
21
|
+
// 1. Per-node timeout — a node RUNNING longer than `nodeTimeoutMs` is cancelled with an
|
|
22
|
+
// explicit `node-timeout` exclusion. `awaiting-human` never counts against it (a gate
|
|
23
|
+
// is quiet by design and burns no worker spend).
|
|
24
|
+
// 2. Campaign wall-clock budget — when `elapsed > wallClockBudgetMs`, remaining nodes are
|
|
25
|
+
// aborted with EXCLUDED-WITH-REASON status: never-launched nodes are excluded at the
|
|
26
|
+
// scheduler so they can never dispatch; in-flight nodes follow the kill policy
|
|
27
|
+
// (`kill-running` cancels them, `abandon-running` lets them finish while launching
|
|
28
|
+
// nothing new). Exclusion is NEVER silent: it lands in supervision state, in the
|
|
29
|
+
// evidence report, on the /ws fan-out as synthetic frames, and in the warn log.
|
|
30
|
+
// 3. Campaign cost budget (opt-in) — same exhaustion path keyed on recorded USD cost.
|
|
31
|
+
// 4. Nightly cap — `assertWithinNightlyCap` fail-closed preflight on node count, so the
|
|
32
|
+
// TH-23 nightly recipe cannot launch an uncapped campaign by omission.
|
|
33
|
+
//
|
|
34
|
+
// Cost per node is recorded in evidence: the supervisor stamps wall-clock per node itself
|
|
35
|
+
// (always available) and accepts token/USD figures via `recordNodeCost` when the worker CLI
|
|
36
|
+
// reports them; `buildSupervisionReport`/`writeSupervisionReport` emit the
|
|
37
|
+
// `campaign-supervision.json` evidence artifact whose per-node rows the TH-14 scoreboard
|
|
38
|
+
// cost column diffs — cost regressions become visible exactly like verdict regressions.
|
|
39
|
+
// (Deliberately an ARTIFACT, not new ledger-manifest fields: manifest 2.1 is wicked-ledger's
|
|
40
|
+
// contract and moves through its owner's release train, never ad hoc — ADR 0006.)
|
|
41
|
+
import { writeFile } from 'node:fs/promises';
|
|
42
|
+
import { join } from 'node:path';
|
|
43
|
+
/** Campaign-proven pin (the 2026-08 studio E2E campaign ran `WICKED_UNIT_TIMEOUT_SECS=900`). */
|
|
44
|
+
export const DEFAULT_UNIT_TIMEOUT_SECS = 900;
|
|
45
|
+
/** Default per-node timeout: 30 min ≈ 4× the observed 7m41s governed feature run. */
|
|
46
|
+
export const DEFAULT_NODE_TIMEOUT_SECS = 1_800;
|
|
47
|
+
/** Default campaign wall-clock budget: 4 h (a 21-node ladder ≈ 2.7 h serial at observed rate). */
|
|
48
|
+
export const DEFAULT_WALL_CLOCK_BUDGET_SECS = 14_400;
|
|
49
|
+
/** Default nightly node cap (the proven ladder was 21 scenarios; 25 leaves headroom). */
|
|
50
|
+
export const DEFAULT_MAX_NODES = 25;
|
|
51
|
+
export const DEFAULT_KILL_POLICY = 'kill-running';
|
|
52
|
+
/** The env knobs `resolveBudgetPolicy` reads. All crew-side; documented in docs/campaign-budgets.md. */
|
|
53
|
+
export const ENV_BUDGET_SECS = 'WICKED_CAMPAIGN_BUDGET_SECS';
|
|
54
|
+
export const ENV_NODE_TIMEOUT_SECS = 'WICKED_CAMPAIGN_NODE_TIMEOUT_SECS';
|
|
55
|
+
export const ENV_UNIT_TIMEOUT_SECS = 'WICKED_UNIT_TIMEOUT_SECS';
|
|
56
|
+
export const ENV_MAX_NODES = 'WICKED_CAMPAIGN_MAX_NODES';
|
|
57
|
+
export const ENV_MAX_COST_USD = 'WICKED_CAMPAIGN_MAX_COST_USD';
|
|
58
|
+
export const ENV_KILL_POLICY = 'WICKED_CAMPAIGN_KILL_POLICY';
|
|
59
|
+
/** Strict positive-number parse: rejects '', '4h', '-1', '0', NaN — never a silent fallback. */
|
|
60
|
+
function parsePositive(name, raw) {
|
|
61
|
+
const trimmed = raw.trim();
|
|
62
|
+
const n = /^\d+(\.\d+)?$/.test(trimmed) ? Number(trimmed) : Number.NaN;
|
|
63
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
64
|
+
throw new Error(`campaign budget policy: ${name}=${JSON.stringify(raw)} is not a positive number — ` +
|
|
65
|
+
`refusing to guess (a malformed ceiling must never mean "unbounded")`);
|
|
66
|
+
}
|
|
67
|
+
return n;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Resolve the budget policy from an environment (defaults ↦ documented ceilings; malformed
|
|
71
|
+
* values throw — fail closed, TH-8 posture). Pass `process.env` in the daemon; tests pass
|
|
72
|
+
* a literal.
|
|
73
|
+
*/
|
|
74
|
+
export function resolveBudgetPolicy(env = process.env) {
|
|
75
|
+
const budgetSecs = env[ENV_BUDGET_SECS] !== undefined && env[ENV_BUDGET_SECS] !== ''
|
|
76
|
+
? parsePositive(ENV_BUDGET_SECS, env[ENV_BUDGET_SECS])
|
|
77
|
+
: DEFAULT_WALL_CLOCK_BUDGET_SECS;
|
|
78
|
+
const nodeTimeoutSecs = env[ENV_NODE_TIMEOUT_SECS] !== undefined && env[ENV_NODE_TIMEOUT_SECS] !== ''
|
|
79
|
+
? parsePositive(ENV_NODE_TIMEOUT_SECS, env[ENV_NODE_TIMEOUT_SECS])
|
|
80
|
+
: DEFAULT_NODE_TIMEOUT_SECS;
|
|
81
|
+
// The ambient WICKED_UNIT_TIMEOUT_SECS is HONORED as the pin (integer seconds — the
|
|
82
|
+
// engine's own u64 parse would reject a fraction, so we must too).
|
|
83
|
+
const rawUnit = env[ENV_UNIT_TIMEOUT_SECS];
|
|
84
|
+
let unitTimeoutSecs = DEFAULT_UNIT_TIMEOUT_SECS;
|
|
85
|
+
if (rawUnit !== undefined && rawUnit !== '') {
|
|
86
|
+
unitTimeoutSecs = parsePositive(ENV_UNIT_TIMEOUT_SECS, rawUnit);
|
|
87
|
+
if (!Number.isInteger(unitTimeoutSecs)) {
|
|
88
|
+
throw new Error(`campaign budget policy: ${ENV_UNIT_TIMEOUT_SECS}=${JSON.stringify(rawUnit)} must be ` +
|
|
89
|
+
`an integer number of seconds (the engine parses it as u64 and would silently ` +
|
|
90
|
+
`ignore this value, falling back to its 2-hour default)`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const maxNodes = env[ENV_MAX_NODES] !== undefined && env[ENV_MAX_NODES] !== ''
|
|
94
|
+
? parsePositive(ENV_MAX_NODES, env[ENV_MAX_NODES])
|
|
95
|
+
: DEFAULT_MAX_NODES;
|
|
96
|
+
if (!Number.isInteger(maxNodes)) {
|
|
97
|
+
throw new Error(`campaign budget policy: ${ENV_MAX_NODES} must be an integer node count, got ` +
|
|
98
|
+
JSON.stringify(env[ENV_MAX_NODES]));
|
|
99
|
+
}
|
|
100
|
+
const rawKill = env[ENV_KILL_POLICY];
|
|
101
|
+
let killPolicy = DEFAULT_KILL_POLICY;
|
|
102
|
+
if (rawKill !== undefined && rawKill !== '') {
|
|
103
|
+
if (rawKill !== 'kill-running' && rawKill !== 'abandon-running') {
|
|
104
|
+
throw new Error(`campaign budget policy: ${ENV_KILL_POLICY}=${JSON.stringify(rawKill)} — expected ` +
|
|
105
|
+
`"kill-running" or "abandon-running"`);
|
|
106
|
+
}
|
|
107
|
+
killPolicy = rawKill;
|
|
108
|
+
}
|
|
109
|
+
const policy = {
|
|
110
|
+
wallClockBudgetMs: budgetSecs * 1_000,
|
|
111
|
+
nodeTimeoutMs: nodeTimeoutSecs * 1_000,
|
|
112
|
+
unitTimeoutSecs,
|
|
113
|
+
maxNodes,
|
|
114
|
+
killPolicy,
|
|
115
|
+
};
|
|
116
|
+
const rawCost = env[ENV_MAX_COST_USD];
|
|
117
|
+
if (rawCost !== undefined && rawCost !== '') {
|
|
118
|
+
policy.maxCostUsd = parsePositive(ENV_MAX_COST_USD, rawCost);
|
|
119
|
+
}
|
|
120
|
+
return policy;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The TH-8-style environment pins every campaign node MUST launch with. Until the full
|
|
124
|
+
* TH-8 environment manifest lands, this is the manifest's budget block: the interim
|
|
125
|
+
* mitigation test-R22 names ("the environment manifest must pin WICKED_UNIT_TIMEOUT_SECS").
|
|
126
|
+
*/
|
|
127
|
+
export function nodeEnvPins(policy) {
|
|
128
|
+
return { [ENV_UNIT_TIMEOUT_SECS]: String(policy.unitTimeoutSecs) };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Fail-closed preflight: a node environment must carry the exact `WICKED_UNIT_TIMEOUT_SECS`
|
|
132
|
+
* pin the policy resolved. Missing ⇒ the engine's 2-hour default applies silently;
|
|
133
|
+
* mismatched ⇒ the manifest lies about the environment. Both refuse.
|
|
134
|
+
*/
|
|
135
|
+
export function assertNodeEnvPinned(nodeEnv, policy) {
|
|
136
|
+
const pin = nodeEnv[ENV_UNIT_TIMEOUT_SECS];
|
|
137
|
+
if (pin === undefined || pin.trim() === '') {
|
|
138
|
+
throw new Error(`campaign preflight: node environment is missing the ${ENV_UNIT_TIMEOUT_SECS} pin ` +
|
|
139
|
+
`(expected "${policy.unitTimeoutSecs}"). Unpinned, the engine defaults to 7200 s ` +
|
|
140
|
+
`per unit — refusing to launch an effectively unbounded node`);
|
|
141
|
+
}
|
|
142
|
+
if (pin.trim() !== String(policy.unitTimeoutSecs)) {
|
|
143
|
+
throw new Error(`campaign preflight: ${ENV_UNIT_TIMEOUT_SECS} pin mismatch — node environment says ` +
|
|
144
|
+
`${JSON.stringify(pin)}, the budget policy pinned "${policy.unitTimeoutSecs}". ` +
|
|
145
|
+
`Fail-closed: fix the environment or the policy, never both silently`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Nightly cap, fail-closed (test-R22 interim: "the nightly recipe must cap campaign size
|
|
150
|
+
* explicitly"). Called with the campaign's node count BEFORE launch; over-cap refuses with
|
|
151
|
+
* both numbers so the operator sees exactly what to trim or raise.
|
|
152
|
+
*/
|
|
153
|
+
export function assertWithinNightlyCap(nodeCount, policy) {
|
|
154
|
+
if (!Number.isInteger(nodeCount) || nodeCount < 0) {
|
|
155
|
+
throw new Error(`campaign preflight: node count must be a non-negative integer, got ${nodeCount}`);
|
|
156
|
+
}
|
|
157
|
+
if (nodeCount > policy.maxNodes) {
|
|
158
|
+
throw new Error(`campaign preflight: ${nodeCount} nodes exceeds the nightly cap of ${policy.maxNodes} ` +
|
|
159
|
+
`(${ENV_MAX_NODES}). Refusing to launch — split the campaign or raise the cap ` +
|
|
160
|
+
`explicitly; an uncapped nightly is an unbounded spend commitment`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/** Default sweep cadence: budgets are minutes-scale; 15 s keeps overshoot ≪ any ceiling. */
|
|
164
|
+
export const DEFAULT_SUPERVISION_SWEEP_INTERVAL_MS = 15_000;
|
|
165
|
+
export class CampaignSupervisor {
|
|
166
|
+
deps;
|
|
167
|
+
campaigns = new Map();
|
|
168
|
+
now;
|
|
169
|
+
log;
|
|
170
|
+
handle = null;
|
|
171
|
+
sweeping = false;
|
|
172
|
+
constructor(deps) {
|
|
173
|
+
this.deps = deps;
|
|
174
|
+
this.now = deps.now ?? Date.now;
|
|
175
|
+
this.log = deps.log ?? (() => undefined);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Begin supervising a campaign under a policy. Call at launch — the wall clock starts
|
|
179
|
+
* HERE, not at the first sweep. Re-registering an id is a bug (two clocks, one spend).
|
|
180
|
+
*/
|
|
181
|
+
supervise(campaignId, policy) {
|
|
182
|
+
if (this.campaigns.has(campaignId)) {
|
|
183
|
+
throw new Error(`campaign ${campaignId} is already supervised — refusing a second clock`);
|
|
184
|
+
}
|
|
185
|
+
this.campaigns.set(campaignId, {
|
|
186
|
+
policy,
|
|
187
|
+
startedAt: this.now(),
|
|
188
|
+
elapsedMs: 0,
|
|
189
|
+
released: false,
|
|
190
|
+
nodes: new Map(),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Stop ENFORCING (campaign reached a terminal status). State stays readable — deliberately
|
|
195
|
+
* no delete, or a post-terminal report would be irrecoverable. Also reached automatically
|
|
196
|
+
* when a sweep observes every node terminal.
|
|
197
|
+
*/
|
|
198
|
+
release(campaignId) {
|
|
199
|
+
const c = this.campaigns.get(campaignId);
|
|
200
|
+
if (c !== undefined)
|
|
201
|
+
c.released = true;
|
|
202
|
+
}
|
|
203
|
+
/** Record pipeline-reported cost for a node (tokens/USD — wall-clock is supervisor-owned). */
|
|
204
|
+
recordNodeCost(campaignId, nodeId, cost) {
|
|
205
|
+
const c = this.campaigns.get(campaignId);
|
|
206
|
+
if (c === undefined)
|
|
207
|
+
return; // unknown campaign: cost with no supervision to attribute it to
|
|
208
|
+
const node = this.trackNode(c, nodeId);
|
|
209
|
+
if (cost.usd !== undefined)
|
|
210
|
+
node.cost.usd = (node.cost.usd ?? 0) + cost.usd;
|
|
211
|
+
if (cost.inputTokens !== undefined) {
|
|
212
|
+
node.cost.inputTokens = (node.cost.inputTokens ?? 0) + cost.inputTokens;
|
|
213
|
+
}
|
|
214
|
+
if (cost.outputTokens !== undefined) {
|
|
215
|
+
node.cost.outputTokens = (node.cost.outputTokens ?? 0) + cost.outputTokens;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/** The supervision state for one campaign, or undefined when it was never supervised. */
|
|
219
|
+
state(campaignId) {
|
|
220
|
+
const c = this.campaigns.get(campaignId);
|
|
221
|
+
if (c === undefined)
|
|
222
|
+
return undefined;
|
|
223
|
+
const totalCostUsd = this.totalCostUsd(c);
|
|
224
|
+
const state = {
|
|
225
|
+
campaignId,
|
|
226
|
+
policy: c.policy,
|
|
227
|
+
startedAt: c.startedAt,
|
|
228
|
+
elapsedMs: c.elapsedMs,
|
|
229
|
+
// Explicit copy: the internal `runningSince` bookkeeping never leaves the supervisor.
|
|
230
|
+
nodes: [...c.nodes.values()].map((n) => {
|
|
231
|
+
const row = { nodeId: n.nodeId, phase: n.phase, cost: { ...n.cost } };
|
|
232
|
+
if (n.runId !== undefined)
|
|
233
|
+
row.runId = n.runId;
|
|
234
|
+
if (n.startedAt !== undefined)
|
|
235
|
+
row.startedAt = n.startedAt;
|
|
236
|
+
if (n.exclusion !== undefined)
|
|
237
|
+
row.exclusion = { ...n.exclusion };
|
|
238
|
+
return row;
|
|
239
|
+
}),
|
|
240
|
+
};
|
|
241
|
+
if (totalCostUsd !== undefined)
|
|
242
|
+
state.totalCostUsd = totalCostUsd;
|
|
243
|
+
if (c.exhausted !== undefined)
|
|
244
|
+
state.exhausted = { ...c.exhausted };
|
|
245
|
+
return state;
|
|
246
|
+
}
|
|
247
|
+
/** One enforcement pass over every supervised campaign. Tests drive it directly. */
|
|
248
|
+
async sweep() {
|
|
249
|
+
if (this.sweeping)
|
|
250
|
+
return; // a slow scheduler read must never stack sweeps
|
|
251
|
+
this.sweeping = true;
|
|
252
|
+
try {
|
|
253
|
+
for (const [campaignId, c] of this.campaigns) {
|
|
254
|
+
try {
|
|
255
|
+
await this.sweepCampaign(campaignId, c);
|
|
256
|
+
}
|
|
257
|
+
catch (err) {
|
|
258
|
+
this.log(`[campaign-supervision] sweep of ${campaignId} failed: ${String(err)}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
finally {
|
|
263
|
+
this.sweeping = false;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/** Arm the periodic sweep. Unref'd — the daemon's server lifecycle owns shutdown. */
|
|
267
|
+
start(intervalMs = DEFAULT_SUPERVISION_SWEEP_INTERVAL_MS) {
|
|
268
|
+
if (this.handle !== null)
|
|
269
|
+
return;
|
|
270
|
+
this.handle = setInterval(() => {
|
|
271
|
+
this.sweep().catch((err) => {
|
|
272
|
+
this.log(`[campaign-supervision] sweep failed: ${String(err)}`);
|
|
273
|
+
});
|
|
274
|
+
}, intervalMs);
|
|
275
|
+
this.handle.unref?.();
|
|
276
|
+
}
|
|
277
|
+
stop() {
|
|
278
|
+
if (this.handle !== null)
|
|
279
|
+
clearInterval(this.handle);
|
|
280
|
+
this.handle = null;
|
|
281
|
+
}
|
|
282
|
+
// -------------------------------------------------------------------------
|
|
283
|
+
async sweepCampaign(campaignId, c) {
|
|
284
|
+
if (c.released)
|
|
285
|
+
return; // over: nothing left to enforce, state stays as it ended
|
|
286
|
+
const nodes = await this.deps.scheduler.listNodes(campaignId);
|
|
287
|
+
const now = this.now();
|
|
288
|
+
c.elapsedMs = now - c.startedAt;
|
|
289
|
+
// Reconcile observed phases + accumulate per-node RUNNING wall clocks. `cost.wallMs`
|
|
290
|
+
// counts running intervals only — an awaiting-human interlude neither bills the node
|
|
291
|
+
// nor counts against its timeout (a gate is quiet by design; sweep granularity bounds
|
|
292
|
+
// the error at one interval).
|
|
293
|
+
for (const view of nodes) {
|
|
294
|
+
const node = this.trackNode(c, view.nodeId);
|
|
295
|
+
if (view.runId !== undefined)
|
|
296
|
+
node.runId = view.runId;
|
|
297
|
+
if (view.phase === 'running') {
|
|
298
|
+
if (node.runningSince === undefined) {
|
|
299
|
+
node.runningSince = now; // (re-)entered running
|
|
300
|
+
node.startedAt ??= now;
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
node.cost.wallMs += now - node.runningSince; // extend the open interval
|
|
304
|
+
node.runningSince = now;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
else if (node.runningSince !== undefined) {
|
|
308
|
+
// Left `running` since the last sweep — close the interval at this observation.
|
|
309
|
+
node.cost.wallMs += now - node.runningSince;
|
|
310
|
+
node.runningSince = undefined;
|
|
311
|
+
}
|
|
312
|
+
node.phase = view.phase;
|
|
313
|
+
}
|
|
314
|
+
// A campaign whose every node is terminal is OVER — a later sweep must never trip its
|
|
315
|
+
// budget on wall time nobody is spending.
|
|
316
|
+
if (nodes.length > 0 && nodes.every((n) => n.phase === 'terminal')) {
|
|
317
|
+
c.released = true;
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (c.exhausted !== undefined)
|
|
321
|
+
return; // exhaustion is enforced exactly once
|
|
322
|
+
// 1) Per-node timeout (accumulated RUNNING time only; awaiting-human never counts).
|
|
323
|
+
for (const node of c.nodes.values()) {
|
|
324
|
+
if (node.phase !== 'running' || node.exclusion !== undefined)
|
|
325
|
+
continue;
|
|
326
|
+
if (node.cost.wallMs <= c.policy.nodeTimeoutMs)
|
|
327
|
+
continue;
|
|
328
|
+
const reason = `node-timeout: ran ${Math.round(node.cost.wallMs / 1000)}s, per-node timeout is ` +
|
|
329
|
+
`${Math.round(c.policy.nodeTimeoutMs / 1000)}s (${ENV_NODE_TIMEOUT_SECS})`;
|
|
330
|
+
await this.excludeOne(campaignId, c, node, 'node-timeout', reason, 'cancelled');
|
|
331
|
+
}
|
|
332
|
+
// 2) Campaign-level ceilings — wall-clock first (always armed), then cost (opt-in).
|
|
333
|
+
if (c.elapsedMs > c.policy.wallClockBudgetMs) {
|
|
334
|
+
await this.exhaust(campaignId, c, {
|
|
335
|
+
kind: 'wall-clock',
|
|
336
|
+
at: new Date(now).toISOString(),
|
|
337
|
+
spent: c.elapsedMs,
|
|
338
|
+
budget: c.policy.wallClockBudgetMs,
|
|
339
|
+
policyApplied: c.policy.killPolicy,
|
|
340
|
+
});
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const totalCostUsd = this.totalCostUsd(c);
|
|
344
|
+
if (c.policy.maxCostUsd !== undefined &&
|
|
345
|
+
totalCostUsd !== undefined &&
|
|
346
|
+
totalCostUsd > c.policy.maxCostUsd) {
|
|
347
|
+
await this.exhaust(campaignId, c, {
|
|
348
|
+
kind: 'cost',
|
|
349
|
+
at: new Date(now).toISOString(),
|
|
350
|
+
spent: totalCostUsd,
|
|
351
|
+
budget: c.policy.maxCostUsd,
|
|
352
|
+
policyApplied: c.policy.killPolicy,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Abort everything the budget no longer covers. Every touched node gets an
|
|
358
|
+
* excluded-with-reason record; one campaignBudgetExceeded frame + one
|
|
359
|
+
* campaignNodeExcluded frame per node ride the /ws fan-out; the warn log names them all.
|
|
360
|
+
*/
|
|
361
|
+
async exhaust(campaignId, c, exhaustion) {
|
|
362
|
+
c.exhausted = exhaustion;
|
|
363
|
+
const kind = exhaustion.kind === 'wall-clock' ? 'campaign-budget' : 'cost-budget';
|
|
364
|
+
const ceiling = exhaustion.kind === 'wall-clock'
|
|
365
|
+
? `wall-clock budget ${Math.round(exhaustion.budget / 1000)}s exceeded ` +
|
|
366
|
+
`(elapsed ${Math.round(exhaustion.spent / 1000)}s)`
|
|
367
|
+
: `cost budget $${exhaustion.budget} exceeded (spent $${exhaustion.spent.toFixed(2)})`;
|
|
368
|
+
const touched = [];
|
|
369
|
+
for (const node of c.nodes.values()) {
|
|
370
|
+
if (node.phase === 'terminal' || node.exclusion !== undefined)
|
|
371
|
+
continue;
|
|
372
|
+
if (node.phase === 'pending') {
|
|
373
|
+
await this.excludeOne(campaignId, c, node, kind, `${ceiling}; node never launched`, 'excluded');
|
|
374
|
+
touched.push(node.nodeId);
|
|
375
|
+
}
|
|
376
|
+
else if (c.policy.killPolicy === 'kill-running') {
|
|
377
|
+
await this.excludeOne(campaignId, c, node, kind, `${ceiling}; in-flight node killed`, 'cancelled');
|
|
378
|
+
touched.push(node.nodeId);
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
// abandon-running: the node may finish, but it is on record as budget-breached and
|
|
382
|
+
// nothing new launches after it. Recorded + broadcast — never silent.
|
|
383
|
+
await this.excludeOne(campaignId, c, node, kind, `${ceiling}; in-flight node abandoned to finish (abandon-running)`, 'abandoned');
|
|
384
|
+
touched.push(node.nodeId);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
this.deps.broadcast({
|
|
388
|
+
type: 'campaignBudgetExceeded',
|
|
389
|
+
campaign: campaignId,
|
|
390
|
+
kind: exhaustion.kind,
|
|
391
|
+
budget: exhaustion.budget,
|
|
392
|
+
spent: exhaustion.spent,
|
|
393
|
+
policyApplied: exhaustion.policyApplied,
|
|
394
|
+
nodes: touched,
|
|
395
|
+
});
|
|
396
|
+
this.log(`[campaign-supervision] campaign ${campaignId}: ${ceiling} — policy ` +
|
|
397
|
+
`${exhaustion.policyApplied} applied to ${touched.length} node(s): ` +
|
|
398
|
+
`${touched.join(', ') || '(none left to touch)'}`);
|
|
399
|
+
}
|
|
400
|
+
async excludeOne(campaignId, c, node, kind, reason, applied) {
|
|
401
|
+
node.exclusion = { kind, reason, at: new Date(this.now()).toISOString(), applied };
|
|
402
|
+
if (applied === 'cancelled') {
|
|
403
|
+
await this.deps.scheduler.cancelNode(campaignId, node.nodeId, node.runId, reason);
|
|
404
|
+
}
|
|
405
|
+
else if (applied === 'excluded') {
|
|
406
|
+
await this.deps.scheduler.excludeNode(campaignId, node.nodeId, reason);
|
|
407
|
+
}
|
|
408
|
+
this.deps.broadcast({
|
|
409
|
+
type: 'campaignNodeExcluded',
|
|
410
|
+
campaign: campaignId,
|
|
411
|
+
node: node.nodeId,
|
|
412
|
+
kind,
|
|
413
|
+
reason,
|
|
414
|
+
applied,
|
|
415
|
+
});
|
|
416
|
+
this.log(`[campaign-supervision] campaign ${campaignId} node ${node.nodeId}: ${applied} — ${reason}`);
|
|
417
|
+
}
|
|
418
|
+
trackNode(c, nodeId) {
|
|
419
|
+
let node = c.nodes.get(nodeId);
|
|
420
|
+
if (node === undefined) {
|
|
421
|
+
node = { nodeId, phase: 'pending', cost: { wallMs: 0 } };
|
|
422
|
+
c.nodes.set(nodeId, node);
|
|
423
|
+
}
|
|
424
|
+
return node;
|
|
425
|
+
}
|
|
426
|
+
totalCostUsd(c) {
|
|
427
|
+
let total;
|
|
428
|
+
for (const node of c.nodes.values()) {
|
|
429
|
+
if (node.cost.usd !== undefined)
|
|
430
|
+
total = (total ?? 0) + node.cost.usd;
|
|
431
|
+
}
|
|
432
|
+
return total;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
// ---------------------------------------------------------------------------
|
|
436
|
+
// Evidence: the campaign-supervision report artifact
|
|
437
|
+
// ---------------------------------------------------------------------------
|
|
438
|
+
/** Filename of the supervision evidence artifact inside a campaign's evidence dir. */
|
|
439
|
+
export const SUPERVISION_REPORT_FILENAME = 'campaign-supervision.json';
|
|
440
|
+
/** Build the evidence report from live supervision state. */
|
|
441
|
+
export function buildSupervisionReport(state) {
|
|
442
|
+
let wallMs = 0;
|
|
443
|
+
let inputTokens;
|
|
444
|
+
let outputTokens;
|
|
445
|
+
for (const n of state.nodes) {
|
|
446
|
+
wallMs += n.cost.wallMs;
|
|
447
|
+
if (n.cost.inputTokens !== undefined)
|
|
448
|
+
inputTokens = (inputTokens ?? 0) + n.cost.inputTokens;
|
|
449
|
+
if (n.cost.outputTokens !== undefined)
|
|
450
|
+
outputTokens = (outputTokens ?? 0) + n.cost.outputTokens;
|
|
451
|
+
}
|
|
452
|
+
const report = {
|
|
453
|
+
report: 'campaign-supervision',
|
|
454
|
+
version: 1,
|
|
455
|
+
campaign_id: state.campaignId,
|
|
456
|
+
generated_at: new Date().toISOString(),
|
|
457
|
+
policy: {
|
|
458
|
+
wall_clock_budget_secs: Math.round(state.policy.wallClockBudgetMs / 1000),
|
|
459
|
+
node_timeout_secs: Math.round(state.policy.nodeTimeoutMs / 1000),
|
|
460
|
+
unit_timeout_pin_secs: state.policy.unitTimeoutSecs,
|
|
461
|
+
max_nodes: state.policy.maxNodes,
|
|
462
|
+
...(state.policy.maxCostUsd !== undefined ? { max_cost_usd: state.policy.maxCostUsd } : {}),
|
|
463
|
+
kill_policy: state.policy.killPolicy,
|
|
464
|
+
},
|
|
465
|
+
started_at: new Date(state.startedAt).toISOString(),
|
|
466
|
+
elapsed_ms: state.elapsedMs,
|
|
467
|
+
budget: state.exhausted !== undefined
|
|
468
|
+
? {
|
|
469
|
+
exhausted: true,
|
|
470
|
+
kind: state.exhausted.kind,
|
|
471
|
+
at: state.exhausted.at,
|
|
472
|
+
policy_applied: state.exhausted.policyApplied,
|
|
473
|
+
}
|
|
474
|
+
: { exhausted: false },
|
|
475
|
+
totals: {
|
|
476
|
+
wall_ms: wallMs,
|
|
477
|
+
...(state.totalCostUsd !== undefined ? { cost_usd: state.totalCostUsd } : {}),
|
|
478
|
+
...(inputTokens !== undefined ? { input_tokens: inputTokens } : {}),
|
|
479
|
+
...(outputTokens !== undefined ? { output_tokens: outputTokens } : {}),
|
|
480
|
+
},
|
|
481
|
+
nodes: state.nodes.map((n) => ({
|
|
482
|
+
node_id: n.nodeId,
|
|
483
|
+
phase: n.phase,
|
|
484
|
+
...(n.runId !== undefined ? { run_id: n.runId } : {}),
|
|
485
|
+
cost: {
|
|
486
|
+
wall_ms: n.cost.wallMs,
|
|
487
|
+
...(n.cost.usd !== undefined ? { usd: n.cost.usd } : {}),
|
|
488
|
+
...(n.cost.inputTokens !== undefined ? { input_tokens: n.cost.inputTokens } : {}),
|
|
489
|
+
...(n.cost.outputTokens !== undefined ? { output_tokens: n.cost.outputTokens } : {}),
|
|
490
|
+
},
|
|
491
|
+
...(n.exclusion !== undefined
|
|
492
|
+
? {
|
|
493
|
+
excluded: {
|
|
494
|
+
kind: n.exclusion.kind,
|
|
495
|
+
reason: n.exclusion.reason,
|
|
496
|
+
at: n.exclusion.at,
|
|
497
|
+
applied: n.exclusion.applied,
|
|
498
|
+
},
|
|
499
|
+
}
|
|
500
|
+
: {}),
|
|
501
|
+
})),
|
|
502
|
+
};
|
|
503
|
+
return report;
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Write the report into a campaign's evidence directory (caller resolves the dir — the
|
|
507
|
+
* QE pipeline owns evidence placement; crew never writes ledger DOMAIN records, and this
|
|
508
|
+
* is an artifact file, not a runs/verdicts row). Returns the absolute path written.
|
|
509
|
+
*/
|
|
510
|
+
export async function writeSupervisionReport(evidenceDir, report) {
|
|
511
|
+
const path = join(evidenceDir, SUPERVISION_REPORT_FILENAME);
|
|
512
|
+
await writeFile(path, `${JSON.stringify(report, null, 2)}\n`, 'utf8');
|
|
513
|
+
return path;
|
|
514
|
+
}
|
|
515
|
+
//# sourceMappingURL=supervision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervision.js","sourceRoot":"","sources":["../../src/campaign/supervision.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,6DAA6D;AAC7D,EAAE;AACF,8FAA8F;AAC9F,kFAAkF;AAClF,4FAA4F;AAC5F,yFAAyF;AACzF,EAAE;AACF,uEAAuE;AACvE,2FAA2F;AAC3F,uFAAuF;AACvF,4FAA4F;AAC5F,sFAAsF;AACtF,uFAAuF;AACvF,4FAA4F;AAC5F,yEAAyE;AACzE,0DAA0D;AAC1D,EAAE;AACF,gFAAgF;AAChF,sFAAsF;AACtF,0FAA0F;AAC1F,2FAA2F;AAC3F,sDAAsD;AACtD,4FAA4F;AAC5F,0FAA0F;AAC1F,oFAAoF;AACpF,wFAAwF;AACxF,sFAAsF;AACtF,qFAAqF;AACrF,wFAAwF;AACxF,0FAA0F;AAC1F,4EAA4E;AAC5E,EAAE;AACF,0FAA0F;AAC1F,4FAA4F;AAC5F,2EAA2E;AAC3E,yFAAyF;AACzF,wFAAwF;AACxF,6FAA6F;AAC7F,kFAAkF;AAElF,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAkCjC,gGAAgG;AAChG,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAC7C,qFAAqF;AACrF,MAAM,CAAC,MAAM,yBAAyB,GAAG,KAAK,CAAC;AAC/C,kGAAkG;AAClG,MAAM,CAAC,MAAM,8BAA8B,GAAG,MAAM,CAAC;AACrD,yFAAyF;AACzF,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AACpC,MAAM,CAAC,MAAM,mBAAmB,GAAuB,cAAc,CAAC;AAEtE,wGAAwG;AACxG,MAAM,CAAC,MAAM,eAAe,GAAG,6BAA6B,CAAC;AAC7D,MAAM,CAAC,MAAM,qBAAqB,GAAG,mCAAmC,CAAC;AACzE,MAAM,CAAC,MAAM,qBAAqB,GAAG,0BAA0B,CAAC;AAChE,MAAM,CAAC,MAAM,aAAa,GAAG,2BAA2B,CAAC;AACzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,8BAA8B,CAAC;AAC/D,MAAM,CAAC,MAAM,eAAe,GAAG,6BAA6B,CAAC;AAE7D,gGAAgG;AAChG,SAAS,aAAa,CAAC,IAAY,EAAE,GAAW;IAC9C,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,MAAM,CAAC,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;IACvE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CACb,2BAA2B,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,8BAA8B;YAClF,qEAAqE,CACxE,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAA0C,OAAO,CAAC,GAAG;IAErD,MAAM,UAAU,GACd,GAAG,CAAC,eAAe,CAAC,KAAK,SAAS,IAAI,GAAG,CAAC,eAAe,CAAC,KAAK,EAAE;QAC/D,CAAC,CAAC,aAAa,CAAC,eAAe,EAAE,GAAG,CAAC,eAAe,CAAC,CAAC;QACtD,CAAC,CAAC,8BAA8B,CAAC;IACrC,MAAM,eAAe,GACnB,GAAG,CAAC,qBAAqB,CAAC,KAAK,SAAS,IAAI,GAAG,CAAC,qBAAqB,CAAC,KAAK,EAAE;QAC3E,CAAC,CAAC,aAAa,CAAC,qBAAqB,EAAE,GAAG,CAAC,qBAAqB,CAAC,CAAC;QAClE,CAAC,CAAC,yBAAyB,CAAC;IAChC,oFAAoF;IACpF,mEAAmE;IACnE,MAAM,OAAO,GAAG,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAC3C,IAAI,eAAe,GAAG,yBAAyB,CAAC;IAChD,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QAC5C,eAAe,GAAG,aAAa,CAAC,qBAAqB,EAAE,OAAO,CAAC,CAAC;QAChE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,eAAe,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CACb,2BAA2B,qBAAqB,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,WAAW;gBACpF,+EAA+E;gBAC/E,wDAAwD,CAC3D,CAAC;QACJ,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GACZ,GAAG,CAAC,aAAa,CAAC,KAAK,SAAS,IAAI,GAAG,CAAC,aAAa,CAAC,KAAK,EAAE;QAC3D,CAAC,CAAC,aAAa,CAAC,aAAa,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;QAClD,CAAC,CAAC,iBAAiB,CAAC;IACxB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,2BAA2B,aAAa,sCAAsC;YAC5E,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CACrC,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,eAAe,CAAC,CAAC;IACrC,IAAI,UAAU,GAAuB,mBAAmB,CAAC;IACzD,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QAC5C,IAAI,OAAO,KAAK,cAAc,IAAI,OAAO,KAAK,iBAAiB,EAAE,CAAC;YAChE,MAAM,IAAI,KAAK,CACb,2BAA2B,eAAe,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,cAAc;gBACjF,qCAAqC,CACxC,CAAC;QACJ,CAAC;QACD,UAAU,GAAG,OAAO,CAAC;IACvB,CAAC;IACD,MAAM,MAAM,GAAyB;QACnC,iBAAiB,EAAE,UAAU,GAAG,KAAK;QACrC,aAAa,EAAE,eAAe,GAAG,KAAK;QACtC,eAAe;QACf,QAAQ;QACR,UAAU;KACX,CAAC;IACF,MAAM,OAAO,GAAG,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACtC,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QAC5C,MAAM,CAAC,UAAU,GAAG,aAAa,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,MAA4B;IACtD,OAAO,EAAE,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC;AACrE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAA2C,EAC3C,MAA4B;IAE5B,MAAM,GAAG,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC3C,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,uDAAuD,qBAAqB,OAAO;YACjF,cAAc,MAAM,CAAC,eAAe,8CAA8C;YAClF,6DAA6D,CAChE,CAAC;IACJ,CAAC;IACD,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CACb,uBAAuB,qBAAqB,wCAAwC;YAClF,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,+BAA+B,MAAM,CAAC,eAAe,KAAK;YAChF,qEAAqE,CACxE,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,SAAiB,EAAE,MAA4B;IACpF,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,sEAAsE,SAAS,EAAE,CAAC,CAAC;IACrG,CAAC;IACD,IAAI,SAAS,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,uBAAuB,SAAS,qCAAqC,MAAM,CAAC,QAAQ,GAAG;YACrF,IAAI,aAAa,8DAA8D;YAC/E,kEAAkE,CACrE,CAAC;IACJ,CAAC;AACH,CAAC;AAiJD,4FAA4F;AAC5F,MAAM,CAAC,MAAM,qCAAqC,GAAG,MAAM,CAAC;AAa5D,MAAM,OAAO,kBAAkB;IAOA;IANZ,SAAS,GAAG,IAAI,GAAG,EAA4B,CAAC;IAChD,GAAG,CAAe;IAClB,GAAG,CAAsB;IAClC,MAAM,GAA0B,IAAI,CAAC;IACrC,QAAQ,GAAG,KAAK,CAAC;IAEzB,YAA6B,IAA4B;QAA5B,SAAI,GAAJ,IAAI,CAAwB;QACvD,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;QAChC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAS,EAAE,CAAC,SAAS,CAAC,CAAC;IACjD,CAAC;IAED;;;OAGG;IACH,SAAS,CAAC,UAAkB,EAAE,MAA4B;QACxD,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,YAAY,UAAU,kDAAkD,CAAC,CAAC;QAC5F,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE;YAC7B,MAAM;YACN,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,SAAS,EAAE,CAAC;YACZ,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,IAAI,GAAG,EAAE;SACjB,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAC,UAAkB;QACxB,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK,SAAS;YAAE,CAAC,CAAC,QAAQ,GAAG,IAAI,CAAC;IACzC,CAAC;IAED,8FAA8F;IAC9F,cAAc,CACZ,UAAkB,EAClB,MAAc,EACd,IAAmE;QAEnE,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK,SAAS;YAAE,OAAO,CAAC,gEAAgE;QAC7F,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACvC,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS;YAAE,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC;QAC5E,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;YACnC,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC;QAC1E,CAAC;QACD,IAAI,IAAI,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,YAAY,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC;QAC7E,CAAC;IACH,CAAC;IAED,yFAAyF;IACzF,KAAK,CAAC,UAAkB;QACtB,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QACtC,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC1C,MAAM,KAAK,GAA6B;YACtC,UAAU;YACV,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,sFAAsF;YACtF,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBACrC,MAAM,GAAG,GAAoB,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;gBACvF,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS;oBAAE,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;gBAC/C,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS;oBAAE,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC;gBAC3D,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS;oBAAE,GAAG,CAAC,SAAS,GAAG,EAAE,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC;gBAClE,OAAO,GAAG,CAAC;YACb,CAAC,CAAC;SACH,CAAC;QACF,IAAI,YAAY,KAAK,SAAS;YAAE,KAAK,CAAC,YAAY,GAAG,YAAY,CAAC;QAClE,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS;YAAE,KAAK,CAAC,SAAS,GAAG,EAAE,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC;QACpE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,oFAAoF;IACpF,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,CAAC,gDAAgD;QAC3E,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC;YACH,KAAK,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBAC7C,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;gBAC1C,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,IAAI,CAAC,GAAG,CAAC,mCAAmC,UAAU,YAAY,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACnF,CAAC;YACH,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACxB,CAAC;IACH,CAAC;IAED,qFAAqF;IACrF,KAAK,CAAC,UAAU,GAAG,qCAAqC;QACtD,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI;YAAE,OAAO;QACjC,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE;YAC7B,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;gBAClC,IAAI,CAAC,GAAG,CAAC,wCAAwC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAClE,CAAC,CAAC,CAAC;QACL,CAAC,EAAE,UAAU,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC;IACxB,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI;YAAE,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IACrB,CAAC;IAED,4EAA4E;IAEpE,KAAK,CAAC,aAAa,CAAC,UAAkB,EAAE,CAAmB;QACjE,IAAI,CAAC,CAAC,QAAQ;YAAE,OAAO,CAAC,yDAAyD;QACjF,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAC9D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,CAAC,CAAC,SAAS,CAAC;QAEhC,qFAAqF;QACrF,qFAAqF;QACrF,sFAAsF;QACtF,8BAA8B;QAC9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;gBAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YACtD,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC7B,IAAI,IAAI,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;oBACpC,IAAI,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC,uBAAuB;oBAChD,IAAI,CAAC,SAAS,KAAK,GAAG,CAAC;gBACzB,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,2BAA2B;oBACxE,IAAI,CAAC,YAAY,GAAG,GAAG,CAAC;gBAC1B,CAAC;YACH,CAAC;iBAAM,IAAI,IAAI,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;gBAC3C,gFAAgF;gBAChF,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC;gBAC5C,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;YAChC,CAAC;YACD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QAC1B,CAAC;QAED,sFAAsF;QACtF,0CAA0C;QAC1C,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,UAAU,CAAC,EAAE,CAAC;YACnE,CAAC,CAAC,QAAQ,GAAG,IAAI,CAAC;YAClB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS;YAAE,OAAO,CAAC,sCAAsC;QAE7E,oFAAoF;QACpF,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACpC,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS;gBAAE,SAAS;YACvE,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,aAAa;gBAAE,SAAS;YACzD,MAAM,MAAM,GACV,qBAAqB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,yBAAyB;gBACjF,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC,MAAM,qBAAqB,GAAG,CAAC;YAC7E,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;QAClF,CAAC;QAED,oFAAoF;QACpF,IAAI,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;YAC7C,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,EAAE;gBAChC,IAAI,EAAE,YAAY;gBAClB,EAAE,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE;gBAC/B,KAAK,EAAE,CAAC,CAAC,SAAS;gBAClB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,iBAAiB;gBAClC,aAAa,EAAE,CAAC,CAAC,MAAM,CAAC,UAAU;aACnC,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC1C,IACE,CAAC,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS;YACjC,YAAY,KAAK,SAAS;YAC1B,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC,UAAU,EAClC,CAAC;YACD,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,EAAE;gBAChC,IAAI,EAAE,MAAM;gBACZ,EAAE,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE;gBAC/B,KAAK,EAAE,YAAY;gBACnB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,UAAU;gBAC3B,aAAa,EAAE,CAAC,CAAC,MAAM,CAAC,UAAU;aACnC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,OAAO,CACnB,UAAkB,EAClB,CAAmB,EACnB,UAA4B;QAE5B,CAAC,CAAC,SAAS,GAAG,UAAU,CAAC;QACzB,MAAM,IAAI,GAAkB,UAAU,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,aAAa,CAAC;QACjG,MAAM,OAAO,GACX,UAAU,CAAC,IAAI,KAAK,YAAY;YAC9B,CAAC,CAAC,qBAAqB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,GAAG,IAAI,CAAC,aAAa;gBACtE,YAAY,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI;YACrD,CAAC,CAAC,gBAAgB,UAAU,CAAC,MAAM,qBAAqB,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;QAC3F,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACpC,IAAI,IAAI,CAAC,KAAK,KAAK,UAAU,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS;gBAAE,SAAS;YACxE,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC7B,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,OAAO,uBAAuB,EAAE,UAAU,CAAC,CAAC;gBAChG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5B,CAAC;iBAAM,IAAI,CAAC,CAAC,MAAM,CAAC,UAAU,KAAK,cAAc,EAAE,CAAC;gBAClD,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,OAAO,yBAAyB,EAAE,WAAW,CAAC,CAAC;gBACnG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACN,mFAAmF;gBACnF,sEAAsE;gBACtE,MAAM,IAAI,CAAC,UAAU,CACnB,UAAU,EACV,CAAC,EACD,IAAI,EACJ,IAAI,EACJ,GAAG,OAAO,wDAAwD,EAClE,WAAW,CACZ,CAAC;gBACF,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;YAClB,IAAI,EAAE,wBAAwB;YAC9B,QAAQ,EAAE,UAAU;YACpB,IAAI,EAAE,UAAU,CAAC,IAAI;YACrB,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,KAAK,EAAE,UAAU,CAAC,KAAK;YACvB,aAAa,EAAE,UAAU,CAAC,aAAa;YACvC,KAAK,EAAE,OAAO;SACf,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CACN,mCAAmC,UAAU,KAAK,OAAO,YAAY;YACnE,GAAG,UAAU,CAAC,aAAa,eAAe,OAAO,CAAC,MAAM,YAAY;YACpE,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,sBAAsB,EAAE,CACpD,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,UAAU,CACtB,UAAkB,EAClB,CAAmB,EACnB,IAAqB,EACrB,IAAmB,EACnB,MAAc,EACd,OAAiC;QAEjC,IAAI,CAAC,SAAS,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,CAAC;QACnF,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;YAC5B,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACpF,CAAC;aAAM,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;YAClC,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACzE,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;YAClB,IAAI,EAAE,sBAAsB;YAC5B,QAAQ,EAAE,UAAU;YACpB,IAAI,EAAE,IAAI,CAAC,MAAM;YACjB,IAAI;YACJ,MAAM;YACN,OAAO;SACR,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CACN,mCAAmC,UAAU,SAAS,IAAI,CAAC,MAAM,KAAK,OAAO,MAAM,MAAM,EAAE,CAC5F,CAAC;IACJ,CAAC;IAEO,SAAS,CAAC,CAAmB,EAAE,MAAc;QACnD,IAAI,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACzD,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,YAAY,CAAC,CAAmB;QACtC,IAAI,KAAyB,CAAC;QAC9B,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACpC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,SAAS;gBAAE,KAAK,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QACxE,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;CACF;AAED,8EAA8E;AAC9E,qDAAqD;AACrD,8EAA8E;AAE9E,sFAAsF;AACtF,MAAM,CAAC,MAAM,2BAA2B,GAAG,2BAA2B,CAAC;AAwDvE,6DAA6D;AAC7D,MAAM,UAAU,sBAAsB,CAAC,KAA+B;IACpE,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,WAA+B,CAAC;IACpC,IAAI,YAAgC,CAAC;IACrC,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;QACxB,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,KAAK,SAAS;YAAE,WAAW,GAAG,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC;QAC5F,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,KAAK,SAAS;YAAE,YAAY,GAAG,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC;IAClG,CAAC;IACD,MAAM,MAAM,GAA8B;QACxC,MAAM,EAAE,sBAAsB;QAC9B,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,KAAK,CAAC,UAAU;QAC7B,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACtC,MAAM,EAAE;YACN,sBAAsB,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,iBAAiB,GAAG,IAAI,CAAC;YACzE,iBAAiB,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC;YAChE,qBAAqB,EAAE,KAAK,CAAC,MAAM,CAAC,eAAe;YACnD,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,QAAQ;YAChC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3F,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,UAAU;SACrC;QACD,UAAU,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE;QACnD,UAAU,EAAE,KAAK,CAAC,SAAS;QAC3B,MAAM,EACJ,KAAK,CAAC,SAAS,KAAK,SAAS;YAC3B,CAAC,CAAC;gBACE,SAAS,EAAE,IAAI;gBACf,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,IAAI;gBAC1B,EAAE,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE;gBACtB,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,aAAa;aAC9C;YACH,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE;QAC1B,MAAM,EAAE;YACN,OAAO,EAAE,MAAM;YACf,GAAG,CAAC,KAAK,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7E,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnE,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACvE;QACD,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC7B,OAAO,EAAE,CAAC,CAAC,MAAM;YACjB,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,GAAG,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,IAAI,EAAE;gBACJ,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM;gBACtB,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACxD,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjF,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACrF;YACD,GAAG,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS;gBAC3B,CAAC,CAAC;oBACE,QAAQ,EAAE;wBACR,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI;wBACtB,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM;wBAC1B,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,EAAE;wBAClB,OAAO,EAAE,CAAC,CAAC,SAAS,CAAC,OAAO;qBAC7B;iBACF;gBACH,CAAC,CAAC,EAAE,CAAC;SACR,CAAC,CAAC;KACJ,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,WAAmB,EACnB,MAAiC;IAEjC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,2BAA2B,CAAC,CAAC;IAC5D,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACtE,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scenario → CampaignNode mapping (TH-9; DES-CAMPAIGN-001 §8).
|
|
3
|
+
*
|
|
4
|
+
* `POST /campaigns` accepts a batch of SCENARIOS (the qe-campaign vocabulary: garden's
|
|
5
|
+
* campaign-recon rungs, or any caller's batch of checks) and this module maps them onto the
|
|
6
|
+
* engine's `CampaignDef` — nodes, `on_success`/`on_terminal` edges, and one composed
|
|
7
|
+
* single-phase workflow per deterministic scenario. Pure data-in/data-out: no engine handle,
|
|
8
|
+
* no I/O, so every rule below is unit-testable without spawning a Core.
|
|
9
|
+
*
|
|
10
|
+
* ## The two scenario shapes, and where the work actually lives
|
|
11
|
+
*
|
|
12
|
+
* - **`tool` (deterministic)** — the scenario is a spec FILE (garden's runner spec, a script,
|
|
13
|
+
* any argv-runnable check) and the node's workflow is one Tool-executor phase running that
|
|
14
|
+
* argv verbatim: no council, no LLM, near-zero cost. The engine's Tool path (`run_tool_cmd`)
|
|
15
|
+
* spawns the command directly.
|
|
16
|
+
* - **`agent` (exploratory / authoring)** — a governed agent run over a problem statement;
|
|
17
|
+
* seats are spent only where judgment is the work (test-R9).
|
|
18
|
+
*
|
|
19
|
+
* ## The 1022-byte rule (load-bearing, enforced here)
|
|
20
|
+
*
|
|
21
|
+
* PTY worker prompts longer than 1022 bytes are SILENTLY DISCARDED by the canonical line
|
|
22
|
+
* discipline — the worker waits on input that never arrives and the unit burns its full
|
|
23
|
+
* timeout (the S8 campaign finding; memory: "PTY canonical line limit"). The mapping
|
|
24
|
+
* therefore refuses, loudly and per-token, anything that smells like a scenario BODY inlined
|
|
25
|
+
* where a FILE PATH belongs: every Tool argv token and every agent problem statement must be
|
|
26
|
+
* a single line of at most {@link MAX_INLINE_BYTES} bytes. The fix is always the same and the
|
|
27
|
+
* error says it: persist the content as a file and pass its path.
|
|
28
|
+
*/
|
|
29
|
+
import type { CampaignDef, LaunchCampaignBody, WorkflowDef } from '../core/types.js';
|
|
30
|
+
/**
|
|
31
|
+
* Byte ceiling for a single inline string handed to a node (a Tool argv token, an agent
|
|
32
|
+
* problem). 1022 is the PTY canonical-line limit — the longest line a worker's stdin
|
|
33
|
+
* accepts before the kernel silently discards it. Tool argv does not itself traverse a PTY,
|
|
34
|
+
* but the ceiling is enforced uniformly because the failure it guards against is the same
|
|
35
|
+
* SHAPE either way: scenario bodies belong in files, and a token this long is a body.
|
|
36
|
+
*/
|
|
37
|
+
export declare const MAX_INLINE_BYTES = 1022;
|
|
38
|
+
/** Composed node workflows carry this prefix — the adapter keeps them OUT of the workflow
|
|
39
|
+
* catalog (`listWorkflows`) while still persisting them to the overlay dir so a fresh-process
|
|
40
|
+
* `resumeCampaign` can re-dispatch pending nodes by workflow id. */
|
|
41
|
+
export declare const CAMPAIGN_WORKFLOW_PREFIX = "campaign-";
|
|
42
|
+
/** The composed workflow id for one deterministic scenario node. */
|
|
43
|
+
export declare function scenarioWorkflowId(campaignId: string, scenarioId: string): string;
|
|
44
|
+
/** A built campaign: the engine def plus the per-node Tool workflows the adapter must arm
|
|
45
|
+
* (hot-register + overlay-persist) BEFORE the def launches. */
|
|
46
|
+
export interface BuiltCampaign {
|
|
47
|
+
def: CampaignDef;
|
|
48
|
+
workflows: WorkflowDef[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Map a `POST /campaigns` body onto the engine's `CampaignDef` + the per-node workflows to arm.
|
|
52
|
+
*
|
|
53
|
+
* Throws a plain `Error` (the route answers 400 with the message) on: a malformed campaign or
|
|
54
|
+
* scenario id, a duplicate scenario id, a scenario with neither/both of `tool`/`agent`, an
|
|
55
|
+
* empty argv, an inline-rule break ({@link assertInline}), an unknown/self/duplicate dep, or an
|
|
56
|
+
* `agent.workflow` that is itself a composed campaign workflow. Cycles are LEFT to the engine's
|
|
57
|
+
* own launch validation — one authority for graph shape, and its reject arrives before anything
|
|
58
|
+
* persists.
|
|
59
|
+
*
|
|
60
|
+
* `defaultClis` is the parsed council roster used for every node that does not carry its own —
|
|
61
|
+
* Tool nodes never convene it (the phase bypasses the council), agent nodes do.
|
|
62
|
+
*/
|
|
63
|
+
export declare function buildCampaign(body: LaunchCampaignBody, defaultClis: unknown[]): BuiltCampaign;
|
|
64
|
+
//# sourceMappingURL=plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/campaigns/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EACV,WAAW,EAIX,kBAAkB,EAElB,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAE1B;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,OAAO,CAAC;AASrC;;qEAEqE;AACrE,eAAO,MAAM,wBAAwB,cAAc,CAAC;AAEpD,oEAAoE;AACpE,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED;gEACgE;AAChE,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,WAAW,CAAC;IACjB,SAAS,EAAE,WAAW,EAAE,CAAC;CAC1B;AAiDD;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,kBAAkB,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,aAAa,CAwD7F"}
|