pi-cohort 7.4.0 → 7.4.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
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [7.4.1] - 2026-10-19
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Non-BLOCKED file-only failures and save errors return an artifact pointer or an explicit not-preserved reason instead of the child reply inline; BLOCKED handoffs and inline mode are unchanged. (#24)
|
|
7
|
+
|
|
3
8
|
## [7.4.0] - 2026-10-19
|
|
4
9
|
|
|
5
10
|
### Added
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cohort",
|
|
3
|
-
"version": "7.4.
|
|
3
|
+
"version": "7.4.1",
|
|
4
4
|
"description": "Delegate Pi work to focused child agents: code review, scouting, implementation, parallel audits, saved chains, and background jobs.",
|
|
5
5
|
"author": "Jacek Juraszek",
|
|
6
6
|
"license": "MIT",
|
|
@@ -7,7 +7,7 @@ import type { Message } from "@earendil-works/pi-ai";
|
|
|
7
7
|
import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
8
8
|
import { appendJsonl, getArtifactPaths } from "../../shared/artifacts.ts";
|
|
9
9
|
import { getPiSpawnCommand, parsePiRuntime, PiRuntimeLaunchError, type PiRuntimeIdentity } from "../shared/pi-spawn.ts";
|
|
10
|
-
import { captureSingleOutputSnapshot, finalizeSingleOutput, formatSavedOutputReference, resolveSingleOutput, type SingleOutputSnapshot } from "../shared/single-output.ts";
|
|
10
|
+
import { captureSingleOutputSnapshot, finalizeSingleOutput, formatFileOnlyFailure, formatSavedOutputReference, resolveSingleOutput, type SingleOutputSnapshot } from "../shared/single-output.ts";
|
|
11
11
|
import {
|
|
12
12
|
type ActivityState,
|
|
13
13
|
type ArtifactConfig,
|
|
@@ -1007,6 +1007,8 @@ export async function runSingleStep(
|
|
|
1007
1007
|
let acceptanceCanFailRun = false;
|
|
1008
1008
|
let effectiveFinalExitCode = resolvedOutput.saveError ? 1 : finalResult?.exitCode ?? 1;
|
|
1009
1009
|
let effectiveFinalError = finalResult?.error;
|
|
1010
|
+
let preservedAt: string | undefined;
|
|
1011
|
+
let notPreservedReason = "artifact output disabled";
|
|
1010
1012
|
if (resolvedOutput.saveError) {
|
|
1011
1013
|
effectiveFinalError = effectiveFinalError
|
|
1012
1014
|
? `${effectiveFinalError}\n${resolvedOutput.saveError}`
|
|
@@ -1037,7 +1039,14 @@ export async function runSingleStep(
|
|
|
1037
1039
|
|
|
1038
1040
|
if (artifactPaths && ctx.artifactConfig?.enabled !== false) {
|
|
1039
1041
|
if (ctx.artifactConfig?.includeOutput !== false) {
|
|
1040
|
-
|
|
1042
|
+
try {
|
|
1043
|
+
fs.writeFileSync(artifactPaths.outputPath, output, "utf-8");
|
|
1044
|
+
preservedAt = artifactPaths.outputPath;
|
|
1045
|
+
} catch (error) {
|
|
1046
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1047
|
+
notPreservedReason = `artifact write failed: ${message}`;
|
|
1048
|
+
throw error;
|
|
1049
|
+
}
|
|
1041
1050
|
}
|
|
1042
1051
|
if (ctx.artifactConfig?.includeMetadata !== false) {
|
|
1043
1052
|
fs.writeFileSync(
|
|
@@ -1063,11 +1072,14 @@ export async function runSingleStep(
|
|
|
1063
1072
|
effectiveFinalError = effectiveFinalError ? `${effectiveFinalError}\n${message}` : message;
|
|
1064
1073
|
}
|
|
1065
1074
|
|
|
1075
|
+
const outputBeforeExternalFinalization = outputForSummary;
|
|
1076
|
+
let externalOutputPersisted = false;
|
|
1066
1077
|
// Native runs have runPiStreaming write outputFile live; external runs persist it
|
|
1067
1078
|
// before their surface can be destructively closed as delivered.
|
|
1068
1079
|
if (externalOwner) {
|
|
1069
1080
|
try {
|
|
1070
1081
|
fs.writeFileSync(ctx.outputFile, outputForSummary, "utf-8");
|
|
1082
|
+
externalOutputPersisted = true;
|
|
1071
1083
|
} catch (error) {
|
|
1072
1084
|
const message = safeExternalFailure("output persistence", error);
|
|
1073
1085
|
effectiveFinalExitCode = 1;
|
|
@@ -1090,6 +1102,30 @@ export async function runSingleStep(
|
|
|
1090
1102
|
executionSurface = { handle: finishResult.handle, retained: finishResult.retained };
|
|
1091
1103
|
}
|
|
1092
1104
|
|
|
1105
|
+
if (step.outputMode === "file-only"
|
|
1106
|
+
&& (effectiveFinalExitCode !== 0 || resolvedOutput.saveError)
|
|
1107
|
+
&& !blockedLine(effectiveFinalError ?? "")) {
|
|
1108
|
+
const pointer = formatFileOnlyFailure({
|
|
1109
|
+
outputPath: step.outputPath,
|
|
1110
|
+
saveError: resolvedOutput.saveError,
|
|
1111
|
+
...(preservedAt !== undefined ? { preservedAt } : { notPreservedReason }),
|
|
1112
|
+
});
|
|
1113
|
+
outputForSummary = attemptNotes.length > 0
|
|
1114
|
+
? `${attemptNotes.join("\n")}\n\n${pointer}`
|
|
1115
|
+
: pointer;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// finish can turn a saved success into a failure after the pre-close log write.
|
|
1119
|
+
if (externalOutputPersisted && outputForSummary !== outputBeforeExternalFinalization) {
|
|
1120
|
+
try {
|
|
1121
|
+
fs.writeFileSync(ctx.outputFile, outputForSummary, "utf-8");
|
|
1122
|
+
} catch (error) {
|
|
1123
|
+
const message = safeExternalFailure("output persistence", error);
|
|
1124
|
+
effectiveFinalExitCode = 1;
|
|
1125
|
+
effectiveFinalError = effectiveFinalError ? `${effectiveFinalError}\n${message}` : message;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1093
1129
|
return {
|
|
1094
1130
|
agent: step.agent,
|
|
1095
1131
|
output: outputForSummary,
|
|
@@ -46,7 +46,7 @@ import { PINNED_AGENT_DIR_OVERRIDE } from "../../shared/agent-dir.ts";
|
|
|
46
46
|
import { createJsonlWriter } from "../../shared/jsonl-writer.ts";
|
|
47
47
|
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
48
48
|
import { buildPiArgs, cleanupTempDir } from "../shared/pi-args.ts";
|
|
49
|
-
import { captureSingleOutputSnapshot, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
49
|
+
import { captureSingleOutputSnapshot, formatFileOnlyFailure, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
50
50
|
import {
|
|
51
51
|
buildModelCandidates,
|
|
52
52
|
formatModelAttemptNote,
|
|
@@ -815,6 +815,8 @@ export async function runSync(
|
|
|
815
815
|
} satisfies SingleResult;
|
|
816
816
|
|
|
817
817
|
let postLoopError: unknown;
|
|
818
|
+
let preservedAt: string | undefined;
|
|
819
|
+
let notPreservedReason = "artifact output disabled";
|
|
818
820
|
try {
|
|
819
821
|
result.usage = aggregateUsage;
|
|
820
822
|
result.attemptedModels = attemptedModels.length > 0 ? attemptedModels : undefined;
|
|
@@ -834,7 +836,14 @@ export async function runSync(
|
|
|
834
836
|
if (artifactPathsResult && options.artifactConfig?.enabled !== false) {
|
|
835
837
|
result.artifactPaths = artifactPathsResult;
|
|
836
838
|
if (options.artifactConfig?.includeOutput !== false) {
|
|
837
|
-
|
|
839
|
+
try {
|
|
840
|
+
writeArtifact(artifactPathsResult.outputPath, artifactOutputByResult.get(result) ?? result.finalOutput ?? "");
|
|
841
|
+
preservedAt = artifactPathsResult.outputPath;
|
|
842
|
+
} catch (error) {
|
|
843
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
844
|
+
notPreservedReason = `artifact write failed: ${message}`;
|
|
845
|
+
throw error;
|
|
846
|
+
}
|
|
838
847
|
}
|
|
839
848
|
if (options.artifactConfig?.includeMetadata !== false) {
|
|
840
849
|
writeMetadata(artifactPathsResult.metadataPath, {
|
|
@@ -912,9 +921,19 @@ export async function runSync(
|
|
|
912
921
|
: safeMessage;
|
|
913
922
|
result.executionSurface ??= { handle: externalOwner.surface, retained: true };
|
|
914
923
|
}
|
|
915
|
-
} else if (postLoopError) {
|
|
916
|
-
throw postLoopError;
|
|
917
924
|
}
|
|
918
925
|
|
|
926
|
+
if (options.outputMode === "file-only"
|
|
927
|
+
&& (result.exitCode !== 0 || result.outputSaveError)
|
|
928
|
+
&& !blockedLine(result.error ?? "")) {
|
|
929
|
+
result.finalOutput = formatFileOnlyFailure({
|
|
930
|
+
outputPath: options.outputPath,
|
|
931
|
+
saveError: result.outputSaveError,
|
|
932
|
+
...(preservedAt !== undefined ? { preservedAt } : { notPreservedReason }),
|
|
933
|
+
});
|
|
934
|
+
result.truncation = undefined;
|
|
935
|
+
}
|
|
936
|
+
if (!externalOwner && postLoopError) throw postLoopError;
|
|
937
|
+
|
|
919
938
|
return result;
|
|
920
939
|
}
|
|
@@ -39,6 +39,7 @@ import { createForkContextResolver } from "../../shared/fork-context.ts";
|
|
|
39
39
|
import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
|
|
40
40
|
import { formatControlNoticeMessage, resolveControlConfig, shouldNotifyControlEvent } from "../shared/subagent-control.ts";
|
|
41
41
|
import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeTopLevelOutput, resolveParallelTaskOutputPath, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
42
|
+
import { blockedLine } from "../shared/completion-guard.ts";
|
|
42
43
|
import { compactForegroundDetails, getSingleResultOutput, mapConcurrent, readStatus, resolveChildCwd } from "../../shared/utils.ts";
|
|
43
44
|
import { resolveSubagentResultStatus } from "../shared/result-children.ts";
|
|
44
45
|
import { buildRevivedAsyncTask, resolveAsyncResumeTarget } from "../background/async-resume.ts";
|
|
@@ -2064,7 +2065,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
2064
2065
|
if (r.artifactPaths) allArtifactPaths.push(r.artifactPaths);
|
|
2065
2066
|
|
|
2066
2067
|
const fullOutput = getSingleResultOutput(r);
|
|
2067
|
-
const
|
|
2068
|
+
const displayOutput = r.outputMode === "file-only" ? r.finalOutput : finalizeSingleOutput({
|
|
2068
2069
|
fullOutput,
|
|
2069
2070
|
truncatedOutput: r.truncation?.text,
|
|
2070
2071
|
outputPath,
|
|
@@ -2073,7 +2074,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
2073
2074
|
savedPath: r.savedOutputPath,
|
|
2074
2075
|
outputReference: r.outputReference,
|
|
2075
2076
|
saveError: r.outputSaveError,
|
|
2076
|
-
});
|
|
2077
|
+
}).displayOutput;
|
|
2077
2078
|
const details = compactForegroundDetails({
|
|
2078
2079
|
mode: "single",
|
|
2079
2080
|
runId,
|
|
@@ -2096,14 +2097,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
2096
2097
|
};
|
|
2097
2098
|
}
|
|
2098
2099
|
|
|
2099
|
-
if (r.exitCode !== 0)
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2100
|
+
if (r.exitCode !== 0) {
|
|
2101
|
+
const text = r.outputMode === "file-only" && !blockedLine(r.error ?? "")
|
|
2102
|
+
? `FAILED (exit code ${r.exitCode})${r.error ? `: ${r.error}` : ""}\n\n${r.finalOutput ?? ""}`
|
|
2103
|
+
: r.error || "Failed";
|
|
2104
|
+
return { content: [{ type: "text", text }], details, isError: true };
|
|
2105
|
+
}
|
|
2105
2106
|
return {
|
|
2106
|
-
content: [{ type: "text", text:
|
|
2107
|
+
content: [{ type: "text", text: displayOutput || "(no output)" }],
|
|
2107
2108
|
details,
|
|
2108
2109
|
};
|
|
2109
2110
|
}
|
|
@@ -106,6 +106,16 @@ export function formatSavedOutputReference(savedPath: string, fullOutput: string
|
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
export function formatFileOnlyFailure(params: {
|
|
110
|
+
outputPath?: string;
|
|
111
|
+
saveError?: string;
|
|
112
|
+
} & ({ preservedAt: string; notPreservedReason?: never } | { preservedAt?: never; notPreservedReason: string })): string {
|
|
113
|
+
const pointer = params.preservedAt !== undefined
|
|
114
|
+
? `Full reply preserved at ${params.preservedAt}`
|
|
115
|
+
: `Full reply not preserved (${params.notPreservedReason})`;
|
|
116
|
+
return params.saveError ? `Output file error: ${params.outputPath}\n${params.saveError}\n\n${pointer}` : pointer;
|
|
117
|
+
}
|
|
118
|
+
|
|
109
119
|
export function validateFileOnlyOutputMode(outputMode: OutputMode | undefined, outputPath: string | undefined, context: string): string | undefined {
|
|
110
120
|
if (outputMode === "file-only" && !outputPath) {
|
|
111
121
|
return `${context} sets outputMode: "file-only" but does not configure an output file. Set output to a path or use outputMode: "inline".`;
|