Skip to content

1.11.0 — Claude Code's agent view on every provider, and Codex everywhere under alc --codex claude - #35

Merged
treeleaves30760 merged 32 commits into
mainfrom
feat/agent-view
Sep 22, 2026
Merged

treeleaves30760 merged 32 commits into
mainfrom
feat/agent-view

Conversation

@treeleaves30760

@treeleaves30760 treeleaves30760 commented Sep 22, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Every Claude Code session alc starts now survives agent view: alc claude agents, alc claude --bg, ← and /bg keep the provider alc gave the session. alc hands Claude Code a --settings file instead of environment variables (Claude Code's supervisor drops those), with the credential behind apiKeyHelper = alc claude-credential. The default alc claude on Claude Code's own login keeps its model the same way.
  • The Codex adapter for Claude Code runs as a background process of its own (alc bridge): loopback, token, remembered port, started on demand, gone after an hour idle. One starter at a time, even when Claude Code's supervisor revives several sessions at once.
  • Under alc --codex claude no request reaches a Claude model: fable/best/opusplan are mapped, [1m] variants (including opus[1m]) are sized at Codex's window, full Claude ids are answered by the Codex model of the same tier, and fast mode and the advisor are off.
  • -- now hands the agent its own flags, so Claude Code's --name and -p are reachable, and the errors say how. claude mcp, doctor, update and the other commands that never reach a model go straight to Claude Code and start nothing.
  • Docs: a new Background sessions page, README, troubleshooting and configuration updates, in English and Traditional Chinese.
  • Includes 1f6c75b (installers set up tmux's optional dependencies), and merges main for fix: send a key saved for a vLLM, Ollama or custom profile #34 (a key saved for a vLLM, Ollama or custom profile is sent); a new test pins that a keyed vLLM profile reaches Claude Code through the credential helper rather than the placeholder token.

Test plan

  • cargo fmt -- --check, cargo clippy --all-targets --all-features -- -D warnings, cargo test --all-targets --all-features (Windows, 711 tests after merging main), Linux clippy from Windows
  • Docusaurus build for both locales with broken links and anchors set to fail
  • Both Windows installer tests under Windows PowerShell 5.1. The tmux one failed on the runner and locally: Windows PowerShell writes a UTF-8 console's byte-order mark into a redirected stdin, so the installer's tmux -V probe was not getting the empty stdin alc's runtime gives it. Fixed in 451f736.
  • End to end on Windows with Claude Code 2.1.278 and a real Codex login, ten of ten:
Step Result What it proves
1 Seed PASS isolated CLAUDE_CONFIG_DIR, no Claude login anywhere
2 Foreground print PASS pong; turn row on gpt-5.6-sol; bridge self-started
3 Background outlives alc PASS alc gone in 1 s, row done in 11 s, pong in claude logs
4 Helper revives the bridge PASS a bare claude --settings … restarts it; the new pid survives the session that started it
5 Aliases and full Claude ids PASS fable and claude-opus-5 both answered by gpt-6-astra; no Claude id in the ledger
6 Fast mode and the advisor PASS fastMode / advisorModel in user settings ignored: no prompt, no Opus
7 Agent view and ← PASS dispatch and background-from-← both answer on Codex; the effort indicator survives /model opus
8 Key profile headers PASS x-api-key and Authorization: Bearer, foreground and from the background supervisor
9 Config path with a space PASS the cmd /c quoting of apiKeyHelper holds
10 Clean up PASS nothing left running

Thirteen real turns, 131,865 tokens, no Claude model id anywhere in the ledger.

  • Two final whole-branch reviews (correctness/security, design/integration): no critical findings; the important ones are fixed in this branch (bracketed aliases, the helper's route on cmd, the bridge start race, /healthz keeping the bridge alive, commands that reach no model, the default login's model).

Compatibility

Key-based providers now receive their key in both X-Api-Key and Authorization: Bearer - how Claude Code sends a helper's credential. alc will not hand a shared Claude Code session to a hub still running 1.10; alc hub stop and retry.

Known flake, not from this branch

tests/cli.rs::a_hub_starts_answers_and_stops failed once across this branch's full-suite runs on Windows: hub status read run/hub.json while the stopping hub was deleting it and got "access denied". The hub code is untouched here, and 15 of 15 isolated reruns passed.

🤖 Generated with Claude Code

treeleaves30760 and others added 30 commits September 19, 2026 21:42
Set up compatible tmux through supported system package managers with an opt-out and non-fatal fallback guidance.

Add offline Unix and Windows installer coverage and CI jobs, and document dependencies and install options in English and Traditional Chinese.

Co-Authored-By: Claude Code <[email protected]>
Under alc --codex claude a full Claude id could still reach chatgpt.com -
/model claude-opus-5, a subagent's frontmatter, a fallback chain - and be
refused there. The bridge now maps any claude-* id (and the bare aliases) to
the most capable, the starting or the cheapest Codex model, and leaves every
other id alone so a new Codex model still works on the day it ships.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
One document per provider class - Codex, key-based, keyless local - holding
what alc used to export, plus the fable alias and the Claude-only features
turned off where the endpoint cannot serve a Claude model. The credential is
never in it: apiKeyHelper runs alc claude-credential, quoted for cmd or sh.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
alc reads its arguments by the C runtime's rules, where a backslash just
before a quote escapes it. A configuration directory given with a trailing
backslash (C:\alc\, or the drive root C:\) therefore swallowed the rest of
the apiKeyHelper line, and the helper failed in every session. The trailing
run is now doubled inside the quotes, and kept rather than trimmed so C:\
still names the drive root. Both quoted values - alc's path and the
configuration directory - go through it.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
…ce, and only as alc named them

write_route now refuses a record whose id is not a route name alc makes,
or is not the name of the profile and login the record holds, before any
path is built from it. The hub writes records that arrive over its control
socket, where a name like ../../x would have put a file outside routes/
and a borrowed name would have moved a route's sessions to another login.

The first token is created with create_new, so two starters on a fresh
configuration can no longer each mint one and leave a bridge holding the
token that lost. The loser waits up to two seconds for the winner to write
it and adopts it; a file that stays empty is reported, never replaced.
rotate_token still replaces, and only a running bridge calls it.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
…e each route writer its own temp

The bridge's token is now minted by remote::settings::create_token, the
way ctl.token is: written in full under a name of its own and linked into
place, so bridge.token is never seen empty while it is being written. An
empty one is therefore stale and is replaced, where the create_new version
left it for every launch and session helper to wait on and then fail.

Route files are written with atomic_write's secret temp, unguessable and
created exclusively, so two writers of one route no longer truncate each
other's temp or find it already renamed away.

atomic_write no longer removes the destination before renaming on
Windows: rename already replaces it there, and the remove only opened a
moment with no file at the path and failed concurrent writers with
access denied.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Windows PowerShell 5.1 starts every file it saves as UTF-8 with a
byte-order mark, and serde_json refuses one, so a user's own `--settings`
file saved that way was refused. One leading U+FEFF now comes off the
file's contents, and off JSON passed inline, before alc looks for the
opening brace and parses. A path is still opened by its exact name.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
A user's `--settings` whose `env` was null, an array or a string won the
merge over alc's whole `env`. The endpoint, the blanked credentials and
the model pins were gone, `finish` found no base URL to fill and said
nothing, and Claude Code would have sent what alc's apiKeyHelper prints
to its own default endpoint. Such an `env` is now refused with the
reason, inline or from a file. The values inside an object are left for
Claude Code to check.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
One detached process per config dir, loopback only, token on every model
request, one route per Codex login, a remembered port it moves off only when
another program holds it, and an hour's idleness before it exits - counted
until a streaming turn's last byte, not its first.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
A listening socket answers TCP before its server answers HTTP, so from the
moment one start binds the remembered port, a second start's hello connects
into the backlog and times out while the winner is still building its runtime.
Believing that one timeout, the loser took the move path and rotated the token
out from under the bridge about to serve - leaving a port held by a bridge
every request 401s against for the whole idle hour. A failed bind now asks up
to five times, a pause apart, and only AddrInUse is worth asking about at all:
PermissionDenied is a range Windows reserves, where nothing is listening.

The in-flight guard's whole mechanism was one capture-only statement that no
test exercised. Two tests now drive a real two-chunk streaming response through
the middleware: in flight after the headers, still in flight before the body is
read, released on the last byte, and released too when a client walks away
without reading it.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
alc claude-credential prints the bridge's token for a Codex route - starting
a bridge first when none is up - or a profile's saved key, and nothing else;
alc bridge reports and stops the process.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Every Claude launch that leaves Claude Code's own login now carries its
provider in a settings document - the fable alias and the Claude-only
features included - with the credential behind apiKeyHelper. A Codex launch
names a route on the background bridge instead of an in-process adapter, and
claude agents takes alc's model and effort after the subcommand.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
A local server ignores the Authorization header and serves only what it has
pulled, so Claude Code's own login has nothing to mean there. An Ollama
profile set to native auth took the Claude-login path all the same and came
away with no settings document - and so with no alias pins, which left
haiku, sonnet and opus resolving to Claude model ids that localhost:11434
answers with a 404. It now gets the local document whatever its auth style
says, as the pins have always followed the kind.

Also puts back the two picker-row assertions that the launch test carried
before this feature moved the picker into the settings document: the most
capable model is listed first, and no row carries a key the setting's schema
does not take.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
prepare finishes the settings document once the bridge is up, writes it by
content and puts --settings after the subcommand, or first. Session commands
(attach, logs, stop, respawn, rm) go straight to Claude Code. A dry run shows
the document and the bridge without writing or starting anything.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
The end-to-end run found Claude Code's --name unreachable: alc's guard for
its own flags fired after `--` too, and the escape the error suggested hit
the same guard. `--` now turns the guard off, which is what a user who
typed it meant. The provider-shortcut error says how to hand -p to the
agent, and `alc bridge` no longer reports routes as served while it is
stopped.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
The paragraph named the settings file between two dashes, and the line
break happened to fall so that the second dash opened a line. CommonMark
lets a list interrupt a paragraph, so the published page cut the sentence
short and rendered its second half as a bullet. Commas do the same job
without the trap; the zh-TW page already read correctly.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
`serve_as` matched the bare aliases exactly and otherwise wanted a
`claude-` prefix, so `opus[1m]`, `sonnet[1m]` and `haiku[1m]` matched
neither: they were relayed to chatgpt.com verbatim, which serves none of
Claude's and answers with an error. Claude Code takes those spellings
from `/model`, from a subagent's frontmatter and from a `fallbackModel`
chain, alc itself uses `opus[1m]` as its fixture for a Claude model the
user actually had, and the background-sessions page promises the `[1m]`
variants are covered.

The suffix asks for a context window on the model named in front of it,
so the id is cut at the first `[` before either arm looks at it.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
The helper Claude Code runs through cmd carried its route unquoted, and the
%/" refusal beside it did not cover it. A profile name only passes
validate_profile_name on a save, in doctor and in the TUI - never on
Store::load - so a hand-edited config.toml reached cmd as written: `my
profile` split into two arguments and failed every refresh, `prod%USERNAME%`
expanded, and `or&calc` ran calc each time Claude Code asked for the key.

A route is `codex-` and twelve hex digits or `profile:` and a valid profile
name, and anything else is refused for both shells alike. What survives the
check needs no quoting in either.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Two alcs asking for a bridge at once could both start one. Freeing a stale
lock and creating it again was three steps, so two starters that judged the
same lock dead each removed the other's fresh lock and both came away
holding it; and the lock was freed at the ten-second deadline even when the
bridge had not answered yet, leaving a moment with neither. The loser then
bound nothing, rotated the token and rewrote the settings out from under the
bridge that did start, which was left serving a port nothing pointed at.
Claude Code's supervisor reviving several background sessions together is
enough to cause it. The starter now holds the lock until the bridge answers,
and a stale lock is taken over by writing an owner mark over it and reading
it back, so only one taker goes on. Only AlreadyExists counts as another alc
holding the lock; a read-only run directory says so instead of waiting ten
seconds for somebody who is not there.

/healthz counted as activity, so anything polling it kept an idle bridge up
for ever; only a request that got past the token does now. An empty
x-api-key hid a correct bearer token beside it; every credential a request
carries is considered. A key with a line break inside it printed two lines
from the credential helper however hard its ends were trimmed; it is refused,
naming where it was read and never printing it. A settings move that failed
on one file abandoned the rest for good; it now rewrites every file it can
and reports the ones it could not.

The doctor test for the bridge row matched "not running" in the hub's row
too, and never asserted the "without calling it a problem" in its name. The
one test that talks to a real bridge had no read deadline.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…aches no model starts nothing

The default `alc claude` - Claude Code on its own login - was the one launch
still carried in the environment, so a session sent to the background came
back on Claude Code's default model instead of the one it was launched with,
and lost its small model and context window with it. It gets a settings
document now like every other launch: the endpoint, the model, the small
model and the window, with the cloud-provider switches blanked. The key
variables are left out rather than blanked, because no helper answers in
their place on this path, and alc still removes a stray one from the process
it starts. A user's own --settings is merged into it, as it is everywhere
else.

`claude mcp list`, `claude doctor`, `claude --version` and the other commands
that never open a model connection took the full launch path: a route file,
the hour-lived detached bridge, a settings document and a session in the
usage ledger. They go straight to Claude Code now, as the session commands
do. `ultrareview` too - it runs on Anthropic's servers under Claude Code's own
login. `agents` does not: agent view dispatches real work.

A dry run printed the bridge's remembered port as though something were
listening on it; it says it is the port the bridge keeps. The Codex launch
test now reads back the route file the settings document names, which is
only read when a request arrives and so could go missing without any other
test noticing. And the comments that still described Claude Code on the
in-process bridge say where it runs now.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Two sentences the last fix made true. On Claude Code's own login the
settings file carries the endpoint and the model and the login answers, so
the pages no longer say every session fetches a key through apiKeyHelper.
And mcp, doctor, plugin, update and the other commands that never reach a
model go straight to Claude Code, starting no bridge and counting no
session. English and Traditional Chinese together.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Brings in #34, which treats a keyless-kind profile that has a key as bearer
auth, once, in launch::build. It merged without conflict. On this branch the
same upgrade also decides Claude Code's settings document, so a keyed vLLM
profile gets the credential helper rather than the placeholder token a
keyless endpoint is given; the next commit pins that.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…helper

#34 tested the upgrade to bearer auth on OpenCode and Codex. Claude Code is
where it meets this branch: a keyed profile has to get the keyed settings
document, with the key fetched through apiKeyHelper, and never the `alc`
placeholder token a keyless endpoint is given - a server started with a key
refuses the placeholder, and a background session has nothing but the
document to go on. A profile without a key still gets the placeholder.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
treeleaves30760 and others added 2 commits September 23, 2026 01:13
Claude Code's agent view on every provider, and every Claude model a Codex
model under alc --codex claude. A feature, so a minor release.

alc used to hand Claude Code its provider as environment variables pointing
at an adapter inside the alc process. Claude Code's supervisor drops those
for a background session, so claude agents, claude --bg and ← on an empty
prompt all produced sessions that came up on Anthropic's API or not logged
in. alc now passes a settings file instead - the one channel Claude Code
keeps for a background session - with the credential behind apiKeyHelper,
and the Codex adapter for Claude Code runs as a background process of its
own (alc bridge): loopback only, behind a token, on a port it keeps,
started by the first session that needs it and gone after an hour idle.
The default alc claude on Claude Code's own login keeps its model through
the background the same way.

Under alc --codex claude no request reaches a Claude model: the fable, best
and opusplan aliases, [1m] variants, full Claude ids, a subagent's model:
and fallback chains are all answered by the Codex model of the same tier,
and fast mode and the advisor are off. Verified end to end on Windows
against Claude Code 2.1.278 and a real Codex login.

`--` now hands Claude Code its own flags, so --name and -p are reachable
(alc --codex claude -- --bg --name nightly "..."), and the errors say how.
claude mcp, doctor, update and the other commands that never reach a model
go straight to Claude Code, starting no bridge and counting no session.

Key-based providers now receive their key in both X-Api-Key and
Authorization: Bearer, which is how Claude Code sends a helper's
credential. alc will not hand a shared Claude Code session to a hub still
running 1.10, which would drop the settings and launch it on the wrong
provider; stop that hub with `alc hub stop` when its sessions are done.

Also in this release: a key saved for a vLLM, Ollama or custom profile is
sent at last, so a server started with --api-key stops answering 401 (#34);
and the installers set up tmux's optional dependencies.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The Windows installer probes each tmux on PATH the way alc does at run time,
with stdin at end of file. Windows PowerShell's Process writes the console
input encoding's byte-order mark into a redirected stdin as soon as the child
starts, so on a UTF-8 console (code page 65001) the probe handed tmux three
bytes, EF BB BF, instead. Real tmux ignores them, but the offline test's
fixture holds the probe to its contract, and failed on the first case both
on the Windows runner and locally. The process now starts under an input
encoding with no preamble, and the console's own is put back after.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@treeleaves30760
treeleaves30760 merged commit 21021fa into main Sep 22, 2026
9 checks passed
@treeleaves30760
treeleaves30760 deleted the feat/agent-view branch September 23, 2026 00:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant