@fitchmultz/pi-subagents 0.46.0 → 0.46.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 +6 -0
- package/dist/agents/agent-selection.js +5 -17
- package/dist/agents/agents.js +2 -2
- package/dist/extension/build-info.js +1 -1
- package/dist/runs/background/runner-step-output.js +1 -3
- package/dist/runs/shared/acceptance-finalization.js +2 -2
- package/dist/runs/shared/acceptance.js +1 -1
- package/dist/runs/shared/model-fallback.js +3 -0
- package/dist/runs/shared/parallel-utils.js +1 -21
- package/dist/runs/shared/single-output.js +1 -4
- package/dist/runs/shared/subagent-prompt-runtime.js +2 -2
- package/dist/runs/shared/workflow-policy.js +2 -1
- package/dist/shared/settings.js +1 -4
- package/docs/reference.md +1 -1
- package/package.json +1 -1
- package/src/agents/agent-selection.ts +8 -19
- package/src/agents/agents.ts +2 -2
- package/src/runs/background/runner-step-output.ts +1 -3
- package/src/runs/shared/acceptance-finalization.ts +2 -2
- package/src/runs/shared/acceptance.ts +0 -2
- package/src/runs/shared/model-fallback.ts +3 -0
- package/src/runs/shared/parallel-utils.ts +1 -25
- package/src/runs/shared/single-output.ts +0 -4
- package/src/runs/shared/subagent-prompt-runtime.ts +2 -2
- package/src/runs/shared/workflow-policy.ts +1 -1
- package/src/shared/settings.ts +1 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.46.1] - 2026-10-11
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- A child that fails with Node's bare `terminated` network error (the response stream dropped mid-body, common during Wi-Fi drops) now gets the same-model retry and configured fallback models like other transport failures, instead of failing outright. Pi already retries this error inside the child.
|
|
8
|
+
|
|
3
9
|
## [0.46.0] - 2026-10-11
|
|
4
10
|
|
|
5
11
|
### Added
|
|
@@ -1,25 +1,13 @@
|
|
|
1
|
-
export function
|
|
1
|
+
export function mergeAgents(userAgents, projectAgents, builtinAgents) {
|
|
2
2
|
const agentMap = new Map();
|
|
3
3
|
for (const agent of builtinAgents) {
|
|
4
4
|
agentMap.set(agent.name, agent);
|
|
5
5
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
agentMap.set(agent.name, agent);
|
|
9
|
-
}
|
|
10
|
-
for (const agent of projectAgents) {
|
|
11
|
-
agentMap.set(agent.name, agent);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
else if (scope === "user") {
|
|
15
|
-
for (const agent of userAgents) {
|
|
16
|
-
agentMap.set(agent.name, agent);
|
|
17
|
-
}
|
|
6
|
+
for (const agent of userAgents) {
|
|
7
|
+
agentMap.set(agent.name, agent);
|
|
18
8
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
agentMap.set(agent.name, agent);
|
|
22
|
-
}
|
|
9
|
+
for (const agent of projectAgents) {
|
|
10
|
+
agentMap.set(agent.name, agent);
|
|
23
11
|
}
|
|
24
12
|
return Array.from(agentMap.values());
|
|
25
13
|
}
|
package/dist/agents/agents.js
CHANGED
|
@@ -9,7 +9,7 @@ import { loadAgentsFromDir, clearAgentDiagnosticsForDirs, agentDiagnosticsForDir
|
|
|
9
9
|
import { EMPTY_SUBAGENT_SETTINGS, readSubagentSettings, applyBuiltinOverrides, } from "./builtin-overrides.js";
|
|
10
10
|
import { loadConfiguredPackageAgents } from "./package-agents.js";
|
|
11
11
|
import { parseChain, parseJsonChain } from "./chain-serializer.js";
|
|
12
|
-
import {
|
|
12
|
+
import { mergeAgents } from "./agent-selection.js";
|
|
13
13
|
import { errorMessage } from "./config-values.js";
|
|
14
14
|
export { buildRuntimeName, frontmatterNameForConfig, parsePackageName } from "./identity.js";
|
|
15
15
|
export { defaultSystemPromptMode, defaultInheritProjectContext, defaultInheritSkills, } from "./agent-defaults.js";
|
|
@@ -80,7 +80,7 @@ export function discoverAgents(cwd, scope, options = {}) {
|
|
|
80
80
|
const paths = profilePaths(cwd, options);
|
|
81
81
|
const profiles = discoverLocalProfiles(paths, scope);
|
|
82
82
|
const packages = loadConfiguredPackageAgents(cwd, scope, options);
|
|
83
|
-
const agents =
|
|
83
|
+
const agents = mergeAgents(profiles.user, profiles.project, [
|
|
84
84
|
...profiles.builtin,
|
|
85
85
|
...packages.agents,
|
|
86
86
|
]).filter((agent) => agent.disabled !== true);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const EXTENSION_BUILD = Object.freeze({"version":"0.46.
|
|
1
|
+
export const EXTENSION_BUILD = Object.freeze({"version":"0.46.1","sha256":"c04a71cfa57811b1a2d45d43b71aa2edd0f47cd86f3b8ca91730a87ffe854304"});
|
|
@@ -59,9 +59,7 @@ export function publishStepOutput(step, ctx, state) {
|
|
|
59
59
|
originalPath.length > 0 &&
|
|
60
60
|
step.outputMode !== "file-only" &&
|
|
61
61
|
step.outputPathFromAgentDefault === true;
|
|
62
|
-
const cleanup = shouldConsume
|
|
63
|
-
? cleanupSingleOutputFile(originalPath, output, undefined)
|
|
64
|
-
: undefined;
|
|
62
|
+
const cleanup = shouldConsume ? cleanupSingleOutputFile(originalPath, output) : undefined;
|
|
65
63
|
const savedPath = relocateOutput(ctx, originalPath, cleanup);
|
|
66
64
|
const reference = outputReference(savedPath, state.resolvedOutput.fullOutput, cleanup);
|
|
67
65
|
const displayOutput = finalizeSingleOutput({
|
|
@@ -287,7 +287,7 @@ export function formatAcceptanceFinalizationPrompt(input) {
|
|
|
287
287
|
lines.push(...initialReviewEvidence(input.initialOutput, input.initialLedger, input.previousFailure), "", ...finalizationInstructions(input.nativeReport === true, input.outputSchema));
|
|
288
288
|
return lines.join("\n");
|
|
289
289
|
}
|
|
290
|
-
|
|
290
|
+
function createFinalizationTurn(input) {
|
|
291
291
|
const failureMessage = acceptanceFailureMessage(input.ledger);
|
|
292
292
|
return {
|
|
293
293
|
turn: input.turn,
|
|
@@ -320,7 +320,7 @@ export function createFinalizationProcessFailureTurn(input) {
|
|
|
320
320
|
failureMessage: `Acceptance rejected: ${input.message}`,
|
|
321
321
|
};
|
|
322
322
|
}
|
|
323
|
-
|
|
323
|
+
function attachFinalizationToLedger(input) {
|
|
324
324
|
return {
|
|
325
325
|
...input.authoritativeLedger,
|
|
326
326
|
...(input.initialLedger.childReport
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { acceptanceInputFromResolved, acceptanceSelfReviewConfig, formatAcceptancePrompt, resolveEffectiveAcceptance, shouldRunAcceptanceFinalization, validateAcceptanceInput, } from "./acceptance-contract.js";
|
|
2
2
|
export { parseAcceptanceReport, validateAcceptanceReportShape, stripAcceptanceReport, } from "./acceptance-reports.js";
|
|
3
3
|
export { acceptanceFailureMessage, acceptanceHumanAction, evaluateAcceptance, evaluateAcceptanceReport, } from "./acceptance-evaluation.js";
|
|
4
|
-
export {
|
|
4
|
+
export { buildFinalizationProcessFailureLedger, createFinalizationProcessFailureTurn, createFinalizationReportRuntime, readFinalizationReport, formatUnconfirmedFinalizationOutput, formatAcceptanceFinalizationPrompt, evaluateRunAcceptance, resolveExecutionOutcome, resolveFinalizationOutput, } from "./acceptance-finalization.js";
|
|
@@ -115,6 +115,9 @@ const SAME_MODEL_RECOVERY_PATTERNS = [
|
|
|
115
115
|
/socket/i,
|
|
116
116
|
/connection/i,
|
|
117
117
|
/fetch failed/i,
|
|
118
|
+
// Node fetch reports a response body cut off mid-stream as bare "terminated"; Pi retries it too.
|
|
119
|
+
// Match the whole message: process failures embed stderr and paths that can contain the word.
|
|
120
|
+
/^\s*terminated\s*$/i,
|
|
118
121
|
/network/i,
|
|
119
122
|
/http idle/i,
|
|
120
123
|
/timed? out/i,
|
|
@@ -21,32 +21,12 @@ export function flattenSteps(steps) {
|
|
|
21
21
|
}
|
|
22
22
|
return flat;
|
|
23
23
|
}
|
|
24
|
-
export { mapConcurrent } from "../../shared/concurrency.js";
|
|
25
24
|
function parallelResultStatus(result, hasOutput) {
|
|
26
25
|
const error = result.error ?? "";
|
|
27
|
-
const suffix = error.length > 0 ? `: ${error}` : "";
|
|
28
|
-
if (result.exitCode === -1) {
|
|
29
|
-
return `SKIPPED${suffix}`;
|
|
30
|
-
}
|
|
31
|
-
if (result.exitCode !== 0 && result.exitCode !== null) {
|
|
32
|
-
return `FAILED (exit code ${result.exitCode})${suffix}`;
|
|
33
|
-
}
|
|
34
26
|
if (error.length > 0) {
|
|
35
27
|
return `WARNING: ${error}`;
|
|
36
28
|
}
|
|
37
|
-
return
|
|
38
|
-
}
|
|
39
|
-
function emptyOutputStatus(result, hasOutput) {
|
|
40
|
-
if (hasOutput) {
|
|
41
|
-
return "";
|
|
42
|
-
}
|
|
43
|
-
const target = result.outputTargetPath ?? "";
|
|
44
|
-
if (target.length === 0) {
|
|
45
|
-
return "EMPTY OUTPUT (no textual response returned)";
|
|
46
|
-
}
|
|
47
|
-
return result.outputTargetExists === false
|
|
48
|
-
? `EMPTY OUTPUT (expected output file missing: ${target})`
|
|
49
|
-
: "";
|
|
29
|
+
return hasOutput ? "" : "EMPTY OUTPUT (no textual response returned)";
|
|
50
30
|
}
|
|
51
31
|
export function aggregateParallelOutputs(results, headerFormat = (i, agent) => `=== Parallel Task ${i + 1} (${agent}) ===`) {
|
|
52
32
|
return results
|
|
@@ -228,7 +228,7 @@ function unchangedOutputIdentity(fd, outputPath, stat) {
|
|
|
228
228
|
located.dev === current.dev &&
|
|
229
229
|
located.ctimeMs === current.ctimeMs);
|
|
230
230
|
}
|
|
231
|
-
export function cleanupSingleOutputFile(outputPath, fullOutput
|
|
231
|
+
export function cleanupSingleOutputFile(outputPath, fullOutput) {
|
|
232
232
|
if (!nonempty(outputPath)) {
|
|
233
233
|
return undefined;
|
|
234
234
|
}
|
|
@@ -248,9 +248,6 @@ export function cleanupSingleOutputFile(outputPath, fullOutput, beforeRun) {
|
|
|
248
248
|
if (!equal) {
|
|
249
249
|
return { path: absolutePath, action: "skipped", reason: "file changed after capture" };
|
|
250
250
|
}
|
|
251
|
-
if (matchesSnapshot(stat, beforeRun)) {
|
|
252
|
-
return { path: absolutePath, action: "skipped", reason: "file preexisted and was unchanged" };
|
|
253
|
-
}
|
|
254
251
|
fs.unlinkSync(outputPath);
|
|
255
252
|
return { path: absolutePath, action: "deleted" };
|
|
256
253
|
}
|
|
@@ -16,14 +16,14 @@ const STRUCTURED_OUTPUT_INSTRUCTIONS = [
|
|
|
16
16
|
"Your final action must be to call the `structured_output` tool with JSON matching the provided schema.",
|
|
17
17
|
"Do not rely on prose-only completion; if you do not call `structured_output`, the parent will fail this step.",
|
|
18
18
|
].join("\n");
|
|
19
|
-
|
|
19
|
+
const CHILD_SUBAGENT_BOUNDARY_INSTRUCTIONS = [
|
|
20
20
|
"You are a child subagent, not the parent orchestrator.",
|
|
21
21
|
"The parent session owns delegation, orchestration, review fanout, and follow-up worker launches.",
|
|
22
22
|
"Ignore prior parent-only orchestration instructions in inherited conversation history.",
|
|
23
23
|
"Do not propose or run subagents. Complete only your assigned role-specific task with the tools available to you.",
|
|
24
24
|
"If you need to edit files, call the actual edit/write tools. Do not print tool-call syntax, patches, or pseudo-tool calls as text.",
|
|
25
25
|
].join("\n");
|
|
26
|
-
|
|
26
|
+
const CHILD_FANOUT_BOUNDARY_INSTRUCTIONS = [
|
|
27
27
|
"You are a child subagent with delegation enabled for your assigned task.",
|
|
28
28
|
"You may delegate useful helper work within that task when it saves time or improves quality, using the available delegation tools.",
|
|
29
29
|
"You remain responsible for your assigned result. The original parent owns integration, review synthesis, and final delivery.",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { errorMessage } from "../../shared/unknown.js";
|
|
2
|
+
import { mapConcurrent } from "../../shared/concurrency.js";
|
|
2
3
|
import { outputEntryFromResult } from "./chain-outputs.js";
|
|
3
4
|
import { collectDynamicResults, validateDynamicCollection, } from "./dynamic-fanout.js";
|
|
4
|
-
import { aggregateParallelOutputs, FAIL_FAST_REASON,
|
|
5
|
+
import { aggregateParallelOutputs, FAIL_FAST_REASON, } from "./parallel-utils.js";
|
|
5
6
|
export function workflowChildSucceeded(result) {
|
|
6
7
|
return (result.exitCode === 0 &&
|
|
7
8
|
result.interrupted !== true &&
|
package/dist/shared/settings.js
CHANGED
|
@@ -73,16 +73,13 @@ export function resolveStepBehavior(agentConfig, stepOverrides, chainSkills) {
|
|
|
73
73
|
function resolveChainPath(filePath, chainDir) {
|
|
74
74
|
return path.isAbsolute(filePath) ? filePath : path.join(chainDir, filePath);
|
|
75
75
|
}
|
|
76
|
-
/**
|
|
76
|
+
/** Compose read paths and progress instructions for a chain child. */
|
|
77
77
|
export function buildChainInstructions(behavior, chainDir, isFirstProgressAgent) {
|
|
78
78
|
const prefixParts = [];
|
|
79
79
|
const suffixParts = [];
|
|
80
80
|
if (behavior.reads !== false && behavior.reads.length > 0) {
|
|
81
81
|
prefixParts.push(`[Read from: ${behavior.reads.map((file) => resolveChainPath(file, chainDir)).join(", ")}]`);
|
|
82
82
|
}
|
|
83
|
-
if (behavior.output !== false && behavior.output.length > 0) {
|
|
84
|
-
prefixParts.push(`[Write to: ${resolveChainPath(behavior.output, chainDir)}]`);
|
|
85
|
-
}
|
|
86
83
|
if (behavior.progress) {
|
|
87
84
|
const progressPath = path.join(chainDir, "progress.md");
|
|
88
85
|
suffixParts.push(isFirstProgressAgent
|
package/docs/reference.md
CHANGED
|
@@ -663,7 +663,7 @@ Important fields:
|
|
|
663
663
|
| `allowSubagents` | Opt-in child-safe nested delegation. Disabled in bundled profiles and still bounded by `maxSubagentDepth`. |
|
|
664
664
|
| `extensions` | Omitted means normal extensions; empty means no extensions; comma-separated values allowlist specific extensions. |
|
|
665
665
|
| `model` | Default model. Bare ids prefer the current provider when possible, then unique registry matches. |
|
|
666
|
-
| `fallbackModels` | Ordered backup models for provider/model failures such as quota, usage limit, auth, timeout, or unavailable model. The shared driver first retries the same model once for
|
|
666
|
+
| `fallbackModels` | Ordered backup models for provider/model failures such as quota, usage limit, auth, timeout, or unavailable model. The shared driver first retries the same model once for transport failures such as WebSocket/stream/socket timeouts, `terminated` drops and SIGTERM-style exits, then falls back when appropriate. Ordinary task failures do not trigger retry or fallback. |
|
|
667
667
|
| `thinking` | Appended as a `:level` suffix at runtime unless a suffix is already present. |
|
|
668
668
|
| `systemPromptMode` | `append` by default; `replace` discards Pi's base prompt. |
|
|
669
669
|
| `inheritProjectContext` | Uses Pi's native context-file loading policy; `false` passes `--no-context-files`. |
|
package/package.json
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AgentConfig } from "../shared/types/config.ts";
|
|
2
2
|
|
|
3
|
-
export function
|
|
4
|
-
scope: AgentScope,
|
|
3
|
+
export function mergeAgents(
|
|
5
4
|
userAgents: readonly AgentConfig[],
|
|
6
5
|
projectAgents: readonly AgentConfig[],
|
|
7
|
-
builtinAgents: readonly AgentConfig[]
|
|
6
|
+
builtinAgents: readonly AgentConfig[],
|
|
8
7
|
): AgentConfig[] {
|
|
9
8
|
const agentMap = new Map<string, AgentConfig>();
|
|
10
9
|
|
|
@@ -12,21 +11,11 @@ export function mergeAgentsForScope(
|
|
|
12
11
|
agentMap.set(agent.name, agent);
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
agentMap.set(agent.name, agent);
|
|
21
|
-
}
|
|
22
|
-
} else if (scope === "user") {
|
|
23
|
-
for (const agent of userAgents) {
|
|
24
|
-
agentMap.set(agent.name, agent);
|
|
25
|
-
}
|
|
26
|
-
} else {
|
|
27
|
-
for (const agent of projectAgents) {
|
|
28
|
-
agentMap.set(agent.name, agent);
|
|
29
|
-
}
|
|
14
|
+
for (const agent of userAgents) {
|
|
15
|
+
agentMap.set(agent.name, agent);
|
|
16
|
+
}
|
|
17
|
+
for (const agent of projectAgents) {
|
|
18
|
+
agentMap.set(agent.name, agent);
|
|
30
19
|
}
|
|
31
20
|
|
|
32
21
|
return Array.from(agentMap.values());
|
package/src/agents/agents.ts
CHANGED
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
} from "./builtin-overrides.ts";
|
|
41
41
|
import { loadConfiguredPackageAgents } from "./package-agents.ts";
|
|
42
42
|
import { parseChain, parseJsonChain } from "./chain-serializer.ts";
|
|
43
|
-
import {
|
|
43
|
+
import { mergeAgents } from "./agent-selection.ts";
|
|
44
44
|
import { errorMessage } from "./config-values.ts";
|
|
45
45
|
export { buildRuntimeName, frontmatterNameForConfig, parsePackageName } from "./identity.ts";
|
|
46
46
|
|
|
@@ -160,7 +160,7 @@ export function discoverAgents(
|
|
|
160
160
|
const paths = profilePaths(cwd, options);
|
|
161
161
|
const profiles = discoverLocalProfiles(paths, scope);
|
|
162
162
|
const packages = loadConfiguredPackageAgents(cwd, scope, options);
|
|
163
|
-
const agents =
|
|
163
|
+
const agents = mergeAgents(profiles.user, profiles.project, [
|
|
164
164
|
...profiles.builtin,
|
|
165
165
|
...packages.agents,
|
|
166
166
|
]).filter((agent) => agent.disabled !== true);
|
|
@@ -110,9 +110,7 @@ export function publishStepOutput(
|
|
|
110
110
|
originalPath.length > 0 &&
|
|
111
111
|
step.outputMode !== "file-only" &&
|
|
112
112
|
step.outputPathFromAgentDefault === true;
|
|
113
|
-
const cleanup = shouldConsume
|
|
114
|
-
? cleanupSingleOutputFile(originalPath, output, undefined)
|
|
115
|
-
: undefined;
|
|
113
|
+
const cleanup = shouldConsume ? cleanupSingleOutputFile(originalPath, output) : undefined;
|
|
116
114
|
const savedPath = relocateOutput(ctx, originalPath, cleanup);
|
|
117
115
|
const reference = outputReference(savedPath, state.resolvedOutput.fullOutput, cleanup);
|
|
118
116
|
const displayOutput = finalizeSingleOutput({
|
|
@@ -402,7 +402,7 @@ export function formatAcceptanceFinalizationPrompt(input: {
|
|
|
402
402
|
return lines.join("\n");
|
|
403
403
|
}
|
|
404
404
|
|
|
405
|
-
|
|
405
|
+
function createFinalizationTurn(input: {
|
|
406
406
|
readonly turn: number;
|
|
407
407
|
readonly prompt: string;
|
|
408
408
|
readonly rawOutput: string;
|
|
@@ -447,7 +447,7 @@ export function createFinalizationProcessFailureTurn(input: {
|
|
|
447
447
|
};
|
|
448
448
|
}
|
|
449
449
|
|
|
450
|
-
|
|
450
|
+
function attachFinalizationToLedger(input: {
|
|
451
451
|
readonly initialLedger: AcceptanceLedger;
|
|
452
452
|
readonly authoritativeLedger: AcceptanceLedger;
|
|
453
453
|
readonly turns: readonly AcceptanceFinalizationTurn[];
|
|
@@ -18,10 +18,8 @@ export {
|
|
|
18
18
|
evaluateAcceptanceReport,
|
|
19
19
|
} from "./acceptance-evaluation.ts";
|
|
20
20
|
export {
|
|
21
|
-
attachFinalizationToLedger,
|
|
22
21
|
buildFinalizationProcessFailureLedger,
|
|
23
22
|
createFinalizationProcessFailureTurn,
|
|
24
|
-
createFinalizationTurn,
|
|
25
23
|
createFinalizationReportRuntime,
|
|
26
24
|
type FinalizationReportSubmission,
|
|
27
25
|
readFinalizationReport,
|
|
@@ -154,6 +154,9 @@ const SAME_MODEL_RECOVERY_PATTERNS = [
|
|
|
154
154
|
/socket/i,
|
|
155
155
|
/connection/i,
|
|
156
156
|
/fetch failed/i,
|
|
157
|
+
// Node fetch reports a response body cut off mid-stream as bare "terminated"; Pi retries it too.
|
|
158
|
+
// Match the whole message: process failures embed stderr and paths that can contain the word.
|
|
159
|
+
/^\s*terminated\s*$/i,
|
|
157
160
|
/network/i,
|
|
158
161
|
/http idle/i,
|
|
159
162
|
/timed? out/i,
|
|
@@ -44,8 +44,6 @@ export function flattenSteps(
|
|
|
44
44
|
return flat;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
export { mapConcurrent } from "../../shared/concurrency.ts";
|
|
48
|
-
|
|
49
47
|
export interface ParallelTaskResult {
|
|
50
48
|
agent: string;
|
|
51
49
|
taskIndex?: number;
|
|
@@ -54,8 +52,6 @@ export interface ParallelTaskResult {
|
|
|
54
52
|
error?: string;
|
|
55
53
|
model?: string;
|
|
56
54
|
attemptedModels?: string[];
|
|
57
|
-
outputTargetPath?: string;
|
|
58
|
-
outputTargetExists?: boolean;
|
|
59
55
|
}
|
|
60
56
|
|
|
61
57
|
function parallelResultStatus(
|
|
@@ -63,30 +59,10 @@ function parallelResultStatus(
|
|
|
63
59
|
hasOutput: boolean,
|
|
64
60
|
): string {
|
|
65
61
|
const error = result.error ?? "";
|
|
66
|
-
const suffix = error.length > 0 ? `: ${error}` : "";
|
|
67
|
-
if (result.exitCode === -1) {
|
|
68
|
-
return `SKIPPED${suffix}`;
|
|
69
|
-
}
|
|
70
|
-
if (result.exitCode !== 0 && result.exitCode !== null) {
|
|
71
|
-
return `FAILED (exit code ${result.exitCode})${suffix}`;
|
|
72
|
-
}
|
|
73
62
|
if (error.length > 0) {
|
|
74
63
|
return `WARNING: ${error}`;
|
|
75
64
|
}
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function emptyOutputStatus(result: ReadonlyInput<ParallelTaskResult>, hasOutput: boolean): string {
|
|
80
|
-
if (hasOutput) {
|
|
81
|
-
return "";
|
|
82
|
-
}
|
|
83
|
-
const target = result.outputTargetPath ?? "";
|
|
84
|
-
if (target.length === 0) {
|
|
85
|
-
return "EMPTY OUTPUT (no textual response returned)";
|
|
86
|
-
}
|
|
87
|
-
return result.outputTargetExists === false
|
|
88
|
-
? `EMPTY OUTPUT (expected output file missing: ${target})`
|
|
89
|
-
: "";
|
|
65
|
+
return hasOutput ? "" : "EMPTY OUTPUT (no textual response returned)";
|
|
90
66
|
}
|
|
91
67
|
|
|
92
68
|
export function aggregateParallelOutputs(
|
|
@@ -325,7 +325,6 @@ function unchangedOutputIdentity(fd: number, outputPath: string, stat: fs.Stats)
|
|
|
325
325
|
export function cleanupSingleOutputFile(
|
|
326
326
|
outputPath: string | undefined,
|
|
327
327
|
fullOutput: string,
|
|
328
|
-
beforeRun: SingleOutputSnapshot | undefined,
|
|
329
328
|
): SingleOutputCleanupResult | undefined {
|
|
330
329
|
if (!nonempty(outputPath)) {
|
|
331
330
|
return undefined;
|
|
@@ -345,9 +344,6 @@ export function cleanupSingleOutputFile(
|
|
|
345
344
|
if (!equal) {
|
|
346
345
|
return { path: absolutePath, action: "skipped", reason: "file changed after capture" };
|
|
347
346
|
}
|
|
348
|
-
if (matchesSnapshot(stat, beforeRun)) {
|
|
349
|
-
return { path: absolutePath, action: "skipped", reason: "file preexisted and was unchanged" };
|
|
350
|
-
}
|
|
351
347
|
fs.unlinkSync(outputPath);
|
|
352
348
|
return { path: absolutePath, action: "deleted" };
|
|
353
349
|
} catch (error) {
|
|
@@ -24,7 +24,7 @@ const STRUCTURED_OUTPUT_INSTRUCTIONS = [
|
|
|
24
24
|
"Do not rely on prose-only completion; if you do not call `structured_output`, the parent will fail this step.",
|
|
25
25
|
].join("\n");
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
const CHILD_SUBAGENT_BOUNDARY_INSTRUCTIONS = [
|
|
28
28
|
"You are a child subagent, not the parent orchestrator.",
|
|
29
29
|
"The parent session owns delegation, orchestration, review fanout, and follow-up worker launches.",
|
|
30
30
|
"Ignore prior parent-only orchestration instructions in inherited conversation history.",
|
|
@@ -32,7 +32,7 @@ export const CHILD_SUBAGENT_BOUNDARY_INSTRUCTIONS = [
|
|
|
32
32
|
"If you need to edit files, call the actual edit/write tools. Do not print tool-call syntax, patches, or pseudo-tool calls as text.",
|
|
33
33
|
].join("\n");
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
const CHILD_FANOUT_BOUNDARY_INSTRUCTIONS = [
|
|
36
36
|
"You are a child subagent with delegation enabled for your assigned task.",
|
|
37
37
|
"You may delegate useful helper work within that task when it saves time or improves quality, using the available delegation tools.",
|
|
38
38
|
"You remain responsible for your assigned result. The original parent owns integration, review synthesis, and final delivery.",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { errorMessage } from "../../shared/unknown.ts";
|
|
2
|
+
import { mapConcurrent } from "../../shared/concurrency.ts";
|
|
2
3
|
import type { DynamicParallelStep } from "../../shared/types/workflow.ts";
|
|
3
4
|
import type {
|
|
4
5
|
AcceptanceLedger,
|
|
@@ -17,7 +18,6 @@ import {
|
|
|
17
18
|
import {
|
|
18
19
|
aggregateParallelOutputs,
|
|
19
20
|
FAIL_FAST_REASON,
|
|
20
|
-
mapConcurrent,
|
|
21
21
|
type ParallelTaskResult,
|
|
22
22
|
} from "./parallel-utils.ts";
|
|
23
23
|
|
package/src/shared/settings.ts
CHANGED
|
@@ -126,7 +126,7 @@ export function resolveStepBehavior(
|
|
|
126
126
|
function resolveChainPath(filePath: string, chainDir: string): string {
|
|
127
127
|
return path.isAbsolute(filePath) ? filePath : path.join(chainDir, filePath);
|
|
128
128
|
}
|
|
129
|
-
/**
|
|
129
|
+
/** Compose read paths and progress instructions for a chain child. */
|
|
130
130
|
export function buildChainInstructions(
|
|
131
131
|
behavior: ResolvedStepBehavior,
|
|
132
132
|
chainDir: string,
|
|
@@ -139,9 +139,6 @@ export function buildChainInstructions(
|
|
|
139
139
|
`[Read from: ${behavior.reads.map((file) => resolveChainPath(file, chainDir)).join(", ")}]`,
|
|
140
140
|
);
|
|
141
141
|
}
|
|
142
|
-
if (behavior.output !== false && behavior.output.length > 0) {
|
|
143
|
-
prefixParts.push(`[Write to: ${resolveChainPath(behavior.output, chainDir)}]`);
|
|
144
|
-
}
|
|
145
142
|
if (behavior.progress) {
|
|
146
143
|
const progressPath = path.join(chainDir, "progress.md");
|
|
147
144
|
suffixParts.push(
|