pi-subagents 0.78.0 → 0.79.1
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 +68 -0
- package/docs/agents.md +4 -2
- package/docs/configuration.md +4 -4
- package/docs/extension-api.md +4 -0
- package/docs/observability.md +11 -3
- package/docs/tool-reference.md +12 -1
- package/docs/workflows.md +6 -4
- package/package.json +1 -1
- package/skills/pi-subagents/SKILL.md +1 -0
- package/skills/pi-subagents/references/execution-controls.md +2 -0
- package/src/extension/declaration-pinning.d.ts.map +1 -1
- package/src/extension/declaration-pinning.js +15 -1
- package/src/extension/declaration-pinning.js.map +1 -1
- package/src/extension/index.d.ts.map +1 -1
- package/src/extension/index.js +19 -4
- package/src/extension/index.js.map +1 -1
- package/src/extension/public-execution.d.ts +1 -0
- package/src/extension/public-execution.d.ts.map +1 -1
- package/src/extension/public-execution.js +3 -0
- package/src/extension/public-execution.js.map +1 -1
- package/src/extension/rpc.d.ts.map +1 -1
- package/src/extension/rpc.js +5 -3
- package/src/extension/rpc.js.map +1 -1
- package/src/extension/schemas.d.ts +2 -0
- package/src/extension/schemas.d.ts.map +1 -1
- package/src/extension/schemas.js +1 -0
- package/src/extension/schemas.js.map +1 -1
- package/src/extension/tool-activation.d.ts +1 -0
- package/src/extension/tool-activation.d.ts.map +1 -1
- package/src/extension/tool-activation.js +57 -18
- package/src/extension/tool-activation.js.map +1 -1
- package/src/extension/upgrade-notice.d.ts.map +1 -1
- package/src/extension/upgrade-notice.js +7 -1
- package/src/extension/upgrade-notice.js.map +1 -1
- package/src/intercom/native-supervisor-channel.d.ts +2 -1
- package/src/intercom/native-supervisor-channel.d.ts.map +1 -1
- package/src/intercom/native-supervisor-channel.js +117 -9
- package/src/intercom/native-supervisor-channel.js.map +1 -1
- package/src/runs/background/async-execution.d.ts.map +1 -1
- package/src/runs/background/async-execution.js +13 -16
- package/src/runs/background/async-execution.js.map +1 -1
- package/src/runs/background/async-job-tracker.d.ts.map +1 -1
- package/src/runs/background/async-job-tracker.js +8 -0
- package/src/runs/background/async-job-tracker.js.map +1 -1
- package/src/runs/background/async-resume.d.ts.map +1 -1
- package/src/runs/background/async-resume.js +2 -1
- package/src/runs/background/async-resume.js.map +1 -1
- package/src/runs/background/async-status-snapshot.d.ts.map +1 -1
- package/src/runs/background/async-status-snapshot.js +4 -2
- package/src/runs/background/async-status-snapshot.js.map +1 -1
- package/src/runs/background/async-status.d.ts.map +1 -1
- package/src/runs/background/async-status.js +16 -8
- package/src/runs/background/async-status.js.map +1 -1
- package/src/runs/background/inspect-rpc.d.ts +5 -0
- package/src/runs/background/inspect-rpc.d.ts.map +1 -1
- package/src/runs/background/inspect-rpc.js +20 -3
- package/src/runs/background/inspect-rpc.js.map +1 -1
- package/src/runs/background/result-delivery-ownership.d.ts +0 -2
- package/src/runs/background/result-delivery-ownership.d.ts.map +1 -1
- package/src/runs/background/result-delivery-ownership.js +5 -21
- package/src/runs/background/result-delivery-ownership.js.map +1 -1
- package/src/runs/background/result-files.d.ts +5 -3
- package/src/runs/background/result-files.d.ts.map +1 -1
- package/src/runs/background/result-files.js +10 -6
- package/src/runs/background/result-files.js.map +1 -1
- package/src/runs/background/result-watcher.d.ts.map +1 -1
- package/src/runs/background/result-watcher.js +15 -8
- package/src/runs/background/result-watcher.js.map +1 -1
- package/src/runs/background/resume-guidance.d.ts.map +1 -1
- package/src/runs/background/resume-guidance.js +3 -2
- package/src/runs/background/resume-guidance.js.map +1 -1
- package/src/runs/background/run-child-session.d.ts.map +1 -1
- package/src/runs/background/run-child-session.js +7 -4
- package/src/runs/background/run-child-session.js.map +1 -1
- package/src/runs/background/run-status.d.ts.map +1 -1
- package/src/runs/background/run-status.js +11 -4
- package/src/runs/background/run-status.js.map +1 -1
- package/src/runs/background/stale-run-reconciler.d.ts.map +1 -1
- package/src/runs/background/stale-run-reconciler.js +53 -5
- package/src/runs/background/stale-run-reconciler.js.map +1 -1
- package/src/runs/background/subagent-runner.d.ts.map +1 -1
- package/src/runs/background/subagent-runner.js +15 -5
- package/src/runs/background/subagent-runner.js.map +1 -1
- package/src/runs/background/workflow-terminal-proof.d.ts +18 -1
- package/src/runs/background/workflow-terminal-proof.d.ts.map +1 -1
- package/src/runs/background/workflow-terminal-proof.js +27 -0
- package/src/runs/background/workflow-terminal-proof.js.map +1 -1
- package/src/runs/foreground/async-dismiss-action.d.ts.map +1 -1
- package/src/runs/foreground/async-dismiss-action.js +2 -1
- package/src/runs/foreground/async-dismiss-action.js.map +1 -1
- package/src/runs/foreground/async-steering-action.d.ts.map +1 -1
- package/src/runs/foreground/async-steering-action.js +3 -2
- package/src/runs/foreground/async-steering-action.js.map +1 -1
- package/src/runs/foreground/async-stop-action.d.ts +1 -0
- package/src/runs/foreground/async-stop-action.d.ts.map +1 -1
- package/src/runs/foreground/async-stop-action.js +3 -1
- package/src/runs/foreground/async-stop-action.js.map +1 -1
- package/src/runs/foreground/command-action.d.ts.map +1 -1
- package/src/runs/foreground/command-action.js +2 -1
- package/src/runs/foreground/command-action.js.map +1 -1
- package/src/runs/foreground/execution.d.ts.map +1 -1
- package/src/runs/foreground/execution.js +18 -7
- package/src/runs/foreground/execution.js.map +1 -1
- package/src/runs/foreground/subagent-executor.d.ts +2 -0
- package/src/runs/foreground/subagent-executor.d.ts.map +1 -1
- package/src/runs/foreground/subagent-executor.js +98 -32
- package/src/runs/foreground/subagent-executor.js.map +1 -1
- package/src/runs/shared/async-status-projection.d.ts +4 -0
- package/src/runs/shared/async-status-projection.d.ts.map +1 -1
- package/src/runs/shared/async-status-projection.js +23 -1
- package/src/runs/shared/async-status-projection.js.map +1 -1
- package/src/runs/shared/child-commands.d.ts +2 -0
- package/src/runs/shared/child-commands.d.ts.map +1 -1
- package/src/runs/shared/child-commands.js +4 -0
- package/src/runs/shared/child-commands.js.map +1 -1
- package/src/runs/shared/child-runtime-config.d.ts +6 -0
- package/src/runs/shared/child-runtime-config.d.ts.map +1 -1
- package/src/runs/shared/child-runtime-config.js +6 -0
- package/src/runs/shared/child-runtime-config.js.map +1 -1
- package/src/runs/shared/child-session.d.ts +6 -6
- package/src/runs/shared/child-session.d.ts.map +1 -1
- package/src/runs/shared/child-session.js +111 -51
- package/src/runs/shared/child-session.js.map +1 -1
- package/src/runs/shared/long-running-guard.d.ts +3 -1
- package/src/runs/shared/long-running-guard.d.ts.map +1 -1
- package/src/runs/shared/long-running-guard.js +75 -17
- package/src/runs/shared/long-running-guard.js.map +1 -1
- package/src/runs/shared/model-resolution.d.ts +5 -1
- package/src/runs/shared/model-resolution.d.ts.map +1 -1
- package/src/runs/shared/model-resolution.js +8 -1
- package/src/runs/shared/model-resolution.js.map +1 -1
- package/src/runs/shared/nested-events.d.ts.map +1 -1
- package/src/runs/shared/nested-events.js +2 -0
- package/src/runs/shared/nested-events.js.map +1 -1
- package/src/runs/shared/pi-spawn.d.ts +5 -1
- package/src/runs/shared/pi-spawn.d.ts.map +1 -1
- package/src/runs/shared/pi-spawn.js +86 -30
- package/src/runs/shared/pi-spawn.js.map +1 -1
- package/src/runs/shared/run-ownership.d.ts +4 -0
- package/src/runs/shared/run-ownership.d.ts.map +1 -0
- package/src/runs/shared/run-ownership.js +9 -0
- package/src/runs/shared/run-ownership.js.map +1 -0
- package/src/runs/shared/subagent-control.d.ts +1 -0
- package/src/runs/shared/subagent-control.d.ts.map +1 -1
- package/src/runs/shared/subagent-control.js +2 -1
- package/src/runs/shared/subagent-control.js.map +1 -1
- package/src/runs/shared/subagent-prompt-runtime.d.ts +45 -18
- package/src/runs/shared/subagent-prompt-runtime.d.ts.map +1 -1
- package/src/runs/shared/subagent-prompt-runtime.js +160 -59
- package/src/runs/shared/subagent-prompt-runtime.js.map +1 -1
- package/src/shared/file-lease.d.ts +7 -1
- package/src/shared/file-lease.d.ts.map +1 -1
- package/src/shared/file-lease.js +8 -2
- package/src/shared/file-lease.js.map +1 -1
- package/src/shared/fork-context.d.ts +3 -1
- package/src/shared/fork-context.d.ts.map +1 -1
- package/src/shared/fork-context.js +23 -13
- package/src/shared/fork-context.js.map +1 -1
- package/src/shared/session-succession.d.ts +5 -0
- package/src/shared/session-succession.d.ts.map +1 -0
- package/src/shared/session-succession.js +25 -0
- package/src/shared/session-succession.js.map +1 -0
- package/src/shared/types.d.ts +15 -1
- package/src/shared/types.d.ts.map +1 -1
- package/src/shared/types.js +3 -0
- package/src/shared/types.js.map +1 -1
- package/src/shared/utils.d.ts +11 -0
- package/src/shared/utils.d.ts.map +1 -1
- package/src/shared/utils.js +17 -0
- package/src/shared/utils.js.map +1 -1
- package/src/slash/slash-commands.d.ts.map +1 -1
- package/src/slash/slash-commands.js +2 -2
- package/src/slash/slash-commands.js.map +1 -1
- package/src/watchdog/register-main.js +2 -2
- package/src/watchdog/register-main.js.map +1 -1
- package/src/watchdog/runtime.d.ts.map +1 -1
- package/src/watchdog/runtime.js +3 -5
- package/src/watchdog/runtime.js.map +1 -1
- package/src/workflows/scripted-workflow.d.ts +1 -1
- package/src/workflows/scripted-workflow.d.ts.map +1 -1
- package/src/workflows/workflow-child-summary.d.ts +5 -0
- package/src/workflows/workflow-child-summary.d.ts.map +1 -1
- package/src/workflows/workflow-child-summary.js +10 -0
- package/src/workflows/workflow-child-summary.js.map +1 -1
- package/src/workflows/workflow-reuse.d.ts +6 -1
- package/src/workflows/workflow-reuse.d.ts.map +1 -1
- package/src/workflows/workflow-reuse.js +22 -1
- package/src/workflows/workflow-reuse.js.map +1 -1
|
@@ -68,6 +68,7 @@ import { canQueueRetainedAsyncFollowUp, steerAsyncRun } from "./async-steering-a
|
|
|
68
68
|
import { resolveWorkflowForegroundSteeringTarget, steerWorkflowForegroundTarget, steerWorkflowRun, } from "./workflow-foreground-steering.js";
|
|
69
69
|
import { stopAsyncRun } from "./async-stop-action.js";
|
|
70
70
|
import { dismissRecoveredWorkflow } from "./async-dismiss-action.js";
|
|
71
|
+
import { inheritedSessionIds } from "../../shared/session-succession.js";
|
|
71
72
|
import { promotePausedWorkflowIfSettled, reconcileDetachedWorkflowChildCompletion } from "./workflow-detach-reconcile.js";
|
|
72
73
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.js";
|
|
73
74
|
import { resolveAsyncRootResultPath, waitForImportedAsyncRoot } from "../background/chain-root-attachment.js";
|
|
@@ -94,7 +95,7 @@ import { getInspectorPlugins } from "../../inspectors/plugins.js";
|
|
|
94
95
|
import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.js";
|
|
95
96
|
import { previewSimpleWorkflowRun, runWorkflowScript, validateWorkflowScript, WorkflowScriptError } from "../../workflows/scripted-workflow.js";
|
|
96
97
|
import { formatIncrementalChildCompletion, incrementalChildCompletionTriggersTurn } from "../background/notify.js";
|
|
97
|
-
import { appendWorkflowChildJournal, findWorkflowReuseSource, matchWorkflowReuse, workflowChildFingerprint, workflowScriptDigest, workflowStopCause
|
|
98
|
+
import { appendWorkflowChildJournal, findWorkflowReuseSource, matchWorkflowReuse, readNamedWorkflowReuseSource, workflowChildFingerprint, workflowRuntimeReplacedRelaunchNotice, workflowScriptDigest, workflowStopCause } from "../../workflows/workflow-reuse.js";
|
|
98
99
|
import { executeWorkflowHostCommand, resolveWorkflowHostOutputClaimPath } from "../../workflows/host-command.js";
|
|
99
100
|
import { buildWorkflowReceipt, readWorkflowReceipt, workflowReceiptPath, resolveWorkflowReceiptResumeEntry, writeWorkflowReceipt } from "../../workflows/workflow-receipt.js";
|
|
100
101
|
import { formatWorkflowKeyRevival, recordWorkflowRevival, withWorkflowRevivals } from "../../workflows/workflow-revival.js";
|
|
@@ -270,11 +271,12 @@ function nestedResolutionScopeForExecutor(deps) {
|
|
|
270
271
|
...(address ? { descendantOf: { parentRunId: address.parentRunId, ...(address.parentStepIndex !== undefined ? { parentStepIndex: address.parentStepIndex } : {}) } } : {}),
|
|
271
272
|
};
|
|
272
273
|
}
|
|
273
|
-
function
|
|
274
|
+
function trustedSessionRoots(parentSessionFile, deps) {
|
|
274
275
|
const roots = deps.config.defaultSessionDir ? [path.resolve(deps.expandTilde(deps.config.defaultSessionDir))] : [];
|
|
275
|
-
|
|
276
|
+
// Inherited runs keep their children under the root of the session that launched them.
|
|
276
277
|
if (parentSessionFile)
|
|
277
|
-
|
|
278
|
+
for (const sessionFile of [parentSessionFile, ...inheritedSessionIds(parentSessionFile)])
|
|
279
|
+
roots.push(deps.getSubagentSessionRoot(sessionFile));
|
|
278
280
|
return [...new Set(roots)];
|
|
279
281
|
}
|
|
280
282
|
/** Children follow the launching session's trust; hosts older than Pi's trust concept keep Pi's default. */
|
|
@@ -1129,7 +1131,7 @@ function pathWithin(base, candidate) {
|
|
|
1129
1131
|
const resolvedCandidate = path.resolve(candidate);
|
|
1130
1132
|
return resolvedCandidate === resolvedBase || resolvedCandidate.startsWith(`${resolvedBase}${path.sep}`);
|
|
1131
1133
|
}
|
|
1132
|
-
function validateNestedSessionFile(run, trustedSessionRoots) {
|
|
1134
|
+
function validateNestedSessionFile(run, trustedSessionRoots, resolveSource, visited = []) {
|
|
1133
1135
|
const sessionFile = nestedRunSessionFile(run);
|
|
1134
1136
|
if (!sessionFile)
|
|
1135
1137
|
throw new Error(`Nested run '${run.id}' does not have a persisted session file to resume from.`);
|
|
@@ -1151,7 +1153,24 @@ function validateNestedSessionFile(run, trustedSessionRoots) {
|
|
|
1151
1153
|
throw new Error(`Nested run '${run.id}' session file is outside trusted nested session roots: ${sessionFile}`);
|
|
1152
1154
|
}
|
|
1153
1155
|
if (!realSessionFile.split(path.sep).includes(run.id)) {
|
|
1154
|
-
|
|
1156
|
+
// A resume replacement continues its source's session; accept it only when that source,
|
|
1157
|
+
// resolved in the caller's own scope, is one the caller could resume itself (the states
|
|
1158
|
+
// resolveNestedResumeTarget refuses), validates on its own and owns this same file.
|
|
1159
|
+
const sourceId = run.revivedFrom;
|
|
1160
|
+
const chain = [...visited, run.id];
|
|
1161
|
+
const source = sourceId && chain.length <= 8 && !chain.includes(sourceId) ? resolveSource(sourceId) : undefined;
|
|
1162
|
+
let sourceFile;
|
|
1163
|
+
if (source && source.state !== "running" && source.state !== "queued" && source.state !== "stopped") {
|
|
1164
|
+
try {
|
|
1165
|
+
sourceFile = validateNestedSessionFile(source, trustedSessionRoots, resolveSource, chain);
|
|
1166
|
+
}
|
|
1167
|
+
catch {
|
|
1168
|
+
sourceFile = undefined;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
if (sourceFile !== realSessionFile) {
|
|
1172
|
+
throw new Error(`Nested run '${run.id}' session file is not under that nested run's session directory: ${sessionFile}`);
|
|
1173
|
+
}
|
|
1155
1174
|
}
|
|
1156
1175
|
return realSessionFile;
|
|
1157
1176
|
}
|
|
@@ -1165,7 +1184,7 @@ export function readNestedRecoveryDescriptor(asyncDir, runId, agent) {
|
|
|
1165
1184
|
throw new Error(`Nested run '${runId}' has a recovery descriptor for a different agent.`);
|
|
1166
1185
|
return recoveryDescriptor;
|
|
1167
1186
|
}
|
|
1168
|
-
function resolveNestedResumeTarget(match, trustedSessionRoots) {
|
|
1187
|
+
function resolveNestedResumeTarget(match, trustedSessionRoots, resolveSource) {
|
|
1169
1188
|
const run = match.match.run;
|
|
1170
1189
|
if (run.state === "running" || run.state === "queued")
|
|
1171
1190
|
throw new Error(`Nested run '${run.id}' is live; route the follow-up to the owner process instead.`);
|
|
@@ -1185,7 +1204,7 @@ function resolveNestedResumeTarget(match, trustedSessionRoots) {
|
|
|
1185
1204
|
agent,
|
|
1186
1205
|
index: 0,
|
|
1187
1206
|
cwd: asyncDir ? path.dirname(asyncDir) : undefined,
|
|
1188
|
-
sessionFile: validateNestedSessionFile(run, trustedSessionRoots),
|
|
1207
|
+
sessionFile: validateNestedSessionFile(run, trustedSessionRoots, resolveSource),
|
|
1189
1208
|
...(run.capabilityCeiling ? { capabilityCeiling: run.capabilityCeiling } : {}),
|
|
1190
1209
|
...(recoveryDescriptor ? { recoveryDescriptor } : {}),
|
|
1191
1210
|
});
|
|
@@ -1522,9 +1541,10 @@ async function resumeExternalJobFollowUp(input) {
|
|
|
1522
1541
|
}
|
|
1523
1542
|
function resolveRequestedResumeTarget(params, deps, parentSessionFile) {
|
|
1524
1543
|
const requestedId = params.id ?? params.runId;
|
|
1544
|
+
const resolutionDeps = omitUndefinedProperties({ state: deps.state, nested: nestedResolutionScopeForExecutor(deps) });
|
|
1525
1545
|
let resolved;
|
|
1526
1546
|
try {
|
|
1527
|
-
resolved = requestedId ? resolveSubagentRunId(requestedId,
|
|
1547
|
+
resolved = requestedId ? resolveSubagentRunId(requestedId, resolutionDeps) : undefined;
|
|
1528
1548
|
}
|
|
1529
1549
|
catch (error) {
|
|
1530
1550
|
const message = error instanceof Error ? error.message : "";
|
|
@@ -1540,11 +1560,18 @@ function resolveRequestedResumeTarget(params, deps, parentSessionFile) {
|
|
|
1540
1560
|
const externalJobTarget = resolveNestedExternalJobResumeTarget(resolved, params, deps);
|
|
1541
1561
|
if (externalJobTarget)
|
|
1542
1562
|
return externalJobTarget;
|
|
1543
|
-
const
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1563
|
+
const rootRunId = resolved.match.rootRunId;
|
|
1564
|
+
// The source must be a nested run the caller could address itself, in the same registry.
|
|
1565
|
+
const resolveSource = (id) => {
|
|
1566
|
+
try {
|
|
1567
|
+
const source = resolveSubagentRunId(id, resolutionDeps);
|
|
1568
|
+
return source?.kind === "nested" && source.id === id && source.match.rootRunId === rootRunId ? source.match.run : undefined;
|
|
1569
|
+
}
|
|
1570
|
+
catch {
|
|
1571
|
+
return undefined;
|
|
1572
|
+
}
|
|
1573
|
+
};
|
|
1574
|
+
return resolveNestedResumeTarget(resolved, trustedSessionRoots(parentSessionFile, deps), resolveSource);
|
|
1548
1575
|
}
|
|
1549
1576
|
return resolveResumeTarget(params, deps.state, { asyncRequireSessionFile: false });
|
|
1550
1577
|
}
|
|
@@ -3012,9 +3039,9 @@ function importWorkflowAwaitedChildResult(completed, options) {
|
|
|
3012
3039
|
};
|
|
3013
3040
|
}
|
|
3014
3041
|
// A replaced runtime leaves the child running so a relaunch of the workflow can re-attach to it.
|
|
3015
|
-
function stopAwaitedAsyncChildOnAbort(signal, state, runId, asyncDir, kill) {
|
|
3042
|
+
function stopAwaitedAsyncChildOnAbort(signal, state, runId, asyncDir, kill, reattachedFromOtherSession = false) {
|
|
3016
3043
|
const stopOnAbort = () => { if (!workflowStopCause(signal?.reason))
|
|
3017
|
-
stopAsyncRun(state, runId, kill, { asyncDir, resolvedId: runId }); };
|
|
3044
|
+
stopAsyncRun(state, runId, kill, { asyncDir, resolvedId: runId, ...(reattachedFromOtherSession ? { reattachedRunId: runId } : {}) }); };
|
|
3018
3045
|
if (signal?.aborted)
|
|
3019
3046
|
stopOnAbort();
|
|
3020
3047
|
else
|
|
@@ -4052,13 +4079,16 @@ export function bindMissionWorkflowChildAsyncLaunch(params, binding, asyncByDefa
|
|
|
4052
4079
|
}
|
|
4053
4080
|
function workflowChildResult(key, result, childParams = {}, resumeState, forcedTerminalOutcome) {
|
|
4054
4081
|
const running = missionWorkflowChildStatus(result) === "running";
|
|
4055
|
-
|
|
4082
|
+
// The receipt opens with the workflow-wide run fan-out line, which says nothing about this child.
|
|
4083
|
+
const fanoutPrefix = result.details.runFanoutBudget ? `${formatRunFanoutBudget(result.details.runFanoutBudget)}\n` : undefined;
|
|
4084
|
+
const fullReceipt = result.content.map((part) => part.type === "text" ? part.text : "").filter(Boolean).join("\n");
|
|
4085
|
+
const receiptOutput = fanoutPrefix && fullReceipt.startsWith(fanoutPrefix) ? fullReceipt.slice(fanoutPrefix.length) : fullReceipt;
|
|
4056
4086
|
const output = running ? "" : result.details.results.length === 1 && result.details.results[0]?.finalOutput !== undefined
|
|
4057
4087
|
? result.details.results[0].finalOutput
|
|
4058
4088
|
: receiptOutput;
|
|
4059
4089
|
const childError = result.details.results.map((child) => child.error).find((error) => Boolean(error));
|
|
4060
4090
|
const stopped = result.details.results.some((child) => child.stopped);
|
|
4061
|
-
// A stopped child
|
|
4091
|
+
// A stopped child reports its structured stop reason alone, without the receipt's output block.
|
|
4062
4092
|
const failureErrorBase = stopped && childError
|
|
4063
4093
|
? childError
|
|
4064
4094
|
: childError && receiptOutput
|
|
@@ -4884,6 +4914,17 @@ export function createSubagentExecutor(deps) {
|
|
|
4884
4914
|
&& !inheritedNestedRoute(deps)
|
|
4885
4915
|
&& !requestParams.workflowParentRunId;
|
|
4886
4916
|
const workflowRunId = asyncWorkflow ? randomUUID() : undefined;
|
|
4917
|
+
if (requestParams.reuseFrom !== undefined && !topLevelAsyncWorkflow) {
|
|
4918
|
+
return { content: [{ type: "text", text: "options.reuseFrom requires an async top-level workflow." }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4919
|
+
}
|
|
4920
|
+
let namedWorkflowReuseSource;
|
|
4921
|
+
try {
|
|
4922
|
+
if (requestParams.reuseFrom !== undefined)
|
|
4923
|
+
namedWorkflowReuseSource = readNamedWorkflowReuseSource(DIRS.async, requestParams.reuseFrom, workflowScriptDigest(requestParams.workflowScript), workflowCwd);
|
|
4924
|
+
}
|
|
4925
|
+
catch (error) {
|
|
4926
|
+
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4927
|
+
}
|
|
4887
4928
|
let workflowCapacity;
|
|
4888
4929
|
if (workflowRunId && topLevelAsyncWorkflow) {
|
|
4889
4930
|
const currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
@@ -4950,7 +4991,7 @@ export function createSubagentExecutor(deps) {
|
|
|
4950
4991
|
const toolCallId = _id;
|
|
4951
4992
|
const workflowSessionRoot = requestParams.sessionDir
|
|
4952
4993
|
? path.resolve(deps.expandTilde(requestParams.sessionDir))
|
|
4953
|
-
:
|
|
4994
|
+
: trustedSessionRoots(ctx.sessionManager.getSessionFile() ?? null, deps)[0];
|
|
4954
4995
|
const asyncDir = path.join(DIRS.async, workflowRunId);
|
|
4955
4996
|
const resultPath = resultFilePath(DIRS.results, workflowRunId);
|
|
4956
4997
|
const statusPath = path.join(asyncDir, "status.json");
|
|
@@ -4961,7 +5002,7 @@ export function createSubagentExecutor(deps) {
|
|
|
4961
5002
|
const completionOwnerId = deps.state.completionOwnerId ?? currentCompletionOwnerId();
|
|
4962
5003
|
deps.state.completionOwnerId = completionOwnerId;
|
|
4963
5004
|
const workflowScriptSourceDigest = workflowScriptDigest(requestParams.workflowScript);
|
|
4964
|
-
const workflowReuseSource = findWorkflowReuseSource(DIRS.async, currentSessionId, workflowScriptSourceDigest, workflowArgsDigest);
|
|
5005
|
+
const workflowReuseSource = namedWorkflowReuseSource ?? findWorkflowReuseSource(DIRS.async, currentSessionId, workflowScriptSourceDigest, workflowArgsDigest);
|
|
4965
5006
|
const workflowRunEvidence = { ...workflowArgsEvidence, scriptDigest: workflowScriptSourceDigest };
|
|
4966
5007
|
if (workflowReuseSource)
|
|
4967
5008
|
workflowRunEvidence.reusedFrom = workflowReuseSource.runId;
|
|
@@ -5486,6 +5527,9 @@ export function createSubagentExecutor(deps) {
|
|
|
5486
5527
|
if (entry.state === "reused" && existing) {
|
|
5487
5528
|
continue;
|
|
5488
5529
|
}
|
|
5530
|
+
// A runtime replacement does not stop awaited async children (stopAwaitedAsyncChildOnAbort): they keep running.
|
|
5531
|
+
if (entry.state === "stopped" && existing?.async === true && existing.runId && workflowStopCause(controller.signal.reason))
|
|
5532
|
+
continue;
|
|
5489
5533
|
const mapped = entry.state === "started" || entry.state === "reused"
|
|
5490
5534
|
? "running"
|
|
5491
5535
|
: entry.state === "completed"
|
|
@@ -5677,7 +5721,7 @@ export function createSubagentExecutor(deps) {
|
|
|
5677
5721
|
const priorRunId = priorChild.runId;
|
|
5678
5722
|
const priorAsyncDir = path.join(DIRS.async, priorRunId);
|
|
5679
5723
|
appendWorkflowChildJournal(asyncDir, { type: "start", key, fingerprint: journalFingerprint, runId: priorRunId });
|
|
5680
|
-
const stopListener = stopAwaitedAsyncChildOnAbort(workflowSignal, deps.state, priorRunId, priorAsyncDir, deps.kill);
|
|
5724
|
+
const stopListener = stopAwaitedAsyncChildOnAbort(workflowSignal, deps.state, priorRunId, priorAsyncDir, deps.kill, requestParams.reuseFrom !== undefined);
|
|
5681
5725
|
const reattached = await awaitExistingAsyncRun(priorAsyncDir, priorRunId, workflowSignal).finally(stopListener.remove);
|
|
5682
5726
|
const claimedPath = reattached.status === "settled" ? claimWorkflowAwaitedResult(priorAsyncDir, workflowRunId, { ...reattached.result.importedPublication, runId: priorRunId }) : undefined;
|
|
5683
5727
|
if (reattached.status === "settled" && claimedPath) {
|
|
@@ -5874,7 +5918,7 @@ export function createSubagentExecutor(deps) {
|
|
|
5874
5918
|
const errorPreview = status.error && status.error.length > 1_000 ? `${status.error.slice(0, 1_000)}… (truncated; full error: ${statusPath} (error))` : status.error;
|
|
5875
5919
|
const terminalSummary = `${status.state === "complete"
|
|
5876
5920
|
? "Workflow completed after detached child finished."
|
|
5877
|
-
: errorPreview ?? (pauseForDetached ? "Workflow paused." : "Workflow failed.")}${stopCause ? ` ${
|
|
5921
|
+
: errorPreview ?? (pauseForDetached ? "Workflow paused." : "Workflow failed.")}${stopCause ? ` ${workflowRuntimeReplacedRelaunchNotice(workflowRunId)}` : ""}${workflowOutputPathMappingSummary(partial.children)}${finalPreflightWarnings.length ? ` ${finalPreflightWarnings.join(" ")}` : ""}`;
|
|
5878
5922
|
const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, terminalSummary, producedChildOutputPaths);
|
|
5879
5923
|
const resultSummary = appendWorkflowOutputWarning(terminalSummary, outputWarning);
|
|
5880
5924
|
const receiptState = status.state === "complete" ? "complete" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "failed";
|
|
@@ -5947,6 +5991,7 @@ export function createSubagentExecutor(deps) {
|
|
|
5947
5991
|
const workflowHostSteps = new Map();
|
|
5948
5992
|
let liveWorkflow = { trace: [], emits: [], console: [], ...(workflowResource ? { resource: workflowResource.provenance } : {}) };
|
|
5949
5993
|
const childProgress = new Map();
|
|
5994
|
+
const childLaunches = new Map();
|
|
5950
5995
|
let activityTimer;
|
|
5951
5996
|
let lastProgressAt = 0;
|
|
5952
5997
|
let workflowProgressClosed = false;
|
|
@@ -5959,7 +6004,7 @@ export function createSubagentExecutor(deps) {
|
|
|
5959
6004
|
clearTimeout(activityTimer);
|
|
5960
6005
|
activityTimer = undefined;
|
|
5961
6006
|
lastProgressAt = Date.now();
|
|
5962
|
-
const liveWorkflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: foregroundWorkflowRunId, workflowState: "running", inventoryComplete: false, trace: liveWorkflow.trace, progress: childProgress });
|
|
6007
|
+
const liveWorkflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: foregroundWorkflowRunId, workflowState: "running", inventoryComplete: false, trace: liveWorkflow.trace, progress: childProgress, launches: childLaunches });
|
|
5963
6008
|
onUpdate?.(workflowProgressUpdate(foregroundWorkflowRunId, chatProgress, liveWorkflow, liveWorkflowChildren, workflowPreflight));
|
|
5964
6009
|
};
|
|
5965
6010
|
try {
|
|
@@ -6047,11 +6092,21 @@ export function createSubagentExecutor(deps) {
|
|
|
6047
6092
|
throw new Error("Workflow child permit supports native Pi children only.");
|
|
6048
6093
|
workflowPermitContexts.set(childRequest, { child: { permit: delegatedWorkflowPermit, workflowRunId: foregroundWorkflowRunId, childKey: key } });
|
|
6049
6094
|
}
|
|
6050
|
-
workflowLaunchObservers.set(childRequest, (launch) =>
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6095
|
+
workflowLaunchObservers.set(childRequest, (launch) => {
|
|
6096
|
+
childLaunches.set(key, { agent: launch.agent, ...(launch.runId ? { runId: launch.runId } : {}) });
|
|
6097
|
+
// Telemetry must not decide the child's outcome, as with onTrace in scripted-workflow.
|
|
6098
|
+
try {
|
|
6099
|
+
sendWorkflowProgress();
|
|
6100
|
+
}
|
|
6101
|
+
catch (error) {
|
|
6102
|
+
console.error("Workflow progress update failed:", error);
|
|
6103
|
+
}
|
|
6104
|
+
recordMissionWorkflowChild(missionBinding, foregroundWorkflowRunId, key, {
|
|
6105
|
+
status: "running",
|
|
6106
|
+
agent: launch.agent,
|
|
6107
|
+
...(launch.sessionFile ? { sessionPath: launch.sessionFile } : {}),
|
|
6108
|
+
});
|
|
6109
|
+
});
|
|
6055
6110
|
return execute(randomUUID(), childRequest, workflowSignal, (update) => {
|
|
6056
6111
|
const progress = update.details.progress?.[0];
|
|
6057
6112
|
if (!progress)
|
|
@@ -6593,7 +6648,7 @@ export function createSubagentExecutor(deps) {
|
|
|
6593
6648
|
};
|
|
6594
6649
|
};
|
|
6595
6650
|
const nestedScope = nestedResolutionScopeForExecutor(deps);
|
|
6596
|
-
const sessionRoots =
|
|
6651
|
+
const sessionRoots = trustedSessionRoots(ctx.sessionManager.getSessionFile() ?? null, deps);
|
|
6597
6652
|
if (action === "debug.run") {
|
|
6598
6653
|
if (!targetRunId && !hasDirectoryTarget) {
|
|
6599
6654
|
return withBudget({ content: [{ type: "text", text: "action='debug.run' requires id, runId, or dir." }], isError: true, details: { mode: "management", results: [] } });
|
|
@@ -7035,12 +7090,20 @@ export function createSubagentExecutor(deps) {
|
|
|
7035
7090
|
// Forked children keep their requested thinking level. Signed Anthropic thinking
|
|
7036
7091
|
// blocks are stripped from the inherited transcript by the resolver (they are bound
|
|
7037
7092
|
// to the parent session), which is not a reason to disable the child's own reasoning.
|
|
7093
|
+
let sessionRunId;
|
|
7038
7094
|
try {
|
|
7039
7095
|
assertLaunchableAgentsAllowedBeforeFork();
|
|
7040
7096
|
const pruneSession = contextPolicy.usesFork && deps.config.forkContext?.mode === "pruned"
|
|
7041
7097
|
? await createPrunedForkSessionWriter(ctx, deps.config.forkContext, signal)
|
|
7042
7098
|
: undefined;
|
|
7043
|
-
const forkContextResolver = createForkContextResolver(ctx.sessionManager, contextPolicy.usesFork ? "fork" : undefined,
|
|
7099
|
+
const forkContextResolver = createForkContextResolver(ctx.sessionManager, contextPolicy.usesFork ? "fork" : undefined, {
|
|
7100
|
+
...(pruneSession ? { pruneSession } : {}),
|
|
7101
|
+
runDirName: () => {
|
|
7102
|
+
if (!sessionRunId)
|
|
7103
|
+
throw new Error("A fork session was requested before the launch's run id was assigned.");
|
|
7104
|
+
return sessionRunId;
|
|
7105
|
+
},
|
|
7106
|
+
});
|
|
7044
7107
|
prepareForkSessionForIndex = forkContextResolver.prepareSessionForIndex;
|
|
7045
7108
|
forkSessionFileForIndex = forkContextResolver.sessionFileForIndex;
|
|
7046
7109
|
}
|
|
@@ -7143,16 +7206,19 @@ export function createSubagentExecutor(deps) {
|
|
|
7143
7206
|
console.warn(`[pi-subagents] ${warning}`);
|
|
7144
7207
|
}
|
|
7145
7208
|
let sessionRoot;
|
|
7209
|
+
// Key the session directory by the id the run is recorded under: nested resume
|
|
7210
|
+
// accepts a session file only inside a directory named by that run's id.
|
|
7211
|
+
sessionRunId = effectiveAsync ? asyncRunId : runId;
|
|
7146
7212
|
if (effectiveParams.sessionDir) {
|
|
7147
7213
|
// An explicit sessionDir is a root keyed by this launch's run id so
|
|
7148
7214
|
// concurrent children resolve distinct per-child session files.
|
|
7149
|
-
sessionRoot = path.join(path.resolve(deps.expandTilde(effectiveParams.sessionDir)),
|
|
7215
|
+
sessionRoot = path.join(path.resolve(deps.expandTilde(effectiveParams.sessionDir)), sessionRunId);
|
|
7150
7216
|
}
|
|
7151
7217
|
else {
|
|
7152
7218
|
const baseSessionRoot = deps.config.defaultSessionDir
|
|
7153
7219
|
? path.resolve(deps.expandTilde(deps.config.defaultSessionDir))
|
|
7154
7220
|
: deps.getSubagentSessionRoot(parentSessionFile);
|
|
7155
|
-
sessionRoot = path.join(baseSessionRoot,
|
|
7221
|
+
sessionRoot = path.join(baseSessionRoot, sessionRunId);
|
|
7156
7222
|
}
|
|
7157
7223
|
try {
|
|
7158
7224
|
fs.mkdirSync(sessionRoot, { recursive: true });
|