@smartmemory/stratum 0.3.4 → 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.
- package/dist/cli/guard.js +63 -6
- package/dist/cli/guard.js.map +1 -1
- package/dist/cli/learn.js +226 -0
- package/dist/cli/learn.js.map +1 -0
- package/dist/cli/stratum.js +3 -1
- package/dist/cli/stratum.js.map +1 -1
- package/dist/connectors/background.js +53 -9
- package/dist/connectors/background.js.map +1 -1
- package/dist/connectors/base.js +16 -0
- package/dist/connectors/base.js.map +1 -1
- package/dist/connectors/cancellation.js +119 -0
- package/dist/connectors/cancellation.js.map +1 -0
- package/dist/connectors/claude-bg-worker.js +9 -1
- package/dist/connectors/claude-bg-worker.js.map +1 -1
- package/dist/connectors/claude.js +179 -86
- package/dist/connectors/claude.js.map +1 -1
- package/dist/connectors/codex.js +219 -62
- package/dist/connectors/codex.js.map +1 -1
- package/dist/connectors/runner.js +44 -3
- package/dist/connectors/runner.js.map +1 -1
- package/dist/contracts/events.json +34 -1
- package/dist/contracts/guard-signers.allowed +36 -0
- package/dist/contracts/mcp-surface.json +1085 -104
- package/dist/engine/checkpoint.js +8 -1
- package/dist/engine/checkpoint.js.map +1 -1
- package/dist/engine/engine.js +403 -42
- package/dist/engine/engine.js.map +1 -1
- package/dist/engine/ledger.js +12 -0
- package/dist/engine/ledger.js.map +1 -1
- package/dist/engine/receipts.js +91 -0
- package/dist/engine/receipts.js.map +1 -0
- package/dist/engine/state.js +51 -1
- package/dist/engine/state.js.map +1 -1
- package/dist/guard/authorization.js +71 -0
- package/dist/guard/authorization.js.map +1 -0
- package/dist/guard/descriptors.js +248 -0
- package/dist/guard/descriptors.js.map +1 -0
- package/dist/guard/errors.js +12 -0
- package/dist/guard/errors.js.map +1 -1
- package/dist/guard/evidence.js +24 -3
- package/dist/guard/evidence.js.map +1 -1
- package/dist/guard/sshsig.js +264 -0
- package/dist/guard/sshsig.js.map +1 -0
- package/dist/guard/store.js +13 -0
- package/dist/guard/store.js.map +1 -1
- package/dist/guard/transition.js +484 -33
- package/dist/guard/transition.js.map +1 -1
- package/dist/guard/trust.js +78 -0
- package/dist/guard/trust.js.map +1 -0
- package/dist/judge/judged.js +1 -1
- package/dist/judge/pricing.js +1 -0
- package/dist/judge/pricing.js.map +1 -1
- package/dist/learn/apply.js +600 -0
- package/dist/learn/apply.js.map +1 -0
- package/dist/learn/candidate.js +186 -0
- package/dist/learn/candidate.js.map +1 -0
- package/dist/learn/classify.js +181 -0
- package/dist/learn/classify.js.map +1 -0
- package/dist/learn/harvest.js +138 -0
- package/dist/learn/harvest.js.map +1 -0
- package/dist/learn/smartmemory_egress.js +330 -0
- package/dist/learn/smartmemory_egress.js.map +1 -0
- package/dist/mcp/server.js +150 -22
- package/dist/mcp/server.js.map +1 -1
- package/dist/policy/bundle.js +195 -0
- package/dist/policy/bundle.js.map +1 -0
- package/dist/policy/events.js +51 -0
- package/dist/policy/events.js.map +1 -0
- package/dist/policy/smartmemory_client.js +332 -0
- package/dist/policy/smartmemory_client.js.map +1 -0
- package/dist/policy/types.js +2 -0
- package/dist/policy/types.js.map +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
const MIN_BACKOFF_MS = 1_000;
|
|
2
|
+
const MAX_BACKOFF_MS = 5 * 60_000;
|
|
3
|
+
const REQUEST_TIMEOUT_MS = 5_000;
|
|
4
|
+
const DEAD_STATUSES = new Set([400, 401, 403, 404, 422]);
|
|
5
|
+
const MEMORY_TYPES = [
|
|
6
|
+
"stratum_usage_debit",
|
|
7
|
+
"stratum_step_reset",
|
|
8
|
+
"stratum_checkpoint_reverted",
|
|
9
|
+
];
|
|
10
|
+
export class LearnEgress {
|
|
11
|
+
url;
|
|
12
|
+
apiKey;
|
|
13
|
+
workspaceId;
|
|
14
|
+
optedIn;
|
|
15
|
+
fetchImpl;
|
|
16
|
+
now;
|
|
17
|
+
random;
|
|
18
|
+
warn;
|
|
19
|
+
setTimeoutImpl;
|
|
20
|
+
clearTimeoutImpl;
|
|
21
|
+
store;
|
|
22
|
+
withReceiptUpdate;
|
|
23
|
+
drains = new Map();
|
|
24
|
+
backoffs = new Map();
|
|
25
|
+
workspaceWarningIssued = false;
|
|
26
|
+
constructor(options) {
|
|
27
|
+
const env = options.env ?? process.env;
|
|
28
|
+
const url = env.SMARTMEMORY_API_URL?.replace(/\/+$/, "");
|
|
29
|
+
this.url = url ? url : undefined;
|
|
30
|
+
this.apiKey = env.SMARTMEMORY_API_KEY || undefined;
|
|
31
|
+
this.workspaceId = env.SMARTMEMORY_WORKSPACE_ID || undefined;
|
|
32
|
+
this.optedIn = env.STRATUM_LEARN_EGRESS === "1";
|
|
33
|
+
this.fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
34
|
+
this.now = options.now ?? Date.now;
|
|
35
|
+
this.random = options.random ?? Math.random;
|
|
36
|
+
this.warn = options.warn ?? ((warning) => console.warn(warning));
|
|
37
|
+
this.setTimeoutImpl = options.setTimeoutImpl ?? setTimeout;
|
|
38
|
+
this.clearTimeoutImpl = options.clearTimeoutImpl ?? clearTimeout;
|
|
39
|
+
this.store = options.store;
|
|
40
|
+
this.withReceiptUpdate = options.withReceiptUpdate;
|
|
41
|
+
}
|
|
42
|
+
enabled() {
|
|
43
|
+
if (!this.optedIn || this.url === undefined || this.apiKey === undefined)
|
|
44
|
+
return false;
|
|
45
|
+
if (this.workspaceId !== undefined)
|
|
46
|
+
return true;
|
|
47
|
+
if (!this.workspaceWarningIssued) {
|
|
48
|
+
this.workspaceWarningIssued = true;
|
|
49
|
+
this.warn("SmartMemory receipt egress is disabled: SMARTMEMORY_WORKSPACE_ID is required");
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
async drainRun(runId) {
|
|
54
|
+
if (!this.enabled())
|
|
55
|
+
return;
|
|
56
|
+
let state = this.drains.get(runId);
|
|
57
|
+
if (state === undefined) {
|
|
58
|
+
state = { dirty: false, inFlight: undefined };
|
|
59
|
+
this.drains.set(runId, state);
|
|
60
|
+
}
|
|
61
|
+
state.dirty = true;
|
|
62
|
+
if (state.inFlight === undefined)
|
|
63
|
+
this.startDrain(runId, state);
|
|
64
|
+
await state.inFlight;
|
|
65
|
+
}
|
|
66
|
+
async drainAll() {
|
|
67
|
+
if (!this.enabled())
|
|
68
|
+
return;
|
|
69
|
+
try {
|
|
70
|
+
await Promise.all((await this.store.list()).map((runId) => this.drainRun(runId)));
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
this.warn(`SmartMemory egress reconciliation failed: ${message(error)}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async close() {
|
|
77
|
+
for (;;) {
|
|
78
|
+
const inFlight = [...this.drains.values()].flatMap((state) => state.inFlight ? [state.inFlight] : []);
|
|
79
|
+
if (inFlight.length === 0)
|
|
80
|
+
return;
|
|
81
|
+
await Promise.all(inFlight);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async verifyRun(runId) {
|
|
85
|
+
if (!this.enabled()) {
|
|
86
|
+
throw new Error("SmartMemory egress is disabled: STRATUM_LEARN_EGRESS=1 and SMARTMEMORY_API_URL/API_KEY/WORKSPACE_ID are required");
|
|
87
|
+
}
|
|
88
|
+
const run = await this.store.load(runId);
|
|
89
|
+
const local = run.receipts ?? [];
|
|
90
|
+
let missingCount = 0;
|
|
91
|
+
let duplicateCount = 0;
|
|
92
|
+
let wrongTypeCount = 0;
|
|
93
|
+
for (const receipt of local) {
|
|
94
|
+
const receiptId = `${runId}:${receipt.seq}`;
|
|
95
|
+
const expectedType = memoryTypeFor(receipt);
|
|
96
|
+
const expectedHits = await this.exactHits(receiptId, expectedType);
|
|
97
|
+
if (expectedHits.length > 0) {
|
|
98
|
+
duplicateCount += expectedHits.length - 1;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
missingCount += 1;
|
|
102
|
+
for (const otherType of MEMORY_TYPES) {
|
|
103
|
+
if (otherType === expectedType)
|
|
104
|
+
continue;
|
|
105
|
+
wrongTypeCount += (await this.exactHits(receiptId, otherType)).length;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
runId,
|
|
110
|
+
receiptCount: local.length,
|
|
111
|
+
missingCount,
|
|
112
|
+
duplicateCount,
|
|
113
|
+
wrongTypeCount,
|
|
114
|
+
deadCount: local.filter((receipt) => receipt.egress === "dead").length,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
async retryDead(runId) {
|
|
118
|
+
let retried = 0;
|
|
119
|
+
try {
|
|
120
|
+
retried = await this.withReceiptUpdate(runId, (run) => {
|
|
121
|
+
let changed = 0;
|
|
122
|
+
for (const receipt of run.receipts ?? []) {
|
|
123
|
+
if (receipt.egress !== "dead")
|
|
124
|
+
continue;
|
|
125
|
+
receipt.egress = "pending";
|
|
126
|
+
delete receipt.egressStatus;
|
|
127
|
+
changed += 1;
|
|
128
|
+
}
|
|
129
|
+
return changed;
|
|
130
|
+
});
|
|
131
|
+
await this.drainRun(runId);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
this.warn(`SmartMemory egress could not retry dead receipts for run ${runId}: ${message(error)}`);
|
|
135
|
+
}
|
|
136
|
+
return retried;
|
|
137
|
+
}
|
|
138
|
+
startDrain(runId, state) {
|
|
139
|
+
const inFlight = this.drainLoop(runId, state)
|
|
140
|
+
.catch((error) => {
|
|
141
|
+
this.warn(`SmartMemory egress could not drain run ${runId}: ${message(error)}`);
|
|
142
|
+
})
|
|
143
|
+
.finally(() => {
|
|
144
|
+
state.inFlight = undefined;
|
|
145
|
+
if (state.dirty)
|
|
146
|
+
this.startDrain(runId, state);
|
|
147
|
+
else if (this.drains.get(runId) === state)
|
|
148
|
+
this.drains.delete(runId);
|
|
149
|
+
});
|
|
150
|
+
state.inFlight = inFlight;
|
|
151
|
+
}
|
|
152
|
+
async drainLoop(runId, state) {
|
|
153
|
+
while (state.dirty) {
|
|
154
|
+
state.dirty = false;
|
|
155
|
+
await this.drainOnce(runId);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async drainOnce(runId) {
|
|
159
|
+
const backoff = this.backoffs.get(runId);
|
|
160
|
+
if (backoff !== undefined && this.now() < backoff.nextAttemptAt)
|
|
161
|
+
return;
|
|
162
|
+
const snapshot = await this.withReceiptUpdate(runId, (run) => ({
|
|
163
|
+
id: run.id,
|
|
164
|
+
flowName: run.flowName,
|
|
165
|
+
...(run.workspaceRoot !== undefined ? { workspaceRoot: run.workspaceRoot } : {}),
|
|
166
|
+
...(run.revisionDigest !== undefined ? { revisionDigest: run.revisionDigest } : {}),
|
|
167
|
+
pending: (run.receipts ?? [])
|
|
168
|
+
.filter((receipt) => receipt.egress === "pending")
|
|
169
|
+
.sort((left, right) => left.seq - right.seq)
|
|
170
|
+
.map((receipt) => structuredClone(receipt)),
|
|
171
|
+
}));
|
|
172
|
+
if (snapshot.pending.length === 0)
|
|
173
|
+
return;
|
|
174
|
+
const transitions = [];
|
|
175
|
+
for (const receipt of snapshot.pending) {
|
|
176
|
+
let response;
|
|
177
|
+
let failure;
|
|
178
|
+
try {
|
|
179
|
+
response = await this.add(snapshot, receipt);
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
failure = error;
|
|
183
|
+
}
|
|
184
|
+
if (response?.ok) {
|
|
185
|
+
transitions.push({ seq: receipt.seq, egress: "sent" });
|
|
186
|
+
this.resetBackoff(runId);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (response !== undefined && DEAD_STATUSES.has(response.status)) {
|
|
190
|
+
transitions.push({ seq: receipt.seq, egress: "dead", status: response.status });
|
|
191
|
+
this.warn(`SmartMemory egress dead-lettered ${snapshot.id}:${receipt.seq}: HTTP ${response.status}`);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
this.recordFailure(runId);
|
|
195
|
+
const detail = response === undefined ? message(failure) : `HTTP ${response.status}`;
|
|
196
|
+
this.warn(`SmartMemory egress stopped at ${snapshot.id}:${receipt.seq}: ${detail}`);
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
if (transitions.length === 0)
|
|
200
|
+
return;
|
|
201
|
+
await this.withReceiptUpdate(runId, (run) => {
|
|
202
|
+
const bySequence = new Map((run.receipts ?? []).map((receipt) => [receipt.seq, receipt]));
|
|
203
|
+
for (const transition of transitions) {
|
|
204
|
+
const receipt = bySequence.get(transition.seq);
|
|
205
|
+
if (receipt === undefined || receipt.egress !== "pending")
|
|
206
|
+
continue;
|
|
207
|
+
receipt.egress = transition.egress;
|
|
208
|
+
if (transition.status === undefined)
|
|
209
|
+
delete receipt.egressStatus;
|
|
210
|
+
else
|
|
211
|
+
receipt.egressStatus = transition.status;
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
async add(run, receipt) {
|
|
216
|
+
return this.request(`${this.url}/memory/add`, {
|
|
217
|
+
content: summary(run, receipt),
|
|
218
|
+
memory_type: memoryTypeFor(receipt),
|
|
219
|
+
metadata: {
|
|
220
|
+
...receipt,
|
|
221
|
+
...(receipt.detail !== undefined ? { detail: receipt.detail } : {}),
|
|
222
|
+
run_id: run.id,
|
|
223
|
+
workspace_root: run.workspaceRoot ?? null,
|
|
224
|
+
flow_name: run.flowName,
|
|
225
|
+
spec_digest: run.revisionDigest ?? null,
|
|
226
|
+
event_ordinal: receipt.seq,
|
|
227
|
+
receipt_id: `${run.id}:${receipt.seq}`,
|
|
228
|
+
origin: "cli:stratum",
|
|
229
|
+
},
|
|
230
|
+
use_pipeline: false,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
async exactHits(receiptId, memoryType) {
|
|
234
|
+
const response = await this.request(`${this.url}/memory/search`, {
|
|
235
|
+
query: receiptId,
|
|
236
|
+
memory_type: memoryType,
|
|
237
|
+
top_k: 20,
|
|
238
|
+
});
|
|
239
|
+
if (!response.ok)
|
|
240
|
+
throw new Error(`SmartMemory verify failed for ${memoryType}: HTTP ${response.status}`);
|
|
241
|
+
const payload = await response.json();
|
|
242
|
+
if (!Array.isArray(payload.results))
|
|
243
|
+
throw new Error(`SmartMemory verify returned no results array for ${memoryType}`);
|
|
244
|
+
return payload.results.filter((result) => resultReceiptId(result) === receiptId && resultMemoryType(result) === memoryType);
|
|
245
|
+
}
|
|
246
|
+
async request(url, body) {
|
|
247
|
+
const controller = new AbortController();
|
|
248
|
+
const timeout = this.setTimeoutImpl(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
249
|
+
try {
|
|
250
|
+
return await this.fetchImpl(url, {
|
|
251
|
+
method: "POST",
|
|
252
|
+
headers: {
|
|
253
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
254
|
+
"X-Workspace-Id": this.workspaceId,
|
|
255
|
+
"Content-Type": "application/json",
|
|
256
|
+
},
|
|
257
|
+
body: JSON.stringify(body),
|
|
258
|
+
signal: controller.signal,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
this.clearTimeoutImpl(timeout);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
recordFailure(runId) {
|
|
266
|
+
const current = this.backoffs.get(runId) ?? { consecutiveFailures: 0, nextAttemptAt: 0 };
|
|
267
|
+
current.consecutiveFailures += 1;
|
|
268
|
+
const exponent = Math.min(current.consecutiveFailures - 1, 20);
|
|
269
|
+
const base = Math.min(MAX_BACKOFF_MS, MIN_BACKOFF_MS * (2 ** exponent));
|
|
270
|
+
const jittered = Math.min(MAX_BACKOFF_MS, base + (base * 0.2 * this.random()));
|
|
271
|
+
current.nextAttemptAt = this.now() + jittered;
|
|
272
|
+
this.backoffs.set(runId, current);
|
|
273
|
+
}
|
|
274
|
+
resetBackoff(runId) {
|
|
275
|
+
this.backoffs.delete(runId);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function memoryTypeFor(receipt) {
|
|
279
|
+
if (receipt.source === "engine" && receipt.dispatchId.startsWith("engine:step_reset:"))
|
|
280
|
+
return "stratum_step_reset";
|
|
281
|
+
if (receipt.source === "engine" && receipt.dispatchId.startsWith("engine:checkpoint_reverted:"))
|
|
282
|
+
return "stratum_checkpoint_reverted";
|
|
283
|
+
return "stratum_usage_debit";
|
|
284
|
+
}
|
|
285
|
+
function summary(run, receipt) {
|
|
286
|
+
const tokens = receipt.amount.tokens ?? 0;
|
|
287
|
+
const milliseconds = receipt.amount.ms ?? receipt.telemetry.durationMs;
|
|
288
|
+
return `${run.flowName}/${receipt.stepId ?? "run"} ${receipt.source} (${receipt.telemetry.model}): ${tokens} tokens, ${milliseconds} ms`;
|
|
289
|
+
}
|
|
290
|
+
function resultReceiptId(result) {
|
|
291
|
+
return resultField(result, (value) => {
|
|
292
|
+
if (typeof value.receipt_id === "string")
|
|
293
|
+
return value.receipt_id;
|
|
294
|
+
if (typeof value.metadata !== "object" || value.metadata === null || Array.isArray(value.metadata))
|
|
295
|
+
return undefined;
|
|
296
|
+
const receiptId = value.metadata.receipt_id;
|
|
297
|
+
return typeof receiptId === "string" ? receiptId : undefined;
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
function resultMemoryType(result) {
|
|
301
|
+
return resultField(result, (value) => {
|
|
302
|
+
if (typeof value.memory_type === "string")
|
|
303
|
+
return value.memory_type;
|
|
304
|
+
if (typeof value.metadata !== "object" || value.metadata === null || Array.isArray(value.metadata))
|
|
305
|
+
return undefined;
|
|
306
|
+
const memoryType = value.metadata.memory_type;
|
|
307
|
+
return typeof memoryType === "string" ? memoryType : undefined;
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
function resultField(result, read) {
|
|
311
|
+
if (typeof result !== "object" || result === null || Array.isArray(result))
|
|
312
|
+
return undefined;
|
|
313
|
+
const row = result;
|
|
314
|
+
const direct = read(row);
|
|
315
|
+
if (direct !== undefined)
|
|
316
|
+
return direct;
|
|
317
|
+
for (const key of ["item", "context"]) {
|
|
318
|
+
const nested = row[key];
|
|
319
|
+
if (typeof nested !== "object" || nested === null || Array.isArray(nested))
|
|
320
|
+
continue;
|
|
321
|
+
const found = read(nested);
|
|
322
|
+
if (found !== undefined)
|
|
323
|
+
return found;
|
|
324
|
+
}
|
|
325
|
+
return undefined;
|
|
326
|
+
}
|
|
327
|
+
function message(error) {
|
|
328
|
+
return error instanceof Error ? error.message : String(error);
|
|
329
|
+
}
|
|
330
|
+
//# sourceMappingURL=smartmemory_egress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smartmemory_egress.js","sourceRoot":"","sources":["../../src/learn/smartmemory_egress.ts"],"names":[],"mappings":"AAGA,MAAM,cAAc,GAAG,KAAK,CAAC;AAC7B,MAAM,cAAc,GAAG,CAAC,GAAG,MAAM,CAAC;AAClC,MAAM,kBAAkB,GAAG,KAAK,CAAC;AACjC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AACzD,MAAM,YAAY,GAAG;IACnB,qBAAqB;IACrB,oBAAoB;IACpB,6BAA6B;CACrB,CAAC;AAiEX,MAAM,OAAO,WAAW;IACL,GAAG,CAAqB;IACxB,MAAM,CAAqB;IAC3B,WAAW,CAAqB;IAChC,OAAO,CAAU;IACjB,SAAS,CAAmB;IAC5B,GAAG,CAAe;IAClB,MAAM,CAAe;IACrB,IAAI,CAA4B;IAChC,cAAc,CAAgE;IAC9E,gBAAgB,CAAkC;IAClD,KAAK,CAAa;IAClB,iBAAiB,CAAoB;IACrC,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAC;IACvC,QAAQ,GAAG,IAAI,GAAG,EAAwB,CAAC;IACpD,sBAAsB,GAAG,KAAK,CAAC;IAEvC,YAAY,OAA2B;QACrC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;QACvC,MAAM,GAAG,GAAG,GAAG,CAAC,mBAAmB,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;QACjC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,mBAAmB,IAAI,SAAS,CAAC;QACnD,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC,wBAAwB,IAAI,SAAS,CAAC;QAC7D,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,oBAAoB,KAAK,GAAG,CAAC;QAChD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC;QACvD,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC;QAC5C,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;QACjE,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,UAAU,CAAC;QAC3D,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,YAAY,CAAC;QACjE,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IACrD,CAAC;IAED,OAAO;QACL,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QACvF,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE,CAAC;YACjC,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC;YACnC,IAAI,CAAC,IAAI,CAAC,8EAA8E,CAAC,CAAC;QAC5F,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAa;QAC1B,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YAAE,OAAO;QAC5B,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,KAAK,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAChC,CAAC;QACD,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;QACnB,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS;YAAE,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAChE,MAAM,KAAK,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YAAE,OAAO;QAC5B,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACpF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,IAAI,CAAC,6CAA6C,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;QACT,SAAS,CAAC;YACR,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACtG,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YAClC,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,KAAa;QAC3B,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,kHAAkH,CAAC,CAAC;QACtI,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC;QACjC,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,IAAI,cAAc,GAAG,CAAC,CAAC;QACvB,IAAI,cAAc,GAAG,CAAC,CAAC;QAEvB,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;YAC5B,MAAM,SAAS,GAAG,GAAG,KAAK,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YAC5C,MAAM,YAAY,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;YAC5C,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;YACnE,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC5B,cAAc,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;gBAC1C,SAAS;YACX,CAAC;YAED,YAAY,IAAI,CAAC,CAAC;YAClB,KAAK,MAAM,SAAS,IAAI,YAAY,EAAE,CAAC;gBACrC,IAAI,SAAS,KAAK,YAAY;oBAAE,SAAS;gBACzC,cAAc,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;YACxE,CAAC;QACH,CAAC;QAED,OAAO;YACL,KAAK;YACL,YAAY,EAAE,KAAK,CAAC,MAAM;YAC1B,YAAY;YACZ,cAAc;YACd,cAAc;YACd,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM;SACvE,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,KAAa;QAC3B,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;gBACpD,IAAI,OAAO,GAAG,CAAC,CAAC;gBAChB,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;oBACzC,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM;wBAAE,SAAS;oBACxC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;oBAC3B,OAAO,OAAO,CAAC,YAAY,CAAC;oBAC5B,OAAO,IAAI,CAAC,CAAC;gBACf,CAAC;gBACD,OAAO,OAAO,CAAC;YACjB,CAAC,CAAC,CAAC;YACH,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,IAAI,CAAC,4DAA4D,KAAK,KAAK,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACpG,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,UAAU,CAAC,KAAa,EAAE,KAAiB;QACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC;aAC1C,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACf,IAAI,CAAC,IAAI,CAAC,0CAA0C,KAAK,KAAK,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAClF,CAAC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE;YACZ,KAAK,CAAC,QAAQ,GAAG,SAAS,CAAC;YAC3B,IAAI,KAAK,CAAC,KAAK;gBAAE,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;iBAC1C,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK;gBAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,CAAC,CAAC,CAAC;QACL,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC5B,CAAC;IAEO,KAAK,CAAC,SAAS,CAAC,KAAa,EAAE,KAAiB;QACtD,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC;YACnB,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;YACpB,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,SAAS,CAAC,KAAa;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,OAAO,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,aAAa;YAAE,OAAO;QAExE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC,GAAG,EAAiB,EAAE,CAAC,CAAC;YAC5E,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,GAAG,CAAC,GAAG,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChF,GAAG,CAAC,GAAG,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnF,OAAO,EAAE,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC;iBAC1B,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC;iBACjD,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;iBAC3C,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;SAC9C,CAAC,CAAC,CAAC;QACJ,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAE1C,MAAM,WAAW,GAAwB,EAAE,CAAC;QAC5C,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YACvC,IAAI,QAAmC,CAAC;YACxC,IAAI,OAAgB,CAAC;YACrB,IAAI,CAAC;gBACH,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC/C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,GAAG,KAAK,CAAC;YAClB,CAAC;YACD,IAAI,QAAQ,EAAE,EAAE,EAAE,CAAC;gBACjB,WAAW,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;gBACvD,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;gBACzB,SAAS;YACX,CAAC;YACD,IAAI,QAAQ,KAAK,SAAS,IAAI,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACjE,WAAW,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;gBAChF,IAAI,CAAC,IAAI,CAAC,oCAAoC,QAAQ,CAAC,EAAE,IAAI,OAAO,CAAC,GAAG,UAAU,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;gBACrG,SAAS;YACX,CAAC;YAED,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAC1B,MAAM,MAAM,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC;YACrF,IAAI,CAAC,IAAI,CAAC,iCAAiC,QAAQ,CAAC,EAAE,IAAI,OAAO,CAAC,GAAG,KAAK,MAAM,EAAE,CAAC,CAAC;YACpF,MAAM;QACR,CAAC;QAED,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACrC,MAAM,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;YAC1C,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;YAC1F,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;gBACrC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;gBAC/C,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;oBAAE,SAAS;gBACpE,OAAO,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;gBACnC,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS;oBAAE,OAAO,OAAO,CAAC,YAAY,CAAC;;oBAC5D,OAAO,CAAC,YAAY,GAAG,UAAU,CAAC,MAAM,CAAC;YAChD,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,GAAG,CAAC,GAAkB,EAAE,OAAsB;QAC1D,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,GAAI,aAAa,EAAE;YAC7C,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC;YAC9B,WAAW,EAAE,aAAa,CAAC,OAAO,CAAC;YACnC,QAAQ,EAAE;gBACR,GAAG,OAAO;gBACV,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnE,MAAM,EAAE,GAAG,CAAC,EAAE;gBACd,cAAc,EAAE,GAAG,CAAC,aAAa,IAAI,IAAI;gBACzC,SAAS,EAAE,GAAG,CAAC,QAAQ;gBACvB,WAAW,EAAE,GAAG,CAAC,cAAc,IAAI,IAAI;gBACvC,aAAa,EAAE,OAAO,CAAC,GAAG;gBAC1B,UAAU,EAAE,GAAG,GAAG,CAAC,EAAE,IAAI,OAAO,CAAC,GAAG,EAAE;gBACtC,MAAM,EAAE,aAAa;aACtB;YACD,YAAY,EAAE,KAAK;SACpB,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,SAAS,CAAC,SAAiB,EAAE,UAAsB;QAC/D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,GAAI,gBAAgB,EAAE;YAChE,KAAK,EAAE,SAAS;YAChB,WAAW,EAAE,UAAU;YACvB,KAAK,EAAE,EAAE;SACV,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,UAAU,UAAU,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1G,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA2B,CAAC;QAC/D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,oDAAoD,UAAU,EAAE,CAAC,CAAC;QACvH,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CACvC,eAAe,CAAC,MAAM,CAAC,KAAK,SAAS,IAAI,gBAAgB,CAAC,MAAM,CAAC,KAAK,UAAU,CAAC,CAAC;IACtF,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,IAAa;QAC9C,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,kBAAkB,CAAC,CAAC;QAClF,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE;gBAC/B,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,aAAa,EAAE,UAAU,IAAI,CAAC,MAAO,EAAE;oBACvC,gBAAgB,EAAE,IAAI,CAAC,WAAY;oBACnC,cAAc,EAAE,kBAAkB;iBACnC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;gBAC1B,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CAAC,CAAC;QACL,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAEO,aAAa,CAAC,KAAa;QACjC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,mBAAmB,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC;QACzF,OAAO,CAAC,mBAAmB,IAAI,CAAC,CAAC;QACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,mBAAmB,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,cAAc,GAAG,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC;QACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,GAAG,CAAC,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC/E,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC;QAC9C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACpC,CAAC;IAEO,YAAY,CAAC,KAAa;QAChC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;CACF;AAED,SAAS,aAAa,CAAC,OAAsB;IAC3C,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,oBAAoB,CAAC;QAAE,OAAO,oBAAoB,CAAC;IACpH,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,6BAA6B,CAAC;QAAE,OAAO,6BAA6B,CAAC;IACtI,OAAO,qBAAqB,CAAC;AAC/B,CAAC;AAED,SAAS,OAAO,CAAC,GAAmC,EAAE,OAAsB;IAC1E,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IAC1C,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC;IACvE,OAAO,GAAG,GAAG,CAAC,QAAQ,IAAI,OAAO,CAAC,MAAM,IAAI,KAAK,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,KAAK,MAAM,MAAM,YAAY,YAAY,KAAK,CAAC;AAC3I,CAAC;AAED,SAAS,eAAe,CAAC,MAAe;IACtC,OAAO,WAAW,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;QACnC,IAAI,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC,UAAU,CAAC;QAClE,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC;YAAE,OAAO,SAAS,CAAC;QACrH,MAAM,SAAS,GAAI,KAAK,CAAC,QAAoC,CAAC,UAAU,CAAC;QACzE,OAAO,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/D,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAe;IACvC,OAAO,WAAW,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;QACnC,IAAI,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC,WAAW,CAAC;QACpE,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC;YAAE,OAAO,SAAS,CAAC;QACrH,MAAM,UAAU,GAAI,KAAK,CAAC,QAAoC,CAAC,WAAW,CAAC;QAC3E,OAAO,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IACjE,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,WAAW,CAClB,MAAe,EACf,IAA4D;IAE5D,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC;IAC7F,MAAM,GAAG,GAAG,MAAiC,CAAC;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACxC,KAAK,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,SAAS,CAAU,EAAE,CAAC;QAC/C,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QACxB,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,SAAS;QACrF,MAAM,KAAK,GAAG,IAAI,CAAC,MAAiC,CAAC,CAAC;QACtD,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;IACxC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,OAAO,CAAC,KAAc;IAC7B,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC"}
|
package/dist/mcp/server.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { linkAbort, teardownDeadline } from "../connectors/cancellation.js";
|
|
1
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
4
|
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
-
import { randomUUID } from "node:crypto";
|
|
5
5
|
import { readFileSync } from "node:fs";
|
|
6
6
|
import { stat } from "node:fs/promises";
|
|
7
7
|
import { cancelBackgroundRun, pollBackgroundRun, runAgent } from "../connectors/index.js";
|
|
8
|
-
import { CheckpointOperationError, SpecValidationError, StratumEngine } from "../engine/engine.js";
|
|
8
|
+
import { CheckpointOperationError, InputValidationError, SpecValidationError, StratumEngine } from "../engine/engine.js";
|
|
9
9
|
import { createEvaluator } from "../eval/expr.js";
|
|
10
10
|
import { createEvaluateRunner } from "../engine/evaluate.js";
|
|
11
11
|
import { validateSpec } from "../ir/validate.js";
|
|
@@ -53,14 +53,65 @@ function defaultEngine() {
|
|
|
53
53
|
evaluateRunner: createEvaluateRunner(),
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
+
/** Every structured MCP error goes through the registry: an envelope the
|
|
57
|
+
* contract does not declare (or whose payload drifts from the declaration)
|
|
58
|
+
* fails here rather than reaching a client as an undeclared shape. */
|
|
59
|
+
async function registryError(envelope, errorCode, message, data) {
|
|
60
|
+
const declaration = (await mcpSurface()).errors[envelope];
|
|
61
|
+
if (!declaration)
|
|
62
|
+
throw new Error(`MCP error registry is missing ${envelope}`);
|
|
63
|
+
assertShape(data, declaration.data, `errors.${envelope}.data`);
|
|
64
|
+
return new McpError(errorCode, message, data);
|
|
65
|
+
}
|
|
66
|
+
/** Rejects a request field before the contract check can run — the cancellation
|
|
67
|
+
* bookkeeping below must be in place before any awaited contract I/O, so these
|
|
68
|
+
* few fields are validated by hand and reported in the declared shape. */
|
|
69
|
+
function inputValidationError(path, message) {
|
|
70
|
+
return registryError("input_validation_failed", ErrorCode.InvalidParams, message, {
|
|
71
|
+
code: "input_validation_failed",
|
|
72
|
+
errors: [{ code: "input_validation_failed", path, message }],
|
|
73
|
+
});
|
|
74
|
+
}
|
|
56
75
|
export function createToolDispatcher(dependencies = {}) {
|
|
57
76
|
const engine = dependencies.engine ?? defaultEngine();
|
|
58
77
|
const agentRun = dependencies.runAgent ?? runAgent;
|
|
59
78
|
const agentPoll = dependencies.pollBackgroundRun ?? pollBackgroundRun;
|
|
60
79
|
const agentCancel = dependencies.cancelBackgroundRun ?? cancelBackgroundRun;
|
|
80
|
+
// Register before contract I/O: an immediate cancellation cannot overtake startup.
|
|
81
|
+
// UUIDs cannot collide with durable background IDs.
|
|
82
|
+
const foreground = new Map();
|
|
83
|
+
const completed = new Map();
|
|
61
84
|
return {
|
|
62
85
|
async call(tool, request, context = {}) {
|
|
86
|
+
// This request has one required string field. Check that exact shape before
|
|
87
|
+
// acting synchronously, otherwise contract loading can let startup overtake
|
|
88
|
+
// an already-received cancellation. Full contract validation still follows.
|
|
89
|
+
if (tool === "stratum_cancel_agent_run" && typeof request.runId === "string"
|
|
90
|
+
&& Object.keys(request).length === 1) {
|
|
91
|
+
foreground.get(request.runId)?.controller.abort(new Error("Foreground agent run cancelled"));
|
|
92
|
+
}
|
|
93
|
+
let cancellationId;
|
|
94
|
+
let controller;
|
|
95
|
+
let settle;
|
|
96
|
+
let unlink;
|
|
97
|
+
let succeeded = false;
|
|
98
|
+
let teardownFailure;
|
|
63
99
|
try {
|
|
100
|
+
if (tool === "stratum_agent_run" && request.cancellationId !== undefined) {
|
|
101
|
+
if (typeof request.cancellationId !== "string"
|
|
102
|
+
|| !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(request.cancellationId)) {
|
|
103
|
+
throw await inputValidationError("cancellationId", "cancellationId must be a UUID");
|
|
104
|
+
}
|
|
105
|
+
cancellationId = request.cancellationId;
|
|
106
|
+
if (request.background === true)
|
|
107
|
+
throw await inputValidationError("background", "cancellationId is only supported for foreground runs");
|
|
108
|
+
if (foreground.has(cancellationId) || completed.has(cancellationId))
|
|
109
|
+
throw await inputValidationError("cancellationId", "cancellationId has already been used");
|
|
110
|
+
controller = new AbortController();
|
|
111
|
+
const settled = new Promise((resolve) => { settle = resolve; });
|
|
112
|
+
foreground.set(cancellationId, { controller, settled });
|
|
113
|
+
unlink = linkAbort(context.signal, controller);
|
|
114
|
+
}
|
|
64
115
|
await assertToolRequest(tool, request);
|
|
65
116
|
let response;
|
|
66
117
|
switch (tool) {
|
|
@@ -91,12 +142,21 @@ export function createToolDispatcher(dependencies = {}) {
|
|
|
91
142
|
response = validation.ok ? { status: "valid" } : { status: "invalid", errors: validation.errors };
|
|
92
143
|
break;
|
|
93
144
|
}
|
|
94
|
-
case "stratum_plan":
|
|
95
|
-
|
|
145
|
+
case "stratum_plan": {
|
|
146
|
+
const policyStepSelector = optionalString(request, "policy_step_selector");
|
|
147
|
+
response = await engine.plan(request.spec, request.input, {
|
|
148
|
+
...option(request, "workspaceRoot"),
|
|
149
|
+
...(request.policy_bundle !== undefined ? { policyBundle: record(request, "policy_bundle") } : {}),
|
|
150
|
+
...(policyStepSelector !== undefined ? { policyStepSelector } : {}),
|
|
151
|
+
});
|
|
96
152
|
break;
|
|
153
|
+
}
|
|
97
154
|
case "stratum_step_done":
|
|
98
155
|
response = await engine.stepDone(string(request, "runId"), string(request, "stepId"), record(request, "result"), string(request, "dispatchToken"));
|
|
99
156
|
break;
|
|
157
|
+
case "stratum_usage_report":
|
|
158
|
+
response = await engine.usageReport(string(request, "runId"), record(request, "receipt"));
|
|
159
|
+
break;
|
|
100
160
|
case "stratum_commit":
|
|
101
161
|
response = { ...await engine.commit(string(request, "flow_id"), string(request, "label")) };
|
|
102
162
|
break;
|
|
@@ -110,7 +170,7 @@ export function createToolDispatcher(dependencies = {}) {
|
|
|
110
170
|
response = auditResponse(await engine.audit(string(request, "runId")));
|
|
111
171
|
break;
|
|
112
172
|
case "stratum_gate_resolve":
|
|
113
|
-
response = await engine.gateResolve(string(request, "runId"), string(request, "stepId"), string(request, "decision"), string(request, "gateToken"));
|
|
173
|
+
response = await engine.gateResolve(string(request, "runId"), string(request, "stepId"), string(request, "decision"), string(request, "gateToken"), optionalString(request, "user_id"));
|
|
114
174
|
break;
|
|
115
175
|
case "stratum_flow_poll":
|
|
116
176
|
response = flowPollResponse(await engine.flowPoll(string(request, "runId"), optionalNumber(request, "cursor")));
|
|
@@ -138,6 +198,7 @@ export function createToolDispatcher(dependencies = {}) {
|
|
|
138
198
|
: undefined;
|
|
139
199
|
const executed = await agentRun({
|
|
140
200
|
agent: string(request, "agent"),
|
|
201
|
+
...(cancellationId !== undefined ? { ownProcessGroup: true } : {}),
|
|
141
202
|
prompt: string(request, "prompt"),
|
|
142
203
|
cwd: string(request, "cwd"),
|
|
143
204
|
// Presence-based forwarding, NOT truthiness: sandboxMode:"" must reach
|
|
@@ -150,37 +211,73 @@ export function createToolDispatcher(dependencies = {}) {
|
|
|
150
211
|
...(allowedTools !== undefined ? { allowedTools } : {}),
|
|
151
212
|
...(disallowedTools !== undefined ? { disallowedTools } : {}),
|
|
152
213
|
...(context.onAgentEvent !== undefined ? { onEvent: context.onAgentEvent } : {}),
|
|
214
|
+
...((controller?.signal ?? context.signal) !== undefined ? { signal: (controller?.signal ?? context.signal) } : {}),
|
|
215
|
+
...(request.thinking !== undefined ? { thinking: record(request, "thinking") } : {}),
|
|
216
|
+
...(request.effort !== undefined ? { effort: string(request, "effort") } : {}),
|
|
153
217
|
});
|
|
218
|
+
unlink?.(); // Provider finished; a late disconnect cannot cancel a completed run.
|
|
219
|
+
succeeded = true;
|
|
154
220
|
response = "status" in executed ? { ...executed } : { status: "complete", ...executed };
|
|
155
221
|
break;
|
|
156
222
|
}
|
|
157
223
|
case "stratum_agent_poll":
|
|
158
224
|
response = await agentPoll(string(request, "runId"));
|
|
159
225
|
break;
|
|
160
|
-
case "stratum_cancel_agent_run":
|
|
161
|
-
|
|
226
|
+
case "stratum_cancel_agent_run": {
|
|
227
|
+
const runId = string(request, "runId");
|
|
228
|
+
const running = foreground.get(runId);
|
|
229
|
+
if (running) {
|
|
230
|
+
running.controller.abort(new Error("Foreground agent run cancelled"));
|
|
231
|
+
// Acknowledge only after the connector has finished teardown.
|
|
232
|
+
await teardownDeadline(running.settled, dependencies.cancellationTimeoutMs ?? Number(process.env.STRATUM_CANCEL_TIMEOUT_MS ?? 15000), "Foreground connector teardown did not settle");
|
|
233
|
+
const outcome = completed.get(runId);
|
|
234
|
+
if (outcome instanceof Error)
|
|
235
|
+
throw outcome;
|
|
236
|
+
response = { status: "cancelled", runId };
|
|
237
|
+
}
|
|
238
|
+
else if (completed.has(runId)) {
|
|
239
|
+
const outcome = completed.get(runId);
|
|
240
|
+
if (outcome instanceof Error)
|
|
241
|
+
throw outcome;
|
|
242
|
+
response = { status: outcome, runId };
|
|
243
|
+
}
|
|
244
|
+
else
|
|
245
|
+
response = await agentCancel(runId);
|
|
162
246
|
break;
|
|
247
|
+
}
|
|
163
248
|
case "stratum_guard_register": {
|
|
164
249
|
const { registerGuard } = await import("../guard/transition.js");
|
|
165
|
-
response = await registerGuard(string(request, "resource_id"), record(request, "graph"), record(request, "edge_predicates"), string(request, "initial"), optionalArray(request, "terminal"), optionalRecord(request, "stakes"), optionalString(request, "workspace_root") ?? null);
|
|
250
|
+
response = await registerGuard(string(request, "resource_id"), record(request, "graph"), record(request, "edge_predicates"), string(request, "initial"), optionalArray(request, "terminal"), optionalRecord(request, "stakes"), optionalString(request, "workspace_root") ?? null, request.policy_bundle !== undefined ? record(request, "policy_bundle") : undefined);
|
|
166
251
|
break;
|
|
167
252
|
}
|
|
168
253
|
case "stratum_guard_transition": {
|
|
169
254
|
const { guardTransition } = await import("../guard/transition.js");
|
|
255
|
+
const runId = optionalString(request, "run_id");
|
|
170
256
|
response = await guardTransition(string(request, "resource_id"), string(request, "from_state"), string(request, "to_state"), {
|
|
171
257
|
artifacts: record(request, "artifacts"), modifiedFiles: optionalArray(request, "modified_files"), idempotencyKey: optionalString(request, "idempotency_key") ?? null, resolvedBy: optionalString(request, "resolved_by") ?? "agent",
|
|
258
|
+
...(runId !== undefined ? { runId } : {}),
|
|
172
259
|
...(dependencies.guardJudge !== undefined ? { judge: dependencies.guardJudge } : {}),
|
|
173
260
|
});
|
|
174
261
|
break;
|
|
175
262
|
}
|
|
176
263
|
case "stratum_guard_override": {
|
|
177
264
|
const { guardOverride } = await import("../guard/transition.js");
|
|
178
|
-
response = await guardOverride(string(request, "resource_id"), string(request, "from_state"), string(request, "to_state"), string(request, "
|
|
265
|
+
response = await guardOverride(string(request, "resource_id"), string(request, "from_state"), string(request, "to_state"), string(request, "authorization"), string(request, "rationale"), optionalString(request, "resolved_by") ?? "human", optionalString(request, "user_id"), optionalString(request, "run_id"));
|
|
179
266
|
break;
|
|
180
267
|
}
|
|
181
268
|
case "stratum_guard_migrate": {
|
|
182
269
|
const { guardMigrate } = await import("../guard/transition.js");
|
|
183
|
-
response = await guardMigrate(string(request, "resource_id"), record(request, "new_graph"), record(request, "new_edge_predicates"), string(request, "
|
|
270
|
+
response = await guardMigrate(string(request, "resource_id"), record(request, "new_graph"), record(request, "new_edge_predicates"), string(request, "authorization"), string(request, "rationale"), optionalArray(request, "new_terminal"), optionalRecord(request, "new_stakes"));
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
case "stratum_guard_upgrade": {
|
|
274
|
+
const { guardUpgrade } = await import("../guard/transition.js");
|
|
275
|
+
response = await guardUpgrade(string(request, "resource_id"), record(request, "new_graph"), record(request, "new_edge_predicates"), string(request, "rationale"), optionalArray(request, "new_terminal"), optionalRecord(request, "new_stakes"));
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
case "stratum_guard_apply_upgrade": {
|
|
279
|
+
const { guardApplyUpgrade } = await import("../guard/transition.js");
|
|
280
|
+
response = await guardApplyUpgrade(string(request, "resource_id"), string(request, "descriptor_id"));
|
|
184
281
|
break;
|
|
185
282
|
}
|
|
186
283
|
case "stratum_guard_history": {
|
|
@@ -194,31 +291,53 @@ export function createToolDispatcher(dependencies = {}) {
|
|
|
194
291
|
await assertEvent(event);
|
|
195
292
|
}
|
|
196
293
|
await assertToolResponse(tool, response);
|
|
294
|
+
succeeded = true;
|
|
197
295
|
return response;
|
|
198
296
|
}
|
|
199
297
|
catch (error) {
|
|
298
|
+
if (error instanceof Error && "code" in error
|
|
299
|
+
&& ["CANCELLATION_TEARDOWN_TIMEOUT", "CANCELLATION_UNCONFIRMED"].includes(String(error.code))) {
|
|
300
|
+
teardownFailure = error;
|
|
301
|
+
}
|
|
200
302
|
if (error instanceof SpecValidationError) {
|
|
201
303
|
const code = tool === "stratum_flow_run_bg"
|
|
202
304
|
&& error.errors.some((entry) => entry.code === "consumer_dispatch_bg_unsupported")
|
|
203
|
-
? "consumer_dispatch_bg_unsupported" : "spec_validation_failed";
|
|
204
|
-
|
|
205
|
-
const declaration = (await mcpSurface()).errors[code];
|
|
206
|
-
if (!declaration)
|
|
207
|
-
throw new Error(`MCP error registry is missing ${code}`);
|
|
208
|
-
assertShape(data, declaration.data, `errors.${code}.data`);
|
|
209
|
-
throw new McpError(ErrorCode.InvalidParams, error.message, data);
|
|
305
|
+
? "consumer_dispatch_bg_unsupported" : error instanceof InputValidationError ? "input_validation_failed" : "spec_validation_failed";
|
|
306
|
+
throw await registryError(code, ErrorCode.InvalidParams, error.message, { code, errors: error.errors });
|
|
210
307
|
}
|
|
211
308
|
if ((tool === "stratum_commit" || tool === "stratum_revert") && error instanceof CheckpointOperationError) {
|
|
212
309
|
const response = checkpointErrorEnvelope(error);
|
|
213
310
|
await assertToolResponse(tool, response);
|
|
214
311
|
return response;
|
|
215
312
|
}
|
|
313
|
+
if (tool === "stratum_agent_run" || tool === "stratum_cancel_agent_run") {
|
|
314
|
+
// An McpError already carries a declared code and payload — rewrapping it
|
|
315
|
+
// as agent_run_failed would replace a precise contract error (a rejected
|
|
316
|
+
// request field, an undeclared response) with a generic provider failure.
|
|
317
|
+
if (error instanceof McpError)
|
|
318
|
+
throw error;
|
|
319
|
+
const failure = error;
|
|
320
|
+
// The envelope is always agent_run_failed; `code` names the specific
|
|
321
|
+
// connector failure (CANCELLATION_TEARDOWN_TIMEOUT and friends) when it has one.
|
|
322
|
+
const data = { code: failure.code ?? "agent_run_failed", ...Object.fromEntries(["usage", "split", "usdSource", "stderr", "telemetry"].filter(key => key in Object(failure)).map(key => [key, failure[key]])) };
|
|
323
|
+
throw await registryError("agent_run_failed", ErrorCode.InternalError, failure.message ?? String(error), data);
|
|
324
|
+
}
|
|
216
325
|
if (!tool.startsWith("stratum_guard_"))
|
|
217
326
|
throw error;
|
|
218
327
|
const response = guardErrorEnvelope(error);
|
|
219
328
|
await assertToolResponse(tool, response);
|
|
220
329
|
return response;
|
|
221
330
|
}
|
|
331
|
+
finally {
|
|
332
|
+
unlink?.();
|
|
333
|
+
if (cancellationId !== undefined && controller) {
|
|
334
|
+
completed.set(cancellationId, teardownFailure ?? (succeeded ? "already_complete" : controller.signal.aborted ? "cancelled" : "already_error"));
|
|
335
|
+
foreground.delete(cancellationId);
|
|
336
|
+
if (completed.size > 1024)
|
|
337
|
+
completed.delete(completed.keys().next().value);
|
|
338
|
+
settle?.();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
222
341
|
},
|
|
223
342
|
};
|
|
224
343
|
}
|
|
@@ -257,20 +376,29 @@ export async function createMcpServer(dependencies = {}) {
|
|
|
257
376
|
heartbeat.unref?.();
|
|
258
377
|
}
|
|
259
378
|
try {
|
|
260
|
-
let context;
|
|
379
|
+
let context = { signal: extra.signal };
|
|
261
380
|
if (request.params.name === "stratum_agent_run" && progressToken !== undefined) {
|
|
262
|
-
|
|
381
|
+
// No flow_id: an agent run has no flow, and a server-invented UUID cannot
|
|
382
|
+
// match any consumer's correlation id — compose dropped EVERY agent event
|
|
383
|
+
// as "misrouted" while it was stamped (2026-08-30 census). Progress
|
|
384
|
+
// notifications are already scoped to this call by progressToken; the
|
|
385
|
+
// consumer stamps its own correlation id on an absent flow_id.
|
|
263
386
|
let eventSeq = 0;
|
|
264
387
|
context = {
|
|
388
|
+
signal: extra.signal,
|
|
265
389
|
onAgentEvent: async (event) => {
|
|
266
390
|
const message = JSON.stringify({
|
|
267
|
-
|
|
268
|
-
|
|
391
|
+
// 0.2.8: flow_id is OPTIONAL on `_agent_run` envelopes (call-local).
|
|
392
|
+
schema_version: "0.2.8",
|
|
269
393
|
step_id: "_agent_run",
|
|
270
394
|
seq: eventSeq,
|
|
271
395
|
ts: new Date().toISOString(),
|
|
272
396
|
kind: event.kind,
|
|
273
|
-
|
|
397
|
+
// Agent connector events are call-local; stamp the same step
|
|
398
|
+
// identity in usage metadata required by the BuildStreamEvent contract.
|
|
399
|
+
metadata: event.kind === "step_usage"
|
|
400
|
+
? { ...event.metadata, stepId: "_agent_run" }
|
|
401
|
+
: event.metadata,
|
|
274
402
|
reply_required: false,
|
|
275
403
|
});
|
|
276
404
|
eventSeq += 1;
|