crosscheck-mcp 0.1.10 → 0.1.12
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 +1057 -378
- 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 +1024 -345
- 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
|
|
@@ -1815,6 +1829,11 @@ async function sendOpenAICompatible(args) {
|
|
|
1815
1829
|
body.reasoning_effort = effort;
|
|
1816
1830
|
}
|
|
1817
1831
|
}
|
|
1832
|
+
if (isReasoningModel(args.provider, args.model) && typeof body.max_completion_tokens === "number") {
|
|
1833
|
+
const raw = Number(process.env["CROSSCHECK_OPENAI_REASONING_HEADROOM_TOKENS"]);
|
|
1834
|
+
const headroom = Number.isFinite(raw) && raw >= 0 ? Math.trunc(raw) : 25e3;
|
|
1835
|
+
body.max_completion_tokens += headroom;
|
|
1836
|
+
}
|
|
1818
1837
|
const doFetch = args.fetchImpl ?? globalThis.fetch;
|
|
1819
1838
|
const init = {
|
|
1820
1839
|
method: "POST",
|
|
@@ -1822,30 +1841,34 @@ async function sendOpenAICompatible(args) {
|
|
|
1822
1841
|
body: JSON.stringify(body)
|
|
1823
1842
|
};
|
|
1824
1843
|
if (args.signal) init.signal = args.signal;
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
respLike = await doFetch(url, init);
|
|
1828
|
-
} catch (e) {
|
|
1829
|
-
throw new ProviderError("network", `${args.provider}: fetch failed: ${e.message}`);
|
|
1830
|
-
}
|
|
1831
|
-
const status = respLike.status;
|
|
1832
|
-
if (status >= 200 && status < 300) {
|
|
1833
|
-
let parsed;
|
|
1844
|
+
const attemptOnce = async () => {
|
|
1845
|
+
let respLike;
|
|
1834
1846
|
try {
|
|
1835
|
-
|
|
1847
|
+
respLike = await doFetch(url, init);
|
|
1836
1848
|
} catch (e) {
|
|
1837
|
-
throw new ProviderError("
|
|
1849
|
+
throw new ProviderError("network", `${args.provider}: fetch failed: ${e.message}`);
|
|
1838
1850
|
}
|
|
1839
|
-
const
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
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);
|
|
1849
1872
|
}
|
|
1850
1873
|
|
|
1851
1874
|
// src/providers/registry.ts
|
|
@@ -1940,7 +1963,51 @@ function makeGeminiProvider(model, apiKey, opts) {
|
|
|
1940
1963
|
// src/server.ts
|
|
1941
1964
|
init_cjs_shims();
|
|
1942
1965
|
var import_server2 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
1943
|
-
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
|
+
}
|
|
1944
2011
|
|
|
1945
2012
|
// src/instructions.ts
|
|
1946
2013
|
init_cjs_shims();
|
|
@@ -2007,7 +2074,7 @@ var import_zod = require("zod");
|
|
|
2007
2074
|
|
|
2008
2075
|
// src/tools/audit.ts
|
|
2009
2076
|
init_cjs_shims();
|
|
2010
|
-
var
|
|
2077
|
+
var import_node_fs5 = require("fs");
|
|
2011
2078
|
var import_node_path4 = require("path");
|
|
2012
2079
|
|
|
2013
2080
|
// src/core/structured.ts
|
|
@@ -2070,27 +2137,27 @@ function extractJson(text) {
|
|
|
2070
2137
|
|
|
2071
2138
|
// src/core/json-schema.ts
|
|
2072
2139
|
init_cjs_shims();
|
|
2073
|
-
function validateSchema(value, schema,
|
|
2140
|
+
function validateSchema(value, schema, path14 = "") {
|
|
2074
2141
|
const errs = [];
|
|
2075
2142
|
if ("anyOf" in schema) {
|
|
2076
2143
|
const subs = schema["anyOf"].map(
|
|
2077
|
-
(s) => validateSchema(value, s,
|
|
2144
|
+
(s) => validateSchema(value, s, path14)
|
|
2078
2145
|
);
|
|
2079
2146
|
if (!subs.some((e) => e.length === 0)) {
|
|
2080
|
-
errs.push(`${
|
|
2147
|
+
errs.push(`${path14 || "<root>"}: did not match anyOf`);
|
|
2081
2148
|
}
|
|
2082
2149
|
return errs;
|
|
2083
2150
|
}
|
|
2084
2151
|
if ("oneOf" in schema) {
|
|
2085
|
-
const passed = schema["oneOf"].filter((s) => validateSchema(value, s,
|
|
2152
|
+
const passed = schema["oneOf"].filter((s) => validateSchema(value, s, path14).length === 0).length;
|
|
2086
2153
|
if (passed !== 1) {
|
|
2087
|
-
errs.push(`${
|
|
2154
|
+
errs.push(`${path14 || "<root>"}: matched ${passed} of oneOf, expected 1`);
|
|
2088
2155
|
}
|
|
2089
2156
|
return errs;
|
|
2090
2157
|
}
|
|
2091
2158
|
if ("const" in schema && !deepEqual(value, schema["const"])) {
|
|
2092
2159
|
errs.push(
|
|
2093
|
-
`${
|
|
2160
|
+
`${path14 || "<root>"}: expected const ${pyRepr(schema["const"])}, got ${pyRepr(value)}`
|
|
2094
2161
|
);
|
|
2095
2162
|
return errs;
|
|
2096
2163
|
}
|
|
@@ -2100,7 +2167,7 @@ function validateSchema(value, schema, path12 = "") {
|
|
|
2100
2167
|
const ok = types.some((tt) => matchesType(value, String(tt)));
|
|
2101
2168
|
if (!ok) {
|
|
2102
2169
|
errs.push(
|
|
2103
|
-
`${
|
|
2170
|
+
`${path14 || "<root>"}: expected type ${pyReprType(t)}, got ${pyTypeName(value)}`
|
|
2104
2171
|
);
|
|
2105
2172
|
return errs;
|
|
2106
2173
|
}
|
|
@@ -2109,29 +2176,29 @@ function validateSchema(value, schema, path12 = "") {
|
|
|
2109
2176
|
if ("enum" in schema) {
|
|
2110
2177
|
const en = schema["enum"];
|
|
2111
2178
|
if (!en.some((x) => deepEqual(x, value))) {
|
|
2112
|
-
errs.push(`${
|
|
2179
|
+
errs.push(`${path14 || "<root>"}: value ${pyRepr(value)} not in enum`);
|
|
2113
2180
|
}
|
|
2114
2181
|
}
|
|
2115
2182
|
if ("minLength" in schema && value.length < schema["minLength"]) {
|
|
2116
|
-
errs.push(`${
|
|
2183
|
+
errs.push(`${path14 || "<root>"}: shorter than minLength ${schema["minLength"]}`);
|
|
2117
2184
|
}
|
|
2118
2185
|
}
|
|
2119
2186
|
if (typeof value === "number" && !Number.isNaN(value)) {
|
|
2120
2187
|
if ("minimum" in schema && value < schema["minimum"]) {
|
|
2121
|
-
errs.push(`${
|
|
2188
|
+
errs.push(`${path14 || "<root>"}: ${value} < minimum ${schema["minimum"]}`);
|
|
2122
2189
|
}
|
|
2123
2190
|
if ("maximum" in schema && value > schema["maximum"]) {
|
|
2124
|
-
errs.push(`${
|
|
2191
|
+
errs.push(`${path14 || "<root>"}: ${value} > maximum ${schema["maximum"]}`);
|
|
2125
2192
|
}
|
|
2126
2193
|
}
|
|
2127
2194
|
if (Array.isArray(value)) {
|
|
2128
2195
|
if ("minItems" in schema && value.length < schema["minItems"]) {
|
|
2129
|
-
errs.push(`${
|
|
2196
|
+
errs.push(`${path14 || "<root>"}: fewer items than minItems ${schema["minItems"]}`);
|
|
2130
2197
|
}
|
|
2131
2198
|
const itemSchema = schema["items"];
|
|
2132
2199
|
if (isObj(itemSchema)) {
|
|
2133
2200
|
for (let i = 0; i < value.length; i++) {
|
|
2134
|
-
errs.push(...validateSchema(value[i], itemSchema, `${
|
|
2201
|
+
errs.push(...validateSchema(value[i], itemSchema, `${path14}[${i}]`));
|
|
2135
2202
|
}
|
|
2136
2203
|
}
|
|
2137
2204
|
}
|
|
@@ -2140,19 +2207,19 @@ function validateSchema(value, schema, path12 = "") {
|
|
|
2140
2207
|
const required = schema["required"] ?? [];
|
|
2141
2208
|
for (const r of required) {
|
|
2142
2209
|
if (!(r in value)) {
|
|
2143
|
-
errs.push(`${
|
|
2210
|
+
errs.push(`${path14 || "<root>"}: missing required key ${pyRepr(r)}`);
|
|
2144
2211
|
}
|
|
2145
2212
|
}
|
|
2146
2213
|
if (schema["additionalProperties"] === false) {
|
|
2147
2214
|
for (const k of Object.keys(value)) {
|
|
2148
2215
|
if (!(k in props)) {
|
|
2149
|
-
errs.push(`${
|
|
2216
|
+
errs.push(`${path14 || "<root>"}: unknown key ${pyRepr(k)}`);
|
|
2150
2217
|
}
|
|
2151
2218
|
}
|
|
2152
2219
|
}
|
|
2153
2220
|
for (const [k, v] of Object.entries(value)) {
|
|
2154
2221
|
const ps = props[k];
|
|
2155
|
-
if (ps) errs.push(...validateSchema(v, ps,
|
|
2222
|
+
if (ps) errs.push(...validateSchema(v, ps, path14 ? `${path14}.${k}` : k));
|
|
2156
2223
|
}
|
|
2157
2224
|
}
|
|
2158
2225
|
return errs;
|
|
@@ -2541,6 +2608,21 @@ async function recordSessionCall(storage, sessionId, answers, wallMs, cpuMs, now
|
|
|
2541
2608
|
}
|
|
2542
2609
|
}
|
|
2543
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
|
+
|
|
2544
2626
|
// src/core/structured.ts
|
|
2545
2627
|
async function requestStructured(provider, baseMessages, schema, opts) {
|
|
2546
2628
|
const maxRetries = opts.maxRetries ?? 1;
|
|
@@ -2615,10 +2697,13 @@ ${schemaText}`;
|
|
|
2615
2697
|
async function askOne(provider, messages, opts) {
|
|
2616
2698
|
const startedWall = performance.now();
|
|
2617
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;
|
|
2618
2703
|
try {
|
|
2619
2704
|
const r = await provider.send({
|
|
2620
2705
|
messages,
|
|
2621
|
-
maxTokens
|
|
2706
|
+
maxTokens,
|
|
2622
2707
|
temperature: opts.temperature,
|
|
2623
2708
|
purpose: opts.purpose,
|
|
2624
2709
|
...opts.signal ? { signal: opts.signal } : {},
|
|
@@ -2659,6 +2744,191 @@ async function askOne(provider, messages, opts) {
|
|
|
2659
2744
|
}
|
|
2660
2745
|
}
|
|
2661
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
|
+
|
|
2662
2932
|
// src/core/utils.ts
|
|
2663
2933
|
init_cjs_shims();
|
|
2664
2934
|
function checkSessionBreakers(session, cfg) {
|
|
@@ -2797,7 +3067,7 @@ function maybeDagBreakerEnvelope(dag, toolName, cfg, sessionId) {
|
|
|
2797
3067
|
|
|
2798
3068
|
// src/core/transcripts.ts
|
|
2799
3069
|
init_cjs_shims();
|
|
2800
|
-
var
|
|
3070
|
+
var import_node_fs4 = require("fs");
|
|
2801
3071
|
var import_node_path3 = __toESM(require("path"), 1);
|
|
2802
3072
|
|
|
2803
3073
|
// src/core/redact.ts
|
|
@@ -2876,6 +3146,13 @@ function redactObj(obj, cfg) {
|
|
|
2876
3146
|
}
|
|
2877
3147
|
return obj;
|
|
2878
3148
|
}
|
|
3149
|
+
var globalRedaction = null;
|
|
3150
|
+
function setRedactionConfig(cfg) {
|
|
3151
|
+
globalRedaction = cfg;
|
|
3152
|
+
}
|
|
3153
|
+
function getRedactionConfig() {
|
|
3154
|
+
return globalRedaction ?? void 0;
|
|
3155
|
+
}
|
|
2879
3156
|
|
|
2880
3157
|
// src/core/transcripts.ts
|
|
2881
3158
|
var FTS_INDEX_MAX_CHARS = 64 * 1024;
|
|
@@ -2933,12 +3210,12 @@ async function writeTranscript(opts) {
|
|
|
2933
3210
|
const now = opts.nowMs ? opts.nowMs() : Date.now();
|
|
2934
3211
|
const stampMs = Math.trunc(now);
|
|
2935
3212
|
const fileName = `${stampMs}-${opts.kind}.json`;
|
|
2936
|
-
const redacted = redactObj(opts.payload, opts.redact);
|
|
3213
|
+
const redacted = redactObj(opts.payload, opts.redact ?? getRedactionConfig());
|
|
2937
3214
|
let absPath;
|
|
2938
3215
|
try {
|
|
2939
|
-
(0,
|
|
3216
|
+
(0, import_node_fs4.mkdirSync)(opts.transcriptsDir, { recursive: true });
|
|
2940
3217
|
absPath = import_node_path3.default.resolve(opts.transcriptsDir, fileName);
|
|
2941
|
-
(0,
|
|
3218
|
+
(0, import_node_fs4.writeFileSync)(absPath, JSON.stringify(redacted, null, 2), "utf8");
|
|
2942
3219
|
} catch {
|
|
2943
3220
|
return null;
|
|
2944
3221
|
}
|
|
@@ -3430,7 +3707,18 @@ async function runAudit(args, opts) {
|
|
|
3430
3707
|
for (const r2 of DEFAULT_AUDIT_RUBRICS) rubricItems.push({ ...r2 });
|
|
3431
3708
|
}
|
|
3432
3709
|
const rubricText = rubricItems.map((it) => `- ${it.id} (severity=${it.severity}): ${it.description}`).join("\n");
|
|
3433
|
-
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;
|
|
3434
3722
|
const userMsg = (userConstraints ? `USER CONSTRAINTS:
|
|
3435
3723
|
${userConstraints}
|
|
3436
3724
|
|
|
@@ -3527,6 +3815,7 @@ ${rubricText}`;
|
|
|
3527
3815
|
audit_process_failure: flags.audit_process_failure,
|
|
3528
3816
|
judges_stats: flags.judges_stats
|
|
3529
3817
|
};
|
|
3818
|
+
if (persona.meta.used !== null) coalesceResult["persona"] = persona.meta;
|
|
3530
3819
|
if (!allPass2 && opts.storage && typeof sessionId === "string" && sessionId) {
|
|
3531
3820
|
const marked = await markStaleOnAuditFailure(
|
|
3532
3821
|
opts.storage,
|
|
@@ -3607,6 +3896,7 @@ ${rubricText}`;
|
|
|
3607
3896
|
overall_score: overall,
|
|
3608
3897
|
passed: allPass
|
|
3609
3898
|
};
|
|
3899
|
+
if (persona.meta.used !== null) result["persona"] = persona.meta;
|
|
3610
3900
|
if (errs.length > 0) result["validation_errors"] = errs;
|
|
3611
3901
|
if (!allPass && opts.storage && typeof sessionId === "string" && sessionId) {
|
|
3612
3902
|
const marked = await markStaleOnAuditFailure(
|
|
@@ -3892,7 +4182,7 @@ function loadAuditTextFromSession(sessionId, transcriptsDir) {
|
|
|
3892
4182
|
if (!transcriptsDir) return "";
|
|
3893
4183
|
let entries;
|
|
3894
4184
|
try {
|
|
3895
|
-
entries = (0,
|
|
4185
|
+
entries = (0, import_node_fs5.readdirSync)(transcriptsDir);
|
|
3896
4186
|
} catch {
|
|
3897
4187
|
return "";
|
|
3898
4188
|
}
|
|
@@ -3903,7 +4193,7 @@ function loadAuditTextFromSession(sessionId, transcriptsDir) {
|
|
|
3903
4193
|
const p = (0, import_node_path4.join)(transcriptsDir, name);
|
|
3904
4194
|
let doc2;
|
|
3905
4195
|
try {
|
|
3906
|
-
doc2 = JSON.parse((0,
|
|
4196
|
+
doc2 = JSON.parse((0, import_node_fs5.readFileSync)(p, "utf8"));
|
|
3907
4197
|
} catch {
|
|
3908
4198
|
continue;
|
|
3909
4199
|
}
|
|
@@ -3911,7 +4201,7 @@ function loadAuditTextFromSession(sessionId, transcriptsDir) {
|
|
|
3911
4201
|
if (sid !== sessionId) continue;
|
|
3912
4202
|
let mtime;
|
|
3913
4203
|
try {
|
|
3914
|
-
mtime = (0,
|
|
4204
|
+
mtime = (0, import_node_fs5.statSync)(p).mtimeMs;
|
|
3915
4205
|
} catch {
|
|
3916
4206
|
continue;
|
|
3917
4207
|
}
|
|
@@ -3923,7 +4213,7 @@ function loadAuditTextFromSession(sessionId, transcriptsDir) {
|
|
|
3923
4213
|
if (bestPath === null) return "";
|
|
3924
4214
|
let doc;
|
|
3925
4215
|
try {
|
|
3926
|
-
doc = JSON.parse((0,
|
|
4216
|
+
doc = JSON.parse((0, import_node_fs5.readFileSync)(bestPath, "utf8"));
|
|
3927
4217
|
} catch {
|
|
3928
4218
|
return "";
|
|
3929
4219
|
}
|
|
@@ -3956,7 +4246,7 @@ function boolArg(v, defaultVal) {
|
|
|
3956
4246
|
|
|
3957
4247
|
// src/tools/bench.ts
|
|
3958
4248
|
init_cjs_shims();
|
|
3959
|
-
var
|
|
4249
|
+
var import_node_fs6 = require("fs");
|
|
3960
4250
|
var import_node_path6 = __toESM(require("path"), 1);
|
|
3961
4251
|
|
|
3962
4252
|
// src/tools/confer.ts
|
|
@@ -4493,21 +4783,21 @@ function extractToolCall(text) {
|
|
|
4493
4783
|
return { call, parseError: null };
|
|
4494
4784
|
}
|
|
4495
4785
|
function wrapToolResult(name, content) {
|
|
4496
|
-
let
|
|
4497
|
-
if (typeof content === "string")
|
|
4786
|
+
let str2;
|
|
4787
|
+
if (typeof content === "string") str2 = content;
|
|
4498
4788
|
else {
|
|
4499
4789
|
try {
|
|
4500
|
-
|
|
4790
|
+
str2 = JSON.stringify(content);
|
|
4501
4791
|
} catch {
|
|
4502
|
-
|
|
4792
|
+
str2 = String(content);
|
|
4503
4793
|
}
|
|
4504
4794
|
}
|
|
4505
|
-
if (
|
|
4506
|
-
|
|
4795
|
+
if (str2.length > WORKER_TOOLS_MAX_RESULT_CHARS) {
|
|
4796
|
+
str2 = str2.slice(0, WORKER_TOOLS_MAX_RESULT_CHARS) + "\n... (truncated)";
|
|
4507
4797
|
}
|
|
4508
4798
|
return `<tool_result name="${name}">
|
|
4509
4799
|
<untrusted_input>
|
|
4510
|
-
${neutralizeInjection(
|
|
4800
|
+
${neutralizeInjection(str2)}
|
|
4511
4801
|
</untrusted_input>
|
|
4512
4802
|
</tool_result>`;
|
|
4513
4803
|
}
|
|
@@ -5072,8 +5362,18 @@ async function runConfer(args, opts) {
|
|
|
5072
5362
|
const untrusted = Boolean(args["untrusted_input"]);
|
|
5073
5363
|
const canary = untrusted ? mintCanary() : null;
|
|
5074
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.";
|
|
5075
|
-
const
|
|
5076
|
-
|
|
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;
|
|
5077
5377
|
const messages = [{ role: "system", content: sysMsg }];
|
|
5078
5378
|
if (context) {
|
|
5079
5379
|
const ctxBody = untrusted ? wrapUntrusted(context, canary) : context;
|
|
@@ -5187,6 +5487,7 @@ ${ctxBody}` });
|
|
|
5187
5487
|
question,
|
|
5188
5488
|
answers: sanitized
|
|
5189
5489
|
};
|
|
5490
|
+
if (persona.meta.used !== null) result["persona"] = persona.meta;
|
|
5190
5491
|
if (earlyStopped) {
|
|
5191
5492
|
result["early_stopped"] = true;
|
|
5192
5493
|
result["skipped_providers"] = skippedProviders;
|
|
@@ -5406,9 +5707,19 @@ async function runSandboxed(args) {
|
|
|
5406
5707
|
}
|
|
5407
5708
|
resolve2(result);
|
|
5408
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);
|
|
5409
5720
|
let child;
|
|
5410
5721
|
try {
|
|
5411
|
-
child = (0, import_node_child_process.spawn)(
|
|
5722
|
+
child = (0, import_node_child_process.spawn)(spawnBin, spawnArgv, {
|
|
5412
5723
|
cwd: tempCwd ?? process.cwd(),
|
|
5413
5724
|
env,
|
|
5414
5725
|
// detached: own process-group on Unix (equivalent to setsid).
|
|
@@ -6097,10 +6408,10 @@ async function runBench(args, opts) {
|
|
|
6097
6408
|
return result;
|
|
6098
6409
|
}
|
|
6099
6410
|
function loadGoldens(dirPath, filter) {
|
|
6100
|
-
if (!(0,
|
|
6411
|
+
if (!(0, import_node_fs6.existsSync)(dirPath)) return [];
|
|
6101
6412
|
let entries;
|
|
6102
6413
|
try {
|
|
6103
|
-
entries = (0,
|
|
6414
|
+
entries = (0, import_node_fs6.readdirSync)(dirPath);
|
|
6104
6415
|
} catch {
|
|
6105
6416
|
return [];
|
|
6106
6417
|
}
|
|
@@ -6109,13 +6420,13 @@ function loadGoldens(dirPath, filter) {
|
|
|
6109
6420
|
if (!name.endsWith(".json")) continue;
|
|
6110
6421
|
const p = import_node_path6.default.join(dirPath, name);
|
|
6111
6422
|
try {
|
|
6112
|
-
(0,
|
|
6423
|
+
(0, import_node_fs6.statSync)(p);
|
|
6113
6424
|
} catch {
|
|
6114
6425
|
continue;
|
|
6115
6426
|
}
|
|
6116
6427
|
let doc;
|
|
6117
6428
|
try {
|
|
6118
|
-
doc = JSON.parse((0,
|
|
6429
|
+
doc = JSON.parse((0, import_node_fs6.readFileSync)(p, "utf8"));
|
|
6119
6430
|
} catch {
|
|
6120
6431
|
continue;
|
|
6121
6432
|
}
|
|
@@ -6195,7 +6506,7 @@ function isObj4(v) {
|
|
|
6195
6506
|
// src/tools/config-pin.ts
|
|
6196
6507
|
init_cjs_shims();
|
|
6197
6508
|
var import_node_crypto3 = require("crypto");
|
|
6198
|
-
var
|
|
6509
|
+
var import_node_fs7 = require("fs");
|
|
6199
6510
|
var import_node_path7 = __toESM(require("path"), 1);
|
|
6200
6511
|
var DEFAULT_PIN_PATH = ".crosscheck/config_pins.json";
|
|
6201
6512
|
var DEFAULT_TARGETS = ["crosscheck.config.json", "config/pricing.json"];
|
|
@@ -6268,10 +6579,10 @@ async function runConfigPin(args, opts) {
|
|
|
6268
6579
|
};
|
|
6269
6580
|
}
|
|
6270
6581
|
const absPath = resolvePinPath(opts);
|
|
6271
|
-
const existed = (0,
|
|
6582
|
+
const existed = (0, import_node_fs7.existsSync)(absPath);
|
|
6272
6583
|
if (existed) {
|
|
6273
6584
|
try {
|
|
6274
|
-
(0,
|
|
6585
|
+
(0, import_node_fs7.unlinkSync)(absPath);
|
|
6275
6586
|
} catch (e) {
|
|
6276
6587
|
return errorEnvelope4(
|
|
6277
6588
|
"CONFIG_PIN_CLEAR_FAILED",
|
|
@@ -6306,7 +6617,7 @@ function computeDrift(opts) {
|
|
|
6306
6617
|
missing_files: missingFiles.sort(),
|
|
6307
6618
|
pin_file: relPath(opts, absPath),
|
|
6308
6619
|
pinned_at: Number(doc?.["pinned_at"] ?? 0) || 0,
|
|
6309
|
-
has_pin_file: (0,
|
|
6620
|
+
has_pin_file: (0, import_node_fs7.existsSync)(absPath)
|
|
6310
6621
|
};
|
|
6311
6622
|
}
|
|
6312
6623
|
function shouldBlock(opts, drift) {
|
|
@@ -6331,9 +6642,9 @@ function resolveTargets(opts) {
|
|
|
6331
6642
|
const out = [];
|
|
6332
6643
|
for (const p of list) {
|
|
6333
6644
|
const abs = import_node_path7.default.isAbsolute(p) ? p : import_node_path7.default.join(opts.repoRoot, p);
|
|
6334
|
-
if ((0,
|
|
6645
|
+
if ((0, import_node_fs7.existsSync)(abs)) {
|
|
6335
6646
|
try {
|
|
6336
|
-
const st = (0,
|
|
6647
|
+
const st = (0, import_node_fs7.statSync)(abs);
|
|
6337
6648
|
if (st.isFile()) out.push(abs);
|
|
6338
6649
|
} catch {
|
|
6339
6650
|
}
|
|
@@ -6344,7 +6655,7 @@ function resolveTargets(opts) {
|
|
|
6344
6655
|
function hashFile(abs) {
|
|
6345
6656
|
let raw;
|
|
6346
6657
|
try {
|
|
6347
|
-
raw = (0,
|
|
6658
|
+
raw = (0, import_node_fs7.readFileSync)(abs);
|
|
6348
6659
|
} catch {
|
|
6349
6660
|
return "";
|
|
6350
6661
|
}
|
|
@@ -6364,9 +6675,9 @@ function resolvePinPath(opts) {
|
|
|
6364
6675
|
return import_node_path7.default.isAbsolute(raw) ? raw : import_node_path7.default.join(opts.repoRoot, raw);
|
|
6365
6676
|
}
|
|
6366
6677
|
function loadDoc(absPath) {
|
|
6367
|
-
if (!(0,
|
|
6678
|
+
if (!(0, import_node_fs7.existsSync)(absPath)) return null;
|
|
6368
6679
|
try {
|
|
6369
|
-
const txt = (0,
|
|
6680
|
+
const txt = (0, import_node_fs7.readFileSync)(absPath, "utf8");
|
|
6370
6681
|
const doc = JSON.parse(txt);
|
|
6371
6682
|
return doc && typeof doc === "object" && !Array.isArray(doc) ? doc : null;
|
|
6372
6683
|
} catch {
|
|
@@ -6375,11 +6686,11 @@ function loadDoc(absPath) {
|
|
|
6375
6686
|
}
|
|
6376
6687
|
function saveDoc(opts, doc) {
|
|
6377
6688
|
const absPath = resolvePinPath(opts);
|
|
6378
|
-
(0,
|
|
6689
|
+
(0, import_node_fs7.mkdirSync)(import_node_path7.default.dirname(absPath), { recursive: true });
|
|
6379
6690
|
const tmp = absPath + ".tmp";
|
|
6380
|
-
(0,
|
|
6691
|
+
(0, import_node_fs7.writeFileSync)(tmp, JSON.stringify(doc, null, 2), "utf8");
|
|
6381
6692
|
try {
|
|
6382
|
-
(0,
|
|
6693
|
+
(0, import_node_fs7.unlinkSync)(absPath);
|
|
6383
6694
|
} catch {
|
|
6384
6695
|
}
|
|
6385
6696
|
const { renameSync: renameSync2 } = require("fs");
|
|
@@ -6428,7 +6739,7 @@ function pyRepr2(v) {
|
|
|
6428
6739
|
// src/tools/create.ts
|
|
6429
6740
|
init_cjs_shims();
|
|
6430
6741
|
var import_node_crypto6 = require("crypto");
|
|
6431
|
-
var
|
|
6742
|
+
var import_node_fs10 = require("fs");
|
|
6432
6743
|
var import_node_path10 = __toESM(require("path"), 1);
|
|
6433
6744
|
|
|
6434
6745
|
// src/tools/orchestrate.ts
|
|
@@ -6531,7 +6842,7 @@ function isDeadModelError(err) {
|
|
|
6531
6842
|
// src/core/node-cache.ts
|
|
6532
6843
|
init_cjs_shims();
|
|
6533
6844
|
var import_node_crypto4 = require("crypto");
|
|
6534
|
-
var
|
|
6845
|
+
var import_node_fs8 = require("fs");
|
|
6535
6846
|
var import_node_path8 = require("path");
|
|
6536
6847
|
var NODE_CACHE_KEY_VERSION = "v2";
|
|
6537
6848
|
var CANON_PATTERNS = [
|
|
@@ -6596,18 +6907,18 @@ function cachePath(cfg, repoRoot, key) {
|
|
|
6596
6907
|
function getNodeCache(cfg, repoRoot, key, nowMs) {
|
|
6597
6908
|
if (!cfg || cfg.enabled === false) return null;
|
|
6598
6909
|
const p = cachePath(cfg, repoRoot, key);
|
|
6599
|
-
if (!(0,
|
|
6910
|
+
if (!(0, import_node_fs8.existsSync)(p)) return null;
|
|
6600
6911
|
const ttlS = cfg.ttl_seconds ?? 604800;
|
|
6601
6912
|
try {
|
|
6602
|
-
const st = (0,
|
|
6913
|
+
const st = (0, import_node_fs8.statSync)(p);
|
|
6603
6914
|
if (ttlS > 0) {
|
|
6604
6915
|
const now = nowMs ?? Date.now();
|
|
6605
6916
|
if ((now - st.mtimeMs) / 1e3 > ttlS) return null;
|
|
6606
6917
|
}
|
|
6607
|
-
const data = JSON.parse((0,
|
|
6918
|
+
const data = JSON.parse((0, import_node_fs8.readFileSync)(p, "utf8"));
|
|
6608
6919
|
try {
|
|
6609
6920
|
const t = (nowMs ?? Date.now()) / 1e3;
|
|
6610
|
-
(0,
|
|
6921
|
+
(0, import_node_fs8.utimesSync)(p, t, t);
|
|
6611
6922
|
} catch {
|
|
6612
6923
|
}
|
|
6613
6924
|
return data;
|
|
@@ -6615,11 +6926,11 @@ function getNodeCache(cfg, repoRoot, key, nowMs) {
|
|
|
6615
6926
|
return null;
|
|
6616
6927
|
}
|
|
6617
6928
|
}
|
|
6618
|
-
function atomicWriteJson(
|
|
6619
|
-
(0,
|
|
6620
|
-
const tmp = `${
|
|
6621
|
-
(0,
|
|
6622
|
-
(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);
|
|
6623
6934
|
}
|
|
6624
6935
|
function putNodeCache(cfg, repoRoot, key, value) {
|
|
6625
6936
|
if (!cfg || cfg.enabled === false) return;
|
|
@@ -6630,23 +6941,23 @@ function putNodeCache(cfg, repoRoot, key, value) {
|
|
|
6630
6941
|
const maxBytes = cfg.max_cache_bytes ?? 100 * 1024 * 1024;
|
|
6631
6942
|
if (maxEntries <= 0 && maxBytes <= 0) return;
|
|
6632
6943
|
const base = resolveCacheDir(cfg, repoRoot);
|
|
6633
|
-
if (!(0,
|
|
6944
|
+
if (!(0, import_node_fs8.existsSync)(base)) return;
|
|
6634
6945
|
const entries = [];
|
|
6635
6946
|
let totalBytes = 0;
|
|
6636
|
-
for (const shard of (0,
|
|
6947
|
+
for (const shard of (0, import_node_fs8.readdirSync)(base)) {
|
|
6637
6948
|
const shardDir = (0, import_node_path8.join)(base, shard);
|
|
6638
6949
|
let stShard;
|
|
6639
6950
|
try {
|
|
6640
|
-
stShard = (0,
|
|
6951
|
+
stShard = (0, import_node_fs8.statSync)(shardDir);
|
|
6641
6952
|
} catch {
|
|
6642
6953
|
continue;
|
|
6643
6954
|
}
|
|
6644
6955
|
if (!stShard.isDirectory()) continue;
|
|
6645
|
-
for (const f of (0,
|
|
6956
|
+
for (const f of (0, import_node_fs8.readdirSync)(shardDir)) {
|
|
6646
6957
|
if (!f.endsWith(".json")) continue;
|
|
6647
6958
|
const fp = (0, import_node_path8.join)(shardDir, f);
|
|
6648
6959
|
try {
|
|
6649
|
-
const st = (0,
|
|
6960
|
+
const st = (0, import_node_fs8.statSync)(fp);
|
|
6650
6961
|
entries.push({ mtime: st.mtimeMs, size: st.size, path: fp });
|
|
6651
6962
|
totalBytes += st.size;
|
|
6652
6963
|
} catch {
|
|
@@ -6660,7 +6971,7 @@ function putNodeCache(cfg, repoRoot, key, value) {
|
|
|
6660
6971
|
while (entries.length > 0 && (maxEntries > 0 && entries.length > maxEntries || maxBytes > 0 && totalBytes > maxBytes)) {
|
|
6661
6972
|
const oldest = entries.shift();
|
|
6662
6973
|
try {
|
|
6663
|
-
(0,
|
|
6974
|
+
(0, import_node_fs8.unlinkSync)(oldest.path);
|
|
6664
6975
|
totalBytes -= oldest.size;
|
|
6665
6976
|
} catch {
|
|
6666
6977
|
}
|
|
@@ -7222,8 +7533,19 @@ NODE OUTPUTS:
|
|
|
7222
7533
|
${recombineLines.join("\n\n")}
|
|
7223
7534
|
|
|
7224
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.";
|
|
7225
7545
|
const recMsgs = [
|
|
7226
|
-
{ role: "system", content:
|
|
7546
|
+
{ role: "system", content: persona.block ? `${persona.block}
|
|
7547
|
+
|
|
7548
|
+
${recombineSys}` : recombineSys },
|
|
7227
7549
|
{ role: "user", content: recombinePrompt }
|
|
7228
7550
|
];
|
|
7229
7551
|
let synthProvider = moderator;
|
|
@@ -7296,6 +7618,7 @@ Synthesize the node outputs into a single coherent deliverable. Preserve any [MI
|
|
|
7296
7618
|
fail_fast: failFast,
|
|
7297
7619
|
cheap_mode: cheapMode
|
|
7298
7620
|
};
|
|
7621
|
+
if (personaMeta.used !== null) result["persona"] = personaMeta;
|
|
7299
7622
|
if (synthModel) result["synth_model"] = synthModel;
|
|
7300
7623
|
if (synthErr) result["synth_error"] = synthErr;
|
|
7301
7624
|
if (plannerErrors.length > 0) result["planner_errors"] = plannerErrors;
|
|
@@ -7687,7 +8010,7 @@ function pyRound6(x) {
|
|
|
7687
8010
|
// src/tools/fetch.ts
|
|
7688
8011
|
init_cjs_shims();
|
|
7689
8012
|
var import_node_crypto5 = require("crypto");
|
|
7690
|
-
var
|
|
8013
|
+
var import_node_fs9 = require("fs");
|
|
7691
8014
|
var import_node_path9 = __toESM(require("path"), 1);
|
|
7692
8015
|
var DEFAULT_CONFIG = {
|
|
7693
8016
|
enabled: true,
|
|
@@ -7769,9 +8092,9 @@ async function runFetch(args, opts) {
|
|
|
7769
8092
|
const evDir = resolveEvidenceDir(cfg.evidence_dir, opts.repoRoot);
|
|
7770
8093
|
const urlHash16 = sha256Hex2(url).slice(0, 16);
|
|
7771
8094
|
const metaPath = import_node_path9.default.join(evDir, `by-url-${urlHash16}.json`);
|
|
7772
|
-
if ((0,
|
|
8095
|
+
if ((0, import_node_fs9.existsSync)(metaPath) && !force) {
|
|
7773
8096
|
try {
|
|
7774
|
-
const meta2 = JSON.parse((0,
|
|
8097
|
+
const meta2 = JSON.parse((0, import_node_fs9.readFileSync)(metaPath, "utf8"));
|
|
7775
8098
|
return {
|
|
7776
8099
|
...base,
|
|
7777
8100
|
accepted: true,
|
|
@@ -7811,15 +8134,41 @@ async function runFetch(args, opts) {
|
|
|
7811
8134
|
}
|
|
7812
8135
|
let data;
|
|
7813
8136
|
try {
|
|
7814
|
-
const
|
|
7815
|
-
if (
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
accepted: false,
|
|
7819
|
-
|
|
7820
|
-
|
|
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
|
+
}
|
|
7821
8171
|
}
|
|
7822
|
-
data = new Uint8Array(buf);
|
|
7823
8172
|
} catch (e) {
|
|
7824
8173
|
return {
|
|
7825
8174
|
...base,
|
|
@@ -7831,8 +8180,8 @@ async function runFetch(args, opts) {
|
|
|
7831
8180
|
const status = response.status;
|
|
7832
8181
|
const sha = sha256HexBytes(data);
|
|
7833
8182
|
const bodyPath = import_node_path9.default.join(evDir, `${sha}.bin`);
|
|
7834
|
-
(0,
|
|
7835
|
-
(0,
|
|
8183
|
+
(0, import_node_fs9.mkdirSync)(evDir, { recursive: true });
|
|
8184
|
+
(0, import_node_fs9.writeFileSync)(bodyPath, data);
|
|
7836
8185
|
const relBody = makeRelative2(bodyPath, opts.repoRoot);
|
|
7837
8186
|
const now = opts.nowEpochSeconds ? opts.nowEpochSeconds() : Math.floor(Date.now() / 1e3);
|
|
7838
8187
|
const meta = {
|
|
@@ -7844,7 +8193,7 @@ async function runFetch(args, opts) {
|
|
|
7844
8193
|
status,
|
|
7845
8194
|
fetched_at: now
|
|
7846
8195
|
};
|
|
7847
|
-
(0,
|
|
8196
|
+
(0, import_node_fs9.writeFileSync)(metaPath, JSON.stringify(meta, null, 2));
|
|
7848
8197
|
if (sessionId && host && opts.storage) {
|
|
7849
8198
|
try {
|
|
7850
8199
|
await opts.storage.recordFetchEgress(sessionId, host, data.byteLength, now);
|
|
@@ -8127,8 +8476,8 @@ ${documentsPayload}`
|
|
|
8127
8476
|
if (targetPath && !dryRun && typeof orchestration["final"] === "string" && orchestration["final"] && !blockWriteStatuses.has(status)) {
|
|
8128
8477
|
try {
|
|
8129
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);
|
|
8130
|
-
(0,
|
|
8131
|
-
(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");
|
|
8132
8481
|
artifacts.push({ path: targetPath, bytes: orchestration["final"].length });
|
|
8133
8482
|
if (status === "audit_failed" || status === "audit_inconclusive") {
|
|
8134
8483
|
warnings.push(
|
|
@@ -8198,7 +8547,7 @@ async function ingestDocuments(documents, sessionId, opts) {
|
|
|
8198
8547
|
if (typeof evidencePath === "string" && evidencePath) {
|
|
8199
8548
|
const abs = import_node_path10.default.isAbsolute(evidencePath) ? evidencePath : opts.repoRoot ? import_node_path10.default.join(opts.repoRoot, evidencePath) : evidencePath;
|
|
8200
8549
|
try {
|
|
8201
|
-
text = (0,
|
|
8550
|
+
text = (0, import_node_fs10.readFileSync)(abs, "utf8");
|
|
8202
8551
|
} catch {
|
|
8203
8552
|
text = "";
|
|
8204
8553
|
}
|
|
@@ -8212,7 +8561,7 @@ async function ingestDocuments(documents, sessionId, opts) {
|
|
|
8212
8561
|
}, text));
|
|
8213
8562
|
} else {
|
|
8214
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);
|
|
8215
|
-
if (!(0,
|
|
8564
|
+
if (!(0, import_node_fs10.existsSync)(abs)) {
|
|
8216
8565
|
out.push({
|
|
8217
8566
|
source: ref,
|
|
8218
8567
|
type: "file",
|
|
@@ -8224,7 +8573,7 @@ async function ingestDocuments(documents, sessionId, opts) {
|
|
|
8224
8573
|
}
|
|
8225
8574
|
let text = "";
|
|
8226
8575
|
try {
|
|
8227
|
-
text = (0,
|
|
8576
|
+
text = (0, import_node_fs10.readFileSync)(abs, "utf8");
|
|
8228
8577
|
} catch (e) {
|
|
8229
8578
|
out.push({
|
|
8230
8579
|
source: ref,
|
|
@@ -9239,15 +9588,27 @@ ${prior}` });
|
|
|
9239
9588
|
let synthesis = null;
|
|
9240
9589
|
let synthesisStructured = null;
|
|
9241
9590
|
let synthesisErrors = [];
|
|
9591
|
+
let personaMeta = null;
|
|
9242
9592
|
if (moderator) {
|
|
9243
9593
|
const condensed = transcript.map(
|
|
9244
9594
|
(e) => `[${e.provider} \u2014 round ${e.round}]
|
|
9245
9595
|
${e.response ?? "(error)"}`
|
|
9246
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.";
|
|
9247
9606
|
const synthMessages = [
|
|
9248
9607
|
{
|
|
9249
9608
|
role: "system",
|
|
9250
|
-
content:
|
|
9609
|
+
content: persona.block ? `${persona.block}
|
|
9610
|
+
|
|
9611
|
+
${synthSys}` : synthSys
|
|
9251
9612
|
},
|
|
9252
9613
|
{
|
|
9253
9614
|
role: "user",
|
|
@@ -9299,6 +9660,7 @@ ${condensed}`
|
|
|
9299
9660
|
transcript,
|
|
9300
9661
|
synthesis
|
|
9301
9662
|
};
|
|
9663
|
+
if (personaMeta && personaMeta.used !== null) result["persona"] = personaMeta;
|
|
9302
9664
|
if (synthesisStructured !== null) {
|
|
9303
9665
|
result["synthesis_structured"] = synthesisStructured;
|
|
9304
9666
|
}
|
|
@@ -9613,7 +9975,7 @@ function pyListRepr2(xs) {
|
|
|
9613
9975
|
|
|
9614
9976
|
// src/tools/explain.ts
|
|
9615
9977
|
init_cjs_shims();
|
|
9616
|
-
var
|
|
9978
|
+
var import_node_fs11 = require("fs");
|
|
9617
9979
|
var import_node_path11 = __toESM(require("path"), 1);
|
|
9618
9980
|
async function runExplain(args, opts) {
|
|
9619
9981
|
const sessionId = args["session_id"];
|
|
@@ -9715,10 +10077,10 @@ async function runExplain(args, opts) {
|
|
|
9715
10077
|
return result;
|
|
9716
10078
|
}
|
|
9717
10079
|
function loadTranscriptsForSession(dir, sessionId) {
|
|
9718
|
-
if (!(0,
|
|
10080
|
+
if (!(0, import_node_fs11.existsSync)(dir)) return [];
|
|
9719
10081
|
let names;
|
|
9720
10082
|
try {
|
|
9721
|
-
names = (0,
|
|
10083
|
+
names = (0, import_node_fs11.readdirSync)(dir);
|
|
9722
10084
|
} catch {
|
|
9723
10085
|
return [];
|
|
9724
10086
|
}
|
|
@@ -9728,13 +10090,13 @@ function loadTranscriptsForSession(dir, sessionId) {
|
|
|
9728
10090
|
const p = import_node_path11.default.join(dir, name);
|
|
9729
10091
|
let stat;
|
|
9730
10092
|
try {
|
|
9731
|
-
stat = (0,
|
|
10093
|
+
stat = (0, import_node_fs11.statSync)(p);
|
|
9732
10094
|
} catch {
|
|
9733
10095
|
continue;
|
|
9734
10096
|
}
|
|
9735
10097
|
let doc;
|
|
9736
10098
|
try {
|
|
9737
|
-
doc = JSON.parse((0,
|
|
10099
|
+
doc = JSON.parse((0, import_node_fs11.readFileSync)(p, "utf8"));
|
|
9738
10100
|
} catch {
|
|
9739
10101
|
continue;
|
|
9740
10102
|
}
|
|
@@ -10590,7 +10952,7 @@ function toStringArray3(v) {
|
|
|
10590
10952
|
|
|
10591
10953
|
// src/tools/scoreboard.ts
|
|
10592
10954
|
init_cjs_shims();
|
|
10593
|
-
var
|
|
10955
|
+
var import_node_fs12 = require("fs");
|
|
10594
10956
|
async function runScoreboard(args, opts) {
|
|
10595
10957
|
if (!opts.storage) {
|
|
10596
10958
|
if (opts.bridge && opts.bridge.toolNames.has("scoreboard")) {
|
|
@@ -10673,9 +11035,9 @@ async function runScoreboard(args, opts) {
|
|
|
10673
11035
|
}
|
|
10674
11036
|
usageTotals.cost_usd = Number(usageTotals.cost_usd.toFixed(8));
|
|
10675
11037
|
let recentEvents = [];
|
|
10676
|
-
if (recentLimit > 0 && opts.eventsPath && (0,
|
|
11038
|
+
if (recentLimit > 0 && opts.eventsPath && (0, import_node_fs12.existsSync)(opts.eventsPath)) {
|
|
10677
11039
|
try {
|
|
10678
|
-
const lines = (0,
|
|
11040
|
+
const lines = (0, import_node_fs12.readFileSync)(opts.eventsPath, "utf8").split("\n").filter((l) => l.length > 0);
|
|
10679
11041
|
const tail = lines.slice(-recentLimit);
|
|
10680
11042
|
for (const ln of tail) {
|
|
10681
11043
|
try {
|
|
@@ -10924,6 +11286,122 @@ function pyRepr6(v) {
|
|
|
10924
11286
|
// src/tools/solve.ts
|
|
10925
11287
|
init_cjs_shims();
|
|
10926
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
|
|
10927
11405
|
async function runSolve(args, opts) {
|
|
10928
11406
|
const problem = typeof args["problem"] === "string" ? args["problem"] : String(args["problem"] ?? "");
|
|
10929
11407
|
if (!problem) {
|
|
@@ -11065,7 +11543,23 @@ Re-emit the FULL solution. No commentary.`
|
|
|
11065
11543
|
const targetPath = args["target_path"];
|
|
11066
11544
|
if (typeof targetPath === "string" && targetPath) {
|
|
11067
11545
|
result["target_path"] = targetPath;
|
|
11068
|
-
|
|
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
|
+
}
|
|
11069
11563
|
}
|
|
11070
11564
|
if (unknownNames.length > 0) result["skipped_unknown_providers"] = unknownNames;
|
|
11071
11565
|
if (blocked.length > 0) result["blocked_by_allowlist"] = blocked;
|
|
@@ -11151,6 +11645,8 @@ async function verifyProposalShell(verifier, proposal) {
|
|
|
11151
11645
|
}
|
|
11152
11646
|
const argv = cmd.map((x) => String(x));
|
|
11153
11647
|
const timeoutS = numberArg2(verifier["timeout_s"], 10);
|
|
11648
|
+
const memoryMb = numberArg2(verifier["memory_mb"], 0);
|
|
11649
|
+
const cpuSeconds = numberArg2(verifier["cpu_seconds"], 0);
|
|
11154
11650
|
const expectExit = pyIntCoerce2(verifier["expect_exit_code"], 0);
|
|
11155
11651
|
const expectContains = typeof verifier["expect_stdout_contains"] === "string" ? verifier["expect_stdout_contains"] : null;
|
|
11156
11652
|
const expectRegex = typeof verifier["expect_stdout_regex"] === "string" ? verifier["expect_stdout_regex"] : null;
|
|
@@ -11160,7 +11656,9 @@ async function verifyProposalShell(verifier, proposal) {
|
|
|
11160
11656
|
input: proposal,
|
|
11161
11657
|
// proposal piped to stdin
|
|
11162
11658
|
stdoutTailBytes: 2048,
|
|
11163
|
-
stderrTailBytes: 2048
|
|
11659
|
+
stderrTailBytes: 2048,
|
|
11660
|
+
...memoryMb > 0 ? { memoryMb } : {},
|
|
11661
|
+
...cpuSeconds > 0 ? { cpuSeconds } : {}
|
|
11164
11662
|
});
|
|
11165
11663
|
if (r.status === "timeout") {
|
|
11166
11664
|
return {
|
|
@@ -11330,24 +11828,24 @@ function pyRepr7(v) {
|
|
|
11330
11828
|
// src/tools/update-crosscheck.ts
|
|
11331
11829
|
init_cjs_shims();
|
|
11332
11830
|
var import_node_child_process2 = require("child_process");
|
|
11333
|
-
var
|
|
11334
|
-
var
|
|
11831
|
+
var import_node_fs15 = require("fs");
|
|
11832
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
11335
11833
|
|
|
11336
11834
|
// src/core/update-notify.ts
|
|
11337
11835
|
init_cjs_shims();
|
|
11338
|
-
var
|
|
11836
|
+
var import_node_fs14 = require("fs");
|
|
11339
11837
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
11340
|
-
var
|
|
11838
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
11341
11839
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
11342
11840
|
var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
|
|
11343
11841
|
var DEFAULT_PACKAGE = "crosscheck-cli";
|
|
11344
11842
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
11345
11843
|
function engineVersion() {
|
|
11346
|
-
return true ? "0.1.
|
|
11844
|
+
return true ? "0.1.12" : "0.0.0-dev";
|
|
11347
11845
|
}
|
|
11348
11846
|
function defaultUpdateCachePath() {
|
|
11349
|
-
const base = process.env["CROSSCHECK_DATA_DIR"] ||
|
|
11350
|
-
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");
|
|
11351
11849
|
}
|
|
11352
11850
|
function isNewer(latest, current) {
|
|
11353
11851
|
const a = parseTriple(latest);
|
|
@@ -11377,8 +11875,8 @@ then reconnect the crosscheck MCP server (run \`/mcp\` in Claude Code, or restar
|
|
|
11377
11875
|
}
|
|
11378
11876
|
function readCache(cachePath2) {
|
|
11379
11877
|
try {
|
|
11380
|
-
if (!(0,
|
|
11381
|
-
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"));
|
|
11382
11880
|
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
11383
11881
|
return data;
|
|
11384
11882
|
}
|
|
@@ -11389,8 +11887,8 @@ function readCache(cachePath2) {
|
|
|
11389
11887
|
}
|
|
11390
11888
|
function writeCache(cachePath2, record2) {
|
|
11391
11889
|
try {
|
|
11392
|
-
(0,
|
|
11393
|
-
(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");
|
|
11394
11892
|
} catch {
|
|
11395
11893
|
}
|
|
11396
11894
|
}
|
|
@@ -11443,7 +11941,7 @@ async function runUpdateCrosscheck(args, opts) {
|
|
|
11443
11941
|
const gitRun = opts.gitRun ?? defaultGitRun(opts.repoRoot);
|
|
11444
11942
|
const fetchFn = opts.httpFetch ?? globalThis.fetch;
|
|
11445
11943
|
const now = opts.nowEpochSeconds ? opts.nowEpochSeconds() : Math.floor(Date.now() / 1e3);
|
|
11446
|
-
const cachePath2 = opts.cachePath ??
|
|
11944
|
+
const cachePath2 = opts.cachePath ?? import_node_path14.default.join(opts.repoRoot, ".crosscheck", "update_check.json");
|
|
11447
11945
|
const local = readLocalSha(gitRun);
|
|
11448
11946
|
if (!local) {
|
|
11449
11947
|
return {
|
|
@@ -11615,8 +12113,8 @@ function gitRelationship(gitRun, local, remote) {
|
|
|
11615
12113
|
}
|
|
11616
12114
|
function writeUpdateCache(p, payload) {
|
|
11617
12115
|
try {
|
|
11618
|
-
(0,
|
|
11619
|
-
(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");
|
|
11620
12118
|
} catch {
|
|
11621
12119
|
}
|
|
11622
12120
|
}
|
|
@@ -12631,8 +13129,8 @@ function pingTool() {
|
|
|
12631
13129
|
|
|
12632
13130
|
// src/core/wrappers.ts
|
|
12633
13131
|
init_cjs_shims();
|
|
12634
|
-
var
|
|
12635
|
-
var
|
|
13132
|
+
var import_node_fs16 = require("fs");
|
|
13133
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
12636
13134
|
function configPinGate(toolName, opts) {
|
|
12637
13135
|
if (toolName === "config_pin") return null;
|
|
12638
13136
|
if (!opts || !opts.repoRoot) return null;
|
|
@@ -12664,8 +13162,8 @@ function configPinGate(toolName, opts) {
|
|
|
12664
13162
|
var cachedUpdateNotice = null;
|
|
12665
13163
|
function readUpdateCache(cachePath2) {
|
|
12666
13164
|
try {
|
|
12667
|
-
if (!(0,
|
|
12668
|
-
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"));
|
|
12669
13167
|
if (data && typeof data === "object" && !Array.isArray(data)) return data;
|
|
12670
13168
|
return null;
|
|
12671
13169
|
} catch {
|
|
@@ -12677,7 +13175,7 @@ function attachUpdateNotice(result, toolName, opts) {
|
|
|
12677
13175
|
if (toolName === "update_crosscheck") return;
|
|
12678
13176
|
if ("update_notice" in result) return;
|
|
12679
13177
|
if (cachedUpdateNotice === null) {
|
|
12680
|
-
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());
|
|
12681
13179
|
const cached = readUpdateCache(cachePath2);
|
|
12682
13180
|
if (cached && cached.update_available && cached.notice && typeof cached.notice === "object") {
|
|
12683
13181
|
cachedUpdateNotice = cached.notice;
|
|
@@ -12875,7 +13373,7 @@ async function flush() {
|
|
|
12875
13373
|
|
|
12876
13374
|
// src/server.ts
|
|
12877
13375
|
var SERVER_NAME = "crosscheck-agent";
|
|
12878
|
-
var SERVER_VERSION = true ? "0.1.
|
|
13376
|
+
var SERVER_VERSION = true ? "0.1.12" : "0.0.0-dev";
|
|
12879
13377
|
function extractRunSummaryText(out) {
|
|
12880
13378
|
if (out === null || typeof out !== "object" || Array.isArray(out)) return void 0;
|
|
12881
13379
|
const rs = out["run_summary"];
|
|
@@ -12897,14 +13395,14 @@ function createServer(opts = {}) {
|
|
|
12897
13395
|
}
|
|
12898
13396
|
);
|
|
12899
13397
|
const tools = buildToolRegistry(opts);
|
|
12900
|
-
server.setRequestHandler(
|
|
13398
|
+
server.setRequestHandler(import_types8.ListToolsRequestSchema, async () => ({
|
|
12901
13399
|
tools: Array.from(tools.values()).map((t) => ({
|
|
12902
13400
|
name: t.name,
|
|
12903
13401
|
description: t.description,
|
|
12904
13402
|
inputSchema: t.inputSchema
|
|
12905
13403
|
}))
|
|
12906
13404
|
}));
|
|
12907
|
-
server.setRequestHandler(
|
|
13405
|
+
server.setRequestHandler(import_types8.CallToolRequestSchema, async (req) => {
|
|
12908
13406
|
const name = req.params.name;
|
|
12909
13407
|
const tool = tools.get(name);
|
|
12910
13408
|
if (!tool) {
|
|
@@ -13028,6 +13526,10 @@ function buildToolRegistry(opts) {
|
|
|
13028
13526
|
if (opts.nodeCache) registerOpts.nodeCache = opts.nodeCache;
|
|
13029
13527
|
if (opts.configPinning) registerOpts.configPinning = opts.configPinning;
|
|
13030
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;
|
|
13031
13533
|
const tools = registerCoreTools(registerOpts);
|
|
13032
13534
|
if (!opts.bridge) return tools;
|
|
13033
13535
|
const proxies = buildPythonProxies(opts.bridge);
|
|
@@ -13037,29 +13539,207 @@ function buildToolRegistry(opts) {
|
|
|
13037
13539
|
return tools;
|
|
13038
13540
|
}
|
|
13039
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
|
+
|
|
13040
13714
|
// src/entrypoints/node-stdio.ts
|
|
13041
13715
|
var SHUTDOWN_TIMEOUT_MS = 2e3;
|
|
13042
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
|
+
}
|
|
13043
13733
|
setEventEmitter(buildDefaultEmitter(process.env));
|
|
13044
|
-
let bridge;
|
|
13045
13734
|
if (process.env["CROSSCHECK_BRIDGE_PYTHON"] === "1") {
|
|
13046
|
-
bridge = await spawnPythonBridge({
|
|
13047
|
-
...process.env["CROSSCHECK_PYTHON_PATH"] ? { pythonPath: process.env["CROSSCHECK_PYTHON_PATH"] } : {},
|
|
13048
|
-
...process.env["CROSSCHECK_PYTHON_SERVER"] ? { serverPath: process.env["CROSSCHECK_PYTHON_SERVER"] } : {}
|
|
13049
|
-
});
|
|
13050
13735
|
process.stderr.write(
|
|
13051
|
-
|
|
13052
|
-
`
|
|
13053
|
-
);
|
|
13054
|
-
} else {
|
|
13055
|
-
process.stderr.write(
|
|
13056
|
-
"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"
|
|
13057
13737
|
);
|
|
13058
13738
|
}
|
|
13059
|
-
installShutdownHandlers(
|
|
13739
|
+
installShutdownHandlers();
|
|
13060
13740
|
const bundledPricing = (() => {
|
|
13061
13741
|
try {
|
|
13062
|
-
return
|
|
13742
|
+
return import_node_path17.default.join(import_node_path17.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl)), "pricing.json");
|
|
13063
13743
|
} catch {
|
|
13064
13744
|
return void 0;
|
|
13065
13745
|
}
|
|
@@ -13068,7 +13748,7 @@ async function main() {
|
|
|
13068
13748
|
process.env["CROSSCHECK_PRICING_PATH"],
|
|
13069
13749
|
resolveRepoFile("config/pricing.json"),
|
|
13070
13750
|
bundledPricing
|
|
13071
|
-
].find((p) => p && (0,
|
|
13751
|
+
].find((p) => p && (0, import_node_fs18.existsSync)(p));
|
|
13072
13752
|
const pricing = pricingPath ? loadPricing(pricingPath) : {};
|
|
13073
13753
|
const providers = buildProviders({ env: process.env, pricing });
|
|
13074
13754
|
if (Object.keys(providers).length > 0) {
|
|
@@ -13082,7 +13762,7 @@ async function main() {
|
|
|
13082
13762
|
const dbPath = process.env["CROSSCHECK_DB_PATH"] ?? resolveRepoFile(".crosscheck/db.sqlite");
|
|
13083
13763
|
if (dbPath) {
|
|
13084
13764
|
try {
|
|
13085
|
-
(0,
|
|
13765
|
+
(0, import_node_fs18.mkdirSync)(import_node_path17.default.dirname(dbPath), { recursive: true });
|
|
13086
13766
|
const { openBetterSqliteStorage: openBetterSqliteStorage2 } = await Promise.resolve().then(() => (init_better_sqlite3(), better_sqlite3_exports));
|
|
13087
13767
|
storage = openBetterSqliteStorage2({ path: dbPath });
|
|
13088
13768
|
await storage.migrate();
|
|
@@ -13099,33 +13779,32 @@ async function main() {
|
|
|
13099
13779
|
}
|
|
13100
13780
|
}
|
|
13101
13781
|
}
|
|
13102
|
-
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");
|
|
13103
13783
|
const repoRoot = process.env["CROSSCHECK_REPO_ROOT"] ?? findGitRoot(__dirname);
|
|
13104
|
-
const transport = new
|
|
13784
|
+
const transport = new import_stdio.StdioServerTransport();
|
|
13105
13785
|
const serverOpts = { providers };
|
|
13106
|
-
if (bridge) serverOpts.bridge = bridge;
|
|
13107
13786
|
if (storage) serverOpts.storage = storage;
|
|
13108
13787
|
if (transcriptsDir) serverOpts.transcriptsDir = transcriptsDir;
|
|
13109
13788
|
if (repoRoot) serverOpts.repoRoot = repoRoot;
|
|
13110
13789
|
if (pricing && Object.keys(pricing).length > 0) {
|
|
13111
13790
|
serverOpts.pricing = pricing;
|
|
13112
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;
|
|
13113
13801
|
await connectAndServe(transport, serverOpts);
|
|
13114
13802
|
}
|
|
13115
|
-
function installShutdownHandlers(
|
|
13803
|
+
function installShutdownHandlers() {
|
|
13116
13804
|
let shuttingDown = false;
|
|
13117
13805
|
const shutdown = async (reason) => {
|
|
13118
13806
|
if (shuttingDown) return;
|
|
13119
13807
|
shuttingDown = true;
|
|
13120
|
-
if (bridge) {
|
|
13121
|
-
await Promise.race([
|
|
13122
|
-
bridge.close(),
|
|
13123
|
-
new Promise(
|
|
13124
|
-
(resolve2) => setTimeout(resolve2, SHUTDOWN_TIMEOUT_MS)
|
|
13125
|
-
)
|
|
13126
|
-
]).catch(() => {
|
|
13127
|
-
});
|
|
13128
|
-
}
|
|
13129
13808
|
await Promise.race([
|
|
13130
13809
|
flush(),
|
|
13131
13810
|
new Promise((resolve2) => setTimeout(resolve2, SHUTDOWN_TIMEOUT_MS))
|
|
@@ -13151,9 +13830,9 @@ function installShutdownHandlers(bridge) {
|
|
|
13151
13830
|
function resolveRepoFile(rel) {
|
|
13152
13831
|
let dir = __dirname;
|
|
13153
13832
|
for (let i = 0; i < 8; i++) {
|
|
13154
|
-
const candidate =
|
|
13155
|
-
if ((0,
|
|
13156
|
-
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);
|
|
13157
13836
|
if (parent === dir) break;
|
|
13158
13837
|
dir = parent;
|
|
13159
13838
|
}
|
|
@@ -13162,8 +13841,8 @@ function resolveRepoFile(rel) {
|
|
|
13162
13841
|
function findGitRoot(startDir) {
|
|
13163
13842
|
let dir = startDir;
|
|
13164
13843
|
for (let i = 0; i < 12; i++) {
|
|
13165
|
-
if ((0,
|
|
13166
|
-
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);
|
|
13167
13846
|
if (parent === dir) break;
|
|
13168
13847
|
dir = parent;
|
|
13169
13848
|
}
|