@inetafrica/open-claudia 3.4.3 → 3.4.5

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
+ ## v3.4.5 — Astra on the picker, two shortcuts fewer
4
+
5
+ - **gpt-6-astra is offered by the codex provider.** OpenAI rolled GPT-6 (Astra) out to ChatGPT-plan Codex accounts on 2026-09-05 — the earlier 400 (`not supported when using Codex with a ChatGPT account`) was a staged rollout, not client staleness. It sits first in `CODEX_MODELS`, so it appears as a `/model` button; the typed form `/model gpt-6-astra` already worked because the typed path never validated against the list. The default model is unchanged (`gpt-5.6-sol` unless `CODEX_MODEL` is set).
6
+ - **`/claude` and `/codex` are gone.** They were one-line aliases of `/backend claude|codex`, and the `/model` picker switches the backend by itself, so three ways to do one thing became one. `/backend` stays.
7
+ - **Codex CLI: the Dockerfile installs `@openai/codex` unpinned, so every image build bakes the then-latest CLI; a running pod keeps whatever its image carried until the next build + roll.** This release's image picks up 0.153.4.
8
+
9
+ ## v3.4.4 — The room's model was saved, shown, and never once used
10
+
11
+ - **A model pinned in a room was silently ignored by every turn in it.** `/model` wrote the choice to the room, `/status` read it back from the room, and the picker highlighted it — all three already room-aware. Only the code that actually launches the provider was not: `captureRunContext` read `state.providerSettings[provider]`, the *speaker's* own bucket, so the room's pinned model was never consulted. The setting looked applied everywhere a human could look while every turn ran the personal default.
12
+ - **This was a v3.4.0 regression, not a long-standing bug.** `run-context.js` dates from 10 Jul, when the speaker's bucket was the only scope that existed and reading it directly was correct. v3.4.0 introduced rooms and the room-aware resolver but never revisited that read. Channels without a room are unaffected — the resolver returns the same personal bucket there.
13
+ - **Three more call sites had the same shape.** `applyProviderSetting` wrote `/effort`, `/budget`, `/plan` and `/worktree` to the speaker instead of the room; the provider-switch payload reported the speaker's bucket; and usage records attributed the wrong model, so cost accounting named a model the turn never ran on.
14
+ - **`getProviderSettings` was not identity-stable.** It rebuilt the bucket (`{ ...defaults, ...bucket }`) and assigned it back on every call, so any caller holding the previous reference wrote into a detached copy that was then discarded. Nothing depended on the rebuild; it now backfills missing defaults in place.
15
+ - **Reads must not mutate.** Routing the run-context read through `getProviderSettings` made merely capturing a turn seed room buckets into `savedState` — side-chat execution began mutating main state. Reads now go through a new pure `resolveProviderSettings`, which creates nothing; the mutating accessor is reserved for the setter that genuinely needs the live object.
16
+ - `test-room-provider-settings.js` pins the behaviour and was verified to FAIL against unmodified code with exactly the reported symptom — `got "personal-default-model", expected "room-pinned-model"` — and also pins that an unpinned room does not inherit another room's choice.
17
+
18
+ - **Kazee replied to everything twice.** chat-central fans `message:new` out to both `chatId` and each participant's `user:<id>` room so delivery survives a failed join. The bot only ever sat in its own user room until v3.4.2 joined chat rooms for the typing indicator — which put it in both, so every inbound message was handled twice and `/model` answered with two identical pickers. The join is correct; the missing half was the id-dedupe every other client already does. Inbound ids are now remembered (bounded, oldest evicted); an id-less message is still let through rather than silently swallowed.
19
+
3
20
  ## v3.4.3 — The chip knew it had started typing; nobody asked it in time
4
21
 
5
22
  - **v3.4.2 restored the indicator and immediately exposed a second bug underneath it.** With delivery fixed, the chip reached the client — and then sat on "thinking" for the whole turn and stopped, never showing "typing". Same symptom, different layer: 3.4.2 was DELIVERY, this is CONTENT.
package/Dockerfile CHANGED
@@ -8,7 +8,7 @@ FROM ${BASE_IMAGE}
8
8
  RUN curl -fsSL https://claude.ai/install.sh | sh || \
9
9
  npm install -g @anthropic-ai/claude-code
10
10
 
11
- # Install OpenAI Codex CLI for the /codex backend
11
+ # Install OpenAI Codex CLI for the codex backend (unpinned: each image build bakes the then-latest CLI)
12
12
  RUN npm install -g @openai/codex
13
13
 
14
14
  # Browser automation for the agent: vercel-labs agent-browser (Rust CLI, token-efficient,
package/README.md CHANGED
@@ -8,7 +8,7 @@ Send text, voice notes, screenshots, and files from your phone. Open Claudia run
8
8
 
9
9
  ### Channels & providers
10
10
  - **Multi-channel** — run the same bot on Telegram, Kazee Chat, or both at once (`CHANNELS=telegram,kazee`). Each channel renders keyboards, files, voice notes, and edits natively
11
- - **Multi-provider** — switch between Claude Code and OpenAI Codex on the fly (`/claude`, `/codex`); each keeps its own project-scoped session state
11
+ - **Multi-provider** — switch between Claude Code and OpenAI Codex on the fly (`/backend claude|codex`, or pick a model in `/model`); each keeps its own project-scoped session state
12
12
  - **Multi-user / team mode** — one bot serves multiple authorized users in parallel, each with their own conversations, settings, model, provider, and usage counters
13
13
  - **Multi-project sessions** — switch between workspace projects; per-project conversation history auto-resumes
14
14
 
@@ -137,8 +137,6 @@ If installed as a background service, the bot starts automatically on login and
137
137
 
138
138
  | Command | Description |
139
139
  |---------|-------------|
140
- | `/claude` | Switch to the Claude Code provider |
141
- | `/codex` | Switch to the OpenAI Codex provider |
142
140
  | `/backend` | Show the current provider with picker |
143
141
 
144
142
  Each provider keeps its own persistent project session. Switching doesn't lose your place — you can go back and forth freely.
@@ -24,6 +24,15 @@ const kazeeState = require("./state");
24
24
  // late is worth less than the next heartbeat tick, which retries the join.
25
25
  const KAZEE_JOIN_ACK_TIMEOUT_MS = 5000;
26
26
 
27
+ // chat-central deliberately fans message:new out TWICE to anyone in the chat
28
+ // room: once via to(chatId) and again via to(`user:<id>`) per participant, so
29
+ // delivery survives a failed room join (see broadcastBridge). Ordinary clients
30
+ // dedupe by message id; the bot never had to, because until it joined chat
31
+ // rooms for the typing indicator it only ever sat in its own user room. How
32
+ // many ids to remember: large enough to cover a burst, small enough to stay
33
+ // trivial to scan.
34
+ const INBOUND_DEDUP_MEMORY = 500;
35
+
27
36
  const CONTEXT_MAX_MESSAGES = 40;
28
37
  const CONTEXT_BODY_LIMIT = 280;
29
38
  const COLD_START_WINDOW = 15;
@@ -71,6 +80,8 @@ class KazeeAdapter {
71
80
  // on every (re)connect.
72
81
  this._joinedRooms = new Set();
73
82
  this._joinPending = new Map();
83
+ // Insertion-ordered, so the oldest id is always the first key.
84
+ this._seenMessageIds = new Set();
74
85
  this._callManager = new KazeeCallManager({ adapter: this });
75
86
  }
76
87
 
@@ -186,8 +197,24 @@ class KazeeAdapter {
186
197
  });
187
198
  }
188
199
 
200
+ // True the SECOND time a message id is seen. An id-less message is always
201
+ // let through: dropping every one of them would silently swallow real
202
+ // traffic, and the duplicate pair we are guarding against always carries an
203
+ // id (it is the same persisted document emitted down two paths).
204
+ _isDuplicateInbound(msg) {
205
+ const id = String(msg._id || msg.id || "");
206
+ if (!id) return false;
207
+ if (this._seenMessageIds.has(id)) return true;
208
+ this._seenMessageIds.add(id);
209
+ if (this._seenMessageIds.size > INBOUND_DEDUP_MEMORY) {
210
+ this._seenMessageIds.delete(this._seenMessageIds.values().next().value);
211
+ }
212
+ return false;
213
+ }
214
+
189
215
  async _handleInboundMessage(msg) {
190
216
  if (!msg) return;
217
+ if (this._isDuplicateInbound(msg)) return;
191
218
  const senderId = String(msg.sender?._id || msg.sender?.id || msg.senderId || "");
192
219
  if (this.botUserId && senderId === String(this.botUserId)) return; // ignore our own echoes
193
220
  const chatField = msg.chat;
package/core/handlers.js CHANGED
@@ -17,7 +17,7 @@ const { currentAdapter, currentChannelId } = require("./context");
17
17
  const {
18
18
  currentState, saveState, rootSession,
19
19
  freshUsage, getActiveProvider, getProjectKey, getProviderSession, setProviderSession, clearProviderSession,
20
- getProviderSettings,
20
+ getProviderSettings, resolveProviderSettings,
21
21
  getProjectSessions, getLastProjectSession, userOwnsProviderSession,
22
22
  createSessionCallbackToken, linkIdentity,
23
23
  roomSettingsFor, setRoomSetting,
@@ -176,7 +176,10 @@ function applyProviderSetting(state, providerId, setting, value, options = {}) {
176
176
  }
177
177
  }
178
178
 
179
- const settings = state.providerSettings?.[provider.id] || getProviderSettings(state, provider.id);
179
+ // Room-aware: a setting changed inside a room lands on the room, so every
180
+ // speaker there gets the same assistant. Preferring the raw personal bucket
181
+ // here wrote /effort, /budget and /plan to the speaker instead.
182
+ const settings = getProviderSettings(state, provider.id);
180
183
  const previous = settings[setting];
181
184
  settings[setting] = normalized;
182
185
  if (options.persist) {
@@ -232,7 +235,7 @@ function switchProviderState(providerId, options = {}) {
232
235
  state.isFirstMessage = previous.isFirstMessage;
233
236
  });
234
237
  return persisted.ok
235
- ? { ok: true, provider, sessionId: getProviderSession(state, provider, project), settings: state.providerSettings[provider] }
238
+ ? { ok: true, provider, sessionId: getProviderSession(state, provider, project), settings: resolveProviderSettings(state, provider) }
236
239
  : persisted;
237
240
  }
238
241
 
@@ -1534,30 +1537,6 @@ register({
1534
1537
  },
1535
1538
  });
1536
1539
 
1537
- async function switchProviderCommand(env, providerId) {
1538
- if (!authorized(env)) return;
1539
- const provider = providerFromRegistry(providerId);
1540
- if (!provider) return send(`Unknown provider: ${providerId}`);
1541
- if (!providerAvailability(provider)) {
1542
- return send(`${provider.label} is unavailable. Install its CLI or configure its path, then run /doctor.`);
1543
- }
1544
- const state = currentState();
1545
- const switched = switchProviderState(provider.id, { state });
1546
- if (!switched.ok) return send(`Could not switch provider: ${switched.error.message}`);
1547
- const session = switched.sessionId ? `\nSession: ${switched.sessionId.slice(0, 8)}...` : "\nNew session.";
1548
- return send(`Switched to ${provider.label}.${session}`);
1549
- }
1550
-
1551
- register({
1552
- name: "claude", description: "Switch to Claude Code provider",
1553
- handler: async (env) => switchProviderCommand(env, "claude"),
1554
- });
1555
-
1556
- register({
1557
- name: "codex", description: "Switch to OpenAI Codex provider",
1558
- handler: async (env) => switchProviderCommand(env, "codex"),
1559
- });
1560
-
1561
1540
  register({
1562
1541
  name: "backend", description: "Show/switch active provider", args: "[claude|codex]",
1563
1542
  handler: async (env, { tail }) => {
@@ -7,7 +7,8 @@ const { createCodexParserState, normalizeCodexEvent } = require("./codex-events"
7
7
  const { createCodexHookTransport } = require("./codex-hook");
8
8
 
9
9
  // gpt-5.6-sol-pro is rejected on ChatGPT-plan auth (400 from the API); it needs API-key billing.
10
- const CODEX_MODELS = Object.freeze(["gpt-5.6-sol", "gpt-5.6-sol-pro", "gpt-5.6-terra", "gpt-5.6-luna"]);
10
+ // gpt-6-astra (GPT-6) reached ChatGPT-plan accounts on 2026-09-05; verified live with codex 0.153.
11
+ const CODEX_MODELS = Object.freeze(["gpt-6-astra", "gpt-5.6-sol", "gpt-5.6-sol-pro", "gpt-5.6-terra", "gpt-5.6-luna"]);
11
12
  // max + ultra verified live against codex 0.144 (ultra = proactive multi-agent behavior).
12
13
  const CODEX_EFFORT_VALUES = Object.freeze(["minimal", "low", "medium", "high", "xhigh", "max", "ultra"]);
13
14
  const REQUIRED_EXEC_HELP_FLAGS = Object.freeze(["--config", "--json", "--sandbox", "--image", "--model", "--skip-git-repo-check"]);
@@ -3,6 +3,7 @@
3
3
  const crypto = require("crypto");
4
4
  const path = require("path");
5
5
  const { currentConversationKey } = require("./context");
6
+ const { resolveProviderSettings } = require("./state");
6
7
 
7
8
  function cloneSerializable(value, label = "run context value") {
8
9
  if (value === undefined) return undefined;
@@ -109,8 +110,15 @@ function captureRunContext({
109
110
  const resolvedChannelId = channelId != null
110
111
  ? String(channelId)
111
112
  : (origin.channelId || (state.channelId != null ? String(state.channelId) : null));
113
+ // Inside a room the model/effort/budget belong to the room, not to whoever
114
+ // happens to be speaking, so this must go through the room-aware resolver.
115
+ // Reading `state.providerSettings[provider]` directly took the speaker's own
116
+ // bucket and silently ignored a room's pinned model: the picker saved it and
117
+ // /status reported it (both already room-aware) while every turn still ran
118
+ // the personal default. Outside a room the resolver returns that same
119
+ // personal bucket, so non-room channels are unaffected.
112
120
  const providerSettings = cloneSerializable({
113
- ...(state.providerSettings?.[provider] || state.settings || {}),
121
+ ...(resolveProviderSettings(state, provider) || {}),
114
122
  ...(opts.providerSettings || {}),
115
123
  }, "provider settings");
116
124
  delete providerSettings.backend;
package/core/runner.js CHANGED
@@ -15,6 +15,7 @@ const {
15
15
  currentState, saveState, recordSession,
16
16
  resetSessionUsage, acquireRunLock, getActiveProvider, getProjectKey,
17
17
  getProviderSession, setProviderSession, clearProviderSession, quarantineProviderSession, getUsageForSession, commitProviderCompaction,
18
+ resolveProviderSettings,
18
19
  } = require("./state");
19
20
  const { chatContext, currentChannelId, currentAdapter } = require("./context");
20
21
  const { buildTurnPrompt } = require("./system-prompt");
@@ -167,7 +168,7 @@ function logTurnUsage(state, usage, costUsd, announce, opts = {}) {
167
168
  provider: backend,
168
169
  model: Object.prototype.hasOwnProperty.call(opts, "model")
169
170
  ? (opts.model || null)
170
- : (state.providerSettings?.[backend]?.model || getProvider(backend).defaultModel()),
171
+ : (resolveProviderSettings(state, backend)?.model || getProvider(backend).defaultModel()),
171
172
  userId: opts.userId || state.userId || null,
172
173
  sessionId: Object.prototype.hasOwnProperty.call(opts, "sessionId")
173
174
  ? (opts.sessionId || null)
package/core/state.js CHANGED
@@ -519,6 +519,40 @@ function isSessionQuarantined(state, sessionId) {
519
519
  && isRecord(state.quarantinedSessions[sessionId]));
520
520
  }
521
521
 
522
+ // Add any missing defaults to an existing settings bucket IN PLACE. Rebuilding
523
+ // it (`{ ...defaults, ...bucket }`) returned a fresh object on every call, so a
524
+ // caller holding the previous reference silently wrote to a detached copy —
525
+ // the settings object has to keep its identity across calls.
526
+ function backfillDefaults(bucket) {
527
+ const defaults = providerDefaults();
528
+ for (const key of Object.keys(defaults)) {
529
+ if (!Object.prototype.hasOwnProperty.call(bucket, key)) bucket[key] = defaults[key];
530
+ }
531
+ return bucket;
532
+ }
533
+
534
+ // Read-only view of the effective provider settings for a turn: the room's
535
+ // bucket when the turn happens inside a room, otherwise the speaker's own,
536
+ // with defaults filled in. Unlike getProviderSettings this creates nothing and
537
+ // mutates nothing — capturing a run context and writing a usage record are
538
+ // reads, and must not seed buckets or write into savedState as a side effect.
539
+ function resolveProviderSettings(state, providerId) {
540
+ const provider = normalizeProviderId(providerId || getActiveProvider(state), null);
541
+ if (!provider) return providerDefaults();
542
+ const roomKey = state && state.roomKey ? String(state.roomKey) : "";
543
+ const room = roomKey && isRecord(savedState.rooms)
544
+ && isRecord(savedState.rooms[roomKey])
545
+ && isRecord(savedState.rooms[roomKey].providerSettings)
546
+ && isRecord(savedState.rooms[roomKey].providerSettings[provider])
547
+ ? savedState.rooms[roomKey].providerSettings[provider]
548
+ : null;
549
+ if (room) return { ...providerDefaults(), ...room };
550
+ const personal = isRecord(state && state.providerSettings) && isRecord(state.providerSettings[provider])
551
+ ? state.providerSettings[provider]
552
+ : {};
553
+ return { ...providerDefaults(), ...personal };
554
+ }
555
+
522
556
  function getProviderSettings(state, providerId) {
523
557
  if (!state || typeof state !== "object") throw new TypeError("state is required");
524
558
  const provider = normalizeProviderId(providerId || getActiveProvider(state), null);
@@ -529,7 +563,7 @@ function getProviderSettings(state, providerId) {
529
563
  }
530
564
  if (!isRecord(state.providerSettings)) state.providerSettings = {};
531
565
  if (!isRecord(state.providerSettings[provider])) state.providerSettings[provider] = providerDefaults();
532
- else state.providerSettings[provider] = { ...providerDefaults(), ...state.providerSettings[provider] };
566
+ else backfillDefaults(state.providerSettings[provider]);
533
567
  // Inside a room, model/effort/budget belong to the room, so every speaker
534
568
  // gets the same assistant. The room's bucket is seeded from the speaker who
535
569
  // first pins it, then diverges — mutations by callers land on the room object
@@ -543,7 +577,7 @@ function getProviderSettings(state, providerId) {
543
577
  if (!isRecord(room.providerSettings[provider])) {
544
578
  room.providerSettings[provider] = { ...providerDefaults(), ...state.providerSettings[provider] };
545
579
  } else {
546
- room.providerSettings[provider] = { ...providerDefaults(), ...room.providerSettings[provider] };
580
+ backfillDefaults(room.providerSettings[provider]);
547
581
  }
548
582
  return room.providerSettings[provider];
549
583
  }
@@ -1490,6 +1524,7 @@ module.exports = {
1490
1524
  quarantineProviderSession,
1491
1525
  isSessionQuarantined,
1492
1526
  getProviderSettings,
1527
+ resolveProviderSettings,
1493
1528
  getActiveUsage,
1494
1529
  getUsageForSession,
1495
1530
  usageSessionKey,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "3.4.3",
3
+ "version": "3.4.5",
4
4
  "description": "An always-on, provider-agnostic coding-agent harness for Claude Code and OpenAI Codex via chat",
5
5
  "main": "bot.js",
6
6
  "bin": {
@@ -10,8 +10,8 @@
10
10
  "test:kazee-call": "node test-kazee-call-audio.js && node test-kazee-call-janus.js && node test-kazee-call-webrtc.js && node test-kazee-call-speech.js && node test-kazee-call-manager.js && node test-kazee-call-session.js",
11
11
  "setup": "node setup.js",
12
12
  "start": "node bot.js",
13
- "pretest": "node test-provider-fixture.js && node test-provider-core-prompt.js && node test-turn-observer.js && node test-provider-prompt-parity.js && node test-provider-env-isolation.js && node test-provider-resume-parity.js && node test-provider-bootstrap.js && node test-provider-registry.js && node test-provider-stream-decoder.js && node test-provider-events.js && node test-provider-claude.js && node test-provider-codex.js && node test-provider-runner.js && node test-provider-refusal-recovery.js && node test-provider-run-lifecycle.js && node test-provider-gateway.js && node test-provider-side-chat.js && node test-single-runtime.js && node test-provider-migration-backup.js && node test-provider-state-migration.js && node test-provider-session-history.js && node test-provider-session-commands.js && node test-provider-compaction.js && node test-provider-scheduler.js && node test-credential-capture.js && node test-cursor-removal.js && node test-utility-provider-policy.js && node test-provider-subagent.js && node test-provider-recall.js && node test-provider-pack-review.js && node test-memory-mutation-queue.js && node test-provider-dream.js && node test-provider-enforcer.js && node test-provider-tool-hooks.js && node test-provider-boot-matrix.js && node test-provider-capabilities.js && node test-provider-language.js && node test-provider-coupling-audit.js && node test-provider-parity-e2e.js && node test-kazee-channel-health.js && node test-kazee-media-caption.js && node test-kazee-typing-join.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-spaces-comment-content.js && node test-boot-health.js && node test-lexical-floor.js && node test-safe-mode.js && node test-logout.js && node test-kazee-call-audio.js && node test-kazee-call-janus.js && node test-kazee-call-webrtc.js && node test-kazee-call-speech.js && node test-kazee-call-manager.js && node test-kazee-call-session.js",
14
- "test": "OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node -e \"require('./vault'); console.log('OK')\" && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-runner-watchdog-static.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-usage-accounting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-engine.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-discoverer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-query.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-walker-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-score-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cokept-warming.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-project-transcripts-smoke.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-abilities.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-extraction.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-couse.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-transfer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-tiers.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-merge-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-learning-e2e.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-nesting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-search.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-extractive-injection.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-dedupe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-signal.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tools.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tooling-mode.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-manifest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-evolution.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-async.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-unified-identity.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-queue-routing.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-packs.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-persona.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-pipeline.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-relationship-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-relationship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-enforcer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-mandate-hygiene.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-external-rule-scopes.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-cards.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-identity-prune.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-message-id.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-people-owner-seed.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-delivery-contract.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-lock.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-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-spaces-autoenable.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-say-loopback.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-union.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-subst-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-section-budgets.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-dedup-adjudicator.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-prospective.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-regressions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cost-report.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-mining.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-certification.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-apprenticeship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-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-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-store-durability.js"
13
+ "pretest": "node test-provider-fixture.js && node test-provider-core-prompt.js && node test-turn-observer.js && node test-provider-prompt-parity.js && node test-provider-env-isolation.js && node test-provider-resume-parity.js && node test-provider-bootstrap.js && node test-provider-registry.js && node test-provider-stream-decoder.js && node test-provider-events.js && node test-provider-claude.js && node test-provider-codex.js && node test-provider-runner.js && node test-provider-refusal-recovery.js && node test-provider-run-lifecycle.js && node test-provider-gateway.js && node test-provider-side-chat.js && node test-single-runtime.js && node test-provider-migration-backup.js && node test-provider-state-migration.js && node test-provider-session-history.js && node test-provider-session-commands.js && node test-provider-compaction.js && node test-provider-scheduler.js && node test-credential-capture.js && node test-cursor-removal.js && node test-utility-provider-policy.js && node test-provider-subagent.js && node test-provider-recall.js && node test-provider-pack-review.js && node test-memory-mutation-queue.js && node test-provider-dream.js && node test-provider-enforcer.js && node test-provider-tool-hooks.js && node test-provider-boot-matrix.js && node test-provider-capabilities.js && node test-provider-language.js && node test-provider-coupling-audit.js && node test-provider-parity-e2e.js && node test-kazee-channel-health.js && node test-kazee-media-caption.js && node test-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-spaces-comment-content.js && node test-boot-health.js && node test-lexical-floor.js && node test-safe-mode.js && node test-logout.js && node test-kazee-call-audio.js && node test-kazee-call-janus.js && node test-kazee-call-webrtc.js && node test-kazee-call-speech.js && node test-kazee-call-manager.js && node test-kazee-call-session.js",
14
+ "test": "OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node -e \"require('./vault'); console.log('OK')\" && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-runner-watchdog-static.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-usage-accounting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-engine.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-discoverer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-query.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-walker-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-score-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cokept-warming.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-project-transcripts-smoke.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-abilities.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-extraction.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-couse.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-transfer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-tiers.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-merge-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-learning-e2e.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-nesting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-search.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-extractive-injection.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-dedupe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-signal.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tools.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tooling-mode.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-manifest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-evolution.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-async.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-unified-identity.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-queue-routing.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-packs.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-persona.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-pipeline.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-relationship-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-relationship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-enforcer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-mandate-hygiene.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-external-rule-scopes.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-cards.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-identity-prune.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-message-id.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-people-owner-seed.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-delivery-contract.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-lock.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-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-spaces-autoenable.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-say-loopback.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-union.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-subst-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-section-budgets.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-dedup-adjudicator.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-prospective.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-regressions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cost-report.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-mining.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-certification.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-apprenticeship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/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-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-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"
15
15
  },
16
16
  "files": [
17
17
  "bot.js",
@@ -46,7 +46,7 @@ assert.strictEqual(provider.label, "OpenAI Codex");
46
46
  assert.strictEqual(provider.isAvailable(), true);
47
47
  assert.strictEqual(provider.executable(), "/fixture/codex");
48
48
  assert.strictEqual(provider.defaultModel(), "gpt-5-codex");
49
- assert.deepStrictEqual(provider.modelChoices(), ["gpt-5.6-sol", "gpt-5.6-sol-pro", "gpt-5.6-terra", "gpt-5.6-luna"]);
49
+ assert.deepStrictEqual(provider.modelChoices(), ["gpt-6-astra", "gpt-5.6-sol", "gpt-5.6-sol-pro", "gpt-5.6-terra", "gpt-5.6-luna"]);
50
50
  assert.ok(!provider.modelChoices().includes("o4-mini"));
51
51
  assert.deepStrictEqual(provider.compatibilityStatus(), {
52
52
  state: "compatible",
@@ -51,6 +51,7 @@ async function routerProbe() {
51
51
 
52
52
  installStub("./core/state", {
53
53
  currentState: () => state,
54
+ resolveProviderSettings: (s, provider) => ({ ...((s || state).providerSettings?.[provider] || {}) }),
54
55
  autoLinkOwnerChannel: () => null,
55
56
  getActiveProvider: (s) => (s || state).settings?.backend || null,
56
57
  getProjectKey: (s) => (s || state).currentSession?.name || "",
@@ -298,6 +299,7 @@ async function schedulerProbe() {
298
299
  getUserState: () => state,
299
300
  getActiveProvider: (s) => (s || state).settings?.backend || null,
300
301
  getProviderSettings: (s, provider) => ({ ...((s || state).providerSettings?.[provider] || {}) }),
302
+ resolveProviderSettings: (s, provider) => ({ ...((s || state).providerSettings?.[provider] || {}) }),
301
303
  getProviderSession: (_state, provider, project) => (
302
304
  state.activeSessions?.[project]?.[provider] || null
303
305
  ),
@@ -410,6 +412,7 @@ function queueProbe() {
410
412
 
411
413
  installStub("./core/state", {
412
414
  currentState: () => state,
415
+ resolveProviderSettings: (s, provider) => ({ ...((s || state).providerSettings?.[provider] || {}) }),
413
416
  saveState: () => {},
414
417
  recordSession: () => {},
415
418
  userOwnsProviderSession: () => false,
@@ -433,7 +433,30 @@ assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
433
433
  // changes now push through state.typingTick on the transition. Reproduced by a
434
434
  // regression test that fails against unmodified code with exactly the reported
435
435
  // symptom (["thinking"] where ["thinking","typing"] was expected).
436
- assert.strictEqual(pkg.version, "3.4.3", "release version must remain unchanged without explicit approval");
436
+ // 3.4.4 approved by Sumeet 2026-09-04 ("yes" to cutting it together with the
437
+ // Kazee dedup fix) after he set this room to fable 5.1 and asked why it was
438
+ // still running opus. Real bug, v3.4.0 regression: /model wrote the choice to
439
+ // the ROOM and /status read it back from the room (both room-aware), but
440
+ // captureRunContext read state.providerSettings[provider] — the speaker's own
441
+ // bucket, correct in July when that was the only scope, never revisited when
442
+ // rooms arrived — so every turn ran the personal default while the setting
443
+ // looked applied everywhere a human could look. Same shape at three more
444
+ // sites (setter, switch payload, usage attribution). Along the way:
445
+ // getProviderSettings rebuilt the bucket on every call (callers holding a
446
+ // reference wrote to a detached copy) and routing a READ through it made
447
+ // capturing a run context mutate savedState — fixed with in-place backfill and
448
+ // a pure resolveProviderSettings. Regression test fails against unmodified
449
+ // code with exactly the reported symptom. Also ships the Kazee inbound
450
+ // dedupe: chat-central fans message:new to both the chat room and each user
451
+ // room, and v3.4.2's room join put the bot in both, so it replied twice.
452
+ // 3.4.5 approved by Sumeet 2026-09-05 ("Can you update open Claudia model
453
+ // picker /model ... /codex probably worth getting rid off. Then I think let's
454
+ // upgrade the CLI to the latest"): gpt-6-astra reached ChatGPT-plan accounts
455
+ // on 2026-09-05 (the 400 was a staged rollout, not client staleness), so it
456
+ // joins CODEX_MODELS first; the /claude and /codex shortcuts go — /backend and
457
+ // the /model picker already switch providers; Codex CLI bumped to 0.153.4 on
458
+ // the operator host and the image (unpinned install) picks it up at build.
459
+ assert.strictEqual(pkg.version, "3.4.5", "release version must remain unchanged without explicit approval");
437
460
  for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
438
461
  assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
439
462
  }
@@ -139,10 +139,12 @@ function restartProbe() {
139
139
 
140
140
  function commandRegistrationProbe() {
141
141
  const names = new Set(commands.list().map((command) => command.name));
142
- for (const name of ["new", "end", "continue", "backend", "claude", "codex"]) assert.ok(names.has(name), `/${name} registered`);
142
+ for (const name of ["new", "end", "continue", "backend"]) assert.ok(names.has(name), `/${name} registered`);
143
+ // v3.4.5: the /claude and /codex aliases were removed — /backend and the /model picker switch providers.
144
+ for (const name of ["claude", "codex"]) assert.ok(!names.has(name), `/${name} must no longer be registered`);
143
145
  const source = fs.readFileSync(path.join(root, "core", "handlers.js"), "utf8");
144
- assert.match(source, /name: "claude"[\s\S]*switchProviderCommand\(env, "claude"/);
145
- assert.match(source, /name: "codex"[\s\S]*switchProviderCommand\(env, "codex"/);
146
+ assert.doesNotMatch(source, /switchProviderCommand/, "the per-provider shortcut helper went with the aliases");
147
+ assert.match(source, /name: "backend"[\s\S]*switchProviderState\(descriptor\.id\)/);
146
148
  assert.match(source, /name: "continue"[\s\S]*activeContinueOptions/);
147
149
  const actions = fs.readFileSync(path.join(root, "core", "actions.js"), "utf8");
148
150
  assert.match(actions, /switchProviderState/);