acp-kernel 0.0.83 → 0.0.84
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/DESIGN.md +16 -3
- package/README.md +104 -3
- package/dist/absorb.d.ts.map +1 -1
- package/dist/boundaries.d.ts.map +1 -1
- package/dist/ccr.d.ts +116 -0
- package/dist/ccr.d.ts.map +1 -0
- package/dist/{chunk-VPRKYTHW.js → chunk-6RNVHY67.js} +3 -3
- package/dist/{chunk-5TQA7ZQM.js → chunk-MXL3G3BN.js} +13 -2
- package/dist/chunk-MXL3G3BN.js.map +1 -0
- package/dist/compress-tools.d.ts +104 -2
- package/dist/compress-tools.d.ts.map +1 -1
- package/dist/compress.d.ts +8 -0
- package/dist/compress.d.ts.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/content-store.d.ts +86 -0
- package/dist/content-store.d.ts.map +1 -0
- package/dist/crush.d.ts +142 -0
- package/dist/crush.d.ts.map +1 -0
- package/dist/image-compress.d.ts +156 -0
- package/dist/image-compress.d.ts.map +1 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2909 -636
- package/dist/index.js.map +1 -1
- package/dist/output-steering.d.ts +176 -0
- package/dist/output-steering.d.ts.map +1 -0
- package/dist/panel/index.js +1 -1
- package/dist/persist/index.js +4 -2
- package/dist/persist/index.js.map +1 -1
- package/dist/persist/state-merge.d.ts.map +1 -1
- package/dist/pipeline.d.ts +3 -0
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/rebuild.d.ts.map +1 -1
- package/dist/state.d.ts.map +1 -1
- package/dist/types.d.ts +82 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-5TQA7ZQM.js.map +0 -1
- /package/dist/{chunk-VPRKYTHW.js.map → chunk-6RNVHY67.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
resolvePrompts,
|
|
17
17
|
thinkingTokenValue,
|
|
18
18
|
viableRanges
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-6RNVHY67.js";
|
|
20
20
|
import {
|
|
21
21
|
activeBlocks,
|
|
22
22
|
advanceSurvival,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
coveredMessageIds,
|
|
27
27
|
createInitialState,
|
|
28
28
|
highestActiveTier
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-MXL3G3BN.js";
|
|
30
30
|
|
|
31
31
|
// src/refs.ts
|
|
32
32
|
var REF_WIDTH = 5;
|
|
@@ -372,9 +372,29 @@ function defaultConfig(modelContextLimit, overrides = {}) {
|
|
|
372
372
|
absorb: {
|
|
373
373
|
enabled: false,
|
|
374
374
|
toolName: "absorb",
|
|
375
|
-
|
|
375
|
+
// Raised 1000 → 4000 (issue #352): lossless CCR takes over large-result
|
|
376
|
+
// handling; absorb's forced distillation only fires above the new bar.
|
|
377
|
+
minToolTokens: 4e3,
|
|
376
378
|
contextThresholdPct: 0,
|
|
377
379
|
excludeTools: []
|
|
380
|
+
},
|
|
381
|
+
crush: {
|
|
382
|
+
enabled: false,
|
|
383
|
+
minReduction: 0.1
|
|
384
|
+
},
|
|
385
|
+
imageCompression: {
|
|
386
|
+
enabled: false,
|
|
387
|
+
minTokens: 512,
|
|
388
|
+
maxDimension: 1280,
|
|
389
|
+
quality: 80,
|
|
390
|
+
format: "webp"
|
|
391
|
+
},
|
|
392
|
+
ccr: {
|
|
393
|
+
enabled: false,
|
|
394
|
+
toolName: "acp_retrieve",
|
|
395
|
+
minToolTokens: 4e3,
|
|
396
|
+
excludeTools: [],
|
|
397
|
+
maxHeadChars: 96
|
|
378
398
|
}
|
|
379
399
|
};
|
|
380
400
|
return {
|
|
@@ -384,7 +404,10 @@ function defaultConfig(modelContextLimit, overrides = {}) {
|
|
|
384
404
|
nudge: { ...base.nudge, ...overrides.nudge },
|
|
385
405
|
truncate: { ...base.truncate, ...overrides.truncate },
|
|
386
406
|
compress: { ...base.compress, ...overrides.compress },
|
|
387
|
-
absorb: overrides.absorb ? { ...base.absorb, ...overrides.absorb } : base.absorb
|
|
407
|
+
absorb: overrides.absorb ? { ...base.absorb, ...overrides.absorb } : base.absorb,
|
|
408
|
+
crush: overrides.crush ? { ...base.crush, ...overrides.crush } : base.crush,
|
|
409
|
+
imageCompression: overrides.imageCompression ? { ...base.imageCompression, ...overrides.imageCompression } : base.imageCompression,
|
|
410
|
+
ccr: overrides.ccr ? { ...base.ccr, ...overrides.ccr } : base.ccr
|
|
388
411
|
};
|
|
389
412
|
}
|
|
390
413
|
function validateConfig(config) {
|
|
@@ -439,470 +462,84 @@ function validateConfig(config) {
|
|
|
439
462
|
errors.push("rules.maxRuleChars must be >= 1");
|
|
440
463
|
}
|
|
441
464
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
// src/boundaries.ts
|
|
446
|
-
var MESSAGE_REF_PATTERN = /^m0*(\d{1,5})$/;
|
|
447
|
-
var BLOCK_REF_PATTERN = /^b(\d{1,9})$/;
|
|
448
|
-
function parseBoundary(ref) {
|
|
449
|
-
const normalized = ref.trim().toLowerCase();
|
|
450
|
-
const messageMatch = MESSAGE_REF_PATTERN.exec(normalized);
|
|
451
|
-
if (messageMatch) {
|
|
452
|
-
const numericId = Number(messageMatch[1]);
|
|
453
|
-
if (numericId >= 1 && numericId <= 99999) {
|
|
454
|
-
return { kind: "message", numericId, raw: normalized };
|
|
465
|
+
if (config.crush) {
|
|
466
|
+
if (!Number.isFinite(config.crush.minReduction) || config.crush.minReduction <= 0 || config.crush.minReduction > 1) {
|
|
467
|
+
errors.push("crush.minReduction must be in (0, 1]");
|
|
455
468
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
this.name = "BoundaryNotFoundError";
|
|
471
|
-
this.code = "BOUNDARY_NOT_FOUND";
|
|
472
|
-
this.kind = kind;
|
|
473
|
-
this.endpoint = endpoint;
|
|
474
|
-
}
|
|
475
|
-
};
|
|
476
|
-
function resolveBoundaries(input) {
|
|
477
|
-
const start = parseBoundary(input.startRef);
|
|
478
|
-
const end = parseBoundary(input.endRef);
|
|
479
|
-
if (!start || !end) {
|
|
480
|
-
throw new Error(
|
|
481
|
-
`Invalid boundary ref(s): startId="${input.startRef}", endId="${input.endRef}". Use mNNNNN or bN.`
|
|
482
|
-
);
|
|
483
|
-
}
|
|
484
|
-
const indexByMessageId = /* @__PURE__ */ new Map();
|
|
485
|
-
input.messages.forEach(
|
|
486
|
-
(message, index) => indexByMessageId.set(message.id, index)
|
|
487
|
-
);
|
|
488
|
-
let snappedBoundaries = [];
|
|
489
|
-
const startAnchor = resolveAnchorIndex(
|
|
490
|
-
start,
|
|
491
|
-
input.state,
|
|
492
|
-
indexByMessageId,
|
|
493
|
-
"start"
|
|
494
|
-
);
|
|
495
|
-
if (startAnchor.snapped) snappedBoundaries.push(startAnchor.snapped);
|
|
496
|
-
const endAnchor = resolveAnchorIndex(
|
|
497
|
-
end,
|
|
498
|
-
input.state,
|
|
499
|
-
indexByMessageId,
|
|
500
|
-
"end"
|
|
501
|
-
);
|
|
502
|
-
if (endAnchor.snapped) snappedBoundaries.push(endAnchor.snapped);
|
|
503
|
-
let startIndex = startAnchor.index;
|
|
504
|
-
let endIndex = endAnchor.index;
|
|
505
|
-
if (startIndex > endIndex) {
|
|
506
|
-
[startIndex, endIndex] = [endIndex, startIndex];
|
|
507
|
-
}
|
|
508
|
-
const messageIds = [];
|
|
509
|
-
for (let index = startIndex; index <= endIndex; index++) {
|
|
510
|
-
const message = input.messages[index];
|
|
511
|
-
if (message && !isRenderedSummaryMessage(message))
|
|
512
|
-
messageIds.push(message.id);
|
|
513
|
-
}
|
|
514
|
-
const boundaryKind = start.kind === "block" || end.kind === "block" ? "block" : "message";
|
|
515
|
-
const nestedBlockIds = [];
|
|
516
|
-
const nestedSeen = /* @__PURE__ */ new Set();
|
|
517
|
-
for (const block of activeBlocks(input.state)) {
|
|
518
|
-
if (blockVisibleInRange(block, indexByMessageId, startIndex, endIndex)) {
|
|
519
|
-
if (!nestedSeen.has(block.blockId)) {
|
|
520
|
-
nestedSeen.add(block.blockId);
|
|
521
|
-
nestedBlockIds.push(block.blockId);
|
|
469
|
+
if (config.crush.strategies) {
|
|
470
|
+
for (const [id, ov] of Object.entries(config.crush.strategies)) {
|
|
471
|
+
if (!ov || typeof ov !== "object" || Array.isArray(ov)) {
|
|
472
|
+
errors.push(`crush.strategies.${id} must be an object`);
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
if (ov.enabled !== void 0 && typeof ov.enabled !== "boolean") {
|
|
476
|
+
errors.push(`crush.strategies.${id}.enabled must be a boolean`);
|
|
477
|
+
}
|
|
478
|
+
if (ov.excludeTools !== void 0 && (!Array.isArray(ov.excludeTools) || ov.excludeTools.some((t) => typeof t !== "string"))) {
|
|
479
|
+
errors.push(
|
|
480
|
+
`crush.strategies.${id}.excludeTools must be a string array`
|
|
481
|
+
);
|
|
482
|
+
}
|
|
522
483
|
}
|
|
523
484
|
}
|
|
524
485
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
messageIds,
|
|
530
|
-
nestedBlockIds,
|
|
531
|
-
boundaryKind,
|
|
532
|
-
protectedGaps,
|
|
533
|
-
snappedBoundaries
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
function resolveAnchorIndex(boundary, state, indexByMessageId, endpoint) {
|
|
537
|
-
const label = endpoint === "start" ? "startId" : "endId";
|
|
538
|
-
if (boundary.kind === "message") {
|
|
539
|
-
const rawId = state.messageRefs.byRef[boundary.raw] ?? state.messageRefs.byRef[formatPaddedRef(boundary.numericId)];
|
|
540
|
-
if (!rawId) {
|
|
541
|
-
throw new BoundaryNotFoundError(
|
|
542
|
-
"unknown",
|
|
543
|
-
endpoint,
|
|
544
|
-
`${label}="${boundary.raw}" does not exist in this session (typo or wrong session) \u2014 run acp_status for current refs.`
|
|
545
|
-
);
|
|
486
|
+
if (config.imageCompression) {
|
|
487
|
+
const ic = config.imageCompression;
|
|
488
|
+
if (ic.minTokens !== void 0 && (!Number.isFinite(ic.minTokens) || ic.minTokens < 0)) {
|
|
489
|
+
errors.push("imageCompression.minTokens must be finite and >= 0");
|
|
546
490
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
return { index, snapped: null };
|
|
491
|
+
if (ic.maxDimension !== void 0 && (!Number.isInteger(ic.maxDimension) || ic.maxDimension < 16)) {
|
|
492
|
+
errors.push("imageCompression.maxDimension must be an integer >= 16");
|
|
550
493
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
return {
|
|
554
|
-
index: owner2,
|
|
555
|
-
snapped: `${label}="${boundary.raw}" refers to a message already compressed into an active block \u2014 anchored to the active block covering it instead.`
|
|
556
|
-
};
|
|
494
|
+
if (ic.quality !== void 0 && (!Number.isFinite(ic.quality) || ic.quality < 1 || ic.quality > 100)) {
|
|
495
|
+
errors.push("imageCompression.quality must be in [1, 100]");
|
|
557
496
|
}
|
|
558
|
-
|
|
559
|
-
"
|
|
560
|
-
endpoint,
|
|
561
|
-
`${label}="${boundary.raw}" not found in visible context (likely consumed by an existing block).`
|
|
562
|
-
);
|
|
563
|
-
}
|
|
564
|
-
const block = blockById(state, `b${boundary.numericId}`);
|
|
565
|
-
if (!block) {
|
|
566
|
-
throw new BoundaryNotFoundError(
|
|
567
|
-
"unknown",
|
|
568
|
-
endpoint,
|
|
569
|
-
`${label}="b${boundary.numericId}" does not exist in this session (typo or wrong session) \u2014 run acp_status for current refs.`
|
|
570
|
-
);
|
|
571
|
-
}
|
|
572
|
-
if (block.active) {
|
|
573
|
-
const anchor = visibleBlockAnchor(block, indexByMessageId);
|
|
574
|
-
if (anchor !== null) {
|
|
575
|
-
return { index: anchor, snapped: null };
|
|
497
|
+
if (ic.format !== void 0 && ic.format !== "webp" && ic.format !== "jpeg" && ic.format !== "png") {
|
|
498
|
+
errors.push('imageCompression.format must be "webp", "jpeg", or "png"');
|
|
576
499
|
}
|
|
577
500
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
indexByMessageId
|
|
582
|
-
);
|
|
583
|
-
if (owner !== null) {
|
|
584
|
-
return {
|
|
585
|
-
index: owner,
|
|
586
|
-
snapped: `${label}="b${boundary.numericId}" was consumed by a higher-tier block \u2014 anchored to the active block covering its content instead.`
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
if (!block.active) {
|
|
590
|
-
throw new BoundaryNotFoundError(
|
|
591
|
-
"consumed",
|
|
592
|
-
endpoint,
|
|
593
|
-
`${label}="b${boundary.numericId}" not found in visible context (block distilled/consumed by a higher-tier block).`
|
|
594
|
-
);
|
|
595
|
-
}
|
|
596
|
-
throw new BoundaryNotFoundError(
|
|
597
|
-
"consumed",
|
|
598
|
-
endpoint,
|
|
599
|
-
`${label}="b${boundary.numericId}" is an active block but none of its content (raw messages or rendered summary) is visible in the current context \u2014 run acp_status to verify.`
|
|
600
|
-
);
|
|
601
|
-
}
|
|
602
|
-
function activeOwnerAnchor(state, ownedIds, indexByMessageId) {
|
|
603
|
-
if (ownedIds.length === 0) return null;
|
|
604
|
-
const owned = new Set(ownedIds);
|
|
605
|
-
let best = null;
|
|
606
|
-
for (const block of state.blocks) {
|
|
607
|
-
if (!block.active) continue;
|
|
608
|
-
const inherited = inheritedContentIds(state, block);
|
|
609
|
-
let ownsInherited = false;
|
|
610
|
-
for (const id of owned) {
|
|
611
|
-
if (inherited.has(id)) {
|
|
612
|
-
ownsInherited = true;
|
|
613
|
-
break;
|
|
614
|
-
}
|
|
501
|
+
if (config.ccr) {
|
|
502
|
+
if (config.ccr.enabled && !config.ccr.toolName) {
|
|
503
|
+
errors.push("ccr.toolName must be a non-empty string when enabled");
|
|
615
504
|
}
|
|
616
|
-
if (!
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
if (
|
|
620
|
-
|
|
505
|
+
if (!Number.isFinite(config.ccr.minToolTokens) || config.ccr.minToolTokens < 0) {
|
|
506
|
+
errors.push("ccr.minToolTokens must be >= 0");
|
|
507
|
+
}
|
|
508
|
+
if (!Number.isFinite(config.ccr.maxHeadChars) || config.ccr.maxHeadChars < 0) {
|
|
509
|
+
errors.push("ccr.maxHeadChars must be >= 0");
|
|
621
510
|
}
|
|
622
511
|
}
|
|
623
|
-
return
|
|
512
|
+
return errors;
|
|
624
513
|
}
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
514
|
+
|
|
515
|
+
// src/surface-config.ts
|
|
516
|
+
function applySectionOverrides(sections, overrides) {
|
|
517
|
+
const out = [];
|
|
518
|
+
for (const [key, text] of sections) {
|
|
519
|
+
const o = overrides?.[key];
|
|
520
|
+
if (o === null) continue;
|
|
521
|
+
out.push(typeof o === "string" ? o : text);
|
|
631
522
|
}
|
|
632
|
-
return
|
|
633
|
-
}
|
|
634
|
-
function formatPaddedRef(index) {
|
|
635
|
-
return `m${String(index).padStart(5, "0")}`;
|
|
636
|
-
}
|
|
637
|
-
function visibleBlockAnchor(block, indexByMessageId) {
|
|
638
|
-
const summaryIndex = indexByMessageId.get(summaryMessageId(block.blockId));
|
|
639
|
-
if (summaryIndex !== void 0) return summaryIndex;
|
|
640
|
-
return earliestIndexOfIds(block.effectiveMessageIds, indexByMessageId);
|
|
523
|
+
return out;
|
|
641
524
|
}
|
|
642
|
-
function
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
return true;
|
|
646
|
-
}
|
|
647
|
-
const rawIndex = earliestIndexOfIds(
|
|
648
|
-
block.effectiveMessageIds,
|
|
649
|
-
indexByMessageId
|
|
650
|
-
);
|
|
651
|
-
return rawIndex !== null && rawIndex >= startIndex && rawIndex <= endIndex;
|
|
525
|
+
function cloneWithDescriptions(schema, overrides) {
|
|
526
|
+
if (Object.keys(overrides).length === 0) return schema;
|
|
527
|
+
return cloneNode(schema, overrides);
|
|
652
528
|
}
|
|
653
|
-
function
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
const
|
|
657
|
-
|
|
658
|
-
|
|
529
|
+
function cloneNode(node, overrides) {
|
|
530
|
+
if (Array.isArray(node)) return node.map((item) => cloneNode(item, overrides));
|
|
531
|
+
if (node && typeof node === "object") {
|
|
532
|
+
const out = {};
|
|
533
|
+
for (const [name, value] of Object.entries(node)) {
|
|
534
|
+
const cloned = cloneNode(value, overrides);
|
|
535
|
+
if (Object.hasOwn(overrides, name) && cloned && typeof cloned === "object" && !Array.isArray(cloned)) {
|
|
536
|
+
cloned.description = overrides[name];
|
|
537
|
+
}
|
|
538
|
+
out[name] = cloned;
|
|
659
539
|
}
|
|
540
|
+
return out;
|
|
660
541
|
}
|
|
661
|
-
return
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
// src/truncate-tools.ts
|
|
665
|
-
var TRUNCATION_MARKER = "[truncated for context space]";
|
|
666
|
-
var DEFAULTS = {
|
|
667
|
-
minOutputTokens: 1e3,
|
|
668
|
-
keepPrefixChars: 2e3,
|
|
669
|
-
keepSuffixChars: 2e3,
|
|
670
|
-
protectRecentMessages: 3
|
|
671
|
-
};
|
|
672
|
-
function truncateLargeToolOutputs(messages, tokenCount, config, countTokens, options = {}) {
|
|
673
|
-
const opts = { ...DEFAULTS, ...options };
|
|
674
|
-
const limit = config.modelContextLimit;
|
|
675
|
-
if (limit <= 0 || tokenCount < config.truncate.threshold * limit) {
|
|
676
|
-
return { messages, truncatedCount: 0, savedTokens: 0, candidatesFound: 0 };
|
|
677
|
-
}
|
|
678
|
-
const protectedIndex = messages.length - opts.protectRecentMessages;
|
|
679
|
-
const findCandidates = (predicate) => {
|
|
680
|
-
const found = [];
|
|
681
|
-
for (let i = 0; i < protectedIndex; i++) {
|
|
682
|
-
const message = messages[i];
|
|
683
|
-
if (!predicate(message)) continue;
|
|
684
|
-
const text = message.text ?? "";
|
|
685
|
-
if (text.length === 0 || text.includes(TRUNCATION_MARKER)) continue;
|
|
686
|
-
if (countTokens(text) < opts.minOutputTokens) continue;
|
|
687
|
-
found.push(message);
|
|
688
|
-
}
|
|
689
|
-
return found.sort(
|
|
690
|
-
(a, b) => countTokens(b.text ?? "") - countTokens(a.text ?? "")
|
|
691
|
-
);
|
|
692
|
-
};
|
|
693
|
-
const toolResults = findCandidates((m) => m.contentType === "tool-result");
|
|
694
|
-
const textMessages = options.includeTextMessages ? findCandidates(
|
|
695
|
-
(m) => m.contentType === "text" && (m.role === "user" || m.role === "assistant") && !isRenderedSummaryMessage(m) && !m.text?.startsWith(SUMMARY_HEADER)
|
|
696
|
-
) : [];
|
|
697
|
-
const candidatesFound = toolResults.length + textMessages.length;
|
|
698
|
-
let truncatedCount = 0;
|
|
699
|
-
let savedTokens = 0;
|
|
700
|
-
let remaining = tokenCount;
|
|
701
|
-
const targetTokens = config.truncate.threshold * limit * 0.9;
|
|
702
|
-
const replacements = /* @__PURE__ */ new Map();
|
|
703
|
-
const applyTo = (candidates) => {
|
|
704
|
-
for (const candidate of candidates) {
|
|
705
|
-
if (remaining <= targetTokens) break;
|
|
706
|
-
const original = candidate.text ?? "";
|
|
707
|
-
const tokens = countTokens(original);
|
|
708
|
-
if (original.length <= opts.keepPrefixChars + opts.keepSuffixChars) {
|
|
709
|
-
continue;
|
|
710
|
-
}
|
|
711
|
-
const prefix = clampPrefix(original, opts.keepPrefixChars);
|
|
712
|
-
const suffix = clampWindow(original, original.length - opts.keepSuffixChars, original.length);
|
|
713
|
-
const replacement = `${prefix}
|
|
714
|
-
|
|
715
|
-
...${TRUNCATION_MARKER} \u2014 original ~${tokens} tokens]...
|
|
716
|
-
|
|
717
|
-
${suffix}`;
|
|
718
|
-
replacements.set(candidate.id, replacement);
|
|
719
|
-
truncatedCount++;
|
|
720
|
-
remaining -= tokens - countTokens(replacement);
|
|
721
|
-
savedTokens += tokens - countTokens(replacement);
|
|
722
|
-
}
|
|
723
|
-
};
|
|
724
|
-
applyTo(toolResults);
|
|
725
|
-
applyTo(textMessages);
|
|
726
|
-
if (replacements.size === 0) {
|
|
727
|
-
return { messages, truncatedCount: 0, savedTokens: 0, candidatesFound };
|
|
728
|
-
}
|
|
729
|
-
return {
|
|
730
|
-
messages: messages.map(
|
|
731
|
-
(m) => replacements.has(m.id) ? { ...m, text: replacements.get(m.id) } : m
|
|
732
|
-
),
|
|
733
|
-
truncatedCount,
|
|
734
|
-
savedTokens,
|
|
735
|
-
candidatesFound
|
|
736
|
-
};
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
// src/hide-consumed.ts
|
|
740
|
-
var KEEP_LAST_ORPHANED = 2;
|
|
741
|
-
function rangeKey(startRef, endRef) {
|
|
742
|
-
return `${startRef}::${endRef}`;
|
|
743
|
-
}
|
|
744
|
-
function parseCallText(text) {
|
|
745
|
-
const raw = text ?? "";
|
|
746
|
-
const start = raw.indexOf("{");
|
|
747
|
-
if (start < 0) return null;
|
|
748
|
-
let parsed;
|
|
749
|
-
try {
|
|
750
|
-
parsed = JSON.parse(raw.slice(start));
|
|
751
|
-
} catch {
|
|
752
|
-
return null;
|
|
753
|
-
}
|
|
754
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
755
|
-
const obj = parsed;
|
|
756
|
-
let content = null;
|
|
757
|
-
let contentWasString = false;
|
|
758
|
-
if (Array.isArray(obj.content)) {
|
|
759
|
-
content = obj.content;
|
|
760
|
-
} else if (typeof obj.content === "string") {
|
|
761
|
-
contentWasString = true;
|
|
762
|
-
try {
|
|
763
|
-
const inner = JSON.parse(obj.content);
|
|
764
|
-
if (Array.isArray(inner)) content = inner;
|
|
765
|
-
} catch {
|
|
766
|
-
content = null;
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
if (!content || content.length === 0) return null;
|
|
770
|
-
return { prefix: raw.slice(0, start), obj, content, contentWasString };
|
|
771
|
-
}
|
|
772
|
-
function rewriteCompressText(text, liveKeys) {
|
|
773
|
-
const parsed = parseCallText(text);
|
|
774
|
-
if (!parsed) return null;
|
|
775
|
-
const { prefix, obj, content, contentWasString } = parsed;
|
|
776
|
-
const kept = content.filter((entry) => {
|
|
777
|
-
if (!entry || typeof entry !== "object") return false;
|
|
778
|
-
const e = entry;
|
|
779
|
-
const s = typeof e.startId === "string" ? e.startId : typeof e.messageId === "string" ? e.messageId : "";
|
|
780
|
-
const end = typeof e.endId === "string" ? e.endId : typeof e.messageId === "string" ? e.messageId : "";
|
|
781
|
-
return liveKeys.has(rangeKey(s, end));
|
|
782
|
-
});
|
|
783
|
-
if (kept.length === 0) return null;
|
|
784
|
-
return prefix + serializeCompacted(obj, kept, contentWasString).text;
|
|
785
|
-
}
|
|
786
|
-
var SUMMARY_STUB_CHARS = 200;
|
|
787
|
-
function compactEntry(entry) {
|
|
788
|
-
if (!entry || typeof entry !== "object") return entry;
|
|
789
|
-
const e = entry;
|
|
790
|
-
if (typeof e.summary !== "string" || e.summary.length <= SUMMARY_STUB_CHARS) return entry;
|
|
791
|
-
return { ...e, summary: `${clampPrefix(e.summary, SUMMARY_STUB_CHARS - 1)}\u2026` };
|
|
792
|
-
}
|
|
793
|
-
function serializeCompacted(obj, content, contentWasString) {
|
|
794
|
-
let changed = false;
|
|
795
|
-
const compacted = content.map((entry) => {
|
|
796
|
-
const out = compactEntry(entry);
|
|
797
|
-
if (out !== entry) changed = true;
|
|
798
|
-
return out;
|
|
799
|
-
});
|
|
800
|
-
const outContent = contentWasString ? JSON.stringify(compacted) : compacted;
|
|
801
|
-
return { text: JSON.stringify({ ...obj, content: outContent }), changed };
|
|
802
|
-
}
|
|
803
|
-
function compactCompressText(text) {
|
|
804
|
-
const parsed = parseCallText(text);
|
|
805
|
-
if (!parsed) return null;
|
|
806
|
-
const { prefix, obj, content, contentWasString } = parsed;
|
|
807
|
-
const { text: out, changed } = serializeCompacted(obj, content, contentWasString);
|
|
808
|
-
return changed ? prefix + out : null;
|
|
809
|
-
}
|
|
810
|
-
function hideConsumedCompressCalls(state, messages) {
|
|
811
|
-
const allBlockCallIds = /* @__PURE__ */ new Set();
|
|
812
|
-
const activeCallIds = /* @__PURE__ */ new Set();
|
|
813
|
-
const liveRangeKeysByCallId = /* @__PURE__ */ new Map();
|
|
814
|
-
const legacyLiveByCallId = /* @__PURE__ */ new Set();
|
|
815
|
-
for (const block of state.blocks) {
|
|
816
|
-
if (!block.compressCallId) continue;
|
|
817
|
-
allBlockCallIds.add(block.compressCallId);
|
|
818
|
-
if (!block.active) continue;
|
|
819
|
-
activeCallIds.add(block.compressCallId);
|
|
820
|
-
if (block.startRef === void 0 || block.endRef === void 0) {
|
|
821
|
-
legacyLiveByCallId.add(block.compressCallId);
|
|
822
|
-
continue;
|
|
823
|
-
}
|
|
824
|
-
let keys = liveRangeKeysByCallId.get(block.compressCallId);
|
|
825
|
-
if (!keys) {
|
|
826
|
-
keys = /* @__PURE__ */ new Set();
|
|
827
|
-
liveRangeKeysByCallId.set(block.compressCallId, keys);
|
|
828
|
-
}
|
|
829
|
-
keys.add(rangeKey(block.startRef, block.endRef));
|
|
830
|
-
}
|
|
831
|
-
const lastOrphanedCallIds = [];
|
|
832
|
-
for (let i = messages.length - 1; i >= 0 && lastOrphanedCallIds.length < KEEP_LAST_ORPHANED; i--) {
|
|
833
|
-
const message = messages[i];
|
|
834
|
-
if (message.toolName !== "compress" || message.contentType !== "tool-call") continue;
|
|
835
|
-
const callId = message.toolCallId;
|
|
836
|
-
if (callId && !allBlockCallIds.has(callId)) {
|
|
837
|
-
lastOrphanedCallIds.push(callId);
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
const keepCallIds = /* @__PURE__ */ new Set([...activeCallIds, ...lastOrphanedCallIds]);
|
|
841
|
-
const hiddenCallIds = /* @__PURE__ */ new Set();
|
|
842
|
-
for (const message of messages) {
|
|
843
|
-
if (message.toolName === "compress" && message.contentType === "tool-call" && (!message.toolCallId || !keepCallIds.has(message.toolCallId))) {
|
|
844
|
-
if (message.toolCallId) hiddenCallIds.add(message.toolCallId);
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
let hidden = 0;
|
|
848
|
-
const result = [];
|
|
849
|
-
for (const message of messages) {
|
|
850
|
-
if (message.toolName === "compress" && message.contentType === "tool-call" && (!message.toolCallId || !keepCallIds.has(message.toolCallId))) {
|
|
851
|
-
hidden++;
|
|
852
|
-
continue;
|
|
853
|
-
}
|
|
854
|
-
if (message.contentType === "tool-result" && message.toolCallId && hiddenCallIds.has(message.toolCallId)) {
|
|
855
|
-
hidden++;
|
|
856
|
-
continue;
|
|
857
|
-
}
|
|
858
|
-
if (message.toolName === "compress" && message.contentType === "tool-call" && message.toolCallId && keepCallIds.has(message.toolCallId)) {
|
|
859
|
-
const liveKeys = liveRangeKeysByCallId.get(message.toolCallId);
|
|
860
|
-
if (liveKeys && liveKeys.size > 0 && !legacyLiveByCallId.has(message.toolCallId)) {
|
|
861
|
-
const rewritten = rewriteCompressText(message.text, liveKeys);
|
|
862
|
-
if (rewritten !== null) {
|
|
863
|
-
result.push({ ...message, text: rewritten });
|
|
864
|
-
continue;
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
const compacted = compactCompressText(message.text);
|
|
868
|
-
if (compacted !== null) {
|
|
869
|
-
result.push({ ...message, text: compacted });
|
|
870
|
-
continue;
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
result.push(message);
|
|
874
|
-
}
|
|
875
|
-
return { messages: result, hidden };
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
// src/surface-config.ts
|
|
879
|
-
function applySectionOverrides(sections, overrides) {
|
|
880
|
-
const out = [];
|
|
881
|
-
for (const [key, text] of sections) {
|
|
882
|
-
const o = overrides?.[key];
|
|
883
|
-
if (o === null) continue;
|
|
884
|
-
out.push(typeof o === "string" ? o : text);
|
|
885
|
-
}
|
|
886
|
-
return out;
|
|
887
|
-
}
|
|
888
|
-
function cloneWithDescriptions(schema, overrides) {
|
|
889
|
-
if (Object.keys(overrides).length === 0) return schema;
|
|
890
|
-
return cloneNode(schema, overrides);
|
|
891
|
-
}
|
|
892
|
-
function cloneNode(node, overrides) {
|
|
893
|
-
if (Array.isArray(node)) return node.map((item) => cloneNode(item, overrides));
|
|
894
|
-
if (node && typeof node === "object") {
|
|
895
|
-
const out = {};
|
|
896
|
-
for (const [name, value] of Object.entries(node)) {
|
|
897
|
-
const cloned = cloneNode(value, overrides);
|
|
898
|
-
if (Object.hasOwn(overrides, name) && cloned && typeof cloned === "object" && !Array.isArray(cloned)) {
|
|
899
|
-
cloned.description = overrides[name];
|
|
900
|
-
}
|
|
901
|
-
out[name] = cloned;
|
|
902
|
-
}
|
|
903
|
-
return out;
|
|
904
|
-
}
|
|
905
|
-
return node;
|
|
542
|
+
return node;
|
|
906
543
|
}
|
|
907
544
|
function applyAcpToolOverrides(tools, overrides) {
|
|
908
545
|
if (!overrides) return [...tools];
|
|
@@ -942,6 +579,7 @@ var SEARCH_CONTEXT_TOOL_NAME = "search_context";
|
|
|
942
579
|
var ACP_STATUS_TOOL_NAME = "acp_status";
|
|
943
580
|
var ACP_CACHE_TOOL_NAME = "acp_cache";
|
|
944
581
|
var ABSORB_TOOL_NAME = "absorb";
|
|
582
|
+
var IMAGE_FULL_TOOL_NAME = "image_full";
|
|
945
583
|
var ACP_TEXT_OPEN = "<acp_compress>";
|
|
946
584
|
var ACP_TEXT_CLOSE = "</acp_compress>";
|
|
947
585
|
var ACP_STATUS_OPEN = "<acp_status>";
|
|
@@ -1475,6 +1113,67 @@ var ABSORB_TOOL_GOOGLE = {
|
|
|
1475
1113
|
description: ABSORB_TOOL_DESCRIPTION,
|
|
1476
1114
|
parameters: ABSORB_PARAMETERS
|
|
1477
1115
|
};
|
|
1116
|
+
var IMAGE_FULL_TOOL_DESCRIPTION = 'Restore original-resolution images for a previously downscaled message. Call when you cannot read details (text, colors, alignment) in a reduced image: pass the message ref ("mNNNNN") from the [Downscaled screenshots] note. Full resolution applies for the rest of this session.';
|
|
1117
|
+
var IMAGE_FULL_PARAMETERS = {
|
|
1118
|
+
type: "object",
|
|
1119
|
+
properties: {
|
|
1120
|
+
ref: {
|
|
1121
|
+
type: "string",
|
|
1122
|
+
description: "mNNNNN ref of the message whose image(s) should be restored to full resolution"
|
|
1123
|
+
}
|
|
1124
|
+
},
|
|
1125
|
+
required: ["ref"]
|
|
1126
|
+
};
|
|
1127
|
+
var RETRIEVE_TOOL_NAME = "acp_retrieve";
|
|
1128
|
+
var RETRIEVE_TOOL_DESCRIPTION = "Retrieve the full original text of a stored tool result. REQUIRED when you need details from a placeholder that starts with \u{1F4E6} [acp-stored #mNNNNN: pass its ref. Returns the exact original text; the placeholder stays in context. Unknown refs return not-found.";
|
|
1129
|
+
var RETRIEVE_PARAMETERS = {
|
|
1130
|
+
type: "object",
|
|
1131
|
+
properties: {
|
|
1132
|
+
ref: {
|
|
1133
|
+
type: "string",
|
|
1134
|
+
description: "mNNNNN ref shown in the \u{1F4E6} [acp-stored placeholder"
|
|
1135
|
+
}
|
|
1136
|
+
},
|
|
1137
|
+
required: ["ref"]
|
|
1138
|
+
};
|
|
1139
|
+
var IMAGE_FULL_TOOL = {
|
|
1140
|
+
name: IMAGE_FULL_TOOL_NAME,
|
|
1141
|
+
description: IMAGE_FULL_TOOL_DESCRIPTION,
|
|
1142
|
+
input_schema: IMAGE_FULL_PARAMETERS
|
|
1143
|
+
};
|
|
1144
|
+
var IMAGE_FULL_TOOL_OPENAI = {
|
|
1145
|
+
type: "function",
|
|
1146
|
+
function: {
|
|
1147
|
+
name: IMAGE_FULL_TOOL_NAME,
|
|
1148
|
+
description: IMAGE_FULL_TOOL_DESCRIPTION,
|
|
1149
|
+
parameters: IMAGE_FULL_PARAMETERS
|
|
1150
|
+
}
|
|
1151
|
+
};
|
|
1152
|
+
var IMAGE_FULL_TOOL_RESPONSES = {
|
|
1153
|
+
type: "function",
|
|
1154
|
+
name: IMAGE_FULL_TOOL_OPENAI.function.name,
|
|
1155
|
+
description: IMAGE_FULL_TOOL_OPENAI.function.description,
|
|
1156
|
+
parameters: IMAGE_FULL_TOOL_OPENAI.function.parameters
|
|
1157
|
+
};
|
|
1158
|
+
var RETRIEVE_TOOL = {
|
|
1159
|
+
name: RETRIEVE_TOOL_NAME,
|
|
1160
|
+
description: RETRIEVE_TOOL_DESCRIPTION,
|
|
1161
|
+
input_schema: RETRIEVE_PARAMETERS
|
|
1162
|
+
};
|
|
1163
|
+
var RETRIEVE_TOOL_OPENAI = {
|
|
1164
|
+
type: "function",
|
|
1165
|
+
function: {
|
|
1166
|
+
name: RETRIEVE_TOOL_NAME,
|
|
1167
|
+
description: RETRIEVE_TOOL_DESCRIPTION,
|
|
1168
|
+
parameters: RETRIEVE_PARAMETERS
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
1171
|
+
var RETRIEVE_TOOL_RESPONSES = {
|
|
1172
|
+
type: "function",
|
|
1173
|
+
name: RETRIEVE_TOOL_NAME,
|
|
1174
|
+
description: RETRIEVE_TOOL_DESCRIPTION,
|
|
1175
|
+
parameters: RETRIEVE_PARAMETERS
|
|
1176
|
+
};
|
|
1478
1177
|
|
|
1479
1178
|
// src/protected.ts
|
|
1480
1179
|
var ALWAYS_PROTECTED_TOOLS = ["compress", "acp_rule"];
|
|
@@ -1552,223 +1251,2268 @@ function isMessageLatestProtected(msg, latest) {
|
|
|
1552
1251
|
return false;
|
|
1553
1252
|
}
|
|
1554
1253
|
|
|
1555
|
-
// src/
|
|
1556
|
-
|
|
1557
|
-
|
|
1254
|
+
// src/content-store.ts
|
|
1255
|
+
import { createHash } from "crypto";
|
|
1256
|
+
function createContentStore() {
|
|
1257
|
+
return { version: 1, byHash: {}, byRef: {} };
|
|
1258
|
+
}
|
|
1259
|
+
function hashContent(text) {
|
|
1260
|
+
return createHash("sha256").update(text, "utf8").digest("hex");
|
|
1261
|
+
}
|
|
1262
|
+
function storeOriginal(store, spec) {
|
|
1263
|
+
if (!spec.ref || store.byRef[spec.ref]) return store;
|
|
1264
|
+
const hash = hashContent(spec.text);
|
|
1265
|
+
const entry = {
|
|
1266
|
+
hash,
|
|
1267
|
+
rawId: spec.rawId,
|
|
1268
|
+
kind: spec.kind,
|
|
1269
|
+
tokens: spec.tokens,
|
|
1270
|
+
chars: spec.text.length,
|
|
1271
|
+
head: spec.head
|
|
1272
|
+
};
|
|
1273
|
+
if (spec.toolName !== void 0) entry.toolName = spec.toolName;
|
|
1274
|
+
const byHash = store.byHash[hash] === void 0 ? { ...store.byHash, [hash]: spec.text } : store.byHash;
|
|
1275
|
+
return { ...store, byHash, byRef: { ...store.byRef, [spec.ref]: entry } };
|
|
1276
|
+
}
|
|
1277
|
+
function retrieveByRef(store, ref) {
|
|
1278
|
+
const entry = store.byRef[ref];
|
|
1279
|
+
if (!entry) return { ok: false, reason: "not-found" };
|
|
1280
|
+
const text = store.byHash[entry.hash];
|
|
1281
|
+
if (text === void 0) return { ok: false, reason: "not-found" };
|
|
1282
|
+
return { ok: true, text, entry };
|
|
1283
|
+
}
|
|
1284
|
+
function hasStoredRef(store, ref) {
|
|
1285
|
+
return store.byRef[ref] !== void 0;
|
|
1286
|
+
}
|
|
1287
|
+
function contentStoreStats(store) {
|
|
1288
|
+
let totalChars = 0;
|
|
1289
|
+
for (const text of Object.values(store.byHash)) totalChars += text.length;
|
|
1290
|
+
return {
|
|
1291
|
+
entries: Object.keys(store.byRef).length,
|
|
1292
|
+
uniqueContents: Object.keys(store.byHash).length,
|
|
1293
|
+
totalChars
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
// src/ccr.ts
|
|
1298
|
+
var RETRIEVE_TOOL_NAME2 = "acp_retrieve";
|
|
1299
|
+
var DEFAULT_CCR_CONFIG = {
|
|
1558
1300
|
enabled: false,
|
|
1559
|
-
toolName:
|
|
1560
|
-
minToolTokens:
|
|
1561
|
-
|
|
1562
|
-
|
|
1301
|
+
toolName: RETRIEVE_TOOL_NAME2,
|
|
1302
|
+
minToolTokens: 4e3,
|
|
1303
|
+
excludeTools: [],
|
|
1304
|
+
maxHeadChars: 96
|
|
1563
1305
|
};
|
|
1564
|
-
function
|
|
1565
|
-
return { ...
|
|
1306
|
+
function resolveCcrConfig(config) {
|
|
1307
|
+
return { ...DEFAULT_CCR_CONFIG, ...config.ccr };
|
|
1308
|
+
}
|
|
1309
|
+
var STORED_PLACEHOLDER_MARKER = "[acp-stored";
|
|
1310
|
+
var RETRIEVED_ID_PREFIX = "acp_retrieved_";
|
|
1311
|
+
var KIND_LABELS = {
|
|
1312
|
+
bash: "shell output",
|
|
1313
|
+
shell: "shell output",
|
|
1314
|
+
exec: "shell output",
|
|
1315
|
+
execute_command: "shell output",
|
|
1316
|
+
run: "shell output",
|
|
1317
|
+
terminal: "shell output",
|
|
1318
|
+
read: "file read",
|
|
1319
|
+
read_file: "file read",
|
|
1320
|
+
cat: "file read",
|
|
1321
|
+
open: "file read",
|
|
1322
|
+
grep: "search output",
|
|
1323
|
+
rg: "search output",
|
|
1324
|
+
search: "search output",
|
|
1325
|
+
glob: "search output",
|
|
1326
|
+
find: "search output",
|
|
1327
|
+
webfetch: "web fetch",
|
|
1328
|
+
web_fetch: "web fetch",
|
|
1329
|
+
fetch: "web fetch",
|
|
1330
|
+
curl: "web fetch"
|
|
1331
|
+
};
|
|
1332
|
+
function classifyKind(toolName) {
|
|
1333
|
+
if (!toolName) return "tool result";
|
|
1334
|
+
return KIND_LABELS[toolName.toLowerCase()] ?? "tool result";
|
|
1335
|
+
}
|
|
1336
|
+
function groupThousands(value) {
|
|
1337
|
+
return String(Math.max(0, Math.round(value))).replace(
|
|
1338
|
+
/\B(?=(\d{3})+(?!\d))/g,
|
|
1339
|
+
","
|
|
1340
|
+
);
|
|
1566
1341
|
}
|
|
1567
|
-
function
|
|
1568
|
-
|
|
1569
|
-
if (
|
|
1570
|
-
return
|
|
1342
|
+
function normalizeHead(text, maxChars) {
|
|
1343
|
+
const singleLine = (text || "").replace(/\s+/g, " ").trim();
|
|
1344
|
+
if (singleLine.length <= maxChars) return singleLine;
|
|
1345
|
+
return singleLine.slice(0, maxChars) + "\u2026";
|
|
1571
1346
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1347
|
+
var COMMAND_FIELDS = ["command", "cmd", "script", "query", "path", "url"];
|
|
1348
|
+
function extractCommand(toolCallText, maxChars) {
|
|
1349
|
+
if (!toolCallText) return void 0;
|
|
1350
|
+
let parsed;
|
|
1351
|
+
try {
|
|
1352
|
+
parsed = JSON.parse(toolCallText);
|
|
1353
|
+
} catch {
|
|
1354
|
+
return void 0;
|
|
1355
|
+
}
|
|
1356
|
+
if (typeof parsed !== "object" || parsed === null) return void 0;
|
|
1357
|
+
const record = parsed;
|
|
1358
|
+
for (const field of COMMAND_FIELDS) {
|
|
1359
|
+
const value = record[field];
|
|
1360
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
1361
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
1362
|
+
return normalized.length <= maxChars ? normalized : normalized.slice(0, maxChars) + "\u2026";
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
return void 0;
|
|
1574
1366
|
}
|
|
1575
|
-
function
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1367
|
+
function buildStoredPlaceholder(input) {
|
|
1368
|
+
const title = input.command ?? input.head;
|
|
1369
|
+
const titlePart = title ? ` \`${title}\`` : "";
|
|
1370
|
+
return `\u{1F4E6} ${STORED_PLACEHOLDER_MARKER} #${input.ref} \xB7 ${input.kind} \xB7 ${groupThousands(input.tokens)} tok]${titlePart}
|
|
1371
|
+
\u2192 ${input.retrieveToolName}("${input.ref}") returns the full text`;
|
|
1579
1372
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
return ACP_TOOL_NAMES.has(toolName);
|
|
1373
|
+
var LEADING_TAG_RE = new RegExp("^\\x3cacp [^>]*>[^\\x3c]*\\x3c\\/acp>\\n?");
|
|
1374
|
+
function stripLeadingTag(text) {
|
|
1375
|
+
return text.replace(LEADING_TAG_RE, "");
|
|
1584
1376
|
}
|
|
1585
|
-
function
|
|
1586
|
-
|
|
1587
|
-
const cfg = resolveAbsorbConfig(config);
|
|
1588
|
-
if (isAcpOrConfiguredTool(msg.toolName, cfg)) return false;
|
|
1589
|
-
if (isMessageProtected(msg, config)) return false;
|
|
1590
|
-
if (latest && isMessageLatestProtected(msg, latest)) return false;
|
|
1591
|
-
for (const pattern of cfg.excludeTools) {
|
|
1592
|
-
if (msg.toolName && matchToolPattern(msg.toolName, pattern)) return false;
|
|
1593
|
-
}
|
|
1594
|
-
return true;
|
|
1377
|
+
function isStoredPlaceholderText(text) {
|
|
1378
|
+
return stripLeadingTag(text).includes(STORED_PLACEHOLDER_MARKER);
|
|
1595
1379
|
}
|
|
1596
|
-
function
|
|
1597
|
-
|
|
1598
|
-
if (records.length === 0) return messages;
|
|
1599
|
-
const hidden = /* @__PURE__ */ new Set();
|
|
1600
|
-
for (const record of records) {
|
|
1601
|
-
if (record.callMessageId) hidden.add(record.callMessageId);
|
|
1602
|
-
if (record.resultMessageId) hidden.add(record.resultMessageId);
|
|
1603
|
-
}
|
|
1604
|
-
return messages.filter((msg) => !hidden.has(msg.id));
|
|
1380
|
+
function retrievedMessageId(ref) {
|
|
1381
|
+
return RETRIEVED_ID_PREFIX + ref;
|
|
1605
1382
|
}
|
|
1606
|
-
function
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1383
|
+
function isRetrievedMessage(message) {
|
|
1384
|
+
return message.id.startsWith(RETRIEVED_ID_PREFIX) && message.role === "system" && message.contentType === "text";
|
|
1385
|
+
}
|
|
1386
|
+
function buildRetrievalInjection(ref, entry, text) {
|
|
1387
|
+
const header = `[acp-retrieved #${ref} \xB7 ${entry.kind} \xB7 ${groupThousands(entry.tokens)} tok]`;
|
|
1388
|
+
return {
|
|
1389
|
+
ackText: `retrieved ${ref}: ${groupThousands(entry.tokens)} tok (${entry.chars} chars)`,
|
|
1390
|
+
injection: {
|
|
1391
|
+
id: retrievedMessageId(ref),
|
|
1392
|
+
role: "system",
|
|
1393
|
+
contentType: "text",
|
|
1394
|
+
text: `${header}
|
|
1395
|
+
${text}`
|
|
1396
|
+
}
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
function applyRetrieve(input) {
|
|
1400
|
+
const ref = input.ref.trim();
|
|
1401
|
+
const found = retrieveByRef(input.store, ref);
|
|
1402
|
+
if (!found.ok) {
|
|
1403
|
+
return {
|
|
1404
|
+
ok: false,
|
|
1405
|
+
reason: "not-found",
|
|
1406
|
+
ackText: `retrieve ${ref}: not found \u2014 no stored original for this ref`
|
|
1407
|
+
};
|
|
1612
1408
|
}
|
|
1613
|
-
const
|
|
1614
|
-
|
|
1615
|
-
|
|
1409
|
+
const built = buildRetrievalInjection(ref, found.entry, found.text);
|
|
1410
|
+
return {
|
|
1411
|
+
ok: true,
|
|
1412
|
+
text: found.text,
|
|
1413
|
+
ackText: built.ackText,
|
|
1414
|
+
injection: built.injection,
|
|
1415
|
+
entry: found.entry
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
function storeLargeResults(input) {
|
|
1419
|
+
const cfg = resolveCcrConfig(input.config);
|
|
1420
|
+
if (!cfg.enabled)
|
|
1421
|
+
return { messages: input.messages, store: input.store, storedCount: 0 };
|
|
1422
|
+
let current = input.store;
|
|
1423
|
+
let storedCount = 0;
|
|
1424
|
+
const callById = /* @__PURE__ */ new Map();
|
|
1425
|
+
for (const message of input.messages) {
|
|
1426
|
+
if (message.contentType === "tool-call" && message.toolCallId) {
|
|
1427
|
+
callById.set(message.toolCallId, message);
|
|
1428
|
+
}
|
|
1616
1429
|
}
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
if (
|
|
1621
|
-
if (
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
if (
|
|
1628
|
-
|
|
1430
|
+
const updated = input.messages.map((message) => {
|
|
1431
|
+
if (message.contentType !== "tool-result") return message;
|
|
1432
|
+
const text = message.text ?? "";
|
|
1433
|
+
if (text.length === 0) return message;
|
|
1434
|
+
if (isStoredPlaceholderText(text)) return message;
|
|
1435
|
+
if (!message.toolCallId) return message;
|
|
1436
|
+
const toolName = message.toolName;
|
|
1437
|
+
if (toolName && (ACP_TOOL_NAMES.has(toolName) || toolName === cfg.toolName)) {
|
|
1438
|
+
return message;
|
|
1439
|
+
}
|
|
1440
|
+
if (toolName && cfg.excludeTools.some((pattern) => matchToolPattern(toolName, pattern))) {
|
|
1441
|
+
return message;
|
|
1442
|
+
}
|
|
1443
|
+
if (isMessageProtected(message, input.config)) return message;
|
|
1444
|
+
const tokens = input.countTokens(text);
|
|
1445
|
+
if (tokens < cfg.minToolTokens) return message;
|
|
1446
|
+
const ref = refForRaw(input.state.messageRefs, message.id);
|
|
1447
|
+
if (!ref || ref === BLOCKED_REF) return message;
|
|
1448
|
+
if (hasStoredRef(current, ref)) return message;
|
|
1449
|
+
const kind = classifyKind(toolName);
|
|
1450
|
+
const head = normalizeHead(text, cfg.maxHeadChars);
|
|
1451
|
+
const command = extractCommand(
|
|
1452
|
+
callById.get(message.toolCallId)?.text,
|
|
1453
|
+
cfg.maxHeadChars
|
|
1454
|
+
);
|
|
1455
|
+
current = storeOriginal(current, {
|
|
1456
|
+
ref,
|
|
1457
|
+
rawId: message.id,
|
|
1458
|
+
text,
|
|
1459
|
+
kind,
|
|
1460
|
+
toolName,
|
|
1461
|
+
tokens,
|
|
1462
|
+
head
|
|
1463
|
+
});
|
|
1464
|
+
storedCount += 1;
|
|
1629
1465
|
return {
|
|
1630
|
-
...
|
|
1631
|
-
text:
|
|
1466
|
+
...message,
|
|
1467
|
+
text: buildStoredPlaceholder({
|
|
1468
|
+
ref,
|
|
1469
|
+
kind,
|
|
1470
|
+
tokens,
|
|
1471
|
+
head,
|
|
1472
|
+
command,
|
|
1473
|
+
retrieveToolName: cfg.toolName
|
|
1474
|
+
})
|
|
1632
1475
|
};
|
|
1633
1476
|
});
|
|
1634
|
-
return { messages:
|
|
1477
|
+
return { messages: updated, store: current, storedCount };
|
|
1635
1478
|
}
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1479
|
+
var ccrStoreNode = {
|
|
1480
|
+
name: "ccr-store",
|
|
1481
|
+
enabled: (_io, ctx) => resolveCcrConfig(ctx.config).enabled,
|
|
1482
|
+
run(io, ctx) {
|
|
1483
|
+
const applied = storeLargeResults({
|
|
1484
|
+
messages: io.messages,
|
|
1485
|
+
state: io.state,
|
|
1486
|
+
store: ctx.contentStore,
|
|
1487
|
+
config: ctx.config,
|
|
1488
|
+
countTokens: ctx.countTokens
|
|
1489
|
+
});
|
|
1490
|
+
const effect = {
|
|
1491
|
+
store: applied.store,
|
|
1492
|
+
storedCount: applied.storedCount
|
|
1493
|
+
};
|
|
1494
|
+
const stats = applied.storedCount > 0 ? {
|
|
1495
|
+
...io.state.stats,
|
|
1496
|
+
storedCount: (io.state.stats.storedCount ?? 0) + applied.storedCount
|
|
1497
|
+
} : io.state.stats;
|
|
1498
|
+
return {
|
|
1499
|
+
...io,
|
|
1500
|
+
messages: applied.messages,
|
|
1501
|
+
state: stats === io.state.stats ? io.state : { ...io.state, stats },
|
|
1502
|
+
effects: { ...io.effects, ccr: effect }
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
};
|
|
1506
|
+
function storeCoveredOriginals(store, messages, state, blockIds, countTokens, maxHeadChars = DEFAULT_CCR_CONFIG.maxHeadChars) {
|
|
1507
|
+
const covered = /* @__PURE__ */ new Set();
|
|
1508
|
+
for (const block of state.blocks) {
|
|
1509
|
+
if (!block.active || !blockIds.includes(block.blockId)) continue;
|
|
1510
|
+
for (const id of block.effectiveMessageIds) covered.add(id);
|
|
1511
|
+
}
|
|
1512
|
+
let current = store;
|
|
1513
|
+
for (const message of messages) {
|
|
1514
|
+
if (!covered.has(message.id)) continue;
|
|
1515
|
+
if (message.contentType === "reasoning") continue;
|
|
1516
|
+
const text = stripLeadingTag(message.text ?? "");
|
|
1517
|
+
if (text.length === 0) continue;
|
|
1518
|
+
const ref = refForRaw(state.messageRefs, message.id);
|
|
1519
|
+
if (!ref || ref === BLOCKED_REF) continue;
|
|
1520
|
+
if (isStoredPlaceholderText(text) && hasStoredRef(current, ref)) continue;
|
|
1521
|
+
current = storeOriginal(current, {
|
|
1522
|
+
ref,
|
|
1523
|
+
rawId: message.id,
|
|
1524
|
+
text,
|
|
1525
|
+
kind: classifyKind(message.toolName),
|
|
1526
|
+
toolName: message.toolName,
|
|
1527
|
+
tokens: countTokens(text),
|
|
1528
|
+
head: normalizeHead(text, maxHeadChars)
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
return current;
|
|
1532
|
+
}
|
|
1533
|
+
function noteRetrieval(state) {
|
|
1534
|
+
return {
|
|
1535
|
+
...state,
|
|
1536
|
+
stats: {
|
|
1537
|
+
...state.stats,
|
|
1538
|
+
retrievalCount: (state.stats.retrievalCount ?? 0) + 1
|
|
1539
|
+
}
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
// src/boundaries.ts
|
|
1544
|
+
var MESSAGE_REF_PATTERN = /^m0*(\d{1,5})$/;
|
|
1545
|
+
var BLOCK_REF_PATTERN = /^b(\d{1,9})$/;
|
|
1546
|
+
function parseBoundary(ref) {
|
|
1547
|
+
const normalized = ref.trim().toLowerCase();
|
|
1548
|
+
const messageMatch = MESSAGE_REF_PATTERN.exec(normalized);
|
|
1549
|
+
if (messageMatch) {
|
|
1550
|
+
const numericId = Number(messageMatch[1]);
|
|
1551
|
+
if (numericId >= 1 && numericId <= 99999) {
|
|
1552
|
+
return { kind: "message", numericId, raw: normalized };
|
|
1646
1553
|
}
|
|
1647
|
-
} else if (input && typeof input === "object") {
|
|
1648
|
-
obj = input;
|
|
1649
1554
|
}
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1555
|
+
const blockMatch = BLOCK_REF_PATTERN.exec(normalized);
|
|
1556
|
+
if (blockMatch) {
|
|
1557
|
+
const numericId = Number(blockMatch[1]);
|
|
1558
|
+
if (numericId >= 1) return { kind: "block", numericId, raw: normalized };
|
|
1653
1559
|
}
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1560
|
+
return null;
|
|
1561
|
+
}
|
|
1562
|
+
var BoundaryNotFoundError = class extends Error {
|
|
1563
|
+
code = "BOUNDARY_NOT_FOUND";
|
|
1564
|
+
kind;
|
|
1565
|
+
endpoint;
|
|
1566
|
+
constructor(kind, endpoint, message) {
|
|
1567
|
+
super(message);
|
|
1568
|
+
this.name = "BoundaryNotFoundError";
|
|
1569
|
+
this.code = "BOUNDARY_NOT_FOUND";
|
|
1570
|
+
this.kind = kind;
|
|
1571
|
+
this.endpoint = endpoint;
|
|
1572
|
+
}
|
|
1573
|
+
};
|
|
1574
|
+
function resolveBoundaries(input) {
|
|
1575
|
+
const start = parseBoundary(input.startRef);
|
|
1576
|
+
const end = parseBoundary(input.endRef);
|
|
1577
|
+
if (!start || !end) {
|
|
1578
|
+
throw new Error(
|
|
1579
|
+
`Invalid boundary ref(s): startId="${input.startRef}", endId="${input.endRef}". Use mNNNNN or bN.`
|
|
1659
1580
|
);
|
|
1660
|
-
return null;
|
|
1661
1581
|
}
|
|
1582
|
+
const indexByMessageId = /* @__PURE__ */ new Map();
|
|
1583
|
+
input.messages.forEach(
|
|
1584
|
+
(message, index) => indexByMessageId.set(message.id, index)
|
|
1585
|
+
);
|
|
1586
|
+
let snappedBoundaries = [];
|
|
1587
|
+
const startAnchor = resolveAnchorIndex(
|
|
1588
|
+
start,
|
|
1589
|
+
input.state,
|
|
1590
|
+
indexByMessageId,
|
|
1591
|
+
"start"
|
|
1592
|
+
);
|
|
1593
|
+
if (startAnchor.snapped) snappedBoundaries.push(startAnchor.snapped);
|
|
1594
|
+
const endAnchor = resolveAnchorIndex(
|
|
1595
|
+
end,
|
|
1596
|
+
input.state,
|
|
1597
|
+
indexByMessageId,
|
|
1598
|
+
"end"
|
|
1599
|
+
);
|
|
1600
|
+
if (endAnchor.snapped) snappedBoundaries.push(endAnchor.snapped);
|
|
1601
|
+
let startIndex = startAnchor.index;
|
|
1602
|
+
let endIndex = endAnchor.index;
|
|
1603
|
+
if (startIndex > endIndex) {
|
|
1604
|
+
[startIndex, endIndex] = [endIndex, startIndex];
|
|
1605
|
+
}
|
|
1606
|
+
const messageIds = [];
|
|
1607
|
+
for (let index = startIndex; index <= endIndex; index++) {
|
|
1608
|
+
const message = input.messages[index];
|
|
1609
|
+
if (message && !isRenderedSummaryMessage(message) && !isRetrievedMessage(message))
|
|
1610
|
+
messageIds.push(message.id);
|
|
1611
|
+
}
|
|
1612
|
+
const boundaryKind = start.kind === "block" || end.kind === "block" ? "block" : "message";
|
|
1613
|
+
const nestedBlockIds = [];
|
|
1614
|
+
const nestedSeen = /* @__PURE__ */ new Set();
|
|
1615
|
+
for (const block of activeBlocks(input.state)) {
|
|
1616
|
+
if (blockVisibleInRange(block, indexByMessageId, startIndex, endIndex)) {
|
|
1617
|
+
if (!nestedSeen.has(block.blockId)) {
|
|
1618
|
+
nestedSeen.add(block.blockId);
|
|
1619
|
+
nestedBlockIds.push(block.blockId);
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
const protectedGaps = [];
|
|
1662
1624
|
return {
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1625
|
+
startIndex,
|
|
1626
|
+
endIndex,
|
|
1627
|
+
messageIds,
|
|
1628
|
+
nestedBlockIds,
|
|
1629
|
+
boundaryKind,
|
|
1630
|
+
protectedGaps,
|
|
1631
|
+
snappedBoundaries
|
|
1666
1632
|
};
|
|
1667
1633
|
}
|
|
1668
|
-
function
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1634
|
+
function resolveAnchorIndex(boundary, state, indexByMessageId, endpoint) {
|
|
1635
|
+
const label = endpoint === "start" ? "startId" : "endId";
|
|
1636
|
+
if (boundary.kind === "message") {
|
|
1637
|
+
const rawId = state.messageRefs.byRef[boundary.raw] ?? state.messageRefs.byRef[formatPaddedRef(boundary.numericId)];
|
|
1638
|
+
if (!rawId) {
|
|
1639
|
+
throw new BoundaryNotFoundError(
|
|
1640
|
+
"unknown",
|
|
1641
|
+
endpoint,
|
|
1642
|
+
`${label}="${boundary.raw}" does not exist in this session (typo or wrong session) \u2014 run acp_status for current refs.`
|
|
1643
|
+
);
|
|
1644
|
+
}
|
|
1645
|
+
const index = indexByMessageId.get(rawId);
|
|
1646
|
+
if (index !== void 0) {
|
|
1647
|
+
return { index, snapped: null };
|
|
1648
|
+
}
|
|
1649
|
+
const owner2 = activeOwnerAnchor(state, [rawId], indexByMessageId);
|
|
1650
|
+
if (owner2 !== null) {
|
|
1651
|
+
return {
|
|
1652
|
+
index: owner2,
|
|
1653
|
+
snapped: `${label}="${boundary.raw}" refers to a message already compressed into an active block \u2014 anchored to the active block covering it instead.`
|
|
1654
|
+
};
|
|
1655
|
+
}
|
|
1656
|
+
throw new BoundaryNotFoundError(
|
|
1657
|
+
"consumed",
|
|
1658
|
+
endpoint,
|
|
1659
|
+
`${label}="${boundary.raw}" not found in visible context (likely consumed by an existing block).`
|
|
1660
|
+
);
|
|
1661
|
+
}
|
|
1662
|
+
const block = blockById(state, `b${boundary.numericId}`);
|
|
1663
|
+
if (!block) {
|
|
1664
|
+
throw new BoundaryNotFoundError(
|
|
1665
|
+
"unknown",
|
|
1666
|
+
endpoint,
|
|
1667
|
+
`${label}="b${boundary.numericId}" does not exist in this session (typo or wrong session) \u2014 run acp_status for current refs.`
|
|
1668
|
+
);
|
|
1669
|
+
}
|
|
1670
|
+
if (block.active) {
|
|
1671
|
+
const anchor = visibleBlockAnchor(block, indexByMessageId);
|
|
1672
|
+
if (anchor !== null) {
|
|
1673
|
+
return { index: anchor, snapped: null };
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
const owner = activeOwnerAnchor(
|
|
1677
|
+
state,
|
|
1678
|
+
block.effectiveMessageIds,
|
|
1679
|
+
indexByMessageId
|
|
1680
|
+
);
|
|
1681
|
+
if (owner !== null) {
|
|
1682
|
+
return {
|
|
1683
|
+
index: owner,
|
|
1684
|
+
snapped: `${label}="b${boundary.numericId}" was consumed by a higher-tier block \u2014 anchored to the active block covering its content instead.`
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1687
|
+
if (!block.active) {
|
|
1688
|
+
throw new BoundaryNotFoundError(
|
|
1689
|
+
"consumed",
|
|
1690
|
+
endpoint,
|
|
1691
|
+
`${label}="b${boundary.numericId}" not found in visible context (block distilled/consumed by a higher-tier block).`
|
|
1692
|
+
);
|
|
1693
|
+
}
|
|
1694
|
+
throw new BoundaryNotFoundError(
|
|
1695
|
+
"consumed",
|
|
1696
|
+
endpoint,
|
|
1697
|
+
`${label}="b${boundary.numericId}" is an active block but none of its content (raw messages or rendered summary) is visible in the current context \u2014 run acp_status to verify.`
|
|
1698
|
+
);
|
|
1699
|
+
}
|
|
1700
|
+
function activeOwnerAnchor(state, ownedIds, indexByMessageId) {
|
|
1701
|
+
if (ownedIds.length === 0) return null;
|
|
1702
|
+
const owned = new Set(ownedIds);
|
|
1703
|
+
let best = null;
|
|
1704
|
+
for (const block of state.blocks) {
|
|
1705
|
+
if (!block.active) continue;
|
|
1706
|
+
const inherited = inheritedContentIds(state, block);
|
|
1707
|
+
let ownsInherited = false;
|
|
1708
|
+
for (const id of owned) {
|
|
1709
|
+
if (inherited.has(id)) {
|
|
1710
|
+
ownsInherited = true;
|
|
1711
|
+
break;
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
if (!ownsInherited) continue;
|
|
1715
|
+
const anchor = visibleBlockAnchor(block, indexByMessageId);
|
|
1716
|
+
if (anchor === null) continue;
|
|
1717
|
+
if (best === null || anchor < best) {
|
|
1718
|
+
best = anchor;
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
return best;
|
|
1722
|
+
}
|
|
1723
|
+
function inheritedContentIds(state, block) {
|
|
1724
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1725
|
+
for (const childId of block.directBlockIds) {
|
|
1726
|
+
const child = blockById(state, childId);
|
|
1727
|
+
if (!child) continue;
|
|
1728
|
+
for (const id of child.effectiveMessageIds) ids.add(id);
|
|
1729
|
+
}
|
|
1730
|
+
return ids;
|
|
1731
|
+
}
|
|
1732
|
+
function formatPaddedRef(index) {
|
|
1733
|
+
return `m${String(index).padStart(5, "0")}`;
|
|
1734
|
+
}
|
|
1735
|
+
function visibleBlockAnchor(block, indexByMessageId) {
|
|
1736
|
+
const summaryIndex = indexByMessageId.get(summaryMessageId(block.blockId));
|
|
1737
|
+
if (summaryIndex !== void 0) return summaryIndex;
|
|
1738
|
+
return earliestIndexOfIds(block.effectiveMessageIds, indexByMessageId);
|
|
1739
|
+
}
|
|
1740
|
+
function blockVisibleInRange(block, indexByMessageId, startIndex, endIndex) {
|
|
1741
|
+
const summaryIndex = indexByMessageId.get(summaryMessageId(block.blockId));
|
|
1742
|
+
if (summaryIndex !== void 0 && summaryIndex >= startIndex && summaryIndex <= endIndex) {
|
|
1743
|
+
return true;
|
|
1744
|
+
}
|
|
1745
|
+
const rawIndex = earliestIndexOfIds(
|
|
1746
|
+
block.effectiveMessageIds,
|
|
1747
|
+
indexByMessageId
|
|
1748
|
+
);
|
|
1749
|
+
return rawIndex !== null && rawIndex >= startIndex && rawIndex <= endIndex;
|
|
1750
|
+
}
|
|
1751
|
+
function earliestIndexOfIds(ids, indexByMessageId) {
|
|
1752
|
+
let earliest = null;
|
|
1753
|
+
for (const id of ids) {
|
|
1754
|
+
const index = indexByMessageId.get(id);
|
|
1755
|
+
if (index !== void 0 && (earliest === null || index < earliest)) {
|
|
1756
|
+
earliest = index;
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
return earliest;
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
// src/truncate-tools.ts
|
|
1763
|
+
var TRUNCATION_MARKER = "[truncated for context space]";
|
|
1764
|
+
var DEFAULTS = {
|
|
1765
|
+
minOutputTokens: 1e3,
|
|
1766
|
+
keepPrefixChars: 2e3,
|
|
1767
|
+
keepSuffixChars: 2e3,
|
|
1768
|
+
protectRecentMessages: 3
|
|
1769
|
+
};
|
|
1770
|
+
function truncateLargeToolOutputs(messages, tokenCount, config, countTokens, options = {}) {
|
|
1771
|
+
const opts = { ...DEFAULTS, ...options };
|
|
1772
|
+
const limit = config.modelContextLimit;
|
|
1773
|
+
if (limit <= 0 || tokenCount < config.truncate.threshold * limit) {
|
|
1774
|
+
return { messages, truncatedCount: 0, savedTokens: 0, candidatesFound: 0 };
|
|
1775
|
+
}
|
|
1776
|
+
const protectedIndex = messages.length - opts.protectRecentMessages;
|
|
1777
|
+
const findCandidates = (predicate) => {
|
|
1778
|
+
const found = [];
|
|
1779
|
+
for (let i = 0; i < protectedIndex; i++) {
|
|
1780
|
+
const message = messages[i];
|
|
1781
|
+
if (!predicate(message)) continue;
|
|
1782
|
+
const text = message.text ?? "";
|
|
1783
|
+
if (text.length === 0 || text.includes(TRUNCATION_MARKER)) continue;
|
|
1784
|
+
if (countTokens(text) < opts.minOutputTokens) continue;
|
|
1785
|
+
found.push(message);
|
|
1786
|
+
}
|
|
1787
|
+
return found.sort(
|
|
1788
|
+
(a, b) => countTokens(b.text ?? "") - countTokens(a.text ?? "")
|
|
1789
|
+
);
|
|
1790
|
+
};
|
|
1791
|
+
const toolResults = findCandidates((m) => m.contentType === "tool-result");
|
|
1792
|
+
const textMessages = options.includeTextMessages ? findCandidates(
|
|
1793
|
+
(m) => m.contentType === "text" && (m.role === "user" || m.role === "assistant") && !isRenderedSummaryMessage(m) && !m.text?.startsWith(SUMMARY_HEADER)
|
|
1794
|
+
) : [];
|
|
1795
|
+
const candidatesFound = toolResults.length + textMessages.length;
|
|
1796
|
+
let truncatedCount = 0;
|
|
1797
|
+
let savedTokens = 0;
|
|
1798
|
+
let remaining = tokenCount;
|
|
1799
|
+
const targetTokens = config.truncate.threshold * limit * 0.9;
|
|
1800
|
+
const replacements = /* @__PURE__ */ new Map();
|
|
1801
|
+
const applyTo = (candidates) => {
|
|
1802
|
+
for (const candidate of candidates) {
|
|
1803
|
+
if (remaining <= targetTokens) break;
|
|
1804
|
+
const original = candidate.text ?? "";
|
|
1805
|
+
const tokens = countTokens(original);
|
|
1806
|
+
if (original.length <= opts.keepPrefixChars + opts.keepSuffixChars) {
|
|
1807
|
+
continue;
|
|
1808
|
+
}
|
|
1809
|
+
const prefix = clampPrefix(original, opts.keepPrefixChars);
|
|
1810
|
+
const suffix = clampWindow(original, original.length - opts.keepSuffixChars, original.length);
|
|
1811
|
+
const replacement = `${prefix}
|
|
1812
|
+
|
|
1813
|
+
...${TRUNCATION_MARKER} \u2014 original ~${tokens} tokens]...
|
|
1814
|
+
|
|
1815
|
+
${suffix}`;
|
|
1816
|
+
replacements.set(candidate.id, replacement);
|
|
1817
|
+
truncatedCount++;
|
|
1818
|
+
remaining -= tokens - countTokens(replacement);
|
|
1819
|
+
savedTokens += tokens - countTokens(replacement);
|
|
1820
|
+
}
|
|
1821
|
+
};
|
|
1822
|
+
applyTo(toolResults);
|
|
1823
|
+
applyTo(textMessages);
|
|
1824
|
+
if (replacements.size === 0) {
|
|
1825
|
+
return { messages, truncatedCount: 0, savedTokens: 0, candidatesFound };
|
|
1826
|
+
}
|
|
1827
|
+
return {
|
|
1828
|
+
messages: messages.map(
|
|
1829
|
+
(m) => replacements.has(m.id) ? { ...m, text: replacements.get(m.id) } : m
|
|
1830
|
+
),
|
|
1831
|
+
truncatedCount,
|
|
1832
|
+
savedTokens,
|
|
1833
|
+
candidatesFound
|
|
1834
|
+
};
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
// src/hide-consumed.ts
|
|
1838
|
+
var KEEP_LAST_ORPHANED = 2;
|
|
1839
|
+
function rangeKey(startRef, endRef) {
|
|
1840
|
+
return `${startRef}::${endRef}`;
|
|
1841
|
+
}
|
|
1842
|
+
function parseCallText(text) {
|
|
1843
|
+
const raw = text ?? "";
|
|
1844
|
+
const start = raw.indexOf("{");
|
|
1845
|
+
if (start < 0) return null;
|
|
1846
|
+
let parsed;
|
|
1847
|
+
try {
|
|
1848
|
+
parsed = JSON.parse(raw.slice(start));
|
|
1849
|
+
} catch {
|
|
1850
|
+
return null;
|
|
1851
|
+
}
|
|
1852
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
1853
|
+
const obj = parsed;
|
|
1854
|
+
let content = null;
|
|
1855
|
+
let contentWasString = false;
|
|
1856
|
+
if (Array.isArray(obj.content)) {
|
|
1857
|
+
content = obj.content;
|
|
1858
|
+
} else if (typeof obj.content === "string") {
|
|
1859
|
+
contentWasString = true;
|
|
1860
|
+
try {
|
|
1861
|
+
const inner = JSON.parse(obj.content);
|
|
1862
|
+
if (Array.isArray(inner)) content = inner;
|
|
1863
|
+
} catch {
|
|
1864
|
+
content = null;
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
if (!content || content.length === 0) return null;
|
|
1868
|
+
return { prefix: raw.slice(0, start), obj, content, contentWasString };
|
|
1869
|
+
}
|
|
1870
|
+
function rewriteCompressText(text, liveKeys) {
|
|
1871
|
+
const parsed = parseCallText(text);
|
|
1872
|
+
if (!parsed) return null;
|
|
1873
|
+
const { prefix, obj, content, contentWasString } = parsed;
|
|
1874
|
+
const kept = content.filter((entry) => {
|
|
1875
|
+
if (!entry || typeof entry !== "object") return false;
|
|
1876
|
+
const e = entry;
|
|
1877
|
+
const s = typeof e.startId === "string" ? e.startId : typeof e.messageId === "string" ? e.messageId : "";
|
|
1878
|
+
const end = typeof e.endId === "string" ? e.endId : typeof e.messageId === "string" ? e.messageId : "";
|
|
1879
|
+
return liveKeys.has(rangeKey(s, end));
|
|
1880
|
+
});
|
|
1881
|
+
if (kept.length === 0) return null;
|
|
1882
|
+
return prefix + serializeCompacted(obj, kept, contentWasString).text;
|
|
1883
|
+
}
|
|
1884
|
+
var SUMMARY_STUB_CHARS = 200;
|
|
1885
|
+
function compactEntry(entry) {
|
|
1886
|
+
if (!entry || typeof entry !== "object") return entry;
|
|
1887
|
+
const e = entry;
|
|
1888
|
+
if (typeof e.summary !== "string" || e.summary.length <= SUMMARY_STUB_CHARS) return entry;
|
|
1889
|
+
return { ...e, summary: `${clampPrefix(e.summary, SUMMARY_STUB_CHARS - 1)}\u2026` };
|
|
1890
|
+
}
|
|
1891
|
+
function serializeCompacted(obj, content, contentWasString) {
|
|
1892
|
+
let changed = false;
|
|
1893
|
+
const compacted = content.map((entry) => {
|
|
1894
|
+
const out = compactEntry(entry);
|
|
1895
|
+
if (out !== entry) changed = true;
|
|
1896
|
+
return out;
|
|
1897
|
+
});
|
|
1898
|
+
const outContent = contentWasString ? JSON.stringify(compacted) : compacted;
|
|
1899
|
+
return { text: JSON.stringify({ ...obj, content: outContent }), changed };
|
|
1900
|
+
}
|
|
1901
|
+
function compactCompressText(text) {
|
|
1902
|
+
const parsed = parseCallText(text);
|
|
1903
|
+
if (!parsed) return null;
|
|
1904
|
+
const { prefix, obj, content, contentWasString } = parsed;
|
|
1905
|
+
const { text: out, changed } = serializeCompacted(obj, content, contentWasString);
|
|
1906
|
+
return changed ? prefix + out : null;
|
|
1907
|
+
}
|
|
1908
|
+
function hideConsumedCompressCalls(state, messages) {
|
|
1909
|
+
const allBlockCallIds = /* @__PURE__ */ new Set();
|
|
1910
|
+
const activeCallIds = /* @__PURE__ */ new Set();
|
|
1911
|
+
const liveRangeKeysByCallId = /* @__PURE__ */ new Map();
|
|
1912
|
+
const legacyLiveByCallId = /* @__PURE__ */ new Set();
|
|
1913
|
+
for (const block of state.blocks) {
|
|
1914
|
+
if (!block.compressCallId) continue;
|
|
1915
|
+
allBlockCallIds.add(block.compressCallId);
|
|
1916
|
+
if (!block.active) continue;
|
|
1917
|
+
activeCallIds.add(block.compressCallId);
|
|
1918
|
+
if (block.startRef === void 0 || block.endRef === void 0) {
|
|
1919
|
+
legacyLiveByCallId.add(block.compressCallId);
|
|
1920
|
+
continue;
|
|
1921
|
+
}
|
|
1922
|
+
let keys = liveRangeKeysByCallId.get(block.compressCallId);
|
|
1923
|
+
if (!keys) {
|
|
1924
|
+
keys = /* @__PURE__ */ new Set();
|
|
1925
|
+
liveRangeKeysByCallId.set(block.compressCallId, keys);
|
|
1926
|
+
}
|
|
1927
|
+
keys.add(rangeKey(block.startRef, block.endRef));
|
|
1928
|
+
}
|
|
1929
|
+
const lastOrphanedCallIds = [];
|
|
1930
|
+
for (let i = messages.length - 1; i >= 0 && lastOrphanedCallIds.length < KEEP_LAST_ORPHANED; i--) {
|
|
1931
|
+
const message = messages[i];
|
|
1932
|
+
if (message.toolName !== "compress" || message.contentType !== "tool-call") continue;
|
|
1933
|
+
const callId = message.toolCallId;
|
|
1934
|
+
if (callId && !allBlockCallIds.has(callId)) {
|
|
1935
|
+
lastOrphanedCallIds.push(callId);
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
const keepCallIds = /* @__PURE__ */ new Set([...activeCallIds, ...lastOrphanedCallIds]);
|
|
1939
|
+
const hiddenCallIds = /* @__PURE__ */ new Set();
|
|
1940
|
+
for (const message of messages) {
|
|
1941
|
+
if (message.toolName === "compress" && message.contentType === "tool-call" && (!message.toolCallId || !keepCallIds.has(message.toolCallId))) {
|
|
1942
|
+
if (message.toolCallId) hiddenCallIds.add(message.toolCallId);
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
let hidden = 0;
|
|
1946
|
+
const result = [];
|
|
1947
|
+
for (const message of messages) {
|
|
1948
|
+
if (message.toolName === "compress" && message.contentType === "tool-call" && (!message.toolCallId || !keepCallIds.has(message.toolCallId))) {
|
|
1949
|
+
hidden++;
|
|
1950
|
+
continue;
|
|
1951
|
+
}
|
|
1952
|
+
if (message.contentType === "tool-result" && message.toolCallId && hiddenCallIds.has(message.toolCallId)) {
|
|
1953
|
+
hidden++;
|
|
1954
|
+
continue;
|
|
1955
|
+
}
|
|
1956
|
+
if (message.toolName === "compress" && message.contentType === "tool-call" && message.toolCallId && keepCallIds.has(message.toolCallId)) {
|
|
1957
|
+
const liveKeys = liveRangeKeysByCallId.get(message.toolCallId);
|
|
1958
|
+
if (liveKeys && liveKeys.size > 0 && !legacyLiveByCallId.has(message.toolCallId)) {
|
|
1959
|
+
const rewritten = rewriteCompressText(message.text, liveKeys);
|
|
1960
|
+
if (rewritten !== null) {
|
|
1961
|
+
result.push({ ...message, text: rewritten });
|
|
1962
|
+
continue;
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
const compacted = compactCompressText(message.text);
|
|
1966
|
+
if (compacted !== null) {
|
|
1967
|
+
result.push({ ...message, text: compacted });
|
|
1968
|
+
continue;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
result.push(message);
|
|
1972
|
+
}
|
|
1973
|
+
return { messages: result, hidden };
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
// src/absorb.ts
|
|
1977
|
+
var ABSORB_PROMPT_MARKER = "[ACP absorb]";
|
|
1978
|
+
var DEFAULT_ABSORB_CONFIG = {
|
|
1979
|
+
enabled: false,
|
|
1980
|
+
toolName: ABSORB_TOOL_NAME,
|
|
1981
|
+
// Raised 1000 → 4000 (issue #352): lossless CCR takes over large-result
|
|
1982
|
+
// handling; absorb's forced distillation only fires above the new bar.
|
|
1983
|
+
minToolTokens: 4e3,
|
|
1984
|
+
contextThresholdPct: 0,
|
|
1985
|
+
excludeTools: []
|
|
1986
|
+
};
|
|
1987
|
+
function resolveAbsorbConfig(config) {
|
|
1988
|
+
return { ...DEFAULT_ABSORB_CONFIG, ...config.absorb ?? {} };
|
|
1989
|
+
}
|
|
1990
|
+
function formatTokenCount(tokens) {
|
|
1991
|
+
if (tokens < 1e3) return String(tokens);
|
|
1992
|
+
if (tokens < 1e4) return (tokens / 1e3).toFixed(1) + "K";
|
|
1993
|
+
return Math.round(tokens / 1e3) + "K";
|
|
1994
|
+
}
|
|
1995
|
+
function buildAbsorbPrompt(ref, tokens, toolName = ABSORB_TOOL_NAME) {
|
|
1996
|
+
return `${ABSORB_PROMPT_MARKER} This tool result (~${formatTokenCount(tokens)} tokens) will be REMOVED from context. Your IMMEDIATE next action: call ${toolName}({ ref: "${ref}", summary: "..." }) \u2014 summary = distilled essentials only (outcome, key values, exact paths:lines, error text verbatim, decisions). Afterwards work from your summary; do NOT re-run this tool. If the result contains nothing you need, call ${toolName} with summary "(nothing needed)".`;
|
|
1997
|
+
}
|
|
1998
|
+
function buildAbsorbSystemPrompt(toolName = ABSORB_TOOL_NAME) {
|
|
1999
|
+
return `INSTANT TOOL-RESULT ABSORPTION (${toolName})
|
|
2000
|
+
|
|
2001
|
+
Some tool results end with a ${ABSORB_PROMPT_MARKER} instruction. When you see one, your IMMEDIATE next action must be calling ${toolName}({ ref, summary }) \u2014 distill that tool result's essentials into summary: outcome, key values, exact paths:lines, error text verbatim, decisions. The original output is then removed from context; your ${toolName} summary becomes the only durable record of it, so distill carefully. Never call another tool or answer the user before absorbing a marked result. Do not re-run the original tool afterwards \u2014 work from your summary. ${toolName} calls are ordinary context: the regular compression system may fold them later like any other message.`;
|
|
2002
|
+
}
|
|
2003
|
+
function isAcpOrConfiguredTool(toolName, cfg) {
|
|
2004
|
+
if (!toolName) return false;
|
|
2005
|
+
if (toolName === cfg.toolName) return true;
|
|
2006
|
+
return ACP_TOOL_NAMES.has(toolName);
|
|
2007
|
+
}
|
|
2008
|
+
function isAbsorbCandidate(msg, config, latest) {
|
|
2009
|
+
if (msg.contentType !== "tool-result" || !msg.toolCallId) return false;
|
|
2010
|
+
if (isStoredPlaceholderText(msg.text ?? "")) return false;
|
|
2011
|
+
const cfg = resolveAbsorbConfig(config);
|
|
2012
|
+
if (isAcpOrConfiguredTool(msg.toolName, cfg)) return false;
|
|
2013
|
+
if (isMessageProtected(msg, config)) return false;
|
|
2014
|
+
if (latest && isMessageLatestProtected(msg, latest)) return false;
|
|
2015
|
+
for (const pattern of cfg.excludeTools) {
|
|
2016
|
+
if (msg.toolName && matchToolPattern(msg.toolName, pattern)) return false;
|
|
2017
|
+
}
|
|
2018
|
+
return true;
|
|
2019
|
+
}
|
|
2020
|
+
function hideAbsorbedMessages(messages, state) {
|
|
2021
|
+
const records = state.absorbed ?? [];
|
|
2022
|
+
if (records.length === 0) return messages;
|
|
2023
|
+
const hidden = /* @__PURE__ */ new Set();
|
|
2024
|
+
for (const record of records) {
|
|
2025
|
+
if (record.callMessageId) hidden.add(record.callMessageId);
|
|
2026
|
+
if (record.resultMessageId) hidden.add(record.resultMessageId);
|
|
2027
|
+
}
|
|
2028
|
+
return messages.filter((msg) => !hidden.has(msg.id));
|
|
2029
|
+
}
|
|
2030
|
+
function appendAbsorbPrompts(messages, state, config, tokenCount, countTokens) {
|
|
2031
|
+
const cfg = resolveAbsorbConfig(config);
|
|
2032
|
+
if (!cfg.enabled) return { messages, promptedCount: 0 };
|
|
2033
|
+
const limit = config.modelContextLimit;
|
|
2034
|
+
if (cfg.contextThresholdPct > 0 && limit > 0 && tokenCount < cfg.contextThresholdPct * limit) {
|
|
2035
|
+
return { messages, promptedCount: 0 };
|
|
2036
|
+
}
|
|
2037
|
+
const absorbedIds = /* @__PURE__ */ new Set();
|
|
2038
|
+
for (const record of state.absorbed ?? []) {
|
|
2039
|
+
if (record.resultMessageId) absorbedIds.add(record.resultMessageId);
|
|
2040
|
+
}
|
|
2041
|
+
let promptedCount = 0;
|
|
2042
|
+
const latest = collectLatestProtected(messages, config);
|
|
2043
|
+
const out = messages.map((msg) => {
|
|
2044
|
+
if (!isAbsorbCandidate(msg, config, latest)) return msg;
|
|
2045
|
+
if (absorbedIds.has(msg.id)) return msg;
|
|
2046
|
+
const text = msg.text ?? "";
|
|
2047
|
+
if (text.includes(ABSORB_PROMPT_MARKER)) return msg;
|
|
2048
|
+
const tokens = countTokens(text);
|
|
2049
|
+
if (tokens < cfg.minToolTokens) return msg;
|
|
2050
|
+
const ref = refForRaw(state.messageRefs, msg.id);
|
|
2051
|
+
if (!ref || ref === BLOCKED_REF) return msg;
|
|
2052
|
+
promptedCount++;
|
|
2053
|
+
return {
|
|
2054
|
+
...msg,
|
|
2055
|
+
text: text + "\n\n" + buildAbsorbPrompt(ref, tokens, cfg.toolName)
|
|
2056
|
+
};
|
|
2057
|
+
});
|
|
2058
|
+
return { messages: out, promptedCount };
|
|
2059
|
+
}
|
|
2060
|
+
function parseAbsorbInput(input, callId, onWarn) {
|
|
2061
|
+
let obj = null;
|
|
2062
|
+
if (typeof input === "string") {
|
|
2063
|
+
try {
|
|
2064
|
+
const parsed = JSON.parse(input);
|
|
2065
|
+
if (parsed && typeof parsed === "object") {
|
|
2066
|
+
obj = parsed;
|
|
2067
|
+
}
|
|
2068
|
+
} catch {
|
|
2069
|
+
obj = null;
|
|
2070
|
+
}
|
|
2071
|
+
} else if (input && typeof input === "object") {
|
|
2072
|
+
obj = input;
|
|
2073
|
+
}
|
|
2074
|
+
if (!obj) {
|
|
2075
|
+
onWarn?.(`[acp-absorb-input] rejected: not an object (${typeof input})`);
|
|
2076
|
+
return null;
|
|
2077
|
+
}
|
|
2078
|
+
const ref = pickString(obj, "ref", "messageId", "of");
|
|
2079
|
+
const summary = pickString(obj, "summary", "content");
|
|
2080
|
+
if (typeof ref !== "string" || typeof summary !== "string") {
|
|
2081
|
+
onWarn?.(
|
|
2082
|
+
`[acp-absorb-input] rejected: need ref (string) + summary (string); keys: ${Object.keys(obj).join(",")}`
|
|
2083
|
+
);
|
|
2084
|
+
return null;
|
|
2085
|
+
}
|
|
2086
|
+
return {
|
|
2087
|
+
ref: ref.trim(),
|
|
2088
|
+
summary,
|
|
2089
|
+
...callId ? { absorbCallId: callId } : {}
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
2092
|
+
function pickString(obj, ...keys) {
|
|
2093
|
+
for (const key of keys) {
|
|
2094
|
+
const value = obj[key];
|
|
2095
|
+
if (typeof value === "string") return value;
|
|
2096
|
+
}
|
|
2097
|
+
return void 0;
|
|
2098
|
+
}
|
|
2099
|
+
function applyAbsorb(input) {
|
|
2100
|
+
const countTokens = input.countTokens ?? ((text) => Math.ceil(text.length / 4));
|
|
2101
|
+
const summary = input.summary?.trim() ?? "";
|
|
2102
|
+
if (!summary) {
|
|
2103
|
+
return {
|
|
2104
|
+
state: input.state,
|
|
2105
|
+
ok: false,
|
|
2106
|
+
resultText: "absorb failed: summary is empty \u2014 provide the distilled key info of the tool result."
|
|
2107
|
+
};
|
|
2108
|
+
}
|
|
2109
|
+
const cfg = resolveAbsorbConfig(input.config);
|
|
2110
|
+
const rawId = rawForRef(input.state.messageRefs, input.ref.trim());
|
|
2111
|
+
if (!rawId) {
|
|
2112
|
+
return {
|
|
2113
|
+
state: input.state,
|
|
2114
|
+
ok: false,
|
|
2115
|
+
resultText: `absorb failed: ref ${input.ref} does not exist in this session (it may be hidden, already compressed, or stale).`
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
const existing = (input.state.absorbed ?? []).find(
|
|
2119
|
+
(record2) => record2.resultMessageId === rawId
|
|
2120
|
+
);
|
|
2121
|
+
if (existing) {
|
|
2122
|
+
return {
|
|
2123
|
+
state: input.state,
|
|
2124
|
+
ok: true,
|
|
2125
|
+
resultText: `already absorbed (${input.ref}) \u2014 no change.`
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2128
|
+
const target = input.messages.find((m) => m.id === rawId);
|
|
2129
|
+
if (!target) {
|
|
2130
|
+
return {
|
|
2131
|
+
state: input.state,
|
|
2132
|
+
ok: false,
|
|
2133
|
+
resultText: `absorb failed: ref ${input.ref} is not visible in this session (hidden or compressed).`
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
if (target.contentType !== "tool-result") {
|
|
2137
|
+
return {
|
|
2138
|
+
state: input.state,
|
|
2139
|
+
ok: false,
|
|
2140
|
+
resultText: `absorb failed: ref ${input.ref} is a ${target.contentType}, not a tool result.`
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
if (isAcpOrConfiguredTool(target.toolName, cfg)) {
|
|
2144
|
+
return {
|
|
2145
|
+
state: input.state,
|
|
2146
|
+
ok: false,
|
|
2147
|
+
resultText: `absorb failed: ${target.toolName} is an ACP-managed tool result \u2014 it is not absorbable.`
|
|
2148
|
+
};
|
|
2149
|
+
}
|
|
2150
|
+
if (isMessageProtected(target, input.config) || isMessageLatestProtected(
|
|
2151
|
+
target,
|
|
2152
|
+
collectLatestProtected(input.messages, input.config)
|
|
2153
|
+
)) {
|
|
2154
|
+
return {
|
|
2155
|
+
state: input.state,
|
|
2156
|
+
ok: false,
|
|
2157
|
+
resultText: `absorb failed: ${target.toolName} is a protected tool \u2014 its results must stay visible.`
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
if (!target.toolCallId) {
|
|
2161
|
+
return {
|
|
2162
|
+
state: input.state,
|
|
2163
|
+
ok: false,
|
|
2164
|
+
resultText: `absorb failed: ref ${input.ref} has no tool-call id \u2014 cannot pair it for hiding.`
|
|
2165
|
+
};
|
|
2166
|
+
}
|
|
2167
|
+
const call = input.messages.find(
|
|
2168
|
+
(m) => m.contentType === "tool-call" && m.toolCallId === target.toolCallId
|
|
2169
|
+
);
|
|
2170
|
+
const tokens = countTokens(target.text ?? "");
|
|
2171
|
+
const summaryTokens = countTokens(summary);
|
|
2172
|
+
const record = {
|
|
2173
|
+
toolCallId: target.toolCallId,
|
|
2174
|
+
callMessageId: call?.id ?? "",
|
|
2175
|
+
resultMessageId: target.id,
|
|
2176
|
+
...input.absorbCallId ? { absorbCallId: input.absorbCallId } : {},
|
|
2177
|
+
summary,
|
|
2178
|
+
tokensReclaimed: tokens,
|
|
2179
|
+
createdAt: Date.now()
|
|
2180
|
+
};
|
|
2181
|
+
const state = {
|
|
2182
|
+
...input.state,
|
|
2183
|
+
absorbed: [...input.state.absorbed ?? [], record],
|
|
2184
|
+
stats: {
|
|
2185
|
+
...input.state.stats,
|
|
2186
|
+
absorbedTokens: (input.state.stats.absorbedTokens ?? 0) + tokens
|
|
2187
|
+
}
|
|
2188
|
+
};
|
|
2189
|
+
const bloat = summaryTokens >= tokens && tokens > 0 ? ` WARNING: your summary (~${formatTokenCount(summaryTokens)} tokens) is not smaller than the original (~${formatTokenCount(tokens)} tokens) \u2014 distill harder next time.` : "";
|
|
2190
|
+
return {
|
|
2191
|
+
state,
|
|
2192
|
+
ok: true,
|
|
2193
|
+
resultText: `absorbed ${input.ref} (~${formatTokenCount(tokens)} tokens \u2192 summary ~${formatTokenCount(summaryTokens)}). The original tool output is now hidden; your summary is the durable record.${bloat}`
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
// src/crush.ts
|
|
2198
|
+
var DEFAULT_CRUSH_CONFIG = {
|
|
2199
|
+
enabled: false,
|
|
2200
|
+
minReduction: 0.1
|
|
2201
|
+
};
|
|
2202
|
+
var MAX_INPUT_CHARS = 2e6;
|
|
2203
|
+
var MAX_DEPTH = 12;
|
|
2204
|
+
var MAX_FOLDS = 1e3;
|
|
2205
|
+
var RUN_MIN = 4;
|
|
2206
|
+
var HOIST_MIN = 4;
|
|
2207
|
+
var CODE_KEEP_RATIO = 0.97;
|
|
2208
|
+
var CRUSH_KEY = "__acp_crush";
|
|
2209
|
+
function isPlainObject(v) {
|
|
2210
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
|
|
2211
|
+
const p = Object.getPrototypeOf(v);
|
|
2212
|
+
return p === null || p === Object.prototype;
|
|
2213
|
+
}
|
|
2214
|
+
function classifyCrushText(text) {
|
|
2215
|
+
const trimmed = text.trim();
|
|
2216
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]"))
|
|
2217
|
+
return "json";
|
|
2218
|
+
if (detectLanguage(text) !== null) return "code";
|
|
2219
|
+
return "log";
|
|
2220
|
+
}
|
|
2221
|
+
function crushText(text, options = {}) {
|
|
2222
|
+
const countTokens = options.countTokens ?? defaultCountTokens;
|
|
2223
|
+
const minReduction = options.minReduction ?? DEFAULT_CRUSH_CONFIG.minReduction;
|
|
2224
|
+
const meta = options.meta ?? {};
|
|
2225
|
+
if (text.length === 0 || text.length > MAX_INPUT_CHARS) return null;
|
|
2226
|
+
const rawTok = countTokens(text);
|
|
2227
|
+
if (rawTok <= 0) return null;
|
|
2228
|
+
const kind = classifyCrushText(text);
|
|
2229
|
+
const wanted = options.plugins ?? registeredPlugins();
|
|
2230
|
+
for (const plugin of wanted) {
|
|
2231
|
+
let res;
|
|
2232
|
+
try {
|
|
2233
|
+
if (!Array.isArray(plugin.kinds) || !plugin.kinds.includes(kind))
|
|
2234
|
+
continue;
|
|
2235
|
+
res = plugin.run(text, meta);
|
|
2236
|
+
} catch {
|
|
2237
|
+
continue;
|
|
2238
|
+
}
|
|
2239
|
+
if (!res || typeof res.text !== "string" || res.text === "" || res.text === text)
|
|
2240
|
+
continue;
|
|
2241
|
+
const newTok = countTokens(res.text);
|
|
2242
|
+
const reduction = (rawTok - newTok) / rawTok;
|
|
2243
|
+
if (reduction < minReduction) continue;
|
|
2244
|
+
if (kind === "log" && res.lossy && !errorLinesSurvive(text, res.text))
|
|
2245
|
+
continue;
|
|
2246
|
+
const out = {
|
|
2247
|
+
text: res.text,
|
|
2248
|
+
strategy: plugin.id,
|
|
2249
|
+
lossy: res.lossy
|
|
2250
|
+
};
|
|
2251
|
+
if (res.stats) out.stats = res.stats;
|
|
2252
|
+
return out;
|
|
2253
|
+
}
|
|
2254
|
+
return null;
|
|
2255
|
+
}
|
|
2256
|
+
function canonOf(v, cache2) {
|
|
2257
|
+
if (!isPlainObject(v) && !Array.isArray(v))
|
|
2258
|
+
return JSON.stringify(v) ?? "null";
|
|
2259
|
+
const key = v;
|
|
2260
|
+
const hit = cache2.get(key);
|
|
2261
|
+
if (hit !== void 0) return hit;
|
|
2262
|
+
let s;
|
|
2263
|
+
if (Array.isArray(v)) {
|
|
2264
|
+
s = `[${v.map((e) => canonOf(e, cache2)).join(",")}]`;
|
|
2265
|
+
} else {
|
|
2266
|
+
const entries = Object.entries(v).map(([k, val]) => `${JSON.stringify(k)}:${canonOf(val, cache2)}`).sort();
|
|
2267
|
+
s = `{${entries.join(",")}}`;
|
|
2268
|
+
}
|
|
2269
|
+
cache2.set(key, s);
|
|
2270
|
+
return s;
|
|
2271
|
+
}
|
|
2272
|
+
function strLen(v) {
|
|
2273
|
+
return JSON.stringify(v).length;
|
|
2274
|
+
}
|
|
2275
|
+
function compactValue(v, depth, stats, cache2) {
|
|
2276
|
+
if (depth > MAX_DEPTH || stats.folds >= MAX_FOLDS) return v;
|
|
2277
|
+
if (Array.isArray(v)) return compactArray(v, depth, stats, cache2);
|
|
2278
|
+
if (isPlainObject(v)) {
|
|
2279
|
+
let changed = false;
|
|
2280
|
+
const out = {};
|
|
2281
|
+
for (const [k, val] of Object.entries(v)) {
|
|
2282
|
+
const nv = compactValue(val, depth + 1, stats, cache2);
|
|
2283
|
+
out[k] = nv;
|
|
2284
|
+
if (nv !== val) changed = true;
|
|
2285
|
+
}
|
|
2286
|
+
return changed ? out : v;
|
|
2287
|
+
}
|
|
2288
|
+
return v;
|
|
2289
|
+
}
|
|
2290
|
+
function compactArray(arr, depth, stats, cache2) {
|
|
2291
|
+
const n = arr.length;
|
|
2292
|
+
if (n < RUN_MIN && n < HOIST_MIN)
|
|
2293
|
+
return arr.map((e) => compactValue(e, depth + 1, stats, cache2));
|
|
2294
|
+
if (stats.folds >= MAX_FOLDS) return arr;
|
|
2295
|
+
const plainStats = { folds: 0 };
|
|
2296
|
+
const plain = arr.map((e) => compactValue(e, depth + 1, plainStats, cache2));
|
|
2297
|
+
let winner = plain;
|
|
2298
|
+
let winnerLen = strLen(plain);
|
|
2299
|
+
let winnerFolds = plainStats.folds;
|
|
2300
|
+
if (n >= RUN_MIN) {
|
|
2301
|
+
const rf = buildRunFold(arr, depth, cache2);
|
|
2302
|
+
if (rf && rf.len < winnerLen) {
|
|
2303
|
+
winner = rf.value;
|
|
2304
|
+
winnerLen = rf.len;
|
|
2305
|
+
winnerFolds = rf.folds;
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
if (n >= HOIST_MIN && arr.every((e) => isPlainObject(e))) {
|
|
2309
|
+
const hc = buildConstHoist(arr, depth, cache2);
|
|
2310
|
+
if (hc && hc.len < winnerLen) {
|
|
2311
|
+
winner = hc.value;
|
|
2312
|
+
winnerLen = hc.len;
|
|
2313
|
+
winnerFolds = hc.folds;
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
stats.folds += winnerFolds;
|
|
2317
|
+
return winner;
|
|
2318
|
+
}
|
|
2319
|
+
function isRunMarker(v) {
|
|
2320
|
+
return isPlainObject(v) && v[CRUSH_KEY] === "identical-run";
|
|
2321
|
+
}
|
|
2322
|
+
function buildRunFold(arr, depth, cache2) {
|
|
2323
|
+
const n = arr.length;
|
|
2324
|
+
const canon = arr.map((e) => canonOf(e, cache2));
|
|
2325
|
+
const segs = [];
|
|
2326
|
+
let runs = 0;
|
|
2327
|
+
let i = 0;
|
|
2328
|
+
while (i < n) {
|
|
2329
|
+
let j = i;
|
|
2330
|
+
while (j + 1 < n && canon[j + 1] === canon[i]) j++;
|
|
2331
|
+
const runLen = j - i + 1;
|
|
2332
|
+
if (runLen >= RUN_MIN) {
|
|
2333
|
+
segs.push({ [CRUSH_KEY]: "identical-run", count: runLen, item: arr[i] });
|
|
2334
|
+
runs++;
|
|
2335
|
+
} else {
|
|
2336
|
+
for (let k = i; k <= j; k++) segs.push(arr[k]);
|
|
2337
|
+
}
|
|
2338
|
+
i = j + 1;
|
|
2339
|
+
}
|
|
2340
|
+
if (runs === 0) return null;
|
|
2341
|
+
const inner = { folds: 0 };
|
|
2342
|
+
const final = segs.map(
|
|
2343
|
+
(s) => isRunMarker(s) ? { ...s, item: compactValue(s.item, depth + 1, inner, cache2) } : compactValue(s, depth + 1, inner, cache2)
|
|
2344
|
+
);
|
|
2345
|
+
return { value: final, len: strLen(final), folds: runs + inner.folds };
|
|
2346
|
+
}
|
|
2347
|
+
function buildConstHoist(arr, depth, cache2) {
|
|
2348
|
+
const n = arr.length;
|
|
2349
|
+
const keys = [];
|
|
2350
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2351
|
+
for (const e of arr) {
|
|
2352
|
+
for (const k of Object.keys(e)) {
|
|
2353
|
+
if (!seen.has(k)) {
|
|
2354
|
+
seen.add(k);
|
|
2355
|
+
keys.push(k);
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
const first = arr[0];
|
|
2360
|
+
if (!first) return null;
|
|
2361
|
+
const constKeys = [];
|
|
2362
|
+
for (const k of keys) {
|
|
2363
|
+
if (!(k in first)) continue;
|
|
2364
|
+
const c0 = canonOf(first[k], cache2);
|
|
2365
|
+
let constant = true;
|
|
2366
|
+
for (let i = 1; i < n; i++) {
|
|
2367
|
+
const row = arr[i];
|
|
2368
|
+
if (!(k in row) || canonOf(row[k], cache2) !== c0) {
|
|
2369
|
+
constant = false;
|
|
2370
|
+
break;
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
if (constant) constKeys.push(k);
|
|
2374
|
+
}
|
|
2375
|
+
if (constKeys.length === 0) return null;
|
|
2376
|
+
const constSet = new Set(constKeys);
|
|
2377
|
+
const constObj = {};
|
|
2378
|
+
for (const k of constKeys) constObj[k] = first[k];
|
|
2379
|
+
const items = arr.map((e) => {
|
|
2380
|
+
const o = {};
|
|
2381
|
+
for (const [k, v] of Object.entries(e)) {
|
|
2382
|
+
if (!constSet.has(k)) o[k] = v;
|
|
2383
|
+
}
|
|
2384
|
+
return o;
|
|
2385
|
+
});
|
|
2386
|
+
const hasVarying = items.some((o) => Object.keys(o).length > 0);
|
|
2387
|
+
const inner = { folds: 0 };
|
|
2388
|
+
const env = {
|
|
2389
|
+
[CRUSH_KEY]: "rows",
|
|
2390
|
+
rows: n,
|
|
2391
|
+
const: compactValue(constObj, depth + 1, inner, cache2)
|
|
2392
|
+
};
|
|
2393
|
+
if (hasVarying)
|
|
2394
|
+
env.items = items.map((it) => compactValue(it, depth + 1, inner, cache2));
|
|
2395
|
+
const len = strLen(env);
|
|
2396
|
+
if (len >= strLen(arr)) return null;
|
|
2397
|
+
return { value: env, len, folds: 1 + inner.folds };
|
|
2398
|
+
}
|
|
2399
|
+
function crushJson(text) {
|
|
2400
|
+
const parsed = JSON.parse(text);
|
|
2401
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
2402
|
+
const stats = { folds: 0 };
|
|
2403
|
+
const out = compactValue(parsed, 0, stats, cache2);
|
|
2404
|
+
if (stats.folds === 0) return null;
|
|
2405
|
+
const s = JSON.stringify(out);
|
|
2406
|
+
return s.length < text.length ? s : null;
|
|
2407
|
+
}
|
|
2408
|
+
function detectLanguage(text) {
|
|
2409
|
+
const sample = text.split(/\r?\n/).slice(0, 300);
|
|
2410
|
+
let py = 0;
|
|
2411
|
+
let js = 0;
|
|
2412
|
+
for (const line of sample) {
|
|
2413
|
+
if (/^\s*#!.*python/.test(line)) py += 4;
|
|
2414
|
+
if (/^\s*(async\s+)?def\s+\w/.test(line) || /^\s*class\s+\w/.test(line))
|
|
2415
|
+
py += 2;
|
|
2416
|
+
if (/\bfrom\s+['"]/.test(line) || /^\s*import\s+['"]/.test(line)) js += 2;
|
|
2417
|
+
else if (/^\s*(import|from)\s+[A-Za-z_]\w*/.test(line)) py += 1;
|
|
2418
|
+
if (/^\s*(function\b|const\s|let\s|var\s)/.test(line)) js += 2;
|
|
2419
|
+
if (/=>/.test(line)) js += 1;
|
|
2420
|
+
if (/^\s*(public|private|protected|static|final|void|return)\b/.test(line))
|
|
2421
|
+
js += 1;
|
|
2422
|
+
if (/;\s*$/.test(line) && line.trim().length > 0) js += 1;
|
|
2423
|
+
if (/\bself\./.test(line) || /^\s*(elif|except|yield)\b/.test(line))
|
|
2424
|
+
py += 1;
|
|
2425
|
+
if (/\bconsole\.log\b|\brequire\s*\(|module\.exports|\bawait\s/.test(line))
|
|
2426
|
+
js += 1;
|
|
2427
|
+
}
|
|
2428
|
+
const MARGIN = 1.5;
|
|
2429
|
+
if (py >= 3 && py > js * MARGIN) return "python";
|
|
2430
|
+
if (js >= 4 && js > py * MARGIN) return "js";
|
|
2431
|
+
return null;
|
|
2432
|
+
}
|
|
2433
|
+
function finalizeTrimmed(original, emitted) {
|
|
2434
|
+
if (emitted.length >= original.split(/\r?\n/).length) return null;
|
|
2435
|
+
const out = emitted.join("\n") + (original.endsWith("\n") ? "\n" : "");
|
|
2436
|
+
return out.length < original.length * CODE_KEEP_RATIO ? out : null;
|
|
2437
|
+
}
|
|
2438
|
+
function trimPython(text) {
|
|
2439
|
+
const lines = text.split(/\r?\n/);
|
|
2440
|
+
const emitted = [];
|
|
2441
|
+
let pending = 0;
|
|
2442
|
+
const flush = () => {
|
|
2443
|
+
if (pending > 0) {
|
|
2444
|
+
emitted.push(
|
|
2445
|
+
`# [acp-crush: elided ${pending} line${pending === 1 ? "" : "s"}]`
|
|
2446
|
+
);
|
|
2447
|
+
pending = 0;
|
|
2448
|
+
}
|
|
2449
|
+
};
|
|
2450
|
+
let state = "code";
|
|
2451
|
+
let quote = "";
|
|
2452
|
+
let isDocstring = false;
|
|
2453
|
+
let blankRun = 0;
|
|
2454
|
+
let atModuleStart = true;
|
|
2455
|
+
let pendingDefClass = false;
|
|
2456
|
+
let docstringSlot = false;
|
|
2457
|
+
const classifySig = (codePart) => {
|
|
2458
|
+
const t = codePart.trim();
|
|
2459
|
+
if (/^(async\s+)?(def|class)\b/.test(t)) pendingDefClass = true;
|
|
2460
|
+
if (t.endsWith(":")) {
|
|
2461
|
+
docstringSlot = pendingDefClass;
|
|
2462
|
+
pendingDefClass = false;
|
|
2463
|
+
} else if (t.length > 0 && !t.startsWith("@")) {
|
|
2464
|
+
docstringSlot = false;
|
|
2465
|
+
}
|
|
2466
|
+
atModuleStart = false;
|
|
2467
|
+
};
|
|
2468
|
+
for (let li = 0; li < lines.length; li++) {
|
|
2469
|
+
const line = lines[li];
|
|
2470
|
+
if (state === "triple") {
|
|
2471
|
+
const close = findTripleClose(line, quote);
|
|
2472
|
+
if (close === -1) {
|
|
2473
|
+
if (isDocstring) pending++;
|
|
2474
|
+
else emitted.push(line);
|
|
2475
|
+
continue;
|
|
2476
|
+
}
|
|
2477
|
+
state = "code";
|
|
2478
|
+
const rest = line.slice(close + 3).trim();
|
|
2479
|
+
if (rest.length > 0) {
|
|
2480
|
+
flush();
|
|
2481
|
+
emitted.push(line);
|
|
2482
|
+
classifySig(rest);
|
|
2483
|
+
} else {
|
|
2484
|
+
if (isDocstring) pending++;
|
|
2485
|
+
else emitted.push(line);
|
|
2486
|
+
}
|
|
2487
|
+
isDocstring = false;
|
|
2488
|
+
docstringSlot = false;
|
|
2489
|
+
pendingDefClass = false;
|
|
2490
|
+
atModuleStart = false;
|
|
2491
|
+
blankRun = 0;
|
|
2492
|
+
continue;
|
|
2493
|
+
}
|
|
2494
|
+
if (line.trim().length === 0) {
|
|
2495
|
+
blankRun++;
|
|
2496
|
+
if (blankRun >= 3) {
|
|
2497
|
+
flush();
|
|
2498
|
+
emitted.push("");
|
|
2499
|
+
blankRun = 1;
|
|
2500
|
+
}
|
|
2501
|
+
continue;
|
|
2502
|
+
}
|
|
2503
|
+
if (li === 0 && line.startsWith("#!")) {
|
|
2504
|
+
flush();
|
|
2505
|
+
emitted.push(line);
|
|
2506
|
+
blankRun = 0;
|
|
2507
|
+
continue;
|
|
2508
|
+
}
|
|
2509
|
+
const m = line.match(/^\s*("""|''')/);
|
|
2510
|
+
if (m) {
|
|
2511
|
+
flush();
|
|
2512
|
+
const q = m[1];
|
|
2513
|
+
const opensHere = line.indexOf(q);
|
|
2514
|
+
const closeOnSameLine = findTripleClose(line.slice(opensHere + 3), q);
|
|
2515
|
+
if (closeOnSameLine !== -1) {
|
|
2516
|
+
const restAfter = line.slice(opensHere + 3 + closeOnSameLine + 3).trim();
|
|
2517
|
+
if (restAfter.length > 0) {
|
|
2518
|
+
emitted.push(line);
|
|
2519
|
+
classifySig(restAfter);
|
|
2520
|
+
} else if (atModuleStart || docstringSlot) {
|
|
2521
|
+
pending++;
|
|
2522
|
+
atModuleStart = false;
|
|
2523
|
+
docstringSlot = false;
|
|
2524
|
+
pendingDefClass = false;
|
|
2525
|
+
} else {
|
|
2526
|
+
emitted.push(line);
|
|
2527
|
+
}
|
|
2528
|
+
blankRun = 0;
|
|
2529
|
+
continue;
|
|
2530
|
+
}
|
|
2531
|
+
state = "triple";
|
|
2532
|
+
quote = q;
|
|
2533
|
+
isDocstring = atModuleStart || docstringSlot;
|
|
2534
|
+
atModuleStart = false;
|
|
2535
|
+
docstringSlot = false;
|
|
2536
|
+
if (!isDocstring) emitted.push(line);
|
|
2537
|
+
blankRun = 0;
|
|
2538
|
+
continue;
|
|
2539
|
+
}
|
|
2540
|
+
const probe = findCommentOrTriple(line);
|
|
2541
|
+
if (probe.kind === "comment-full") {
|
|
2542
|
+
pending++;
|
|
2543
|
+
continue;
|
|
2544
|
+
}
|
|
2545
|
+
if (probe.kind === "triple-mid") {
|
|
2546
|
+
flush();
|
|
2547
|
+
state = "triple";
|
|
2548
|
+
quote = probe.quote;
|
|
2549
|
+
isDocstring = false;
|
|
2550
|
+
emitted.push(line);
|
|
2551
|
+
classifySig(line.slice(0, probe.idx));
|
|
2552
|
+
blankRun = 0;
|
|
2553
|
+
continue;
|
|
2554
|
+
}
|
|
2555
|
+
flush();
|
|
2556
|
+
emitted.push(line);
|
|
2557
|
+
classifySig(line);
|
|
2558
|
+
blankRun = 0;
|
|
2559
|
+
}
|
|
2560
|
+
if (state === "triple") return null;
|
|
2561
|
+
flush();
|
|
2562
|
+
return finalizeTrimmed(text, emitted);
|
|
2563
|
+
}
|
|
2564
|
+
function findTripleClose(line, quote) {
|
|
2565
|
+
let i = 0;
|
|
2566
|
+
while (i < line.length) {
|
|
2567
|
+
if (line[i] === "\\") {
|
|
2568
|
+
i += 2;
|
|
2569
|
+
continue;
|
|
2570
|
+
}
|
|
2571
|
+
if (line.startsWith(quote, i)) return i;
|
|
2572
|
+
i++;
|
|
2573
|
+
}
|
|
2574
|
+
return -1;
|
|
2575
|
+
}
|
|
2576
|
+
function findCommentOrTriple(line) {
|
|
2577
|
+
let i = 0;
|
|
2578
|
+
let inStr = null;
|
|
2579
|
+
while (i < line.length) {
|
|
2580
|
+
const c = line[i];
|
|
2581
|
+
if (inStr) {
|
|
2582
|
+
if (c === "\\") i += 2;
|
|
2583
|
+
else if (c === inStr) inStr = null;
|
|
2584
|
+
else i++;
|
|
2585
|
+
continue;
|
|
2586
|
+
}
|
|
2587
|
+
if (c === "'" || c === '"') {
|
|
2588
|
+
inStr = c;
|
|
2589
|
+
i++;
|
|
2590
|
+
continue;
|
|
2591
|
+
}
|
|
2592
|
+
if (c === "#") {
|
|
2593
|
+
return {
|
|
2594
|
+
kind: line.slice(0, i).trim().length === 0 ? "comment-full" : "none",
|
|
2595
|
+
idx: i,
|
|
2596
|
+
quote: ""
|
|
2597
|
+
};
|
|
2598
|
+
}
|
|
2599
|
+
if (line.startsWith('"""', i) || line.startsWith("'''", i)) {
|
|
2600
|
+
return { kind: "triple-mid", idx: i, quote: line.slice(i, i + 3) };
|
|
2601
|
+
}
|
|
2602
|
+
i++;
|
|
2603
|
+
}
|
|
2604
|
+
return { kind: "none", idx: -1, quote: "" };
|
|
2605
|
+
}
|
|
2606
|
+
function scanTemplateRest(line, from, st) {
|
|
2607
|
+
let m = st.mode;
|
|
2608
|
+
let q = st.quote;
|
|
2609
|
+
let d = st.depth;
|
|
2610
|
+
let i = from;
|
|
2611
|
+
while (i < line.length) {
|
|
2612
|
+
const c = line[i];
|
|
2613
|
+
if (m === "str") {
|
|
2614
|
+
if (c === "\\") i += 2;
|
|
2615
|
+
else if (c === q) m = "expr";
|
|
2616
|
+
else i++;
|
|
2617
|
+
continue;
|
|
2618
|
+
}
|
|
2619
|
+
if (m === "expr") {
|
|
2620
|
+
if (c === "'" || c === '"') {
|
|
2621
|
+
q = c;
|
|
2622
|
+
m = "str";
|
|
2623
|
+
i++;
|
|
2624
|
+
continue;
|
|
2625
|
+
}
|
|
2626
|
+
if (c === "`")
|
|
2627
|
+
return {
|
|
2628
|
+
mode: m,
|
|
2629
|
+
quote: q,
|
|
2630
|
+
depth: d,
|
|
2631
|
+
closed: false,
|
|
2632
|
+
bail: true,
|
|
2633
|
+
consumed: 0
|
|
2634
|
+
};
|
|
2635
|
+
if (c === "{") d++;
|
|
2636
|
+
else if (c === "}") {
|
|
2637
|
+
d--;
|
|
2638
|
+
if (d === 0) m = "tpl";
|
|
2639
|
+
}
|
|
2640
|
+
i++;
|
|
2641
|
+
continue;
|
|
2642
|
+
}
|
|
2643
|
+
if (c === "\\") {
|
|
2644
|
+
i += 2;
|
|
2645
|
+
continue;
|
|
2646
|
+
}
|
|
2647
|
+
if (c === "`")
|
|
2648
|
+
return {
|
|
2649
|
+
mode: "tpl",
|
|
2650
|
+
quote: "",
|
|
2651
|
+
depth: 0,
|
|
2652
|
+
closed: true,
|
|
2653
|
+
bail: false,
|
|
2654
|
+
consumed: i - from + 1
|
|
2655
|
+
};
|
|
2656
|
+
if (c === "$" && line[i + 1] === "{") {
|
|
2657
|
+
m = "expr";
|
|
2658
|
+
d = 1;
|
|
2659
|
+
i += 2;
|
|
2660
|
+
continue;
|
|
2661
|
+
}
|
|
2662
|
+
i++;
|
|
2663
|
+
}
|
|
2664
|
+
return {
|
|
2665
|
+
mode: m,
|
|
2666
|
+
quote: q,
|
|
2667
|
+
depth: d,
|
|
2668
|
+
closed: false,
|
|
2669
|
+
bail: false,
|
|
2670
|
+
consumed: 0
|
|
2671
|
+
};
|
|
2672
|
+
}
|
|
2673
|
+
function regexAllowedAfter(prev) {
|
|
2674
|
+
if (prev === null) return true;
|
|
2675
|
+
if (/[A-Za-z0-9_]/.test(prev)) return false;
|
|
2676
|
+
return !".'\"`) ]".includes(prev);
|
|
2677
|
+
}
|
|
2678
|
+
function prevSigChar(line, i) {
|
|
2679
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
2680
|
+
const c = line[j];
|
|
2681
|
+
if (c === " " || c === " ") continue;
|
|
2682
|
+
return c;
|
|
2683
|
+
}
|
|
2684
|
+
return null;
|
|
2685
|
+
}
|
|
2686
|
+
function scanRegexLiteral(line, i) {
|
|
2687
|
+
let j = i + 1;
|
|
2688
|
+
let inClass = false;
|
|
2689
|
+
while (j < line.length) {
|
|
2690
|
+
const c = line[j];
|
|
2691
|
+
if (c === "\\") {
|
|
2692
|
+
j += 2;
|
|
2693
|
+
continue;
|
|
2694
|
+
}
|
|
2695
|
+
if (inClass) {
|
|
2696
|
+
if (c === "]") inClass = false;
|
|
2697
|
+
} else if (c === "[") {
|
|
2698
|
+
inClass = true;
|
|
2699
|
+
} else if (c === "/") {
|
|
2700
|
+
let f = j + 1;
|
|
2701
|
+
while (f < line.length && /[a-z]/i.test(line[f])) f++;
|
|
2702
|
+
return f;
|
|
2703
|
+
}
|
|
2704
|
+
j++;
|
|
2705
|
+
}
|
|
2706
|
+
return null;
|
|
2707
|
+
}
|
|
2708
|
+
function scanJsCodeLine(line) {
|
|
2709
|
+
let i = 0;
|
|
2710
|
+
let inStr = null;
|
|
2711
|
+
while (i < line.length) {
|
|
2712
|
+
const c = line[i];
|
|
2713
|
+
if (inStr) {
|
|
2714
|
+
if (c === "\\") i += 2;
|
|
2715
|
+
else if (c === inStr) inStr = null;
|
|
2716
|
+
else i++;
|
|
2717
|
+
continue;
|
|
2718
|
+
}
|
|
2719
|
+
if (c === "'" || c === '"') {
|
|
2720
|
+
inStr = c;
|
|
2721
|
+
i++;
|
|
2722
|
+
continue;
|
|
2723
|
+
}
|
|
2724
|
+
if (c === "`") {
|
|
2725
|
+
const r = scanTemplateRest(line, i, { mode: "tpl", quote: "", depth: 0 });
|
|
2726
|
+
if (r.bail) return { kind: "code", bail: true };
|
|
2727
|
+
if (!r.closed)
|
|
2728
|
+
return {
|
|
2729
|
+
kind: "template-open",
|
|
2730
|
+
tmpl: { mode: r.mode, quote: r.quote, depth: r.depth }
|
|
2731
|
+
};
|
|
2732
|
+
i += r.consumed;
|
|
2733
|
+
continue;
|
|
2734
|
+
}
|
|
2735
|
+
if (c === "/") {
|
|
2736
|
+
const nxt = line[i + 1];
|
|
2737
|
+
if (nxt === "/") {
|
|
2738
|
+
return {
|
|
2739
|
+
kind: line.slice(0, i).trim().length === 0 ? "comment-full" : "code"
|
|
2740
|
+
};
|
|
2741
|
+
}
|
|
2742
|
+
if (nxt !== "*") {
|
|
2743
|
+
if (regexAllowedAfter(prevSigChar(line, i))) {
|
|
2744
|
+
const end = scanRegexLiteral(line, i);
|
|
2745
|
+
if (end !== null) {
|
|
2746
|
+
i = end;
|
|
2747
|
+
continue;
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
i++;
|
|
2751
|
+
continue;
|
|
2752
|
+
}
|
|
2753
|
+
const close = line.indexOf("*/", i + 2);
|
|
2754
|
+
const before = line.slice(0, i).trim().length > 0;
|
|
2755
|
+
if (close === -1)
|
|
2756
|
+
return { kind: before ? "block-open-mid" : "block-open-only" };
|
|
2757
|
+
const rest = line.slice(close + 2).trim();
|
|
2758
|
+
if (rest.length === 0) return { kind: before ? "code" : "comment-full" };
|
|
2759
|
+
i = close + 2;
|
|
2760
|
+
continue;
|
|
2761
|
+
}
|
|
2762
|
+
i++;
|
|
2763
|
+
}
|
|
2764
|
+
return { kind: "code" };
|
|
2765
|
+
}
|
|
2766
|
+
function trimJsTs(text) {
|
|
2767
|
+
const lines = text.split(/\r?\n/);
|
|
2768
|
+
const emitted = [];
|
|
2769
|
+
let pending = 0;
|
|
2770
|
+
const flush = () => {
|
|
2771
|
+
if (pending > 0) {
|
|
2772
|
+
emitted.push(
|
|
2773
|
+
`// [acp-crush: elided ${pending} line${pending === 1 ? "" : "s"}]`
|
|
2774
|
+
);
|
|
2775
|
+
pending = 0;
|
|
2776
|
+
}
|
|
2777
|
+
};
|
|
2778
|
+
let state = "code";
|
|
2779
|
+
let tmpl = { mode: "tpl", quote: "", depth: 0 };
|
|
2780
|
+
let blankRun = 0;
|
|
2781
|
+
for (const line of lines) {
|
|
2782
|
+
if (state === "block") {
|
|
2783
|
+
const close = line.indexOf("*/");
|
|
2784
|
+
if (close === -1) {
|
|
2785
|
+
pending++;
|
|
2786
|
+
continue;
|
|
2787
|
+
}
|
|
2788
|
+
state = "code";
|
|
2789
|
+
if (line.slice(close + 2).trim().length === 0) pending++;
|
|
2790
|
+
else {
|
|
2791
|
+
flush();
|
|
2792
|
+
emitted.push(line);
|
|
2793
|
+
}
|
|
2794
|
+
blankRun = 0;
|
|
2795
|
+
continue;
|
|
2796
|
+
}
|
|
2797
|
+
if (state === "template") {
|
|
2798
|
+
const r = scanTemplateRest(line, 0, tmpl);
|
|
2799
|
+
if (r.bail) return null;
|
|
2800
|
+
tmpl = { mode: r.mode, quote: r.quote, depth: r.depth };
|
|
2801
|
+
if (r.closed) state = "code";
|
|
2802
|
+
emitted.push(line);
|
|
2803
|
+
blankRun = 0;
|
|
2804
|
+
continue;
|
|
2805
|
+
}
|
|
2806
|
+
if (line.trim().length === 0) {
|
|
2807
|
+
blankRun++;
|
|
2808
|
+
if (blankRun >= 3) {
|
|
2809
|
+
flush();
|
|
2810
|
+
emitted.push("");
|
|
2811
|
+
blankRun = 1;
|
|
2812
|
+
}
|
|
2813
|
+
continue;
|
|
2814
|
+
}
|
|
2815
|
+
const scan = scanJsCodeLine(line);
|
|
2816
|
+
if (scan.bail) return null;
|
|
2817
|
+
if (scan.kind === "comment-full") {
|
|
2818
|
+
pending++;
|
|
2819
|
+
continue;
|
|
2820
|
+
}
|
|
2821
|
+
if (scan.kind === "block-open-only") {
|
|
2822
|
+
pending++;
|
|
2823
|
+
state = "block";
|
|
2824
|
+
continue;
|
|
2825
|
+
}
|
|
2826
|
+
if (scan.kind === "block-open-mid") {
|
|
2827
|
+
flush();
|
|
2828
|
+
state = "block";
|
|
2829
|
+
emitted.push(line);
|
|
2830
|
+
blankRun = 0;
|
|
2831
|
+
continue;
|
|
2832
|
+
}
|
|
2833
|
+
if (scan.kind === "template-open") {
|
|
2834
|
+
flush();
|
|
2835
|
+
state = "template";
|
|
2836
|
+
tmpl = scan.tmpl ?? { mode: "tpl", quote: "", depth: 0 };
|
|
2837
|
+
emitted.push(line);
|
|
2838
|
+
blankRun = 0;
|
|
2839
|
+
continue;
|
|
2840
|
+
}
|
|
2841
|
+
flush();
|
|
2842
|
+
emitted.push(line);
|
|
2843
|
+
blankRun = 0;
|
|
2844
|
+
}
|
|
2845
|
+
if (state !== "code") return null;
|
|
2846
|
+
flush();
|
|
2847
|
+
return finalizeTrimmed(text, emitted);
|
|
2848
|
+
}
|
|
2849
|
+
function crushCode(text) {
|
|
2850
|
+
const lang = detectLanguage(text);
|
|
2851
|
+
if (lang === null) return null;
|
|
2852
|
+
if (lang === "python") return trimPython(text);
|
|
2853
|
+
return trimJsTs(text);
|
|
2854
|
+
}
|
|
2855
|
+
var LOG_MIN_LINES = 50;
|
|
2856
|
+
var LOG_MAX_TOTAL_LINES = 100;
|
|
2857
|
+
var LOG_MAX_ERRORS = 20;
|
|
2858
|
+
var LOG_ERROR_CONTEXT = 3;
|
|
2859
|
+
var LOG_MAX_WARNINGS = 5;
|
|
2860
|
+
var LOG_MAX_STACK_TRACES = 3;
|
|
2861
|
+
var LOG_STACK_MAX_LINES = 20;
|
|
2862
|
+
var LOG_TRACE_HEAD_FRAMES = 3;
|
|
2863
|
+
var LOG_TRACE_APP_FRAMES = 5;
|
|
2864
|
+
var LOG_CLASSIFIED_GATE = 5;
|
|
2865
|
+
var LOG_SUMMARY_GATE = 3;
|
|
2866
|
+
var LEVEL_PATTERNS = [
|
|
2867
|
+
["error", /\b(ERROR|FATAL|CRITICAL)\b/i],
|
|
2868
|
+
["fail", /\b(FAIL|FAILED)\b/i],
|
|
2869
|
+
["warn", /\b(WARN|WARNING)\b/i],
|
|
2870
|
+
["info", /\bINFO\b/i],
|
|
2871
|
+
["debug", /\bDEBUG\b/i],
|
|
2872
|
+
["trace", /\bTRACE\b/i]
|
|
2873
|
+
];
|
|
2874
|
+
function classifyLevel(line) {
|
|
2875
|
+
for (const [level, re] of LEVEL_PATTERNS) {
|
|
2876
|
+
if (re.test(line)) return level;
|
|
2877
|
+
}
|
|
2878
|
+
return "unknown";
|
|
2879
|
+
}
|
|
2880
|
+
function isLogSummaryLine(line) {
|
|
2881
|
+
if (line.startsWith("===") || line.startsWith("---")) return true;
|
|
2882
|
+
let d = 0;
|
|
2883
|
+
while (d < line.length && line.charCodeAt(d) >= 48 && line.charCodeAt(d) <= 57)
|
|
2884
|
+
d++;
|
|
2885
|
+
if (d > 0 && line[d] === " ") {
|
|
2886
|
+
const rest = line.slice(d + 1);
|
|
2887
|
+
if (rest.startsWith("passed") || rest.startsWith("failed") || rest.startsWith("skipped") || rest.startsWith("error") || rest.startsWith("warning"))
|
|
2888
|
+
return true;
|
|
2889
|
+
}
|
|
2890
|
+
for (const prefix of [
|
|
2891
|
+
"Test ",
|
|
2892
|
+
"Tests ",
|
|
2893
|
+
"Tests:",
|
|
2894
|
+
"Test:",
|
|
2895
|
+
"Suite ",
|
|
2896
|
+
"Suites ",
|
|
2897
|
+
"Suites:",
|
|
2898
|
+
"Suite:"
|
|
2899
|
+
]) {
|
|
2900
|
+
if (line.startsWith(prefix)) {
|
|
2901
|
+
const m = line.slice(prefix.length).match(/\S/);
|
|
2902
|
+
if (m !== null && m[0].charCodeAt(0) >= 48 && m[0].charCodeAt(0) <= 57)
|
|
2903
|
+
return true;
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
if (line.startsWith("TOTAL") || line.startsWith("Total") || line.startsWith("Summary"))
|
|
2907
|
+
return true;
|
|
2908
|
+
for (const prefix of ["Build", "Compile", "Test"]) {
|
|
2909
|
+
if (line.startsWith(prefix) && (line.includes("succeeded") || line.includes("failed") || line.includes("complete")))
|
|
2910
|
+
return true;
|
|
2911
|
+
}
|
|
2912
|
+
return false;
|
|
2913
|
+
}
|
|
2914
|
+
function isDigitChar(c) {
|
|
2915
|
+
return c >= "0" && c <= "9";
|
|
2916
|
+
}
|
|
2917
|
+
function isAsciiAlnum(c) {
|
|
2918
|
+
const n = c.charCodeAt(0);
|
|
2919
|
+
return n >= 97 && n <= 122 || n >= 65 && n <= 90 || n >= 48 && n <= 57;
|
|
2920
|
+
}
|
|
2921
|
+
function hasLineColSuffix(s) {
|
|
2922
|
+
for (let i = 0; i + 1 < s.length; i++) {
|
|
2923
|
+
if (s[i] === ":" && isDigitChar(s[i + 1])) {
|
|
2924
|
+
let j = i + 1;
|
|
2925
|
+
while (j < s.length && isDigitChar(s[j])) j++;
|
|
2926
|
+
if (j + 1 < s.length && s[j] === ":" && isDigitChar(s[j + 1]))
|
|
2927
|
+
return true;
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
return false;
|
|
2931
|
+
}
|
|
2932
|
+
function isPythonFileFrame(s) {
|
|
2933
|
+
return s.startsWith('File "') && s.includes('", line ') && s.length > 0 && isDigitChar(s[s.length - 1]);
|
|
2934
|
+
}
|
|
2935
|
+
function isJsAtFrame(s) {
|
|
2936
|
+
return s.startsWith("at ") && s.includes("(") && s.includes(")") && hasLineColSuffix(s);
|
|
2937
|
+
}
|
|
2938
|
+
function isJavaAtFrame(s) {
|
|
2939
|
+
if (!s.startsWith("at ") || !s.includes("(")) return false;
|
|
2940
|
+
const open = s.indexOf("(");
|
|
2941
|
+
const body = s.slice(3, open);
|
|
2942
|
+
if (body.length === 0) return false;
|
|
2943
|
+
for (const c of body) {
|
|
2944
|
+
if (!(isAsciiAlnum(c) || c === "." || c === "_" || c === "$" || c === "/"))
|
|
2945
|
+
return false;
|
|
2946
|
+
}
|
|
2947
|
+
return true;
|
|
2948
|
+
}
|
|
2949
|
+
function isRustPanicOpener(s) {
|
|
2950
|
+
return s.startsWith("thread '") && s.includes("panicked at");
|
|
2951
|
+
}
|
|
2952
|
+
function isGoroutineHeader(line) {
|
|
2953
|
+
if (!line.startsWith("goroutine ")) return false;
|
|
2954
|
+
const rest = line.slice(10);
|
|
2955
|
+
let d = 0;
|
|
2956
|
+
while (d < rest.length && isDigitChar(rest[d])) d++;
|
|
2957
|
+
return d > 0 && rest.slice(d).startsWith(" [");
|
|
2958
|
+
}
|
|
2959
|
+
function isGoPanicOpener(line) {
|
|
2960
|
+
return line.startsWith("panic: ") || line.startsWith("fatal error: ") || isGoroutineHeader(line);
|
|
2961
|
+
}
|
|
2962
|
+
function isGoFileFrame(line) {
|
|
2963
|
+
return line.startsWith(" ") && line.includes(".go:") && line.includes(" +0x");
|
|
2964
|
+
}
|
|
2965
|
+
function isGoCallFrame(line) {
|
|
2966
|
+
if (line.startsWith("created by ")) return true;
|
|
2967
|
+
if (line.startsWith(" ") || line.startsWith(" ") || !line.endsWith(")"))
|
|
2968
|
+
return false;
|
|
2969
|
+
const open = line.indexOf("(");
|
|
2970
|
+
if (open === -1) return false;
|
|
2971
|
+
const symbol = line.slice(0, open);
|
|
2972
|
+
if (symbol.length === 0 || !symbol.includes(".")) return false;
|
|
2973
|
+
for (const c of symbol) {
|
|
2974
|
+
if (!(isAsciiAlnum(c) || c === "." || c === "_" || c === "/" || c === "*"))
|
|
2975
|
+
return false;
|
|
2976
|
+
}
|
|
2977
|
+
return true;
|
|
2978
|
+
}
|
|
2979
|
+
function isDotnetFrame(s) {
|
|
2980
|
+
return s.startsWith("at ") && s.includes(") in ") && s.includes(":line ");
|
|
2981
|
+
}
|
|
2982
|
+
function isDotnetExceptionHead(trimmed) {
|
|
2983
|
+
const colon = trimmed.indexOf(":");
|
|
2984
|
+
if (colon === -1) return false;
|
|
2985
|
+
const head = trimmed.slice(0, colon);
|
|
2986
|
+
if (!head.endsWith("Exception") || !head.includes(".")) return false;
|
|
2987
|
+
for (const c of head) {
|
|
2988
|
+
if (!(isAsciiAlnum(c) || c === "." || c === "_" || c === "`" || c === "+"))
|
|
2989
|
+
return false;
|
|
2990
|
+
}
|
|
2991
|
+
return true;
|
|
2992
|
+
}
|
|
2993
|
+
function isRustBacktraceFrame(s) {
|
|
2994
|
+
const t = s.trimStart();
|
|
2995
|
+
let i = 0;
|
|
2996
|
+
while (i < t.length && isDigitChar(t[i])) i++;
|
|
2997
|
+
if (i === 0 || t[i] !== ":") return false;
|
|
2998
|
+
i++;
|
|
2999
|
+
while (i < t.length && t[i] === " ") i++;
|
|
3000
|
+
const rest = t.slice(i);
|
|
3001
|
+
if (!rest.startsWith("0x")) return false;
|
|
3002
|
+
let h = 0;
|
|
3003
|
+
for (const c of rest.slice(2)) {
|
|
3004
|
+
if (c >= "0" && c <= "9" || c >= "a" && c <= "f" || c >= "A" && c <= "F")
|
|
3005
|
+
h++;
|
|
3006
|
+
else break;
|
|
3007
|
+
}
|
|
3008
|
+
return h > 0;
|
|
3009
|
+
}
|
|
3010
|
+
function isJavaMoreSummary(trimmed) {
|
|
3011
|
+
if (!trimmed.startsWith("... ")) return false;
|
|
3012
|
+
const rest = trimmed.slice(4);
|
|
3013
|
+
let d = 0;
|
|
3014
|
+
while (d < rest.length && isDigitChar(rest[d])) d++;
|
|
3015
|
+
return d > 0 && rest.slice(d).trim() === "more";
|
|
3016
|
+
}
|
|
3017
|
+
function traceFlavorFor(line) {
|
|
3018
|
+
const t = line.trimStart();
|
|
3019
|
+
if (t.startsWith("Traceback (most recent call last)") || isPythonFileFrame(t))
|
|
3020
|
+
return "py";
|
|
3021
|
+
if (t.startsWith("Unhandled exception.") || isDotnetFrame(t)) return "dotnet";
|
|
3022
|
+
if (isJsAtFrame(t)) return "js";
|
|
3023
|
+
if (isJavaAtFrame(t)) return "java";
|
|
3024
|
+
if (t.startsWith("--> ") && hasLineColSuffix(t)) return "rust-error";
|
|
3025
|
+
if (isRustPanicOpener(t) || t.startsWith("stack backtrace:") || isRustBacktraceFrame(line))
|
|
3026
|
+
return "rust-backtrace";
|
|
3027
|
+
if (isGoPanicOpener(line)) return "go-panic";
|
|
3028
|
+
return null;
|
|
3029
|
+
}
|
|
3030
|
+
function traceTerminates(flavor, line, linesSoFar) {
|
|
3031
|
+
const t = line.trimStart();
|
|
3032
|
+
switch (flavor) {
|
|
3033
|
+
case "py": {
|
|
3034
|
+
const indentedOrBlank = line.startsWith(" ") || line.startsWith(" ") || line.length === 0;
|
|
3035
|
+
const continuation = t.startsWith("Traceback") || t.startsWith("File ") || t.startsWith("During handling") || t.startsWith("The above exception");
|
|
3036
|
+
if (indentedOrBlank || continuation) return false;
|
|
3037
|
+
return !(t.length > 0 && t[0] >= "A" && t[0] <= "Z");
|
|
3038
|
+
}
|
|
3039
|
+
case "js":
|
|
3040
|
+
return !t.startsWith("at ") && line.length !== 0;
|
|
3041
|
+
case "java": {
|
|
3042
|
+
const chain = t.startsWith("Caused by:") || t.startsWith("Suppressed:") || isJavaMoreSummary(t);
|
|
3043
|
+
return !t.startsWith("at ") && !chain && line.length !== 0;
|
|
3044
|
+
}
|
|
3045
|
+
case "dotnet": {
|
|
3046
|
+
if (line.length === 0) return false;
|
|
3047
|
+
const continues = t.startsWith("at ") || t.startsWith("--->") || t.startsWith("--- End of") || isDotnetExceptionHead(t);
|
|
3048
|
+
return !continues;
|
|
3049
|
+
}
|
|
3050
|
+
case "rust-error":
|
|
3051
|
+
return !t.startsWith("--> ") && line.length !== 0;
|
|
3052
|
+
case "rust-backtrace": {
|
|
3053
|
+
if (line.length === 0 || linesSoFar === 1) return false;
|
|
3054
|
+
const isFrame = t.length > 0 && isDigitChar(t[0]);
|
|
3055
|
+
const continuation = line.startsWith(" ") || line.startsWith(" ") || t.startsWith("stack backtrace:") || t.startsWith("note: run with");
|
|
3056
|
+
return !isFrame && !continuation;
|
|
3057
|
+
}
|
|
3058
|
+
case "go-panic": {
|
|
3059
|
+
if (line.length === 0) return false;
|
|
3060
|
+
const continues = line.startsWith(" ") || isGoroutineHeader(line) || isGoCallFrame(line) || line.startsWith("panic: ") || line.startsWith("fatal error: ") || line.startsWith("[signal ");
|
|
3061
|
+
return !continues;
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
function scoreLogLine(l) {
|
|
3066
|
+
const base = l.level === "error" || l.level === "fail" ? 1 : l.level === "warn" ? 0.5 : l.level === "info" || l.level === "unknown" ? 0.1 : l.level === "debug" ? 0.05 : 0.02;
|
|
3067
|
+
return Math.min(base + (l.isStack ? 0.3 : 0) + (l.isSummary ? 0.4 : 0), 1);
|
|
3068
|
+
}
|
|
3069
|
+
function parseLogLines(lines) {
|
|
3070
|
+
const out = new Array(lines.length);
|
|
3071
|
+
let active = null;
|
|
3072
|
+
let traceLines = 0;
|
|
3073
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3074
|
+
const line = lines[i];
|
|
3075
|
+
const entry = {
|
|
3076
|
+
i,
|
|
3077
|
+
content: line,
|
|
3078
|
+
level: classifyLevel(line),
|
|
3079
|
+
isStack: false,
|
|
3080
|
+
isSummary: isLogSummaryLine(line),
|
|
3081
|
+
score: 0
|
|
3082
|
+
};
|
|
3083
|
+
if (active !== null) {
|
|
3084
|
+
const flavor = active;
|
|
3085
|
+
if (traceLines >= LOG_STACK_MAX_LINES || traceTerminates(flavor, line, traceLines)) {
|
|
3086
|
+
const capHit = traceLines >= LOG_STACK_MAX_LINES;
|
|
3087
|
+
active = null;
|
|
3088
|
+
traceLines = 0;
|
|
3089
|
+
const nf = traceFlavorFor(line);
|
|
3090
|
+
if (nf !== null) {
|
|
3091
|
+
active = nf;
|
|
3092
|
+
traceLines = 1;
|
|
3093
|
+
entry.isStack = true;
|
|
3094
|
+
} else if (capHit && !traceTerminates(flavor, line, 2)) {
|
|
3095
|
+
active = flavor;
|
|
3096
|
+
traceLines = 1;
|
|
3097
|
+
entry.isStack = true;
|
|
3098
|
+
}
|
|
3099
|
+
} else {
|
|
3100
|
+
entry.isStack = true;
|
|
3101
|
+
traceLines++;
|
|
3102
|
+
}
|
|
3103
|
+
} else {
|
|
3104
|
+
const f = traceFlavorFor(line);
|
|
3105
|
+
if (f !== null) {
|
|
3106
|
+
active = f;
|
|
3107
|
+
traceLines = 1;
|
|
3108
|
+
entry.isStack = true;
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
entry.score = scoreLogLine(entry);
|
|
3112
|
+
out[i] = entry;
|
|
3113
|
+
}
|
|
3114
|
+
return out;
|
|
3115
|
+
}
|
|
3116
|
+
function selectWithFirstLast(arr, maxCount) {
|
|
3117
|
+
if (arr.length <= maxCount) return arr.slice();
|
|
3118
|
+
const out = [];
|
|
3119
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3120
|
+
const push = (l) => {
|
|
3121
|
+
if (seen.add(l.i)) out.push(l);
|
|
3122
|
+
};
|
|
3123
|
+
push(arr[0]);
|
|
3124
|
+
push(arr[arr.length - 1]);
|
|
3125
|
+
if (out.length < maxCount) {
|
|
3126
|
+
const byScore = arr.slice().sort((a, b) => b.score - a.score || a.i - b.i);
|
|
3127
|
+
for (const l of byScore) {
|
|
3128
|
+
if (!seen.has(l.i)) {
|
|
3129
|
+
push(l);
|
|
3130
|
+
if (out.length >= maxCount) break;
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
return out;
|
|
3135
|
+
}
|
|
3136
|
+
function normalizeForDedupe(content) {
|
|
3137
|
+
let splitAt = content.length;
|
|
3138
|
+
for (let i = 0; i < content.length; i++) {
|
|
3139
|
+
if (content[i] === ":" || content[i] === "=") {
|
|
3140
|
+
splitAt = i;
|
|
3141
|
+
break;
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
const suffix = content.slice(splitAt).replace(/\d+/g, "N").replace(/0x[0-9a-fA-F]+/g, "ADDR").replace(/\/[\w/]+\//g, "/PATH/");
|
|
3145
|
+
return content.slice(0, splitAt) + suffix;
|
|
3146
|
+
}
|
|
3147
|
+
var RUNTIME_FRAME_PREFIXES = [
|
|
3148
|
+
"at java.",
|
|
3149
|
+
"at jdk.",
|
|
3150
|
+
"at sun.",
|
|
3151
|
+
"at javax.",
|
|
3152
|
+
"at scala.",
|
|
3153
|
+
"at System.",
|
|
3154
|
+
"at Microsoft.",
|
|
3155
|
+
"runtime.",
|
|
3156
|
+
"created by runtime."
|
|
3157
|
+
];
|
|
3158
|
+
var RUNTIME_FRAME_MARKERS = [
|
|
3159
|
+
"site-packages/",
|
|
3160
|
+
"/usr/lib/python",
|
|
3161
|
+
"lib/python3.",
|
|
3162
|
+
"node:internal/",
|
|
3163
|
+
"node_modules/",
|
|
3164
|
+
"(internal/",
|
|
3165
|
+
"core::",
|
|
3166
|
+
"std::",
|
|
3167
|
+
"alloc::",
|
|
3168
|
+
"rust_begin_unwind",
|
|
3169
|
+
"__rust_",
|
|
3170
|
+
"/rustc/",
|
|
3171
|
+
"/usr/local/go/src/",
|
|
3172
|
+
"/libexec/src/runtime/"
|
|
3173
|
+
];
|
|
3174
|
+
function isFrameLine(content) {
|
|
3175
|
+
const t = content.trimStart();
|
|
3176
|
+
return t.startsWith("at ") || t.startsWith('File "') && t.includes('", line ') || isRustBacktraceFrame(content) || isGoFileFrame(content) || isGoCallFrame(content);
|
|
3177
|
+
}
|
|
3178
|
+
function isChainHeadLine(content) {
|
|
3179
|
+
const t = content.trimStart();
|
|
3180
|
+
return t.startsWith("Caused by:") || t.startsWith("Suppressed:") || t.startsWith("... ") || t.startsWith("--->") || t.startsWith("--- End of") || t.startsWith("During handling") || t.startsWith("The above exception");
|
|
3181
|
+
}
|
|
3182
|
+
function isRuntimeFrame(content) {
|
|
3183
|
+
const t = content.trimStart();
|
|
3184
|
+
return RUNTIME_FRAME_PREFIXES.some((p) => t.startsWith(p)) || RUNTIME_FRAME_MARKERS.some((m) => content.includes(m));
|
|
3185
|
+
}
|
|
3186
|
+
function collapseTraceFrames(stack, headFrames, appFrames) {
|
|
3187
|
+
const kept = [];
|
|
3188
|
+
const dropped = /* @__PURE__ */ new Set();
|
|
3189
|
+
let framesSeen = 0;
|
|
3190
|
+
let appKept = 0;
|
|
3191
|
+
let runStart = -1;
|
|
3192
|
+
let runLen = 0;
|
|
3193
|
+
let prevDropped = false;
|
|
3194
|
+
const flushRun = () => {
|
|
3195
|
+
if (runStart !== -1) {
|
|
3196
|
+
kept.push({
|
|
3197
|
+
i: runStart,
|
|
3198
|
+
content: ` [... ${runLen} frames collapsed]`,
|
|
3199
|
+
level: "unknown",
|
|
3200
|
+
isStack: true,
|
|
3201
|
+
isSummary: false,
|
|
3202
|
+
score: 0.8
|
|
3203
|
+
});
|
|
3204
|
+
runStart = -1;
|
|
3205
|
+
runLen = 0;
|
|
3206
|
+
}
|
|
3207
|
+
};
|
|
3208
|
+
for (const line of stack) {
|
|
3209
|
+
if (isFrameLine(line.content) && !isChainHeadLine(line.content)) {
|
|
3210
|
+
framesSeen++;
|
|
3211
|
+
const runtime = isRuntimeFrame(line.content);
|
|
3212
|
+
const keep = framesSeen <= headFrames || !runtime && appKept < appFrames;
|
|
3213
|
+
if (keep) {
|
|
3214
|
+
if (!runtime) appKept++;
|
|
3215
|
+
flushRun();
|
|
3216
|
+
kept.push(line);
|
|
3217
|
+
prevDropped = false;
|
|
3218
|
+
} else {
|
|
3219
|
+
if (runStart === -1) runStart = line.i;
|
|
3220
|
+
runLen++;
|
|
3221
|
+
dropped.add(line.i);
|
|
3222
|
+
prevDropped = true;
|
|
3223
|
+
}
|
|
3224
|
+
} else if (prevDropped && (line.content.startsWith(" ") || line.content.startsWith(" ")) && !isChainHeadLine(line.content)) {
|
|
3225
|
+
runLen++;
|
|
3226
|
+
dropped.add(line.i);
|
|
3227
|
+
} else {
|
|
3228
|
+
flushRun();
|
|
3229
|
+
kept.push(line);
|
|
3230
|
+
prevDropped = false;
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
flushRun();
|
|
3234
|
+
return { kept, dropped };
|
|
1674
3235
|
}
|
|
1675
|
-
function
|
|
1676
|
-
const
|
|
1677
|
-
const
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
3236
|
+
function selectLogLines(all) {
|
|
3237
|
+
const errors = [];
|
|
3238
|
+
const fails = [];
|
|
3239
|
+
const warnings = [];
|
|
3240
|
+
const summaries = [];
|
|
3241
|
+
const stacks = [];
|
|
3242
|
+
let current = [];
|
|
3243
|
+
for (const l of all) {
|
|
3244
|
+
if (l.level === "error") errors.push(l);
|
|
3245
|
+
else if (l.level === "fail") fails.push(l);
|
|
3246
|
+
else if (l.level === "warn") warnings.push(l);
|
|
3247
|
+
if (l.isStack) current.push(l);
|
|
3248
|
+
else if (current.length > 0) {
|
|
3249
|
+
stacks.push(current);
|
|
3250
|
+
current = [];
|
|
3251
|
+
}
|
|
3252
|
+
if (l.isSummary) summaries.push(l);
|
|
3253
|
+
}
|
|
3254
|
+
if (current.length > 0) stacks.push(current);
|
|
3255
|
+
const selected = /* @__PURE__ */ new Map();
|
|
3256
|
+
for (const l of selectWithFirstLast(errors, LOG_MAX_ERRORS))
|
|
3257
|
+
selected.set(l.i, l);
|
|
3258
|
+
for (const l of selectWithFirstLast(fails, LOG_MAX_ERRORS))
|
|
3259
|
+
selected.set(l.i, l);
|
|
3260
|
+
const seenWarn = /* @__PURE__ */ new Set();
|
|
3261
|
+
const dedupedWarnings = [];
|
|
3262
|
+
for (const w of warnings) {
|
|
3263
|
+
const key = normalizeForDedupe(w.content);
|
|
3264
|
+
if (!seenWarn.has(key)) {
|
|
3265
|
+
seenWarn.add(key);
|
|
3266
|
+
dedupedWarnings.push(w);
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
for (const w of dedupedWarnings.slice(0, LOG_MAX_WARNINGS))
|
|
3270
|
+
selected.set(w.i, w);
|
|
3271
|
+
for (const stack of stacks.slice(0, LOG_MAX_STACK_TRACES)) {
|
|
3272
|
+
if (stack.length > LOG_STACK_MAX_LINES) {
|
|
3273
|
+
const collapsed = collapseTraceFrames(
|
|
3274
|
+
stack,
|
|
3275
|
+
LOG_TRACE_HEAD_FRAMES,
|
|
3276
|
+
LOG_TRACE_APP_FRAMES
|
|
3277
|
+
);
|
|
3278
|
+
for (const i of collapsed.dropped) selected.delete(i);
|
|
3279
|
+
for (const l of collapsed.kept.slice(0, LOG_STACK_MAX_LINES))
|
|
3280
|
+
selected.set(l.i, l);
|
|
3281
|
+
} else {
|
|
3282
|
+
for (const l of stack) selected.set(l.i, l);
|
|
3283
|
+
}
|
|
1684
3284
|
}
|
|
1685
|
-
const
|
|
1686
|
-
const
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
};
|
|
3285
|
+
for (const s of summaries) selected.set(s.i, s);
|
|
3286
|
+
for (const idx of Array.from(selected.keys())) {
|
|
3287
|
+
const lo = Math.max(0, idx - LOG_ERROR_CONTEXT);
|
|
3288
|
+
const hi = Math.min(all.length, idx + LOG_ERROR_CONTEXT + 1);
|
|
3289
|
+
for (let i = lo; i < hi; i++) {
|
|
3290
|
+
if (i !== idx && !selected.has(i)) selected.set(i, all[i]);
|
|
3291
|
+
}
|
|
1693
3292
|
}
|
|
1694
|
-
|
|
1695
|
-
|
|
3293
|
+
let ordered = Array.from(selected.values());
|
|
3294
|
+
if (ordered.length > LOG_MAX_TOTAL_LINES) {
|
|
3295
|
+
ordered.sort((a, b) => b.score - a.score || a.i - b.i);
|
|
3296
|
+
ordered = ordered.slice(0, LOG_MAX_TOTAL_LINES);
|
|
3297
|
+
ordered.sort((a, b) => a.i - b.i);
|
|
3298
|
+
}
|
|
3299
|
+
return ordered;
|
|
3300
|
+
}
|
|
3301
|
+
function formatLogOutput(selected, all) {
|
|
3302
|
+
const count = (lv) => all.reduce((n, l) => l.level === lv ? n + 1 : n, 0);
|
|
3303
|
+
const output = selected.map((l) => l.content);
|
|
3304
|
+
const omitted = all.length - selected.length;
|
|
3305
|
+
if (omitted > 0) {
|
|
3306
|
+
const parts = [];
|
|
3307
|
+
const e = count("error");
|
|
3308
|
+
const f = count("fail");
|
|
3309
|
+
const w = count("warn");
|
|
3310
|
+
const inf = count("info");
|
|
3311
|
+
if (e > 0) parts.push(`${e} ERROR`);
|
|
3312
|
+
if (f > 0) parts.push(`${f} FAIL`);
|
|
3313
|
+
if (w > 0) parts.push(`${w} WARN`);
|
|
3314
|
+
if (inf > 0) parts.push(`${inf} INFO`);
|
|
3315
|
+
if (parts.length > 0)
|
|
3316
|
+
output.push(`[${omitted} lines omitted: ${parts.join(", ")}]`);
|
|
3317
|
+
}
|
|
3318
|
+
return output.join("\n");
|
|
3319
|
+
}
|
|
3320
|
+
var LOG_FORMAT_TABLE = [
|
|
3321
|
+
[
|
|
3322
|
+
"pytest",
|
|
3323
|
+
[
|
|
3324
|
+
"=== FAILURES",
|
|
3325
|
+
"=== ERRORS",
|
|
3326
|
+
"=== test session",
|
|
3327
|
+
"=== short test summary",
|
|
3328
|
+
"PASSED [",
|
|
3329
|
+
"FAILED [",
|
|
3330
|
+
"ERROR [",
|
|
3331
|
+
"SKIPPED [",
|
|
3332
|
+
"collected "
|
|
3333
|
+
]
|
|
3334
|
+
],
|
|
3335
|
+
["npm", ["npm ERR!", "npm WARN", "npm info", "npm http"]],
|
|
3336
|
+
["cargo", ["Compiling ", "Finished ", "Running ", "warning: ", "error[E"]],
|
|
3337
|
+
["jest", ["PASS ", "FAIL ", "Test Suites:"]],
|
|
3338
|
+
["make", ["make[", "make:", "gcc ", "g++ ", "clang "]]
|
|
3339
|
+
];
|
|
3340
|
+
function detectLogFormat(lines) {
|
|
3341
|
+
const sample = lines.slice(0, 100);
|
|
3342
|
+
let best = "generic";
|
|
3343
|
+
let bestScore = 0;
|
|
3344
|
+
for (const [name, pats] of LOG_FORMAT_TABLE) {
|
|
3345
|
+
let score = 0;
|
|
3346
|
+
for (const line of sample) {
|
|
3347
|
+
if (pats.some((p) => line.includes(p))) score++;
|
|
3348
|
+
}
|
|
3349
|
+
if (score > bestScore) {
|
|
3350
|
+
bestScore = score;
|
|
3351
|
+
best = name;
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
return best;
|
|
3355
|
+
}
|
|
3356
|
+
function crushLog(text) {
|
|
3357
|
+
const lines = text.split(/\r?\n/);
|
|
3358
|
+
if (lines.length < LOG_MIN_LINES) return null;
|
|
3359
|
+
let classified = 0;
|
|
3360
|
+
let summaries = 0;
|
|
3361
|
+
let traces = 0;
|
|
3362
|
+
for (const line of lines) {
|
|
3363
|
+
const lv = classifyLevel(line);
|
|
3364
|
+
if (lv !== "unknown") classified++;
|
|
3365
|
+
if (isLogSummaryLine(line)) summaries++;
|
|
3366
|
+
if (traceFlavorFor(line) !== null) traces++;
|
|
3367
|
+
}
|
|
3368
|
+
if (!(classified >= LOG_CLASSIFIED_GATE || traces >= 1 || summaries >= LOG_SUMMARY_GATE || detectLogFormat(lines) !== "generic"))
|
|
3369
|
+
return null;
|
|
3370
|
+
const parsed = parseLogLines(lines);
|
|
3371
|
+
const selected = selectLogLines(parsed);
|
|
3372
|
+
if (selected.length >= lines.length) return null;
|
|
3373
|
+
const out = formatLogOutput(selected, parsed);
|
|
3374
|
+
if (out.length >= text.length) return null;
|
|
3375
|
+
return out;
|
|
3376
|
+
}
|
|
3377
|
+
function errorLinesSurvive(input, output) {
|
|
3378
|
+
const kept = new Set(
|
|
3379
|
+
output.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0)
|
|
1696
3380
|
);
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
};
|
|
3381
|
+
for (const line of input.split(/\r?\n/)) {
|
|
3382
|
+
const level = classifyLevel(line);
|
|
3383
|
+
if (level !== "error" && level !== "fail") continue;
|
|
3384
|
+
const t = line.trim();
|
|
3385
|
+
if (t.length > 0 && !kept.has(t)) return false;
|
|
1703
3386
|
}
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
3387
|
+
return true;
|
|
3388
|
+
}
|
|
3389
|
+
var jsonFoldPlugin = {
|
|
3390
|
+
id: "json-fold",
|
|
3391
|
+
kinds: ["json"],
|
|
3392
|
+
run(text) {
|
|
3393
|
+
const out = crushJson(text);
|
|
3394
|
+
return out ? { text: out, lossy: false } : null;
|
|
1711
3395
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
3396
|
+
};
|
|
3397
|
+
var codeTrimPlugin = {
|
|
3398
|
+
id: "code-trim",
|
|
3399
|
+
kinds: ["code"],
|
|
3400
|
+
run(text) {
|
|
3401
|
+
const out = crushCode(text);
|
|
3402
|
+
return out ? { text: out, lossy: true } : null;
|
|
1718
3403
|
}
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
3404
|
+
};
|
|
3405
|
+
var logSelectPlugin = {
|
|
3406
|
+
id: "log-select",
|
|
3407
|
+
kinds: ["log"],
|
|
3408
|
+
run(text) {
|
|
3409
|
+
const out = crushLog(text);
|
|
3410
|
+
return out ? { text: out, lossy: true } : null;
|
|
1725
3411
|
}
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
3412
|
+
};
|
|
3413
|
+
var builtInPlugins = [
|
|
3414
|
+
jsonFoldPlugin,
|
|
3415
|
+
codeTrimPlugin,
|
|
3416
|
+
logSelectPlugin
|
|
3417
|
+
];
|
|
3418
|
+
var crushRegistry = /* @__PURE__ */ new Map();
|
|
3419
|
+
for (const p of builtInPlugins) crushRegistry.set(p.id, p);
|
|
3420
|
+
function registerCrushPlugin(def) {
|
|
3421
|
+
crushRegistry.set(def.id, def);
|
|
3422
|
+
}
|
|
3423
|
+
function unregisterCrushPlugin(id) {
|
|
3424
|
+
crushRegistry.delete(id);
|
|
3425
|
+
}
|
|
3426
|
+
function listCrushPlugins() {
|
|
3427
|
+
return Array.from(crushRegistry.values()).map((p) => ({
|
|
3428
|
+
id: p.id,
|
|
3429
|
+
kinds: p.kinds
|
|
3430
|
+
}));
|
|
3431
|
+
}
|
|
3432
|
+
function resetCrushPlugins() {
|
|
3433
|
+
crushRegistry.clear();
|
|
3434
|
+
for (const p of builtInPlugins) crushRegistry.set(p.id, p);
|
|
3435
|
+
}
|
|
3436
|
+
function registeredPlugins() {
|
|
3437
|
+
return Array.from(crushRegistry.values());
|
|
3438
|
+
}
|
|
3439
|
+
function resolveCrushConfig(config) {
|
|
3440
|
+
return { ...DEFAULT_CRUSH_CONFIG, ...config.crush ?? {} };
|
|
3441
|
+
}
|
|
3442
|
+
function effectivePlugins(crush, meta) {
|
|
3443
|
+
const toolName = meta.toolName;
|
|
3444
|
+
return registeredPlugins().filter((p) => {
|
|
3445
|
+
const ov = crush.strategies?.[p.id];
|
|
3446
|
+
if (ov?.enabled === false) return false;
|
|
3447
|
+
if (toolName && ov?.excludeTools?.some((pat) => matchToolPattern(toolName, pat)))
|
|
3448
|
+
return false;
|
|
3449
|
+
return true;
|
|
3450
|
+
});
|
|
3451
|
+
}
|
|
3452
|
+
function evaluateToolResult(input) {
|
|
3453
|
+
const { absorb, crush, tokenCount, modelContextLimit, meta = {} } = input;
|
|
3454
|
+
const countTokens = input.countTokens ?? defaultCountTokens;
|
|
3455
|
+
const text = input.text;
|
|
3456
|
+
const rawTokens = countTokens(text);
|
|
3457
|
+
if (absorb.contextThresholdPct > 0 && modelContextLimit > 0 && tokenCount < absorb.contextThresholdPct * modelContextLimit) {
|
|
3458
|
+
return { kind: "skip", text, rawTokens };
|
|
1735
3459
|
}
|
|
1736
|
-
if (
|
|
1737
|
-
return {
|
|
1738
|
-
state: input.state,
|
|
1739
|
-
ok: false,
|
|
1740
|
-
resultText: `absorb failed: ref ${input.ref} has no tool-call id \u2014 cannot pair it for hiding.`
|
|
1741
|
-
};
|
|
3460
|
+
if (rawTokens < absorb.minToolTokens) {
|
|
3461
|
+
return { kind: "skip", text, rawTokens };
|
|
1742
3462
|
}
|
|
1743
|
-
const
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
resultMessageId: target.id,
|
|
1752
|
-
...input.absorbCallId ? { absorbCallId: input.absorbCallId } : {},
|
|
1753
|
-
summary,
|
|
1754
|
-
tokensReclaimed: tokens,
|
|
1755
|
-
createdAt: Date.now()
|
|
1756
|
-
};
|
|
1757
|
-
const state = {
|
|
1758
|
-
...input.state,
|
|
1759
|
-
absorbed: [...input.state.absorbed ?? [], record],
|
|
1760
|
-
stats: {
|
|
1761
|
-
...input.state.stats,
|
|
1762
|
-
absorbedTokens: (input.state.stats.absorbedTokens ?? 0) + tokens
|
|
1763
|
-
}
|
|
1764
|
-
};
|
|
1765
|
-
const bloat = summaryTokens >= tokens && tokens > 0 ? ` WARNING: your summary (~${formatTokenCount(summaryTokens)} tokens) is not smaller than the original (~${formatTokenCount(tokens)} tokens) \u2014 distill harder next time.` : "";
|
|
3463
|
+
const out = crushText(text, {
|
|
3464
|
+
minReduction: crush.minReduction,
|
|
3465
|
+
countTokens,
|
|
3466
|
+
meta,
|
|
3467
|
+
plugins: effectivePlugins(crush, meta)
|
|
3468
|
+
});
|
|
3469
|
+
if (!out) return { kind: "distill", text, rawTokens };
|
|
3470
|
+
const newTokens = countTokens(out.text);
|
|
1766
3471
|
return {
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
3472
|
+
kind: newTokens < absorb.minToolTokens ? "crushed" : "distill",
|
|
3473
|
+
text: out.text,
|
|
3474
|
+
rawTokens,
|
|
3475
|
+
newTokens,
|
|
3476
|
+
reduction: (rawTokens - newTokens) / rawTokens,
|
|
3477
|
+
strategy: out.strategy,
|
|
3478
|
+
lossy: out.lossy
|
|
1770
3479
|
};
|
|
1771
3480
|
}
|
|
3481
|
+
function applyCrushToMessages(messages, config, tokenCount, countTokens) {
|
|
3482
|
+
const absorb = resolveAbsorbConfig(config);
|
|
3483
|
+
const crush = resolveCrushConfig(config);
|
|
3484
|
+
let crushedCount = 0;
|
|
3485
|
+
let distilledCount = 0;
|
|
3486
|
+
let changed = false;
|
|
3487
|
+
const out = [];
|
|
3488
|
+
for (const msg of messages) {
|
|
3489
|
+
if (!isAbsorbCandidate(msg, config)) {
|
|
3490
|
+
out.push(msg);
|
|
3491
|
+
continue;
|
|
3492
|
+
}
|
|
3493
|
+
const text = msg.text ?? "";
|
|
3494
|
+
const markerAt = text.indexOf(ABSORB_PROMPT_MARKER);
|
|
3495
|
+
const payload = markerAt >= 0 ? text.slice(0, markerAt) : text;
|
|
3496
|
+
const ev = evaluateToolResult({
|
|
3497
|
+
text: payload,
|
|
3498
|
+
absorb,
|
|
3499
|
+
crush,
|
|
3500
|
+
tokenCount,
|
|
3501
|
+
modelContextLimit: config.modelContextLimit,
|
|
3502
|
+
meta: { toolName: msg.toolName },
|
|
3503
|
+
countTokens
|
|
3504
|
+
});
|
|
3505
|
+
if (ev.kind === "skip") {
|
|
3506
|
+
out.push(msg);
|
|
3507
|
+
continue;
|
|
3508
|
+
}
|
|
3509
|
+
changed = true;
|
|
3510
|
+
if (ev.kind === "crushed") crushedCount++;
|
|
3511
|
+
else distilledCount++;
|
|
3512
|
+
out.push({ ...msg, text: ev.text });
|
|
3513
|
+
}
|
|
3514
|
+
return { messages: changed ? out : messages, crushedCount, distilledCount };
|
|
3515
|
+
}
|
|
1772
3516
|
|
|
1773
3517
|
// src/filter/registry.ts
|
|
1774
3518
|
var registry = /* @__PURE__ */ new Map();
|
|
@@ -2684,10 +4428,12 @@ function createCore(ports = {}) {
|
|
|
2684
4428
|
`[acp-kernel] Config validation warnings: ${configErrors.join("; ")}. Thresholds may not fire correctly.`
|
|
2685
4429
|
);
|
|
2686
4430
|
}
|
|
4431
|
+
const contentStore = input.contentStore ?? createContentStore();
|
|
2687
4432
|
const ctx = {
|
|
2688
4433
|
config: input.config,
|
|
2689
4434
|
tokenCount: input.tokenCount,
|
|
2690
|
-
countTokens
|
|
4435
|
+
countTokens,
|
|
4436
|
+
contentStore
|
|
2691
4437
|
};
|
|
2692
4438
|
const initial = {
|
|
2693
4439
|
messages: input.messages,
|
|
@@ -2697,14 +4443,19 @@ function createCore(ports = {}) {
|
|
|
2697
4443
|
const strategy = input.renderTags ?? "all";
|
|
2698
4444
|
const nodes = buildNodes(strategy);
|
|
2699
4445
|
const result = runPipeline(nodes, initial, ctx);
|
|
4446
|
+
const ccrEffect = result.effects.ccr;
|
|
2700
4447
|
return {
|
|
2701
4448
|
messages: result.messages,
|
|
2702
4449
|
state: result.state,
|
|
2703
4450
|
nudge: result.effects.nudge,
|
|
2704
4451
|
terminalEscape: result.effects.terminalEscape,
|
|
2705
|
-
truncationSkipped: result.effects.truncationSkipped
|
|
4452
|
+
truncationSkipped: result.effects.truncationSkipped,
|
|
4453
|
+
contentStore: ccrEffect?.store ?? contentStore
|
|
2706
4454
|
};
|
|
2707
4455
|
}
|
|
4456
|
+
function retrieve(store, ref) {
|
|
4457
|
+
return applyRetrieve({ store, ref });
|
|
4458
|
+
}
|
|
2708
4459
|
function decompress(blockId, state) {
|
|
2709
4460
|
return blockById(state, blockId);
|
|
2710
4461
|
}
|
|
@@ -2724,7 +4475,12 @@ function createCore(ports = {}) {
|
|
|
2724
4475
|
activeBlocks: active.length,
|
|
2725
4476
|
totalBlocks: state.blocks.length,
|
|
2726
4477
|
tokensCompressed: state.stats.tokensCompressed,
|
|
2727
|
-
breakdown: {
|
|
4478
|
+
breakdown: {
|
|
4479
|
+
active: active.length,
|
|
4480
|
+
total: state.blocks.length,
|
|
4481
|
+
storedMessages: state.stats.storedCount ?? 0,
|
|
4482
|
+
retrievals: state.stats.retrievalCount ?? 0
|
|
4483
|
+
}
|
|
2728
4484
|
};
|
|
2729
4485
|
}
|
|
2730
4486
|
function defaultNodes() {
|
|
@@ -2735,7 +4491,9 @@ function createCore(ports = {}) {
|
|
|
2735
4491
|
assignRefsNode,
|
|
2736
4492
|
syncBlocksNode,
|
|
2737
4493
|
pruneNode,
|
|
4494
|
+
ccrStoreNode,
|
|
2738
4495
|
absorbHideNode,
|
|
4496
|
+
crushNode,
|
|
2739
4497
|
absorbPromptNode,
|
|
2740
4498
|
filterNode,
|
|
2741
4499
|
hideCompressCallsNode,
|
|
@@ -2748,6 +4506,7 @@ function createCore(ports = {}) {
|
|
|
2748
4506
|
}
|
|
2749
4507
|
return {
|
|
2750
4508
|
processTurn,
|
|
4509
|
+
retrieve,
|
|
2751
4510
|
applyCompression,
|
|
2752
4511
|
defaultNodes,
|
|
2753
4512
|
decompress,
|
|
@@ -2764,7 +4523,9 @@ var assignRefsNode = {
|
|
|
2764
4523
|
const refResult = assignRefs(io.messages, {
|
|
2765
4524
|
existing: io.state.messageRefs,
|
|
2766
4525
|
nextIndex: highestUsedIndex(io.state.messageRefs) + 1,
|
|
2767
|
-
isProtected: protectedFn
|
|
4526
|
+
isProtected: protectedFn,
|
|
4527
|
+
// Ephemeral retrieval injections never consume a ref slot.
|
|
4528
|
+
shouldSkip: (m) => m.id.startsWith(RETRIEVED_ID_PREFIX)
|
|
2768
4529
|
});
|
|
2769
4530
|
return { ...io, state: { ...io.state, messageRefs: refResult.map } };
|
|
2770
4531
|
}
|
|
@@ -2808,6 +4569,27 @@ var absorbPromptNode = {
|
|
|
2808
4569
|
};
|
|
2809
4570
|
}
|
|
2810
4571
|
};
|
|
4572
|
+
var crushNode = {
|
|
4573
|
+
name: "crush",
|
|
4574
|
+
enabled: (_io, ctx) => ctx.config.crush?.enabled === true && ctx.config.absorb?.enabled === true,
|
|
4575
|
+
run(io, ctx) {
|
|
4576
|
+
const applied = applyCrushToMessages(
|
|
4577
|
+
io.messages,
|
|
4578
|
+
ctx.config,
|
|
4579
|
+
ctx.tokenCount,
|
|
4580
|
+
ctx.countTokens
|
|
4581
|
+
);
|
|
4582
|
+
return {
|
|
4583
|
+
...io,
|
|
4584
|
+
messages: applied.messages,
|
|
4585
|
+
effects: {
|
|
4586
|
+
...io.effects,
|
|
4587
|
+
crushCount: applied.crushedCount,
|
|
4588
|
+
crushDistilledCount: applied.distilledCount
|
|
4589
|
+
}
|
|
4590
|
+
};
|
|
4591
|
+
}
|
|
4592
|
+
};
|
|
2811
4593
|
var filterNode = {
|
|
2812
4594
|
name: "filter",
|
|
2813
4595
|
enabled: (_io, ctx) => !!ctx.config.messageFilters?.enabled && listMessageFilters().length > 0,
|
|
@@ -4744,6 +6526,294 @@ function formatRulesList(rules) {
|
|
|
4744
6526
|
return rules.map((rule, i) => `${i + 1}. [${rule.id}] ${rule.text}`).join("\n");
|
|
4745
6527
|
}
|
|
4746
6528
|
|
|
6529
|
+
// src/image-compress.ts
|
|
6530
|
+
var DEFAULT_IMAGE_COMPRESSION_CONFIG = {
|
|
6531
|
+
enabled: false,
|
|
6532
|
+
minTokens: 512,
|
|
6533
|
+
maxDimension: 1280,
|
|
6534
|
+
quality: 80,
|
|
6535
|
+
format: "webp"
|
|
6536
|
+
};
|
|
6537
|
+
function resolveImageCompressionConfig(config) {
|
|
6538
|
+
return { ...DEFAULT_IMAGE_COMPRESSION_CONFIG, ...config.imageCompression ?? {} };
|
|
6539
|
+
}
|
|
6540
|
+
var PIXEL_IMAGE_FALLBACK_TOKENS = 16384;
|
|
6541
|
+
var TILE_EDGE = 512;
|
|
6542
|
+
var TILE_SHORT_SIDE = 768;
|
|
6543
|
+
var TILE_MAX_EDGE = 2048;
|
|
6544
|
+
function byteAt(b, off) {
|
|
6545
|
+
return b[off] ?? 0;
|
|
6546
|
+
}
|
|
6547
|
+
function u16be(b, off) {
|
|
6548
|
+
return byteAt(b, off) << 8 | byteAt(b, off + 1);
|
|
6549
|
+
}
|
|
6550
|
+
function u16le(b, off) {
|
|
6551
|
+
return byteAt(b, off) | byteAt(b, off + 1) << 8;
|
|
6552
|
+
}
|
|
6553
|
+
function u32be(b, off) {
|
|
6554
|
+
return (byteAt(b, off) << 24 | byteAt(b, off + 1) << 16 | byteAt(b, off + 2) << 8 | byteAt(b, off + 3)) >>> 0;
|
|
6555
|
+
}
|
|
6556
|
+
function i32le(b, off) {
|
|
6557
|
+
return byteAt(b, off) | byteAt(b, off + 1) << 8 | byteAt(b, off + 2) << 16 | byteAt(b, off + 3) << 24;
|
|
6558
|
+
}
|
|
6559
|
+
function parseImageDimensions(bytes) {
|
|
6560
|
+
const b = bytes;
|
|
6561
|
+
if (b.length < 10) return void 0;
|
|
6562
|
+
if (b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71) {
|
|
6563
|
+
if (b.length < 24) return void 0;
|
|
6564
|
+
const w = u32be(b, 16);
|
|
6565
|
+
const h = u32be(b, 20);
|
|
6566
|
+
return w > 0 && h > 0 ? { width: w, height: h } : void 0;
|
|
6567
|
+
}
|
|
6568
|
+
if (b[0] === 71 && b[1] === 73 && b[2] === 70) {
|
|
6569
|
+
const w = u16le(b, 6);
|
|
6570
|
+
const h = u16le(b, 8);
|
|
6571
|
+
return w > 0 && h > 0 ? { width: w, height: h } : void 0;
|
|
6572
|
+
}
|
|
6573
|
+
if (b.length >= 26 && b[0] === 66 && b[1] === 77) {
|
|
6574
|
+
const w = i32le(b, 18);
|
|
6575
|
+
const h = Math.abs(i32le(b, 22));
|
|
6576
|
+
return w > 0 && h > 0 ? { width: w, height: h } : void 0;
|
|
6577
|
+
}
|
|
6578
|
+
if (b.length >= 20 && b[0] === 82 && b[1] === 73 && b[2] === 70 && b[3] === 70 && b[8] === 87 && b[9] === 69 && b[10] === 66 && b[11] === 80) {
|
|
6579
|
+
if (b[12] === 86 && b[13] === 80 && b[14] === 56 && b[15] === 88) {
|
|
6580
|
+
if (b.length < 30) return void 0;
|
|
6581
|
+
const w = byteAt(b, 24) | byteAt(b, 25) << 8 | byteAt(b, 26) << 16;
|
|
6582
|
+
const h = byteAt(b, 27) | byteAt(b, 28) << 8 | byteAt(b, 29) << 16;
|
|
6583
|
+
return w > 0 && h > 0 ? { width: w + 1, height: h + 1 } : void 0;
|
|
6584
|
+
}
|
|
6585
|
+
if (b[12] === 86 && b[13] === 80 && b[14] === 56 && b[15] === 76) {
|
|
6586
|
+
if (b.length < 25) return void 0;
|
|
6587
|
+
if (b[20] !== 47) return void 0;
|
|
6588
|
+
const w = ((byteAt(b, 22) & 63) << 8 | byteAt(b, 21)) + 1;
|
|
6589
|
+
const h = (byteAt(b, 24) & 15) << 10 | byteAt(b, 23) << 2 | (byteAt(b, 22) & 192) >> 6;
|
|
6590
|
+
return w > 0 && h > 0 ? { width: w, height: h + 1 } : void 0;
|
|
6591
|
+
}
|
|
6592
|
+
if (b[12] === 86 && b[13] === 80 && b[14] === 56 && b[15] === 32) {
|
|
6593
|
+
if (b.length < 30) return void 0;
|
|
6594
|
+
if (b[23] !== 157 || b[24] !== 1 || b[25] !== 42) return void 0;
|
|
6595
|
+
const w = u16le(b, 26) & 16383;
|
|
6596
|
+
const h = u16le(b, 28) & 16383;
|
|
6597
|
+
return w > 0 && h > 0 ? { width: w, height: h } : void 0;
|
|
6598
|
+
}
|
|
6599
|
+
return void 0;
|
|
6600
|
+
}
|
|
6601
|
+
if (b[0] === 255 && b[1] === 216) {
|
|
6602
|
+
let off = 2;
|
|
6603
|
+
while (off + 4 <= b.length) {
|
|
6604
|
+
if (b[off] !== 255) {
|
|
6605
|
+
off += 1;
|
|
6606
|
+
continue;
|
|
6607
|
+
}
|
|
6608
|
+
const marker = byteAt(b, off + 1);
|
|
6609
|
+
if (marker === 216 || marker === 1 || marker >= 208 && marker <= 217) {
|
|
6610
|
+
off += 2;
|
|
6611
|
+
continue;
|
|
6612
|
+
}
|
|
6613
|
+
if (marker === 218) break;
|
|
6614
|
+
const segLen = u16be(b, off + 2);
|
|
6615
|
+
if (segLen < 2) break;
|
|
6616
|
+
if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) {
|
|
6617
|
+
if (off + 9 > b.length) break;
|
|
6618
|
+
const h = u16be(b, off + 5);
|
|
6619
|
+
const w = u16be(b, off + 7);
|
|
6620
|
+
if (w > 0 && h > 0) return { width: w, height: h };
|
|
6621
|
+
break;
|
|
6622
|
+
}
|
|
6623
|
+
off += 2 + segLen;
|
|
6624
|
+
}
|
|
6625
|
+
}
|
|
6626
|
+
return void 0;
|
|
6627
|
+
}
|
|
6628
|
+
var HEADER_SCAN_CHARS = 64;
|
|
6629
|
+
var JPEG_SCAN_CHARS = 35e4;
|
|
6630
|
+
function parseImageDimensionsFromBase64(b64) {
|
|
6631
|
+
if (typeof b64 !== "string" || b64.length === 0) return void 0;
|
|
6632
|
+
const head = parseImageDimensions(Buffer.from(b64.slice(0, HEADER_SCAN_CHARS), "base64"));
|
|
6633
|
+
if (head) return head;
|
|
6634
|
+
if (b64.startsWith("/9j/") && b64.length > HEADER_SCAN_CHARS) {
|
|
6635
|
+
return parseImageDimensions(Buffer.from(b64.slice(0, JPEG_SCAN_CHARS), "base64"));
|
|
6636
|
+
}
|
|
6637
|
+
return void 0;
|
|
6638
|
+
}
|
|
6639
|
+
function pixelTileEstimate(width, height) {
|
|
6640
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
6641
|
+
return PIXEL_IMAGE_FALLBACK_TOKENS;
|
|
6642
|
+
}
|
|
6643
|
+
let sw = width;
|
|
6644
|
+
let sh = height;
|
|
6645
|
+
const shortSide = Math.min(sw, sh);
|
|
6646
|
+
if (shortSide > 0 && shortSide < TILE_SHORT_SIDE) {
|
|
6647
|
+
const s = TILE_SHORT_SIDE / shortSide;
|
|
6648
|
+
sw *= s;
|
|
6649
|
+
sh *= s;
|
|
6650
|
+
}
|
|
6651
|
+
const longSide = Math.max(sw, sh);
|
|
6652
|
+
if (longSide > TILE_MAX_EDGE) {
|
|
6653
|
+
const s = TILE_MAX_EDGE / longSide;
|
|
6654
|
+
sw *= s;
|
|
6655
|
+
sh *= s;
|
|
6656
|
+
}
|
|
6657
|
+
const tiles = Math.ceil(sw / TILE_EDGE) * Math.ceil(sh / TILE_EDGE);
|
|
6658
|
+
return 85 + 170 * tiles;
|
|
6659
|
+
}
|
|
6660
|
+
function estimateImageTokens(meta) {
|
|
6661
|
+
const billing = meta.billing ?? "bytes";
|
|
6662
|
+
if (billing === "bytes") return Math.ceil(meta.base64Length / 4);
|
|
6663
|
+
const w = meta.width;
|
|
6664
|
+
const h = meta.height;
|
|
6665
|
+
if (w && h) return pixelTileEstimate(w, h);
|
|
6666
|
+
if (meta.base64) {
|
|
6667
|
+
const dims = parseImageDimensionsFromBase64(meta.base64);
|
|
6668
|
+
if (dims) return pixelTileEstimate(dims.width, dims.height);
|
|
6669
|
+
}
|
|
6670
|
+
return PIXEL_IMAGE_FALLBACK_TOKENS;
|
|
6671
|
+
}
|
|
6672
|
+
function createHeuristicClassifier(options = {}) {
|
|
6673
|
+
const aspectRatioMin = options.aspectRatioMin ?? 1.4;
|
|
6674
|
+
const aspectRatioMax = options.aspectRatioMax ?? 2.6;
|
|
6675
|
+
const minShortSide = options.minShortSide ?? 720;
|
|
6676
|
+
return {
|
|
6677
|
+
name: "heuristic-v1",
|
|
6678
|
+
isScreenshotLike(meta) {
|
|
6679
|
+
const w = meta.width;
|
|
6680
|
+
const h = meta.height;
|
|
6681
|
+
if (!w || !h || w <= 0 || h <= 0) return false;
|
|
6682
|
+
const shortSide = Math.min(w, h);
|
|
6683
|
+
if (shortSide < minShortSide) return false;
|
|
6684
|
+
const ratio = Math.max(w, h) / shortSide;
|
|
6685
|
+
return ratio >= aspectRatioMin && ratio <= aspectRatioMax;
|
|
6686
|
+
}
|
|
6687
|
+
};
|
|
6688
|
+
}
|
|
6689
|
+
var DEFAULT_SCREENSHOT_CLASSIFIER = createHeuristicClassifier();
|
|
6690
|
+
function decideImageRoute(meta, config, classifier = DEFAULT_SCREENSHOT_CLASSIFIER) {
|
|
6691
|
+
const cfg = resolveImageCompressionConfig(config);
|
|
6692
|
+
const estimatedTokens = estimateImageTokens(meta);
|
|
6693
|
+
if (!cfg.enabled) return { action: "pass", reason: "disabled", estimatedTokens };
|
|
6694
|
+
if (!meta.mediaType.startsWith("image/")) {
|
|
6695
|
+
return { action: "pass", reason: "not-an-image", estimatedTokens };
|
|
6696
|
+
}
|
|
6697
|
+
if (estimatedTokens < cfg.minTokens) {
|
|
6698
|
+
return { action: "pass", reason: "below-min-tokens", estimatedTokens };
|
|
6699
|
+
}
|
|
6700
|
+
if (!classifier.isScreenshotLike(meta)) {
|
|
6701
|
+
return { action: "pass", reason: "not-screenshot-like", estimatedTokens };
|
|
6702
|
+
}
|
|
6703
|
+
return {
|
|
6704
|
+
action: "downsample",
|
|
6705
|
+
reason: "downsample",
|
|
6706
|
+
estimatedTokens,
|
|
6707
|
+
recipe: { maxDimension: cfg.maxDimension, quality: cfg.quality, format: cfg.format }
|
|
6708
|
+
};
|
|
6709
|
+
}
|
|
6710
|
+
var IMAGE_FULL_FAILURE_MARKER = "[image_full FAILED:";
|
|
6711
|
+
function buildImageFullSystemNote(count) {
|
|
6712
|
+
return `[Downscaled screenshots: ${count} image(s) were reduced before entering context. If you cannot read details (text, colors, alignment) in a reduced image, call ${IMAGE_FULL_TOOL_NAME} with that message's ref ("mNNNNN") to restore the original resolution.]`;
|
|
6713
|
+
}
|
|
6714
|
+
function parseImageFullInput(input, callId, onWarn) {
|
|
6715
|
+
let obj = null;
|
|
6716
|
+
if (typeof input === "string") {
|
|
6717
|
+
try {
|
|
6718
|
+
const parsed = JSON.parse(input);
|
|
6719
|
+
if (parsed && typeof parsed === "object") obj = parsed;
|
|
6720
|
+
} catch {
|
|
6721
|
+
obj = null;
|
|
6722
|
+
}
|
|
6723
|
+
} else if (input && typeof input === "object") {
|
|
6724
|
+
obj = input;
|
|
6725
|
+
}
|
|
6726
|
+
if (!obj) {
|
|
6727
|
+
onWarn?.(`[acp-image-full-input] rejected: not an object (${typeof input})`);
|
|
6728
|
+
return null;
|
|
6729
|
+
}
|
|
6730
|
+
let ref;
|
|
6731
|
+
for (const key of ["ref", "messageId", "of"]) {
|
|
6732
|
+
const value = obj[key];
|
|
6733
|
+
if (typeof value === "string") {
|
|
6734
|
+
ref = value;
|
|
6735
|
+
break;
|
|
6736
|
+
}
|
|
6737
|
+
}
|
|
6738
|
+
if (typeof ref !== "string") {
|
|
6739
|
+
onWarn?.(
|
|
6740
|
+
`[acp-image-full-input] rejected: need ref (string); keys: ${Object.keys(obj).join(",")}`
|
|
6741
|
+
);
|
|
6742
|
+
return null;
|
|
6743
|
+
}
|
|
6744
|
+
const trimmed = ref.trim();
|
|
6745
|
+
if (!trimmed) {
|
|
6746
|
+
onWarn?.("[acp-image-full-input] rejected: ref is empty");
|
|
6747
|
+
return null;
|
|
6748
|
+
}
|
|
6749
|
+
return { ref: trimmed, ...callId ? { callId } : {} };
|
|
6750
|
+
}
|
|
6751
|
+
function recordImageShrink(state, record) {
|
|
6752
|
+
const bytesSaved = Math.max(0, record.originalBytes - record.shrunkBytes);
|
|
6753
|
+
const tokensSaved = Math.max(0, record.tokensBefore - record.tokensAfter);
|
|
6754
|
+
return {
|
|
6755
|
+
...state,
|
|
6756
|
+
imageShrinks: [...state.imageShrinks ?? [], record],
|
|
6757
|
+
stats: {
|
|
6758
|
+
...state.stats,
|
|
6759
|
+
imagesShrunk: (state.stats.imagesShrunk ?? 0) + 1,
|
|
6760
|
+
imageBytesSaved: (state.stats.imageBytesSaved ?? 0) + bytesSaved,
|
|
6761
|
+
imageTokensSaved: (state.stats.imageTokensSaved ?? 0) + tokensSaved
|
|
6762
|
+
}
|
|
6763
|
+
};
|
|
6764
|
+
}
|
|
6765
|
+
function isImageFullRestored(state, ref) {
|
|
6766
|
+
return (state.imageFullRestored ?? []).includes(ref);
|
|
6767
|
+
}
|
|
6768
|
+
function imageShrinksForRef(state, ref) {
|
|
6769
|
+
return (state.imageShrinks ?? []).filter((r) => r.ref === ref);
|
|
6770
|
+
}
|
|
6771
|
+
function applyImageFull(input) {
|
|
6772
|
+
const cfg = resolveImageCompressionConfig(input.config);
|
|
6773
|
+
const ref = input.ref.trim();
|
|
6774
|
+
if (!cfg.enabled) {
|
|
6775
|
+
return {
|
|
6776
|
+
state: input.state,
|
|
6777
|
+
ok: false,
|
|
6778
|
+
resultText: `${IMAGE_FULL_FAILURE_MARKER} image compression is disabled in this session \u2014 no image was ever downscaled]`
|
|
6779
|
+
};
|
|
6780
|
+
}
|
|
6781
|
+
const rawId = rawForRef(input.state.messageRefs, ref);
|
|
6782
|
+
if (!rawId) {
|
|
6783
|
+
return {
|
|
6784
|
+
state: input.state,
|
|
6785
|
+
ok: false,
|
|
6786
|
+
resultText: `${IMAGE_FULL_FAILURE_MARKER} ref ${ref} does not exist in this session]`
|
|
6787
|
+
};
|
|
6788
|
+
}
|
|
6789
|
+
if (isImageFullRestored(input.state, ref)) {
|
|
6790
|
+
return {
|
|
6791
|
+
state: input.state,
|
|
6792
|
+
ok: true,
|
|
6793
|
+
resultText: `already restored (${ref}) \u2014 full resolution stays in effect for the rest of this session.`
|
|
6794
|
+
};
|
|
6795
|
+
}
|
|
6796
|
+
const shrinks = imageShrinksForRef(input.state, ref);
|
|
6797
|
+
if (shrinks.length === 0) {
|
|
6798
|
+
return {
|
|
6799
|
+
state: input.state,
|
|
6800
|
+
ok: false,
|
|
6801
|
+
resultText: `${IMAGE_FULL_FAILURE_MARKER} no downscaled image is recorded for ${ref} \u2014 it was passed through untouched or never carried an image]`
|
|
6802
|
+
};
|
|
6803
|
+
}
|
|
6804
|
+
return {
|
|
6805
|
+
state: {
|
|
6806
|
+
...input.state,
|
|
6807
|
+
imageFullRestored: [...input.state.imageFullRestored ?? [], ref]
|
|
6808
|
+
},
|
|
6809
|
+
ok: true,
|
|
6810
|
+
resultText: `restored original-resolution image(s) for ${ref} (${shrinks.length} image${shrinks.length === 1 ? "" : "s"}); full resolution applies for the rest of this session.`
|
|
6811
|
+
};
|
|
6812
|
+
}
|
|
6813
|
+
function resetImageFullState(state) {
|
|
6814
|
+
return { ...state, imageFullRestored: [], imageShrinks: [] };
|
|
6815
|
+
}
|
|
6816
|
+
|
|
4747
6817
|
// src/parse-compress-input.ts
|
|
4748
6818
|
function parseCompressArgs(input, opts) {
|
|
4749
6819
|
const callId = opts?.callId;
|
|
@@ -5272,7 +7342,8 @@ function rebuildCompressionState(state, messages, config, ports = {}) {
|
|
|
5272
7342
|
const core = createCore({ countTokens: ports.countTokens ?? defaultCountTokens });
|
|
5273
7343
|
const refResult = assignRefs(messages, {
|
|
5274
7344
|
existing: state.messageRefs,
|
|
5275
|
-
nextIndex: highestUsedIndex(state.messageRefs) + 1
|
|
7345
|
+
nextIndex: highestUsedIndex(state.messageRefs) + 1,
|
|
7346
|
+
shouldSkip: (m) => m.id.startsWith(RETRIEVED_ID_PREFIX)
|
|
5276
7347
|
});
|
|
5277
7348
|
let working = { ...state, messageRefs: refResult.map };
|
|
5278
7349
|
const invocations = collectCompressInvocations(messages);
|
|
@@ -5636,6 +7707,129 @@ function makePreview(text, query, len) {
|
|
|
5636
7707
|
const suffix = end < text.length ? "\u2026" : "";
|
|
5637
7708
|
return prefix + clampWindow(text, start, end).trim() + suffix;
|
|
5638
7709
|
}
|
|
7710
|
+
|
|
7711
|
+
// src/output-steering.ts
|
|
7712
|
+
function classifyTurn(messages) {
|
|
7713
|
+
if (messages.length === 0) return "unknown";
|
|
7714
|
+
const last = messages[messages.length - 1];
|
|
7715
|
+
if (!last || last.role !== "user") return "unknown";
|
|
7716
|
+
if (typeof last.text === "string")
|
|
7717
|
+
return last.text.trim() ? "new_user_ask" : "unknown";
|
|
7718
|
+
const blocks = last.blocks;
|
|
7719
|
+
if (!Array.isArray(blocks) || blocks.length === 0) return "unknown";
|
|
7720
|
+
let sawToolResult = false;
|
|
7721
|
+
let sawError = false;
|
|
7722
|
+
for (const b of blocks) {
|
|
7723
|
+
if (!b) return "unknown";
|
|
7724
|
+
switch (b.kind) {
|
|
7725
|
+
case "tool_result":
|
|
7726
|
+
sawToolResult = true;
|
|
7727
|
+
if (b.isError === true) sawError = true;
|
|
7728
|
+
break;
|
|
7729
|
+
case "text":
|
|
7730
|
+
case "image":
|
|
7731
|
+
case "document":
|
|
7732
|
+
return "new_user_ask";
|
|
7733
|
+
default:
|
|
7734
|
+
return "unknown";
|
|
7735
|
+
}
|
|
7736
|
+
}
|
|
7737
|
+
if (sawError) return "error_continuation";
|
|
7738
|
+
if (sawToolResult) return "mechanical_continuation";
|
|
7739
|
+
return "unknown";
|
|
7740
|
+
}
|
|
7741
|
+
var MIN_VERBOSITY_LEVEL = 0;
|
|
7742
|
+
var MAX_VERBOSITY_LEVEL = 4;
|
|
7743
|
+
var DEFAULT_VERBOSITY_LEVEL = 2;
|
|
7744
|
+
var VERBOSITY_LEVELS = {
|
|
7745
|
+
1: "Skip preamble and postamble. Do not announce what you are about to do or recap what you just did; start with the substance.",
|
|
7746
|
+
2: "Skip preamble and postamble; start with the substance. Never restate code, file contents, diffs, or tool output that already appear in this conversation \u2014 reference them by path and line instead. After a tool call succeeds, continue without narrating the result.",
|
|
7747
|
+
3: "Skip preamble and postamble. Never restate code, file contents, diffs, or tool output already in this conversation \u2014 cite the exact file path and line or symbol instead, always; a reference that omits the location is not a reference. Give conclusions only; omit rationale unless the user asks why. Prefer the smallest edit over rewriting whole files. Keep prose to the minimum needed to be unambiguous. Never drop anything the turn or task needs to be correct, including negations (not, never, no, only, except) \u2014 shorten how you say it, not what you say. Use full prose for destructive or irreversible actions, security warnings, and any multi-step sequence where brevity would create ambiguity.",
|
|
7748
|
+
4: "Minimum tokens. Fragments fine. No preamble, no postamble, no restating context, no rationale. Answer, smallest-possible edits, nothing else. Never drop anything the turn or task needs to be correct, including negations (not, never, no, only, except). Use full prose for destructive or irreversible actions, security warnings, and any multi-step sequence where brevity would create ambiguity."
|
|
7749
|
+
};
|
|
7750
|
+
function verbosityDirective(level) {
|
|
7751
|
+
if (!Number.isInteger(level)) return null;
|
|
7752
|
+
return VERBOSITY_LEVELS[level] ?? null;
|
|
7753
|
+
}
|
|
7754
|
+
var DEFAULT_STEERING_SENTINEL = "<acp_output_steering>";
|
|
7755
|
+
function renderSteeringBlock(level, sentinel = DEFAULT_STEERING_SENTINEL) {
|
|
7756
|
+
const text = verbosityDirective(level);
|
|
7757
|
+
if (text === null) return null;
|
|
7758
|
+
return `${sentinel}
|
|
7759
|
+
${text}
|
|
7760
|
+
</${sentinel.slice(1)}`;
|
|
7761
|
+
}
|
|
7762
|
+
function applySteeringToPrompt(existing, block, sentinel = DEFAULT_STEERING_SENTINEL) {
|
|
7763
|
+
const suffix = `</${sentinel.slice(1)}`;
|
|
7764
|
+
const start = existing.indexOf(sentinel);
|
|
7765
|
+
if (start >= 0) {
|
|
7766
|
+
const found = existing.indexOf(suffix, start);
|
|
7767
|
+
const end = found < 0 ? existing.length : found + suffix.length;
|
|
7768
|
+
const prefix = existing.slice(0, start).replace(/\s+$/, "");
|
|
7769
|
+
const tail = existing.slice(end).replace(/^\n+/, "");
|
|
7770
|
+
const parts = [prefix, block, tail].filter((p) => p.length > 0);
|
|
7771
|
+
const updated2 = parts.join("\n\n");
|
|
7772
|
+
return { updated: updated2, changed: updated2 !== existing };
|
|
7773
|
+
}
|
|
7774
|
+
const trimmed = existing.trim();
|
|
7775
|
+
const updated = trimmed.length > 0 ? `${existing.replace(/\s+$/, "")}
|
|
7776
|
+
|
|
7777
|
+
${block}` : block;
|
|
7778
|
+
return { updated, changed: updated !== existing };
|
|
7779
|
+
}
|
|
7780
|
+
var EFFORT_LADDER = [
|
|
7781
|
+
"minimal",
|
|
7782
|
+
"low",
|
|
7783
|
+
"medium",
|
|
7784
|
+
"high",
|
|
7785
|
+
"xhigh"
|
|
7786
|
+
];
|
|
7787
|
+
function clampEffortToFloor(current, floor = "low") {
|
|
7788
|
+
if (typeof current !== "string") return null;
|
|
7789
|
+
const ci = EFFORT_LADDER.indexOf(current);
|
|
7790
|
+
if (ci === -1) return null;
|
|
7791
|
+
const fi = EFFORT_LADDER.indexOf(floor);
|
|
7792
|
+
return ci > fi ? floor : null;
|
|
7793
|
+
}
|
|
7794
|
+
var DEFAULT_OUTPUT_STEERING_CONFIG = {
|
|
7795
|
+
enabled: false,
|
|
7796
|
+
verbosityLevel: DEFAULT_VERBOSITY_LEVEL,
|
|
7797
|
+
effortRouting: true
|
|
7798
|
+
};
|
|
7799
|
+
function resolveVerbosityLevel(v) {
|
|
7800
|
+
if (typeof v === "number" && Number.isInteger(v) && v >= MIN_VERBOSITY_LEVEL && v <= MAX_VERBOSITY_LEVEL) {
|
|
7801
|
+
return { level: v };
|
|
7802
|
+
}
|
|
7803
|
+
const level = DEFAULT_OUTPUT_STEERING_CONFIG.verbosityLevel;
|
|
7804
|
+
if (v === void 0) return { level };
|
|
7805
|
+
return {
|
|
7806
|
+
level,
|
|
7807
|
+
warning: `[config] outputSteering.verbosityLevel must be an integer ${MIN_VERBOSITY_LEVEL}-${MAX_VERBOSITY_LEVEL}; got ${JSON.stringify(v)} \u2014 falling back to ${level}`
|
|
7808
|
+
};
|
|
7809
|
+
}
|
|
7810
|
+
function resolveOutputSteeringConfig(v) {
|
|
7811
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) {
|
|
7812
|
+
return { config: DEFAULT_OUTPUT_STEERING_CONFIG, warnings: [] };
|
|
7813
|
+
}
|
|
7814
|
+
const obj = v;
|
|
7815
|
+
const lvl = resolveVerbosityLevel(obj.verbosityLevel);
|
|
7816
|
+
return {
|
|
7817
|
+
config: {
|
|
7818
|
+
enabled: obj.enabled === true,
|
|
7819
|
+
verbosityLevel: lvl.level,
|
|
7820
|
+
effortRouting: obj.effortRouting !== false
|
|
7821
|
+
},
|
|
7822
|
+
warnings: lvl.warning ? [lvl.warning] : []
|
|
7823
|
+
};
|
|
7824
|
+
}
|
|
7825
|
+
function decideOutputSteering(messages, config = DEFAULT_OUTPUT_STEERING_CONFIG) {
|
|
7826
|
+
const turnKind = classifyTurn(messages);
|
|
7827
|
+
return {
|
|
7828
|
+
turnKind,
|
|
7829
|
+
verbosityLevel: config.enabled ? config.verbosityLevel : 0,
|
|
7830
|
+
lowerEffort: config.enabled && config.effortRouting && turnKind === "mechanical_continuation"
|
|
7831
|
+
};
|
|
7832
|
+
}
|
|
5639
7833
|
export {
|
|
5640
7834
|
ABSORB_PROMPT_MARKER,
|
|
5641
7835
|
ABSORB_TOOL,
|
|
@@ -5684,10 +7878,33 @@ export {
|
|
|
5684
7878
|
DECOMPRESS_TOOL_RESPONSES,
|
|
5685
7879
|
DEFAULT_ABSORB_CONFIG,
|
|
5686
7880
|
DEFAULT_ALGORITHM,
|
|
7881
|
+
DEFAULT_CCR_CONFIG,
|
|
7882
|
+
DEFAULT_CRUSH_CONFIG,
|
|
7883
|
+
DEFAULT_IMAGE_COMPRESSION_CONFIG,
|
|
7884
|
+
DEFAULT_OUTPUT_STEERING_CONFIG,
|
|
5687
7885
|
DEFAULT_ROLE_WEIGHTS,
|
|
5688
7886
|
DEFAULT_RULE_LIMITS,
|
|
7887
|
+
DEFAULT_SCREENSHOT_CLASSIFIER,
|
|
7888
|
+
DEFAULT_STEERING_SENTINEL,
|
|
7889
|
+
DEFAULT_VERBOSITY_LEVEL,
|
|
7890
|
+
EFFORT_LADDER,
|
|
5689
7891
|
HOW_TO_COMPRESS_RULES,
|
|
7892
|
+
IMAGE_FULL_FAILURE_MARKER,
|
|
7893
|
+
IMAGE_FULL_TOOL,
|
|
7894
|
+
IMAGE_FULL_TOOL_DESCRIPTION,
|
|
7895
|
+
IMAGE_FULL_TOOL_NAME,
|
|
7896
|
+
IMAGE_FULL_TOOL_OPENAI,
|
|
7897
|
+
IMAGE_FULL_TOOL_RESPONSES,
|
|
5690
7898
|
LEAN_HOW_TO_COMPRESS,
|
|
7899
|
+
MAX_VERBOSITY_LEVEL,
|
|
7900
|
+
MIN_VERBOSITY_LEVEL,
|
|
7901
|
+
PIXEL_IMAGE_FALLBACK_TOKENS,
|
|
7902
|
+
RETRIEVED_ID_PREFIX,
|
|
7903
|
+
RETRIEVE_TOOL,
|
|
7904
|
+
RETRIEVE_TOOL_DESCRIPTION,
|
|
7905
|
+
RETRIEVE_TOOL_NAME,
|
|
7906
|
+
RETRIEVE_TOOL_OPENAI,
|
|
7907
|
+
RETRIEVE_TOOL_RESPONSES,
|
|
5691
7908
|
RULES_USAGE_PROMPT,
|
|
5692
7909
|
RULE_TOOL_NAME,
|
|
5693
7910
|
SEARCH_CONTEXT_TOOL,
|
|
@@ -5695,9 +7912,11 @@ export {
|
|
|
5695
7912
|
SEARCH_CONTEXT_TOOL_NAME,
|
|
5696
7913
|
SEARCH_CONTEXT_TOOL_OPENAI,
|
|
5697
7914
|
SEARCH_CONTEXT_TOOL_RESPONSES,
|
|
7915
|
+
STORED_PLACEHOLDER_MARKER,
|
|
5698
7916
|
SUMMARY_HEADER,
|
|
5699
7917
|
TIER2_DISTILL_RULES,
|
|
5700
7918
|
TIER3_CONDENSE_RULES,
|
|
7919
|
+
VERBOSITY_LEVELS,
|
|
5701
7920
|
VIABLE_RANGE_MIN_TOKENS,
|
|
5702
7921
|
activeBlockSpans,
|
|
5703
7922
|
activeBlocks,
|
|
@@ -5709,8 +7928,12 @@ export {
|
|
|
5709
7928
|
appendAbsorbPrompts,
|
|
5710
7929
|
applyAbsorb,
|
|
5711
7930
|
applyAcpToolOverrides,
|
|
7931
|
+
applyCrushToMessages,
|
|
7932
|
+
applyImageFull,
|
|
5712
7933
|
applyMessageFilters,
|
|
7934
|
+
applyRetrieve,
|
|
5713
7935
|
applySectionOverrides,
|
|
7936
|
+
applySteeringToPrompt,
|
|
5714
7937
|
assignRefs,
|
|
5715
7938
|
blockById,
|
|
5716
7939
|
blockDocs,
|
|
@@ -5721,26 +7944,41 @@ export {
|
|
|
5721
7944
|
buildCompressHybridSystemPrompt,
|
|
5722
7945
|
buildCompressSystemPrompt,
|
|
5723
7946
|
buildCompressTextSystemPrompt,
|
|
7947
|
+
buildImageFullSystemNote,
|
|
5724
7948
|
buildRecap,
|
|
5725
7949
|
buildRestoredContentPreview,
|
|
7950
|
+
buildRetrievalInjection,
|
|
5726
7951
|
buildStatusReport,
|
|
7952
|
+
buildStoredPlaceholder,
|
|
5727
7953
|
builtinSource,
|
|
7954
|
+
ccrStoreNode,
|
|
7955
|
+
clampEffortToFloor,
|
|
7956
|
+
classifyCrushText,
|
|
7957
|
+
classifyKind,
|
|
7958
|
+
classifyTurn,
|
|
5728
7959
|
clearDocFeatures,
|
|
5729
7960
|
clearMessageFilters,
|
|
5730
7961
|
clearRules,
|
|
5731
7962
|
cloneWithDescriptions,
|
|
7963
|
+
codeTrimPlugin,
|
|
5732
7964
|
collectBlockContent,
|
|
5733
7965
|
collectLatestProtected,
|
|
5734
7966
|
computeFoldEconomics,
|
|
7967
|
+
contentStoreStats,
|
|
5735
7968
|
countMessageTokens,
|
|
5736
7969
|
coveredMessageIds,
|
|
5737
7970
|
createBpeTokenizer,
|
|
7971
|
+
createContentStore,
|
|
5738
7972
|
createCore,
|
|
5739
7973
|
createDirPackSource,
|
|
7974
|
+
createHeuristicClassifier,
|
|
5740
7975
|
createInitialState,
|
|
5741
7976
|
createPackResolver,
|
|
5742
7977
|
createRenderRefsNode,
|
|
7978
|
+
crushText,
|
|
5743
7979
|
deactivateBlock,
|
|
7980
|
+
decideImageRoute,
|
|
7981
|
+
decideOutputSteering,
|
|
5744
7982
|
decomposeSample,
|
|
5745
7983
|
defaultConfig,
|
|
5746
7984
|
defaultCountTokens,
|
|
@@ -5750,7 +7988,10 @@ export {
|
|
|
5750
7988
|
docCacheInfo,
|
|
5751
7989
|
docFeatures,
|
|
5752
7990
|
emptyRefMap,
|
|
7991
|
+
estimateImageTokens,
|
|
5753
7992
|
estimateTokensFast,
|
|
7993
|
+
evaluateToolResult,
|
|
7994
|
+
extractCommand,
|
|
5754
7995
|
findActiveAncestor,
|
|
5755
7996
|
findBlocksOverlappingMessages,
|
|
5756
7997
|
formatCacheReport,
|
|
@@ -5760,35 +8001,52 @@ export {
|
|
|
5760
8001
|
formatRulesList,
|
|
5761
8002
|
getMessageFilter,
|
|
5762
8003
|
getSearchAlgorithm,
|
|
8004
|
+
hasStoredRef,
|
|
8005
|
+
hashContent,
|
|
5763
8006
|
hideAbsorbedMessages,
|
|
5764
8007
|
hideConsumedCompressCalls,
|
|
5765
8008
|
highestActiveTier,
|
|
5766
8009
|
highestUsedIndex,
|
|
8010
|
+
imageShrinksForRef,
|
|
5767
8011
|
indexToRef,
|
|
5768
8012
|
isAbsorbCandidate,
|
|
8013
|
+
isImageFullRestored,
|
|
5769
8014
|
isMessageLatestProtected,
|
|
5770
8015
|
isMessageProtected,
|
|
5771
8016
|
isRenderedSummaryMessage,
|
|
8017
|
+
isRetrievedMessage,
|
|
8018
|
+
isStoredPlaceholderText,
|
|
5772
8019
|
isSummaryMessageId,
|
|
5773
8020
|
isValidPackName,
|
|
8021
|
+
jsonFoldPlugin,
|
|
5774
8022
|
leanPack,
|
|
8023
|
+
listCrushPlugins,
|
|
5775
8024
|
listMessageFilters,
|
|
5776
8025
|
listRules,
|
|
5777
8026
|
listSearchAlgorithms,
|
|
8027
|
+
logSelectPlugin,
|
|
5778
8028
|
makeIO,
|
|
5779
8029
|
matchSession,
|
|
5780
8030
|
matchToolPattern,
|
|
5781
8031
|
messageDocs,
|
|
8032
|
+
normalizeHead,
|
|
8033
|
+
noteRetrieval,
|
|
5782
8034
|
parseAbsorbInput,
|
|
5783
8035
|
parseBlockIdArg,
|
|
5784
8036
|
parseBoundary,
|
|
5785
8037
|
parseCompressArgs,
|
|
5786
8038
|
parseCompressInput,
|
|
8039
|
+
parseImageDimensions,
|
|
8040
|
+
parseImageDimensionsFromBase64,
|
|
8041
|
+
parseImageFullInput,
|
|
8042
|
+
pixelTileEstimate,
|
|
5787
8043
|
prune,
|
|
5788
8044
|
rawForRef,
|
|
5789
8045
|
rebuildCompressionState,
|
|
8046
|
+
recordImageShrink,
|
|
5790
8047
|
refForRaw,
|
|
5791
8048
|
refToIndex,
|
|
8049
|
+
registerCrushPlugin,
|
|
5792
8050
|
registerMessageFilter,
|
|
5793
8051
|
registerSearchAlgorithm,
|
|
5794
8052
|
removeRule,
|
|
@@ -5796,23 +8054,38 @@ export {
|
|
|
5796
8054
|
renderMessage2 as renderMessage,
|
|
5797
8055
|
renderNudgeText,
|
|
5798
8056
|
renderRefsNode,
|
|
8057
|
+
renderSteeringBlock,
|
|
5799
8058
|
renderVisibleRefs,
|
|
8059
|
+
resetCrushPlugins,
|
|
8060
|
+
resetImageFullState,
|
|
5800
8061
|
resolveAbsorbConfig,
|
|
5801
8062
|
resolveBlockSpan,
|
|
5802
8063
|
resolveBoundaries,
|
|
8064
|
+
resolveCcrConfig,
|
|
8065
|
+
resolveCrushConfig,
|
|
8066
|
+
resolveImageCompressionConfig,
|
|
8067
|
+
resolveOutputSteeringConfig,
|
|
5803
8068
|
resolvePrompts,
|
|
5804
8069
|
resolveRuleLimits,
|
|
5805
8070
|
resolveTransformChannel,
|
|
8071
|
+
resolveVerbosityLevel,
|
|
8072
|
+
retrieveByRef,
|
|
8073
|
+
retrievedMessageId,
|
|
5806
8074
|
runPipeline,
|
|
5807
8075
|
sanitizePackSurface,
|
|
5808
8076
|
searchBlocks,
|
|
5809
8077
|
searchBlocksAsync,
|
|
5810
8078
|
setDocCacheCap,
|
|
8079
|
+
storeCoveredOriginals,
|
|
8080
|
+
storeLargeResults,
|
|
8081
|
+
storeOriginal,
|
|
5811
8082
|
summarizeFoldEconomics,
|
|
5812
8083
|
summaryMessageId,
|
|
5813
8084
|
syncBlocks,
|
|
5814
8085
|
truncateLargeToolOutputs,
|
|
8086
|
+
unregisterCrushPlugin,
|
|
5815
8087
|
validateConfig,
|
|
8088
|
+
verbosityDirective,
|
|
5816
8089
|
viableRanges,
|
|
5817
8090
|
visibleBlockAnchor
|
|
5818
8091
|
};
|