@epoch-agent/core 0.7.1 → 0.9.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/README.md +28 -0
- package/dist/index.d.ts +1044 -53
- package/dist/index.js +1394 -391
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { existsSync, statSync, readdirSync, readFileSync, mkdirSync, writeFileSync, rmSync, symlinkSync, cpSync,
|
|
1
|
+
import { existsSync, statSync, readdirSync, readFileSync, mkdirSync, writeFileSync, renameSync, rmSync, symlinkSync, cpSync, mkdtempSync, realpathSync, copyFileSync, unlinkSync, openSync, writeSync, closeSync, readSync, lstatSync } from 'fs';
|
|
2
2
|
import { homedir, platform, tmpdir, hostname } from 'os';
|
|
3
|
-
import { join, dirname, resolve,
|
|
3
|
+
import { join, dirname, resolve, relative, isAbsolute, sep, extname, basename } from 'path';
|
|
4
4
|
import { execFile, execSync, execFileSync, spawn } from 'child_process';
|
|
5
|
-
import { createLogger, dbPath, resolveHomeDir, DEFAULT_MAX_TURNS, t, unknownKeyIssues, parseLenient, issueDetails, splitWords, checkpointsDir, trustedImportsPath, budgetStatePath, artifactsDir, UNLIMITED_TURNS, turnLimitOf, configPath, managedSettingsPath, projectSettingsPath, projectLocalSettingsPath, getSecretValues, listStoredSecretNames, maskApiKey, setSectionField, isInWorkspace, workspacesPath,
|
|
5
|
+
import { createLogger, dbPath, resolveHomeDir, DEFAULT_MAX_TURNS, t, openDatabase, migrate, releaseDatabase, unknownKeyIssues, parseLenient, issueDetails, splitWords, checkpointsDir, trustedImportsPath, budgetStatePath, artifactsDir, UNLIMITED_TURNS, turnLimitOf, configPath, managedSettingsPath, projectSettingsPath, projectLocalSettingsPath, getSecretValues, listStoredSecretNames, maskApiKey, setSectionField, isInWorkspace, workspacesPath, automationLogsDir, projectPoliciesDir, projectComplianceDir, projectHooksPath, projectClaudeSettingsPath, projectClaudeLocalSettingsPath, projectSkillsDir, trustPath, detectBackend, EXEC_PATH_AS_NODE_ENV, isolate, createStreamDecoder, parseShellCommand, writeArtifact, envPath, checkDangerousCommand, checkObfuscation, countCodePoints, keepHeadAndTail, normalizeForMatch, isReadOnlyCommand, CODE_EXEC_ROOTS, headCodePoints, 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, 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, systemNote, totalUsageTokens, NOOP_TELEMETRY, METRIC, contentToText, GEN_AI, 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, attachSessionSurface, isContextNearlyFull, isOperationType, normalizeApproval, 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, 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, systemNote, 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, attachSessionSurface, isContextNearlyFull, isOperationType, normalizeApproval, 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';
|
|
@@ -561,8 +561,9 @@ function mapOperation(tool) {
|
|
|
561
561
|
if (tool.operation && isOperationType(tool.operation)) return tool.operation;
|
|
562
562
|
return fromAnnotations(tool.annotations) ?? mapOperationType(tool.name);
|
|
563
563
|
}
|
|
564
|
+
var MCP_TOOL_PREFIX = "mcp__";
|
|
564
565
|
function isOpenWorldTool(tool) {
|
|
565
|
-
return tool.annotations?.openWorldHint === true;
|
|
566
|
+
return tool.annotations?.openWorldHint === true || tool.name.startsWith(MCP_TOOL_PREFIX);
|
|
566
567
|
}
|
|
567
568
|
// src/code-mode/readonly.ts
|
|
568
569
|
var BLOCKED_OPERATIONS = /* @__PURE__ */ new Set(["command", "code_exec"]);
|
|
@@ -628,7 +629,7 @@ var ToolRegistry = class {
|
|
|
628
629
|
this.addTool(tool, source);
|
|
629
630
|
}
|
|
630
631
|
registerTools(tools, source = "builtin") {
|
|
631
|
-
for (const
|
|
632
|
+
for (const t78 of tools) this.addTool(t78, source);
|
|
632
633
|
}
|
|
633
634
|
addTool(tool, source) {
|
|
634
635
|
const existing = this.tools.get(tool.name);
|
|
@@ -677,14 +678,14 @@ var ToolRegistry = class {
|
|
|
677
678
|
return isCodeModeDeferred(tool) ? ToolExposure.Deferred : declared;
|
|
678
679
|
}
|
|
679
680
|
exposed() {
|
|
680
|
-
return this.list().filter((
|
|
681
|
+
return this.list().filter((t78) => this.exposureOf(t78) === ToolExposure.Direct);
|
|
681
682
|
}
|
|
682
683
|
byExposure(exposure) {
|
|
683
|
-
return this.list().filter((
|
|
684
|
+
return this.list().filter((t78) => this.exposureOf(t78) === exposure);
|
|
684
685
|
}
|
|
685
686
|
codeModeDeclared() {
|
|
686
687
|
if (!this.codeMode) return [];
|
|
687
|
-
return this.list().filter((
|
|
688
|
+
return this.list().filter((t78) => isCodeModeDeferred(t78));
|
|
688
689
|
}
|
|
689
690
|
sourceOf(name) {
|
|
690
691
|
return this.tools.get(name)?.source;
|
|
@@ -733,7 +734,7 @@ var ToolRegistry = class {
|
|
|
733
734
|
function staticToolProvider(tools) {
|
|
734
735
|
return {
|
|
735
736
|
list: () => tools,
|
|
736
|
-
get: (name) => tools.find((
|
|
737
|
+
get: (name) => tools.find((t78) => t78.name === name)
|
|
737
738
|
};
|
|
738
739
|
}
|
|
739
740
|
var log = createLogger("tokenizer");
|
|
@@ -1145,7 +1146,7 @@ var DelegateManager = class {
|
|
|
1145
1146
|
}
|
|
1146
1147
|
normalizeTasks(args) {
|
|
1147
1148
|
if (args.tasks && args.tasks.length > 0) {
|
|
1148
|
-
return args.role ? args.tasks.map((
|
|
1149
|
+
return args.role ? args.tasks.map((t78) => ({ ...t78, role: t78.role ?? args.role })) : args.tasks;
|
|
1149
1150
|
}
|
|
1150
1151
|
if (args.goal) return [{ goal: args.goal, ...args.role ? { role: args.role } : {} }];
|
|
1151
1152
|
return [];
|
|
@@ -1155,7 +1156,7 @@ var DelegateManager = class {
|
|
|
1155
1156
|
for (let i = 0; i < tasks.length; i += maxConcurrent) {
|
|
1156
1157
|
const batch = tasks.slice(i, i + maxConcurrent);
|
|
1157
1158
|
const batchResults = await Promise.allSettled(
|
|
1158
|
-
batch.map((
|
|
1159
|
+
batch.map((t78) => this.runSingleTask(t78, parentSessionId))
|
|
1159
1160
|
);
|
|
1160
1161
|
for (const r of batchResults) {
|
|
1161
1162
|
if (r.status === "fulfilled") {
|
|
@@ -1296,21 +1297,21 @@ function createDelegateTool(runner, config, roles) {
|
|
|
1296
1297
|
operation: "command",
|
|
1297
1298
|
describeTarget: (args) => {
|
|
1298
1299
|
const tasks = Array.isArray(args["tasks"]) ? args["tasks"] : [];
|
|
1299
|
-
const goals = tasks.map((
|
|
1300
|
+
const goals = tasks.map((t78) => String(t78["goal"] ?? ""));
|
|
1300
1301
|
const single = typeof args["goal"] === "string" ? args["goal"] : "";
|
|
1301
1302
|
return [...goals, single].filter(Boolean).join(" | ");
|
|
1302
1303
|
},
|
|
1303
1304
|
async execute(args, ctx) {
|
|
1304
1305
|
const start = Date.now();
|
|
1305
1306
|
const rawTasks = Array.isArray(args["tasks"]) ? args["tasks"] : void 0;
|
|
1306
|
-
const tasks = rawTasks?.map((
|
|
1307
|
-
const o =
|
|
1307
|
+
const tasks = rawTasks?.map((t78) => {
|
|
1308
|
+
const o = t78;
|
|
1308
1309
|
return {
|
|
1309
1310
|
goal: String(o["goal"] ?? "").trim(),
|
|
1310
1311
|
context: String(o["context"] ?? ""),
|
|
1311
1312
|
...typeof o["role"] === "string" && o["role"].trim() ? { role: o["role"].trim() } : {}
|
|
1312
1313
|
};
|
|
1313
|
-
}).filter((
|
|
1314
|
+
}).filter((t78) => t78.goal.length > 0);
|
|
1314
1315
|
const goal = typeof args["goal"] === "string" ? args["goal"].trim() : "";
|
|
1315
1316
|
const topRole = typeof args["role"] === "string" ? args["role"].trim() : "";
|
|
1316
1317
|
if ((!tasks || tasks.length === 0) && !goal) {
|
|
@@ -1326,7 +1327,7 @@ function createDelegateTool(runner, config, roles) {
|
|
|
1326
1327
|
};
|
|
1327
1328
|
}
|
|
1328
1329
|
const known = listRoles();
|
|
1329
|
-
const requested = [...(tasks ?? []).map((
|
|
1330
|
+
const requested = [...(tasks ?? []).map((t78) => t78.role), topRole].filter(
|
|
1330
1331
|
(r) => Boolean(r)
|
|
1331
1332
|
);
|
|
1332
1333
|
const unknown = [...new Set(requested)].filter((r) => !known.some((k) => k.name === r));
|
|
@@ -1498,9 +1499,9 @@ function createGoalTool(deps) {
|
|
|
1498
1499
|
};
|
|
1499
1500
|
}
|
|
1500
1501
|
var VALID_TARGETS = ["memory", "user"];
|
|
1501
|
-
var VALID_ACTIONS = ["add", "replace", "remove", "search", "budget"];
|
|
1502
|
-
function isValidTarget(
|
|
1503
|
-
return typeof
|
|
1502
|
+
var VALID_ACTIONS = ["add", "replace", "remove", "search", "read", "budget"];
|
|
1503
|
+
function isValidTarget(t78) {
|
|
1504
|
+
return typeof t78 === "string" && VALID_TARGETS.includes(t78);
|
|
1504
1505
|
}
|
|
1505
1506
|
function isValidAction(a) {
|
|
1506
1507
|
return VALID_ACTIONS.includes(a);
|
|
@@ -1521,9 +1522,10 @@ function createMemoryTool(memory) {
|
|
|
1521
1522
|
name: "memory",
|
|
1522
1523
|
description: [
|
|
1523
1524
|
"\u8BFB\u5199\u957F\u671F\u8BB0\u5FC6\u3002\u8DE8\u4F1A\u8BDD\u6301\u4E45\u5316\uFF0C\u7528\u4E8E\u8BB0\u4F4F\u7528\u6237\u504F\u597D\u3001\u9879\u76EE\u7EA6\u5B9A\u3001\u8E29\u8FC7\u7684\u5751\u3002",
|
|
1524
|
-
"action=add \u65B0\u589E\u6761\u76EE\uFF1Breplace \u66FF\u6362\u5DF2\u6709\u6761\u76EE\uFF1Bremove \u5220\u9664\uFF1Bsearch \u68C0\u7D22\uFF1Bbudget \u67E5\u770B\u914D\u989D\u3002",
|
|
1525
|
+
"action=add \u65B0\u589E\u6761\u76EE\uFF1Breplace \u66FF\u6362\u5DF2\u6709\u6761\u76EE\uFF1Bremove \u5220\u9664\uFF1Bsearch \u68C0\u7D22\uFF1Bread \u6309 id \u53D6\u5168\u6587\uFF1Bbudget \u67E5\u770B\u914D\u989D\u3002",
|
|
1525
1526
|
'target=user \u5B58"\u7528\u6237\u662F\u8C01/\u504F\u597D\u4EC0\u4E48"\uFF1Btarget=memory \u5B58"\u6211\u9700\u8981\u8BB0\u4F4F\u7684\u9879\u76EE\u77E5\u8BC6"\u3002',
|
|
1526
|
-
"\u53EA\u8BB0\u5F55\u975E\u663E\u800C\u6613\u89C1\u3001\u4EE5\u540E\u8FD8\u4F1A\u7528\u5230\u7684\u4FE1\u606F\u2014\u2014\u4E0D\u8981\u8BB0\u5F55\u80FD\u4ECE\u4EE3\u7801\u6216 git \u5386\u53F2\u8BFB\u51FA\u6765\u7684\u5185\u5BB9\u3002"
|
|
1527
|
+
"\u53EA\u8BB0\u5F55\u975E\u663E\u800C\u6613\u89C1\u3001\u4EE5\u540E\u8FD8\u4F1A\u7528\u5230\u7684\u4FE1\u606F\u2014\u2014\u4E0D\u8981\u8BB0\u5F55\u80FD\u4ECE\u4EE3\u7801\u6216 git \u5386\u53F2\u8BFB\u51FA\u6765\u7684\u5185\u5BB9\u3002",
|
|
1528
|
+
"system prompt \u91CC\u7684 MEMORY INDEX \u53EA\u6709\u6BCF\u6761\u7684\u9996\u884C\u6458\u8981\uFF1B\u8981\u5168\u6587\u7528 read\uFF0C\u627E\u4E0D\u5230\u7684\u7528 search\u3002"
|
|
1527
1529
|
].join("\n"),
|
|
1528
1530
|
parameters: {
|
|
1529
1531
|
type: "object",
|
|
@@ -1531,7 +1533,7 @@ function createMemoryTool(memory) {
|
|
|
1531
1533
|
action: {
|
|
1532
1534
|
type: "string",
|
|
1533
1535
|
description: "\u64CD\u4F5C\u7C7B\u578B",
|
|
1534
|
-
enum: ["add", "replace", "remove", "search", "budget"]
|
|
1536
|
+
enum: ["add", "replace", "remove", "search", "read", "budget"]
|
|
1535
1537
|
},
|
|
1536
1538
|
target: {
|
|
1537
1539
|
type: "string",
|
|
@@ -1544,7 +1546,11 @@ function createMemoryTool(memory) {
|
|
|
1544
1546
|
},
|
|
1545
1547
|
old_text: {
|
|
1546
1548
|
type: "string",
|
|
1547
|
-
description: "replace/remove \
|
|
1549
|
+
description: "replace/remove \u5B9A\u4F4D\u6761\u76EE\u7684**\u5168\u6587**\uFF08\u9700\u5B8C\u5168\u76F8\u540C\uFF09\u3002\u624B\u4E0A\u53EA\u6709\u7D22\u5F15\u65F6\u7528 id\uFF0C\u522B\u7528\u8FD9\u4E2A\u2014\u2014\u7D22\u5F15\u91CC\u7684\u6458\u8981\u662F\u622A\u65AD\u7684\uFF0C\u5339\u914D\u4E0D\u4E0A\u3002"
|
|
1550
|
+
},
|
|
1551
|
+
id: {
|
|
1552
|
+
type: "string",
|
|
1553
|
+
description: "replace/remove \u8981\u5B9A\u4F4D\u7684\u6761\u76EE id\uFF0C\u5F62\u5982 m:a1b2c3\uFF08\u6765\u81EA MEMORY INDEX \u6216 search\uFF09\u3002\u4E0E old_text \u4E8C\u9009\u4E00\uFF0C\u4F18\u5148\u7528\u5B83\u3002"
|
|
1548
1554
|
},
|
|
1549
1555
|
query: {
|
|
1550
1556
|
type: "string",
|
|
@@ -1553,6 +1559,11 @@ function createMemoryTool(memory) {
|
|
|
1553
1559
|
limit: {
|
|
1554
1560
|
type: "number",
|
|
1555
1561
|
description: "search \u8FD4\u56DE\u6761\u6570\u4E0A\u9650\uFF0C\u9ED8\u8BA4 5\u3002"
|
|
1562
|
+
},
|
|
1563
|
+
ids: {
|
|
1564
|
+
type: "array",
|
|
1565
|
+
items: { type: "string" },
|
|
1566
|
+
description: "read \u8981\u53D6\u7684\u6761\u76EE id\uFF0C\u5F62\u5982 m:a1b2c3 / u:0b22e9\uFF08\u6765\u81EA MEMORY INDEX \u6216 search \u7ED3\u679C\uFF09\u3002"
|
|
1556
1567
|
}
|
|
1557
1568
|
},
|
|
1558
1569
|
required: ["action"]
|
|
@@ -1590,7 +1601,21 @@ function createMemoryTool(memory) {
|
|
|
1590
1601
|
const limit = typeof args["limit"] === "number" ? args["limit"] : 5;
|
|
1591
1602
|
const results = await memory.search(query, limit);
|
|
1592
1603
|
if (results.length === 0) return ok("(\u65E0\u5339\u914D\u8BB0\u5FC6)", start);
|
|
1593
|
-
return ok(results.map((r) =>
|
|
1604
|
+
return ok(results.map((r) => `${r.id} [${r.target}] ${r.content}`).join("\n"), start);
|
|
1605
|
+
}
|
|
1606
|
+
if (action === "read") {
|
|
1607
|
+
const raw = args["ids"];
|
|
1608
|
+
const ids = Array.isArray(raw) ? raw.map((v) => String(v)) : [];
|
|
1609
|
+
if (ids.length === 0) return fail("read \u9700\u8981\u975E\u7A7A ids \u6570\u7EC4", start);
|
|
1610
|
+
const { found, missing } = await memory.read(ids);
|
|
1611
|
+
const lines = found.map((f) => `${f.id} [${f.target}]
|
|
1612
|
+
${f.content}`);
|
|
1613
|
+
if (missing.length > 0) {
|
|
1614
|
+
lines.push(
|
|
1615
|
+
`(\u8BA4\u4E0D\u51FA\u7684 id: ${missing.join(", ")} \u2014\u2014 \u5B83\u4EEC\u4E0D\u5728\u5F53\u524D\u8BB0\u5FC6\u5E93\u91CC\uFF0C\u7528 search \u91CD\u65B0\u627E)`
|
|
1616
|
+
);
|
|
1617
|
+
}
|
|
1618
|
+
return ok(lines.join("\n\n"), start);
|
|
1594
1619
|
}
|
|
1595
1620
|
const target = args["target"];
|
|
1596
1621
|
if (!isValidTarget(target)) {
|
|
@@ -1605,18 +1630,24 @@ function createMemoryTool(memory) {
|
|
|
1605
1630
|
return fail(r.error ?? "\u5199\u5165\u5931\u8D25", start);
|
|
1606
1631
|
}
|
|
1607
1632
|
if (action === "replace") {
|
|
1608
|
-
const
|
|
1633
|
+
const ref2 = String(args["id"] ?? args["old_text"] ?? "").trim();
|
|
1609
1634
|
const content = String(args["content"] ?? "").trim();
|
|
1610
|
-
if (!
|
|
1635
|
+
if (!ref2) return fail("replace \u9700\u8981 id \u6216 old_text", start);
|
|
1611
1636
|
if (!content) return fail("replace \u9700\u8981\u975E\u7A7A content", start);
|
|
1612
|
-
const r = await memory.replace(target,
|
|
1637
|
+
const r = await memory.replace(target, ref2, content);
|
|
1613
1638
|
if (r.success) return ok(`\u5DF2\u66FF\u6362 ${target} \u8BB0\u5FC6\u6761\u76EE`, start);
|
|
1614
1639
|
if (r.consolidateHint) return ok(r.consolidateHint, start);
|
|
1615
1640
|
return fail(r.error ?? "\u66FF\u6362\u5931\u8D25", start);
|
|
1616
1641
|
}
|
|
1617
|
-
const
|
|
1618
|
-
if (!
|
|
1619
|
-
await memory.remove(target,
|
|
1642
|
+
const ref = String(args["id"] ?? args["old_text"] ?? "").trim();
|
|
1643
|
+
if (!ref) return fail("remove \u9700\u8981 id \u6216 old_text", start);
|
|
1644
|
+
const done2 = await memory.remove(target, ref);
|
|
1645
|
+
if (!done2) {
|
|
1646
|
+
return fail(
|
|
1647
|
+
`${target} \u91CC\u6CA1\u6709\u5339\u914D "${ref}" \u7684\u6761\u76EE\u3002\u7528 MEMORY INDEX \u91CC\u7684 id\uFF08\u5982 id="m:a1b2c3"\uFF09\uFF0C\u6216\u5148 memory(action="search") \u627E\u4E00\u904D \u2014\u2014 old_text \u8981\u6C42\u5168\u6587\u5B8C\u5168\u76F8\u540C\uFF0C\u7D22\u5F15\u91CC\u90A3\u884C\u662F\u622A\u65AD\u8FC7\u7684\u3002`,
|
|
1648
|
+
start
|
|
1649
|
+
);
|
|
1650
|
+
}
|
|
1620
1651
|
return ok(`\u5DF2\u5220\u9664 ${target} \u8BB0\u5FC6\u6761\u76EE`, start);
|
|
1621
1652
|
} catch (err) {
|
|
1622
1653
|
return fail(err instanceof Error ? err.message : String(err), start, 4002);
|
|
@@ -2398,6 +2429,57 @@ var SCHEDULE_DEFAULT_ALLOWLIST = {
|
|
|
2398
2429
|
allowOperations: ["file_read"],
|
|
2399
2430
|
allowRules: []
|
|
2400
2431
|
};
|
|
2432
|
+
// src/schedule/unattended-lint.ts
|
|
2433
|
+
var ANCHORS = [
|
|
2434
|
+
{ kind: "asks-human", zh: /问(?:一下|下)?我/, en: /\bask\s+(?:me|us)\b/i },
|
|
2435
|
+
{
|
|
2436
|
+
kind: "asks-human",
|
|
2437
|
+
zh: /(?:告诉我|让我知道)[^\n]{0,8}(?:要不要|是否|该不该|需不需要)/,
|
|
2438
|
+
en: /\blet\s+(?:me|us)\s+know\s+(?:if|whether|before)\b/i
|
|
2439
|
+
},
|
|
2440
|
+
{
|
|
2441
|
+
kind: "asks-human",
|
|
2442
|
+
zh: /(?:请(?:先)?确认|(?:跟|向|和)我确认|征求我)/,
|
|
2443
|
+
en: /\b(?:confirm|check)\s+with\s+(?:me|us)\b/i
|
|
2444
|
+
},
|
|
2445
|
+
{
|
|
2446
|
+
kind: "asks-human",
|
|
2447
|
+
zh: /等(?:我|用户)[^\n]{0,6}(?:确认|回复|答复|决定|批)/,
|
|
2448
|
+
en: /\bwait\s+for\s+(?:my|your|user)\w*\s+(?:confirmation|approval|reply|response)\b/i
|
|
2449
|
+
},
|
|
2450
|
+
{
|
|
2451
|
+
kind: "asks-human",
|
|
2452
|
+
zh: /(?:不确定|拿不准|有疑问)(?:的话|时|就|再)?[^\n]{0,6}(?:问|确认一下|找我)/,
|
|
2453
|
+
en: /\bif\s+(?:you'?re\s+)?(?:unsure|not\s+sure|in\s+doubt)\b[^\n]{0,24}\bask\b/i
|
|
2454
|
+
},
|
|
2455
|
+
{
|
|
2456
|
+
kind: "needs-context",
|
|
2457
|
+
zh: /(?:刚才|上面(?:那|说|提到|的))/,
|
|
2458
|
+
en: /\b(?:the\s+\w+\s+above|we\s+just\s+(?:did|said|discussed))\b/i
|
|
2459
|
+
},
|
|
2460
|
+
{
|
|
2461
|
+
kind: "needs-context",
|
|
2462
|
+
zh: /我们(?:之前|刚|前面|上次)[^\n]{0,4}(?:说|聊|讨论|定|提)/,
|
|
2463
|
+
en: /\bas\s+(?:we\s+)?(?:discussed|agreed|mentioned)\b/i
|
|
2464
|
+
},
|
|
2465
|
+
{
|
|
2466
|
+
kind: "needs-context",
|
|
2467
|
+
zh: /(?:按|照|接着|继续)上(?:次|一次|轮|回)/,
|
|
2468
|
+
en: /\b(?:like|same\s+as|per)\s+last\s+time\b/i
|
|
2469
|
+
}
|
|
2470
|
+
];
|
|
2471
|
+
function lintUnattendedPrompt(prompt) {
|
|
2472
|
+
const out = [];
|
|
2473
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2474
|
+
for (const anchor of ANCHORS) {
|
|
2475
|
+
if (seen.has(anchor.kind)) continue;
|
|
2476
|
+
const hit = anchor.zh.exec(prompt) ?? anchor.en.exec(prompt);
|
|
2477
|
+
if (!hit) continue;
|
|
2478
|
+
seen.add(anchor.kind);
|
|
2479
|
+
out.push({ kind: anchor.kind, matched: hit[0] });
|
|
2480
|
+
}
|
|
2481
|
+
return out;
|
|
2482
|
+
}
|
|
2401
2483
|
// src/permission/rule-syntax.ts
|
|
2402
2484
|
var ESCAPABLE = /* @__PURE__ */ new Set(["(", ")", "\\"]);
|
|
2403
2485
|
function isEscaped(s, index) {
|
|
@@ -2524,8 +2606,8 @@ function resolvePathPattern(content, workDir) {
|
|
|
2524
2606
|
const head = wildcardAt === -1 ? raw : raw.slice(0, wildcardAt);
|
|
2525
2607
|
const tail = wildcardAt === -1 ? "" : raw.slice(wildcardAt);
|
|
2526
2608
|
const resolvedHead = base === "" && isAbsolute(head) ? resolve(head) : resolve(base || ".", head);
|
|
2527
|
-
const
|
|
2528
|
-
return `${resolvedHead}${
|
|
2609
|
+
const sep7 = head.endsWith("/") || head.endsWith("\\") ? "/" : "";
|
|
2610
|
+
return `${resolvedHead}${sep7}${tail}`;
|
|
2529
2611
|
}
|
|
2530
2612
|
function contentMatchesPath(content, operation) {
|
|
2531
2613
|
if (!operation.target) return false;
|
|
@@ -3017,6 +3099,32 @@ function validateDraft(draft, homeDir, current) {
|
|
|
3017
3099
|
(x) => x.detail ? `${x.code}\uFF08${x.detail}\uFF09` : x.code
|
|
3018
3100
|
);
|
|
3019
3101
|
}
|
|
3102
|
+
var UNATTENDED_TEXT = {
|
|
3103
|
+
"asks-human": "\u8FD9\u6BB5\u8BDD\u5728\u51B2\u7740\u4E00\u4E2A\u4EBA\u8BF4\uFF0C\u800C\u5230\u70B9\u4E86\u6CA1\u6709\u4EBA\u5728 \u2014\u2014 \u65E0\u4EBA\u503C\u5B88\u4E0B\u5BA1\u6279\u4F1A\u88AB\u76F4\u63A5\u62D2\uFF08\u8BB0\u4E00\u6761\u6B20\u6761\uFF09\u3001`ask_question` \u4E5F\u6CA1\u6709\u95EE\u51FA\u53BB\u7684\u90A3\u6761\u8DEF\u3002\u628A\u6BCF\u4E00\u4E2A\u5C94\u8DEF\u53E3\u5C31\u5730\u5B9A\u6B7B\uFF1A\u4E0E\u5176\u300C\u4E0D\u786E\u5B9A\u5C31\u95EE\u6211\u300D\uFF0C\u4E0D\u5982\u5199\u300C\u4E0D\u786E\u5B9A\u5C31\u6309 X \u529E\uFF0C\u5E76\u5728\u62A5\u544A\u91CC\u5355\u5217\u4E00\u8282\u8BF4\u660E\u4E3A\u4EC0\u4E48\u62FF\u4E0D\u51C6\u300D",
|
|
3104
|
+
"needs-context": "\u8FD9\u6BB5\u8BDD\u6307\u671B\u7740\u4E0A\u4E00\u8F6E\u7684\u4E0A\u4E0B\u6587\uFF0C\u800C**\u6BCF\u4E00\u6B21\u89E6\u53D1\u90FD\u662F\u4E00\u6761\u5168\u65B0\u4F1A\u8BDD** \u2014\u2014 \u6CA1\u6709\u300C\u521A\u624D\u300D\u3001\u6CA1\u6709\u300C\u4E0A\u9762\u90A3\u4E2A\u300D\uFF0C\u4F60\u73B0\u5728\u77E5\u9053\u7684\u4E1C\u897F\u5B83\u4E00\u4E2A\u5B57\u90FD\u4E0D\u77E5\u9053\u3002\u628A\u6307\u4EE3\u6362\u6210\u5199\u6B7B\u7684\u4E1C\u897F\uFF1A\u5177\u4F53\u7684\u8DEF\u5F84\u3001\u5177\u4F53\u7684\u5206\u652F\u540D\u3001\u5177\u4F53\u7684\u5224\u636E"
|
|
3105
|
+
};
|
|
3106
|
+
function unattendedIssues(draft) {
|
|
3107
|
+
const prompt = draft.prompt;
|
|
3108
|
+
if (prompt === void 0 || prompt === "") return [];
|
|
3109
|
+
return lintUnattendedPrompt(prompt).map((x) => `\u300C${x.matched}\u300D\u2014\u2014 ${UNATTENDED_TEXT[x.kind]}`);
|
|
3110
|
+
}
|
|
3111
|
+
function structuralNotes(draft, current) {
|
|
3112
|
+
const out = [];
|
|
3113
|
+
const workDir = draft.workDir ?? current?.workDir;
|
|
3114
|
+
if (workDir === void 0 || workDir === "") {
|
|
3115
|
+
out.push(
|
|
3116
|
+
"\u8FD9\u6761\u4EFB\u52A1**\u6CA1\u6709\u7ED1\u5DE5\u4F5C\u76EE\u5F55**\uFF0C\u6240\u4EE5\u5230\u70B9\u4E86\u5B83\u8DD1\u5728 `~/.epoch/automation/<id>/` \u91CC \u2014\u2014 \u4E0D\u662F\u4F60\u6B64\u523B\u6240\u5728\u7684\u8FD9\u4E2A\u9879\u76EE\u3002\u63D0\u793A\u8BCD\u91CC\u51E1\u662F\u6307\u7740\u7528\u6237\u4ED3\u5E93\u7684\u8DEF\u5F84\u90FD\u8981\u5199\u6210**\u7EDD\u5BF9\u8DEF\u5F84**\uFF0C\u6216\u8005\u5E72\u8106\u7ED9 `workDir`\uFF1B\u76F8\u5BF9\u8DEF\u5F84\u4F1A\u843D\u5728\u90A3\u4E2A\u4EFB\u52A1\u81EA\u5DF1\u7684\u76EE\u5F55\u4E0B\u9762\uFF08\u62A5\u544A\u843D\u90A3\u513F\u6B63\u597D\uFF0C\u522B\u7684\u591A\u534A\u4E0D\u662F\u4F60\u8981\u7684\uFF09\u3002"
|
|
3117
|
+
);
|
|
3118
|
+
}
|
|
3119
|
+
const permission2 = draft.permission ?? current?.permission;
|
|
3120
|
+
const listed = (draft.allowRules ?? current?.allowRules ?? []).length + (draft.allowTools ?? current?.allowTools ?? []).length + (draft.allowOperations ?? current?.allowOperations ?? []).length;
|
|
3121
|
+
if (permission2 !== void 0 && needsAllowlist(permission2) && listed === 0) {
|
|
3122
|
+
out.push(
|
|
3123
|
+
`\u6743\u9650\u6863\u662F ${permission2}\uFF0C\u800C\u4E09\u4EFD\u6E05\u5355\u4E00\u6761\u90FD\u6CA1\u6709 \u2014\u2014 \u8FD9\u6761\u4EFB\u52A1\u9664\u4E86\u8BFB\u4E4B\u5916**\u4EC0\u4E48\u90FD\u505A\u4E0D\u4E86**\uFF1A\u5199\u6587\u4EF6\u548C\u8DD1\u547D\u4EE4\u5728\u8FD9\u4E00\u6863\u4E0B\u90FD\u8981\u786E\u8BA4\uFF0C\u800C\u6CA1\u4EBA\u53EF\u786E\u8BA4\u65F6\u662F\u76F4\u63A5\u62D2 + \u8BB0\u4E00\u6761\u6B20\u6761\u3002\u628A\u5B83\u5230\u70B9\u4E86\u771F\u8981\u505A\u7684\u4E8B\u9010\u6761\u5217\u8FDB \`allowRules\`\uFF08\u8FDE\u843D\u62A5\u544A\u90A3\u6761 \`file_write\` \u4E00\u8D77\uFF09\u3002`
|
|
3124
|
+
);
|
|
3125
|
+
}
|
|
3126
|
+
return out;
|
|
3127
|
+
}
|
|
3020
3128
|
function summarize(draft, current) {
|
|
3021
3129
|
const lines = [];
|
|
3022
3130
|
lines.push(
|
|
@@ -3031,6 +3139,7 @@ function summarize(draft, current) {
|
|
|
3031
3139
|
`\u26A0\uFE0F \u8FD9\u4EFD\u63D0\u6848\u653E\u5BBD\u4E86\u6388\u6743\u9762\uFF1A${widening.map((k) => WIDENING_TEXT[k]).join("\uFF1B")}\u3002\u5728\u4F60\u7684\u56DE\u590D\u91CC\u4E3B\u52A8\u628A\u8FD9\u4E00\u70B9\u8BF4\u7ED9\u7528\u6237\u542C\uFF0C\u522B\u8BA9\u4ED6\u4ECE\u5361\u7247\u4E0A\u81EA\u5DF1\u53D1\u73B0\u3002`
|
|
3032
3140
|
);
|
|
3033
3141
|
}
|
|
3142
|
+
for (const note of structuralNotes(draft, current)) lines.push(`\u26A0\uFE0F ${note}`);
|
|
3034
3143
|
const cli = draftCommandLine(draft);
|
|
3035
3144
|
lines.push(
|
|
3036
3145
|
cli === void 0 ? "\uFF08\u6CA1\u6709\u754C\u9762\u7684\u5BBF\u4E3B\u4E0A\uFF1A\u6539\u4E00\u6761\u5DF2\u6709\u4EFB\u52A1\u4ECA\u5929\u53EA\u6709\u754C\u9762\u90A3\u6761\u8DEF \u2014\u2014 `epoch schedule` \u5E95\u4E0B\u6CA1\u6709\u6539\u5B57\u6BB5\u7684\u5B50\u547D\u4EE4\uFF0C\u53EA\u6709 enable / disable / rm\u3002\u522B\u7F16\u4E00\u6761 update \u51FA\u6765\u3002\uFF09" : `\u6CA1\u6709\u754C\u9762\u7684\u5BBF\u4E3B\u4E0A\uFF0C\u7B49\u4EF7\u7684\u547D\u4EE4\u884C\u662F\uFF08\u9884\u7B97\u90A3\u4E00\u683C\u8981\u7528\u6237\u81EA\u5DF1\u586B\uFF09\uFF1A
|
|
@@ -3148,6 +3257,17 @@ ${rows.map(describeRow).join("\n")}`,
|
|
|
3148
3257
|
)
|
|
3149
3258
|
);
|
|
3150
3259
|
}
|
|
3260
|
+
const unattended = unattendedIssues(draft);
|
|
3261
|
+
if (unattended.length > 0) {
|
|
3262
|
+
return Promise.resolve(
|
|
3263
|
+
say4(
|
|
3264
|
+
`\u8FD9\u4EFD\u8349\u7A3F\u4FDD\u5B58\u5F97\u4E86\uFF0C\u4F46\u5B83\u5230\u70B9\u4E86**\u8DD1\u4E0D\u51FA\u7ED3\u679C** \u2014\u2014 \u5B9A\u65F6\u4EFB\u52A1\u662F\u65E0\u4EBA\u503C\u5B88\u7684\uFF0C\u6CA1\u4EBA\u5728\u65C1\u8FB9\u56DE\u8BDD\u3002\u6539\u5B8C\u518D\u4EA4\uFF1A
|
|
3265
|
+
${unattended.map((x) => `\xB7 ${x}`).join("\n")}`,
|
|
3266
|
+
false,
|
|
3267
|
+
startedAt
|
|
3268
|
+
)
|
|
3269
|
+
);
|
|
3270
|
+
}
|
|
3151
3271
|
return Promise.resolve(say4(summarize(draft, current), true, startedAt));
|
|
3152
3272
|
}
|
|
3153
3273
|
};
|
|
@@ -4005,6 +4125,7 @@ function createSkillViewTool(deps) {
|
|
|
4005
4125
|
},
|
|
4006
4126
|
exposure: ToolExposure.Direct,
|
|
4007
4127
|
supportsParallel: true,
|
|
4128
|
+
governsOwnOutput: true,
|
|
4008
4129
|
annotations: {
|
|
4009
4130
|
readOnlyHint: true,
|
|
4010
4131
|
destructiveHint: false,
|
|
@@ -4370,7 +4491,7 @@ function available(tools) {
|
|
|
4370
4491
|
if (tools.length === 0) {
|
|
4371
4492
|
return "No deferred tools in this session \u2014 every tool is already in your tool list.";
|
|
4372
4493
|
}
|
|
4373
|
-
const names = tools.map((
|
|
4494
|
+
const names = tools.map((t78) => t78.name).sort();
|
|
4374
4495
|
const shown = names.slice(0, MAX_LISTED);
|
|
4375
4496
|
const rest = names.length - shown.length;
|
|
4376
4497
|
return `Searchable tools: ${shown.join(", ")}${rest > 0 ? ` (\u2026and ${rest} more)` : ""}.`;
|
|
@@ -4431,6 +4552,83 @@ ${lines.join("\n")}${truncated}`,
|
|
|
4431
4552
|
}
|
|
4432
4553
|
};
|
|
4433
4554
|
}
|
|
4555
|
+
function isOpenTodo(status) {
|
|
4556
|
+
return status === "pending" || status === "in_progress";
|
|
4557
|
+
}
|
|
4558
|
+
var NS = "tracker";
|
|
4559
|
+
var MIGRATIONS = [
|
|
4560
|
+
{
|
|
4561
|
+
id: "tracker-baseline",
|
|
4562
|
+
version: 1,
|
|
4563
|
+
description: "TODO \u8868\uFF08\u57FA\u7EBF\uFF09",
|
|
4564
|
+
up: (db) => {
|
|
4565
|
+
db.exec(`
|
|
4566
|
+
CREATE TABLE IF NOT EXISTS todos (
|
|
4567
|
+
id TEXT PRIMARY KEY,
|
|
4568
|
+
session_id TEXT NOT NULL,
|
|
4569
|
+
content TEXT NOT NULL,
|
|
4570
|
+
status TEXT NOT NULL DEFAULT 'pending'
|
|
4571
|
+
CHECK(status IN ('pending','in_progress','completed','cancelled')),
|
|
4572
|
+
created_at TEXT NOT NULL,
|
|
4573
|
+
updated_at TEXT NOT NULL
|
|
4574
|
+
);
|
|
4575
|
+
CREATE INDEX IF NOT EXISTS idx_todos_session ON todos(session_id);
|
|
4576
|
+
`);
|
|
4577
|
+
}
|
|
4578
|
+
},
|
|
4579
|
+
{
|
|
4580
|
+
id: "todos-reason-column",
|
|
4581
|
+
version: 2,
|
|
4582
|
+
description: "TODO \u8868\u52A0 reason \u5217\uFF08\u53D6\u6D88\u7406\u7531\uFF0C\u53EA\u6709 cancelled \u90A3\u4E00\u6863\u4F1A\u6709\uFF09",
|
|
4583
|
+
up: (db) => {
|
|
4584
|
+
db.exec(`ALTER TABLE todos ADD COLUMN reason TEXT;`);
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4587
|
+
];
|
|
4588
|
+
var TrackerDB = class {
|
|
4589
|
+
db;
|
|
4590
|
+
path;
|
|
4591
|
+
constructor(dbPath) {
|
|
4592
|
+
this.path = dbPath;
|
|
4593
|
+
this.db = openDatabase(this.path);
|
|
4594
|
+
migrate(this.db, NS, MIGRATIONS);
|
|
4595
|
+
}
|
|
4596
|
+
listBySession(sessionId) {
|
|
4597
|
+
const rows = this.db.prepare(
|
|
4598
|
+
"SELECT id, content, status, reason, created_at, updated_at FROM todos WHERE session_id = ? ORDER BY updated_at ASC"
|
|
4599
|
+
).all(sessionId);
|
|
4600
|
+
return rows.map(({ reason: reason9, ...rest }) => reason9 ? { ...rest, reason: reason9 } : rest);
|
|
4601
|
+
}
|
|
4602
|
+
countOpen(sessionId) {
|
|
4603
|
+
const row = this.db.prepare(
|
|
4604
|
+
"SELECT COUNT(*) AS n FROM todos WHERE session_id = ? AND status IN ('pending','in_progress')"
|
|
4605
|
+
).get(sessionId);
|
|
4606
|
+
return row.n;
|
|
4607
|
+
}
|
|
4608
|
+
replaceAll(sessionId, todos) {
|
|
4609
|
+
const insertStmt = this.db.prepare(
|
|
4610
|
+
"INSERT INTO todos (id, session_id, content, status, reason, created_at, updated_at) VALUES (@id, @session_id, @content, @status, @reason, @created_at, @updated_at)"
|
|
4611
|
+
);
|
|
4612
|
+
this.db.transaction(() => {
|
|
4613
|
+
this.db.prepare("DELETE FROM todos WHERE session_id = ?").run(sessionId);
|
|
4614
|
+
for (const t78 of todos) {
|
|
4615
|
+
insertStmt.run({
|
|
4616
|
+
id: t78.id,
|
|
4617
|
+
session_id: sessionId,
|
|
4618
|
+
content: t78.content,
|
|
4619
|
+
status: t78.status,
|
|
4620
|
+
reason: t78.reason || null,
|
|
4621
|
+
created_at: t78.created_at,
|
|
4622
|
+
updated_at: t78.updated_at
|
|
4623
|
+
});
|
|
4624
|
+
}
|
|
4625
|
+
})();
|
|
4626
|
+
}
|
|
4627
|
+
close() {
|
|
4628
|
+
releaseDatabase(this.path);
|
|
4629
|
+
}
|
|
4630
|
+
};
|
|
4631
|
+
// src/tools/builtin/todo.ts
|
|
4434
4632
|
function generateShortId() {
|
|
4435
4633
|
return randomBytes(3).toString("hex");
|
|
4436
4634
|
}
|
|
@@ -4443,9 +4641,9 @@ function formatTodoList(todos, pendingCount) {
|
|
|
4443
4641
|
cancelled: "\u5DF2\u53D6\u6D88"
|
|
4444
4642
|
};
|
|
4445
4643
|
lines.push(`[todo] \u72B6\u6001\u6C47\u603B: ${pendingCount} \u4E2A\u5F85\u5B8C\u6210`);
|
|
4446
|
-
for (const
|
|
4447
|
-
const label = statusLabel[
|
|
4448
|
-
lines.push(`- [${label}] ${
|
|
4644
|
+
for (const t78 of todos) {
|
|
4645
|
+
const label = statusLabel[t78.status] || t78.status;
|
|
4646
|
+
lines.push(`- [${label}] ${t78.content}${t78.reason ? ` \u2014\u2014 ${t78.reason}` : ""}`);
|
|
4449
4647
|
}
|
|
4450
4648
|
return lines.join("\n");
|
|
4451
4649
|
}
|
|
@@ -4519,14 +4717,14 @@ function createTodoTool(db) {
|
|
|
4519
4717
|
};
|
|
4520
4718
|
}
|
|
4521
4719
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4522
|
-
const existing = new Map(db.listBySession(ctx.sessionId).map((
|
|
4523
|
-
const todos = rawTodos.map((
|
|
4524
|
-
const prev =
|
|
4525
|
-
const status = isValidStatus(
|
|
4526
|
-
const reason9 = normalizeReason(status,
|
|
4720
|
+
const existing = new Map(db.listBySession(ctx.sessionId).map((t78) => [t78.id, t78]));
|
|
4721
|
+
const todos = rawTodos.map((t78) => {
|
|
4722
|
+
const prev = t78.id ? existing.get(t78.id) : void 0;
|
|
4723
|
+
const status = isValidStatus(t78.status) ? t78.status : "pending";
|
|
4724
|
+
const reason9 = normalizeReason(status, t78.reason);
|
|
4527
4725
|
return {
|
|
4528
|
-
id:
|
|
4529
|
-
content: String(
|
|
4726
|
+
id: t78.id || generateShortId(),
|
|
4727
|
+
content: String(t78.content || "").slice(0, 500),
|
|
4530
4728
|
status,
|
|
4531
4729
|
...reason9 ? { reason: reason9 } : {},
|
|
4532
4730
|
created_at: prev?.created_at || now,
|
|
@@ -4534,9 +4732,7 @@ function createTodoTool(db) {
|
|
|
4534
4732
|
};
|
|
4535
4733
|
});
|
|
4536
4734
|
db.replaceAll(ctx.sessionId, todos);
|
|
4537
|
-
const pendingCount = todos.filter(
|
|
4538
|
-
(t76) => t76.status === "pending" || t76.status === "in_progress"
|
|
4539
|
-
).length;
|
|
4735
|
+
const pendingCount = todos.filter((t78) => isOpenTodo(t78.status)).length;
|
|
4540
4736
|
return {
|
|
4541
4737
|
success: true,
|
|
4542
4738
|
output: formatTodoList(todos, pendingCount),
|
|
@@ -4871,7 +5067,7 @@ function intersectNames(base, incoming) {
|
|
|
4871
5067
|
if (!base) return incoming ? [...incoming] : void 0;
|
|
4872
5068
|
if (!incoming) return [...base];
|
|
4873
5069
|
const baseSet = new Set(base);
|
|
4874
|
-
return incoming.filter((
|
|
5070
|
+
return incoming.filter((t78) => baseSet.has(t78));
|
|
4875
5071
|
}
|
|
4876
5072
|
function findRole(roles, name) {
|
|
4877
5073
|
return roles.find((r) => r.name === name);
|
|
@@ -4957,7 +5153,7 @@ function roleScopedProvider(base, role, blocked) {
|
|
|
4957
5153
|
return allow ? allow.has(name) : true;
|
|
4958
5154
|
};
|
|
4959
5155
|
return {
|
|
4960
|
-
list: () => base.list().filter((
|
|
5156
|
+
list: () => base.list().filter((t78) => permitted(t78.name)),
|
|
4961
5157
|
get: (name) => permitted(name) ? base.get(name) : void 0
|
|
4962
5158
|
};
|
|
4963
5159
|
}
|
|
@@ -4984,7 +5180,7 @@ function createRoleScope(base, sessionRole, blocked) {
|
|
|
4984
5180
|
}
|
|
4985
5181
|
function unknownRoleTools(base, role, blocked) {
|
|
4986
5182
|
if (!role?.tools) return [];
|
|
4987
|
-
const available2 = new Set(base.list().map((
|
|
5183
|
+
const available2 = new Set(base.list().map((t78) => t78.name));
|
|
4988
5184
|
const blockedSet = new Set(blocked);
|
|
4989
5185
|
return role.tools.filter((name) => !available2.has(name) && !blockedSet.has(name));
|
|
4990
5186
|
}
|
|
@@ -5320,7 +5516,7 @@ function createToolScope(base) {
|
|
|
5320
5516
|
const permitted = (name) => allow ? allow.has(name) : true;
|
|
5321
5517
|
return {
|
|
5322
5518
|
provider: {
|
|
5323
|
-
list: () => base.list().filter((
|
|
5519
|
+
list: () => base.list().filter((t78) => permitted(t78.name)),
|
|
5324
5520
|
get: (name) => permitted(name) ? base.get(name) : void 0
|
|
5325
5521
|
},
|
|
5326
5522
|
enter: (next) => {
|
|
@@ -5490,7 +5686,7 @@ function scanHooks(dir, issues) {
|
|
|
5490
5686
|
return [...parsed.entries].map(([type, list]) => ({
|
|
5491
5687
|
type,
|
|
5492
5688
|
count: list.reduce((sum, entry) => sum + entry.hooks.length, 0)
|
|
5493
|
-
})).filter((
|
|
5689
|
+
})).filter((t78) => t78.count > 0).sort((a, b) => a.type.localeCompare(b.type));
|
|
5494
5690
|
}
|
|
5495
5691
|
function scanMcp(dir, plugin, issues) {
|
|
5496
5692
|
const path = join(dir, PLUGIN_LAYOUT.mcp);
|
|
@@ -5568,11 +5764,11 @@ function firstLine(err) {
|
|
|
5568
5764
|
return message.split("\n")[0] ?? t("plugin_inventory.unknown_error");
|
|
5569
5765
|
}
|
|
5570
5766
|
var PLUGIN_MANIFEST_FILE = "epoch-plugin.json";
|
|
5571
|
-
var PLUGIN_NAME_PATTERN = /^[a-z0-9][a-z0-
|
|
5767
|
+
var PLUGIN_NAME_PATTERN = /^[a-z0-9][a-z0-9_-]*$/;
|
|
5572
5768
|
function isValidPluginName(name) {
|
|
5573
5769
|
return PLUGIN_NAME_PATTERN.test(name);
|
|
5574
5770
|
}
|
|
5575
|
-
var
|
|
5771
|
+
var PluginAuthorSchema = z.object({
|
|
5576
5772
|
name: z.string().min(1),
|
|
5577
5773
|
email: z.string().min(1).optional(),
|
|
5578
5774
|
url: z.string().min(1).optional()
|
|
@@ -5581,7 +5777,7 @@ var ManifestSchema = z.object({
|
|
|
5581
5777
|
name: z.string().min(1),
|
|
5582
5778
|
version: z.string().min(1),
|
|
5583
5779
|
description: z.string().min(1).optional(),
|
|
5584
|
-
author:
|
|
5780
|
+
author: PluginAuthorSchema.optional(),
|
|
5585
5781
|
epochVersion: z.string().min(1).optional(),
|
|
5586
5782
|
homepage: z.string().min(1).optional(),
|
|
5587
5783
|
keywords: z.array(z.string().min(1)).optional()
|
|
@@ -5750,7 +5946,7 @@ var RecordSchema = z.object({
|
|
|
5750
5946
|
name: z.string().regex(PLUGIN_NAME_PATTERN),
|
|
5751
5947
|
version: z.string().min(1),
|
|
5752
5948
|
source: z.string().min(1),
|
|
5753
|
-
sourceType: z.enum(["local", "git", "npm", "archive"]),
|
|
5949
|
+
sourceType: z.enum(["local", "git", "npm", "archive", "host"]),
|
|
5754
5950
|
path: z.string().min(1),
|
|
5755
5951
|
linked: z.boolean(),
|
|
5756
5952
|
enabled: z.boolean(),
|
|
@@ -6148,6 +6344,144 @@ function flattenSingleRoot(dest) {
|
|
|
6148
6344
|
}
|
|
6149
6345
|
rmSync(inner, { recursive: true, force: true });
|
|
6150
6346
|
}
|
|
6347
|
+
var PACK_EXCLUDES = [
|
|
6348
|
+
".git",
|
|
6349
|
+
"node_modules",
|
|
6350
|
+
".DS_Store",
|
|
6351
|
+
"__pycache__"
|
|
6352
|
+
];
|
|
6353
|
+
var SECRET_LOOKING = [
|
|
6354
|
+
/^\.env(\..+)?$/i,
|
|
6355
|
+
/\.pem$/i,
|
|
6356
|
+
/\.key$/i,
|
|
6357
|
+
/^id_(rsa|dsa|ecdsa|ed25519)$/i
|
|
6358
|
+
];
|
|
6359
|
+
var PACK_MAX_BYTES = 50 * 1024 * 1024;
|
|
6360
|
+
var PACK_MAX_FILES = 2e3;
|
|
6361
|
+
function planPack(dir, limits = {}) {
|
|
6362
|
+
const read = readPluginManifest(dir);
|
|
6363
|
+
if (!read.manifest) {
|
|
6364
|
+
const detail = read.issues.map((i) => `${i.path}: ${i.message}`).join("\n");
|
|
6365
|
+
return { ok: false, reason: t("plugin_pack.not_a_plugin", { detail: `
|
|
6366
|
+
${detail}` }) };
|
|
6367
|
+
}
|
|
6368
|
+
const maxFiles = limits.maxFiles ?? PACK_MAX_FILES;
|
|
6369
|
+
const maxBytes = limits.maxBytes ?? PACK_MAX_BYTES;
|
|
6370
|
+
const files = [];
|
|
6371
|
+
const skippedLinks = [];
|
|
6372
|
+
let totalBytes = 0;
|
|
6373
|
+
const walk3 = (current) => {
|
|
6374
|
+
let entries;
|
|
6375
|
+
try {
|
|
6376
|
+
entries = readdirSync(current, { withFileTypes: true });
|
|
6377
|
+
} catch (err) {
|
|
6378
|
+
return t("plugin_pack.read_failed", { path: current, reason: firstLine3(err) });
|
|
6379
|
+
}
|
|
6380
|
+
for (const entry of entries) {
|
|
6381
|
+
if (PACK_EXCLUDES.includes(entry.name)) continue;
|
|
6382
|
+
const abs = join(current, entry.name);
|
|
6383
|
+
const rel = relative(dir, abs).split(sep).join("/");
|
|
6384
|
+
if (entry.isSymbolicLink()) {
|
|
6385
|
+
skippedLinks.push(rel);
|
|
6386
|
+
continue;
|
|
6387
|
+
}
|
|
6388
|
+
if (entry.isDirectory()) {
|
|
6389
|
+
if (isVirtualenv(abs)) continue;
|
|
6390
|
+
const failed = walk3(abs);
|
|
6391
|
+
if (failed) return failed;
|
|
6392
|
+
continue;
|
|
6393
|
+
}
|
|
6394
|
+
if (!entry.isFile()) continue;
|
|
6395
|
+
if (rel.includes("\n") || rel.includes("\r")) {
|
|
6396
|
+
return t("plugin_pack.bad_name", { path: rel });
|
|
6397
|
+
}
|
|
6398
|
+
let stat5;
|
|
6399
|
+
try {
|
|
6400
|
+
stat5 = statSync(abs);
|
|
6401
|
+
} catch (err) {
|
|
6402
|
+
return t("plugin_pack.read_failed", { path: abs, reason: firstLine3(err) });
|
|
6403
|
+
}
|
|
6404
|
+
files.push({ path: rel, bytes: stat5.size, mtimeMs: Math.floor(stat5.mtimeMs) });
|
|
6405
|
+
totalBytes += stat5.size;
|
|
6406
|
+
if (files.length > maxFiles) {
|
|
6407
|
+
return t("plugin_pack.too_many_files", { max: maxFiles });
|
|
6408
|
+
}
|
|
6409
|
+
if (totalBytes > maxBytes) {
|
|
6410
|
+
return t("plugin_pack.too_large", { max: Math.floor(maxBytes / (1024 * 1024)) });
|
|
6411
|
+
}
|
|
6412
|
+
}
|
|
6413
|
+
return void 0;
|
|
6414
|
+
};
|
|
6415
|
+
const failure = walk3(dir);
|
|
6416
|
+
if (failure) return { ok: false, reason: failure };
|
|
6417
|
+
if (files.length === 0) return { ok: false, reason: t("plugin_pack.empty") };
|
|
6418
|
+
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
6419
|
+
const suspicious = files.map((f) => f.path).filter((p) => SECRET_LOOKING.some((re) => re.test(p.split("/").pop() ?? "")));
|
|
6420
|
+
return {
|
|
6421
|
+
ok: true,
|
|
6422
|
+
plan: { dir, manifest: read.manifest, files, totalBytes, suspicious, skippedLinks }
|
|
6423
|
+
};
|
|
6424
|
+
}
|
|
6425
|
+
function packFileName(manifest) {
|
|
6426
|
+
return `${manifest.name}-${manifest.version}.zip`;
|
|
6427
|
+
}
|
|
6428
|
+
async function packPlugin(plan, destDir) {
|
|
6429
|
+
const rel = relative(plan.dir, destDir);
|
|
6430
|
+
const outside = rel !== "" && (rel.startsWith("..") || isAbsolute(rel));
|
|
6431
|
+
if (!outside) {
|
|
6432
|
+
return { ok: false, reason: t("plugin_pack.dest_inside", { dir: plan.dir }) };
|
|
6433
|
+
}
|
|
6434
|
+
const listFile = join(destDir, "epoch-pack-files.txt");
|
|
6435
|
+
const archivePath = join(destDir, packFileName(plan.manifest));
|
|
6436
|
+
try {
|
|
6437
|
+
writeFileSync(listFile, `${plan.files.map((f) => f.path).join("\n")}
|
|
6438
|
+
`, "utf-8");
|
|
6439
|
+
} catch (err) {
|
|
6440
|
+
return {
|
|
6441
|
+
ok: false,
|
|
6442
|
+
reason: t("plugin_pack.read_failed", { path: listFile, reason: firstLine3(err) })
|
|
6443
|
+
};
|
|
6444
|
+
}
|
|
6445
|
+
let result;
|
|
6446
|
+
try {
|
|
6447
|
+
result = await runCommand(resolveTar(), [
|
|
6448
|
+
"-c",
|
|
6449
|
+
"-f",
|
|
6450
|
+
archivePath,
|
|
6451
|
+
"--format",
|
|
6452
|
+
"zip",
|
|
6453
|
+
"-C",
|
|
6454
|
+
plan.dir,
|
|
6455
|
+
"-T",
|
|
6456
|
+
listFile
|
|
6457
|
+
]);
|
|
6458
|
+
} finally {
|
|
6459
|
+
rmSync(listFile, { force: true });
|
|
6460
|
+
}
|
|
6461
|
+
if (!result.ok) {
|
|
6462
|
+
return {
|
|
6463
|
+
ok: false,
|
|
6464
|
+
reason: t("plugin_pack.tar_failed", { detail: result.detail }) + (result.notFound ? t("plugin_archive.no_tar_hint") : "")
|
|
6465
|
+
};
|
|
6466
|
+
}
|
|
6467
|
+
let bytes;
|
|
6468
|
+
try {
|
|
6469
|
+
bytes = statSync(archivePath).size;
|
|
6470
|
+
} catch (err) {
|
|
6471
|
+
return {
|
|
6472
|
+
ok: false,
|
|
6473
|
+
reason: t("plugin_pack.read_failed", { path: archivePath, reason: firstLine3(err) })
|
|
6474
|
+
};
|
|
6475
|
+
}
|
|
6476
|
+
return { ok: true, archivePath, bytes };
|
|
6477
|
+
}
|
|
6478
|
+
function isVirtualenv(dir) {
|
|
6479
|
+
return existsSync(join(dir, "pyvenv.cfg"));
|
|
6480
|
+
}
|
|
6481
|
+
function firstLine3(err) {
|
|
6482
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6483
|
+
return message.split("\n")[0] ?? message;
|
|
6484
|
+
}
|
|
6151
6485
|
var NPM_SPEC = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*(?:@[a-zA-Z0-9.^~*+-]+)?$/;
|
|
6152
6486
|
var REMOTE_URL = /^https?:\/\/[^\s'"\\^|<>]+$/;
|
|
6153
6487
|
function resolveSource(raw) {
|
|
@@ -6177,6 +6511,11 @@ function resolveSource(raw) {
|
|
|
6177
6511
|
if (!NPM_SPEC.test(pkg)) return void 0;
|
|
6178
6512
|
return { type: "npm", raw: input, target: pkg };
|
|
6179
6513
|
}
|
|
6514
|
+
if (input.startsWith("host:")) {
|
|
6515
|
+
const opaque = input.slice("host:".length).trim();
|
|
6516
|
+
if (!opaque) return void 0;
|
|
6517
|
+
return { type: "host", raw: input, target: opaque };
|
|
6518
|
+
}
|
|
6180
6519
|
if (input.startsWith("https://") || input.startsWith("http://")) {
|
|
6181
6520
|
const [url = "", fragment] = input.split("#", 2);
|
|
6182
6521
|
if (!REMOTE_URL.test(url)) return void 0;
|
|
@@ -6192,7 +6531,11 @@ function resolveSource(raw) {
|
|
|
6192
6531
|
}
|
|
6193
6532
|
return { type: "git", raw: input, target: url };
|
|
6194
6533
|
}
|
|
6195
|
-
|
|
6534
|
+
const abs = isAbsolute(input) ? input : resolve(input);
|
|
6535
|
+
if (isArchiveUrl(abs)) {
|
|
6536
|
+
return { type: "archive", raw: input, target: abs, localFile: true };
|
|
6537
|
+
}
|
|
6538
|
+
return { type: "local", raw: input, target: abs };
|
|
6196
6539
|
}
|
|
6197
6540
|
function isArchiveUrl(url) {
|
|
6198
6541
|
const path = url.split("?")[0] ?? "";
|
|
@@ -6237,7 +6580,8 @@ async function runInstall(resolved, staging, opts) {
|
|
|
6237
6580
|
return { ok: false, reason: t("plugin_install.manifest_unusable", { detail: `
|
|
6238
6581
|
${detail}` }) };
|
|
6239
6582
|
}
|
|
6240
|
-
const
|
|
6583
|
+
const taken = new PluginStore(opts.statePath).read().records.find((r) => r.name === manifest.name);
|
|
6584
|
+
const conflict = taken && taken.name !== opts.replace ? taken : void 0;
|
|
6241
6585
|
const current = opts.epochVersion ?? currentEpochVersion();
|
|
6242
6586
|
const version = checkEpochVersion(manifest.epochVersion, current);
|
|
6243
6587
|
const versionUnmet = !version.ok && manifest.epochVersion ? { required: manifest.epochVersion, current, reason: version.reason } : void 0;
|
|
@@ -6255,7 +6599,7 @@ ${detail}` }) };
|
|
|
6255
6599
|
reason: t("plugin_install.renamed", { found: manifest.name, expected: opts.replace })
|
|
6256
6600
|
};
|
|
6257
6601
|
}
|
|
6258
|
-
if (conflict
|
|
6602
|
+
if (conflict) {
|
|
6259
6603
|
return {
|
|
6260
6604
|
ok: false,
|
|
6261
6605
|
preview: preview3,
|
|
@@ -6320,8 +6664,51 @@ async function fetchInto(source, staging, opts) {
|
|
|
6320
6664
|
if (source.type === "local") return fetchLocal(source);
|
|
6321
6665
|
if (source.type === "git") return fetchGit(source, staging, opts);
|
|
6322
6666
|
if (source.type === "npm") return fetchNpm(source, staging, opts);
|
|
6667
|
+
if (source.type === "host") return fetchFromHost(source, staging, opts);
|
|
6668
|
+
if (source.localFile) return fetchLocalArchive(source, staging);
|
|
6323
6669
|
return fetchArchive(source, staging, opts);
|
|
6324
6670
|
}
|
|
6671
|
+
async function fetchLocalArchive(source, staging) {
|
|
6672
|
+
if (!existsSync(source.target)) {
|
|
6673
|
+
return { ok: false, reason: t("plugin_install.archive_missing", { path: source.target }) };
|
|
6674
|
+
}
|
|
6675
|
+
mkdirSync(staging, { recursive: true });
|
|
6676
|
+
return extractArchive(source.target, staging);
|
|
6677
|
+
}
|
|
6678
|
+
async function fetchFromHost(source, staging, opts) {
|
|
6679
|
+
const ask = opts.fetchFromHost;
|
|
6680
|
+
if (!ask) {
|
|
6681
|
+
return { ok: false, reason: t("plugin_install.host_fetch_unavailable") };
|
|
6682
|
+
}
|
|
6683
|
+
opts.onProgress?.(t("plugin_install.host_fetching", { target: source.target }));
|
|
6684
|
+
mkdirSync(staging, { recursive: true });
|
|
6685
|
+
return withDownloadDir(async (dl) => {
|
|
6686
|
+
let answer;
|
|
6687
|
+
try {
|
|
6688
|
+
answer = await ask({ target: source.target, destDir: dl });
|
|
6689
|
+
} catch (err) {
|
|
6690
|
+
return {
|
|
6691
|
+
ok: false,
|
|
6692
|
+
reason: t("plugin_install.host_fetch_threw", {
|
|
6693
|
+
reason: (err instanceof Error ? err.message : String(err)).split("\n")[0] ?? ""
|
|
6694
|
+
})
|
|
6695
|
+
};
|
|
6696
|
+
}
|
|
6697
|
+
if (!answer.ok) {
|
|
6698
|
+
return {
|
|
6699
|
+
ok: false,
|
|
6700
|
+
reason: answer.reason ?? t("plugin_install.host_fetch_failed", { target: source.target })
|
|
6701
|
+
};
|
|
6702
|
+
}
|
|
6703
|
+
if (!existsSync(answer.archivePath)) {
|
|
6704
|
+
return {
|
|
6705
|
+
ok: false,
|
|
6706
|
+
reason: t("plugin_install.host_fetch_no_file", { path: answer.archivePath })
|
|
6707
|
+
};
|
|
6708
|
+
}
|
|
6709
|
+
return extractArchive(answer.archivePath, staging);
|
|
6710
|
+
});
|
|
6711
|
+
}
|
|
6325
6712
|
function fetchLocal(source) {
|
|
6326
6713
|
if (!existsSync(source.target)) {
|
|
6327
6714
|
return { ok: false, reason: t("plugin_install.local_missing", { path: source.target }) };
|
|
@@ -6468,18 +6855,44 @@ async function updatePlugin(name, opts) {
|
|
|
6468
6855
|
});
|
|
6469
6856
|
}
|
|
6470
6857
|
var MARKETPLACE_FILE = "epoch-marketplace.json";
|
|
6471
|
-
var NAME_PATTERN =
|
|
6858
|
+
var NAME_PATTERN = PLUGIN_NAME_PATTERN;
|
|
6472
6859
|
var EntrySchema = z.object({
|
|
6473
6860
|
name: z.string().regex(NAME_PATTERN),
|
|
6474
6861
|
description: z.string().min(1).optional(),
|
|
6475
6862
|
source: z.string().min(1),
|
|
6476
6863
|
category: z.string().min(1).optional(),
|
|
6477
|
-
keywords: z.array(z.string().min(1)).optional()
|
|
6864
|
+
keywords: z.array(z.string().min(1)).optional(),
|
|
6865
|
+
author: PluginAuthorSchema.optional(),
|
|
6866
|
+
downloads: z.number().int().nonnegative().optional(),
|
|
6867
|
+
likes: z.number().int().nonnegative().optional(),
|
|
6868
|
+
displayName: z.string().min(1).optional(),
|
|
6869
|
+
icon: z.string().min(1).optional(),
|
|
6870
|
+
version: z.string().min(1).optional(),
|
|
6871
|
+
changelog: z.string().min(1).optional(),
|
|
6872
|
+
size: z.number().int().positive().optional(),
|
|
6873
|
+
homepage: z.string().min(1).optional(),
|
|
6874
|
+
visibility: z.enum(["private", "public"]).optional().catch(void 0),
|
|
6875
|
+
status: z.enum(["listed", "reviewing", "rejected", "offline"]).optional().catch(void 0)
|
|
6876
|
+
});
|
|
6877
|
+
var MARKETPLACE_DISPLAY_FIELDS = [
|
|
6878
|
+
"status",
|
|
6879
|
+
"downloads",
|
|
6880
|
+
"likes",
|
|
6881
|
+
"author",
|
|
6882
|
+
"category",
|
|
6883
|
+
"visibility",
|
|
6884
|
+
"icon",
|
|
6885
|
+
"homepage",
|
|
6886
|
+
"size"
|
|
6887
|
+
];
|
|
6888
|
+
var DisplaySchema = z.object({
|
|
6889
|
+
fields: z.array(z.string().min(1)).optional()
|
|
6478
6890
|
});
|
|
6479
6891
|
var CatalogSchema = z.object({
|
|
6480
6892
|
name: z.string().regex(NAME_PATTERN),
|
|
6481
6893
|
description: z.string().min(1).optional(),
|
|
6482
6894
|
owner: z.object({ name: z.string().min(1), url: z.string().min(1).optional() }).optional(),
|
|
6895
|
+
display: DisplaySchema.optional(),
|
|
6483
6896
|
plugins: z.array(EntrySchema)
|
|
6484
6897
|
});
|
|
6485
6898
|
var RecordSchema2 = z.object({
|
|
@@ -6492,6 +6905,16 @@ var StateSchema2 = z.object({
|
|
|
6492
6905
|
version: z.literal(1).optional(),
|
|
6493
6906
|
marketplaces: z.array(RecordSchema2)
|
|
6494
6907
|
});
|
|
6908
|
+
function projectEntryForDisplay(entry, display) {
|
|
6909
|
+
const allowed = display?.fields;
|
|
6910
|
+
if (allowed === void 0) return entry;
|
|
6911
|
+
const keep = new Set(allowed);
|
|
6912
|
+
const out = { ...entry };
|
|
6913
|
+
for (const field of MARKETPLACE_DISPLAY_FIELDS) {
|
|
6914
|
+
if (!keep.has(field)) delete out[field];
|
|
6915
|
+
}
|
|
6916
|
+
return out;
|
|
6917
|
+
}
|
|
6495
6918
|
function readMarketplaces(statePath) {
|
|
6496
6919
|
if (!existsSync(statePath)) return { records: [], issues: [] };
|
|
6497
6920
|
let raw;
|
|
@@ -6580,10 +7003,15 @@ function searchMarketplaces(keyword, statePath) {
|
|
|
6580
7003
|
entry.name,
|
|
6581
7004
|
entry.description ?? "",
|
|
6582
7005
|
entry.category ?? "",
|
|
7006
|
+
entry.author?.name ?? "",
|
|
6583
7007
|
...entry.keywords ?? []
|
|
6584
7008
|
].join(" ").toLowerCase();
|
|
6585
7009
|
if (needle && !haystack.includes(needle)) continue;
|
|
6586
|
-
hits.push({
|
|
7010
|
+
hits.push({
|
|
7011
|
+
marketplace: record.name,
|
|
7012
|
+
entry: projectEntryForDisplay(entry, record.catalog.display),
|
|
7013
|
+
ref: `${record.name}/${entry.name}`
|
|
7014
|
+
});
|
|
6587
7015
|
}
|
|
6588
7016
|
}
|
|
6589
7017
|
return hits;
|
|
@@ -6944,14 +7372,14 @@ var CheckpointStore = class {
|
|
|
6944
7372
|
}
|
|
6945
7373
|
async enforceTotalSize(protectSession, protectTurnIndex) {
|
|
6946
7374
|
const turns = await this.collectTurns();
|
|
6947
|
-
let total = turns.reduce((sum,
|
|
7375
|
+
let total = turns.reduce((sum, t78) => sum + t78.bytes, 0);
|
|
6948
7376
|
if (total <= this.maxTotalBytes) return;
|
|
6949
7377
|
turns.sort((a, b) => a.createdAt - b.createdAt);
|
|
6950
|
-
for (const
|
|
7378
|
+
for (const t78 of turns) {
|
|
6951
7379
|
if (total <= this.maxTotalBytes) return;
|
|
6952
|
-
if (
|
|
6953
|
-
await this.removeTurn(
|
|
6954
|
-
total -=
|
|
7380
|
+
if (t78.sessionId === protectSession && t78.turnIndex === protectTurnIndex) continue;
|
|
7381
|
+
await this.removeTurn(t78.sessionId, t78.turnIndex);
|
|
7382
|
+
total -= t78.bytes;
|
|
6955
7383
|
}
|
|
6956
7384
|
}
|
|
6957
7385
|
async collectTurns() {
|
|
@@ -7472,7 +7900,8 @@ function addUsage(a, b) {
|
|
|
7472
7900
|
const cacheHit = (a.cacheHitTokens ?? 0) + (b.cacheHitTokens ?? 0);
|
|
7473
7901
|
const cacheWrite = (a.cacheWriteTokens ?? 0) + (b.cacheWriteTokens ?? 0);
|
|
7474
7902
|
const reasoning = (a.reasoningTokens ?? 0) + (b.reasoningTokens ?? 0);
|
|
7475
|
-
const
|
|
7903
|
+
const reported = a.totalTokens !== void 0 || b.totalTokens !== void 0;
|
|
7904
|
+
const total = reported ? totalUsageTokens(a) + totalUsageTokens(b) : 0;
|
|
7476
7905
|
return {
|
|
7477
7906
|
inputTokens: a.inputTokens + b.inputTokens,
|
|
7478
7907
|
outputTokens: a.outputTokens + b.outputTokens,
|
|
@@ -7754,10 +8183,13 @@ function systemPromptSegments(params) {
|
|
|
7754
8183
|
`## \u89C4\u5219`,
|
|
7755
8184
|
"- \u7528\u4E2D\u6587\u56DE\u590D\u7528\u6237",
|
|
7756
8185
|
"- \u6D89\u53CA\u6587\u4EF6\u64CD\u4F5C\u65F6\uFF0C\u5148\u7528\u5DE5\u5177\u83B7\u53D6\u4FE1\u606F\u518D\u884C\u52A8",
|
|
7757
|
-
"- \u4E0D\u786E\u5B9A\
|
|
8186
|
+
"- \u4E8B\u5B9E\u4E0D\u786E\u5B9A\u5C31\u7528\u5DE5\u5177\u53BB\u67E5\uFF0C\u67E5\u4E0D\u5230\u5C31\u8BF4\u4E0D\u77E5\u9053 \u2014\u2014 \u522B\u7F16",
|
|
8187
|
+
"- \u505A\u6CD5\u6709\u4E24\u79CD\u5408\u7406\u8D70\u6CD5\u3001\u4E14\u9009\u9519\u4E86\u8981\u767D\u5E72\u65F6\uFF0C\u624D\u505C\u4E0B\u6765\u95EE\u4E00\u53E5\uFF1B\u6D3B\u591A\u3001\u8981\u82B1\u65F6\u95F4\u3001\u6B65\u9AA4\u91CD\u590D\u90FD\u4E0D\u662F\u8981\u95EE\u7684\u7406\u7531",
|
|
7758
8188
|
"- file_patch \u6BD4 file_write \u66F4\u5B89\u5168\uFF0C\u4F18\u5148\u4F7F\u7528",
|
|
7759
8189
|
"- \u540C\u65F6\u8BFB\u53D6\u591A\u4E2A\u6587\u4EF6\u65F6\u7528 file_multi_read",
|
|
7760
|
-
"- \
|
|
8190
|
+
"- \u7528\u6237\u7ED9\u4E86\u8303\u56F4\u5C31\u628A\u90A3\u4E2A\u8303\u56F4\u505A\u5B8C\uFF1A\u8BF4 100 \u6761\u5C31\u662F 100 \u6761\u3002\u505A 3 \u6761\u518D\u95EE\u300C\u8981\u4E0D\u8981\u7EE7\u7EED\u300D\u662F\u6CA1\u505A\u5B8C\uFF0C\u4E0D\u662F\u8C28\u614E",
|
|
8191
|
+
"- \u8BE5\u7701\u7684\u662F\u91CD\u590D\u8C03\u540C\u4E00\u6B21\u5DE5\u5177\uFF08\u540C\u6837\u7684\u53C2\u6570\u95EE\u7B2C\u4E8C\u904D\uFF09\uFF0C\u4E0D\u662F\u5C11\u505A\u51E0\u4EF6\u4E8B \u2014\u2014 \u5DE5\u5177\u8C03\u7528\u6B21\u6570\u4ECE\u6765\u4E0D\u662F\u8981\u538B\u7684\u6307\u6807",
|
|
8192
|
+
"- \u8BF4\u4E86\u4E0B\u4E00\u6B65\u5C31\u5F53\u573A\u505A\uFF1A\u300C\u6211\u5148\u53BB\u67E5\u4E00\u4E0B\u300D\u300Clet me \u68C0\u67E5\u4E00\u4E0B\u300D\u8FD9\u79CD\u8BDD\u540E\u9762\u5FC5\u987B\u7D27\u8DDF\u4E00\u6B21\u5DE5\u5177\u8C03\u7528\uFF0C\u522B\u62FF\u5B83\u5F53\u4E00\u8F6E\u56DE\u590D\u7684\u7ED3\u5C3E",
|
|
7761
8193
|
"- \u88AB\u95EE\u5230\u4F60\u662F\u8C01 / \u4F60\u662F\u4EC0\u4E48\uFF1A\u8BF4\u4F60\u662F\u4E00\u4E2A\u8DD1\u5728\u7528\u6237\u672C\u673A\u7684\u667A\u80FD\u4F53\uFF0C\u518D\u8BF4\u6E05\u695A\u4F60\u80FD\u505A\u4EC0\u4E48\u3002\u4E0D\u62A5\u54C1\u724C\u540D\uFF0C\u4E5F\u522B\u81EA\u79F0\u662F\u522B\u5BB6\u4EA7\u54C1\u6216\u5382\u5546\u7684\u5B98\u65B9\u52A9\u624B \u2014\u2014 \u540E\u8005\u662F\u5047\u8BDD",
|
|
7762
8194
|
"- \u7528\u6237\u76F4\u63A5\u95EE\u5E95\u5C42\u662F\u4EC0\u4E48\u6A21\u578B\uFF1A\u7167\u5B9E\u8BF4\uFF08`## \u73AF\u5883` \u91CC\u90A3\u884C\u300C\u5F53\u524D\u6A21\u578B\u300D\u5C31\u662F\uFF09\uFF0C\u4F46\u522B\u628A\u5B83\u5F53\u6210\u4F60\u7684\u8EAB\u4EFD"
|
|
7763
8195
|
].join("\n")
|
|
@@ -8903,6 +9335,79 @@ ${this.describeWarning(status)}
|
|
|
8903
9335
|
});
|
|
8904
9336
|
}
|
|
8905
9337
|
};
|
|
9338
|
+
// src/provider/errors.ts
|
|
9339
|
+
var ProviderError = class extends Error {
|
|
9340
|
+
constructor(message, code, recoverable, recovery = "abort") {
|
|
9341
|
+
super(message);
|
|
9342
|
+
this.code = code;
|
|
9343
|
+
this.recoverable = recoverable;
|
|
9344
|
+
this.recovery = recovery;
|
|
9345
|
+
this.name = "ProviderError";
|
|
9346
|
+
}
|
|
9347
|
+
code;
|
|
9348
|
+
recoverable;
|
|
9349
|
+
recovery;
|
|
9350
|
+
};
|
|
9351
|
+
var PROVIDER_NO_CREDENTIAL = 1001;
|
|
9352
|
+
var PROVIDER_ABORTED = 1060;
|
|
9353
|
+
var PROVIDER_CONTEXT_OVERFLOW = 1030;
|
|
9354
|
+
function isContextOverflow(err) {
|
|
9355
|
+
if (err instanceof ProviderError) return err.code === PROVIDER_CONTEXT_OVERFLOW;
|
|
9356
|
+
return classifyProviderError(err).code === PROVIDER_CONTEXT_OVERFLOW;
|
|
9357
|
+
}
|
|
9358
|
+
function isAbortError(err) {
|
|
9359
|
+
for (let cur = err, depth = 0; cur && depth < 5; depth++) {
|
|
9360
|
+
if (typeof cur !== "object") return false;
|
|
9361
|
+
if (cur.name === "AbortError") return true;
|
|
9362
|
+
cur = cur.cause;
|
|
9363
|
+
}
|
|
9364
|
+
return false;
|
|
9365
|
+
}
|
|
9366
|
+
function extractStatus(err) {
|
|
9367
|
+
if (!err || typeof err !== "object") return void 0;
|
|
9368
|
+
const e = err;
|
|
9369
|
+
for (const candidate of [e.statusCode, e.status, e.response?.status]) {
|
|
9370
|
+
if (typeof candidate === "number" && candidate >= 100 && candidate < 600) return candidate;
|
|
9371
|
+
}
|
|
9372
|
+
return void 0;
|
|
9373
|
+
}
|
|
9374
|
+
function extractStatusFromMessage(msg) {
|
|
9375
|
+
const leading = msg.match(/^\s*\[?(4\d{2}|5\d{2})\b/);
|
|
9376
|
+
if (leading?.[1]) return Number(leading[1]);
|
|
9377
|
+
const labelled = msg.match(/(?:status(?:\s*code)?|http|code)\D{0,3}(4\d{2}|5\d{2})\b/i);
|
|
9378
|
+
if (labelled?.[1]) return Number(labelled[1]);
|
|
9379
|
+
return void 0;
|
|
9380
|
+
}
|
|
9381
|
+
function classifyProviderError(err) {
|
|
9382
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
9383
|
+
const status = extractStatus(err);
|
|
9384
|
+
if (isAbortError(err)) return { code: PROVIDER_ABORTED, recovery: "abort" };
|
|
9385
|
+
if (/context.?length|context.?window|maximum.*token|token.?limit|too.?many.?tokens|prompt.*too.?long|reduce.*(length|tokens)/i.test(
|
|
9386
|
+
msg
|
|
9387
|
+
))
|
|
9388
|
+
return { code: PROVIDER_CONTEXT_OVERFLOW, recovery: "abort" };
|
|
9389
|
+
if (/invalid.*api.?key|unauthorized|authentication|permission.?denied|forbidden/i.test(msg))
|
|
9390
|
+
return { code: 1001, recovery: "rotate_credential" };
|
|
9391
|
+
if (/billing|quota.*exceeded|insufficient.*(balance|credit)|payment.?required/i.test(msg))
|
|
9392
|
+
return { code: 1012, recovery: "switch_provider" };
|
|
9393
|
+
if (/rate.?limit|too.?many.?requests/i.test(msg)) return { code: 1010, recovery: "retry" };
|
|
9394
|
+
if (/model.?not.?found|model.*does.*not.*exist|unknown.?model|no.?such.?model/i.test(msg))
|
|
9395
|
+
return { code: 1040, recovery: "switch_model" };
|
|
9396
|
+
if (/overloaded|server.?error|bad.?gateway|service.?unavailable/i.test(msg))
|
|
9397
|
+
return { code: 1020, recovery: "retry" };
|
|
9398
|
+
if (/timeout|timed.?out|etimedout|econnreset|enotfound|socket.?hang.?up/i.test(msg))
|
|
9399
|
+
return { code: 1041, recovery: "retry" };
|
|
9400
|
+
const code = status ?? extractStatusFromMessage(msg);
|
|
9401
|
+
if (code !== void 0) {
|
|
9402
|
+
if (code === 401 || code === 403) return { code: 1001, recovery: "rotate_credential" };
|
|
9403
|
+
if (code === 402) return { code: 1012, recovery: "switch_provider" };
|
|
9404
|
+
if (code === 404) return { code: 1040, recovery: "switch_model" };
|
|
9405
|
+
if (code === 429) return { code: 1010, recovery: "retry" };
|
|
9406
|
+
if (code >= 500) return { code: 1020, recovery: "retry" };
|
|
9407
|
+
if (code >= 400) return { code: 1050, recovery: "abort" };
|
|
9408
|
+
}
|
|
9409
|
+
return { code: 9999, recovery: "retry" };
|
|
9410
|
+
}
|
|
8906
9411
|
function toModelMessages(messages) {
|
|
8907
9412
|
return messages.map(toModelMessage);
|
|
8908
9413
|
}
|
|
@@ -9140,6 +9645,34 @@ var HookDispatcher = class {
|
|
|
9140
9645
|
return verifications.some((v) => v.action === "continue");
|
|
9141
9646
|
}
|
|
9142
9647
|
};
|
|
9648
|
+
// src/agent/idle-turn-detector.ts
|
|
9649
|
+
var IDLE_ANCHORS = [
|
|
9650
|
+
{
|
|
9651
|
+
zh: /^我(?:先|现在就?|这就|马上|来|去|再|打算|准备|会先|会再|会去|会用)\s*\S/,
|
|
9652
|
+
en: /^i['’]?(?:ll|m\s+going\s+to|\s+will|\s+am\s+going\s+to)\s+\S/i
|
|
9653
|
+
},
|
|
9654
|
+
{ zh: /^让我\s*\S/, en: /^let\s+me\s*\S/i },
|
|
9655
|
+
{ zh: /^接下来我\s*\S/, en: /^next[,,]?\s+i['’]?ll\s+\S/i },
|
|
9656
|
+
{ zh: /^先\s*\S/, en: /^first[,,]?\s+(?:let\s+me|i['’]?ll)\s+\S/i }
|
|
9657
|
+
];
|
|
9658
|
+
var SPEECH_ACTS = [
|
|
9659
|
+
{
|
|
9660
|
+
zh: /^(?:我(?:先|现在就?|这就|马上|来|去|再|打算|准备)|让我|接下来我|先)\s*(?:说|讲|解释|回答|答|总结|归纳|汇报|说明|捋|梳理|聊|谈)/,
|
|
9661
|
+
en: /^(?:let\s+me|i['’]?(?:ll|m\s+going\s+to)|next[,,]?\s+i['’]?ll|first[,,]?\s+(?:let\s+me|i['’]?ll))\s+(?:explain|summari[sz]e|clarify|recap|answer|describe|outline|walk\s+you\s+through|tell\s+you)\b/i
|
|
9662
|
+
}
|
|
9663
|
+
];
|
|
9664
|
+
function sentenceHeads(text) {
|
|
9665
|
+
return text.split(/[\n。!?!?]+/).map((s) => s.trim()).filter((s) => s !== "");
|
|
9666
|
+
}
|
|
9667
|
+
function announcedNextStep(text) {
|
|
9668
|
+
for (const head of sentenceHeads(text)) {
|
|
9669
|
+
const hit = IDLE_ANCHORS.some((a) => a.zh.test(head) || a.en.test(head));
|
|
9670
|
+
if (!hit) continue;
|
|
9671
|
+
if (SPEECH_ACTS.some((a) => a.zh.test(head) || a.en.test(head))) continue;
|
|
9672
|
+
return true;
|
|
9673
|
+
}
|
|
9674
|
+
return false;
|
|
9675
|
+
}
|
|
9143
9676
|
// src/agent/message-codec.ts
|
|
9144
9677
|
var TOOL_OUTPUT_PREFIX = "[tool output] ";
|
|
9145
9678
|
function flattenMessage(m) {
|
|
@@ -9327,14 +9860,32 @@ function currentGoalBlock(view) {
|
|
|
9327
9860
|
}
|
|
9328
9861
|
function wrapCurrentRequest(userText) {
|
|
9329
9862
|
return [
|
|
9330
|
-
"[\u4EE5\u4E0B\u662F\u7528\u6237\u6B64\u523B\u53D1\u6765\u7684**\u65B0\u8BF7\u6C42**\u3002\u4E0A\u9762\u90A3\u4E9B\u7528\u6237\u6D88\u606F\
|
|
9331
|
-
"\
|
|
9332
|
-
"\
|
|
9863
|
+
"[\u4EE5\u4E0B\u662F\u7528\u6237\u6B64\u523B\u53D1\u6765\u7684**\u65B0\u8BF7\u6C42**\u3002\u4E0A\u9762\u90A3\u4E9B\u7528\u6237\u6D88\u606F\u91CC**\u5DF2\u7ECF\u529E\u5B8C**\u7684\u90A3\u4E9B\u4E0D\u8981\u91CD\u505A\uFF0C",
|
|
9864
|
+
"\u9664\u975E\u8FD9\u4E00\u6761\u660E\u786E\u63D0\u5230\uFF1B\u800C\u5176\u4E2D\u7684\u504F\u597D\u548C\u957F\u671F\u7EA6\u675F\uFF08\u56DE\u590D\u8BED\u8A00\u3001\u4EE3\u7801\u98CE\u683C\u3001\u4E0D\u8BB8\u78B0\u7684\u4E1C\u897F\uFF09",
|
|
9865
|
+
"\u4ECD\u7136\u6709\u6548\u3002\u6709\u51B2\u7A81\u65F6\u4EE5\u8FD9\u4E00\u6761\u4E3A\u51C6\u3002",
|
|
9866
|
+
"\u26A0\uFE0F **\u529E\u4E86\u4E00\u534A\u7684\u4E0D\u7B97\u529E\u5B8C**\uFF1A\u4E0A\u6587\u91CC\u8FD8\u6B20\u7740\u7684\u6D3B\u4ECD\u7136\u8981\u505A\u5B8C\u3002\u8FD9\u4E00\u6761\u662F\u300C\u7EE7\u7EED\u300D\u300C\u786E\u8BA4\u300D",
|
|
9867
|
+
"\u300C\u597D\u300D\u8FD9\u7C7B\u77ED\u56DE\u590D\u65F6\uFF0C\u5B83\u6307\u7684\u5C31\u662F\u90A3\u4EF6\u6B20\u7740\u7684\u6D3B\uFF0C\u4E0D\u662F\u300C\u6CA1\u4E8B\u53EF\u505A\u4E86\u300D\u3002]",
|
|
9333
9868
|
"<current_request>",
|
|
9334
9869
|
userText,
|
|
9335
9870
|
"</current_request>"
|
|
9336
9871
|
].join("\n");
|
|
9337
9872
|
}
|
|
9873
|
+
function unfinishedTodoNote(open) {
|
|
9874
|
+
return [
|
|
9875
|
+
`\u4F60\u7684 TODO \u6E05\u5355\u4E0A\u8FD8\u6709 ${open} \u9879\u6CA1\u505A\u5B8C\uFF0C\u6240\u4EE5\u8FD9\u4EF6\u4E8B\u8FD8\u6CA1\u7ED3\u675F\u3002`,
|
|
9876
|
+
"\u63A5\u7740\u505A\u4E0B\u4E00\u9879\uFF0C\u522B\u518D\u95EE\u4E00\u904D\u300C\u8981\u4E0D\u8981\u7EE7\u7EED\u300D\u2014\u2014 \u7528\u6237\u5DF2\u7ECF\u8981\u8FC7\u8FD9\u4EF6\u4E8B\u4E86\u3002",
|
|
9877
|
+
"\u771F\u505A\u4E0D\u4E0B\u53BB\uFF08\u53D6\u6570\u4E00\u76F4\u5931\u8D25\u3001\u7F3A\u51ED\u636E\u3001\u8981\u82B1\u94B1\uFF09\u5C31\u8BF4\u6E05\u5DF2\u5B8C\u6210\u51E0\u9879\u3001\u8FD8\u5269\u51E0\u9879\u3001\u5361\u5728\u54EA\u4E00\u9879\u7684\u54EA\u4E00\u6B65\u3002"
|
|
9878
|
+
].join("\n");
|
|
9879
|
+
}
|
|
9880
|
+
function announcedButIdleNote() {
|
|
9881
|
+
return [
|
|
9882
|
+
"\u4F60\u521A\u624D\u90A3\u6BB5\u8BDD\u5BA3\u5E03\u4E86\u4E0B\u4E00\u6B65\uFF0C\u4F46\u8FD9\u4E00\u8F6E\u4E00\u4E2A\u5DE5\u5177\u90FD\u6CA1\u8C03 \u2014\u2014 \u5728\u7528\u6237\u90A3\u8FB9\uFF0C\u8FD9\u4E00\u8F6E\u5C31\u662F\u505C\u4F4F\u4E86\u3002",
|
|
9883
|
+
"\u73B0\u5728\u5C31\u628A\u4F60\u8BF4\u7684\u90A3\u4E00\u6B65\u505A\u6389\uFF1A\u76F4\u63A5\u8C03\u5DE5\u5177\uFF0C\u522B\u518D\u6362\u4E2A\u8BF4\u6CD5\u628A\u300C\u6211\u9A6C\u4E0A\u53BB\u505A\u300D\u5199\u7B2C\u4E8C\u904D\u3002",
|
|
9884
|
+
"\u5982\u679C\u4F60\u5176\u5B9E\u5DF2\u7ECF\u628A\u7528\u6237\u8981\u7684\u4E8B\u505A\u5B8C\u4E86\u3001\u6216\u8005\u521A\u624D\u53EA\u662F\u5728\u56DE\u7B54\u4E00\u4E2A\u95EE\u9898\uFF0C\u90A3\u5C31\u76F4\u63A5\u8BF4\u7ED3\u8BBA\uFF0C\u522B\u518D\u5BA3\u5E03\u4E0B\u4E00\u6B65\u3002",
|
|
9885
|
+
"\u5982\u679C\u7528\u6237\u660E\u8BF4\u4E86\u300C\u5148\u522B\u52A8\u624B\u300D\u300C\u53EA\u8BF4\u8BA1\u5212\u300D\u300C\u5148\u522B\u6539\u300D\u8FD9\u7C7B\u8BDD\uFF0C\u90A3\u5C31\u7167\u7528\u6237\u8BF4\u7684\u529E \u2014\u2014 \u8FD9\u6761\u63D0\u9192\u7BA1\u4E0D\u7740\u7528\u6237\u521A\u4E0B\u7684\u6307\u4EE4\uFF0C\u522B\u56E0\u4E3A\u5B83\u53BB\u8C03\u5DE5\u5177\u3002",
|
|
9886
|
+
"\u771F\u505A\u4E0D\u4E0B\u53BB\uFF08\u7F3A\u51ED\u636E\u3001\u8981\u82B1\u94B1\u3001\u53D6\u6570\u4E00\u76F4\u5931\u8D25\uFF09\u5C31\u8BF4\u6E05\u5361\u5728\u54EA\u4E00\u6B65\u7684\u54EA\u4E00\u73AF\u3002"
|
|
9887
|
+
].join("\n");
|
|
9888
|
+
}
|
|
9338
9889
|
var CODE_VERIFICATION_POLICY = [
|
|
9339
9890
|
"<code_verification>",
|
|
9340
9891
|
"\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",
|
|
@@ -9356,6 +9907,20 @@ var ASK_QUESTION_POLICY = [
|
|
|
9356
9907
|
"\u522B\u7528\u5B83\u95EE\u300C\u6211\u7684\u65B9\u6848\u884C\u4E0D\u884C\u300D\uFF08\u90A3\u662F `exit_plan_mode`\uFF09\uFF0C\u4E5F\u522B\u7528\u5B83\u6C47\u62A5\u8FDB\u5EA6\u3002",
|
|
9357
9908
|
"</ask_user_question>"
|
|
9358
9909
|
].join("\n");
|
|
9910
|
+
var BATCH_WORK_POLICY = [
|
|
9911
|
+
"<batch_work>",
|
|
9912
|
+
"\u4E00\u4EF6\u4E8B\u8981\u5BF9 N \u4E2A\u5BF9\u8C61\u5404\u505A\u4E00\u904D\uFF08\u4E00\u6279\u5546\u54C1 / \u4E00\u6279\u6587\u4EF6 / \u4E00\u6279\u884C\uFF09\u65F6\uFF1A",
|
|
9913
|
+
"1. **\u5148\u6570\u6E05\u662F\u51E0\u4E2A**\uFF0C\u7528 `todo` \u843D\u6210\u6E05\u5355\u518D\u52A8\u624B\u3002\u6570\u76EE\u591A\u5C31\u5206\u6279\u5EFA\u6761\u76EE\uFF0C\u522B\u53EA\u5EFA\u524D\u51E0\u4E2A\u3002",
|
|
9914
|
+
"2. **\u4E00\u6761\u4E00\u6761\u505A\u5B8C**\uFF0C\u6BCF\u505A\u5B8C\u4E00\u6761\u5C31\u628A\u5B83\u6807 `completed`\u3002\u4E2D\u95F4\u4E0D\u8981\u505C\u4E0B\u6765\u6C47\u62A5\u8FDB\u5EA6 \u2014\u2014",
|
|
9915
|
+
" \u6C47\u62A5\u4E00\u6B21\u5C31\u662F\u6536\u5DE5\u4E00\u6B21\uFF0C\u4E4B\u540E\u8981\u7528\u6237\u518D\u5F00\u53E3\u624D\u80FD\u7EE7\u7EED\u3002",
|
|
9916
|
+
"3. **\u4E0D\u8BB8\u62FF\u300C\u6B65\u9AA4\u5F88\u591A\u300D\u300C\u8981\u82B1\u65F6\u95F4\u300D\u300C\u6BCF\u4E2A\u90FD\u8981\u53D6\u4E00\u904D\u6570\u636E\u300D\u5F53\u7406\u7531\u7F29\u5C0F\u8303\u56F4\u3002**",
|
|
9917
|
+
" \u90A3\u6B63\u662F\u7528\u6237\u8BA9\u4F60\u505A\u8FD9\u4EF6\u4E8B\u7684\u539F\u56E0\u3002\u6162\u4E0D\u662F\u95EE\u9898\uFF0C\u53EA\u505A\u4E00\u90E8\u5206\u624D\u662F\u3002",
|
|
9918
|
+
"4. \u53EA\u5728**\u771F\u505A\u4E0D\u4E0B\u53BB**\u65F6\u505C\uFF08\u53D6\u6570\u4E00\u76F4\u5931\u8D25\u3001\u7F3A\u51ED\u636E\u3001\u8981\u82B1\u94B1\uFF09\u3002\u505C\u7684\u65F6\u5019\u8BF4\u6E05",
|
|
9919
|
+
" \u300C\u5DF2\u5B8C\u6210 X \u9879\u3001\u8FD8\u5269 Y \u9879\u3001\u5361\u5728\u54EA\u4E00\u9879\u7684\u54EA\u4E00\u6B65\u300D\uFF0C**\u522B\u95EE\u300C\u8981\u4E0D\u8981\u7EE7\u7EED\u300D**\u3002",
|
|
9920
|
+
"5. \u6570\u636E\u6E90\u4E00\u6B21\u53D6\u4E0D\u5168\uFF08\u5DE5\u5177\u8F93\u51FA\u88AB\u622A\u65AD\uFF09\u65F6\uFF0C\u5206\u6BB5\u53D6\u6216\u6362\u66F4\u7CBE\u786E\u7684\u53D6\u6CD5\u518D\u53D6\u4E00\u6B21 \u2014\u2014",
|
|
9921
|
+
" **\u522B\u628A\u770B\u5F97\u89C1\u7684\u90A3\u51E0\u6761\u5F53\u6210\u5168\u90E8**\u3002",
|
|
9922
|
+
"</batch_work>"
|
|
9923
|
+
].join("\n");
|
|
9359
9924
|
var PromptBuilder = class {
|
|
9360
9925
|
constructor(deps) {
|
|
9361
9926
|
this.deps = deps;
|
|
@@ -9364,9 +9929,10 @@ var PromptBuilder = class {
|
|
|
9364
9929
|
memoryInitialized = false;
|
|
9365
9930
|
projectBlock(tools) {
|
|
9366
9931
|
const permLevel = this.deps.permissionManager?.getLevel() || "default";
|
|
9367
|
-
const hasPlanTool = tools.some((
|
|
9368
|
-
const hasLspTool = tools.some((
|
|
9369
|
-
const hasAskTool = tools.some((
|
|
9932
|
+
const hasPlanTool = tools.some((t78) => t78.name === "enter_plan_mode");
|
|
9933
|
+
const hasLspTool = tools.some((t78) => t78.name === "lsp_diagnostics");
|
|
9934
|
+
const hasAskTool = tools.some((t78) => t78.name === "ask_user_question");
|
|
9935
|
+
const hasTodoTool = tools.some((t78) => t78.name === "todo");
|
|
9370
9936
|
const plan = this.deps.approvedPlan?.()?.trim();
|
|
9371
9937
|
const codeSdk = renderSdkDeclaration(this.deps.codeModeTools?.() ?? []);
|
|
9372
9938
|
return renderProjectContext(this.deps.site().projectContext) + `
|
|
@@ -9379,7 +9945,10 @@ ${PLAN_MODE_POLICY}` : "") + (hasLspTool ? `
|
|
|
9379
9945
|
|
|
9380
9946
|
${CODE_VERIFICATION_POLICY}` : "") + (hasAskTool ? `
|
|
9381
9947
|
|
|
9382
|
-
${ASK_QUESTION_POLICY}` : "") +
|
|
9948
|
+
${ASK_QUESTION_POLICY}` : "") +
|
|
9949
|
+
(hasTodoTool ? `
|
|
9950
|
+
|
|
9951
|
+
${BATCH_WORK_POLICY}` : "") + (codeSdk ? `
|
|
9383
9952
|
|
|
9384
9953
|
${codeSdk}` : "") +
|
|
9385
9954
|
(plan ? `
|
|
@@ -9435,9 +10004,16 @@ ${recalled}
|
|
|
9435
10004
|
}
|
|
9436
10005
|
};
|
|
9437
10006
|
var STALL_THRESHOLD = 5;
|
|
10007
|
+
var FAILURE_THRESHOLD = 3;
|
|
10008
|
+
var MAX_FAILURE_KEYS = 64;
|
|
10009
|
+
var FINGERPRINT_MAX_CHARS = 200;
|
|
10010
|
+
function fingerprint(output) {
|
|
10011
|
+
return output.replace(/\s+/g, " ").trim().slice(0, FINGERPRINT_MAX_CHARS);
|
|
10012
|
+
}
|
|
9438
10013
|
var StallDetector = class {
|
|
9439
10014
|
history = [];
|
|
9440
|
-
|
|
10015
|
+
failures = /* @__PURE__ */ new Map();
|
|
10016
|
+
recordAction(toolName, args, outcome) {
|
|
9441
10017
|
const argsKey2 = JSON.stringify(args);
|
|
9442
10018
|
const last = this.history[this.history.length - 1];
|
|
9443
10019
|
if (last && last.toolName === toolName && last.args === argsKey2) {
|
|
@@ -9445,26 +10021,66 @@ var StallDetector = class {
|
|
|
9445
10021
|
} else {
|
|
9446
10022
|
this.history = [{ toolName, args: argsKey2, consecutive: 1 }];
|
|
9447
10023
|
}
|
|
10024
|
+
if (!outcome) return;
|
|
10025
|
+
const key = `${toolName}\0${argsKey2}`;
|
|
10026
|
+
if (outcome.success) {
|
|
10027
|
+
this.failures.delete(key);
|
|
10028
|
+
return;
|
|
10029
|
+
}
|
|
10030
|
+
const error = fingerprint(outcome.output);
|
|
10031
|
+
const rec = this.failures.get(key);
|
|
10032
|
+
if (rec?.error === error) {
|
|
10033
|
+
rec.count++;
|
|
10034
|
+
return;
|
|
10035
|
+
}
|
|
10036
|
+
if (rec === void 0 && this.failures.size >= MAX_FAILURE_KEYS) {
|
|
10037
|
+
const oldest = this.failures.keys().next();
|
|
10038
|
+
if (!oldest.done) this.failures.delete(oldest.value);
|
|
10039
|
+
}
|
|
10040
|
+
this.failures.set(key, { toolName, args: argsKey2, error, count: 1 });
|
|
9448
10041
|
}
|
|
9449
10042
|
isStalled() {
|
|
9450
|
-
|
|
9451
|
-
return last ? last.consecutive >= STALL_THRESHOLD : false;
|
|
10043
|
+
return this.stalledFailure() !== null || this.stalledStreak() !== null;
|
|
9452
10044
|
}
|
|
9453
10045
|
getStallInfo() {
|
|
9454
|
-
const
|
|
9455
|
-
if (
|
|
9456
|
-
|
|
9457
|
-
|
|
9458
|
-
|
|
9459
|
-
|
|
9460
|
-
|
|
10046
|
+
const failure = this.stalledFailure();
|
|
10047
|
+
if (failure) {
|
|
10048
|
+
return t("agent_run.stall_failure_info", {
|
|
10049
|
+
count: failure.count,
|
|
10050
|
+
tool: failure.toolName,
|
|
10051
|
+
args: failure.args,
|
|
10052
|
+
error: failure.error
|
|
10053
|
+
});
|
|
10054
|
+
}
|
|
10055
|
+
const streak = this.stalledStreak();
|
|
10056
|
+
if (streak) {
|
|
10057
|
+
return t("agent_run.stall_info", {
|
|
10058
|
+
count: streak.consecutive,
|
|
10059
|
+
tool: streak.toolName,
|
|
10060
|
+
args: streak.args
|
|
10061
|
+
});
|
|
10062
|
+
}
|
|
10063
|
+
return null;
|
|
9461
10064
|
}
|
|
9462
|
-
|
|
10065
|
+
reset() {
|
|
9463
10066
|
this.history = [];
|
|
10067
|
+
this.failures.clear();
|
|
9464
10068
|
}
|
|
9465
10069
|
getLastToolName() {
|
|
9466
10070
|
return this.history[this.history.length - 1]?.toolName ?? null;
|
|
9467
10071
|
}
|
|
10072
|
+
stalledStreak() {
|
|
10073
|
+
const last = this.history[this.history.length - 1];
|
|
10074
|
+
return last && last.consecutive >= STALL_THRESHOLD ? last : null;
|
|
10075
|
+
}
|
|
10076
|
+
stalledFailure() {
|
|
10077
|
+
let worst = null;
|
|
10078
|
+
for (const rec of this.failures.values()) {
|
|
10079
|
+
if (rec.count < FAILURE_THRESHOLD) continue;
|
|
10080
|
+
if (worst === null || rec.count > worst.count) worst = rec;
|
|
10081
|
+
}
|
|
10082
|
+
return worst;
|
|
10083
|
+
}
|
|
9468
10084
|
};
|
|
9469
10085
|
var ARTIFACT_RETENTION_DEFAULTS = {
|
|
9470
10086
|
maxAgeDays: 7,
|
|
@@ -9530,10 +10146,133 @@ function pruneArtifacts(root, keepSession, retention = ARTIFACT_RETENTION_DEFAUL
|
|
|
9530
10146
|
async function removeSessionArtifacts(sessionId, homeDir) {
|
|
9531
10147
|
await rm(artifactsDir(sessionId, homeDir), { recursive: true, force: true });
|
|
9532
10148
|
}
|
|
9533
|
-
|
|
9534
|
-
var MAX_TOOL_OUTPUT = 2e3;
|
|
10149
|
+
var MAX_TOOL_OUTPUT = 3e4;
|
|
9535
10150
|
var MAX_SUB_CALL_OUTPUT = CODE_MODE_LIMITS.maxValueChars;
|
|
9536
|
-
|
|
10151
|
+
function budgetOf2(tool, depth, path = "success") {
|
|
10152
|
+
if (tool.governsOwnOutput && path === "success") return Number.POSITIVE_INFINITY;
|
|
10153
|
+
return depth > 0 ? MAX_SUB_CALL_OUTPUT : MAX_TOOL_OUTPUT;
|
|
10154
|
+
}
|
|
10155
|
+
function shapeOf(output) {
|
|
10156
|
+
if (output === void 0 || output === null) return "text";
|
|
10157
|
+
return typeof output === "string" ? "text" : "json";
|
|
10158
|
+
}
|
|
10159
|
+
var NEXT_STEP = "\u8981\u62FF\u5B8C\u6574\u5185\u5BB9\uFF1A\u6536\u7A84\u8FD9\u4E2A\u5DE5\u5177\u7684\u8303\u56F4\u53C2\u6570\uFF08\u5206\u9875 / \u5B57\u6BB5 / limit\uFF09\u540E\u91CD\u53D6\uFF0C\u6216\u5728 run_code \u91CC\u8C03\u5B83\u3001\u5728\u7A0B\u5E8F\u91CC\u7B5B";
|
|
10160
|
+
var CUT_LABEL = {
|
|
10161
|
+
line: "\u5207\u5728\u884C\u8FB9\u754C",
|
|
10162
|
+
space: "\u5207\u5728\u7A7A\u767D\u8FB9\u754C",
|
|
10163
|
+
hard: "\u786C\u5207\uFF08\u8FD9\u4E00\u6BB5\u91CC\u6CA1\u6709\u6362\u884C\uFF0C\u672B\u5C3E\u53EF\u80FD\u662F\u6B8B\u7684\uFF09"
|
|
10164
|
+
};
|
|
10165
|
+
var CUT_RANK = { line: 0, space: 1, hard: 2 };
|
|
10166
|
+
function truncateForModel(text, limit, shape) {
|
|
10167
|
+
if (!Number.isFinite(limit)) return text;
|
|
10168
|
+
const total = countCodePoints(text);
|
|
10169
|
+
if (total <= limit) return text;
|
|
10170
|
+
return shape === "json" ? cutJson(text, limit, total) : cutText(text, limit, total);
|
|
10171
|
+
}
|
|
10172
|
+
function notice(detail) {
|
|
10173
|
+
return `[\u5F15\u64CE\u6309\u8F93\u51FA\u9884\u7B97\u622A\u65AD\uFF1A${detail}\u3002${NEXT_STEP}]`;
|
|
10174
|
+
}
|
|
10175
|
+
function cutText(text, limit, total) {
|
|
10176
|
+
const half = Math.floor(limit / 2);
|
|
10177
|
+
const head = backOff(headCodePoints(text, half), "head");
|
|
10178
|
+
const tail = backOff(tailCodePoints(text, limit - half), "tail");
|
|
10179
|
+
const skipped = total - countCodePoints(head.text) - countCodePoints(tail.text);
|
|
10180
|
+
const kind = CUT_RANK[head.kind] >= CUT_RANK[tail.kind] ? head.kind : tail.kind;
|
|
10181
|
+
return [
|
|
10182
|
+
head.text,
|
|
10183
|
+
`\u2026[\u4E2D\u6BB5\u7701\u7565 ${skipped} \u5B57\u7B26\uFF0C${CUT_LABEL[kind]}]\u2026`,
|
|
10184
|
+
tail.text,
|
|
10185
|
+
notice(`\u539F\u6587 ${total} \u5B57\u7B26\uFF0C\u9884\u7B97 ${limit}`)
|
|
10186
|
+
].join("\n");
|
|
10187
|
+
}
|
|
10188
|
+
function backOff(piece, side) {
|
|
10189
|
+
const floor = piece.length / 2;
|
|
10190
|
+
for (const [kind, ch] of [
|
|
10191
|
+
["line", "\n"],
|
|
10192
|
+
["space", " "]
|
|
10193
|
+
]) {
|
|
10194
|
+
const at = side === "head" ? piece.lastIndexOf(ch) : piece.indexOf(ch);
|
|
10195
|
+
if (at < 0) continue;
|
|
10196
|
+
const kept = side === "head" ? piece.slice(0, at) : piece.slice(at + 1);
|
|
10197
|
+
if (kept.length >= floor) return { text: kept, kind };
|
|
10198
|
+
}
|
|
10199
|
+
return { text: piece, kind: "hard" };
|
|
10200
|
+
}
|
|
10201
|
+
function cutJson(text, limit, total) {
|
|
10202
|
+
const cut = scanJson(text, limit);
|
|
10203
|
+
const head = `\u5E8F\u5217\u5316\u540E ${total} \u5B57\u7B26\uFF0C\u9884\u7B97 ${limit}`;
|
|
10204
|
+
if (cut.element !== void 0) {
|
|
10205
|
+
const dropped = total - countCodePoints(cut.element);
|
|
10206
|
+
return `${cut.element}
|
|
10207
|
+
${notice(`${head}\uFF0C\u5DF2\u6309\u7ED3\u6784\u622A\u5230\u6700\u540E\u4E00\u4E2A\u5B8C\u6574\u7684\u6570\u7EC4\u5143\u7D20\u5E76\u8865\u9F50\u62EC\u53F7\uFF0C\u4E22\u6389\u5C3E\u90E8 ${dropped} \u5B57\u7B26\u3002\u7559\u4E0B\u6765\u7684\u6BCF\u4E00\u6761\u90FD\u662F\u6574\u6761\uFF0C\u4F46**\u88AB\u8865\u9F50\u7684\u5BB9\u5668\u540E\u9762\u53EF\u80FD\u8FD8\u7F3A\u5B57\u6BB5**\uFF08\u6BD4\u5982\u6839\u5BF9\u8C61\u4E0A\u6392\u5728\u540E\u9762\u7684\u90A3\u4E9B\u952E\uFF09`)}`;
|
|
10208
|
+
}
|
|
10209
|
+
if (cut.field !== void 0) {
|
|
10210
|
+
const dropped = total - countCodePoints(cut.field);
|
|
10211
|
+
return `${cut.field}
|
|
10212
|
+
${notice(`${head}\uFF0C\u9884\u7B97\u5185\u653E\u4E0D\u4E0B\u4E00\u4E2A\u5B8C\u6574\u5143\u7D20\uFF0C\u6539\u4E3A\u5C31\u5730\u622A\u65AD\u90A3\u4E2A\u5B57\u7B26\u4E32\u5B57\u6BB5\uFF08\u6807\u8BB0\u5728\u503C\u91CC\u9762\uFF09\uFF0C\u4E22\u6389 ${dropped} \u5B57\u7B26`)}`;
|
|
10213
|
+
}
|
|
10214
|
+
return notice(
|
|
10215
|
+
`${head}\uFF0C\u800C\u9884\u7B97\u5185\u8FDE\u4E00\u4E2A\u5B8C\u6574\u5143\u7D20\u90FD\u653E\u4E0D\u4E0B\u3002**\u8FD9\u91CC\u6CA1\u6709\u6570\u636E** \u2014\u2014 \u6309\u5B57\u7B26\u5207\u4F1A\u5207\u51FA\u4E00\u4E2A\u5408\u6CD5\u4F46\u9519\u8BEF\u7684\u6570\u503C\uFF0C\u6240\u4EE5\u4E00\u4E2A\u5B57\u90FD\u4E0D\u7ED9`
|
|
10216
|
+
);
|
|
10217
|
+
}
|
|
10218
|
+
var FIELD_MARK = "\u2026[\u672C\u5B57\u6BB5\u88AB\u5F15\u64CE\u622A\u65AD]";
|
|
10219
|
+
function scanJson(text, limit) {
|
|
10220
|
+
const stack = [];
|
|
10221
|
+
const expectKey = [];
|
|
10222
|
+
let inString = false;
|
|
10223
|
+
let escaped = false;
|
|
10224
|
+
let element;
|
|
10225
|
+
let field;
|
|
10226
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
10227
|
+
if (i === limit && inString && expectKey[expectKey.length - 1] !== true) {
|
|
10228
|
+
field = closeString(text, i, escaped, stack);
|
|
10229
|
+
}
|
|
10230
|
+
if (i > limit) break;
|
|
10231
|
+
const c = text[i];
|
|
10232
|
+
if (inString) {
|
|
10233
|
+
if (escaped) escaped = false;
|
|
10234
|
+
else if (c === "\\") escaped = true;
|
|
10235
|
+
else if (c === '"') inString = false;
|
|
10236
|
+
continue;
|
|
10237
|
+
}
|
|
10238
|
+
if (c === '"') inString = true;
|
|
10239
|
+
else if (c === "{" || c === "[") {
|
|
10240
|
+
stack.push(c === "{" ? "}" : "]");
|
|
10241
|
+
expectKey.push(c === "{");
|
|
10242
|
+
} else if (c === "}" || c === "]") {
|
|
10243
|
+
stack.pop();
|
|
10244
|
+
expectKey.pop();
|
|
10245
|
+
if (inArray(stack) && i + 1 + stack.length <= limit) {
|
|
10246
|
+
element = text.slice(0, i + 1) + closers(stack);
|
|
10247
|
+
}
|
|
10248
|
+
} else if (c === ":") {
|
|
10249
|
+
expectKey[expectKey.length - 1] = false;
|
|
10250
|
+
} else if (c === ",") {
|
|
10251
|
+
expectKey[expectKey.length - 1] = stack[stack.length - 1] === "}";
|
|
10252
|
+
if (inArray(stack) && i + stack.length <= limit) {
|
|
10253
|
+
element = text.slice(0, i) + closers(stack);
|
|
10254
|
+
}
|
|
10255
|
+
}
|
|
10256
|
+
}
|
|
10257
|
+
return {
|
|
10258
|
+
...element !== void 0 ? { element } : {},
|
|
10259
|
+
...field !== void 0 ? { field } : {}
|
|
10260
|
+
};
|
|
10261
|
+
}
|
|
10262
|
+
function closers(stack) {
|
|
10263
|
+
return [...stack].reverse().join("");
|
|
10264
|
+
}
|
|
10265
|
+
function inArray(stack) {
|
|
10266
|
+
return stack[stack.length - 1] === "]";
|
|
10267
|
+
}
|
|
10268
|
+
function closeString(text, at, escaped, stack) {
|
|
10269
|
+
const kept = dropLoneHighSurrogate(text.slice(0, escaped ? at - 1 : at));
|
|
10270
|
+
return `${kept}${FIELD_MARK}"${closers(stack)}`;
|
|
10271
|
+
}
|
|
10272
|
+
function dropLoneHighSurrogate(text) {
|
|
10273
|
+
return /[\uD800-\uDBFF]$/.test(text) ? text.slice(0, -1) : text;
|
|
10274
|
+
}
|
|
10275
|
+
// src/agent/tool-executor.ts
|
|
9537
10276
|
var MAX_ARGS_KEY = 200;
|
|
9538
10277
|
var OUTPUT_TAG = "tool_output";
|
|
9539
10278
|
var MAX_SUB_CALL_DEPTH = 1;
|
|
@@ -9554,17 +10293,14 @@ function sourceLabel2(toolName) {
|
|
|
9554
10293
|
let label = toolName;
|
|
9555
10294
|
if (toolName.startsWith("mcp__")) {
|
|
9556
10295
|
const rest = toolName.slice("mcp__".length);
|
|
9557
|
-
const
|
|
9558
|
-
label = `mcp:${
|
|
10296
|
+
const sep7 = rest.indexOf("__");
|
|
10297
|
+
label = `mcp:${sep7 > 0 ? rest.slice(0, sep7) : rest}`;
|
|
9559
10298
|
}
|
|
9560
10299
|
return label.replace(/[^A-Za-z0-9_.:-]/g, "_");
|
|
9561
10300
|
}
|
|
9562
|
-
function
|
|
9563
|
-
|
|
9564
|
-
|
|
9565
|
-
function appendArtifactSummaries(text, artifacts, limit) {
|
|
9566
|
-
if (artifacts.length === 0) return truncate(text, limit);
|
|
9567
|
-
const body2 = truncate(text, limit);
|
|
10301
|
+
function appendArtifactSummaries(text, artifacts, limit, shape) {
|
|
10302
|
+
const body2 = truncateForModel(text, limit, shape);
|
|
10303
|
+
if (artifacts.length === 0) return body2;
|
|
9568
10304
|
const lines = artifacts.map((a) => a.summary).join("\n");
|
|
9569
10305
|
return body2 ? `${body2}
|
|
9570
10306
|
${lines}` : lines;
|
|
@@ -9662,6 +10398,7 @@ var ToolExecutor = class {
|
|
|
9662
10398
|
permissionLevel: this.deps.permissionManager?.getLevel() || "auto",
|
|
9663
10399
|
...signal ? { signal } : {},
|
|
9664
10400
|
...onOutput ? { onOutput } : {},
|
|
10401
|
+
outputBudget: budgetOf2(et, depth),
|
|
9665
10402
|
...depth < MAX_SUB_CALL_DEPTH ? { callTool: this.subCallChannel(sessionId, signal, span, depth) } : {}
|
|
9666
10403
|
});
|
|
9667
10404
|
if (snapshotted.length > 0) {
|
|
@@ -9693,11 +10430,7 @@ var ToolExecutor = class {
|
|
|
9693
10430
|
success: true,
|
|
9694
10431
|
output: this.renderOutput(
|
|
9695
10432
|
et,
|
|
9696
|
-
appendArtifactSummaries(
|
|
9697
|
-
text,
|
|
9698
|
-
artifacts,
|
|
9699
|
-
depth > 0 ? MAX_SUB_CALL_OUTPUT : MAX_TOOL_OUTPUT
|
|
9700
|
-
)
|
|
10433
|
+
appendArtifactSummaries(text, artifacts, budgetOf2(et, depth), shapeOf(r.output))
|
|
9701
10434
|
),
|
|
9702
10435
|
...artifacts.length > 0 ? { artifacts } : {},
|
|
9703
10436
|
...diffs.length > 0 ? { diffs } : {}
|
|
@@ -9708,7 +10441,7 @@ var ToolExecutor = class {
|
|
|
9708
10441
|
success: false,
|
|
9709
10442
|
output: this.renderOutput(
|
|
9710
10443
|
et,
|
|
9711
|
-
|
|
10444
|
+
truncateForModel(failureText(r), budgetOf2(et, depth, "failure"), "text")
|
|
9712
10445
|
)
|
|
9713
10446
|
});
|
|
9714
10447
|
} catch (err) {
|
|
@@ -9820,7 +10553,8 @@ var ToolExecutor = class {
|
|
|
9820
10553
|
type: opType,
|
|
9821
10554
|
target: opTarget,
|
|
9822
10555
|
detail: opDetail,
|
|
9823
|
-
reason: decision.reason
|
|
10556
|
+
reason: decision.reason,
|
|
10557
|
+
...decision.rememberable === false ? { rememberable: false } : {}
|
|
9824
10558
|
})
|
|
9825
10559
|
);
|
|
9826
10560
|
this.deps.telemetry?.counter(METRIC.TOOL_APPROVAL, 1, {
|
|
@@ -9844,6 +10578,72 @@ var ToolExecutor = class {
|
|
|
9844
10578
|
return { allowed: true };
|
|
9845
10579
|
}
|
|
9846
10580
|
};
|
|
10581
|
+
var WINDOW_CHARS = 1024;
|
|
10582
|
+
var NGRAM = 16;
|
|
10583
|
+
var MAX_DISTINCT_RATIO = 0.2;
|
|
10584
|
+
var CHECK_EVERY_CHARS = 256;
|
|
10585
|
+
var SUSTAIN_CHECKS = 2;
|
|
10586
|
+
var MIN_NORMALIZED_CHARS = 256;
|
|
10587
|
+
function normalizeRuns(text) {
|
|
10588
|
+
return text.replace(/\s+/g, " ").replace(/(.)\1{3,}/gu, (_m, c) => c.repeat(3));
|
|
10589
|
+
}
|
|
10590
|
+
function distinctNgramRatio(text, n = NGRAM) {
|
|
10591
|
+
if (text.length < n) return 1;
|
|
10592
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10593
|
+
let total = 0;
|
|
10594
|
+
for (let i = 0; i + n <= text.length; i++) {
|
|
10595
|
+
seen.add(text.slice(i, i + n));
|
|
10596
|
+
total++;
|
|
10597
|
+
}
|
|
10598
|
+
return total === 0 ? 1 : seen.size / total;
|
|
10599
|
+
}
|
|
10600
|
+
var RepetitionDetector = class {
|
|
10601
|
+
buf = "";
|
|
10602
|
+
sinceCheck = 0;
|
|
10603
|
+
streak = 0;
|
|
10604
|
+
chars = 0;
|
|
10605
|
+
hit = null;
|
|
10606
|
+
push(delta) {
|
|
10607
|
+
if (this.hit) return true;
|
|
10608
|
+
if (!delta) return false;
|
|
10609
|
+
this.chars += delta.length;
|
|
10610
|
+
this.buf = (this.buf + delta).slice(-WINDOW_CHARS);
|
|
10611
|
+
this.sinceCheck += delta.length;
|
|
10612
|
+
if (this.sinceCheck < CHECK_EVERY_CHARS) return false;
|
|
10613
|
+
this.sinceCheck = 0;
|
|
10614
|
+
if (this.buf.length < WINDOW_CHARS) return false;
|
|
10615
|
+
const norm = normalizeRuns(this.buf);
|
|
10616
|
+
if (norm.length < MIN_NORMALIZED_CHARS) {
|
|
10617
|
+
this.streak = 0;
|
|
10618
|
+
return false;
|
|
10619
|
+
}
|
|
10620
|
+
const ratio = distinctNgramRatio(norm);
|
|
10621
|
+
if (ratio > MAX_DISTINCT_RATIO) {
|
|
10622
|
+
this.streak = 0;
|
|
10623
|
+
return false;
|
|
10624
|
+
}
|
|
10625
|
+
if (++this.streak < SUSTAIN_CHECKS) return false;
|
|
10626
|
+
this.hit = { chars: this.chars, distinctRatio: ratio };
|
|
10627
|
+
return true;
|
|
10628
|
+
}
|
|
10629
|
+
pushAll(text) {
|
|
10630
|
+
for (let i = 0; i < text.length; i += CHECK_EVERY_CHARS) {
|
|
10631
|
+
if (this.push(text.slice(i, i + CHECK_EVERY_CHARS))) return true;
|
|
10632
|
+
}
|
|
10633
|
+
return false;
|
|
10634
|
+
}
|
|
10635
|
+
isLooping() {
|
|
10636
|
+
return this.hit !== null;
|
|
10637
|
+
}
|
|
10638
|
+
getInfo() {
|
|
10639
|
+
if (!this.hit) return null;
|
|
10640
|
+
return t("agent_run.looping_info", {
|
|
10641
|
+
chars: this.hit.chars,
|
|
10642
|
+
percent: Math.round(this.hit.distinctRatio * 100)
|
|
10643
|
+
});
|
|
10644
|
+
}
|
|
10645
|
+
};
|
|
10646
|
+
// src/agent/turn.ts
|
|
9847
10647
|
async function* runTurn(opts) {
|
|
9848
10648
|
if (opts.streaming) {
|
|
9849
10649
|
return yield* runTurnStreaming(opts);
|
|
@@ -9852,55 +10652,72 @@ async function* runTurn(opts) {
|
|
|
9852
10652
|
}
|
|
9853
10653
|
async function* runTurnStreaming(opts) {
|
|
9854
10654
|
const { provider, input, guard, reasoningGuard } = opts;
|
|
10655
|
+
const repetition = new RepetitionDetector();
|
|
10656
|
+
const reasoningRepetition = new RepetitionDetector();
|
|
9855
10657
|
let text = "";
|
|
9856
10658
|
let reasoningText = "";
|
|
9857
10659
|
let finishReason = "stop";
|
|
10660
|
+
let looping;
|
|
9858
10661
|
let usage;
|
|
9859
10662
|
const toolCalls = [];
|
|
9860
10663
|
let streamError;
|
|
9861
10664
|
let model;
|
|
9862
10665
|
let warnings;
|
|
9863
|
-
|
|
9864
|
-
|
|
9865
|
-
|
|
9866
|
-
|
|
9867
|
-
|
|
9868
|
-
|
|
9869
|
-
|
|
9870
|
-
|
|
9871
|
-
|
|
9872
|
-
|
|
10666
|
+
const ac = new AbortController();
|
|
10667
|
+
const unlink = linkAbort(input.signal, ac);
|
|
10668
|
+
try {
|
|
10669
|
+
for await (const chunk of provider.stream({ ...input, signal: ac.signal })) {
|
|
10670
|
+
switch (chunk.type) {
|
|
10671
|
+
case "text-delta": {
|
|
10672
|
+
const raw = chunk.textDelta ?? "";
|
|
10673
|
+
if (!raw) break;
|
|
10674
|
+
const delta = guard ? guard.push(raw) : raw;
|
|
10675
|
+
if (!delta) break;
|
|
10676
|
+
text += delta;
|
|
10677
|
+
yield { type: "text-delta", text: delta };
|
|
10678
|
+
if (repetition.push(delta)) looping = repetition;
|
|
10679
|
+
break;
|
|
10680
|
+
}
|
|
10681
|
+
case "reasoning-delta": {
|
|
10682
|
+
const raw = chunk.textDelta ?? "";
|
|
10683
|
+
if (!raw) break;
|
|
10684
|
+
const delta = reasoningGuard ? reasoningGuard.push(raw) : raw;
|
|
10685
|
+
if (!delta) break;
|
|
10686
|
+
reasoningText += delta;
|
|
10687
|
+
yield { type: "reasoning-delta", text: delta };
|
|
10688
|
+
if (reasoningRepetition.push(delta)) looping = reasoningRepetition;
|
|
10689
|
+
break;
|
|
10690
|
+
}
|
|
10691
|
+
case "tool-call":
|
|
10692
|
+
if (chunk.toolCall) {
|
|
10693
|
+
toolCalls.push({
|
|
10694
|
+
toolCallId: chunk.toolCall.toolCallId,
|
|
10695
|
+
toolName: chunk.toolCall.toolName,
|
|
10696
|
+
input: chunk.toolCall.input
|
|
10697
|
+
});
|
|
10698
|
+
}
|
|
10699
|
+
break;
|
|
10700
|
+
case "finish":
|
|
10701
|
+
if (chunk.usage) usage = chunk.usage;
|
|
10702
|
+
if (chunk.finishReason) finishReason = chunk.finishReason;
|
|
10703
|
+
if (chunk.model) model = chunk.model;
|
|
10704
|
+
if (chunk.warnings) warnings = chunk.warnings;
|
|
10705
|
+
break;
|
|
10706
|
+
case "error":
|
|
10707
|
+
streamError = chunk.error ?? t("agent_run.unknown_provider_error");
|
|
10708
|
+
break;
|
|
10709
|
+
case "tool-result":
|
|
10710
|
+
break;
|
|
9873
10711
|
}
|
|
9874
|
-
|
|
9875
|
-
|
|
9876
|
-
if (!raw) break;
|
|
9877
|
-
const delta = reasoningGuard ? reasoningGuard.push(raw) : raw;
|
|
9878
|
-
if (!delta) break;
|
|
9879
|
-
reasoningText += delta;
|
|
9880
|
-
yield { type: "reasoning-delta", text: delta };
|
|
10712
|
+
if (looping) {
|
|
10713
|
+
ac.abort();
|
|
9881
10714
|
break;
|
|
9882
10715
|
}
|
|
9883
|
-
case "tool-call":
|
|
9884
|
-
if (chunk.toolCall) {
|
|
9885
|
-
toolCalls.push({
|
|
9886
|
-
toolCallId: chunk.toolCall.toolCallId,
|
|
9887
|
-
toolName: chunk.toolCall.toolName,
|
|
9888
|
-
input: chunk.toolCall.input
|
|
9889
|
-
});
|
|
9890
|
-
}
|
|
9891
|
-
break;
|
|
9892
|
-
case "finish":
|
|
9893
|
-
if (chunk.usage) usage = chunk.usage;
|
|
9894
|
-
if (chunk.finishReason) finishReason = chunk.finishReason;
|
|
9895
|
-
if (chunk.model) model = chunk.model;
|
|
9896
|
-
if (chunk.warnings) warnings = chunk.warnings;
|
|
9897
|
-
break;
|
|
9898
|
-
case "error":
|
|
9899
|
-
streamError = chunk.error ?? t("agent_run.unknown_provider_error");
|
|
9900
|
-
break;
|
|
9901
10716
|
}
|
|
10717
|
+
} finally {
|
|
10718
|
+
unlink();
|
|
9902
10719
|
}
|
|
9903
|
-
if (streamError) throw new Error(streamError);
|
|
10720
|
+
if (streamError && !looping) throw new Error(streamError);
|
|
9904
10721
|
if (guard) {
|
|
9905
10722
|
const tail = guard.flush();
|
|
9906
10723
|
if (tail) {
|
|
@@ -9918,7 +10735,8 @@ async function* runTurnStreaming(opts) {
|
|
|
9918
10735
|
return {
|
|
9919
10736
|
text,
|
|
9920
10737
|
...usage ? { usage } : {},
|
|
9921
|
-
finishReason,
|
|
10738
|
+
finishReason: looping ? "looping" : finishReason,
|
|
10739
|
+
...looping ? { loopingInfo: looping.getInfo() ?? "" } : {},
|
|
9922
10740
|
...toolCalls.length > 0 ? { toolCalls } : {},
|
|
9923
10741
|
...reasoningText ? { reasoningText } : {},
|
|
9924
10742
|
...model ? { model } : {},
|
|
@@ -9937,20 +10755,53 @@ async function* runTurnBuffered(opts) {
|
|
|
9937
10755
|
yield { type: "text-delta", text };
|
|
9938
10756
|
}
|
|
9939
10757
|
const { reasoningText: _replaced, ...rest } = result;
|
|
9940
|
-
|
|
10758
|
+
const textLoop = new RepetitionDetector();
|
|
10759
|
+
const reasoningLoop = new RepetitionDetector();
|
|
10760
|
+
textLoop.pushAll(text);
|
|
10761
|
+
if (reasoningText) reasoningLoop.pushAll(reasoningText);
|
|
10762
|
+
let looping;
|
|
10763
|
+
if (textLoop.isLooping()) looping = textLoop;
|
|
10764
|
+
else if (reasoningLoop.isLooping()) looping = reasoningLoop;
|
|
10765
|
+
return {
|
|
10766
|
+
...rest,
|
|
10767
|
+
text,
|
|
10768
|
+
...reasoningText ? { reasoningText } : {},
|
|
10769
|
+
...looping ? { finishReason: "looping", loopingInfo: looping.getInfo() ?? "" } : {}
|
|
10770
|
+
};
|
|
10771
|
+
}
|
|
10772
|
+
function linkAbort(outer, ac) {
|
|
10773
|
+
if (!outer) return () => {
|
|
10774
|
+
};
|
|
10775
|
+
if (outer.aborted) {
|
|
10776
|
+
ac.abort(outer.reason);
|
|
10777
|
+
return () => {
|
|
10778
|
+
};
|
|
10779
|
+
}
|
|
10780
|
+
const onAbort = () => ac.abort(outer.reason);
|
|
10781
|
+
outer.addEventListener("abort", onAbort, { once: true });
|
|
10782
|
+
return () => outer.removeEventListener("abort", onAbort);
|
|
9941
10783
|
}
|
|
9942
10784
|
function filterWhole(guard, text) {
|
|
9943
10785
|
if (!guard || !text) return text;
|
|
9944
10786
|
return guard.push(text) + guard.flush();
|
|
9945
10787
|
}
|
|
10788
|
+
// src/agent/types.ts
|
|
10789
|
+
function isAbortSignal(signal) {
|
|
10790
|
+
return !!signal && typeof signal.addEventListener === "function" && typeof signal.removeEventListener === "function";
|
|
10791
|
+
}
|
|
9946
10792
|
// src/agent/loop.ts
|
|
9947
10793
|
var DEFAULT_CONTEXT_LENGTH = 128e3;
|
|
10794
|
+
function shrank(before, after) {
|
|
10795
|
+
const chars = (list) => list.reduce((sum, m) => sum + m.content.length, 0);
|
|
10796
|
+
return chars(after) < chars(before);
|
|
10797
|
+
}
|
|
10798
|
+
var NO_OUTPUT_TEXT = "(\u65E0\u8F93\u51FA)";
|
|
9948
10799
|
function imageParts(content) {
|
|
9949
10800
|
return typeof content === "string" ? [] : content.filter((p) => p.type === "image");
|
|
9950
10801
|
}
|
|
9951
10802
|
function toolSignatureOf(config, tools) {
|
|
9952
|
-
const direct = tools.map((
|
|
9953
|
-
const declared = (config.codeModeTools?.() ?? []).map((
|
|
10803
|
+
const direct = tools.map((t78) => t78.name).join("\0");
|
|
10804
|
+
const declared = (config.codeModeTools?.() ?? []).map((t78) => t78.name).join("\0");
|
|
9954
10805
|
return declared ? `${direct}|${declared}` : direct;
|
|
9955
10806
|
}
|
|
9956
10807
|
function modelSignatureOf(info) {
|
|
@@ -10110,7 +10961,7 @@ var AgentLoop = class {
|
|
|
10110
10961
|
}
|
|
10111
10962
|
async *runTurns(input, turns) {
|
|
10112
10963
|
const maxTurns = turnLimitOf(input.maxTurns ?? this.config.maxTurns);
|
|
10113
|
-
this.stallDetector.
|
|
10964
|
+
this.stallDetector.reset();
|
|
10114
10965
|
const rawUserText = contentToText(input.userMessage, { imagesCarriedAsParts: true });
|
|
10115
10966
|
const images = imageParts(input.userMessage);
|
|
10116
10967
|
const inputGate = this.config.compliance?.checkInput(rawUserText);
|
|
@@ -10152,6 +11003,7 @@ var AgentLoop = class {
|
|
|
10152
11003
|
let lastPromptTokens = 0;
|
|
10153
11004
|
let lastText = "";
|
|
10154
11005
|
let imagesDroppedReported = false;
|
|
11006
|
+
let overflowRescued = false;
|
|
10155
11007
|
let runUsage;
|
|
10156
11008
|
let lastModel;
|
|
10157
11009
|
const addRun = (u) => {
|
|
@@ -10194,6 +11046,8 @@ var AgentLoop = class {
|
|
|
10194
11046
|
{ role: "user", content: userContent, ...userParts ? { parts: userParts } : {} }
|
|
10195
11047
|
];
|
|
10196
11048
|
const taskNotice = new TaskNoticeTracker(this.config.backgroundTasks);
|
|
11049
|
+
let todoNudged = false;
|
|
11050
|
+
let idleNudged = false;
|
|
10197
11051
|
for (let turn = 1; turn <= maxTurns; turn++) {
|
|
10198
11052
|
if (this.isAborted(input.signal)) {
|
|
10199
11053
|
yield finish("aborted");
|
|
@@ -10214,8 +11068,8 @@ var AgentLoop = class {
|
|
|
10214
11068
|
yield finish("budget-exceeded", stop.breach);
|
|
10215
11069
|
return;
|
|
10216
11070
|
}
|
|
10217
|
-
const
|
|
10218
|
-
if (
|
|
11071
|
+
const notice2 = taskNotice.notice();
|
|
11072
|
+
if (notice2) messages.push({ role: "user", content: notice2 });
|
|
10219
11073
|
this.hooks.fire("turn:before", {
|
|
10220
11074
|
sessionId: input.sessionId,
|
|
10221
11075
|
userMessage: transformed,
|
|
@@ -10232,11 +11086,35 @@ var AgentLoop = class {
|
|
|
10232
11086
|
system = await this.prompt.buildSystem(input.sessionId);
|
|
10233
11087
|
}
|
|
10234
11088
|
if (turn === 1) {
|
|
11089
|
+
const contextLength = this.config.contextLength;
|
|
11090
|
+
let estimated = estimateRunTokens({ system, messages, tools });
|
|
11091
|
+
if (this.config.contextEngine && isPromptOversize(estimated, contextLength)) {
|
|
11092
|
+
const rescued = await this.compressNow(messages, {
|
|
11093
|
+
promptTokens: estimated,
|
|
11094
|
+
totalTokens: totalInput + totalOutput
|
|
11095
|
+
});
|
|
11096
|
+
if (rescued.messages !== messages) {
|
|
11097
|
+
messages = rescued.messages;
|
|
11098
|
+
estimated = estimateRunTokens({ system, messages, tools });
|
|
11099
|
+
yield {
|
|
11100
|
+
type: "notice",
|
|
11101
|
+
code: "context-compacted",
|
|
11102
|
+
text: t("agent_run.context_compacted")
|
|
11103
|
+
};
|
|
11104
|
+
}
|
|
11105
|
+
if (rescued.usage) {
|
|
11106
|
+
addRun(rescued.usage);
|
|
11107
|
+
yield* budget.toEvents(
|
|
11108
|
+
budget.add(rescued.usage, this.rateOf(rescued.model)),
|
|
11109
|
+
runUsage
|
|
11110
|
+
);
|
|
11111
|
+
}
|
|
11112
|
+
}
|
|
10235
11113
|
const role = this.config.role?.();
|
|
10236
11114
|
yield {
|
|
10237
11115
|
type: "run-start",
|
|
10238
11116
|
...role ? { role: { name: role.name, ...role.source ? { source: role.source } : {} } } : {},
|
|
10239
|
-
estimatedTokens:
|
|
11117
|
+
estimatedTokens: estimated
|
|
10240
11118
|
};
|
|
10241
11119
|
if (learned.length > 0) {
|
|
10242
11120
|
this.learnedSkills.splice(0, learned.length);
|
|
@@ -10260,17 +11138,24 @@ var AgentLoop = class {
|
|
|
10260
11138
|
},
|
|
10261
11139
|
runSpan
|
|
10262
11140
|
);
|
|
10263
|
-
|
|
10264
|
-
|
|
11141
|
+
let guard = this.config.compliance?.outputGuard();
|
|
11142
|
+
let reasoningGuard = this.config.compliance?.outputGuard();
|
|
11143
|
+
const requestFor = (msgs) => ({
|
|
11144
|
+
provider: this.config.provider,
|
|
11145
|
+
input: {
|
|
11146
|
+
messages: msgs,
|
|
11147
|
+
tools,
|
|
11148
|
+
system,
|
|
11149
|
+
maxSteps: 1,
|
|
11150
|
+
...isAbortSignal(input.signal) ? { signal: input.signal } : {}
|
|
11151
|
+
},
|
|
11152
|
+
streaming,
|
|
11153
|
+
...guard ? { guard } : {},
|
|
11154
|
+
...reasoningGuard ? { reasoningGuard } : {}
|
|
11155
|
+
});
|
|
10265
11156
|
let result;
|
|
10266
11157
|
try {
|
|
10267
|
-
result = yield* runTurn(
|
|
10268
|
-
provider: this.config.provider,
|
|
10269
|
-
input: { messages, tools, system, maxSteps: 1 },
|
|
10270
|
-
streaming,
|
|
10271
|
-
...guard ? { guard } : {},
|
|
10272
|
-
...reasoningGuard ? { reasoningGuard } : {}
|
|
10273
|
-
});
|
|
11158
|
+
result = yield* runTurn(requestFor(messages));
|
|
10274
11159
|
if (result.usage) {
|
|
10275
11160
|
llmSpan.setAttribute(GEN_AI.USAGE_INPUT_TOKENS, result.usage.inputTokens);
|
|
10276
11161
|
llmSpan.setAttribute(GEN_AI.USAGE_OUTPUT_TOKENS, result.usage.outputTokens);
|
|
@@ -10278,7 +11163,26 @@ var AgentLoop = class {
|
|
|
10278
11163
|
llmSpan.setAttribute("epoch.finish_reason", result.finishReason);
|
|
10279
11164
|
} catch (err) {
|
|
10280
11165
|
llmSpan.recordError(err);
|
|
10281
|
-
|
|
11166
|
+
const engine = this.config.contextEngine;
|
|
11167
|
+
if (overflowRescued || !engine || !isContextOverflow(err)) throw err;
|
|
11168
|
+
overflowRescued = true;
|
|
11169
|
+
const rescued = await this.compressNow(messages, {
|
|
11170
|
+
totalTokens: totalInput + totalOutput
|
|
11171
|
+
});
|
|
11172
|
+
if (rescued.usage) {
|
|
11173
|
+
addRun(rescued.usage);
|
|
11174
|
+
yield* budget.toEvents(budget.add(rescued.usage, this.rateOf(rescued.model)), runUsage);
|
|
11175
|
+
}
|
|
11176
|
+
if (!shrank(messages, rescued.messages)) throw err;
|
|
11177
|
+
messages = rescued.messages;
|
|
11178
|
+
yield {
|
|
11179
|
+
type: "notice",
|
|
11180
|
+
code: "context-compacted",
|
|
11181
|
+
text: t("agent_run.context_compacted")
|
|
11182
|
+
};
|
|
11183
|
+
guard = this.config.compliance?.outputGuard();
|
|
11184
|
+
reasoningGuard = this.config.compliance?.outputGuard();
|
|
11185
|
+
result = yield* runTurn(requestFor(messages));
|
|
10282
11186
|
} finally {
|
|
10283
11187
|
llmSpan.end();
|
|
10284
11188
|
this.telemetry.histogram(METRIC.LLM_CALL_LATENCY, Date.now() - turnStart);
|
|
@@ -10314,6 +11218,16 @@ var AgentLoop = class {
|
|
|
10314
11218
|
this.telemetry.histogram(METRIC.TOKEN_USAGE, usage.outputTokens, {
|
|
10315
11219
|
"epoch.token_type": "output"
|
|
10316
11220
|
});
|
|
11221
|
+
if (usage.cacheHitTokens !== void 0) {
|
|
11222
|
+
this.telemetry.histogram(METRIC.TOKEN_USAGE, usage.cacheHitTokens, {
|
|
11223
|
+
"epoch.token_type": "cache_read"
|
|
11224
|
+
});
|
|
11225
|
+
}
|
|
11226
|
+
if (usage.cacheWriteTokens !== void 0) {
|
|
11227
|
+
this.telemetry.histogram(METRIC.TOKEN_USAGE, usage.cacheWriteTokens, {
|
|
11228
|
+
"epoch.token_type": "cache_write"
|
|
11229
|
+
});
|
|
11230
|
+
}
|
|
10317
11231
|
input.onTurnUsage?.(usage);
|
|
10318
11232
|
addRun(usage);
|
|
10319
11233
|
lastModel = result.model;
|
|
@@ -10343,6 +11257,19 @@ var AgentLoop = class {
|
|
|
10343
11257
|
await this.prompt.syncMemory(userText, msg2);
|
|
10344
11258
|
return;
|
|
10345
11259
|
}
|
|
11260
|
+
if (result.finishReason === "looping") {
|
|
11261
|
+
const msg2 = t("agent_run.looping", { info: result.loopingInfo ?? "" });
|
|
11262
|
+
yield { type: "text-delta", text: msg2 };
|
|
11263
|
+
await this.prompt.syncMemory(userText, msg2);
|
|
11264
|
+
this.hooks.fire("stop", {
|
|
11265
|
+
sessionId: input.sessionId,
|
|
11266
|
+
response: msg2,
|
|
11267
|
+
totalTokens: totalInput + totalOutput,
|
|
11268
|
+
turns
|
|
11269
|
+
});
|
|
11270
|
+
yield finish("looping");
|
|
11271
|
+
return;
|
|
11272
|
+
}
|
|
10346
11273
|
const toolCalls = result.toolCalls ?? [];
|
|
10347
11274
|
const truncated = result.finishReason === "length" && toolCalls.length === 0;
|
|
10348
11275
|
if (truncated) {
|
|
@@ -10375,7 +11302,7 @@ ${finalText}` };
|
|
|
10375
11302
|
);
|
|
10376
11303
|
if (wantContinue && turn < maxTurns) {
|
|
10377
11304
|
messages.push(
|
|
10378
|
-
{ role: "assistant", content: finalText ||
|
|
11305
|
+
{ role: "assistant", content: finalText || NO_OUTPUT_TEXT },
|
|
10379
11306
|
{
|
|
10380
11307
|
role: "user",
|
|
10381
11308
|
content: systemNote(
|
|
@@ -10385,6 +11312,28 @@ ${finalText}` };
|
|
|
10385
11312
|
);
|
|
10386
11313
|
continue;
|
|
10387
11314
|
}
|
|
11315
|
+
if (!todoNudged && turn < maxTurns) {
|
|
11316
|
+
const open = this.config.pendingTodos?.(input.sessionId);
|
|
11317
|
+
if (open !== void 0 && open > 0) {
|
|
11318
|
+
todoNudged = true;
|
|
11319
|
+
messages.push(
|
|
11320
|
+
{ role: "assistant", content: finalText || NO_OUTPUT_TEXT },
|
|
11321
|
+
{
|
|
11322
|
+
role: "user",
|
|
11323
|
+
content: systemNote(unfinishedTodoNote(open))
|
|
11324
|
+
}
|
|
11325
|
+
);
|
|
11326
|
+
continue;
|
|
11327
|
+
}
|
|
11328
|
+
}
|
|
11329
|
+
if (!idleNudged && turn < maxTurns && !truncated && announcedNextStep(finalText)) {
|
|
11330
|
+
idleNudged = true;
|
|
11331
|
+
messages.push(
|
|
11332
|
+
{ role: "assistant", content: finalText || NO_OUTPUT_TEXT },
|
|
11333
|
+
{ role: "user", content: systemNote(announcedButIdleNote()) }
|
|
11334
|
+
);
|
|
11335
|
+
continue;
|
|
11336
|
+
}
|
|
10388
11337
|
this.hooks.fire("stop", {
|
|
10389
11338
|
sessionId: input.sessionId,
|
|
10390
11339
|
response: finalText,
|
|
@@ -10418,7 +11367,10 @@ ${finalText}` };
|
|
|
10418
11367
|
(onOutput) => this.toolExecutor.execute(tc, input.sessionId, input.signal, runSpan, onOutput)
|
|
10419
11368
|
);
|
|
10420
11369
|
executions.push(exec);
|
|
10421
|
-
this.stallDetector.recordAction(exec.toolName, exec.args
|
|
11370
|
+
this.stallDetector.recordAction(exec.toolName, exec.args, {
|
|
11371
|
+
success: exec.success,
|
|
11372
|
+
output: exec.output
|
|
11373
|
+
});
|
|
10422
11374
|
for (const d of exec.diffs ?? []) {
|
|
10423
11375
|
yield {
|
|
10424
11376
|
type: "diff",
|
|
@@ -10543,7 +11495,11 @@ ${finalText}` };
|
|
|
10543
11495
|
}
|
|
10544
11496
|
async maybeCompress(messages, lastPromptTokens, totalTokens2) {
|
|
10545
11497
|
const engine = this.config.contextEngine;
|
|
10546
|
-
if (!engine
|
|
11498
|
+
if (!engine) return { messages };
|
|
11499
|
+
const oversize = isPromptOversize(lastPromptTokens, this.config.contextLength);
|
|
11500
|
+
if (!oversize && !engine.shouldCompress(lastPromptTokens, this.config.contextLength)) {
|
|
11501
|
+
return { messages };
|
|
11502
|
+
}
|
|
10547
11503
|
return this.compressNow(messages, { totalTokens: totalTokens2, promptTokens: lastPromptTokens });
|
|
10548
11504
|
}
|
|
10549
11505
|
async compactNow(messages, instruction) {
|
|
@@ -10643,6 +11599,14 @@ var CompressionSchema = z.object({
|
|
|
10643
11599
|
enabled: z.boolean().default(true),
|
|
10644
11600
|
threshold: z.number().gt(0).max(1).default(DEFAULT_COMPRESSION_THRESHOLD)
|
|
10645
11601
|
});
|
|
11602
|
+
var DEFAULT_MEMORY_CHAR_LIMIT = 2e4;
|
|
11603
|
+
var DEFAULT_USER_CHAR_LIMIT = 6e3;
|
|
11604
|
+
var DEFAULT_MEMORY_INDEX_CHAR_LIMIT = 4e3;
|
|
11605
|
+
var MemorySchema = z.object({
|
|
11606
|
+
memoryCharLimit: z.int().min(1).default(DEFAULT_MEMORY_CHAR_LIMIT),
|
|
11607
|
+
userCharLimit: z.int().min(1).default(DEFAULT_USER_CHAR_LIMIT),
|
|
11608
|
+
indexCharLimit: z.int().min(1).default(DEFAULT_MEMORY_INDEX_CHAR_LIMIT)
|
|
11609
|
+
});
|
|
10646
11610
|
var SandboxSchema = z.object({
|
|
10647
11611
|
terminal: z.boolean().default(true)
|
|
10648
11612
|
});
|
|
@@ -10686,7 +11650,7 @@ var StatusLineSchema = z.object({
|
|
|
10686
11650
|
command: z.string().min(1).optional(),
|
|
10687
11651
|
intervalMs: z.int().min(500).max(3e5).optional()
|
|
10688
11652
|
});
|
|
10689
|
-
var
|
|
11653
|
+
var DisplaySchema2 = z.object({
|
|
10690
11654
|
language: z.enum(LANGS).optional()
|
|
10691
11655
|
});
|
|
10692
11656
|
var SidebarMenuItemSchema = z.object({
|
|
@@ -10754,6 +11718,7 @@ var EpochConfigSchema = z.object({
|
|
|
10754
11718
|
tools: ToolsSchema.prefault({}),
|
|
10755
11719
|
skills: SkillsSchema.optional(),
|
|
10756
11720
|
compression: CompressionSchema.prefault({}),
|
|
11721
|
+
memory: MemorySchema.prefault({}),
|
|
10757
11722
|
artifacts: ArtifactsSchema.prefault({}),
|
|
10758
11723
|
budget: BudgetSchema.optional(),
|
|
10759
11724
|
compliance: ComplianceSchema.optional(),
|
|
@@ -10762,7 +11727,7 @@ var EpochConfigSchema = z.object({
|
|
|
10762
11727
|
search: SearchSchema.optional(),
|
|
10763
11728
|
sessionSearch: SessionSearchSchema.optional(),
|
|
10764
11729
|
statusLine: StatusLineSchema.optional(),
|
|
10765
|
-
display:
|
|
11730
|
+
display: DisplaySchema2.optional(),
|
|
10766
11731
|
brand: z.string().min(1).optional(),
|
|
10767
11732
|
sidebarMenu: z.array(SidebarMenuItemSchema).min(1).refine((items) => new Set(items.map((item) => item.id)).size === items.length, {
|
|
10768
11733
|
message: "sidebarMenu: id must be unique"
|
|
@@ -11447,64 +12412,6 @@ function fileStamp(path) {
|
|
|
11447
12412
|
function parseKeys(raw) {
|
|
11448
12413
|
return raw.split(",").map((s) => s.trim()).filter(Boolean).map((k) => ({ apiKey: k }));
|
|
11449
12414
|
}
|
|
11450
|
-
// src/provider/errors.ts
|
|
11451
|
-
var ProviderError = class extends Error {
|
|
11452
|
-
constructor(message, code, recoverable, recovery = "abort") {
|
|
11453
|
-
super(message);
|
|
11454
|
-
this.code = code;
|
|
11455
|
-
this.recoverable = recoverable;
|
|
11456
|
-
this.recovery = recovery;
|
|
11457
|
-
this.name = "ProviderError";
|
|
11458
|
-
}
|
|
11459
|
-
code;
|
|
11460
|
-
recoverable;
|
|
11461
|
-
recovery;
|
|
11462
|
-
};
|
|
11463
|
-
var PROVIDER_NO_CREDENTIAL = 1001;
|
|
11464
|
-
function extractStatus(err) {
|
|
11465
|
-
if (!err || typeof err !== "object") return void 0;
|
|
11466
|
-
const e = err;
|
|
11467
|
-
for (const candidate of [e.statusCode, e.status, e.response?.status]) {
|
|
11468
|
-
if (typeof candidate === "number" && candidate >= 100 && candidate < 600) return candidate;
|
|
11469
|
-
}
|
|
11470
|
-
return void 0;
|
|
11471
|
-
}
|
|
11472
|
-
function extractStatusFromMessage(msg) {
|
|
11473
|
-
const leading = msg.match(/^\s*\[?(4\d{2}|5\d{2})\b/);
|
|
11474
|
-
if (leading?.[1]) return Number(leading[1]);
|
|
11475
|
-
const labelled = msg.match(/(?:status(?:\s*code)?|http|code)\D{0,3}(4\d{2}|5\d{2})\b/i);
|
|
11476
|
-
if (labelled?.[1]) return Number(labelled[1]);
|
|
11477
|
-
return void 0;
|
|
11478
|
-
}
|
|
11479
|
-
function classifyProviderError(err) {
|
|
11480
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
11481
|
-
const status = extractStatus(err);
|
|
11482
|
-
if (/context.?length|context.?window|maximum.*token|token.?limit|too.?many.?tokens|prompt.*too.?long|reduce.*(length|tokens)/i.test(
|
|
11483
|
-
msg
|
|
11484
|
-
))
|
|
11485
|
-
return { code: 1030, recovery: "abort" };
|
|
11486
|
-
if (/invalid.*api.?key|unauthorized|authentication|permission.?denied|forbidden/i.test(msg))
|
|
11487
|
-
return { code: 1001, recovery: "rotate_credential" };
|
|
11488
|
-
if (/billing|quota.*exceeded|insufficient.*(balance|credit)|payment.?required/i.test(msg))
|
|
11489
|
-
return { code: 1012, recovery: "switch_provider" };
|
|
11490
|
-
if (/rate.?limit|too.?many.?requests/i.test(msg)) return { code: 1010, recovery: "retry" };
|
|
11491
|
-
if (/model.?not.?found|model.*does.*not.*exist|unknown.?model|no.?such.?model/i.test(msg))
|
|
11492
|
-
return { code: 1040, recovery: "switch_model" };
|
|
11493
|
-
if (/overloaded|server.?error|bad.?gateway|service.?unavailable/i.test(msg))
|
|
11494
|
-
return { code: 1020, recovery: "retry" };
|
|
11495
|
-
if (/timeout|timed.?out|etimedout|econnreset|enotfound|socket.?hang.?up/i.test(msg))
|
|
11496
|
-
return { code: 1041, recovery: "retry" };
|
|
11497
|
-
const code = status ?? extractStatusFromMessage(msg);
|
|
11498
|
-
if (code !== void 0) {
|
|
11499
|
-
if (code === 401 || code === 403) return { code: 1001, recovery: "rotate_credential" };
|
|
11500
|
-
if (code === 402) return { code: 1012, recovery: "switch_provider" };
|
|
11501
|
-
if (code === 404) return { code: 1040, recovery: "switch_model" };
|
|
11502
|
-
if (code === 429) return { code: 1010, recovery: "retry" };
|
|
11503
|
-
if (code >= 500) return { code: 1020, recovery: "retry" };
|
|
11504
|
-
if (code >= 400) return { code: 1050, recovery: "abort" };
|
|
11505
|
-
}
|
|
11506
|
-
return { code: 9999, recovery: "retry" };
|
|
11507
|
-
}
|
|
11508
12415
|
// src/provider/fallback.ts
|
|
11509
12416
|
var DEFAULT_MODELS = {
|
|
11510
12417
|
openai: "gpt-4o-mini",
|
|
@@ -12256,6 +13163,7 @@ var ProviderRouter = class {
|
|
|
12256
13163
|
const { code, recovery } = classifyProviderError(err);
|
|
12257
13164
|
const message = err instanceof Error ? err.message : String(err);
|
|
12258
13165
|
const stay = { kind: "continue", ct: state.ct, cm: state.cm };
|
|
13166
|
+
if (code === PROVIDER_ABORTED) return { kind: "give-up", message, code };
|
|
12259
13167
|
if (recovery === "rotate_credential" && this.rotateCredential())
|
|
12260
13168
|
return { ...stay, attempt: state.attempt + 1 };
|
|
12261
13169
|
if (recovery === "retry") {
|
|
@@ -12326,6 +13234,7 @@ var ProviderRouter = class {
|
|
|
12326
13234
|
system: input.system,
|
|
12327
13235
|
stopWhen: isStepCount(input.maxSteps ?? 1),
|
|
12328
13236
|
...input.maxTokens ? { maxOutputTokens: input.maxTokens } : {},
|
|
13237
|
+
...input.signal ? { abortSignal: input.signal } : {},
|
|
12329
13238
|
output,
|
|
12330
13239
|
providerOptions: toSdkProviderOptions(input.providerOptions)
|
|
12331
13240
|
});
|
|
@@ -12382,6 +13291,7 @@ var ProviderRouter = class {
|
|
|
12382
13291
|
system: input.system,
|
|
12383
13292
|
stopWhen: isStepCount(input.maxSteps ?? 1),
|
|
12384
13293
|
...input.maxTokens ? { maxOutputTokens: input.maxTokens } : {},
|
|
13294
|
+
...input.signal ? { abortSignal: input.signal } : {},
|
|
12385
13295
|
output,
|
|
12386
13296
|
providerOptions: toSdkProviderOptions(input.providerOptions)
|
|
12387
13297
|
});
|
|
@@ -12661,6 +13571,9 @@ var SETTING_WRITE_KEYS = [
|
|
|
12661
13571
|
"contextLength",
|
|
12662
13572
|
"compression.enabled",
|
|
12663
13573
|
"compression.threshold",
|
|
13574
|
+
"memory.memoryCharLimit",
|
|
13575
|
+
"memory.userCharLimit",
|
|
13576
|
+
"memory.indexCharLimit",
|
|
12664
13577
|
"budget.maxCostUsd",
|
|
12665
13578
|
"budget.maxTokens",
|
|
12666
13579
|
"budget.warnAtPercent",
|
|
@@ -12719,6 +13632,9 @@ var VALUE_SCHEMA = {
|
|
|
12719
13632
|
contextLength: EpochConfigSchema.shape.contextLength.unwrap(),
|
|
12720
13633
|
"compression.enabled": EpochConfigSchema.shape.compression.unwrap().shape.enabled.unwrap(),
|
|
12721
13634
|
"compression.threshold": EpochConfigSchema.shape.compression.unwrap().shape.threshold.unwrap(),
|
|
13635
|
+
"memory.memoryCharLimit": EpochConfigSchema.shape.memory.unwrap().shape.memoryCharLimit.unwrap(),
|
|
13636
|
+
"memory.userCharLimit": EpochConfigSchema.shape.memory.unwrap().shape.userCharLimit.unwrap(),
|
|
13637
|
+
"memory.indexCharLimit": EpochConfigSchema.shape.memory.unwrap().shape.indexCharLimit.unwrap(),
|
|
12722
13638
|
"budget.maxCostUsd": EpochConfigSchema.shape.budget.unwrap().shape.maxCostUsd.unwrap(),
|
|
12723
13639
|
"budget.maxTokens": EpochConfigSchema.shape.budget.unwrap().shape.maxTokens.unwrap(),
|
|
12724
13640
|
"budget.warnAtPercent": EpochConfigSchema.shape.budget.unwrap().shape.warnAtPercent.unwrap(),
|
|
@@ -13131,16 +14047,16 @@ function buildSearchQuery(query) {
|
|
|
13131
14047
|
}
|
|
13132
14048
|
const latinPart = cleaned.replace(CJK_RUN, " ");
|
|
13133
14049
|
for (const w of latinPart.split(/\s+/)) {
|
|
13134
|
-
const
|
|
13135
|
-
if (
|
|
14050
|
+
const t78 = w.trim();
|
|
14051
|
+
if (t78.length >= 2) terms.add(t78);
|
|
13136
14052
|
}
|
|
13137
14053
|
if (terms.size === 0) return null;
|
|
13138
|
-
return [...terms].map((
|
|
14054
|
+
return [...terms].map((t78) => `"${t78.replace(/"/g, "")}"`).join(" OR ");
|
|
13139
14055
|
}
|
|
13140
14056
|
// src/memory/db.ts
|
|
13141
|
-
var
|
|
14057
|
+
var NS2 = "memory";
|
|
13142
14058
|
var FTS_SCHEMA_VERSION = 2;
|
|
13143
|
-
var
|
|
14059
|
+
var MIGRATIONS2 = [
|
|
13144
14060
|
{
|
|
13145
14061
|
id: "memory-baseline",
|
|
13146
14062
|
version: 1,
|
|
@@ -13177,7 +14093,7 @@ var MemoryDB = class {
|
|
|
13177
14093
|
constructor(dbPath) {
|
|
13178
14094
|
this.path = dbPath;
|
|
13179
14095
|
this.db = openDatabase(this.path);
|
|
13180
|
-
migrate(this.db,
|
|
14096
|
+
migrate(this.db, NS2, MIGRATIONS2);
|
|
13181
14097
|
this.migrateFts();
|
|
13182
14098
|
for (const target of ["memory", "user"]) {
|
|
13183
14099
|
const row = this.db.prepare(
|
|
@@ -13348,12 +14264,12 @@ var MemoryDB = class {
|
|
|
13348
14264
|
}
|
|
13349
14265
|
};
|
|
13350
14266
|
// src/memory/types.ts
|
|
13351
|
-
var DEFAULT_CONFIG = {
|
|
13352
|
-
memoryCharLimit: 2200,
|
|
13353
|
-
userCharLimit: 1375
|
|
13354
|
-
};
|
|
13355
14267
|
function defaultMemoryConfig() {
|
|
13356
|
-
return {
|
|
14268
|
+
return {
|
|
14269
|
+
memoryCharLimit: DEFAULT_MEMORY_CHAR_LIMIT,
|
|
14270
|
+
userCharLimit: DEFAULT_USER_CHAR_LIMIT,
|
|
14271
|
+
indexCharLimit: DEFAULT_MEMORY_INDEX_CHAR_LIMIT
|
|
14272
|
+
};
|
|
13357
14273
|
}
|
|
13358
14274
|
// src/memory/manager.ts
|
|
13359
14275
|
var DB_PATH = join(homedir(), ".epoch", "memory.db");
|
|
@@ -13361,6 +14277,8 @@ var MEM_DIR = join(homedir(), ".epoch", "memories");
|
|
|
13361
14277
|
var ENTRY_DELIMITER = "\n\xA7 ";
|
|
13362
14278
|
var MAX_ENTRY_CHARS = 4e3;
|
|
13363
14279
|
var MAX_CONSOLIDATION_FAILURES_PER_TURN = 3;
|
|
14280
|
+
var SHORT_ID_HEX = 6;
|
|
14281
|
+
var INDEX_SUMMARY_CHARS = 80;
|
|
13364
14282
|
var INJECTION_PATTERNS = [
|
|
13365
14283
|
{ pattern: /<\|im_start\|>/i, label: "role injection" },
|
|
13366
14284
|
{ pattern: /<\|im_end\|>/i, label: "role injection" },
|
|
@@ -13370,27 +14288,36 @@ var INJECTION_PATTERNS = [
|
|
|
13370
14288
|
{ pattern: /^assistant:/im, label: "role prefix" },
|
|
13371
14289
|
{ pattern: /^user:/im, label: "role prefix" }
|
|
13372
14290
|
];
|
|
14291
|
+
function contentKey(target, content) {
|
|
14292
|
+
return `${target}\0${content}`;
|
|
14293
|
+
}
|
|
14294
|
+
function stripUndefined(obj) {
|
|
14295
|
+
if (!obj) return {};
|
|
14296
|
+
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== void 0));
|
|
14297
|
+
}
|
|
13373
14298
|
var MemoryManager = class {
|
|
13374
14299
|
db;
|
|
13375
14300
|
config;
|
|
13376
14301
|
memDir;
|
|
13377
|
-
snapshot =
|
|
14302
|
+
snapshot = "";
|
|
14303
|
+
ids = /* @__PURE__ */ new Map();
|
|
14304
|
+
idByContent = /* @__PURE__ */ new Map();
|
|
13378
14305
|
initialized = false;
|
|
13379
14306
|
consolidationFailures = 0;
|
|
13380
14307
|
reviewIssues = [];
|
|
13381
|
-
constructor(dbPath, memDir) {
|
|
14308
|
+
constructor(dbPath, memDir, config) {
|
|
13382
14309
|
const dp = dbPath ?? DB_PATH;
|
|
13383
14310
|
const md = memDir ?? MEM_DIR;
|
|
13384
14311
|
this.memDir = md;
|
|
13385
14312
|
mkdirSync(dirname(dp), { recursive: true });
|
|
13386
14313
|
mkdirSync(this.memDir, { recursive: true });
|
|
13387
14314
|
this.db = new MemoryDB(dp);
|
|
13388
|
-
this.config = defaultMemoryConfig();
|
|
14315
|
+
this.config = { ...defaultMemoryConfig(), ...stripUndefined(config) };
|
|
13389
14316
|
}
|
|
13390
14317
|
async initialize(_sessionId) {
|
|
13391
14318
|
this.initialized = true;
|
|
13392
14319
|
await this.reloadFromDisk();
|
|
13393
|
-
return this.
|
|
14320
|
+
return this.snapshot;
|
|
13394
14321
|
}
|
|
13395
14322
|
async prefetch(userQuery) {
|
|
13396
14323
|
if (!this.initialized) return "";
|
|
@@ -13412,10 +14339,24 @@ var MemoryManager = class {
|
|
|
13412
14339
|
const safe = deduped.map((c) => this.scanInjection(c)).filter((c) => c.length > 0);
|
|
13413
14340
|
this.syncTarget(target, safe);
|
|
13414
14341
|
}
|
|
13415
|
-
this.
|
|
13416
|
-
|
|
13417
|
-
|
|
13418
|
-
|
|
14342
|
+
this.assignIds();
|
|
14343
|
+
this.snapshot = this.renderIndexBlock();
|
|
14344
|
+
}
|
|
14345
|
+
assignIds() {
|
|
14346
|
+
this.ids.clear();
|
|
14347
|
+
this.idByContent.clear();
|
|
14348
|
+
for (const target of ["memory", "user"]) {
|
|
14349
|
+
const prefix = target === "memory" ? "m" : "u";
|
|
14350
|
+
for (const content of this.db.getAllContent(target)) {
|
|
14351
|
+
const hash = createHash("sha256").update(content).digest("hex");
|
|
14352
|
+
let id = `${prefix}:${hash.slice(0, SHORT_ID_HEX)}`;
|
|
14353
|
+
for (let len = SHORT_ID_HEX + 4; this.ids.has(id) && len <= hash.length; len += 4) {
|
|
14354
|
+
id = `${prefix}:${hash.slice(0, len)}`;
|
|
14355
|
+
}
|
|
14356
|
+
this.ids.set(id, { target, content });
|
|
14357
|
+
this.idByContent.set(contentKey(target, content), id);
|
|
14358
|
+
}
|
|
14359
|
+
}
|
|
13419
14360
|
}
|
|
13420
14361
|
syncTarget(target, desired) {
|
|
13421
14362
|
const existing = new Map(this.db.getAll(target).map((e) => [e.content, e]));
|
|
@@ -13446,6 +14387,7 @@ var MemoryManager = class {
|
|
|
13446
14387
|
async flushToDisk() {
|
|
13447
14388
|
writeFileSync(join(this.memDir, "MEMORY.md"), this.renderFile("memory"), "utf-8");
|
|
13448
14389
|
writeFileSync(join(this.memDir, "USER.md"), this.renderFile("user"), "utf-8");
|
|
14390
|
+
this.assignIds();
|
|
13449
14391
|
}
|
|
13450
14392
|
renderFile(target) {
|
|
13451
14393
|
const entries = this.db.getAllContent(target);
|
|
@@ -13456,7 +14398,7 @@ var MemoryManager = class {
|
|
|
13456
14398
|
const results = this.db.searchFts(query, void 0, limit);
|
|
13457
14399
|
for (const r of results) this.db.markAccessed(r.id);
|
|
13458
14400
|
return results.map((r) => ({
|
|
13459
|
-
id: r.id,
|
|
14401
|
+
id: this.idByContent.get(contentKey(r.target, r.content)) ?? r.id,
|
|
13460
14402
|
content: r.content,
|
|
13461
14403
|
target: r.target,
|
|
13462
14404
|
score: r.score
|
|
@@ -13465,6 +14407,22 @@ var MemoryManager = class {
|
|
|
13465
14407
|
async keywordSearch(query, limit) {
|
|
13466
14408
|
return this.search(query, limit);
|
|
13467
14409
|
}
|
|
14410
|
+
async read(ids) {
|
|
14411
|
+
const found = [];
|
|
14412
|
+
const missing = [];
|
|
14413
|
+
for (const raw of ids) {
|
|
14414
|
+
const id = raw.trim();
|
|
14415
|
+
const hit = this.ids.get(id);
|
|
14416
|
+
if (!hit) {
|
|
14417
|
+
missing.push(id);
|
|
14418
|
+
continue;
|
|
14419
|
+
}
|
|
14420
|
+
found.push({ id, target: hit.target, content: hit.content });
|
|
14421
|
+
const entry = this.db.findByContent(hit.target, hit.content);
|
|
14422
|
+
if (entry) this.db.markAccessed(entry.id);
|
|
14423
|
+
}
|
|
14424
|
+
return { found, missing };
|
|
14425
|
+
}
|
|
13468
14426
|
getAllEntries() {
|
|
13469
14427
|
return [
|
|
13470
14428
|
...this.db.getAllContent("memory").map((c) => ({ content: c })),
|
|
@@ -13516,9 +14474,9 @@ var MemoryManager = class {
|
|
|
13516
14474
|
return { success: true, done: true };
|
|
13517
14475
|
}
|
|
13518
14476
|
async replace(target, oldText, content) {
|
|
13519
|
-
const existing = this.
|
|
14477
|
+
const existing = this.locate(target, oldText);
|
|
13520
14478
|
if (!existing) {
|
|
13521
|
-
return { success: false, done: true, error:
|
|
14479
|
+
return { success: false, done: true, error: this.notFoundHint(target, oldText) };
|
|
13522
14480
|
}
|
|
13523
14481
|
const budget = this.getBudget()[target];
|
|
13524
14482
|
const delta = content.length - existing.charLength;
|
|
@@ -13533,7 +14491,7 @@ var MemoryManager = class {
|
|
|
13533
14491
|
consolidateHint: this.buildConsolidateHint(target)
|
|
13534
14492
|
};
|
|
13535
14493
|
}
|
|
13536
|
-
this.db.replace(target,
|
|
14494
|
+
this.db.replace(target, existing.content, {
|
|
13537
14495
|
...existing,
|
|
13538
14496
|
content,
|
|
13539
14497
|
charLength: content.length,
|
|
@@ -13544,8 +14502,24 @@ var MemoryManager = class {
|
|
|
13544
14502
|
return { success: true, done: true };
|
|
13545
14503
|
}
|
|
13546
14504
|
async remove(target, oldText) {
|
|
13547
|
-
this.
|
|
14505
|
+
const existing = this.locate(target, oldText);
|
|
14506
|
+
if (!existing) return false;
|
|
14507
|
+
this.db.remove(target, existing.content);
|
|
14508
|
+
this.consolidationFailures = 0;
|
|
13548
14509
|
await this.flushToDisk();
|
|
14510
|
+
return true;
|
|
14511
|
+
}
|
|
14512
|
+
locate(target, ref) {
|
|
14513
|
+
const key = ref.trim();
|
|
14514
|
+
const byId = this.ids.get(key);
|
|
14515
|
+
if (byId && byId.target === target) {
|
|
14516
|
+
return this.db.findByContent(target, byId.content);
|
|
14517
|
+
}
|
|
14518
|
+
return this.db.findByContent(target, key);
|
|
14519
|
+
}
|
|
14520
|
+
notFoundHint(target, ref) {
|
|
14521
|
+
const looksLikeId = /^[mu]:[0-9a-f]{6,}$/.test(ref.trim());
|
|
14522
|
+
return looksLikeId ? `No entry with id "${ref}" in ${target}. The index is frozen at session start; this entry may have been changed or removed since. Use memory(action="search") to find it again.` : `No entry in ${target} matches that exact text. \u26A0\uFE0F old_text must be the FULL text of an entry \u2014 the MEMORY INDEX only shows a truncated first line, so copying from it will not match. Pass the entry's id instead (e.g. id="m:a1b2c3"), or memory(action="read") it first to get the full text.`;
|
|
13549
14523
|
}
|
|
13550
14524
|
getBudget() {
|
|
13551
14525
|
const mem = this.db.getBudget("memory");
|
|
@@ -13602,35 +14576,101 @@ var MemoryManager = class {
|
|
|
13602
14576
|
}
|
|
13603
14577
|
return content;
|
|
13604
14578
|
}
|
|
13605
|
-
|
|
13606
|
-
|
|
13607
|
-
const content
|
|
13608
|
-
|
|
13609
|
-
|
|
14579
|
+
indexEntries(target) {
|
|
14580
|
+
const out = [];
|
|
14581
|
+
for (const content of this.db.getAllContent(target)) {
|
|
14582
|
+
const id = this.idByContent.get(contentKey(target, content));
|
|
14583
|
+
if (!id) continue;
|
|
14584
|
+
const firstLine4 = (content.split("\n")[0] ?? "").trim();
|
|
14585
|
+
const summary = firstLine4.length > INDEX_SUMMARY_CHARS ? `${firstLine4.slice(0, INDEX_SUMMARY_CHARS)}\u2026` : firstLine4;
|
|
14586
|
+
out.push({ id, target, summary, charLength: content.length });
|
|
14587
|
+
}
|
|
14588
|
+
return out;
|
|
14589
|
+
}
|
|
14590
|
+
renderIndexBlock() {
|
|
14591
|
+
const all = [...this.indexEntries("memory"), ...this.indexEntries("user")];
|
|
14592
|
+
if (all.length === 0) return "";
|
|
14593
|
+
const order = new Map(all.map((e, i) => [e.id, i]));
|
|
14594
|
+
const heat = /* @__PURE__ */ new Map();
|
|
14595
|
+
for (const row of this.db.getAll()) {
|
|
14596
|
+
const id = this.idByContent.get(contentKey(row.target, row.content));
|
|
14597
|
+
if (id) heat.set(id, { count: row.accessCount, at: row.lastAccessedAt?.getTime() ?? 0 });
|
|
14598
|
+
}
|
|
14599
|
+
const rank = (id) => heat.get(id) ?? { count: 0, at: 0 };
|
|
14600
|
+
const ranked = [...all].sort(
|
|
14601
|
+
(a, b) => rank(b.id).count - rank(a.id).count || rank(b.id).at - rank(a.id).at || order.get(a.id) - order.get(b.id)
|
|
14602
|
+
);
|
|
14603
|
+
const kept = [];
|
|
14604
|
+
let used = 0;
|
|
14605
|
+
for (const e of ranked) {
|
|
14606
|
+
const line = this.indexLine(e);
|
|
14607
|
+
if (used + line.length + 1 > this.config.indexCharLimit) continue;
|
|
14608
|
+
used += line.length + 1;
|
|
14609
|
+
kept.push(e);
|
|
14610
|
+
}
|
|
14611
|
+
kept.sort((a, b) => order.get(a.id) - order.get(b.id));
|
|
14612
|
+
const omitted = all.length - kept.length;
|
|
14613
|
+
const sections = [];
|
|
14614
|
+
for (const target of ["memory", "user"]) {
|
|
14615
|
+
const rows = kept.filter((e) => e.target === target);
|
|
14616
|
+
if (rows.length === 0) continue;
|
|
14617
|
+
const budget = this.getBudget()[target];
|
|
14618
|
+
const label = target === "user" ? "USER PROFILE (who the user is)" : "MEMORY (your personal notes)";
|
|
14619
|
+
sections.push(
|
|
14620
|
+
[
|
|
14621
|
+
`${label} [${rows.length} entries \xB7 ${budget.current.toLocaleString()}/${budget.limit.toLocaleString()} chars]`,
|
|
14622
|
+
...rows.map((e) => this.indexLine(e))
|
|
14623
|
+
].join("\n")
|
|
14624
|
+
);
|
|
14625
|
+
}
|
|
13610
14626
|
const separator = "\u2550".repeat(46);
|
|
13611
|
-
const note = "[System note:
|
|
13612
|
-
|
|
13613
|
-
|
|
13614
|
-
|
|
13615
|
-
|
|
13616
|
-
|
|
13617
|
-
|
|
13618
|
-
|
|
13619
|
-
|
|
13620
|
-
|
|
13621
|
-
|
|
13622
|
-
|
|
13623
|
-
|
|
13624
|
-
|
|
14627
|
+
const note = "[System note: This is an INDEX of your stored memory, NOT new user input. Only one-line summaries are listed here \u2014 the full text is not in this prompt.]";
|
|
14628
|
+
const how = [
|
|
14629
|
+
'To read an entry in full: memory(action="read", ids=["m:a1b2c3"]).',
|
|
14630
|
+
'To find something not listed: memory(action="search", query="...").'
|
|
14631
|
+
];
|
|
14632
|
+
if (omitted > 0) {
|
|
14633
|
+
how.unshift(
|
|
14634
|
+
`\u26A0\uFE0F ${omitted} more ${omitted === 1 ? "entry is" : "entries are"} stored but NOT listed above (index budget). Do not conclude something is absent from memory without searching first.`
|
|
14635
|
+
);
|
|
14636
|
+
}
|
|
14637
|
+
return [
|
|
14638
|
+
separator,
|
|
14639
|
+
"MEMORY INDEX",
|
|
14640
|
+
separator,
|
|
14641
|
+
note,
|
|
14642
|
+
"<memory-index>",
|
|
14643
|
+
sections.join("\n\n"),
|
|
14644
|
+
"</memory-index>",
|
|
14645
|
+
...how
|
|
14646
|
+
].join("\n");
|
|
14647
|
+
}
|
|
14648
|
+
indexLine(e) {
|
|
14649
|
+
return `${e.id} ${e.summary} (${e.charLength} chars)`;
|
|
13625
14650
|
}
|
|
13626
14651
|
buildConsolidateHint(target) {
|
|
13627
14652
|
const budget = this.getBudget()[target];
|
|
14653
|
+
const heat = /* @__PURE__ */ new Map();
|
|
14654
|
+
for (const row of this.db.getAll(target)) {
|
|
14655
|
+
const id = this.idByContent.get(contentKey(row.target, row.content));
|
|
14656
|
+
if (id) heat.set(id, row.accessCount);
|
|
14657
|
+
}
|
|
14658
|
+
const candidates2 = this.indexEntries(target).sort((a, b) => (heat.get(a.id) ?? 0) - (heat.get(b.id) ?? 0) || b.charLength - a.charLength).slice(0, 5);
|
|
13628
14659
|
return [
|
|
13629
14660
|
`Memory at ${budget.current.toLocaleString()}/${budget.limit.toLocaleString()} chars (${budget.percent}%) for ${target}. Cannot add new entry.`,
|
|
13630
|
-
"To make room, use replace() to combine or shorten existing entries. For example:",
|
|
13631
|
-
"- Merge '\u7528\u6237\u7528 pnpm' and '\u7528\u6237\u7528 pnpm v9' into '\u7528\u6237\u4F7F\u7528 pnpm v9'",
|
|
13632
|
-
"- Shorten verbose entries to their essence",
|
|
13633
14661
|
"",
|
|
14662
|
+
"To make room, work by id \u2014 NOT by old_text. The MEMORY INDEX only shows a truncated",
|
|
14663
|
+
"first line, so text copied from it will never match an entry exactly.",
|
|
14664
|
+
"",
|
|
14665
|
+
` memory(action="remove", target="${target}", id="m:a1b2c3") drop an obsolete entry`,
|
|
14666
|
+
` memory(action="replace", target="${target}", id="m:a1b2c3", content="...") shorten or merge one`,
|
|
14667
|
+
' memory(action="read", ids=["m:a1b2c3"]) see the full text first',
|
|
14668
|
+
"",
|
|
14669
|
+
...candidates2.length > 0 ? [
|
|
14670
|
+
"Least-used entries in this store (cheapest to drop or shorten):",
|
|
14671
|
+
...candidates2.map((e) => ` ${e.id} ${e.summary} (${e.charLength} chars)`),
|
|
14672
|
+
""
|
|
14673
|
+
] : [],
|
|
13634
14674
|
`Per-turn consolidation limit: ${MAX_CONSOLIDATION_FAILURES_PER_TURN} attempts. After that, stop and proceed.`
|
|
13635
14675
|
].join("\n");
|
|
13636
14676
|
}
|
|
@@ -14115,7 +15155,7 @@ function encodeTurnFacts(facts) {
|
|
|
14115
15155
|
return facts ? JSON.stringify(facts) : null;
|
|
14116
15156
|
}
|
|
14117
15157
|
// src/session/db.ts
|
|
14118
|
-
var
|
|
15158
|
+
var NS3 = "session";
|
|
14119
15159
|
var log2 = createLogger("session-db");
|
|
14120
15160
|
var META_COL_MAP = {
|
|
14121
15161
|
displayName: "display_name",
|
|
@@ -14401,13 +15441,13 @@ var SessionDB = class {
|
|
|
14401
15441
|
constructor(dbPath$1) {
|
|
14402
15442
|
this.path = dbPath$1 ?? dbPath();
|
|
14403
15443
|
this.db = openDatabase(this.path);
|
|
14404
|
-
migrate(this.db,
|
|
15444
|
+
migrate(this.db, NS3, SESSION_MIGRATIONS);
|
|
14405
15445
|
this.migrateFtsIndex();
|
|
14406
15446
|
this.refreshMsgId();
|
|
14407
15447
|
}
|
|
14408
15448
|
migrateFtsIndex() {
|
|
14409
15449
|
try {
|
|
14410
|
-
migrate(this.db,
|
|
15450
|
+
migrate(this.db, NS3, FTS_EXTERNAL_MIGRATIONS);
|
|
14411
15451
|
} catch (err) {
|
|
14412
15452
|
log2.warn(
|
|
14413
15453
|
"FTS index rebuild (v8) failed; keeping the legacy schema for now (content stored three times, ~36% larger). Will retry on next start.",
|
|
@@ -14892,7 +15932,7 @@ function checkFtsHealth(db) {
|
|
|
14892
15932
|
messages,
|
|
14893
15933
|
tables,
|
|
14894
15934
|
missingTriggers,
|
|
14895
|
-
healthy: missingTriggers.length === 0 && tables.every((
|
|
15935
|
+
healthy: missingTriggers.length === 0 && tables.every((t78) => t78.error === null && t78.drift === null && t78.rows === messages),
|
|
14896
15936
|
indexBytes: indexBytesOf(db),
|
|
14897
15937
|
space: spaceOf(db)
|
|
14898
15938
|
};
|
|
@@ -15707,18 +16747,7 @@ function checkCodeExec(isolation) {
|
|
|
15707
16747
|
}
|
|
15708
16748
|
function checkAuto(operation, isolation) {
|
|
15709
16749
|
if (operation.type === "code_exec") return checkCodeExec(isolation);
|
|
15710
|
-
|
|
15711
|
-
if (operation.type === "file_write") return { allowed: true, code: "level-default" };
|
|
15712
|
-
if (operation.type === "network") return { allowed: true, code: "level-default" };
|
|
15713
|
-
if (operation.type === "command" && isReadOnlyCommand(operation.detail)) {
|
|
15714
|
-
return { allowed: true, code: "level-default" };
|
|
15715
|
-
}
|
|
15716
|
-
return {
|
|
15717
|
-
allowed: false,
|
|
15718
|
-
requireApproval: true,
|
|
15719
|
-
reason: t("permission_reason.write_command", { command: operation.detail.slice(0, 50) }),
|
|
15720
|
-
code: "level-default"
|
|
15721
|
-
};
|
|
16750
|
+
return { allowed: true, code: "level-default" };
|
|
15722
16751
|
}
|
|
15723
16752
|
function checkPlan(operation) {
|
|
15724
16753
|
if (operation.type === "code_exec") {
|
|
@@ -15825,9 +16854,17 @@ function isSensitivePath(target) {
|
|
|
15825
16854
|
function checkHardLimits(operation) {
|
|
15826
16855
|
const danger = checkDangerousCommand(operation.detail);
|
|
15827
16856
|
if (danger.dangerous) {
|
|
16857
|
+
if (danger.hardline) {
|
|
16858
|
+
return {
|
|
16859
|
+
allowed: false,
|
|
16860
|
+
reason: t("permission_reason.dangerous", { desc: danger.desc ?? "" }),
|
|
16861
|
+
code: "dangerous-command"
|
|
16862
|
+
};
|
|
16863
|
+
}
|
|
15828
16864
|
return {
|
|
15829
16865
|
allowed: false,
|
|
15830
|
-
|
|
16866
|
+
requireApproval: true,
|
|
16867
|
+
reason: t("permission_reason.dangerous_recoverable", { desc: danger.desc ?? "" }),
|
|
15831
16868
|
code: "dangerous-command"
|
|
15832
16869
|
};
|
|
15833
16870
|
}
|
|
@@ -15960,11 +16997,11 @@ var ApprovalCache = class {
|
|
|
15960
16997
|
return !rel.startsWith("..") && !isAbsolute(rel);
|
|
15961
16998
|
}
|
|
15962
16999
|
normalizeTarget(target) {
|
|
15963
|
-
const
|
|
15964
|
-
if (!
|
|
15965
|
-
const unix =
|
|
17000
|
+
const t78 = target.trim();
|
|
17001
|
+
if (!t78) return "";
|
|
17002
|
+
const unix = t78.replace(/\\/g, "/");
|
|
15966
17003
|
if (unix.startsWith("/") || /^[A-Za-z]:\//.test(unix)) return resolve(unix);
|
|
15967
|
-
return
|
|
17004
|
+
return t78;
|
|
15968
17005
|
}
|
|
15969
17006
|
load() {
|
|
15970
17007
|
try {
|
|
@@ -16067,9 +17104,9 @@ function createSharedPermissionState(cache) {
|
|
|
16067
17104
|
};
|
|
16068
17105
|
}
|
|
16069
17106
|
function cacheKeyFor(raw) {
|
|
16070
|
-
const
|
|
16071
|
-
if (!
|
|
16072
|
-
return
|
|
17107
|
+
const t78 = raw.trim();
|
|
17108
|
+
if (!t78) return null;
|
|
17109
|
+
return t78.slice(0, 500);
|
|
16073
17110
|
}
|
|
16074
17111
|
// src/permission/manager.ts
|
|
16075
17112
|
var log3 = createLogger("permission");
|
|
@@ -16164,12 +17201,9 @@ var PermissionManager = class _PermissionManager {
|
|
|
16164
17201
|
decide(operation) {
|
|
16165
17202
|
const hard = checkHardLimits(operation);
|
|
16166
17203
|
if (hard) {
|
|
16167
|
-
if (hard.requireApproval
|
|
16168
|
-
|
|
16169
|
-
|
|
16170
|
-
reason: t("permission_reason.non_interactive_denied", { reason: hard.reason ?? "" }),
|
|
16171
|
-
code: hard.code
|
|
16172
|
-
};
|
|
17204
|
+
if (hard.requireApproval) {
|
|
17205
|
+
const settled = this.settleUnaskable(operation, hard);
|
|
17206
|
+
if (settled) return settled;
|
|
16173
17207
|
}
|
|
16174
17208
|
return hard;
|
|
16175
17209
|
}
|
|
@@ -16177,6 +17211,58 @@ var PermissionManager = class _PermissionManager {
|
|
|
16177
17211
|
if (decision.allowed || !decision.requireApproval) return decision;
|
|
16178
17212
|
return this.applyPreauthorization(operation, decision);
|
|
16179
17213
|
}
|
|
17214
|
+
settleUnaskable(operation, hard) {
|
|
17215
|
+
if (this.level === "plan") {
|
|
17216
|
+
return {
|
|
17217
|
+
allowed: false,
|
|
17218
|
+
reason: t("permission_reason.plan_no_write"),
|
|
17219
|
+
code: hard.code
|
|
17220
|
+
};
|
|
17221
|
+
}
|
|
17222
|
+
if (this.level === "bypass" && hard.code === "obfuscated") {
|
|
17223
|
+
return {
|
|
17224
|
+
allowed: true,
|
|
17225
|
+
reason: t("permission_reason.bypass_unaskable", { reason: hard.reason ?? "" }),
|
|
17226
|
+
code: hard.code
|
|
17227
|
+
};
|
|
17228
|
+
}
|
|
17229
|
+
const cached = this.cachedDecision(operation);
|
|
17230
|
+
if (cached) return cached;
|
|
17231
|
+
if (this.shared.nonInteractive) {
|
|
17232
|
+
return {
|
|
17233
|
+
allowed: false,
|
|
17234
|
+
reason: t("permission_reason.non_interactive_denied", { reason: hard.reason ?? "" }),
|
|
17235
|
+
code: hard.code
|
|
17236
|
+
};
|
|
17237
|
+
}
|
|
17238
|
+
return null;
|
|
17239
|
+
}
|
|
17240
|
+
approvalCacheActive() {
|
|
17241
|
+
return this.level !== "bypass";
|
|
17242
|
+
}
|
|
17243
|
+
rememberFlag() {
|
|
17244
|
+
return this.approvalCacheActive() ? {} : { rememberable: false };
|
|
17245
|
+
}
|
|
17246
|
+
cachedDecision(operation) {
|
|
17247
|
+
const cacheTool = operation.toolName ?? operation.type;
|
|
17248
|
+
const cacheKey = cacheKeyFor(operation.target || operation.detail);
|
|
17249
|
+
const cached = cacheKey ? this.shared.cache.get(cacheTool, cacheKey) : null;
|
|
17250
|
+
if (cached === "allow-always" || cached === "allow-session") {
|
|
17251
|
+
return {
|
|
17252
|
+
allowed: true,
|
|
17253
|
+
reason: t("permission_reason.cached_allow"),
|
|
17254
|
+
code: "approval-cache"
|
|
17255
|
+
};
|
|
17256
|
+
}
|
|
17257
|
+
if (cached === "deny") {
|
|
17258
|
+
return {
|
|
17259
|
+
allowed: false,
|
|
17260
|
+
reason: t("permission_reason.cached_deny"),
|
|
17261
|
+
code: "approval-cache"
|
|
17262
|
+
};
|
|
17263
|
+
}
|
|
17264
|
+
return null;
|
|
17265
|
+
}
|
|
16180
17266
|
checkRulesAndCache(operation) {
|
|
16181
17267
|
const listMatch = matchRuleLists(this.shared.ruleLists, operation);
|
|
16182
17268
|
if (listMatch?.verdict === "deny") {
|
|
@@ -16191,34 +17277,18 @@ var PermissionManager = class _PermissionManager {
|
|
|
16191
17277
|
if (ruleDecision && !ruleDecision.allowed && !ruleDecision.requireApproval) {
|
|
16192
17278
|
return ruleDecision;
|
|
16193
17279
|
}
|
|
16194
|
-
|
|
16195
|
-
|
|
16196
|
-
const cacheKey = cacheKeyFor(operation.target || operation.detail);
|
|
16197
|
-
const cached = cacheKey ? this.shared.cache.get(cacheTool, cacheKey) : null;
|
|
16198
|
-
if (cached === "allow-always" || cached === "allow-session") {
|
|
16199
|
-
return {
|
|
16200
|
-
allowed: true,
|
|
16201
|
-
reason: t("permission_reason.cached_allow"),
|
|
16202
|
-
code: "approval-cache"
|
|
16203
|
-
};
|
|
16204
|
-
}
|
|
16205
|
-
if (cached === "deny") {
|
|
16206
|
-
return {
|
|
16207
|
-
allowed: false,
|
|
16208
|
-
reason: t("permission_reason.cached_deny"),
|
|
16209
|
-
code: "approval-cache"
|
|
16210
|
-
};
|
|
16211
|
-
}
|
|
16212
|
-
}
|
|
17280
|
+
const cached = this.approvalCacheActive() ? this.cachedDecision(operation) : null;
|
|
17281
|
+
if (cached) return cached;
|
|
16213
17282
|
if (listMatch?.verdict === "ask") {
|
|
16214
17283
|
return {
|
|
16215
17284
|
allowed: false,
|
|
16216
17285
|
requireApproval: true,
|
|
16217
17286
|
reason: t("permission_reason.rule_ask", { rule: listMatch.rule }),
|
|
16218
|
-
code: "rule-match"
|
|
17287
|
+
code: "rule-match",
|
|
17288
|
+
...this.rememberFlag()
|
|
16219
17289
|
};
|
|
16220
17290
|
}
|
|
16221
|
-
if (ruleDecision?.requireApproval) return ruleDecision;
|
|
17291
|
+
if (ruleDecision?.requireApproval) return { ...ruleDecision, ...this.rememberFlag() };
|
|
16222
17292
|
if (listMatch?.verdict === "allow") {
|
|
16223
17293
|
return {
|
|
16224
17294
|
allowed: true,
|
|
@@ -16405,8 +17475,8 @@ function suggestRuleFromApproval(request) {
|
|
|
16405
17475
|
].join("\n")
|
|
16406
17476
|
};
|
|
16407
17477
|
}
|
|
16408
|
-
var
|
|
16409
|
-
var
|
|
17478
|
+
var NS4 = "schedule";
|
|
17479
|
+
var MIGRATIONS3 = [
|
|
16410
17480
|
{
|
|
16411
17481
|
id: "schedule-baseline",
|
|
16412
17482
|
version: 1,
|
|
@@ -16480,7 +17550,7 @@ var ScheduleStore = class {
|
|
|
16480
17550
|
constructor(dbPath) {
|
|
16481
17551
|
this.path = dbPath;
|
|
16482
17552
|
this.db = openDatabase(this.path);
|
|
16483
|
-
migrate(this.db,
|
|
17553
|
+
migrate(this.db, NS4, MIGRATIONS3);
|
|
16484
17554
|
}
|
|
16485
17555
|
get connection() {
|
|
16486
17556
|
return this.db;
|
|
@@ -20916,73 +21986,6 @@ function trunc(stdout, stderr) {
|
|
|
20916
21986
|
const skipped = out.length - TRUNCATE_HEAD - TRUNCATE_TAIL;
|
|
20917
21987
|
return `${head}${nl}...[\u5DF2\u622A\u65AD ${(skipped / 1024).toFixed(1)} KB]...${nl}${tail}`;
|
|
20918
21988
|
}
|
|
20919
|
-
var NS4 = "tracker";
|
|
20920
|
-
var MIGRATIONS3 = [
|
|
20921
|
-
{
|
|
20922
|
-
id: "tracker-baseline",
|
|
20923
|
-
version: 1,
|
|
20924
|
-
description: "TODO \u8868\uFF08\u57FA\u7EBF\uFF09",
|
|
20925
|
-
up: (db) => {
|
|
20926
|
-
db.exec(`
|
|
20927
|
-
CREATE TABLE IF NOT EXISTS todos (
|
|
20928
|
-
id TEXT PRIMARY KEY,
|
|
20929
|
-
session_id TEXT NOT NULL,
|
|
20930
|
-
content TEXT NOT NULL,
|
|
20931
|
-
status TEXT NOT NULL DEFAULT 'pending'
|
|
20932
|
-
CHECK(status IN ('pending','in_progress','completed','cancelled')),
|
|
20933
|
-
created_at TEXT NOT NULL,
|
|
20934
|
-
updated_at TEXT NOT NULL
|
|
20935
|
-
);
|
|
20936
|
-
CREATE INDEX IF NOT EXISTS idx_todos_session ON todos(session_id);
|
|
20937
|
-
`);
|
|
20938
|
-
}
|
|
20939
|
-
},
|
|
20940
|
-
{
|
|
20941
|
-
id: "todos-reason-column",
|
|
20942
|
-
version: 2,
|
|
20943
|
-
description: "TODO \u8868\u52A0 reason \u5217\uFF08\u53D6\u6D88\u7406\u7531\uFF0C\u53EA\u6709 cancelled \u90A3\u4E00\u6863\u4F1A\u6709\uFF09",
|
|
20944
|
-
up: (db) => {
|
|
20945
|
-
db.exec(`ALTER TABLE todos ADD COLUMN reason TEXT;`);
|
|
20946
|
-
}
|
|
20947
|
-
}
|
|
20948
|
-
];
|
|
20949
|
-
var TrackerDB = class {
|
|
20950
|
-
db;
|
|
20951
|
-
path;
|
|
20952
|
-
constructor(dbPath) {
|
|
20953
|
-
this.path = dbPath;
|
|
20954
|
-
this.db = openDatabase(this.path);
|
|
20955
|
-
migrate(this.db, NS4, MIGRATIONS3);
|
|
20956
|
-
}
|
|
20957
|
-
listBySession(sessionId) {
|
|
20958
|
-
const rows = this.db.prepare(
|
|
20959
|
-
"SELECT id, content, status, reason, created_at, updated_at FROM todos WHERE session_id = ? ORDER BY updated_at ASC"
|
|
20960
|
-
).all(sessionId);
|
|
20961
|
-
return rows.map(({ reason: reason9, ...rest }) => reason9 ? { ...rest, reason: reason9 } : rest);
|
|
20962
|
-
}
|
|
20963
|
-
replaceAll(sessionId, todos) {
|
|
20964
|
-
const insertStmt = this.db.prepare(
|
|
20965
|
-
"INSERT INTO todos (id, session_id, content, status, reason, created_at, updated_at) VALUES (@id, @session_id, @content, @status, @reason, @created_at, @updated_at)"
|
|
20966
|
-
);
|
|
20967
|
-
this.db.transaction(() => {
|
|
20968
|
-
this.db.prepare("DELETE FROM todos WHERE session_id = ?").run(sessionId);
|
|
20969
|
-
for (const t76 of todos) {
|
|
20970
|
-
insertStmt.run({
|
|
20971
|
-
id: t76.id,
|
|
20972
|
-
session_id: sessionId,
|
|
20973
|
-
content: t76.content,
|
|
20974
|
-
status: t76.status,
|
|
20975
|
-
reason: t76.reason || null,
|
|
20976
|
-
created_at: t76.created_at,
|
|
20977
|
-
updated_at: t76.updated_at
|
|
20978
|
-
});
|
|
20979
|
-
}
|
|
20980
|
-
})();
|
|
20981
|
-
}
|
|
20982
|
-
close() {
|
|
20983
|
-
releaseDatabase(this.path);
|
|
20984
|
-
}
|
|
20985
|
-
};
|
|
20986
21989
|
/**
|
|
20987
21990
|
* @license
|
|
20988
21991
|
* Copyright 2025 Google LLC
|
|
@@ -21028,4 +22031,4 @@ var TrackerDB = class {
|
|
|
21028
22031
|
* `--tree` 的命令帮助里,让用户自己知道 `--tree` 意味着什么。
|
|
21029
22032
|
*/
|
|
21030
22033
|
|
|
21031
|
-
export { AGENT_ROLE_DIAG_MODULE, ARTIFACT_MAX_TOTAL_BYTES, ARTIFACT_RETENTION_DEFAULTS, AgentLoop, ApprovalCache, BLOCKED_TOOLS, BudgetGuard, BudgetStore, CHECKPOINT_MAX_TOTAL_BYTES, CLAUDE_EVENT_NAMES, CLAUDE_TOOL_NAMES, CODE_MODE_LIMITS, CODE_MODE_SDK_TAG, CONFIG_KEYS, CURRENT_CONFIG_VERSION, CheckpointManager, CheckpointStore, CodeSandbox, ComplianceEngine, ContextCompressor, DEFAULT_COMPLIANCE_ACTIONS, DEFAULT_COMPLIANCE_SETTINGS, DEFAULT_GOAL_MAX_ROUNDS, DEFAULT_MAX_HOLD_CHARS, DelegateManager, EMPTY_RULES, EPOCH_DISK_LEDGER, EpochConfigSchema, FTS_TABLES, FTS_TRIGGERS, GOAL_BLOCK_CODES, GoalService, GoalStore, HEADLESS_AUDIT_CODES, HookManager, INSTRUCTION_BUDGET_CHARS, INSTRUCTION_FILES, ImageLoadError, ImportTrustStore, LAUNCHD_LABEL_PREFIX, LOCK_HEARTBEAT_MS, LOCK_STALE_MS, MARKETPLACE_FILE, MAX_AUDIT_ENTRIES, MAX_COMMAND_DEPTH, MAX_GOAL_MAX_ROUNDS, MAX_IMPORT_DEPTH, MAX_OBJECTIVE_CHARS, MAX_REASON_CHARS, MAX_RECENT_WORKSPACES, MODEL_GIVE_UP_THRESHOLD, MULTIMODAL_DEFAULTS, ManagedPolicyError, ManagedSettingsSchema, MemoryManager, MemoryReviewer, ModelFailureTracker, NOMINAL_TOTAL_BYTES, NO_REVEALED_TOOLS, PERMISSION_AUDIT_CODES, PLUGIN_LAYOUT, PLUGIN_MANIFEST_FILE, PLUGIN_NAME_PATTERN, PLUGIN_ROOT_ENV, PLUGIN_ROOT_TOKEN, POLICY_DECISIONS, PROJECT_AGENTS_SUBDIR, PROVIDER_NO_CREDENTIAL, PermissionManager, PlanModeState, PluginStore, PolicyRuleSchema, ProviderError, ProviderRouter, RECORDING_KEEP_RUNS, RECORDING_MAX_BYTES, ROLE_FRONTMATTER_SCHEMA, RUN_CODE_TOOL, RecentWorkspaces, SANDBOX_COVERS, SANDBOX_EXCLUDES, SCALAR_KEYS, SCHEDULE_DEFAULTS, SCHEDULE_DEFAULT_ALLOWLIST, SCHEDULE_ISSUE_CODES, SCHTASKS_FOLDER, SESSION_MESSAGES_READ_TOOL, SESSION_MESSAGES_SEARCH_TOOL, SESSION_QUERY_TOOLS, SESSION_SEARCH_TOOL, SESSION_TRACE_TOOL, SETTINGS_LAYER_ORDER, SETTING_WRITE_APPLY, SETTING_WRITE_KEYS, SETTING_WRITE_LAYERS, SKILL_CREATE_TOOL, SKILL_EDIT_TOOL, SKILL_PATCH_TOOL, SKILL_VIEW_TOOL, SKILL_WRITE_FILE_TOOL, SLASH_COMMANDS_DIAG_MODULE, ScheduleRecorder, ScheduleRegistrar, ScheduleStore, SessionManager, SessionQueries, SessionReferences, SettingsFileError, SettingsFileSchema, SkillLearner, SkillSystem, TOOL_SEARCH_TOOL, TaskNoticeTracker, ToolRegistry, TrackerDB, TrustManager, Workspace, acquireScheduleLock, addMarketplace, addUsage, admitArtifact, admitArtifacts, affectedPaths, artifactLaunchArgv, artifactOpenRefusal, assertLevelAllowed, blockedRoleTools, buildContextBreakdown, buildFireArgv, buildFtsMatchQuery, buildSchemaExports, buildSystemPrompt, buildTrigramMatchQuery, builtinRoles, checkEpochVersion, checkFtsHealth, checkShadowing, classifyProviderError, clearModelCache, coalesceRecording, codeModeWorkerPath, commandNameFromRelPath, compileRuleLists, compressionOptions, computeCost, computeCostMicros, countRules, createAllowAllGate, createAllowAllImportGate, createAskQuestionTool, createCodeExecTool, createDelegateTool, createDenyAllGate, createDenyAllImportGate, createGoalTool, createImportGate, createMemoryTool, createPlanModeTools, createRoleFile, createRoleScope, createRunCodeTool, createScheduleDraftTool, createSessionMessagesReadTool, createSessionMessagesSearchTool, createSessionSearchTool, createSessionSearchTools, createSessionTraceTool, createSkillViewTool, createSkillWriteTools, createTodoTool, createToolRevealState, createToolScope, createToolSearchTool, createTrustGate, currentEpochVersion, decideImport, decideTrust, defaultConfig, defaultModelOf, describeArtifactRefusal, describeBackgroundTask, describeHeadlessPolicy, describeIsolation, describeJsonError, diagnoseSchedules, discoverMarkdown, discoverModels, discoverProject, draftCommandLine, draftWidening, escapeRuleContent, estimateImageTokens, estimateRunTokens, estimateTokens, evaluateSelection, expandCommand, expandImports, explainSetting, findInstructionFiles, findProjectInstructions, findRole, formatBytes, formatSkillIndex, formatUsd, gateForTool, getCapability, getConfigIssues, getConfigOrigins, getImportGate, getTrustGate, hasPromptCaching, hasProviderCredentials, imageFromBase64, imageFromPath, imagePartTokens, importSkills, installPlugin, instructionDirs, instructionNotices, intervalSlots, inventoryTotal, isBlockCode, isCodeModeDeferred, isEmptyRules, isGoalPhase, isNonInteractive, isReadOnlySubCall, isSettingWriteLayer, isValidPluginName, isWithinWindow, isWritableSettingKey, labelSlug, layerRank, listSessionCandidates, listWorkspaceFiles, loadCommandDefinitions, loadCommandFile, loadCommands, loadConfig, loadHookSources, loadKeybindings, loadLexiconDir, loadLexiconFiles, loadPlugins, loadPolicyFiles, loadPolicyRules, loadRoleDefinitions, loadRoleDir, loadedImports, localDateString, managedSwitchNotes, mapClaudeToolName, mapEpochToolName, mapOperationType, matchPreauthorization, matchRuleLists, matchesHook, mentionsPluginRoot, mergeHeadlessPolicy, mergeRoles, mergeSchedulePatch, microsToUsd, modelListCandidates, needsAllowlist, nextFallbackModel, nextRunAt, noConfigOrigins, noManagedPolicy, noPlugins, openArtifact, originOf, parseClaudeSettingsHooks, parseClock, parseDate, parseFrontmatter, parseHooksConfig, parseHooksConfigVerbose, parseImportLine, parseModelList, parseRoleDefinition, parseScheduleDraft, parseSkillFrontmatter, parseTrigger, pendingImageTokens, persistParts, persistToolResults, persistedUsage, previewSkillImport, pruneArtifacts, pruneRecordings, pruneSchedules, readImageSize, readMarketplaces, readPluginManifest, readPluginRecords, readProviderKeyEnv, readRecording, readSessionSurface, reclaimFtsSpace, redactLine, refreshOpenRouterMetadata, removeMarketplace, removeSessionArtifacts, renderImportTree, renderProjectContext, renderSdkDeclaration, repairSchedules, resolveArtifactRetention, resolveCodeModeWorker, resolveComplianceDirs, resolveEntrySource, resolveInWorkspace, resolveMarketplaceRef, resolveMentions, resolveMultimodalPolicy, resolvePolicyDirs, resolveProjectRoot, resolveScheduleBackend, resolveSessionReference, resolveSessionVisibility, resolveSettings, resolveSkillDirs, resolveSource, resolveWorkspace, restoreParts, revealScopedProvider, roleScopedProvider, roleSourceLabel, ruleCovers, ruleFromString, ruleToString, runCode, runCommand2 as runCommand, sanitizeArgNames, sanitizeCommand, sanitizeFtsQuery, sanitizePath, sanitizeToolOutput, scanInstructions, scanPluginDir, scanSkillDirs, searchMarketplaces, setImportGate, setPluginEnabled, setTrustGate, settingValueChoices, settingValueKind, settingWriteLayers, settingWriteTargets, skillIndexDescription, skillIndexResidency, skillIndexStats, sniffMediaType, splitFrontmatter, staticToolProvider, stripArtifactData, substitutePluginRoot, suggestRuleFromApproval, systemOpenArgv, systemPromptSegments, toPosix, toolDefinitionTokens, toolMatches, toolTableTokens, totalTokens, traceSession, triggerPeriodMs, unescapeRuleContent, uninstallPlugin, unknownRoleSkills, unknownRoleTools, unloadedInstructionFiles, updateMarketplace, updatePlugin, usdToMicros, validateSchedule, validateTrigger, validateWindow, withGoalCompression, withGoalPrompt, workspaceDisplayPath, writeSettingValue };
|
|
22034
|
+
export { AGENT_ROLE_DIAG_MODULE, ARTIFACT_MAX_TOTAL_BYTES, ARTIFACT_RETENTION_DEFAULTS, AgentLoop, ApprovalCache, BLOCKED_TOOLS, BudgetGuard, BudgetStore, CHECKPOINT_MAX_TOTAL_BYTES, CLAUDE_EVENT_NAMES, CLAUDE_TOOL_NAMES, CODE_MODE_LIMITS, CODE_MODE_SDK_TAG, CONFIG_KEYS, CURRENT_CONFIG_VERSION, CheckpointManager, CheckpointStore, CodeSandbox, ComplianceEngine, ContextCompressor, DEFAULT_COMPLIANCE_ACTIONS, DEFAULT_COMPLIANCE_SETTINGS, DEFAULT_COMPRESSION_THRESHOLD, DEFAULT_GOAL_MAX_ROUNDS, DEFAULT_MAX_HOLD_CHARS, DelegateManager, EMPTY_RULES, EPOCH_DISK_LEDGER, EpochConfigSchema, FTS_TABLES, FTS_TRIGGERS, GOAL_BLOCK_CODES, GoalService, GoalStore, HEADLESS_AUDIT_CODES, HookManager, INSTRUCTION_BUDGET_CHARS, INSTRUCTION_FILES, ImageLoadError, ImportTrustStore, LAUNCHD_LABEL_PREFIX, LOCK_HEARTBEAT_MS, LOCK_STALE_MS, MARKETPLACE_DISPLAY_FIELDS, MARKETPLACE_FILE, MAX_AUDIT_ENTRIES, MAX_COMMAND_DEPTH, MAX_GOAL_MAX_ROUNDS, MAX_IMPORT_DEPTH, MAX_OBJECTIVE_CHARS, MAX_REASON_CHARS, MAX_RECENT_WORKSPACES, MODEL_GIVE_UP_THRESHOLD, MULTIMODAL_DEFAULTS, ManagedPolicyError, ManagedSettingsSchema, MemoryManager, MemoryReviewer, ModelFailureTracker, NOMINAL_TOTAL_BYTES, NO_REVEALED_TOOLS, PACK_EXCLUDES, PACK_MAX_BYTES, PACK_MAX_FILES, PERMISSION_AUDIT_CODES, PLUGIN_LAYOUT, PLUGIN_MANIFEST_FILE, PLUGIN_NAME_PATTERN, PLUGIN_ROOT_ENV, PLUGIN_ROOT_TOKEN, POLICY_DECISIONS, PROJECT_AGENTS_SUBDIR, PROVIDER_NO_CREDENTIAL, PermissionManager, PlanModeState, PluginAuthorSchema, PluginStore, PolicyRuleSchema, ProviderError, ProviderRouter, RECORDING_KEEP_RUNS, RECORDING_MAX_BYTES, ROLE_FRONTMATTER_SCHEMA, RUN_CODE_TOOL, RecentWorkspaces, SANDBOX_COVERS, SANDBOX_EXCLUDES, SCALAR_KEYS, SCHEDULE_DEFAULTS, SCHEDULE_DEFAULT_ALLOWLIST, SCHEDULE_ISSUE_CODES, SCHTASKS_FOLDER, SESSION_MESSAGES_READ_TOOL, SESSION_MESSAGES_SEARCH_TOOL, SESSION_QUERY_TOOLS, SESSION_SEARCH_TOOL, SESSION_TRACE_TOOL, SETTINGS_LAYER_ORDER, SETTING_WRITE_APPLY, SETTING_WRITE_KEYS, SETTING_WRITE_LAYERS, SKILL_CREATE_TOOL, SKILL_EDIT_TOOL, SKILL_PATCH_TOOL, SKILL_VIEW_TOOL, SKILL_WRITE_FILE_TOOL, SLASH_COMMANDS_DIAG_MODULE, ScheduleRecorder, ScheduleRegistrar, ScheduleStore, SessionManager, SessionQueries, SessionReferences, SettingsFileError, SettingsFileSchema, SkillLearner, SkillSystem, TOOL_SEARCH_TOOL, TaskNoticeTracker, ToolRegistry, TrackerDB, TrustManager, Workspace, acquireScheduleLock, addMarketplace, addUsage, admitArtifact, admitArtifacts, affectedPaths, artifactLaunchArgv, artifactOpenRefusal, assertLevelAllowed, blockedRoleTools, buildContextBreakdown, buildFireArgv, buildFtsMatchQuery, buildSchemaExports, buildSystemPrompt, buildTrigramMatchQuery, builtinRoles, checkEpochVersion, checkFtsHealth, checkShadowing, classifyProviderError, clearModelCache, coalesceRecording, codeModeWorkerPath, commandNameFromRelPath, compileRuleLists, compressionOptions, computeCost, computeCostMicros, countRules, createAllowAllGate, createAllowAllImportGate, createAskQuestionTool, createCodeExecTool, createDelegateTool, createDenyAllGate, createDenyAllImportGate, createGoalTool, createImportGate, createMemoryTool, createPlanModeTools, createRoleFile, createRoleScope, createRunCodeTool, createScheduleDraftTool, createSessionMessagesReadTool, createSessionMessagesSearchTool, createSessionSearchTool, createSessionSearchTools, createSessionTraceTool, createSkillViewTool, createSkillWriteTools, createTodoTool, createToolRevealState, createToolScope, createToolSearchTool, createTrustGate, currentEpochVersion, decideImport, decideTrust, defaultConfig, defaultModelOf, describeArtifactRefusal, describeBackgroundTask, describeHeadlessPolicy, describeIsolation, describeJsonError, diagnoseSchedules, discoverMarkdown, discoverModels, discoverProject, draftCommandLine, draftWidening, escapeRuleContent, estimateImageTokens, estimateRunTokens, estimateTokens, evaluateSelection, expandCommand, expandImports, explainSetting, extractArchive, findInstructionFiles, findProjectInstructions, findRole, formatBytes, formatSkillIndex, formatUsd, gateForTool, getCapability, getConfigIssues, getConfigOrigins, getImportGate, getTrustGate, hasPromptCaching, hasProviderCredentials, imageFromBase64, imageFromPath, imagePartTokens, importSkills, installPlugin, instructionDirs, instructionNotices, intervalSlots, inventoryTotal, isBlockCode, isCodeModeDeferred, isEmptyRules, isGoalPhase, isNonInteractive, isReadOnlySubCall, isSettingWriteLayer, isValidPluginName, isWithinWindow, isWritableSettingKey, labelSlug, layerRank, listSessionCandidates, listWorkspaceFiles, loadCommandDefinitions, loadCommandFile, loadCommands, loadConfig, loadHookSources, loadKeybindings, loadLexiconDir, loadLexiconFiles, loadPlugins, loadPolicyFiles, loadPolicyRules, loadRoleDefinitions, loadRoleDir, loadedImports, localDateString, managedSwitchNotes, mapClaudeToolName, mapEpochToolName, mapOperationType, matchPreauthorization, matchRuleLists, matchesHook, mentionsPluginRoot, mergeHeadlessPolicy, mergeRoles, mergeSchedulePatch, microsToUsd, modelListCandidates, needsAllowlist, nextFallbackModel, nextRunAt, noConfigOrigins, noManagedPolicy, noPlugins, openArtifact, originOf, packFileName, packPlugin, parseClaudeSettingsHooks, parseClock, parseDate, parseFrontmatter, parseHooksConfig, parseHooksConfigVerbose, parseImportLine, parseModelList, parseRoleDefinition, parseScheduleDraft, parseSkillFrontmatter, parseTrigger, pendingImageTokens, persistParts, persistToolResults, persistedUsage, planPack, previewSkillImport, projectEntryForDisplay, pruneArtifacts, pruneRecordings, pruneSchedules, readImageSize, readMarketplaces, readPluginManifest, readPluginRecords, readProviderKeyEnv, readRecording, readSessionSurface, reclaimFtsSpace, redactLine, refreshOpenRouterMetadata, removeMarketplace, removeSessionArtifacts, renderImportTree, renderProjectContext, renderSdkDeclaration, repairSchedules, resolveArtifactRetention, resolveCodeModeWorker, resolveComplianceDirs, resolveEntrySource, resolveInWorkspace, resolveMarketplaceRef, resolveMentions, resolveMultimodalPolicy, resolvePolicyDirs, resolveProjectRoot, resolveScheduleBackend, resolveSessionReference, resolveSessionVisibility, resolveSettings, resolveSkillDirs, resolveSource, resolveTar, resolveWorkspace, restoreParts, revealScopedProvider, roleScopedProvider, roleSourceLabel, ruleCovers, ruleFromString, ruleToString, runCode, runCommand2 as runCommand, sanitizeArgNames, sanitizeCommand, sanitizeFtsQuery, sanitizePath, sanitizeToolOutput, scanInstructions, scanPluginDir, scanSkillDirs, searchMarketplaces, setImportGate, setPluginEnabled, setTrustGate, settingValueChoices, settingValueKind, settingWriteLayers, settingWriteTargets, skillIndexDescription, skillIndexResidency, skillIndexStats, sniffMediaType, splitFrontmatter, staticToolProvider, stripArtifactData, substitutePluginRoot, suggestRuleFromApproval, systemOpenArgv, systemPromptSegments, toPosix, toolDefinitionTokens, toolMatches, toolTableTokens, totalTokens, traceSession, triggerPeriodMs, unescapeRuleContent, uninstallPlugin, unknownRoleSkills, unknownRoleTools, unloadedInstructionFiles, updateMarketplace, updatePlugin, usdToMicros, validateSchedule, validateTrigger, validateWindow, withGoalCompression, withGoalPrompt, workspaceDisplayPath, writeSettingValue };
|