@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 +12 -0
- package/Dockerfile +1 -1
- package/README.md +1 -3
- package/core/handlers.js +0 -24
- package/core/providers/codex-hook.js +7 -5
- package/core/providers/codex.js +2 -1
- package/docs/permissions-audit-2026-07.md +3 -1
- package/package.json +1 -1
- package/test-fixtures/fake-agent-cli.js +1 -2
- package/test-provider-boot-matrix.js +1 -1
- package/test-provider-codex.js +1 -1
- package/test-provider-language.js +15 -1
- package/test-provider-session-commands.js +5 -3
- package/test-provider-tool-hooks.js +8 -4
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
|
|
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`, `/
|
|
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
|
-
|
|
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
|
: {
|
package/core/providers/codex.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
@@ -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
|
-
|
|
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
|
|
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");
|
package/test-provider-codex.js
CHANGED
|
@@ -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
|
-
|
|
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"
|
|
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.
|
|
145
|
-
assert.match(source, /name: "
|
|
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
|
|
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
|
|
141
|
-
//
|
|
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, "
|
|
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");
|