Repository navigation
Provider plugins v2, daemon ownership and host environment (umbrella) #3747
Copy link
Copy link
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Purpose
One provider plugin contract loaded three ways, an explicit owner for every daemon, and a daemon that reads no process globals.
The three ways a plugin loads:
- built-in providers;
agent-device plugins add(the store);- a JS host that imports the plugin package as an ordinary npm dependency.
This replaces the current workaround in e2e's @e2e-dev/testmu. It fakes the plugin store: it symlinks the package into plugins/<uuid>/node_modules, writes a private AGENT_DEVICE_HOME, and lets a client start a detached daemon implicitly.
Release rule
Every child lands additively in 0.21.x. Superseded shapes keep working until #3759 removes them in 0.22.
Children
- fix(daemon): refuse leases for providers without a registered runtime #3748 — refuse leases for providers without a registered runtime
- refactor(host-kit): capture HostEnvironment once at entry points #3749 — HostEnvironment captured once at entry points (foundation)
- refactor(platforms): read host settings from HostEnvironment in platform and capture packages #3750 — HostEnvironment in platform and capture packages (blocked by refactor(host-kit): capture HostEnvironment once at entry points #3749)
- refactor: read host settings from HostEnvironment in root src and remaining packages #3751 — HostEnvironment in root src and remaining packages (blocked by refactor(host-kit): capture HostEnvironment once at entry points #3749)
- refactor(daemon): daemon runtime owns no process globals #3752 — daemon runtime owns no process globals;
ShutdownReport(blocked by refactor(host-kit): capture HostEnvironment once at entry points #3749) - chore(layering): forbid process globals outside entry points #3753 — gate: no process globals outside entry points (blocked by refactor(platforms): read host settings from HostEnvironment in platform and capture packages #3750, refactor: read host settings from HostEnvironment in root src and remaining packages #3751, refactor(daemon): daemon runtime owns no process globals #3752)
- feat(client): explicit daemon lifecycle with ensureDaemon() and attach clients #3754 — explicit daemon lifecycle:
ensureDaemon()and attach clients - feat(plugins): plugin contract v2 and one provider registry #3755 — plugin contract v2, one provider registry, inventory, ADR 0034 (blocked by fix(daemon): refuse leases for providers without a registered runtime #3748)
- feat(provider-testmu): publish @agent-device/testmu on plugin contract v2 #3756 —
@agent-device/testmuon plugin contract v2 (blocked by feat(plugins): plugin contract v2 and one provider registry #3755) - feat(host): startAgentDevice() for embedder-owned daemons #3757 —
agent-device/hoststartAgentDevice()(blocked by feat(daemon): the runtime owns its state and never the process #3772 and the HostEnvironment PRs feat(host): capture HostEnvironment once at entry points #3763/refactor(host): read host settings from HostEnvironment in root src and remaining packages #3775/refactor(platforms): read host settings from HostEnvironment in platform and capture packages #3777; design pass done 2026-10-17) - proposal: per-lease provider options #3758 — proposal: per-lease provider options (not scheduled)
- 0.22: remove superseded plugin, client and environment APIs #3759 — 0.22 removals (blocked by all of the above and refactor(provider-limrun): ship Limrun as the
@agent-device/limrunplugin #3311)
Decisions (maintainer, 2026-10-16)
- Plugins are factories called with options. A store install passes
config.jsonoptions; a JS host callstestmu({ credentials })directly. - No module-URL identity. Nothing relies on
import.meta.url. - No Effect dependency.
{ webDriver }stays a data descriptor. Plugins import only types from agent-device.- One registry. Sources compose in the order built-in → store → embedder. A plugin that fails is listed, not fatal.
- The plugin set is fixed for a daemon's lifetime.
- Clients never manage the daemon implicitly. Starting or replacing one is an explicit
ensureDaemon()call. Embedders own their daemon throughagent-device/host. - The ADR is brief. ADR 0034 records the plugin registry, with only a short note on daemon ownership.
External acceptance
- e2e:
tester-army/e2emigrates@e2e-dev/testmuand@e2e-dev/limruntoagent-device/host, deletinginstallPluginand the CLI-based daemon stop. That work is tracked in e2e after feat(host): startAgentDevice() for embedder-owned daemons #3757 ships. - refactor(provider-limrun): ship Limrun as the
@agent-device/limrunplugin #3311: the Limrun extraction ships@agent-device/limrunon the v2 contract from feat(plugins): plugin contract v2 and one provider registry #3755.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request
Activity
thymikee commentedon Oct 17, 2026
Decision needed before #3750/#3751 build on #3763: should
HostEnvironmentreach daemon handlers and platform modules explicitly (scope.host, host ports such asAndroidAdbHost.environment), or through the ambientcommandHostEnvironment()that #3763 introduced? Recommendation: keep ambient only for spawn env and exec diagnostics (whatwithCommandHostalready owns) and make everything else explicit, so a missing host is a type error and the throwing accessor andbindTestCommandHostsuites go away. Context: the review on #3763.fix(daemon-client): wait for an acknowledged quiesce instead of killi…
fix(daemon-client): name the state dir in the not-running hint and re…
fix(daemon-client): document the drain timeout and tighten ensureDaem…