Everything an extension could do stopped at the machine the panel runs on,
which is at odds with a product built around a fleet of servers. A plugin can
now run an agent command and get its result back.
This is also where agent.command:<action> starts meaning something. The
manifest permission has existed as a declaration — collected in the manifest,
surfaced at install time for consent — with nothing enforcing it, because
plugins called the agent registry directly. Dispatching through the SDK checks
it first, matched verbatim, so a plugin can only run the actions it asked the
operator for by name. The one bundled extension that uses agent commands still
calls the registry directly; retrofitting it onto this is a follow-up.
Nothing underneath is re-implemented: the per-server ACL still decides whether
that server permits the action, and the ServerCommand audit row is still
written — pass user_id so it names a person rather than "the panel".
Three things the wrapper adds over a bare send_command:
- it unwraps the double envelope. A transport-successful reply can carry a
handler failure inside, which reads as success to anyone checking only the
outer flag.
- it fails before dispatch on an offline agent or a missing capability, so the
caller gets a reason instead of an unknown-action error from the far side.
- the default timeout is 45s rather than the registry's 30s. An agent on the
HTTP long-poll fallback can sit ~25s before it even sees the command, which
leaves a 30s budget almost no room to actually run anything.
Failures raise with a .code, so a plugin can tell an offline agent from a
refused one from a timeout, and map them to its own HTTP responses.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>