@lexwdex-org/opencode-dcp 3.3.3 → 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 +292 -27
- 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,16 +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 availableBlockRefs = Array.from(lookup.keys()).filter((key) => key.startsWith("b")).sort((a, b) => Number.parseInt(a.slice(1), 10) - Number.parseInt(b.slice(1), 10)).join(", ");
|
|
2006
|
-
const blockHint = availableBlockRefs ? ` Available block IDs: ${availableBlockRefs}.` : " No block IDs available.";
|
|
2007
2085
|
if (!startReference) {
|
|
2086
|
+
const wasKnown = state.messageIds.byRef.has(parsedStartId.ref);
|
|
2087
|
+
const reason = wasKnown ? " (message was likely compacted out of the conversation)" : " (invalid ID)";
|
|
2008
2088
|
issues.push(
|
|
2009
|
-
`startId ${parsedStartId.ref} is not available
|
|
2089
|
+
`startId ${parsedStartId.ref} is not available${reason}. Choose an injected ID visible in context.${hint}${guidance}`
|
|
2010
2090
|
);
|
|
2011
2091
|
}
|
|
2012
2092
|
if (!endReference) {
|
|
2093
|
+
const wasKnown = state.messageIds.byRef.has(parsedEndId.ref);
|
|
2094
|
+
const reason = wasKnown ? " (message was likely compacted out of the conversation)" : " (invalid ID)";
|
|
2013
2095
|
issues.push(
|
|
2014
|
-
`endId ${parsedEndId.ref} is not available
|
|
2096
|
+
`endId ${parsedEndId.ref} is not available${reason}. Choose an injected ID visible in context.${hint}${guidance}`
|
|
2015
2097
|
);
|
|
2016
2098
|
}
|
|
2017
2099
|
}
|
|
@@ -2117,6 +2199,17 @@ function resolveAnchorMessageId(startReference) {
|
|
|
2117
2199
|
}
|
|
2118
2200
|
return startReference.messageId;
|
|
2119
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
|
+
}
|
|
2120
2213
|
function buildBoundaryLookup(context, state) {
|
|
2121
2214
|
const lookup = /* @__PURE__ */ new Map();
|
|
2122
2215
|
for (const [messageRef, messageId] of state.messageIds.byRef) {
|
|
@@ -2434,8 +2527,10 @@ function validateArgs(args) {
|
|
|
2434
2527
|
if (typeof entry?.topic !== "string" || entry.topic.trim().length === 0) {
|
|
2435
2528
|
throw new Error(`${prefix}.topic is required and must be a non-empty string`);
|
|
2436
2529
|
}
|
|
2437
|
-
if (
|
|
2438
|
-
|
|
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
|
+
}
|
|
2439
2534
|
}
|
|
2440
2535
|
}
|
|
2441
2536
|
}
|
|
@@ -4153,8 +4248,118 @@ ${output}`);
|
|
|
4153
4248
|
return summary + heading + protectedOutputs.join("");
|
|
4154
4249
|
}
|
|
4155
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
|
+
|
|
4156
4347
|
// lib/compress/message.ts
|
|
4157
|
-
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");
|
|
4158
4363
|
return {
|
|
4159
4364
|
topic: tool.schema.string().describe(
|
|
4160
4365
|
"Short label (3-5 words) for the overall batch - e.g., 'Closed Research Notes'"
|
|
@@ -4163,7 +4368,7 @@ function buildSchema() {
|
|
|
4163
4368
|
tool.schema.object({
|
|
4164
4369
|
messageId: tool.schema.string().describe("Raw message ID to compress (e.g. m0001)"),
|
|
4165
4370
|
topic: tool.schema.string().describe("Short label (3-5 words) for this one message summary"),
|
|
4166
|
-
summary:
|
|
4371
|
+
summary: summaryField
|
|
4167
4372
|
})
|
|
4168
4373
|
).describe("Batch of individual message summaries to create in one tool call")
|
|
4169
4374
|
};
|
|
@@ -4171,9 +4376,10 @@ function buildSchema() {
|
|
|
4171
4376
|
function createCompressMessageTool(ctx) {
|
|
4172
4377
|
ctx.prompts.reload();
|
|
4173
4378
|
const runtimePrompts = ctx.prompts.getRuntimePrompts();
|
|
4379
|
+
const externalModelEnabled = ctx.config.compress.externalModel !== void 0;
|
|
4174
4380
|
return tool({
|
|
4175
|
-
description: runtimePrompts.compressMessage +
|
|
4176
|
-
args: buildSchema(),
|
|
4381
|
+
description: runtimePrompts.compressMessage + buildMessageFormatExtension(externalModelEnabled),
|
|
4382
|
+
args: buildSchema(externalModelEnabled),
|
|
4177
4383
|
async execute(args, toolCtx) {
|
|
4178
4384
|
const input = args;
|
|
4179
4385
|
validateArgs(input);
|
|
@@ -4192,9 +4398,29 @@ function createCompressMessageTool(ctx) {
|
|
|
4192
4398
|
if (plans.length === 0 && skippedCount > 0) {
|
|
4193
4399
|
throw new Error(formatIssues(skippedIssues, skippedCount));
|
|
4194
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
|
+
}
|
|
4195
4416
|
const notifications = [];
|
|
4196
4417
|
const preparedPlans = [];
|
|
4197
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
|
+
}
|
|
4198
4424
|
const summaryWithPromptInfo = appendProtectedPromptInfo(
|
|
4199
4425
|
plan.entry.summary,
|
|
4200
4426
|
plan.selection,
|
|
@@ -4275,8 +4501,10 @@ function validateArgs2(args) {
|
|
|
4275
4501
|
if (typeof entry?.endId !== "string" || entry.endId.trim().length === 0) {
|
|
4276
4502
|
throw new Error(`${prefix}.endId is required and must be a non-empty string`);
|
|
4277
4503
|
}
|
|
4278
|
-
if (
|
|
4279
|
-
|
|
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
|
+
}
|
|
4280
4508
|
}
|
|
4281
4509
|
}
|
|
4282
4510
|
}
|
|
@@ -4482,7 +4710,23 @@ ${right}`;
|
|
|
4482
4710
|
}
|
|
4483
4711
|
|
|
4484
4712
|
// lib/compress/range.ts
|
|
4485
|
-
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");
|
|
4486
4730
|
return {
|
|
4487
4731
|
topic: tool2.schema.string().describe("Short label (3-5 words) for display - e.g., 'Auth System Exploration'"),
|
|
4488
4732
|
content: tool2.schema.array(
|
|
@@ -4491,7 +4735,7 @@ function buildSchema2() {
|
|
|
4491
4735
|
"Message or block ID marking the beginning of range (e.g. m0001, b2)"
|
|
4492
4736
|
),
|
|
4493
4737
|
endId: tool2.schema.string().describe("Message or block ID marking the end of range (e.g. m0012, b5)"),
|
|
4494
|
-
summary:
|
|
4738
|
+
summary: summaryField
|
|
4495
4739
|
})
|
|
4496
4740
|
).describe(
|
|
4497
4741
|
"One or more ranges to compress, each with start/end boundaries and a summary"
|
|
@@ -4501,9 +4745,10 @@ function buildSchema2() {
|
|
|
4501
4745
|
function createCompressRangeTool(ctx) {
|
|
4502
4746
|
ctx.prompts.reload();
|
|
4503
4747
|
const runtimePrompts = ctx.prompts.getRuntimePrompts();
|
|
4748
|
+
const externalModelEnabled = ctx.config.compress.externalModel !== void 0;
|
|
4504
4749
|
return tool2({
|
|
4505
|
-
description: runtimePrompts.compressRange +
|
|
4506
|
-
args: buildSchema2(),
|
|
4750
|
+
description: runtimePrompts.compressRange + buildRangeFormatExtension(externalModelEnabled),
|
|
4751
|
+
args: buildSchema2(externalModelEnabled),
|
|
4507
4752
|
async execute(args, toolCtx) {
|
|
4508
4753
|
const input = args;
|
|
4509
4754
|
validateArgs2(input);
|
|
@@ -4515,10 +4760,30 @@ function createCompressRangeTool(ctx) {
|
|
|
4515
4760
|
);
|
|
4516
4761
|
const resolvedPlans = resolveRanges(input, searchContext, ctx.state);
|
|
4517
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
|
+
}
|
|
4518
4778
|
const notifications = [];
|
|
4519
4779
|
const preparedPlans = [];
|
|
4520
4780
|
let totalCompressedMessages = 0;
|
|
4521
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
|
+
}
|
|
4522
4787
|
const parsedPlaceholders = parseBlockPlaceholders(plan.entry.summary);
|
|
4523
4788
|
const missingBlockIds = validateSummaryPlaceholders(
|
|
4524
4789
|
parsedPlaceholders,
|