@cydm/pie 1.0.10 → 1.0.12
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/dist/builtin/extensions/ask-user/index.js +2 -2
- package/dist/builtin/extensions/plan-mode/index.js +2 -2
- package/dist/builtin/extensions/subagent/index.js +3 -3
- package/dist/builtin/extensions/todo/index.js +2 -2
- package/dist/chunks/{chunk-A5JSJAPK.js → chunk-D7NAXU7F.js} +258 -71
- package/dist/chunks/{chunk-EJGQAAKS.js → chunk-SW6G4XW2.js} +329 -65
- package/dist/chunks/{chunk-R2HMYSEK.js → chunk-YLOLJRLJ.js} +2 -2
- package/dist/chunks/{src-3X3HBT2G.js → src-LZC56DRG.js} +1 -1
- package/dist/cli.js +27 -8
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
createAskUserCapability
|
|
4
|
-
} from "../../../chunks/chunk-
|
|
5
|
-
import "../../../chunks/chunk-
|
|
4
|
+
} from "../../../chunks/chunk-SW6G4XW2.js";
|
|
5
|
+
import "../../../chunks/chunk-D7NAXU7F.js";
|
|
6
6
|
import "../../../chunks/chunk-TG2EQLX2.js";
|
|
7
7
|
|
|
8
8
|
// builtin/extensions/ask-user/index.ts
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
isPlanModeSafeCommand,
|
|
8
8
|
markCompletedPlanSteps,
|
|
9
9
|
restoreExecutionState
|
|
10
|
-
} from "../../../chunks/chunk-
|
|
11
|
-
import "../../../chunks/chunk-
|
|
10
|
+
} from "../../../chunks/chunk-SW6G4XW2.js";
|
|
11
|
+
import "../../../chunks/chunk-D7NAXU7F.js";
|
|
12
12
|
import "../../../chunks/chunk-TG2EQLX2.js";
|
|
13
13
|
|
|
14
14
|
// builtin/extensions/plan-mode/index.ts
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
createCliHostCapabilities
|
|
4
|
-
} from "../../../chunks/chunk-
|
|
4
|
+
} from "../../../chunks/chunk-YLOLJRLJ.js";
|
|
5
5
|
import {
|
|
6
6
|
createSharedFileSystemTools,
|
|
7
7
|
createSubagentCapability
|
|
8
|
-
} from "../../../chunks/chunk-
|
|
9
|
-
import "../../../chunks/chunk-
|
|
8
|
+
} from "../../../chunks/chunk-SW6G4XW2.js";
|
|
9
|
+
import "../../../chunks/chunk-D7NAXU7F.js";
|
|
10
10
|
import "../../../chunks/chunk-TG2EQLX2.js";
|
|
11
11
|
|
|
12
12
|
// builtin/extensions/subagent/index.ts
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
executeManageTodoList,
|
|
8
8
|
executionStateToTodos,
|
|
9
9
|
restoreExecutionState
|
|
10
|
-
} from "../../../chunks/chunk-
|
|
11
|
-
import "../../../chunks/chunk-
|
|
10
|
+
} from "../../../chunks/chunk-SW6G4XW2.js";
|
|
11
|
+
import "../../../chunks/chunk-D7NAXU7F.js";
|
|
12
12
|
import "../../../chunks/chunk-TG2EQLX2.js";
|
|
13
13
|
|
|
14
14
|
// builtin/extensions/todo/index.ts
|
|
@@ -3758,6 +3758,7 @@ var NodeHttpClient = class {
|
|
|
3758
3758
|
async request(url, options = {}) {
|
|
3759
3759
|
const requestStartTime = Date.now();
|
|
3760
3760
|
const requestId = `${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
3761
|
+
const requestTimeoutMs = options.timeoutMs ?? 6e4;
|
|
3761
3762
|
logHttp("DEBUG", `Request [${requestId}] started`, {
|
|
3762
3763
|
url: url.slice(0, 100),
|
|
3763
3764
|
method: options.method || "GET",
|
|
@@ -3857,13 +3858,32 @@ var NodeHttpClient = class {
|
|
|
3857
3858
|
let done = false;
|
|
3858
3859
|
let error = null;
|
|
3859
3860
|
let waiting = null;
|
|
3860
|
-
const
|
|
3861
|
+
const wake = (value) => {
|
|
3862
|
+
if (!waiting) return;
|
|
3863
|
+
const w = waiting;
|
|
3864
|
+
waiting = null;
|
|
3865
|
+
w(value);
|
|
3866
|
+
};
|
|
3867
|
+
const failStream = (e) => {
|
|
3868
|
+
if (done && error) return;
|
|
3869
|
+
error = e;
|
|
3870
|
+
done = true;
|
|
3871
|
+
wake({ done: true, value: void 0 });
|
|
3872
|
+
logHttp("ERROR", `Request [${requestId}] SSE stream error`, { error: e.message });
|
|
3873
|
+
};
|
|
3874
|
+
const flushBuffer = (final = false) => {
|
|
3861
3875
|
if (bufferLength === 0) return;
|
|
3862
3876
|
const buffer = Buffer.concat(bufferChunks, bufferLength);
|
|
3863
3877
|
const bufferStr = buffer.toString("utf-8");
|
|
3864
3878
|
const lines = bufferStr.split("\n");
|
|
3865
3879
|
const lastLine = lines.pop();
|
|
3866
|
-
if (
|
|
3880
|
+
if (final) {
|
|
3881
|
+
if (lastLine !== void 0 && lastLine.length > 0) {
|
|
3882
|
+
lines.push(lastLine);
|
|
3883
|
+
}
|
|
3884
|
+
bufferChunks = [];
|
|
3885
|
+
bufferLength = 0;
|
|
3886
|
+
} else if (lastLine !== void 0) {
|
|
3867
3887
|
bufferChunks = [Buffer.from(lastLine)];
|
|
3868
3888
|
bufferLength = lastLine.length;
|
|
3869
3889
|
} else {
|
|
@@ -3874,10 +3894,17 @@ var NodeHttpClient = class {
|
|
|
3874
3894
|
lineQueue.push(line);
|
|
3875
3895
|
}
|
|
3876
3896
|
};
|
|
3897
|
+
let abortHandler = null;
|
|
3877
3898
|
if (signal) {
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3899
|
+
abortHandler = () => {
|
|
3900
|
+
failStream(new Error("Request was aborted"));
|
|
3901
|
+
res.destroy(new Error("Request was aborted"));
|
|
3902
|
+
};
|
|
3903
|
+
if (signal.aborted) {
|
|
3904
|
+
abortHandler();
|
|
3905
|
+
} else {
|
|
3906
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
3907
|
+
}
|
|
3881
3908
|
}
|
|
3882
3909
|
res.on("data", (chunk) => {
|
|
3883
3910
|
totalBytes += chunk.length;
|
|
@@ -3886,22 +3913,18 @@ var NodeHttpClient = class {
|
|
|
3886
3913
|
if (bufferLength > 16384 || chunk.includes(10)) {
|
|
3887
3914
|
flushBuffer();
|
|
3888
3915
|
}
|
|
3889
|
-
|
|
3890
|
-
const w = waiting;
|
|
3891
|
-
waiting = null;
|
|
3892
|
-
w({ done: false, value: void 0 });
|
|
3893
|
-
}
|
|
3916
|
+
wake({ done: false, value: void 0 });
|
|
3894
3917
|
});
|
|
3895
3918
|
res.on("end", () => {
|
|
3896
3919
|
if (bufferLength > 0) {
|
|
3897
|
-
flushBuffer();
|
|
3920
|
+
flushBuffer(true);
|
|
3898
3921
|
}
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
waiting = null;
|
|
3903
|
-
w({ done: true, value: void 0 });
|
|
3922
|
+
if (!res.complete) {
|
|
3923
|
+
failStream(new Error("SSE stream ended before response completed"));
|
|
3924
|
+
return;
|
|
3904
3925
|
}
|
|
3926
|
+
done = true;
|
|
3927
|
+
wake({ done: true, value: void 0 });
|
|
3905
3928
|
logHttp("DEBUG", `Request [${requestId}] SSE stream ended`, {
|
|
3906
3929
|
totalBytes,
|
|
3907
3930
|
lineCount,
|
|
@@ -3909,14 +3932,15 @@ var NodeHttpClient = class {
|
|
|
3909
3932
|
});
|
|
3910
3933
|
});
|
|
3911
3934
|
res.on("error", (e) => {
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3935
|
+
failStream(e);
|
|
3936
|
+
});
|
|
3937
|
+
res.on("aborted", () => {
|
|
3938
|
+
failStream(new Error("SSE stream aborted"));
|
|
3939
|
+
});
|
|
3940
|
+
res.on("close", () => {
|
|
3941
|
+
if (!done || !res.complete) {
|
|
3942
|
+
failStream(new Error("SSE stream closed before end"));
|
|
3918
3943
|
}
|
|
3919
|
-
logHttp("ERROR", `Request [${requestId}] SSE stream error`, { error: e.message });
|
|
3920
3944
|
});
|
|
3921
3945
|
try {
|
|
3922
3946
|
while (true) {
|
|
@@ -3936,9 +3960,12 @@ var NodeHttpClient = class {
|
|
|
3936
3960
|
const waitResult = await new Promise((r) => {
|
|
3937
3961
|
waiting = r;
|
|
3938
3962
|
});
|
|
3939
|
-
|
|
3963
|
+
void waitResult;
|
|
3940
3964
|
}
|
|
3941
3965
|
} finally {
|
|
3966
|
+
if (signal && abortHandler) {
|
|
3967
|
+
signal.removeEventListener("abort", abortHandler);
|
|
3968
|
+
}
|
|
3942
3969
|
if (!done && !res.destroyed) {
|
|
3943
3970
|
res.destroy();
|
|
3944
3971
|
}
|
|
@@ -3965,6 +3992,7 @@ var NodeHttpClient = class {
|
|
|
3965
3992
|
logHttp("ERROR", `Request [${requestId}] timeout`);
|
|
3966
3993
|
req.destroy(new Error("Request timeout"));
|
|
3967
3994
|
});
|
|
3995
|
+
req.setTimeout(requestTimeoutMs);
|
|
3968
3996
|
if (options.signal) {
|
|
3969
3997
|
options.signal.addEventListener("abort", () => {
|
|
3970
3998
|
logHttp("DEBUG", `Request [${requestId}] aborted`);
|
|
@@ -4371,6 +4399,47 @@ var FileSystemGateway = class {
|
|
|
4371
4399
|
const fs = nodeRequire("fs");
|
|
4372
4400
|
fs.writeFileSync(path, content, encoding);
|
|
4373
4401
|
}
|
|
4402
|
+
/**
|
|
4403
|
+
* Copy a file.
|
|
4404
|
+
*/
|
|
4405
|
+
copyFile(source, destination) {
|
|
4406
|
+
if (!this.allowWrites) {
|
|
4407
|
+
throw new Error(`Writes not allowed: ${destination}`);
|
|
4408
|
+
}
|
|
4409
|
+
this.validatePath(source, "copy");
|
|
4410
|
+
this.validatePath(destination, "copy");
|
|
4411
|
+
const platform = detectPlatform();
|
|
4412
|
+
const dir = this.dirname(destination);
|
|
4413
|
+
this.mkdir(dir, { recursive: true });
|
|
4414
|
+
if (platform === "puerts" && typeof CS !== "undefined") {
|
|
4415
|
+
CS.System.IO.File.Copy(source, destination, true);
|
|
4416
|
+
return;
|
|
4417
|
+
}
|
|
4418
|
+
const fs = nodeRequire("fs");
|
|
4419
|
+
fs.copyFileSync(source, destination);
|
|
4420
|
+
}
|
|
4421
|
+
/**
|
|
4422
|
+
* Rename or move a file.
|
|
4423
|
+
*/
|
|
4424
|
+
renameFile(source, destination) {
|
|
4425
|
+
if (!this.allowWrites) {
|
|
4426
|
+
throw new Error(`Writes not allowed: ${destination}`);
|
|
4427
|
+
}
|
|
4428
|
+
this.validatePath(source, "rename");
|
|
4429
|
+
this.validatePath(destination, "rename");
|
|
4430
|
+
const platform = detectPlatform();
|
|
4431
|
+
const dir = this.dirname(destination);
|
|
4432
|
+
this.mkdir(dir, { recursive: true });
|
|
4433
|
+
if (platform === "puerts" && typeof CS !== "undefined") {
|
|
4434
|
+
if (CS.System.IO.File.Exists(destination)) {
|
|
4435
|
+
CS.System.IO.File.Delete(destination);
|
|
4436
|
+
}
|
|
4437
|
+
CS.System.IO.File.Move(source, destination);
|
|
4438
|
+
return;
|
|
4439
|
+
}
|
|
4440
|
+
const fs = nodeRequire("fs");
|
|
4441
|
+
fs.renameSync(source, destination);
|
|
4442
|
+
}
|
|
4374
4443
|
/**
|
|
4375
4444
|
* Delete file
|
|
4376
4445
|
*/
|
|
@@ -4937,6 +5006,52 @@ function mapOpenAIStopReason(reason) {
|
|
|
4937
5006
|
}
|
|
4938
5007
|
}
|
|
4939
5008
|
|
|
5009
|
+
// ../../packages/ai/src/providers/assistant-content-validation.ts
|
|
5010
|
+
var EMPTY_ASSISTANT_CONTENT_ERROR = "stream ended without assistant content";
|
|
5011
|
+
var EMPTY_ASSISTANT_CONTENT_ERROR_CODE = "empty_assistant_content";
|
|
5012
|
+
var EmptyAssistantContentError = class extends Error {
|
|
5013
|
+
code = EMPTY_ASSISTANT_CONTENT_ERROR_CODE;
|
|
5014
|
+
constructor(message = EMPTY_ASSISTANT_CONTENT_ERROR) {
|
|
5015
|
+
super(message);
|
|
5016
|
+
this.name = "EmptyAssistantContentError";
|
|
5017
|
+
}
|
|
5018
|
+
};
|
|
5019
|
+
function hasAssistantContent(message) {
|
|
5020
|
+
return message.content.some((block) => {
|
|
5021
|
+
if (block.type === "text") {
|
|
5022
|
+
return block.text.trim().length > 0;
|
|
5023
|
+
}
|
|
5024
|
+
if (block.type === "thinking") {
|
|
5025
|
+
return block.thinking.trim().length > 0;
|
|
5026
|
+
}
|
|
5027
|
+
if (block.type === "toolCall") {
|
|
5028
|
+
return block.name.trim().length > 0;
|
|
5029
|
+
}
|
|
5030
|
+
return false;
|
|
5031
|
+
});
|
|
5032
|
+
}
|
|
5033
|
+
function assertAssistantHasContent(message) {
|
|
5034
|
+
if (!hasAssistantContent(message)) {
|
|
5035
|
+
throw new EmptyAssistantContentError();
|
|
5036
|
+
}
|
|
5037
|
+
}
|
|
5038
|
+
function isEmptyAssistantContentError(error) {
|
|
5039
|
+
return error instanceof EmptyAssistantContentError || typeof error === "object" && error !== null && "code" in error && error.code === EMPTY_ASSISTANT_CONTENT_ERROR_CODE;
|
|
5040
|
+
}
|
|
5041
|
+
function logEmptyAssistantContentError(error, model, options) {
|
|
5042
|
+
if (!isEmptyAssistantContentError(error)) {
|
|
5043
|
+
return;
|
|
5044
|
+
}
|
|
5045
|
+
getLogger().child({ module: "ai.provider" }).warn(EMPTY_ASSISTANT_CONTENT_ERROR, {
|
|
5046
|
+
code: EMPTY_ASSISTANT_CONTENT_ERROR_CODE,
|
|
5047
|
+
provider: model.provider,
|
|
5048
|
+
model: model.id,
|
|
5049
|
+
api: model.api,
|
|
5050
|
+
sessionId: options?.sessionId,
|
|
5051
|
+
metadata: options?.metadata
|
|
5052
|
+
});
|
|
5053
|
+
}
|
|
5054
|
+
|
|
4940
5055
|
// ../../packages/ai/src/providers/openai-compat.ts
|
|
4941
5056
|
var streamOpenAICompletions = (model, context, options) => {
|
|
4942
5057
|
const stream = new AssistantMessageEventStream();
|
|
@@ -5172,9 +5287,11 @@ var streamOpenAICompletions = (model, context, options) => {
|
|
|
5172
5287
|
if (output.stopReason === "aborted" || output.stopReason === "error") {
|
|
5173
5288
|
throw new Error("An unknown error occurred");
|
|
5174
5289
|
}
|
|
5290
|
+
assertAssistantHasContent(output);
|
|
5175
5291
|
stream.push({ type: "done", reason: output.stopReason, message: output });
|
|
5176
5292
|
stream.end();
|
|
5177
5293
|
} catch (error) {
|
|
5294
|
+
logEmptyAssistantContentError(error, model, options);
|
|
5178
5295
|
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
5179
5296
|
output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
|
|
5180
5297
|
stream.push({ type: "error", reason: output.stopReason, error: output });
|
|
@@ -5553,9 +5670,11 @@ var streamAnthropic = (model, context, options) => {
|
|
|
5553
5670
|
if (options?.signal?.aborted) {
|
|
5554
5671
|
throw new Error("Request was aborted");
|
|
5555
5672
|
}
|
|
5673
|
+
assertAssistantHasContent(output);
|
|
5556
5674
|
stream.push({ type: "done", reason: output.stopReason, message: output });
|
|
5557
5675
|
stream.end();
|
|
5558
5676
|
} catch (error) {
|
|
5677
|
+
logEmptyAssistantContentError(error, model, options);
|
|
5559
5678
|
for (const block of output.content) delete block.index;
|
|
5560
5679
|
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
5561
5680
|
output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
|
|
@@ -9002,35 +9121,78 @@ registerApiProvider({
|
|
|
9002
9121
|
});
|
|
9003
9122
|
|
|
9004
9123
|
// ../../packages/agent-core/src/agent-loop.ts
|
|
9124
|
+
function createLoopErrorMessage(error, config, signal) {
|
|
9125
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9126
|
+
return {
|
|
9127
|
+
role: "assistant",
|
|
9128
|
+
content: [{ type: "text", text: "" }],
|
|
9129
|
+
api: config.model.api,
|
|
9130
|
+
provider: config.model.provider,
|
|
9131
|
+
model: config.model.id,
|
|
9132
|
+
usage: {
|
|
9133
|
+
input: 0,
|
|
9134
|
+
output: 0,
|
|
9135
|
+
cacheRead: 0,
|
|
9136
|
+
cacheWrite: 0,
|
|
9137
|
+
totalTokens: 0,
|
|
9138
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
|
|
9139
|
+
},
|
|
9140
|
+
stopReason: signal?.aborted ? "aborted" : "error",
|
|
9141
|
+
errorMessage: message,
|
|
9142
|
+
timestamp: Date.now()
|
|
9143
|
+
};
|
|
9144
|
+
}
|
|
9145
|
+
async function notifyOnError(config, error, phase) {
|
|
9146
|
+
try {
|
|
9147
|
+
await config.hooks?.onError?.(error instanceof Error ? error : new Error(String(error)), { phase });
|
|
9148
|
+
} catch (hookError) {
|
|
9149
|
+
console.warn(`[AgentLoop] onError hook failed during ${phase}:`, hookError);
|
|
9150
|
+
}
|
|
9151
|
+
}
|
|
9152
|
+
function endStreamWithError(stream, newMessages, config, signal, error) {
|
|
9153
|
+
const errorMessage = createLoopErrorMessage(error, config, signal);
|
|
9154
|
+
newMessages.push(errorMessage);
|
|
9155
|
+
stream.push({ type: "message_start", message: errorMessage });
|
|
9156
|
+
stream.push({ type: "message_end", message: errorMessage });
|
|
9157
|
+
stream.push({ type: "turn_end", message: errorMessage, toolResults: [] });
|
|
9158
|
+
stream.push({ type: "agent_end", messages: newMessages });
|
|
9159
|
+
stream.end(newMessages);
|
|
9160
|
+
}
|
|
9005
9161
|
function agentLoop(prompts, context, config, signal, streamFn) {
|
|
9006
9162
|
const stream = createAgentStream();
|
|
9007
9163
|
(async () => {
|
|
9008
|
-
try {
|
|
9009
|
-
await config.hooks?.beforeAgentStart?.();
|
|
9010
|
-
} catch (e) {
|
|
9011
|
-
console.warn("[AgentLoop] beforeAgentStart hook failed:", e);
|
|
9012
|
-
}
|
|
9013
9164
|
const newMessages = [...prompts];
|
|
9014
|
-
const currentContext = {
|
|
9015
|
-
...context,
|
|
9016
|
-
messages: [...context.messages, ...prompts]
|
|
9017
|
-
};
|
|
9018
|
-
stream.push({ type: "agent_start" });
|
|
9019
|
-
stream.push({ type: "turn_start" });
|
|
9020
|
-
for (const prompt of prompts) {
|
|
9021
|
-
stream.push({ type: "message_start", message: prompt });
|
|
9022
|
-
stream.push({ type: "message_end", message: prompt });
|
|
9023
|
-
}
|
|
9024
9165
|
try {
|
|
9025
|
-
|
|
9166
|
+
try {
|
|
9167
|
+
await config.hooks?.beforeAgentStart?.();
|
|
9168
|
+
} catch (e) {
|
|
9169
|
+
console.warn("[AgentLoop] beforeAgentStart hook failed:", e);
|
|
9170
|
+
}
|
|
9171
|
+
const currentContext = {
|
|
9172
|
+
...context,
|
|
9173
|
+
messages: [...context.messages, ...prompts]
|
|
9174
|
+
};
|
|
9175
|
+
stream.push({ type: "agent_start" });
|
|
9176
|
+
stream.push({ type: "turn_start" });
|
|
9177
|
+
for (const prompt of prompts) {
|
|
9178
|
+
stream.push({ type: "message_start", message: prompt });
|
|
9179
|
+
stream.push({ type: "message_end", message: prompt });
|
|
9180
|
+
}
|
|
9181
|
+
try {
|
|
9182
|
+
await runLoop(currentContext, newMessages, config, signal, stream, streamFn);
|
|
9183
|
+
} catch (error) {
|
|
9184
|
+
await notifyOnError(config, error, "runLoop");
|
|
9185
|
+
endStreamWithError(stream, newMessages, config, signal, error);
|
|
9186
|
+
return;
|
|
9187
|
+
}
|
|
9188
|
+
try {
|
|
9189
|
+
await config.hooks?.afterAgentEnd?.(newMessages);
|
|
9190
|
+
} catch (e) {
|
|
9191
|
+
console.warn("[AgentLoop] afterAgentEnd hook failed:", e);
|
|
9192
|
+
}
|
|
9026
9193
|
} catch (error) {
|
|
9027
|
-
config
|
|
9028
|
-
|
|
9029
|
-
}
|
|
9030
|
-
try {
|
|
9031
|
-
await config.hooks?.afterAgentEnd?.(newMessages);
|
|
9032
|
-
} catch (e) {
|
|
9033
|
-
console.warn("[AgentLoop] afterAgentEnd hook failed:", e);
|
|
9194
|
+
await notifyOnError(config, error, "agentLoop");
|
|
9195
|
+
endStreamWithError(stream, newMessages, config, signal, error);
|
|
9034
9196
|
}
|
|
9035
9197
|
})();
|
|
9036
9198
|
return stream;
|
|
@@ -9044,25 +9206,31 @@ function agentLoopContinue(context, config, signal, streamFn) {
|
|
|
9044
9206
|
}
|
|
9045
9207
|
const stream = createAgentStream();
|
|
9046
9208
|
(async () => {
|
|
9047
|
-
try {
|
|
9048
|
-
await config.hooks?.beforeAgentStart?.();
|
|
9049
|
-
} catch (e) {
|
|
9050
|
-
console.warn("[AgentLoop] beforeAgentStart hook failed:", e);
|
|
9051
|
-
}
|
|
9052
9209
|
const newMessages = [];
|
|
9053
|
-
const currentContext = { ...context };
|
|
9054
|
-
stream.push({ type: "agent_start" });
|
|
9055
|
-
stream.push({ type: "turn_start" });
|
|
9056
9210
|
try {
|
|
9057
|
-
|
|
9211
|
+
try {
|
|
9212
|
+
await config.hooks?.beforeAgentStart?.();
|
|
9213
|
+
} catch (e) {
|
|
9214
|
+
console.warn("[AgentLoop] beforeAgentStart hook failed:", e);
|
|
9215
|
+
}
|
|
9216
|
+
const currentContext = { ...context };
|
|
9217
|
+
stream.push({ type: "agent_start" });
|
|
9218
|
+
stream.push({ type: "turn_start" });
|
|
9219
|
+
try {
|
|
9220
|
+
await runLoop(currentContext, newMessages, config, signal, stream, streamFn);
|
|
9221
|
+
} catch (error) {
|
|
9222
|
+
await notifyOnError(config, error, "runLoop");
|
|
9223
|
+
endStreamWithError(stream, newMessages, config, signal, error);
|
|
9224
|
+
return;
|
|
9225
|
+
}
|
|
9226
|
+
try {
|
|
9227
|
+
await config.hooks?.afterAgentEnd?.(newMessages);
|
|
9228
|
+
} catch (e) {
|
|
9229
|
+
console.warn("[AgentLoop] afterAgentEnd hook failed:", e);
|
|
9230
|
+
}
|
|
9058
9231
|
} catch (error) {
|
|
9059
|
-
config
|
|
9060
|
-
|
|
9061
|
-
}
|
|
9062
|
-
try {
|
|
9063
|
-
await config.hooks?.afterAgentEnd?.(newMessages);
|
|
9064
|
-
} catch (e) {
|
|
9065
|
-
console.warn("[AgentLoop] afterAgentEnd hook failed:", e);
|
|
9232
|
+
await notifyOnError(config, error, "agentLoopContinue");
|
|
9233
|
+
endStreamWithError(stream, newMessages, config, signal, error);
|
|
9066
9234
|
}
|
|
9067
9235
|
})();
|
|
9068
9236
|
return stream;
|
|
@@ -9098,7 +9266,7 @@ async function runLoop(currentContext, newMessages, config, signal, stream, stre
|
|
|
9098
9266
|
try {
|
|
9099
9267
|
message = await streamAssistantResponse(currentContext, config, signal, stream, streamFn);
|
|
9100
9268
|
} catch (error) {
|
|
9101
|
-
config
|
|
9269
|
+
await notifyOnError(config, error, "streamAssistantResponse");
|
|
9102
9270
|
throw error;
|
|
9103
9271
|
}
|
|
9104
9272
|
newMessages.push(message);
|
|
@@ -9274,10 +9442,14 @@ async function executeToolCalls(tools, assistantMessage, signal, stream, getStee
|
|
|
9274
9442
|
details: {}
|
|
9275
9443
|
};
|
|
9276
9444
|
isError = true;
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9445
|
+
try {
|
|
9446
|
+
await hooks?.onError?.(e instanceof Error ? e : new Error(String(e)), {
|
|
9447
|
+
toolName: toolCall.name,
|
|
9448
|
+
phase: "toolExecution"
|
|
9449
|
+
});
|
|
9450
|
+
} catch (hookError) {
|
|
9451
|
+
console.warn(`[AgentLoop] onError hook failed during toolExecution for ${toolCall.name}:`, hookError);
|
|
9452
|
+
}
|
|
9281
9453
|
}
|
|
9282
9454
|
}
|
|
9283
9455
|
try {
|
|
@@ -9789,15 +9961,30 @@ var Agent = class {
|
|
|
9789
9961
|
this.abortController = void 0;
|
|
9790
9962
|
}
|
|
9791
9963
|
}
|
|
9964
|
+
reportObserverError(kind, eventType, error) {
|
|
9965
|
+
console.warn(`[Agent] ${kind} failed while handling ${eventType}:`, error);
|
|
9966
|
+
}
|
|
9792
9967
|
emit(e) {
|
|
9793
9968
|
for (const listener of this.listeners) {
|
|
9794
|
-
|
|
9969
|
+
try {
|
|
9970
|
+
listener(e);
|
|
9971
|
+
} catch (error) {
|
|
9972
|
+
this.reportObserverError("agent event listener", e.type, error);
|
|
9973
|
+
}
|
|
9974
|
+
}
|
|
9975
|
+
try {
|
|
9976
|
+
this.emitDerivedSemanticEvents(e);
|
|
9977
|
+
} catch (error) {
|
|
9978
|
+
this.reportObserverError("derived semantic event", e.type, error);
|
|
9795
9979
|
}
|
|
9796
|
-
this.emitDerivedSemanticEvents(e);
|
|
9797
9980
|
}
|
|
9798
9981
|
emitSemantic(e) {
|
|
9799
9982
|
for (const listener of this.semanticListeners) {
|
|
9800
|
-
|
|
9983
|
+
try {
|
|
9984
|
+
listener(e);
|
|
9985
|
+
} catch (error) {
|
|
9986
|
+
this.reportObserverError("semantic event listener", e.type, error);
|
|
9987
|
+
}
|
|
9801
9988
|
}
|
|
9802
9989
|
}
|
|
9803
9990
|
updateStatusSnapshot(next) {
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
getFileSystem,
|
|
9
9
|
getPlatformConfig,
|
|
10
10
|
streamSimple
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-D7NAXU7F.js";
|
|
12
12
|
import {
|
|
13
13
|
__require
|
|
14
14
|
} from "./chunk-TG2EQLX2.js";
|
|
@@ -374,6 +374,182 @@ function createCompactionSummaryMessage(summary, timestamp) {
|
|
|
374
374
|
};
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
+
// ../../packages/agent-framework/src/session/durable-file.ts
|
|
378
|
+
var DEFAULT_FILE_RETRY_DELAYS_MS = [0, 25, 75, 150];
|
|
379
|
+
function durableFilePaths(filePath) {
|
|
380
|
+
return {
|
|
381
|
+
main: filePath,
|
|
382
|
+
backup: `${filePath}.bak`,
|
|
383
|
+
previous: `${filePath}.prev`
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
function isTransientFileError(error) {
|
|
387
|
+
const code = typeof error === "object" && error && "code" in error ? String(error.code) : "";
|
|
388
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
389
|
+
return /^(EPERM|EACCES|EBUSY|ENOTEMPTY)$/.test(code) || /\b(EPERM|EACCES|EBUSY|locked|busy)\b/i.test(message);
|
|
390
|
+
}
|
|
391
|
+
function sleep(ms) {
|
|
392
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
393
|
+
}
|
|
394
|
+
async function retryFileOperation(operation, retryDelaysMs) {
|
|
395
|
+
let lastError;
|
|
396
|
+
for (const [index, delayMs] of retryDelaysMs.entries()) {
|
|
397
|
+
if (delayMs > 0) {
|
|
398
|
+
await sleep(delayMs);
|
|
399
|
+
}
|
|
400
|
+
try {
|
|
401
|
+
operation();
|
|
402
|
+
return;
|
|
403
|
+
} catch (error) {
|
|
404
|
+
lastError = error;
|
|
405
|
+
if (!isTransientFileError(error) || index === retryDelaysMs.length - 1) {
|
|
406
|
+
throw error;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
throw lastError;
|
|
411
|
+
}
|
|
412
|
+
function retryDelays(options) {
|
|
413
|
+
return options.retryDelaysMs ?? DEFAULT_FILE_RETRY_DELAYS_MS;
|
|
414
|
+
}
|
|
415
|
+
function tempPathFor(filePath, operation) {
|
|
416
|
+
return `${filePath}.${operation}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
417
|
+
}
|
|
418
|
+
async function replaceFile(gateway, filePath, content, retryDelaysMs) {
|
|
419
|
+
const tempPath = tempPathFor(filePath, "durable");
|
|
420
|
+
gateway.writeFile(tempPath, content, "utf-8");
|
|
421
|
+
try {
|
|
422
|
+
try {
|
|
423
|
+
await retryFileOperation(() => gateway.renameFile(tempPath, filePath), retryDelaysMs);
|
|
424
|
+
} catch {
|
|
425
|
+
await retryFileOperation(() => gateway.copyFile(tempPath, filePath), retryDelaysMs);
|
|
426
|
+
try {
|
|
427
|
+
gateway.deleteFile(tempPath);
|
|
428
|
+
} catch {
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
} catch (error) {
|
|
432
|
+
try {
|
|
433
|
+
gateway.deleteFile(tempPath);
|
|
434
|
+
} catch {
|
|
435
|
+
}
|
|
436
|
+
throw error;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
async function copyExistingFile(gateway, sourcePath, destinationPath, retryDelaysMs) {
|
|
440
|
+
if (!gateway.exists(sourcePath)) {
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
await retryFileOperation(() => gateway.copyFile(sourcePath, destinationPath), retryDelaysMs);
|
|
444
|
+
return true;
|
|
445
|
+
}
|
|
446
|
+
function readCandidate(options, role, path3) {
|
|
447
|
+
if (!options.gateway.exists(path3)) {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
const rawContent = options.gateway.readFile(path3, "utf-8");
|
|
451
|
+
const parsed = options.parse(rawContent, path3);
|
|
452
|
+
const updatedAt = Number.isFinite(parsed.updatedAt) ? parsed.updatedAt : 0;
|
|
453
|
+
return {
|
|
454
|
+
...parsed,
|
|
455
|
+
updatedAt,
|
|
456
|
+
role,
|
|
457
|
+
path: path3,
|
|
458
|
+
rawContent
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
function candidatePriority(role) {
|
|
462
|
+
if (role === "main") return 3;
|
|
463
|
+
if (role === "backup") return 2;
|
|
464
|
+
return 1;
|
|
465
|
+
}
|
|
466
|
+
function chooseBestCandidate(candidates) {
|
|
467
|
+
return [...candidates].sort((a, b) => {
|
|
468
|
+
const updatedAtDiff = b.updatedAt - a.updatedAt;
|
|
469
|
+
if (updatedAtDiff !== 0) return updatedAtDiff;
|
|
470
|
+
return candidatePriority(b.role) - candidatePriority(a.role);
|
|
471
|
+
})[0];
|
|
472
|
+
}
|
|
473
|
+
async function writeDurableFile(options, content) {
|
|
474
|
+
const parsed = options.parse(content, options.filePath);
|
|
475
|
+
const durableContent = parsed.content;
|
|
476
|
+
const paths = durableFilePaths(options.filePath);
|
|
477
|
+
const delays = retryDelays(options);
|
|
478
|
+
if (options.gateway.exists(paths.backup)) {
|
|
479
|
+
await copyExistingFile(options.gateway, paths.backup, paths.previous, delays);
|
|
480
|
+
}
|
|
481
|
+
await replaceFile(options.gateway, paths.backup, durableContent, delays);
|
|
482
|
+
await replaceFile(options.gateway, paths.main, durableContent, delays);
|
|
483
|
+
}
|
|
484
|
+
async function loadDurableFile(options) {
|
|
485
|
+
const paths = durableFilePaths(options.filePath);
|
|
486
|
+
const roles = [
|
|
487
|
+
["main", paths.main],
|
|
488
|
+
["backup", paths.backup],
|
|
489
|
+
["previous", paths.previous]
|
|
490
|
+
];
|
|
491
|
+
const candidates = [];
|
|
492
|
+
for (const [role, path3] of roles) {
|
|
493
|
+
try {
|
|
494
|
+
const candidate = readCandidate(options, role, path3);
|
|
495
|
+
if (candidate) {
|
|
496
|
+
candidates.push(candidate);
|
|
497
|
+
}
|
|
498
|
+
} catch {
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
if (candidates.length === 0) {
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
const best = chooseBestCandidate(candidates);
|
|
505
|
+
const main = candidates.find((candidate) => candidate.role === "main");
|
|
506
|
+
const backup = candidates.find((candidate) => candidate.role === "backup");
|
|
507
|
+
const needsMainRestore = !main || main.rawContent !== best.content;
|
|
508
|
+
const needsBackupRefresh = !backup || backup.rawContent !== best.content;
|
|
509
|
+
let restoredMain = false;
|
|
510
|
+
let refreshedBackup = false;
|
|
511
|
+
let repairError;
|
|
512
|
+
const delays = retryDelays(options);
|
|
513
|
+
try {
|
|
514
|
+
if (needsBackupRefresh) {
|
|
515
|
+
if (backup && backup.rawContent !== best.content) {
|
|
516
|
+
await replaceFile(options.gateway, paths.previous, backup.content, delays);
|
|
517
|
+
}
|
|
518
|
+
await replaceFile(options.gateway, paths.backup, best.content, delays);
|
|
519
|
+
refreshedBackup = true;
|
|
520
|
+
}
|
|
521
|
+
if (needsMainRestore) {
|
|
522
|
+
await replaceFile(options.gateway, paths.main, best.content, delays);
|
|
523
|
+
restoredMain = true;
|
|
524
|
+
}
|
|
525
|
+
} catch (error) {
|
|
526
|
+
repairError = error;
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
data: best.data,
|
|
530
|
+
content: best.content,
|
|
531
|
+
source: best.role,
|
|
532
|
+
restoredMain,
|
|
533
|
+
refreshedBackup,
|
|
534
|
+
repairError
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
function durableFileExists(gateway, filePath) {
|
|
538
|
+
const paths = durableFilePaths(filePath);
|
|
539
|
+
return gateway.exists(paths.main) || gateway.exists(paths.backup) || gateway.exists(paths.previous);
|
|
540
|
+
}
|
|
541
|
+
function deleteDurableFile(gateway, filePath) {
|
|
542
|
+
const paths = durableFilePaths(filePath);
|
|
543
|
+
let deleted = false;
|
|
544
|
+
for (const path3 of [paths.main, paths.backup, paths.previous]) {
|
|
545
|
+
if (gateway.exists(path3)) {
|
|
546
|
+
gateway.deleteFile(path3);
|
|
547
|
+
deleted = true;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
return deleted;
|
|
551
|
+
}
|
|
552
|
+
|
|
377
553
|
// ../../packages/agent-framework/src/session/store.ts
|
|
378
554
|
var SESSION_VERSION = 2;
|
|
379
555
|
function isSessionEntry(value) {
|
|
@@ -434,28 +610,6 @@ function migrateV1ToV2(data) {
|
|
|
434
610
|
// Keep for backward compatibility
|
|
435
611
|
};
|
|
436
612
|
}
|
|
437
|
-
function writeFileAtomic(gateway, filePath, content) {
|
|
438
|
-
const tempPath = filePath + ".tmp";
|
|
439
|
-
gateway.writeFile(tempPath, content, "utf-8");
|
|
440
|
-
try {
|
|
441
|
-
const req = globalThis.require;
|
|
442
|
-
if (typeof req === "function") {
|
|
443
|
-
req("fs").renameSync(tempPath, filePath);
|
|
444
|
-
} else {
|
|
445
|
-
gateway.writeFile(filePath, content, "utf-8");
|
|
446
|
-
try {
|
|
447
|
-
gateway.deleteFile(tempPath);
|
|
448
|
-
} catch {
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
} catch {
|
|
452
|
-
gateway.writeFile(filePath, content, "utf-8");
|
|
453
|
-
try {
|
|
454
|
-
gateway.deleteFile(tempPath);
|
|
455
|
-
} catch {
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
613
|
var FileSessionStore = class {
|
|
460
614
|
sessionsDir;
|
|
461
615
|
gateway;
|
|
@@ -485,6 +639,33 @@ var FileSessionStore = class {
|
|
|
485
639
|
const safeId = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
486
640
|
return this.gateway.join(this.sessionsDir, `${safeId}.json`);
|
|
487
641
|
}
|
|
642
|
+
normalizeLoadedData(data) {
|
|
643
|
+
if (data.version === LEGACY_VERSION || !data.version) {
|
|
644
|
+
return { data: migrateV1ToV2(data), shouldPersist: true };
|
|
645
|
+
}
|
|
646
|
+
if (data.version !== SESSION_VERSION) {
|
|
647
|
+
console.warn(
|
|
648
|
+
`[SessionStore] Session version mismatch: ${data.version} vs ${SESSION_VERSION}`
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
if (!data.entries) {
|
|
652
|
+
data.entries = [];
|
|
653
|
+
}
|
|
654
|
+
return { data, shouldPersist: false };
|
|
655
|
+
}
|
|
656
|
+
parseSessionContent(content, filePath) {
|
|
657
|
+
if (!content || content.trim().length === 0) {
|
|
658
|
+
throw new Error("empty session file");
|
|
659
|
+
}
|
|
660
|
+
const parsed = JSON.parse(content);
|
|
661
|
+
const normalized = this.normalizeLoadedData(parsed);
|
|
662
|
+
const persistedContent = normalized.shouldPersist ? JSON.stringify(normalized.data, null, 2) : content;
|
|
663
|
+
return {
|
|
664
|
+
data: normalized.data,
|
|
665
|
+
content: persistedContent,
|
|
666
|
+
updatedAt: normalized.data.metadata?.updatedAt ?? 0
|
|
667
|
+
};
|
|
668
|
+
}
|
|
488
669
|
/**
|
|
489
670
|
* Save a session to disk (v2 format)
|
|
490
671
|
*/
|
|
@@ -514,7 +695,11 @@ var FileSessionStore = class {
|
|
|
514
695
|
};
|
|
515
696
|
const json = JSON.stringify(sessionData, null, 2);
|
|
516
697
|
const filePath = this.getSessionPath(sessionId);
|
|
517
|
-
|
|
698
|
+
await writeDurableFile({
|
|
699
|
+
gateway: this.gateway,
|
|
700
|
+
filePath,
|
|
701
|
+
parse: (content, path3) => this.parseSessionContent(content, path3)
|
|
702
|
+
}, json);
|
|
518
703
|
}
|
|
519
704
|
/**
|
|
520
705
|
* Legacy save method for backward compatibility
|
|
@@ -542,32 +727,23 @@ var FileSessionStore = class {
|
|
|
542
727
|
*/
|
|
543
728
|
async load(sessionId) {
|
|
544
729
|
const filePath = this.getSessionPath(sessionId);
|
|
545
|
-
if (!this.gateway.exists(filePath)) {
|
|
546
|
-
return null;
|
|
547
|
-
}
|
|
548
730
|
try {
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
this.
|
|
731
|
+
const loaded = await loadDurableFile({
|
|
732
|
+
gateway: this.gateway,
|
|
733
|
+
filePath,
|
|
734
|
+
parse: (content, path3) => this.parseSessionContent(content, path3)
|
|
735
|
+
});
|
|
736
|
+
if (!loaded) {
|
|
553
737
|
return null;
|
|
554
738
|
}
|
|
555
|
-
|
|
556
|
-
if (data.version === LEGACY_VERSION || !data.version) {
|
|
557
|
-
const migrated = migrateV1ToV2(data);
|
|
558
|
-
const migratedJson = JSON.stringify(migrated, null, 2);
|
|
559
|
-
writeFileAtomic(this.gateway, filePath, migratedJson);
|
|
560
|
-
return migrated;
|
|
561
|
-
}
|
|
562
|
-
if (data.version !== SESSION_VERSION) {
|
|
739
|
+
if (loaded.restoredMain || loaded.refreshedBackup) {
|
|
563
740
|
console.warn(
|
|
564
|
-
`[SessionStore]
|
|
741
|
+
`[SessionStore] Repaired session ${sessionId} from ${loaded.source}` + (loaded.repairError ? ` with repair warning: ${loaded.repairError}` : "")
|
|
565
742
|
);
|
|
743
|
+
} else if (loaded.repairError) {
|
|
744
|
+
console.warn(`[SessionStore] Loaded session ${sessionId} but sidecar refresh failed: ${loaded.repairError}`);
|
|
566
745
|
}
|
|
567
|
-
|
|
568
|
-
data.entries = [];
|
|
569
|
-
}
|
|
570
|
-
return data;
|
|
746
|
+
return loaded.data;
|
|
571
747
|
} catch (e) {
|
|
572
748
|
console.error(`[SessionStore] Failed to load session: ${sessionId}`, e);
|
|
573
749
|
return null;
|
|
@@ -578,12 +754,8 @@ var FileSessionStore = class {
|
|
|
578
754
|
*/
|
|
579
755
|
async delete(sessionId) {
|
|
580
756
|
const filePath = this.getSessionPath(sessionId);
|
|
581
|
-
if (!this.gateway.exists(filePath)) {
|
|
582
|
-
return false;
|
|
583
|
-
}
|
|
584
757
|
try {
|
|
585
|
-
this.gateway
|
|
586
|
-
return true;
|
|
758
|
+
return deleteDurableFile(this.gateway, filePath);
|
|
587
759
|
} catch (e) {
|
|
588
760
|
console.error(`[SessionStore] Failed to delete session: ${sessionId}`, e);
|
|
589
761
|
return false;
|
|
@@ -594,7 +766,7 @@ var FileSessionStore = class {
|
|
|
594
766
|
*/
|
|
595
767
|
async exists(sessionId) {
|
|
596
768
|
const filePath = this.getSessionPath(sessionId);
|
|
597
|
-
return this.gateway
|
|
769
|
+
return durableFileExists(this.gateway, filePath);
|
|
598
770
|
}
|
|
599
771
|
/**
|
|
600
772
|
* List all session IDs
|
|
@@ -604,7 +776,20 @@ var FileSessionStore = class {
|
|
|
604
776
|
return [];
|
|
605
777
|
}
|
|
606
778
|
const files = this.gateway.readdir(this.sessionsDir);
|
|
607
|
-
|
|
779
|
+
const sessionIds = /* @__PURE__ */ new Set();
|
|
780
|
+
for (const file of files) {
|
|
781
|
+
if (file.endsWith("-files.json") || file.endsWith("-files.json.bak") || file.endsWith("-files.json.prev")) {
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
if (file.endsWith(".json")) {
|
|
785
|
+
sessionIds.add(file.replace(/\.json$/i, ""));
|
|
786
|
+
} else if (file.endsWith(".json.bak")) {
|
|
787
|
+
sessionIds.add(file.replace(/\.json\.bak$/i, ""));
|
|
788
|
+
} else if (file.endsWith(".json.prev")) {
|
|
789
|
+
sessionIds.add(file.replace(/\.json\.prev$/i, ""));
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
return [...sessionIds];
|
|
608
793
|
}
|
|
609
794
|
/**
|
|
610
795
|
* List all session metadata
|
|
@@ -1230,6 +1415,8 @@ function summarizeToolResultForGuard(result) {
|
|
|
1230
1415
|
const text = content.filter((block) => !!block && typeof block === "object" && "type" in block).filter((block) => block.type === "text").map((block) => block.text || "").join("\n").trim();
|
|
1231
1416
|
return text || stableStringify(result);
|
|
1232
1417
|
}
|
|
1418
|
+
var RUNAWAY_TOOLCALL_WHITESPACE_DELTA_CHARS = 16384;
|
|
1419
|
+
var RUNAWAY_TOOLCALL_WHITESPACE_DELTA_EVENTS = 40;
|
|
1233
1420
|
function cloneQueuedInput(input) {
|
|
1234
1421
|
return {
|
|
1235
1422
|
...input,
|
|
@@ -1262,6 +1449,10 @@ var AgentSessionController = class {
|
|
|
1262
1449
|
pendingAutoCompact;
|
|
1263
1450
|
queueIdCounter = 0;
|
|
1264
1451
|
repeatedToolFailures = /* @__PURE__ */ new Map();
|
|
1452
|
+
streamGuard = {
|
|
1453
|
+
whitespaceToolCallDeltaChars: 0,
|
|
1454
|
+
whitespaceToolCallDeltaEvents: 0
|
|
1455
|
+
};
|
|
1265
1456
|
runtimeGuardTriggered = false;
|
|
1266
1457
|
constructor(options) {
|
|
1267
1458
|
this.agent = options.agent;
|
|
@@ -1285,6 +1476,9 @@ var AgentSessionController = class {
|
|
|
1285
1476
|
this.resetRuntimeGuardState();
|
|
1286
1477
|
this.emit({ type: "streaming_changed", isStreaming: true });
|
|
1287
1478
|
}
|
|
1479
|
+
if (event.type === "message_update") {
|
|
1480
|
+
this.recordMessageUpdateForGuard(event);
|
|
1481
|
+
}
|
|
1288
1482
|
if (event.type === "tool_execution_end") {
|
|
1289
1483
|
this.recordToolExecutionEndForGuard(event);
|
|
1290
1484
|
}
|
|
@@ -1879,8 +2073,52 @@ var AgentSessionController = class {
|
|
|
1879
2073
|
}
|
|
1880
2074
|
resetRuntimeGuardState() {
|
|
1881
2075
|
this.repeatedToolFailures.clear();
|
|
2076
|
+
this.streamGuard = {
|
|
2077
|
+
whitespaceToolCallDeltaChars: 0,
|
|
2078
|
+
whitespaceToolCallDeltaEvents: 0
|
|
2079
|
+
};
|
|
1882
2080
|
this.runtimeGuardTriggered = false;
|
|
1883
2081
|
}
|
|
2082
|
+
triggerRuntimeGuard(toolName, repeatCount, reason) {
|
|
2083
|
+
if (this.runtimeGuardTriggered) {
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
this.runtimeGuardTriggered = true;
|
|
2087
|
+
this.cancelRetry();
|
|
2088
|
+
this.cancelAutoCompactTimerOnly();
|
|
2089
|
+
this.cancelDispatchTimerOnly();
|
|
2090
|
+
this.cancelAutoContinueTimerOnly();
|
|
2091
|
+
this.emit({ type: "runtime_guard_triggered", toolName, repeatCount, reason });
|
|
2092
|
+
this.agent.abort();
|
|
2093
|
+
}
|
|
2094
|
+
recordMessageUpdateForGuard(event) {
|
|
2095
|
+
if (this.runtimeGuardTriggered) {
|
|
2096
|
+
return;
|
|
2097
|
+
}
|
|
2098
|
+
const assistantEvent = event.assistantMessageEvent;
|
|
2099
|
+
if (assistantEvent.type !== "toolcall_delta") {
|
|
2100
|
+
this.streamGuard.whitespaceToolCallDeltaChars = 0;
|
|
2101
|
+
this.streamGuard.whitespaceToolCallDeltaEvents = 0;
|
|
2102
|
+
return;
|
|
2103
|
+
}
|
|
2104
|
+
if (assistantEvent.delta.trim().length > 0) {
|
|
2105
|
+
this.streamGuard.whitespaceToolCallDeltaChars = 0;
|
|
2106
|
+
this.streamGuard.whitespaceToolCallDeltaEvents = 0;
|
|
2107
|
+
return;
|
|
2108
|
+
}
|
|
2109
|
+
this.streamGuard.whitespaceToolCallDeltaChars += assistantEvent.delta.length;
|
|
2110
|
+
this.streamGuard.whitespaceToolCallDeltaEvents += 1;
|
|
2111
|
+
const content = event.message.role === "assistant" && Array.isArray(event.message.content) ? event.message.content[assistantEvent.contentIndex] : void 0;
|
|
2112
|
+
const partialArgs = typeof content?.partialArgs === "string" ? content.partialArgs : "";
|
|
2113
|
+
const sparsePartialArgs = partialArgs.length >= RUNAWAY_TOOLCALL_WHITESPACE_DELTA_CHARS && partialArgs.replace(/\s/g, "").length / partialArgs.length < 0.05;
|
|
2114
|
+
if (sparsePartialArgs || this.streamGuard.whitespaceToolCallDeltaChars >= RUNAWAY_TOOLCALL_WHITESPACE_DELTA_CHARS && this.streamGuard.whitespaceToolCallDeltaEvents >= RUNAWAY_TOOLCALL_WHITESPACE_DELTA_EVENTS) {
|
|
2115
|
+
this.triggerRuntimeGuard(
|
|
2116
|
+
"assistant_stream",
|
|
2117
|
+
this.streamGuard.whitespaceToolCallDeltaEvents,
|
|
2118
|
+
"runaway_tool_call_arguments"
|
|
2119
|
+
);
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
1884
2122
|
recordToolExecutionEndForGuard(event) {
|
|
1885
2123
|
if (!event.isError) {
|
|
1886
2124
|
const prefix = `${event.toolName}:`;
|
|
@@ -1898,14 +2136,7 @@ var AgentSessionController = class {
|
|
|
1898
2136
|
if (repeatCount < 3 || this.runtimeGuardTriggered) {
|
|
1899
2137
|
return;
|
|
1900
2138
|
}
|
|
1901
|
-
this.
|
|
1902
|
-
this.cancelRetry();
|
|
1903
|
-
this.cancelAutoCompactTimerOnly();
|
|
1904
|
-
this.cancelDispatchTimerOnly();
|
|
1905
|
-
this.cancelAutoContinueTimerOnly();
|
|
1906
|
-
const reason = "repeated_tool_error";
|
|
1907
|
-
this.emit({ type: "runtime_guard_triggered", toolName: event.toolName, repeatCount, reason });
|
|
1908
|
-
this.agent.abort();
|
|
2139
|
+
this.triggerRuntimeGuard(event.toolName, repeatCount, "repeated_tool_error");
|
|
1909
2140
|
}
|
|
1910
2141
|
emit(event) {
|
|
1911
2142
|
for (const listener of this.listeners) {
|
|
@@ -3184,7 +3415,11 @@ var SessionTraceSnapshotWriter = class {
|
|
|
3184
3415
|
this.flushTimer = null;
|
|
3185
3416
|
}
|
|
3186
3417
|
if (!this.cachedController || !this.cachedSessionId) return;
|
|
3187
|
-
|
|
3418
|
+
try {
|
|
3419
|
+
this.options.saveTrace(this.cachedController.getSnapshot());
|
|
3420
|
+
} catch (error) {
|
|
3421
|
+
console.warn("[SessionTrace] Failed to save session trace:", error);
|
|
3422
|
+
}
|
|
3188
3423
|
}
|
|
3189
3424
|
notePendingUserText(text) {
|
|
3190
3425
|
this.mutate((controller) => {
|
|
@@ -3567,7 +3802,7 @@ function inferUncertainty(text, signals) {
|
|
|
3567
3802
|
function isParentHandoffRequired(task) {
|
|
3568
3803
|
return task.needsParentWrite || task.status === "timeout" || task.status === "failed" || task.confidenceSignals.some((signal) => signal.type === "tool_error" || signal.type === "no_tool_use");
|
|
3569
3804
|
}
|
|
3570
|
-
async function runSubagentWithProgress(deps, task, reportProgress) {
|
|
3805
|
+
async function runSubagentWithProgress(deps, task, reportProgress, signal) {
|
|
3571
3806
|
const startTime = Date.now();
|
|
3572
3807
|
task.status = "running";
|
|
3573
3808
|
let timedOut = false;
|
|
@@ -3588,6 +3823,9 @@ async function runSubagentWithProgress(deps, task, reportProgress) {
|
|
|
3588
3823
|
if (!apiKey) {
|
|
3589
3824
|
throw new Error("No API key available for subagent execution.");
|
|
3590
3825
|
}
|
|
3826
|
+
if (signal?.aborted) {
|
|
3827
|
+
throw new Error("Subagent aborted by parent");
|
|
3828
|
+
}
|
|
3591
3829
|
deps.log(`[Subagent ${task.name}] Plan: ${plan.complexity}/${plan.modelClass}, ${tools.length} tools`);
|
|
3592
3830
|
deps.debugLog?.("plan", "SubagentPlan", plan);
|
|
3593
3831
|
const systemPrompt = `You are a specialized subagent focused on one bounded task.
|
|
@@ -3611,6 +3849,7 @@ ${formatSkillsForPrompt(deps.skills ?? [])}`;
|
|
|
3611
3849
|
let rejectTimeout = () => void 0;
|
|
3612
3850
|
let timeout;
|
|
3613
3851
|
let unsubscribe;
|
|
3852
|
+
let removeAbortListener;
|
|
3614
3853
|
const timeoutPromise = new Promise((_, reject) => {
|
|
3615
3854
|
rejectTimeout = reject;
|
|
3616
3855
|
});
|
|
@@ -3620,6 +3859,19 @@ ${formatSkillsForPrompt(deps.skills ?? [])}`;
|
|
|
3620
3859
|
subagent.abort?.();
|
|
3621
3860
|
rejectTimeout(new Error(timeoutMessage));
|
|
3622
3861
|
}, plan.timeoutSeconds * 1e3);
|
|
3862
|
+
const abortPromise = new Promise((_, reject) => {
|
|
3863
|
+
if (!signal) return;
|
|
3864
|
+
const abortSubagent = () => {
|
|
3865
|
+
subagent.abort?.();
|
|
3866
|
+
reject(new Error("Subagent aborted by parent"));
|
|
3867
|
+
};
|
|
3868
|
+
if (signal.aborted) {
|
|
3869
|
+
abortSubagent();
|
|
3870
|
+
return;
|
|
3871
|
+
}
|
|
3872
|
+
signal.addEventListener("abort", abortSubagent, { once: true });
|
|
3873
|
+
removeAbortListener = () => signal.removeEventListener("abort", abortSubagent);
|
|
3874
|
+
});
|
|
3623
3875
|
unsubscribe = subagent.subscribe((event) => {
|
|
3624
3876
|
if (event.type === "tool_execution_start") {
|
|
3625
3877
|
toolCalls++;
|
|
@@ -3654,12 +3906,14 @@ ${formatSkillsForPrompt(deps.skills ?? [])}`;
|
|
|
3654
3906
|
await subagent.prompt(prompt);
|
|
3655
3907
|
await subagent.waitForIdle?.();
|
|
3656
3908
|
})(),
|
|
3657
|
-
timeoutPromise
|
|
3909
|
+
timeoutPromise,
|
|
3910
|
+
abortPromise
|
|
3658
3911
|
]);
|
|
3659
3912
|
} finally {
|
|
3660
3913
|
if (timeout) {
|
|
3661
3914
|
clearTimeout(timeout);
|
|
3662
3915
|
}
|
|
3916
|
+
removeAbortListener?.();
|
|
3663
3917
|
unsubscribe?.();
|
|
3664
3918
|
}
|
|
3665
3919
|
const messages = subagent.state.messages;
|
|
@@ -3795,9 +4049,19 @@ Use this for independent investigation, search, review, or triage work. Provide
|
|
|
3795
4049
|
{ description: "Array of tasks to run in parallel", minItems: 1, maxItems: 10 }
|
|
3796
4050
|
)
|
|
3797
4051
|
}, { additionalProperties: false }),
|
|
3798
|
-
async execute(first, second) {
|
|
4052
|
+
async execute(first, second, third, fourth) {
|
|
3799
4053
|
const args = typeof first === "string" ? second : first;
|
|
3800
|
-
const
|
|
4054
|
+
const signal = typeof first === "string" ? third : second?.signal;
|
|
4055
|
+
const reportProgress = typeof first === "string" ? fourth ? (update) => {
|
|
4056
|
+
fourth({
|
|
4057
|
+
content: update.message ? [{ type: "text", text: update.message }] : [],
|
|
4058
|
+
details: { increment: update.increment }
|
|
4059
|
+
});
|
|
4060
|
+
} : () => void 0 : second?.reportProgress ?? (() => void 0);
|
|
4061
|
+
const toolContext = {
|
|
4062
|
+
log: typeof first === "string" ? deps.log : second?.log ?? deps.log,
|
|
4063
|
+
reportProgress
|
|
4064
|
+
};
|
|
3801
4065
|
const normalized = normalizeTaskDefs(args);
|
|
3802
4066
|
if ("error" in normalized) {
|
|
3803
4067
|
return {
|
|
@@ -3825,7 +4089,7 @@ Use this for independent investigation, search, review, or triage work. Provide
|
|
|
3825
4089
|
toolContext.log?.(`Spawning ${tasks.length} parallel subagents: ${tasks.map((task) => task.name).join(", ")}`);
|
|
3826
4090
|
toolContext.reportProgress?.({ message: `Launching ${tasks.length} parallel subagents...`, increment: 5 });
|
|
3827
4091
|
const startTime = Date.now();
|
|
3828
|
-
await Promise.all(tasks.map((task) => runSubagentWithProgress(deps, task, toolContext.reportProgress
|
|
4092
|
+
await Promise.all(tasks.map((task) => runSubagentWithProgress(deps, task, toolContext.reportProgress, signal)));
|
|
3829
4093
|
const totalDuration = Date.now() - startTime;
|
|
3830
4094
|
const completed = tasks.filter((task) => task.status === "completed").length;
|
|
3831
4095
|
const failed = tasks.filter((task) => task.status === "failed" || task.status === "timeout").length;
|
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
createSharedWebSearchTool,
|
|
9
9
|
interpretShellExit,
|
|
10
10
|
requestInteraction
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-SW6G4XW2.js";
|
|
12
12
|
import {
|
|
13
13
|
Type
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-D7NAXU7F.js";
|
|
15
15
|
|
|
16
16
|
// src/config.ts
|
|
17
17
|
import { existsSync, mkdirSync, readFileSync, renameSync } from "fs";
|
package/dist/cli.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
getSettingsPath,
|
|
15
15
|
getThemesDir,
|
|
16
16
|
migrateConfigFromAgentDir
|
|
17
|
-
} from "./chunks/chunk-
|
|
17
|
+
} from "./chunks/chunk-YLOLJRLJ.js";
|
|
18
18
|
import {
|
|
19
19
|
AGENTS_CONTEXT_FILE_NAME,
|
|
20
20
|
AgentSessionController,
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
selectToolsForRuntimePolicy,
|
|
53
53
|
shouldPreserveExecutionStateForUserText,
|
|
54
54
|
supersedeExecutionState
|
|
55
|
-
} from "./chunks/chunk-
|
|
55
|
+
} from "./chunks/chunk-SW6G4XW2.js";
|
|
56
56
|
import {
|
|
57
57
|
Deref,
|
|
58
58
|
Errors,
|
|
@@ -86,7 +86,7 @@ import {
|
|
|
86
86
|
setLogger,
|
|
87
87
|
sortToolModelCandidatesByCapability,
|
|
88
88
|
type_exports
|
|
89
|
-
} from "./chunks/chunk-
|
|
89
|
+
} from "./chunks/chunk-D7NAXU7F.js";
|
|
90
90
|
import {
|
|
91
91
|
resolveCliProjectRoot
|
|
92
92
|
} from "./chunks/chunk-NTYHFBUA.js";
|
|
@@ -56031,6 +56031,27 @@ var ExecutionStateManager = class {
|
|
|
56031
56031
|
// src/session-trace.ts
|
|
56032
56032
|
import * as fs15 from "node:fs";
|
|
56033
56033
|
import * as path16 from "node:path";
|
|
56034
|
+
function saveTraceFileBestEffort(tracePath, content) {
|
|
56035
|
+
const tempPath = `${tracePath}.${process.pid}.${Date.now()}.tmp`;
|
|
56036
|
+
try {
|
|
56037
|
+
fs15.writeFileSync(tempPath, content, "utf8");
|
|
56038
|
+
try {
|
|
56039
|
+
fs15.renameSync(tempPath, tracePath);
|
|
56040
|
+
return;
|
|
56041
|
+
} catch {
|
|
56042
|
+
try {
|
|
56043
|
+
fs15.copyFileSync(tempPath, tracePath);
|
|
56044
|
+
} catch {
|
|
56045
|
+
} finally {
|
|
56046
|
+
try {
|
|
56047
|
+
fs15.unlinkSync(tempPath);
|
|
56048
|
+
} catch {
|
|
56049
|
+
}
|
|
56050
|
+
}
|
|
56051
|
+
}
|
|
56052
|
+
} catch {
|
|
56053
|
+
}
|
|
56054
|
+
}
|
|
56034
56055
|
var SessionTraceWriter = class extends SessionTraceSnapshotWriter {
|
|
56035
56056
|
constructor(sessionsDir, sessionManager) {
|
|
56036
56057
|
super({
|
|
@@ -56046,9 +56067,7 @@ var SessionTraceWriter = class extends SessionTraceSnapshotWriter {
|
|
|
56046
56067
|
},
|
|
56047
56068
|
saveTrace: (trace) => {
|
|
56048
56069
|
const tracePath = path16.join(sessionsDir, `${trace.sessionId}.trace.json`);
|
|
56049
|
-
|
|
56050
|
-
fs15.writeFileSync(tempPath, JSON.stringify(trace, null, 2), "utf8");
|
|
56051
|
-
fs15.renameSync(tempPath, tracePath);
|
|
56070
|
+
saveTraceFileBestEffort(tracePath, JSON.stringify(trace, null, 2));
|
|
56052
56071
|
}
|
|
56053
56072
|
});
|
|
56054
56073
|
this.sessionsDir = sessionsDir;
|
|
@@ -65328,7 +65347,7 @@ var InteractiveMode = class {
|
|
|
65328
65347
|
if (savedLevel) {
|
|
65329
65348
|
this.thinkingLevel = savedLevel;
|
|
65330
65349
|
}
|
|
65331
|
-
const { Agent } = await import("./chunks/src-
|
|
65350
|
+
const { Agent } = await import("./chunks/src-LZC56DRG.js");
|
|
65332
65351
|
const activeSession = this.sessionManager.getActiveSession();
|
|
65333
65352
|
this.agent = new Agent({
|
|
65334
65353
|
initialState: {
|
|
@@ -67321,7 +67340,7 @@ async function startChat(initialPrompt, testCommand) {
|
|
|
67321
67340
|
`);
|
|
67322
67341
|
console.log("Assistant: ");
|
|
67323
67342
|
}
|
|
67324
|
-
const { Agent } = await import("./chunks/src-
|
|
67343
|
+
const { Agent } = await import("./chunks/src-LZC56DRG.js");
|
|
67325
67344
|
let agent;
|
|
67326
67345
|
const printModeExtensions = await loadPrintModeExtensions({
|
|
67327
67346
|
cwd,
|