@narumitw/pi-subagents 2.0.1 → 2.0.3
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/README.md +20 -7
- package/dist/chunks/auto-transport-SY2VHUFH.ts +109 -0
- package/dist/chunks/auto-transport-SY2VHUFH.ts.map +7 -0
- package/dist/chunks/capability-grant-CGEWOEKE.ts +19 -0
- package/dist/chunks/capability-grant-CGEWOEKE.ts.map +7 -0
- package/dist/chunks/chunk-434NII74.ts +282 -0
- package/dist/chunks/chunk-434NII74.ts.map +7 -0
- package/dist/chunks/chunk-47SK2QYC.ts +56 -0
- package/dist/chunks/chunk-47SK2QYC.ts.map +7 -0
- package/dist/chunks/chunk-6KJ34M6S.ts +159 -0
- package/dist/chunks/chunk-6KJ34M6S.ts.map +7 -0
- package/dist/chunks/chunk-7OBINKFM.ts +80 -0
- package/dist/chunks/chunk-7OBINKFM.ts.map +7 -0
- package/dist/chunks/chunk-7QPPBBXZ.ts +335 -0
- package/dist/chunks/chunk-7QPPBBXZ.ts.map +7 -0
- package/dist/chunks/chunk-AEUYS7JC.ts +73 -0
- package/dist/chunks/chunk-AEUYS7JC.ts.map +7 -0
- package/dist/chunks/chunk-BFK2Z4ZF.ts +189 -0
- package/dist/chunks/chunk-BFK2Z4ZF.ts.map +7 -0
- package/dist/chunks/chunk-BOAXY55Y.ts +153 -0
- package/dist/chunks/chunk-BOAXY55Y.ts.map +7 -0
- package/dist/chunks/chunk-C4L6P266.ts +22 -0
- package/dist/chunks/chunk-C4L6P266.ts.map +7 -0
- package/dist/chunks/chunk-DPPVEQAM.ts +29 -0
- package/dist/chunks/chunk-DPPVEQAM.ts.map +7 -0
- package/dist/chunks/chunk-DQMN4OYM.ts +913 -0
- package/dist/chunks/chunk-DQMN4OYM.ts.map +7 -0
- package/dist/chunks/chunk-DSOOH73Y.ts +238 -0
- package/dist/chunks/chunk-DSOOH73Y.ts.map +7 -0
- package/dist/chunks/chunk-EFBPISNT.ts +421 -0
- package/dist/chunks/chunk-EFBPISNT.ts.map +7 -0
- package/dist/chunks/chunk-EWWXFU7N.ts +150 -0
- package/dist/chunks/chunk-EWWXFU7N.ts.map +7 -0
- package/dist/chunks/chunk-F4L2UVSK.ts +380 -0
- package/dist/chunks/chunk-F4L2UVSK.ts.map +7 -0
- package/dist/chunks/chunk-F5YWVHQX.ts +1543 -0
- package/dist/chunks/chunk-F5YWVHQX.ts.map +7 -0
- package/dist/chunks/chunk-G3RSMSXJ.ts +63 -0
- package/dist/chunks/chunk-G3RSMSXJ.ts.map +7 -0
- package/dist/chunks/chunk-G6VZTCFH.ts +70 -0
- package/dist/chunks/chunk-G6VZTCFH.ts.map +7 -0
- package/dist/chunks/chunk-H3FP6DLR.ts +160 -0
- package/dist/chunks/chunk-H3FP6DLR.ts.map +7 -0
- package/dist/chunks/chunk-HA36LPCO.ts +130 -0
- package/dist/chunks/chunk-HA36LPCO.ts.map +7 -0
- package/dist/chunks/chunk-HKC4ES4B.ts +65 -0
- package/dist/chunks/chunk-HKC4ES4B.ts.map +7 -0
- package/dist/chunks/chunk-HOP5FXDT.ts +27 -0
- package/dist/chunks/chunk-HOP5FXDT.ts.map +7 -0
- package/dist/chunks/chunk-I2FAK44T.ts +324 -0
- package/dist/chunks/chunk-I2FAK44T.ts.map +7 -0
- package/dist/chunks/chunk-IGAMVWFN.ts +139 -0
- package/dist/chunks/chunk-IGAMVWFN.ts.map +7 -0
- package/dist/chunks/chunk-KMGKCEO4.ts +38 -0
- package/dist/chunks/chunk-KMGKCEO4.ts.map +7 -0
- package/dist/chunks/chunk-N7BLVXKK.ts +73 -0
- package/dist/chunks/chunk-N7BLVXKK.ts.map +7 -0
- package/dist/chunks/chunk-NIF42QMF.ts +297 -0
- package/dist/chunks/chunk-NIF42QMF.ts.map +7 -0
- package/dist/chunks/chunk-O4VO6JUJ.ts +52 -0
- package/dist/chunks/chunk-O4VO6JUJ.ts.map +7 -0
- package/dist/chunks/chunk-ONDTY4EL.ts +91 -0
- package/dist/chunks/chunk-ONDTY4EL.ts.map +7 -0
- package/dist/chunks/chunk-OVPHFGKO.ts +1769 -0
- package/dist/chunks/chunk-OVPHFGKO.ts.map +7 -0
- package/dist/chunks/chunk-P7OH4XMF.ts +60 -0
- package/dist/chunks/chunk-P7OH4XMF.ts.map +7 -0
- package/dist/chunks/chunk-PSK43Y6A.ts +339 -0
- package/dist/chunks/chunk-PSK43Y6A.ts.map +7 -0
- package/dist/chunks/chunk-QBORTI4W.ts +468 -0
- package/dist/chunks/chunk-QBORTI4W.ts.map +7 -0
- package/dist/chunks/chunk-QDVGH2X7.ts +171 -0
- package/dist/chunks/chunk-QDVGH2X7.ts.map +7 -0
- package/dist/chunks/chunk-RHRQMT7Q.ts +490 -0
- package/dist/chunks/chunk-RHRQMT7Q.ts.map +7 -0
- package/dist/chunks/chunk-RRR66UWR.ts +28 -0
- package/dist/chunks/chunk-RRR66UWR.ts.map +7 -0
- package/dist/chunks/chunk-RSUXZD6S.ts +275 -0
- package/dist/chunks/chunk-RSUXZD6S.ts.map +7 -0
- package/dist/chunks/chunk-RTCVYIZA.ts +221 -0
- package/dist/chunks/chunk-RTCVYIZA.ts.map +7 -0
- package/dist/chunks/chunk-S2IWVK3J.ts +44 -0
- package/dist/chunks/chunk-S2IWVK3J.ts.map +7 -0
- package/dist/chunks/chunk-SCZ33MYW.ts +52 -0
- package/dist/chunks/chunk-SCZ33MYW.ts.map +7 -0
- package/dist/chunks/chunk-SWGQLFSD.ts +60 -0
- package/dist/chunks/chunk-SWGQLFSD.ts.map +7 -0
- package/dist/chunks/chunk-UMBPMVJW.ts +22 -0
- package/dist/chunks/chunk-UMBPMVJW.ts.map +7 -0
- package/dist/chunks/chunk-UVFVHIOP.ts +735 -0
- package/dist/chunks/chunk-UVFVHIOP.ts.map +7 -0
- package/dist/chunks/completion-delivery-YPOWSSV3.ts +16 -0
- package/dist/chunks/completion-delivery-YPOWSSV3.ts.map +7 -0
- package/dist/chunks/config-status-J4GPWP46.ts +34 -0
- package/dist/chunks/config-status-J4GPWP46.ts.map +7 -0
- package/dist/chunks/config-ui-3IBMHTQB.ts +1146 -0
- package/dist/chunks/config-ui-3IBMHTQB.ts.map +7 -0
- package/dist/chunks/consult-NYXVDN5G.ts +518 -0
- package/dist/chunks/consult-NYXVDN5G.ts.map +7 -0
- package/dist/chunks/context-QDKLVQXE.ts +11 -0
- package/dist/chunks/context-QDKLVQXE.ts.map +7 -0
- package/dist/chunks/create-stateful-transport-4WKZHRSI.ts +130 -0
- package/dist/chunks/create-stateful-transport-4WKZHRSI.ts.map +7 -0
- package/dist/chunks/cwd-policy-NB6XZ5GF.ts +17 -0
- package/dist/chunks/cwd-policy-NB6XZ5GF.ts.map +7 -0
- package/dist/chunks/delegation-contract-WJPT4FVS.ts +24 -0
- package/dist/chunks/delegation-contract-WJPT4FVS.ts.map +7 -0
- package/dist/chunks/discovery-K25T3SH4.ts +12 -0
- package/dist/chunks/discovery-K25T3SH4.ts.map +7 -0
- package/dist/chunks/execution-DS2DEIQZ.ts +4034 -0
- package/dist/chunks/execution-DS2DEIQZ.ts.map +7 -0
- package/dist/chunks/in-process-transport-TEYAWSEC.ts +38 -0
- package/dist/chunks/in-process-transport-TEYAWSEC.ts.map +7 -0
- package/dist/chunks/inspect-UOQQ3KNL.ts +619 -0
- package/dist/chunks/inspect-UOQQ3KNL.ts.map +7 -0
- package/dist/chunks/peer-communication-KL36Z6CO.ts +294 -0
- package/dist/chunks/peer-communication-KL36Z6CO.ts.map +7 -0
- package/dist/chunks/persistence-VSGXAW4P.ts +344 -0
- package/dist/chunks/persistence-VSGXAW4P.ts.map +7 -0
- package/dist/chunks/registry-E6XPJB7L.ts +1324 -0
- package/dist/chunks/registry-E6XPJB7L.ts.map +7 -0
- package/dist/chunks/retained-semantic-state-7WXSGRJU.ts +81 -0
- package/dist/chunks/retained-semantic-state-7WXSGRJU.ts.map +7 -0
- package/dist/chunks/rpc-transport-Y6LA5BOP.ts +1154 -0
- package/dist/chunks/rpc-transport-Y6LA5BOP.ts.map +7 -0
- package/dist/chunks/runtime-policy-5YELCOVC.ts +15 -0
- package/dist/chunks/runtime-policy-5YELCOVC.ts.map +7 -0
- package/dist/chunks/semantic-snapshot-WPJ3UAFW.ts +19 -0
- package/dist/chunks/semantic-snapshot-WPJ3UAFW.ts.map +7 -0
- package/dist/chunks/spawn-idempotency-QGHO2WKC.ts +13 -0
- package/dist/chunks/spawn-idempotency-QGHO2WKC.ts.map +7 -0
- package/dist/chunks/stateful-lifecycle-JAR6K55H.ts +15 -0
- package/dist/chunks/stateful-lifecycle-JAR6K55H.ts.map +7 -0
- package/dist/chunks/subprocess-transport-C2XVR25R.ts +158 -0
- package/dist/chunks/subprocess-transport-C2XVR25R.ts.map +7 -0
- package/dist/chunks/workspace-WDKLN72J.ts +11 -0
- package/dist/chunks/workspace-WDKLN72J.ts.map +7 -0
- package/dist/index.ts +1057 -0
- package/dist/index.ts.map +7 -0
- package/package.json +9 -5
- package/src/index.ts +1 -1
- package/src/settings-reader.ts +208 -0
- package/src/settings.ts +22 -187
- package/src/stateful-registration.ts +1230 -0
- package/src/stateful-safety.ts +1 -1
- package/src/stateful.ts +2 -1234
- package/src/subagents-extension.ts +307 -0
- package/src/subagents.ts +1 -308
|
@@ -0,0 +1,913 @@
|
|
|
1
|
+
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
|
+
// @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
|
|
3
|
+
import {
|
|
4
|
+
resolvePiInvocation
|
|
5
|
+
} from "./chunk-IGAMVWFN.ts";
|
|
6
|
+
import {
|
|
7
|
+
classifyStructuredOutcome
|
|
8
|
+
} from "./chunk-47SK2QYC.ts";
|
|
9
|
+
import {
|
|
10
|
+
revokeCapabilityGrant
|
|
11
|
+
} from "./chunk-HKC4ES4B.ts";
|
|
12
|
+
import {
|
|
13
|
+
buildTimeoutFinalizationPrompt,
|
|
14
|
+
resolveTimeoutFinalizationMs
|
|
15
|
+
} from "./chunk-G3RSMSXJ.ts";
|
|
16
|
+
import {
|
|
17
|
+
formatResultFailure,
|
|
18
|
+
getFinalOutput,
|
|
19
|
+
getResultFinalOutput,
|
|
20
|
+
isResultError
|
|
21
|
+
} from "./chunk-AEUYS7JC.ts";
|
|
22
|
+
import {
|
|
23
|
+
TURN_TERMINATION_VERSION,
|
|
24
|
+
TimeoutProgressJournal,
|
|
25
|
+
formatTimeoutCheckpoint,
|
|
26
|
+
formatTurnTerminationMessage,
|
|
27
|
+
journalMessages
|
|
28
|
+
} from "./chunk-RTCVYIZA.ts";
|
|
29
|
+
import {
|
|
30
|
+
TurnBudgetMonitor
|
|
31
|
+
} from "./chunk-ONDTY4EL.ts";
|
|
32
|
+
import {
|
|
33
|
+
parseAnyStructuredSubagentResult
|
|
34
|
+
} from "./chunk-RSUXZD6S.ts";
|
|
35
|
+
import {
|
|
36
|
+
DEFAULT_MAX_CONTEXT_BYTES,
|
|
37
|
+
DEFAULT_MAX_MESSAGES,
|
|
38
|
+
DEFAULT_MAX_OUTPUT_BYTES,
|
|
39
|
+
DEFAULT_MAX_STDERR_BYTES,
|
|
40
|
+
MAX_SUBAGENT_TIMEOUT_MS,
|
|
41
|
+
appendBounded,
|
|
42
|
+
truncateUtf8
|
|
43
|
+
} from "./chunk-H3FP6DLR.ts";
|
|
44
|
+
|
|
45
|
+
// src/protocol.ts
|
|
46
|
+
import { StringDecoder } from "node:string_decoder";
|
|
47
|
+
var DEFAULT_MAX_LINE_BYTES = 256 * 1024;
|
|
48
|
+
var JsonLineDecoder = class {
|
|
49
|
+
constructor(options) {
|
|
50
|
+
this.options = options;
|
|
51
|
+
const maxLineBytes = options.maxLineBytes ?? DEFAULT_MAX_LINE_BYTES;
|
|
52
|
+
if (!Number.isSafeInteger(maxLineBytes) || maxLineBytes < 1) {
|
|
53
|
+
throw new Error("JSON line limit must be a positive safe integer");
|
|
54
|
+
}
|
|
55
|
+
this.maxLineBytes = maxLineBytes;
|
|
56
|
+
}
|
|
57
|
+
options;
|
|
58
|
+
buffer = "";
|
|
59
|
+
droppingOversizedLine = false;
|
|
60
|
+
maxLineBytes;
|
|
61
|
+
decoder = new StringDecoder("utf8");
|
|
62
|
+
push(chunk) {
|
|
63
|
+
this.buffer += typeof chunk === "string" ? chunk : this.decoder.write(chunk);
|
|
64
|
+
this.drain(false);
|
|
65
|
+
}
|
|
66
|
+
finish() {
|
|
67
|
+
this.buffer += this.decoder.end();
|
|
68
|
+
this.drain(true);
|
|
69
|
+
this.buffer = "";
|
|
70
|
+
this.droppingOversizedLine = false;
|
|
71
|
+
}
|
|
72
|
+
drain(flush) {
|
|
73
|
+
while (true) {
|
|
74
|
+
const newline = this.buffer.indexOf("\n");
|
|
75
|
+
if (newline < 0) break;
|
|
76
|
+
const line = this.buffer.slice(0, newline).replace(/\r$/, "");
|
|
77
|
+
this.buffer = this.buffer.slice(newline + 1);
|
|
78
|
+
if (this.droppingOversizedLine) {
|
|
79
|
+
this.droppingOversizedLine = false;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
this.processLine(line);
|
|
83
|
+
}
|
|
84
|
+
if (!flush && Buffer.byteLength(this.buffer, "utf8") > this.maxLineBytes) {
|
|
85
|
+
this.options.onOversized?.(Buffer.byteLength(this.buffer, "utf8"));
|
|
86
|
+
this.buffer = "";
|
|
87
|
+
this.droppingOversizedLine = true;
|
|
88
|
+
}
|
|
89
|
+
if (flush && this.buffer.length > 0 && !this.droppingOversizedLine) {
|
|
90
|
+
this.processLine(this.buffer.replace(/\r$/, ""));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
processLine(line) {
|
|
94
|
+
if (!line.trim()) return;
|
|
95
|
+
const bytes = Buffer.byteLength(line, "utf8");
|
|
96
|
+
if (bytes > this.maxLineBytes) {
|
|
97
|
+
this.options.onOversized?.(bytes);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
this.options.onValue(JSON.parse(line));
|
|
102
|
+
} catch {
|
|
103
|
+
this.options.onMalformed?.(line);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// src/runner.ts
|
|
109
|
+
import { spawn } from "node:child_process";
|
|
110
|
+
import * as fs from "node:fs";
|
|
111
|
+
import * as os from "node:os";
|
|
112
|
+
import * as path from "node:path";
|
|
113
|
+
import { withFileMutationQueue } from "@earendil-works/pi-coding-agent";
|
|
114
|
+
|
|
115
|
+
// src/pi-args.ts
|
|
116
|
+
function buildPiArgs(options) {
|
|
117
|
+
const args = ["--mode", "json", "-p", "--no-session"];
|
|
118
|
+
if (options.model) args.push("--model", options.model);
|
|
119
|
+
if (options.thinkingLevel) args.push("--thinking", options.thinkingLevel);
|
|
120
|
+
if (options.disableExtensions) args.push("--no-extensions");
|
|
121
|
+
for (const extensionPath of options.extensionPaths ?? []) args.push("-e", extensionPath);
|
|
122
|
+
if (options.disableSkills) args.push("--no-skills");
|
|
123
|
+
if (options.disablePromptTemplates) args.push("--no-prompt-templates");
|
|
124
|
+
if (options.disableContextFiles) args.push("--no-context-files");
|
|
125
|
+
if (options.projectTrust !== void 0) {
|
|
126
|
+
args.push(options.projectTrust ? "--approve" : "--no-approve");
|
|
127
|
+
}
|
|
128
|
+
if (Array.isArray(options.tools)) {
|
|
129
|
+
if (options.tools.length > 0) args.push("--tools", options.tools.join(","));
|
|
130
|
+
else args.push("--no-tools");
|
|
131
|
+
}
|
|
132
|
+
if (options.baseSystemPromptPath) args.push("--system-prompt", options.baseSystemPromptPath);
|
|
133
|
+
for (const promptPath of options.appendSystemPromptPaths ?? []) {
|
|
134
|
+
args.push("--append-system-prompt", promptPath);
|
|
135
|
+
}
|
|
136
|
+
if (options.systemPromptPath) args.push("--append-system-prompt", options.systemPromptPath);
|
|
137
|
+
args.push(`Task: ${options.task}`);
|
|
138
|
+
return args;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/runner-usage.ts
|
|
142
|
+
var MAX_USAGE_VALUE = Number.MAX_SAFE_INTEGER;
|
|
143
|
+
function protocolUsageCount(value) {
|
|
144
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : 0;
|
|
145
|
+
}
|
|
146
|
+
function protocolUsageCost(value) {
|
|
147
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.min(value, MAX_USAGE_VALUE) : 0;
|
|
148
|
+
}
|
|
149
|
+
function addUsageValue(current, addition) {
|
|
150
|
+
return Math.min(MAX_USAGE_VALUE, current + addition);
|
|
151
|
+
}
|
|
152
|
+
function mergeUsageStats(target, addition) {
|
|
153
|
+
for (const key of ["input", "output", "cacheRead", "cacheWrite", "cost", "turns"]) {
|
|
154
|
+
target[key] = addUsageValue(target[key], addition[key]);
|
|
155
|
+
}
|
|
156
|
+
for (const key of [
|
|
157
|
+
"costInput",
|
|
158
|
+
"costOutput",
|
|
159
|
+
"costCacheRead",
|
|
160
|
+
"costCacheWrite",
|
|
161
|
+
"totalTokens"
|
|
162
|
+
]) {
|
|
163
|
+
if (addition[key] !== void 0) {
|
|
164
|
+
target[key] = addUsageValue(target[key] ?? 0, addition[key]);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
target.contextTokens = addition.contextTokens || target.contextTokens;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/runner.ts
|
|
171
|
+
var KILL_GRACE_MS = 5e3;
|
|
172
|
+
var MAX_RECENT_ACTIVITY_ITEMS = 10;
|
|
173
|
+
var MAX_RECENT_ACTIVITY_BYTES = 8 * 1024;
|
|
174
|
+
var MAX_RECENT_ACTIVITY_ARGUMENT_BYTES = 1024;
|
|
175
|
+
function boundMessageText(message, maxBytes) {
|
|
176
|
+
const originalBytes = Buffer.byteLength(JSON.stringify(message), "utf8");
|
|
177
|
+
if (Number.isSafeInteger(maxBytes) && maxBytes >= 0 && originalBytes <= maxBytes) {
|
|
178
|
+
return { message, bytes: originalBytes, truncated: false };
|
|
179
|
+
}
|
|
180
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) return { bytes: 0, truncated: true };
|
|
181
|
+
const content = [];
|
|
182
|
+
const bounded = () => ({ ...message, content });
|
|
183
|
+
const fits = () => Buffer.byteLength(JSON.stringify(bounded()), "utf8") <= maxBytes;
|
|
184
|
+
const addText = (text, prepend = false) => {
|
|
185
|
+
if (!text.trim()) return;
|
|
186
|
+
const part = { type: "text", text: "" };
|
|
187
|
+
if (prepend) content.unshift(part);
|
|
188
|
+
else content.push(part);
|
|
189
|
+
if (!fits()) {
|
|
190
|
+
content.splice(content.indexOf(part), 1);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
let low = 0;
|
|
194
|
+
let high = Buffer.byteLength(text, "utf8");
|
|
195
|
+
while (low < high) {
|
|
196
|
+
const middle = Math.ceil((low + high) / 2);
|
|
197
|
+
part.text = truncateUtf8(text, middle).text;
|
|
198
|
+
if (fits()) low = middle;
|
|
199
|
+
else high = middle - 1;
|
|
200
|
+
}
|
|
201
|
+
part.text = truncateUtf8(text, low).text;
|
|
202
|
+
if (!part.text.trim()) content.splice(content.indexOf(part), 1);
|
|
203
|
+
};
|
|
204
|
+
const addToolCall = (part) => {
|
|
205
|
+
const toolCall = {
|
|
206
|
+
type: "toolCall",
|
|
207
|
+
id: part.id,
|
|
208
|
+
name: part.name,
|
|
209
|
+
arguments: part.arguments
|
|
210
|
+
};
|
|
211
|
+
content.unshift(toolCall);
|
|
212
|
+
if (fits()) return;
|
|
213
|
+
const arguments_ = {};
|
|
214
|
+
for (const key of ["command", "path", "file_path", "pattern", "url"]) {
|
|
215
|
+
const value = part.arguments[key];
|
|
216
|
+
if (typeof value === "string") arguments_[key] = truncateUtf8(value, 256).text;
|
|
217
|
+
}
|
|
218
|
+
toolCall.arguments = arguments_;
|
|
219
|
+
if (fits()) return;
|
|
220
|
+
toolCall.arguments = {};
|
|
221
|
+
if (!fits()) content.shift();
|
|
222
|
+
};
|
|
223
|
+
if (message.role === "assistant") {
|
|
224
|
+
for (let index = message.content.length - 1; index >= 0; index--) {
|
|
225
|
+
const part = message.content[index];
|
|
226
|
+
if (part.type === "text") addText(part.text, true);
|
|
227
|
+
else if (part.type === "toolCall") addToolCall(part);
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
for (const part of message.content) {
|
|
231
|
+
if (typeof part === "object" && part && part.type === "text") addText(part.text);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (content.length === 0) return { bytes: 0, truncated: true };
|
|
235
|
+
const result = bounded();
|
|
236
|
+
const bytes = Buffer.byteLength(JSON.stringify(result), "utf8");
|
|
237
|
+
return { message: result, bytes, truncated: true };
|
|
238
|
+
}
|
|
239
|
+
function compactRecentActivityArguments(args) {
|
|
240
|
+
if (Buffer.byteLength(JSON.stringify(args), "utf8") <= MAX_RECENT_ACTIVITY_ARGUMENT_BYTES)
|
|
241
|
+
return args;
|
|
242
|
+
const compact = {};
|
|
243
|
+
for (const key of ["command", "path", "file_path", "pattern", "url", "selector"]) {
|
|
244
|
+
const value = args[key];
|
|
245
|
+
if (typeof value === "string") compact[key] = truncateUtf8(value, 256).text;
|
|
246
|
+
else if (typeof value === "number" || typeof value === "boolean") compact[key] = value;
|
|
247
|
+
}
|
|
248
|
+
return compact;
|
|
249
|
+
}
|
|
250
|
+
function appendRecentActivity(result, message) {
|
|
251
|
+
if (message.role !== "assistant") return;
|
|
252
|
+
const append = (item) => {
|
|
253
|
+
result.recentActivity ??= [];
|
|
254
|
+
result.recentActivityTotal = (result.recentActivityTotal ?? 0) + 1;
|
|
255
|
+
result.recentActivity.push(item);
|
|
256
|
+
if (result.recentActivity.length > MAX_RECENT_ACTIVITY_ITEMS) {
|
|
257
|
+
result.recentActivity.splice(0, result.recentActivity.length - MAX_RECENT_ACTIVITY_ITEMS);
|
|
258
|
+
}
|
|
259
|
+
while (Buffer.byteLength(JSON.stringify(result.recentActivity), "utf8") > MAX_RECENT_ACTIVITY_BYTES) {
|
|
260
|
+
result.recentActivity.shift();
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
for (const part of message.content) {
|
|
264
|
+
if (part.type === "text") {
|
|
265
|
+
const text = part.text.trim();
|
|
266
|
+
if (text) append({ type: "text", text: truncateUtf8(text, 1024).text });
|
|
267
|
+
} else if (part.type === "toolCall") {
|
|
268
|
+
append({
|
|
269
|
+
type: "toolCall",
|
|
270
|
+
name: part.name,
|
|
271
|
+
args: compactRecentActivityArguments(part.arguments)
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function buildFanInContext(results, maxBytes = DEFAULT_MAX_CONTEXT_BYTES) {
|
|
277
|
+
const text = results.map((result, index) => {
|
|
278
|
+
const failed = isResultError(result);
|
|
279
|
+
const status = result.exitCode === -1 ? "running" : failed ? "failed" : "completed";
|
|
280
|
+
const output = getResultFinalOutput(result);
|
|
281
|
+
const error = result.errorMessage || result.stderr.trim();
|
|
282
|
+
const resultText = failed ? `${error ? "Error" : output ? "Partial output" : "Error"}:
|
|
283
|
+
${formatResultFailure(result)}` : result.structuredResult ? `Structured result:
|
|
284
|
+
${JSON.stringify(result.structuredResult)}` : output ? `Output:
|
|
285
|
+
${output}` : "Output: (no output)";
|
|
286
|
+
return [
|
|
287
|
+
`## Result ${index + 1}: ${result.agent} (${status})`,
|
|
288
|
+
`Task: ${result.task}`,
|
|
289
|
+
resultText
|
|
290
|
+
].join("\n\n");
|
|
291
|
+
}).join("\n\n---\n\n");
|
|
292
|
+
return truncateUtf8(text, maxBytes).text;
|
|
293
|
+
}
|
|
294
|
+
async function mapWithConcurrencyLimit(items, concurrency, fn, signal, onSkipped) {
|
|
295
|
+
if (items.length === 0) return [];
|
|
296
|
+
const limit = Math.max(1, Math.min(concurrency, items.length));
|
|
297
|
+
const results = new Array(items.length);
|
|
298
|
+
let nextIndex = 0;
|
|
299
|
+
const workers = new Array(limit).fill(null).map(async () => {
|
|
300
|
+
while (true) {
|
|
301
|
+
const current = nextIndex++;
|
|
302
|
+
if (current >= items.length) return;
|
|
303
|
+
if (signal?.aborted && onSkipped) {
|
|
304
|
+
results[current] = onSkipped(items[current], current);
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
results[current] = await fn(items[current], current);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
await Promise.all(workers);
|
|
311
|
+
return results;
|
|
312
|
+
}
|
|
313
|
+
async function writePromptToTempFile(agentName, prompt) {
|
|
314
|
+
const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "pi-subagent-"));
|
|
315
|
+
const safeName = agentName.replace(/[^\w.-]+/g, "_");
|
|
316
|
+
const filePath = path.join(tmpDir, `prompt-${safeName}.md`);
|
|
317
|
+
await withFileMutationQueue(filePath, async () => {
|
|
318
|
+
await fs.promises.writeFile(filePath, prompt, { encoding: "utf-8", mode: 384 });
|
|
319
|
+
});
|
|
320
|
+
return { dir: tmpDir, filePath };
|
|
321
|
+
}
|
|
322
|
+
function signalProcess(proc, signal) {
|
|
323
|
+
if (process.platform !== "win32" && proc.pid) {
|
|
324
|
+
try {
|
|
325
|
+
process.kill(-proc.pid, signal);
|
|
326
|
+
return;
|
|
327
|
+
} catch {
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
try {
|
|
331
|
+
proc.kill(signal);
|
|
332
|
+
} catch {
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function terminateProcess(proc, graceMs = KILL_GRACE_MS) {
|
|
336
|
+
const leaderExited = proc.exitCode !== null || proc.signalCode !== null;
|
|
337
|
+
const capturedOutputClosed = [proc.stdout, proc.stderr].every(
|
|
338
|
+
(stream) => !stream || stream.readableEnded || stream.destroyed
|
|
339
|
+
);
|
|
340
|
+
let closed = leaderExited && capturedOutputClosed;
|
|
341
|
+
const onClose = () => {
|
|
342
|
+
closed = true;
|
|
343
|
+
};
|
|
344
|
+
proc.once("close", onClose);
|
|
345
|
+
if (!closed) signalProcess(proc, "SIGTERM");
|
|
346
|
+
const escalation = setTimeout(() => {
|
|
347
|
+
if (!closed) signalProcess(proc, "SIGKILL");
|
|
348
|
+
}, graceMs);
|
|
349
|
+
escalation.unref();
|
|
350
|
+
return () => {
|
|
351
|
+
clearTimeout(escalation);
|
|
352
|
+
proc.off("close", onClose);
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, thinkingLevel, timeoutMs, onUpdate, makeDetails, invocationOverride, launchPolicy) {
|
|
356
|
+
const agent = agents.find((a) => a.name === agentName);
|
|
357
|
+
if (!agent) {
|
|
358
|
+
const available = agents.map((a) => `"${a.name}"`).join(", ") || "none";
|
|
359
|
+
return {
|
|
360
|
+
agent: agentName,
|
|
361
|
+
agentSource: "unknown",
|
|
362
|
+
task,
|
|
363
|
+
exitCode: 1,
|
|
364
|
+
messages: [],
|
|
365
|
+
stderr: `Unknown agent: "${agentName}". Available agents: ${available}.`,
|
|
366
|
+
usage: {
|
|
367
|
+
input: 0,
|
|
368
|
+
output: 0,
|
|
369
|
+
cacheRead: 0,
|
|
370
|
+
cacheWrite: 0,
|
|
371
|
+
cost: 0,
|
|
372
|
+
contextTokens: 0,
|
|
373
|
+
turns: 0
|
|
374
|
+
},
|
|
375
|
+
thinkingLevel,
|
|
376
|
+
step,
|
|
377
|
+
finalOutput: ""
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
const temporaryPrompts = [];
|
|
381
|
+
let tmpPromptPath = null;
|
|
382
|
+
let baseSystemPromptPath = null;
|
|
383
|
+
let latestAssistantOutput = "";
|
|
384
|
+
let terminalAssistantOutput;
|
|
385
|
+
const progressJournal = new TimeoutProgressJournal();
|
|
386
|
+
const currentResult = {
|
|
387
|
+
agent: agentName,
|
|
388
|
+
agentSource: agent.source,
|
|
389
|
+
task: launchPolicy?.displayTask ?? task,
|
|
390
|
+
exitCode: 0,
|
|
391
|
+
messages: [],
|
|
392
|
+
stderr: "",
|
|
393
|
+
usage: {
|
|
394
|
+
input: 0,
|
|
395
|
+
output: 0,
|
|
396
|
+
cacheRead: 0,
|
|
397
|
+
cacheWrite: 0,
|
|
398
|
+
cost: 0,
|
|
399
|
+
contextTokens: 0,
|
|
400
|
+
turns: 0
|
|
401
|
+
},
|
|
402
|
+
model: agent.model ?? void 0,
|
|
403
|
+
thinkingLevel,
|
|
404
|
+
step,
|
|
405
|
+
timeoutMs,
|
|
406
|
+
contract: launchPolicy?.contract,
|
|
407
|
+
resultFormat: launchPolicy?.resultFormat,
|
|
408
|
+
executionPlan: launchPolicy?.executionPlan,
|
|
409
|
+
capabilityGrant: launchPolicy?.capabilityGrant
|
|
410
|
+
};
|
|
411
|
+
const selectedAssistantOutput = () => terminalAssistantOutput !== void 0 ? terminalAssistantOutput : latestAssistantOutput || getFinalOutput(currentResult.messages);
|
|
412
|
+
const setErrorMessage = (message) => {
|
|
413
|
+
const bounded = truncateUtf8(message, DEFAULT_MAX_STDERR_BYTES);
|
|
414
|
+
currentResult.errorMessage = bounded.text;
|
|
415
|
+
currentResult.truncated ||= bounded.truncated;
|
|
416
|
+
return bounded.text;
|
|
417
|
+
};
|
|
418
|
+
const emitUpdate = () => {
|
|
419
|
+
const latest = truncateUtf8(selectedAssistantOutput(), DEFAULT_MAX_OUTPUT_BYTES);
|
|
420
|
+
currentResult.finalOutput = latest.text;
|
|
421
|
+
currentResult.truncated ||= latest.truncated;
|
|
422
|
+
if (onUpdate) {
|
|
423
|
+
onUpdate({
|
|
424
|
+
content: [{ type: "text", text: currentResult.finalOutput || "(running...)" }],
|
|
425
|
+
details: makeDetails([currentResult])
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
try {
|
|
430
|
+
const effectiveCwd = cwd ?? defaultCwd;
|
|
431
|
+
try {
|
|
432
|
+
if (!fs.statSync(effectiveCwd).isDirectory()) throw new Error("not a directory");
|
|
433
|
+
} catch (error) {
|
|
434
|
+
currentResult.exitCode = 1;
|
|
435
|
+
currentResult.stopReason = "error";
|
|
436
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
437
|
+
currentResult.stderr = setErrorMessage(`Invalid subagent cwd: ${effectiveCwd} (${reason})`);
|
|
438
|
+
return currentResult;
|
|
439
|
+
}
|
|
440
|
+
if (signal?.aborted) {
|
|
441
|
+
currentResult.exitCode = 130;
|
|
442
|
+
currentResult.aborted = true;
|
|
443
|
+
currentResult.stopReason = "aborted";
|
|
444
|
+
setErrorMessage("Subagent was aborted before start");
|
|
445
|
+
return currentResult;
|
|
446
|
+
}
|
|
447
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs < 1 || timeoutMs > MAX_SUBAGENT_TIMEOUT_MS) {
|
|
448
|
+
currentResult.exitCode = 1;
|
|
449
|
+
currentResult.stopReason = "error";
|
|
450
|
+
setErrorMessage(
|
|
451
|
+
`Invalid subagent timeout: expected 1-${MAX_SUBAGENT_TIMEOUT_MS}ms, received ${timeoutMs}`
|
|
452
|
+
);
|
|
453
|
+
return currentResult;
|
|
454
|
+
}
|
|
455
|
+
if (launchPolicy?.baseSystemPrompt?.trim()) {
|
|
456
|
+
const tmp = await writePromptToTempFile(`${agent.name}-base`, launchPolicy.baseSystemPrompt);
|
|
457
|
+
temporaryPrompts.push(tmp);
|
|
458
|
+
baseSystemPromptPath = tmp.filePath;
|
|
459
|
+
}
|
|
460
|
+
if (agent.systemPrompt.trim()) {
|
|
461
|
+
const tmp = await writePromptToTempFile(agent.name, agent.systemPrompt);
|
|
462
|
+
temporaryPrompts.push(tmp);
|
|
463
|
+
tmpPromptPath = tmp.filePath;
|
|
464
|
+
}
|
|
465
|
+
if (signal?.aborted) {
|
|
466
|
+
currentResult.exitCode = 130;
|
|
467
|
+
currentResult.aborted = true;
|
|
468
|
+
currentResult.stopReason = "aborted";
|
|
469
|
+
setErrorMessage("Subagent was aborted before launch");
|
|
470
|
+
return currentResult;
|
|
471
|
+
}
|
|
472
|
+
const effectiveTools = launchPolicy && Object.hasOwn(launchPolicy, "tools") ? launchPolicy.tools : agent.tools;
|
|
473
|
+
const selectedTools = effectiveTools === void 0 ? void 0 : [.../* @__PURE__ */ new Set([...effectiveTools, ...launchPolicy?.additionalTools ?? []])];
|
|
474
|
+
const args = buildPiArgs({
|
|
475
|
+
model: agent.model,
|
|
476
|
+
thinkingLevel,
|
|
477
|
+
tools: selectedTools,
|
|
478
|
+
disableExtensions: launchPolicy?.disableExtensions,
|
|
479
|
+
disableSkills: launchPolicy?.disableSkills,
|
|
480
|
+
disablePromptTemplates: launchPolicy?.disablePromptTemplates,
|
|
481
|
+
disableContextFiles: launchPolicy?.disableContextFiles,
|
|
482
|
+
projectTrust: launchPolicy?.projectTrust,
|
|
483
|
+
baseSystemPromptPath: baseSystemPromptPath ?? void 0,
|
|
484
|
+
appendSystemPromptPaths: launchPolicy?.appendSystemPromptPaths,
|
|
485
|
+
extensionPaths: launchPolicy?.extensionPaths,
|
|
486
|
+
systemPromptPath: tmpPromptPath ?? void 0,
|
|
487
|
+
task
|
|
488
|
+
});
|
|
489
|
+
let invocation;
|
|
490
|
+
try {
|
|
491
|
+
invocation = invocationOverride ? {
|
|
492
|
+
command: invocationOverride.command,
|
|
493
|
+
args: [...invocationOverride.argsPrefix ?? [], ...args]
|
|
494
|
+
} : resolvePiInvocation(args);
|
|
495
|
+
} catch (error) {
|
|
496
|
+
currentResult.launchFailed = true;
|
|
497
|
+
currentResult.exitCode = 1;
|
|
498
|
+
currentResult.stderr = setErrorMessage(
|
|
499
|
+
error instanceof Error ? error.message : String(error)
|
|
500
|
+
);
|
|
501
|
+
return currentResult;
|
|
502
|
+
}
|
|
503
|
+
let wasAborted = false;
|
|
504
|
+
let timedOut = false;
|
|
505
|
+
let budgetStop;
|
|
506
|
+
const exitCode = await new Promise((resolve) => {
|
|
507
|
+
let settled = false;
|
|
508
|
+
let cleanupTermination;
|
|
509
|
+
let timeout;
|
|
510
|
+
let terminationDeadline;
|
|
511
|
+
let abortHandler;
|
|
512
|
+
let budgetMonitor;
|
|
513
|
+
const finish = (code) => {
|
|
514
|
+
if (settled) return;
|
|
515
|
+
settled = true;
|
|
516
|
+
if (timeout) clearTimeout(timeout);
|
|
517
|
+
if (terminationDeadline) clearTimeout(terminationDeadline);
|
|
518
|
+
cleanupTermination?.();
|
|
519
|
+
budgetMonitor?.dispose();
|
|
520
|
+
if (signal && abortHandler) signal.removeEventListener("abort", abortHandler);
|
|
521
|
+
resolve(code);
|
|
522
|
+
};
|
|
523
|
+
let proc;
|
|
524
|
+
try {
|
|
525
|
+
proc = spawn(invocation.command, invocation.args, {
|
|
526
|
+
cwd: effectiveCwd,
|
|
527
|
+
detached: process.platform !== "win32",
|
|
528
|
+
shell: false,
|
|
529
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
530
|
+
env: {
|
|
531
|
+
...process.env,
|
|
532
|
+
...launchPolicy?.env,
|
|
533
|
+
PI_SUBAGENT_DEPTH: String(
|
|
534
|
+
(Number.parseInt(process.env.PI_SUBAGENT_DEPTH ?? "0", 10) || 0) + 1
|
|
535
|
+
)
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
} catch (error) {
|
|
539
|
+
currentResult.launchFailed = true;
|
|
540
|
+
currentResult.stderr = setErrorMessage(
|
|
541
|
+
error instanceof Error ? error.message : String(error)
|
|
542
|
+
);
|
|
543
|
+
finish(1);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const addMessage = (msg) => {
|
|
547
|
+
const boundedMessage = boundMessageText(msg, DEFAULT_MAX_OUTPUT_BYTES - 2);
|
|
548
|
+
currentResult.truncated ||= boundedMessage.truncated;
|
|
549
|
+
if (!boundedMessage.message) return;
|
|
550
|
+
while (currentResult.messages.length >= DEFAULT_MAX_MESSAGES || Buffer.byteLength(
|
|
551
|
+
JSON.stringify([...currentResult.messages, boundedMessage.message]),
|
|
552
|
+
"utf8"
|
|
553
|
+
) > DEFAULT_MAX_OUTPUT_BYTES) {
|
|
554
|
+
const removed = currentResult.messages.shift();
|
|
555
|
+
if (!removed) break;
|
|
556
|
+
currentResult.truncated = true;
|
|
557
|
+
}
|
|
558
|
+
if (Buffer.byteLength(
|
|
559
|
+
JSON.stringify([...currentResult.messages, boundedMessage.message]),
|
|
560
|
+
"utf8"
|
|
561
|
+
) > DEFAULT_MAX_OUTPUT_BYTES) {
|
|
562
|
+
currentResult.truncated = true;
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
currentResult.messages.push(boundedMessage.message);
|
|
566
|
+
};
|
|
567
|
+
const processEvent = (raw) => {
|
|
568
|
+
if (!raw || typeof raw !== "object") return;
|
|
569
|
+
const event = raw;
|
|
570
|
+
if (event.type === "message_end" && event.message) {
|
|
571
|
+
const msg = event.message;
|
|
572
|
+
journalMessages(progressJournal, [msg]);
|
|
573
|
+
if (msg.role === "assistant") {
|
|
574
|
+
const output = truncateUtf8(getFinalOutput([msg]), DEFAULT_MAX_OUTPUT_BYTES);
|
|
575
|
+
currentResult.truncated ||= output.truncated;
|
|
576
|
+
if (output.text) latestAssistantOutput = output.text;
|
|
577
|
+
if (msg.stopReason === "stop" || msg.stopReason === "length") {
|
|
578
|
+
terminalAssistantOutput = output.text;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
appendRecentActivity(currentResult, msg);
|
|
582
|
+
addMessage(msg);
|
|
583
|
+
if (msg.role === "assistant") {
|
|
584
|
+
currentResult.usage.turns++;
|
|
585
|
+
budgetMonitor?.recordToolCalls(
|
|
586
|
+
msg.content.filter((part) => part.type === "toolCall").length
|
|
587
|
+
);
|
|
588
|
+
budgetMonitor?.recordAssistantTurn(msg.stopReason);
|
|
589
|
+
const usage = msg.usage;
|
|
590
|
+
if (usage && typeof usage === "object") {
|
|
591
|
+
const input = protocolUsageCount(usage.input);
|
|
592
|
+
const output = protocolUsageCount(usage.output);
|
|
593
|
+
const cacheRead = protocolUsageCount(usage.cacheRead);
|
|
594
|
+
const cacheWrite = protocolUsageCount(usage.cacheWrite);
|
|
595
|
+
const reportedTotal = protocolUsageCount(usage.totalTokens);
|
|
596
|
+
const turnTotal = reportedTotal || addUsageValue(addUsageValue(input, output), addUsageValue(cacheRead, cacheWrite));
|
|
597
|
+
const cost = usage.cost && typeof usage.cost === "object" ? usage.cost : void 0;
|
|
598
|
+
currentResult.usage.input = addUsageValue(currentResult.usage.input, input);
|
|
599
|
+
currentResult.usage.output = addUsageValue(currentResult.usage.output, output);
|
|
600
|
+
currentResult.usage.cacheRead = addUsageValue(
|
|
601
|
+
currentResult.usage.cacheRead,
|
|
602
|
+
cacheRead
|
|
603
|
+
);
|
|
604
|
+
currentResult.usage.cacheWrite = addUsageValue(
|
|
605
|
+
currentResult.usage.cacheWrite,
|
|
606
|
+
cacheWrite
|
|
607
|
+
);
|
|
608
|
+
currentResult.usage.cost = addUsageValue(
|
|
609
|
+
currentResult.usage.cost,
|
|
610
|
+
protocolUsageCost(cost?.total)
|
|
611
|
+
);
|
|
612
|
+
currentResult.usage.costInput = addUsageValue(
|
|
613
|
+
currentResult.usage.costInput ?? 0,
|
|
614
|
+
protocolUsageCost(cost?.input)
|
|
615
|
+
);
|
|
616
|
+
currentResult.usage.costOutput = addUsageValue(
|
|
617
|
+
currentResult.usage.costOutput ?? 0,
|
|
618
|
+
protocolUsageCost(cost?.output)
|
|
619
|
+
);
|
|
620
|
+
currentResult.usage.costCacheRead = addUsageValue(
|
|
621
|
+
currentResult.usage.costCacheRead ?? 0,
|
|
622
|
+
protocolUsageCost(cost?.cacheRead)
|
|
623
|
+
);
|
|
624
|
+
currentResult.usage.costCacheWrite = addUsageValue(
|
|
625
|
+
currentResult.usage.costCacheWrite ?? 0,
|
|
626
|
+
protocolUsageCost(cost?.cacheWrite)
|
|
627
|
+
);
|
|
628
|
+
currentResult.usage.totalTokens = addUsageValue(
|
|
629
|
+
currentResult.usage.totalTokens ?? 0,
|
|
630
|
+
turnTotal
|
|
631
|
+
);
|
|
632
|
+
currentResult.usage.contextTokens = turnTotal;
|
|
633
|
+
}
|
|
634
|
+
if (typeof msg.provider === "string") currentResult.actualProvider = msg.provider;
|
|
635
|
+
const actualModel = typeof msg.responseModel === "string" ? msg.responseModel : typeof msg.model === "string" ? msg.model : void 0;
|
|
636
|
+
if (actualModel) currentResult.actualModel = actualModel;
|
|
637
|
+
if (typeof msg.stopReason === "string") currentResult.stopReason = msg.stopReason;
|
|
638
|
+
if (typeof msg.errorMessage === "string") setErrorMessage(msg.errorMessage);
|
|
639
|
+
}
|
|
640
|
+
emitUpdate();
|
|
641
|
+
} else if (event.type === "tool_result_end" && event.message) {
|
|
642
|
+
journalMessages(progressJournal, [event.message]);
|
|
643
|
+
budgetMonitor?.recordActivity();
|
|
644
|
+
addMessage(event.message);
|
|
645
|
+
emitUpdate();
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
const decoder = new JsonLineDecoder({
|
|
649
|
+
onValue: processEvent,
|
|
650
|
+
onMalformed: () => {
|
|
651
|
+
currentResult.malformedEvents = (currentResult.malformedEvents ?? 0) + 1;
|
|
652
|
+
},
|
|
653
|
+
onOversized: () => {
|
|
654
|
+
currentResult.truncated = true;
|
|
655
|
+
}
|
|
656
|
+
});
|
|
657
|
+
const beginTermination = (exitCode2) => {
|
|
658
|
+
if (cleanupTermination || settled) return;
|
|
659
|
+
cleanupTermination = terminateProcess(proc);
|
|
660
|
+
terminationDeadline = setTimeout(() => {
|
|
661
|
+
decoder.finish();
|
|
662
|
+
proc.stdin?.destroy();
|
|
663
|
+
proc.stdout?.destroy();
|
|
664
|
+
proc.stderr?.destroy();
|
|
665
|
+
finish(exitCode2);
|
|
666
|
+
}, KILL_GRACE_MS + 1e3);
|
|
667
|
+
};
|
|
668
|
+
const stopForBudget = (stop) => {
|
|
669
|
+
if (budgetStop || settled || wasAborted) return;
|
|
670
|
+
budgetStop = stop;
|
|
671
|
+
timedOut = stop.reason.endsWith("timeout");
|
|
672
|
+
currentResult.timedOut = timedOut || void 0;
|
|
673
|
+
currentResult.stopReason = timedOut ? "timeout" : "limit";
|
|
674
|
+
const message = formatTurnTerminationMessage(stop.reason, stop.limit);
|
|
675
|
+
setErrorMessage(message);
|
|
676
|
+
const bounded = appendBounded(
|
|
677
|
+
currentResult.stderr,
|
|
678
|
+
`
|
|
679
|
+
${message}.`,
|
|
680
|
+
DEFAULT_MAX_STDERR_BYTES
|
|
681
|
+
);
|
|
682
|
+
currentResult.stderr = bounded.text;
|
|
683
|
+
currentResult.truncated ||= bounded.truncated;
|
|
684
|
+
emitUpdate();
|
|
685
|
+
beginTermination(124);
|
|
686
|
+
};
|
|
687
|
+
budgetMonitor = new TurnBudgetMonitor({
|
|
688
|
+
...launchPolicy?.turnLimits,
|
|
689
|
+
onExceeded: stopForBudget
|
|
690
|
+
});
|
|
691
|
+
proc.once("spawn", () => {
|
|
692
|
+
currentResult.processStarted = true;
|
|
693
|
+
if (settled || budgetStop || wasAborted) return;
|
|
694
|
+
timeout = setTimeout(() => {
|
|
695
|
+
stopForBudget({
|
|
696
|
+
reason: launchPolicy?.workTimeoutReason ?? "work_timeout",
|
|
697
|
+
limit: launchPolicy?.workTimeoutReportLimit ?? timeoutMs
|
|
698
|
+
});
|
|
699
|
+
}, timeoutMs);
|
|
700
|
+
timeout.unref();
|
|
701
|
+
});
|
|
702
|
+
proc.stdout?.on("data", (data) => decoder.push(data));
|
|
703
|
+
proc.stderr?.on("data", (data) => {
|
|
704
|
+
const bounded = appendBounded(
|
|
705
|
+
currentResult.stderr,
|
|
706
|
+
data.toString(),
|
|
707
|
+
DEFAULT_MAX_STDERR_BYTES
|
|
708
|
+
);
|
|
709
|
+
currentResult.stderr = bounded.text;
|
|
710
|
+
currentResult.truncated ||= bounded.truncated;
|
|
711
|
+
});
|
|
712
|
+
proc.on("close", (code) => {
|
|
713
|
+
decoder.finish();
|
|
714
|
+
finish(budgetStop ? 124 : wasAborted ? 130 : code ?? 0);
|
|
715
|
+
});
|
|
716
|
+
proc.on("error", (error) => {
|
|
717
|
+
currentResult.launchFailed = currentResult.processStarted ? void 0 : true;
|
|
718
|
+
const message = setErrorMessage(error.message);
|
|
719
|
+
const bounded = appendBounded(
|
|
720
|
+
currentResult.stderr,
|
|
721
|
+
`${currentResult.stderr ? "\n" : ""}${message}`,
|
|
722
|
+
DEFAULT_MAX_STDERR_BYTES
|
|
723
|
+
);
|
|
724
|
+
currentResult.stderr = bounded.text;
|
|
725
|
+
currentResult.truncated ||= bounded.truncated;
|
|
726
|
+
if (currentResult.processStarted) beginTermination(1);
|
|
727
|
+
else finish(1);
|
|
728
|
+
});
|
|
729
|
+
if (signal) {
|
|
730
|
+
abortHandler = () => {
|
|
731
|
+
if (budgetStop || settled) return;
|
|
732
|
+
wasAborted = true;
|
|
733
|
+
currentResult.aborted = true;
|
|
734
|
+
currentResult.stopReason = "aborted";
|
|
735
|
+
setErrorMessage("Subagent was aborted");
|
|
736
|
+
beginTermination(130);
|
|
737
|
+
};
|
|
738
|
+
if (signal.aborted) abortHandler();
|
|
739
|
+
else signal.addEventListener("abort", abortHandler, { once: true });
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
if (signal?.aborted && budgetStop) {
|
|
743
|
+
budgetStop = void 0;
|
|
744
|
+
timedOut = false;
|
|
745
|
+
currentResult.timedOut = void 0;
|
|
746
|
+
currentResult.aborted = true;
|
|
747
|
+
currentResult.stopReason = "aborted";
|
|
748
|
+
setErrorMessage("Subagent was aborted");
|
|
749
|
+
}
|
|
750
|
+
currentResult.exitCode = currentResult.aborted ? 130 : exitCode;
|
|
751
|
+
const final = truncateUtf8(selectedAssistantOutput(), DEFAULT_MAX_OUTPUT_BYTES);
|
|
752
|
+
currentResult.finalOutput = final.text;
|
|
753
|
+
currentResult.truncated ||= final.truncated;
|
|
754
|
+
if (budgetStop) {
|
|
755
|
+
currentResult.partialOutput = currentResult.finalOutput || void 0;
|
|
756
|
+
currentResult.termination = {
|
|
757
|
+
version: TURN_TERMINATION_VERSION,
|
|
758
|
+
reason: budgetStop.reason,
|
|
759
|
+
limit: budgetStop.limit,
|
|
760
|
+
checkpoint: progressJournal.checkpoint(task, currentResult.partialOutput),
|
|
761
|
+
finalization: { attempted: false, status: "skipped", durationMs: 0 }
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
const remainingFinalizationMs = launchPolicy?.orchestrationDeadlineAt ? Math.floor(launchPolicy.orchestrationDeadlineAt - Date.now()) : void 0;
|
|
765
|
+
if (budgetStop && budgetStop.reason !== "orchestration_timeout" && launchPolicy?.finalizeOnTimeout !== false && !signal?.aborted && (remainingFinalizationMs === void 0 || remainingFinalizationMs > 0)) {
|
|
766
|
+
const finalizationStartedAt = Date.now();
|
|
767
|
+
const requestedFinalizationMs = resolveTimeoutFinalizationMs(
|
|
768
|
+
timeoutMs,
|
|
769
|
+
launchPolicy?.timeoutFinalizationMs
|
|
770
|
+
);
|
|
771
|
+
const finalizationMs = Math.min(
|
|
772
|
+
requestedFinalizationMs,
|
|
773
|
+
remainingFinalizationMs ?? requestedFinalizationMs
|
|
774
|
+
);
|
|
775
|
+
const summary = await runSingleAgent(
|
|
776
|
+
defaultCwd,
|
|
777
|
+
agents,
|
|
778
|
+
agentName,
|
|
779
|
+
buildTimeoutFinalizationPrompt({
|
|
780
|
+
task,
|
|
781
|
+
partialOutput: currentResult.partialOutput,
|
|
782
|
+
recentActivity: currentResult.recentActivity,
|
|
783
|
+
checkpoint: currentResult.termination?.checkpoint,
|
|
784
|
+
terminationReason: budgetStop.reason,
|
|
785
|
+
resultFormat: launchPolicy?.timeoutResultFormat
|
|
786
|
+
}),
|
|
787
|
+
cwd,
|
|
788
|
+
step,
|
|
789
|
+
signal,
|
|
790
|
+
thinkingLevel,
|
|
791
|
+
finalizationMs,
|
|
792
|
+
void 0,
|
|
793
|
+
makeDetails,
|
|
794
|
+
invocationOverride,
|
|
795
|
+
{
|
|
796
|
+
...launchPolicy,
|
|
797
|
+
tools: [],
|
|
798
|
+
disableExtensions: true,
|
|
799
|
+
disableSkills: true,
|
|
800
|
+
disablePromptTemplates: true,
|
|
801
|
+
disableContextFiles: true,
|
|
802
|
+
appendSystemPromptPaths: void 0,
|
|
803
|
+
finalizeOnTimeout: false,
|
|
804
|
+
turnLimits: void 0,
|
|
805
|
+
workTimeoutReason: "work_timeout",
|
|
806
|
+
workTimeoutReportLimit: finalizationMs,
|
|
807
|
+
orchestrationDeadlineAt: void 0
|
|
808
|
+
}
|
|
809
|
+
);
|
|
810
|
+
mergeUsageStats(currentResult.usage, summary.usage);
|
|
811
|
+
const summaryOutput = getResultFinalOutput(summary).trim();
|
|
812
|
+
if (summary.exitCode === 0 && summaryOutput) {
|
|
813
|
+
currentResult.timeoutSummary = summaryOutput;
|
|
814
|
+
currentResult.finalOutput = summaryOutput;
|
|
815
|
+
if (currentResult.termination) {
|
|
816
|
+
currentResult.termination.finalization = {
|
|
817
|
+
attempted: true,
|
|
818
|
+
status: "completed",
|
|
819
|
+
durationMs: Date.now() - finalizationStartedAt
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
} else {
|
|
823
|
+
currentResult.timeoutSummaryError = summary.errorMessage || summary.stderr.trim() || "Summary produced no final text";
|
|
824
|
+
if (currentResult.termination) {
|
|
825
|
+
currentResult.termination.finalization = {
|
|
826
|
+
attempted: true,
|
|
827
|
+
status: summary.timedOut ? "timed_out" : "failed",
|
|
828
|
+
durationMs: Date.now() - finalizationStartedAt,
|
|
829
|
+
error: currentResult.timeoutSummaryError
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
currentResult.truncated ||= summary.truncated;
|
|
834
|
+
}
|
|
835
|
+
if (currentResult.termination && !currentResult.finalOutput.trim()) {
|
|
836
|
+
currentResult.finalOutput = formatTimeoutCheckpoint(currentResult.termination.checkpoint);
|
|
837
|
+
}
|
|
838
|
+
if (currentResult.exitCode === 0 && launchPolicy?.resultFormat !== void 0) {
|
|
839
|
+
currentResult.structuredResult = parseAnyStructuredSubagentResult(
|
|
840
|
+
currentResult.finalOutput ?? "",
|
|
841
|
+
launchPolicy.resultFormat
|
|
842
|
+
);
|
|
843
|
+
currentResult.resultContractInvalid = launchPolicy.resultFormat !== "text" && currentResult.structuredResult === void 0;
|
|
844
|
+
if (currentResult.structuredResult?.version === "pi-subagents:result:v2" && launchPolicy.executionPlan) {
|
|
845
|
+
currentResult.structuredResult.provenance = {
|
|
846
|
+
...currentResult.structuredResult.provenance,
|
|
847
|
+
...launchPolicy.executionPlan.taskId ? { taskId: launchPolicy.executionPlan.taskId } : {},
|
|
848
|
+
taskGeneration: launchPolicy.executionPlan.taskGeneration,
|
|
849
|
+
executionPlanId: launchPolicy.executionPlan.id,
|
|
850
|
+
cancellationLineage: [...launchPolicy.executionPlan.cancellationLineage]
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
if (currentResult.structuredResult?.version === "pi-subagents:result:v2") {
|
|
854
|
+
currentResult.outcome = classifyStructuredOutcome(
|
|
855
|
+
currentResult.structuredResult.status,
|
|
856
|
+
currentResult.structuredResult.reasonCode
|
|
857
|
+
);
|
|
858
|
+
} else if (currentResult.resultContractInvalid) {
|
|
859
|
+
currentResult.outcome = classifyStructuredOutcome(
|
|
860
|
+
"contract-invalid",
|
|
861
|
+
"malformed-structured-result"
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
if (currentResult.exitCode === 0 && currentResult.stopReason !== "error" && (currentResult.stopReason === "toolUse" || !currentResult.finalOutput.trim())) {
|
|
866
|
+
currentResult.exitCode = 1;
|
|
867
|
+
currentResult.stopReason = "error";
|
|
868
|
+
setErrorMessage("Subagent completed without final text");
|
|
869
|
+
}
|
|
870
|
+
if (currentResult.capabilityGrant?.state === "active") {
|
|
871
|
+
currentResult.capabilityGrant = revokeCapabilityGrant(
|
|
872
|
+
currentResult.capabilityGrant,
|
|
873
|
+
"turn-settled",
|
|
874
|
+
Date.now()
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
currentResult.policy = {
|
|
878
|
+
inherited: ["environment"],
|
|
879
|
+
overridden: [
|
|
880
|
+
"cwd",
|
|
881
|
+
...agent.model ? ["model"] : [],
|
|
882
|
+
...thinkingLevel ? ["thinkingLevel"] : [],
|
|
883
|
+
...effectiveTools !== void 0 ? ["tools"] : [],
|
|
884
|
+
...launchPolicy?.disableExtensions ? ["extensions"] : [],
|
|
885
|
+
...launchPolicy?.disableSkills ? ["skills"] : [],
|
|
886
|
+
...launchPolicy?.disablePromptTemplates ? ["promptTemplates"] : [],
|
|
887
|
+
...launchPolicy?.disableContextFiles ? ["contextFiles"] : []
|
|
888
|
+
],
|
|
889
|
+
unsupported: ["approvalPolicy", "sandboxProfile", "providerHeaders"]
|
|
890
|
+
};
|
|
891
|
+
return currentResult;
|
|
892
|
+
} finally {
|
|
893
|
+
for (const temporary of temporaryPrompts.reverse()) {
|
|
894
|
+
try {
|
|
895
|
+
fs.unlinkSync(temporary.filePath);
|
|
896
|
+
} catch {
|
|
897
|
+
}
|
|
898
|
+
try {
|
|
899
|
+
fs.rmdirSync(temporary.dir);
|
|
900
|
+
} catch {
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
export {
|
|
907
|
+
JsonLineDecoder,
|
|
908
|
+
buildFanInContext,
|
|
909
|
+
mapWithConcurrencyLimit,
|
|
910
|
+
terminateProcess,
|
|
911
|
+
runSingleAgent
|
|
912
|
+
};
|
|
913
|
+
//# sourceMappingURL=chunk-DQMN4OYM.ts.map
|