@inetafrica/open-claudia 3.5.17 → 3.5.18
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/.env.example +10 -0
- package/CHANGELOG.md +18 -0
- package/README.md +3 -0
- package/bot.js +7 -0
- package/core/config.js +9 -6
- package/core/handlers.js +56 -7
- package/core/limit-pickup.js +313 -0
- package/core/providers/claude-events.js +70 -0
- package/core/providers/events.js +4 -1
- package/core/router.js +10 -0
- package/core/run-context.js +3 -0
- package/core/runner.js +186 -13
- package/core/scheduler.js +33 -2
- package/core/state.js +91 -0
- package/core/turn-journal.js +2 -0
- package/core/usage-limits.js +321 -0
- package/package.json +2 -2
- package/test-fixtures/fake-agent-cli.js +22 -1
- package/test-provider-compaction.js +66 -1
- package/test-provider-events.js +85 -0
- package/test-provider-language.js +9 -1
- package/test-provider-scheduler.js +69 -0
- package/test-usage-accounting.js +4 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Plan usage limits: the 5-hour and weekly windows of a Claude Pro/Max plan
|
|
4
|
+
// and of the ChatGPT plan behind Codex.
|
|
5
|
+
//
|
|
6
|
+
// - recordWindows() keeps the latest use of each window (Claude's
|
|
7
|
+
// rate_limit_event, Codex's rollout snapshot) and says which warning
|
|
8
|
+
// percentages a turn has just crossed, once per window;
|
|
9
|
+
// - limitHitDetails() works out when the limit that stopped a turn resets,
|
|
10
|
+
// from the provider's own reset time, else from the wording of its notice;
|
|
11
|
+
// - the format* helpers word the chat lines.
|
|
12
|
+
//
|
|
13
|
+
// The pick-up job that re-runs a stopped message at the reset lives in
|
|
14
|
+
// core/limit-pickup.js.
|
|
15
|
+
|
|
16
|
+
const path = require("path");
|
|
17
|
+
const { CONFIG_DIR, config } = require("./config");
|
|
18
|
+
const { atomicWriteFileSync, readJsonWithFallback, withFileLock } = require("./fsutil");
|
|
19
|
+
|
|
20
|
+
const DEFAULT_FILE = path.join(CONFIG_DIR, "usage-limits.json");
|
|
21
|
+
const DEFAULT_WARN_PERCENTS = [80, 95];
|
|
22
|
+
// A window counts as new (warnings start over) when its reset time moves by
|
|
23
|
+
// more than this; Claude and Codex both report a fixed reset per window.
|
|
24
|
+
const NEW_WINDOW_SLACK_MS = 10 * 60 * 1000;
|
|
25
|
+
const PROVIDER_LABELS = { claude: "Claude", codex: "Codex" };
|
|
26
|
+
const WINDOW_LABELS = {
|
|
27
|
+
five_hour: "5-hour limit",
|
|
28
|
+
seven_day: "weekly limit",
|
|
29
|
+
seven_day_opus: "weekly Opus limit",
|
|
30
|
+
seven_day_sonnet: "weekly Sonnet limit",
|
|
31
|
+
seven_day_overage_included: "weekly limit",
|
|
32
|
+
overage: "extra-usage limit",
|
|
33
|
+
};
|
|
34
|
+
const MONTHS = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"];
|
|
35
|
+
|
|
36
|
+
const timeZoneDefault = () => process.env.OC_TIMEZONE || config.OC_TIMEZONE || "Africa/Nairobi";
|
|
37
|
+
|
|
38
|
+
// LIMIT_WARN_PERCENTS: comma-separated (default 80,95); "off" for none.
|
|
39
|
+
function warnPercents(raw = process.env.LIMIT_WARN_PERCENTS ?? config.LIMIT_WARN_PERCENTS) {
|
|
40
|
+
if (raw === undefined || raw === null || String(raw).trim() === "") return [...DEFAULT_WARN_PERCENTS];
|
|
41
|
+
if (/^(off|no|none|false|0)$/i.test(String(raw).trim())) return [];
|
|
42
|
+
return String(raw).split(",").map((s) => Number(s.trim()))
|
|
43
|
+
.filter((n) => Number.isFinite(n) && n > 0 && n <= 100)
|
|
44
|
+
.sort((a, b) => a - b);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function providerLabel(provider) {
|
|
48
|
+
return PROVIDER_LABELS[provider] || String(provider || "The provider");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function windowLabel(id) {
|
|
52
|
+
if (!id) return "usage limit";
|
|
53
|
+
if (WINDOW_LABELS[id]) return WINDOW_LABELS[id];
|
|
54
|
+
const minutes = /^(\d+)m$/.exec(id);
|
|
55
|
+
if (minutes) {
|
|
56
|
+
const n = Number(minutes[1]);
|
|
57
|
+
return n % 60 === 0 ? `${n / 60}-hour limit` : `${n}-minute limit`;
|
|
58
|
+
}
|
|
59
|
+
return "usage limit";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Codex's rollout snapshot ({ primary, secondary } windows with used_percent,
|
|
63
|
+
// window_minutes, resets_at) as windows named like Claude's. Which window is
|
|
64
|
+
// "primary" depends on the plan: on a Pro account it is the weekly one.
|
|
65
|
+
function codexWindows(rateLimits) {
|
|
66
|
+
if (!rateLimits || typeof rateLimits !== "object") return [];
|
|
67
|
+
const windows = [];
|
|
68
|
+
for (const window of [rateLimits.primary, rateLimits.secondary]) {
|
|
69
|
+
if (!window || !Number.isFinite(window.usedPercent)) continue;
|
|
70
|
+
const minutes = Number(window.windowMinutes);
|
|
71
|
+
const id = minutes === 300 ? "five_hour" : (minutes === 10080 ? "seven_day" : (Number.isFinite(minutes) && minutes > 0 ? `${minutes}m` : "plan"));
|
|
72
|
+
const resetsAt = window.resetsAt ? Date.parse(window.resetsAt) : NaN;
|
|
73
|
+
windows.push({ id, usedPercent: window.usedPercent, resetsAt: Number.isFinite(resetsAt) ? resetsAt : null });
|
|
74
|
+
}
|
|
75
|
+
return windows;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function readState(file) {
|
|
79
|
+
const state = readJsonWithFallback(file, null);
|
|
80
|
+
return state && typeof state === "object" && state.windows && typeof state.windows === "object"
|
|
81
|
+
? state
|
|
82
|
+
: { windows: {} };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Remember the latest use of each window and return the warnings now due:
|
|
87
|
+
* at most one per window, for the highest percentage crossed that this window
|
|
88
|
+
* has not been warned about. A new window (reset time moved) or a drop below a
|
|
89
|
+
* percentage (window reset, or another account) clears its warnings.
|
|
90
|
+
*/
|
|
91
|
+
function recordWindows({ provider, windows, file = DEFAULT_FILE, percents = warnPercents(), now = Date.now() } = {}) {
|
|
92
|
+
const usable = (windows || []).filter((w) => w && w.id && Number.isFinite(w.usedPercent));
|
|
93
|
+
if (!provider || !usable.length) return [];
|
|
94
|
+
// A test run that did not point the config dir somewhere else never writes
|
|
95
|
+
// the live bot's file.
|
|
96
|
+
if (file === DEFAULT_FILE && process.env.OPEN_CLAUDIA_TEST === "1" && !process.env.OPEN_CLAUDIA_CONFIG_DIR) return [];
|
|
97
|
+
return withFileLock(file, () => {
|
|
98
|
+
const state = readState(file);
|
|
99
|
+
const due = [];
|
|
100
|
+
for (const window of usable) {
|
|
101
|
+
const key = `${provider}:${window.id}`;
|
|
102
|
+
const previous = state.windows[key] || null;
|
|
103
|
+
const sameWindow = previous
|
|
104
|
+
&& (!previous.resetsAt || !window.resetsAt || Math.abs(previous.resetsAt - window.resetsAt) <= NEW_WINDOW_SLACK_MS);
|
|
105
|
+
const warned = (sameWindow ? previous.warned || [] : []).filter((p) => window.usedPercent >= p);
|
|
106
|
+
const crossed = percents.filter((p) => window.usedPercent >= p && !warned.includes(p));
|
|
107
|
+
if (crossed.length) {
|
|
108
|
+
due.push({ provider, id: window.id, percent: crossed[crossed.length - 1], usedPercent: window.usedPercent, resetsAt: window.resetsAt || null });
|
|
109
|
+
}
|
|
110
|
+
state.windows[key] = {
|
|
111
|
+
usedPercent: window.usedPercent,
|
|
112
|
+
resetsAt: window.resetsAt || null,
|
|
113
|
+
updatedAt: new Date(now).toISOString(),
|
|
114
|
+
warned: [...new Set([...warned, ...crossed])].sort((a, b) => a - b),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
atomicWriteFileSync(file, JSON.stringify(state, null, 2) + "\n");
|
|
118
|
+
return due;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** The windows last seen per provider, newest reading first, for /usage. */
|
|
123
|
+
function knownWindows({ file = DEFAULT_FILE, now = Date.now() } = {}) {
|
|
124
|
+
const state = readState(file);
|
|
125
|
+
return Object.entries(state.windows)
|
|
126
|
+
.map(([key, value]) => {
|
|
127
|
+
const [provider, id] = key.split(":");
|
|
128
|
+
// A window whose reset has passed no longer says anything about now.
|
|
129
|
+
const stale = value.resetsAt && value.resetsAt <= now;
|
|
130
|
+
return stale ? null : { provider, id, ...value };
|
|
131
|
+
})
|
|
132
|
+
.filter(Boolean)
|
|
133
|
+
.sort((a, b) => String(b.updatedAt).localeCompare(String(a.updatedAt)));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// Time zones and wording
|
|
138
|
+
|
|
139
|
+
function zonedParts(t, timeZone) {
|
|
140
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
141
|
+
timeZone, hourCycle: "h23", year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit",
|
|
142
|
+
}).formatToParts(new Date(t));
|
|
143
|
+
const p = Object.fromEntries(parts.map((x) => [x.type, Number(x.value)]));
|
|
144
|
+
return { year: p.year, month: p.month - 1, day: p.day, hour: p.hour % 24, minute: p.minute, second: p.second };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// The instant a wall-clock time in `timeZone` names (month is 0-based; day
|
|
148
|
+
// may overflow, Date.UTC rolls it). Two passes settle a DST change.
|
|
149
|
+
function zonedInstant({ year, month, day, hour, minute }, timeZone) {
|
|
150
|
+
const wall = Date.UTC(year, month, day, hour, minute);
|
|
151
|
+
let guess = wall;
|
|
152
|
+
for (let i = 0; i < 2; i += 1) {
|
|
153
|
+
const p = zonedParts(guess, timeZone);
|
|
154
|
+
const offset = Date.UTC(p.year, p.month, p.day, p.hour, p.minute, p.second) - Math.floor(guess / 1000) * 1000;
|
|
155
|
+
guess = wall - offset;
|
|
156
|
+
}
|
|
157
|
+
return guess;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function validTimeZone(timeZone) {
|
|
161
|
+
try { new Intl.DateTimeFormat("en-US", { timeZone }); return true; } catch (_) { return false; }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function whenParts(at, { now = Date.now(), timeZone = timeZoneDefault() } = {}) {
|
|
165
|
+
const tz = validTimeZone(timeZone) ? timeZone : "UTC";
|
|
166
|
+
const time = new Intl.DateTimeFormat("en-GB", { timeZone: tz, hour: "2-digit", minute: "2-digit", hourCycle: "h23" }).format(new Date(at));
|
|
167
|
+
const day = (t) => { const p = zonedParts(t, tz); return Date.UTC(p.year, p.month, p.day); };
|
|
168
|
+
const days = Math.round((day(at) - day(now)) / 86400000);
|
|
169
|
+
const date = new Intl.DateTimeFormat("en-GB", { timeZone: tz, weekday: "short", day: "numeric", month: "short" }).format(new Date(at)).replace(",", "");
|
|
170
|
+
return { time, days, date };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** "17:20" today, "tomorrow 08:00", "yesterday 12:59", else "Fri 9 Oct, 08:00" (local time). */
|
|
174
|
+
function formatResetTime(at, options = {}) {
|
|
175
|
+
const { time, days, date } = whenParts(at, options);
|
|
176
|
+
if (days === 0) return time;
|
|
177
|
+
if (days === 1) return `tomorrow ${time}`;
|
|
178
|
+
if (days === -1) return `yesterday ${time}`;
|
|
179
|
+
return `${date}, ${time}`;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** The same with its preposition: "at 17:20", "tomorrow at 08:00", "on Fri 9 Oct at 08:00". */
|
|
183
|
+
function whenPhrase(at, options = {}) {
|
|
184
|
+
const { time, days, date } = whenParts(at, options);
|
|
185
|
+
if (days === 0) return `at ${time}`;
|
|
186
|
+
if (days === 1) return `tomorrow at ${time}`;
|
|
187
|
+
if (days === -1) return `yesterday at ${time}`;
|
|
188
|
+
return `on ${date} at ${time}`;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function formatWarning(due, options = {}) {
|
|
192
|
+
const used = Math.min(100, Math.round(due.usedPercent));
|
|
193
|
+
const resets = due.resetsAt ? ` It resets ${whenPhrase(due.resetsAt, options)}.` : "";
|
|
194
|
+
return `⚠️ ${providerLabel(due.provider)}: ${used}% of the ${windowLabel(due.id)} is used.${resets}`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
// When does the limit that stopped a turn reset?
|
|
199
|
+
|
|
200
|
+
function hour24(hour, ampm) {
|
|
201
|
+
let h = Number(hour);
|
|
202
|
+
if (!ampm) return h;
|
|
203
|
+
h %= 12;
|
|
204
|
+
return /pm/i.test(ampm) ? h + 12 : h;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function monthIndex(name) {
|
|
208
|
+
return MONTHS.indexOf(String(name || "").slice(0, 3).toLowerCase());
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Claude Code: "You've hit your session limit · resets 11:30am (Africa/Nairobi)",
|
|
212
|
+
// "… resets Aug 21 at 8am (Africa/Nairobi)", "… resets Oct 9, 5am (UTC)".
|
|
213
|
+
function parseClaudeReset(text, { now, timeZone }) {
|
|
214
|
+
const m = /resets\s+(?:([A-Za-z]{3,9})\.?\s+(\d{1,2})(?:st|nd|rd|th)?,?\s+(?:at\s+)?)?(\d{1,2})(?::(\d{2}))?\s*(am|pm)?(?:\s*\(([^)]+)\))?/i.exec(text);
|
|
215
|
+
if (!m) return null;
|
|
216
|
+
const tz = m[6] && validTimeZone(m[6].trim()) ? m[6].trim() : timeZone;
|
|
217
|
+
const hour = hour24(m[3], m[5]);
|
|
218
|
+
const minute = Number(m[4] || 0);
|
|
219
|
+
if (hour > 23 || minute > 59) return null;
|
|
220
|
+
const today = zonedParts(now, tz);
|
|
221
|
+
if (m[1]) {
|
|
222
|
+
const month = monthIndex(m[1]);
|
|
223
|
+
if (month < 0) return null;
|
|
224
|
+
let at = zonedInstant({ year: today.year, month, day: Number(m[2]), hour, minute }, tz);
|
|
225
|
+
// "Jan 2" read in late December means next year.
|
|
226
|
+
if (at < now - 180 * 86400000) at = zonedInstant({ year: today.year + 1, month, day: Number(m[2]), hour, minute }, tz);
|
|
227
|
+
return at;
|
|
228
|
+
}
|
|
229
|
+
let at = zonedInstant({ year: today.year, month: today.month, day: today.day, hour, minute }, tz);
|
|
230
|
+
if (at <= now) at = zonedInstant({ year: today.year, month: today.month, day: today.day + 1, hour, minute }, tz);
|
|
231
|
+
return at;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Codex: "… or try again at Sep 7th, 2026 5:28 AM." / "try again at 5:28 AM."
|
|
235
|
+
// (the CLI's local time, which is this process's), or "try again in 2 days 3 hours".
|
|
236
|
+
function parseCodexReset(text, { now }) {
|
|
237
|
+
const at = /try again at\s+(?:([A-Za-z]{3,9})\.?\s+(\d{1,2})(?:st|nd|rd|th)?,?\s+(\d{4})\s+)?(\d{1,2}):(\d{2})\s*(AM|PM)?/i.exec(text);
|
|
238
|
+
if (at) {
|
|
239
|
+
const hour = hour24(at[4], at[6]);
|
|
240
|
+
const minute = Number(at[5]);
|
|
241
|
+
if (at[1]) {
|
|
242
|
+
const month = monthIndex(at[1]);
|
|
243
|
+
if (month < 0) return null;
|
|
244
|
+
return new Date(Number(at[3]), month, Number(at[2]), hour, minute).getTime();
|
|
245
|
+
}
|
|
246
|
+
const base = new Date(now);
|
|
247
|
+
let t = new Date(base.getFullYear(), base.getMonth(), base.getDate(), hour, minute).getTime();
|
|
248
|
+
if (t <= now) t += 86400000;
|
|
249
|
+
return t;
|
|
250
|
+
}
|
|
251
|
+
const inMatch = /try again in\s+([^.]+)/i.exec(text);
|
|
252
|
+
if (!inMatch) return null;
|
|
253
|
+
const units = { day: 86400000, hour: 3600000, minute: 60000, min: 60000, second: 1000, sec: 1000 };
|
|
254
|
+
let total = 0;
|
|
255
|
+
for (const part of inMatch[1].matchAll(/(\d+)\s*(day|hour|minute|min|second|sec)s?/gi)) {
|
|
256
|
+
total += Number(part[1]) * units[part[2].toLowerCase()];
|
|
257
|
+
}
|
|
258
|
+
return total > 0 ? now + total : null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function parseResetText(text, { now = Date.now(), timeZone = timeZoneDefault() } = {}) {
|
|
262
|
+
const s = String(text || "");
|
|
263
|
+
if (/resets\s/i.test(s)) return parseClaudeReset(s, { now, timeZone });
|
|
264
|
+
if (/try again (?:at|in)\s/i.test(s)) return parseCodexReset(s, { now });
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function limitTypeFromText(text) {
|
|
269
|
+
const m = /hit your\s+(session|weekly|opus|sonnet|fable)?\s*limit/i.exec(String(text || ""));
|
|
270
|
+
if (!m || !m[1]) return null;
|
|
271
|
+
return { session: "five_hour", weekly: "seven_day", opus: "seven_day_opus", sonnet: "seven_day_sonnet", fable: "seven_day" }[m[1].toLowerCase()] || null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* What stopped a turn and when it lifts. `error` is the run's failure (its
|
|
276
|
+
* details carry Claude's limitResetsAt/limitType), `rateLimit` the run's last
|
|
277
|
+
* Claude reading, `windows` Codex's rollout windows. Returns null when the
|
|
278
|
+
* failure was not a usage limit; resetsAt is null when no reset time is known.
|
|
279
|
+
*/
|
|
280
|
+
function limitHitDetails({ provider, error, rateLimit = null, windows = [], now = Date.now(), timeZone = timeZoneDefault() } = {}) {
|
|
281
|
+
if (!error || error.code !== "PROVIDER_USAGE_LIMIT") return null;
|
|
282
|
+
const details = error.details || {};
|
|
283
|
+
const text = String(details.message || error.message || "");
|
|
284
|
+
let resetsAt = Number.isFinite(details.limitResetsAt) ? details.limitResetsAt : null;
|
|
285
|
+
let limitType = details.limitType || null;
|
|
286
|
+
if (!resetsAt && rateLimit && rateLimit.status === "rejected" && rateLimit.resetsAt) {
|
|
287
|
+
resetsAt = rateLimit.resetsAt;
|
|
288
|
+
limitType = limitType || rateLimit.limitType || null;
|
|
289
|
+
}
|
|
290
|
+
if (!resetsAt) {
|
|
291
|
+
// Codex: the window the plan used up.
|
|
292
|
+
const spent = (windows || []).filter((w) => w.usedPercent >= 100 && w.resetsAt && w.resetsAt > now)
|
|
293
|
+
.sort((a, b) => b.resetsAt - a.resetsAt)[0];
|
|
294
|
+
if (spent) { resetsAt = spent.resetsAt; limitType = limitType || spent.id; }
|
|
295
|
+
}
|
|
296
|
+
if (!resetsAt) resetsAt = parseResetText(text, { now, timeZone });
|
|
297
|
+
return {
|
|
298
|
+
provider,
|
|
299
|
+
limitType: limitType || limitTypeFromText(text),
|
|
300
|
+
resetsAt: Number.isFinite(resetsAt) ? resetsAt : null,
|
|
301
|
+
text,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
module.exports = {
|
|
306
|
+
DEFAULT_FILE,
|
|
307
|
+
DEFAULT_WARN_PERCENTS,
|
|
308
|
+
warnPercents,
|
|
309
|
+
providerLabel,
|
|
310
|
+
windowLabel,
|
|
311
|
+
codexWindows,
|
|
312
|
+
recordWindows,
|
|
313
|
+
knownWindows,
|
|
314
|
+
formatResetTime,
|
|
315
|
+
whenPhrase,
|
|
316
|
+
formatWarning,
|
|
317
|
+
parseResetText,
|
|
318
|
+
limitTypeFromText,
|
|
319
|
+
limitHitDetails,
|
|
320
|
+
zonedInstant,
|
|
321
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inetafrica/open-claudia",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.18",
|
|
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-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-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-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-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-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-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-browser-view.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-browser-view.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"
|
|
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-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-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-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-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-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-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-usage-limits.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-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"
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"bot.js",
|
|
@@ -290,7 +290,28 @@ function claudeEvents(sessionId) {
|
|
|
290
290
|
];
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
if (scenario === "usage-limit") {
|
|
294
|
+
// Claude Code 2.1.280 at a plan limit: the rejected rate_limit_event, the
|
|
295
|
+
// synthetic notice (error "rate_limit") and an error result.
|
|
296
|
+
const resetsAt = integerControl("FAKE_AGENT_LIMIT_RESETS_AT", Math.floor(Date.now() / 1000) + 3600, { min: 0, max: 2 ** 31 });
|
|
297
|
+
const text = process.env.FAKE_AGENT_LIMIT_TEXT || "You've hit your session limit · resets 5pm (UTC)";
|
|
298
|
+
return [
|
|
299
|
+
init,
|
|
300
|
+
{
|
|
301
|
+
type: "rate_limit_event",
|
|
302
|
+
rate_limit_info: { status: "rejected", resetsAt, rateLimitType: process.env.FAKE_AGENT_LIMIT_TYPE || "five_hour", isUsingOverage: false },
|
|
303
|
+
session_id: sessionId,
|
|
304
|
+
},
|
|
305
|
+
{ type: "assistant", error: "rate_limit", message: { model: "<synthetic>", role: "assistant", content: [{ type: "text", text }] }, session_id: sessionId },
|
|
306
|
+
{ type: "result", subtype: "success", is_error: true, session_id: sessionId, result: text },
|
|
307
|
+
];
|
|
308
|
+
}
|
|
309
|
+
|
|
293
310
|
const events = [init];
|
|
311
|
+
// Plan use as Claude Code reports it on a fresh run (JSON rate_limit_info).
|
|
312
|
+
if (process.env.FAKE_AGENT_RATE_LIMIT_INFO) {
|
|
313
|
+
events.push({ type: "rate_limit_event", rate_limit_info: JSON.parse(process.env.FAKE_AGENT_RATE_LIMIT_INFO), session_id: sessionId });
|
|
314
|
+
}
|
|
294
315
|
if (process.env.FAKE_AGENT_THINKING) {
|
|
295
316
|
events.push({
|
|
296
317
|
type: "assistant",
|
|
@@ -589,7 +610,7 @@ async function main() {
|
|
|
589
610
|
setInterval(() => {}, 1000);
|
|
590
611
|
return;
|
|
591
612
|
}
|
|
592
|
-
if (scenario === "auth-failure" || scenario === "refusal") {
|
|
613
|
+
if (scenario === "auth-failure" || scenario === "refusal" || scenario === "usage-limit") {
|
|
593
614
|
process.exitCode = 1;
|
|
594
615
|
return;
|
|
595
616
|
}
|
|
@@ -97,9 +97,11 @@ async function immutableSuccessProbe() {
|
|
|
97
97
|
assert.strictEqual(stateApi.acquireRunLock(state), false, "foreground admission stays closed between compaction stages");
|
|
98
98
|
alphaLane().isCompacting = false;
|
|
99
99
|
const contexts = [];
|
|
100
|
-
const
|
|
100
|
+
const prompts = [];
|
|
101
|
+
const service = serviceWith(async (prompt, _cwd, options) => {
|
|
101
102
|
assert.strictEqual(options.persist, false, "compaction provider stages have no ordinary capture persistence");
|
|
102
103
|
contexts.push(options.runContext);
|
|
104
|
+
prompts.push(String(prompt));
|
|
103
105
|
assert.strictEqual(stateApi.getProviderSession(state, "claude", "alpha"), "old-claude", "pointer stays old during both provider stages");
|
|
104
106
|
if (contexts.length === 1) {
|
|
105
107
|
state.currentSession = { name: "beta", dir: betaDir };
|
|
@@ -125,6 +127,11 @@ async function immutableSuccessProbe() {
|
|
|
125
127
|
assert.strictEqual(contexts[1].timeoutMs, contexts[0].timeoutMs);
|
|
126
128
|
assert.strictEqual(contexts[0].purpose, "compact-summary");
|
|
127
129
|
assert.strictEqual(contexts[0].recallQuery, null, "summary stage carries no recall query");
|
|
130
|
+
// Claude runs the summary stage with --max-turns 1: a tool call (a transcript
|
|
131
|
+
// search, a status check) uses up the one turn and ends it with no brief, so
|
|
132
|
+
// the compaction fails and its cost is wasted (4 of 23 failed on 7-8 Oct).
|
|
133
|
+
assert.strictEqual(contexts[0].maxTurns, 1);
|
|
134
|
+
assert.ok(prompts[0].includes("Do NOT use any tools"), "the one-turn summary stage forbids tool calls");
|
|
128
135
|
assert.strictEqual(contexts[1].purpose, "compact-seed");
|
|
129
136
|
assert.ok(String(contexts[1].recallQuery || "").includes("A sufficiently detailed condensed continuation"),
|
|
130
137
|
"seed recall query carries the condensed brief, not seed-prompt boilerplate");
|
|
@@ -156,6 +163,63 @@ async function immutableSuccessProbe() {
|
|
|
156
163
|
assert.strictEqual(alphaLane().isCompacting, false);
|
|
157
164
|
}
|
|
158
165
|
|
|
166
|
+
// A scheduled job's own conversation (a branch beside the thread's live
|
|
167
|
+
// session) is compacted in place of the live one: the summary reads the
|
|
168
|
+
// branch, the lineage lands in the history and the index, and the thread's
|
|
169
|
+
// live pointer stays where it is. Only the job follows the new session.
|
|
170
|
+
async function branchCompactionProbe() {
|
|
171
|
+
resetFixture("claude");
|
|
172
|
+
state.usageBySession["alpha\0claude\0job-branch"] = { ...stateApi.freshUsage(), turns: 17, liveContextTokens: 894000 };
|
|
173
|
+
const contexts = [];
|
|
174
|
+
const recorded = [];
|
|
175
|
+
const service = serviceWith(async (_prompt, _cwd, options) => {
|
|
176
|
+
contexts.push(options.runContext);
|
|
177
|
+
return result({ sessionId: contexts.length === 1 ? "job-branch" : "job-branch-new" });
|
|
178
|
+
}, {
|
|
179
|
+
commitBranchCompaction: stateApi.commitBranchCompaction,
|
|
180
|
+
recordCompaction: (entry) => recorded.push(entry),
|
|
181
|
+
});
|
|
182
|
+
const compacted = await service.compact(alphaDir, {
|
|
183
|
+
operationId: "compact-branch", sessionId: "job-branch", provider: "claude", compactReason: "cold-resume",
|
|
184
|
+
});
|
|
185
|
+
assert.strictEqual(compacted.compacted, true, JSON.stringify(compacted));
|
|
186
|
+
assert.strictEqual(compacted.branch, true);
|
|
187
|
+
assert.strictEqual(compacted.oldSessionId, "job-branch");
|
|
188
|
+
assert.strictEqual(contexts[0].sessionId, "job-branch", "the summary stage reads the job's conversation");
|
|
189
|
+
assert.strictEqual(contexts[0].resumeSessionId, "job-branch");
|
|
190
|
+
assert.strictEqual(stateApi.getProviderSession(state, "claude", "alpha"), "old-claude", "the thread's live pointer stays");
|
|
191
|
+
const record = stateApi.getProjectSessions(state.userId, "alpha", "claude").find((entry) => entry.id === "job-branch-new");
|
|
192
|
+
assert.deepStrictEqual(record.lineage, {
|
|
193
|
+
type: "compaction",
|
|
194
|
+
operationId: "compact-branch",
|
|
195
|
+
previousSessionId: "job-branch",
|
|
196
|
+
newSessionId: "job-branch-new",
|
|
197
|
+
branch: true,
|
|
198
|
+
});
|
|
199
|
+
assert.strictEqual(state.completedCompactions["compact-branch"].previousSessionId, "job-branch");
|
|
200
|
+
assert.strictEqual(state.completedCompactions["compact-branch"].newSessionId, "job-branch-new");
|
|
201
|
+
const usage = stateApi.getUsageForSession(state, "alpha", "claude", "job-branch-new");
|
|
202
|
+
assert.strictEqual(usage.turns, 0, "the seed is not billed as a turn");
|
|
203
|
+
assert.strictEqual(usage.compactionCheckpoint.previousSessionId, "job-branch");
|
|
204
|
+
assert.ok(usage.lastCompactedAt > 0, "the new session carries the compaction spacing");
|
|
205
|
+
assert.strictEqual(recorded[0].branch, true);
|
|
206
|
+
assert.strictEqual(recorded[0].contextTokensBefore, 894000, "the context it replaced is the branch's, not the thread's");
|
|
207
|
+
assert.strictEqual(runner.recordCompactionUsage({ ...recorded[0] }, { appendUsageRecord: (entry) => entry }).scope, "job", "the ledger marks a job conversation");
|
|
208
|
+
assert.ok(!("scope" in runner.recordCompactionUsage({ compactReason: "threshold", provider: "claude" }, { appendUsageRecord: (entry) => entry })), "and only that");
|
|
209
|
+
assert.strictEqual(alphaLane().isCompacting, false);
|
|
210
|
+
|
|
211
|
+
const again = stateApi.commitBranchCompaction(state, {
|
|
212
|
+
operationId: "compact-branch", canonicalUserId: state.userId, project: "alpha", provider: "claude",
|
|
213
|
+
previousSessionId: "job-branch", newSessionId: "job-branch-new",
|
|
214
|
+
});
|
|
215
|
+
assert.strictEqual(again.duplicate, true, "a repeated commit is a no-op");
|
|
216
|
+
assert.throws(() => stateApi.commitBranchCompaction(state, {
|
|
217
|
+
operationId: "compact-became-live", canonicalUserId: state.userId, project: "alpha", provider: "claude",
|
|
218
|
+
previousSessionId: "old-claude", newSessionId: "other-new",
|
|
219
|
+
}), /became the live session/, "a branch that is now the live session is the thread's to compact");
|
|
220
|
+
assert.strictEqual(stateApi.getProviderSession(state, "claude", "alpha"), "old-claude");
|
|
221
|
+
}
|
|
222
|
+
|
|
159
223
|
async function failureProbe() {
|
|
160
224
|
resetFixture("claude");
|
|
161
225
|
alphaLane().runningProcess = { pid: 123 };
|
|
@@ -492,6 +556,7 @@ function restartRecoveryProbe() {
|
|
|
492
556
|
assert.strictEqual(typeof runner.createCompactionService, "function");
|
|
493
557
|
assert.strictEqual(typeof stateApi.commitProviderCompaction, "function");
|
|
494
558
|
await immutableSuccessProbe();
|
|
559
|
+
await branchCompactionProbe();
|
|
495
560
|
await failureProbe();
|
|
496
561
|
await codexRestrictionProbe();
|
|
497
562
|
await deferredWatchAcrossCompactionProbe();
|