Pin managed messaging to Opus tier

Pin managed messaging Claude tasks to the Opus tier so phone sessions route through Admin MODEL_OPUS/MODEL instead of inheriting interactive Claude model picker state.
This commit is contained in:
Ali Khokhar 2026-07-07 20:21:03 -07:00 committed by GitHub
parent dac6d4e88d
commit c9adffffbf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 47 additions and 10 deletions

View file

@ -545,11 +545,14 @@ and native Anthropic Messages transports may receive them.
Discord and Telegram messaging. Managed task invocations set
`ANTHROPIC_API_URL`, `ANTHROPIC_BASE_URL`, gateway model discovery,
non-interactive terminal settings, optional `--resume`, optional
`--fork-session`, and `--output-format stream-json`. The managed session parser
extracts persistent Claude session IDs and yields Claude stream-json events to
the messaging event parser. Managed Claude also owns subprocess stderr
diagnostic classification so known benign Claude Code notices do not become
messaging task errors, while unknown stderr remains fatal.
`--fork-session`, `--model opus`, and `--output-format stream-json`. Messaging
pins this Claude tier alias so phone sessions route through `MODEL_OPUS` or the
`MODEL` fallback instead of inheriting a user's interactive `/model` picker
state. The managed session parser extracts persistent Claude session IDs and
yields Claude stream-json events to the messaging event parser. Managed Claude
also owns subprocess stderr diagnostic classification so known benign Claude
Code notices do not become messaging task errors, while unknown stderr remains
fatal.
Codex is supported through `fcc-codex` and Codex extensions. FCC does not keep an
internal managed-Codex session runner because no user-facing messaging setting

View file

@ -13,6 +13,8 @@ from cli.claude_env import (
claude_auth_token,
)
MANAGED_CLAUDE_MODEL_TIER = "opus"
@dataclass(frozen=True, slots=True)
class ManagedClaudeTaskRequest:
@ -89,6 +91,7 @@ def build_managed_claude_invocation(
"prompt": request.prompt,
"cwd": config.workspace_path,
"claude_binary": config.claude_bin,
"managed_model_tier": MANAGED_CLAUDE_MODEL_TIER,
"cli_argv": cmd,
},
)
@ -134,6 +137,8 @@ def build_managed_claude_command(
if fork_session:
cmd.append("--fork-session")
cmd += [
"--model",
MANAGED_CLAUDE_MODEL_TIER,
"-p",
prompt,
"--output-format",
@ -144,6 +149,8 @@ def build_managed_claude_command(
else:
cmd = [
claude_bin,
"--model",
MANAGED_CLAUDE_MODEL_TIER,
"-p",
prompt,
"--output-format",

View file

@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "free-claude-code"
version = "3.4.9"
version = "3.4.10"
description = "Middleware between Claude Code CLI (Anthropic API) and NVIDIA NIM"
readme = "README.md"
requires-python = ">=3.14.0"

View file

@ -1,6 +1,7 @@
import os
from cli.managed.claude import (
MANAGED_CLAUDE_MODEL_TIER,
ManagedClaudeConfig,
ManagedClaudeParseState,
ManagedClaudeTaskRequest,
@ -51,7 +52,12 @@ def test_managed_claude_builds_new_task_command_and_env() -> None:
base_env={"PATH": "keep", "ANTHROPIC_API_KEY": "official"},
)
assert invocation.argv[:2] == ("claude", "-p")
assert invocation.argv[:4] == (
"claude",
"--model",
MANAGED_CLAUDE_MODEL_TIER,
"-p",
)
assert "hello" in invocation.argv
assert "--output-format" in invocation.argv
assert "stream-json" in invocation.argv
@ -65,6 +71,7 @@ def test_managed_claude_builds_new_task_command_and_env() -> None:
assert "ANTHROPIC_API_KEY" not in invocation.env
assert invocation.trace_metadata["client_cli_id"] == "claude"
assert invocation.trace_metadata["claude_binary"] == "claude"
assert invocation.trace_metadata["managed_model_tier"] == MANAGED_CLAUDE_MODEL_TIER
def test_managed_claude_builds_resume_and_fork_commands() -> None:
@ -81,9 +88,24 @@ def test_managed_claude_builds_resume_and_fork_commands() -> None:
base_env={},
)
assert resume.argv[:3] == ("claude", "--resume", "sess_1")
assert resume.argv[:6] == (
"claude",
"--resume",
"sess_1",
"--model",
MANAGED_CLAUDE_MODEL_TIER,
"-p",
)
assert "--fork-session" not in resume.argv
assert fork.argv[:3] == ("claude", "--resume", "sess_1")
assert fork.argv[:7] == (
"claude",
"--resume",
"sess_1",
"--fork-session",
"--model",
MANAGED_CLAUDE_MODEL_TIER,
"-p",
)
assert "--fork-session" in fork.argv

View file

@ -608,6 +608,11 @@ def test_cli_surfaces_are_explicit_launchers_and_managed_claude() -> None:
claude_env_text = (cli_root / "claude_env.py").read_text(encoding="utf-8")
assert 'CLAUDE_CODE_AUTO_COMPACT_WINDOW = "190000"' in claude_env_text
assert 'CLAUDE_NO_AUTH_SENTINEL = "fcc-no-auth"' in claude_env_text
managed_claude_text = (cli_root / "managed" / "claude.py").read_text(
encoding="utf-8"
)
assert 'MANAGED_CLAUDE_MODEL_TIER = "opus"' in managed_claude_text
assert '"managed_model_tier": MANAGED_CLAUDE_MODEL_TIER' in managed_claude_text
for path in {
cli_root / "launchers" / "claude.py",
cli_root / "managed" / "claude.py",

2
uv.lock generated
View file

@ -561,7 +561,7 @@ wheels = [
[[package]]
name = "free-claude-code"
version = "3.4.9"
version = "3.4.10"
source = { editable = "." }
dependencies = [
{ name = "aiohttp" },