@inetafrica/open-claudia 3.1.28 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+
3
+ // C2: memory regression suite — every corrected recall miss becomes a
4
+ // permanent test case, re-graded nightly. Cases live in a JSONL store; the
5
+ // nightly runner grades each query against the DETERMINISTIC recall floor
6
+ // (matchPacks + matchEntities — the classic candidate stage every engine
7
+ // builds on; the discoverer's LLM walk only adds to it and is fail-open back
8
+ // to this). No model call anywhere: the suite must run — and mean the same
9
+ // thing — even on nights the providers are down. Pass-rate + delta land in
10
+ // the dream summary's memory-health section.
11
+
12
+ const fs = require("fs");
13
+ const path = require("path");
14
+ const { atomicWriteFileSync } = require("./fsutil");
15
+ const { resolveConfigDir, ensureConfigDir } = require("./config-dir");
16
+
17
+ const NIGHTLY_CAP = Number(process.env.RECALL_REGRESSION_CAP || 20);
18
+ const HISTORY_MAX = 90;
19
+
20
+ function casesPath() { return path.join(resolveConfigDir(), "recall-regressions.jsonl"); }
21
+ function historyPath() { return path.join(resolveConfigDir(), "recall-health-history.json"); }
22
+
23
+ function loadCases() {
24
+ try {
25
+ return fs.readFileSync(casesPath(), "utf-8")
26
+ .split("\n").filter(Boolean)
27
+ .map((l) => { try { return JSON.parse(l); } catch (e) { return null; } })
28
+ .filter(Boolean);
29
+ } catch (e) {
30
+ return [];
31
+ }
32
+ }
33
+
34
+ function saveCases(cases) {
35
+ ensureConfigDir();
36
+ atomicWriteFileSync(casesPath(), cases.map((c) => JSON.stringify(c)).join("\n") + (cases.length ? "\n" : ""));
37
+ }
38
+
39
+ function addCase({ query, expectSlugs, source = "manual", note = "" } = {}) {
40
+ const q = String(query || "").trim();
41
+ if (!q) throw new Error("query is required");
42
+ const expect = [].concat(expectSlugs || []).map((s) => String(s || "").trim()).filter(Boolean);
43
+ if (expect.length === 0) throw new Error("at least one expected slug is required");
44
+ if (source !== "manual" && source !== "live-miss") throw new Error(`source must be manual or live-miss (got ${source})`);
45
+ const entry = {
46
+ id: `rc_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
47
+ query: q,
48
+ expectSlugs: expect,
49
+ source,
50
+ note: String(note || ""),
51
+ addedAt: new Date().toISOString(),
52
+ lastRunAt: null,
53
+ lastResult: null,
54
+ };
55
+ const cases = loadCases();
56
+ cases.push(entry);
57
+ saveCases(cases);
58
+ return entry;
59
+ }
60
+
61
+ function listCases() {
62
+ return loadCases();
63
+ }
64
+
65
+ function removeCase(id) {
66
+ const cases = loadCases();
67
+ const next = cases.filter((c) => c.id !== id);
68
+ if (next.length === cases.length) return false;
69
+ saveCases(next);
70
+ return true;
71
+ }
72
+
73
+ // Grade one case against the deterministic floor. Expected slugs that no
74
+ // longer exist anywhere (pack merged away, entity removed) are not recall
75
+ // failures — a case with NO live target is "orphaned"; live targets are
76
+ // graded normally.
77
+ function gradeCase(c, { packLimit, entityLimit } = {}) {
78
+ const packs = require("./packs");
79
+ const entities = require("./entities");
80
+ const pl = packLimit || Number(process.env.PACK_MATCH_LIMIT || 6);
81
+ const el = entityLimit || 4;
82
+ const exists = (slug) => {
83
+ try { if (packs.readPack(slug)) return true; } catch (e) {}
84
+ try { if (entities.readEntity(slug)) return true; } catch (e) {}
85
+ return false;
86
+ };
87
+ const live = c.expectSlugs.filter(exists);
88
+ if (live.length === 0) return { status: "orphaned", missing: c.expectSlugs.slice() };
89
+ const found = new Set();
90
+ try { for (const m of packs.matchPacks(c.query, { limit: pl })) found.add(m.dir); } catch (e) {}
91
+ try { for (const m of entities.matchEntities(c.query, { limit: el })) found.add(m.slug); } catch (e) {}
92
+ const missing = live.filter((s) => !found.has(s));
93
+ return missing.length === 0
94
+ ? { status: "pass" }
95
+ : { status: "fail", missing, matched: [...found] };
96
+ }
97
+
98
+ // Nightly-capped, oldest-run-first rotation: a store bigger than the cap
99
+ // still gets full coverage over successive nights. Orphaned cases are
100
+ // dropped from the store (same policy as prospective-reminder GC).
101
+ function runSuite({ cap = NIGHTLY_CAP, packLimit, entityLimit, now = Date.now() } = {}) {
102
+ const all = loadCases();
103
+ if (all.length === 0) return { ran: 0, passed: 0, failed: 0, orphaned: 0, failures: [], total: 0 };
104
+ const order = [...all].sort((x, y) => String(x.lastRunAt || "").localeCompare(String(y.lastRunAt || "")));
105
+ const toRun = order.slice(0, Math.max(1, cap));
106
+ const stamp = new Date(now).toISOString();
107
+ const failures = [];
108
+ const orphanIds = new Set();
109
+ let passed = 0, failed = 0;
110
+ for (const c of toRun) {
111
+ const g = gradeCase(c, { packLimit, entityLimit });
112
+ c.lastRunAt = stamp;
113
+ c.lastResult = g.status;
114
+ if (g.status === "pass") passed++;
115
+ else if (g.status === "fail") { failed++; failures.push({ id: c.id, query: c.query, missing: g.missing }); }
116
+ else orphanIds.add(c.id);
117
+ }
118
+ const kept = all.filter((c) => !orphanIds.has(c.id));
119
+ saveCases(kept);
120
+ return { ran: toRun.length - orphanIds.size, passed, failed, orphaned: orphanIds.size, failures, total: kept.length };
121
+ }
122
+
123
+ function loadHistory() {
124
+ try {
125
+ const h = JSON.parse(fs.readFileSync(historyPath(), "utf-8"));
126
+ return Array.isArray(h) ? h : [];
127
+ } catch (e) {
128
+ return [];
129
+ }
130
+ }
131
+
132
+ // Dream entry point: grade, record the trend, and return announce lines for
133
+ // the consolidation report. Empty store → zero cost, zero lines.
134
+ function nightlyRun(opts = {}) {
135
+ const res = runSuite(opts);
136
+ if (res.ran === 0 && res.orphaned === 0) return { ...res, passRate: null, lines: [] };
137
+ const history = loadHistory();
138
+ const prev = history.length ? history[history.length - 1] : null;
139
+ const rate = res.ran > 0 ? Math.round((res.passed / res.ran) * 100) : null;
140
+ if (res.ran > 0) {
141
+ history.push({ at: new Date().toISOString(), ran: res.ran, passed: res.passed, failed: res.failed, orphaned: res.orphaned, passRate: rate });
142
+ ensureConfigDir();
143
+ atomicWriteFileSync(historyPath(), JSON.stringify(history.slice(-HISTORY_MAX), null, 2) + "\n");
144
+ }
145
+ const lines = [];
146
+ if (res.ran > 0) {
147
+ const delta = prev && typeof prev.passRate === "number" && rate !== null ? rate - prev.passRate : null;
148
+ const deltaTxt = delta === null ? "" : ` (${delta >= 0 ? "+" : ""}${delta} vs last run)`;
149
+ let line = `🧪 Memory health: ${res.passed}/${res.ran} recall regression case${res.ran === 1 ? "" : "s"} passing — ${rate}%${deltaTxt}.`;
150
+ if (res.failed > 0) {
151
+ const detail = res.failures.slice(0, 3).map((f) => `"${f.query.slice(0, 40)}" (missing ${f.missing.join(", ")})`).join("; ");
152
+ line += ` Failing: ${detail}${res.failures.length > 3 ? ` +${res.failures.length - 3} more` : ""} — open-claudia recall-case list`;
153
+ }
154
+ lines.push(line);
155
+ }
156
+ if (res.orphaned > 0) lines.push(`🧪 Dropped ${res.orphaned} recall regression case${res.orphaned === 1 ? "" : "s"} whose target no longer exists.`);
157
+ return { ...res, passRate: rate, lines };
158
+ }
159
+
160
+ module.exports = {
161
+ NIGHTLY_CAP,
162
+ addCase,
163
+ listCases,
164
+ removeCase,
165
+ gradeCase,
166
+ runSuite,
167
+ nightlyRun,
168
+ loadHistory,
169
+ casesPath,
170
+ historyPath,
171
+ };
@@ -7,7 +7,7 @@ const { SOUL_FILE, CRONS_FILE, VAULT_FILE, FILES_DIR, BOT_DIR, config } = requir
7
7
  const { canSpeak } = require("./media");
8
8
  const CONFIG_DIR = require("../config-dir");
9
9
  const { currentState, getProjectKey, getProviderSession } = require("./state");
10
- const { currentAdapter, currentChannelId, currentUserId } = require("./context");
10
+ const { currentAdapter, currentChannelId, currentUserId, currentSpeakerName } = require("./context");
11
11
  const { vault } = require("./vault-store");
12
12
  const {
13
13
  transcriptPointerNoteForRun,
@@ -369,6 +369,20 @@ function buildSpeakerContextBlock() {
369
369
  const speaker = adapter && channelId
370
370
  ? people.findByHandle(adapter.type, String(currentUserId() || channelId))
371
371
  : null;
372
+ // Without a people record the speaker used to render as "no named profile"
373
+ // even when identity was known: an env-configured owner (KAZEE_OWNER_USER_ID,
374
+ // never auto-contacted) or a not-yet-recorded group member. In group chats
375
+ // the model then rightly refused to attribute their messages. Fall back to
376
+ // the canonical owner resolution the guard stack uses, then to the
377
+ // transport-reported display name. Names never confer privileges (R1):
378
+ // owner standing comes only from the canonical check, and a bare transport
379
+ // name is labelled unverified.
380
+ let canonicalSpeaker = null;
381
+ if (!speaker && adapter && channelId) {
382
+ try { canonicalSpeaker = require("./relationship").speakerFor(adapter.type, String(channelId), currentUserId()); } catch (e) {}
383
+ }
384
+ let transportName = "";
385
+ try { transportName = String(currentSpeakerName() || "").trim(); } catch (e) {}
372
386
  if (speaker) {
373
387
  const recentNotes = (speaker.notes || [])
374
388
  .slice(-3)
@@ -381,6 +395,23 @@ function buildSpeakerContextBlock() {
381
395
  speaker.bio ? `Bio: ${speaker.bio}` : "",
382
396
  recentNotes ? `Recent notes:\n${recentNotes}` : "",
383
397
  );
398
+ } else if (canonicalSpeaker && canonicalSpeaker.isOwner) {
399
+ // Prefer the live transport name: canonicalSpeaker.name falls back to
400
+ // owners()[0], which can be a different owner on multi-owner pods.
401
+ const name = transportName || canonicalSpeaker.name;
402
+ blocks.push(
403
+ "",
404
+ "## Speaker",
405
+ name
406
+ ? `You are talking to ${name} (the owner) on ${adapter.type}.`
407
+ : `You are talking to the owner on ${adapter.type}.`,
408
+ );
409
+ } else if (transportName) {
410
+ blocks.push(
411
+ "",
412
+ "## Speaker",
413
+ `The current message is from "${transportName}" on ${adapter.type} — transport-reported name, no saved profile; identity unverified.`,
414
+ );
384
415
  } else {
385
416
  blocks.push("", "## Speaker", "No named speaker profile is available for this turn.");
386
417
  }
@@ -651,6 +682,22 @@ function formatPackForContext(pack, packsLib, opts = {}) {
651
682
  return clip(parts.join("\n\n"), PACK_INJECT_MAX_CHARS);
652
683
  }
653
684
 
685
+ // C1 prospective memory: topic-bound reminders ride the recall injection of
686
+ // their target pack/entity. Fetched at render time but CONSUMED only after
687
+ // the budget accepts the committed block — a dropped render must never eat a
688
+ // one-shot reminder. All calls are defensive: reminder machinery failing can
689
+ // never break recall injection itself.
690
+ function pendingRemindersFor(type, slug) {
691
+ try { return require("./prospective").pendingFor(type, slug); } catch (e) { return []; }
692
+ }
693
+ function renderReminders(entries) {
694
+ try { return require("./prospective").renderPendingLines(entries); } catch (e) { return ""; }
695
+ }
696
+ function fireReminders(entries) {
697
+ if (!entries || entries.length === 0) return;
698
+ try { require("./prospective").fire(entries, "recall"); } catch (e) {}
699
+ }
700
+
654
701
  function buildPackBlock(matches, budget, opts = {}) {
655
702
  try {
656
703
  const packsLib = require("./packs");
@@ -671,17 +718,29 @@ function buildPackBlock(matches, budget, opts = {}) {
671
718
  used.push(m.dir);
672
719
  const key = `${adapter?.id || "?"}:${channelId || "?"}:${m.dir}`;
673
720
  const stamp = `${runIdentity}:${pack.updated}`;
721
+ const pending = pendingRemindersFor("pack", m.dir);
722
+ const pendingText = pending.length ? renderReminders(pending) : "";
674
723
  // Inject a pack's body once per (channel, session, version). A
675
724
  // compaction mints a new session id, which changes the stamp and
676
725
  // forces a fresh re-injection on the next turn (same mechanism
677
726
  // as the task tree), so the pack survives compaction without paying
678
727
  // to re-stamp an anchor on every intervening turn.
679
- if (packsInjectedFor.get(key) === stamp) continue;
680
- const block = formatPackForContext(pack, packsLib, { progressive, headline, extractive, matchedLines: extractive ? (matchedByDir.get(m.dir) || []) : undefined });
728
+ if (packsInjectedFor.get(key) === stamp) {
729
+ // C1: a reminder doesn't bump pack.updated, so the dedupe stamp
730
+ // would silently swallow it — surface a tiny standalone block.
731
+ if (pendingText) {
732
+ const note = `### Pack: ${pack.name || m.dir} (${m.dir})\n${pendingText}`;
733
+ if (tryUseRecallBudget(budget, note)) { blocks.push(note); fireReminders(pending); }
734
+ }
735
+ continue;
736
+ }
737
+ let block = formatPackForContext(pack, packsLib, { progressive, headline, extractive, matchedLines: extractive ? (matchedByDir.get(m.dir) || []) : undefined });
738
+ if (pendingText) block += `\n\n${pendingText}`;
681
739
  if (!tryUseRecallBudget(budget, block)) continue;
682
740
  packsInjectedFor.set(key, stamp);
683
741
  collector.packs.push(pack.name || m.dir);
684
742
  blocks.push(block);
743
+ if (pendingText) fireReminders(pending);
685
744
  }
686
745
  if (used.length > 0) setImmediate(() => { try { packsLib.touchUsed(used); } catch (e) {} });
687
746
  if (blocks.length === 0) return "";
@@ -738,12 +797,23 @@ function buildEntityBlock(matches, budget, opts = {}) {
738
797
  seen.push(m.slug);
739
798
  const key = `${adapter?.id || "?"}:${channelId || "?"}:${m.slug}`;
740
799
  const stamp = `${runIdentity}:${ent.updated}`;
741
- if (entitiesInjectedFor.get(key) === stamp) continue;
742
- const block = formatEntityForContext(ent, { headline: recallHeadline(), slug: m.slug });
800
+ const pending = pendingRemindersFor("entity", m.slug);
801
+ const pendingText = pending.length ? renderReminders(pending) : "";
802
+ if (entitiesInjectedFor.get(key) === stamp) {
803
+ // C1: same as packs — a reminder must survive the dedupe stamp.
804
+ if (pendingText) {
805
+ const note = `### ${ent.name} (${m.slug})\n${pendingText}`;
806
+ if (tryUseRecallBudget(budget, note)) { blocks.push(note); fireReminders(pending); }
807
+ }
808
+ continue;
809
+ }
810
+ let block = formatEntityForContext(ent, { headline: recallHeadline(), slug: m.slug });
811
+ if (pendingText) block += `\n\n${pendingText}`;
743
812
  if (!tryUseRecallBudget(budget, block)) continue;
744
813
  entitiesInjectedFor.set(key, stamp);
745
814
  collector.entities.push(ent.name || m.slug);
746
815
  blocks.push(block);
816
+ if (pendingText) fireReminders(pending);
747
817
  }
748
818
  if (seen.length > 0) setImmediate(() => { try { entitiesLib.touchSeen(seen); } catch (e) {} });
749
819
  if (blocks.length === 0) return "";
package/core/tools.js CHANGED
@@ -168,6 +168,82 @@ function fingerprint(name) {
168
168
  return { status: "changed", current, verified: s.verifiedHash, verifiedAt: s.verifiedAt || "" };
169
169
  }
170
170
 
171
+ // ── Skill certification (C3) ─────────────────────────────────────────────────
172
+ // Per-verb trust tier derived purely from the run telemetry above. Earned, not
173
+ // granted: "certified" needs ≥CERTIFY_CLEAN_RUNS clean runs with no failure in
174
+ // the probation window; any recent failure demotes to "probation"; everything
175
+ // else is "learning". Derived data + report-only proposals ONLY — certification
176
+ // never loosens a gate or grants anything by itself; the owner acts through the
177
+ // existing approval-card/guardrails flows or ignores it.
178
+ const CERTIFY_CLEAN_RUNS = 5;
179
+ const PROBATION_WINDOW_MS = 7 * 86400000;
180
+
181
+ // Mirrors recordRunResult's verb extraction so history lines re-attribute to
182
+ // the same verb key they were counted under.
183
+ function historyVerb(argLine) {
184
+ const first = String(argLine || "").split(/\s+/).find((a) => a && !a.startsWith("-")) || "(default)";
185
+ return first.slice(0, 40);
186
+ }
187
+
188
+ function cleanRunsSince(verb, history, sinceT) {
189
+ let n = 0;
190
+ for (const h of Array.isArray(history) ? history : []) {
191
+ const t = Date.parse(h.at || "");
192
+ if (!Number.isFinite(t) || t <= sinceT) continue;
193
+ if (h.exit !== 0) continue;
194
+ if (historyVerb(h.args) === verb) n += 1;
195
+ }
196
+ return n;
197
+ }
198
+
199
+ function verbCertification(verb, v = {}, history = [], now = Date.now()) {
200
+ const runs = Number(v.runs) || 0;
201
+ const lastFailureT = Date.parse(v.lastFailure || "");
202
+ const failed = Number.isFinite(lastFailureT);
203
+ if (failed && now - lastFailureT < PROBATION_WINDOW_MS) {
204
+ return { tier: "probation", cleanRuns: cleanRunsSince(verb, history, lastFailureT), runs };
205
+ }
206
+ // No failure ever → every recorded run was clean. After an old failure only
207
+ // successes SINCE it count toward re-certification (history-capped, so this
208
+ // is an honest lower bound).
209
+ const cleanRuns = failed ? cleanRunsSince(verb, history, lastFailureT) : runs;
210
+ return { tier: cleanRuns >= CERTIFY_CLEAN_RUNS ? "certified" : "learning", cleanRuns, runs };
211
+ }
212
+
213
+ function certificationForState(s = {}, now = Date.now()) {
214
+ const out = {};
215
+ for (const [verb, v] of Object.entries(s.verbs || {})) {
216
+ out[verb] = verbCertification(verb, v, s.history, now);
217
+ }
218
+ return out;
219
+ }
220
+
221
+ function certification(name, now = Date.now()) {
222
+ return certificationForState(readState(name), now);
223
+ }
224
+
225
+ // Dream phase (report-only, max 2/night): certified verbs on gated tiers are
226
+ // candidates for a standing Always-allow grant. Read-only verbs run freely and
227
+ // need no grant; probation/learning verbs haven't earned a proposal. The line
228
+ // is the ENTIRE output — no grant is created here, ever.
229
+ function certificationProposals({ now = Date.now(), max = 2 } = {}) {
230
+ const candidates = [];
231
+ for (const t of listTools()) {
232
+ if (!t.verbs || !t.manifest || !t.manifest.length) continue;
233
+ const certs = certificationForState({ verbs: t.verbs, history: t.history }, now);
234
+ for (const m of t.manifest) {
235
+ const tier = m.tier || t.risk || "write";
236
+ if (tier === "read-only") continue;
237
+ const c = certs[m.name];
238
+ if (!c || c.tier !== "certified") continue;
239
+ candidates.push({ tool: t.name, verb: m.name, tier, cleanRuns: c.cleanRuns });
240
+ }
241
+ }
242
+ candidates.sort((a, b) => b.cleanRuns - a.cleanRuns);
243
+ return candidates.slice(0, max).map((c) =>
244
+ `🎓 ${c.tool} ${c.verb} is certified (${c.cleanRuns} clean ${c.tier} runs) — candidate for a standing Always-allow grant if you want it; grant via the approval card / guardrails, or ignore.`);
245
+ }
246
+
171
247
  // Bump a tool's run counter without outcome detail. Still called at end of turn
172
248
  // from the runner's transcript parse; for directory tools that's a no-op (the
173
249
  // CLI already stamped the authoritative result — double-counting would lie),
@@ -1018,6 +1094,7 @@ module.exports = {
1018
1094
  runEnv, missingRequires, toolNameFromPath, sanitizeName, ensureDir, normalizeRisk,
1019
1095
  toolPaths, readState, writeState, recordRunResult, runGate, approvedTierFromArgv,
1020
1096
  resolveTier, invokedVerb, sourceHash, fingerprint,
1097
+ CERTIFY_CLEAN_RUNS, PROBATION_WINDOW_MS, verbCertification, certificationForState, certification, certificationProposals,
1021
1098
  listLib, readLib, writeLib, libDependents,
1022
1099
  matchTools, recordRun, recordCreate, readUsage,
1023
1100
  lintToolSource, checkSyntax, envRefsIn, assertNoSecrets,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "3.1.28",
3
+ "version": "3.2.0",
4
4
  "description": "An always-on, provider-agnostic coding-agent harness for Claude Code and OpenAI Codex via chat",
5
5
  "main": "bot.js",
6
6
  "bin": {
@@ -11,7 +11,7 @@
11
11
  "setup": "node setup.js",
12
12
  "start": "node bot.js",
13
13
  "pretest": "node test-provider-fixture.js && node test-provider-core-prompt.js && node test-turn-observer.js && node test-provider-prompt-parity.js && node test-provider-env-isolation.js && node test-provider-resume-parity.js && node test-provider-bootstrap.js && node test-provider-registry.js && node test-provider-stream-decoder.js && node test-provider-events.js && node test-provider-claude.js && node test-provider-codex.js && node test-provider-runner.js && node test-provider-refusal-recovery.js && node test-provider-run-lifecycle.js && node test-provider-gateway.js && node test-provider-side-chat.js && node test-single-runtime.js && node test-provider-migration-backup.js && node test-provider-state-migration.js && node test-provider-session-history.js && node test-provider-session-commands.js && node test-provider-compaction.js && node test-provider-scheduler.js && node test-credential-capture.js && node test-cursor-removal.js && node test-utility-provider-policy.js && node test-provider-subagent.js && node test-provider-recall.js && node test-provider-pack-review.js && node test-memory-mutation-queue.js && node test-provider-dream.js && node test-provider-enforcer.js && node test-provider-tool-hooks.js && node test-provider-boot-matrix.js && node test-provider-capabilities.js && node test-provider-language.js && node test-provider-coupling-audit.js && node test-provider-parity-e2e.js && node test-kazee-channel-health.js && node test-kazee-media-caption.js && node test-prompt-budget.js && node test-agency-ledger.js && node test-agency-deliberate.js && node test-guardrail.js && node test-spaces-comment-content.js && node test-boot-health.js && node test-lexical-floor.js && node test-safe-mode.js && node test-logout.js && node test-kazee-call-audio.js && node test-kazee-call-janus.js && node test-kazee-call-webrtc.js && node test-kazee-call-speech.js && node test-kazee-call-manager.js && node test-kazee-call-session.js",
14
- "test": "OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node -e \"require('./vault'); console.log('OK')\" && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-runner-watchdog-static.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-usage-accounting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-engine.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-discoverer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-query.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-walker-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-score-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cokept-warming.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-project-transcripts-smoke.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-abilities.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-extraction.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-couse.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-transfer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-tiers.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-merge-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-learning-e2e.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-nesting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-search.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-extractive-injection.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-dedupe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-signal.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tools.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tooling-mode.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-manifest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-evolution.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-async.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-unified-identity.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-queue-routing.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-packs.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-persona.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-pipeline.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-relationship-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-relationship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-enforcer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-mandate-hygiene.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-external-rule-scopes.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-cards.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-identity-prune.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-message-id.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-people-owner-seed.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-delivery-contract.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-lock.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-web-sessions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-poll-recovery.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-409-grace.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-conflict-heal-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-single-instance.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-streaming-split.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-spaces-autoenable.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-say-loopback.js"
14
+ "test": "OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node -e \"require('./vault'); console.log('OK')\" && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-runner-watchdog-static.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-usage-accounting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-engine.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-discoverer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-query.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-walker-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-score-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cokept-warming.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-project-transcripts-smoke.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-abilities.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-extraction.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-couse.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-transfer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-tiers.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-merge-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-learning-e2e.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-nesting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-search.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-extractive-injection.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-dedupe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-signal.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tools.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tooling-mode.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-manifest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-evolution.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-async.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-unified-identity.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-queue-routing.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-packs.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-persona.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-pipeline.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-relationship-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-relationship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-enforcer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-mandate-hygiene.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-external-rule-scopes.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-cards.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-identity-prune.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-message-id.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-people-owner-seed.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-delivery-contract.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-lock.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-web-sessions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-poll-recovery.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-409-grace.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-conflict-heal-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-single-instance.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-streaming-split.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-spaces-autoenable.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-say-loopback.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-union.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-subst-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-section-budgets.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-dedup-adjudicator.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-prospective.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-regressions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cost-report.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-mining.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-certification.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-apprenticeship.js"
15
15
  },
16
16
  "files": [
17
17
  "bot.js",
@@ -341,7 +341,12 @@ assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
341
341
  // hold so no getUpdates could ever complete, and _armHealthyCheck cleared its
342
342
  // own pending timer on every re-arm so the only code that resets _wedgedSince
343
343
  // never ran. One ordinary ECONNRESET cost 10 minutes and a process.exit.
344
- assert.strictEqual(pkg.version, "3.1.28", "release version must remain unchanged without explicit approval");
344
+ // 3.2.0 approved by Sumeet 2026-08-03 ("i like them all then go ahead and add
345
+ // to a plan then go ahead and implement all the plans pending from this review
346
+ // into a new version work through till the end and deploy out a new version"):
347
+ // the employee batch — memory adoption A1-A4, the codegraph reusable tool,
348
+ // employee capabilities C1-C6, and the Kazee group speaker-identity fix.
349
+ assert.strictEqual(pkg.version, "3.2.0", "release version must remain unchanged without explicit approval");
345
350
  for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
346
351
  assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
347
352
  }