opencode-gbk-tools 0.1.23 → 0.1.24
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/dist/opencode-tools/gbk_edit.js +44 -17
- package/dist/opencode-tools/text_edit.js +44 -17
- package/dist/plugin/index.js +329 -33
- package/dist/plugins/opencode-gbk-tools.js +329 -33
- package/dist/release-manifest.json +2 -2
- package/package.json +1 -1
|
@@ -16238,6 +16238,48 @@ function tool(input) {
|
|
|
16238
16238
|
}
|
|
16239
16239
|
tool.schema = external_exports;
|
|
16240
16240
|
|
|
16241
|
+
// src/lib/model-context.ts
|
|
16242
|
+
var FALLBACK_MAX_OUTPUT_CHARS = 8e3;
|
|
16243
|
+
var MIN_BASE_MAX_OUTPUT_CHARS = 4e3;
|
|
16244
|
+
var MAX_BASE_OUTPUT_CHARS = 32e3;
|
|
16245
|
+
var MIN_PRESSURED_OUTPUT_CHARS = 1500;
|
|
16246
|
+
var sessionStates = /* @__PURE__ */ new Map();
|
|
16247
|
+
function getSessionState(sessionID) {
|
|
16248
|
+
if (!sessionID) return null;
|
|
16249
|
+
return sessionStates.get(sessionID) ?? null;
|
|
16250
|
+
}
|
|
16251
|
+
function getCompactionPressureFactor(compactionCount) {
|
|
16252
|
+
if (compactionCount >= 3) return 0.35;
|
|
16253
|
+
if (compactionCount === 2) return 0.5;
|
|
16254
|
+
if (compactionCount === 1) return 0.75;
|
|
16255
|
+
return 1;
|
|
16256
|
+
}
|
|
16257
|
+
function getBaseMaxOutputChars(contextTokens, fallback) {
|
|
16258
|
+
if (contextTokens === null) return fallback;
|
|
16259
|
+
const computed = Math.round(contextTokens * 0.01 * 4);
|
|
16260
|
+
return Math.max(MIN_BASE_MAX_OUTPUT_CHARS, Math.min(MAX_BASE_OUTPUT_CHARS, computed));
|
|
16261
|
+
}
|
|
16262
|
+
function buildTruncationSuffix(sessionID, maxChars) {
|
|
16263
|
+
const state = getSessionState(sessionID);
|
|
16264
|
+
if (!state || state.compactionCount === 0) {
|
|
16265
|
+
return `\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650`;
|
|
16266
|
+
}
|
|
16267
|
+
return `\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650\uFF1B\u5F53\u524D\u4F1A\u8BDD\u5DF2\u538B\u7F29 ${state.compactionCount} \u6B21\uFF0C\u5DF2\u81EA\u52A8\u6536\u7D27\u5DE5\u5177\u8F93\u51FA\u9884\u7B97`;
|
|
16268
|
+
}
|
|
16269
|
+
function getMaxOutputChars(sessionID, fallback = FALLBACK_MAX_OUTPUT_CHARS) {
|
|
16270
|
+
const state = getSessionState(sessionID);
|
|
16271
|
+
const base = getBaseMaxOutputChars(state?.contextTokens ?? null, fallback);
|
|
16272
|
+
const pressured = Math.round(base * getCompactionPressureFactor(state?.compactionCount ?? 0));
|
|
16273
|
+
return Math.max(MIN_PRESSURED_OUTPUT_CHARS, pressured);
|
|
16274
|
+
}
|
|
16275
|
+
function truncateToolOutput(content, sessionID, fallback = FALLBACK_MAX_OUTPUT_CHARS) {
|
|
16276
|
+
const maxChars = getMaxOutputChars(sessionID, fallback);
|
|
16277
|
+
if (content.length <= maxChars) return content;
|
|
16278
|
+
const truncated = content.slice(0, maxChars);
|
|
16279
|
+
return `${truncated}
|
|
16280
|
+
\x1B[2m... (${buildTruncationSuffix(sessionID, maxChars)})\x1B[0m`;
|
|
16281
|
+
}
|
|
16282
|
+
|
|
16241
16283
|
// src/lib/gbk-file.ts
|
|
16242
16284
|
var import_iconv_lite = __toESM(require_lib(), 1);
|
|
16243
16285
|
import crypto from "crypto";
|
|
@@ -17314,22 +17356,7 @@ async function replaceGbkFileText(input) {
|
|
|
17314
17356
|
};
|
|
17315
17357
|
}
|
|
17316
17358
|
|
|
17317
|
-
// src/lib/model-context.ts
|
|
17318
|
-
var _currentContextTokens = null;
|
|
17319
|
-
function getMaxOutputChars(fallback = 8e3) {
|
|
17320
|
-
if (_currentContextTokens === null) return fallback;
|
|
17321
|
-
const computed = Math.round(_currentContextTokens * 0.01 * 4);
|
|
17322
|
-
return Math.max(4e3, Math.min(32e3, computed));
|
|
17323
|
-
}
|
|
17324
|
-
|
|
17325
17359
|
// src/tools/gbk_edit.ts
|
|
17326
|
-
function truncateToolOutput(content) {
|
|
17327
|
-
const maxChars = getMaxOutputChars();
|
|
17328
|
-
if (content.length <= maxChars) return content;
|
|
17329
|
-
const truncated = content.slice(0, maxChars);
|
|
17330
|
-
return truncated + `
|
|
17331
|
-
\x1B[2m... (\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650)\x1B[0m`;
|
|
17332
|
-
}
|
|
17333
17360
|
var gbk_edit_default = tool({
|
|
17334
17361
|
description: `Edit GBK/GB18030 encoded text files with exact string replacement.
|
|
17335
17362
|
|
|
@@ -17399,8 +17426,8 @@ Insert mode:
|
|
|
17399
17426
|
diffPreview
|
|
17400
17427
|
}
|
|
17401
17428
|
});
|
|
17402
|
-
if (diffPreview) return truncateToolOutput(diffPreview);
|
|
17403
|
-
return truncateToolOutput(JSON.stringify(result, null, 2));
|
|
17429
|
+
if (diffPreview) return truncateToolOutput(diffPreview, context.sessionID);
|
|
17430
|
+
return truncateToolOutput(JSON.stringify(result, null, 2), context.sessionID);
|
|
17404
17431
|
}
|
|
17405
17432
|
});
|
|
17406
17433
|
export {
|
|
@@ -16238,6 +16238,48 @@ function tool(input) {
|
|
|
16238
16238
|
}
|
|
16239
16239
|
tool.schema = external_exports;
|
|
16240
16240
|
|
|
16241
|
+
// src/lib/model-context.ts
|
|
16242
|
+
var FALLBACK_MAX_OUTPUT_CHARS = 8e3;
|
|
16243
|
+
var MIN_BASE_MAX_OUTPUT_CHARS = 4e3;
|
|
16244
|
+
var MAX_BASE_OUTPUT_CHARS = 32e3;
|
|
16245
|
+
var MIN_PRESSURED_OUTPUT_CHARS = 1500;
|
|
16246
|
+
var sessionStates = /* @__PURE__ */ new Map();
|
|
16247
|
+
function getSessionState(sessionID) {
|
|
16248
|
+
if (!sessionID) return null;
|
|
16249
|
+
return sessionStates.get(sessionID) ?? null;
|
|
16250
|
+
}
|
|
16251
|
+
function getCompactionPressureFactor(compactionCount) {
|
|
16252
|
+
if (compactionCount >= 3) return 0.35;
|
|
16253
|
+
if (compactionCount === 2) return 0.5;
|
|
16254
|
+
if (compactionCount === 1) return 0.75;
|
|
16255
|
+
return 1;
|
|
16256
|
+
}
|
|
16257
|
+
function getBaseMaxOutputChars(contextTokens, fallback) {
|
|
16258
|
+
if (contextTokens === null) return fallback;
|
|
16259
|
+
const computed = Math.round(contextTokens * 0.01 * 4);
|
|
16260
|
+
return Math.max(MIN_BASE_MAX_OUTPUT_CHARS, Math.min(MAX_BASE_OUTPUT_CHARS, computed));
|
|
16261
|
+
}
|
|
16262
|
+
function buildTruncationSuffix(sessionID, maxChars) {
|
|
16263
|
+
const state = getSessionState(sessionID);
|
|
16264
|
+
if (!state || state.compactionCount === 0) {
|
|
16265
|
+
return `\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650`;
|
|
16266
|
+
}
|
|
16267
|
+
return `\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650\uFF1B\u5F53\u524D\u4F1A\u8BDD\u5DF2\u538B\u7F29 ${state.compactionCount} \u6B21\uFF0C\u5DF2\u81EA\u52A8\u6536\u7D27\u5DE5\u5177\u8F93\u51FA\u9884\u7B97`;
|
|
16268
|
+
}
|
|
16269
|
+
function getMaxOutputChars(sessionID, fallback = FALLBACK_MAX_OUTPUT_CHARS) {
|
|
16270
|
+
const state = getSessionState(sessionID);
|
|
16271
|
+
const base = getBaseMaxOutputChars(state?.contextTokens ?? null, fallback);
|
|
16272
|
+
const pressured = Math.round(base * getCompactionPressureFactor(state?.compactionCount ?? 0));
|
|
16273
|
+
return Math.max(MIN_PRESSURED_OUTPUT_CHARS, pressured);
|
|
16274
|
+
}
|
|
16275
|
+
function truncateToolOutput(content, sessionID, fallback = FALLBACK_MAX_OUTPUT_CHARS) {
|
|
16276
|
+
const maxChars = getMaxOutputChars(sessionID, fallback);
|
|
16277
|
+
if (content.length <= maxChars) return content;
|
|
16278
|
+
const truncated = content.slice(0, maxChars);
|
|
16279
|
+
return `${truncated}
|
|
16280
|
+
\x1B[2m... (${buildTruncationSuffix(sessionID, maxChars)})\x1B[0m`;
|
|
16281
|
+
}
|
|
16282
|
+
|
|
16241
16283
|
// src/lib/text-file.ts
|
|
16242
16284
|
var import_iconv_lite2 = __toESM(require_lib(), 1);
|
|
16243
16285
|
import crypto2 from "crypto";
|
|
@@ -16990,22 +17032,7 @@ async function replaceTextFileText(input) {
|
|
|
16990
17032
|
};
|
|
16991
17033
|
}
|
|
16992
17034
|
|
|
16993
|
-
// src/lib/model-context.ts
|
|
16994
|
-
var _currentContextTokens = null;
|
|
16995
|
-
function getMaxOutputChars(fallback = 8e3) {
|
|
16996
|
-
if (_currentContextTokens === null) return fallback;
|
|
16997
|
-
const computed = Math.round(_currentContextTokens * 0.01 * 4);
|
|
16998
|
-
return Math.max(4e3, Math.min(32e3, computed));
|
|
16999
|
-
}
|
|
17000
|
-
|
|
17001
17035
|
// src/tools/text_edit.ts
|
|
17002
|
-
function truncateToolOutput(content) {
|
|
17003
|
-
const maxChars = getMaxOutputChars();
|
|
17004
|
-
if (content.length <= maxChars) return content;
|
|
17005
|
-
const truncated = content.slice(0, maxChars);
|
|
17006
|
-
return truncated + `
|
|
17007
|
-
\x1B[2m... (\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650)\x1B[0m`;
|
|
17008
|
-
}
|
|
17009
17036
|
var text_edit_default = tool({
|
|
17010
17037
|
description: `Edit text files with automatic encoding detection and preservation.
|
|
17011
17038
|
|
|
@@ -17056,8 +17083,8 @@ var text_edit_default = tool({
|
|
|
17056
17083
|
diffPreview
|
|
17057
17084
|
}
|
|
17058
17085
|
});
|
|
17059
|
-
if (diffPreview) return truncateToolOutput(diffPreview);
|
|
17060
|
-
return truncateToolOutput(JSON.stringify(result, null, 2));
|
|
17086
|
+
if (diffPreview) return truncateToolOutput(diffPreview, context.sessionID);
|
|
17087
|
+
return truncateToolOutput(JSON.stringify(result, null, 2), context.sessionID);
|
|
17061
17088
|
}
|
|
17062
17089
|
});
|
|
17063
17090
|
export {
|
package/dist/plugin/index.js
CHANGED
|
@@ -3816,6 +3816,100 @@ var require_lib = __commonJS({
|
|
|
3816
3816
|
}
|
|
3817
3817
|
});
|
|
3818
3818
|
|
|
3819
|
+
// src/lib/session-pressure.ts
|
|
3820
|
+
var AUTO_SUMMARIZE_PRESSURE_RATIO = 0.85;
|
|
3821
|
+
var AUTO_SUMMARIZE_COOLDOWN_MS = 6e4;
|
|
3822
|
+
var PRESSURE_CHECK_INTERVAL_MS = 15e3;
|
|
3823
|
+
var SESSION_PRESSURE_MESSAGE_LIMIT = 200;
|
|
3824
|
+
function estimateUnknownChars(value) {
|
|
3825
|
+
if (typeof value === "string") return value.length;
|
|
3826
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value).length;
|
|
3827
|
+
if (Array.isArray(value)) {
|
|
3828
|
+
return value.reduce((total, item) => total + estimateUnknownChars(item), 0);
|
|
3829
|
+
}
|
|
3830
|
+
if (!value || typeof value !== "object") return 0;
|
|
3831
|
+
try {
|
|
3832
|
+
return JSON.stringify(value).length;
|
|
3833
|
+
} catch {
|
|
3834
|
+
return 0;
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
function estimateDiffChars(summary) {
|
|
3838
|
+
let chars = (summary.title?.length ?? 0) + (summary.body?.length ?? 0);
|
|
3839
|
+
for (const diff of summary.diffs ?? []) {
|
|
3840
|
+
chars += diff.file.length + diff.before.length + diff.after.length;
|
|
3841
|
+
}
|
|
3842
|
+
return chars;
|
|
3843
|
+
}
|
|
3844
|
+
function estimateToolPartChars(part) {
|
|
3845
|
+
let chars = part.tool.length + estimateUnknownChars(part.metadata);
|
|
3846
|
+
switch (part.state.status) {
|
|
3847
|
+
case "pending":
|
|
3848
|
+
chars += estimateUnknownChars(part.state.input) + part.state.raw.length;
|
|
3849
|
+
break;
|
|
3850
|
+
case "running":
|
|
3851
|
+
chars += estimateUnknownChars(part.state.input);
|
|
3852
|
+
chars += part.state.title?.length ?? 0;
|
|
3853
|
+
chars += estimateUnknownChars(part.state.metadata);
|
|
3854
|
+
break;
|
|
3855
|
+
case "completed":
|
|
3856
|
+
chars += estimateUnknownChars(part.state.input);
|
|
3857
|
+
chars += part.state.output.length + part.state.title.length;
|
|
3858
|
+
chars += estimateUnknownChars(part.state.metadata);
|
|
3859
|
+
break;
|
|
3860
|
+
case "error":
|
|
3861
|
+
chars += estimateUnknownChars(part.state.input);
|
|
3862
|
+
chars += part.state.error.length + estimateUnknownChars(part.state.metadata);
|
|
3863
|
+
break;
|
|
3864
|
+
}
|
|
3865
|
+
return chars;
|
|
3866
|
+
}
|
|
3867
|
+
function estimatePartChars(part) {
|
|
3868
|
+
switch (part.type) {
|
|
3869
|
+
case "text":
|
|
3870
|
+
return part.text.length;
|
|
3871
|
+
case "reasoning":
|
|
3872
|
+
return Math.round(part.text.length * 0.25);
|
|
3873
|
+
case "file":
|
|
3874
|
+
return (part.filename?.length ?? 0) + (part.source?.text.value.length ?? 0);
|
|
3875
|
+
case "tool":
|
|
3876
|
+
return estimateToolPartChars(part);
|
|
3877
|
+
case "step-start":
|
|
3878
|
+
return part.snapshot?.length ?? 0;
|
|
3879
|
+
case "step-finish":
|
|
3880
|
+
return part.reason.length + (part.snapshot?.length ?? 0);
|
|
3881
|
+
case "snapshot":
|
|
3882
|
+
return part.snapshot.length;
|
|
3883
|
+
case "patch":
|
|
3884
|
+
return part.hash.length + part.files.join("\n").length;
|
|
3885
|
+
case "agent":
|
|
3886
|
+
return part.name.length + (part.source?.value.length ?? 0);
|
|
3887
|
+
case "retry":
|
|
3888
|
+
return estimateUnknownChars(part.error);
|
|
3889
|
+
case "compaction":
|
|
3890
|
+
return 32;
|
|
3891
|
+
case "subtask":
|
|
3892
|
+
return part.prompt.length + part.description.length + part.agent.length;
|
|
3893
|
+
}
|
|
3894
|
+
}
|
|
3895
|
+
function estimateMessageChars(message, parts) {
|
|
3896
|
+
let chars = 0;
|
|
3897
|
+
if (message.role === "user") {
|
|
3898
|
+
chars += message.system?.length ?? 0;
|
|
3899
|
+
if (message.summary) {
|
|
3900
|
+
chars += estimateDiffChars(message.summary);
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
for (const part of parts) {
|
|
3904
|
+
chars += estimatePartChars(part);
|
|
3905
|
+
}
|
|
3906
|
+
return chars;
|
|
3907
|
+
}
|
|
3908
|
+
function estimateSessionTokens(messages) {
|
|
3909
|
+
const totalChars = messages.reduce((total, message) => total + estimateMessageChars(message.info, message.parts), 0);
|
|
3910
|
+
return Math.ceil(totalChars / 4);
|
|
3911
|
+
}
|
|
3912
|
+
|
|
3819
3913
|
// node_modules/zod/v4/classic/external.js
|
|
3820
3914
|
var external_exports = {};
|
|
3821
3915
|
__export(external_exports, {
|
|
@@ -16238,6 +16332,132 @@ function tool(input) {
|
|
|
16238
16332
|
}
|
|
16239
16333
|
tool.schema = external_exports;
|
|
16240
16334
|
|
|
16335
|
+
// src/lib/model-context.ts
|
|
16336
|
+
var FALLBACK_MAX_OUTPUT_CHARS = 8e3;
|
|
16337
|
+
var MIN_BASE_MAX_OUTPUT_CHARS = 4e3;
|
|
16338
|
+
var MAX_BASE_OUTPUT_CHARS = 32e3;
|
|
16339
|
+
var MIN_PRESSURED_OUTPUT_CHARS = 1500;
|
|
16340
|
+
var sessionStates = /* @__PURE__ */ new Map();
|
|
16341
|
+
function getOrCreateSessionState(sessionID) {
|
|
16342
|
+
let state = sessionStates.get(sessionID);
|
|
16343
|
+
if (!state) {
|
|
16344
|
+
state = {
|
|
16345
|
+
contextTokens: null,
|
|
16346
|
+
compactionCount: 0,
|
|
16347
|
+
estimatedTokens: null,
|
|
16348
|
+
pressureRatio: null,
|
|
16349
|
+
lastPressureCheckedAt: null,
|
|
16350
|
+
autoSummarizeInFlight: false,
|
|
16351
|
+
lastAutoSummarizeAt: null,
|
|
16352
|
+
autoSummarizeCount: 0
|
|
16353
|
+
};
|
|
16354
|
+
sessionStates.set(sessionID, state);
|
|
16355
|
+
}
|
|
16356
|
+
return state;
|
|
16357
|
+
}
|
|
16358
|
+
function getSessionState(sessionID) {
|
|
16359
|
+
if (!sessionID) return null;
|
|
16360
|
+
return sessionStates.get(sessionID) ?? null;
|
|
16361
|
+
}
|
|
16362
|
+
function getCompactionPressureFactor(compactionCount) {
|
|
16363
|
+
if (compactionCount >= 3) return 0.35;
|
|
16364
|
+
if (compactionCount === 2) return 0.5;
|
|
16365
|
+
if (compactionCount === 1) return 0.75;
|
|
16366
|
+
return 1;
|
|
16367
|
+
}
|
|
16368
|
+
function getBaseMaxOutputChars(contextTokens, fallback) {
|
|
16369
|
+
if (contextTokens === null) return fallback;
|
|
16370
|
+
const computed = Math.round(contextTokens * 0.01 * 4);
|
|
16371
|
+
return Math.max(MIN_BASE_MAX_OUTPUT_CHARS, Math.min(MAX_BASE_OUTPUT_CHARS, computed));
|
|
16372
|
+
}
|
|
16373
|
+
function buildTruncationSuffix(sessionID, maxChars) {
|
|
16374
|
+
const state = getSessionState(sessionID);
|
|
16375
|
+
if (!state || state.compactionCount === 0) {
|
|
16376
|
+
return `\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650`;
|
|
16377
|
+
}
|
|
16378
|
+
return `\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650\uFF1B\u5F53\u524D\u4F1A\u8BDD\u5DF2\u538B\u7F29 ${state.compactionCount} \u6B21\uFF0C\u5DF2\u81EA\u52A8\u6536\u7D27\u5DE5\u5177\u8F93\u51FA\u9884\u7B97`;
|
|
16379
|
+
}
|
|
16380
|
+
function setCurrentContextTokens(sessionID, tokens) {
|
|
16381
|
+
if (!sessionID || tokens <= 0) return;
|
|
16382
|
+
const state = getOrCreateSessionState(sessionID);
|
|
16383
|
+
state.contextTokens = tokens;
|
|
16384
|
+
}
|
|
16385
|
+
function markSessionCompacted(sessionID) {
|
|
16386
|
+
if (!sessionID) return;
|
|
16387
|
+
const state = getOrCreateSessionState(sessionID);
|
|
16388
|
+
state.compactionCount += 1;
|
|
16389
|
+
state.estimatedTokens = null;
|
|
16390
|
+
state.pressureRatio = null;
|
|
16391
|
+
state.lastPressureCheckedAt = null;
|
|
16392
|
+
}
|
|
16393
|
+
function getSessionCompactionCount(sessionID) {
|
|
16394
|
+
return getSessionState(sessionID)?.compactionCount ?? 0;
|
|
16395
|
+
}
|
|
16396
|
+
function updateSessionPressure(sessionID, estimatedTokens, pressureRatio, checkedAt = Date.now()) {
|
|
16397
|
+
if (!sessionID) return;
|
|
16398
|
+
const state = getOrCreateSessionState(sessionID);
|
|
16399
|
+
state.estimatedTokens = estimatedTokens;
|
|
16400
|
+
state.pressureRatio = pressureRatio;
|
|
16401
|
+
state.lastPressureCheckedAt = checkedAt;
|
|
16402
|
+
}
|
|
16403
|
+
function clearSessionPressure(sessionID) {
|
|
16404
|
+
if (!sessionID) return;
|
|
16405
|
+
const state = getOrCreateSessionState(sessionID);
|
|
16406
|
+
state.estimatedTokens = null;
|
|
16407
|
+
state.pressureRatio = null;
|
|
16408
|
+
state.lastPressureCheckedAt = null;
|
|
16409
|
+
}
|
|
16410
|
+
function getSessionPressure(sessionID) {
|
|
16411
|
+
const state = getSessionState(sessionID);
|
|
16412
|
+
if (!state || state.estimatedTokens === null || state.pressureRatio === null || state.lastPressureCheckedAt === null) {
|
|
16413
|
+
return null;
|
|
16414
|
+
}
|
|
16415
|
+
return {
|
|
16416
|
+
estimatedTokens: state.estimatedTokens,
|
|
16417
|
+
pressureRatio: state.pressureRatio,
|
|
16418
|
+
checkedAt: state.lastPressureCheckedAt
|
|
16419
|
+
};
|
|
16420
|
+
}
|
|
16421
|
+
function isAutoSummarizeInFlight(sessionID) {
|
|
16422
|
+
return getSessionState(sessionID)?.autoSummarizeInFlight ?? false;
|
|
16423
|
+
}
|
|
16424
|
+
function markAutoSummarizeStarted(sessionID) {
|
|
16425
|
+
if (!sessionID) return;
|
|
16426
|
+
const state = getOrCreateSessionState(sessionID);
|
|
16427
|
+
state.autoSummarizeInFlight = true;
|
|
16428
|
+
}
|
|
16429
|
+
function markAutoSummarizeFinished(sessionID, summarized, finishedAt = Date.now()) {
|
|
16430
|
+
if (!sessionID) return;
|
|
16431
|
+
const state = getOrCreateSessionState(sessionID);
|
|
16432
|
+
state.autoSummarizeInFlight = false;
|
|
16433
|
+
if (summarized) {
|
|
16434
|
+
state.lastAutoSummarizeAt = finishedAt;
|
|
16435
|
+
state.autoSummarizeCount += 1;
|
|
16436
|
+
state.estimatedTokens = null;
|
|
16437
|
+
state.pressureRatio = null;
|
|
16438
|
+
state.lastPressureCheckedAt = null;
|
|
16439
|
+
}
|
|
16440
|
+
}
|
|
16441
|
+
function getLastAutoSummarizeAt(sessionID) {
|
|
16442
|
+
return getSessionState(sessionID)?.lastAutoSummarizeAt ?? null;
|
|
16443
|
+
}
|
|
16444
|
+
function getAutoSummarizeCount(sessionID) {
|
|
16445
|
+
return getSessionState(sessionID)?.autoSummarizeCount ?? 0;
|
|
16446
|
+
}
|
|
16447
|
+
function getMaxOutputChars(sessionID, fallback = FALLBACK_MAX_OUTPUT_CHARS) {
|
|
16448
|
+
const state = getSessionState(sessionID);
|
|
16449
|
+
const base = getBaseMaxOutputChars(state?.contextTokens ?? null, fallback);
|
|
16450
|
+
const pressured = Math.round(base * getCompactionPressureFactor(state?.compactionCount ?? 0));
|
|
16451
|
+
return Math.max(MIN_PRESSURED_OUTPUT_CHARS, pressured);
|
|
16452
|
+
}
|
|
16453
|
+
function truncateToolOutput(content, sessionID, fallback = FALLBACK_MAX_OUTPUT_CHARS) {
|
|
16454
|
+
const maxChars = getMaxOutputChars(sessionID, fallback);
|
|
16455
|
+
if (content.length <= maxChars) return content;
|
|
16456
|
+
const truncated = content.slice(0, maxChars);
|
|
16457
|
+
return `${truncated}
|
|
16458
|
+
\x1B[2m... (${buildTruncationSuffix(sessionID, maxChars)})\x1B[0m`;
|
|
16459
|
+
}
|
|
16460
|
+
|
|
16241
16461
|
// src/lib/gbk-file.ts
|
|
16242
16462
|
var import_iconv_lite = __toESM(require_lib(), 1);
|
|
16243
16463
|
import crypto from "crypto";
|
|
@@ -17610,25 +17830,7 @@ async function writeGbkFile(input) {
|
|
|
17610
17830
|
}
|
|
17611
17831
|
}
|
|
17612
17832
|
|
|
17613
|
-
// src/lib/model-context.ts
|
|
17614
|
-
var _currentContextTokens = null;
|
|
17615
|
-
function setCurrentContextTokens(tokens) {
|
|
17616
|
-
_currentContextTokens = tokens;
|
|
17617
|
-
}
|
|
17618
|
-
function getMaxOutputChars(fallback = 8e3) {
|
|
17619
|
-
if (_currentContextTokens === null) return fallback;
|
|
17620
|
-
const computed = Math.round(_currentContextTokens * 0.01 * 4);
|
|
17621
|
-
return Math.max(4e3, Math.min(32e3, computed));
|
|
17622
|
-
}
|
|
17623
|
-
|
|
17624
17833
|
// src/tools/gbk_edit.ts
|
|
17625
|
-
function truncateToolOutput(content) {
|
|
17626
|
-
const maxChars = getMaxOutputChars();
|
|
17627
|
-
if (content.length <= maxChars) return content;
|
|
17628
|
-
const truncated = content.slice(0, maxChars);
|
|
17629
|
-
return truncated + `
|
|
17630
|
-
\x1B[2m... (\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650)\x1B[0m`;
|
|
17631
|
-
}
|
|
17632
17834
|
var gbk_edit_default = tool({
|
|
17633
17835
|
description: `Edit GBK/GB18030 encoded text files with exact string replacement.
|
|
17634
17836
|
|
|
@@ -17698,8 +17900,8 @@ Insert mode:
|
|
|
17698
17900
|
diffPreview
|
|
17699
17901
|
}
|
|
17700
17902
|
});
|
|
17701
|
-
if (diffPreview) return truncateToolOutput(diffPreview);
|
|
17702
|
-
return truncateToolOutput(JSON.stringify(result, null, 2));
|
|
17903
|
+
if (diffPreview) return truncateToolOutput(diffPreview, context.sessionID);
|
|
17904
|
+
return truncateToolOutput(JSON.stringify(result, null, 2), context.sessionID);
|
|
17703
17905
|
}
|
|
17704
17906
|
});
|
|
17705
17907
|
|
|
@@ -18765,13 +18967,6 @@ async function replaceTextFileText(input) {
|
|
|
18765
18967
|
}
|
|
18766
18968
|
|
|
18767
18969
|
// src/tools/text_edit.ts
|
|
18768
|
-
function truncateToolOutput2(content) {
|
|
18769
|
-
const maxChars = getMaxOutputChars();
|
|
18770
|
-
if (content.length <= maxChars) return content;
|
|
18771
|
-
const truncated = content.slice(0, maxChars);
|
|
18772
|
-
return truncated + `
|
|
18773
|
-
\x1B[2m... (\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650)\x1B[0m`;
|
|
18774
|
-
}
|
|
18775
18970
|
var text_edit_default = tool({
|
|
18776
18971
|
description: `Edit text files with automatic encoding detection and preservation.
|
|
18777
18972
|
|
|
@@ -18822,8 +19017,8 @@ var text_edit_default = tool({
|
|
|
18822
19017
|
diffPreview
|
|
18823
19018
|
}
|
|
18824
19019
|
});
|
|
18825
|
-
if (diffPreview) return
|
|
18826
|
-
return
|
|
19020
|
+
if (diffPreview) return truncateToolOutput(diffPreview, context.sessionID);
|
|
19021
|
+
return truncateToolOutput(JSON.stringify(result, null, 2), context.sessionID);
|
|
18827
19022
|
}
|
|
18828
19023
|
});
|
|
18829
19024
|
|
|
@@ -18904,7 +19099,66 @@ var text_write_default = tool({
|
|
|
18904
19099
|
});
|
|
18905
19100
|
|
|
18906
19101
|
// src/plugin/index.ts
|
|
18907
|
-
|
|
19102
|
+
var MANAGED_TOOL_IDS = /* @__PURE__ */ new Set([
|
|
19103
|
+
"gbk_read",
|
|
19104
|
+
"gbk_write",
|
|
19105
|
+
"gbk_edit",
|
|
19106
|
+
"gbk_search",
|
|
19107
|
+
"text_read",
|
|
19108
|
+
"text_write",
|
|
19109
|
+
"text_edit"
|
|
19110
|
+
]);
|
|
19111
|
+
function truncateMetadataPreview(value, sessionID) {
|
|
19112
|
+
const previewMaxChars = Math.max(800, Math.min(2e3, Math.floor(getMaxOutputChars(sessionID) / 2)));
|
|
19113
|
+
if (value.length <= previewMaxChars) return value;
|
|
19114
|
+
const truncated = value.slice(0, previewMaxChars);
|
|
19115
|
+
return `${truncated}
|
|
19116
|
+
\x1B[2m... (metadata diffPreview \u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${previewMaxChars} \u5B57\u7B26\u4E0A\u9650)\x1B[0m`;
|
|
19117
|
+
}
|
|
19118
|
+
async function maybeAutoSummarizeSession(client, directory, input) {
|
|
19119
|
+
if (!client?.session?.messages || !client.session.summarize) return;
|
|
19120
|
+
if (isAutoSummarizeInFlight(input.sessionID)) return;
|
|
19121
|
+
const contextTokens = input.model.limit?.context;
|
|
19122
|
+
if (typeof contextTokens !== "number" || contextTokens <= 0) return;
|
|
19123
|
+
const now = Date.now();
|
|
19124
|
+
const lastAutoSummarizeAt = getLastAutoSummarizeAt(input.sessionID);
|
|
19125
|
+
if (lastAutoSummarizeAt !== null && now - lastAutoSummarizeAt < AUTO_SUMMARIZE_COOLDOWN_MS) {
|
|
19126
|
+
return;
|
|
19127
|
+
}
|
|
19128
|
+
let pressure = getSessionPressure(input.sessionID);
|
|
19129
|
+
if (!pressure || now - pressure.checkedAt >= PRESSURE_CHECK_INTERVAL_MS) {
|
|
19130
|
+
const response = await client.session.messages({
|
|
19131
|
+
path: { id: input.sessionID },
|
|
19132
|
+
query: {
|
|
19133
|
+
directory,
|
|
19134
|
+
limit: SESSION_PRESSURE_MESSAGE_LIMIT
|
|
19135
|
+
},
|
|
19136
|
+
throwOnError: true
|
|
19137
|
+
});
|
|
19138
|
+
const estimatedTokens = estimateSessionTokens(Array.isArray(response.data) ? response.data : []);
|
|
19139
|
+
const pressureRatio = estimatedTokens / contextTokens;
|
|
19140
|
+
updateSessionPressure(input.sessionID, estimatedTokens, pressureRatio, now);
|
|
19141
|
+
pressure = getSessionPressure(input.sessionID);
|
|
19142
|
+
}
|
|
19143
|
+
if (!pressure || pressure.pressureRatio < AUTO_SUMMARIZE_PRESSURE_RATIO) return;
|
|
19144
|
+
markAutoSummarizeStarted(input.sessionID);
|
|
19145
|
+
try {
|
|
19146
|
+
await client.session.summarize({
|
|
19147
|
+
path: { id: input.sessionID },
|
|
19148
|
+
body: {
|
|
19149
|
+
providerID: input.model.providerID,
|
|
19150
|
+
modelID: input.model.id
|
|
19151
|
+
},
|
|
19152
|
+
query: { directory },
|
|
19153
|
+
throwOnError: true
|
|
19154
|
+
});
|
|
19155
|
+
clearSessionPressure(input.sessionID);
|
|
19156
|
+
markAutoSummarizeFinished(input.sessionID, true);
|
|
19157
|
+
} catch {
|
|
19158
|
+
markAutoSummarizeFinished(input.sessionID, false);
|
|
19159
|
+
}
|
|
19160
|
+
}
|
|
19161
|
+
function createOpencodeGbkHooks(client, directory) {
|
|
18908
19162
|
return {
|
|
18909
19163
|
tool: {
|
|
18910
19164
|
gbk_read: gbk_read_default,
|
|
@@ -18915,21 +19169,63 @@ function createOpencodeGbkHooks() {
|
|
|
18915
19169
|
text_write: text_write_default,
|
|
18916
19170
|
text_edit: text_edit_default
|
|
18917
19171
|
},
|
|
19172
|
+
async event(input) {
|
|
19173
|
+
if (input.event.type === "session.compacted") {
|
|
19174
|
+
const sessionID = input.event.properties.sessionID;
|
|
19175
|
+
if (sessionID) {
|
|
19176
|
+
markSessionCompacted(sessionID);
|
|
19177
|
+
}
|
|
19178
|
+
}
|
|
19179
|
+
},
|
|
18918
19180
|
async "chat.params"(input) {
|
|
18919
19181
|
const contextTokens = input.model?.limit?.context;
|
|
18920
19182
|
if (typeof contextTokens === "number" && contextTokens > 0) {
|
|
18921
|
-
setCurrentContextTokens(contextTokens);
|
|
19183
|
+
setCurrentContextTokens(input.sessionID, contextTokens);
|
|
19184
|
+
await maybeAutoSummarizeSession(client, directory, input);
|
|
18922
19185
|
}
|
|
18923
19186
|
},
|
|
19187
|
+
async "tool.execute.after"(input, output) {
|
|
19188
|
+
if (!MANAGED_TOOL_IDS.has(input.tool)) return;
|
|
19189
|
+
const maxOutputChars = getMaxOutputChars(input.sessionID);
|
|
19190
|
+
const compactionCount = getSessionCompactionCount(input.sessionID);
|
|
19191
|
+
const sessionPressure = getSessionPressure(input.sessionID);
|
|
19192
|
+
const autoSummarizeCount = getAutoSummarizeCount(input.sessionID);
|
|
19193
|
+
const metadata = output.metadata && typeof output.metadata === "object" ? { ...output.metadata } : {};
|
|
19194
|
+
const nextOutput = truncateToolOutput(output.output, input.sessionID);
|
|
19195
|
+
if (nextOutput !== output.output) {
|
|
19196
|
+
metadata.outputTruncated = true;
|
|
19197
|
+
output.output = nextOutput;
|
|
19198
|
+
}
|
|
19199
|
+
if (typeof metadata.diffPreview === "string") {
|
|
19200
|
+
metadata.diffPreview = truncateMetadataPreview(metadata.diffPreview, input.sessionID);
|
|
19201
|
+
}
|
|
19202
|
+
metadata.maxOutputChars = maxOutputChars;
|
|
19203
|
+
if (compactionCount > 0) {
|
|
19204
|
+
metadata.sessionCompactions = compactionCount;
|
|
19205
|
+
}
|
|
19206
|
+
if (sessionPressure) {
|
|
19207
|
+
metadata.estimatedSessionTokens = sessionPressure.estimatedTokens;
|
|
19208
|
+
metadata.sessionPressureRatio = Number(sessionPressure.pressureRatio.toFixed(3));
|
|
19209
|
+
}
|
|
19210
|
+
if (autoSummarizeCount > 0) {
|
|
19211
|
+
metadata.autoSummarizeCount = autoSummarizeCount;
|
|
19212
|
+
}
|
|
19213
|
+
output.metadata = metadata;
|
|
19214
|
+
},
|
|
18924
19215
|
async "experimental.chat.system.transform"(_input, output) {
|
|
18925
19216
|
appendTextToolSystemPrompt(output.system);
|
|
19217
|
+
},
|
|
19218
|
+
async "experimental.session.compacting"(_input, output) {
|
|
19219
|
+
output.context.push(
|
|
19220
|
+
"Aggressively compress prior tool outputs. Keep only unresolved tasks, final decisions, exact file paths or line ranges, and the smallest snippets needed to continue. Drop repeated raw file content, full JSON payloads, verbose logs, and duplicated diff previews."
|
|
19221
|
+
);
|
|
18926
19222
|
}
|
|
18927
19223
|
};
|
|
18928
19224
|
}
|
|
18929
19225
|
var pluginModule = {
|
|
18930
19226
|
id: "opencode-gbk-tools",
|
|
18931
|
-
async server() {
|
|
18932
|
-
return createOpencodeGbkHooks();
|
|
19227
|
+
async server(ctx) {
|
|
19228
|
+
return createOpencodeGbkHooks(ctx.client, ctx.directory);
|
|
18933
19229
|
}
|
|
18934
19230
|
};
|
|
18935
19231
|
var plugin_default = pluginModule;
|
|
@@ -3816,6 +3816,100 @@ var require_lib = __commonJS({
|
|
|
3816
3816
|
}
|
|
3817
3817
|
});
|
|
3818
3818
|
|
|
3819
|
+
// src/lib/session-pressure.ts
|
|
3820
|
+
var AUTO_SUMMARIZE_PRESSURE_RATIO = 0.85;
|
|
3821
|
+
var AUTO_SUMMARIZE_COOLDOWN_MS = 6e4;
|
|
3822
|
+
var PRESSURE_CHECK_INTERVAL_MS = 15e3;
|
|
3823
|
+
var SESSION_PRESSURE_MESSAGE_LIMIT = 200;
|
|
3824
|
+
function estimateUnknownChars(value) {
|
|
3825
|
+
if (typeof value === "string") return value.length;
|
|
3826
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value).length;
|
|
3827
|
+
if (Array.isArray(value)) {
|
|
3828
|
+
return value.reduce((total, item) => total + estimateUnknownChars(item), 0);
|
|
3829
|
+
}
|
|
3830
|
+
if (!value || typeof value !== "object") return 0;
|
|
3831
|
+
try {
|
|
3832
|
+
return JSON.stringify(value).length;
|
|
3833
|
+
} catch {
|
|
3834
|
+
return 0;
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
function estimateDiffChars(summary) {
|
|
3838
|
+
let chars = (summary.title?.length ?? 0) + (summary.body?.length ?? 0);
|
|
3839
|
+
for (const diff of summary.diffs ?? []) {
|
|
3840
|
+
chars += diff.file.length + diff.before.length + diff.after.length;
|
|
3841
|
+
}
|
|
3842
|
+
return chars;
|
|
3843
|
+
}
|
|
3844
|
+
function estimateToolPartChars(part) {
|
|
3845
|
+
let chars = part.tool.length + estimateUnknownChars(part.metadata);
|
|
3846
|
+
switch (part.state.status) {
|
|
3847
|
+
case "pending":
|
|
3848
|
+
chars += estimateUnknownChars(part.state.input) + part.state.raw.length;
|
|
3849
|
+
break;
|
|
3850
|
+
case "running":
|
|
3851
|
+
chars += estimateUnknownChars(part.state.input);
|
|
3852
|
+
chars += part.state.title?.length ?? 0;
|
|
3853
|
+
chars += estimateUnknownChars(part.state.metadata);
|
|
3854
|
+
break;
|
|
3855
|
+
case "completed":
|
|
3856
|
+
chars += estimateUnknownChars(part.state.input);
|
|
3857
|
+
chars += part.state.output.length + part.state.title.length;
|
|
3858
|
+
chars += estimateUnknownChars(part.state.metadata);
|
|
3859
|
+
break;
|
|
3860
|
+
case "error":
|
|
3861
|
+
chars += estimateUnknownChars(part.state.input);
|
|
3862
|
+
chars += part.state.error.length + estimateUnknownChars(part.state.metadata);
|
|
3863
|
+
break;
|
|
3864
|
+
}
|
|
3865
|
+
return chars;
|
|
3866
|
+
}
|
|
3867
|
+
function estimatePartChars(part) {
|
|
3868
|
+
switch (part.type) {
|
|
3869
|
+
case "text":
|
|
3870
|
+
return part.text.length;
|
|
3871
|
+
case "reasoning":
|
|
3872
|
+
return Math.round(part.text.length * 0.25);
|
|
3873
|
+
case "file":
|
|
3874
|
+
return (part.filename?.length ?? 0) + (part.source?.text.value.length ?? 0);
|
|
3875
|
+
case "tool":
|
|
3876
|
+
return estimateToolPartChars(part);
|
|
3877
|
+
case "step-start":
|
|
3878
|
+
return part.snapshot?.length ?? 0;
|
|
3879
|
+
case "step-finish":
|
|
3880
|
+
return part.reason.length + (part.snapshot?.length ?? 0);
|
|
3881
|
+
case "snapshot":
|
|
3882
|
+
return part.snapshot.length;
|
|
3883
|
+
case "patch":
|
|
3884
|
+
return part.hash.length + part.files.join("\n").length;
|
|
3885
|
+
case "agent":
|
|
3886
|
+
return part.name.length + (part.source?.value.length ?? 0);
|
|
3887
|
+
case "retry":
|
|
3888
|
+
return estimateUnknownChars(part.error);
|
|
3889
|
+
case "compaction":
|
|
3890
|
+
return 32;
|
|
3891
|
+
case "subtask":
|
|
3892
|
+
return part.prompt.length + part.description.length + part.agent.length;
|
|
3893
|
+
}
|
|
3894
|
+
}
|
|
3895
|
+
function estimateMessageChars(message, parts) {
|
|
3896
|
+
let chars = 0;
|
|
3897
|
+
if (message.role === "user") {
|
|
3898
|
+
chars += message.system?.length ?? 0;
|
|
3899
|
+
if (message.summary) {
|
|
3900
|
+
chars += estimateDiffChars(message.summary);
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
for (const part of parts) {
|
|
3904
|
+
chars += estimatePartChars(part);
|
|
3905
|
+
}
|
|
3906
|
+
return chars;
|
|
3907
|
+
}
|
|
3908
|
+
function estimateSessionTokens(messages) {
|
|
3909
|
+
const totalChars = messages.reduce((total, message) => total + estimateMessageChars(message.info, message.parts), 0);
|
|
3910
|
+
return Math.ceil(totalChars / 4);
|
|
3911
|
+
}
|
|
3912
|
+
|
|
3819
3913
|
// node_modules/zod/v4/classic/external.js
|
|
3820
3914
|
var external_exports = {};
|
|
3821
3915
|
__export(external_exports, {
|
|
@@ -16238,6 +16332,132 @@ function tool(input) {
|
|
|
16238
16332
|
}
|
|
16239
16333
|
tool.schema = external_exports;
|
|
16240
16334
|
|
|
16335
|
+
// src/lib/model-context.ts
|
|
16336
|
+
var FALLBACK_MAX_OUTPUT_CHARS = 8e3;
|
|
16337
|
+
var MIN_BASE_MAX_OUTPUT_CHARS = 4e3;
|
|
16338
|
+
var MAX_BASE_OUTPUT_CHARS = 32e3;
|
|
16339
|
+
var MIN_PRESSURED_OUTPUT_CHARS = 1500;
|
|
16340
|
+
var sessionStates = /* @__PURE__ */ new Map();
|
|
16341
|
+
function getOrCreateSessionState(sessionID) {
|
|
16342
|
+
let state = sessionStates.get(sessionID);
|
|
16343
|
+
if (!state) {
|
|
16344
|
+
state = {
|
|
16345
|
+
contextTokens: null,
|
|
16346
|
+
compactionCount: 0,
|
|
16347
|
+
estimatedTokens: null,
|
|
16348
|
+
pressureRatio: null,
|
|
16349
|
+
lastPressureCheckedAt: null,
|
|
16350
|
+
autoSummarizeInFlight: false,
|
|
16351
|
+
lastAutoSummarizeAt: null,
|
|
16352
|
+
autoSummarizeCount: 0
|
|
16353
|
+
};
|
|
16354
|
+
sessionStates.set(sessionID, state);
|
|
16355
|
+
}
|
|
16356
|
+
return state;
|
|
16357
|
+
}
|
|
16358
|
+
function getSessionState(sessionID) {
|
|
16359
|
+
if (!sessionID) return null;
|
|
16360
|
+
return sessionStates.get(sessionID) ?? null;
|
|
16361
|
+
}
|
|
16362
|
+
function getCompactionPressureFactor(compactionCount) {
|
|
16363
|
+
if (compactionCount >= 3) return 0.35;
|
|
16364
|
+
if (compactionCount === 2) return 0.5;
|
|
16365
|
+
if (compactionCount === 1) return 0.75;
|
|
16366
|
+
return 1;
|
|
16367
|
+
}
|
|
16368
|
+
function getBaseMaxOutputChars(contextTokens, fallback) {
|
|
16369
|
+
if (contextTokens === null) return fallback;
|
|
16370
|
+
const computed = Math.round(contextTokens * 0.01 * 4);
|
|
16371
|
+
return Math.max(MIN_BASE_MAX_OUTPUT_CHARS, Math.min(MAX_BASE_OUTPUT_CHARS, computed));
|
|
16372
|
+
}
|
|
16373
|
+
function buildTruncationSuffix(sessionID, maxChars) {
|
|
16374
|
+
const state = getSessionState(sessionID);
|
|
16375
|
+
if (!state || state.compactionCount === 0) {
|
|
16376
|
+
return `\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650`;
|
|
16377
|
+
}
|
|
16378
|
+
return `\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650\uFF1B\u5F53\u524D\u4F1A\u8BDD\u5DF2\u538B\u7F29 ${state.compactionCount} \u6B21\uFF0C\u5DF2\u81EA\u52A8\u6536\u7D27\u5DE5\u5177\u8F93\u51FA\u9884\u7B97`;
|
|
16379
|
+
}
|
|
16380
|
+
function setCurrentContextTokens(sessionID, tokens) {
|
|
16381
|
+
if (!sessionID || tokens <= 0) return;
|
|
16382
|
+
const state = getOrCreateSessionState(sessionID);
|
|
16383
|
+
state.contextTokens = tokens;
|
|
16384
|
+
}
|
|
16385
|
+
function markSessionCompacted(sessionID) {
|
|
16386
|
+
if (!sessionID) return;
|
|
16387
|
+
const state = getOrCreateSessionState(sessionID);
|
|
16388
|
+
state.compactionCount += 1;
|
|
16389
|
+
state.estimatedTokens = null;
|
|
16390
|
+
state.pressureRatio = null;
|
|
16391
|
+
state.lastPressureCheckedAt = null;
|
|
16392
|
+
}
|
|
16393
|
+
function getSessionCompactionCount(sessionID) {
|
|
16394
|
+
return getSessionState(sessionID)?.compactionCount ?? 0;
|
|
16395
|
+
}
|
|
16396
|
+
function updateSessionPressure(sessionID, estimatedTokens, pressureRatio, checkedAt = Date.now()) {
|
|
16397
|
+
if (!sessionID) return;
|
|
16398
|
+
const state = getOrCreateSessionState(sessionID);
|
|
16399
|
+
state.estimatedTokens = estimatedTokens;
|
|
16400
|
+
state.pressureRatio = pressureRatio;
|
|
16401
|
+
state.lastPressureCheckedAt = checkedAt;
|
|
16402
|
+
}
|
|
16403
|
+
function clearSessionPressure(sessionID) {
|
|
16404
|
+
if (!sessionID) return;
|
|
16405
|
+
const state = getOrCreateSessionState(sessionID);
|
|
16406
|
+
state.estimatedTokens = null;
|
|
16407
|
+
state.pressureRatio = null;
|
|
16408
|
+
state.lastPressureCheckedAt = null;
|
|
16409
|
+
}
|
|
16410
|
+
function getSessionPressure(sessionID) {
|
|
16411
|
+
const state = getSessionState(sessionID);
|
|
16412
|
+
if (!state || state.estimatedTokens === null || state.pressureRatio === null || state.lastPressureCheckedAt === null) {
|
|
16413
|
+
return null;
|
|
16414
|
+
}
|
|
16415
|
+
return {
|
|
16416
|
+
estimatedTokens: state.estimatedTokens,
|
|
16417
|
+
pressureRatio: state.pressureRatio,
|
|
16418
|
+
checkedAt: state.lastPressureCheckedAt
|
|
16419
|
+
};
|
|
16420
|
+
}
|
|
16421
|
+
function isAutoSummarizeInFlight(sessionID) {
|
|
16422
|
+
return getSessionState(sessionID)?.autoSummarizeInFlight ?? false;
|
|
16423
|
+
}
|
|
16424
|
+
function markAutoSummarizeStarted(sessionID) {
|
|
16425
|
+
if (!sessionID) return;
|
|
16426
|
+
const state = getOrCreateSessionState(sessionID);
|
|
16427
|
+
state.autoSummarizeInFlight = true;
|
|
16428
|
+
}
|
|
16429
|
+
function markAutoSummarizeFinished(sessionID, summarized, finishedAt = Date.now()) {
|
|
16430
|
+
if (!sessionID) return;
|
|
16431
|
+
const state = getOrCreateSessionState(sessionID);
|
|
16432
|
+
state.autoSummarizeInFlight = false;
|
|
16433
|
+
if (summarized) {
|
|
16434
|
+
state.lastAutoSummarizeAt = finishedAt;
|
|
16435
|
+
state.autoSummarizeCount += 1;
|
|
16436
|
+
state.estimatedTokens = null;
|
|
16437
|
+
state.pressureRatio = null;
|
|
16438
|
+
state.lastPressureCheckedAt = null;
|
|
16439
|
+
}
|
|
16440
|
+
}
|
|
16441
|
+
function getLastAutoSummarizeAt(sessionID) {
|
|
16442
|
+
return getSessionState(sessionID)?.lastAutoSummarizeAt ?? null;
|
|
16443
|
+
}
|
|
16444
|
+
function getAutoSummarizeCount(sessionID) {
|
|
16445
|
+
return getSessionState(sessionID)?.autoSummarizeCount ?? 0;
|
|
16446
|
+
}
|
|
16447
|
+
function getMaxOutputChars(sessionID, fallback = FALLBACK_MAX_OUTPUT_CHARS) {
|
|
16448
|
+
const state = getSessionState(sessionID);
|
|
16449
|
+
const base = getBaseMaxOutputChars(state?.contextTokens ?? null, fallback);
|
|
16450
|
+
const pressured = Math.round(base * getCompactionPressureFactor(state?.compactionCount ?? 0));
|
|
16451
|
+
return Math.max(MIN_PRESSURED_OUTPUT_CHARS, pressured);
|
|
16452
|
+
}
|
|
16453
|
+
function truncateToolOutput(content, sessionID, fallback = FALLBACK_MAX_OUTPUT_CHARS) {
|
|
16454
|
+
const maxChars = getMaxOutputChars(sessionID, fallback);
|
|
16455
|
+
if (content.length <= maxChars) return content;
|
|
16456
|
+
const truncated = content.slice(0, maxChars);
|
|
16457
|
+
return `${truncated}
|
|
16458
|
+
\x1B[2m... (${buildTruncationSuffix(sessionID, maxChars)})\x1B[0m`;
|
|
16459
|
+
}
|
|
16460
|
+
|
|
16241
16461
|
// src/lib/gbk-file.ts
|
|
16242
16462
|
var import_iconv_lite = __toESM(require_lib(), 1);
|
|
16243
16463
|
import crypto from "crypto";
|
|
@@ -17610,25 +17830,7 @@ async function writeGbkFile(input) {
|
|
|
17610
17830
|
}
|
|
17611
17831
|
}
|
|
17612
17832
|
|
|
17613
|
-
// src/lib/model-context.ts
|
|
17614
|
-
var _currentContextTokens = null;
|
|
17615
|
-
function setCurrentContextTokens(tokens) {
|
|
17616
|
-
_currentContextTokens = tokens;
|
|
17617
|
-
}
|
|
17618
|
-
function getMaxOutputChars(fallback = 8e3) {
|
|
17619
|
-
if (_currentContextTokens === null) return fallback;
|
|
17620
|
-
const computed = Math.round(_currentContextTokens * 0.01 * 4);
|
|
17621
|
-
return Math.max(4e3, Math.min(32e3, computed));
|
|
17622
|
-
}
|
|
17623
|
-
|
|
17624
17833
|
// src/tools/gbk_edit.ts
|
|
17625
|
-
function truncateToolOutput(content) {
|
|
17626
|
-
const maxChars = getMaxOutputChars();
|
|
17627
|
-
if (content.length <= maxChars) return content;
|
|
17628
|
-
const truncated = content.slice(0, maxChars);
|
|
17629
|
-
return truncated + `
|
|
17630
|
-
\x1B[2m... (\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650)\x1B[0m`;
|
|
17631
|
-
}
|
|
17632
17834
|
var gbk_edit_default = tool({
|
|
17633
17835
|
description: `Edit GBK/GB18030 encoded text files with exact string replacement.
|
|
17634
17836
|
|
|
@@ -17698,8 +17900,8 @@ Insert mode:
|
|
|
17698
17900
|
diffPreview
|
|
17699
17901
|
}
|
|
17700
17902
|
});
|
|
17701
|
-
if (diffPreview) return truncateToolOutput(diffPreview);
|
|
17702
|
-
return truncateToolOutput(JSON.stringify(result, null, 2));
|
|
17903
|
+
if (diffPreview) return truncateToolOutput(diffPreview, context.sessionID);
|
|
17904
|
+
return truncateToolOutput(JSON.stringify(result, null, 2), context.sessionID);
|
|
17703
17905
|
}
|
|
17704
17906
|
});
|
|
17705
17907
|
|
|
@@ -18765,13 +18967,6 @@ async function replaceTextFileText(input) {
|
|
|
18765
18967
|
}
|
|
18766
18968
|
|
|
18767
18969
|
// src/tools/text_edit.ts
|
|
18768
|
-
function truncateToolOutput2(content) {
|
|
18769
|
-
const maxChars = getMaxOutputChars();
|
|
18770
|
-
if (content.length <= maxChars) return content;
|
|
18771
|
-
const truncated = content.slice(0, maxChars);
|
|
18772
|
-
return truncated + `
|
|
18773
|
-
\x1B[2m... (\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650)\x1B[0m`;
|
|
18774
|
-
}
|
|
18775
18970
|
var text_edit_default = tool({
|
|
18776
18971
|
description: `Edit text files with automatic encoding detection and preservation.
|
|
18777
18972
|
|
|
@@ -18822,8 +19017,8 @@ var text_edit_default = tool({
|
|
|
18822
19017
|
diffPreview
|
|
18823
19018
|
}
|
|
18824
19019
|
});
|
|
18825
|
-
if (diffPreview) return
|
|
18826
|
-
return
|
|
19020
|
+
if (diffPreview) return truncateToolOutput(diffPreview, context.sessionID);
|
|
19021
|
+
return truncateToolOutput(JSON.stringify(result, null, 2), context.sessionID);
|
|
18827
19022
|
}
|
|
18828
19023
|
});
|
|
18829
19024
|
|
|
@@ -18904,7 +19099,66 @@ var text_write_default = tool({
|
|
|
18904
19099
|
});
|
|
18905
19100
|
|
|
18906
19101
|
// src/plugin/index.ts
|
|
18907
|
-
|
|
19102
|
+
var MANAGED_TOOL_IDS = /* @__PURE__ */ new Set([
|
|
19103
|
+
"gbk_read",
|
|
19104
|
+
"gbk_write",
|
|
19105
|
+
"gbk_edit",
|
|
19106
|
+
"gbk_search",
|
|
19107
|
+
"text_read",
|
|
19108
|
+
"text_write",
|
|
19109
|
+
"text_edit"
|
|
19110
|
+
]);
|
|
19111
|
+
function truncateMetadataPreview(value, sessionID) {
|
|
19112
|
+
const previewMaxChars = Math.max(800, Math.min(2e3, Math.floor(getMaxOutputChars(sessionID) / 2)));
|
|
19113
|
+
if (value.length <= previewMaxChars) return value;
|
|
19114
|
+
const truncated = value.slice(0, previewMaxChars);
|
|
19115
|
+
return `${truncated}
|
|
19116
|
+
\x1B[2m... (metadata diffPreview \u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${previewMaxChars} \u5B57\u7B26\u4E0A\u9650)\x1B[0m`;
|
|
19117
|
+
}
|
|
19118
|
+
async function maybeAutoSummarizeSession(client, directory, input) {
|
|
19119
|
+
if (!client?.session?.messages || !client.session.summarize) return;
|
|
19120
|
+
if (isAutoSummarizeInFlight(input.sessionID)) return;
|
|
19121
|
+
const contextTokens = input.model.limit?.context;
|
|
19122
|
+
if (typeof contextTokens !== "number" || contextTokens <= 0) return;
|
|
19123
|
+
const now = Date.now();
|
|
19124
|
+
const lastAutoSummarizeAt = getLastAutoSummarizeAt(input.sessionID);
|
|
19125
|
+
if (lastAutoSummarizeAt !== null && now - lastAutoSummarizeAt < AUTO_SUMMARIZE_COOLDOWN_MS) {
|
|
19126
|
+
return;
|
|
19127
|
+
}
|
|
19128
|
+
let pressure = getSessionPressure(input.sessionID);
|
|
19129
|
+
if (!pressure || now - pressure.checkedAt >= PRESSURE_CHECK_INTERVAL_MS) {
|
|
19130
|
+
const response = await client.session.messages({
|
|
19131
|
+
path: { id: input.sessionID },
|
|
19132
|
+
query: {
|
|
19133
|
+
directory,
|
|
19134
|
+
limit: SESSION_PRESSURE_MESSAGE_LIMIT
|
|
19135
|
+
},
|
|
19136
|
+
throwOnError: true
|
|
19137
|
+
});
|
|
19138
|
+
const estimatedTokens = estimateSessionTokens(Array.isArray(response.data) ? response.data : []);
|
|
19139
|
+
const pressureRatio = estimatedTokens / contextTokens;
|
|
19140
|
+
updateSessionPressure(input.sessionID, estimatedTokens, pressureRatio, now);
|
|
19141
|
+
pressure = getSessionPressure(input.sessionID);
|
|
19142
|
+
}
|
|
19143
|
+
if (!pressure || pressure.pressureRatio < AUTO_SUMMARIZE_PRESSURE_RATIO) return;
|
|
19144
|
+
markAutoSummarizeStarted(input.sessionID);
|
|
19145
|
+
try {
|
|
19146
|
+
await client.session.summarize({
|
|
19147
|
+
path: { id: input.sessionID },
|
|
19148
|
+
body: {
|
|
19149
|
+
providerID: input.model.providerID,
|
|
19150
|
+
modelID: input.model.id
|
|
19151
|
+
},
|
|
19152
|
+
query: { directory },
|
|
19153
|
+
throwOnError: true
|
|
19154
|
+
});
|
|
19155
|
+
clearSessionPressure(input.sessionID);
|
|
19156
|
+
markAutoSummarizeFinished(input.sessionID, true);
|
|
19157
|
+
} catch {
|
|
19158
|
+
markAutoSummarizeFinished(input.sessionID, false);
|
|
19159
|
+
}
|
|
19160
|
+
}
|
|
19161
|
+
function createOpencodeGbkHooks(client, directory) {
|
|
18908
19162
|
return {
|
|
18909
19163
|
tool: {
|
|
18910
19164
|
gbk_read: gbk_read_default,
|
|
@@ -18915,21 +19169,63 @@ function createOpencodeGbkHooks() {
|
|
|
18915
19169
|
text_write: text_write_default,
|
|
18916
19170
|
text_edit: text_edit_default
|
|
18917
19171
|
},
|
|
19172
|
+
async event(input) {
|
|
19173
|
+
if (input.event.type === "session.compacted") {
|
|
19174
|
+
const sessionID = input.event.properties.sessionID;
|
|
19175
|
+
if (sessionID) {
|
|
19176
|
+
markSessionCompacted(sessionID);
|
|
19177
|
+
}
|
|
19178
|
+
}
|
|
19179
|
+
},
|
|
18918
19180
|
async "chat.params"(input) {
|
|
18919
19181
|
const contextTokens = input.model?.limit?.context;
|
|
18920
19182
|
if (typeof contextTokens === "number" && contextTokens > 0) {
|
|
18921
|
-
setCurrentContextTokens(contextTokens);
|
|
19183
|
+
setCurrentContextTokens(input.sessionID, contextTokens);
|
|
19184
|
+
await maybeAutoSummarizeSession(client, directory, input);
|
|
18922
19185
|
}
|
|
18923
19186
|
},
|
|
19187
|
+
async "tool.execute.after"(input, output) {
|
|
19188
|
+
if (!MANAGED_TOOL_IDS.has(input.tool)) return;
|
|
19189
|
+
const maxOutputChars = getMaxOutputChars(input.sessionID);
|
|
19190
|
+
const compactionCount = getSessionCompactionCount(input.sessionID);
|
|
19191
|
+
const sessionPressure = getSessionPressure(input.sessionID);
|
|
19192
|
+
const autoSummarizeCount = getAutoSummarizeCount(input.sessionID);
|
|
19193
|
+
const metadata = output.metadata && typeof output.metadata === "object" ? { ...output.metadata } : {};
|
|
19194
|
+
const nextOutput = truncateToolOutput(output.output, input.sessionID);
|
|
19195
|
+
if (nextOutput !== output.output) {
|
|
19196
|
+
metadata.outputTruncated = true;
|
|
19197
|
+
output.output = nextOutput;
|
|
19198
|
+
}
|
|
19199
|
+
if (typeof metadata.diffPreview === "string") {
|
|
19200
|
+
metadata.diffPreview = truncateMetadataPreview(metadata.diffPreview, input.sessionID);
|
|
19201
|
+
}
|
|
19202
|
+
metadata.maxOutputChars = maxOutputChars;
|
|
19203
|
+
if (compactionCount > 0) {
|
|
19204
|
+
metadata.sessionCompactions = compactionCount;
|
|
19205
|
+
}
|
|
19206
|
+
if (sessionPressure) {
|
|
19207
|
+
metadata.estimatedSessionTokens = sessionPressure.estimatedTokens;
|
|
19208
|
+
metadata.sessionPressureRatio = Number(sessionPressure.pressureRatio.toFixed(3));
|
|
19209
|
+
}
|
|
19210
|
+
if (autoSummarizeCount > 0) {
|
|
19211
|
+
metadata.autoSummarizeCount = autoSummarizeCount;
|
|
19212
|
+
}
|
|
19213
|
+
output.metadata = metadata;
|
|
19214
|
+
},
|
|
18924
19215
|
async "experimental.chat.system.transform"(_input, output) {
|
|
18925
19216
|
appendTextToolSystemPrompt(output.system);
|
|
19217
|
+
},
|
|
19218
|
+
async "experimental.session.compacting"(_input, output) {
|
|
19219
|
+
output.context.push(
|
|
19220
|
+
"Aggressively compress prior tool outputs. Keep only unresolved tasks, final decisions, exact file paths or line ranges, and the smallest snippets needed to continue. Drop repeated raw file content, full JSON payloads, verbose logs, and duplicated diff previews."
|
|
19221
|
+
);
|
|
18926
19222
|
}
|
|
18927
19223
|
};
|
|
18928
19224
|
}
|
|
18929
19225
|
|
|
18930
19226
|
// src/local-plugin/opencode-gbk-tools.ts
|
|
18931
|
-
var OpencodeGbkToolsLocalPlugin = async () => {
|
|
18932
|
-
return createOpencodeGbkHooks();
|
|
19227
|
+
var OpencodeGbkToolsLocalPlugin = async (ctx) => {
|
|
19228
|
+
return createOpencodeGbkHooks(ctx.client, ctx.directory);
|
|
18933
19229
|
};
|
|
18934
19230
|
export {
|
|
18935
19231
|
OpencodeGbkToolsLocalPlugin
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifestVersion": 1,
|
|
3
3
|
"packageName": "opencode-gbk-tools",
|
|
4
|
-
"packageVersion": "0.1.
|
|
4
|
+
"packageVersion": "0.1.24",
|
|
5
5
|
"artifacts": [
|
|
6
6
|
{
|
|
7
7
|
"relativePath": "plugins/opencode-gbk-tools.js",
|
|
8
8
|
"kind": "plugin",
|
|
9
|
-
"expectedHash": "
|
|
9
|
+
"expectedHash": "55f2eae379bf3bd5a9129fbc413f255a4ffd9186d90a4fe26ecb3cc75b5a4c68",
|
|
10
10
|
"hashAlgorithm": "sha256"
|
|
11
11
|
}
|
|
12
12
|
]
|