@arhen/pi-core-subagent 1.3.4 → 1.3.6

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 CHANGED
@@ -5,6 +5,15 @@
5
5
  [![license](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
6
6
  [![pi extension](https://img.shields.io/badge/pi-extension-7c3aed)](https://github.com/earendil-works/pi)
7
7
 
8
+ ## Install
9
+
10
+ Requires the [pi coding agent](https://github.com/earendil-works/pi) — install it first: `npm install -g @earendil-works/pi-coding-agent`.
11
+
12
+ ```sh
13
+ pi install npm:@arhen/pi-core-subagent
14
+ # or locally: pi install /path/to/pi-subagents
15
+ ```
16
+
8
17
  Minimalist pi extension: **fast in-process subagents** with single / parallel / graph modes, background runs, cancellation, intercom (child↔leader) and an agent↔agent mailbox.
9
18
 
10
19
  Built for one job: delegate work to isolated subagents **without bloating the parent context**.
@@ -72,13 +81,6 @@ flowchart TB
72
81
 
73
82
  The dotted arrows are the whole point: a child may burn 200k tokens reading files, and the leader receives only its final answer.
74
83
 
75
- ## Install
76
-
77
- ```sh
78
- pi install npm:@arhen/pi-core-subagent
79
- # or locally: pi install /path/to/pi-subagents
80
- ```
81
-
82
84
  ## Usage — the leader invents the agents
83
85
 
84
86
  Define agents inline per call — never creates or reads agent files. Model resolution: explicit `provider/model-id` (or bare id) via the pi model registry → agent-file `model` → the parent's current model → settings default.
@@ -219,14 +221,13 @@ flowchart LR
219
221
 
220
222
  And the rule that keeps this from becoming ceremony: **zero `needs` anywhere = plain parallel.** No waves, no gates, no graph vocabulary imposed on flat work.
221
223
 
222
- Background + intercom:
224
+ Background (default) + intercom — the run returns a runId immediately; you stay steerable while it works:
223
225
 
224
226
  ```json
225
227
  {
226
228
  "agent": "auditor",
227
229
  "prompt": "You audit dependencies.",
228
230
  "task": "Audit package.json for outdated deps",
229
- "background": true,
230
231
  "allowIntercom": true
231
232
  }
232
233
  ```
@@ -235,7 +236,7 @@ Background + intercom:
235
236
 
236
237
  | Tool | Purpose |
237
238
  |---|---|
238
- | `subagent` | single / `tasks` (parallel or graph via `needs`) / `chain` (`{previous}`); `background:true` fire-and-forget; `allowIntercom:true` enables child talk tools; `notifyPerTask: true` wakes you as each task completes (background runs only; default off) |
239
+ | `subagent` | single / `tasks` (parallel or graph via `needs`) / `chain` (`{previous}`); background is the default (`background:false` for inline result in this turn); `allowIntercom:true` enables child talk tools; `notifyPerTask` (default true) wakes you as each task completes (background runs only) |
239
240
  | `subagent_status` | live per-task snapshot (non-blocking), including each child's session file path |
240
241
  | `subagent_result` | full output of a run or one task |
241
242
  | `await_subagent` | block until a run finishes (optional `timeoutMs`) |
@@ -255,6 +256,11 @@ Background + intercom:
255
256
  | `send_agent_message` | message to a sibling subagent's mailbox (`to` = its task id, or `"leader"`) |
256
257
  | `poll_agent_messages` | drain this subagent's mailbox |
257
258
 
259
+ ## Commands
260
+
261
+ - `/subagents` — list runs; `/subagents peek` (or `ctrl+shift+a`) — browsable pane
262
+ - `/subagents auto-bg on|off` — toggle background-by-default for subagent calls (persists to `~/.pi/agent/subagents-config.json`; default on). `off` makes calls block until the run finishes, result inline in the same turn. Bare `/subagents auto-bg` shows the current state.
263
+
258
264
  ## Peek — `/subagents peek` or `ctrl+shift+a`
259
265
 
260
266
  Read-only pane over the session's subagents:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arhen/pi-core-subagent",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "type": "module",
5
5
  "description": "pi extension: fast in-process subagents with a dependency-graph scheduler (needs edges gate tasks and carry upstream output into dependent prompts), plus background runs, intercom and agent-to-agent mailbox. Leader defines agents inline.",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
@@ -76,14 +76,22 @@ export default function (pi: ExtensionAPI) {
76
76
  );
77
77
  };
78
78
  pi.registerCommand("subagents", {
79
- description: "List subagent runs. `/subagents peek` opens the browsable pane.",
79
+ description: "List subagent runs. `/subagents peek` opens the browsable pane; `/subagents auto-bg on|off` toggles background-by-default.",
80
80
  handler: async (args, ctx) => {
81
- if (
82
- String(args ?? "")
83
- .trim()
84
- .toLowerCase() === "peek"
85
- )
86
- return openPeek(ctx);
81
+ const arg = String(args ?? "")
82
+ .trim()
83
+ .toLowerCase();
84
+ if (arg === "peek") return openPeek(ctx);
85
+ if (arg === "auto-bg" || arg.startsWith("auto-bg ")) {
86
+ const value = arg.split(/\s+/)[1];
87
+ if (value === "on" || value === "off") {
88
+ const next = manager.setAutoBg(value === "on");
89
+ ctx.ui.notify(`auto-bg ${next ? "on" : "off"} — subagent calls default to ${next ? "background" : "blocking (inline result)"}.`, "info");
90
+ } else {
91
+ ctx.ui.notify(`auto-bg is ${manager.autoBgOn ? "on" : "off"} — use \`/subagents auto-bg on|off\` to change it.`, "info");
92
+ }
93
+ return;
94
+ }
87
95
  const runs = manager.listRuns().slice(0, 10);
88
96
  if (runs.length === 0) {
89
97
  ctx.ui.notify("No subagent runs in this session.", "info");
@@ -120,7 +128,7 @@ export default function (pi: ExtensionAPI) {
120
128
  // ponytail: this string is billed on every request. One example — the graph one —
121
129
  // covers ids, needs, write and Verify; the simpler shapes are subsets of it.
122
130
  description:
123
- 'Run isolated subagents (own context, own session). You invent each agent: name, optional system prompt, toolset (read-only default, write:true to edit). Use `agent`+`task` for one, `tasks` for many. `needs` declares dependency edges: a task waits for its needs and receives their outputs prepended to its prompt. background:true returns immediately; allowIntercom:true lets children talk to you and each other.\n\nsubagent({ tasks: [{ id: "api", agent: "api-mapper", task: "Map API routes" }, { id: "db", agent: "db-mapper", task: "Map DB schema" }, { id: "doc", agent: "writer", needs: ["api", "db"], write: true, task: "Write ARCHITECTURE.md. Verify: test -s ARCHITECTURE.md" }] })',
131
+ 'Run isolated subagents (own context, own session). You invent each agent: name, optional system prompt, toolset (read-only default, write:true to edit). Use `agent`+`task` for one, `tasks` for many. `needs` declares dependency edges: a task waits for its needs and receives their outputs prepended to its prompt. background is the default (returns a runId immediately; toggle via `/subagents auto-bg off`); set background:false when you need the result inline in this turn. allowIntercom:true lets children talk to you and each other.\n\nsubagent({ tasks: [{ id: "api", agent: "api-mapper", task: "Map API routes" }, { id: "db", agent: "db-mapper", task: "Map DB schema" }, { id: "doc", agent: "writer", needs: ["api", "db"], write: true, task: "Write ARCHITECTURE.md. Verify: test -s ARCHITECTURE.md" }] })',
124
132
  promptSnippet: "Define and delegate work to specialized subagents.",
125
133
  promptGuidelines: [
126
134
  "Use subagent when independent review, testing, research, or parallel analysis improves quality.",
@@ -128,13 +136,14 @@ export default function (pi: ExtensionAPI) {
128
136
  "Order comes from `needs`, not from separate calls: give tasks an `id`, list the ids each depends on. Tasks with no unmet needs run in parallel; dependents receive their upstream outputs automatically — do not restate them.",
129
137
  "End each task with a runnable check, e.g. 'Verify: npx tsc --noEmit && bun test'. A subagent's claim of success is not evidence.",
130
138
  "Define each agent yourself: invented name, focused system prompt, and read-only (default) or write:true. Prefer read-only.",
131
- "Use background:true for long work; allowIntercom:true only when a child may need to ask you something.",
139
+ "Prefer blocking (background:false) whenever the run's result is something you must wait for before your next step — do not default to background for work you depend on inline. When a background run is active, settle its pending results and task dependencies (await_subagent / subagent_result, then continue dependent work) before starting unrelated work.",
140
+ "allowIntercom:true only when a child may need to ask you something.",
132
141
  ],
133
142
  parameters: SubagentParams,
134
143
  executionMode: "parallel", // sibling subagent calls run concurrently, not serialized
135
144
  async execute(_toolCallId, params, signal, onUpdate, ctx) {
136
145
  const typed = params as SubagentParamsShape;
137
- if (typed.background) {
146
+ if ((typed.background ?? manager.autoBgOn) && typed.background !== false) {
138
147
  const details = manager.startInBackground(typed, ctx);
139
148
  return {
140
149
  content: [
package/src/manager.ts CHANGED
@@ -200,9 +200,30 @@ export class SubagentManager {
200
200
  private widgetRuns: RunSnapshot[] = [];
201
201
  private eventSeq = 0;
202
202
 
203
+ /** Default for `background` when the agent doesn't say — toggle via `/subagents auto-bg on|off`. */
204
+ private autoBg = true;
205
+
203
206
  turnActivity = false;
204
207
 
205
- constructor(private readonly pi: ExtensionAPI) {}
208
+ constructor(private readonly pi: ExtensionAPI) {
209
+ try {
210
+ const cfg = JSON.parse(readFileSync(join(getAgentDir(), "subagents-config.json"), "utf8"));
211
+ if (typeof cfg.autoBg === "boolean") this.autoBg = cfg.autoBg;
212
+ } catch {
213
+ /* no config yet — default true */
214
+ }
215
+ }
216
+
217
+ /** Flip the background-by-default flag; persists to the agent dir. Returns the new value. */
218
+ setAutoBg(on: boolean): boolean {
219
+ this.autoBg = on;
220
+ void writeFile(join(getAgentDir(), "subagents-config.json"), JSON.stringify({ autoBg: on }, null, 2)).catch(() => {});
221
+ return on;
222
+ }
223
+
224
+ get autoBgOn(): boolean {
225
+ return this.autoBg;
226
+ }
206
227
 
207
228
  /** Any run still has queued/running tasks? */
208
229
  hasActiveRun(): boolean {
@@ -798,9 +819,9 @@ export class SubagentManager {
798
819
  id: newId("run"),
799
820
  mode,
800
821
  status: "queued",
801
- background: Boolean(params.background),
822
+ background: params.background ?? this.autoBg,
802
823
  allowIntercom: Boolean(params.allowIntercom),
803
- notifyPerTask: params.notifyPerTask ?? false,
824
+ notifyPerTask: params.notifyPerTask ?? true,
804
825
  createdAt: Date.now(),
805
826
  concurrency: Math.max(1, Math.min(params.concurrency ?? DEFAULT_CONCURRENCY, MAX_CONCURRENCY)),
806
827
  tasks: inputs.map((input, index) => ({
package/src/schemas.ts CHANGED
@@ -55,12 +55,17 @@ export const SubagentParams = Type.Object({
55
55
  }),
56
56
  ),
57
57
  background: Type.Optional(
58
- Type.Boolean({ description: "Fire-and-forget: return immediately with a runId; you'll be notified on completion" }),
58
+ Type.Boolean({
59
+ description:
60
+ "Fire-and-forget: return immediately with a runId; you'll be notified on completion. Default true — set false when you need the result inline in this turn.",
61
+ default: true,
62
+ }),
59
63
  ),
60
64
  notifyPerTask: Type.Optional(
61
65
  Type.Boolean({
62
66
  description:
63
- "Wake you (queued follow-up turn) as each task completes — background runs only, since blocking runs can't be woken mid-tool. Default false.",
67
+ "Wake you (queued follow-up turn) as each task completes — background runs only, since blocking runs can't be woken mid-tool. Default true.",
68
+ default: true,
64
69
  }),
65
70
  ),
66
71
  allowIntercom: Type.Optional(