@lexwdex-org/opencode-dcp 4.0.0 → 5.0.1
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 +5 -17
- package/README.md +5 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +92 -270
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts +0 -6
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/dtc/digest.d.ts.map +1 -1
- package/dist/lib/dtc/engine.d.ts +10 -0
- package/dist/lib/dtc/engine.d.ts.map +1 -1
- package/dist/lib/dtc/state.d.ts +4 -1
- package/dist/lib/dtc/state.d.ts.map +1 -1
- package/dist/lib/dtc/types.d.ts +1 -0
- package/dist/lib/dtc/types.d.ts.map +1 -1
- package/dist/lib/hooks.d.ts +11 -19
- package/dist/lib/hooks.d.ts.map +1 -1
- package/package.json +2 -3
- package/dist/lib/prompts/compaction.d.ts +0 -4
- package/dist/lib/prompts/compaction.d.ts.map +0 -1
- package/dist/lib/prompts/store.d.ts +0 -17
- package/dist/lib/prompts/store.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1028,6 +1028,7 @@ function estimateSlice(messages, start, end) {
|
|
|
1028
1028
|
}
|
|
1029
1029
|
if (typeof state.output === "string") tokens += estimateTokens(state.output);
|
|
1030
1030
|
if (state.input) tokens += estimateTokens(JSON.stringify(state.input));
|
|
1031
|
+
if (typeof state.error === "string") tokens += estimateTokens(state.error);
|
|
1031
1032
|
}
|
|
1032
1033
|
}
|
|
1033
1034
|
return tokens;
|
|
@@ -1044,6 +1045,10 @@ var DTC_DEFAULTS = {
|
|
|
1044
1045
|
var C_ZONE_MAX_TURNS = 8;
|
|
1045
1046
|
var M_ZONE_MAX_TURNS = 12;
|
|
1046
1047
|
var M_TEXT_KEEP_CHARS = 200;
|
|
1048
|
+
var M_ERROR_KEEP_CHARS = 200;
|
|
1049
|
+
var D_ERROR_KEEP_CHARS = 100;
|
|
1050
|
+
var INPUT_TARGET_KEYS = ["filePath", "path", "file", "filename", "pattern", "directory"];
|
|
1051
|
+
var COMMAND_KEEP_CHARS = 80;
|
|
1047
1052
|
var FOLDED_TEXT = " ";
|
|
1048
1053
|
var NO_STATS = {
|
|
1049
1054
|
messages: 0,
|
|
@@ -1054,6 +1059,8 @@ var NO_STATS = {
|
|
|
1054
1059
|
contextTokens: 0,
|
|
1055
1060
|
foldedTools: 0,
|
|
1056
1061
|
foldedTexts: 0,
|
|
1062
|
+
reducedInputs: 0,
|
|
1063
|
+
foldedErrors: 0,
|
|
1057
1064
|
digestedTurns: 0,
|
|
1058
1065
|
skipped: void 0
|
|
1059
1066
|
};
|
|
@@ -1105,11 +1112,18 @@ function transformMessages(messages, deps) {
|
|
|
1105
1112
|
const zones = computeZones(messages, headTurns, sessionID, state, config);
|
|
1106
1113
|
const now = (deps.now ?? Date.now)();
|
|
1107
1114
|
let level = Math.max(1, minLevel);
|
|
1108
|
-
|
|
1115
|
+
let appliedBand = 0;
|
|
1116
|
+
const applyUpTo = (lvl) => {
|
|
1117
|
+
while (appliedBand < lvl) {
|
|
1118
|
+
appliedBand++;
|
|
1119
|
+
applyBand(messages, headTurns, zones, appliedBand, deps, stats, now);
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
applyUpTo(level);
|
|
1109
1123
|
let estimatedAfter = estimateSlice(messages, 0, messages.length);
|
|
1110
1124
|
while (estimatedAfter > target && level < 3) {
|
|
1111
1125
|
level = level + 1;
|
|
1112
|
-
|
|
1126
|
+
applyUpTo(level);
|
|
1113
1127
|
estimatedAfter = estimateSlice(messages, 0, messages.length);
|
|
1114
1128
|
}
|
|
1115
1129
|
stats.level = level;
|
|
@@ -1123,6 +1137,8 @@ function transformMessages(messages, deps) {
|
|
|
1123
1137
|
estimatedAfter,
|
|
1124
1138
|
contextTokens,
|
|
1125
1139
|
foldedTools: stats.foldedTools,
|
|
1140
|
+
reducedInputs: stats.reducedInputs,
|
|
1141
|
+
foldedErrors: stats.foldedErrors,
|
|
1126
1142
|
digestedTurns: stats.digestedTurns
|
|
1127
1143
|
});
|
|
1128
1144
|
return stats;
|
|
@@ -1146,19 +1162,16 @@ function computeZones(messages, headTurns, sessionID, state, config) {
|
|
|
1146
1162
|
const mStart = Math.min(cStart, Math.max(secondLast, cStart - M_ZONE_MAX_TURNS));
|
|
1147
1163
|
return { mStart, cStart };
|
|
1148
1164
|
}
|
|
1149
|
-
function
|
|
1150
|
-
|
|
1151
|
-
if (level >= 1 && prev < 1) {
|
|
1165
|
+
function applyBand(messages, headTurns, zones, band, deps, stats, now) {
|
|
1166
|
+
if (band === 1) {
|
|
1152
1167
|
for (let t = 0; t < zones.mStart; t++) {
|
|
1153
1168
|
foldDistant(messages, headTurns[t], t + 1, deps, stats, now);
|
|
1154
1169
|
}
|
|
1155
|
-
}
|
|
1156
|
-
if (level >= 2 && prev < 2) {
|
|
1170
|
+
} else if (band === 2) {
|
|
1157
1171
|
for (let t = zones.mStart; t < zones.cStart; t++) {
|
|
1158
1172
|
foldMiddle(messages, headTurns[t], stats, now);
|
|
1159
1173
|
}
|
|
1160
|
-
}
|
|
1161
|
-
if (level >= 3 && prev < 3) {
|
|
1174
|
+
} else if (band === 3) {
|
|
1162
1175
|
for (let t = zones.cStart; t < headTurns.length; t++) {
|
|
1163
1176
|
foldCurrent(messages, headTurns[t], deps.config.toolOutputKeepChars, stats);
|
|
1164
1177
|
}
|
|
@@ -1177,7 +1190,11 @@ function foldDistant(messages, turn, ordinal, deps, stats, now) {
|
|
|
1177
1190
|
const message = messages[i];
|
|
1178
1191
|
for (const part of message?.parts ?? []) {
|
|
1179
1192
|
if (!part || typeof part !== "object") continue;
|
|
1180
|
-
if (
|
|
1193
|
+
if (part.type === "tool" && part.state?.status === "error") {
|
|
1194
|
+
foldErrorPart(part, D_ERROR_KEEP_CHARS, true, stats);
|
|
1195
|
+
continue;
|
|
1196
|
+
}
|
|
1197
|
+
if (foldToolPart(part, now, "distant", stats)) {
|
|
1181
1198
|
stats.foldedTools++;
|
|
1182
1199
|
continue;
|
|
1183
1200
|
}
|
|
@@ -1201,7 +1218,11 @@ function foldMiddle(messages, turn, stats, now) {
|
|
|
1201
1218
|
for (let i = turn.start; i < turn.end; i++) {
|
|
1202
1219
|
for (const part of messages[i]?.parts ?? []) {
|
|
1203
1220
|
if (!part || typeof part !== "object") continue;
|
|
1204
|
-
if (
|
|
1221
|
+
if (part.type === "tool" && part.state?.status === "error") {
|
|
1222
|
+
foldErrorPart(part, M_ERROR_KEEP_CHARS, false, stats);
|
|
1223
|
+
continue;
|
|
1224
|
+
}
|
|
1225
|
+
if (foldToolPart(part, now, "middle", stats)) {
|
|
1205
1226
|
stats.foldedTools++;
|
|
1206
1227
|
continue;
|
|
1207
1228
|
}
|
|
@@ -1233,7 +1254,20 @@ function foldCurrent(messages, turn, keepChars, stats) {
|
|
|
1233
1254
|
}
|
|
1234
1255
|
}
|
|
1235
1256
|
}
|
|
1236
|
-
function
|
|
1257
|
+
function reduceInput(input) {
|
|
1258
|
+
if (!input || typeof input !== "object") return {};
|
|
1259
|
+
const reduced = {};
|
|
1260
|
+
for (const key of INPUT_TARGET_KEYS) {
|
|
1261
|
+
const value = input[key];
|
|
1262
|
+
if (typeof value === "string" && value) reduced[key] = value;
|
|
1263
|
+
}
|
|
1264
|
+
const command = input.command;
|
|
1265
|
+
if (typeof command === "string" && command) {
|
|
1266
|
+
reduced.command = firstLine(command, COMMAND_KEEP_CHARS);
|
|
1267
|
+
}
|
|
1268
|
+
return reduced;
|
|
1269
|
+
}
|
|
1270
|
+
function foldToolPart(part, now, zone, stats) {
|
|
1237
1271
|
if (part.type !== "tool") return false;
|
|
1238
1272
|
const state = part.state;
|
|
1239
1273
|
if (!state || state.status !== "completed") return false;
|
|
@@ -1242,11 +1276,24 @@ function foldToolPart(part, now, clearInput) {
|
|
|
1242
1276
|
} else {
|
|
1243
1277
|
state.time = { compacted: now };
|
|
1244
1278
|
}
|
|
1245
|
-
if (
|
|
1246
|
-
state.input = {};
|
|
1279
|
+
if (state.input && typeof state.input === "object") {
|
|
1280
|
+
state.input = zone === "distant" ? {} : reduceInput(state.input);
|
|
1281
|
+
stats.reducedInputs++;
|
|
1247
1282
|
}
|
|
1248
1283
|
return true;
|
|
1249
1284
|
}
|
|
1285
|
+
function foldErrorPart(part, keepChars, clearInput, stats) {
|
|
1286
|
+
const state = part.state;
|
|
1287
|
+
if (!state) return;
|
|
1288
|
+
if (typeof state.error === "string" && state.error.length > keepChars) {
|
|
1289
|
+
state.error = firstLine(state.error, keepChars) || FOLDED_TEXT;
|
|
1290
|
+
stats.foldedErrors++;
|
|
1291
|
+
}
|
|
1292
|
+
if (state.input && typeof state.input === "object") {
|
|
1293
|
+
state.input = clearInput ? {} : reduceInput(state.input);
|
|
1294
|
+
stats.reducedInputs++;
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1250
1297
|
function findSessionID(messages) {
|
|
1251
1298
|
for (const message of messages) {
|
|
1252
1299
|
const id = message?.info?.sessionID;
|
|
@@ -1265,11 +1312,8 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
1265
1312
|
"enabled",
|
|
1266
1313
|
"autoUpdate",
|
|
1267
1314
|
"debug",
|
|
1268
|
-
"language",
|
|
1269
1315
|
"commands",
|
|
1270
1316
|
"commands.enabled",
|
|
1271
|
-
"experimental",
|
|
1272
|
-
"experimental.customPrompts",
|
|
1273
1317
|
"dtc",
|
|
1274
1318
|
"dtc.enabled",
|
|
1275
1319
|
"dtc.tailTurns",
|
|
@@ -1322,6 +1366,9 @@ var DEPRECATED_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
1322
1366
|
"protectedFilePatterns",
|
|
1323
1367
|
"commands.protectedTools",
|
|
1324
1368
|
"experimental.allowSubAgents",
|
|
1369
|
+
"language",
|
|
1370
|
+
"experimental",
|
|
1371
|
+
"experimental.customPrompts",
|
|
1325
1372
|
"summarize",
|
|
1326
1373
|
"summarize.failureCooldownMs",
|
|
1327
1374
|
"autoPrune",
|
|
@@ -1367,13 +1414,6 @@ function validateConfigTypes(config) {
|
|
|
1367
1414
|
if (config.debug !== void 0 && typeof config.debug !== "boolean") {
|
|
1368
1415
|
errors.push({ key: "debug", expected: "boolean", actual: typeof config.debug });
|
|
1369
1416
|
}
|
|
1370
|
-
if (config.language !== void 0 && config.language !== "zh" && config.language !== "en") {
|
|
1371
|
-
errors.push({
|
|
1372
|
-
key: "language",
|
|
1373
|
-
expected: '"zh" or "en"',
|
|
1374
|
-
actual: JSON.stringify(config.language)
|
|
1375
|
-
});
|
|
1376
|
-
}
|
|
1377
1417
|
const commands = config.commands;
|
|
1378
1418
|
if (commands !== void 0) {
|
|
1379
1419
|
if (typeof commands !== "object" || commands === null || Array.isArray(commands)) {
|
|
@@ -1386,18 +1426,6 @@ function validateConfigTypes(config) {
|
|
|
1386
1426
|
});
|
|
1387
1427
|
}
|
|
1388
1428
|
}
|
|
1389
|
-
const experimental = config.experimental;
|
|
1390
|
-
if (experimental !== void 0) {
|
|
1391
|
-
if (typeof experimental !== "object" || experimental === null || Array.isArray(experimental)) {
|
|
1392
|
-
errors.push({ key: "experimental", expected: "object", actual: typeof experimental });
|
|
1393
|
-
} else if (experimental.customPrompts !== void 0 && typeof experimental.customPrompts !== "boolean") {
|
|
1394
|
-
errors.push({
|
|
1395
|
-
key: "experimental.customPrompts",
|
|
1396
|
-
expected: "boolean",
|
|
1397
|
-
actual: typeof experimental.customPrompts
|
|
1398
|
-
});
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
1429
|
const dtc = config.dtc;
|
|
1402
1430
|
if (dtc !== void 0) {
|
|
1403
1431
|
if (typeof dtc !== "object" || dtc === null || Array.isArray(dtc)) {
|
|
@@ -1491,13 +1519,9 @@ var defaultConfig = {
|
|
|
1491
1519
|
enabled: true,
|
|
1492
1520
|
autoUpdate: true,
|
|
1493
1521
|
debug: false,
|
|
1494
|
-
language: "zh",
|
|
1495
1522
|
commands: {
|
|
1496
1523
|
enabled: true
|
|
1497
1524
|
},
|
|
1498
|
-
experimental: {
|
|
1499
|
-
customPrompts: false
|
|
1500
|
-
},
|
|
1501
1525
|
dtc: { ...DEFAULT_DTC },
|
|
1502
1526
|
tool: {
|
|
1503
1527
|
enabled: true
|
|
@@ -1576,14 +1600,6 @@ function mergeCommands(base, override) {
|
|
|
1576
1600
|
enabled: typeof override.enabled === "boolean" ? override.enabled : base.enabled
|
|
1577
1601
|
};
|
|
1578
1602
|
}
|
|
1579
|
-
function mergeExperimental(base, override) {
|
|
1580
|
-
if (!override) {
|
|
1581
|
-
return base;
|
|
1582
|
-
}
|
|
1583
|
-
return {
|
|
1584
|
-
customPrompts: typeof override.customPrompts === "boolean" ? override.customPrompts : base.customPrompts
|
|
1585
|
-
};
|
|
1586
|
-
}
|
|
1587
1603
|
function mergeDtc(base, override, legacyDrift) {
|
|
1588
1604
|
const driftFallback = legacyDrift ?? base.driftThreshold;
|
|
1589
1605
|
if (!override || typeof override !== "object" || Array.isArray(override)) {
|
|
@@ -1611,7 +1627,6 @@ function deepCloneConfig(config) {
|
|
|
1611
1627
|
return {
|
|
1612
1628
|
...config,
|
|
1613
1629
|
commands: { ...config.commands },
|
|
1614
|
-
experimental: { ...config.experimental },
|
|
1615
1630
|
dtc: { ...config.dtc },
|
|
1616
1631
|
tool: { ...config.tool }
|
|
1617
1632
|
};
|
|
@@ -1621,9 +1636,7 @@ function mergeLayer(config, data) {
|
|
|
1621
1636
|
enabled: typeof data.enabled === "boolean" ? data.enabled : config.enabled,
|
|
1622
1637
|
autoUpdate: typeof data.autoUpdate === "boolean" ? data.autoUpdate : config.autoUpdate,
|
|
1623
1638
|
debug: typeof data.debug === "boolean" ? data.debug : config.debug,
|
|
1624
|
-
language: data.language === "zh" || data.language === "en" ? data.language : config.language,
|
|
1625
1639
|
commands: mergeCommands(config.commands, data.commands),
|
|
1626
|
-
experimental: mergeExperimental(config.experimental, data.experimental),
|
|
1627
1640
|
dtc: mergeDtc(config.dtc, data.dtc, legacyDriftThreshold(data)),
|
|
1628
1641
|
tool: mergeTool(config.tool, data.tool)
|
|
1629
1642
|
};
|
|
@@ -1704,7 +1717,10 @@ var DtcState = class {
|
|
|
1704
1717
|
contextTokens(sessionID) {
|
|
1705
1718
|
return this.sessions.get(sessionID)?.contextTokens;
|
|
1706
1719
|
}
|
|
1707
|
-
/** Called by the compacting hook; consumed by the very next transform.
|
|
1720
|
+
/** Called by the compacting hook; consumed by the very next transform.
|
|
1721
|
+
* Accepted leak: if the host aborts between the hook and the summarizer
|
|
1722
|
+
* transform, the flag skips folding once for the next unrelated request —
|
|
1723
|
+
* fail-open (extra tokens, never lost information) and self-healing. */
|
|
1708
1724
|
armCompactionSkip(sessionID) {
|
|
1709
1725
|
const state = this.session(sessionID);
|
|
1710
1726
|
state.skipNextTransform = true;
|
|
@@ -1759,56 +1775,18 @@ function eventSessionID(properties) {
|
|
|
1759
1775
|
// lib/hooks.ts
|
|
1760
1776
|
var DEFAULT_TAIL_TURNS = 4;
|
|
1761
1777
|
var DEFAULT_PRESERVE_RECENT_TOKENS = 32e3;
|
|
1762
|
-
function extractPreviousCheckpoint(messages) {
|
|
1763
|
-
if (!Array.isArray(messages)) return void 0;
|
|
1764
|
-
for (let index = messages.length - 1; index >= 0; index--) {
|
|
1765
|
-
const message = messages[index];
|
|
1766
|
-
const info = message?.info;
|
|
1767
|
-
if (info?.role !== "assistant" || info.summary !== true) continue;
|
|
1768
|
-
const parts = Array.isArray(message?.parts) ? message.parts : [];
|
|
1769
|
-
const text = parts.filter((part) => part?.type === "text" && typeof part.text === "string").map((part) => part.text.trim()).filter(Boolean).join("\n\n").trim();
|
|
1770
|
-
if (text) return text;
|
|
1771
|
-
}
|
|
1772
|
-
return void 0;
|
|
1773
|
-
}
|
|
1774
|
-
async function fetchPreviousCheckpoint(client, sessionID) {
|
|
1775
|
-
if (!sessionID) return void 0;
|
|
1776
|
-
try {
|
|
1777
|
-
const response = await client.session.messages({ path: { id: sessionID } });
|
|
1778
|
-
return extractPreviousCheckpoint(response.data ?? response);
|
|
1779
|
-
} catch {
|
|
1780
|
-
return void 0;
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
1778
|
function createSessionCompactingHandler(deps) {
|
|
1784
|
-
return async (input,
|
|
1779
|
+
return async (input, _output) => {
|
|
1785
1780
|
try {
|
|
1786
1781
|
deps.state.armCompactionSkip(input.sessionID);
|
|
1787
|
-
deps.
|
|
1788
|
-
|
|
1789
|
-
const previous = await fetchPreviousCheckpoint(deps.client, input.sessionID);
|
|
1790
|
-
const withCheckpoint = previous ? `${prompt}
|
|
1791
|
-
|
|
1792
|
-
<previous-checkpoint>
|
|
1793
|
-
${previous}
|
|
1794
|
-
</previous-checkpoint>` : prompt;
|
|
1795
|
-
if (!output.prompt) {
|
|
1796
|
-
output.prompt = withCheckpoint;
|
|
1797
|
-
} else if (!output.context.includes(prompt)) {
|
|
1798
|
-
output.context.push(withCheckpoint);
|
|
1799
|
-
}
|
|
1800
|
-
deps.logger.debug("Applied semantic pruning prompt", {
|
|
1801
|
-
sessionId: input.sessionID,
|
|
1802
|
-
carriedCheckpoint: Boolean(previous)
|
|
1782
|
+
deps.logger.debug("Armed DTC skip for the native compaction input", {
|
|
1783
|
+
sessionId: input.sessionID
|
|
1803
1784
|
});
|
|
1804
1785
|
} catch (error) {
|
|
1805
|
-
deps.logger.warn(
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1810
|
-
}
|
|
1811
|
-
);
|
|
1786
|
+
deps.logger.warn("Failed to arm the compaction skip; native compaction continues", {
|
|
1787
|
+
sessionId: input.sessionID,
|
|
1788
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1789
|
+
});
|
|
1812
1790
|
}
|
|
1813
1791
|
};
|
|
1814
1792
|
}
|
|
@@ -2124,153 +2102,6 @@ var Logger = class {
|
|
|
2124
2102
|
}
|
|
2125
2103
|
};
|
|
2126
2104
|
|
|
2127
|
-
// lib/prompts/store.ts
|
|
2128
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2129
|
-
import { homedir as homedir3 } from "os";
|
|
2130
|
-
import { dirname as dirname2, join as join3 } from "path";
|
|
2131
|
-
|
|
2132
|
-
// lib/prompts/compaction.ts
|
|
2133
|
-
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\u68C0\u67E5\u70B9\u4E4B\u540E\u76F4\u63A5\u4FDD\u7559\u6700\u8FD1\u82E5\u5E72\u8F6E\u5B8C\u6574\u5BF9\u8BDD\uFF08\u4E0D\u7ECF\u538B\u7F29\uFF0C\u8303\u56F4\u7531\u5BBF\u4E3B\u914D\u7F6E\u51B3\u5B9A\uFF09\uFF0C\u8FD9\u90E8\u5206\u5C3E\u90E8\u4E0D\u5C5E\u4E8E\u538B\u7F29\u5BF9\u8C61\uFF0C\u4E0D\u8981\u590D\u8FF0\u3001\u603B\u7ED3\u6216\u6539\u5199\u5B83\u4EEC\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
|
|
2134
|
-
|
|
2135
|
-
\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\u5206\u5C42\u89C4\u5219\u538B\u7F29\uFF1A
|
|
2136
|
-
|
|
2137
|
-
1. \u6EDA\u52A8\u5408\u5E76\uFF1A\u5982\u679C\u8F93\u5165\u672B\u5C3E\u542B\u6709 <previous-checkpoint> \u6807\u7B7E\u5305\u88F9\u7684\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\u9010\u5B57\u91CD\u590D\u65E7\u68C0\u67E5\u70B9\uFF0C\u5931\u6548\u5185\u5BB9\u76F4\u63A5\u4E22\u5F03\u3002\u6CA1\u6709\u8BE5\u6807\u7B7E\u65F6\u5FFD\u7565\u672C\u6761\u3002
|
|
2138
|
-
2. \u8FDC\u8DDD\u79BB\u5185\u5BB9\uFF08\u65E9\u671F\u4E0E\u4E2D\u90E8\u5386\u53F2\uFF09\u91CD\u5EA6\u538B\u7F29\uFF1A\u6BCF\u4E2A\u4E3B\u9898\u53EA\u7559\u4E00\u53E5\u8BDD\u7ED3\u8BBA\uFF0C\u4E0D\u7559\u8FC7\u7A0B\u3001\u4E0D\u590D\u8FF0\u5BF9\u8BDD\u5F80\u6765\u3002
|
|
2139
|
-
3. \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
|
|
2140
|
-
4. \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
|
|
2141
|
-
5. \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
|
|
2142
|
-
6. \u8FD1\u8DDD\u79BB\u5185\u5BB9\uFF08\u81EA\u4E0A\u4E00\u4EFD\u68C0\u67E5\u70B9\u4EE5\u6765\u7684\u65B0\u5185\u5BB9\uFF0C\u4E14\u4E0D\u5C5E\u4E8E\u5BBF\u4E3B\u4FDD\u7559\u7684\u5C3E\u90E8\uFF09\u8F7B\u5EA6\u538B\u7F29\uFF1A\u5F53\u524D\u4EFB\u52A1\u3001\u76EE\u6807\u3001\u72B6\u6001\u4E0E\u8FDB\u884C\u4E2D\u7684\u60C5\u51B5\u505A\u6C47\u603B\u4F46\u4E0D\u4E22\u7EC6\u8282\u2014\u2014\u4FDD\u7559\u7EE7\u7EED\u5DE5\u4F5C\u6240\u9700\u7684\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\uFF0C\u53EA\u6298\u53E0\u91CD\u590D\u4E0E\u5DF2\u5931\u6548\u7684\u5185\u5BB9\u3002
|
|
2143
|
-
|
|
2144
|
-
\u4F7F\u7528\u4EE5\u4E0B\u56FA\u5B9A\u7ED3\u6784\uFF0C\u7701\u7565\u786E\u5B9E\u4E3A\u7A7A\u7684\u6761\u76EE\uFF1A
|
|
2145
|
-
|
|
2146
|
-
## \u5386\u53F2\u6982\u8981
|
|
2147
|
-
\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
|
|
2148
|
-
|
|
2149
|
-
## \u5DF2\u5B8C\u6210\u4EFB\u52A1\u7684\u6982\u62EC
|
|
2150
|
-
\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
|
|
2151
|
-
|
|
2152
|
-
## \u8FDB\u884C\u4E2D\u4EFB\u52A1\u8BE6\u60C5
|
|
2153
|
-
\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
|
|
2154
|
-
|
|
2155
|
-
## \u672A\u89E3\u51B3\u95EE\u9898
|
|
2156
|
-
\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
|
|
2157
|
-
|
|
2158
|
-
\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`;
|
|
2159
|
-
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 the most recent conversation turns directly after the checkpoint, uncompacted (their range is a host setting). That tail is outside the compaction scope: do not restate, summarize, or rewrite 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.
|
|
2160
|
-
|
|
2161
|
-
This is not a chat-log summary but a semantic pruning result one can resume working from directly. Compress by these tiered rules:
|
|
2162
|
-
|
|
2163
|
-
1. Rolling merge: if the input ends with a <previous-checkpoint> block containing the previous checkpoint, merge its still-valid information into the new checkpoint; do not nest, quote, or repeat it verbatim, and drop stale content outright. Ignore this rule when the tag is absent.
|
|
2164
|
-
2. Distant content (early and middle history) is compressed heavily: one concluding sentence per topic, no process detail, no dialogue restatement.
|
|
2165
|
-
3. Remove irrelevant chitchat, conversations about other projects or repositories, repeated explanations, and approaches that were overturned and no longer carry diagnostic value.
|
|
2166
|
-
4. 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.
|
|
2167
|
-
5. 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.
|
|
2168
|
-
6. Recent content (everything generated since the previous checkpoint, excluding the host-retained tail) is compressed lightly: summarize the current task, goals, status, and in-progress work without losing detail\u2014keep the file paths, interfaces, commands, test results, error facts, and still-valid decisions with rationale needed to continue, folding only duplicates and invalidated content.
|
|
2169
|
-
|
|
2170
|
-
Use the following fixed structure, omitting sections that are truly empty:
|
|
2171
|
-
|
|
2172
|
-
## History Overview
|
|
2173
|
-
Topics and background from early and middle history, one concluding sentence each; long-completed tasks belong here, without execution process.
|
|
2174
|
-
|
|
2175
|
-
## Completed Task Summaries
|
|
2176
|
-
Recently completed tasks, one sentence per task covering its outcome and key outputs.
|
|
2177
|
-
|
|
2178
|
-
## In-Progress Task Details
|
|
2179
|
-
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.
|
|
2180
|
-
|
|
2181
|
-
## Unresolved Issues
|
|
2182
|
-
Cross-task lingering risks and items awaiting confirmation. Only list items not already covered under In-Progress Task Details, to avoid duplication.
|
|
2183
|
-
|
|
2184
|
-
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.`;
|
|
2185
|
-
function getCompactionPrompt(language) {
|
|
2186
|
-
return language === "en" ? COMPACTION_EN : COMPACTION;
|
|
2187
|
-
}
|
|
2188
|
-
|
|
2189
|
-
// lib/prompts/store.ts
|
|
2190
|
-
function findOpencodeDir2(startDir) {
|
|
2191
|
-
let current = startDir;
|
|
2192
|
-
while (current !== "/") {
|
|
2193
|
-
const candidate = join3(current, ".opencode");
|
|
2194
|
-
if (existsSync3(candidate) && statSync2(candidate).isDirectory()) return candidate;
|
|
2195
|
-
const parent = dirname2(current);
|
|
2196
|
-
if (parent === current) break;
|
|
2197
|
-
current = parent;
|
|
2198
|
-
}
|
|
2199
|
-
return null;
|
|
2200
|
-
}
|
|
2201
|
-
function resolvePaths(workingDirectory) {
|
|
2202
|
-
const configHome = process.env.XDG_CONFIG_HOME || join3(homedir3(), ".config");
|
|
2203
|
-
const globalRoot = join3(configHome, "opencode", "dcp-prompts");
|
|
2204
|
-
const opencodeDir = findOpencodeDir2(workingDirectory);
|
|
2205
|
-
return {
|
|
2206
|
-
defaultsDir: join3(globalRoot, "defaults"),
|
|
2207
|
-
overrides: [
|
|
2208
|
-
opencodeDir ? join3(opencodeDir, "dcp-prompts", "overrides", "compaction.md") : "",
|
|
2209
|
-
process.env.OPENCODE_CONFIG_DIR ? join3(process.env.OPENCODE_CONFIG_DIR, "dcp-prompts", "overrides", "compaction.md") : "",
|
|
2210
|
-
join3(globalRoot, "overrides", "compaction.md")
|
|
2211
|
-
].filter(Boolean)
|
|
2212
|
-
};
|
|
2213
|
-
}
|
|
2214
|
-
function normalize(content) {
|
|
2215
|
-
return content.replace(/^\uFEFF/, "").replace(/\r\n?/g, "\n").replace(/<!--[\s\S]*?-->/g, "").trim();
|
|
2216
|
-
}
|
|
2217
|
-
var PromptStore = class {
|
|
2218
|
-
constructor(logger, workingDirectory, customPromptsEnabled = false, language) {
|
|
2219
|
-
this.logger = logger;
|
|
2220
|
-
this.customPromptsEnabled = customPromptsEnabled;
|
|
2221
|
-
this.paths = resolvePaths(workingDirectory);
|
|
2222
|
-
this.defaultPrompt = getCompactionPrompt(language);
|
|
2223
|
-
this.runtime = { compaction: this.defaultPrompt };
|
|
2224
|
-
if (customPromptsEnabled) this.ensureDefaults();
|
|
2225
|
-
this.reload();
|
|
2226
|
-
}
|
|
2227
|
-
paths;
|
|
2228
|
-
defaultPrompt;
|
|
2229
|
-
runtime;
|
|
2230
|
-
lastReloadAt = 0;
|
|
2231
|
-
getRuntimePrompts() {
|
|
2232
|
-
return { ...this.runtime };
|
|
2233
|
-
}
|
|
2234
|
-
reload() {
|
|
2235
|
-
const now = Date.now();
|
|
2236
|
-
if (now - this.lastReloadAt < 1e3) return;
|
|
2237
|
-
this.lastReloadAt = now;
|
|
2238
|
-
this.runtime = { compaction: this.defaultPrompt };
|
|
2239
|
-
if (!this.customPromptsEnabled) return;
|
|
2240
|
-
for (const path of this.paths.overrides) {
|
|
2241
|
-
if (!existsSync3(path)) continue;
|
|
2242
|
-
try {
|
|
2243
|
-
const prompt = normalize(readFileSync2(path, "utf-8"));
|
|
2244
|
-
if (prompt) this.runtime = { compaction: prompt };
|
|
2245
|
-
return;
|
|
2246
|
-
} catch (error) {
|
|
2247
|
-
this.logger.warn("Failed to load compaction prompt override", {
|
|
2248
|
-
path,
|
|
2249
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2250
|
-
});
|
|
2251
|
-
}
|
|
2252
|
-
}
|
|
2253
|
-
}
|
|
2254
|
-
ensureDefaults() {
|
|
2255
|
-
try {
|
|
2256
|
-
mkdirSync2(this.paths.defaultsDir, { recursive: true });
|
|
2257
|
-
writeFileSync2(
|
|
2258
|
-
join3(this.paths.defaultsDir, "compaction.md"),
|
|
2259
|
-
`${this.defaultPrompt.trim()}
|
|
2260
|
-
`
|
|
2261
|
-
);
|
|
2262
|
-
writeFileSync2(
|
|
2263
|
-
join3(this.paths.defaultsDir, "README.md"),
|
|
2264
|
-
"# DCP compaction prompt\n\nCopy `compaction.md` to an `overrides` directory and restart OpenCode.\n"
|
|
2265
|
-
);
|
|
2266
|
-
} catch (error) {
|
|
2267
|
-
this.logger.warn("Failed to write bundled compaction prompt", {
|
|
2268
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2269
|
-
});
|
|
2270
|
-
}
|
|
2271
|
-
}
|
|
2272
|
-
};
|
|
2273
|
-
|
|
2274
2105
|
// lib/prune-tool.ts
|
|
2275
2106
|
import { tool } from "@opencode-ai/plugin";
|
|
2276
2107
|
var PRUNE_TOOL_NAME = "dcp_prune";
|
|
@@ -2299,7 +2130,7 @@ function createPruneTool(deps) {
|
|
|
2299
2130
|
|
|
2300
2131
|
// lib/update.ts
|
|
2301
2132
|
import { readFile } from "fs/promises";
|
|
2302
|
-
import { basename, dirname as
|
|
2133
|
+
import { basename, dirname as dirname2, join as join3 } from "path";
|
|
2303
2134
|
import { fileURLToPath } from "url";
|
|
2304
2135
|
var PACKAGE_NAME = "@lexwdex-org/opencode-dcp";
|
|
2305
2136
|
function startAutoUpdate(ctx, enabled) {
|
|
@@ -2325,7 +2156,7 @@ function startAutoUpdate(ctx, enabled) {
|
|
|
2325
2156
|
async function checkAutoUpdate(signal) {
|
|
2326
2157
|
const packageDir = await findPackageDir(PACKAGE_NAME);
|
|
2327
2158
|
if (!packageDir) return { available: false };
|
|
2328
|
-
const pkg = await readPackageJson(
|
|
2159
|
+
const pkg = await readPackageJson(join3(packageDir, "package.json"));
|
|
2329
2160
|
if (!pkg?.name || !pkg.version) return { available: false };
|
|
2330
2161
|
const latest = await fetchLatestVersion(pkg.name, signal);
|
|
2331
2162
|
if (!latest || !isVersionNewer(latest, pkg.version)) return { available: false };
|
|
@@ -2334,21 +2165,21 @@ async function checkAutoUpdate(signal) {
|
|
|
2334
2165
|
return { available: true, name: pkg.name, current: pkg.version, latest };
|
|
2335
2166
|
}
|
|
2336
2167
|
async function findPackageDir(name) {
|
|
2337
|
-
let dir =
|
|
2168
|
+
let dir = dirname2(fileURLToPath(import.meta.url));
|
|
2338
2169
|
for (; ; ) {
|
|
2339
|
-
const pkg = await readPackageJson(
|
|
2170
|
+
const pkg = await readPackageJson(join3(dir, "package.json"));
|
|
2340
2171
|
if (pkg?.name === name) return dir;
|
|
2341
|
-
const parent =
|
|
2172
|
+
const parent = dirname2(dir);
|
|
2342
2173
|
if (parent === dir) return void 0;
|
|
2343
2174
|
dir = parent;
|
|
2344
2175
|
}
|
|
2345
2176
|
}
|
|
2346
2177
|
async function findAutoUpdateWrapperDir(packageDir, name) {
|
|
2347
|
-
const packageParent =
|
|
2348
|
-
const nodeModulesDir = basename(packageParent).startsWith("@") ?
|
|
2178
|
+
const packageParent = dirname2(packageDir);
|
|
2179
|
+
const nodeModulesDir = basename(packageParent).startsWith("@") ? dirname2(packageParent) : packageParent;
|
|
2349
2180
|
if (basename(nodeModulesDir) !== "node_modules") return void 0;
|
|
2350
|
-
const wrapperDir =
|
|
2351
|
-
const wrapperPkg = await readPackageJson(
|
|
2181
|
+
const wrapperDir = dirname2(nodeModulesDir);
|
|
2182
|
+
const wrapperPkg = await readPackageJson(join3(wrapperDir, "package.json"));
|
|
2352
2183
|
const spec = wrapperSpec(wrapperDir, name) ?? wrapperPkg?.dependencies?.[name];
|
|
2353
2184
|
if (!spec || !isAutoUpdatableSpec(spec)) return void 0;
|
|
2354
2185
|
return wrapperDir;
|
|
@@ -2356,7 +2187,7 @@ async function findAutoUpdateWrapperDir(packageDir, name) {
|
|
|
2356
2187
|
function wrapperSpec(wrapperDir, name) {
|
|
2357
2188
|
if (name.startsWith("@")) {
|
|
2358
2189
|
const [scope, pkg] = name.split("/");
|
|
2359
|
-
if (!scope || !pkg || basename(
|
|
2190
|
+
if (!scope || !pkg || basename(dirname2(wrapperDir)) !== scope) return void 0;
|
|
2360
2191
|
const prefix2 = `${pkg}@`;
|
|
2361
2192
|
const base2 = basename(wrapperDir);
|
|
2362
2193
|
return base2.startsWith(prefix2) ? base2.slice(prefix2.length) : void 0;
|
|
@@ -2437,37 +2268,28 @@ var server = (async (ctx) => {
|
|
|
2437
2268
|
const config = getConfig(ctx);
|
|
2438
2269
|
if (!config.enabled) return {};
|
|
2439
2270
|
const logger = new Logger(config.debug);
|
|
2440
|
-
const prompts = new PromptStore(
|
|
2441
|
-
logger,
|
|
2442
|
-
ctx.directory,
|
|
2443
|
-
config.experimental.customPrompts,
|
|
2444
|
-
config.language
|
|
2445
|
-
);
|
|
2446
2271
|
const state = new DtcState();
|
|
2447
2272
|
logger.info("DCP initialized", {
|
|
2448
2273
|
commands: config.commands.enabled,
|
|
2449
2274
|
dtc: config.dtc.enabled,
|
|
2450
|
-
tool: config.tool.enabled
|
|
2451
|
-
customPrompts: config.experimental.customPrompts
|
|
2275
|
+
tool: config.tool.enabled
|
|
2452
2276
|
});
|
|
2453
2277
|
startAutoUpdate(ctx, config.autoUpdate);
|
|
2454
2278
|
return {
|
|
2455
|
-
"experimental.session.compacting": createSessionCompactingHandler({
|
|
2456
|
-
prompts,
|
|
2457
|
-
logger,
|
|
2458
|
-
client: ctx.client,
|
|
2459
|
-
state
|
|
2460
|
-
}),
|
|
2461
2279
|
// THE compression surface: dynamic tiered folding on every model
|
|
2462
2280
|
// request, plus the chat.params feed that teaches the engine each
|
|
2463
|
-
// session's context-window size.
|
|
2281
|
+
// session's context-window size. The compacting hook rides along
|
|
2282
|
+
// solely to keep the host's native compaction input unfolded — the
|
|
2283
|
+
// native prompt is never touched. With DTC off, DCP registers no
|
|
2284
|
+
// compaction-adjacent hook at all.
|
|
2464
2285
|
...config.dtc.enabled && {
|
|
2465
2286
|
"experimental.chat.messages.transform": createTransformHandler({
|
|
2466
2287
|
state,
|
|
2467
2288
|
config: config.dtc,
|
|
2468
2289
|
logger
|
|
2469
2290
|
}),
|
|
2470
|
-
"chat.params": createChatParamsHandler({ state, logger })
|
|
2291
|
+
"chat.params": createChatParamsHandler({ state, logger }),
|
|
2292
|
+
"experimental.session.compacting": createSessionCompactingHandler({ state, logger })
|
|
2471
2293
|
},
|
|
2472
2294
|
// Lifecycle cleanup for DTC session state (LRU-bounded regardless).
|
|
2473
2295
|
event: createEventHandler({ state, logger }),
|