@tangle-network/agent-runtime 0.219.0 → 0.220.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/agent.d.ts +1 -1
- package/dist/agent.js +1 -1
- package/dist/{coordination-driver-CvinSUI-.js → coordination-driver-DwgDZ6nf.js} +598 -354
- package/dist/coordination-driver-DwgDZ6nf.js.map +1 -0
- package/dist/{delegate-BH8r8hkQ.js → delegate-BYMK7jGe.js} +2 -2
- package/dist/{delegate-BH8r8hkQ.js.map → delegate-BYMK7jGe.js.map} +1 -1
- package/dist/durable.d.ts +1 -1
- package/dist/durable.js +1 -1
- package/dist/{graph-5N4gLnOt.js → graph-DHx2fTiH.js} +2 -2
- package/dist/{graph-5N4gLnOt.js.map → graph-DHx2fTiH.js.map} +1 -1
- package/dist/{index-CLDpu-bs.d.ts → index-B9_U4zYI.d.ts} +16 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/kernel.d.ts +1 -1
- package/dist/kernel.js +6 -6
- package/dist/{loop-runner-bin-Cn9DBrLv.d.ts → loop-runner-bin-BaeOlnpi.d.ts} +2 -2
- package/dist/{loop-runner-bin-CTEJAcac.js → loop-runner-bin-eB3kQCi8.js} +2 -2
- package/dist/{loop-runner-bin-CTEJAcac.js.map → loop-runner-bin-eB3kQCi8.js.map} +1 -1
- package/dist/loop-runner-bin.d.ts +1 -1
- package/dist/loop-runner-bin.js +1 -1
- package/dist/mcp/bin.js +2 -2
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +3 -3
- package/dist/{provision-supervisor-40EOGn14.js → provision-supervisor-CJ8EkREK.js} +2 -2
- package/dist/{provision-supervisor-40EOGn14.js.map → provision-supervisor-CJ8EkREK.js.map} +1 -1
- package/dist/{runtime-BDK_GOAd.js → runtime-DMRwKO-Y.js} +6 -6
- package/dist/{runtime-BDK_GOAd.js.map → runtime-DMRwKO-Y.js.map} +1 -1
- package/dist/{server-DuvbSAVA.js → server-1WLTlddl.js} +2 -2
- package/dist/{server-DuvbSAVA.js.map → server-1WLTlddl.js.map} +1 -1
- package/dist/{supervise-W_mtzswa.js → supervise-DRDPNM81.js} +436 -418
- package/dist/supervise-DRDPNM81.js.map +1 -0
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +11 -11
- package/dist/tui/index.d.ts +1 -1
- package/dist/tui/index.js +1 -1
- package/package.json +1 -1
- package/dist/coordination-driver-CvinSUI-.js.map +0 -1
- package/dist/supervise-W_mtzswa.js.map +0 -1
|
@@ -2,9 +2,9 @@ import { $ as createExecutor, $r as attestRuntimeOwnedScopeOwner, A as scopeReta
|
|
|
2
2
|
import { f as RuntimeRunStateError, i as ConfigError, m as ValidationError, r as BackendTransportError, t as AgentEvalError } from "./errors-DodWX-cb.js";
|
|
3
3
|
import { a as concreteProfileModel, c as profileModelExecutionSettings, d as agentHarness, f as harnessRunsAgent, n as assertModelAllowed, o as enforceTokenLimits, r as assertProfileModelsAllowed, s as profileBridgeWireModel, t as assertExecutableAgentProfile } from "./model-policy-DKDyr-fc.js";
|
|
4
4
|
import { t as composeRuntimeHooks } from "./runtime-hooks-tXpAarhW.js";
|
|
5
|
-
import { k as writeRunCancellation, o as readRunCancelRequest, s as readRunCancellation } from "./run-layout-hZREkcfu.js";
|
|
6
|
-
import { C as createCoordinationTools, F as watchRunCancellation, M as createInMemoryRunContext, P as applyRunCancellation, S as coordinationVerbNames, c as createProgressTracker, d as progressStop, j as createFileRunContext, r as driverAgent, y as DEFAULT_AWAIT_EVENT_TIMEOUT_MS } from "./coordination-driver-CvinSUI-.js";
|
|
7
5
|
import { t as createStdioToolServer } from "./tool-server-BJbCPhoW.js";
|
|
6
|
+
import { D as coordinationVerbNames, O as createCoordinationTools, S as watchRunCancellation, c as createProgressTracker, d as progressStop, r as driverAgent, v as createFileRunContext, w as DEFAULT_AWAIT_EVENT_TIMEOUT_MS, x as applyRunCancellation, y as createInMemoryRunContext } from "./coordination-driver-DwgDZ6nf.js";
|
|
7
|
+
import { k as writeRunCancellation, o as readRunCancelRequest, s as readRunCancellation } from "./run-layout-hZREkcfu.js";
|
|
8
8
|
import { agentProfileSchema, canonicalAgentProfileDigest, canonicalCandidateDigest, canonicalCandidateJson, validateAgentProfileSecurity } from "@tangle-network/agent-interface";
|
|
9
9
|
import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
10
10
|
import { resolve } from "node:path";
|
|
@@ -142,330 +142,16 @@ function mapExecutorResult(inner, map) {
|
|
|
142
142
|
await settle(inner.resultArtifact(), task);
|
|
143
143
|
})();
|
|
144
144
|
return (async () => settle(await execution, task))();
|
|
145
|
-
},
|
|
146
|
-
teardown: (grace) => inner.teardown(grace),
|
|
147
|
-
resultArtifact() {
|
|
148
|
-
if (!mapped) throw new Error("mapExecutorResult: resultArtifact() read before execute()");
|
|
149
|
-
return mapped;
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
function isAsyncIterable$2(v) {
|
|
154
|
-
return v != null && typeof v[Symbol.asyncIterator] === "function";
|
|
155
|
-
}
|
|
156
|
-
//#endregion
|
|
157
|
-
//#region src/runtime/supervise/otel-spans.ts
|
|
158
|
-
/**
|
|
159
|
-
* Supervisor tree → OTLP spans. OPT-IN, off by default.
|
|
160
|
-
*
|
|
161
|
-
* WHY. A supervised tree is legible today only by parsing this package's own spawn journal, so
|
|
162
|
-
* every other multi-agent shape on the machine (a coding-CLI's subagents, a pi fanout, ad-hoc tool
|
|
163
|
-
* parallelism) needs its own bespoke reader. A span carrying `parent_span_id` IS a tree, and any
|
|
164
|
-
* system can emit one — so emitting spans makes the supervisor readable by the same viewer as
|
|
165
|
-
* everything else, with no per-system reader.
|
|
166
|
-
*
|
|
167
|
-
* WHAT IT IS NOT. This is telemetry, never the record of truth. The spawn journal remains the sole
|
|
168
|
-
* durable ledger for replay/resume and for cost; nothing here is read back, and a run whose export
|
|
169
|
-
* fails is unaffected in every observable way. The two data models are deliberately separate.
|
|
170
|
-
*
|
|
171
|
-
* HOW IT ATTACHES. This is a pure `RuntimeHooks` observer over the lifecycle events `Scope` ALREADY
|
|
172
|
-
* emits — `agent.spawn` (a node opened), `agent.child` (a node settled), `agent.turn` (a driver
|
|
173
|
-
* inference turn was metered). It adds no event, mutates no journal, and changes no result. Because
|
|
174
|
-
* `Scope` re-seeds the same hooks into every nested scope (`makeNestedScopeSeam`), one observer sees
|
|
175
|
-
* the WHOLE recursion at arbitrary depth.
|
|
176
|
-
*
|
|
177
|
-
* SPAN SHAPE. One span per supervised node, opened at spawn and closed at settle, parented to its
|
|
178
|
-
* parent node's span; the run root is the trace. Driver inference rides as an `LLM` child span under
|
|
179
|
-
* the node that metered it. Attributes reuse the vocabulary the rest of the stack already reads
|
|
180
|
-
* (`openinference.span.kind`, `agent.name`, `llm.token_count.*`, `llm.cost_usd`, `tangle.cost.usd`)
|
|
181
|
-
* — see `@tangle-network/agent-eval`'s `src/trace/attribute-vocabulary.ts`, which is the consumer.
|
|
182
|
-
*
|
|
183
|
-
* UNKNOWN IS NEVER ZERO. `Spend.tokensKnown === false` / `usdKnown === false` mark work that
|
|
184
|
-
* HAPPENED with an unreported count. Those spans OMIT the token/cost attribute entirely and set
|
|
185
|
-
* `tangle.supervise.tokens_known` / `tangle.supervise.cost_known` to `false`, so a reader can never
|
|
186
|
-
* mistake an unmeasured turn for a free one.
|
|
187
|
-
*/
|
|
188
|
-
/** OTEL status codes (`UNSET` / `OK` / `ERROR`) — the numeric wire values `OtelSpan.status` carries. */
|
|
189
|
-
const STATUS_UNSET = 0;
|
|
190
|
-
const STATUS_OK = 1;
|
|
191
|
-
const STATUS_ERROR = 2;
|
|
192
|
-
/** Longest string attribute value written from free-form detail, so an oversized turn payload
|
|
193
|
-
* cannot inflate a span. Identity/label attributes we control are never truncated. */
|
|
194
|
-
const MAX_DETAIL_CHARS = 256;
|
|
195
|
-
/**
|
|
196
|
-
* Build the span recorder for one supervised run, or `undefined` when no exporter resolves — the
|
|
197
|
-
* off-by-default path. A run that passes no `exporter` and no `exportConfig` never reaches this
|
|
198
|
-
* function at all; one that passes an `exportConfig` with no endpoint (and no env endpoint) gets
|
|
199
|
-
* `undefined` here, so "configured but unreachable" also costs nothing.
|
|
200
|
-
*/
|
|
201
|
-
function createSupervisorSpanRecorder(opts) {
|
|
202
|
-
const exporter = opts.exporter ?? createOtelExporter(opts.exportConfig);
|
|
203
|
-
if (!exporter) return void 0;
|
|
204
|
-
const ownsExporter = opts.exporter === void 0;
|
|
205
|
-
const now = opts.now ?? Date.now;
|
|
206
|
-
const traceId = normalizeTraceId(opts.traceId, opts.runId);
|
|
207
|
-
const rootSpanId = generateSpanId();
|
|
208
|
-
const rootStartMs = now();
|
|
209
|
-
const base = {
|
|
210
|
-
"tangle.run.id": opts.runId,
|
|
211
|
-
"tangle.sessionId": opts.runId,
|
|
212
|
-
...opts.attributes ?? {}
|
|
213
|
-
};
|
|
214
|
-
/** Node id → its open span. Seeded with the run id ⇒ the root span, because a depth-0 spawn's
|
|
215
|
-
* `parentId` is the run id itself and every deeper spawn's is a real node id. */
|
|
216
|
-
const open = /* @__PURE__ */ new Map();
|
|
217
|
-
const spanIdOf = /* @__PURE__ */ new Map([[opts.runId, rootSpanId]]);
|
|
218
|
-
let finished = false;
|
|
219
|
-
/** Every export is best-effort: a throwing exporter must never reach the run. */
|
|
220
|
-
const emit = (span) => {
|
|
221
|
-
try {
|
|
222
|
-
exporter.exportSpan(span);
|
|
223
|
-
} catch {}
|
|
224
|
-
};
|
|
225
|
-
const span = (spanId, parentSpanId, name, startMs, endMs, attrs, status, message) => ({
|
|
226
|
-
traceId,
|
|
227
|
-
spanId,
|
|
228
|
-
...parentSpanId ? { parentSpanId } : {},
|
|
229
|
-
name,
|
|
230
|
-
kind: 1,
|
|
231
|
-
startTimeUnixNano: msToNano(startMs),
|
|
232
|
-
endTimeUnixNano: msToNano(Math.max(startMs, endMs)),
|
|
233
|
-
attributes: toOtelAttributes(attrs),
|
|
234
|
-
status: {
|
|
235
|
-
code: status,
|
|
236
|
-
...message ? { message } : {}
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
function onSpawn(event) {
|
|
240
|
-
const p = record(event.payload);
|
|
241
|
-
const childId = str(p.childId);
|
|
242
|
-
if (!childId) return;
|
|
243
|
-
const label = str(p.label) ?? "node";
|
|
244
|
-
const runtime = str(p.runtime);
|
|
245
|
-
const isWait = runtime === "wait";
|
|
246
|
-
const attrs = {
|
|
247
|
-
...base,
|
|
248
|
-
"openinference.span.kind": isWait ? "CHAIN" : "AGENT",
|
|
249
|
-
"agent.name": label,
|
|
250
|
-
"tangle.supervise.node.id": childId,
|
|
251
|
-
"tangle.supervise.node.label": label,
|
|
252
|
-
"tangle.supervise.node.kind": isWait ? "wait" : "agent",
|
|
253
|
-
"tangle.supervise.tree.root": event.runId
|
|
254
|
-
};
|
|
255
|
-
if (event.parentId) attrs["tangle.supervise.node.parent_id"] = event.parentId;
|
|
256
|
-
if (runtime) attrs["tangle.supervise.node.runtime"] = runtime;
|
|
257
|
-
if (typeof p.depth === "number") attrs["tangle.supervise.node.depth"] = p.depth;
|
|
258
|
-
if (typeof event.stepIndex === "number") attrs["tangle.supervise.node.ordinal"] = event.stepIndex;
|
|
259
|
-
if (p.resumed === true) attrs["tangle.supervise.node.resumed"] = true;
|
|
260
|
-
assignBudget(attrs, p.budget);
|
|
261
|
-
const spanId = generateSpanId();
|
|
262
|
-
spanIdOf.set(childId, spanId);
|
|
263
|
-
open.set(childId, {
|
|
264
|
-
spanId,
|
|
265
|
-
parentSpanId: event.parentId && spanIdOf.get(event.parentId) || rootSpanId,
|
|
266
|
-
name: label,
|
|
267
|
-
startMs: event.timestamp,
|
|
268
|
-
attrs
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
function onSettled(event) {
|
|
272
|
-
const p = record(event.payload);
|
|
273
|
-
const childId = str(p.childId);
|
|
274
|
-
if (!childId) return;
|
|
275
|
-
const node = open.get(childId);
|
|
276
|
-
if (!node) return;
|
|
277
|
-
open.delete(childId);
|
|
278
|
-
const status = str(p.status);
|
|
279
|
-
const down = status === "down";
|
|
280
|
-
const attrs = {
|
|
281
|
-
...node.attrs,
|
|
282
|
-
"tangle.supervise.node.status": status ?? "done"
|
|
283
|
-
};
|
|
284
|
-
if (typeof event.stepIndex === "number") attrs["tangle.supervise.node.seq"] = event.stepIndex;
|
|
285
|
-
if (typeof p.outRef === "string") attrs["tangle.supervise.node.out_ref"] = p.outRef;
|
|
286
|
-
if (typeof p.valid === "boolean") attrs["tangle.supervise.verdict.valid"] = p.valid;
|
|
287
|
-
if (typeof p.score === "number") attrs["tangle.supervise.verdict.score"] = p.score;
|
|
288
|
-
if (down) {
|
|
289
|
-
attrs["error.type"] = p.infra === true ? "infra" : "child-down";
|
|
290
|
-
const reason = str(p.reason);
|
|
291
|
-
if (reason) attrs["error.message"] = truncate(reason);
|
|
292
|
-
if (typeof p.infra === "boolean") attrs["tangle.supervise.node.infra"] = p.infra;
|
|
293
|
-
}
|
|
294
|
-
const wokeBy = str(record(p.wait).settled);
|
|
295
|
-
if (wokeBy) attrs["tangle.supervise.wait.settled"] = wokeBy;
|
|
296
|
-
assignSpend(attrs, p.spent);
|
|
297
|
-
emit(span(node.spanId, node.parentSpanId, node.name, node.startMs, event.timestamp, attrs, down ? STATUS_ERROR : STATUS_OK, down ? str(p.reason) ?? "child down" : void 0));
|
|
298
|
-
}
|
|
299
|
-
function onTurn(event) {
|
|
300
|
-
const p = record(event.payload);
|
|
301
|
-
const parentId = event.parentId;
|
|
302
|
-
const parentSpanId = parentId && spanIdOf.get(parentId) || rootSpanId;
|
|
303
|
-
const attrs = {
|
|
304
|
-
...base,
|
|
305
|
-
"openinference.span.kind": "LLM",
|
|
306
|
-
"inference.observation_kind": "LLM",
|
|
307
|
-
"tangle.supervise.node.kind": "inference"
|
|
308
|
-
};
|
|
309
|
-
if (parentId) attrs["tangle.supervise.node.id"] = parentId;
|
|
310
|
-
for (const [key, value] of Object.entries(p)) {
|
|
311
|
-
if (key === "spend") continue;
|
|
312
|
-
if (key === "driver" && typeof value === "string") {
|
|
313
|
-
attrs["agent.name"] = value;
|
|
314
|
-
attrs["inference.agent_name"] = value;
|
|
315
|
-
continue;
|
|
316
|
-
}
|
|
317
|
-
if (key === "model" && typeof value === "string") {
|
|
318
|
-
attrs["llm.model_name"] = value;
|
|
319
|
-
continue;
|
|
320
|
-
}
|
|
321
|
-
if (Array.isArray(value)) {
|
|
322
|
-
const scalars = value.filter((v) => typeof v === "string" || typeof v === "number");
|
|
323
|
-
if (scalars.length > 0) attrs[`tangle.supervise.turn.${key}`] = truncate(scalars.join(","));
|
|
324
|
-
continue;
|
|
325
|
-
}
|
|
326
|
-
if (typeof value === "string") attrs[`tangle.supervise.turn.${key}`] = truncate(value);
|
|
327
|
-
else if (typeof value === "number" || typeof value === "boolean") attrs[`tangle.supervise.turn.${key}`] = value;
|
|
328
|
-
}
|
|
329
|
-
const spend = assignSpend(attrs, p.spend);
|
|
330
|
-
const endMs = event.timestamp + (spend?.ms ?? 0);
|
|
331
|
-
emit(span(generateSpanId(), parentSpanId, "gen_ai.client.inference", event.timestamp, endMs, attrs, STATUS_OK));
|
|
332
|
-
}
|
|
333
|
-
return {
|
|
334
|
-
hooks: { onEvent(event) {
|
|
335
|
-
if (finished) return;
|
|
336
|
-
try {
|
|
337
|
-
if (event.phase !== "after") return;
|
|
338
|
-
if (event.target === "agent.spawn") onSpawn(event);
|
|
339
|
-
else if (event.target === "agent.child") onSettled(event);
|
|
340
|
-
else if (event.target === "agent.turn") onTurn(event);
|
|
341
|
-
} catch {}
|
|
342
|
-
} },
|
|
343
|
-
traceId,
|
|
344
|
-
rootSpanId,
|
|
345
|
-
workerTrace(spawningNodeId) {
|
|
346
|
-
return {
|
|
347
|
-
traceId,
|
|
348
|
-
parentSpanId: spanIdOf.get(spawningNodeId) ?? rootSpanId
|
|
349
|
-
};
|
|
350
|
-
},
|
|
351
|
-
async finish(outcome) {
|
|
352
|
-
if (finished) return;
|
|
353
|
-
finished = true;
|
|
354
|
-
const endMs = now();
|
|
355
|
-
try {
|
|
356
|
-
for (const [nodeId, node] of open) emit(span(node.spanId, node.parentSpanId, node.name, node.startMs, endMs, {
|
|
357
|
-
...node.attrs,
|
|
358
|
-
"tangle.supervise.node.status": "unsettled",
|
|
359
|
-
"tangle.supervise.node.settled": false,
|
|
360
|
-
"tangle.supervise.node.id": nodeId
|
|
361
|
-
}, STATUS_UNSET));
|
|
362
|
-
open.clear();
|
|
363
|
-
emit(span(rootSpanId, opts.parentSpanId, "supervisor.run", rootStartMs, endMs, rootAttrs(base, opts.agentName ?? "supervisor", outcome), rootStatus(outcome), rootMessage(outcome)));
|
|
364
|
-
await exporter.flush();
|
|
365
|
-
if (ownsExporter) await exporter.shutdown();
|
|
366
|
-
} catch {}
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
function rootAttrs(base, agentName, outcome) {
|
|
371
|
-
const attrs = {
|
|
372
|
-
...base,
|
|
373
|
-
"openinference.span.kind": "AGENT",
|
|
374
|
-
"inference.observation_kind": "AGENT",
|
|
375
|
-
"agent.name": agentName,
|
|
376
|
-
"inference.agent_name": agentName,
|
|
377
|
-
"tangle.supervise.node.kind": "root"
|
|
378
|
-
};
|
|
379
|
-
const result = outcome?.result;
|
|
380
|
-
if (result) {
|
|
381
|
-
attrs["tangle.supervise.result"] = result.kind;
|
|
382
|
-
if (result.kind === "no-winner") {
|
|
383
|
-
attrs["tangle.supervise.reason"] = result.reason;
|
|
384
|
-
attrs["tangle.supervise.down_count"] = result.downCount;
|
|
385
|
-
if (result.reason === "driver-failed") {
|
|
386
|
-
attrs["error.type"] = result.error.name;
|
|
387
|
-
attrs["error.message"] = truncate(result.error.message);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
assignSpend(attrs, result.spentTotal);
|
|
391
|
-
}
|
|
392
|
-
if (outcome?.error !== void 0) {
|
|
393
|
-
attrs["tangle.supervise.result"] = "error";
|
|
394
|
-
const err = outcome.error;
|
|
395
|
-
attrs["error.type"] = err instanceof Error ? err.name : typeof err;
|
|
396
|
-
attrs["error.message"] = truncate(err instanceof Error ? err.message : String(err));
|
|
397
|
-
}
|
|
398
|
-
return attrs;
|
|
399
|
-
}
|
|
400
|
-
function rootStatus(outcome) {
|
|
401
|
-
if (outcome?.error !== void 0) return STATUS_ERROR;
|
|
402
|
-
if (!outcome?.result) return STATUS_UNSET;
|
|
403
|
-
return outcome.result.kind === "winner" ? STATUS_OK : STATUS_ERROR;
|
|
404
|
-
}
|
|
405
|
-
function rootMessage(outcome) {
|
|
406
|
-
if (outcome?.error !== void 0) return truncate(outcome.error instanceof Error ? outcome.error.message : String(outcome.error));
|
|
407
|
-
const result = outcome?.result;
|
|
408
|
-
return result && result.kind === "no-winner" ? result.reason : void 0;
|
|
409
|
-
}
|
|
410
|
-
/**
|
|
411
|
-
* Write a `Spend` onto a span, honouring the "a missing measurement is never zero" invariant: a
|
|
412
|
-
* channel marked NOT known contributes no number at all and instead flags itself, so nothing
|
|
413
|
-
* downstream can sum an unmeasured turn as free. Returns the spend it read (for the duration).
|
|
414
|
-
*/
|
|
415
|
-
function assignSpend(attrs, value) {
|
|
416
|
-
if (!isRecord(value)) return void 0;
|
|
417
|
-
const spend = value;
|
|
418
|
-
const tokens = isRecord(spend.tokens) ? spend.tokens : void 0;
|
|
419
|
-
if (spend.tokensKnown === false) attrs["tangle.supervise.tokens_known"] = false;
|
|
420
|
-
else if (tokens) {
|
|
421
|
-
if (typeof tokens.input === "number") attrs["llm.token_count.prompt"] = tokens.input;
|
|
422
|
-
if (typeof tokens.output === "number") attrs["llm.token_count.completion"] = tokens.output;
|
|
423
|
-
}
|
|
424
|
-
if (spend.usdKnown === false) attrs["tangle.supervise.cost_known"] = false;
|
|
425
|
-
else if (typeof spend.usd === "number" && Number.isFinite(spend.usd)) {
|
|
426
|
-
attrs["llm.cost_usd"] = spend.usd;
|
|
427
|
-
attrs["tangle.cost.usd"] = spend.usd;
|
|
428
|
-
}
|
|
429
|
-
if (typeof spend.iterations === "number") attrs["tangle.supervise.iterations"] = spend.iterations;
|
|
430
|
-
if (typeof spend.ms === "number" && spend.ms > 0) attrs["tangle.supervise.duration_ms"] = spend.ms;
|
|
431
|
-
if (typeof spend.boxMinutes === "number" && Number.isFinite(spend.boxMinutes)) attrs["tangle.platform.box_minutes"] = spend.boxMinutes;
|
|
432
|
-
if (spend.boxMinutesProvenance !== void 0) {
|
|
433
|
-
attrs["tangle.platform.box_minutes_provenance"] = spend.boxMinutesProvenance;
|
|
434
|
-
attrs["tangle.platform.box_minutes_known"] = spend.boxMinutesKnown === true;
|
|
435
|
-
}
|
|
436
|
-
if (spend.tokensProvenance !== void 0) attrs["tangle.supervise.tokens_provenance"] = spend.tokensProvenance;
|
|
437
|
-
return spend;
|
|
438
|
-
}
|
|
439
|
-
function assignBudget(attrs, value) {
|
|
440
|
-
if (!isRecord(value)) return;
|
|
441
|
-
const budget = value;
|
|
442
|
-
if (typeof budget.maxTokens === "number") attrs["tangle.supervise.budget.max_tokens"] = budget.maxTokens;
|
|
443
|
-
if (typeof budget.maxIterations === "number") attrs["tangle.supervise.budget.max_iterations"] = budget.maxIterations;
|
|
444
|
-
if (typeof budget.maxUsd === "number") attrs["tangle.supervise.budget.max_usd"] = budget.maxUsd;
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* A trace id must be 32 hex characters. A caller-supplied one is used verbatim when it already is;
|
|
448
|
-
* anything else (and the default) is DERIVED from the run id by content address — deterministic, so
|
|
449
|
-
* a resumed run rejoins the trace its first process opened rather than forking a new one.
|
|
450
|
-
*/
|
|
451
|
-
function normalizeTraceId(traceId, runId) {
|
|
452
|
-
if (traceId && /^[0-9a-f]{32}$/i.test(traceId)) return traceId.toLowerCase();
|
|
453
|
-
return contentAddress(traceId ?? runId).slice(7, 39);
|
|
454
|
-
}
|
|
455
|
-
function msToNano(ms) {
|
|
456
|
-
return (BigInt(Number.isFinite(ms) ? Math.floor(ms) : 0) * 1000000n).toString();
|
|
457
|
-
}
|
|
458
|
-
function isRecord(value) {
|
|
459
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
460
|
-
}
|
|
461
|
-
function record(value) {
|
|
462
|
-
return isRecord(value) ? value : {};
|
|
463
|
-
}
|
|
464
|
-
function str(value) {
|
|
465
|
-
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
145
|
+
},
|
|
146
|
+
teardown: (grace) => inner.teardown(grace),
|
|
147
|
+
resultArtifact() {
|
|
148
|
+
if (!mapped) throw new Error("mapExecutorResult: resultArtifact() read before execute()");
|
|
149
|
+
return mapped;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
466
152
|
}
|
|
467
|
-
function
|
|
468
|
-
return
|
|
153
|
+
function isAsyncIterable$2(v) {
|
|
154
|
+
return v != null && typeof v[Symbol.asyncIterator] === "function";
|
|
469
155
|
}
|
|
470
156
|
//#endregion
|
|
471
157
|
//#region src/runtime/supervise/coordination-http.ts
|
|
@@ -904,6 +590,7 @@ async function serveCoordinationMcp(opts) {
|
|
|
904
590
|
...opts.priorAnalystDefinitions?.length ? { priorAnalystDefinitions: opts.priorAnalystDefinitions } : {},
|
|
905
591
|
...opts.preflightSpawn ? { preflightSpawn: opts.preflightSpawn } : {},
|
|
906
592
|
...opts.resolveSpawnProfile ? { resolveSpawnProfile: opts.resolveSpawnProfile } : {},
|
|
593
|
+
...opts.spawnResourceRoot ? { spawnResourceRoot: opts.spawnResourceRoot } : {},
|
|
907
594
|
...opts.peerMail ? { peerMail: typeof opts.peerMail === "object" && opts.peerMail.limits ? { limits: opts.peerMail.limits } : {} } : {}
|
|
908
595
|
});
|
|
909
596
|
await coord.ready();
|
|
@@ -978,104 +665,418 @@ async function serveCoordinationMcp(opts) {
|
|
|
978
665
|
await new Promise((resolve) => server.close(() => resolve()));
|
|
979
666
|
throw error;
|
|
980
667
|
}
|
|
981
|
-
const mailbox = coord.peerMail;
|
|
982
|
-
const mailListener = mailbox === void 0 ? void 0 : await servePeerMail(mailbox, host);
|
|
668
|
+
const mailbox = coord.peerMail;
|
|
669
|
+
const mailListener = mailbox === void 0 ? void 0 : await servePeerMail(mailbox, host);
|
|
670
|
+
return {
|
|
671
|
+
url,
|
|
672
|
+
port,
|
|
673
|
+
get headers() {
|
|
674
|
+
return headers;
|
|
675
|
+
},
|
|
676
|
+
get credentialExpiresAt() {
|
|
677
|
+
return credentialExpiresAt;
|
|
678
|
+
},
|
|
679
|
+
rotateCredential,
|
|
680
|
+
settled: () => coord.settled(),
|
|
681
|
+
submittedResult: () => coord.submittedResult(),
|
|
682
|
+
drainResolved: () => coord.drainResolved(),
|
|
683
|
+
isStopped: () => coord.isStopped(),
|
|
684
|
+
history: () => coord.history(),
|
|
685
|
+
stats: () => coord.stats(),
|
|
686
|
+
raiseFinding: (finding) => coord.raiseFinding(finding),
|
|
687
|
+
mailHistory: () => mailbox?.history() ?? [],
|
|
688
|
+
stopMailThread: (threadId) => mailbox?.stopThread(threadId) ?? false,
|
|
689
|
+
close: async () => {
|
|
690
|
+
closed = true;
|
|
691
|
+
await new Promise((resolve) => {
|
|
692
|
+
server.close(() => resolve());
|
|
693
|
+
});
|
|
694
|
+
await mailListener?.close();
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Stand up the peer-mail capability listener: one HTTP server, one secret path per worker, and on
|
|
700
|
+
* each path a tool server carrying ONLY `send_mail` / `read_mail` with that worker's identity
|
|
701
|
+
* closed over. An unknown path is a flat 404 — the path is the credential, so a request that does
|
|
702
|
+
* not present a minted one is not a client to reason with.
|
|
703
|
+
*
|
|
704
|
+
* The host is the coordination host, which the caller has already had to justify: the loopback gate
|
|
705
|
+
* above governs both listeners, and there is deliberately no way to bind mail somewhere else.
|
|
706
|
+
*/
|
|
707
|
+
async function servePeerMail(mailbox, host) {
|
|
708
|
+
const servers = /* @__PURE__ */ new Map();
|
|
709
|
+
const forCapability = (capabilityId) => {
|
|
710
|
+
const existing = servers.get(capabilityId);
|
|
711
|
+
if (existing) return existing;
|
|
712
|
+
const created = createStdioToolServer({
|
|
713
|
+
serverName: "peer-mail",
|
|
714
|
+
serverVersion: "1",
|
|
715
|
+
tools: mailbox.tools(capabilityId)
|
|
716
|
+
});
|
|
717
|
+
servers.set(capabilityId, created);
|
|
718
|
+
return created;
|
|
719
|
+
};
|
|
720
|
+
const listener = createServer((req, res) => {
|
|
721
|
+
const capabilityId = /^\/mail\/([0-9a-f]{32})$/.exec(req.url ?? "")?.[1];
|
|
722
|
+
if (req.method !== "POST" || capabilityId === void 0 || !mailbox.hasCapability(capabilityId)) {
|
|
723
|
+
res.writeHead(404).end();
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
let body = "";
|
|
727
|
+
req.on("data", (chunk) => {
|
|
728
|
+
body += chunk;
|
|
729
|
+
});
|
|
730
|
+
req.on("end", () => {
|
|
731
|
+
(async () => {
|
|
732
|
+
try {
|
|
733
|
+
const message = JSON.parse(body);
|
|
734
|
+
const response = await forCapability(capabilityId).handle(message);
|
|
735
|
+
if (response === null) {
|
|
736
|
+
res.writeHead(202).end();
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
740
|
+
res.end(JSON.stringify(response));
|
|
741
|
+
} catch (e) {
|
|
742
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
743
|
+
res.end(JSON.stringify({
|
|
744
|
+
jsonrpc: "2.0",
|
|
745
|
+
id: null,
|
|
746
|
+
error: {
|
|
747
|
+
code: -32700,
|
|
748
|
+
message: e instanceof Error ? e.message : "parse error"
|
|
749
|
+
}
|
|
750
|
+
}));
|
|
751
|
+
}
|
|
752
|
+
})();
|
|
753
|
+
});
|
|
754
|
+
});
|
|
755
|
+
const port = await new Promise((resolve, reject) => {
|
|
756
|
+
listener.once("error", reject);
|
|
757
|
+
listener.listen(0, host, () => {
|
|
758
|
+
const addr = listener.address();
|
|
759
|
+
resolve(typeof addr === "object" && addr ? addr.port : 0);
|
|
760
|
+
});
|
|
761
|
+
});
|
|
762
|
+
mailbox.setEndpoint(`http://${host}:${port}/mail`);
|
|
763
|
+
return { close: () => new Promise((resolve) => {
|
|
764
|
+
listener.close(() => resolve());
|
|
765
|
+
}) };
|
|
766
|
+
}
|
|
767
|
+
//#endregion
|
|
768
|
+
//#region src/runtime/supervise/otel-spans.ts
|
|
769
|
+
/**
|
|
770
|
+
* Supervisor tree → OTLP spans. OPT-IN, off by default.
|
|
771
|
+
*
|
|
772
|
+
* WHY. A supervised tree is legible today only by parsing this package's own spawn journal, so
|
|
773
|
+
* every other multi-agent shape on the machine (a coding-CLI's subagents, a pi fanout, ad-hoc tool
|
|
774
|
+
* parallelism) needs its own bespoke reader. A span carrying `parent_span_id` IS a tree, and any
|
|
775
|
+
* system can emit one — so emitting spans makes the supervisor readable by the same viewer as
|
|
776
|
+
* everything else, with no per-system reader.
|
|
777
|
+
*
|
|
778
|
+
* WHAT IT IS NOT. This is telemetry, never the record of truth. The spawn journal remains the sole
|
|
779
|
+
* durable ledger for replay/resume and for cost; nothing here is read back, and a run whose export
|
|
780
|
+
* fails is unaffected in every observable way. The two data models are deliberately separate.
|
|
781
|
+
*
|
|
782
|
+
* HOW IT ATTACHES. This is a pure `RuntimeHooks` observer over the lifecycle events `Scope` ALREADY
|
|
783
|
+
* emits — `agent.spawn` (a node opened), `agent.child` (a node settled), `agent.turn` (a driver
|
|
784
|
+
* inference turn was metered). It adds no event, mutates no journal, and changes no result. Because
|
|
785
|
+
* `Scope` re-seeds the same hooks into every nested scope (`makeNestedScopeSeam`), one observer sees
|
|
786
|
+
* the WHOLE recursion at arbitrary depth.
|
|
787
|
+
*
|
|
788
|
+
* SPAN SHAPE. One span per supervised node, opened at spawn and closed at settle, parented to its
|
|
789
|
+
* parent node's span; the run root is the trace. Driver inference rides as an `LLM` child span under
|
|
790
|
+
* the node that metered it. Attributes reuse the vocabulary the rest of the stack already reads
|
|
791
|
+
* (`openinference.span.kind`, `agent.name`, `llm.token_count.*`, `llm.cost_usd`, `tangle.cost.usd`)
|
|
792
|
+
* — see `@tangle-network/agent-eval`'s `src/trace/attribute-vocabulary.ts`, which is the consumer.
|
|
793
|
+
*
|
|
794
|
+
* UNKNOWN IS NEVER ZERO. `Spend.tokensKnown === false` / `usdKnown === false` mark work that
|
|
795
|
+
* HAPPENED with an unreported count. Those spans OMIT the token/cost attribute entirely and set
|
|
796
|
+
* `tangle.supervise.tokens_known` / `tangle.supervise.cost_known` to `false`, so a reader can never
|
|
797
|
+
* mistake an unmeasured turn for a free one.
|
|
798
|
+
*/
|
|
799
|
+
/** OTEL status codes (`UNSET` / `OK` / `ERROR`) — the numeric wire values `OtelSpan.status` carries. */
|
|
800
|
+
const STATUS_UNSET = 0;
|
|
801
|
+
const STATUS_OK = 1;
|
|
802
|
+
const STATUS_ERROR = 2;
|
|
803
|
+
/** Longest string attribute value written from free-form detail, so an oversized turn payload
|
|
804
|
+
* cannot inflate a span. Identity/label attributes we control are never truncated. */
|
|
805
|
+
const MAX_DETAIL_CHARS = 256;
|
|
806
|
+
/**
|
|
807
|
+
* Build the span recorder for one supervised run, or `undefined` when no exporter resolves — the
|
|
808
|
+
* off-by-default path. A run that passes no `exporter` and no `exportConfig` never reaches this
|
|
809
|
+
* function at all; one that passes an `exportConfig` with no endpoint (and no env endpoint) gets
|
|
810
|
+
* `undefined` here, so "configured but unreachable" also costs nothing.
|
|
811
|
+
*/
|
|
812
|
+
function createSupervisorSpanRecorder(opts) {
|
|
813
|
+
const exporter = opts.exporter ?? createOtelExporter(opts.exportConfig);
|
|
814
|
+
if (!exporter) return void 0;
|
|
815
|
+
const ownsExporter = opts.exporter === void 0;
|
|
816
|
+
const now = opts.now ?? Date.now;
|
|
817
|
+
const traceId = normalizeTraceId(opts.traceId, opts.runId);
|
|
818
|
+
const rootSpanId = generateSpanId();
|
|
819
|
+
const rootStartMs = now();
|
|
820
|
+
const base = {
|
|
821
|
+
"tangle.run.id": opts.runId,
|
|
822
|
+
"tangle.sessionId": opts.runId,
|
|
823
|
+
...opts.attributes ?? {}
|
|
824
|
+
};
|
|
825
|
+
/** Node id → its open span. Seeded with the run id ⇒ the root span, because a depth-0 spawn's
|
|
826
|
+
* `parentId` is the run id itself and every deeper spawn's is a real node id. */
|
|
827
|
+
const open = /* @__PURE__ */ new Map();
|
|
828
|
+
const spanIdOf = /* @__PURE__ */ new Map([[opts.runId, rootSpanId]]);
|
|
829
|
+
let finished = false;
|
|
830
|
+
/** Every export is best-effort: a throwing exporter must never reach the run. */
|
|
831
|
+
const emit = (span) => {
|
|
832
|
+
try {
|
|
833
|
+
exporter.exportSpan(span);
|
|
834
|
+
} catch {}
|
|
835
|
+
};
|
|
836
|
+
const span = (spanId, parentSpanId, name, startMs, endMs, attrs, status, message) => ({
|
|
837
|
+
traceId,
|
|
838
|
+
spanId,
|
|
839
|
+
...parentSpanId ? { parentSpanId } : {},
|
|
840
|
+
name,
|
|
841
|
+
kind: 1,
|
|
842
|
+
startTimeUnixNano: msToNano(startMs),
|
|
843
|
+
endTimeUnixNano: msToNano(Math.max(startMs, endMs)),
|
|
844
|
+
attributes: toOtelAttributes(attrs),
|
|
845
|
+
status: {
|
|
846
|
+
code: status,
|
|
847
|
+
...message ? { message } : {}
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
function onSpawn(event) {
|
|
851
|
+
const p = record(event.payload);
|
|
852
|
+
const childId = str(p.childId);
|
|
853
|
+
if (!childId) return;
|
|
854
|
+
const label = str(p.label) ?? "node";
|
|
855
|
+
const runtime = str(p.runtime);
|
|
856
|
+
const isWait = runtime === "wait";
|
|
857
|
+
const attrs = {
|
|
858
|
+
...base,
|
|
859
|
+
"openinference.span.kind": isWait ? "CHAIN" : "AGENT",
|
|
860
|
+
"agent.name": label,
|
|
861
|
+
"tangle.supervise.node.id": childId,
|
|
862
|
+
"tangle.supervise.node.label": label,
|
|
863
|
+
"tangle.supervise.node.kind": isWait ? "wait" : "agent",
|
|
864
|
+
"tangle.supervise.tree.root": event.runId
|
|
865
|
+
};
|
|
866
|
+
if (event.parentId) attrs["tangle.supervise.node.parent_id"] = event.parentId;
|
|
867
|
+
if (runtime) attrs["tangle.supervise.node.runtime"] = runtime;
|
|
868
|
+
if (typeof p.depth === "number") attrs["tangle.supervise.node.depth"] = p.depth;
|
|
869
|
+
if (typeof event.stepIndex === "number") attrs["tangle.supervise.node.ordinal"] = event.stepIndex;
|
|
870
|
+
if (p.resumed === true) attrs["tangle.supervise.node.resumed"] = true;
|
|
871
|
+
assignBudget(attrs, p.budget);
|
|
872
|
+
const spanId = generateSpanId();
|
|
873
|
+
spanIdOf.set(childId, spanId);
|
|
874
|
+
open.set(childId, {
|
|
875
|
+
spanId,
|
|
876
|
+
parentSpanId: event.parentId && spanIdOf.get(event.parentId) || rootSpanId,
|
|
877
|
+
name: label,
|
|
878
|
+
startMs: event.timestamp,
|
|
879
|
+
attrs
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
function onSettled(event) {
|
|
883
|
+
const p = record(event.payload);
|
|
884
|
+
const childId = str(p.childId);
|
|
885
|
+
if (!childId) return;
|
|
886
|
+
const node = open.get(childId);
|
|
887
|
+
if (!node) return;
|
|
888
|
+
open.delete(childId);
|
|
889
|
+
const status = str(p.status);
|
|
890
|
+
const down = status === "down";
|
|
891
|
+
const attrs = {
|
|
892
|
+
...node.attrs,
|
|
893
|
+
"tangle.supervise.node.status": status ?? "done"
|
|
894
|
+
};
|
|
895
|
+
if (typeof event.stepIndex === "number") attrs["tangle.supervise.node.seq"] = event.stepIndex;
|
|
896
|
+
if (typeof p.outRef === "string") attrs["tangle.supervise.node.out_ref"] = p.outRef;
|
|
897
|
+
if (typeof p.valid === "boolean") attrs["tangle.supervise.verdict.valid"] = p.valid;
|
|
898
|
+
if (typeof p.score === "number") attrs["tangle.supervise.verdict.score"] = p.score;
|
|
899
|
+
if (down) {
|
|
900
|
+
attrs["error.type"] = p.infra === true ? "infra" : "child-down";
|
|
901
|
+
const reason = str(p.reason);
|
|
902
|
+
if (reason) attrs["error.message"] = truncate(reason);
|
|
903
|
+
if (typeof p.infra === "boolean") attrs["tangle.supervise.node.infra"] = p.infra;
|
|
904
|
+
}
|
|
905
|
+
const wokeBy = str(record(p.wait).settled);
|
|
906
|
+
if (wokeBy) attrs["tangle.supervise.wait.settled"] = wokeBy;
|
|
907
|
+
assignSpend(attrs, p.spent);
|
|
908
|
+
emit(span(node.spanId, node.parentSpanId, node.name, node.startMs, event.timestamp, attrs, down ? STATUS_ERROR : STATUS_OK, down ? str(p.reason) ?? "child down" : void 0));
|
|
909
|
+
}
|
|
910
|
+
function onTurn(event) {
|
|
911
|
+
const p = record(event.payload);
|
|
912
|
+
const parentId = event.parentId;
|
|
913
|
+
const parentSpanId = parentId && spanIdOf.get(parentId) || rootSpanId;
|
|
914
|
+
const attrs = {
|
|
915
|
+
...base,
|
|
916
|
+
"openinference.span.kind": "LLM",
|
|
917
|
+
"inference.observation_kind": "LLM",
|
|
918
|
+
"tangle.supervise.node.kind": "inference"
|
|
919
|
+
};
|
|
920
|
+
if (parentId) attrs["tangle.supervise.node.id"] = parentId;
|
|
921
|
+
for (const [key, value] of Object.entries(p)) {
|
|
922
|
+
if (key === "spend") continue;
|
|
923
|
+
if (key === "driver" && typeof value === "string") {
|
|
924
|
+
attrs["agent.name"] = value;
|
|
925
|
+
attrs["inference.agent_name"] = value;
|
|
926
|
+
continue;
|
|
927
|
+
}
|
|
928
|
+
if (key === "model" && typeof value === "string") {
|
|
929
|
+
attrs["llm.model_name"] = value;
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
if (Array.isArray(value)) {
|
|
933
|
+
const scalars = value.filter((v) => typeof v === "string" || typeof v === "number");
|
|
934
|
+
if (scalars.length > 0) attrs[`tangle.supervise.turn.${key}`] = truncate(scalars.join(","));
|
|
935
|
+
continue;
|
|
936
|
+
}
|
|
937
|
+
if (typeof value === "string") attrs[`tangle.supervise.turn.${key}`] = truncate(value);
|
|
938
|
+
else if (typeof value === "number" || typeof value === "boolean") attrs[`tangle.supervise.turn.${key}`] = value;
|
|
939
|
+
}
|
|
940
|
+
const spend = assignSpend(attrs, p.spend);
|
|
941
|
+
const endMs = event.timestamp + (spend?.ms ?? 0);
|
|
942
|
+
emit(span(generateSpanId(), parentSpanId, "gen_ai.client.inference", event.timestamp, endMs, attrs, STATUS_OK));
|
|
943
|
+
}
|
|
983
944
|
return {
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
945
|
+
hooks: { onEvent(event) {
|
|
946
|
+
if (finished) return;
|
|
947
|
+
try {
|
|
948
|
+
if (event.phase !== "after") return;
|
|
949
|
+
if (event.target === "agent.spawn") onSpawn(event);
|
|
950
|
+
else if (event.target === "agent.child") onSettled(event);
|
|
951
|
+
else if (event.target === "agent.turn") onTurn(event);
|
|
952
|
+
} catch {}
|
|
953
|
+
} },
|
|
954
|
+
traceId,
|
|
955
|
+
rootSpanId,
|
|
956
|
+
workerTrace(spawningNodeId) {
|
|
957
|
+
return {
|
|
958
|
+
traceId,
|
|
959
|
+
parentSpanId: spanIdOf.get(spawningNodeId) ?? rootSpanId
|
|
960
|
+
};
|
|
991
961
|
},
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
962
|
+
async finish(outcome) {
|
|
963
|
+
if (finished) return;
|
|
964
|
+
finished = true;
|
|
965
|
+
const endMs = now();
|
|
966
|
+
try {
|
|
967
|
+
for (const [nodeId, node] of open) emit(span(node.spanId, node.parentSpanId, node.name, node.startMs, endMs, {
|
|
968
|
+
...node.attrs,
|
|
969
|
+
"tangle.supervise.node.status": "unsettled",
|
|
970
|
+
"tangle.supervise.node.settled": false,
|
|
971
|
+
"tangle.supervise.node.id": nodeId
|
|
972
|
+
}, STATUS_UNSET));
|
|
973
|
+
open.clear();
|
|
974
|
+
emit(span(rootSpanId, opts.parentSpanId, "supervisor.run", rootStartMs, endMs, rootAttrs(base, opts.agentName ?? "supervisor", outcome), rootStatus(outcome), rootMessage(outcome)));
|
|
975
|
+
await exporter.flush();
|
|
976
|
+
if (ownsExporter) await exporter.shutdown();
|
|
977
|
+
} catch {}
|
|
1008
978
|
}
|
|
1009
979
|
};
|
|
1010
980
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
*/
|
|
1020
|
-
async function servePeerMail(mailbox, host) {
|
|
1021
|
-
const servers = /* @__PURE__ */ new Map();
|
|
1022
|
-
const forCapability = (capabilityId) => {
|
|
1023
|
-
const existing = servers.get(capabilityId);
|
|
1024
|
-
if (existing) return existing;
|
|
1025
|
-
const created = createStdioToolServer({
|
|
1026
|
-
serverName: "peer-mail",
|
|
1027
|
-
serverVersion: "1",
|
|
1028
|
-
tools: mailbox.tools(capabilityId)
|
|
1029
|
-
});
|
|
1030
|
-
servers.set(capabilityId, created);
|
|
1031
|
-
return created;
|
|
981
|
+
function rootAttrs(base, agentName, outcome) {
|
|
982
|
+
const attrs = {
|
|
983
|
+
...base,
|
|
984
|
+
"openinference.span.kind": "AGENT",
|
|
985
|
+
"inference.observation_kind": "AGENT",
|
|
986
|
+
"agent.name": agentName,
|
|
987
|
+
"inference.agent_name": agentName,
|
|
988
|
+
"tangle.supervise.node.kind": "root"
|
|
1032
989
|
};
|
|
1033
|
-
const
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
990
|
+
const result = outcome?.result;
|
|
991
|
+
if (result) {
|
|
992
|
+
attrs["tangle.supervise.result"] = result.kind;
|
|
993
|
+
if (result.kind === "no-winner") {
|
|
994
|
+
attrs["tangle.supervise.reason"] = result.reason;
|
|
995
|
+
attrs["tangle.supervise.down_count"] = result.downCount;
|
|
996
|
+
if (result.reason === "driver-failed") {
|
|
997
|
+
attrs["error.type"] = result.error.name;
|
|
998
|
+
attrs["error.message"] = truncate(result.error.message);
|
|
999
|
+
}
|
|
1038
1000
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1001
|
+
assignSpend(attrs, result.spentTotal);
|
|
1002
|
+
}
|
|
1003
|
+
if (outcome?.error !== void 0) {
|
|
1004
|
+
attrs["tangle.supervise.result"] = "error";
|
|
1005
|
+
const err = outcome.error;
|
|
1006
|
+
attrs["error.type"] = err instanceof Error ? err.name : typeof err;
|
|
1007
|
+
attrs["error.message"] = truncate(err instanceof Error ? err.message : String(err));
|
|
1008
|
+
}
|
|
1009
|
+
return attrs;
|
|
1010
|
+
}
|
|
1011
|
+
function rootStatus(outcome) {
|
|
1012
|
+
if (outcome?.error !== void 0) return STATUS_ERROR;
|
|
1013
|
+
if (!outcome?.result) return STATUS_UNSET;
|
|
1014
|
+
return outcome.result.kind === "winner" ? STATUS_OK : STATUS_ERROR;
|
|
1015
|
+
}
|
|
1016
|
+
function rootMessage(outcome) {
|
|
1017
|
+
if (outcome?.error !== void 0) return truncate(outcome.error instanceof Error ? outcome.error.message : String(outcome.error));
|
|
1018
|
+
const result = outcome?.result;
|
|
1019
|
+
return result && result.kind === "no-winner" ? result.reason : void 0;
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Write a `Spend` onto a span, honouring the "a missing measurement is never zero" invariant: a
|
|
1023
|
+
* channel marked NOT known contributes no number at all and instead flags itself, so nothing
|
|
1024
|
+
* downstream can sum an unmeasured turn as free. Returns the spend it read (for the duration).
|
|
1025
|
+
*/
|
|
1026
|
+
function assignSpend(attrs, value) {
|
|
1027
|
+
if (!isRecord(value)) return void 0;
|
|
1028
|
+
const spend = value;
|
|
1029
|
+
const tokens = isRecord(spend.tokens) ? spend.tokens : void 0;
|
|
1030
|
+
if (spend.tokensKnown === false) attrs["tangle.supervise.tokens_known"] = false;
|
|
1031
|
+
else if (tokens) {
|
|
1032
|
+
if (typeof tokens.input === "number") attrs["llm.token_count.prompt"] = tokens.input;
|
|
1033
|
+
if (typeof tokens.output === "number") attrs["llm.token_count.completion"] = tokens.output;
|
|
1034
|
+
}
|
|
1035
|
+
if (spend.usdKnown === false) attrs["tangle.supervise.cost_known"] = false;
|
|
1036
|
+
else if (typeof spend.usd === "number" && Number.isFinite(spend.usd)) {
|
|
1037
|
+
attrs["llm.cost_usd"] = spend.usd;
|
|
1038
|
+
attrs["tangle.cost.usd"] = spend.usd;
|
|
1039
|
+
}
|
|
1040
|
+
if (typeof spend.iterations === "number") attrs["tangle.supervise.iterations"] = spend.iterations;
|
|
1041
|
+
if (typeof spend.ms === "number" && spend.ms > 0) attrs["tangle.supervise.duration_ms"] = spend.ms;
|
|
1042
|
+
if (typeof spend.boxMinutes === "number" && Number.isFinite(spend.boxMinutes)) attrs["tangle.platform.box_minutes"] = spend.boxMinutes;
|
|
1043
|
+
if (spend.boxMinutesProvenance !== void 0) {
|
|
1044
|
+
attrs["tangle.platform.box_minutes_provenance"] = spend.boxMinutesProvenance;
|
|
1045
|
+
attrs["tangle.platform.box_minutes_known"] = spend.boxMinutesKnown === true;
|
|
1046
|
+
}
|
|
1047
|
+
if (spend.tokensProvenance !== void 0) attrs["tangle.supervise.tokens_provenance"] = spend.tokensProvenance;
|
|
1048
|
+
return spend;
|
|
1049
|
+
}
|
|
1050
|
+
function assignBudget(attrs, value) {
|
|
1051
|
+
if (!isRecord(value)) return;
|
|
1052
|
+
const budget = value;
|
|
1053
|
+
if (typeof budget.maxTokens === "number") attrs["tangle.supervise.budget.max_tokens"] = budget.maxTokens;
|
|
1054
|
+
if (typeof budget.maxIterations === "number") attrs["tangle.supervise.budget.max_iterations"] = budget.maxIterations;
|
|
1055
|
+
if (typeof budget.maxUsd === "number") attrs["tangle.supervise.budget.max_usd"] = budget.maxUsd;
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* A trace id must be 32 hex characters. A caller-supplied one is used verbatim when it already is;
|
|
1059
|
+
* anything else (and the default) is DERIVED from the run id by content address — deterministic, so
|
|
1060
|
+
* a resumed run rejoins the trace its first process opened rather than forking a new one.
|
|
1061
|
+
*/
|
|
1062
|
+
function normalizeTraceId(traceId, runId) {
|
|
1063
|
+
if (traceId && /^[0-9a-f]{32}$/i.test(traceId)) return traceId.toLowerCase();
|
|
1064
|
+
return contentAddress(traceId ?? runId).slice(7, 39);
|
|
1065
|
+
}
|
|
1066
|
+
function msToNano(ms) {
|
|
1067
|
+
return (BigInt(Number.isFinite(ms) ? Math.floor(ms) : 0) * 1000000n).toString();
|
|
1068
|
+
}
|
|
1069
|
+
function isRecord(value) {
|
|
1070
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1071
|
+
}
|
|
1072
|
+
function record(value) {
|
|
1073
|
+
return isRecord(value) ? value : {};
|
|
1074
|
+
}
|
|
1075
|
+
function str(value) {
|
|
1076
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1077
|
+
}
|
|
1078
|
+
function truncate(value) {
|
|
1079
|
+
return value.length > MAX_DETAIL_CHARS ? `${value.slice(0, MAX_DETAIL_CHARS)}…` : value;
|
|
1079
1080
|
}
|
|
1080
1081
|
//#endregion
|
|
1081
1082
|
//#region src/runtime/supervise/driver-retry.ts
|
|
@@ -1621,6 +1622,7 @@ function buildSupervisorAgent(profile, deps, testBrain) {
|
|
|
1621
1622
|
...deps.continuityByProfile ? { continuityByProfile: deps.continuityByProfile } : {},
|
|
1622
1623
|
...deps.preflightSpawn ? { preflightSpawn: deps.preflightSpawn } : {},
|
|
1623
1624
|
...deps.resolveSpawnProfile ? { resolveSpawnProfile: deps.resolveSpawnProfile } : {},
|
|
1625
|
+
...deps.spawnResourceRoot ? { spawnResourceRoot: deps.spawnResourceRoot } : {},
|
|
1624
1626
|
...deps.stopRule ? { stopRule: deps.stopRule } : {},
|
|
1625
1627
|
...deps.onProgressStop ? { onProgressStop: deps.onProgressStop } : {},
|
|
1626
1628
|
...deps.maxTurns !== void 0 ? { maxTurns: deps.maxTurns } : {},
|
|
@@ -1708,6 +1710,7 @@ function buildSupervisorAgent(profile, deps, testBrain) {
|
|
|
1708
1710
|
...deps.replaySettlements ? { replaySettlements: true } : {},
|
|
1709
1711
|
...deps.preflightSpawn ? { preflightSpawn: deps.preflightSpawn } : {},
|
|
1710
1712
|
...deps.resolveSpawnProfile ? { resolveSpawnProfile: deps.resolveSpawnProfile } : {},
|
|
1713
|
+
...deps.spawnResourceRoot ? { spawnResourceRoot: deps.spawnResourceRoot } : {},
|
|
1711
1714
|
...deps.peerMail ? { peerMail: deps.peerMail } : {},
|
|
1712
1715
|
...priorCoordination?.questions.length ? { priorQuestions: priorCoordination.questions } : {},
|
|
1713
1716
|
...priorCoordination?.escalations?.length ? { priorEscalations: priorCoordination.escalations } : {},
|
|
@@ -2984,6 +2987,19 @@ function assertNoUncapturedExecutableOption(decisionData) {
|
|
|
2984
2987
|
* detached and frozen; executable ports are copied as the exact references selected at intake.
|
|
2985
2988
|
* Service internals intentionally remain live, while replacing a callback/service on the caller's
|
|
2986
2989
|
* mutable options object can no longer change an in-flight run. */
|
|
2990
|
+
/** The directory a root manager's spawn may resolve an inline resource `path` under. Only a
|
|
2991
|
+
* loopback bridge driver has one this process can read: its `cwd` is a directory on this host.
|
|
2992
|
+
* A remote bridge, a provider sandbox, or an in-process driver with no cwd yields `undefined`. */
|
|
2993
|
+
function rootSpawnResourceRoot(backend) {
|
|
2994
|
+
if (backend === void 0 || backend.backend !== "bridge" || backend.cwd === void 0) return void 0;
|
|
2995
|
+
let host;
|
|
2996
|
+
try {
|
|
2997
|
+
host = new URL(backend.bridgeUrl).hostname;
|
|
2998
|
+
} catch {
|
|
2999
|
+
return;
|
|
3000
|
+
}
|
|
3001
|
+
return isLoopbackHost(host) ? resolve(backend.cwd) : void 0;
|
|
3002
|
+
}
|
|
2987
3003
|
function captureSuperviseOptions(opts) {
|
|
2988
3004
|
assertSuperviseOptionKeys(opts, "supervise");
|
|
2989
3005
|
const { backend, coordination, driverBackend, deliverable, resolveDeliverable, router, compaction, watchWorkers, analysts, makeWorkerAgent, makeLeafAgent, resolveSpawnProfile, blobs, journal, probes, registry, hooks, otel, authorizeSpawn, authorizeMessage, driveHarness, resolveDriveHarness, resolveSupervisorTools, escalateQuestion, onCoordinationEvent, executeExtraTool, stopRule, onProgressStop, onDriverAttempt, onUnmetContract, workerRetry, onWorkerRetry, finalizer, now, signal, rootHandle, ...decisionData } = opts;
|
|
@@ -3301,6 +3317,7 @@ function superviseInternal(profile, task, opts, testBrain) {
|
|
|
3301
3317
|
const managerBackend = options.driverBackend ?? (options.rootDriverFromBackend === false ? void 0 : options.backend);
|
|
3302
3318
|
if (options.driveHarness && options.resolveDriveHarness) throw new ValidationError("supervise: provide driveHarness or resolveDriveHarness, not both");
|
|
3303
3319
|
const hasCustomDriveHarness = Boolean(options.driveHarness || options.resolveDriveHarness);
|
|
3320
|
+
const spawnResourceRoot = hasCustomDriveHarness ? void 0 : rootSpawnResourceRoot(managerBackend);
|
|
3304
3321
|
const spawnPreflight = composeSpawnPreflights(profileToolSpawnPreflight(options.makeWorkerAgent === void 0, options.resolveSupervisorTools !== void 0), coordinationChannelSpawnPreflight(options.makeWorkerAgent === void 0, hasCustomDriveHarness, managerBackend, options.coordination), options.backend?.backend === "bridge" ? bridgeSpawnPreflight(options.backend) : void 0);
|
|
3305
3322
|
const driverMaterialization = hasCustomDriveHarness ? options.driveHarnessMaterialization ?? fullProfileMaterialization : managerBackend && automaticDriverBackendSupported(managerBackend, options.coordination) ? backendProfileMaterialization(managerBackend) : void 0;
|
|
3306
3323
|
if (isExternalSupervisor(canonicalProfile) && !options.driveHarness && !options.resolveDriveHarness && (!managerBackend || !automaticDriverBackendSupported(managerBackend, options.coordination))) throw new ValidationError(`supervise: external supervisor profile.harness=${JSON.stringify(canonicalProfile.harness)} requires a local bridge, a provider with authenticated coordination.publicUrl and runtime MCP attachments, or an explicit driveHarness with reachable coordination transport`);
|
|
@@ -3529,6 +3546,7 @@ function superviseInternal(profile, task, opts, testBrain) {
|
|
|
3529
3546
|
...options.coordination && isExternalSupervisor(canonicalProfile) ? { coordination: options.coordination } : {},
|
|
3530
3547
|
...spawnPreflight ? { preflightSpawn: spawnPreflight } : {},
|
|
3531
3548
|
...options.resolveSpawnProfile ? { resolveSpawnProfile: options.resolveSpawnProfile } : {},
|
|
3549
|
+
...spawnResourceRoot === void 0 ? {} : { spawnResourceRoot },
|
|
3532
3550
|
...options.peerMail ? { peerMail: options.peerMail } : {},
|
|
3533
3551
|
...options.maxLiveWorkers !== void 0 ? { maxLiveWorkers: options.maxLiveWorkers } : {},
|
|
3534
3552
|
...options.router ? { router: options.router } : {},
|
|
@@ -3657,6 +3675,6 @@ function rootProviderModelEvidenceFromExecution(evidence) {
|
|
|
3657
3675
|
return evidence ?? rootProviderModelEvidence([]);
|
|
3658
3676
|
}
|
|
3659
3677
|
//#endregion
|
|
3660
|
-
export {
|
|
3678
|
+
export { createSupervisorSpanRecorder as _, isPreSpawnExecutorFailure as a, mapExecutorResult as b, withWorkerSpawnRetry as c, resolveSupervisorProfile as d, supervisorAgent as f, defaultUnmetContractSteer as g, classifyDriverFailure as h, workerFromBackend as i, assertCoordinationBinding as l, DriverAttemptsExhaustedError as m, supervise as n, resolveWorkerSpawnRetry as o, supervisorAgentWithTestBrain as p, superviseWithTestBrain as r, retryPreSpawnRefusals as s, DEFAULT_AUTHORED_PROFILE_SECURITY_POLICY as t, coordinationProfileToolPrefix as u, serveCoordinationMcp as v, gateOnDeliverable as y };
|
|
3661
3679
|
|
|
3662
|
-
//# sourceMappingURL=supervise-
|
|
3680
|
+
//# sourceMappingURL=supervise-DRDPNM81.js.map
|