Let the connector do the work itself, on the coding agent you choose
What this does
basecamp connect -P <profile> --orchestrator <agent> makes the connector do the work itself, with no session reading its output. Each trusted request goes to a headless session of the coding agent named. That session follows the basecamp-connect skill as it ships and hands the work to a worker, a session of its own, which replies in Basecamp as the agent.
Without the new flags the connector behaves as it does today: it prints request lines for the session that started it. connect stays hidden.
Why
The skill needs a Claude Code session left open, because it relies on two things that harness has: a monitor that wakes the session for each request line, and background subagents. The connector now supplies both from the CLI, so the same Agent Skill runs on other coding agents and keeps running with no session open.
How it is put together
internal/connector/runner(new). An adapter per coding agent CLI (the arguments of a headless turn and how its event stream reads), the orchestrator loop, the workers, the state file and a unix socket. There are three built-in adapters; a new one is a new adapter plus a stream fixture.HandoffOptions.Deliver. The handoff gives the runner each request after writing its line and before closing its record, so a crash repeats a request rather than losing it, provided the connector starts again within the handoff's grace of a minute. Nil for every run without the new flags.- Commands the orchestrator runs, over the socket:
connect dispatch,workers list|send|stop,say,stop,restart. runner.json, besideconnect.json: rules that choose a worker's coding agent, model and effort by repo, project, card table, to-do list or Ping, and what the connector was last started with (--runreuses it).- The skill.
SKILL.mdgains one section at the end for this path, and its opening no longer says the connector requires Claude Code.subagent.mdis unchanged. The runner gives both files to its agents verbatim and adds only a table mapping the mechanisms they name onto its commands.
Worth a reviewer's attention
- Permissions.
--permissions fullis the default: each CLI is started with its permission prompts off, because a headless turn has nobody to answer one.guardedstarts each under its own unattended guardrails (Claude Code's auto mode; Codex's sandbox with automatic review) and refuses a CLI that has none. - The runner's commands inside a sandbox. An agent the connector starts is told which runner started it through two environment variables, the socket's path and the profile, so
dispatchand the rest reach the socket without readingconnect.json, whose ownership cannot be checked from inside a coding agent's sandbox. The check itself is unchanged. The socket is only used in a directory that is the person's own and closed to everyone else, which is what decides who may use it. - Follow-ups. A later request on the same card, to-do, message or chat goes to the worker that handled the first, in the same session. A new worker starts only when that session cannot take it.
- Stops and crashes. A worker a stop interrupted is continued at the next start. A process a crash left running is ended by its recorded process group and confirmed gone before its session is continued, and a start that cannot confirm it does not start. While the connector's hold stands, no agent is started.
Verified
bin/cion a clean checkout of this branch.- An opt-in test against the installed CLIs (
go test -tags runner_live ./internal/connector/runner -run TestInstalled): a turn answers, a second turn continues the session, a session that no longer exists fails cleanly, and underguardedboth Claude Code and Codex commit in a repo on a new and a resumed session. - Live, against a real Basecamp project on Linux, with Claude Code and Codex each as orchestrator and as worker, under
fullandguarded: a question answered in place, repo work with a worktree, commit and pushed branch, a follow-up continuing the same worker, a restart in the middle of a task, a rule choosing the worker by card table and by repo, and--runfrom kept settings. - The edited skill in a fresh Claude Code session still takes the original path: it started the plain connector, armed its monitor, and a background subagent replied.
Not verified
- macOS.
- Pings: every live run was narrowed to one project.
- A pull request on a hosted remote: pushes went to a local one.
- A run longer than a few minutes, and a hard kill of the connector outside the unit test.
- The skill's model evals, which need an API key this was not run with.
- The new skill section followed by a session that is not Claude Code.