pi-agent-squad 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,205 @@
1
+ # subagents — generic multi-agent messaging extension
2
+
3
+ > Status: fully implemented and verified end-to-end
4
+ > Design principle: **generic messaging at the bottom layer, identity defined by prompts**
5
+
6
+ ## Install
7
+
8
+ ```bash
9
+ pi install npm:pi-agent-squad
10
+ ```
11
+
12
+ Reload Pi after installation:
13
+
14
+ ```text
15
+ /reload
16
+ ```
17
+
18
+ ## Core design
19
+
20
+ **Bottom layer (generic, no identity concept)**:
21
+ - Two parties communicate: `main` (the main agent) and any subagent.
22
+ - Message primitives: `send_message` / `read_inbox` / `reply_message`.
23
+ - Routing: `to=main` -> inject into the main session; `to=<subagent>` -> forward to its resident process.
24
+ - File channel + polling (no process pipes, no identity required).
25
+
26
+ **Identity (prompt layer)**:
27
+ - `agents/*.md`: defines each subagent's identity, duties, model, tools (e.g. planner/reviewer/actor).
28
+ - `orchestrator.md`: defines the main agent's outcome-oriented Discover → Decide → Execute → Verify workflow (optional injection).
29
+ - The bottom layer never cares who is who — it only delivers messages.
30
+
31
+ ## Messaging tools (shared by all agents)
32
+
33
+ | Tool | Purpose |
34
+ |---|---|
35
+ | `send_message({to, content, wait, timeoutSeconds?})` | Send a message to any target; wait=true blocks for and returns the reply |
36
+ | `read_inbox()` | Read messages others sent you |
37
+ | `reply_message({message_id, content})` | Reply to a received message |
38
+
39
+ `to` is either `main` (the main agent) or any subagent name.
40
+
41
+ ## Features
42
+
43
+ - **Delegation**: `subagent` tool (sync / background `async:true`); background results are injected into the main session when done.
44
+ - **Real-time two-way**: subagent<->main and subagent<->subagent, via file channel + resident RPC process pool.
45
+ - **Non-blocking**: background tasks do not occupy the main session.
46
+ - **Default safety gate**: unless the current user explicitly requests subagent involvement or `/orchestrate` is enabled, the main-agent system prompt forbids subagent delegation.
47
+ - **Bounded execution**: one-shot and resident tasks have configurable timeouts (default 6 hours, maximum 3 days); omit `timeoutSeconds` unless the user explicitly requested a time. Timed-out or crashed resident processes are discarded before the next task.
48
+ - **Reliable messaging**: `send_message(wait=true)` waits for and returns the target's actual reply; `wait=false` remains fire-and-forget.
49
+ - **Deadlock prevention**: synchronous wait cycles such as `main -> planner -> main`, self-messages, and `actor -> reviewer -> actor` are detected and rejected immediately with a recovery hint.
50
+ - **Retryable routing**: requests are marked delivered only after successful injection/routing; transient delivery failures are retried instead of being silently stranded until timeout.
51
+ - **Compact transcript events**: incoming messages and background completion/failure results use transparent, icon-led Markdown renderers instead of the default colored custom-message box or a synthetic user message.
52
+ - **Session isolation**: message channels use the real pi session id instead of a shared `ephemeral` directory.
53
+ - **Running widget**: while subagents are active, a compact widget above the editor shows their names, elapsed times, execution mode, and a shortened task/message summary; it disappears automatically when the last activity finishes.
54
+ - **Session navigation**: Shift+Up/Down selects a running subagent, Enter opens the exact RPC session behind that activity, and Esc returns to main while the subagent keeps running.
55
+
56
+ ## Running subagent widget
57
+
58
+ The TUI-only widget is installed above the editor while at least one subagent is running:
59
+
60
+ ```text
61
+ ⠋ Subagents · 2 running · ⇧+↑/↓ select · Enter open
62
+ › actor [bg] · 12s · Implement the login flow and run tests…
63
+ └ reviewer [msg] · 4s · Review the current changes…
64
+ ```
65
+
66
+ - No suffix: synchronous `subagent` task.
67
+ - `[bg]`: background `subagent(async=true)` task.
68
+ - `[msg]`: resident task started through `send_message` or subagent-to-subagent routing.
69
+ - At most four activities are shown; additional concurrency is summarized as `… +N more`, and the visible window follows the selected activity.
70
+ - Task summaries are dimmed and capped at 40 terminal columns so they do not dominate the widget.
71
+ - The title includes dim keyboard hints. Before selection it shows
72
+ `⇧+↑/↓ select · Enter open`; with an active selection it changes to
73
+ `⇧+↑/↓ move · Enter open · Esc clear`. Narrow terminals progressively shorten
74
+ the hint and hide it when the title itself needs the space.
75
+ - The elapsed time and spinner refresh once per second while work is active, avoiding hot-loop rerenders on very large sessions.
76
+ - Normal completion, failure, timeout, cancellation, crash, and session shutdown all remove the matching activity. The widget itself is removed when no activities remain.
77
+ - JSON/RPC/print modes do not install the widget.
78
+
79
+ ### Keyboard navigation
80
+
81
+ - `Shift+Down`: select the first activity, then move down (wraps).
82
+ - `Shift+Up`: select the last activity, then move up (wraps).
83
+ - `Enter`: open the selected subagent session.
84
+ - `Esc`: clear widget selection.
85
+ - Without a selected activity, normal Enter/arrow input remains owned by the main editor.
86
+ - Duplicate Shift+arrow reports arriving within 150ms are coalesced, preventing terminals that emit the same physical keypress more than once from skipping activities.
87
+
88
+ The session view is a focused overlay connected to the exact process/session
89
+ that is running the selected task—not a new conversation with another copy of
90
+ the same agent.
91
+
92
+ The overlay uses 96% of the terminal width and 85% of its height (with no outer margin).
93
+ Its transcript viewport grows with terminal height instead of being fixed to a
94
+ small number of rows.
95
+
96
+ ```text
97
+ ╭──────────────────────── actor session · working… ────────────────────────╮
98
+ │ Initial delegated task │
99
+ │ │
100
+ │ ⠋ tool calling... │
101
+ │ └ ⠋ bash npm test (4.2s) │
102
+ │ │
103
+ │ Working on the implementation… │
104
+ │ │
105
+ │ › additional instruction │
106
+ │ Enter send · Esc main · Ctrl+O expand · PgUp/PgDn scroll │
107
+ ╰────────────────────────────────────────────────────────────────────────────╯
108
+ ```
109
+
110
+ - Enter sends a prompt when idle or a steering message while streaming.
111
+ - Esc closes only the overlay and returns focus to main; the subagent continues.
112
+ - Ctrl+X aborts the selected subagent's current operation.
113
+ - Ctrl+O expands/collapses every compact thinking/tool block.
114
+ - PageUp/PageDown scroll the transcript; End returns to live output.
115
+ - History is loaded on entry and early streaming events are buffered to avoid a startup race.
116
+ - Tool calls show their arguments, streaming output, and final `[done]`/`[error]` status. Accumulated progress updates are converted to new output only, so previously shown lines are not duplicated.
117
+ - User and assistant messages use pi's normal Markdown styling. Assistant fenced code blocks use compact-mode's bordered syntax-highlighted rendering.
118
+ - Thinking and consecutive tool calls are merged into isolated compact-mode blocks with the same rails, colors, token estimates, collapsed limits, expanded result previews, and visible-text boundaries as the main transcript.
119
+ - If Enter is pressed before the RPC process is ready, the requested session opens automatically as soon as its handle becomes available.
120
+
121
+ ## Main transcript message rendering
122
+
123
+ Subagent-originated events use transparent backgrounds with one column of left
124
+ padding. Protocol metadata stays in the underlying model message but is hidden
125
+ from the visible transcript:
126
+
127
+ ```text
128
+ ← reviewer [msg] • reply requested
129
+ │ Please verify the cancellation behavior.
130
+ └ Include the timeout recovery case.
131
+
132
+ ✓ actor [bg] • completed · 42s
133
+ │ Implemented and tested the requested changes.
134
+ └ All checks pass.
135
+
136
+ ✗ planner [bg] • failed · 10s
137
+ └ Subagent timed out after 10 seconds.
138
+ ```
139
+
140
+ - `←` marks a message entering the main session.
141
+ - `✓` and `✗` mark background completion and failure.
142
+ - A dim `│` rail marks every body row and changes to `└` on the final row,
143
+ making each subagent event's exact transcript range immediately visible.
144
+ - Agent names are emphasized; `[msg]` / `[bg]`, status, and elapsed time remain compact.
145
+ - Message bodies render as Markdown and reuse compact-mode's bordered,
146
+ syntax-highlighted fenced-code style.
147
+ - Visible rows omit message IDs, routing paths, run IDs, and
148
+ `reply_message` protocol instructions.
149
+ - Fire-and-forget messages do not ask the main agent to call `reply_message`;
150
+ their request files are removed immediately after successful injection.
151
+ - Background results are delivered as custom messages with
152
+ `triggerTurn: true` and `deliverAs: "steer"`, preserving the former
153
+ synthetic-user-message behavior without inheriting `userMessageBg`.
154
+
155
+ ## Architecture
156
+
157
+ ```
158
+ Main agent (primary outcome owner; optional specialist workflow defined by prompt)
159
+ |
160
+ |-- subagent tool (sync/background spawns an RPC-backed run session)
161
+ | `-- widget selection / interactive overlay attach to that exact session
162
+ |-- RPC resident process pool (receives inter-subagent messages)
163
+ |-- message router (500ms poll)
164
+ | |-- to=main -> inject into main session -> reply_message replies
165
+ | |-- to=subagent -> route to its resident process -> reply written back
166
+ |
167
+ Subagents (separate processes, child mode):
168
+ |-- send_message / read_inbox / reply_message tools
169
+ |-- file channel: /tmp/pi-subagents-messages/<session>/<run>/<agent>/<idx>/{requests,replies}/
170
+ ```
171
+
172
+ ## Layout
173
+
174
+ ```
175
+ subagents/
176
+ |-- package.json
177
+ |-- index.ts # main (subagent tool + message router + background) / child (messaging tools)
178
+ |-- agents.ts # agent discovery (frontmatter parsing)
179
+ |-- agents/*.md # subagent identity definitions (planner / reviewer / actor)
180
+ |-- spawn.ts # RPC-backed interactive runs + legacy JSON one-shot compatibility
181
+ |-- pool.ts # RPC resident process pool (inter-subagent message routing)
182
+ |-- message.ts # generic messaging (file channel + send/reply/read + main-side router)
183
+ |-- session.ts # common interactive session-handle interface
184
+ |-- session-ui.ts # focused overlay for live transcript + interactive input
185
+ |-- orchestrator.md # main-agent outcome/workflow prompt (optional injection)
186
+ `-- README.md
187
+ ```
188
+
189
+ ## Usage
190
+
191
+ ```bash
192
+ # optional: inject the main-agent specialist-workflow identity
193
+ pi --append-system-prompt ~/.pi/agent/extensions/subagents/orchestrator.md
194
+
195
+ # or in-session
196
+ /orchestrate # enable orchestrator mode (injects the triage identity every turn)
197
+ /orchestrate off # disable
198
+ /orchestrate status # check state
199
+
200
+ # in conversation
201
+ "Resolve this architecture decision, then implement it" # planner is used when a Decision Brief is needed
202
+ "Use subagent async=true, agent=actor, task=..." # explicit background delegation
203
+ "Use subagent agent=reviewer timeoutSeconds=120 ..." # override the default 6h task timeout (only when the user asked)
204
+ "Have reviewer review the recent changes" # main agent delegates to reviewer
205
+ ```
@@ -0,0 +1,51 @@
1
+ ---
2
+ thinking: max
3
+ name: actor
4
+ description: Implement a clear Task Brief or Decision Record and verify the resulting change
5
+ tools: read, write, edit, bash, grep, find, ls
6
+ model: opencode-go-responses/deepseek-v4-flash
7
+ ---
8
+
9
+ You are the implementation specialist.
10
+
11
+ You receive either a direct Task Brief from main or an implementation outline
12
+ derived from a design decision. A separate planner result is optional. Form the
13
+ local execution steps needed to complete the brief, then implement and verify
14
+ the change.
15
+
16
+ ## Task Brief
17
+
18
+ Use the supplied brief as the working contract:
19
+
20
+ - Desired outcome
21
+ - Relevant subsystem or files
22
+ - Required behavior
23
+ - Constraints
24
+ - Verification criteria
25
+
26
+ ## Responsibilities
27
+
28
+ 1. Inspect the relevant code and translate the brief into concrete local steps.
29
+ 2. Implement the requested behavior with focused changes.
30
+ 3. Verify the result using the supplied criteria and appropriate tests.
31
+ 4. Report blockers or newly discovered decisions with the evidence that exposed them.
32
+
33
+ When a consequential decision remains unresolved, return a concise Decision
34
+ Brief candidate to main. When the direction is clear, make the local
35
+ implementation judgment needed to complete the task.
36
+
37
+ ## Implementation quality
38
+
39
+ - Preserve project conventions and existing boundaries.
40
+ - Keep changes small, direct, and relevant to the requested outcome.
41
+ - Use proportionate handling for real edge cases and failure modes.
42
+ - Read back changed files and run relevant verification.
43
+
44
+ ## Output
45
+
46
+ Report:
47
+
48
+ - Files changed
49
+ - Per-file summary
50
+ - Verification performed and results
51
+ - Remaining blockers, decisions, or assumptions
@@ -0,0 +1,57 @@
1
+ ---
2
+ thinking: xhigh
3
+ name: planner
4
+ description: Resolve a concrete design decision from a Decision Brief and produce an actor-ready decision record
5
+ tools: read, bash, grep, find, ls
6
+ model: mvp-anthropic/glm-5.3
7
+ ---
8
+
9
+ You are the design-decision specialist.
10
+
11
+ You receive a Decision Brief describing a consequential implementation choice
12
+ that remains unresolved after initial discovery. Your distinct contribution is
13
+ to resolve that choice and make the downstream implementation direction clear.
14
+
15
+ ## Decision Brief
16
+
17
+ Use the supplied brief as the working contract:
18
+
19
+ - Decision to make
20
+ - Why the decision matters
21
+ - Known facts and evidence
22
+ - Constraints
23
+ - Candidate approaches or unresolved boundary
24
+ - Downstream use by the executor
25
+
26
+ ## Responsibilities
27
+
28
+ 1. Validate the relevant facts and constraints using read-only inspection.
29
+ 2. Compare the viable approaches against those constraints.
30
+ 3. Select a direction and explain why it best serves the requested outcome.
31
+ 4. Identify consequences for interfaces, data, migration, compatibility, testing, and rollout where relevant.
32
+ 5. Produce an implementation outline specific enough for actor to execute without guessing.
33
+
34
+ ## Evidence gathering
35
+
36
+ Use `read`, `grep`, `find`, and `ls` for source inspection. Use `bash` for
37
+ read-only evidence gathering such as `git status`, `git diff`, `git log`,
38
+ package metadata, and non-mutating verification commands.
39
+
40
+ When additional evidence is needed, return the smallest concrete evidence
41
+ request in the final result so main can gather it and update the Decision
42
+ Brief.
43
+
44
+ ## Output
45
+
46
+ Return structured Markdown with:
47
+
48
+ 1. **Decision**
49
+ 2. **Rationale**
50
+ 3. **Evidence**
51
+ 4. **Alternatives considered**
52
+ 5. **Consequences and risks**
53
+ 6. **Actor-ready implementation outline**
54
+ 7. **Verification strategy**
55
+ 8. **Assumptions to confirm**
56
+
57
+ End with a one-line `Decision complete` summary.
@@ -0,0 +1,40 @@
1
+ ---
2
+ thinking: xhigh
3
+ name: reviewer
4
+ description: Independently verify completed work against the user outcome, brief, code, and test evidence
5
+ tools: read, grep, find, ls, bash
6
+ model: mvp-openai/gpt-5.6-sol
7
+ ---
8
+
9
+ You are the independent verification specialist.
10
+
11
+ Evaluate completed work against the user's requested outcome and the supplied
12
+ Verification Brief. A planner document is optional; when a Decision Record
13
+ exists, verify that the implementation preserves it.
14
+
15
+ ## Verification Brief
16
+
17
+ Use the supplied evidence:
18
+
19
+ - User outcome
20
+ - Task Brief
21
+ - Decision Record, when one exists
22
+ - Relevant diff or files
23
+ - Verification already performed
24
+
25
+ ## Responsibilities
26
+
27
+ 1. Establish the intended behavior from the outcome and brief.
28
+ 2. Inspect the actual changes and relevant surrounding code.
29
+ 3. Check correctness, requirement coverage, regressions, edge cases, and project conventions.
30
+ 4. Run proportionate read-only tests or diagnostics when they add evidence.
31
+ 5. Distinguish concrete defects from optional improvements.
32
+
33
+ ## Output
34
+
35
+ Return one of:
36
+
37
+ - `Approved` with a concise evidence-based reason.
38
+ - `Rejected` with a prioritized issue list containing file/line, impact, evidence, and a concrete correction.
39
+
40
+ Focus review depth on the risk and consequence of the completed change.
package/agents.ts ADDED
@@ -0,0 +1,87 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ export interface AgentConfig {
6
+ name: string;
7
+ description: string;
8
+ tools?: string[];
9
+ model?: string;
10
+ thinking?: string;
11
+ systemPrompt: string;
12
+ source: string;
13
+ }
14
+
15
+ /** Parse YAML frontmatter + markdown body */
16
+ function parseFrontmatter(
17
+ content: string,
18
+ ): { frontmatter: Record<string, string>; body: string } {
19
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
20
+ if (!match) return { frontmatter: {}, body: content };
21
+ const raw = match[1] ?? "";
22
+ const body = match[2] ?? "";
23
+ const frontmatter: Record<string, string> = {};
24
+ for (const line of raw.split("\n")) {
25
+ const idx = line.indexOf(":");
26
+ if (idx <= 0) continue;
27
+ const key = line.slice(0, idx).trim();
28
+ const value = line.slice(idx + 1).trim().replace(/^["']|["']$/g, "");
29
+ if (key) frontmatter[key] = value;
30
+ }
31
+ return { frontmatter, body: body.trim() };
32
+ }
33
+
34
+ function loadAgentsFromDir(dir: string): AgentConfig[] {
35
+ if (!fs.existsSync(dir)) return [];
36
+ const agents: AgentConfig[] = [];
37
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
38
+ if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
39
+ const filePath = path.join(dir, entry.name);
40
+ const content = fs.readFileSync(filePath, "utf-8");
41
+ const { frontmatter, body } = parseFrontmatter(content);
42
+ if (!frontmatter.name || !frontmatter.description) continue;
43
+ const tools = frontmatter.tools
44
+ ?.split(",")
45
+ .map((t) => t.trim())
46
+ .filter(Boolean);
47
+ agents.push({
48
+ name: frontmatter.name,
49
+ description: frontmatter.description,
50
+ tools: tools && tools.length > 0 ? tools : undefined,
51
+ model: frontmatter.model || undefined,
52
+ thinking: frontmatter.thinking || undefined,
53
+ systemPrompt: body,
54
+ source: dir,
55
+ });
56
+ }
57
+ return agents;
58
+ }
59
+
60
+ /** Built-in agent directory (the extension's own agents/) */
61
+ function builtinAgentsDir(): string {
62
+ const here = path.dirname(fileURLToPath(import.meta.url));
63
+ return path.join(here, "agents");
64
+ }
65
+
66
+ /** User-level agent directory ~/.pi/agent/agents */
67
+ export function userAgentsDir(home = process.env.HOME ?? ""): string {
68
+ return path.join(home, ".pi", "agent", "agents");
69
+ }
70
+
71
+ /**
72
+ * Discover agents: built-in agents/ + user-level ~/.pi/agent/agents.
73
+ * A user-level agent with the same name overrides the built-in one.
74
+ */
75
+ export function discoverAgents(cwd?: string): AgentConfig[] {
76
+ const dirs = [builtinAgentsDir()];
77
+ if (cwd) {
78
+ // project-level .pi/agents is intentionally not enabled (safety)
79
+ }
80
+ const byName = new Map<string, AgentConfig>();
81
+ for (const dir of dirs) {
82
+ for (const agent of loadAgentsFromDir(dir)) {
83
+ byName.set(agent.name, agent);
84
+ }
85
+ }
86
+ return [...byName.values()];
87
+ }