@epoch-agent/core 0.17.0 → 0.19.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 +2 -2
- package/dist/index.d.ts +216 -48
- package/dist/index.js +250 -262
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -650,7 +650,7 @@ var ToolRegistry = class {
|
|
|
650
650
|
this.addTool(tool, source);
|
|
651
651
|
}
|
|
652
652
|
registerTools(tools, source = "builtin") {
|
|
653
|
-
for (const
|
|
653
|
+
for (const t80 of tools) this.addTool(t80, source);
|
|
654
654
|
}
|
|
655
655
|
addTool(tool, source) {
|
|
656
656
|
const existing = this.tools.get(tool.name);
|
|
@@ -699,14 +699,14 @@ var ToolRegistry = class {
|
|
|
699
699
|
return isCodeModeDeferred(tool) ? ToolExposure.Deferred : declared;
|
|
700
700
|
}
|
|
701
701
|
exposed() {
|
|
702
|
-
return this.list().filter((
|
|
702
|
+
return this.list().filter((t80) => this.exposureOf(t80) === ToolExposure.Direct);
|
|
703
703
|
}
|
|
704
704
|
byExposure(exposure) {
|
|
705
|
-
return this.list().filter((
|
|
705
|
+
return this.list().filter((t80) => this.exposureOf(t80) === exposure);
|
|
706
706
|
}
|
|
707
707
|
codeModeDeclared() {
|
|
708
708
|
if (!this.codeMode) return [];
|
|
709
|
-
return this.list().filter((
|
|
709
|
+
return this.list().filter((t80) => isCodeModeDeferred(t80));
|
|
710
710
|
}
|
|
711
711
|
sourceOf(name) {
|
|
712
712
|
return this.tools.get(name)?.source;
|
|
@@ -755,7 +755,7 @@ var ToolRegistry = class {
|
|
|
755
755
|
function staticToolProvider(tools) {
|
|
756
756
|
return {
|
|
757
757
|
list: () => tools,
|
|
758
|
-
get: (name) => tools.find((
|
|
758
|
+
get: (name) => tools.find((t80) => t80.name === name)
|
|
759
759
|
};
|
|
760
760
|
}
|
|
761
761
|
var log = createLogger("tokenizer");
|
|
@@ -1388,7 +1388,7 @@ var DelegateManager = class {
|
|
|
1388
1388
|
}
|
|
1389
1389
|
normalizeTasks(args) {
|
|
1390
1390
|
if (args.tasks && args.tasks.length > 0) {
|
|
1391
|
-
return args.role ? args.tasks.map((
|
|
1391
|
+
return args.role ? args.tasks.map((t80) => ({ ...t80, role: t80.role ?? args.role })) : args.tasks;
|
|
1392
1392
|
}
|
|
1393
1393
|
if (args.goal) return [{ goal: args.goal, ...args.role ? { role: args.role } : {} }];
|
|
1394
1394
|
return [];
|
|
@@ -1398,7 +1398,7 @@ var DelegateManager = class {
|
|
|
1398
1398
|
for (let i = 0; i < tasks.length; i += maxConcurrent) {
|
|
1399
1399
|
const batch = tasks.slice(i, i + maxConcurrent);
|
|
1400
1400
|
const batchResults = await Promise.allSettled(
|
|
1401
|
-
batch.map((
|
|
1401
|
+
batch.map((t80) => this.runSingleTask(t80, parentSessionId, signal))
|
|
1402
1402
|
);
|
|
1403
1403
|
for (const r of batchResults) {
|
|
1404
1404
|
if (r.status === "fulfilled") {
|
|
@@ -1556,21 +1556,21 @@ function createDelegateTool(runner, config, roles) {
|
|
|
1556
1556
|
operation: "command",
|
|
1557
1557
|
describeTarget: (args) => {
|
|
1558
1558
|
const tasks = Array.isArray(args["tasks"]) ? args["tasks"] : [];
|
|
1559
|
-
const goals = tasks.map((
|
|
1559
|
+
const goals = tasks.map((t80) => String(t80["goal"] ?? ""));
|
|
1560
1560
|
const single = typeof args["goal"] === "string" ? args["goal"] : "";
|
|
1561
1561
|
return [...goals, single].filter(Boolean).join(" | ");
|
|
1562
1562
|
},
|
|
1563
1563
|
async execute(args, ctx) {
|
|
1564
1564
|
const start = Date.now();
|
|
1565
1565
|
const rawTasks = Array.isArray(args["tasks"]) ? args["tasks"] : void 0;
|
|
1566
|
-
const tasks = rawTasks?.map((
|
|
1567
|
-
const o =
|
|
1566
|
+
const tasks = rawTasks?.map((t80) => {
|
|
1567
|
+
const o = t80;
|
|
1568
1568
|
return {
|
|
1569
1569
|
goal: String(o["goal"] ?? "").trim(),
|
|
1570
1570
|
context: String(o["context"] ?? ""),
|
|
1571
1571
|
...typeof o["role"] === "string" && o["role"].trim() ? { role: o["role"].trim() } : {}
|
|
1572
1572
|
};
|
|
1573
|
-
}).filter((
|
|
1573
|
+
}).filter((t80) => t80.goal.length > 0);
|
|
1574
1574
|
const goal = typeof args["goal"] === "string" ? args["goal"].trim() : "";
|
|
1575
1575
|
const topRole = typeof args["role"] === "string" ? args["role"].trim() : "";
|
|
1576
1576
|
if ((!tasks || tasks.length === 0) && !goal) {
|
|
@@ -1586,7 +1586,7 @@ function createDelegateTool(runner, config, roles) {
|
|
|
1586
1586
|
};
|
|
1587
1587
|
}
|
|
1588
1588
|
const known = listRoles();
|
|
1589
|
-
const requested = [...(tasks ?? []).map((
|
|
1589
|
+
const requested = [...(tasks ?? []).map((t80) => t80.role), topRole].filter(
|
|
1590
1590
|
(r) => Boolean(r)
|
|
1591
1591
|
);
|
|
1592
1592
|
const unknown = [...new Set(requested)].filter((r) => !known.some((k) => k.name === r));
|
|
@@ -1760,8 +1760,8 @@ function createGoalTool(deps) {
|
|
|
1760
1760
|
}
|
|
1761
1761
|
var VALID_TARGETS = ["memory", "user"];
|
|
1762
1762
|
var VALID_ACTIONS = ["add", "replace", "remove", "search", "read", "budget"];
|
|
1763
|
-
function isValidTarget(
|
|
1764
|
-
return typeof
|
|
1763
|
+
function isValidTarget(t80) {
|
|
1764
|
+
return typeof t80 === "string" && VALID_TARGETS.includes(t80);
|
|
1765
1765
|
}
|
|
1766
1766
|
function isValidAction(a) {
|
|
1767
1767
|
return VALID_ACTIONS.includes(a);
|
|
@@ -4915,7 +4915,7 @@ function available(tools) {
|
|
|
4915
4915
|
if (tools.length === 0) {
|
|
4916
4916
|
return "No deferred tools in this session \u2014 every tool is already in your tool list.";
|
|
4917
4917
|
}
|
|
4918
|
-
const names = tools.map((
|
|
4918
|
+
const names = tools.map((t80) => t80.name).sort();
|
|
4919
4919
|
const shown = names.slice(0, MAX_LISTED);
|
|
4920
4920
|
const rest = names.length - shown.length;
|
|
4921
4921
|
return `Searchable tools: ${shown.join(", ")}${rest > 0 ? ` (\u2026and ${rest} more)` : ""}.`;
|
|
@@ -5035,15 +5035,15 @@ var TrackerDB = class {
|
|
|
5035
5035
|
);
|
|
5036
5036
|
this.db.transaction(() => {
|
|
5037
5037
|
this.db.prepare("DELETE FROM todos WHERE session_id = ?").run(sessionId);
|
|
5038
|
-
for (const
|
|
5038
|
+
for (const t80 of todos) {
|
|
5039
5039
|
insertStmt.run({
|
|
5040
|
-
id:
|
|
5040
|
+
id: t80.id,
|
|
5041
5041
|
session_id: sessionId,
|
|
5042
|
-
content:
|
|
5043
|
-
status:
|
|
5044
|
-
reason:
|
|
5045
|
-
created_at:
|
|
5046
|
-
updated_at:
|
|
5042
|
+
content: t80.content,
|
|
5043
|
+
status: t80.status,
|
|
5044
|
+
reason: t80.reason || null,
|
|
5045
|
+
created_at: t80.created_at,
|
|
5046
|
+
updated_at: t80.updated_at
|
|
5047
5047
|
});
|
|
5048
5048
|
}
|
|
5049
5049
|
})();
|
|
@@ -5065,9 +5065,9 @@ function formatTodoList(todos, pendingCount) {
|
|
|
5065
5065
|
cancelled: "\u5DF2\u53D6\u6D88"
|
|
5066
5066
|
};
|
|
5067
5067
|
lines.push(`[todo] \u72B6\u6001\u6C47\u603B: ${pendingCount} \u4E2A\u5F85\u5B8C\u6210`);
|
|
5068
|
-
for (const
|
|
5069
|
-
const label = statusLabel[
|
|
5070
|
-
lines.push(`- [${label}] ${
|
|
5068
|
+
for (const t80 of todos) {
|
|
5069
|
+
const label = statusLabel[t80.status] || t80.status;
|
|
5070
|
+
lines.push(`- [${label}] ${t80.content}${t80.reason ? ` \u2014\u2014 ${t80.reason}` : ""}`);
|
|
5071
5071
|
}
|
|
5072
5072
|
return lines.join("\n");
|
|
5073
5073
|
}
|
|
@@ -5141,14 +5141,14 @@ function createTodoTool(db) {
|
|
|
5141
5141
|
};
|
|
5142
5142
|
}
|
|
5143
5143
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5144
|
-
const existing = new Map(db.listBySession(ctx.sessionId).map((
|
|
5145
|
-
const todos = rawTodos.map((
|
|
5146
|
-
const prev =
|
|
5147
|
-
const status = isValidStatus(
|
|
5148
|
-
const reason9 = normalizeReason(status,
|
|
5144
|
+
const existing = new Map(db.listBySession(ctx.sessionId).map((t80) => [t80.id, t80]));
|
|
5145
|
+
const todos = rawTodos.map((t80) => {
|
|
5146
|
+
const prev = t80.id ? existing.get(t80.id) : void 0;
|
|
5147
|
+
const status = isValidStatus(t80.status) ? t80.status : "pending";
|
|
5148
|
+
const reason9 = normalizeReason(status, t80.reason);
|
|
5149
5149
|
return {
|
|
5150
|
-
id:
|
|
5151
|
-
content: String(
|
|
5150
|
+
id: t80.id || generateShortId(),
|
|
5151
|
+
content: String(t80.content || "").slice(0, 500),
|
|
5152
5152
|
status,
|
|
5153
5153
|
...reason9 ? { reason: reason9 } : {},
|
|
5154
5154
|
created_at: prev?.created_at || now,
|
|
@@ -5156,7 +5156,7 @@ function createTodoTool(db) {
|
|
|
5156
5156
|
};
|
|
5157
5157
|
});
|
|
5158
5158
|
db.replaceAll(ctx.sessionId, todos);
|
|
5159
|
-
const pendingCount = todos.filter((
|
|
5159
|
+
const pendingCount = todos.filter((t80) => isOpenTodo(t80.status)).length;
|
|
5160
5160
|
return {
|
|
5161
5161
|
success: true,
|
|
5162
5162
|
output: formatTodoList(todos, pendingCount),
|
|
@@ -5491,7 +5491,7 @@ function intersectNames(base, incoming) {
|
|
|
5491
5491
|
if (!base) return incoming ? [...incoming] : void 0;
|
|
5492
5492
|
if (!incoming) return [...base];
|
|
5493
5493
|
const baseSet = new Set(base);
|
|
5494
|
-
return incoming.filter((
|
|
5494
|
+
return incoming.filter((t80) => baseSet.has(t80));
|
|
5495
5495
|
}
|
|
5496
5496
|
function findRole(roles, name) {
|
|
5497
5497
|
return roles.find((r) => r.name === name);
|
|
@@ -5635,7 +5635,7 @@ function roleScopedProvider(base, role, blocked) {
|
|
|
5635
5635
|
return allow ? allow.has(name) : true;
|
|
5636
5636
|
};
|
|
5637
5637
|
return {
|
|
5638
|
-
list: () => base.list().filter((
|
|
5638
|
+
list: () => base.list().filter((t80) => permitted(t80.name)),
|
|
5639
5639
|
get: (name) => permitted(name) ? base.get(name) : void 0
|
|
5640
5640
|
};
|
|
5641
5641
|
}
|
|
@@ -5662,7 +5662,7 @@ function createRoleScope(base, sessionRole, blocked) {
|
|
|
5662
5662
|
}
|
|
5663
5663
|
function unknownRoleTools(base, role, blocked) {
|
|
5664
5664
|
if (!role?.tools) return [];
|
|
5665
|
-
const available2 = new Set(base.list().map((
|
|
5665
|
+
const available2 = new Set(base.list().map((t80) => t80.name));
|
|
5666
5666
|
const blockedSet = new Set(blocked);
|
|
5667
5667
|
return role.tools.filter((name) => !available2.has(name) && !blockedSet.has(name));
|
|
5668
5668
|
}
|
|
@@ -5998,7 +5998,7 @@ function createToolScope(base) {
|
|
|
5998
5998
|
const permitted = (name) => allow ? allow.has(name) : true;
|
|
5999
5999
|
return {
|
|
6000
6000
|
provider: {
|
|
6001
|
-
list: () => base.list().filter((
|
|
6001
|
+
list: () => base.list().filter((t80) => permitted(t80.name)),
|
|
6002
6002
|
get: (name) => permitted(name) ? base.get(name) : void 0
|
|
6003
6003
|
},
|
|
6004
6004
|
enter: (next) => {
|
|
@@ -6168,7 +6168,7 @@ function scanHooks(dir, issues) {
|
|
|
6168
6168
|
return [...parsed.entries].map(([type, list]) => ({
|
|
6169
6169
|
type,
|
|
6170
6170
|
count: list.reduce((sum, entry) => sum + entry.hooks.length, 0)
|
|
6171
|
-
})).filter((
|
|
6171
|
+
})).filter((t80) => t80.count > 0).sort((a, b) => a.type.localeCompare(b.type));
|
|
6172
6172
|
}
|
|
6173
6173
|
function scanMcp(dir, plugin, issues) {
|
|
6174
6174
|
const path = join(dir, PLUGIN_LAYOUT.mcp);
|
|
@@ -7926,14 +7926,14 @@ var CheckpointStore = class {
|
|
|
7926
7926
|
}
|
|
7927
7927
|
async enforceTotalSize(protectSession, protectTurnIndex) {
|
|
7928
7928
|
const turns = await this.collectTurns();
|
|
7929
|
-
let total = turns.reduce((sum,
|
|
7929
|
+
let total = turns.reduce((sum, t80) => sum + t80.bytes, 0);
|
|
7930
7930
|
if (total <= this.maxTotalBytes) return;
|
|
7931
7931
|
turns.sort((a, b) => a.createdAt - b.createdAt);
|
|
7932
|
-
for (const
|
|
7932
|
+
for (const t80 of turns) {
|
|
7933
7933
|
if (total <= this.maxTotalBytes) return;
|
|
7934
|
-
if (
|
|
7935
|
-
await this.removeTurn(
|
|
7936
|
-
total -=
|
|
7934
|
+
if (t80.sessionId === protectSession && t80.turnIndex === protectTurnIndex) continue;
|
|
7935
|
+
await this.removeTurn(t80.sessionId, t80.turnIndex);
|
|
7936
|
+
total -= t80.bytes;
|
|
7937
7937
|
}
|
|
7938
7938
|
}
|
|
7939
7939
|
async collectTurns() {
|
|
@@ -10734,11 +10734,11 @@ var PromptBuilder = class {
|
|
|
10734
10734
|
memoryInitialized = false;
|
|
10735
10735
|
projectBlock(tools) {
|
|
10736
10736
|
const permLevel = this.deps.permissionManager?.getLevel() || "default";
|
|
10737
|
-
const hasPlanTool = tools.some((
|
|
10738
|
-
const hasLspTool = tools.some((
|
|
10739
|
-
const hasAskTool = tools.some((
|
|
10740
|
-
const hasTodoTool = tools.some((
|
|
10741
|
-
const hasTerminal = tools.some((
|
|
10737
|
+
const hasPlanTool = tools.some((t80) => t80.name === "enter_plan_mode");
|
|
10738
|
+
const hasLspTool = tools.some((t80) => t80.name === "lsp_diagnostics");
|
|
10739
|
+
const hasAskTool = tools.some((t80) => t80.name === "ask_user_question");
|
|
10740
|
+
const hasTodoTool = tools.some((t80) => t80.name === "todo");
|
|
10741
|
+
const hasTerminal = tools.some((t80) => t80.name === "terminal");
|
|
10742
10742
|
const needsCommandApproval = hasTerminal && permLevel === "default";
|
|
10743
10743
|
const plan = this.deps.approvedPlan?.()?.trim();
|
|
10744
10744
|
const codeSdk = renderSdkDeclaration(this.deps.codeModeTools?.() ?? []);
|
|
@@ -10775,7 +10775,7 @@ ${approvedPlanBlock(plan)}` : "");
|
|
|
10775
10775
|
return systemPromptSegments({
|
|
10776
10776
|
workDir: this.deps.site().workDir,
|
|
10777
10777
|
os: platform(),
|
|
10778
|
-
date: (/* @__PURE__ */ new Date())
|
|
10778
|
+
date: localDateString(/* @__PURE__ */ new Date()),
|
|
10779
10779
|
...role ? { role } : {},
|
|
10780
10780
|
...skillIndex ? { skillIndex } : {},
|
|
10781
10781
|
...model ? { model } : {},
|
|
@@ -11428,8 +11428,8 @@ function imageParts(content) {
|
|
|
11428
11428
|
return typeof content === "string" ? [] : content.filter((p) => p.type === "image");
|
|
11429
11429
|
}
|
|
11430
11430
|
function toolSignatureOf(config, tools) {
|
|
11431
|
-
const direct = tools.map((
|
|
11432
|
-
const declared = (config.codeModeTools?.() ?? []).map((
|
|
11431
|
+
const direct = tools.map((t80) => t80.name).join("\0");
|
|
11432
|
+
const declared = (config.codeModeTools?.() ?? []).map((t80) => t80.name).join("\0");
|
|
11433
11433
|
return declared ? `${direct}|${declared}` : direct;
|
|
11434
11434
|
}
|
|
11435
11435
|
function modelSignatureOf(info) {
|
|
@@ -11676,6 +11676,7 @@ var AgentLoop = class {
|
|
|
11676
11676
|
let toolSignature = toolSignatureOf(this.config, this.toolProvider.list());
|
|
11677
11677
|
let modelSignature = modelSignatureOf(this.modelInfo());
|
|
11678
11678
|
let planSignature = this.config.approvedPlan?.() ?? "";
|
|
11679
|
+
let dateSignature = localDateString(/* @__PURE__ */ new Date());
|
|
11679
11680
|
const streaming = this.config.streaming ?? true;
|
|
11680
11681
|
let totalInput = 0;
|
|
11681
11682
|
let totalOutput = 0;
|
|
@@ -11779,10 +11780,12 @@ var AgentLoop = class {
|
|
|
11779
11780
|
const signature = toolSignatureOf(this.config, tools);
|
|
11780
11781
|
const planNow = this.config.approvedPlan?.() ?? "";
|
|
11781
11782
|
const modelNow = modelSignatureOf(this.modelInfo());
|
|
11782
|
-
|
|
11783
|
+
const dateNow = localDateString(/* @__PURE__ */ new Date());
|
|
11784
|
+
if (signature !== toolSignature || planNow !== planSignature || modelNow !== modelSignature || dateNow !== dateSignature) {
|
|
11783
11785
|
toolSignature = signature;
|
|
11784
11786
|
planSignature = planNow;
|
|
11785
11787
|
modelSignature = modelNow;
|
|
11788
|
+
dateSignature = dateNow;
|
|
11786
11789
|
system = await this.prompt.buildSystem(input.sessionId);
|
|
11787
11790
|
}
|
|
11788
11791
|
if (turn === 1) {
|
|
@@ -11826,6 +11829,9 @@ var AgentLoop = class {
|
|
|
11826
11829
|
};
|
|
11827
11830
|
}
|
|
11828
11831
|
}
|
|
11832
|
+
for (const notice3 of this.config.memory?.takeNotices?.() ?? []) {
|
|
11833
|
+
yield { type: "notice", code: notice3.code, text: notice3.text };
|
|
11834
|
+
}
|
|
11829
11835
|
}
|
|
11830
11836
|
const turnStart = Date.now();
|
|
11831
11837
|
const llmSpan = this.telemetry.startSpan(
|
|
@@ -15068,11 +15074,11 @@ function buildSearchQuery(query) {
|
|
|
15068
15074
|
}
|
|
15069
15075
|
const latinPart = cleaned.replace(CJK_RUN, " ");
|
|
15070
15076
|
for (const w of latinPart.split(/\s+/)) {
|
|
15071
|
-
const
|
|
15072
|
-
if (
|
|
15077
|
+
const t80 = w.trim();
|
|
15078
|
+
if (t80.length >= 2) terms.add(t80);
|
|
15073
15079
|
}
|
|
15074
15080
|
if (terms.size === 0) return null;
|
|
15075
|
-
return [...terms].map((
|
|
15081
|
+
return [...terms].map((t80) => `"${t80.replace(/"/g, "")}"`).join(" OR ");
|
|
15076
15082
|
}
|
|
15077
15083
|
// src/memory/db.ts
|
|
15078
15084
|
var NS2 = "memory";
|
|
@@ -15105,6 +15111,14 @@ var MIGRATIONS2 = [
|
|
|
15105
15111
|
);
|
|
15106
15112
|
`);
|
|
15107
15113
|
}
|
|
15114
|
+
},
|
|
15115
|
+
{
|
|
15116
|
+
id: "memory-blocked-flag",
|
|
15117
|
+
version: 2,
|
|
15118
|
+
description: "\u6761\u76EE\u52A0 blocked \u6807\u8BB0\uFF1A\u547D\u4E2D\u6CE8\u5165\u6A21\u5F0F\u7684\u539F\u6587\u7559\u5728\u76D8\u4E0A\uFF0C\u53EA\u5728\u6E32\u67D3\u7ED9\u6A21\u578B\u65F6\u906E\u7F69",
|
|
15119
|
+
up: (db) => {
|
|
15120
|
+
db.exec("ALTER TABLE memory_entries ADD COLUMN blocked INTEGER NOT NULL DEFAULT 0");
|
|
15121
|
+
}
|
|
15108
15122
|
}
|
|
15109
15123
|
];
|
|
15110
15124
|
var MemoryDB = class {
|
|
@@ -15151,14 +15165,15 @@ var MemoryDB = class {
|
|
|
15151
15165
|
insert(entry) {
|
|
15152
15166
|
this.db.prepare(
|
|
15153
15167
|
`
|
|
15154
|
-
INSERT INTO memory_entries (id, target, content, position, char_length, access_count, last_accessed_at, created_at, updated_at)
|
|
15155
|
-
VALUES (@id, @target, @content, @position, @charLength, @accessCount, @lastAccessedAt, @createdAt, @updatedAt)
|
|
15168
|
+
INSERT INTO memory_entries (id, target, content, position, char_length, access_count, last_accessed_at, created_at, updated_at, blocked)
|
|
15169
|
+
VALUES (@id, @target, @content, @position, @charLength, @accessCount, @lastAccessedAt, @createdAt, @updatedAt, @blocked)
|
|
15156
15170
|
`
|
|
15157
15171
|
).run({
|
|
15158
15172
|
...entry,
|
|
15159
15173
|
createdAt: entry.createdAt.getTime(),
|
|
15160
15174
|
updatedAt: entry.updatedAt.getTime(),
|
|
15161
|
-
lastAccessedAt: entry.lastAccessedAt.getTime()
|
|
15175
|
+
lastAccessedAt: entry.lastAccessedAt.getTime(),
|
|
15176
|
+
blocked: entry.blocked ? 1 : 0
|
|
15162
15177
|
});
|
|
15163
15178
|
if (entry.position >= this.nextPosition[entry.target]) {
|
|
15164
15179
|
this.nextPosition[entry.target] = entry.position + 1;
|
|
@@ -15196,11 +15211,12 @@ var MemoryDB = class {
|
|
|
15196
15211
|
const existing = this.db.prepare("SELECT rowid FROM memory_entries WHERE target = ? AND content = ?").get(target, oldContent);
|
|
15197
15212
|
if (!existing) return;
|
|
15198
15213
|
this.db.prepare(
|
|
15199
|
-
"UPDATE memory_entries SET content = @content, char_length = @charLength, updated_at = @updatedAt WHERE rowid = @rowid"
|
|
15214
|
+
"UPDATE memory_entries SET content = @content, char_length = @charLength, updated_at = @updatedAt, blocked = @blocked WHERE rowid = @rowid"
|
|
15200
15215
|
).run({
|
|
15201
15216
|
content: newEntry.content,
|
|
15202
15217
|
charLength: newEntry.charLength,
|
|
15203
15218
|
updatedAt: newEntry.updatedAt.getTime(),
|
|
15219
|
+
blocked: newEntry.blocked ? 1 : 0,
|
|
15204
15220
|
rowid: existing.rowid
|
|
15205
15221
|
});
|
|
15206
15222
|
this.unsyncFts(existing.rowid);
|
|
@@ -15212,6 +15228,9 @@ var MemoryDB = class {
|
|
|
15212
15228
|
this.db.prepare("DELETE FROM memory_entries WHERE rowid = ?").run(existing.rowid);
|
|
15213
15229
|
this.unsyncFts(existing.rowid);
|
|
15214
15230
|
}
|
|
15231
|
+
setBlocked(id, blocked) {
|
|
15232
|
+
this.db.prepare("UPDATE memory_entries SET blocked = @blocked WHERE id = @id").run({ id, blocked: blocked ? 1 : 0 });
|
|
15233
|
+
}
|
|
15215
15234
|
updatePosition(id, position) {
|
|
15216
15235
|
this.db.prepare("UPDATE memory_entries SET position = @position WHERE id = @id").run({
|
|
15217
15236
|
id,
|
|
@@ -15280,7 +15299,8 @@ var MemoryDB = class {
|
|
|
15280
15299
|
createdAt: new Date(row["created_at"]),
|
|
15281
15300
|
updatedAt: new Date(row["updated_at"]),
|
|
15282
15301
|
accessCount: row["access_count"],
|
|
15283
|
-
lastAccessedAt: new Date(row["last_accessed_at"] ?? 0)
|
|
15302
|
+
lastAccessedAt: new Date(row["last_accessed_at"] ?? 0),
|
|
15303
|
+
blocked: Number(row["blocked"] ?? 0) !== 0
|
|
15284
15304
|
};
|
|
15285
15305
|
}
|
|
15286
15306
|
};
|
|
@@ -15298,6 +15318,7 @@ var MEM_DIR = join(homedir(), ".epoch", "memories");
|
|
|
15298
15318
|
var ENTRY_DELIMITER = "\n\xA7 ";
|
|
15299
15319
|
var MAX_ENTRY_CHARS = 4e3;
|
|
15300
15320
|
var MAX_CONSOLIDATION_FAILURES_PER_TURN = 3;
|
|
15321
|
+
var QUOTA_WARN_PCT = 90;
|
|
15301
15322
|
var SHORT_ID_HEX = 6;
|
|
15302
15323
|
var INDEX_SUMMARY_CHARS = 80;
|
|
15303
15324
|
var INJECTION_PATTERNS = [
|
|
@@ -15309,6 +15330,9 @@ var INJECTION_PATTERNS = [
|
|
|
15309
15330
|
{ pattern: /^assistant:/im, label: "role prefix" },
|
|
15310
15331
|
{ pattern: /^user:/im, label: "role prefix" }
|
|
15311
15332
|
];
|
|
15333
|
+
function injectionLabelOf(content) {
|
|
15334
|
+
return INJECTION_PATTERNS.find((p) => p.pattern.test(content))?.label;
|
|
15335
|
+
}
|
|
15312
15336
|
function contentKey(target, content) {
|
|
15313
15337
|
return `${target}\0${content}`;
|
|
15314
15338
|
}
|
|
@@ -15326,6 +15350,9 @@ var MemoryManager = class {
|
|
|
15326
15350
|
initialized = false;
|
|
15327
15351
|
consolidationFailures = 0;
|
|
15328
15352
|
reviewIssues = [];
|
|
15353
|
+
notices = [];
|
|
15354
|
+
notifiedBlocked = false;
|
|
15355
|
+
notifiedQuota = false;
|
|
15329
15356
|
constructor(dbPath, memDir, config) {
|
|
15330
15357
|
const dp = dbPath ?? DB_PATH;
|
|
15331
15358
|
const md = memDir ?? MEM_DIR;
|
|
@@ -15353,55 +15380,103 @@ var MemoryManager = class {
|
|
|
15353
15380
|
await this.flushToDisk();
|
|
15354
15381
|
}
|
|
15355
15382
|
async reloadFromDisk() {
|
|
15383
|
+
let blockedCount = 0;
|
|
15356
15384
|
for (const target of ["memory", "user"]) {
|
|
15357
15385
|
const file = target === "memory" ? "MEMORY.md" : "USER.md";
|
|
15358
15386
|
const fromDisk = this.readFile(join(this.memDir, file));
|
|
15359
15387
|
const deduped = [...new Set(fromDisk)];
|
|
15360
|
-
const
|
|
15361
|
-
|
|
15388
|
+
const classified = deduped.map((c) => this.classifyEntry(c));
|
|
15389
|
+
blockedCount += classified.filter((e) => e.blocked).length;
|
|
15390
|
+
this.syncTarget(target, classified);
|
|
15362
15391
|
}
|
|
15363
15392
|
this.assignIds();
|
|
15364
15393
|
this.snapshot = this.renderIndexBlock();
|
|
15394
|
+
if (blockedCount > 0 && !this.notifiedBlocked) {
|
|
15395
|
+
this.notifiedBlocked = true;
|
|
15396
|
+
this.notices.push({
|
|
15397
|
+
code: "memory-blocked",
|
|
15398
|
+
text: t("memory_review.blocked_notice", {
|
|
15399
|
+
count: blockedCount,
|
|
15400
|
+
path: join(this.memDir, "MEMORY.md")
|
|
15401
|
+
})
|
|
15402
|
+
});
|
|
15403
|
+
}
|
|
15404
|
+
this.checkQuota();
|
|
15405
|
+
}
|
|
15406
|
+
blockedPlaceholder(content) {
|
|
15407
|
+
return t("memory_review.blocked_placeholder", {
|
|
15408
|
+
pattern: injectionLabelOf(content) ?? "unknown"
|
|
15409
|
+
});
|
|
15410
|
+
}
|
|
15411
|
+
refuseInjection(label) {
|
|
15412
|
+
this.reviewIssues.push({
|
|
15413
|
+
type: "injection",
|
|
15414
|
+
severity: "high",
|
|
15415
|
+
message: t("memory_review.injection", { pattern: label }),
|
|
15416
|
+
suggestion: t("memory_review.injection_suggestion")
|
|
15417
|
+
});
|
|
15418
|
+
return {
|
|
15419
|
+
success: false,
|
|
15420
|
+
done: true,
|
|
15421
|
+
error: t("memory_review.write_blocked", { pattern: label })
|
|
15422
|
+
};
|
|
15423
|
+
}
|
|
15424
|
+
takeNotices() {
|
|
15425
|
+
const out = this.notices;
|
|
15426
|
+
this.notices = [];
|
|
15427
|
+
return out;
|
|
15428
|
+
}
|
|
15429
|
+
checkQuota() {
|
|
15430
|
+
if (this.notifiedQuota) return;
|
|
15431
|
+
const b = this.getBudget().memory;
|
|
15432
|
+
if (b.percent < QUOTA_WARN_PCT) return;
|
|
15433
|
+
this.notifiedQuota = true;
|
|
15434
|
+
this.notices.push({
|
|
15435
|
+
code: "memory-quota",
|
|
15436
|
+
text: t("memory_review.quota", { percent: b.percent, current: b.current, limit: b.limit })
|
|
15437
|
+
});
|
|
15365
15438
|
}
|
|
15366
15439
|
assignIds() {
|
|
15367
15440
|
this.ids.clear();
|
|
15368
15441
|
this.idByContent.clear();
|
|
15369
15442
|
for (const target of ["memory", "user"]) {
|
|
15370
15443
|
const prefix = target === "memory" ? "m" : "u";
|
|
15371
|
-
for (const
|
|
15372
|
-
const hash = createHash("sha256").update(content).digest("hex");
|
|
15444
|
+
for (const entry of this.db.getAll(target)) {
|
|
15445
|
+
const hash = createHash("sha256").update(entry.content).digest("hex");
|
|
15373
15446
|
let id = `${prefix}:${hash.slice(0, SHORT_ID_HEX)}`;
|
|
15374
15447
|
for (let len = SHORT_ID_HEX + 4; this.ids.has(id) && len <= hash.length; len += 4) {
|
|
15375
15448
|
id = `${prefix}:${hash.slice(0, len)}`;
|
|
15376
15449
|
}
|
|
15377
|
-
this.ids.set(id, { target, content });
|
|
15378
|
-
this.idByContent.set(contentKey(target, content), id);
|
|
15450
|
+
this.ids.set(id, { target, content: entry.content, blocked: entry.blocked });
|
|
15451
|
+
this.idByContent.set(contentKey(target, entry.content), id);
|
|
15379
15452
|
}
|
|
15380
15453
|
}
|
|
15381
15454
|
}
|
|
15382
15455
|
syncTarget(target, desired) {
|
|
15383
15456
|
const existing = new Map(this.db.getAll(target).map((e) => [e.content, e]));
|
|
15384
|
-
const desiredSet = new Set(desired);
|
|
15457
|
+
const desiredSet = new Set(desired.map((d) => d.content));
|
|
15385
15458
|
for (const [content, entry] of existing) {
|
|
15386
15459
|
if (!desiredSet.has(content)) this.db.removeById(entry.id);
|
|
15387
15460
|
}
|
|
15388
|
-
for (const [i,
|
|
15389
|
-
const prev = existing.get(content);
|
|
15461
|
+
for (const [i, item] of desired.entries()) {
|
|
15462
|
+
const prev = existing.get(item.content);
|
|
15390
15463
|
if (prev) {
|
|
15391
15464
|
if (prev.position !== i) this.db.updatePosition(prev.id, i);
|
|
15465
|
+
if (prev.blocked !== item.blocked) this.db.setBlocked(prev.id, item.blocked);
|
|
15392
15466
|
continue;
|
|
15393
15467
|
}
|
|
15394
15468
|
const now = /* @__PURE__ */ new Date();
|
|
15395
15469
|
this.db.insert({
|
|
15396
15470
|
id: randomUUID(),
|
|
15397
15471
|
target,
|
|
15398
|
-
content,
|
|
15472
|
+
content: item.content,
|
|
15399
15473
|
position: i,
|
|
15400
|
-
charLength: content.length,
|
|
15474
|
+
charLength: item.content.length,
|
|
15401
15475
|
accessCount: 0,
|
|
15402
15476
|
lastAccessedAt: now,
|
|
15403
15477
|
createdAt: now,
|
|
15404
|
-
updatedAt: now
|
|
15478
|
+
updatedAt: now,
|
|
15479
|
+
blocked: item.blocked
|
|
15405
15480
|
});
|
|
15406
15481
|
}
|
|
15407
15482
|
}
|
|
@@ -15420,7 +15495,7 @@ var MemoryManager = class {
|
|
|
15420
15495
|
for (const r of results) this.db.markAccessed(r.id);
|
|
15421
15496
|
return results.map((r) => ({
|
|
15422
15497
|
id: this.idByContent.get(contentKey(r.target, r.content)) ?? r.id,
|
|
15423
|
-
content: r.content,
|
|
15498
|
+
content: r.blocked ? this.blockedPlaceholder(r.content) : r.content,
|
|
15424
15499
|
target: r.target,
|
|
15425
15500
|
score: r.score
|
|
15426
15501
|
}));
|
|
@@ -15438,22 +15513,27 @@ var MemoryManager = class {
|
|
|
15438
15513
|
missing.push(id);
|
|
15439
15514
|
continue;
|
|
15440
15515
|
}
|
|
15441
|
-
found.push({
|
|
15516
|
+
found.push({
|
|
15517
|
+
id,
|
|
15518
|
+
target: hit.target,
|
|
15519
|
+
content: hit.blocked ? this.blockedPlaceholder(hit.content) : hit.content
|
|
15520
|
+
});
|
|
15442
15521
|
const entry = this.db.findByContent(hit.target, hit.content);
|
|
15443
15522
|
if (entry) this.db.markAccessed(entry.id);
|
|
15444
15523
|
}
|
|
15445
15524
|
return { found, missing };
|
|
15446
15525
|
}
|
|
15447
15526
|
getAllEntries() {
|
|
15448
|
-
return [
|
|
15449
|
-
|
|
15450
|
-
|
|
15451
|
-
];
|
|
15527
|
+
return ["memory", "user"].flatMap(
|
|
15528
|
+
(target) => this.db.getAll(target).map((e) => ({ content: e.content, target, blocked: e.blocked }))
|
|
15529
|
+
);
|
|
15452
15530
|
}
|
|
15453
15531
|
async semanticSearch(query, limit) {
|
|
15454
15532
|
return this.search(query, limit);
|
|
15455
15533
|
}
|
|
15456
15534
|
async add(target, content) {
|
|
15535
|
+
const label = injectionLabelOf(content);
|
|
15536
|
+
if (label !== void 0) return this.refuseInjection(label);
|
|
15457
15537
|
const budget = this.getBudget()[target];
|
|
15458
15538
|
if (budget.current + content.length > budget.limit) {
|
|
15459
15539
|
this.consolidationFailures++;
|
|
@@ -15487,7 +15567,8 @@ var MemoryManager = class {
|
|
|
15487
15567
|
accessCount: 0,
|
|
15488
15568
|
lastAccessedAt: /* @__PURE__ */ new Date(),
|
|
15489
15569
|
createdAt: /* @__PURE__ */ new Date(),
|
|
15490
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
15570
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
15571
|
+
blocked: false
|
|
15491
15572
|
};
|
|
15492
15573
|
this.db.insert(entry);
|
|
15493
15574
|
this.consolidationFailures = 0;
|
|
@@ -15499,6 +15580,8 @@ var MemoryManager = class {
|
|
|
15499
15580
|
if (!existing) {
|
|
15500
15581
|
return { success: false, done: true, error: this.notFoundHint(target, oldText) };
|
|
15501
15582
|
}
|
|
15583
|
+
const label = injectionLabelOf(content);
|
|
15584
|
+
if (label !== void 0) return this.refuseInjection(label);
|
|
15502
15585
|
const budget = this.getBudget()[target];
|
|
15503
15586
|
const delta = content.length - existing.charLength;
|
|
15504
15587
|
if (budget.current + delta > budget.limit) {
|
|
@@ -15516,7 +15599,8 @@ var MemoryManager = class {
|
|
|
15516
15599
|
...existing,
|
|
15517
15600
|
content,
|
|
15518
15601
|
charLength: content.length,
|
|
15519
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
15602
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
15603
|
+
blocked: false
|
|
15520
15604
|
});
|
|
15521
15605
|
this.consolidationFailures = 0;
|
|
15522
15606
|
await this.flushToDisk();
|
|
@@ -15580,31 +15664,42 @@ var MemoryManager = class {
|
|
|
15580
15664
|
if (current) entries2.push(current.join("\n").trim());
|
|
15581
15665
|
return entries2.filter((e) => e.length > 0);
|
|
15582
15666
|
}
|
|
15583
|
-
|
|
15584
|
-
|
|
15585
|
-
|
|
15586
|
-
|
|
15587
|
-
|
|
15588
|
-
|
|
15589
|
-
|
|
15590
|
-
|
|
15591
|
-
|
|
15592
|
-
|
|
15593
|
-
}
|
|
15667
|
+
classifyEntry(content) {
|
|
15668
|
+
const label = injectionLabelOf(content);
|
|
15669
|
+
if (label !== void 0) {
|
|
15670
|
+
this.reviewIssues.push({
|
|
15671
|
+
type: "injection",
|
|
15672
|
+
severity: "high",
|
|
15673
|
+
message: t("memory_review.injection", { pattern: label }),
|
|
15674
|
+
suggestion: t("memory_review.injection_suggestion")
|
|
15675
|
+
});
|
|
15676
|
+
return { content, blocked: true };
|
|
15594
15677
|
}
|
|
15595
15678
|
if (countCodePoints(content) > MAX_ENTRY_CHARS) {
|
|
15596
|
-
return
|
|
15679
|
+
return {
|
|
15680
|
+
content: headCodePoints(content, MAX_ENTRY_CHARS) + " \u2026[\u6761\u76EE\u8FC7\u957F\u5DF2\u622A\u65AD]",
|
|
15681
|
+
blocked: false
|
|
15682
|
+
};
|
|
15597
15683
|
}
|
|
15598
|
-
return content;
|
|
15684
|
+
return { content, blocked: false };
|
|
15599
15685
|
}
|
|
15600
15686
|
indexEntries(target) {
|
|
15601
15687
|
const out = [];
|
|
15602
|
-
for (const
|
|
15603
|
-
const id = this.idByContent.get(contentKey(target, content));
|
|
15688
|
+
for (const entry of this.db.getAll(target)) {
|
|
15689
|
+
const id = this.idByContent.get(contentKey(target, entry.content));
|
|
15604
15690
|
if (!id) continue;
|
|
15605
|
-
|
|
15691
|
+
if (entry.blocked) {
|
|
15692
|
+
out.push({
|
|
15693
|
+
id,
|
|
15694
|
+
target,
|
|
15695
|
+
summary: this.blockedPlaceholder(entry.content),
|
|
15696
|
+
charLength: entry.content.length
|
|
15697
|
+
});
|
|
15698
|
+
continue;
|
|
15699
|
+
}
|
|
15700
|
+
const firstLine4 = (entry.content.split("\n")[0] ?? "").trim();
|
|
15606
15701
|
const summary = countCodePoints(firstLine4) > INDEX_SUMMARY_CHARS ? `${headCodePoints(firstLine4, INDEX_SUMMARY_CHARS)}\u2026` : firstLine4;
|
|
15607
|
-
out.push({ id, target, summary, charLength: content.length });
|
|
15702
|
+
out.push({ id, target, summary, charLength: entry.content.length });
|
|
15608
15703
|
}
|
|
15609
15704
|
return out;
|
|
15610
15705
|
}
|
|
@@ -15696,148 +15791,6 @@ var MemoryManager = class {
|
|
|
15696
15791
|
].join("\n");
|
|
15697
15792
|
}
|
|
15698
15793
|
};
|
|
15699
|
-
var DEFAULT_INTERVAL = 5 * 60 * 1e3;
|
|
15700
|
-
var QUOTA_WARN_PCT = 90;
|
|
15701
|
-
var MemoryReviewer = class {
|
|
15702
|
-
manager;
|
|
15703
|
-
timer = null;
|
|
15704
|
-
issues = [];
|
|
15705
|
-
interval;
|
|
15706
|
-
constructor(manager, interval = DEFAULT_INTERVAL) {
|
|
15707
|
-
this.manager = manager;
|
|
15708
|
-
this.interval = interval;
|
|
15709
|
-
}
|
|
15710
|
-
start() {
|
|
15711
|
-
if (this.timer) return;
|
|
15712
|
-
this.scanForIssues().catch(() => {
|
|
15713
|
-
});
|
|
15714
|
-
this.timer = setInterval(() => {
|
|
15715
|
-
this.scanForIssues().catch(() => {
|
|
15716
|
-
});
|
|
15717
|
-
}, this.interval);
|
|
15718
|
-
}
|
|
15719
|
-
stop() {
|
|
15720
|
-
if (this.timer) {
|
|
15721
|
-
clearInterval(this.timer);
|
|
15722
|
-
this.timer = null;
|
|
15723
|
-
}
|
|
15724
|
-
}
|
|
15725
|
-
getIssues() {
|
|
15726
|
-
return [...this.issues];
|
|
15727
|
-
}
|
|
15728
|
-
clearIssues() {
|
|
15729
|
-
this.issues = [];
|
|
15730
|
-
}
|
|
15731
|
-
async scanForIssues() {
|
|
15732
|
-
const newIssues = [];
|
|
15733
|
-
const dups = await this.checkDuplicates();
|
|
15734
|
-
newIssues.push(...dups);
|
|
15735
|
-
const contras = await this.checkContradictions();
|
|
15736
|
-
newIssues.push(...contras);
|
|
15737
|
-
const warns = this.checkQuota();
|
|
15738
|
-
newIssues.push(...warns);
|
|
15739
|
-
this.issues = newIssues;
|
|
15740
|
-
return newIssues;
|
|
15741
|
-
}
|
|
15742
|
-
async checkDuplicates() {
|
|
15743
|
-
const issues = [];
|
|
15744
|
-
const allEntries = this.manager.getAllEntries();
|
|
15745
|
-
const seen = /* @__PURE__ */ new Set();
|
|
15746
|
-
for (let i = 0; i < allEntries.length; i++) {
|
|
15747
|
-
if (seen.has(i)) continue;
|
|
15748
|
-
const entryI = allEntries[i];
|
|
15749
|
-
if (!entryI) continue;
|
|
15750
|
-
for (let j = i + 1; j < allEntries.length; j++) {
|
|
15751
|
-
if (seen.has(j)) continue;
|
|
15752
|
-
const entryJ = allEntries[j];
|
|
15753
|
-
if (!entryJ) continue;
|
|
15754
|
-
const sim = textSimilarity(entryI.content, entryJ.content);
|
|
15755
|
-
if (sim > 0.7) {
|
|
15756
|
-
seen.add(i);
|
|
15757
|
-
seen.add(j);
|
|
15758
|
-
issues.push({
|
|
15759
|
-
type: "duplicate",
|
|
15760
|
-
severity: sim > 0.95 ? "high" : "medium",
|
|
15761
|
-
message: t("memory_review.duplicate", { percent: (sim * 100).toFixed(0) }),
|
|
15762
|
-
entries: [entryI.content, entryJ.content],
|
|
15763
|
-
suggestion: t("memory_review.duplicate_suggestion")
|
|
15764
|
-
});
|
|
15765
|
-
}
|
|
15766
|
-
}
|
|
15767
|
-
}
|
|
15768
|
-
return issues;
|
|
15769
|
-
}
|
|
15770
|
-
async checkContradictions() {
|
|
15771
|
-
const issues = [];
|
|
15772
|
-
const entries2 = this.manager.getAllEntries();
|
|
15773
|
-
const contents = entries2.map((e) => e.content.toLowerCase());
|
|
15774
|
-
const patterns = [
|
|
15775
|
-
{
|
|
15776
|
-
a: /pnpm|yarn|npm/,
|
|
15777
|
-
b: /pnpm|yarn|npm/,
|
|
15778
|
-
label: t("memory_review.label_package_manager")
|
|
15779
|
-
},
|
|
15780
|
-
{
|
|
15781
|
-
a: /typescript|javascript|ts|js/,
|
|
15782
|
-
b: /typescript|javascript|ts|js/,
|
|
15783
|
-
label: t("memory_review.label_language")
|
|
15784
|
-
},
|
|
15785
|
-
{
|
|
15786
|
-
a: /node\s*(18|20|22)/,
|
|
15787
|
-
b: /node\s*(18|20|22)/,
|
|
15788
|
-
label: t("memory_review.label_node_version")
|
|
15789
|
-
}
|
|
15790
|
-
];
|
|
15791
|
-
for (let i = 0; i < contents.length; i++) {
|
|
15792
|
-
for (let j = i + 1; j < contents.length; j++) {
|
|
15793
|
-
const contI = contents[i];
|
|
15794
|
-
const contJ = contents[j];
|
|
15795
|
-
if (!contI || !contJ) continue;
|
|
15796
|
-
for (const { a, b, label } of patterns) {
|
|
15797
|
-
const ma = a.exec(contI), mb = b.exec(contJ);
|
|
15798
|
-
if (ma && mb && ma[0] !== mb[0]) {
|
|
15799
|
-
issues.push({
|
|
15800
|
-
type: "contradiction",
|
|
15801
|
-
severity: "medium",
|
|
15802
|
-
message: t("memory_review.contradiction", { label }),
|
|
15803
|
-
entries: [entries2[i]?.content ?? "", entries2[j]?.content ?? ""],
|
|
15804
|
-
suggestion: t("memory_review.contradiction_suggestion", { label })
|
|
15805
|
-
});
|
|
15806
|
-
}
|
|
15807
|
-
}
|
|
15808
|
-
}
|
|
15809
|
-
}
|
|
15810
|
-
return issues;
|
|
15811
|
-
}
|
|
15812
|
-
checkQuota() {
|
|
15813
|
-
const budget = this.manager.getBudget();
|
|
15814
|
-
const issues = [];
|
|
15815
|
-
if (budget.memory.percent >= QUOTA_WARN_PCT) {
|
|
15816
|
-
issues.push({
|
|
15817
|
-
type: "quota_warning",
|
|
15818
|
-
severity: budget.memory.percent >= 98 ? "high" : "medium",
|
|
15819
|
-
message: t("memory_review.quota", {
|
|
15820
|
-
percent: budget.memory.percent,
|
|
15821
|
-
current: budget.memory.current,
|
|
15822
|
-
limit: budget.memory.limit
|
|
15823
|
-
}),
|
|
15824
|
-
suggestion: t("memory_review.quota_suggestion")
|
|
15825
|
-
});
|
|
15826
|
-
}
|
|
15827
|
-
return issues;
|
|
15828
|
-
}
|
|
15829
|
-
};
|
|
15830
|
-
function textSimilarity(a, b) {
|
|
15831
|
-
const wordsA = new Set(a.toLowerCase().split(/\s+/));
|
|
15832
|
-
const wordsB = new Set(b.toLowerCase().split(/\s+/));
|
|
15833
|
-
if (wordsA.size === 0 && wordsB.size === 0) return 1;
|
|
15834
|
-
let intersection = 0;
|
|
15835
|
-
for (const w of wordsA) {
|
|
15836
|
-
if (wordsB.has(w)) intersection++;
|
|
15837
|
-
}
|
|
15838
|
-
const union = /* @__PURE__ */ new Set([...wordsA, ...wordsB]);
|
|
15839
|
-
return intersection / union.size;
|
|
15840
|
-
}
|
|
15841
15794
|
var ATTACHMENT_RETENTION_DEFAULTS = {
|
|
15842
15795
|
maxAgeDays: 7,
|
|
15843
15796
|
maxTotalBytes: ATTACHMENT_MAX_TOTAL_BYTES
|
|
@@ -17144,7 +17097,7 @@ function checkFtsHealth(db) {
|
|
|
17144
17097
|
messages,
|
|
17145
17098
|
tables,
|
|
17146
17099
|
missingTriggers,
|
|
17147
|
-
healthy: missingTriggers.length === 0 && tables.every((
|
|
17100
|
+
healthy: missingTriggers.length === 0 && tables.every((t80) => t80.error === null && t80.drift === null && t80.rows === messages),
|
|
17148
17101
|
indexBytes: indexBytesOf(db),
|
|
17149
17102
|
space: spaceOf(db)
|
|
17150
17103
|
};
|
|
@@ -18488,11 +18441,11 @@ var ApprovalCache = class {
|
|
|
18488
18441
|
return !rel.startsWith("..") && !isAbsolute(rel);
|
|
18489
18442
|
}
|
|
18490
18443
|
normalizeTarget(target) {
|
|
18491
|
-
const
|
|
18492
|
-
if (!
|
|
18493
|
-
const unix =
|
|
18444
|
+
const t80 = target.trim();
|
|
18445
|
+
if (!t80) return "";
|
|
18446
|
+
const unix = t80.replace(/\\/g, "/");
|
|
18494
18447
|
if (unix.startsWith("/") || /^[A-Za-z]:\//.test(unix)) return resolve(unix);
|
|
18495
|
-
return
|
|
18448
|
+
return t80;
|
|
18496
18449
|
}
|
|
18497
18450
|
load() {
|
|
18498
18451
|
try {
|
|
@@ -18595,9 +18548,9 @@ function createSharedPermissionState(cache) {
|
|
|
18595
18548
|
};
|
|
18596
18549
|
}
|
|
18597
18550
|
function cacheKeyFor(raw) {
|
|
18598
|
-
const
|
|
18599
|
-
if (!
|
|
18600
|
-
return
|
|
18551
|
+
const t80 = raw.trim();
|
|
18552
|
+
if (!t80) return null;
|
|
18553
|
+
return t80.slice(0, 500);
|
|
18601
18554
|
}
|
|
18602
18555
|
// src/permission/manager.ts
|
|
18603
18556
|
var log3 = createLogger("permission");
|
|
@@ -22044,7 +21997,8 @@ function loadOne(mdFile, catPath, category, dirName, spec, skills, issues) {
|
|
|
22044
21997
|
}
|
|
22045
21998
|
const parsed = parseSkillFrontmatter(raw, label);
|
|
22046
21999
|
issues.push(...parsed.issues);
|
|
22047
|
-
const
|
|
22000
|
+
const declared = parsed.fm.name?.trim();
|
|
22001
|
+
const base = declared ? declared : dirName;
|
|
22048
22002
|
const name = spec.prefix ? `${spec.prefix}:${base}` : base;
|
|
22049
22003
|
const previous = skills.get(name);
|
|
22050
22004
|
if (previous && previous.scope !== scope) {
|
|
@@ -22094,6 +22048,18 @@ function normalizeDirs(input) {
|
|
|
22094
22048
|
}
|
|
22095
22049
|
return dirs.length > 0 ? dirs : [{ dir: SKILLS_DIR, source: "user" }];
|
|
22096
22050
|
}
|
|
22051
|
+
function realpathOfDeepestExisting(target) {
|
|
22052
|
+
const tail = [];
|
|
22053
|
+
let cur = target;
|
|
22054
|
+
while (!existsSync(cur)) {
|
|
22055
|
+
const parent = dirname(cur);
|
|
22056
|
+
if (parent === cur) return target;
|
|
22057
|
+
tail.unshift(basename(cur));
|
|
22058
|
+
cur = parent;
|
|
22059
|
+
}
|
|
22060
|
+
const real = realpathSync(cur);
|
|
22061
|
+
return tail.length === 0 ? real : join(real, ...tail);
|
|
22062
|
+
}
|
|
22097
22063
|
var SkillSystem = class {
|
|
22098
22064
|
dirs;
|
|
22099
22065
|
skillsDir;
|
|
@@ -22186,12 +22152,16 @@ var SkillSystem = class {
|
|
|
22186
22152
|
}
|
|
22187
22153
|
async create(name, content, category = "general") {
|
|
22188
22154
|
this.scanIfNeeded();
|
|
22189
|
-
|
|
22190
|
-
const
|
|
22155
|
+
const declared = parseSkillFrontmatter(content, name).fm.name?.trim();
|
|
22156
|
+
const indexName = declared ? declared : name;
|
|
22157
|
+
if (this.skills.has(indexName)) throw new Error(`Skill "${indexName}" already exists`);
|
|
22158
|
+
const dir = join(this.skillsDir, category, indexName);
|
|
22191
22159
|
const base = resolve(this.skillsDir);
|
|
22192
22160
|
const resolved = resolve(dir);
|
|
22193
22161
|
if (resolved === base || !resolved.startsWith(base + sep)) {
|
|
22194
|
-
throw new Error(
|
|
22162
|
+
throw new Error(
|
|
22163
|
+
`Skill name or category escapes the skills directory: ${category}/${indexName}`
|
|
22164
|
+
);
|
|
22195
22165
|
}
|
|
22196
22166
|
mkdirSync(dir, { recursive: true });
|
|
22197
22167
|
writeFileSync(join(dir, "SKILL.md"), content, "utf-8");
|
|
@@ -22205,7 +22175,7 @@ var SkillSystem = class {
|
|
|
22205
22175
|
}
|
|
22206
22176
|
async patch(name, oldString, newString, filePath) {
|
|
22207
22177
|
const skill = this.requireWritable(name);
|
|
22208
|
-
const targetPath =
|
|
22178
|
+
const targetPath = this.resolveInsideSkill(skill.path, filePath ?? "SKILL.md");
|
|
22209
22179
|
const raw = readFileSync(targetPath, "utf-8");
|
|
22210
22180
|
if (!raw.includes(oldString)) throw new Error("old_string not found");
|
|
22211
22181
|
writeFileSync(targetPath, raw.replace(oldString, newString), "utf-8");
|
|
@@ -22225,21 +22195,30 @@ var SkillSystem = class {
|
|
|
22225
22195
|
`filePath must be under references/templates/scripts/assets, got: ${filePath}`
|
|
22226
22196
|
);
|
|
22227
22197
|
}
|
|
22228
|
-
const resolved =
|
|
22229
|
-
const baseDir = resolve(skill.path);
|
|
22230
|
-
if (resolved !== baseDir && !resolved.startsWith(baseDir + sep)) {
|
|
22231
|
-
throw new Error("Path traversal denied");
|
|
22232
|
-
}
|
|
22198
|
+
const resolved = this.resolveInsideSkill(skill.path, filePath);
|
|
22233
22199
|
mkdirSync(dirname(resolved), { recursive: true });
|
|
22234
22200
|
writeFileSync(resolved, content, "utf-8");
|
|
22235
22201
|
this.invalidateCache();
|
|
22236
22202
|
}
|
|
22237
22203
|
async removeFile(name, filePath) {
|
|
22238
22204
|
const skill = this.requireWritable(name);
|
|
22239
|
-
const resolved =
|
|
22205
|
+
const resolved = this.resolveInsideSkill(skill.path, filePath);
|
|
22240
22206
|
rmSync(resolved);
|
|
22241
22207
|
this.invalidateCache();
|
|
22242
22208
|
}
|
|
22209
|
+
resolveInsideSkill(skillPath, relPath) {
|
|
22210
|
+
const base = resolve(skillPath);
|
|
22211
|
+
const resolved = resolve(join(skillPath, relPath));
|
|
22212
|
+
if (resolved !== base && !resolved.startsWith(base + sep)) {
|
|
22213
|
+
throw new Error("Path traversal denied");
|
|
22214
|
+
}
|
|
22215
|
+
const realBase = realpathSync(base);
|
|
22216
|
+
const realTarget = realpathOfDeepestExisting(resolved);
|
|
22217
|
+
if (realTarget !== realBase && !realTarget.startsWith(realBase + sep)) {
|
|
22218
|
+
throw new Error("Path traversal denied");
|
|
22219
|
+
}
|
|
22220
|
+
return resolved;
|
|
22221
|
+
}
|
|
22243
22222
|
requireWritable(name) {
|
|
22244
22223
|
this.scanIfNeeded();
|
|
22245
22224
|
const skill = this.skills.get(name);
|
|
@@ -22648,12 +22627,21 @@ var SkillLearner = class {
|
|
|
22648
22627
|
if (!parsed.name || !parsed.content) {
|
|
22649
22628
|
return { created: false, reason: "missing-name-or-content" };
|
|
22650
22629
|
}
|
|
22651
|
-
|
|
22630
|
+
const { fm } = parseSkillFrontmatter(parsed.content, parsed.name);
|
|
22631
|
+
const declared = fm.name?.trim();
|
|
22632
|
+
if (!declared) {
|
|
22652
22633
|
return { created: false, reason: "missing-frontmatter" };
|
|
22653
22634
|
}
|
|
22635
|
+
if ((fm.description ?? "").trim() === "") {
|
|
22636
|
+
return { created: false, reason: "missing-description" };
|
|
22637
|
+
}
|
|
22638
|
+
const category = parsed.category?.trim() || "general";
|
|
22639
|
+
if (skillSegmentIssue(declared) !== void 0 || skillSegmentIssue(category) !== void 0) {
|
|
22640
|
+
return { created: false, reason: "invalid-name" };
|
|
22641
|
+
}
|
|
22654
22642
|
try {
|
|
22655
|
-
await this.skillSystem.create(
|
|
22656
|
-
return { created: true, skillName:
|
|
22643
|
+
await this.skillSystem.create(declared, parsed.content, category);
|
|
22644
|
+
return { created: true, skillName: declared };
|
|
22657
22645
|
} catch (err) {
|
|
22658
22646
|
return { created: false, reason: "create-failed", error: messageOf2(err) };
|
|
22659
22647
|
}
|
|
@@ -23804,4 +23792,4 @@ function trunc(stdout, stderr) {
|
|
|
23804
23792
|
* `--tree` 的命令帮助里,让用户自己知道 `--tree` 意味着什么。
|
|
23805
23793
|
*/
|
|
23806
23794
|
|
|
23807
|
-
export { AGENT_ROLE_DIAG_MODULE, ARTIFACT_MAX_TOTAL_BYTES, ARTIFACT_RETENTION_DEFAULTS, ATTACHMENT_MAX_TOTAL_BYTES, ATTACHMENT_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, LastOpenSessions, MARKETPLACE_DISPLAY_FIELDS, MARKETPLACE_FILE, MAX_AUDIT_ENTRIES, MAX_COMMAND_DEPTH, MAX_GOAL_MAX_ROUNDS, MAX_IMPORT_DEPTH, MAX_LAST_OPEN_SESSIONS, MAX_OBJECTIVE_CHARS, MAX_REASON_CHARS, MAX_RECENT_WORKSPACES, MODEL_GIVE_UP_THRESHOLD, MULTIMODAL_DEFAULTS, ManagedPolicyError, ManagedSettingsSchema, MemoryManager,
|
|
23795
|
+
export { AGENT_ROLE_DIAG_MODULE, ARTIFACT_MAX_TOTAL_BYTES, ARTIFACT_RETENTION_DEFAULTS, ATTACHMENT_MAX_TOTAL_BYTES, ATTACHMENT_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, LastOpenSessions, MARKETPLACE_DISPLAY_FIELDS, MARKETPLACE_FILE, MAX_AUDIT_ENTRIES, MAX_COMMAND_DEPTH, MAX_GOAL_MAX_ROUNDS, MAX_IMPORT_DEPTH, MAX_LAST_OPEN_SESSIONS, MAX_OBJECTIVE_CHARS, MAX_REASON_CHARS, MAX_RECENT_WORKSPACES, MODEL_GIVE_UP_THRESHOLD, MULTIMODAL_DEFAULTS, ManagedPolicyError, ManagedSettingsSchema, MemoryManager, ModelFailureTracker, NOMINAL_TOTAL_BYTES, NO_REVEALED_TOOLS, PACK_EXCLUDES, PACK_MAX_BYTES, PACK_MAX_FILES, PERMISSION_AUDIT_CODES, PLUGIN_LAYOUT, PLUGIN_MANIFEST_FILE, PLUGIN_MANIFEST_KEYS, PLUGIN_NAME_PATTERN, PLUGIN_ROOT_ENV, PLUGIN_ROOT_TOKEN, PLUGIN_VISIBILITY, POLICY_DECISIONS, PROBE_VERSION, 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_FORM_BUDGET_USD, 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_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, classifyPersistFailure, classifyProviderError, clearModelCache, coalesceRecording, codeModeWorkerPath, commandNameFromRelPath, compileRuleLists, compressionOptions, computeCost, computeCostMicros, computeExecHash, 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, displayAllows, displayCurrency, draftCommandLine, draftWidening, escapeRuleContent, estimateImageTokens, estimateRunTokens, estimateTokens, evaluateSelection, expandCommand, expandImports, explainSetting, extractArchive, findInstructionFiles, findProjectInstructions, findRole, formatBytes, formatSkillIndex, formatUsd, gateForTool, getCapability, getConfigIssues, getConfigOrigins, getImportGate, getTrustGate, hasDedicatedExecContent, hasExecTierContent, hasPromptCaching, hasProviderCredentials, hasReadTierContent, imageFromBase64, imageFromPath, imagePartTokens, importParts, importSkills, installPlugin, instructionDirs, instructionNotices, intervalSlots, inventoryProjectExtensions, inventoryTotal, isBlockCode, isCodeModeDeferred, isEmptyRules, isGoalPhase, isNonInteractive, isReadOnlySubCall, isSettingWriteLayer, isTransientPersistFailure, isValidPluginName, isWithinWindow, isWritableSettingKey, labelSlug, layerRank, listRunningSchedules, 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, persistFailureCode, persistParts, persistToolResults, persistedUsage, planPack, previewSkillImport, pricingFor, projectEntryForDisplay, pruneArtifacts, pruneAttachments, pruneRecordings, pruneSchedules, readImageSize, readMarketplaces, readPluginManifest, readPluginRecords, readProviderKeyEnv, readRecording, readSessionSurface, reclaimFtsSpace, redactLine, refreshOpenRouterMetadata, removeMarketplace, removeRecordings, removeRoleFile, removeSessionArtifacts, removeSessionAttachments, renderImportTree, renderProjectContext, renderSdkDeclaration, renderSelfIndex, repairSchedules, resolveArtifactRetention, resolveCodeModeWorker, resolveComplianceDirs, resolveEntrySource, resolveInWorkspace, resolveMarketplaceRef, resolveMentions, resolveMultimodalPolicy, resolvePolicyDirs, resolvePricingAt, 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, setDisplayCurrency, setImportGate, setPluginEnabled, setTrustGate, settingValueChoices, settingValueKind, settingWriteApply, 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 };
|