Make the agent provider a first-class, operator-chosen property instead of a
Claude-only assumption. Trunk gains the seams; the actual non-default payloads
(Codex first) install from the `providers` branch.
Setup
- A provider registry feeds a hard-wired setup picker (Claude | Codex). Picking
a non-default provider installs its payload (setup/add-codex.sh, channel-style),
runs a vault-only auth walkthrough (--step provider-auth), and records the pick
on the first agent before its first spawn.
- Picking Claude changes nothing — default installs are byte-for-byte unaffected.
Provider as a DB property
- Provider lives on container_configs.provider (materialized to container.json,
read by resolveProviderName). Creation stays provider-agnostic; the picked
provider is applied via the picked-provider seam. The deprecated
agent_groups.agent_provider path is not used.
Switching + memory
- Switch a live group with `ncl groups config update --provider` + restart.
- Memory never migrates at runtime — each provider keeps its own store. The
/migrate-memory skill carries a group's memory across a switch in either
direction (flat CLAUDE.local.md <-> memory/ scaffold). group-init seeds an
imported-agent-memory note for non-default providers; the runner's memory
definition reads it first turn. See docs/provider-migration.md.
No install-wide default, no runtime provider guard — switching is operator-by-
convention, consistent with the no-install-gating posture.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Scratch agent uses fixed folder `_ping-test` so it can never collide
with a real agent on re-runs
- Added --folder flag to init-cli-agent.ts and cli-agent step wrapper
- Delete always targets `_ping-test` exactly — no re-derivation needed
- Removed normalizeName coupling and FOLDER status field (no longer needed)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- delete-cli-agent.ts discovers tables with agent_group_id dynamically
instead of hardcoding a list
- cli-agent step emits FOLDER in its status block so setup/auto.ts
reads it from the step result instead of re-deriving via normalizeName
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- InboundEvent gains an optional replyTo; router stamps the row's address
fields from it when set, so replies can route to a different channel than
the one the inbound came in on.
- ChannelSetup adds onInboundEvent for admin-transport adapters that build
the full event themselves.
- CLI wire format accepts {text, to, reply_to}. Routed messages go through
onInboundEvent and do not evict an active chat client.
- init-first-agent hands the DM welcome to the running service via
data/cli.sock — synchronous wake, no sweep wait. Fails loudly if the
service is down; no silent fallback.
- Split the CLI scratch-agent bootstrap into scripts/init-cli-agent.ts;
init-first-agent is DM-only.
Agents cannot set replyTo: it lives only on the inbound/router seam and is
consumed once when writing messages_in.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aggregates the loose OneCLI install, secret registration, and first-agent
wiring commands from /setup into three new dispatcher steps. Adds
--cli-only mode to init-first-agent so /new-setup can reach a working
2-way CLI chat with the bare minimum.
- setup/onecli.ts: idempotent install + PATH + api-host + .env, polls /health
- setup/auth.ts: --check verifies secret; --create --value registers it
- setup/cli-agent.ts: wraps init-first-agent --cli-only
- scripts/init-first-agent.ts: --cli-only mode; DM mode unchanged
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>