crosscheck-mcp 0.2.27 → 0.2.28

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.
@@ -958,8 +958,8 @@ __export(node_stdio_exports, {
958
958
  });
959
959
  module.exports = __toCommonJS(node_stdio_exports);
960
960
  init_cjs_shims();
961
- var import_node_fs19 = require("fs");
962
- var import_node_path18 = __toESM(require("path"), 1);
961
+ var import_node_fs20 = require("fs");
962
+ var import_node_path19 = __toESM(require("path"), 1);
963
963
  var import_node_url2 = require("url");
964
964
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
965
965
 
@@ -976,10 +976,10 @@ var StderrEmitter = class {
976
976
  }
977
977
  };
978
978
  var FileEmitter = class {
979
- constructor(path15) {
980
- this.path = path15;
979
+ constructor(path16) {
980
+ this.path = path16;
981
981
  try {
982
- (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path15), { recursive: true });
982
+ (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path16), { recursive: true });
983
983
  } catch {
984
984
  }
985
985
  }
@@ -1012,13 +1012,13 @@ var NullEmitter = class {
1012
1012
  function buildDefaultEmitter(env) {
1013
1013
  const mode = (env["CROSSCHECK_EVENTS"] ?? "stderr").toLowerCase();
1014
1014
  if (mode === "off") return new NullEmitter();
1015
- const path15 = env["CROSSCHECK_EVENTS_PATH"];
1015
+ const path16 = env["CROSSCHECK_EVENTS_PATH"];
1016
1016
  if (mode === "file") {
1017
- return path15 ? new FileEmitter(path15) : new NullEmitter();
1017
+ return path16 ? new FileEmitter(path16) : new NullEmitter();
1018
1018
  }
1019
1019
  if (mode === "both") {
1020
1020
  const ems = [new StderrEmitter()];
1021
- if (path15) ems.push(new FileEmitter(path15));
1021
+ if (path16) ems.push(new FileEmitter(path16));
1022
1022
  return new TeeEmitter(ems);
1023
1023
  }
1024
1024
  return new StderrEmitter();
@@ -1067,10 +1067,10 @@ function envelopeBytes(v) {
1067
1067
  // src/core/pricing.ts
1068
1068
  init_cjs_shims();
1069
1069
  var import_node_fs2 = require("fs");
1070
- function loadPricing(path15) {
1070
+ function loadPricing(path16) {
1071
1071
  let raw;
1072
1072
  try {
1073
- raw = (0, import_node_fs2.readFileSync)(path15, "utf8");
1073
+ raw = (0, import_node_fs2.readFileSync)(path16, "utf8");
1074
1074
  } catch {
1075
1075
  return {};
1076
1076
  }
@@ -1087,20 +1087,30 @@ function modelPricing(pricing, provider, model) {
1087
1087
  const block = pricing[provider];
1088
1088
  if (!block || typeof block !== "object" || Array.isArray(block)) return null;
1089
1089
  const table = block;
1090
+ if (model.startsWith("_")) return null;
1090
1091
  let entry = table[model];
1091
- if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
1092
- const best = Object.keys(table).filter((k) => model.startsWith(k)).sort((a, b) => b.length - a.length)[0];
1093
- entry = best !== void 0 ? table[best] : void 0;
1092
+ let match = "exact";
1093
+ if (!isRates(entry)) {
1094
+ 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];
1095
+ if (best !== void 0) {
1096
+ entry = table[best];
1097
+ match = "stamp";
1098
+ }
1094
1099
  }
1095
- if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
1100
+ if (!isRates(entry)) return null;
1096
1101
  const e = entry;
1097
1102
  return {
1098
1103
  prompt_per_1k: numberOrZero(e["prompt_per_1k"]),
1099
1104
  completion_per_1k: numberOrZero(e["completion_per_1k"]),
1100
1105
  cached_per_1k: numberOrZero(e["cached_per_1k"]),
1101
- ...e["estimated"] === true ? { estimated: true } : {}
1106
+ ...e["estimated"] === true ? { estimated: true } : {},
1107
+ match
1102
1108
  };
1103
1109
  }
1110
+ var DATE_STAMP = /^-\d{8}$/;
1111
+ function isRates(v) {
1112
+ return Boolean(v) && typeof v === "object" && !Array.isArray(v);
1113
+ }
1104
1114
  function numberOrZero(v) {
1105
1115
  if (typeof v === "number" && Number.isFinite(v)) return v;
1106
1116
  if (typeof v === "string") {
@@ -1240,8 +1250,20 @@ var PROVIDER_CAPS = {
1240
1250
  gemini: {
1241
1251
  family: "gemini",
1242
1252
  system_role: "separate",
1243
- supports_temperature: true,
1244
- reasoning_prefixes: ["gemini-3.1-pro", "gemini-3.5-flash", "gemini-2.5-pro"]
1253
+ // No sampling, on any Gemini model. Ignored since 3.6 Flash and rejected
1254
+ // by upcoming models (Google, 2026-10). "model" means "omit for reasoning
1255
+ // models", and every Gemini model is reasoning-class (below) — so this
1256
+ // resolves to never, through the same rule the other providers use. The
1257
+ // adapter also never sends it; this keeps the record honest for anything
1258
+ // else that reads it.
1259
+ supports_temperature: "model",
1260
+ // EVERY Gemini model is reasoning-class. The old list named three
1261
+ // families and missed gemini-3.8-flash — the DEFAULT — so the default
1262
+ // model got the small non-reasoning budgets (768 tokens for an audit)
1263
+ // while spending up to ~480 tokens of each call thinking. Google's docs
1264
+ // say thinking cannot be switched off on current models, so a list of
1265
+ // which ones think is a list that can only ever be incomplete.
1266
+ reasoning_prefixes: ["gemini-"]
1245
1267
  },
1246
1268
  // Qwen's "Max" tier (qwen-v3.8, qwen-v3.7) is a standard instruct/chat
1247
1269
  // model, not a visible-reasoning model like QwQ — normal temperature and
@@ -1366,6 +1388,74 @@ function httpFailureToProviderError(provider, status, bodyText, retryAfterS, mod
1366
1388
  );
1367
1389
  }
1368
1390
 
1391
+ // src/providers/network-errors.ts
1392
+ init_cjs_shims();
1393
+ function codeOf(e) {
1394
+ if (!e || typeof e !== "object") return null;
1395
+ const c = e.code;
1396
+ return typeof c === "string" && c.length > 0 ? c : null;
1397
+ }
1398
+ function rootCause(e, depth = 0) {
1399
+ if (depth > 5 || !e || typeof e !== "object") return e;
1400
+ const o = e;
1401
+ if (Array.isArray(o.errors) && o.errors.length > 0) {
1402
+ const coded = o.errors.find((x) => codeOf(x) !== null);
1403
+ return rootCause(coded ?? o.errors[0], depth + 1);
1404
+ }
1405
+ if (o.cause !== void 0 && o.cause !== null) return rootCause(o.cause, depth + 1);
1406
+ return e;
1407
+ }
1408
+ var BY_CODE = {
1409
+ ENOTFOUND: { cause: "dns", detail: "the provider's hostname did not resolve \u2014 DNS, or a typo'd base URL", transient: false, kind: "network" },
1410
+ EAI_AGAIN: { cause: "dns", detail: "DNS lookup failed temporarily \u2014 resolver or network is flapping", transient: true, kind: "network" },
1411
+ ECONNREFUSED: { cause: "refused", detail: "the connection was refused \u2014 nothing is listening, or a proxy blocked it", transient: false, kind: "network" },
1412
+ 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" },
1413
+ EPIPE: { cause: "reset", detail: "the connection closed while we were still writing", transient: true, kind: "network" },
1414
+ ETIMEDOUT: { cause: "connect_timeout", detail: "the connection timed out before it was established", transient: true, kind: "timeout" },
1415
+ EHOSTUNREACH: { cause: "unreachable", detail: "no route to the provider \u2014 VPN, or offline", transient: true, kind: "network" },
1416
+ ENETUNREACH: { cause: "unreachable", detail: "the network is unreachable \u2014 offline, or a routing change", transient: true, kind: "network" },
1417
+ ENETDOWN: { cause: "unreachable", detail: "the local network is down", transient: true, kind: "network" },
1418
+ EPROTO: { cause: "tls", detail: "the TLS handshake failed \u2014 an intercepting proxy, or a protocol mismatch", transient: false, kind: "network" },
1419
+ ERR_TLS_CERT_ALTNAME_INVALID: { cause: "tls", detail: "the certificate did not match the hostname \u2014 traffic is being intercepted", transient: false, kind: "network" },
1420
+ CERT_HAS_EXPIRED: { cause: "tls", detail: "the server certificate has expired, or this machine's clock is wrong", transient: false, kind: "network" },
1421
+ 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" },
1422
+ 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" },
1423
+ 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" },
1424
+ UND_ERR_BODY_TIMEOUT: { cause: "read_timeout", detail: "the response body stalled mid-stream", transient: true, kind: "timeout" },
1425
+ UND_ERR_SOCKET: { cause: "reset", detail: "the socket closed unexpectedly", transient: true, kind: "network" },
1426
+ UND_ERR_ABORTED: { cause: "aborted", detail: "the request was cancelled", transient: false, kind: "network" },
1427
+ ABORT_ERR: { cause: "aborted", detail: "the request was cancelled", transient: false, kind: "network" }
1428
+ };
1429
+ function diagnoseNetworkError(e) {
1430
+ const root = rootCause(e);
1431
+ const code = codeOf(root) ?? codeOf(e);
1432
+ if (code && BY_CODE[code]) return { ...BY_CODE[code], code };
1433
+ const name = root && typeof root === "object" ? root.name : void 0;
1434
+ if (name === "AbortError" || name === "TimeoutError") {
1435
+ return {
1436
+ cause: name === "TimeoutError" ? "read_timeout" : "aborted",
1437
+ code: code ?? String(name),
1438
+ detail: name === "TimeoutError" ? "the request exceeded its timeout" : "the request was cancelled",
1439
+ transient: name === "TimeoutError",
1440
+ kind: name === "TimeoutError" ? "timeout" : "network"
1441
+ };
1442
+ }
1443
+ return {
1444
+ cause: "unknown",
1445
+ code,
1446
+ detail: code ? `the connection failed with ${code}` : "the connection failed and the underlying error carried no code",
1447
+ transient: true,
1448
+ kind: "network"
1449
+ };
1450
+ }
1451
+ function looksLikeNetworkFailure(d) {
1452
+ return d.code !== null || d.cause !== "unknown";
1453
+ }
1454
+ function networkErrorMessage(provider, d) {
1455
+ const code = d.code ? ` [${d.code}]` : "";
1456
+ return `${provider}: ${d.detail}${code}`;
1457
+ }
1458
+
1369
1459
  // src/providers/retry.ts
1370
1460
  init_cjs_shims();
1371
1461
  var override = null;
@@ -1697,7 +1787,11 @@ async function sendAnthropic(args) {
1697
1787
  try {
1698
1788
  respLike = await doFetch(url, init);
1699
1789
  } catch (e) {
1700
- throw new ProviderError("network", `anthropic: fetch failed: ${e.message}`);
1790
+ const d = diagnoseNetworkError(e);
1791
+ if (!looksLikeNetworkFailure(d)) throw e;
1792
+ throw new ProviderError(d.kind, networkErrorMessage("anthropic", d), {
1793
+ transient: d.transient
1794
+ });
1701
1795
  }
1702
1796
  const status = respLike.status;
1703
1797
  if (status >= 200 && status < 300) {
@@ -1771,6 +1865,11 @@ function jsonSchemaToGeminiSchema(schema) {
1771
1865
  }
1772
1866
  return out;
1773
1867
  }
1868
+ var GEMINI_THINKING_HEADROOM_TOKENS = 16384;
1869
+ function thinkingHeadroom() {
1870
+ const raw = Number(process.env["CROSSCHECK_GEMINI_THINKING_HEADROOM_TOKENS"]);
1871
+ return Number.isFinite(raw) && raw >= 0 ? Math.trunc(raw) : GEMINI_THINKING_HEADROOM_TOKENS;
1872
+ }
1774
1873
  function buildGeminiRequest(opts) {
1775
1874
  const contents = [];
1776
1875
  let systemText = null;
@@ -1789,8 +1888,9 @@ function buildGeminiRequest(opts) {
1789
1888
  const body = {
1790
1889
  contents,
1791
1890
  generationConfig: {
1792
- maxOutputTokens: opts.maxTokens,
1793
- temperature: opts.temperature
1891
+ // Answer budget + thinking headroom. No temperature / topP / topK /
1892
+ // thinkingBudget, on any model — see the header note.
1893
+ maxOutputTokens: opts.maxTokens + thinkingHeadroom()
1794
1894
  }
1795
1895
  };
1796
1896
  if (systemText !== null && systemText !== "") {
@@ -1800,8 +1900,8 @@ function buildGeminiRequest(opts) {
1800
1900
  body.generationConfig.responseMimeType = "application/json";
1801
1901
  body.generationConfig.responseSchema = jsonSchemaToGeminiSchema(opts.jsonSchema);
1802
1902
  }
1803
- const url = `${GEMINI_API_URL_BASE}/${encodeURIComponent(opts.model)}:generateContent?key=${encodeURIComponent(opts.apiKey)}`;
1804
- return { url, headers: {}, body };
1903
+ const url = `${GEMINI_API_URL_BASE}/${encodeURIComponent(opts.model)}:generateContent`;
1904
+ return { url, headers: { "x-goog-api-key": opts.apiKey }, body };
1805
1905
  }
1806
1906
  function parseGeminiResponse(opts) {
1807
1907
  const r = opts.resp ?? {};
@@ -1831,16 +1931,19 @@ function parseGeminiResponse(opts) {
1831
1931
  }
1832
1932
  try {
1833
1933
  const first = cands[0];
1834
- const content = first["content"];
1835
- const parts = content["parts"];
1934
+ const finishReason = typeof first["finishReason"] === "string" ? first["finishReason"] : void 0;
1935
+ const content = first["content"] ?? {};
1936
+ const parts = Array.isArray(content["parts"]) ? content["parts"] : [];
1836
1937
  let text = "";
1837
1938
  for (const p of parts) {
1838
1939
  if (p && typeof p === "object") {
1839
- const t = p["text"];
1940
+ const part = p;
1941
+ if (part["thought"] === true) continue;
1942
+ const t = part["text"];
1840
1943
  if (typeof t === "string") text += t;
1841
1944
  }
1842
1945
  }
1843
- return { text, usage };
1946
+ return { text, usage, ...finishReason ? { finishReason } : {} };
1844
1947
  } catch (e) {
1845
1948
  throw new ProviderError(
1846
1949
  "parse",
@@ -1869,7 +1972,8 @@ async function sendGemini(args) {
1869
1972
  apiKey: args.apiKey,
1870
1973
  messages: args.messages,
1871
1974
  maxTokens: args.maxTokens,
1872
- temperature: args.temperature,
1975
+ // args.temperature is deliberately NOT forwarded: Gemini ignores it and
1976
+ // upcoming models reject it. See the header note.
1873
1977
  ...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
1874
1978
  });
1875
1979
  const doFetch = args.fetchImpl ?? globalThis.fetch;
@@ -1884,7 +1988,11 @@ async function sendGemini(args) {
1884
1988
  try {
1885
1989
  respLike = await doFetch(url, init);
1886
1990
  } catch (e) {
1887
- throw new ProviderError("network", `gemini: fetch failed: ${e.message}`);
1991
+ const d = diagnoseNetworkError(e);
1992
+ if (!looksLikeNetworkFailure(d)) throw e;
1993
+ throw new ProviderError(d.kind, networkErrorMessage("gemini", d), {
1994
+ transient: d.transient
1995
+ });
1888
1996
  }
1889
1997
  const status = respLike.status;
1890
1998
  if (status >= 200 && status < 300) {
@@ -1894,11 +2002,18 @@ async function sendGemini(args) {
1894
2002
  } catch (e) {
1895
2003
  throw new ProviderError("parse", `gemini: response body not JSON: ${e.message}`);
1896
2004
  }
1897
- const { text, usage } = parseGeminiResponse({
2005
+ const { text, usage, finishReason } = parseGeminiResponse({
1898
2006
  resp: parsed,
1899
2007
  model: args.model,
1900
2008
  purpose: args.purpose ?? "worker"
1901
2009
  });
2010
+ if (text.trim() === "" && finishReason === "MAX_TOKENS") {
2011
+ throw new ProviderError(
2012
+ "parse",
2013
+ `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.`,
2014
+ { transient: false }
2015
+ );
2016
+ }
1902
2017
  return { text, attempts: 1, usage: applyPricing2(usage, args.pricing) };
1903
2018
  }
1904
2019
  const bodyText = await respLike.text().catch(() => "");
@@ -2066,7 +2181,11 @@ async function sendOpenAICompatible(args) {
2066
2181
  try {
2067
2182
  respLike = await doFetch(url, init);
2068
2183
  } catch (e) {
2069
- throw new ProviderError("network", `${args.provider}: fetch failed: ${e.message}`);
2184
+ const d = diagnoseNetworkError(e);
2185
+ if (!looksLikeNetworkFailure(d)) throw e;
2186
+ throw new ProviderError(d.kind, networkErrorMessage(args.provider, d), {
2187
+ transient: d.transient
2188
+ });
2070
2189
  }
2071
2190
  const status = respLike.status;
2072
2191
  if (status >= 200 && status < 300) {
@@ -2251,50 +2370,6 @@ init_cjs_shims();
2251
2370
  var import_server = require("@modelcontextprotocol/sdk/server/index.js");
2252
2371
  var import_types9 = require("@modelcontextprotocol/sdk/types.js");
2253
2372
 
2254
- // src/bridge/index.ts
2255
- init_cjs_shims();
2256
-
2257
- // src/bridge/proxy-tools.ts
2258
- init_cjs_shims();
2259
- function buildPythonProxies(bridge) {
2260
- const proxies = /* @__PURE__ */ new Map();
2261
- for (const name of bridge.toolNames) {
2262
- proxies.set(name, makeProxy(bridge, name));
2263
- }
2264
- return proxies;
2265
- }
2266
- function makeProxy(bridge, name) {
2267
- return {
2268
- name,
2269
- description: `(forwarded to Python crosscheck-agent) ${name}`,
2270
- // We don't carry the per-tool input schema across the bridge in
2271
- // route-all mode — the TS server is a transparent forwarder, so
2272
- // arg validation happens on the Python side. inputSchema is left
2273
- // permissive; the Python validator throws structured errors that
2274
- // tunnel back through `callTool()`.
2275
- inputSchema: {
2276
- type: "object",
2277
- additionalProperties: true,
2278
- description: `Forwarded to Python \u2014 see Python tool '${name}' for full schema.`
2279
- },
2280
- handler: async (args) => {
2281
- const r = await bridge.callTool(name, args);
2282
- if (!r.content.length) {
2283
- return {};
2284
- }
2285
- const first = r.content[0];
2286
- if (first.type === "text" && first.text) {
2287
- try {
2288
- return JSON.parse(first.text);
2289
- } catch {
2290
- return { text: first.text };
2291
- }
2292
- }
2293
- return { content: r.content };
2294
- }
2295
- };
2296
- }
2297
-
2298
2373
  // src/instructions.ts
2299
2374
  init_cjs_shims();
2300
2375
  var ROUTABLE_TOOLS = [
@@ -2381,7 +2456,7 @@ var import_zod = require("zod");
2381
2456
  // src/server-meta.ts
2382
2457
  init_cjs_shims();
2383
2458
  var SERVER_NAME = "crosscheck-agent";
2384
- var SERVER_VERSION = true ? "0.2.27" : "0.0.0-dev";
2459
+ var SERVER_VERSION = true ? "0.2.28" : "0.0.0-dev";
2385
2460
 
2386
2461
  // src/tools/audit.ts
2387
2462
  init_cjs_shims();
@@ -2448,27 +2523,27 @@ function extractJson(text) {
2448
2523
 
2449
2524
  // src/core/json-schema.ts
2450
2525
  init_cjs_shims();
2451
- function validateSchema(value, schema, path15 = "") {
2526
+ function validateSchema(value, schema, path16 = "") {
2452
2527
  const errs = [];
2453
2528
  if ("anyOf" in schema) {
2454
2529
  const subs = schema["anyOf"].map(
2455
- (s) => validateSchema(value, s, path15)
2530
+ (s) => validateSchema(value, s, path16)
2456
2531
  );
2457
2532
  if (!subs.some((e) => e.length === 0)) {
2458
- errs.push(`${path15 || "<root>"}: did not match anyOf`);
2533
+ errs.push(`${path16 || "<root>"}: did not match anyOf`);
2459
2534
  }
2460
2535
  return errs;
2461
2536
  }
2462
2537
  if ("oneOf" in schema) {
2463
- const passed = schema["oneOf"].filter((s) => validateSchema(value, s, path15).length === 0).length;
2538
+ const passed = schema["oneOf"].filter((s) => validateSchema(value, s, path16).length === 0).length;
2464
2539
  if (passed !== 1) {
2465
- errs.push(`${path15 || "<root>"}: matched ${passed} of oneOf, expected 1`);
2540
+ errs.push(`${path16 || "<root>"}: matched ${passed} of oneOf, expected 1`);
2466
2541
  }
2467
2542
  return errs;
2468
2543
  }
2469
2544
  if ("const" in schema && !deepEqual(value, schema["const"])) {
2470
2545
  errs.push(
2471
- `${path15 || "<root>"}: expected const ${pyRepr(schema["const"])}, got ${pyRepr(value)}`
2546
+ `${path16 || "<root>"}: expected const ${pyRepr(schema["const"])}, got ${pyRepr(value)}`
2472
2547
  );
2473
2548
  return errs;
2474
2549
  }
@@ -2478,7 +2553,7 @@ function validateSchema(value, schema, path15 = "") {
2478
2553
  const ok = types.some((tt) => matchesType(value, String(tt)));
2479
2554
  if (!ok) {
2480
2555
  errs.push(
2481
- `${path15 || "<root>"}: expected type ${pyReprType(t)}, got ${pyTypeName(value)}`
2556
+ `${path16 || "<root>"}: expected type ${pyReprType(t)}, got ${pyTypeName(value)}`
2482
2557
  );
2483
2558
  return errs;
2484
2559
  }
@@ -2487,29 +2562,29 @@ function validateSchema(value, schema, path15 = "") {
2487
2562
  if ("enum" in schema) {
2488
2563
  const en = schema["enum"];
2489
2564
  if (!en.some((x) => deepEqual(x, value))) {
2490
- errs.push(`${path15 || "<root>"}: value ${pyRepr(value)} not in enum`);
2565
+ errs.push(`${path16 || "<root>"}: value ${pyRepr(value)} not in enum`);
2491
2566
  }
2492
2567
  }
2493
2568
  if ("minLength" in schema && value.length < schema["minLength"]) {
2494
- errs.push(`${path15 || "<root>"}: shorter than minLength ${schema["minLength"]}`);
2569
+ errs.push(`${path16 || "<root>"}: shorter than minLength ${schema["minLength"]}`);
2495
2570
  }
2496
2571
  }
2497
2572
  if (typeof value === "number" && !Number.isNaN(value)) {
2498
2573
  if ("minimum" in schema && value < schema["minimum"]) {
2499
- errs.push(`${path15 || "<root>"}: ${value} < minimum ${schema["minimum"]}`);
2574
+ errs.push(`${path16 || "<root>"}: ${value} < minimum ${schema["minimum"]}`);
2500
2575
  }
2501
2576
  if ("maximum" in schema && value > schema["maximum"]) {
2502
- errs.push(`${path15 || "<root>"}: ${value} > maximum ${schema["maximum"]}`);
2577
+ errs.push(`${path16 || "<root>"}: ${value} > maximum ${schema["maximum"]}`);
2503
2578
  }
2504
2579
  }
2505
2580
  if (Array.isArray(value)) {
2506
2581
  if ("minItems" in schema && value.length < schema["minItems"]) {
2507
- errs.push(`${path15 || "<root>"}: fewer items than minItems ${schema["minItems"]}`);
2582
+ errs.push(`${path16 || "<root>"}: fewer items than minItems ${schema["minItems"]}`);
2508
2583
  }
2509
2584
  const itemSchema = schema["items"];
2510
2585
  if (isObj(itemSchema)) {
2511
2586
  for (let i = 0; i < value.length; i++) {
2512
- errs.push(...validateSchema(value[i], itemSchema, `${path15}[${i}]`));
2587
+ errs.push(...validateSchema(value[i], itemSchema, `${path16}[${i}]`));
2513
2588
  }
2514
2589
  }
2515
2590
  }
@@ -2518,19 +2593,19 @@ function validateSchema(value, schema, path15 = "") {
2518
2593
  const required = schema["required"] ?? [];
2519
2594
  for (const r of required) {
2520
2595
  if (!(r in value)) {
2521
- errs.push(`${path15 || "<root>"}: missing required key ${pyRepr(r)}`);
2596
+ errs.push(`${path16 || "<root>"}: missing required key ${pyRepr(r)}`);
2522
2597
  }
2523
2598
  }
2524
2599
  if (schema["additionalProperties"] === false) {
2525
2600
  for (const k of Object.keys(value)) {
2526
2601
  if (!(k in props)) {
2527
- errs.push(`${path15 || "<root>"}: unknown key ${pyRepr(k)}`);
2602
+ errs.push(`${path16 || "<root>"}: unknown key ${pyRepr(k)}`);
2528
2603
  }
2529
2604
  }
2530
2605
  }
2531
2606
  for (const [k, v] of Object.entries(value)) {
2532
2607
  const ps = props[k];
2533
- if (ps) errs.push(...validateSchema(v, ps, path15 ? `${path15}.${k}` : k));
2608
+ if (ps) errs.push(...validateSchema(v, ps, path16 ? `${path16}.${k}` : k));
2534
2609
  }
2535
2610
  }
2536
2611
  return errs;
@@ -2612,6 +2687,34 @@ function pyReprType(t) {
2612
2687
 
2613
2688
  // src/core/usage.ts
2614
2689
  init_cjs_shims();
2690
+
2691
+ // src/core/session.ts
2692
+ init_cjs_shims();
2693
+ var import_node_crypto2 = require("crypto");
2694
+ var processSessionId = null;
2695
+ var startedAt = null;
2696
+ function engineSessionId() {
2697
+ if (processSessionId === null) {
2698
+ processSessionId = (0, import_node_crypto2.randomUUID)();
2699
+ startedAt = Date.now();
2700
+ }
2701
+ return processSessionId;
2702
+ }
2703
+ function resolveSessionId(callerSupplied) {
2704
+ const trimmed = typeof callerSupplied === "string" ? callerSupplied.trim() : "";
2705
+ return trimmed.length > 0 ? trimmed : engineSessionId();
2706
+ }
2707
+ function localSessionRecordingEnabled(env = globalThis.process?.env ?? {}) {
2708
+ const v = (env["CROSSCHECK_RECORD_SESSIONS"] ?? "").trim().toLowerCase();
2709
+ return !["0", "false", "off", "no"].includes(v);
2710
+ }
2711
+ function recordingSessionId(callerSupplied) {
2712
+ if (localSessionRecordingEnabled()) return resolveSessionId(callerSupplied);
2713
+ const trimmed = typeof callerSupplied === "string" ? callerSupplied.trim() : "";
2714
+ return trimmed.length > 0 ? trimmed : null;
2715
+ }
2716
+
2717
+ // src/core/usage.ts
2615
2718
  function emptyUsage(provider, model, purpose = "worker") {
2616
2719
  return {
2617
2720
  provider,
@@ -2716,11 +2819,11 @@ function withCommas(n) {
2716
2819
  function renderRunSummary(opts) {
2717
2820
  const endedAt = opts.endedAtS ?? Math.trunc(Date.now() / 1e3);
2718
2821
  let scope = "call";
2719
- let startedAt = null;
2822
+ let startedAt2 = null;
2720
2823
  let rows = [];
2721
2824
  if (opts.sessionScope && opts.sessionScope.rows.length > 0) {
2722
2825
  scope = "session";
2723
- startedAt = opts.sessionScope.startedAt;
2826
+ startedAt2 = opts.sessionScope.startedAt;
2724
2827
  rows = opts.sessionScope.rows.map((r) => ({ ...r }));
2725
2828
  } else {
2726
2829
  const byPurpose = /* @__PURE__ */ new Map();
@@ -2751,6 +2854,7 @@ function renderRunSummary(opts) {
2751
2854
  row.cpu_ms += a.cpu_ms ?? 0;
2752
2855
  if (a.cache_hit) row.cache_hits += 1;
2753
2856
  if (a.error) row.errors += 1;
2857
+ if (a.usage?.estimated) row.estimated = true;
2754
2858
  }
2755
2859
  rows = Array.from(byPurpose.values());
2756
2860
  }
@@ -2763,16 +2867,18 @@ function renderRunSummary(opts) {
2763
2867
  wall_ms: rows.reduce((s, r) => s + r.wall_ms, 0),
2764
2868
  cpu_ms: rows.reduce((s, r) => s + r.cpu_ms, 0),
2765
2869
  cache_hits: rows.reduce((s, r) => s + r.cache_hits, 0),
2766
- errors: rows.reduce((s, r) => s + r.errors, 0)
2870
+ errors: rows.reduce((s, r) => s + r.errors, 0),
2871
+ ...rows.some((r) => r.estimated) ? { estimated: true } : {}
2767
2872
  };
2768
2873
  const titleLeft = scope === "session" ? `session: ${opts.sessionId ?? ""}` : `call: ${opts.toolName}`;
2769
- 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)`;
2874
+ const costText = totals.estimated ? `$${totals.cost_usd.toFixed(4)}+ (some calls unpriced)` : `$${totals.cost_usd.toFixed(4)}`;
2875
+ 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)`;
2770
2876
  const bodyLines = [];
2771
2877
  for (let i = 0; i < rows.length; i++) {
2772
2878
  const r = rows[i];
2773
2879
  const glyph = i === rows.length - 1 ? "`-" : "|-";
2774
2880
  bodyLines.push(
2775
- `${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)`
2881
+ `${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)`
2776
2882
  );
2777
2883
  }
2778
2884
  const callByModel = rollupSpendByModel(
@@ -2820,7 +2926,7 @@ function renderRunSummary(opts) {
2820
2926
  tool: opts.toolName,
2821
2927
  scope,
2822
2928
  currency: "USD",
2823
- started_at: startedAt,
2929
+ started_at: startedAt2,
2824
2930
  ended_at: endedAt,
2825
2931
  rows,
2826
2932
  totals,
@@ -2945,14 +3051,15 @@ async function attachUsageBlock(result, answers, opts) {
2945
3051
  delete result["_suppress_run_summary"];
2946
3052
  }
2947
3053
  async function appendUsageLog(storage, sessionId, toolName, answers, nowS) {
2948
- if (!sessionId || answers.length === 0) return;
3054
+ const sid = recordingSessionId(sessionId);
3055
+ if (!sid || answers.length === 0) return;
2949
3056
  const now = nowS ?? Math.trunc(Date.now() / 1e3);
2950
3057
  const rows = [];
2951
3058
  for (const a of answers) {
2952
3059
  const u = a.usage;
2953
3060
  if (!u || typeof u.provider !== "string" || u.provider === "") continue;
2954
3061
  rows.push({
2955
- session_id: sessionId,
3062
+ session_id: sid,
2956
3063
  ts: now,
2957
3064
  tool: toolName,
2958
3065
  purpose: u.purpose ?? "worker",
@@ -2975,13 +3082,14 @@ async function appendUsageLog(storage, sessionId, toolName, answers, nowS) {
2975
3082
  }
2976
3083
  }
2977
3084
  async function recordSessionCall(storage, sessionId, answers, wallMs, cpuMs, nowS) {
2978
- if (!sessionId) return;
3085
+ const sid = recordingSessionId(sessionId);
3086
+ if (!sid) return;
2979
3087
  const now = nowS ?? Math.trunc(Date.now() / 1e3);
2980
3088
  try {
2981
- let existing = await storage.getSession(sessionId);
3089
+ let existing = await storage.getSession(sid);
2982
3090
  if (!existing) {
2983
3091
  await storage.upsertSession({
2984
- session_id: sessionId,
3092
+ session_id: sid,
2985
3093
  started_at: now,
2986
3094
  last_at: now,
2987
3095
  calls: 0,
@@ -2994,7 +3102,7 @@ async function recordSessionCall(storage, sessionId, answers, wallMs, cpuMs, now
2994
3102
  total_cost_usd: 0,
2995
3103
  total_cpu_ms: 0
2996
3104
  });
2997
- existing = await storage.getSession(sessionId);
3105
+ existing = await storage.getSession(sid);
2998
3106
  }
2999
3107
  if (!existing) return;
3000
3108
  let promptDelta = 0;
@@ -3014,7 +3122,7 @@ async function recordSessionCall(storage, sessionId, answers, wallMs, cpuMs, now
3014
3122
  }
3015
3123
  if (a.cache_hit) cacheHitDelta += 1;
3016
3124
  }
3017
- await storage.accumulateSessionTotals(sessionId, {
3125
+ await storage.accumulateSessionTotals(sid, {
3018
3126
  calls: answers.length,
3019
3127
  wall_ms: wallMs,
3020
3128
  cache_hits: cacheHitDelta,
@@ -3409,7 +3517,7 @@ function buildPersonaInjection(opts) {
3409
3517
 
3410
3518
  // src/core/canary.ts
3411
3519
  init_cjs_shims();
3412
- var import_node_crypto2 = require("crypto");
3520
+ var import_node_crypto3 = require("crypto");
3413
3521
 
3414
3522
  // src/core/injection.ts
3415
3523
  init_cjs_shims();
@@ -3428,8 +3536,8 @@ var UNTRUSTED_SYSTEM_NOTE = "Some inputs in this conversation are wrapped in <un
3428
3536
  function mintCanary() {
3429
3537
  const t = process.hrtime.bigint().toString();
3430
3538
  const pid = String(process.pid);
3431
- const r = (0, import_node_crypto2.randomBytes)(16).toString("hex");
3432
- const hex = (0, import_node_crypto2.createHash)("sha256").update(`${t}-${pid}-${r}`).digest("hex").slice(0, 16).toUpperCase();
3539
+ const r = (0, import_node_crypto3.randomBytes)(16).toString("hex");
3540
+ const hex = (0, import_node_crypto3.createHash)("sha256").update(`${t}-${pid}-${r}`).digest("hex").slice(0, 16).toUpperCase();
3433
3541
  return `CC_CANARY_${hex}`;
3434
3542
  }
3435
3543
  function wrapUntrusted(content, canary) {
@@ -3619,7 +3727,7 @@ var import_node_path3 = __toESM(require("path"), 1);
3619
3727
 
3620
3728
  // src/core/redact.ts
3621
3729
  init_cjs_shims();
3622
- var import_node_crypto3 = require("crypto");
3730
+ var import_node_crypto4 = require("crypto");
3623
3731
  function builtinRedactionRules() {
3624
3732
  return [
3625
3733
  { pattern: /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g, label: "EMAIL", prefix_group: false },
@@ -3646,7 +3754,7 @@ function hmacSuffix(cfg2, label, value) {
3646
3754
  const sid = cfg2.session_id ?? "";
3647
3755
  const sidBytes = Buffer.from(sid, "utf8");
3648
3756
  const key = Buffer.concat([Buffer.from(secret), sidBytes]);
3649
- const digest = (0, import_node_crypto3.createHmac)("sha256", key).update(`${label}:${value}`, "utf8").digest("hex");
3757
+ const digest = (0, import_node_crypto4.createHmac)("sha256", key).update(`${label}:${value}`, "utf8").digest("hex");
3650
3758
  return digest.slice(0, 8);
3651
3759
  }
3652
3760
  function redactText(s, cfg2) {
@@ -5983,10 +6091,6 @@ If you need supporting evidence, you may emit ONE \`<tool_call>...</tool_call>\`
5983
6091
  }
5984
6092
 
5985
6093
  // src/tools/confer.ts
5986
- var DEFERRED_OPTS = [
5987
- // empty — every confer opt runs natively except worker_tools, which
5988
- // is gated below (non-empty list → defer).
5989
- ];
5990
6094
  async function runConfer(args, opts) {
5991
6095
  const callStartedWall = import_node_perf_hooks2.performance.now();
5992
6096
  const callStartedCpu = process.cpuUsage();
@@ -6002,16 +6106,11 @@ async function runConfer(args, opts) {
6002
6106
  rejectedWorkerTools.push(t);
6003
6107
  }
6004
6108
  }
6005
- const workerToolsNeedsBridge = acceptedWorkerTools.length > 0 && !opts.innerCallers;
6006
- const needsBridge = DEFERRED_OPTS.some((k) => Boolean(args[k])) || workerToolsNeedsBridge;
6007
- if (needsBridge) {
6008
- if (opts.bridge && opts.bridge.toolNames.has("confer")) {
6009
- return await deferConfer(args, opts.bridge);
6010
- }
6109
+ if (acceptedWorkerTools.length > 0 && !opts.innerCallers) {
6011
6110
  return errorEnvelope2(
6012
6111
  "CONFER_OPT_NOT_NATIVE",
6013
6112
  "worker_tools requested but innerCallers are not wired",
6014
- "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)."
6113
+ "Native confer covers untrusted_input + early_stop + extract_claims + inject_session_memory + auto_panel. worker_tools needs innerCallers wired by the host."
6015
6114
  );
6016
6115
  }
6017
6116
  const question = typeof args["question"] === "string" ? args["question"] : String(args["question"] ?? "");
@@ -6362,21 +6461,6 @@ function unknownProviderError(unknownNames, available) {
6362
6461
  available_now: Object.keys(available).sort()
6363
6462
  };
6364
6463
  }
6365
- async function deferConfer(args, bridge) {
6366
- const r = await bridge.callTool("confer", args);
6367
- const text = r.content[0]?.text;
6368
- if (typeof text === "string") {
6369
- try {
6370
- return JSON.parse(text);
6371
- } catch {
6372
- }
6373
- }
6374
- return errorEnvelope2(
6375
- "CONFER_BRIDGE_BAD_ENVELOPE",
6376
- "bridge returned an unparseable envelope for confer",
6377
- "Check that the Python child is healthy."
6378
- );
6379
- }
6380
6464
  function errorEnvelope2(code, message, hint) {
6381
6465
  return {
6382
6466
  tool: "confer",
@@ -6752,7 +6836,7 @@ function errorEnvelope3(code, message, hint) {
6752
6836
  transient: false
6753
6837
  };
6754
6838
  }
6755
- async function runVerify(args, bridge, fetchConfig) {
6839
+ async function runVerify(args, fetchConfig) {
6756
6840
  const checks = args["checks"];
6757
6841
  if (!Array.isArray(checks) || checks.length === 0) {
6758
6842
  return {
@@ -6764,7 +6848,6 @@ async function runVerify(args, bridge, fetchConfig) {
6764
6848
  )
6765
6849
  };
6766
6850
  }
6767
- void bridge;
6768
6851
  const allowShell = Boolean(args["allow_shell"] ?? false);
6769
6852
  const wallStart = import_node_perf_hooks3.performance.now();
6770
6853
  const cpuStart = process.cpuUsage();
@@ -7066,7 +7149,6 @@ async function runBench(args, opts) {
7066
7149
  const innerOpts = {
7067
7150
  providers: opts.providers,
7068
7151
  allowlist: opts.allowlist ?? null,
7069
- ...opts.bridge ? { bridge: opts.bridge } : {},
7070
7152
  ...opts.moderator ? { moderator: opts.moderator } : {}
7071
7153
  };
7072
7154
  let inner;
@@ -7279,7 +7361,7 @@ function isObj4(v) {
7279
7361
 
7280
7362
  // src/tools/config-pin.ts
7281
7363
  init_cjs_shims();
7282
- var import_node_crypto4 = require("crypto");
7364
+ var import_node_crypto5 = require("crypto");
7283
7365
  var import_node_fs7 = require("fs");
7284
7366
  var import_node_path7 = __toESM(require("path"), 1);
7285
7367
  var DEFAULT_PIN_PATH = ".crosscheck/config_pins.json";
@@ -7434,7 +7516,7 @@ function hashFile(abs) {
7434
7516
  return "";
7435
7517
  }
7436
7518
  const normalized = stripCrBeforeLf(raw);
7437
- return "sha256:" + (0, import_node_crypto4.createHash)("sha256").update(normalized).digest("hex");
7519
+ return "sha256:" + (0, import_node_crypto5.createHash)("sha256").update(normalized).digest("hex");
7438
7520
  }
7439
7521
  function stripCrBeforeLf(buf) {
7440
7522
  const out = [];
@@ -7512,7 +7594,7 @@ function pyRepr2(v) {
7512
7594
 
7513
7595
  // src/tools/create.ts
7514
7596
  init_cjs_shims();
7515
- var import_node_crypto7 = require("crypto");
7597
+ var import_node_crypto8 = require("crypto");
7516
7598
  var import_node_fs10 = require("fs");
7517
7599
  var import_node_path10 = __toESM(require("path"), 1);
7518
7600
 
@@ -7615,7 +7697,7 @@ function isDeadModelError(err) {
7615
7697
 
7616
7698
  // src/core/node-cache.ts
7617
7699
  init_cjs_shims();
7618
- var import_node_crypto5 = require("crypto");
7700
+ var import_node_crypto6 = require("crypto");
7619
7701
  var import_node_fs8 = require("fs");
7620
7702
  var import_node_path8 = require("path");
7621
7703
  var NODE_CACHE_KEY_VERSION = "v2";
@@ -7642,7 +7724,7 @@ function canonicalizeNodeText(s) {
7642
7724
  return out;
7643
7725
  }
7644
7726
  function sha256Hex(s) {
7645
- return (0, import_node_crypto5.createHash)("sha256").update(s, "utf8").digest("hex");
7727
+ return (0, import_node_crypto6.createHash)("sha256").update(s, "utf8").digest("hex");
7646
7728
  }
7647
7729
  function nodeCacheKey(node, upstreamOutputs, provider, model) {
7648
7730
  const canonUpstream = {};
@@ -7700,11 +7782,11 @@ function getNodeCache(cfg2, repoRoot, key, nowMs) {
7700
7782
  return null;
7701
7783
  }
7702
7784
  }
7703
- function atomicWriteJson(path15, payload) {
7704
- (0, import_node_fs8.mkdirSync)((0, import_node_path8.dirname)(path15), { recursive: true });
7705
- const tmp = `${path15}.${process.pid}.${Date.now()}.tmp`;
7785
+ function atomicWriteJson(path16, payload) {
7786
+ (0, import_node_fs8.mkdirSync)((0, import_node_path8.dirname)(path16), { recursive: true });
7787
+ const tmp = `${path16}.${process.pid}.${Date.now()}.tmp`;
7706
7788
  (0, import_node_fs8.writeFileSync)(tmp, JSON.stringify(payload), "utf8");
7707
- (0, import_node_fs8.renameSync)(tmp, path15);
7789
+ (0, import_node_fs8.renameSync)(tmp, path16);
7708
7790
  }
7709
7791
  function putNodeCache(cfg2, repoRoot, key, value) {
7710
7792
  if (!cfg2 || cfg2.enabled === false) return;
@@ -7762,7 +7844,6 @@ var EST_TOKENS = {
7762
7844
  med: [1500, 800],
7763
7845
  high: [2500, 1500]
7764
7846
  };
7765
- var DEFERRED_OPTS2 = [];
7766
7847
  var REACTIVE_SIGNAL_RE = /<signals>\s*(\{[\s\S]*?\})\s*<\/signals>/g;
7767
7848
  function parseReactiveSignals(output) {
7768
7849
  if (typeof output !== "string" || !output.includes("<signals>")) return [];
@@ -7802,17 +7883,6 @@ async function runOrchestrate(args, opts) {
7802
7883
  );
7803
7884
  if (breakerEnv) return breakerEnv;
7804
7885
  }
7805
- const needsBridge = DEFERRED_OPTS2.some((k) => Boolean(args[k]));
7806
- if (needsBridge) {
7807
- if (opts.bridge && opts.bridge.toolNames.has("orchestrate")) {
7808
- return await deferOrchestrate(args, opts.bridge);
7809
- }
7810
- return errorEnvelope5(
7811
- "ORCHESTRATE_OPT_NOT_NATIVE",
7812
- "this orchestrate opt-in requires the Python bridge in v1",
7813
- "v1 native covers the plain DAG flow + cheap_mode. reactive (mid-flight DAG updates) still defers. Set CROSSCHECK_BRIDGE_PYTHON=1 to enable."
7814
- );
7815
- }
7816
7886
  const goal = typeof args["goal"] === "string" ? args["goal"] : null;
7817
7887
  const dagIn = isObj5(args["dag"]) ? args["dag"] : null;
7818
7888
  if (goal === null === (dagIn === null)) {
@@ -8754,21 +8824,6 @@ function resolveProviders3(names, available, allowlist) {
8754
8824
  }
8755
8825
  return out;
8756
8826
  }
8757
- async function deferOrchestrate(args, bridge) {
8758
- const r = await bridge.callTool("orchestrate", args);
8759
- const text = r.content[0]?.text;
8760
- if (typeof text === "string") {
8761
- try {
8762
- return JSON.parse(text);
8763
- } catch {
8764
- }
8765
- }
8766
- return errorEnvelope5(
8767
- "ORCHESTRATE_BRIDGE_BAD_ENVELOPE",
8768
- "bridge returned an unparseable envelope for orchestrate",
8769
- "Check that the Python child is healthy."
8770
- );
8771
- }
8772
8827
  function errorEnvelope5(code, message, hint, kind = "client") {
8773
8828
  return {
8774
8829
  tool: "orchestrate",
@@ -8817,7 +8872,7 @@ function pyRound6(x) {
8817
8872
 
8818
8873
  // src/tools/fetch.ts
8819
8874
  init_cjs_shims();
8820
- var import_node_crypto6 = require("crypto");
8875
+ var import_node_crypto7 = require("crypto");
8821
8876
  var import_node_fs9 = require("fs");
8822
8877
  var import_node_path9 = __toESM(require("path"), 1);
8823
8878
  var DEFAULT_CONFIG = {
@@ -9031,10 +9086,10 @@ function extractHost(url) {
9031
9086
  }
9032
9087
  }
9033
9088
  function sha256Hex2(s) {
9034
- return (0, import_node_crypto6.createHash)("sha256").update(s, "utf8").digest("hex");
9089
+ return (0, import_node_crypto7.createHash)("sha256").update(s, "utf8").digest("hex");
9035
9090
  }
9036
9091
  function sha256HexBytes(bytes) {
9037
- return (0, import_node_crypto6.createHash)("sha256").update(bytes).digest("hex");
9092
+ return (0, import_node_crypto7.createHash)("sha256").update(bytes).digest("hex");
9038
9093
  }
9039
9094
  function resolveEvidenceDir(configured, repoRoot) {
9040
9095
  if (import_node_path9.default.isAbsolute(configured)) return configured;
@@ -9133,7 +9188,6 @@ ${documentsPayload}`;
9133
9188
  }, {
9134
9189
  providers: opts.providers,
9135
9190
  allowlist: opts.allowlist ?? null,
9136
- ...opts.bridge ? { bridge: opts.bridge } : {},
9137
9191
  ctx
9138
9192
  });
9139
9193
  const scopeAnswer = Array.isArray(scope["answers"]) ? scope["answers"].filter((a) => typeof a["response"] === "string" && a["response"]).map((a) => `[${a["provider"]}]
@@ -9156,7 +9210,6 @@ ${scopeAnswer || "(none)"}
9156
9210
  providers: opts.providers,
9157
9211
  allowlist: opts.allowlist ?? null,
9158
9212
  moderator,
9159
- ...opts.bridge ? { bridge: opts.bridge } : {},
9160
9213
  ctx
9161
9214
  });
9162
9215
  let attempts = 1;
@@ -9191,7 +9244,6 @@ ${scopeAnswer || "(none)"}
9191
9244
  }, {
9192
9245
  providers: opts.providers,
9193
9246
  allowlist: opts.allowlist ?? null,
9194
- ...opts.bridge ? { bridge: opts.bridge } : {},
9195
9247
  ctx
9196
9248
  });
9197
9249
  }
@@ -9214,7 +9266,6 @@ ${scopeAnswer || "(none)"}
9214
9266
  }, {
9215
9267
  providers: opts.providers,
9216
9268
  allowlist: opts.allowlist ?? null,
9217
- ...opts.bridge ? { bridge: opts.bridge } : {},
9218
9269
  ctx
9219
9270
  });
9220
9271
  let overallF = toNumberOrNull(auditEnvelope["overall_score"]);
@@ -9239,7 +9290,6 @@ ${documentsPayload}`
9239
9290
  providers: opts.providers,
9240
9291
  allowlist: opts.allowlist ?? null,
9241
9292
  moderator,
9242
- ...opts.bridge ? { bridge: opts.bridge } : {},
9243
9293
  ctx
9244
9294
  }
9245
9295
  );
@@ -9259,7 +9309,6 @@ ${documentsPayload}`
9259
9309
  }, {
9260
9310
  providers: opts.providers,
9261
9311
  allowlist: opts.allowlist ?? null,
9262
- ...opts.bridge ? { bridge: opts.bridge } : {},
9263
9312
  ctx
9264
9313
  });
9265
9314
  overallF = toNumberOrNull(auditEnvelope["overall_score"]);
@@ -9396,7 +9445,7 @@ async function ingestDocuments(documents, sessionId, opts) {
9396
9445
  source: ref,
9397
9446
  type: "file",
9398
9447
  status: "ok",
9399
- hash: (0, import_node_crypto7.createHash)("sha256").update(text, "utf8").digest("hex"),
9448
+ hash: (0, import_node_crypto8.createHash)("sha256").update(text, "utf8").digest("hex"),
9400
9449
  content: ""
9401
9450
  }, text));
9402
9451
  }
@@ -9433,7 +9482,7 @@ ${d.content}
9433
9482
  function makeSessionId(opts, supplied) {
9434
9483
  if (typeof supplied === "string" && supplied) return supplied;
9435
9484
  const stamp = opts.nowEpochSeconds ? opts.nowEpochSeconds() : Math.floor(Date.now() / 1e3);
9436
- const rnd = opts.randomBytes ? opts.randomBytes() : (0, import_node_crypto7.createHash)("sha256").update(`${stamp}-${process.pid}-${Math.random()}`).digest("hex");
9485
+ const rnd = opts.randomBytes ? opts.randomBytes() : (0, import_node_crypto8.createHash)("sha256").update(`${stamp}-${process.pid}-${Math.random()}`).digest("hex");
9437
9486
  const suffix = rnd.slice(0, 8);
9438
9487
  return `${opts.toolName}-${stamp}-${suffix}`;
9439
9488
  }
@@ -9498,11 +9547,6 @@ function errorEnvelope6(toolName, code, message, hint) {
9498
9547
  // src/tools/coordinate.ts
9499
9548
  init_cjs_shims();
9500
9549
  var import_node_perf_hooks7 = require("perf_hooks");
9501
- var DEFERRED_OPTS3 = [
9502
- // empty — every coordinate opt runs natively when its deps
9503
- // (storage / innerCallers) are wired. Without them the
9504
- // worker_tools gate still triggers a bridge defer.
9505
- ];
9506
9550
  var COORDINATE_DEFAULT_TEMP = 0.4;
9507
9551
  var ROLE_TURN_SCHEMA = {
9508
9552
  type: "object",
@@ -9557,23 +9601,18 @@ async function runCoordinate(args, opts) {
9557
9601
  rejectedWorkerTools.push(t);
9558
9602
  }
9559
9603
  }
9560
- const workerToolsNeedsBridge = acceptedWorkerTools.length > 0 && !opts.innerCallers;
9561
- const needsBridge = DEFERRED_OPTS3.some((k) => Boolean(args[k])) || workerToolsNeedsBridge;
9562
- if (needsBridge) {
9604
+ if (acceptedWorkerTools.length > 0 && !opts.innerCallers) {
9563
9605
  if (superRequested) {
9564
9606
  return errorEnvelope7(
9565
9607
  "COORDINATE_SUPER_NOT_NATIVE",
9566
- "super mode requested but worker_tools needs the Python bridge",
9567
- "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."
9608
+ "super mode requested with worker_tools, but innerCallers are not wired",
9609
+ "worker_tools needs innerCallers wired by the host. Drop worker_tools, or wire innerCallers, to use super."
9568
9610
  );
9569
9611
  }
9570
- if (opts.bridge && opts.bridge.toolNames.has("coordinate")) {
9571
- return await deferCoordinate(args, opts.bridge);
9572
- }
9573
9612
  return errorEnvelope7(
9574
9613
  "COORDINATE_OPT_NOT_NATIVE",
9575
9614
  "worker_tools requested but innerCallers are not wired",
9576
- "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)."
9615
+ "Native coordinate covers the three-role flow + untrusted_input + inject_session_memory. worker_tools needs innerCallers wired by the host."
9577
9616
  );
9578
9617
  }
9579
9618
  const untrusted = Boolean(args["untrusted_input"]);
@@ -9973,21 +10012,6 @@ function unknownProviderError3(unknownNames, available) {
9973
10012
  available_now: Object.keys(available).sort()
9974
10013
  };
9975
10014
  }
9976
- async function deferCoordinate(args, bridge) {
9977
- const r = await bridge.callTool("coordinate", args);
9978
- const text = r.content[0]?.text;
9979
- if (typeof text === "string") {
9980
- try {
9981
- return JSON.parse(text);
9982
- } catch {
9983
- }
9984
- }
9985
- return errorEnvelope7(
9986
- "COORDINATE_BRIDGE_BAD_ENVELOPE",
9987
- "bridge returned an unparseable envelope for coordinate",
9988
- "Check that the Python child is healthy."
9989
- );
9990
- }
9991
10015
  function errorEnvelope7(code, message, hint) {
9992
10016
  return {
9993
10017
  tool: "coordinate",
@@ -10320,10 +10344,6 @@ function buildModeratorTranscript(transcript) {
10320
10344
 
10321
10345
  // src/tools/debate.ts
10322
10346
  var import_node_perf_hooks9 = require("perf_hooks");
10323
- var DEFERRED_OPTS4 = [
10324
- // empty — every debate opt runs natively when its deps
10325
- // (storage / innerCallers) are wired.
10326
- ];
10327
10347
  async function runDebate(args, opts) {
10328
10348
  const callStartedWall = import_node_perf_hooks9.performance.now();
10329
10349
  const callStartedCpu = process.cpuUsage();
@@ -10341,23 +10361,18 @@ async function runDebate(args, opts) {
10341
10361
  rejectedWorkerTools.push(t);
10342
10362
  }
10343
10363
  }
10344
- const workerToolsNeedsBridge = acceptedWorkerTools.length > 0 && !opts.innerCallers;
10345
- const needsBridge = DEFERRED_OPTS4.some((k) => Boolean(args[k])) || workerToolsNeedsBridge;
10346
- if (needsBridge) {
10364
+ if (acceptedWorkerTools.length > 0 && !opts.innerCallers) {
10347
10365
  if (superRequested) {
10348
10366
  return errorEnvelope9(
10349
10367
  "DEBATE_SUPER_NOT_NATIVE",
10350
- "super mode requested but worker_tools needs the Python bridge",
10351
- "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."
10368
+ "super mode requested with worker_tools, but innerCallers are not wired",
10369
+ "worker_tools needs innerCallers wired by the host. Drop worker_tools, or wire innerCallers, to use super."
10352
10370
  );
10353
10371
  }
10354
- if (opts.bridge && opts.bridge.toolNames.has("debate")) {
10355
- return await deferDebate(args, opts.bridge);
10356
- }
10357
10372
  return errorEnvelope9(
10358
10373
  "DEBATE_OPT_NOT_NATIVE",
10359
10374
  "worker_tools requested but innerCallers are not wired",
10360
- "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)."
10375
+ "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."
10361
10376
  );
10362
10377
  }
10363
10378
  const topic = typeof args["topic"] === "string" ? args["topic"] : String(args["topic"] ?? "");
@@ -10794,21 +10809,6 @@ function unknownProviderError5(unknownNames, available) {
10794
10809
  available_now: Object.keys(available).sort()
10795
10810
  };
10796
10811
  }
10797
- async function deferDebate(args, bridge) {
10798
- const r = await bridge.callTool("debate", args);
10799
- const text = r.content[0]?.text;
10800
- if (typeof text === "string") {
10801
- try {
10802
- return JSON.parse(text);
10803
- } catch {
10804
- }
10805
- }
10806
- return errorEnvelope9(
10807
- "DEBATE_BRIDGE_BAD_ENVELOPE",
10808
- "bridge returned an unparseable envelope for debate",
10809
- "Check that the Python child is healthy."
10810
- );
10811
- }
10812
10812
  function errorEnvelope9(code, message, hint) {
10813
10813
  return {
10814
10814
  tool: "debate",
@@ -10829,9 +10829,6 @@ var DEFAULT_DELEGATION_LIMITS = {
10829
10829
  };
10830
10830
  async function runDelegate(args, opts) {
10831
10831
  if (!opts.storage) {
10832
- if (opts.bridge && opts.bridge.toolNames.has("delegate")) {
10833
- return await deferDelegate(args, opts.bridge);
10834
- }
10835
10832
  return errorEnvelope10(
10836
10833
  "DELEGATE_STORAGE_UNAVAILABLE",
10837
10834
  "delegate needs the local database (for quota tracking), which isn't available",
@@ -10903,7 +10900,6 @@ async function runDelegate(args, opts) {
10903
10900
  const innerOpts = {
10904
10901
  providers: opts.providers,
10905
10902
  allowlist: opts.allowlist ?? null,
10906
- ...opts.bridge ? { bridge: opts.bridge } : {},
10907
10903
  ...opts.moderator ? { moderator: opts.moderator } : {}
10908
10904
  };
10909
10905
  let result;
@@ -10942,21 +10938,6 @@ async function record(storage, sessionId, requester, toolCall, via, accepted, no
10942
10938
  created_at: nowEpochSeconds
10943
10939
  });
10944
10940
  }
10945
- async function deferDelegate(args, bridge) {
10946
- const r = await bridge.callTool("delegate", args);
10947
- const text = r.content[0]?.text;
10948
- if (typeof text === "string") {
10949
- try {
10950
- return JSON.parse(text);
10951
- } catch {
10952
- }
10953
- }
10954
- return errorEnvelope10(
10955
- "DELEGATE_BRIDGE_BAD_ENVELOPE",
10956
- "bridge returned an unparseable envelope for delegate",
10957
- "Check that the Python child is healthy."
10958
- );
10959
- }
10960
10941
  function errorEnvelope10(code, message, hint) {
10961
10942
  return {
10962
10943
  tool: "delegate",
@@ -11000,9 +10981,6 @@ async function runExplain(args, opts) {
11000
10981
  );
11001
10982
  }
11002
10983
  if (!opts.storage) {
11003
- if (opts.bridge && opts.bridge.toolNames.has("explain")) {
11004
- return await deferExplain(args, opts.bridge);
11005
- }
11006
10984
  return errorEnvelope11(
11007
10985
  "EXPLAIN_STORAGE_UNAVAILABLE",
11008
10986
  "explain needs the local transcript/stats database, which isn't available",
@@ -11284,21 +11262,6 @@ function pyRepr5(v) {
11284
11262
  }
11285
11263
  return String(v);
11286
11264
  }
11287
- async function deferExplain(args, bridge) {
11288
- const r = await bridge.callTool("explain", args);
11289
- const text = r.content[0]?.text;
11290
- if (typeof text === "string") {
11291
- try {
11292
- return JSON.parse(text);
11293
- } catch {
11294
- }
11295
- }
11296
- return errorEnvelope11(
11297
- "EXPLAIN_BRIDGE_BAD_ENVELOPE",
11298
- "bridge returned an unparseable envelope for explain",
11299
- "Check that the Python child is healthy."
11300
- );
11301
- }
11302
11265
  function errorEnvelope11(code, message, hint, kind = "client", extra = {}) {
11303
11266
  return {
11304
11267
  tool: "explain",
@@ -11856,9 +11819,6 @@ async function runRecall(args, opts) {
11856
11819
  if (k < 1) k = 1;
11857
11820
  if (k > 50) k = 50;
11858
11821
  if (!opts.storage) {
11859
- if (opts.bridge && opts.bridge.toolNames.has("recall")) {
11860
- return await deferRecall(args, opts.bridge);
11861
- }
11862
11822
  return errorEnvelope12(
11863
11823
  "RECALL_STORAGE_UNAVAILABLE",
11864
11824
  "recall needs the local database, which isn't available",
@@ -11911,21 +11871,6 @@ async function runRecall(args, opts) {
11911
11871
  applied_filters: applied
11912
11872
  };
11913
11873
  }
11914
- async function deferRecall(args, bridge) {
11915
- const r = await bridge.callTool("recall", args);
11916
- const text = r.content[0]?.text;
11917
- if (typeof text === "string") {
11918
- try {
11919
- return JSON.parse(text);
11920
- } catch {
11921
- }
11922
- }
11923
- return errorEnvelope12(
11924
- "RECALL_BRIDGE_BAD_ENVELOPE",
11925
- "bridge returned an unparseable envelope for recall",
11926
- "Check that the Python child is healthy."
11927
- );
11928
- }
11929
11874
  function errorEnvelope12(code, message, hint) {
11930
11875
  return {
11931
11876
  tool: "recall",
@@ -11949,9 +11894,6 @@ async function runRecommendPanel(args, opts) {
11949
11894
  );
11950
11895
  }
11951
11896
  if (!opts.storage) {
11952
- if (opts.bridge && opts.bridge.toolNames.has("recommend_panel")) {
11953
- return await deferRecommendPanel(args, opts.bridge);
11954
- }
11955
11897
  return errorEnvelope13(
11956
11898
  "RECOMMEND_PANEL_STORAGE_UNAVAILABLE",
11957
11899
  "recommend_panel needs the local stats database, which isn't available",
@@ -12014,21 +11956,6 @@ async function runRecommendPanel(args, opts) {
12014
11956
  meta: result.meta
12015
11957
  };
12016
11958
  }
12017
- async function deferRecommendPanel(args, bridge) {
12018
- const r = await bridge.callTool("recommend_panel", args);
12019
- const text = r.content[0]?.text;
12020
- if (typeof text === "string") {
12021
- try {
12022
- return JSON.parse(text);
12023
- } catch {
12024
- }
12025
- }
12026
- return errorEnvelope13(
12027
- "RECOMMEND_PANEL_BRIDGE_BAD_ENVELOPE",
12028
- "bridge returned an unparseable envelope for recommend_panel",
12029
- "Check that the Python child is healthy."
12030
- );
12031
- }
12032
11959
  function errorEnvelope13(code, message, hint) {
12033
11960
  return {
12034
11961
  tool: "recommend_panel",
@@ -12059,9 +11986,6 @@ init_cjs_shims();
12059
11986
  var import_node_fs12 = require("fs");
12060
11987
  async function runScoreboard(args, opts) {
12061
11988
  if (!opts.storage) {
12062
- if (opts.bridge && opts.bridge.toolNames.has("scoreboard")) {
12063
- return await deferScoreboard(args, opts.bridge);
12064
- }
12065
11989
  return errorEnvelope14(
12066
11990
  "SCOREBOARD_STORAGE_UNAVAILABLE",
12067
11991
  "scoreboard needs the local stats database, which isn't available",
@@ -12184,21 +12108,6 @@ function clampInt3(raw, min, max, fallback) {
12184
12108
  if (!Number.isFinite(n)) return fallback;
12185
12109
  return Math.min(max, Math.max(min, Math.trunc(n)));
12186
12110
  }
12187
- async function deferScoreboard(args, bridge) {
12188
- const r = await bridge.callTool("scoreboard", args);
12189
- const text = r.content[0]?.text;
12190
- if (typeof text === "string") {
12191
- try {
12192
- return JSON.parse(text);
12193
- } catch {
12194
- }
12195
- }
12196
- return errorEnvelope14(
12197
- "SCOREBOARD_BRIDGE_BAD_ENVELOPE",
12198
- "bridge returned an unparseable envelope for scoreboard",
12199
- "Check that the Python child is healthy."
12200
- );
12201
- }
12202
12111
  function errorEnvelope14(code, message, hint) {
12203
12112
  return {
12204
12113
  tool: "scoreboard",
@@ -12236,9 +12145,6 @@ async function runSessionMemory(args, opts) {
12236
12145
  );
12237
12146
  }
12238
12147
  if (!opts.storage) {
12239
- if (opts.bridge && opts.bridge.toolNames.has("session_memory")) {
12240
- return await deferSessionMemory(args, opts.bridge);
12241
- }
12242
12148
  return errorEnvelope15(
12243
12149
  "SESSION_MEMORY_STORAGE_UNAVAILABLE",
12244
12150
  "session_memory needs the local database, which isn't available",
@@ -12343,21 +12249,6 @@ function clampInt4(raw, min, max, fallback) {
12343
12249
  if (!Number.isFinite(n)) return fallback;
12344
12250
  return Math.min(max, Math.max(min, Math.trunc(n)));
12345
12251
  }
12346
- async function deferSessionMemory(args, bridge) {
12347
- const r = await bridge.callTool("session_memory", args);
12348
- const text = r.content[0]?.text;
12349
- if (typeof text === "string") {
12350
- try {
12351
- return JSON.parse(text);
12352
- } catch {
12353
- }
12354
- }
12355
- return errorEnvelope15(
12356
- "SESSION_MEMORY_BRIDGE_BAD_ENVELOPE",
12357
- "bridge returned an unparseable envelope for session_memory",
12358
- "Check that the Python child is healthy."
12359
- );
12360
- }
12361
12252
  function errorEnvelope15(code, message, hint) {
12362
12253
  return {
12363
12254
  tool: "session_memory",
@@ -12981,7 +12872,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
12981
12872
  var DEFAULT_PACKAGE = "crosscheck-cli";
12982
12873
  var FETCH_TIMEOUT_MS = 3e3;
12983
12874
  function engineVersion() {
12984
- return true ? "0.2.27" : "0.0.0-dev";
12875
+ return true ? "0.2.28" : "0.0.0-dev";
12985
12876
  }
12986
12877
  function defaultUpdateCachePath() {
12987
12878
  const base = process.env["CROSSCHECK_DATA_DIR"] || import_node_path13.default.join(import_node_os2.default.homedir() || import_node_os2.default.tmpdir(), ".crosscheck");
@@ -13755,12 +13646,11 @@ function helpTool() {
13755
13646
  }
13756
13647
  });
13757
13648
  }
13758
- function registerCoreTools(opts = {}) {
13759
- const o = opts && typeof opts === "object" && "toolNames" in opts ? { bridge: opts } : opts;
13649
+ function registerCoreTools(o = {}) {
13760
13650
  const tools = /* @__PURE__ */ new Map();
13761
13651
  const list = [
13762
13652
  pingTool(),
13763
- verifyTool(o.bridge, o.fetchConfig),
13653
+ verifyTool(o.fetchConfig),
13764
13654
  pickTool(
13765
13655
  o.providers ?? {},
13766
13656
  o.providerAllowlist ?? null,
@@ -13772,7 +13662,6 @@ function registerCoreTools(opts = {}) {
13772
13662
  auditTool(
13773
13663
  o.providers ?? {},
13774
13664
  o.providerAllowlist ?? null,
13775
- o.bridge,
13776
13665
  o.transcriptsDir,
13777
13666
  o.pricing,
13778
13667
  o.storage,
@@ -13782,7 +13671,6 @@ function registerCoreTools(opts = {}) {
13782
13671
  conferTool(
13783
13672
  o.providers ?? {},
13784
13673
  o.providerAllowlist ?? null,
13785
- o.bridge,
13786
13674
  o.moderatorDefault ?? "anthropic",
13787
13675
  o.pricing,
13788
13676
  o.storage,
@@ -13795,7 +13683,6 @@ function registerCoreTools(opts = {}) {
13795
13683
  debateTool(
13796
13684
  o.providers ?? {},
13797
13685
  o.providerAllowlist ?? null,
13798
- o.bridge,
13799
13686
  buildInnerCallers(o),
13800
13687
  o.storage,
13801
13688
  o.pricing,
@@ -13807,7 +13694,6 @@ function registerCoreTools(opts = {}) {
13807
13694
  coordinateTool(
13808
13695
  o.providers ?? {},
13809
13696
  o.providerAllowlist ?? null,
13810
- o.bridge,
13811
13697
  buildInnerCallers(o),
13812
13698
  o.storage,
13813
13699
  o.circuitBreakers,
@@ -13818,7 +13704,6 @@ function registerCoreTools(opts = {}) {
13818
13704
  triangulateTool(
13819
13705
  o.providers ?? {},
13820
13706
  o.providerAllowlist ?? null,
13821
- o.bridge,
13822
13707
  o.storage,
13823
13708
  o.circuitBreakers,
13824
13709
  o.transcriptsDir,
@@ -13827,7 +13712,6 @@ function registerCoreTools(opts = {}) {
13827
13712
  planTool(
13828
13713
  o.providers ?? {},
13829
13714
  o.providerAllowlist ?? null,
13830
- o.bridge,
13831
13715
  o.storage,
13832
13716
  o.circuitBreakers,
13833
13717
  o.transcriptsDir,
@@ -13836,7 +13720,6 @@ function registerCoreTools(opts = {}) {
13836
13720
  critiqueTool(
13837
13721
  o.providers ?? {},
13838
13722
  o.providerAllowlist ?? null,
13839
- o.bridge,
13840
13723
  o.storage,
13841
13724
  o.circuitBreakers,
13842
13725
  o.transcriptsDir,
@@ -13845,7 +13728,6 @@ function registerCoreTools(opts = {}) {
13845
13728
  reviewTool(
13846
13729
  o.providers ?? {},
13847
13730
  o.providerAllowlist ?? null,
13848
- o.bridge,
13849
13731
  o.storage,
13850
13732
  o.circuitBreakers,
13851
13733
  o.transcriptsDir,
@@ -13853,25 +13735,23 @@ function registerCoreTools(opts = {}) {
13853
13735
  ),
13854
13736
  listProvidersTool(o.providers ?? {}, o.activeProviders ?? null, o.moderatorDefault ?? "anthropic"),
13855
13737
  modelsTool(o.providers ?? {}, o.activeProviders ?? null),
13856
- recallTool(o.storage, o.bridge),
13857
- sessionMemoryTool(o.storage, o.bridge),
13858
- scoreboardTool(o.storage, o.bridge, o.eventsPath),
13859
- explainTool(o.storage, o.bridge, o.transcriptsDir),
13738
+ recallTool(o.storage),
13739
+ sessionMemoryTool(o.storage),
13740
+ scoreboardTool(o.storage, o.eventsPath),
13741
+ explainTool(o.storage, o.transcriptsDir),
13860
13742
  delegateTool(
13861
13743
  o.providers ?? {},
13862
13744
  o.providerAllowlist ?? null,
13863
13745
  o.storage,
13864
- o.bridge,
13865
13746
  o.moderatorDefault ?? "anthropic"
13866
13747
  ),
13867
13748
  fetchTool(o.storage, o.fetchConfig, o.repoRoot),
13868
- recommendPanelTool(o.providers ?? {}, o.storage, o.bridge),
13749
+ recommendPanelTool(o.providers ?? {}, o.storage),
13869
13750
  updateCrosscheckTool(o.repoRoot ?? null),
13870
13751
  benchTool(
13871
13752
  o.providers ?? {},
13872
13753
  o.providerAllowlist ?? null,
13873
13754
  o.storage,
13874
- o.bridge,
13875
13755
  o.moderatorDefault ?? "anthropic",
13876
13756
  o.benchGoldensDir,
13877
13757
  o.circuitBreakers,
@@ -13882,7 +13762,6 @@ function registerCoreTools(opts = {}) {
13882
13762
  solveTool(
13883
13763
  o.providers ?? {},
13884
13764
  o.providerAllowlist ?? null,
13885
- o.bridge,
13886
13765
  o.storage,
13887
13766
  o.circuitBreakers,
13888
13767
  o.transcriptsDir,
@@ -13891,7 +13770,6 @@ function registerCoreTools(opts = {}) {
13891
13770
  orchestrateTool(
13892
13771
  o.providers ?? {},
13893
13772
  o.providerAllowlist ?? null,
13894
- o.bridge,
13895
13773
  o.moderatorDefault ?? "anthropic",
13896
13774
  o.storage,
13897
13775
  o.pricing,
@@ -13938,7 +13816,6 @@ function createTool(o, toolName, cheapDefault) {
13938
13816
  providers: o.providers ?? {},
13939
13817
  allowlist: o.providerAllowlist ?? null,
13940
13818
  ...o.storage ? { storage: o.storage } : {},
13941
- ...o.bridge ? { bridge: o.bridge } : {},
13942
13819
  ...o.moderatorDefault ? { moderator: o.moderatorDefault } : {},
13943
13820
  ...o.fetchConfig ? { fetchConfig: o.fetchConfig } : {},
13944
13821
  ...o.repoRoot ? { repoRoot: o.repoRoot } : {},
@@ -13947,7 +13824,7 @@ function createTool(o, toolName, cheapDefault) {
13947
13824
  })
13948
13825
  };
13949
13826
  }
13950
- function orchestrateTool(providers, allowlist, bridge, moderator, storage, pricing, breakers, transcriptsDir, repoRoot, nodeCache) {
13827
+ function orchestrateTool(providers, allowlist, moderator, storage, pricing, breakers, transcriptsDir, repoRoot, nodeCache) {
13951
13828
  return {
13952
13829
  name: "orchestrate",
13953
13830
  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.",
@@ -13972,7 +13849,6 @@ function orchestrateTool(providers, allowlist, bridge, moderator, storage, prici
13972
13849
  providers,
13973
13850
  allowlist,
13974
13851
  moderator,
13975
- ...bridge ? { bridge } : {},
13976
13852
  ...storage ? { storage } : {},
13977
13853
  ...pricing ? { pricing } : {},
13978
13854
  ...breakers ? { breakers } : {},
@@ -13982,7 +13858,7 @@ function orchestrateTool(providers, allowlist, bridge, moderator, storage, prici
13982
13858
  })
13983
13859
  };
13984
13860
  }
13985
- function solveTool(providers, allowlist, bridge, storage, breakers, transcriptsDir, repoRoot) {
13861
+ function solveTool(providers, allowlist, storage, breakers, transcriptsDir, repoRoot) {
13986
13862
  return {
13987
13863
  name: "solve",
13988
13864
  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.",
@@ -14004,7 +13880,6 @@ function solveTool(providers, allowlist, bridge, storage, breakers, transcriptsD
14004
13880
  handler: (args) => runSolve(args, {
14005
13881
  providers,
14006
13882
  allowlist,
14007
- ...bridge ? { bridge } : {},
14008
13883
  ...storage ? { storage } : {},
14009
13884
  ...breakers ? { breakers } : {},
14010
13885
  ...transcriptsDir ? { transcriptsDir } : {},
@@ -14043,7 +13918,7 @@ function configPinTool(repoRoot, config2, rejectDriftEnv) {
14043
13918
  }
14044
13919
  };
14045
13920
  }
14046
- function benchTool(providers, allowlist, storage, bridge, moderator, defaultGoldensDir, breakers, transcriptsDir, repoRoot) {
13921
+ function benchTool(providers, allowlist, storage, moderator, defaultGoldensDir, breakers, transcriptsDir, repoRoot) {
14047
13922
  return {
14048
13923
  name: "bench",
14049
13924
  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).",
@@ -14062,7 +13937,6 @@ function benchTool(providers, allowlist, storage, bridge, moderator, defaultGold
14062
13937
  allowlist,
14063
13938
  moderator,
14064
13939
  ...storage ? { storage } : {},
14065
- ...bridge ? { bridge } : {},
14066
13940
  ...defaultGoldensDir ? { defaultGoldensDir } : {},
14067
13941
  ...breakers ? { breakers } : {},
14068
13942
  ...transcriptsDir ? { transcriptsDir } : {},
@@ -14089,7 +13963,7 @@ function updateCrosscheckTool(repoRoot) {
14089
13963
  }
14090
13964
  };
14091
13965
  }
14092
- function recommendPanelTool(providers, storage, bridge) {
13966
+ function recommendPanelTool(providers, storage) {
14093
13967
  return {
14094
13968
  name: "recommend_panel",
14095
13969
  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.",
@@ -14107,8 +13981,7 @@ function recommendPanelTool(providers, storage, bridge) {
14107
13981
  },
14108
13982
  handler: (args) => runRecommendPanel(args, {
14109
13983
  providers,
14110
- ...storage ? { storage } : {},
14111
- ...bridge ? { bridge } : {}
13984
+ ...storage ? { storage } : {}
14112
13985
  })
14113
13986
  };
14114
13987
  }
@@ -14133,7 +14006,7 @@ function fetchTool(storage, fetchConfig, repoRoot) {
14133
14006
  })
14134
14007
  };
14135
14008
  }
14136
- function delegateTool(providers, allowlist, storage, bridge, moderator) {
14009
+ function delegateTool(providers, allowlist, storage, moderator) {
14137
14010
  return {
14138
14011
  name: "delegate",
14139
14012
  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.",
@@ -14153,12 +14026,11 @@ function delegateTool(providers, allowlist, storage, bridge, moderator) {
14153
14026
  providers,
14154
14027
  allowlist,
14155
14028
  moderator,
14156
- ...storage ? { storage } : {},
14157
- ...bridge ? { bridge } : {}
14029
+ ...storage ? { storage } : {}
14158
14030
  })
14159
14031
  };
14160
14032
  }
14161
- function explainTool(storage, bridge, transcriptsDir) {
14033
+ function explainTool(storage, transcriptsDir) {
14162
14034
  return {
14163
14035
  name: "explain",
14164
14036
  description: "Replay a session as a navigable tree with cost/latency annotations. Reads usage_log + optionally walks the transcripts directory for per-tool summaries.",
@@ -14176,12 +14048,11 @@ function explainTool(storage, bridge, transcriptsDir) {
14176
14048
  },
14177
14049
  handler: (args) => runExplain(args, {
14178
14050
  ...storage ? { storage } : {},
14179
- ...bridge ? { bridge } : {},
14180
14051
  ...transcriptsDir ? { transcriptsDir } : {}
14181
14052
  })
14182
14053
  };
14183
14054
  }
14184
- function scoreboardTool(storage, bridge, eventsPath) {
14055
+ function scoreboardTool(storage, eventsPath) {
14185
14056
  return {
14186
14057
  name: "scoreboard",
14187
14058
  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).",
@@ -14195,12 +14066,11 @@ function scoreboardTool(storage, bridge, eventsPath) {
14195
14066
  },
14196
14067
  handler: (args) => runScoreboard(args, {
14197
14068
  ...storage ? { storage } : {},
14198
- ...bridge ? { bridge } : {},
14199
14069
  ...eventsPath ? { eventsPath } : {}
14200
14070
  })
14201
14071
  };
14202
14072
  }
14203
- function sessionMemoryTool(storage, bridge) {
14073
+ function sessionMemoryTool(storage) {
14204
14074
  return {
14205
14075
  name: "session_memory",
14206
14076
  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).",
@@ -14223,15 +14093,14 @@ function sessionMemoryTool(storage, bridge) {
14223
14093
  required: ["action", "session_id"]
14224
14094
  },
14225
14095
  handler: (args) => runSessionMemory(args, {
14226
- ...storage ? { storage } : {},
14227
- ...bridge ? { bridge } : {}
14096
+ ...storage ? { storage } : {}
14228
14097
  })
14229
14098
  };
14230
14099
  }
14231
- function recallTool(storage, bridge) {
14100
+ function recallTool(storage) {
14232
14101
  return {
14233
14102
  name: "recall",
14234
- 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.",
14103
+ description: "Full-text search across persisted transcripts via SQLite FTS5. Returns rows ordered by relevance with a windowed snippet. Requires a wired Storage adapter.",
14235
14104
  inputSchema: {
14236
14105
  type: "object",
14237
14106
  additionalProperties: true,
@@ -14245,8 +14114,7 @@ function recallTool(storage, bridge) {
14245
14114
  required: ["query"]
14246
14115
  },
14247
14116
  handler: (args) => runRecall(args, {
14248
- ...storage ? { storage } : {},
14249
- ...bridge ? { bridge } : {}
14117
+ ...storage ? { storage } : {}
14250
14118
  })
14251
14119
  };
14252
14120
  }
@@ -14282,7 +14150,7 @@ function modelsTool(providers, activeProviders) {
14282
14150
  handler: async (args) => runModels(args, { providers, activeProviders })
14283
14151
  };
14284
14152
  }
14285
- function reviewTool(providers, allowlist, bridge, storage, breakers, transcriptsDir, repoRoot) {
14153
+ function reviewTool(providers, allowlist, storage, breakers, transcriptsDir, repoRoot) {
14286
14154
  return {
14287
14155
  name: "review",
14288
14156
  description: "Have an LLM panel peer-review a code or proposal snippet. Returns the confer envelope (one answer per provider).",
@@ -14301,7 +14169,6 @@ function reviewTool(providers, allowlist, bridge, storage, breakers, transcripts
14301
14169
  handler: (args) => runReview(args, {
14302
14170
  providers,
14303
14171
  allowlist,
14304
- ...bridge ? { bridge } : {},
14305
14172
  ...storage ? { storage } : {},
14306
14173
  ...breakers ? { breakers } : {},
14307
14174
  ...transcriptsDir ? { transcriptsDir } : {},
@@ -14309,7 +14176,7 @@ function reviewTool(providers, allowlist, bridge, storage, breakers, transcripts
14309
14176
  })
14310
14177
  };
14311
14178
  }
14312
- function critiqueTool(providers, allowlist, bridge, storage, breakers, transcriptsDir, repoRoot) {
14179
+ function critiqueTool(providers, allowlist, storage, breakers, transcriptsDir, repoRoot) {
14313
14180
  return {
14314
14181
  name: "critique",
14315
14182
  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.",
@@ -14329,7 +14196,6 @@ function critiqueTool(providers, allowlist, bridge, storage, breakers, transcrip
14329
14196
  handler: (args) => runCritique(args, {
14330
14197
  providers,
14331
14198
  allowlist,
14332
- ...bridge ? { bridge } : {},
14333
14199
  ...storage ? { storage } : {},
14334
14200
  ...breakers ? { breakers } : {},
14335
14201
  ...transcriptsDir ? { transcriptsDir } : {},
@@ -14337,7 +14203,7 @@ function critiqueTool(providers, allowlist, bridge, storage, breakers, transcrip
14337
14203
  })
14338
14204
  };
14339
14205
  }
14340
- function planTool(providers, allowlist, bridge, storage, breakers, transcriptsDir, repoRoot) {
14206
+ function planTool(providers, allowlist, storage, breakers, transcriptsDir, repoRoot) {
14341
14207
  return {
14342
14208
  name: "plan",
14343
14209
  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.',
@@ -14364,7 +14230,6 @@ function planTool(providers, allowlist, bridge, storage, breakers, transcriptsDi
14364
14230
  handler: (args) => runPlan(args, {
14365
14231
  providers,
14366
14232
  allowlist,
14367
- ...bridge ? { bridge } : {},
14368
14233
  ...storage ? { storage } : {},
14369
14234
  ...breakers ? { breakers } : {},
14370
14235
  ...transcriptsDir ? { transcriptsDir } : {},
@@ -14372,7 +14237,7 @@ function planTool(providers, allowlist, bridge, storage, breakers, transcriptsDi
14372
14237
  })
14373
14238
  };
14374
14239
  }
14375
- function triangulateTool(providers, allowlist, bridge, storage, breakers, transcriptsDir, repoRoot) {
14240
+ function triangulateTool(providers, allowlist, storage, breakers, transcriptsDir, repoRoot) {
14376
14241
  return {
14377
14242
  name: "triangulate",
14378
14243
  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.",
@@ -14393,7 +14258,6 @@ function triangulateTool(providers, allowlist, bridge, storage, breakers, transc
14393
14258
  handler: (args) => runTriangulate(args, {
14394
14259
  providers,
14395
14260
  allowlist,
14396
- ...bridge ? { bridge } : {},
14397
14261
  ...storage ? { storage } : {},
14398
14262
  ...breakers ? { breakers } : {},
14399
14263
  ...transcriptsDir ? { transcriptsDir } : {},
@@ -14401,7 +14265,7 @@ function triangulateTool(providers, allowlist, bridge, storage, breakers, transc
14401
14265
  })
14402
14266
  };
14403
14267
  }
14404
- function coordinateTool(providers, allowlist, bridge, innerCallers, storage, breakers, transcriptsDir, repoRoot, workerToolsCfg) {
14268
+ function coordinateTool(providers, allowlist, innerCallers, storage, breakers, transcriptsDir, repoRoot, workerToolsCfg) {
14405
14269
  return {
14406
14270
  name: "coordinate",
14407
14271
  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).",
@@ -14426,7 +14290,6 @@ function coordinateTool(providers, allowlist, bridge, innerCallers, storage, bre
14426
14290
  handler: (args) => runCoordinate(args, {
14427
14291
  providers,
14428
14292
  allowlist,
14429
- ...bridge ? { bridge } : {},
14430
14293
  innerCallers,
14431
14294
  ...storage ? { storage } : {},
14432
14295
  ...breakers ? { breakers } : {},
@@ -14436,7 +14299,7 @@ function coordinateTool(providers, allowlist, bridge, innerCallers, storage, bre
14436
14299
  })
14437
14300
  };
14438
14301
  }
14439
- function debateTool(providers, allowlist, bridge, innerCallers, storage, pricing, breakers, transcriptsDir, repoRoot, workerToolsCfg) {
14302
+ function debateTool(providers, allowlist, innerCallers, storage, pricing, breakers, transcriptsDir, repoRoot, workerToolsCfg) {
14440
14303
  return {
14441
14304
  name: "debate",
14442
14305
  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).",
@@ -14466,7 +14329,6 @@ function debateTool(providers, allowlist, bridge, innerCallers, storage, pricing
14466
14329
  handler: (args) => runDebate(args, {
14467
14330
  providers,
14468
14331
  allowlist,
14469
- ...bridge ? { bridge } : {},
14470
14332
  innerCallers,
14471
14333
  ...storage ? { storage } : {},
14472
14334
  ...pricing ? { pricing } : {},
@@ -14486,10 +14348,10 @@ function buildInnerCallers(o) {
14486
14348
  ...o.repoRoot ? { repoRoot: o.repoRoot } : {}
14487
14349
  });
14488
14350
  }
14489
- out.verify = (args) => runVerify(args, o.bridge, o.fetchConfig);
14351
+ out.verify = (args) => runVerify(args, o.fetchConfig);
14490
14352
  return out;
14491
14353
  }
14492
- function conferTool(providers, allowlist, bridge, moderator, pricing, storage, innerCallers, breakers, transcriptsDir, repoRoot, workerToolsCfg) {
14354
+ function conferTool(providers, allowlist, moderator, pricing, storage, innerCallers, breakers, transcriptsDir, repoRoot, workerToolsCfg) {
14493
14355
  return {
14494
14356
  name: "confer",
14495
14357
  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).",
@@ -14516,7 +14378,6 @@ function conferTool(providers, allowlist, bridge, moderator, pricing, storage, i
14516
14378
  handler: (args) => runConfer(args, {
14517
14379
  providers,
14518
14380
  allowlist,
14519
- ...bridge ? { bridge } : {},
14520
14381
  moderator,
14521
14382
  ...pricing ? { pricing } : {},
14522
14383
  ...storage ? { storage } : {},
@@ -14528,7 +14389,7 @@ function conferTool(providers, allowlist, bridge, moderator, pricing, storage, i
14528
14389
  })
14529
14390
  };
14530
14391
  }
14531
- function auditTool(providers, allowlist, bridge, transcriptsDir, pricing, storage, breakers, repoRoot) {
14392
+ function auditTool(providers, allowlist, transcriptsDir, pricing, storage, breakers, repoRoot) {
14532
14393
  return {
14533
14394
  name: "audit",
14534
14395
  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.",
@@ -14554,7 +14415,6 @@ function auditTool(providers, allowlist, bridge, transcriptsDir, pricing, storag
14554
14415
  handler: (args) => runAudit(args, {
14555
14416
  providers,
14556
14417
  allowlist,
14557
- ...bridge ? { bridge } : {},
14558
14418
  ...transcriptsDir ? { transcriptsDir } : {},
14559
14419
  ...pricing ? { pricing } : {},
14560
14420
  ...storage ? { storage } : {},
@@ -14618,7 +14478,7 @@ function pickTool(providers, allowlist, storage, breakers, transcriptsDir, repoR
14618
14478
  })
14619
14479
  };
14620
14480
  }
14621
- function verifyTool(bridge, fetchConfig) {
14481
+ function verifyTool(fetchConfig) {
14622
14482
  return {
14623
14483
  name: "verify",
14624
14484
  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.",
@@ -14636,7 +14496,7 @@ function verifyTool(bridge, fetchConfig) {
14636
14496
  },
14637
14497
  required: ["checks"]
14638
14498
  },
14639
- handler: (args) => runVerify(args, bridge, fetchConfig)
14499
+ handler: (args) => runVerify(args, fetchConfig)
14640
14500
  };
14641
14501
  }
14642
14502
  function pingTool() {
@@ -14716,18 +14576,18 @@ function attachUpdateNotice(result, toolName, opts) {
14716
14576
 
14717
14577
  // src/core/telemetry.ts
14718
14578
  init_cjs_shims();
14719
- var import_node_crypto10 = __toESM(require("crypto"), 1);
14579
+ var import_node_crypto11 = __toESM(require("crypto"), 1);
14720
14580
 
14721
14581
  // src/core/segments.ts
14722
14582
  init_cjs_shims();
14723
- var import_node_crypto9 = __toESM(require("crypto"), 1);
14583
+ var import_node_crypto10 = __toESM(require("crypto"), 1);
14724
14584
  var import_node_fs17 = require("fs");
14725
14585
  var import_node_path16 = __toESM(require("path"), 1);
14726
14586
 
14727
14587
  // src/core/git-context.ts
14728
14588
  init_cjs_shims();
14729
14589
  var import_node_child_process3 = require("child_process");
14730
- var import_node_crypto8 = __toESM(require("crypto"), 1);
14590
+ var import_node_crypto9 = __toESM(require("crypto"), 1);
14731
14591
  var TRUNK_BRANCHES = /* @__PURE__ */ new Set([
14732
14592
  "main",
14733
14593
  "master",
@@ -14756,7 +14616,7 @@ function repoIdentity(cwd) {
14756
14616
  return top ? `path:${top}` : null;
14757
14617
  }
14758
14618
  function fingerprintOf(seatKey, parts) {
14759
- return import_node_crypto8.default.createHmac("sha256", seatKey).update(parts.join("\0")).digest("hex");
14619
+ return import_node_crypto9.default.createHmac("sha256", seatKey).update(parts.join("\0")).digest("hex");
14760
14620
  }
14761
14621
  function readGitContext(seatKey, cwd) {
14762
14622
  try {
@@ -14809,7 +14669,7 @@ function canonicalSegmentBody(s) {
14809
14669
  ].join("|");
14810
14670
  }
14811
14671
  function signSegment(seatKey, s) {
14812
- return import_node_crypto9.default.createHmac("sha256", seatKey).update(canonicalSegmentBody(s)).digest("hex");
14672
+ return import_node_crypto10.default.createHmac("sha256", seatKey).update(canonicalSegmentBody(s)).digest("hex");
14813
14673
  }
14814
14674
  var cfgResolved = false;
14815
14675
  var cfg = null;
@@ -14824,7 +14684,7 @@ function getConfig() {
14824
14684
  const seatKey = process.env["CROSSCHECK_SEAT_HMAC_KEY"];
14825
14685
  if (!base || !seatId || !orgId || !seatKey) return cfg = null;
14826
14686
  if (!UUID_RE.test(seatId) || !UUID_RE.test(orgId)) return cfg = null;
14827
- const sessionId = process.env["CLAUDE_SESSION_ID"] || process.env["CLAUDE_CODE_SESSION_ID"] || `engine-${import_node_crypto9.default.randomUUID()}`;
14687
+ const sessionId = process.env["CLAUDE_SESSION_ID"] || process.env["CLAUDE_CODE_SESSION_ID"] || `engine-${import_node_crypto10.default.randomUUID()}`;
14828
14688
  cfg = {
14829
14689
  url: base.replace(/\/telemetry\/?$/, "/segments"),
14830
14690
  seatId,
@@ -14897,7 +14757,7 @@ function syncSegment(cwd) {
14897
14757
  const now = (/* @__PURE__ */ new Date()).toISOString();
14898
14758
  if (current) emit(c, current, now);
14899
14759
  current = {
14900
- segmentId: import_node_crypto9.default.randomUUID(),
14760
+ segmentId: import_node_crypto10.default.randomUUID(),
14901
14761
  seq: nextSeq++,
14902
14762
  context,
14903
14763
  startedAt: now
@@ -14968,10 +14828,12 @@ function canonicalEventBody(e) {
14968
14828
  e.status,
14969
14829
  e.errorClass ?? ""
14970
14830
  ].join("|");
14971
- if (e.runId === void 0 && e.purpose === void 0 && e.fingerprint === void 0) {
14972
- return base;
14973
- }
14974
- return [base, e.runId ?? "", e.purpose ?? "", e.fingerprint ?? ""].join("|");
14831
+ const hasV2 = e.runId !== void 0 || e.purpose !== void 0 || e.fingerprint !== void 0;
14832
+ const hasV3 = e.sessionId !== void 0;
14833
+ if (!hasV2 && !hasV3) return base;
14834
+ const parts = [base, e.runId ?? "", e.purpose ?? "", e.fingerprint ?? ""];
14835
+ if (hasV3) parts.push(e.sessionId ?? "");
14836
+ return parts.join("|");
14975
14837
  }
14976
14838
  function normalizePurpose(v) {
14977
14839
  if (typeof v !== "string") return void 0;
@@ -14979,7 +14841,7 @@ function normalizePurpose(v) {
14979
14841
  return /^[a-z0-9][a-z0-9_-]*$/.test(slug) ? slug : void 0;
14980
14842
  }
14981
14843
  function signEvent(seatSigningKey, e) {
14982
- return import_node_crypto10.default.createHmac("sha256", seatSigningKey).update(canonicalEventBody(e)).digest("hex");
14844
+ return import_node_crypto11.default.createHmac("sha256", seatSigningKey).update(canonicalEventBody(e)).digest("hex");
14983
14845
  }
14984
14846
  var configResolved = false;
14985
14847
  var config = null;
@@ -15089,9 +14951,9 @@ function scheduleFlush() {
15089
14951
  }, FLUSH_INTERVAL_MS);
15090
14952
  if (typeof flushTimer.unref === "function") flushTimer.unref();
15091
14953
  }
15092
- function enqueue(cfg2, input, runId, fingerprint) {
14954
+ function enqueue(cfg2, input, runId, fingerprint, sessionId) {
15093
14955
  const base = {
15094
- eventId: import_node_crypto10.default.randomUUID(),
14956
+ eventId: import_node_crypto11.default.randomUUID(),
15095
14957
  seatId: cfg2.seatId,
15096
14958
  orgId: cfg2.orgId,
15097
14959
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -15108,6 +14970,7 @@ function enqueue(cfg2, input, runId, fingerprint) {
15108
14970
  // omitting keeps the JSON honest about what we actually know.
15109
14971
  ...input.errorClass === void 0 ? {} : { errorClass: input.errorClass },
15110
14972
  runId,
14973
+ sessionId,
15111
14974
  // Omit entirely (not "") when unknown, so the canonical body stays
15112
14975
  // consistent with what the server reconstructs from the JSON.
15113
14976
  ...input.purpose === void 0 ? {} : { purpose: input.purpose },
@@ -15122,15 +14985,23 @@ function enqueue(cfg2, input, runId, fingerprint) {
15122
14985
  if (queue.length >= FLUSH_BATCH_AT) void flush();
15123
14986
  else scheduleFlush();
15124
14987
  }
14988
+ function sessionIdFromEnvelope(out) {
14989
+ if (!out || typeof out !== "object") return null;
14990
+ const rs = out["run_summary"];
14991
+ if (!rs || typeof rs !== "object") return null;
14992
+ const v = rs["session_id"];
14993
+ return typeof v === "string" && v.trim().length > 0 ? v : null;
14994
+ }
15125
14995
  function recordUsage(out, toolName) {
15126
14996
  try {
15127
14997
  const cfg2 = getConfig2();
15128
14998
  if (!cfg2) return;
15129
14999
  if (toolName === "update_crosscheck") return;
15130
- const runId = import_node_crypto10.default.randomUUID();
15000
+ const runId = import_node_crypto11.default.randomUUID();
15001
+ const sessionId = resolveSessionId(sessionIdFromEnvelope(out));
15131
15002
  const fingerprint = currentFingerprint();
15132
15003
  for (const ev of usageEventsFromEnvelope(out, toolName)) {
15133
- enqueue(cfg2, ev, runId, fingerprint);
15004
+ enqueue(cfg2, ev, runId, fingerprint, sessionId);
15134
15005
  }
15135
15006
  } catch {
15136
15007
  }
@@ -15237,7 +15108,7 @@ function createServer(opts = {}) {
15237
15108
  throw new Error(`unknown tool: ${name}`);
15238
15109
  }
15239
15110
  const args = req.params.arguments ?? {};
15240
- const startedAt = (/* @__PURE__ */ new Date()).toISOString();
15111
+ const startedAt2 = (/* @__PURE__ */ new Date()).toISOString();
15241
15112
  const startedHr = process.hrtime.bigint();
15242
15113
  const id = nextEventId();
15243
15114
  syncSegment(opts.repoRoot);
@@ -15251,7 +15122,7 @@ function createServer(opts = {}) {
15251
15122
  event: "tool_invoke",
15252
15123
  id,
15253
15124
  tool: name,
15254
- started_at: startedAt,
15125
+ started_at: startedAt2,
15255
15126
  duration_ms: 0,
15256
15127
  status: "ok",
15257
15128
  args_keys: topLevelKeys(args),
@@ -15285,7 +15156,7 @@ function createServer(opts = {}) {
15285
15156
  event: "tool_invoke",
15286
15157
  id,
15287
15158
  tool: name,
15288
- started_at: startedAt,
15159
+ started_at: startedAt2,
15289
15160
  duration_ms: durationMs,
15290
15161
  status: "ok",
15291
15162
  args_keys: topLevelKeys(args),
@@ -15307,7 +15178,7 @@ function createServer(opts = {}) {
15307
15178
  event: "tool_invoke",
15308
15179
  id,
15309
15180
  tool: name,
15310
- started_at: startedAt,
15181
+ started_at: startedAt2,
15311
15182
  duration_ms: durationMs,
15312
15183
  status: "error",
15313
15184
  args_keys: topLevelKeys(args),
@@ -15338,7 +15209,6 @@ async function connectAndServe(transport, opts = {}) {
15338
15209
  }
15339
15210
  function buildToolRegistry(opts) {
15340
15211
  const registerOpts = {};
15341
- if (opts.bridge) registerOpts.bridge = opts.bridge;
15342
15212
  if (opts.providers) registerOpts.providers = opts.providers;
15343
15213
  if (opts.providerAllowlist !== void 0)
15344
15214
  registerOpts.providerAllowlist = opts.providerAllowlist;
@@ -15355,20 +15225,14 @@ function buildToolRegistry(opts) {
15355
15225
  if (opts.moderatorDefault) registerOpts.moderatorDefault = opts.moderatorDefault;
15356
15226
  if (opts.benchGoldensDir) registerOpts.benchGoldensDir = opts.benchGoldensDir;
15357
15227
  if (opts.eventsPath) registerOpts.eventsPath = opts.eventsPath;
15358
- const tools = registerCoreTools(registerOpts);
15359
- if (!opts.bridge) return tools;
15360
- const proxies = buildPythonProxies(opts.bridge);
15361
- for (const [name, proxy] of proxies) {
15362
- if (!tools.has(name)) tools.set(name, proxy);
15363
- }
15364
- return tools;
15228
+ return registerCoreTools(registerOpts);
15365
15229
  }
15366
15230
 
15367
15231
  // src/core/config-file.ts
15368
15232
  init_cjs_shims();
15369
15233
  var import_node_fs18 = require("fs");
15370
15234
  var import_node_path17 = __toESM(require("path"), 1);
15371
- var import_node_crypto11 = require("crypto");
15235
+ var import_node_crypto12 = require("crypto");
15372
15236
  function asObj(v) {
15373
15237
  return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
15374
15238
  }
@@ -15502,7 +15366,7 @@ function mapConfig(raw, sourcePath) {
15502
15366
  if (extras && extras.length > 0) cfg2.patterns_extra = extras;
15503
15367
  if (bool(red["hmac_tokens"]) === true) {
15504
15368
  cfg2.hmac_tokens = true;
15505
- cfg2.hmac_secret = (0, import_node_crypto11.randomBytes)(32);
15369
+ cfg2.hmac_secret = (0, import_node_crypto12.randomBytes)(32);
15506
15370
  }
15507
15371
  if (Object.keys(cfg2).length > 0) setRedactionConfig(cfg2);
15508
15372
  }
@@ -15536,14 +15400,39 @@ function loadConfigFile(opts) {
15536
15400
  return mapConfig(obj, file);
15537
15401
  }
15538
15402
 
15403
+ // src/core/process-registry.ts
15404
+ init_cjs_shims();
15405
+ var import_node_fs19 = require("fs");
15406
+ var import_node_os3 = __toESM(require("os"), 1);
15407
+ var import_node_path18 = __toESM(require("path"), 1);
15408
+ function registryDir(env = process.env) {
15409
+ const base = env["CROSSCHECK_DATA_DIR"] || import_node_path18.default.join(env["HOME"] ?? env["USERPROFILE"] ?? import_node_os3.default.homedir(), ".crosscheck");
15410
+ return import_node_path18.default.join(base, "procs");
15411
+ }
15412
+ function registerProcess(rec, env = process.env) {
15413
+ try {
15414
+ const dir = registryDir(env);
15415
+ (0, import_node_fs19.mkdirSync)(dir, { recursive: true, mode: 448 });
15416
+ (0, import_node_fs19.writeFileSync)(import_node_path18.default.join(dir, `${rec.pid}.json`), JSON.stringify(rec), { mode: 384 });
15417
+ } catch {
15418
+ }
15419
+ }
15420
+ function unregisterProcess(pid = process.pid, env = process.env) {
15421
+ try {
15422
+ (0, import_node_fs19.rmSync)(import_node_path18.default.join(registryDir(env), `${pid}.json`), { force: true });
15423
+ } catch {
15424
+ }
15425
+ }
15426
+
15539
15427
  // src/entrypoints/node-stdio.ts
15428
+ var HERE = typeof __dirname !== "undefined" ? __dirname : import_node_path19.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
15540
15429
  var SHUTDOWN_TIMEOUT_MS = 2e3;
15541
15430
  async function main() {
15542
15431
  enforceLicenseOrExit();
15543
- const cfgDataDir = process.env["CROSSCHECK_DATA_DIR"] || import_node_path18.default.join(process.env["HOME"] ?? process.env["USERPROFILE"] ?? "", ".crosscheck");
15432
+ const cfgDataDir = process.env["CROSSCHECK_DATA_DIR"] || import_node_path19.default.join(process.env["HOME"] ?? process.env["USERPROFILE"] ?? "", ".crosscheck");
15544
15433
  const configFile = loadConfigFile({
15545
15434
  startDir: process.cwd(),
15546
- repoRoot: findGitRoot(__dirname),
15435
+ repoRoot: findGitRoot(HERE),
15547
15436
  dataDir: cfgDataDir
15548
15437
  });
15549
15438
  if (configFile) {
@@ -15557,15 +15446,17 @@ async function main() {
15557
15446
  `);
15558
15447
  }
15559
15448
  setEventEmitter(buildDefaultEmitter(process.env));
15560
- if (process.env["CROSSCHECK_BRIDGE_PYTHON"] === "1") {
15561
- process.stderr.write(
15562
- "crosscheck-agent: CROSSCHECK_BRIDGE_PYTHON is deprecated and ignored \u2014 the engine is native-only.\n"
15563
- );
15564
- }
15565
15449
  installShutdownHandlers();
15450
+ registerProcess({
15451
+ pid: process.pid,
15452
+ version: SERVER_VERSION,
15453
+ startedAt: Date.now(),
15454
+ ppid: process.ppid,
15455
+ sessionId: engineSessionId()
15456
+ });
15566
15457
  const bundledPricing = (() => {
15567
15458
  try {
15568
- return import_node_path18.default.join(import_node_path18.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl)), "pricing.json");
15459
+ return import_node_path19.default.join(import_node_path19.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl)), "pricing.json");
15569
15460
  } catch {
15570
15461
  return void 0;
15571
15462
  }
@@ -15574,7 +15465,7 @@ async function main() {
15574
15465
  process.env["CROSSCHECK_PRICING_PATH"],
15575
15466
  resolveRepoFile("config/pricing.json"),
15576
15467
  bundledPricing
15577
- ].find((p) => p && (0, import_node_fs19.existsSync)(p));
15468
+ ].find((p) => p && (0, import_node_fs20.existsSync)(p));
15578
15469
  const pricing = pricingPath ? loadPricing(pricingPath) : {};
15579
15470
  const providers = buildProviders({ env: process.env, pricing });
15580
15471
  if (Object.keys(providers).length > 0) {
@@ -15592,10 +15483,10 @@ async function main() {
15592
15483
  }
15593
15484
  let storage;
15594
15485
  if (process.env["CROSSCHECK_DB_DISABLED"] !== "1") {
15595
- const dbPath = process.env["CROSSCHECK_DB_PATH"] ?? resolveRepoFile(".crosscheck/db.sqlite") ?? import_node_path18.default.join(cfgDataDir, "db.sqlite");
15486
+ const dbPath = process.env["CROSSCHECK_DB_PATH"] ?? resolveRepoFile(".crosscheck/db.sqlite") ?? import_node_path19.default.join(cfgDataDir, "db.sqlite");
15596
15487
  if (dbPath) {
15597
15488
  try {
15598
- (0, import_node_fs19.mkdirSync)(import_node_path18.default.dirname(dbPath), { recursive: true });
15489
+ (0, import_node_fs20.mkdirSync)(import_node_path19.default.dirname(dbPath), { recursive: true });
15599
15490
  const { openBetterSqliteStorage: openBetterSqliteStorage2 } = await Promise.resolve().then(() => (init_better_sqlite3(), better_sqlite3_exports));
15600
15491
  storage = openBetterSqliteStorage2({ path: dbPath });
15601
15492
  await storage.migrate();
@@ -15613,7 +15504,7 @@ async function main() {
15613
15504
  }
15614
15505
  }
15615
15506
  const transcriptsDir = configFile?.logTranscripts === false ? void 0 : process.env["CROSSCHECK_TRANSCRIPTS_DIR"] ?? configFile?.transcriptsDir ?? resolveRepoFile(".crosscheck/transcripts");
15616
- const repoRoot = process.env["CROSSCHECK_REPO_ROOT"] ?? findGitRoot(__dirname);
15507
+ const repoRoot = process.env["CROSSCHECK_REPO_ROOT"] ?? findGitRoot(HERE);
15617
15508
  const transport = new import_stdio.StdioServerTransport();
15618
15509
  const serverOpts = { providers };
15619
15510
  if (storage) serverOpts.storage = storage;
@@ -15643,6 +15534,7 @@ function installShutdownHandlers() {
15643
15534
  new Promise((resolve2) => setTimeout(resolve2, SHUTDOWN_TIMEOUT_MS))
15644
15535
  ]).catch(() => {
15645
15536
  });
15537
+ unregisterProcess();
15646
15538
  process.stderr.write(`crosscheck-agent: shutdown (${reason})
15647
15539
  `);
15648
15540
  process.exit(0);
@@ -15661,11 +15553,11 @@ function installShutdownHandlers() {
15661
15553
  });
15662
15554
  }
15663
15555
  function resolveRepoFile(rel) {
15664
- let dir = __dirname;
15556
+ let dir = HERE;
15665
15557
  for (let i = 0; i < 8; i++) {
15666
- const candidate = import_node_path18.default.join(dir, rel);
15667
- if ((0, import_node_fs19.existsSync)(candidate)) return candidate;
15668
- const parent = import_node_path18.default.dirname(dir);
15558
+ const candidate = import_node_path19.default.join(dir, rel);
15559
+ if ((0, import_node_fs20.existsSync)(candidate)) return candidate;
15560
+ const parent = import_node_path19.default.dirname(dir);
15669
15561
  if (parent === dir) break;
15670
15562
  dir = parent;
15671
15563
  }
@@ -15674,8 +15566,8 @@ function resolveRepoFile(rel) {
15674
15566
  function findGitRoot(startDir) {
15675
15567
  let dir = startDir;
15676
15568
  for (let i = 0; i < 12; i++) {
15677
- if ((0, import_node_fs19.existsSync)(import_node_path18.default.join(dir, ".git"))) return dir;
15678
- const parent = import_node_path18.default.dirname(dir);
15569
+ if ((0, import_node_fs20.existsSync)(import_node_path19.default.join(dir, ".git"))) return dir;
15570
+ const parent = import_node_path19.default.dirname(dir);
15679
15571
  if (parent === dir) break;
15680
15572
  dir = parent;
15681
15573
  }
@@ -15685,7 +15577,7 @@ var isMainEntry = (() => {
15685
15577
  try {
15686
15578
  const argv1 = process.argv[1];
15687
15579
  if (!argv1) return false;
15688
- return /(?:\/|\\)node-stdio\.(?:js|cjs|mjs)$/.test(argv1);
15580
+ return /(?:\/|\\)node-stdio\.(?:js|cjs|mjs|ts)$/.test(argv1);
15689
15581
  } catch {
15690
15582
  return false;
15691
15583
  }