@inetafrica/open-claudia 3.5.17 → 3.5.19
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 +27 -0
- package/README.md +3 -0
- package/bot.js +7 -0
- package/core/config.js +9 -6
- package/core/dedup-adjudicator.js +42 -19
- 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/recall-filter.js +28 -9
- 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/core/utility-agent.js +7 -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 +14 -1
- package/test-provider-recall.js +38 -5
- package/test-provider-scheduler.js +69 -0
- package/test-usage-accounting.js +4 -0
- package/test-utility-provider-policy.js +27 -0
package/core/scheduler.js
CHANGED
|
@@ -33,6 +33,7 @@ const {
|
|
|
33
33
|
getProjectKey,
|
|
34
34
|
getProviderSession,
|
|
35
35
|
getProviderSettings,
|
|
36
|
+
getProjectSessions,
|
|
36
37
|
getUserState,
|
|
37
38
|
peekLane,
|
|
38
39
|
laneBusy,
|
|
@@ -247,6 +248,7 @@ function createSchedulerService(dependencies = {}) {
|
|
|
247
248
|
: dependencies.legacyCronsFile;
|
|
248
249
|
const runInProvider = dependencies.runAgent || ((...args) => require("./runner").runAgent(...args));
|
|
249
250
|
const admit = dependencies.admitRunContext || ((...args) => require("./runner").admitRunContext(...args));
|
|
251
|
+
const projectSessions = dependencies.getProjectSessions || getProjectSessions;
|
|
250
252
|
const timers = new Map();
|
|
251
253
|
const checksInFlight = new Set();
|
|
252
254
|
const firesInFlight = new Set();
|
|
@@ -335,6 +337,26 @@ function createSchedulerService(dependencies = {}) {
|
|
|
335
337
|
return getProviderSession(state, job.provider, roomKey) || null;
|
|
336
338
|
}
|
|
337
339
|
|
|
340
|
+
// previous session -> the session it was compacted into, from the
|
|
341
|
+
// conversation's session history (each compaction's lineage is kept on the
|
|
342
|
+
// new session's record). The completed-compactions index holds only the
|
|
343
|
+
// last 20, under a day of token-diet compactions across every thread, so a
|
|
344
|
+
// job made before them lost its way: 7 Oct the PBX thread's wakeup and
|
|
345
|
+
// watch resumed a 790k conversation compacted that morning ($86).
|
|
346
|
+
function historySuccessors(state, job) {
|
|
347
|
+
const successors = new Map();
|
|
348
|
+
let records = [];
|
|
349
|
+
try { records = projectSessions(state?.userId || job.canonicalUserId, job.project, job.provider) || []; }
|
|
350
|
+
catch (_) { records = []; }
|
|
351
|
+
// Newest first: if a session was ever compacted twice, the latest wins.
|
|
352
|
+
for (const record of records) {
|
|
353
|
+
const lineage = record?.lineage;
|
|
354
|
+
if (lineage?.type !== "compaction" || !lineage.previousSessionId || !lineage.newSessionId) continue;
|
|
355
|
+
if (!successors.has(lineage.previousSessionId)) successors.set(lineage.previousSessionId, lineage.newSessionId);
|
|
356
|
+
}
|
|
357
|
+
return successors;
|
|
358
|
+
}
|
|
359
|
+
|
|
338
360
|
function resolveCompactionLineage(state, job) {
|
|
339
361
|
let sessionId = job.sessionId || null;
|
|
340
362
|
// A thread job never resumes its room's MAIN session: a job made in a
|
|
@@ -347,6 +369,7 @@ function createSchedulerService(dependencies = {}) {
|
|
|
347
369
|
sessionId = own && own !== mainSession ? own : null;
|
|
348
370
|
}
|
|
349
371
|
if (!sessionId) return { sessionId: null, attachSession: true };
|
|
372
|
+
let recorded = null;
|
|
350
373
|
const seen = new Set();
|
|
351
374
|
while (sessionId && !seen.has(sessionId)) {
|
|
352
375
|
seen.add(sessionId);
|
|
@@ -355,9 +378,9 @@ function createSchedulerService(dependencies = {}) {
|
|
|
355
378
|
&& entry?.project === job.project
|
|
356
379
|
&& entry?.previousSessionId === sessionId
|
|
357
380
|
&& entry?.newSessionId
|
|
358
|
-
));
|
|
381
|
+
))?.newSessionId || (recorded ||= historySuccessors(state, job)).get(sessionId);
|
|
359
382
|
if (!successor) break;
|
|
360
|
-
sessionId = successor
|
|
383
|
+
sessionId = successor;
|
|
361
384
|
}
|
|
362
385
|
const live = getProviderSession(state, job.provider, job.project);
|
|
363
386
|
return {
|
|
@@ -694,6 +717,14 @@ function createSchedulerService(dependencies = {}) {
|
|
|
694
717
|
lastProvider: target.provider,
|
|
695
718
|
fallbackFromProvider: target.fallbackFrom,
|
|
696
719
|
};
|
|
720
|
+
// A recurring job keeps its own conversation: remember the session this
|
|
721
|
+
// run resumed and ended on (lineage or a compaction may have moved it), so
|
|
722
|
+
// the next run starts there even after the lineage records age out. Only
|
|
723
|
+
// for a run that resumed the job's conversation under its own provider.
|
|
724
|
+
const ranOn = typeof result?.sessionId === "string" && result.sessionId ? result.sessionId : null;
|
|
725
|
+
if (!isOneShot(job) && ranOn && ranOn !== job.sessionId && target.sessionId && !target.fresh && target.provider === job.provider) {
|
|
726
|
+
successPatch.sessionId = ranOn;
|
|
727
|
+
}
|
|
697
728
|
let marked;
|
|
698
729
|
try { marked = jobsStore.update(job.id, successPatch); }
|
|
699
730
|
catch (error) { return { ok: false, status: "failed", error: stableFailure(error), result }; }
|
package/core/state.js
CHANGED
|
@@ -1671,6 +1671,96 @@ function commitProviderCompaction(state, transaction, options = {}) {
|
|
|
1671
1671
|
}
|
|
1672
1672
|
}
|
|
1673
1673
|
|
|
1674
|
+
// A scheduled job resumes its own conversation: a branch beside its thread's
|
|
1675
|
+
// live session once the thread has moved on. Compacting that branch records
|
|
1676
|
+
// the lineage (on the new session's history record and in the completed-
|
|
1677
|
+
// compactions index) so the job moves on to the new session, but never
|
|
1678
|
+
// touches the thread's live pointer: the thread is not the job's conversation.
|
|
1679
|
+
function commitBranchCompaction(state, transaction, options = {}) {
|
|
1680
|
+
const tx = validateCompactionTransaction(state, transaction);
|
|
1681
|
+
if (state.completedCompactions?.[tx.operationId]) {
|
|
1682
|
+
return {
|
|
1683
|
+
committed: true,
|
|
1684
|
+
duplicate: true,
|
|
1685
|
+
operationId: tx.operationId,
|
|
1686
|
+
sessionId: state.completedCompactions[tx.operationId].newSessionId,
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
if (getProviderSession(state, tx.provider, tx.project) === tx.previousSessionId) {
|
|
1690
|
+
throw compactionError("COMPACTION_POINTER_CHANGED", "the branch became the live session before its compaction commit");
|
|
1691
|
+
}
|
|
1692
|
+
const persistState = options.persistState || saveState;
|
|
1693
|
+
const recordHistory = options.recordHistory || recordSession;
|
|
1694
|
+
const nowValue = typeof options.now === "function" ? options.now() : new Date();
|
|
1695
|
+
const now = nowValue instanceof Date ? nowValue : new Date(nowValue);
|
|
1696
|
+
if (!Number.isFinite(now.getTime())) throw compactionError("INVALID_COMPACTION", "compaction clock is invalid");
|
|
1697
|
+
requireCompactionWrite(
|
|
1698
|
+
recordHistory(
|
|
1699
|
+
tx.canonicalUserId,
|
|
1700
|
+
tx.project,
|
|
1701
|
+
tx.provider,
|
|
1702
|
+
tx.newSessionId,
|
|
1703
|
+
tx.title || `Scheduled job conversation, compacted ${now.toLocaleDateString()}`,
|
|
1704
|
+
{
|
|
1705
|
+
strict: true,
|
|
1706
|
+
now: () => now,
|
|
1707
|
+
lineage: {
|
|
1708
|
+
type: "compaction",
|
|
1709
|
+
operationId: tx.operationId,
|
|
1710
|
+
previousSessionId: tx.previousSessionId,
|
|
1711
|
+
newSessionId: tx.newSessionId,
|
|
1712
|
+
branch: true,
|
|
1713
|
+
},
|
|
1714
|
+
},
|
|
1715
|
+
),
|
|
1716
|
+
"branch compaction history",
|
|
1717
|
+
);
|
|
1718
|
+
const newUsageKey = usageSessionKey(tx.project, tx.provider, tx.newSessionId);
|
|
1719
|
+
if (!isRecord(state.usageBySession)) state.usageBySession = {};
|
|
1720
|
+
const previousUsageBySession = state.usageBySession;
|
|
1721
|
+
const previousCompleted = state.completedCompactions;
|
|
1722
|
+
const nextUsage = {
|
|
1723
|
+
...freshUsage(),
|
|
1724
|
+
lastCompactedAt: now.getTime(),
|
|
1725
|
+
compactionCheckpoint: {
|
|
1726
|
+
operationId: tx.operationId,
|
|
1727
|
+
previousSessionId: tx.previousSessionId,
|
|
1728
|
+
newSessionId: tx.newSessionId,
|
|
1729
|
+
at: now.toISOString(),
|
|
1730
|
+
branch: true,
|
|
1731
|
+
},
|
|
1732
|
+
};
|
|
1733
|
+
if (tx.provider === "codex" && isRecord(tx.seedUsage)) {
|
|
1734
|
+
nextUsage.providerUsageSnapshot = {
|
|
1735
|
+
input_tokens: Number(tx.seedUsage.input_tokens) || 0,
|
|
1736
|
+
cached_input_tokens: Number(tx.seedUsage.cached_input_tokens) || 0,
|
|
1737
|
+
output_tokens: Number(tx.seedUsage.output_tokens) || 0,
|
|
1738
|
+
};
|
|
1739
|
+
}
|
|
1740
|
+
state.usageBySession = boundUsageBySession({ ...previousUsageBySession, [newUsageKey]: nextUsage });
|
|
1741
|
+
state.completedCompactions = Object.fromEntries(Object.entries({
|
|
1742
|
+
...cloneRecord(previousCompleted),
|
|
1743
|
+
[tx.operationId]: {
|
|
1744
|
+
project: tx.project,
|
|
1745
|
+
provider: tx.provider,
|
|
1746
|
+
previousSessionId: tx.previousSessionId,
|
|
1747
|
+
newSessionId: tx.newSessionId,
|
|
1748
|
+
committedAt: now.toISOString(),
|
|
1749
|
+
branch: true,
|
|
1750
|
+
},
|
|
1751
|
+
}).slice(-20));
|
|
1752
|
+
try {
|
|
1753
|
+
requireCompactionWrite(persistState({ strict: true }), "branch compaction state");
|
|
1754
|
+
} catch (error) {
|
|
1755
|
+
// The history record stays: the new session IS the branch's compacted
|
|
1756
|
+
// continuation, and the scheduler follows it from there.
|
|
1757
|
+
state.usageBySession = previousUsageBySession;
|
|
1758
|
+
state.completedCompactions = previousCompleted;
|
|
1759
|
+
throw error;
|
|
1760
|
+
}
|
|
1761
|
+
return { committed: true, duplicate: false, branch: true, operationId: tx.operationId, sessionId: tx.newSessionId };
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1674
1764
|
function migrateUserData(fromUserId, toUserId) {
|
|
1675
1765
|
const from = normalizeCanonicalUserId(fromUserId);
|
|
1676
1766
|
const to = normalizeCanonicalUserId(toUserId);
|
|
@@ -1828,6 +1918,7 @@ module.exports = {
|
|
|
1828
1918
|
createSessionCallbackToken,
|
|
1829
1919
|
consumeSessionCallbackToken,
|
|
1830
1920
|
commitProviderCompaction,
|
|
1921
|
+
commitBranchCompaction,
|
|
1831
1922
|
recoverPendingCompaction,
|
|
1832
1923
|
migrateUserData,
|
|
1833
1924
|
linkIdentity,
|
package/core/turn-journal.js
CHANGED
|
@@ -498,6 +498,8 @@ function _resetForTests() {
|
|
|
498
498
|
|
|
499
499
|
module.exports = {
|
|
500
500
|
journalable, begin, end, enqueue, dropQueued, beat, list, fileFor,
|
|
501
|
+
// Shared with core/limit-pickup.js, which re-runs a message the same way.
|
|
502
|
+
conversationOf, chatScope, findAdapter, defaultDeps,
|
|
501
503
|
resumeInterrupted, handleResumeAction, RESUME_PROMPT,
|
|
502
504
|
rememberDropped, droppedKeyboard, handleRunDropped,
|
|
503
505
|
drain, exitAfterDrain, isDraining, markShuttingDown,
|
|
@@ -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/core/utility-agent.js
CHANGED
|
@@ -123,6 +123,13 @@ function validateSchema(value, schema, location = "$", depth = 0) {
|
|
|
123
123
|
|
|
124
124
|
function prepareSchemaFile(schema) {
|
|
125
125
|
if (!schema) return null;
|
|
126
|
+
// Claude Code turns the schema into a tool whose input_schema the API
|
|
127
|
+
// requires to be an object, and OpenAI strict mode wants an object root too.
|
|
128
|
+
// Any other root is refused on every call, which a fail-open caller shows
|
|
129
|
+
// only as a warning (the dedup adjudicator's array root, 3.2.2 to 3.5.18).
|
|
130
|
+
if (typeof schema !== "object" || Array.isArray(schema) || schema.type !== "object") {
|
|
131
|
+
throw new UtilityAgentError("INVALID_UTILITY_SCHEMA", 'outputSchema must have top-level type "object"');
|
|
132
|
+
}
|
|
126
133
|
let serialized;
|
|
127
134
|
try { serialized = JSON.stringify(schema); }
|
|
128
135
|
catch (_) { throw new UtilityAgentError("INVALID_UTILITY_SCHEMA", "outputSchema must be JSON-serializable"); }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inetafrica/open-claudia",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.19",
|
|
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",
|