pi-subagents 0.42.0 → 0.43.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/CHANGELOG.md +35 -0
- package/README.md +3 -5
- package/package.json +1 -1
- package/skills/pi-subagents/SKILL.md +2 -2
- package/skills/pi-subagents/references/constraints-and-recipes.md +35 -32
- package/skills/pi-subagents/references/execution-controls.md +27 -43
- package/skills/pi-subagents/references/management-authoring-rpc.md +20 -3
- package/skills/pi-subagents/references/prompting-and-roles.md +16 -49
- package/src/agents/agent-refinements.ts +624 -0
- package/src/agents/agents.ts +0 -2
- package/src/agents/proactive-skills.ts +1 -1
- package/src/api/delegation.ts +1 -2
- package/src/extension/control-notices.ts +2 -2
- package/src/extension/fanout-child.ts +46 -22
- package/src/extension/index.ts +29 -12
- package/src/extension/public-execution.ts +71 -0
- package/src/extension/rpc.ts +7 -8
- package/src/extension/schemas.ts +17 -17
- package/src/extension/tool-description.ts +14 -14
- package/src/missions/actions.ts +47 -11
- package/src/missions/goal-driver.ts +162 -0
- package/src/missions/lifecycle.ts +44 -12
- package/src/missions/store.ts +68 -3
- package/src/missions/types.ts +25 -3
- package/src/missions/workflow-state.ts +77 -0
- package/src/profiles/profiles.ts +1 -3
- package/src/runs/background/async-execution.ts +3 -0
- package/src/runs/background/async-job-tracker.ts +2 -17
- package/src/runs/background/control-channel.ts +50 -6
- package/src/runs/background/retained-children.ts +68 -0
- package/src/runs/background/scheduled-runs.ts +17 -13
- package/src/runs/background/steering.ts +7 -5
- package/src/runs/background/subagent-runner.ts +10 -4
- package/src/runs/foreground/async-steering-action.ts +36 -10
- package/src/runs/foreground/chain-clarify.ts +13 -16
- package/src/runs/foreground/execution.ts +4 -0
- package/src/runs/foreground/subagent-executor.ts +200 -59
- package/src/runs/shared/acceptance.ts +154 -9
- package/src/runs/shared/subagent-prompt-runtime.ts +94 -25
- package/src/shared/types.ts +17 -3
- package/src/slash/delegation-adapters.ts +0 -13
- package/src/slash/prompt-template-bridge.ts +14 -12
- package/src/slash/prompt-workflows.ts +10 -4
- package/src/slash/slash-bridge.ts +8 -6
- package/src/slash/slash-commands.ts +27 -12
- package/src/slash/slash-live-state.ts +4 -2
- package/src/tui/fleet-status.ts +8 -4
- package/src/tui/fleet.ts +15 -5
- package/src/tui/render.ts +13 -31
- package/src/workflows/chat-progress.ts +2 -2
- package/src/workflows/scripted-workflow.ts +97 -10
- package/agents/context-builder.md +0 -46
- package/agents/planner.md +0 -56
- package/prompts/parallel-context-build.md +0 -55
- package/prompts/parallel-handoff-plan.md +0 -61
|
@@ -5,15 +5,16 @@ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
|
5
5
|
import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
6
6
|
import type { AsyncStatus, Details, SubagentState, ToolBudgetConfig, TurnBudgetConfig } from "../../shared/types.ts";
|
|
7
7
|
import { readStatus } from "../../shared/utils.ts";
|
|
8
|
-
import { consumeSteerAcks, deliverInterruptRequest, requestAsyncSteer } from "../background/control-channel.ts";
|
|
8
|
+
import { consumeSteerAcks, deliverInterruptRequest, queueRevivalBrief, requestAsyncSteer, type SteerDeliveryMode, type SteerRequest } from "../background/control-channel.ts";
|
|
9
9
|
import { resolveAsyncResumeTarget } from "../background/async-resume.ts";
|
|
10
10
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
|
|
11
|
-
import { actionResultFromSteeringStatus, claimSteeringRecovery, remainingSteeringRecoveryLimits, updateSteeringTarget, waitForSteeringAction } from "../background/steering.ts";
|
|
11
|
+
import { actionResultFromSteeringStatus, claimSteeringRecovery, createSteeringStatus, recordSteeringRequest, remainingSteeringRecoveryLimits, updateSteeringTarget, waitForSteeringAction } from "../background/steering.ts";
|
|
12
12
|
|
|
13
13
|
export async function steerAsyncRun(input: {
|
|
14
14
|
state: SubagentState;
|
|
15
15
|
runId: string;
|
|
16
16
|
message: string;
|
|
17
|
+
mode?: SteerDeliveryMode;
|
|
17
18
|
index?: number;
|
|
18
19
|
kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;
|
|
19
20
|
location: { asyncDir: string | null };
|
|
@@ -41,14 +42,38 @@ export async function steerAsyncRun(input: {
|
|
|
41
42
|
details: { mode: "management", results: [] },
|
|
42
43
|
};
|
|
43
44
|
}
|
|
44
|
-
if (!status
|
|
45
|
+
if (!status) {
|
|
45
46
|
return {
|
|
46
|
-
content: [{ type: "text", text: `Async run '${input.runId}'
|
|
47
|
+
content: [{ type: "text", text: `Async run '${input.runId}' was not found.` }],
|
|
47
48
|
isError: true,
|
|
48
49
|
details: { mode: "management", results: [] },
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
52
|
const steps = status.steps ?? [];
|
|
53
|
+
if (status.state !== "running" && status.state !== "queued") {
|
|
54
|
+
const retained = input.mode === "follow_up" && status.state === "complete" && Boolean(status.parentWorkflowRunId) && steps.length === 1 && (steps[0]?.status === "complete" || steps[0]?.status === "completed") && Boolean(steps[0]?.sessionFile ?? status.sessionFile);
|
|
55
|
+
if (!retained) {
|
|
56
|
+
return {
|
|
57
|
+
content: [{ type: "text", text: `Async run '${input.runId}' is not running or queued and cannot be steered.` }],
|
|
58
|
+
isError: true,
|
|
59
|
+
details: { mode: "management", results: [] },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (input.index !== undefined && input.index !== 0) {
|
|
63
|
+
return { content: [{ type: "text", text: `Retained async run '${status.runId}' has one child. Index ${input.index} is out of range.` }], isError: true, details: { mode: "management", results: [] } };
|
|
64
|
+
}
|
|
65
|
+
const request: SteerRequest = { type: "steer", id: randomUUID(), ts: Date.now(), message: input.message.trim(), mode: "follow_up", targetIndex: 0, source: "steer-action" };
|
|
66
|
+
try {
|
|
67
|
+
queueRevivalBrief(asyncDir, request);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
return { content: [{ type: "text", text: `Failed to queue retained follow-up for async run ${status.runId}: ${error instanceof Error ? error.message : String(error)}` }], isError: true, details: { mode: "management", results: [] } };
|
|
70
|
+
}
|
|
71
|
+
status.steering ??= createSteeringStatus();
|
|
72
|
+
recordSteeringRequest(status.steering, { id: request.id, requestedAt: request.ts, source: request.source, message: request.message, targets: [{ index: 0, state: "scheduled" }] });
|
|
73
|
+
writeAtomicJson(path.join(asyncDir, "status.json"), status);
|
|
74
|
+
const queued = { requestId: request.id, state: "scheduled" as const, deliveryStatus: "queued" as const, sourceRunId: status.runId, targets: [{ index: 0, state: "scheduled" as const }] };
|
|
75
|
+
return { content: [{ type: "text", text: `Follow-up queued for the next resume of retained async run ${status.runId} (request ${request.id}).` }], details: { mode: "management", results: [], steering: queued } };
|
|
76
|
+
}
|
|
52
77
|
if (input.index !== undefined) {
|
|
53
78
|
if (input.index < 0 || input.index >= steps.length) {
|
|
54
79
|
return {
|
|
@@ -92,6 +117,7 @@ export async function steerAsyncRun(input: {
|
|
|
92
117
|
try {
|
|
93
118
|
requestPath = requestAsyncSteer(asyncDir, {
|
|
94
119
|
message: input.message,
|
|
120
|
+
mode: input.mode,
|
|
95
121
|
...(effectiveTargetIndex !== undefined ? { targetIndex: effectiveTargetIndex } : { targetIndexes }),
|
|
96
122
|
source: "steer-action",
|
|
97
123
|
id: requestId,
|
|
@@ -108,11 +134,11 @@ export async function steerAsyncRun(input: {
|
|
|
108
134
|
if (tracked) tracked.updatedAt = Date.now();
|
|
109
135
|
const targets = targetIndexes.map((index) => ({ index, state: status.steps?.[index]?.status === "pending" ? "scheduled" as const : "pending" as const }));
|
|
110
136
|
if (targets.every((target) => target.state === "scheduled")) {
|
|
111
|
-
const scheduled = { requestId, state: "scheduled" as const, sourceRunId: status.runId, targets };
|
|
137
|
+
const scheduled = { requestId, state: "scheduled" as const, deliveryStatus: "queued" as const, sourceRunId: status.runId, targets };
|
|
112
138
|
return { content: [{ type: "text", text: `Steering scheduled for async run ${status.runId} (request ${requestId}).` }], details: { mode: "management", results: [], steering: scheduled } };
|
|
113
139
|
}
|
|
114
140
|
const waited = await waitForSteeringAction({ asyncDir, sourceRunId: status.runId, requestId, timeoutMs: input.ackTimeoutMs ?? 3_000, signal: input.signal });
|
|
115
|
-
const result = waited ?? { requestId, state: "pending" as const, sourceRunId: status.runId, targets };
|
|
141
|
+
const result = waited ?? { requestId, state: "pending" as const, deliveryStatus: "queued" as const, sourceRunId: status.runId, targets };
|
|
116
142
|
if (input.signal?.aborted) {
|
|
117
143
|
return { content: [{ type: "text", text: `Steering pending for async run ${status.runId} (request ${requestId}); caller aborted before recovery.` }], details: { mode: "management", results: [], steering: result } };
|
|
118
144
|
}
|
|
@@ -122,7 +148,7 @@ export async function steerAsyncRun(input: {
|
|
|
122
148
|
return { content: [{ type: "text", text: `Steering delivered for async run ${status.runId} (request ${requestId}).` }], details: { mode: "management", results: [], steering: finalResult } };
|
|
123
149
|
}
|
|
124
150
|
const running = (finalStatus?.steps ?? status.steps ?? []).filter((step) => step.status === "running");
|
|
125
|
-
const recoveryAllowed = status.mode === "single" && status.isNested !== true && running.length === 1 && Boolean(finalStatus?.steering) && (input.index === undefined || input.index === 0);
|
|
151
|
+
const recoveryAllowed = (input.mode ?? "steer") === "steer" && status.mode === "single" && status.isNested !== true && running.length === 1 && Boolean(finalStatus?.steering) && (input.index === undefined || input.index === 0);
|
|
126
152
|
if (recoveryAllowed && finalResult?.state !== "scheduled" && input.recover) {
|
|
127
153
|
const appendSteeringNotice = (state: "failed" | "recovered", message: string): void => {
|
|
128
154
|
try {
|
|
@@ -203,7 +229,7 @@ export async function steerAsyncRun(input: {
|
|
|
203
229
|
}
|
|
204
230
|
const recovered = sourceStatus?.steering ? actionResultFromSteeringStatus(sourceStatus.steering, status.runId, requestId, revived.details.asyncId) : undefined;
|
|
205
231
|
appendSteeringNotice("recovered", `Steering recovered for run ${status.runId}; replacement ${revived.details.asyncId} launched.`);
|
|
206
|
-
return { content: [{ type: "text", text: `Steering recovered for async run ${status.runId}; replacement ${revived.details.asyncId} launched after the source paused.` }], details: { mode: "management", results: [], steering: recovered ?? { requestId, state: "recovered", sourceRunId: status.runId, replacementRunId: revived.details.asyncId, targets: [{ index: input.index ?? 0, state: "recovered" }] } } };
|
|
232
|
+
return { content: [{ type: "text", text: `Steering recovered for async run ${status.runId}; replacement ${revived.details.asyncId} launched after the source paused.` }], details: { mode: "management", results: [], steering: recovered ?? { requestId, state: "recovered", sourceRunId: status.runId, replacementRunId: revived.details.asyncId, deliveryStatus: "delivered", targets: [{ index: input.index ?? 0, state: "recovered" }] } } };
|
|
207
233
|
} catch (error) {
|
|
208
234
|
const reason = error instanceof Error ? error.message : String(error);
|
|
209
235
|
const failedStatus = readStatus(asyncDir);
|
|
@@ -217,10 +243,10 @@ export async function steerAsyncRun(input: {
|
|
|
217
243
|
}
|
|
218
244
|
const failed = failedStatus?.steering ? actionResultFromSteeringStatus(failedStatus.steering, status.runId, requestId) : undefined;
|
|
219
245
|
appendSteeringNotice("failed", `Steering failed for run ${status.runId}: ${reason}`);
|
|
220
|
-
return { content: [{ type: "text", text: `Steering failed for async run ${status.runId} (request ${requestId}): ${reason}` }], isError: true, details: { mode: "management", results: [], steering: failed ?? { requestId, state: "failed", sourceRunId: status.runId, targets: [{ index: input.index ?? 0, state: "failed", reason }] } } };
|
|
246
|
+
return { content: [{ type: "text", text: `Steering failed for async run ${status.runId} (request ${requestId}): ${reason}` }], isError: true, details: { mode: "management", results: [], steering: failed ?? { requestId, state: "failed", sourceRunId: status.runId, deliveryStatus: "queued", targets: [{ index: input.index ?? 0, state: "failed", reason }] } } };
|
|
221
247
|
}
|
|
222
248
|
}
|
|
223
|
-
const stateText = result.state === "
|
|
249
|
+
const stateText = result.state === "failed" ? "failed" : result.state === "partial" ? "partial" : result.deliveryStatus === "queued" ? "queued" : result.state === "delivered" ? "delivered" : result.state === "scheduled" ? "scheduled" : result.state === "recovered" ? "recovered" : "pending";
|
|
224
250
|
const isError = result.state === "failed" || result.state === "partial";
|
|
225
251
|
return {
|
|
226
252
|
content: [{ type: "text", text: `Steering ${stateText} for async run ${status.runId} (request ${requestId}).` }],
|
|
@@ -882,25 +882,22 @@ export class ChainClarifyComponent implements Component {
|
|
|
882
882
|
}
|
|
883
883
|
}
|
|
884
884
|
|
|
885
|
-
render(
|
|
885
|
+
render(width: number): string[] {
|
|
886
|
+
let lines: string[] = [];
|
|
886
887
|
if (this.editingStep !== null) {
|
|
887
|
-
if (this.editMode === "model")
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
888
|
+
if (this.editMode === "model") lines = this.renderModelSelector();
|
|
889
|
+
else if (this.editMode === "thinking") lines = this.renderThinkingSelector();
|
|
890
|
+
else if (this.editMode === "skills") lines = this.renderSkillSelector();
|
|
891
|
+
else lines = this.renderFullEditMode();
|
|
892
|
+
} else {
|
|
893
|
+
switch (this.mode) {
|
|
894
|
+
case 'single': lines = this.renderSingleMode(); break;
|
|
895
|
+
case 'parallel': lines = this.renderParallelMode(); break;
|
|
896
|
+
case 'chain': lines = this.renderChainMode(); break;
|
|
895
897
|
}
|
|
896
|
-
return this.renderFullEditMode();
|
|
897
|
-
}
|
|
898
|
-
// Mode-based navigation rendering
|
|
899
|
-
switch (this.mode) {
|
|
900
|
-
case 'single': return this.renderSingleMode();
|
|
901
|
-
case 'parallel': return this.renderParallelMode();
|
|
902
|
-
case 'chain': return this.renderChainMode();
|
|
903
898
|
}
|
|
899
|
+
const renderWidth = Math.max(0, Math.min(this.width, Math.floor(width)));
|
|
900
|
+
return lines.map((line) => truncateToWidth(line, renderWidth));
|
|
904
901
|
}
|
|
905
902
|
|
|
906
903
|
/** Render the model selector view */
|
|
@@ -7,6 +7,7 @@ import { existsSync, unlinkSync } from "node:fs";
|
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import type { Message } from "@earendil-works/pi-ai";
|
|
9
9
|
import type { AgentConfig } from "../../agents/agents.ts";
|
|
10
|
+
import { appendAgentRefinementOverlay } from "../../agents/agent-refinements.ts";
|
|
10
11
|
import {
|
|
11
12
|
ensureArtifactsDir,
|
|
12
13
|
formatOutputArtifactContent,
|
|
@@ -1438,6 +1439,7 @@ async function runSyncCompletion(
|
|
|
1438
1439
|
if (memoryInjection) {
|
|
1439
1440
|
systemPrompt = systemPrompt ? `${systemPrompt}\n\n${memoryInjection}` : memoryInjection;
|
|
1440
1441
|
}
|
|
1442
|
+
systemPrompt = appendAgentRefinementOverlay(systemPrompt, { cwd: skillCwd, agentName });
|
|
1441
1443
|
systemPrompt = injectOutputPathSystemPrompt(systemPrompt, options.outputPath, agent);
|
|
1442
1444
|
|
|
1443
1445
|
const candidates = buildModelCandidates(
|
|
@@ -1699,6 +1701,8 @@ async function runSyncCompletion(
|
|
|
1699
1701
|
: undefined,
|
|
1700
1702
|
cwd: options.cwd ?? runtimeCwd,
|
|
1701
1703
|
reportOptional: isAgentContractV1(options.agentContract),
|
|
1704
|
+
artifactsDir: options.artifactsDir,
|
|
1705
|
+
runId: options.runId,
|
|
1702
1706
|
});
|
|
1703
1707
|
}
|
|
1704
1708
|
} catch (error) {
|