@lexwdex-org/opencode-dcp 3.3.4 → 3.4.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 +290 -0
- package/README.md +112 -56
- package/dist/index.js +288 -35
- package/dist/index.js.map +1 -1
- package/dist/lib/compress/external-inference.d.ts +15 -0
- package/dist/lib/compress/external-inference.d.ts.map +1 -0
- package/dist/lib/compress/message-utils.d.ts.map +1 -1
- package/dist/lib/compress/message.d.ts.map +1 -1
- package/dist/lib/compress/range-utils.d.ts.map +1 -1
- package/dist/lib/compress/range.d.ts.map +1 -1
- package/dist/lib/compress/search.d.ts.map +1 -1
- package/dist/lib/compress/types.d.ts +2 -2
- package/dist/lib/compress/types.d.ts.map +1 -1
- package/dist/lib/config-env-override.d.ts +3 -0
- package/dist/lib/config-env-override.d.ts.map +1 -0
- package/dist/lib/config.d.ts +8 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/prompts/extensions/tool.d.ts +4 -2
- package/dist/lib/prompts/extensions/tool.d.ts.map +1 -1
- package/dist/lib/state/state.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -862,6 +862,37 @@ var ParseErrorCode;
|
|
|
862
862
|
ParseErrorCode2[ParseErrorCode2["InvalidCharacter"] = 16] = "InvalidCharacter";
|
|
863
863
|
})(ParseErrorCode || (ParseErrorCode = {}));
|
|
864
864
|
|
|
865
|
+
// lib/config-env-override.ts
|
|
866
|
+
function applyExternalModelEnvOverride(config) {
|
|
867
|
+
const envUrl = process.env.OPENCODE_DCP_EXTERNAL_COMPRESS_URL;
|
|
868
|
+
const envApiKey = process.env.OPENCODE_DCP_EXTERNAL_COMPRESS_KEY;
|
|
869
|
+
const envModel = process.env.OPENCODE_DCP_EXTERNAL_COMPRESS_MODEL;
|
|
870
|
+
const envTimeout = process.env.OPENCODE_DCP_EXTERNAL_COMPRESS_TIMEOUT;
|
|
871
|
+
const envRetries = process.env.OPENCODE_DCP_EXTERNAL_COMPRESS_RETRIES;
|
|
872
|
+
if (envUrl && envModel) {
|
|
873
|
+
const externalModel = {
|
|
874
|
+
url: envUrl,
|
|
875
|
+
model: envModel
|
|
876
|
+
};
|
|
877
|
+
if (envApiKey) {
|
|
878
|
+
externalModel.apiKey = envApiKey;
|
|
879
|
+
}
|
|
880
|
+
if (envTimeout) {
|
|
881
|
+
const parsed = parseInt(envTimeout, 10);
|
|
882
|
+
if (parsed > 0) {
|
|
883
|
+
externalModel.timeout = parsed;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
if (envRetries) {
|
|
887
|
+
const parsed = parseInt(envRetries, 10);
|
|
888
|
+
if (parsed >= 0) {
|
|
889
|
+
externalModel.retries = parsed;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
config.compress.externalModel = externalModel;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
865
896
|
// lib/config.ts
|
|
866
897
|
var DEFAULT_PROTECTED_TOOLS = [
|
|
867
898
|
"task",
|
|
@@ -912,6 +943,7 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
912
943
|
"compress.protectedTools",
|
|
913
944
|
"compress.protectTags",
|
|
914
945
|
"compress.protectUserMessages",
|
|
946
|
+
"compress.externalModel",
|
|
915
947
|
"strategies",
|
|
916
948
|
"strategies.deduplication",
|
|
917
949
|
"strategies.deduplication.enabled",
|
|
@@ -1213,6 +1245,30 @@ function validateConfigTypes(config) {
|
|
|
1213
1245
|
actual: JSON.stringify(compress.permission)
|
|
1214
1246
|
});
|
|
1215
1247
|
}
|
|
1248
|
+
if (compress.externalModel !== void 0) {
|
|
1249
|
+
if (typeof compress.externalModel !== "object" || compress.externalModel === null || Array.isArray(compress.externalModel)) {
|
|
1250
|
+
errors.push({
|
|
1251
|
+
key: "compress.externalModel",
|
|
1252
|
+
expected: "object with url and model",
|
|
1253
|
+
actual: typeof compress.externalModel
|
|
1254
|
+
});
|
|
1255
|
+
} else {
|
|
1256
|
+
if (typeof compress.externalModel.url !== "string") {
|
|
1257
|
+
errors.push({
|
|
1258
|
+
key: "compress.externalModel.url",
|
|
1259
|
+
expected: "string",
|
|
1260
|
+
actual: typeof compress.externalModel.url
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
if (typeof compress.externalModel.model !== "string") {
|
|
1264
|
+
errors.push({
|
|
1265
|
+
key: "compress.externalModel.model",
|
|
1266
|
+
expected: "string",
|
|
1267
|
+
actual: typeof compress.externalModel.model
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1216
1272
|
if (compress.showCompression !== void 0 && typeof compress.showCompression !== "boolean") {
|
|
1217
1273
|
errors.push({
|
|
1218
1274
|
key: "compress.showCompression",
|
|
@@ -1342,7 +1398,8 @@ var defaultConfig = {
|
|
|
1342
1398
|
nudgeForce: "strong",
|
|
1343
1399
|
protectedTools: [...COMPRESS_DEFAULT_PROTECTED_TOOLS],
|
|
1344
1400
|
protectTags: false,
|
|
1345
|
-
protectUserMessages: false
|
|
1401
|
+
protectUserMessages: false,
|
|
1402
|
+
externalModel: void 0
|
|
1346
1403
|
},
|
|
1347
1404
|
strategies: {
|
|
1348
1405
|
deduplication: {
|
|
@@ -1465,7 +1522,8 @@ function mergeCompress(base, override) {
|
|
|
1465
1522
|
nudgeForce: override.nudgeForce ?? base.nudgeForce,
|
|
1466
1523
|
protectedTools: [.../* @__PURE__ */ new Set([...base.protectedTools, ...override.protectedTools ?? []])],
|
|
1467
1524
|
protectTags: override.protectTags ?? base.protectTags,
|
|
1468
|
-
protectUserMessages: override.protectUserMessages ?? base.protectUserMessages
|
|
1525
|
+
protectUserMessages: override.protectUserMessages ?? base.protectUserMessages,
|
|
1526
|
+
externalModel: override.externalModel ?? base.externalModel
|
|
1469
1527
|
};
|
|
1470
1528
|
}
|
|
1471
1529
|
function mergeCommands(base, override) {
|
|
@@ -1509,7 +1567,8 @@ function deepCloneConfig(config) {
|
|
|
1509
1567
|
...config.compress,
|
|
1510
1568
|
modelMaxLimits: { ...config.compress.modelMaxLimits },
|
|
1511
1569
|
modelMinLimits: { ...config.compress.modelMinLimits },
|
|
1512
|
-
protectedTools: [...config.compress.protectedTools]
|
|
1570
|
+
protectedTools: [...config.compress.protectedTools],
|
|
1571
|
+
externalModel: config.compress.externalModel ? { ...config.compress.externalModel } : void 0
|
|
1513
1572
|
},
|
|
1514
1573
|
strategies: {
|
|
1515
1574
|
deduplication: {
|
|
@@ -1591,6 +1650,7 @@ Using previous/default values`
|
|
|
1591
1650
|
showConfigWarnings(ctx, layer.path, result.data, layer.isProject);
|
|
1592
1651
|
config = mergeLayer(config, result.data);
|
|
1593
1652
|
}
|
|
1653
|
+
applyExternalModelEnvOverride(config);
|
|
1594
1654
|
return config;
|
|
1595
1655
|
}
|
|
1596
1656
|
|
|
@@ -1793,7 +1853,12 @@ function countAllMessageTokens(msg) {
|
|
|
1793
1853
|
}
|
|
1794
1854
|
|
|
1795
1855
|
// lib/prompts/extensions/tool.ts
|
|
1796
|
-
|
|
1856
|
+
function buildRangeFormatExtension(externalModelEnabled) {
|
|
1857
|
+
const summaryField = externalModelEnabled ? `summary?: string // \u66FF\u6362\u8303\u56F4\u5185\u6240\u6709\u5185\u5BB9\u7684\u5B8C\u6574\u6280\u672F\u6458\u8981\uFF08\u53EF\u7701\u7565\uFF1B\u5916\u90E8\u6A21\u578B\u751F\u6210\uFF09` : `summary: string // \u66FF\u6362\u8303\u56F4\u5185\u6240\u6709\u5185\u5BB9\u7684\u5B8C\u6574\u6280\u672F\u6458\u8981`;
|
|
1858
|
+
const externalHint = externalModelEnabled ? `
|
|
1859
|
+
|
|
1860
|
+
\u5F53\u542F\u7528\u5916\u90E8\u6A21\u578B\u538B\u7F29\u65F6\uFF0Csummary \u5B57\u6BB5\u53EF\u7701\u7565\uFF1B\u63D2\u4EF6\u5C06\u81EA\u52A8\u8C03\u7528\u5916\u90E8\u6A21\u578B\u751F\u6210\u3002` : "";
|
|
1861
|
+
return `
|
|
1797
1862
|
\u538B\u7F29\u683C\u5F0F
|
|
1798
1863
|
|
|
1799
1864
|
\`\`\`
|
|
@@ -1803,12 +1868,18 @@ var RANGE_FORMAT_EXTENSION = `
|
|
|
1803
1868
|
{
|
|
1804
1869
|
startId: string, // \u8303\u56F4\u5F00\u59CB\u7684\u8FB9\u754C ID\uFF1AmNNNN \u6216 bN
|
|
1805
1870
|
endId: string, // \u8303\u56F4\u7ED3\u675F\u7684\u8FB9\u754C ID\uFF1AmNNNN \u6216 bN
|
|
1806
|
-
|
|
1871
|
+
${summaryField}
|
|
1807
1872
|
}
|
|
1808
1873
|
]
|
|
1809
1874
|
}
|
|
1810
|
-
|
|
1811
|
-
|
|
1875
|
+
\`\`\`${externalHint}`;
|
|
1876
|
+
}
|
|
1877
|
+
function buildMessageFormatExtension(externalModelEnabled) {
|
|
1878
|
+
const summaryField = externalModelEnabled ? `summary?: string // \u66FF\u6362\u8BE5\u6761\u6D88\u606F\u7684\u5B8C\u6574\u6280\u672F\u6458\u8981\uFF08\u53EF\u7701\u7565\uFF1B\u5916\u90E8\u6A21\u578B\u751F\u6210\uFF09` : `summary: string // \u66FF\u6362\u8BE5\u6761\u6D88\u606F\u7684\u5B8C\u6574\u6280\u672F\u6458\u8981`;
|
|
1879
|
+
const externalHint = externalModelEnabled ? `
|
|
1880
|
+
|
|
1881
|
+
\u5F53\u542F\u7528\u5916\u90E8\u6A21\u578B\u538B\u7F29\u65F6\uFF0Csummary \u5B57\u6BB5\u53EF\u7701\u7565\uFF1B\u63D2\u4EF6\u5C06\u81EA\u52A8\u8C03\u7528\u5916\u90E8\u6A21\u578B\u751F\u6210\u3002` : "";
|
|
1882
|
+
return `
|
|
1812
1883
|
\u538B\u7F29\u683C\u5F0F
|
|
1813
1884
|
|
|
1814
1885
|
\`\`\`
|
|
@@ -1818,11 +1889,14 @@ var MESSAGE_FORMAT_EXTENSION = `
|
|
|
1818
1889
|
{
|
|
1819
1890
|
messageId: string, // \u4EC5\u539F\u59CB\u6D88\u606F ID\uFF1AmNNNN\uFF08\u5FFD\u7565 priority \u7B49\u5143\u6570\u636E\u5C5E\u6027\uFF09
|
|
1820
1891
|
topic: string, // \u6B64\u5355\u6761\u6D88\u606F\u6458\u8981\u7684\u77ED\u6807\u7B7E\uFF083-5 \u4E2A\u8BCD\uFF09
|
|
1821
|
-
|
|
1892
|
+
${summaryField}
|
|
1822
1893
|
}
|
|
1823
1894
|
]
|
|
1824
1895
|
}
|
|
1825
|
-
|
|
1896
|
+
\`\`\`${externalHint}`;
|
|
1897
|
+
}
|
|
1898
|
+
var RANGE_FORMAT_EXTENSION = buildRangeFormatExtension(false);
|
|
1899
|
+
var MESSAGE_FORMAT_EXTENSION = buildMessageFormatExtension(false);
|
|
1826
1900
|
|
|
1827
1901
|
// lib/message-ids.ts
|
|
1828
1902
|
var MESSAGE_REF_REGEX = /^m(\d{4})$/;
|
|
@@ -1985,11 +2059,17 @@ function resolveBoundaryIds(context, state, startId, endId) {
|
|
|
1985
2059
|
const issues = [];
|
|
1986
2060
|
const parsedStartId = parseBoundaryId(startId);
|
|
1987
2061
|
const parsedEndId = parseBoundaryId(endId);
|
|
2062
|
+
const hint = formatAvailableBoundaryHint(lookup);
|
|
2063
|
+
const guidance = formatBoundaryUsageGuidance();
|
|
1988
2064
|
if (parsedStartId === null) {
|
|
1989
|
-
issues.push(
|
|
2065
|
+
issues.push(
|
|
2066
|
+
`startId is invalid. Use an injected message ID (mNNNN) or block ID (bN).${hint}${guidance}`
|
|
2067
|
+
);
|
|
1990
2068
|
}
|
|
1991
2069
|
if (parsedEndId === null) {
|
|
1992
|
-
issues.push(
|
|
2070
|
+
issues.push(
|
|
2071
|
+
`endId is invalid. Use an injected message ID (mNNNN) or block ID (bN).${hint}${guidance}`
|
|
2072
|
+
);
|
|
1993
2073
|
}
|
|
1994
2074
|
if (issues.length > 0) {
|
|
1995
2075
|
throw new Error(
|
|
@@ -2002,28 +2082,18 @@ function resolveBoundaryIds(context, state, startId, endId) {
|
|
|
2002
2082
|
const startReference = lookup.get(parsedStartId.ref);
|
|
2003
2083
|
const endReference = lookup.get(parsedEndId.ref);
|
|
2004
2084
|
if (!startReference || !endReference) {
|
|
2005
|
-
const allKeys = Array.from(lookup.keys());
|
|
2006
|
-
const availableBlockRefs = allKeys.filter((key) => key.startsWith("b")).sort(
|
|
2007
|
-
(a, b) => Number.parseInt(a.slice(1), 10) - Number.parseInt(b.slice(1), 10)
|
|
2008
|
-
);
|
|
2009
|
-
const availableMsgRefs = allKeys.filter((key) => key.startsWith("m")).sort(
|
|
2010
|
-
(a, b) => Number.parseInt(a.slice(1), 10) - Number.parseInt(b.slice(1), 10)
|
|
2011
|
-
);
|
|
2012
|
-
const blockHint = availableBlockRefs.length ? ` Available block IDs: ${availableBlockRefs.join(", ")}.` : " No block IDs available.";
|
|
2013
|
-
const msgHint = availableMsgRefs.length === 0 ? " No message IDs available." : availableMsgRefs.length <= 10 ? ` Available message IDs: ${availableMsgRefs.join(", ")}.` : ` Available message IDs: ${availableMsgRefs[0]} to ${availableMsgRefs[availableMsgRefs.length - 1]} (${availableMsgRefs.length} total).`;
|
|
2014
|
-
const hint = `${msgHint}${blockHint}`;
|
|
2015
2085
|
if (!startReference) {
|
|
2016
2086
|
const wasKnown = state.messageIds.byRef.has(parsedStartId.ref);
|
|
2017
2087
|
const reason = wasKnown ? " (message was likely compacted out of the conversation)" : " (invalid ID)";
|
|
2018
2088
|
issues.push(
|
|
2019
|
-
`startId ${parsedStartId.ref} is not available${reason}. Choose an injected ID visible in context.${hint}`
|
|
2089
|
+
`startId ${parsedStartId.ref} is not available${reason}. Choose an injected ID visible in context.${hint}${guidance}`
|
|
2020
2090
|
);
|
|
2021
2091
|
}
|
|
2022
2092
|
if (!endReference) {
|
|
2023
2093
|
const wasKnown = state.messageIds.byRef.has(parsedEndId.ref);
|
|
2024
2094
|
const reason = wasKnown ? " (message was likely compacted out of the conversation)" : " (invalid ID)";
|
|
2025
2095
|
issues.push(
|
|
2026
|
-
`endId ${parsedEndId.ref} is not available${reason}. Choose an injected ID visible in context.${hint}`
|
|
2096
|
+
`endId ${parsedEndId.ref} is not available${reason}. Choose an injected ID visible in context.${hint}${guidance}`
|
|
2027
2097
|
);
|
|
2028
2098
|
}
|
|
2029
2099
|
}
|
|
@@ -2129,6 +2199,17 @@ function resolveAnchorMessageId(startReference) {
|
|
|
2129
2199
|
}
|
|
2130
2200
|
return startReference.messageId;
|
|
2131
2201
|
}
|
|
2202
|
+
function formatAvailableBoundaryHint(lookup) {
|
|
2203
|
+
const allKeys = Array.from(lookup.keys());
|
|
2204
|
+
const availableBlockRefs = allKeys.filter((key) => key.startsWith("b")).sort((a, b) => Number.parseInt(a.slice(1), 10) - Number.parseInt(b.slice(1), 10));
|
|
2205
|
+
const availableMsgRefs = allKeys.filter((key) => key.startsWith("m")).sort((a, b) => Number.parseInt(a.slice(1), 10) - Number.parseInt(b.slice(1), 10));
|
|
2206
|
+
const blockHint = availableBlockRefs.length ? ` Available block IDs: ${availableBlockRefs.join(", ")}.` : " No block IDs available.";
|
|
2207
|
+
const msgHint = availableMsgRefs.length === 0 ? " No message IDs available." : availableMsgRefs.length <= 10 ? ` Available message IDs: ${availableMsgRefs.join(", ")}.` : ` Available message IDs: ${availableMsgRefs[0]} to ${availableMsgRefs[availableMsgRefs.length - 1]} (${availableMsgRefs.length} total).`;
|
|
2208
|
+
return `${msgHint}${blockHint}`;
|
|
2209
|
+
}
|
|
2210
|
+
function formatBoundaryUsageGuidance() {
|
|
2211
|
+
return " Use exactly one of the listed injected IDs. For already compressed content, use its bN block ID.";
|
|
2212
|
+
}
|
|
2132
2213
|
function buildBoundaryLookup(context, state) {
|
|
2133
2214
|
const lookup = /* @__PURE__ */ new Map();
|
|
2134
2215
|
for (const [messageRef, messageId] of state.messageIds.byRef) {
|
|
@@ -2446,8 +2527,10 @@ function validateArgs(args) {
|
|
|
2446
2527
|
if (typeof entry?.topic !== "string" || entry.topic.trim().length === 0) {
|
|
2447
2528
|
throw new Error(`${prefix}.topic is required and must be a non-empty string`);
|
|
2448
2529
|
}
|
|
2449
|
-
if (
|
|
2450
|
-
|
|
2530
|
+
if (entry?.summary !== void 0) {
|
|
2531
|
+
if (typeof entry.summary !== "string" || entry.summary.trim().length === 0) {
|
|
2532
|
+
throw new Error(`${prefix}.summary must be a non-empty string when provided`);
|
|
2533
|
+
}
|
|
2451
2534
|
}
|
|
2452
2535
|
}
|
|
2453
2536
|
}
|
|
@@ -4165,8 +4248,118 @@ ${output}`);
|
|
|
4165
4248
|
return summary + heading + protectedOutputs.join("");
|
|
4166
4249
|
}
|
|
4167
4250
|
|
|
4251
|
+
// lib/compress/external-inference.ts
|
|
4252
|
+
var ExternalModelError = class extends Error {
|
|
4253
|
+
constructor(message, kind, cause) {
|
|
4254
|
+
super(message);
|
|
4255
|
+
this.kind = kind;
|
|
4256
|
+
this.cause = cause;
|
|
4257
|
+
this.name = "ExternalModelError";
|
|
4258
|
+
}
|
|
4259
|
+
};
|
|
4260
|
+
function resolveCompressBaseUrl(url) {
|
|
4261
|
+
const normalized = url.replace(/\/$/, "");
|
|
4262
|
+
if (normalized.endsWith("/chat/completions")) {
|
|
4263
|
+
return normalized;
|
|
4264
|
+
}
|
|
4265
|
+
return `${normalized}/chat/completions`;
|
|
4266
|
+
}
|
|
4267
|
+
async function callExternalModel(endpoint, cfg, req) {
|
|
4268
|
+
const controller = new AbortController();
|
|
4269
|
+
const timeoutMs = cfg.timeout ?? 12e4;
|
|
4270
|
+
const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);
|
|
4271
|
+
let response;
|
|
4272
|
+
try {
|
|
4273
|
+
response = await fetch(endpoint, {
|
|
4274
|
+
method: "POST",
|
|
4275
|
+
headers: {
|
|
4276
|
+
"Content-Type": "application/json",
|
|
4277
|
+
...cfg.apiKey ? { Authorization: `Bearer ${cfg.apiKey}` } : {}
|
|
4278
|
+
},
|
|
4279
|
+
body: JSON.stringify({
|
|
4280
|
+
model: cfg.model,
|
|
4281
|
+
messages: [
|
|
4282
|
+
{ role: "system", content: req.systemPrompt },
|
|
4283
|
+
{ role: "user", content: req.userContent }
|
|
4284
|
+
],
|
|
4285
|
+
temperature: 0
|
|
4286
|
+
}),
|
|
4287
|
+
signal: controller.signal
|
|
4288
|
+
});
|
|
4289
|
+
} catch (error) {
|
|
4290
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
4291
|
+
throw new ExternalModelError(
|
|
4292
|
+
`External model request timed out after ${timeoutMs}ms`,
|
|
4293
|
+
"timeout",
|
|
4294
|
+
error
|
|
4295
|
+
);
|
|
4296
|
+
}
|
|
4297
|
+
throw new ExternalModelError(
|
|
4298
|
+
`External model request failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
4299
|
+
"network",
|
|
4300
|
+
error
|
|
4301
|
+
);
|
|
4302
|
+
} finally {
|
|
4303
|
+
clearTimeout(timeoutHandle);
|
|
4304
|
+
}
|
|
4305
|
+
if (!response.ok) {
|
|
4306
|
+
let errorBody = "";
|
|
4307
|
+
try {
|
|
4308
|
+
errorBody = await response.text();
|
|
4309
|
+
} catch {
|
|
4310
|
+
}
|
|
4311
|
+
throw new ExternalModelError(
|
|
4312
|
+
`External model returned HTTP ${response.status}: ${errorBody.slice(0, 200)}`,
|
|
4313
|
+
"http",
|
|
4314
|
+
{ status: response.status, body: errorBody }
|
|
4315
|
+
);
|
|
4316
|
+
}
|
|
4317
|
+
let parsed;
|
|
4318
|
+
try {
|
|
4319
|
+
parsed = await response.json();
|
|
4320
|
+
} catch (error) {
|
|
4321
|
+
throw new ExternalModelError("External model returned invalid JSON", "parse", error);
|
|
4322
|
+
}
|
|
4323
|
+
const content = parsed?.choices?.[0]?.message?.content;
|
|
4324
|
+
if (typeof content !== "string" || content.trim().length === 0) {
|
|
4325
|
+
throw new ExternalModelError(
|
|
4326
|
+
"External model returned empty or missing summary content",
|
|
4327
|
+
"empty",
|
|
4328
|
+
parsed
|
|
4329
|
+
);
|
|
4330
|
+
}
|
|
4331
|
+
return content;
|
|
4332
|
+
}
|
|
4333
|
+
async function generateSummaryViaExternal(cfg, req) {
|
|
4334
|
+
const endpoint = resolveCompressBaseUrl(cfg.url);
|
|
4335
|
+
const maxRetries = cfg.retries ?? 1;
|
|
4336
|
+
let lastError;
|
|
4337
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
4338
|
+
try {
|
|
4339
|
+
return await callExternalModel(endpoint, cfg, req);
|
|
4340
|
+
} catch (error) {
|
|
4341
|
+
lastError = error;
|
|
4342
|
+
}
|
|
4343
|
+
}
|
|
4344
|
+
throw lastError;
|
|
4345
|
+
}
|
|
4346
|
+
|
|
4168
4347
|
// lib/compress/message.ts
|
|
4169
|
-
function
|
|
4348
|
+
function extractMessageText(messageId, searchContext) {
|
|
4349
|
+
const msg = searchContext.rawMessagesById.get(messageId);
|
|
4350
|
+
if (!msg) return "";
|
|
4351
|
+
const parts = [];
|
|
4352
|
+
for (const part of msg.parts) {
|
|
4353
|
+
if (part.type === "text" && typeof part.text === "string") {
|
|
4354
|
+
parts.push(part.text);
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
return parts.join("\n\n");
|
|
4358
|
+
}
|
|
4359
|
+
function buildSchema(externalModelEnabled) {
|
|
4360
|
+
const summaryField = externalModelEnabled ? tool.schema.string().optional().describe(
|
|
4361
|
+
"Complete technical summary replacing that one message (optional when external model is configured)"
|
|
4362
|
+
) : tool.schema.string().describe("Complete technical summary replacing that one message");
|
|
4170
4363
|
return {
|
|
4171
4364
|
topic: tool.schema.string().describe(
|
|
4172
4365
|
"Short label (3-5 words) for the overall batch - e.g., 'Closed Research Notes'"
|
|
@@ -4175,7 +4368,7 @@ function buildSchema() {
|
|
|
4175
4368
|
tool.schema.object({
|
|
4176
4369
|
messageId: tool.schema.string().describe("Raw message ID to compress (e.g. m0001)"),
|
|
4177
4370
|
topic: tool.schema.string().describe("Short label (3-5 words) for this one message summary"),
|
|
4178
|
-
summary:
|
|
4371
|
+
summary: summaryField
|
|
4179
4372
|
})
|
|
4180
4373
|
).describe("Batch of individual message summaries to create in one tool call")
|
|
4181
4374
|
};
|
|
@@ -4183,9 +4376,10 @@ function buildSchema() {
|
|
|
4183
4376
|
function createCompressMessageTool(ctx) {
|
|
4184
4377
|
ctx.prompts.reload();
|
|
4185
4378
|
const runtimePrompts = ctx.prompts.getRuntimePrompts();
|
|
4379
|
+
const externalModelEnabled = ctx.config.compress.externalModel !== void 0;
|
|
4186
4380
|
return tool({
|
|
4187
|
-
description: runtimePrompts.compressMessage +
|
|
4188
|
-
args: buildSchema(),
|
|
4381
|
+
description: runtimePrompts.compressMessage + buildMessageFormatExtension(externalModelEnabled),
|
|
4382
|
+
args: buildSchema(externalModelEnabled),
|
|
4189
4383
|
async execute(args, toolCtx) {
|
|
4190
4384
|
const input = args;
|
|
4191
4385
|
validateArgs(input);
|
|
@@ -4204,9 +4398,29 @@ function createCompressMessageTool(ctx) {
|
|
|
4204
4398
|
if (plans.length === 0 && skippedCount > 0) {
|
|
4205
4399
|
throw new Error(formatIssues(skippedIssues, skippedCount));
|
|
4206
4400
|
}
|
|
4401
|
+
if (ctx.config.compress.externalModel) {
|
|
4402
|
+
for (const plan of plans) {
|
|
4403
|
+
if (plan.entry.summary === void 0) {
|
|
4404
|
+
const userContent = extractMessageText(plan.entry.messageId, searchContext);
|
|
4405
|
+
const generated = await generateSummaryViaExternal(
|
|
4406
|
+
ctx.config.compress.externalModel,
|
|
4407
|
+
{
|
|
4408
|
+
systemPrompt: runtimePrompts.compressMessage,
|
|
4409
|
+
userContent
|
|
4410
|
+
}
|
|
4411
|
+
);
|
|
4412
|
+
plan.entry.summary = generated;
|
|
4413
|
+
}
|
|
4414
|
+
}
|
|
4415
|
+
}
|
|
4207
4416
|
const notifications = [];
|
|
4208
4417
|
const preparedPlans = [];
|
|
4209
4418
|
for (const plan of plans) {
|
|
4419
|
+
if (plan.entry.summary === void 0) {
|
|
4420
|
+
throw new Error(
|
|
4421
|
+
"\u7F3A\u5C11 summary\uFF1A\u672A\u914D\u7F6E\u5916\u90E8\u6A21\u578B\uFF0C\u8BF7\u63D0\u4F9B summary \u6216\u914D\u7F6E OPENCODE_DCP_EXTERNAL_COMPRESS_URL/MODEL"
|
|
4422
|
+
);
|
|
4423
|
+
}
|
|
4210
4424
|
const summaryWithPromptInfo = appendProtectedPromptInfo(
|
|
4211
4425
|
plan.entry.summary,
|
|
4212
4426
|
plan.selection,
|
|
@@ -4287,8 +4501,10 @@ function validateArgs2(args) {
|
|
|
4287
4501
|
if (typeof entry?.endId !== "string" || entry.endId.trim().length === 0) {
|
|
4288
4502
|
throw new Error(`${prefix}.endId is required and must be a non-empty string`);
|
|
4289
4503
|
}
|
|
4290
|
-
if (
|
|
4291
|
-
|
|
4504
|
+
if (entry?.summary !== void 0) {
|
|
4505
|
+
if (typeof entry.summary !== "string" || entry.summary.trim().length === 0) {
|
|
4506
|
+
throw new Error(`${prefix}.summary must be a non-empty string when provided`);
|
|
4507
|
+
}
|
|
4292
4508
|
}
|
|
4293
4509
|
}
|
|
4294
4510
|
}
|
|
@@ -4494,7 +4710,23 @@ ${right}`;
|
|
|
4494
4710
|
}
|
|
4495
4711
|
|
|
4496
4712
|
// lib/compress/range.ts
|
|
4497
|
-
function
|
|
4713
|
+
function extractRangeText(selection, searchContext) {
|
|
4714
|
+
const parts = [];
|
|
4715
|
+
for (const messageId of selection.messageIds) {
|
|
4716
|
+
const msg = searchContext.rawMessagesById.get(messageId);
|
|
4717
|
+
if (!msg) continue;
|
|
4718
|
+
for (const part of msg.parts) {
|
|
4719
|
+
if (part.type === "text" && typeof part.text === "string") {
|
|
4720
|
+
parts.push(part.text);
|
|
4721
|
+
}
|
|
4722
|
+
}
|
|
4723
|
+
}
|
|
4724
|
+
return parts.join("\n\n");
|
|
4725
|
+
}
|
|
4726
|
+
function buildSchema2(externalModelEnabled) {
|
|
4727
|
+
const summaryField = externalModelEnabled ? tool2.schema.string().optional().describe(
|
|
4728
|
+
"Complete technical summary replacing all content in range (optional when external model is configured)"
|
|
4729
|
+
) : tool2.schema.string().describe("Complete technical summary replacing all content in range");
|
|
4498
4730
|
return {
|
|
4499
4731
|
topic: tool2.schema.string().describe("Short label (3-5 words) for display - e.g., 'Auth System Exploration'"),
|
|
4500
4732
|
content: tool2.schema.array(
|
|
@@ -4503,7 +4735,7 @@ function buildSchema2() {
|
|
|
4503
4735
|
"Message or block ID marking the beginning of range (e.g. m0001, b2)"
|
|
4504
4736
|
),
|
|
4505
4737
|
endId: tool2.schema.string().describe("Message or block ID marking the end of range (e.g. m0012, b5)"),
|
|
4506
|
-
summary:
|
|
4738
|
+
summary: summaryField
|
|
4507
4739
|
})
|
|
4508
4740
|
).describe(
|
|
4509
4741
|
"One or more ranges to compress, each with start/end boundaries and a summary"
|
|
@@ -4513,9 +4745,10 @@ function buildSchema2() {
|
|
|
4513
4745
|
function createCompressRangeTool(ctx) {
|
|
4514
4746
|
ctx.prompts.reload();
|
|
4515
4747
|
const runtimePrompts = ctx.prompts.getRuntimePrompts();
|
|
4748
|
+
const externalModelEnabled = ctx.config.compress.externalModel !== void 0;
|
|
4516
4749
|
return tool2({
|
|
4517
|
-
description: runtimePrompts.compressRange +
|
|
4518
|
-
args: buildSchema2(),
|
|
4750
|
+
description: runtimePrompts.compressRange + buildRangeFormatExtension(externalModelEnabled),
|
|
4751
|
+
args: buildSchema2(externalModelEnabled),
|
|
4519
4752
|
async execute(args, toolCtx) {
|
|
4520
4753
|
const input = args;
|
|
4521
4754
|
validateArgs2(input);
|
|
@@ -4527,10 +4760,30 @@ function createCompressRangeTool(ctx) {
|
|
|
4527
4760
|
);
|
|
4528
4761
|
const resolvedPlans = resolveRanges(input, searchContext, ctx.state);
|
|
4529
4762
|
validateNonOverlapping(resolvedPlans);
|
|
4763
|
+
if (ctx.config.compress.externalModel) {
|
|
4764
|
+
for (const plan of resolvedPlans) {
|
|
4765
|
+
if (plan.entry.summary === void 0) {
|
|
4766
|
+
const userContent = extractRangeText(plan.selection, searchContext);
|
|
4767
|
+
const generated = await generateSummaryViaExternal(
|
|
4768
|
+
ctx.config.compress.externalModel,
|
|
4769
|
+
{
|
|
4770
|
+
systemPrompt: runtimePrompts.compressRange,
|
|
4771
|
+
userContent
|
|
4772
|
+
}
|
|
4773
|
+
);
|
|
4774
|
+
plan.entry.summary = generated;
|
|
4775
|
+
}
|
|
4776
|
+
}
|
|
4777
|
+
}
|
|
4530
4778
|
const notifications = [];
|
|
4531
4779
|
const preparedPlans = [];
|
|
4532
4780
|
let totalCompressedMessages = 0;
|
|
4533
4781
|
for (const plan of resolvedPlans) {
|
|
4782
|
+
if (plan.entry.summary === void 0) {
|
|
4783
|
+
throw new Error(
|
|
4784
|
+
"\u7F3A\u5C11 summary\uFF1A\u672A\u914D\u7F6E\u5916\u90E8\u6A21\u578B\uFF0C\u8BF7\u63D0\u4F9B summary \u6216\u914D\u7F6E OPENCODE_DCP_EXTERNAL_COMPRESS_URL/MODEL"
|
|
4785
|
+
);
|
|
4786
|
+
}
|
|
4534
4787
|
const parsedPlaceholders = parseBlockPlaceholders(plan.entry.summary);
|
|
4535
4788
|
const missingBlockIds = validateSummaryPlaceholders(
|
|
4536
4789
|
parsedPlaceholders,
|