crosscheck-mcp 0.2.15 → 0.2.16

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.
@@ -934,8 +934,8 @@ var init_better_sqlite3 = __esm({
934
934
 
935
935
  // src/entrypoints/node-stdio.ts
936
936
  init_esm_shims();
937
- import { existsSync as existsSync13, mkdirSync as mkdirSync9 } from "fs";
938
- import path14 from "path";
937
+ import { existsSync as existsSync14, mkdirSync as mkdirSync10 } from "fs";
938
+ import path15 from "path";
939
939
  import { fileURLToPath as fileURLToPath3 } from "url";
940
940
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
941
941
 
@@ -952,10 +952,10 @@ var StderrEmitter = class {
952
952
  }
953
953
  };
954
954
  var FileEmitter = class {
955
- constructor(path15) {
956
- this.path = path15;
955
+ constructor(path16) {
956
+ this.path = path16;
957
957
  try {
958
- mkdirSync(dirname(path15), { recursive: true });
958
+ mkdirSync(dirname(path16), { recursive: true });
959
959
  } catch {
960
960
  }
961
961
  }
@@ -988,13 +988,13 @@ var NullEmitter = class {
988
988
  function buildDefaultEmitter(env) {
989
989
  const mode = (env["CROSSCHECK_EVENTS"] ?? "stderr").toLowerCase();
990
990
  if (mode === "off") return new NullEmitter();
991
- const path15 = env["CROSSCHECK_EVENTS_PATH"];
991
+ const path16 = env["CROSSCHECK_EVENTS_PATH"];
992
992
  if (mode === "file") {
993
- return path15 ? new FileEmitter(path15) : new NullEmitter();
993
+ return path16 ? new FileEmitter(path16) : new NullEmitter();
994
994
  }
995
995
  if (mode === "both") {
996
996
  const ems = [new StderrEmitter()];
997
- if (path15) ems.push(new FileEmitter(path15));
997
+ if (path16) ems.push(new FileEmitter(path16));
998
998
  return new TeeEmitter(ems);
999
999
  }
1000
1000
  return new StderrEmitter();
@@ -1043,10 +1043,10 @@ function envelopeBytes(v) {
1043
1043
  // src/core/pricing.ts
1044
1044
  init_esm_shims();
1045
1045
  import { readFileSync } from "fs";
1046
- function loadPricing(path15) {
1046
+ function loadPricing(path16) {
1047
1047
  let raw;
1048
1048
  try {
1049
- raw = readFileSync(path15, "utf8");
1049
+ raw = readFileSync(path16, "utf8");
1050
1050
  } catch {
1051
1051
  return {};
1052
1052
  }
@@ -1302,8 +1302,8 @@ function httpFailureToProviderError(provider, status, bodyText, retryAfterS, mod
1302
1302
  // src/providers/retry.ts
1303
1303
  init_esm_shims();
1304
1304
  var override = null;
1305
- function setRetryConfig(cfg) {
1306
- override = cfg;
1305
+ function setRetryConfig(cfg2) {
1306
+ override = cfg2;
1307
1307
  }
1308
1308
  function resolveRetryConfig() {
1309
1309
  const envInt = (k, d) => {
@@ -1365,21 +1365,21 @@ function defaultSleep(ms, signal) {
1365
1365
  signal?.addEventListener?.("abort", onAbort);
1366
1366
  });
1367
1367
  }
1368
- async function sendWithRetry(attemptOnce, cfg, signal, deps) {
1368
+ async function sendWithRetry(attemptOnce, cfg2, signal, deps) {
1369
1369
  const sleep = deps?.sleepImpl ?? ((ms) => defaultSleep(ms, signal));
1370
1370
  const rand = deps?.random ?? Math.random;
1371
1371
  let lastErr;
1372
- for (let attempt = 1; attempt <= cfg.maxAttempts; attempt += 1) {
1372
+ for (let attempt = 1; attempt <= cfg2.maxAttempts; attempt += 1) {
1373
1373
  try {
1374
1374
  const result = await attemptOnce();
1375
1375
  return { ...result, attempts: attempt };
1376
1376
  } catch (e) {
1377
1377
  lastErr = e;
1378
1378
  const pe = e instanceof ProviderError ? e : null;
1379
- if (pe?.transient !== true || attempt >= cfg.maxAttempts) throw e;
1380
- let backoffMs = pe.retryAfterS !== void 0 ? pe.retryAfterS * 1e3 : cfg.backoffBaseMs * 2 ** (attempt - 1);
1381
- backoffMs += rand() * cfg.backoffBaseMs;
1382
- backoffMs = Math.min(backoffMs, cfg.maxBackoffMs);
1379
+ if (pe?.transient !== true || attempt >= cfg2.maxAttempts) throw e;
1380
+ let backoffMs = pe.retryAfterS !== void 0 ? pe.retryAfterS * 1e3 : cfg2.backoffBaseMs * 2 ** (attempt - 1);
1381
+ backoffMs += rand() * cfg2.backoffBaseMs;
1382
+ backoffMs = Math.min(backoffMs, cfg2.maxBackoffMs);
1383
1383
  await sleep(backoffMs);
1384
1384
  }
1385
1385
  }
@@ -1391,8 +1391,8 @@ init_esm_shims();
1391
1391
  var DEFAULT_SPEC = { capacity: 5, refillPerSec: 5 };
1392
1392
  var MAX_WAIT_SLICE_MS = 250;
1393
1393
  var override2 = null;
1394
- function setRateLimitConfig(cfg) {
1395
- override2 = cfg;
1394
+ function setRateLimitConfig(cfg2) {
1395
+ override2 = cfg2;
1396
1396
  buckets.clear();
1397
1397
  }
1398
1398
  function disabled() {
@@ -2018,9 +2018,9 @@ function detectStalePins(env) {
2018
2018
  const out = [];
2019
2019
  for (const [provider, envVar] of Object.entries(MODEL_ENV_VARS)) {
2020
2020
  const pinned = env[envVar];
2021
- const current = DEFAULT_MODELS[provider];
2022
- if (pinned && current && pinned !== current) {
2023
- out.push({ provider, envVar, pinned, current });
2021
+ const current2 = DEFAULT_MODELS[provider];
2022
+ if (pinned && current2 && pinned !== current2) {
2023
+ out.push({ provider, envVar, pinned, current: current2 });
2024
2024
  }
2025
2025
  }
2026
2026
  return out;
@@ -2254,7 +2254,7 @@ import { z } from "zod";
2254
2254
  // src/server-meta.ts
2255
2255
  init_esm_shims();
2256
2256
  var SERVER_NAME = "crosscheck-agent";
2257
- var SERVER_VERSION = true ? "0.2.15" : "0.0.0-dev";
2257
+ var SERVER_VERSION = true ? "0.2.16" : "0.0.0-dev";
2258
2258
 
2259
2259
  // src/tools/audit.ts
2260
2260
  init_esm_shims();
@@ -2321,27 +2321,27 @@ function extractJson(text) {
2321
2321
 
2322
2322
  // src/core/json-schema.ts
2323
2323
  init_esm_shims();
2324
- function validateSchema(value, schema, path15 = "") {
2324
+ function validateSchema(value, schema, path16 = "") {
2325
2325
  const errs = [];
2326
2326
  if ("anyOf" in schema) {
2327
2327
  const subs = schema["anyOf"].map(
2328
- (s) => validateSchema(value, s, path15)
2328
+ (s) => validateSchema(value, s, path16)
2329
2329
  );
2330
2330
  if (!subs.some((e) => e.length === 0)) {
2331
- errs.push(`${path15 || "<root>"}: did not match anyOf`);
2331
+ errs.push(`${path16 || "<root>"}: did not match anyOf`);
2332
2332
  }
2333
2333
  return errs;
2334
2334
  }
2335
2335
  if ("oneOf" in schema) {
2336
- const passed = schema["oneOf"].filter((s) => validateSchema(value, s, path15).length === 0).length;
2336
+ const passed = schema["oneOf"].filter((s) => validateSchema(value, s, path16).length === 0).length;
2337
2337
  if (passed !== 1) {
2338
- errs.push(`${path15 || "<root>"}: matched ${passed} of oneOf, expected 1`);
2338
+ errs.push(`${path16 || "<root>"}: matched ${passed} of oneOf, expected 1`);
2339
2339
  }
2340
2340
  return errs;
2341
2341
  }
2342
2342
  if ("const" in schema && !deepEqual(value, schema["const"])) {
2343
2343
  errs.push(
2344
- `${path15 || "<root>"}: expected const ${pyRepr(schema["const"])}, got ${pyRepr(value)}`
2344
+ `${path16 || "<root>"}: expected const ${pyRepr(schema["const"])}, got ${pyRepr(value)}`
2345
2345
  );
2346
2346
  return errs;
2347
2347
  }
@@ -2351,7 +2351,7 @@ function validateSchema(value, schema, path15 = "") {
2351
2351
  const ok = types.some((tt) => matchesType(value, String(tt)));
2352
2352
  if (!ok) {
2353
2353
  errs.push(
2354
- `${path15 || "<root>"}: expected type ${pyReprType(t)}, got ${pyTypeName(value)}`
2354
+ `${path16 || "<root>"}: expected type ${pyReprType(t)}, got ${pyTypeName(value)}`
2355
2355
  );
2356
2356
  return errs;
2357
2357
  }
@@ -2360,29 +2360,29 @@ function validateSchema(value, schema, path15 = "") {
2360
2360
  if ("enum" in schema) {
2361
2361
  const en = schema["enum"];
2362
2362
  if (!en.some((x) => deepEqual(x, value))) {
2363
- errs.push(`${path15 || "<root>"}: value ${pyRepr(value)} not in enum`);
2363
+ errs.push(`${path16 || "<root>"}: value ${pyRepr(value)} not in enum`);
2364
2364
  }
2365
2365
  }
2366
2366
  if ("minLength" in schema && value.length < schema["minLength"]) {
2367
- errs.push(`${path15 || "<root>"}: shorter than minLength ${schema["minLength"]}`);
2367
+ errs.push(`${path16 || "<root>"}: shorter than minLength ${schema["minLength"]}`);
2368
2368
  }
2369
2369
  }
2370
2370
  if (typeof value === "number" && !Number.isNaN(value)) {
2371
2371
  if ("minimum" in schema && value < schema["minimum"]) {
2372
- errs.push(`${path15 || "<root>"}: ${value} < minimum ${schema["minimum"]}`);
2372
+ errs.push(`${path16 || "<root>"}: ${value} < minimum ${schema["minimum"]}`);
2373
2373
  }
2374
2374
  if ("maximum" in schema && value > schema["maximum"]) {
2375
- errs.push(`${path15 || "<root>"}: ${value} > maximum ${schema["maximum"]}`);
2375
+ errs.push(`${path16 || "<root>"}: ${value} > maximum ${schema["maximum"]}`);
2376
2376
  }
2377
2377
  }
2378
2378
  if (Array.isArray(value)) {
2379
2379
  if ("minItems" in schema && value.length < schema["minItems"]) {
2380
- errs.push(`${path15 || "<root>"}: fewer items than minItems ${schema["minItems"]}`);
2380
+ errs.push(`${path16 || "<root>"}: fewer items than minItems ${schema["minItems"]}`);
2381
2381
  }
2382
2382
  const itemSchema = schema["items"];
2383
2383
  if (isObj(itemSchema)) {
2384
2384
  for (let i = 0; i < value.length; i++) {
2385
- errs.push(...validateSchema(value[i], itemSchema, `${path15}[${i}]`));
2385
+ errs.push(...validateSchema(value[i], itemSchema, `${path16}[${i}]`));
2386
2386
  }
2387
2387
  }
2388
2388
  }
@@ -2391,19 +2391,19 @@ function validateSchema(value, schema, path15 = "") {
2391
2391
  const required = schema["required"] ?? [];
2392
2392
  for (const r of required) {
2393
2393
  if (!(r in value)) {
2394
- errs.push(`${path15 || "<root>"}: missing required key ${pyRepr(r)}`);
2394
+ errs.push(`${path16 || "<root>"}: missing required key ${pyRepr(r)}`);
2395
2395
  }
2396
2396
  }
2397
2397
  if (schema["additionalProperties"] === false) {
2398
2398
  for (const k of Object.keys(value)) {
2399
2399
  if (!(k in props)) {
2400
- errs.push(`${path15 || "<root>"}: unknown key ${pyRepr(k)}`);
2400
+ errs.push(`${path16 || "<root>"}: unknown key ${pyRepr(k)}`);
2401
2401
  }
2402
2402
  }
2403
2403
  }
2404
2404
  for (const [k, v] of Object.entries(value)) {
2405
2405
  const ps = props[k];
2406
- if (ps) errs.push(...validateSchema(v, ps, path15 ? `${path15}.${k}` : k));
2406
+ if (ps) errs.push(...validateSchema(v, ps, path16 ? `${path16}.${k}` : k));
2407
2407
  }
2408
2408
  }
2409
2409
  return errs;
@@ -2904,8 +2904,8 @@ async function recordSessionCall(storage, sessionId, answers, wallMs, cpuMs, now
2904
2904
  // src/core/budgets.ts
2905
2905
  init_esm_shims();
2906
2906
  var operatorBudgets = null;
2907
- function setOperatorBudgets(cfg) {
2908
- operatorBudgets = cfg;
2907
+ function setOperatorBudgets(cfg2) {
2908
+ operatorBudgets = cfg2;
2909
2909
  }
2910
2910
  function operatorCeiling(purpose, provider) {
2911
2911
  const direct = operatorBudgets?.token_budgets?.[purpose];
@@ -3106,8 +3106,8 @@ function isPersonaId(s) {
3106
3106
  return PERSONA_IDS.includes(s);
3107
3107
  }
3108
3108
  var personaConfig = null;
3109
- function setPersonaConfig(cfg) {
3110
- personaConfig = cfg;
3109
+ function setPersonaConfig(cfg2) {
3110
+ personaConfig = cfg2;
3111
3111
  }
3112
3112
  var processFirstCallConsumed = false;
3113
3113
  var sessionsSeen = /* @__PURE__ */ new Set();
@@ -3174,8 +3174,8 @@ function routePersona(prompt, toolName) {
3174
3174
  return { id: "engineering", scores };
3175
3175
  }
3176
3176
  function resolvePersona(opts) {
3177
- const cfg = opts.config ?? personaConfig;
3178
- if (cfg?.enabled === false) return { id: null, source: "disabled", bypassed: true };
3177
+ const cfg2 = opts.config ?? personaConfig;
3178
+ if (cfg2?.enabled === false) return { id: null, source: "disabled", bypassed: true };
3179
3179
  const explicit = opts.explicit?.trim().toLowerCase();
3180
3180
  if (explicit === "none" || explicit === "off") {
3181
3181
  return { id: null, source: "none", bypassed: true };
@@ -3221,12 +3221,12 @@ function readIfExists(p) {
3221
3221
  }
3222
3222
  }
3223
3223
  function loadPersonaBody(id, opts) {
3224
- const cfg = opts?.config ?? personaConfig;
3225
- const cacheKey = `${id}|${opts?.repoRoot ?? ""}|${cfg?.dir ?? ""}`;
3224
+ const cfg2 = opts?.config ?? personaConfig;
3225
+ const cacheKey = `${id}|${opts?.repoRoot ?? ""}|${cfg2?.dir ?? ""}`;
3226
3226
  const cached = bodyCache.get(cacheKey);
3227
3227
  if (cached !== void 0) return cached;
3228
3228
  const candidates = [];
3229
- if (cfg?.dir) candidates.push(path2.join(cfg.dir, `${id}.md`));
3229
+ if (cfg2?.dir) candidates.push(path2.join(cfg2.dir, `${id}.md`));
3230
3230
  if (opts?.repoRoot) candidates.push(path2.join(opts.repoRoot, ".crosscheck", "personas", `${id}.md`));
3231
3231
  if (id === "engineering" && opts?.repoRoot) candidates.push(path2.join(opts.repoRoot, "CLAUDE.md"));
3232
3232
  const bundled = bundledPersonasDir();
@@ -3276,9 +3276,9 @@ function buildPersonaInjection(opts) {
3276
3276
 
3277
3277
  // src/core/utils.ts
3278
3278
  init_esm_shims();
3279
- function checkSessionBreakers(session, cfg) {
3279
+ function checkSessionBreakers(session, cfg2) {
3280
3280
  if (!session || typeof session !== "object") return null;
3281
- const c = cfg ?? {};
3281
+ const c = cfg2 ?? {};
3282
3282
  const costCap = Number(c.max_session_cost_usd ?? 0);
3283
3283
  if (costCap > 0 && Number(session.total_cost_usd ?? 0) >= costCap) {
3284
3284
  return {
@@ -3302,10 +3302,10 @@ function checkSessionBreakers(session, cfg) {
3302
3302
  }
3303
3303
  return null;
3304
3304
  }
3305
- function checkDagBreakers(dag, cfg) {
3305
+ function checkDagBreakers(dag, cfg2) {
3306
3306
  if (!dag || typeof dag !== "object") return null;
3307
3307
  const nodes = Array.isArray(dag.nodes) ? dag.nodes : [];
3308
- const c = cfg ?? {};
3308
+ const c = cfg2 ?? {};
3309
3309
  const maxNodes = Math.trunc(Number(c.max_dag_nodes ?? 0));
3310
3310
  if (maxNodes > 0 && nodes.length > maxNodes) {
3311
3311
  return {
@@ -3390,8 +3390,8 @@ function breakerEnvelope(toolName, trip, sessionId) {
3390
3390
  transient: false
3391
3391
  };
3392
3392
  }
3393
- async function maybeBreakerEnvelope(storage, sessionId, toolName, cfg, extras) {
3394
- if (!sessionId || !storage || !cfg) return null;
3393
+ async function maybeBreakerEnvelope(storage, sessionId, toolName, cfg2, extras) {
3394
+ if (!sessionId || !storage || !cfg2) return null;
3395
3395
  let session;
3396
3396
  try {
3397
3397
  session = await storage.getSession(sessionId) ?? null;
@@ -3399,13 +3399,13 @@ async function maybeBreakerEnvelope(storage, sessionId, toolName, cfg, extras) {
3399
3399
  return null;
3400
3400
  }
3401
3401
  const target = extras && extras.length > 0 ? projectSessionWithAnswers(session, extras) : session;
3402
- const trip = checkSessionBreakers(target, cfg);
3402
+ const trip = checkSessionBreakers(target, cfg2);
3403
3403
  if (!trip) return null;
3404
3404
  return breakerEnvelope(toolName, trip, sessionId);
3405
3405
  }
3406
- function maybeDagBreakerEnvelope(dag, toolName, cfg, sessionId) {
3407
- if (!cfg) return null;
3408
- const trip = checkDagBreakers(dag ?? null, cfg);
3406
+ function maybeDagBreakerEnvelope(dag, toolName, cfg2, sessionId) {
3407
+ if (!cfg2) return null;
3408
+ const trip = checkDagBreakers(dag ?? null, cfg2);
3409
3409
  if (!trip) return null;
3410
3410
  return breakerEnvelope(toolName, trip, sessionId);
3411
3411
  }
@@ -3428,9 +3428,9 @@ function builtinRedactionRules() {
3428
3428
  { pattern: /\b(?:\d{4}[ -]?){3}\d{4}\b/g, label: "CARD", prefix_group: false }
3429
3429
  ];
3430
3430
  }
3431
- function compileRules(cfg) {
3431
+ function compileRules(cfg2) {
3432
3432
  const rules = builtinRedactionRules();
3433
- const extras = cfg?.patterns_extra ?? [];
3433
+ const extras = cfg2?.patterns_extra ?? [];
3434
3434
  for (const src of extras) {
3435
3435
  try {
3436
3436
  rules.push({ pattern: new RegExp(src, "g"), label: "EXTRA", prefix_group: false });
@@ -3439,25 +3439,25 @@ function compileRules(cfg) {
3439
3439
  }
3440
3440
  return { rules };
3441
3441
  }
3442
- function hmacSuffix(cfg, label, value) {
3443
- const secret = cfg.hmac_secret ?? Buffer.alloc(0);
3444
- const sid = cfg.session_id ?? "";
3442
+ function hmacSuffix(cfg2, label, value) {
3443
+ const secret = cfg2.hmac_secret ?? Buffer.alloc(0);
3444
+ const sid = cfg2.session_id ?? "";
3445
3445
  const sidBytes = Buffer.from(sid, "utf8");
3446
3446
  const key = Buffer.concat([Buffer.from(secret), sidBytes]);
3447
3447
  const digest = createHmac("sha256", key).update(`${label}:${value}`, "utf8").digest("hex");
3448
3448
  return digest.slice(0, 8);
3449
3449
  }
3450
- function redactText(s, cfg) {
3450
+ function redactText(s, cfg2) {
3451
3451
  if (typeof s !== "string" || s.length === 0) return s;
3452
- if (cfg && cfg.enabled === false) return s;
3453
- const { rules } = compileRules(cfg);
3454
- const hmacMode = Boolean(cfg?.hmac_tokens);
3452
+ if (cfg2 && cfg2.enabled === false) return s;
3453
+ const { rules } = compileRules(cfg2);
3454
+ const hmacMode = Boolean(cfg2?.hmac_tokens);
3455
3455
  let out = s;
3456
3456
  for (const { pattern, label, prefix_group } of rules) {
3457
3457
  pattern.lastIndex = 0;
3458
3458
  if (hmacMode) {
3459
3459
  out = out.replace(pattern, (match, ...args) => {
3460
- const suffix = hmacSuffix(cfg ?? {}, label, match);
3460
+ const suffix = hmacSuffix(cfg2 ?? {}, label, match);
3461
3461
  const token = `[REDACTED_${label}:${suffix}]`;
3462
3462
  if (prefix_group) {
3463
3463
  const g1 = args[0];
@@ -3477,23 +3477,23 @@ function redactText(s, cfg) {
3477
3477
  }
3478
3478
  return out;
3479
3479
  }
3480
- function redactObj(obj, cfg) {
3481
- if (typeof obj === "string") return redactText(obj, cfg);
3480
+ function redactObj(obj, cfg2) {
3481
+ if (typeof obj === "string") return redactText(obj, cfg2);
3482
3482
  if (Array.isArray(obj)) {
3483
- return obj.map((v) => redactObj(v, cfg));
3483
+ return obj.map((v) => redactObj(v, cfg2));
3484
3484
  }
3485
3485
  if (obj && typeof obj === "object") {
3486
3486
  const out = {};
3487
3487
  for (const [k, v] of Object.entries(obj)) {
3488
- out[k] = redactObj(v, cfg);
3488
+ out[k] = redactObj(v, cfg2);
3489
3489
  }
3490
3490
  return out;
3491
3491
  }
3492
3492
  return obj;
3493
3493
  }
3494
3494
  var globalRedaction = null;
3495
- function setRedactionConfig(cfg) {
3496
- globalRedaction = cfg;
3495
+ function setRedactionConfig(cfg2) {
3496
+ globalRedaction = cfg2;
3497
3497
  }
3498
3498
  function getRedactionConfig() {
3499
3499
  return globalRedaction ?? void 0;
@@ -4743,8 +4743,8 @@ function neutralizeInjection(s) {
4743
4743
 
4744
4744
  // src/core/worker.ts
4745
4745
  var WORKER_TOOL_COST_CAP_MODES = ["warn", "enforce", "off"];
4746
- function workerToolCostCapDefaults(callerCapUsd, callerMode, cfg) {
4747
- const cfgObj = cfg ?? {};
4746
+ function workerToolCostCapDefaults(callerCapUsd, callerMode, cfg2) {
4747
+ const cfgObj = cfg2 ?? {};
4748
4748
  let capRaw = callerCapUsd !== void 0 && callerCapUsd !== null ? callerCapUsd : cfgObj.cost_cap_usd;
4749
4749
  let cap;
4750
4750
  if (capRaw === void 0 || capRaw === null) {
@@ -6355,7 +6355,7 @@ function tryKill(child, signal, isUnix) {
6355
6355
  init_esm_shims();
6356
6356
  function shlexSplit(s) {
6357
6357
  const out = [];
6358
- let current = "";
6358
+ let current2 = "";
6359
6359
  let hasCurrent = false;
6360
6360
  let mode = "normal";
6361
6361
  for (let i = 0; i < s.length; i++) {
@@ -6373,7 +6373,7 @@ function shlexSplit(s) {
6373
6373
  }
6374
6374
  if (ch === "\\") {
6375
6375
  if (i + 1 < s.length) {
6376
- current += s[i + 1];
6376
+ current2 += s[i + 1];
6377
6377
  i++;
6378
6378
  hasCurrent = true;
6379
6379
  }
@@ -6381,13 +6381,13 @@ function shlexSplit(s) {
6381
6381
  }
6382
6382
  if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
6383
6383
  if (hasCurrent) {
6384
- out.push(current);
6385
- current = "";
6384
+ out.push(current2);
6385
+ current2 = "";
6386
6386
  hasCurrent = false;
6387
6387
  }
6388
6388
  continue;
6389
6389
  }
6390
- current += ch;
6390
+ current2 += ch;
6391
6391
  hasCurrent = true;
6392
6392
  continue;
6393
6393
  }
@@ -6396,7 +6396,7 @@ function shlexSplit(s) {
6396
6396
  mode = "normal";
6397
6397
  continue;
6398
6398
  }
6399
- current += ch;
6399
+ current2 += ch;
6400
6400
  continue;
6401
6401
  }
6402
6402
  if (mode === "double") {
@@ -6407,21 +6407,21 @@ function shlexSplit(s) {
6407
6407
  if (ch === "\\") {
6408
6408
  const nxt = s[i + 1];
6409
6409
  if (nxt === "$" || nxt === "`" || nxt === '"' || nxt === "\\" || nxt === "\n") {
6410
- current += nxt;
6410
+ current2 += nxt;
6411
6411
  i++;
6412
6412
  continue;
6413
6413
  }
6414
- current += "\\";
6414
+ current2 += "\\";
6415
6415
  continue;
6416
6416
  }
6417
- current += ch;
6417
+ current2 += ch;
6418
6418
  continue;
6419
6419
  }
6420
6420
  }
6421
6421
  if (mode !== "normal") {
6422
6422
  throw new Error("unmatched quote");
6423
6423
  }
6424
- if (hasCurrent) out.push(current);
6424
+ if (hasCurrent) out.push(current2);
6425
6425
  return out;
6426
6426
  }
6427
6427
 
@@ -7136,16 +7136,16 @@ function computeDrift(opts) {
7136
7136
  const absPath = resolvePinPath(opts);
7137
7137
  const doc = loadDoc(absPath);
7138
7138
  const pinned = doc && typeof doc === "object" && doc["pins"] && typeof doc["pins"] === "object" ? doc["pins"] : {};
7139
- const current = computeCurrent(opts);
7139
+ const current2 = computeCurrent(opts);
7140
7140
  const drift = [];
7141
7141
  for (const [p, h] of Object.entries(pinned)) {
7142
- if (current[p] && current[p] !== h) drift.push(p);
7142
+ if (current2[p] && current2[p] !== h) drift.push(p);
7143
7143
  }
7144
- const missingFromPin = Object.keys(current).filter((p) => !(p in pinned));
7145
- const missingFiles = Object.keys(pinned).filter((p) => !(p in current));
7144
+ const missingFromPin = Object.keys(current2).filter((p) => !(p in pinned));
7145
+ const missingFiles = Object.keys(pinned).filter((p) => !(p in current2));
7146
7146
  return {
7147
7147
  pinned,
7148
- current,
7148
+ current: current2,
7149
7149
  drift: drift.sort(),
7150
7150
  missing_from_pin: missingFromPin.sort(),
7151
7151
  missing_files: missingFiles.sort(),
@@ -7440,19 +7440,19 @@ function sortKeyJsonifyTopLevel(json) {
7440
7440
  for (const k of Object.keys(obj).sort()) sorted[k] = obj[k];
7441
7441
  return JSON.stringify(sorted);
7442
7442
  }
7443
- function resolveCacheDir(cfg, repoRoot) {
7444
- const raw = cfg.dir ?? ".crosscheck/node_cache";
7443
+ function resolveCacheDir(cfg2, repoRoot) {
7444
+ const raw = cfg2.dir ?? ".crosscheck/node_cache";
7445
7445
  if (isAbsolute(raw)) return raw;
7446
7446
  return resolve(repoRoot ?? process.cwd(), raw);
7447
7447
  }
7448
- function cachePath(cfg, repoRoot, key) {
7449
- return join3(resolveCacheDir(cfg, repoRoot), key.slice(0, 2), `${key}.json`);
7448
+ function cachePath(cfg2, repoRoot, key) {
7449
+ return join3(resolveCacheDir(cfg2, repoRoot), key.slice(0, 2), `${key}.json`);
7450
7450
  }
7451
- function getNodeCache(cfg, repoRoot, key, nowMs) {
7452
- if (!cfg || cfg.enabled === false) return null;
7453
- const p = cachePath(cfg, repoRoot, key);
7451
+ function getNodeCache(cfg2, repoRoot, key, nowMs) {
7452
+ if (!cfg2 || cfg2.enabled === false) return null;
7453
+ const p = cachePath(cfg2, repoRoot, key);
7454
7454
  if (!existsSync4(p)) return null;
7455
- const ttlS = cfg.ttl_seconds ?? 604800;
7455
+ const ttlS = cfg2.ttl_seconds ?? 604800;
7456
7456
  try {
7457
7457
  const st = statSync4(p);
7458
7458
  if (ttlS > 0) {
@@ -7470,21 +7470,21 @@ function getNodeCache(cfg, repoRoot, key, nowMs) {
7470
7470
  return null;
7471
7471
  }
7472
7472
  }
7473
- function atomicWriteJson(path15, payload) {
7474
- mkdirSync4(dirname2(path15), { recursive: true });
7475
- const tmp = `${path15}.${process.pid}.${Date.now()}.tmp`;
7473
+ function atomicWriteJson(path16, payload) {
7474
+ mkdirSync4(dirname2(path16), { recursive: true });
7475
+ const tmp = `${path16}.${process.pid}.${Date.now()}.tmp`;
7476
7476
  writeFileSync3(tmp, JSON.stringify(payload), "utf8");
7477
- renameSync(tmp, path15);
7477
+ renameSync(tmp, path16);
7478
7478
  }
7479
- function putNodeCache(cfg, repoRoot, key, value) {
7480
- if (!cfg || cfg.enabled === false) return;
7479
+ function putNodeCache(cfg2, repoRoot, key, value) {
7480
+ if (!cfg2 || cfg2.enabled === false) return;
7481
7481
  try {
7482
- const p = cachePath(cfg, repoRoot, key);
7482
+ const p = cachePath(cfg2, repoRoot, key);
7483
7483
  atomicWriteJson(p, value);
7484
- const maxEntries = cfg.max_entries ?? 2e3;
7485
- const maxBytes = cfg.max_cache_bytes ?? 100 * 1024 * 1024;
7484
+ const maxEntries = cfg2.max_entries ?? 2e3;
7485
+ const maxBytes = cfg2.max_cache_bytes ?? 100 * 1024 * 1024;
7486
7486
  if (maxEntries <= 0 && maxBytes <= 0) return;
7487
- const base = resolveCacheDir(cfg, repoRoot);
7487
+ const base = resolveCacheDir(cfg2, repoRoot);
7488
7488
  if (!existsSync4(base)) return;
7489
7489
  const entries = [];
7490
7490
  let totalBytes = 0;
@@ -8602,31 +8602,31 @@ async function runFetch(args, opts) {
8602
8602
  const url = String(args["url"] ?? "");
8603
8603
  const force = Boolean(args["force_refresh"]);
8604
8604
  const sessionId = typeof args["session_id"] === "string" ? args["session_id"] : null;
8605
- const cfg = { ...DEFAULT_CONFIG, ...opts.config ?? {} };
8605
+ const cfg2 = { ...DEFAULT_CONFIG, ...opts.config ?? {} };
8606
8606
  const base = { tool: "fetch", url };
8607
- if (!cfg.enabled) {
8607
+ if (!cfg2.enabled) {
8608
8608
  return { ...base, accepted: false, reason: "fetch is disabled" };
8609
8609
  }
8610
8610
  if (!(url.startsWith("https://") || url.startsWith("http://"))) {
8611
8611
  return { ...base, accepted: false, reason: "only http/https schemes are supported" };
8612
8612
  }
8613
- if (cfg.url_allowlist.length === 0) {
8613
+ if (cfg2.url_allowlist.length === 0) {
8614
8614
  return {
8615
8615
  ...base,
8616
8616
  accepted: false,
8617
8617
  reason: "fetch.url_allowlist is empty; no URLs may be fetched"
8618
8618
  };
8619
8619
  }
8620
- if (!isUrlAllowed(url, cfg.url_allowlist)) {
8620
+ if (!isUrlAllowed(url, cfg2.url_allowlist)) {
8621
8621
  return {
8622
8622
  ...base,
8623
8623
  accepted: false,
8624
8624
  reason: "url is not covered by fetch.url_allowlist",
8625
- allowlist: cfg.url_allowlist
8625
+ allowlist: cfg2.url_allowlist
8626
8626
  };
8627
8627
  }
8628
- const maxBytesSession = Math.max(0, cfg.max_bytes_per_session);
8629
- const maxUniqueHosts = Math.max(0, cfg.max_unique_hosts_per_session);
8628
+ const maxBytesSession = Math.max(0, cfg2.max_bytes_per_session);
8629
+ const maxUniqueHosts = Math.max(0, cfg2.max_unique_hosts_per_session);
8630
8630
  const host = extractHost(url);
8631
8631
  if (sessionId && opts.storage && (maxBytesSession > 0 || maxUniqueHosts > 0)) {
8632
8632
  const { total_bytes, unique_hosts } = await opts.storage.getFetchEgressTotals(sessionId);
@@ -8666,7 +8666,7 @@ async function runFetch(args, opts) {
8666
8666
  }
8667
8667
  }
8668
8668
  }
8669
- const evDir = resolveEvidenceDir(cfg.evidence_dir, opts.repoRoot);
8669
+ const evDir = resolveEvidenceDir(cfg2.evidence_dir, opts.repoRoot);
8670
8670
  const urlHash16 = sha256Hex2(url).slice(0, 16);
8671
8671
  const metaPath = path6.join(evDir, `by-url-${urlHash16}.json`);
8672
8672
  if (existsSync5(metaPath) && !force) {
@@ -8688,7 +8688,7 @@ async function runFetch(args, opts) {
8688
8688
  try {
8689
8689
  response = await withTimeout(
8690
8690
  fetchFn(url, { headers: { "User-Agent": "crosscheck-agent/0.1" } }),
8691
- cfg.timeout_s * 1e3
8691
+ cfg2.timeout_s * 1e3
8692
8692
  );
8693
8693
  } catch (e) {
8694
8694
  return {
@@ -8714,8 +8714,8 @@ async function runFetch(args, opts) {
8714
8714
  const reader = response.body?.getReader?.();
8715
8715
  if (!reader) {
8716
8716
  const buf = await response.arrayBuffer();
8717
- if (buf.byteLength > cfg.max_bytes) {
8718
- return { ...base, accepted: false, reason: `response exceeds max_bytes=${cfg.max_bytes}` };
8717
+ if (buf.byteLength > cfg2.max_bytes) {
8718
+ return { ...base, accepted: false, reason: `response exceeds max_bytes=${cfg2.max_bytes}` };
8719
8719
  }
8720
8720
  data = new Uint8Array(buf);
8721
8721
  } else {
@@ -8727,7 +8727,7 @@ async function runFetch(args, opts) {
8727
8727
  if (done) break;
8728
8728
  if (value) {
8729
8729
  total += value.byteLength;
8730
- if (total > cfg.max_bytes) {
8730
+ if (total > cfg2.max_bytes) {
8731
8731
  exceeded = true;
8732
8732
  await reader.cancel().catch(() => {
8733
8733
  });
@@ -8737,7 +8737,7 @@ async function runFetch(args, opts) {
8737
8737
  }
8738
8738
  }
8739
8739
  if (exceeded) {
8740
- return { ...base, accepted: false, reason: `response exceeds max_bytes=${cfg.max_bytes}` };
8740
+ return { ...base, accepted: false, reason: `response exceeds max_bytes=${cfg2.max_bytes}` };
8741
8741
  }
8742
8742
  data = new Uint8Array(total);
8743
8743
  let offset = 0;
@@ -12591,15 +12591,15 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
12591
12591
  var DEFAULT_PACKAGE = "crosscheck-cli";
12592
12592
  var FETCH_TIMEOUT_MS = 3e3;
12593
12593
  function engineVersion() {
12594
- return true ? "0.2.15" : "0.0.0-dev";
12594
+ return true ? "0.2.16" : "0.0.0-dev";
12595
12595
  }
12596
12596
  function defaultUpdateCachePath() {
12597
12597
  const base = process.env["CROSSCHECK_DATA_DIR"] || path10.join(os.homedir() || os.tmpdir(), ".crosscheck");
12598
12598
  return path10.join(base, "update_check.json");
12599
12599
  }
12600
- function isNewer(latest, current) {
12600
+ function isNewer(latest, current2) {
12601
12601
  const a = parseTriple(latest);
12602
- const b = parseTriple(current);
12602
+ const b = parseTriple(current2);
12603
12603
  for (let i = 0; i < 3; i += 1) {
12604
12604
  if (a[i] !== b[i]) return a[i] > b[i];
12605
12605
  }
@@ -12609,13 +12609,13 @@ function parseTriple(v) {
12609
12609
  const parts = (v.split("-")[0] ?? "").split(".").map((n) => Number.parseInt(n, 10) || 0);
12610
12610
  return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
12611
12611
  }
12612
- function buildNotice(pkg, current, latest) {
12612
+ function buildNotice(pkg, current2, latest) {
12613
12613
  return {
12614
12614
  update_available: true,
12615
12615
  package: pkg,
12616
- current_version: current,
12616
+ current_version: current2,
12617
12617
  latest_version: latest,
12618
- message: `crosscheck ${latest} is available \u2014 you're on ${current}. To upgrade, run:
12618
+ message: `crosscheck ${latest} is available \u2014 you're on ${current2}. To upgrade, run:
12619
12619
 
12620
12620
  npm install -g crosscheck-cli@latest
12621
12621
 
@@ -14302,13 +14302,214 @@ function attachUpdateNotice(result, toolName, opts) {
14302
14302
 
14303
14303
  // src/core/telemetry.ts
14304
14304
  init_esm_shims();
14305
+ import crypto4 from "crypto";
14306
+
14307
+ // src/core/segments.ts
14308
+ init_esm_shims();
14309
+ import crypto3 from "crypto";
14310
+ import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "fs";
14311
+ import path13 from "path";
14312
+
14313
+ // src/core/git-context.ts
14314
+ init_esm_shims();
14315
+ import { execFileSync } from "child_process";
14305
14316
  import crypto2 from "crypto";
14317
+ var TRUNK_BRANCHES = /* @__PURE__ */ new Set([
14318
+ "main",
14319
+ "master",
14320
+ "dev",
14321
+ "develop",
14322
+ "trunk"
14323
+ ]);
14324
+ function git(args, cwd) {
14325
+ try {
14326
+ return execFileSync("git", args, {
14327
+ cwd,
14328
+ encoding: "utf8",
14329
+ stdio: ["ignore", "pipe", "ignore"],
14330
+ timeout: 2e3
14331
+ }).trim();
14332
+ } catch {
14333
+ return null;
14334
+ }
14335
+ }
14336
+ function repoIdentity(cwd) {
14337
+ const root = git(["rev-list", "--max-parents=0", "HEAD"], cwd);
14338
+ if (root) return `root:${root.split("\n").pop().trim()}`;
14339
+ const remote = git(["config", "--get", "remote.origin.url"], cwd);
14340
+ if (remote) return `remote:${remote}`;
14341
+ const top = git(["rev-parse", "--show-toplevel"], cwd);
14342
+ return top ? `path:${top}` : null;
14343
+ }
14344
+ function fingerprintOf(seatKey, parts) {
14345
+ return crypto2.createHmac("sha256", seatKey).update(parts.join("\0")).digest("hex");
14346
+ }
14347
+ function readGitContext(seatKey, cwd) {
14348
+ try {
14349
+ const inside = git(["rev-parse", "--is-inside-work-tree"], cwd);
14350
+ if (inside !== "true") return { signal: "no_repo" };
14351
+ const repo = repoIdentity(cwd);
14352
+ if (!repo) return { signal: "no_repo" };
14353
+ const branch = git(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
14354
+ if (!branch || branch === "HEAD") {
14355
+ const wt = git(["rev-parse", "--show-toplevel"], cwd) ?? cwd;
14356
+ return {
14357
+ fingerprint: fingerprintOf(seatKey, [repo, "worktree", wt]),
14358
+ signal: "worktree",
14359
+ localLabel: `worktree:${wt}`
14360
+ };
14361
+ }
14362
+ if (TRUNK_BRANCHES.has(branch)) {
14363
+ return {
14364
+ fingerprint: fingerprintOf(seatKey, [repo, "trunk"]),
14365
+ signal: "trunk",
14366
+ localLabel: `${branch} (maintenance)`
14367
+ };
14368
+ }
14369
+ return {
14370
+ fingerprint: fingerprintOf(seatKey, [repo, "branch", branch]),
14371
+ signal: "branch",
14372
+ localLabel: branch
14373
+ };
14374
+ } catch {
14375
+ return { signal: "no_repo" };
14376
+ }
14377
+ }
14378
+ function sameContext(a, b) {
14379
+ return a.fingerprint === b.fingerprint && a.signal === b.signal;
14380
+ }
14381
+
14382
+ // src/core/segments.ts
14383
+ var POST_TIMEOUT_MS = 5e3;
14384
+ function canonicalSegmentBody(s) {
14385
+ return [
14386
+ s.segmentId,
14387
+ s.seatId,
14388
+ s.orgId,
14389
+ s.sessionId,
14390
+ String(s.seq),
14391
+ s.fingerprint ?? "",
14392
+ s.signal,
14393
+ s.startedAt,
14394
+ s.endedAt ?? ""
14395
+ ].join("|");
14396
+ }
14397
+ function signSegment(seatKey, s) {
14398
+ return crypto3.createHmac("sha256", seatKey).update(canonicalSegmentBody(s)).digest("hex");
14399
+ }
14400
+ var cfgResolved = false;
14401
+ var cfg = null;
14306
14402
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
14403
+ function getConfig() {
14404
+ if (cfgResolved) return cfg;
14405
+ cfgResolved = true;
14406
+ if (process.env["CROSSCHECK_TELEMETRY"] === "off") return cfg = null;
14407
+ const base = process.env["CROSSCHECK_TELEMETRY_URL"];
14408
+ const seatId = process.env["CROSSCHECK_SEAT_ID"];
14409
+ const orgId = process.env["CROSSCHECK_ORG_ID"];
14410
+ const seatKey = process.env["CROSSCHECK_SEAT_HMAC_KEY"];
14411
+ if (!base || !seatId || !orgId || !seatKey) return cfg = null;
14412
+ if (!UUID_RE.test(seatId) || !UUID_RE.test(orgId)) return cfg = null;
14413
+ const sessionId = process.env["CLAUDE_SESSION_ID"] || process.env["CLAUDE_CODE_SESSION_ID"] || `engine-${crypto3.randomUUID()}`;
14414
+ cfg = {
14415
+ url: base.replace(/\/telemetry\/?$/, "/segments"),
14416
+ seatId,
14417
+ orgId,
14418
+ seatKey,
14419
+ sessionId
14420
+ };
14421
+ return cfg;
14422
+ }
14423
+ var current = null;
14424
+ var nextSeq = 0;
14425
+ function currentFingerprint() {
14426
+ return current?.context.fingerprint;
14427
+ }
14428
+ function rememberLabel(context) {
14429
+ if (!context.fingerprint || !context.localLabel) return;
14430
+ try {
14431
+ const dir = process.env["CROSSCHECK_DATA_DIR"] || path13.join(process.env["HOME"] ?? process.env["USERPROFILE"] ?? ".", ".crosscheck");
14432
+ const file = path13.join(dir, "features.json");
14433
+ let map = {};
14434
+ if (existsSync12(file)) {
14435
+ try {
14436
+ const parsed = JSON.parse(readFileSync14(file, "utf8"));
14437
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) map = parsed;
14438
+ } catch {
14439
+ }
14440
+ }
14441
+ map[context.fingerprint] = {
14442
+ label: context.localLabel,
14443
+ lastSeen: (/* @__PURE__ */ new Date()).toISOString()
14444
+ };
14445
+ mkdirSync9(dir, { recursive: true });
14446
+ writeFileSync8(file, `${JSON.stringify(map, null, 2)}
14447
+ `, "utf8");
14448
+ } catch {
14449
+ }
14450
+ }
14451
+ async function post(url, segments) {
14452
+ try {
14453
+ await fetch(url, {
14454
+ method: "POST",
14455
+ headers: { "content-type": "application/json" },
14456
+ body: JSON.stringify({ segments }),
14457
+ signal: AbortSignal.timeout(POST_TIMEOUT_MS)
14458
+ });
14459
+ } catch {
14460
+ }
14461
+ }
14462
+ function emit(c, seg, endedAt) {
14463
+ const payload = {
14464
+ segmentId: seg.segmentId,
14465
+ seatId: c.seatId,
14466
+ orgId: c.orgId,
14467
+ sessionId: c.sessionId,
14468
+ seq: seg.seq,
14469
+ ...seg.context.fingerprint ? { fingerprint: seg.context.fingerprint } : {},
14470
+ signal: seg.context.signal,
14471
+ startedAt: seg.startedAt,
14472
+ ...endedAt ? { endedAt } : {}
14473
+ };
14474
+ const signatureHmac = signSegment(c.seatKey, payload);
14475
+ void post(c.url, [{ ...payload, signatureHmac }]);
14476
+ }
14477
+ function syncSegment(cwd) {
14478
+ try {
14479
+ const c = getConfig();
14480
+ if (!c) return;
14481
+ const context = readGitContext(c.seatKey, cwd ?? process.cwd());
14482
+ if (current && sameContext(current.context, context)) return;
14483
+ const now = (/* @__PURE__ */ new Date()).toISOString();
14484
+ if (current) emit(c, current, now);
14485
+ current = {
14486
+ segmentId: crypto3.randomUUID(),
14487
+ seq: nextSeq++,
14488
+ context,
14489
+ startedAt: now
14490
+ };
14491
+ rememberLabel(context);
14492
+ emit(c, current);
14493
+ } catch {
14494
+ }
14495
+ }
14496
+ function closeSegment() {
14497
+ try {
14498
+ const c = getConfig();
14499
+ if (!c || !current) return;
14500
+ emit(c, current, (/* @__PURE__ */ new Date()).toISOString());
14501
+ current = null;
14502
+ } catch {
14503
+ }
14504
+ }
14505
+
14506
+ // src/core/telemetry.ts
14507
+ var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
14307
14508
  var BATCH_MAX = 100;
14308
14509
  var FLUSH_BATCH_AT = 25;
14309
14510
  var FLUSH_INTERVAL_MS = 15e3;
14310
14511
  var MAX_QUEUE = 500;
14311
- var POST_TIMEOUT_MS = 5e3;
14512
+ var POST_TIMEOUT_MS2 = 5e3;
14312
14513
  var PROVIDER_TO_SCHEMA = {
14313
14514
  anthropic: "anthropic",
14314
14515
  openai: "openai",
@@ -14338,7 +14539,7 @@ function toolToPattern(toolName) {
14338
14539
  return TOOL_TO_PATTERN[String(toolName).toLowerCase()] ?? "other";
14339
14540
  }
14340
14541
  function canonicalEventBody(e) {
14341
- return [
14542
+ const base = [
14342
14543
  e.eventId,
14343
14544
  e.seatId,
14344
14545
  e.orgId,
@@ -14353,13 +14554,22 @@ function canonicalEventBody(e) {
14353
14554
  e.status,
14354
14555
  e.errorClass ?? ""
14355
14556
  ].join("|");
14557
+ if (e.runId === void 0 && e.purpose === void 0 && e.fingerprint === void 0) {
14558
+ return base;
14559
+ }
14560
+ return [base, e.runId ?? "", e.purpose ?? "", e.fingerprint ?? ""].join("|");
14561
+ }
14562
+ function normalizePurpose(v) {
14563
+ if (typeof v !== "string") return void 0;
14564
+ const slug = v.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-").slice(0, 32);
14565
+ return /^[a-z0-9][a-z0-9_-]*$/.test(slug) ? slug : void 0;
14356
14566
  }
14357
14567
  function signEvent(seatSigningKey, e) {
14358
- return crypto2.createHmac("sha256", seatSigningKey).update(canonicalEventBody(e)).digest("hex");
14568
+ return crypto4.createHmac("sha256", seatSigningKey).update(canonicalEventBody(e)).digest("hex");
14359
14569
  }
14360
14570
  var configResolved = false;
14361
14571
  var config = null;
14362
- function getConfig() {
14572
+ function getConfig2() {
14363
14573
  if (configResolved) return config;
14364
14574
  configResolved = true;
14365
14575
  if (process.env["CROSSCHECK_TELEMETRY"] === "off") return config = null;
@@ -14368,7 +14578,7 @@ function getConfig() {
14368
14578
  const orgId = process.env["CROSSCHECK_ORG_ID"];
14369
14579
  const seatKey = process.env["CROSSCHECK_SEAT_HMAC_KEY"];
14370
14580
  if (!url || !seatId || !orgId || !seatKey) return config = null;
14371
- if (!UUID_RE.test(seatId) || !UUID_RE.test(orgId)) return config = null;
14581
+ if (!UUID_RE2.test(seatId) || !UUID_RE2.test(orgId)) return config = null;
14372
14582
  config = { url, seatId, orgId, seatKey };
14373
14583
  return config;
14374
14584
  }
@@ -14403,7 +14613,10 @@ function usageEventsFromEnvelope(out, toolName) {
14403
14613
  completionTokens: intNonNeg(u.completion_tokens),
14404
14614
  costUsdEstimate: cost,
14405
14615
  latencyMs,
14406
- status: "ok"
14616
+ status: "ok",
14617
+ // Envelopes have carried `purpose` per call all along (debate /
14618
+ // debate-coreason / synth / worker); it just was never forwarded.
14619
+ purpose: normalizePurpose(u.purpose)
14407
14620
  });
14408
14621
  }
14409
14622
  return events;
@@ -14418,11 +14631,11 @@ function scheduleFlush() {
14418
14631
  }, FLUSH_INTERVAL_MS);
14419
14632
  if (typeof flushTimer.unref === "function") flushTimer.unref();
14420
14633
  }
14421
- function enqueue(cfg, input) {
14634
+ function enqueue(cfg2, input, runId, fingerprint) {
14422
14635
  const base = {
14423
- eventId: crypto2.randomUUID(),
14424
- seatId: cfg.seatId,
14425
- orgId: cfg.orgId,
14636
+ eventId: crypto4.randomUUID(),
14637
+ seatId: cfg2.seatId,
14638
+ orgId: cfg2.orgId,
14426
14639
  ts: (/* @__PURE__ */ new Date()).toISOString(),
14427
14640
  provider: input.provider,
14428
14641
  model: input.model,
@@ -14431,9 +14644,17 @@ function enqueue(cfg, input) {
14431
14644
  completionTokens: input.completionTokens,
14432
14645
  costUsdEstimate: input.costUsdEstimate,
14433
14646
  latencyMs: input.latencyMs,
14434
- status: input.status
14435
- };
14436
- const signatureHmac = signEvent(cfg.seatKey, base);
14647
+ status: input.status,
14648
+ runId,
14649
+ // Omit entirely (not "") when unknown, so the canonical body stays
14650
+ // consistent with what the server reconstructs from the JSON.
14651
+ ...input.purpose === void 0 ? {} : { purpose: input.purpose },
14652
+ // Panel attribution is EXACT: we know the active git segment at emit
14653
+ // time, so there is no time-join and no ambiguity window — unlike host
14654
+ // tokens, which can only be matched by session and timestamp.
14655
+ ...fingerprint === void 0 ? {} : { fingerprint }
14656
+ };
14657
+ const signatureHmac = signEvent(cfg2.seatKey, base);
14437
14658
  queue.push({ ...base, signatureHmac });
14438
14659
  if (queue.length > MAX_QUEUE) queue.splice(0, queue.length - MAX_QUEUE);
14439
14660
  if (queue.length >= FLUSH_BATCH_AT) void flush();
@@ -14441,10 +14662,14 @@ function enqueue(cfg, input) {
14441
14662
  }
14442
14663
  function recordUsage(out, toolName) {
14443
14664
  try {
14444
- const cfg = getConfig();
14445
- if (!cfg) return;
14665
+ const cfg2 = getConfig2();
14666
+ if (!cfg2) return;
14446
14667
  if (toolName === "update_crosscheck") return;
14447
- for (const ev of usageEventsFromEnvelope(out, toolName)) enqueue(cfg, ev);
14668
+ const runId = crypto4.randomUUID();
14669
+ const fingerprint = currentFingerprint();
14670
+ for (const ev of usageEventsFromEnvelope(out, toolName)) {
14671
+ enqueue(cfg2, ev, runId, fingerprint);
14672
+ }
14448
14673
  } catch {
14449
14674
  }
14450
14675
  }
@@ -14455,7 +14680,7 @@ async function postBatch(url, batch) {
14455
14680
  method: "POST",
14456
14681
  headers: { "content-type": "application/json" },
14457
14682
  body: JSON.stringify({ events: batch }),
14458
- signal: AbortSignal.timeout(POST_TIMEOUT_MS)
14683
+ signal: AbortSignal.timeout(POST_TIMEOUT_MS2)
14459
14684
  });
14460
14685
  } catch {
14461
14686
  queue.unshift(...batch);
@@ -14477,15 +14702,15 @@ async function postBatch(url, batch) {
14477
14702
  if (queue.length > MAX_QUEUE) queue.splice(0, queue.length - MAX_QUEUE);
14478
14703
  }
14479
14704
  async function flush() {
14480
- const cfg = getConfig();
14481
- if (!cfg) return;
14705
+ const cfg2 = getConfig2();
14706
+ if (!cfg2) return;
14482
14707
  if (flushTimer) {
14483
14708
  clearTimeout(flushTimer);
14484
14709
  flushTimer = null;
14485
14710
  }
14486
14711
  while (queue.length > 0) {
14487
14712
  const batch = queue.splice(0, BATCH_MAX);
14488
- await postBatch(cfg.url, batch);
14713
+ await postBatch(cfg2.url, batch);
14489
14714
  if (queue.length > 0 && queue[0] === batch[0]) break;
14490
14715
  }
14491
14716
  }
@@ -14553,6 +14778,7 @@ function createServer(opts = {}) {
14553
14778
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
14554
14779
  const startedHr = process.hrtime.bigint();
14555
14780
  const id = nextEventId();
14781
+ syncSegment(opts.repoRoot);
14556
14782
  const gateBlock = configPinGate(name, {
14557
14783
  ...opts.repoRoot ? { repoRoot: opts.repoRoot } : {},
14558
14784
  ...opts.configPinning ? { config: opts.configPinning } : {},
@@ -14634,6 +14860,13 @@ function createServer(opts = {}) {
14634
14860
  async function connectAndServe(transport, opts = {}) {
14635
14861
  const server = createServer(opts);
14636
14862
  await server.connect(transport);
14863
+ for (const sig of ["SIGINT", "SIGTERM"]) {
14864
+ process.once(sig, () => {
14865
+ closeSegment();
14866
+ process.exit(0);
14867
+ });
14868
+ }
14869
+ process.once("beforeExit", () => closeSegment());
14637
14870
  const cliVersion = process.env["CROSSCHECK_CLI_VERSION"];
14638
14871
  void maybeCheckForUpdate({
14639
14872
  currentVersion: cliVersion ?? SERVER_VERSION,
@@ -14671,8 +14904,8 @@ function buildToolRegistry(opts) {
14671
14904
 
14672
14905
  // src/core/config-file.ts
14673
14906
  init_esm_shims();
14674
- import { existsSync as existsSync12, readFileSync as readFileSync14 } from "fs";
14675
- import path13 from "path";
14907
+ import { existsSync as existsSync13, readFileSync as readFileSync15 } from "fs";
14908
+ import path14 from "path";
14676
14909
  import { randomBytes as randomBytes2 } from "crypto";
14677
14910
  function asObj(v) {
14678
14911
  return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
@@ -14700,18 +14933,18 @@ function pickNums(src, keys) {
14700
14933
  }
14701
14934
  function findConfigFile(opts) {
14702
14935
  const direct = opts.explicitPath ?? process.env["CROSSCHECK_CONFIG_PATH"];
14703
- if (direct) return existsSync12(direct) ? direct : void 0;
14936
+ if (direct) return existsSync13(direct) ? direct : void 0;
14704
14937
  const candidates = [];
14705
14938
  let dir = opts.startDir;
14706
14939
  for (let i = 0; dir && i < 8; i += 1) {
14707
- candidates.push(path13.join(dir, "crosscheck.config.json"));
14708
- const parent = path13.dirname(dir);
14940
+ candidates.push(path14.join(dir, "crosscheck.config.json"));
14941
+ const parent = path14.dirname(dir);
14709
14942
  if (parent === dir) break;
14710
14943
  dir = parent;
14711
14944
  }
14712
- if (opts.repoRoot) candidates.push(path13.join(opts.repoRoot, "crosscheck.config.json"));
14713
- if (opts.dataDir) candidates.push(path13.join(opts.dataDir, "crosscheck.config.json"));
14714
- return candidates.find((p) => existsSync12(p));
14945
+ if (opts.repoRoot) candidates.push(path14.join(opts.repoRoot, "crosscheck.config.json"));
14946
+ if (opts.dataDir) candidates.push(path14.join(opts.dataDir, "crosscheck.config.json"));
14947
+ return candidates.find((p) => existsSync13(p));
14715
14948
  }
14716
14949
  function mapConfig(raw, sourcePath) {
14717
14950
  const result = { sourcePath };
@@ -14728,16 +14961,16 @@ function mapConfig(raw, sourcePath) {
14728
14961
  }
14729
14962
  const nc = asObj(raw["node_cache"]);
14730
14963
  if (nc) {
14731
- const cfg = {
14964
+ const cfg2 = {
14732
14965
  ...pickNums(nc, ["ttl_seconds", "max_entries", "max_cache_bytes"])
14733
14966
  };
14734
- if (bool(nc["enabled"]) !== void 0) cfg.enabled = bool(nc["enabled"]);
14735
- if (str(nc["dir"]) !== void 0) cfg.dir = str(nc["dir"]);
14736
- result.nodeCache = cfg;
14967
+ if (bool(nc["enabled"]) !== void 0) cfg2.enabled = bool(nc["enabled"]);
14968
+ if (str(nc["dir"]) !== void 0) cfg2.dir = str(nc["dir"]);
14969
+ result.nodeCache = cfg2;
14737
14970
  }
14738
14971
  const f = asObj(raw["fetch"]);
14739
14972
  if (f) {
14740
- const cfg = {
14973
+ const cfg2 = {
14741
14974
  ...pickNums(f, [
14742
14975
  "max_bytes",
14743
14976
  "max_bytes_per_session",
@@ -14745,10 +14978,10 @@ function mapConfig(raw, sourcePath) {
14745
14978
  "timeout_s"
14746
14979
  ])
14747
14980
  };
14748
- if (bool(f["enabled"]) !== void 0) cfg.enabled = bool(f["enabled"]);
14749
- if (strArr(f["url_allowlist"]) !== void 0) cfg.url_allowlist = strArr(f["url_allowlist"]);
14750
- if (str(f["evidence_dir"]) !== void 0) cfg.evidence_dir = str(f["evidence_dir"]);
14751
- result.fetchConfig = cfg;
14981
+ if (bool(f["enabled"]) !== void 0) cfg2.enabled = bool(f["enabled"]);
14982
+ if (strArr(f["url_allowlist"]) !== void 0) cfg2.url_allowlist = strArr(f["url_allowlist"]);
14983
+ if (str(f["evidence_dir"]) !== void 0) cfg2.evidence_dir = str(f["evidence_dir"]);
14984
+ result.fetchConfig = cfg2;
14752
14985
  }
14753
14986
  if ("provider_allowlist" in raw) {
14754
14987
  const pa = raw["provider_allowlist"];
@@ -14772,10 +15005,10 @@ function mapConfig(raw, sourcePath) {
14772
15005
  if (retries) {
14773
15006
  const maxAttempts = num(retries["max_attempts"]);
14774
15007
  const baseS = num(retries["backoff_base_s"]);
14775
- const cfg = {};
14776
- if (maxAttempts !== void 0) cfg.maxAttempts = Math.max(1, Math.trunc(maxAttempts));
14777
- if (baseS !== void 0) cfg.backoffBaseMs = Math.round(baseS * 1e3);
14778
- if (Object.keys(cfg).length > 0) setRetryConfig(cfg);
15008
+ const cfg2 = {};
15009
+ if (maxAttempts !== void 0) cfg2.maxAttempts = Math.max(1, Math.trunc(maxAttempts));
15010
+ if (baseS !== void 0) cfg2.backoffBaseMs = Math.round(baseS * 1e3);
15011
+ if (Object.keys(cfg2).length > 0) setRetryConfig(cfg2);
14779
15012
  }
14780
15013
  const rl = asObj(raw["rate_limits"]);
14781
15014
  if (rl) {
@@ -14801,22 +15034,22 @@ function mapConfig(raw, sourcePath) {
14801
15034
  }
14802
15035
  const red = asObj(raw["redaction"]);
14803
15036
  if (red) {
14804
- const cfg = {};
14805
- if (bool(red["enabled"]) !== void 0) cfg.enabled = bool(red["enabled"]);
15037
+ const cfg2 = {};
15038
+ if (bool(red["enabled"]) !== void 0) cfg2.enabled = bool(red["enabled"]);
14806
15039
  const extras = strArr(red["patterns_extra"]);
14807
- if (extras && extras.length > 0) cfg.patterns_extra = extras;
15040
+ if (extras && extras.length > 0) cfg2.patterns_extra = extras;
14808
15041
  if (bool(red["hmac_tokens"]) === true) {
14809
- cfg.hmac_tokens = true;
14810
- cfg.hmac_secret = randomBytes2(32);
15042
+ cfg2.hmac_tokens = true;
15043
+ cfg2.hmac_secret = randomBytes2(32);
14811
15044
  }
14812
- if (Object.keys(cfg).length > 0) setRedactionConfig(cfg);
15045
+ if (Object.keys(cfg2).length > 0) setRedactionConfig(cfg2);
14813
15046
  }
14814
15047
  const personas = asObj(raw["personas"]);
14815
15048
  if (personas) {
14816
- const cfg = {};
14817
- if (bool(personas["enabled"]) !== void 0) cfg.enabled = bool(personas["enabled"]);
14818
- if (str(personas["dir"]) !== void 0) cfg.dir = str(personas["dir"]);
14819
- if (Object.keys(cfg).length > 0) setPersonaConfig(cfg);
15049
+ const cfg2 = {};
15050
+ if (bool(personas["enabled"]) !== void 0) cfg2.enabled = bool(personas["enabled"]);
15051
+ if (str(personas["dir"]) !== void 0) cfg2.dir = str(personas["dir"]);
15052
+ if (Object.keys(cfg2).length > 0) setPersonaConfig(cfg2);
14820
15053
  }
14821
15054
  return result;
14822
15055
  }
@@ -14825,7 +15058,7 @@ function loadConfigFile(opts) {
14825
15058
  if (!file) return null;
14826
15059
  let raw;
14827
15060
  try {
14828
- raw = JSON.parse(readFileSync14(file, "utf8"));
15061
+ raw = JSON.parse(readFileSync15(file, "utf8"));
14829
15062
  } catch (e) {
14830
15063
  try {
14831
15064
  process.stderr.write(
@@ -14845,7 +15078,7 @@ function loadConfigFile(opts) {
14845
15078
  var SHUTDOWN_TIMEOUT_MS = 2e3;
14846
15079
  async function main() {
14847
15080
  enforceLicenseOrExit();
14848
- const cfgDataDir = process.env["CROSSCHECK_DATA_DIR"] || path14.join(process.env["HOME"] ?? process.env["USERPROFILE"] ?? "", ".crosscheck");
15081
+ const cfgDataDir = process.env["CROSSCHECK_DATA_DIR"] || path15.join(process.env["HOME"] ?? process.env["USERPROFILE"] ?? "", ".crosscheck");
14849
15082
  const configFile = loadConfigFile({
14850
15083
  startDir: process.cwd(),
14851
15084
  repoRoot: findGitRoot(__dirname),
@@ -14870,7 +15103,7 @@ async function main() {
14870
15103
  installShutdownHandlers();
14871
15104
  const bundledPricing = (() => {
14872
15105
  try {
14873
- return path14.join(path14.dirname(fileURLToPath3(import.meta.url)), "pricing.json");
15106
+ return path15.join(path15.dirname(fileURLToPath3(import.meta.url)), "pricing.json");
14874
15107
  } catch {
14875
15108
  return void 0;
14876
15109
  }
@@ -14879,7 +15112,7 @@ async function main() {
14879
15112
  process.env["CROSSCHECK_PRICING_PATH"],
14880
15113
  resolveRepoFile("config/pricing.json"),
14881
15114
  bundledPricing
14882
- ].find((p) => p && existsSync13(p));
15115
+ ].find((p) => p && existsSync14(p));
14883
15116
  const pricing = pricingPath ? loadPricing(pricingPath) : {};
14884
15117
  const providers = buildProviders({ env: process.env, pricing });
14885
15118
  if (Object.keys(providers).length > 0) {
@@ -14897,10 +15130,10 @@ async function main() {
14897
15130
  }
14898
15131
  let storage;
14899
15132
  if (process.env["CROSSCHECK_DB_DISABLED"] !== "1") {
14900
- const dbPath = process.env["CROSSCHECK_DB_PATH"] ?? resolveRepoFile(".crosscheck/db.sqlite") ?? path14.join(cfgDataDir, "db.sqlite");
15133
+ const dbPath = process.env["CROSSCHECK_DB_PATH"] ?? resolveRepoFile(".crosscheck/db.sqlite") ?? path15.join(cfgDataDir, "db.sqlite");
14901
15134
  if (dbPath) {
14902
15135
  try {
14903
- mkdirSync9(path14.dirname(dbPath), { recursive: true });
15136
+ mkdirSync10(path15.dirname(dbPath), { recursive: true });
14904
15137
  const { openBetterSqliteStorage: openBetterSqliteStorage2 } = await Promise.resolve().then(() => (init_better_sqlite3(), better_sqlite3_exports));
14905
15138
  storage = openBetterSqliteStorage2({ path: dbPath });
14906
15139
  await storage.migrate();
@@ -14968,9 +15201,9 @@ function installShutdownHandlers() {
14968
15201
  function resolveRepoFile(rel) {
14969
15202
  let dir = __dirname;
14970
15203
  for (let i = 0; i < 8; i++) {
14971
- const candidate = path14.join(dir, rel);
14972
- if (existsSync13(candidate)) return candidate;
14973
- const parent = path14.dirname(dir);
15204
+ const candidate = path15.join(dir, rel);
15205
+ if (existsSync14(candidate)) return candidate;
15206
+ const parent = path15.dirname(dir);
14974
15207
  if (parent === dir) break;
14975
15208
  dir = parent;
14976
15209
  }
@@ -14979,8 +15212,8 @@ function resolveRepoFile(rel) {
14979
15212
  function findGitRoot(startDir) {
14980
15213
  let dir = startDir;
14981
15214
  for (let i = 0; i < 12; i++) {
14982
- if (existsSync13(path14.join(dir, ".git"))) return dir;
14983
- const parent = path14.dirname(dir);
15215
+ if (existsSync14(path15.join(dir, ".git"))) return dir;
15216
+ const parent = path15.dirname(dir);
14984
15217
  if (parent === dir) break;
14985
15218
  dir = parent;
14986
15219
  }