Skip to content

Retarget Agent Flow plugin to a summonable full canvas overlay - #1325

Merged
chr1syy merged 26 commits into
RunMaestro:rcfrom
chr1syy:feat/agent-flow
Jul 29, 2026
Merged

Retarget Agent Flow plugin to a summonable full canvas overlay#1325
chr1syy merged 26 commits into
RunMaestro:rcfrom
chr1syy:feat/agent-flow

Conversation

@chr1syy

@chr1syy chr1syy commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Agent Flow overlay redesign (Phase 1)

Retargets the bundled Agent Flow plugin from a cramped docked panel into a hotkey-summoned full-canvas overlay of all running agents, addressing the placement complaint from live testing.

Change

  • Plugin panel moves from right dock to a full-window overlay, summoned by a keybinding.
  • Renders all running agents as nodes with tool satellites and a live thought bubble; click a node to jump to that session.
  • Two host-API additions it needs: a metadata-only session.activated event (so the plugin tracks the focused agent) and a sessions.focus verb (to request a session jump).
  • Bumps plugin host API 1.14.0 -> 1.16.0 (deliberately skips 1.15.0, which PR feat: Board task DAG + Agent Profiles (dispatcher on Cue tick, worktree isolation, plugin-bus events, CLI parity) #1272 uses, to avoid collision), with matching SDK, docs, and tests.

Validation

Type-check (main + lint + cli), ESLint, Prettier clean. Scoped tests -> 21 files, 515 pass (incl. plugin-sdk drift.test.ts).

⚠️ Notes

  • Bundled plugin files changed (plugin.json, main.js, panel.html, README.md), so the plugin SHA changed and it must be re-signed for any local/release build (the A1/Sign bundled plugins during release with drift guard #1298 signing coupling). examples/plugins/*/signature.json is gitignored (produced at sign time).
  • Phase 1 only: the DVR/replay timeline and the gamified glow are deferred to Phase 2 per the design plan.
  • Visual check in a real (re-signed) build recommended - overlay render, chord summon, and session-jump are UI-heavy.

Base: upstream/rc. Playbook: .maestro/playbooks/FEAT-AGENTFLOW-OVERLAY-01.md. Design: .maestro/plans/agent-flow-overlay-redesign.md.

Summary by CodeRabbit

  • New Features
    • Agent Flow now works as a full-window overlay with improved “focus” and “jump” behavior (Alt+Shift+F) and refreshed overlay visuals.
    • Plugins can focus existing sessions and summon their own modal panels via open/close/toggle, including full-size modals.
    • Added the session.activated plugin event to reflect focus changes.
  • Documentation
    • Updated plugin/API guides to reflect host API 1.16.0, including new capabilities, events, and modal panel behavior.
  • Tests
    • Expanded test coverage for session focusing, modal panel visibility, and the Agent Flow overlay.
  • Chores
    • Updated ignore rules for release-time plugin signing artifacts.

chr1syy and others added 22 commits July 20, 2026 22:30
Agent Flow plugin, phase 1. Adds a metadata-only `tool.executed` topic to
the host -> plugin event catalog and emits it from the process-listener
that already forwards `process:tool-execution` to the renderer.

The payload carries name and timing only: sessionId, toolName, timestamp,
plus optional toolCallId and a best-effort `phase` lifecycle string lifted
defensively out of the provider `state` blob. The `state` object itself
(tool arguments and results) is never forwarded, per the metadata-only
contract in src/shared/plugins/events.ts.

The topic and payload are mirrored into the vendored plugin SDK so the
drift guard in packages/plugin-sdk stays green; the HOST_API_VERSION bump
belongs to a later phase.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e 2)

Adds the missing host-to-panel push path: a plugin sandbox can now push
JSON-only, size-capped data into its OWN declared panels via
maestro.ui.panelPost(panelId, data). Flow: sandbox -> main (validate +
cap) -> renderer broadcast -> panel frame -> webview guest -> page.

- rpc-protocol: ui.panelPost gated behind ui:panel capability
- contributions: MAX_PANEL_POST_BYTES = 64KB per-message cap
- panel-host: PANEL_DATA_CHANNEL constant
- sandbox SDK wrapper + host handler (own-panels-only, fail-closed sink)
- main wiring via safeSend broadcast; preload + PluginPanelFrame delivery
- guest preload relays only maestro:panelData into the page, no reply path
- tests: rpc table, full handler suite, deps-wiring guard

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Phase 3)

Officialize the Phase 1-2 additive host-API surfaces (tool.executed event
topic, ui.panelPost host-to-panel push):

- HOST_API_VERSION 1.12.0 -> 1.13.0 (src/shared/plugins/host-api.ts).
- Vendor ui.panelPost + MAX_PANEL_POST_BYTES into @maestro/plugin-sdk, bump
  its HOST_API_VERSION + version-history comment, add panelPost to MaestroUiApi,
  bump package.json 0.7.0 -> 0.8.0, refresh drift-guard pin + panel-cap check.
- CLAUDE-PLUGINS.md: version bump, semver-history entry, panelPost handler
  bullet + tool.executed events note.
- PLUGIN-DEVELOPMENT.md: tool.executed topic row, 'Pushing live data to your
  panel' subsection, panelPost host-method row, minHostApi 1.13.0 notes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds examples/plugins/agent-flow/: a tier-2 plugin whose sandbox entry
subscribes to the metadata-only host event stream, maintains a per-session
execution-graph model (lane per session, tool-call nodes merged by
toolCallId, 300-node cap), and pushes coalesced snapshots to its panel via
maestro.ui.panelPost (250ms trailing-edge, 60KB size guard). panel.html is a
Phase-5 placeholder. Manifest validates with zero errors against host API
1.13.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ine) (Agent Flow Phase 5)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ow Phase 6)

Folds issue RunMaestro#1231 ("more insight to long running thinking tasks") into the
Agent Flow visualizer, using metadata only (no thinking prose or tool
arguments/outputs).

main.js: subscribe to agent.awaiting; track per-lane lastActivityAt,
runningToolCount, awaiting, and lastError; emit a top-level summary
{ busyLanes, runningTools, awaitingLanes, erroredLanes } and the four new
fields per lane. runningToolCount is maintained independently of the capped
nodes array (decremented on close and on trim of a still-open node).

panel.html: header activity strip from snapshot.summary; per-lane health
badges (coarse status, tool count, live elapsed timer, amber stall warning
past 30s, red error badge with recoverability hint); a 1s interval repaints
only the summary + badges against the live clock so timers advance between
snapshots without re-rendering the SVG graph.

README: new "Activity and health (issue RunMaestro#1231)" section noting the
metadata-only boundary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Final validation pass for the Agent Flow plugin effort. No code behavior
changes: this closes out the playbook's verify/audit/wrap-up phase.

- README: add "Security notes" (six audited invariants, each PASS) and a
  "Result" section (what shipped, the two host-API additions at 1.13.0,
  install steps, known limitations).
- panel.html: replace six en-dash "no value" placeholders with a plain
  hyphen (repo rule: no em/en dashes anywhere). File still parses; single
  script/style block; no external references.

Verified: tsc clean (all three configs), eslint src/ clean, scoped tests
green (196 root + 29 plugin-sdk), main.js parses.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CI on PR RunMaestro#1254 flagged two real issues from the Phase 2 host-to-panel
channel:

- Test: PluginPanelFrame now subscribes to
  window.maestro.plugins.onPanelData in a useEffect, but the renderer
  plugin-bridge test doubles predate that method, so any test mounting the
  frame threw "onPanelData is not a function". Added onPanelData (returns an
  unsubscribe fn, matching onChanged) to the global mock in
  src/__tests__/setup.ts and the local double in PluginPanelSlot.test.tsx.
- Format: `prettier --check .` (whole repo in CI) flagged
  docs/agent-guides/PLUGIN-DEVELOPMENT.md (my added host-method table row
  widened the column, re-padding the table) and the Phase 2 block in
  plugin-host-handlers.test.ts. Applied prettier --write; formatting only.

Verified: prettier --check . clean, eslint src/ clean, tsc (3 configs)
clean, and the plugin-panel test files pass (112 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…least privilege

Resolves the Greptile P1s and CodeRabbit nits on the Agent Flow plugin,
verified against the host event contracts:

main.js (sandbox model):
- Agent events update all lanes matching the agentId and no longer
  synthesize a nodeless agentId-keyed ghost lane (was: first-match, could
  update the wrong tab's lane and let a ghost lane win the lookup).
- usage.updated carries per-turn counts, not session totals, so accumulate
  tokens/cost across turns (contextWindow is a capacity: take latest).
- On a terminal agent/run event (completed/exited/run.completed) close any
  still-open tool nodes and zero the running count, so a producer that emits
  "running" without a terminal event no longer leaves a lane "working"
  forever.
- Clear lastError on any tool activity (open, terminal, or phase-less), not
  only when opening a new node, so a recovered agent drops its error badge.
- If even a 1-node-per-lane snapshot exceeds the cap, drop the
  least-recently-active lanes so the host accepts a reduced snapshot instead
  of rejecting the post (fleet summary still counts every lane).

Late panel mount (main.js + plugin.json + panel.html):
- Add a "sync" command the panel invokes on load and on becoming visible, so
  a panel opened after activity ended pulls the current snapshot instead of
  sitting empty (panelPost only reaches a mounted panel).

plugin.json: drop the unused storage:read / storage:write permissions
(main.js never touches sdk.storage) - least privilege, smaller install prompt.

PluginPanelFrame.tsx: the host-to-panel send no longer blanket-swallows;
expected not-attached/tearing-down races are dropped, anything else is
reported via captureException so real regressions still reach Sentry.

README: updated capability list (3, not 5), documented the sync/refresh
behavior, and added the multi-tab-status and lane-drop limitations.

Verified: prettier --check clean, eslint clean, tsc (3 configs) clean,
plugin manifest valid, main.js parses + stub-driven behavior check passes,
panel.html self-contained + parses, affected renderer tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 2 added an inbound `ipcRenderer.on('maestro:panelData', ...)` relay to
the panel guest preload, but plugin-panel.test.ts mocked `electron` with only
`ipcRenderer.sendToHost`, so importing the module threw
"ipcRenderer.on is not a function" (ubuntu shard 2).

Added `on` to the mock and a test asserting the preload relays exactly the one
inbound `maestro:panelData` channel into the page as a window message and never
turns it into an outbound call.

Verified: plugin-panel.test.ts green (5), all preload tests green (454), all
plugin test dirs green (595).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…14.0

Upstream rc shipped its own 1.13.0 (the host-mediated PluginUiSurface
registry + trusted-chrome guard), colliding with this branch's 1.13.0.
Following the repo's version-collision convention, this PR's two additive
surfaces (the tool.executed event topic and the ui.panelPost host-to-panel
push) move up to 1.14.0.

Conflicts resolved:
- src/shared/plugins/host-api.ts + packages/plugin-sdk/src/index.ts:
  HOST_API_VERSION -> 1.14.0, version-history comment now lists 1.13.0
  (upstream PluginUiSurface) below the 1.14.0 entry.
- packages/plugin-sdk/src/__tests__/drift.test.ts: kept both new source
  imports (MAX_PANEL_POST_BYTES + upstream PROTECTED_UI_SURFACES), dropped a
  duplicate serializedJsonByteLength import, pinned assertion -> 1.14.0.
- CLAUDE-PLUGINS.md: merged the semver-history paragraph (1.14.0 over 1.13.0).

Also fixed a silent semantic-merge duplication the auto-merge introduced in
packages/plugin-sdk/src/__tests__/drift.test-d.ts (duplicate PluginContributions
/ UiItemContribution type imports that broke the type-level drift guard).

Version refs bumped to 1.14.0 for this PR's surfaces: the agent-flow
plugin.json minHostApi, its README, PLUGIN-DEVELOPMENT.md (tool.executed +
ui.panelPost), the CLAUDE-PLUGINS.md source-of-truth line, and the plugin-sdk
package.json (0.8.0 -> 0.9.0, tracking-comment 1.14.0).

Verified: tsc (3 configs) clean, eslint src/ clean, prettier clean, plugin-sdk
drift guard green at 1.14.0 (30), plugin/preload/forwarding tests green (639).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the focused-agent signal the Agent Flow overlay needs (phase 1, host-API
addition A):

- new PLUGIN_EVENT_TOPICS entry 'session.activated' with payload
  { sessionId, tabId? } in src/shared/plugins/events.ts, mirrored by hand into
  the vendored packages/plugin-sdk (CI does not check that parity)
- emitted from the sessions:setActiveSessionId handler with its own 100ms
  trailing debounce, separate from the existing 400ms disk-write debounce, and
  suppressed when the same session is re-focused
- tabId is omitted: that IPC only reports the focused session and the stored
  tab state can lag the live one

Payload is ids only, per the events.ts metadata-only contract.
Adds a narrow, navigation-only host verb so a plugin can jump the user to
an existing agent's session without holding tabs:manage (which also carries
tab creation and destruction).

- new sessions:focus capability (risk low, no scope)
- sessions.focus verb: closed { sessionId, tabId? } schema, broker check,
  unknown-session rejection, main-side effect
- pluginAiFocusFields(): main-side mirror of the renderer's
  aiTabFocusFields(), now shared with tabs.focus so the two cannot drift
  (it also adds the activeGroupId: null the old inline literal was missing)
- sandbox shim + vendored plugin-sdk mirror (capability rows, HOST_API row,
  MaestroSessionsApi.focus)
Third host-API addition for the agent-flow overlay (Phase 1).

- contributions: optional panel `size?: 'default' | 'full'`, parsed leniently
  (absent is not an error, invalid errors and defaults) so existing manifests
  are byte-identical in behaviour.
- rpc-protocol: `ui.openPanel` / `ui.closePanel` / `ui.togglePanel` under the
  existing `ui:panel` capability, so no new consent prompt.
- plugin-host-handlers: one shared factory for the three verbs - closed
  {panelId} schema, broker check, own-panel resolution via the same getPanel
  dep `ui.panelPost` uses, non-modal placements rejected. Registered only when
  the new panelVisibility sink is wired (fail closed).
- main/preload: `plugins:panel-visibility` broadcast + `onPanelVisibility`
  bridge. Pure show/hide signal, no payload, no reply channel.
- renderer: `uiStore.openPluginPanelId` and the single App-level
  `PluginModalPanelMount`. Settings' launch button now sets the same store
  field, so the Settings path and a plugin's own summon share one mount and one
  webview guest.
- PluginPanelHost: `full` renders edge-to-edge (inset-4); the dead local
  Escape handler (onKeyDown on a non-focusable backdrop) is replaced with a
  layer-stack registration in the reserved plugin band.
- Vendored plugin-sdk mirrored by hand (CI does not check that parity).
Covers the three Phase 1 agent-flow overlay additions: the metadata-only
session.activated event, the sessions.focus verb plus its narrow
sessions:focus capability, and ui.openPanel/closePanel/togglePanel plus the
optional panel size field. 1.15.0 is taken by the Board + Profiles work on
this fork, so it is skipped.

Mirrors the constant and comment into the vendored plugin-sdk (package
0.9.0 -> 0.11.0; 0.10.0 is also taken by Board + Profiles), moves the
drift-guard pin, and lands the doc rows deferred from the three
host-API tasks in PLUGIN-DEVELOPMENT.md and CLAUDE-PLUGINS.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Manifest-only change: the flow panel moves from placement 'right' to
placement 'modal' + size 'full', gains an 'overlay' command bound to
Alt+Shift+F, and requests sessions:focus for click-to-jump. minHostApi
tracks the new 1.16.0 host surface.

Alt+Shift+F rather than Ctrl+Shift+F: the latter is the app's Go to Files
shortcut and usePluginKeybindings folds Ctrl into meta, so it would never
have fired.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add the overlay command (ui.togglePanel on the plugin's own flow panel),
track the focused agent from the metadata-only session.activated topic and
ship it in snapshots as focusedSessionId, and handle the panel-posted jump
message by calling sessions.focus.

jump stays out of contributes.commands: it is meaningless without args, and
the sandbox dispatches registered handlers without a manifest cross-check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replace the per-session lane columns in the agent-flow panel with one node
per agent on a single canvas: status-coloured pulsing halo, cost pill and
token bar, up to six orbiting tool satellites (name + phase + duration only),
and click-to-jump on a node or a finished card. Pan/zoom with auto-fit until
the user moves the view.

Adds a jsdom test that loads the real panel.html and drives it through the
host's message bridge, and refreshes the plugin README.
The bundled agent-flow plugin is signed at release time (or locally with a
dev key for testing), and the resulting examples/plugins/*/signature.json is
a build artifact that must never be committed - it pins the SHA of every
other file in the plugin dir. Mirrors the A1 signing branch, which has not
landed on rc yet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2fbd566f-463a-40e9-ae74-46639bdfc479

📥 Commits

Reviewing files that changed from the base of the PR and between 9f75650 and 639d1c9.

📒 Files selected for processing (2)
  • src/__tests__/main/ipc/handlers/persistence.test.ts
  • src/main/ipc/handlers/persistence.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/tests/main/ipc/handlers/persistence.test.ts
  • src/main/ipc/handlers/persistence.ts

📝 Walkthrough

Walkthrough

The PR upgrades the plugin host API to 1.16.0 with session focus, activation events, modal panel controls, and panel sizing. It rewires modal panel mounting and updates Agent Flow into a full-window overlay with focus-aware visualization, keyboard summoning, and jump navigation.

Changes

Plugin host contracts and schemas

Layer / File(s) Summary
Host API 1.16.0 contracts
src/shared/plugins/*, packages/plugin-sdk/src/index.ts, CLAUDE-PLUGINS.md, docs/agent-guides/PLUGIN-DEVELOPMENT.md
Adds sessions:focus, session.activated, panel visibility methods, and PanelSize support.
SDK and contribution updates
src/shared/plugins/contributions.ts, src/shared/plugins/permissions.ts, src/shared/plugins/rpc-protocol.ts, packages/plugin-sdk/package.json
Parses panel sizes with fallback behavior and updates SDK metadata and capability mappings.

Session activation and focus

Layer / File(s) Summary
Activation event flow
src/main/ipc/handlers/persistence.ts, src/__tests__/main/ipc/handlers/persistence.test.ts
Emits debounced, coalesced session.activated events when the active session changes.
Focus host path
src/main/index.ts, src/main/plugins/*, src/main/preload/settings.ts, src/renderer/hooks/session/*
Adds validated session focus, AI-tab selection, renderer focus requests, and sandbox SDK wiring.
Focus validation tests
src/__tests__/main/plugins/*, src/__tests__/shared/plugins/*, src/__tests__/renderer/hooks/*
Tests target validation, permissions, closed schemas, unavailable dependencies, and RPC mapping.

Modal plugin panels

Layer / File(s) Summary
Visibility bridge
src/main/index.ts, src/main/plugins/*, src/main/preload/plugins.ts, src/renderer/global.d.ts
Adds open, close, and toggle panel calls and broadcasts visibility actions.
Global modal mount
src/renderer/App.tsx, src/renderer/components/plugins/*, src/renderer/stores/uiStore.ts
Moves modal panel ownership to an app-level mount backed by transient store state.
Panel rendering and validation
src/renderer/components/Settings/*, src/shared/plugins/contributions.ts, src/renderer/components/plugins/__tests__/*
Supports full-size modal chrome, layer-stack Escape handling, modal-only invocation, contribution invalidation, and visibility synchronization.

Agent Flow overlay

Layer / File(s) Summary
Overlay plugin wiring
examples/plugins/agent-flow/main.js, examples/plugins/agent-flow/plugin.json, examples/plugins/agent-flow/README.md
Tracks focused sessions, adds overlay and jump commands, and configures the full modal panel and keybinding.
Shared-canvas visualization
examples/plugins/agent-flow/panel.html
Renders agent nodes, tool satellites, health badges, usage visuals, focus rings, pan/zoom, reset, clear, and dismissal controls.
Overlay tests
src/__tests__/plugins/*, src/__tests__/renderer/hooks/usePluginKeybindings.test.ts, vitest.config.mts
Tests plugin commands, focus snapshots, rendering, health updates, interactions, keyboard handling, and jsdom execution.

Repository support

Layer / File(s) Summary
Bundled signing artifact ignore rule
.gitignore
Ignores release-time bundled plugin signing artifacts.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested labels: approved

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: converting Agent Flow into a summonable full-window overlay.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread src/main/index.ts
Comment thread src/main/index.ts
@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown

Greptile Summary

Retargets Agent Flow as a summonable full-canvas plugin overlay.

  • Adds plugin APIs for panel visibility, session focusing, and focused-session events.
  • Adds full-size modal panel contributions and an app-level plugin panel mount.
  • Redesigns the bundled Agent Flow panel as a multi-agent canvas with navigation.
  • Updates the SDK, host API version, permissions, documentation, and tests.

Confidence Score: 2/5

The PR is not safe to merge until session focusing updates the live renderer and panel visibility is scoped to the initiating window.

The primary click-to-jump path reports success without changing the visible renderer state, while overlay actions in one window are broadcast and applied to every open window.

Files Needing Attention: src/main/index.ts and src/renderer/components/plugins/PluginModalPanelMount.tsx

Important Files Changed

Filename Overview
src/main/index.ts Adds session focusing and panel visibility wiring, but the focus path bypasses live renderer state and panel actions are broadcast across windows.
src/renderer/components/plugins/PluginModalPanelMount.tsx Adds the app-level panel mount and visibility listener, but applies broadcasts without window scoping.
examples/plugins/agent-flow/main.js Registers overlay and jump commands and tracks focused-session metadata using the new host APIs.
examples/plugins/agent-flow/panel.html Replaces the docked timeline UI with a full-canvas multi-agent graph and click-to-jump interactions.
src/main/plugins/plugin-host-handlers.ts Adds brokered sessions.focus and own-modal panel visibility handlers with capability and ownership checks.
src/shared/plugins/contributions.ts Adds validated default and full panel sizing while preserving older manifest behavior.

Sequence Diagram

sequenceDiagram
    participant W as Initiating Renderer
    participant M as Main Process
    participant P as Plugin Sandbox
    participant R as Renderer Panel Mount
    W->>M: invoke plugin overlay command
    M->>P: invokeCommand
    P->>M: ui.togglePanel(flow)
    M-->>R: plugins:panel-visibility
    R->>R: update openPluginPanelId
    R->>M: click agent invokes jump
    M->>P: invokeCommand(jump)
    P->>M: sessions.focus(sessionId)
    M->>M: update persisted session state
Loading

Reviews (1): Last reviewed commit: "MAESTRO: test the agent-flow overlay sum..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/plugin-sdk/src/index.ts (1)

904-911: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Preserve compatibility for the optional panel size field.

The manifest field is optional and missing values normalize to default, but both exported PanelContribution interfaces now require size. This can break TypeScript consumers and existing fixtures that construct the public shape, despite older manifests remaining valid.

  • packages/plugin-sdk/src/index.ts#L904-L911: make size optional in the published input shape, or split raw and normalized contribution types.
  • src/shared/plugins/contributions.ts#L198-L208: retain an optional input field while keeping the parser's normalized value as default.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/plugin-sdk/src/index.ts` around lines 904 - 911, Preserve the
optional panel size input while normalizing missing values to default: update
PanelContribution in packages/plugin-sdk/src/index.ts at lines 904-911 so size
is optional, and update the contribution type at
src/shared/plugins/contributions.ts lines 198-208 to keep size optional on input
while the parser’s normalized output remains default.
🧹 Nitpick comments (3)
src/main/plugins/plugin-host-handlers.ts (1)

1364-1403: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate panelId validation with ui.panelPost.

The panelId shape check (typeof !== 'string' || trim() === '' || panelId !== trim()) is identical to the one in ui.panelPost a few lines above (around line 1345). Could extract a small requirePanelId(p) helper shared by both.

♻️ Optional dedup
+const requirePanelId = (p: Record<string, unknown>): string => {
+	const panelId = p.panelId;
+	if (typeof panelId !== 'string' || panelId.trim() === '' || panelId !== panelId.trim()) {
+		throw new Error('panelId is required');
+	}
+	return panelId;
+};

Then use const panelId = requirePanelId(p); in both ui.panelPost and makeVisibilityHandler.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/plugins/plugin-host-handlers.ts` around lines 1364 - 1403, Extract
the duplicated panelId validation into a shared requirePanelId helper near the
existing ui.panelPost handling, returning the validated string or throwing the
current error for invalid values. Replace the inline checks in both ui.panelPost
and makeVisibilityHandler with const panelId = requirePanelId(p), preserving all
existing authorization and panel behavior.
examples/plugins/agent-flow/panel.html (1)

515-530: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

postMessage uses '*' as the target origin (static analysis hint). Payload is ids only and the parent is the host frame, so exposure is minimal, but if the host frame's origin is known/stable in the guest, pin it instead of '*'.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/plugins/agent-flow/panel.html` around lines 515 - 530, Update the
invoke function’s parent.postMessage call to use the known, stable host-frame
origin instead of the wildcard '*' target origin, while preserving the existing
commandId and ids-only args payload.

Source: Linters/SAST tools

src/__tests__/plugins/agent-flow-panel.test.ts (1)

34-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Panel listeners accumulate across tests. mountPanel() re-evaluates the inline script every beforeEach, but the script's document/window listeners (Escape keydown, visibilitychange, message) from prior mounts are never removed - only document.body.innerHTML is cleared. So by the last test, several stale handlers still run against detached nodes and post duplicate commands. The current assertions use toContain, so it passes today, but it makes tighter assertions (like the toEqual in the first test) order-dependent and flaky.

Consider capturing the listeners (e.g. patch document.addEventListener/window.addEventListener during mountPanel and remove them in afterEach) so each test starts from a clean guest.

Also applies to: 77-88

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/__tests__/plugins/agent-flow-panel.test.ts` around lines 34 - 46, Update
the mountPanel test helper to track every document/window listener registered by
the evaluated panel script and remove those listeners during afterEach,
restoring any patched addEventListener methods afterward. Ensure each beforeEach
mount starts with no handlers from prior mounts, including keydown,
visibilitychange, and message listeners, while preserving the existing panel
initialization behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/agent-guides/PLUGIN-DEVELOPMENT.md`:
- Line 293: Update the descriptive text for the modal panel size option to
hyphenate the compound modifier, changing “mission-control style” to
“mission-control-style” while leaving the surrounding documentation unchanged.

In `@src/main/index.ts`:
- Around line 1991-2016: Update pluginSessionsFocus and the nearby
pluginTabsFocus flow to emit the session.activated plugin event whenever
plugin-driven focus changes the active session, since directly updating
sessionsStore bypasses the persistence IPC handler. Reuse the existing
pluginEventBus/emitPluginEvent mechanism at this call site and preserve the
current validation and focus-target behavior.

---

Outside diff comments:
In `@packages/plugin-sdk/src/index.ts`:
- Around line 904-911: Preserve the optional panel size input while normalizing
missing values to default: update PanelContribution in
packages/plugin-sdk/src/index.ts at lines 904-911 so size is optional, and
update the contribution type at src/shared/plugins/contributions.ts lines
198-208 to keep size optional on input while the parser’s normalized output
remains default.

---

Nitpick comments:
In `@examples/plugins/agent-flow/panel.html`:
- Around line 515-530: Update the invoke function’s parent.postMessage call to
use the known, stable host-frame origin instead of the wildcard '*' target
origin, while preserving the existing commandId and ids-only args payload.

In `@src/__tests__/plugins/agent-flow-panel.test.ts`:
- Around line 34-46: Update the mountPanel test helper to track every
document/window listener registered by the evaluated panel script and remove
those listeners during afterEach, restoring any patched addEventListener methods
afterward. Ensure each beforeEach mount starts with no handlers from prior
mounts, including keydown, visibilitychange, and message listeners, while
preserving the existing panel initialization behavior.

In `@src/main/plugins/plugin-host-handlers.ts`:
- Around line 1364-1403: Extract the duplicated panelId validation into a shared
requirePanelId helper near the existing ui.panelPost handling, returning the
validated string or throwing the current error for invalid values. Replace the
inline checks in both ui.panelPost and makeVisibilityHandler with const panelId
= requirePanelId(p), preserving all existing authorization and panel behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a0cd7b53-9ec6-416b-8a5b-f56123c46fd2

📥 Commits

Reviewing files that changed from the base of the PR and between be414fe and 2584f5d.

📒 Files selected for processing (37)
  • .gitignore
  • CLAUDE-PLUGINS.md
  • docs/agent-guides/PLUGIN-DEVELOPMENT.md
  • examples/plugins/agent-flow/README.md
  • examples/plugins/agent-flow/main.js
  • examples/plugins/agent-flow/panel.html
  • examples/plugins/agent-flow/plugin.json
  • packages/plugin-sdk/package.json
  • packages/plugin-sdk/src/__tests__/drift.test.ts
  • packages/plugin-sdk/src/index.ts
  • src/__tests__/main/ipc/handlers/persistence.test.ts
  • src/__tests__/main/plugins/plugin-host-handlers.test.ts
  • src/__tests__/plugins/agent-flow-main.test.ts
  • src/__tests__/plugins/agent-flow-panel.test.ts
  • src/__tests__/renderer/hooks/usePluginKeybindings.test.ts
  • src/__tests__/shared/plugins/contributions.test.ts
  • src/__tests__/shared/plugins/events.test.ts
  • src/__tests__/shared/plugins/rpc-protocol.test.ts
  • src/main/index.ts
  • src/main/ipc/handlers/persistence.ts
  • src/main/plugins/plugin-host-handlers.ts
  • src/main/plugins/plugin-sandbox-entry.ts
  • src/main/preload/plugins.ts
  • src/renderer/App.tsx
  • src/renderer/components/Settings/PluginPanelHost.tsx
  • src/renderer/components/Settings/PluginsPanel.tsx
  • src/renderer/components/plugins/PluginModalPanelMount.tsx
  • src/renderer/components/plugins/__tests__/PluginModalPanelMount.test.tsx
  • src/renderer/components/plugins/__tests__/PluginPanelSlot.test.tsx
  • src/renderer/global.d.ts
  • src/renderer/stores/uiStore.ts
  • src/shared/plugins/contributions.ts
  • src/shared/plugins/events.ts
  • src/shared/plugins/host-api.ts
  • src/shared/plugins/permissions.ts
  • src/shared/plugins/rpc-protocol.ts
  • vitest.config.mts

Comment thread docs/agent-guides/PLUGIN-DEVELOPMENT.md Outdated
Comment thread src/main/index.ts
Plugin focus verbs (sessions.focus / tabs.focus) write sessionsStore
directly and never reach the sessions:setActiveSessionId IPC handler where
session.activated is emitted, so plugins subscribed to that event never saw
plugin-driven jumps. Emit the metadata-only event from both plugin focus
paths via a shared helper so subscribers observe plugin-initiated focus, not
just user-driven Left Bar navigation.

Also hyphenate "mission-control-style" in PLUGIN-DEVELOPMENT.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
docs/agent-guides/PLUGIN-DEVELOPMENT.md (1)

293-300: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Show the required host version in the full-panel example.

The documentation requires minHostApi: "1.16.0" for size: "full", but the adjacent example does not show that manifest field. Add a nearby complete manifest example or an explicit declaration snippet.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/agent-guides/PLUGIN-DEVELOPMENT.md` around lines 293 - 300, Update the
full-panel manifest example for “Agent Flow” to include the required minHostApi
field set to "1.16.0". Keep the existing size: "full" example intact otherwise,
so the example explicitly demonstrates the complete requirement.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/agent-guides/PLUGIN-DEVELOPMENT.md`:
- Line 293: Clarify the panel size documentation near the size definition by
explicitly stating that an unknown modal size is an exception to the general
invalid-contribution policy: it reports a manifest error and falls back to
default rather than dropping the panel. Keep the existing behavior and the
surrounding size semantics unchanged.

---

Nitpick comments:
In `@docs/agent-guides/PLUGIN-DEVELOPMENT.md`:
- Around line 293-300: Update the full-panel manifest example for “Agent Flow”
to include the required minHostApi field set to "1.16.0". Keep the existing
size: "full" example intact otherwise, so the example explicitly demonstrates
the complete requirement.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 55cb616b-b640-4e9b-81ec-23df38a0f56e

📥 Commits

Reviewing files that changed from the base of the PR and between 2584f5d and acf74c6.

📒 Files selected for processing (2)
  • docs/agent-guides/PLUGIN-DEVELOPMENT.md
  • src/main/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/index.ts

Comment thread docs/agent-guides/PLUGIN-DEVELOPMENT.md Outdated
Note that an invalid panel `size` falls back to `default` and keeps the
panel, rather than being dropped like a general bad contribution.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@pedramamini

Copy link
Copy Markdown
Collaborator

@chr1syy - thanks for this, and thank you for the contribution. The overlay redesign is a real improvement over the docked panel, and the write-up quality here (playbook link, design plan, explicit re-signing note, the reasoning comments in pluginAiFocusFields and flushSessionActivated) made this a genuinely pleasant PR to review. CI is green on both matrix legs and there are no merge conflicts.

I went through the bot findings and your responses independently. Most are settled. Two items I'd like resolved before this merges.


1. sessions.focus does not move the visible workspace

I want to push back gently on the scoping call in your reply to Greptile's P1.

You're factually right that the pre-existing verbs write sessionsStore the same way: pluginTabsFocus, pluginSessionsCreate, pluginSessionsUpdate and pluginTabsClose all do this on rc today, and following the established pattern rather than inventing a second one is the right instinct. But I traced the read side and there is no path back:

  • The renderer's Zustand useSessionStore is canonical, and it reads main's stored state only at startup - sessions.getAll() and sessions.getActiveSessionId() in src/renderer/hooks/session/useSessionRestoration.ts:549,558.
  • It pushes its own tree back down via useDebouncedPersistence.ts:387 (sessions.setAll) and the incremental sessions:persist merge, so the main-side write is not just invisible, it gets clobbered on the next flush for that session.
  • PluginModalPanelMount doesn't trigger any re-hydration, and the jump command in main.js:593 doesn't dismiss the overlay either.

Net effect: a user clicks a node in the overlay, sessions.focus returns true, and nothing visible happens - the overlay stays up and the workspace behind it is still on the old agent. The jump lands only after an app restart, assuming persistence hasn't overwritten it first.

The difference from the pre-existing verbs is that this PR ships a user-facing feature whose entire purpose is the jump ("click a node to jump to that session" in both the PR body and the plugin README). Consistency with a pattern that doesn't reach the renderer still leaves the headline interaction non-functional.

I don't think this needs the full cross-cutting main -> renderer session sync channel you (correctly) called out as out of scope. A targeted version should be small: safeSend a sessions:focus-request alongside the store write, and add a small renderer listener that applies it through the canonical helpers - updateSessionWith(sessionId, ...) from sessionStore.ts spread with aiTabFocusFields(tabId) from utils/tabHelpers.ts (the same helper pluginAiFocusFields already mirrors), plus setActiveSessionId. That keeps the store write as the persistence path and makes the live path work, without opening a general sync channel.

If you'd rather not take that on here, the alternative I'd accept is landing the overlay with click-to-jump explicitly documented as non-functional pending that follow-up - but shipping it as a working feature when it isn't is the part I can't sign off on.

2. session.activated dedupe desyncs between the two emit paths

This one is new, introduced by the fix in acf74c6, so neither bot re-checked it.

flushSessionActivated in src/main/ipc/handlers/persistence.ts guards against repeats with a module-local lastEmittedActivatedSessionId. The new emitPluginSessionActivated in src/main/index.ts:1966 emits onto the same pluginEventBus but never updates that variable, so the two paths disagree about what subscribers were last told.

Concretely:

  1. User navigates to agent A in the Left Bar. flushSessionActivated emits A, lastEmittedActivatedSessionId = 'A'.
  2. Agent Flow calls sessions.focus('B'). emitPluginSessionActivated emits B directly. lastEmittedActivatedSessionId is still 'A'.
  3. User switches tabs within A (or clicks A in the Left Bar). sessions:setActiveSessionId('A') fires, flushSessionActivated sees id === lastEmittedActivatedSessionId and suppresses.

Subscribers' last-known focused session is now B while the real one is A, permanently, until focus moves to some third session. That directly degrades this PR's own feature: Agent Flow's focusedSessionId highlight (main.js:60,367) sticks on the wrong node. The reverse ordering just yields a duplicate B event, which is harmless by comparison.

Fix should be small - either have the plugin path record into the same dedupe state (export a noteSessionActivated(id) from the persistence closure, or hoist the emit + dedupe into one shared helper both call), or route the plugin path through the same debounced flush. A unit test covering the "plugin focuses B, user returns to A, A is still emitted" ordering would be worth adding alongside it.


Non-blocking

Cross-window panel broadcast (Greptile's other P1): I agree with your scoping. Plugin panels genuinely aren't window-scoped anywhere today, plugins:panel-data already broadcasts identically, and per-window targeting is a new capability across the whole panel API rather than something this PR introduces. Worth noting for a follow-up though: the app does support multiple windows (window-manager.ts), and a hotkey-summoned full-canvas overlay appearing in every window is a lot more noticeable than a broadcast data post. Not a blocker for this PR.

Things I checked and am happy with:

  • ui.openPanel/closePanel/togglePanel ownership guards in plugin-host-handlers.ts:1364-1402 are tight: own-panels-only resolution, modal-only enforcement, closed schema, panelId trim validation, and registered only when the sink is wired (fail closed).
  • panel.html builds all agent-derived text with textContent/createElementNS throughout - no innerHTML anywhere, so live agent output can't inject into the overlay. Nice.
  • The .gitignore entry for examples/plugins/*/signature.json and the vitest.config.mts jsdom carve-out are both correctly scoped.
  • The 1.14.0 -> 1.16.0 skip to dodge feat: Board task DAG + Agent Profiles (dispatcher on Cue tick, worktree isolation, plugin-bus events, CLI parity) #1272's 1.15.0 is sensible.

Happy to take another look as soon as those two are addressed. And noted on the re-signing requirement for any local or release build.

…ivated dedupe

Blocking review items on PR RunMaestro#1325 (feat/agent-flow):

1. sessions.focus now moves the visible workspace. The verb wrote main's
   sessionsStore, but the renderer's Zustand store is canonical (reads main only
   at startup, then flushes its own tree back down), so the write was invisible
   and clobbered on the next flush. Alongside the store write, pluginSessionsFocus
   now safeSends a `sessions:focus-request` event; a small renderer listener
   (usePluginFocusRequestListener) applies it through the canonical helpers
   (updateSessionWith + aiTabFocusFields + setActiveSessionId). The store write
   stays the persistence path; the event drives the live path. The Agent Flow
   overlay also now dismisses on a successful node jump (ui.closePanel).

2. session.activated dedupe no longer desyncs between the two emit paths.
   flushSessionActivated guards repeats with a module-local last-emitted id, but
   the plugin path (emitPluginSessionActivated) emitted onto the same bus without
   updating it, so after a plugin focus the flush path could wrongly suppress a
   later user navigation back to the prior session. registerPersistenceHandlers
   now returns noteSessionActivated(id); the plugin path records into the shared
   dedupe after emitting.

Tests: renderer listener applies focus via canonical helpers (+ guards unknown/
empty ids, unsubscribes); plugin jump dismisses overlay on success and keeps it
up on rejection; persistence dedupe ordering (plugin focuses B, user returns to
A -> A still emitted).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@chr1syy

chr1syy commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the careful trace, Pedram - both blocking items are addressed in 9f75650, and I took your accepted approach for each rather than reaching for anything broader.

1. sessions.focus now moves the visible workspace

You were right: the store write alone is a dead end. The renderer's Zustand store reads main only at startup (useSessionRestoration.ts:549,558) and flushes its own tree back down (useDebouncedPersistence.ts:387), so the main-side write was both invisible and clobbered on the next flush.

I implemented exactly the targeted version you described, no general sync channel:

  • pluginSessionsFocus still writes sessionsStore (persistence path), and now also safeSends a sessions:focus-request event alongside it.
  • A small renderer listener, usePluginFocusRequestListener (wired in App.tsx), applies the jump through the canonical helpers: updateSessionWith(sessionId, s => ({ ...s, ...aiTabFocusFields(tabId) })) plus setActiveSessionId - the same aiTabFocusFields the main side already mirrors in pluginAiFocusFields. It guards against an unknown/empty id so a stale request can't hijack focus.
  • The overlay now dismisses on jump: the main.js jump command chains off the sessions.focus promise and calls ui.closePanel('flow') only on a successful focus (a rejected/false jump leaves the graph up).

2. session.activated dedupe no longer desyncs

registerPersistenceHandlers now returns noteSessionActivated(id), which records into the same module-local lastEmittedActivatedSessionId. emitPluginSessionActivated calls it right after emitting, so both paths share one last-emitted id. Your exact scenario (user on A, plugin focuses B, user returns to A) now re-emits A instead of suppressing it, so subscribers - and Agent Flow's own focusedSessionId highlight - no longer stick on B.

Tests (scoped)

  • usePluginFocusRequestListener.test.tsx: focus-request applies via updateSessionWith + aiTabFocusFields + setActiveSessionId; ignores unknown/empty ids; unsubscribes on unmount.
  • agent-flow-main.test.ts: jump dismisses the overlay on a successful focus, and leaves it up when focus resolves false.
  • persistence.test.ts: the "plugin focuses B, user returns to A -> A still emitted" ordering you called out.

Type-check (main + lint tsconfigs), eslint, and prettier are clean on the changed files.

Re-signing

examples/plugins/agent-flow/main.js changed (the jump handler now closes the overlay), so the bundled plugin needs a re-sign before release. I have not touched signature.json.

The non-blocking cross-window panel scoping is left as-is per your note.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/ipc/handlers/persistence.ts`:
- Around line 183-186: Update noteSessionActivated to clear the pending
activation timer and pending session ID before recording the successfully
emitted session, so any debounced activation for an earlier session cannot emit
afterward. Add a regression test covering pending activation A followed by
direct activation B and verify subscribers remain on B.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 68fd6b13-1238-4a20-8949-d1d318464d5d

📥 Commits

Reviewing files that changed from the base of the PR and between 7adc409 and 9f75650.

📒 Files selected for processing (10)
  • examples/plugins/agent-flow/main.js
  • src/__tests__/main/ipc/handlers/persistence.test.ts
  • src/__tests__/plugins/agent-flow-main.test.ts
  • src/__tests__/renderer/hooks/usePluginFocusRequestListener.test.tsx
  • src/main/index.ts
  • src/main/ipc/handlers/persistence.ts
  • src/main/preload/settings.ts
  • src/renderer/App.tsx
  • src/renderer/global.d.ts
  • src/renderer/hooks/session/usePluginFocusRequestListener.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/renderer/App.tsx
  • src/main/index.ts
  • src/tests/plugins/agent-flow-main.test.ts
  • examples/plugins/agent-flow/main.js

Comment thread src/main/ipc/handlers/persistence.ts
CodeRabbit Major follow-up on PR RunMaestro#1325. noteSessionActivated recorded the direct
plugin focus id into the shared dedupe but did not cancel an already-armed
debounced flush. A pending activation for a DIFFERENT session (user navigates to
A, its 100ms timer still armed) could fire AFTER the plugin directly emitted B,
re-announcing A and leaving subscribers on the wrong active session - the same
desync class, via the timer path.

Fix: noteSessionActivated now also clears pendingActivatedSessionId and cancels
sessionActivatedTimer, so a direct emit is authoritative and no stale queued
flush can fire afterward. Added a unit test: A flush pending, plugin focuses B
-> A is never emitted after B, and a later genuine navigation to A still emits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@chr1syy
chr1syy merged commit cf76169 into RunMaestro:rc Jul 29, 2026
6 checks passed
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.

2 participants