@inetafrica/open-claudia 3.5.19 → 3.5.23

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.
@@ -9,7 +9,7 @@ const { MAX_PROCESS_TIMEOUT } = require("./config");
9
9
  const { killProcessTree } = require("./process-tree");
10
10
  const { redactSensitive } = require("./redact");
11
11
  const { validateInvocation } = require("./providers/contract");
12
- const { selectTerminalText } = require("./providers/events");
12
+ const { providerFailureCode, selectTerminalText } = require("./providers/events");
13
13
  const { createUtilityPolicy, normalizePurpose } = require("./providers/utility-policy");
14
14
 
15
15
  const DEFAULT_TIMEOUT_MS = 10 * 60 * 1000;
@@ -20,6 +20,11 @@ const DEFAULT_KILL_GRACE_MS = 500;
20
20
  const DEFAULT_GLOBAL_CONCURRENCY = 6;
21
21
  const DEFAULT_PURPOSE_CONCURRENCY = 3;
22
22
  const DEFAULT_RECURSION_DEPTH = 2;
23
+ // Claude Code's refresh-lock race passes in seconds: one more try after this,
24
+ // for work nobody waits on. A chat's own checks (recall, a passive reply, a
25
+ // title, the guard) go without instead of holding the reply up.
26
+ const DEFAULT_TRANSIENT_RETRY_MS = 20 * 1000;
27
+ const TRANSIENT_RETRY_PURPOSES = new Set(["dream", "introspection", "review", "dedup", "subagent"]);
23
28
  const DEFAULT_SYSTEM_PROMPT = [
24
29
  "You are a bounded Open Claudia utility agent.",
25
30
  "Follow the supplied task and return only the requested result.",
@@ -62,8 +67,22 @@ function appendBounded(current, addition, maxBytes) {
62
67
  return clipUtf8(joined, maxBytes);
63
68
  }
64
69
 
65
- function stableError(code, message) {
66
- return { code, message: redactSensitive(String(message || "Utility agent failed")) };
70
+ function stableError(code, message, details = null) {
71
+ return {
72
+ code,
73
+ message: redactSensitive(String(message || "Utility agent failed")),
74
+ ...(details ? { details } : {}),
75
+ };
76
+ }
77
+
78
+ // A limit's reset as the provider reported it (Claude: the rate_limit_event);
79
+ // usage-limits.limitHitDetails reads it, else the wording of the message.
80
+ function failureDetails(event, code) {
81
+ if (code !== "PROVIDER_USAGE_LIMIT") return null;
82
+ return {
83
+ limitResetsAt: Number.isFinite(event?.limitResetsAt) ? event.limitResetsAt : null,
84
+ limitType: event?.limitType || null,
85
+ };
67
86
  }
68
87
 
69
88
  function extractJson(text) {
@@ -298,10 +317,10 @@ function executeUtilityInvocation(options) {
298
317
  else if (cancelled) failure = stableError("UTILITY_CANCELLED", "Utility agent was cancelled");
299
318
  else if (timedOut) failure = stableError("UTILITY_TIMEOUT", "Utility agent timed out");
300
319
  else if (outputLimit) failure = stableError("UTILITY_OUTPUT_LIMIT", "Utility provider stdout exceeded its hard limit");
301
- else if (normalizedFailure) failure = stableError(
302
- normalizedFailure.authError ? "PROVIDER_UNAUTHENTICATED" : "UTILITY_PROVIDER_ERROR",
303
- normalizedFailure.message || "Utility provider failed",
304
- );
320
+ else if (normalizedFailure) {
321
+ const code = providerFailureCode(normalizedFailure, "UTILITY_PROVIDER_ERROR");
322
+ failure = stableError(code, normalizedFailure.message || "Utility provider failed", failureDetails(normalizedFailure, code));
323
+ }
305
324
  else if (closeInfo?.code !== 0) failure = stableError(
306
325
  "UTILITY_PROCESS_FAILED",
307
326
  safeStderr || `Utility provider exited with code ${closeInfo?.code == null ? "unknown" : closeInfo.code}`,
@@ -423,6 +442,21 @@ function executeUtilityInvocation(options) {
423
442
  });
424
443
  }
425
444
 
445
+ // Wait `ms`, or less when the call is cancelled meanwhile.
446
+ function pause(ms, signal) {
447
+ return new Promise((resolve) => {
448
+ if (signal?.aborted || !(ms > 0)) return resolve();
449
+ let timer = null;
450
+ const done = () => {
451
+ clearTimeout(timer);
452
+ signal?.removeEventListener?.("abort", done);
453
+ resolve();
454
+ };
455
+ timer = setTimeout(done, ms);
456
+ signal?.addEventListener?.("abort", done, { once: true });
457
+ });
458
+ }
459
+
426
460
  function fallbackEligible(errorCode) {
427
461
  return new Set([
428
462
  "PROVIDER_UNAVAILABLE",
@@ -438,6 +472,12 @@ function createUtilityAgentService(options = {}) {
438
472
  const registry = options.registry || require("./providers");
439
473
  const env = options.env || require("./config").config;
440
474
  const policy = options.policy || createUtilityPolicy({ registry, env, activeProvider: options.activeProvider });
475
+ // The limit record (core/usage-limits.js): a used-up provider is not
476
+ // started; a limit hit is recorded; a call that worked clears it.
477
+ const limitsLib = options.limits || require("./usage-limits");
478
+ const transientRetryMs = Number.isFinite(Number(options.transientRetryMs)) && Number(options.transientRetryMs) >= 0
479
+ ? Number(options.transientRetryMs)
480
+ : DEFAULT_TRANSIENT_RETRY_MS;
441
481
  const spawnProcess = options.spawnProcess || spawn;
442
482
  const killTree = options.killTree || killProcessTree;
443
483
  const killGraceMs = positiveInteger(options.killGraceMs, DEFAULT_KILL_GRACE_MS, "killGraceMs");
@@ -490,6 +530,20 @@ function createUtilityAgentService(options = {}) {
490
530
  else activeByPurpose.set(purpose, activePurpose - 1);
491
531
  }
492
532
 
533
+ function usedUpFor(selection) {
534
+ try { return limitsLib.limitBlock(selection.providerId, { model: selection.model }); }
535
+ catch (_) { return null; }
536
+ }
537
+
538
+ function noteLimits(providerId, result) {
539
+ try {
540
+ if (result.ok) { limitsLib.clearLimitBlock(providerId, { model: result.model || null }); return; }
541
+ if (result.error?.code !== "PROVIDER_USAGE_LIMIT" || result.notStarted) return;
542
+ const hit = limitsLib.limitHitDetails({ provider: providerId, error: result.error });
543
+ if (hit?.resetsAt) limitsLib.noteLimitHit({ provider: providerId, limitType: hit.limitType, resetsAt: hit.resetsAt });
544
+ } catch (_) { /* the record is best effort */ }
545
+ }
546
+
493
547
  function stats() {
494
548
  return {
495
549
  activeGlobal,
@@ -567,18 +621,48 @@ function createUtilityAgentService(options = {}) {
567
621
  ...(effort ? { effort } : {}),
568
622
  };
569
623
 
570
- let invocation;
571
- try {
572
- invocation = validateInvocation(provider.buildUtilityInvocation(invocationContext));
573
- invocation = validateInvocation({
574
- ...invocation,
624
+ // A provider used up until its reset is not started: the caller waits
625
+ // for the reset (the dream) or goes without (recall, a title).
626
+ const block = usedUpFor(selection);
627
+ if (block) {
628
+ const result = {
629
+ ok: false,
630
+ status: "failed",
631
+ text: "",
632
+ provider: selection.providerId,
633
+ model: selection.model,
634
+ tier: selection.tier,
635
+ purpose,
636
+ preferredProvider: selection.preferredProviderId,
637
+ fallbackUsed: selection.fallbackUsed,
638
+ effort,
639
+ notStarted: "usage limit",
640
+ error: stableError(
641
+ "PROVIDER_USAGE_LIMIT",
642
+ `${limitsLib.providerLabel(selection.providerId)}'s ${limitsLib.windowLabel(block.limitType)} is used up until ${new Date(block.resetsAt).toISOString()}`,
643
+ { limitResetsAt: block.resetsAt, limitType: block.limitType || null },
644
+ ),
645
+ };
646
+ lastResult = result;
647
+ if (index + 1 < candidates.length && fallbackEligible(result.error.code)) continue;
648
+ throw new UtilityAgentError(result.error.code, result.error.message, result);
649
+ }
650
+
651
+ const buildInvocation = () => {
652
+ const built = validateInvocation(provider.buildUtilityInvocation(invocationContext));
653
+ return validateInvocation({
654
+ ...built,
575
655
  env: {
576
- ...invocation.env,
656
+ ...built.env,
577
657
  OC_PROVIDER: selection.providerId,
578
658
  OC_UTILITY_DEPTH: String(depth + 1),
579
659
  OC_UTILITY_PURPOSE: purpose,
580
660
  },
581
661
  });
662
+ };
663
+ let invocation;
664
+ try {
665
+ invocation = buildInvocation();
582
666
  } catch (error) {
583
667
  const result = {
584
668
  ok: false,
@@ -598,9 +682,9 @@ function createUtilityAgentService(options = {}) {
598
682
  throw new UtilityAgentError(result.error.code, result.error.message, result);
599
683
  }
600
684
 
601
- const result = await context.run({ depth: depth + 1, purpose }, () => executeUtilityInvocation({
685
+ const execute = (built) => context.run({ depth: depth + 1, purpose }, () => executeUtilityInvocation({
602
686
  provider,
603
- invocation,
687
+ invocation: built,
604
688
  cwd: request.cwd || process.cwd(),
605
689
  spawnProcess,
606
690
  killTree,
@@ -612,6 +696,15 @@ function createUtilityAgentService(options = {}) {
612
696
  maxStdoutBytes,
613
697
  outputSchema: request.outputSchema || null,
614
698
  }));
699
+ let result = await execute(invocation);
700
+ // Claude Code's refresh-lock race passes: one more try, a little later.
701
+ if (!result.ok && result.error?.code === "PROVIDER_TRANSIENT" && TRANSIENT_RETRY_PURPOSES.has(purpose) && !request.signal?.aborted) {
702
+ await pause(transientRetryMs, request.signal);
703
+ if (!request.signal?.aborted) {
704
+ try { result = await execute(buildInvocation()); }
705
+ catch (_) { /* keep the first failure */ }
706
+ }
707
+ }
615
708
  Object.assign(result, {
616
709
  provider: selection.providerId,
617
710
  model: selection.model,
@@ -624,6 +717,7 @@ function createUtilityAgentService(options = {}) {
624
717
  noProductSideEffects: readOnlyMetadata(provider, request.readOnly !== false),
625
718
  });
626
719
  lastResult = result;
720
+ noteLimits(selection.providerId, result);
627
721
  if (result.ok) return result;
628
722
  if (index + 1 < candidates.length && fallbackEligible(result.error?.code)) continue;
629
723
  throw new UtilityAgentError(result.error?.code || "UTILITY_AGENT_FAILED", result.error?.message || "Utility agent failed", result);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "3.5.19",
3
+ "version": "3.5.23",
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-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"
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-stop-queue.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-waits.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-browser-control.js && node test-browser-teach.js && node test-kazee-bot-apps.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-browser-app.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",
@@ -13,7 +13,19 @@ const { spawn, spawnSync } = require("child_process");
13
13
 
14
14
  const argv = process.argv.slice(2);
15
15
  const kind = process.env.FAKE_AGENT_KIND;
16
- const scenario = process.env.FAKE_AGENT_SCENARIO || "success";
16
+ // "provider-error": the run fails with FAKE_AGENT_ERROR_TEXT (a dead sign-in,
17
+ // Claude Code's refresh-lock race). With FAKE_AGENT_ERROR_ONCE_FILE set, only
18
+ // the first run fails (the file marks it) and later runs work.
19
+ function effectiveScenario() {
20
+ const named = process.env.FAKE_AGENT_SCENARIO || "success";
21
+ const once = process.env.FAKE_AGENT_ERROR_ONCE_FILE;
22
+ if (named === "provider-error" && once) {
23
+ if (fs.existsSync(once)) return "success";
24
+ fs.writeFileSync(once, "failed once\n");
25
+ }
26
+ return named;
27
+ }
28
+ const scenario = effectiveScenario();
17
29
  let text = process.env.FAKE_AGENT_TEXT || "fixture says café 👋";
18
30
  let invocationCapturePath = null;
19
31
  let invocationCapture = null;
@@ -290,6 +302,19 @@ function claudeEvents(sessionId) {
290
302
  ];
291
303
  }
292
304
 
305
+ if (scenario === "provider-error") {
306
+ return [
307
+ init,
308
+ {
309
+ type: "result",
310
+ subtype: "error_during_execution",
311
+ is_error: true,
312
+ session_id: sessionId,
313
+ result: process.env.FAKE_AGENT_ERROR_TEXT || "provider error fixture",
314
+ },
315
+ ];
316
+ }
317
+
293
318
  if (scenario === "usage-limit") {
294
319
  // Claude Code 2.1.280 at a plan limit: the rejected rate_limit_event, the
295
320
  // synthetic notice (error "rate_limit") and an error result.
@@ -390,6 +415,9 @@ function codexEvents(sessionId) {
390
415
  { type: "turn.failed", error: { message: "Authentication failed" } },
391
416
  ];
392
417
  }
418
+ if (scenario === "provider-error") {
419
+ return [started, { type: "turn.failed", error: { message: process.env.FAKE_AGENT_ERROR_TEXT || "provider error fixture" } }];
420
+ }
393
421
 
394
422
  const events = [started, { type: "turn.started" }];
395
423
  if (process.env.FAKE_AGENT_THINKING) {
@@ -526,6 +554,19 @@ async function holdUntilReleased(stdin) {
526
554
  while (!fs.existsSync(file) && Date.now() < deadline) await sleep(20);
527
555
  }
528
556
 
557
+ // FAKE_AGENT_PROMPT_PROBES: {"name": "<regex source>", ...}; the run log says
558
+ // which of them matched the prompt (never the prompt itself).
559
+ function promptProbes(text) {
560
+ let spec = null;
561
+ try { spec = JSON.parse(process.env.FAKE_AGENT_PROMPT_PROBES || "null"); } catch (_) { return null; }
562
+ if (!spec || typeof spec !== "object") return null;
563
+ const out = {};
564
+ for (const [name, source] of Object.entries(spec)) {
565
+ try { out[name] = new RegExp(source).test(String(text || "")); } catch (_) { out[name] = false; }
566
+ }
567
+ return out;
568
+ }
569
+
529
570
  function logRun(event, stdin) {
530
571
  const logPath = process.env.FAKE_AGENT_RUN_LOG;
531
572
  if (!logPath) return;
@@ -538,6 +579,9 @@ function logRun(event, stdin) {
538
579
  threadId: process.env.OC_THREAD_ID || null,
539
580
  runId: process.env.OC_RUN_ID || null,
540
581
  tag: tag ? tag[1] : null,
582
+ // Every [[tag:…]] in the prompt, in order (a turn can carry several).
583
+ tags: [...String(stdin || "").matchAll(/\[\[tag:([\w-]{1,40})\]\]/g)].map((m) => m[1]),
584
+ probes: promptProbes(stdin),
541
585
  fork: argv.includes("--fork-session"),
542
586
  resume: argv.indexOf("--resume") >= 0 ? argv[argv.indexOf("--resume") + 1] || null : null,
543
587
  sessionEnv: process.env.OC_SESSION_ID || null,
@@ -610,7 +654,7 @@ async function main() {
610
654
  setInterval(() => {}, 1000);
611
655
  return;
612
656
  }
613
- if (scenario === "auth-failure" || scenario === "refusal" || scenario === "usage-limit") {
657
+ if (scenario === "auth-failure" || scenario === "refusal" || scenario === "usage-limit" || scenario === "provider-error") {
614
658
  process.exitCode = 1;
615
659
  return;
616
660
  }
@@ -297,7 +297,9 @@ if (process.argv[2] === "--probe") {
297
297
  DISAPPEAR_PROVIDER: scenario.disappear || "",
298
298
  },
299
299
  encoding: "utf8",
300
- timeout: 30000,
300
+ // A boot takes about 1 s; loaded CI runners pushed one past 30 s (4 and 5 Oct,
301
+ // 9 Oct twice), so allow 2 min: this checks what boots, not how fast.
302
+ timeout: 120000,
301
303
  });
302
304
 
303
305
  try {
@@ -374,7 +376,9 @@ if (process.argv[2] === "--probe") {
374
376
  WEB_FIXTURE_WORKSPACE: workspace,
375
377
  },
376
378
  encoding: "utf8",
377
- timeout: 30000,
379
+ // A boot takes about 1 s; loaded CI runners pushed one past 30 s (4 and 5 Oct,
380
+ // 9 Oct twice), so allow 2 min: this checks what boots, not how fast.
381
+ timeout: 120000,
378
382
  });
379
383
  assert.ifError(webChild.error);
380
384
  assert.strictEqual(webChild.status, 0, webChild.stderr || webChild.stdout);
@@ -162,7 +162,7 @@ async function commandProbe() {
162
162
  assert.ok(modelControls.includes("mb:codex:gpt-5.6-sol"));
163
163
  assert.ok(modelControls.includes("mb:codex:gpt-5.6-luna"));
164
164
  assert.ok(!modelControls.includes("mb:codex:o4-mini"));
165
- assert.ok(modelControls.includes("mb:claude:claude-sonnet-4-6"));
165
+ assert.ok(modelControls.includes("mb:claude:claude-sonnet-5-5"));
166
166
  }
167
167
 
168
168
  async function callbackProbe() {
@@ -48,7 +48,7 @@ assert.strictEqual(provider.defaultModel(), "claude-default-fixture");
48
48
  assert.ok(provider.modelChoices().includes("claude-fable-5-1"));
49
49
  assert.ok(provider.modelChoices().includes("claude-opus-5-5"), "opus 5.5 is offered in the model picker");
50
50
  assert.ok(provider.modelChoices().includes("claude-fable-5"));
51
- assert.ok(provider.modelChoices().includes("claude-sonnet-4-6"));
51
+ assert.ok(provider.modelChoices().includes("claude-sonnet-5-5"));
52
52
  // 3.4.21: the superseded opus 4.x entries are gone from the picker. They are
53
53
  // BUTTONS only - /model takes a typed name verbatim (core/handlers.js reads
54
54
  // tail without consulting this list), so the rollback path survives removal.
@@ -57,7 +57,9 @@ for (const retired of ["claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6"])
57
57
  }
58
58
  // These two are NOT picker garnish - they are what tierModel returns for
59
59
  // utility work, so they must survive any picker trim.
60
- assert.ok(provider.modelChoices().includes("claude-haiku-4-5-20251001"), "haiku stays: it is the low tier");
60
+ assert.ok(provider.modelChoices().includes("claude-haiku-5-5"), "haiku stays: it is the low tier");
61
+ assert.ok(!provider.modelChoices().includes("claude-haiku-4-5-20251001"), "Haiku 4.5 left the picker for Haiku 5.5 (typed /model still works)");
62
+ assert.ok(!provider.modelChoices().includes("claude-sonnet-4-6"), "Sonnet 4.6 left the picker for Sonnet 5.5 (typed /model still works)");
61
63
 
62
64
  // The SHIPPED default, asserted on the real module rather than the fixture above,
63
65
  // so changing the constant cannot pass unnoticed. Read from source because the
@@ -72,8 +74,8 @@ assert.match(
72
74
  );
73
75
  // The default governs main turns only: utility tiers stay cheap. These are what
74
76
  // stop a default bump from silently repricing recall, pack review and the enforcer.
75
- assert.strictEqual(claudeProvider.tierModel("low"), "claude-haiku-4-5-20251001", "low-tier utility work stays on haiku");
76
- assert.strictEqual(claudeProvider.tierModel("medium"), "claude-sonnet-4-6", "medium-tier utility work stays on sonnet");
77
+ assert.strictEqual(claudeProvider.tierModel("low"), "claude-haiku-5-5", "low-tier utility work stays on haiku (Haiku 5.5 since 9 Oct 2026)");
78
+ assert.strictEqual(claudeProvider.tierModel("medium"), "claude-sonnet-5-5", "medium-tier utility work stays on sonnet (Sonnet 5.5 since 9 Oct 2026)");
77
79
  // Guard on the RESOLVED config, not process.env: loadEnv folds ~/.open-claudia/.env
78
80
  // over process.env, so an operator pin lives in config.config and never appears in
79
81
  // process.env. The 3.4.20 version of this check tested the wrong one and passed
@@ -205,7 +207,7 @@ assert.deepStrictEqual(utility.args.slice(utility.args.indexOf("--allowedTools")
205
207
  assert.deepStrictEqual(utility.args.slice(utility.args.indexOf("--permission-mode"), utility.args.indexOf("--permission-mode") + 2), ["--permission-mode", "plan"]);
206
208
  assert.strictEqual(utility.args.filter((arg) => arg === "/fixture/memory").length, 1, "read-only utility directories are deduplicated");
207
209
  assert.deepStrictEqual(utility.args.slice(utility.args.indexOf("/fixture/memory") - 1, utility.args.indexOf("/fixture/memory") + 1), ["--add-dir", "/fixture/memory"]);
208
- assert.strictEqual(utility.args[utility.args.indexOf("--model") + 1], "claude-haiku-4-5-20251001");
210
+ assert.strictEqual(utility.args[utility.args.indexOf("--model") + 1], "claude-haiku-5-5");
209
211
  assert.strictEqual(utility.args[utility.args.indexOf("--json-schema") + 1], JSON.stringify({ type: "object", required: ["ok"] }));
210
212
  assert.strictEqual(utility.stdin, "UTILITY PROMPT", "utility prompt uses protected stdin transport");
211
213
  assert.ok(!utility.args.includes("UTILITY PROMPT"), "utility prompt is absent from argv");
@@ -5,7 +5,7 @@
5
5
  const assert = require("assert");
6
6
  const fs = require("fs");
7
7
  const path = require("path");
8
- const { NORMALIZED_EVENT_TYPES, selectTerminalText } = require("./core/providers/events");
8
+ const { NORMALIZED_EVENT_TYPES, providerErrorFlags, providerFailureCode, selectTerminalText } = require("./core/providers/events");
9
9
  const { createClaudeParserState, normalizeClaudeEvent } = require("./core/providers/claude-events");
10
10
  const { createCodexParserState, normalizeCodexEvent } = require("./core/providers/codex-events");
11
11
 
@@ -118,6 +118,7 @@ assert.deepStrictEqual(claudeAuth, [{
118
118
  message: "Authentication failed",
119
119
  authError: true,
120
120
  usageLimit: false,
121
+ transient: false,
121
122
  refusal: false,
122
123
  }]);
123
124
 
@@ -241,6 +242,7 @@ assert.deepStrictEqual(limitFailure, [{
241
242
  message: limitText,
242
243
  authError: false,
243
244
  usageLimit: true,
245
+ transient: false,
244
246
  refusal: false,
245
247
  limitResetsAt: 1791205800000,
246
248
  limitType: "five_hour",
@@ -306,6 +308,7 @@ assert.deepStrictEqual(codexAuth, [{
306
308
  message: "401 Unauthorized",
307
309
  authError: true,
308
310
  usageLimit: false,
311
+ transient: false,
309
312
  refusal: false,
310
313
  }]);
311
314
 
@@ -346,4 +349,43 @@ assert.deepStrictEqual(normalizeClaudeEvent(samples.claude.session, isolatedB),
346
349
  );
347
350
  }
348
351
 
352
+ // What a failure means, read one way for chat turns and background calls
353
+ // (9 Oct 2026: the 07:00 dream failed on 18 of 24 fleet bots; Claude Code
354
+ // 2.1.280's own wording below).
355
+ {
356
+ const deadRefresh = "Failed to authenticate: OAuth session expired and could not be refreshed";
357
+ assert.deepStrictEqual(
358
+ [providerErrorFlags(deadRefresh).authError, providerErrorFlags(deadRefresh).transient],
359
+ [true, false],
360
+ "a sign-in that died is a sign-in error (it used to read as a plain provider error)",
361
+ );
362
+ for (const text of [
363
+ "OAuth refresh token is no longer valid; run /login to re-authenticate",
364
+ "API Error: 401 OAuth access token is invalid",
365
+ "OAuth access token is invalid",
366
+ "Not logged in · Please run /login",
367
+ "Your authentication token has expired. Please try signing in again.",
368
+ ]) {
369
+ assert.strictEqual(providerErrorFlags(text).authError, true, text);
370
+ assert.strictEqual(providerFailureCode(providerErrorFlags(text), "UTILITY_PROVIDER_ERROR"), "PROVIDER_UNAUTHENTICATED", text);
371
+ }
372
+ for (const text of [
373
+ "Failed to refresh OAuth token: another Claude Code process is refreshing it or exited mid-refresh. This is usually transient; retry in a minute, and if it persists close other Claude Code processes or sign in again",
374
+ "another Claude Code process is refreshing it (or exited mid-refresh)",
375
+ ]) {
376
+ const flags = providerErrorFlags(text);
377
+ assert.strictEqual(flags.transient, true, text);
378
+ assert.strictEqual(flags.authError, false, "the refresh race is not a dead sign-in");
379
+ assert.strictEqual(providerFailureCode(flags), "PROVIDER_TRANSIENT", text);
380
+ }
381
+ const weekly = providerErrorFlags("You've hit your weekly limit · resets Oct 9, 5am (UTC)");
382
+ assert.strictEqual(providerFailureCode(weekly, "UTILITY_PROVIDER_ERROR"), "PROVIDER_USAGE_LIMIT");
383
+ assert.strictEqual(providerFailureCode({ refusal: true, authError: true }), "PROVIDER_REFUSAL", "a refusal leads");
384
+ assert.strictEqual(providerFailureCode({}, "UTILITY_PROVIDER_ERROR"), "UTILITY_PROVIDER_ERROR");
385
+ assert.strictEqual(providerFailureCode({}), "PROVIDER_EVENT_ERROR");
386
+ // Codex's turn.failed carries the same flags.
387
+ const codexExpired = normalizeCodexEvent({ type: "turn.failed", error: { message: "Your authentication token has expired. Please try signing in again." } }, createCodexParserState());
388
+ assert.strictEqual(codexExpired[0].authError, true);
389
+ }
390
+
349
391
  console.log("provider events OK");
@@ -591,7 +591,21 @@ assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
591
591
  // dedup judge and the classic recall filter reply in an object (the API
592
592
  // refused their bare-array formats on every Claude call), and the utility
593
593
  // agent refuses any reply format without an object root. This Mac bot first.
594
- assert.strictEqual(pkg.version, "3.5.19", "release version must remain unchanged without explicit approval");
594
+ // 3.5.20 approved by Sumeet 2026-10-09 12:02 EAT ("fine go ahead and push but
595
+ // also ensure you clear the images after they all done"): the Browser app in
596
+ // Kazee Chat (bot apps v1): Take over, Teach me with its pictures deleted when
597
+ // the work is done, and the bot paused while a person drives. A canary bot first.
598
+ // 3.5.23 approved by Sumeet 2026-10-09 15:56 EAT ("yeh release and upgrade all
599
+ // the bots, include rahil for the updates going forward"), after 14:18 ("there's
600
+ // something about the cancel that you seem to have broke … one for a qued message
601
+ // a small card with delete queued … get all the messages that are queued at once"),
602
+ // 14:51 ("have it shifted to the new haiku 5.5") and 15:36 ("sonnet should also be
603
+ // just 5.5"): usage limits as one wait-for-reset rule, /stop as a cancel, the
604
+ // queue's ✕ and run-together, Haiku 5.5 and Sonnet 5.5 tiers. Carries 3.5.20.
605
+ // Tagged first as 3.5.21 (cbfd547), whose CI build failed on test-stop-queue's
606
+ // registration before anything published; 3.5.22 (bd95677) failed on two
607
+ // load-sensitive timing checks, now given more time; the release ships as 3.5.23.
608
+ assert.strictEqual(pkg.version, "3.5.23", "release version must remain unchanged without explicit approval");
595
609
  for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
596
610
  assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
597
611
  }