@inetafrica/open-claudia 3.4.4 → 3.4.6

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,17 @@
1
1
  # Changelog
2
2
 
3
+ ## v3.4.6 — Codex runs again: the gate stopped asking for the impossible
4
+
5
+ - **Every Codex turn since Codex CLI 0.153.x failed with `OpenAI Codex is incompatible: Codex rejected the isolated PreToolUse configuration or could not disable uncovered tool transports`.** The mandatory tool-hook gate ran `codex features list` with our overrides and required `unified_exec` to read `false`. Codex 0.153.2, 0.153.3 and 0.153.4 all ignore `-c features.unified_exec=false` (the feature is locked on and `codex features list` reports `unified_exec stable true` regardless), so the gate refused the provider on every turn — the failure Sumeet hit right after switching to gpt-6-astra on 3.4.5.
6
+ - **Why it is safe to stop requiring it off.** Probed live on 0.153.4 (2026-09-05): a shell command run through unified exec still arrives at the PreToolUse hook as tool_name `Bash` with `tool_input.command`, and the real deny gate caught the probe command (deny audited, marker file never written). Unified exec is a covered transport, not an uncovered one. Plugins stay forced off and hooks stay forced on; project trust stays untrusted.
7
+ - **What changed.** `core/providers/codex-hook.js` no longer emits the `features.unified_exec=false` override nor checks its state. Fixtures and the tool-hook tests now model Codex as it is (`unified_exec stable true`) and assert no unified_exec override is generated. Fleet pods are affected identically (the 3.4.4 and 3.4.5 images carry Codex 0.153.x), so Codex on pods works only from this release onward.
8
+
9
+ ## v3.4.5 — Astra on the picker, two shortcuts fewer
10
+
11
+ - **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).
12
+ - **`/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.
13
+ - **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.
14
+
3
15
  ## v3.4.4 — The room's model was saved, shown, and never once used
4
16
 
5
17
  - **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.
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.
package/core/handlers.js CHANGED
@@ -1537,30 +1537,6 @@ register({
1537
1537
  },
1538
1538
  });
1539
1539
 
1540
- async function switchProviderCommand(env, providerId) {
1541
- if (!authorized(env)) return;
1542
- const provider = providerFromRegistry(providerId);
1543
- if (!provider) return send(`Unknown provider: ${providerId}`);
1544
- if (!providerAvailability(provider)) {
1545
- return send(`${provider.label} is unavailable. Install its CLI or configure its path, then run /doctor.`);
1546
- }
1547
- const state = currentState();
1548
- const switched = switchProviderState(provider.id, { state });
1549
- if (!switched.ok) return send(`Could not switch provider: ${switched.error.message}`);
1550
- const session = switched.sessionId ? `\nSession: ${switched.sessionId.slice(0, 8)}...` : "\nNew session.";
1551
- return send(`Switched to ${provider.label}.${session}`);
1552
- }
1553
-
1554
- register({
1555
- name: "claude", description: "Switch to Claude Code provider",
1556
- handler: async (env) => switchProviderCommand(env, "claude"),
1557
- });
1558
-
1559
- register({
1560
- name: "codex", description: "Switch to OpenAI Codex provider",
1561
- handler: async (env) => switchProviderCommand(env, "codex"),
1562
- });
1563
-
1564
1540
  register({
1565
1541
  name: "backend", description: "Show/switch active provider", args: "[claude|codex]",
1566
1542
  handler: async (env, { tail }) => {
@@ -38,7 +38,12 @@ function buildCodexHookOverrides({ cwd, nodePath, cliPath } = {}) {
38
38
  return Object.freeze({
39
39
  featuresHooks: "features.hooks=true",
40
40
  featuresPlugins: "features.plugins=false",
41
- featuresUnifiedExec: "features.unified_exec=false",
41
+ // unified_exec is deliberately NOT forced off. Probed live 2026-09-05 on
42
+ // Codex 0.153.4 (0.153.2 and 0.153.3 behave the same): `-c
43
+ // features.unified_exec=false` is silently ignored — the feature is locked
44
+ // on — while unified-exec shell calls still reach PreToolUse as tool_name
45
+ // "Bash" (the deny gate observed the probe command, marker not executed).
46
+ // Requiring it off made every Codex turn fail MANDATORY_TOOL_HOOK_UNAVAILABLE.
42
47
  // A path is a dynamic TOML map key. Current Codex `-c` parsing rejects a
43
48
  // quoted path inside dotted-key notation, so replace the isolated projects
44
49
  // table with one explicit inline table (user config is ignored below).
@@ -58,7 +63,6 @@ function hookArgs(overrides) {
58
63
  "--strict-config",
59
64
  "-c", overrides.featuresHooks,
60
65
  "-c", overrides.featuresPlugins,
61
- "-c", overrides.featuresUnifiedExec,
62
66
  "-c", overrides.projectTrust,
63
67
  "-c", overrides.preToolUse,
64
68
  "--dangerously-bypass-hook-trust",
@@ -150,15 +154,13 @@ function checkCodexHookCompatibility({ binary, runCommand, env, cwd, prepared =
150
154
  "features",
151
155
  "-c", o.featuresHooks,
152
156
  "-c", o.featuresPlugins,
153
- "-c", o.featuresUnifiedExec,
154
157
  "-c", o.projectTrust,
155
158
  "-c", o.preToolUse,
156
159
  "list",
157
160
  ], opts);
158
161
  const configured = features.ok
159
162
  && featureState(features.output, "hooks", true)
160
- && featureState(features.output, "plugins", false)
161
- && featureState(features.output, "unified_exec", false);
163
+ && featureState(features.output, "plugins", false);
162
164
  result = configured
163
165
  ? { ok: true, code: null, reason: null }
164
166
  : {
@@ -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"]);
@@ -246,7 +246,9 @@ Verified-OK pathways (no change needed): scheduler re-runs stamp
246
246
  `OC_CHANNEL_USER_ID` (file gate stays live on approved external re-runs);
247
247
  Spaces turns flow through the common router (triage covered); sub-agents
248
248
  spawn read-only and inherit channel env; codex transport confinement
249
- (hooks on, plugins/unified_exec off, untrusted trust) is mandatory-or-refuse.
249
+ (hooks on, plugins off, untrusted trust) is mandatory-or-refuse. unified_exec is
250
+ no longer forced off (2026-09-05): Codex 0.153.x locks it on and its shell calls
251
+ still reach the PreToolUse hook as "Bash".
250
252
 
251
253
  G3 closed with P3 (test-approval-cards.js pins the `apr:` handler). `tool run`
252
254
  remains the one authorised channel reachable on external turns — by design:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "3.4.4",
3
+ "version": "3.4.6",
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": {
@@ -143,7 +143,7 @@ function handleCompatibilityCommand() {
143
143
  process.stdout.write([
144
144
  `hooks stable ${overrides.includes("features.hooks=true")}`,
145
145
  `plugins stable ${!overrides.includes("features.plugins=false")}`,
146
- `unified_exec stable ${!overrides.includes("features.unified_exec=false")}`,
146
+ "unified_exec stable true",
147
147
  ].join("\n") + "\n");
148
148
  return true;
149
149
  }
@@ -160,7 +160,6 @@ function runHookProbe() {
160
160
  && !argv.includes("--profile")
161
161
  && overrides.includes("features.hooks=true")
162
162
  && overrides.includes("features.plugins=false")
163
- && overrides.includes("features.unified_exec=false")
164
163
  && overrides.some((value) => /^projects=\{.+trust_level="untrusted".+\}$/.test(value))
165
164
  && hookOverrides.length === 1
166
165
  && /matcher\s*=\s*"\^\(Bash\|apply_patch\)\$"/.test(hookOverrides[0]);
@@ -34,7 +34,7 @@ if (same("--version")) {
34
34
  ? "--config --json --sandbox --model --skip-git-repo-check --strict-config --ignore-user-config --dangerously-bypass-hook-trust\n"
35
35
  : "--config --json --sandbox --image --model --skip-git-repo-check --strict-config --ignore-user-config --dangerously-bypass-hook-trust\n")});
36
36
  } else if (${JSON.stringify(kind)} === "codex" && args[0] === "features" && args.at(-1) === "list") {
37
- process.stdout.write("hooks stable true\\nplugins stable false\\nunified_exec stable false\\n");
37
+ process.stdout.write("hooks stable true\\nplugins stable false\\nunified_exec stable true\\n");
38
38
  } else if (${JSON.stringify(kind)} === "codex" && same("login", "status")) {
39
39
  if (${JSON.stringify(mode)} === "unauthenticated") {
40
40
  process.stderr.write("not logged in\\n");
@@ -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",
@@ -449,7 +449,21 @@ assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
449
449
  // code with exactly the reported symptom. Also ships the Kazee inbound
450
450
  // dedupe: chat-central fans message:new to both the chat room and each user
451
451
  // room, and v3.4.2's room join put the bot in both, so it replied twice.
452
- assert.strictEqual(pkg.version, "3.4.4", "release version must remain unchanged without explicit approval");
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
+ // 3.4.6 prepared 2026-09-05 after Sumeet sent a screenshot of the first Codex
460
+ // turn on 3.4.5 failing ("Codex rejected the isolated PreToolUse configuration
461
+ // or could not disable uncovered tool transports"): Codex 0.153.x ignores
462
+ // `-c features.unified_exec=false` (locked on), so the hook gate refused every
463
+ // Codex turn. Live probe: unified-exec shell calls still reach PreToolUse as
464
+ // tool_name "Bash" and the deny gate caught the probe, so the gate no longer
465
+ // requires unified_exec off. Push approved by Sumeet before release.
466
+ assert.strictEqual(pkg.version, "3.4.6", "release version must remain unchanged without explicit approval");
453
467
  for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
454
468
  assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
455
469
  }
@@ -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/);
@@ -59,7 +59,7 @@ function compatibleCodexCommand(_binary, args) {
59
59
  output: [
60
60
  "hooks stable true",
61
61
  "plugins stable false",
62
- "unified_exec stable false",
62
+ "unified_exec stable true",
63
63
  ].join("\n"),
64
64
  code: 0,
65
65
  };
@@ -137,12 +137,15 @@ try {
137
137
  assert.ok(defaultClaudeInvocation.args.includes("--settings"), "Claude factories enforce the generated hook by default");
138
138
 
139
139
  // Codex uses only transient, generated CLI overrides. User config is ignored,
140
- // the current project is explicitly untrusted, plugin hooks and unified_exec
141
- // are disabled, and hook trust is bypassed only after exact verification.
140
+ // the current project is explicitly untrusted, plugin hooks are disabled, and
141
+ // hook trust is bypassed only after exact verification. unified_exec is NOT
142
+ // forced off: Codex 0.153.x ignores the override (locked on) and its shell
143
+ // calls still arrive as tool_name "Bash" (probed live 2026-09-05).
142
144
  const overrides = buildCodexHookOverrides({ cwd: projectDir });
143
145
  assert.strictEqual(overrides.featuresHooks, "features.hooks=true");
144
146
  assert.strictEqual(overrides.featuresPlugins, "features.plugins=false");
145
- assert.strictEqual(overrides.featuresUnifiedExec, "features.unified_exec=false");
147
+ assert.strictEqual(overrides.featuresUnifiedExec, undefined, "unified_exec is no longer forced off (Codex ignores it)");
148
+ assert.ok(!Object.values(overrides).some((value) => String(value).includes("unified_exec")), "no unified_exec override is generated");
146
149
  assert.match(overrides.projectTrust, /^projects=\{.+trust_level="untrusted".+\}$/);
147
150
  assert.match(overrides.preToolUse, /^hooks\.PreToolUse=/);
148
151
  assert.match(overrides.preToolUse, /deny-gate-hook/);
@@ -158,6 +161,7 @@ try {
158
161
  assert.ok(prepared.args.includes("--ignore-user-config"));
159
162
  assert.ok(prepared.args.includes("--strict-config"));
160
163
  assert.ok(prepared.args.includes("--dangerously-bypass-hook-trust"));
164
+ assert.ok(!prepared.args.some((value) => String(value).includes("features.unified_exec")), "argv carries no unified_exec override");
161
165
  assert.ok(!prepared.args.includes("--profile"), "a merging Codex profile is never trusted");
162
166
  const tampered = Object.freeze({ ...prepared, args: Object.freeze([...prepared.args, "--profile", "project-hooks"]) });
163
167
  assert.strictEqual(verifyPreparedCodexHook(tampered), false, "trust bypass rejects non-generated argv");