opencode-acp 1.17.1-pr.393.150 → 1.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/index.d.ts.map +1 -1
  2. package/dist/index.js +242 -1005
  3. package/dist/index.js.map +1 -1
  4. package/dist/lib/compress/range-utils.d.ts +1 -16
  5. package/dist/lib/compress/range-utils.d.ts.map +1 -1
  6. package/dist/lib/compress/range.d.ts.map +1 -1
  7. package/dist/lib/compress/search.d.ts +1 -3
  8. package/dist/lib/compress/search.d.ts.map +1 -1
  9. package/dist/lib/compress/status.d.ts +1 -1
  10. package/dist/lib/compress/status.d.ts.map +1 -1
  11. package/dist/lib/config-validation.d.ts.map +1 -1
  12. package/dist/lib/config.d.ts +1 -7
  13. package/dist/lib/config.d.ts.map +1 -1
  14. package/dist/lib/hooks.d.ts.map +1 -1
  15. package/dist/lib/messages/inject/inject.d.ts +1 -1
  16. package/dist/lib/messages/inject/inject.d.ts.map +1 -1
  17. package/dist/lib/messages/sync.d.ts +1 -1
  18. package/dist/lib/messages/sync.d.ts.map +1 -1
  19. package/dist/lib/prompts/compress-range.d.ts +1 -9
  20. package/dist/lib/prompts/compress-range.d.ts.map +1 -1
  21. package/dist/lib/prompts/context-limit-nudge.d.ts +0 -6
  22. package/dist/lib/prompts/context-limit-nudge.d.ts.map +1 -1
  23. package/dist/lib/prompts/iteration-nudge.d.ts +0 -5
  24. package/dist/lib/prompts/iteration-nudge.d.ts.map +1 -1
  25. package/dist/lib/prompts/store.d.ts +1 -2
  26. package/dist/lib/prompts/store.d.ts.map +1 -1
  27. package/dist/lib/prompts/system.d.ts +1 -10
  28. package/dist/lib/prompts/system.d.ts.map +1 -1
  29. package/dist/lib/prompts/turn-nudge.d.ts +0 -5
  30. package/dist/lib/prompts/turn-nudge.d.ts.map +1 -1
  31. package/dist/lib/state/rebuild.d.ts +0 -8
  32. package/dist/lib/state/rebuild.d.ts.map +1 -1
  33. package/dist/lib/state/state.d.ts.map +1 -1
  34. package/dist/lib/state/types.d.ts +0 -2
  35. package/dist/lib/state/types.d.ts.map +1 -1
  36. package/dist/lib/state/utils.d.ts +0 -1
  37. package/dist/lib/state/utils.d.ts.map +1 -1
  38. package/package.json +1 -1
  39. package/dist/lib/messages/inject/candidates.d.ts +0 -30
  40. package/dist/lib/messages/inject/candidates.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -885,7 +885,6 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
885
885
  "compress.permission",
886
886
  "compress.showCompression",
887
887
  "compress.summaryBuffer",
888
- "compress.candidates",
889
888
  "compress.maxContextLimit",
890
889
  "compress.minContextLimit",
891
890
  "compress.modelMaxLimits",
@@ -1760,7 +1759,6 @@ var defaultConfig = {
1760
1759
  permission: "allow",
1761
1760
  showCompression: true,
1762
1761
  summaryBuffer: true,
1763
- candidates: false,
1764
1762
  maxContextLimit: "80%",
1765
1763
  minContextLimit: "80%",
1766
1764
  contextLimitFallback: 128e3,
@@ -1929,7 +1927,6 @@ function mergeCompress(base, override) {
1929
1927
  permission: override.permission ?? base.permission,
1930
1928
  showCompression: override.showCompression ?? base.showCompression,
1931
1929
  summaryBuffer: override.summaryBuffer ?? base.summaryBuffer,
1932
- candidates: override.candidates ?? base.candidates,
1933
1930
  maxContextLimit: override.maxContextLimit ?? base.maxContextLimit,
1934
1931
  minContextLimit: override.minContextLimit ?? base.minContextLimit,
1935
1932
  modelMaxLimits: override.modelMaxLimits ?? base.modelMaxLimits,
@@ -2516,12 +2513,12 @@ function serializePruneMessagesState(messagesState) {
2516
2513
  markedForCleanup: Array.from(messagesState.markedForCleanup)
2517
2514
  };
2518
2515
  }
2519
- async function getSessionParentId(client, sessionID) {
2516
+ async function isSubAgentSession(client, sessionID) {
2520
2517
  try {
2521
2518
  const result = await client.session.get({ path: { id: sessionID } });
2522
- return typeof result.data?.parentID === "string" ? result.data.parentID : void 0;
2519
+ return !!result.data?.parentID;
2523
2520
  } catch (error) {
2524
- return void 0;
2521
+ return false;
2525
2522
  }
2526
2523
  }
2527
2524
  function findLastCompactionTimestamp(messages) {
@@ -2562,8 +2559,7 @@ function createPruneMessagesState() {
2562
2559
  activeByAnchorMessageId: /* @__PURE__ */ new Map(),
2563
2560
  nextBlockId: 1,
2564
2561
  nextRunId: 1,
2565
- markedForCleanup: /* @__PURE__ */ new Set(),
2566
- membershipsVerified: false
2562
+ markedForCleanup: /* @__PURE__ */ new Set()
2567
2563
  };
2568
2564
  }
2569
2565
  function loadPruneMessagesState(persisted) {
@@ -3695,7 +3691,7 @@ function clampMessageRef(requested, context, state) {
3695
3691
  if (maxIndex < 0 || requested.index <= maxIndex) return null;
3696
3692
  return { ref: formatMessageRef(maxIndex) };
3697
3693
  }
3698
- function resolveSelection(context, startReference, endReference, options) {
3694
+ function resolveSelection(context, startReference, endReference) {
3699
3695
  const startRawIndex = startReference.rawIndex;
3700
3696
  const endRawIndex = endReference.rawIndex;
3701
3697
  const messageIds = [];
@@ -3718,7 +3714,7 @@ function resolveSelection(context, startReference, endReference, options) {
3718
3714
  messageSeen.add(messageId);
3719
3715
  messageIds.push(messageId);
3720
3716
  }
3721
- if (options?.includeTokenAccounting !== false && !messageTokenById.has(messageId)) {
3717
+ if (!messageTokenById.has(messageId)) {
3722
3718
  messageTokenById.set(messageId, estimateAllMessageTokensFast(rawMessage));
3723
3719
  }
3724
3720
  const parts = Array.isArray(rawMessage.parts) ? rawMessage.parts : [];
@@ -3829,10 +3825,10 @@ function buildBoundaryLookup(context, state) {
3829
3825
  }
3830
3826
  return lookup;
3831
3827
  }
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.
3828
+ var SEARCH_CONTEXT_TOOL_DESCRIPTION = `Search through all compressed block summaries AND visible messages to find relevant content. Use this BEFORE decompressing to find the right block. Returns a hit list with block/message IDs, relevance scores, and previews.
3833
3829
 
3834
3830
  Examples:
3835
- - search_context({ query: "decoder accuracy" }) \u2014 find compressed blocks about decoder accuracy
3831
+ - search_context({ query: "decoder accuracy" }) \u2014 find blocks/messages about decoder accuracy
3836
3832
  - search_context({ query: "training loss PPL" }) \u2014 find training results
3837
3833
  - search_context({ query: "architecture design", limit: 5 }) \u2014 top 5 results`;
3838
3834
  function countOccurrences(text, term) {
@@ -3863,7 +3859,7 @@ function createSearchContextTool(factoryCtx) {
3863
3859
  query: tool.schema.string().describe("Search query \u2014 keywords or phrase to find"),
3864
3860
  limit: tool.schema.number().optional().describe("Maximum results to return (default: 10)"),
3865
3861
  deep: tool.schema.boolean().optional().describe(
3866
- "Reserved for compatibility; the current search indexes active compressed block summaries."
3862
+ "If true, also search visible (uncompressed) messages. Slower but more thorough (default: false)"
3867
3863
  )
3868
3864
  },
3869
3865
  async execute(args, toolCtx) {
@@ -4315,7 +4311,7 @@ function validateArgs(args) {
4315
4311
  }
4316
4312
  }
4317
4313
  }
4318
- function resolveRanges(args, searchContext, state, logger, options) {
4314
+ function resolveRanges(args, searchContext, state, logger) {
4319
4315
  return args.content.map((entry, index) => {
4320
4316
  const normalizedEntry = {
4321
4317
  topic: typeof entry.topic === "string" && entry.topic.trim().length > 0 ? entry.topic.trim() : void 0,
@@ -4330,7 +4326,7 @@ function resolveRanges(args, searchContext, state, logger, options) {
4330
4326
  normalizedEntry.endId,
4331
4327
  logger
4332
4328
  );
4333
- const selection = resolveSelection(searchContext, startReference, endReference, options);
4329
+ const selection = resolveSelection(searchContext, startReference, endReference);
4334
4330
  return {
4335
4331
  index,
4336
4332
  entry: normalizedEntry,
@@ -4363,54 +4359,6 @@ function validateNonOverlapping(plans) {
4363
4359
  );
4364
4360
  }
4365
4361
  }
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
- }
4414
4362
  function parseBlockPlaceholders(summary) {
4415
4363
  const placeholders = [];
4416
4364
  const regex = new RegExp(BLOCK_PLACEHOLDER_REGEX);
@@ -4482,7 +4430,6 @@ function appendMissingBlockSummaries(summary, _missingBlockIds, _summaryByBlockI
4482
4430
  }
4483
4431
 
4484
4432
  // lib/state/rebuild.ts
4485
- import { createHash } from "crypto";
4486
4433
  function collectCompressInvocations(messages) {
4487
4434
  const invocations = [];
4488
4435
  for (const message of messages) {
@@ -4534,128 +4481,6 @@ function dedupeBlockIds(ids) {
4534
4481
  }
4535
4482
  return result;
4536
4483
  }
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
- }
4659
4484
  function rebuildRangeInvocation(state, input, searchContext, invocation, protectedTools, protectedFilePatterns, gcConfig, logger) {
4660
4485
  const plans = resolveRanges(input, searchContext, state);
4661
4486
  const runId = allocateRunId(state);
@@ -4722,7 +4547,12 @@ function resolveMessageEntry(entry, searchContext, state) {
4722
4547
  return null;
4723
4548
  }
4724
4549
  try {
4725
- const { startReference, endReference } = resolveBoundaryIds(searchContext, state, ref, ref);
4550
+ const { startReference, endReference } = resolveBoundaryIds(
4551
+ searchContext,
4552
+ state,
4553
+ ref,
4554
+ ref
4555
+ );
4726
4556
  const selection = resolveSelection(searchContext, startReference, endReference);
4727
4557
  return {
4728
4558
  selection,
@@ -5026,9 +4856,8 @@ async function ensureSessionInitialized(client, state, sessionId, logger, messag
5026
4856
  resetSessionState(state);
5027
4857
  state.sessionId = sessionId;
5028
4858
  state.storageDir = config?.storagePath ? resolveStorageDir(config.storagePath, projectDir ?? cwd()) : void 0;
5029
- const parentSessionId = await getSessionParentId(client, sessionId);
5030
- const isChildSession = parentSessionId !== void 0;
5031
- state.isSubAgent = isChildSession;
4859
+ const isSubAgent = await isSubAgentSession(client, sessionId);
4860
+ state.isSubAgent = isSubAgent;
5032
4861
  state.lastCompaction = findLastCompactionTimestamp(messages);
5033
4862
  state.currentTurn = countTurns(state, messages);
5034
4863
  state.nudges.turnNudgeAnchors = collectTurnNudgeAnchors(messages);
@@ -5042,42 +4871,13 @@ async function ensureSessionInitialized(client, state, sessionId, logger, messag
5042
4871
  );
5043
4872
  }
5044
4873
  if (config) {
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) {
4874
+ const rebuilt = rebuildCompressionState(state, messages, config, logger);
4875
+ if (rebuilt > 0) {
5074
4876
  await saveSessionState(state, logger);
5075
4877
  }
5076
4878
  }
5077
- state.isSubAgent = isChildSession;
5078
4879
  return;
5079
4880
  }
5080
- state.isSubAgent = isChildSession;
5081
4881
  state.prune.messages = loadPruneMessagesState(persisted.prune.messages);
5082
4882
  state.nudges.contextLimitAnchors = new Set(persisted.nudges.contextLimitAnchors || []);
5083
4883
  state.nudges.turnNudgeAnchors = /* @__PURE__ */ new Set([
@@ -6788,12 +6588,12 @@ ${afterClose}`;
6788
6588
  }
6789
6589
 
6790
6590
  // lib/messages/utils.ts
6791
- import { createHash as createHash2 } from "crypto";
6591
+ import { createHash } from "crypto";
6792
6592
  var SUMMARY_ID_HASH_LENGTH = 16;
6793
6593
  var DCP_PAIRED_TAG_REGEX = /<(?:dcp|acp)[^>]*>[\s\S]*?<\/(?:dcp|acp)[^>]*>/gi;
6794
6594
  var DCP_UNPAIRED_TAG_REGEX = /<\/?(?:dcp|acp)[^>]*>/gi;
6795
6595
  var generateStableId = (prefix, seed) => {
6796
- const hash = createHash2("sha256").update(seed).digest("hex").slice(0, SUMMARY_ID_HASH_LENGTH);
6596
+ const hash = createHash("sha256").update(seed).digest("hex").slice(0, SUMMARY_ID_HASH_LENGTH);
6797
6597
  return `${prefix}_${hash}`;
6798
6598
  };
6799
6599
  var createSyntheticMessage = (baseMessage, content, stableSeed, role = "user") => {
@@ -7790,13 +7590,58 @@ function createCompressRangeTool(factoryCtx) {
7790
7590
  ...ctx0,
7791
7591
  config: applyCompressOverrides(ctx0.config, providerId, modelId)
7792
7592
  };
7793
- const { plans: filteredPlans } = prepareExecutableRangePlans(
7794
- input,
7795
- searchContext,
7796
- ctx.state,
7797
- ctx.config,
7798
- ctx.logger
7799
- );
7593
+ const resolvedPlans = resolveRanges(input, searchContext, ctx.state, ctx.logger);
7594
+ validateNonOverlapping(resolvedPlans);
7595
+ const filteredPlans = resolvedPlans.map((plan) => ({
7596
+ ...plan,
7597
+ selection: filterProtectedToolMessages(
7598
+ plan.selection,
7599
+ searchContext,
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
+ }
7800
7645
  const notifications = [];
7801
7646
  let preparedPlans = [];
7802
7647
  let totalCompressedMessages = 0;
@@ -7901,9 +7746,7 @@ function createCompressRangeTool(factoryCtx) {
7901
7746
  const bypassQuality = acknowledgeRisk && ctx.state.qualityGateRetryPending;
7902
7747
  const ignoredAcknowledgeRisk = acknowledgeRisk && !ctx.state.qualityGateRetryPending;
7903
7748
  if (ignoredAcknowledgeRisk) {
7904
- ctx.logger.warn(
7905
- "compress: acknowledgeRisk ignored \u2014 no quality gate rejection pending"
7906
- );
7749
+ ctx.logger.warn("compress: acknowledgeRisk ignored \u2014 no quality gate rejection pending");
7907
7750
  }
7908
7751
  ctx.state.qualityGateRetryPending = false;
7909
7752
  if (!bypassQuality) {
@@ -8073,24 +7916,10 @@ function sortBlocksByCreation(a, b) {
8073
7916
  }
8074
7917
  return a.blockId - b.blockId;
8075
7918
  }
8076
- function sameBlockIds(left, right) {
8077
- if (left.size !== right.size) return false;
8078
- return Array.from(left).every((id) => right.has(id));
8079
- }
8080
7919
  var syncCompressionBlocks = (state, logger, messages) => {
8081
7920
  const messagesState = state.prune.messages;
8082
7921
  if (!messagesState?.blocksById?.size) {
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;
7922
+ return;
8094
7923
  }
8095
7924
  const messageIds = new Set(messages.map((msg) => msg.info.id));
8096
7925
  const structureVersion = messagesState.structureVersion ?? 0;
@@ -8108,12 +7937,11 @@ var syncCompressionBlocks = (state, logger, messages) => {
8108
7937
  previous.set(anchor, id);
8109
7938
  }
8110
7939
  }
8111
- return false;
7940
+ return;
8112
7941
  }
8113
7942
  const previousActiveBlockIds = new Set(
8114
7943
  Array.from(messagesState.blocksById.values()).filter((block) => block.active).map((block) => block.blockId)
8115
7944
  );
8116
- const indexedActiveBlockIds = new Set(messagesState.activeBlockIds);
8117
7945
  messagesState.activeBlockIds.clear();
8118
7946
  messagesState.activeByAnchorMessageId.clear();
8119
7947
  const now = Date.now();
@@ -8153,14 +7981,10 @@ var syncCompressionBlocks = (state, logger, messages) => {
8153
7981
  messagesState.activeByAnchorMessageId.set(block.anchorMessageId, block.blockId);
8154
7982
  }
8155
7983
  }
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;
7984
+ for (const entry of messagesState.byMessageId.values()) {
7985
+ const allBlockIds = Array.isArray(entry.allBlockIds) ? [...new Set(entry.allBlockIds.filter((id) => Number.isInteger(id) && id > 0))] : [];
7986
+ entry.allBlockIds = allBlockIds;
7987
+ entry.activeBlockIds = allBlockIds.filter((id) => messagesState.activeBlockIds.has(id));
8164
7988
  }
8165
7989
  const nextActiveBlockIds = messagesState.activeBlockIds;
8166
7990
  let deactivatedCount = 0;
@@ -8182,7 +8006,6 @@ var syncCompressionBlocks = (state, logger, messages) => {
8182
8006
  });
8183
8007
  }
8184
8008
  messagesState.lastSyncedStructureVersion = structureVersion;
8185
- return membershipsRebuilt || deactivatedCount > 0 || reactivatedCount > 0;
8186
8009
  };
8187
8010
 
8188
8011
  // lib/host-permissions.ts
@@ -8257,484 +8080,6 @@ var syncCompressPermissionState = (state, config, hostPermissions, messages) =>
8257
8080
  );
8258
8081
  };
8259
8082
 
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
-
8738
8083
  // lib/messages/inject/inject.ts
8739
8084
  var ACP_SUFFIX_SEED = "acp-dynamic-guidance";
8740
8085
  function createSuffixMessage(messages) {
@@ -8744,7 +8089,7 @@ function createSuffixMessage(messages) {
8744
8089
  messages.push(synthetic);
8745
8090
  return synthetic;
8746
8091
  }
8747
- var injectCompressNudges = (state, config, logger, messages, prompts, compressionPriorities, debugNotify, preCompressTokens, candidateMessages) => {
8092
+ var injectCompressNudges = (state, config, logger, messages, prompts, compressionPriorities, debugNotify, preCompressTokens) => {
8748
8093
  if (compressPermission(state, config) === "deny") {
8749
8094
  return;
8750
8095
  }
@@ -8856,10 +8201,7 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
8856
8201
  (message) => message.info.id === lastUserMessage.info.id
8857
8202
  );
8858
8203
  if (lastUserMessageIndex >= 0) {
8859
- const messagesSinceUser = countMessagesAfterIndex(
8860
- messages,
8861
- lastUserMessageIndex
8862
- );
8204
+ const messagesSinceUser = countMessagesAfterIndex(messages, lastUserMessageIndex);
8863
8205
  const iterationThreshold = getIterationNudgeThreshold(config);
8864
8206
  if (lastMessage.index > lastUserMessageIndex && messagesSinceUser >= iterationThreshold) {
8865
8207
  const interval = getNudgeFrequency(config);
@@ -8934,69 +8276,24 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
8934
8276
  config.protectedFilePatterns,
8935
8277
  protectedRefs
8936
8278
  ) : { compressible: [], protected: [] };
8937
- const unprotectedCompressible = excludeProtectedRanges(
8938
- contextRanges.compressible,
8939
- protectedRefs
8940
- );
8279
+ const unprotectedCompressible = excludeProtectedRanges(contextRanges.compressible, protectedRefs);
8941
8280
  const recommendedRanges = filterRecommendedRanges(
8942
8281
  unprotectedCompressible,
8943
8282
  contextRanges.protected,
8944
8283
  { logger, minEffectiveTokens: resolveEffectiveFloor(config) }
8945
8284
  );
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) : "";
8285
+ const hasRecommendations = recommendedRanges.length > 0;
8963
8286
  const allProtected = contextRanges.compressible.length === 0 && contextRanges.protected.length > 0;
8964
8287
  const allInProtectedZone = protectedRefs.size > 0 && unprotectedCompressible.length === 0;
8965
8288
  const allBelowMin = contextRanges.compressible.length > 0 && recommendedRanges.length === 0;
8966
- const noExecutableCandidates = candidatesEnabled && nudgeAllowed && candidateMessages !== void 0 && candidatePlan.candidates.length === 0;
8967
- const nothingToCompress = candidatesEnabled && candidateMessages !== void 0 ? noExecutableCandidates : allProtected || allInProtectedZone || allBelowMin;
8289
+ const nothingToCompress = allProtected || allInProtectedZone || allBelowMin;
8968
8290
  const emergencyNoTargets = emergencyOverride && nothingToCompress;
8969
8291
  const noticeCadenceMet = state.nudges.lastNudgeShownTokens === void 0 || growthSinceBaseline !== void 0 && growthSinceBaseline >= growthFloor;
8970
8292
  const shouldInjectNudge = nudgeAllowed && !nothingToCompress;
8971
8293
  const shouldInjectNotice = emergencyNoTargets && noticeCadenceMet;
8972
8294
  let shouldInject = shouldInjectNudge || shouldInjectNotice;
8973
8295
  if (shouldInjectNudge) {
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
- }
8296
+ applyAnchoredNudges(state, config, messages, prompts, compressionPriorities, currentTokens, modelContextLimit, suffixMessage);
9000
8297
  }
9001
8298
  if (state.nudges.lastPerMessageNudgeTokens === void 0 && currentTokens !== void 0) {
9002
8299
  state.nudges.lastPerMessageNudgeTokens = currentTokens;
@@ -9005,26 +8302,14 @@ ${ITERATION_CANDIDATE_GUIDANCE}` : ITERATION_CANDIDATE_GUIDANCE
9005
8302
  if (suffixMessage && !shouldInject) {
9006
8303
  const tierUsage = tierUsageEarly;
9007
8304
  const tierChecks = [
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
- }
8305
+ { triggerTier: 2, targetTier: 1, tokens: tierUsage.tier1Tokens, lastNudge: state.nudges.lastTier2NudgeTokens },
8306
+ { triggerTier: 3, targetTier: 2, tokens: tierUsage.tier2Tokens, lastNudge: state.nudges.lastTier3NudgeTokens }
9020
8307
  ];
9021
8308
  for (const tc of tierChecks) {
9022
8309
  if (tc.tokens < nudgeGrowthTokens) continue;
9023
8310
  const cadenceMet = tc.lastNudge === void 0 || currentTokens !== void 0 && currentTokens - tc.lastNudge >= growthFloor;
9024
8311
  if (!cadenceMet) continue;
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);
8312
+ let candidates = [...state.prune.messages.activeBlockIds].map((id) => state.prune.messages.blocksById.get(id)).filter((b) => b !== void 0 && b.active && (b.tier ?? 1) === tc.targetTier).sort((a, b) => a.blockId - b.blockId);
9028
8313
  if (candidates.length >= 2) {
9029
8314
  const firstId = candidates[0].blockId;
9030
8315
  const lastId = candidates[candidates.length - 1].blockId;
@@ -9072,9 +8357,7 @@ ${ITERATION_CANDIDATE_GUIDANCE}` : ITERATION_CANDIDATE_GUIDANCE
9072
8357
  const fmt = (n) => n >= 1e3 ? `${(n / 1e3).toFixed(1)}K` : String(n);
9073
8358
  const sourceTier = tc.triggerTier === 2 ? "Tier 1" : "Tier 2";
9074
8359
  const action = tc.triggerTier === 2 ? "Distill" : "Condense";
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");
8360
+ const blockList = candidates.slice(0, 10).map((b) => `b${b.blockId} (age=${b.survivedCount}, ${fmt(b.summaryTokens)}tok): "${b.topic}"`).join("\n");
9078
8361
  const extraCount = candidates.length > 10 ? `
9079
8362
  ...and ${candidates.length - 10} more` : "";
9080
8363
  const tierText = `
@@ -9115,19 +8398,7 @@ ${rules}`;
9115
8398
  usagePct,
9116
8399
  growthSinceBaseline,
9117
8400
  growthFloor,
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
8401
+ recommendedRanges: recommendedRanges.length
9131
8402
  });
9132
8403
  } else if (shouldInjectNotice) {
9133
8404
  logger.info("Emergency /compact notice injected (no compressible targets)", {
@@ -9138,7 +8409,7 @@ ${rules}`;
9138
8409
  } else if (nudgeAllowed && nothingToCompress) {
9139
8410
  logger.info("Nudge suppressed: nothing to compress", {
9140
8411
  session: state.sessionId,
9141
- reason: allProtected ? "all_protected" : allInProtectedZone ? "in_protected_zone" : allBelowMin ? "below_effective_floor" : "no_executable_candidates",
8412
+ reason: allProtected ? "all_protected" : allInProtectedZone ? "in_protected_zone" : "below_effective_floor",
9142
8413
  currentTokens,
9143
8414
  usagePct,
9144
8415
  compressibleRanges: contextRanges.compressible.length
@@ -9150,7 +8421,7 @@ ${rules}`;
9150
8421
  const lines = [
9151
8422
  `[ACP Debug] Recommendation filter:`,
9152
8423
  ` Input: ${compressible.length} range(s), ${fmt(compressible.reduce((s, r) => s + r.tokens, 0))} tokens`,
9153
- candidatesEnabled ? ` Output: ${candidatePlan.candidates.length} candidate(s) (${candidatePlan.truncatedCount} truncated)` : ` Output: ${recommendedRanges.length} range(s) (last segment marked dangerous)`
8424
+ ` Output: ${recommendedRanges.length} range(s) (last segment marked dangerous)`
9154
8425
  ];
9155
8426
  logger.debug(lines.join("\n"));
9156
8427
  }
@@ -9162,7 +8433,10 @@ ${rules}`;
9162
8433
  const growth = currentTokens !== void 0 && (state.nudges.lastNudgeShownTokens ?? state.nudges.lastPerMessageNudgeTokens) !== void 0 ? currentTokens - (state.nudges.lastNudgeShownTokens ?? state.nudges.lastPerMessageNudgeTokens) : 0;
9163
8434
  const growthStr = growth > 0 ? ` (+${fmt(growth)} since last nudge)` : "";
9164
8435
  const plainTextTokens = composition.textTokens;
9165
- const efficiencyNote = effectiveTipsVariant !== "maxLimit" ? candidatesEnabled ? "\nThis is an efficiency nudge to compress early when content is no longer needed and keep context lean \u2014 not an overflow warning. A separate, stronger alert will appear if the context is actually full.\n\n" + COMPRESS_PHILOSOPHY : "\nThis is an efficiency nudge to compress early and keep context lean \u2014 not an overflow warning. A separate, stronger alert will appear if the context is actually full.\n\n" + COMPRESS_PHILOSOPHY : "";
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}` : "";
9166
8440
  const sysPart = composition.systemTokens > 0 ? `${fmt(composition.systemTokens)} system (${pct2(composition.systemTokens)}%) | ` : "";
9167
8441
  let breakdown = `${efficiencyNote}
9168
8442
  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}`;
@@ -9171,20 +8445,21 @@ Breakdown: ${sysPart}${fmt(composition.toolTokens)} tool (${pct2(composition.too
9171
8445
  breakdown += `
9172
8446
  \u26A0\uFE0F ${fmt(composition.protectedTokens)} tokens are protected (environment-managed tools) \u2014 not compressible. Effective compressible: ~${fmt(compressibleTokens)}.`;
9173
8447
  }
9174
- if (candidateText) {
8448
+ if (recommendedRanges.length > 0) {
9175
8449
  breakdown += `
9176
8450
 
9177
8451
  ${HOW_TO_COMPRESS_RULES}
9178
8452
 
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: [{...}, {...}]`).";
8453
+ ${formatCompressibleRanges(recommendedRanges, contextRanges.protected)}`;
8454
+ breakdown += `
8455
+ \u{1F4A1} Compress all ranges in one call (pass multiple content entries: \`content: [{...}, {...}]\`).`;
9181
8456
  }
9182
8457
  breakdown += `
9183
8458
  Use \`acp_status({scope:"uncompressed"})\` to re-fetch compressible ranges after compressing, or \`acp_status\` for compressed block details.`;
9184
8459
  appendToLastTextPart(suffixMessage, breakdown);
9185
8460
  }
9186
8461
  if (effectiveTipsVariant === "maxLimit" && !emergencyNoTargets) {
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.';
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.';
9188
8463
  } else if (shouldInjectNotice) {
9189
8464
  const emergencyPct = currentTokens !== void 0 && modelContextLimit !== void 0 && modelContextLimit > 0 ? Math.round(currentTokens / modelContextLimit * 100) : void 0;
9190
8465
  tipsText = `
@@ -9199,7 +8474,9 @@ Then stop retrying and await the user's response.`;
9199
8474
  }
9200
8475
  state.nudges.lastNudgeShownTokens = currentTokens;
9201
8476
  {
9202
- const visibleMessageIds = new Set(messages.map((message) => message.info.id));
8477
+ const visibleMessageIds = new Set(
8478
+ messages.map((message) => message.info.id)
8479
+ );
9203
8480
  const blockGuidance = buildCompressedBlockGuidance(state, {
9204
8481
  currentTokens,
9205
8482
  modelContextLimit,
@@ -9983,14 +9260,14 @@ function hideConsumedCompressCalls(state, messages) {
9983
9260
  }
9984
9261
 
9985
9262
  // lib/compress/status.ts
9986
- var ACP_STATUS_TOOL_DESCRIPTION = `Show context status \u2014 overview includes compressible ranges (compression candidates when compress.candidates is enabled).
9263
+ var ACP_STATUS_TOOL_DESCRIPTION = `Show context status \u2014 overview includes compressible ranges by default.
9987
9264
 
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.
9265
+ No args: Overview with totals, compressed blocks, and compressible ranges.
9266
+ scope:"uncompressed": Compressible ranges only (default view:"ranges"). Add view:"messages" for per-message listing with tool/sort filters.
9990
9267
  scope:"compressed": Drill into compressed blocks \u2014 list each with full details (age, generation, consumed lineage).
9991
9268
 
9992
9269
  Use this tool to:
9993
- - See what's consuming context + compressible targets in one call (no args)
9270
+ - See what's consuming context + compressible ranges in one call (no args)
9994
9271
  - Focus on ranges only (scope:"uncompressed")
9995
9272
  - Find all messages of a specific tool type (scope:"uncompressed", view:"messages", tool:"bash")
9996
9273
  - Check block details before decompressing (scope:"compressed")`;
@@ -10154,39 +9431,31 @@ function renderOverview(visibleMessages, summaryTokens, systemTokens, blocks, fe
10154
9431
  }
10155
9432
  }
10156
9433
  if (!fetchFailed) {
10157
- if (ctx.config?.compress?.candidates === true) {
10158
- const candidates = renderCompressionCandidates(rawMessages, ctx);
10159
- if (candidates.length > 0) {
10160
- lines.push("");
10161
- lines.push(...candidates);
10162
- }
10163
- } else {
10164
- const pruneMap = ctx.state.prune.messages.byMessageId;
10165
- const visibleRaw = rawMessages.filter((msg) => {
10166
- const msgId = msg.info?.id || "";
10167
- const entry = pruneMap.get(msgId);
10168
- return !entry || entry.activeBlockIds.length === 0;
10169
- });
10170
- const protectedRefs = ctx.config?.compress ? computeProtectedRefs(visibleRaw, ctx.state, ctx.config.compress) : /* @__PURE__ */ new Set();
10171
- const contextRanges = buildCompressibleRanges(
10172
- visibleRaw,
10173
- ctx.state,
10174
- ctx.config?.compress?.protectedTools ?? [],
10175
- ctx.config?.protectedFilePatterns ?? [],
10176
- protectedRefs
9434
+ const pruneMap = ctx.state.prune.messages.byMessageId;
9435
+ const visibleRaw = rawMessages.filter((msg) => {
9436
+ const msgId = msg.info?.id || "";
9437
+ const entry = pruneMap.get(msgId);
9438
+ return !entry || entry.activeBlockIds.length === 0;
9439
+ });
9440
+ const protectedRefs = ctx.config?.compress ? computeProtectedRefs(visibleRaw, ctx.state, ctx.config.compress) : /* @__PURE__ */ new Set();
9441
+ const contextRanges = buildCompressibleRanges(
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) {
9449
+ lines.push("");
9450
+ lines.push(
9451
+ formatCompressibleRanges(contextRanges.compressible, contextRanges.protected)
10177
9452
  );
10178
- if (contextRanges.compressible.length > 0 || contextRanges.protected.length > 0) {
10179
- lines.push("");
10180
- lines.push(
10181
- formatCompressibleRanges(contextRanges.compressible, contextRanges.protected)
10182
- );
10183
- }
10184
9453
  }
10185
9454
  }
10186
9455
  lines.push("");
10187
9456
  const hintTool = topToolName || "bash";
10188
9457
  lines.push(
10189
- ctx.config?.compress?.candidates === true ? `Tip: acp_status({scope:"uncompressed", view:"ranges"}) for raw ranges, or acp_status({scope:"uncompressed", view:"messages", tool:"${hintTool}"}) for per-message listing` : `Tip: acp_status({scope:"uncompressed", view:"messages", tool:"${hintTool}"}) for per-message listing`
9458
+ `Tip: acp_status({scope:"uncompressed", view:"messages", tool:"${hintTool}"}) for per-message listing`
10190
9459
  );
10191
9460
  return lines;
10192
9461
  }
@@ -10223,44 +9492,6 @@ function renderUncompressedRanges(rawMessages, ctx) {
10223
9492
  lines.push(`Filter by tool: acp_status({scope:"uncompressed", view:"messages", tool:"bash"})`);
10224
9493
  return lines;
10225
9494
  }
10226
- function renderCompressionCandidates(rawMessages, ctx) {
10227
- const lines = ["COMPRESSION CANDIDATES"];
10228
- if (!ctx.config?.compress || ctx.config.compress.candidates !== true) {
10229
- return renderUncompressedRanges(rawMessages, ctx);
10230
- }
10231
- try {
10232
- const plan = planCompressionCandidates(rawMessages, ctx.state, ctx.config);
10233
- const visibleMessages = rawMessages.filter((message) => {
10234
- const entry = ctx.state.prune.messages.byMessageId.get(message.info.id);
10235
- return !entry || entry.activeBlockIds.length === 0;
10236
- });
10237
- const protectedRefs = computeProtectedRefs(visibleMessages, ctx.state, ctx.config.compress);
10238
- const contextRanges = buildCompressibleRanges(
10239
- visibleMessages,
10240
- ctx.state,
10241
- ctx.config.compress.protectedTools ?? [],
10242
- ctx.config.protectedFilePatterns ?? [],
10243
- protectedRefs
10244
- );
10245
- const protectedText = formatCompressibleRanges([], contextRanges.protected);
10246
- if (plan.candidates.length === 0) {
10247
- lines.push(" (no eligible candidates)");
10248
- if (protectedText.includes("PROTECTED")) lines.push("", protectedText);
10249
- return lines;
10250
- }
10251
- lines.length = 0;
10252
- lines.push(formatCompressionCandidates(plan));
10253
- if (protectedText.includes("PROTECTED")) lines.push("", protectedText);
10254
- if (plan.omitted.length > 0) {
10255
- lines.push(
10256
- ` ${plan.omitted.length} structurally unsafe or undersized candidate${plan.omitted.length === 1 ? "" : "s"} omitted`
10257
- );
10258
- }
10259
- return lines;
10260
- } catch {
10261
- return ["COMPRESSION CANDIDATES", " (candidate planning unavailable)"];
10262
- }
10263
- }
10264
9495
  function renderUncompressedDrilldown(visibleMessages, toolFilter, sort, limit) {
10265
9496
  const lines = [];
10266
9497
  let filtered = visibleMessages;
@@ -10363,12 +9594,14 @@ function renderCompressedDrilldown(blocks, sort, limit, blocksById) {
10363
9594
  }
10364
9595
  function buildStatusReport(renderCtx, rawMessages, options) {
10365
9596
  const scope = options?.scope;
10366
- const view = options?.view ?? "candidates";
9597
+ const view = options?.view ?? "ranges";
10367
9598
  const toolFilter = options?.tool;
10368
9599
  const sort = options?.sort ?? "size";
10369
9600
  const limit = options?.limit ?? 30;
10370
9601
  const msgState = renderCtx.state.prune.messages;
10371
- const allBlocks = Array.from(msgState.blocksById.values()).sort((a, b) => a.blockId - b.blockId);
9602
+ const allBlocks = Array.from(msgState.blocksById.values()).sort(
9603
+ (a, b) => a.blockId - b.blockId
9604
+ );
10372
9605
  const activeBlocks = allBlocks.filter((b) => b.active);
10373
9606
  const lines = [];
10374
9607
  if (scope === "compressed") {
@@ -10382,10 +9615,8 @@ function buildStatusReport(renderCtx, rawMessages, options) {
10382
9615
  if (scope === "uncompressed") {
10383
9616
  if (view === "messages") {
10384
9617
  lines.push(...renderUncompressedDrilldown(visibleMsgs, toolFilter, sort, limit));
10385
- } else if (view === "ranges") {
10386
- lines.push(...renderUncompressedRanges(rawMessages, renderCtx));
10387
9618
  } else {
10388
- lines.push(...renderCompressionCandidates(rawMessages, renderCtx));
9619
+ lines.push(...renderUncompressedRanges(rawMessages, renderCtx));
10389
9620
  }
10390
9621
  } else {
10391
9622
  lines.push(
@@ -10409,7 +9640,7 @@ function createAcpStatusTool(factoryCtx) {
10409
9640
  args: {
10410
9641
  scope: tool4.schema.string().optional().describe('Drill down: "compressed" or "uncompressed". No arg = overview of both.'),
10411
9642
  view: tool4.schema.string().optional().describe(
10412
- 'Display format for scope:"uncompressed": "candidates" (default when compress.candidates is enabled \u2014 otherwise "ranges"), "ranges" (raw grouped ranges), or "messages" (per-message listing with sort/filter)'
9643
+ 'Display format for scope:"uncompressed": "ranges" (default, grouped by turn \u2014 matches nudge format) or "messages" (per-message listing with sort/filter)'
10413
9644
  ),
10414
9645
  tool: tool4.schema.string().optional().describe(
10415
9646
  'Filter by tool type (only with scope:"uncompressed", view:"messages"). e.g., "bash", "todowrite", "write"'
@@ -10420,16 +9651,16 @@ function createAcpStatusTool(factoryCtx) {
10420
9651
  async execute(args, toolCtx) {
10421
9652
  const ctx = resolveToolContext(factoryCtx, toolCtx.sessionID);
10422
9653
  const scope = args.scope === "compressed" || args.scope === "uncompressed" ? args.scope : void 0;
10423
- const view = args.view === "messages" || args.view === "ranges" || args.view === "candidates" ? args.view : "candidates";
9654
+ const view = args.view === "messages" ? "messages" : "ranges";
10424
9655
  const toolFilter = typeof args.tool === "string" ? args.tool : void 0;
10425
9656
  const sort = args.sort === "time" || args.sort === "tool" || args.sort === "age" ? args.sort : "size";
10426
9657
  const limit = Number.isFinite(args.limit) && args.limit > 0 ? Math.min(args.limit, 200) : 30;
10427
9658
  if (scope === "compressed") {
10428
- return buildStatusReport({ state: ctx.state, config: ctx.config }, [], {
10429
- scope: "compressed",
10430
- sort,
10431
- limit
10432
- });
9659
+ return buildStatusReport(
9660
+ { state: ctx.state, config: ctx.config },
9661
+ [],
9662
+ { scope: "compressed", sort, limit }
9663
+ );
10433
9664
  }
10434
9665
  let rawMessages = [];
10435
9666
  try {
@@ -10439,13 +9670,11 @@ function createAcpStatusTool(factoryCtx) {
10439
9670
  rawMessages = [];
10440
9671
  }
10441
9672
  hideConsumedCompressCalls(ctx.state, rawMessages);
10442
- return buildStatusReport({ state: ctx.state, config: ctx.config }, rawMessages, {
10443
- scope,
10444
- view,
10445
- tool: toolFilter,
10446
- sort,
10447
- limit
10448
- });
9673
+ return buildStatusReport(
9674
+ { state: ctx.state, config: ctx.config },
9675
+ rawMessages,
9676
+ { scope, view, tool: toolFilter, sort, limit }
9677
+ );
10449
9678
  }
10450
9679
  });
10451
9680
  }
@@ -10553,7 +9782,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
10553
9782
  import { join as join4 } from "path";
10554
9783
  import { existsSync as existsSync4 } from "fs";
10555
9784
  import { homedir as homedir3 } from "os";
10556
- var LOG_VERSION = true ? "1.17.1-pr.393.150" : "dev";
9785
+ var LOG_VERSION = true ? "1.17.1" : "dev";
10557
9786
  var LEVEL_RANK = {
10558
9787
  debug: 10,
10559
9788
  info: 20,
@@ -10776,32 +10005,7 @@ import { join as join5, dirname as dirname2 } from "path";
10776
10005
  import { homedir as homedir4 } from "os";
10777
10006
 
10778
10007
  // lib/prompts/system.ts
10779
- var ACP_STATUS_TOOL_LINE_RANGES = `- \`acp_status\` \u2014 Context status with compressible ranges. No args = overview + ranges. \`scope:"uncompressed"\` for range view; add \`view:"messages"\` for per-message listing with \`tool\`/\`sort\` filters. \`scope:"compressed"\` for block details.`;
10780
- var ACP_STATUS_TOOL_LINE_CANDIDATES = `- \`acp_status\` \u2014 Context status with compression candidates. No args = overview + candidates. \`scope:"uncompressed"\` defaults to independent candidates; use \`view:"ranges"\` for raw grouped ranges or \`view:"messages"\` for per-message listing with \`tool\`/\`sort\` filters. \`scope:"compressed"\` shows block details.`;
10781
- var PHILOSOPHY_TAIL_RANGES = `All ranges listed in the context breakdown should be compressed to summary format \u2014 the only exceptions are protected content, content the current step is actively using, or critical content you cannot reconstruct.`;
10782
- var PHILOSOPHY_TAIL_CANDIDATES = `Candidate guidance is advisory: a candidate is structurally safe to submit, not a command to compress. Preserve current intent and active work even when a candidate is displayed.`;
10783
- var COMPRESSION_CANDIDATES_SECTION = `COMPRESSION CANDIDATES
10784
-
10785
- ACP may append a bounded list headed \`COMPRESSION CANDIDATES\` below a nudge or status report:
10786
-
10787
- - \`MICRO\` is one large plain message or one complete tool transaction, including every message needed to keep its tool call/result structure valid.
10788
- - \`EPISODE\` is a contiguous historical segment made from smaller adjacent units.
10789
- - Candidate ranges are independent, non-overlapping, and can be submitted together as separate \`content[]\` entries.
10790
- - Choose candidates only when their content is no longer needed for the current task. Do not invent a target when no candidate is listed; call \`acp_status\` for a fresh view. If an arbitrary range is unavoidable, verify current visible IDs, protection boundaries, and complete tool-pair coverage first.
10791
- - Keep summaries self-contained and use the existing \`compress\` tool. Candidate labels do not replace the semantic judgment to preserve useful context.
10792
- - When a nudge lists a clearly stale candidate, call \`compress\` in that reply before continuing. A repeated nudge means compression was not completed; act on one clearly stale candidate rather than merely recommending compression.`;
10793
- var BREAKDOWN_TAIL_RANGES = `Below the breakdown, the system lists compressible ranges grouped by conversation turn. All listed ranges should be compressed to summary format \u2014 the only exceptions are protected content, content the current step is actively using, or critical content you cannot reconstruct. Compress the largest ranges first when the current step no longer needs them.`;
10794
- var BREAKDOWN_TAIL_CANDIDATES = `Below the breakdown, the system may list independent compression candidates. Prefer a large stale micro-range when it removes an isolated artifact; use an episode range when a completed historical phase is more coherent as one summary. Compress only content the current step no longer needs.`;
10795
- function buildSystemPrompt(candidatesEnabled) {
10796
- const acpStatusToolLine = candidatesEnabled ? ACP_STATUS_TOOL_LINE_CANDIDATES : ACP_STATUS_TOOL_LINE_RANGES;
10797
- const philosophyTail = candidatesEnabled ? PHILOSOPHY_TAIL_CANDIDATES : PHILOSOPHY_TAIL_RANGES;
10798
- const compressionCandidatesBlock = candidatesEnabled ? `
10799
-
10800
- ${COMPRESSION_CANDIDATES_SECTION}
10801
-
10802
- ` : "\n\n";
10803
- const breakdownTail = candidatesEnabled ? BREAKDOWN_TAIL_CANDIDATES : BREAKDOWN_TAIL_RANGES;
10804
- return `
10008
+ var SYSTEM = `
10805
10009
 
10806
10010
  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.
10807
10011
 
@@ -10827,7 +10031,7 @@ You have five context-management tools:
10827
10031
  - \`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: "..." }] })\`.
10828
10032
  - \`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 })\`.
10829
10033
  - \`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" })\`.
10830
- ${acpStatusToolLine}
10034
+ - \`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.
10831
10035
 
10832
10036
  COMPRESSION PHILOSOPHY
10833
10037
 
@@ -10835,7 +10039,7 @@ Two failure modes to avoid:
10835
10039
  - Over-compression: Compressing too aggressively loses critical details, decisions, and state needed for your task. This directly harms task quality.
10836
10040
  - Under-compression: Failing to compress verbose outputs causes context overflow, reducing accuracy and eventually blocking your work.
10837
10041
 
10838
- Balance is key. The single test for whether to compress is: "Is this content still needed by the current task step?" If yes, keep it. If no, compress it. ${philosophyTail}
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. 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.
10839
10043
 
10840
10044
  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.
10841
10045
 
@@ -10856,7 +10060,9 @@ WHEN NOT TO COMPRESS
10856
10060
  - Important user messages \u2014 preserve their exact intent, constraints, and acceptance criteria verbatim, not just the most recent one.
10857
10061
  - Protected tool outputs (default: \`skill\` only) \u2014 hard-excluded from compression ranges, survive intact in visible context.
10858
10062
 
10859
- ${HOW_TO_COMPRESS_RULES}${compressionCandidatesBlock}MULTI-TIER COMPRESSION
10063
+ ${HOW_TO_COMPRESS_RULES}
10064
+
10065
+ MULTI-TIER COMPRESSION
10860
10066
 
10861
10067
  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.
10862
10068
 
@@ -10881,21 +10087,13 @@ Breakdown: 4.2K system (21%) | 8.0K tool (40%) | 2.0K summaries (10%) | 2.6K cod
10881
10087
  - "text" = plain text messages
10882
10088
  - "reasoning" = model thinking blocks (counted to match real API usage; freed when their message is compressed)
10883
10089
 
10884
- ${breakdownTail}
10090
+ 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.
10885
10091
 
10886
10092
  Each compression creates a reusable summary block you can decompress later if needed.
10887
10093
  `;
10888
- }
10889
- var SYSTEM = buildSystemPrompt(true);
10890
10094
 
10891
10095
  // lib/prompts/compress-range.ts
10892
- var CANDIDATE_GUIDANCE_SECTION = `CANDIDATE GUIDANCE
10893
- ACP may display a \`COMPRESSION CANDIDATES\` list in a nudge or \`acp_status\` report. A \`MICRO\` entry targets one large message or complete tool transaction; an \`EPISODE\` entry targets a contiguous historical segment. Displayed candidates are independent and non-overlapping, so they may be batched, but they are advisory: compress only entries whose content is no longer needed. When a nudge lists a clearly stale candidate, use its exact IDs and call \`compress\` before continuing. Keep current intent and active work visible, and use \`acp_status\` when the candidate list is stale.`;
10894
- function buildCompressRangePrompt(candidatesEnabled) {
10895
- const candidateGuidanceBlock = candidatesEnabled ? `${CANDIDATE_GUIDANCE_SECTION}
10896
-
10897
- ` : "";
10898
- return `Collapse a range in the conversation into a detailed summary.
10096
+ var COMPRESS_RANGE = `Collapse a range in the conversation into a detailed summary.
10899
10097
 
10900
10098
  COMPRESSED BLOCK PLACEHOLDERS
10901
10099
  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.
@@ -10925,7 +10123,7 @@ Rules:
10925
10123
 
10926
10124
  - Pick \`startId\` and \`endId\` directly from injected IDs in context.
10927
10125
  - IDs must exist in the current visible context. If you cannot see an ID in the messages above, it is stale and will fail.
10928
- - Prefer \`startId\` before \`endId\` in conversation order. ACP can normalize reversed boundaries, but do not rely on that behavior.
10126
+ - \`startId\` must appear before \`endId\`.
10929
10127
  - Do not invent IDs. Use only IDs that are present in context.
10930
10128
  - 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.
10931
10129
 
@@ -10942,7 +10140,7 @@ compress({ content: [
10942
10140
  ]})
10943
10141
  \`\`\`
10944
10142
 
10945
- ${candidateGuidanceBlock}KEEP AND REF MARKERS
10143
+ KEEP AND REF MARKERS
10946
10144
  When writing a summary, you may embed markers that reference specific messages in the compressed range. The system resolves them automatically:
10947
10145
 
10948
10146
  - \`[[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.
@@ -10959,8 +10157,65 @@ The rest of the bash calls were repetitive export commands. See [[REF:m00078|tes
10959
10157
 
10960
10158
  Use KEEP sparingly \u2014 each expansion adds to the summary length. Prefer REF for content that is important but not immediately critical.
10961
10159
  `;
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
+ }
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": "..." }]
10962
10214
  }
10963
- var COMPRESS_RANGE = buildCompressRangePrompt(true);
10215
+
10216
+ \u26A0\uFE0F ONLY use IDs from tags visible above. Do NOT invent or copy example IDs.
10217
+ </system-reminder>
10218
+ `;
10964
10219
 
10965
10220
  // lib/prompts/extensions/system.ts
10966
10221
  var SUBAGENT_SYSTEM_EXTENSION = `<dcp-system-reminder>
@@ -11052,27 +10307,24 @@ var HTML_COMMENT_REGEX = /<!--[\s\S]*?-->/g;
11052
10307
  var LEGACY_INLINE_COMMENT_LINE_REGEX = /^[ \t]*\/\/.*?\/\/[ \t]*$/gm;
11053
10308
  var DCP_SYSTEM_REMINDER_TAG_REGEX = /^\s*<dcp-system-reminder\b[^>]*>[\s\S]*<\/(?:dcp|acp)-system-reminder>\s*$/i;
11054
10309
  var DEFAULTS_README_FILE = "README.md";
11055
- function getBundledEditablePrompts(candidatesEnabled) {
11056
- return {
11057
- system: buildSystemPrompt(candidatesEnabled),
11058
- compressRange: buildCompressRangePrompt(candidatesEnabled),
11059
- contextLimitNudge: CONTEXT_LIMIT_NUDGE,
11060
- turnNudge: TURN_NUDGE,
11061
- iterationNudge: ITERATION_NUDGE
11062
- };
11063
- }
10310
+ var BUNDLED_EDITABLE_PROMPTS = {
10311
+ system: SYSTEM,
10312
+ compressRange: COMPRESS_RANGE,
10313
+ contextLimitNudge: CONTEXT_LIMIT_NUDGE,
10314
+ turnNudge: TURN_NUDGE,
10315
+ iterationNudge: ITERATION_NUDGE
10316
+ };
11064
10317
  var INTERNAL_PROMPT_EXTENSIONS = {
11065
10318
  subagentExtension: SUBAGENT_SYSTEM_EXTENSION,
11066
10319
  decompressExtension: DECOMPRESS_SYSTEM_EXTENSION
11067
10320
  };
11068
- function createBundledRuntimePrompts(candidatesEnabled) {
11069
- const bundled = getBundledEditablePrompts(candidatesEnabled);
10321
+ function createBundledRuntimePrompts() {
11070
10322
  return {
11071
- system: bundled.system,
11072
- compressRange: bundled.compressRange,
11073
- contextLimitNudge: bundled.contextLimitNudge,
11074
- turnNudge: bundled.turnNudge,
11075
- iterationNudge: bundled.iterationNudge,
10323
+ system: BUNDLED_EDITABLE_PROMPTS.system,
10324
+ compressRange: BUNDLED_EDITABLE_PROMPTS.compressRange,
10325
+ contextLimitNudge: BUNDLED_EDITABLE_PROMPTS.contextLimitNudge,
10326
+ turnNudge: BUNDLED_EDITABLE_PROMPTS.turnNudge,
10327
+ iterationNudge: BUNDLED_EDITABLE_PROMPTS.iterationNudge,
11076
10328
  subagentExtension: INTERNAL_PROMPT_EXTENSIONS.subagentExtension,
11077
10329
  decompressExtension: INTERNAL_PROMPT_EXTENSIONS.decompressExtension
11078
10330
  };
@@ -11217,14 +10469,12 @@ var PromptStore = class {
11217
10469
  logger;
11218
10470
  paths;
11219
10471
  customPromptsEnabled;
11220
- candidatesEnabled;
11221
10472
  runtimePrompts;
11222
- constructor(logger, workingDirectory, customPromptsEnabled = false, candidatesEnabled = false) {
10473
+ constructor(logger, workingDirectory, customPromptsEnabled = false) {
11223
10474
  this.logger = logger;
11224
10475
  this.paths = resolvePromptPaths(workingDirectory);
11225
10476
  this.customPromptsEnabled = customPromptsEnabled;
11226
- this.candidatesEnabled = candidatesEnabled;
11227
- this.runtimePrompts = createBundledRuntimePrompts(this.candidatesEnabled);
10477
+ this.runtimePrompts = createBundledRuntimePrompts();
11228
10478
  if (this.customPromptsEnabled) {
11229
10479
  this.ensureDefaultFiles();
11230
10480
  }
@@ -11234,13 +10484,13 @@ var PromptStore = class {
11234
10484
  return { ...this.runtimePrompts };
11235
10485
  }
11236
10486
  reload() {
11237
- const nextPrompts = createBundledRuntimePrompts(this.candidatesEnabled);
10487
+ const nextPrompts = createBundledRuntimePrompts();
11238
10488
  if (!this.customPromptsEnabled) {
11239
10489
  this.runtimePrompts = nextPrompts;
11240
10490
  return;
11241
10491
  }
11242
10492
  for (const definition of PROMPT_DEFINITIONS) {
11243
- const bundledSource = getBundledEditablePrompts(this.candidatesEnabled)[definition.runtimeField];
10493
+ const bundledSource = BUNDLED_EDITABLE_PROMPTS[definition.runtimeField];
11244
10494
  const bundledEditable = toEditablePromptText(definition, bundledSource);
11245
10495
  const bundledRuntime = wrapRuntimePromptContent(definition, bundledEditable);
11246
10496
  const fallbackValue = bundledRuntime || bundledSource.trim();
@@ -11301,14 +10551,13 @@ var PromptStore = class {
11301
10551
  });
11302
10552
  return;
11303
10553
  }
11304
- const bundledPrompts = getBundledEditablePrompts(this.candidatesEnabled);
11305
10554
  for (const definition of PROMPT_DEFINITIONS) {
11306
10555
  const bundledEditable = toEditablePromptText(
11307
10556
  definition,
11308
- bundledPrompts[definition.runtimeField]
10557
+ BUNDLED_EDITABLE_PROMPTS[definition.runtimeField]
11309
10558
  );
11310
10559
  const managedContent = buildDefaultPromptFileContent(
11311
- bundledEditable || bundledPrompts[definition.runtimeField]
10560
+ bundledEditable || BUNDLED_EDITABLE_PROMPTS[definition.runtimeField]
11312
10561
  );
11313
10562
  const filePath = join5(this.paths.defaultsDir, definition.fileName);
11314
10563
  try {
@@ -12753,8 +12002,8 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
12753
12002
  cacheSystemPromptTokens(state, output.messages);
12754
12003
  assignMessageRefs(state, output.messages);
12755
12004
  const activeBlockCountBefore = state.prune.messages.activeBlockIds.size;
12756
- const compressionStateChanged = syncCompressionBlocks(state, logger, output.messages);
12757
- if (compressionStateChanged || state.prune.messages.activeBlockIds.size !== activeBlockCountBefore) {
12005
+ syncCompressionBlocks(state, logger, output.messages);
12006
+ if (state.prune.messages.activeBlockIds.size !== activeBlockCountBefore) {
12758
12007
  saveSessionState(state, logger).catch(() => {
12759
12008
  });
12760
12009
  }
@@ -12766,8 +12015,9 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
12766
12015
  });
12767
12016
  }
12768
12017
  const prePruneTokens = getCurrentTokenUsage(state, output.messages);
12769
- const candidateMessages = config.compress.candidates === true ? output.messages.slice() : void 0;
12770
12018
  prune(state, logger, config, output.messages);
12019
+ truncateLargeToolOutputs(state, config, logger, output.messages);
12020
+ enforceContextBudget(state, config, logger, output.messages);
12771
12021
  hideConsumedCompressCalls(state, output.messages);
12772
12022
  assignMessageRefs(state, output.messages);
12773
12023
  const compressionPriorities = buildPriorityMap(config, state, output.messages);
@@ -12792,16 +12042,8 @@ ${text}`);
12792
12042
  }).catch(() => {
12793
12043
  });
12794
12044
  } : void 0,
12795
- prePruneTokens,
12796
- candidateMessages
12045
+ prePruneTokens
12797
12046
  );
12798
- truncateLargeToolOutputs(
12799
- state,
12800
- config,
12801
- logger,
12802
- output.messages.filter((message) => !isSyntheticMessage(message))
12803
- );
12804
- enforceContextBudget(state, config, logger, output.messages);
12805
12047
  injectMessageIds(state, config, output.messages, compressionPriorities);
12806
12048
  hideFailedCompressCalls(output.messages);
12807
12049
  stripStaleMetadata(output.messages);
@@ -13236,7 +12478,7 @@ var server = (async (ctx) => {
13236
12478
  }
13237
12479
  const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
13238
12480
  logger.info("ACP plugin initialized", {
13239
- version: true ? "1.17.1-pr.393.150" : "dev",
12481
+ version: true ? "1.17.1" : "dev",
13240
12482
  workspace: ctx.directory,
13241
12483
  logLevel: logger.level,
13242
12484
  debug: config.debug,
@@ -13244,12 +12486,7 @@ var server = (async (ctx) => {
13244
12486
  secureMode: isSecureMode()
13245
12487
  });
13246
12488
  const registry4 = new SessionStateRegistry(logger, ctx.directory);
13247
- const prompts = new PromptStore(
13248
- logger,
13249
- ctx.directory,
13250
- config.experimental.customPrompts,
13251
- config.compress.candidates === true
13252
- );
12489
+ const prompts = new PromptStore(logger, ctx.directory, config.experimental.customPrompts);
13253
12490
  const hostPermissions = {
13254
12491
  global: void 0,
13255
12492
  agents: {}