opencode-acp 1.17.1 → 1.18.0-pr.392.149
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 +982 -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 +6 -0
- 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: ${
|
|
9153
|
+
` Output: ${candidatePlan.candidates.length} candidate(s) (${candidatePlan.truncatedCount} truncated)`
|
|
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" ? "\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 : "";
|
|
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,16 @@ 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
|
-
const
|
|
9441
|
-
|
|
9442
|
-
visibleRaw,
|
|
9443
|
-
ctx.state,
|
|
9444
|
-
ctx.config?.compress?.protectedTools ?? [],
|
|
9445
|
-
ctx.config?.protectedFilePatterns ?? [],
|
|
9446
|
-
protectedRefs
|
|
9447
|
-
);
|
|
9448
|
-
if (contextRanges.compressible.length > 0 || contextRanges.protected.length > 0) {
|
|
10163
|
+
const candidates = renderCompressionCandidates(rawMessages, ctx);
|
|
10164
|
+
if (candidates.length > 0) {
|
|
9449
10165
|
lines.push("");
|
|
9450
|
-
lines.push(
|
|
9451
|
-
formatCompressibleRanges(contextRanges.compressible, contextRanges.protected)
|
|
9452
|
-
);
|
|
10166
|
+
lines.push(...candidates);
|
|
9453
10167
|
}
|
|
9454
10168
|
}
|
|
9455
10169
|
lines.push("");
|
|
9456
10170
|
const hintTool = topToolName || "bash";
|
|
9457
10171
|
lines.push(
|
|
9458
|
-
`Tip: acp_status({scope:"uncompressed", view:"messages", tool:"${hintTool}"}) for per-message listing`
|
|
10172
|
+
`Tip: acp_status({scope:"uncompressed", view:"ranges"}) for raw ranges, or acp_status({scope:"uncompressed", view:"messages", tool:"${hintTool}"}) for per-message listing`
|
|
9459
10173
|
);
|
|
9460
10174
|
return lines;
|
|
9461
10175
|
}
|
|
@@ -9492,6 +10206,44 @@ function renderUncompressedRanges(rawMessages, ctx) {
|
|
|
9492
10206
|
lines.push(`Filter by tool: acp_status({scope:"uncompressed", view:"messages", tool:"bash"})`);
|
|
9493
10207
|
return lines;
|
|
9494
10208
|
}
|
|
10209
|
+
function renderCompressionCandidates(rawMessages, ctx) {
|
|
10210
|
+
const lines = ["COMPRESSION CANDIDATES"];
|
|
10211
|
+
if (!ctx.config?.compress || ctx.config.compress.candidates !== true) {
|
|
10212
|
+
return renderUncompressedRanges(rawMessages, ctx);
|
|
10213
|
+
}
|
|
10214
|
+
try {
|
|
10215
|
+
const plan = planCompressionCandidates(rawMessages, ctx.state, ctx.config);
|
|
10216
|
+
const visibleMessages = rawMessages.filter((message) => {
|
|
10217
|
+
const entry = ctx.state.prune.messages.byMessageId.get(message.info.id);
|
|
10218
|
+
return !entry || entry.activeBlockIds.length === 0;
|
|
10219
|
+
});
|
|
10220
|
+
const protectedRefs = computeProtectedRefs(visibleMessages, ctx.state, ctx.config.compress);
|
|
10221
|
+
const contextRanges = buildCompressibleRanges(
|
|
10222
|
+
visibleMessages,
|
|
10223
|
+
ctx.state,
|
|
10224
|
+
ctx.config.compress.protectedTools ?? [],
|
|
10225
|
+
ctx.config.protectedFilePatterns ?? [],
|
|
10226
|
+
protectedRefs
|
|
10227
|
+
);
|
|
10228
|
+
const protectedText = formatCompressibleRanges([], contextRanges.protected);
|
|
10229
|
+
if (plan.candidates.length === 0) {
|
|
10230
|
+
lines.push(" (no eligible candidates)");
|
|
10231
|
+
if (protectedText.includes("PROTECTED")) lines.push("", protectedText);
|
|
10232
|
+
return lines;
|
|
10233
|
+
}
|
|
10234
|
+
lines.length = 0;
|
|
10235
|
+
lines.push(formatCompressionCandidates(plan));
|
|
10236
|
+
if (protectedText.includes("PROTECTED")) lines.push("", protectedText);
|
|
10237
|
+
if (plan.omitted.length > 0) {
|
|
10238
|
+
lines.push(
|
|
10239
|
+
` ${plan.omitted.length} structurally unsafe or undersized candidate${plan.omitted.length === 1 ? "" : "s"} omitted`
|
|
10240
|
+
);
|
|
10241
|
+
}
|
|
10242
|
+
return lines;
|
|
10243
|
+
} catch {
|
|
10244
|
+
return ["COMPRESSION CANDIDATES", " (candidate planning unavailable)"];
|
|
10245
|
+
}
|
|
10246
|
+
}
|
|
9495
10247
|
function renderUncompressedDrilldown(visibleMessages, toolFilter, sort, limit) {
|
|
9496
10248
|
const lines = [];
|
|
9497
10249
|
let filtered = visibleMessages;
|
|
@@ -9594,14 +10346,12 @@ function renderCompressedDrilldown(blocks, sort, limit, blocksById) {
|
|
|
9594
10346
|
}
|
|
9595
10347
|
function buildStatusReport(renderCtx, rawMessages, options) {
|
|
9596
10348
|
const scope = options?.scope;
|
|
9597
|
-
const view = options?.view ?? "
|
|
10349
|
+
const view = options?.view ?? "candidates";
|
|
9598
10350
|
const toolFilter = options?.tool;
|
|
9599
10351
|
const sort = options?.sort ?? "size";
|
|
9600
10352
|
const limit = options?.limit ?? 30;
|
|
9601
10353
|
const msgState = renderCtx.state.prune.messages;
|
|
9602
|
-
const allBlocks = Array.from(msgState.blocksById.values()).sort(
|
|
9603
|
-
(a, b) => a.blockId - b.blockId
|
|
9604
|
-
);
|
|
10354
|
+
const allBlocks = Array.from(msgState.blocksById.values()).sort((a, b) => a.blockId - b.blockId);
|
|
9605
10355
|
const activeBlocks = allBlocks.filter((b) => b.active);
|
|
9606
10356
|
const lines = [];
|
|
9607
10357
|
if (scope === "compressed") {
|
|
@@ -9615,8 +10365,10 @@ function buildStatusReport(renderCtx, rawMessages, options) {
|
|
|
9615
10365
|
if (scope === "uncompressed") {
|
|
9616
10366
|
if (view === "messages") {
|
|
9617
10367
|
lines.push(...renderUncompressedDrilldown(visibleMsgs, toolFilter, sort, limit));
|
|
9618
|
-
} else {
|
|
10368
|
+
} else if (view === "ranges") {
|
|
9619
10369
|
lines.push(...renderUncompressedRanges(rawMessages, renderCtx));
|
|
10370
|
+
} else {
|
|
10371
|
+
lines.push(...renderCompressionCandidates(rawMessages, renderCtx));
|
|
9620
10372
|
}
|
|
9621
10373
|
} else {
|
|
9622
10374
|
lines.push(
|
|
@@ -9640,7 +10392,7 @@ function createAcpStatusTool(factoryCtx) {
|
|
|
9640
10392
|
args: {
|
|
9641
10393
|
scope: tool4.schema.string().optional().describe('Drill down: "compressed" or "uncompressed". No arg = overview of both.'),
|
|
9642
10394
|
view: tool4.schema.string().optional().describe(
|
|
9643
|
-
'Display format for scope:"uncompressed": "
|
|
10395
|
+
'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
10396
|
),
|
|
9645
10397
|
tool: tool4.schema.string().optional().describe(
|
|
9646
10398
|
'Filter by tool type (only with scope:"uncompressed", view:"messages"). e.g., "bash", "todowrite", "write"'
|
|
@@ -9651,16 +10403,16 @@ function createAcpStatusTool(factoryCtx) {
|
|
|
9651
10403
|
async execute(args, toolCtx) {
|
|
9652
10404
|
const ctx = resolveToolContext(factoryCtx, toolCtx.sessionID);
|
|
9653
10405
|
const scope = args.scope === "compressed" || args.scope === "uncompressed" ? args.scope : void 0;
|
|
9654
|
-
const view = args.view === "messages"
|
|
10406
|
+
const view = args.view === "messages" || args.view === "ranges" || args.view === "candidates" ? args.view : "candidates";
|
|
9655
10407
|
const toolFilter = typeof args.tool === "string" ? args.tool : void 0;
|
|
9656
10408
|
const sort = args.sort === "time" || args.sort === "tool" || args.sort === "age" ? args.sort : "size";
|
|
9657
10409
|
const limit = Number.isFinite(args.limit) && args.limit > 0 ? Math.min(args.limit, 200) : 30;
|
|
9658
10410
|
if (scope === "compressed") {
|
|
9659
|
-
return buildStatusReport(
|
|
9660
|
-
|
|
9661
|
-
|
|
9662
|
-
|
|
9663
|
-
);
|
|
10411
|
+
return buildStatusReport({ state: ctx.state, config: ctx.config }, [], {
|
|
10412
|
+
scope: "compressed",
|
|
10413
|
+
sort,
|
|
10414
|
+
limit
|
|
10415
|
+
});
|
|
9664
10416
|
}
|
|
9665
10417
|
let rawMessages = [];
|
|
9666
10418
|
try {
|
|
@@ -9670,11 +10422,13 @@ function createAcpStatusTool(factoryCtx) {
|
|
|
9670
10422
|
rawMessages = [];
|
|
9671
10423
|
}
|
|
9672
10424
|
hideConsumedCompressCalls(ctx.state, rawMessages);
|
|
9673
|
-
return buildStatusReport(
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
|
|
10425
|
+
return buildStatusReport({ state: ctx.state, config: ctx.config }, rawMessages, {
|
|
10426
|
+
scope,
|
|
10427
|
+
view,
|
|
10428
|
+
tool: toolFilter,
|
|
10429
|
+
sort,
|
|
10430
|
+
limit
|
|
10431
|
+
});
|
|
9678
10432
|
}
|
|
9679
10433
|
});
|
|
9680
10434
|
}
|
|
@@ -9782,7 +10536,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
|
9782
10536
|
import { join as join4 } from "path";
|
|
9783
10537
|
import { existsSync as existsSync4 } from "fs";
|
|
9784
10538
|
import { homedir as homedir3 } from "os";
|
|
9785
|
-
var LOG_VERSION = true ? "1.
|
|
10539
|
+
var LOG_VERSION = true ? "1.18.0-pr.392.149" : "dev";
|
|
9786
10540
|
var LEVEL_RANK = {
|
|
9787
10541
|
debug: 10,
|
|
9788
10542
|
info: 20,
|
|
@@ -10005,7 +10759,32 @@ import { join as join5, dirname as dirname2 } from "path";
|
|
|
10005
10759
|
import { homedir as homedir4 } from "os";
|
|
10006
10760
|
|
|
10007
10761
|
// lib/prompts/system.ts
|
|
10008
|
-
var
|
|
10762
|
+
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.`;
|
|
10763
|
+
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.`;
|
|
10764
|
+
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.`;
|
|
10765
|
+
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.`;
|
|
10766
|
+
var COMPRESSION_CANDIDATES_SECTION = `COMPRESSION CANDIDATES
|
|
10767
|
+
|
|
10768
|
+
ACP may append a bounded list headed \`COMPRESSION CANDIDATES\` below a nudge or status report:
|
|
10769
|
+
|
|
10770
|
+
- \`MICRO\` is one large plain message or one complete tool transaction, including every message needed to keep its tool call/result structure valid.
|
|
10771
|
+
- \`EPISODE\` is a contiguous historical segment made from smaller adjacent units.
|
|
10772
|
+
- Candidate ranges are independent, non-overlapping, and can be submitted together as separate \`content[]\` entries.
|
|
10773
|
+
- 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.
|
|
10774
|
+
- Keep summaries self-contained and use the existing \`compress\` tool. Candidate labels do not replace the semantic judgment to preserve useful context.
|
|
10775
|
+
- 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.`;
|
|
10776
|
+
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.`;
|
|
10777
|
+
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.`;
|
|
10778
|
+
function buildSystemPrompt(candidatesEnabled) {
|
|
10779
|
+
const acpStatusToolLine = candidatesEnabled ? ACP_STATUS_TOOL_LINE_CANDIDATES : ACP_STATUS_TOOL_LINE_RANGES;
|
|
10780
|
+
const philosophyTail = candidatesEnabled ? PHILOSOPHY_TAIL_CANDIDATES : PHILOSOPHY_TAIL_RANGES;
|
|
10781
|
+
const compressionCandidatesBlock = candidatesEnabled ? `
|
|
10782
|
+
|
|
10783
|
+
${COMPRESSION_CANDIDATES_SECTION}
|
|
10784
|
+
|
|
10785
|
+
` : "\n\n";
|
|
10786
|
+
const breakdownTail = candidatesEnabled ? BREAKDOWN_TAIL_CANDIDATES : BREAKDOWN_TAIL_RANGES;
|
|
10787
|
+
return `
|
|
10009
10788
|
|
|
10010
10789
|
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
10790
|
|
|
@@ -10031,7 +10810,7 @@ You have five context-management tools:
|
|
|
10031
10810
|
- \`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
10811
|
- \`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
10812
|
- \`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
|
-
|
|
10813
|
+
${acpStatusToolLine}
|
|
10035
10814
|
|
|
10036
10815
|
COMPRESSION PHILOSOPHY
|
|
10037
10816
|
|
|
@@ -10039,7 +10818,7 @@ Two failure modes to avoid:
|
|
|
10039
10818
|
- Over-compression: Compressing too aggressively loses critical details, decisions, and state needed for your task. This directly harms task quality.
|
|
10040
10819
|
- Under-compression: Failing to compress verbose outputs causes context overflow, reducing accuracy and eventually blocking your work.
|
|
10041
10820
|
|
|
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.
|
|
10821
|
+
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
10822
|
|
|
10044
10823
|
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
10824
|
|
|
@@ -10060,9 +10839,7 @@ WHEN NOT TO COMPRESS
|
|
|
10060
10839
|
- Important user messages \u2014 preserve their exact intent, constraints, and acceptance criteria verbatim, not just the most recent one.
|
|
10061
10840
|
- Protected tool outputs (default: \`skill\` only) \u2014 hard-excluded from compression ranges, survive intact in visible context.
|
|
10062
10841
|
|
|
10063
|
-
${HOW_TO_COMPRESS_RULES}
|
|
10064
|
-
|
|
10065
|
-
MULTI-TIER COMPRESSION
|
|
10842
|
+
${HOW_TO_COMPRESS_RULES}${compressionCandidatesBlock}MULTI-TIER COMPRESSION
|
|
10066
10843
|
|
|
10067
10844
|
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
10845
|
|
|
@@ -10087,13 +10864,21 @@ Breakdown: 4.2K system (21%) | 8.0K tool (40%) | 2.0K summaries (10%) | 2.6K cod
|
|
|
10087
10864
|
- "text" = plain text messages
|
|
10088
10865
|
- "reasoning" = model thinking blocks (counted to match real API usage; freed when their message is compressed)
|
|
10089
10866
|
|
|
10090
|
-
|
|
10867
|
+
${breakdownTail}
|
|
10091
10868
|
|
|
10092
10869
|
Each compression creates a reusable summary block you can decompress later if needed.
|
|
10093
10870
|
`;
|
|
10871
|
+
}
|
|
10872
|
+
var SYSTEM = buildSystemPrompt(true);
|
|
10094
10873
|
|
|
10095
10874
|
// lib/prompts/compress-range.ts
|
|
10096
|
-
var
|
|
10875
|
+
var CANDIDATE_GUIDANCE_SECTION = `CANDIDATE GUIDANCE
|
|
10876
|
+
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.`;
|
|
10877
|
+
function buildCompressRangePrompt(candidatesEnabled) {
|
|
10878
|
+
const candidateGuidanceBlock = candidatesEnabled ? `${CANDIDATE_GUIDANCE_SECTION}
|
|
10879
|
+
|
|
10880
|
+
` : "";
|
|
10881
|
+
return `Collapse a range in the conversation into a detailed summary.
|
|
10097
10882
|
|
|
10098
10883
|
COMPRESSED BLOCK PLACEHOLDERS
|
|
10099
10884
|
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 +10908,7 @@ Rules:
|
|
|
10123
10908
|
|
|
10124
10909
|
- Pick \`startId\` and \`endId\` directly from injected IDs in context.
|
|
10125
10910
|
- 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\`
|
|
10911
|
+
- Prefer \`startId\` before \`endId\` in conversation order. ACP can normalize reversed boundaries, but do not rely on that behavior.
|
|
10127
10912
|
- Do not invent IDs. Use only IDs that are present in context.
|
|
10128
10913
|
- 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
10914
|
|
|
@@ -10140,7 +10925,7 @@ compress({ content: [
|
|
|
10140
10925
|
]})
|
|
10141
10926
|
\`\`\`
|
|
10142
10927
|
|
|
10143
|
-
KEEP AND REF MARKERS
|
|
10928
|
+
${candidateGuidanceBlock}KEEP AND REF MARKERS
|
|
10144
10929
|
When writing a summary, you may embed markers that reference specific messages in the compressed range. The system resolves them automatically:
|
|
10145
10930
|
|
|
10146
10931
|
- \`[[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 +10942,8 @@ The rest of the bash calls were repetitive export commands. See [[REF:m00078|tes
|
|
|
10157
10942
|
|
|
10158
10943
|
Use KEEP sparingly \u2014 each expansion adds to the summary length. Prefer REF for content that is important but not immediately critical.
|
|
10159
10944
|
`;
|
|
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
|
-
}
|
|
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
10945
|
}
|
|
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
|
-
`;
|
|
10946
|
+
var COMPRESS_RANGE = buildCompressRangePrompt(true);
|
|
10219
10947
|
|
|
10220
10948
|
// lib/prompts/extensions/system.ts
|
|
10221
10949
|
var SUBAGENT_SYSTEM_EXTENSION = `<dcp-system-reminder>
|
|
@@ -10307,24 +11035,27 @@ var HTML_COMMENT_REGEX = /<!--[\s\S]*?-->/g;
|
|
|
10307
11035
|
var LEGACY_INLINE_COMMENT_LINE_REGEX = /^[ \t]*\/\/.*?\/\/[ \t]*$/gm;
|
|
10308
11036
|
var DCP_SYSTEM_REMINDER_TAG_REGEX = /^\s*<dcp-system-reminder\b[^>]*>[\s\S]*<\/(?:dcp|acp)-system-reminder>\s*$/i;
|
|
10309
11037
|
var DEFAULTS_README_FILE = "README.md";
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
|
|
11038
|
+
function getBundledEditablePrompts(candidatesEnabled) {
|
|
11039
|
+
return {
|
|
11040
|
+
system: buildSystemPrompt(candidatesEnabled),
|
|
11041
|
+
compressRange: buildCompressRangePrompt(candidatesEnabled),
|
|
11042
|
+
contextLimitNudge: CONTEXT_LIMIT_NUDGE,
|
|
11043
|
+
turnNudge: TURN_NUDGE,
|
|
11044
|
+
iterationNudge: ITERATION_NUDGE
|
|
11045
|
+
};
|
|
11046
|
+
}
|
|
10317
11047
|
var INTERNAL_PROMPT_EXTENSIONS = {
|
|
10318
11048
|
subagentExtension: SUBAGENT_SYSTEM_EXTENSION,
|
|
10319
11049
|
decompressExtension: DECOMPRESS_SYSTEM_EXTENSION
|
|
10320
11050
|
};
|
|
10321
|
-
function createBundledRuntimePrompts() {
|
|
11051
|
+
function createBundledRuntimePrompts(candidatesEnabled) {
|
|
11052
|
+
const bundled = getBundledEditablePrompts(candidatesEnabled);
|
|
10322
11053
|
return {
|
|
10323
|
-
system:
|
|
10324
|
-
compressRange:
|
|
10325
|
-
contextLimitNudge:
|
|
10326
|
-
turnNudge:
|
|
10327
|
-
iterationNudge:
|
|
11054
|
+
system: bundled.system,
|
|
11055
|
+
compressRange: bundled.compressRange,
|
|
11056
|
+
contextLimitNudge: bundled.contextLimitNudge,
|
|
11057
|
+
turnNudge: bundled.turnNudge,
|
|
11058
|
+
iterationNudge: bundled.iterationNudge,
|
|
10328
11059
|
subagentExtension: INTERNAL_PROMPT_EXTENSIONS.subagentExtension,
|
|
10329
11060
|
decompressExtension: INTERNAL_PROMPT_EXTENSIONS.decompressExtension
|
|
10330
11061
|
};
|
|
@@ -10469,12 +11200,14 @@ var PromptStore = class {
|
|
|
10469
11200
|
logger;
|
|
10470
11201
|
paths;
|
|
10471
11202
|
customPromptsEnabled;
|
|
11203
|
+
candidatesEnabled;
|
|
10472
11204
|
runtimePrompts;
|
|
10473
|
-
constructor(logger, workingDirectory, customPromptsEnabled = false) {
|
|
11205
|
+
constructor(logger, workingDirectory, customPromptsEnabled = false, candidatesEnabled = false) {
|
|
10474
11206
|
this.logger = logger;
|
|
10475
11207
|
this.paths = resolvePromptPaths(workingDirectory);
|
|
10476
11208
|
this.customPromptsEnabled = customPromptsEnabled;
|
|
10477
|
-
this.
|
|
11209
|
+
this.candidatesEnabled = candidatesEnabled;
|
|
11210
|
+
this.runtimePrompts = createBundledRuntimePrompts(this.candidatesEnabled);
|
|
10478
11211
|
if (this.customPromptsEnabled) {
|
|
10479
11212
|
this.ensureDefaultFiles();
|
|
10480
11213
|
}
|
|
@@ -10484,13 +11217,13 @@ var PromptStore = class {
|
|
|
10484
11217
|
return { ...this.runtimePrompts };
|
|
10485
11218
|
}
|
|
10486
11219
|
reload() {
|
|
10487
|
-
const nextPrompts = createBundledRuntimePrompts();
|
|
11220
|
+
const nextPrompts = createBundledRuntimePrompts(this.candidatesEnabled);
|
|
10488
11221
|
if (!this.customPromptsEnabled) {
|
|
10489
11222
|
this.runtimePrompts = nextPrompts;
|
|
10490
11223
|
return;
|
|
10491
11224
|
}
|
|
10492
11225
|
for (const definition of PROMPT_DEFINITIONS) {
|
|
10493
|
-
const bundledSource =
|
|
11226
|
+
const bundledSource = getBundledEditablePrompts(this.candidatesEnabled)[definition.runtimeField];
|
|
10494
11227
|
const bundledEditable = toEditablePromptText(definition, bundledSource);
|
|
10495
11228
|
const bundledRuntime = wrapRuntimePromptContent(definition, bundledEditable);
|
|
10496
11229
|
const fallbackValue = bundledRuntime || bundledSource.trim();
|
|
@@ -10551,13 +11284,14 @@ var PromptStore = class {
|
|
|
10551
11284
|
});
|
|
10552
11285
|
return;
|
|
10553
11286
|
}
|
|
11287
|
+
const bundledPrompts = getBundledEditablePrompts(this.candidatesEnabled);
|
|
10554
11288
|
for (const definition of PROMPT_DEFINITIONS) {
|
|
10555
11289
|
const bundledEditable = toEditablePromptText(
|
|
10556
11290
|
definition,
|
|
10557
|
-
|
|
11291
|
+
bundledPrompts[definition.runtimeField]
|
|
10558
11292
|
);
|
|
10559
11293
|
const managedContent = buildDefaultPromptFileContent(
|
|
10560
|
-
bundledEditable ||
|
|
11294
|
+
bundledEditable || bundledPrompts[definition.runtimeField]
|
|
10561
11295
|
);
|
|
10562
11296
|
const filePath = join5(this.paths.defaultsDir, definition.fileName);
|
|
10563
11297
|
try {
|
|
@@ -12002,8 +12736,8 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
|
|
|
12002
12736
|
cacheSystemPromptTokens(state, output.messages);
|
|
12003
12737
|
assignMessageRefs(state, output.messages);
|
|
12004
12738
|
const activeBlockCountBefore = state.prune.messages.activeBlockIds.size;
|
|
12005
|
-
syncCompressionBlocks(state, logger, output.messages);
|
|
12006
|
-
if (state.prune.messages.activeBlockIds.size !== activeBlockCountBefore) {
|
|
12739
|
+
const compressionStateChanged = syncCompressionBlocks(state, logger, output.messages);
|
|
12740
|
+
if (compressionStateChanged || state.prune.messages.activeBlockIds.size !== activeBlockCountBefore) {
|
|
12007
12741
|
saveSessionState(state, logger).catch(() => {
|
|
12008
12742
|
});
|
|
12009
12743
|
}
|
|
@@ -12015,9 +12749,8 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
|
|
|
12015
12749
|
});
|
|
12016
12750
|
}
|
|
12017
12751
|
const prePruneTokens = getCurrentTokenUsage(state, output.messages);
|
|
12752
|
+
const candidateMessages = output.messages.slice();
|
|
12018
12753
|
prune(state, logger, config, output.messages);
|
|
12019
|
-
truncateLargeToolOutputs(state, config, logger, output.messages);
|
|
12020
|
-
enforceContextBudget(state, config, logger, output.messages);
|
|
12021
12754
|
hideConsumedCompressCalls(state, output.messages);
|
|
12022
12755
|
assignMessageRefs(state, output.messages);
|
|
12023
12756
|
const compressionPriorities = buildPriorityMap(config, state, output.messages);
|
|
@@ -12042,8 +12775,16 @@ ${text}`);
|
|
|
12042
12775
|
}).catch(() => {
|
|
12043
12776
|
});
|
|
12044
12777
|
} : void 0,
|
|
12045
|
-
prePruneTokens
|
|
12778
|
+
prePruneTokens,
|
|
12779
|
+
candidateMessages
|
|
12780
|
+
);
|
|
12781
|
+
truncateLargeToolOutputs(
|
|
12782
|
+
state,
|
|
12783
|
+
config,
|
|
12784
|
+
logger,
|
|
12785
|
+
output.messages.filter((message) => !isSyntheticMessage(message))
|
|
12046
12786
|
);
|
|
12787
|
+
enforceContextBudget(state, config, logger, output.messages);
|
|
12047
12788
|
injectMessageIds(state, config, output.messages, compressionPriorities);
|
|
12048
12789
|
hideFailedCompressCalls(output.messages);
|
|
12049
12790
|
stripStaleMetadata(output.messages);
|
|
@@ -12478,7 +13219,7 @@ var server = (async (ctx) => {
|
|
|
12478
13219
|
}
|
|
12479
13220
|
const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
|
|
12480
13221
|
logger.info("ACP plugin initialized", {
|
|
12481
|
-
version: true ? "1.
|
|
13222
|
+
version: true ? "1.18.0-pr.392.149" : "dev",
|
|
12482
13223
|
workspace: ctx.directory,
|
|
12483
13224
|
logLevel: logger.level,
|
|
12484
13225
|
debug: config.debug,
|
|
@@ -12486,7 +13227,12 @@ var server = (async (ctx) => {
|
|
|
12486
13227
|
secureMode: isSecureMode()
|
|
12487
13228
|
});
|
|
12488
13229
|
const registry4 = new SessionStateRegistry(logger, ctx.directory);
|
|
12489
|
-
const prompts = new PromptStore(
|
|
13230
|
+
const prompts = new PromptStore(
|
|
13231
|
+
logger,
|
|
13232
|
+
ctx.directory,
|
|
13233
|
+
config.experimental.customPrompts,
|
|
13234
|
+
config.compress.candidates === true
|
|
13235
|
+
);
|
|
12490
13236
|
const hostPermissions = {
|
|
12491
13237
|
global: void 0,
|
|
12492
13238
|
agents: {}
|