@inetafrica/open-claudia 3.5.67 → 3.5.68
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/CHANGELOG.md +9 -0
- package/core/tool-guard.js +381 -0
- package/package.json +2 -2
- package/test-provider-language.js +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.5.68
|
|
4
|
+
|
|
5
|
+
3.5.67 plus a guard on test runs (Sumeet, 19 Oct 11:47 EAT, card "Mac: stop parallel threads from running it out of memory": "Both: checker rule and safety net"). The Mac froze twice that morning (power button held at 10:51 and 11:09 EAT): parallel conversations' test runs wanted about 26 GB on the 16 GB Mac, 12 of those GB from one helper's `npx jest` with no worker cap. The same morning the Chat Spaces bot pod was OOM-killed twice by `jest --maxWorkers=4` under its 3 Gi limit. The rule in lessons.md never reached helpers; the tool hook does.
|
|
6
|
+
|
|
7
|
+
- **Test-run guard** (`core/tool-guard.js`, every Bash call of every conversation and helper). A jest or vitest run is refused, with the exact fix, when it would start more than 2 workers (`--maxWorkers=2`, `--runInBand`, or a project config that sets `maxWorkers` all pass); when another test run is already going on the machine (only one at a time, plus a 30 s claim so two cannot start in the same second); or when free memory is under 40% (macOS `memory_pressure`; on pods the cgroup limit, else MemAvailable). It follows `cd`, package scripts (`npm test` -> `jest --ci`, `npm test -- --maxWorkers=2`), `npx`/`npm exec`/`yarn`/`pnpm exec`, `node .../jest.js` and `bash -c`; greps, reads and quoted text that mention jest stay free. Runs ahead of the relaxed tooling mode (it protects the machine). Refusals log as `kind: "test-run"`, outside the tool-first KPI. `OC_TEST_RUN_MAX_WORKERS`, `OC_TEST_RUN_MIN_FREE_PCT` change the limits; `OC_TEST_RUN_GUARD=off` turns it off. Any error in the guard allows.
|
|
8
|
+
- Proven with a real `claude -p` run: an Agent-tool helper's `npx jest` was refused by the hook before jest started. Not covered: a raw `claude -p` started by hand without the bot's hook settings, and test runners inside script files; on the Mac the memory guard (mac-health `memory-guard`, a launchd agent that stops the newest big test/build run below 20% free) is the net under this.
|
|
9
|
+
|
|
10
|
+
Tests: `test-test-run-guard.js` (worker cap by flag, script and config; one at a time; claim; memory floor; cgroup and meminfo; env limits; fail-open; through the hook).
|
|
11
|
+
|
|
3
12
|
## 3.5.67
|
|
4
13
|
|
|
5
14
|
3.5.66 plus card building blocks (APP-31; Sumeet, 16 Oct 08:28 EAT: "ok build and push, ensure you also update open claudia so it knows how to use these, we also need to nudge it to try to discover them as it works"). Pairs with chat-central 5d28c9c (v2 cards: table block; tick, date, slots, rows, board questions), Kazee Chat web and phone 4.6.0.
|
package/core/tool-guard.js
CHANGED
|
@@ -994,6 +994,375 @@ function browserDriverDenial(command, now = Date.now()) {
|
|
|
994
994
|
}
|
|
995
995
|
}
|
|
996
996
|
|
|
997
|
+
// ── test runs: worker cap, one at a time, enough free memory ──────────────
|
|
998
|
+
// 19 Oct 2026: Sumeet's 16 GB Mac froze twice (power button held at 10:51 and
|
|
999
|
+
// 11:09 EAT) when parallel conversations' test runs wanted ~26 GB: a helper's
|
|
1000
|
+
// `npx jest` with no worker cap started 12 workers of ~1 GB each next to
|
|
1001
|
+
// another thread's jest. The same morning the Chat Spaces bot pod was
|
|
1002
|
+
// OOM-killed twice by `jest --maxWorkers=4` under its 3 Gi limit. A rule in
|
|
1003
|
+
// lessons.md did not reach helpers; this hook does, because every Bash call
|
|
1004
|
+
// of every conversation and helper passes here. So a jest/vitest run must cap
|
|
1005
|
+
// its workers (2 by default), only one runs at a time on the machine, and
|
|
1006
|
+
// none starts while free memory is already low. On the Mac, mac-health
|
|
1007
|
+
// memory-guard is the net under this for anything that slips past (a script
|
|
1008
|
+
// file, a runner this does not know). Deliberately ahead of the relaxed
|
|
1009
|
+
// tooling-mode switch: it protects the machine, it is not a workflow
|
|
1010
|
+
// preference. Any error here allows: the net still holds.
|
|
1011
|
+
const TEST_RUNNERS = new Set(["jest", "vitest"]);
|
|
1012
|
+
const PACKAGE_MANAGERS = new Set(["npm", "yarn", "pnpm", "bun"]);
|
|
1013
|
+
const TEST_RUN_CLAIM_MS = 30000;
|
|
1014
|
+
const NODE_VALUE_FLAGS = new Set(["-r", "--require", "--import", "--loader", "--experimental-loader", "--env-file", "--conditions", "-C"]);
|
|
1015
|
+
const NOT_A_RUN_FLAGS = new Set(["--listTests", "--showConfig", "--version", "-v", "--help", "-h", "--clearCache"]);
|
|
1016
|
+
// A process that IS a test run (not a shell or grep that mentions one).
|
|
1017
|
+
const TEST_PROCESS_RE = /(?:^|[\s/])(?:jest|jest-cli|vitest)(?:\.c?m?js)?(?=\s|$)|jest-worker\/|vitest\/dist\/|tinypool\//;
|
|
1018
|
+
const TEST_PROCESS_BINS = new Set(["node", "nodejs", "jest", "vitest", "npm", "npx", "yarn", "pnpm", "pnpx", "bun", "bunx"]);
|
|
1019
|
+
|
|
1020
|
+
function positiveInt(value, fallback) {
|
|
1021
|
+
const n = Number(value);
|
|
1022
|
+
return Number.isInteger(n) && n > 0 ? n : fallback;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
function testRunLimits() {
|
|
1026
|
+
return {
|
|
1027
|
+
maxWorkers: positiveInt(process.env.OC_TEST_RUN_MAX_WORKERS, 2),
|
|
1028
|
+
minFreePct: positiveInt(process.env.OC_TEST_RUN_MIN_FREE_PCT, 40),
|
|
1029
|
+
claimFile: process.env.OC_TEST_RUN_CLAIM || path.join(CONFIG_DIR, "test-run.claim.json"),
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
function readText(file, limit = 256 * 1024) {
|
|
1034
|
+
try {
|
|
1035
|
+
const st = fs.statSync(file);
|
|
1036
|
+
if (!st.isFile() || st.size > limit) return null;
|
|
1037
|
+
return fs.readFileSync(file, "utf8");
|
|
1038
|
+
} catch (e) { return null; }
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
function readPackageJson(dir) {
|
|
1042
|
+
const text = readText(path.join(dir, "package.json"));
|
|
1043
|
+
if (text === null) return null;
|
|
1044
|
+
try { return JSON.parse(text); } catch (e) { return null; }
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
// jest/vitest by name or path: jest, npx jest, node_modules/.bin/jest,
|
|
1048
|
+
// node [flags] node_modules/jest/bin/jest.js, and the package managers' own
|
|
1049
|
+
// ways of running a binary (npm exec, yarn, pnpm exec/dlx, bun x).
|
|
1050
|
+
function directTestRunner(words, at, pkg) {
|
|
1051
|
+
const name = baseName(words[at]).replace(/\.c?m?js$/, "");
|
|
1052
|
+
if (TEST_RUNNERS.has(name)) return { runner: name, args: words.slice(at + 1) };
|
|
1053
|
+
if (name === "node" || name === "nodejs") {
|
|
1054
|
+
let j = at + 1;
|
|
1055
|
+
while (j < words.length && words[j].startsWith("-")) j += NODE_VALUE_FLAGS.has(words[j]) ? 2 : 1;
|
|
1056
|
+
const script = words[j] ? baseName(words[j]).replace(/\.c?m?js$/, "") : "";
|
|
1057
|
+
if (TEST_RUNNERS.has(script)) return { runner: script, args: words.slice(j + 1) };
|
|
1058
|
+
return null;
|
|
1059
|
+
}
|
|
1060
|
+
const sub = words[at + 1];
|
|
1061
|
+
const bin = (k) => {
|
|
1062
|
+
let j = k;
|
|
1063
|
+
if (words[j] === "--") j += 1;
|
|
1064
|
+
const runner = words[j] ? baseName(words[j]) : "";
|
|
1065
|
+
return TEST_RUNNERS.has(runner) ? { runner, args: words.slice(j + 1) } : null;
|
|
1066
|
+
};
|
|
1067
|
+
if (name === "npm" && (sub === "exec" || sub === "x")) return bin(at + 2);
|
|
1068
|
+
if (name === "pnpm" && (sub === "exec" || sub === "dlx")) return bin(at + 2);
|
|
1069
|
+
if (name === "bun" && sub === "x") return bin(at + 2);
|
|
1070
|
+
if (name === "yarn" && sub === "exec") return bin(at + 2);
|
|
1071
|
+
const scripts = (pkg && pkg.scripts) || {};
|
|
1072
|
+
if ((name === "yarn" || name === "pnpm") && TEST_RUNNERS.has(sub) && !scripts[sub]) return bin(at + 1);
|
|
1073
|
+
return null;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
// A package script that runs tests: npm test / npm run <s> -- args,
|
|
1077
|
+
// yarn [run] <s> args, pnpm [run] <s> args, bun run <s> args. Returns the
|
|
1078
|
+
// script's text with the extra args appended, or null.
|
|
1079
|
+
function packageScriptRun(words, at, pkg) {
|
|
1080
|
+
const name = baseName(words[at]);
|
|
1081
|
+
if (!PACKAGE_MANAGERS.has(name) || !pkg || !pkg.scripts) return null;
|
|
1082
|
+
let j = at + 1;
|
|
1083
|
+
while (j < words.length && words[j].startsWith("-")) j += 1; // --silent, -s ...
|
|
1084
|
+
const sub = words[j];
|
|
1085
|
+
if (!sub) return null;
|
|
1086
|
+
let script = null;
|
|
1087
|
+
let extra = [];
|
|
1088
|
+
const after = (k) => {
|
|
1089
|
+
const rest = words.slice(k);
|
|
1090
|
+
if (name === "npm") {
|
|
1091
|
+
const dash = rest.indexOf("--");
|
|
1092
|
+
return dash < 0 ? [] : rest.slice(dash + 1);
|
|
1093
|
+
}
|
|
1094
|
+
return rest[0] === "--" ? rest.slice(1) : rest;
|
|
1095
|
+
};
|
|
1096
|
+
if (["test", "t", "tst"].includes(sub) && name !== "bun") { script = "test"; extra = after(j + 1); }
|
|
1097
|
+
else if (["run", "run-script", "rum", "urn"].includes(sub)) { script = words[j + 1]; extra = after(j + 2); }
|
|
1098
|
+
else if ((name === "yarn" || name === "pnpm") && pkg.scripts[sub]) { script = sub; extra = after(j + 1); }
|
|
1099
|
+
if (!script || typeof pkg.scripts[script] !== "string") return null;
|
|
1100
|
+
const quoted = extra.map((arg) => require("./providers/hook-command").shellQuote(arg));
|
|
1101
|
+
// How to pass a flag through to the script, for the deny message.
|
|
1102
|
+
const example = name === "npm"
|
|
1103
|
+
? (script === "test" ? "npm test --" : `npm run ${script} --`)
|
|
1104
|
+
: name === "bun" ? `bun run ${script}` : `${name} ${script}`;
|
|
1105
|
+
return { script, example, text: [pkg.scripts[script], ...quoted].join(" ") };
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
// Every jest/vitest run a command line would start: [{ runner, args, cwd }].
|
|
1109
|
+
// Follows `cd` within the line, package scripts (npm test → "jest --ci"),
|
|
1110
|
+
// and what a shell runs from a string (bash -c, eval, $(...)).
|
|
1111
|
+
function testRunInvocations(command, cwd, depth = 0) {
|
|
1112
|
+
const runs = [];
|
|
1113
|
+
if (depth > 4 || !/jest|vitest|test|run|\bt\b|exec|yarn|pnpm/.test(String(command || ""))) return runs;
|
|
1114
|
+
let dir = path.resolve(String(cwd || process.cwd()));
|
|
1115
|
+
const { commands, nested } = shellCommands(command);
|
|
1116
|
+
const scripts = [...nested];
|
|
1117
|
+
for (const { words, input } of commands) {
|
|
1118
|
+
const at = commandWordIndex(words, scripts);
|
|
1119
|
+
if (at < 0) continue;
|
|
1120
|
+
const name = baseName(words[at]);
|
|
1121
|
+
if (name === "cd" || name === "pushd") {
|
|
1122
|
+
const target = words.slice(at + 1).find((w) => !w.startsWith("-"));
|
|
1123
|
+
dir = target ? path.resolve(dir, expandHome(target)) : os.homedir();
|
|
1124
|
+
continue;
|
|
1125
|
+
}
|
|
1126
|
+
if (name === "eval") { scripts.push(words.slice(at + 1).join(" ")); continue; }
|
|
1127
|
+
if (SHELLS.has(name)) {
|
|
1128
|
+
const flag = words.findIndex((w, k) => k > at && /^-[A-Za-z]*c[A-Za-z]*$/.test(w));
|
|
1129
|
+
if (flag > 0 && words[flag + 1] !== undefined) runs.push(...testRunInvocations(words[flag + 1], dir, depth + 1));
|
|
1130
|
+
else for (const text of input) runs.push(...testRunInvocations(text, dir, depth + 1));
|
|
1131
|
+
continue;
|
|
1132
|
+
}
|
|
1133
|
+
const pkg = PACKAGE_MANAGERS.has(name) ? readPackageJson(dir) : null;
|
|
1134
|
+
const direct = directTestRunner(words, at, pkg);
|
|
1135
|
+
if (direct) { runs.push({ ...direct, cwd: dir }); continue; }
|
|
1136
|
+
const scriptRun = packageScriptRun(words, at, pkg);
|
|
1137
|
+
if (scriptRun) {
|
|
1138
|
+
for (const run of testRunInvocations(scriptRun.text, dir, depth + 1)) runs.push({ ...run, via: scriptRun.example });
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
for (const script of scripts) runs.push(...testRunInvocations(script, dir, depth + 1));
|
|
1142
|
+
return runs;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
function flagValue(args, names) {
|
|
1146
|
+
for (let k = 0; k < args.length; k += 1) {
|
|
1147
|
+
for (const name of names) {
|
|
1148
|
+
if (args[k] === name && args[k + 1] !== undefined) return args[k + 1];
|
|
1149
|
+
if (args[k].startsWith(`${name}=`)) return args[k].slice(name.length + 1);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return null;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
function workersFrom(value, cpus) {
|
|
1156
|
+
const text = String(value).trim().replace(/^['"]|['"]$/g, "");
|
|
1157
|
+
const pct = text.match(/^(\d+(?:\.\d+)?)%$/);
|
|
1158
|
+
if (pct) return Math.max(1, Math.floor((cpus * Number(pct[1])) / 100));
|
|
1159
|
+
const n = Number(text);
|
|
1160
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : null;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
const CONFIG_CAP_RE = {
|
|
1164
|
+
jest: /\bmaxWorkers\b\s*[:=]?\s*(['"]?\d+(?:\.\d+)?%?['"]?)?/,
|
|
1165
|
+
vitest: /\b(maxWorkers|maxThreads|maxForks|singleThread|singleFork|fileParallelism)\b\s*[:=]?\s*(['"]?[\w.%]+['"]?)?/,
|
|
1166
|
+
};
|
|
1167
|
+
const DEFAULT_CONFIGS = {
|
|
1168
|
+
jest: ["jest.config.js", "jest.config.ts", "jest.config.mjs", "jest.config.cjs", "jest.config.json"],
|
|
1169
|
+
vitest: ["vitest.config.ts", "vitest.config.mts", "vitest.config.js", "vitest.config.mjs", "vitest.config.cjs", "vite.config.ts", "vite.config.mts", "vite.config.js", "vite.config.mjs"],
|
|
1170
|
+
};
|
|
1171
|
+
|
|
1172
|
+
// The project's own config caps the workers: { workers } (null = set by code
|
|
1173
|
+
// we cannot read, trusted) or null when the config sets no cap.
|
|
1174
|
+
function configCap(runner, args, cwd, cpus) {
|
|
1175
|
+
const files = [];
|
|
1176
|
+
const explicit = flagValue(args, ["--config", "-c"]);
|
|
1177
|
+
if (explicit) files.push(path.resolve(cwd, expandHome(explicit)));
|
|
1178
|
+
else files.push(...DEFAULT_CONFIGS[runner].map((f) => path.join(cwd, f)));
|
|
1179
|
+
for (const file of files) {
|
|
1180
|
+
const text = readText(file);
|
|
1181
|
+
if (text === null) continue;
|
|
1182
|
+
const m = text.match(CONFIG_CAP_RE[runner]);
|
|
1183
|
+
if (!m) continue;
|
|
1184
|
+
if (runner === "vitest") {
|
|
1185
|
+
if (/^single/.test(m[1])) return { workers: /false/.test(m[2] || "") ? null : 1 };
|
|
1186
|
+
if (m[1] === "fileParallelism") return /false/.test(m[2] || "") ? { workers: 1 } : null;
|
|
1187
|
+
return { workers: m[2] ? workersFrom(m[2], cpus) : null };
|
|
1188
|
+
}
|
|
1189
|
+
return { workers: m[1] ? workersFrom(m[1], cpus) : null };
|
|
1190
|
+
}
|
|
1191
|
+
if (runner === "jest" && !explicit) {
|
|
1192
|
+
const pkg = readPackageJson(cwd);
|
|
1193
|
+
if (pkg && pkg.jest && typeof pkg.jest === "object" && pkg.jest.maxWorkers !== undefined) {
|
|
1194
|
+
return { workers: workersFrom(pkg.jest.maxWorkers, cpus) };
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
return null;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
// How many workers a run will start: { notARun } | { workers, source }.
|
|
1201
|
+
// workers null = capped by project code we cannot evaluate (trusted).
|
|
1202
|
+
function testRunWorkers(run, cpus) {
|
|
1203
|
+
const { runner, args } = run;
|
|
1204
|
+
if (args.some((a) => NOT_A_RUN_FLAGS.has(a)) || (runner === "vitest" && ["list", "init"].includes(args[0]))) {
|
|
1205
|
+
return { notARun: true };
|
|
1206
|
+
}
|
|
1207
|
+
if (runner === "jest") {
|
|
1208
|
+
if (args.includes("--runInBand") || args.includes("-i") || args.includes("--run-in-band")) return { workers: 1, source: "--runInBand" };
|
|
1209
|
+
const value = flagValue(args, ["--maxWorkers", "--max-workers", "-w"]);
|
|
1210
|
+
if (value !== null) return { workers: workersFrom(value, cpus) ?? cpus, source: "--maxWorkers" };
|
|
1211
|
+
} else {
|
|
1212
|
+
if (args.includes("--no-file-parallelism") || args.includes("--fileParallelism=false")
|
|
1213
|
+
|| args.includes("--poolOptions.threads.singleThread") || args.includes("--poolOptions.forks.singleFork")) {
|
|
1214
|
+
return { workers: 1, source: "single worker" };
|
|
1215
|
+
}
|
|
1216
|
+
const value = flagValue(args, ["--maxWorkers", "--poolOptions.threads.maxThreads", "--poolOptions.forks.maxForks", "--poolOptions.vmThreads.maxThreads"]);
|
|
1217
|
+
if (value !== null) return { workers: workersFrom(value, cpus) ?? cpus, source: "--maxWorkers" };
|
|
1218
|
+
}
|
|
1219
|
+
const cap = configCap(runner, args, run.cwd, cpus);
|
|
1220
|
+
if (cap) return { workers: cap.workers, source: "config" };
|
|
1221
|
+
return { workers: Math.max(1, cpus - 1), source: "default" };
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
// Free memory as a percentage: macOS memory_pressure; Linux the cgroup's
|
|
1225
|
+
// limit (a pod) or the host's MemAvailable. null when unknown (no check).
|
|
1226
|
+
function linuxFreeMemoryPct({ root = "/" } = {}) {
|
|
1227
|
+
const read = (rel) => readText(path.join(root, rel));
|
|
1228
|
+
const max2 = read("sys/fs/cgroup/memory.max");
|
|
1229
|
+
if (max2 !== null && /^\d+\s*$/.test(max2)) {
|
|
1230
|
+
const max = Number(max2);
|
|
1231
|
+
const current = Number(read("sys/fs/cgroup/memory.current"));
|
|
1232
|
+
const inactive = Number(((read("sys/fs/cgroup/memory.stat") || "").match(/^inactive_file (\d+)$/m) || [])[1] || 0);
|
|
1233
|
+
if (max > 0 && Number.isFinite(current)) return Math.round(((max - Math.max(0, current - inactive)) / max) * 100);
|
|
1234
|
+
}
|
|
1235
|
+
const max1 = Number(read("sys/fs/cgroup/memory/memory.limit_in_bytes"));
|
|
1236
|
+
if (max1 > 0 && max1 < 2 ** 60) {
|
|
1237
|
+
const usage = Number(read("sys/fs/cgroup/memory/memory.usage_in_bytes"));
|
|
1238
|
+
const inactive = Number(((read("sys/fs/cgroup/memory/memory.stat") || "").match(/^total_inactive_file (\d+)$/m) || [])[1] || 0);
|
|
1239
|
+
if (Number.isFinite(usage)) return Math.round(((max1 - Math.max(0, usage - inactive)) / max1) * 100);
|
|
1240
|
+
}
|
|
1241
|
+
const info = read("proc/meminfo") || "";
|
|
1242
|
+
const total = Number((info.match(/^MemTotal:\s+(\d+)/m) || [])[1]);
|
|
1243
|
+
const avail = Number((info.match(/^MemAvailable:\s+(\d+)/m) || [])[1]);
|
|
1244
|
+
return total > 0 && Number.isFinite(avail) ? Math.round((avail / total) * 100) : null;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
const systemProbe = {
|
|
1248
|
+
cpus: () => (os.availableParallelism ? os.availableParallelism() : os.cpus().length) || 1,
|
|
1249
|
+
freePct() {
|
|
1250
|
+
if (process.platform === "darwin") {
|
|
1251
|
+
const out = require("child_process").spawnSync("memory_pressure", ["-Q"], { encoding: "utf8", timeout: 3000 });
|
|
1252
|
+
const m = String(out.stdout || "").match(/free percentage:\s*(\d+)%/);
|
|
1253
|
+
return m ? Number(m[1]) : null;
|
|
1254
|
+
}
|
|
1255
|
+
return process.platform === "linux" ? linuxFreeMemoryPct() : null;
|
|
1256
|
+
},
|
|
1257
|
+
processes() {
|
|
1258
|
+
const out = require("child_process").spawnSync("ps", ["-axo", "pid=,etime=,command="], { encoding: "utf8", timeout: 3000 });
|
|
1259
|
+
return String(out.stdout || "").split("\n").map((line) => {
|
|
1260
|
+
const m = line.match(/^\s*(\d+)\s+(\S+)\s+(.*)$/);
|
|
1261
|
+
return m && { pid: Number(m[1]), etime: m[2], command: m[3] };
|
|
1262
|
+
}).filter(Boolean);
|
|
1263
|
+
},
|
|
1264
|
+
cwdOf(pid) {
|
|
1265
|
+
if (process.platform === "linux") { try { return fs.readlinkSync(`/proc/${pid}/cwd`); } catch (e) { return ""; } }
|
|
1266
|
+
const out = require("child_process").spawnSync("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], { encoding: "utf8", timeout: 3000 });
|
|
1267
|
+
const line = String(out.stdout || "").split("\n").find((l) => l.startsWith("n"));
|
|
1268
|
+
return line ? line.slice(1) : "";
|
|
1269
|
+
},
|
|
1270
|
+
now: () => Date.now(),
|
|
1271
|
+
};
|
|
1272
|
+
|
|
1273
|
+
// A process that is a test run: the runner or one of its workers, not a shell,
|
|
1274
|
+
// grep or `node -e` script whose text mentions jest (and never claude, whose
|
|
1275
|
+
// command line carries the whole system prompt).
|
|
1276
|
+
function isTestProcess(command) {
|
|
1277
|
+
const words = String(command).split(/\s+/);
|
|
1278
|
+
const bin = baseName(words[0] || "");
|
|
1279
|
+
if (!TEST_PROCESS_BINS.has(bin)) return false;
|
|
1280
|
+
if (bin === "node" || bin === "nodejs" || bin === "bun") {
|
|
1281
|
+
let j = 1;
|
|
1282
|
+
while (j < words.length && words[j].startsWith("-")) {
|
|
1283
|
+
if (["-e", "--eval", "-p", "--print"].includes(words[j])) return false;
|
|
1284
|
+
j += NODE_VALUE_FLAGS.has(words[j]) ? 2 : 1;
|
|
1285
|
+
}
|
|
1286
|
+
return TEST_PROCESS_RE.test(words[j] || "");
|
|
1287
|
+
}
|
|
1288
|
+
return TEST_PROCESS_RE.test(command);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
function runningTestProcess(probe) {
|
|
1292
|
+
return probe.processes().find((p) => p.pid !== process.pid && isTestProcess(p.command)) || null;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
function readClaim(file) {
|
|
1296
|
+
const text = readText(file, 64 * 1024);
|
|
1297
|
+
if (text === null) return null;
|
|
1298
|
+
try { return JSON.parse(text); } catch (e) { return null; }
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
const home = (p) => String(p || "").replace(os.homedir(), "~");
|
|
1302
|
+
|
|
1303
|
+
// null (allow) or { reason, message } for a Bash command. `session` is the
|
|
1304
|
+
// hook payload's session_id: a conversation retrying its own just-started
|
|
1305
|
+
// run is not blocked by its own claim.
|
|
1306
|
+
function testRunDenial(command, { cwd, session = "", probe = systemProbe, limits = testRunLimits() } = {}) {
|
|
1307
|
+
try {
|
|
1308
|
+
if (process.env.OC_TEST_RUN_GUARD === "off") return null;
|
|
1309
|
+
const runs = testRunInvocations(command, cwd);
|
|
1310
|
+
if (!runs.length) return null;
|
|
1311
|
+
const cpus = probe.cpus();
|
|
1312
|
+
let heavy = null;
|
|
1313
|
+
for (const run of runs) {
|
|
1314
|
+
const verdict = testRunWorkers(run, cpus);
|
|
1315
|
+
if (verdict.notARun) continue;
|
|
1316
|
+
heavy = heavy || run;
|
|
1317
|
+
if (verdict.workers !== null && verdict.workers > limits.maxWorkers) {
|
|
1318
|
+
const capped = `--maxWorkers=${limits.maxWorkers}`;
|
|
1319
|
+
const how = run.via ? `${run.via} ${capped}` : `npx ${run.runner} ${capped} <files>`;
|
|
1320
|
+
return {
|
|
1321
|
+
reason: "workers",
|
|
1322
|
+
message:
|
|
1323
|
+
`⛔ Test-run guard: this ${run.runner} run would start ${verdict.workers} workers (${verdict.source === "default" ? "no worker cap: one per CPU core" : `${verdict.source} allows ${verdict.workers}`}), about 1 GB each. ` +
|
|
1324
|
+
`On 19 Oct uncapped test runs from parallel conversations froze the Mac twice.\n` +
|
|
1325
|
+
`Run it with at most ${limits.maxWorkers} workers: add ${capped} (or --runInBand for a few files), e.g. \`${how}\`.`,
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
if (!heavy) return null;
|
|
1330
|
+
const busy = runningTestProcess(probe);
|
|
1331
|
+
if (busy) {
|
|
1332
|
+
const where = home(probe.cwdOf(busy.pid));
|
|
1333
|
+
return {
|
|
1334
|
+
reason: "busy",
|
|
1335
|
+
message:
|
|
1336
|
+
`⛔ Test-run guard: another test run is already going on this machine (pid ${busy.pid}, running ${busy.etime}${where ? `, in ${where}` : ""}): ${String(busy.command).slice(0, 140)}\n` +
|
|
1337
|
+
`Only one test run at a time, so parallel conversations cannot run the machine out of memory. Wait for it to finish, then run yours. If it is your own leftover run, stop it first.`,
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
const claim = readClaim(limits.claimFile);
|
|
1341
|
+
if (claim && claim.session !== session && probe.now() - Number(claim.at || 0) < TEST_RUN_CLAIM_MS) {
|
|
1342
|
+
return {
|
|
1343
|
+
reason: "busy",
|
|
1344
|
+
message:
|
|
1345
|
+
`⛔ Test-run guard: another conversation started a test run ${Math.round((probe.now() - claim.at) / 1000)} s ago${claim.cwd ? ` in ${home(claim.cwd)}` : ""}. Only one runs at a time; wait for it to finish, then run yours.`,
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
const free = probe.freePct();
|
|
1349
|
+
if (free !== null && free < limits.minFreePct) {
|
|
1350
|
+
return {
|
|
1351
|
+
reason: "memory",
|
|
1352
|
+
message:
|
|
1353
|
+
`⛔ Test-run guard: free memory is ${free}%, under the ${limits.minFreePct}% a test run needs. Wait a few minutes and try again; do not raise the worker count.` +
|
|
1354
|
+
(process.platform === "darwin" ? " See what is using memory: open-claudia tool run mac-health memory-guard-status" : ""),
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
try {
|
|
1358
|
+
fs.writeFileSync(limits.claimFile, JSON.stringify({ at: probe.now(), session, cwd: heavy.cwd, runner: heavy.runner }));
|
|
1359
|
+
} catch (e) { /* best effort */ }
|
|
1360
|
+
return null;
|
|
1361
|
+
} catch (e) {
|
|
1362
|
+
return null;
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
|
|
997
1366
|
// Claude sends the shell command as a string; accept an argv array too.
|
|
998
1367
|
function shellCommandOf(input) {
|
|
999
1368
|
if (!input || typeof input !== "object") return null;
|
|
@@ -1072,6 +1441,17 @@ function runHook(payload) {
|
|
|
1072
1441
|
logGuardEvent({ kind: "deny", rule: "browser-driver", subcommand: driving.subcommand });
|
|
1073
1442
|
return { exit: 2, stderr: driving.message };
|
|
1074
1443
|
}
|
|
1444
|
+
// Test runs (worker cap, one at a time, free memory): ahead of the
|
|
1445
|
+
// tooling-mode switch too. Logged as its own kind so the tool-first KPI
|
|
1446
|
+
// (denies/bypasses) does not count it.
|
|
1447
|
+
const testRun = testRunDenial(command, {
|
|
1448
|
+
cwd: typeof data.cwd === "string" ? data.cwd : undefined,
|
|
1449
|
+
session: typeof data.session_id === "string" ? data.session_id : "",
|
|
1450
|
+
});
|
|
1451
|
+
if (testRun) {
|
|
1452
|
+
logGuardEvent({ kind: "test-run", decision: "deny", reason: testRun.reason, command: String(command).slice(0, 300) });
|
|
1453
|
+
return { exit: 2, stderr: testRun.message };
|
|
1454
|
+
}
|
|
1075
1455
|
// Relaxed tooling mode: the gate observes but never blocks. The event is
|
|
1076
1456
|
// still logged (as relaxed-pass, not deny) so the dream KPI can report
|
|
1077
1457
|
// what WOULD have been denied — visibility survives the mode switch.
|
|
@@ -1107,4 +1487,5 @@ module.exports = {
|
|
|
1107
1487
|
protectedReadRoots, isProtectedReadPath, isProtectedSearchRoot, secretReadRule,
|
|
1108
1488
|
matchRawOp, noteRawOp, readGuardEvents,
|
|
1109
1489
|
agentBrowserInvocations, browserDriverDenial,
|
|
1490
|
+
testRunInvocations, testRunWorkers, testRunDenial, linuxFreeMemoryPct,
|
|
1110
1491
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inetafrica/open-claudia",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.68",
|
|
4
4
|
"description": "An always-on, provider-agnostic coding-agent harness for Claude Code and OpenAI Codex via chat",
|
|
5
5
|
"main": "bot.js",
|
|
6
6
|
"bin": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"setup": "node setup.js",
|
|
12
12
|
"start": "node bot.js",
|
|
13
13
|
"pretest": "node test-provider-fixture.js && node test-provider-core-prompt.js && node test-turn-observer.js && node test-provider-prompt-parity.js && node test-provider-env-isolation.js && node test-provider-resume-parity.js && node test-provider-bootstrap.js && node test-provider-registry.js && node test-provider-stream-decoder.js && node test-provider-events.js && node test-provider-claude.js && node test-provider-codex.js && node test-codex-output-schema.js && node test-codex-device-login.js && node test-provider-runner.js && node test-provider-refusal-recovery.js && node test-provider-run-lifecycle.js && node test-provider-gateway.js && node test-provider-side-chat.js && node test-single-runtime.js && node test-provider-migration-backup.js && node test-provider-state-migration.js && node test-provider-session-history.js && node test-provider-session-commands.js && node test-provider-compaction.js && node test-provider-scheduler.js && node test-credential-capture.js && node test-cursor-removal.js && node test-utility-provider-policy.js && node test-provider-subagent.js && node test-provider-recall.js && node test-provider-pack-review.js && node test-memory-mutation-queue.js && node test-provider-dream.js && node test-provider-enforcer.js && node test-provider-tool-hooks.js && node test-provider-boot-matrix.js && node test-provider-capabilities.js && node test-provider-language.js && node test-upgrade-path.js && node test-provider-coupling-audit.js && node test-provider-parity-e2e.js && node test-kazee-channel-health.js && node test-kazee-media-caption.js && node test-kazee-context-media.js && node test-kazee-typing-join.js && node test-kazee-inbound-dedup.js && node test-typing-phase-push.js && node test-socket-reconnect.js && node test-speech-loopback.js && node test-prompt-budget.js && node test-agency-ledger.js && node test-agency-deliberate.js && node test-guardrail.js && node test-group-status-notes.js && node test-spaces-comment-content.js && node test-boot-health.js && node test-lexical-floor.js && node test-safe-mode.js && node test-logout.js && node test-kazee-call-audio.js && node test-kazee-call-janus.js && node test-kazee-call-webrtc.js && node test-kazee-call-speech.js && node test-kazee-call-manager.js && node test-call-speech.js && node test-kazee-call-session.js",
|
|
14
|
-
"test": "OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node -e \"require('./vault'); console.log('OK')\" && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-runner-watchdog-static.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-usage-accounting.js && node test-codex-rollout.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-engine.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-discoverer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-query.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-walker-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-score-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cokept-warming.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-project-transcripts-smoke.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-transcript-room-scope.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-abilities.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-extraction.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-couse.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-transfer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-tiers.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-merge-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-learning-e2e.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-nesting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-search.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-extractive-injection.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-dedupe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-signal.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tools.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tooling-mode.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-manifest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-evolution.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-async.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-unified-identity.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-packs.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-persona.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-pipeline.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-relationship-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-relationship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-enforcer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-mandate-hygiene.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-external-rule-scopes.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-owner-cards-dm.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-cards.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-identity-prune.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-message-id.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-threads.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-people-owner-seed.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-delivery-contract.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-lock.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-thread-lanes.js && node test-thread-titles.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-thread-stop.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-stop-queue.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-typing-heartbeat.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-web-sessions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-poll-recovery.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-409-grace.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-conflict-heal-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-single-instance.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-streaming-split.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-stream-preview-purpose.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-quiet-defaults.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-spaces-autoenable.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-say-loopback.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-loopback-threads.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-transcript-threads.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-union.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-subst-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-section-budgets.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-dedup-adjudicator.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-prospective.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-regressions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cost-report.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cost-by-part.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-mining.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-certification.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-apprenticeship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-attest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-attest.js && node test-sandbox-probe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-bot-endpoint.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cli-owner-gates.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-verify-contact.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-loopback-owner-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-watch-check-env.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-intro-adoption.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-grants.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-room-provider-settings.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-store-durability.js && node test-kazee-cards.js && node test-card-blocks.js && node test-decision-upkeep.js && node test-passive-replies.js && node test-kazee-bot-loops.js && node test-kazee-bot-handoffs.js && node test-placeholder-reply.js && node test-media-speech.js && node test-speech-voices.js && node test-tts-voice.js && node test-passive-replies-router.js && node test-loopback-cards.js && node test-task-injection.js && node test-speaker-extras.js && node test-token-diet.js && node test-dream-tasks.js && node test-task-archive.js && node test-cron-tolerance.js && node test-dream-schedule.js && node test-job-failures.js && node test-scheduled-placeholder.js && node test-lessons-since.js && node test-pack-reinject.js && node test-turn-journal.js && node test-kazee-catchup.js && node test-usage-limits.js && node test-limit-waits.js && node test-signin-race-retry.js && node test-limit-pickup.js && node test-browser-view.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-browser-view.js && node test-browser-address.js && node test-browser-control.js && node test-browser-drive.js && node test-browser-teach.js && node test-kazee-bot-apps.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-browser-app.js && node test-safe-filename.js && node test-media-filename-paths.js && node test-router-document-guard.js && OPEN_CLAUDIA_TEST=1 node test-web-dashboard-escape.js && OPEN_CLAUDIA_TEST=1 node test-web-bad-requests.js && OPEN_CLAUDIA_TEST=1 node test-kazee-download-limit.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-auth-routes.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-otp-link-preview.js && node test-call-quick-voice.js && node test-call-run-stop.js && node test-call-stopped-transcript.js && node test-call-greeting-context.js && node test-call-camera.js && node test-switchboard-calls.js && node test-switchboard-supervisor.js"
|
|
14
|
+
"test": "OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node -e \"require('./vault'); console.log('OK')\" && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-runner-watchdog-static.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-usage-accounting.js && node test-codex-rollout.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-engine.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-discoverer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-query.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-walker-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-score-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cokept-warming.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-project-transcripts-smoke.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-transcript-room-scope.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-abilities.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-extraction.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-couse.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-transfer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-tiers.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-merge-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-learning-e2e.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-nesting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-search.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-extractive-injection.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-dedupe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-signal.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tools.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-test-run-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tooling-mode.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-manifest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-evolution.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-async.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-unified-identity.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-packs.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-persona.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-pipeline.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-relationship-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-relationship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-enforcer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-mandate-hygiene.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-external-rule-scopes.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-owner-cards-dm.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-cards.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-identity-prune.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-message-id.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-threads.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-people-owner-seed.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-delivery-contract.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-lock.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-thread-lanes.js && node test-thread-titles.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-thread-stop.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-stop-queue.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-typing-heartbeat.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-web-sessions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-poll-recovery.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-409-grace.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-conflict-heal-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-single-instance.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-streaming-split.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-stream-preview-purpose.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-quiet-defaults.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-spaces-autoenable.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-say-loopback.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-loopback-threads.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-transcript-threads.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-union.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-subst-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-section-budgets.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-dedup-adjudicator.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-prospective.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-regressions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cost-report.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cost-by-part.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-mining.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-certification.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-apprenticeship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-attest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-attest.js && node test-sandbox-probe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-bot-endpoint.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cli-owner-gates.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-verify-contact.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-loopback-owner-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-watch-check-env.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-intro-adoption.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-grants.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-room-provider-settings.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-store-durability.js && node test-kazee-cards.js && node test-card-blocks.js && node test-decision-upkeep.js && node test-passive-replies.js && node test-kazee-bot-loops.js && node test-kazee-bot-handoffs.js && node test-placeholder-reply.js && node test-media-speech.js && node test-speech-voices.js && node test-tts-voice.js && node test-passive-replies-router.js && node test-loopback-cards.js && node test-task-injection.js && node test-speaker-extras.js && node test-token-diet.js && node test-dream-tasks.js && node test-task-archive.js && node test-cron-tolerance.js && node test-dream-schedule.js && node test-job-failures.js && node test-scheduled-placeholder.js && node test-lessons-since.js && node test-pack-reinject.js && node test-turn-journal.js && node test-kazee-catchup.js && node test-usage-limits.js && node test-limit-waits.js && node test-signin-race-retry.js && node test-limit-pickup.js && node test-browser-view.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-browser-view.js && node test-browser-address.js && node test-browser-control.js && node test-browser-drive.js && node test-browser-teach.js && node test-kazee-bot-apps.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-browser-app.js && node test-safe-filename.js && node test-media-filename-paths.js && node test-router-document-guard.js && OPEN_CLAUDIA_TEST=1 node test-web-dashboard-escape.js && OPEN_CLAUDIA_TEST=1 node test-web-bad-requests.js && OPEN_CLAUDIA_TEST=1 node test-kazee-download-limit.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-auth-routes.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-otp-link-preview.js && node test-call-quick-voice.js && node test-call-run-stop.js && node test-call-stopped-transcript.js && node test-call-greeting-context.js && node test-call-camera.js && node test-switchboard-calls.js && node test-switchboard-supervisor.js"
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"bot.js",
|
|
@@ -650,7 +650,10 @@ assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
|
|
|
650
650
|
// 3.5.67 = 3.5.66 + card building blocks (APP-31): examples, wake wording for
|
|
651
651
|
// tick/date/slots/rows/board, `card pieces`, the card coach nudge.
|
|
652
652
|
// Sumeet, 16 Oct 08:28 EAT: "ok build and push, ensure you also update open claudia".
|
|
653
|
-
|
|
653
|
+
// 3.5.68 = 3.5.67 + test-run guard (jest/vitest: 2 workers, one run at a time,
|
|
654
|
+
// 40% free memory) in the tool hook, after the Mac froze twice on 19 Oct.
|
|
655
|
+
// Sumeet, 19 Oct 11:47 EAT card: "Both: checker rule and safety net".
|
|
656
|
+
assert.strictEqual(pkg.version, "3.5.68", "release version must remain unchanged without explicit approval");
|
|
654
657
|
for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
|
|
655
658
|
assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
|
|
656
659
|
}
|