@inetafrica/open-claudia 3.1.1 → 3.1.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## v3.1.2 — On-demand recall query + cold-walker fallback fix
4
+
5
+ Two things fold in: a new `recall query` CLI so the working agent can ask long-term memory a question mid-task, and a latent reliability fix in the recall walker's fallback path.
6
+
7
+ - **New `open-claudia recall query "<question>"`** — the agent's side door into the seed → graph → walker pipeline. Where per-turn recall seeds on the *user's* message, this seeds the graph with the agent's OWN working context (via `--context`), walks the typed-edge graph with the **active-provider** judge, and returns the kept pack sections with why-bullets and pointers. It closes the documented cold-start hole: mid-build, when the vocabulary you're using shares no words with the pack that governs the work, ordinary recall never resurfaces it — an explicit query carrying your working context does. Read-only (no edge reinforcement), no pre-gate (an explicit ask always runs full), and fail-open (a failed judge returns the whole candidate list unjudged rather than nothing). Flags: `--context`, `--json`, `--no-walker`, `--no-episodes`, `--limit`, `--timeout`.
8
+ - **Cold-walker outputSchema 400 fix** — the recall walker prefers a warm persistent judge (Claude only) and falls back to a cold spawn otherwise. The cold path was passing an array-typed `outputSchema`, which the Anthropic API turns into a custom-tool `input_schema` and rejects with `400 input_schema.type: Input should be 'object'`. So any time the warm walker errored and fell back, recall silently 400'd and failed-open — judgement quietly disabled on the fallback path. Removed the schema entirely (the warm path never sent one, and `walk()` already extracts the JSON array from freeform text, filters to known ids, and clamps scores), so freeform is the proven contract on both paths.
9
+ - **Agent-space / OpenClaw:** new `core/recall/query.js` (`query()` + `mergeMatches`), `excerptFor` exported from the discoverer, `recall query` wired into `bin/recall.js`. New hermetic `test-recall-query.js` in the pretest chain (unjudged fail-open, judged keep-only, judged-empty, garbage fail-open, context-seed merge, blank-question reject).
10
+
3
11
  ## v3.1.1 — /logout and switch-account: re-auth is finally reachable from chat
4
12
 
5
13
  Reported live: with Claude or Codex already connected, there was **no way to sign into a different account** — `/login` showed "connected" and tapping a model just switched models, because the auth-aware picker (correctly, since v3.0.36) only starts a login flow when a provider is *not* authenticated. And `/clear_oauth_token` alone didn't help: it clears the bot-held token but the CLI credential store keeps `authStatus` reading "authenticated", so the picker still never offered a sign-in. Re-auth needs a *full* sign-out first — this release adds it.
package/bin/recall.js CHANGED
@@ -1,16 +1,26 @@
1
1
  // open-claudia recall-stats [--json]
2
2
  // open-claudia recall graph [--sync] [--json]
3
+ // open-claudia recall query "<question>" [--context "<text>"] [--json]
3
4
  //
4
5
  // Inspect the recall system: discoverer metrics (precision/miss-rescue/noise,
5
- // latency, cost) and the typed-edge graph (node/edge counts, sync).
6
+ // latency, cost), the typed-edge graph (node/edge counts, sync), and the
7
+ // on-demand walker query (ask long-term memory a question mid-task).
6
8
 
7
9
  const path = require("path");
8
10
 
9
11
  const HELP = `
10
- Inspect the memory recall system.
12
+ Inspect and query the memory recall system.
11
13
 
12
14
  open-claudia recall-stats [--json] Discoverer metrics summary
13
15
  open-claudia recall graph [--sync] Recall-graph stats (optionally re-sync structural edges first)
16
+ open-claudia recall query "<question>" On-demand memory lookup: FTS seeds + graph expansion +
17
+ walker judgement → kept sections with why-bullets
18
+ --context "<text>" Working context to seed alongside the question (what you're doing now)
19
+ --json Machine-readable result
20
+ --no-walker Skip the LLM judge; print the raw seed+graph candidate list
21
+ --no-episodes Skip past-conversation transcript hits
22
+ --limit <n> Pack seed width (default 6)
23
+ --timeout <ms> Walker timeout (default 25000)
14
24
 
15
25
  The discoverer engine logs one line per turn to recall-metrics.jsonl and a
16
26
  rolling summary to recall-metrics-summary.json. Switch engines per chat with
@@ -59,10 +69,66 @@ function graph(args) {
59
69
  for (const [type, n] of Object.entries(s.byType || {})) console.log(` ${type}: ${n}`);
60
70
  }
61
71
 
72
+ async function queryCmd(args) {
73
+ const flags = { context: "", json: false, noWalker: false, noEpisodes: false, limit: 0, timeout: 0 };
74
+ const positional = [];
75
+ for (let i = 0; i < args.length; i++) {
76
+ const a = args[i];
77
+ if (a === "--json") flags.json = true;
78
+ else if (a === "--no-walker") flags.noWalker = true;
79
+ else if (a === "--no-episodes") flags.noEpisodes = true;
80
+ else if (a === "--context") flags.context = args[++i] || "";
81
+ else if (a === "--limit") flags.limit = Number(args[++i]);
82
+ else if (a === "--timeout") flags.timeout = Number(args[++i]);
83
+ else positional.push(a);
84
+ }
85
+ const question = positional.join(" ").trim();
86
+ if (!question) {
87
+ console.error('Usage: open-claudia recall query "<question>" [--context "<text>"] [--json] [--no-walker] [--no-episodes] [--limit <n>] [--timeout <ms>]');
88
+ process.exit(1);
89
+ }
90
+ // The walker reads its timeout from env at module load — set before require.
91
+ if (flags.timeout > 0) process.env.RECALL_DISCOVERER_TIMEOUT_MS = String(flags.timeout);
92
+ const lib = require(path.join(__dirname, "..", "core", "recall", "query"));
93
+ let res;
94
+ try {
95
+ res = await lib.query(question, {
96
+ context: flags.context,
97
+ walker: !flags.noWalker,
98
+ episodes: !flags.noEpisodes,
99
+ limit: flags.limit || undefined,
100
+ });
101
+ } catch (e) {
102
+ console.error(`recall query failed: ${e.message}`);
103
+ process.exit(1);
104
+ }
105
+ if (flags.json) { console.log(JSON.stringify(res, null, 2)); return; }
106
+ const w = res.walker || {};
107
+ console.log(`Recall query — ${res.results.length} kept of ${res.candidates} candidate(s) (seeds: ${res.seeds.packs} pack, ${res.seeds.entities} entity, ${res.seeds.tools} tool; +${res.activated} graph-activated)`);
108
+ console.log(res.judged
109
+ ? `Walker: ${w.provider || "?"}${w.model ? `/${w.model}` : ""} (${w.source}, ${w.ms}ms)`
110
+ : `Walker: ${w.source === "off" ? "off" : "failed"} — UNJUDGED candidate list (fail-open)`);
111
+ for (const r of res.results) {
112
+ console.log("");
113
+ const score = r.score == null ? "" : ` (score ${r.score})`;
114
+ const viaTag = r.activated ? ` [via ${r.via || "graph"}]` : "";
115
+ console.log(`■ ${r.kind}: ${r.name}${score}${viaTag}`);
116
+ if (r.why) console.log(` why: ${r.why}`);
117
+ if (r.kind === "pack" && r.lines && r.lines.length) {
118
+ for (const l of r.lines) console.log(` > [${l.section}:${l.lineno}] ${l.text}`);
119
+ } else if (r.excerpt) {
120
+ console.log(` > ${r.excerpt.replace(/\n/g, " ").slice(0, 300)}`);
121
+ }
122
+ if (r.pointer) console.log(` ↳ ${r.pointer}`);
123
+ }
124
+ if (!res.results.length) console.log('\n(nothing relevant — rephrase, add --context, or use --no-walker to see raw candidates)');
125
+ }
126
+
62
127
  function run(args) {
63
128
  const sub = args[0];
64
129
  if (sub === "--help" || sub === "help") { console.log(HELP); process.exit(0); }
65
130
  if (sub === "graph") { graph(args.slice(1)); return; }
131
+ if (sub === "query") { queryCmd(args.slice(1)); return; }
66
132
  // default / "stats": metrics summary
67
133
  stats(args.filter((a) => a !== "stats"));
68
134
  }
@@ -282,6 +282,13 @@ async function runWalker(prompt, options = {}) {
282
282
  };
283
283
  } catch (e) { /* fall back to cold spawn below */ }
284
284
  }
285
+ // No outputSchema: a provider structured-output schema becomes a custom-tool
286
+ // input_schema, which the Anthropic API requires to be an OBJECT — the
287
+ // walker's top-level array schema is rejected 400 ("input_schema.type: Input
288
+ // should be 'object'"), so the cold path used to fail-open on every fallback.
289
+ // The warm path never sends a schema and the walk() parser already extracts
290
+ // the JSON array from freeform text, filters to known ids, and clamps scores,
291
+ // so freeform is the proven contract on both paths.
285
292
  const result = await runUtility(prompt, {
286
293
  purpose: "recall",
287
294
  provider: selection.providerId,
@@ -291,14 +298,6 @@ async function runWalker(prompt, options = {}) {
291
298
  legacyClaudeModelEnv: "RECALL_DISCOVERER_MODEL",
292
299
  systemPrompt: WALKER_SYSTEM,
293
300
  readOnly: true,
294
- outputSchema: {
295
- type: "array",
296
- items: {
297
- type: "object",
298
- required: ["id"],
299
- properties: { id: { type: "string" }, why: { type: "string" }, score: { type: "number" } },
300
- },
301
- },
302
301
  timeoutMs: WALKER_TIMEOUT_MS,
303
302
  cwd: options.cwd || process.cwd(),
304
303
  });
@@ -779,4 +778,4 @@ async function run(ctx) {
779
778
  };
780
779
  }
781
780
 
782
- module.exports = { name: "discoverer", run, runWalker, needsRecall, recallTier, walk, cascadeVerdict, cokeptPairs, scoutToolGaps, renderToolGaps, episodesAllowedForSpeaker };
781
+ module.exports = { name: "discoverer", run, runWalker, needsRecall, recallTier, walk, excerptFor, cascadeVerdict, cokeptPairs, scoutToolGaps, renderToolGaps, episodesAllowedForSpeaker };
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+
3
+ // On-demand recall: the working agent's side door into the same
4
+ // seed → graph-expansion → walker pipeline the discoverer runs per turn.
5
+ // `open-claudia recall query "<question>" [--context "<working context>"]`
6
+ //
7
+ // The per-turn engine seeds from the USER's message; mid-build the agent's
8
+ // actual question often shares zero words with the governing pack (measured
9
+ // 2026-07-16: the embeddings-measurement stance never resurfaced during the
10
+ // build it governed). Here the agent seeds with its own question + working
11
+ // context, and graph edges + walker judgement bridge the vocabulary gap.
12
+ //
13
+ // Deliberate differences from the per-turn engine:
14
+ // - no pre-gate: an explicit query is always intentional (always "full").
15
+ // - read-only: never reinforces edges, never warms co-kept pairs — a
16
+ // query is a lookup, not a turn.
17
+ // - cold walker only: a one-shot CLI process must not own a warm child.
18
+ // - fail-open returns ALL candidates marked unjudged — the caller is an
19
+ // agent that can judge for itself; an empty answer helps nobody.
20
+
21
+ const graph = require("./graph");
22
+ const metrics = require("./metrics");
23
+ const tuning = require("./tuning");
24
+ const discoverer = require("./discoverer");
25
+
26
+ let transcriptIndex = null;
27
+ try { transcriptIndex = require("../transcript-index"); } catch (e) {}
28
+
29
+ const COLD_ONLY_WARM = { isEnabled: () => false };
30
+ const EXCERPT_CHARS = 600;
31
+
32
+ function mergeMatches(primary, secondary, keyOf) {
33
+ const out = primary.map((m) => ({ ...m, origin: "user" }));
34
+ const seen = new Set(primary.map(keyOf));
35
+ for (const m of secondary) {
36
+ if (seen.has(keyOf(m))) continue;
37
+ out.push({ ...m, origin: "context" });
38
+ }
39
+ return out;
40
+ }
41
+
42
+ function clip(s, n) { return s && s.length > n ? s.slice(0, n) + "…" : (s || ""); }
43
+
44
+ async function query(question, opts = {}) {
45
+ const packsLib = opts.packsLib || require("../packs");
46
+ const entitiesLib = opts.entitiesLib || require("../entities");
47
+ const toolsLib = opts.toolsLib || require("../tools");
48
+ const started = Date.now();
49
+ const q = String(question || "").trim();
50
+ if (!q) throw new Error("recall query needs a question");
51
+ const context = String(opts.context || "").trim();
52
+ const packLimit = Number(opts.limit) > 0 ? Math.min(Number(opts.limit), 12) : 6;
53
+
54
+ // Seed via FTS on the question, then the working context (deduped).
55
+ let packSeeds = [];
56
+ let entSeeds = [];
57
+ try {
58
+ packSeeds = mergeMatches(
59
+ packsLib.matchPacks(q, { limit: packLimit }),
60
+ context ? packsLib.matchPacks(context, { limit: packLimit }) : [],
61
+ (m) => m.dir,
62
+ );
63
+ } catch (e) {}
64
+ try {
65
+ entSeeds = mergeMatches(
66
+ entitiesLib.matchEntities(q, { limit: 4 }),
67
+ context ? entitiesLib.matchEntities(context, { limit: 4 }) : [],
68
+ (m) => m.slug,
69
+ );
70
+ } catch (e) {}
71
+ const toolSeeds = new Map();
72
+ try {
73
+ for (const t of toolsLib.matchTools(q, { limit: 5 })) toolSeeds.set(t.name, { ...t, origin: "user" });
74
+ if (context) {
75
+ for (const t of toolsLib.matchTools(context, { limit: 5 })) {
76
+ if (!toolSeeds.has(t.name)) toolSeeds.set(t.name, { ...t, origin: "context" });
77
+ }
78
+ }
79
+ } catch (e) {}
80
+
81
+ // Spreading activation from seeds across the typed-edge graph.
82
+ const seedNodes = [
83
+ ...packSeeds.map((m) => ({ id: `pack:${m.dir}`, score: m.score || 2 })),
84
+ ...entSeeds.map((m) => ({ id: `entity:${m.slug}`, score: m.score || 2 })),
85
+ ];
86
+ const seedIds = new Set(seedNodes.map((s) => s.id));
87
+ let activated = new Map();
88
+ if (graph.available()) {
89
+ try { graph.syncFromCorpus(packsLib, entitiesLib); } catch (e) {}
90
+ try { activated = graph.expand(seedNodes, {}); } catch (e) {}
91
+ }
92
+
93
+ // Candidates: question seeds by score, context seeds, then graph-activated.
94
+ const seedRank = new Map();
95
+ for (const m of packSeeds) seedRank.set(`pack:${m.dir}`, { score: m.score || 2, origin: m.origin });
96
+ for (const m of entSeeds) seedRank.set(`entity:${m.slug}`, { score: m.score || 2, origin: m.origin });
97
+ const orderedIds = [...seedIds].sort((a, b) => {
98
+ const ra = seedRank.get(a) || {}, rb = seedRank.get(b) || {};
99
+ const ua = ra.origin === "user" ? 0 : 1, ub = rb.origin === "user" ? 0 : 1;
100
+ return ua - ub || (rb.score || 0) - (ra.score || 0);
101
+ });
102
+ for (const [id] of [...activated.entries()].sort((x, y) => (y[1].activation || 0) - (x[1].activation || 0))) {
103
+ if (!seedIds.has(id)) orderedIds.push(id);
104
+ }
105
+ const candidates = [];
106
+ for (const id of orderedIds.slice(0, tuning.get("walkerMaxCandidates"))) {
107
+ const base = discoverer.excerptFor(id, packsLib, entitiesLib);
108
+ if (!base) continue;
109
+ const act = activated.get(id);
110
+ candidates.push({ ...base, activated: !seedIds.has(id), via: act ? act.via : null });
111
+ }
112
+
113
+ // Tools: direct seeds plus tools declared by any candidate pack.
114
+ const candPackDirs = new Set();
115
+ for (const c of candidates) if (c.id.startsWith("pack:")) candPackDirs.add(c.id.slice(5));
116
+ const toolCand = new Map();
117
+ for (const [name] of toolSeeds) toolCand.set(name, { name, via: null });
118
+ if (candPackDirs.size) {
119
+ try {
120
+ for (const t of toolsLib.listTools()) {
121
+ if (t.pack && candPackDirs.has(t.pack) && !toolCand.has(t.name)) {
122
+ toolCand.set(t.name, { name: t.name, via: `pack:${t.pack}` });
123
+ }
124
+ }
125
+ } catch (e) {}
126
+ }
127
+ for (const c of toolCand.values()) {
128
+ const base = discoverer.excerptFor(`tool:${c.name}`, packsLib, entitiesLib);
129
+ if (!base) continue;
130
+ candidates.push({ ...base, activated: !!c.via, via: c.via });
131
+ }
132
+
133
+ // Episodes: FTS over past transcripts. The CLI has no speaker context, so
134
+ // the R6 owner gate is open by design (the CLI runs as the owner on their
135
+ // own box); bot-side callers still pass through the per-turn engine's gate.
136
+ if (opts.episodes !== false && transcriptIndex && transcriptIndex.available()) {
137
+ try {
138
+ const { hits } = transcriptIndex.search(q, { limit: tuning.get("episodeLimit") });
139
+ for (const h of (hits || [])) {
140
+ const terms = [...String(h.snip || "").matchAll(/>>(.+?)<</g)].map((m) => m[1]).slice(0, 4);
141
+ candidates.push({
142
+ id: `episode:${require("path").basename(String(h.file || ""), ".jsonl")}:${h.line}`,
143
+ name: `Past conversation${h.project ? ` (${h.project})` : ""}${h.ts ? ` ${String(h.ts).slice(0, 10)}` : ""}`,
144
+ description: "",
145
+ excerpt: clip(String(h.snip || "").replace(/>>|<</g, ""), EXCERPT_CHARS),
146
+ activated: true,
147
+ via: "transcript",
148
+ _terms: terms,
149
+ });
150
+ }
151
+ } catch (e) { /* episodic recall is best-effort */ }
152
+ }
153
+
154
+ // Walker judgement (cold spawn only). opts.walker === false skips the spawn.
155
+ let walkRes = { kept: null, scores: null, source: "off", walkerMs: 0, provider: null, model: null, costUsd: null };
156
+ if (opts.walker !== false && candidates.length) {
157
+ walkRes = await discoverer.walk(q, context, candidates, {
158
+ warmWalker: COLD_ONLY_WARM,
159
+ cwd: opts.cwd,
160
+ ...(opts.walkerOptions || {}),
161
+ });
162
+ }
163
+ const whyMap = walkRes.kept;
164
+ const scores = walkRes.scores;
165
+
166
+ const results = [];
167
+ for (const c of candidates) {
168
+ if (whyMap && !whyMap.has(c.id)) continue;
169
+ const kind = c.id.startsWith("pack:") ? "pack"
170
+ : c.id.startsWith("entity:") ? "entity"
171
+ : c.id.startsWith("tool:") ? "tool" : "episode";
172
+ const r = {
173
+ id: c.id,
174
+ kind,
175
+ name: c.name,
176
+ why: whyMap ? whyMap.get(c.id) || "" : "",
177
+ score: scores && scores.has(c.id) ? scores.get(c.id) : null,
178
+ activated: !!c.activated,
179
+ via: c.via || null,
180
+ excerpt: c.excerpt || "",
181
+ };
182
+ if (kind === "pack") {
183
+ const dir = c.id.slice(5);
184
+ r.pointer = `open-claudia pack show ${dir}`;
185
+ try {
186
+ const hits = packsLib.searchLines([q, context].filter(Boolean).join(" "), { dir, k: 8 });
187
+ if (hits && hits.length) r.lines = hits.map((h) => ({ section: h.section, lineno: h.lineno, text: h.text }));
188
+ } catch (e) { /* a pack with no line hits still shows its excerpt */ }
189
+ } else if (kind === "entity") {
190
+ r.pointer = `open-claudia entity show ${c.id.slice(7)}`;
191
+ } else if (kind === "tool") {
192
+ r.pointer = `open-claudia tool show ${c.id.slice(5)}`;
193
+ } else {
194
+ const qterms = (c._terms && c._terms.length
195
+ ? c._terms.join(" ")
196
+ : String(c.excerpt).split(/\s+/).slice(0, 4).join(" ")).replace(/"/g, "");
197
+ r.pointer = `open-claudia transcript-search "${qterms}" --all`;
198
+ }
199
+ results.push(r);
200
+ }
201
+ results.sort((a, b) => (b.score ?? -1) - (a.score ?? -1));
202
+
203
+ const out = {
204
+ question: q,
205
+ hasContext: !!context,
206
+ seeds: { packs: packSeeds.length, entities: entSeeds.length, tools: toolSeeds.size },
207
+ activated: activated.size,
208
+ candidates: candidates.length,
209
+ judged: !!whyMap,
210
+ walker: {
211
+ source: walkRes.source,
212
+ ms: walkRes.walkerMs || 0,
213
+ provider: walkRes.provider || null,
214
+ model: walkRes.model || null,
215
+ },
216
+ results,
217
+ latencyMs: Date.now() - started,
218
+ };
219
+
220
+ try {
221
+ metrics.logTurn({
222
+ engine: "query",
223
+ provider: walkRes.provider || "",
224
+ model: walkRes.model || "",
225
+ query: q,
226
+ tier: "query",
227
+ seeds: seedNodes,
228
+ activated: [...activated.entries()].map(([id, a]) => ({ id, activation: a.activation, hop: a.hop })),
229
+ cand: candidates.map((c) => ({ id: c.id, act: !!c.activated })),
230
+ kept: results.map((r) => ({ id: r.id, why: r.why, score: r.score })),
231
+ gated: false,
232
+ latencyMs: out.latencyMs,
233
+ walkerMs: walkRes.walkerMs,
234
+ walker: walkRes.source,
235
+ costUsd: walkRes.costUsd || 0,
236
+ });
237
+ } catch (e) {}
238
+
239
+ return out;
240
+ }
241
+
242
+ module.exports = { query, mergeMatches };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "3.1.1",
3
+ "version": "3.1.2",
4
4
  "description": "An always-on, provider-agnostic coding-agent harness for Claude Code and OpenAI Codex via chat",
5
5
  "main": "bot.js",
6
6
  "bin": {
@@ -10,7 +10,7 @@
10
10
  "setup": "node setup.js",
11
11
  "start": "node bot.js",
12
12
  "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-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",
13
- "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-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-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-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"
13
+ "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-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-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"
14
14
  },
15
15
  "files": [
16
16
  "bot.js",
@@ -157,7 +157,17 @@ assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
157
157
  // never re-offers a sign-in); a 🔁 Switch account button on connected
158
158
  // providers in /login behind an explicit confirm that names the signed-in
159
159
  // account where the CLI exposes it, then chains logout → fresh login.
160
- assert.strictEqual(pkg.version, "3.1.1", "release version must remain unchanged without explicit approval");
160
+ // 3.1.2 approved by Sumeet 2026-07-20 ("If everything you have works and is
161
+ // ready fold it in and push all out in a new version"): on-demand recall query
162
+ // CLI (`open-claudia recall query "<question>"`) — seeds the graph with the
163
+ // agent's OWN working context (not the user's words), walks it with the
164
+ // active-provider judge, returns pack sections + why-bullets; closes the
165
+ // cold-start hole where mid-build vocabulary shared no words with the governing
166
+ // pack. Also fixes a latent cold-walker bug: the warm→cold fallback sent an
167
+ // array-typed outputSchema the Anthropic API rejects 400, so every fallback
168
+ // silently failed-open — removed; freeform JSON parse is the proven contract on
169
+ // both paths.
170
+ assert.strictEqual(pkg.version, "3.1.2", "release version must remain unchanged without explicit approval");
161
171
  for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
162
172
  assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
163
173
  }