Skip to content

fix(pi-plugin): support pi-web multi-session and RPC hosts - #350

Open
elrond298 wants to merge 2 commits into
cortexkit:masterfrom
elrond298:fix/pi-web-compat
Open

fix(pi-plugin): support pi-web multi-session and RPC hosts#350
elrond298 wants to merge 2 commits into
cortexkit:masterfrom
elrond298:fix/pi-web-compat

Conversation

@elrond298

@elrond298 elrond298 commented Aug 21, 2026

Copy link
Copy Markdown

Summary

This PR adapts the Pi plugin for pi-web, where multiple Pi sessions share one persistent Node.js process and commands run through Pi’s RPC mode.

It addresses shared-process session isolation, duplicate startup work, unsafe subagent CLI detection, RPC command feedback, and /ctx-dream failures before the first model turn.

Changes

Session isolation

  • Replace the process-global child-session flag with an AsyncLocalStorage<boolean> marker.
  • Update the marker through the public subagents:child:session-created and subagents:child:disposed events.
  • Suppress initialization only for in-process child subagents.
  • Allow independent sessions in the same pi-web process to initialize normally.
  • Remove child lifecycle listeners during session_shutdown to prevent stale handlers after reloads.

Process-wide startup maintenance

  • Claim deferred startup maintenance once per process.
  • Schedule the legacy-memory identity rekey introduced by database migration v22 only once per process.
  • Schedule the session-to-project mapping backfill only once per process.
  • Load session history only after acquiring the durable backfill lease, avoiding redundant JSONL scans.

Safer subagent CLI detection

  • Reuse process.argv[1] only when it identifies a supported Pi CLI; otherwise use the packaged executable, bundled CLI, or PATH fallback as appropriate.
  • This prevents embedded hosts such as Next.js from accidentally launching another web server when creating a subagent.

RPC command presentation

  • Preserve model-invisible command entries while presenting them through the RPC UI.
  • Show short progress updates through ctx.ui.notify.
  • Show formatted results in modal dialogs through ctx.ui.custom.
  • Apply this behavior to /ctx-status, /ctx-embed, /ctx-recomp, /ctx-session-upgrade, and /ctx-dream.

Dreamer registration

  • Track Dreamer registration ownership per Pi extension instance.
  • Prevent one session’s shutdown from deregistering a same-project sibling.
  • Transfer the active registration to a remaining worktree owner when necessary.
  • Synchronize the current project’s Dreamer registration immediately before a manual /ctx-dream run.
  • This allows /ctx-dream to work before the first before_agent_start event.

Verification

  • bun run --cwd packages/pi-plugin build
  • bun run --cwd packages/pi-plugin test804 passed, 0 failed
  • bun test packages/plugin/src/features/magic-context/session-project-backfill.test.ts9 passed, 0 failed
  • git diff --check

Regression coverage includes:

  • independent same-process sessions versus child-session suppression
  • lifecycle listener cleanup during shutdown
  • process-wide startup maintenance claimed through full runtime initialization
  • lazy backfill lease gating without session-history reads
  • owner-aware Dreamer registration, shutdown isolation, and ownership transfer
  • single-timer Dreamer handoff when remaining owners repeat a worktree directory
  • embedded-host Pi CLI discrimination
  • RPC notification and modal-dialog routing
  • pre-execution /ctx-dream registration synchronization

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Adds pi-web multi-session and RPC support to packages/pi-plugin and stabilizes Dreamer owner handoff. Old behavior suppressed all same‑process inits via a global latch; new behavior suppresses only in‑process child subagents via lifecycle‑scoped AsyncLocalStorage, runs startup maintenance once per process, and avoids re‑running embedded hosts.

  • In‑process child guard: mark child sessions via subagents:child:session-created/subagents:child:disposed on an AsyncLocalStorage context; only marked children no‑op; independent same‑process sessions initialize normally. Unsubscribe lifecycle listeners on session_shutdown.
  • Process‑wide startup tasks: claim once per process. Run v22 legacy‑memory rekey and session→project backfill only once; defer session‑history reads until the backfill lease is acquired.
  • Safer subagent CLI resolution: reuse process.argv[1] only when it is a Pi CLI (@earendil-works/pi-coding-agent/dist/cli.js). Otherwise use the packaged binary, bundled CLI, or pi on PATH. Never spawn with a shell; prevents embedded hosts (e.g., Next.js) from re‑running themselves.
  • RPC command presentation: keep model‑invisible ctx-status entries and present them in RPC. Short progress → ctx.ui.notify; detailed results or rpcDisplay: "dialog" → modal via ctx.ui.custom. Applied to /ctx-status, /ctx-embed, /ctx-recomp, /ctx-session-upgrade, and /ctx-dream. RPC presenter is set only in RPC mode.
  • Dreamer registration ownership: track owners per extension instance and per project. One session’s shutdown no longer deregisters a sibling. On active‑owner exit, rebuild once from the most recent remaining owner (worktree handoff handled even when dirs repeat). registerCtxDreamCommand accepts ensureRegistered to sync before manual runs.

Review notes

  • Verify only marked children are suppressed and that lifecycle listeners are removed on shutdown.
  • Confirm once‑per‑process gating for startup tasks and that session‑history reads occur only after the backfill lease.
  • Check CLI discrimination: Pi hosts reuse their CLI; embedded hosts fall back correctly; no shell spawn.
  • Exercise RPC rendering: notification vs dialog behavior, and rpcDisplay: "dialog" across updated commands.
  • Validate Dreamer owner map and handoff: sibling survival, worktree switch rebuild, and safe unregister when the last owner leaves.

Written for commit 72dc718. Summary will update on new commits.

Review in cubic

Greptile Summary

This PR adapts the Pi plugin for multiple sessions sharing one process and for RPC-based hosts.

  • Replaces global child-session suppression with lifecycle-scoped detection.
  • Coordinates startup maintenance and Dreamer registration across extension instances.
  • Adds RPC notifications and dialogs for command output.
  • Tightens child CLI selection for embedded hosts.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains within the eligible follow-up-review scope.

No blocking failure remains.

Important Files Changed

Filename Overview
packages/pi-plugin/src/index.ts Reworks child-session detection, process-wide maintenance, RPC presentation, Dreamer ownership wiring, and shutdown cleanup for persistent multi-session hosts.
packages/pi-plugin/src/dreamer/index.ts Adds owner-aware project registration and active-owner handoff to preserve Dreamer scheduling across sibling sessions.
packages/pi-plugin/src/subagent-runner.ts Restricts argv-based CLI reuse and introduces packaged, bundled, and PATH fallback resolution for child processes.
packages/pi-plugin/src/commands/pi-command-utils.ts Adds RPC presenters, notification-versus-dialog routing, and a modal status component while retaining model-invisible entries.
packages/pi-plugin/src/commands/ctx-dream.ts Synchronizes Dreamer registration immediately before manual execution and presents completed results through RPC dialogs.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  Host[Persistent Pi or pi-web host] --> SessionA[Extension instance A]
  Host --> SessionB[Extension instance B]
  SessionA --> Shared[Shared process state and SQLite]
  SessionB --> Shared
  SessionA --> OwnerMap[Dreamer owner registry]
  SessionB --> OwnerMap
  SessionA --> RPC[RPC notifications and dialogs]
  SessionB --> RPC
  SessionA --> Child[Resolved Pi child CLI]
  SessionB --> Child
Loading

Reviews (2): Last reviewed commit: "fix(pi-plugin): stabilize Dreamer owner ..." | Re-trigger Greptile

Context used:

Scope in-process child detection to lifecycle AsyncLocalStorage so independent sessions initialize normally, and release lifecycle subscriptions on shutdown. Run startup maintenance once per process and defer session-history reads until the backfill lease is acquired.

Resolve the child Pi CLI independently from embedded host argv, present command output through RPC notifications and dialogs, and ensure Dreamer registration before manual runs.

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 17 files

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

Re-trigger cubic

Comment thread packages/pi-plugin/src/dreamer/index.ts Outdated
ualtinok added a commit that referenced this pull request Aug 21, 2026
…amer registry, session-scoped drains, presenter ctx capture; NOT a security blocker — rpc-server untouched)

Co-Authored-By: Alfonso <alfonso@cortexkit.io>

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 17 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/pi-plugin/src/dreamer/index.ts">

<violation number="1" location="packages/pi-plugin/src/dreamer/index.ts:129">
P2: When a stable owner re-registers after a later worktree change, `Map.set` keeps its original insertion position, so shutdown handoff can select a stale sibling checkout. Delete the owner key before setting it to preserve registration recency.</violation>
</file>

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

Re-trigger cubic


const existing = registeredProjects.get(opts.projectIdentity);
const owners = existing?.owners ?? new Map<object, PiDreamerOptions>();
owners.set(opts.registrationOwner, opts);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: When a stable owner re-registers after a later worktree change, Map.set keeps its original insertion position, so shutdown handoff can select a stale sibling checkout. Delete the owner key before setting it to preserve registration recency.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/pi-plugin/src/dreamer/index.ts, line 129:

<comment>When a stable owner re-registers after a later worktree change, `Map.set` keeps its original insertion position, so shutdown handoff can select a stale sibling checkout. Delete the owner key before setting it to preserve registration recency.</comment>

<file context>
@@ -121,8 +125,12 @@ export function registerPiDreamerProject(opts: PiDreamerOptions): void {
 
 	const existing = registeredProjects.get(opts.projectIdentity);
+	const owners = existing?.owners ?? new Map<object, PiDreamerOptions>();
+	owners.set(opts.registrationOwner, opts);
 	if (existing) {
 		// Same identity, same directory → genuinely already registered, no-op.
</file context>

@alfonso-magic-context alfonso-magic-context left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for this — especially as a first contribution. The diagnosis is right: the #247 process-global latch is what makes the second pi-web session skip Magic Context entirely, and routing child suppression through subagents:child:session-created/disposed plus AsyncLocalStorage is the correct seam. Once-per-process startup maintenance, not reusing a non-Pi argv[1], Dreamer sibling ownership, and keeping ctx-status entries model-invisible while presenting them in RPC are all the right instincts. And noted that you already pushed the Dreamer owner-handoff stabilization mid-review — that resolves one of the items we had flagged, and that kind of responsiveness makes this easy to shepherd.

Two clarifications so we don't talk past each other:

  1. "RPC hosts" here is Pi RPC mode (ctx.ui.notify/ctx.ui.custom). It does not change Magic Context's RPC server, which must stay on 127.0.0.1 with a bearer token. We checked; this PR does not touch that.
  2. The old "second init in the process is a no-op" test should change — that contract is the bug for pi-web. Please keep the child-only skip test (you did).

Before we can merge:

  • Dreamer registeredProjects on globalThis (same jiti moduleCache:false reason as the child marker), so two sessions in one repo don't start two timers.
  • session_shutdown draining only that session's in-flight work — in pi-web, shutdown is not process exit.
  • RPC presentation using the command's live ctx, not a session_start closure.
  • The #177 "never spawn bare pi" test kept alongside the new embedded-host test.
  • packages/pi-plugin/PARITY.md updated for RPC dialogs, the multi-session process model, and the latch → ALS change.

We've approved CI for this PR so your next push gets the full check suite. Really solid work — happy to re-review quickly.

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