@ferris1225/pi-subagents 4.1.20 → 4.1.21
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 +9 -10
- package/package.json +1 -1
- package/src/dispatch.ts +8 -6
- package/src/tools.ts +6 -58
package/README.md
CHANGED
|
@@ -184,13 +184,12 @@ that delivers without another gate.
|
|
|
184
184
|
where they are in `~/.pi/agent/pi-subagents-recovery.json`. Every later session
|
|
185
185
|
start repeats that notice until you remove the artifacts.
|
|
186
186
|
|
|
187
|
-
## Threads:
|
|
187
|
+
## Threads: resume, stop
|
|
188
188
|
|
|
189
189
|
Every dispatch returns a stable `#id`, which is the handle for the thread tools:
|
|
190
190
|
|
|
191
191
|
| Tool | What it does |
|
|
192
192
|
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
193
|
-
| `subagent_wait` | Block in-turn until already-dispatched run(s) settle and return their results (`id` or prefix; omit for all active runs). |
|
|
194
193
|
| `subagent_control` | `resume` a parked or settled thread with its full retained context, optionally appending a new `objective`. |
|
|
195
194
|
| `subagent_stop` | Destructively cancel, deliver the partial output, and retire the thread. Steering and follow-up messages still queued in the child are dropped so nothing can revive it later. |
|
|
196
195
|
|
|
@@ -198,14 +197,14 @@ Every dispatch returns a stable `#id`, which is the handle for the thread tools:
|
|
|
198
197
|
subagent_control({ action: "resume", id: 7, objective: "Finish the tests." });
|
|
199
198
|
```
|
|
200
199
|
|
|
201
|
-
There is deliberately no status or
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
timer
|
|
208
|
-
settles, a parked run answers immediately with its resume handle, and
|
|
200
|
+
There is deliberately no status, polling, or wait tool. Every result delivers
|
|
201
|
+
itself as a completion that wakes the main model, so a turn never blocks on a
|
|
202
|
+
running subagent — keep working or end the turn, and the completion continues
|
|
203
|
+
it. The one in-turn block is `wait: true` on a dispatch, which holds that call
|
|
204
|
+
until the runs it started settle: the escape hatch for one-shot `pi -p`
|
|
205
|
+
parents, which exit at end of turn and would otherwise never see them. That
|
|
206
|
+
wait runs on no timer and no timeout the model picks: it resolves the instant
|
|
207
|
+
its run settles, a parked run answers immediately with its resume handle, and
|
|
209
208
|
aborting the turn is the escape hatch. Control operations are all bounded, so
|
|
210
209
|
they never hang on a generation that is still settling.
|
|
211
210
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ferris1225/pi-subagents",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.21",
|
|
4
4
|
"description": "A managed sub-agent team for pi: specialized roles, pre-commit documentation sync, retained threads, auto-fix chains, model fallback, and Git worktree isolation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/dispatch.ts
CHANGED
|
@@ -151,12 +151,14 @@ function withReviewerFixStageAgent(agent: AgentConfig): AgentConfig {
|
|
|
151
151
|
};
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
/**
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
154
|
+
/** In-turn wait behind dispatch `wait: true` — the escape hatch for one-shot
|
|
155
|
+
* `pi -p` parents that exit at end of turn: hold the call until every run it
|
|
156
|
+
* started settles, then hand back their result blocks. Interactive sessions
|
|
157
|
+
* never take this path; their results arrive as completion wake-ups. No
|
|
158
|
+
* timer: a waiter resolves the moment its run's result registers (children
|
|
159
|
+
* are bounded by the idle watchdog), an already-parked run answers
|
|
160
|
+
* immediately with its resume handle, and the turn's abort signal remains the
|
|
161
|
+
* escape hatch. */
|
|
160
162
|
export async function awaitRunResults(
|
|
161
163
|
runtime: SubagentRuntime,
|
|
162
164
|
runIds: number[],
|
package/src/tools.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Thread controls around the subagent runtime: subagent_control (resume)
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* resolve the moment a run settles, never on a timer.
|
|
2
|
+
* Thread controls around the subagent runtime: subagent_control (resume) and
|
|
3
|
+
* destructive subagent_stop. There is no status/poll tool — completions carry
|
|
4
|
+
* each result (with an on-disk artifact when truncated) and wake the main
|
|
5
|
+
* model, so waiting is never a tool call; the only in-turn block is `wait:
|
|
6
|
+
* true` on a dispatch, for one-shot parents that exit at end of turn.
|
|
8
7
|
*/
|
|
9
8
|
|
|
10
9
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
@@ -13,11 +12,10 @@ import { Text } from "@earendil-works/pi-tui";
|
|
|
13
12
|
import { existsSync } from "node:fs";
|
|
14
13
|
import { Type } from "typebox";
|
|
15
14
|
import { DEFAULT_MAX_RESULT_LINES, loadConfig } from "./config.ts";
|
|
16
|
-
import { awaitRunResults } from "./dispatch.ts";
|
|
17
15
|
import { removeThreadRecord } from "./durable.ts";
|
|
18
16
|
import { formatCompletionBlock, matchRunIds } from "./format.ts";
|
|
19
17
|
import { emptyUsage } from "./rpc-run.ts";
|
|
20
|
-
import { formatTaskSummary,
|
|
18
|
+
import { formatTaskSummary, monitor } from "./monitor.ts";
|
|
21
19
|
import { persistRecoveryRecords, recoveryRecordFromFinalization } from "./recovery.ts";
|
|
22
20
|
import type { SubagentRuntime, SubagentThread } from "./runtime.ts";
|
|
23
21
|
import { CONTROL_QUIESCE_TIMEOUT_MS, projectResultsRoot, quiesced } from "./thread-lifecycle.ts";
|
|
@@ -112,56 +110,6 @@ export function registerLookupTools(pi: ExtensionAPI, runtime: SubagentRuntime):
|
|
|
112
110
|
},
|
|
113
111
|
});
|
|
114
112
|
|
|
115
|
-
const SubagentWaitParams = Type.Object({
|
|
116
|
-
id: Type.Optional(
|
|
117
|
-
Type.String({ description: "Run id or prefix to wait for (see dispatch output). Omit to wait for every active run." }),
|
|
118
|
-
),
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
pi.registerTool({
|
|
122
|
-
name: "subagent_wait",
|
|
123
|
-
label: "Subagent Wait",
|
|
124
|
-
description:
|
|
125
|
-
"Block until dispatched background run(s) settle, then return their results — resolves the moment a run settles, never on a timer.",
|
|
126
|
-
promptSnippet: "Block until dispatched runs settle; returns results the moment they finish.",
|
|
127
|
-
parameters: SubagentWaitParams,
|
|
128
|
-
|
|
129
|
-
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
130
|
-
await runtime.durableRestore;
|
|
131
|
-
const config = await loadConfig(runtime.configPath);
|
|
132
|
-
const active = monitor.getRuns()
|
|
133
|
-
.filter((run) => isRunActiveStatus(run.status) || run.status === "parked")
|
|
134
|
-
.map((run) => run.id);
|
|
135
|
-
const requested = params.id?.trim();
|
|
136
|
-
// Settled runs stay addressable: a wait issued just after completion
|
|
137
|
-
// returns that result immediately instead of missing the run.
|
|
138
|
-
const targets = requested
|
|
139
|
-
? matchRunIds([...new Set([...active, ...runtime.settledRuns.keys()])], requested)
|
|
140
|
-
: active;
|
|
141
|
-
if (targets.length === 0) {
|
|
142
|
-
const activeList = active.map((id) => `#${id}`).join(", ");
|
|
143
|
-
return {
|
|
144
|
-
content: [{
|
|
145
|
-
type: "text",
|
|
146
|
-
text: requested
|
|
147
|
-
? `No subagent run matches "${requested}".${activeList ? ` Active runs: ${activeList}.` : ""}`
|
|
148
|
-
: "No active subagent runs to wait for.",
|
|
149
|
-
}],
|
|
150
|
-
details: {},
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
const blocks = await awaitRunResults(runtime, targets, signal, config.maxResultLines, ctx.cwd);
|
|
154
|
-
return { content: [{ type: "text", text: blocks }], details: {} };
|
|
155
|
-
},
|
|
156
|
-
|
|
157
|
-
renderCall(args, theme) {
|
|
158
|
-
return new Text(`${theme.fg("toolTitle", theme.bold("subagent_wait "))}${theme.fg("accent", args.id ? `#${args.id}` : "all")}`, 0, 0);
|
|
159
|
-
},
|
|
160
|
-
renderResult(result, _options, theme) {
|
|
161
|
-
return renderFirstLine(result, "subagent_wait ", theme);
|
|
162
|
-
},
|
|
163
|
-
});
|
|
164
|
-
|
|
165
113
|
// Cancel one or more active runs: aborts the queue controller, which
|
|
166
114
|
// terminates the child and delivers an aborted result (with whatever partial
|
|
167
115
|
// output it produced) so the main agent always knows the run stopped.
|