shariq-pi-extensions 0.2.5 → 0.2.7
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/docs/EXTENSIONS.md +2 -2
- package/extensions/background-terminals/README.md +2 -2
- package/extensions/background-terminals/index.ts +24 -29
- package/extensions/shared/README.md +1 -0
- package/extensions/shared/settlement-delivery.ts +19 -0
- package/extensions/subagents/README.md +1 -2
- package/extensions/subagents/index.ts +20 -45
- package/extensions/subagents/src/prompt.ts +2 -1
- package/package.json +1 -1
- package/skills/background-terminals/SKILL.md +3 -1
- package/skills/subagents/SKILL.md +2 -2
- package/extensions/subagents/src/result-delivery.ts +0 -20
package/docs/EXTENSIONS.md
CHANGED
|
@@ -38,7 +38,7 @@ The goal extension adds persistent, branch-safe objectives, progress evidence, b
|
|
|
38
38
|
|
|
39
39
|
The subagent extension runs flat Pi child agents with profiles, capability policies, continuation, result delivery, optional worktrees, and a dashboard. Configuration lives in `<agent-dir>/subagents.json`; trusted projects may override it through their Pi config directory. The configured concurrency ceiling is 50.
|
|
40
40
|
|
|
41
|
-
The extension supplies tools including `spawn_agent`, `task`, `check_agent`, `list_agents`, `wait_agent`, `send_message`, `close_agent`, `reply_question`, and `apply_agent_changes`. Child settlement
|
|
41
|
+
The extension supplies tools including `spawn_agent`, `task`, `check_agent`, `list_agents`, `wait_agent`, `send_message`, `close_agent`, `reply_question`, and `apply_agent_changes`. Child settlement is handed to Pi immediately as an extension-originated user follow-up, so Pi queues it while the parent is active or starts a new parent turn when idle with the summary guaranteed in model context; status tools are for explicit inspection, not waiting.
|
|
42
42
|
|
|
43
43
|
### [Orchestration](../extensions/orchestration/README.md)
|
|
44
44
|
|
|
@@ -50,7 +50,7 @@ The model-facing `create_orchestration` tool starts planning only after an expli
|
|
|
50
50
|
|
|
51
51
|
Managed PTYs support servers, watchers, long builds, downloads, and interactive processes. The extension tracks up to eight concurrent terminals, retains bounded output, stores full logs in restrictive temporary directories, and stops process groups during shutdown or reload.
|
|
52
52
|
|
|
53
|
-
Its tools are `start_terminal`, `read_terminal`, `write_terminal`, `list_terminals`, and `stop_terminal`. A model-started terminal
|
|
53
|
+
Its tools are `start_terminal`, `read_terminal`, `write_terminal`, `list_terminals`, and `stop_terminal`. A model-started terminal immediately hands its completion or failure to Pi as an extension-originated user follow-up, which Pi queues while the parent is active or uses to start a new parent turn when idle with the bounded output guaranteed in model context. Reading a terminal does not suppress that delivery; agents should inspect only for explicit progress requests or immediate interaction.
|
|
54
54
|
|
|
55
55
|
## Web access
|
|
56
56
|
|
|
@@ -10,7 +10,7 @@ Session-scoped background pseudo-terminals for Pi. The extension combines Codex-
|
|
|
10
10
|
- `list_terminals` — list running and settled terminals.
|
|
11
11
|
- `stop_terminal` — stop complete process groups with TERM-to-KILL escalation.
|
|
12
12
|
|
|
13
|
-
Each output response carries a byte cursor. Pass it to the next read/write operation to avoid repeating output. Long or uncertain commands should use `start_terminal` instead of a large blocking `bash` timeout.
|
|
13
|
+
Each output response carries a byte cursor. Pass it to the next read/write operation to avoid repeating output. Long or uncertain commands should use `start_terminal` instead of a large blocking `bash` timeout. Settlement is handed to Pi immediately as an extension-originated user follow-up: it queues while the parent is active or starts a new parent turn when idle with bounded output guaranteed in model context, so the parent can continue other work or end its turn rather than poll.
|
|
14
14
|
|
|
15
15
|
## User interface
|
|
16
16
|
|
|
@@ -34,7 +34,7 @@ Each output response carries a byte cursor. Pass it to the next read/write opera
|
|
|
34
34
|
- Output is sanitized before TUI or model rendering.
|
|
35
35
|
- Processes run in their own PTY process group and are stopped on session shutdown, replacement, or reload.
|
|
36
36
|
- Shutdown and stop operations are bounded and escalate from SIGTERM to SIGKILL.
|
|
37
|
-
- Model-started terminals
|
|
37
|
+
- Model-started terminals immediately hand one model-visible completion/failure follow-up to Pi; Pi queues it while the parent is active or starts the next parent turn when idle.
|
|
38
38
|
- Reading settled output does not consume or suppress the automatic completion delivery.
|
|
39
39
|
- Completion delivery is keyed by terminal id to prevent duplicate follow-ups.
|
|
40
40
|
|
|
@@ -3,12 +3,12 @@ import * as path from "node:path";
|
|
|
3
3
|
import type {
|
|
4
4
|
ExtensionAPI,
|
|
5
5
|
ExtensionCommandContext,
|
|
6
|
-
ExtensionContext,
|
|
7
6
|
ExtensionUIContext,
|
|
8
7
|
} from "@earendil-works/pi-coding-agent";
|
|
9
8
|
import { getMarkdownTheme } from "@earendil-works/pi-coding-agent";
|
|
10
9
|
import { Markdown, Text } from "@earendil-works/pi-tui";
|
|
11
10
|
import { Type } from "typebox";
|
|
11
|
+
import { deliverSettlement } from "../shared/settlement-delivery.ts";
|
|
12
12
|
import { oneLine, sanitizeTerminalText, stateLabel } from "../shared/tui-dashboard.ts";
|
|
13
13
|
import { TerminalManager, MAX_RUNNING_TERMINALS } from "./src/manager.ts";
|
|
14
14
|
import {
|
|
@@ -37,12 +37,12 @@ function resolveCwd(base: string, requested?: string): string {
|
|
|
37
37
|
|
|
38
38
|
export default function backgroundTerminals(pi: ExtensionAPI) {
|
|
39
39
|
let manager: TerminalManager | undefined;
|
|
40
|
-
let sessionContext: ExtensionContext | undefined;
|
|
41
40
|
let ui: ExtensionUIContext | undefined;
|
|
42
41
|
let unsubscribe: (() => void) | undefined;
|
|
43
42
|
let lastStatus = "";
|
|
44
43
|
const pendingResults = new Map<string, TerminalSnapshot>();
|
|
45
44
|
const modelOwned = new Set<string>();
|
|
45
|
+
const starting = new Set<string>();
|
|
46
46
|
|
|
47
47
|
const getManager = (): TerminalManager => {
|
|
48
48
|
if (manager) return manager;
|
|
@@ -56,7 +56,7 @@ export default function backgroundTerminals(pi: ExtensionAPI) {
|
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
pendingResults.set(snapshot.id, snapshot);
|
|
59
|
-
if (
|
|
59
|
+
if (!starting.has(snapshot.id)) flushResult(snapshot.id);
|
|
60
60
|
});
|
|
61
61
|
unsubscribe = manager.view.subscribe(updateStatus);
|
|
62
62
|
updateStatus();
|
|
@@ -85,40 +85,31 @@ export default function backgroundTerminals(pi: ExtensionAPI) {
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
function
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
88
|
+
function flushResult(id: string): void {
|
|
89
|
+
const snapshot = pendingResults.get(id);
|
|
90
|
+
if (!snapshot) return;
|
|
91
|
+
pendingResults.delete(id);
|
|
92
|
+
deliverSettlement(pi, {
|
|
93
|
+
customType: "background-terminal-result",
|
|
94
|
+
content: formatCompletion(snapshot),
|
|
95
|
+
display: true,
|
|
96
|
+
details: {
|
|
97
|
+
id: snapshot.id,
|
|
98
|
+
title: snapshot.title,
|
|
99
|
+
status: snapshot.status,
|
|
100
|
+
exitCode: snapshot.exitCode,
|
|
100
101
|
},
|
|
101
|
-
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function flushResults(): void {
|
|
106
|
-
const results = [...pendingResults.values()];
|
|
107
|
-
pendingResults.clear();
|
|
108
|
-
for (const snapshot of results) deliver(snapshot);
|
|
102
|
+
});
|
|
109
103
|
}
|
|
110
104
|
|
|
111
105
|
pi.on("session_start", (_event, ctx) => {
|
|
112
|
-
sessionContext = ctx;
|
|
113
106
|
if (ctx.hasUI) ui = ctx.ui;
|
|
114
107
|
});
|
|
115
108
|
|
|
116
|
-
pi.on("agent_settled", flushResults);
|
|
117
|
-
|
|
118
109
|
pi.on("session_shutdown", async () => {
|
|
119
|
-
sessionContext = undefined;
|
|
120
110
|
pendingResults.clear();
|
|
121
111
|
modelOwned.clear();
|
|
112
|
+
starting.clear();
|
|
122
113
|
unsubscribe?.();
|
|
123
114
|
unsubscribe = undefined;
|
|
124
115
|
ui?.setStatus(STATUS_KEY, undefined);
|
|
@@ -140,7 +131,7 @@ export default function backgroundTerminals(pi: ExtensionAPI) {
|
|
|
140
131
|
promptSnippet: "Start an interactive or long-running command in a managed background PTY.",
|
|
141
132
|
promptGuidelines: [
|
|
142
133
|
"Use start_terminal by default for servers, watchers, downloads, long or uncertain builds and tests, interactive shells, and any command that should not occupy the main turn; reserve bash for short commands whose result is needed immediately. Never use a large bash timeout merely to wait for long work.",
|
|
143
|
-
"After start_terminal returns, continue only genuinely independent work. If none remains, end the turn immediately.
|
|
134
|
+
"After start_terminal returns, continue only genuinely independent work. If none remains, end the turn immediately. Terminal settlement is handed to Pi immediately: it queues a follow-up while the parent is active or starts the next parent turn when idle. When that result invokes the parent, continue the original task immediately without waiting for the user or rereading the same terminal; do not call read_terminal, list_terminals, or start a timer merely to check whether it finished.",
|
|
144
135
|
"Use stop_terminal when a managed process is no longer needed. Background terminals are session-scoped and are stopped during session shutdown or reload.",
|
|
145
136
|
],
|
|
146
137
|
parameters: Type.Object({
|
|
@@ -158,6 +149,7 @@ export default function backgroundTerminals(pi: ExtensionAPI) {
|
|
|
158
149
|
cwd: resolveCwd(ctx.cwd, params.working_dir),
|
|
159
150
|
});
|
|
160
151
|
modelOwned.add(terminal.id);
|
|
152
|
+
starting.add(terminal.id);
|
|
161
153
|
let result: TerminalReadResult;
|
|
162
154
|
try {
|
|
163
155
|
result = await getManager().read(terminal.id, {
|
|
@@ -166,12 +158,15 @@ export default function backgroundTerminals(pi: ExtensionAPI) {
|
|
|
166
158
|
signal,
|
|
167
159
|
});
|
|
168
160
|
} catch (error) {
|
|
169
|
-
|
|
161
|
+
starting.delete(terminal.id);
|
|
170
162
|
modelOwned.delete(terminal.id);
|
|
163
|
+
await getManager().kill([terminal.id]).catch(() => undefined);
|
|
171
164
|
consume(terminal.id);
|
|
172
165
|
throw error;
|
|
173
166
|
}
|
|
167
|
+
starting.delete(terminal.id);
|
|
174
168
|
if (result.snapshot.status !== "running") consume(terminal.id);
|
|
169
|
+
else flushResult(terminal.id);
|
|
175
170
|
let text = `Started background terminal ${terminal.id} "${oneLine(terminal.title)}" (pid ${terminal.pid}). Use cursor ${result.cursor} for incremental reads.`;
|
|
176
171
|
if (result.text || result.snapshot.status !== "running") {
|
|
177
172
|
text += `\n\n${formatReadResult(result)}`;
|
|
@@ -8,6 +8,7 @@ Internal runtime utilities used by more than one extension. This directory is no
|
|
|
8
8
|
- `child-session.ts` owns trust-aware child resources and bounded session shutdown.
|
|
9
9
|
- `context-utilization.ts` formats model-context usage and capacity.
|
|
10
10
|
- `dashboard-state.ts` keeps list selection stable as live rows change.
|
|
11
|
+
- `settlement-delivery.ts` immediately hands asynchronous results to Pi as extension-originated user follow-ups, guaranteeing model-visible settlement context.
|
|
11
12
|
- `tool-call-timeout.ts` applies cancellation-aware execution limits to registered tools.
|
|
12
13
|
- `tui-dashboard.ts` provides bounded, sanitized terminal-dashboard rendering helpers.
|
|
13
14
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
type SettlementMessage = Parameters<ExtensionAPI["sendMessage"]>[0];
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hand settlement to Pi as an extension-originated user follow-up. A custom
|
|
7
|
+
* message can wake the parent yet fail to appear in the invoked model turn;
|
|
8
|
+
* sendUserMessage guarantees that the bounded result is model-visible. Pi
|
|
9
|
+
* starts a turn when idle and queues the same input while the parent is active.
|
|
10
|
+
*/
|
|
11
|
+
export function deliverSettlement(pi: ExtensionAPI, message: SettlementMessage): void {
|
|
12
|
+
const content = typeof message.content === "string"
|
|
13
|
+
? message.content
|
|
14
|
+
: message.content
|
|
15
|
+
.filter((part) => part.type === "text")
|
|
16
|
+
.map((part) => part.text)
|
|
17
|
+
.join("\n");
|
|
18
|
+
pi.sendUserMessage(content, { deliverAs: "followUp" });
|
|
19
|
+
}
|
|
@@ -19,7 +19,7 @@ The system is deliberately flat. Only the main Pi thread can spawn subagents. Ch
|
|
|
19
19
|
- `reply_question` — answer a child’s blocking `ask_parent` request
|
|
20
20
|
- `task` — atomically reserve capacity for up to the configured limit (maximum 50), start the fan-out in the background, and return child ids immediately
|
|
21
21
|
|
|
22
|
-
Child sessions receive `message_parent`, `ask_parent`, `list_peers`, and `message_peer`. Peer messages are routed through the main-thread manager and can steer a running child or continue a settled one; they cannot create agents.
|
|
22
|
+
Child sessions receive `message_parent`, `ask_parent`, `list_peers`, and `message_peer`. Peer messages are routed through the main-thread manager and can steer a running child or continue a settled one; they cannot create agents. Child settlement is handed to Pi immediately as an extension-originated user follow-up: it queues while the parent is active or starts a new parent turn when idle with the summary guaranteed in model context, so the main turn can continue independent work or end and remain available to the user.
|
|
23
23
|
|
|
24
24
|
## Profiles and capabilities
|
|
25
25
|
|
|
@@ -86,4 +86,3 @@ Children share the requested workspace by default. Use `isolation: "worktree"` o
|
|
|
86
86
|
- `src/worktree.ts` — isolated worktree creation, inspection, conflict preflight, integration, and cleanup
|
|
87
87
|
- `src/runtime.ts` — managed runtime and Pi backend registry
|
|
88
88
|
- `src/ui/` — dashboard, transcript, and takeover components
|
|
89
|
-
- `src/result-delivery.ts` — deferred automatic result delivery
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
} from "@earendil-works/pi-coding-agent";
|
|
27
27
|
import { Markdown, Text } from "@earendil-works/pi-tui";
|
|
28
28
|
import { Type } from "typebox";
|
|
29
|
+
import { deliverSettlement } from "../shared/settlement-delivery.ts";
|
|
29
30
|
import {
|
|
30
31
|
formatElapsed,
|
|
31
32
|
latestText,
|
|
@@ -56,7 +57,6 @@ import {
|
|
|
56
57
|
SUBAGENT_WAIT_TOOL_DESCRIPTION,
|
|
57
58
|
WORKTREE_ISOLATION_DESCRIPTION,
|
|
58
59
|
} from "./src/prompt.ts";
|
|
59
|
-
import { createDeferredResultDelivery } from "./src/result-delivery.ts";
|
|
60
60
|
import {
|
|
61
61
|
CAPABILITY_MODES,
|
|
62
62
|
ISOLATION_MODES,
|
|
@@ -197,10 +197,8 @@ function resolveChildProjectTrust(options: {
|
|
|
197
197
|
export default function (pi: ExtensionAPI) {
|
|
198
198
|
let runtime: SubagentRuntime | undefined;
|
|
199
199
|
let managerPromise: Promise<SubagentManagerShape> | undefined;
|
|
200
|
-
let sessionContext: ExtensionContext | undefined;
|
|
201
200
|
let ui: ExtensionUIContext | undefined;
|
|
202
201
|
let unsubStatus: (() => void) | undefined;
|
|
203
|
-
const resultDelivery = createDeferredResultDelivery<SubagentSnapshot>();
|
|
204
202
|
const archived = new Map<string, ArchivedSubagent>();
|
|
205
203
|
const peerHistory: PeerMessage[] = [];
|
|
206
204
|
const pendingQuestions = new Map<
|
|
@@ -524,35 +522,24 @@ export default function (pi: ExtensionAPI) {
|
|
|
524
522
|
};
|
|
525
523
|
|
|
526
524
|
const deliverResult = (snap: SubagentSnapshot) => {
|
|
527
|
-
pi
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
},
|
|
540
|
-
{ deliverAs: "followUp", triggerTurn: true },
|
|
541
|
-
);
|
|
542
|
-
};
|
|
543
|
-
|
|
544
|
-
const flushResults = () => {
|
|
545
|
-
for (const snap of resultDelivery.drain()) deliverResult(snap);
|
|
525
|
+
deliverSettlement(pi, {
|
|
526
|
+
customType: "subagent-result",
|
|
527
|
+
content: buildSubagentResultMessage({
|
|
528
|
+
id: snap.id,
|
|
529
|
+
title: snap.title,
|
|
530
|
+
status: snap.status,
|
|
531
|
+
errorText: snap.errorText,
|
|
532
|
+
output: truncatedOutput(snap),
|
|
533
|
+
}),
|
|
534
|
+
display: true,
|
|
535
|
+
details: { id: snap.id, title: snap.title, status: snap.status },
|
|
536
|
+
});
|
|
546
537
|
};
|
|
547
538
|
|
|
548
539
|
const onSettled = (snap: SubagentSnapshot, consumed: boolean) => {
|
|
549
540
|
persistSnapshot(snap);
|
|
550
|
-
if (snap.meta.origin === "orchestration")
|
|
551
|
-
resultDelivery.consume([snap.id]);
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
541
|
+
if (snap.meta.origin === "orchestration") return;
|
|
554
542
|
if (snap.meta.origin === "btw") {
|
|
555
|
-
resultDelivery.consume([snap.id]);
|
|
556
543
|
pi.appendEntry<BtwEntryData>(BTW_ENTRY_TYPE, {
|
|
557
544
|
id: snap.id,
|
|
558
545
|
title: snap.title,
|
|
@@ -562,16 +549,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
562
549
|
});
|
|
563
550
|
return;
|
|
564
551
|
}
|
|
565
|
-
if (consumed)
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
}
|
|
569
|
-
// Keep the result retractable while the parent is working. A later
|
|
570
|
-
// wait_agent can consume it before agent_settled flushes follow-ups.
|
|
571
|
-
// Defer a copy: the live snapshot keeps mutating if the subagent is
|
|
572
|
-
// restarted before the deferred result flushes.
|
|
573
|
-
resultDelivery.defer({ ...snap, meta: { ...snap.meta } });
|
|
574
|
-
if (sessionContext?.isIdle()) flushResults();
|
|
552
|
+
if (consumed) return;
|
|
553
|
+
// Hand the immutable settlement to Pi immediately. Pi queues it when the
|
|
554
|
+
// parent is active and starts a new parent turn when idle.
|
|
555
|
+
deliverResult({ ...snap, meta: { ...snap.meta } });
|
|
575
556
|
};
|
|
576
557
|
|
|
577
558
|
const coordinator: SubagentCoordinator = {
|
|
@@ -641,7 +622,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
641
622
|
);
|
|
642
623
|
|
|
643
624
|
pi.on("session_start", (_event, ctx) => {
|
|
644
|
-
sessionContext = ctx;
|
|
645
625
|
archived.clear();
|
|
646
626
|
peerHistory.length = 0;
|
|
647
627
|
for (const record of loadSubagentCatalog().values()) archived.set(record.id, record);
|
|
@@ -662,12 +642,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
662
642
|
if (ctx.hasUI) ui = ctx.ui;
|
|
663
643
|
});
|
|
664
644
|
|
|
665
|
-
pi.on("agent_settled", flushResults);
|
|
666
|
-
|
|
667
645
|
pi.on("session_shutdown", async () => {
|
|
668
|
-
sessionContext = undefined;
|
|
669
646
|
unsubscribeCoordinator();
|
|
670
|
-
resultDelivery.clear();
|
|
671
647
|
for (const pending of pendingQuestions.values()) {
|
|
672
648
|
pending.reject(new Error("Parent Pi session shut down before replying."));
|
|
673
649
|
}
|
|
@@ -692,7 +668,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
692
668
|
promptSnippet: SUBAGENT_SPAWN_PROMPT_SNIPPET,
|
|
693
669
|
promptGuidelines: [
|
|
694
670
|
...SUBAGENT_SPAWN_PROMPT_GUIDELINES,
|
|
695
|
-
"After spawn_agent starts a child, continue only independent parent work or end the turn immediately. Do not call wait_agent, list_agents, or check_agent merely to watch it run;
|
|
671
|
+
"After spawn_agent starts a child, continue only independent parent work or end the turn immediately. Do not call wait_agent, list_agents, or check_agent merely to watch it run. Settlement is handed to Pi immediately; when its attached summary invokes the parent, continue the original task without waiting for another user message.",
|
|
696
672
|
],
|
|
697
673
|
parameters: Type.Object({
|
|
698
674
|
message: Type.String({
|
|
@@ -832,7 +808,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
832
808
|
const snapshots = ids.map((id) => manager.view.get(id)!);
|
|
833
809
|
const settled = snapshots.filter((snap) => snap.status !== "running");
|
|
834
810
|
const pending = snapshots.filter((snap) => snap.status === "running");
|
|
835
|
-
resultDelivery.consume(settled.map((snap) => snap.id));
|
|
836
811
|
|
|
837
812
|
const sections: string[] = [];
|
|
838
813
|
let remainingBytes = WAIT_OUTPUT_MAX_BYTES;
|
|
@@ -1121,7 +1096,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1121
1096
|
label: "Start Pi Subagent Tasks",
|
|
1122
1097
|
description: "Start independent subagent tasks together in the background and return their ids immediately. Completion notices automatically start the next parent turn, so the parent should end its current turn when no independent work remains instead of checking status.",
|
|
1123
1098
|
promptGuidelines: [
|
|
1124
|
-
"After task starts children, continue only independent parent work or end the turn immediately. Do not call wait_agent, list_agents, or check_agent merely to watch them run;
|
|
1099
|
+
"After task starts children, continue only independent parent work or end the turn immediately. Do not call wait_agent, list_agents, or check_agent merely to watch them run. Settlements are handed to Pi immediately; when their attached summaries invoke the parent, continue the original task without waiting for another user message.",
|
|
1125
1100
|
],
|
|
1126
1101
|
parameters: Type.Object({
|
|
1127
1102
|
tasks: Type.Array(
|
|
@@ -19,6 +19,7 @@ export const SUBAGENT_SPAWN_PROMPT_GUIDELINES = [
|
|
|
19
19
|
"Use resume_from to continue a completed child's existing context instead of restating its original task.",
|
|
20
20
|
"After spawn_agent or task starts background work, continue useful parent work; otherwise end the turn so Pi remains available to the user.",
|
|
21
21
|
"Do not poll background agents. Completion notices arrive automatically and wake the parent; wait_agent only collects results already available and reports running agents without blocking.",
|
|
22
|
+
"When a completion notice invokes the parent, use its attached summary and continue the original task immediately; do not wait for another user message or call a status tool for the same result.",
|
|
22
23
|
];
|
|
23
24
|
|
|
24
25
|
export const SUBAGENT_SPAWN_PARAMETER_DESCRIPTIONS = {
|
|
@@ -51,7 +52,7 @@ export function buildSubagentSpawnResult(options: {
|
|
|
51
52
|
].filter(Boolean);
|
|
52
53
|
return (
|
|
53
54
|
`Started Pi subagent ${options.id} "${options.title}" (${attributes.join(", ")}).\n` +
|
|
54
|
-
"It will report and wake the parent when finished. Continue useful work or end the turn instead of polling."
|
|
55
|
+
"It will report and wake the parent when finished. Continue useful work or end the turn instead of polling; when invoked by the result, continue the original task immediately."
|
|
55
56
|
);
|
|
56
57
|
}
|
|
57
58
|
|
package/package.json
CHANGED
|
@@ -24,7 +24,9 @@ Provide:
|
|
|
24
24
|
- the working directory when it differs from the current directory;
|
|
25
25
|
- an initial wait only when startup output is needed for the next decision.
|
|
26
26
|
|
|
27
|
-
After startup, continue only genuinely useful independent work. If none remains, end the turn immediately.
|
|
27
|
+
After startup, continue only genuinely useful independent work. If none remains, end the turn immediately. Ending the turn is the waiting mechanism: terminal settlement is handed to Pi immediately as an extension-originated user follow-up, queued if the parent is still active, and otherwise starts the next parent turn with the final status and bounded output visible in model context. Do not keep the current turn alive to wait, invent monitoring work, or call terminal tools merely to see whether the process finished.
|
|
28
|
+
|
|
29
|
+
When that completion follow-up invokes the next turn, treat its model-visible output as the terminal result and continue the original task immediately. Do not wait for another user message, announce that you are still waiting, or call `read_terminal` to retrieve the same result again. If `start_terminal` itself returns a settled result, the output is already synchronous and no second completion notice is needed.
|
|
28
30
|
|
|
29
31
|
## Inspect and interact only when necessary
|
|
30
32
|
|
|
@@ -24,14 +24,14 @@ This skill governs temporary Pi child agents. The `codex-thread-orchestrator` sk
|
|
|
24
24
|
|
|
25
25
|
## Wait by notification; inspect progress only when justified
|
|
26
26
|
|
|
27
|
-
A successful `spawn_agent` or `task` call starts asynchronous work and returns control to the parent. When a child finishes, its
|
|
27
|
+
A successful `spawn_agent` or `task` call starts asynchronous work and returns control to the parent. When a child finishes, its settlement is handed to Pi immediately as an extension-originated user follow-up, queued if the parent is still active, and otherwise starts the next main-agent turn with the summary visible in model context. The parent does not need to remain active or check once before ending its turn.
|
|
28
28
|
|
|
29
29
|
After dispatch:
|
|
30
30
|
|
|
31
31
|
1. Continue only parent work that is independently useful to the requested result.
|
|
32
32
|
2. If no such work remains, end the turn immediately. A short progress note is enough when the user needs one.
|
|
33
33
|
3. Do not call `wait_agent`, `list_agents`, or `check_agent` in the same turn merely because the child was just launched. Ending the turn is the waiting mechanism.
|
|
34
|
-
4.
|
|
34
|
+
4. When a child completion notice invokes the main agent, treat its model-visible summary as the child result and continue the original task immediately. Reconcile completed results, launch any intentionally queued work if capacity requires waves, and otherwise keep waiting through notifications. Do not wait for the user to prompt you again or call a status tool to retrieve the same result.
|
|
35
35
|
|
|
36
36
|
A progress check is reasonable when the user asks for status, a child has run materially longer than expected for its task and model, an interruption left its state unclear, or current status will change an immediate coordination decision. Prefer `check_agent` for one known child and `list_agents` for a batch overview. Use `wait_agent` to collect results already expected to be available, not as a running-status probe.
|
|
37
37
|
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export function createDeferredResultDelivery<T extends { id: string }>() {
|
|
2
|
-
const pending = new Map<string, T>();
|
|
3
|
-
|
|
4
|
-
return {
|
|
5
|
-
defer(result: T) {
|
|
6
|
-
pending.set(result.id, result);
|
|
7
|
-
},
|
|
8
|
-
consume(ids: Iterable<string>) {
|
|
9
|
-
for (const id of ids) pending.delete(id);
|
|
10
|
-
},
|
|
11
|
-
drain() {
|
|
12
|
-
const results = [...pending.values()];
|
|
13
|
-
pending.clear();
|
|
14
|
-
return results;
|
|
15
|
-
},
|
|
16
|
-
clear() {
|
|
17
|
-
pending.clear();
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
}
|