@lexwdex-org/opencode-dcp 3.4.11 → 3.4.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +42 -11
- package/README.md +42 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +376 -37
- 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 +14 -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 +1 -1
- package/dist/lib/prompts/compaction.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,6 +975,14 @@ 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",
|
|
@@ -874,7 +993,16 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
874
993
|
"experimental",
|
|
875
994
|
"experimental.customPrompts",
|
|
876
995
|
"summarize",
|
|
877
|
-
"summarize.failureCooldownMs"
|
|
996
|
+
"summarize.failureCooldownMs",
|
|
997
|
+
"autoPrune",
|
|
998
|
+
"autoPrune.enabled",
|
|
999
|
+
"autoPrune.minMessages",
|
|
1000
|
+
"autoPrune.volumeThreshold",
|
|
1001
|
+
"autoPrune.driftThreshold",
|
|
1002
|
+
"autoPrune.idleGapMs",
|
|
1003
|
+
"autoPrune.cooldownMs",
|
|
1004
|
+
"tool",
|
|
1005
|
+
"tool.enabled"
|
|
878
1006
|
]);
|
|
879
1007
|
var DEPRECATED_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
880
1008
|
"compress",
|
|
@@ -999,6 +1127,60 @@ function validateConfigTypes(config) {
|
|
|
999
1127
|
}
|
|
1000
1128
|
}
|
|
1001
1129
|
}
|
|
1130
|
+
const autoPrune = config.autoPrune;
|
|
1131
|
+
if (autoPrune !== void 0) {
|
|
1132
|
+
if (typeof autoPrune !== "object" || autoPrune === null || Array.isArray(autoPrune)) {
|
|
1133
|
+
errors.push({
|
|
1134
|
+
key: "autoPrune",
|
|
1135
|
+
expected: "object",
|
|
1136
|
+
actual: typeof autoPrune
|
|
1137
|
+
});
|
|
1138
|
+
} else {
|
|
1139
|
+
const numericKeys = [
|
|
1140
|
+
["minMessages", 1, Number.POSITIVE_INFINITY],
|
|
1141
|
+
["volumeThreshold", 2, Number.POSITIVE_INFINITY],
|
|
1142
|
+
["driftThreshold", 0, 1],
|
|
1143
|
+
["idleGapMs", 0, Number.POSITIVE_INFINITY],
|
|
1144
|
+
["cooldownMs", 0, Number.POSITIVE_INFINITY]
|
|
1145
|
+
];
|
|
1146
|
+
for (const [key, min, max] of numericKeys) {
|
|
1147
|
+
const value = autoPrune[key];
|
|
1148
|
+
if (value !== void 0 && (typeof value !== "number" || !Number.isFinite(value) || value < min || value > max)) {
|
|
1149
|
+
errors.push({
|
|
1150
|
+
key: `autoPrune.${key}`,
|
|
1151
|
+
expected: `number in [${min}, ${max === Number.POSITIVE_INFINITY ? "\u221E" : max}]`,
|
|
1152
|
+
actual: JSON.stringify(value)
|
|
1153
|
+
});
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
for (const key of ["enabled"]) {
|
|
1157
|
+
const value = autoPrune[key];
|
|
1158
|
+
if (value !== void 0 && typeof value !== "boolean") {
|
|
1159
|
+
errors.push({
|
|
1160
|
+
key: `autoPrune.${key}`,
|
|
1161
|
+
expected: "boolean",
|
|
1162
|
+
actual: typeof value
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
const tool2 = config.tool;
|
|
1169
|
+
if (tool2 !== void 0) {
|
|
1170
|
+
if (typeof tool2 !== "object" || tool2 === null || Array.isArray(tool2)) {
|
|
1171
|
+
errors.push({
|
|
1172
|
+
key: "tool",
|
|
1173
|
+
expected: "object",
|
|
1174
|
+
actual: typeof tool2
|
|
1175
|
+
});
|
|
1176
|
+
} else if (tool2.enabled !== void 0 && typeof tool2.enabled !== "boolean") {
|
|
1177
|
+
errors.push({
|
|
1178
|
+
key: "tool.enabled",
|
|
1179
|
+
expected: "boolean",
|
|
1180
|
+
actual: typeof tool2.enabled
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1002
1184
|
return errors;
|
|
1003
1185
|
}
|
|
1004
1186
|
function showConfigWarnings(ctx, configPath, configData, isProject) {
|
|
@@ -1057,6 +1239,10 @@ var defaultConfig = {
|
|
|
1057
1239
|
},
|
|
1058
1240
|
summarize: {
|
|
1059
1241
|
failureCooldownMs: DEFAULT_FAILURE_COOLDOWN_MS
|
|
1242
|
+
},
|
|
1243
|
+
autoPrune: { ...DEFAULT_AUTO_PRUNE },
|
|
1244
|
+
tool: {
|
|
1245
|
+
enabled: true
|
|
1060
1246
|
}
|
|
1061
1247
|
};
|
|
1062
1248
|
var GLOBAL_CONFIG_DIR = process.env.XDG_CONFIG_HOME ? join(process.env.XDG_CONFIG_HOME, "opencode") : join(homedir(), ".config", "opencode");
|
|
@@ -1148,12 +1334,36 @@ function mergeSummarize(base, override) {
|
|
|
1148
1334
|
failureCooldownMs: typeof override.failureCooldownMs === "number" && Number.isFinite(override.failureCooldownMs) && override.failureCooldownMs >= 0 ? override.failureCooldownMs : base.failureCooldownMs
|
|
1149
1335
|
};
|
|
1150
1336
|
}
|
|
1337
|
+
function mergeAutoPrune(base, override) {
|
|
1338
|
+
if (!override || typeof override !== "object" || Array.isArray(override)) {
|
|
1339
|
+
return base;
|
|
1340
|
+
}
|
|
1341
|
+
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];
|
|
1342
|
+
return {
|
|
1343
|
+
enabled: typeof override.enabled === "boolean" ? override.enabled : base.enabled,
|
|
1344
|
+
minMessages: number("minMessages", 1),
|
|
1345
|
+
volumeThreshold: number("volumeThreshold", 2),
|
|
1346
|
+
driftThreshold: number("driftThreshold", 0, 1),
|
|
1347
|
+
idleGapMs: number("idleGapMs", 0),
|
|
1348
|
+
cooldownMs: number("cooldownMs", 0)
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
function mergeTool(base, override) {
|
|
1352
|
+
if (!override) {
|
|
1353
|
+
return base;
|
|
1354
|
+
}
|
|
1355
|
+
return {
|
|
1356
|
+
enabled: typeof override.enabled === "boolean" ? override.enabled : base.enabled
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1151
1359
|
function deepCloneConfig(config) {
|
|
1152
1360
|
return {
|
|
1153
1361
|
...config,
|
|
1154
1362
|
commands: { ...config.commands },
|
|
1155
1363
|
experimental: { ...config.experimental },
|
|
1156
|
-
summarize: { ...config.summarize }
|
|
1364
|
+
summarize: { ...config.summarize },
|
|
1365
|
+
autoPrune: { ...config.autoPrune },
|
|
1366
|
+
tool: { ...config.tool }
|
|
1157
1367
|
};
|
|
1158
1368
|
}
|
|
1159
1369
|
function mergeLayer(config, data) {
|
|
@@ -1163,7 +1373,9 @@ function mergeLayer(config, data) {
|
|
|
1163
1373
|
debug: typeof data.debug === "boolean" ? data.debug : config.debug,
|
|
1164
1374
|
commands: mergeCommands(config.commands, data.commands),
|
|
1165
1375
|
experimental: mergeExperimental(config.experimental, data.experimental),
|
|
1166
|
-
summarize: mergeSummarize(config.summarize, data.summarize)
|
|
1376
|
+
summarize: mergeSummarize(config.summarize, data.summarize),
|
|
1377
|
+
autoPrune: mergeAutoPrune(config.autoPrune, data.autoPrune),
|
|
1378
|
+
tool: mergeTool(config.tool, data.tool)
|
|
1167
1379
|
};
|
|
1168
1380
|
}
|
|
1169
1381
|
function scheduleParseWarning(ctx, title, message) {
|
|
@@ -1216,6 +1428,29 @@ Using previous/default values`
|
|
|
1216
1428
|
return config;
|
|
1217
1429
|
}
|
|
1218
1430
|
|
|
1431
|
+
// lib/session-model.ts
|
|
1432
|
+
function latestUserModel(messages) {
|
|
1433
|
+
if (!Array.isArray(messages)) return null;
|
|
1434
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
1435
|
+
const info = messages[index]?.info;
|
|
1436
|
+
if (info?.role !== "user") continue;
|
|
1437
|
+
const providerID = info.model?.providerID;
|
|
1438
|
+
const modelID = info.model?.modelID;
|
|
1439
|
+
if (typeof providerID === "string" && typeof modelID === "string") {
|
|
1440
|
+
return { providerID, modelID };
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
return null;
|
|
1444
|
+
}
|
|
1445
|
+
async function resolveSessionModel(client, sessionID) {
|
|
1446
|
+
try {
|
|
1447
|
+
const response = await client.session.messages({ path: { id: sessionID } });
|
|
1448
|
+
return latestUserModel(response.data ?? response);
|
|
1449
|
+
} catch {
|
|
1450
|
+
return null;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1219
1454
|
// lib/hooks.ts
|
|
1220
1455
|
function createSessionCompactingHandler(prompts, logger) {
|
|
1221
1456
|
return async (input, output) => {
|
|
@@ -1236,24 +1471,71 @@ function createSessionCompactingHandler(prompts, logger) {
|
|
|
1236
1471
|
}
|
|
1237
1472
|
};
|
|
1238
1473
|
}
|
|
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
1474
|
async function showToast(client, title, message, variant = "info") {
|
|
1253
1475
|
await client.tui.showToast({
|
|
1254
1476
|
body: { title, message, variant, duration: 5e3 }
|
|
1255
1477
|
}).catch(() => void 0);
|
|
1256
1478
|
}
|
|
1479
|
+
function createChatMessageHandler(autoPruner) {
|
|
1480
|
+
return async (input, _output) => {
|
|
1481
|
+
const parts = _output?.parts ?? [];
|
|
1482
|
+
autoPruner.observeUserMessage(input.sessionID, parts);
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
var SIGNAL_LABELS = {
|
|
1486
|
+
"topic-drift": "\u8BDD\u9898\u53D8\u66F4",
|
|
1487
|
+
volume: "\u6D88\u606F\u91CF\u8FBE\u5230\u9608\u503C",
|
|
1488
|
+
"idle-gap": "\u957F\u65F6\u95F4\u4E2D\u65AD\u540E\u6062\u590D"
|
|
1489
|
+
};
|
|
1490
|
+
function createEventHandler(deps) {
|
|
1491
|
+
async function triggerAutoPrune(sessionID, signals) {
|
|
1492
|
+
const reason = signals.map((signal) => SIGNAL_LABELS[signal]).join("\u3001");
|
|
1493
|
+
const model = await resolveSessionModel(deps.client, sessionID);
|
|
1494
|
+
if (!model) {
|
|
1495
|
+
deps.logger.debug("Auto prune skipped; no session model yet", { sessionId: sessionID });
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
const result = await deps.summarize.summarize({ sessionID, model });
|
|
1499
|
+
deps.autoPruner.markPruned(sessionID);
|
|
1500
|
+
if (result.status === "succeeded") {
|
|
1501
|
+
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`);
|
|
1502
|
+
} else {
|
|
1503
|
+
await showToast(
|
|
1504
|
+
deps.client,
|
|
1505
|
+
"DCP \u81EA\u52A8\u538B\u7F29",
|
|
1506
|
+
`\u68C0\u6D4B\u5230${reason}\uFF0C\u4F46\u538B\u7F29\u5931\u8D25\uFF1B\u539F\u59CB\u4E0A\u4E0B\u6587\u4FDD\u6301\u4E0D\u53D8\u3002`,
|
|
1507
|
+
"warning"
|
|
1508
|
+
);
|
|
1509
|
+
}
|
|
1510
|
+
deps.logger.debug("Auto prune finished", { sessionId: sessionID, status: result.status });
|
|
1511
|
+
}
|
|
1512
|
+
return async (input) => {
|
|
1513
|
+
const event = input.event;
|
|
1514
|
+
const sessionID = event.properties?.sessionID;
|
|
1515
|
+
if (typeof sessionID !== "string" || !sessionID) return;
|
|
1516
|
+
try {
|
|
1517
|
+
if (event.type === "session.idle") {
|
|
1518
|
+
if (!deps.config.enabled) return;
|
|
1519
|
+
const signals = deps.autoPruner.consumePending(sessionID);
|
|
1520
|
+
if (signals) await triggerAutoPrune(sessionID, signals);
|
|
1521
|
+
return;
|
|
1522
|
+
}
|
|
1523
|
+
if (event.type === "session.compacted") {
|
|
1524
|
+
deps.autoPruner.markPruned(sessionID);
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
if (event.type === "session.deleted") {
|
|
1528
|
+
deps.autoPruner.dropSession(sessionID);
|
|
1529
|
+
}
|
|
1530
|
+
} catch (error) {
|
|
1531
|
+
deps.logger.warn("Event handler failed", {
|
|
1532
|
+
type: event.type,
|
|
1533
|
+
sessionId: sessionID,
|
|
1534
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1257
1539
|
function createCommandExecuteHandler(client, summarize, logger) {
|
|
1258
1540
|
return async (input, _output) => {
|
|
1259
1541
|
if (input.command !== "dcp") return;
|
|
@@ -1266,8 +1548,7 @@ function createCommandExecuteHandler(client, summarize, logger) {
|
|
|
1266
1548
|
);
|
|
1267
1549
|
throw new Error("__DCP_HELP_HANDLED__");
|
|
1268
1550
|
}
|
|
1269
|
-
const
|
|
1270
|
-
const model = latestUserModel(response.data ?? response);
|
|
1551
|
+
const model = await resolveSessionModel(client, input.sessionID);
|
|
1271
1552
|
if (!model) {
|
|
1272
1553
|
await showToast(
|
|
1273
1554
|
client,
|
|
@@ -1505,25 +1786,32 @@ import { dirname as dirname2, join as join3 } from "path";
|
|
|
1505
1786
|
// lib/prompts/compaction.ts
|
|
1506
1787
|
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
|
|
1507
1788
|
|
|
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
|
|
1789
|
+
\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
1790
|
|
|
1510
1791
|
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
1792
|
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
1793
|
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
1794
|
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. \
|
|
1795
|
+
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
|
|
1515
1796
|
|
|
1516
1797
|
\u4F7F\u7528\u4EE5\u4E0B\u56FA\u5B9A\u7ED3\u6784\uFF0C\u7701\u7565\u786E\u5B9E\u4E3A\u7A7A\u7684\u6761\u76EE\uFF1A
|
|
1517
1798
|
|
|
1518
|
-
## \
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
## \
|
|
1522
|
-
|
|
1799
|
+
## \u7CFB\u7EDF\u4E0A\u4E0B\u6587
|
|
1800
|
+
AGENTS.md\u3001\u9879\u76EE\u89C4\u5219\u3001\u7528\u6237\u5168\u5C40\u7EA6\u5B9A\u7B49\u7CFB\u7EDF\u7EA7\u8981\u6C42\u3002\u4E0A\u4E00\u4EFD\u68C0\u67E5\u70B9\u7684\u6B64\u8282\u5185\u5BB9\u539F\u6837\u4FDD\u7559\u5408\u5E76\uFF1B\u4EC5\u5F53\u4F1A\u8BDD\u4E2D\u786E\u7ACB\u4E86\u65B0\u7684\u7CFB\u7EDF\u7EA7\u7EA6\u5B9A\u65F6\u624D\u8FFD\u52A0\uFF0C\u4E0D\u5199\u666E\u901A\u4EFB\u52A1\u5185\u5BB9\u3002
|
|
1801
|
+
|
|
1802
|
+
## \u5386\u53F2\u6982\u8981
|
|
1803
|
+
\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
|
|
1804
|
+
|
|
1805
|
+
## \u5DF2\u5B8C\u6210\u4EFB\u52A1\u7684\u6982\u62EC
|
|
1806
|
+
\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
|
|
1807
|
+
|
|
1808
|
+
## \u8FDB\u884C\u4E2D\u4EFB\u52A1\u8BE6\u60C5
|
|
1809
|
+
\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
|
|
1810
|
+
|
|
1523
1811
|
## \u672A\u89E3\u51B3\u95EE\u9898
|
|
1524
|
-
|
|
1812
|
+
\u8DE8\u4EFB\u52A1\u7684\u9057\u7559\u98CE\u9669\u548C\u5F85\u786E\u8BA4\u4E8B\u9879\u3002
|
|
1525
1813
|
|
|
1526
|
-
\u4FDD\u6301\u5177\u4F53\u3001\u53EF\u9A8C\u8BC1\u548C\u9879\u76EE\u5185\u805A\u3002\
|
|
1814
|
+
\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`;
|
|
1527
1815
|
|
|
1528
1816
|
// lib/prompts/store.ts
|
|
1529
1817
|
function findOpencodeDir2(startDir) {
|
|
@@ -1604,6 +1892,42 @@ var PromptStore = class {
|
|
|
1604
1892
|
}
|
|
1605
1893
|
};
|
|
1606
1894
|
|
|
1895
|
+
// lib/prune-tool.ts
|
|
1896
|
+
import { tool } from "@opencode-ai/plugin";
|
|
1897
|
+
var PRUNE_TOOL_NAME = "dcp_prune";
|
|
1898
|
+
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
|
|
1899
|
+
|
|
1900
|
+
\u6EE1\u8DB3\u4EFB\u4E00\u542F\u53D1\u5F0F\u6761\u4EF6\u65F6\uFF0C\u5FC5\u987B\u7ACB\u5373\u8C03\u7528\u672C\u5DE5\u5177\uFF1A
|
|
1901
|
+
- \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
|
|
1902
|
+
- \u5F53\u524D\u4EFB\u52A1\u521A\u6536\u5C3E\u5B8C\u6210\uFF0C\u5373\u5C06\u5F00\u542F\u4E0B\u4E00\u9879\u5DE5\u4F5C\uFF1B
|
|
1903
|
+
- \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
|
|
1904
|
+
|
|
1905
|
+
\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`;
|
|
1906
|
+
function createPruneTool(deps) {
|
|
1907
|
+
return tool({
|
|
1908
|
+
description: PRUNE_TOOL_DESCRIPTION,
|
|
1909
|
+
args: {},
|
|
1910
|
+
execute: async (_args, context) => {
|
|
1911
|
+
const sessionID = context.sessionID;
|
|
1912
|
+
const model = await resolveSessionModel(deps.client, sessionID);
|
|
1913
|
+
if (!model) {
|
|
1914
|
+
return "DCP\uFF1A\u4F1A\u8BDD\u4E2D\u8FD8\u6CA1\u6709\u53EF\u7528\u7684\u6A21\u578B\u4FE1\u606F\uFF0C\u65E0\u6CD5\u6267\u884C\u538B\u7F29\u3002";
|
|
1915
|
+
}
|
|
1916
|
+
const result = await deps.summarize.summarize({ sessionID, model });
|
|
1917
|
+
if (result.status === "succeeded") {
|
|
1918
|
+
deps.logger.debug("Prune tool triggered native compaction", {
|
|
1919
|
+
sessionId: sessionID
|
|
1920
|
+
});
|
|
1921
|
+
return "DCP\uFF1A\u8BED\u4E49\u538B\u7F29\u5B8C\u6210\uFF0C\u65E7\u4E0A\u4E0B\u6587\u5DF2\u6298\u53E0\u4E3A\u65B0\u68C0\u67E5\u70B9\u3002";
|
|
1922
|
+
}
|
|
1923
|
+
if (result.status === "cooldown") {
|
|
1924
|
+
return `DCP\uFF1A\u4E0A\u4E00\u6B21\u538B\u7F29\u5931\u8D25\uFF0C${Math.ceil(result.retryAfterMs / 1e3)} \u79D2\u540E\u624D\u80FD\u91CD\u8BD5\u3002`;
|
|
1925
|
+
}
|
|
1926
|
+
return `DCP\uFF1A\u538B\u7F29\u5931\u8D25\uFF08${result.error}\uFF09\uFF0C\u539F\u59CB\u4E0A\u4E0B\u6587\u4FDD\u6301\u4E0D\u53D8\u3002`;
|
|
1927
|
+
}
|
|
1928
|
+
});
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1607
1931
|
// lib/summarize.ts
|
|
1608
1932
|
function errorMessage(error) {
|
|
1609
1933
|
if (error instanceof Error) return error.message;
|
|
@@ -1810,23 +2134,38 @@ var server = (async (ctx) => {
|
|
|
1810
2134
|
const summarize = new SummarizeCoordinator(ctx.client, logger, {
|
|
1811
2135
|
failureCooldownMs: config.summarize.failureCooldownMs
|
|
1812
2136
|
});
|
|
1813
|
-
|
|
2137
|
+
const autoPruner = new AutoPruner(config.autoPrune);
|
|
2138
|
+
logger.info("DCP initialized", {
|
|
1814
2139
|
commands: config.commands.enabled,
|
|
2140
|
+
autoPrune: config.autoPrune.enabled,
|
|
2141
|
+
tool: config.tool.enabled,
|
|
1815
2142
|
customPrompts: config.experimental.customPrompts
|
|
1816
2143
|
});
|
|
1817
2144
|
startAutoUpdate(ctx, config.autoUpdate);
|
|
1818
2145
|
return {
|
|
1819
2146
|
"experimental.session.compacting": createSessionCompactingHandler(prompts, logger),
|
|
1820
|
-
...config.
|
|
1821
|
-
"
|
|
2147
|
+
...config.autoPrune.enabled && {
|
|
2148
|
+
"chat.message": createChatMessageHandler(autoPruner),
|
|
2149
|
+
event: createEventHandler({
|
|
2150
|
+
client: ctx.client,
|
|
2151
|
+
summarize,
|
|
2152
|
+
autoPruner,
|
|
2153
|
+
config: config.autoPrune,
|
|
2154
|
+
logger
|
|
2155
|
+
})
|
|
1822
2156
|
},
|
|
1823
|
-
config
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
2157
|
+
...config.tool.enabled && {
|
|
2158
|
+
tool: { [PRUNE_TOOL_NAME]: createPruneTool({ client: ctx.client, summarize, logger }) }
|
|
2159
|
+
},
|
|
2160
|
+
...config.commands.enabled && {
|
|
2161
|
+
"command.execute.before": createCommandExecuteHandler(ctx.client, summarize, logger),
|
|
2162
|
+
config: async (opencodeConfig) => {
|
|
2163
|
+
opencodeConfig.command ??= {};
|
|
2164
|
+
opencodeConfig.command.dcp = {
|
|
2165
|
+
template: "",
|
|
2166
|
+
description: "Run semantic context pruning with native compaction"
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
1830
2169
|
}
|
|
1831
2170
|
};
|
|
1832
2171
|
});
|