Skip to content

[Bug] session:activated fan-out skips the activation-scope check that session.getActive enforces #1754

Closed

Description

@SeanLiu326

What happened? / 问题描述

#1745 scope-checked the pull side of the session-follow feature, but the push side broadcasts to everyone:

  • Pull: pi.session.getActive() refuses a project-scoped plugin whose plugin is not active in the session's project — apps/desktop/electron/main/plugin-runtime.ts:4479-4488 ("A project-scoped plugin is not part of another project's conversation"); ui.revealView has the same check (:4518-4520).
  • Push: broadcastSessionEvent (apps/desktop/electron/main/services/plugin-services.ts:575-604) treats session:activated (in SESSION_SCOPED_EVENTS, :95) as scoped only in the sense of "skip panel windows", then fans out through plugins.broadcastEvent (plugin-runtime.ts:1997-2015 — an unconditional loop over every loaded plugin) and pluginViews.broadcast. There is no project filter, and the payload carries the other project's sessionId and projectPath.

So a plugin scoped to project A only needs to subscribe to session:activated to receive project B's session identity — information its pull API explicitly refuses to give it. The push/pull double standard contradicts #1745's own commit message ("keep session follow inside scope and ordered under switches").

Steps to reproduce / 复现步骤

  1. Load a plugin whose scope is limited to project A.
  2. Open project B and switch between its sessions.
  3. Observe the plugin process receiving session:activated with B's sessionId/projectPath, while the same plugin's pi.session.getActive() call is rejected.

Expected behavior / 预期行为

The fan-out applies the same activation-scope check the pull side enforces: a plugin receives session:activated only for sessions its scope allows.

Actual behavior / 实际行为

The event fans out unconditionally; project scoping holds for pulls only.

App version / 应用版本

0.21.0-beta.4 (main 589e580; introduced with the #1745 series)

Operating system / 操作系统

All

Extra environment / 其他环境信息

Fix direction: run the same per-plugin scope check used by getActive/revealView when fanning out session:activated (both to plugin processes and docked plugin views).

Activity

  1. muzimu217 commented on Oct 19, 2026

    @muzimu217
    Contributor

    Confirmed the asymmetry on current main (589e5803a).

    Pull side is guarded — apps/desktop/electron/main/plugin-runtime.ts:4482-4488:

    // A project-scoped plugin is not part of another project's conversation, so
    // it does not get to read which session that project has open.
    if (!this.services.isPluginActiveInProject?.(loaded.manifest.id)) {
      throw apiError("PERMISSION_DENIED", "plugin is not active in this project");
    }

    Push side is not — apps/desktop/electron/main/services/plugin-services.ts:573-586:

    plugins.broadcastEvent(event, [payload]);        // every loaded plugin process
    ...
    if (!SESSION_SCOPED_EVENTS.has(event)) {          // panels: skipped for session:activated
      pluginPanels.broadcast(event, payload);
    }
    pluginViews.broadcast(event, payload);            // views: no scope check either

    SESSION_SCOPED_EVENTS only excludes detached ui.panel windows; it does not filter by activation scope. So a project-scoped plugin in project A receives session:activated for a session in project B — precisely the information readActiveSession refuses to give it. The dependency the fix needs is already injected (isPluginActiveInProject is a PluginServicesDependencies member, :116) and sessionProjects (:557-562) already maps session → project path, so the filter can be applied per recipient without new plumbing.

    One design question worth settling while fixing it: pluginViews.broadcast follows the user's selection and a docked view belongs to the current session, so the right predicate is probably "is this plugin active in the project of the session being announced" — not the project of the view's own host window. Getting that backwards would silence the very panels the feature exists for.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions