opencode-acp 1.17.1 → 1.18.0-pr.392.147
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.d.ts.map +1 -1
- package/dist/index.js +999 -236
- package/dist/index.js.map +1 -1
- package/dist/lib/compress/range-utils.d.ts +16 -1
- package/dist/lib/compress/range-utils.d.ts.map +1 -1
- package/dist/lib/compress/range.d.ts.map +1 -1
- package/dist/lib/compress/search.d.ts +3 -1
- package/dist/lib/compress/search.d.ts.map +1 -1
- package/dist/lib/compress/status.d.ts +1 -1
- package/dist/lib/compress/status.d.ts.map +1 -1
- package/dist/lib/config-validation.d.ts.map +1 -1
- package/dist/lib/config.d.ts +7 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/messages/inject/candidates.d.ts +30 -0
- package/dist/lib/messages/inject/candidates.d.ts.map +1 -0
- package/dist/lib/messages/inject/inject.d.ts +1 -1
- package/dist/lib/messages/inject/inject.d.ts.map +1 -1
- package/dist/lib/messages/sync.d.ts +1 -1
- package/dist/lib/messages/sync.d.ts.map +1 -1
- package/dist/lib/prompts/compress-range.d.ts +9 -1
- package/dist/lib/prompts/compress-range.d.ts.map +1 -1
- package/dist/lib/prompts/context-limit-nudge.d.ts +6 -0
- package/dist/lib/prompts/context-limit-nudge.d.ts.map +1 -1
- package/dist/lib/prompts/iteration-nudge.d.ts +5 -0
- package/dist/lib/prompts/iteration-nudge.d.ts.map +1 -1
- package/dist/lib/prompts/store.d.ts +2 -1
- package/dist/lib/prompts/store.d.ts.map +1 -1
- package/dist/lib/prompts/system.d.ts +10 -1
- package/dist/lib/prompts/system.d.ts.map +1 -1
- package/dist/lib/prompts/turn-nudge.d.ts +5 -0
- package/dist/lib/prompts/turn-nudge.d.ts.map +1 -1
- package/dist/lib/state/rebuild.d.ts +8 -0
- package/dist/lib/state/rebuild.d.ts.map +1 -1
- package/dist/lib/state/state.d.ts.map +1 -1
- package/dist/lib/state/types.d.ts +2 -0
- package/dist/lib/state/types.d.ts.map +1 -1
- package/dist/lib/state/utils.d.ts +1 -0
- package/dist/lib/state/utils.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -885,6 +885,7 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
885
885
|
"compress.permission",
|
|
886
886
|
"compress.showCompression",
|
|
887
887
|
"compress.summaryBuffer",
|
|
888
|
+
"compress.candidates",
|
|
888
889
|
"compress.maxContextLimit",
|
|
889
890
|
"compress.minContextLimit",
|
|
890
891
|
"compress.modelMaxLimits",
|
|
@@ -1759,6 +1760,7 @@ var defaultConfig = {
|
|
|
1759
1760
|
permission: "allow",
|
|
1760
1761
|
showCompression: true,
|
|
1761
1762
|
summaryBuffer: true,
|
|
1763
|
+
candidates: false,
|
|
1762
1764
|
maxContextLimit: "80%",
|
|
1763
1765
|
minContextLimit: "80%",
|
|
1764
1766
|
contextLimitFallback: 128e3,
|
|
@@ -1927,6 +1929,7 @@ function mergeCompress(base, override) {
|
|
|
1927
1929
|
permission: override.permission ?? base.permission,
|
|
1928
1930
|
showCompression: override.showCompression ?? base.showCompression,
|
|
1929
1931
|
summaryBuffer: override.summaryBuffer ?? base.summaryBuffer,
|
|
1932
|
+
candidates: override.candidates ?? base.candidates,
|
|
1930
1933
|
maxContextLimit: override.maxContextLimit ?? base.maxContextLimit,
|
|
1931
1934
|
minContextLimit: override.minContextLimit ?? base.minContextLimit,
|
|
1932
1935
|
modelMaxLimits: override.modelMaxLimits ?? base.modelMaxLimits,
|
|
@@ -2513,12 +2516,12 @@ function serializePruneMessagesState(messagesState) {
|
|
|
2513
2516
|
markedForCleanup: Array.from(messagesState.markedForCleanup)
|
|
2514
2517
|
};
|
|
2515
2518
|
}
|
|
2516
|
-
async function
|
|
2519
|
+
async function getSessionParentId(client, sessionID) {
|
|
2517
2520
|
try {
|
|
2518
2521
|
const result = await client.session.get({ path: { id: sessionID } });
|
|
2519
|
-
return
|
|
2522
|
+
return typeof result.data?.parentID === "string" ? result.data.parentID : void 0;
|
|
2520
2523
|
} catch (error) {
|
|
2521
|
-
return
|
|
2524
|
+
return void 0;
|
|
2522
2525
|
}
|
|
2523
2526
|
}
|
|
2524
2527
|
function findLastCompactionTimestamp(messages) {
|
|
@@ -2559,7 +2562,8 @@ function createPruneMessagesState() {
|
|
|
2559
2562
|
activeByAnchorMessageId: /* @__PURE__ */ new Map(),
|
|
2560
2563
|
nextBlockId: 1,
|
|
2561
2564
|
nextRunId: 1,
|
|
2562
|
-
markedForCleanup: /* @__PURE__ */ new Set()
|
|
2565
|
+
markedForCleanup: /* @__PURE__ */ new Set(),
|
|
2566
|
+
membershipsVerified: false
|
|
2563
2567
|
};
|
|
2564
2568
|
}
|
|
2565
2569
|
function loadPruneMessagesState(persisted) {
|
|
@@ -3691,7 +3695,7 @@ function clampMessageRef(requested, context, state) {
|
|
|
3691
3695
|
if (maxIndex < 0 || requested.index <= maxIndex) return null;
|
|
3692
3696
|
return { ref: formatMessageRef(maxIndex) };
|
|
3693
3697
|
}
|
|
3694
|
-
function resolveSelection(context, startReference, endReference) {
|
|
3698
|
+
function resolveSelection(context, startReference, endReference, options) {
|
|
3695
3699
|
const startRawIndex = startReference.rawIndex;
|
|
3696
3700
|
const endRawIndex = endReference.rawIndex;
|
|
3697
3701
|
const messageIds = [];
|
|
@@ -3714,7 +3718,7 @@ function resolveSelection(context, startReference, endReference) {
|
|
|
3714
3718
|
messageSeen.add(messageId);
|
|
3715
3719
|
messageIds.push(messageId);
|
|
3716
3720
|
}
|
|
3717
|
-
if (!messageTokenById.has(messageId)) {
|
|
3721
|
+
if (options?.includeTokenAccounting !== false && !messageTokenById.has(messageId)) {
|
|
3718
3722
|
messageTokenById.set(messageId, estimateAllMessageTokensFast(rawMessage));
|
|
3719
3723
|
}
|
|
3720
3724
|
const parts = Array.isArray(rawMessage.parts) ? rawMessage.parts : [];
|
|
@@ -3825,10 +3829,10 @@ function buildBoundaryLookup(context, state) {
|
|
|
3825
3829
|
}
|
|
3826
3830
|
return lookup;
|
|
3827
3831
|
}
|
|
3828
|
-
var SEARCH_CONTEXT_TOOL_DESCRIPTION = `Search through
|
|
3832
|
+
var SEARCH_CONTEXT_TOOL_DESCRIPTION = `Search through active compressed block summaries to find relevant content. Use this BEFORE decompressing to find the right block. Returns a hit list with block IDs, relevance scores, and previews.
|
|
3829
3833
|
|
|
3830
3834
|
Examples:
|
|
3831
|
-
- search_context({ query: "decoder accuracy" }) \u2014 find blocks
|
|
3835
|
+
- search_context({ query: "decoder accuracy" }) \u2014 find compressed blocks about decoder accuracy
|
|
3832
3836
|
- search_context({ query: "training loss PPL" }) \u2014 find training results
|
|
3833
3837
|
- search_context({ query: "architecture design", limit: 5 }) \u2014 top 5 results`;
|
|
3834
3838
|
function countOccurrences(text, term) {
|
|
@@ -3859,7 +3863,7 @@ function createSearchContextTool(factoryCtx) {
|
|
|
3859
3863
|
query: tool.schema.string().describe("Search query \u2014 keywords or phrase to find"),
|
|
3860
3864
|
limit: tool.schema.number().optional().describe("Maximum results to return (default: 10)"),
|
|
3861
3865
|
deep: tool.schema.boolean().optional().describe(
|
|
3862
|
-
"
|
|
3866
|
+
"Reserved for compatibility; the current search indexes active compressed block summaries."
|
|
3863
3867
|
)
|
|
3864
3868
|
},
|
|
3865
3869
|
async execute(args, toolCtx) {
|
|
@@ -4311,7 +4315,7 @@ function validateArgs(args) {
|
|
|
4311
4315
|
}
|
|
4312
4316
|
}
|
|
4313
4317
|
}
|
|
4314
|
-
function resolveRanges(args, searchContext, state, logger) {
|
|
4318
|
+
function resolveRanges(args, searchContext, state, logger, options) {
|
|
4315
4319
|
return args.content.map((entry, index) => {
|
|
4316
4320
|
const normalizedEntry = {
|
|
4317
4321
|
topic: typeof entry.topic === "string" && entry.topic.trim().length > 0 ? entry.topic.trim() : void 0,
|
|
@@ -4326,7 +4330,7 @@ function resolveRanges(args, searchContext, state, logger) {
|
|
|
4326
4330
|
normalizedEntry.endId,
|
|
4327
4331
|
logger
|
|
4328
4332
|
);
|
|
4329
|
-
const selection = resolveSelection(searchContext, startReference, endReference);
|
|
4333
|
+
const selection = resolveSelection(searchContext, startReference, endReference, options);
|
|
4330
4334
|
return {
|
|
4331
4335
|
index,
|
|
4332
4336
|
entry: normalizedEntry,
|
|
@@ -4359,6 +4363,54 @@ function validateNonOverlapping(plans) {
|
|
|
4359
4363
|
);
|
|
4360
4364
|
}
|
|
4361
4365
|
}
|
|
4366
|
+
function prepareExecutableRangePlans(args, searchContext, state, config, logger, options) {
|
|
4367
|
+
const normalizedArgs = "content" in args ? args : {
|
|
4368
|
+
content: [args]
|
|
4369
|
+
};
|
|
4370
|
+
const resolvedPlans = resolveRanges(normalizedArgs, searchContext, state, logger, options);
|
|
4371
|
+
validateNonOverlapping(resolvedPlans);
|
|
4372
|
+
const plans = resolvedPlans.map((plan) => ({
|
|
4373
|
+
...plan,
|
|
4374
|
+
selection: filterProtectedToolMessages(
|
|
4375
|
+
plan.selection,
|
|
4376
|
+
searchContext,
|
|
4377
|
+
config.compress.protectedTools,
|
|
4378
|
+
config.protectedFilePatterns
|
|
4379
|
+
)
|
|
4380
|
+
})).map((plan) => ({
|
|
4381
|
+
...plan,
|
|
4382
|
+
selection: filterLastUserMessage(plan.selection, searchContext, state, config.compress)
|
|
4383
|
+
})).map((plan) => ({
|
|
4384
|
+
...plan,
|
|
4385
|
+
selection: filterProtectedRecentMessages(
|
|
4386
|
+
plan.selection,
|
|
4387
|
+
searchContext,
|
|
4388
|
+
state,
|
|
4389
|
+
config.compress
|
|
4390
|
+
)
|
|
4391
|
+
})).filter((plan) => plan.selection.messageIds.length > 0);
|
|
4392
|
+
if (plans.length === 0) {
|
|
4393
|
+
throw new Error(
|
|
4394
|
+
"All selected messages were filtered out (protected tool outputs and/or the last user message). They must remain in visible context."
|
|
4395
|
+
);
|
|
4396
|
+
}
|
|
4397
|
+
const counted = /* @__PURE__ */ new Set();
|
|
4398
|
+
let totalChars = 0;
|
|
4399
|
+
for (const plan of plans) {
|
|
4400
|
+
for (const messageId of plan.selection.messageIds) {
|
|
4401
|
+
if (counted.has(messageId)) continue;
|
|
4402
|
+
counted.add(messageId);
|
|
4403
|
+
const rawMessage = searchContext.rawMessagesById.get(messageId);
|
|
4404
|
+
if (rawMessage) totalChars += countMessageCharacters(rawMessage);
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4407
|
+
if (config.compress.minCompressRange > 0 && totalChars < config.compress.minCompressRange) {
|
|
4408
|
+
throw new Error(
|
|
4409
|
+
`Range too small (${totalChars} chars, min ${config.compress.minCompressRange}). Not worth compressing \u2014 overhead exceeds savings.`
|
|
4410
|
+
);
|
|
4411
|
+
}
|
|
4412
|
+
return { plans, totalChars };
|
|
4413
|
+
}
|
|
4362
4414
|
function parseBlockPlaceholders(summary) {
|
|
4363
4415
|
const placeholders = [];
|
|
4364
4416
|
const regex = new RegExp(BLOCK_PLACEHOLDER_REGEX);
|
|
@@ -4430,6 +4482,7 @@ function appendMissingBlockSummaries(summary, _missingBlockIds, _summaryByBlockI
|
|
|
4430
4482
|
}
|
|
4431
4483
|
|
|
4432
4484
|
// lib/state/rebuild.ts
|
|
4485
|
+
import { createHash } from "crypto";
|
|
4433
4486
|
function collectCompressInvocations(messages) {
|
|
4434
4487
|
const invocations = [];
|
|
4435
4488
|
for (const message of messages) {
|
|
@@ -4481,6 +4534,128 @@ function dedupeBlockIds(ids) {
|
|
|
4481
4534
|
}
|
|
4482
4535
|
return result;
|
|
4483
4536
|
}
|
|
4537
|
+
function fingerprintMessage(message) {
|
|
4538
|
+
const parts = (message.parts ?? []).map((part) => {
|
|
4539
|
+
if (part.type === "text") {
|
|
4540
|
+
return { type: "text", text: part.text };
|
|
4541
|
+
}
|
|
4542
|
+
if (part.type === "tool") {
|
|
4543
|
+
if (part.tool === "compress") {
|
|
4544
|
+
return { type: "tool", tool: part.tool, status: part.state?.status };
|
|
4545
|
+
}
|
|
4546
|
+
return {
|
|
4547
|
+
type: "tool",
|
|
4548
|
+
tool: part.tool,
|
|
4549
|
+
status: part.state?.status,
|
|
4550
|
+
input: part.state?.input,
|
|
4551
|
+
output: part.state?.status === "completed" ? part.state.output : void 0
|
|
4552
|
+
};
|
|
4553
|
+
}
|
|
4554
|
+
return { type: part.type };
|
|
4555
|
+
});
|
|
4556
|
+
return createHash("sha256").update(JSON.stringify({ role: message.info.role, parts })).digest("hex");
|
|
4557
|
+
}
|
|
4558
|
+
function mapForkIds(state, parent, parentMessages, forkMessages) {
|
|
4559
|
+
const parentRefs = parent.messageIds?.byRef;
|
|
4560
|
+
if (!parentRefs) return null;
|
|
4561
|
+
const parentById = new Map(parentMessages.map((message) => [message.info.id, message]));
|
|
4562
|
+
const forkById = new Map(forkMessages.map((message) => [message.info.id, message]));
|
|
4563
|
+
const messages = /* @__PURE__ */ new Map();
|
|
4564
|
+
const tools = /* @__PURE__ */ new Map();
|
|
4565
|
+
for (const [ref, parentId] of Object.entries(parentRefs)) {
|
|
4566
|
+
const forkId = state.messageIds.byRef.get(ref);
|
|
4567
|
+
if (!forkId) continue;
|
|
4568
|
+
const parentMessage = parentById.get(parentId);
|
|
4569
|
+
const forkMessage = forkById.get(forkId);
|
|
4570
|
+
if (!parentMessage || !forkMessage) continue;
|
|
4571
|
+
if (fingerprintMessage(parentMessage) !== fingerprintMessage(forkMessage)) continue;
|
|
4572
|
+
messages.set(parentId, forkId);
|
|
4573
|
+
for (let index = 0; index < parentMessage.parts.length; index++) {
|
|
4574
|
+
const parentPart = parentMessage.parts[index];
|
|
4575
|
+
const forkPart = forkMessage.parts[index];
|
|
4576
|
+
if (parentPart?.type === "tool" && typeof parentPart.callID === "string" && forkPart?.type === "tool" && forkPart.tool === parentPart.tool && typeof forkPart.callID === "string") {
|
|
4577
|
+
tools.set(parentPart.callID, forkPart.callID);
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4581
|
+
return { messages, tools };
|
|
4582
|
+
}
|
|
4583
|
+
function translateIds(ids, mapped) {
|
|
4584
|
+
const translated = ids.map((id) => mapped.get(id));
|
|
4585
|
+
return translated.some((id) => !id) ? null : translated;
|
|
4586
|
+
}
|
|
4587
|
+
function restoreForkCompressionState(state, forkMessages, parent, parentMessages, logger) {
|
|
4588
|
+
if (!parent.prune.messages || !parent.messageIds) return 0;
|
|
4589
|
+
assignMessageRefs(state, forkMessages);
|
|
4590
|
+
const mapped = mapForkIds(state, parent, parentMessages, forkMessages);
|
|
4591
|
+
if (!mapped) return 0;
|
|
4592
|
+
const parentBlocks = Object.values(parent.prune.messages.blocksById);
|
|
4593
|
+
const translatedBlocks = /* @__PURE__ */ new Map();
|
|
4594
|
+
for (const block of parentBlocks) {
|
|
4595
|
+
const anchorMessageId = mapped.messages.get(block.anchorMessageId);
|
|
4596
|
+
const compressMessageId = mapped.messages.get(block.compressMessageId);
|
|
4597
|
+
const directMessageIds = translateIds(block.directMessageIds, mapped.messages);
|
|
4598
|
+
const effectiveMessageIds = translateIds(block.effectiveMessageIds, mapped.messages);
|
|
4599
|
+
const directToolIds = translateIds(block.directToolIds, mapped.tools);
|
|
4600
|
+
const effectiveToolIds = translateIds(block.effectiveToolIds, mapped.tools);
|
|
4601
|
+
const compressCallId = block.compressCallId ? mapped.tools.get(block.compressCallId) : void 0;
|
|
4602
|
+
if (!anchorMessageId || !compressMessageId || !directMessageIds || !effectiveMessageIds || !directToolIds || !effectiveToolIds || block.compressCallId && !compressCallId) {
|
|
4603
|
+
continue;
|
|
4604
|
+
}
|
|
4605
|
+
translatedBlocks.set(block.blockId, {
|
|
4606
|
+
...block,
|
|
4607
|
+
anchorMessageId,
|
|
4608
|
+
compressMessageId,
|
|
4609
|
+
compressCallId,
|
|
4610
|
+
directMessageIds,
|
|
4611
|
+
directToolIds,
|
|
4612
|
+
effectiveMessageIds,
|
|
4613
|
+
effectiveToolIds
|
|
4614
|
+
});
|
|
4615
|
+
}
|
|
4616
|
+
const activeParentIds = new Set(parent.prune.messages.activeBlockIds);
|
|
4617
|
+
if (activeParentIds.size === 0 || Array.from(activeParentIds).some((blockId) => !translatedBlocks.has(blockId))) {
|
|
4618
|
+
return 0;
|
|
4619
|
+
}
|
|
4620
|
+
const copiedBlockIds = new Set(translatedBlocks.keys());
|
|
4621
|
+
for (const block of translatedBlocks.values()) {
|
|
4622
|
+
if (block.active && [...block.consumedBlockIds, ...block.includedBlockIds].some(
|
|
4623
|
+
(blockId) => !copiedBlockIds.has(blockId)
|
|
4624
|
+
)) {
|
|
4625
|
+
return 0;
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
const messagesState = createPruneMessagesState();
|
|
4629
|
+
for (const block of translatedBlocks.values()) {
|
|
4630
|
+
messagesState.blocksById.set(block.blockId, block);
|
|
4631
|
+
if (block.active) {
|
|
4632
|
+
messagesState.activeBlockIds.add(block.blockId);
|
|
4633
|
+
messagesState.activeByAnchorMessageId.set(block.anchorMessageId, block.blockId);
|
|
4634
|
+
}
|
|
4635
|
+
}
|
|
4636
|
+
for (const [parentMessageId, entry] of Object.entries(parent.prune.messages.byMessageId)) {
|
|
4637
|
+
const forkMessageId = mapped.messages.get(parentMessageId);
|
|
4638
|
+
if (!forkMessageId) continue;
|
|
4639
|
+
const allBlockIds = entry.allBlockIds.filter((blockId) => copiedBlockIds.has(blockId));
|
|
4640
|
+
if (allBlockIds.length === 0) continue;
|
|
4641
|
+
messagesState.byMessageId.set(forkMessageId, {
|
|
4642
|
+
tokenCount: entry.tokenCount,
|
|
4643
|
+
allBlockIds,
|
|
4644
|
+
activeBlockIds: allBlockIds.filter(
|
|
4645
|
+
(blockId) => messagesState.activeBlockIds.has(blockId)
|
|
4646
|
+
)
|
|
4647
|
+
});
|
|
4648
|
+
}
|
|
4649
|
+
messagesState.nextBlockId = Math.max(parent.prune.messages.nextBlockId, ...copiedBlockIds) + 1;
|
|
4650
|
+
messagesState.nextRunId = parent.prune.messages.nextRunId;
|
|
4651
|
+
messagesState.membershipsVerified = true;
|
|
4652
|
+
state.prune.messages = messagesState;
|
|
4653
|
+
logger.info("fork: restored compression state from parent", {
|
|
4654
|
+
blocks: copiedBlockIds.size,
|
|
4655
|
+
activeBlocks: messagesState.activeBlockIds.size
|
|
4656
|
+
});
|
|
4657
|
+
return copiedBlockIds.size;
|
|
4658
|
+
}
|
|
4484
4659
|
function rebuildRangeInvocation(state, input, searchContext, invocation, protectedTools, protectedFilePatterns, gcConfig, logger) {
|
|
4485
4660
|
const plans = resolveRanges(input, searchContext, state);
|
|
4486
4661
|
const runId = allocateRunId(state);
|
|
@@ -4547,12 +4722,7 @@ function resolveMessageEntry(entry, searchContext, state) {
|
|
|
4547
4722
|
return null;
|
|
4548
4723
|
}
|
|
4549
4724
|
try {
|
|
4550
|
-
const { startReference, endReference } = resolveBoundaryIds(
|
|
4551
|
-
searchContext,
|
|
4552
|
-
state,
|
|
4553
|
-
ref,
|
|
4554
|
-
ref
|
|
4555
|
-
);
|
|
4725
|
+
const { startReference, endReference } = resolveBoundaryIds(searchContext, state, ref, ref);
|
|
4556
4726
|
const selection = resolveSelection(searchContext, startReference, endReference);
|
|
4557
4727
|
return {
|
|
4558
4728
|
selection,
|
|
@@ -4856,8 +5026,9 @@ async function ensureSessionInitialized(client, state, sessionId, logger, messag
|
|
|
4856
5026
|
resetSessionState(state);
|
|
4857
5027
|
state.sessionId = sessionId;
|
|
4858
5028
|
state.storageDir = config?.storagePath ? resolveStorageDir(config.storagePath, projectDir ?? cwd()) : void 0;
|
|
4859
|
-
const
|
|
4860
|
-
|
|
5029
|
+
const parentSessionId = await getSessionParentId(client, sessionId);
|
|
5030
|
+
const isChildSession = parentSessionId !== void 0;
|
|
5031
|
+
state.isSubAgent = isChildSession;
|
|
4861
5032
|
state.lastCompaction = findLastCompactionTimestamp(messages);
|
|
4862
5033
|
state.currentTurn = countTurns(state, messages);
|
|
4863
5034
|
state.nudges.turnNudgeAnchors = collectTurnNudgeAnchors(messages);
|
|
@@ -4871,13 +5042,42 @@ async function ensureSessionInitialized(client, state, sessionId, logger, messag
|
|
|
4871
5042
|
);
|
|
4872
5043
|
}
|
|
4873
5044
|
if (config) {
|
|
4874
|
-
|
|
4875
|
-
if (
|
|
5045
|
+
let restored = 0;
|
|
5046
|
+
if (parentSessionId) {
|
|
5047
|
+
try {
|
|
5048
|
+
const parent = await loadSessionState(parentSessionId, logger);
|
|
5049
|
+
const response = parent ? await client.session.messages({ path: { id: parentSessionId } }) : void 0;
|
|
5050
|
+
const parentMessages = Array.isArray(response?.data) ? response.data : Array.isArray(response) ? response : [];
|
|
5051
|
+
if (parent && parentMessages.length > 0) {
|
|
5052
|
+
state.isSubAgent = false;
|
|
5053
|
+
try {
|
|
5054
|
+
restored = restoreForkCompressionState(
|
|
5055
|
+
state,
|
|
5056
|
+
messages,
|
|
5057
|
+
parent,
|
|
5058
|
+
parentMessages,
|
|
5059
|
+
logger
|
|
5060
|
+
);
|
|
5061
|
+
} finally {
|
|
5062
|
+
state.isSubAgent = isChildSession;
|
|
5063
|
+
}
|
|
5064
|
+
}
|
|
5065
|
+
} catch (error) {
|
|
5066
|
+
logger.warn("fork: parent state transfer unavailable, replaying history", {
|
|
5067
|
+
parentSessionId,
|
|
5068
|
+
error: error?.message
|
|
5069
|
+
});
|
|
5070
|
+
}
|
|
5071
|
+
}
|
|
5072
|
+
const rebuilt = restored > 0 ? 0 : rebuildCompressionState(state, messages, config, logger);
|
|
5073
|
+
if (restored > 0 || rebuilt > 0) {
|
|
4876
5074
|
await saveSessionState(state, logger);
|
|
4877
5075
|
}
|
|
4878
5076
|
}
|
|
5077
|
+
state.isSubAgent = isChildSession;
|
|
4879
5078
|
return;
|
|
4880
5079
|
}
|
|
5080
|
+
state.isSubAgent = isChildSession;
|
|
4881
5081
|
state.prune.messages = loadPruneMessagesState(persisted.prune.messages);
|
|
4882
5082
|
state.nudges.contextLimitAnchors = new Set(persisted.nudges.contextLimitAnchors || []);
|
|
4883
5083
|
state.nudges.turnNudgeAnchors = /* @__PURE__ */ new Set([
|
|
@@ -6588,12 +6788,12 @@ ${afterClose}`;
|
|
|
6588
6788
|
}
|
|
6589
6789
|
|
|
6590
6790
|
// lib/messages/utils.ts
|
|
6591
|
-
import { createHash } from "crypto";
|
|
6791
|
+
import { createHash as createHash2 } from "crypto";
|
|
6592
6792
|
var SUMMARY_ID_HASH_LENGTH = 16;
|
|
6593
6793
|
var DCP_PAIRED_TAG_REGEX = /<(?:dcp|acp)[^>]*>[\s\S]*?<\/(?:dcp|acp)[^>]*>/gi;
|
|
6594
6794
|
var DCP_UNPAIRED_TAG_REGEX = /<\/?(?:dcp|acp)[^>]*>/gi;
|
|
6595
6795
|
var generateStableId = (prefix, seed) => {
|
|
6596
|
-
const hash =
|
|
6796
|
+
const hash = createHash2("sha256").update(seed).digest("hex").slice(0, SUMMARY_ID_HASH_LENGTH);
|
|
6597
6797
|
return `${prefix}_${hash}`;
|
|
6598
6798
|
};
|
|
6599
6799
|
var createSyntheticMessage = (baseMessage, content, stableSeed, role = "user") => {
|
|
@@ -7590,58 +7790,13 @@ function createCompressRangeTool(factoryCtx) {
|
|
|
7590
7790
|
...ctx0,
|
|
7591
7791
|
config: applyCompressOverrides(ctx0.config, providerId, modelId)
|
|
7592
7792
|
};
|
|
7593
|
-
const
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
ctx.config.compress.protectedTools,
|
|
7601
|
-
ctx.config.protectedFilePatterns
|
|
7602
|
-
)
|
|
7603
|
-
})).map((plan) => ({
|
|
7604
|
-
...plan,
|
|
7605
|
-
selection: filterLastUserMessage(
|
|
7606
|
-
plan.selection,
|
|
7607
|
-
searchContext,
|
|
7608
|
-
ctx.state,
|
|
7609
|
-
ctx.config.compress
|
|
7610
|
-
)
|
|
7611
|
-
})).map((plan) => ({
|
|
7612
|
-
...plan,
|
|
7613
|
-
selection: filterProtectedRecentMessages(
|
|
7614
|
-
plan.selection,
|
|
7615
|
-
searchContext,
|
|
7616
|
-
ctx.state,
|
|
7617
|
-
ctx.config.compress
|
|
7618
|
-
)
|
|
7619
|
-
})).filter((plan) => plan.selection.messageIds.length > 0);
|
|
7620
|
-
if (filteredPlans.length === 0) {
|
|
7621
|
-
throw new Error(
|
|
7622
|
-
"All selected messages were filtered out (protected tool outputs and/or the last user message). They must remain in visible context."
|
|
7623
|
-
);
|
|
7624
|
-
}
|
|
7625
|
-
const minCompressRange = ctx.config.compress.minCompressRange;
|
|
7626
|
-
if (minCompressRange > 0) {
|
|
7627
|
-
let totalChars = 0;
|
|
7628
|
-
const counted = /* @__PURE__ */ new Set();
|
|
7629
|
-
for (const plan of filteredPlans) {
|
|
7630
|
-
for (const messageId of plan.selection.messageIds) {
|
|
7631
|
-
if (counted.has(messageId)) continue;
|
|
7632
|
-
counted.add(messageId);
|
|
7633
|
-
const rawMessage = searchContext.rawMessagesById.get(messageId);
|
|
7634
|
-
if (rawMessage) {
|
|
7635
|
-
totalChars += countMessageCharacters(rawMessage);
|
|
7636
|
-
}
|
|
7637
|
-
}
|
|
7638
|
-
}
|
|
7639
|
-
if (totalChars < minCompressRange) {
|
|
7640
|
-
throw new Error(
|
|
7641
|
-
`Range too small (${totalChars} chars, min ${minCompressRange}). Not worth compressing \u2014 overhead exceeds savings.`
|
|
7642
|
-
);
|
|
7643
|
-
}
|
|
7644
|
-
}
|
|
7793
|
+
const { plans: filteredPlans } = prepareExecutableRangePlans(
|
|
7794
|
+
input,
|
|
7795
|
+
searchContext,
|
|
7796
|
+
ctx.state,
|
|
7797
|
+
ctx.config,
|
|
7798
|
+
ctx.logger
|
|
7799
|
+
);
|
|
7645
7800
|
const notifications = [];
|
|
7646
7801
|
let preparedPlans = [];
|
|
7647
7802
|
let totalCompressedMessages = 0;
|
|
@@ -7746,7 +7901,9 @@ function createCompressRangeTool(factoryCtx) {
|
|
|
7746
7901
|
const bypassQuality = acknowledgeRisk && ctx.state.qualityGateRetryPending;
|
|
7747
7902
|
const ignoredAcknowledgeRisk = acknowledgeRisk && !ctx.state.qualityGateRetryPending;
|
|
7748
7903
|
if (ignoredAcknowledgeRisk) {
|
|
7749
|
-
ctx.logger.warn(
|
|
7904
|
+
ctx.logger.warn(
|
|
7905
|
+
"compress: acknowledgeRisk ignored \u2014 no quality gate rejection pending"
|
|
7906
|
+
);
|
|
7750
7907
|
}
|
|
7751
7908
|
ctx.state.qualityGateRetryPending = false;
|
|
7752
7909
|
if (!bypassQuality) {
|
|
@@ -7916,10 +8073,24 @@ function sortBlocksByCreation(a, b) {
|
|
|
7916
8073
|
}
|
|
7917
8074
|
return a.blockId - b.blockId;
|
|
7918
8075
|
}
|
|
8076
|
+
function sameBlockIds(left, right) {
|
|
8077
|
+
if (left.size !== right.size) return false;
|
|
8078
|
+
return Array.from(left).every((id) => right.has(id));
|
|
8079
|
+
}
|
|
7919
8080
|
var syncCompressionBlocks = (state, logger, messages) => {
|
|
7920
8081
|
const messagesState = state.prune.messages;
|
|
7921
8082
|
if (!messagesState?.blocksById?.size) {
|
|
7922
|
-
|
|
8083
|
+
if (!messagesState.membershipsVerified) {
|
|
8084
|
+
messagesState.activeBlockIds.clear();
|
|
8085
|
+
messagesState.activeByAnchorMessageId.clear();
|
|
8086
|
+
for (const entry of messagesState.byMessageId.values()) {
|
|
8087
|
+
entry.allBlockIds = Array.isArray(entry.allBlockIds) ? [...new Set(entry.allBlockIds.filter((id) => Number.isInteger(id) && id > 0))] : [];
|
|
8088
|
+
entry.activeBlockIds = [];
|
|
8089
|
+
}
|
|
8090
|
+
messagesState.membershipsVerified = true;
|
|
8091
|
+
return true;
|
|
8092
|
+
}
|
|
8093
|
+
return false;
|
|
7923
8094
|
}
|
|
7924
8095
|
const messageIds = new Set(messages.map((msg) => msg.info.id));
|
|
7925
8096
|
const structureVersion = messagesState.structureVersion ?? 0;
|
|
@@ -7937,11 +8108,12 @@ var syncCompressionBlocks = (state, logger, messages) => {
|
|
|
7937
8108
|
previous.set(anchor, id);
|
|
7938
8109
|
}
|
|
7939
8110
|
}
|
|
7940
|
-
return;
|
|
8111
|
+
return false;
|
|
7941
8112
|
}
|
|
7942
8113
|
const previousActiveBlockIds = new Set(
|
|
7943
8114
|
Array.from(messagesState.blocksById.values()).filter((block) => block.active).map((block) => block.blockId)
|
|
7944
8115
|
);
|
|
8116
|
+
const indexedActiveBlockIds = new Set(messagesState.activeBlockIds);
|
|
7945
8117
|
messagesState.activeBlockIds.clear();
|
|
7946
8118
|
messagesState.activeByAnchorMessageId.clear();
|
|
7947
8119
|
const now = Date.now();
|
|
@@ -7981,10 +8153,14 @@ var syncCompressionBlocks = (state, logger, messages) => {
|
|
|
7981
8153
|
messagesState.activeByAnchorMessageId.set(block.anchorMessageId, block.blockId);
|
|
7982
8154
|
}
|
|
7983
8155
|
}
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
entry.
|
|
7987
|
-
|
|
8156
|
+
const membershipsRebuilt = !messagesState.membershipsVerified || !sameBlockIds(indexedActiveBlockIds, messagesState.activeBlockIds);
|
|
8157
|
+
if (membershipsRebuilt) {
|
|
8158
|
+
for (const entry of messagesState.byMessageId.values()) {
|
|
8159
|
+
const allBlockIds = Array.isArray(entry.allBlockIds) ? [...new Set(entry.allBlockIds.filter((id) => Number.isInteger(id) && id > 0))] : [];
|
|
8160
|
+
entry.allBlockIds = allBlockIds;
|
|
8161
|
+
entry.activeBlockIds = allBlockIds.filter((id) => messagesState.activeBlockIds.has(id));
|
|
8162
|
+
}
|
|
8163
|
+
messagesState.membershipsVerified = true;
|
|
7988
8164
|
}
|
|
7989
8165
|
const nextActiveBlockIds = messagesState.activeBlockIds;
|
|
7990
8166
|
let deactivatedCount = 0;
|
|
@@ -8006,6 +8182,7 @@ var syncCompressionBlocks = (state, logger, messages) => {
|
|
|
8006
8182
|
});
|
|
8007
8183
|
}
|
|
8008
8184
|
messagesState.lastSyncedStructureVersion = structureVersion;
|
|
8185
|
+
return membershipsRebuilt || deactivatedCount > 0 || reactivatedCount > 0;
|
|
8009
8186
|
};
|
|
8010
8187
|
|
|
8011
8188
|
// lib/host-permissions.ts
|
|
@@ -8080,6 +8257,484 @@ var syncCompressPermissionState = (state, config, hostPermissions, messages) =>
|
|
|
8080
8257
|
);
|
|
8081
8258
|
};
|
|
8082
8259
|
|
|
8260
|
+
// lib/prompts/context-limit-nudge.ts
|
|
8261
|
+
var CONTEXT_LIMIT_NUDGE = `
|
|
8262
|
+
<system-reminder>
|
|
8263
|
+
\u26A0\uFE0F Context limit reached \u2014 time to compress the largest ranges you no longer need. Prioritize completed tool outputs and resolved work. You can decompress specific blocks later if you need details. Keeping context lean helps you stay accurate.
|
|
8264
|
+
|
|
8265
|
+
If mid-atomic-operation, finish that step first, then compress.
|
|
8266
|
+
|
|
8267
|
+
HOW TO CALL COMPRESS:
|
|
8268
|
+
{
|
|
8269
|
+
"topic": "Short Label",
|
|
8270
|
+
"content": [
|
|
8271
|
+
{
|
|
8272
|
+
"startId": "<ID from early in this conversation>",
|
|
8273
|
+
"endId": "<ID from later in this conversation>",
|
|
8274
|
+
"summary": "Complete technical summary of everything in the range"
|
|
8275
|
+
}
|
|
8276
|
+
]
|
|
8277
|
+
}
|
|
8278
|
+
|
|
8279
|
+
\u26A0\uFE0F ID RULES \u2014 MOST COMMON CAUSE OF ERRORS:
|
|
8280
|
+
- ONLY use IDs you can see in tags in the messages ABOVE.
|
|
8281
|
+
- Do NOT copy IDs from this example. Do NOT invent IDs.
|
|
8282
|
+
- Do NOT use IDs from compressed block summaries \u2014 they are stale.
|
|
8283
|
+
- startId must appear BEFORE endId in the conversation.
|
|
8284
|
+
|
|
8285
|
+
RANGE STRATEGY:
|
|
8286
|
+
- Prefer one large range over multiple small ones.
|
|
8287
|
+
- Compress OLDER resolved history first. Keep recent active work.
|
|
8288
|
+
</system-reminder>
|
|
8289
|
+
`;
|
|
8290
|
+
var CANDIDATE_GUIDANCE = `
|
|
8291
|
+
<system-reminder>
|
|
8292
|
+
COMPRESSION CANDIDATES:
|
|
8293
|
+
- MICRO identifies one large message or complete tool transaction.
|
|
8294
|
+
- EPISODE identifies a contiguous historical segment made from smaller units.
|
|
8295
|
+
- Candidate entries are independent and non-overlapping, so they can be batched in one \`content[]\` array.
|
|
8296
|
+
- Candidates are suggestions, not mandatory targets. Choose only content no longer needed for the current task and do not invent a target when none is listed.
|
|
8297
|
+
- Use \`acp_status\` for a fresh candidate view if the list is stale or missing.
|
|
8298
|
+
- When the context limit is reached and candidates are listed, select at least one clearly stale candidate and call the \`compress\` tool in your next reply. Batch only additional candidates that are also clearly stale. Do not merely recommend compression.
|
|
8299
|
+
</system-reminder>
|
|
8300
|
+
`;
|
|
8301
|
+
|
|
8302
|
+
// lib/prompts/turn-nudge.ts
|
|
8303
|
+
var TURN_NUDGE = `
|
|
8304
|
+
<system-reminder>
|
|
8305
|
+
Context is getting full. If you've finished reading tool outputs or exploration results, compress them \u2014 you can decompress later if needed. This keeps your focus on the current task and improves accuracy.
|
|
8306
|
+
|
|
8307
|
+
{
|
|
8308
|
+
"topic": "Short Label",
|
|
8309
|
+
"content": [{ "startId": "<visible message ID>", "endId": "<visible message ID>", "summary": "..." }]
|
|
8310
|
+
}
|
|
8311
|
+
|
|
8312
|
+
\u26A0\uFE0F ONLY use IDs from tags visible above. Do NOT invent or copy example IDs.
|
|
8313
|
+
</system-reminder>
|
|
8314
|
+
`;
|
|
8315
|
+
var TURN_CANDIDATE_GUIDANCE = `
|
|
8316
|
+
MICRO candidates are complete large messages or tool transactions. EPISODE candidates are contiguous historical segments. They are independent suggestions and may be batched, but compress only content no longer needed for the current task. Do not compress active work or every candidate.
|
|
8317
|
+
`;
|
|
8318
|
+
|
|
8319
|
+
// lib/prompts/iteration-nudge.ts
|
|
8320
|
+
var ITERATION_NUDGE = `
|
|
8321
|
+
<system-reminder>
|
|
8322
|
+
You've been iterating for a while. If any earlier work is closed and unlikely to be referenced, compress it now.
|
|
8323
|
+
|
|
8324
|
+
{
|
|
8325
|
+
"topic": "Short Label",
|
|
8326
|
+
"content": [{ "startId": "<visible message ID>", "endId": "<visible message ID>", "summary": "..." }]
|
|
8327
|
+
}
|
|
8328
|
+
|
|
8329
|
+
\u26A0\uFE0F ONLY use IDs from tags visible above. Do NOT invent or copy example IDs.
|
|
8330
|
+
</system-reminder>
|
|
8331
|
+
`;
|
|
8332
|
+
var ITERATION_CANDIDATE_GUIDANCE = `
|
|
8333
|
+
MICRO candidates are complete large messages or tool transactions. EPISODE candidates are contiguous historical segments. They are independent suggestions and may be batched, but preserve anything still needed for the current task. Do not compress active work or every candidate.
|
|
8334
|
+
`;
|
|
8335
|
+
|
|
8336
|
+
// lib/messages/inject/candidates.ts
|
|
8337
|
+
function partToolNames(message) {
|
|
8338
|
+
const names = /* @__PURE__ */ new Set();
|
|
8339
|
+
for (const part of message.parts ?? []) {
|
|
8340
|
+
if (part.type === "tool" && part.tool !== "compress") {
|
|
8341
|
+
names.add(part.tool);
|
|
8342
|
+
}
|
|
8343
|
+
}
|
|
8344
|
+
return [...names];
|
|
8345
|
+
}
|
|
8346
|
+
function hasNonCompressTool(message) {
|
|
8347
|
+
return partToolNames(message).length > 0;
|
|
8348
|
+
}
|
|
8349
|
+
function collectToolSpans(messages) {
|
|
8350
|
+
const spansByCallId = /* @__PURE__ */ new Map();
|
|
8351
|
+
for (let index = 0; index < messages.length; index++) {
|
|
8352
|
+
const message = messages[index];
|
|
8353
|
+
if (!message) continue;
|
|
8354
|
+
const callIds = /* @__PURE__ */ new Set();
|
|
8355
|
+
for (const part of message.parts ?? []) {
|
|
8356
|
+
if (part.type !== "tool" || part.tool === "compress" || !part.callID) continue;
|
|
8357
|
+
callIds.add(part.callID);
|
|
8358
|
+
}
|
|
8359
|
+
for (const callID of callIds) {
|
|
8360
|
+
const existing = spansByCallId.get(callID);
|
|
8361
|
+
if (existing) {
|
|
8362
|
+
existing.endIndex = index;
|
|
8363
|
+
} else {
|
|
8364
|
+
spansByCallId.set(callID, { startIndex: index, endIndex: index });
|
|
8365
|
+
}
|
|
8366
|
+
}
|
|
8367
|
+
}
|
|
8368
|
+
return [...spansByCallId.values()].sort(
|
|
8369
|
+
(left, right) => left.startIndex - right.startIndex || left.endIndex - right.endIndex
|
|
8370
|
+
);
|
|
8371
|
+
}
|
|
8372
|
+
function mergeOverlappingSpans(messages, spans) {
|
|
8373
|
+
const merged = [];
|
|
8374
|
+
for (const span of spans) {
|
|
8375
|
+
const last = merged[merged.length - 1];
|
|
8376
|
+
if (last && span.startIndex <= last.endIndex) {
|
|
8377
|
+
last.endIndex = Math.max(last.endIndex, span.endIndex);
|
|
8378
|
+
} else {
|
|
8379
|
+
merged.push({ ...span });
|
|
8380
|
+
}
|
|
8381
|
+
}
|
|
8382
|
+
const result = [];
|
|
8383
|
+
let spanIndex = 0;
|
|
8384
|
+
let index = 0;
|
|
8385
|
+
while (index < messages.length) {
|
|
8386
|
+
while (spanIndex < merged.length && merged[spanIndex].endIndex < index) {
|
|
8387
|
+
spanIndex++;
|
|
8388
|
+
}
|
|
8389
|
+
const span = merged[spanIndex];
|
|
8390
|
+
if (span?.startIndex === index) {
|
|
8391
|
+
result.push(span);
|
|
8392
|
+
index = span.endIndex + 1;
|
|
8393
|
+
continue;
|
|
8394
|
+
}
|
|
8395
|
+
result.push({ startIndex: index, endIndex: index });
|
|
8396
|
+
index++;
|
|
8397
|
+
}
|
|
8398
|
+
return result;
|
|
8399
|
+
}
|
|
8400
|
+
function buildAtomicUnits(messages, state) {
|
|
8401
|
+
const omissions = [];
|
|
8402
|
+
const spans = mergeOverlappingSpans(messages, collectToolSpans(messages));
|
|
8403
|
+
const units = [];
|
|
8404
|
+
for (const span of spans) {
|
|
8405
|
+
const source = messages.slice(span.startIndex, span.endIndex + 1);
|
|
8406
|
+
const refs = source.map((message) => state.messageIds.byRawId.get(message.info.id));
|
|
8407
|
+
const startRef = refs[0];
|
|
8408
|
+
const endRef = refs[refs.length - 1];
|
|
8409
|
+
if (!startRef || !endRef) {
|
|
8410
|
+
omissions.push({ kind: "micro", startRef, endRef, reason: "missing-reference" });
|
|
8411
|
+
continue;
|
|
8412
|
+
}
|
|
8413
|
+
const invalidShape = source.some(
|
|
8414
|
+
(message) => isSyntheticMessage(message) || isIgnoredUserMessage(message)
|
|
8415
|
+
);
|
|
8416
|
+
if (invalidShape) {
|
|
8417
|
+
omissions.push({ kind: "micro", startRef, endRef, reason: "synthetic-or-ignored" });
|
|
8418
|
+
continue;
|
|
8419
|
+
}
|
|
8420
|
+
const active = source.some((message) => {
|
|
8421
|
+
const entry = state.prune.messages.byMessageId.get(message.info.id);
|
|
8422
|
+
return entry !== void 0 && entry.activeBlockIds.length > 0;
|
|
8423
|
+
});
|
|
8424
|
+
if (active) {
|
|
8425
|
+
omissions.push({ kind: "micro", startRef, endRef, reason: "active-compression" });
|
|
8426
|
+
continue;
|
|
8427
|
+
}
|
|
8428
|
+
let isTool = false;
|
|
8429
|
+
const toolNames = /* @__PURE__ */ new Set();
|
|
8430
|
+
let retainedChars = 0;
|
|
8431
|
+
let toolMessageCount = 0;
|
|
8432
|
+
let textMessageCount = 0;
|
|
8433
|
+
for (const message of source) {
|
|
8434
|
+
retainedChars += countMessageCharacters(message);
|
|
8435
|
+
if (hasNonCompressTool(message)) {
|
|
8436
|
+
isTool = true;
|
|
8437
|
+
toolMessageCount++;
|
|
8438
|
+
} else {
|
|
8439
|
+
textMessageCount++;
|
|
8440
|
+
}
|
|
8441
|
+
for (const toolName of partToolNames(message)) toolNames.add(toolName);
|
|
8442
|
+
}
|
|
8443
|
+
units.push({
|
|
8444
|
+
startIndex: span.startIndex,
|
|
8445
|
+
endIndex: span.endIndex,
|
|
8446
|
+
messages: source,
|
|
8447
|
+
sourceMessageIds: source.map((message) => message.info.id),
|
|
8448
|
+
startRef,
|
|
8449
|
+
endRef,
|
|
8450
|
+
retainedChars,
|
|
8451
|
+
estimatedTokens: Math.ceil(retainedChars / 4),
|
|
8452
|
+
isTool,
|
|
8453
|
+
toolNames: [...toolNames],
|
|
8454
|
+
toolMessageCount,
|
|
8455
|
+
textMessageCount
|
|
8456
|
+
});
|
|
8457
|
+
}
|
|
8458
|
+
return { units, omissions };
|
|
8459
|
+
}
|
|
8460
|
+
function draftCandidateLabel(kind, units) {
|
|
8461
|
+
if (kind === "episode") return "historical conversation segment";
|
|
8462
|
+
const toolNames = [...new Set(units.flatMap((unit) => unit.toolNames))];
|
|
8463
|
+
if (toolNames.length > 0) {
|
|
8464
|
+
return `${toolNames.slice(0, 3).join("/")} transaction`;
|
|
8465
|
+
}
|
|
8466
|
+
return "large conversation message";
|
|
8467
|
+
}
|
|
8468
|
+
function mergeUnits(units) {
|
|
8469
|
+
const first = units[0];
|
|
8470
|
+
const last = units[units.length - 1];
|
|
8471
|
+
const toolNames = /* @__PURE__ */ new Set();
|
|
8472
|
+
for (const unit of units) {
|
|
8473
|
+
for (const toolName of unit.toolNames) toolNames.add(toolName);
|
|
8474
|
+
}
|
|
8475
|
+
return {
|
|
8476
|
+
startIndex: first.startIndex,
|
|
8477
|
+
endIndex: last.endIndex,
|
|
8478
|
+
messages: units.flatMap((unit) => unit.messages),
|
|
8479
|
+
sourceMessageIds: units.flatMap((unit) => unit.sourceMessageIds),
|
|
8480
|
+
startRef: first.startRef,
|
|
8481
|
+
endRef: last.endRef,
|
|
8482
|
+
retainedChars: units.reduce((total, unit) => total + unit.retainedChars, 0),
|
|
8483
|
+
estimatedTokens: units.reduce((total, unit) => total + unit.estimatedTokens, 0),
|
|
8484
|
+
isTool: units.some((unit) => unit.isTool),
|
|
8485
|
+
toolNames: [...toolNames],
|
|
8486
|
+
toolMessageCount: units.reduce((total, unit) => total + unit.toolMessageCount, 0),
|
|
8487
|
+
textMessageCount: units.reduce((total, unit) => total + unit.textMessageCount, 0)
|
|
8488
|
+
};
|
|
8489
|
+
}
|
|
8490
|
+
function buildDrafts(units, minChars, state, config, protectedRefs) {
|
|
8491
|
+
const drafts = [];
|
|
8492
|
+
const omissions = [];
|
|
8493
|
+
const residual = [];
|
|
8494
|
+
for (const unit of units) {
|
|
8495
|
+
const recentProtected = unit.sourceMessageIds.some(
|
|
8496
|
+
(id) => protectedRefs.has(state.messageIds.byRawId.get(id) ?? "")
|
|
8497
|
+
);
|
|
8498
|
+
const protectedToolOrFile = unit.messages.some(
|
|
8499
|
+
(message) => messageContainsProtectedTool(
|
|
8500
|
+
message,
|
|
8501
|
+
config.compress.protectedTools,
|
|
8502
|
+
config.protectedFilePatterns
|
|
8503
|
+
)
|
|
8504
|
+
);
|
|
8505
|
+
if (recentProtected || protectedToolOrFile) {
|
|
8506
|
+
if (residual.length > 0) {
|
|
8507
|
+
const merged = mergeUnits(residual);
|
|
8508
|
+
if (merged.retainedChars >= minChars) {
|
|
8509
|
+
drafts.push({ kind: "episode", units: [...residual] });
|
|
8510
|
+
}
|
|
8511
|
+
residual.length = 0;
|
|
8512
|
+
}
|
|
8513
|
+
omissions.push({
|
|
8514
|
+
kind: unit.retainedChars >= minChars ? "micro" : "episode",
|
|
8515
|
+
startRef: unit.startRef,
|
|
8516
|
+
endRef: unit.endRef,
|
|
8517
|
+
reason: recentProtected ? "recent-protection" : "protected-tool-or-file"
|
|
8518
|
+
});
|
|
8519
|
+
continue;
|
|
8520
|
+
}
|
|
8521
|
+
if (unit.retainedChars > 0 && (minChars <= 0 || unit.retainedChars >= minChars)) {
|
|
8522
|
+
drafts.push({ kind: "micro", units: [unit] });
|
|
8523
|
+
} else if (minChars > 0 && unit.retainedChars > 0) {
|
|
8524
|
+
residual.push(unit);
|
|
8525
|
+
}
|
|
8526
|
+
}
|
|
8527
|
+
let episode = [];
|
|
8528
|
+
const flushEpisode = () => {
|
|
8529
|
+
if (episode.length === 0) return;
|
|
8530
|
+
const merged = mergeUnits(episode);
|
|
8531
|
+
if (merged.retainedChars >= minChars) {
|
|
8532
|
+
drafts.push({ kind: "episode", units: [...episode] });
|
|
8533
|
+
}
|
|
8534
|
+
episode = [];
|
|
8535
|
+
};
|
|
8536
|
+
for (let index = 0; index < residual.length; index++) {
|
|
8537
|
+
const unit = residual[index];
|
|
8538
|
+
const previous = residual[index - 1];
|
|
8539
|
+
if (previous && unit.startIndex !== previous.endIndex + 1) flushEpisode();
|
|
8540
|
+
episode.push(unit);
|
|
8541
|
+
}
|
|
8542
|
+
flushEpisode();
|
|
8543
|
+
return { drafts, omissions };
|
|
8544
|
+
}
|
|
8545
|
+
function compareIds(left, right) {
|
|
8546
|
+
const leftNumber = Number.parseInt(left.slice(1), 10);
|
|
8547
|
+
const rightNumber = Number.parseInt(right.slice(1), 10);
|
|
8548
|
+
return leftNumber - rightNumber || left.localeCompare(right);
|
|
8549
|
+
}
|
|
8550
|
+
function candidateDraftRange(draft) {
|
|
8551
|
+
return mergeUnits(draft.units);
|
|
8552
|
+
}
|
|
8553
|
+
function selectionMatchesSource(result, sourceMessageIds, unit) {
|
|
8554
|
+
if (result.plans.length !== 1) return false;
|
|
8555
|
+
const plan = result.plans[0];
|
|
8556
|
+
if (!plan) return false;
|
|
8557
|
+
if (plan.selection.startReference.rawIndex !== unit.startIndex || plan.selection.endReference.rawIndex !== unit.endIndex) {
|
|
8558
|
+
return false;
|
|
8559
|
+
}
|
|
8560
|
+
if (plan.selection.messageIds.length !== sourceMessageIds.length) return false;
|
|
8561
|
+
return plan.selection.messageIds.every((id, index) => id === sourceMessageIds[index]);
|
|
8562
|
+
}
|
|
8563
|
+
function validateDraft(draft, unit, messages, state, config, protectedRefs, prepared) {
|
|
8564
|
+
const source = messages.slice(unit.startIndex, unit.endIndex + 1);
|
|
8565
|
+
if (source.some(
|
|
8566
|
+
(message) => protectedRefs.has(state.messageIds.byRawId.get(message.info.id) ?? "")
|
|
8567
|
+
)) {
|
|
8568
|
+
return {
|
|
8569
|
+
omission: {
|
|
8570
|
+
kind: draft.kind,
|
|
8571
|
+
startRef: unit.startRef,
|
|
8572
|
+
endRef: unit.endRef,
|
|
8573
|
+
reason: "recent-protection"
|
|
8574
|
+
}
|
|
8575
|
+
};
|
|
8576
|
+
}
|
|
8577
|
+
if (source.some(
|
|
8578
|
+
(message) => messageContainsProtectedTool(
|
|
8579
|
+
message,
|
|
8580
|
+
config.compress.protectedTools,
|
|
8581
|
+
config.protectedFilePatterns
|
|
8582
|
+
)
|
|
8583
|
+
)) {
|
|
8584
|
+
return {
|
|
8585
|
+
omission: {
|
|
8586
|
+
kind: draft.kind,
|
|
8587
|
+
startRef: unit.startRef,
|
|
8588
|
+
endRef: unit.endRef,
|
|
8589
|
+
reason: "protected-tool-or-file"
|
|
8590
|
+
}
|
|
8591
|
+
};
|
|
8592
|
+
}
|
|
8593
|
+
if (config.compress.minCompressRange > 0 && unit.retainedChars < config.compress.minCompressRange) {
|
|
8594
|
+
return {
|
|
8595
|
+
omission: {
|
|
8596
|
+
kind: draft.kind,
|
|
8597
|
+
startRef: unit.startRef,
|
|
8598
|
+
endRef: unit.endRef,
|
|
8599
|
+
reason: "below-minimum"
|
|
8600
|
+
}
|
|
8601
|
+
};
|
|
8602
|
+
}
|
|
8603
|
+
try {
|
|
8604
|
+
let result = prepared;
|
|
8605
|
+
if (!result) {
|
|
8606
|
+
const searchContext = buildSearchContext(state, messages);
|
|
8607
|
+
result = prepareExecutableRangePlans(
|
|
8608
|
+
{
|
|
8609
|
+
content: [
|
|
8610
|
+
{
|
|
8611
|
+
topic: draftCandidateLabel(draft.kind, draft.units),
|
|
8612
|
+
startId: unit.startRef,
|
|
8613
|
+
endId: unit.endRef,
|
|
8614
|
+
summary: "candidate"
|
|
8615
|
+
}
|
|
8616
|
+
]
|
|
8617
|
+
},
|
|
8618
|
+
searchContext,
|
|
8619
|
+
state,
|
|
8620
|
+
config
|
|
8621
|
+
);
|
|
8622
|
+
}
|
|
8623
|
+
if (!selectionMatchesSource(result, unit.sourceMessageIds, unit)) {
|
|
8624
|
+
return {
|
|
8625
|
+
omission: {
|
|
8626
|
+
kind: draft.kind,
|
|
8627
|
+
startRef: unit.startRef,
|
|
8628
|
+
endRef: unit.endRef,
|
|
8629
|
+
reason: "executor-selection-drift"
|
|
8630
|
+
}
|
|
8631
|
+
};
|
|
8632
|
+
}
|
|
8633
|
+
return {
|
|
8634
|
+
candidate: {
|
|
8635
|
+
kind: draft.kind,
|
|
8636
|
+
startRef: unit.startRef,
|
|
8637
|
+
endRef: unit.endRef,
|
|
8638
|
+
messageCount: unit.sourceMessageIds.length,
|
|
8639
|
+
retainedChars: unit.retainedChars,
|
|
8640
|
+
estimatedTokens: unit.estimatedTokens,
|
|
8641
|
+
toolPct: Math.round(unit.toolMessageCount / unit.sourceMessageIds.length * 100),
|
|
8642
|
+
textPct: Math.round(unit.textMessageCount / unit.sourceMessageIds.length * 100),
|
|
8643
|
+
label: draftCandidateLabel(draft.kind, draft.units),
|
|
8644
|
+
sourceMessageIds: [...unit.sourceMessageIds]
|
|
8645
|
+
}
|
|
8646
|
+
};
|
|
8647
|
+
} catch {
|
|
8648
|
+
return {
|
|
8649
|
+
omission: {
|
|
8650
|
+
kind: draft.kind,
|
|
8651
|
+
startRef: unit.startRef,
|
|
8652
|
+
endRef: unit.endRef,
|
|
8653
|
+
reason: "executor-rejected"
|
|
8654
|
+
}
|
|
8655
|
+
};
|
|
8656
|
+
}
|
|
8657
|
+
}
|
|
8658
|
+
function planCompressionCandidates(messages, state, config) {
|
|
8659
|
+
const protectedRefs = computeProtectedRefs(messages, state, config.compress);
|
|
8660
|
+
const { units, omissions } = buildAtomicUnits(messages, state);
|
|
8661
|
+
const minChars = config.compress.minCompressRange ?? 5e3;
|
|
8662
|
+
const draftResult = buildDrafts(units, minChars, state, config, protectedRefs);
|
|
8663
|
+
const drafts = draftResult.drafts;
|
|
8664
|
+
const candidates = [];
|
|
8665
|
+
const allOmissions = [...omissions, ...draftResult.omissions];
|
|
8666
|
+
let batchResult;
|
|
8667
|
+
try {
|
|
8668
|
+
const searchContext = buildSearchContext(state, messages);
|
|
8669
|
+
const entries = drafts.map((draft) => {
|
|
8670
|
+
const unit = candidateDraftRange(draft);
|
|
8671
|
+
return {
|
|
8672
|
+
topic: draftCandidateLabel(draft.kind, draft.units),
|
|
8673
|
+
startId: unit.startRef,
|
|
8674
|
+
endId: unit.endRef,
|
|
8675
|
+
summary: "candidate"
|
|
8676
|
+
};
|
|
8677
|
+
});
|
|
8678
|
+
if (entries.length > 0) {
|
|
8679
|
+
batchResult = prepareExecutableRangePlans(
|
|
8680
|
+
{ content: entries },
|
|
8681
|
+
searchContext,
|
|
8682
|
+
state,
|
|
8683
|
+
config,
|
|
8684
|
+
void 0,
|
|
8685
|
+
{ includeTokenAccounting: false }
|
|
8686
|
+
);
|
|
8687
|
+
}
|
|
8688
|
+
} catch {
|
|
8689
|
+
}
|
|
8690
|
+
const preparedPlansByIndex = new Map(batchResult?.plans.map((plan) => [plan.index, plan]));
|
|
8691
|
+
for (let draftIndex = 0; draftIndex < drafts.length; draftIndex++) {
|
|
8692
|
+
const draft = drafts[draftIndex];
|
|
8693
|
+
const unit = candidateDraftRange(draft);
|
|
8694
|
+
const preparedPlan = preparedPlansByIndex.get(draftIndex);
|
|
8695
|
+
const prepared = batchResult ? {
|
|
8696
|
+
plans: preparedPlan ? [preparedPlan] : [],
|
|
8697
|
+
totalChars: preparedPlan ? unit.retainedChars : 0
|
|
8698
|
+
} : void 0;
|
|
8699
|
+
const validated = validateDraft(
|
|
8700
|
+
draft,
|
|
8701
|
+
unit,
|
|
8702
|
+
messages,
|
|
8703
|
+
state,
|
|
8704
|
+
config,
|
|
8705
|
+
protectedRefs,
|
|
8706
|
+
prepared
|
|
8707
|
+
);
|
|
8708
|
+
if ("candidate" in validated) {
|
|
8709
|
+
candidates.push(validated.candidate);
|
|
8710
|
+
} else {
|
|
8711
|
+
allOmissions.push(validated.omission);
|
|
8712
|
+
}
|
|
8713
|
+
}
|
|
8714
|
+
candidates.sort((left, right) => {
|
|
8715
|
+
return right.estimatedTokens - left.estimatedTokens || (left.kind === "micro" ? -1 : 1) - (right.kind === "micro" ? -1 : 1) || compareIds(left.startRef, right.startRef) || compareIds(left.endRef, right.endRef);
|
|
8716
|
+
});
|
|
8717
|
+
const maxCandidates = 12;
|
|
8718
|
+
const bounded = candidates.slice(0, maxCandidates);
|
|
8719
|
+
return {
|
|
8720
|
+
candidates: bounded,
|
|
8721
|
+
omitted: allOmissions,
|
|
8722
|
+
truncatedCount: Math.max(0, candidates.length - bounded.length)
|
|
8723
|
+
};
|
|
8724
|
+
}
|
|
8725
|
+
function formatCompressionCandidates(plan) {
|
|
8726
|
+
if (plan.candidates.length === 0) return "";
|
|
8727
|
+
const fmt = (value) => value >= 1e3 ? `${(value / 1e3).toFixed(1)}K` : String(value);
|
|
8728
|
+
const lines = plan.candidates.map((candidate) => {
|
|
8729
|
+
const range = `${candidate.startRef}\u2013${candidate.endRef}`;
|
|
8730
|
+
return ` ${candidate.kind.toUpperCase().padEnd(7)} ${range} ${candidate.messageCount} msg${candidate.messageCount === 1 ? "" : "s"} ${fmt(candidate.estimatedTokens)} tokens ${candidate.label}`;
|
|
8731
|
+
});
|
|
8732
|
+
const suffix = plan.truncatedCount > 0 ? `
|
|
8733
|
+
...${plan.truncatedCount} additional candidate${plan.truncatedCount === 1 ? "" : "s"} omitted` : "";
|
|
8734
|
+
return `COMPRESSION CANDIDATES (independent; batchable)
|
|
8735
|
+
${lines.join("\n")}${suffix}`;
|
|
8736
|
+
}
|
|
8737
|
+
|
|
8083
8738
|
// lib/messages/inject/inject.ts
|
|
8084
8739
|
var ACP_SUFFIX_SEED = "acp-dynamic-guidance";
|
|
8085
8740
|
function createSuffixMessage(messages) {
|
|
@@ -8089,7 +8744,7 @@ function createSuffixMessage(messages) {
|
|
|
8089
8744
|
messages.push(synthetic);
|
|
8090
8745
|
return synthetic;
|
|
8091
8746
|
}
|
|
8092
|
-
var injectCompressNudges = (state, config, logger, messages, prompts, compressionPriorities, debugNotify, preCompressTokens) => {
|
|
8747
|
+
var injectCompressNudges = (state, config, logger, messages, prompts, compressionPriorities, debugNotify, preCompressTokens, candidateMessages) => {
|
|
8093
8748
|
if (compressPermission(state, config) === "deny") {
|
|
8094
8749
|
return;
|
|
8095
8750
|
}
|
|
@@ -8201,7 +8856,10 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
|
|
|
8201
8856
|
(message) => message.info.id === lastUserMessage.info.id
|
|
8202
8857
|
);
|
|
8203
8858
|
if (lastUserMessageIndex >= 0) {
|
|
8204
|
-
const messagesSinceUser = countMessagesAfterIndex(
|
|
8859
|
+
const messagesSinceUser = countMessagesAfterIndex(
|
|
8860
|
+
messages,
|
|
8861
|
+
lastUserMessageIndex
|
|
8862
|
+
);
|
|
8205
8863
|
const iterationThreshold = getIterationNudgeThreshold(config);
|
|
8206
8864
|
if (lastMessage.index > lastUserMessageIndex && messagesSinceUser >= iterationThreshold) {
|
|
8207
8865
|
const interval = getNudgeFrequency(config);
|
|
@@ -8276,24 +8934,69 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
|
|
|
8276
8934
|
config.protectedFilePatterns,
|
|
8277
8935
|
protectedRefs
|
|
8278
8936
|
) : { compressible: [], protected: [] };
|
|
8279
|
-
const unprotectedCompressible = excludeProtectedRanges(
|
|
8937
|
+
const unprotectedCompressible = excludeProtectedRanges(
|
|
8938
|
+
contextRanges.compressible,
|
|
8939
|
+
protectedRefs
|
|
8940
|
+
);
|
|
8280
8941
|
const recommendedRanges = filterRecommendedRanges(
|
|
8281
8942
|
unprotectedCompressible,
|
|
8282
8943
|
contextRanges.protected,
|
|
8283
8944
|
{ logger, minEffectiveTokens: resolveEffectiveFloor(config) }
|
|
8284
8945
|
);
|
|
8285
|
-
|
|
8946
|
+
let candidatePlan = {
|
|
8947
|
+
candidates: [],
|
|
8948
|
+
omitted: [],
|
|
8949
|
+
truncatedCount: 0
|
|
8950
|
+
};
|
|
8951
|
+
const candidatesEnabled = config.compress.candidates === true;
|
|
8952
|
+
if (nudgeAllowed && candidatesEnabled) {
|
|
8953
|
+
try {
|
|
8954
|
+
candidatePlan = planCompressionCandidates(candidateMessages ?? messages, state, config);
|
|
8955
|
+
} catch (error) {
|
|
8956
|
+
logger.warn("Compression candidate planning failed closed", {
|
|
8957
|
+
session: state.sessionId,
|
|
8958
|
+
error: error instanceof Error ? error.message : String(error)
|
|
8959
|
+
});
|
|
8960
|
+
}
|
|
8961
|
+
}
|
|
8962
|
+
const candidateText = candidatesEnabled ? formatCompressionCandidates(candidatePlan) || (candidateMessages === void 0 && recommendedRanges.length > 0 ? formatCompressibleRanges(recommendedRanges, contextRanges.protected) : "") : recommendedRanges.length > 0 ? formatCompressibleRanges(recommendedRanges, contextRanges.protected) : "";
|
|
8286
8963
|
const allProtected = contextRanges.compressible.length === 0 && contextRanges.protected.length > 0;
|
|
8287
8964
|
const allInProtectedZone = protectedRefs.size > 0 && unprotectedCompressible.length === 0;
|
|
8288
8965
|
const allBelowMin = contextRanges.compressible.length > 0 && recommendedRanges.length === 0;
|
|
8289
|
-
const
|
|
8966
|
+
const noExecutableCandidates = candidatesEnabled && nudgeAllowed && candidateMessages !== void 0 && candidatePlan.candidates.length === 0;
|
|
8967
|
+
const nothingToCompress = candidatesEnabled && candidateMessages !== void 0 ? noExecutableCandidates : allProtected || allInProtectedZone || allBelowMin;
|
|
8290
8968
|
const emergencyNoTargets = emergencyOverride && nothingToCompress;
|
|
8291
8969
|
const noticeCadenceMet = state.nudges.lastNudgeShownTokens === void 0 || growthSinceBaseline !== void 0 && growthSinceBaseline >= growthFloor;
|
|
8292
8970
|
const shouldInjectNudge = nudgeAllowed && !nothingToCompress;
|
|
8293
8971
|
const shouldInjectNotice = emergencyNoTargets && noticeCadenceMet;
|
|
8294
8972
|
let shouldInject = shouldInjectNudge || shouldInjectNotice;
|
|
8295
8973
|
if (shouldInjectNudge) {
|
|
8296
|
-
|
|
8974
|
+
const effectivePrompts = candidatesEnabled ? {
|
|
8975
|
+
...prompts,
|
|
8976
|
+
contextLimitNudge: prompts.contextLimitNudge ? `${prompts.contextLimitNudge}
|
|
8977
|
+
|
|
8978
|
+
${CANDIDATE_GUIDANCE}` : CANDIDATE_GUIDANCE,
|
|
8979
|
+
turnNudge: prompts.turnNudge ? `${prompts.turnNudge}
|
|
8980
|
+
|
|
8981
|
+
${TURN_CANDIDATE_GUIDANCE}` : TURN_CANDIDATE_GUIDANCE,
|
|
8982
|
+
iterationNudge: prompts.iterationNudge ? `${prompts.iterationNudge}
|
|
8983
|
+
|
|
8984
|
+
${ITERATION_CANDIDATE_GUIDANCE}` : ITERATION_CANDIDATE_GUIDANCE
|
|
8985
|
+
} : prompts;
|
|
8986
|
+
applyAnchoredNudges(
|
|
8987
|
+
state,
|
|
8988
|
+
config,
|
|
8989
|
+
messages,
|
|
8990
|
+
effectivePrompts,
|
|
8991
|
+
compressionPriorities,
|
|
8992
|
+
currentTokens,
|
|
8993
|
+
modelContextLimit,
|
|
8994
|
+
suffixMessage
|
|
8995
|
+
);
|
|
8996
|
+
if (candidatesEnabled && suffixMessage && candidateText && effectiveTipsVariant !== "maxLimit") {
|
|
8997
|
+
const actionHint = hasPendingNudge ? "\n\n\u26A0\uFE0F This compression nudge has already been shown without a compression. If candidates are clearly stale, call the `compress` tool for one clearly stale candidate before continuing. Do not compress active work or things needed to proceed well." : "\n\n\u{1F4A1} If one listed candidate is clearly complete and no longer needed, call the `compress` tool in this reply before continuing. Do not compress active work or every candidate.";
|
|
8998
|
+
appendToLastTextPart(suffixMessage, actionHint);
|
|
8999
|
+
}
|
|
8297
9000
|
}
|
|
8298
9001
|
if (state.nudges.lastPerMessageNudgeTokens === void 0 && currentTokens !== void 0) {
|
|
8299
9002
|
state.nudges.lastPerMessageNudgeTokens = currentTokens;
|
|
@@ -8302,14 +9005,26 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
|
|
|
8302
9005
|
if (suffixMessage && !shouldInject) {
|
|
8303
9006
|
const tierUsage = tierUsageEarly;
|
|
8304
9007
|
const tierChecks = [
|
|
8305
|
-
{
|
|
8306
|
-
|
|
9008
|
+
{
|
|
9009
|
+
triggerTier: 2,
|
|
9010
|
+
targetTier: 1,
|
|
9011
|
+
tokens: tierUsage.tier1Tokens,
|
|
9012
|
+
lastNudge: state.nudges.lastTier2NudgeTokens
|
|
9013
|
+
},
|
|
9014
|
+
{
|
|
9015
|
+
triggerTier: 3,
|
|
9016
|
+
targetTier: 2,
|
|
9017
|
+
tokens: tierUsage.tier2Tokens,
|
|
9018
|
+
lastNudge: state.nudges.lastTier3NudgeTokens
|
|
9019
|
+
}
|
|
8307
9020
|
];
|
|
8308
9021
|
for (const tc of tierChecks) {
|
|
8309
9022
|
if (tc.tokens < nudgeGrowthTokens) continue;
|
|
8310
9023
|
const cadenceMet = tc.lastNudge === void 0 || currentTokens !== void 0 && currentTokens - tc.lastNudge >= growthFloor;
|
|
8311
9024
|
if (!cadenceMet) continue;
|
|
8312
|
-
let candidates = [...state.prune.messages.activeBlockIds].map((id) => state.prune.messages.blocksById.get(id)).filter(
|
|
9025
|
+
let candidates = [...state.prune.messages.activeBlockIds].map((id) => state.prune.messages.blocksById.get(id)).filter(
|
|
9026
|
+
(b) => b !== void 0 && b.active && (b.tier ?? 1) === tc.targetTier
|
|
9027
|
+
).sort((a, b) => a.blockId - b.blockId);
|
|
8313
9028
|
if (candidates.length >= 2) {
|
|
8314
9029
|
const firstId = candidates[0].blockId;
|
|
8315
9030
|
const lastId = candidates[candidates.length - 1].blockId;
|
|
@@ -8357,7 +9072,9 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
|
|
|
8357
9072
|
const fmt = (n) => n >= 1e3 ? `${(n / 1e3).toFixed(1)}K` : String(n);
|
|
8358
9073
|
const sourceTier = tc.triggerTier === 2 ? "Tier 1" : "Tier 2";
|
|
8359
9074
|
const action = tc.triggerTier === 2 ? "Distill" : "Condense";
|
|
8360
|
-
const blockList = candidates.slice(0, 10).map(
|
|
9075
|
+
const blockList = candidates.slice(0, 10).map(
|
|
9076
|
+
(b) => `b${b.blockId} (age=${b.survivedCount}, ${fmt(b.summaryTokens)}tok): "${b.topic}"`
|
|
9077
|
+
).join("\n");
|
|
8361
9078
|
const extraCount = candidates.length > 10 ? `
|
|
8362
9079
|
...and ${candidates.length - 10} more` : "";
|
|
8363
9080
|
const tierText = `
|
|
@@ -8398,7 +9115,19 @@ ${rules}`;
|
|
|
8398
9115
|
usagePct,
|
|
8399
9116
|
growthSinceBaseline,
|
|
8400
9117
|
growthFloor,
|
|
8401
|
-
|
|
9118
|
+
candidateCount: candidatePlan.candidates.length,
|
|
9119
|
+
microCandidates: candidatePlan.candidates.filter(
|
|
9120
|
+
(candidate) => candidate.kind === "micro"
|
|
9121
|
+
).length,
|
|
9122
|
+
episodeCandidates: candidatePlan.candidates.filter(
|
|
9123
|
+
(candidate) => candidate.kind === "episode"
|
|
9124
|
+
).length,
|
|
9125
|
+
estimatedCandidateTokens: candidatePlan.candidates.reduce(
|
|
9126
|
+
(total, candidate) => total + candidate.estimatedTokens,
|
|
9127
|
+
0
|
|
9128
|
+
),
|
|
9129
|
+
omittedCandidates: candidatePlan.omitted.length,
|
|
9130
|
+
truncatedCandidates: candidatePlan.truncatedCount
|
|
8402
9131
|
});
|
|
8403
9132
|
} else if (shouldInjectNotice) {
|
|
8404
9133
|
logger.info("Emergency /compact notice injected (no compressible targets)", {
|
|
@@ -8409,7 +9138,7 @@ ${rules}`;
|
|
|
8409
9138
|
} else if (nudgeAllowed && nothingToCompress) {
|
|
8410
9139
|
logger.info("Nudge suppressed: nothing to compress", {
|
|
8411
9140
|
session: state.sessionId,
|
|
8412
|
-
reason: allProtected ? "all_protected" : allInProtectedZone ? "in_protected_zone" : "below_effective_floor",
|
|
9141
|
+
reason: allProtected ? "all_protected" : allInProtectedZone ? "in_protected_zone" : allBelowMin ? "below_effective_floor" : "no_executable_candidates",
|
|
8413
9142
|
currentTokens,
|
|
8414
9143
|
usagePct,
|
|
8415
9144
|
compressibleRanges: contextRanges.compressible.length
|
|
@@ -8421,7 +9150,7 @@ ${rules}`;
|
|
|
8421
9150
|
const lines = [
|
|
8422
9151
|
`[ACP Debug] Recommendation filter:`,
|
|
8423
9152
|
` Input: ${compressible.length} range(s), ${fmt(compressible.reduce((s, r) => s + r.tokens, 0))} tokens`,
|
|
8424
|
-
` Output: ${recommendedRanges.length} range(s) (last segment marked dangerous)`
|
|
9153
|
+
candidatesEnabled ? ` Output: ${candidatePlan.candidates.length} candidate(s) (${candidatePlan.truncatedCount} truncated)` : ` Output: ${recommendedRanges.length} range(s) (last segment marked dangerous)`
|
|
8425
9154
|
];
|
|
8426
9155
|
logger.debug(lines.join("\n"));
|
|
8427
9156
|
}
|
|
@@ -8433,10 +9162,7 @@ ${rules}`;
|
|
|
8433
9162
|
const growth = currentTokens !== void 0 && (state.nudges.lastNudgeShownTokens ?? state.nudges.lastPerMessageNudgeTokens) !== void 0 ? currentTokens - (state.nudges.lastNudgeShownTokens ?? state.nudges.lastPerMessageNudgeTokens) : 0;
|
|
8434
9163
|
const growthStr = growth > 0 ? ` (+${fmt(growth)} since last nudge)` : "";
|
|
8435
9164
|
const plainTextTokens = composition.textTokens;
|
|
8436
|
-
const efficiencyNote = effectiveTipsVariant !== "maxLimit" ?
|
|
8437
|
-
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.
|
|
8438
|
-
|
|
8439
|
-
${COMPRESS_PHILOSOPHY}` : "";
|
|
9165
|
+
const efficiencyNote = effectiveTipsVariant !== "maxLimit" ? candidatesEnabled ? "\nThis is an efficiency nudge to compress early when content is no longer needed and keep context lean \u2014 not an overflow warning. A separate, stronger alert will appear if the context is actually full.\n\n" + COMPRESS_PHILOSOPHY : "\nThis 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.\n\n" + COMPRESS_PHILOSOPHY : "";
|
|
8440
9166
|
const sysPart = composition.systemTokens > 0 ? `${fmt(composition.systemTokens)} system (${pct2(composition.systemTokens)}%) | ` : "";
|
|
8441
9167
|
let breakdown = `${efficiencyNote}
|
|
8442
9168
|
Breakdown: ${sysPart}${fmt(composition.toolTokens)} tool (${pct2(composition.toolTokens)}%) | ${fmt(composition.summaryTokens)} summaries (${pct2(composition.summaryTokens)}%) | ${fmt(composition.codeTokens)} code (${pct2(composition.codeTokens)}%) | ${fmt(plainTextTokens)} text (${pct2(plainTextTokens)}%) | ${fmt(composition.reasoningTokens)} reasoning (${pct2(composition.reasoningTokens)}%)${growthStr}`;
|
|
@@ -8445,21 +9171,20 @@ Breakdown: ${sysPart}${fmt(composition.toolTokens)} tool (${pct2(composition.too
|
|
|
8445
9171
|
breakdown += `
|
|
8446
9172
|
\u26A0\uFE0F ${fmt(composition.protectedTokens)} tokens are protected (environment-managed tools) \u2014 not compressible. Effective compressible: ~${fmt(compressibleTokens)}.`;
|
|
8447
9173
|
}
|
|
8448
|
-
if (
|
|
9174
|
+
if (candidateText) {
|
|
8449
9175
|
breakdown += `
|
|
8450
9176
|
|
|
8451
9177
|
${HOW_TO_COMPRESS_RULES}
|
|
8452
9178
|
|
|
8453
|
-
${
|
|
8454
|
-
breakdown += `
|
|
8455
|
-
\u{1F4A1} Compress all ranges in one call (pass multiple content entries: \`content: [{...}, {...}]\`).`;
|
|
9179
|
+
${candidateText}`;
|
|
9180
|
+
breakdown += candidatesEnabled ? "\n\u{1F4A1} You may batch selected independent candidates in one call (pass multiple content entries: `content: [{...}, {...}]`); omit any candidate whose content is still needed." : "\n\u{1F4A1} Compress all ranges in one call (pass multiple content entries: `content: [{...}, {...}]`).";
|
|
8456
9181
|
}
|
|
8457
9182
|
breakdown += `
|
|
8458
9183
|
Use \`acp_status({scope:"uncompressed"})\` to re-fetch compressible ranges after compressing, or \`acp_status\` for compressed block details.`;
|
|
8459
9184
|
appendToLastTextPart(suffixMessage, breakdown);
|
|
8460
9185
|
}
|
|
8461
9186
|
if (effectiveTipsVariant === "maxLimit" && !emergencyNoTargets) {
|
|
8462
|
-
tipsText = "\n\n\u26A0\uFE0F Context limit reached \u2014 compress now. Prioritize consumed tool outputs.\n\n" + HOW_TO_COMPRESS_RULES + '\n\n{ "topic": "...", "content": [{ "startId": "<ID>", "endId": "<ID>", "summary": "..." }] }\n\nOnly use IDs from visible messages above. Compress older work first.';
|
|
9187
|
+
tipsText = candidatesEnabled ? "\n\n\u26A0\uFE0F Context limit reached \u2014 compress now. Select at least one listed candidate whose content is no longer needed and call the `compress` tool in your next reply. Do not merely recommend compression. Candidates are advisory about selection: preserve current intent and active work, and never compress content still needed for the current task.\n\n" + HOW_TO_COMPRESS_RULES + '\n\n{ "topic": "...", "content": [{ "startId": "<ID>", "endId": "<ID>", "summary": "..." }] }\n\nOnly use IDs from visible messages above. If several selected candidates are stale, older work may be preferable; never override semantic judgment or compress content still needed for the current task.' : "\n\n\u26A0\uFE0F Context limit reached \u2014 compress now. Prioritize consumed tool outputs.\n\n" + HOW_TO_COMPRESS_RULES + '\n\n{ "topic": "...", "content": [{ "startId": "<ID>", "endId": "<ID>", "summary": "..." }] }\n\nOnly use IDs from visible messages above. Compress older work first.';
|
|
8463
9188
|
} else if (shouldInjectNotice) {
|
|
8464
9189
|
const emergencyPct = currentTokens !== void 0 && modelContextLimit !== void 0 && modelContextLimit > 0 ? Math.round(currentTokens / modelContextLimit * 100) : void 0;
|
|
8465
9190
|
tipsText = `
|
|
@@ -8474,9 +9199,7 @@ Then stop retrying and await the user's response.`;
|
|
|
8474
9199
|
}
|
|
8475
9200
|
state.nudges.lastNudgeShownTokens = currentTokens;
|
|
8476
9201
|
{
|
|
8477
|
-
const visibleMessageIds = new Set(
|
|
8478
|
-
messages.map((message) => message.info.id)
|
|
8479
|
-
);
|
|
9202
|
+
const visibleMessageIds = new Set(messages.map((message) => message.info.id));
|
|
8480
9203
|
const blockGuidance = buildCompressedBlockGuidance(state, {
|
|
8481
9204
|
currentTokens,
|
|
8482
9205
|
modelContextLimit,
|
|
@@ -9260,14 +9983,14 @@ function hideConsumedCompressCalls(state, messages) {
|
|
|
9260
9983
|
}
|
|
9261
9984
|
|
|
9262
9985
|
// lib/compress/status.ts
|
|
9263
|
-
var ACP_STATUS_TOOL_DESCRIPTION = `Show context status \u2014 overview includes compressible ranges
|
|
9986
|
+
var ACP_STATUS_TOOL_DESCRIPTION = `Show context status \u2014 overview includes compressible ranges (compression candidates when compress.candidates is enabled).
|
|
9264
9987
|
|
|
9265
|
-
No args: Overview with totals, compressed blocks, and compressible ranges.
|
|
9266
|
-
scope:"uncompressed": Compressible ranges
|
|
9988
|
+
No args: Overview with totals, compressed blocks, and compressible ranges (or candidates when enabled).
|
|
9989
|
+
scope:"uncompressed": Compressible ranges by default (view:"candidates" when compress.candidates is enabled). Use view:"ranges" for raw grouped ranges or view:"messages" for per-message listing.
|
|
9267
9990
|
scope:"compressed": Drill into compressed blocks \u2014 list each with full details (age, generation, consumed lineage).
|
|
9268
9991
|
|
|
9269
9992
|
Use this tool to:
|
|
9270
|
-
- See what's consuming context + compressible
|
|
9993
|
+
- See what's consuming context + compressible targets in one call (no args)
|
|
9271
9994
|
- Focus on ranges only (scope:"uncompressed")
|
|
9272
9995
|
- Find all messages of a specific tool type (scope:"uncompressed", view:"messages", tool:"bash")
|
|
9273
9996
|
- Check block details before decompressing (scope:"compressed")`;
|
|
@@ -9437,25 +10160,33 @@ function renderOverview(visibleMessages, summaryTokens, systemTokens, blocks, fe
|
|
|
9437
10160
|
const entry = pruneMap.get(msgId);
|
|
9438
10161
|
return !entry || entry.activeBlockIds.length === 0;
|
|
9439
10162
|
});
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
10163
|
+
if (ctx.config?.compress?.candidates === true) {
|
|
10164
|
+
const candidates = renderCompressionCandidates(rawMessages, ctx);
|
|
10165
|
+
if (candidates.length > 0) {
|
|
10166
|
+
lines.push("");
|
|
10167
|
+
lines.push(...candidates);
|
|
10168
|
+
}
|
|
10169
|
+
} else {
|
|
10170
|
+
const protectedRefs = ctx.config?.compress ? computeProtectedRefs(visibleRaw, ctx.state, ctx.config.compress) : /* @__PURE__ */ new Set();
|
|
10171
|
+
const contextRanges = buildCompressibleRanges(
|
|
10172
|
+
visibleRaw,
|
|
10173
|
+
ctx.state,
|
|
10174
|
+
ctx.config?.compress?.protectedTools ?? [],
|
|
10175
|
+
ctx.config?.protectedFilePatterns ?? [],
|
|
10176
|
+
protectedRefs
|
|
9452
10177
|
);
|
|
10178
|
+
if (contextRanges.compressible.length > 0 || contextRanges.protected.length > 0) {
|
|
10179
|
+
lines.push("");
|
|
10180
|
+
lines.push(
|
|
10181
|
+
formatCompressibleRanges(contextRanges.compressible, contextRanges.protected)
|
|
10182
|
+
);
|
|
10183
|
+
}
|
|
9453
10184
|
}
|
|
9454
10185
|
}
|
|
9455
10186
|
lines.push("");
|
|
9456
10187
|
const hintTool = topToolName || "bash";
|
|
9457
10188
|
lines.push(
|
|
9458
|
-
`Tip: acp_status({scope:"uncompressed", view:"messages", tool:"${hintTool}"}) for per-message listing`
|
|
10189
|
+
ctx.config?.compress?.candidates === true ? `Tip: acp_status({scope:"uncompressed", view:"ranges"}) for raw ranges, or acp_status({scope:"uncompressed", view:"messages", tool:"${hintTool}"}) for per-message listing` : `Tip: acp_status({scope:"uncompressed", view:"messages", tool:"${hintTool}"}) for per-message listing`
|
|
9459
10190
|
);
|
|
9460
10191
|
return lines;
|
|
9461
10192
|
}
|
|
@@ -9492,6 +10223,44 @@ function renderUncompressedRanges(rawMessages, ctx) {
|
|
|
9492
10223
|
lines.push(`Filter by tool: acp_status({scope:"uncompressed", view:"messages", tool:"bash"})`);
|
|
9493
10224
|
return lines;
|
|
9494
10225
|
}
|
|
10226
|
+
function renderCompressionCandidates(rawMessages, ctx) {
|
|
10227
|
+
const lines = ["COMPRESSION CANDIDATES"];
|
|
10228
|
+
if (!ctx.config?.compress || ctx.config.compress.candidates !== true) {
|
|
10229
|
+
return renderUncompressedRanges(rawMessages, ctx);
|
|
10230
|
+
}
|
|
10231
|
+
try {
|
|
10232
|
+
const plan = planCompressionCandidates(rawMessages, ctx.state, ctx.config);
|
|
10233
|
+
const visibleMessages = rawMessages.filter((message) => {
|
|
10234
|
+
const entry = ctx.state.prune.messages.byMessageId.get(message.info.id);
|
|
10235
|
+
return !entry || entry.activeBlockIds.length === 0;
|
|
10236
|
+
});
|
|
10237
|
+
const protectedRefs = computeProtectedRefs(visibleMessages, ctx.state, ctx.config.compress);
|
|
10238
|
+
const contextRanges = buildCompressibleRanges(
|
|
10239
|
+
visibleMessages,
|
|
10240
|
+
ctx.state,
|
|
10241
|
+
ctx.config.compress.protectedTools ?? [],
|
|
10242
|
+
ctx.config.protectedFilePatterns ?? [],
|
|
10243
|
+
protectedRefs
|
|
10244
|
+
);
|
|
10245
|
+
const protectedText = formatCompressibleRanges([], contextRanges.protected);
|
|
10246
|
+
if (plan.candidates.length === 0) {
|
|
10247
|
+
lines.push(" (no eligible candidates)");
|
|
10248
|
+
if (protectedText.includes("PROTECTED")) lines.push("", protectedText);
|
|
10249
|
+
return lines;
|
|
10250
|
+
}
|
|
10251
|
+
lines.length = 0;
|
|
10252
|
+
lines.push(formatCompressionCandidates(plan));
|
|
10253
|
+
if (protectedText.includes("PROTECTED")) lines.push("", protectedText);
|
|
10254
|
+
if (plan.omitted.length > 0) {
|
|
10255
|
+
lines.push(
|
|
10256
|
+
` ${plan.omitted.length} structurally unsafe or undersized candidate${plan.omitted.length === 1 ? "" : "s"} omitted`
|
|
10257
|
+
);
|
|
10258
|
+
}
|
|
10259
|
+
return lines;
|
|
10260
|
+
} catch {
|
|
10261
|
+
return ["COMPRESSION CANDIDATES", " (candidate planning unavailable)"];
|
|
10262
|
+
}
|
|
10263
|
+
}
|
|
9495
10264
|
function renderUncompressedDrilldown(visibleMessages, toolFilter, sort, limit) {
|
|
9496
10265
|
const lines = [];
|
|
9497
10266
|
let filtered = visibleMessages;
|
|
@@ -9594,14 +10363,12 @@ function renderCompressedDrilldown(blocks, sort, limit, blocksById) {
|
|
|
9594
10363
|
}
|
|
9595
10364
|
function buildStatusReport(renderCtx, rawMessages, options) {
|
|
9596
10365
|
const scope = options?.scope;
|
|
9597
|
-
const view = options?.view ?? "
|
|
10366
|
+
const view = options?.view ?? "candidates";
|
|
9598
10367
|
const toolFilter = options?.tool;
|
|
9599
10368
|
const sort = options?.sort ?? "size";
|
|
9600
10369
|
const limit = options?.limit ?? 30;
|
|
9601
10370
|
const msgState = renderCtx.state.prune.messages;
|
|
9602
|
-
const allBlocks = Array.from(msgState.blocksById.values()).sort(
|
|
9603
|
-
(a, b) => a.blockId - b.blockId
|
|
9604
|
-
);
|
|
10371
|
+
const allBlocks = Array.from(msgState.blocksById.values()).sort((a, b) => a.blockId - b.blockId);
|
|
9605
10372
|
const activeBlocks = allBlocks.filter((b) => b.active);
|
|
9606
10373
|
const lines = [];
|
|
9607
10374
|
if (scope === "compressed") {
|
|
@@ -9615,8 +10382,10 @@ function buildStatusReport(renderCtx, rawMessages, options) {
|
|
|
9615
10382
|
if (scope === "uncompressed") {
|
|
9616
10383
|
if (view === "messages") {
|
|
9617
10384
|
lines.push(...renderUncompressedDrilldown(visibleMsgs, toolFilter, sort, limit));
|
|
9618
|
-
} else {
|
|
10385
|
+
} else if (view === "ranges") {
|
|
9619
10386
|
lines.push(...renderUncompressedRanges(rawMessages, renderCtx));
|
|
10387
|
+
} else {
|
|
10388
|
+
lines.push(...renderCompressionCandidates(rawMessages, renderCtx));
|
|
9620
10389
|
}
|
|
9621
10390
|
} else {
|
|
9622
10391
|
lines.push(
|
|
@@ -9640,7 +10409,7 @@ function createAcpStatusTool(factoryCtx) {
|
|
|
9640
10409
|
args: {
|
|
9641
10410
|
scope: tool4.schema.string().optional().describe('Drill down: "compressed" or "uncompressed". No arg = overview of both.'),
|
|
9642
10411
|
view: tool4.schema.string().optional().describe(
|
|
9643
|
-
'Display format for scope:"uncompressed": "
|
|
10412
|
+
'Display format for scope:"uncompressed": "candidates" (default when compress.candidates is enabled \u2014 otherwise "ranges"), "ranges" (raw grouped ranges), or "messages" (per-message listing with sort/filter)'
|
|
9644
10413
|
),
|
|
9645
10414
|
tool: tool4.schema.string().optional().describe(
|
|
9646
10415
|
'Filter by tool type (only with scope:"uncompressed", view:"messages"). e.g., "bash", "todowrite", "write"'
|
|
@@ -9651,16 +10420,16 @@ function createAcpStatusTool(factoryCtx) {
|
|
|
9651
10420
|
async execute(args, toolCtx) {
|
|
9652
10421
|
const ctx = resolveToolContext(factoryCtx, toolCtx.sessionID);
|
|
9653
10422
|
const scope = args.scope === "compressed" || args.scope === "uncompressed" ? args.scope : void 0;
|
|
9654
|
-
const view = args.view === "messages"
|
|
10423
|
+
const view = args.view === "messages" || args.view === "ranges" || args.view === "candidates" ? args.view : "candidates";
|
|
9655
10424
|
const toolFilter = typeof args.tool === "string" ? args.tool : void 0;
|
|
9656
10425
|
const sort = args.sort === "time" || args.sort === "tool" || args.sort === "age" ? args.sort : "size";
|
|
9657
10426
|
const limit = Number.isFinite(args.limit) && args.limit > 0 ? Math.min(args.limit, 200) : 30;
|
|
9658
10427
|
if (scope === "compressed") {
|
|
9659
|
-
return buildStatusReport(
|
|
9660
|
-
|
|
9661
|
-
|
|
9662
|
-
|
|
9663
|
-
);
|
|
10428
|
+
return buildStatusReport({ state: ctx.state, config: ctx.config }, [], {
|
|
10429
|
+
scope: "compressed",
|
|
10430
|
+
sort,
|
|
10431
|
+
limit
|
|
10432
|
+
});
|
|
9664
10433
|
}
|
|
9665
10434
|
let rawMessages = [];
|
|
9666
10435
|
try {
|
|
@@ -9670,11 +10439,13 @@ function createAcpStatusTool(factoryCtx) {
|
|
|
9670
10439
|
rawMessages = [];
|
|
9671
10440
|
}
|
|
9672
10441
|
hideConsumedCompressCalls(ctx.state, rawMessages);
|
|
9673
|
-
return buildStatusReport(
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
|
|
10442
|
+
return buildStatusReport({ state: ctx.state, config: ctx.config }, rawMessages, {
|
|
10443
|
+
scope,
|
|
10444
|
+
view,
|
|
10445
|
+
tool: toolFilter,
|
|
10446
|
+
sort,
|
|
10447
|
+
limit
|
|
10448
|
+
});
|
|
9678
10449
|
}
|
|
9679
10450
|
});
|
|
9680
10451
|
}
|
|
@@ -9782,7 +10553,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
|
9782
10553
|
import { join as join4 } from "path";
|
|
9783
10554
|
import { existsSync as existsSync4 } from "fs";
|
|
9784
10555
|
import { homedir as homedir3 } from "os";
|
|
9785
|
-
var LOG_VERSION = true ? "1.
|
|
10556
|
+
var LOG_VERSION = true ? "1.18.0-pr.392.147" : "dev";
|
|
9786
10557
|
var LEVEL_RANK = {
|
|
9787
10558
|
debug: 10,
|
|
9788
10559
|
info: 20,
|
|
@@ -10005,7 +10776,32 @@ import { join as join5, dirname as dirname2 } from "path";
|
|
|
10005
10776
|
import { homedir as homedir4 } from "os";
|
|
10006
10777
|
|
|
10007
10778
|
// lib/prompts/system.ts
|
|
10008
|
-
var
|
|
10779
|
+
var ACP_STATUS_TOOL_LINE_RANGES = `- \`acp_status\` \u2014 Context status with compressible ranges. No args = overview + ranges. \`scope:"uncompressed"\` for range view; add \`view:"messages"\` for per-message listing with \`tool\`/\`sort\` filters. \`scope:"compressed"\` for block details.`;
|
|
10780
|
+
var ACP_STATUS_TOOL_LINE_CANDIDATES = `- \`acp_status\` \u2014 Context status with compression candidates. No args = overview + candidates. \`scope:"uncompressed"\` defaults to independent candidates; use \`view:"ranges"\` for raw grouped ranges or \`view:"messages"\` for per-message listing with \`tool\`/\`sort\` filters. \`scope:"compressed"\` shows block details.`;
|
|
10781
|
+
var PHILOSOPHY_TAIL_RANGES = `All ranges listed in the context breakdown 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.`;
|
|
10782
|
+
var PHILOSOPHY_TAIL_CANDIDATES = `Candidate guidance is advisory: a candidate is structurally safe to submit, not a command to compress. Preserve current intent and active work even when a candidate is displayed.`;
|
|
10783
|
+
var COMPRESSION_CANDIDATES_SECTION = `COMPRESSION CANDIDATES
|
|
10784
|
+
|
|
10785
|
+
ACP may append a bounded list headed \`COMPRESSION CANDIDATES\` below a nudge or status report:
|
|
10786
|
+
|
|
10787
|
+
- \`MICRO\` is one large plain message or one complete tool transaction, including every message needed to keep its tool call/result structure valid.
|
|
10788
|
+
- \`EPISODE\` is a contiguous historical segment made from smaller adjacent units.
|
|
10789
|
+
- Candidate ranges are independent, non-overlapping, and can be submitted together as separate \`content[]\` entries.
|
|
10790
|
+
- Choose candidates only when their content is no longer needed for the current task. Do not invent a target when no candidate is listed; call \`acp_status\` for a fresh view. If an arbitrary range is unavoidable, verify current visible IDs, protection boundaries, and complete tool-pair coverage first.
|
|
10791
|
+
- Keep summaries self-contained and use the existing \`compress\` tool. Candidate labels do not replace the semantic judgment to preserve useful context.
|
|
10792
|
+
- When a nudge lists a clearly stale candidate, call \`compress\` in that reply before continuing. A repeated nudge means compression was not completed; act on one clearly stale candidate rather than merely recommending compression.`;
|
|
10793
|
+
var BREAKDOWN_TAIL_RANGES = `Below the breakdown, the system lists compressible ranges grouped by conversation turn. All listed ranges 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. Compress the largest ranges first when the current step no longer needs them.`;
|
|
10794
|
+
var BREAKDOWN_TAIL_CANDIDATES = `Below the breakdown, the system may list independent compression candidates. Prefer a large stale micro-range when it removes an isolated artifact; use an episode range when a completed historical phase is more coherent as one summary. Compress only content the current step no longer needs.`;
|
|
10795
|
+
function buildSystemPrompt(candidatesEnabled) {
|
|
10796
|
+
const acpStatusToolLine = candidatesEnabled ? ACP_STATUS_TOOL_LINE_CANDIDATES : ACP_STATUS_TOOL_LINE_RANGES;
|
|
10797
|
+
const philosophyTail = candidatesEnabled ? PHILOSOPHY_TAIL_CANDIDATES : PHILOSOPHY_TAIL_RANGES;
|
|
10798
|
+
const compressionCandidatesBlock = candidatesEnabled ? `
|
|
10799
|
+
|
|
10800
|
+
${COMPRESSION_CANDIDATES_SECTION}
|
|
10801
|
+
|
|
10802
|
+
` : "\n\n";
|
|
10803
|
+
const breakdownTail = candidatesEnabled ? BREAKDOWN_TAIL_CANDIDATES : BREAKDOWN_TAIL_RANGES;
|
|
10804
|
+
return `
|
|
10009
10805
|
|
|
10010
10806
|
You operate in a context-constrained environment. All compression serves the primary task, but be frugal. Context management helps preserve retrieval quality, but your primary goal is completing the task at hand. Do not let context management distract from the actual work.
|
|
10011
10807
|
|
|
@@ -10031,7 +10827,7 @@ You have five context-management tools:
|
|
|
10031
10827
|
- \`compress\` \u2014 Replace a contiguous range of older conversation with a single detailed summary you write. Use when content is genuinely consumed (no longer needed for the current task step). Single range: \`compress({ topic: "API exploration", content: [{ startId: "m00150", endId: "m00220", summary: "..." }] })\`. Batch (multiple unrelated ranges, each with its own topic): \`compress({ content: [{ topic: "Auth", startId: "m00150", endId: "m00220", summary: "..." }, { topic: "Deploy", startId: "m00300", endId: "m00350", summary: "..." }] })\`.
|
|
10032
10828
|
- \`decompress\` \u2014 Restore a previously compressed block's content. By default restores one tier up (T2\u2192T1 summaries, not raw messages). Use \`full: true\` to restore all the way to original messages. Use \`toFile\` to write to file instead of inflating context. Example: \`decompress({ blockId: "b5" })\` or \`decompress({ blockId: "b5", toFile: "path" })\` or \`decompress({ blockId: "b5", full: true })\`.
|
|
10033
10829
|
- \`search_context\` \u2014 Search compressed block summaries (and optionally visible messages) by keyword. Use BEFORE decompressing to find the right block. Example: \`search_context({ query: "auth token refresh" })\`.
|
|
10034
|
-
|
|
10830
|
+
${acpStatusToolLine}
|
|
10035
10831
|
|
|
10036
10832
|
COMPRESSION PHILOSOPHY
|
|
10037
10833
|
|
|
@@ -10039,7 +10835,7 @@ Two failure modes to avoid:
|
|
|
10039
10835
|
- Over-compression: Compressing too aggressively loses critical details, decisions, and state needed for your task. This directly harms task quality.
|
|
10040
10836
|
- Under-compression: Failing to compress verbose outputs causes context overflow, reducing accuracy and eventually blocking your work.
|
|
10041
10837
|
|
|
10042
|
-
Balance is key. The single test for whether to compress is: "Is this content still needed by the current task step?" If yes, keep it. If no, compress it.
|
|
10838
|
+
Balance is key. The single test for whether to compress is: "Is this content still needed by the current task step?" If yes, keep it. If no, compress it. ${philosophyTail}
|
|
10043
10839
|
|
|
10044
10840
|
Be frugal with context. Compress obvious waste proactively \u2014 verbose outputs you have already used, duplicate reads, abandoned explorations. Do not wait until context is critically full; that harms retrieval quality and risks overflow. But never let the urge to compress distract from the actual task.
|
|
10045
10841
|
|
|
@@ -10060,9 +10856,7 @@ WHEN NOT TO COMPRESS
|
|
|
10060
10856
|
- Important user messages \u2014 preserve their exact intent, constraints, and acceptance criteria verbatim, not just the most recent one.
|
|
10061
10857
|
- Protected tool outputs (default: \`skill\` only) \u2014 hard-excluded from compression ranges, survive intact in visible context.
|
|
10062
10858
|
|
|
10063
|
-
${HOW_TO_COMPRESS_RULES}
|
|
10064
|
-
|
|
10065
|
-
MULTI-TIER COMPRESSION
|
|
10859
|
+
${HOW_TO_COMPRESS_RULES}${compressionCandidatesBlock}MULTI-TIER COMPRESSION
|
|
10066
10860
|
|
|
10067
10861
|
Summaries accumulate as the session grows. When tier-1 summaries pile up, the system injects a [Tier 2 Trigger] prompting you to DISTILL old blocks into a single tier-2 summary. If tier-2 summaries also accumulate, a [Tier 3 Trigger] asks you to CONDENSE them further.
|
|
10068
10862
|
|
|
@@ -10087,13 +10881,21 @@ Breakdown: 4.2K system (21%) | 8.0K tool (40%) | 2.0K summaries (10%) | 2.6K cod
|
|
|
10087
10881
|
- "text" = plain text messages
|
|
10088
10882
|
- "reasoning" = model thinking blocks (counted to match real API usage; freed when their message is compressed)
|
|
10089
10883
|
|
|
10090
|
-
|
|
10884
|
+
${breakdownTail}
|
|
10091
10885
|
|
|
10092
10886
|
Each compression creates a reusable summary block you can decompress later if needed.
|
|
10093
10887
|
`;
|
|
10888
|
+
}
|
|
10889
|
+
var SYSTEM = buildSystemPrompt(true);
|
|
10094
10890
|
|
|
10095
10891
|
// lib/prompts/compress-range.ts
|
|
10096
|
-
var
|
|
10892
|
+
var CANDIDATE_GUIDANCE_SECTION = `CANDIDATE GUIDANCE
|
|
10893
|
+
ACP may display a \`COMPRESSION CANDIDATES\` list in a nudge or \`acp_status\` report. A \`MICRO\` entry targets one large message or complete tool transaction; an \`EPISODE\` entry targets a contiguous historical segment. Displayed candidates are independent and non-overlapping, so they may be batched, but they are advisory: compress only entries whose content is no longer needed. When a nudge lists a clearly stale candidate, use its exact IDs and call \`compress\` before continuing. Keep current intent and active work visible, and use \`acp_status\` when the candidate list is stale.`;
|
|
10894
|
+
function buildCompressRangePrompt(candidatesEnabled) {
|
|
10895
|
+
const candidateGuidanceBlock = candidatesEnabled ? `${CANDIDATE_GUIDANCE_SECTION}
|
|
10896
|
+
|
|
10897
|
+
` : "";
|
|
10898
|
+
return `Collapse a range in the conversation into a detailed summary.
|
|
10097
10899
|
|
|
10098
10900
|
COMPRESSED BLOCK PLACEHOLDERS
|
|
10099
10901
|
The system auto-detects any previously compressed blocks whose anchor messages fall inside your selected range. You do NOT need to manually list \`(bN)\` placeholders in your summary \u2014 every consumed block is tracked automatically.
|
|
@@ -10123,7 +10925,7 @@ Rules:
|
|
|
10123
10925
|
|
|
10124
10926
|
- Pick \`startId\` and \`endId\` directly from injected IDs in context.
|
|
10125
10927
|
- IDs must exist in the current visible context. If you cannot see an ID in the messages above, it is stale and will fail.
|
|
10126
|
-
- \`startId\`
|
|
10928
|
+
- Prefer \`startId\` before \`endId\` in conversation order. ACP can normalize reversed boundaries, but do not rely on that behavior.
|
|
10127
10929
|
- Do not invent IDs. Use only IDs that are present in context.
|
|
10128
10930
|
- NEVER use IDs from compressed block summaries, previous nudges, or your own memory \u2014 only IDs currently visible as XML metadata tags in the conversation.
|
|
10129
10931
|
|
|
@@ -10140,7 +10942,7 @@ compress({ content: [
|
|
|
10140
10942
|
]})
|
|
10141
10943
|
\`\`\`
|
|
10142
10944
|
|
|
10143
|
-
KEEP AND REF MARKERS
|
|
10945
|
+
${candidateGuidanceBlock}KEEP AND REF MARKERS
|
|
10144
10946
|
When writing a summary, you may embed markers that reference specific messages in the compressed range. The system resolves them automatically:
|
|
10145
10947
|
|
|
10146
10948
|
- \`[[KEEP:mNNNNN]]\` \u2014 Expands to the original message content inline (truncated to a max length). Use for critical content you want preserved verbatim in the summary without re-typing it: key function definitions, important error messages, essential file contents.
|
|
@@ -10157,65 +10959,8 @@ The rest of the bash calls were repetitive export commands. See [[REF:m00078|tes
|
|
|
10157
10959
|
|
|
10158
10960
|
Use KEEP sparingly \u2014 each expansion adds to the summary length. Prefer REF for content that is important but not immediately critical.
|
|
10159
10961
|
`;
|
|
10160
|
-
|
|
10161
|
-
// lib/prompts/context-limit-nudge.ts
|
|
10162
|
-
var CONTEXT_LIMIT_NUDGE = `
|
|
10163
|
-
<system-reminder>
|
|
10164
|
-
\u26A0\uFE0F Context limit reached \u2014 time to compress the largest ranges you no longer need. Prioritize completed tool outputs and resolved work. You can decompress specific blocks later if you need details. Keeping context lean helps you stay accurate.
|
|
10165
|
-
|
|
10166
|
-
If mid-atomic-operation, finish that step first, then compress.
|
|
10167
|
-
|
|
10168
|
-
HOW TO CALL COMPRESS:
|
|
10169
|
-
{
|
|
10170
|
-
"topic": "Short Label",
|
|
10171
|
-
"content": [
|
|
10172
|
-
{
|
|
10173
|
-
"startId": "<ID from early in this conversation>",
|
|
10174
|
-
"endId": "<ID from later in this conversation>",
|
|
10175
|
-
"summary": "Complete technical summary of everything in the range"
|
|
10176
|
-
}
|
|
10177
|
-
]
|
|
10178
10962
|
}
|
|
10179
|
-
|
|
10180
|
-
\u26A0\uFE0F ID RULES \u2014 MOST COMMON CAUSE OF ERRORS:
|
|
10181
|
-
- ONLY use IDs you can see in tags in the messages ABOVE.
|
|
10182
|
-
- Do NOT copy IDs from this example. Do NOT invent IDs.
|
|
10183
|
-
- Do NOT use IDs from compressed block summaries \u2014 they are stale.
|
|
10184
|
-
- startId must appear BEFORE endId in the conversation.
|
|
10185
|
-
|
|
10186
|
-
RANGE STRATEGY:
|
|
10187
|
-
- Prefer one large range over multiple small ones.
|
|
10188
|
-
- Compress OLDER resolved history first. Keep recent active work.
|
|
10189
|
-
</system-reminder>
|
|
10190
|
-
`;
|
|
10191
|
-
|
|
10192
|
-
// lib/prompts/turn-nudge.ts
|
|
10193
|
-
var TURN_NUDGE = `
|
|
10194
|
-
<system-reminder>
|
|
10195
|
-
Context is getting full. If you've finished reading tool outputs or exploration results, compress them \u2014 you can decompress later if needed. This keeps your focus on the current task and improves accuracy.
|
|
10196
|
-
|
|
10197
|
-
{
|
|
10198
|
-
"topic": "Short Label",
|
|
10199
|
-
"content": [{ "startId": "<visible message ID>", "endId": "<visible message ID>", "summary": "..." }]
|
|
10200
|
-
}
|
|
10201
|
-
|
|
10202
|
-
\u26A0\uFE0F ONLY use IDs from tags visible above. Do NOT invent or copy example IDs.
|
|
10203
|
-
</system-reminder>
|
|
10204
|
-
`;
|
|
10205
|
-
|
|
10206
|
-
// lib/prompts/iteration-nudge.ts
|
|
10207
|
-
var ITERATION_NUDGE = `
|
|
10208
|
-
<system-reminder>
|
|
10209
|
-
You've been iterating for a while. If any earlier work is closed and unlikely to be referenced, compress it now.
|
|
10210
|
-
|
|
10211
|
-
{
|
|
10212
|
-
"topic": "Short Label",
|
|
10213
|
-
"content": [{ "startId": "<visible message ID>", "endId": "<visible message ID>", "summary": "..." }]
|
|
10214
|
-
}
|
|
10215
|
-
|
|
10216
|
-
\u26A0\uFE0F ONLY use IDs from tags visible above. Do NOT invent or copy example IDs.
|
|
10217
|
-
</system-reminder>
|
|
10218
|
-
`;
|
|
10963
|
+
var COMPRESS_RANGE = buildCompressRangePrompt(true);
|
|
10219
10964
|
|
|
10220
10965
|
// lib/prompts/extensions/system.ts
|
|
10221
10966
|
var SUBAGENT_SYSTEM_EXTENSION = `<dcp-system-reminder>
|
|
@@ -10307,24 +11052,27 @@ var HTML_COMMENT_REGEX = /<!--[\s\S]*?-->/g;
|
|
|
10307
11052
|
var LEGACY_INLINE_COMMENT_LINE_REGEX = /^[ \t]*\/\/.*?\/\/[ \t]*$/gm;
|
|
10308
11053
|
var DCP_SYSTEM_REMINDER_TAG_REGEX = /^\s*<dcp-system-reminder\b[^>]*>[\s\S]*<\/(?:dcp|acp)-system-reminder>\s*$/i;
|
|
10309
11054
|
var DEFAULTS_README_FILE = "README.md";
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
|
|
11055
|
+
function getBundledEditablePrompts(candidatesEnabled) {
|
|
11056
|
+
return {
|
|
11057
|
+
system: buildSystemPrompt(candidatesEnabled),
|
|
11058
|
+
compressRange: buildCompressRangePrompt(candidatesEnabled),
|
|
11059
|
+
contextLimitNudge: CONTEXT_LIMIT_NUDGE,
|
|
11060
|
+
turnNudge: TURN_NUDGE,
|
|
11061
|
+
iterationNudge: ITERATION_NUDGE
|
|
11062
|
+
};
|
|
11063
|
+
}
|
|
10317
11064
|
var INTERNAL_PROMPT_EXTENSIONS = {
|
|
10318
11065
|
subagentExtension: SUBAGENT_SYSTEM_EXTENSION,
|
|
10319
11066
|
decompressExtension: DECOMPRESS_SYSTEM_EXTENSION
|
|
10320
11067
|
};
|
|
10321
|
-
function createBundledRuntimePrompts() {
|
|
11068
|
+
function createBundledRuntimePrompts(candidatesEnabled) {
|
|
11069
|
+
const bundled = getBundledEditablePrompts(candidatesEnabled);
|
|
10322
11070
|
return {
|
|
10323
|
-
system:
|
|
10324
|
-
compressRange:
|
|
10325
|
-
contextLimitNudge:
|
|
10326
|
-
turnNudge:
|
|
10327
|
-
iterationNudge:
|
|
11071
|
+
system: bundled.system,
|
|
11072
|
+
compressRange: bundled.compressRange,
|
|
11073
|
+
contextLimitNudge: bundled.contextLimitNudge,
|
|
11074
|
+
turnNudge: bundled.turnNudge,
|
|
11075
|
+
iterationNudge: bundled.iterationNudge,
|
|
10328
11076
|
subagentExtension: INTERNAL_PROMPT_EXTENSIONS.subagentExtension,
|
|
10329
11077
|
decompressExtension: INTERNAL_PROMPT_EXTENSIONS.decompressExtension
|
|
10330
11078
|
};
|
|
@@ -10469,12 +11217,14 @@ var PromptStore = class {
|
|
|
10469
11217
|
logger;
|
|
10470
11218
|
paths;
|
|
10471
11219
|
customPromptsEnabled;
|
|
11220
|
+
candidatesEnabled;
|
|
10472
11221
|
runtimePrompts;
|
|
10473
|
-
constructor(logger, workingDirectory, customPromptsEnabled = false) {
|
|
11222
|
+
constructor(logger, workingDirectory, customPromptsEnabled = false, candidatesEnabled = false) {
|
|
10474
11223
|
this.logger = logger;
|
|
10475
11224
|
this.paths = resolvePromptPaths(workingDirectory);
|
|
10476
11225
|
this.customPromptsEnabled = customPromptsEnabled;
|
|
10477
|
-
this.
|
|
11226
|
+
this.candidatesEnabled = candidatesEnabled;
|
|
11227
|
+
this.runtimePrompts = createBundledRuntimePrompts(this.candidatesEnabled);
|
|
10478
11228
|
if (this.customPromptsEnabled) {
|
|
10479
11229
|
this.ensureDefaultFiles();
|
|
10480
11230
|
}
|
|
@@ -10484,13 +11234,13 @@ var PromptStore = class {
|
|
|
10484
11234
|
return { ...this.runtimePrompts };
|
|
10485
11235
|
}
|
|
10486
11236
|
reload() {
|
|
10487
|
-
const nextPrompts = createBundledRuntimePrompts();
|
|
11237
|
+
const nextPrompts = createBundledRuntimePrompts(this.candidatesEnabled);
|
|
10488
11238
|
if (!this.customPromptsEnabled) {
|
|
10489
11239
|
this.runtimePrompts = nextPrompts;
|
|
10490
11240
|
return;
|
|
10491
11241
|
}
|
|
10492
11242
|
for (const definition of PROMPT_DEFINITIONS) {
|
|
10493
|
-
const bundledSource =
|
|
11243
|
+
const bundledSource = getBundledEditablePrompts(this.candidatesEnabled)[definition.runtimeField];
|
|
10494
11244
|
const bundledEditable = toEditablePromptText(definition, bundledSource);
|
|
10495
11245
|
const bundledRuntime = wrapRuntimePromptContent(definition, bundledEditable);
|
|
10496
11246
|
const fallbackValue = bundledRuntime || bundledSource.trim();
|
|
@@ -10551,13 +11301,14 @@ var PromptStore = class {
|
|
|
10551
11301
|
});
|
|
10552
11302
|
return;
|
|
10553
11303
|
}
|
|
11304
|
+
const bundledPrompts = getBundledEditablePrompts(this.candidatesEnabled);
|
|
10554
11305
|
for (const definition of PROMPT_DEFINITIONS) {
|
|
10555
11306
|
const bundledEditable = toEditablePromptText(
|
|
10556
11307
|
definition,
|
|
10557
|
-
|
|
11308
|
+
bundledPrompts[definition.runtimeField]
|
|
10558
11309
|
);
|
|
10559
11310
|
const managedContent = buildDefaultPromptFileContent(
|
|
10560
|
-
bundledEditable ||
|
|
11311
|
+
bundledEditable || bundledPrompts[definition.runtimeField]
|
|
10561
11312
|
);
|
|
10562
11313
|
const filePath = join5(this.paths.defaultsDir, definition.fileName);
|
|
10563
11314
|
try {
|
|
@@ -12002,8 +12753,8 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
|
|
|
12002
12753
|
cacheSystemPromptTokens(state, output.messages);
|
|
12003
12754
|
assignMessageRefs(state, output.messages);
|
|
12004
12755
|
const activeBlockCountBefore = state.prune.messages.activeBlockIds.size;
|
|
12005
|
-
syncCompressionBlocks(state, logger, output.messages);
|
|
12006
|
-
if (state.prune.messages.activeBlockIds.size !== activeBlockCountBefore) {
|
|
12756
|
+
const compressionStateChanged = syncCompressionBlocks(state, logger, output.messages);
|
|
12757
|
+
if (compressionStateChanged || state.prune.messages.activeBlockIds.size !== activeBlockCountBefore) {
|
|
12007
12758
|
saveSessionState(state, logger).catch(() => {
|
|
12008
12759
|
});
|
|
12009
12760
|
}
|
|
@@ -12015,9 +12766,8 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
|
|
|
12015
12766
|
});
|
|
12016
12767
|
}
|
|
12017
12768
|
const prePruneTokens = getCurrentTokenUsage(state, output.messages);
|
|
12769
|
+
const candidateMessages = config.compress.candidates === true ? output.messages.slice() : void 0;
|
|
12018
12770
|
prune(state, logger, config, output.messages);
|
|
12019
|
-
truncateLargeToolOutputs(state, config, logger, output.messages);
|
|
12020
|
-
enforceContextBudget(state, config, logger, output.messages);
|
|
12021
12771
|
hideConsumedCompressCalls(state, output.messages);
|
|
12022
12772
|
assignMessageRefs(state, output.messages);
|
|
12023
12773
|
const compressionPriorities = buildPriorityMap(config, state, output.messages);
|
|
@@ -12042,8 +12792,16 @@ ${text}`);
|
|
|
12042
12792
|
}).catch(() => {
|
|
12043
12793
|
});
|
|
12044
12794
|
} : void 0,
|
|
12045
|
-
prePruneTokens
|
|
12795
|
+
prePruneTokens,
|
|
12796
|
+
candidateMessages
|
|
12797
|
+
);
|
|
12798
|
+
truncateLargeToolOutputs(
|
|
12799
|
+
state,
|
|
12800
|
+
config,
|
|
12801
|
+
logger,
|
|
12802
|
+
output.messages.filter((message) => !isSyntheticMessage(message))
|
|
12046
12803
|
);
|
|
12804
|
+
enforceContextBudget(state, config, logger, output.messages);
|
|
12047
12805
|
injectMessageIds(state, config, output.messages, compressionPriorities);
|
|
12048
12806
|
hideFailedCompressCalls(output.messages);
|
|
12049
12807
|
stripStaleMetadata(output.messages);
|
|
@@ -12478,7 +13236,7 @@ var server = (async (ctx) => {
|
|
|
12478
13236
|
}
|
|
12479
13237
|
const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
|
|
12480
13238
|
logger.info("ACP plugin initialized", {
|
|
12481
|
-
version: true ? "1.
|
|
13239
|
+
version: true ? "1.18.0-pr.392.147" : "dev",
|
|
12482
13240
|
workspace: ctx.directory,
|
|
12483
13241
|
logLevel: logger.level,
|
|
12484
13242
|
debug: config.debug,
|
|
@@ -12486,7 +13244,12 @@ var server = (async (ctx) => {
|
|
|
12486
13244
|
secureMode: isSecureMode()
|
|
12487
13245
|
});
|
|
12488
13246
|
const registry4 = new SessionStateRegistry(logger, ctx.directory);
|
|
12489
|
-
const prompts = new PromptStore(
|
|
13247
|
+
const prompts = new PromptStore(
|
|
13248
|
+
logger,
|
|
13249
|
+
ctx.directory,
|
|
13250
|
+
config.experimental.customPrompts,
|
|
13251
|
+
config.compress.candidates === true
|
|
13252
|
+
);
|
|
12490
13253
|
const hostPermissions = {
|
|
12491
13254
|
global: void 0,
|
|
12492
13255
|
agents: {}
|