opencode-acp 1.18.4-pr.483.216 → 1.18.4-pr.484.217

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/README.md CHANGED
@@ -431,9 +431,15 @@ Each level overrides the previous, so project settings take priority over global
431
431
  // compression, so their thinking rides along every request as an
432
432
  // unreclaimable floor. Closed turns only; the active round is never
433
433
  // touched. Small thinkings under the threshold are kept.
434
+ // [#482] Pin the wire form of historical thinking: fold each assistant
435
+ // message's reasoning parts into one <thinking>-marked text part at
436
+ // request time, so serialized bytes stay deterministic across host
437
+ // metadata-table refreshes. On by default; set normalize:false for
438
+ // direct providers needing structured thinking replay.
434
439
  "reasoning": {
435
440
  "drop": true,
436
441
  "threshold": 2048,
442
+ "normalize": true
437
443
  },
438
444
  // [#478] Image archive: spill data-URL images to disk when they enter
439
445
  // the session so folded ranges keep re-readable pointers instead of
package/README.zh-CN.md CHANGED
@@ -376,9 +376,14 @@ ACP 使用自己的配置文件,按以下顺序搜索:
376
376
  // compress 调用被硬排除在压缩之外,其思考内容会随每次请求原样重发,
377
377
  // 形成无法回收的上下文底座。仅作用于已关闭的轮次,活跃轮永不触碰;
378
378
  // 低于阈值的小思考保留。
379
+ // [#482] 钉死历史思考的 wire 形态:请求时把每个 assistant 消息的
380
+ // reasoning 部分折叠为一个带 <thinking> 标记的 text 部分,使序列化字节
381
+ // 在宿主元数据表刷新后保持确定。默认开启;直连需要结构化 thinking
382
+ // 回放的 provider 设 normalize:false。
379
383
  "reasoning": {
380
384
  "drop": true,
381
385
  "threshold": 2048,
386
+ "normalize": true
382
387
  },
383
388
  // [#478] 图片归档:图片(data-URL)进入会话时即落盘,折叠后摘要里保留
384
389
  // 可重读的路径指针,而不是把像素悄悄丢掉。文件落在
package/dist/index.js CHANGED
@@ -916,6 +916,7 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
916
916
  "compress.reasoning",
917
917
  "compress.reasoning.drop",
918
918
  "compress.reasoning.threshold",
919
+ "compress.reasoning.normalize",
919
920
  "compress.images",
920
921
  "compress.images.enabled",
921
922
  "compress.images.minBytes",
@@ -1292,7 +1293,7 @@ function validateConfigTypes(config) {
1292
1293
  if (typeof compress.reasoning !== "object" || compress.reasoning === null || Array.isArray(compress.reasoning)) {
1293
1294
  errors.push({
1294
1295
  key: "compress.reasoning",
1295
- expected: "object { drop?: boolean; threshold?: integer (>= 0) }",
1296
+ expected: "object { drop?: boolean; threshold?: integer (>= 0); normalize?: boolean }",
1296
1297
  actual: JSON.stringify(compress.reasoning)
1297
1298
  });
1298
1299
  } else {
@@ -1310,6 +1311,13 @@ function validateConfigTypes(config) {
1310
1311
  actual: JSON.stringify(compress.reasoning.threshold)
1311
1312
  });
1312
1313
  }
1314
+ if (compress.reasoning.normalize !== void 0 && typeof compress.reasoning.normalize !== "boolean") {
1315
+ errors.push({
1316
+ key: "compress.reasoning.normalize",
1317
+ expected: "boolean",
1318
+ actual: typeof compress.reasoning.normalize
1319
+ });
1320
+ }
1313
1321
  }
1314
1322
  }
1315
1323
  if (compress.images !== void 0) {
@@ -1496,7 +1504,7 @@ function validateConfigTypes(config) {
1496
1504
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
1497
1505
  errors.push({
1498
1506
  key,
1499
- expected: "object { drop?: boolean; threshold?: integer (>= 0) }",
1507
+ expected: "object { drop?: boolean; threshold?: integer (>= 0); normalize?: boolean }",
1500
1508
  actual: JSON.stringify(value)
1501
1509
  });
1502
1510
  break;
@@ -1516,6 +1524,13 @@ function validateConfigTypes(config) {
1516
1524
  actual: JSON.stringify(reasoning.threshold)
1517
1525
  });
1518
1526
  }
1527
+ if (reasoning.normalize !== void 0 && typeof reasoning.normalize !== "boolean") {
1528
+ errors.push({
1529
+ key: `${key}.normalize`,
1530
+ expected: "boolean",
1531
+ actual: typeof reasoning.normalize
1532
+ });
1533
+ }
1519
1534
  break;
1520
1535
  }
1521
1536
  };
@@ -1976,6 +1991,7 @@ function mergeCompress(base, override) {
1976
1991
  if (!override) {
1977
1992
  return base;
1978
1993
  }
1994
+ const normalizeResolved = override.reasoning?.normalize ?? base.reasoning?.normalize;
1979
1995
  return {
1980
1996
  permission: override.permission ?? base.permission,
1981
1997
  showCompression: override.showCompression ?? base.showCompression,
@@ -2010,7 +2026,8 @@ function mergeCompress(base, override) {
2010
2026
  preserveLastUserMessage: override.preserveLastUserMessage ?? base.preserveLastUserMessage,
2011
2027
  reasoning: {
2012
2028
  drop: override.reasoning?.drop ?? base.reasoning?.drop ?? DEFAULT_COMPRESS_REASONING.drop,
2013
- threshold: override.reasoning?.threshold ?? base.reasoning?.threshold ?? DEFAULT_COMPRESS_REASONING.threshold
2029
+ threshold: override.reasoning?.threshold ?? base.reasoning?.threshold ?? DEFAULT_COMPRESS_REASONING.threshold,
2030
+ ...normalizeResolved !== void 0 ? { normalize: normalizeResolved } : {}
2014
2031
  },
2015
2032
  images: {
2016
2033
  enabled: override.images?.enabled ?? base.images?.enabled ?? DEFAULT_COMPRESS_IMAGES.enabled,
@@ -9884,6 +9901,8 @@ var injectMessageIds = (state, config, messages, compressionPriorities) => {
9884
9901
  };
9885
9902
 
9886
9903
  // lib/messages/reasoning-strip.ts
9904
+ var isReasoningWithText = (part) => part.type === "reasoning" && typeof part.text === "string";
9905
+ var isTextWithText = (part) => part.type === "text" && typeof part.text === "string";
9887
9906
  function stripStaleMetadata(messages) {
9888
9907
  const lastUserMessage = getLastUserMessage(messages);
9889
9908
  if (lastUserMessage?.info.role !== "user") {
@@ -9952,62 +9971,57 @@ function dropCompressReasoning(messages, threshold) {
9952
9971
  }
9953
9972
  return removed;
9954
9973
  }
9955
-
9956
- // lib/messages/reasoning-canonicalize.ts
9957
- var CANONICAL_OPEN = "<thinking>\n";
9958
- var CANONICAL_CLOSE = "\n</thinking>";
9959
- function canonicalizeReasoningShape(messages) {
9974
+ function normalizeThinkingForm(messages) {
9960
9975
  let normalized = 0;
9961
9976
  for (const message of messages) {
9962
9977
  if (!message || message.info.role !== "assistant") {
9963
9978
  continue;
9964
9979
  }
9965
9980
  const parts = Array.isArray(message.parts) ? message.parts : [];
9966
- let allReasoning = "";
9967
- for (const part of parts) {
9968
- if (part.type === "reasoning") {
9969
- allReasoning += part.text;
9970
- }
9971
- }
9972
- if (allReasoning.length === 0) {
9981
+ const reasoningParts = parts.filter(isReasoningWithText);
9982
+ if (reasoningParts.length === 0) {
9973
9983
  continue;
9974
9984
  }
9975
- let allText = "";
9985
+ const R = reasoningParts.map((part) => part.text).join("");
9986
+ const textParts = parts.filter(isTextWithText);
9987
+ const T = textParts.map((part) => part.text).join("");
9988
+ const mergedText = `<thinking>
9989
+ ${R}
9990
+ </thinking>` + (T.length > 0 ? `
9991
+ ${T}` : "");
9992
+ const donor = textParts[0];
9993
+ const newParts = [];
9994
+ let inserted = false;
9976
9995
  for (const part of parts) {
9977
- if (part.type === "text") {
9978
- allText += part.text;
9996
+ if (part.type === "reasoning" || part.type === "text") {
9997
+ if (!inserted) {
9998
+ if (donor) {
9999
+ newParts.push({ ...donor, text: mergedText });
10000
+ } else {
10001
+ newParts.push({
10002
+ id: `${message.info.id}-acp-think`,
10003
+ sessionID: message.info.sessionID,
10004
+ messageID: message.info.id,
10005
+ type: "text",
10006
+ text: mergedText
10007
+ });
10008
+ }
10009
+ inserted = true;
10010
+ }
10011
+ continue;
9979
10012
  }
10013
+ newParts.push(part);
9980
10014
  }
9981
- const merged = allText.length > 0 ? `${CANONICAL_OPEN}${allReasoning}${CANONICAL_CLOSE}
9982
- ${allText}` : `${CANONICAL_OPEN}${allReasoning}${CANONICAL_CLOSE}`;
9983
- const rebuilt = [];
9984
- let placed = false;
9985
- const placeMerged = (carrier) => {
9986
- if (placed) {
9987
- return;
9988
- }
9989
- placed = true;
9990
- const part = {
9991
- id: carrier.id,
10015
+ if (!inserted) {
10016
+ newParts.unshift({
10017
+ id: `${message.info.id}-acp-think`,
9992
10018
  sessionID: message.info.sessionID,
9993
10019
  messageID: message.info.id,
9994
10020
  type: "text",
9995
- text: merged
9996
- };
9997
- rebuilt.push(part);
9998
- };
9999
- for (const part of parts) {
10000
- if (part.type === "reasoning") {
10001
- placeMerged(part);
10002
- continue;
10003
- }
10004
- if (part.type === "text") {
10005
- placeMerged(part);
10006
- continue;
10007
- }
10008
- rebuilt.push(part);
10021
+ text: mergedText
10022
+ });
10009
10023
  }
10010
- message.parts = rebuilt;
10024
+ message.parts = newParts;
10011
10025
  normalized++;
10012
10026
  }
10013
10027
  return normalized;
@@ -11389,7 +11403,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
11389
11403
  import { join as join5 } from "path";
11390
11404
  import { existsSync as existsSync5 } from "fs";
11391
11405
  import { homedir as homedir3 } from "os";
11392
- var LOG_VERSION = true ? "1.18.4-pr.483.216" : "dev";
11406
+ var LOG_VERSION = true ? "1.18.4-pr.484.217" : "dev";
11393
11407
  var LEVEL_RANK = {
11394
11408
  debug: 10,
11395
11409
  info: 20,
@@ -13588,11 +13602,13 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
13588
13602
  });
13589
13603
  }
13590
13604
  }
13591
- const canonicalizedReasoning = canonicalizeReasoningShape(output.messages);
13592
- if (canonicalizedReasoning > 0) {
13593
- logger.debug("reasoning-canonicalize: pinned historical thinking shape", {
13594
- messages: canonicalizedReasoning
13595
- });
13605
+ if (reasoningConfig?.normalize !== false) {
13606
+ const normalizedThinking = normalizeThinkingForm(output.messages);
13607
+ if (normalizedThinking > 0) {
13608
+ logger.debug("compress.reasoning: pinned thinking wire form", {
13609
+ normalizedMessages: normalizedThinking
13610
+ });
13611
+ }
13596
13612
  }
13597
13613
  ensureBuiltinFiltersRegistered();
13598
13614
  const effectiveLimit = resolveEffectiveContextLimit(state, config);
@@ -14105,7 +14121,7 @@ var server = (async (ctx) => {
14105
14121
  }
14106
14122
  const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
14107
14123
  logger.info("ACP plugin initialized", {
14108
- version: true ? "1.18.4-pr.483.216" : "dev",
14124
+ version: true ? "1.18.4-pr.484.217" : "dev",
14109
14125
  workspace: ctx.directory,
14110
14126
  logLevel: logger.level,
14111
14127
  debug: config.debug,