acp-kernel 0.0.83 → 0.0.84-pr.365.231
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 +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2948 -640
- 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/protected.d.ts +1 -0
- package/dist/protected.d.ts.map +1 -1
- package/dist/rebuild.d.ts.map +1 -1
- package/dist/recommend.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/dist/wire/index.js +19 -2
- package/dist/wire/index.js.map +1 -1
- package/dist/wire/openai.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"];
|
|
@@ -1551,224 +1250,2289 @@ function isMessageLatestProtected(msg, latest) {
|
|
|
1551
1250
|
}
|
|
1552
1251
|
return false;
|
|
1553
1252
|
}
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1253
|
+
function hasMediaPayload(msg) {
|
|
1254
|
+
const m = msg;
|
|
1255
|
+
if (typeof m.imageBase64 === "string" && m.imageBase64.length > 0) return true;
|
|
1256
|
+
if (m.rawOpenaiContent != null) return true;
|
|
1257
|
+
if (Array.isArray(m.rawOpenaiContentParts) && m.rawOpenaiContentParts.length > 0)
|
|
1258
|
+
return true;
|
|
1259
|
+
if (isObjWith(m.rawAnthropicBlock, "type", "image")) return true;
|
|
1260
|
+
const item = m.rawResponsesItem;
|
|
1261
|
+
if (isObjWith(item, "type", "input_image")) return true;
|
|
1262
|
+
if (item && typeof item === "object") {
|
|
1263
|
+
const content = item.content;
|
|
1264
|
+
if (Array.isArray(content)) {
|
|
1265
|
+
return content.some((p) => isObjWith(p, "type", "input_image"));
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
return false;
|
|
1566
1269
|
}
|
|
1567
|
-
function
|
|
1568
|
-
|
|
1569
|
-
if (tokens < 1e4) return (tokens / 1e3).toFixed(1) + "K";
|
|
1570
|
-
return Math.round(tokens / 1e3) + "K";
|
|
1270
|
+
function isObjWith(v, key, value) {
|
|
1271
|
+
return typeof v === "object" && v !== null && v[key] === value;
|
|
1571
1272
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1273
|
+
|
|
1274
|
+
// src/content-store.ts
|
|
1275
|
+
import { createHash } from "crypto";
|
|
1276
|
+
function createContentStore() {
|
|
1277
|
+
return { version: 1, byHash: {}, byRef: {} };
|
|
1278
|
+
}
|
|
1279
|
+
function hashContent(text) {
|
|
1280
|
+
return createHash("sha256").update(text, "utf8").digest("hex");
|
|
1281
|
+
}
|
|
1282
|
+
function storeOriginal(store, spec) {
|
|
1283
|
+
if (!spec.ref || store.byRef[spec.ref]) return store;
|
|
1284
|
+
const hash = hashContent(spec.text);
|
|
1285
|
+
const entry = {
|
|
1286
|
+
hash,
|
|
1287
|
+
rawId: spec.rawId,
|
|
1288
|
+
kind: spec.kind,
|
|
1289
|
+
tokens: spec.tokens,
|
|
1290
|
+
chars: spec.text.length,
|
|
1291
|
+
head: spec.head
|
|
1292
|
+
};
|
|
1293
|
+
if (spec.toolName !== void 0) entry.toolName = spec.toolName;
|
|
1294
|
+
const byHash = store.byHash[hash] === void 0 ? { ...store.byHash, [hash]: spec.text } : store.byHash;
|
|
1295
|
+
return { ...store, byHash, byRef: { ...store.byRef, [spec.ref]: entry } };
|
|
1296
|
+
}
|
|
1297
|
+
function retrieveByRef(store, ref) {
|
|
1298
|
+
const entry = store.byRef[ref];
|
|
1299
|
+
if (!entry) return { ok: false, reason: "not-found" };
|
|
1300
|
+
const text = store.byHash[entry.hash];
|
|
1301
|
+
if (text === void 0) return { ok: false, reason: "not-found" };
|
|
1302
|
+
return { ok: true, text, entry };
|
|
1303
|
+
}
|
|
1304
|
+
function hasStoredRef(store, ref) {
|
|
1305
|
+
return store.byRef[ref] !== void 0;
|
|
1306
|
+
}
|
|
1307
|
+
function contentStoreStats(store) {
|
|
1308
|
+
let totalChars = 0;
|
|
1309
|
+
for (const text of Object.values(store.byHash)) totalChars += text.length;
|
|
1310
|
+
return {
|
|
1311
|
+
entries: Object.keys(store.byRef).length,
|
|
1312
|
+
uniqueContents: Object.keys(store.byHash).length,
|
|
1313
|
+
totalChars
|
|
1314
|
+
};
|
|
1574
1315
|
}
|
|
1575
|
-
function buildAbsorbSystemPrompt(toolName = ABSORB_TOOL_NAME) {
|
|
1576
|
-
return `INSTANT TOOL-RESULT ABSORPTION (${toolName})
|
|
1577
1316
|
|
|
1578
|
-
|
|
1317
|
+
// src/ccr.ts
|
|
1318
|
+
var RETRIEVE_TOOL_NAME2 = "acp_retrieve";
|
|
1319
|
+
var DEFAULT_CCR_CONFIG = {
|
|
1320
|
+
enabled: false,
|
|
1321
|
+
toolName: RETRIEVE_TOOL_NAME2,
|
|
1322
|
+
minToolTokens: 4e3,
|
|
1323
|
+
excludeTools: [],
|
|
1324
|
+
maxHeadChars: 96
|
|
1325
|
+
};
|
|
1326
|
+
function resolveCcrConfig(config) {
|
|
1327
|
+
return { ...DEFAULT_CCR_CONFIG, ...config.ccr };
|
|
1328
|
+
}
|
|
1329
|
+
var STORED_PLACEHOLDER_MARKER = "[acp-stored";
|
|
1330
|
+
var RETRIEVED_ID_PREFIX = "acp_retrieved_";
|
|
1331
|
+
var KIND_LABELS = {
|
|
1332
|
+
bash: "shell output",
|
|
1333
|
+
shell: "shell output",
|
|
1334
|
+
exec: "shell output",
|
|
1335
|
+
execute_command: "shell output",
|
|
1336
|
+
run: "shell output",
|
|
1337
|
+
terminal: "shell output",
|
|
1338
|
+
read: "file read",
|
|
1339
|
+
read_file: "file read",
|
|
1340
|
+
cat: "file read",
|
|
1341
|
+
open: "file read",
|
|
1342
|
+
grep: "search output",
|
|
1343
|
+
rg: "search output",
|
|
1344
|
+
search: "search output",
|
|
1345
|
+
glob: "search output",
|
|
1346
|
+
find: "search output",
|
|
1347
|
+
webfetch: "web fetch",
|
|
1348
|
+
web_fetch: "web fetch",
|
|
1349
|
+
fetch: "web fetch",
|
|
1350
|
+
curl: "web fetch"
|
|
1351
|
+
};
|
|
1352
|
+
function classifyKind(toolName) {
|
|
1353
|
+
if (!toolName) return "tool result";
|
|
1354
|
+
return KIND_LABELS[toolName.toLowerCase()] ?? "tool result";
|
|
1355
|
+
}
|
|
1356
|
+
function groupThousands(value) {
|
|
1357
|
+
return String(Math.max(0, Math.round(value))).replace(
|
|
1358
|
+
/\B(?=(\d{3})+(?!\d))/g,
|
|
1359
|
+
","
|
|
1360
|
+
);
|
|
1579
1361
|
}
|
|
1580
|
-
function
|
|
1581
|
-
|
|
1582
|
-
if (
|
|
1583
|
-
return
|
|
1362
|
+
function normalizeHead(text, maxChars) {
|
|
1363
|
+
const singleLine = (text || "").replace(/\s+/g, " ").trim();
|
|
1364
|
+
if (singleLine.length <= maxChars) return singleLine;
|
|
1365
|
+
return singleLine.slice(0, maxChars) + "\u2026";
|
|
1584
1366
|
}
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1367
|
+
var COMMAND_FIELDS = ["command", "cmd", "script", "query", "path", "url"];
|
|
1368
|
+
function extractCommand(toolCallText, maxChars) {
|
|
1369
|
+
if (!toolCallText) return void 0;
|
|
1370
|
+
let parsed;
|
|
1371
|
+
try {
|
|
1372
|
+
parsed = JSON.parse(toolCallText);
|
|
1373
|
+
} catch {
|
|
1374
|
+
return void 0;
|
|
1593
1375
|
}
|
|
1594
|
-
return
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
if (record.resultMessageId) hidden.add(record.resultMessageId);
|
|
1376
|
+
if (typeof parsed !== "object" || parsed === null) return void 0;
|
|
1377
|
+
const record = parsed;
|
|
1378
|
+
for (const field of COMMAND_FIELDS) {
|
|
1379
|
+
const value = record[field];
|
|
1380
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
1381
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
1382
|
+
return normalized.length <= maxChars ? normalized : normalized.slice(0, maxChars) + "\u2026";
|
|
1383
|
+
}
|
|
1603
1384
|
}
|
|
1604
|
-
return
|
|
1385
|
+
return void 0;
|
|
1605
1386
|
}
|
|
1606
|
-
function
|
|
1607
|
-
const
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1387
|
+
function buildStoredPlaceholder(input) {
|
|
1388
|
+
const title = input.command ?? input.head;
|
|
1389
|
+
const titlePart = title ? ` \`${title}\`` : "";
|
|
1390
|
+
return `\u{1F4E6} ${STORED_PLACEHOLDER_MARKER} #${input.ref} \xB7 ${input.kind} \xB7 ${groupThousands(input.tokens)} tok]${titlePart}
|
|
1391
|
+
\u2192 ${input.retrieveToolName}("${input.ref}") returns the full text`;
|
|
1392
|
+
}
|
|
1393
|
+
var LEADING_TAG_RE = new RegExp("^\\x3cacp [^>]*>[^\\x3c]*\\x3c\\/acp>\\n?");
|
|
1394
|
+
function stripLeadingTag(text) {
|
|
1395
|
+
return text.replace(LEADING_TAG_RE, "");
|
|
1396
|
+
}
|
|
1397
|
+
function isStoredPlaceholderText(text) {
|
|
1398
|
+
return stripLeadingTag(text).includes(STORED_PLACEHOLDER_MARKER);
|
|
1399
|
+
}
|
|
1400
|
+
function retrievedMessageId(ref) {
|
|
1401
|
+
return RETRIEVED_ID_PREFIX + ref;
|
|
1402
|
+
}
|
|
1403
|
+
function isRetrievedMessage(message) {
|
|
1404
|
+
return message.id.startsWith(RETRIEVED_ID_PREFIX) && message.role === "system" && message.contentType === "text";
|
|
1405
|
+
}
|
|
1406
|
+
function buildRetrievalInjection(ref, entry, text) {
|
|
1407
|
+
const header = `[acp-retrieved #${ref} \xB7 ${entry.kind} \xB7 ${groupThousands(entry.tokens)} tok]`;
|
|
1408
|
+
return {
|
|
1409
|
+
ackText: `retrieved ${ref}: ${groupThousands(entry.tokens)} tok (${entry.chars} chars)`,
|
|
1410
|
+
injection: {
|
|
1411
|
+
id: retrievedMessageId(ref),
|
|
1412
|
+
role: "system",
|
|
1413
|
+
contentType: "text",
|
|
1414
|
+
text: `${header}
|
|
1415
|
+
${text}`
|
|
1416
|
+
}
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
function applyRetrieve(input) {
|
|
1420
|
+
const ref = input.ref.trim();
|
|
1421
|
+
const found = retrieveByRef(input.store, ref);
|
|
1422
|
+
if (!found.ok) {
|
|
1423
|
+
return {
|
|
1424
|
+
ok: false,
|
|
1425
|
+
reason: "not-found",
|
|
1426
|
+
ackText: `retrieve ${ref}: not found \u2014 no stored original for this ref`
|
|
1427
|
+
};
|
|
1612
1428
|
}
|
|
1613
|
-
const
|
|
1614
|
-
|
|
1615
|
-
|
|
1429
|
+
const built = buildRetrievalInjection(ref, found.entry, found.text);
|
|
1430
|
+
return {
|
|
1431
|
+
ok: true,
|
|
1432
|
+
text: found.text,
|
|
1433
|
+
ackText: built.ackText,
|
|
1434
|
+
injection: built.injection,
|
|
1435
|
+
entry: found.entry
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
function storeLargeResults(input) {
|
|
1439
|
+
const cfg = resolveCcrConfig(input.config);
|
|
1440
|
+
if (!cfg.enabled)
|
|
1441
|
+
return { messages: input.messages, store: input.store, storedCount: 0 };
|
|
1442
|
+
let current = input.store;
|
|
1443
|
+
let storedCount = 0;
|
|
1444
|
+
const callById = /* @__PURE__ */ new Map();
|
|
1445
|
+
for (const message of input.messages) {
|
|
1446
|
+
if (message.contentType === "tool-call" && message.toolCallId) {
|
|
1447
|
+
callById.set(message.toolCallId, message);
|
|
1448
|
+
}
|
|
1616
1449
|
}
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
if (
|
|
1621
|
-
if (
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
if (
|
|
1628
|
-
|
|
1450
|
+
const updated = input.messages.map((message) => {
|
|
1451
|
+
if (message.contentType !== "tool-result") return message;
|
|
1452
|
+
const text = message.text ?? "";
|
|
1453
|
+
if (text.length === 0) return message;
|
|
1454
|
+
if (isStoredPlaceholderText(text)) return message;
|
|
1455
|
+
if (!message.toolCallId) return message;
|
|
1456
|
+
const toolName = message.toolName;
|
|
1457
|
+
if (toolName && (ACP_TOOL_NAMES.has(toolName) || toolName === cfg.toolName)) {
|
|
1458
|
+
return message;
|
|
1459
|
+
}
|
|
1460
|
+
if (toolName && cfg.excludeTools.some((pattern) => matchToolPattern(toolName, pattern))) {
|
|
1461
|
+
return message;
|
|
1462
|
+
}
|
|
1463
|
+
if (isMessageProtected(message, input.config)) return message;
|
|
1464
|
+
const tokens = input.countTokens(text);
|
|
1465
|
+
if (tokens < cfg.minToolTokens) return message;
|
|
1466
|
+
const ref = refForRaw(input.state.messageRefs, message.id);
|
|
1467
|
+
if (!ref || ref === BLOCKED_REF) return message;
|
|
1468
|
+
if (hasStoredRef(current, ref)) return message;
|
|
1469
|
+
const kind = classifyKind(toolName);
|
|
1470
|
+
const head = normalizeHead(text, cfg.maxHeadChars);
|
|
1471
|
+
const command = extractCommand(
|
|
1472
|
+
callById.get(message.toolCallId)?.text,
|
|
1473
|
+
cfg.maxHeadChars
|
|
1474
|
+
);
|
|
1475
|
+
current = storeOriginal(current, {
|
|
1476
|
+
ref,
|
|
1477
|
+
rawId: message.id,
|
|
1478
|
+
text,
|
|
1479
|
+
kind,
|
|
1480
|
+
toolName,
|
|
1481
|
+
tokens,
|
|
1482
|
+
head
|
|
1483
|
+
});
|
|
1484
|
+
storedCount += 1;
|
|
1629
1485
|
return {
|
|
1630
|
-
...
|
|
1631
|
-
text:
|
|
1486
|
+
...message,
|
|
1487
|
+
text: buildStoredPlaceholder({
|
|
1488
|
+
ref,
|
|
1489
|
+
kind,
|
|
1490
|
+
tokens,
|
|
1491
|
+
head,
|
|
1492
|
+
command,
|
|
1493
|
+
retrieveToolName: cfg.toolName
|
|
1494
|
+
})
|
|
1632
1495
|
};
|
|
1633
1496
|
});
|
|
1634
|
-
return { messages:
|
|
1497
|
+
return { messages: updated, store: current, storedCount };
|
|
1635
1498
|
}
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
}
|
|
1647
|
-
|
|
1648
|
-
|
|
1499
|
+
var ccrStoreNode = {
|
|
1500
|
+
name: "ccr-store",
|
|
1501
|
+
enabled: (_io, ctx) => resolveCcrConfig(ctx.config).enabled,
|
|
1502
|
+
run(io, ctx) {
|
|
1503
|
+
const applied = storeLargeResults({
|
|
1504
|
+
messages: io.messages,
|
|
1505
|
+
state: io.state,
|
|
1506
|
+
store: ctx.contentStore,
|
|
1507
|
+
config: ctx.config,
|
|
1508
|
+
countTokens: ctx.countTokens
|
|
1509
|
+
});
|
|
1510
|
+
const effect = {
|
|
1511
|
+
store: applied.store,
|
|
1512
|
+
storedCount: applied.storedCount
|
|
1513
|
+
};
|
|
1514
|
+
const stats = applied.storedCount > 0 ? {
|
|
1515
|
+
...io.state.stats,
|
|
1516
|
+
storedCount: (io.state.stats.storedCount ?? 0) + applied.storedCount
|
|
1517
|
+
} : io.state.stats;
|
|
1518
|
+
return {
|
|
1519
|
+
...io,
|
|
1520
|
+
messages: applied.messages,
|
|
1521
|
+
state: stats === io.state.stats ? io.state : { ...io.state, stats },
|
|
1522
|
+
effects: { ...io.effects, ccr: effect }
|
|
1523
|
+
};
|
|
1649
1524
|
}
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1525
|
+
};
|
|
1526
|
+
function storeCoveredOriginals(store, messages, state, blockIds, countTokens, maxHeadChars = DEFAULT_CCR_CONFIG.maxHeadChars) {
|
|
1527
|
+
const covered = /* @__PURE__ */ new Set();
|
|
1528
|
+
for (const block of state.blocks) {
|
|
1529
|
+
if (!block.active || !blockIds.includes(block.blockId)) continue;
|
|
1530
|
+
for (const id of block.effectiveMessageIds) covered.add(id);
|
|
1653
1531
|
}
|
|
1654
|
-
|
|
1655
|
-
const
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
);
|
|
1660
|
-
|
|
1532
|
+
let current = store;
|
|
1533
|
+
for (const message of messages) {
|
|
1534
|
+
if (!covered.has(message.id)) continue;
|
|
1535
|
+
if (message.contentType === "reasoning") continue;
|
|
1536
|
+
const text = stripLeadingTag(message.text ?? "");
|
|
1537
|
+
if (text.length === 0) continue;
|
|
1538
|
+
const ref = refForRaw(state.messageRefs, message.id);
|
|
1539
|
+
if (!ref || ref === BLOCKED_REF) continue;
|
|
1540
|
+
if (isStoredPlaceholderText(text) && hasStoredRef(current, ref)) continue;
|
|
1541
|
+
current = storeOriginal(current, {
|
|
1542
|
+
ref,
|
|
1543
|
+
rawId: message.id,
|
|
1544
|
+
text,
|
|
1545
|
+
kind: classifyKind(message.toolName),
|
|
1546
|
+
toolName: message.toolName,
|
|
1547
|
+
tokens: countTokens(text),
|
|
1548
|
+
head: normalizeHead(text, maxHeadChars)
|
|
1549
|
+
});
|
|
1661
1550
|
}
|
|
1551
|
+
return current;
|
|
1552
|
+
}
|
|
1553
|
+
function noteRetrieval(state) {
|
|
1662
1554
|
return {
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1555
|
+
...state,
|
|
1556
|
+
stats: {
|
|
1557
|
+
...state.stats,
|
|
1558
|
+
retrievalCount: (state.stats.retrievalCount ?? 0) + 1
|
|
1559
|
+
}
|
|
1666
1560
|
};
|
|
1667
1561
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1562
|
+
|
|
1563
|
+
// src/boundaries.ts
|
|
1564
|
+
var MESSAGE_REF_PATTERN = /^m0*(\d{1,5})$/;
|
|
1565
|
+
var BLOCK_REF_PATTERN = /^b(\d{1,9})$/;
|
|
1566
|
+
function parseBoundary(ref) {
|
|
1567
|
+
const normalized = ref.trim().toLowerCase();
|
|
1568
|
+
const messageMatch = MESSAGE_REF_PATTERN.exec(normalized);
|
|
1569
|
+
if (messageMatch) {
|
|
1570
|
+
const numericId = Number(messageMatch[1]);
|
|
1571
|
+
if (numericId >= 1 && numericId <= 99999) {
|
|
1572
|
+
return { kind: "message", numericId, raw: normalized };
|
|
1573
|
+
}
|
|
1672
1574
|
}
|
|
1673
|
-
|
|
1575
|
+
const blockMatch = BLOCK_REF_PATTERN.exec(normalized);
|
|
1576
|
+
if (blockMatch) {
|
|
1577
|
+
const numericId = Number(blockMatch[1]);
|
|
1578
|
+
if (numericId >= 1) return { kind: "block", numericId, raw: normalized };
|
|
1579
|
+
}
|
|
1580
|
+
return null;
|
|
1674
1581
|
}
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1582
|
+
var BoundaryNotFoundError = class extends Error {
|
|
1583
|
+
code = "BOUNDARY_NOT_FOUND";
|
|
1584
|
+
kind;
|
|
1585
|
+
endpoint;
|
|
1586
|
+
constructor(kind, endpoint, message) {
|
|
1587
|
+
super(message);
|
|
1588
|
+
this.name = "BoundaryNotFoundError";
|
|
1589
|
+
this.code = "BOUNDARY_NOT_FOUND";
|
|
1590
|
+
this.kind = kind;
|
|
1591
|
+
this.endpoint = endpoint;
|
|
1592
|
+
}
|
|
1593
|
+
};
|
|
1594
|
+
function resolveBoundaries(input) {
|
|
1595
|
+
const start = parseBoundary(input.startRef);
|
|
1596
|
+
const end = parseBoundary(input.endRef);
|
|
1597
|
+
if (!start || !end) {
|
|
1598
|
+
throw new Error(
|
|
1599
|
+
`Invalid boundary ref(s): startId="${input.startRef}", endId="${input.endRef}". Use mNNNNN or bN.`
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1602
|
+
const indexByMessageId = /* @__PURE__ */ new Map();
|
|
1603
|
+
input.messages.forEach(
|
|
1604
|
+
(message, index) => indexByMessageId.set(message.id, index)
|
|
1605
|
+
);
|
|
1606
|
+
let snappedBoundaries = [];
|
|
1607
|
+
const startAnchor = resolveAnchorIndex(
|
|
1608
|
+
start,
|
|
1609
|
+
input.state,
|
|
1610
|
+
indexByMessageId,
|
|
1611
|
+
"start"
|
|
1612
|
+
);
|
|
1613
|
+
if (startAnchor.snapped) snappedBoundaries.push(startAnchor.snapped);
|
|
1614
|
+
const endAnchor = resolveAnchorIndex(
|
|
1615
|
+
end,
|
|
1616
|
+
input.state,
|
|
1617
|
+
indexByMessageId,
|
|
1618
|
+
"end"
|
|
1619
|
+
);
|
|
1620
|
+
if (endAnchor.snapped) snappedBoundaries.push(endAnchor.snapped);
|
|
1621
|
+
let startIndex = startAnchor.index;
|
|
1622
|
+
let endIndex = endAnchor.index;
|
|
1623
|
+
if (startIndex > endIndex) {
|
|
1624
|
+
[startIndex, endIndex] = [endIndex, startIndex];
|
|
1625
|
+
}
|
|
1626
|
+
const messageIds = [];
|
|
1627
|
+
for (let index = startIndex; index <= endIndex; index++) {
|
|
1628
|
+
const message = input.messages[index];
|
|
1629
|
+
if (message && !isRenderedSummaryMessage(message) && !isRetrievedMessage(message))
|
|
1630
|
+
messageIds.push(message.id);
|
|
1631
|
+
}
|
|
1632
|
+
const boundaryKind = start.kind === "block" || end.kind === "block" ? "block" : "message";
|
|
1633
|
+
const nestedBlockIds = [];
|
|
1634
|
+
const nestedSeen = /* @__PURE__ */ new Set();
|
|
1635
|
+
for (const block of activeBlocks(input.state)) {
|
|
1636
|
+
if (blockVisibleInRange(block, indexByMessageId, startIndex, endIndex)) {
|
|
1637
|
+
if (!nestedSeen.has(block.blockId)) {
|
|
1638
|
+
nestedSeen.add(block.blockId);
|
|
1639
|
+
nestedBlockIds.push(block.blockId);
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
const protectedGaps = [];
|
|
1644
|
+
return {
|
|
1645
|
+
startIndex,
|
|
1646
|
+
endIndex,
|
|
1647
|
+
messageIds,
|
|
1648
|
+
nestedBlockIds,
|
|
1649
|
+
boundaryKind,
|
|
1650
|
+
protectedGaps,
|
|
1651
|
+
snappedBoundaries
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
function resolveAnchorIndex(boundary, state, indexByMessageId, endpoint) {
|
|
1655
|
+
const label = endpoint === "start" ? "startId" : "endId";
|
|
1656
|
+
if (boundary.kind === "message") {
|
|
1657
|
+
const rawId = state.messageRefs.byRef[boundary.raw] ?? state.messageRefs.byRef[formatPaddedRef(boundary.numericId)];
|
|
1658
|
+
if (!rawId) {
|
|
1659
|
+
throw new BoundaryNotFoundError(
|
|
1660
|
+
"unknown",
|
|
1661
|
+
endpoint,
|
|
1662
|
+
`${label}="${boundary.raw}" does not exist in this session (typo or wrong session) \u2014 run acp_status for current refs.`
|
|
1663
|
+
);
|
|
1664
|
+
}
|
|
1665
|
+
const index = indexByMessageId.get(rawId);
|
|
1666
|
+
if (index !== void 0) {
|
|
1667
|
+
return { index, snapped: null };
|
|
1668
|
+
}
|
|
1669
|
+
const owner2 = activeOwnerAnchor(state, [rawId], indexByMessageId);
|
|
1670
|
+
if (owner2 !== null) {
|
|
1671
|
+
return {
|
|
1672
|
+
index: owner2,
|
|
1673
|
+
snapped: `${label}="${boundary.raw}" refers to a message already compressed into an active block \u2014 anchored to the active block covering it instead.`
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
throw new BoundaryNotFoundError(
|
|
1677
|
+
"consumed",
|
|
1678
|
+
endpoint,
|
|
1679
|
+
`${label}="${boundary.raw}" not found in visible context (likely consumed by an existing block).`
|
|
1680
|
+
);
|
|
1681
|
+
}
|
|
1682
|
+
const block = blockById(state, `b${boundary.numericId}`);
|
|
1683
|
+
if (!block) {
|
|
1684
|
+
throw new BoundaryNotFoundError(
|
|
1685
|
+
"unknown",
|
|
1686
|
+
endpoint,
|
|
1687
|
+
`${label}="b${boundary.numericId}" does not exist in this session (typo or wrong session) \u2014 run acp_status for current refs.`
|
|
1688
|
+
);
|
|
1689
|
+
}
|
|
1690
|
+
if (block.active) {
|
|
1691
|
+
const anchor = visibleBlockAnchor(block, indexByMessageId);
|
|
1692
|
+
if (anchor !== null) {
|
|
1693
|
+
return { index: anchor, snapped: null };
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
const owner = activeOwnerAnchor(
|
|
1697
|
+
state,
|
|
1698
|
+
block.effectiveMessageIds,
|
|
1699
|
+
indexByMessageId
|
|
1700
|
+
);
|
|
1701
|
+
if (owner !== null) {
|
|
1702
|
+
return {
|
|
1703
|
+
index: owner,
|
|
1704
|
+
snapped: `${label}="b${boundary.numericId}" was consumed by a higher-tier block \u2014 anchored to the active block covering its content instead.`
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
if (!block.active) {
|
|
1708
|
+
throw new BoundaryNotFoundError(
|
|
1709
|
+
"consumed",
|
|
1710
|
+
endpoint,
|
|
1711
|
+
`${label}="b${boundary.numericId}" not found in visible context (block distilled/consumed by a higher-tier block).`
|
|
1712
|
+
);
|
|
1713
|
+
}
|
|
1714
|
+
throw new BoundaryNotFoundError(
|
|
1715
|
+
"consumed",
|
|
1716
|
+
endpoint,
|
|
1717
|
+
`${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.`
|
|
1718
|
+
);
|
|
1719
|
+
}
|
|
1720
|
+
function activeOwnerAnchor(state, ownedIds, indexByMessageId) {
|
|
1721
|
+
if (ownedIds.length === 0) return null;
|
|
1722
|
+
const owned = new Set(ownedIds);
|
|
1723
|
+
let best = null;
|
|
1724
|
+
for (const block of state.blocks) {
|
|
1725
|
+
if (!block.active) continue;
|
|
1726
|
+
const inherited = inheritedContentIds(state, block);
|
|
1727
|
+
let ownsInherited = false;
|
|
1728
|
+
for (const id of owned) {
|
|
1729
|
+
if (inherited.has(id)) {
|
|
1730
|
+
ownsInherited = true;
|
|
1731
|
+
break;
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
if (!ownsInherited) continue;
|
|
1735
|
+
const anchor = visibleBlockAnchor(block, indexByMessageId);
|
|
1736
|
+
if (anchor === null) continue;
|
|
1737
|
+
if (best === null || anchor < best) {
|
|
1738
|
+
best = anchor;
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
return best;
|
|
1742
|
+
}
|
|
1743
|
+
function inheritedContentIds(state, block) {
|
|
1744
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1745
|
+
for (const childId of block.directBlockIds) {
|
|
1746
|
+
const child = blockById(state, childId);
|
|
1747
|
+
if (!child) continue;
|
|
1748
|
+
for (const id of child.effectiveMessageIds) ids.add(id);
|
|
1749
|
+
}
|
|
1750
|
+
return ids;
|
|
1751
|
+
}
|
|
1752
|
+
function formatPaddedRef(index) {
|
|
1753
|
+
return `m${String(index).padStart(5, "0")}`;
|
|
1754
|
+
}
|
|
1755
|
+
function visibleBlockAnchor(block, indexByMessageId) {
|
|
1756
|
+
const summaryIndex = indexByMessageId.get(summaryMessageId(block.blockId));
|
|
1757
|
+
if (summaryIndex !== void 0) return summaryIndex;
|
|
1758
|
+
return earliestIndexOfIds(block.effectiveMessageIds, indexByMessageId);
|
|
1759
|
+
}
|
|
1760
|
+
function blockVisibleInRange(block, indexByMessageId, startIndex, endIndex) {
|
|
1761
|
+
const summaryIndex = indexByMessageId.get(summaryMessageId(block.blockId));
|
|
1762
|
+
if (summaryIndex !== void 0 && summaryIndex >= startIndex && summaryIndex <= endIndex) {
|
|
1763
|
+
return true;
|
|
1764
|
+
}
|
|
1765
|
+
const rawIndex = earliestIndexOfIds(
|
|
1766
|
+
block.effectiveMessageIds,
|
|
1767
|
+
indexByMessageId
|
|
1768
|
+
);
|
|
1769
|
+
return rawIndex !== null && rawIndex >= startIndex && rawIndex <= endIndex;
|
|
1770
|
+
}
|
|
1771
|
+
function earliestIndexOfIds(ids, indexByMessageId) {
|
|
1772
|
+
let earliest = null;
|
|
1773
|
+
for (const id of ids) {
|
|
1774
|
+
const index = indexByMessageId.get(id);
|
|
1775
|
+
if (index !== void 0 && (earliest === null || index < earliest)) {
|
|
1776
|
+
earliest = index;
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
return earliest;
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
// src/truncate-tools.ts
|
|
1783
|
+
var TRUNCATION_MARKER = "[truncated for context space]";
|
|
1784
|
+
var DEFAULTS = {
|
|
1785
|
+
minOutputTokens: 1e3,
|
|
1786
|
+
keepPrefixChars: 2e3,
|
|
1787
|
+
keepSuffixChars: 2e3,
|
|
1788
|
+
protectRecentMessages: 3
|
|
1789
|
+
};
|
|
1790
|
+
function truncateLargeToolOutputs(messages, tokenCount, config, countTokens, options = {}) {
|
|
1791
|
+
const opts = { ...DEFAULTS, ...options };
|
|
1792
|
+
const limit = config.modelContextLimit;
|
|
1793
|
+
if (limit <= 0 || tokenCount < config.truncate.threshold * limit) {
|
|
1794
|
+
return { messages, truncatedCount: 0, savedTokens: 0, candidatesFound: 0 };
|
|
1795
|
+
}
|
|
1796
|
+
const protectedIndex = messages.length - opts.protectRecentMessages;
|
|
1797
|
+
const findCandidates = (predicate) => {
|
|
1798
|
+
const found = [];
|
|
1799
|
+
for (let i = 0; i < protectedIndex; i++) {
|
|
1800
|
+
const message = messages[i];
|
|
1801
|
+
if (!predicate(message)) continue;
|
|
1802
|
+
const text = message.text ?? "";
|
|
1803
|
+
if (text.length === 0 || text.includes(TRUNCATION_MARKER)) continue;
|
|
1804
|
+
if (countTokens(text) < opts.minOutputTokens) continue;
|
|
1805
|
+
found.push(message);
|
|
1806
|
+
}
|
|
1807
|
+
return found.sort(
|
|
1808
|
+
(a, b) => countTokens(b.text ?? "") - countTokens(a.text ?? "")
|
|
1809
|
+
);
|
|
1810
|
+
};
|
|
1811
|
+
const toolResults = findCandidates((m) => m.contentType === "tool-result");
|
|
1812
|
+
const textMessages = options.includeTextMessages ? findCandidates(
|
|
1813
|
+
(m) => m.contentType === "text" && (m.role === "user" || m.role === "assistant") && !isRenderedSummaryMessage(m) && !m.text?.startsWith(SUMMARY_HEADER)
|
|
1814
|
+
) : [];
|
|
1815
|
+
const candidatesFound = toolResults.length + textMessages.length;
|
|
1816
|
+
let truncatedCount = 0;
|
|
1817
|
+
let savedTokens = 0;
|
|
1818
|
+
let remaining = tokenCount;
|
|
1819
|
+
const targetTokens = config.truncate.threshold * limit * 0.9;
|
|
1820
|
+
const replacements = /* @__PURE__ */ new Map();
|
|
1821
|
+
const applyTo = (candidates) => {
|
|
1822
|
+
for (const candidate of candidates) {
|
|
1823
|
+
if (remaining <= targetTokens) break;
|
|
1824
|
+
const original = candidate.text ?? "";
|
|
1825
|
+
const tokens = countTokens(original);
|
|
1826
|
+
if (original.length <= opts.keepPrefixChars + opts.keepSuffixChars) {
|
|
1827
|
+
continue;
|
|
1828
|
+
}
|
|
1829
|
+
const prefix = clampPrefix(original, opts.keepPrefixChars);
|
|
1830
|
+
const suffix = clampWindow(original, original.length - opts.keepSuffixChars, original.length);
|
|
1831
|
+
const replacement = `${prefix}
|
|
1832
|
+
|
|
1833
|
+
...${TRUNCATION_MARKER} \u2014 original ~${tokens} tokens]...
|
|
1834
|
+
|
|
1835
|
+
${suffix}`;
|
|
1836
|
+
replacements.set(candidate.id, replacement);
|
|
1837
|
+
truncatedCount++;
|
|
1838
|
+
remaining -= tokens - countTokens(replacement);
|
|
1839
|
+
savedTokens += tokens - countTokens(replacement);
|
|
1840
|
+
}
|
|
1841
|
+
};
|
|
1842
|
+
applyTo(toolResults);
|
|
1843
|
+
applyTo(textMessages);
|
|
1844
|
+
if (replacements.size === 0) {
|
|
1845
|
+
return { messages, truncatedCount: 0, savedTokens: 0, candidatesFound };
|
|
1846
|
+
}
|
|
1847
|
+
return {
|
|
1848
|
+
messages: messages.map(
|
|
1849
|
+
(m) => replacements.has(m.id) ? { ...m, text: replacements.get(m.id) } : m
|
|
1850
|
+
),
|
|
1851
|
+
truncatedCount,
|
|
1852
|
+
savedTokens,
|
|
1853
|
+
candidatesFound
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
// src/hide-consumed.ts
|
|
1858
|
+
var KEEP_LAST_ORPHANED = 2;
|
|
1859
|
+
function rangeKey(startRef, endRef) {
|
|
1860
|
+
return `${startRef}::${endRef}`;
|
|
1861
|
+
}
|
|
1862
|
+
function parseCallText(text) {
|
|
1863
|
+
const raw = text ?? "";
|
|
1864
|
+
const start = raw.indexOf("{");
|
|
1865
|
+
if (start < 0) return null;
|
|
1866
|
+
let parsed;
|
|
1867
|
+
try {
|
|
1868
|
+
parsed = JSON.parse(raw.slice(start));
|
|
1869
|
+
} catch {
|
|
1870
|
+
return null;
|
|
1871
|
+
}
|
|
1872
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
1873
|
+
const obj = parsed;
|
|
1874
|
+
let content = null;
|
|
1875
|
+
let contentWasString = false;
|
|
1876
|
+
if (Array.isArray(obj.content)) {
|
|
1877
|
+
content = obj.content;
|
|
1878
|
+
} else if (typeof obj.content === "string") {
|
|
1879
|
+
contentWasString = true;
|
|
1880
|
+
try {
|
|
1881
|
+
const inner = JSON.parse(obj.content);
|
|
1882
|
+
if (Array.isArray(inner)) content = inner;
|
|
1883
|
+
} catch {
|
|
1884
|
+
content = null;
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
if (!content || content.length === 0) return null;
|
|
1888
|
+
return { prefix: raw.slice(0, start), obj, content, contentWasString };
|
|
1889
|
+
}
|
|
1890
|
+
function rewriteCompressText(text, liveKeys) {
|
|
1891
|
+
const parsed = parseCallText(text);
|
|
1892
|
+
if (!parsed) return null;
|
|
1893
|
+
const { prefix, obj, content, contentWasString } = parsed;
|
|
1894
|
+
const kept = content.filter((entry) => {
|
|
1895
|
+
if (!entry || typeof entry !== "object") return false;
|
|
1896
|
+
const e = entry;
|
|
1897
|
+
const s = typeof e.startId === "string" ? e.startId : typeof e.messageId === "string" ? e.messageId : "";
|
|
1898
|
+
const end = typeof e.endId === "string" ? e.endId : typeof e.messageId === "string" ? e.messageId : "";
|
|
1899
|
+
return liveKeys.has(rangeKey(s, end));
|
|
1900
|
+
});
|
|
1901
|
+
if (kept.length === 0) return null;
|
|
1902
|
+
return prefix + serializeCompacted(obj, kept, contentWasString).text;
|
|
1903
|
+
}
|
|
1904
|
+
var SUMMARY_STUB_CHARS = 200;
|
|
1905
|
+
function compactEntry(entry) {
|
|
1906
|
+
if (!entry || typeof entry !== "object") return entry;
|
|
1907
|
+
const e = entry;
|
|
1908
|
+
if (typeof e.summary !== "string" || e.summary.length <= SUMMARY_STUB_CHARS) return entry;
|
|
1909
|
+
return { ...e, summary: `${clampPrefix(e.summary, SUMMARY_STUB_CHARS - 1)}\u2026` };
|
|
1910
|
+
}
|
|
1911
|
+
function serializeCompacted(obj, content, contentWasString) {
|
|
1912
|
+
let changed = false;
|
|
1913
|
+
const compacted = content.map((entry) => {
|
|
1914
|
+
const out = compactEntry(entry);
|
|
1915
|
+
if (out !== entry) changed = true;
|
|
1916
|
+
return out;
|
|
1917
|
+
});
|
|
1918
|
+
const outContent = contentWasString ? JSON.stringify(compacted) : compacted;
|
|
1919
|
+
return { text: JSON.stringify({ ...obj, content: outContent }), changed };
|
|
1920
|
+
}
|
|
1921
|
+
function compactCompressText(text) {
|
|
1922
|
+
const parsed = parseCallText(text);
|
|
1923
|
+
if (!parsed) return null;
|
|
1924
|
+
const { prefix, obj, content, contentWasString } = parsed;
|
|
1925
|
+
const { text: out, changed } = serializeCompacted(obj, content, contentWasString);
|
|
1926
|
+
return changed ? prefix + out : null;
|
|
1927
|
+
}
|
|
1928
|
+
function hideConsumedCompressCalls(state, messages) {
|
|
1929
|
+
const allBlockCallIds = /* @__PURE__ */ new Set();
|
|
1930
|
+
const activeCallIds = /* @__PURE__ */ new Set();
|
|
1931
|
+
const liveRangeKeysByCallId = /* @__PURE__ */ new Map();
|
|
1932
|
+
const legacyLiveByCallId = /* @__PURE__ */ new Set();
|
|
1933
|
+
for (const block of state.blocks) {
|
|
1934
|
+
if (!block.compressCallId) continue;
|
|
1935
|
+
allBlockCallIds.add(block.compressCallId);
|
|
1936
|
+
if (!block.active) continue;
|
|
1937
|
+
activeCallIds.add(block.compressCallId);
|
|
1938
|
+
if (block.startRef === void 0 || block.endRef === void 0) {
|
|
1939
|
+
legacyLiveByCallId.add(block.compressCallId);
|
|
1940
|
+
continue;
|
|
1941
|
+
}
|
|
1942
|
+
let keys = liveRangeKeysByCallId.get(block.compressCallId);
|
|
1943
|
+
if (!keys) {
|
|
1944
|
+
keys = /* @__PURE__ */ new Set();
|
|
1945
|
+
liveRangeKeysByCallId.set(block.compressCallId, keys);
|
|
1946
|
+
}
|
|
1947
|
+
keys.add(rangeKey(block.startRef, block.endRef));
|
|
1948
|
+
}
|
|
1949
|
+
const lastOrphanedCallIds = [];
|
|
1950
|
+
for (let i = messages.length - 1; i >= 0 && lastOrphanedCallIds.length < KEEP_LAST_ORPHANED; i--) {
|
|
1951
|
+
const message = messages[i];
|
|
1952
|
+
if (message.toolName !== "compress" || message.contentType !== "tool-call") continue;
|
|
1953
|
+
const callId = message.toolCallId;
|
|
1954
|
+
if (callId && !allBlockCallIds.has(callId)) {
|
|
1955
|
+
lastOrphanedCallIds.push(callId);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
const keepCallIds = /* @__PURE__ */ new Set([...activeCallIds, ...lastOrphanedCallIds]);
|
|
1959
|
+
const hiddenCallIds = /* @__PURE__ */ new Set();
|
|
1960
|
+
for (const message of messages) {
|
|
1961
|
+
if (message.toolName === "compress" && message.contentType === "tool-call" && (!message.toolCallId || !keepCallIds.has(message.toolCallId))) {
|
|
1962
|
+
if (message.toolCallId) hiddenCallIds.add(message.toolCallId);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
let hidden = 0;
|
|
1966
|
+
const result = [];
|
|
1967
|
+
for (const message of messages) {
|
|
1968
|
+
if (message.toolName === "compress" && message.contentType === "tool-call" && (!message.toolCallId || !keepCallIds.has(message.toolCallId))) {
|
|
1969
|
+
hidden++;
|
|
1970
|
+
continue;
|
|
1971
|
+
}
|
|
1972
|
+
if (message.contentType === "tool-result" && message.toolCallId && hiddenCallIds.has(message.toolCallId)) {
|
|
1973
|
+
hidden++;
|
|
1974
|
+
continue;
|
|
1975
|
+
}
|
|
1976
|
+
if (message.toolName === "compress" && message.contentType === "tool-call" && message.toolCallId && keepCallIds.has(message.toolCallId)) {
|
|
1977
|
+
const liveKeys = liveRangeKeysByCallId.get(message.toolCallId);
|
|
1978
|
+
if (liveKeys && liveKeys.size > 0 && !legacyLiveByCallId.has(message.toolCallId)) {
|
|
1979
|
+
const rewritten = rewriteCompressText(message.text, liveKeys);
|
|
1980
|
+
if (rewritten !== null) {
|
|
1981
|
+
result.push({ ...message, text: rewritten });
|
|
1982
|
+
continue;
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
const compacted = compactCompressText(message.text);
|
|
1986
|
+
if (compacted !== null) {
|
|
1987
|
+
result.push({ ...message, text: compacted });
|
|
1988
|
+
continue;
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
result.push(message);
|
|
1992
|
+
}
|
|
1993
|
+
return { messages: result, hidden };
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
// src/absorb.ts
|
|
1997
|
+
var ABSORB_PROMPT_MARKER = "[ACP absorb]";
|
|
1998
|
+
var DEFAULT_ABSORB_CONFIG = {
|
|
1999
|
+
enabled: false,
|
|
2000
|
+
toolName: ABSORB_TOOL_NAME,
|
|
2001
|
+
// Raised 1000 → 4000 (issue #352): lossless CCR takes over large-result
|
|
2002
|
+
// handling; absorb's forced distillation only fires above the new bar.
|
|
2003
|
+
minToolTokens: 4e3,
|
|
2004
|
+
contextThresholdPct: 0,
|
|
2005
|
+
excludeTools: []
|
|
2006
|
+
};
|
|
2007
|
+
function resolveAbsorbConfig(config) {
|
|
2008
|
+
return { ...DEFAULT_ABSORB_CONFIG, ...config.absorb ?? {} };
|
|
2009
|
+
}
|
|
2010
|
+
function formatTokenCount(tokens) {
|
|
2011
|
+
if (tokens < 1e3) return String(tokens);
|
|
2012
|
+
if (tokens < 1e4) return (tokens / 1e3).toFixed(1) + "K";
|
|
2013
|
+
return Math.round(tokens / 1e3) + "K";
|
|
2014
|
+
}
|
|
2015
|
+
function buildAbsorbPrompt(ref, tokens, toolName = ABSORB_TOOL_NAME) {
|
|
2016
|
+
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)".`;
|
|
2017
|
+
}
|
|
2018
|
+
function buildAbsorbSystemPrompt(toolName = ABSORB_TOOL_NAME) {
|
|
2019
|
+
return `INSTANT TOOL-RESULT ABSORPTION (${toolName})
|
|
2020
|
+
|
|
2021
|
+
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.`;
|
|
2022
|
+
}
|
|
2023
|
+
function isAcpOrConfiguredTool(toolName, cfg) {
|
|
2024
|
+
if (!toolName) return false;
|
|
2025
|
+
if (toolName === cfg.toolName) return true;
|
|
2026
|
+
return ACP_TOOL_NAMES.has(toolName);
|
|
2027
|
+
}
|
|
2028
|
+
function isAbsorbCandidate(msg, config, latest) {
|
|
2029
|
+
if (msg.contentType !== "tool-result" || !msg.toolCallId) return false;
|
|
2030
|
+
if (isStoredPlaceholderText(msg.text ?? "")) return false;
|
|
2031
|
+
const cfg = resolveAbsorbConfig(config);
|
|
2032
|
+
if (isAcpOrConfiguredTool(msg.toolName, cfg)) return false;
|
|
2033
|
+
if (isMessageProtected(msg, config)) return false;
|
|
2034
|
+
if (latest && isMessageLatestProtected(msg, latest)) return false;
|
|
2035
|
+
for (const pattern of cfg.excludeTools) {
|
|
2036
|
+
if (msg.toolName && matchToolPattern(msg.toolName, pattern)) return false;
|
|
2037
|
+
}
|
|
2038
|
+
return true;
|
|
2039
|
+
}
|
|
2040
|
+
function hideAbsorbedMessages(messages, state) {
|
|
2041
|
+
const records = state.absorbed ?? [];
|
|
2042
|
+
if (records.length === 0) return messages;
|
|
2043
|
+
const hidden = /* @__PURE__ */ new Set();
|
|
2044
|
+
for (const record of records) {
|
|
2045
|
+
if (record.callMessageId) hidden.add(record.callMessageId);
|
|
2046
|
+
if (record.resultMessageId) hidden.add(record.resultMessageId);
|
|
2047
|
+
}
|
|
2048
|
+
return messages.filter((msg) => !hidden.has(msg.id));
|
|
2049
|
+
}
|
|
2050
|
+
function appendAbsorbPrompts(messages, state, config, tokenCount, countTokens) {
|
|
2051
|
+
const cfg = resolveAbsorbConfig(config);
|
|
2052
|
+
if (!cfg.enabled) return { messages, promptedCount: 0 };
|
|
2053
|
+
const limit = config.modelContextLimit;
|
|
2054
|
+
if (cfg.contextThresholdPct > 0 && limit > 0 && tokenCount < cfg.contextThresholdPct * limit) {
|
|
2055
|
+
return { messages, promptedCount: 0 };
|
|
2056
|
+
}
|
|
2057
|
+
const absorbedIds = /* @__PURE__ */ new Set();
|
|
2058
|
+
for (const record of state.absorbed ?? []) {
|
|
2059
|
+
if (record.resultMessageId) absorbedIds.add(record.resultMessageId);
|
|
2060
|
+
}
|
|
2061
|
+
let promptedCount = 0;
|
|
2062
|
+
const latest = collectLatestProtected(messages, config);
|
|
2063
|
+
const out = messages.map((msg) => {
|
|
2064
|
+
if (!isAbsorbCandidate(msg, config, latest)) return msg;
|
|
2065
|
+
if (absorbedIds.has(msg.id)) return msg;
|
|
2066
|
+
const text = msg.text ?? "";
|
|
2067
|
+
if (text.includes(ABSORB_PROMPT_MARKER)) return msg;
|
|
2068
|
+
const tokens = countTokens(text);
|
|
2069
|
+
if (tokens < cfg.minToolTokens) return msg;
|
|
2070
|
+
const ref = refForRaw(state.messageRefs, msg.id);
|
|
2071
|
+
if (!ref || ref === BLOCKED_REF) return msg;
|
|
2072
|
+
promptedCount++;
|
|
2073
|
+
return {
|
|
2074
|
+
...msg,
|
|
2075
|
+
text: text + "\n\n" + buildAbsorbPrompt(ref, tokens, cfg.toolName)
|
|
2076
|
+
};
|
|
2077
|
+
});
|
|
2078
|
+
return { messages: out, promptedCount };
|
|
2079
|
+
}
|
|
2080
|
+
function parseAbsorbInput(input, callId, onWarn) {
|
|
2081
|
+
let obj = null;
|
|
2082
|
+
if (typeof input === "string") {
|
|
2083
|
+
try {
|
|
2084
|
+
const parsed = JSON.parse(input);
|
|
2085
|
+
if (parsed && typeof parsed === "object") {
|
|
2086
|
+
obj = parsed;
|
|
2087
|
+
}
|
|
2088
|
+
} catch {
|
|
2089
|
+
obj = null;
|
|
2090
|
+
}
|
|
2091
|
+
} else if (input && typeof input === "object") {
|
|
2092
|
+
obj = input;
|
|
2093
|
+
}
|
|
2094
|
+
if (!obj) {
|
|
2095
|
+
onWarn?.(`[acp-absorb-input] rejected: not an object (${typeof input})`);
|
|
2096
|
+
return null;
|
|
2097
|
+
}
|
|
2098
|
+
const ref = pickString(obj, "ref", "messageId", "of");
|
|
2099
|
+
const summary = pickString(obj, "summary", "content");
|
|
2100
|
+
if (typeof ref !== "string" || typeof summary !== "string") {
|
|
2101
|
+
onWarn?.(
|
|
2102
|
+
`[acp-absorb-input] rejected: need ref (string) + summary (string); keys: ${Object.keys(obj).join(",")}`
|
|
2103
|
+
);
|
|
2104
|
+
return null;
|
|
2105
|
+
}
|
|
2106
|
+
return {
|
|
2107
|
+
ref: ref.trim(),
|
|
2108
|
+
summary,
|
|
2109
|
+
...callId ? { absorbCallId: callId } : {}
|
|
2110
|
+
};
|
|
2111
|
+
}
|
|
2112
|
+
function pickString(obj, ...keys) {
|
|
2113
|
+
for (const key of keys) {
|
|
2114
|
+
const value = obj[key];
|
|
2115
|
+
if (typeof value === "string") return value;
|
|
2116
|
+
}
|
|
2117
|
+
return void 0;
|
|
2118
|
+
}
|
|
2119
|
+
function applyAbsorb(input) {
|
|
2120
|
+
const countTokens = input.countTokens ?? ((text) => Math.ceil(text.length / 4));
|
|
2121
|
+
const summary = input.summary?.trim() ?? "";
|
|
2122
|
+
if (!summary) {
|
|
2123
|
+
return {
|
|
2124
|
+
state: input.state,
|
|
2125
|
+
ok: false,
|
|
2126
|
+
resultText: "absorb failed: summary is empty \u2014 provide the distilled key info of the tool result."
|
|
2127
|
+
};
|
|
2128
|
+
}
|
|
2129
|
+
const cfg = resolveAbsorbConfig(input.config);
|
|
2130
|
+
const rawId = rawForRef(input.state.messageRefs, input.ref.trim());
|
|
2131
|
+
if (!rawId) {
|
|
2132
|
+
return {
|
|
2133
|
+
state: input.state,
|
|
2134
|
+
ok: false,
|
|
2135
|
+
resultText: `absorb failed: ref ${input.ref} does not exist in this session (it may be hidden, already compressed, or stale).`
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
2138
|
+
const existing = (input.state.absorbed ?? []).find(
|
|
2139
|
+
(record2) => record2.resultMessageId === rawId
|
|
2140
|
+
);
|
|
2141
|
+
if (existing) {
|
|
2142
|
+
return {
|
|
2143
|
+
state: input.state,
|
|
2144
|
+
ok: true,
|
|
2145
|
+
resultText: `already absorbed (${input.ref}) \u2014 no change.`
|
|
2146
|
+
};
|
|
2147
|
+
}
|
|
2148
|
+
const target = input.messages.find((m) => m.id === rawId);
|
|
2149
|
+
if (!target) {
|
|
2150
|
+
return {
|
|
2151
|
+
state: input.state,
|
|
2152
|
+
ok: false,
|
|
2153
|
+
resultText: `absorb failed: ref ${input.ref} is not visible in this session (hidden or compressed).`
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
if (target.contentType !== "tool-result") {
|
|
2157
|
+
return {
|
|
2158
|
+
state: input.state,
|
|
2159
|
+
ok: false,
|
|
2160
|
+
resultText: `absorb failed: ref ${input.ref} is a ${target.contentType}, not a tool result.`
|
|
2161
|
+
};
|
|
2162
|
+
}
|
|
2163
|
+
if (isAcpOrConfiguredTool(target.toolName, cfg)) {
|
|
2164
|
+
return {
|
|
2165
|
+
state: input.state,
|
|
2166
|
+
ok: false,
|
|
2167
|
+
resultText: `absorb failed: ${target.toolName} is an ACP-managed tool result \u2014 it is not absorbable.`
|
|
2168
|
+
};
|
|
2169
|
+
}
|
|
2170
|
+
if (isMessageProtected(target, input.config) || isMessageLatestProtected(
|
|
2171
|
+
target,
|
|
2172
|
+
collectLatestProtected(input.messages, input.config)
|
|
2173
|
+
)) {
|
|
2174
|
+
return {
|
|
2175
|
+
state: input.state,
|
|
2176
|
+
ok: false,
|
|
2177
|
+
resultText: `absorb failed: ${target.toolName} is a protected tool \u2014 its results must stay visible.`
|
|
2178
|
+
};
|
|
2179
|
+
}
|
|
2180
|
+
if (!target.toolCallId) {
|
|
2181
|
+
return {
|
|
2182
|
+
state: input.state,
|
|
2183
|
+
ok: false,
|
|
2184
|
+
resultText: `absorb failed: ref ${input.ref} has no tool-call id \u2014 cannot pair it for hiding.`
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2187
|
+
const call = input.messages.find(
|
|
2188
|
+
(m) => m.contentType === "tool-call" && m.toolCallId === target.toolCallId
|
|
2189
|
+
);
|
|
2190
|
+
const tokens = countTokens(target.text ?? "");
|
|
2191
|
+
const summaryTokens = countTokens(summary);
|
|
2192
|
+
const record = {
|
|
2193
|
+
toolCallId: target.toolCallId,
|
|
2194
|
+
callMessageId: call?.id ?? "",
|
|
2195
|
+
resultMessageId: target.id,
|
|
2196
|
+
...input.absorbCallId ? { absorbCallId: input.absorbCallId } : {},
|
|
2197
|
+
summary,
|
|
2198
|
+
tokensReclaimed: tokens,
|
|
2199
|
+
createdAt: Date.now()
|
|
2200
|
+
};
|
|
2201
|
+
const state = {
|
|
2202
|
+
...input.state,
|
|
2203
|
+
absorbed: [...input.state.absorbed ?? [], record],
|
|
2204
|
+
stats: {
|
|
2205
|
+
...input.state.stats,
|
|
2206
|
+
absorbedTokens: (input.state.stats.absorbedTokens ?? 0) + tokens
|
|
2207
|
+
}
|
|
2208
|
+
};
|
|
2209
|
+
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.` : "";
|
|
2210
|
+
return {
|
|
2211
|
+
state,
|
|
2212
|
+
ok: true,
|
|
2213
|
+
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}`
|
|
2214
|
+
};
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
// src/crush.ts
|
|
2218
|
+
var DEFAULT_CRUSH_CONFIG = {
|
|
2219
|
+
enabled: false,
|
|
2220
|
+
minReduction: 0.1
|
|
2221
|
+
};
|
|
2222
|
+
var MAX_INPUT_CHARS = 2e6;
|
|
2223
|
+
var MAX_DEPTH = 12;
|
|
2224
|
+
var MAX_FOLDS = 1e3;
|
|
2225
|
+
var RUN_MIN = 4;
|
|
2226
|
+
var HOIST_MIN = 4;
|
|
2227
|
+
var CODE_KEEP_RATIO = 0.97;
|
|
2228
|
+
var CRUSH_KEY = "__acp_crush";
|
|
2229
|
+
function isPlainObject(v) {
|
|
2230
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
|
|
2231
|
+
const p = Object.getPrototypeOf(v);
|
|
2232
|
+
return p === null || p === Object.prototype;
|
|
2233
|
+
}
|
|
2234
|
+
function classifyCrushText(text) {
|
|
2235
|
+
const trimmed = text.trim();
|
|
2236
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]"))
|
|
2237
|
+
return "json";
|
|
2238
|
+
if (detectLanguage(text) !== null) return "code";
|
|
2239
|
+
return "log";
|
|
2240
|
+
}
|
|
2241
|
+
function crushText(text, options = {}) {
|
|
2242
|
+
const countTokens = options.countTokens ?? defaultCountTokens;
|
|
2243
|
+
const minReduction = options.minReduction ?? DEFAULT_CRUSH_CONFIG.minReduction;
|
|
2244
|
+
const meta = options.meta ?? {};
|
|
2245
|
+
if (text.length === 0 || text.length > MAX_INPUT_CHARS) return null;
|
|
2246
|
+
const rawTok = countTokens(text);
|
|
2247
|
+
if (rawTok <= 0) return null;
|
|
2248
|
+
const kind = classifyCrushText(text);
|
|
2249
|
+
const wanted = options.plugins ?? registeredPlugins();
|
|
2250
|
+
for (const plugin of wanted) {
|
|
2251
|
+
let res;
|
|
2252
|
+
try {
|
|
2253
|
+
if (!Array.isArray(plugin.kinds) || !plugin.kinds.includes(kind))
|
|
2254
|
+
continue;
|
|
2255
|
+
res = plugin.run(text, meta);
|
|
2256
|
+
} catch {
|
|
2257
|
+
continue;
|
|
2258
|
+
}
|
|
2259
|
+
if (!res || typeof res.text !== "string" || res.text === "" || res.text === text)
|
|
2260
|
+
continue;
|
|
2261
|
+
const newTok = countTokens(res.text);
|
|
2262
|
+
const reduction = (rawTok - newTok) / rawTok;
|
|
2263
|
+
if (reduction < minReduction) continue;
|
|
2264
|
+
if (kind === "log" && res.lossy && !errorLinesSurvive(text, res.text))
|
|
2265
|
+
continue;
|
|
2266
|
+
const out = {
|
|
2267
|
+
text: res.text,
|
|
2268
|
+
strategy: plugin.id,
|
|
2269
|
+
lossy: res.lossy
|
|
2270
|
+
};
|
|
2271
|
+
if (res.stats) out.stats = res.stats;
|
|
2272
|
+
return out;
|
|
2273
|
+
}
|
|
2274
|
+
return null;
|
|
2275
|
+
}
|
|
2276
|
+
function canonOf(v, cache2) {
|
|
2277
|
+
if (!isPlainObject(v) && !Array.isArray(v))
|
|
2278
|
+
return JSON.stringify(v) ?? "null";
|
|
2279
|
+
const key = v;
|
|
2280
|
+
const hit = cache2.get(key);
|
|
2281
|
+
if (hit !== void 0) return hit;
|
|
2282
|
+
let s;
|
|
2283
|
+
if (Array.isArray(v)) {
|
|
2284
|
+
s = `[${v.map((e) => canonOf(e, cache2)).join(",")}]`;
|
|
2285
|
+
} else {
|
|
2286
|
+
const entries = Object.entries(v).map(([k, val]) => `${JSON.stringify(k)}:${canonOf(val, cache2)}`).sort();
|
|
2287
|
+
s = `{${entries.join(",")}}`;
|
|
2288
|
+
}
|
|
2289
|
+
cache2.set(key, s);
|
|
2290
|
+
return s;
|
|
2291
|
+
}
|
|
2292
|
+
function strLen(v) {
|
|
2293
|
+
return JSON.stringify(v).length;
|
|
2294
|
+
}
|
|
2295
|
+
function compactValue(v, depth, stats, cache2) {
|
|
2296
|
+
if (depth > MAX_DEPTH || stats.folds >= MAX_FOLDS) return v;
|
|
2297
|
+
if (Array.isArray(v)) return compactArray(v, depth, stats, cache2);
|
|
2298
|
+
if (isPlainObject(v)) {
|
|
2299
|
+
let changed = false;
|
|
2300
|
+
const out = {};
|
|
2301
|
+
for (const [k, val] of Object.entries(v)) {
|
|
2302
|
+
const nv = compactValue(val, depth + 1, stats, cache2);
|
|
2303
|
+
out[k] = nv;
|
|
2304
|
+
if (nv !== val) changed = true;
|
|
2305
|
+
}
|
|
2306
|
+
return changed ? out : v;
|
|
2307
|
+
}
|
|
2308
|
+
return v;
|
|
2309
|
+
}
|
|
2310
|
+
function compactArray(arr, depth, stats, cache2) {
|
|
2311
|
+
const n = arr.length;
|
|
2312
|
+
if (n < RUN_MIN && n < HOIST_MIN)
|
|
2313
|
+
return arr.map((e) => compactValue(e, depth + 1, stats, cache2));
|
|
2314
|
+
if (stats.folds >= MAX_FOLDS) return arr;
|
|
2315
|
+
const plainStats = { folds: 0 };
|
|
2316
|
+
const plain = arr.map((e) => compactValue(e, depth + 1, plainStats, cache2));
|
|
2317
|
+
let winner = plain;
|
|
2318
|
+
let winnerLen = strLen(plain);
|
|
2319
|
+
let winnerFolds = plainStats.folds;
|
|
2320
|
+
if (n >= RUN_MIN) {
|
|
2321
|
+
const rf = buildRunFold(arr, depth, cache2);
|
|
2322
|
+
if (rf && rf.len < winnerLen) {
|
|
2323
|
+
winner = rf.value;
|
|
2324
|
+
winnerLen = rf.len;
|
|
2325
|
+
winnerFolds = rf.folds;
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
if (n >= HOIST_MIN && arr.every((e) => isPlainObject(e))) {
|
|
2329
|
+
const hc = buildConstHoist(arr, depth, cache2);
|
|
2330
|
+
if (hc && hc.len < winnerLen) {
|
|
2331
|
+
winner = hc.value;
|
|
2332
|
+
winnerLen = hc.len;
|
|
2333
|
+
winnerFolds = hc.folds;
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
stats.folds += winnerFolds;
|
|
2337
|
+
return winner;
|
|
2338
|
+
}
|
|
2339
|
+
function isRunMarker(v) {
|
|
2340
|
+
return isPlainObject(v) && v[CRUSH_KEY] === "identical-run";
|
|
2341
|
+
}
|
|
2342
|
+
function buildRunFold(arr, depth, cache2) {
|
|
2343
|
+
const n = arr.length;
|
|
2344
|
+
const canon = arr.map((e) => canonOf(e, cache2));
|
|
2345
|
+
const segs = [];
|
|
2346
|
+
let runs = 0;
|
|
2347
|
+
let i = 0;
|
|
2348
|
+
while (i < n) {
|
|
2349
|
+
let j = i;
|
|
2350
|
+
while (j + 1 < n && canon[j + 1] === canon[i]) j++;
|
|
2351
|
+
const runLen = j - i + 1;
|
|
2352
|
+
if (runLen >= RUN_MIN) {
|
|
2353
|
+
segs.push({ [CRUSH_KEY]: "identical-run", count: runLen, item: arr[i] });
|
|
2354
|
+
runs++;
|
|
2355
|
+
} else {
|
|
2356
|
+
for (let k = i; k <= j; k++) segs.push(arr[k]);
|
|
2357
|
+
}
|
|
2358
|
+
i = j + 1;
|
|
2359
|
+
}
|
|
2360
|
+
if (runs === 0) return null;
|
|
2361
|
+
const inner = { folds: 0 };
|
|
2362
|
+
const final = segs.map(
|
|
2363
|
+
(s) => isRunMarker(s) ? { ...s, item: compactValue(s.item, depth + 1, inner, cache2) } : compactValue(s, depth + 1, inner, cache2)
|
|
2364
|
+
);
|
|
2365
|
+
return { value: final, len: strLen(final), folds: runs + inner.folds };
|
|
2366
|
+
}
|
|
2367
|
+
function buildConstHoist(arr, depth, cache2) {
|
|
2368
|
+
const n = arr.length;
|
|
2369
|
+
const keys = [];
|
|
2370
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2371
|
+
for (const e of arr) {
|
|
2372
|
+
for (const k of Object.keys(e)) {
|
|
2373
|
+
if (!seen.has(k)) {
|
|
2374
|
+
seen.add(k);
|
|
2375
|
+
keys.push(k);
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
const first = arr[0];
|
|
2380
|
+
if (!first) return null;
|
|
2381
|
+
const constKeys = [];
|
|
2382
|
+
for (const k of keys) {
|
|
2383
|
+
if (!(k in first)) continue;
|
|
2384
|
+
const c0 = canonOf(first[k], cache2);
|
|
2385
|
+
let constant = true;
|
|
2386
|
+
for (let i = 1; i < n; i++) {
|
|
2387
|
+
const row = arr[i];
|
|
2388
|
+
if (!(k in row) || canonOf(row[k], cache2) !== c0) {
|
|
2389
|
+
constant = false;
|
|
2390
|
+
break;
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
if (constant) constKeys.push(k);
|
|
2394
|
+
}
|
|
2395
|
+
if (constKeys.length === 0) return null;
|
|
2396
|
+
const constSet = new Set(constKeys);
|
|
2397
|
+
const constObj = {};
|
|
2398
|
+
for (const k of constKeys) constObj[k] = first[k];
|
|
2399
|
+
const items = arr.map((e) => {
|
|
2400
|
+
const o = {};
|
|
2401
|
+
for (const [k, v] of Object.entries(e)) {
|
|
2402
|
+
if (!constSet.has(k)) o[k] = v;
|
|
2403
|
+
}
|
|
2404
|
+
return o;
|
|
2405
|
+
});
|
|
2406
|
+
const hasVarying = items.some((o) => Object.keys(o).length > 0);
|
|
2407
|
+
const inner = { folds: 0 };
|
|
2408
|
+
const env = {
|
|
2409
|
+
[CRUSH_KEY]: "rows",
|
|
2410
|
+
rows: n,
|
|
2411
|
+
const: compactValue(constObj, depth + 1, inner, cache2)
|
|
2412
|
+
};
|
|
2413
|
+
if (hasVarying)
|
|
2414
|
+
env.items = items.map((it) => compactValue(it, depth + 1, inner, cache2));
|
|
2415
|
+
const len = strLen(env);
|
|
2416
|
+
if (len >= strLen(arr)) return null;
|
|
2417
|
+
return { value: env, len, folds: 1 + inner.folds };
|
|
2418
|
+
}
|
|
2419
|
+
function crushJson(text) {
|
|
2420
|
+
const parsed = JSON.parse(text);
|
|
2421
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
2422
|
+
const stats = { folds: 0 };
|
|
2423
|
+
const out = compactValue(parsed, 0, stats, cache2);
|
|
2424
|
+
if (stats.folds === 0) return null;
|
|
2425
|
+
const s = JSON.stringify(out);
|
|
2426
|
+
return s.length < text.length ? s : null;
|
|
2427
|
+
}
|
|
2428
|
+
function detectLanguage(text) {
|
|
2429
|
+
const sample = text.split(/\r?\n/).slice(0, 300);
|
|
2430
|
+
let py = 0;
|
|
2431
|
+
let js = 0;
|
|
2432
|
+
for (const line of sample) {
|
|
2433
|
+
if (/^\s*#!.*python/.test(line)) py += 4;
|
|
2434
|
+
if (/^\s*(async\s+)?def\s+\w/.test(line) || /^\s*class\s+\w/.test(line))
|
|
2435
|
+
py += 2;
|
|
2436
|
+
if (/\bfrom\s+['"]/.test(line) || /^\s*import\s+['"]/.test(line)) js += 2;
|
|
2437
|
+
else if (/^\s*(import|from)\s+[A-Za-z_]\w*/.test(line)) py += 1;
|
|
2438
|
+
if (/^\s*(function\b|const\s|let\s|var\s)/.test(line)) js += 2;
|
|
2439
|
+
if (/=>/.test(line)) js += 1;
|
|
2440
|
+
if (/^\s*(public|private|protected|static|final|void|return)\b/.test(line))
|
|
2441
|
+
js += 1;
|
|
2442
|
+
if (/;\s*$/.test(line) && line.trim().length > 0) js += 1;
|
|
2443
|
+
if (/\bself\./.test(line) || /^\s*(elif|except|yield)\b/.test(line))
|
|
2444
|
+
py += 1;
|
|
2445
|
+
if (/\bconsole\.log\b|\brequire\s*\(|module\.exports|\bawait\s/.test(line))
|
|
2446
|
+
js += 1;
|
|
2447
|
+
}
|
|
2448
|
+
const MARGIN = 1.5;
|
|
2449
|
+
if (py >= 3 && py > js * MARGIN) return "python";
|
|
2450
|
+
if (js >= 4 && js > py * MARGIN) return "js";
|
|
2451
|
+
return null;
|
|
2452
|
+
}
|
|
2453
|
+
function finalizeTrimmed(original, emitted) {
|
|
2454
|
+
if (emitted.length >= original.split(/\r?\n/).length) return null;
|
|
2455
|
+
const out = emitted.join("\n") + (original.endsWith("\n") ? "\n" : "");
|
|
2456
|
+
return out.length < original.length * CODE_KEEP_RATIO ? out : null;
|
|
2457
|
+
}
|
|
2458
|
+
function trimPython(text) {
|
|
2459
|
+
const lines = text.split(/\r?\n/);
|
|
2460
|
+
const emitted = [];
|
|
2461
|
+
let pending = 0;
|
|
2462
|
+
const flush = () => {
|
|
2463
|
+
if (pending > 0) {
|
|
2464
|
+
emitted.push(
|
|
2465
|
+
`# [acp-crush: elided ${pending} line${pending === 1 ? "" : "s"}]`
|
|
2466
|
+
);
|
|
2467
|
+
pending = 0;
|
|
2468
|
+
}
|
|
2469
|
+
};
|
|
2470
|
+
let state = "code";
|
|
2471
|
+
let quote = "";
|
|
2472
|
+
let isDocstring = false;
|
|
2473
|
+
let blankRun = 0;
|
|
2474
|
+
let atModuleStart = true;
|
|
2475
|
+
let pendingDefClass = false;
|
|
2476
|
+
let docstringSlot = false;
|
|
2477
|
+
const classifySig = (codePart) => {
|
|
2478
|
+
const t = codePart.trim();
|
|
2479
|
+
if (/^(async\s+)?(def|class)\b/.test(t)) pendingDefClass = true;
|
|
2480
|
+
if (t.endsWith(":")) {
|
|
2481
|
+
docstringSlot = pendingDefClass;
|
|
2482
|
+
pendingDefClass = false;
|
|
2483
|
+
} else if (t.length > 0 && !t.startsWith("@")) {
|
|
2484
|
+
docstringSlot = false;
|
|
2485
|
+
}
|
|
2486
|
+
atModuleStart = false;
|
|
2487
|
+
};
|
|
2488
|
+
for (let li = 0; li < lines.length; li++) {
|
|
2489
|
+
const line = lines[li];
|
|
2490
|
+
if (state === "triple") {
|
|
2491
|
+
const close = findTripleClose(line, quote);
|
|
2492
|
+
if (close === -1) {
|
|
2493
|
+
if (isDocstring) pending++;
|
|
2494
|
+
else emitted.push(line);
|
|
2495
|
+
continue;
|
|
2496
|
+
}
|
|
2497
|
+
state = "code";
|
|
2498
|
+
const rest = line.slice(close + 3).trim();
|
|
2499
|
+
if (rest.length > 0) {
|
|
2500
|
+
flush();
|
|
2501
|
+
emitted.push(line);
|
|
2502
|
+
classifySig(rest);
|
|
2503
|
+
} else {
|
|
2504
|
+
if (isDocstring) pending++;
|
|
2505
|
+
else emitted.push(line);
|
|
2506
|
+
}
|
|
2507
|
+
isDocstring = false;
|
|
2508
|
+
docstringSlot = false;
|
|
2509
|
+
pendingDefClass = false;
|
|
2510
|
+
atModuleStart = false;
|
|
2511
|
+
blankRun = 0;
|
|
2512
|
+
continue;
|
|
2513
|
+
}
|
|
2514
|
+
if (line.trim().length === 0) {
|
|
2515
|
+
blankRun++;
|
|
2516
|
+
if (blankRun >= 3) {
|
|
2517
|
+
flush();
|
|
2518
|
+
emitted.push("");
|
|
2519
|
+
blankRun = 1;
|
|
2520
|
+
}
|
|
2521
|
+
continue;
|
|
2522
|
+
}
|
|
2523
|
+
if (li === 0 && line.startsWith("#!")) {
|
|
2524
|
+
flush();
|
|
2525
|
+
emitted.push(line);
|
|
2526
|
+
blankRun = 0;
|
|
2527
|
+
continue;
|
|
2528
|
+
}
|
|
2529
|
+
const m = line.match(/^\s*("""|''')/);
|
|
2530
|
+
if (m) {
|
|
2531
|
+
flush();
|
|
2532
|
+
const q = m[1];
|
|
2533
|
+
const opensHere = line.indexOf(q);
|
|
2534
|
+
const closeOnSameLine = findTripleClose(line.slice(opensHere + 3), q);
|
|
2535
|
+
if (closeOnSameLine !== -1) {
|
|
2536
|
+
const restAfter = line.slice(opensHere + 3 + closeOnSameLine + 3).trim();
|
|
2537
|
+
if (restAfter.length > 0) {
|
|
2538
|
+
emitted.push(line);
|
|
2539
|
+
classifySig(restAfter);
|
|
2540
|
+
} else if (atModuleStart || docstringSlot) {
|
|
2541
|
+
pending++;
|
|
2542
|
+
atModuleStart = false;
|
|
2543
|
+
docstringSlot = false;
|
|
2544
|
+
pendingDefClass = false;
|
|
2545
|
+
} else {
|
|
2546
|
+
emitted.push(line);
|
|
2547
|
+
}
|
|
2548
|
+
blankRun = 0;
|
|
2549
|
+
continue;
|
|
2550
|
+
}
|
|
2551
|
+
state = "triple";
|
|
2552
|
+
quote = q;
|
|
2553
|
+
isDocstring = atModuleStart || docstringSlot;
|
|
2554
|
+
atModuleStart = false;
|
|
2555
|
+
docstringSlot = false;
|
|
2556
|
+
if (!isDocstring) emitted.push(line);
|
|
2557
|
+
blankRun = 0;
|
|
2558
|
+
continue;
|
|
2559
|
+
}
|
|
2560
|
+
const probe = findCommentOrTriple(line);
|
|
2561
|
+
if (probe.kind === "comment-full") {
|
|
2562
|
+
pending++;
|
|
2563
|
+
continue;
|
|
2564
|
+
}
|
|
2565
|
+
if (probe.kind === "triple-mid") {
|
|
2566
|
+
flush();
|
|
2567
|
+
state = "triple";
|
|
2568
|
+
quote = probe.quote;
|
|
2569
|
+
isDocstring = false;
|
|
2570
|
+
emitted.push(line);
|
|
2571
|
+
classifySig(line.slice(0, probe.idx));
|
|
2572
|
+
blankRun = 0;
|
|
2573
|
+
continue;
|
|
2574
|
+
}
|
|
2575
|
+
flush();
|
|
2576
|
+
emitted.push(line);
|
|
2577
|
+
classifySig(line);
|
|
2578
|
+
blankRun = 0;
|
|
2579
|
+
}
|
|
2580
|
+
if (state === "triple") return null;
|
|
2581
|
+
flush();
|
|
2582
|
+
return finalizeTrimmed(text, emitted);
|
|
2583
|
+
}
|
|
2584
|
+
function findTripleClose(line, quote) {
|
|
2585
|
+
let i = 0;
|
|
2586
|
+
while (i < line.length) {
|
|
2587
|
+
if (line[i] === "\\") {
|
|
2588
|
+
i += 2;
|
|
2589
|
+
continue;
|
|
2590
|
+
}
|
|
2591
|
+
if (line.startsWith(quote, i)) return i;
|
|
2592
|
+
i++;
|
|
2593
|
+
}
|
|
2594
|
+
return -1;
|
|
2595
|
+
}
|
|
2596
|
+
function findCommentOrTriple(line) {
|
|
2597
|
+
let i = 0;
|
|
2598
|
+
let inStr = null;
|
|
2599
|
+
while (i < line.length) {
|
|
2600
|
+
const c = line[i];
|
|
2601
|
+
if (inStr) {
|
|
2602
|
+
if (c === "\\") i += 2;
|
|
2603
|
+
else if (c === inStr) inStr = null;
|
|
2604
|
+
else i++;
|
|
2605
|
+
continue;
|
|
2606
|
+
}
|
|
2607
|
+
if (c === "'" || c === '"') {
|
|
2608
|
+
inStr = c;
|
|
2609
|
+
i++;
|
|
2610
|
+
continue;
|
|
2611
|
+
}
|
|
2612
|
+
if (c === "#") {
|
|
2613
|
+
return {
|
|
2614
|
+
kind: line.slice(0, i).trim().length === 0 ? "comment-full" : "none",
|
|
2615
|
+
idx: i,
|
|
2616
|
+
quote: ""
|
|
2617
|
+
};
|
|
2618
|
+
}
|
|
2619
|
+
if (line.startsWith('"""', i) || line.startsWith("'''", i)) {
|
|
2620
|
+
return { kind: "triple-mid", idx: i, quote: line.slice(i, i + 3) };
|
|
2621
|
+
}
|
|
2622
|
+
i++;
|
|
2623
|
+
}
|
|
2624
|
+
return { kind: "none", idx: -1, quote: "" };
|
|
2625
|
+
}
|
|
2626
|
+
function scanTemplateRest(line, from, st) {
|
|
2627
|
+
let m = st.mode;
|
|
2628
|
+
let q = st.quote;
|
|
2629
|
+
let d = st.depth;
|
|
2630
|
+
let i = from;
|
|
2631
|
+
while (i < line.length) {
|
|
2632
|
+
const c = line[i];
|
|
2633
|
+
if (m === "str") {
|
|
2634
|
+
if (c === "\\") i += 2;
|
|
2635
|
+
else if (c === q) m = "expr";
|
|
2636
|
+
else i++;
|
|
2637
|
+
continue;
|
|
2638
|
+
}
|
|
2639
|
+
if (m === "expr") {
|
|
2640
|
+
if (c === "'" || c === '"') {
|
|
2641
|
+
q = c;
|
|
2642
|
+
m = "str";
|
|
2643
|
+
i++;
|
|
2644
|
+
continue;
|
|
2645
|
+
}
|
|
2646
|
+
if (c === "`")
|
|
2647
|
+
return {
|
|
2648
|
+
mode: m,
|
|
2649
|
+
quote: q,
|
|
2650
|
+
depth: d,
|
|
2651
|
+
closed: false,
|
|
2652
|
+
bail: true,
|
|
2653
|
+
consumed: 0
|
|
2654
|
+
};
|
|
2655
|
+
if (c === "{") d++;
|
|
2656
|
+
else if (c === "}") {
|
|
2657
|
+
d--;
|
|
2658
|
+
if (d === 0) m = "tpl";
|
|
2659
|
+
}
|
|
2660
|
+
i++;
|
|
2661
|
+
continue;
|
|
2662
|
+
}
|
|
2663
|
+
if (c === "\\") {
|
|
2664
|
+
i += 2;
|
|
2665
|
+
continue;
|
|
2666
|
+
}
|
|
2667
|
+
if (c === "`")
|
|
2668
|
+
return {
|
|
2669
|
+
mode: "tpl",
|
|
2670
|
+
quote: "",
|
|
2671
|
+
depth: 0,
|
|
2672
|
+
closed: true,
|
|
2673
|
+
bail: false,
|
|
2674
|
+
consumed: i - from + 1
|
|
2675
|
+
};
|
|
2676
|
+
if (c === "$" && line[i + 1] === "{") {
|
|
2677
|
+
m = "expr";
|
|
2678
|
+
d = 1;
|
|
2679
|
+
i += 2;
|
|
2680
|
+
continue;
|
|
2681
|
+
}
|
|
2682
|
+
i++;
|
|
2683
|
+
}
|
|
2684
|
+
return {
|
|
2685
|
+
mode: m,
|
|
2686
|
+
quote: q,
|
|
2687
|
+
depth: d,
|
|
2688
|
+
closed: false,
|
|
2689
|
+
bail: false,
|
|
2690
|
+
consumed: 0
|
|
2691
|
+
};
|
|
2692
|
+
}
|
|
2693
|
+
function regexAllowedAfter(prev) {
|
|
2694
|
+
if (prev === null) return true;
|
|
2695
|
+
if (/[A-Za-z0-9_]/.test(prev)) return false;
|
|
2696
|
+
return !".'\"`) ]".includes(prev);
|
|
2697
|
+
}
|
|
2698
|
+
function prevSigChar(line, i) {
|
|
2699
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
2700
|
+
const c = line[j];
|
|
2701
|
+
if (c === " " || c === " ") continue;
|
|
2702
|
+
return c;
|
|
2703
|
+
}
|
|
2704
|
+
return null;
|
|
2705
|
+
}
|
|
2706
|
+
function scanRegexLiteral(line, i) {
|
|
2707
|
+
let j = i + 1;
|
|
2708
|
+
let inClass = false;
|
|
2709
|
+
while (j < line.length) {
|
|
2710
|
+
const c = line[j];
|
|
2711
|
+
if (c === "\\") {
|
|
2712
|
+
j += 2;
|
|
2713
|
+
continue;
|
|
2714
|
+
}
|
|
2715
|
+
if (inClass) {
|
|
2716
|
+
if (c === "]") inClass = false;
|
|
2717
|
+
} else if (c === "[") {
|
|
2718
|
+
inClass = true;
|
|
2719
|
+
} else if (c === "/") {
|
|
2720
|
+
let f = j + 1;
|
|
2721
|
+
while (f < line.length && /[a-z]/i.test(line[f])) f++;
|
|
2722
|
+
return f;
|
|
2723
|
+
}
|
|
2724
|
+
j++;
|
|
2725
|
+
}
|
|
2726
|
+
return null;
|
|
2727
|
+
}
|
|
2728
|
+
function scanJsCodeLine(line) {
|
|
2729
|
+
let i = 0;
|
|
2730
|
+
let inStr = null;
|
|
2731
|
+
while (i < line.length) {
|
|
2732
|
+
const c = line[i];
|
|
2733
|
+
if (inStr) {
|
|
2734
|
+
if (c === "\\") i += 2;
|
|
2735
|
+
else if (c === inStr) inStr = null;
|
|
2736
|
+
else i++;
|
|
2737
|
+
continue;
|
|
2738
|
+
}
|
|
2739
|
+
if (c === "'" || c === '"') {
|
|
2740
|
+
inStr = c;
|
|
2741
|
+
i++;
|
|
2742
|
+
continue;
|
|
2743
|
+
}
|
|
2744
|
+
if (c === "`") {
|
|
2745
|
+
const r = scanTemplateRest(line, i, { mode: "tpl", quote: "", depth: 0 });
|
|
2746
|
+
if (r.bail) return { kind: "code", bail: true };
|
|
2747
|
+
if (!r.closed)
|
|
2748
|
+
return {
|
|
2749
|
+
kind: "template-open",
|
|
2750
|
+
tmpl: { mode: r.mode, quote: r.quote, depth: r.depth }
|
|
2751
|
+
};
|
|
2752
|
+
i += r.consumed;
|
|
2753
|
+
continue;
|
|
2754
|
+
}
|
|
2755
|
+
if (c === "/") {
|
|
2756
|
+
const nxt = line[i + 1];
|
|
2757
|
+
if (nxt === "/") {
|
|
2758
|
+
return {
|
|
2759
|
+
kind: line.slice(0, i).trim().length === 0 ? "comment-full" : "code"
|
|
2760
|
+
};
|
|
2761
|
+
}
|
|
2762
|
+
if (nxt !== "*") {
|
|
2763
|
+
if (regexAllowedAfter(prevSigChar(line, i))) {
|
|
2764
|
+
const end = scanRegexLiteral(line, i);
|
|
2765
|
+
if (end !== null) {
|
|
2766
|
+
i = end;
|
|
2767
|
+
continue;
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
i++;
|
|
2771
|
+
continue;
|
|
2772
|
+
}
|
|
2773
|
+
const close = line.indexOf("*/", i + 2);
|
|
2774
|
+
const before = line.slice(0, i).trim().length > 0;
|
|
2775
|
+
if (close === -1)
|
|
2776
|
+
return { kind: before ? "block-open-mid" : "block-open-only" };
|
|
2777
|
+
const rest = line.slice(close + 2).trim();
|
|
2778
|
+
if (rest.length === 0) return { kind: before ? "code" : "comment-full" };
|
|
2779
|
+
i = close + 2;
|
|
2780
|
+
continue;
|
|
2781
|
+
}
|
|
2782
|
+
i++;
|
|
2783
|
+
}
|
|
2784
|
+
return { kind: "code" };
|
|
2785
|
+
}
|
|
2786
|
+
function trimJsTs(text) {
|
|
2787
|
+
const lines = text.split(/\r?\n/);
|
|
2788
|
+
const emitted = [];
|
|
2789
|
+
let pending = 0;
|
|
2790
|
+
const flush = () => {
|
|
2791
|
+
if (pending > 0) {
|
|
2792
|
+
emitted.push(
|
|
2793
|
+
`// [acp-crush: elided ${pending} line${pending === 1 ? "" : "s"}]`
|
|
2794
|
+
);
|
|
2795
|
+
pending = 0;
|
|
2796
|
+
}
|
|
2797
|
+
};
|
|
2798
|
+
let state = "code";
|
|
2799
|
+
let tmpl = { mode: "tpl", quote: "", depth: 0 };
|
|
2800
|
+
let blankRun = 0;
|
|
2801
|
+
for (const line of lines) {
|
|
2802
|
+
if (state === "block") {
|
|
2803
|
+
const close = line.indexOf("*/");
|
|
2804
|
+
if (close === -1) {
|
|
2805
|
+
pending++;
|
|
2806
|
+
continue;
|
|
2807
|
+
}
|
|
2808
|
+
state = "code";
|
|
2809
|
+
if (line.slice(close + 2).trim().length === 0) pending++;
|
|
2810
|
+
else {
|
|
2811
|
+
flush();
|
|
2812
|
+
emitted.push(line);
|
|
2813
|
+
}
|
|
2814
|
+
blankRun = 0;
|
|
2815
|
+
continue;
|
|
2816
|
+
}
|
|
2817
|
+
if (state === "template") {
|
|
2818
|
+
const r = scanTemplateRest(line, 0, tmpl);
|
|
2819
|
+
if (r.bail) return null;
|
|
2820
|
+
tmpl = { mode: r.mode, quote: r.quote, depth: r.depth };
|
|
2821
|
+
if (r.closed) state = "code";
|
|
2822
|
+
emitted.push(line);
|
|
2823
|
+
blankRun = 0;
|
|
2824
|
+
continue;
|
|
2825
|
+
}
|
|
2826
|
+
if (line.trim().length === 0) {
|
|
2827
|
+
blankRun++;
|
|
2828
|
+
if (blankRun >= 3) {
|
|
2829
|
+
flush();
|
|
2830
|
+
emitted.push("");
|
|
2831
|
+
blankRun = 1;
|
|
2832
|
+
}
|
|
2833
|
+
continue;
|
|
2834
|
+
}
|
|
2835
|
+
const scan = scanJsCodeLine(line);
|
|
2836
|
+
if (scan.bail) return null;
|
|
2837
|
+
if (scan.kind === "comment-full") {
|
|
2838
|
+
pending++;
|
|
2839
|
+
continue;
|
|
2840
|
+
}
|
|
2841
|
+
if (scan.kind === "block-open-only") {
|
|
2842
|
+
pending++;
|
|
2843
|
+
state = "block";
|
|
2844
|
+
continue;
|
|
2845
|
+
}
|
|
2846
|
+
if (scan.kind === "block-open-mid") {
|
|
2847
|
+
flush();
|
|
2848
|
+
state = "block";
|
|
2849
|
+
emitted.push(line);
|
|
2850
|
+
blankRun = 0;
|
|
2851
|
+
continue;
|
|
2852
|
+
}
|
|
2853
|
+
if (scan.kind === "template-open") {
|
|
2854
|
+
flush();
|
|
2855
|
+
state = "template";
|
|
2856
|
+
tmpl = scan.tmpl ?? { mode: "tpl", quote: "", depth: 0 };
|
|
2857
|
+
emitted.push(line);
|
|
2858
|
+
blankRun = 0;
|
|
2859
|
+
continue;
|
|
2860
|
+
}
|
|
2861
|
+
flush();
|
|
2862
|
+
emitted.push(line);
|
|
2863
|
+
blankRun = 0;
|
|
2864
|
+
}
|
|
2865
|
+
if (state !== "code") return null;
|
|
2866
|
+
flush();
|
|
2867
|
+
return finalizeTrimmed(text, emitted);
|
|
2868
|
+
}
|
|
2869
|
+
function crushCode(text) {
|
|
2870
|
+
const lang = detectLanguage(text);
|
|
2871
|
+
if (lang === null) return null;
|
|
2872
|
+
if (lang === "python") return trimPython(text);
|
|
2873
|
+
return trimJsTs(text);
|
|
2874
|
+
}
|
|
2875
|
+
var LOG_MIN_LINES = 50;
|
|
2876
|
+
var LOG_MAX_TOTAL_LINES = 100;
|
|
2877
|
+
var LOG_MAX_ERRORS = 20;
|
|
2878
|
+
var LOG_ERROR_CONTEXT = 3;
|
|
2879
|
+
var LOG_MAX_WARNINGS = 5;
|
|
2880
|
+
var LOG_MAX_STACK_TRACES = 3;
|
|
2881
|
+
var LOG_STACK_MAX_LINES = 20;
|
|
2882
|
+
var LOG_TRACE_HEAD_FRAMES = 3;
|
|
2883
|
+
var LOG_TRACE_APP_FRAMES = 5;
|
|
2884
|
+
var LOG_CLASSIFIED_GATE = 5;
|
|
2885
|
+
var LOG_SUMMARY_GATE = 3;
|
|
2886
|
+
var LEVEL_PATTERNS = [
|
|
2887
|
+
["error", /\b(ERROR|FATAL|CRITICAL)\b/i],
|
|
2888
|
+
["fail", /\b(FAIL|FAILED)\b/i],
|
|
2889
|
+
["warn", /\b(WARN|WARNING)\b/i],
|
|
2890
|
+
["info", /\bINFO\b/i],
|
|
2891
|
+
["debug", /\bDEBUG\b/i],
|
|
2892
|
+
["trace", /\bTRACE\b/i]
|
|
2893
|
+
];
|
|
2894
|
+
function classifyLevel(line) {
|
|
2895
|
+
for (const [level, re] of LEVEL_PATTERNS) {
|
|
2896
|
+
if (re.test(line)) return level;
|
|
2897
|
+
}
|
|
2898
|
+
return "unknown";
|
|
2899
|
+
}
|
|
2900
|
+
function isLogSummaryLine(line) {
|
|
2901
|
+
if (line.startsWith("===") || line.startsWith("---")) return true;
|
|
2902
|
+
let d = 0;
|
|
2903
|
+
while (d < line.length && line.charCodeAt(d) >= 48 && line.charCodeAt(d) <= 57)
|
|
2904
|
+
d++;
|
|
2905
|
+
if (d > 0 && line[d] === " ") {
|
|
2906
|
+
const rest = line.slice(d + 1);
|
|
2907
|
+
if (rest.startsWith("passed") || rest.startsWith("failed") || rest.startsWith("skipped") || rest.startsWith("error") || rest.startsWith("warning"))
|
|
2908
|
+
return true;
|
|
2909
|
+
}
|
|
2910
|
+
for (const prefix of [
|
|
2911
|
+
"Test ",
|
|
2912
|
+
"Tests ",
|
|
2913
|
+
"Tests:",
|
|
2914
|
+
"Test:",
|
|
2915
|
+
"Suite ",
|
|
2916
|
+
"Suites ",
|
|
2917
|
+
"Suites:",
|
|
2918
|
+
"Suite:"
|
|
2919
|
+
]) {
|
|
2920
|
+
if (line.startsWith(prefix)) {
|
|
2921
|
+
const m = line.slice(prefix.length).match(/\S/);
|
|
2922
|
+
if (m !== null && m[0].charCodeAt(0) >= 48 && m[0].charCodeAt(0) <= 57)
|
|
2923
|
+
return true;
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
if (line.startsWith("TOTAL") || line.startsWith("Total") || line.startsWith("Summary"))
|
|
2927
|
+
return true;
|
|
2928
|
+
for (const prefix of ["Build", "Compile", "Test"]) {
|
|
2929
|
+
if (line.startsWith(prefix) && (line.includes("succeeded") || line.includes("failed") || line.includes("complete")))
|
|
2930
|
+
return true;
|
|
2931
|
+
}
|
|
2932
|
+
return false;
|
|
2933
|
+
}
|
|
2934
|
+
function isDigitChar(c) {
|
|
2935
|
+
return c >= "0" && c <= "9";
|
|
2936
|
+
}
|
|
2937
|
+
function isAsciiAlnum(c) {
|
|
2938
|
+
const n = c.charCodeAt(0);
|
|
2939
|
+
return n >= 97 && n <= 122 || n >= 65 && n <= 90 || n >= 48 && n <= 57;
|
|
2940
|
+
}
|
|
2941
|
+
function hasLineColSuffix(s) {
|
|
2942
|
+
for (let i = 0; i + 1 < s.length; i++) {
|
|
2943
|
+
if (s[i] === ":" && isDigitChar(s[i + 1])) {
|
|
2944
|
+
let j = i + 1;
|
|
2945
|
+
while (j < s.length && isDigitChar(s[j])) j++;
|
|
2946
|
+
if (j + 1 < s.length && s[j] === ":" && isDigitChar(s[j + 1]))
|
|
2947
|
+
return true;
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
return false;
|
|
2951
|
+
}
|
|
2952
|
+
function isPythonFileFrame(s) {
|
|
2953
|
+
return s.startsWith('File "') && s.includes('", line ') && s.length > 0 && isDigitChar(s[s.length - 1]);
|
|
2954
|
+
}
|
|
2955
|
+
function isJsAtFrame(s) {
|
|
2956
|
+
return s.startsWith("at ") && s.includes("(") && s.includes(")") && hasLineColSuffix(s);
|
|
2957
|
+
}
|
|
2958
|
+
function isJavaAtFrame(s) {
|
|
2959
|
+
if (!s.startsWith("at ") || !s.includes("(")) return false;
|
|
2960
|
+
const open = s.indexOf("(");
|
|
2961
|
+
const body = s.slice(3, open);
|
|
2962
|
+
if (body.length === 0) return false;
|
|
2963
|
+
for (const c of body) {
|
|
2964
|
+
if (!(isAsciiAlnum(c) || c === "." || c === "_" || c === "$" || c === "/"))
|
|
2965
|
+
return false;
|
|
2966
|
+
}
|
|
2967
|
+
return true;
|
|
2968
|
+
}
|
|
2969
|
+
function isRustPanicOpener(s) {
|
|
2970
|
+
return s.startsWith("thread '") && s.includes("panicked at");
|
|
2971
|
+
}
|
|
2972
|
+
function isGoroutineHeader(line) {
|
|
2973
|
+
if (!line.startsWith("goroutine ")) return false;
|
|
2974
|
+
const rest = line.slice(10);
|
|
2975
|
+
let d = 0;
|
|
2976
|
+
while (d < rest.length && isDigitChar(rest[d])) d++;
|
|
2977
|
+
return d > 0 && rest.slice(d).startsWith(" [");
|
|
2978
|
+
}
|
|
2979
|
+
function isGoPanicOpener(line) {
|
|
2980
|
+
return line.startsWith("panic: ") || line.startsWith("fatal error: ") || isGoroutineHeader(line);
|
|
2981
|
+
}
|
|
2982
|
+
function isGoFileFrame(line) {
|
|
2983
|
+
return line.startsWith(" ") && line.includes(".go:") && line.includes(" +0x");
|
|
2984
|
+
}
|
|
2985
|
+
function isGoCallFrame(line) {
|
|
2986
|
+
if (line.startsWith("created by ")) return true;
|
|
2987
|
+
if (line.startsWith(" ") || line.startsWith(" ") || !line.endsWith(")"))
|
|
2988
|
+
return false;
|
|
2989
|
+
const open = line.indexOf("(");
|
|
2990
|
+
if (open === -1) return false;
|
|
2991
|
+
const symbol = line.slice(0, open);
|
|
2992
|
+
if (symbol.length === 0 || !symbol.includes(".")) return false;
|
|
2993
|
+
for (const c of symbol) {
|
|
2994
|
+
if (!(isAsciiAlnum(c) || c === "." || c === "_" || c === "/" || c === "*"))
|
|
2995
|
+
return false;
|
|
2996
|
+
}
|
|
2997
|
+
return true;
|
|
2998
|
+
}
|
|
2999
|
+
function isDotnetFrame(s) {
|
|
3000
|
+
return s.startsWith("at ") && s.includes(") in ") && s.includes(":line ");
|
|
3001
|
+
}
|
|
3002
|
+
function isDotnetExceptionHead(trimmed) {
|
|
3003
|
+
const colon = trimmed.indexOf(":");
|
|
3004
|
+
if (colon === -1) return false;
|
|
3005
|
+
const head = trimmed.slice(0, colon);
|
|
3006
|
+
if (!head.endsWith("Exception") || !head.includes(".")) return false;
|
|
3007
|
+
for (const c of head) {
|
|
3008
|
+
if (!(isAsciiAlnum(c) || c === "." || c === "_" || c === "`" || c === "+"))
|
|
3009
|
+
return false;
|
|
3010
|
+
}
|
|
3011
|
+
return true;
|
|
3012
|
+
}
|
|
3013
|
+
function isRustBacktraceFrame(s) {
|
|
3014
|
+
const t = s.trimStart();
|
|
3015
|
+
let i = 0;
|
|
3016
|
+
while (i < t.length && isDigitChar(t[i])) i++;
|
|
3017
|
+
if (i === 0 || t[i] !== ":") return false;
|
|
3018
|
+
i++;
|
|
3019
|
+
while (i < t.length && t[i] === " ") i++;
|
|
3020
|
+
const rest = t.slice(i);
|
|
3021
|
+
if (!rest.startsWith("0x")) return false;
|
|
3022
|
+
let h = 0;
|
|
3023
|
+
for (const c of rest.slice(2)) {
|
|
3024
|
+
if (c >= "0" && c <= "9" || c >= "a" && c <= "f" || c >= "A" && c <= "F")
|
|
3025
|
+
h++;
|
|
3026
|
+
else break;
|
|
3027
|
+
}
|
|
3028
|
+
return h > 0;
|
|
3029
|
+
}
|
|
3030
|
+
function isJavaMoreSummary(trimmed) {
|
|
3031
|
+
if (!trimmed.startsWith("... ")) return false;
|
|
3032
|
+
const rest = trimmed.slice(4);
|
|
3033
|
+
let d = 0;
|
|
3034
|
+
while (d < rest.length && isDigitChar(rest[d])) d++;
|
|
3035
|
+
return d > 0 && rest.slice(d).trim() === "more";
|
|
3036
|
+
}
|
|
3037
|
+
function traceFlavorFor(line) {
|
|
3038
|
+
const t = line.trimStart();
|
|
3039
|
+
if (t.startsWith("Traceback (most recent call last)") || isPythonFileFrame(t))
|
|
3040
|
+
return "py";
|
|
3041
|
+
if (t.startsWith("Unhandled exception.") || isDotnetFrame(t)) return "dotnet";
|
|
3042
|
+
if (isJsAtFrame(t)) return "js";
|
|
3043
|
+
if (isJavaAtFrame(t)) return "java";
|
|
3044
|
+
if (t.startsWith("--> ") && hasLineColSuffix(t)) return "rust-error";
|
|
3045
|
+
if (isRustPanicOpener(t) || t.startsWith("stack backtrace:") || isRustBacktraceFrame(line))
|
|
3046
|
+
return "rust-backtrace";
|
|
3047
|
+
if (isGoPanicOpener(line)) return "go-panic";
|
|
3048
|
+
return null;
|
|
3049
|
+
}
|
|
3050
|
+
function traceTerminates(flavor, line, linesSoFar) {
|
|
3051
|
+
const t = line.trimStart();
|
|
3052
|
+
switch (flavor) {
|
|
3053
|
+
case "py": {
|
|
3054
|
+
const indentedOrBlank = line.startsWith(" ") || line.startsWith(" ") || line.length === 0;
|
|
3055
|
+
const continuation = t.startsWith("Traceback") || t.startsWith("File ") || t.startsWith("During handling") || t.startsWith("The above exception");
|
|
3056
|
+
if (indentedOrBlank || continuation) return false;
|
|
3057
|
+
return !(t.length > 0 && t[0] >= "A" && t[0] <= "Z");
|
|
3058
|
+
}
|
|
3059
|
+
case "js":
|
|
3060
|
+
return !t.startsWith("at ") && line.length !== 0;
|
|
3061
|
+
case "java": {
|
|
3062
|
+
const chain = t.startsWith("Caused by:") || t.startsWith("Suppressed:") || isJavaMoreSummary(t);
|
|
3063
|
+
return !t.startsWith("at ") && !chain && line.length !== 0;
|
|
3064
|
+
}
|
|
3065
|
+
case "dotnet": {
|
|
3066
|
+
if (line.length === 0) return false;
|
|
3067
|
+
const continues = t.startsWith("at ") || t.startsWith("--->") || t.startsWith("--- End of") || isDotnetExceptionHead(t);
|
|
3068
|
+
return !continues;
|
|
3069
|
+
}
|
|
3070
|
+
case "rust-error":
|
|
3071
|
+
return !t.startsWith("--> ") && line.length !== 0;
|
|
3072
|
+
case "rust-backtrace": {
|
|
3073
|
+
if (line.length === 0 || linesSoFar === 1) return false;
|
|
3074
|
+
const isFrame = t.length > 0 && isDigitChar(t[0]);
|
|
3075
|
+
const continuation = line.startsWith(" ") || line.startsWith(" ") || t.startsWith("stack backtrace:") || t.startsWith("note: run with");
|
|
3076
|
+
return !isFrame && !continuation;
|
|
3077
|
+
}
|
|
3078
|
+
case "go-panic": {
|
|
3079
|
+
if (line.length === 0) return false;
|
|
3080
|
+
const continues = line.startsWith(" ") || isGoroutineHeader(line) || isGoCallFrame(line) || line.startsWith("panic: ") || line.startsWith("fatal error: ") || line.startsWith("[signal ");
|
|
3081
|
+
return !continues;
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
function scoreLogLine(l) {
|
|
3086
|
+
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;
|
|
3087
|
+
return Math.min(base + (l.isStack ? 0.3 : 0) + (l.isSummary ? 0.4 : 0), 1);
|
|
3088
|
+
}
|
|
3089
|
+
function parseLogLines(lines) {
|
|
3090
|
+
const out = new Array(lines.length);
|
|
3091
|
+
let active = null;
|
|
3092
|
+
let traceLines = 0;
|
|
3093
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3094
|
+
const line = lines[i];
|
|
3095
|
+
const entry = {
|
|
3096
|
+
i,
|
|
3097
|
+
content: line,
|
|
3098
|
+
level: classifyLevel(line),
|
|
3099
|
+
isStack: false,
|
|
3100
|
+
isSummary: isLogSummaryLine(line),
|
|
3101
|
+
score: 0
|
|
3102
|
+
};
|
|
3103
|
+
if (active !== null) {
|
|
3104
|
+
const flavor = active;
|
|
3105
|
+
if (traceLines >= LOG_STACK_MAX_LINES || traceTerminates(flavor, line, traceLines)) {
|
|
3106
|
+
const capHit = traceLines >= LOG_STACK_MAX_LINES;
|
|
3107
|
+
active = null;
|
|
3108
|
+
traceLines = 0;
|
|
3109
|
+
const nf = traceFlavorFor(line);
|
|
3110
|
+
if (nf !== null) {
|
|
3111
|
+
active = nf;
|
|
3112
|
+
traceLines = 1;
|
|
3113
|
+
entry.isStack = true;
|
|
3114
|
+
} else if (capHit && !traceTerminates(flavor, line, 2)) {
|
|
3115
|
+
active = flavor;
|
|
3116
|
+
traceLines = 1;
|
|
3117
|
+
entry.isStack = true;
|
|
3118
|
+
}
|
|
3119
|
+
} else {
|
|
3120
|
+
entry.isStack = true;
|
|
3121
|
+
traceLines++;
|
|
3122
|
+
}
|
|
3123
|
+
} else {
|
|
3124
|
+
const f = traceFlavorFor(line);
|
|
3125
|
+
if (f !== null) {
|
|
3126
|
+
active = f;
|
|
3127
|
+
traceLines = 1;
|
|
3128
|
+
entry.isStack = true;
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
entry.score = scoreLogLine(entry);
|
|
3132
|
+
out[i] = entry;
|
|
3133
|
+
}
|
|
3134
|
+
return out;
|
|
3135
|
+
}
|
|
3136
|
+
function selectWithFirstLast(arr, maxCount) {
|
|
3137
|
+
if (arr.length <= maxCount) return arr.slice();
|
|
3138
|
+
const out = [];
|
|
3139
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3140
|
+
const push = (l) => {
|
|
3141
|
+
if (seen.add(l.i)) out.push(l);
|
|
3142
|
+
};
|
|
3143
|
+
push(arr[0]);
|
|
3144
|
+
push(arr[arr.length - 1]);
|
|
3145
|
+
if (out.length < maxCount) {
|
|
3146
|
+
const byScore = arr.slice().sort((a, b) => b.score - a.score || a.i - b.i);
|
|
3147
|
+
for (const l of byScore) {
|
|
3148
|
+
if (!seen.has(l.i)) {
|
|
3149
|
+
push(l);
|
|
3150
|
+
if (out.length >= maxCount) break;
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
return out;
|
|
3155
|
+
}
|
|
3156
|
+
function normalizeForDedupe(content) {
|
|
3157
|
+
let splitAt = content.length;
|
|
3158
|
+
for (let i = 0; i < content.length; i++) {
|
|
3159
|
+
if (content[i] === ":" || content[i] === "=") {
|
|
3160
|
+
splitAt = i;
|
|
3161
|
+
break;
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
const suffix = content.slice(splitAt).replace(/\d+/g, "N").replace(/0x[0-9a-fA-F]+/g, "ADDR").replace(/\/[\w/]+\//g, "/PATH/");
|
|
3165
|
+
return content.slice(0, splitAt) + suffix;
|
|
3166
|
+
}
|
|
3167
|
+
var RUNTIME_FRAME_PREFIXES = [
|
|
3168
|
+
"at java.",
|
|
3169
|
+
"at jdk.",
|
|
3170
|
+
"at sun.",
|
|
3171
|
+
"at javax.",
|
|
3172
|
+
"at scala.",
|
|
3173
|
+
"at System.",
|
|
3174
|
+
"at Microsoft.",
|
|
3175
|
+
"runtime.",
|
|
3176
|
+
"created by runtime."
|
|
3177
|
+
];
|
|
3178
|
+
var RUNTIME_FRAME_MARKERS = [
|
|
3179
|
+
"site-packages/",
|
|
3180
|
+
"/usr/lib/python",
|
|
3181
|
+
"lib/python3.",
|
|
3182
|
+
"node:internal/",
|
|
3183
|
+
"node_modules/",
|
|
3184
|
+
"(internal/",
|
|
3185
|
+
"core::",
|
|
3186
|
+
"std::",
|
|
3187
|
+
"alloc::",
|
|
3188
|
+
"rust_begin_unwind",
|
|
3189
|
+
"__rust_",
|
|
3190
|
+
"/rustc/",
|
|
3191
|
+
"/usr/local/go/src/",
|
|
3192
|
+
"/libexec/src/runtime/"
|
|
3193
|
+
];
|
|
3194
|
+
function isFrameLine(content) {
|
|
3195
|
+
const t = content.trimStart();
|
|
3196
|
+
return t.startsWith("at ") || t.startsWith('File "') && t.includes('", line ') || isRustBacktraceFrame(content) || isGoFileFrame(content) || isGoCallFrame(content);
|
|
3197
|
+
}
|
|
3198
|
+
function isChainHeadLine(content) {
|
|
3199
|
+
const t = content.trimStart();
|
|
3200
|
+
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");
|
|
3201
|
+
}
|
|
3202
|
+
function isRuntimeFrame(content) {
|
|
3203
|
+
const t = content.trimStart();
|
|
3204
|
+
return RUNTIME_FRAME_PREFIXES.some((p) => t.startsWith(p)) || RUNTIME_FRAME_MARKERS.some((m) => content.includes(m));
|
|
3205
|
+
}
|
|
3206
|
+
function collapseTraceFrames(stack, headFrames, appFrames) {
|
|
3207
|
+
const kept = [];
|
|
3208
|
+
const dropped = /* @__PURE__ */ new Set();
|
|
3209
|
+
let framesSeen = 0;
|
|
3210
|
+
let appKept = 0;
|
|
3211
|
+
let runStart = -1;
|
|
3212
|
+
let runLen = 0;
|
|
3213
|
+
let prevDropped = false;
|
|
3214
|
+
const flushRun = () => {
|
|
3215
|
+
if (runStart !== -1) {
|
|
3216
|
+
kept.push({
|
|
3217
|
+
i: runStart,
|
|
3218
|
+
content: ` [... ${runLen} frames collapsed]`,
|
|
3219
|
+
level: "unknown",
|
|
3220
|
+
isStack: true,
|
|
3221
|
+
isSummary: false,
|
|
3222
|
+
score: 0.8
|
|
3223
|
+
});
|
|
3224
|
+
runStart = -1;
|
|
3225
|
+
runLen = 0;
|
|
3226
|
+
}
|
|
3227
|
+
};
|
|
3228
|
+
for (const line of stack) {
|
|
3229
|
+
if (isFrameLine(line.content) && !isChainHeadLine(line.content)) {
|
|
3230
|
+
framesSeen++;
|
|
3231
|
+
const runtime = isRuntimeFrame(line.content);
|
|
3232
|
+
const keep = framesSeen <= headFrames || !runtime && appKept < appFrames;
|
|
3233
|
+
if (keep) {
|
|
3234
|
+
if (!runtime) appKept++;
|
|
3235
|
+
flushRun();
|
|
3236
|
+
kept.push(line);
|
|
3237
|
+
prevDropped = false;
|
|
3238
|
+
} else {
|
|
3239
|
+
if (runStart === -1) runStart = line.i;
|
|
3240
|
+
runLen++;
|
|
3241
|
+
dropped.add(line.i);
|
|
3242
|
+
prevDropped = true;
|
|
3243
|
+
}
|
|
3244
|
+
} else if (prevDropped && (line.content.startsWith(" ") || line.content.startsWith(" ")) && !isChainHeadLine(line.content)) {
|
|
3245
|
+
runLen++;
|
|
3246
|
+
dropped.add(line.i);
|
|
3247
|
+
} else {
|
|
3248
|
+
flushRun();
|
|
3249
|
+
kept.push(line);
|
|
3250
|
+
prevDropped = false;
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
flushRun();
|
|
3254
|
+
return { kept, dropped };
|
|
3255
|
+
}
|
|
3256
|
+
function selectLogLines(all) {
|
|
3257
|
+
const errors = [];
|
|
3258
|
+
const fails = [];
|
|
3259
|
+
const warnings = [];
|
|
3260
|
+
const summaries = [];
|
|
3261
|
+
const stacks = [];
|
|
3262
|
+
let current = [];
|
|
3263
|
+
for (const l of all) {
|
|
3264
|
+
if (l.level === "error") errors.push(l);
|
|
3265
|
+
else if (l.level === "fail") fails.push(l);
|
|
3266
|
+
else if (l.level === "warn") warnings.push(l);
|
|
3267
|
+
if (l.isStack) current.push(l);
|
|
3268
|
+
else if (current.length > 0) {
|
|
3269
|
+
stacks.push(current);
|
|
3270
|
+
current = [];
|
|
3271
|
+
}
|
|
3272
|
+
if (l.isSummary) summaries.push(l);
|
|
3273
|
+
}
|
|
3274
|
+
if (current.length > 0) stacks.push(current);
|
|
3275
|
+
const selected = /* @__PURE__ */ new Map();
|
|
3276
|
+
for (const l of selectWithFirstLast(errors, LOG_MAX_ERRORS))
|
|
3277
|
+
selected.set(l.i, l);
|
|
3278
|
+
for (const l of selectWithFirstLast(fails, LOG_MAX_ERRORS))
|
|
3279
|
+
selected.set(l.i, l);
|
|
3280
|
+
const seenWarn = /* @__PURE__ */ new Set();
|
|
3281
|
+
const dedupedWarnings = [];
|
|
3282
|
+
for (const w of warnings) {
|
|
3283
|
+
const key = normalizeForDedupe(w.content);
|
|
3284
|
+
if (!seenWarn.has(key)) {
|
|
3285
|
+
seenWarn.add(key);
|
|
3286
|
+
dedupedWarnings.push(w);
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
for (const w of dedupedWarnings.slice(0, LOG_MAX_WARNINGS))
|
|
3290
|
+
selected.set(w.i, w);
|
|
3291
|
+
for (const stack of stacks.slice(0, LOG_MAX_STACK_TRACES)) {
|
|
3292
|
+
if (stack.length > LOG_STACK_MAX_LINES) {
|
|
3293
|
+
const collapsed = collapseTraceFrames(
|
|
3294
|
+
stack,
|
|
3295
|
+
LOG_TRACE_HEAD_FRAMES,
|
|
3296
|
+
LOG_TRACE_APP_FRAMES
|
|
3297
|
+
);
|
|
3298
|
+
for (const i of collapsed.dropped) selected.delete(i);
|
|
3299
|
+
for (const l of collapsed.kept.slice(0, LOG_STACK_MAX_LINES))
|
|
3300
|
+
selected.set(l.i, l);
|
|
3301
|
+
} else {
|
|
3302
|
+
for (const l of stack) selected.set(l.i, l);
|
|
3303
|
+
}
|
|
1684
3304
|
}
|
|
1685
|
-
const
|
|
1686
|
-
const
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
};
|
|
3305
|
+
for (const s of summaries) selected.set(s.i, s);
|
|
3306
|
+
for (const idx of Array.from(selected.keys())) {
|
|
3307
|
+
const lo = Math.max(0, idx - LOG_ERROR_CONTEXT);
|
|
3308
|
+
const hi = Math.min(all.length, idx + LOG_ERROR_CONTEXT + 1);
|
|
3309
|
+
for (let i = lo; i < hi; i++) {
|
|
3310
|
+
if (i !== idx && !selected.has(i)) selected.set(i, all[i]);
|
|
3311
|
+
}
|
|
1693
3312
|
}
|
|
1694
|
-
|
|
1695
|
-
|
|
3313
|
+
let ordered = Array.from(selected.values());
|
|
3314
|
+
if (ordered.length > LOG_MAX_TOTAL_LINES) {
|
|
3315
|
+
ordered.sort((a, b) => b.score - a.score || a.i - b.i);
|
|
3316
|
+
ordered = ordered.slice(0, LOG_MAX_TOTAL_LINES);
|
|
3317
|
+
ordered.sort((a, b) => a.i - b.i);
|
|
3318
|
+
}
|
|
3319
|
+
return ordered;
|
|
3320
|
+
}
|
|
3321
|
+
function formatLogOutput(selected, all) {
|
|
3322
|
+
const count = (lv) => all.reduce((n, l) => l.level === lv ? n + 1 : n, 0);
|
|
3323
|
+
const output = selected.map((l) => l.content);
|
|
3324
|
+
const omitted = all.length - selected.length;
|
|
3325
|
+
if (omitted > 0) {
|
|
3326
|
+
const parts = [];
|
|
3327
|
+
const e = count("error");
|
|
3328
|
+
const f = count("fail");
|
|
3329
|
+
const w = count("warn");
|
|
3330
|
+
const inf = count("info");
|
|
3331
|
+
if (e > 0) parts.push(`${e} ERROR`);
|
|
3332
|
+
if (f > 0) parts.push(`${f} FAIL`);
|
|
3333
|
+
if (w > 0) parts.push(`${w} WARN`);
|
|
3334
|
+
if (inf > 0) parts.push(`${inf} INFO`);
|
|
3335
|
+
if (parts.length > 0)
|
|
3336
|
+
output.push(`[${omitted} lines omitted: ${parts.join(", ")}]`);
|
|
3337
|
+
}
|
|
3338
|
+
return output.join("\n");
|
|
3339
|
+
}
|
|
3340
|
+
var LOG_FORMAT_TABLE = [
|
|
3341
|
+
[
|
|
3342
|
+
"pytest",
|
|
3343
|
+
[
|
|
3344
|
+
"=== FAILURES",
|
|
3345
|
+
"=== ERRORS",
|
|
3346
|
+
"=== test session",
|
|
3347
|
+
"=== short test summary",
|
|
3348
|
+
"PASSED [",
|
|
3349
|
+
"FAILED [",
|
|
3350
|
+
"ERROR [",
|
|
3351
|
+
"SKIPPED [",
|
|
3352
|
+
"collected "
|
|
3353
|
+
]
|
|
3354
|
+
],
|
|
3355
|
+
["npm", ["npm ERR!", "npm WARN", "npm info", "npm http"]],
|
|
3356
|
+
["cargo", ["Compiling ", "Finished ", "Running ", "warning: ", "error[E"]],
|
|
3357
|
+
["jest", ["PASS ", "FAIL ", "Test Suites:"]],
|
|
3358
|
+
["make", ["make[", "make:", "gcc ", "g++ ", "clang "]]
|
|
3359
|
+
];
|
|
3360
|
+
function detectLogFormat(lines) {
|
|
3361
|
+
const sample = lines.slice(0, 100);
|
|
3362
|
+
let best = "generic";
|
|
3363
|
+
let bestScore = 0;
|
|
3364
|
+
for (const [name, pats] of LOG_FORMAT_TABLE) {
|
|
3365
|
+
let score = 0;
|
|
3366
|
+
for (const line of sample) {
|
|
3367
|
+
if (pats.some((p) => line.includes(p))) score++;
|
|
3368
|
+
}
|
|
3369
|
+
if (score > bestScore) {
|
|
3370
|
+
bestScore = score;
|
|
3371
|
+
best = name;
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
return best;
|
|
3375
|
+
}
|
|
3376
|
+
function crushLog(text) {
|
|
3377
|
+
const lines = text.split(/\r?\n/);
|
|
3378
|
+
if (lines.length < LOG_MIN_LINES) return null;
|
|
3379
|
+
let classified = 0;
|
|
3380
|
+
let summaries = 0;
|
|
3381
|
+
let traces = 0;
|
|
3382
|
+
for (const line of lines) {
|
|
3383
|
+
const lv = classifyLevel(line);
|
|
3384
|
+
if (lv !== "unknown") classified++;
|
|
3385
|
+
if (isLogSummaryLine(line)) summaries++;
|
|
3386
|
+
if (traceFlavorFor(line) !== null) traces++;
|
|
3387
|
+
}
|
|
3388
|
+
if (!(classified >= LOG_CLASSIFIED_GATE || traces >= 1 || summaries >= LOG_SUMMARY_GATE || detectLogFormat(lines) !== "generic"))
|
|
3389
|
+
return null;
|
|
3390
|
+
const parsed = parseLogLines(lines);
|
|
3391
|
+
const selected = selectLogLines(parsed);
|
|
3392
|
+
if (selected.length >= lines.length) return null;
|
|
3393
|
+
const out = formatLogOutput(selected, parsed);
|
|
3394
|
+
if (out.length >= text.length) return null;
|
|
3395
|
+
return out;
|
|
3396
|
+
}
|
|
3397
|
+
function errorLinesSurvive(input, output) {
|
|
3398
|
+
const kept = new Set(
|
|
3399
|
+
output.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0)
|
|
1696
3400
|
);
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
};
|
|
3401
|
+
for (const line of input.split(/\r?\n/)) {
|
|
3402
|
+
const level = classifyLevel(line);
|
|
3403
|
+
if (level !== "error" && level !== "fail") continue;
|
|
3404
|
+
const t = line.trim();
|
|
3405
|
+
if (t.length > 0 && !kept.has(t)) return false;
|
|
1703
3406
|
}
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
3407
|
+
return true;
|
|
3408
|
+
}
|
|
3409
|
+
var jsonFoldPlugin = {
|
|
3410
|
+
id: "json-fold",
|
|
3411
|
+
kinds: ["json"],
|
|
3412
|
+
run(text) {
|
|
3413
|
+
const out = crushJson(text);
|
|
3414
|
+
return out ? { text: out, lossy: false } : null;
|
|
1711
3415
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
3416
|
+
};
|
|
3417
|
+
var codeTrimPlugin = {
|
|
3418
|
+
id: "code-trim",
|
|
3419
|
+
kinds: ["code"],
|
|
3420
|
+
run(text) {
|
|
3421
|
+
const out = crushCode(text);
|
|
3422
|
+
return out ? { text: out, lossy: true } : null;
|
|
1718
3423
|
}
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
3424
|
+
};
|
|
3425
|
+
var logSelectPlugin = {
|
|
3426
|
+
id: "log-select",
|
|
3427
|
+
kinds: ["log"],
|
|
3428
|
+
run(text) {
|
|
3429
|
+
const out = crushLog(text);
|
|
3430
|
+
return out ? { text: out, lossy: true } : null;
|
|
1725
3431
|
}
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
3432
|
+
};
|
|
3433
|
+
var builtInPlugins = [
|
|
3434
|
+
jsonFoldPlugin,
|
|
3435
|
+
codeTrimPlugin,
|
|
3436
|
+
logSelectPlugin
|
|
3437
|
+
];
|
|
3438
|
+
var crushRegistry = /* @__PURE__ */ new Map();
|
|
3439
|
+
for (const p of builtInPlugins) crushRegistry.set(p.id, p);
|
|
3440
|
+
function registerCrushPlugin(def) {
|
|
3441
|
+
crushRegistry.set(def.id, def);
|
|
3442
|
+
}
|
|
3443
|
+
function unregisterCrushPlugin(id) {
|
|
3444
|
+
crushRegistry.delete(id);
|
|
3445
|
+
}
|
|
3446
|
+
function listCrushPlugins() {
|
|
3447
|
+
return Array.from(crushRegistry.values()).map((p) => ({
|
|
3448
|
+
id: p.id,
|
|
3449
|
+
kinds: p.kinds
|
|
3450
|
+
}));
|
|
3451
|
+
}
|
|
3452
|
+
function resetCrushPlugins() {
|
|
3453
|
+
crushRegistry.clear();
|
|
3454
|
+
for (const p of builtInPlugins) crushRegistry.set(p.id, p);
|
|
3455
|
+
}
|
|
3456
|
+
function registeredPlugins() {
|
|
3457
|
+
return Array.from(crushRegistry.values());
|
|
3458
|
+
}
|
|
3459
|
+
function resolveCrushConfig(config) {
|
|
3460
|
+
return { ...DEFAULT_CRUSH_CONFIG, ...config.crush ?? {} };
|
|
3461
|
+
}
|
|
3462
|
+
function effectivePlugins(crush, meta) {
|
|
3463
|
+
const toolName = meta.toolName;
|
|
3464
|
+
return registeredPlugins().filter((p) => {
|
|
3465
|
+
const ov = crush.strategies?.[p.id];
|
|
3466
|
+
if (ov?.enabled === false) return false;
|
|
3467
|
+
if (toolName && ov?.excludeTools?.some((pat) => matchToolPattern(toolName, pat)))
|
|
3468
|
+
return false;
|
|
3469
|
+
return true;
|
|
3470
|
+
});
|
|
3471
|
+
}
|
|
3472
|
+
function evaluateToolResult(input) {
|
|
3473
|
+
const { absorb, crush, tokenCount, modelContextLimit, meta = {} } = input;
|
|
3474
|
+
const countTokens = input.countTokens ?? defaultCountTokens;
|
|
3475
|
+
const text = input.text;
|
|
3476
|
+
const rawTokens = countTokens(text);
|
|
3477
|
+
if (absorb.contextThresholdPct > 0 && modelContextLimit > 0 && tokenCount < absorb.contextThresholdPct * modelContextLimit) {
|
|
3478
|
+
return { kind: "skip", text, rawTokens };
|
|
1735
3479
|
}
|
|
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
|
-
};
|
|
3480
|
+
if (rawTokens < absorb.minToolTokens) {
|
|
3481
|
+
return { kind: "skip", text, rawTokens };
|
|
1742
3482
|
}
|
|
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.` : "";
|
|
3483
|
+
const out = crushText(text, {
|
|
3484
|
+
minReduction: crush.minReduction,
|
|
3485
|
+
countTokens,
|
|
3486
|
+
meta,
|
|
3487
|
+
plugins: effectivePlugins(crush, meta)
|
|
3488
|
+
});
|
|
3489
|
+
if (!out) return { kind: "distill", text, rawTokens };
|
|
3490
|
+
const newTokens = countTokens(out.text);
|
|
1766
3491
|
return {
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
3492
|
+
kind: newTokens < absorb.minToolTokens ? "crushed" : "distill",
|
|
3493
|
+
text: out.text,
|
|
3494
|
+
rawTokens,
|
|
3495
|
+
newTokens,
|
|
3496
|
+
reduction: (rawTokens - newTokens) / rawTokens,
|
|
3497
|
+
strategy: out.strategy,
|
|
3498
|
+
lossy: out.lossy
|
|
1770
3499
|
};
|
|
1771
3500
|
}
|
|
3501
|
+
function applyCrushToMessages(messages, config, tokenCount, countTokens) {
|
|
3502
|
+
const absorb = resolveAbsorbConfig(config);
|
|
3503
|
+
const crush = resolveCrushConfig(config);
|
|
3504
|
+
let crushedCount = 0;
|
|
3505
|
+
let distilledCount = 0;
|
|
3506
|
+
let changed = false;
|
|
3507
|
+
const out = [];
|
|
3508
|
+
for (const msg of messages) {
|
|
3509
|
+
if (!isAbsorbCandidate(msg, config)) {
|
|
3510
|
+
out.push(msg);
|
|
3511
|
+
continue;
|
|
3512
|
+
}
|
|
3513
|
+
const text = msg.text ?? "";
|
|
3514
|
+
const markerAt = text.indexOf(ABSORB_PROMPT_MARKER);
|
|
3515
|
+
const payload = markerAt >= 0 ? text.slice(0, markerAt) : text;
|
|
3516
|
+
const ev = evaluateToolResult({
|
|
3517
|
+
text: payload,
|
|
3518
|
+
absorb,
|
|
3519
|
+
crush,
|
|
3520
|
+
tokenCount,
|
|
3521
|
+
modelContextLimit: config.modelContextLimit,
|
|
3522
|
+
meta: { toolName: msg.toolName },
|
|
3523
|
+
countTokens
|
|
3524
|
+
});
|
|
3525
|
+
if (ev.kind === "skip") {
|
|
3526
|
+
out.push(msg);
|
|
3527
|
+
continue;
|
|
3528
|
+
}
|
|
3529
|
+
changed = true;
|
|
3530
|
+
if (ev.kind === "crushed") crushedCount++;
|
|
3531
|
+
else distilledCount++;
|
|
3532
|
+
out.push({ ...msg, text: ev.text });
|
|
3533
|
+
}
|
|
3534
|
+
return { messages: changed ? out : messages, crushedCount, distilledCount };
|
|
3535
|
+
}
|
|
1772
3536
|
|
|
1773
3537
|
// src/filter/registry.ts
|
|
1774
3538
|
var registry = /* @__PURE__ */ new Map();
|
|
@@ -2251,6 +4015,11 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
|
|
|
2251
4015
|
skipSinceProtected = true;
|
|
2252
4016
|
continue;
|
|
2253
4017
|
}
|
|
4018
|
+
if (hasMediaPayload(msg)) {
|
|
4019
|
+
skipSinceCompressible = true;
|
|
4020
|
+
skipSinceProtected = true;
|
|
4021
|
+
continue;
|
|
4022
|
+
}
|
|
2254
4023
|
if (isMessageProtectedWithPairing(msg, config, protectedCallIds) || isMessageLatestProtected(msg, latest)) {
|
|
2255
4024
|
protectedMsgs.push({
|
|
2256
4025
|
ref,
|
|
@@ -2684,10 +4453,12 @@ function createCore(ports = {}) {
|
|
|
2684
4453
|
`[acp-kernel] Config validation warnings: ${configErrors.join("; ")}. Thresholds may not fire correctly.`
|
|
2685
4454
|
);
|
|
2686
4455
|
}
|
|
4456
|
+
const contentStore = input.contentStore ?? createContentStore();
|
|
2687
4457
|
const ctx = {
|
|
2688
4458
|
config: input.config,
|
|
2689
4459
|
tokenCount: input.tokenCount,
|
|
2690
|
-
countTokens
|
|
4460
|
+
countTokens,
|
|
4461
|
+
contentStore
|
|
2691
4462
|
};
|
|
2692
4463
|
const initial = {
|
|
2693
4464
|
messages: input.messages,
|
|
@@ -2697,14 +4468,19 @@ function createCore(ports = {}) {
|
|
|
2697
4468
|
const strategy = input.renderTags ?? "all";
|
|
2698
4469
|
const nodes = buildNodes(strategy);
|
|
2699
4470
|
const result = runPipeline(nodes, initial, ctx);
|
|
4471
|
+
const ccrEffect = result.effects.ccr;
|
|
2700
4472
|
return {
|
|
2701
4473
|
messages: result.messages,
|
|
2702
4474
|
state: result.state,
|
|
2703
4475
|
nudge: result.effects.nudge,
|
|
2704
4476
|
terminalEscape: result.effects.terminalEscape,
|
|
2705
|
-
truncationSkipped: result.effects.truncationSkipped
|
|
4477
|
+
truncationSkipped: result.effects.truncationSkipped,
|
|
4478
|
+
contentStore: ccrEffect?.store ?? contentStore
|
|
2706
4479
|
};
|
|
2707
4480
|
}
|
|
4481
|
+
function retrieve(store, ref) {
|
|
4482
|
+
return applyRetrieve({ store, ref });
|
|
4483
|
+
}
|
|
2708
4484
|
function decompress(blockId, state) {
|
|
2709
4485
|
return blockById(state, blockId);
|
|
2710
4486
|
}
|
|
@@ -2724,7 +4500,12 @@ function createCore(ports = {}) {
|
|
|
2724
4500
|
activeBlocks: active.length,
|
|
2725
4501
|
totalBlocks: state.blocks.length,
|
|
2726
4502
|
tokensCompressed: state.stats.tokensCompressed,
|
|
2727
|
-
breakdown: {
|
|
4503
|
+
breakdown: {
|
|
4504
|
+
active: active.length,
|
|
4505
|
+
total: state.blocks.length,
|
|
4506
|
+
storedMessages: state.stats.storedCount ?? 0,
|
|
4507
|
+
retrievals: state.stats.retrievalCount ?? 0
|
|
4508
|
+
}
|
|
2728
4509
|
};
|
|
2729
4510
|
}
|
|
2730
4511
|
function defaultNodes() {
|
|
@@ -2735,7 +4516,9 @@ function createCore(ports = {}) {
|
|
|
2735
4516
|
assignRefsNode,
|
|
2736
4517
|
syncBlocksNode,
|
|
2737
4518
|
pruneNode,
|
|
4519
|
+
ccrStoreNode,
|
|
2738
4520
|
absorbHideNode,
|
|
4521
|
+
crushNode,
|
|
2739
4522
|
absorbPromptNode,
|
|
2740
4523
|
filterNode,
|
|
2741
4524
|
hideCompressCallsNode,
|
|
@@ -2748,6 +4531,7 @@ function createCore(ports = {}) {
|
|
|
2748
4531
|
}
|
|
2749
4532
|
return {
|
|
2750
4533
|
processTurn,
|
|
4534
|
+
retrieve,
|
|
2751
4535
|
applyCompression,
|
|
2752
4536
|
defaultNodes,
|
|
2753
4537
|
decompress,
|
|
@@ -2760,11 +4544,13 @@ var assignRefsNode = {
|
|
|
2760
4544
|
run(io, ctx) {
|
|
2761
4545
|
const hasProtection = ctx.config.protectedTools.length > 0 || !!ctx.config.isToolProtected || (ctx.config.protectedLatestTools?.length ?? 0) > 0;
|
|
2762
4546
|
const latest = hasProtection ? collectLatestProtected(io.messages, ctx.config) : void 0;
|
|
2763
|
-
const protectedFn =
|
|
4547
|
+
const protectedFn = (m) => hasMediaPayload(m) || (hasProtection ? isMessageProtected(m, ctx.config) || (latest ? isMessageLatestProtected(m, latest) : false) : false);
|
|
2764
4548
|
const refResult = assignRefs(io.messages, {
|
|
2765
4549
|
existing: io.state.messageRefs,
|
|
2766
4550
|
nextIndex: highestUsedIndex(io.state.messageRefs) + 1,
|
|
2767
|
-
isProtected: protectedFn
|
|
4551
|
+
isProtected: protectedFn,
|
|
4552
|
+
// Ephemeral retrieval injections never consume a ref slot.
|
|
4553
|
+
shouldSkip: (m) => m.id.startsWith(RETRIEVED_ID_PREFIX)
|
|
2768
4554
|
});
|
|
2769
4555
|
return { ...io, state: { ...io.state, messageRefs: refResult.map } };
|
|
2770
4556
|
}
|
|
@@ -2808,6 +4594,27 @@ var absorbPromptNode = {
|
|
|
2808
4594
|
};
|
|
2809
4595
|
}
|
|
2810
4596
|
};
|
|
4597
|
+
var crushNode = {
|
|
4598
|
+
name: "crush",
|
|
4599
|
+
enabled: (_io, ctx) => ctx.config.crush?.enabled === true && ctx.config.absorb?.enabled === true,
|
|
4600
|
+
run(io, ctx) {
|
|
4601
|
+
const applied = applyCrushToMessages(
|
|
4602
|
+
io.messages,
|
|
4603
|
+
ctx.config,
|
|
4604
|
+
ctx.tokenCount,
|
|
4605
|
+
ctx.countTokens
|
|
4606
|
+
);
|
|
4607
|
+
return {
|
|
4608
|
+
...io,
|
|
4609
|
+
messages: applied.messages,
|
|
4610
|
+
effects: {
|
|
4611
|
+
...io.effects,
|
|
4612
|
+
crushCount: applied.crushedCount,
|
|
4613
|
+
crushDistilledCount: applied.distilledCount
|
|
4614
|
+
}
|
|
4615
|
+
};
|
|
4616
|
+
}
|
|
4617
|
+
};
|
|
2811
4618
|
var filterNode = {
|
|
2812
4619
|
name: "filter",
|
|
2813
4620
|
enabled: (_io, ctx) => !!ctx.config.messageFilters?.enabled && listMessageFilters().length > 0,
|
|
@@ -3020,6 +4827,15 @@ function applySingleRange(input) {
|
|
|
3020
4827
|
if (!kept.has(id)) effectiveMessageIds.delete(id);
|
|
3021
4828
|
}
|
|
3022
4829
|
}
|
|
4830
|
+
const mediaExcluded = directMessageIds.filter((id) => {
|
|
4831
|
+
const msg = input.messages.find((m) => m.id === id);
|
|
4832
|
+
return !!msg && hasMediaPayload(msg);
|
|
4833
|
+
});
|
|
4834
|
+
if (mediaExcluded.length > 0) {
|
|
4835
|
+
warnings.push(
|
|
4836
|
+
`Excluded ${mediaExcluded.length} message(s) carrying image/attachment payload(s) from compression range \u2014 their bytes are unrecoverable once folded (billion-context#1188); enable stripImages to release old ones.`
|
|
4837
|
+
);
|
|
4838
|
+
}
|
|
3023
4839
|
const protectedRefs = input.protectedMessageIds;
|
|
3024
4840
|
const hitProtectedRaw = protectedRefs ? filteredIds.filter((id) => {
|
|
3025
4841
|
const ref = input.state.messageRefs.byRaw[id];
|
|
@@ -3185,7 +5001,7 @@ function filterProtectedToolMessages(directMessageIds, messages, config) {
|
|
|
3185
5001
|
for (const id of directMessageIds) {
|
|
3186
5002
|
const msg = messages.find((m) => m.id === id);
|
|
3187
5003
|
if (!msg) continue;
|
|
3188
|
-
if (isMessageProtected(msg, config) || isMessageLatestProtected(msg, latest)) {
|
|
5004
|
+
if (hasMediaPayload(msg) || isMessageProtected(msg, config) || isMessageLatestProtected(msg, latest)) {
|
|
3189
5005
|
removedIds.add(id);
|
|
3190
5006
|
if (msg.toolCallId) protectedCallIds.add(msg.toolCallId);
|
|
3191
5007
|
}
|
|
@@ -4744,6 +6560,294 @@ function formatRulesList(rules) {
|
|
|
4744
6560
|
return rules.map((rule, i) => `${i + 1}. [${rule.id}] ${rule.text}`).join("\n");
|
|
4745
6561
|
}
|
|
4746
6562
|
|
|
6563
|
+
// src/image-compress.ts
|
|
6564
|
+
var DEFAULT_IMAGE_COMPRESSION_CONFIG = {
|
|
6565
|
+
enabled: false,
|
|
6566
|
+
minTokens: 512,
|
|
6567
|
+
maxDimension: 1280,
|
|
6568
|
+
quality: 80,
|
|
6569
|
+
format: "webp"
|
|
6570
|
+
};
|
|
6571
|
+
function resolveImageCompressionConfig(config) {
|
|
6572
|
+
return { ...DEFAULT_IMAGE_COMPRESSION_CONFIG, ...config.imageCompression ?? {} };
|
|
6573
|
+
}
|
|
6574
|
+
var PIXEL_IMAGE_FALLBACK_TOKENS = 16384;
|
|
6575
|
+
var TILE_EDGE = 512;
|
|
6576
|
+
var TILE_SHORT_SIDE = 768;
|
|
6577
|
+
var TILE_MAX_EDGE = 2048;
|
|
6578
|
+
function byteAt(b, off) {
|
|
6579
|
+
return b[off] ?? 0;
|
|
6580
|
+
}
|
|
6581
|
+
function u16be(b, off) {
|
|
6582
|
+
return byteAt(b, off) << 8 | byteAt(b, off + 1);
|
|
6583
|
+
}
|
|
6584
|
+
function u16le(b, off) {
|
|
6585
|
+
return byteAt(b, off) | byteAt(b, off + 1) << 8;
|
|
6586
|
+
}
|
|
6587
|
+
function u32be(b, off) {
|
|
6588
|
+
return (byteAt(b, off) << 24 | byteAt(b, off + 1) << 16 | byteAt(b, off + 2) << 8 | byteAt(b, off + 3)) >>> 0;
|
|
6589
|
+
}
|
|
6590
|
+
function i32le(b, off) {
|
|
6591
|
+
return byteAt(b, off) | byteAt(b, off + 1) << 8 | byteAt(b, off + 2) << 16 | byteAt(b, off + 3) << 24;
|
|
6592
|
+
}
|
|
6593
|
+
function parseImageDimensions(bytes) {
|
|
6594
|
+
const b = bytes;
|
|
6595
|
+
if (b.length < 10) return void 0;
|
|
6596
|
+
if (b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71) {
|
|
6597
|
+
if (b.length < 24) return void 0;
|
|
6598
|
+
const w = u32be(b, 16);
|
|
6599
|
+
const h = u32be(b, 20);
|
|
6600
|
+
return w > 0 && h > 0 ? { width: w, height: h } : void 0;
|
|
6601
|
+
}
|
|
6602
|
+
if (b[0] === 71 && b[1] === 73 && b[2] === 70) {
|
|
6603
|
+
const w = u16le(b, 6);
|
|
6604
|
+
const h = u16le(b, 8);
|
|
6605
|
+
return w > 0 && h > 0 ? { width: w, height: h } : void 0;
|
|
6606
|
+
}
|
|
6607
|
+
if (b.length >= 26 && b[0] === 66 && b[1] === 77) {
|
|
6608
|
+
const w = i32le(b, 18);
|
|
6609
|
+
const h = Math.abs(i32le(b, 22));
|
|
6610
|
+
return w > 0 && h > 0 ? { width: w, height: h } : void 0;
|
|
6611
|
+
}
|
|
6612
|
+
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) {
|
|
6613
|
+
if (b[12] === 86 && b[13] === 80 && b[14] === 56 && b[15] === 88) {
|
|
6614
|
+
if (b.length < 30) return void 0;
|
|
6615
|
+
const w = byteAt(b, 24) | byteAt(b, 25) << 8 | byteAt(b, 26) << 16;
|
|
6616
|
+
const h = byteAt(b, 27) | byteAt(b, 28) << 8 | byteAt(b, 29) << 16;
|
|
6617
|
+
return w > 0 && h > 0 ? { width: w + 1, height: h + 1 } : void 0;
|
|
6618
|
+
}
|
|
6619
|
+
if (b[12] === 86 && b[13] === 80 && b[14] === 56 && b[15] === 76) {
|
|
6620
|
+
if (b.length < 25) return void 0;
|
|
6621
|
+
if (b[20] !== 47) return void 0;
|
|
6622
|
+
const w = ((byteAt(b, 22) & 63) << 8 | byteAt(b, 21)) + 1;
|
|
6623
|
+
const h = (byteAt(b, 24) & 15) << 10 | byteAt(b, 23) << 2 | (byteAt(b, 22) & 192) >> 6;
|
|
6624
|
+
return w > 0 && h > 0 ? { width: w, height: h + 1 } : void 0;
|
|
6625
|
+
}
|
|
6626
|
+
if (b[12] === 86 && b[13] === 80 && b[14] === 56 && b[15] === 32) {
|
|
6627
|
+
if (b.length < 30) return void 0;
|
|
6628
|
+
if (b[23] !== 157 || b[24] !== 1 || b[25] !== 42) return void 0;
|
|
6629
|
+
const w = u16le(b, 26) & 16383;
|
|
6630
|
+
const h = u16le(b, 28) & 16383;
|
|
6631
|
+
return w > 0 && h > 0 ? { width: w, height: h } : void 0;
|
|
6632
|
+
}
|
|
6633
|
+
return void 0;
|
|
6634
|
+
}
|
|
6635
|
+
if (b[0] === 255 && b[1] === 216) {
|
|
6636
|
+
let off = 2;
|
|
6637
|
+
while (off + 4 <= b.length) {
|
|
6638
|
+
if (b[off] !== 255) {
|
|
6639
|
+
off += 1;
|
|
6640
|
+
continue;
|
|
6641
|
+
}
|
|
6642
|
+
const marker = byteAt(b, off + 1);
|
|
6643
|
+
if (marker === 216 || marker === 1 || marker >= 208 && marker <= 217) {
|
|
6644
|
+
off += 2;
|
|
6645
|
+
continue;
|
|
6646
|
+
}
|
|
6647
|
+
if (marker === 218) break;
|
|
6648
|
+
const segLen = u16be(b, off + 2);
|
|
6649
|
+
if (segLen < 2) break;
|
|
6650
|
+
if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) {
|
|
6651
|
+
if (off + 9 > b.length) break;
|
|
6652
|
+
const h = u16be(b, off + 5);
|
|
6653
|
+
const w = u16be(b, off + 7);
|
|
6654
|
+
if (w > 0 && h > 0) return { width: w, height: h };
|
|
6655
|
+
break;
|
|
6656
|
+
}
|
|
6657
|
+
off += 2 + segLen;
|
|
6658
|
+
}
|
|
6659
|
+
}
|
|
6660
|
+
return void 0;
|
|
6661
|
+
}
|
|
6662
|
+
var HEADER_SCAN_CHARS = 64;
|
|
6663
|
+
var JPEG_SCAN_CHARS = 35e4;
|
|
6664
|
+
function parseImageDimensionsFromBase64(b64) {
|
|
6665
|
+
if (typeof b64 !== "string" || b64.length === 0) return void 0;
|
|
6666
|
+
const head = parseImageDimensions(Buffer.from(b64.slice(0, HEADER_SCAN_CHARS), "base64"));
|
|
6667
|
+
if (head) return head;
|
|
6668
|
+
if (b64.startsWith("/9j/") && b64.length > HEADER_SCAN_CHARS) {
|
|
6669
|
+
return parseImageDimensions(Buffer.from(b64.slice(0, JPEG_SCAN_CHARS), "base64"));
|
|
6670
|
+
}
|
|
6671
|
+
return void 0;
|
|
6672
|
+
}
|
|
6673
|
+
function pixelTileEstimate(width, height) {
|
|
6674
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
6675
|
+
return PIXEL_IMAGE_FALLBACK_TOKENS;
|
|
6676
|
+
}
|
|
6677
|
+
let sw = width;
|
|
6678
|
+
let sh = height;
|
|
6679
|
+
const shortSide = Math.min(sw, sh);
|
|
6680
|
+
if (shortSide > 0 && shortSide < TILE_SHORT_SIDE) {
|
|
6681
|
+
const s = TILE_SHORT_SIDE / shortSide;
|
|
6682
|
+
sw *= s;
|
|
6683
|
+
sh *= s;
|
|
6684
|
+
}
|
|
6685
|
+
const longSide = Math.max(sw, sh);
|
|
6686
|
+
if (longSide > TILE_MAX_EDGE) {
|
|
6687
|
+
const s = TILE_MAX_EDGE / longSide;
|
|
6688
|
+
sw *= s;
|
|
6689
|
+
sh *= s;
|
|
6690
|
+
}
|
|
6691
|
+
const tiles = Math.ceil(sw / TILE_EDGE) * Math.ceil(sh / TILE_EDGE);
|
|
6692
|
+
return 85 + 170 * tiles;
|
|
6693
|
+
}
|
|
6694
|
+
function estimateImageTokens(meta) {
|
|
6695
|
+
const billing = meta.billing ?? "bytes";
|
|
6696
|
+
if (billing === "bytes") return Math.ceil(meta.base64Length / 4);
|
|
6697
|
+
const w = meta.width;
|
|
6698
|
+
const h = meta.height;
|
|
6699
|
+
if (w && h) return pixelTileEstimate(w, h);
|
|
6700
|
+
if (meta.base64) {
|
|
6701
|
+
const dims = parseImageDimensionsFromBase64(meta.base64);
|
|
6702
|
+
if (dims) return pixelTileEstimate(dims.width, dims.height);
|
|
6703
|
+
}
|
|
6704
|
+
return PIXEL_IMAGE_FALLBACK_TOKENS;
|
|
6705
|
+
}
|
|
6706
|
+
function createHeuristicClassifier(options = {}) {
|
|
6707
|
+
const aspectRatioMin = options.aspectRatioMin ?? 1.4;
|
|
6708
|
+
const aspectRatioMax = options.aspectRatioMax ?? 2.6;
|
|
6709
|
+
const minShortSide = options.minShortSide ?? 720;
|
|
6710
|
+
return {
|
|
6711
|
+
name: "heuristic-v1",
|
|
6712
|
+
isScreenshotLike(meta) {
|
|
6713
|
+
const w = meta.width;
|
|
6714
|
+
const h = meta.height;
|
|
6715
|
+
if (!w || !h || w <= 0 || h <= 0) return false;
|
|
6716
|
+
const shortSide = Math.min(w, h);
|
|
6717
|
+
if (shortSide < minShortSide) return false;
|
|
6718
|
+
const ratio = Math.max(w, h) / shortSide;
|
|
6719
|
+
return ratio >= aspectRatioMin && ratio <= aspectRatioMax;
|
|
6720
|
+
}
|
|
6721
|
+
};
|
|
6722
|
+
}
|
|
6723
|
+
var DEFAULT_SCREENSHOT_CLASSIFIER = createHeuristicClassifier();
|
|
6724
|
+
function decideImageRoute(meta, config, classifier = DEFAULT_SCREENSHOT_CLASSIFIER) {
|
|
6725
|
+
const cfg = resolveImageCompressionConfig(config);
|
|
6726
|
+
const estimatedTokens = estimateImageTokens(meta);
|
|
6727
|
+
if (!cfg.enabled) return { action: "pass", reason: "disabled", estimatedTokens };
|
|
6728
|
+
if (!meta.mediaType.startsWith("image/")) {
|
|
6729
|
+
return { action: "pass", reason: "not-an-image", estimatedTokens };
|
|
6730
|
+
}
|
|
6731
|
+
if (estimatedTokens < cfg.minTokens) {
|
|
6732
|
+
return { action: "pass", reason: "below-min-tokens", estimatedTokens };
|
|
6733
|
+
}
|
|
6734
|
+
if (!classifier.isScreenshotLike(meta)) {
|
|
6735
|
+
return { action: "pass", reason: "not-screenshot-like", estimatedTokens };
|
|
6736
|
+
}
|
|
6737
|
+
return {
|
|
6738
|
+
action: "downsample",
|
|
6739
|
+
reason: "downsample",
|
|
6740
|
+
estimatedTokens,
|
|
6741
|
+
recipe: { maxDimension: cfg.maxDimension, quality: cfg.quality, format: cfg.format }
|
|
6742
|
+
};
|
|
6743
|
+
}
|
|
6744
|
+
var IMAGE_FULL_FAILURE_MARKER = "[image_full FAILED:";
|
|
6745
|
+
function buildImageFullSystemNote(count) {
|
|
6746
|
+
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.]`;
|
|
6747
|
+
}
|
|
6748
|
+
function parseImageFullInput(input, callId, onWarn) {
|
|
6749
|
+
let obj = null;
|
|
6750
|
+
if (typeof input === "string") {
|
|
6751
|
+
try {
|
|
6752
|
+
const parsed = JSON.parse(input);
|
|
6753
|
+
if (parsed && typeof parsed === "object") obj = parsed;
|
|
6754
|
+
} catch {
|
|
6755
|
+
obj = null;
|
|
6756
|
+
}
|
|
6757
|
+
} else if (input && typeof input === "object") {
|
|
6758
|
+
obj = input;
|
|
6759
|
+
}
|
|
6760
|
+
if (!obj) {
|
|
6761
|
+
onWarn?.(`[acp-image-full-input] rejected: not an object (${typeof input})`);
|
|
6762
|
+
return null;
|
|
6763
|
+
}
|
|
6764
|
+
let ref;
|
|
6765
|
+
for (const key of ["ref", "messageId", "of"]) {
|
|
6766
|
+
const value = obj[key];
|
|
6767
|
+
if (typeof value === "string") {
|
|
6768
|
+
ref = value;
|
|
6769
|
+
break;
|
|
6770
|
+
}
|
|
6771
|
+
}
|
|
6772
|
+
if (typeof ref !== "string") {
|
|
6773
|
+
onWarn?.(
|
|
6774
|
+
`[acp-image-full-input] rejected: need ref (string); keys: ${Object.keys(obj).join(",")}`
|
|
6775
|
+
);
|
|
6776
|
+
return null;
|
|
6777
|
+
}
|
|
6778
|
+
const trimmed = ref.trim();
|
|
6779
|
+
if (!trimmed) {
|
|
6780
|
+
onWarn?.("[acp-image-full-input] rejected: ref is empty");
|
|
6781
|
+
return null;
|
|
6782
|
+
}
|
|
6783
|
+
return { ref: trimmed, ...callId ? { callId } : {} };
|
|
6784
|
+
}
|
|
6785
|
+
function recordImageShrink(state, record) {
|
|
6786
|
+
const bytesSaved = Math.max(0, record.originalBytes - record.shrunkBytes);
|
|
6787
|
+
const tokensSaved = Math.max(0, record.tokensBefore - record.tokensAfter);
|
|
6788
|
+
return {
|
|
6789
|
+
...state,
|
|
6790
|
+
imageShrinks: [...state.imageShrinks ?? [], record],
|
|
6791
|
+
stats: {
|
|
6792
|
+
...state.stats,
|
|
6793
|
+
imagesShrunk: (state.stats.imagesShrunk ?? 0) + 1,
|
|
6794
|
+
imageBytesSaved: (state.stats.imageBytesSaved ?? 0) + bytesSaved,
|
|
6795
|
+
imageTokensSaved: (state.stats.imageTokensSaved ?? 0) + tokensSaved
|
|
6796
|
+
}
|
|
6797
|
+
};
|
|
6798
|
+
}
|
|
6799
|
+
function isImageFullRestored(state, ref) {
|
|
6800
|
+
return (state.imageFullRestored ?? []).includes(ref);
|
|
6801
|
+
}
|
|
6802
|
+
function imageShrinksForRef(state, ref) {
|
|
6803
|
+
return (state.imageShrinks ?? []).filter((r) => r.ref === ref);
|
|
6804
|
+
}
|
|
6805
|
+
function applyImageFull(input) {
|
|
6806
|
+
const cfg = resolveImageCompressionConfig(input.config);
|
|
6807
|
+
const ref = input.ref.trim();
|
|
6808
|
+
if (!cfg.enabled) {
|
|
6809
|
+
return {
|
|
6810
|
+
state: input.state,
|
|
6811
|
+
ok: false,
|
|
6812
|
+
resultText: `${IMAGE_FULL_FAILURE_MARKER} image compression is disabled in this session \u2014 no image was ever downscaled]`
|
|
6813
|
+
};
|
|
6814
|
+
}
|
|
6815
|
+
const rawId = rawForRef(input.state.messageRefs, ref);
|
|
6816
|
+
if (!rawId) {
|
|
6817
|
+
return {
|
|
6818
|
+
state: input.state,
|
|
6819
|
+
ok: false,
|
|
6820
|
+
resultText: `${IMAGE_FULL_FAILURE_MARKER} ref ${ref} does not exist in this session]`
|
|
6821
|
+
};
|
|
6822
|
+
}
|
|
6823
|
+
if (isImageFullRestored(input.state, ref)) {
|
|
6824
|
+
return {
|
|
6825
|
+
state: input.state,
|
|
6826
|
+
ok: true,
|
|
6827
|
+
resultText: `already restored (${ref}) \u2014 full resolution stays in effect for the rest of this session.`
|
|
6828
|
+
};
|
|
6829
|
+
}
|
|
6830
|
+
const shrinks = imageShrinksForRef(input.state, ref);
|
|
6831
|
+
if (shrinks.length === 0) {
|
|
6832
|
+
return {
|
|
6833
|
+
state: input.state,
|
|
6834
|
+
ok: false,
|
|
6835
|
+
resultText: `${IMAGE_FULL_FAILURE_MARKER} no downscaled image is recorded for ${ref} \u2014 it was passed through untouched or never carried an image]`
|
|
6836
|
+
};
|
|
6837
|
+
}
|
|
6838
|
+
return {
|
|
6839
|
+
state: {
|
|
6840
|
+
...input.state,
|
|
6841
|
+
imageFullRestored: [...input.state.imageFullRestored ?? [], ref]
|
|
6842
|
+
},
|
|
6843
|
+
ok: true,
|
|
6844
|
+
resultText: `restored original-resolution image(s) for ${ref} (${shrinks.length} image${shrinks.length === 1 ? "" : "s"}); full resolution applies for the rest of this session.`
|
|
6845
|
+
};
|
|
6846
|
+
}
|
|
6847
|
+
function resetImageFullState(state) {
|
|
6848
|
+
return { ...state, imageFullRestored: [], imageShrinks: [] };
|
|
6849
|
+
}
|
|
6850
|
+
|
|
4747
6851
|
// src/parse-compress-input.ts
|
|
4748
6852
|
function parseCompressArgs(input, opts) {
|
|
4749
6853
|
const callId = opts?.callId;
|
|
@@ -5272,7 +7376,8 @@ function rebuildCompressionState(state, messages, config, ports = {}) {
|
|
|
5272
7376
|
const core = createCore({ countTokens: ports.countTokens ?? defaultCountTokens });
|
|
5273
7377
|
const refResult = assignRefs(messages, {
|
|
5274
7378
|
existing: state.messageRefs,
|
|
5275
|
-
nextIndex: highestUsedIndex(state.messageRefs) + 1
|
|
7379
|
+
nextIndex: highestUsedIndex(state.messageRefs) + 1,
|
|
7380
|
+
shouldSkip: (m) => m.id.startsWith(RETRIEVED_ID_PREFIX)
|
|
5276
7381
|
});
|
|
5277
7382
|
let working = { ...state, messageRefs: refResult.map };
|
|
5278
7383
|
const invocations = collectCompressInvocations(messages);
|
|
@@ -5636,6 +7741,129 @@ function makePreview(text, query, len) {
|
|
|
5636
7741
|
const suffix = end < text.length ? "\u2026" : "";
|
|
5637
7742
|
return prefix + clampWindow(text, start, end).trim() + suffix;
|
|
5638
7743
|
}
|
|
7744
|
+
|
|
7745
|
+
// src/output-steering.ts
|
|
7746
|
+
function classifyTurn(messages) {
|
|
7747
|
+
if (messages.length === 0) return "unknown";
|
|
7748
|
+
const last = messages[messages.length - 1];
|
|
7749
|
+
if (!last || last.role !== "user") return "unknown";
|
|
7750
|
+
if (typeof last.text === "string")
|
|
7751
|
+
return last.text.trim() ? "new_user_ask" : "unknown";
|
|
7752
|
+
const blocks = last.blocks;
|
|
7753
|
+
if (!Array.isArray(blocks) || blocks.length === 0) return "unknown";
|
|
7754
|
+
let sawToolResult = false;
|
|
7755
|
+
let sawError = false;
|
|
7756
|
+
for (const b of blocks) {
|
|
7757
|
+
if (!b) return "unknown";
|
|
7758
|
+
switch (b.kind) {
|
|
7759
|
+
case "tool_result":
|
|
7760
|
+
sawToolResult = true;
|
|
7761
|
+
if (b.isError === true) sawError = true;
|
|
7762
|
+
break;
|
|
7763
|
+
case "text":
|
|
7764
|
+
case "image":
|
|
7765
|
+
case "document":
|
|
7766
|
+
return "new_user_ask";
|
|
7767
|
+
default:
|
|
7768
|
+
return "unknown";
|
|
7769
|
+
}
|
|
7770
|
+
}
|
|
7771
|
+
if (sawError) return "error_continuation";
|
|
7772
|
+
if (sawToolResult) return "mechanical_continuation";
|
|
7773
|
+
return "unknown";
|
|
7774
|
+
}
|
|
7775
|
+
var MIN_VERBOSITY_LEVEL = 0;
|
|
7776
|
+
var MAX_VERBOSITY_LEVEL = 4;
|
|
7777
|
+
var DEFAULT_VERBOSITY_LEVEL = 2;
|
|
7778
|
+
var VERBOSITY_LEVELS = {
|
|
7779
|
+
1: "Skip preamble and postamble. Do not announce what you are about to do or recap what you just did; start with the substance.",
|
|
7780
|
+
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.",
|
|
7781
|
+
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.",
|
|
7782
|
+
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."
|
|
7783
|
+
};
|
|
7784
|
+
function verbosityDirective(level) {
|
|
7785
|
+
if (!Number.isInteger(level)) return null;
|
|
7786
|
+
return VERBOSITY_LEVELS[level] ?? null;
|
|
7787
|
+
}
|
|
7788
|
+
var DEFAULT_STEERING_SENTINEL = "<acp_output_steering>";
|
|
7789
|
+
function renderSteeringBlock(level, sentinel = DEFAULT_STEERING_SENTINEL) {
|
|
7790
|
+
const text = verbosityDirective(level);
|
|
7791
|
+
if (text === null) return null;
|
|
7792
|
+
return `${sentinel}
|
|
7793
|
+
${text}
|
|
7794
|
+
</${sentinel.slice(1)}`;
|
|
7795
|
+
}
|
|
7796
|
+
function applySteeringToPrompt(existing, block, sentinel = DEFAULT_STEERING_SENTINEL) {
|
|
7797
|
+
const suffix = `</${sentinel.slice(1)}`;
|
|
7798
|
+
const start = existing.indexOf(sentinel);
|
|
7799
|
+
if (start >= 0) {
|
|
7800
|
+
const found = existing.indexOf(suffix, start);
|
|
7801
|
+
const end = found < 0 ? existing.length : found + suffix.length;
|
|
7802
|
+
const prefix = existing.slice(0, start).replace(/\s+$/, "");
|
|
7803
|
+
const tail = existing.slice(end).replace(/^\n+/, "");
|
|
7804
|
+
const parts = [prefix, block, tail].filter((p) => p.length > 0);
|
|
7805
|
+
const updated2 = parts.join("\n\n");
|
|
7806
|
+
return { updated: updated2, changed: updated2 !== existing };
|
|
7807
|
+
}
|
|
7808
|
+
const trimmed = existing.trim();
|
|
7809
|
+
const updated = trimmed.length > 0 ? `${existing.replace(/\s+$/, "")}
|
|
7810
|
+
|
|
7811
|
+
${block}` : block;
|
|
7812
|
+
return { updated, changed: updated !== existing };
|
|
7813
|
+
}
|
|
7814
|
+
var EFFORT_LADDER = [
|
|
7815
|
+
"minimal",
|
|
7816
|
+
"low",
|
|
7817
|
+
"medium",
|
|
7818
|
+
"high",
|
|
7819
|
+
"xhigh"
|
|
7820
|
+
];
|
|
7821
|
+
function clampEffortToFloor(current, floor = "low") {
|
|
7822
|
+
if (typeof current !== "string") return null;
|
|
7823
|
+
const ci = EFFORT_LADDER.indexOf(current);
|
|
7824
|
+
if (ci === -1) return null;
|
|
7825
|
+
const fi = EFFORT_LADDER.indexOf(floor);
|
|
7826
|
+
return ci > fi ? floor : null;
|
|
7827
|
+
}
|
|
7828
|
+
var DEFAULT_OUTPUT_STEERING_CONFIG = {
|
|
7829
|
+
enabled: false,
|
|
7830
|
+
verbosityLevel: DEFAULT_VERBOSITY_LEVEL,
|
|
7831
|
+
effortRouting: true
|
|
7832
|
+
};
|
|
7833
|
+
function resolveVerbosityLevel(v) {
|
|
7834
|
+
if (typeof v === "number" && Number.isInteger(v) && v >= MIN_VERBOSITY_LEVEL && v <= MAX_VERBOSITY_LEVEL) {
|
|
7835
|
+
return { level: v };
|
|
7836
|
+
}
|
|
7837
|
+
const level = DEFAULT_OUTPUT_STEERING_CONFIG.verbosityLevel;
|
|
7838
|
+
if (v === void 0) return { level };
|
|
7839
|
+
return {
|
|
7840
|
+
level,
|
|
7841
|
+
warning: `[config] outputSteering.verbosityLevel must be an integer ${MIN_VERBOSITY_LEVEL}-${MAX_VERBOSITY_LEVEL}; got ${JSON.stringify(v)} \u2014 falling back to ${level}`
|
|
7842
|
+
};
|
|
7843
|
+
}
|
|
7844
|
+
function resolveOutputSteeringConfig(v) {
|
|
7845
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) {
|
|
7846
|
+
return { config: DEFAULT_OUTPUT_STEERING_CONFIG, warnings: [] };
|
|
7847
|
+
}
|
|
7848
|
+
const obj = v;
|
|
7849
|
+
const lvl = resolveVerbosityLevel(obj.verbosityLevel);
|
|
7850
|
+
return {
|
|
7851
|
+
config: {
|
|
7852
|
+
enabled: obj.enabled === true,
|
|
7853
|
+
verbosityLevel: lvl.level,
|
|
7854
|
+
effortRouting: obj.effortRouting !== false
|
|
7855
|
+
},
|
|
7856
|
+
warnings: lvl.warning ? [lvl.warning] : []
|
|
7857
|
+
};
|
|
7858
|
+
}
|
|
7859
|
+
function decideOutputSteering(messages, config = DEFAULT_OUTPUT_STEERING_CONFIG) {
|
|
7860
|
+
const turnKind = classifyTurn(messages);
|
|
7861
|
+
return {
|
|
7862
|
+
turnKind,
|
|
7863
|
+
verbosityLevel: config.enabled ? config.verbosityLevel : 0,
|
|
7864
|
+
lowerEffort: config.enabled && config.effortRouting && turnKind === "mechanical_continuation"
|
|
7865
|
+
};
|
|
7866
|
+
}
|
|
5639
7867
|
export {
|
|
5640
7868
|
ABSORB_PROMPT_MARKER,
|
|
5641
7869
|
ABSORB_TOOL,
|
|
@@ -5684,10 +7912,33 @@ export {
|
|
|
5684
7912
|
DECOMPRESS_TOOL_RESPONSES,
|
|
5685
7913
|
DEFAULT_ABSORB_CONFIG,
|
|
5686
7914
|
DEFAULT_ALGORITHM,
|
|
7915
|
+
DEFAULT_CCR_CONFIG,
|
|
7916
|
+
DEFAULT_CRUSH_CONFIG,
|
|
7917
|
+
DEFAULT_IMAGE_COMPRESSION_CONFIG,
|
|
7918
|
+
DEFAULT_OUTPUT_STEERING_CONFIG,
|
|
5687
7919
|
DEFAULT_ROLE_WEIGHTS,
|
|
5688
7920
|
DEFAULT_RULE_LIMITS,
|
|
7921
|
+
DEFAULT_SCREENSHOT_CLASSIFIER,
|
|
7922
|
+
DEFAULT_STEERING_SENTINEL,
|
|
7923
|
+
DEFAULT_VERBOSITY_LEVEL,
|
|
7924
|
+
EFFORT_LADDER,
|
|
5689
7925
|
HOW_TO_COMPRESS_RULES,
|
|
7926
|
+
IMAGE_FULL_FAILURE_MARKER,
|
|
7927
|
+
IMAGE_FULL_TOOL,
|
|
7928
|
+
IMAGE_FULL_TOOL_DESCRIPTION,
|
|
7929
|
+
IMAGE_FULL_TOOL_NAME,
|
|
7930
|
+
IMAGE_FULL_TOOL_OPENAI,
|
|
7931
|
+
IMAGE_FULL_TOOL_RESPONSES,
|
|
5690
7932
|
LEAN_HOW_TO_COMPRESS,
|
|
7933
|
+
MAX_VERBOSITY_LEVEL,
|
|
7934
|
+
MIN_VERBOSITY_LEVEL,
|
|
7935
|
+
PIXEL_IMAGE_FALLBACK_TOKENS,
|
|
7936
|
+
RETRIEVED_ID_PREFIX,
|
|
7937
|
+
RETRIEVE_TOOL,
|
|
7938
|
+
RETRIEVE_TOOL_DESCRIPTION,
|
|
7939
|
+
RETRIEVE_TOOL_NAME,
|
|
7940
|
+
RETRIEVE_TOOL_OPENAI,
|
|
7941
|
+
RETRIEVE_TOOL_RESPONSES,
|
|
5691
7942
|
RULES_USAGE_PROMPT,
|
|
5692
7943
|
RULE_TOOL_NAME,
|
|
5693
7944
|
SEARCH_CONTEXT_TOOL,
|
|
@@ -5695,9 +7946,11 @@ export {
|
|
|
5695
7946
|
SEARCH_CONTEXT_TOOL_NAME,
|
|
5696
7947
|
SEARCH_CONTEXT_TOOL_OPENAI,
|
|
5697
7948
|
SEARCH_CONTEXT_TOOL_RESPONSES,
|
|
7949
|
+
STORED_PLACEHOLDER_MARKER,
|
|
5698
7950
|
SUMMARY_HEADER,
|
|
5699
7951
|
TIER2_DISTILL_RULES,
|
|
5700
7952
|
TIER3_CONDENSE_RULES,
|
|
7953
|
+
VERBOSITY_LEVELS,
|
|
5701
7954
|
VIABLE_RANGE_MIN_TOKENS,
|
|
5702
7955
|
activeBlockSpans,
|
|
5703
7956
|
activeBlocks,
|
|
@@ -5709,8 +7962,12 @@ export {
|
|
|
5709
7962
|
appendAbsorbPrompts,
|
|
5710
7963
|
applyAbsorb,
|
|
5711
7964
|
applyAcpToolOverrides,
|
|
7965
|
+
applyCrushToMessages,
|
|
7966
|
+
applyImageFull,
|
|
5712
7967
|
applyMessageFilters,
|
|
7968
|
+
applyRetrieve,
|
|
5713
7969
|
applySectionOverrides,
|
|
7970
|
+
applySteeringToPrompt,
|
|
5714
7971
|
assignRefs,
|
|
5715
7972
|
blockById,
|
|
5716
7973
|
blockDocs,
|
|
@@ -5721,26 +7978,41 @@ export {
|
|
|
5721
7978
|
buildCompressHybridSystemPrompt,
|
|
5722
7979
|
buildCompressSystemPrompt,
|
|
5723
7980
|
buildCompressTextSystemPrompt,
|
|
7981
|
+
buildImageFullSystemNote,
|
|
5724
7982
|
buildRecap,
|
|
5725
7983
|
buildRestoredContentPreview,
|
|
7984
|
+
buildRetrievalInjection,
|
|
5726
7985
|
buildStatusReport,
|
|
7986
|
+
buildStoredPlaceholder,
|
|
5727
7987
|
builtinSource,
|
|
7988
|
+
ccrStoreNode,
|
|
7989
|
+
clampEffortToFloor,
|
|
7990
|
+
classifyCrushText,
|
|
7991
|
+
classifyKind,
|
|
7992
|
+
classifyTurn,
|
|
5728
7993
|
clearDocFeatures,
|
|
5729
7994
|
clearMessageFilters,
|
|
5730
7995
|
clearRules,
|
|
5731
7996
|
cloneWithDescriptions,
|
|
7997
|
+
codeTrimPlugin,
|
|
5732
7998
|
collectBlockContent,
|
|
5733
7999
|
collectLatestProtected,
|
|
5734
8000
|
computeFoldEconomics,
|
|
8001
|
+
contentStoreStats,
|
|
5735
8002
|
countMessageTokens,
|
|
5736
8003
|
coveredMessageIds,
|
|
5737
8004
|
createBpeTokenizer,
|
|
8005
|
+
createContentStore,
|
|
5738
8006
|
createCore,
|
|
5739
8007
|
createDirPackSource,
|
|
8008
|
+
createHeuristicClassifier,
|
|
5740
8009
|
createInitialState,
|
|
5741
8010
|
createPackResolver,
|
|
5742
8011
|
createRenderRefsNode,
|
|
8012
|
+
crushText,
|
|
5743
8013
|
deactivateBlock,
|
|
8014
|
+
decideImageRoute,
|
|
8015
|
+
decideOutputSteering,
|
|
5744
8016
|
decomposeSample,
|
|
5745
8017
|
defaultConfig,
|
|
5746
8018
|
defaultCountTokens,
|
|
@@ -5750,7 +8022,10 @@ export {
|
|
|
5750
8022
|
docCacheInfo,
|
|
5751
8023
|
docFeatures,
|
|
5752
8024
|
emptyRefMap,
|
|
8025
|
+
estimateImageTokens,
|
|
5753
8026
|
estimateTokensFast,
|
|
8027
|
+
evaluateToolResult,
|
|
8028
|
+
extractCommand,
|
|
5754
8029
|
findActiveAncestor,
|
|
5755
8030
|
findBlocksOverlappingMessages,
|
|
5756
8031
|
formatCacheReport,
|
|
@@ -5760,35 +8035,53 @@ export {
|
|
|
5760
8035
|
formatRulesList,
|
|
5761
8036
|
getMessageFilter,
|
|
5762
8037
|
getSearchAlgorithm,
|
|
8038
|
+
hasMediaPayload,
|
|
8039
|
+
hasStoredRef,
|
|
8040
|
+
hashContent,
|
|
5763
8041
|
hideAbsorbedMessages,
|
|
5764
8042
|
hideConsumedCompressCalls,
|
|
5765
8043
|
highestActiveTier,
|
|
5766
8044
|
highestUsedIndex,
|
|
8045
|
+
imageShrinksForRef,
|
|
5767
8046
|
indexToRef,
|
|
5768
8047
|
isAbsorbCandidate,
|
|
8048
|
+
isImageFullRestored,
|
|
5769
8049
|
isMessageLatestProtected,
|
|
5770
8050
|
isMessageProtected,
|
|
5771
8051
|
isRenderedSummaryMessage,
|
|
8052
|
+
isRetrievedMessage,
|
|
8053
|
+
isStoredPlaceholderText,
|
|
5772
8054
|
isSummaryMessageId,
|
|
5773
8055
|
isValidPackName,
|
|
8056
|
+
jsonFoldPlugin,
|
|
5774
8057
|
leanPack,
|
|
8058
|
+
listCrushPlugins,
|
|
5775
8059
|
listMessageFilters,
|
|
5776
8060
|
listRules,
|
|
5777
8061
|
listSearchAlgorithms,
|
|
8062
|
+
logSelectPlugin,
|
|
5778
8063
|
makeIO,
|
|
5779
8064
|
matchSession,
|
|
5780
8065
|
matchToolPattern,
|
|
5781
8066
|
messageDocs,
|
|
8067
|
+
normalizeHead,
|
|
8068
|
+
noteRetrieval,
|
|
5782
8069
|
parseAbsorbInput,
|
|
5783
8070
|
parseBlockIdArg,
|
|
5784
8071
|
parseBoundary,
|
|
5785
8072
|
parseCompressArgs,
|
|
5786
8073
|
parseCompressInput,
|
|
8074
|
+
parseImageDimensions,
|
|
8075
|
+
parseImageDimensionsFromBase64,
|
|
8076
|
+
parseImageFullInput,
|
|
8077
|
+
pixelTileEstimate,
|
|
5787
8078
|
prune,
|
|
5788
8079
|
rawForRef,
|
|
5789
8080
|
rebuildCompressionState,
|
|
8081
|
+
recordImageShrink,
|
|
5790
8082
|
refForRaw,
|
|
5791
8083
|
refToIndex,
|
|
8084
|
+
registerCrushPlugin,
|
|
5792
8085
|
registerMessageFilter,
|
|
5793
8086
|
registerSearchAlgorithm,
|
|
5794
8087
|
removeRule,
|
|
@@ -5796,23 +8089,38 @@ export {
|
|
|
5796
8089
|
renderMessage2 as renderMessage,
|
|
5797
8090
|
renderNudgeText,
|
|
5798
8091
|
renderRefsNode,
|
|
8092
|
+
renderSteeringBlock,
|
|
5799
8093
|
renderVisibleRefs,
|
|
8094
|
+
resetCrushPlugins,
|
|
8095
|
+
resetImageFullState,
|
|
5800
8096
|
resolveAbsorbConfig,
|
|
5801
8097
|
resolveBlockSpan,
|
|
5802
8098
|
resolveBoundaries,
|
|
8099
|
+
resolveCcrConfig,
|
|
8100
|
+
resolveCrushConfig,
|
|
8101
|
+
resolveImageCompressionConfig,
|
|
8102
|
+
resolveOutputSteeringConfig,
|
|
5803
8103
|
resolvePrompts,
|
|
5804
8104
|
resolveRuleLimits,
|
|
5805
8105
|
resolveTransformChannel,
|
|
8106
|
+
resolveVerbosityLevel,
|
|
8107
|
+
retrieveByRef,
|
|
8108
|
+
retrievedMessageId,
|
|
5806
8109
|
runPipeline,
|
|
5807
8110
|
sanitizePackSurface,
|
|
5808
8111
|
searchBlocks,
|
|
5809
8112
|
searchBlocksAsync,
|
|
5810
8113
|
setDocCacheCap,
|
|
8114
|
+
storeCoveredOriginals,
|
|
8115
|
+
storeLargeResults,
|
|
8116
|
+
storeOriginal,
|
|
5811
8117
|
summarizeFoldEconomics,
|
|
5812
8118
|
summaryMessageId,
|
|
5813
8119
|
syncBlocks,
|
|
5814
8120
|
truncateLargeToolOutputs,
|
|
8121
|
+
unregisterCrushPlugin,
|
|
5815
8122
|
validateConfig,
|
|
8123
|
+
verbosityDirective,
|
|
5816
8124
|
viableRanges,
|
|
5817
8125
|
visibleBlockAnchor
|
|
5818
8126
|
};
|