Skip to content

feat: implement Rozenite as a built-in plugin#29

Open
burczu wants to merge 9 commits into
callstackincubator:masterfrom
burczu:feat/rozenite-plugin
Open

feat: implement Rozenite as a built-in plugin#29
burczu wants to merge 9 commits into
callstackincubator:masterfrom
burczu:feat/rozenite-plugin

Conversation

@burczu
Copy link
Copy Markdown
Contributor

@burczu burczu commented May 21, 2026

Closes #27

Summary

Implements the Rozenite plugin on top of the generic plugin system from #26. The plugin lets agent-cdp interoperate with Rozenite's React Native agent tooling without leaking any Rozenite-specific details into the core daemon or CLI.

Rozenite plugin

  • Lives entirely under packages/agent-cdp/src/plugins/rozenite/* — core code is unaware of Fusebox globals, Rozenite domains, or tool registry internals
  • Activates only for React Native targets; fails cleanly on unsupported targets
  • On target selection, bootstraps a CDP bridge: calls Runtime.enable, polls for __FUSEBOX_REACT_DEVTOOLS_DISPATCHER__, reads the dynamic BINDING_NAME, registers it via Runtime.addBinding, initialises the rozenite domain, and sends agent-session-ready
  • Tools are discovered dynamically from register-tool / unregister-tool binding events and held in an in-memory registry
  • Tool calls are dispatched by evaluating __FUSEBOX_REACT_DEVTOOLS_DISPATCHER__.sendMessage and correlated with tool-result binding events via a callId
  • Exposes a fixed CLI surface: rozenite status, rozenite tools, rozenite tool-schema <name>, rozenite call <name> [--input JSON]

Playground integration

  • Adds @rozenite/metro (dev) and @rozenite/agent-bridge to the playground
  • metro.config.js wraps the Expo Metro config with withRozenite (opt-in via WITH_ROZENITE=true)
  • useRozeniteBridge hook registers three test tools (app.echo, app.getTimestamp, app.getPlaygroundInfo) for end-to-end testing

Testing

# start Metro with Rozenite enabled
cd playground && WITH_ROZENITE=true pnpm start

# run the app, then:
node packages/agent-cdp/dist/bin.js start
node packages/agent-cdp/dist/bin.js target list
node packages/agent-cdp/dist/bin.js target select <id>
node packages/agent-cdp/dist/bin.js rozenite status        # state: ready, toolCount: 3
node packages/agent-cdp/dist/bin.js rozenite tools
node packages/agent-cdp/dist/bin.js rozenite call app.echo --input '{"text":"hello"}'

@burczu burczu changed the title feat: implement Rozenite as a built-in plugin (closes #27) feat: implement Rozenite as a built-in plugin May 21, 2026
burczu added 7 commits May 21, 2026 10:27
Constants and message type definitions for the Rozenite CDP binding
protocol — binding payload envelope, app↔agent message union types,
and AgentTool shape.
In-memory registry for dynamically registered Rozenite tools —
supports register by domain (qualifies names as domain.toolName),
unregister by qualified name, and clear on disconnect.
Async poll → bind → domain-init sequence for establishing the Rozenite
CDP binding. Polls for __FUSEBOX_REACT_DEVTOOLS_DISPATCHER__ at 500ms
intervals, times out after 30s with a descriptive error, and supports
AbortSignal cancellation for clean target-cleared teardown.
- Add alwaysExecutable flag to AgentPluginCommand so status-style
  commands can run regardless of plugin state; honour it in dispatch
- Implement RozenitePlugin: fire-and-forget bootstrap, onDisconnected
  wiring, tool registry lifecycle, in-flight call rejection on
  disconnect, and four commands: status, tools, tool-schema, call
Add rozenite CLI subcommand family (status, tools, tool-schema, call)
and register RozenitePlugin in the PluginOrchestrator alongside the
runtime bridge plugin.
18 tests covering bootstrap lifecycle, tool registry, and all four
commands — including alwaysExecutable status, tool-level vs transport
failure distinction, and disconnect rejection of in-flight calls.
…gration

The CDP binding approach (Runtime.addBinding + Runtime.bindingCalled) does not
work in Hermes/Fusebox New Architecture — binding events are routed to the
DevTools frontend only, not to debugging sessions. Switch to @rozenite/metro's
HTTP agent API (/rozenite/agent/*) which is designed for this use case.

Remove bootstrap.ts and tool-registry.ts; the HTTP session owns tool state.
CDP session disconnects no longer tear down the Rozenite session since the
HTTP session is independent of the CDP transport.

Add playground integration: metro.config.js wraps Expo config with withRozenite,
use-rozenite-bridge.ts registers three test tools (echo, getTimestamp,
getPlaygroundInfo), and index.tsx renders a Rozenite status section when the
Fusebox dispatcher global is present.
@burczu burczu force-pushed the feat/rozenite-plugin branch from 4bd4d97 to 538f24a Compare May 21, 2026 08:27
burczu added 2 commits May 22, 2026 08:22
Replaces the HTTP API approach with the correct CDP implementation using
Runtime.addBinding / Runtime.bindingCalled. Tools are discovered
dynamically from binding events rather than polled via HTTP.

Key fixes over the original attempt: Runtime.enable is called before
Runtime.addBinding, and the onEvent listener is registered before
initializeDomain is called to avoid missing early binding events.

Includes verbose console.log output on every Runtime.bindingCalled event
to aid investigation of CDP session behaviour.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement Rozenite as a built-in plugin on top of the plugin system

1 participant