billion-context 0.1.45 → 0.1.46-pr.202.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +949 -731
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -43467,206 +43467,561 @@ var require_lib = __commonJS({
|
|
|
43467
43467
|
}
|
|
43468
43468
|
});
|
|
43469
43469
|
|
|
43470
|
-
// node_modules/acp-kernel/dist/
|
|
43470
|
+
// node_modules/acp-kernel/dist/chunk-MWXUJVMN.js
|
|
43471
43471
|
import { createRequire } from "module";
|
|
43472
|
-
var
|
|
43473
|
-
|
|
43474
|
-
|
|
43475
|
-
|
|
43476
|
-
|
|
43477
|
-
|
|
43478
|
-
if (!Number.isInteger(index) || index < MIN_INDEX || index > MAX_INDEX) {
|
|
43479
|
-
throw new RangeError(
|
|
43480
|
-
`ref index out of bounds: ${index} (allowed ${MIN_INDEX}-${MAX_INDEX})`
|
|
43481
|
-
);
|
|
43482
|
-
}
|
|
43483
|
-
return `m${String(index).padStart(REF_WIDTH, "0")}`;
|
|
43484
|
-
}
|
|
43485
|
-
function refToIndex(ref) {
|
|
43486
|
-
const match = REF_PATTERN.exec(ref.trim().toLowerCase());
|
|
43487
|
-
if (!match) return null;
|
|
43488
|
-
const index = Number(match[1]);
|
|
43489
|
-
if (index < MIN_INDEX || index > MAX_INDEX) return null;
|
|
43490
|
-
return index;
|
|
43491
|
-
}
|
|
43492
|
-
function refForRaw(map, rawId) {
|
|
43493
|
-
return map.byRaw[rawId] ?? null;
|
|
43472
|
+
var require2 = createRequire(import.meta.url);
|
|
43473
|
+
function defaultCountTokens(text) {
|
|
43474
|
+
if (!text) return 0;
|
|
43475
|
+
const cjk = text.match(/[\u4e00-\u9fff\u3040-\u30ff\uac00-\ud7af]/g);
|
|
43476
|
+
const cjkCount = cjk?.length ?? 0;
|
|
43477
|
+
return cjkCount + Math.ceil((text.length - cjkCount) / 4);
|
|
43494
43478
|
}
|
|
43495
|
-
function
|
|
43496
|
-
|
|
43497
|
-
|
|
43498
|
-
byRef: { ...options.existing.byRef }
|
|
43499
|
-
};
|
|
43500
|
-
let cursor = Number.isInteger(options.nextIndex) && options.nextIndex >= MIN_INDEX ? options.nextIndex : MIN_INDEX;
|
|
43501
|
-
let newlyAssigned = 0;
|
|
43502
|
-
for (const message of messages) {
|
|
43503
|
-
if (!message.id || options.shouldSkip?.(message)) continue;
|
|
43504
|
-
if (map.byRaw[message.id]) continue;
|
|
43505
|
-
if (options.isProtected?.(message)) {
|
|
43506
|
-
map.byRaw[message.id] = BLOCKED_REF;
|
|
43507
|
-
continue;
|
|
43508
|
-
}
|
|
43509
|
-
const ref = allocateFreeRef(map, cursor);
|
|
43510
|
-
cursor = ref.index + 1;
|
|
43511
|
-
map.byRaw[message.id] = ref.text;
|
|
43512
|
-
map.byRef[ref.text] = message.id;
|
|
43513
|
-
newlyAssigned++;
|
|
43514
|
-
}
|
|
43515
|
-
return { map, nextIndex: cursor, newlyAssigned };
|
|
43479
|
+
function estimateTokensFast(text) {
|
|
43480
|
+
if (!text) return 0;
|
|
43481
|
+
return Math.ceil(text.length / 4);
|
|
43516
43482
|
}
|
|
43517
|
-
|
|
43518
|
-
|
|
43519
|
-
|
|
43520
|
-
|
|
43521
|
-
|
|
43522
|
-
|
|
43483
|
+
var COMPRESS_PHILOSOPHY = `Compression Philosophy:
|
|
43484
|
+
- All compression serves the primary task, but be frugal.
|
|
43485
|
+
- Context capacity is precious. Save context by compressing consumed outputs, not by avoiding tools.
|
|
43486
|
+
- Compress by need, not by percentage.
|
|
43487
|
+
- Work from summaries, not raw tool outputs. All listed ranges (user prompts, tool outputs, code, logs, exploration, intermediate steps) should be compressed to summary format \u2014 the ONLY exceptions are protected content, content the current step is actively using, or critical content you cannot reconstruct.`;
|
|
43488
|
+
var HOW_TO_COMPRESS_RULES = `HOW TO COMPRESS
|
|
43489
|
+
|
|
43490
|
+
When you call \`compress\`, the summary you write becomes the only record of the replaced conversation. Make it self-contained and complete: every user request, experiment purpose, and work task in the range must be accurately captured. A later reader (or you, after decompressing) should be able to continue the task WITHOUT needing the original.
|
|
43491
|
+
|
|
43492
|
+
KEEP VERBATIM \u2014 never paraphrase or abbreviate these:
|
|
43493
|
+
- Full file paths with line numbers, directory prefix on every mention (\`lib/hooks.ts:347\`, \`src/index.ts:12-18\`, \`gatenet_v3/model.py:45\`). Never abbreviate to a bare filename (\`hooks.ts\`, \`model.py\`) \u2014 they are ambiguous and cannot be grepped or decompressed-to later.
|
|
43494
|
+
- Function, class, and type signatures (exact names, params, return types) AND critical code lines that encode logic \u2014 the line that IS the finding, not just the function name (e.g. \`kv_keys += define_gate * a_key[i](emb)\` is more useful than "see model_kvnet.py").
|
|
43495
|
+
- Error messages and stack traces (exact text \u2014 you need the literal string to grep for it later).
|
|
43496
|
+
- Key details from reports and analyses \u2014 not just the conclusion. Keep the comparison numbers and the mechanism, not "X is worse" alone (write "1.76\xD7 PPL gap because KV store is static", not "KVNet underperforms").
|
|
43497
|
+
- Decisions and their rationale ("chose X over Y because Z" \u2014 the "because" is load-bearing; without it the decision looks arbitrary).
|
|
43498
|
+
- Constraints discovered ("must support Node 22", "no new dependencies", "AGENTS.md forbids \`as any\`").
|
|
43499
|
+
- Exact values: versions, config keys, thresholds, magic numbers.
|
|
43500
|
+
- User intent \u2014 quote short user messages verbatim. When the message is too long to quote, preserve intent with extra care: do not change scope, constraints, priorities, acceptance criteria, or requested outcomes. Mark them clearly as past quotes (e.g., "User said: ..."), not as current directives. Losing these changes the task itself.
|
|
43501
|
+
- The user's overall goal and any changes to it \u2014 the big-picture objective plus how it evolved during the compressed range. Each summary must reflect the goal as it stood at the end of the range, including pivots (e.g., "initially: fix bug X \u2192 pivoted to: refactor module Y after discovering root cause"). Losing the goal or its evolution makes all subsequent work appear unmotivated.
|
|
43502
|
+
- Purpose behind each significant action \u2014 preserve not just what was done but why: the hypothesis behind each experiment, the question behind each exploration, the task goal behind each work action. Without purpose, the summary reads as disconnected technical steps with no through-line.
|
|
43503
|
+
- Open questions and unresolved TODOs \u2014 losing these changes what work appears to remain.
|
|
43504
|
+
- Message refs of key anchors (\`m00420\`, \`m00510\u2013m00520\`) \u2014 they let you or a later reader jump back via decompress to the exact original.
|
|
43505
|
+
|
|
43506
|
+
DROP \u2014 extract the signal, discard the vessel:
|
|
43507
|
+
- Verbose logs (build/test/\`npm\` output) once you have captured the error line or the result.
|
|
43508
|
+
- Duplicate file reads once the needed content is recorded.
|
|
43509
|
+
- Consumed exploration \u2014 search hits, agent return values, successful tool outputs \u2014 once you have extracted the facts you need (same rule as dead-ends, but nothing went wrong; the content is simply spent).
|
|
43510
|
+
- Dead-end exploration \u2014 but PRESERVE the lesson in one line: "tried X, failed because Y".
|
|
43511
|
+
- Back-and-forth discussion and self-corrections once the final position is captured (keep the outcome, drop the journey to it).
|
|
43512
|
+
- Repeated status checks (\`git status\`, \`ls\`) once state is known.
|
|
43513
|
+
|
|
43514
|
+
For each significant item you DROP (scripts, reports, large analyses, long tool outputs), add a one-line CONTENT description of what it covers \u2014 not where it lives. Bad: "probe script at /path/probe_kvnet.py". Good: "probe_kvnet.py: tests n-gram baseline, generation quality, long-range dependency, position sensitivity, op pipeline, QUERY attention." This lets a later decompress target the right block by relevance, not by guessing locations.
|
|
43515
|
+
|
|
43516
|
+
PRIORITY \u2014 when the summary must be compact, preserve in this order:
|
|
43517
|
+
1. User's overall goal, goal evolution, intent, and hard constraints (losing these changes the task).
|
|
43518
|
+
2. Decisions and rationale.
|
|
43519
|
+
3. Exact technical artifacts: paths, signatures, errors, values.
|
|
43520
|
+
4. Conclusions and key findings.
|
|
43521
|
+
5. Lessons learned: what failed and why.
|
|
43522
|
+
|
|
43523
|
+
Write dense, scannable bullets \u2014 not narrative prose. If the range spans distinct concerns (request \u2192 findings \u2192 decision), group bullets under short thematic headers so a reader can scan to the part they need. Every line must earn its place. Do not mimic the style of existing summaries in context; follow these rules.`;
|
|
43524
|
+
var TIER2_DISTILL_RULES = `TIER 2 COMPRESSION \u2014 DISTILLATION
|
|
43525
|
+
|
|
43526
|
+
You are compressing historical summaries (not raw conversation). These summaries have already captured the details. Your job is to DISTILL them: extract only what matters for future work, discard the process.
|
|
43527
|
+
|
|
43528
|
+
KEEP \u2014 these are the only things that survive distillation:
|
|
43529
|
+
- Decisions and their rationale ("chose X over Y because Z" \u2014 the "because" is load-bearing).
|
|
43530
|
+
- Final outcomes: version numbers shipped, PR numbers merged/closed, bugs fixed or deferred.
|
|
43531
|
+
- Key lessons: what failed and why ("tried X, failed because Y"). These prevent repeating mistakes.
|
|
43532
|
+
- Critical constraints discovered ("must support Node 22", "AGENTS.md forbids as any").
|
|
43533
|
+
- Design decisions with architectural impact ("chose compress-as-anchor over synthetic messages because prefix cache").
|
|
43534
|
+
- Whether content is OBSOLETE or SUPERSEDED \u2014 mark with one line: "[SUPERSEDED by PR #NNN]" or "[OBSOLETE: deleted in vX.Y.Z]". Do NOT keep the obsolete content's details \u2014 just the marker and reason.
|
|
43535
|
+
- Function/class/type names and module paths that are the SUBJECT of the work \u2014 e.g., "fixed filterCompressedRanges in prune.ts", "added SessionStateRegistry in state.ts". Not exact line numbers or full signatures \u2014 just enough to LOCATE the code without searching.
|
|
43536
|
+
- Exploration findings: if a block was exploratory with no decision, keep the CONCLUSION in one line ("explored X, not viable because Y"). Do not keep the exploration process.
|
|
43537
|
+
|
|
43538
|
+
DROP \u2014 these were useful during the work but are no longer needed:
|
|
43539
|
+
- Exact line numbers, diffs, verbose function signatures, full code listings.
|
|
43540
|
+
- Build/deploy process details, test execution steps.
|
|
43541
|
+
- Review process details (who reviewed, what rounds, test counts).
|
|
43542
|
+
- Verbose logs, command output, intermediate debugging steps.
|
|
43543
|
+
|
|
43544
|
+
FORMAT:
|
|
43545
|
+
- Start each distilled block with a source header line:
|
|
43546
|
+
\`Source: bN+bM+... (XK\u2192YK tok, Zx). [original topic]\`
|
|
43547
|
+
Example: \`Source: b5+b7 (56K+44K\u2192268 tok, 375x). [Tool-result recap + publish]\`
|
|
43548
|
+
- 3-5 bullet points per source block, each a self-contained fact.
|
|
43549
|
+
- Dense, scannable \u2014 no narrative prose.
|
|
43550
|
+
- Start with the outcome, not the process: "v1.13.0 shipped (7 PRs bundled)" not "implemented 7 PRs then reviewed then merged".
|
|
43551
|
+
- Cross-block synthesis: if multiple source blocks cover the same topic (same PR, same feature, same bug), MERGE them into a single group of bullets. Do not repeat the same fact from different blocks \u2014 keep it once under the most relevant source header.
|
|
43552
|
+
|
|
43553
|
+
SIZE TARGET: 50-150 tokens per source block (excluding the header). If you can't fit it in 150 tokens, you're keeping too much process. If a block has nothing worth keeping (pure noise), output just the header followed by "[no actionable content]."`;
|
|
43554
|
+
var TIER3_CONDENSE_RULES = `TIER 3 COMPRESSION \u2014 ULTRA-CONDENSATION
|
|
43555
|
+
|
|
43556
|
+
You are compressing distilled summaries (Tier 2) into ultra-condensed facts (Tier 3). The distilled summaries already contain only decisions and outcomes. Your job is to reduce them to bare factual references.
|
|
43557
|
+
|
|
43558
|
+
PRIORITY \u2014 when a source block has more facts than the size target allows, keep in this order:
|
|
43559
|
+
1. Shipped outcomes (versions released, PRs merged) \u2014 these are permanent record.
|
|
43560
|
+
2. Open work (PRs/issues still pending) \u2014 these may need follow-up.
|
|
43561
|
+
3. Key decisions with architectural impact ("chose X over Y because Z").
|
|
43562
|
+
4. Critical constraints ("must support Node 22").
|
|
43563
|
+
Drop everything else. Tier 3 is a lookup index, not a knowledge base.
|
|
43564
|
+
|
|
43565
|
+
FORMAT:
|
|
43566
|
+
- Start with a source header line:
|
|
43567
|
+
\`Source: bN+bM+... (XK\u2192YK tok, Zx). [original topic]\`
|
|
43568
|
+
- Output 1-3 facts per source block. Each fact is a single line: subject + outcome.
|
|
43569
|
+
- No explanations, no rationale, no process \u2014 just the fact.
|
|
43570
|
+
- Format: "[PR/Issue/Version] \u2014 [outcome in \u22648 words]"
|
|
43571
|
+
- Merge related facts from different source blocks if they concern the same topic.
|
|
43572
|
+
|
|
43573
|
+
EXAMPLES:
|
|
43574
|
+
- "v1.13.0 shipped \u2014 quality gate + GC fix (7 PRs)"
|
|
43575
|
+
- "PR #196 merged \u2014 preserve-first-user (supersedes #169)"
|
|
43576
|
+
- "Bug 1214 fixed \u2014 compress consumed all user messages"
|
|
43577
|
+
- "Chose compress-as-anchor \u2014 prefix cache benefit over synthetic injection"
|
|
43578
|
+
- "Constraint: AGENTS.md forbids as any \u2014 never suppress types"
|
|
43579
|
+
|
|
43580
|
+
DROP:
|
|
43581
|
+
- Multi-sentence context. If a fact needs >1 sentence, it's too detailed for Tier 3.
|
|
43582
|
+
- Lessons learned ("tried X, failed because Y") \u2014 drop UNLESS the failure is likely to recur and the block is <30 days old.
|
|
43583
|
+
- Design rationale details \u2014 keep the decision, drop the "because" unless it's a critical constraint.
|
|
43584
|
+
- Anything marked [OBSOLETE] or [SUPERSEDED] \u2014 drop entirely, note "[N blocks obsolete]" in the summary.
|
|
43585
|
+
|
|
43586
|
+
SIZE TARGET: 30-60 tokens per source block (including header). For a batch of N source blocks, total output \u2248 N \xD7 40 tokens. If a source block has only one trivial fact, output just the header + one line.`;
|
|
43587
|
+
var defaultPrompts = Object.freeze({
|
|
43588
|
+
compressPhilosophy: COMPRESS_PHILOSOPHY,
|
|
43589
|
+
howToCompressRules: HOW_TO_COMPRESS_RULES,
|
|
43590
|
+
tier2DistillRules: TIER2_DISTILL_RULES,
|
|
43591
|
+
tier3CondenseRules: TIER3_CONDENSE_RULES
|
|
43592
|
+
});
|
|
43593
|
+
function resolvePrompts(overrides, options = {}) {
|
|
43594
|
+
const clean = {};
|
|
43595
|
+
if (overrides) {
|
|
43596
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
43597
|
+
if (typeof value === "string") {
|
|
43598
|
+
clean[key] = value;
|
|
43599
|
+
}
|
|
43523
43600
|
}
|
|
43524
|
-
candidate++;
|
|
43525
43601
|
}
|
|
43526
|
-
|
|
43527
|
-
|
|
43528
|
-
|
|
43529
|
-
}
|
|
43530
|
-
|
|
43531
|
-
let highest = 0;
|
|
43532
|
-
for (const ref of Object.values(map.byRaw)) {
|
|
43533
|
-
const index = ref === BLOCKED_REF ? null : refToIndex(ref);
|
|
43534
|
-
if (index !== null && index > highest) highest = index;
|
|
43602
|
+
const keys = Object.keys(clean);
|
|
43603
|
+
if (keys.length > 0 && !options.acknowledgeRisk) {
|
|
43604
|
+
throw new Error(
|
|
43605
|
+
`resolvePrompts: overriding compression rules requires { acknowledgeRisk: true }. Overridden keys: ${keys.join(", ")}. These rules are quality-critical (tuned over months of production use); changing them can degrade summary quality and break retrieval (summaries may lose paths, signatures, decisions).`
|
|
43606
|
+
);
|
|
43535
43607
|
}
|
|
43536
|
-
return
|
|
43537
|
-
}
|
|
43538
|
-
function createInitialState() {
|
|
43539
|
-
return {
|
|
43540
|
-
blocks: [],
|
|
43541
|
-
messageRefs: { byRaw: {}, byRef: {} },
|
|
43542
|
-
tokenSnapshot: {},
|
|
43543
|
-
nudge: {
|
|
43544
|
-
lastPerMessageNudgeTokens: 0,
|
|
43545
|
-
lastNudgeShownTokens: 0,
|
|
43546
|
-
baselineTokens: 0,
|
|
43547
|
-
anchors: {},
|
|
43548
|
-
lastShownByTier: {}
|
|
43549
|
-
},
|
|
43550
|
-
stats: { tokensCompressed: 0, compressionCount: 0 },
|
|
43551
|
-
nextBlockId: 1,
|
|
43552
|
-
nextRunId: 1
|
|
43553
|
-
};
|
|
43608
|
+
return { ...defaultPrompts, ...clean };
|
|
43554
43609
|
}
|
|
43555
|
-
function
|
|
43556
|
-
|
|
43557
|
-
|
|
43558
|
-
|
|
43610
|
+
function efficiencyNote(prompts) {
|
|
43611
|
+
return `This is an efficiency nudge to compress early and keep context lean \u2014 not an overflow warning. A separate, stronger alert will appear if the context is actually full.
|
|
43612
|
+
|
|
43613
|
+
${prompts.compressPhilosophy}`;
|
|
43559
43614
|
}
|
|
43560
|
-
function
|
|
43561
|
-
|
|
43562
|
-
|
|
43563
|
-
|
|
43615
|
+
function emergencyHeader(prompts) {
|
|
43616
|
+
return `\u26A0\uFE0F Context limit reached \u2014 compress now. Prioritize consumed tool outputs.
|
|
43617
|
+
|
|
43618
|
+
${prompts.compressPhilosophy}`;
|
|
43564
43619
|
}
|
|
43565
|
-
function
|
|
43566
|
-
|
|
43620
|
+
function formatK(n) {
|
|
43621
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
43622
|
+
return `${n}`;
|
|
43567
43623
|
}
|
|
43568
|
-
function
|
|
43569
|
-
|
|
43624
|
+
function formatBreakdown(bd) {
|
|
43625
|
+
if (!bd) return "";
|
|
43626
|
+
const parts = [];
|
|
43627
|
+
if (bd.system > 0) parts.push(`${formatK(bd.system)} system`);
|
|
43628
|
+
if (bd.tool > 0) parts.push(`${formatK(bd.tool)} tool`);
|
|
43629
|
+
if (bd.summaries > 0) parts.push(`${formatK(bd.summaries)} summaries`);
|
|
43630
|
+
if (bd.code > 0) parts.push(`${formatK(bd.code)} code`);
|
|
43631
|
+
if (bd.text > 0) parts.push(`${formatK(bd.text)} text`);
|
|
43632
|
+
const growth = bd.growth > 0 ? `
|
|
43633
|
+
+${formatK(bd.growth)} since last nudge` : "";
|
|
43634
|
+
return `Context breakdown: ${parts.join(" | ")}${growth}`;
|
|
43570
43635
|
}
|
|
43571
|
-
function
|
|
43572
|
-
|
|
43573
|
-
|
|
43574
|
-
if (!block.active) continue;
|
|
43575
|
-
for (const id of block.effectiveMessageIds) covered.add(id);
|
|
43636
|
+
function formatTierTargetBlocks(blocks) {
|
|
43637
|
+
if (blocks.length === 0) {
|
|
43638
|
+
return "Target blocks: (none \u2014 no tier blocks found)";
|
|
43576
43639
|
}
|
|
43577
|
-
|
|
43578
|
-
|
|
43579
|
-
|
|
43580
|
-
|
|
43581
|
-
|
|
43582
|
-
|
|
43583
|
-
|
|
43584
|
-
block.generation = "old";
|
|
43585
|
-
}
|
|
43586
|
-
}
|
|
43587
|
-
}
|
|
43588
|
-
var SUMMARY_HEADER = "[Compressed conversation section]";
|
|
43589
|
-
function prune(messages, state, options = {}) {
|
|
43590
|
-
const covered = coveredMessageIds(state);
|
|
43591
|
-
if (covered.size === 0) return [...messages];
|
|
43592
|
-
const inject = options.injectSummaries ?? true;
|
|
43593
|
-
const firstUserIndex = messages.findIndex(
|
|
43594
|
-
(message) => message.role === "user"
|
|
43595
|
-
);
|
|
43596
|
-
const indexById = /* @__PURE__ */ new Map();
|
|
43597
|
-
messages.forEach((message, index) => indexById.set(message.id, index));
|
|
43598
|
-
const anchors = inject ? collectSummaryAnchors(state, indexById) : [];
|
|
43599
|
-
return stripOrphanedReasoning(
|
|
43600
|
-
stripOrphanedToolResults(
|
|
43601
|
-
stripOrphanedToolCalls(
|
|
43602
|
-
rebuildMessages(messages, covered, firstUserIndex, anchors)
|
|
43603
|
-
)
|
|
43604
|
-
)
|
|
43605
|
-
);
|
|
43640
|
+
const lines = blocks.map((b2) => {
|
|
43641
|
+
const summaryTokens = Math.ceil((b2.summary ?? "").length / 4);
|
|
43642
|
+
const topic = b2.topic ? ` "${b2.topic}"` : "";
|
|
43643
|
+
return ` ${b2.blockId} ${b2.effectiveMessageIds.length} msgs ${formatK(b2.compressedTokens)}\u2192${formatK(summaryTokens)}${topic}`;
|
|
43644
|
+
});
|
|
43645
|
+
return `Target ${blocks[0].tier === 1 ? "tier-1" : "tier-2"} blocks to distill (${blocks.length}):
|
|
43646
|
+
${lines.join("\n")}`;
|
|
43606
43647
|
}
|
|
43607
|
-
function
|
|
43608
|
-
|
|
43609
|
-
|
|
43610
|
-
|
|
43611
|
-
|
|
43612
|
-
|
|
43613
|
-
|
|
43614
|
-
|
|
43648
|
+
function formatRanges(compressible, protectedRanges) {
|
|
43649
|
+
if (compressible.length === 0 && protectedRanges.length === 0) {
|
|
43650
|
+
return "[No specific ranges detected \u2014 compress any consumed content.]";
|
|
43651
|
+
}
|
|
43652
|
+
const refNum2 = (ref) => {
|
|
43653
|
+
const m2 = ref.match(/\d+/);
|
|
43654
|
+
return m2 ? parseInt(m2[0], 10) : 0;
|
|
43655
|
+
};
|
|
43656
|
+
const entries = [];
|
|
43657
|
+
for (const r of compressible) {
|
|
43658
|
+
entries.push({
|
|
43659
|
+
startRef: r.startRef,
|
|
43660
|
+
endRef: r.endRef,
|
|
43661
|
+
startNum: refNum2(r.startRef),
|
|
43662
|
+
endNum: refNum2(r.endRef),
|
|
43663
|
+
count: r.count,
|
|
43664
|
+
tokens: r.tokens,
|
|
43665
|
+
toolPct: r.toolPct,
|
|
43666
|
+
textPct: r.textPct,
|
|
43667
|
+
compressibleTokens: r.tokens,
|
|
43668
|
+
compressibleCount: r.count,
|
|
43669
|
+
protectedTokens: 0,
|
|
43670
|
+
protectedCount: 0,
|
|
43671
|
+
protectedTools: [],
|
|
43672
|
+
dangerous: r.dangerous ?? false
|
|
43673
|
+
});
|
|
43674
|
+
}
|
|
43675
|
+
for (const r of protectedRanges) {
|
|
43676
|
+
entries.push({
|
|
43677
|
+
startRef: r.startRef,
|
|
43678
|
+
endRef: r.endRef,
|
|
43679
|
+
startNum: refNum2(r.startRef),
|
|
43680
|
+
endNum: refNum2(r.endRef),
|
|
43681
|
+
count: r.count,
|
|
43682
|
+
tokens: r.tokens,
|
|
43683
|
+
toolPct: 0,
|
|
43684
|
+
textPct: 0,
|
|
43685
|
+
compressibleTokens: 0,
|
|
43686
|
+
compressibleCount: 0,
|
|
43687
|
+
protectedTokens: r.tokens,
|
|
43688
|
+
protectedCount: r.count,
|
|
43689
|
+
protectedTools: [...r.tools],
|
|
43690
|
+
dangerous: false
|
|
43691
|
+
});
|
|
43692
|
+
}
|
|
43693
|
+
entries.sort((a, b2) => a.startNum - b2.startNum);
|
|
43694
|
+
const merged = [];
|
|
43695
|
+
for (const e of entries) {
|
|
43696
|
+
const last = merged[merged.length - 1];
|
|
43697
|
+
if (last && e.startNum <= last.endNum + 1) {
|
|
43698
|
+
last.endRef = e.endRef;
|
|
43699
|
+
last.endNum = Math.max(last.endNum, e.endNum);
|
|
43700
|
+
last.count += e.count;
|
|
43701
|
+
last.tokens += e.tokens;
|
|
43702
|
+
last.compressibleTokens += e.compressibleTokens;
|
|
43703
|
+
last.compressibleCount += e.compressibleCount;
|
|
43704
|
+
last.protectedTokens += e.protectedTokens;
|
|
43705
|
+
last.protectedCount += e.protectedCount;
|
|
43706
|
+
if (e.dangerous) last.dangerous = true;
|
|
43707
|
+
for (const t of e.protectedTools) {
|
|
43708
|
+
if (!last.protectedTools.includes(t)) last.protectedTools.push(t);
|
|
43615
43709
|
}
|
|
43710
|
+
} else {
|
|
43711
|
+
merged.push({ ...e });
|
|
43616
43712
|
}
|
|
43617
|
-
anchors.push({
|
|
43618
|
-
blockId: block.blockId,
|
|
43619
|
-
summary: block.summary,
|
|
43620
|
-
topic: block.topic,
|
|
43621
|
-
insertAt: earliest ?? 0
|
|
43622
|
-
});
|
|
43623
43713
|
}
|
|
43624
|
-
|
|
43625
|
-
|
|
43626
|
-
|
|
43627
|
-
|
|
43628
|
-
const result = [];
|
|
43629
|
-
const pending = [...anchors];
|
|
43630
|
-
for (let index = 0; index < messages.length; index++) {
|
|
43631
|
-
while (pending.length > 0 && pending[0].insertAt === index) {
|
|
43632
|
-
result.push(renderSummary(pending.shift()));
|
|
43714
|
+
const lines = merged.map((e) => {
|
|
43715
|
+
const suffix = e.dangerous && e.compressibleTokens > 0 ? " \u26A0\uFE0F NOT recommended unless you are certain." : "";
|
|
43716
|
+
if (e.protectedTokens > 0 && e.compressibleTokens === 0) {
|
|
43717
|
+
return ` ${e.startRef}\u2013${e.endRef} ${e.count} msgs ${formatK(e.tokens)} [PROTECTED: ${e.protectedTools.join(", ")} \u2014 not compressible]${suffix}`;
|
|
43633
43718
|
}
|
|
43634
|
-
if (
|
|
43635
|
-
|
|
43636
|
-
continue;
|
|
43719
|
+
if (e.protectedTokens > 0 && e.compressibleTokens > 0) {
|
|
43720
|
+
return ` ${e.startRef}\u2013${e.endRef} ${e.count} msgs ${formatK(e.tokens)} [${formatK(e.compressibleTokens)} compressible | ${formatK(e.protectedTokens)} protected: ${e.protectedTools.join(", ")}]${suffix}`;
|
|
43637
43721
|
}
|
|
43638
|
-
|
|
43639
|
-
|
|
43722
|
+
return ` ${e.startRef}\u2013${e.endRef} ${e.count} msgs ${formatK(e.tokens)} [tool ${e.toolPct}% | text ${e.textPct}%]${suffix}`;
|
|
43723
|
+
});
|
|
43724
|
+
return `Compressible ranges (${merged.length}, oldest first):
|
|
43725
|
+
${lines.join("\n")}`;
|
|
43726
|
+
}
|
|
43727
|
+
function renderNudgeText(decision, prompts = defaultPrompts) {
|
|
43728
|
+
const breakdownStr = formatBreakdown(decision.contextBreakdown);
|
|
43729
|
+
const rangesStr = formatRanges(decision.compressibleRanges, decision.protectedRanges ?? []);
|
|
43730
|
+
const isEmergency = !!decision.breakdown?.emergencyOverride || !!decision.breakdown?.overLimit;
|
|
43731
|
+
if (decision.tier !== null && decision.tier >= 2) {
|
|
43732
|
+
const isT2 = decision.tier === 2;
|
|
43733
|
+
const targets = decision.tierTargetBlocks ?? [];
|
|
43734
|
+
const blockList = formatTierTargetBlocks(targets);
|
|
43735
|
+
const startId = targets[0]?.blockId ?? "b1";
|
|
43736
|
+
const endId = targets[targets.length - 1]?.blockId ?? "b5";
|
|
43737
|
+
const voice = isEmergency ? "emergency" : "gentle";
|
|
43738
|
+
const triggerLine = isEmergency ? `[EMERGENCY \u2014 TIER ${decision.tier} ${isT2 ? "DISTILLATION" : "CONDENSATION"}] Context limit reached \u2014 distill NOW into a denser summary to reclaim tokens.` : `[TIER ${decision.tier} ${isT2 ? "DISTILLATION" : "CONDENSATION"} TRIGGER]`;
|
|
43739
|
+
return {
|
|
43740
|
+
voice,
|
|
43741
|
+
text: [
|
|
43742
|
+
efficiencyNote(prompts),
|
|
43743
|
+
"",
|
|
43744
|
+
breakdownStr,
|
|
43745
|
+
"",
|
|
43746
|
+
triggerLine,
|
|
43747
|
+
isT2 ? `Your tier-1 compression summaries have accumulated. Distill them into a single denser tier-2 summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-2 block as well \u2014 apply HOW TO COMPRESS to those raw messages and the TIER 2 distillation rules to the existing summaries, so the whole span is covered and nothing is lost.` : `Your tier-2 compression summaries have accumulated. Condense them further into a tier-3 ultra-condensed summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-3 block as well \u2014 apply HOW TO COMPRESS to those raw messages and the TIER 3 condensation rules to the existing summaries, so the whole span is covered and nothing is lost.`,
|
|
43748
|
+
blockList,
|
|
43749
|
+
`Example: compress({ content: [{ startId: "${startId}", endId: "${endId}", summary: "..." }] })`,
|
|
43750
|
+
"",
|
|
43751
|
+
prompts.howToCompressRules,
|
|
43752
|
+
"",
|
|
43753
|
+
isT2 ? prompts.tier2DistillRules : prompts.tier3CondenseRules
|
|
43754
|
+
].join("\n")
|
|
43755
|
+
};
|
|
43640
43756
|
}
|
|
43641
|
-
|
|
43642
|
-
|
|
43757
|
+
if (isEmergency) {
|
|
43758
|
+
return {
|
|
43759
|
+
voice: "emergency",
|
|
43760
|
+
text: [
|
|
43761
|
+
emergencyHeader(prompts),
|
|
43762
|
+
"",
|
|
43763
|
+
breakdownStr,
|
|
43764
|
+
"",
|
|
43765
|
+
prompts.howToCompressRules,
|
|
43766
|
+
"",
|
|
43767
|
+
`{ "topic": "...", "content": [{ "startId": "<ID>", "endId": "<ID>", "summary": "..." }] }`,
|
|
43768
|
+
"Only use IDs from visible messages above. Compress older work first.",
|
|
43769
|
+
"",
|
|
43770
|
+
rangesStr
|
|
43771
|
+
].join("\n")
|
|
43772
|
+
};
|
|
43643
43773
|
}
|
|
43644
|
-
return
|
|
43774
|
+
return {
|
|
43775
|
+
voice: "gentle",
|
|
43776
|
+
text: [
|
|
43777
|
+
efficiencyNote(prompts),
|
|
43778
|
+
"",
|
|
43779
|
+
breakdownStr,
|
|
43780
|
+
"",
|
|
43781
|
+
prompts.howToCompressRules,
|
|
43782
|
+
"",
|
|
43783
|
+
rangesStr,
|
|
43784
|
+
"",
|
|
43785
|
+
`\u{1F4A1} Compress all ranges in one call (pass multiple content entries: \`content: [{...}, {...}]\`).`
|
|
43786
|
+
].join("\n")
|
|
43787
|
+
};
|
|
43645
43788
|
}
|
|
43646
|
-
|
|
43647
|
-
|
|
43648
|
-
|
|
43649
|
-
|
|
43650
|
-
|
|
43789
|
+
var VIABLE_RANGE_MIN_TOKENS = 200;
|
|
43790
|
+
function viableRanges(ranges) {
|
|
43791
|
+
return ranges.filter((r) => r.tokens >= VIABLE_RANGE_MIN_TOKENS);
|
|
43792
|
+
}
|
|
43793
|
+
|
|
43794
|
+
// node_modules/acp-kernel/dist/chunk-DPH62BGM.js
|
|
43795
|
+
function createInitialState() {
|
|
43651
43796
|
return {
|
|
43652
|
-
|
|
43653
|
-
|
|
43654
|
-
|
|
43655
|
-
|
|
43797
|
+
blocks: [],
|
|
43798
|
+
messageRefs: { byRaw: {}, byRef: {} },
|
|
43799
|
+
tokenSnapshot: {},
|
|
43800
|
+
nudge: {
|
|
43801
|
+
lastPerMessageNudgeTokens: 0,
|
|
43802
|
+
lastNudgeShownTokens: 0,
|
|
43803
|
+
baselineTokens: 0,
|
|
43804
|
+
anchors: {},
|
|
43805
|
+
lastShownByTier: {}
|
|
43806
|
+
},
|
|
43807
|
+
stats: { tokensCompressed: 0, compressionCount: 0 },
|
|
43808
|
+
nextBlockId: 1,
|
|
43809
|
+
nextRunId: 1
|
|
43656
43810
|
};
|
|
43657
43811
|
}
|
|
43658
|
-
function
|
|
43659
|
-
const
|
|
43660
|
-
|
|
43661
|
-
|
|
43662
|
-
|
|
43812
|
+
function allocateBlockId(state) {
|
|
43813
|
+
const id = state.nextBlockId;
|
|
43814
|
+
state.nextBlockId = Math.max(1, id) + 1;
|
|
43815
|
+
return `b${id}`;
|
|
43816
|
+
}
|
|
43817
|
+
function allocateRunId(state) {
|
|
43818
|
+
const id = state.nextRunId;
|
|
43819
|
+
state.nextRunId = Math.max(1, id) + 1;
|
|
43820
|
+
return `r${id}`;
|
|
43821
|
+
}
|
|
43822
|
+
function blockById(state, blockId) {
|
|
43823
|
+
return state.blocks.find((block) => block.blockId === blockId);
|
|
43824
|
+
}
|
|
43825
|
+
function activeBlocks(state) {
|
|
43826
|
+
return state.blocks.filter((block) => block.active);
|
|
43827
|
+
}
|
|
43828
|
+
function coveredMessageIds(state) {
|
|
43829
|
+
const covered = /* @__PURE__ */ new Set();
|
|
43830
|
+
for (const block of state.blocks) {
|
|
43831
|
+
if (!block.active) continue;
|
|
43832
|
+
for (const id of block.effectiveMessageIds) covered.add(id);
|
|
43833
|
+
}
|
|
43834
|
+
return covered;
|
|
43835
|
+
}
|
|
43836
|
+
function advanceSurvival(state, promotionThreshold) {
|
|
43837
|
+
for (const block of state.blocks) {
|
|
43838
|
+
if (!block.active) continue;
|
|
43839
|
+
block.survivedCount += 1;
|
|
43840
|
+
if (block.survivedCount >= promotionThreshold) {
|
|
43841
|
+
block.generation = "old";
|
|
43663
43842
|
}
|
|
43664
43843
|
}
|
|
43665
|
-
return messages.filter(
|
|
43666
|
-
(m2) => m2.contentType !== "tool-result" || !m2.toolCallId || knownCallIds.has(m2.toolCallId)
|
|
43667
|
-
);
|
|
43668
43844
|
}
|
|
43669
|
-
|
|
43845
|
+
|
|
43846
|
+
// node_modules/acp-kernel/dist/index.js
|
|
43847
|
+
var REF_WIDTH = 5;
|
|
43848
|
+
var MIN_INDEX = 1;
|
|
43849
|
+
var MAX_INDEX = 99999;
|
|
43850
|
+
var REF_PATTERN = /^m0*(\d{1,5})$/;
|
|
43851
|
+
var BLOCKED_REF = "BLOCKED";
|
|
43852
|
+
function indexToRef(index) {
|
|
43853
|
+
if (!Number.isInteger(index) || index < MIN_INDEX || index > MAX_INDEX) {
|
|
43854
|
+
throw new RangeError(
|
|
43855
|
+
`ref index out of bounds: ${index} (allowed ${MIN_INDEX}-${MAX_INDEX})`
|
|
43856
|
+
);
|
|
43857
|
+
}
|
|
43858
|
+
return `m${String(index).padStart(REF_WIDTH, "0")}`;
|
|
43859
|
+
}
|
|
43860
|
+
function refToIndex(ref) {
|
|
43861
|
+
const match = REF_PATTERN.exec(ref.trim().toLowerCase());
|
|
43862
|
+
if (!match) return null;
|
|
43863
|
+
const index = Number(match[1]);
|
|
43864
|
+
if (index < MIN_INDEX || index > MAX_INDEX) return null;
|
|
43865
|
+
return index;
|
|
43866
|
+
}
|
|
43867
|
+
function refForRaw(map, rawId) {
|
|
43868
|
+
return map.byRaw[rawId] ?? null;
|
|
43869
|
+
}
|
|
43870
|
+
function assignRefs(messages, options) {
|
|
43871
|
+
const map = {
|
|
43872
|
+
byRaw: { ...options.existing.byRaw },
|
|
43873
|
+
byRef: { ...options.existing.byRef }
|
|
43874
|
+
};
|
|
43875
|
+
let cursor = Number.isInteger(options.nextIndex) && options.nextIndex >= MIN_INDEX ? options.nextIndex : MIN_INDEX;
|
|
43876
|
+
let newlyAssigned = 0;
|
|
43877
|
+
for (const message of messages) {
|
|
43878
|
+
if (!message.id || options.shouldSkip?.(message)) continue;
|
|
43879
|
+
if (map.byRaw[message.id]) continue;
|
|
43880
|
+
if (options.isProtected?.(message)) {
|
|
43881
|
+
map.byRaw[message.id] = BLOCKED_REF;
|
|
43882
|
+
continue;
|
|
43883
|
+
}
|
|
43884
|
+
const ref = allocateFreeRef(map, cursor);
|
|
43885
|
+
cursor = ref.index + 1;
|
|
43886
|
+
map.byRaw[message.id] = ref.text;
|
|
43887
|
+
map.byRef[ref.text] = message.id;
|
|
43888
|
+
newlyAssigned++;
|
|
43889
|
+
}
|
|
43890
|
+
return { map, nextIndex: cursor, newlyAssigned };
|
|
43891
|
+
}
|
|
43892
|
+
function allocateFreeRef(map, start) {
|
|
43893
|
+
let candidate = Math.max(start, MIN_INDEX);
|
|
43894
|
+
while (candidate <= MAX_INDEX) {
|
|
43895
|
+
const text = indexToRef(candidate);
|
|
43896
|
+
if (!map.byRef[text]) {
|
|
43897
|
+
return { text, index: candidate };
|
|
43898
|
+
}
|
|
43899
|
+
candidate++;
|
|
43900
|
+
}
|
|
43901
|
+
throw new Error(
|
|
43902
|
+
`ref capacity exhausted: cannot allocate beyond ${indexToRef(MAX_INDEX)}`
|
|
43903
|
+
);
|
|
43904
|
+
}
|
|
43905
|
+
function highestUsedIndex(map) {
|
|
43906
|
+
let highest = 0;
|
|
43907
|
+
for (const ref of Object.values(map.byRaw)) {
|
|
43908
|
+
const index = ref === BLOCKED_REF ? null : refToIndex(ref);
|
|
43909
|
+
if (index !== null && index > highest) highest = index;
|
|
43910
|
+
}
|
|
43911
|
+
return highest;
|
|
43912
|
+
}
|
|
43913
|
+
var SUMMARY_HEADER = "[Compressed conversation section]";
|
|
43914
|
+
var SUMMARY_ID_PREFIX = "acp_summary_";
|
|
43915
|
+
function summaryMessageId(blockId) {
|
|
43916
|
+
return `${SUMMARY_ID_PREFIX}${blockId}`;
|
|
43917
|
+
}
|
|
43918
|
+
function isSummaryMessageId(id) {
|
|
43919
|
+
return id.startsWith(SUMMARY_ID_PREFIX);
|
|
43920
|
+
}
|
|
43921
|
+
function isRenderedSummaryMessage(message) {
|
|
43922
|
+
return isSummaryMessageId(message.id) && message.role === "system" && message.contentType === "text";
|
|
43923
|
+
}
|
|
43924
|
+
function prune(messages, state, options = {}) {
|
|
43925
|
+
const covered = coveredMessageIds(state);
|
|
43926
|
+
if (covered.size === 0) return [...messages];
|
|
43927
|
+
const inject = options.injectSummaries ?? true;
|
|
43928
|
+
const firstUserIndex = messages.findIndex(
|
|
43929
|
+
(message) => message.role === "user"
|
|
43930
|
+
);
|
|
43931
|
+
const indexById = /* @__PURE__ */ new Map();
|
|
43932
|
+
const summaryIndexById = /* @__PURE__ */ new Map();
|
|
43933
|
+
messages.forEach((message, index) => {
|
|
43934
|
+
indexById.set(message.id, index);
|
|
43935
|
+
if (isRenderedSummaryMessage(message))
|
|
43936
|
+
summaryIndexById.set(message.id, index);
|
|
43937
|
+
});
|
|
43938
|
+
const anchors = inject ? collectSummaryAnchors(state, indexById, summaryIndexById) : [];
|
|
43939
|
+
return stripOrphanedReasoning(
|
|
43940
|
+
stripOrphanedToolResults(
|
|
43941
|
+
stripOrphanedToolCalls(
|
|
43942
|
+
rebuildMessages(messages, covered, firstUserIndex, anchors)
|
|
43943
|
+
)
|
|
43944
|
+
)
|
|
43945
|
+
);
|
|
43946
|
+
}
|
|
43947
|
+
function collectSummaryAnchors(state, indexById, summaryIndexById) {
|
|
43948
|
+
const anchors = [];
|
|
43949
|
+
for (const block of activeBlocks(state)) {
|
|
43950
|
+
const existingIndex = summaryIndexById.get(summaryMessageId(block.blockId));
|
|
43951
|
+
if (existingIndex !== void 0) {
|
|
43952
|
+
anchors.push({
|
|
43953
|
+
blockId: block.blockId,
|
|
43954
|
+
summary: block.summary,
|
|
43955
|
+
topic: block.topic,
|
|
43956
|
+
insertAt: existingIndex
|
|
43957
|
+
});
|
|
43958
|
+
continue;
|
|
43959
|
+
}
|
|
43960
|
+
let earliest = null;
|
|
43961
|
+
for (const id of block.effectiveMessageIds) {
|
|
43962
|
+
const index = indexById.get(id);
|
|
43963
|
+
if (index !== void 0 && (earliest === null || index < earliest)) {
|
|
43964
|
+
earliest = index;
|
|
43965
|
+
}
|
|
43966
|
+
}
|
|
43967
|
+
anchors.push({
|
|
43968
|
+
blockId: block.blockId,
|
|
43969
|
+
summary: block.summary,
|
|
43970
|
+
topic: block.topic,
|
|
43971
|
+
insertAt: earliest ?? 0
|
|
43972
|
+
});
|
|
43973
|
+
}
|
|
43974
|
+
anchors.sort((left, right) => left.insertAt - right.insertAt);
|
|
43975
|
+
return anchors;
|
|
43976
|
+
}
|
|
43977
|
+
function rebuildMessages(messages, covered, firstUserIndex, anchors) {
|
|
43978
|
+
const result = [];
|
|
43979
|
+
const pending = [...anchors];
|
|
43980
|
+
const anchoredSummaryIds = new Set(
|
|
43981
|
+
anchors.map((anchor) => summaryMessageId(anchor.blockId))
|
|
43982
|
+
);
|
|
43983
|
+
for (let index = 0; index < messages.length; index++) {
|
|
43984
|
+
while (pending.length > 0 && pending[0].insertAt === index) {
|
|
43985
|
+
result.push(renderSummary(pending.shift()));
|
|
43986
|
+
}
|
|
43987
|
+
if (index === firstUserIndex && firstUserIndex >= 0) {
|
|
43988
|
+
result.push(messages[index]);
|
|
43989
|
+
continue;
|
|
43990
|
+
}
|
|
43991
|
+
if (covered.has(messages[index].id)) continue;
|
|
43992
|
+
if (isRenderedSummaryMessage(messages[index]) && anchoredSummaryIds.has(messages[index].id))
|
|
43993
|
+
continue;
|
|
43994
|
+
result.push(messages[index]);
|
|
43995
|
+
}
|
|
43996
|
+
while (pending.length > 0) {
|
|
43997
|
+
result.push(renderSummary(pending.shift()));
|
|
43998
|
+
}
|
|
43999
|
+
return result;
|
|
44000
|
+
}
|
|
44001
|
+
function renderSummary(anchor) {
|
|
44002
|
+
const body = anchor.summary.trim();
|
|
44003
|
+
const topicLine = anchor.topic ? `${SUMMARY_HEADER} \u2014 ${anchor.topic}` : SUMMARY_HEADER;
|
|
44004
|
+
const text = body.length === 0 ? topicLine : `${topicLine}
|
|
44005
|
+
${body}`;
|
|
44006
|
+
return {
|
|
44007
|
+
id: summaryMessageId(anchor.blockId),
|
|
44008
|
+
role: "system",
|
|
44009
|
+
contentType: "text",
|
|
44010
|
+
text
|
|
44011
|
+
};
|
|
44012
|
+
}
|
|
44013
|
+
function stripOrphanedToolResults(messages) {
|
|
44014
|
+
const knownCallIds = /* @__PURE__ */ new Set();
|
|
44015
|
+
for (const m2 of messages) {
|
|
44016
|
+
if (m2.contentType === "tool-call" && m2.toolCallId) {
|
|
44017
|
+
knownCallIds.add(m2.toolCallId);
|
|
44018
|
+
}
|
|
44019
|
+
}
|
|
44020
|
+
return messages.filter(
|
|
44021
|
+
(m2) => m2.contentType !== "tool-result" || !m2.toolCallId || knownCallIds.has(m2.toolCallId)
|
|
44022
|
+
);
|
|
44023
|
+
}
|
|
44024
|
+
function stripOrphanedToolCalls(messages) {
|
|
43670
44025
|
const knownResultIds = /* @__PURE__ */ new Set();
|
|
43671
44026
|
for (const m2 of messages) {
|
|
43672
44027
|
if (m2.contentType === "tool-result" && m2.toolCallId) {
|
|
@@ -43738,9 +44093,7 @@ function syncBlocks(messages, state) {
|
|
|
43738
44093
|
continue;
|
|
43739
44094
|
}
|
|
43740
44095
|
block.active = true;
|
|
43741
|
-
const stillPresent = block.effectiveMessageIds.some(
|
|
43742
|
-
(id) => presentIds.has(id)
|
|
43743
|
-
);
|
|
44096
|
+
const stillPresent = block.effectiveMessageIds.some((id) => presentIds.has(id)) || presentIds.has(summaryMessageId(block.blockId));
|
|
43744
44097
|
if (!stillPresent) {
|
|
43745
44098
|
block.active = false;
|
|
43746
44099
|
deactivated.push(block.blockId);
|
|
@@ -43748,17 +44101,6 @@ function syncBlocks(messages, state) {
|
|
|
43748
44101
|
}
|
|
43749
44102
|
return { state: result, deactivated };
|
|
43750
44103
|
}
|
|
43751
|
-
var require2 = createRequire(import.meta.url);
|
|
43752
|
-
function defaultCountTokens(text) {
|
|
43753
|
-
if (!text) return 0;
|
|
43754
|
-
const cjk = text.match(/[\u4e00-\u9fff\u3040-\u30ff\uac00-\ud7af]/g);
|
|
43755
|
-
const cjkCount = cjk?.length ?? 0;
|
|
43756
|
-
return cjkCount + Math.ceil((text.length - cjkCount) / 4);
|
|
43757
|
-
}
|
|
43758
|
-
function estimateTokensFast(text) {
|
|
43759
|
-
if (!text) return 0;
|
|
43760
|
-
return Math.ceil(text.length / 4);
|
|
43761
|
-
}
|
|
43762
44104
|
function defaultConfig(modelContextLimit, overrides = {}) {
|
|
43763
44105
|
const base = {
|
|
43764
44106
|
tiers: { enabled: true, tier2Trigger: 5, tier3Trigger: 10 },
|
|
@@ -43864,14 +44206,24 @@ function resolveBoundaries(input) {
|
|
|
43864
44206
|
`Invalid boundary ref(s): startId="${input.startRef}", endId="${input.endRef}". Use mNNNNN or bN.`
|
|
43865
44207
|
);
|
|
43866
44208
|
}
|
|
43867
|
-
const
|
|
44209
|
+
const indexByMessageId = /* @__PURE__ */ new Map();
|
|
43868
44210
|
input.messages.forEach(
|
|
43869
|
-
(message, index) =>
|
|
44211
|
+
(message, index) => indexByMessageId.set(message.id, index)
|
|
43870
44212
|
);
|
|
43871
44213
|
let snappedBoundaries = [];
|
|
43872
|
-
const startAnchor = resolveAnchorIndex(
|
|
44214
|
+
const startAnchor = resolveAnchorIndex(
|
|
44215
|
+
start,
|
|
44216
|
+
input.state,
|
|
44217
|
+
indexByMessageId,
|
|
44218
|
+
"start"
|
|
44219
|
+
);
|
|
43873
44220
|
if (startAnchor.snapped) snappedBoundaries.push(startAnchor.snapped);
|
|
43874
|
-
const endAnchor = resolveAnchorIndex(
|
|
44221
|
+
const endAnchor = resolveAnchorIndex(
|
|
44222
|
+
end,
|
|
44223
|
+
input.state,
|
|
44224
|
+
indexByMessageId,
|
|
44225
|
+
"end"
|
|
44226
|
+
);
|
|
43875
44227
|
if (endAnchor.snapped) snappedBoundaries.push(endAnchor.snapped);
|
|
43876
44228
|
let startIndex = startAnchor.index;
|
|
43877
44229
|
let endIndex = endAnchor.index;
|
|
@@ -43881,14 +44233,14 @@ function resolveBoundaries(input) {
|
|
|
43881
44233
|
const messageIds = [];
|
|
43882
44234
|
for (let index = startIndex; index <= endIndex; index++) {
|
|
43883
44235
|
const message = input.messages[index];
|
|
43884
|
-
if (message
|
|
44236
|
+
if (message && !isRenderedSummaryMessage(message))
|
|
44237
|
+
messageIds.push(message.id);
|
|
43885
44238
|
}
|
|
43886
44239
|
const boundaryKind = start.kind === "block" || end.kind === "block" ? "block" : "message";
|
|
43887
44240
|
const nestedBlockIds = [];
|
|
43888
44241
|
const nestedSeen = /* @__PURE__ */ new Set();
|
|
43889
44242
|
for (const block of activeBlocks(input.state)) {
|
|
43890
|
-
|
|
43891
|
-
if (anchor !== null && anchor >= startIndex && anchor <= endIndex) {
|
|
44243
|
+
if (blockVisibleInRange(block, indexByMessageId, startIndex, endIndex)) {
|
|
43892
44244
|
if (!nestedSeen.has(block.blockId)) {
|
|
43893
44245
|
nestedSeen.add(block.blockId);
|
|
43894
44246
|
nestedBlockIds.push(block.blockId);
|
|
@@ -43906,7 +44258,7 @@ function resolveBoundaries(input) {
|
|
|
43906
44258
|
snappedBoundaries
|
|
43907
44259
|
};
|
|
43908
44260
|
}
|
|
43909
|
-
function resolveAnchorIndex(boundary, state,
|
|
44261
|
+
function resolveAnchorIndex(boundary, state, indexByMessageId, endpoint) {
|
|
43910
44262
|
const label = endpoint === "start" ? "startId" : "endId";
|
|
43911
44263
|
if (boundary.kind === "message") {
|
|
43912
44264
|
const rawId = state.messageRefs.byRef[boundary.raw] ?? state.messageRefs.byRef[formatPaddedRef(boundary.numericId)];
|
|
@@ -43917,15 +44269,15 @@ function resolveAnchorIndex(boundary, state, indexByRawId, endpoint) {
|
|
|
43917
44269
|
`${label}="${boundary.raw}" does not exist in this session (typo or wrong session) \u2014 run acp_status for current refs.`
|
|
43918
44270
|
);
|
|
43919
44271
|
}
|
|
43920
|
-
const index =
|
|
44272
|
+
const index = indexByMessageId.get(rawId);
|
|
43921
44273
|
if (index !== void 0) {
|
|
43922
44274
|
return { index, snapped: null };
|
|
43923
44275
|
}
|
|
43924
|
-
const owner2 = activeOwnerAnchor(state, [rawId],
|
|
44276
|
+
const owner2 = activeOwnerAnchor(state, [rawId], indexByMessageId);
|
|
43925
44277
|
if (owner2 !== null) {
|
|
43926
44278
|
return {
|
|
43927
44279
|
index: owner2,
|
|
43928
|
-
snapped: `${label}="${boundary.raw}" refers to a message already compressed into an active block \u2014 anchored to
|
|
44280
|
+
snapped: `${label}="${boundary.raw}" refers to a message already compressed into an active block \u2014 anchored to the active block covering it instead.`
|
|
43929
44281
|
};
|
|
43930
44282
|
}
|
|
43931
44283
|
throw new BoundaryNotFoundError(
|
|
@@ -43943,12 +44295,16 @@ function resolveAnchorIndex(boundary, state, indexByRawId, endpoint) {
|
|
|
43943
44295
|
);
|
|
43944
44296
|
}
|
|
43945
44297
|
if (block.active) {
|
|
43946
|
-
const anchor =
|
|
44298
|
+
const anchor = visibleBlockAnchor(block, indexByMessageId);
|
|
43947
44299
|
if (anchor !== null) {
|
|
43948
44300
|
return { index: anchor, snapped: null };
|
|
43949
44301
|
}
|
|
43950
44302
|
}
|
|
43951
|
-
const owner = activeOwnerAnchor(
|
|
44303
|
+
const owner = activeOwnerAnchor(
|
|
44304
|
+
state,
|
|
44305
|
+
block.effectiveMessageIds,
|
|
44306
|
+
indexByMessageId
|
|
44307
|
+
);
|
|
43952
44308
|
if (owner !== null) {
|
|
43953
44309
|
return {
|
|
43954
44310
|
index: owner,
|
|
@@ -43965,31 +44321,64 @@ function resolveAnchorIndex(boundary, state, indexByRawId, endpoint) {
|
|
|
43965
44321
|
throw new BoundaryNotFoundError(
|
|
43966
44322
|
"consumed",
|
|
43967
44323
|
endpoint,
|
|
43968
|
-
`${label}="b${boundary.numericId}"
|
|
44324
|
+
`${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.`
|
|
43969
44325
|
);
|
|
43970
44326
|
}
|
|
43971
|
-
function activeOwnerAnchor(state, ownedIds,
|
|
44327
|
+
function activeOwnerAnchor(state, ownedIds, indexByMessageId) {
|
|
43972
44328
|
if (ownedIds.length === 0) return null;
|
|
43973
44329
|
const owned = new Set(ownedIds);
|
|
43974
44330
|
let best = null;
|
|
43975
44331
|
for (const block of state.blocks) {
|
|
43976
44332
|
if (!block.active) continue;
|
|
43977
|
-
const
|
|
44333
|
+
const inherited = inheritedContentIds(state, block);
|
|
44334
|
+
let ownsInherited = false;
|
|
44335
|
+
for (const id of owned) {
|
|
44336
|
+
if (inherited.has(id)) {
|
|
44337
|
+
ownsInherited = true;
|
|
44338
|
+
break;
|
|
44339
|
+
}
|
|
44340
|
+
}
|
|
44341
|
+
if (!ownsInherited) continue;
|
|
44342
|
+
const anchor = visibleBlockAnchor(block, indexByMessageId);
|
|
43978
44343
|
if (anchor === null) continue;
|
|
43979
|
-
|
|
43980
|
-
if (ownsContent && (best === null || anchor < best)) {
|
|
44344
|
+
if (best === null || anchor < best) {
|
|
43981
44345
|
best = anchor;
|
|
43982
44346
|
}
|
|
43983
44347
|
}
|
|
43984
44348
|
return best;
|
|
43985
44349
|
}
|
|
44350
|
+
function inheritedContentIds(state, block) {
|
|
44351
|
+
const ids = /* @__PURE__ */ new Set();
|
|
44352
|
+
for (const childId of block.directBlockIds) {
|
|
44353
|
+
const child = blockById(state, childId);
|
|
44354
|
+
if (!child) continue;
|
|
44355
|
+
for (const id of child.effectiveMessageIds) ids.add(id);
|
|
44356
|
+
}
|
|
44357
|
+
return ids;
|
|
44358
|
+
}
|
|
43986
44359
|
function formatPaddedRef(index) {
|
|
43987
44360
|
return `m${String(index).padStart(5, "0")}`;
|
|
43988
44361
|
}
|
|
43989
|
-
function
|
|
44362
|
+
function visibleBlockAnchor(block, indexByMessageId) {
|
|
44363
|
+
const summaryIndex = indexByMessageId.get(summaryMessageId(block.blockId));
|
|
44364
|
+
if (summaryIndex !== void 0) return summaryIndex;
|
|
44365
|
+
return earliestIndexOfIds(block.effectiveMessageIds, indexByMessageId);
|
|
44366
|
+
}
|
|
44367
|
+
function blockVisibleInRange(block, indexByMessageId, startIndex, endIndex) {
|
|
44368
|
+
const summaryIndex = indexByMessageId.get(summaryMessageId(block.blockId));
|
|
44369
|
+
if (summaryIndex !== void 0 && summaryIndex >= startIndex && summaryIndex <= endIndex) {
|
|
44370
|
+
return true;
|
|
44371
|
+
}
|
|
44372
|
+
const rawIndex = earliestIndexOfIds(
|
|
44373
|
+
block.effectiveMessageIds,
|
|
44374
|
+
indexByMessageId
|
|
44375
|
+
);
|
|
44376
|
+
return rawIndex !== null && rawIndex >= startIndex && rawIndex <= endIndex;
|
|
44377
|
+
}
|
|
44378
|
+
function earliestIndexOfIds(ids, indexByMessageId) {
|
|
43990
44379
|
let earliest = null;
|
|
43991
44380
|
for (const id of ids) {
|
|
43992
|
-
const index =
|
|
44381
|
+
const index = indexByMessageId.get(id);
|
|
43993
44382
|
if (index !== void 0 && (earliest === null || index < earliest)) {
|
|
43994
44383
|
earliest = index;
|
|
43995
44384
|
}
|
|
@@ -44615,7 +45004,12 @@ function createCore(ports = {}) {
|
|
|
44615
45004
|
let tokensCompressed = 0;
|
|
44616
45005
|
const errors = [];
|
|
44617
45006
|
const warnings = [];
|
|
44618
|
-
const protectedMessageIds = input.protectedMessageIds ?? computeProtectedRefs(
|
|
45007
|
+
const protectedMessageIds = input.protectedMessageIds ?? computeProtectedRefs(
|
|
45008
|
+
input.messages,
|
|
45009
|
+
input.state,
|
|
45010
|
+
input.config,
|
|
45011
|
+
countTokens
|
|
45012
|
+
);
|
|
44619
45013
|
const preExistingCoverage = collectCoverage(state);
|
|
44620
45014
|
const classifications = /* @__PURE__ */ new Map();
|
|
44621
45015
|
const classificationErrors = [];
|
|
@@ -44646,7 +45040,10 @@ function createCore(ports = {}) {
|
|
|
44646
45040
|
error: error instanceof Error ? error : new Error(String(error))
|
|
44647
45041
|
});
|
|
44648
45042
|
classificationErrors.push(
|
|
44649
|
-
rangeError(
|
|
45043
|
+
rangeError(
|
|
45044
|
+
spec,
|
|
45045
|
+
error instanceof Error ? error.message : String(error)
|
|
45046
|
+
)
|
|
44650
45047
|
);
|
|
44651
45048
|
}
|
|
44652
45049
|
}
|
|
@@ -44657,32 +45054,27 @@ function createCore(ports = {}) {
|
|
|
44657
45054
|
if (resolution.status === "ok") resolvableCount++;
|
|
44658
45055
|
else if (resolution.status === "unknown") unknownCount++;
|
|
44659
45056
|
}
|
|
44660
|
-
const
|
|
45057
|
+
const rangeSpans = [];
|
|
44661
45058
|
for (const [spec, resolution] of classifications) {
|
|
44662
45059
|
if (resolution.status !== "ok") continue;
|
|
44663
|
-
|
|
44664
|
-
|
|
44665
|
-
|
|
44666
|
-
|
|
44667
|
-
|
|
44668
|
-
|
|
44669
|
-
|
|
44670
|
-
const bMin = b2.indices.length > 0 ? Math.min(...b2.indices) : Infinity;
|
|
44671
|
-
return aMin - bMin;
|
|
44672
|
-
});
|
|
45060
|
+
rangeSpans.push({
|
|
45061
|
+
spec,
|
|
45062
|
+
start: resolution.resolved.startIndex,
|
|
45063
|
+
end: resolution.resolved.endIndex
|
|
45064
|
+
});
|
|
45065
|
+
}
|
|
45066
|
+
const sortedRanges = [...rangeSpans].sort((a, b2) => a.start - b2.start);
|
|
44673
45067
|
const skipSpecs = /* @__PURE__ */ new Set();
|
|
44674
45068
|
let acceptedMaxIndex = -1;
|
|
44675
45069
|
for (const entry of sortedRanges) {
|
|
44676
|
-
|
|
44677
|
-
const entryMin = entry.indices.length > 0 ? Math.min(...entry.indices) : -1;
|
|
44678
|
-
if (entryMin >= 0 && entryMin <= acceptedMaxIndex) {
|
|
45070
|
+
if (entry.start <= acceptedMaxIndex) {
|
|
44679
45071
|
skipSpecs.add(entry.spec);
|
|
44680
45072
|
warnings.push(
|
|
44681
45073
|
`Skipped range (${entry.spec.startRef}..${entry.spec.endRef}) \u2014 overlaps an earlier range in the batch; the earlier range takes precedence. Keep ranges disjoint.`
|
|
44682
45074
|
);
|
|
44683
45075
|
continue;
|
|
44684
45076
|
}
|
|
44685
|
-
if (
|
|
45077
|
+
if (entry.end > acceptedMaxIndex) acceptedMaxIndex = entry.end;
|
|
44686
45078
|
}
|
|
44687
45079
|
if (input.config.compress.minCompressRange > 0 && input.ranges.length > 0) {
|
|
44688
45080
|
let totalRangeChars = 0;
|
|
@@ -44745,7 +45137,12 @@ function createCore(ports = {}) {
|
|
|
44745
45137
|
tokensCompressed += outcome.tokens;
|
|
44746
45138
|
warnings.push(...outcome.warnings);
|
|
44747
45139
|
} catch (error) {
|
|
44748
|
-
errors.push(
|
|
45140
|
+
errors.push(
|
|
45141
|
+
rangeError(
|
|
45142
|
+
spec,
|
|
45143
|
+
error instanceof Error ? error.message : String(error)
|
|
45144
|
+
)
|
|
45145
|
+
);
|
|
44749
45146
|
}
|
|
44750
45147
|
}
|
|
44751
45148
|
state.stats.compressionCount += blocksCreated;
|
|
@@ -44755,12 +45152,17 @@ function createCore(ports = {}) {
|
|
|
44755
45152
|
state.nudge.lastNudgeShownTokens = 0;
|
|
44756
45153
|
state.nudge.lastShownByTier = {};
|
|
44757
45154
|
}
|
|
44758
|
-
return {
|
|
45155
|
+
return {
|
|
45156
|
+
state,
|
|
45157
|
+
result: { blocksCreated, tokensCompressed, errors, warnings }
|
|
45158
|
+
};
|
|
44759
45159
|
}
|
|
44760
45160
|
function processTurn(input) {
|
|
44761
45161
|
const configErrors = validateConfig(input.config);
|
|
44762
45162
|
if (configErrors.length > 0) {
|
|
44763
|
-
console.warn(
|
|
45163
|
+
console.warn(
|
|
45164
|
+
`[acp-kernel] Config validation warnings: ${configErrors.join("; ")}. Thresholds may not fire correctly.`
|
|
45165
|
+
);
|
|
44764
45166
|
}
|
|
44765
45167
|
const ctx = {
|
|
44766
45168
|
config: input.config,
|
|
@@ -44820,7 +45222,14 @@ function createCore(ports = {}) {
|
|
|
44820
45222
|
if (strategy === "none") return base;
|
|
44821
45223
|
return [...base, createRenderRefsNode(strategy)];
|
|
44822
45224
|
}
|
|
44823
|
-
return {
|
|
45225
|
+
return {
|
|
45226
|
+
processTurn,
|
|
45227
|
+
applyCompression,
|
|
45228
|
+
defaultNodes,
|
|
45229
|
+
decompress,
|
|
45230
|
+
search,
|
|
45231
|
+
status
|
|
45232
|
+
};
|
|
44824
45233
|
}
|
|
44825
45234
|
var assignRefsNode = {
|
|
44826
45235
|
name: "assign-refs",
|
|
@@ -44920,7 +45329,10 @@ var nudgeNode = {
|
|
|
44920
45329
|
if (nudge.shouldInject) {
|
|
44921
45330
|
stamped.lastNudgeShownTokens = ctx.tokenCount;
|
|
44922
45331
|
if (nudge.tier !== null) {
|
|
44923
|
-
stamped.lastShownByTier = {
|
|
45332
|
+
stamped.lastShownByTier = {
|
|
45333
|
+
...stamped.lastShownByTier,
|
|
45334
|
+
[nudge.tier]: ctx.tokenCount
|
|
45335
|
+
};
|
|
44924
45336
|
}
|
|
44925
45337
|
}
|
|
44926
45338
|
return {
|
|
@@ -44960,17 +45372,16 @@ function applySingleRange(input) {
|
|
|
44960
45372
|
const rangeMessageIds = applyPairBoundaryAdjustments(
|
|
44961
45373
|
resolved,
|
|
44962
45374
|
input.messages
|
|
44963
|
-
);
|
|
45375
|
+
).filter((id) => !isSummaryMessageId(id));
|
|
44964
45376
|
if (rangeMessageIds.length > resolved.messageIds.length) {
|
|
44965
|
-
const
|
|
44966
|
-
input.messages.forEach((m2, i) =>
|
|
44967
|
-
const adjustedStart =
|
|
44968
|
-
const adjustedEnd =
|
|
45377
|
+
const indexByMessageId = /* @__PURE__ */ new Map();
|
|
45378
|
+
input.messages.forEach((m2, i) => indexByMessageId.set(m2.id, i));
|
|
45379
|
+
const adjustedStart = rangeMessageIds.length > 0 ? indexByMessageId.get(rangeMessageIds[0]) ?? resolved.startIndex : resolved.startIndex;
|
|
45380
|
+
const adjustedEnd = rangeMessageIds.length > 0 ? indexByMessageId.get(rangeMessageIds[rangeMessageIds.length - 1]) ?? resolved.endIndex : resolved.endIndex;
|
|
44969
45381
|
const nestedSeen = new Set(resolved.nestedBlockIds);
|
|
44970
45382
|
for (const block2 of activeBlocks(input.state)) {
|
|
44971
45383
|
if (nestedSeen.has(block2.blockId)) continue;
|
|
44972
|
-
|
|
44973
|
-
if (anchor !== null && anchor >= adjustedStart && anchor <= adjustedEnd) {
|
|
45384
|
+
if (blockVisibleInRange(block2, indexByMessageId, adjustedStart, adjustedEnd)) {
|
|
44974
45385
|
nestedSeen.add(block2.blockId);
|
|
44975
45386
|
resolved.nestedBlockIds.push(block2.blockId);
|
|
44976
45387
|
}
|
|
@@ -45033,6 +45444,15 @@ function applySingleRange(input) {
|
|
|
45033
45444
|
)} from compression range (recent/last-user zone).`
|
|
45034
45445
|
);
|
|
45035
45446
|
}
|
|
45447
|
+
if (!isBlockBoundary && filteredIds.length === 0 && consumedBlockIds.length > 0) {
|
|
45448
|
+
const first = consumedBlockIds[0];
|
|
45449
|
+
const last = consumedBlockIds[consumedBlockIds.length - 1];
|
|
45450
|
+
throw new Error(
|
|
45451
|
+
`Range ${input.spec.startRef}..${input.spec.endRef} contains no new compressible messages \u2014 every message in it is already covered by active block(s) ${consumedBlockIds.join(
|
|
45452
|
+
", "
|
|
45453
|
+
)}. Nothing was compressed. To rewrite or merge those blocks, reference them by block ID (${first}..${last}); otherwise run acp_status and compress a range it reports as compressible.`
|
|
45454
|
+
);
|
|
45455
|
+
}
|
|
45036
45456
|
validateCompressionRange(input, filteredIds, consumedBlockIds.length);
|
|
45037
45457
|
let compressedTokens = 0;
|
|
45038
45458
|
for (const id of filteredIds) {
|
|
@@ -45185,12 +45605,21 @@ function pendingByTier(state, recommendation, countTokens, minCompressRange) {
|
|
|
45185
45605
|
const out = {};
|
|
45186
45606
|
const merged = recommendation?.recommendedRanges ?? [];
|
|
45187
45607
|
const effective = minCompressRange > 0 ? merged.filter((r) => (r.chars ?? r.tokens * 4) >= minCompressRange) : merged;
|
|
45188
|
-
out[1] = {
|
|
45608
|
+
out[1] = {
|
|
45609
|
+
pending: effective.reduce((s3, r) => s3 + r.tokens, 0),
|
|
45610
|
+
targetBlocks: []
|
|
45611
|
+
};
|
|
45189
45612
|
const active = activeBlocks(state);
|
|
45190
45613
|
const t1 = active.filter((b2) => b2.tier === 1);
|
|
45191
45614
|
const t2 = active.filter((b2) => b2.tier === 2);
|
|
45192
|
-
out[2] = {
|
|
45193
|
-
|
|
45615
|
+
out[2] = {
|
|
45616
|
+
pending: t1.reduce((s3, b2) => s3 + countTokens(b2.summary), 0),
|
|
45617
|
+
targetBlocks: t1
|
|
45618
|
+
};
|
|
45619
|
+
out[3] = {
|
|
45620
|
+
pending: t2.reduce((s3, b2) => s3 + countTokens(b2.summary), 0),
|
|
45621
|
+
targetBlocks: t2
|
|
45622
|
+
};
|
|
45194
45623
|
return out;
|
|
45195
45624
|
}
|
|
45196
45625
|
function decideNudge(input) {
|
|
@@ -45212,475 +45641,186 @@ function decideNudge(input) {
|
|
|
45212
45641
|
);
|
|
45213
45642
|
const growthSinceReference = tokenCount - growthReference;
|
|
45214
45643
|
const rec = recommendation;
|
|
45215
|
-
const tiers = pendingByTier(
|
|
45216
|
-
state,
|
|
45217
|
-
rec,
|
|
45218
|
-
countTokens,
|
|
45219
|
-
config.compress.minCompressRange
|
|
45220
|
-
);
|
|
45221
|
-
const tier2Threshold = Math.round(
|
|
45222
|
-
nudgeGrowthTokens * (config.nudge.tier2GrowthMultiplier ?? 1.5)
|
|
45223
|
-
);
|
|
45224
|
-
let injectedTier = null;
|
|
45225
|
-
let injectedReason = "";
|
|
45226
|
-
const growthReady = growthSinceReference >= growthFloor;
|
|
45227
|
-
const t1Eff = tiers[1]?.pending ?? 0;
|
|
45228
|
-
const t2Pen = tiers[2]?.pending ?? 0;
|
|
45229
|
-
const t3Pen = tiers[3]?.pending ?? 0;
|
|
45230
|
-
if (pressure) {
|
|
45231
|
-
const candidates = [1];
|
|
45232
|
-
if (config.tiers.enabled) {
|
|
45233
|
-
candidates.push(2, 3);
|
|
45234
|
-
}
|
|
45235
|
-
let best = null;
|
|
45236
|
-
let bestPending = 0;
|
|
45237
|
-
for (const t of candidates) {
|
|
45238
|
-
const p2 = tiers[t]?.pending ?? 0;
|
|
45239
|
-
if (p2 > bestPending) {
|
|
45240
|
-
bestPending = p2;
|
|
45241
|
-
best = t;
|
|
45242
|
-
}
|
|
45243
|
-
}
|
|
45244
|
-
if (best !== null && bestPending > 0) {
|
|
45245
|
-
injectedTier = best;
|
|
45246
|
-
const label = emergencyOverride ? "EMERGENCY" : "OVER-LIMIT";
|
|
45247
|
-
injectedReason = best === 1 ? `${label} T1: max effective pending ${bestPending}, usage ${Math.round(usage * 100)}%` : `${label} T${best} distill: max pending ${bestPending} (T1 effective ${t1Eff}, T2 ${t2Pen}, T3 ${t3Pen}), usage ${Math.round(usage * 100)}%`;
|
|
45248
|
-
}
|
|
45249
|
-
} else if (growthReady) {
|
|
45250
|
-
if (t1Eff >= nudgeGrowthTokens) {
|
|
45251
|
-
injectedTier = 1;
|
|
45252
|
-
injectedReason = `T1 effective ${t1Eff} >= ${nudgeGrowthTokens}, growth ${growthSinceReference}, usage ${Math.round(usage * 100)}%`;
|
|
45253
|
-
} else if (config.tiers.enabled && t2Pen >= tier2Threshold && t2Pen > t1Eff) {
|
|
45254
|
-
const lastShown = state.nudge.lastShownByTier[2] ?? 0;
|
|
45255
|
-
const cadenceMet = lastShown === 0 || tokenCount - lastShown >= growthFloor;
|
|
45256
|
-
if (cadenceMet) {
|
|
45257
|
-
injectedTier = 2;
|
|
45258
|
-
injectedReason = `T2 distill ready: ${tiers[2].targetBlocks.length} tier-1 blocks (${t2Pen} tokens) >= ${tier2Threshold} (1.5x) and > T1 effective ${t1Eff}, usage ${Math.round(usage * 100)}%`;
|
|
45259
|
-
}
|
|
45260
|
-
} else if (config.tiers.enabled && t3Pen >= tier2Threshold && t3Pen > t2Pen && t3Pen > t1Eff) {
|
|
45261
|
-
const lastShown = state.nudge.lastShownByTier[3] ?? 0;
|
|
45262
|
-
const cadenceMet = lastShown === 0 || tokenCount - lastShown >= growthFloor;
|
|
45263
|
-
if (cadenceMet) {
|
|
45264
|
-
injectedTier = 3;
|
|
45265
|
-
injectedReason = `T3 condense ready: ${tiers[3].targetBlocks.length} tier-2 blocks (${t3Pen} tokens) >= ${tier2Threshold} (1.5x) and > T2 ${t2Pen} and > T1 effective ${t1Eff}, usage ${Math.round(usage * 100)}%`;
|
|
45266
|
-
}
|
|
45267
|
-
}
|
|
45268
|
-
}
|
|
45269
|
-
const shouldInject = injectedTier !== null;
|
|
45270
|
-
let reason;
|
|
45271
|
-
if (injectedTier !== null) {
|
|
45272
|
-
reason = injectedReason;
|
|
45273
|
-
} else if (pressure) {
|
|
45274
|
-
const label = emergencyOverride ? "EMERGENCY" : "OVER-LIMIT";
|
|
45275
|
-
reason = `${label}: usage ${Math.round(usage * 100)}% but no tier has effective compressible content (T1 effective ${t1Eff}, T2 ${t2Pen}, T3 ${t3Pen}) \u2014 nudge suppressed to avoid offering ranges below minCompressRange`;
|
|
45276
|
-
} else {
|
|
45277
|
-
const tiersList = [1, 2, 3];
|
|
45278
|
-
const eligible = tiersList.filter((t) => config.tiers.enabled || t === 1);
|
|
45279
|
-
const ready = eligible.filter((t) => (tiers[t]?.pending ?? 0) >= nudgeGrowthTokens).map((t) => `T${t} ${tiers[t].pending}`);
|
|
45280
|
-
const readyHint = ready.length > 0 ? `, ready: ${ready.join(", ")}` : "";
|
|
45281
|
-
const blocked = eligible.filter((t) => (tiers[t]?.pending ?? 0) >= nudgeGrowthTokens && (state.nudge.lastShownByTier[t] ?? 0) > 0 && tokenCount - (state.nudge.lastShownByTier[t] ?? 0) < growthFloor).map((t) => `T${t} (cadence)`);
|
|
45282
|
-
const blockedHint = blocked.length > 0 ? `, blocked: ${blocked.join(", ")}` : "";
|
|
45283
|
-
const maxPending = Math.max(0, ...Object.values(tiers).map((t) => t.pending));
|
|
45284
|
-
const pendingShort = maxPending < nudgeGrowthTokens;
|
|
45285
|
-
const growthShort = growthSinceReference < growthFloor;
|
|
45286
|
-
const parts = [];
|
|
45287
|
-
if (pendingShort) parts.push(`max compressible ${maxPending} < threshold ${nudgeGrowthTokens}`);
|
|
45288
|
-
if (growthShort) parts.push(`growth ${growthSinceReference} < floor ${growthFloor}`);
|
|
45289
|
-
if (parts.length === 0) parts.push(`max compressible ${maxPending}, growth ${growthSinceReference}`);
|
|
45290
|
-
reason = `${parts.join("; ")}${readyHint}${blockedHint}`;
|
|
45291
|
-
}
|
|
45292
|
-
const ctxBreakdown = computeContextBreakdown(input.messages, tokenCount, growthSinceReference, countTokens);
|
|
45293
|
-
return {
|
|
45294
|
-
shouldInject,
|
|
45295
|
-
reason,
|
|
45296
|
-
compressibleRanges: rec?.recommendedRanges ?? [],
|
|
45297
|
-
protectedRanges: rec?.contextRanges.protected ?? [],
|
|
45298
|
-
tierTargetBlocks: injectedTier ? tiers[injectedTier].targetBlocks : [],
|
|
45299
|
-
contextUsage: usage,
|
|
45300
|
-
tier: injectedTier,
|
|
45301
|
-
breakdown: {
|
|
45302
|
-
usage,
|
|
45303
|
-
growth: growthSinceReference,
|
|
45304
|
-
growthReference,
|
|
45305
|
-
effectiveThreshold,
|
|
45306
|
-
nudgeGrowthTokens,
|
|
45307
|
-
growthFloor,
|
|
45308
|
-
hasPendingNudge: hasPendingNudge ? 1 : 0,
|
|
45309
|
-
overLimit: overLimit ? 1 : 0,
|
|
45310
|
-
emergencyOverride: emergencyOverride ? 1 : 0,
|
|
45311
|
-
pendingT1: tiers[1].pending,
|
|
45312
|
-
pendingT2: tiers[2].pending,
|
|
45313
|
-
pendingT3: tiers[3].pending
|
|
45314
|
-
},
|
|
45315
|
-
contextBreakdown: ctxBreakdown
|
|
45316
|
-
};
|
|
45317
|
-
}
|
|
45318
|
-
function computeContextBreakdown(messages, total, growth, countTokens) {
|
|
45319
|
-
const count = countTokens ?? ((t) => Math.ceil(t.length / 4));
|
|
45320
|
-
let system = 0, tool = 0, summaries = 0, code = 0, text = 0;
|
|
45321
|
-
for (const msg2 of messages) {
|
|
45322
|
-
const tokens = count(msg2.text ?? "");
|
|
45323
|
-
if (msg2.text?.startsWith("[Compressed conversation section]")) {
|
|
45324
|
-
summaries += tokens;
|
|
45325
|
-
} else if (msg2.contentType === "tool-call" || msg2.contentType === "tool-result") {
|
|
45326
|
-
tool += tokens;
|
|
45327
|
-
} else if (msg2.role === "system") {
|
|
45328
|
-
system += tokens;
|
|
45329
|
-
} else if (msg2.text?.includes("```")) {
|
|
45330
|
-
code += tokens;
|
|
45331
|
-
} else {
|
|
45332
|
-
text += tokens;
|
|
45333
|
-
}
|
|
45334
|
-
}
|
|
45335
|
-
return { system, tool, summaries, code, text, total, growth };
|
|
45336
|
-
}
|
|
45337
|
-
function cloneState(state) {
|
|
45338
|
-
return {
|
|
45339
|
-
blocks: state.blocks.map((block) => ({
|
|
45340
|
-
...block,
|
|
45341
|
-
directMessageIds: [...block.directMessageIds],
|
|
45342
|
-
effectiveMessageIds: [...block.effectiveMessageIds],
|
|
45343
|
-
directBlockIds: [...block.directBlockIds]
|
|
45344
|
-
})),
|
|
45345
|
-
messageRefs: {
|
|
45346
|
-
byRaw: { ...state.messageRefs.byRaw },
|
|
45347
|
-
byRef: { ...state.messageRefs.byRef }
|
|
45348
|
-
},
|
|
45349
|
-
tokenSnapshot: { ...state.tokenSnapshot ?? {} },
|
|
45350
|
-
nudge: { ...state.nudge, anchors: { ...state.nudge.anchors } },
|
|
45351
|
-
stats: { ...state.stats },
|
|
45352
|
-
nextBlockId: state.nextBlockId,
|
|
45353
|
-
nextRunId: state.nextRunId
|
|
45354
|
-
};
|
|
45355
|
-
}
|
|
45356
|
-
function scoreRelevance(block, terms) {
|
|
45357
|
-
const topic = (block.topic ?? "").toLowerCase();
|
|
45358
|
-
const summary = block.summary.toLowerCase();
|
|
45359
|
-
let score = 0;
|
|
45360
|
-
for (const term of terms) {
|
|
45361
|
-
const topicHits = countOccurrences(topic, term);
|
|
45362
|
-
if (topicHits > 0) score += Math.min(topicHits * 0.15, 0.45);
|
|
45363
|
-
const summaryHits = countOccurrences(summary, term);
|
|
45364
|
-
if (summaryHits > 0) score += Math.min(summaryHits * 0.04, 0.2);
|
|
45365
|
-
}
|
|
45366
|
-
return Math.min(score, 1);
|
|
45367
|
-
}
|
|
45368
|
-
function countOccurrences(haystack, needle) {
|
|
45369
|
-
if (!haystack || !needle) return 0;
|
|
45370
|
-
let count = 0;
|
|
45371
|
-
let position = 0;
|
|
45372
|
-
while ((position = haystack.indexOf(needle, position)) !== -1) {
|
|
45373
|
-
count++;
|
|
45374
|
-
position += needle.length;
|
|
45375
|
-
}
|
|
45376
|
-
return count;
|
|
45377
|
-
}
|
|
45378
|
-
var COMPRESS_PHILOSOPHY = `Compression Philosophy:
|
|
45379
|
-
- All compression serves the primary task, but be frugal.
|
|
45380
|
-
- Context capacity is precious. Save context by compressing consumed outputs, not by avoiding tools.
|
|
45381
|
-
- Compress by need, not by percentage.
|
|
45382
|
-
- Work from summaries, not raw tool outputs. All listed ranges (user prompts, tool outputs, code, logs, exploration, intermediate steps) should be compressed to summary format \u2014 the ONLY exceptions are protected content, content the current step is actively using, or critical content you cannot reconstruct.`;
|
|
45383
|
-
var HOW_TO_COMPRESS_RULES = `HOW TO COMPRESS
|
|
45384
|
-
|
|
45385
|
-
When you call \`compress\`, the summary you write becomes the only record of the replaced conversation. Make it self-contained and complete: every user request, experiment purpose, and work task in the range must be accurately captured. A later reader (or you, after decompressing) should be able to continue the task WITHOUT needing the original.
|
|
45386
|
-
|
|
45387
|
-
KEEP VERBATIM \u2014 never paraphrase or abbreviate these:
|
|
45388
|
-
- Full file paths with line numbers, directory prefix on every mention (\`lib/hooks.ts:347\`, \`src/index.ts:12-18\`, \`gatenet_v3/model.py:45\`). Never abbreviate to a bare filename (\`hooks.ts\`, \`model.py\`) \u2014 they are ambiguous and cannot be grepped or decompressed-to later.
|
|
45389
|
-
- Function, class, and type signatures (exact names, params, return types) AND critical code lines that encode logic \u2014 the line that IS the finding, not just the function name (e.g. \`kv_keys += define_gate * a_key[i](emb)\` is more useful than "see model_kvnet.py").
|
|
45390
|
-
- Error messages and stack traces (exact text \u2014 you need the literal string to grep for it later).
|
|
45391
|
-
- Key details from reports and analyses \u2014 not just the conclusion. Keep the comparison numbers and the mechanism, not "X is worse" alone (write "1.76\xD7 PPL gap because KV store is static", not "KVNet underperforms").
|
|
45392
|
-
- Decisions and their rationale ("chose X over Y because Z" \u2014 the "because" is load-bearing; without it the decision looks arbitrary).
|
|
45393
|
-
- Constraints discovered ("must support Node 22", "no new dependencies", "AGENTS.md forbids \`as any\`").
|
|
45394
|
-
- Exact values: versions, config keys, thresholds, magic numbers.
|
|
45395
|
-
- User intent \u2014 quote short user messages verbatim. When the message is too long to quote, preserve intent with extra care: do not change scope, constraints, priorities, acceptance criteria, or requested outcomes. Mark them clearly as past quotes (e.g., "User said: ..."), not as current directives. Losing these changes the task itself.
|
|
45396
|
-
- The user's overall goal and any changes to it \u2014 the big-picture objective plus how it evolved during the compressed range. Each summary must reflect the goal as it stood at the end of the range, including pivots (e.g., "initially: fix bug X \u2192 pivoted to: refactor module Y after discovering root cause"). Losing the goal or its evolution makes all subsequent work appear unmotivated.
|
|
45397
|
-
- Purpose behind each significant action \u2014 preserve not just what was done but why: the hypothesis behind each experiment, the question behind each exploration, the task goal behind each work action. Without purpose, the summary reads as disconnected technical steps with no through-line.
|
|
45398
|
-
- Open questions and unresolved TODOs \u2014 losing these changes what work appears to remain.
|
|
45399
|
-
- Message refs of key anchors (\`m00420\`, \`m00510\u2013m00520\`) \u2014 they let you or a later reader jump back via decompress to the exact original.
|
|
45400
|
-
|
|
45401
|
-
DROP \u2014 extract the signal, discard the vessel:
|
|
45402
|
-
- Verbose logs (build/test/\`npm\` output) once you have captured the error line or the result.
|
|
45403
|
-
- Duplicate file reads once the needed content is recorded.
|
|
45404
|
-
- Consumed exploration \u2014 search hits, agent return values, successful tool outputs \u2014 once you have extracted the facts you need (same rule as dead-ends, but nothing went wrong; the content is simply spent).
|
|
45405
|
-
- Dead-end exploration \u2014 but PRESERVE the lesson in one line: "tried X, failed because Y".
|
|
45406
|
-
- Back-and-forth discussion and self-corrections once the final position is captured (keep the outcome, drop the journey to it).
|
|
45407
|
-
- Repeated status checks (\`git status\`, \`ls\`) once state is known.
|
|
45408
|
-
|
|
45409
|
-
For each significant item you DROP (scripts, reports, large analyses, long tool outputs), add a one-line CONTENT description of what it covers \u2014 not where it lives. Bad: "probe script at /path/probe_kvnet.py". Good: "probe_kvnet.py: tests n-gram baseline, generation quality, long-range dependency, position sensitivity, op pipeline, QUERY attention." This lets a later decompress target the right block by relevance, not by guessing locations.
|
|
45410
|
-
|
|
45411
|
-
PRIORITY \u2014 when the summary must be compact, preserve in this order:
|
|
45412
|
-
1. User's overall goal, goal evolution, intent, and hard constraints (losing these changes the task).
|
|
45413
|
-
2. Decisions and rationale.
|
|
45414
|
-
3. Exact technical artifacts: paths, signatures, errors, values.
|
|
45415
|
-
4. Conclusions and key findings.
|
|
45416
|
-
5. Lessons learned: what failed and why.
|
|
45417
|
-
|
|
45418
|
-
Write dense, scannable bullets \u2014 not narrative prose. If the range spans distinct concerns (request \u2192 findings \u2192 decision), group bullets under short thematic headers so a reader can scan to the part they need. Every line must earn its place. Do not mimic the style of existing summaries in context; follow these rules.`;
|
|
45419
|
-
var TIER2_DISTILL_RULES = `TIER 2 COMPRESSION \u2014 DISTILLATION
|
|
45420
|
-
|
|
45421
|
-
You are compressing historical summaries (not raw conversation). These summaries have already captured the details. Your job is to DISTILL them: extract only what matters for future work, discard the process.
|
|
45422
|
-
|
|
45423
|
-
KEEP \u2014 these are the only things that survive distillation:
|
|
45424
|
-
- Decisions and their rationale ("chose X over Y because Z" \u2014 the "because" is load-bearing).
|
|
45425
|
-
- Final outcomes: version numbers shipped, PR numbers merged/closed, bugs fixed or deferred.
|
|
45426
|
-
- Key lessons: what failed and why ("tried X, failed because Y"). These prevent repeating mistakes.
|
|
45427
|
-
- Critical constraints discovered ("must support Node 22", "AGENTS.md forbids as any").
|
|
45428
|
-
- Design decisions with architectural impact ("chose compress-as-anchor over synthetic messages because prefix cache").
|
|
45429
|
-
- Whether content is OBSOLETE or SUPERSEDED \u2014 mark with one line: "[SUPERSEDED by PR #NNN]" or "[OBSOLETE: deleted in vX.Y.Z]". Do NOT keep the obsolete content's details \u2014 just the marker and reason.
|
|
45430
|
-
- Function/class/type names and module paths that are the SUBJECT of the work \u2014 e.g., "fixed filterCompressedRanges in prune.ts", "added SessionStateRegistry in state.ts". Not exact line numbers or full signatures \u2014 just enough to LOCATE the code without searching.
|
|
45431
|
-
- Exploration findings: if a block was exploratory with no decision, keep the CONCLUSION in one line ("explored X, not viable because Y"). Do not keep the exploration process.
|
|
45432
|
-
|
|
45433
|
-
DROP \u2014 these were useful during the work but are no longer needed:
|
|
45434
|
-
- Exact line numbers, diffs, verbose function signatures, full code listings.
|
|
45435
|
-
- Build/deploy process details, test execution steps.
|
|
45436
|
-
- Review process details (who reviewed, what rounds, test counts).
|
|
45437
|
-
- Verbose logs, command output, intermediate debugging steps.
|
|
45438
|
-
|
|
45439
|
-
FORMAT:
|
|
45440
|
-
- Start each distilled block with a source header line:
|
|
45441
|
-
\`Source: bN+bM+... (XK\u2192YK tok, Zx). [original topic]\`
|
|
45442
|
-
Example: \`Source: b5+b7 (56K+44K\u2192268 tok, 375x). [Tool-result recap + publish]\`
|
|
45443
|
-
- 3-5 bullet points per source block, each a self-contained fact.
|
|
45444
|
-
- Dense, scannable \u2014 no narrative prose.
|
|
45445
|
-
- Start with the outcome, not the process: "v1.13.0 shipped (7 PRs bundled)" not "implemented 7 PRs then reviewed then merged".
|
|
45446
|
-
- Cross-block synthesis: if multiple source blocks cover the same topic (same PR, same feature, same bug), MERGE them into a single group of bullets. Do not repeat the same fact from different blocks \u2014 keep it once under the most relevant source header.
|
|
45447
|
-
|
|
45448
|
-
SIZE TARGET: 50-150 tokens per source block (excluding the header). If you can't fit it in 150 tokens, you're keeping too much process. If a block has nothing worth keeping (pure noise), output just the header followed by "[no actionable content]."`;
|
|
45449
|
-
var TIER3_CONDENSE_RULES = `TIER 3 COMPRESSION \u2014 ULTRA-CONDENSATION
|
|
45450
|
-
|
|
45451
|
-
You are compressing distilled summaries (Tier 2) into ultra-condensed facts (Tier 3). The distilled summaries already contain only decisions and outcomes. Your job is to reduce them to bare factual references.
|
|
45452
|
-
|
|
45453
|
-
PRIORITY \u2014 when a source block has more facts than the size target allows, keep in this order:
|
|
45454
|
-
1. Shipped outcomes (versions released, PRs merged) \u2014 these are permanent record.
|
|
45455
|
-
2. Open work (PRs/issues still pending) \u2014 these may need follow-up.
|
|
45456
|
-
3. Key decisions with architectural impact ("chose X over Y because Z").
|
|
45457
|
-
4. Critical constraints ("must support Node 22").
|
|
45458
|
-
Drop everything else. Tier 3 is a lookup index, not a knowledge base.
|
|
45459
|
-
|
|
45460
|
-
FORMAT:
|
|
45461
|
-
- Start with a source header line:
|
|
45462
|
-
\`Source: bN+bM+... (XK\u2192YK tok, Zx). [original topic]\`
|
|
45463
|
-
- Output 1-3 facts per source block. Each fact is a single line: subject + outcome.
|
|
45464
|
-
- No explanations, no rationale, no process \u2014 just the fact.
|
|
45465
|
-
- Format: "[PR/Issue/Version] \u2014 [outcome in \u22648 words]"
|
|
45466
|
-
- Merge related facts from different source blocks if they concern the same topic.
|
|
45467
|
-
|
|
45468
|
-
EXAMPLES:
|
|
45469
|
-
- "v1.13.0 shipped \u2014 quality gate + GC fix (7 PRs)"
|
|
45470
|
-
- "PR #196 merged \u2014 preserve-first-user (supersedes #169)"
|
|
45471
|
-
- "Bug 1214 fixed \u2014 compress consumed all user messages"
|
|
45472
|
-
- "Chose compress-as-anchor \u2014 prefix cache benefit over synthetic injection"
|
|
45473
|
-
- "Constraint: AGENTS.md forbids as any \u2014 never suppress types"
|
|
45474
|
-
|
|
45475
|
-
DROP:
|
|
45476
|
-
- Multi-sentence context. If a fact needs >1 sentence, it's too detailed for Tier 3.
|
|
45477
|
-
- Lessons learned ("tried X, failed because Y") \u2014 drop UNLESS the failure is likely to recur and the block is <30 days old.
|
|
45478
|
-
- Design rationale details \u2014 keep the decision, drop the "because" unless it's a critical constraint.
|
|
45479
|
-
- Anything marked [OBSOLETE] or [SUPERSEDED] \u2014 drop entirely, note "[N blocks obsolete]" in the summary.
|
|
45480
|
-
|
|
45481
|
-
SIZE TARGET: 30-60 tokens per source block (including header). For a batch of N source blocks, total output \u2248 N \xD7 40 tokens. If a source block has only one trivial fact, output just the header + one line.`;
|
|
45482
|
-
var defaultPrompts = Object.freeze({
|
|
45483
|
-
compressPhilosophy: COMPRESS_PHILOSOPHY,
|
|
45484
|
-
howToCompressRules: HOW_TO_COMPRESS_RULES,
|
|
45485
|
-
tier2DistillRules: TIER2_DISTILL_RULES,
|
|
45486
|
-
tier3CondenseRules: TIER3_CONDENSE_RULES
|
|
45487
|
-
});
|
|
45488
|
-
function resolvePrompts(overrides, options = {}) {
|
|
45489
|
-
const clean = {};
|
|
45490
|
-
if (overrides) {
|
|
45491
|
-
for (const [key, value] of Object.entries(overrides)) {
|
|
45492
|
-
if (typeof value === "string") {
|
|
45493
|
-
clean[key] = value;
|
|
45494
|
-
}
|
|
45495
|
-
}
|
|
45496
|
-
}
|
|
45497
|
-
const keys = Object.keys(clean);
|
|
45498
|
-
if (keys.length > 0 && !options.acknowledgeRisk) {
|
|
45499
|
-
throw new Error(
|
|
45500
|
-
`resolvePrompts: overriding compression rules requires { acknowledgeRisk: true }. Overridden keys: ${keys.join(", ")}. These rules are quality-critical (tuned over months of production use); changing them can degrade summary quality and break retrieval (summaries may lose paths, signatures, decisions).`
|
|
45501
|
-
);
|
|
45502
|
-
}
|
|
45503
|
-
return { ...defaultPrompts, ...clean };
|
|
45504
|
-
}
|
|
45505
|
-
function efficiencyNote(prompts) {
|
|
45506
|
-
return `This is an efficiency nudge to compress early and keep context lean \u2014 not an overflow warning. A separate, stronger alert will appear if the context is actually full.
|
|
45507
|
-
|
|
45508
|
-
${prompts.compressPhilosophy}`;
|
|
45509
|
-
}
|
|
45510
|
-
function emergencyHeader(prompts) {
|
|
45511
|
-
return `\u26A0\uFE0F Context limit reached \u2014 compress now. Prioritize consumed tool outputs.
|
|
45512
|
-
|
|
45513
|
-
${prompts.compressPhilosophy}`;
|
|
45514
|
-
}
|
|
45515
|
-
function formatK(n) {
|
|
45516
|
-
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
45517
|
-
return `${n}`;
|
|
45518
|
-
}
|
|
45519
|
-
function formatBreakdown(bd) {
|
|
45520
|
-
if (!bd) return "";
|
|
45521
|
-
const parts = [];
|
|
45522
|
-
if (bd.system > 0) parts.push(`${formatK(bd.system)} system`);
|
|
45523
|
-
if (bd.tool > 0) parts.push(`${formatK(bd.tool)} tool`);
|
|
45524
|
-
if (bd.summaries > 0) parts.push(`${formatK(bd.summaries)} summaries`);
|
|
45525
|
-
if (bd.code > 0) parts.push(`${formatK(bd.code)} code`);
|
|
45526
|
-
if (bd.text > 0) parts.push(`${formatK(bd.text)} text`);
|
|
45527
|
-
const growth = bd.growth > 0 ? `
|
|
45528
|
-
+${formatK(bd.growth)} since last nudge` : "";
|
|
45529
|
-
return `Context breakdown: ${parts.join(" | ")}${growth}`;
|
|
45530
|
-
}
|
|
45531
|
-
function formatTierTargetBlocks(blocks) {
|
|
45532
|
-
if (blocks.length === 0) {
|
|
45533
|
-
return "Target blocks: (none \u2014 no tier blocks found)";
|
|
45534
|
-
}
|
|
45535
|
-
const lines = blocks.map((b2) => {
|
|
45536
|
-
const summaryTokens = Math.ceil((b2.summary ?? "").length / 4);
|
|
45537
|
-
const topic = b2.topic ? ` "${b2.topic}"` : "";
|
|
45538
|
-
return ` ${b2.blockId} ${b2.effectiveMessageIds.length} msgs ${formatK(b2.compressedTokens)}\u2192${formatK(summaryTokens)}${topic}`;
|
|
45539
|
-
});
|
|
45540
|
-
return `Target ${blocks[0].tier === 1 ? "tier-1" : "tier-2"} blocks to distill (${blocks.length}):
|
|
45541
|
-
${lines.join("\n")}`;
|
|
45542
|
-
}
|
|
45543
|
-
function formatRanges(compressible, protectedRanges) {
|
|
45544
|
-
if (compressible.length === 0 && protectedRanges.length === 0) {
|
|
45545
|
-
return "[No specific ranges detected \u2014 compress any consumed content.]";
|
|
45546
|
-
}
|
|
45547
|
-
const refNum2 = (ref) => {
|
|
45548
|
-
const m2 = ref.match(/\d+/);
|
|
45549
|
-
return m2 ? parseInt(m2[0], 10) : 0;
|
|
45550
|
-
};
|
|
45551
|
-
const entries = [];
|
|
45552
|
-
for (const r of compressible) {
|
|
45553
|
-
entries.push({
|
|
45554
|
-
startRef: r.startRef,
|
|
45555
|
-
endRef: r.endRef,
|
|
45556
|
-
startNum: refNum2(r.startRef),
|
|
45557
|
-
endNum: refNum2(r.endRef),
|
|
45558
|
-
count: r.count,
|
|
45559
|
-
tokens: r.tokens,
|
|
45560
|
-
toolPct: r.toolPct,
|
|
45561
|
-
textPct: r.textPct,
|
|
45562
|
-
compressibleTokens: r.tokens,
|
|
45563
|
-
compressibleCount: r.count,
|
|
45564
|
-
protectedTokens: 0,
|
|
45565
|
-
protectedCount: 0,
|
|
45566
|
-
protectedTools: [],
|
|
45567
|
-
dangerous: r.dangerous ?? false
|
|
45568
|
-
});
|
|
45569
|
-
}
|
|
45570
|
-
for (const r of protectedRanges) {
|
|
45571
|
-
entries.push({
|
|
45572
|
-
startRef: r.startRef,
|
|
45573
|
-
endRef: r.endRef,
|
|
45574
|
-
startNum: refNum2(r.startRef),
|
|
45575
|
-
endNum: refNum2(r.endRef),
|
|
45576
|
-
count: r.count,
|
|
45577
|
-
tokens: r.tokens,
|
|
45578
|
-
toolPct: 0,
|
|
45579
|
-
textPct: 0,
|
|
45580
|
-
compressibleTokens: 0,
|
|
45581
|
-
compressibleCount: 0,
|
|
45582
|
-
protectedTokens: r.tokens,
|
|
45583
|
-
protectedCount: r.count,
|
|
45584
|
-
protectedTools: [...r.tools],
|
|
45585
|
-
dangerous: false
|
|
45586
|
-
});
|
|
45587
|
-
}
|
|
45588
|
-
entries.sort((a, b2) => a.startNum - b2.startNum);
|
|
45589
|
-
const merged = [];
|
|
45590
|
-
for (const e of entries) {
|
|
45591
|
-
const last = merged[merged.length - 1];
|
|
45592
|
-
if (last && e.startNum <= last.endNum + 1) {
|
|
45593
|
-
last.endRef = e.endRef;
|
|
45594
|
-
last.endNum = Math.max(last.endNum, e.endNum);
|
|
45595
|
-
last.count += e.count;
|
|
45596
|
-
last.tokens += e.tokens;
|
|
45597
|
-
last.compressibleTokens += e.compressibleTokens;
|
|
45598
|
-
last.compressibleCount += e.compressibleCount;
|
|
45599
|
-
last.protectedTokens += e.protectedTokens;
|
|
45600
|
-
last.protectedCount += e.protectedCount;
|
|
45601
|
-
if (e.dangerous) last.dangerous = true;
|
|
45602
|
-
for (const t of e.protectedTools) {
|
|
45603
|
-
if (!last.protectedTools.includes(t)) last.protectedTools.push(t);
|
|
45644
|
+
const tiers = pendingByTier(
|
|
45645
|
+
state,
|
|
45646
|
+
rec,
|
|
45647
|
+
countTokens,
|
|
45648
|
+
config.compress.minCompressRange
|
|
45649
|
+
);
|
|
45650
|
+
const tier2Threshold = Math.round(
|
|
45651
|
+
nudgeGrowthTokens * (config.nudge.tier2GrowthMultiplier ?? 1.5)
|
|
45652
|
+
);
|
|
45653
|
+
let injectedTier = null;
|
|
45654
|
+
let injectedReason = "";
|
|
45655
|
+
const growthReady = growthSinceReference >= growthFloor;
|
|
45656
|
+
const t1Eff = tiers[1]?.pending ?? 0;
|
|
45657
|
+
const t2Pen = tiers[2]?.pending ?? 0;
|
|
45658
|
+
const t3Pen = tiers[3]?.pending ?? 0;
|
|
45659
|
+
if (pressure) {
|
|
45660
|
+
const candidates = [1];
|
|
45661
|
+
if (config.tiers.enabled) {
|
|
45662
|
+
candidates.push(2, 3);
|
|
45663
|
+
}
|
|
45664
|
+
let best = null;
|
|
45665
|
+
let bestPending = 0;
|
|
45666
|
+
for (const t of candidates) {
|
|
45667
|
+
const p2 = tiers[t]?.pending ?? 0;
|
|
45668
|
+
if (p2 > bestPending) {
|
|
45669
|
+
bestPending = p2;
|
|
45670
|
+
best = t;
|
|
45604
45671
|
}
|
|
45605
|
-
} else {
|
|
45606
|
-
merged.push({ ...e });
|
|
45607
45672
|
}
|
|
45608
|
-
|
|
45609
|
-
|
|
45610
|
-
|
|
45611
|
-
|
|
45612
|
-
return ` ${e.startRef}\u2013${e.endRef} ${e.count} msgs ${formatK(e.tokens)} [PROTECTED: ${e.protectedTools.join(", ")} \u2014 not compressible]${suffix}`;
|
|
45673
|
+
if (best !== null && bestPending > 0) {
|
|
45674
|
+
injectedTier = best;
|
|
45675
|
+
const label = emergencyOverride ? "EMERGENCY" : "OVER-LIMIT";
|
|
45676
|
+
injectedReason = best === 1 ? `${label} T1: max effective pending ${bestPending}, usage ${Math.round(usage * 100)}%` : `${label} T${best} distill: max pending ${bestPending} (T1 effective ${t1Eff}, T2 ${t2Pen}, T3 ${t3Pen}), usage ${Math.round(usage * 100)}%`;
|
|
45613
45677
|
}
|
|
45614
|
-
|
|
45615
|
-
|
|
45678
|
+
} else if (growthReady) {
|
|
45679
|
+
if (t1Eff >= nudgeGrowthTokens) {
|
|
45680
|
+
injectedTier = 1;
|
|
45681
|
+
injectedReason = `T1 effective ${t1Eff} >= ${nudgeGrowthTokens}, growth ${growthSinceReference}, usage ${Math.round(usage * 100)}%`;
|
|
45682
|
+
} else if (config.tiers.enabled && t2Pen >= tier2Threshold && t2Pen > t1Eff) {
|
|
45683
|
+
const lastShown = state.nudge.lastShownByTier[2] ?? 0;
|
|
45684
|
+
const cadenceMet = lastShown === 0 || tokenCount - lastShown >= growthFloor;
|
|
45685
|
+
if (cadenceMet) {
|
|
45686
|
+
injectedTier = 2;
|
|
45687
|
+
injectedReason = `T2 distill ready: ${tiers[2].targetBlocks.length} tier-1 blocks (${t2Pen} tokens) >= ${tier2Threshold} (1.5x) and > T1 effective ${t1Eff}, usage ${Math.round(usage * 100)}%`;
|
|
45688
|
+
}
|
|
45689
|
+
} else if (config.tiers.enabled && t3Pen >= tier2Threshold && t3Pen > t2Pen && t3Pen > t1Eff) {
|
|
45690
|
+
const lastShown = state.nudge.lastShownByTier[3] ?? 0;
|
|
45691
|
+
const cadenceMet = lastShown === 0 || tokenCount - lastShown >= growthFloor;
|
|
45692
|
+
if (cadenceMet) {
|
|
45693
|
+
injectedTier = 3;
|
|
45694
|
+
injectedReason = `T3 condense ready: ${tiers[3].targetBlocks.length} tier-2 blocks (${t3Pen} tokens) >= ${tier2Threshold} (1.5x) and > T2 ${t2Pen} and > T1 effective ${t1Eff}, usage ${Math.round(usage * 100)}%`;
|
|
45695
|
+
}
|
|
45616
45696
|
}
|
|
45617
|
-
return ` ${e.startRef}\u2013${e.endRef} ${e.count} msgs ${formatK(e.tokens)} [tool ${e.toolPct}% | text ${e.textPct}%]${suffix}`;
|
|
45618
|
-
});
|
|
45619
|
-
return `Compressible ranges (${merged.length}, oldest first):
|
|
45620
|
-
${lines.join("\n")}`;
|
|
45621
|
-
}
|
|
45622
|
-
function renderNudgeText(decision, prompts = defaultPrompts) {
|
|
45623
|
-
const breakdownStr = formatBreakdown(decision.contextBreakdown);
|
|
45624
|
-
const rangesStr = formatRanges(decision.compressibleRanges, decision.protectedRanges ?? []);
|
|
45625
|
-
const isEmergency = !!decision.breakdown?.emergencyOverride || !!decision.breakdown?.overLimit;
|
|
45626
|
-
if (decision.tier !== null && decision.tier >= 2) {
|
|
45627
|
-
const isT2 = decision.tier === 2;
|
|
45628
|
-
const targets = decision.tierTargetBlocks ?? [];
|
|
45629
|
-
const blockList = formatTierTargetBlocks(targets);
|
|
45630
|
-
const startId = targets[0]?.blockId ?? "b1";
|
|
45631
|
-
const endId = targets[targets.length - 1]?.blockId ?? "b5";
|
|
45632
|
-
const voice = isEmergency ? "emergency" : "gentle";
|
|
45633
|
-
const triggerLine = isEmergency ? `[EMERGENCY \u2014 TIER ${decision.tier} ${isT2 ? "DISTILLATION" : "CONDENSATION"}] Context limit reached \u2014 distill NOW into a denser summary to reclaim tokens.` : `[TIER ${decision.tier} ${isT2 ? "DISTILLATION" : "CONDENSATION"} TRIGGER]`;
|
|
45634
|
-
return {
|
|
45635
|
-
voice,
|
|
45636
|
-
text: [
|
|
45637
|
-
efficiencyNote(prompts),
|
|
45638
|
-
"",
|
|
45639
|
-
breakdownStr,
|
|
45640
|
-
"",
|
|
45641
|
-
triggerLine,
|
|
45642
|
-
isT2 ? `Your tier-1 compression summaries have accumulated. Distill them into a single denser tier-2 summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-2 block as well \u2014 apply HOW TO COMPRESS to those raw messages and the TIER 2 distillation rules to the existing summaries, so the whole span is covered and nothing is lost.` : `Your tier-2 compression summaries have accumulated. Condense them further into a tier-3 ultra-condensed summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-3 block as well \u2014 apply HOW TO COMPRESS to those raw messages and the TIER 3 condensation rules to the existing summaries, so the whole span is covered and nothing is lost.`,
|
|
45643
|
-
blockList,
|
|
45644
|
-
`Example: compress({ content: [{ startId: "${startId}", endId: "${endId}", summary: "..." }] })`,
|
|
45645
|
-
"",
|
|
45646
|
-
prompts.howToCompressRules,
|
|
45647
|
-
"",
|
|
45648
|
-
isT2 ? prompts.tier2DistillRules : prompts.tier3CondenseRules
|
|
45649
|
-
].join("\n")
|
|
45650
|
-
};
|
|
45651
45697
|
}
|
|
45652
|
-
|
|
45653
|
-
|
|
45654
|
-
|
|
45655
|
-
|
|
45656
|
-
|
|
45657
|
-
|
|
45658
|
-
|
|
45659
|
-
|
|
45660
|
-
|
|
45661
|
-
|
|
45662
|
-
|
|
45663
|
-
|
|
45664
|
-
|
|
45665
|
-
|
|
45666
|
-
|
|
45667
|
-
};
|
|
45698
|
+
const shouldInject = injectedTier !== null;
|
|
45699
|
+
let reason;
|
|
45700
|
+
if (injectedTier !== null) {
|
|
45701
|
+
reason = injectedReason;
|
|
45702
|
+
} else if (pressure) {
|
|
45703
|
+
const label = emergencyOverride ? "EMERGENCY" : "OVER-LIMIT";
|
|
45704
|
+
reason = `${label}: usage ${Math.round(usage * 100)}% but no tier has effective compressible content (T1 effective ${t1Eff}, T2 ${t2Pen}, T3 ${t3Pen}) \u2014 nudge suppressed to avoid offering ranges below minCompressRange`;
|
|
45705
|
+
} else {
|
|
45706
|
+
const tiersList = [1, 2, 3];
|
|
45707
|
+
const eligible = tiersList.filter((t) => config.tiers.enabled || t === 1);
|
|
45708
|
+
const ready = eligible.filter((t) => (tiers[t]?.pending ?? 0) >= nudgeGrowthTokens).map((t) => `T${t} ${tiers[t].pending}`);
|
|
45709
|
+
const readyHint = ready.length > 0 ? `, ready: ${ready.join(", ")}` : "";
|
|
45710
|
+
const blocked = eligible.filter(
|
|
45711
|
+
(t) => (tiers[t]?.pending ?? 0) >= nudgeGrowthTokens && (state.nudge.lastShownByTier[t] ?? 0) > 0 && tokenCount - (state.nudge.lastShownByTier[t] ?? 0) < growthFloor
|
|
45712
|
+
).map((t) => `T${t} (cadence)`);
|
|
45713
|
+
const blockedHint = blocked.length > 0 ? `, blocked: ${blocked.join(", ")}` : "";
|
|
45714
|
+
const maxPending = Math.max(
|
|
45715
|
+
0,
|
|
45716
|
+
...Object.values(tiers).map((t) => t.pending)
|
|
45717
|
+
);
|
|
45718
|
+
const pendingShort = maxPending < nudgeGrowthTokens;
|
|
45719
|
+
const growthShort = growthSinceReference < growthFloor;
|
|
45720
|
+
const parts = [];
|
|
45721
|
+
if (pendingShort)
|
|
45722
|
+
parts.push(
|
|
45723
|
+
`max compressible ${maxPending} < threshold ${nudgeGrowthTokens}`
|
|
45724
|
+
);
|
|
45725
|
+
if (growthShort)
|
|
45726
|
+
parts.push(`growth ${growthSinceReference} < floor ${growthFloor}`);
|
|
45727
|
+
if (parts.length === 0)
|
|
45728
|
+
parts.push(
|
|
45729
|
+
`max compressible ${maxPending}, growth ${growthSinceReference}`
|
|
45730
|
+
);
|
|
45731
|
+
reason = `${parts.join("; ")}${readyHint}${blockedHint}`;
|
|
45668
45732
|
}
|
|
45733
|
+
const ctxBreakdown = computeContextBreakdown(
|
|
45734
|
+
input.messages,
|
|
45735
|
+
tokenCount,
|
|
45736
|
+
growthSinceReference,
|
|
45737
|
+
countTokens
|
|
45738
|
+
);
|
|
45669
45739
|
return {
|
|
45670
|
-
|
|
45671
|
-
|
|
45672
|
-
|
|
45673
|
-
|
|
45674
|
-
|
|
45675
|
-
|
|
45676
|
-
|
|
45677
|
-
|
|
45678
|
-
|
|
45679
|
-
|
|
45680
|
-
|
|
45681
|
-
|
|
45740
|
+
shouldInject,
|
|
45741
|
+
reason,
|
|
45742
|
+
compressibleRanges: rec?.recommendedRanges ?? [],
|
|
45743
|
+
protectedRanges: rec?.contextRanges.protected ?? [],
|
|
45744
|
+
tierTargetBlocks: injectedTier ? tiers[injectedTier].targetBlocks : [],
|
|
45745
|
+
contextUsage: usage,
|
|
45746
|
+
tier: injectedTier,
|
|
45747
|
+
breakdown: {
|
|
45748
|
+
usage,
|
|
45749
|
+
growth: growthSinceReference,
|
|
45750
|
+
growthReference,
|
|
45751
|
+
effectiveThreshold,
|
|
45752
|
+
nudgeGrowthTokens,
|
|
45753
|
+
growthFloor,
|
|
45754
|
+
hasPendingNudge: hasPendingNudge ? 1 : 0,
|
|
45755
|
+
overLimit: overLimit ? 1 : 0,
|
|
45756
|
+
emergencyOverride: emergencyOverride ? 1 : 0,
|
|
45757
|
+
pendingT1: tiers[1].pending,
|
|
45758
|
+
pendingT2: tiers[2].pending,
|
|
45759
|
+
pendingT3: tiers[3].pending
|
|
45760
|
+
},
|
|
45761
|
+
contextBreakdown: ctxBreakdown
|
|
45762
|
+
};
|
|
45763
|
+
}
|
|
45764
|
+
function computeContextBreakdown(messages, total, growth, countTokens) {
|
|
45765
|
+
const count = countTokens ?? ((t) => Math.ceil(t.length / 4));
|
|
45766
|
+
let system = 0, tool = 0, summaries = 0, code = 0, text = 0;
|
|
45767
|
+
for (const msg2 of messages) {
|
|
45768
|
+
const tokens = count(msg2.text ?? "");
|
|
45769
|
+
if (msg2.text?.startsWith("[Compressed conversation section]")) {
|
|
45770
|
+
summaries += tokens;
|
|
45771
|
+
} else if (msg2.contentType === "tool-call" || msg2.contentType === "tool-result") {
|
|
45772
|
+
tool += tokens;
|
|
45773
|
+
} else if (msg2.role === "system") {
|
|
45774
|
+
system += tokens;
|
|
45775
|
+
} else if (msg2.text?.includes("```")) {
|
|
45776
|
+
code += tokens;
|
|
45777
|
+
} else {
|
|
45778
|
+
text += tokens;
|
|
45779
|
+
}
|
|
45780
|
+
}
|
|
45781
|
+
return { system, tool, summaries, code, text, total, growth };
|
|
45782
|
+
}
|
|
45783
|
+
function cloneState(state) {
|
|
45784
|
+
return {
|
|
45785
|
+
blocks: state.blocks.map((block) => ({
|
|
45786
|
+
...block,
|
|
45787
|
+
directMessageIds: [...block.directMessageIds],
|
|
45788
|
+
effectiveMessageIds: [...block.effectiveMessageIds],
|
|
45789
|
+
directBlockIds: [...block.directBlockIds]
|
|
45790
|
+
})),
|
|
45791
|
+
messageRefs: {
|
|
45792
|
+
byRaw: { ...state.messageRefs.byRaw },
|
|
45793
|
+
byRef: { ...state.messageRefs.byRef }
|
|
45794
|
+
},
|
|
45795
|
+
tokenSnapshot: { ...state.tokenSnapshot ?? {} },
|
|
45796
|
+
nudge: { ...state.nudge, anchors: { ...state.nudge.anchors } },
|
|
45797
|
+
stats: { ...state.stats },
|
|
45798
|
+
nextBlockId: state.nextBlockId,
|
|
45799
|
+
nextRunId: state.nextRunId
|
|
45682
45800
|
};
|
|
45683
45801
|
}
|
|
45802
|
+
function scoreRelevance(block, terms) {
|
|
45803
|
+
const topic = (block.topic ?? "").toLowerCase();
|
|
45804
|
+
const summary = block.summary.toLowerCase();
|
|
45805
|
+
let score = 0;
|
|
45806
|
+
for (const term of terms) {
|
|
45807
|
+
const topicHits = countOccurrences(topic, term);
|
|
45808
|
+
if (topicHits > 0) score += Math.min(topicHits * 0.15, 0.45);
|
|
45809
|
+
const summaryHits = countOccurrences(summary, term);
|
|
45810
|
+
if (summaryHits > 0) score += Math.min(summaryHits * 0.04, 0.2);
|
|
45811
|
+
}
|
|
45812
|
+
return Math.min(score, 1);
|
|
45813
|
+
}
|
|
45814
|
+
function countOccurrences(haystack, needle) {
|
|
45815
|
+
if (!haystack || !needle) return 0;
|
|
45816
|
+
let count = 0;
|
|
45817
|
+
let position = 0;
|
|
45818
|
+
while ((position = haystack.indexOf(needle, position)) !== -1) {
|
|
45819
|
+
count++;
|
|
45820
|
+
position += needle.length;
|
|
45821
|
+
}
|
|
45822
|
+
return count;
|
|
45823
|
+
}
|
|
45684
45824
|
function deactivateBlock(state, blockIds, options = {}) {
|
|
45685
45825
|
const targets = new Set(blockIds);
|
|
45686
45826
|
const updated = state.blocks.map((block) => {
|
|
@@ -47369,6 +47509,33 @@ function safeParse(s3) {
|
|
|
47369
47509
|
return {};
|
|
47370
47510
|
}
|
|
47371
47511
|
}
|
|
47512
|
+
var FORMS = [
|
|
47513
|
+
{ open: "<think>\n", close: "\n</think>" },
|
|
47514
|
+
{ open: "<thinking>\n", close: "\n</thinking>" },
|
|
47515
|
+
{ open: "```thinking\n", close: "\n```" }
|
|
47516
|
+
];
|
|
47517
|
+
function splitDemotedThinking(content) {
|
|
47518
|
+
let rest = content;
|
|
47519
|
+
const parts = [];
|
|
47520
|
+
for (; ; ) {
|
|
47521
|
+
let matched = false;
|
|
47522
|
+
for (const form of FORMS) {
|
|
47523
|
+
if (!rest.startsWith(form.open)) continue;
|
|
47524
|
+
const end = rest.indexOf(form.close, form.open.length);
|
|
47525
|
+
if (end < 0) continue;
|
|
47526
|
+
const inner = rest.slice(form.open.length, end);
|
|
47527
|
+
if (inner.length === 0) continue;
|
|
47528
|
+
parts.push(inner);
|
|
47529
|
+
rest = rest.slice(end + form.close.length);
|
|
47530
|
+
if (rest.startsWith("\n")) rest = rest.slice(1);
|
|
47531
|
+
matched = true;
|
|
47532
|
+
break;
|
|
47533
|
+
}
|
|
47534
|
+
if (!matched) break;
|
|
47535
|
+
}
|
|
47536
|
+
if (parts.length === 0) return null;
|
|
47537
|
+
return { reasoning: parts.join("\n"), text: rest };
|
|
47538
|
+
}
|
|
47372
47539
|
function parseDataUrl(url) {
|
|
47373
47540
|
const m2 = /^data:([^;,]+)(?:;base64)?,(.+)$/i.exec(url);
|
|
47374
47541
|
if (!m2) return void 0;
|
|
@@ -47376,11 +47543,16 @@ function parseDataUrl(url) {
|
|
|
47376
47543
|
}
|
|
47377
47544
|
function openaiToCore(body) {
|
|
47378
47545
|
const msgs = [];
|
|
47546
|
+
const systemParts = [];
|
|
47379
47547
|
const clusters = new ClusterCounter();
|
|
47380
47548
|
for (const m2 of body.messages) {
|
|
47381
47549
|
switch (m2.role) {
|
|
47382
47550
|
case "system":
|
|
47383
47551
|
case "developer": {
|
|
47552
|
+
if (msgs.length === 0) {
|
|
47553
|
+
systemParts.push(stringContent(m2.content));
|
|
47554
|
+
break;
|
|
47555
|
+
}
|
|
47384
47556
|
const base = deriveMessageId(m2.role, "text", stringContent(m2.content));
|
|
47385
47557
|
msgs.push({ id: clusters.next(base), role: "system", contentType: "text", text: stringContent(m2.content), originalRole: m2.role });
|
|
47386
47558
|
break;
|
|
@@ -47402,7 +47574,16 @@ function openaiToCore(body) {
|
|
|
47402
47574
|
break;
|
|
47403
47575
|
}
|
|
47404
47576
|
case "assistant": {
|
|
47405
|
-
const
|
|
47577
|
+
const fieldReasoning = typeof m2.reasoning_content === "string" ? m2.reasoning_content : "";
|
|
47578
|
+
let reasoning = fieldReasoning;
|
|
47579
|
+
let text = stringContent(m2.content);
|
|
47580
|
+
if (!reasoning) {
|
|
47581
|
+
const split = splitDemotedThinking(text);
|
|
47582
|
+
if (split) {
|
|
47583
|
+
reasoning = split.reasoning;
|
|
47584
|
+
text = split.text;
|
|
47585
|
+
}
|
|
47586
|
+
}
|
|
47406
47587
|
if (reasoning) {
|
|
47407
47588
|
const base = deriveMessageId("assistant", "reasoning", reasoning);
|
|
47408
47589
|
msgs.push({
|
|
@@ -47413,7 +47594,6 @@ function openaiToCore(body) {
|
|
|
47413
47594
|
reasoningContent: reasoning
|
|
47414
47595
|
});
|
|
47415
47596
|
}
|
|
47416
|
-
const text = stringContent(m2.content);
|
|
47417
47597
|
if (text) {
|
|
47418
47598
|
const base = deriveMessageId("assistant", "text", text);
|
|
47419
47599
|
msgs.push({ id: clusters.next(base), role: "assistant", contentType: "text", text });
|
|
@@ -47451,7 +47631,7 @@ function openaiToCore(body) {
|
|
|
47451
47631
|
}
|
|
47452
47632
|
}
|
|
47453
47633
|
}
|
|
47454
|
-
return { msgs };
|
|
47634
|
+
return { msgs, systemText: systemParts.join("\n\n") };
|
|
47455
47635
|
}
|
|
47456
47636
|
function coreToOpenai(messages) {
|
|
47457
47637
|
const out = [];
|
|
@@ -47574,6 +47754,22 @@ function partText(part) {
|
|
|
47574
47754
|
function messageContent(content) {
|
|
47575
47755
|
return typeof content === "string" ? content : content.map(partText).join("\n");
|
|
47576
47756
|
}
|
|
47757
|
+
function reasoningText(item) {
|
|
47758
|
+
const fromParts = (parts, type) => {
|
|
47759
|
+
if (!Array.isArray(parts)) return "";
|
|
47760
|
+
const texts = [];
|
|
47761
|
+
for (const part of parts) {
|
|
47762
|
+
if (part && typeof part === "object" && "type" in part && "text" in part) {
|
|
47763
|
+
const rec = part;
|
|
47764
|
+
if (rec.type === type && typeof rec.text === "string") texts.push(rec.text);
|
|
47765
|
+
}
|
|
47766
|
+
}
|
|
47767
|
+
return texts.join("\n");
|
|
47768
|
+
};
|
|
47769
|
+
const content = "content" in item ? item.content : void 0;
|
|
47770
|
+
const summary = "summary" in item ? item.summary : void 0;
|
|
47771
|
+
return fromParts(content, "reasoning_text") || fromParts(summary, "summary_text");
|
|
47772
|
+
}
|
|
47577
47773
|
function responsesToCore(body) {
|
|
47578
47774
|
const msgs = [];
|
|
47579
47775
|
const systemParts = [];
|
|
@@ -47598,7 +47794,8 @@ function responsesToCore(body) {
|
|
|
47598
47794
|
droppedReasoning++;
|
|
47599
47795
|
continue;
|
|
47600
47796
|
}
|
|
47601
|
-
const
|
|
47797
|
+
const text = reasoningText(item);
|
|
47798
|
+
const rid = text.length > 0 ? text : "id" in item && typeof item.id === "string" ? item.id : hashId(JSON.stringify(item));
|
|
47602
47799
|
coreId = clusters.next(deriveMessageId("assistant", "reasoning", rid));
|
|
47603
47800
|
msgs.push({
|
|
47604
47801
|
id: coreId,
|
|
@@ -47618,17 +47815,33 @@ function responsesToCore(body) {
|
|
|
47618
47815
|
continue;
|
|
47619
47816
|
} else if (message.role === "user" || message.role === "assistant" && text) {
|
|
47620
47817
|
const role = message.role;
|
|
47621
|
-
|
|
47622
|
-
|
|
47623
|
-
|
|
47624
|
-
|
|
47625
|
-
|
|
47626
|
-
|
|
47627
|
-
|
|
47628
|
-
|
|
47629
|
-
|
|
47630
|
-
|
|
47631
|
-
|
|
47818
|
+
let effText = text;
|
|
47819
|
+
if (role === "assistant") {
|
|
47820
|
+
const split = splitDemotedThinking(text);
|
|
47821
|
+
if (split) {
|
|
47822
|
+
msgs.push({
|
|
47823
|
+
id: clusters.next(deriveMessageId("assistant", "reasoning", split.reasoning)),
|
|
47824
|
+
role: "assistant",
|
|
47825
|
+
contentType: "reasoning",
|
|
47826
|
+
text: split.reasoning,
|
|
47827
|
+
rawResponsesItem: item
|
|
47828
|
+
});
|
|
47829
|
+
effText = split.text;
|
|
47830
|
+
}
|
|
47831
|
+
}
|
|
47832
|
+
if (effText) {
|
|
47833
|
+
coreId = clusters.next(deriveMessageId(role, "text", effText));
|
|
47834
|
+
const imageUrl = Array.isArray(message.content) ? message.content.find((part) => part.type === "input_image" && typeof part.image_url === "string")?.image_url : void 0;
|
|
47835
|
+
const image = typeof imageUrl === "string" ? parseDataUrl(imageUrl) : void 0;
|
|
47836
|
+
msgs.push({
|
|
47837
|
+
id: coreId,
|
|
47838
|
+
role,
|
|
47839
|
+
contentType: "text",
|
|
47840
|
+
text: effText,
|
|
47841
|
+
rawResponsesItem: item,
|
|
47842
|
+
...image ? { imageMediaType: image.mediaType, imageBase64: image.base64 } : {}
|
|
47843
|
+
});
|
|
47844
|
+
}
|
|
47632
47845
|
}
|
|
47633
47846
|
break;
|
|
47634
47847
|
}
|
|
@@ -48767,7 +48980,7 @@ function applyRanges(ranges, ctx) {
|
|
|
48767
48980
|
try {
|
|
48768
48981
|
const res = ctx.core.applyCompression({
|
|
48769
48982
|
ranges,
|
|
48770
|
-
messages: ctx.messages,
|
|
48983
|
+
messages: ctx.compressMessages ?? ctx.messages,
|
|
48771
48984
|
state: ctx.session.state,
|
|
48772
48985
|
config: ctx.config
|
|
48773
48986
|
});
|
|
@@ -49810,7 +50023,7 @@ async function* iterSseChunks(stream2) {
|
|
|
49810
50023
|
reader.releaseLock();
|
|
49811
50024
|
}
|
|
49812
50025
|
}
|
|
49813
|
-
function createOpenaiAdapter(requestBody) {
|
|
50026
|
+
function createOpenaiAdapter(requestBody, clientSystem) {
|
|
49814
50027
|
const model = requestBody.model ?? "unknown";
|
|
49815
50028
|
let responseId = `chatcmpl-proxy-${Date.now()}`;
|
|
49816
50029
|
let toolIndex = 0;
|
|
@@ -49880,7 +50093,7 @@ function createOpenaiAdapter(requestBody) {
|
|
|
49880
50093
|
return {
|
|
49881
50094
|
buildRequest(coreMessages, systemPrompt, body) {
|
|
49882
50095
|
const messages = coreToOpenai(coreMessages);
|
|
49883
|
-
const withSys = injectOpenaiSystem(messages, [systemPrompt]);
|
|
50096
|
+
const withSys = injectOpenaiSystem(messages, [clientSystem, systemPrompt].filter((p2) => typeof p2 === "string" && p2.length > 0));
|
|
49884
50097
|
return { ...body, messages: withSys };
|
|
49885
50098
|
},
|
|
49886
50099
|
async *parseStream(upstream, _round) {
|
|
@@ -50206,7 +50419,7 @@ ${systemPrompt}` : systemPrompt;
|
|
|
50206
50419
|
yield {
|
|
50207
50420
|
kind: "reasoning",
|
|
50208
50421
|
delta: delta.thinking,
|
|
50209
|
-
|
|
50422
|
+
raw: remapIndexInEvent(eventStr, ci2)
|
|
50210
50423
|
};
|
|
50211
50424
|
} else if (delta.type === "signature_delta" && typeof delta.signature === "string") {
|
|
50212
50425
|
const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
|
|
@@ -50214,7 +50427,7 @@ ${systemPrompt}` : systemPrompt;
|
|
|
50214
50427
|
kind: "reasoning",
|
|
50215
50428
|
delta: "",
|
|
50216
50429
|
signature: delta.signature,
|
|
50217
|
-
|
|
50430
|
+
raw: remapIndexInEvent(eventStr, ci2)
|
|
50218
50431
|
};
|
|
50219
50432
|
} else if (round === 1) {
|
|
50220
50433
|
const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
|
|
@@ -50232,10 +50445,8 @@ ${systemPrompt}` : systemPrompt;
|
|
|
50232
50445
|
arguments: tb.json
|
|
50233
50446
|
};
|
|
50234
50447
|
} else if (thinkingIndexes.delete(upstreamIndex)) {
|
|
50235
|
-
|
|
50236
|
-
|
|
50237
|
-
yield { kind: "meta", chunk: remapIndexInEvent(eventStr, ci2), firstRoundOnly: true };
|
|
50238
|
-
}
|
|
50448
|
+
const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
|
|
50449
|
+
yield { kind: "meta", chunk: remapIndexInEvent(eventStr, ci2), firstRoundOnly: false };
|
|
50239
50450
|
yield { kind: "reasoning", delta: "", blockEnd: true };
|
|
50240
50451
|
} else {
|
|
50241
50452
|
const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
|
|
@@ -50318,9 +50529,9 @@ data: ${JSON.stringify({ type: "content_block_stop", index })}
|
|
|
50318
50529
|
}
|
|
50319
50530
|
|
|
50320
50531
|
// src/loop/index.ts
|
|
50321
|
-
function pickAdapter(protocol, requestBody, textProtocol, responsesProjection, anthropicSystem) {
|
|
50532
|
+
function pickAdapter(protocol, requestBody, textProtocol, responsesProjection, anthropicSystem, openaiSystem) {
|
|
50322
50533
|
if (protocol === "responses") return createResponsesAdapter(textProtocol, responsesProjection);
|
|
50323
|
-
if (protocol === "openai") return createOpenaiAdapter(requestBody);
|
|
50534
|
+
if (protocol === "openai") return createOpenaiAdapter(requestBody, openaiSystem);
|
|
50324
50535
|
if (protocol === "anthropic") return createAnthropicAdapter(requestBody, anthropicSystem);
|
|
50325
50536
|
throw new Error(`[acp-loop] unknown protocol: ${protocol}`);
|
|
50326
50537
|
}
|
|
@@ -52823,6 +53034,7 @@ function prepareAnthropic(parsed, req, opts, core, config, prompts, log2, sessio
|
|
|
52823
53034
|
const tokenCount = session.stats.lastInputTokens;
|
|
52824
53035
|
const turn = core.processTurn({ messages: msgs, state: session.state, config, tokenCount, renderTags: "text-only" });
|
|
52825
53036
|
session.state = turn.state;
|
|
53037
|
+
if (turn.nudge) turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
|
|
52826
53038
|
nudge = turn.nudge;
|
|
52827
53039
|
session.stats.contextTokens = tokenCount;
|
|
52828
53040
|
if (!session.meta.title) {
|
|
@@ -52860,6 +53072,7 @@ function prepareOpenai(parsed, req, opts, core, config, prompts, log2, session,
|
|
|
52860
53072
|
const sessionId = session.id;
|
|
52861
53073
|
const stream2 = parsed.stream === true;
|
|
52862
53074
|
++session.stats.requests;
|
|
53075
|
+
let openaiSystemText = "";
|
|
52863
53076
|
let processedMessages = [];
|
|
52864
53077
|
let originalMessages = [];
|
|
52865
53078
|
let nudge;
|
|
@@ -52870,11 +53083,13 @@ function prepareOpenai(parsed, req, opts, core, config, prompts, log2, session,
|
|
|
52870
53083
|
const shouldInject = opts.compress.injectTool && !isTitleGen;
|
|
52871
53084
|
const injectTools = shouldInject && !pluginMode;
|
|
52872
53085
|
try {
|
|
52873
|
-
const { msgs } = openaiToCore(parsed);
|
|
53086
|
+
const { msgs, systemText } = openaiToCore(parsed);
|
|
53087
|
+
openaiSystemText = systemText;
|
|
52874
53088
|
originalMessages = msgs;
|
|
52875
53089
|
const tokenCount = session.stats.lastInputTokens;
|
|
52876
53090
|
const turn = core.processTurn({ messages: msgs, state: session.state, config, tokenCount, renderTags: "text-only" });
|
|
52877
53091
|
session.state = turn.state;
|
|
53092
|
+
if (turn.nudge) turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
|
|
52878
53093
|
nudge = turn.nudge;
|
|
52879
53094
|
session.stats.contextTokens = tokenCount;
|
|
52880
53095
|
if (!session.meta.title) {
|
|
@@ -52887,6 +53102,7 @@ function prepareOpenai(parsed, req, opts, core, config, prompts, log2, session,
|
|
|
52887
53102
|
reapOrphanBlocks(session, msgs, deactivateBlock);
|
|
52888
53103
|
rebuiltMessages = coreToOpenai(processedMessages);
|
|
52889
53104
|
const sysParts = [];
|
|
53105
|
+
if (systemText) sysParts.push(systemText);
|
|
52890
53106
|
if (shouldInject) sysParts.push(buildCompressSystemPrompt(prompts));
|
|
52891
53107
|
rebuiltMessages = injectOpenaiSystem(rebuiltMessages, sysParts);
|
|
52892
53108
|
if (injectTools) {
|
|
@@ -52911,7 +53127,7 @@ function prepareOpenai(parsed, req, opts, core, config, prompts, log2, session,
|
|
|
52911
53127
|
}
|
|
52912
53128
|
snapshotMessages(session, originalMessages);
|
|
52913
53129
|
markDirty(session);
|
|
52914
|
-
return { body: JSON.stringify(rebuilt), session, processedMessages, originalMessages, protocol: "openai", stream: stream2, compressInjected: injectTools, pluginMode, nudge, prompts };
|
|
53130
|
+
return { body: JSON.stringify(rebuilt), session, processedMessages, originalMessages, protocol: "openai", stream: stream2, compressInjected: injectTools, pluginMode, nudge, prompts, openaiSystemText };
|
|
52915
53131
|
}
|
|
52916
53132
|
function prepareResponses(parsed, req, opts, core, config, prompts, log2, session, identity, pluginMode) {
|
|
52917
53133
|
const sessionId = session.id;
|
|
@@ -52940,6 +53156,7 @@ function prepareResponses(parsed, req, opts, core, config, prompts, log2, sessio
|
|
|
52940
53156
|
const tokenCount = session.stats.lastInputTokens;
|
|
52941
53157
|
const turn = core.processTurn({ messages: msgs, state: session.state, config, tokenCount, renderTags: process.env.ACP_RENDER_NONE ? "none" : "text-only" });
|
|
52942
53158
|
session.state = turn.state;
|
|
53159
|
+
if (turn.nudge) turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
|
|
52943
53160
|
nudge = turn.nudge;
|
|
52944
53161
|
session.stats.contextTokens = tokenCount;
|
|
52945
53162
|
if (!session.meta.title) {
|
|
@@ -53349,10 +53566,10 @@ ${hdrText}
|
|
|
53349
53566
|
const reqHeaders = buildForwardHeaders(headers);
|
|
53350
53567
|
const textProtocol = prepared.protocol === "responses" && !!prepared.responsesTextProtocol;
|
|
53351
53568
|
const systemPrompt = textProtocol ? buildCompressHybridSystemPrompt(prepared.prompts ?? defaultPrompts) : buildCompressSystemPrompt(prepared.prompts ?? defaultPrompts);
|
|
53352
|
-
const adapter = pickAdapter(prepared.protocol, parsedReq, textProtocol, prepared.responsesProjection, prepared.anthropicSystem);
|
|
53569
|
+
const adapter = pickAdapter(prepared.protocol, parsedReq, textProtocol, prepared.responsesProjection, prepared.anthropicSystem, prepared.openaiSystemText);
|
|
53353
53570
|
const loop = runCompressLoop(
|
|
53354
53571
|
streamToRead,
|
|
53355
|
-
{ core, config, messages: prepared.processedMessages.length > 0 ? prepared.processedMessages : prepared.originalMessages, session: prepared.session, log: ctx.log, proxyUrl, protocol: prepared.protocol, textProtocol, debug: opts.debug, nudge: prepared.nudge },
|
|
53572
|
+
{ core, config, messages: prepared.processedMessages.length > 0 ? prepared.processedMessages : prepared.originalMessages, compressMessages: prepared.originalMessages, session: prepared.session, log: ctx.log, proxyUrl, protocol: prepared.protocol, textProtocol, debug: opts.debug, nudge: prepared.nudge },
|
|
53356
53573
|
parsedReq,
|
|
53357
53574
|
{ url: upstreamUrl, headers: reqHeaders },
|
|
53358
53575
|
adapter,
|
|
@@ -57362,8 +57579,9 @@ function codexRemove() {
|
|
|
57362
57579
|
if (start < 0) return `codex: not installed (${file})`;
|
|
57363
57580
|
const lineStart = text.lastIndexOf("\n", start - 1) + 1;
|
|
57364
57581
|
const after = text.slice(start);
|
|
57365
|
-
const
|
|
57366
|
-
const
|
|
57582
|
+
const firstNewline = after.indexOf("\n");
|
|
57583
|
+
const nextTable = firstNewline < 0 ? -1 : after.slice(firstNewline + 1).search(/^[ \t]*\[/m);
|
|
57584
|
+
const end = nextTable >= 0 ? start + firstNewline + 1 + nextTable : text.length;
|
|
57367
57585
|
const cleaned = (text.slice(0, lineStart).replace(/\n+$/, "\n") + text.slice(end)).replace(/^\n+/, "");
|
|
57368
57586
|
backupOnce(file);
|
|
57369
57587
|
fs9.writeFileSync(file, cleaned);
|