@epoch-agent/core 0.15.0 → 0.17.0
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/index.d.ts +7536 -7487
- package/dist/index.js +399 -252
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { join, dirname, resolve, relative, isAbsolute, sep, extname, basename }
|
|
|
4
4
|
import { execFile, execSync, execFileSync, spawn } from 'child_process';
|
|
5
5
|
import { createLogger, dbPath, resolveHomeDir, DEFAULT_MAX_TURNS, PROJECT_DIR_NAME, guardSpawn, t, suggestKey, misspelledKeyIssues, openDatabase, migrate, releaseDatabase, parseLenient, issueDetails, splitWords, unknownKeyIssues, checkpointsDir, pluginsDir, projectHooksPath, projectClaudeSettingsPath, projectClaudeLocalSettingsPath, projectSettingsPath, projectLocalSettingsPath, projectPoliciesDir, trustedImportsPath, canProbeGit, budgetStatePath, artifactsDir, UNLIMITED_TURNS, turnLimitOf, configPath, managedSettingsPath, getSecretValues, listStoredSecretNames, maskApiKey, setSectionField, countCodePoints, headCodePoints, attachmentsDir, isInWorkspace, workspacesPath, samePath, lastSessionsPath, automationLogsDir, projectComplianceDir, projectSkillsDir, trustPath, projectCommandsDir, detectBackend, EXEC_PATH_AS_NODE_ENV, isolate, createStreamDecoder, parseShellCommand, writeArtifact, envPath, checkDangerousCommand, checkObfuscation, keepHeadAndTail, normalizeForMatch, isReadOnlyCommand, CODE_EXEC_ROOTS, tailCodePoints } from '@epoch-agent/infra';
|
|
6
6
|
export { IS_WINDOWS, WINDOWS_HIDE_FLAGS, buildBwrapArgs, buildProfile, checkDangerousCommand, checkObfuscation, createLogger, detectBackend, getDefaultShell, getPythonCommand, isInWorkspace, isReadOnlyCommand, isolate, maskApiKey, probeBubblewrap, probeSeatbelt, complianceDir as resolveComplianceDir, policiesDir as resolvePoliciesDir, setLogLevel } from '@epoch-agent/infra';
|
|
7
|
-
import { PROVIDER_TYPES, OPERATION_TYPES as OPERATION_TYPES$1, COMPLIANCE_CATEGORIES, COMPLIANCE_ACTIONS, SEARCH_PROVIDER_TYPES, CURRENCIES, LANGS, PERMISSION_LEVELS, SHELL_KINDS, ToolExposure, MAX_OPTIONS, MIN_OPTIONS, MAX_HEADER_CHARS, MAX_QUESTIONS, SCHEDULE_INTERVAL_MINUTES, isPermissionLevel, SCHEDULE_DRAFT_TOOL, isValidAgentRoleName, DEFAULT_AGENT_ROLE, isValidCommandName, RESERVED_COMMAND_NAMES, parseModelRef, isWirePluginArchiveName, systemNote, formatMoney, totalUsageTokens, NOOP_TELEMETRY, METRIC, contentToText, GEN_AI, isPromptOversize, promptTokens, apiKeyEnvVar, PROVIDER_INFOS, isProviderType, API_KEY_ENV_VARS, KEY_ACTIONS, tableOf, perAction, isKeyAction, parseSequence, RESERVED_CHORDS, chordId, sequenceId, ACTION_CONTEXTS, SEQUENCE_CONTEXTS, assertNeverPart, MAX_ATTACH_FILES, MAX_ATTACH_TOTAL_BYTES, MAX_SESSION_REFS, utf8ByteLength, truncateToBudget, attachSessionSurface, isContextNearlyFull, isOperationType, normalizeApproval, imagePartOmittedSummary, MAX_ATTACH_BYTES, SIMULTANEOUS_CONTEXTS, SESSION_REFERENCE_ERROR_CODES, filePartSummary, sessionPartSummary } from '@epoch-agent/protocol';
|
|
7
|
+
import { PROVIDER_TYPES, OPERATION_TYPES as OPERATION_TYPES$1, COMPLIANCE_CATEGORIES, COMPLIANCE_ACTIONS, SEARCH_PROVIDER_TYPES, CURRENCIES, LANGS, isSettingsPaneId, SETTINGS_PANE_IDS, PERMISSION_LEVELS, SHELL_KINDS, ToolExposure, MAX_OPTIONS, MIN_OPTIONS, MAX_HEADER_CHARS, MAX_QUESTIONS, SCHEDULE_INTERVAL_MINUTES, isPermissionLevel, SCHEDULE_DRAFT_TOOL, isValidAgentRoleName, DEFAULT_AGENT_ROLE, isValidCommandName, RESERVED_COMMAND_NAMES, parseModelRef, isWirePluginArchiveName, systemNote, formatMoney, totalUsageTokens, NOOP_TELEMETRY, METRIC, contentToText, GEN_AI, isPromptOversize, promptTokens, apiKeyEnvVar, PROVIDER_INFOS, isProviderType, API_KEY_ENV_VARS, KEY_ACTIONS, tableOf, perAction, isKeyAction, parseSequence, RESERVED_CHORDS, chordId, sequenceId, ACTION_CONTEXTS, SEQUENCE_CONTEXTS, assertNeverPart, MAX_ATTACH_FILES, MAX_ATTACH_TOTAL_BYTES, MAX_SESSION_REFS, utf8ByteLength, truncateToBudget, attachSessionSurface, isContextNearlyFull, isOperationType, normalizeApproval, imagePartOmittedSummary, MAX_ATTACH_BYTES, SIMULTANEOUS_CONTEXTS, SESSION_REFERENCE_ERROR_CODES, filePartSummary, sessionPartSummary } from '@epoch-agent/protocol';
|
|
8
8
|
export { API_KEY_ENV_VARS, COMPLIANCE_ACTIONS, COMPLIANCE_CATEGORIES, MAX_ATTACH_BYTES, MAX_ATTACH_FILES, MAX_ATTACH_TOTAL_BYTES, MAX_SESSION_ATTACH_BYTES, MAX_SESSION_REFS, OPERATION_TYPES, PERMISSION_LEVELS, PLAN_OUTCOMES, PROVIDER_INFOS, PROVIDER_TYPES, ToolExposure, apiKeyEnvVar, extractAllMentions, extractMentions, extractSessionMentions, getProviderInfo, isOperationType, isPermissionLevel, isPlanOutcome, isProviderType, normalizeApproval } from '@epoch-agent/protocol';
|
|
9
9
|
import { createRequire } from 'module';
|
|
10
10
|
import { randomUUID, createHash, randomBytes } from 'crypto';
|
|
@@ -34,7 +34,8 @@ __export(schtasks_exports, {
|
|
|
34
34
|
quoteTaskRun: () => quoteTaskRun,
|
|
35
35
|
scheduleArgs: () => scheduleArgs,
|
|
36
36
|
specFromTrigger: () => specFromTrigger,
|
|
37
|
-
specFromXml: () => specFromXml
|
|
37
|
+
specFromXml: () => specFromXml,
|
|
38
|
+
taskRunArgv: () => taskRunArgv
|
|
38
39
|
});
|
|
39
40
|
function createSchtasksBackend(opts = {}) {
|
|
40
41
|
const run = opts.run ?? runCommand2;
|
|
@@ -99,9 +100,11 @@ function createSchtasksBackend(opts = {}) {
|
|
|
99
100
|
async inspect(osTaskId) {
|
|
100
101
|
const res = await run("schtasks", ["/Query", "/TN", osTaskId, "/XML"]);
|
|
101
102
|
if (res.code !== 0) return null;
|
|
103
|
+
const commandLine = commandLineFromXml(res.stdout);
|
|
102
104
|
return {
|
|
103
105
|
osTaskId,
|
|
104
|
-
commandLine
|
|
106
|
+
commandLine,
|
|
107
|
+
argv: taskRunArgv(commandLine),
|
|
105
108
|
spec: specFromXml(res.stdout),
|
|
106
109
|
enabled: !/<Enabled>\s*false\s*<\/Enabled>/i.test(res.stdout)
|
|
107
110
|
};
|
|
@@ -118,6 +121,13 @@ function createSchtasksBackend(opts = {}) {
|
|
|
118
121
|
function quoteTaskRun(argv) {
|
|
119
122
|
return argv.map((part) => `"${part.replace(/"/g, "")}"`).join(" ");
|
|
120
123
|
}
|
|
124
|
+
function taskRunArgv(commandLine) {
|
|
125
|
+
const out = [];
|
|
126
|
+
const re = /"([^"]*)"|(\S+)/g;
|
|
127
|
+
let m;
|
|
128
|
+
while ((m = re.exec(commandLine)) !== null) out.push(m[1] ?? m[2] ?? "");
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
121
131
|
function scheduleArgs(trigger) {
|
|
122
132
|
if (trigger.kind === "interval") {
|
|
123
133
|
const slots = intervalSlots(trigger.everyMinutes);
|
|
@@ -331,9 +341,11 @@ function createLaunchdBackend(opts = {}) {
|
|
|
331
341
|
return null;
|
|
332
342
|
}
|
|
333
343
|
const disabled = await isDisabled(run, label);
|
|
344
|
+
const argv = extractProgramArguments(xml2);
|
|
334
345
|
return {
|
|
335
346
|
osTaskId: label,
|
|
336
|
-
commandLine:
|
|
347
|
+
commandLine: argv.join(" "),
|
|
348
|
+
argv,
|
|
337
349
|
spec: extractCalendarSpec(xml2),
|
|
338
350
|
enabled: !disabled
|
|
339
351
|
};
|
|
@@ -1001,6 +1013,11 @@ function createAskQuestionTool(deps) {
|
|
|
1001
1013
|
}
|
|
1002
1014
|
};
|
|
1003
1015
|
}
|
|
1016
|
+
// src/agent/types.ts
|
|
1017
|
+
function isAbortSignal(signal) {
|
|
1018
|
+
return !!signal && typeof signal.addEventListener === "function" && typeof signal.removeEventListener === "function";
|
|
1019
|
+
}
|
|
1020
|
+
// src/tools/builtin/code-exec.ts
|
|
1004
1021
|
var MAX_CODE_LENGTH = 1e5;
|
|
1005
1022
|
function createCodeExecTool(sandbox) {
|
|
1006
1023
|
const iso = sandbox.getIsolation();
|
|
@@ -1046,7 +1063,7 @@ function createCodeExecTool(sandbox) {
|
|
|
1046
1063
|
},
|
|
1047
1064
|
operation: "code_exec",
|
|
1048
1065
|
describeTarget: (args) => `${String(args["language"] ?? "")}:${String(args["code"] ?? "")}`,
|
|
1049
|
-
async execute(args,
|
|
1066
|
+
async execute(args, ctx) {
|
|
1050
1067
|
const start = Date.now();
|
|
1051
1068
|
const language = String(args["language"] ?? "");
|
|
1052
1069
|
const code = String(args["code"] ?? "");
|
|
@@ -1084,7 +1101,8 @@ function createCodeExecTool(sandbox) {
|
|
|
1084
1101
|
language,
|
|
1085
1102
|
code,
|
|
1086
1103
|
timeout,
|
|
1087
|
-
allowNetwork: args["allow_network"] === true
|
|
1104
|
+
allowNetwork: args["allow_network"] === true,
|
|
1105
|
+
...isAbortSignal(ctx.signal) ? { signal: ctx.signal } : {}
|
|
1088
1106
|
});
|
|
1089
1107
|
const header = `[\u9694\u79BB: ${result.isolation === "os" ? "OS \u7EA7" : "\u4EC5\u8FDB\u7A0B\u7EA7"}]`;
|
|
1090
1108
|
const body2 = result.output || "(\u65E0\u8F93\u51FA)";
|
|
@@ -1096,7 +1114,9 @@ ${body2}`,
|
|
|
1096
1114
|
error: {
|
|
1097
1115
|
code: 6004,
|
|
1098
1116
|
message: result.error ?? "\u6267\u884C\u5931\u8D25",
|
|
1099
|
-
...result.errorType === "oom" ? { suggestion: "\u51CF\u5C11\u5185\u5B58\u5360\u7528\uFF0C\u6216\u5206\u6279\u5904\u7406\u6570\u636E" } :
|
|
1117
|
+
...result.errorType === "oom" ? { suggestion: "\u51CF\u5C11\u5185\u5B58\u5360\u7528\uFF0C\u6216\u5206\u6279\u5904\u7406\u6570\u636E" } : (
|
|
1118
|
+
result.errorType === "timeout" && !ctx.signal?.aborted ? { suggestion: "\u4F18\u5316\u7B97\u6CD5\uFF0C\u6216\u63D0\u9AD8 timeout\uFF08\u4E0A\u9650 120 \u79D2\uFF09" } : {}
|
|
1119
|
+
)
|
|
1100
1120
|
},
|
|
1101
1121
|
duration: result.duration
|
|
1102
1122
|
};
|
|
@@ -1106,6 +1126,218 @@ ${body2}`, duration: result.duration };
|
|
|
1106
1126
|
}
|
|
1107
1127
|
};
|
|
1108
1128
|
}
|
|
1129
|
+
var WINDOW_CHARS = 1024;
|
|
1130
|
+
var NGRAM = 16;
|
|
1131
|
+
var MAX_DISTINCT_RATIO = 0.2;
|
|
1132
|
+
var CHECK_EVERY_CHARS = 256;
|
|
1133
|
+
var SUSTAIN_CHECKS = 2;
|
|
1134
|
+
var MIN_NORMALIZED_CHARS = 256;
|
|
1135
|
+
function normalizeRuns(text) {
|
|
1136
|
+
return text.replace(/\s+/g, " ").replace(/(.)\1{3,}/gu, (_m, c) => c.repeat(3));
|
|
1137
|
+
}
|
|
1138
|
+
function distinctNgramRatio(text, n = NGRAM) {
|
|
1139
|
+
if (text.length < n) return 1;
|
|
1140
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1141
|
+
let total = 0;
|
|
1142
|
+
for (let i = 0; i + n <= text.length; i++) {
|
|
1143
|
+
seen.add(text.slice(i, i + n));
|
|
1144
|
+
total++;
|
|
1145
|
+
}
|
|
1146
|
+
return total === 0 ? 1 : seen.size / total;
|
|
1147
|
+
}
|
|
1148
|
+
var RepetitionDetector = class {
|
|
1149
|
+
buf = "";
|
|
1150
|
+
sinceCheck = 0;
|
|
1151
|
+
streak = 0;
|
|
1152
|
+
chars = 0;
|
|
1153
|
+
hit = null;
|
|
1154
|
+
push(delta) {
|
|
1155
|
+
if (this.hit) return true;
|
|
1156
|
+
if (!delta) return false;
|
|
1157
|
+
this.chars += delta.length;
|
|
1158
|
+
this.buf = (this.buf + delta).slice(-WINDOW_CHARS);
|
|
1159
|
+
this.sinceCheck += delta.length;
|
|
1160
|
+
if (this.sinceCheck < CHECK_EVERY_CHARS) return false;
|
|
1161
|
+
this.sinceCheck = 0;
|
|
1162
|
+
if (this.buf.length < WINDOW_CHARS) return false;
|
|
1163
|
+
const norm = normalizeRuns(this.buf);
|
|
1164
|
+
if (norm.length < MIN_NORMALIZED_CHARS) {
|
|
1165
|
+
this.streak = 0;
|
|
1166
|
+
return false;
|
|
1167
|
+
}
|
|
1168
|
+
const ratio = distinctNgramRatio(norm);
|
|
1169
|
+
if (ratio > MAX_DISTINCT_RATIO) {
|
|
1170
|
+
this.streak = 0;
|
|
1171
|
+
return false;
|
|
1172
|
+
}
|
|
1173
|
+
if (++this.streak < SUSTAIN_CHECKS) return false;
|
|
1174
|
+
this.hit = { chars: this.chars, distinctRatio: ratio };
|
|
1175
|
+
return true;
|
|
1176
|
+
}
|
|
1177
|
+
pushAll(text) {
|
|
1178
|
+
for (let i = 0; i < text.length; i += CHECK_EVERY_CHARS) {
|
|
1179
|
+
if (this.push(text.slice(i, i + CHECK_EVERY_CHARS))) return true;
|
|
1180
|
+
}
|
|
1181
|
+
return false;
|
|
1182
|
+
}
|
|
1183
|
+
isLooping() {
|
|
1184
|
+
return this.hit !== null;
|
|
1185
|
+
}
|
|
1186
|
+
getInfo() {
|
|
1187
|
+
if (!this.hit) return null;
|
|
1188
|
+
return t("agent_run.looping_info", {
|
|
1189
|
+
chars: this.hit.chars,
|
|
1190
|
+
percent: Math.round(this.hit.distinctRatio * 100)
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
};
|
|
1194
|
+
// src/agent/turn.ts
|
|
1195
|
+
async function* runTurn(opts) {
|
|
1196
|
+
if (opts.streaming) {
|
|
1197
|
+
return yield* runTurnStreaming(opts);
|
|
1198
|
+
}
|
|
1199
|
+
return yield* runTurnBuffered(opts);
|
|
1200
|
+
}
|
|
1201
|
+
async function* runTurnStreaming(opts) {
|
|
1202
|
+
const { provider, input, guard, reasoningGuard } = opts;
|
|
1203
|
+
const repetition = new RepetitionDetector();
|
|
1204
|
+
const reasoningRepetition = new RepetitionDetector();
|
|
1205
|
+
let text = "";
|
|
1206
|
+
let reasoningText = "";
|
|
1207
|
+
let finishReason = "stop";
|
|
1208
|
+
let looping;
|
|
1209
|
+
let usage;
|
|
1210
|
+
const toolCalls = [];
|
|
1211
|
+
let streamError;
|
|
1212
|
+
let model;
|
|
1213
|
+
let requestedModel;
|
|
1214
|
+
let warnings;
|
|
1215
|
+
const ac = new AbortController();
|
|
1216
|
+
const unlink = linkAbort(input.signal, ac);
|
|
1217
|
+
try {
|
|
1218
|
+
for await (const chunk of provider.stream({ ...input, signal: ac.signal })) {
|
|
1219
|
+
switch (chunk.type) {
|
|
1220
|
+
case "text-delta": {
|
|
1221
|
+
const raw = chunk.textDelta ?? "";
|
|
1222
|
+
if (!raw) break;
|
|
1223
|
+
const delta = guard ? guard.push(raw) : raw;
|
|
1224
|
+
if (!delta) break;
|
|
1225
|
+
text += delta;
|
|
1226
|
+
yield { type: "text-delta", text: delta };
|
|
1227
|
+
if (repetition.push(delta)) looping = repetition;
|
|
1228
|
+
break;
|
|
1229
|
+
}
|
|
1230
|
+
case "reasoning-delta": {
|
|
1231
|
+
const raw = chunk.textDelta ?? "";
|
|
1232
|
+
if (!raw) break;
|
|
1233
|
+
const delta = reasoningGuard ? reasoningGuard.push(raw) : raw;
|
|
1234
|
+
if (!delta) break;
|
|
1235
|
+
reasoningText += delta;
|
|
1236
|
+
yield { type: "reasoning-delta", text: delta };
|
|
1237
|
+
if (reasoningRepetition.push(delta)) looping = reasoningRepetition;
|
|
1238
|
+
break;
|
|
1239
|
+
}
|
|
1240
|
+
case "tool-call":
|
|
1241
|
+
if (chunk.toolCall) {
|
|
1242
|
+
toolCalls.push({
|
|
1243
|
+
toolCallId: chunk.toolCall.toolCallId,
|
|
1244
|
+
toolName: chunk.toolCall.toolName,
|
|
1245
|
+
input: chunk.toolCall.input
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
break;
|
|
1249
|
+
case "finish":
|
|
1250
|
+
if (chunk.usage) usage = chunk.usage;
|
|
1251
|
+
if (chunk.finishReason) finishReason = chunk.finishReason;
|
|
1252
|
+
if (chunk.model) model = chunk.model;
|
|
1253
|
+
if (chunk.requestedModel) requestedModel = chunk.requestedModel;
|
|
1254
|
+
if (chunk.warnings) warnings = chunk.warnings;
|
|
1255
|
+
break;
|
|
1256
|
+
case "error":
|
|
1257
|
+
streamError = chunk.error ?? t("agent_run.unknown_provider_error");
|
|
1258
|
+
break;
|
|
1259
|
+
case "tool-result":
|
|
1260
|
+
break;
|
|
1261
|
+
}
|
|
1262
|
+
if (looping) {
|
|
1263
|
+
ac.abort();
|
|
1264
|
+
break;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
} finally {
|
|
1268
|
+
unlink();
|
|
1269
|
+
}
|
|
1270
|
+
if (streamError && !looping) throw new Error(streamError);
|
|
1271
|
+
if (guard) {
|
|
1272
|
+
const tail = guard.flush();
|
|
1273
|
+
if (tail) {
|
|
1274
|
+
text += tail;
|
|
1275
|
+
yield { type: "text-delta", text: tail };
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
if (reasoningGuard) {
|
|
1279
|
+
const tail = reasoningGuard.flush();
|
|
1280
|
+
if (tail) {
|
|
1281
|
+
reasoningText += tail;
|
|
1282
|
+
yield { type: "reasoning-delta", text: tail };
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
return {
|
|
1286
|
+
text,
|
|
1287
|
+
...usage ? { usage } : {},
|
|
1288
|
+
finishReason: looping ? "looping" : finishReason,
|
|
1289
|
+
...looping ? { loopingInfo: looping.getInfo() ?? "" } : {},
|
|
1290
|
+
...toolCalls.length > 0 ? { toolCalls } : {},
|
|
1291
|
+
...reasoningText ? { reasoningText } : {},
|
|
1292
|
+
...model ? { model } : {},
|
|
1293
|
+
...requestedModel ? { requestedModel } : {},
|
|
1294
|
+
...warnings && warnings.length > 0 ? { warnings } : {}
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
async function* runTurnBuffered(opts) {
|
|
1298
|
+
const { provider, input, guard, reasoningGuard } = opts;
|
|
1299
|
+
const result = await provider.generate(input);
|
|
1300
|
+
const reasoningText = result.reasoningText ? filterWhole(reasoningGuard, result.reasoningText) : result.reasoningText;
|
|
1301
|
+
if (reasoningText) {
|
|
1302
|
+
yield { type: "reasoning-delta", text: reasoningText };
|
|
1303
|
+
}
|
|
1304
|
+
const text = filterWhole(guard, result.text);
|
|
1305
|
+
if (text) {
|
|
1306
|
+
yield { type: "text-delta", text };
|
|
1307
|
+
}
|
|
1308
|
+
const { reasoningText: _replaced, ...rest } = result;
|
|
1309
|
+
const textLoop = new RepetitionDetector();
|
|
1310
|
+
const reasoningLoop = new RepetitionDetector();
|
|
1311
|
+
textLoop.pushAll(text);
|
|
1312
|
+
if (reasoningText) reasoningLoop.pushAll(reasoningText);
|
|
1313
|
+
let looping;
|
|
1314
|
+
if (textLoop.isLooping()) looping = textLoop;
|
|
1315
|
+
else if (reasoningLoop.isLooping()) looping = reasoningLoop;
|
|
1316
|
+
return {
|
|
1317
|
+
...rest,
|
|
1318
|
+
text,
|
|
1319
|
+
...reasoningText ? { reasoningText } : {},
|
|
1320
|
+
...looping ? { finishReason: "looping", loopingInfo: looping.getInfo() ?? "" } : {}
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
function linkAbort(outer, ac) {
|
|
1324
|
+
if (!outer) return () => {
|
|
1325
|
+
};
|
|
1326
|
+
if (outer.aborted) {
|
|
1327
|
+
ac.abort(outer.reason);
|
|
1328
|
+
return () => {
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
const onAbort = () => ac.abort(outer.reason);
|
|
1332
|
+
outer.addEventListener("abort", onAbort, { once: true });
|
|
1333
|
+
return () => outer.removeEventListener("abort", onAbort);
|
|
1334
|
+
}
|
|
1335
|
+
function filterWhole(guard, text) {
|
|
1336
|
+
if (!guard || !text) return text;
|
|
1337
|
+
return guard.push(text) + guard.flush();
|
|
1338
|
+
}
|
|
1339
|
+
// src/delegate/manager.ts
|
|
1340
|
+
var ABORTED = "\u5DF2\u4E2D\u6B62\uFF1A\u7528\u6237\u7EC8\u6B62\u4E86\u8FD9\u4E00\u8F6E";
|
|
1109
1341
|
var BLOCKED_TOOLS = Object.freeze([
|
|
1110
1342
|
"delegate_task",
|
|
1111
1343
|
"clarify",
|
|
@@ -1135,7 +1367,7 @@ var DelegateManager = class {
|
|
|
1135
1367
|
...config
|
|
1136
1368
|
};
|
|
1137
1369
|
}
|
|
1138
|
-
async delegate(args, parentSessionId) {
|
|
1370
|
+
async delegate(args, parentSessionId, signal) {
|
|
1139
1371
|
const start = Date.now();
|
|
1140
1372
|
const tasks = this.normalizeTasks(args);
|
|
1141
1373
|
const maxConcurrent = args.max_concurrent ?? this.config.max_concurrent_children;
|
|
@@ -1146,7 +1378,7 @@ var DelegateManager = class {
|
|
|
1146
1378
|
duration: 0
|
|
1147
1379
|
};
|
|
1148
1380
|
}
|
|
1149
|
-
const results = await this.runWithConcurrency(tasks, maxConcurrent, parentSessionId);
|
|
1381
|
+
const results = await this.runWithConcurrency(tasks, maxConcurrent, parentSessionId, signal);
|
|
1150
1382
|
const summary = this.buildSummary(results, Date.now() - start);
|
|
1151
1383
|
return {
|
|
1152
1384
|
success: results.every((r) => r.success),
|
|
@@ -1161,12 +1393,12 @@ var DelegateManager = class {
|
|
|
1161
1393
|
if (args.goal) return [{ goal: args.goal, ...args.role ? { role: args.role } : {} }];
|
|
1162
1394
|
return [];
|
|
1163
1395
|
}
|
|
1164
|
-
async runWithConcurrency(tasks, maxConcurrent, parentSessionId) {
|
|
1396
|
+
async runWithConcurrency(tasks, maxConcurrent, parentSessionId, signal) {
|
|
1165
1397
|
const results = [];
|
|
1166
1398
|
for (let i = 0; i < tasks.length; i += maxConcurrent) {
|
|
1167
1399
|
const batch = tasks.slice(i, i + maxConcurrent);
|
|
1168
1400
|
const batchResults = await Promise.allSettled(
|
|
1169
|
-
batch.map((t81) => this.runSingleTask(t81, parentSessionId))
|
|
1401
|
+
batch.map((t81) => this.runSingleTask(t81, parentSessionId, signal))
|
|
1170
1402
|
);
|
|
1171
1403
|
for (const r of batchResults) {
|
|
1172
1404
|
if (r.status === "fulfilled") {
|
|
@@ -1184,14 +1416,40 @@ var DelegateManager = class {
|
|
|
1184
1416
|
}
|
|
1185
1417
|
return results;
|
|
1186
1418
|
}
|
|
1187
|
-
async runSingleTask(task, parentSessionId) {
|
|
1419
|
+
async runSingleTask(task, parentSessionId, signal) {
|
|
1188
1420
|
const start = Date.now();
|
|
1189
1421
|
const sessionId = `delegate-${randomUUID().slice(0, 8)}`;
|
|
1422
|
+
const failed = (error, summary = "") => ({
|
|
1423
|
+
goal: task.goal,
|
|
1424
|
+
success: false,
|
|
1425
|
+
summary,
|
|
1426
|
+
error,
|
|
1427
|
+
duration: Date.now() - start,
|
|
1428
|
+
...task.role ? { role: task.role } : {}
|
|
1429
|
+
});
|
|
1430
|
+
if (signal?.aborted) return failed(ABORTED);
|
|
1431
|
+
const controller = new AbortController();
|
|
1432
|
+
const unlink = linkAbort(isAbortSignal(signal) ? signal : void 0, controller);
|
|
1433
|
+
const ms = this.config.task_timeout;
|
|
1434
|
+
let timer;
|
|
1190
1435
|
try {
|
|
1191
|
-
const result = await
|
|
1192
|
-
this.runner(
|
|
1193
|
-
|
|
1194
|
-
|
|
1436
|
+
const result = await Promise.race([
|
|
1437
|
+
this.runner(
|
|
1438
|
+
task.goal,
|
|
1439
|
+
task.context ?? "",
|
|
1440
|
+
sessionId,
|
|
1441
|
+
task.role,
|
|
1442
|
+
parentSessionId,
|
|
1443
|
+
controller.signal
|
|
1444
|
+
),
|
|
1445
|
+
new Promise((_, reject2) => {
|
|
1446
|
+
timer = setTimeout(() => {
|
|
1447
|
+
reject2(new Error(`\u8D85\u65F6 (${ms / 1e3}s)`));
|
|
1448
|
+
controller.abort();
|
|
1449
|
+
}, ms);
|
|
1450
|
+
})
|
|
1451
|
+
]);
|
|
1452
|
+
if (controller.signal.aborted) return failed(ABORTED, result.text);
|
|
1195
1453
|
return {
|
|
1196
1454
|
goal: task.goal,
|
|
1197
1455
|
success: !result.error,
|
|
@@ -1202,22 +1460,13 @@ var DelegateManager = class {
|
|
|
1202
1460
|
};
|
|
1203
1461
|
} catch (err) {
|
|
1204
1462
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1205
|
-
return
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
duration: Date.now() - start,
|
|
1211
|
-
...task.role ? { role: task.role } : {}
|
|
1212
|
-
};
|
|
1463
|
+
if (signal?.aborted) return failed(ABORTED);
|
|
1464
|
+
return failed(msg.includes("\u8D85\u65F6") ? `[\u8D85\u65F6] ${msg}` : msg);
|
|
1465
|
+
} finally {
|
|
1466
|
+
clearTimeout(timer);
|
|
1467
|
+
unlink();
|
|
1213
1468
|
}
|
|
1214
1469
|
}
|
|
1215
|
-
async runWithTimeout(promise, ms) {
|
|
1216
|
-
const timeout = new Promise(
|
|
1217
|
-
(_, reject2) => setTimeout(() => reject2(new Error(`\u8D85\u65F6 (${ms / 1e3}s)`)), ms)
|
|
1218
|
-
);
|
|
1219
|
-
return Promise.race([promise, timeout]);
|
|
1220
|
-
}
|
|
1221
1470
|
buildSummary(results, totalDuration) {
|
|
1222
1471
|
const lines = [];
|
|
1223
1472
|
const succeeded = results.filter((r) => r.success).length;
|
|
@@ -1361,12 +1610,13 @@ function createDelegateTool(runner, config, roles) {
|
|
|
1361
1610
|
...topRole ? { role: topRole } : {},
|
|
1362
1611
|
...maxConcurrent ? { max_concurrent: maxConcurrent } : {}
|
|
1363
1612
|
},
|
|
1364
|
-
ctx.sessionId
|
|
1613
|
+
ctx.sessionId,
|
|
1614
|
+
ctx.signal
|
|
1365
1615
|
);
|
|
1366
1616
|
return {
|
|
1367
1617
|
success: result.success,
|
|
1368
1618
|
output: result.output,
|
|
1369
|
-
...result.success ? {} : { error: { code: 7002, message:
|
|
1619
|
+
...result.success ? {} : { error: { code: 7002, message: result.output } },
|
|
1370
1620
|
duration: result.duration
|
|
1371
1621
|
};
|
|
1372
1622
|
}
|
|
@@ -10430,6 +10680,17 @@ function blankResponseNote(chars) {
|
|
|
10430
10680
|
"\u4E0A\u4E00\u8F6E\u7684\u5DE5\u5177\u7ED3\u679C\u90FD\u8FD8\u5728\u4E0A\u9762\u7684\u4E0A\u4E0B\u6587\u91CC\uFF0C\u76F4\u63A5\u636E\u6B64\u4F5C\u7B54\uFF0C\u522B\u628A\u90A3\u4E9B\u5DE5\u5177\u91CD\u8DD1\u4E00\u904D\u3002"
|
|
10431
10681
|
].join("\n");
|
|
10432
10682
|
}
|
|
10683
|
+
var STALL_NOTE_ARGS_MAX_CHARS = 200;
|
|
10684
|
+
function stallNote(detail) {
|
|
10685
|
+
const args = detail.args.length > STALL_NOTE_ARGS_MAX_CHARS ? `${detail.args.slice(0, STALL_NOTE_ARGS_MAX_CHARS)}\u2026` : detail.args;
|
|
10686
|
+
const fact = detail.kind === "failure" ? `\u4F60\u5DF2\u7ECF\u7528\u540C\u4E00\u4EFD\u53C2\u6570\u8C03\u4E86 ${detail.count} \u6B21 ${detail.tool}\uFF08${args}\uFF09\uFF0C\u6BCF\u6B21\u90FD\u5931\u8D25\uFF0C\u800C\u4E14\u662F\u540C\u4E00\u6761\u9519\u8BEF\uFF1A${detail.error ?? ""}` : `\u4F60\u5DF2\u7ECF\u8FDE\u7EED ${detail.count} \u8F6E\u7528\u540C\u4E00\u4EFD\u53C2\u6570\u8C03 ${detail.tool}\uFF08${args}\uFF09\uFF0C\u7ED3\u679C\u4E0D\u4F1A\u56E0\u4E3A\u518D\u8C03\u4E00\u6B21\u800C\u6539\u53D8\u3002`;
|
|
10687
|
+
return [
|
|
10688
|
+
`${fact} \u2014\u2014 \u8FD9\u662F\u5728\u539F\u5730\u6253\u8F6C\u3002`,
|
|
10689
|
+
detail.kind === "failure" ? "\u522B\u518D\u539F\u6837\u91CD\u8BD5\u3002\u5148\u8BFB\u61C2\u8FD9\u6761\u9519\u8BEF\u8BF4\u7684\u662F\u4EC0\u4E48\uFF0C\u7136\u540E\u6362\u4E00\u79CD\u505A\u6CD5\uFF1A\u6362\u53C2\u6570\u3001\u6362\u4E00\u4E2A\u5DE5\u5177\uFF0C\u6216\u8005\u7ED5\u5F00\u8FD9\u4E00\u6B65\u7528\u5DF2\u7ECF\u62FF\u5230\u7684\u7ED3\u679C\u5F80\u4E0B\u8D70\u3002" : "\u522B\u518D\u539F\u6837\u8C03\u4E00\u6B21\u3002\u5B83\u7684\u7ED3\u679C\u5DF2\u7ECF\u5728\u4E0A\u9762\u7684\u4E0A\u4E0B\u6587\u91CC\u4E86\uFF0C\u76F4\u63A5\u7528\uFF1B\u8981\u4E48\u6362\u53C2\u6570\u3001\u6362\u4E00\u4E2A\u5DE5\u5177\uFF0C\u6362\u4E00\u79CD\u505A\u6CD5\u5F80\u4E0B\u8D70\u3002",
|
|
10690
|
+
"\u5982\u679C\u786E\u5B9E\u6CA1\u6709\u522B\u7684\u8DEF\uFF08\u5DE5\u5177\u672C\u8EAB\u574F\u4E86\u3001\u7F3A\u6743\u9650\u6216\u51ED\u636E\u3001\u9700\u8981\u7528\u6237\u5148\u505A\u70B9\u4EC0\u4E48\uFF09\uFF0C\u5C31\u505C\u4E0B\u6765\uFF0C\u76F4\u63A5\u544A\u8BC9\u7528\u6237\u5361\u5728\u54EA\u4E00\u6B65\u3001\u9700\u8981\u4ED6\u505A\u4EC0\u4E48\u3002",
|
|
10691
|
+
"\u8FD9\u662F\u552F\u4E00\u4E00\u6B21\u63D0\u9192\uFF1A\u518D\u9677\u8FDB\u540C\u6837\u7684\u91CD\u590D\uFF0C\u4EFB\u52A1\u4F1A\u88AB\u76F4\u63A5\u7EC8\u6B62\u3002"
|
|
10692
|
+
].join("\n");
|
|
10693
|
+
}
|
|
10433
10694
|
var CODE_VERIFICATION_POLICY = [
|
|
10434
10695
|
"<code_verification>",
|
|
10435
10696
|
"\u6539\u5B8C\u4EE3\u7801\u3001\u5BA3\u5E03\u5B8C\u6210\u4E4B\u524D\uFF0C\u8C03\u4E00\u6B21 `lsp_diagnostics`\uFF0C\u628A\u672C\u8F6E\u65B0\u5EFA\u6216\u4FEE\u6539\u8FC7\u7684\u6E90\u6587\u4EF6\u5217\u8FDB\u53BB\u3002",
|
|
@@ -10615,6 +10876,28 @@ var StallDetector = class {
|
|
|
10615
10876
|
}
|
|
10616
10877
|
return null;
|
|
10617
10878
|
}
|
|
10879
|
+
getStallDetail() {
|
|
10880
|
+
const failure = this.stalledFailure();
|
|
10881
|
+
if (failure) {
|
|
10882
|
+
return {
|
|
10883
|
+
kind: "failure",
|
|
10884
|
+
tool: failure.toolName,
|
|
10885
|
+
args: failure.args,
|
|
10886
|
+
count: failure.count,
|
|
10887
|
+
error: failure.error
|
|
10888
|
+
};
|
|
10889
|
+
}
|
|
10890
|
+
const streak = this.stalledStreak();
|
|
10891
|
+
if (streak) {
|
|
10892
|
+
return {
|
|
10893
|
+
kind: "streak",
|
|
10894
|
+
tool: streak.toolName,
|
|
10895
|
+
args: streak.args,
|
|
10896
|
+
count: streak.consecutive
|
|
10897
|
+
};
|
|
10898
|
+
}
|
|
10899
|
+
return null;
|
|
10900
|
+
}
|
|
10618
10901
|
reset() {
|
|
10619
10902
|
this.history = [];
|
|
10620
10903
|
this.failures.clear();
|
|
@@ -10933,7 +11216,7 @@ var ToolExecutor = class {
|
|
|
10933
11216
|
output: `\u64CD\u4F5C\u88AB hook \u963B\u6B62: ${blocked.reason || "\u672A\u77E5\u539F\u56E0"}`
|
|
10934
11217
|
});
|
|
10935
11218
|
}
|
|
10936
|
-
const gate = await this.checkPermission(et, tc.input);
|
|
11219
|
+
const gate = await this.checkPermission(et, tc.input, signal);
|
|
10937
11220
|
if (!gate.allowed) {
|
|
10938
11221
|
return finish({ ...base, success: false, output: gate.output });
|
|
10939
11222
|
}
|
|
@@ -11076,7 +11359,7 @@ var ToolExecutor = class {
|
|
|
11076
11359
|
);
|
|
11077
11360
|
return guessed || argsKey(args);
|
|
11078
11361
|
}
|
|
11079
|
-
async checkPermission(et, args) {
|
|
11362
|
+
async checkPermission(et, args, signal) {
|
|
11080
11363
|
const pm = this.deps.permissionManager;
|
|
11081
11364
|
if (!pm) return { allowed: true };
|
|
11082
11365
|
const toolName = et.name;
|
|
@@ -11110,6 +11393,9 @@ var ToolExecutor = class {
|
|
|
11110
11393
|
...decision.rememberable === false ? { rememberable: false } : {}
|
|
11111
11394
|
})
|
|
11112
11395
|
);
|
|
11396
|
+
if (signal?.aborted) {
|
|
11397
|
+
return { allowed: false, output: "\u64CD\u4F5C\u5DF2\u4E2D\u6B62\uFF1A\u7528\u6237\u7EC8\u6B62\u4E86\u8FD9\u4E00\u8F6E\u3002" };
|
|
11398
|
+
}
|
|
11113
11399
|
this.deps.telemetry?.counter(METRIC.TOOL_APPROVAL, 1, {
|
|
11114
11400
|
[GEN_AI.TOOL_NAME]: toolName,
|
|
11115
11401
|
"epoch.approval.outcome": outcome
|
|
@@ -11131,220 +11417,6 @@ var ToolExecutor = class {
|
|
|
11131
11417
|
return { allowed: true };
|
|
11132
11418
|
}
|
|
11133
11419
|
};
|
|
11134
|
-
var WINDOW_CHARS = 1024;
|
|
11135
|
-
var NGRAM = 16;
|
|
11136
|
-
var MAX_DISTINCT_RATIO = 0.2;
|
|
11137
|
-
var CHECK_EVERY_CHARS = 256;
|
|
11138
|
-
var SUSTAIN_CHECKS = 2;
|
|
11139
|
-
var MIN_NORMALIZED_CHARS = 256;
|
|
11140
|
-
function normalizeRuns(text) {
|
|
11141
|
-
return text.replace(/\s+/g, " ").replace(/(.)\1{3,}/gu, (_m, c) => c.repeat(3));
|
|
11142
|
-
}
|
|
11143
|
-
function distinctNgramRatio(text, n = NGRAM) {
|
|
11144
|
-
if (text.length < n) return 1;
|
|
11145
|
-
const seen = /* @__PURE__ */ new Set();
|
|
11146
|
-
let total = 0;
|
|
11147
|
-
for (let i = 0; i + n <= text.length; i++) {
|
|
11148
|
-
seen.add(text.slice(i, i + n));
|
|
11149
|
-
total++;
|
|
11150
|
-
}
|
|
11151
|
-
return total === 0 ? 1 : seen.size / total;
|
|
11152
|
-
}
|
|
11153
|
-
var RepetitionDetector = class {
|
|
11154
|
-
buf = "";
|
|
11155
|
-
sinceCheck = 0;
|
|
11156
|
-
streak = 0;
|
|
11157
|
-
chars = 0;
|
|
11158
|
-
hit = null;
|
|
11159
|
-
push(delta) {
|
|
11160
|
-
if (this.hit) return true;
|
|
11161
|
-
if (!delta) return false;
|
|
11162
|
-
this.chars += delta.length;
|
|
11163
|
-
this.buf = (this.buf + delta).slice(-WINDOW_CHARS);
|
|
11164
|
-
this.sinceCheck += delta.length;
|
|
11165
|
-
if (this.sinceCheck < CHECK_EVERY_CHARS) return false;
|
|
11166
|
-
this.sinceCheck = 0;
|
|
11167
|
-
if (this.buf.length < WINDOW_CHARS) return false;
|
|
11168
|
-
const norm = normalizeRuns(this.buf);
|
|
11169
|
-
if (norm.length < MIN_NORMALIZED_CHARS) {
|
|
11170
|
-
this.streak = 0;
|
|
11171
|
-
return false;
|
|
11172
|
-
}
|
|
11173
|
-
const ratio = distinctNgramRatio(norm);
|
|
11174
|
-
if (ratio > MAX_DISTINCT_RATIO) {
|
|
11175
|
-
this.streak = 0;
|
|
11176
|
-
return false;
|
|
11177
|
-
}
|
|
11178
|
-
if (++this.streak < SUSTAIN_CHECKS) return false;
|
|
11179
|
-
this.hit = { chars: this.chars, distinctRatio: ratio };
|
|
11180
|
-
return true;
|
|
11181
|
-
}
|
|
11182
|
-
pushAll(text) {
|
|
11183
|
-
for (let i = 0; i < text.length; i += CHECK_EVERY_CHARS) {
|
|
11184
|
-
if (this.push(text.slice(i, i + CHECK_EVERY_CHARS))) return true;
|
|
11185
|
-
}
|
|
11186
|
-
return false;
|
|
11187
|
-
}
|
|
11188
|
-
isLooping() {
|
|
11189
|
-
return this.hit !== null;
|
|
11190
|
-
}
|
|
11191
|
-
getInfo() {
|
|
11192
|
-
if (!this.hit) return null;
|
|
11193
|
-
return t("agent_run.looping_info", {
|
|
11194
|
-
chars: this.hit.chars,
|
|
11195
|
-
percent: Math.round(this.hit.distinctRatio * 100)
|
|
11196
|
-
});
|
|
11197
|
-
}
|
|
11198
|
-
};
|
|
11199
|
-
// src/agent/turn.ts
|
|
11200
|
-
async function* runTurn(opts) {
|
|
11201
|
-
if (opts.streaming) {
|
|
11202
|
-
return yield* runTurnStreaming(opts);
|
|
11203
|
-
}
|
|
11204
|
-
return yield* runTurnBuffered(opts);
|
|
11205
|
-
}
|
|
11206
|
-
async function* runTurnStreaming(opts) {
|
|
11207
|
-
const { provider, input, guard, reasoningGuard } = opts;
|
|
11208
|
-
const repetition = new RepetitionDetector();
|
|
11209
|
-
const reasoningRepetition = new RepetitionDetector();
|
|
11210
|
-
let text = "";
|
|
11211
|
-
let reasoningText = "";
|
|
11212
|
-
let finishReason = "stop";
|
|
11213
|
-
let looping;
|
|
11214
|
-
let usage;
|
|
11215
|
-
const toolCalls = [];
|
|
11216
|
-
let streamError;
|
|
11217
|
-
let model;
|
|
11218
|
-
let requestedModel;
|
|
11219
|
-
let warnings;
|
|
11220
|
-
const ac = new AbortController();
|
|
11221
|
-
const unlink = linkAbort(input.signal, ac);
|
|
11222
|
-
try {
|
|
11223
|
-
for await (const chunk of provider.stream({ ...input, signal: ac.signal })) {
|
|
11224
|
-
switch (chunk.type) {
|
|
11225
|
-
case "text-delta": {
|
|
11226
|
-
const raw = chunk.textDelta ?? "";
|
|
11227
|
-
if (!raw) break;
|
|
11228
|
-
const delta = guard ? guard.push(raw) : raw;
|
|
11229
|
-
if (!delta) break;
|
|
11230
|
-
text += delta;
|
|
11231
|
-
yield { type: "text-delta", text: delta };
|
|
11232
|
-
if (repetition.push(delta)) looping = repetition;
|
|
11233
|
-
break;
|
|
11234
|
-
}
|
|
11235
|
-
case "reasoning-delta": {
|
|
11236
|
-
const raw = chunk.textDelta ?? "";
|
|
11237
|
-
if (!raw) break;
|
|
11238
|
-
const delta = reasoningGuard ? reasoningGuard.push(raw) : raw;
|
|
11239
|
-
if (!delta) break;
|
|
11240
|
-
reasoningText += delta;
|
|
11241
|
-
yield { type: "reasoning-delta", text: delta };
|
|
11242
|
-
if (reasoningRepetition.push(delta)) looping = reasoningRepetition;
|
|
11243
|
-
break;
|
|
11244
|
-
}
|
|
11245
|
-
case "tool-call":
|
|
11246
|
-
if (chunk.toolCall) {
|
|
11247
|
-
toolCalls.push({
|
|
11248
|
-
toolCallId: chunk.toolCall.toolCallId,
|
|
11249
|
-
toolName: chunk.toolCall.toolName,
|
|
11250
|
-
input: chunk.toolCall.input
|
|
11251
|
-
});
|
|
11252
|
-
}
|
|
11253
|
-
break;
|
|
11254
|
-
case "finish":
|
|
11255
|
-
if (chunk.usage) usage = chunk.usage;
|
|
11256
|
-
if (chunk.finishReason) finishReason = chunk.finishReason;
|
|
11257
|
-
if (chunk.model) model = chunk.model;
|
|
11258
|
-
if (chunk.requestedModel) requestedModel = chunk.requestedModel;
|
|
11259
|
-
if (chunk.warnings) warnings = chunk.warnings;
|
|
11260
|
-
break;
|
|
11261
|
-
case "error":
|
|
11262
|
-
streamError = chunk.error ?? t("agent_run.unknown_provider_error");
|
|
11263
|
-
break;
|
|
11264
|
-
case "tool-result":
|
|
11265
|
-
break;
|
|
11266
|
-
}
|
|
11267
|
-
if (looping) {
|
|
11268
|
-
ac.abort();
|
|
11269
|
-
break;
|
|
11270
|
-
}
|
|
11271
|
-
}
|
|
11272
|
-
} finally {
|
|
11273
|
-
unlink();
|
|
11274
|
-
}
|
|
11275
|
-
if (streamError && !looping) throw new Error(streamError);
|
|
11276
|
-
if (guard) {
|
|
11277
|
-
const tail = guard.flush();
|
|
11278
|
-
if (tail) {
|
|
11279
|
-
text += tail;
|
|
11280
|
-
yield { type: "text-delta", text: tail };
|
|
11281
|
-
}
|
|
11282
|
-
}
|
|
11283
|
-
if (reasoningGuard) {
|
|
11284
|
-
const tail = reasoningGuard.flush();
|
|
11285
|
-
if (tail) {
|
|
11286
|
-
reasoningText += tail;
|
|
11287
|
-
yield { type: "reasoning-delta", text: tail };
|
|
11288
|
-
}
|
|
11289
|
-
}
|
|
11290
|
-
return {
|
|
11291
|
-
text,
|
|
11292
|
-
...usage ? { usage } : {},
|
|
11293
|
-
finishReason: looping ? "looping" : finishReason,
|
|
11294
|
-
...looping ? { loopingInfo: looping.getInfo() ?? "" } : {},
|
|
11295
|
-
...toolCalls.length > 0 ? { toolCalls } : {},
|
|
11296
|
-
...reasoningText ? { reasoningText } : {},
|
|
11297
|
-
...model ? { model } : {},
|
|
11298
|
-
...requestedModel ? { requestedModel } : {},
|
|
11299
|
-
...warnings && warnings.length > 0 ? { warnings } : {}
|
|
11300
|
-
};
|
|
11301
|
-
}
|
|
11302
|
-
async function* runTurnBuffered(opts) {
|
|
11303
|
-
const { provider, input, guard, reasoningGuard } = opts;
|
|
11304
|
-
const result = await provider.generate(input);
|
|
11305
|
-
const reasoningText = result.reasoningText ? filterWhole(reasoningGuard, result.reasoningText) : result.reasoningText;
|
|
11306
|
-
if (reasoningText) {
|
|
11307
|
-
yield { type: "reasoning-delta", text: reasoningText };
|
|
11308
|
-
}
|
|
11309
|
-
const text = filterWhole(guard, result.text);
|
|
11310
|
-
if (text) {
|
|
11311
|
-
yield { type: "text-delta", text };
|
|
11312
|
-
}
|
|
11313
|
-
const { reasoningText: _replaced, ...rest } = result;
|
|
11314
|
-
const textLoop = new RepetitionDetector();
|
|
11315
|
-
const reasoningLoop = new RepetitionDetector();
|
|
11316
|
-
textLoop.pushAll(text);
|
|
11317
|
-
if (reasoningText) reasoningLoop.pushAll(reasoningText);
|
|
11318
|
-
let looping;
|
|
11319
|
-
if (textLoop.isLooping()) looping = textLoop;
|
|
11320
|
-
else if (reasoningLoop.isLooping()) looping = reasoningLoop;
|
|
11321
|
-
return {
|
|
11322
|
-
...rest,
|
|
11323
|
-
text,
|
|
11324
|
-
...reasoningText ? { reasoningText } : {},
|
|
11325
|
-
...looping ? { finishReason: "looping", loopingInfo: looping.getInfo() ?? "" } : {}
|
|
11326
|
-
};
|
|
11327
|
-
}
|
|
11328
|
-
function linkAbort(outer, ac) {
|
|
11329
|
-
if (!outer) return () => {
|
|
11330
|
-
};
|
|
11331
|
-
if (outer.aborted) {
|
|
11332
|
-
ac.abort(outer.reason);
|
|
11333
|
-
return () => {
|
|
11334
|
-
};
|
|
11335
|
-
}
|
|
11336
|
-
const onAbort = () => ac.abort(outer.reason);
|
|
11337
|
-
outer.addEventListener("abort", onAbort, { once: true });
|
|
11338
|
-
return () => outer.removeEventListener("abort", onAbort);
|
|
11339
|
-
}
|
|
11340
|
-
function filterWhole(guard, text) {
|
|
11341
|
-
if (!guard || !text) return text;
|
|
11342
|
-
return guard.push(text) + guard.flush();
|
|
11343
|
-
}
|
|
11344
|
-
// src/agent/types.ts
|
|
11345
|
-
function isAbortSignal(signal) {
|
|
11346
|
-
return !!signal && typeof signal.addEventListener === "function" && typeof signal.removeEventListener === "function";
|
|
11347
|
-
}
|
|
11348
11420
|
// src/agent/loop.ts
|
|
11349
11421
|
var DEFAULT_CONTEXT_LENGTH = 128e3;
|
|
11350
11422
|
function shrank(before, after) {
|
|
@@ -11671,6 +11743,7 @@ var AgentLoop = class {
|
|
|
11671
11743
|
let idleNudged = false;
|
|
11672
11744
|
let blankNudged = false;
|
|
11673
11745
|
let blankAfterNudge = false;
|
|
11746
|
+
let stallNudged = false;
|
|
11674
11747
|
for (let turn = 1; turn <= maxTurns; turn++) {
|
|
11675
11748
|
if (this.isAborted(input.signal)) {
|
|
11676
11749
|
if (turns.length > 0 || lastText) {
|
|
@@ -12069,7 +12142,17 @@ ${finalText}` };
|
|
|
12069
12142
|
turn
|
|
12070
12143
|
});
|
|
12071
12144
|
yield { type: "turn-complete", turn };
|
|
12072
|
-
|
|
12145
|
+
const stall = this.stallDetector.getStallDetail();
|
|
12146
|
+
if (stall && !stallNudged && turn < maxTurns) {
|
|
12147
|
+
stallNudged = true;
|
|
12148
|
+
yield {
|
|
12149
|
+
type: "notice",
|
|
12150
|
+
code: "stall-nudged",
|
|
12151
|
+
text: t("agent_run.stall_nudged", { info: this.stallDetector.getStallInfo() ?? "" })
|
|
12152
|
+
};
|
|
12153
|
+
this.stallDetector.reset();
|
|
12154
|
+
messages.push({ role: "user", content: systemNote(stallNote(stall)) });
|
|
12155
|
+
} else if (stall) {
|
|
12073
12156
|
const msg2 = t("agent_run.stalled", { info: this.stallDetector.getStallInfo() ?? "" });
|
|
12074
12157
|
yield { type: "text-delta", text: msg2 };
|
|
12075
12158
|
await this.prompt.syncMemory(userText, msg2);
|
|
@@ -12421,8 +12504,48 @@ var EpochConfigSchema = z.object({
|
|
|
12421
12504
|
message: "blankCards: id must be unique"
|
|
12422
12505
|
}).optional(),
|
|
12423
12506
|
blankGreeting: BlankGreetingSchema.optional(),
|
|
12424
|
-
user: HostUserSchema.optional()
|
|
12507
|
+
user: HostUserSchema.optional(),
|
|
12508
|
+
settingsHidden: z.array(z.string().min(1)).min(1).optional(),
|
|
12509
|
+
settingsPanes: z.array(
|
|
12510
|
+
z.object({
|
|
12511
|
+
id: z.string().min(1).refine((id) => !isSettingsPaneId(id), {
|
|
12512
|
+
message: `id must not collide with a built-in pane (${SETTINGS_PANE_IDS.join(", ")})`
|
|
12513
|
+
}),
|
|
12514
|
+
label: z.string().min(1),
|
|
12515
|
+
icon: z.string().min(1).optional(),
|
|
12516
|
+
url: z.string().min(1).refine(
|
|
12517
|
+
(url) => {
|
|
12518
|
+
try {
|
|
12519
|
+
const scheme = new URL(url).protocol;
|
|
12520
|
+
return scheme === "http:" || scheme === "https:";
|
|
12521
|
+
} catch {
|
|
12522
|
+
return false;
|
|
12523
|
+
}
|
|
12524
|
+
},
|
|
12525
|
+
{ message: "url must be an absolute http(s) URL" }
|
|
12526
|
+
),
|
|
12527
|
+
before: z.string().min(1).optional()
|
|
12528
|
+
})
|
|
12529
|
+
).min(1).refine((items) => new Set(items.map((item) => item.id)).size === items.length, {
|
|
12530
|
+
message: "settingsPanes: id must be unique"
|
|
12531
|
+
}).optional()
|
|
12425
12532
|
});
|
|
12533
|
+
function settingsHiddenIssues(config) {
|
|
12534
|
+
const hidden = config.settingsHidden;
|
|
12535
|
+
if (hidden === void 0) return [];
|
|
12536
|
+
return hidden.filter((id) => !isSettingsPaneId(id)).map((id) => ({
|
|
12537
|
+
path: "settingsHidden",
|
|
12538
|
+
message: `unknown settings pane id: ${id} (known: ${SETTINGS_PANE_IDS.join(", ")})`
|
|
12539
|
+
}));
|
|
12540
|
+
}
|
|
12541
|
+
function settingsPaneBeforeIssues(config) {
|
|
12542
|
+
const panes = config.settingsPanes;
|
|
12543
|
+
if (panes === void 0) return [];
|
|
12544
|
+
return panes.filter((pane) => pane.before !== void 0 && !isSettingsPaneId(pane.before)).map((pane) => ({
|
|
12545
|
+
path: `settingsPanes.${pane.id}.before`,
|
|
12546
|
+
message: `unknown settings pane id: ${String(pane.before)} (known: ${SETTINGS_PANE_IDS.join(", ")})`
|
|
12547
|
+
}));
|
|
12548
|
+
}
|
|
12426
12549
|
function defaultConfig() {
|
|
12427
12550
|
return EpochConfigSchema.parse({});
|
|
12428
12551
|
}
|
|
@@ -12893,6 +13016,8 @@ function parseRawConfig(raw, issues, track) {
|
|
|
12893
13016
|
EpochConfigSchema.parse({})
|
|
12894
13017
|
);
|
|
12895
13018
|
issues.push(...outcome.issues);
|
|
13019
|
+
issues.push(...settingsHiddenIssues(outcome.value));
|
|
13020
|
+
issues.push(...settingsPaneBeforeIssues(outcome.value));
|
|
12896
13021
|
if (outcome.issues.length === 0) collectKeyPaths(migrated.raw, track.file);
|
|
12897
13022
|
return outcome.value;
|
|
12898
13023
|
}
|
|
@@ -19524,6 +19649,9 @@ var ScheduleRegistrar = class {
|
|
|
19524
19649
|
this.opts = opts;
|
|
19525
19650
|
}
|
|
19526
19651
|
opts;
|
|
19652
|
+
get homeDir() {
|
|
19653
|
+
return this.opts.homeDir;
|
|
19654
|
+
}
|
|
19527
19655
|
osTaskIdFor(taskId) {
|
|
19528
19656
|
const backend = this.opts.backend;
|
|
19529
19657
|
if (!backend) return "";
|
|
@@ -19594,7 +19722,6 @@ var ScheduleRegistrar = class {
|
|
|
19594
19722
|
if (this.opts.backend && def.osTaskId) await this.opts.backend.retire(def.osTaskId);
|
|
19595
19723
|
}
|
|
19596
19724
|
};
|
|
19597
|
-
// src/schedule/doctor.ts
|
|
19598
19725
|
async function diagnoseSchedules(opts) {
|
|
19599
19726
|
const { store, backend, registrar } = opts;
|
|
19600
19727
|
if (!backend) return { checked: 0, drifts: [], unsupported: true };
|
|
@@ -19631,8 +19758,18 @@ async function diagnoseSchedules(opts) {
|
|
|
19631
19758
|
});
|
|
19632
19759
|
}
|
|
19633
19760
|
}
|
|
19761
|
+
let foreign = 0;
|
|
19634
19762
|
for (const osTaskId of osTaskIds) {
|
|
19635
19763
|
if (seen.has(osTaskId)) continue;
|
|
19764
|
+
const snapshot = await backend.inspect(osTaskId);
|
|
19765
|
+
if (!snapshot) {
|
|
19766
|
+
foreign++;
|
|
19767
|
+
continue;
|
|
19768
|
+
}
|
|
19769
|
+
if (!ownedByThisHome(snapshot.argv, registrar.homeDir)) {
|
|
19770
|
+
foreign++;
|
|
19771
|
+
continue;
|
|
19772
|
+
}
|
|
19636
19773
|
const taskId = backend.taskIdFrom(osTaskId);
|
|
19637
19774
|
drifts.push({
|
|
19638
19775
|
kind: "orphan-in-os",
|
|
@@ -19640,7 +19777,17 @@ async function diagnoseSchedules(opts) {
|
|
|
19640
19777
|
...taskId ? { scheduleId: taskId } : {}
|
|
19641
19778
|
});
|
|
19642
19779
|
}
|
|
19643
|
-
return {
|
|
19780
|
+
return {
|
|
19781
|
+
checked: defs.length + osTaskIds.length - seen.size - foreign,
|
|
19782
|
+
drifts,
|
|
19783
|
+
unsupported: false
|
|
19784
|
+
};
|
|
19785
|
+
}
|
|
19786
|
+
function ownedByThisHome(argv, homeDir) {
|
|
19787
|
+
const at = argv.indexOf("--home");
|
|
19788
|
+
const home = at >= 0 ? argv[at + 1] : void 0;
|
|
19789
|
+
if (home === void 0 || home.length === 0) return true;
|
|
19790
|
+
return samePath(home, homeDir);
|
|
19644
19791
|
}
|
|
19645
19792
|
function compare2(def, snapshot, backend, registrar) {
|
|
19646
19793
|
const expected = backend.expectedSnapshot(registrar.toRegisterInput(def, def.osCommand));
|