@inetafrica/open-claudia 3.5.77 → 3.5.79

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,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.5.79
4
+
5
+ 3.5.78 plus one change (Sumeet 13 Nov 13:17 EAT, during Anthropic's Claude Code sign-in incident that began 09:58 UTC: "set up wake ups to keep retrying").
6
+
7
+ - **A reply refused as signed out keeps trying** (`core/limit-pickup.js`, `core/runner.js`): when Claude Code is still signed out after the 25 s of re-checks, the message is not dropped. The bot posts one line ("Claude Code can't sign in right now. I'll try your message again every minute for up to 30 minutes. If you write to me before then, I'll answer that instead."), then tries the original message again every minute without posting each failure, answers as soon as sign-in is back, and stops with one line after 30 tries. It uses the usage-limit pick-up store, so a restart keeps it, and writing again, /stop, /new or /end drops it. `SIGNIN_PICKUP_MS` changes the gap; `LIMIT_PICKUP=off` turns both pick-ups off.
8
+
9
+ Tests: `test-signin-pickup.js` (new, in the suite; fails without the change).
10
+
11
+ ## 3.5.78
12
+
13
+ 3.5.77 plus one fix (Sumeet 13 Nov 12:57 EAT: "fix this in the bot", after a short internet drop on the Mac lost replies).
14
+
15
+ - **A short drop no longer refuses replies** (`core/runner.js`): before a run, the bot asks Claude Code whether it is signed in. On 13 Nov the Mac's line kept dropping for about four minutes, one "signed out" answer refused about 21 replies on the spot ("Claude Code authentication failed"), and the sign-in was fine once the line came back. Now a "signed out" answer is checked again after 5, 10 and 10 seconds (about 25 s in all); the reply is refused only if every check says signed out. A live call is never held, and /stop during the wait ends the run quietly. `AUTH_RECHECK_MS` (comma-separated waits in ms) changes the waits; an empty list restores the old single check.
16
+ - **Scheduled jobs wait out a signed-out provider** (`core/scheduler.js`): a wake-up, cron or watch whose provider says signed out (or whose run was refused as signed out) now waits a minute and tries again, up to 30 times, without counting a failed try. Before, it spent its 5 tries 30 s apart and gave up (`SCHEDULED_PROVIDER_UNAVAILABLE`), which is how a wake-up died during the same drop. It gives up, with the usual notice, only if sign-in is still down after half an hour.
17
+
18
+ Tests: `test-auth-recheck.js` (new, in the suite) and a sign-in wait probe in `test-provider-scheduler.js`; both fail without the fix.
19
+
3
20
  ## 3.5.77
4
21
 
5
22
  3.5.76 plus two pieces first run on the Mac build (Sumeet 12 Nov, card 6af54622: one test bot first, then the rest on his word).
@@ -29,6 +29,15 @@ const limits = require("./usage-limits");
29
29
 
30
30
  const FILE = () => process.env.OC_LIMIT_PICKUPS_FILE || path.join(CONFIG_DIR, "limit-pickups.json");
31
31
  const MAX_ATTEMPTS = 5;
32
+ // A reply refused because the provider is signed out (Claude Code's sign-in
33
+ // down: 13 Nov 2026 Anthropic's sign-in incident and a dropping line) is tried
34
+ // again every SIGNIN_PICKUP_MS, quietly, up to SIGNIN_MAX_ATTEMPTS times
35
+ // (Sumeet 13 Nov 13:17 EAT: "set up wake ups to keep retrying").
36
+ const SIGNIN_MAX_ATTEMPTS = 30;
37
+ const signinRetryMs = () => {
38
+ const n = Number(setting("SIGNIN_PICKUP_MS"));
39
+ return Number.isFinite(n) && n > 0 ? n : 60 * 1000;
40
+ };
32
41
  // setTimeout's ceiling is ~24.8 days; a plan window is a week at most.
33
42
  const MAX_TIMER_MS = 2 ** 31 - 1;
34
43
  const BOOT_GRACE_MS = 60000;
@@ -96,6 +105,8 @@ function limitName(provider, limitType) {
96
105
 
97
106
  const PICKUP_PROMPT = (hitAt, provider, original) => `Your ${limits.providerLabel(provider)} usage limit stopped this turn at ${hitAt}. The limit has reset, so pick it up now. The message was: ${original}\n\nCheck what you already did (your history, git, files, tool state, the chat) before acting; never repeat a send, push, deploy or write that already happened; then finish.`;
98
107
 
108
+ const SIGNIN_PICKUP_PROMPT = (hitAt, original) => `This message came in at ${hitAt}, when the bot could not sign in to answer it. It is signed in again, so answer it now. The message was: ${original}\n\nCheck what you already did (your history, git, files, tool state, the chat) before acting; never repeat a send, push, deploy or write that already happened; then finish.`;
109
+
99
110
  /**
100
111
  * What a turn stopped by a limit should do: arm a pick-up (armable), wait in
101
112
  * the scheduler (waits: a scheduled run), or only say when the limit lifts.
@@ -111,7 +122,7 @@ function planFor(result, runContext, { now = Date.now(), windows = [], guarded =
111
122
  const name = guarded ? "My usage limit" : limitName(hit.provider, hit.limitType);
112
123
  const runAt = limits.limitRetryAt(hit.resetsAt, { now });
113
124
  const known = runAt !== null;
114
- const pickup = runContext?.limitPickup || null;
125
+ const pickup = runContext?.limitPickup?.kind === "signin" ? null : (runContext?.limitPickup || null);
115
126
  const attempts = (pickup?.attempts || 0) + 1;
116
127
  const waitable = known && limits.canWaitFor(runAt, { now }) && attempts <= MAX_ATTEMPTS;
117
128
  // A scheduled run waits in the jobs store for the reset (core/scheduler.js).
@@ -148,6 +159,43 @@ function planFor(result, runContext, { now = Date.now(), windows = [], guarded =
148
159
  return { hit, armable, waits, runAt, attempts, notice };
149
160
  }
150
161
 
162
+ /**
163
+ * A foreground reply refused as signed out: try it again in a minute, quietly,
164
+ * up to SIGNIN_MAX_ATTEMPTS times. `notice` is posted (first refusal, or the
165
+ * last one); `quiet` means a retry that is still signed out posts nothing.
166
+ */
167
+ function signinPlanFor(result, runContext, { now = Date.now() } = {}) {
168
+ if (result?.ok || result?.error?.code !== "PROVIDER_UNAUTHENTICATED") return null;
169
+ if (runContext?.purpose !== "foreground") return null;
170
+ const provider = runContext?.provider || result?.provider || "claude";
171
+ const prior = runContext?.limitPickup?.kind === "signin" ? runContext.limitPickup : null;
172
+ const attempts = (prior?.attempts || 0) + 1;
173
+ const runAt = now + signinRetryMs();
174
+ const armable = enabled()
175
+ && attempts <= SIGNIN_MAX_ATTEMPTS
176
+ && !runContext?.passive
177
+ && !runContext?.liveCall
178
+ && !!runContext?.origin?.adapterId
179
+ && !!runContext?.channelId;
180
+ const label = limits.providerLabel(provider) === "Claude" ? "Claude Code" : limits.providerLabel(provider);
181
+ let notice = null;
182
+ if (armable && !prior) {
183
+ notice = `⏳ ${label} can't sign in right now. I'll try your message again every minute for up to ${SIGNIN_MAX_ATTEMPTS} minutes. If you write to me before then, I'll answer that instead.`;
184
+ } else if (!armable && prior) {
185
+ notice = `⏳ ${label} still can't sign in after ${SIGNIN_MAX_ATTEMPTS} tries. Send your message again once it's back.`;
186
+ }
187
+ return {
188
+ kind: "signin",
189
+ hit: { provider, limitType: null, resetsAt: runAt },
190
+ armable,
191
+ waits: false,
192
+ runAt,
193
+ attempts,
194
+ notice,
195
+ quiet: armable && !!prior,
196
+ };
197
+ }
198
+
151
199
  // ── arm / cancel ────────────────────────────────────────────────────────
152
200
 
153
201
  const timers = new Map(); // keyFor(person, conversation) -> timeout
@@ -172,6 +220,7 @@ function arm(runContext, plan, { now = Date.now() } = {}) {
172
220
  replyToMsgId: runContext.replyToMsgId || null,
173
221
  project: runContext.project ? { name: runContext.project.name || null, dir: runContext.project.dir || null } : null,
174
222
  },
223
+ kind: plan.kind || "limit",
175
224
  provider: plan.hit.provider,
176
225
  limitType: plan.hit.limitType || null,
177
226
  resetsAt: plan.hit.resetsAt,
@@ -184,7 +233,8 @@ function arm(runContext, plan, { now = Date.now() } = {}) {
184
233
  update((pickups) => { pickups[key] = record; });
185
234
  index().add(key);
186
235
  schedule(key, record, now);
187
- console.log(`[limit-pickup] armed ${key}: ${record.provider} ${record.limitType || "limit"} resets ${new Date(record.resetsAt).toISOString()}, runs ${new Date(record.runAt).toISOString()} (attempt ${record.attempts})`);
236
+ if (record.kind === "signin") console.log(`[limit-pickup] armed ${key}: ${record.provider} signed out, tries again ${new Date(record.runAt).toISOString()} (attempt ${record.attempts} of ${SIGNIN_MAX_ATTEMPTS})`);
237
+ else console.log(`[limit-pickup] armed ${key}: ${record.provider} ${record.limitType || "limit"} resets ${new Date(record.resetsAt).toISOString()}, runs ${new Date(record.runAt).toISOString()} (attempt ${record.attempts})`);
188
238
  return record;
189
239
  }
190
240
 
@@ -271,6 +321,15 @@ async function fire(key, id) {
271
321
  }
272
322
  const scope = deps.chatScope(record.conversation, adapter, deps);
273
323
  const hitAt = limits.formatResetTime(record.firstHitAt, { now: deps.now() });
324
+ if (record.kind === "signin") {
325
+ // Quiet: a retry that is still signed out posts nothing and arms the next.
326
+ return deps.runInChat(scope, async () => deps.runAgent(SIGNIN_PICKUP_PROMPT(hitAt, record.turn.prompt), record.turn.project?.dir || null, record.turn.replyToMsgId, {
327
+ purpose: "foreground",
328
+ imagePaths: record.turn.imagePaths || [],
329
+ attachments: record.turn.attachments || [],
330
+ limitPickup: { kind: "signin", originalPrompt: record.turn.prompt, firstHitAt: record.firstHitAt, attempts: record.attempts, limitType: null },
331
+ }));
332
+ }
274
333
  return deps.runInChat(scope, async () => {
275
334
  // An outside contact is not told which model or plan the bot runs on.
276
335
  const which = deps.isGuarded() ? "my usage limit" : limitName(record.provider, record.limitType);
@@ -302,6 +361,11 @@ function start(adapters, overrides = {}) {
302
361
  return out;
303
362
  }
304
363
 
364
+ function _setAttemptsForTests(canonicalUserId, laneKey, attempts) {
365
+ const key = keyFor(canonicalUserId, laneKey);
366
+ update((pickups) => { if (pickups[key]) pickups[key].attempts = attempts; });
367
+ }
368
+
305
369
  function _resetForTests() {
306
370
  for (const timer of timers.values()) clearTimeout(timer);
307
371
  timers.clear();
@@ -310,8 +374,8 @@ function _resetForTests() {
310
374
  }
311
375
 
312
376
  module.exports = {
313
- PICKUP_PROMPT, MAX_ATTEMPTS,
377
+ PICKUP_PROMPT, SIGNIN_PICKUP_PROMPT, MAX_ATTEMPTS, SIGNIN_MAX_ATTEMPTS,
314
378
  enabled, delayMs, maxWaitMs,
315
- keyFor, planFor, arm, cancel, cancelAllFor, list, get, start, fire,
316
- _resetForTests,
379
+ keyFor, planFor, signinPlanFor, arm, cancel, cancelAllFor, list, get, start, fire,
380
+ _resetForTests, _setAttemptsForTests,
317
381
  };
package/core/runner.js CHANGED
@@ -808,6 +808,21 @@ function lostSigninRace(result) {
808
808
  }
809
809
 
810
810
  // Wait in short steps so /stop, or a call that hung up, ends the wait.
811
+ // Claude Code's sign-in check can say "signed out" for a moment while the
812
+ // token renews over a line that is dropping: on 13 Nov 2026 the Mac's internet
813
+ // kept dropping for about four minutes and one signed-out answer refused about
814
+ // 21 replies on the spot ("Claude Code authentication failed"), though the
815
+ // sign-in was fine once the line came back. Before refusing, check again after
816
+ // short waits; refuse only when every check says signed out. Never for a live
817
+ // call: its caller would sit through the wait in silence.
818
+ const DEFAULT_AUTH_RECHECK_WAITS_MS = [5 * 1000, 10 * 1000, 10 * 1000];
819
+ function authRecheckWaits() {
820
+ const raw = process.env.AUTH_RECHECK_MS;
821
+ if (raw === undefined || raw === "") return DEFAULT_AUTH_RECHECK_WAITS_MS;
822
+ return String(raw).split(",").map((part) => Number(part.trim()))
823
+ .filter((ms) => Number.isFinite(ms) && ms >= 0);
824
+ }
825
+
811
826
  async function waitUnlessStopped(ms, stopped) {
812
827
  const deadline = Date.now() + ms;
813
828
  while (Date.now() < deadline) {
@@ -864,7 +879,23 @@ function createRunnerService(dependencies = {}) {
864
879
  });
865
880
  }
866
881
  try {
867
- const authFailure = typeof provider.preflightAuth === "function" ? provider.preflightAuth() : null;
882
+ const preflight = () => (typeof provider.preflightAuth === "function" ? provider.preflightAuth() : null);
883
+ let authFailure = preflight();
884
+ const authWaits = runContext.liveCall === true ? [] : authRecheckWaits();
885
+ for (let i = 0; authFailure?.code === "PROVIDER_UNAUTHENTICATED" && i < authWaits.length; i += 1) {
886
+ console.warn(`[auth-recheck] run ${runContext.runId}: ${provider.id || "provider"} sign-in check said signed out; checking again in ${Math.round(authWaits[i] / 1000)}s (check ${i + 2} of ${authWaits.length + 1})`);
887
+ let ended = null;
888
+ await waitUnlessStopped(authWaits[i], () => {
889
+ if (!capture && dependencies.isObsolete?.()) ended = cancelledCallRunResult(runContext);
890
+ else if (dependencies.shouldAbort && dependencies.shouldAbort(runContext, { capture })) {
891
+ ended = cancelledRunResult(runContext, provider, "Cancelled by /stop while checking the sign-in again");
892
+ }
893
+ return !!ended;
894
+ });
895
+ if (ended) return settleImmediateRunResult(ended, runContext, dependencies, capture);
896
+ authFailure = preflight();
897
+ if (!authFailure) console.warn(`[auth-recheck] run ${runContext.runId}: signed in on check ${i + 2}; going ahead`);
898
+ }
868
899
  if (authFailure) return immediateFailure(provider, authFailure);
869
900
  } catch (error) {
870
901
  return immediateFailure(provider, error);
@@ -1854,6 +1885,11 @@ function limitPlanFor(result, runContext) {
1854
1885
  try { guarded = require("./relationship").isCurrentSpeakerGuarded(); } catch (_) {}
1855
1886
  try { plan = limitPickup.planFor(result, runContext, { windows: planLimitWindows(result, runContext), guarded }); }
1856
1887
  catch (error) { console.warn(`[limit-pickup] plan failed: ${redactSensitive(String(error.message || error))}`); }
1888
+ } else if (!result.ok && result.error?.code === "PROVIDER_UNAUTHENTICATED") {
1889
+ // Signed out even after the pre-run re-checks: keep trying the message
1890
+ // every minute instead of dropping it (core/limit-pickup signinPlanFor).
1891
+ try { plan = limitPickup.signinPlanFor(result, runContext); }
1892
+ catch (error) { console.warn(`[limit-pickup] sign-in plan failed: ${redactSensitive(String(error.message || error))}`); }
1857
1893
  }
1858
1894
  return keepOnResult(result, "limitPlan", plan);
1859
1895
  }
@@ -1897,6 +1933,8 @@ function providerRunDiagnostic(result, runContext) {
1897
1933
  const label = provider.label || runContext.provider;
1898
1934
  const code = result.error?.code || "PROVIDER_RUN_FAILED";
1899
1935
  if (code === "PROVIDER_UNAUTHENTICATED") {
1936
+ const plan = limitPlanFor(result, runContext);
1937
+ if (plan?.notice) return plan.notice;
1900
1938
  const help = typeof provider.authHelp === "function" ? provider.authHelp() : "";
1901
1939
  return `${label} authentication failed.${help ? `\n\n${help}` : ""}`;
1902
1940
  }
@@ -2199,6 +2237,8 @@ async function deliverForegroundResult(result, runContext, state = currentState(
2199
2237
  }
2200
2238
  // Nobody asked for a passive reply: a limit that stopped one is not news.
2201
2239
  if (limitPlan && runContext.passive === true) return { ok: true, held: true, suppressed: "passive" };
2240
+ // A sign-in retry that is still signed out says nothing; the next is armed.
2241
+ if (limitPlan?.quiet) return { ok: true, held: true, suppressed: "signin-retry" };
2202
2242
  const diagnostic = providerRunDiagnostic(result, runContext);
2203
2243
  if (diagnostic) result.diagnostic = diagnostic;
2204
2244
  const cleanAnswer = String(result.finalAnswerText || "").trim();
@@ -3499,6 +3539,7 @@ module.exports = {
3499
3539
  shouldRetryMissingConversation,
3500
3540
  lostSigninRace,
3501
3541
  signinRaceWaits,
3542
+ authRecheckWaits,
3502
3543
  retireMissingConversation,
3503
3544
  shouldStartSideChat,
3504
3545
  runSideChatRequest,
package/core/scheduler.js CHANGED
@@ -49,6 +49,13 @@ const LIMIT_WAIT_CLEARED = Object.freeze({
49
49
  limitWaitCount: 0, limitWaitUntil: null, limitWaitType: null, limitWaitProvider: null, limitFirstHitAt: null,
50
50
  });
51
51
  const RETRY_DELAY_MS = 30 * 1000;
52
+ // A signed-out provider is waited out, not counted as a failed try: on 13 Nov
53
+ // 2026 the Mac's line kept dropping for about four minutes, Claude Code's token
54
+ // could not renew, and a wake-up spent its 5 tries 30 s apart and gave up
55
+ // (SCHEDULED_PROVIDER_UNAVAILABLE). A job now waits a minute at a time, up to
56
+ // half an hour, and gives up only if sign-in is still down after that.
57
+ const SIGNIN_WAIT_MS = 60 * 1000;
58
+ const SIGNIN_WAIT_MAX = 30;
52
59
  const BUSY_DELAY_MS = 30 * 1000;
53
60
  // A watch alarm that has waited this long behind a busy room says so in the
54
61
  // chat, once per occurrence, so a busy room never hides an alarm for hours
@@ -243,6 +250,8 @@ function createSchedulerService(dependencies = {}) {
243
250
  );
244
251
  const retryDelayMs = Number.isFinite(dependencies.retryDelayMs) ? dependencies.retryDelayMs : RETRY_DELAY_MS;
245
252
  const busyDelayMs = Number.isFinite(dependencies.busyDelayMs) ? dependencies.busyDelayMs : BUSY_DELAY_MS;
253
+ const signinWaitMs = Number.isFinite(dependencies.signinWaitMs) ? dependencies.signinWaitMs : SIGNIN_WAIT_MS;
254
+ const signinWaitMax = Number.isFinite(dependencies.signinWaitMax) ? dependencies.signinWaitMax : SIGNIN_WAIT_MAX;
246
255
  const busyNoticeAfterMs = Number.isFinite(dependencies.busyNoticeAfterMs) ? dependencies.busyNoticeAfterMs : BUSY_NOTICE_AFTER_MS;
247
256
  const missedGraceMs = Number.isFinite(dependencies.missedGraceMs) ? dependencies.missedGraceMs : MISSED_WAKEUP_GRACE_MS;
248
257
  const watchTimeoutMs = Number.isFinite(dependencies.watchTimeoutMs) ? dependencies.watchTimeoutMs : DEFAULT_WATCH_TIMEOUT_MS;
@@ -666,6 +675,7 @@ function createSchedulerService(dependencies = {}) {
666
675
  disabled: false,
667
676
  occurrenceId: occurrenceIdFactory(latest),
668
677
  attemptCount: 0,
678
+ signinWaitCount: 0,
669
679
  busyDeferralCount: 0,
670
680
  nextAttemptAt: null,
671
681
  lastFireAt: now(),
@@ -707,6 +717,36 @@ function createSchedulerService(dependencies = {}) {
707
717
  return { ok: false, status: updated.status, job: updated, error: updated.lastError };
708
718
  }
709
719
 
720
+ // Sign-in down (the provider says signed out, or the runner refused the run
721
+ // as signed out after its own re-checks): wait and try again, without
722
+ // counting a failed try, up to signinWaitMax waits. Returns null when the
723
+ // waits are used up, so the caller fails the job as before.
724
+ function markSigninWait(job, failure, { attemptCounted = false } = {}) {
725
+ const latest = jobsStore.get(job.id) || job;
726
+ const waits = Number(latest.signinWaitCount) || 0;
727
+ if (waits >= signinWaitMax) return null;
728
+ const patch = {
729
+ status: "deferred",
730
+ disabled: false,
731
+ attemptCount: Math.max(0, (Number(latest.attemptCount) || 0) - (attemptCounted ? 1 : 0)),
732
+ nextAttemptAt: now() + signinWaitMs,
733
+ lastFireAt: now(),
734
+ lastFireOk: false,
735
+ lastError: stableFailure(failure),
736
+ signinWaitCount: waits + 1,
737
+ };
738
+ const updated = jobsStore.update(job.id, patch) || { ...latest, ...patch };
739
+ armRetry(updated, signinWaitMs);
740
+ console.log(`[scheduler] ${job.id}: provider signed out; waiting ${Math.round(signinWaitMs / 1000)} s for sign-in (wait ${patch.signinWaitCount} of ${signinWaitMax})`);
741
+ return { ok: false, status: "deferred", signinWait: true, job: updated };
742
+ }
743
+
744
+ function signedOut(job, state) {
745
+ const current = getActiveProvider(state);
746
+ return [current, job.provider].filter(Boolean)
747
+ .some((id) => providerStatusSafe(id)?.auth?.state === "unauthenticated");
748
+ }
749
+
710
750
  function markBusy(job) {
711
751
  const latest = jobsStore.get(job.id) || job;
712
752
  const busyDeferralCount = (latest.busyDeferralCount || 0) + 1;
@@ -771,6 +811,7 @@ function createSchedulerService(dependencies = {}) {
771
811
 
772
812
  async function settleSuccess(job, result, target) {
773
813
  const successPatch = {
814
+ signinWaitCount: 0,
774
815
  status: "succeeded",
775
816
  disabled: false,
776
817
  nextAttemptAt: null,
@@ -952,11 +993,13 @@ function createSchedulerService(dependencies = {}) {
952
993
 
953
994
  const target = executionTarget(job, state);
954
995
  if (!target) {
996
+ const failure = {
997
+ code: "SCHEDULED_PROVIDER_UNAVAILABLE",
998
+ message: `Saved provider ${job.provider} is unavailable and no explicit fallback is configured`,
999
+ };
955
1000
  try {
956
- return markFailure(job, {
957
- code: "SCHEDULED_PROVIDER_UNAVAILABLE",
958
- message: `Saved provider ${job.provider} is unavailable and no explicit fallback is configured`,
959
- });
1001
+ const waiting = signedOut(job, state) ? markSigninWait(job, failure) : null;
1002
+ return waiting || markFailure(job, failure);
960
1003
  } catch (error) { return keepRetrying(job, error); }
961
1004
  }
962
1005
 
@@ -1056,6 +1099,12 @@ function createSchedulerService(dependencies = {}) {
1056
1099
  try { return markLimitWait(running, limitPlan, result); }
1057
1100
  catch (error) { return { ...keepRetrying(running, error), result }; }
1058
1101
  }
1102
+ if (result?.error?.code === "PROVIDER_UNAUTHENTICATED") {
1103
+ try {
1104
+ const waiting = markSigninWait(running, result, { attemptCounted: true });
1105
+ if (waiting) return { ...waiting, result };
1106
+ } catch (error) { return { ...keepRetrying(running, error), result }; }
1107
+ }
1059
1108
  try { return markFailure(running, result, { attemptAlreadyCounted: true }); }
1060
1109
  catch (error) { return { ...keepRetrying(running, error), result }; }
1061
1110
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "3.5.77",
3
+ "version": "3.5.79",
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-effort-reset.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 && node test-call-flags.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-test-run-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-owner-cards-dm.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-quiet-defaults.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-thread-handoff.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-cost-by-part.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-card-blocks.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-speech-voices.js && node test-tts-voice.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-kazee-catchup.js && node test-usage-limits.js && node test-limit-waits.js && node test-signin-race-retry.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-address.js && node test-browser-control.js && node test-browser-drive.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 && node test-call-quick-voice.js && node test-call-run-stop.js && node test-call-stopped-transcript.js && node test-call-greeting-context.js && node test-call-camera.js && node test-switchboard-calls.js && node test-switchboard-supervisor.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-topic-lessons.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-env-pins.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-session-core.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-related.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-decisions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-external.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-introspection-retry.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-every-cron.js && node test-rust-cli-parity.js && OPEN_CLAUDIA_TEST=1 node test-bot-acts-for-owner.js && node test-walker-idle-stop.js && node test-usage-history-lazy.js && node test-node-flags-entrypoint.js && node test-recall-engine-env.js && node test-review-queue.js && node test-lazy-heavy-libs.js && node test-lessons-meta-keep.js && node test-views.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-test-run-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-owner-cards-dm.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-quiet-defaults.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-thread-handoff.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-cost-by-part.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-card-blocks.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-speech-voices.js && node test-tts-voice.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-kazee-catchup.js && node test-usage-limits.js && node test-limit-waits.js && node test-signin-race-retry.js && node test-auth-recheck.js && node test-signin-pickup.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-address.js && node test-browser-control.js && node test-browser-drive.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 && node test-call-quick-voice.js && node test-call-run-stop.js && node test-call-stopped-transcript.js && node test-call-greeting-context.js && node test-call-camera.js && node test-switchboard-calls.js && node test-switchboard-supervisor.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-topic-lessons.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-env-pins.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-session-core.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-related.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-decisions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-external.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-introspection-retry.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-every-cron.js && node test-rust-cli-parity.js && OPEN_CLAUDIA_TEST=1 node test-bot-acts-for-owner.js && node test-walker-idle-stop.js && node test-usage-history-lazy.js && node test-node-flags-entrypoint.js && node test-recall-engine-env.js && node test-review-queue.js && node test-lazy-heavy-libs.js && node test-lessons-meta-keep.js && node test-views.js"
15
15
  },
16
16
  "files": [
17
17
  "bot.js",
@@ -669,13 +669,19 @@ assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
669
669
  // one-tap switch-on card (from the Maia build: plan v2 of 25 Oct, SEC-28 of 30 Oct).
670
670
  // 3.5.77 = 3.5.76 + Fable 5.2 in the picker and prices, call --help never dials
671
671
  // (Sumeet 12 Nov 07:53 EAT, card 6af54622: "Build 3.5.77 with the 3 pieces, test on one bot, roll on your word").
672
+ // 3.5.79 = 3.5.78 + a reply refused as signed out is tried again every minute, quietly,
673
+ // up to 30 times (Sumeet 13 Nov 13:17 EAT, during Anthropic's sign-in incident:
674
+ // "set up wake ups to keep retrying").
675
+ // 3.5.78 = 3.5.77 + sign-in re-checks before a reply is refused, and scheduled
676
+ // jobs wait out a signed-out provider (Sumeet 13 Nov 12:57 EAT, main chat: "fix
677
+ // this in the bot ... ship in the next bot release through the normal release flow").
672
678
  // 3.5.76 = 3.5.75 + memory cuts, the lessons ranking fix, the empty-lock fix,
673
679
  // handoff --new, HTML views and the type-check guard (Sumeet 9 Nov: "all bots get all features").
674
680
  // 3.5.73 = 3.5.72 + a smaller image (Sumeet 31 Oct 15:06, bot resource plan: "proceed ... roll out the others").
675
681
  // 3.5.72 = 3.5.71 + the Mac memory fixes of 22-28 Oct:
676
682
  // topic lessons ride with their pack, .env recall pins, prompt fixes, past
677
683
  // chats reach recall, settled decisions, /engine x|mem0, nightly retry.
678
- assert.strictEqual(pkg.version, "3.5.77", "release version must remain unchanged without explicit approval");
684
+ assert.strictEqual(pkg.version, "3.5.79", "release version must remain unchanged without explicit approval");
679
685
  for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
680
686
  assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
681
687
  }
@@ -1353,6 +1353,65 @@ async function durablePendingProbe(createSchedulerService, jobsApi) {
1353
1353
  }
1354
1354
  }
1355
1355
 
1356
+ // A sign-in that is down for a few minutes (13 Nov 2026: the Mac's line kept
1357
+ // dropping 09:47-09:51Z, Claude Code's token could not renew, and a wake-up
1358
+ // burned its 5 tries 30 s apart and "gave up (SCHEDULED_PROVIDER_UNAVAILABLE)")
1359
+ // is a wait, not a failed try: the job waits and fires once sign-in is back.
1360
+ // Only a sign-in that stays down past the most waits gives up.
1361
+ async function signinWaitProbe(createSchedulerService) {
1362
+ let signedIn = false;
1363
+ const h = schedulerHarness(createSchedulerService, [baseJob({ id: "signin", maxAttempts: 5 })], {
1364
+ providerStatus: () => (signedIn
1365
+ ? { availability: { state: "available" }, auth: { state: "authenticated" } }
1366
+ : { availability: { state: "available" }, auth: { state: "unauthenticated", reason: "OAuth token has expired" } }),
1367
+ outcomes: [{
1368
+ ok: true,
1369
+ status: "succeeded",
1370
+ persistence: { transcript: { ok: true }, usage: { ok: true }, session: { ok: true } },
1371
+ delivery: { ok: true },
1372
+ }],
1373
+ });
1374
+ for (let i = 0; i < 8; i += 1) {
1375
+ const waiting = await h.service.fireJob("signin");
1376
+ assert.strictEqual(waiting.ok, false);
1377
+ const job = h.jobsStore.get("signin");
1378
+ assert.notStrictEqual(job.status, "dead-letter", `signed out on try ${i + 1} must not give up (the 13 Nov wake-up died after 5 tries)`);
1379
+ assert.strictEqual(job.status, "deferred", "a signed-out provider defers the job");
1380
+ assert.strictEqual(job.disabled, false);
1381
+ assert.strictEqual(job.attemptCount || 0, 0, "a sign-in wait is not a failed try");
1382
+ assert.strictEqual(job.signinWaitCount, i + 1);
1383
+ }
1384
+ assert.strictEqual(h.runCalls.length, 0);
1385
+ assert.strictEqual(h.sends.filter((t) => /gave up/.test(t)).length, 0, "no death notice while waiting");
1386
+ signedIn = true;
1387
+ const ran = await h.service.fireJob("signin");
1388
+ assert.strictEqual(ran.ok, true, "fires once sign-in is back");
1389
+ assert.strictEqual(h.runCalls.length, 1);
1390
+ assert.ok(!h.jobsStore.get("signin")?.signinWaitCount, "the wait count clears on success (a one-shot is removed)");
1391
+
1392
+ // A run the runner refused as signed out (after its own re-checks) waits too.
1393
+ const r = schedulerHarness(createSchedulerService, [baseJob({ id: "refused", maxAttempts: 1 })], {
1394
+ outcomes: [{
1395
+ ok: false,
1396
+ status: "failed",
1397
+ error: { code: "PROVIDER_UNAUTHENTICATED", message: "Claude Code authentication failed" },
1398
+ persistence: { transcript: { ok: true }, usage: { ok: true }, session: { ok: true } },
1399
+ delivery: { ok: true },
1400
+ }],
1401
+ });
1402
+ await r.service.fireJob("refused");
1403
+ const refused = r.jobsStore.get("refused");
1404
+ assert.strictEqual(refused.status, "deferred", "a run refused as signed out waits instead of dead-lettering");
1405
+ assert.strictEqual(refused.signinWaitCount, 1);
1406
+
1407
+ // Signed out past the most waits: gives up once, with the usual notice.
1408
+ const d = schedulerHarness(createSchedulerService, [baseJob({ id: "dead", maxAttempts: 1, signinWaitCount: 30 })], {
1409
+ providerStatus: () => ({ availability: { state: "available" }, auth: { state: "unauthenticated" } }),
1410
+ });
1411
+ await d.service.fireJob("dead");
1412
+ assert.strictEqual(d.jobsStore.get("dead").status, "dead-letter", "a sign-in down past 30 waits gives up");
1413
+ }
1414
+
1356
1415
  async function unavailableAndFallbackProbe(createSchedulerService) {
1357
1416
  const noFallback = schedulerHarness(createSchedulerService, [baseJob({ id: "unavailable" })], {
1358
1417
  providerStatus: () => ({ availability: { state: "missing" }, auth: { state: "not_checked" } }),
@@ -1713,6 +1772,7 @@ async function main() {
1713
1772
  listingProbe();
1714
1773
  await strandedDeliveryProbe(schedulerApi.createSchedulerService);
1715
1774
  await unavailableAndFallbackProbe(schedulerApi.createSchedulerService);
1775
+ await signinWaitProbe(schedulerApi.createSchedulerService);
1716
1776
  await cronOccurrenceProbe(schedulerApi.createSchedulerService);
1717
1777
  await watchGateProbe(schedulerApi.createSchedulerService);
1718
1778
  watchExpiryStampProbe(schedulerApi.createSchedulerService, schedulerApi.DEFAULT_WATCH_TIMEOUT_MS);