@inetafrica/open-claudia 3.5.16 โ†’ 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.
Files changed (46) hide show
  1. package/.env.example +10 -0
  2. package/CHANGELOG.md +53 -0
  3. package/README.md +4 -0
  4. package/bin/cli.js +1 -1
  5. package/bin/task.js +51 -0
  6. package/bot.js +26 -0
  7. package/channels/kazee/adapter.js +130 -21
  8. package/channels/telegram/adapter.js +4 -2
  9. package/core/actions.js +8 -0
  10. package/core/config.js +22 -1
  11. package/core/cost-report.js +3 -0
  12. package/core/cron-schedule.js +59 -0
  13. package/core/dream.js +141 -34
  14. package/core/guardrail.js +5 -0
  15. package/core/handlers.js +78 -17
  16. package/core/lessons.js +11 -5
  17. package/core/limit-pickup.js +313 -0
  18. package/core/loopback.js +29 -0
  19. package/core/providers/claude-events.js +79 -0
  20. package/core/providers/codex.js +48 -0
  21. package/core/providers/events.js +4 -1
  22. package/core/relationship.js +1 -1
  23. package/core/router.js +16 -1
  24. package/core/run-context.js +3 -0
  25. package/core/runner.js +440 -14
  26. package/core/safe-filename.js +47 -0
  27. package/core/scheduler.js +170 -38
  28. package/core/state.js +91 -0
  29. package/core/system-prompt.js +89 -16
  30. package/core/tasks.js +208 -6
  31. package/core/turn-journal.js +508 -0
  32. package/core/usage-limits.js +321 -0
  33. package/core/utility-agent.js +17 -1
  34. package/package.json +4 -4
  35. package/test-fixtures/fake-agent-cli.js +26 -2
  36. package/test-guardrail.js +19 -0
  37. package/test-kazee-context-media.js +1 -1
  38. package/test-provider-compaction.js +66 -1
  39. package/test-provider-dream.js +3 -1
  40. package/test-provider-events.js +106 -0
  41. package/test-provider-language.js +16 -1
  42. package/test-provider-pack-review.js +3 -1
  43. package/test-provider-scheduler.js +72 -1
  44. package/test-thread-lanes.js +6 -1
  45. package/test-usage-accounting.js +4 -0
  46. package/web.js +104 -51
@@ -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
+ };
@@ -514,6 +514,7 @@ function createUtilityAgentService(options = {}) {
514
514
  activeControllers.add(controller);
515
515
  request = { ...request, signal: controller.signal };
516
516
  let schemaResource = null;
517
+ const providerSchemaResources = [];
517
518
  try {
518
519
  const candidates = policy.candidates({ ...request, purpose });
519
520
  if (request.outputSchema) schemaResource = prepareSchemaFile(request.outputSchema);
@@ -522,6 +523,20 @@ function createUtilityAgentService(options = {}) {
522
523
  const selection = candidates[index];
523
524
  const provider = selection.provider;
524
525
  const effort = providerEffort(provider, request.effort);
526
+ // A provider may need its own dialect of the caller's schema, or none at
527
+ // all (Codex: OpenAI strict mode, 6 Oct 2026). The reply is still checked
528
+ // against the caller's schema after the run.
529
+ let providerSchema = request.outputSchema || null;
530
+ let providerSchemaPath = schemaResource ? schemaResource.file : null;
531
+ if (providerSchema && typeof provider.outputSchemaFor === "function") {
532
+ providerSchema = provider.outputSchemaFor(request.outputSchema) || null;
533
+ if (!providerSchema) providerSchemaPath = null;
534
+ else if (JSON.stringify(providerSchema) !== JSON.stringify(request.outputSchema)) {
535
+ const resource = prepareSchemaFile(providerSchema);
536
+ providerSchemaResources.push(resource);
537
+ providerSchemaPath = resource.file;
538
+ }
539
+ }
525
540
  const systemPrompt = typeof request.systemPrompt === "string" && request.systemPrompt.trim()
526
541
  ? request.systemPrompt
527
542
  : DEFAULT_SYSTEM_PROMPT;
@@ -541,7 +556,7 @@ function createUtilityAgentService(options = {}) {
541
556
  : {}),
542
557
  ...(request.allowedTools !== undefined ? { allowedTools: request.allowedTools } : {}),
543
558
  ...(request.disallowedTools !== undefined ? { disallowedTools: request.disallowedTools } : {}),
544
- ...(request.outputSchema ? { outputSchema: request.outputSchema, outputSchemaPath: schemaResource.file } : {}),
559
+ ...(providerSchema ? { outputSchema: providerSchema, outputSchemaPath: providerSchemaPath } : {}),
545
560
  ...(effort ? { effort } : {}),
546
561
  };
547
562
 
@@ -615,6 +630,7 @@ function createUtilityAgentService(options = {}) {
615
630
  activeControllers.delete(controller);
616
631
  if (externalSignal) externalSignal.removeEventListener("abort", onExternalAbort);
617
632
  if (schemaResource) schemaResource.cleanup();
633
+ for (const resource of providerSchemaResources) resource.cleanup();
618
634
  release(purpose);
619
635
  }
620
636
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "3.5.16",
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": {
@@ -10,8 +10,8 @@
10
10
  "test:kazee-call": "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",
11
11
  "setup": "node setup.js",
12
12
  "start": "node bot.js",
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-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-browser-view.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-browser-view.js"
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-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",
@@ -152,7 +152,7 @@
152
152
  "ws": "^8.21.1"
153
153
  },
154
154
  "dependencies": {
155
- "node-cron": "^4.2.1",
155
+ "node-cron": "^4.6.0",
156
156
  "node-telegram-bot-api": "^0.67.0",
157
157
  "opusscript": "0.1.1",
158
158
  "socket.io-client": "^4.7.5",
@@ -14,7 +14,7 @@ const { spawn, spawnSync } = require("child_process");
14
14
  const argv = process.argv.slice(2);
15
15
  const kind = process.env.FAKE_AGENT_KIND;
16
16
  const scenario = process.env.FAKE_AGENT_SCENARIO || "success";
17
- const text = process.env.FAKE_AGENT_TEXT || "fixture says cafรฉ ๐Ÿ‘‹";
17
+ let text = process.env.FAKE_AGENT_TEXT || "fixture says cafรฉ ๐Ÿ‘‹";
18
18
  let invocationCapturePath = null;
19
19
  let invocationCapture = null;
20
20
 
@@ -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",
@@ -561,6 +582,9 @@ async function main() {
561
582
 
562
583
  // The foreground prompt rides argv, utility prompts ride stdin.
563
584
  const promptText = `${argv.join(" ")}\n${stdin}`;
585
+ // [[placeholder]] in the prompt: answer with the resume placeholder
586
+ // "No response requested." instead of the configured text.
587
+ if (/\[\[placeholder\]\]/.test(promptText)) text = "No response requested.";
564
588
  chunkDelayOverride = promptChunkDelay(promptText);
565
589
  logRun("start", promptText);
566
590
  await sleep(runDelayMs(promptText));
@@ -586,7 +610,7 @@ async function main() {
586
610
  setInterval(() => {}, 1000);
587
611
  return;
588
612
  }
589
- if (scenario === "auth-failure" || scenario === "refusal") {
613
+ if (scenario === "auth-failure" || scenario === "refusal" || scenario === "usage-limit") {
590
614
  process.exitCode = 1;
591
615
  return;
592
616
  }
package/test-guardrail.js CHANGED
@@ -50,6 +50,19 @@ assert.strictEqual(G.detect("Stop the server.").isRule, false, "stop + non-gerun
50
50
  assert.strictEqual(G.detect("Never mind.").isRule, false, "below the word floor");
51
51
  assert.strictEqual(G.detect("Make it a rule?").isRule, false, "a question is not a directive");
52
52
 
53
+ // silence: reassurances open like directives, however they go on. 7 Oct 2026:
54
+ // "Don't worry so much about that." (a voice-note aside) was offered as a
55
+ // standing rule, and "Never mind, ..." read as a never-directive auto-captures.
56
+ assert.strictEqual(G.detect("Don't worry so much about that.").isRule, false, "a reassurance is not a rule");
57
+ assert.strictEqual(G.detect("Ok, don't worry about it.").isRule, false, "reassurance after a soft opener");
58
+ assert.strictEqual(G.detect("Do not worry about the build.").isRule, false);
59
+ assert.strictEqual(G.detect("Never mind, I found it.").isRule, false, "never mind is not a never-directive");
60
+ assert.strictEqual(G.detect("Nevermind, the build passed on its own.").isRule, false);
61
+ // ...while real directives built from the same words still count
62
+ assert.strictEqual(G.detect("Never ever push to main without tests.").confidence, "high");
63
+ assert.strictEqual(G.detect("Never mention the old pricing to customers.").confidence, "high");
64
+ assert.strictEqual(G.detect("Don't use tables in Kazee replies.").confidence, "medium");
65
+
53
66
  // โ”€โ”€ owner gate (hard) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
54
67
  (async () => {
55
68
  resetLessons();
@@ -86,6 +99,12 @@ assert.strictEqual(G.detect("Make it a rule?").isRule, false, "a question is not
86
99
  assert.strictEqual(b.action, "reinforced", "second identical rule reinforces");
87
100
  assert.strictEqual(lessons.readLessons().length, 1, "no duplicate lesson");
88
101
 
102
+ // a reassurance never reaches lessons, though it opens with "never"
103
+ resetLessons();
104
+ const nm = await G.consider({ text: "Never mind, I found it.", speaker: OWNER, announce: null });
105
+ assert.strictEqual(nm.action, "silent", "never mind is neither captured nor asked");
106
+ assert.strictEqual(lessons.readLessons().length, 0, "nothing written for a reassurance");
107
+
89
108
  // โ”€โ”€ C0: medium-confidence asks first (pending), then confirm captures โ”€โ”€
90
109
  resetLessons();
91
110
  const calls2 = [];
@@ -25,7 +25,7 @@ const server = http.createServer((req, res) => {
25
25
  (async () => {
26
26
  await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
27
27
  const origin = `http://127.0.0.1:${server.address().port}`;
28
- const adapter = new KazeeAdapter({ url: "https://chat.invalid", token: "dummy", botUserId: "bot" });
28
+ const adapter = new KazeeAdapter({ url: "https://chat.invalid", token: "dummy", botUserId: "bot", mediaHosts: new URL(origin).host });
29
29
  adapter._chatMeta = async () => ({ isGroupChat: true, assistantAction: "assistant_tagged_reply" });
30
30
  const screenshot = { _id: "screen", chat: "group", type: "image", content: "Trip concern", createdAt: "2026-09-07T13:56:04Z",
31
31
  sender: { _id: "samson", name: "Samson" }, medias: [{ _id: "photo", chat: "group", type: "image", url: `${origin}/image.png`, fileName: "image.png" }] };