opencode-acp 1.18.2-pr.448.192 → 1.18.2-pr.449.193

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
@@ -6384,24 +6384,32 @@ function computeStats(plan) {
6384
6384
  const retentionPct = originalTokens > 0 ? (summaryChars / (originalTokens * 4) * 100).toFixed(2) : "?";
6385
6385
  return { originalTokens, summaryChars, ratio, retentionPct };
6386
6386
  }
6387
- function buildQualityRejectionError(plan, result) {
6387
+ function coreReason(reason) {
6388
+ if (!reason) return "unknown";
6389
+ const candidates = [reason.indexOf(":"), reason.indexOf("(")].filter((i) => i >= 0);
6390
+ const cut = Math.min(...candidates);
6391
+ return (cut > 0 ? reason.slice(0, cut) : reason).trim() || "unknown";
6392
+ }
6393
+ function buildQualityRejectionError(plan, result, logger) {
6388
6394
  const stats = computeStats(plan);
6389
- const metrics = [
6390
- `Reason: ${result.reason || "unknown"}`,
6391
- `Original: ~${stats.originalTokens} tokens`,
6392
- `Summary: ${stats.summaryChars} chars`,
6393
- `Ratio: ${stats.ratio}:1`,
6394
- `Retention: ${stats.retentionPct}%`,
6395
- `Gate layer: ${result.layer ?? "unknown"}`,
6396
- `rougeF1: ${formatMetric(result, "rougeF1")}`,
6397
- `top20Recall: ${formatMetric(result, "top20Recall")}`
6398
- ];
6395
+ logger?.warn("quality gate rejected compression", {
6396
+ range: `${plan.startId}\u2013${plan.endId}`,
6397
+ reason: result.reason || "unknown",
6398
+ layer: result.layer ?? "unknown",
6399
+ originalTokens: stats.originalTokens,
6400
+ summaryChars: stats.summaryChars,
6401
+ ratio: stats.ratio,
6402
+ retentionPct: stats.retentionPct,
6403
+ rougeF1: formatMetric(result, "rougeF1"),
6404
+ top20Recall: formatMetric(result, "top20Recall")
6405
+ });
6399
6406
  const message = `\u26A0\uFE0F COMPRESSION REJECTED \u2014 QUALITY GATE FAILURE
6400
6407
 
6401
6408
  Range: ${plan.startId}\u2013${plan.endId}
6402
- ${metrics.join("\n")}
6409
+ Reason: ${coreReason(result.reason)}
6410
+ Original: ~${stats.originalTokens} tokens \xB7 Summary: ${stats.summaryChars} chars \xB7 Retention: ${stats.retentionPct}%
6403
6411
 
6404
- Retry: rewrite a more complete summary that preserves critical details (file paths, decisions, exact values, errors) and call compress again on the same range \u2014 the gate re-evaluates automatically. Full compression rules are already in your system prompt. If you are confident the summary is correct despite the metrics, add "acknowledgeRisk": true to bypass the quality gate on your next compress call.`;
6412
+ Retry: write a more complete summary preserving critical details (file paths, decisions, exact values, errors), then call compress again on the same range. If you are confident the summary is adequate despite the metrics, pass "acknowledgeRisk": true.`;
6405
6413
  return new Error(message);
6406
6414
  }
6407
6415
 
@@ -8063,7 +8071,8 @@ function createCompressRangeTool(factoryCtx) {
8063
8071
  messageIds: plan.selection.messageIds,
8064
8072
  messageTokenById: plan.selection.messageTokenById
8065
8073
  },
8066
- result
8074
+ result,
8075
+ ctx.logger
8067
8076
  );
8068
8077
  }
8069
8078
  }
@@ -9823,6 +9832,81 @@ function computeRestoredMessages(messagesState, activeMessagesBefore) {
9823
9832
  }
9824
9833
  return { restoredMessageCount, restoredTokens };
9825
9834
  }
9835
+ function collectRequiredDecompressMessageIds(messagesState, targets, options = {}) {
9836
+ const targetBlockIds = /* @__PURE__ */ new Set();
9837
+ for (const target of targets) {
9838
+ for (const block of target.blocks) {
9839
+ targetBlockIds.add(block.blockId);
9840
+ }
9841
+ }
9842
+ if (targetBlockIds.size === 0) {
9843
+ return [];
9844
+ }
9845
+ const consumedClosure = /* @__PURE__ */ new Set();
9846
+ const queue = [];
9847
+ for (const target of targets) {
9848
+ for (const block of target.blocks) {
9849
+ for (const id of block.consumedBlockIds ?? []) {
9850
+ if (!consumedClosure.has(id)) {
9851
+ queue.push(id);
9852
+ }
9853
+ }
9854
+ }
9855
+ }
9856
+ while (queue.length > 0) {
9857
+ const id = queue.shift();
9858
+ if (consumedClosure.has(id)) continue;
9859
+ consumedClosure.add(id);
9860
+ const consumed = messagesState.blocksById.get(id);
9861
+ if (consumed) {
9862
+ for (const nested of consumed.consumedBlockIds ?? []) {
9863
+ if (!consumedClosure.has(nested)) {
9864
+ queue.push(nested);
9865
+ }
9866
+ }
9867
+ }
9868
+ }
9869
+ let shieldedMessageIds = null;
9870
+ if (!options.full) {
9871
+ shieldedMessageIds = /* @__PURE__ */ new Set();
9872
+ for (const id of consumedClosure) {
9873
+ const block = messagesState.blocksById.get(id);
9874
+ if (!block || block.deactivatedByUser || block.deactivatedByUserDeep) continue;
9875
+ for (const messageId of block.effectiveMessageIds ?? []) {
9876
+ shieldedMessageIds.add(messageId);
9877
+ }
9878
+ }
9879
+ }
9880
+ const required = [];
9881
+ for (const [messageId, entry] of messagesState.byMessageId) {
9882
+ if (entry.activeBlockIds.length === 0) continue;
9883
+ let exclusivelyCoveredByTargets = true;
9884
+ for (const blockId of entry.activeBlockIds) {
9885
+ if (!targetBlockIds.has(blockId)) {
9886
+ exclusivelyCoveredByTargets = false;
9887
+ break;
9888
+ }
9889
+ }
9890
+ if (!exclusivelyCoveredByTargets) continue;
9891
+ if (shieldedMessageIds?.has(messageId)) continue;
9892
+ required.push(messageId);
9893
+ }
9894
+ required.sort();
9895
+ return required;
9896
+ }
9897
+ function checkDecompressSourceAvailability(messagesState, targets, options, presentInHistory) {
9898
+ const requiredMessageIds = collectRequiredDecompressMessageIds(messagesState, targets, options);
9899
+ const available = [];
9900
+ const missing = [];
9901
+ for (const messageId of requiredMessageIds) {
9902
+ if (presentInHistory.has(messageId)) {
9903
+ available.push(messageId);
9904
+ } else {
9905
+ missing.push(messageId);
9906
+ }
9907
+ }
9908
+ return { requiredMessageIds, availableMessageIds: available, missingMessageIds: missing };
9909
+ }
9826
9910
  function computeReactivatedBlockIds(messagesState, activeBlockIdsBefore) {
9827
9911
  return Array.from(messagesState.activeBlockIds).filter((blockId) => !activeBlockIdsBefore.has(blockId)).sort((a, b) => a - b);
9828
9912
  }
@@ -10120,6 +10204,28 @@ function createDecompressTool(factoryCtx) {
10120
10204
  const displayIds2 = targets.map((t) => `b${t.displayId}`).join(", ");
10121
10205
  return `Block(s) ${displayIds2} content (${sourceNote}, ${fileContent.length} chars) written to ${targetPath}. Block(s) stay compressed \u2014 context unchanged. Use read tool to access specific parts.`;
10122
10206
  }
10207
+ const availability = checkDecompressSourceAvailability(
10208
+ messagesState,
10209
+ targets,
10210
+ { full: args.full === true },
10211
+ new Set(rawMessages.map((m) => m.info.id))
10212
+ );
10213
+ if (availability.missingMessageIds.length > 0) {
10214
+ const displayIds2 = targets.map((t) => `b${t.displayId}`).join(", ");
10215
+ const missingRefs = availability.missingMessageIds.map(
10216
+ (id) => ctx.state.messageIds.byRawId.get(id) ?? id
10217
+ );
10218
+ const shownRefs = missingRefs.slice(0, 10).join(", ");
10219
+ const extraCount = missingRefs.length - Math.min(missingRefs.length, 10);
10220
+ ctx.logger.warn("Decompress aborted: source messages missing from host history", {
10221
+ targetDisplayIds: displayIds2,
10222
+ full: args.full === true,
10223
+ requiredCount: availability.requiredMessageIds.length,
10224
+ missingCount: availability.missingMessageIds.length,
10225
+ missingRefs
10226
+ });
10227
+ return `Error: Cannot decompress ${displayIds2}: ${availability.missingMessageIds.length} of ${availability.requiredMessageIds.length} source message(s) are no longer present in session history (removed by native compaction or external deletion). Missing: ${shownRefs}` + (extraCount > 0 ? ` (+${extraCount} more)` : "") + `. Block(s) stay compressed \u2014 the summary remains available and nothing was changed. This is a missing-source condition, not a transient fetch failure.`;
10228
+ }
10123
10229
  const activeMessagesBefore = snapshotActiveMessages(messagesState);
10124
10230
  const activeBlockIdsBefore = new Set(messagesState.activeBlockIds);
10125
10231
  for (const target of targets) {
@@ -10931,7 +11037,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
10931
11037
  import { join as join4 } from "path";
10932
11038
  import { existsSync as existsSync4 } from "fs";
10933
11039
  import { homedir as homedir3 } from "os";
10934
- var LOG_VERSION = true ? "1.18.2-pr.448.192" : "dev";
11040
+ var LOG_VERSION = true ? "1.18.2-pr.449.193" : "dev";
10935
11041
  var LEVEL_RANK = {
10936
11042
  debug: 10,
10937
11043
  info: 20,
@@ -13649,7 +13755,7 @@ var server = (async (ctx) => {
13649
13755
  }
13650
13756
  const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
13651
13757
  logger.info("ACP plugin initialized", {
13652
- version: true ? "1.18.2-pr.448.192" : "dev",
13758
+ version: true ? "1.18.2-pr.449.193" : "dev",
13653
13759
  workspace: ctx.directory,
13654
13760
  logLevel: logger.level,
13655
13761
  debug: config.debug,