v0.4.0 — plugin parity, owned servers, per-target scoping
Everything in this release came out of one dogfooding cycle: running agentstack
against a real machine with 8 CLI harnesses, and fixing what actually broke.
Plugins: adopt once, install anywhere
plugins adoptlifts an installed native plugin (Claude Code or Codex)
into the manifest. Bundled skills are copied into the central library
with plugin provenance (source,rev) recorded — the recipe survives
native plugin updates and uninstalls instead of path-pointing into a
versioned plugin cache. Codex auth wiring (bearer_token_env_var,
env_http_headers) is carried through as portable${REF}headers.- Satisfied natively: the harness a recipe was adopted from is satisfied
by its still-installed native plugin.plugins statusanddoctorreport
it as up to date at the adopted version + rev and surface drift when the
native plugin moves ahead — they never suggest double-installing. - Generated Claude Code packages no longer reference the auto-loaded
hooks/hooks.jsonpath fromplugin.json(Claude Code treats that as a
duplicate-hooks error that breaks the whole plugin); hook-less recipes ship
no hooks file at all.
Servers: scoping, native keys, and app-owned config
- Per-server targets —
[servers.X] targets = ["claude-code"]scopes the
fan-out like instructions and hooks already could;targets = []opts a
server out entirely (how adopted plugin servers are stored, so the same
server is never configured twice on a harness its plugin already serves). - Per-target extras —
[servers.X.extra.codex] startup_timeout_sec = 20
passes native keys the manifest schema doesn't model through to one
adapter, verbatim.initandadoptlift unknown config keys back into
extras, so hand-tuned settings surviveapply --writeround trips. - Owned servers —
[servers.X] owner = "codex"makes the owning app's
config the source of truth. When the app rewrites its own entry (a
self-update), every plan refreshes the manifest from disk and fans the
fresh values to the other CLIs — instead of proposing a downgrade.
${REF}values stay manifest-canonical so secrets never leak back into
the manifest, and valid trust is re-pinned across the machine-derived
rewrite.
Secrets & reporting
- Secret refs are strict
${IDENTIFIER}s: shell fallback syntax
(${VAR:-fallback}) and prompt-style placeholders (${input:key}) pass
through verbatim and are no longer misread as manifest secrets. - One keychain read per ref per run; a transient read failure is retried and
a persistent one reports as keychain read failed — distinct from not
found, so a flaky keychain daemon can no longer block writes by claiming
a stored secret is missing. apply --writesummaries count targets actually written ("X of Y written —
Z blocked"), not planned changes.
Docs: README gains the plugin-parity walkthrough and owned-servers story; the
feature reference covers the new manifest fields and secret semantics; the
shipped using-agentstack skill teaches agents the new rules.