Skip to content

feat(plugins): plugin contract v2 and one provider registry #3755

Closed

Description

@thymikee

Part of #3747. Blocked by #3748.

Required behavior

Plugin contract v2. The type-only contract comes from agent-device/plugins; there's no runtime helper.

import type { AgentDevicePluginFactory } from 'agent-device/plugins';

const testmu: AgentDevicePluginFactory<TestmuOptions> = (options = {}) => ({
  apiVersion: 2,
  provider: 'testmu',
  credentials: { variables: ['LT_USERNAME', 'LT_ACCESS_KEY'], byBackend?: { [backend: string]: string[] } },
  connection?: { capabilities, resolve, verify },
  setup: async (host) => ({ runtime, platformModule, dispose }), // or ({ webDriver, dispose })
});
export default testmu;
  • The default export is a factory called with options:
    • the store path calls it with config.json plugins[name].options;
    • JS hosts call it directly.
  • { webDriver } stays a data descriptor that the host interprets.

Entry discovery for v2

  • Resolve the package's ./agent-device export with import conditions, falling back to the package root only when that subpath is absent. If the subpath's import or setup throws, there is no fallback.
  • v2 packages need no agentDevicePlugin manifest.
  • v1 packages, those with agentDevicePlugin.apiVersion: 1, are dispatched by their manifest before any factory runs. They load through an adapter that reuses today's normalization (packages/plugins/src/load.ts:67) until 0.22: remove superseded plugin, client and environment APIs #3759.

One registry

  • Order: built-in → store → embedder. Store entries are taken in a deterministic order (package name).
  • Built-ins use the same shape: BrowserStack, AWS Device Farm, the WebDriver clouds, and Limrun until refactor(provider-limrun): ship Limrun as the @agent-device/limrun plugin #3311.
  • Validation: one function validates the factory's result for every source.
  • Duplicate providers: the first registration claims the ID, even if that registration fails. Later ones are recorded as failed. cloud and proxy stay reserved as routing identities.
  • Retire these lists: RESERVED_PLUGIN_PROVIDERS and DEFAULT_PROVIDER_RUNTIME_REQUIRED_IDS. Move their consumers in the same change:
    • src/cli/connection/provider-policy.ts:7;
    • src/provider-credential-fingerprint.ts:7;
    • connection capability discovery (packages/plugins/src/connection.ts:26), which reads capabilities from the registry instead of manifests.

Failure isolation and inventory

  • A plugin that fails to enumerate, load, validate or set up no longer stops the daemon. The daemon starts with the remaining plugins, cleans up any partial registration, and records:
    { provider?, source: 'builtin' | 'store' | 'embedder', package?, version?, state: 'active' | 'failed', error?, credentials: { variables, byBackend } }.
    Store enumeration errors (packages/plugins/src/store.ts:59) are isolated the same way.
  • The inventory is published in daemon.json and returned by a new agent-device daemon status --json. That command needs a command-registry entry, versioned help, and a schema projection, including MCP if it's projected there.
  • plugins list --json works offline. It reports store selections and, when a daemon is running, its inventory.
  • The client credential fingerprint reads variable names from the daemon inventory, falling back to v1 manifests. It narrows per backend through byBackend, preserving the refactor(provider-limrun): ship Limrun as the @agent-device/limrun plugin #3311 requirement. Credentials passed as options are excluded from the environment fingerprint.
  • provider-not-registered (fix(daemon): refuse leases for providers without a registered runtime #3748) includes the failed inventory entry for that provider.

ADR 0034 "Provider plugin registry" (brief) covers: the three sources, one shape, the set fixed per daemon, failure isolation, and a short daemon-ownership note pointing to #3754 and #3757.

Done when

  • Built-in providers, a v1 fixture plugin and a v2 fixture plugin all load through the one registry.
  • Integration tests cover a broken and a working plugin side by side, and a duplicate provider ID.
  • connect testmu and the credential-change refusal still work.
  • A packaged-export smoke test for agent-device/plugins types passes.
  • The website plugins page shows v2 authoring and the inventory.
  • There's a changeset.

Activity

  1. thymikee commented on Oct 17, 2026

    @thymikee
    MemberAuthor

    Done: #3767 (plugin contract v2, one provider registry, failure isolation, inventory), #3768 (daemon status), #3769 (docs, ADR 0034). TestMu on v2 (#3756) landed in #3770.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions