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.
@@ -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 {
@@ -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 truncateToolOutput2(diffPreview);
18826
- return truncateToolOutput2(JSON.stringify(result, null, 2));
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
- function createOpencodeGbkHooks() {
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 truncateToolOutput2(diffPreview);
18826
- return truncateToolOutput2(JSON.stringify(result, null, 2));
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
- function createOpencodeGbkHooks() {
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.23",
4
+ "packageVersion": "0.1.24",
5
5
  "artifacts": [
6
6
  {
7
7
  "relativePath": "plugins/opencode-gbk-tools.js",
8
8
  "kind": "plugin",
9
- "expectedHash": "28e00665097934ea390c85f9a991f9074f77d31ff7ffe6f118035e834b911d2d",
9
+ "expectedHash": "55f2eae379bf3bd5a9129fbc413f255a4ffd9186d90a4fe26ecb3cc75b5a4c68",
10
10
  "hashAlgorithm": "sha256"
11
11
  }
12
12
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-gbk-tools",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "description": "Auto-encoding text tools plus GBK/GB18030 tools for OpenCode",
5
5
  "type": "module",
6
6
  "main": "./dist/plugin/index.js",