@inetafrica/open-claudia 3.4.11 → 3.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,312 @@
1
+ "use strict";
2
+
3
+ // Codex writes one JSONL "rollout" per thread under $CODEX_HOME/sessions/
4
+ // YYYY/MM/DD/rollout-<local start>-<thread id>.jsonl. Every model request
5
+ // appends an event_msg/token_count record whose info.last_token_usage is that
6
+ // single request (input_tokens INCLUDES cached_input_tokens, so it is the
7
+ // window occupancy after the request), plus info.model_context_window and a
8
+ // rate_limits snapshot (the ChatGPT-plan quota). Codex's own compactions land
9
+ // as top-level "compacted" records, and threads the model spawned with
10
+ // spawn_agent get their own rollout whose session_meta carries
11
+ // thread_source "subagent" and parent_thread_id.
12
+ //
13
+ // `codex exec --json` itself only reports the per-turn total (turn.completed),
14
+ // so this reader is how Open Claudia measures Codex the way it measures
15
+ // Claude: peak/last single-request context instead of the turn-summed input,
16
+ // and sub-agent threads the turn total never mentions (probed live
17
+ // 2026-09-05: three parallel sub-agents took a weekly quota from 75% to 100%
18
+ // while the ledger saw none of it).
19
+
20
+ const fs = require("fs");
21
+ const os = require("os");
22
+ const path = require("path");
23
+
24
+ const DAY_MS = 24 * 60 * 60 * 1000;
25
+ const DEFAULT_LOOKBACK_DAYS = 3;
26
+ // session_meta carries the full base instructions (~22 KB observed on
27
+ // 0.153.4), so the first line is read in chunks until its newline.
28
+ const FIRST_LINE_CHUNK_BYTES = 65536;
29
+ const FIRST_LINE_MAX_BYTES = 4 * 1024 * 1024;
30
+
31
+ function resolveCodexHome(env = process.env) {
32
+ const raw = (env && env.CODEX_HOME) || process.env.CODEX_HOME;
33
+ return raw ? path.resolve(String(raw)) : path.join(os.homedir(), ".codex");
34
+ }
35
+
36
+ function pad(n) { return String(n).padStart(2, "0"); }
37
+
38
+ function dayDirs(codexHome, { now = Date.now(), days = DEFAULT_LOOKBACK_DAYS } = {}) {
39
+ // Rollout directories use the local date; include the UTC date too so a
40
+ // thread that straddles midnight in either clock is still found.
41
+ const seen = new Set();
42
+ const dirs = [];
43
+ for (let back = 0; back < days; back += 1) {
44
+ const d = new Date(now - back * DAY_MS);
45
+ const candidates = [
46
+ `${d.getFullYear()}/${pad(d.getMonth() + 1)}/${pad(d.getDate())}`,
47
+ `${d.getUTCFullYear()}/${pad(d.getUTCMonth() + 1)}/${pad(d.getUTCDate())}`,
48
+ ];
49
+ for (const rel of candidates) {
50
+ if (seen.has(rel)) continue;
51
+ seen.add(rel);
52
+ const full = path.join(codexHome, "sessions", rel);
53
+ try { if (fs.statSync(full).isDirectory()) dirs.push(full); } catch (_) {}
54
+ }
55
+ }
56
+ return dirs;
57
+ }
58
+
59
+ function listRollouts(codexHome, options = {}) {
60
+ const out = [];
61
+ for (const dir of dayDirs(codexHome, options)) {
62
+ let names = [];
63
+ try { names = fs.readdirSync(dir); } catch (_) { continue; }
64
+ for (const name of names) {
65
+ if (!name.startsWith("rollout-") || !name.endsWith(".jsonl")) continue;
66
+ out.push(path.join(dir, name));
67
+ }
68
+ }
69
+ return out;
70
+ }
71
+
72
+ function threadIdOf(filePath) {
73
+ const base = path.basename(filePath, ".jsonl");
74
+ // rollout-2026-09-05T19-19-01-<uuid>: the uuid is the trailing 36 chars.
75
+ const match = base.match(/-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i);
76
+ return match ? match[1] : null;
77
+ }
78
+
79
+ function findRolloutFile(threadId, { codexHome = resolveCodexHome(), now = Date.now(), days = DEFAULT_LOOKBACK_DAYS } = {}) {
80
+ if (!threadId) return null;
81
+ const suffix = `-${String(threadId)}.jsonl`;
82
+ for (const file of listRollouts(codexHome, { now, days })) {
83
+ if (file.endsWith(suffix)) return file;
84
+ }
85
+ return null;
86
+ }
87
+
88
+ function parseTs(value) {
89
+ const t = Date.parse(String(value || ""));
90
+ return Number.isFinite(t) ? t : null;
91
+ }
92
+
93
+ function readSessionMeta(filePath) {
94
+ let fd = null;
95
+ try {
96
+ fd = fs.openSync(filePath, "r");
97
+ const chunks = [];
98
+ let total = 0;
99
+ let firstLine = null;
100
+ while (total < FIRST_LINE_MAX_BYTES) {
101
+ const buffer = Buffer.alloc(FIRST_LINE_CHUNK_BYTES);
102
+ const read = fs.readSync(fd, buffer, 0, FIRST_LINE_CHUNK_BYTES, total);
103
+ if (read <= 0) break;
104
+ chunks.push(buffer.subarray(0, read));
105
+ total += read;
106
+ const newline = buffer.subarray(0, read).indexOf(0x0a);
107
+ if (newline >= 0) {
108
+ firstLine = Buffer.concat(chunks).toString("utf8", 0, total - read + newline);
109
+ break;
110
+ }
111
+ }
112
+ if (firstLine === null) firstLine = Buffer.concat(chunks).toString("utf8");
113
+ const record = JSON.parse(firstLine);
114
+ if (record && record.type === "session_meta" && record.payload) return normalizeSessionMeta(record);
115
+ return null;
116
+ } catch (_) {
117
+ return null;
118
+ } finally {
119
+ if (fd !== null) { try { fs.closeSync(fd); } catch (_) {} }
120
+ }
121
+ }
122
+
123
+ function normalizeSessionMeta(record) {
124
+ const p = record.payload || {};
125
+ return {
126
+ threadId: p.id || p.session_id || null,
127
+ timestamp: parseTs(p.timestamp || record.timestamp),
128
+ threadSource: typeof p.thread_source === "string" ? p.thread_source : (p.thread_source ? "subagent" : null),
129
+ parentThreadId: p.parent_thread_id || null,
130
+ agentNickname: p.agent_nickname || null,
131
+ cliVersion: p.cli_version || null,
132
+ originator: p.originator || null,
133
+ };
134
+ }
135
+
136
+ function num(value) {
137
+ const n = Number(value);
138
+ return Number.isFinite(n) ? n : 0;
139
+ }
140
+
141
+ function usageOf(raw) {
142
+ if (!raw || typeof raw !== "object") return null;
143
+ return {
144
+ input: num(raw.input_tokens),
145
+ cached: num(raw.cached_input_tokens),
146
+ cacheWrite: num(raw.cache_write_input_tokens),
147
+ output: num(raw.output_tokens),
148
+ reasoning: num(raw.reasoning_output_tokens),
149
+ };
150
+ }
151
+
152
+ function emptyTotals() {
153
+ return { input: 0, cached: 0, cacheWrite: 0, output: 0, reasoning: 0 };
154
+ }
155
+
156
+ function addTotals(target, usage) {
157
+ target.input += usage.input;
158
+ target.cached += usage.cached;
159
+ target.cacheWrite += usage.cacheWrite;
160
+ target.output += usage.output;
161
+ target.reasoning += usage.reasoning;
162
+ return target;
163
+ }
164
+
165
+ function rateLimitsOf(raw) {
166
+ if (!raw || typeof raw !== "object") return null;
167
+ const primary = raw.primary && typeof raw.primary === "object" ? raw.primary : null;
168
+ const secondary = raw.secondary && typeof raw.secondary === "object" ? raw.secondary : null;
169
+ const window = (w) => (w ? {
170
+ usedPercent: Number.isFinite(Number(w.used_percent)) ? Number(w.used_percent) : null,
171
+ windowMinutes: Number.isFinite(Number(w.window_minutes)) ? Number(w.window_minutes) : null,
172
+ resetsAt: Number.isFinite(Number(w.resets_at)) ? new Date(Number(w.resets_at) * 1000).toISOString() : null,
173
+ } : null);
174
+ return {
175
+ planType: raw.plan_type || null,
176
+ primary: window(primary),
177
+ secondary: window(secondary),
178
+ limitReached: raw.rate_limit_reached_type || null,
179
+ };
180
+ }
181
+
182
+ // Parse one rollout. `since` (ms epoch) keeps only records from the current
183
+ // turn — a rollout holds the whole thread, every turn since it was created.
184
+ function parseRollout(filePath, { since = 0 } = {}) {
185
+ let text;
186
+ try { text = fs.readFileSync(filePath, "utf8"); } catch (_) { return null; }
187
+ const result = {
188
+ file: filePath,
189
+ threadId: threadIdOf(filePath),
190
+ sessionMeta: null,
191
+ requests: [],
192
+ totals: emptyTotals(),
193
+ peakContextTokens: 0,
194
+ lastContextTokens: 0,
195
+ modelContextWindow: null,
196
+ compactions: 0,
197
+ rateLimits: null,
198
+ lastEventAt: null,
199
+ };
200
+ let previousFingerprint = null;
201
+ for (const line of text.split("\n")) {
202
+ if (!line) continue;
203
+ let record;
204
+ try { record = JSON.parse(line); } catch (_) { continue; }
205
+ if (!record || typeof record !== "object") continue;
206
+ if (record.type === "session_meta" && !result.sessionMeta) {
207
+ result.sessionMeta = normalizeSessionMeta(record);
208
+ if (!result.threadId) result.threadId = result.sessionMeta.threadId;
209
+ continue;
210
+ }
211
+ const ts = parseTs(record.timestamp);
212
+ if (ts !== null) result.lastEventAt = ts;
213
+ const inTurn = since ? (ts === null || ts >= since) : true;
214
+ if (record.type === "compacted") {
215
+ if (inTurn) result.compactions += 1;
216
+ continue;
217
+ }
218
+ const payload = record.payload;
219
+ if (!payload || payload.type !== "token_count") continue;
220
+ const info = payload.info || null;
221
+ if (info && Number.isFinite(Number(info.model_context_window))) {
222
+ result.modelContextWindow = Number(info.model_context_window);
223
+ }
224
+ const limits = rateLimitsOf(payload.rate_limits);
225
+ if (limits) result.rateLimits = limits;
226
+ if (!inTurn || !info) continue;
227
+ const last = usageOf(info.last_token_usage);
228
+ if (!last) continue;
229
+ const total = usageOf(info.total_token_usage) || last;
230
+ // Codex re-emits token_count for rate-limit refreshes with the same usage;
231
+ // one request must count once.
232
+ const fingerprint = JSON.stringify([last, total]);
233
+ if (fingerprint === previousFingerprint) continue;
234
+ previousFingerprint = fingerprint;
235
+ result.requests.push({ ts, ...last });
236
+ addTotals(result.totals, last);
237
+ if (last.input > result.peakContextTokens) result.peakContextTokens = last.input;
238
+ result.lastContextTokens = last.input;
239
+ }
240
+ return result;
241
+ }
242
+
243
+ function findChildRollouts(parentThreadId, { codexHome = resolveCodexHome(), since = 0, now = Date.now(), days = DEFAULT_LOOKBACK_DAYS, exclude = new Set() } = {}) {
244
+ if (!parentThreadId) return [];
245
+ const children = [];
246
+ for (const file of listRollouts(codexHome, { now, days })) {
247
+ if (exclude.has(file)) continue;
248
+ const meta = readSessionMeta(file);
249
+ if (!meta || meta.parentThreadId !== parentThreadId) continue;
250
+ if (since && meta.timestamp !== null && meta.timestamp < since) continue;
251
+ children.push({ file, threadId: meta.threadId || threadIdOf(file), meta });
252
+ }
253
+ return children;
254
+ }
255
+
256
+ // Everything the ledger and the compaction gate need for one Codex turn.
257
+ // Returns null when the rollout is not on disk (the caller falls back to the
258
+ // turn total Codex reported).
259
+ function collectCodexTurnUsage({ threadId, since = 0, env = process.env, codexHome = resolveCodexHome(env), now = Date.now(), includeSubagents = true, maxDepth = 2 } = {}) {
260
+ const file = findRolloutFile(threadId, { codexHome, now });
261
+ if (!file) return null;
262
+ const parent = parseRollout(file, { since });
263
+ if (!parent) return null;
264
+ const subagents = { threads: 0, requests: 0, totals: emptyTotals(), compactions: 0, threadIds: [] };
265
+ if (includeSubagents) {
266
+ const visited = new Set([file]);
267
+ let frontier = [threadId];
268
+ for (let depth = 0; depth < maxDepth && frontier.length; depth += 1) {
269
+ const next = [];
270
+ for (const parentId of frontier) {
271
+ for (const child of findChildRollouts(parentId, { codexHome, since, now, exclude: visited })) {
272
+ visited.add(child.file);
273
+ const parsed = parseRollout(child.file, { since: 0 });
274
+ if (!parsed) continue;
275
+ subagents.threads += 1;
276
+ subagents.requests += parsed.requests.length;
277
+ addTotals(subagents.totals, parsed.totals);
278
+ subagents.compactions += parsed.compactions;
279
+ subagents.threadIds.push(child.threadId);
280
+ if (child.threadId) next.push(child.threadId);
281
+ }
282
+ }
283
+ frontier = next;
284
+ }
285
+ }
286
+ return {
287
+ source: "rollout",
288
+ file,
289
+ threadId: parent.threadId || threadId,
290
+ requests: parent.requests.length,
291
+ peakContextTokens: parent.peakContextTokens,
292
+ lastContextTokens: parent.lastContextTokens,
293
+ modelContextWindow: parent.modelContextWindow,
294
+ totals: parent.totals,
295
+ compactions: parent.compactions,
296
+ rateLimits: parent.rateLimits,
297
+ subagents,
298
+ };
299
+ }
300
+
301
+ module.exports = {
302
+ DEFAULT_LOOKBACK_DAYS,
303
+ resolveCodexHome,
304
+ dayDirs,
305
+ listRollouts,
306
+ threadIdOf,
307
+ findRolloutFile,
308
+ readSessionMeta,
309
+ parseRollout,
310
+ findChildRollouts,
311
+ collectCodexTurnUsage,
312
+ };
@@ -119,6 +119,12 @@ function createCodexProvider(options = {}) {
119
119
  }
120
120
  }
121
121
 
122
+ function codexMultiAgentEnabled(settings) {
123
+ if (settings && settings.multiAgent === false) return false;
124
+ const raw = String(process.env.CODEX_MULTI_AGENT || "").trim().toLowerCase();
125
+ return !(raw === "0" || raw === "off" || raw === "false" || raw === "disabled");
126
+ }
127
+
122
128
  function buildInvocation(context, utility) {
123
129
  if (!context || typeof context !== "object") throw providerError("INVALID_PROVIDER_CONTEXT", "provider context must be an object");
124
130
  assertSupportedContext(context);
@@ -165,6 +171,12 @@ function createCodexProvider(options = {}) {
165
171
  if (preparedHook) args.push(...preparedHook.args);
166
172
 
167
173
  args.push("-c", `developer_instructions=${JSON.stringify(coreInstructions)}`);
174
+ // Codex 0.153 ships multi_agent on: the model may spawn parallel sub-agent
175
+ // threads (spawn_agent) whose usage its turn total never reports. Probed
176
+ // 2026-09-05: three parallel sub-agents took a weekly Pro quota from 75% to
177
+ // 100% in two hours. CODEX_MULTI_AGENT=off (or providerSettings.multiAgent
178
+ // false) disables the feature for every run the bot launches.
179
+ if (!codexMultiAgentEnabled(settings)) args.push("-c", "features.multi_agent=false");
168
180
 
169
181
  const effort = context.effort || settings.effort || null;
170
182
  if (effort) {
package/core/runner.js CHANGED
@@ -64,24 +64,36 @@ function usageNumber(usage, ...keys) {
64
64
  return 0;
65
65
  }
66
66
 
67
+ const { collectCodexTurnUsage } = require("./providers/codex-rollout");
68
+
69
+ // Never let a rollout read break a turn: the ledger falls back to the turn
70
+ // total Codex reported when the file is missing or unreadable.
71
+ function readCodexRollout(options) {
72
+ try { return collectCodexTurnUsage(options); } catch (_) { return null; }
73
+ }
74
+
67
75
  function usageParts(usage, backend) {
68
76
  if (!new Set(["claude", "codex"]).has(backend)) {
69
77
  throw Object.assign(new TypeError("usage accounting requires an explicit provider"), { code: "INVALID_PROVIDER" });
70
78
  }
71
- const input = usageNumber(usage, "input_tokens");
79
+ const rawInput = usageNumber(usage, "input_tokens");
72
80
  const cacheRead = backend === "codex"
73
81
  ? usageNumber(usage, "cached_input_tokens", "cache_read_input_tokens")
74
82
  : usageNumber(usage, "cache_read_input_tokens", "cached_input_tokens");
75
83
  const cacheCreation = usageNumber(usage, "cache_creation_input_tokens");
76
84
  const output = usageNumber(usage, "output_tokens");
85
+ // Codex/OpenAI reports cached input INSIDE input_tokens; Anthropic reports
86
+ // cache reads BESIDE input_tokens. Normalise so `input` is the uncached
87
+ // input for both providers (like-for-like in /usage and the ledger — on
88
+ // 2026-09-05 the raw numbers read 89.7M vs 4.5k for what was really 2.4M vs
89
+ // 0.8M) while `context` stays the full prefix each provider actually sent.
90
+ const input = backend === "codex" ? Math.max(0, rawInput - cacheRead) : rawInput;
77
91
  return {
78
92
  input,
79
93
  cacheRead,
80
94
  cacheCreation,
81
95
  output,
82
- // Codex/OpenAI reports cached input as part of input_tokens. Keep the
83
- // cache-read field for attribution, but do not add it again to context.
84
- context: backend === "codex" ? input : input + cacheRead + cacheCreation,
96
+ context: backend === "codex" ? rawInput : rawInput + cacheRead + cacheCreation,
85
97
  };
86
98
  }
87
99
 
@@ -109,24 +121,15 @@ function clampUsageDelta(current, previous) {
109
121
  };
110
122
  }
111
123
 
112
- function snapshotCodexUsage(state, sessionId, usage, options = {}) {
113
- if (!state || !sessionId || !usage) return null;
114
- const project = options.project || getProjectKey(state);
115
- if (!project) return null;
116
- const bucket = getUsageForSession(state, project, "codex", sessionId);
117
- const previous = bucket.providerUsageSnapshot || null;
118
- bucket.providerUsageSnapshot = {
119
- input_tokens: usageNumber(usage, "input_tokens"),
120
- cached_input_tokens: usageNumber(usage, "cached_input_tokens"),
121
- output_tokens: usageNumber(usage, "output_tokens"),
122
- };
123
- return previous;
124
- }
125
-
124
+ // Codex's turn.completed usage is "the usage of tokens during a turn"
125
+ // (codex-rs/exec/src/exec_events.rs) and every `codex exec resume` is its own
126
+ // process, so the number is already per turn. Until 3.4.13 it was treated as a
127
+ // cumulative session total and the previous turn subtracted: any turn smaller
128
+ // than its predecessor was recorded as 0 and the rest were undercounted (9 of
129
+ // 36 Codex turns on 2026-09-05 logged as zero). Pass it through unchanged.
126
130
  function normalizeCodexUsage(state, sessionId, usage, options = {}) {
127
- const previous = snapshotCodexUsage(state, sessionId, usage, options);
128
- if (!previous) return { usage, usageScope: "session_total", rawUsage: usage };
129
- return { usage: clampUsageDelta(usage, previous), usageScope: "turn_delta", rawUsage: usage };
131
+ void state; void sessionId; void options;
132
+ return { usage, usageScope: "turn", rawUsage: usage };
130
133
  }
131
134
 
132
135
  function applyUsageToState(state, usage, costUsd, opts = {}) {
@@ -147,6 +150,18 @@ function applyUsageToState(state, usage, costUsd, opts = {}) {
147
150
  u.lastUsageScope = opts.usageScope || "turn_delta";
148
151
  if (Number.isFinite(opts.liveContextTokens)) u.liveContextTokens = opts.liveContextTokens;
149
152
  else u.liveContextTokens = parts.context;
153
+ if (Number.isFinite(opts.contextWindowTokens) && opts.contextWindowTokens > 0) u.contextWindowTokens = opts.contextWindowTokens;
154
+ if (opts.quota && Number.isFinite(opts.quota.usedPercent)) {
155
+ u.quotaUsedPercent = opts.quota.usedPercent;
156
+ u.quotaResetsAt = opts.quota.resetsAt || null;
157
+ }
158
+ if (opts.subagents && opts.subagents.threads > 0) {
159
+ const totals = opts.subagents.totals || {};
160
+ u.subagentThreads = (u.subagentThreads || 0) + opts.subagents.threads;
161
+ u.subagentInputTokens = (u.subagentInputTokens || 0) + Math.max(0, (totals.input || 0) - (totals.cached || 0));
162
+ u.subagentCacheReadTokens = (u.subagentCacheReadTokens || 0) + (totals.cached || 0);
163
+ u.subagentOutputTokens = (u.subagentOutputTokens || 0) + (totals.output || 0);
164
+ }
150
165
  if (typeof costUsd === "number") u.costUsd += costUsd;
151
166
  return parts;
152
167
  }
@@ -185,6 +200,23 @@ function logTurnUsage(state, usage, costUsd, announce, opts = {}) {
185
200
  costUsd: typeof costUsd === "number" ? costUsd : 0,
186
201
  };
187
202
  if (typeof opts.coldStart === "boolean") record.coldStart = opts.coldStart;
203
+ if (opts.contextSource) record.contextSource = opts.contextSource;
204
+ if (Number.isFinite(opts.contextWindowTokens) && opts.contextWindowTokens > 0) record.contextWindowTokens = opts.contextWindowTokens;
205
+ if (Number.isFinite(opts.requests) && opts.requests > 0) record.requests = opts.requests;
206
+ if (Number.isFinite(opts.providerCompactions)) record.providerCompactions = opts.providerCompactions;
207
+ if (opts.quota && Number.isFinite(opts.quota.usedPercent)) {
208
+ record.quotaUsedPercent = opts.quota.usedPercent;
209
+ if (opts.quota.windowMinutes) record.quotaWindowMinutes = opts.quota.windowMinutes;
210
+ if (opts.quota.resetsAt) record.quotaResetsAt = opts.quota.resetsAt;
211
+ }
212
+ if (opts.subagents && opts.subagents.threads > 0) {
213
+ const totals = opts.subagents.totals || {};
214
+ record.subagentThreads = opts.subagents.threads;
215
+ record.subagentRequests = opts.subagents.requests || 0;
216
+ record.subagentInputTokens = Math.max(0, (totals.input || 0) - (totals.cached || 0));
217
+ record.subagentCacheReadTokens = totals.cached || 0;
218
+ record.subagentOutputTokens = totals.output || 0;
219
+ }
188
220
  if (opts.rawUsage && opts.rawUsage !== usage) {
189
221
  const raw = usageParts(opts.rawUsage, backend);
190
222
  record.rawInputTokens = raw.input;
@@ -207,7 +239,7 @@ function logTurnUsage(state, usage, costUsd, announce, opts = {}) {
207
239
  if (announce && usageOwnerOnly) {
208
240
  const baseline = result.trend?.baselineAvgContextTokens || 0;
209
241
  const rate = result.trend?.latestRate || 0;
210
- const contextLabel = hasPeak || usageScope === "turn_delta" ? "Context this turn" : "Session context total";
242
+ const contextLabel = usageScope === "session_total" && !hasPeak ? "Session context total" : "Context this turn";
211
243
  announce([
212
244
  "Token usage alert:",
213
245
  `${contextLabel}: ${fmtTokens(record.contextTokens)}`,
@@ -948,11 +980,22 @@ function resolveRunCwd(cwd) {
948
980
  return fallback;
949
981
  }
950
982
 
951
- function effectiveCompactThreshold(state = currentState()) {
983
+ const CODEX_WINDOW_COMPACT_FRACTION = 0.8;
984
+
985
+ function effectiveCompactThreshold(state = currentState(), opts = {}) {
952
986
  const override = state.settings?.compactWindow;
953
987
  if (override === 0) return 0;
954
- if (Number.isFinite(override) && override > 0) return override;
955
- return Number.isFinite(AUTO_COMPACT_TOKENS) ? AUTO_COMPACT_TOKENS : 380000;
988
+ const base = Number.isFinite(override) && override > 0
989
+ ? override
990
+ : (Number.isFinite(AUTO_COMPACT_TOKENS) ? AUTO_COMPACT_TOKENS : 380000);
991
+ // Codex's window (258k for gpt-6-astra) sits below the default threshold, so
992
+ // measured honestly the bot would never compact a Codex session — and would
993
+ // lose the brief that feeds memory. Compact at 80% of the reported window.
994
+ const window = Number(opts.contextWindowTokens ?? state.sessionUsage?.contextWindowTokens);
995
+ if (getActiveProvider(state) === "codex" && Number.isFinite(window) && window > 0) {
996
+ return Math.min(base, Math.floor(window * CODEX_WINDOW_COMPACT_FRACTION));
997
+ }
998
+ return base;
956
999
  }
957
1000
 
958
1001
  function shouldAutoCompact(state = currentState(), opts = {}) {
@@ -1511,13 +1554,32 @@ async function persistForegroundUsage(result, runContext, state = currentState()
1511
1554
  ?? usageParts(usage, runContext.provider).context;
1512
1555
  const usageSessionId = result.sessionId || runContext.sessionId;
1513
1556
  const usageProject = runContext.project?.name || null;
1557
+ let rollout = null;
1514
1558
  if (runContext.provider === "codex") {
1515
1559
  const normalized = normalizeCodexUsage(state, usageSessionId, usage, { project: usageProject });
1516
1560
  usage = normalized.usage;
1517
1561
  usageScope = normalized.usageScope;
1518
1562
  rawUsage = normalized.rawUsage;
1519
- liveContextTokens = usageParts(rawUsage, "codex").context;
1563
+ // Measure Codex like Claude: the last single-request window occupancy
1564
+ // from its rollout, never the turn-summed input — that sum tripped
1565
+ // auto-compact after almost every substantial turn (8 needless Codex
1566
+ // compactions on 2026-09-05) and hid the sub-agent threads entirely.
1567
+ rollout = readCodexRollout({
1568
+ threadId: usageSessionId,
1569
+ since: (result.timings?.startedAt || Date.now()) - 5000,
1570
+ });
1571
+ liveContextTokens = rollout && rollout.lastContextTokens > 0
1572
+ ? rollout.lastContextTokens
1573
+ : usageParts(rawUsage, "codex").context;
1520
1574
  }
1575
+ const rolloutOpts = rollout ? {
1576
+ contextSource: "rollout_last_request",
1577
+ contextWindowTokens: rollout.modelContextWindow || undefined,
1578
+ requests: rollout.requests,
1579
+ providerCompactions: rollout.compactions,
1580
+ quota: rollout.rateLimits?.primary || null,
1581
+ subagents: rollout.subagents,
1582
+ } : (runContext.provider === "codex" ? { contextSource: "turn_total_fallback" } : {});
1521
1583
  result.usage = usage;
1522
1584
  result.usageScope = usageScope;
1523
1585
  applyUsageToState(state, usage, result.costUsd || 0, {
@@ -1526,6 +1588,7 @@ async function persistForegroundUsage(result, runContext, state = currentState()
1526
1588
  liveContextTokens,
1527
1589
  project: usageProject,
1528
1590
  sessionId: usageSessionId,
1591
+ ...rolloutOpts,
1529
1592
  });
1530
1593
  logTurnUsage(
1531
1594
  state,
@@ -1544,6 +1607,7 @@ async function persistForegroundUsage(result, runContext, state = currentState()
1544
1607
  rawUsage,
1545
1608
  coldStart: usageSessionColdStart(usageSessionId),
1546
1609
  strict: true,
1610
+ ...rolloutOpts,
1547
1611
  },
1548
1612
  );
1549
1613
  saveState({ strict: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "3.4.11",
3
+ "version": "3.4.13",
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-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-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-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-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-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 && 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-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-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-queue-routing.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-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-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-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-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-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-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-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-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"
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-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-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-queue-routing.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-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-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-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-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-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-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-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-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"
15
15
  },
16
16
  "files": [
17
17
  "bot.js",