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>
Customize `brightSelect`'s render function so the focused option's
label paints in brand cyan during selection and the submitted answer
paints in dim cyan after the user moves on. Inactive options keep
their default rendering — only the cursor and submitted state pick
up the color, matching the body-text emphasis added in #2101.
Also migrate the one remaining `p.select` call site (the "What next?"
prompt after the first chat) to `brightSelect` so every menu in the
setup flow goes through the same render path. The shape of the call
matches what `brightSelect` already supports — message + options
with value/label/hint — so no feature is lost in the swap.
Reuses `brandBody` from #2101 for the cyan, so the prompt highlight
and the body prose share one definition of the brand body color.
- Container step: duration hint + 3-line rolling output window with
60s stall detector that offers "keep waiting" vs "ask Claude"
- First chat: reframed as a try-out with sandbox-model explainer
(wakes on message, sleeps when idle, context persists)
- Timezone: auto-detected non-UTC zones now get an explicit
confirm from the user instead of silent persist
- Outro: added always-on warning + prominent "check your DM" banner
when a channel was configured; directive last line
- Discord: always show token-location reminder even when user says
they have one; new "do you have a server?" branch walks through
server creation if not
- All select prompts: custom brightSelect renderer keeps inactive
option labels at full brightness (was dim gray); adds @clack/core
as a direct dep
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>