opencode-acp 1.18.1-pr.433.184 → 1.18.1-pr.439.185

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6822,6 +6822,33 @@ function buildToolIdList(state, messages) {
6822
6822
  var stripHallucinationsFromString = (text) => {
6823
6823
  return text.replace(DCP_PAIRED_TAG_REGEX, "").replace(DCP_UNPAIRED_TAG_REGEX, "");
6824
6824
  };
6825
+ var LEAKED_TRAILING_REF_REGEX = /(?:^|\n)[ \t]*(?:m(\d{4,5})|b([1-9]\d*))\b/g;
6826
+ var stripLeakedTrailingRefs = (text, bounds) => {
6827
+ if (typeof text !== "string" || text.length === 0) {
6828
+ return text;
6829
+ }
6830
+ const rawMessageBound = bounds.nextMessageRef;
6831
+ const rawBlockBound = bounds.nextBlockRef;
6832
+ const messageBound = typeof rawMessageBound === "number" && Number.isInteger(rawMessageBound) ? rawMessageBound : null;
6833
+ const blockBound = typeof rawBlockBound === "number" && Number.isInteger(rawBlockBound) ? rawBlockBound : null;
6834
+ if (messageBound === null && blockBound === null) {
6835
+ return text;
6836
+ }
6837
+ const pattern = new RegExp(LEAKED_TRAILING_REF_REGEX.source, "g");
6838
+ let match;
6839
+ while ((match = pattern.exec(text)) !== null) {
6840
+ if (match[1] !== void 0) {
6841
+ if (messageBound !== null && Number(match[1]) >= messageBound) {
6842
+ return text.slice(0, match.index).replace(/[ \t\r\n]*$/, "");
6843
+ }
6844
+ } else if (match[2] !== void 0) {
6845
+ if (blockBound !== null && Number(match[2]) >= blockBound) {
6846
+ return text.slice(0, match.index).replace(/[ \t\r\n]*$/, "");
6847
+ }
6848
+ }
6849
+ }
6850
+ return text;
6851
+ };
6825
6852
  var stripHallucinations = (messages) => {
6826
6853
  for (const message of messages) {
6827
6854
  for (const part of message.parts) {
@@ -8182,7 +8209,7 @@ COMPRESSION CANDIDATES:
8182
8209
  - EPISODE identifies a contiguous historical segment made from smaller units.
8183
8210
  - Candidate entries are independent and non-overlapping, so they can be batched in one \`content[]\` array.
8184
8211
  - 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.
8185
- - Use \`acp_status\` for a fresh candidate view if the list is stale or missing.
8212
+ - Listed candidates were validated against the current conversation when this nudge was built. Compress them directly; do not call \`acp_status\` before compressing. If a \`compress\` call fails on a stale ID, run \`acp_status\` once and re-issue using only the refs it reports.
8186
8213
  - 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.
8187
8214
  </system-reminder>
8188
8215
  `;
@@ -10544,7 +10571,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
10544
10571
  import { join as join4 } from "path";
10545
10572
  import { existsSync as existsSync4 } from "fs";
10546
10573
  import { homedir as homedir3 } from "os";
10547
- var LOG_VERSION = true ? "1.18.1-pr.433.184" : "dev";
10574
+ var LOG_VERSION = true ? "1.18.1-pr.439.185" : "dev";
10548
10575
  var LEVEL_RANK = {
10549
10576
  debug: 10,
10550
10577
  info: 20,
@@ -10778,7 +10805,7 @@ ACP may append a bounded list headed \`COMPRESSION CANDIDATES\` below a nudge or
10778
10805
  - \`MICRO\` is one large plain message or one complete tool transaction, including every message needed to keep its tool call/result structure valid.
10779
10806
  - \`EPISODE\` is a contiguous historical segment made from smaller adjacent units.
10780
10807
  - Candidate ranges are independent, non-overlapping, and can be submitted together as separate \`content[]\` entries.
10781
- - 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.
10808
+ - Listed candidates were validated against the current conversation when the nudge or status report was built. Submit them directly with \`compress\`; do not call \`acp_status\` first. Choose candidates only when their content is no longer needed for the current task. Do not invent a target when no candidate is listed. If an arbitrary range is unavoidable, verify current visible IDs, protection boundaries, and complete tool-pair coverage first.
10782
10809
  - Keep summaries self-contained and use the existing \`compress\` tool. Candidate labels do not replace the semantic judgment to preserve useful context.
10783
10810
  - 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.`;
10784
10811
  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.`;
@@ -10810,6 +10837,7 @@ When you see past \`compress\` tool calls in the conversation, their \`summary\`
10810
10837
  - Do NOT echo, repeat, or continue summary content as your own output. Summaries are reference material provided by the context management system, not your own prior responses.
10811
10838
  - Summaries may contain errors or simplifications. Use \`decompress\` to verify critical details before acting on them.
10812
10839
  - The \`startId\`/\`endId\` in past compress calls are historical \u2014 do NOT reuse them as targets for new compress calls without verifying via \`acp_status\` that the range is still uncompressed.
10840
+ - If a \`compress\` call fails because a ref is stale or unknown, do NOT adjust ranges by arithmetic: run \`acp_status\`, then re-issue the \`compress\` in the same turn using only the refs it reports. Submit all target ranges in one batch call.
10813
10841
 
10814
10842
  TOOLS
10815
10843
 
@@ -10857,8 +10885,6 @@ Summaries accumulate as the session grows. When tier-1 summaries pile up, the sy
10857
10885
 
10858
10886
  To compress blocks: use block IDs as boundaries: \`compress({ content: [{ startId: "b3", endId: "b15", summary: "..." }] })\`. Multiple entries create separate blocks: \`compress({ content: [{ startId: "b3", endId: "b10", summary: "..." }, { startId: "b11", endId: "b20", summary: "..." }] })\`. This deactivates the consumed blocks and creates a new higher-tier block per entry. The system prompt at the trigger tells you which rules to follow.
10859
10887
 
10860
- If you are unsure which \`mNNNNN\` refs are still compressible, or which blocks have already consumed which ranges, call \`acp_status\` first. It returns the visible context breakdown and the compressed block list.
10861
-
10862
10888
  CONTEXT BREAKDOWN
10863
10889
 
10864
10890
  When context usage passes a threshold, the system appends a breakdown showing where your context tokens are spent:
@@ -10881,7 +10907,7 @@ var SYSTEM = buildSystemPrompt(true);
10881
10907
 
10882
10908
  // lib/prompts/compress-range.ts
10883
10909
  var CANDIDATE_GUIDANCE_SECTION = `CANDIDATE GUIDANCE
10884
- 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.`;
10910
+ 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. Do not call \`acp_status\` before compressing listed candidates; if a \`compress\` call fails on a stale ID, run \`acp_status\` and re-issue using only the refs it reports.`;
10885
10911
  function buildCompressRangePrompt(candidatesEnabled) {
10886
10912
  const candidateGuidanceBlock = candidatesEnabled ? `${CANDIDATE_GUIDANCE_SECTION}
10887
10913
 
@@ -12884,9 +12910,30 @@ function createCommandExecuteHandler(client, registry4, logger, config, workingD
12884
12910
  }
12885
12911
  };
12886
12912
  }
12887
- function createTextCompleteHandler() {
12888
- return async (_input, output) => {
12889
- output.text = stripHallucinationsFromString(output.text);
12913
+ function createTextCompleteHandler(registry4, logger) {
12914
+ return async (input, output) => {
12915
+ if (typeof output.text !== "string" || output.text.length === 0) {
12916
+ return;
12917
+ }
12918
+ let text = stripHallucinationsFromString(output.text);
12919
+ const state = typeof input.sessionID === "string" ? registry4.get(input.sessionID) : void 0;
12920
+ if (state) {
12921
+ const nextBlockId = state.prune?.messages?.nextBlockId;
12922
+ const stripped = stripLeakedTrailingRefs(text, {
12923
+ nextMessageRef: Number.isInteger(state.messageIds.nextRef) ? state.messageIds.nextRef : null,
12924
+ nextBlockRef: Number.isInteger(nextBlockId) ? nextBlockId : null
12925
+ });
12926
+ if (stripped !== text) {
12927
+ logger.warn("Stripped leaked trailing ACP ref from assistant output", {
12928
+ sessionID: input.sessionID,
12929
+ messageID: input.messageID,
12930
+ removedChars: text.length - stripped.length,
12931
+ removedTailPreview: text.slice(stripped.length, stripped.length + 200)
12932
+ });
12933
+ text = stripped;
12934
+ }
12935
+ }
12936
+ output.text = text;
12890
12937
  };
12891
12938
  }
12892
12939
  function createEventHandler(registry4, logger) {
@@ -13228,7 +13275,7 @@ var server = (async (ctx) => {
13228
13275
  }
13229
13276
  const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
13230
13277
  logger.info("ACP plugin initialized", {
13231
- version: true ? "1.18.1-pr.433.184" : "dev",
13278
+ version: true ? "1.18.1-pr.439.185" : "dev",
13232
13279
  workspace: ctx.directory,
13233
13280
  logLevel: logger.level,
13234
13281
  debug: config.debug,
@@ -13293,7 +13340,7 @@ var server = (async (ctx) => {
13293
13340
  hostPermissions
13294
13341
  )
13295
13342
  ),
13296
- "experimental.text.complete": guard(createTextCompleteHandler()),
13343
+ "experimental.text.complete": guard(createTextCompleteHandler(registry4, logger)),
13297
13344
  "command.execute.before": guard(
13298
13345
  createCommandExecuteHandler(
13299
13346
  ctx.client,