Skip to content

epic: replace process relaunch with in-process session switching #17

Description

@neilwashere

Pi can change the working directory of a live session in-process. We never found out, so this extension relaunches the whole pi process through a terminal multiplexer instead.

The mechanism we missed

ctx.switchSession(sessionPath, { withSession }) tears down the current runtime and rebuilds it bound to the target session's cwd:

// pi-coding-agent/dist/core/agent-session-runtime.js:128asyncswitchSession(sessionPath,options){awaitthis.teardownCurrent("resume",sessionManager.getSessionFile());this.apply(awaitthis.createRuntime({cwd: sessionManager.getCwd(),// the whole runtime is rebound here
...
}));awaitthis.finishSessionReplacement(options?.withSession);}

Verified against the dist, not inferred:

  • Tools rebind.createBashToolDefinition(cwd, …) closes over the cwd handed to it at creation. New runtime, new cwd, correct bash/read/edit.
  • Resources reload from the new directory.core/agent-session-services.js:53 rebuilds SettingsManager and DefaultResourceLoader against the new cwd — project extensions, skills, AGENTS.md. Equivalent to a restart.
  • It is on our floor.switchSession and SessionManager.forkFrom both exist in the 0.83.0 copy in node_modules. Our peer dep is already >=0.83.0.
  • All three modes wire it — interactive, print and rpc (modes/*.js). Headless included.

Prior art: @narumitw/pi-worktree does the switch in about 100 lines (src/session.ts): fork the current session into the target path with SessionManager.forkFrom, switchSession to it, report from withSession.

Why it matters

Roughly 1,000–1,500 of our ~5,500 extension lines exist only because we restart the process:

FileLinesFate
extensions/worktree-transport.ts551mux detection, pane-ownership probing, waiter scheduling — relaunch-only
extensions/worktree-handoff.ts435~half is relaunch plumbing (FORK_IF_USABLE, buildRelaunchCommand); caveats and continuations survive
buildVerifiedTeardownScript (in worktree.ts)~150exists only because the process stands in the doomed directory
extensions/worktree-receipt.ts694claim/receipt logic survives; waiter-ownership proving does not

It also retires the outcomes we are least proud of. manual-restart and most of path-target exist because a multiplexer was absent — a constraint that stops applying. Headless runs get a real hop for the first time.

Order of work

  1. spike: prove ctx.switchSession re-camps a live session #18 — prove the switch on the lowest-risk path before committing to anything
  2. feat(enter): switch the session in-process instead of relaunching #19/worktree enter, command-driven and idle
  3. feat(tool): arm the switch in the tool, perform it at agent_settled #20 — the model-callable path, armed at agent_settled
  4. feat(dispose): switch first, then tear down from the main checkout #21 — switch first, tear down second
  5. refactor: retire the relaunch transport, or demote it to an opt-in tab re-camp #22 — retire the relaunch rig, or demote it to an opt-in tab re-camp
  6. feat(dispose): adopt recovery-risk and index-state checks before teardown #23 — port the removal-safety depth from the prior art (independent of the above)

Two risks that must be tested, not assumed

process.cwd() never changes.grep process.chdir across the pi dist returns nothing. Tools take an explicit cwd so they are fine, but after a dispose the process's OS cwd points at a deleted directory, and any bare process.cwd() call then throws ENOENT.

A tool cannot switch its own session. Calling switchSession mid-tool tears down the agent awaiting the result, and waitForIdle() would deadlock on our own execution. The prior art sidesteps this by registering no tool at all; we cannot.

What is not on the table

The switch replaces the transport, not the product. Provisioning hooks, provisioning receipts, concurrency claims, the discipline guard, pi --worktree, conventional-commit branch resolution, the worktree_session tool, and dispose-the-tree-you-are-standing-in all stay. The prior art has none of them.

The truthful outcome contract stays too. Fewer outcomes will be reachable; none may start lying.

Activity

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

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions