opencode-acp 1.12.10 → 1.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +29 -0
- package/README.md +116 -36
- package/README.zh-CN.md +94 -20
- package/dist/index.js +878 -44
- package/dist/index.js.map +1 -1
- package/dist/lib/compress/pipeline.d.ts.map +1 -1
- package/dist/lib/compress/quality-gate/algorithms/index.d.ts +3 -0
- package/dist/lib/compress/quality-gate/algorithms/index.d.ts.map +1 -0
- package/dist/lib/compress/quality-gate/evaluate.d.ts +8 -0
- package/dist/lib/compress/quality-gate/evaluate.d.ts.map +1 -0
- package/dist/lib/compress/quality-gate/index.d.ts +5 -0
- package/dist/lib/compress/quality-gate/index.d.ts.map +1 -0
- package/dist/lib/compress/quality-gate/registry.d.ts +6 -0
- package/dist/lib/compress/quality-gate/registry.d.ts.map +1 -0
- package/dist/lib/compress/quality-gate/types.d.ts +51 -0
- package/dist/lib/compress/quality-gate/types.d.ts.map +1 -0
- package/dist/lib/config-validation.d.ts.map +1 -1
- package/dist/lib/config.d.ts +9 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/messages/inject/policy/index.d.ts +5 -0
- package/dist/lib/messages/inject/policy/index.d.ts.map +1 -0
- package/dist/lib/messages/inject/policy/registry.d.ts +8 -0
- package/dist/lib/messages/inject/policy/registry.d.ts.map +1 -0
- package/dist/lib/messages/inject/policy/types.d.ts +2 -0
- package/dist/lib/messages/inject/policy/types.d.ts.map +1 -0
- package/dist/lib/messages/inject/utils.d.ts +0 -9
- package/dist/lib/messages/inject/utils.d.ts.map +1 -1
- package/dist/lib/messages/utils.d.ts.map +1 -1
- package/dist/lib/ui/notification.d.ts.map +1 -1
- package/package.json +5 -2
- package/dist/lib/prompts/compression-rules.d.ts +0 -20
- package/dist/lib/prompts/compression-rules.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -926,7 +926,11 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
926
926
|
"strategies.purgeErrors",
|
|
927
927
|
"strategies.purgeErrors.enabled",
|
|
928
928
|
"strategies.purgeErrors.turns",
|
|
929
|
-
"strategies.purgeErrors.protectedTools"
|
|
929
|
+
"strategies.purgeErrors.protectedTools",
|
|
930
|
+
"qualityGate",
|
|
931
|
+
"qualityGate.enabled",
|
|
932
|
+
"qualityGate.algorithm",
|
|
933
|
+
"qualityGate.algorithms"
|
|
930
934
|
]);
|
|
931
935
|
function getConfigKeyPaths(obj, prefix = "") {
|
|
932
936
|
const keys = [];
|
|
@@ -1554,7 +1558,10 @@ var defaultConfig = {
|
|
|
1554
1558
|
autoUpdate: true,
|
|
1555
1559
|
debug: false,
|
|
1556
1560
|
pruneNotification: "detailed",
|
|
1557
|
-
|
|
1561
|
+
// [FIX #20] Default to toast — chat-mode notifications inject an empty
|
|
1562
|
+
// user message that freezes the session on providers that reject empty
|
|
1563
|
+
// messages (zhipuai-lb code 1214). See lib/ui/notification.ts.
|
|
1564
|
+
pruneNotificationType: "toast",
|
|
1558
1565
|
commands: {
|
|
1559
1566
|
enabled: true,
|
|
1560
1567
|
protectedTools: [...DEFAULT_PROTECTED_TOOLS]
|
|
@@ -1618,6 +1625,18 @@ var defaultConfig = {
|
|
|
1618
1625
|
highThreshold: "75%",
|
|
1619
1626
|
forceThreshold: "90%"
|
|
1620
1627
|
}
|
|
1628
|
+
},
|
|
1629
|
+
qualityGate: {
|
|
1630
|
+
enabled: false,
|
|
1631
|
+
algorithm: "rouge-recall-v1",
|
|
1632
|
+
algorithms: {
|
|
1633
|
+
"rouge-recall-v1": {
|
|
1634
|
+
layer1MinChars: 200,
|
|
1635
|
+
layer1MinRetentionPct: 1,
|
|
1636
|
+
layer2MaxRougeF1: 0.05,
|
|
1637
|
+
layer2MaxTop20Recall: 0.2
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1621
1640
|
}
|
|
1622
1641
|
};
|
|
1623
1642
|
var GLOBAL_CONFIG_DIR = process.env.XDG_CONFIG_HOME ? join(process.env.XDG_CONFIG_HOME, "opencode") : join(homedir(), ".config", "opencode");
|
|
@@ -1815,6 +1834,11 @@ function deepCloneConfig(config) {
|
|
|
1815
1834
|
gc: {
|
|
1816
1835
|
...config.gc,
|
|
1817
1836
|
batchCleanup: { ...config.gc.batchCleanup }
|
|
1837
|
+
},
|
|
1838
|
+
qualityGate: {
|
|
1839
|
+
enabled: config.qualityGate.enabled,
|
|
1840
|
+
algorithm: config.qualityGate.algorithm,
|
|
1841
|
+
algorithms: { ...config.qualityGate.algorithms }
|
|
1818
1842
|
}
|
|
1819
1843
|
};
|
|
1820
1844
|
}
|
|
@@ -1828,6 +1852,14 @@ function mergeGC(base, override) {
|
|
|
1828
1852
|
batchCleanup: { ...base.batchCleanup, ...override.batchCleanup ?? {} }
|
|
1829
1853
|
};
|
|
1830
1854
|
}
|
|
1855
|
+
function mergeQualityGate(base, override) {
|
|
1856
|
+
if (!override) return base;
|
|
1857
|
+
return {
|
|
1858
|
+
enabled: override.enabled ?? base.enabled,
|
|
1859
|
+
algorithm: override.algorithm ?? base.algorithm,
|
|
1860
|
+
algorithms: { ...base.algorithms, ...override.algorithms ?? {} }
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1831
1863
|
function mergeLayer(config, data) {
|
|
1832
1864
|
return {
|
|
1833
1865
|
enabled: data.enabled ?? config.enabled,
|
|
@@ -1847,7 +1879,8 @@ function mergeLayer(config, data) {
|
|
|
1847
1879
|
],
|
|
1848
1880
|
compress: mergeCompress(config.compress, data.compress),
|
|
1849
1881
|
gc: mergeGC(config.gc, data.gc),
|
|
1850
|
-
strategies: mergeStrategies(config.strategies, data.strategies)
|
|
1882
|
+
strategies: mergeStrategies(config.strategies, data.strategies),
|
|
1883
|
+
qualityGate: mergeQualityGate(config.qualityGate, data.qualityGate)
|
|
1851
1884
|
};
|
|
1852
1885
|
}
|
|
1853
1886
|
function scheduleParseWarning(ctx, title, message) {
|
|
@@ -3287,6 +3320,8 @@ var SoftIssue = class extends Error {
|
|
|
3287
3320
|
this.kind = kind;
|
|
3288
3321
|
this.messageId = messageId;
|
|
3289
3322
|
}
|
|
3323
|
+
kind;
|
|
3324
|
+
messageId;
|
|
3290
3325
|
};
|
|
3291
3326
|
function validateArgs(args) {
|
|
3292
3327
|
if (typeof args.topic !== "string" || args.topic.trim().length === 0) {
|
|
@@ -5145,20 +5180,22 @@ ${progressBar}`;
|
|
|
5145
5180
|
\u2192 Compression (~${summaryTokensStr}): ${displaySummary}`;
|
|
5146
5181
|
}
|
|
5147
5182
|
}
|
|
5148
|
-
if (config.pruneNotificationType === "
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
title: "ACP: Compress Notification",
|
|
5154
|
-
message: toastMessage,
|
|
5155
|
-
variant: "info",
|
|
5156
|
-
duration: 5e3
|
|
5157
|
-
}
|
|
5158
|
-
});
|
|
5159
|
-
return true;
|
|
5183
|
+
if (config.pruneNotificationType === "chat") {
|
|
5184
|
+
logger.warn(
|
|
5185
|
+
"compress.pruneNotificationType 'chat' is no longer supported (it injects an empty user message that causes provider 400 errors); falling back to toast. Set pruneNotificationType to 'toast' (or pruneNotification to 'off') to silence this warning.",
|
|
5186
|
+
{ sessionId }
|
|
5187
|
+
);
|
|
5160
5188
|
}
|
|
5161
|
-
|
|
5189
|
+
let toastMessage = message;
|
|
5190
|
+
toastMessage = config.pruneNotification === "minimal" ? toastMessage : truncateToastBody(toastMessage);
|
|
5191
|
+
await client.tui.showToast({
|
|
5192
|
+
body: {
|
|
5193
|
+
title: "ACP: Compress Notification",
|
|
5194
|
+
message: toastMessage,
|
|
5195
|
+
variant: "info",
|
|
5196
|
+
duration: 5e3
|
|
5197
|
+
}
|
|
5198
|
+
});
|
|
5162
5199
|
return true;
|
|
5163
5200
|
}
|
|
5164
5201
|
async function sendIgnoredMessage(client, sessionID, text, params, logger) {
|
|
@@ -5192,6 +5229,737 @@ async function sendIgnoredMessage(client, sessionID, text, params, logger) {
|
|
|
5192
5229
|
}
|
|
5193
5230
|
}
|
|
5194
5231
|
|
|
5232
|
+
// lib/compress/quality-gate/registry.ts
|
|
5233
|
+
var registry = /* @__PURE__ */ new Map();
|
|
5234
|
+
function registerQualityGate(gate) {
|
|
5235
|
+
if (registry.has(gate.name)) {
|
|
5236
|
+
const existing = registry.get(gate.name);
|
|
5237
|
+
if (existing !== gate && existing.version !== gate.version) {
|
|
5238
|
+
throw new Error(
|
|
5239
|
+
`Quality gate "${gate.name}" already registered with version ${existing.version} (attempted ${gate.version})`
|
|
5240
|
+
);
|
|
5241
|
+
}
|
|
5242
|
+
}
|
|
5243
|
+
registry.set(gate.name, gate);
|
|
5244
|
+
}
|
|
5245
|
+
function getQualityGate(name) {
|
|
5246
|
+
return registry.get(name);
|
|
5247
|
+
}
|
|
5248
|
+
|
|
5249
|
+
// node_modules/context-compress-algorithms/dist/chunk-E6LYOQFY.js
|
|
5250
|
+
var ENGLISH_WORD_RE = /[a-z][a-z0-9_]+/g;
|
|
5251
|
+
var CJK_RE = /[\u4e00-\u9fff]/g;
|
|
5252
|
+
var FILE_PATH_RE = /(?:[a-zA-Z0-9_-]+\/){1,}[a-zA-Z0-9_-]+\.[a-zA-Z]{1,5}/g;
|
|
5253
|
+
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
5254
|
+
"the",
|
|
5255
|
+
"that",
|
|
5256
|
+
"this",
|
|
5257
|
+
"these",
|
|
5258
|
+
"those",
|
|
5259
|
+
"there",
|
|
5260
|
+
"their",
|
|
5261
|
+
"them",
|
|
5262
|
+
"then",
|
|
5263
|
+
"than",
|
|
5264
|
+
"thats",
|
|
5265
|
+
"with",
|
|
5266
|
+
"will",
|
|
5267
|
+
"would",
|
|
5268
|
+
"could",
|
|
5269
|
+
"should",
|
|
5270
|
+
"from",
|
|
5271
|
+
"have",
|
|
5272
|
+
"has",
|
|
5273
|
+
"had",
|
|
5274
|
+
"having",
|
|
5275
|
+
"were",
|
|
5276
|
+
"what",
|
|
5277
|
+
"which",
|
|
5278
|
+
"when",
|
|
5279
|
+
"where",
|
|
5280
|
+
"while",
|
|
5281
|
+
"your",
|
|
5282
|
+
"yours",
|
|
5283
|
+
"theirs",
|
|
5284
|
+
"ours",
|
|
5285
|
+
"mine",
|
|
5286
|
+
"hers",
|
|
5287
|
+
"whose",
|
|
5288
|
+
"they",
|
|
5289
|
+
"them",
|
|
5290
|
+
"those",
|
|
5291
|
+
"these",
|
|
5292
|
+
"this",
|
|
5293
|
+
"that",
|
|
5294
|
+
"such",
|
|
5295
|
+
"some",
|
|
5296
|
+
"same",
|
|
5297
|
+
"other",
|
|
5298
|
+
"another",
|
|
5299
|
+
"each",
|
|
5300
|
+
"into",
|
|
5301
|
+
"onto",
|
|
5302
|
+
"upon",
|
|
5303
|
+
"over",
|
|
5304
|
+
"under",
|
|
5305
|
+
"between",
|
|
5306
|
+
"through",
|
|
5307
|
+
"during",
|
|
5308
|
+
"before",
|
|
5309
|
+
"after",
|
|
5310
|
+
"above",
|
|
5311
|
+
"below",
|
|
5312
|
+
"among",
|
|
5313
|
+
"across",
|
|
5314
|
+
"along",
|
|
5315
|
+
"around",
|
|
5316
|
+
"about",
|
|
5317
|
+
"because",
|
|
5318
|
+
"since",
|
|
5319
|
+
"unless",
|
|
5320
|
+
"although",
|
|
5321
|
+
"though",
|
|
5322
|
+
"whereas",
|
|
5323
|
+
"whether",
|
|
5324
|
+
"either",
|
|
5325
|
+
"neither",
|
|
5326
|
+
"both",
|
|
5327
|
+
"also",
|
|
5328
|
+
"only",
|
|
5329
|
+
"just",
|
|
5330
|
+
"very",
|
|
5331
|
+
"more",
|
|
5332
|
+
"most",
|
|
5333
|
+
"much",
|
|
5334
|
+
"many",
|
|
5335
|
+
"less",
|
|
5336
|
+
"least",
|
|
5337
|
+
"several",
|
|
5338
|
+
"enough",
|
|
5339
|
+
"make",
|
|
5340
|
+
"made",
|
|
5341
|
+
"makes",
|
|
5342
|
+
"making",
|
|
5343
|
+
"take",
|
|
5344
|
+
"took",
|
|
5345
|
+
"taken",
|
|
5346
|
+
"takes",
|
|
5347
|
+
"taking",
|
|
5348
|
+
"get",
|
|
5349
|
+
"got",
|
|
5350
|
+
"getting",
|
|
5351
|
+
"gets",
|
|
5352
|
+
"give",
|
|
5353
|
+
"gave",
|
|
5354
|
+
"given",
|
|
5355
|
+
"gives",
|
|
5356
|
+
"giving",
|
|
5357
|
+
"come",
|
|
5358
|
+
"came",
|
|
5359
|
+
"comes",
|
|
5360
|
+
"coming",
|
|
5361
|
+
"were",
|
|
5362
|
+
"been",
|
|
5363
|
+
"being",
|
|
5364
|
+
"have",
|
|
5365
|
+
"make",
|
|
5366
|
+
"want",
|
|
5367
|
+
"need",
|
|
5368
|
+
"using",
|
|
5369
|
+
"true",
|
|
5370
|
+
"false",
|
|
5371
|
+
"null",
|
|
5372
|
+
"undefined",
|
|
5373
|
+
"void",
|
|
5374
|
+
"return",
|
|
5375
|
+
"returns",
|
|
5376
|
+
"returning",
|
|
5377
|
+
"function",
|
|
5378
|
+
"const",
|
|
5379
|
+
"class",
|
|
5380
|
+
"interface",
|
|
5381
|
+
"type",
|
|
5382
|
+
"typeof",
|
|
5383
|
+
"instanceof",
|
|
5384
|
+
"import",
|
|
5385
|
+
"export",
|
|
5386
|
+
"require",
|
|
5387
|
+
"module",
|
|
5388
|
+
"default",
|
|
5389
|
+
"async",
|
|
5390
|
+
"await",
|
|
5391
|
+
"static",
|
|
5392
|
+
"public",
|
|
5393
|
+
"private",
|
|
5394
|
+
"protected",
|
|
5395
|
+
"readonly",
|
|
5396
|
+
"partial",
|
|
5397
|
+
"abstract",
|
|
5398
|
+
"virtual",
|
|
5399
|
+
"override",
|
|
5400
|
+
"final",
|
|
5401
|
+
"super",
|
|
5402
|
+
"value",
|
|
5403
|
+
"values",
|
|
5404
|
+
"param",
|
|
5405
|
+
"params",
|
|
5406
|
+
"name",
|
|
5407
|
+
"names",
|
|
5408
|
+
"test",
|
|
5409
|
+
"tests",
|
|
5410
|
+
"testing",
|
|
5411
|
+
"tested",
|
|
5412
|
+
"expect",
|
|
5413
|
+
"expected",
|
|
5414
|
+
"actual",
|
|
5415
|
+
"actuals",
|
|
5416
|
+
"result",
|
|
5417
|
+
"results",
|
|
5418
|
+
"output",
|
|
5419
|
+
"outputs",
|
|
5420
|
+
"input",
|
|
5421
|
+
"inputs",
|
|
5422
|
+
"data",
|
|
5423
|
+
"record",
|
|
5424
|
+
"records",
|
|
5425
|
+
"item",
|
|
5426
|
+
"items",
|
|
5427
|
+
"like",
|
|
5428
|
+
"want",
|
|
5429
|
+
"need",
|
|
5430
|
+
"used",
|
|
5431
|
+
"uses",
|
|
5432
|
+
"said",
|
|
5433
|
+
"say",
|
|
5434
|
+
"says",
|
|
5435
|
+
"went",
|
|
5436
|
+
"goes",
|
|
5437
|
+
"here",
|
|
5438
|
+
"there",
|
|
5439
|
+
"where",
|
|
5440
|
+
"when",
|
|
5441
|
+
"what",
|
|
5442
|
+
"who",
|
|
5443
|
+
"how",
|
|
5444
|
+
"why",
|
|
5445
|
+
"which",
|
|
5446
|
+
"whose",
|
|
5447
|
+
"whom",
|
|
5448
|
+
"yourself",
|
|
5449
|
+
"myself",
|
|
5450
|
+
"itself",
|
|
5451
|
+
"themselves",
|
|
5452
|
+
"ourselves",
|
|
5453
|
+
"himself",
|
|
5454
|
+
"herself",
|
|
5455
|
+
"yeah",
|
|
5456
|
+
"okay",
|
|
5457
|
+
"ok",
|
|
5458
|
+
"yes",
|
|
5459
|
+
"no",
|
|
5460
|
+
"not",
|
|
5461
|
+
"nor",
|
|
5462
|
+
"or",
|
|
5463
|
+
"and",
|
|
5464
|
+
"but",
|
|
5465
|
+
"if",
|
|
5466
|
+
"then",
|
|
5467
|
+
"else",
|
|
5468
|
+
"elif",
|
|
5469
|
+
"when",
|
|
5470
|
+
"while",
|
|
5471
|
+
"for",
|
|
5472
|
+
"to",
|
|
5473
|
+
"of",
|
|
5474
|
+
"in",
|
|
5475
|
+
"on",
|
|
5476
|
+
"at",
|
|
5477
|
+
"by",
|
|
5478
|
+
"with",
|
|
5479
|
+
"from",
|
|
5480
|
+
"into",
|
|
5481
|
+
"onto",
|
|
5482
|
+
"\u7684",
|
|
5483
|
+
"\u662F",
|
|
5484
|
+
"\u4E86",
|
|
5485
|
+
"\u5728",
|
|
5486
|
+
"\u548C",
|
|
5487
|
+
"\u4E0E",
|
|
5488
|
+
"\u6216",
|
|
5489
|
+
"\u4E5F",
|
|
5490
|
+
"\u90FD",
|
|
5491
|
+
"\u5C31",
|
|
5492
|
+
"\u8FD8",
|
|
5493
|
+
"\u53C8",
|
|
5494
|
+
"\u624D",
|
|
5495
|
+
"\u518D",
|
|
5496
|
+
"\u5DF2",
|
|
5497
|
+
"\u5C06",
|
|
5498
|
+
"\u4F1A",
|
|
5499
|
+
"\u80FD",
|
|
5500
|
+
"\u53EF",
|
|
5501
|
+
"\u53EF\u4EE5",
|
|
5502
|
+
"\u8981",
|
|
5503
|
+
"\u60F3",
|
|
5504
|
+
"\u9700\u8981",
|
|
5505
|
+
"\u5E94\u8BE5",
|
|
5506
|
+
"\u5FC5\u987B",
|
|
5507
|
+
"\u6CA1",
|
|
5508
|
+
"\u6CA1\u6709",
|
|
5509
|
+
"\u4E0D",
|
|
5510
|
+
"\u975E",
|
|
5511
|
+
"\u65E0",
|
|
5512
|
+
"\u83AB",
|
|
5513
|
+
"\u6211",
|
|
5514
|
+
"\u4F60",
|
|
5515
|
+
"\u4ED6",
|
|
5516
|
+
"\u5979",
|
|
5517
|
+
"\u5B83",
|
|
5518
|
+
"\u6211\u4EEC",
|
|
5519
|
+
"\u4F60\u4EEC",
|
|
5520
|
+
"\u4ED6\u4EEC",
|
|
5521
|
+
"\u5979\u4EEC",
|
|
5522
|
+
"\u5B83\u4EEC",
|
|
5523
|
+
"\u54B1",
|
|
5524
|
+
"\u54B1\u4EEC",
|
|
5525
|
+
"\u81EA\u5DF1",
|
|
5526
|
+
"\u8FD9",
|
|
5527
|
+
"\u90A3",
|
|
5528
|
+
"\u8FD9\u4E2A",
|
|
5529
|
+
"\u90A3\u4E2A",
|
|
5530
|
+
"\u8FD9\u4E9B",
|
|
5531
|
+
"\u90A3\u4E9B",
|
|
5532
|
+
"\u8FD9\u6837",
|
|
5533
|
+
"\u90A3\u6837",
|
|
5534
|
+
"\u8FD9\u91CC",
|
|
5535
|
+
"\u90A3\u91CC",
|
|
5536
|
+
"\u8FD9\u4E48",
|
|
5537
|
+
"\u90A3\u4E48",
|
|
5538
|
+
"\u4EC0\u4E48",
|
|
5539
|
+
"\u600E\u4E48",
|
|
5540
|
+
"\u4E3A\u4EC0\u4E48",
|
|
5541
|
+
"\u54EA",
|
|
5542
|
+
"\u54EA\u4E2A",
|
|
5543
|
+
"\u54EA\u4E9B",
|
|
5544
|
+
"\u54EA\u91CC",
|
|
5545
|
+
"\u600E\u6837",
|
|
5546
|
+
"\u591A\u5C11",
|
|
5547
|
+
"\u51E0",
|
|
5548
|
+
"\u591A",
|
|
5549
|
+
"\u5C11",
|
|
5550
|
+
"\u4E8E",
|
|
5551
|
+
"\u4ECE",
|
|
5552
|
+
"\u5411",
|
|
5553
|
+
"\u5F80",
|
|
5554
|
+
"\u5230",
|
|
5555
|
+
"\u81F3",
|
|
5556
|
+
"\u4E3A",
|
|
5557
|
+
"\u5BF9\u4E8E",
|
|
5558
|
+
"\u5173\u4E8E",
|
|
5559
|
+
"\u81F3\u4E8E",
|
|
5560
|
+
"\u7531\u4E8E",
|
|
5561
|
+
"\u56E0\u4E3A",
|
|
5562
|
+
"\u6240\u4EE5",
|
|
5563
|
+
"\u4F46\u662F",
|
|
5564
|
+
"\u4F46",
|
|
5565
|
+
"\u4E0D\u8FC7",
|
|
5566
|
+
"\u7136\u800C",
|
|
5567
|
+
"\u53EF\u662F",
|
|
5568
|
+
"\u53EA\u662F",
|
|
5569
|
+
"\u53EA\u6709",
|
|
5570
|
+
"\u9664\u4E86",
|
|
5571
|
+
"\u9664\u975E",
|
|
5572
|
+
"\u65E0\u8BBA",
|
|
5573
|
+
"\u4E0D\u7BA1",
|
|
5574
|
+
"\u5C3D\u7BA1",
|
|
5575
|
+
"\u867D\u7136",
|
|
5576
|
+
"\u867D\u8BF4",
|
|
5577
|
+
"\u5373\u4F7F",
|
|
5578
|
+
"\u5373\u4FBF",
|
|
5579
|
+
"\u54EA\u6015",
|
|
5580
|
+
"\u4E00\u65E6",
|
|
5581
|
+
"\u5982\u679C",
|
|
5582
|
+
"\u8981\u662F",
|
|
5583
|
+
"\u5047\u5982",
|
|
5584
|
+
"\u5047\u4F7F",
|
|
5585
|
+
"\u5018\u82E5",
|
|
5586
|
+
"\u4E4B",
|
|
5587
|
+
"\u5176",
|
|
5588
|
+
"\u5176\u4E2D",
|
|
5589
|
+
"\u5176\u4ED6",
|
|
5590
|
+
"\u5176\u5B83",
|
|
5591
|
+
"\u5176\u4F59",
|
|
5592
|
+
"\u53E6\u4E00",
|
|
5593
|
+
"\u53E6\u5916",
|
|
5594
|
+
"\u6B64\u5916",
|
|
5595
|
+
"\u5E76\u4E14",
|
|
5596
|
+
"\u5E76",
|
|
5597
|
+
"\u4E14",
|
|
5598
|
+
"\u7740",
|
|
5599
|
+
"\u8FC7",
|
|
5600
|
+
"\u5427",
|
|
5601
|
+
"\u5417",
|
|
5602
|
+
"\u5462",
|
|
5603
|
+
"\u554A",
|
|
5604
|
+
"\u54E6",
|
|
5605
|
+
"\u55EF",
|
|
5606
|
+
"\u5440",
|
|
5607
|
+
"\u54C7",
|
|
5608
|
+
"\u54C8",
|
|
5609
|
+
"\u561B",
|
|
5610
|
+
"\u54AF",
|
|
5611
|
+
"\u54DF"
|
|
5612
|
+
]);
|
|
5613
|
+
var ZH_STOPWORD_BIGRAMS = /* @__PURE__ */ new Set([
|
|
5614
|
+
"\u6211\u4EEC",
|
|
5615
|
+
"\u4F60\u4EEC",
|
|
5616
|
+
"\u4ED6\u4EEC",
|
|
5617
|
+
"\u5979\u4EEC",
|
|
5618
|
+
"\u5B83\u4EEC",
|
|
5619
|
+
"\u54B1\u4EEC",
|
|
5620
|
+
"\u8FD9\u4E2A",
|
|
5621
|
+
"\u90A3\u4E2A",
|
|
5622
|
+
"\u8FD9\u4E9B",
|
|
5623
|
+
"\u90A3\u4E9B",
|
|
5624
|
+
"\u4EC0\u4E48",
|
|
5625
|
+
"\u600E\u4E48",
|
|
5626
|
+
"\u4E3A\u4EC0\u4E48",
|
|
5627
|
+
"\u5982\u4F55",
|
|
5628
|
+
"\u53EF\u4EE5",
|
|
5629
|
+
"\u5E94\u8BE5",
|
|
5630
|
+
"\u4F46\u662F",
|
|
5631
|
+
"\u56E0\u4E3A",
|
|
5632
|
+
"\u6240\u4EE5",
|
|
5633
|
+
"\u5982\u679C",
|
|
5634
|
+
"\u867D\u7136",
|
|
5635
|
+
"\u5373\u4F7F",
|
|
5636
|
+
"\u5C3D\u7BA1",
|
|
5637
|
+
"\u4E3A\u4E86",
|
|
5638
|
+
"\u7531\u4E8E",
|
|
5639
|
+
"\u4E0D\u4F46",
|
|
5640
|
+
"\u800C\u4E14",
|
|
5641
|
+
"\u5E76\u4E14",
|
|
5642
|
+
"\u6216\u8005",
|
|
5643
|
+
"\u8FD8\u662F",
|
|
5644
|
+
"\u4EE5\u53CA",
|
|
5645
|
+
"\u4EE5\u4E3A",
|
|
5646
|
+
"\u4E8E\u662F",
|
|
5647
|
+
"\u7136\u800C",
|
|
5648
|
+
"\u5176\u5B9E",
|
|
5649
|
+
"\u5C31\u662F",
|
|
5650
|
+
"\u53EA\u662F",
|
|
5651
|
+
"\u53EA\u6709",
|
|
5652
|
+
"\u9664\u4E86",
|
|
5653
|
+
"\u9664\u975E",
|
|
5654
|
+
"\u8FD9\u6837",
|
|
5655
|
+
"\u90A3\u6837",
|
|
5656
|
+
"\u8FD9\u4E48",
|
|
5657
|
+
"\u90A3\u4E48",
|
|
5658
|
+
"\u8FD9\u4E9B",
|
|
5659
|
+
"\u90A3\u4E9B",
|
|
5660
|
+
"\u8FD9\u91CC",
|
|
5661
|
+
"\u90A3\u91CC",
|
|
5662
|
+
"\u73B0\u5728",
|
|
5663
|
+
"\u4EE5\u540E",
|
|
5664
|
+
"\u4EE5\u524D",
|
|
5665
|
+
"\u4E4B\u540E",
|
|
5666
|
+
"\u4E4B\u524D",
|
|
5667
|
+
"\u7136\u540E",
|
|
5668
|
+
"\u5F53\u7136",
|
|
5669
|
+
"\u53EF\u80FD",
|
|
5670
|
+
"\u4E00\u4E9B",
|
|
5671
|
+
"\u8BB8\u591A",
|
|
5672
|
+
"\u975E\u5E38",
|
|
5673
|
+
"\u5341\u5206",
|
|
5674
|
+
"\u6BD4\u8F83",
|
|
5675
|
+
"\u66F4\u52A0",
|
|
5676
|
+
"\u6700\u4E3A",
|
|
5677
|
+
"\u4E5F\u662F",
|
|
5678
|
+
"\u8FD8\u662F",
|
|
5679
|
+
"\u5C31\u662F",
|
|
5680
|
+
"\u4E0D\u8FC7",
|
|
5681
|
+
"\u4E0D\u8981",
|
|
5682
|
+
"\u4E0D\u80FD",
|
|
5683
|
+
"\u4E0D\u4F1A",
|
|
5684
|
+
"\u6CA1\u6709",
|
|
5685
|
+
"\u4E0D\u662F",
|
|
5686
|
+
"\u4E0D\u7528",
|
|
5687
|
+
"\u4E0D\u5FC5",
|
|
5688
|
+
"\u4E00\u76F4",
|
|
5689
|
+
"\u5DF2\u7ECF",
|
|
5690
|
+
"\u6B63\u5728",
|
|
5691
|
+
"\u9A6C\u4E0A"
|
|
5692
|
+
]);
|
|
5693
|
+
var DEFAULT_OPTS = {
|
|
5694
|
+
english: true,
|
|
5695
|
+
zhUnigrams: true,
|
|
5696
|
+
zhBigrams: true
|
|
5697
|
+
};
|
|
5698
|
+
function tokenize(text, opts = {}) {
|
|
5699
|
+
if (!text || typeof text !== "string") return [];
|
|
5700
|
+
const options = { ...DEFAULT_OPTS, ...opts };
|
|
5701
|
+
const tokens = [];
|
|
5702
|
+
const lower = text.toLowerCase();
|
|
5703
|
+
if (options.english) {
|
|
5704
|
+
const matches = lower.match(ENGLISH_WORD_RE);
|
|
5705
|
+
if (matches) {
|
|
5706
|
+
for (const w of matches) {
|
|
5707
|
+
if (w.length >= 4 && !STOPWORDS.has(w) && !/^\d+$/.test(w)) {
|
|
5708
|
+
tokens.push(w);
|
|
5709
|
+
}
|
|
5710
|
+
}
|
|
5711
|
+
}
|
|
5712
|
+
}
|
|
5713
|
+
if (options.zhUnigrams || options.zhBigrams) {
|
|
5714
|
+
const cjkChars = text.match(CJK_RE);
|
|
5715
|
+
if (cjkChars && cjkChars.length > 0) {
|
|
5716
|
+
const cjkStr = cjkChars.join("");
|
|
5717
|
+
if (options.zhUnigrams) {
|
|
5718
|
+
for (const c of cjkStr) {
|
|
5719
|
+
if (!STOPWORDS.has(c)) tokens.push(c);
|
|
5720
|
+
}
|
|
5721
|
+
}
|
|
5722
|
+
if (options.zhBigrams) {
|
|
5723
|
+
for (let i = 0; i < cjkStr.length - 1; i++) {
|
|
5724
|
+
const bg = cjkStr.slice(i, i + 2);
|
|
5725
|
+
if (!ZH_STOPWORD_BIGRAMS.has(bg) && !STOPWORDS.has(bg[0]) && !STOPWORDS.has(bg[1])) {
|
|
5726
|
+
tokens.push(bg);
|
|
5727
|
+
}
|
|
5728
|
+
}
|
|
5729
|
+
}
|
|
5730
|
+
}
|
|
5731
|
+
}
|
|
5732
|
+
return tokens;
|
|
5733
|
+
}
|
|
5734
|
+
function termFrequency(tokens) {
|
|
5735
|
+
const tf = /* @__PURE__ */ new Map();
|
|
5736
|
+
for (const t of tokens) {
|
|
5737
|
+
tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
5738
|
+
}
|
|
5739
|
+
return tf;
|
|
5740
|
+
}
|
|
5741
|
+
function topKByTf(tokens, k) {
|
|
5742
|
+
if (tokens.length === 0 || k <= 0) return [];
|
|
5743
|
+
const tf = termFrequency(tokens);
|
|
5744
|
+
const sorted = [...tf.entries()].sort((a, b) => {
|
|
5745
|
+
if (b[1] !== a[1]) return b[1] - a[1];
|
|
5746
|
+
return a[0].localeCompare(b[0]);
|
|
5747
|
+
});
|
|
5748
|
+
return sorted.slice(0, k).map((e) => e[0]);
|
|
5749
|
+
}
|
|
5750
|
+
function extractFilePaths(text) {
|
|
5751
|
+
if (!text) return /* @__PURE__ */ new Set();
|
|
5752
|
+
const paths = /* @__PURE__ */ new Set();
|
|
5753
|
+
const matches = text.match(FILE_PATH_RE);
|
|
5754
|
+
if (matches) {
|
|
5755
|
+
for (const m of matches) paths.add(m);
|
|
5756
|
+
}
|
|
5757
|
+
return paths;
|
|
5758
|
+
}
|
|
5759
|
+
function rouge1Recall(summaryTokens, originalTokens) {
|
|
5760
|
+
if (originalTokens.length === 0) return 0;
|
|
5761
|
+
const summarySet = new Set(summaryTokens);
|
|
5762
|
+
const originalSet = new Set(originalTokens);
|
|
5763
|
+
let hit = 0;
|
|
5764
|
+
for (const t of originalSet) if (summarySet.has(t)) hit++;
|
|
5765
|
+
return hit / originalSet.size;
|
|
5766
|
+
}
|
|
5767
|
+
function rouge1Precision(summaryTokens, originalTokens) {
|
|
5768
|
+
if (summaryTokens.length === 0) return 0;
|
|
5769
|
+
const summarySet = new Set(summaryTokens);
|
|
5770
|
+
const originalSet = new Set(originalTokens);
|
|
5771
|
+
let hit = 0;
|
|
5772
|
+
for (const t of summarySet) if (originalSet.has(t)) hit++;
|
|
5773
|
+
return hit / summarySet.size;
|
|
5774
|
+
}
|
|
5775
|
+
function rouge1F1(summaryTokens, originalTokens) {
|
|
5776
|
+
const r = rouge1Recall(summaryTokens, originalTokens);
|
|
5777
|
+
const p = rouge1Precision(summaryTokens, originalTokens);
|
|
5778
|
+
if (r + p === 0) return 0;
|
|
5779
|
+
return 2 * r * p / (r + p);
|
|
5780
|
+
}
|
|
5781
|
+
function topKRecall(summaryTokens, originalTokens, k) {
|
|
5782
|
+
if (originalTokens.length === 0 || k <= 0) return 0;
|
|
5783
|
+
const top = topKByTf(originalTokens, k);
|
|
5784
|
+
if (top.length === 0) return 0;
|
|
5785
|
+
const summarySet = new Set(summaryTokens);
|
|
5786
|
+
let hit = 0;
|
|
5787
|
+
for (const t of top) if (summarySet.has(t)) hit++;
|
|
5788
|
+
return hit / top.length;
|
|
5789
|
+
}
|
|
5790
|
+
var DEFAULT_ROUGE_RECALL_V1_CONFIG = {
|
|
5791
|
+
layer1MinChars: 200,
|
|
5792
|
+
layer1MinRetentionPct: 1,
|
|
5793
|
+
layer2MaxRougeF1: 0.05,
|
|
5794
|
+
layer2MaxTop20Recall: 0.2
|
|
5795
|
+
};
|
|
5796
|
+
var TOP_K = 20;
|
|
5797
|
+
var ORIGINAL_TOKEN_ESTIMATE_CHARS_PER_TOKEN = 4;
|
|
5798
|
+
function resolveConfig(input) {
|
|
5799
|
+
if (!input || typeof input !== "object") return DEFAULT_ROUGE_RECALL_V1_CONFIG;
|
|
5800
|
+
const c = input;
|
|
5801
|
+
return {
|
|
5802
|
+
layer1MinChars: typeof c.layer1MinChars === "number" && c.layer1MinChars > 0 ? c.layer1MinChars : DEFAULT_ROUGE_RECALL_V1_CONFIG.layer1MinChars,
|
|
5803
|
+
layer1MinRetentionPct: typeof c.layer1MinRetentionPct === "number" && c.layer1MinRetentionPct >= 0 ? c.layer1MinRetentionPct : DEFAULT_ROUGE_RECALL_V1_CONFIG.layer1MinRetentionPct,
|
|
5804
|
+
layer2MaxRougeF1: typeof c.layer2MaxRougeF1 === "number" && c.layer2MaxRougeF1 >= 0 ? c.layer2MaxRougeF1 : DEFAULT_ROUGE_RECALL_V1_CONFIG.layer2MaxRougeF1,
|
|
5805
|
+
layer2MaxTop20Recall: typeof c.layer2MaxTop20Recall === "number" && c.layer2MaxTop20Recall >= 0 ? c.layer2MaxTop20Recall : DEFAULT_ROUGE_RECALL_V1_CONFIG.layer2MaxTop20Recall
|
|
5806
|
+
};
|
|
5807
|
+
}
|
|
5808
|
+
var rougeRecallV1 = {
|
|
5809
|
+
name: "rouge-recall-v1",
|
|
5810
|
+
version: "1.0.0",
|
|
5811
|
+
description: "Two-layer gate: length floor (L1) then ROUGE-1 F1 AND top-20 keyword recall (L2)",
|
|
5812
|
+
evaluate(ctx, rawConfig) {
|
|
5813
|
+
const cfg = resolveConfig(rawConfig);
|
|
5814
|
+
const summaryLen = ctx.summary.length;
|
|
5815
|
+
const originalChars = ctx.originalText.length;
|
|
5816
|
+
const retentionPct = ctx.block.compressedTokens > 0 ? summaryLen / (ctx.block.compressedTokens * ORIGINAL_TOKEN_ESTIMATE_CHARS_PER_TOKEN) * 100 : 0;
|
|
5817
|
+
const baseMetrics = [
|
|
5818
|
+
{ name: "summaryLen", value: summaryLen },
|
|
5819
|
+
{ name: "retentionPct", value: +retentionPct.toFixed(2), format: "percent" },
|
|
5820
|
+
{ name: "originalTokens", value: ctx.block.compressedTokens }
|
|
5821
|
+
];
|
|
5822
|
+
if (summaryLen < cfg.layer1MinChars || originalChars > 0 && retentionPct < cfg.layer1MinRetentionPct) {
|
|
5823
|
+
return {
|
|
5824
|
+
passed: false,
|
|
5825
|
+
layer: "L1-length",
|
|
5826
|
+
reason: `Summary too short: ${summaryLen} chars, ${retentionPct.toFixed(2)}% retention (threshold: ${cfg.layer1MinChars} chars OR ${cfg.layer1MinRetentionPct}% retention)`,
|
|
5827
|
+
metrics: baseMetrics
|
|
5828
|
+
};
|
|
5829
|
+
}
|
|
5830
|
+
if (ctx.originalText.length === 0) {
|
|
5831
|
+
return { passed: true, metrics: baseMetrics };
|
|
5832
|
+
}
|
|
5833
|
+
const summaryTokens = tokenize(ctx.summary);
|
|
5834
|
+
const originalTokens = tokenize(ctx.originalText);
|
|
5835
|
+
const rougeF1 = rouge1F1(summaryTokens, originalTokens);
|
|
5836
|
+
const rougeRecall = rouge1Recall(summaryTokens, originalTokens);
|
|
5837
|
+
const top20 = topKRecall(summaryTokens, originalTokens, TOP_K);
|
|
5838
|
+
const summaryPaths = extractFilePaths(ctx.summary);
|
|
5839
|
+
const originalPaths = extractFilePaths(ctx.originalText);
|
|
5840
|
+
const pathCoverage = originalPaths.size >= 5 ? [...summaryPaths].filter((p) => originalPaths.has(p)).length / originalPaths.size : -1;
|
|
5841
|
+
const contentMetrics = [
|
|
5842
|
+
...baseMetrics,
|
|
5843
|
+
{ name: "rougeF1", value: +rougeF1.toFixed(4), format: "ratio" },
|
|
5844
|
+
{ name: "rougeRecall", value: +rougeRecall.toFixed(4), format: "ratio" },
|
|
5845
|
+
{ name: "top20Recall", value: +top20.toFixed(4), format: "ratio" },
|
|
5846
|
+
{ name: "nOriginalPaths", value: originalPaths.size },
|
|
5847
|
+
{ name: "nSummaryPaths", value: summaryPaths.size }
|
|
5848
|
+
];
|
|
5849
|
+
if (pathCoverage >= 0) {
|
|
5850
|
+
contentMetrics.push({ name: "pathCoverage", value: +pathCoverage.toFixed(3), format: "ratio" });
|
|
5851
|
+
}
|
|
5852
|
+
if (rougeF1 < cfg.layer2MaxRougeF1 && top20 < cfg.layer2MaxTop20Recall) {
|
|
5853
|
+
return {
|
|
5854
|
+
passed: false,
|
|
5855
|
+
layer: "L2-recall",
|
|
5856
|
+
reason: `Content coverage too low: rougeF1=${rougeF1.toFixed(3)}, top20Recall=${top20.toFixed(3)} (threshold: rougeF1<${cfg.layer2MaxRougeF1} AND top20<${cfg.layer2MaxTop20Recall})`,
|
|
5857
|
+
metrics: contentMetrics
|
|
5858
|
+
};
|
|
5859
|
+
}
|
|
5860
|
+
return { passed: true, metrics: contentMetrics };
|
|
5861
|
+
}
|
|
5862
|
+
};
|
|
5863
|
+
|
|
5864
|
+
// lib/compress/quality-gate/algorithms/index.ts
|
|
5865
|
+
function ensureBuiltinGatesRegistered() {
|
|
5866
|
+
registerQualityGate(rougeRecallV1);
|
|
5867
|
+
}
|
|
5868
|
+
|
|
5869
|
+
// lib/compress/quality-gate/evaluate.ts
|
|
5870
|
+
var CHARS_PER_TOKEN_ESTIMATE = 4;
|
|
5871
|
+
var TOOL_OUTPUT_MAX_CHARS = 1500;
|
|
5872
|
+
var TOOL_INPUT_MAX_CHARS = 500;
|
|
5873
|
+
function extractMessageText(parts) {
|
|
5874
|
+
if (!parts || !Array.isArray(parts)) return "";
|
|
5875
|
+
let text = "";
|
|
5876
|
+
for (const part of parts) {
|
|
5877
|
+
if (!part || typeof part !== "object") continue;
|
|
5878
|
+
if (part.type === "text") {
|
|
5879
|
+
text += part.text + "\n";
|
|
5880
|
+
} else if (part.type === "tool") {
|
|
5881
|
+
const state = part.state;
|
|
5882
|
+
const input = state.status === "completed" && typeof state.input === "object" ? JSON.stringify(state.input).slice(0, TOOL_INPUT_MAX_CHARS) : "";
|
|
5883
|
+
const output = state.status === "completed" && typeof state.output === "string" ? state.output.slice(0, TOOL_OUTPUT_MAX_CHARS) : state.status === "completed" && typeof state.output === "object" ? JSON.stringify(state.output).slice(0, TOOL_OUTPUT_MAX_CHARS) : "";
|
|
5884
|
+
text += `[tool:${part.tool}] ${input}
|
|
5885
|
+
${output}
|
|
5886
|
+
`;
|
|
5887
|
+
}
|
|
5888
|
+
}
|
|
5889
|
+
return text;
|
|
5890
|
+
}
|
|
5891
|
+
function buildContext(block, rawMessages) {
|
|
5892
|
+
const directIds = block.directMessageIds;
|
|
5893
|
+
if (!directIds || directIds.length === 0) return null;
|
|
5894
|
+
const idToMsg = /* @__PURE__ */ new Map();
|
|
5895
|
+
for (const m of rawMessages) {
|
|
5896
|
+
const id = m?.info?.id;
|
|
5897
|
+
if (typeof id === "string") idToMsg.set(id, m);
|
|
5898
|
+
}
|
|
5899
|
+
const chunks = [];
|
|
5900
|
+
for (const id of directIds) {
|
|
5901
|
+
const m = idToMsg.get(id);
|
|
5902
|
+
if (!m) continue;
|
|
5903
|
+
chunks.push(extractMessageText(m.parts));
|
|
5904
|
+
}
|
|
5905
|
+
if (chunks.length === 0) return null;
|
|
5906
|
+
const originalText = chunks.join("\n");
|
|
5907
|
+
return {
|
|
5908
|
+
block,
|
|
5909
|
+
summary: block.summary ?? "",
|
|
5910
|
+
originalChunks: chunks,
|
|
5911
|
+
originalText,
|
|
5912
|
+
originalTokens: Math.ceil(originalText.length / CHARS_PER_TOKEN_ESTIMATE)
|
|
5913
|
+
};
|
|
5914
|
+
}
|
|
5915
|
+
function evaluateBlockQuality(state, rawMessages, entry, config, logger) {
|
|
5916
|
+
const qg = config.qualityGate;
|
|
5917
|
+
if (!qg || qg.enabled !== true) return null;
|
|
5918
|
+
ensureBuiltinGatesRegistered();
|
|
5919
|
+
const algoName = qg.algorithm;
|
|
5920
|
+
if (!algoName) {
|
|
5921
|
+
logger.warn("Quality gate enabled but no algorithm specified", {});
|
|
5922
|
+
return null;
|
|
5923
|
+
}
|
|
5924
|
+
const gate = getQualityGate(algoName);
|
|
5925
|
+
if (!gate) {
|
|
5926
|
+
logger.warn("Quality gate algorithm not found in registry", { algorithm: algoName });
|
|
5927
|
+
return null;
|
|
5928
|
+
}
|
|
5929
|
+
const block = state.prune.messages.blocksById.get(entry.blockId);
|
|
5930
|
+
if (!block) {
|
|
5931
|
+
logger.warn("Quality gate: block not found", { blockId: entry.blockId });
|
|
5932
|
+
return null;
|
|
5933
|
+
}
|
|
5934
|
+
const ctx = buildContext(block, rawMessages);
|
|
5935
|
+
if (!ctx) return null;
|
|
5936
|
+
const algoConfig = (qg.algorithms && qg.algorithms[algoName]) ?? {};
|
|
5937
|
+
try {
|
|
5938
|
+
return gate.evaluate(ctx, algoConfig);
|
|
5939
|
+
} catch (err) {
|
|
5940
|
+
logger.warn("Quality gate threw \u2014 treating as pass", {
|
|
5941
|
+
gate: gate.name,
|
|
5942
|
+
blockId: entry.blockId,
|
|
5943
|
+
error: err instanceof Error ? err.message : String(err)
|
|
5944
|
+
});
|
|
5945
|
+
return { passed: true, metrics: [] };
|
|
5946
|
+
}
|
|
5947
|
+
}
|
|
5948
|
+
function evaluateBatchQuality(state, rawMessages, entries, config, logger) {
|
|
5949
|
+
const failures = [];
|
|
5950
|
+
for (const entry of entries) {
|
|
5951
|
+
const result = evaluateBlockQuality(state, rawMessages, entry, config, logger);
|
|
5952
|
+
if (result && !result.passed) {
|
|
5953
|
+
failures.push({ blockId: entry.blockId, result });
|
|
5954
|
+
}
|
|
5955
|
+
}
|
|
5956
|
+
return {
|
|
5957
|
+
total: entries.length,
|
|
5958
|
+
passed: entries.length - failures.length,
|
|
5959
|
+
failures
|
|
5960
|
+
};
|
|
5961
|
+
}
|
|
5962
|
+
|
|
5195
5963
|
// lib/compress/pipeline.ts
|
|
5196
5964
|
function snapshotCompressionState(state) {
|
|
5197
5965
|
return {
|
|
@@ -5240,6 +6008,27 @@ async function finalizeSession(ctx, toolCtx, rawMessages, entries, batchTopic) {
|
|
|
5240
6008
|
ctx.state.manualMode = ctx.state.manualMode ? "active" : false;
|
|
5241
6009
|
applyPendingCompressionDurations(ctx.state);
|
|
5242
6010
|
await saveSessionState(ctx.state, ctx.logger);
|
|
6011
|
+
if (entries.length > 0) {
|
|
6012
|
+
const qualityReport = evaluateBatchQuality(
|
|
6013
|
+
ctx.state,
|
|
6014
|
+
rawMessages,
|
|
6015
|
+
entries,
|
|
6016
|
+
ctx.config,
|
|
6017
|
+
ctx.logger
|
|
6018
|
+
);
|
|
6019
|
+
for (const failure of qualityReport.failures) {
|
|
6020
|
+
const metrics = Object.fromEntries(
|
|
6021
|
+
failure.result.metrics.map((m) => [m.name, m.value])
|
|
6022
|
+
);
|
|
6023
|
+
ctx.logger.warn("Compression quality gate FAILED", {
|
|
6024
|
+
blockId: failure.blockId,
|
|
6025
|
+
algorithm: ctx.config.qualityGate.algorithm,
|
|
6026
|
+
layer: failure.result.layer,
|
|
6027
|
+
reason: failure.result.reason,
|
|
6028
|
+
...metrics
|
|
6029
|
+
});
|
|
6030
|
+
}
|
|
6031
|
+
}
|
|
5243
6032
|
const params = getCurrentParams(ctx.state, rawMessages, ctx.logger);
|
|
5244
6033
|
const sessionMessageIds = rawMessages.filter((msg) => !isIgnoredUserMessage(msg)).map((msg) => msg.info.id);
|
|
5245
6034
|
const contextTokensBefore = getCurrentTokenUsage(ctx.state, rawMessages);
|
|
@@ -6220,7 +7009,7 @@ var dropEmptyMessages = (messages) => {
|
|
|
6220
7009
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
6221
7010
|
const parts = Array.isArray(messages[i].parts) ? messages[i].parts : [];
|
|
6222
7011
|
const isEmpty = parts.every(
|
|
6223
|
-
(part) => part.type === "text" && (typeof part.text !== "string" || part.text.trim().length === 0)
|
|
7012
|
+
(part) => part.type === "text" && (typeof part.text !== "string" || part.text.trim().length === 0 || part.ignored === true)
|
|
6224
7013
|
);
|
|
6225
7014
|
if (isEmpty) {
|
|
6226
7015
|
messages.splice(i, 1);
|
|
@@ -6388,6 +7177,62 @@ function listPriorityRefsBeforeIndex(messages, priorities, anchorIndex, priority
|
|
|
6388
7177
|
return refs;
|
|
6389
7178
|
}
|
|
6390
7179
|
|
|
7180
|
+
// node_modules/context-compress-algorithms/dist/chunk-UX2UW5FY.js
|
|
7181
|
+
var NUDGE_GROWTH_FLOOR = 6e3;
|
|
7182
|
+
var NUDGE_GROWTH_CAP = 5e4;
|
|
7183
|
+
var NUDGE_GROWTH_RATIO = 0.05;
|
|
7184
|
+
function computeShouldNudge(params) {
|
|
7185
|
+
const { currentTokens, overMinLimit, overMaxLimit } = params;
|
|
7186
|
+
if (currentTokens === void 0) {
|
|
7187
|
+
return { shouldNudge: false, tipsVariant: null };
|
|
7188
|
+
}
|
|
7189
|
+
if (params.lastNudgeTokens === void 0) {
|
|
7190
|
+
return { shouldNudge: false, tipsVariant: null };
|
|
7191
|
+
}
|
|
7192
|
+
const growthSinceLastNudge = currentTokens - params.lastNudgeTokens;
|
|
7193
|
+
const shouldNudge = growthSinceLastNudge >= params.nudgeGrowthTokens || overMaxLimit;
|
|
7194
|
+
if (!shouldNudge) {
|
|
7195
|
+
return { shouldNudge: false, tipsVariant: null };
|
|
7196
|
+
}
|
|
7197
|
+
const tipsVariant = overMaxLimit ? "maxLimit" : overMinLimit ? "minLimit" : "normal";
|
|
7198
|
+
return { shouldNudge: true, tipsVariant };
|
|
7199
|
+
}
|
|
7200
|
+
function resolveAdaptiveNudgeGrowth(modelContextLimit) {
|
|
7201
|
+
if (!modelContextLimit || modelContextLimit <= 0) return NUDGE_GROWTH_FLOOR;
|
|
7202
|
+
return Math.min(
|
|
7203
|
+
NUDGE_GROWTH_CAP,
|
|
7204
|
+
Math.max(NUDGE_GROWTH_FLOOR, Math.round(modelContextLimit * NUDGE_GROWTH_RATIO))
|
|
7205
|
+
);
|
|
7206
|
+
}
|
|
7207
|
+
var defaultTriggerPolicy = {
|
|
7208
|
+
name: "context-compress-algorithms-trigger",
|
|
7209
|
+
version: "1.0.0",
|
|
7210
|
+
description: "Growth-only cadence: nudge when context growth since last nudge exceeds adaptive threshold, or when over max limit.",
|
|
7211
|
+
computeShouldNudge,
|
|
7212
|
+
resolveAdaptiveNudgeGrowth
|
|
7213
|
+
};
|
|
7214
|
+
|
|
7215
|
+
// lib/messages/inject/policy/registry.ts
|
|
7216
|
+
var registry2 = /* @__PURE__ */ new Map();
|
|
7217
|
+
var defaultPolicy = null;
|
|
7218
|
+
function registerTriggerPolicy2(policy) {
|
|
7219
|
+
if (!policy.name) {
|
|
7220
|
+
throw new Error("TriggerPolicy must have a name");
|
|
7221
|
+
}
|
|
7222
|
+
registry2.set(policy.name, policy);
|
|
7223
|
+
if (!defaultPolicy) {
|
|
7224
|
+
defaultPolicy = policy;
|
|
7225
|
+
}
|
|
7226
|
+
}
|
|
7227
|
+
function getDefaultTriggerPolicy() {
|
|
7228
|
+
return defaultPolicy;
|
|
7229
|
+
}
|
|
7230
|
+
|
|
7231
|
+
// lib/messages/inject/policy/index.ts
|
|
7232
|
+
function ensureBuiltinTriggerPolicyRegistered() {
|
|
7233
|
+
registerTriggerPolicy2(defaultTriggerPolicy);
|
|
7234
|
+
}
|
|
7235
|
+
|
|
6391
7236
|
// lib/messages/inject/utils.ts
|
|
6392
7237
|
var MESSAGE_MODE_NUDGE_PRIORITY = "high";
|
|
6393
7238
|
function getNudgeFrequency(config) {
|
|
@@ -6500,31 +7345,20 @@ function isContextOverLimits(config, state, providerId, modelId, messages) {
|
|
|
6500
7345
|
modelContextLimit: state.modelContextLimit
|
|
6501
7346
|
};
|
|
6502
7347
|
}
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
}
|
|
6508
|
-
if (params.lastNudgeTokens === void 0) {
|
|
6509
|
-
return { shouldNudge: false, tipsVariant: null };
|
|
6510
|
-
}
|
|
6511
|
-
const growthSinceLastNudge = currentTokens - params.lastNudgeTokens;
|
|
6512
|
-
const shouldNudge = growthSinceLastNudge >= params.nudgeGrowthTokens || overMaxLimit;
|
|
6513
|
-
if (!shouldNudge) {
|
|
7348
|
+
ensureBuiltinTriggerPolicyRegistered();
|
|
7349
|
+
function computeShouldNudge2(params) {
|
|
7350
|
+
const policy = getDefaultTriggerPolicy();
|
|
7351
|
+
if (!policy) {
|
|
6514
7352
|
return { shouldNudge: false, tipsVariant: null };
|
|
6515
7353
|
}
|
|
6516
|
-
|
|
6517
|
-
return { shouldNudge: true, tipsVariant };
|
|
7354
|
+
return policy.computeShouldNudge(params);
|
|
6518
7355
|
}
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
return
|
|
6525
|
-
NUDGE_GROWTH_CAP,
|
|
6526
|
-
Math.max(NUDGE_GROWTH_FLOOR, Math.round(modelContextLimit * NUDGE_GROWTH_RATIO))
|
|
6527
|
-
);
|
|
7356
|
+
function resolveAdaptiveNudgeGrowth2(modelContextLimit) {
|
|
7357
|
+
const policy = getDefaultTriggerPolicy();
|
|
7358
|
+
if (!policy) {
|
|
7359
|
+
return 6e3;
|
|
7360
|
+
}
|
|
7361
|
+
return policy.resolveAdaptiveNudgeGrowth(modelContextLimit);
|
|
6528
7362
|
}
|
|
6529
7363
|
function addAnchor(anchorMessageIds, anchorMessageId, anchorMessageIndex, messages, interval) {
|
|
6530
7364
|
if (anchorMessageIndex < 0) {
|
|
@@ -7100,7 +7934,7 @@ ${lines2.join("\n")}`;
|
|
|
7100
7934
|
${lines.join("\n")}`;
|
|
7101
7935
|
}
|
|
7102
7936
|
|
|
7103
|
-
//
|
|
7937
|
+
// node_modules/context-compress-algorithms/dist/chunk-ZRHPFN6B.js
|
|
7104
7938
|
var COMPRESS_PHILOSOPHY = `Compression Philosophy:
|
|
7105
7939
|
- All compression serves the primary task, but be frugal.
|
|
7106
7940
|
- Context capacity is precious. Save context by compressing consumed outputs, not by avoiding tools.
|
|
@@ -7276,7 +8110,7 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
|
|
|
7276
8110
|
}
|
|
7277
8111
|
}
|
|
7278
8112
|
const suffixMessage = createSuffixMessage(messages);
|
|
7279
|
-
const nudgeGrowthTokens = config.compress?.nudgeGrowthTokens ??
|
|
8113
|
+
const nudgeGrowthTokens = config.compress?.nudgeGrowthTokens ?? resolveAdaptiveNudgeGrowth2(modelContextLimit);
|
|
7280
8114
|
const growthFloor = Math.max(
|
|
7281
8115
|
config.compress?.minNudgeGrowthFloor ?? 5e3,
|
|
7282
8116
|
(config.compress?.minNudgeGrowthRatio ?? 0.45) * nudgeGrowthTokens
|
|
@@ -7291,7 +8125,7 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
|
|
|
7291
8125
|
const hasPendingNudge = state.nudges.lastNudgeShownTokens !== void 0;
|
|
7292
8126
|
const effectiveThreshold = hasPendingNudge ? Math.floor(nudgeGrowthTokens / 2) : nudgeGrowthTokens;
|
|
7293
8127
|
const growthReference = state.nudges.lastNudgeShownTokens ?? state.nudges.lastPerMessageNudgeTokens;
|
|
7294
|
-
const decision =
|
|
8128
|
+
const decision = computeShouldNudge2({
|
|
7295
8129
|
currentTokens,
|
|
7296
8130
|
modelContextLimit,
|
|
7297
8131
|
overMinLimit,
|
|
@@ -8025,7 +8859,7 @@ function buildSchema3() {
|
|
|
8025
8859
|
function extractMessageId(m) {
|
|
8026
8860
|
return m.id ?? m.messageId ?? "";
|
|
8027
8861
|
}
|
|
8028
|
-
function
|
|
8862
|
+
function extractMessageText2(m) {
|
|
8029
8863
|
const msg = m;
|
|
8030
8864
|
const role = msg.role || msg.type || "unknown";
|
|
8031
8865
|
const content = typeof msg.content === "string" ? msg.content : typeof msg.text === "string" ? msg.text : JSON.stringify(msg.content || msg.text || "");
|
|
@@ -8078,7 +8912,7 @@ function createDecompressTool(ctx) {
|
|
|
8078
8912
|
}
|
|
8079
8913
|
}
|
|
8080
8914
|
const blockMessages = rawMessages.filter((m) => msgIdSet.has(extractMessageId(m)));
|
|
8081
|
-
const lines2 = blockMessages.map(
|
|
8915
|
+
const lines2 = blockMessages.map(extractMessageText2);
|
|
8082
8916
|
const { writeFile: writeFile3 } = await import("fs/promises");
|
|
8083
8917
|
const fileContent = lines2.length > 0 ? lines2.join("\n\n---\n\n") : activeBlocks[0]?.summary ?? "(no content available)";
|
|
8084
8918
|
await writeFile3(targetPath, fileContent, "utf-8");
|