crosscheck-mcp 0.2.27 → 0.2.29

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/embed.js CHANGED
@@ -7,16 +7,17 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
7
7
  });
8
8
 
9
9
  // src/entrypoints/embed.ts
10
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
10
11
  import { existsSync as existsSync11 } from "fs";
11
- import path11 from "path";
12
+ import path12 from "path";
12
13
  import { fileURLToPath as fileURLToPath2 } from "url";
13
14
 
14
15
  // src/core/pricing.ts
15
16
  import { readFileSync } from "fs";
16
- function loadPricing(path12) {
17
+ function loadPricing(path13) {
17
18
  let raw;
18
19
  try {
19
- raw = readFileSync(path12, "utf8");
20
+ raw = readFileSync(path13, "utf8");
20
21
  } catch {
21
22
  return {};
22
23
  }
@@ -33,20 +34,30 @@ function modelPricing(pricing, provider, model) {
33
34
  const block = pricing[provider];
34
35
  if (!block || typeof block !== "object" || Array.isArray(block)) return null;
35
36
  const table = block;
37
+ if (model.startsWith("_")) return null;
36
38
  let entry = table[model];
37
- if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
38
- const best = Object.keys(table).filter((k) => model.startsWith(k)).sort((a, b) => b.length - a.length)[0];
39
- entry = best !== void 0 ? table[best] : void 0;
39
+ let match = "exact";
40
+ if (!isRates(entry)) {
41
+ const best = Object.keys(table).filter((k) => !k.startsWith("_")).filter((k) => model.startsWith(k) && DATE_STAMP.test(model.slice(k.length))).sort((a, b) => b.length - a.length)[0];
42
+ if (best !== void 0) {
43
+ entry = table[best];
44
+ match = "stamp";
45
+ }
40
46
  }
41
- if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
47
+ if (!isRates(entry)) return null;
42
48
  const e = entry;
43
49
  return {
44
50
  prompt_per_1k: numberOrZero(e["prompt_per_1k"]),
45
51
  completion_per_1k: numberOrZero(e["completion_per_1k"]),
46
52
  cached_per_1k: numberOrZero(e["cached_per_1k"]),
47
- ...e["estimated"] === true ? { estimated: true } : {}
53
+ ...e["estimated"] === true ? { estimated: true } : {},
54
+ match
48
55
  };
49
56
  }
57
+ var DATE_STAMP = /^-\d{8}$/;
58
+ function isRates(v) {
59
+ return Boolean(v) && typeof v === "object" && !Array.isArray(v);
60
+ }
50
61
  function numberOrZero(v) {
51
62
  if (typeof v === "number" && Number.isFinite(v)) return v;
52
63
  if (typeof v === "string") {
@@ -126,8 +137,20 @@ var PROVIDER_CAPS = {
126
137
  gemini: {
127
138
  family: "gemini",
128
139
  system_role: "separate",
129
- supports_temperature: true,
130
- reasoning_prefixes: ["gemini-3.1-pro", "gemini-3.5-flash", "gemini-2.5-pro"]
140
+ // No sampling, on any Gemini model. Ignored since 3.6 Flash and rejected
141
+ // by upcoming models (Google, 2026-10). "model" means "omit for reasoning
142
+ // models", and every Gemini model is reasoning-class (below) — so this
143
+ // resolves to never, through the same rule the other providers use. The
144
+ // adapter also never sends it; this keeps the record honest for anything
145
+ // else that reads it.
146
+ supports_temperature: "model",
147
+ // EVERY Gemini model is reasoning-class. The old list named three
148
+ // families and missed gemini-3.8-flash — the DEFAULT — so the default
149
+ // model got the small non-reasoning budgets (768 tokens for an audit)
150
+ // while spending up to ~480 tokens of each call thinking. Google's docs
151
+ // say thinking cannot be switched off on current models, so a list of
152
+ // which ones think is a list that can only ever be incomplete.
153
+ reasoning_prefixes: ["gemini-"]
131
154
  },
132
155
  // Qwen's "Max" tier (qwen-v3.8, qwen-v3.7) is a standard instruct/chat
133
156
  // model, not a visible-reasoning model like QwQ — normal temperature and
@@ -250,6 +273,73 @@ function httpFailureToProviderError(provider, status, bodyText, retryAfterS, mod
250
273
  );
251
274
  }
252
275
 
276
+ // src/providers/network-errors.ts
277
+ function codeOf(e) {
278
+ if (!e || typeof e !== "object") return null;
279
+ const c = e.code;
280
+ return typeof c === "string" && c.length > 0 ? c : null;
281
+ }
282
+ function rootCause(e, depth = 0) {
283
+ if (depth > 5 || !e || typeof e !== "object") return e;
284
+ const o = e;
285
+ if (Array.isArray(o.errors) && o.errors.length > 0) {
286
+ const coded = o.errors.find((x) => codeOf(x) !== null);
287
+ return rootCause(coded ?? o.errors[0], depth + 1);
288
+ }
289
+ if (o.cause !== void 0 && o.cause !== null) return rootCause(o.cause, depth + 1);
290
+ return e;
291
+ }
292
+ var BY_CODE = {
293
+ ENOTFOUND: { cause: "dns", detail: "the provider's hostname did not resolve \u2014 DNS, or a typo'd base URL", transient: false, kind: "network" },
294
+ EAI_AGAIN: { cause: "dns", detail: "DNS lookup failed temporarily \u2014 resolver or network is flapping", transient: true, kind: "network" },
295
+ ECONNREFUSED: { cause: "refused", detail: "the connection was refused \u2014 nothing is listening, or a proxy blocked it", transient: false, kind: "network" },
296
+ ECONNRESET: { cause: "reset", detail: "the peer closed the connection mid-request \u2014 flaky path, or an upstream error behind a proxy", transient: true, kind: "network" },
297
+ EPIPE: { cause: "reset", detail: "the connection closed while we were still writing", transient: true, kind: "network" },
298
+ ETIMEDOUT: { cause: "connect_timeout", detail: "the connection timed out before it was established", transient: true, kind: "timeout" },
299
+ EHOSTUNREACH: { cause: "unreachable", detail: "no route to the provider \u2014 VPN, or offline", transient: true, kind: "network" },
300
+ ENETUNREACH: { cause: "unreachable", detail: "the network is unreachable \u2014 offline, or a routing change", transient: true, kind: "network" },
301
+ ENETDOWN: { cause: "unreachable", detail: "the local network is down", transient: true, kind: "network" },
302
+ EPROTO: { cause: "tls", detail: "the TLS handshake failed \u2014 an intercepting proxy, or a protocol mismatch", transient: false, kind: "network" },
303
+ ERR_TLS_CERT_ALTNAME_INVALID: { cause: "tls", detail: "the certificate did not match the hostname \u2014 traffic is being intercepted", transient: false, kind: "network" },
304
+ CERT_HAS_EXPIRED: { cause: "tls", detail: "the server certificate has expired, or this machine's clock is wrong", transient: false, kind: "network" },
305
+ UNABLE_TO_VERIFY_LEAF_SIGNATURE: { cause: "tls", detail: "the certificate chain could not be verified \u2014 a corporate proxy's root is probably not trusted here", transient: false, kind: "network" },
306
+ UND_ERR_CONNECT_TIMEOUT: { cause: "connect_timeout", detail: "could not open a connection within the timeout \u2014 firewall, or the host is down", transient: true, kind: "timeout" },
307
+ UND_ERR_HEADERS_TIMEOUT: { cause: "read_timeout", detail: "connected, but the provider sent no response headers in time \u2014 it is overloaded or thinking too long", transient: true, kind: "timeout" },
308
+ UND_ERR_BODY_TIMEOUT: { cause: "read_timeout", detail: "the response body stalled mid-stream", transient: true, kind: "timeout" },
309
+ UND_ERR_SOCKET: { cause: "reset", detail: "the socket closed unexpectedly", transient: true, kind: "network" },
310
+ UND_ERR_ABORTED: { cause: "aborted", detail: "the request was cancelled", transient: false, kind: "network" },
311
+ ABORT_ERR: { cause: "aborted", detail: "the request was cancelled", transient: false, kind: "network" }
312
+ };
313
+ function diagnoseNetworkError(e) {
314
+ const root = rootCause(e);
315
+ const code = codeOf(root) ?? codeOf(e);
316
+ if (code && BY_CODE[code]) return { ...BY_CODE[code], code };
317
+ const name = root && typeof root === "object" ? root.name : void 0;
318
+ if (name === "AbortError" || name === "TimeoutError") {
319
+ return {
320
+ cause: name === "TimeoutError" ? "read_timeout" : "aborted",
321
+ code: code ?? String(name),
322
+ detail: name === "TimeoutError" ? "the request exceeded its timeout" : "the request was cancelled",
323
+ transient: name === "TimeoutError",
324
+ kind: name === "TimeoutError" ? "timeout" : "network"
325
+ };
326
+ }
327
+ return {
328
+ cause: "unknown",
329
+ code,
330
+ detail: code ? `the connection failed with ${code}` : "the connection failed and the underlying error carried no code",
331
+ transient: true,
332
+ kind: "network"
333
+ };
334
+ }
335
+ function looksLikeNetworkFailure(d) {
336
+ return d.code !== null || d.cause !== "unknown";
337
+ }
338
+ function networkErrorMessage(provider, d) {
339
+ const code = d.code ? ` [${d.code}]` : "";
340
+ return `${provider}: ${d.detail}${code}`;
341
+ }
342
+
253
343
  // src/providers/retry.ts
254
344
  var override = null;
255
345
  function resolveRetryConfig() {
@@ -402,6 +492,52 @@ function rejectsDisabledThinking(model) {
402
492
  const m = model.toLowerCase();
403
493
  return ADAPTIVE_THINKING_PREFIXES.some((p) => m.startsWith(p));
404
494
  }
495
+ var NO_FORCED_TOOL_PREFIXES = [
496
+ "claude-fable-5-1",
497
+ "claude-mythos-5-1",
498
+ "claude-opus-5-5",
499
+ "claude-sonnet-5-5"
500
+ ];
501
+ function rejectsForcedToolUse(model) {
502
+ const m = model.toLowerCase();
503
+ return NO_FORCED_TOOL_PREFIXES.some((p) => m.startsWith(p));
504
+ }
505
+ var UNSUPPORTED_SCHEMA_KEYS = /* @__PURE__ */ new Set([
506
+ "minimum",
507
+ "maximum",
508
+ "exclusiveMinimum",
509
+ "exclusiveMaximum",
510
+ "multipleOf",
511
+ "minLength",
512
+ "maxLength",
513
+ "pattern",
514
+ "minItems",
515
+ "maxItems",
516
+ "uniqueItems",
517
+ "minProperties",
518
+ "maxProperties",
519
+ "$schema"
520
+ ]);
521
+ function toStructuredOutputSchema(schema) {
522
+ const walk = (node) => {
523
+ if (Array.isArray(node)) return node.map(walk);
524
+ if (!node || typeof node !== "object") return node;
525
+ const out = {};
526
+ for (const [k, v] of Object.entries(node)) {
527
+ if (UNSUPPORTED_SCHEMA_KEYS.has(k)) continue;
528
+ if (k === "properties" && v && typeof v === "object") {
529
+ out[k] = Object.fromEntries(Object.entries(v).map(([pk, pv]) => [pk, walk(pv)]));
530
+ } else if (["items", "anyOf", "allOf", "oneOf", "not", "$defs", "definitions"].includes(k)) {
531
+ out[k] = k === "$defs" || k === "definitions" ? Object.fromEntries(Object.entries(v ?? {}).map(([dk, dv]) => [dk, walk(dv)])) : walk(v);
532
+ } else {
533
+ out[k] = v;
534
+ }
535
+ }
536
+ if (out["type"] === "object" || "properties" in out) out["additionalProperties"] = false;
537
+ return out;
538
+ };
539
+ return walk(schema);
540
+ }
405
541
  var ANTHROPIC_STRUCTURED_TOOL_NAME = "structured_output";
406
542
  function applyPromptCaching(body, env = process.env) {
407
543
  const flag = (env["CROSSCHECK_ANTHROPIC_PROMPT_CACHE"] ?? "").trim();
@@ -453,7 +589,12 @@ function buildAnthropicRequest(opts) {
453
589
  body.thinking = { type: "disabled" };
454
590
  }
455
591
  }
456
- if (opts.jsonSchema) {
592
+ if (opts.jsonSchema && rejectsForcedToolUse(opts.model)) {
593
+ body.output_config = {
594
+ ...body.output_config ?? {},
595
+ format: { type: "json_schema", schema: toStructuredOutputSchema(opts.jsonSchema) }
596
+ };
597
+ } else if (opts.jsonSchema) {
457
598
  body.tools = [{
458
599
  name: ANTHROPIC_STRUCTURED_TOOL_NAME,
459
600
  description: "Emit a single JSON object matching the requested schema.",
@@ -572,7 +713,11 @@ async function sendAnthropic(args) {
572
713
  try {
573
714
  respLike = await doFetch(url, init);
574
715
  } catch (e) {
575
- throw new ProviderError("network", `anthropic: fetch failed: ${e.message}`);
716
+ const d = diagnoseNetworkError(e);
717
+ if (!looksLikeNetworkFailure(d)) throw e;
718
+ throw new ProviderError(d.kind, networkErrorMessage("anthropic", d), {
719
+ transient: d.transient
720
+ });
576
721
  }
577
722
  const status = respLike.status;
578
723
  if (status >= 200 && status < 300) {
@@ -645,6 +790,11 @@ function jsonSchemaToGeminiSchema(schema) {
645
790
  }
646
791
  return out;
647
792
  }
793
+ var GEMINI_THINKING_HEADROOM_TOKENS = 16384;
794
+ function thinkingHeadroom() {
795
+ const raw = Number(process.env["CROSSCHECK_GEMINI_THINKING_HEADROOM_TOKENS"]);
796
+ return Number.isFinite(raw) && raw >= 0 ? Math.trunc(raw) : GEMINI_THINKING_HEADROOM_TOKENS;
797
+ }
648
798
  function buildGeminiRequest(opts) {
649
799
  const contents = [];
650
800
  let systemText = null;
@@ -663,8 +813,9 @@ function buildGeminiRequest(opts) {
663
813
  const body = {
664
814
  contents,
665
815
  generationConfig: {
666
- maxOutputTokens: opts.maxTokens,
667
- temperature: opts.temperature
816
+ // Answer budget + thinking headroom. No temperature / topP / topK /
817
+ // thinkingBudget, on any model — see the header note.
818
+ maxOutputTokens: opts.maxTokens + thinkingHeadroom()
668
819
  }
669
820
  };
670
821
  if (systemText !== null && systemText !== "") {
@@ -674,8 +825,8 @@ function buildGeminiRequest(opts) {
674
825
  body.generationConfig.responseMimeType = "application/json";
675
826
  body.generationConfig.responseSchema = jsonSchemaToGeminiSchema(opts.jsonSchema);
676
827
  }
677
- const url = `${GEMINI_API_URL_BASE}/${encodeURIComponent(opts.model)}:generateContent?key=${encodeURIComponent(opts.apiKey)}`;
678
- return { url, headers: {}, body };
828
+ const url = `${GEMINI_API_URL_BASE}/${encodeURIComponent(opts.model)}:generateContent`;
829
+ return { url, headers: { "x-goog-api-key": opts.apiKey }, body };
679
830
  }
680
831
  function parseGeminiResponse(opts) {
681
832
  const r = opts.resp ?? {};
@@ -705,16 +856,19 @@ function parseGeminiResponse(opts) {
705
856
  }
706
857
  try {
707
858
  const first = cands[0];
708
- const content = first["content"];
709
- const parts = content["parts"];
859
+ const finishReason = typeof first["finishReason"] === "string" ? first["finishReason"] : void 0;
860
+ const content = first["content"] ?? {};
861
+ const parts = Array.isArray(content["parts"]) ? content["parts"] : [];
710
862
  let text = "";
711
863
  for (const p of parts) {
712
864
  if (p && typeof p === "object") {
713
- const t = p["text"];
865
+ const part = p;
866
+ if (part["thought"] === true) continue;
867
+ const t = part["text"];
714
868
  if (typeof t === "string") text += t;
715
869
  }
716
870
  }
717
- return { text, usage };
871
+ return { text, usage, ...finishReason ? { finishReason } : {} };
718
872
  } catch (e) {
719
873
  throw new ProviderError(
720
874
  "parse",
@@ -743,7 +897,8 @@ async function sendGemini(args) {
743
897
  apiKey: args.apiKey,
744
898
  messages: args.messages,
745
899
  maxTokens: args.maxTokens,
746
- temperature: args.temperature,
900
+ // args.temperature is deliberately NOT forwarded: Gemini ignores it and
901
+ // upcoming models reject it. See the header note.
747
902
  ...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
748
903
  });
749
904
  const doFetch = args.fetchImpl ?? globalThis.fetch;
@@ -758,7 +913,11 @@ async function sendGemini(args) {
758
913
  try {
759
914
  respLike = await doFetch(url, init);
760
915
  } catch (e) {
761
- throw new ProviderError("network", `gemini: fetch failed: ${e.message}`);
916
+ const d = diagnoseNetworkError(e);
917
+ if (!looksLikeNetworkFailure(d)) throw e;
918
+ throw new ProviderError(d.kind, networkErrorMessage("gemini", d), {
919
+ transient: d.transient
920
+ });
762
921
  }
763
922
  const status = respLike.status;
764
923
  if (status >= 200 && status < 300) {
@@ -768,11 +927,18 @@ async function sendGemini(args) {
768
927
  } catch (e) {
769
928
  throw new ProviderError("parse", `gemini: response body not JSON: ${e.message}`);
770
929
  }
771
- const { text, usage } = parseGeminiResponse({
930
+ const { text, usage, finishReason } = parseGeminiResponse({
772
931
  resp: parsed,
773
932
  model: args.model,
774
933
  purpose: args.purpose ?? "worker"
775
934
  });
935
+ if (text.trim() === "" && finishReason === "MAX_TOKENS") {
936
+ throw new ProviderError(
937
+ "parse",
938
+ `gemini: the answer was cut off before any visible text \u2014 thinking used the whole output budget (${usage.completion_tokens} tokens). Raise CROSSCHECK_GEMINI_THINKING_HEADROOM_TOKENS or the purpose's budget.`,
939
+ { transient: false }
940
+ );
941
+ }
776
942
  return { text, attempts: 1, usage: applyPricing2(usage, args.pricing) };
777
943
  }
778
944
  const bodyText = await respLike.text().catch(() => "");
@@ -829,7 +995,15 @@ function buildOpenAICompatibleRequest(opts) {
829
995
  json_schema: {
830
996
  name: "structured_output",
831
997
  schema: opts.jsonSchema,
832
- strict: true
998
+ // NOT strict. Strict mode requires `additionalProperties: false` on
999
+ // every object and every property listed in `required` — the
1000
+ // engine's schemas don't meet that, so every structured OpenAI call
1001
+ // (planner, judges, synthesis) was a 400, unnoticed until the canary
1002
+ // grew a structured check (2026-10-11). Non-strict still shapes the
1003
+ // reply to the schema, and structured.ts validates it against the
1004
+ // full schema regardless, so strictness added a failure mode and no
1005
+ // safety.
1006
+ strict: false
833
1007
  }
834
1008
  };
835
1009
  }
@@ -939,7 +1113,11 @@ async function sendOpenAICompatible(args) {
939
1113
  try {
940
1114
  respLike = await doFetch(url, init);
941
1115
  } catch (e) {
942
- throw new ProviderError("network", `${args.provider}: fetch failed: ${e.message}`);
1116
+ const d = diagnoseNetworkError(e);
1117
+ if (!looksLikeNetworkFailure(d)) throw e;
1118
+ throw new ProviderError(d.kind, networkErrorMessage(args.provider, d), {
1119
+ transient: d.transient
1120
+ });
943
1121
  }
944
1122
  const status = respLike.status;
945
1123
  if (status >= 200 && status < 300) {
@@ -1113,7 +1291,7 @@ import { z } from "zod";
1113
1291
 
1114
1292
  // src/server-meta.ts
1115
1293
  var SERVER_NAME = "crosscheck-agent";
1116
- var SERVER_VERSION = true ? "0.2.27" : "0.0.0-dev";
1294
+ var SERVER_VERSION = true ? "0.2.29" : "0.0.0-dev";
1117
1295
 
1118
1296
  // src/tools/audit.ts
1119
1297
  import { readdirSync, readFileSync as readFileSync3, statSync } from "fs";
@@ -1174,27 +1352,27 @@ function extractJson(text) {
1174
1352
  }
1175
1353
 
1176
1354
  // src/core/json-schema.ts
1177
- function validateSchema(value, schema, path12 = "") {
1355
+ function validateSchema(value, schema, path13 = "") {
1178
1356
  const errs = [];
1179
1357
  if ("anyOf" in schema) {
1180
1358
  const subs = schema["anyOf"].map(
1181
- (s) => validateSchema(value, s, path12)
1359
+ (s) => validateSchema(value, s, path13)
1182
1360
  );
1183
1361
  if (!subs.some((e) => e.length === 0)) {
1184
- errs.push(`${path12 || "<root>"}: did not match anyOf`);
1362
+ errs.push(`${path13 || "<root>"}: did not match anyOf`);
1185
1363
  }
1186
1364
  return errs;
1187
1365
  }
1188
1366
  if ("oneOf" in schema) {
1189
- const passed = schema["oneOf"].filter((s) => validateSchema(value, s, path12).length === 0).length;
1367
+ const passed = schema["oneOf"].filter((s) => validateSchema(value, s, path13).length === 0).length;
1190
1368
  if (passed !== 1) {
1191
- errs.push(`${path12 || "<root>"}: matched ${passed} of oneOf, expected 1`);
1369
+ errs.push(`${path13 || "<root>"}: matched ${passed} of oneOf, expected 1`);
1192
1370
  }
1193
1371
  return errs;
1194
1372
  }
1195
1373
  if ("const" in schema && !deepEqual(value, schema["const"])) {
1196
1374
  errs.push(
1197
- `${path12 || "<root>"}: expected const ${pyRepr(schema["const"])}, got ${pyRepr(value)}`
1375
+ `${path13 || "<root>"}: expected const ${pyRepr(schema["const"])}, got ${pyRepr(value)}`
1198
1376
  );
1199
1377
  return errs;
1200
1378
  }
@@ -1204,7 +1382,7 @@ function validateSchema(value, schema, path12 = "") {
1204
1382
  const ok = types.some((tt) => matchesType(value, String(tt)));
1205
1383
  if (!ok) {
1206
1384
  errs.push(
1207
- `${path12 || "<root>"}: expected type ${pyReprType(t)}, got ${pyTypeName(value)}`
1385
+ `${path13 || "<root>"}: expected type ${pyReprType(t)}, got ${pyTypeName(value)}`
1208
1386
  );
1209
1387
  return errs;
1210
1388
  }
@@ -1213,29 +1391,29 @@ function validateSchema(value, schema, path12 = "") {
1213
1391
  if ("enum" in schema) {
1214
1392
  const en = schema["enum"];
1215
1393
  if (!en.some((x) => deepEqual(x, value))) {
1216
- errs.push(`${path12 || "<root>"}: value ${pyRepr(value)} not in enum`);
1394
+ errs.push(`${path13 || "<root>"}: value ${pyRepr(value)} not in enum`);
1217
1395
  }
1218
1396
  }
1219
1397
  if ("minLength" in schema && value.length < schema["minLength"]) {
1220
- errs.push(`${path12 || "<root>"}: shorter than minLength ${schema["minLength"]}`);
1398
+ errs.push(`${path13 || "<root>"}: shorter than minLength ${schema["minLength"]}`);
1221
1399
  }
1222
1400
  }
1223
1401
  if (typeof value === "number" && !Number.isNaN(value)) {
1224
1402
  if ("minimum" in schema && value < schema["minimum"]) {
1225
- errs.push(`${path12 || "<root>"}: ${value} < minimum ${schema["minimum"]}`);
1403
+ errs.push(`${path13 || "<root>"}: ${value} < minimum ${schema["minimum"]}`);
1226
1404
  }
1227
1405
  if ("maximum" in schema && value > schema["maximum"]) {
1228
- errs.push(`${path12 || "<root>"}: ${value} > maximum ${schema["maximum"]}`);
1406
+ errs.push(`${path13 || "<root>"}: ${value} > maximum ${schema["maximum"]}`);
1229
1407
  }
1230
1408
  }
1231
1409
  if (Array.isArray(value)) {
1232
1410
  if ("minItems" in schema && value.length < schema["minItems"]) {
1233
- errs.push(`${path12 || "<root>"}: fewer items than minItems ${schema["minItems"]}`);
1411
+ errs.push(`${path13 || "<root>"}: fewer items than minItems ${schema["minItems"]}`);
1234
1412
  }
1235
1413
  const itemSchema = schema["items"];
1236
1414
  if (isObj(itemSchema)) {
1237
1415
  for (let i = 0; i < value.length; i++) {
1238
- errs.push(...validateSchema(value[i], itemSchema, `${path12}[${i}]`));
1416
+ errs.push(...validateSchema(value[i], itemSchema, `${path13}[${i}]`));
1239
1417
  }
1240
1418
  }
1241
1419
  }
@@ -1244,19 +1422,19 @@ function validateSchema(value, schema, path12 = "") {
1244
1422
  const required = schema["required"] ?? [];
1245
1423
  for (const r of required) {
1246
1424
  if (!(r in value)) {
1247
- errs.push(`${path12 || "<root>"}: missing required key ${pyRepr(r)}`);
1425
+ errs.push(`${path13 || "<root>"}: missing required key ${pyRepr(r)}`);
1248
1426
  }
1249
1427
  }
1250
1428
  if (schema["additionalProperties"] === false) {
1251
1429
  for (const k of Object.keys(value)) {
1252
1430
  if (!(k in props)) {
1253
- errs.push(`${path12 || "<root>"}: unknown key ${pyRepr(k)}`);
1431
+ errs.push(`${path13 || "<root>"}: unknown key ${pyRepr(k)}`);
1254
1432
  }
1255
1433
  }
1256
1434
  }
1257
1435
  for (const [k, v] of Object.entries(value)) {
1258
1436
  const ps = props[k];
1259
- if (ps) errs.push(...validateSchema(v, ps, path12 ? `${path12}.${k}` : k));
1437
+ if (ps) errs.push(...validateSchema(v, ps, path13 ? `${path13}.${k}` : k));
1260
1438
  }
1261
1439
  }
1262
1440
  return errs;
@@ -1336,6 +1514,31 @@ function pyReprType(t) {
1336
1514
  return String(t);
1337
1515
  }
1338
1516
 
1517
+ // src/core/session.ts
1518
+ import { randomUUID } from "crypto";
1519
+ var processSessionId = null;
1520
+ var startedAt = null;
1521
+ function engineSessionId() {
1522
+ if (processSessionId === null) {
1523
+ processSessionId = randomUUID();
1524
+ startedAt = Date.now();
1525
+ }
1526
+ return processSessionId;
1527
+ }
1528
+ function resolveSessionId(callerSupplied) {
1529
+ const trimmed = typeof callerSupplied === "string" ? callerSupplied.trim() : "";
1530
+ return trimmed.length > 0 ? trimmed : engineSessionId();
1531
+ }
1532
+ function localSessionRecordingEnabled(env = globalThis.process?.env ?? {}) {
1533
+ const v = (env["CROSSCHECK_RECORD_SESSIONS"] ?? "").trim().toLowerCase();
1534
+ return !["0", "false", "off", "no"].includes(v);
1535
+ }
1536
+ function recordingSessionId(callerSupplied) {
1537
+ if (localSessionRecordingEnabled()) return resolveSessionId(callerSupplied);
1538
+ const trimmed = typeof callerSupplied === "string" ? callerSupplied.trim() : "";
1539
+ return trimmed.length > 0 ? trimmed : null;
1540
+ }
1541
+
1339
1542
  // src/core/usage.ts
1340
1543
  function emptyUsage(provider, model, purpose = "worker") {
1341
1544
  return {
@@ -1441,11 +1644,11 @@ function withCommas(n) {
1441
1644
  function renderRunSummary(opts) {
1442
1645
  const endedAt = opts.endedAtS ?? Math.trunc(Date.now() / 1e3);
1443
1646
  let scope = "call";
1444
- let startedAt = null;
1647
+ let startedAt2 = null;
1445
1648
  let rows = [];
1446
1649
  if (opts.sessionScope && opts.sessionScope.rows.length > 0) {
1447
1650
  scope = "session";
1448
- startedAt = opts.sessionScope.startedAt;
1651
+ startedAt2 = opts.sessionScope.startedAt;
1449
1652
  rows = opts.sessionScope.rows.map((r) => ({ ...r }));
1450
1653
  } else {
1451
1654
  const byPurpose = /* @__PURE__ */ new Map();
@@ -1476,6 +1679,7 @@ function renderRunSummary(opts) {
1476
1679
  row.cpu_ms += a.cpu_ms ?? 0;
1477
1680
  if (a.cache_hit) row.cache_hits += 1;
1478
1681
  if (a.error) row.errors += 1;
1682
+ if (a.usage?.estimated) row.estimated = true;
1479
1683
  }
1480
1684
  rows = Array.from(byPurpose.values());
1481
1685
  }
@@ -1488,16 +1692,18 @@ function renderRunSummary(opts) {
1488
1692
  wall_ms: rows.reduce((s, r) => s + r.wall_ms, 0),
1489
1693
  cpu_ms: rows.reduce((s, r) => s + r.cpu_ms, 0),
1490
1694
  cache_hits: rows.reduce((s, r) => s + r.cache_hits, 0),
1491
- errors: rows.reduce((s, r) => s + r.errors, 0)
1695
+ errors: rows.reduce((s, r) => s + r.errors, 0),
1696
+ ...rows.some((r) => r.estimated) ? { estimated: true } : {}
1492
1697
  };
1493
1698
  const titleLeft = scope === "session" ? `session: ${opts.sessionId ?? ""}` : `call: ${opts.toolName}`;
1494
- const header = `${titleLeft} (${totals.calls} calls, ${withCommas(totals.total_tokens)} tokens, $${totals.cost_usd.toFixed(4)}, ${(totals.wall_ms / 1e3).toFixed(1)}s wall, ${(totals.cpu_ms / 1e3).toFixed(3)}s cpu)`;
1699
+ const costText = totals.estimated ? `$${totals.cost_usd.toFixed(4)}+ (some calls unpriced)` : `$${totals.cost_usd.toFixed(4)}`;
1700
+ const header = `${titleLeft} (${totals.calls} calls, ${withCommas(totals.total_tokens)} tokens, ${costText}, ${(totals.wall_ms / 1e3).toFixed(1)}s wall, ${(totals.cpu_ms / 1e3).toFixed(3)}s cpu)`;
1495
1701
  const bodyLines = [];
1496
1702
  for (let i = 0; i < rows.length; i++) {
1497
1703
  const r = rows[i];
1498
1704
  const glyph = i === rows.length - 1 ? "`-" : "|-";
1499
1705
  bodyLines.push(
1500
- `${glyph} purpose: ${r.purpose} (calls=${r.calls} tokens=${withCommas(r.total_tokens)} cost=$${r.cost_usd.toFixed(4)} wall=${(r.wall_ms / 1e3).toFixed(1)}s cpu=${(r.cpu_ms / 1e3).toFixed(3)}s)`
1706
+ `${glyph} purpose: ${r.purpose} (calls=${r.calls} tokens=${withCommas(r.total_tokens)} cost=$${r.cost_usd.toFixed(4)}${r.estimated ? "+" : ""} wall=${(r.wall_ms / 1e3).toFixed(1)}s cpu=${(r.cpu_ms / 1e3).toFixed(3)}s)`
1501
1707
  );
1502
1708
  }
1503
1709
  const callByModel = rollupSpendByModel(
@@ -1545,7 +1751,7 @@ function renderRunSummary(opts) {
1545
1751
  tool: opts.toolName,
1546
1752
  scope,
1547
1753
  currency: "USD",
1548
- started_at: startedAt,
1754
+ started_at: startedAt2,
1549
1755
  ended_at: endedAt,
1550
1756
  rows,
1551
1757
  totals,
@@ -1670,14 +1876,15 @@ async function attachUsageBlock(result, answers, opts) {
1670
1876
  delete result["_suppress_run_summary"];
1671
1877
  }
1672
1878
  async function appendUsageLog(storage, sessionId, toolName, answers, nowS) {
1673
- if (!sessionId || answers.length === 0) return;
1879
+ const sid = recordingSessionId(sessionId);
1880
+ if (!sid || answers.length === 0) return;
1674
1881
  const now = nowS ?? Math.trunc(Date.now() / 1e3);
1675
1882
  const rows = [];
1676
1883
  for (const a of answers) {
1677
1884
  const u = a.usage;
1678
1885
  if (!u || typeof u.provider !== "string" || u.provider === "") continue;
1679
1886
  rows.push({
1680
- session_id: sessionId,
1887
+ session_id: sid,
1681
1888
  ts: now,
1682
1889
  tool: toolName,
1683
1890
  purpose: u.purpose ?? "worker",
@@ -1700,13 +1907,14 @@ async function appendUsageLog(storage, sessionId, toolName, answers, nowS) {
1700
1907
  }
1701
1908
  }
1702
1909
  async function recordSessionCall(storage, sessionId, answers, wallMs, cpuMs, nowS) {
1703
- if (!sessionId) return;
1910
+ const sid = recordingSessionId(sessionId);
1911
+ if (!sid) return;
1704
1912
  const now = nowS ?? Math.trunc(Date.now() / 1e3);
1705
1913
  try {
1706
- let existing = await storage.getSession(sessionId);
1914
+ let existing = await storage.getSession(sid);
1707
1915
  if (!existing) {
1708
1916
  await storage.upsertSession({
1709
- session_id: sessionId,
1917
+ session_id: sid,
1710
1918
  started_at: now,
1711
1919
  last_at: now,
1712
1920
  calls: 0,
@@ -1719,7 +1927,7 @@ async function recordSessionCall(storage, sessionId, answers, wallMs, cpuMs, now
1719
1927
  total_cost_usd: 0,
1720
1928
  total_cpu_ms: 0
1721
1929
  });
1722
- existing = await storage.getSession(sessionId);
1930
+ existing = await storage.getSession(sid);
1723
1931
  }
1724
1932
  if (!existing) return;
1725
1933
  let promptDelta = 0;
@@ -1739,7 +1947,7 @@ async function recordSessionCall(storage, sessionId, answers, wallMs, cpuMs, now
1739
1947
  }
1740
1948
  if (a.cache_hit) cacheHitDelta += 1;
1741
1949
  }
1742
- await storage.accumulateSessionTotals(sessionId, {
1950
+ await storage.accumulateSessionTotals(sid, {
1743
1951
  calls: answers.length,
1744
1952
  wall_ms: wallMs,
1745
1953
  cache_hits: cacheHitDelta,
@@ -4671,10 +4879,6 @@ If you need supporting evidence, you may emit ONE \`<tool_call>...</tool_call>\`
4671
4879
  }
4672
4880
 
4673
4881
  // src/tools/confer.ts
4674
- var DEFERRED_OPTS = [
4675
- // empty — every confer opt runs natively except worker_tools, which
4676
- // is gated below (non-empty list → defer).
4677
- ];
4678
4882
  async function runConfer(args, opts) {
4679
4883
  const callStartedWall = performance3.now();
4680
4884
  const callStartedCpu = process.cpuUsage();
@@ -4690,16 +4894,11 @@ async function runConfer(args, opts) {
4690
4894
  rejectedWorkerTools.push(t);
4691
4895
  }
4692
4896
  }
4693
- const workerToolsNeedsBridge = acceptedWorkerTools.length > 0 && !opts.innerCallers;
4694
- const needsBridge = DEFERRED_OPTS.some((k) => Boolean(args[k])) || workerToolsNeedsBridge;
4695
- if (needsBridge) {
4696
- if (opts.bridge && opts.bridge.toolNames.has("confer")) {
4697
- return await deferConfer(args, opts.bridge);
4698
- }
4897
+ if (acceptedWorkerTools.length > 0 && !opts.innerCallers) {
4699
4898
  return errorEnvelope2(
4700
4899
  "CONFER_OPT_NOT_NATIVE",
4701
4900
  "worker_tools requested but innerCallers are not wired",
4702
- "Native confer covers untrusted_input + early_stop + extract_claims + inject_session_memory + auto_panel. worker_tools needs innerCallers wired by the host (or CROSSCHECK_BRIDGE_PYTHON=1 as a backstop)."
4901
+ "Native confer covers untrusted_input + early_stop + extract_claims + inject_session_memory + auto_panel. worker_tools needs innerCallers wired by the host."
4703
4902
  );
4704
4903
  }
4705
4904
  const question = typeof args["question"] === "string" ? args["question"] : String(args["question"] ?? "");
@@ -5050,21 +5249,6 @@ function unknownProviderError(unknownNames, available) {
5050
5249
  available_now: Object.keys(available).sort()
5051
5250
  };
5052
5251
  }
5053
- async function deferConfer(args, bridge) {
5054
- const r = await bridge.callTool("confer", args);
5055
- const text = r.content[0]?.text;
5056
- if (typeof text === "string") {
5057
- try {
5058
- return JSON.parse(text);
5059
- } catch {
5060
- }
5061
- }
5062
- return errorEnvelope2(
5063
- "CONFER_BRIDGE_BAD_ENVELOPE",
5064
- "bridge returned an unparseable envelope for confer",
5065
- "Check that the Python child is healthy."
5066
- );
5067
- }
5068
5252
  function errorEnvelope2(code, message, hint) {
5069
5253
  return {
5070
5254
  tool: "confer",
@@ -5436,7 +5620,7 @@ function errorEnvelope3(code, message, hint) {
5436
5620
  transient: false
5437
5621
  };
5438
5622
  }
5439
- async function runVerify(args, bridge, fetchConfig) {
5623
+ async function runVerify(args, fetchConfig) {
5440
5624
  const checks = args["checks"];
5441
5625
  if (!Array.isArray(checks) || checks.length === 0) {
5442
5626
  return {
@@ -5448,7 +5632,6 @@ async function runVerify(args, bridge, fetchConfig) {
5448
5632
  )
5449
5633
  };
5450
5634
  }
5451
- void bridge;
5452
5635
  const allowShell = Boolean(args["allow_shell"] ?? false);
5453
5636
  const wallStart = performance4.now();
5454
5637
  const cpuStart = process.cpuUsage();
@@ -5750,7 +5933,6 @@ async function runBench(args, opts) {
5750
5933
  const innerOpts = {
5751
5934
  providers: opts.providers,
5752
5935
  allowlist: opts.allowlist ?? null,
5753
- ...opts.bridge ? { bridge: opts.bridge } : {},
5754
5936
  ...opts.moderator ? { moderator: opts.moderator } : {}
5755
5937
  };
5756
5938
  let inner;
@@ -6212,6 +6394,19 @@ import { performance as performance7 } from "perf_hooks";
6212
6394
  import { AsyncLocalStorage } from "async_hooks";
6213
6395
  import { performance as performance6 } from "perf_hooks";
6214
6396
  var progressStorage = new AsyncLocalStorage();
6397
+ async function withProgress(token, sendNotification, fn) {
6398
+ if (token === void 0 || token === null) {
6399
+ return fn();
6400
+ }
6401
+ const ctx = {
6402
+ token,
6403
+ wallStart: performance6.now(),
6404
+ cpuStart: process.cpuUsage(),
6405
+ step: { value: 0 },
6406
+ sendNotification
6407
+ };
6408
+ return progressStorage.run(ctx, fn);
6409
+ }
6215
6410
  async function emitProgress(message, opts) {
6216
6411
  const ctx = progressStorage.getStore();
6217
6412
  const wallStart = ctx ? ctx.wallStart : performance6.now();
@@ -6382,11 +6577,11 @@ function getNodeCache(cfg, repoRoot, key, nowMs) {
6382
6577
  return null;
6383
6578
  }
6384
6579
  }
6385
- function atomicWriteJson(path12, payload) {
6386
- mkdirSync3(dirname(path12), { recursive: true });
6387
- const tmp = `${path12}.${process.pid}.${Date.now()}.tmp`;
6580
+ function atomicWriteJson(path13, payload) {
6581
+ mkdirSync3(dirname(path13), { recursive: true });
6582
+ const tmp = `${path13}.${process.pid}.${Date.now()}.tmp`;
6388
6583
  writeFileSync3(tmp, JSON.stringify(payload), "utf8");
6389
- renameSync(tmp, path12);
6584
+ renameSync(tmp, path13);
6390
6585
  }
6391
6586
  function putNodeCache(cfg, repoRoot, key, value) {
6392
6587
  if (!cfg || cfg.enabled === false) return;
@@ -6444,7 +6639,6 @@ var EST_TOKENS = {
6444
6639
  med: [1500, 800],
6445
6640
  high: [2500, 1500]
6446
6641
  };
6447
- var DEFERRED_OPTS2 = [];
6448
6642
  var REACTIVE_SIGNAL_RE = /<signals>\s*(\{[\s\S]*?\})\s*<\/signals>/g;
6449
6643
  function parseReactiveSignals(output) {
6450
6644
  if (typeof output !== "string" || !output.includes("<signals>")) return [];
@@ -6484,17 +6678,6 @@ async function runOrchestrate(args, opts) {
6484
6678
  );
6485
6679
  if (breakerEnv) return breakerEnv;
6486
6680
  }
6487
- const needsBridge = DEFERRED_OPTS2.some((k) => Boolean(args[k]));
6488
- if (needsBridge) {
6489
- if (opts.bridge && opts.bridge.toolNames.has("orchestrate")) {
6490
- return await deferOrchestrate(args, opts.bridge);
6491
- }
6492
- return errorEnvelope5(
6493
- "ORCHESTRATE_OPT_NOT_NATIVE",
6494
- "this orchestrate opt-in requires the Python bridge in v1",
6495
- "v1 native covers the plain DAG flow + cheap_mode. reactive (mid-flight DAG updates) still defers. Set CROSSCHECK_BRIDGE_PYTHON=1 to enable."
6496
- );
6497
- }
6498
6681
  const goal = typeof args["goal"] === "string" ? args["goal"] : null;
6499
6682
  const dagIn = isObj5(args["dag"]) ? args["dag"] : null;
6500
6683
  if (goal === null === (dagIn === null)) {
@@ -7436,21 +7619,6 @@ function resolveProviders3(names, available, allowlist) {
7436
7619
  }
7437
7620
  return out;
7438
7621
  }
7439
- async function deferOrchestrate(args, bridge) {
7440
- const r = await bridge.callTool("orchestrate", args);
7441
- const text = r.content[0]?.text;
7442
- if (typeof text === "string") {
7443
- try {
7444
- return JSON.parse(text);
7445
- } catch {
7446
- }
7447
- }
7448
- return errorEnvelope5(
7449
- "ORCHESTRATE_BRIDGE_BAD_ENVELOPE",
7450
- "bridge returned an unparseable envelope for orchestrate",
7451
- "Check that the Python child is healthy."
7452
- );
7453
- }
7454
7622
  function errorEnvelope5(code, message, hint, kind = "client") {
7455
7623
  return {
7456
7624
  tool: "orchestrate",
@@ -7768,6 +7936,16 @@ function buildCallContext(args) {
7768
7936
  var CREATE_DOC_MAX_BYTES = 32 * 1024;
7769
7937
  var CREATE_AUDIT_THRESHOLD = 0.7;
7770
7938
  async function runCreate(args, opts) {
7939
+ const subEnvelopes = [];
7940
+ const tracked = (fn) => async (...a) => {
7941
+ const out = await fn(...a);
7942
+ subEnvelopes.push(out);
7943
+ return out;
7944
+ };
7945
+ const confer = tracked(runConfer);
7946
+ const orchestrate = tracked(runOrchestrate);
7947
+ const review = tracked(runReview);
7948
+ const audit = tracked(runAudit);
7771
7949
  const instruction = typeof args["instruction"] === "string" ? args["instruction"] : String(args["instruction"] ?? "");
7772
7950
  if (!instruction) {
7773
7951
  return errorEnvelope6(
@@ -7804,7 +7982,7 @@ ${constraints}
7804
7982
 
7805
7983
  ` : "") + `DOCUMENTS (${okCount} of ${descriptors.length} ingested):
7806
7984
  ${documentsPayload}`;
7807
- const scope = await runConfer({
7985
+ const scope = await confer({
7808
7986
  question: "We are about to orchestrate a full plan->build->review->audit lifecycle for this instruction. List 3-6 concrete sub-tasks (each with a one-line description and a difficulty: low|med|high), any critical assumptions, and any missing-information risks. Be concise and decisive \u2014 your output will be used to build the orchestration DAG.\n\nINSTRUCTION:\n" + instruction,
7809
7987
  context: contextBlock,
7810
7988
  ...providers !== void 0 ? { providers } : {},
@@ -7813,7 +7991,6 @@ ${documentsPayload}`;
7813
7991
  }, {
7814
7992
  providers: opts.providers,
7815
7993
  allowlist: opts.allowlist ?? null,
7816
- ...opts.bridge ? { bridge: opts.bridge } : {},
7817
7994
  ctx
7818
7995
  });
7819
7996
  const scopeAnswer = Array.isArray(scope["answers"]) ? scope["answers"].filter((a) => typeof a["response"] === "string" && a["response"]).map((a) => `[${a["provider"]}]
@@ -7832,11 +8009,10 @@ ${scopeAnswer || "(none)"}
7832
8009
  };
7833
8010
  if (providers !== void 0) orchestrateArgs["providers"] = providers;
7834
8011
  if (planOnly) orchestrateArgs["plan_only"] = true;
7835
- let orchestration = await runOrchestrate(orchestrateArgs, {
8012
+ let orchestration = await orchestrate(orchestrateArgs, {
7836
8013
  providers: opts.providers,
7837
8014
  allowlist: opts.allowlist ?? null,
7838
8015
  moderator,
7839
- ...opts.bridge ? { bridge: opts.bridge } : {},
7840
8016
  ctx
7841
8017
  });
7842
8018
  let attempts = 1;
@@ -7862,7 +8038,7 @@ ${scopeAnswer || "(none)"}
7862
8038
  }
7863
8039
  let reviewEnvelope = null;
7864
8040
  if (!skipReview && typeof orchestration["final"] === "string" && orchestration["final"]) {
7865
- reviewEnvelope = await runReview({
8041
+ reviewEnvelope = await review({
7866
8042
  snippet: orchestration["final"],
7867
8043
  intent: `final deliverable for: ${instruction}`,
7868
8044
  ...providers !== void 0 ? { providers } : {},
@@ -7871,7 +8047,6 @@ ${scopeAnswer || "(none)"}
7871
8047
  }, {
7872
8048
  providers: opts.providers,
7873
8049
  allowlist: opts.allowlist ?? null,
7874
- ...opts.bridge ? { bridge: opts.bridge } : {},
7875
8050
  ctx
7876
8051
  });
7877
8052
  }
@@ -7883,7 +8058,7 @@ ${scopeAnswer || "(none)"}
7883
8058
  const panelExhausted = producingPanel.length > 0 && producingPanel.every(
7884
8059
  (p) => p in opts.providers || Object.keys(opts.providers).includes(p.toLowerCase())
7885
8060
  ) && producingPanel.length >= Object.keys(opts.providers).length;
7886
- auditEnvelope = await runAudit({
8061
+ auditEnvelope = await audit({
7887
8062
  output_to_audit: orchestration["final"],
7888
8063
  producing_panelists: producingPanel,
7889
8064
  ...auditRubric !== void 0 ? { rubric: auditRubric } : {},
@@ -7894,7 +8069,6 @@ ${scopeAnswer || "(none)"}
7894
8069
  }, {
7895
8070
  providers: opts.providers,
7896
8071
  allowlist: opts.allowlist ?? null,
7897
- ...opts.bridge ? { bridge: opts.bridge } : {},
7898
8072
  ctx
7899
8073
  });
7900
8074
  let overallF = toNumberOrNull(auditEnvelope["overall_score"]);
@@ -7913,13 +8087,12 @@ ${retryConstraints}
7913
8087
  DOCUMENTS:
7914
8088
  ${documentsPayload}`
7915
8089
  };
7916
- const retryOrchestration = await runOrchestrate(
8090
+ const retryOrchestration = await orchestrate(
7917
8091
  retryOrchestrateArgs,
7918
8092
  {
7919
8093
  providers: opts.providers,
7920
8094
  allowlist: opts.allowlist ?? null,
7921
8095
  moderator,
7922
- ...opts.bridge ? { bridge: opts.bridge } : {},
7923
8096
  ctx
7924
8097
  }
7925
8098
  );
@@ -7928,7 +8101,7 @@ ${documentsPayload}`
7928
8101
  if (typeof retryOrchestration["final"] === "string" && retryOrchestration["final"]) {
7929
8102
  const retryPanel = computeProducingPanel(retryOrchestration);
7930
8103
  const retryPanelExhausted = retryPanel.length > 0 && retryPanel.length >= Object.keys(opts.providers).length;
7931
- auditEnvelope = await runAudit({
8104
+ auditEnvelope = await audit({
7932
8105
  output_to_audit: retryOrchestration["final"],
7933
8106
  producing_panelists: retryPanel,
7934
8107
  ...auditRubric !== void 0 ? { rubric: auditRubric } : {},
@@ -7939,7 +8112,6 @@ ${documentsPayload}`
7939
8112
  }, {
7940
8113
  providers: opts.providers,
7941
8114
  allowlist: opts.allowlist ?? null,
7942
- ...opts.bridge ? { bridge: opts.bridge } : {},
7943
8115
  ctx
7944
8116
  });
7945
8117
  overallF = toNumberOrNull(auditEnvelope["overall_score"]);
@@ -8004,9 +8176,28 @@ ${documentsPayload}`
8004
8176
  ["auditor", "items", "overall_score", "passed", "rubric"]
8005
8177
  ),
8006
8178
  artifacts,
8007
- warnings
8179
+ warnings,
8180
+ ...combinedUsage(subEnvelopes)
8008
8181
  };
8009
8182
  }
8183
+ function combinedUsage(envelopes) {
8184
+ const usages = [];
8185
+ const byCallTiming = [];
8186
+ let wall = 0, cpu = 0;
8187
+ for (const env of envelopes) {
8188
+ const e = env ?? {};
8189
+ const calls = e["usage"]?.["by_call"] ?? [];
8190
+ const t = e["timing"] ?? {};
8191
+ const timing = Array.isArray(t["by_call"]) ? t["by_call"] : [];
8192
+ if (!Array.isArray(calls) || calls.length === 0) continue;
8193
+ usages.push(...calls);
8194
+ if (timing.length === calls.length) byCallTiming.push(...timing);
8195
+ else for (const c of calls) byCallTiming.push({ provider: c.provider, model: c.model, purpose: c.purpose ?? null, wall_ms: 0, cpu_ms: 0, cache_hit: false });
8196
+ wall += Number(t["wall_ms"] ?? 0) || 0;
8197
+ cpu += Number(t["cpu_ms"] ?? 0) || 0;
8198
+ }
8199
+ return { usage: aggregateUsage(usages), timing: { wall_ms: wall, cpu_ms: cpu, by_call: byCallTiming } };
8200
+ }
8010
8201
  async function ingestDocuments(documents, sessionId, opts) {
8011
8202
  if (documents.length === 0) return [];
8012
8203
  const out = [];
@@ -8177,11 +8368,6 @@ function errorEnvelope6(toolName, code, message, hint) {
8177
8368
 
8178
8369
  // src/tools/coordinate.ts
8179
8370
  import { performance as performance8 } from "perf_hooks";
8180
- var DEFERRED_OPTS3 = [
8181
- // empty — every coordinate opt runs natively when its deps
8182
- // (storage / innerCallers) are wired. Without them the
8183
- // worker_tools gate still triggers a bridge defer.
8184
- ];
8185
8371
  var COORDINATE_DEFAULT_TEMP = 0.4;
8186
8372
  var ROLE_TURN_SCHEMA = {
8187
8373
  type: "object",
@@ -8236,23 +8422,18 @@ async function runCoordinate(args, opts) {
8236
8422
  rejectedWorkerTools.push(t);
8237
8423
  }
8238
8424
  }
8239
- const workerToolsNeedsBridge = acceptedWorkerTools.length > 0 && !opts.innerCallers;
8240
- const needsBridge = DEFERRED_OPTS3.some((k) => Boolean(args[k])) || workerToolsNeedsBridge;
8241
- if (needsBridge) {
8425
+ if (acceptedWorkerTools.length > 0 && !opts.innerCallers) {
8242
8426
  if (superRequested) {
8243
8427
  return errorEnvelope7(
8244
8428
  "COORDINATE_SUPER_NOT_NATIVE",
8245
- "super mode requested but worker_tools needs the Python bridge",
8246
- "worker_tools without innerCallers wired defers to the Python bridge, which doesn't support super mode. Drop worker_tools, or wire innerCallers, to use super."
8429
+ "super mode requested with worker_tools, but innerCallers are not wired",
8430
+ "worker_tools needs innerCallers wired by the host. Drop worker_tools, or wire innerCallers, to use super."
8247
8431
  );
8248
8432
  }
8249
- if (opts.bridge && opts.bridge.toolNames.has("coordinate")) {
8250
- return await deferCoordinate(args, opts.bridge);
8251
- }
8252
8433
  return errorEnvelope7(
8253
8434
  "COORDINATE_OPT_NOT_NATIVE",
8254
8435
  "worker_tools requested but innerCallers are not wired",
8255
- "Native coordinate covers the three-role flow + untrusted_input + inject_session_memory. worker_tools needs innerCallers wired by the host (or CROSSCHECK_BRIDGE_PYTHON=1 as a backstop)."
8436
+ "Native coordinate covers the three-role flow + untrusted_input + inject_session_memory. worker_tools needs innerCallers wired by the host."
8256
8437
  );
8257
8438
  }
8258
8439
  const untrusted = Boolean(args["untrusted_input"]);
@@ -8652,21 +8833,6 @@ function unknownProviderError3(unknownNames, available) {
8652
8833
  available_now: Object.keys(available).sort()
8653
8834
  };
8654
8835
  }
8655
- async function deferCoordinate(args, bridge) {
8656
- const r = await bridge.callTool("coordinate", args);
8657
- const text = r.content[0]?.text;
8658
- if (typeof text === "string") {
8659
- try {
8660
- return JSON.parse(text);
8661
- } catch {
8662
- }
8663
- }
8664
- return errorEnvelope7(
8665
- "COORDINATE_BRIDGE_BAD_ENVELOPE",
8666
- "bridge returned an unparseable envelope for coordinate",
8667
- "Check that the Python child is healthy."
8668
- );
8669
- }
8670
8836
  function errorEnvelope7(code, message, hint) {
8671
8837
  return {
8672
8838
  tool: "coordinate",
@@ -8994,10 +9160,6 @@ function buildModeratorTranscript(transcript) {
8994
9160
 
8995
9161
  // src/tools/debate.ts
8996
9162
  import { performance as performance10 } from "perf_hooks";
8997
- var DEFERRED_OPTS4 = [
8998
- // empty — every debate opt runs natively when its deps
8999
- // (storage / innerCallers) are wired.
9000
- ];
9001
9163
  async function runDebate(args, opts) {
9002
9164
  const callStartedWall = performance10.now();
9003
9165
  const callStartedCpu = process.cpuUsage();
@@ -9015,23 +9177,18 @@ async function runDebate(args, opts) {
9015
9177
  rejectedWorkerTools.push(t);
9016
9178
  }
9017
9179
  }
9018
- const workerToolsNeedsBridge = acceptedWorkerTools.length > 0 && !opts.innerCallers;
9019
- const needsBridge = DEFERRED_OPTS4.some((k) => Boolean(args[k])) || workerToolsNeedsBridge;
9020
- if (needsBridge) {
9180
+ if (acceptedWorkerTools.length > 0 && !opts.innerCallers) {
9021
9181
  if (superRequested) {
9022
9182
  return errorEnvelope9(
9023
9183
  "DEBATE_SUPER_NOT_NATIVE",
9024
- "super mode requested but worker_tools needs the Python bridge",
9025
- "worker_tools without innerCallers wired defers to the Python bridge, which doesn't support super mode. Drop worker_tools, or wire innerCallers, to use super."
9184
+ "super mode requested with worker_tools, but innerCallers are not wired",
9185
+ "worker_tools needs innerCallers wired by the host. Drop worker_tools, or wire innerCallers, to use super."
9026
9186
  );
9027
9187
  }
9028
- if (opts.bridge && opts.bridge.toolNames.has("debate")) {
9029
- return await deferDebate(args, opts.bridge);
9030
- }
9031
9188
  return errorEnvelope9(
9032
9189
  "DEBATE_OPT_NOT_NATIVE",
9033
9190
  "worker_tools requested but innerCallers are not wired",
9034
- "Native debate covers N-round + plain/structured moderator synthesis + inject_session_memory + auto_panel + extract_claims + early_stop. worker_tools needs innerCallers wired by the host (or CROSSCHECK_BRIDGE_PYTHON=1 as a backstop)."
9191
+ "Native debate covers N-round + plain/structured moderator synthesis + inject_session_memory + auto_panel + extract_claims + early_stop. worker_tools needs innerCallers wired by the host."
9035
9192
  );
9036
9193
  }
9037
9194
  const topic = typeof args["topic"] === "string" ? args["topic"] : String(args["topic"] ?? "");
@@ -9468,21 +9625,6 @@ function unknownProviderError5(unknownNames, available) {
9468
9625
  available_now: Object.keys(available).sort()
9469
9626
  };
9470
9627
  }
9471
- async function deferDebate(args, bridge) {
9472
- const r = await bridge.callTool("debate", args);
9473
- const text = r.content[0]?.text;
9474
- if (typeof text === "string") {
9475
- try {
9476
- return JSON.parse(text);
9477
- } catch {
9478
- }
9479
- }
9480
- return errorEnvelope9(
9481
- "DEBATE_BRIDGE_BAD_ENVELOPE",
9482
- "bridge returned an unparseable envelope for debate",
9483
- "Check that the Python child is healthy."
9484
- );
9485
- }
9486
9628
  function errorEnvelope9(code, message, hint) {
9487
9629
  return {
9488
9630
  tool: "debate",
@@ -9502,9 +9644,6 @@ var DEFAULT_DELEGATION_LIMITS = {
9502
9644
  };
9503
9645
  async function runDelegate(args, opts) {
9504
9646
  if (!opts.storage) {
9505
- if (opts.bridge && opts.bridge.toolNames.has("delegate")) {
9506
- return await deferDelegate(args, opts.bridge);
9507
- }
9508
9647
  return errorEnvelope10(
9509
9648
  "DELEGATE_STORAGE_UNAVAILABLE",
9510
9649
  "delegate needs the local database (for quota tracking), which isn't available",
@@ -9576,7 +9715,6 @@ async function runDelegate(args, opts) {
9576
9715
  const innerOpts = {
9577
9716
  providers: opts.providers,
9578
9717
  allowlist: opts.allowlist ?? null,
9579
- ...opts.bridge ? { bridge: opts.bridge } : {},
9580
9718
  ...opts.moderator ? { moderator: opts.moderator } : {}
9581
9719
  };
9582
9720
  let result;
@@ -9615,21 +9753,6 @@ async function record(storage, sessionId, requester, toolCall, via, accepted, no
9615
9753
  created_at: nowEpochSeconds
9616
9754
  });
9617
9755
  }
9618
- async function deferDelegate(args, bridge) {
9619
- const r = await bridge.callTool("delegate", args);
9620
- const text = r.content[0]?.text;
9621
- if (typeof text === "string") {
9622
- try {
9623
- return JSON.parse(text);
9624
- } catch {
9625
- }
9626
- }
9627
- return errorEnvelope10(
9628
- "DELEGATE_BRIDGE_BAD_ENVELOPE",
9629
- "bridge returned an unparseable envelope for delegate",
9630
- "Check that the Python child is healthy."
9631
- );
9632
- }
9633
9756
  function errorEnvelope10(code, message, hint) {
9634
9757
  return {
9635
9758
  tool: "delegate",
@@ -9672,9 +9795,6 @@ async function runExplain(args, opts) {
9672
9795
  );
9673
9796
  }
9674
9797
  if (!opts.storage) {
9675
- if (opts.bridge && opts.bridge.toolNames.has("explain")) {
9676
- return await deferExplain(args, opts.bridge);
9677
- }
9678
9798
  return errorEnvelope11(
9679
9799
  "EXPLAIN_STORAGE_UNAVAILABLE",
9680
9800
  "explain needs the local transcript/stats database, which isn't available",
@@ -9956,21 +10076,6 @@ function pyRepr5(v) {
9956
10076
  }
9957
10077
  return String(v);
9958
10078
  }
9959
- async function deferExplain(args, bridge) {
9960
- const r = await bridge.callTool("explain", args);
9961
- const text = r.content[0]?.text;
9962
- if (typeof text === "string") {
9963
- try {
9964
- return JSON.parse(text);
9965
- } catch {
9966
- }
9967
- }
9968
- return errorEnvelope11(
9969
- "EXPLAIN_BRIDGE_BAD_ENVELOPE",
9970
- "bridge returned an unparseable envelope for explain",
9971
- "Check that the Python child is healthy."
9972
- );
9973
- }
9974
10079
  function errorEnvelope11(code, message, hint, kind = "client", extra = {}) {
9975
10080
  return {
9976
10081
  tool: "explain",
@@ -10523,9 +10628,6 @@ async function runRecall(args, opts) {
10523
10628
  if (k < 1) k = 1;
10524
10629
  if (k > 50) k = 50;
10525
10630
  if (!opts.storage) {
10526
- if (opts.bridge && opts.bridge.toolNames.has("recall")) {
10527
- return await deferRecall(args, opts.bridge);
10528
- }
10529
10631
  return errorEnvelope12(
10530
10632
  "RECALL_STORAGE_UNAVAILABLE",
10531
10633
  "recall needs the local database, which isn't available",
@@ -10578,21 +10680,6 @@ async function runRecall(args, opts) {
10578
10680
  applied_filters: applied
10579
10681
  };
10580
10682
  }
10581
- async function deferRecall(args, bridge) {
10582
- const r = await bridge.callTool("recall", args);
10583
- const text = r.content[0]?.text;
10584
- if (typeof text === "string") {
10585
- try {
10586
- return JSON.parse(text);
10587
- } catch {
10588
- }
10589
- }
10590
- return errorEnvelope12(
10591
- "RECALL_BRIDGE_BAD_ENVELOPE",
10592
- "bridge returned an unparseable envelope for recall",
10593
- "Check that the Python child is healthy."
10594
- );
10595
- }
10596
10683
  function errorEnvelope12(code, message, hint) {
10597
10684
  return {
10598
10685
  tool: "recall",
@@ -10615,9 +10702,6 @@ async function runRecommendPanel(args, opts) {
10615
10702
  );
10616
10703
  }
10617
10704
  if (!opts.storage) {
10618
- if (opts.bridge && opts.bridge.toolNames.has("recommend_panel")) {
10619
- return await deferRecommendPanel(args, opts.bridge);
10620
- }
10621
10705
  return errorEnvelope13(
10622
10706
  "RECOMMEND_PANEL_STORAGE_UNAVAILABLE",
10623
10707
  "recommend_panel needs the local stats database, which isn't available",
@@ -10680,21 +10764,6 @@ async function runRecommendPanel(args, opts) {
10680
10764
  meta: result.meta
10681
10765
  };
10682
10766
  }
10683
- async function deferRecommendPanel(args, bridge) {
10684
- const r = await bridge.callTool("recommend_panel", args);
10685
- const text = r.content[0]?.text;
10686
- if (typeof text === "string") {
10687
- try {
10688
- return JSON.parse(text);
10689
- } catch {
10690
- }
10691
- }
10692
- return errorEnvelope13(
10693
- "RECOMMEND_PANEL_BRIDGE_BAD_ENVELOPE",
10694
- "bridge returned an unparseable envelope for recommend_panel",
10695
- "Check that the Python child is healthy."
10696
- );
10697
- }
10698
10767
  function errorEnvelope13(code, message, hint) {
10699
10768
  return {
10700
10769
  tool: "recommend_panel",
@@ -10724,9 +10793,6 @@ function toStringArray3(v) {
10724
10793
  import { readFileSync as readFileSync10, existsSync as existsSync8 } from "fs";
10725
10794
  async function runScoreboard(args, opts) {
10726
10795
  if (!opts.storage) {
10727
- if (opts.bridge && opts.bridge.toolNames.has("scoreboard")) {
10728
- return await deferScoreboard(args, opts.bridge);
10729
- }
10730
10796
  return errorEnvelope14(
10731
10797
  "SCOREBOARD_STORAGE_UNAVAILABLE",
10732
10798
  "scoreboard needs the local stats database, which isn't available",
@@ -10849,21 +10915,6 @@ function clampInt3(raw, min, max, fallback) {
10849
10915
  if (!Number.isFinite(n)) return fallback;
10850
10916
  return Math.min(max, Math.max(min, Math.trunc(n)));
10851
10917
  }
10852
- async function deferScoreboard(args, bridge) {
10853
- const r = await bridge.callTool("scoreboard", args);
10854
- const text = r.content[0]?.text;
10855
- if (typeof text === "string") {
10856
- try {
10857
- return JSON.parse(text);
10858
- } catch {
10859
- }
10860
- }
10861
- return errorEnvelope14(
10862
- "SCOREBOARD_BRIDGE_BAD_ENVELOPE",
10863
- "bridge returned an unparseable envelope for scoreboard",
10864
- "Check that the Python child is healthy."
10865
- );
10866
- }
10867
10918
  function errorEnvelope14(code, message, hint) {
10868
10919
  return {
10869
10920
  tool: "scoreboard",
@@ -10900,9 +10951,6 @@ async function runSessionMemory(args, opts) {
10900
10951
  );
10901
10952
  }
10902
10953
  if (!opts.storage) {
10903
- if (opts.bridge && opts.bridge.toolNames.has("session_memory")) {
10904
- return await deferSessionMemory(args, opts.bridge);
10905
- }
10906
10954
  return errorEnvelope15(
10907
10955
  "SESSION_MEMORY_STORAGE_UNAVAILABLE",
10908
10956
  "session_memory needs the local database, which isn't available",
@@ -11007,21 +11055,6 @@ function clampInt4(raw, min, max, fallback) {
11007
11055
  if (!Number.isFinite(n)) return fallback;
11008
11056
  return Math.min(max, Math.max(min, Math.trunc(n)));
11009
11057
  }
11010
- async function deferSessionMemory(args, bridge) {
11011
- const r = await bridge.callTool("session_memory", args);
11012
- const text = r.content[0]?.text;
11013
- if (typeof text === "string") {
11014
- try {
11015
- return JSON.parse(text);
11016
- } catch {
11017
- }
11018
- }
11019
- return errorEnvelope15(
11020
- "SESSION_MEMORY_BRIDGE_BAD_ENVELOPE",
11021
- "bridge returned an unparseable envelope for session_memory",
11022
- "Check that the Python child is healthy."
11023
- );
11024
- }
11025
11058
  function errorEnvelope15(code, message, hint) {
11026
11059
  return {
11027
11060
  tool: "session_memory",
@@ -11641,7 +11674,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
11641
11674
  var DEFAULT_PACKAGE = "crosscheck-cli";
11642
11675
  var FETCH_TIMEOUT_MS = 3e3;
11643
11676
  function engineVersion() {
11644
- return true ? "0.2.27" : "0.0.0-dev";
11677
+ return true ? "0.2.29" : "0.0.0-dev";
11645
11678
  }
11646
11679
  function defaultUpdateCachePath() {
11647
11680
  const base = process.env["CROSSCHECK_DATA_DIR"] || path9.join(os.homedir() || os.tmpdir(), ".crosscheck");
@@ -12413,12 +12446,11 @@ function helpTool() {
12413
12446
  }
12414
12447
  });
12415
12448
  }
12416
- function registerCoreTools(opts = {}) {
12417
- const o = opts && typeof opts === "object" && "toolNames" in opts ? { bridge: opts } : opts;
12449
+ function registerCoreTools(o = {}) {
12418
12450
  const tools = /* @__PURE__ */ new Map();
12419
12451
  const list = [
12420
12452
  pingTool(),
12421
- verifyTool(o.bridge, o.fetchConfig),
12453
+ verifyTool(o.fetchConfig),
12422
12454
  pickTool(
12423
12455
  o.providers ?? {},
12424
12456
  o.providerAllowlist ?? null,
@@ -12430,7 +12462,6 @@ function registerCoreTools(opts = {}) {
12430
12462
  auditTool(
12431
12463
  o.providers ?? {},
12432
12464
  o.providerAllowlist ?? null,
12433
- o.bridge,
12434
12465
  o.transcriptsDir,
12435
12466
  o.pricing,
12436
12467
  o.storage,
@@ -12440,7 +12471,6 @@ function registerCoreTools(opts = {}) {
12440
12471
  conferTool(
12441
12472
  o.providers ?? {},
12442
12473
  o.providerAllowlist ?? null,
12443
- o.bridge,
12444
12474
  o.moderatorDefault ?? "anthropic",
12445
12475
  o.pricing,
12446
12476
  o.storage,
@@ -12453,7 +12483,6 @@ function registerCoreTools(opts = {}) {
12453
12483
  debateTool(
12454
12484
  o.providers ?? {},
12455
12485
  o.providerAllowlist ?? null,
12456
- o.bridge,
12457
12486
  buildInnerCallers(o),
12458
12487
  o.storage,
12459
12488
  o.pricing,
@@ -12465,7 +12494,6 @@ function registerCoreTools(opts = {}) {
12465
12494
  coordinateTool(
12466
12495
  o.providers ?? {},
12467
12496
  o.providerAllowlist ?? null,
12468
- o.bridge,
12469
12497
  buildInnerCallers(o),
12470
12498
  o.storage,
12471
12499
  o.circuitBreakers,
@@ -12476,7 +12504,6 @@ function registerCoreTools(opts = {}) {
12476
12504
  triangulateTool(
12477
12505
  o.providers ?? {},
12478
12506
  o.providerAllowlist ?? null,
12479
- o.bridge,
12480
12507
  o.storage,
12481
12508
  o.circuitBreakers,
12482
12509
  o.transcriptsDir,
@@ -12485,7 +12512,6 @@ function registerCoreTools(opts = {}) {
12485
12512
  planTool(
12486
12513
  o.providers ?? {},
12487
12514
  o.providerAllowlist ?? null,
12488
- o.bridge,
12489
12515
  o.storage,
12490
12516
  o.circuitBreakers,
12491
12517
  o.transcriptsDir,
@@ -12494,7 +12520,6 @@ function registerCoreTools(opts = {}) {
12494
12520
  critiqueTool(
12495
12521
  o.providers ?? {},
12496
12522
  o.providerAllowlist ?? null,
12497
- o.bridge,
12498
12523
  o.storage,
12499
12524
  o.circuitBreakers,
12500
12525
  o.transcriptsDir,
@@ -12503,7 +12528,6 @@ function registerCoreTools(opts = {}) {
12503
12528
  reviewTool(
12504
12529
  o.providers ?? {},
12505
12530
  o.providerAllowlist ?? null,
12506
- o.bridge,
12507
12531
  o.storage,
12508
12532
  o.circuitBreakers,
12509
12533
  o.transcriptsDir,
@@ -12511,25 +12535,23 @@ function registerCoreTools(opts = {}) {
12511
12535
  ),
12512
12536
  listProvidersTool(o.providers ?? {}, o.activeProviders ?? null, o.moderatorDefault ?? "anthropic"),
12513
12537
  modelsTool(o.providers ?? {}, o.activeProviders ?? null),
12514
- recallTool(o.storage, o.bridge),
12515
- sessionMemoryTool(o.storage, o.bridge),
12516
- scoreboardTool(o.storage, o.bridge, o.eventsPath),
12517
- explainTool(o.storage, o.bridge, o.transcriptsDir),
12538
+ recallTool(o.storage),
12539
+ sessionMemoryTool(o.storage),
12540
+ scoreboardTool(o.storage, o.eventsPath),
12541
+ explainTool(o.storage, o.transcriptsDir),
12518
12542
  delegateTool(
12519
12543
  o.providers ?? {},
12520
12544
  o.providerAllowlist ?? null,
12521
12545
  o.storage,
12522
- o.bridge,
12523
12546
  o.moderatorDefault ?? "anthropic"
12524
12547
  ),
12525
12548
  fetchTool(o.storage, o.fetchConfig, o.repoRoot),
12526
- recommendPanelTool(o.providers ?? {}, o.storage, o.bridge),
12549
+ recommendPanelTool(o.providers ?? {}, o.storage),
12527
12550
  updateCrosscheckTool(o.repoRoot ?? null),
12528
12551
  benchTool(
12529
12552
  o.providers ?? {},
12530
12553
  o.providerAllowlist ?? null,
12531
12554
  o.storage,
12532
- o.bridge,
12533
12555
  o.moderatorDefault ?? "anthropic",
12534
12556
  o.benchGoldensDir,
12535
12557
  o.circuitBreakers,
@@ -12540,7 +12562,6 @@ function registerCoreTools(opts = {}) {
12540
12562
  solveTool(
12541
12563
  o.providers ?? {},
12542
12564
  o.providerAllowlist ?? null,
12543
- o.bridge,
12544
12565
  o.storage,
12545
12566
  o.circuitBreakers,
12546
12567
  o.transcriptsDir,
@@ -12549,7 +12570,6 @@ function registerCoreTools(opts = {}) {
12549
12570
  orchestrateTool(
12550
12571
  o.providers ?? {},
12551
12572
  o.providerAllowlist ?? null,
12552
- o.bridge,
12553
12573
  o.moderatorDefault ?? "anthropic",
12554
12574
  o.storage,
12555
12575
  o.pricing,
@@ -12596,7 +12616,6 @@ function createTool(o, toolName, cheapDefault) {
12596
12616
  providers: o.providers ?? {},
12597
12617
  allowlist: o.providerAllowlist ?? null,
12598
12618
  ...o.storage ? { storage: o.storage } : {},
12599
- ...o.bridge ? { bridge: o.bridge } : {},
12600
12619
  ...o.moderatorDefault ? { moderator: o.moderatorDefault } : {},
12601
12620
  ...o.fetchConfig ? { fetchConfig: o.fetchConfig } : {},
12602
12621
  ...o.repoRoot ? { repoRoot: o.repoRoot } : {},
@@ -12605,7 +12624,7 @@ function createTool(o, toolName, cheapDefault) {
12605
12624
  })
12606
12625
  };
12607
12626
  }
12608
- function orchestrateTool(providers, allowlist, bridge, moderator, storage, pricing, breakers, transcriptsDir, repoRoot, nodeCache) {
12627
+ function orchestrateTool(providers, allowlist, moderator, storage, pricing, breakers, transcriptsDir, repoRoot, nodeCache) {
12609
12628
  return {
12610
12629
  name: "orchestrate",
12611
12630
  description: "Plan and execute a DAG of LLM subtasks. Either supply a `goal` (planner will draft the DAG) OR a hand-authored `dag`. Workers run topologically; recombine produces a single `final`. Covers the sequential flow, cheap_mode (per-node tier picking), and reactive (mid-flight DAG updates) \u2014 all native.",
@@ -12630,7 +12649,6 @@ function orchestrateTool(providers, allowlist, bridge, moderator, storage, prici
12630
12649
  providers,
12631
12650
  allowlist,
12632
12651
  moderator,
12633
- ...bridge ? { bridge } : {},
12634
12652
  ...storage ? { storage } : {},
12635
12653
  ...pricing ? { pricing } : {},
12636
12654
  ...breakers ? { breakers } : {},
@@ -12640,7 +12658,7 @@ function orchestrateTool(providers, allowlist, bridge, moderator, storage, prici
12640
12658
  })
12641
12659
  };
12642
12660
  }
12643
- function solveTool(providers, allowlist, bridge, storage, breakers, transcriptsDir, repoRoot) {
12661
+ function solveTool(providers, allowlist, storage, breakers, transcriptsDir, repoRoot) {
12644
12662
  return {
12645
12663
  name: "solve",
12646
12664
  description: "Iterative LLM solver. Generates a proposal, verifies it against the supplied verifier, and retries with feedback up to max_attempts. Verifier kinds: regex_response and shell (run in a native sandbox with wall-clock + optional memory/CPU limits). With target_path, returns a unified-diff patch.",
@@ -12662,7 +12680,6 @@ function solveTool(providers, allowlist, bridge, storage, breakers, transcriptsD
12662
12680
  handler: (args) => runSolve(args, {
12663
12681
  providers,
12664
12682
  allowlist,
12665
- ...bridge ? { bridge } : {},
12666
12683
  ...storage ? { storage } : {},
12667
12684
  ...breakers ? { breakers } : {},
12668
12685
  ...transcriptsDir ? { transcriptsDir } : {},
@@ -12701,7 +12718,7 @@ function configPinTool(repoRoot, config2, rejectDriftEnv) {
12701
12718
  }
12702
12719
  };
12703
12720
  }
12704
- function benchTool(providers, allowlist, storage, bridge, moderator, defaultGoldensDir, breakers, transcriptsDir, repoRoot) {
12721
+ function benchTool(providers, allowlist, storage, moderator, defaultGoldensDir, breakers, transcriptsDir, repoRoot) {
12705
12722
  return {
12706
12723
  name: "bench",
12707
12724
  description: "Run benchmark goldens against each provider's confer/review output. Each golden: {name, tool_call: confer|review, args, verifiers[]}. Returns per-provider pass/fail/error + a ranking sorted by score. Records ballots into provider_stats when storage is wired (used by triangulate's win-rate weights).",
@@ -12720,7 +12737,6 @@ function benchTool(providers, allowlist, storage, bridge, moderator, defaultGold
12720
12737
  allowlist,
12721
12738
  moderator,
12722
12739
  ...storage ? { storage } : {},
12723
- ...bridge ? { bridge } : {},
12724
12740
  ...defaultGoldensDir ? { defaultGoldensDir } : {},
12725
12741
  ...breakers ? { breakers } : {},
12726
12742
  ...transcriptsDir ? { transcriptsDir } : {},
@@ -12747,7 +12763,7 @@ function updateCrosscheckTool(repoRoot) {
12747
12763
  }
12748
12764
  };
12749
12765
  }
12750
- function recommendPanelTool(providers, storage, bridge) {
12766
+ function recommendPanelTool(providers, storage) {
12751
12767
  return {
12752
12768
  name: "recommend_panel",
12753
12769
  description: "Recommend a minimal effective panel for a given purpose, based on historical usage_log + provider_stats. Cold-start falls back to the configured panel ordered by win-rate.",
@@ -12765,8 +12781,7 @@ function recommendPanelTool(providers, storage, bridge) {
12765
12781
  },
12766
12782
  handler: (args) => runRecommendPanel(args, {
12767
12783
  providers,
12768
- ...storage ? { storage } : {},
12769
- ...bridge ? { bridge } : {}
12784
+ ...storage ? { storage } : {}
12770
12785
  })
12771
12786
  };
12772
12787
  }
@@ -12791,7 +12806,7 @@ function fetchTool(storage, fetchConfig, repoRoot) {
12791
12806
  })
12792
12807
  };
12793
12808
  }
12794
- function delegateTool(providers, allowlist, storage, bridge, moderator) {
12809
+ function delegateTool(providers, allowlist, storage, moderator) {
12795
12810
  return {
12796
12811
  name: "delegate",
12797
12812
  description: "Run a delegable tool (confer | review) restricted to a single named provider, with explicit quota check. Records every attempt (accepted or refused) to the delegations table; quota in the response reflects counts AFTER the current call.",
@@ -12811,12 +12826,11 @@ function delegateTool(providers, allowlist, storage, bridge, moderator) {
12811
12826
  providers,
12812
12827
  allowlist,
12813
12828
  moderator,
12814
- ...storage ? { storage } : {},
12815
- ...bridge ? { bridge } : {}
12829
+ ...storage ? { storage } : {}
12816
12830
  })
12817
12831
  };
12818
12832
  }
12819
- function explainTool(storage, bridge, transcriptsDir) {
12833
+ function explainTool(storage, transcriptsDir) {
12820
12834
  return {
12821
12835
  name: "explain",
12822
12836
  description: "Replay a session as a navigable tree with cost/latency annotations. Reads usage_log + optionally walks the transcripts directory for per-tool summaries.",
@@ -12834,12 +12848,11 @@ function explainTool(storage, bridge, transcriptsDir) {
12834
12848
  },
12835
12849
  handler: (args) => runExplain(args, {
12836
12850
  ...storage ? { storage } : {},
12837
- ...bridge ? { bridge } : {},
12838
12851
  ...transcriptsDir ? { transcriptsDir } : {}
12839
12852
  })
12840
12853
  };
12841
12854
  }
12842
- function scoreboardTool(storage, bridge, eventsPath) {
12855
+ function scoreboardTool(storage, eventsPath) {
12843
12856
  return {
12844
12857
  name: "scoreboard",
12845
12858
  description: "Aggregate provider ballot stats + delegation counts + table totals. Supports top_k (rank limit) and recent_limit (tail of events.jsonl when configured). Reads the engine's local stats database (auto-created at <data dir>/db.sqlite).",
@@ -12853,12 +12866,11 @@ function scoreboardTool(storage, bridge, eventsPath) {
12853
12866
  },
12854
12867
  handler: (args) => runScoreboard(args, {
12855
12868
  ...storage ? { storage } : {},
12856
- ...bridge ? { bridge } : {},
12857
12869
  ...eventsPath ? { eventsPath } : {}
12858
12870
  })
12859
12871
  };
12860
12872
  }
12861
- function sessionMemoryTool(storage, bridge) {
12873
+ function sessionMemoryTool(storage) {
12862
12874
  return {
12863
12875
  name: "session_memory",
12864
12876
  description: "CRUD over the per-session working memory ledger. Actions: list, add, mark_stale, clear. Uses the engine's local database (auto-created at <data dir>/db.sqlite).",
@@ -12881,15 +12893,14 @@ function sessionMemoryTool(storage, bridge) {
12881
12893
  required: ["action", "session_id"]
12882
12894
  },
12883
12895
  handler: (args) => runSessionMemory(args, {
12884
- ...storage ? { storage } : {},
12885
- ...bridge ? { bridge } : {}
12896
+ ...storage ? { storage } : {}
12886
12897
  })
12887
12898
  };
12888
12899
  }
12889
- function recallTool(storage, bridge) {
12900
+ function recallTool(storage) {
12890
12901
  return {
12891
12902
  name: "recall",
12892
- description: "Full-text search across persisted transcripts via SQLite FTS5. Returns rows ordered by relevance with a windowed snippet. Requires a wired Storage adapter; falls back to the Python bridge when not available.",
12903
+ description: "Full-text search across persisted transcripts via SQLite FTS5. Returns rows ordered by relevance with a windowed snippet. Requires a wired Storage adapter.",
12893
12904
  inputSchema: {
12894
12905
  type: "object",
12895
12906
  additionalProperties: true,
@@ -12903,8 +12914,7 @@ function recallTool(storage, bridge) {
12903
12914
  required: ["query"]
12904
12915
  },
12905
12916
  handler: (args) => runRecall(args, {
12906
- ...storage ? { storage } : {},
12907
- ...bridge ? { bridge } : {}
12917
+ ...storage ? { storage } : {}
12908
12918
  })
12909
12919
  };
12910
12920
  }
@@ -12940,7 +12950,7 @@ function modelsTool(providers, activeProviders) {
12940
12950
  handler: async (args) => runModels(args, { providers, activeProviders })
12941
12951
  };
12942
12952
  }
12943
- function reviewTool(providers, allowlist, bridge, storage, breakers, transcriptsDir, repoRoot) {
12953
+ function reviewTool(providers, allowlist, storage, breakers, transcriptsDir, repoRoot) {
12944
12954
  return {
12945
12955
  name: "review",
12946
12956
  description: "Have an LLM panel peer-review a code or proposal snippet. Returns the confer envelope (one answer per provider).",
@@ -12959,7 +12969,6 @@ function reviewTool(providers, allowlist, bridge, storage, breakers, transcripts
12959
12969
  handler: (args) => runReview(args, {
12960
12970
  providers,
12961
12971
  allowlist,
12962
- ...bridge ? { bridge } : {},
12963
12972
  ...storage ? { storage } : {},
12964
12973
  ...breakers ? { breakers } : {},
12965
12974
  ...transcriptsDir ? { transcriptsDir } : {},
@@ -12967,7 +12976,7 @@ function reviewTool(providers, allowlist, bridge, storage, breakers, transcripts
12967
12976
  })
12968
12977
  };
12969
12978
  }
12970
- function critiqueTool(providers, allowlist, bridge, storage, breakers, transcriptsDir, repoRoot) {
12979
+ function critiqueTool(providers, allowlist, storage, breakers, transcriptsDir, repoRoot) {
12971
12980
  return {
12972
12981
  name: "critique",
12973
12982
  description: "Have each LLM panelist list the top weaknesses of a proposed answer or approach (severity-rated). Returns per-provider weakness lists + a merged list ordered by severity.",
@@ -12987,7 +12996,6 @@ function critiqueTool(providers, allowlist, bridge, storage, breakers, transcrip
12987
12996
  handler: (args) => runCritique(args, {
12988
12997
  providers,
12989
12998
  allowlist,
12990
- ...bridge ? { bridge } : {},
12991
12999
  ...storage ? { storage } : {},
12992
13000
  ...breakers ? { breakers } : {},
12993
13001
  ...transcriptsDir ? { transcriptsDir } : {},
@@ -12995,7 +13003,7 @@ function critiqueTool(providers, allowlist, bridge, storage, breakers, transcrip
12995
13003
  })
12996
13004
  };
12997
13005
  }
12998
- function planTool(providers, allowlist, bridge, storage, breakers, transcriptsDir, repoRoot) {
13006
+ function planTool(providers, allowlist, storage, breakers, transcriptsDir, repoRoot) {
12999
13007
  return {
13000
13008
  name: "plan",
13001
13009
  description: 'Have an LLM panel debate a step-by-step plan for the stated goal under the given constraints. Returns the debate envelope with a moderator-synthesised plan. Defaults to `mode: "fast"` (2 rounds + early_stop) \u2014 set `mode: "thorough"` for 5 rounds without early_stop. Explicit `max_rounds` / `early_stop` / `early_stop_threshold` always override the mode preset. Use `structured: true` for schema-validated synthesis.',
@@ -13022,7 +13030,6 @@ function planTool(providers, allowlist, bridge, storage, breakers, transcriptsDi
13022
13030
  handler: (args) => runPlan(args, {
13023
13031
  providers,
13024
13032
  allowlist,
13025
- ...bridge ? { bridge } : {},
13026
13033
  ...storage ? { storage } : {},
13027
13034
  ...breakers ? { breakers } : {},
13028
13035
  ...transcriptsDir ? { transcriptsDir } : {},
@@ -13030,7 +13037,7 @@ function planTool(providers, allowlist, bridge, storage, breakers, transcriptsDi
13030
13037
  })
13031
13038
  };
13032
13039
  }
13033
- function triangulateTool(providers, allowlist, bridge, storage, breakers, transcriptsDir, repoRoot) {
13040
+ function triangulateTool(providers, allowlist, storage, breakers, transcriptsDir, repoRoot) {
13034
13041
  return {
13035
13042
  name: "triangulate",
13036
13043
  description: "Run a coordinate flow and reshape the output as a consensus + minority report with per-provider weights. v1 uses 1.0 weights (matches a fresh provider_stats DB); future versions thread real win-rate weights when the DB layer ports.",
@@ -13051,7 +13058,6 @@ function triangulateTool(providers, allowlist, bridge, storage, breakers, transc
13051
13058
  handler: (args) => runTriangulate(args, {
13052
13059
  providers,
13053
13060
  allowlist,
13054
- ...bridge ? { bridge } : {},
13055
13061
  ...storage ? { storage } : {},
13056
13062
  ...breakers ? { breakers } : {},
13057
13063
  ...transcriptsDir ? { transcriptsDir } : {},
@@ -13059,7 +13065,7 @@ function triangulateTool(providers, allowlist, bridge, storage, breakers, transc
13059
13065
  })
13060
13066
  };
13061
13067
  }
13062
- function coordinateTool(providers, allowlist, bridge, innerCallers, storage, breakers, transcriptsDir, repoRoot, workerToolsCfg) {
13068
+ function coordinateTool(providers, allowlist, innerCallers, storage, breakers, transcriptsDir, repoRoot, workerToolsCfg) {
13063
13069
  return {
13064
13070
  name: "coordinate",
13065
13071
  description: "Run a three-role coordination flow (proposer \u2192 critics \u2192 synthesizer) with structured output at every step. Native opts: untrusted_input (canary + neutralize), worker_tools (bounded ReAct on proposer + critic roles; synth never sees inner tools), inject_session_memory (storage-backed working-memory block prepended to the topic).",
@@ -13084,7 +13090,6 @@ function coordinateTool(providers, allowlist, bridge, innerCallers, storage, bre
13084
13090
  handler: (args) => runCoordinate(args, {
13085
13091
  providers,
13086
13092
  allowlist,
13087
- ...bridge ? { bridge } : {},
13088
13093
  innerCallers,
13089
13094
  ...storage ? { storage } : {},
13090
13095
  ...breakers ? { breakers } : {},
@@ -13094,7 +13099,7 @@ function coordinateTool(providers, allowlist, bridge, innerCallers, storage, bre
13094
13099
  })
13095
13100
  };
13096
13101
  }
13097
- function debateTool(providers, allowlist, bridge, innerCallers, storage, pricing, breakers, transcriptsDir, repoRoot, workerToolsCfg) {
13102
+ function debateTool(providers, allowlist, innerCallers, storage, pricing, breakers, transcriptsDir, repoRoot, workerToolsCfg) {
13098
13103
  return {
13099
13104
  name: "debate",
13100
13105
  description: "Run a multi-round debate between LLM providers and synthesise the result via a moderator. Native opts: inject_session_memory, auto_panel, worker_tools, structured synthesis (schema-validated), extract_claims (final-round distillation), early_stop (post-round agreement check).",
@@ -13124,7 +13129,6 @@ function debateTool(providers, allowlist, bridge, innerCallers, storage, pricing
13124
13129
  handler: (args) => runDebate(args, {
13125
13130
  providers,
13126
13131
  allowlist,
13127
- ...bridge ? { bridge } : {},
13128
13132
  innerCallers,
13129
13133
  ...storage ? { storage } : {},
13130
13134
  ...pricing ? { pricing } : {},
@@ -13144,10 +13148,10 @@ function buildInnerCallers(o) {
13144
13148
  ...o.repoRoot ? { repoRoot: o.repoRoot } : {}
13145
13149
  });
13146
13150
  }
13147
- out.verify = (args) => runVerify(args, o.bridge, o.fetchConfig);
13151
+ out.verify = (args) => runVerify(args, o.fetchConfig);
13148
13152
  return out;
13149
13153
  }
13150
- function conferTool(providers, allowlist, bridge, moderator, pricing, storage, innerCallers, breakers, transcriptsDir, repoRoot, workerToolsCfg) {
13154
+ function conferTool(providers, allowlist, moderator, pricing, storage, innerCallers, breakers, transcriptsDir, repoRoot, workerToolsCfg) {
13151
13155
  return {
13152
13156
  name: "confer",
13153
13157
  description: "Ask a panel of LLM providers the same question; return one answer per provider. Native opts: untrusted_input (canary + neutralization), early_stop (cheap-tier agreement check after first 2 panelists when N>=3), extract_claims (cheap-tier extractor with per-provider support maps), inject_session_memory (storage-backed working-memory block prepended to the first user message), auto_panel (router-driven top-N panel pick), worker_tools (bounded ReAct loop per panelist when innerCallers are wired by the host).",
@@ -13174,7 +13178,6 @@ function conferTool(providers, allowlist, bridge, moderator, pricing, storage, i
13174
13178
  handler: (args) => runConfer(args, {
13175
13179
  providers,
13176
13180
  allowlist,
13177
- ...bridge ? { bridge } : {},
13178
13181
  moderator,
13179
13182
  ...pricing ? { pricing } : {},
13180
13183
  ...storage ? { storage } : {},
@@ -13186,7 +13189,7 @@ function conferTool(providers, allowlist, bridge, moderator, pricing, storage, i
13186
13189
  })
13187
13190
  };
13188
13191
  }
13189
- function auditTool(providers, allowlist, bridge, transcriptsDir, pricing, storage, breakers, repoRoot) {
13192
+ function auditTool(providers, allowlist, transcriptsDir, pricing, storage, breakers, repoRoot) {
13190
13193
  return {
13191
13194
  name: "audit",
13192
13195
  description: "Score a piece of output against an audit rubric. Single-judge by default; coalesce-mode (multi-judge consensus) and panel-exhausted self-audit run natively. cheap_mode picks the cheapest 'med'-tier model as auditor when a pricing.json is wired. session_id-only inputs load the latest transcript automatically when a transcripts directory is wired.",
@@ -13212,7 +13215,6 @@ function auditTool(providers, allowlist, bridge, transcriptsDir, pricing, storag
13212
13215
  handler: (args) => runAudit(args, {
13213
13216
  providers,
13214
13217
  allowlist,
13215
- ...bridge ? { bridge } : {},
13216
13218
  ...transcriptsDir ? { transcriptsDir } : {},
13217
13219
  ...pricing ? { pricing } : {},
13218
13220
  ...storage ? { storage } : {},
@@ -13276,7 +13278,7 @@ function pickTool(providers, allowlist, storage, breakers, transcriptsDir, repoR
13276
13278
  })
13277
13279
  };
13278
13280
  }
13279
- function verifyTool(bridge, fetchConfig) {
13281
+ function verifyTool(fetchConfig) {
13280
13282
  return {
13281
13283
  name: "verify",
13282
13284
  description: "Run a list of deterministic property checks against caller-supplied data. No LLM calls; everything is local. Returns per-check {passed, reason} plus all_passed / summary / timing fields. Supports the text kinds (contains, not_contains, regex_match, contains_any, contains_all, min_length), shell (sandboxed subprocess with timeout + Unix pgid isolation), and url_head (HEAD request gated by fetch.url_allowlist). All kinds run natively.",
@@ -13294,7 +13296,7 @@ function verifyTool(bridge, fetchConfig) {
13294
13296
  },
13295
13297
  required: ["checks"]
13296
13298
  },
13297
- handler: (args) => runVerify(args, bridge, fetchConfig)
13299
+ handler: (args) => runVerify(args, fetchConfig)
13298
13300
  };
13299
13301
  }
13300
13302
  function pingTool() {
@@ -13378,6 +13380,64 @@ async function flush() {
13378
13380
  }
13379
13381
  }
13380
13382
 
13383
+ // src/core/process-registry.ts
13384
+ import { mkdirSync as mkdirSync8, readdirSync as readdirSync5, readFileSync as readFileSync13, rmSync, writeFileSync as writeFileSync8 } from "fs";
13385
+ import os2 from "os";
13386
+ import path11 from "path";
13387
+ function registryDir(env = process.env) {
13388
+ const base = env["CROSSCHECK_DATA_DIR"] || path11.join(env["HOME"] ?? env["USERPROFILE"] ?? os2.homedir(), ".crosscheck");
13389
+ return path11.join(base, "procs");
13390
+ }
13391
+ function isAlive(pid) {
13392
+ try {
13393
+ process.kill(pid, 0);
13394
+ return true;
13395
+ } catch (err) {
13396
+ return err.code === "EPERM";
13397
+ }
13398
+ }
13399
+ function listProcesses(installedVersion, env = process.env, now = Date.now()) {
13400
+ const dir = registryDir(env);
13401
+ let files;
13402
+ try {
13403
+ files = readdirSync5(dir).filter((f) => f.endsWith(".json"));
13404
+ } catch {
13405
+ return [];
13406
+ }
13407
+ const out = [];
13408
+ for (const f of files) {
13409
+ let rec;
13410
+ try {
13411
+ rec = JSON.parse(readFileSync13(path11.join(dir, f), "utf8"));
13412
+ } catch {
13413
+ try {
13414
+ rmSync(path11.join(dir, f), { force: true });
13415
+ } catch {
13416
+ }
13417
+ continue;
13418
+ }
13419
+ if (typeof rec.pid !== "number") continue;
13420
+ const alive = isAlive(rec.pid);
13421
+ if (!alive) {
13422
+ try {
13423
+ rmSync(path11.join(dir, f), { force: true });
13424
+ } catch {
13425
+ }
13426
+ continue;
13427
+ }
13428
+ out.push({
13429
+ ...rec,
13430
+ alive,
13431
+ outdated: Boolean(rec.version) && rec.version !== installedVersion,
13432
+ ageMs: Math.max(0, now - (rec.startedAt ?? now))
13433
+ });
13434
+ }
13435
+ return out.sort((a, b) => a.startedAt - b.startedAt);
13436
+ }
13437
+ function processRegistryPath(env = process.env) {
13438
+ return registryDir(env);
13439
+ }
13440
+
13381
13441
  // src/entrypoints/embed.ts
13382
13442
  var KEY_ENV = {
13383
13443
  anthropic: "ANTHROPIC_API_KEY",
@@ -13405,6 +13465,107 @@ var ENGINE_TO_VAULT = { xai: "grok" };
13405
13465
  function vaultId(engineName) {
13406
13466
  return ENGINE_TO_VAULT[engineName] ?? engineName;
13407
13467
  }
13468
+ var ACTIVITY_TEXT_CAP = 8e3;
13469
+ var ASKED_CAP = 280;
13470
+ var runContext = new AsyncLocalStorage2();
13471
+ var SYNTHESIS_PURPOSES = /* @__PURE__ */ new Set(["synth", "moderator", "recombine", "judge", "unify"]);
13472
+ function exemptFromSkip(ctx, provider, purpose) {
13473
+ return provider === ctx.moderator && SYNTHESIS_PURPOSES.has(purpose);
13474
+ }
13475
+ function skippedError(provider, why, limitMs) {
13476
+ return new ProviderError(
13477
+ "client",
13478
+ why === "timeout" ? `${provider}: stopped after ${Math.round((limitMs ?? 0) / 1e3)}s (your per-model time limit)` : `${provider}: stopped waiting (you skipped it)`,
13479
+ { transient: false }
13480
+ );
13481
+ }
13482
+ function lastUserText(args) {
13483
+ const msgs = args.messages;
13484
+ for (let i = msgs.length - 1; i >= 0; i--) {
13485
+ const m = msgs[i];
13486
+ if (m && m.role === "user" && typeof m.content === "string") {
13487
+ const t = m.content.replace(/\s+/g, " ").trim();
13488
+ return t.length > ASKED_CAP ? `${t.slice(0, ASKED_CAP - 1)}\u2026` : t;
13489
+ }
13490
+ }
13491
+ return "";
13492
+ }
13493
+ function withActivity(p) {
13494
+ const send = p.send.bind(p);
13495
+ return {
13496
+ name: p.name,
13497
+ model: p.model,
13498
+ ...p.fallbackModels ? { fallbackModels: p.fallbackModels } : {},
13499
+ send: async (args) => {
13500
+ const ctx = runContext.getStore();
13501
+ if (!ctx) return send(args);
13502
+ const callId = ctx.nextCallId();
13503
+ const provider = vaultId(p.name);
13504
+ const model = args.modelOverride ?? p.model;
13505
+ const purpose = args.purpose ?? "worker";
13506
+ const started = Date.now();
13507
+ const exempt = exemptFromSkip(ctx, provider, purpose);
13508
+ if (!exempt && ctx.skipped.has(provider)) throw skippedError(provider, "user");
13509
+ ctx.emit({ type: "call.start", runId: ctx.runId, at: started, callId, provider, model, purpose, asked: lastUserText(args) });
13510
+ const controller = new AbortController();
13511
+ if (args.signal) {
13512
+ if (args.signal.aborted) controller.abort(args.signal.reason);
13513
+ else args.signal.addEventListener("abort", () => controller.abort(args.signal.reason), { once: true });
13514
+ }
13515
+ let stopped;
13516
+ if (!exempt) ctx.inflight.set(callId, { provider, purpose, controller });
13517
+ const timer = !exempt && ctx.callTimeoutMs && ctx.callTimeoutMs > 0 ? setTimeout(() => {
13518
+ stopped = "timeout";
13519
+ controller.abort("timeout");
13520
+ }, ctx.callTimeoutMs) : void 0;
13521
+ controller.signal.addEventListener("abort", () => {
13522
+ if (!stopped && controller.signal.reason === "skip") stopped = "user";
13523
+ }, { once: true });
13524
+ try {
13525
+ const res = await send({ ...args, signal: controller.signal });
13526
+ const text = typeof res.text === "string" ? res.text : "";
13527
+ ctx.emit({
13528
+ type: "call.done",
13529
+ runId: ctx.runId,
13530
+ at: Date.now(),
13531
+ callId,
13532
+ provider,
13533
+ model,
13534
+ purpose,
13535
+ ok: true,
13536
+ ms: Date.now() - started,
13537
+ text: text.slice(0, ACTIVITY_TEXT_CAP),
13538
+ truncated: text.length > ACTIVITY_TEXT_CAP,
13539
+ error: null,
13540
+ usage: usageOf(res)
13541
+ });
13542
+ return res;
13543
+ } catch (err) {
13544
+ const out = stopped ? skippedError(provider, stopped, ctx.callTimeoutMs) : err;
13545
+ ctx.emit({
13546
+ type: "call.done",
13547
+ runId: ctx.runId,
13548
+ at: Date.now(),
13549
+ callId,
13550
+ provider,
13551
+ model,
13552
+ purpose,
13553
+ ok: false,
13554
+ ms: Date.now() - started,
13555
+ text: "",
13556
+ truncated: false,
13557
+ error: out instanceof Error ? out.message : String(out),
13558
+ usage: null,
13559
+ ...stopped ? { stopped } : {}
13560
+ });
13561
+ throw out;
13562
+ } finally {
13563
+ if (timer) clearTimeout(timer);
13564
+ ctx.inflight.delete(callId);
13565
+ }
13566
+ }
13567
+ };
13568
+ }
13408
13569
  function usageOf(answer) {
13409
13570
  const u = answer["usage"];
13410
13571
  if (!u || typeof u !== "object") return null;
@@ -13434,12 +13595,14 @@ function errorOf(answer) {
13434
13595
  }
13435
13596
  function bundledPricingPath() {
13436
13597
  try {
13437
- return path11.join(path11.dirname(fileURLToPath2(import.meta.url)), "pricing.json");
13598
+ return path12.join(path12.dirname(fileURLToPath2(import.meta.url)), "pricing.json");
13438
13599
  } catch {
13439
13600
  return void 0;
13440
13601
  }
13441
13602
  }
13442
13603
  var runCounter = 0;
13604
+ var callCounter = 0;
13605
+ var nextCallId = () => ++callCounter;
13443
13606
  function nextRunId() {
13444
13607
  runCounter += 1;
13445
13608
  return `run_${Date.now().toString(36)}_${runCounter}`;
@@ -13458,7 +13621,9 @@ function createEmbeddedEngine(opts) {
13458
13621
  (p) => Boolean(p) && existsSync11(p)
13459
13622
  );
13460
13623
  const pricing = pricingPath ? loadPricing(pricingPath) : {};
13461
- const built = buildProviders({ env, pricing });
13624
+ const built = Object.fromEntries(
13625
+ Object.entries(buildProviders({ env, pricing })).map(([name, p]) => [name, withActivity(p)])
13626
+ );
13462
13627
  const moderator = opts.moderator ?? "anthropic";
13463
13628
  const tools = registerCoreTools({ providers: built, pricing });
13464
13629
  const providerIds = Object.keys(built).map(vaultId).sort();
@@ -13475,7 +13640,8 @@ function createEmbeddedEngine(opts) {
13475
13640
  if (!tool) return Promise.reject(new Error(`unknown tool: ${name}`));
13476
13641
  return Promise.resolve(tool.handler(args));
13477
13642
  };
13478
- const run = async (name, args, onEvent) => {
13643
+ const liveRuns = /* @__PURE__ */ new Map();
13644
+ const run = async (name, args, onEvent, runOpts = {}) => {
13479
13645
  const runId = nextRunId();
13480
13646
  const started = Date.now();
13481
13647
  const emit = (e) => {
@@ -13484,6 +13650,78 @@ function createEmbeddedEngine(opts) {
13484
13650
  } catch {
13485
13651
  }
13486
13652
  };
13653
+ const ctx = {
13654
+ runId,
13655
+ emit,
13656
+ nextCallId,
13657
+ skipped: /* @__PURE__ */ new Set(),
13658
+ inflight: /* @__PURE__ */ new Map(),
13659
+ moderator: vaultId(moderator),
13660
+ ...runOpts.callTimeoutMs && runOpts.callTimeoutMs > 0 ? { callTimeoutMs: runOpts.callTimeoutMs } : {}
13661
+ };
13662
+ liveRuns.set(runId, ctx);
13663
+ try {
13664
+ return await runContext.run(ctx, () => withProgress(runId, (n) => {
13665
+ if (n.params.message) emit({ type: "progress", runId, at: Date.now(), message: n.params.message });
13666
+ }, () => runInner(name, args, runId, started, emit)));
13667
+ } finally {
13668
+ liveRuns.delete(runId);
13669
+ }
13670
+ };
13671
+ const skip = (runId, provider) => {
13672
+ const ctx = liveRuns.get(runId);
13673
+ if (!ctx) return 0;
13674
+ ctx.skipped.add(provider);
13675
+ let stopped = 0;
13676
+ for (const call2 of ctx.inflight.values()) {
13677
+ if (call2.provider === provider && !exemptFromSkip(ctx, call2.provider, call2.purpose)) {
13678
+ call2.controller.abort("skip");
13679
+ stopped += 1;
13680
+ }
13681
+ }
13682
+ return stopped;
13683
+ };
13684
+ const ask = async (o) => {
13685
+ const engineName = Object.keys(built).find((n) => vaultId(n) === o.provider);
13686
+ const base = engineName ? built[engineName] : void 0;
13687
+ if (!base) {
13688
+ return { ok: false, text: "", json: null, model: o.model ?? "", error: `No ${o.provider} key is configured, so it can't be asked.` };
13689
+ }
13690
+ const provider = o.model && o.model !== base.model ? retargetProvider(base, o.model) : base;
13691
+ const maxTokens = o.maxTokens ?? 8e3;
13692
+ const doAsk = async () => {
13693
+ if (o.schema) {
13694
+ const res = await requestStructured(provider, o.messages, o.schema, { maxTokens, purpose: o.purpose, maxRetries: 1 });
13695
+ return {
13696
+ ok: res.obj !== null,
13697
+ text: res.answer.response ?? "",
13698
+ json: res.obj,
13699
+ error: res.obj !== null ? null : res.answer.error ?? res.errors.join("; ") ?? "invalid reply",
13700
+ model: provider.model
13701
+ };
13702
+ }
13703
+ const a = await askOne(provider, o.messages, { maxTokens, temperature: 0.3, purpose: o.purpose });
13704
+ return { ok: !a.error, text: a.response ?? "", json: null, error: a.error ?? null, model: provider.model };
13705
+ };
13706
+ if (!o.runId || !o.onEvent) return doAsk();
13707
+ const sink = o.onEvent;
13708
+ const emit = (e) => {
13709
+ try {
13710
+ sink(e);
13711
+ } catch {
13712
+ }
13713
+ };
13714
+ const ctx = {
13715
+ runId: o.runId,
13716
+ emit,
13717
+ nextCallId,
13718
+ skipped: /* @__PURE__ */ new Set(),
13719
+ inflight: /* @__PURE__ */ new Map(),
13720
+ moderator: o.provider
13721
+ };
13722
+ return runContext.run(ctx, doAsk);
13723
+ };
13724
+ const runInner = async (name, args, runId, started, emit) => {
13487
13725
  emit({
13488
13726
  type: "run.start",
13489
13727
  runId,
@@ -13561,8 +13799,16 @@ function createEmbeddedEngine(opts) {
13561
13799
  providers: providerIds,
13562
13800
  models,
13563
13801
  listTools: () => [...tools.keys()],
13802
+ requiredArgs: (name) => {
13803
+ const tool = tools.get(name);
13804
+ if (!tool) return null;
13805
+ const req = tool.inputSchema?.required;
13806
+ return Array.isArray(req) ? req.filter((x) => typeof x === "string") : [];
13807
+ },
13564
13808
  call,
13565
13809
  run,
13810
+ skip,
13811
+ ask,
13566
13812
  close: async () => {
13567
13813
  await flush().catch(() => {
13568
13814
  });
@@ -13571,12 +13817,16 @@ function createEmbeddedEngine(opts) {
13571
13817
  }
13572
13818
  var __test_internals = { usageOf, textOf, errorOf, vaultId };
13573
13819
  export {
13820
+ ACTIVITY_TEXT_CAP,
13574
13821
  DEFAULT_EXCLUDED_PROVIDERS,
13575
13822
  DEFAULT_FALLBACKS,
13576
13823
  DEFAULT_MODELS,
13577
13824
  KEY_ENV as __KEY_ENV,
13578
13825
  MODEL_ENV as __MODEL_ENV,
13579
13826
  __test_internals,
13580
- createEmbeddedEngine
13827
+ createEmbeddedEngine,
13828
+ isAlive,
13829
+ listProcesses,
13830
+ processRegistryPath
13581
13831
  };
13582
13832
  //# sourceMappingURL=embed.js.map