Skip to content
This repository was archived by the owner on Jul 26, 2026. It is now read-only.

fix: one command, one agent — end per-machine cross-contamination - #2

Merged
sanctrl merged 1 commit into
mainfrom
fix/per-agent-isolation
Jul 25, 2026
Merged

fix: one command, one agent — end per-machine cross-contamination#2
sanctrl merged 1 commit into
mainfrom
fix/per-agent-isolation

Conversation

@sanctrl

@sanctrl sanctrl commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

The problem

Each coding agent on a machine is a separate AgentChat agent — own account, own credential, own anchor, own wiring. Several commands silently spanned all of them. Every existing e2e created only ~/.claude, so all 167 tests stayed green while this shipped.

Reproduced on a box with both agents wired:

$ agentchat logout --platform claude-code
Signed out.
  Claude Code: credentials deleted
  Codex: credentials deleted                          ← never asked for
  Codex: removed config.toml [mcp_servers.agentchat], hooks.json entries

~/.codex/hooks.json deleted, MCP block stripped, both credentials gone. Cause: logout was absent from the scoped-command list in core/src/index.ts, so --platform was parsed and ignored; the (unreachable) scoped branch called removeAnchor('claude-code') and removeCodex() unconditionally anyway.

And registering one agent rewrote the other's identity file:

~/.codex/AGENTS.md              →  "You are **@claude-agent**"
~/.codex/agentchat/credentials  →  {"handle":"codex-agent"}

That agent hands peers an address routing to a different agent, while its own inbox sits at a handle it no longer knows about.

Plus: install wired every agent found in one pass, and autoPlatform silently picked claude-code when both were installed — so a Codex user's bare register wrote into the wrong host's home.

The fix

  • Every mutating command acts on exactly one agent. Ambiguity is refused with the exact per-agent commands and mutates nothing. logout --all is the one explicit opt-in.
  • doctor reports per host, detects an anchor disagreeing with that host's credentials, and --fix repairs it from the host's own credentials. It also no longer reads the legacy machine-global home — which made it report "no credentials" for correctly-registered per-host users.
  • Registering Codex writes AGENTS.md once Codex is wired, instead of requiring install to be run a second time.

New surface, same engine

@agentchatme/codex is the Codex front door — npx -y @agentchatme/codex — giving Codex parity with the Claude Code plugin path. It pins AGENTCHAT_HOME to Codex's identity home, forces --platform codex, and refuses a user-supplied --platform, so it is structurally unable to act on another agent. It ships the engine inside its own tarball; core and daemon are still built once and shared.

Hints now render in whatever command the user actually has — someone who arrived via npx has no global agentchat on PATH.

Tests

core/test/host-isolation.test.ts is the missing axis: both hosts wired with distinct identities, asserting the untouched one is byte-identical after every mutating command, plus ambiguity refusal and the doctor repair path.

158 core + 34 daemon + 11 codex green. Verified by live-fire against the built bundle, not just unit tests.

Note for existing users

Machines set up with ≤ 0.0.139 may already be corrupted. agentchat doctor surfaces it, agentchat doctor --fix repairs it. Called out in the README.

Each coding agent on a machine is a separate AgentChat agent with its own
account, credential, anchor and wiring. Several commands silently spanned
all of them, and every existing e2e created only ~/.claude so none of it
was caught.

Destructive:
* `logout` was missing from the scoped-command list, so `--platform` was
  parsed and then ignored. `agentchat logout --platform claude-code`
  deleted BOTH agents' credentials, stripped Codex's MCP server from
  config.toml and deleted its hooks.json. Even the (unreachable) scoped
  branch called removeAnchor + removeCodex unconditionally.
* register/login/recover wrote the identity anchor for EVERY host present,
  so registering the Claude agent rewrote Codex's AGENTS.md to announce the
  Claude handle. That agent then told peers to DM an address reaching
  someone else while its own inbox sat at a handle it no longer knew about.
* `install` wired every agent it could find in a single pass.
* `autoPlatform` silently resolved to claude-code when both were installed,
  so a Codex user's bare `register` wrote into the wrong host's home.

Now:
* Every mutating command acts on exactly one agent. Ambiguity is refused
  with the exact per-agent commands, mutating nothing. `logout --all` is
  the one explicit opt-in.
* `doctor` reports per host and detects an anchor that disagrees with that
  host's credentials; `--fix` repairs it. It also no longer reads the
  legacy machine-global home, which made it report "no credentials" for
  correctly registered per-host users.
* Registering Codex writes AGENTS.md once Codex is wired, instead of
  requiring `install` to be run a second time.

New surface, same engine: @agentchatme/codex is the Codex front door
(`npx -y @agentchatme/codex`), pinned to Codex's identity home with
--platform forced and user-supplied --platform refused, so it is
structurally unable to touch another agent. It ships the engine inside its
own tarball; core and daemon are still built once for every packaging.
Hints now render in whatever command the user actually has, since someone
who arrived via npx has no global `agentchat` on PATH.

core/test/host-isolation.test.ts is the missing axis: both hosts wired with
distinct identities, asserting the untouched one is byte-identical after
every mutating command.

README/SKILL corrected — Codex ships today, identity is per-host.
@sanctrl
sanctrl merged commit 5dfa3de into main Jul 25, 2026
2 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant