@lexwdex-org/opencode-dcp 3.4.11 → 3.4.13
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.en.md +44 -11
- package/README.md +44 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +429 -44
- package/dist/index.js.map +1 -1
- package/dist/lib/auto-prune.d.ts +20 -0
- package/dist/lib/auto-prune.d.ts.map +1 -0
- package/dist/lib/config.d.ts +16 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/hooks.d.ts +18 -0
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/prompts/compaction.d.ts +3 -1
- package/dist/lib/prompts/compaction.d.ts.map +1 -1
- package/dist/lib/prompts/store.d.ts +2 -1
- package/dist/lib/prompts/store.d.ts.map +1 -1
- package/dist/lib/prune-tool.d.ts +15 -0
- package/dist/lib/prune-tool.d.ts.map +1 -0
- package/dist/lib/session-model.d.ts +8 -0
- package/dist/lib/session-model.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,114 @@
|
|
|
1
|
+
// lib/auto-prune.ts
|
|
2
|
+
function tokenize(text) {
|
|
3
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
4
|
+
for (const match of text.toLowerCase().matchAll(/[\p{L}\p{N}]+/gu)) {
|
|
5
|
+
const word = match[0];
|
|
6
|
+
if (/[\u4e00-\u9fff]/.test(word)) {
|
|
7
|
+
if (word.length === 1) {
|
|
8
|
+
tokens.add(word);
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
for (let index = 0; index < word.length - 1; index++) {
|
|
12
|
+
tokens.add(word.slice(index, index + 2));
|
|
13
|
+
}
|
|
14
|
+
} else {
|
|
15
|
+
tokens.add(word);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return tokens;
|
|
19
|
+
}
|
|
20
|
+
function jaccard(a, b) {
|
|
21
|
+
if (a.size === 0 && b.size === 0) return 1;
|
|
22
|
+
let intersection = 0;
|
|
23
|
+
for (const token of a) {
|
|
24
|
+
if (b.has(token)) intersection++;
|
|
25
|
+
}
|
|
26
|
+
return intersection / (a.size + b.size - intersection);
|
|
27
|
+
}
|
|
28
|
+
var WINDOW_SIZE = 4;
|
|
29
|
+
var DRIFT_BASELINE = 3;
|
|
30
|
+
function extractText(parts) {
|
|
31
|
+
const texts = [];
|
|
32
|
+
for (const part of parts) {
|
|
33
|
+
if (part && typeof part === "object" && part.type === "text" && typeof part.text === "string") {
|
|
34
|
+
texts.push(part.text);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return texts.join(" ").trim();
|
|
38
|
+
}
|
|
39
|
+
var AutoPruner = class {
|
|
40
|
+
constructor(config, now) {
|
|
41
|
+
this.config = config;
|
|
42
|
+
this.now = now ?? Date.now;
|
|
43
|
+
}
|
|
44
|
+
sessions = /* @__PURE__ */ new Map();
|
|
45
|
+
now;
|
|
46
|
+
observeUserMessage(sessionID, parts, at = this.now()) {
|
|
47
|
+
const state = this.state(sessionID);
|
|
48
|
+
const text = extractText(parts);
|
|
49
|
+
const signals = this.evaluate(state, text, at);
|
|
50
|
+
if (text) {
|
|
51
|
+
state.window.push(text);
|
|
52
|
+
if (state.window.length > WINDOW_SIZE) state.window.shift();
|
|
53
|
+
}
|
|
54
|
+
state.count += 1;
|
|
55
|
+
state.lastAt = at;
|
|
56
|
+
for (const signal of signals) {
|
|
57
|
+
if (!state.pendingSignals.includes(signal)) state.pendingSignals.push(signal);
|
|
58
|
+
}
|
|
59
|
+
return { signals };
|
|
60
|
+
}
|
|
61
|
+
consumePending(sessionID, at = this.now()) {
|
|
62
|
+
const state = this.sessions.get(sessionID);
|
|
63
|
+
if (!state || state.pendingSignals.length === 0) return null;
|
|
64
|
+
const signals = [...state.pendingSignals];
|
|
65
|
+
state.pendingSignals = [];
|
|
66
|
+
if (at - state.lastTriggerAt < this.config.cooldownMs) return null;
|
|
67
|
+
state.lastTriggerAt = at;
|
|
68
|
+
return signals;
|
|
69
|
+
}
|
|
70
|
+
markPruned(sessionID, at = this.now()) {
|
|
71
|
+
const state = this.sessions.get(sessionID);
|
|
72
|
+
if (!state) return;
|
|
73
|
+
state.count = 0;
|
|
74
|
+
state.window = [];
|
|
75
|
+
state.pendingSignals = [];
|
|
76
|
+
state.lastTriggerAt = at;
|
|
77
|
+
}
|
|
78
|
+
dropSession(sessionID) {
|
|
79
|
+
this.sessions.delete(sessionID);
|
|
80
|
+
}
|
|
81
|
+
evaluate(state, text, at) {
|
|
82
|
+
if (state.count + 1 < this.config.minMessages) return [];
|
|
83
|
+
const signals = [];
|
|
84
|
+
if (state.count > 0 && at - state.lastAt >= this.config.idleGapMs) {
|
|
85
|
+
signals.push("idle-gap");
|
|
86
|
+
}
|
|
87
|
+
if (state.count >= DRIFT_BASELINE && text) {
|
|
88
|
+
const current = tokenize(text);
|
|
89
|
+
let max = 0;
|
|
90
|
+
for (let index = Math.max(0, state.window.length - DRIFT_BASELINE); index < state.window.length; index++) {
|
|
91
|
+
max = Math.max(max, jaccard(current, tokenize(state.window[index])));
|
|
92
|
+
}
|
|
93
|
+
if (max < this.config.driftThreshold) signals.push("topic-drift");
|
|
94
|
+
}
|
|
95
|
+
if (state.count + 1 >= this.config.volumeThreshold) signals.push("volume");
|
|
96
|
+
return signals;
|
|
97
|
+
}
|
|
98
|
+
state(sessionID) {
|
|
99
|
+
let state = this.sessions.get(sessionID);
|
|
100
|
+
if (!state) {
|
|
101
|
+
state = { window: [], count: 0, lastAt: 0, pendingSignals: [], lastTriggerAt: 0 };
|
|
102
|
+
this.sessions.set(sessionID, state);
|
|
103
|
+
if (this.sessions.size > 200) {
|
|
104
|
+
const oldest = this.sessions.keys().next().value;
|
|
105
|
+
if (oldest !== void 0 && oldest !== sessionID) this.sessions.delete(oldest);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return state;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
1
112
|
// lib/config.ts
|
|
2
113
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync } from "fs";
|
|
3
114
|
import { join, dirname } from "path";
|
|
@@ -864,17 +975,35 @@ var ParseErrorCode;
|
|
|
864
975
|
|
|
865
976
|
// lib/config.ts
|
|
866
977
|
var DEFAULT_FAILURE_COOLDOWN_MS = 3e4;
|
|
978
|
+
var DEFAULT_AUTO_PRUNE = {
|
|
979
|
+
enabled: true,
|
|
980
|
+
minMessages: 8,
|
|
981
|
+
volumeThreshold: 30,
|
|
982
|
+
driftThreshold: 0.18,
|
|
983
|
+
idleGapMs: 30 * 6e4,
|
|
984
|
+
cooldownMs: 5 * 6e4
|
|
985
|
+
};
|
|
867
986
|
var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
868
987
|
"$schema",
|
|
869
988
|
"enabled",
|
|
870
989
|
"autoUpdate",
|
|
871
990
|
"debug",
|
|
991
|
+
"language",
|
|
872
992
|
"commands",
|
|
873
993
|
"commands.enabled",
|
|
874
994
|
"experimental",
|
|
875
995
|
"experimental.customPrompts",
|
|
876
996
|
"summarize",
|
|
877
|
-
"summarize.failureCooldownMs"
|
|
997
|
+
"summarize.failureCooldownMs",
|
|
998
|
+
"autoPrune",
|
|
999
|
+
"autoPrune.enabled",
|
|
1000
|
+
"autoPrune.minMessages",
|
|
1001
|
+
"autoPrune.volumeThreshold",
|
|
1002
|
+
"autoPrune.driftThreshold",
|
|
1003
|
+
"autoPrune.idleGapMs",
|
|
1004
|
+
"autoPrune.cooldownMs",
|
|
1005
|
+
"tool",
|
|
1006
|
+
"tool.enabled"
|
|
878
1007
|
]);
|
|
879
1008
|
var DEPRECATED_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
880
1009
|
"compress",
|
|
@@ -949,6 +1078,13 @@ function validateConfigTypes(config) {
|
|
|
949
1078
|
if (config.debug !== void 0 && typeof config.debug !== "boolean") {
|
|
950
1079
|
errors.push({ key: "debug", expected: "boolean", actual: typeof config.debug });
|
|
951
1080
|
}
|
|
1081
|
+
if (config.language !== void 0 && config.language !== "zh" && config.language !== "en") {
|
|
1082
|
+
errors.push({
|
|
1083
|
+
key: "language",
|
|
1084
|
+
expected: '"zh" or "en"',
|
|
1085
|
+
actual: JSON.stringify(config.language)
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
952
1088
|
const commands = config.commands;
|
|
953
1089
|
if (commands !== void 0) {
|
|
954
1090
|
if (typeof commands !== "object" || commands === null || Array.isArray(commands)) {
|
|
@@ -999,6 +1135,60 @@ function validateConfigTypes(config) {
|
|
|
999
1135
|
}
|
|
1000
1136
|
}
|
|
1001
1137
|
}
|
|
1138
|
+
const autoPrune = config.autoPrune;
|
|
1139
|
+
if (autoPrune !== void 0) {
|
|
1140
|
+
if (typeof autoPrune !== "object" || autoPrune === null || Array.isArray(autoPrune)) {
|
|
1141
|
+
errors.push({
|
|
1142
|
+
key: "autoPrune",
|
|
1143
|
+
expected: "object",
|
|
1144
|
+
actual: typeof autoPrune
|
|
1145
|
+
});
|
|
1146
|
+
} else {
|
|
1147
|
+
const numericKeys = [
|
|
1148
|
+
["minMessages", 1, Number.POSITIVE_INFINITY],
|
|
1149
|
+
["volumeThreshold", 2, Number.POSITIVE_INFINITY],
|
|
1150
|
+
["driftThreshold", 0, 1],
|
|
1151
|
+
["idleGapMs", 0, Number.POSITIVE_INFINITY],
|
|
1152
|
+
["cooldownMs", 0, Number.POSITIVE_INFINITY]
|
|
1153
|
+
];
|
|
1154
|
+
for (const [key, min, max] of numericKeys) {
|
|
1155
|
+
const value = autoPrune[key];
|
|
1156
|
+
if (value !== void 0 && (typeof value !== "number" || !Number.isFinite(value) || value < min || value > max)) {
|
|
1157
|
+
errors.push({
|
|
1158
|
+
key: `autoPrune.${key}`,
|
|
1159
|
+
expected: `number in [${min}, ${max === Number.POSITIVE_INFINITY ? "\u221E" : max}]`,
|
|
1160
|
+
actual: JSON.stringify(value)
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
for (const key of ["enabled"]) {
|
|
1165
|
+
const value = autoPrune[key];
|
|
1166
|
+
if (value !== void 0 && typeof value !== "boolean") {
|
|
1167
|
+
errors.push({
|
|
1168
|
+
key: `autoPrune.${key}`,
|
|
1169
|
+
expected: "boolean",
|
|
1170
|
+
actual: typeof value
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
const tool2 = config.tool;
|
|
1177
|
+
if (tool2 !== void 0) {
|
|
1178
|
+
if (typeof tool2 !== "object" || tool2 === null || Array.isArray(tool2)) {
|
|
1179
|
+
errors.push({
|
|
1180
|
+
key: "tool",
|
|
1181
|
+
expected: "object",
|
|
1182
|
+
actual: typeof tool2
|
|
1183
|
+
});
|
|
1184
|
+
} else if (tool2.enabled !== void 0 && typeof tool2.enabled !== "boolean") {
|
|
1185
|
+
errors.push({
|
|
1186
|
+
key: "tool.enabled",
|
|
1187
|
+
expected: "boolean",
|
|
1188
|
+
actual: typeof tool2.enabled
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1002
1192
|
return errors;
|
|
1003
1193
|
}
|
|
1004
1194
|
function showConfigWarnings(ctx, configPath, configData, isProject) {
|
|
@@ -1049,6 +1239,7 @@ var defaultConfig = {
|
|
|
1049
1239
|
enabled: true,
|
|
1050
1240
|
autoUpdate: true,
|
|
1051
1241
|
debug: false,
|
|
1242
|
+
language: "zh",
|
|
1052
1243
|
commands: {
|
|
1053
1244
|
enabled: true
|
|
1054
1245
|
},
|
|
@@ -1057,6 +1248,10 @@ var defaultConfig = {
|
|
|
1057
1248
|
},
|
|
1058
1249
|
summarize: {
|
|
1059
1250
|
failureCooldownMs: DEFAULT_FAILURE_COOLDOWN_MS
|
|
1251
|
+
},
|
|
1252
|
+
autoPrune: { ...DEFAULT_AUTO_PRUNE },
|
|
1253
|
+
tool: {
|
|
1254
|
+
enabled: true
|
|
1060
1255
|
}
|
|
1061
1256
|
};
|
|
1062
1257
|
var GLOBAL_CONFIG_DIR = process.env.XDG_CONFIG_HOME ? join(process.env.XDG_CONFIG_HOME, "opencode") : join(homedir(), ".config", "opencode");
|
|
@@ -1148,12 +1343,36 @@ function mergeSummarize(base, override) {
|
|
|
1148
1343
|
failureCooldownMs: typeof override.failureCooldownMs === "number" && Number.isFinite(override.failureCooldownMs) && override.failureCooldownMs >= 0 ? override.failureCooldownMs : base.failureCooldownMs
|
|
1149
1344
|
};
|
|
1150
1345
|
}
|
|
1346
|
+
function mergeAutoPrune(base, override) {
|
|
1347
|
+
if (!override || typeof override !== "object" || Array.isArray(override)) {
|
|
1348
|
+
return base;
|
|
1349
|
+
}
|
|
1350
|
+
const number = (key, min, max = Number.POSITIVE_INFINITY) => typeof override[key] === "number" && Number.isFinite(override[key]) && override[key] >= min && override[key] <= max ? override[key] : base[key];
|
|
1351
|
+
return {
|
|
1352
|
+
enabled: typeof override.enabled === "boolean" ? override.enabled : base.enabled,
|
|
1353
|
+
minMessages: number("minMessages", 1),
|
|
1354
|
+
volumeThreshold: number("volumeThreshold", 2),
|
|
1355
|
+
driftThreshold: number("driftThreshold", 0, 1),
|
|
1356
|
+
idleGapMs: number("idleGapMs", 0),
|
|
1357
|
+
cooldownMs: number("cooldownMs", 0)
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
function mergeTool(base, override) {
|
|
1361
|
+
if (!override) {
|
|
1362
|
+
return base;
|
|
1363
|
+
}
|
|
1364
|
+
return {
|
|
1365
|
+
enabled: typeof override.enabled === "boolean" ? override.enabled : base.enabled
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1151
1368
|
function deepCloneConfig(config) {
|
|
1152
1369
|
return {
|
|
1153
1370
|
...config,
|
|
1154
1371
|
commands: { ...config.commands },
|
|
1155
1372
|
experimental: { ...config.experimental },
|
|
1156
|
-
summarize: { ...config.summarize }
|
|
1373
|
+
summarize: { ...config.summarize },
|
|
1374
|
+
autoPrune: { ...config.autoPrune },
|
|
1375
|
+
tool: { ...config.tool }
|
|
1157
1376
|
};
|
|
1158
1377
|
}
|
|
1159
1378
|
function mergeLayer(config, data) {
|
|
@@ -1161,9 +1380,12 @@ function mergeLayer(config, data) {
|
|
|
1161
1380
|
enabled: typeof data.enabled === "boolean" ? data.enabled : config.enabled,
|
|
1162
1381
|
autoUpdate: typeof data.autoUpdate === "boolean" ? data.autoUpdate : config.autoUpdate,
|
|
1163
1382
|
debug: typeof data.debug === "boolean" ? data.debug : config.debug,
|
|
1383
|
+
language: data.language === "zh" || data.language === "en" ? data.language : config.language,
|
|
1164
1384
|
commands: mergeCommands(config.commands, data.commands),
|
|
1165
1385
|
experimental: mergeExperimental(config.experimental, data.experimental),
|
|
1166
|
-
summarize: mergeSummarize(config.summarize, data.summarize)
|
|
1386
|
+
summarize: mergeSummarize(config.summarize, data.summarize),
|
|
1387
|
+
autoPrune: mergeAutoPrune(config.autoPrune, data.autoPrune),
|
|
1388
|
+
tool: mergeTool(config.tool, data.tool)
|
|
1167
1389
|
};
|
|
1168
1390
|
}
|
|
1169
1391
|
function scheduleParseWarning(ctx, title, message) {
|
|
@@ -1216,6 +1438,29 @@ Using previous/default values`
|
|
|
1216
1438
|
return config;
|
|
1217
1439
|
}
|
|
1218
1440
|
|
|
1441
|
+
// lib/session-model.ts
|
|
1442
|
+
function latestUserModel(messages) {
|
|
1443
|
+
if (!Array.isArray(messages)) return null;
|
|
1444
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
1445
|
+
const info = messages[index]?.info;
|
|
1446
|
+
if (info?.role !== "user") continue;
|
|
1447
|
+
const providerID = info.model?.providerID;
|
|
1448
|
+
const modelID = info.model?.modelID;
|
|
1449
|
+
if (typeof providerID === "string" && typeof modelID === "string") {
|
|
1450
|
+
return { providerID, modelID };
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
return null;
|
|
1454
|
+
}
|
|
1455
|
+
async function resolveSessionModel(client, sessionID) {
|
|
1456
|
+
try {
|
|
1457
|
+
const response = await client.session.messages({ path: { id: sessionID } });
|
|
1458
|
+
return latestUserModel(response.data ?? response);
|
|
1459
|
+
} catch {
|
|
1460
|
+
return null;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1219
1464
|
// lib/hooks.ts
|
|
1220
1465
|
function createSessionCompactingHandler(prompts, logger) {
|
|
1221
1466
|
return async (input, output) => {
|
|
@@ -1236,24 +1481,71 @@ function createSessionCompactingHandler(prompts, logger) {
|
|
|
1236
1481
|
}
|
|
1237
1482
|
};
|
|
1238
1483
|
}
|
|
1239
|
-
function latestUserModel(messages) {
|
|
1240
|
-
if (!Array.isArray(messages)) return null;
|
|
1241
|
-
for (let index = messages.length - 1; index >= 0; index--) {
|
|
1242
|
-
const info = messages[index]?.info;
|
|
1243
|
-
if (info?.role !== "user") continue;
|
|
1244
|
-
const providerID = info.model?.providerID;
|
|
1245
|
-
const modelID = info.model?.modelID;
|
|
1246
|
-
if (typeof providerID === "string" && typeof modelID === "string") {
|
|
1247
|
-
return { providerID, modelID };
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
return null;
|
|
1251
|
-
}
|
|
1252
1484
|
async function showToast(client, title, message, variant = "info") {
|
|
1253
1485
|
await client.tui.showToast({
|
|
1254
1486
|
body: { title, message, variant, duration: 5e3 }
|
|
1255
1487
|
}).catch(() => void 0);
|
|
1256
1488
|
}
|
|
1489
|
+
function createChatMessageHandler(autoPruner) {
|
|
1490
|
+
return async (input, _output) => {
|
|
1491
|
+
const parts = _output?.parts ?? [];
|
|
1492
|
+
autoPruner.observeUserMessage(input.sessionID, parts);
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
var SIGNAL_LABELS = {
|
|
1496
|
+
"topic-drift": "\u8BDD\u9898\u53D8\u66F4",
|
|
1497
|
+
volume: "\u6D88\u606F\u91CF\u8FBE\u5230\u9608\u503C",
|
|
1498
|
+
"idle-gap": "\u957F\u65F6\u95F4\u4E2D\u65AD\u540E\u6062\u590D"
|
|
1499
|
+
};
|
|
1500
|
+
function createEventHandler(deps) {
|
|
1501
|
+
async function triggerAutoPrune(sessionID, signals) {
|
|
1502
|
+
const reason = signals.map((signal) => SIGNAL_LABELS[signal]).join("\u3001");
|
|
1503
|
+
const model = await resolveSessionModel(deps.client, sessionID);
|
|
1504
|
+
if (!model) {
|
|
1505
|
+
deps.logger.debug("Auto prune skipped; no session model yet", { sessionId: sessionID });
|
|
1506
|
+
return;
|
|
1507
|
+
}
|
|
1508
|
+
const result = await deps.summarize.summarize({ sessionID, model });
|
|
1509
|
+
deps.autoPruner.markPruned(sessionID);
|
|
1510
|
+
if (result.status === "succeeded") {
|
|
1511
|
+
await showToast(deps.client, "DCP \u81EA\u52A8\u538B\u7F29", `\u68C0\u6D4B\u5230${reason}\uFF0C\u5DF2\u751F\u6210\u65B0\u7684\u8BED\u4E49\u68C0\u67E5\u70B9\u3002`);
|
|
1512
|
+
} else {
|
|
1513
|
+
await showToast(
|
|
1514
|
+
deps.client,
|
|
1515
|
+
"DCP \u81EA\u52A8\u538B\u7F29",
|
|
1516
|
+
`\u68C0\u6D4B\u5230${reason}\uFF0C\u4F46\u538B\u7F29\u5931\u8D25\uFF1B\u539F\u59CB\u4E0A\u4E0B\u6587\u4FDD\u6301\u4E0D\u53D8\u3002`,
|
|
1517
|
+
"warning"
|
|
1518
|
+
);
|
|
1519
|
+
}
|
|
1520
|
+
deps.logger.debug("Auto prune finished", { sessionId: sessionID, status: result.status });
|
|
1521
|
+
}
|
|
1522
|
+
return async (input) => {
|
|
1523
|
+
const event = input.event;
|
|
1524
|
+
const sessionID = event.properties?.sessionID;
|
|
1525
|
+
if (typeof sessionID !== "string" || !sessionID) return;
|
|
1526
|
+
try {
|
|
1527
|
+
if (event.type === "session.idle") {
|
|
1528
|
+
if (!deps.config.enabled) return;
|
|
1529
|
+
const signals = deps.autoPruner.consumePending(sessionID);
|
|
1530
|
+
if (signals) await triggerAutoPrune(sessionID, signals);
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
if (event.type === "session.compacted") {
|
|
1534
|
+
deps.autoPruner.markPruned(sessionID);
|
|
1535
|
+
return;
|
|
1536
|
+
}
|
|
1537
|
+
if (event.type === "session.deleted") {
|
|
1538
|
+
deps.autoPruner.dropSession(sessionID);
|
|
1539
|
+
}
|
|
1540
|
+
} catch (error) {
|
|
1541
|
+
deps.logger.warn("Event handler failed", {
|
|
1542
|
+
type: event.type,
|
|
1543
|
+
sessionId: sessionID,
|
|
1544
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1257
1549
|
function createCommandExecuteHandler(client, summarize, logger) {
|
|
1258
1550
|
return async (input, _output) => {
|
|
1259
1551
|
if (input.command !== "dcp") return;
|
|
@@ -1266,8 +1558,7 @@ function createCommandExecuteHandler(client, summarize, logger) {
|
|
|
1266
1558
|
);
|
|
1267
1559
|
throw new Error("__DCP_HELP_HANDLED__");
|
|
1268
1560
|
}
|
|
1269
|
-
const
|
|
1270
|
-
const model = latestUserModel(response.data ?? response);
|
|
1561
|
+
const model = await resolveSessionModel(client, input.sessionID);
|
|
1271
1562
|
if (!model) {
|
|
1272
1563
|
await showToast(
|
|
1273
1564
|
client,
|
|
@@ -1503,27 +1794,59 @@ import { homedir as homedir3 } from "os";
|
|
|
1503
1794
|
import { dirname as dirname2, join as join3 } from "path";
|
|
1504
1795
|
|
|
1505
1796
|
// lib/prompts/compaction.ts
|
|
1506
|
-
var COMPACTION = `\u4F60\u6B63\u5728\u751F\u6210\u5F53\u524D\u4F1A\u8BDD\u552F\u4E00\u7684\u6EDA\u52A8\u68C0\u67E5\u70B9\u3002\u8F93\u51FA\u5C06\u66FF\u4EE3\u65E7\u5BF9\u8BDD\u524D\u7F00\uFF0C\u6210\u4E3A\u540E\u7EED\u6A21\u578B\u770B\u5230\u7684\u7B2C\u4E00\u6BB5\u4E0A\u4E0B\u6587\uFF1BOpenCode \u4F1A\u5728\u5B83\u540E\u9762\u4FDD\u7559\u5C1A\u672A\u538B\u7F29\u7684\u8FD1\u671F\u5C3E\u90E8\u3002
|
|
1797
|
+
var COMPACTION = `\u4F60\u6B63\u5728\u751F\u6210\u5F53\u524D\u4F1A\u8BDD\u552F\u4E00\u7684\u6EDA\u52A8\u68C0\u67E5\u70B9\u3002\u8F93\u51FA\u5C06\u66FF\u4EE3\u65E7\u5BF9\u8BDD\u524D\u7F00\uFF0C\u6210\u4E3A\u540E\u7EED\u6A21\u578B\u770B\u5230\u7684\u7B2C\u4E00\u6BB5\u4E0A\u4E0B\u6587\uFF1BOpenCode \u4F1A\u5728\u5B83\u540E\u9762\u4FDD\u7559\u5C1A\u672A\u538B\u7F29\u7684\u8FD1\u671F\u5C3E\u90E8\u3002\u7CFB\u7EDF\u7EA7\u5185\u5BB9\uFF08AGENTS.md\u3001\u9879\u76EE\u89C4\u5219\u7B49\uFF09\u7531 OpenCode \u5728\u6BCF\u6B21\u8BF7\u6C42\u65F6\u72EC\u7ACB\u6CE8\u5165\uFF0C\u4E0D\u5C5E\u4E8E\u538B\u7F29\u8303\u56F4\uFF0C\u4E0D\u8981\u590D\u8FF0\u5B83\u4EEC\u3002
|
|
1507
1798
|
|
|
1508
|
-
\u8FD9\u4E0D\u662F\u804A\u5929\u8BB0\u5F55\u6458\u8981\uFF0C\u800C\u662F\u53EF\u76F4\u63A5\u7EE7\u7EED\u5DE5\u4F5C\u7684\u8BED\u4E49\u526A\u679D\u7ED3\u679C\uFF1A
|
|
1799
|
+
\u8FD9\u4E0D\u662F\u804A\u5929\u8BB0\u5F55\u6458\u8981\uFF0C\u800C\u662F\u53EF\u76F4\u63A5\u7EE7\u7EED\u5DE5\u4F5C\u7684\u8BED\u4E49\u526A\u679D\u7ED3\u679C\u3002\u6309\u4EE5\u4E0B\u89C4\u5219\u538B\u7F29\uFF1A
|
|
1509
1800
|
|
|
1510
1801
|
1. \u5982\u679C\u8F93\u5165\u542B\u6709\u4E0A\u4E00\u4EFD\u68C0\u67E5\u70B9\uFF0C\u628A\u4ECD\u6709\u6548\u7684\u4FE1\u606F\u5408\u5E76\u8FDB\u65B0\u68C0\u67E5\u70B9\uFF1B\u4E0D\u8981\u5D4C\u5957\u3001\u5F15\u7528\u6216\u91CD\u590D\u65E7\u68C0\u67E5\u70B9\u3002
|
|
1511
1802
|
2. \u5220\u9664\u65E0\u5173\u95F2\u804A\u3001\u5176\u4ED6\u9879\u76EE\u6216\u5176\u4ED6\u4ED3\u5E93\u7684\u5BF9\u8BDD\u3001\u91CD\u590D\u89E3\u91CA\u3001\u5DF2\u7ECF\u63A8\u7FFB\u4E14\u4E0D\u518D\u6709\u8BCA\u65AD\u4EF7\u503C\u7684\u65B9\u6848\u3002
|
|
1512
1803
|
3. \u591A\u6B21\u5DE5\u5177\u8C03\u7528\u8BD5\u9519\u6216\u5931\u8D25\u540E\u6210\u529F\u65F6\uFF0C\u53EA\u4FDD\u7559\u6700\u7EC8\u6210\u529F\u7ED3\u679C\uFF1B\u4EC5\u5F53\u6839\u56E0\u4F1A\u5F71\u54CD\u540E\u7EED\u5DE5\u4F5C\u65F6\u4FDD\u7559\u4E00\u6B21\u7B80\u77ED\u5931\u8D25\u539F\u56E0\u3002
|
|
1513
1804
|
4. \u540C\u4E00\u5185\u5BB9\u6216\u6587\u4EF6\u88AB\u91CD\u590D\u7F16\u8F91\u65F6\uFF0C\u53EA\u4FDD\u7559\u6700\u7EC8\u72B6\u6001\u3001\u4ECD\u6709\u6548\u7684\u5173\u952E\u51B3\u7B56\u548C\u5FC5\u8981\u7406\u7531\uFF0C\u4E0D\u590D\u8FF0\u6BCF\u8F6E\u4FEE\u6539\u3002
|
|
1514
|
-
5. \
|
|
1805
|
+
5. \u6309\u65F6\u95F4\u5206\u5C42\u51B3\u5B9A\u538B\u7F29\u6DF1\u5EA6\uFF1A\u65E9\u671F\u5386\u53F2\u548C\u4E2D\u90E8\u5386\u53F2\u9AD8\u5EA6\u538B\u7F29\u2014\u2014\u6BCF\u4E2A\u4E3B\u9898\u53EA\u7559\u4E00\u53E5\u8BDD\u7ED3\u8BBA\uFF0C\u4E0D\u7559\u8FC7\u7A0B\uFF1B\u6700\u8FD1\u5386\u53F2\u8F7B\u5EA6\u538B\u7F29\u2014\u2014\u5C24\u5176\u662F\u4E0E\u5F53\u524D\u4EFB\u52A1\u76F8\u5173\u7684\u5185\u5BB9\uFF0C\u4FDD\u7559\u7EE7\u7EED\u5DE5\u4F5C\u6240\u9700\u7684\u5173\u952E\u7EC6\u8282\uFF08\u6587\u4EF6\u8DEF\u5F84\u3001\u63A5\u53E3\u3001\u547D\u4EE4\u3001\u6D4B\u8BD5\u7ED3\u679C\u3001\u9519\u8BEF\u4E8B\u5B9E\u3001\u4ECD\u6709\u6548\u7684\u51B3\u7B56\u53CA\u7406\u7531\uFF09\uFF0C\u53EA\u6298\u53E0\u91CD\u590D\u4E0E\u5DF2\u5931\u6548\u7684\u5185\u5BB9\u3002\u6700\u8FD1\u5386\u53F2\u6307\u81EA\u4E0A\u4E00\u4EFD\u68C0\u67E5\u70B9\u4EE5\u6765\u7684\u65B0\u5185\u5BB9\u3002
|
|
1515
1806
|
|
|
1516
1807
|
\u4F7F\u7528\u4EE5\u4E0B\u56FA\u5B9A\u7ED3\u6784\uFF0C\u7701\u7565\u786E\u5B9E\u4E3A\u7A7A\u7684\u6761\u76EE\uFF1A
|
|
1517
1808
|
|
|
1518
|
-
## \
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
## \
|
|
1522
|
-
|
|
1809
|
+
## \u5386\u53F2\u6982\u8981
|
|
1810
|
+
\u65E9\u671F\u4E0E\u4E2D\u90E8\u5386\u53F2\u7684\u4E3B\u9898\u548C\u80CC\u666F\uFF0C\u6BCF\u9879\u4E00\u53E5\u8BDD\u7ED3\u8BBA\uFF1B\u8FDC\u671F\u5DF2\u5B8C\u6210\u4EFB\u52A1\u5F52\u5165\u6B64\u5904\uFF0C\u4E0D\u542B\u6267\u884C\u8FC7\u7A0B\u3002
|
|
1811
|
+
|
|
1812
|
+
## \u5DF2\u5B8C\u6210\u4EFB\u52A1\u7684\u6982\u62EC
|
|
1813
|
+
\u6700\u8FD1\u7684\u5DF2\u5B8C\u6210\u4EFB\u52A1\uFF0C\u6BCF\u4E2A\u4EFB\u52A1\u4E00\u53E5\u8BDD\u6982\u62EC\u5176\u7ED3\u679C\u4E0E\u5173\u952E\u4EA7\u51FA\u3002
|
|
1814
|
+
|
|
1815
|
+
## \u8FDB\u884C\u4E2D\u4EFB\u52A1\u8BE6\u60C5
|
|
1816
|
+
\u5F53\u524D\u6B63\u5728\u8FDB\u884C\u7684\u4EFB\u52A1\u9010\u9879\u5199\u6E05\uFF1A\u76EE\u6807\u3001\u5DF2\u5B8C\u6210\u6B65\u9AA4\u3001\u6D89\u53CA\u6587\u4EF6\u8DEF\u5F84\u4E0E\u63A5\u53E3\u3001\u5173\u952E\u51B3\u7B56\u3001\u9047\u5230\u7684\u963B\u585E\u3001\u4E0B\u4E00\u6B65\u5177\u4F53\u52A8\u4F5C\u3002\u672C\u8282\u5C5E\u4E8E\u8F7B\u5EA6\u538B\u7F29\u533A\uFF0C\u5B81\u53EF\u591A\u4FDD\u7559\u7EC6\u8282\uFF0C\u4E0D\u505A\u4E8C\u6B21\u63A8\u65AD\u3002
|
|
1817
|
+
|
|
1523
1818
|
## \u672A\u89E3\u51B3\u95EE\u9898
|
|
1524
|
-
|
|
1819
|
+
\u8DE8\u4EFB\u52A1\u7684\u9057\u7559\u98CE\u9669\u548C\u5F85\u786E\u8BA4\u4E8B\u9879\u3002\u53EA\u5199\u672A\u5728\u300C\u8FDB\u884C\u4E2D\u4EFB\u52A1\u8BE6\u60C5\u300D\u4E2D\u51FA\u73B0\u7684\u5185\u5BB9\uFF0C\u907F\u514D\u4E0E\u8BE5\u8282\u91CD\u590D\u3002
|
|
1820
|
+
|
|
1821
|
+
\u4FDD\u6301\u5177\u4F53\u3001\u53EF\u9A8C\u8BC1\u548C\u9879\u76EE\u5185\u805A\u3002\u8FDB\u884C\u4E2D\u7684\u4EFB\u52A1\u5FC5\u987B\u80FD\u51ED\u68C0\u67E5\u70B9\u76F4\u63A5\u7EE7\u7EED\uFF0C\u4E0D\u8981\u4F9D\u8D56\u5DF2\u88AB\u538B\u7F29\u6389\u7684\u4E2D\u95F4\u8FC7\u7A0B\u3002\u4FDD\u7559\u6587\u4EF6\u8DEF\u5F84\u3001\u63A5\u53E3\u3001\u547D\u4EE4\u3001\u6D4B\u8BD5\u7ED3\u679C\u548C\u9519\u8BEF\u4E8B\u5B9E\u7B49\u786C\u4E8B\u5B9E\uFF0C\u4F46\u4E0D\u8981\u4FDD\u7559\u6D88\u606F ID\u3001\u5757 ID\u3001\u951A\u70B9\u3001\u5360\u4F4D\u7B26\u3001\u63A7\u5236\u6807\u7B7E\u6216\u8FC7\u7A0B\u6027\u804A\u5929\u3002`;
|
|
1822
|
+
var COMPACTION_EN = `You are generating the single rolling checkpoint for this session. Your output will replace the old conversation prefix as the first context the model sees afterwards; OpenCode keeps an uncompacted recent tail right after it. System-level content (AGENTS.md, project rules, etc.) is injected independently by OpenCode on every request and is not part of compaction; do not restate it.
|
|
1525
1823
|
|
|
1526
|
-
|
|
1824
|
+
This is not a chat-log summary but a semantic pruning result one can resume working from directly. Compress by these rules:
|
|
1825
|
+
|
|
1826
|
+
1. If the input contains a previous checkpoint, merge the still-valid information into the new checkpoint; do not nest, quote, or duplicate the old one.
|
|
1827
|
+
2. Remove irrelevant chitchat, conversations about other projects or repositories, repeated explanations, and approaches that were overturned and no longer carry diagnostic value.
|
|
1828
|
+
3. When repeated tool trial-and-error ends in success, keep only the final successful outcome; retain one brief failure reason only if the root cause affects future work.
|
|
1829
|
+
4. When the same content or file was edited repeatedly, keep only the final state, the still-valid key decisions, and the necessary rationale; do not restate each round of edits.
|
|
1830
|
+
5. Choose compression depth by recency tiers: early and middle history are compressed heavily\u2014one concluding sentence per topic, no process detail; recent history is compressed lightly\u2014especially content related to the current task, keeping the key details needed to continue (file paths, interfaces, commands, test results, error facts, still-valid decisions and their rationale), folding only duplicates and invalidated content. Recent history means everything generated since the previous checkpoint.
|
|
1831
|
+
|
|
1832
|
+
Use the following fixed structure, omitting sections that are truly empty:
|
|
1833
|
+
|
|
1834
|
+
## History Overview
|
|
1835
|
+
Topics and background from early and middle history, one concluding sentence each; long-completed tasks belong here, without execution process.
|
|
1836
|
+
|
|
1837
|
+
## Completed Task Summaries
|
|
1838
|
+
Recently completed tasks, one sentence per task covering its outcome and key outputs.
|
|
1839
|
+
|
|
1840
|
+
## In-Progress Task Details
|
|
1841
|
+
For the current task, itemize: goal, completed steps, file paths and interfaces involved, key decisions, blockers encountered, concrete next actions. This is the lightly-compressed zone; prefer keeping more detail and avoid second-order inference.
|
|
1842
|
+
|
|
1843
|
+
## Unresolved Issues
|
|
1844
|
+
Cross-task lingering risks and items awaiting confirmation. Only list items not already covered under In-Progress Task Details, to avoid duplication.
|
|
1845
|
+
|
|
1846
|
+
Stay specific, verifiable, and project-cohesive. An in-progress task must be resumable straight from this checkpoint, without relying on intermediate process that was pruned away. Keep hard facts such as file paths, interfaces, commands, test results, and error facts, but never message IDs, block IDs, anchors, placeholders, control tags, or procedural chatter.`;
|
|
1847
|
+
function getCompactionPrompt(language) {
|
|
1848
|
+
return language === "en" ? COMPACTION_EN : COMPACTION;
|
|
1849
|
+
}
|
|
1527
1850
|
|
|
1528
1851
|
// lib/prompts/store.ts
|
|
1529
1852
|
function findOpencodeDir2(startDir) {
|
|
@@ -1554,15 +1877,18 @@ function normalize(content) {
|
|
|
1554
1877
|
return content.replace(/^\uFEFF/, "").replace(/\r\n?/g, "\n").replace(/<!--[\s\S]*?-->/g, "").trim();
|
|
1555
1878
|
}
|
|
1556
1879
|
var PromptStore = class {
|
|
1557
|
-
constructor(logger, workingDirectory, customPromptsEnabled = false) {
|
|
1880
|
+
constructor(logger, workingDirectory, customPromptsEnabled = false, language) {
|
|
1558
1881
|
this.logger = logger;
|
|
1559
1882
|
this.customPromptsEnabled = customPromptsEnabled;
|
|
1560
1883
|
this.paths = resolvePaths(workingDirectory);
|
|
1884
|
+
this.defaultPrompt = getCompactionPrompt(language);
|
|
1885
|
+
this.runtime = { compaction: this.defaultPrompt };
|
|
1561
1886
|
if (customPromptsEnabled) this.ensureDefaults();
|
|
1562
1887
|
this.reload();
|
|
1563
1888
|
}
|
|
1564
1889
|
paths;
|
|
1565
|
-
|
|
1890
|
+
defaultPrompt;
|
|
1891
|
+
runtime;
|
|
1566
1892
|
lastReloadAt = 0;
|
|
1567
1893
|
getRuntimePrompts() {
|
|
1568
1894
|
return { ...this.runtime };
|
|
@@ -1571,7 +1897,7 @@ var PromptStore = class {
|
|
|
1571
1897
|
const now = Date.now();
|
|
1572
1898
|
if (now - this.lastReloadAt < 1e3) return;
|
|
1573
1899
|
this.lastReloadAt = now;
|
|
1574
|
-
this.runtime = { compaction:
|
|
1900
|
+
this.runtime = { compaction: this.defaultPrompt };
|
|
1575
1901
|
if (!this.customPromptsEnabled) return;
|
|
1576
1902
|
for (const path of this.paths.overrides) {
|
|
1577
1903
|
if (!existsSync3(path)) continue;
|
|
@@ -1590,8 +1916,11 @@ var PromptStore = class {
|
|
|
1590
1916
|
ensureDefaults() {
|
|
1591
1917
|
try {
|
|
1592
1918
|
mkdirSync2(this.paths.defaultsDir, { recursive: true });
|
|
1593
|
-
writeFileSync2(
|
|
1594
|
-
|
|
1919
|
+
writeFileSync2(
|
|
1920
|
+
join3(this.paths.defaultsDir, "compaction.md"),
|
|
1921
|
+
`${this.defaultPrompt.trim()}
|
|
1922
|
+
`
|
|
1923
|
+
);
|
|
1595
1924
|
writeFileSync2(
|
|
1596
1925
|
join3(this.paths.defaultsDir, "README.md"),
|
|
1597
1926
|
"# DCP compaction prompt\n\nCopy `compaction.md` to an `overrides` directory and restart OpenCode.\n"
|
|
@@ -1604,6 +1933,42 @@ var PromptStore = class {
|
|
|
1604
1933
|
}
|
|
1605
1934
|
};
|
|
1606
1935
|
|
|
1936
|
+
// lib/prune-tool.ts
|
|
1937
|
+
import { tool } from "@opencode-ai/plugin";
|
|
1938
|
+
var PRUNE_TOOL_NAME = "dcp_prune";
|
|
1939
|
+
var PRUNE_TOOL_DESCRIPTION = `\u7ACB\u5373\u5BF9\u5F53\u524D\u4F1A\u8BDD\u6267\u884C\u8BED\u4E49\u4E0A\u4E0B\u6587\u538B\u7F29\uFF1A\u628A\u65E7\u5BF9\u8BDD\u524D\u7F00\u6298\u53E0\u4E3A\u4E00\u4E2A\u6EDA\u52A8\u68C0\u67E5\u70B9\uFF08\u4FDD\u7559\u7CFB\u7EDF\u7EA7\u89C4\u5219\u3001\u538B\u7F29\u4E2D\u90E8\u5386\u53F2\u3001\u8BE6\u8FF0\u8FDB\u884C\u4E2D\u7684\u4EFB\u52A1\uFF09\uFF0C\u8FD1\u671F\u5C3E\u90E8\u4E0D\u53D7\u5F71\u54CD\u3002
|
|
1940
|
+
|
|
1941
|
+
\u6EE1\u8DB3\u4EFB\u4E00\u542F\u53D1\u5F0F\u6761\u4EF6\u65F6\uFF0C\u5FC5\u987B\u7ACB\u5373\u8C03\u7528\u672C\u5DE5\u5177\uFF1A
|
|
1942
|
+
- \u5BF9\u8BDD\u8BDD\u9898\u53D1\u751F\u660E\u663E\u53D8\u66F4\uFF1A\u5F00\u59CB\u5904\u7406\u65B0\u7684\u95EE\u9898\u57DF\u3001\u5207\u6362\u5230\u53E6\u4E00\u4E2A\u6A21\u5757/\u4ED3\u5E93/\u4EFB\u52A1\uFF1B
|
|
1943
|
+
- \u5F53\u524D\u4EFB\u52A1\u521A\u6536\u5C3E\u5B8C\u6210\uFF0C\u5373\u5C06\u5F00\u542F\u4E0B\u4E00\u9879\u5DE5\u4F5C\uFF1B
|
|
1944
|
+
- \u5BF9\u8BDD\u8F6E\u6570\u6216\u4E0A\u4E0B\u6587\u660E\u663E\u53D8\u957F\uFF0C\u65E9\u671F\u7EC6\u8282\u5DF2\u4E0D\u9700\u8981\u9010\u5B57\u4FDD\u7559\u3002
|
|
1945
|
+
|
|
1946
|
+
\u8C03\u7528\u662F\u5B89\u5168\u7684\uFF1A\u5E76\u53D1\u8BF7\u6C42\u4F1A\u81EA\u52A8\u5408\u5E76\uFF0C\u5931\u8D25\u4E0D\u4F1A\u7834\u574F\u73B0\u6709\u4E0A\u4E0B\u6587\u3002\u4E0D\u8981\u4E3A\u540C\u4E00\u8BDD\u9898\u53CD\u590D\u8FDE\u7EED\u8C03\u7528\u3002`;
|
|
1947
|
+
function createPruneTool(deps) {
|
|
1948
|
+
return tool({
|
|
1949
|
+
description: PRUNE_TOOL_DESCRIPTION,
|
|
1950
|
+
args: {},
|
|
1951
|
+
execute: async (_args, context) => {
|
|
1952
|
+
const sessionID = context.sessionID;
|
|
1953
|
+
const model = await resolveSessionModel(deps.client, sessionID);
|
|
1954
|
+
if (!model) {
|
|
1955
|
+
return "DCP\uFF1A\u4F1A\u8BDD\u4E2D\u8FD8\u6CA1\u6709\u53EF\u7528\u7684\u6A21\u578B\u4FE1\u606F\uFF0C\u65E0\u6CD5\u6267\u884C\u538B\u7F29\u3002";
|
|
1956
|
+
}
|
|
1957
|
+
const result = await deps.summarize.summarize({ sessionID, model });
|
|
1958
|
+
if (result.status === "succeeded") {
|
|
1959
|
+
deps.logger.debug("Prune tool triggered native compaction", {
|
|
1960
|
+
sessionId: sessionID
|
|
1961
|
+
});
|
|
1962
|
+
return "DCP\uFF1A\u8BED\u4E49\u538B\u7F29\u5B8C\u6210\uFF0C\u65E7\u4E0A\u4E0B\u6587\u5DF2\u6298\u53E0\u4E3A\u65B0\u68C0\u67E5\u70B9\u3002";
|
|
1963
|
+
}
|
|
1964
|
+
if (result.status === "cooldown") {
|
|
1965
|
+
return `DCP\uFF1A\u4E0A\u4E00\u6B21\u538B\u7F29\u5931\u8D25\uFF0C${Math.ceil(result.retryAfterMs / 1e3)} \u79D2\u540E\u624D\u80FD\u91CD\u8BD5\u3002`;
|
|
1966
|
+
}
|
|
1967
|
+
return `DCP\uFF1A\u538B\u7F29\u5931\u8D25\uFF08${result.error}\uFF09\uFF0C\u539F\u59CB\u4E0A\u4E0B\u6587\u4FDD\u6301\u4E0D\u53D8\u3002`;
|
|
1968
|
+
}
|
|
1969
|
+
});
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1607
1972
|
// lib/summarize.ts
|
|
1608
1973
|
function errorMessage(error) {
|
|
1609
1974
|
if (error instanceof Error) return error.message;
|
|
@@ -1806,27 +2171,47 @@ var server = (async (ctx) => {
|
|
|
1806
2171
|
const config = getConfig(ctx);
|
|
1807
2172
|
if (!config.enabled) return {};
|
|
1808
2173
|
const logger = new Logger(config.debug);
|
|
1809
|
-
const prompts = new PromptStore(
|
|
2174
|
+
const prompts = new PromptStore(
|
|
2175
|
+
logger,
|
|
2176
|
+
ctx.directory,
|
|
2177
|
+
config.experimental.customPrompts,
|
|
2178
|
+
config.language
|
|
2179
|
+
);
|
|
1810
2180
|
const summarize = new SummarizeCoordinator(ctx.client, logger, {
|
|
1811
2181
|
failureCooldownMs: config.summarize.failureCooldownMs
|
|
1812
2182
|
});
|
|
1813
|
-
|
|
2183
|
+
const autoPruner = new AutoPruner(config.autoPrune);
|
|
2184
|
+
logger.info("DCP initialized", {
|
|
1814
2185
|
commands: config.commands.enabled,
|
|
2186
|
+
autoPrune: config.autoPrune.enabled,
|
|
2187
|
+
tool: config.tool.enabled,
|
|
1815
2188
|
customPrompts: config.experimental.customPrompts
|
|
1816
2189
|
});
|
|
1817
2190
|
startAutoUpdate(ctx, config.autoUpdate);
|
|
1818
2191
|
return {
|
|
1819
2192
|
"experimental.session.compacting": createSessionCompactingHandler(prompts, logger),
|
|
1820
|
-
...config.
|
|
1821
|
-
"
|
|
2193
|
+
...config.autoPrune.enabled && {
|
|
2194
|
+
"chat.message": createChatMessageHandler(autoPruner),
|
|
2195
|
+
event: createEventHandler({
|
|
2196
|
+
client: ctx.client,
|
|
2197
|
+
summarize,
|
|
2198
|
+
autoPruner,
|
|
2199
|
+
config: config.autoPrune,
|
|
2200
|
+
logger
|
|
2201
|
+
})
|
|
1822
2202
|
},
|
|
1823
|
-
config
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
2203
|
+
...config.tool.enabled && {
|
|
2204
|
+
tool: { [PRUNE_TOOL_NAME]: createPruneTool({ client: ctx.client, summarize, logger }) }
|
|
2205
|
+
},
|
|
2206
|
+
...config.commands.enabled && {
|
|
2207
|
+
"command.execute.before": createCommandExecuteHandler(ctx.client, summarize, logger),
|
|
2208
|
+
config: async (opencodeConfig) => {
|
|
2209
|
+
opencodeConfig.command ??= {};
|
|
2210
|
+
opencodeConfig.command.dcp = {
|
|
2211
|
+
template: "",
|
|
2212
|
+
description: "Run semantic context pruning with native compaction"
|
|
2213
|
+
};
|
|
2214
|
+
}
|
|
1830
2215
|
}
|
|
1831
2216
|
};
|
|
1832
2217
|
});
|