@graelo/pi-agent-switch0.2.0
Switch the main pi session between pre-defined agents, so pi-permission-system applies that agent's permission rules
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
pi-agent-switch
Switch the main pi session between pre-defined agents, so
@gotgenes/pi-permission-system applies that agent's permission rules.
Pick an agent and the permission system applies its agent-specific rules to the main session until you pick another. A named agent without agent-specific rules can restore the config-derived policy; see Returning to baseline.
How it works
@gotgenes/pi-permission-system resolves the active agent name in
resolveAgentName (src/session/permission-session.ts), in this order:
getActiveAgentName(ctx)— the most recent session entry withcustomType: "active_agent"getActiveAgentNameFromSystemPrompt()— the<active_agent name="..."/>system-prompt tag- the last name it saw
This extension writes both channels. Switching appends the session entry
(channel 1, which takes precedence) and injects the tag into the system prompt
(channel 2, the documented one, as a fallback). The permission system then
applies that agent's frontmatter over your config.json policy.
Agent policy enforcement is the permission system's. This extension does not interpret permission rules or prompt for individual tool calls. It has one narrow fail-closed guard: if the selected branch's identity cannot be safely reconciled with the permission system, model tool calls are blocked until you select a named agent. This guard is not a replacement for the permission system.
Compatibility
This extension does not depend on a subagent package. With nicobailon's npm
package pi-subagents 0.84.0, native child prompts carry an <active_agent>
tag. This does not guarantee per-agent permission enforcement: the
permission-system extension must be loaded in the child, and it must be able to
find a matching agent policy. In particular, foreground children do not load
ambient extensions automatically, and external runners have their own behavior.
Consult pi-subagents' agent documentation for child extension configuration.
The permission system looks for agent policy files by active name in its own
global and project agent directories, not every location pi-subagents scans.
The parent entry written by this extension is not a substitute for configuring
child permissions.
Caveat: the session-entry channel is undocumented — only the prompt tag appears in the permission system's docs, so the entry is not a public integration API. The regression tests use
@gotgenes/pi-permission-system42.0.0; re-check behavior on upgrade. The prompt tag is a lower-precedence fallback, not a guaranteed replacement.
Install
pi install npm:@graelo/pi-agent-switch
Requires Pi 1.5.0 or later; tested with Pi 1.5.0 and
@gotgenes/pi-permission-system 42.0.0. The permission system must be
installed separately; without it, switching still records the agent but
nothing enforces it.
Agents
This extension reads top-level agent files from these directories, lowest precedence first:
<agent-dir>/agents/*.md<cwd>/.pi/agents/*.md
The switcher uses the filename stem as the agent name. For shared definitions,
use a matching frontmatter name and filename (for example reviewer.md and
name: reviewer), without package qualification. A project definition shadows
a global one in the picker, and enabled: false disables it there. The
permission system composes agent rules from both global and project files;
shadowing in the picker does not suppress global permission rules.
Nicobailon's pi-subagents also discovers nested files, builtins, package
agents, legacy .agents files, and configurable scan roots. Those are not all
available in this switcher or resolvable by the permission system. This
extension only reads definitions; it never writes them.
The project tier is skipped when the project is untrusted. Agent frontmatter must be valid YAML with no duplicate keys. Invalid or unreadable definitions are marked invalid and cannot be selected; an invalid project file still shadows the global file of the same name. If a recorded agent becomes invalid, model tool calls are blocked until a valid agent is selected. Definitions without frontmatter remain supported.
The switcher validates inheritSkills (a YAML boolean) and skills and
skillPath (comma-separated strings or lists of non-empty strings). These
fields determine the main-session skill catalogue as described below.
An agent file contributes agent-specific rules through its permission: block.
The picker marks files without one with [no permission block], and switching
to one warns. Another scope with the same name may still contribute rules:
---
name: reviewer
description: Read-only review
permission:
read: allow
write: deny
edit: deny
bash:
"*": ask
---
You are reviewing, not implementing.
Usage
| Action | Effect |
|---|---|
Alt+Tab (default) |
Cycle through named agents |
/agent |
Open a picker |
/agent <name> |
Switch directly (tab-completes) |
/agent-skills |
Inspect the last run's skill-resolution snapshot |
The status bar shows ⬢ <agent>. A new session may start without a selection,
but there is no clear action: select a named baseline instead. On /tree, the
agent and hint state follow the selected branch. A valid configured default is
used when that branch has no recorded choice.
If a branch has no safely resolvable identity after a previous selection, or
its recorded agent is missing, disabled, or a legacy null entry, the status
shows ⚠ agent unresolved and model tool calls are blocked. Use /agent or
Alt+Tab to select a named agent and resume. A genuinely fresh session with no
selection can use the baseline policy normally. The guard covers model tool
calls, not shell commands entered directly by the user.
Choices survive /resume and /reload. Because the permission system scans
all session entries rather than just the active branch, the switcher records a
reconciliation entry when needed so both extensions resolve the same name.
By default, the next agent run after a switch receives a one-time, hidden
model-facing message naming the selected agent and reminding it to reassess
available tools and permissions. Several switches before a run produce one
hint for the final selection; returning to the original agent produces none.
Switching alone does not start a run. The hint survives reload before delivery
and is stored in the conversation after delivery.
It does not describe or enforce specific permissions. Set switchHint to
false to disable it, independently of applySystemPrompt.
Agent skill selection
For the detailed field, resolution and failure contract, see the skill-selection specification.
Agent definitions can select the model-visible skill catalogue per run:
---
inheritSkills: false
skillPath: [../video-skills]
skills: [manimce-best-practices, explainer-video]
---
inheritSkills defaults to true. false starts with an empty catalogue;
skills adds named selections, and an empty list selects none. skillPath
contains discovery directories or Markdown skill files, resolved relative to
the agent definition, not the session's working directory. It does not add
all skills in those paths. skills and skillPath accept YAML string lists
or comma-separated strings. Paths do not expand shell variables or ~.
Selected private skills take precedence over ambient skills with the same
name; an absent private name falls back to the incoming ambient catalogue.
Invalid roots, ambiguous or broken selected skills, and skills that disable
model invocation are errors. If a private candidate cannot be classified,
selection is unresolved because its name could collide with a selected skill.
This is deliberately stricter than pi-subagents' child selection, which may
fall back after an unreadable local candidate. The switcher checks known
problems before a switch; the incoming ambient catalogue
is available only at run time. If resolution fails then, it keeps the recorded
agent, reports the error, empties the structured catalogue, aborts the run and
blocks model tool calls. Repair the skill or select another agent; the next run
rechecks the files. /agent-skills shows the last resolution, including names,
paths and diagnostics, or reports that it is unresolved. It is a snapshot, not
a live view: switching, reload, file changes, later extensions and forced
prompts can make it stale or inaccurate.
This changes automatic model-facing discovery, not access control or
conversation isolation. Prior messages and instructions remain visible,
filesystem permissions do not change, and Pi's /skill:name commands are not
updated: private skills may lack a command and filtered ambient skills may
remain invocable. Other extensions can change the catalogue afterward, and a
forced system prompt can mask it. Cancellation was verified with Pi 1.5.0's
OpenAI Responses adapter in simulated mode bindings and actual TUI, print, JSON
and RPC entry points; OpenAI Completions in print and JSON; and Anthropic
Messages in print, JSON and RPC. These checks used local endpoints. A provider
that ignores an abort signal may still send a request. A fresh session is the
predictable choice when switching skill ecosystems.
Returning to baseline
Create <agent-dir>/agents/baseline.md with no permission: block and no
prompt body:
---
name: baseline
description: Config-derived permissions
---
Select it with /agent baseline. The permission system then uses its global
and project config-derived policy instead of the previous agent's rules.
Neither the global nor project baseline.md may contribute agent-specific
rules for this to hold; runtime approvals and other permission-system behavior
may still apply. The switcher warns about the missing block, which is expected.
/agent none is not a clear operation. The permission system can retain its
last known agent when given a null entry, so clearing cannot safely promise
baseline permissions. An actual definition named none is just another agent.
Config
Optional, resolved by
@graelo/pi-ext-config:
| Tier | Path |
|---|---|
| Project | <repo-root>/.pi/extensions/pi-agent-switch/config.json |
| Global | <agent-dir>/extensions/pi-agent-switch/config.json |
{
"defaultAgent": "reviewer",
"applySystemPrompt": true,
"switchHint": true,
"shortcut": "alt+tab"
}
| Key | Default | Effect |
|---|---|---|
defaultAgent |
null |
Agent for a branch with no recorded choice; a valid default is recorded when applied. An existing choice wins, including a legacy null entry. |
applySystemPrompt |
true |
Also append the agent's own system prompt (its file body) to the session's. |
switchHint |
true |
Send a one-time model-facing reminder on the next run after a switch. |
shortcut |
"alt+tab" |
Global-only Pi key identifier for cycling agents; set to null to disable the shortcut. |
Loaded with shallow-merge, so a project config can set one key and inherit the
rest. The shortcut is the exception: it is read only from the global config
because Pi registers shortcuts before project trust is available. Run /reload
after changing it. Invalid values disable the shortcut and produce a warning.
Use Pi's key syntax and check /hotkeys for
conflicts; a terminal or multiplexer may also intercept a key.
applySystemPrompt only ever appends. prompt_mode: replace is deliberately
not honoured in the main session: replacing pi's assembled system prompt would
discard its tool instructions along with everything else. Set it to false to
switch permissions only and leave agent body prompting alone; the identity tag
is still added.
Pi renders the agent tag and body in its structured append section, after existing appended text but before project context and skills. Another extension that forces a complete system prompt can mask these additions. An earlier forced prompt produces a warning; a later override cannot be detected here. The session entry remains the primary permission identity channel.
Terminal bindings
Pi does not assign Alt+Tab by default; Shift+Tab cycles thinking level.
On macOS, Alt+Tab is Option+Tab. Terminal and multiplexer key handling varies;
/hotkeys shows whether Pi registered the shortcut, and pressing it verifies
the keystroke reaches Pi. If another application intercepts it, configure a
different shortcut.
Layout
src/index.ts extension entry: events, shortcut, command
src/agents.ts agent discovery, frontmatter parsing
License
MIT