Skip to content

feat: add managed provider plugin infrastructure - #3121

Merged
thymikee merged 2 commits into
mainfrom
feat/managed-provider-plugins
Oct 3, 2026
Merged

thymikee merged 2 commits into
mainfrom
feat/managed-provider-plugins

Conversation

@thymikee

@thymikee thymikee commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

Summary

Add managed npm provider plugins under AGENT_DEVICE_HOME:

  • agent-device plugins add @vendor/provider@^1
  • agent-device plugins list
  • agent-device plugins update @vendor/provider

Install immutable npm directories before atomically activating user config; check metadata and compatibility before loading factory code. Removal leaves active daemon sessions alone until restart. Existing providers remain bundled; Doublespeed and TestMu are the first external adapter targets.

Keep the experimental agent-device/plugins SDK to the factory context (env, options, createError). Provider implementation contracts remain private to core, with their source declarations linked in the author guide. Removing the transitive runtime exports cuts added declarations from 99.6 kB to 0.4 kB. A packed declaration-size guard prevents recurrence.

36 files; 1,000 gross changed lines. Implementation precedes the final enforcement commit.

Validation

Tested commit: 986b32b54190637d16d32a25ae652f0d0ce61007.

  • pnpm check:affected --run: all runnable checks passed, including 518 related files / 4,565 tests and package gates.
  • Installed-package smoke, real npm/daemon add-update-remove smoke, and an isolated strict plugin-context consumer without Node types passed. Restoring the old declarations fails the new size guard.
  • Independent Claude review supported the reduction; its return-contract documentation finding is addressed.
  • Exact-head CI Size: +8.1 kB unpacked / +3.0 kB download, down from +108.6 / +26.9 kB. Typecheck & Package, Integration Tests and Coverage passed; one platform smoke check is running. No live provider integration is claimed.

@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-10-03 14:10 UTC

@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
Installed (including dependencies) 4.93 MB 4.94 MB +8.1 kB
Package (unpacked) 4.93 MB 4.94 MB +8.1 kB
Package (download) 1.48 MB 1.48 MB +3.0 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 25.2 ms 25.9 ms +0.7 ms
CLI --help 78.0 ms 77.7 ms -0.2 ms

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 35 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/__tests__/provider-device-runtimes.test.ts Outdated
Comment thread src/plugins/manifest.test.ts
Comment thread src/plugins/load.test.ts
Comment thread src/daemon/server/daemon-runtime.ts
Comment thread src/plugins/load.ts
Comment thread website/docs/docs/plugins.md Outdated
Comment thread src/plugins/store.ts Outdated
Comment thread src/cli/commands/plugins.ts Outdated
Comment thread website/docs/docs/configuration.md
@thymikee

thymikee commented Oct 2, 2026

Copy link
Copy Markdown
Member Author

At 36d958f, plugins update A fails when a different plugin B has an unreadable or incompatible manifest, and the same happens the other way round. In changePlugin, add and update call readInstallation on every other selection before the collision check. If a sibling's install directory is gone, its package.json is damaged, or a later core release bumps PROVIDER_PLUGIN_API_VERSION, the staged plugin is deleted and the sibling's error is rethrown. After an ABI bump with two plugins, neither can be updated. That contradicts plugins.md ("Update or remove that plugin to recover") and the plugins update <name> hint in plugins.ts. It also means one broken selection blocks plugins add of any unrelated plugin. Users would have to remove and re-add plugins, which loses pinned versions and options. The rule to satisfy is that a change to plugin X may fail only on X's own install or manifest, or on a provider ID claimed by another selection whose manifest reads successfully. Please build the sibling provider set from readable siblings and skip unreadable ones. Their provider IDs are checked again when they are themselves updated, and the daemon refuses them anyway. Please add a store test where one sibling has a missing installation directory and update of the other plugin succeeds. That test should fail without the fix.

Coverage fails only in src/__tests__/client-api-examples-drift.test.ts:124 ("client-api.md documents every published package entry point"). The PR's new ./plugins export adds agent-device/plugins to the expected list, but the subpath manifest in website/docs/docs/client-api.md was not updated. Adding the subpath there should clear it, so this failure comes from the diff.

Not blocking, and you can take or leave these: the size report shows +108.6 kB unpacked (+27 kB download) for about 530 production lines, far above the 3 kB threshold in docs/agents/pull-requests.md, and I could not build to find the cause. A type-only entry plus a CLI handler and loader should cost a few kB, so please diff the dist file list against main and look for duplicated chunks from the new entry or the dynamic import of plugins/load.ts. Then explain any remaining growth in the PR body. Also, plugins is added by hand to three local-command lists in cli.ts (REMOTE_MATERIALIZATION_DEFERRED_COMMANDS, resolveActiveConnectionDefaults, shouldResolveRemoteAuth) although the registry already declares the 'local-cli' group, so a follow-up could derive them from a registry trait. And the ./plugins export uses the default condition and sits before ., while sibling entries use import and follow ..

Could the loader keep only the owner and provider match check and let the gateway reject the rest? createPlatformRuntimeGateway and the duplicate check at provider-device-runtime.ts:264 already enforce runtime shape at composition time. I found no smaller interface for the store itself, and the reuse of the lock, publish and command helpers looks right.

I did not run the local-registry npm smoke or any tests. I could not attribute the size growth without building both bases. I did not trace the daemon held-lock branch behind the open thread on daemon-runtime.ts:350, but the mechanism it states still applies, so that thread stands. I also did not check whether provider IDs outside DEFAULT_PROVIDER_RUNTIME_REQUIRED_IDS, such as connection or proxy names used by connect, should also be reserved. No conflicts are known. Before merge, a broken sibling must no longer block add or update of another plugin, with a regression test, and agent-device/plugins must be added to the client-api.md subpath manifest so Coverage passes.

@thymikee
thymikee force-pushed the feat/managed-provider-plugins branch from 36d958f to 2860f56 Compare October 3, 2026 06:16
Comment thread src/plugins/load.test.ts Fixed
@thymikee

thymikee commented Oct 3, 2026 •

Copy link
Copy Markdown
Member Author

Addressed in 2860f56b2c:

  • Add/update now check collisions against readable siblings only. Missing directories, malformed JSON, and incompatible ABI siblings no longer block another package's update or addition. Tests preserve the target's pin/options and the broken sibling's selection; daemon startup still refuses the broken set. The missing-directory test failed before the fix.
  • Update also repairs damaged selection fields using validated prior constraints/options.
  • Added agent-device/plugins to the SDK subpath manifest; the documentation drift test passes. Normalized its export condition to import and placed it after the existing entries.
  • Fixed held-lock and startup-failure cleanup, validated owner instances, and strengthened inert-loading and synchronous-shutdown tests. All nine inline review threads have mapped replies and are resolved.

Built both main and the updated head: dist grows 108,507 bytes, of which 99,645 are declarations and 8,862 JavaScript. There are no repeated declaration regions. The plugin ABI exposes existing runtime/host contracts; Claude's independent review agreed that shrinking it would require an adapter layer. Removed unused production exports identified by that review.

I kept runtime function checks: the gateway checks owner matching/duplicate registration, and provider-device-runtime.ts checks duplicate IDs; neither validates third-party runtime methods at composition. Registry-derived CLI routing remains a follow-up. Connection/proxy transports are not additional bundled provider runtime IDs; connect-extension naming belongs with that future registration seam.

pnpm check:affected --run passed at this head, including 4,565 related tests. The real npm/local-registry and built-daemon smoke also passed. Exact-head Coverage and provider integration remain pending in GitHub.

Update at 986b32b541: removed the experimental public runtime/registration aliases; agent-device/plugins now exports only ProviderPluginHost. Core keeps its typed implementation contracts and runtime validation, with the returned contracts linked in author documentation. Added declarations fall from 99.6 kB to 0.4 kB, and restoring the old output fails the packed size guard. Exact-head CI reports +8.1 kB unpacked / +3.0 kB download; Typecheck & Package and Integration Tests passed. All runnable affected gates passed locally (4,565 related tests), as did isolated strict author-context compilation without Node types. Claude supported this smaller surface; its documentation finding is addressed. Coverage and platform smoke checks are still running. Updated the existing Doublespeed and TestMu migration guidance to use the host-only SDK.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 19 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/plugins/store.test.ts
Comment thread src/plugins/store.test.ts Outdated
Comment thread src/plugins/manifest.test.ts Outdated
Comment thread website/docs/docs/plugins.md Outdated
@thymikee
thymikee force-pushed the feat/managed-provider-plugins branch from 2860f56 to d535379 Compare October 3, 2026 06:29
@thymikee

thymikee commented Oct 3, 2026

Copy link
Copy Markdown
Member Author

This PR is ready on the code. The 36d958f findings from the earlier review (#3121 (comment)) are fixed at 2860f56, and I found no new problems in the changes since then.

Not blocking, and you can take or leave these: Promise.allSettled(providerDeviceRuntimes.map(async (runtime) => await runtime.shutdown())) now appears three times in startDaemonRuntime (https://github.com/callstack/agent-device/blob/2860f56/src/daemon/server/daemon-runtime.ts#L652), and load.ts has a fourth copy, so one local shutdownProviderRuntimes() closure would cover all exits. In https://github.com/callstack/agent-device/blob/2860f56/src/plugins/manifest.ts#L36 the AppError cause is now an error as Error cast, and the earlier error instanceof Error ? error : undefined guard was safer.

Smoke Tests was still running with no log, so CI is not green yet. This PR touches that route: every daemon start now runs createDaemonProviderRuntimeComposition, which reads the plugin config under AGENT_DEVICE_HOME, and cli.ts routing changed. A smoke failure therefore cannot be dismissed by pointing at main. With no plugins configured, composition should do nothing. I did not run any tests or the local-registry npm smoke, and I did not re-check the bundle-size growth I noted earlier. I judged the earlier fixes by reading the code.

Before merge, Smoke Tests needs to pass. The open Cubic threads on the weakened store.test and manifest.test assertions (exact npm argv, cleared selection after remove, incompatible_plugin details) came from this latest change, and they still stand. Please restore those assertions.

This review covers 2860f56. The newer head d535379 changes one line in src/plugins/load.test.ts and does not touch the points above.

@thymikee
thymikee force-pushed the feat/managed-provider-plugins branch from d535379 to 0a69fc4 Compare October 3, 2026 06:47
@thymikee

thymikee commented Oct 3, 2026

Copy link
Copy Markdown
Member Author

Both problems from the earlier review at 36d958f are fixed at 0a69fc4, and I found nothing new. The regression tests now cover the cases from that review. I judged them by reading the code before and after the fix. I did not run the tests or a local-registry npm smoke.

All 22 checks pass, including Coverage and Smoke Tests. Smoke Tests exercises the daemon startup composition this PR changes. There are no conflicts. No further changes are needed from my side.

I did not re-check the bundle-size numbers. The earlier note stands: +108.5 kB, of which 99.6 kB is declarations.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Oct 3, 2026
@thymikee
thymikee force-pushed the feat/managed-provider-plugins branch from 0a69fc4 to 986b32b Compare October 3, 2026 11:01
@thymikee

thymikee commented Oct 3, 2026

Copy link
Copy Markdown
Member Author

The changes since 0a69fc4 look good at 986b32b, and I found no new problems. CI is green, with 22 of 22 checks passing at this head, and there are no conflicts. I did not build the package, so I could not confirm that the emitted dist/src/plugins.d.ts exports ProviderPluginHost as a value and stays under 1024 bytes. That check rests on the integration test passing in CI. All earlier review threads were opened before the last review and none is open now. Cubic has not reviewed this head yet. After that, the PR waits for a maintainer to merge.

@thymikee
thymikee merged commit 2177ea8 into main Oct 3, 2026
22 checks passed
@thymikee
thymikee deleted the feat/managed-provider-plugins branch October 3, 2026 14:09
thymikee added a commit to hassantsyed/agent-device that referenced this pull request Oct 3, 2026
…lugin

* origin/main: (628 commits)
  fix(ios): pin runner build roots under derived data (callstack#3158)
  feat: add managed provider plugin infrastructure (callstack#3121)
  fix(ios): report keyboard focus from the AX bridge's is-editing trait (callstack#3163)
  feat(devices): report model and osVersion (callstack#3119)
  fix: guard alert deadline before native tap synthesis (callstack#3113)
  feat(install-source): accept archive URLs from any public host (callstack#3110)
  test: keep uptime responsive behind busy runner work (callstack#3114)
  fix(apple): read the launch confirmation whenever the open cannot see the app (callstack#3115)
  fix(host-kit): keep extracted directories owner-accessible (callstack#3111)
  fix(daemon): run Apple tools with the requesting client's DEVELOPER_DIR (callstack#3109)
  fix(daemon): fence daemon.json removal to its owning process (callstack#3102)
  fix(snapshot): stop sibling-sized chrome containers from covering their own region (callstack#2996) (callstack#3097)
  fix(ios): stop reading windows past the one the runner resolved (callstack#3103)
  refactor(daemon): apply one dispatch-disclosure rule to returned and thrown failures (callstack#3099)
  chore: drop unused production exports and suppress dynamic consumers (callstack#3100)
  docs(help): document wait readiness and restart exhaustion (callstack#3098)
  docs: simplify Host to fresh Simlock devices and lease recovery (callstack#3095)
  feat(capture): report the display rotation a screenshot was rendered in (callstack#3088)
  refactor(snapshot): preserve normalized node attributes through presentation (callstack#3092)
  refactor(help): colocate fold guidance and extract workflows (callstack#3093)
  ...

# Conflicts:
#	README.md
#	package.json
#	packages/kernel/src/snapshot.ts
#	src/__tests__/eager-closure-budgets.ts
#	src/cli/commands/connection-presentation.ts
#	src/commands/schema/cli-help.ts
#	src/commands/schema/command-overrides.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants