feat(types): pluggable agent-type registry#154
Conversation
Discover agent types from types/<name>/type.conf manifests instead of hardcoded case arms across whoami/join/spawn/delivery, so adding a type is a manifest + template (cf. the 8-file OpenCode addition fujibee#136). Detection (detect=/detect_proc=), the join whitelist, spawn (direct-CLI cli= and Node-launcher spawn=), and delivery hooks routing (hooks_file=) all read the registry. Manifests are read-only key=value DATA, never sourced. External add-on types plug in via a spawn= Node launcher (universal --name/--team/--project/--initial-input flags) and aliases= (a type owns another's spawn name), needing no built-in edits. The six built-in types are converted with zero behavior change; full suite 296 -> 312. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… only Agent types are selected explicitly: 'spawn codex' is plain codex, 'spawn codex-app-server' is the add-on — no automatic alias hijacks a built-in's spawn name. Removes agmsg_type_alias_for, spawn.sh's reverse-alias resolution, the aliases= manifest key (schema + node-launcher docs), and the alias fixture/test. The node-launcher (spawn=) plug point for external add-ons stays. Full suite 311/311; built-in behavior unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thanks for this — and the OpenCode (#136) "8-file scatter" framing is exactly the drift we want to kill. We're landing this direction in #181 (the 1.1.0 "restructure agent types" branch): each type becomes a read-only |
|
Closing as superseded by #181, which landed the pluggable agent-type registry on |
Summary
Adding an agent type to agmsg is currently a hardcoded, multi-file edit. The most
recent addition — OpenCode (#136) — touched 8 files, adding per-type
casearms in
whoami.sh,join.sh,spawn.sh, anddelivery.shplus a template anddocs. Every new type repeats that scatter, and the type list drifts between files.
This PR makes a type data: a small read-only manifest at
types/<name>/type.confdescribes detection, the whitelist, spawn, and deliveryrouting, and the scripts read it instead of hardcoded
casearms. The sixexisting types are converted with zero behavior change — the full suite stays
green (296 → 311 with the new registry tests).
What changes
scripts/lib/type-registry.sh— discovery + a per-key reader.Manifests are read-only
key=valuedata, neversourced (a third-partytype cannot execute code; an adversarial
cli=$(touch X)is read as a literal).types/{claude-code,codex,gemini,antigravity,copilot,opencode}/type.conf— thesix built-ins as manifests (
detect/detect_proc,cli/spawnable,hooks_file,monitor,template).whoami.sh— detection readsdetect=(env vars) anddetect_proc=(processglobs); sorted order preserves the claude-code < codex < gemini precedence.
join.sh— the whitelist validates against the registry.spawn.sh— the spawnable set and launch binary come from the manifest; theerror lists the computed spawnable set.
delivery.sh—resolve_hooks_filereadshooks_file=(the per-type hookformat in
apply_settings_*is unchanged).install.sh/tests/test_helper.bash— ship/copytypes/.docs/agent-types.md— the manifest schema + "adding a type = manifest +template".
Adding a type, after this PR
Drop
types/<name>/type.conf+templates/cmd.<name>.md.whoami,join, andspawnpick it up with no further edits. (A genuinely new delivery hook formatstill needs an
apply_settings_<name>; reusing a format needs no code.)Extensible to external add-on types
Beyond built-in CLI types, a type can declare a node launcher so an agent
runtime ships entirely outside the agmsg tree and plugs in with no core edits:
spawn=<launcher>.mjs—spawn.shlaunches it via Node with four universalflags (
--name --team --project --initial-input); all type-specific config isthe launcher's own default/env, so core names no add-on.
Types are always selected explicitly (
spawn <type>), so an add-on neverhijacks a built-in's spawn name.
Real-world example
The node-launcher plug point is demonstrated by a real, separately-released add-on:
lucianlamp/agmsg-codex-app-server— a Codex runtime (app-server + host-managed turn-trigger watcher + a
scroll-region TUI) that installs on top of a stock agmsg purely via the
registry (a
types/codex-app-server/manifest withspawn=), touching nobuilt-in; spawned explicitly as
spawn codex-app-server <name>.lucianlamp/agmsg— a stock install of this PR with the add-on dropped on top.
The add-on lives in its own repo and is not part of this PR — it is only the
proof that the mechanism hosts external types cleanly.
Testing
tests/test_type_registry.bats: discovery, the key reader + default,detection-manifest values + precedence + end-to-end via
whoami.sh, thespawnable set, the node-launcher (
spawn=) plug point (an in-tree fixture, sothe mechanism is not dead code), a no-hardcoded-type-name guard, and a guard
that manifests are data (the adversarial
$(touch …)runs nothing). 311/311.spawn codex-app-serverproduces a boot command that runs its Node launcherwith the universal flags.
Scope / non-goals
source changes (case → manifest).
apply_settings_*) is unchanged.