crosscheck-mcp 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/node-stdio.cjs +1076 -402
- package/dist/node-stdio.cjs.map +1 -1
- package/dist/node-stdio.d.cts +44 -25
- package/dist/node-stdio.d.ts +44 -25
- package/dist/node-stdio.js +1043 -369
- package/dist/node-stdio.js.map +1 -1
- package/dist/personas/auditor-finance.md +35 -0
- package/dist/personas/auditor-security.md +34 -0
- package/dist/personas/design.md +34 -0
- package/dist/personas/engineering.md +41 -0
- package/dist/personas/finance.md +33 -0
- package/dist/personas/research.md +34 -0
- package/package.json +1 -1
package/dist/node-stdio.cjs
CHANGED
|
@@ -942,159 +942,15 @@ __export(node_stdio_exports, {
|
|
|
942
942
|
});
|
|
943
943
|
module.exports = __toCommonJS(node_stdio_exports);
|
|
944
944
|
init_cjs_shims();
|
|
945
|
-
var
|
|
946
|
-
var
|
|
945
|
+
var import_node_fs18 = require("fs");
|
|
946
|
+
var import_node_path17 = __toESM(require("path"), 1);
|
|
947
947
|
var import_node_url2 = require("url");
|
|
948
|
-
var
|
|
949
|
-
|
|
950
|
-
// src/bridge/index.ts
|
|
951
|
-
init_cjs_shims();
|
|
952
|
-
|
|
953
|
-
// src/bridge/python-bridge.ts
|
|
954
|
-
init_cjs_shims();
|
|
955
|
-
var import_node_path = __toESM(require("path"), 1);
|
|
956
|
-
var import_node_url = require("url");
|
|
957
|
-
var import_client = require("@modelcontextprotocol/sdk/client/index.js");
|
|
958
|
-
var import_stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
959
|
-
function defaultServerPath() {
|
|
960
|
-
let here;
|
|
961
|
-
try {
|
|
962
|
-
here = import_node_path.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
963
|
-
} catch {
|
|
964
|
-
here = __dirname;
|
|
965
|
-
}
|
|
966
|
-
const upToPkg = here.includes(`${import_node_path.default.sep}src${import_node_path.default.sep}bridge`) ? "../.." : "..";
|
|
967
|
-
return import_node_path.default.resolve(here, upToPkg, "..", "python", "crosscheck_server.py");
|
|
968
|
-
}
|
|
969
|
-
async function spawnPythonBridge(opts = {}) {
|
|
970
|
-
const pythonPath = opts.pythonPath ?? "python3";
|
|
971
|
-
const serverPath = opts.serverPath ?? defaultServerPath();
|
|
972
|
-
const args = [...opts.extraArgs ?? [], serverPath];
|
|
973
|
-
const transport = new import_stdio.StdioClientTransport({
|
|
974
|
-
command: pythonPath,
|
|
975
|
-
args,
|
|
976
|
-
env: { ...process.env, ...opts.env ?? {} }
|
|
977
|
-
});
|
|
978
|
-
const client = new import_client.Client(
|
|
979
|
-
{ name: "crosscheck-agent-bridge", version: "0.1.0" },
|
|
980
|
-
{ capabilities: {} }
|
|
981
|
-
);
|
|
982
|
-
const initDeadline = opts.initTimeoutMs ?? 3e4;
|
|
983
|
-
await Promise.race([
|
|
984
|
-
client.connect(transport),
|
|
985
|
-
new Promise(
|
|
986
|
-
(_, rej) => setTimeout(
|
|
987
|
-
() => rej(new Error(`bridge: handshake timeout after ${initDeadline}ms`)),
|
|
988
|
-
initDeadline
|
|
989
|
-
)
|
|
990
|
-
)
|
|
991
|
-
]);
|
|
992
|
-
let toolNames = await fetchToolNames(client, initDeadline);
|
|
993
|
-
let closed = false;
|
|
994
|
-
return {
|
|
995
|
-
get toolNames() {
|
|
996
|
-
return toolNames;
|
|
997
|
-
},
|
|
998
|
-
get pid() {
|
|
999
|
-
const p = transport.pid;
|
|
1000
|
-
return typeof p === "number" ? p : null;
|
|
1001
|
-
},
|
|
1002
|
-
async callTool(name, callArgs) {
|
|
1003
|
-
const r = await client.callTool({ name, arguments: callArgs });
|
|
1004
|
-
const content = r.content;
|
|
1005
|
-
if (!Array.isArray(content)) {
|
|
1006
|
-
throw new Error(
|
|
1007
|
-
`bridge: tools/call(${name}) returned a malformed envelope: ${JSON.stringify(r).slice(0, 200)}`
|
|
1008
|
-
);
|
|
1009
|
-
}
|
|
1010
|
-
const out = [];
|
|
1011
|
-
for (const c of content) {
|
|
1012
|
-
if (c && typeof c === "object") {
|
|
1013
|
-
const co = c;
|
|
1014
|
-
out.push({
|
|
1015
|
-
type: String(co["type"] ?? "text"),
|
|
1016
|
-
text: typeof co["text"] === "string" ? co["text"] : JSON.stringify(co["text"])
|
|
1017
|
-
});
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
const isError = r.isError;
|
|
1021
|
-
return isError !== void 0 ? { content: out, isError } : { content: out };
|
|
1022
|
-
},
|
|
1023
|
-
async refreshTools() {
|
|
1024
|
-
toolNames = await fetchToolNames(client, initDeadline);
|
|
1025
|
-
return toolNames;
|
|
1026
|
-
},
|
|
1027
|
-
async close() {
|
|
1028
|
-
if (closed) return;
|
|
1029
|
-
closed = true;
|
|
1030
|
-
try {
|
|
1031
|
-
await client.close();
|
|
1032
|
-
} catch {
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
};
|
|
1036
|
-
}
|
|
1037
|
-
async function fetchToolNames(client, timeoutMs) {
|
|
1038
|
-
const r = await Promise.race([
|
|
1039
|
-
client.listTools(),
|
|
1040
|
-
new Promise(
|
|
1041
|
-
(_, rej) => setTimeout(
|
|
1042
|
-
() => rej(new Error(`bridge: tools/list timeout after ${timeoutMs}ms`)),
|
|
1043
|
-
timeoutMs
|
|
1044
|
-
)
|
|
1045
|
-
)
|
|
1046
|
-
]);
|
|
1047
|
-
const tools = r.tools ?? [];
|
|
1048
|
-
return new Set(
|
|
1049
|
-
tools.map((t) => String(t?.name ?? "")).filter((n) => n.length > 0)
|
|
1050
|
-
);
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
// src/bridge/proxy-tools.ts
|
|
1054
|
-
init_cjs_shims();
|
|
1055
|
-
function buildPythonProxies(bridge) {
|
|
1056
|
-
const proxies = /* @__PURE__ */ new Map();
|
|
1057
|
-
for (const name of bridge.toolNames) {
|
|
1058
|
-
proxies.set(name, makeProxy(bridge, name));
|
|
1059
|
-
}
|
|
1060
|
-
return proxies;
|
|
1061
|
-
}
|
|
1062
|
-
function makeProxy(bridge, name) {
|
|
1063
|
-
return {
|
|
1064
|
-
name,
|
|
1065
|
-
description: `(forwarded to Python crosscheck-agent) ${name}`,
|
|
1066
|
-
// We don't carry the per-tool input schema across the bridge in
|
|
1067
|
-
// route-all mode — the TS server is a transparent forwarder, so
|
|
1068
|
-
// arg validation happens on the Python side. inputSchema is left
|
|
1069
|
-
// permissive; the Python validator throws structured errors that
|
|
1070
|
-
// tunnel back through `callTool()`.
|
|
1071
|
-
inputSchema: {
|
|
1072
|
-
type: "object",
|
|
1073
|
-
additionalProperties: true,
|
|
1074
|
-
description: `Forwarded to Python \u2014 see Python tool '${name}' for full schema.`
|
|
1075
|
-
},
|
|
1076
|
-
handler: async (args) => {
|
|
1077
|
-
const r = await bridge.callTool(name, args);
|
|
1078
|
-
if (!r.content.length) {
|
|
1079
|
-
return {};
|
|
1080
|
-
}
|
|
1081
|
-
const first = r.content[0];
|
|
1082
|
-
if (first.type === "text" && first.text) {
|
|
1083
|
-
try {
|
|
1084
|
-
return JSON.parse(first.text);
|
|
1085
|
-
} catch {
|
|
1086
|
-
return { text: first.text };
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
return { content: r.content };
|
|
1090
|
-
}
|
|
1091
|
-
};
|
|
1092
|
-
}
|
|
948
|
+
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
1093
949
|
|
|
1094
950
|
// src/core/events.ts
|
|
1095
951
|
init_cjs_shims();
|
|
1096
952
|
var import_node_fs = require("fs");
|
|
1097
|
-
var
|
|
953
|
+
var import_node_path = require("path");
|
|
1098
954
|
var StderrEmitter = class {
|
|
1099
955
|
emit(event) {
|
|
1100
956
|
try {
|
|
@@ -1104,10 +960,10 @@ var StderrEmitter = class {
|
|
|
1104
960
|
}
|
|
1105
961
|
};
|
|
1106
962
|
var FileEmitter = class {
|
|
1107
|
-
constructor(
|
|
1108
|
-
this.path =
|
|
963
|
+
constructor(path14) {
|
|
964
|
+
this.path = path14;
|
|
1109
965
|
try {
|
|
1110
|
-
(0, import_node_fs.mkdirSync)((0,
|
|
966
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path14), { recursive: true });
|
|
1111
967
|
} catch {
|
|
1112
968
|
}
|
|
1113
969
|
}
|
|
@@ -1140,13 +996,13 @@ var NullEmitter = class {
|
|
|
1140
996
|
function buildDefaultEmitter(env) {
|
|
1141
997
|
const mode = (env["CROSSCHECK_EVENTS"] ?? "stderr").toLowerCase();
|
|
1142
998
|
if (mode === "off") return new NullEmitter();
|
|
1143
|
-
const
|
|
999
|
+
const path14 = env["CROSSCHECK_EVENTS_PATH"];
|
|
1144
1000
|
if (mode === "file") {
|
|
1145
|
-
return
|
|
1001
|
+
return path14 ? new FileEmitter(path14) : new NullEmitter();
|
|
1146
1002
|
}
|
|
1147
1003
|
if (mode === "both") {
|
|
1148
1004
|
const ems = [new StderrEmitter()];
|
|
1149
|
-
if (
|
|
1005
|
+
if (path14) ems.push(new FileEmitter(path14));
|
|
1150
1006
|
return new TeeEmitter(ems);
|
|
1151
1007
|
}
|
|
1152
1008
|
return new StderrEmitter();
|
|
@@ -1195,10 +1051,10 @@ function envelopeBytes(v) {
|
|
|
1195
1051
|
// src/core/pricing.ts
|
|
1196
1052
|
init_cjs_shims();
|
|
1197
1053
|
var import_node_fs2 = require("fs");
|
|
1198
|
-
function loadPricing(
|
|
1054
|
+
function loadPricing(path14) {
|
|
1199
1055
|
let raw;
|
|
1200
1056
|
try {
|
|
1201
|
-
raw = (0, import_node_fs2.readFileSync)(
|
|
1057
|
+
raw = (0, import_node_fs2.readFileSync)(path14, "utf8");
|
|
1202
1058
|
} catch {
|
|
1203
1059
|
return {};
|
|
1204
1060
|
}
|
|
@@ -1328,7 +1184,7 @@ function isCreditsFailure(status, bodyText) {
|
|
|
1328
1184
|
}
|
|
1329
1185
|
return false;
|
|
1330
1186
|
}
|
|
1331
|
-
function httpFailureToProviderError(provider, status, bodyText) {
|
|
1187
|
+
function httpFailureToProviderError(provider, status, bodyText, retryAfterS) {
|
|
1332
1188
|
const detail = bodyText.slice(0, 512);
|
|
1333
1189
|
if (isCreditsFailure(status, bodyText)) {
|
|
1334
1190
|
return new ProviderError(
|
|
@@ -1348,14 +1204,14 @@ function httpFailureToProviderError(provider, status, bodyText) {
|
|
|
1348
1204
|
return new ProviderError(
|
|
1349
1205
|
"rate_limit",
|
|
1350
1206
|
`${provider}: rate limited (HTTP 429). Detail: ${detail}`,
|
|
1351
|
-
{ status }
|
|
1207
|
+
{ status, ...retryAfterS !== void 0 ? { retryAfterS } : {} }
|
|
1352
1208
|
);
|
|
1353
1209
|
}
|
|
1354
1210
|
if (status >= 500 && status <= 599) {
|
|
1355
1211
|
return new ProviderError(
|
|
1356
1212
|
"server",
|
|
1357
1213
|
`${provider}: upstream server error (HTTP ${status}). Detail: ${detail}`,
|
|
1358
|
-
{ status }
|
|
1214
|
+
{ status, ...retryAfterS !== void 0 ? { retryAfterS } : {} }
|
|
1359
1215
|
);
|
|
1360
1216
|
}
|
|
1361
1217
|
return new ProviderError(
|
|
@@ -1365,6 +1221,156 @@ function httpFailureToProviderError(provider, status, bodyText) {
|
|
|
1365
1221
|
);
|
|
1366
1222
|
}
|
|
1367
1223
|
|
|
1224
|
+
// src/providers/retry.ts
|
|
1225
|
+
init_cjs_shims();
|
|
1226
|
+
var override = null;
|
|
1227
|
+
function setRetryConfig(cfg) {
|
|
1228
|
+
override = cfg;
|
|
1229
|
+
}
|
|
1230
|
+
function resolveRetryConfig() {
|
|
1231
|
+
const envInt = (k, d) => {
|
|
1232
|
+
const n = Number(process.env[k]);
|
|
1233
|
+
return Number.isFinite(n) && n >= 0 ? Math.trunc(n) : d;
|
|
1234
|
+
};
|
|
1235
|
+
const envSecMs = (k, dMs) => {
|
|
1236
|
+
const n = Number(process.env[k]);
|
|
1237
|
+
return Number.isFinite(n) && n >= 0 ? Math.round(n * 1e3) : dMs;
|
|
1238
|
+
};
|
|
1239
|
+
return {
|
|
1240
|
+
maxAttempts: Math.max(1, override?.maxAttempts ?? envInt("CROSSCHECK_RETRY_MAX_ATTEMPTS", 3)),
|
|
1241
|
+
backoffBaseMs: override?.backoffBaseMs ?? envSecMs("CROSSCHECK_RETRY_BACKOFF_BASE_S", 750),
|
|
1242
|
+
maxBackoffMs: override?.maxBackoffMs ?? 2e4
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
function parseRetryAfter(respLike) {
|
|
1246
|
+
try {
|
|
1247
|
+
const h = respLike?.headers;
|
|
1248
|
+
if (!h) return void 0;
|
|
1249
|
+
let raw = null;
|
|
1250
|
+
const getter = h.get;
|
|
1251
|
+
if (typeof getter === "function") {
|
|
1252
|
+
raw = getter.call(h, "retry-after");
|
|
1253
|
+
} else if (typeof h === "object") {
|
|
1254
|
+
const rec = h;
|
|
1255
|
+
raw = rec["retry-after"] ?? rec["Retry-After"] ?? null;
|
|
1256
|
+
}
|
|
1257
|
+
if (!raw) return void 0;
|
|
1258
|
+
const secs = Number(raw);
|
|
1259
|
+
if (Number.isFinite(secs) && secs >= 0) return secs;
|
|
1260
|
+
const dateMs = Date.parse(raw);
|
|
1261
|
+
if (Number.isFinite(dateMs)) {
|
|
1262
|
+
const delta = (dateMs - Date.now()) / 1e3;
|
|
1263
|
+
return delta > 0 ? delta : 0;
|
|
1264
|
+
}
|
|
1265
|
+
} catch {
|
|
1266
|
+
}
|
|
1267
|
+
return void 0;
|
|
1268
|
+
}
|
|
1269
|
+
function defaultSleep(ms, signal) {
|
|
1270
|
+
return new Promise((resolve2, reject) => {
|
|
1271
|
+
if (signal?.aborted) {
|
|
1272
|
+
reject(new ProviderError("network", "request aborted", { transient: false }));
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
const cleanup = () => {
|
|
1276
|
+
clearTimeout(t);
|
|
1277
|
+
signal?.removeEventListener?.("abort", onAbort);
|
|
1278
|
+
};
|
|
1279
|
+
const onAbort = () => {
|
|
1280
|
+
cleanup();
|
|
1281
|
+
reject(new ProviderError("network", "request aborted", { transient: false }));
|
|
1282
|
+
};
|
|
1283
|
+
const t = setTimeout(() => {
|
|
1284
|
+
cleanup();
|
|
1285
|
+
resolve2();
|
|
1286
|
+
}, ms);
|
|
1287
|
+
signal?.addEventListener?.("abort", onAbort);
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
async function sendWithRetry(attemptOnce, cfg, signal, deps) {
|
|
1291
|
+
const sleep = deps?.sleepImpl ?? ((ms) => defaultSleep(ms, signal));
|
|
1292
|
+
const rand = deps?.random ?? Math.random;
|
|
1293
|
+
let lastErr;
|
|
1294
|
+
for (let attempt = 1; attempt <= cfg.maxAttempts; attempt += 1) {
|
|
1295
|
+
try {
|
|
1296
|
+
const result = await attemptOnce();
|
|
1297
|
+
return { ...result, attempts: attempt };
|
|
1298
|
+
} catch (e) {
|
|
1299
|
+
lastErr = e;
|
|
1300
|
+
const pe = e instanceof ProviderError ? e : null;
|
|
1301
|
+
if (pe?.transient !== true || attempt >= cfg.maxAttempts) throw e;
|
|
1302
|
+
let backoffMs = pe.retryAfterS !== void 0 ? pe.retryAfterS * 1e3 : cfg.backoffBaseMs * 2 ** (attempt - 1);
|
|
1303
|
+
backoffMs += rand() * cfg.backoffBaseMs;
|
|
1304
|
+
backoffMs = Math.min(backoffMs, cfg.maxBackoffMs);
|
|
1305
|
+
await sleep(backoffMs);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
throw lastErr;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// src/providers/rate-limit.ts
|
|
1312
|
+
init_cjs_shims();
|
|
1313
|
+
var DEFAULT_SPEC = { capacity: 5, refillPerSec: 5 };
|
|
1314
|
+
var MAX_WAIT_SLICE_MS = 250;
|
|
1315
|
+
var override2 = null;
|
|
1316
|
+
function setRateLimitConfig(cfg) {
|
|
1317
|
+
override2 = cfg;
|
|
1318
|
+
buckets.clear();
|
|
1319
|
+
}
|
|
1320
|
+
function disabled() {
|
|
1321
|
+
return process.env["CROSSCHECK_RATE_LIMIT"] === "off";
|
|
1322
|
+
}
|
|
1323
|
+
function specFor(provider) {
|
|
1324
|
+
const fromCfg = override2?.[provider] ?? override2?.["default"];
|
|
1325
|
+
const spec = fromCfg ?? DEFAULT_SPEC;
|
|
1326
|
+
const capacity = Number.isFinite(spec.capacity) && spec.capacity > 0 ? spec.capacity : DEFAULT_SPEC.capacity;
|
|
1327
|
+
const refillPerSec = Number.isFinite(spec.refillPerSec) && spec.refillPerSec > 0 ? spec.refillPerSec : capacity;
|
|
1328
|
+
return { capacity, refillPerSec };
|
|
1329
|
+
}
|
|
1330
|
+
var Bucket = class {
|
|
1331
|
+
constructor(spec, now) {
|
|
1332
|
+
this.spec = spec;
|
|
1333
|
+
this.tokens = spec.capacity;
|
|
1334
|
+
this.last = now();
|
|
1335
|
+
}
|
|
1336
|
+
spec;
|
|
1337
|
+
tokens;
|
|
1338
|
+
last;
|
|
1339
|
+
refill(now) {
|
|
1340
|
+
const elapsed = (now - this.last) / 1e3;
|
|
1341
|
+
this.tokens = Math.min(this.spec.capacity, this.tokens + elapsed * this.spec.refillPerSec);
|
|
1342
|
+
this.last = now;
|
|
1343
|
+
}
|
|
1344
|
+
async acquire(deps) {
|
|
1345
|
+
for (; ; ) {
|
|
1346
|
+
this.refill(deps.now());
|
|
1347
|
+
if (this.tokens >= 1) {
|
|
1348
|
+
this.tokens -= 1;
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
if (deps.signal?.aborted) {
|
|
1352
|
+
throw new ProviderError("network", "request aborted while rate-limited", { transient: false });
|
|
1353
|
+
}
|
|
1354
|
+
const needed = 1 - this.tokens;
|
|
1355
|
+
const waitMs = needed / Math.max(this.spec.refillPerSec, 1e-6) * 1e3;
|
|
1356
|
+
await deps.sleep(Math.min(waitMs, MAX_WAIT_SLICE_MS));
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
};
|
|
1360
|
+
var buckets = /* @__PURE__ */ new Map();
|
|
1361
|
+
var realSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
1362
|
+
async function acquireRateLimit(provider, deps) {
|
|
1363
|
+
if (disabled()) return;
|
|
1364
|
+
const now = deps?.now ?? Date.now;
|
|
1365
|
+
const sleep = deps?.sleep ?? realSleep;
|
|
1366
|
+
let bucket = buckets.get(provider);
|
|
1367
|
+
if (!bucket) {
|
|
1368
|
+
bucket = new Bucket(specFor(provider), now);
|
|
1369
|
+
buckets.set(provider, bucket);
|
|
1370
|
+
}
|
|
1371
|
+
await bucket.acquire({ now, sleep, ...deps?.signal ? { signal: deps.signal } : {} });
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1368
1374
|
// src/providers/anthropic.ts
|
|
1369
1375
|
var ANTHROPIC_API_URL = "https://api.anthropic.com/v1/messages";
|
|
1370
1376
|
var ANTHROPIC_VERSION_HEADER = "2023-06-01";
|
|
@@ -1491,29 +1497,33 @@ async function sendAnthropic(args) {
|
|
|
1491
1497
|
body: JSON.stringify(body)
|
|
1492
1498
|
};
|
|
1493
1499
|
if (args.signal) init.signal = args.signal;
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
respLike = await doFetch(url, init);
|
|
1497
|
-
} catch (e) {
|
|
1498
|
-
throw new ProviderError("network", `anthropic: fetch failed: ${e.message}`);
|
|
1499
|
-
}
|
|
1500
|
-
const status = respLike.status;
|
|
1501
|
-
if (status >= 200 && status < 300) {
|
|
1502
|
-
let parsed;
|
|
1500
|
+
const attemptOnce = async () => {
|
|
1501
|
+
let respLike;
|
|
1503
1502
|
try {
|
|
1504
|
-
|
|
1503
|
+
respLike = await doFetch(url, init);
|
|
1505
1504
|
} catch (e) {
|
|
1506
|
-
throw new ProviderError("
|
|
1505
|
+
throw new ProviderError("network", `anthropic: fetch failed: ${e.message}`);
|
|
1507
1506
|
}
|
|
1508
|
-
const
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1507
|
+
const status = respLike.status;
|
|
1508
|
+
if (status >= 200 && status < 300) {
|
|
1509
|
+
let parsed;
|
|
1510
|
+
try {
|
|
1511
|
+
parsed = await respLike.json();
|
|
1512
|
+
} catch (e) {
|
|
1513
|
+
throw new ProviderError("parse", `anthropic: response body not JSON: ${e.message}`);
|
|
1514
|
+
}
|
|
1515
|
+
const { text, usage } = parseAnthropicResponse({
|
|
1516
|
+
resp: parsed,
|
|
1517
|
+
model: args.model,
|
|
1518
|
+
purpose: args.purpose ?? "worker"
|
|
1519
|
+
});
|
|
1520
|
+
return { text, attempts: 1, usage: applyPricing(usage, args.pricing) };
|
|
1521
|
+
}
|
|
1522
|
+
const bodyText = await respLike.text().catch(() => "");
|
|
1523
|
+
throw httpFailureToProviderError("anthropic", status, bodyText, parseRetryAfter(respLike));
|
|
1524
|
+
};
|
|
1525
|
+
await acquireRateLimit("anthropic", args.signal ? { signal: args.signal } : void 0);
|
|
1526
|
+
return sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
1517
1527
|
}
|
|
1518
1528
|
|
|
1519
1529
|
// src/providers/gemini.ts
|
|
@@ -1665,29 +1675,33 @@ async function sendGemini(args) {
|
|
|
1665
1675
|
body: JSON.stringify(body)
|
|
1666
1676
|
};
|
|
1667
1677
|
if (args.signal) init.signal = args.signal;
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
respLike = await doFetch(url, init);
|
|
1671
|
-
} catch (e) {
|
|
1672
|
-
throw new ProviderError("network", `gemini: fetch failed: ${e.message}`);
|
|
1673
|
-
}
|
|
1674
|
-
const status = respLike.status;
|
|
1675
|
-
if (status >= 200 && status < 300) {
|
|
1676
|
-
let parsed;
|
|
1678
|
+
const attemptOnce = async () => {
|
|
1679
|
+
let respLike;
|
|
1677
1680
|
try {
|
|
1678
|
-
|
|
1681
|
+
respLike = await doFetch(url, init);
|
|
1679
1682
|
} catch (e) {
|
|
1680
|
-
throw new ProviderError("
|
|
1683
|
+
throw new ProviderError("network", `gemini: fetch failed: ${e.message}`);
|
|
1681
1684
|
}
|
|
1682
|
-
const
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1685
|
+
const status = respLike.status;
|
|
1686
|
+
if (status >= 200 && status < 300) {
|
|
1687
|
+
let parsed;
|
|
1688
|
+
try {
|
|
1689
|
+
parsed = await respLike.json();
|
|
1690
|
+
} catch (e) {
|
|
1691
|
+
throw new ProviderError("parse", `gemini: response body not JSON: ${e.message}`);
|
|
1692
|
+
}
|
|
1693
|
+
const { text, usage } = parseGeminiResponse({
|
|
1694
|
+
resp: parsed,
|
|
1695
|
+
model: args.model,
|
|
1696
|
+
purpose: args.purpose ?? "worker"
|
|
1697
|
+
});
|
|
1698
|
+
return { text, attempts: 1, usage: applyPricing2(usage, args.pricing) };
|
|
1699
|
+
}
|
|
1700
|
+
const bodyText = await respLike.text().catch(() => "");
|
|
1701
|
+
throw httpFailureToProviderError("gemini", status, bodyText, parseRetryAfter(respLike));
|
|
1702
|
+
};
|
|
1703
|
+
await acquireRateLimit("gemini", args.signal ? { signal: args.signal } : void 0);
|
|
1704
|
+
return sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
1691
1705
|
}
|
|
1692
1706
|
|
|
1693
1707
|
// src/providers/openai-compatible.ts
|
|
@@ -1827,30 +1841,34 @@ async function sendOpenAICompatible(args) {
|
|
|
1827
1841
|
body: JSON.stringify(body)
|
|
1828
1842
|
};
|
|
1829
1843
|
if (args.signal) init.signal = args.signal;
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
respLike = await doFetch(url, init);
|
|
1833
|
-
} catch (e) {
|
|
1834
|
-
throw new ProviderError("network", `${args.provider}: fetch failed: ${e.message}`);
|
|
1835
|
-
}
|
|
1836
|
-
const status = respLike.status;
|
|
1837
|
-
if (status >= 200 && status < 300) {
|
|
1838
|
-
let parsed;
|
|
1844
|
+
const attemptOnce = async () => {
|
|
1845
|
+
let respLike;
|
|
1839
1846
|
try {
|
|
1840
|
-
|
|
1847
|
+
respLike = await doFetch(url, init);
|
|
1841
1848
|
} catch (e) {
|
|
1842
|
-
throw new ProviderError("
|
|
1849
|
+
throw new ProviderError("network", `${args.provider}: fetch failed: ${e.message}`);
|
|
1843
1850
|
}
|
|
1844
|
-
const
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1851
|
+
const status = respLike.status;
|
|
1852
|
+
if (status >= 200 && status < 300) {
|
|
1853
|
+
let parsed;
|
|
1854
|
+
try {
|
|
1855
|
+
parsed = await respLike.json();
|
|
1856
|
+
} catch (e) {
|
|
1857
|
+
throw new ProviderError("parse", `${args.provider}: response body not JSON: ${e.message}`);
|
|
1858
|
+
}
|
|
1859
|
+
const { text, usage } = parseOpenAICompatibleResponse({
|
|
1860
|
+
resp: parsed,
|
|
1861
|
+
provider: args.provider,
|
|
1862
|
+
model: args.model,
|
|
1863
|
+
purpose: args.purpose ?? "worker"
|
|
1864
|
+
});
|
|
1865
|
+
return { text, attempts: 1, usage: applyPricing3(usage, args.pricing) };
|
|
1866
|
+
}
|
|
1867
|
+
const bodyText = await respLike.text().catch(() => "");
|
|
1868
|
+
throw httpFailureToProviderError(args.provider, status, bodyText, parseRetryAfter(respLike));
|
|
1869
|
+
};
|
|
1870
|
+
await acquireRateLimit(args.provider, args.signal ? { signal: args.signal } : void 0);
|
|
1871
|
+
return sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
1854
1872
|
}
|
|
1855
1873
|
|
|
1856
1874
|
// src/providers/registry.ts
|
|
@@ -1945,7 +1963,51 @@ function makeGeminiProvider(model, apiKey, opts) {
|
|
|
1945
1963
|
// src/server.ts
|
|
1946
1964
|
init_cjs_shims();
|
|
1947
1965
|
var import_server2 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
1948
|
-
var
|
|
1966
|
+
var import_types8 = require("@modelcontextprotocol/sdk/types.js");
|
|
1967
|
+
|
|
1968
|
+
// src/bridge/index.ts
|
|
1969
|
+
init_cjs_shims();
|
|
1970
|
+
|
|
1971
|
+
// src/bridge/proxy-tools.ts
|
|
1972
|
+
init_cjs_shims();
|
|
1973
|
+
function buildPythonProxies(bridge) {
|
|
1974
|
+
const proxies = /* @__PURE__ */ new Map();
|
|
1975
|
+
for (const name of bridge.toolNames) {
|
|
1976
|
+
proxies.set(name, makeProxy(bridge, name));
|
|
1977
|
+
}
|
|
1978
|
+
return proxies;
|
|
1979
|
+
}
|
|
1980
|
+
function makeProxy(bridge, name) {
|
|
1981
|
+
return {
|
|
1982
|
+
name,
|
|
1983
|
+
description: `(forwarded to Python crosscheck-agent) ${name}`,
|
|
1984
|
+
// We don't carry the per-tool input schema across the bridge in
|
|
1985
|
+
// route-all mode — the TS server is a transparent forwarder, so
|
|
1986
|
+
// arg validation happens on the Python side. inputSchema is left
|
|
1987
|
+
// permissive; the Python validator throws structured errors that
|
|
1988
|
+
// tunnel back through `callTool()`.
|
|
1989
|
+
inputSchema: {
|
|
1990
|
+
type: "object",
|
|
1991
|
+
additionalProperties: true,
|
|
1992
|
+
description: `Forwarded to Python \u2014 see Python tool '${name}' for full schema.`
|
|
1993
|
+
},
|
|
1994
|
+
handler: async (args) => {
|
|
1995
|
+
const r = await bridge.callTool(name, args);
|
|
1996
|
+
if (!r.content.length) {
|
|
1997
|
+
return {};
|
|
1998
|
+
}
|
|
1999
|
+
const first = r.content[0];
|
|
2000
|
+
if (first.type === "text" && first.text) {
|
|
2001
|
+
try {
|
|
2002
|
+
return JSON.parse(first.text);
|
|
2003
|
+
} catch {
|
|
2004
|
+
return { text: first.text };
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
return { content: r.content };
|
|
2008
|
+
}
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
1949
2011
|
|
|
1950
2012
|
// src/instructions.ts
|
|
1951
2013
|
init_cjs_shims();
|
|
@@ -2012,7 +2074,7 @@ var import_zod = require("zod");
|
|
|
2012
2074
|
|
|
2013
2075
|
// src/tools/audit.ts
|
|
2014
2076
|
init_cjs_shims();
|
|
2015
|
-
var
|
|
2077
|
+
var import_node_fs5 = require("fs");
|
|
2016
2078
|
var import_node_path4 = require("path");
|
|
2017
2079
|
|
|
2018
2080
|
// src/core/structured.ts
|
|
@@ -2075,27 +2137,27 @@ function extractJson(text) {
|
|
|
2075
2137
|
|
|
2076
2138
|
// src/core/json-schema.ts
|
|
2077
2139
|
init_cjs_shims();
|
|
2078
|
-
function validateSchema(value, schema,
|
|
2140
|
+
function validateSchema(value, schema, path14 = "") {
|
|
2079
2141
|
const errs = [];
|
|
2080
2142
|
if ("anyOf" in schema) {
|
|
2081
2143
|
const subs = schema["anyOf"].map(
|
|
2082
|
-
(s) => validateSchema(value, s,
|
|
2144
|
+
(s) => validateSchema(value, s, path14)
|
|
2083
2145
|
);
|
|
2084
2146
|
if (!subs.some((e) => e.length === 0)) {
|
|
2085
|
-
errs.push(`${
|
|
2147
|
+
errs.push(`${path14 || "<root>"}: did not match anyOf`);
|
|
2086
2148
|
}
|
|
2087
2149
|
return errs;
|
|
2088
2150
|
}
|
|
2089
2151
|
if ("oneOf" in schema) {
|
|
2090
|
-
const passed = schema["oneOf"].filter((s) => validateSchema(value, s,
|
|
2152
|
+
const passed = schema["oneOf"].filter((s) => validateSchema(value, s, path14).length === 0).length;
|
|
2091
2153
|
if (passed !== 1) {
|
|
2092
|
-
errs.push(`${
|
|
2154
|
+
errs.push(`${path14 || "<root>"}: matched ${passed} of oneOf, expected 1`);
|
|
2093
2155
|
}
|
|
2094
2156
|
return errs;
|
|
2095
2157
|
}
|
|
2096
2158
|
if ("const" in schema && !deepEqual(value, schema["const"])) {
|
|
2097
2159
|
errs.push(
|
|
2098
|
-
`${
|
|
2160
|
+
`${path14 || "<root>"}: expected const ${pyRepr(schema["const"])}, got ${pyRepr(value)}`
|
|
2099
2161
|
);
|
|
2100
2162
|
return errs;
|
|
2101
2163
|
}
|
|
@@ -2105,7 +2167,7 @@ function validateSchema(value, schema, path12 = "") {
|
|
|
2105
2167
|
const ok = types.some((tt) => matchesType(value, String(tt)));
|
|
2106
2168
|
if (!ok) {
|
|
2107
2169
|
errs.push(
|
|
2108
|
-
`${
|
|
2170
|
+
`${path14 || "<root>"}: expected type ${pyReprType(t)}, got ${pyTypeName(value)}`
|
|
2109
2171
|
);
|
|
2110
2172
|
return errs;
|
|
2111
2173
|
}
|
|
@@ -2114,29 +2176,29 @@ function validateSchema(value, schema, path12 = "") {
|
|
|
2114
2176
|
if ("enum" in schema) {
|
|
2115
2177
|
const en = schema["enum"];
|
|
2116
2178
|
if (!en.some((x) => deepEqual(x, value))) {
|
|
2117
|
-
errs.push(`${
|
|
2179
|
+
errs.push(`${path14 || "<root>"}: value ${pyRepr(value)} not in enum`);
|
|
2118
2180
|
}
|
|
2119
2181
|
}
|
|
2120
2182
|
if ("minLength" in schema && value.length < schema["minLength"]) {
|
|
2121
|
-
errs.push(`${
|
|
2183
|
+
errs.push(`${path14 || "<root>"}: shorter than minLength ${schema["minLength"]}`);
|
|
2122
2184
|
}
|
|
2123
2185
|
}
|
|
2124
2186
|
if (typeof value === "number" && !Number.isNaN(value)) {
|
|
2125
2187
|
if ("minimum" in schema && value < schema["minimum"]) {
|
|
2126
|
-
errs.push(`${
|
|
2188
|
+
errs.push(`${path14 || "<root>"}: ${value} < minimum ${schema["minimum"]}`);
|
|
2127
2189
|
}
|
|
2128
2190
|
if ("maximum" in schema && value > schema["maximum"]) {
|
|
2129
|
-
errs.push(`${
|
|
2191
|
+
errs.push(`${path14 || "<root>"}: ${value} > maximum ${schema["maximum"]}`);
|
|
2130
2192
|
}
|
|
2131
2193
|
}
|
|
2132
2194
|
if (Array.isArray(value)) {
|
|
2133
2195
|
if ("minItems" in schema && value.length < schema["minItems"]) {
|
|
2134
|
-
errs.push(`${
|
|
2196
|
+
errs.push(`${path14 || "<root>"}: fewer items than minItems ${schema["minItems"]}`);
|
|
2135
2197
|
}
|
|
2136
2198
|
const itemSchema = schema["items"];
|
|
2137
2199
|
if (isObj(itemSchema)) {
|
|
2138
2200
|
for (let i = 0; i < value.length; i++) {
|
|
2139
|
-
errs.push(...validateSchema(value[i], itemSchema, `${
|
|
2201
|
+
errs.push(...validateSchema(value[i], itemSchema, `${path14}[${i}]`));
|
|
2140
2202
|
}
|
|
2141
2203
|
}
|
|
2142
2204
|
}
|
|
@@ -2145,19 +2207,19 @@ function validateSchema(value, schema, path12 = "") {
|
|
|
2145
2207
|
const required = schema["required"] ?? [];
|
|
2146
2208
|
for (const r of required) {
|
|
2147
2209
|
if (!(r in value)) {
|
|
2148
|
-
errs.push(`${
|
|
2210
|
+
errs.push(`${path14 || "<root>"}: missing required key ${pyRepr(r)}`);
|
|
2149
2211
|
}
|
|
2150
2212
|
}
|
|
2151
2213
|
if (schema["additionalProperties"] === false) {
|
|
2152
2214
|
for (const k of Object.keys(value)) {
|
|
2153
2215
|
if (!(k in props)) {
|
|
2154
|
-
errs.push(`${
|
|
2216
|
+
errs.push(`${path14 || "<root>"}: unknown key ${pyRepr(k)}`);
|
|
2155
2217
|
}
|
|
2156
2218
|
}
|
|
2157
2219
|
}
|
|
2158
2220
|
for (const [k, v] of Object.entries(value)) {
|
|
2159
2221
|
const ps = props[k];
|
|
2160
|
-
if (ps) errs.push(...validateSchema(v, ps,
|
|
2222
|
+
if (ps) errs.push(...validateSchema(v, ps, path14 ? `${path14}.${k}` : k));
|
|
2161
2223
|
}
|
|
2162
2224
|
}
|
|
2163
2225
|
return errs;
|
|
@@ -2546,6 +2608,21 @@ async function recordSessionCall(storage, sessionId, answers, wallMs, cpuMs, now
|
|
|
2546
2608
|
}
|
|
2547
2609
|
}
|
|
2548
2610
|
|
|
2611
|
+
// src/core/budgets.ts
|
|
2612
|
+
init_cjs_shims();
|
|
2613
|
+
var operatorBudgets = null;
|
|
2614
|
+
function setOperatorBudgets(cfg) {
|
|
2615
|
+
operatorBudgets = cfg;
|
|
2616
|
+
}
|
|
2617
|
+
function operatorCeiling(purpose, provider) {
|
|
2618
|
+
const direct = operatorBudgets?.token_budgets?.[purpose];
|
|
2619
|
+
if (typeof direct === "number" && direct > 0) return Math.trunc(direct);
|
|
2620
|
+
const byProv = operatorBudgets?.token_budgets_by_provider?.[provider.toLowerCase()];
|
|
2621
|
+
const pv = byProv?.[purpose];
|
|
2622
|
+
if (typeof pv === "number" && pv > 0) return Math.trunc(pv);
|
|
2623
|
+
return null;
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2549
2626
|
// src/core/structured.ts
|
|
2550
2627
|
async function requestStructured(provider, baseMessages, schema, opts) {
|
|
2551
2628
|
const maxRetries = opts.maxRetries ?? 1;
|
|
@@ -2620,10 +2697,13 @@ ${schemaText}`;
|
|
|
2620
2697
|
async function askOne(provider, messages, opts) {
|
|
2621
2698
|
const startedWall = performance.now();
|
|
2622
2699
|
const startedCpu = process.cpuUsage();
|
|
2700
|
+
let maxTokens = opts.maxTokens;
|
|
2701
|
+
const ceiling = operatorCeiling(opts.purpose, provider.name);
|
|
2702
|
+
if (ceiling !== null && ceiling < maxTokens) maxTokens = ceiling;
|
|
2623
2703
|
try {
|
|
2624
2704
|
const r = await provider.send({
|
|
2625
2705
|
messages,
|
|
2626
|
-
maxTokens
|
|
2706
|
+
maxTokens,
|
|
2627
2707
|
temperature: opts.temperature,
|
|
2628
2708
|
purpose: opts.purpose,
|
|
2629
2709
|
...opts.signal ? { signal: opts.signal } : {},
|
|
@@ -2664,6 +2744,191 @@ async function askOne(provider, messages, opts) {
|
|
|
2664
2744
|
}
|
|
2665
2745
|
}
|
|
2666
2746
|
|
|
2747
|
+
// src/core/personas.ts
|
|
2748
|
+
init_cjs_shims();
|
|
2749
|
+
var import_node_fs3 = require("fs");
|
|
2750
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
2751
|
+
var import_node_url = require("url");
|
|
2752
|
+
var PERSONA_IDS = [
|
|
2753
|
+
"engineering",
|
|
2754
|
+
"research",
|
|
2755
|
+
"auditor-security",
|
|
2756
|
+
"auditor-finance",
|
|
2757
|
+
"finance",
|
|
2758
|
+
"design"
|
|
2759
|
+
];
|
|
2760
|
+
function isPersonaId(s) {
|
|
2761
|
+
return PERSONA_IDS.includes(s);
|
|
2762
|
+
}
|
|
2763
|
+
var personaConfig = null;
|
|
2764
|
+
function setPersonaConfig(cfg) {
|
|
2765
|
+
personaConfig = cfg;
|
|
2766
|
+
}
|
|
2767
|
+
var processFirstCallConsumed = false;
|
|
2768
|
+
var sessionsSeen = /* @__PURE__ */ new Set();
|
|
2769
|
+
function consumeFirstCall(sessionId) {
|
|
2770
|
+
if (sessionId) {
|
|
2771
|
+
if (sessionsSeen.has(sessionId)) return false;
|
|
2772
|
+
sessionsSeen.add(sessionId);
|
|
2773
|
+
return true;
|
|
2774
|
+
}
|
|
2775
|
+
if (processFirstCallConsumed) return false;
|
|
2776
|
+
processFirstCallConsumed = true;
|
|
2777
|
+
return true;
|
|
2778
|
+
}
|
|
2779
|
+
var TOOL_PRIORS = {
|
|
2780
|
+
// The audit tool is for auditing — lean to security audit by default (prior
|
|
2781
|
+
// clears the >=2 margin on its own), while finance keywords still flip it to
|
|
2782
|
+
// auditor-finance.
|
|
2783
|
+
audit: { "auditor-security": 3, "auditor-finance": 1 }
|
|
2784
|
+
};
|
|
2785
|
+
var TERMS = {
|
|
2786
|
+
"auditor-security": [
|
|
2787
|
+
[/\b(vuln(erabilit|)|cve|cwe|exploit|owasp|injection|sql\s*i|xss|csrf|ssrf|rce|deserializ|auth[zn]|secret|threat\s*model|attack\s*surface|privilege)\b/i, 3],
|
|
2788
|
+
[/\b(security|hardening|pentest|malicious|sandbox\s*escape)\b/i, 1]
|
|
2789
|
+
],
|
|
2790
|
+
"auditor-finance": [
|
|
2791
|
+
[/\b(gaap|ifrs|sox|materiality|reconcil|ledger|accrual|audit\s*trail|segregation\s*of\s*duties|internal\s*control|journal\s*entry)\b/i, 3],
|
|
2792
|
+
[/\b(audit|invoice|p&l|balance\s*sheet)\b/i, 1]
|
|
2793
|
+
],
|
|
2794
|
+
finance: [
|
|
2795
|
+
[/\b(valuation|dcf|npv|irr|cash\s*flow|unit\s*economics|burn\s*rate|runway|cap\s*table|forecast|p&l|ebitda|cohort|ltv|cac)\b/i, 3],
|
|
2796
|
+
[/\b(budget|revenue|margin|pricing|financial\s*model)\b/i, 1]
|
|
2797
|
+
],
|
|
2798
|
+
research: [
|
|
2799
|
+
[/\b(literature|hypothes(is|es)|methodology|empirical|citation|peer[-\s]?review|ablation|systematic\s*review|meta[-\s]?analysis)\b/i, 3],
|
|
2800
|
+
[/\b(research|study|survey|evidence|paper|findings)\b/i, 1]
|
|
2801
|
+
],
|
|
2802
|
+
design: [
|
|
2803
|
+
[/\b(ux|usability|wireframe|figma|interaction\s*design|user\s*journey|design\s*system|a11y|accessibilit|heuristic\s*evaluation|information\s*architecture)\b/i, 3],
|
|
2804
|
+
[/\b(design|layout|visual|prototype|onboarding\s*flow)\b/i, 1]
|
|
2805
|
+
]
|
|
2806
|
+
};
|
|
2807
|
+
function routePersona(prompt, toolName) {
|
|
2808
|
+
const text = String(prompt ?? "");
|
|
2809
|
+
const scores = { engineering: 1 };
|
|
2810
|
+
const priors = TOOL_PRIORS[toolName] ?? {};
|
|
2811
|
+
for (const id of PERSONA_IDS) {
|
|
2812
|
+
if (id === "engineering") continue;
|
|
2813
|
+
let s = priors[id] ?? 0;
|
|
2814
|
+
for (const [re, w] of TERMS[id]) {
|
|
2815
|
+
if (re.test(text)) s += w;
|
|
2816
|
+
}
|
|
2817
|
+
scores[id] = s;
|
|
2818
|
+
}
|
|
2819
|
+
let bestId = "engineering";
|
|
2820
|
+
let bestScore = -Infinity;
|
|
2821
|
+
for (const id of PERSONA_IDS) {
|
|
2822
|
+
if (id === "engineering") continue;
|
|
2823
|
+
if (scores[id] > bestScore) {
|
|
2824
|
+
bestScore = scores[id];
|
|
2825
|
+
bestId = id;
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
if (bestScore - (scores["engineering"] ?? 0) >= 2) return { id: bestId, scores };
|
|
2829
|
+
return { id: "engineering", scores };
|
|
2830
|
+
}
|
|
2831
|
+
function resolvePersona(opts) {
|
|
2832
|
+
const cfg = opts.config ?? personaConfig;
|
|
2833
|
+
if (cfg?.enabled === false) return { id: null, source: "disabled", bypassed: true };
|
|
2834
|
+
const explicit = opts.explicit?.trim().toLowerCase();
|
|
2835
|
+
if (explicit === "none" || explicit === "off") {
|
|
2836
|
+
return { id: null, source: "none", bypassed: true };
|
|
2837
|
+
}
|
|
2838
|
+
if (explicit && explicit !== "auto" && isPersonaId(explicit)) {
|
|
2839
|
+
return { id: explicit, source: "explicit", bypassed: false };
|
|
2840
|
+
}
|
|
2841
|
+
const envPin = (opts.envPin ?? process.env["CROSSCHECK_PERSONA"])?.trim().toLowerCase();
|
|
2842
|
+
if (envPin && isPersonaId(envPin)) {
|
|
2843
|
+
return { id: envPin, source: "env", bypassed: false };
|
|
2844
|
+
}
|
|
2845
|
+
const routed = routePersona(opts.prompt, opts.toolName);
|
|
2846
|
+
if (routed.id !== "engineering") {
|
|
2847
|
+
return { id: routed.id, source: "heuristic", scores: routed.scores, bypassed: false };
|
|
2848
|
+
}
|
|
2849
|
+
if (opts.firstCall) {
|
|
2850
|
+
return { id: "engineering", source: "default-first-call", scores: routed.scores, bypassed: false };
|
|
2851
|
+
}
|
|
2852
|
+
return { id: null, source: "heuristic", scores: routed.scores, bypassed: true };
|
|
2853
|
+
}
|
|
2854
|
+
var bodyCache = /* @__PURE__ */ new Map();
|
|
2855
|
+
function bundledPersonasDir() {
|
|
2856
|
+
let dir;
|
|
2857
|
+
try {
|
|
2858
|
+
dir = import_node_path2.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
2859
|
+
} catch {
|
|
2860
|
+
dir = process.cwd();
|
|
2861
|
+
}
|
|
2862
|
+
for (let i = 0; i < 8; i += 1) {
|
|
2863
|
+
const cand = import_node_path2.default.join(dir, "personas");
|
|
2864
|
+
if ((0, import_node_fs3.existsSync)(import_node_path2.default.join(cand, "engineering.md"))) return cand;
|
|
2865
|
+
const parent = import_node_path2.default.dirname(dir);
|
|
2866
|
+
if (parent === dir) break;
|
|
2867
|
+
dir = parent;
|
|
2868
|
+
}
|
|
2869
|
+
return void 0;
|
|
2870
|
+
}
|
|
2871
|
+
function readIfExists(p) {
|
|
2872
|
+
try {
|
|
2873
|
+
return (0, import_node_fs3.existsSync)(p) ? (0, import_node_fs3.readFileSync)(p, "utf8") : null;
|
|
2874
|
+
} catch {
|
|
2875
|
+
return null;
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
function loadPersonaBody(id, opts) {
|
|
2879
|
+
const cfg = opts?.config ?? personaConfig;
|
|
2880
|
+
const cacheKey = `${id}|${opts?.repoRoot ?? ""}|${cfg?.dir ?? ""}`;
|
|
2881
|
+
const cached = bodyCache.get(cacheKey);
|
|
2882
|
+
if (cached !== void 0) return cached;
|
|
2883
|
+
const candidates = [];
|
|
2884
|
+
if (cfg?.dir) candidates.push(import_node_path2.default.join(cfg.dir, `${id}.md`));
|
|
2885
|
+
if (opts?.repoRoot) candidates.push(import_node_path2.default.join(opts.repoRoot, ".crosscheck", "personas", `${id}.md`));
|
|
2886
|
+
if (id === "engineering" && opts?.repoRoot) candidates.push(import_node_path2.default.join(opts.repoRoot, "CLAUDE.md"));
|
|
2887
|
+
const bundled = bundledPersonasDir();
|
|
2888
|
+
if (bundled) candidates.push(import_node_path2.default.join(bundled, `${id}.md`));
|
|
2889
|
+
for (const c of candidates) {
|
|
2890
|
+
const body = readIfExists(c);
|
|
2891
|
+
if (body && body.trim()) {
|
|
2892
|
+
const trimmed = body.trim();
|
|
2893
|
+
bodyCache.set(cacheKey, trimmed);
|
|
2894
|
+
return trimmed;
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
return null;
|
|
2898
|
+
}
|
|
2899
|
+
function renderPersonaBlock(id, body) {
|
|
2900
|
+
return `<operating-context source="${id}">
|
|
2901
|
+
${body}
|
|
2902
|
+
</operating-context>`;
|
|
2903
|
+
}
|
|
2904
|
+
function buildPersonaInjection(opts) {
|
|
2905
|
+
try {
|
|
2906
|
+
const firstCall = consumeFirstCall(opts.sessionId);
|
|
2907
|
+
const res = resolvePersona({
|
|
2908
|
+
toolName: opts.toolName,
|
|
2909
|
+
prompt: opts.prompt,
|
|
2910
|
+
explicit: opts.explicit,
|
|
2911
|
+
firstCall,
|
|
2912
|
+
config: personaConfig
|
|
2913
|
+
});
|
|
2914
|
+
const meta = {
|
|
2915
|
+
used: res.id,
|
|
2916
|
+
source: res.source,
|
|
2917
|
+
injected_into: res.id ? ["moderator"] : [],
|
|
2918
|
+
bypassed: res.bypassed,
|
|
2919
|
+
...res.scores ? { scores: res.scores } : {}
|
|
2920
|
+
};
|
|
2921
|
+
if (!res.id) return { block: null, meta };
|
|
2922
|
+
const body = loadPersonaBody(res.id, { repoRoot: opts.repoRoot });
|
|
2923
|
+
if (!body) {
|
|
2924
|
+
return { block: null, meta: { ...meta, injected_into: [], bypassed: true } };
|
|
2925
|
+
}
|
|
2926
|
+
return { block: renderPersonaBlock(res.id, body), meta };
|
|
2927
|
+
} catch {
|
|
2928
|
+
return { block: null, meta: { used: null, source: "none", injected_into: [], bypassed: true } };
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2667
2932
|
// src/core/utils.ts
|
|
2668
2933
|
init_cjs_shims();
|
|
2669
2934
|
function checkSessionBreakers(session, cfg) {
|
|
@@ -2802,7 +3067,7 @@ function maybeDagBreakerEnvelope(dag, toolName, cfg, sessionId) {
|
|
|
2802
3067
|
|
|
2803
3068
|
// src/core/transcripts.ts
|
|
2804
3069
|
init_cjs_shims();
|
|
2805
|
-
var
|
|
3070
|
+
var import_node_fs4 = require("fs");
|
|
2806
3071
|
var import_node_path3 = __toESM(require("path"), 1);
|
|
2807
3072
|
|
|
2808
3073
|
// src/core/redact.ts
|
|
@@ -2881,6 +3146,13 @@ function redactObj(obj, cfg) {
|
|
|
2881
3146
|
}
|
|
2882
3147
|
return obj;
|
|
2883
3148
|
}
|
|
3149
|
+
var globalRedaction = null;
|
|
3150
|
+
function setRedactionConfig(cfg) {
|
|
3151
|
+
globalRedaction = cfg;
|
|
3152
|
+
}
|
|
3153
|
+
function getRedactionConfig() {
|
|
3154
|
+
return globalRedaction ?? void 0;
|
|
3155
|
+
}
|
|
2884
3156
|
|
|
2885
3157
|
// src/core/transcripts.ts
|
|
2886
3158
|
var FTS_INDEX_MAX_CHARS = 64 * 1024;
|
|
@@ -2938,12 +3210,12 @@ async function writeTranscript(opts) {
|
|
|
2938
3210
|
const now = opts.nowMs ? opts.nowMs() : Date.now();
|
|
2939
3211
|
const stampMs = Math.trunc(now);
|
|
2940
3212
|
const fileName = `${stampMs}-${opts.kind}.json`;
|
|
2941
|
-
const redacted = redactObj(opts.payload, opts.redact);
|
|
3213
|
+
const redacted = redactObj(opts.payload, opts.redact ?? getRedactionConfig());
|
|
2942
3214
|
let absPath;
|
|
2943
3215
|
try {
|
|
2944
|
-
(0,
|
|
3216
|
+
(0, import_node_fs4.mkdirSync)(opts.transcriptsDir, { recursive: true });
|
|
2945
3217
|
absPath = import_node_path3.default.resolve(opts.transcriptsDir, fileName);
|
|
2946
|
-
(0,
|
|
3218
|
+
(0, import_node_fs4.writeFileSync)(absPath, JSON.stringify(redacted, null, 2), "utf8");
|
|
2947
3219
|
} catch {
|
|
2948
3220
|
return null;
|
|
2949
3221
|
}
|
|
@@ -3435,7 +3707,18 @@ async function runAudit(args, opts) {
|
|
|
3435
3707
|
for (const r2 of DEFAULT_AUDIT_RUBRICS) rubricItems.push({ ...r2 });
|
|
3436
3708
|
}
|
|
3437
3709
|
const rubricText = rubricItems.map((it) => `- ${it.id} (severity=${it.severity}): ${it.description}`).join("\n");
|
|
3438
|
-
const
|
|
3710
|
+
const persona = buildPersonaInjection({
|
|
3711
|
+
toolName: "audit",
|
|
3712
|
+
prompt: `${userConstraints ?? ""}
|
|
3713
|
+
${outputResolved}`,
|
|
3714
|
+
...typeof args["persona"] === "string" ? { explicit: args["persona"] } : {},
|
|
3715
|
+
...opts.repoRoot ? { repoRoot: opts.repoRoot } : {},
|
|
3716
|
+
sessionId: typeof args["session_id"] === "string" ? args["session_id"] : null
|
|
3717
|
+
});
|
|
3718
|
+
const auditorSys = "You are an independent auditor. Score the OUTPUT against each rubric item on a 0..1 likelihood that the rubric is satisfied. Set pass=true iff score >= 0.7. Be concise in `rationale` (1-2 sentences each).";
|
|
3719
|
+
const sysMsg = persona.block ? `${persona.block}
|
|
3720
|
+
|
|
3721
|
+
${auditorSys}` : auditorSys;
|
|
3439
3722
|
const userMsg = (userConstraints ? `USER CONSTRAINTS:
|
|
3440
3723
|
${userConstraints}
|
|
3441
3724
|
|
|
@@ -3532,6 +3815,7 @@ ${rubricText}`;
|
|
|
3532
3815
|
audit_process_failure: flags.audit_process_failure,
|
|
3533
3816
|
judges_stats: flags.judges_stats
|
|
3534
3817
|
};
|
|
3818
|
+
if (persona.meta.used !== null) coalesceResult["persona"] = persona.meta;
|
|
3535
3819
|
if (!allPass2 && opts.storage && typeof sessionId === "string" && sessionId) {
|
|
3536
3820
|
const marked = await markStaleOnAuditFailure(
|
|
3537
3821
|
opts.storage,
|
|
@@ -3612,6 +3896,7 @@ ${rubricText}`;
|
|
|
3612
3896
|
overall_score: overall,
|
|
3613
3897
|
passed: allPass
|
|
3614
3898
|
};
|
|
3899
|
+
if (persona.meta.used !== null) result["persona"] = persona.meta;
|
|
3615
3900
|
if (errs.length > 0) result["validation_errors"] = errs;
|
|
3616
3901
|
if (!allPass && opts.storage && typeof sessionId === "string" && sessionId) {
|
|
3617
3902
|
const marked = await markStaleOnAuditFailure(
|
|
@@ -3897,7 +4182,7 @@ function loadAuditTextFromSession(sessionId, transcriptsDir) {
|
|
|
3897
4182
|
if (!transcriptsDir) return "";
|
|
3898
4183
|
let entries;
|
|
3899
4184
|
try {
|
|
3900
|
-
entries = (0,
|
|
4185
|
+
entries = (0, import_node_fs5.readdirSync)(transcriptsDir);
|
|
3901
4186
|
} catch {
|
|
3902
4187
|
return "";
|
|
3903
4188
|
}
|
|
@@ -3908,7 +4193,7 @@ function loadAuditTextFromSession(sessionId, transcriptsDir) {
|
|
|
3908
4193
|
const p = (0, import_node_path4.join)(transcriptsDir, name);
|
|
3909
4194
|
let doc2;
|
|
3910
4195
|
try {
|
|
3911
|
-
doc2 = JSON.parse((0,
|
|
4196
|
+
doc2 = JSON.parse((0, import_node_fs5.readFileSync)(p, "utf8"));
|
|
3912
4197
|
} catch {
|
|
3913
4198
|
continue;
|
|
3914
4199
|
}
|
|
@@ -3916,7 +4201,7 @@ function loadAuditTextFromSession(sessionId, transcriptsDir) {
|
|
|
3916
4201
|
if (sid !== sessionId) continue;
|
|
3917
4202
|
let mtime;
|
|
3918
4203
|
try {
|
|
3919
|
-
mtime = (0,
|
|
4204
|
+
mtime = (0, import_node_fs5.statSync)(p).mtimeMs;
|
|
3920
4205
|
} catch {
|
|
3921
4206
|
continue;
|
|
3922
4207
|
}
|
|
@@ -3928,7 +4213,7 @@ function loadAuditTextFromSession(sessionId, transcriptsDir) {
|
|
|
3928
4213
|
if (bestPath === null) return "";
|
|
3929
4214
|
let doc;
|
|
3930
4215
|
try {
|
|
3931
|
-
doc = JSON.parse((0,
|
|
4216
|
+
doc = JSON.parse((0, import_node_fs5.readFileSync)(bestPath, "utf8"));
|
|
3932
4217
|
} catch {
|
|
3933
4218
|
return "";
|
|
3934
4219
|
}
|
|
@@ -3961,7 +4246,7 @@ function boolArg(v, defaultVal) {
|
|
|
3961
4246
|
|
|
3962
4247
|
// src/tools/bench.ts
|
|
3963
4248
|
init_cjs_shims();
|
|
3964
|
-
var
|
|
4249
|
+
var import_node_fs6 = require("fs");
|
|
3965
4250
|
var import_node_path6 = __toESM(require("path"), 1);
|
|
3966
4251
|
|
|
3967
4252
|
// src/tools/confer.ts
|
|
@@ -4498,21 +4783,21 @@ function extractToolCall(text) {
|
|
|
4498
4783
|
return { call, parseError: null };
|
|
4499
4784
|
}
|
|
4500
4785
|
function wrapToolResult(name, content) {
|
|
4501
|
-
let
|
|
4502
|
-
if (typeof content === "string")
|
|
4786
|
+
let str2;
|
|
4787
|
+
if (typeof content === "string") str2 = content;
|
|
4503
4788
|
else {
|
|
4504
4789
|
try {
|
|
4505
|
-
|
|
4790
|
+
str2 = JSON.stringify(content);
|
|
4506
4791
|
} catch {
|
|
4507
|
-
|
|
4792
|
+
str2 = String(content);
|
|
4508
4793
|
}
|
|
4509
4794
|
}
|
|
4510
|
-
if (
|
|
4511
|
-
|
|
4795
|
+
if (str2.length > WORKER_TOOLS_MAX_RESULT_CHARS) {
|
|
4796
|
+
str2 = str2.slice(0, WORKER_TOOLS_MAX_RESULT_CHARS) + "\n... (truncated)";
|
|
4512
4797
|
}
|
|
4513
4798
|
return `<tool_result name="${name}">
|
|
4514
4799
|
<untrusted_input>
|
|
4515
|
-
${neutralizeInjection(
|
|
4800
|
+
${neutralizeInjection(str2)}
|
|
4516
4801
|
</untrusted_input>
|
|
4517
4802
|
</tool_result>`;
|
|
4518
4803
|
}
|
|
@@ -5077,8 +5362,18 @@ async function runConfer(args, opts) {
|
|
|
5077
5362
|
const untrusted = Boolean(args["untrusted_input"]);
|
|
5078
5363
|
const canary = untrusted ? mintCanary() : null;
|
|
5079
5364
|
const baseSys = "You are part of a panel of LLMs consulted by an engineer working inside Claude Code. Answer directly, cite assumptions, and keep it crisp.";
|
|
5080
|
-
const
|
|
5081
|
-
|
|
5365
|
+
const persona = buildPersonaInjection({
|
|
5366
|
+
toolName: "confer",
|
|
5367
|
+
prompt: question,
|
|
5368
|
+
...typeof args["persona"] === "string" ? { explicit: args["persona"] } : {},
|
|
5369
|
+
...opts.repoRoot ? { repoRoot: opts.repoRoot } : {},
|
|
5370
|
+
sessionId: typeof args["session_id"] === "string" ? args["session_id"] : null
|
|
5371
|
+
});
|
|
5372
|
+
const baseSysP = persona.block ? `${persona.block}
|
|
5373
|
+
|
|
5374
|
+
${baseSys}` : baseSys;
|
|
5375
|
+
const sysMsg = untrusted ? `${baseSysP}
|
|
5376
|
+
${UNTRUSTED_SYSTEM_NOTE}` : baseSysP;
|
|
5082
5377
|
const messages = [{ role: "system", content: sysMsg }];
|
|
5083
5378
|
if (context) {
|
|
5084
5379
|
const ctxBody = untrusted ? wrapUntrusted(context, canary) : context;
|
|
@@ -5192,6 +5487,7 @@ ${ctxBody}` });
|
|
|
5192
5487
|
question,
|
|
5193
5488
|
answers: sanitized
|
|
5194
5489
|
};
|
|
5490
|
+
if (persona.meta.used !== null) result["persona"] = persona.meta;
|
|
5195
5491
|
if (earlyStopped) {
|
|
5196
5492
|
result["early_stopped"] = true;
|
|
5197
5493
|
result["skipped_providers"] = skippedProviders;
|
|
@@ -5411,9 +5707,19 @@ async function runSandboxed(args) {
|
|
|
5411
5707
|
}
|
|
5412
5708
|
resolve2(result);
|
|
5413
5709
|
};
|
|
5710
|
+
const memKb = isUnix && args.memoryMb && args.memoryMb > 0 ? Math.trunc(args.memoryMb * 1024) : 0;
|
|
5711
|
+
const cpuS = isUnix && args.cpuSeconds && args.cpuSeconds > 0 ? Math.trunc(args.cpuSeconds) : 0;
|
|
5712
|
+
const useUlimit = memKb > 0 || cpuS > 0;
|
|
5713
|
+
const spawnBin = useUlimit ? "/bin/sh" : args.cmd[0];
|
|
5714
|
+
const spawnArgv = useUlimit ? [
|
|
5715
|
+
"-c",
|
|
5716
|
+
`${[memKb > 0 ? `ulimit -v ${memKb}` : "", cpuS > 0 ? `ulimit -t ${cpuS}` : ""].filter(Boolean).join("; ")} 2>/dev/null; exec "$@"`,
|
|
5717
|
+
"sh",
|
|
5718
|
+
...args.cmd
|
|
5719
|
+
] : args.cmd.slice(1);
|
|
5414
5720
|
let child;
|
|
5415
5721
|
try {
|
|
5416
|
-
child = (0, import_node_child_process.spawn)(
|
|
5722
|
+
child = (0, import_node_child_process.spawn)(spawnBin, spawnArgv, {
|
|
5417
5723
|
cwd: tempCwd ?? process.cwd(),
|
|
5418
5724
|
env,
|
|
5419
5725
|
// detached: own process-group on Unix (equivalent to setsid).
|
|
@@ -6102,10 +6408,10 @@ async function runBench(args, opts) {
|
|
|
6102
6408
|
return result;
|
|
6103
6409
|
}
|
|
6104
6410
|
function loadGoldens(dirPath, filter) {
|
|
6105
|
-
if (!(0,
|
|
6411
|
+
if (!(0, import_node_fs6.existsSync)(dirPath)) return [];
|
|
6106
6412
|
let entries;
|
|
6107
6413
|
try {
|
|
6108
|
-
entries = (0,
|
|
6414
|
+
entries = (0, import_node_fs6.readdirSync)(dirPath);
|
|
6109
6415
|
} catch {
|
|
6110
6416
|
return [];
|
|
6111
6417
|
}
|
|
@@ -6114,13 +6420,13 @@ function loadGoldens(dirPath, filter) {
|
|
|
6114
6420
|
if (!name.endsWith(".json")) continue;
|
|
6115
6421
|
const p = import_node_path6.default.join(dirPath, name);
|
|
6116
6422
|
try {
|
|
6117
|
-
(0,
|
|
6423
|
+
(0, import_node_fs6.statSync)(p);
|
|
6118
6424
|
} catch {
|
|
6119
6425
|
continue;
|
|
6120
6426
|
}
|
|
6121
6427
|
let doc;
|
|
6122
6428
|
try {
|
|
6123
|
-
doc = JSON.parse((0,
|
|
6429
|
+
doc = JSON.parse((0, import_node_fs6.readFileSync)(p, "utf8"));
|
|
6124
6430
|
} catch {
|
|
6125
6431
|
continue;
|
|
6126
6432
|
}
|
|
@@ -6200,7 +6506,7 @@ function isObj4(v) {
|
|
|
6200
6506
|
// src/tools/config-pin.ts
|
|
6201
6507
|
init_cjs_shims();
|
|
6202
6508
|
var import_node_crypto3 = require("crypto");
|
|
6203
|
-
var
|
|
6509
|
+
var import_node_fs7 = require("fs");
|
|
6204
6510
|
var import_node_path7 = __toESM(require("path"), 1);
|
|
6205
6511
|
var DEFAULT_PIN_PATH = ".crosscheck/config_pins.json";
|
|
6206
6512
|
var DEFAULT_TARGETS = ["crosscheck.config.json", "config/pricing.json"];
|
|
@@ -6273,10 +6579,10 @@ async function runConfigPin(args, opts) {
|
|
|
6273
6579
|
};
|
|
6274
6580
|
}
|
|
6275
6581
|
const absPath = resolvePinPath(opts);
|
|
6276
|
-
const existed = (0,
|
|
6582
|
+
const existed = (0, import_node_fs7.existsSync)(absPath);
|
|
6277
6583
|
if (existed) {
|
|
6278
6584
|
try {
|
|
6279
|
-
(0,
|
|
6585
|
+
(0, import_node_fs7.unlinkSync)(absPath);
|
|
6280
6586
|
} catch (e) {
|
|
6281
6587
|
return errorEnvelope4(
|
|
6282
6588
|
"CONFIG_PIN_CLEAR_FAILED",
|
|
@@ -6311,7 +6617,7 @@ function computeDrift(opts) {
|
|
|
6311
6617
|
missing_files: missingFiles.sort(),
|
|
6312
6618
|
pin_file: relPath(opts, absPath),
|
|
6313
6619
|
pinned_at: Number(doc?.["pinned_at"] ?? 0) || 0,
|
|
6314
|
-
has_pin_file: (0,
|
|
6620
|
+
has_pin_file: (0, import_node_fs7.existsSync)(absPath)
|
|
6315
6621
|
};
|
|
6316
6622
|
}
|
|
6317
6623
|
function shouldBlock(opts, drift) {
|
|
@@ -6336,9 +6642,9 @@ function resolveTargets(opts) {
|
|
|
6336
6642
|
const out = [];
|
|
6337
6643
|
for (const p of list) {
|
|
6338
6644
|
const abs = import_node_path7.default.isAbsolute(p) ? p : import_node_path7.default.join(opts.repoRoot, p);
|
|
6339
|
-
if ((0,
|
|
6645
|
+
if ((0, import_node_fs7.existsSync)(abs)) {
|
|
6340
6646
|
try {
|
|
6341
|
-
const st = (0,
|
|
6647
|
+
const st = (0, import_node_fs7.statSync)(abs);
|
|
6342
6648
|
if (st.isFile()) out.push(abs);
|
|
6343
6649
|
} catch {
|
|
6344
6650
|
}
|
|
@@ -6349,7 +6655,7 @@ function resolveTargets(opts) {
|
|
|
6349
6655
|
function hashFile(abs) {
|
|
6350
6656
|
let raw;
|
|
6351
6657
|
try {
|
|
6352
|
-
raw = (0,
|
|
6658
|
+
raw = (0, import_node_fs7.readFileSync)(abs);
|
|
6353
6659
|
} catch {
|
|
6354
6660
|
return "";
|
|
6355
6661
|
}
|
|
@@ -6369,9 +6675,9 @@ function resolvePinPath(opts) {
|
|
|
6369
6675
|
return import_node_path7.default.isAbsolute(raw) ? raw : import_node_path7.default.join(opts.repoRoot, raw);
|
|
6370
6676
|
}
|
|
6371
6677
|
function loadDoc(absPath) {
|
|
6372
|
-
if (!(0,
|
|
6678
|
+
if (!(0, import_node_fs7.existsSync)(absPath)) return null;
|
|
6373
6679
|
try {
|
|
6374
|
-
const txt = (0,
|
|
6680
|
+
const txt = (0, import_node_fs7.readFileSync)(absPath, "utf8");
|
|
6375
6681
|
const doc = JSON.parse(txt);
|
|
6376
6682
|
return doc && typeof doc === "object" && !Array.isArray(doc) ? doc : null;
|
|
6377
6683
|
} catch {
|
|
@@ -6380,11 +6686,11 @@ function loadDoc(absPath) {
|
|
|
6380
6686
|
}
|
|
6381
6687
|
function saveDoc(opts, doc) {
|
|
6382
6688
|
const absPath = resolvePinPath(opts);
|
|
6383
|
-
(0,
|
|
6689
|
+
(0, import_node_fs7.mkdirSync)(import_node_path7.default.dirname(absPath), { recursive: true });
|
|
6384
6690
|
const tmp = absPath + ".tmp";
|
|
6385
|
-
(0,
|
|
6691
|
+
(0, import_node_fs7.writeFileSync)(tmp, JSON.stringify(doc, null, 2), "utf8");
|
|
6386
6692
|
try {
|
|
6387
|
-
(0,
|
|
6693
|
+
(0, import_node_fs7.unlinkSync)(absPath);
|
|
6388
6694
|
} catch {
|
|
6389
6695
|
}
|
|
6390
6696
|
const { renameSync: renameSync2 } = require("fs");
|
|
@@ -6433,7 +6739,7 @@ function pyRepr2(v) {
|
|
|
6433
6739
|
// src/tools/create.ts
|
|
6434
6740
|
init_cjs_shims();
|
|
6435
6741
|
var import_node_crypto6 = require("crypto");
|
|
6436
|
-
var
|
|
6742
|
+
var import_node_fs10 = require("fs");
|
|
6437
6743
|
var import_node_path10 = __toESM(require("path"), 1);
|
|
6438
6744
|
|
|
6439
6745
|
// src/tools/orchestrate.ts
|
|
@@ -6536,7 +6842,7 @@ function isDeadModelError(err) {
|
|
|
6536
6842
|
// src/core/node-cache.ts
|
|
6537
6843
|
init_cjs_shims();
|
|
6538
6844
|
var import_node_crypto4 = require("crypto");
|
|
6539
|
-
var
|
|
6845
|
+
var import_node_fs8 = require("fs");
|
|
6540
6846
|
var import_node_path8 = require("path");
|
|
6541
6847
|
var NODE_CACHE_KEY_VERSION = "v2";
|
|
6542
6848
|
var CANON_PATTERNS = [
|
|
@@ -6601,18 +6907,18 @@ function cachePath(cfg, repoRoot, key) {
|
|
|
6601
6907
|
function getNodeCache(cfg, repoRoot, key, nowMs) {
|
|
6602
6908
|
if (!cfg || cfg.enabled === false) return null;
|
|
6603
6909
|
const p = cachePath(cfg, repoRoot, key);
|
|
6604
|
-
if (!(0,
|
|
6910
|
+
if (!(0, import_node_fs8.existsSync)(p)) return null;
|
|
6605
6911
|
const ttlS = cfg.ttl_seconds ?? 604800;
|
|
6606
6912
|
try {
|
|
6607
|
-
const st = (0,
|
|
6913
|
+
const st = (0, import_node_fs8.statSync)(p);
|
|
6608
6914
|
if (ttlS > 0) {
|
|
6609
6915
|
const now = nowMs ?? Date.now();
|
|
6610
6916
|
if ((now - st.mtimeMs) / 1e3 > ttlS) return null;
|
|
6611
6917
|
}
|
|
6612
|
-
const data = JSON.parse((0,
|
|
6918
|
+
const data = JSON.parse((0, import_node_fs8.readFileSync)(p, "utf8"));
|
|
6613
6919
|
try {
|
|
6614
6920
|
const t = (nowMs ?? Date.now()) / 1e3;
|
|
6615
|
-
(0,
|
|
6921
|
+
(0, import_node_fs8.utimesSync)(p, t, t);
|
|
6616
6922
|
} catch {
|
|
6617
6923
|
}
|
|
6618
6924
|
return data;
|
|
@@ -6620,11 +6926,11 @@ function getNodeCache(cfg, repoRoot, key, nowMs) {
|
|
|
6620
6926
|
return null;
|
|
6621
6927
|
}
|
|
6622
6928
|
}
|
|
6623
|
-
function atomicWriteJson(
|
|
6624
|
-
(0,
|
|
6625
|
-
const tmp = `${
|
|
6626
|
-
(0,
|
|
6627
|
-
(0,
|
|
6929
|
+
function atomicWriteJson(path14, payload) {
|
|
6930
|
+
(0, import_node_fs8.mkdirSync)((0, import_node_path8.dirname)(path14), { recursive: true });
|
|
6931
|
+
const tmp = `${path14}.${process.pid}.${Date.now()}.tmp`;
|
|
6932
|
+
(0, import_node_fs8.writeFileSync)(tmp, JSON.stringify(payload), "utf8");
|
|
6933
|
+
(0, import_node_fs8.renameSync)(tmp, path14);
|
|
6628
6934
|
}
|
|
6629
6935
|
function putNodeCache(cfg, repoRoot, key, value) {
|
|
6630
6936
|
if (!cfg || cfg.enabled === false) return;
|
|
@@ -6635,23 +6941,23 @@ function putNodeCache(cfg, repoRoot, key, value) {
|
|
|
6635
6941
|
const maxBytes = cfg.max_cache_bytes ?? 100 * 1024 * 1024;
|
|
6636
6942
|
if (maxEntries <= 0 && maxBytes <= 0) return;
|
|
6637
6943
|
const base = resolveCacheDir(cfg, repoRoot);
|
|
6638
|
-
if (!(0,
|
|
6944
|
+
if (!(0, import_node_fs8.existsSync)(base)) return;
|
|
6639
6945
|
const entries = [];
|
|
6640
6946
|
let totalBytes = 0;
|
|
6641
|
-
for (const shard of (0,
|
|
6947
|
+
for (const shard of (0, import_node_fs8.readdirSync)(base)) {
|
|
6642
6948
|
const shardDir = (0, import_node_path8.join)(base, shard);
|
|
6643
6949
|
let stShard;
|
|
6644
6950
|
try {
|
|
6645
|
-
stShard = (0,
|
|
6951
|
+
stShard = (0, import_node_fs8.statSync)(shardDir);
|
|
6646
6952
|
} catch {
|
|
6647
6953
|
continue;
|
|
6648
6954
|
}
|
|
6649
6955
|
if (!stShard.isDirectory()) continue;
|
|
6650
|
-
for (const f of (0,
|
|
6956
|
+
for (const f of (0, import_node_fs8.readdirSync)(shardDir)) {
|
|
6651
6957
|
if (!f.endsWith(".json")) continue;
|
|
6652
6958
|
const fp = (0, import_node_path8.join)(shardDir, f);
|
|
6653
6959
|
try {
|
|
6654
|
-
const st = (0,
|
|
6960
|
+
const st = (0, import_node_fs8.statSync)(fp);
|
|
6655
6961
|
entries.push({ mtime: st.mtimeMs, size: st.size, path: fp });
|
|
6656
6962
|
totalBytes += st.size;
|
|
6657
6963
|
} catch {
|
|
@@ -6665,7 +6971,7 @@ function putNodeCache(cfg, repoRoot, key, value) {
|
|
|
6665
6971
|
while (entries.length > 0 && (maxEntries > 0 && entries.length > maxEntries || maxBytes > 0 && totalBytes > maxBytes)) {
|
|
6666
6972
|
const oldest = entries.shift();
|
|
6667
6973
|
try {
|
|
6668
|
-
(0,
|
|
6974
|
+
(0, import_node_fs8.unlinkSync)(oldest.path);
|
|
6669
6975
|
totalBytes -= oldest.size;
|
|
6670
6976
|
} catch {
|
|
6671
6977
|
}
|
|
@@ -7227,8 +7533,19 @@ NODE OUTPUTS:
|
|
|
7227
7533
|
${recombineLines.join("\n\n")}
|
|
7228
7534
|
|
|
7229
7535
|
Synthesize the node outputs into a single coherent deliverable. Preserve any [MISSING: ...] markers verbatim where the upstream node failed so the caller sees what is incomplete.`;
|
|
7536
|
+
const persona = buildPersonaInjection({
|
|
7537
|
+
toolName: "orchestrate",
|
|
7538
|
+
prompt: goal ?? recombinePrompt,
|
|
7539
|
+
...typeof args["persona"] === "string" ? { explicit: args["persona"] } : {},
|
|
7540
|
+
...opts.repoRoot ? { repoRoot: opts.repoRoot } : {},
|
|
7541
|
+
sessionId: typeof args["session_id"] === "string" ? args["session_id"] : null
|
|
7542
|
+
});
|
|
7543
|
+
const personaMeta = persona.meta;
|
|
7544
|
+
const recombineSys = "You are the orchestrator. Combine node outputs into the final result.";
|
|
7230
7545
|
const recMsgs = [
|
|
7231
|
-
{ role: "system", content:
|
|
7546
|
+
{ role: "system", content: persona.block ? `${persona.block}
|
|
7547
|
+
|
|
7548
|
+
${recombineSys}` : recombineSys },
|
|
7232
7549
|
{ role: "user", content: recombinePrompt }
|
|
7233
7550
|
];
|
|
7234
7551
|
let synthProvider = moderator;
|
|
@@ -7301,6 +7618,7 @@ Synthesize the node outputs into a single coherent deliverable. Preserve any [MI
|
|
|
7301
7618
|
fail_fast: failFast,
|
|
7302
7619
|
cheap_mode: cheapMode
|
|
7303
7620
|
};
|
|
7621
|
+
if (personaMeta.used !== null) result["persona"] = personaMeta;
|
|
7304
7622
|
if (synthModel) result["synth_model"] = synthModel;
|
|
7305
7623
|
if (synthErr) result["synth_error"] = synthErr;
|
|
7306
7624
|
if (plannerErrors.length > 0) result["planner_errors"] = plannerErrors;
|
|
@@ -7692,7 +8010,7 @@ function pyRound6(x) {
|
|
|
7692
8010
|
// src/tools/fetch.ts
|
|
7693
8011
|
init_cjs_shims();
|
|
7694
8012
|
var import_node_crypto5 = require("crypto");
|
|
7695
|
-
var
|
|
8013
|
+
var import_node_fs9 = require("fs");
|
|
7696
8014
|
var import_node_path9 = __toESM(require("path"), 1);
|
|
7697
8015
|
var DEFAULT_CONFIG = {
|
|
7698
8016
|
enabled: true,
|
|
@@ -7774,9 +8092,9 @@ async function runFetch(args, opts) {
|
|
|
7774
8092
|
const evDir = resolveEvidenceDir(cfg.evidence_dir, opts.repoRoot);
|
|
7775
8093
|
const urlHash16 = sha256Hex2(url).slice(0, 16);
|
|
7776
8094
|
const metaPath = import_node_path9.default.join(evDir, `by-url-${urlHash16}.json`);
|
|
7777
|
-
if ((0,
|
|
8095
|
+
if ((0, import_node_fs9.existsSync)(metaPath) && !force) {
|
|
7778
8096
|
try {
|
|
7779
|
-
const meta2 = JSON.parse((0,
|
|
8097
|
+
const meta2 = JSON.parse((0, import_node_fs9.readFileSync)(metaPath, "utf8"));
|
|
7780
8098
|
return {
|
|
7781
8099
|
...base,
|
|
7782
8100
|
accepted: true,
|
|
@@ -7816,15 +8134,41 @@ async function runFetch(args, opts) {
|
|
|
7816
8134
|
}
|
|
7817
8135
|
let data;
|
|
7818
8136
|
try {
|
|
7819
|
-
const
|
|
7820
|
-
if (
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
accepted: false,
|
|
7824
|
-
|
|
7825
|
-
|
|
8137
|
+
const reader = response.body?.getReader?.();
|
|
8138
|
+
if (!reader) {
|
|
8139
|
+
const buf = await response.arrayBuffer();
|
|
8140
|
+
if (buf.byteLength > cfg.max_bytes) {
|
|
8141
|
+
return { ...base, accepted: false, reason: `response exceeds max_bytes=${cfg.max_bytes}` };
|
|
8142
|
+
}
|
|
8143
|
+
data = new Uint8Array(buf);
|
|
8144
|
+
} else {
|
|
8145
|
+
const chunks = [];
|
|
8146
|
+
let total = 0;
|
|
8147
|
+
let exceeded = false;
|
|
8148
|
+
for (; ; ) {
|
|
8149
|
+
const { done, value } = await reader.read();
|
|
8150
|
+
if (done) break;
|
|
8151
|
+
if (value) {
|
|
8152
|
+
total += value.byteLength;
|
|
8153
|
+
if (total > cfg.max_bytes) {
|
|
8154
|
+
exceeded = true;
|
|
8155
|
+
await reader.cancel().catch(() => {
|
|
8156
|
+
});
|
|
8157
|
+
break;
|
|
8158
|
+
}
|
|
8159
|
+
chunks.push(value);
|
|
8160
|
+
}
|
|
8161
|
+
}
|
|
8162
|
+
if (exceeded) {
|
|
8163
|
+
return { ...base, accepted: false, reason: `response exceeds max_bytes=${cfg.max_bytes}` };
|
|
8164
|
+
}
|
|
8165
|
+
data = new Uint8Array(total);
|
|
8166
|
+
let offset = 0;
|
|
8167
|
+
for (const c of chunks) {
|
|
8168
|
+
data.set(c, offset);
|
|
8169
|
+
offset += c.byteLength;
|
|
8170
|
+
}
|
|
7826
8171
|
}
|
|
7827
|
-
data = new Uint8Array(buf);
|
|
7828
8172
|
} catch (e) {
|
|
7829
8173
|
return {
|
|
7830
8174
|
...base,
|
|
@@ -7836,8 +8180,8 @@ async function runFetch(args, opts) {
|
|
|
7836
8180
|
const status = response.status;
|
|
7837
8181
|
const sha = sha256HexBytes(data);
|
|
7838
8182
|
const bodyPath = import_node_path9.default.join(evDir, `${sha}.bin`);
|
|
7839
|
-
(0,
|
|
7840
|
-
(0,
|
|
8183
|
+
(0, import_node_fs9.mkdirSync)(evDir, { recursive: true });
|
|
8184
|
+
(0, import_node_fs9.writeFileSync)(bodyPath, data);
|
|
7841
8185
|
const relBody = makeRelative2(bodyPath, opts.repoRoot);
|
|
7842
8186
|
const now = opts.nowEpochSeconds ? opts.nowEpochSeconds() : Math.floor(Date.now() / 1e3);
|
|
7843
8187
|
const meta = {
|
|
@@ -7849,7 +8193,7 @@ async function runFetch(args, opts) {
|
|
|
7849
8193
|
status,
|
|
7850
8194
|
fetched_at: now
|
|
7851
8195
|
};
|
|
7852
|
-
(0,
|
|
8196
|
+
(0, import_node_fs9.writeFileSync)(metaPath, JSON.stringify(meta, null, 2));
|
|
7853
8197
|
if (sessionId && host && opts.storage) {
|
|
7854
8198
|
try {
|
|
7855
8199
|
await opts.storage.recordFetchEgress(sessionId, host, data.byteLength, now);
|
|
@@ -8132,8 +8476,8 @@ ${documentsPayload}`
|
|
|
8132
8476
|
if (targetPath && !dryRun && typeof orchestration["final"] === "string" && orchestration["final"] && !blockWriteStatuses.has(status)) {
|
|
8133
8477
|
try {
|
|
8134
8478
|
const tp = import_node_path10.default.isAbsolute(targetPath) ? targetPath : opts.repoRoot ? import_node_path10.default.join(opts.repoRoot, targetPath) : import_node_path10.default.resolve(targetPath);
|
|
8135
|
-
(0,
|
|
8136
|
-
(0,
|
|
8479
|
+
(0, import_node_fs10.mkdirSync)(import_node_path10.default.dirname(tp), { recursive: true });
|
|
8480
|
+
(0, import_node_fs10.writeFileSync)(tp, orchestration["final"], "utf8");
|
|
8137
8481
|
artifacts.push({ path: targetPath, bytes: orchestration["final"].length });
|
|
8138
8482
|
if (status === "audit_failed" || status === "audit_inconclusive") {
|
|
8139
8483
|
warnings.push(
|
|
@@ -8203,7 +8547,7 @@ async function ingestDocuments(documents, sessionId, opts) {
|
|
|
8203
8547
|
if (typeof evidencePath === "string" && evidencePath) {
|
|
8204
8548
|
const abs = import_node_path10.default.isAbsolute(evidencePath) ? evidencePath : opts.repoRoot ? import_node_path10.default.join(opts.repoRoot, evidencePath) : evidencePath;
|
|
8205
8549
|
try {
|
|
8206
|
-
text = (0,
|
|
8550
|
+
text = (0, import_node_fs10.readFileSync)(abs, "utf8");
|
|
8207
8551
|
} catch {
|
|
8208
8552
|
text = "";
|
|
8209
8553
|
}
|
|
@@ -8217,7 +8561,7 @@ async function ingestDocuments(documents, sessionId, opts) {
|
|
|
8217
8561
|
}, text));
|
|
8218
8562
|
} else {
|
|
8219
8563
|
const abs = import_node_path10.default.isAbsolute(ref) ? ref : opts.repoRoot ? import_node_path10.default.join(opts.repoRoot, ref) : import_node_path10.default.resolve(ref);
|
|
8220
|
-
if (!(0,
|
|
8564
|
+
if (!(0, import_node_fs10.existsSync)(abs)) {
|
|
8221
8565
|
out.push({
|
|
8222
8566
|
source: ref,
|
|
8223
8567
|
type: "file",
|
|
@@ -8229,7 +8573,7 @@ async function ingestDocuments(documents, sessionId, opts) {
|
|
|
8229
8573
|
}
|
|
8230
8574
|
let text = "";
|
|
8231
8575
|
try {
|
|
8232
|
-
text = (0,
|
|
8576
|
+
text = (0, import_node_fs10.readFileSync)(abs, "utf8");
|
|
8233
8577
|
} catch (e) {
|
|
8234
8578
|
out.push({
|
|
8235
8579
|
source: ref,
|
|
@@ -9244,15 +9588,27 @@ ${prior}` });
|
|
|
9244
9588
|
let synthesis = null;
|
|
9245
9589
|
let synthesisStructured = null;
|
|
9246
9590
|
let synthesisErrors = [];
|
|
9591
|
+
let personaMeta = null;
|
|
9247
9592
|
if (moderator) {
|
|
9248
9593
|
const condensed = transcript.map(
|
|
9249
9594
|
(e) => `[${e.provider} \u2014 round ${e.round}]
|
|
9250
9595
|
${e.response ?? "(error)"}`
|
|
9251
9596
|
).join("\n\n");
|
|
9597
|
+
const persona = buildPersonaInjection({
|
|
9598
|
+
toolName: "debate",
|
|
9599
|
+
prompt: topic,
|
|
9600
|
+
...typeof args["persona"] === "string" ? { explicit: args["persona"] } : {},
|
|
9601
|
+
...opts.repoRoot ? { repoRoot: opts.repoRoot } : {},
|
|
9602
|
+
sessionId: typeof args["session_id"] === "string" ? args["session_id"] : null
|
|
9603
|
+
});
|
|
9604
|
+
personaMeta = persona.meta;
|
|
9605
|
+
const synthSys = "You are the moderator. Synthesise the debate into a single grounded recommendation.";
|
|
9252
9606
|
const synthMessages = [
|
|
9253
9607
|
{
|
|
9254
9608
|
role: "system",
|
|
9255
|
-
content:
|
|
9609
|
+
content: persona.block ? `${persona.block}
|
|
9610
|
+
|
|
9611
|
+
${synthSys}` : synthSys
|
|
9256
9612
|
},
|
|
9257
9613
|
{
|
|
9258
9614
|
role: "user",
|
|
@@ -9304,6 +9660,7 @@ ${condensed}`
|
|
|
9304
9660
|
transcript,
|
|
9305
9661
|
synthesis
|
|
9306
9662
|
};
|
|
9663
|
+
if (personaMeta && personaMeta.used !== null) result["persona"] = personaMeta;
|
|
9307
9664
|
if (synthesisStructured !== null) {
|
|
9308
9665
|
result["synthesis_structured"] = synthesisStructured;
|
|
9309
9666
|
}
|
|
@@ -9463,9 +9820,9 @@ async function runDelegate(args, opts) {
|
|
|
9463
9820
|
return await deferDelegate(args, opts.bridge);
|
|
9464
9821
|
}
|
|
9465
9822
|
return errorEnvelope10(
|
|
9466
|
-
"
|
|
9467
|
-
"delegate
|
|
9468
|
-
"
|
|
9823
|
+
"DELEGATE_STORAGE_UNAVAILABLE",
|
|
9824
|
+
"delegate needs the local database (for quota tracking), which isn't available",
|
|
9825
|
+
"The engine stores data at <data dir>/db.sqlite (default ~/.crosscheck/db.sqlite). This usually means the native better-sqlite3 module failed to load \u2014 run `npx -y crosscheck-cli@latest doctor`, or set CROSSCHECK_DB_PATH to a writable location, then reconnect."
|
|
9469
9826
|
);
|
|
9470
9827
|
}
|
|
9471
9828
|
const storage = opts.storage;
|
|
@@ -9618,7 +9975,7 @@ function pyListRepr2(xs) {
|
|
|
9618
9975
|
|
|
9619
9976
|
// src/tools/explain.ts
|
|
9620
9977
|
init_cjs_shims();
|
|
9621
|
-
var
|
|
9978
|
+
var import_node_fs11 = require("fs");
|
|
9622
9979
|
var import_node_path11 = __toESM(require("path"), 1);
|
|
9623
9980
|
async function runExplain(args, opts) {
|
|
9624
9981
|
const sessionId = args["session_id"];
|
|
@@ -9634,9 +9991,9 @@ async function runExplain(args, opts) {
|
|
|
9634
9991
|
return await deferExplain(args, opts.bridge);
|
|
9635
9992
|
}
|
|
9636
9993
|
return errorEnvelope11(
|
|
9637
|
-
"
|
|
9638
|
-
"explain
|
|
9639
|
-
"
|
|
9994
|
+
"EXPLAIN_STORAGE_UNAVAILABLE",
|
|
9995
|
+
"explain needs the local transcript/stats database, which isn't available",
|
|
9996
|
+
"The engine stores data at <data dir>/db.sqlite (default ~/.crosscheck/db.sqlite). This usually means the native better-sqlite3 module failed to load \u2014 run `npx -y crosscheck-cli@latest doctor`, or set CROSSCHECK_DB_PATH to a writable location, then reconnect."
|
|
9640
9997
|
);
|
|
9641
9998
|
}
|
|
9642
9999
|
const storage = opts.storage;
|
|
@@ -9720,10 +10077,10 @@ async function runExplain(args, opts) {
|
|
|
9720
10077
|
return result;
|
|
9721
10078
|
}
|
|
9722
10079
|
function loadTranscriptsForSession(dir, sessionId) {
|
|
9723
|
-
if (!(0,
|
|
10080
|
+
if (!(0, import_node_fs11.existsSync)(dir)) return [];
|
|
9724
10081
|
let names;
|
|
9725
10082
|
try {
|
|
9726
|
-
names = (0,
|
|
10083
|
+
names = (0, import_node_fs11.readdirSync)(dir);
|
|
9727
10084
|
} catch {
|
|
9728
10085
|
return [];
|
|
9729
10086
|
}
|
|
@@ -9733,13 +10090,13 @@ function loadTranscriptsForSession(dir, sessionId) {
|
|
|
9733
10090
|
const p = import_node_path11.default.join(dir, name);
|
|
9734
10091
|
let stat;
|
|
9735
10092
|
try {
|
|
9736
|
-
stat = (0,
|
|
10093
|
+
stat = (0, import_node_fs11.statSync)(p);
|
|
9737
10094
|
} catch {
|
|
9738
10095
|
continue;
|
|
9739
10096
|
}
|
|
9740
10097
|
let doc;
|
|
9741
10098
|
try {
|
|
9742
|
-
doc = JSON.parse((0,
|
|
10099
|
+
doc = JSON.parse((0, import_node_fs11.readFileSync)(p, "utf8"));
|
|
9743
10100
|
} catch {
|
|
9744
10101
|
continue;
|
|
9745
10102
|
}
|
|
@@ -10399,9 +10756,9 @@ async function runRecall(args, opts) {
|
|
|
10399
10756
|
return await deferRecall(args, opts.bridge);
|
|
10400
10757
|
}
|
|
10401
10758
|
return errorEnvelope12(
|
|
10402
|
-
"
|
|
10403
|
-
"recall
|
|
10404
|
-
"
|
|
10759
|
+
"RECALL_STORAGE_UNAVAILABLE",
|
|
10760
|
+
"recall needs the local database, which isn't available",
|
|
10761
|
+
"The engine stores data at <data dir>/db.sqlite (default ~/.crosscheck/db.sqlite). This usually means the native better-sqlite3 module failed to load \u2014 run `npx -y crosscheck-cli@latest doctor`, or set CROSSCHECK_DB_PATH to a writable location, then reconnect."
|
|
10405
10762
|
);
|
|
10406
10763
|
}
|
|
10407
10764
|
const filter = {};
|
|
@@ -10492,9 +10849,9 @@ async function runRecommendPanel(args, opts) {
|
|
|
10492
10849
|
return await deferRecommendPanel(args, opts.bridge);
|
|
10493
10850
|
}
|
|
10494
10851
|
return errorEnvelope13(
|
|
10495
|
-
"
|
|
10496
|
-
"recommend_panel
|
|
10497
|
-
"
|
|
10852
|
+
"RECOMMEND_PANEL_STORAGE_UNAVAILABLE",
|
|
10853
|
+
"recommend_panel needs the local stats database, which isn't available",
|
|
10854
|
+
"The engine stores stats at <data dir>/db.sqlite (default ~/.crosscheck/db.sqlite). This usually means the native better-sqlite3 module failed to load \u2014 run `npx -y crosscheck-cli@latest doctor`, or set CROSSCHECK_DB_PATH to a writable location, then reconnect."
|
|
10498
10855
|
);
|
|
10499
10856
|
}
|
|
10500
10857
|
const n = Math.max(1, clampInt2(args["n"], 1, 100, 2));
|
|
@@ -10595,16 +10952,16 @@ function toStringArray3(v) {
|
|
|
10595
10952
|
|
|
10596
10953
|
// src/tools/scoreboard.ts
|
|
10597
10954
|
init_cjs_shims();
|
|
10598
|
-
var
|
|
10955
|
+
var import_node_fs12 = require("fs");
|
|
10599
10956
|
async function runScoreboard(args, opts) {
|
|
10600
10957
|
if (!opts.storage) {
|
|
10601
10958
|
if (opts.bridge && opts.bridge.toolNames.has("scoreboard")) {
|
|
10602
10959
|
return await deferScoreboard(args, opts.bridge);
|
|
10603
10960
|
}
|
|
10604
10961
|
return errorEnvelope14(
|
|
10605
|
-
"
|
|
10606
|
-
"scoreboard
|
|
10607
|
-
"
|
|
10962
|
+
"SCOREBOARD_STORAGE_UNAVAILABLE",
|
|
10963
|
+
"scoreboard needs the local stats database, which isn't available",
|
|
10964
|
+
"The engine stores stats at <data dir>/db.sqlite (default ~/.crosscheck/db.sqlite). This usually means the native better-sqlite3 module failed to load \u2014 run `npx -y crosscheck-cli@latest doctor`, or set CROSSCHECK_DB_PATH to a writable location, then reconnect."
|
|
10608
10965
|
);
|
|
10609
10966
|
}
|
|
10610
10967
|
const topK = Math.max(1, clampInt3(args["top_k"], 1, 1e4, 20));
|
|
@@ -10678,9 +11035,9 @@ async function runScoreboard(args, opts) {
|
|
|
10678
11035
|
}
|
|
10679
11036
|
usageTotals.cost_usd = Number(usageTotals.cost_usd.toFixed(8));
|
|
10680
11037
|
let recentEvents = [];
|
|
10681
|
-
if (recentLimit > 0 && opts.eventsPath && (0,
|
|
11038
|
+
if (recentLimit > 0 && opts.eventsPath && (0, import_node_fs12.existsSync)(opts.eventsPath)) {
|
|
10682
11039
|
try {
|
|
10683
|
-
const lines = (0,
|
|
11040
|
+
const lines = (0, import_node_fs12.readFileSync)(opts.eventsPath, "utf8").split("\n").filter((l) => l.length > 0);
|
|
10684
11041
|
const tail = lines.slice(-recentLimit);
|
|
10685
11042
|
for (const ln of tail) {
|
|
10686
11043
|
try {
|
|
@@ -10779,9 +11136,9 @@ async function runSessionMemory(args, opts) {
|
|
|
10779
11136
|
return await deferSessionMemory(args, opts.bridge);
|
|
10780
11137
|
}
|
|
10781
11138
|
return errorEnvelope15(
|
|
10782
|
-
"
|
|
10783
|
-
"session_memory
|
|
10784
|
-
"
|
|
11139
|
+
"SESSION_MEMORY_STORAGE_UNAVAILABLE",
|
|
11140
|
+
"session_memory needs the local database, which isn't available",
|
|
11141
|
+
"The engine stores data at <data dir>/db.sqlite (default ~/.crosscheck/db.sqlite). This usually means the native better-sqlite3 module failed to load \u2014 run `npx -y crosscheck-cli@latest doctor`, or set CROSSCHECK_DB_PATH to a writable location, then reconnect."
|
|
10785
11142
|
);
|
|
10786
11143
|
}
|
|
10787
11144
|
const storage = opts.storage;
|
|
@@ -10929,6 +11286,122 @@ function pyRepr6(v) {
|
|
|
10929
11286
|
// src/tools/solve.ts
|
|
10930
11287
|
init_cjs_shims();
|
|
10931
11288
|
var import_node_perf_hooks11 = require("perf_hooks");
|
|
11289
|
+
var import_node_fs13 = require("fs");
|
|
11290
|
+
var import_node_path12 = __toESM(require("path"), 1);
|
|
11291
|
+
|
|
11292
|
+
// src/core/unified-diff.ts
|
|
11293
|
+
init_cjs_shims();
|
|
11294
|
+
function diffLines(a, b) {
|
|
11295
|
+
const n = a.length;
|
|
11296
|
+
const m = b.length;
|
|
11297
|
+
const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
|
|
11298
|
+
for (let i2 = n - 1; i2 >= 0; i2 -= 1) {
|
|
11299
|
+
for (let j2 = m - 1; j2 >= 0; j2 -= 1) {
|
|
11300
|
+
dp[i2][j2] = a[i2] === b[j2] ? dp[i2 + 1][j2 + 1] + 1 : Math.max(dp[i2 + 1][j2], dp[i2][j2 + 1]);
|
|
11301
|
+
}
|
|
11302
|
+
}
|
|
11303
|
+
const ops = [];
|
|
11304
|
+
let i = 0;
|
|
11305
|
+
let j = 0;
|
|
11306
|
+
while (i < n && j < m) {
|
|
11307
|
+
if (a[i] === b[j]) {
|
|
11308
|
+
ops.push({ type: "eq", line: a[i] });
|
|
11309
|
+
i += 1;
|
|
11310
|
+
j += 1;
|
|
11311
|
+
} else if (dp[i + 1][j] >= dp[i][j + 1]) {
|
|
11312
|
+
ops.push({ type: "del", line: a[i] });
|
|
11313
|
+
i += 1;
|
|
11314
|
+
} else {
|
|
11315
|
+
ops.push({ type: "ins", line: b[j] });
|
|
11316
|
+
j += 1;
|
|
11317
|
+
}
|
|
11318
|
+
}
|
|
11319
|
+
while (i < n) {
|
|
11320
|
+
ops.push({ type: "del", line: a[i] });
|
|
11321
|
+
i += 1;
|
|
11322
|
+
}
|
|
11323
|
+
while (j < m) {
|
|
11324
|
+
ops.push({ type: "ins", line: b[j] });
|
|
11325
|
+
j += 1;
|
|
11326
|
+
}
|
|
11327
|
+
return ops;
|
|
11328
|
+
}
|
|
11329
|
+
function unifiedDiff(oldText, newText, opts) {
|
|
11330
|
+
if (oldText === newText) return "";
|
|
11331
|
+
const context = Math.max(0, opts?.context ?? 3);
|
|
11332
|
+
const a = oldText.split("\n");
|
|
11333
|
+
const b = newText.split("\n");
|
|
11334
|
+
const ops = diffLines(a, b);
|
|
11335
|
+
const indexed = [];
|
|
11336
|
+
let ai = 0;
|
|
11337
|
+
let bi = 0;
|
|
11338
|
+
for (const op of ops) {
|
|
11339
|
+
if (op.type === "eq") {
|
|
11340
|
+
indexed.push({ ...op, aIdx: ai, bIdx: bi });
|
|
11341
|
+
ai += 1;
|
|
11342
|
+
bi += 1;
|
|
11343
|
+
} else if (op.type === "del") {
|
|
11344
|
+
indexed.push({ ...op, aIdx: ai, bIdx: bi });
|
|
11345
|
+
ai += 1;
|
|
11346
|
+
} else {
|
|
11347
|
+
indexed.push({ ...op, aIdx: ai, bIdx: bi });
|
|
11348
|
+
bi += 1;
|
|
11349
|
+
}
|
|
11350
|
+
}
|
|
11351
|
+
const changeIdx = indexed.map((o, k) => o.type === "eq" ? -1 : k).filter((k) => k >= 0);
|
|
11352
|
+
if (changeIdx.length === 0) return "";
|
|
11353
|
+
const groups = [];
|
|
11354
|
+
let gStart = changeIdx[0];
|
|
11355
|
+
let gEnd = changeIdx[0];
|
|
11356
|
+
for (let k = 1; k < changeIdx.length; k += 1) {
|
|
11357
|
+
const idx = changeIdx[k];
|
|
11358
|
+
if (idx - gEnd <= 2 * context + 1) {
|
|
11359
|
+
gEnd = idx;
|
|
11360
|
+
} else {
|
|
11361
|
+
groups.push([gStart, gEnd]);
|
|
11362
|
+
gStart = idx;
|
|
11363
|
+
gEnd = idx;
|
|
11364
|
+
}
|
|
11365
|
+
}
|
|
11366
|
+
groups.push([gStart, gEnd]);
|
|
11367
|
+
const lines = [];
|
|
11368
|
+
lines.push(`--- ${opts?.fromFile ?? "a"}`);
|
|
11369
|
+
lines.push(`+++ ${opts?.toFile ?? "b"}`);
|
|
11370
|
+
for (const [start, end] of groups) {
|
|
11371
|
+
const from = Math.max(0, start - context);
|
|
11372
|
+
const to = Math.min(indexed.length - 1, end + context);
|
|
11373
|
+
let aCount = 0;
|
|
11374
|
+
let bCount = 0;
|
|
11375
|
+
let aStart = -1;
|
|
11376
|
+
let bStart = -1;
|
|
11377
|
+
const body = [];
|
|
11378
|
+
for (let k = from; k <= to; k += 1) {
|
|
11379
|
+
const o = indexed[k];
|
|
11380
|
+
if (o.type === "eq") {
|
|
11381
|
+
if (aStart < 0) aStart = o.aIdx;
|
|
11382
|
+
if (bStart < 0) bStart = o.bIdx;
|
|
11383
|
+
aCount += 1;
|
|
11384
|
+
bCount += 1;
|
|
11385
|
+
body.push(` ${o.line}`);
|
|
11386
|
+
} else if (o.type === "del") {
|
|
11387
|
+
if (aStart < 0) aStart = o.aIdx;
|
|
11388
|
+
aCount += 1;
|
|
11389
|
+
body.push(`-${o.line}`);
|
|
11390
|
+
} else {
|
|
11391
|
+
if (bStart < 0) bStart = o.bIdx;
|
|
11392
|
+
bCount += 1;
|
|
11393
|
+
body.push(`+${o.line}`);
|
|
11394
|
+
}
|
|
11395
|
+
}
|
|
11396
|
+
const aHdr = aCount === 0 ? 0 : aStart + 1;
|
|
11397
|
+
const bHdr = bCount === 0 ? 0 : bStart + 1;
|
|
11398
|
+
lines.push(`@@ -${aHdr},${aCount} +${bHdr},${bCount} @@`);
|
|
11399
|
+
lines.push(...body);
|
|
11400
|
+
}
|
|
11401
|
+
return lines.join("\n") + "\n";
|
|
11402
|
+
}
|
|
11403
|
+
|
|
11404
|
+
// src/tools/solve.ts
|
|
10932
11405
|
async function runSolve(args, opts) {
|
|
10933
11406
|
const problem = typeof args["problem"] === "string" ? args["problem"] : String(args["problem"] ?? "");
|
|
10934
11407
|
if (!problem) {
|
|
@@ -11070,7 +11543,23 @@ Re-emit the FULL solution. No commentary.`
|
|
|
11070
11543
|
const targetPath = args["target_path"];
|
|
11071
11544
|
if (typeof targetPath === "string" && targetPath) {
|
|
11072
11545
|
result["target_path"] = targetPath;
|
|
11073
|
-
|
|
11546
|
+
if (solved && typeof finalProposal === "string") {
|
|
11547
|
+
const abs = import_node_path12.default.isAbsolute(targetPath) ? targetPath : import_node_path12.default.join(opts.repoRoot ?? process.cwd(), targetPath);
|
|
11548
|
+
let oldText = "";
|
|
11549
|
+
try {
|
|
11550
|
+
oldText = (0, import_node_fs13.readFileSync)(abs, "utf8");
|
|
11551
|
+
} catch {
|
|
11552
|
+
oldText = "";
|
|
11553
|
+
}
|
|
11554
|
+
const newText = finalProposal.endsWith("\n") ? finalProposal : `${finalProposal}
|
|
11555
|
+
`;
|
|
11556
|
+
result["patch"] = unifiedDiff(oldText, newText, {
|
|
11557
|
+
fromFile: `a/${targetPath}`,
|
|
11558
|
+
toFile: `b/${targetPath}`
|
|
11559
|
+
}) || null;
|
|
11560
|
+
} else {
|
|
11561
|
+
result["patch"] = null;
|
|
11562
|
+
}
|
|
11074
11563
|
}
|
|
11075
11564
|
if (unknownNames.length > 0) result["skipped_unknown_providers"] = unknownNames;
|
|
11076
11565
|
if (blocked.length > 0) result["blocked_by_allowlist"] = blocked;
|
|
@@ -11156,6 +11645,8 @@ async function verifyProposalShell(verifier, proposal) {
|
|
|
11156
11645
|
}
|
|
11157
11646
|
const argv = cmd.map((x) => String(x));
|
|
11158
11647
|
const timeoutS = numberArg2(verifier["timeout_s"], 10);
|
|
11648
|
+
const memoryMb = numberArg2(verifier["memory_mb"], 0);
|
|
11649
|
+
const cpuSeconds = numberArg2(verifier["cpu_seconds"], 0);
|
|
11159
11650
|
const expectExit = pyIntCoerce2(verifier["expect_exit_code"], 0);
|
|
11160
11651
|
const expectContains = typeof verifier["expect_stdout_contains"] === "string" ? verifier["expect_stdout_contains"] : null;
|
|
11161
11652
|
const expectRegex = typeof verifier["expect_stdout_regex"] === "string" ? verifier["expect_stdout_regex"] : null;
|
|
@@ -11165,7 +11656,9 @@ async function verifyProposalShell(verifier, proposal) {
|
|
|
11165
11656
|
input: proposal,
|
|
11166
11657
|
// proposal piped to stdin
|
|
11167
11658
|
stdoutTailBytes: 2048,
|
|
11168
|
-
stderrTailBytes: 2048
|
|
11659
|
+
stderrTailBytes: 2048,
|
|
11660
|
+
...memoryMb > 0 ? { memoryMb } : {},
|
|
11661
|
+
...cpuSeconds > 0 ? { cpuSeconds } : {}
|
|
11169
11662
|
});
|
|
11170
11663
|
if (r.status === "timeout") {
|
|
11171
11664
|
return {
|
|
@@ -11335,24 +11828,24 @@ function pyRepr7(v) {
|
|
|
11335
11828
|
// src/tools/update-crosscheck.ts
|
|
11336
11829
|
init_cjs_shims();
|
|
11337
11830
|
var import_node_child_process2 = require("child_process");
|
|
11338
|
-
var
|
|
11339
|
-
var
|
|
11831
|
+
var import_node_fs15 = require("fs");
|
|
11832
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
11340
11833
|
|
|
11341
11834
|
// src/core/update-notify.ts
|
|
11342
11835
|
init_cjs_shims();
|
|
11343
|
-
var
|
|
11836
|
+
var import_node_fs14 = require("fs");
|
|
11344
11837
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
11345
|
-
var
|
|
11838
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
11346
11839
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
11347
11840
|
var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
|
|
11348
11841
|
var DEFAULT_PACKAGE = "crosscheck-cli";
|
|
11349
11842
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
11350
11843
|
function engineVersion() {
|
|
11351
|
-
return true ? "0.1.
|
|
11844
|
+
return true ? "0.1.13" : "0.0.0-dev";
|
|
11352
11845
|
}
|
|
11353
11846
|
function defaultUpdateCachePath() {
|
|
11354
|
-
const base = process.env["CROSSCHECK_DATA_DIR"] ||
|
|
11355
|
-
return
|
|
11847
|
+
const base = process.env["CROSSCHECK_DATA_DIR"] || import_node_path13.default.join(import_node_os2.default.homedir() || import_node_os2.default.tmpdir(), ".crosscheck");
|
|
11848
|
+
return import_node_path13.default.join(base, "update_check.json");
|
|
11356
11849
|
}
|
|
11357
11850
|
function isNewer(latest, current) {
|
|
11358
11851
|
const a = parseTriple(latest);
|
|
@@ -11382,8 +11875,8 @@ then reconnect the crosscheck MCP server (run \`/mcp\` in Claude Code, or restar
|
|
|
11382
11875
|
}
|
|
11383
11876
|
function readCache(cachePath2) {
|
|
11384
11877
|
try {
|
|
11385
|
-
if (!(0,
|
|
11386
|
-
const data = JSON.parse((0,
|
|
11878
|
+
if (!(0, import_node_fs14.existsSync)(cachePath2)) return null;
|
|
11879
|
+
const data = JSON.parse((0, import_node_fs14.readFileSync)(cachePath2, "utf8"));
|
|
11387
11880
|
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
11388
11881
|
return data;
|
|
11389
11882
|
}
|
|
@@ -11394,8 +11887,8 @@ function readCache(cachePath2) {
|
|
|
11394
11887
|
}
|
|
11395
11888
|
function writeCache(cachePath2, record2) {
|
|
11396
11889
|
try {
|
|
11397
|
-
(0,
|
|
11398
|
-
(0,
|
|
11890
|
+
(0, import_node_fs14.mkdirSync)(import_node_path13.default.dirname(cachePath2), { recursive: true });
|
|
11891
|
+
(0, import_node_fs14.writeFileSync)(cachePath2, JSON.stringify(record2, null, 2), "utf8");
|
|
11399
11892
|
} catch {
|
|
11400
11893
|
}
|
|
11401
11894
|
}
|
|
@@ -11448,7 +11941,7 @@ async function runUpdateCrosscheck(args, opts) {
|
|
|
11448
11941
|
const gitRun = opts.gitRun ?? defaultGitRun(opts.repoRoot);
|
|
11449
11942
|
const fetchFn = opts.httpFetch ?? globalThis.fetch;
|
|
11450
11943
|
const now = opts.nowEpochSeconds ? opts.nowEpochSeconds() : Math.floor(Date.now() / 1e3);
|
|
11451
|
-
const cachePath2 = opts.cachePath ??
|
|
11944
|
+
const cachePath2 = opts.cachePath ?? import_node_path14.default.join(opts.repoRoot, ".crosscheck", "update_check.json");
|
|
11452
11945
|
const local = readLocalSha(gitRun);
|
|
11453
11946
|
if (!local) {
|
|
11454
11947
|
return {
|
|
@@ -11620,8 +12113,8 @@ function gitRelationship(gitRun, local, remote) {
|
|
|
11620
12113
|
}
|
|
11621
12114
|
function writeUpdateCache(p, payload) {
|
|
11622
12115
|
try {
|
|
11623
|
-
(0,
|
|
11624
|
-
(0,
|
|
12116
|
+
(0, import_node_fs15.mkdirSync)(import_node_path14.default.dirname(p), { recursive: true });
|
|
12117
|
+
(0, import_node_fs15.writeFileSync)(p, JSON.stringify(payload, null, 2), "utf8");
|
|
11625
12118
|
} catch {
|
|
11626
12119
|
}
|
|
11627
12120
|
}
|
|
@@ -11968,7 +12461,7 @@ function createTool(o, toolName, cheapDefault) {
|
|
|
11968
12461
|
function orchestrateTool(providers, allowlist, bridge, moderator, storage, pricing, breakers, transcriptsDir, repoRoot, nodeCache) {
|
|
11969
12462
|
return {
|
|
11970
12463
|
name: "orchestrate",
|
|
11971
|
-
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`.
|
|
12464
|
+
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.",
|
|
11972
12465
|
inputSchema: {
|
|
11973
12466
|
type: "object",
|
|
11974
12467
|
additionalProperties: true,
|
|
@@ -12002,7 +12495,7 @@ function orchestrateTool(providers, allowlist, bridge, moderator, storage, prici
|
|
|
12002
12495
|
function solveTool(providers, allowlist, bridge, storage, breakers, transcriptsDir, repoRoot) {
|
|
12003
12496
|
return {
|
|
12004
12497
|
name: "solve",
|
|
12005
|
-
description: "Iterative LLM solver. Generates a proposal, verifies it against the supplied verifier, and retries with feedback up to max_attempts.
|
|
12498
|
+
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.",
|
|
12006
12499
|
inputSchema: {
|
|
12007
12500
|
type: "object",
|
|
12008
12501
|
additionalProperties: true,
|
|
@@ -12200,7 +12693,7 @@ function explainTool(storage, bridge, transcriptsDir) {
|
|
|
12200
12693
|
function scoreboardTool(storage, bridge, eventsPath) {
|
|
12201
12694
|
return {
|
|
12202
12695
|
name: "scoreboard",
|
|
12203
|
-
description: "Aggregate provider ballot stats + delegation counts + table totals. Supports top_k (rank limit) and recent_limit (tail of events.jsonl when configured).
|
|
12696
|
+
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).",
|
|
12204
12697
|
inputSchema: {
|
|
12205
12698
|
type: "object",
|
|
12206
12699
|
additionalProperties: true,
|
|
@@ -12219,7 +12712,7 @@ function scoreboardTool(storage, bridge, eventsPath) {
|
|
|
12219
12712
|
function sessionMemoryTool(storage, bridge) {
|
|
12220
12713
|
return {
|
|
12221
12714
|
name: "session_memory",
|
|
12222
|
-
description: "CRUD over the per-session working memory ledger. Actions: list, add, mark_stale, clear.
|
|
12715
|
+
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).",
|
|
12223
12716
|
inputSchema: {
|
|
12224
12717
|
type: "object",
|
|
12225
12718
|
additionalProperties: true,
|
|
@@ -12636,8 +13129,8 @@ function pingTool() {
|
|
|
12636
13129
|
|
|
12637
13130
|
// src/core/wrappers.ts
|
|
12638
13131
|
init_cjs_shims();
|
|
12639
|
-
var
|
|
12640
|
-
var
|
|
13132
|
+
var import_node_fs16 = require("fs");
|
|
13133
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
12641
13134
|
function configPinGate(toolName, opts) {
|
|
12642
13135
|
if (toolName === "config_pin") return null;
|
|
12643
13136
|
if (!opts || !opts.repoRoot) return null;
|
|
@@ -12669,8 +13162,8 @@ function configPinGate(toolName, opts) {
|
|
|
12669
13162
|
var cachedUpdateNotice = null;
|
|
12670
13163
|
function readUpdateCache(cachePath2) {
|
|
12671
13164
|
try {
|
|
12672
|
-
if (!(0,
|
|
12673
|
-
const data = JSON.parse((0,
|
|
13165
|
+
if (!(0, import_node_fs16.existsSync)(cachePath2)) return null;
|
|
13166
|
+
const data = JSON.parse((0, import_node_fs16.readFileSync)(cachePath2, "utf8"));
|
|
12674
13167
|
if (data && typeof data === "object" && !Array.isArray(data)) return data;
|
|
12675
13168
|
return null;
|
|
12676
13169
|
} catch {
|
|
@@ -12682,7 +13175,7 @@ function attachUpdateNotice(result, toolName, opts) {
|
|
|
12682
13175
|
if (toolName === "update_crosscheck") return;
|
|
12683
13176
|
if ("update_notice" in result) return;
|
|
12684
13177
|
if (cachedUpdateNotice === null) {
|
|
12685
|
-
const cachePath2 = opts?.cachePath ?? (opts?.repoRoot ?
|
|
13178
|
+
const cachePath2 = opts?.cachePath ?? (opts?.repoRoot ? import_node_path15.default.join(opts.repoRoot, ".crosscheck", "update_check.json") : defaultUpdateCachePath());
|
|
12686
13179
|
const cached = readUpdateCache(cachePath2);
|
|
12687
13180
|
if (cached && cached.update_available && cached.notice && typeof cached.notice === "object") {
|
|
12688
13181
|
cachedUpdateNotice = cached.notice;
|
|
@@ -12880,7 +13373,7 @@ async function flush() {
|
|
|
12880
13373
|
|
|
12881
13374
|
// src/server.ts
|
|
12882
13375
|
var SERVER_NAME = "crosscheck-agent";
|
|
12883
|
-
var SERVER_VERSION = true ? "0.1.
|
|
13376
|
+
var SERVER_VERSION = true ? "0.1.13" : "0.0.0-dev";
|
|
12884
13377
|
function extractRunSummaryText(out) {
|
|
12885
13378
|
if (out === null || typeof out !== "object" || Array.isArray(out)) return void 0;
|
|
12886
13379
|
const rs = out["run_summary"];
|
|
@@ -12902,14 +13395,14 @@ function createServer(opts = {}) {
|
|
|
12902
13395
|
}
|
|
12903
13396
|
);
|
|
12904
13397
|
const tools = buildToolRegistry(opts);
|
|
12905
|
-
server.setRequestHandler(
|
|
13398
|
+
server.setRequestHandler(import_types8.ListToolsRequestSchema, async () => ({
|
|
12906
13399
|
tools: Array.from(tools.values()).map((t) => ({
|
|
12907
13400
|
name: t.name,
|
|
12908
13401
|
description: t.description,
|
|
12909
13402
|
inputSchema: t.inputSchema
|
|
12910
13403
|
}))
|
|
12911
13404
|
}));
|
|
12912
|
-
server.setRequestHandler(
|
|
13405
|
+
server.setRequestHandler(import_types8.CallToolRequestSchema, async (req) => {
|
|
12913
13406
|
const name = req.params.name;
|
|
12914
13407
|
const tool = tools.get(name);
|
|
12915
13408
|
if (!tool) {
|
|
@@ -13033,6 +13526,10 @@ function buildToolRegistry(opts) {
|
|
|
13033
13526
|
if (opts.nodeCache) registerOpts.nodeCache = opts.nodeCache;
|
|
13034
13527
|
if (opts.configPinning) registerOpts.configPinning = opts.configPinning;
|
|
13035
13528
|
if (opts.rejectConfigDriftEnv) registerOpts.rejectConfigDriftEnv = opts.rejectConfigDriftEnv;
|
|
13529
|
+
if (opts.fetchConfig) registerOpts.fetchConfig = opts.fetchConfig;
|
|
13530
|
+
if (opts.moderatorDefault) registerOpts.moderatorDefault = opts.moderatorDefault;
|
|
13531
|
+
if (opts.benchGoldensDir) registerOpts.benchGoldensDir = opts.benchGoldensDir;
|
|
13532
|
+
if (opts.eventsPath) registerOpts.eventsPath = opts.eventsPath;
|
|
13036
13533
|
const tools = registerCoreTools(registerOpts);
|
|
13037
13534
|
if (!opts.bridge) return tools;
|
|
13038
13535
|
const proxies = buildPythonProxies(opts.bridge);
|
|
@@ -13042,29 +13539,207 @@ function buildToolRegistry(opts) {
|
|
|
13042
13539
|
return tools;
|
|
13043
13540
|
}
|
|
13044
13541
|
|
|
13542
|
+
// src/core/config-file.ts
|
|
13543
|
+
init_cjs_shims();
|
|
13544
|
+
var import_node_fs17 = require("fs");
|
|
13545
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
13546
|
+
var import_node_crypto8 = require("crypto");
|
|
13547
|
+
function asObj(v) {
|
|
13548
|
+
return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
|
|
13549
|
+
}
|
|
13550
|
+
function num(v) {
|
|
13551
|
+
const n = Number(v);
|
|
13552
|
+
return Number.isFinite(n) ? n : void 0;
|
|
13553
|
+
}
|
|
13554
|
+
function str(v) {
|
|
13555
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
13556
|
+
}
|
|
13557
|
+
function bool(v) {
|
|
13558
|
+
return typeof v === "boolean" ? v : void 0;
|
|
13559
|
+
}
|
|
13560
|
+
function strArr(v) {
|
|
13561
|
+
return Array.isArray(v) ? v.filter((x) => typeof x === "string") : void 0;
|
|
13562
|
+
}
|
|
13563
|
+
function pickNums(src, keys) {
|
|
13564
|
+
const out = {};
|
|
13565
|
+
for (const k of keys) {
|
|
13566
|
+
const n = num(src[k]);
|
|
13567
|
+
if (n !== void 0) out[k] = n;
|
|
13568
|
+
}
|
|
13569
|
+
return out;
|
|
13570
|
+
}
|
|
13571
|
+
function findConfigFile(opts) {
|
|
13572
|
+
const direct = opts.explicitPath ?? process.env["CROSSCHECK_CONFIG_PATH"];
|
|
13573
|
+
if (direct) return (0, import_node_fs17.existsSync)(direct) ? direct : void 0;
|
|
13574
|
+
const candidates = [];
|
|
13575
|
+
let dir = opts.startDir;
|
|
13576
|
+
for (let i = 0; dir && i < 8; i += 1) {
|
|
13577
|
+
candidates.push(import_node_path16.default.join(dir, "crosscheck.config.json"));
|
|
13578
|
+
const parent = import_node_path16.default.dirname(dir);
|
|
13579
|
+
if (parent === dir) break;
|
|
13580
|
+
dir = parent;
|
|
13581
|
+
}
|
|
13582
|
+
if (opts.repoRoot) candidates.push(import_node_path16.default.join(opts.repoRoot, "crosscheck.config.json"));
|
|
13583
|
+
if (opts.dataDir) candidates.push(import_node_path16.default.join(opts.dataDir, "crosscheck.config.json"));
|
|
13584
|
+
return candidates.find((p) => (0, import_node_fs17.existsSync)(p));
|
|
13585
|
+
}
|
|
13586
|
+
function mapConfig(raw, sourcePath) {
|
|
13587
|
+
const result = { sourcePath };
|
|
13588
|
+
const cb = asObj(raw["circuit_breakers"]);
|
|
13589
|
+
if (cb) {
|
|
13590
|
+
const picked = pickNums(cb, [
|
|
13591
|
+
"max_session_cost_usd",
|
|
13592
|
+
"max_session_tokens",
|
|
13593
|
+
"max_session_wall_seconds",
|
|
13594
|
+
"max_dag_nodes",
|
|
13595
|
+
"max_dag_depth"
|
|
13596
|
+
]);
|
|
13597
|
+
if (Object.keys(picked).length > 0) result.circuitBreakers = picked;
|
|
13598
|
+
}
|
|
13599
|
+
const nc = asObj(raw["node_cache"]);
|
|
13600
|
+
if (nc) {
|
|
13601
|
+
const cfg = {
|
|
13602
|
+
...pickNums(nc, ["ttl_seconds", "max_entries", "max_cache_bytes"])
|
|
13603
|
+
};
|
|
13604
|
+
if (bool(nc["enabled"]) !== void 0) cfg.enabled = bool(nc["enabled"]);
|
|
13605
|
+
if (str(nc["dir"]) !== void 0) cfg.dir = str(nc["dir"]);
|
|
13606
|
+
result.nodeCache = cfg;
|
|
13607
|
+
}
|
|
13608
|
+
const f = asObj(raw["fetch"]);
|
|
13609
|
+
if (f) {
|
|
13610
|
+
const cfg = {
|
|
13611
|
+
...pickNums(f, [
|
|
13612
|
+
"max_bytes",
|
|
13613
|
+
"max_bytes_per_session",
|
|
13614
|
+
"max_unique_hosts_per_session",
|
|
13615
|
+
"timeout_s"
|
|
13616
|
+
])
|
|
13617
|
+
};
|
|
13618
|
+
if (bool(f["enabled"]) !== void 0) cfg.enabled = bool(f["enabled"]);
|
|
13619
|
+
if (strArr(f["url_allowlist"]) !== void 0) cfg.url_allowlist = strArr(f["url_allowlist"]);
|
|
13620
|
+
if (str(f["evidence_dir"]) !== void 0) cfg.evidence_dir = str(f["evidence_dir"]);
|
|
13621
|
+
result.fetchConfig = cfg;
|
|
13622
|
+
}
|
|
13623
|
+
if ("provider_allowlist" in raw) {
|
|
13624
|
+
const pa = raw["provider_allowlist"];
|
|
13625
|
+
if (pa === null) result.providerAllowlist = null;
|
|
13626
|
+
else if (strArr(pa) !== void 0) result.providerAllowlist = strArr(pa);
|
|
13627
|
+
}
|
|
13628
|
+
const moderator = str(raw["moderator"]);
|
|
13629
|
+
if (moderator) result.moderatorDefault = moderator.toLowerCase();
|
|
13630
|
+
const bench = asObj(raw["bench"]);
|
|
13631
|
+
const goldens = bench ? str(bench["goldens_dir"]) : void 0;
|
|
13632
|
+
if (goldens) result.benchGoldensDir = goldens;
|
|
13633
|
+
const td = str(raw["transcript_dir"]);
|
|
13634
|
+
if (td) result.transcriptsDir = td;
|
|
13635
|
+
const ev = str(raw["events_log"]);
|
|
13636
|
+
if (ev) result.eventsPath = ev;
|
|
13637
|
+
const db = str(raw["session_db"]);
|
|
13638
|
+
if (db) result.dbPath = db;
|
|
13639
|
+
const lt = bool(raw["log_transcripts"]);
|
|
13640
|
+
if (lt !== void 0) result.logTranscripts = lt;
|
|
13641
|
+
const retries = asObj(raw["retries"]);
|
|
13642
|
+
if (retries) {
|
|
13643
|
+
const maxAttempts = num(retries["max_attempts"]);
|
|
13644
|
+
const baseS = num(retries["backoff_base_s"]);
|
|
13645
|
+
const cfg = {};
|
|
13646
|
+
if (maxAttempts !== void 0) cfg.maxAttempts = Math.max(1, Math.trunc(maxAttempts));
|
|
13647
|
+
if (baseS !== void 0) cfg.backoffBaseMs = Math.round(baseS * 1e3);
|
|
13648
|
+
if (Object.keys(cfg).length > 0) setRetryConfig(cfg);
|
|
13649
|
+
}
|
|
13650
|
+
const rl = asObj(raw["rate_limits"]);
|
|
13651
|
+
if (rl) {
|
|
13652
|
+
const mapped = {};
|
|
13653
|
+
for (const [provider, spec] of Object.entries(rl)) {
|
|
13654
|
+
const s = asObj(spec);
|
|
13655
|
+
const capacity = s ? num(s["capacity"]) : void 0;
|
|
13656
|
+
const refill = s ? num(s["refill_per_sec"]) : void 0;
|
|
13657
|
+
if (capacity !== void 0 && refill !== void 0) {
|
|
13658
|
+
mapped[provider] = { capacity, refillPerSec: refill };
|
|
13659
|
+
}
|
|
13660
|
+
}
|
|
13661
|
+
if (Object.keys(mapped).length > 0) setRateLimitConfig(mapped);
|
|
13662
|
+
}
|
|
13663
|
+
const tb = asObj(raw["token_budgets"]);
|
|
13664
|
+
if (tb) {
|
|
13665
|
+
const budgets = {};
|
|
13666
|
+
for (const [purpose, v] of Object.entries(tb)) {
|
|
13667
|
+
const n = num(v);
|
|
13668
|
+
if (n !== void 0 && n > 0) budgets[purpose] = Math.trunc(n);
|
|
13669
|
+
}
|
|
13670
|
+
if (Object.keys(budgets).length > 0) setOperatorBudgets({ token_budgets: budgets });
|
|
13671
|
+
}
|
|
13672
|
+
const red = asObj(raw["redaction"]);
|
|
13673
|
+
if (red) {
|
|
13674
|
+
const cfg = {};
|
|
13675
|
+
if (bool(red["enabled"]) !== void 0) cfg.enabled = bool(red["enabled"]);
|
|
13676
|
+
const extras = strArr(red["patterns_extra"]);
|
|
13677
|
+
if (extras && extras.length > 0) cfg.patterns_extra = extras;
|
|
13678
|
+
if (bool(red["hmac_tokens"]) === true) {
|
|
13679
|
+
cfg.hmac_tokens = true;
|
|
13680
|
+
cfg.hmac_secret = (0, import_node_crypto8.randomBytes)(32);
|
|
13681
|
+
}
|
|
13682
|
+
if (Object.keys(cfg).length > 0) setRedactionConfig(cfg);
|
|
13683
|
+
}
|
|
13684
|
+
const personas = asObj(raw["personas"]);
|
|
13685
|
+
if (personas) {
|
|
13686
|
+
const cfg = {};
|
|
13687
|
+
if (bool(personas["enabled"]) !== void 0) cfg.enabled = bool(personas["enabled"]);
|
|
13688
|
+
if (str(personas["dir"]) !== void 0) cfg.dir = str(personas["dir"]);
|
|
13689
|
+
if (Object.keys(cfg).length > 0) setPersonaConfig(cfg);
|
|
13690
|
+
}
|
|
13691
|
+
return result;
|
|
13692
|
+
}
|
|
13693
|
+
function loadConfigFile(opts) {
|
|
13694
|
+
const file = findConfigFile(opts);
|
|
13695
|
+
if (!file) return null;
|
|
13696
|
+
let raw;
|
|
13697
|
+
try {
|
|
13698
|
+
raw = JSON.parse((0, import_node_fs17.readFileSync)(file, "utf8"));
|
|
13699
|
+
} catch (e) {
|
|
13700
|
+
try {
|
|
13701
|
+
process.stderr.write(
|
|
13702
|
+
`crosscheck-agent: failed to parse ${file} (${e.message}); using defaults
|
|
13703
|
+
`
|
|
13704
|
+
);
|
|
13705
|
+
} catch {
|
|
13706
|
+
}
|
|
13707
|
+
return null;
|
|
13708
|
+
}
|
|
13709
|
+
const obj = asObj(raw);
|
|
13710
|
+
if (!obj) return null;
|
|
13711
|
+
return mapConfig(obj, file);
|
|
13712
|
+
}
|
|
13713
|
+
|
|
13045
13714
|
// src/entrypoints/node-stdio.ts
|
|
13046
13715
|
var SHUTDOWN_TIMEOUT_MS = 2e3;
|
|
13047
13716
|
async function main() {
|
|
13717
|
+
const cfgDataDir = process.env["CROSSCHECK_DATA_DIR"] || import_node_path17.default.join(process.env["HOME"] ?? process.env["USERPROFILE"] ?? "", ".crosscheck");
|
|
13718
|
+
const configFile = loadConfigFile({
|
|
13719
|
+
startDir: process.cwd(),
|
|
13720
|
+
repoRoot: findGitRoot(__dirname),
|
|
13721
|
+
dataDir: cfgDataDir
|
|
13722
|
+
});
|
|
13723
|
+
if (configFile) {
|
|
13724
|
+
if (configFile.eventsPath && !process.env["CROSSCHECK_EVENTS_PATH"]) {
|
|
13725
|
+
process.env["CROSSCHECK_EVENTS_PATH"] = configFile.eventsPath;
|
|
13726
|
+
}
|
|
13727
|
+
if (configFile.dbPath && !process.env["CROSSCHECK_DB_PATH"]) {
|
|
13728
|
+
process.env["CROSSCHECK_DB_PATH"] = configFile.dbPath;
|
|
13729
|
+
}
|
|
13730
|
+
process.stderr.write(`crosscheck-agent: loaded config from ${configFile.sourcePath}
|
|
13731
|
+
`);
|
|
13732
|
+
}
|
|
13048
13733
|
setEventEmitter(buildDefaultEmitter(process.env));
|
|
13049
|
-
let bridge;
|
|
13050
13734
|
if (process.env["CROSSCHECK_BRIDGE_PYTHON"] === "1") {
|
|
13051
|
-
bridge = await spawnPythonBridge({
|
|
13052
|
-
...process.env["CROSSCHECK_PYTHON_PATH"] ? { pythonPath: process.env["CROSSCHECK_PYTHON_PATH"] } : {},
|
|
13053
|
-
...process.env["CROSSCHECK_PYTHON_SERVER"] ? { serverPath: process.env["CROSSCHECK_PYTHON_SERVER"] } : {}
|
|
13054
|
-
});
|
|
13055
13735
|
process.stderr.write(
|
|
13056
|
-
|
|
13057
|
-
`
|
|
13058
|
-
);
|
|
13059
|
-
} else {
|
|
13060
|
-
process.stderr.write(
|
|
13061
|
-
"crosscheck-agent: native-only mode (set CROSSCHECK_BRIDGE_PYTHON=1 to enable the optional Python backstop)\n"
|
|
13736
|
+
"crosscheck-agent: CROSSCHECK_BRIDGE_PYTHON is deprecated and ignored \u2014 the engine is native-only.\n"
|
|
13062
13737
|
);
|
|
13063
13738
|
}
|
|
13064
|
-
installShutdownHandlers(
|
|
13739
|
+
installShutdownHandlers();
|
|
13065
13740
|
const bundledPricing = (() => {
|
|
13066
13741
|
try {
|
|
13067
|
-
return
|
|
13742
|
+
return import_node_path17.default.join(import_node_path17.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl)), "pricing.json");
|
|
13068
13743
|
} catch {
|
|
13069
13744
|
return void 0;
|
|
13070
13745
|
}
|
|
@@ -13073,7 +13748,7 @@ async function main() {
|
|
|
13073
13748
|
process.env["CROSSCHECK_PRICING_PATH"],
|
|
13074
13749
|
resolveRepoFile("config/pricing.json"),
|
|
13075
13750
|
bundledPricing
|
|
13076
|
-
].find((p) => p && (0,
|
|
13751
|
+
].find((p) => p && (0, import_node_fs18.existsSync)(p));
|
|
13077
13752
|
const pricing = pricingPath ? loadPricing(pricingPath) : {};
|
|
13078
13753
|
const providers = buildProviders({ env: process.env, pricing });
|
|
13079
13754
|
if (Object.keys(providers).length > 0) {
|
|
@@ -13084,10 +13759,10 @@ async function main() {
|
|
|
13084
13759
|
}
|
|
13085
13760
|
let storage;
|
|
13086
13761
|
if (process.env["CROSSCHECK_DB_DISABLED"] !== "1") {
|
|
13087
|
-
const dbPath = process.env["CROSSCHECK_DB_PATH"] ?? resolveRepoFile(".crosscheck/db.sqlite");
|
|
13762
|
+
const dbPath = process.env["CROSSCHECK_DB_PATH"] ?? resolveRepoFile(".crosscheck/db.sqlite") ?? import_node_path17.default.join(cfgDataDir, "db.sqlite");
|
|
13088
13763
|
if (dbPath) {
|
|
13089
13764
|
try {
|
|
13090
|
-
(0,
|
|
13765
|
+
(0, import_node_fs18.mkdirSync)(import_node_path17.default.dirname(dbPath), { recursive: true });
|
|
13091
13766
|
const { openBetterSqliteStorage: openBetterSqliteStorage2 } = await Promise.resolve().then(() => (init_better_sqlite3(), better_sqlite3_exports));
|
|
13092
13767
|
storage = openBetterSqliteStorage2({ path: dbPath });
|
|
13093
13768
|
await storage.migrate();
|
|
@@ -13097,40 +13772,39 @@ async function main() {
|
|
|
13097
13772
|
);
|
|
13098
13773
|
} catch (e) {
|
|
13099
13774
|
process.stderr.write(
|
|
13100
|
-
`crosscheck-agent: storage init failed (${e.message}); storage tools will
|
|
13775
|
+
`crosscheck-agent: storage init failed (${e.message}); storage-backed tools (recall, scoreboard, recommend_panel, session_memory, explain) will be unavailable
|
|
13101
13776
|
`
|
|
13102
13777
|
);
|
|
13103
13778
|
storage = void 0;
|
|
13104
13779
|
}
|
|
13105
13780
|
}
|
|
13106
13781
|
}
|
|
13107
|
-
const transcriptsDir = process.env["CROSSCHECK_TRANSCRIPTS_DIR"] ?? resolveRepoFile(".crosscheck/transcripts");
|
|
13782
|
+
const transcriptsDir = configFile?.logTranscripts === false ? void 0 : process.env["CROSSCHECK_TRANSCRIPTS_DIR"] ?? configFile?.transcriptsDir ?? resolveRepoFile(".crosscheck/transcripts");
|
|
13108
13783
|
const repoRoot = process.env["CROSSCHECK_REPO_ROOT"] ?? findGitRoot(__dirname);
|
|
13109
|
-
const transport = new
|
|
13784
|
+
const transport = new import_stdio.StdioServerTransport();
|
|
13110
13785
|
const serverOpts = { providers };
|
|
13111
|
-
if (bridge) serverOpts.bridge = bridge;
|
|
13112
13786
|
if (storage) serverOpts.storage = storage;
|
|
13113
13787
|
if (transcriptsDir) serverOpts.transcriptsDir = transcriptsDir;
|
|
13114
13788
|
if (repoRoot) serverOpts.repoRoot = repoRoot;
|
|
13115
13789
|
if (pricing && Object.keys(pricing).length > 0) {
|
|
13116
13790
|
serverOpts.pricing = pricing;
|
|
13117
13791
|
}
|
|
13792
|
+
if (configFile?.circuitBreakers) serverOpts.circuitBreakers = configFile.circuitBreakers;
|
|
13793
|
+
if (configFile?.nodeCache) serverOpts.nodeCache = configFile.nodeCache;
|
|
13794
|
+
if (configFile?.fetchConfig) serverOpts.fetchConfig = configFile.fetchConfig;
|
|
13795
|
+
if (configFile?.providerAllowlist !== void 0) {
|
|
13796
|
+
serverOpts.providerAllowlist = configFile.providerAllowlist;
|
|
13797
|
+
}
|
|
13798
|
+
if (configFile?.moderatorDefault) serverOpts.moderatorDefault = configFile.moderatorDefault;
|
|
13799
|
+
if (configFile?.benchGoldensDir) serverOpts.benchGoldensDir = configFile.benchGoldensDir;
|
|
13800
|
+
if (configFile?.eventsPath) serverOpts.eventsPath = configFile.eventsPath;
|
|
13118
13801
|
await connectAndServe(transport, serverOpts);
|
|
13119
13802
|
}
|
|
13120
|
-
function installShutdownHandlers(
|
|
13803
|
+
function installShutdownHandlers() {
|
|
13121
13804
|
let shuttingDown = false;
|
|
13122
13805
|
const shutdown = async (reason) => {
|
|
13123
13806
|
if (shuttingDown) return;
|
|
13124
13807
|
shuttingDown = true;
|
|
13125
|
-
if (bridge) {
|
|
13126
|
-
await Promise.race([
|
|
13127
|
-
bridge.close(),
|
|
13128
|
-
new Promise(
|
|
13129
|
-
(resolve2) => setTimeout(resolve2, SHUTDOWN_TIMEOUT_MS)
|
|
13130
|
-
)
|
|
13131
|
-
]).catch(() => {
|
|
13132
|
-
});
|
|
13133
|
-
}
|
|
13134
13808
|
await Promise.race([
|
|
13135
13809
|
flush(),
|
|
13136
13810
|
new Promise((resolve2) => setTimeout(resolve2, SHUTDOWN_TIMEOUT_MS))
|
|
@@ -13156,9 +13830,9 @@ function installShutdownHandlers(bridge) {
|
|
|
13156
13830
|
function resolveRepoFile(rel) {
|
|
13157
13831
|
let dir = __dirname;
|
|
13158
13832
|
for (let i = 0; i < 8; i++) {
|
|
13159
|
-
const candidate =
|
|
13160
|
-
if ((0,
|
|
13161
|
-
const parent =
|
|
13833
|
+
const candidate = import_node_path17.default.join(dir, rel);
|
|
13834
|
+
if ((0, import_node_fs18.existsSync)(candidate)) return candidate;
|
|
13835
|
+
const parent = import_node_path17.default.dirname(dir);
|
|
13162
13836
|
if (parent === dir) break;
|
|
13163
13837
|
dir = parent;
|
|
13164
13838
|
}
|
|
@@ -13167,8 +13841,8 @@ function resolveRepoFile(rel) {
|
|
|
13167
13841
|
function findGitRoot(startDir) {
|
|
13168
13842
|
let dir = startDir;
|
|
13169
13843
|
for (let i = 0; i < 12; i++) {
|
|
13170
|
-
if ((0,
|
|
13171
|
-
const parent =
|
|
13844
|
+
if ((0, import_node_fs18.existsSync)(import_node_path17.default.join(dir, ".git"))) return dir;
|
|
13845
|
+
const parent = import_node_path17.default.dirname(dir);
|
|
13172
13846
|
if (parent === dir) break;
|
|
13173
13847
|
dir = parent;
|
|
13174
13848
|
}
|