opencode-acp 1.14.27-pr.370.111 → 1.14.27-pr.376.109

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
@@ -381,13 +381,6 @@ Each level overrides the previous, so project settings take priority over global
381
381
  // Preserve your messages during compression.
382
382
  // Warning: large copy-pasted prompts will never be compressed away
383
383
  "protectUserMessages": false,
384
- // Closed-turn thinking stripping on protected-exempt messages (#368).
385
- // "reasoning" parts of compress/skill tool calls in closed historical
386
- // turns are dropped at request time; the active turn is never touched.
387
- "stripProtectedReasoning": true,
388
- "stripProtectedReasoningThreshold": 0,
389
- "stripProtectedReasoningProviders": ["anthropic", "gemini"],
390
- "stripProtectedReasoningMinMessages": 100
391
384
  },
392
385
  // Garbage collection — hardcoded 100% fallback only
393
386
  "gc": {
package/README.zh-CN.md CHANGED
@@ -334,13 +334,6 @@ ACP 使用自己的配置文件,按以下顺序搜索:
334
334
  // Preserve your messages during compression.
335
335
  // Warning: large copy-pasted prompts will never be compressed away
336
336
  "protectUserMessages": false,
337
- // 受保护豁免消息的闭轮思考剥离(#368)。
338
- // 请求时丢弃已关闭历史轮次中 compress/skill 工具调用的
339
- // "reasoning" 部分;当前活跃轮次永不受影响。
340
- "stripProtectedReasoning": true,
341
- "stripProtectedReasoningThreshold": 0,
342
- "stripProtectedReasoningProviders": ["anthropic", "gemini"],
343
- "stripProtectedReasoningMinMessages": 100
344
337
  },
345
338
  // 垃圾回收与批量清理
346
339
  "gc": {
package/dist/index.js CHANGED
@@ -909,10 +909,6 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
909
909
  "compress.preserveRecentMessages",
910
910
  "compress.preserveRecentTokens",
911
911
  "compress.preserveLastUserMessage",
912
- "compress.stripProtectedReasoning",
913
- "compress.stripProtectedReasoningThreshold",
914
- "compress.stripProtectedReasoningProviders",
915
- "compress.stripProtectedReasoningMinMessages",
916
912
  "gc",
917
913
  "gc.algorithm",
918
914
  "gc.promotionThreshold",
@@ -1266,36 +1262,6 @@ function validateConfigTypes(config) {
1266
1262
  actual: typeof compress.preserveLastUserMessage
1267
1263
  });
1268
1264
  }
1269
- if (compress.stripProtectedReasoning !== void 0 && typeof compress.stripProtectedReasoning !== "boolean") {
1270
- errors.push({
1271
- key: "compress.stripProtectedReasoning",
1272
- expected: "boolean",
1273
- actual: typeof compress.stripProtectedReasoning
1274
- });
1275
- }
1276
- if (compress.stripProtectedReasoningThreshold !== void 0 && (typeof compress.stripProtectedReasoningThreshold !== "number" || !Number.isFinite(compress.stripProtectedReasoningThreshold) || compress.stripProtectedReasoningThreshold < 0)) {
1277
- errors.push({
1278
- key: "compress.stripProtectedReasoningThreshold",
1279
- expected: "number (>= 0)",
1280
- actual: JSON.stringify(compress.stripProtectedReasoningThreshold)
1281
- });
1282
- }
1283
- if (compress.stripProtectedReasoningProviders !== void 0 && (!Array.isArray(compress.stripProtectedReasoningProviders) || !compress.stripProtectedReasoningProviders.every(
1284
- (entry) => typeof entry === "string" && entry.trim() !== ""
1285
- ))) {
1286
- errors.push({
1287
- key: "compress.stripProtectedReasoningProviders",
1288
- expected: 'string[] (non-empty strings; "*" = all providers)',
1289
- actual: JSON.stringify(compress.stripProtectedReasoningProviders)
1290
- });
1291
- }
1292
- if (compress.stripProtectedReasoningMinMessages !== void 0 && (typeof compress.stripProtectedReasoningMinMessages !== "number" || !Number.isInteger(compress.stripProtectedReasoningMinMessages) || compress.stripProtectedReasoningMinMessages < 0)) {
1293
- errors.push({
1294
- key: "compress.stripProtectedReasoningMinMessages",
1295
- expected: "integer (>= 0)",
1296
- actual: JSON.stringify(compress.stripProtectedReasoningMinMessages)
1297
- });
1298
- }
1299
1265
  if (typeof compress.iterationNudgeThreshold === "number" && compress.iterationNudgeThreshold < 1) {
1300
1266
  errors.push({
1301
1267
  key: "compress.iterationNudgeThreshold",
@@ -1693,11 +1659,7 @@ var defaultConfig = {
1693
1659
  lastSegmentSoftBlock: true,
1694
1660
  preserveRecentMessages: 5,
1695
1661
  preserveRecentTokens: 5e3,
1696
- preserveLastUserMessage: true,
1697
- stripProtectedReasoning: true,
1698
- stripProtectedReasoningThreshold: 0,
1699
- stripProtectedReasoningProviders: ["anthropic", "gemini"],
1700
- stripProtectedReasoningMinMessages: 100
1662
+ preserveLastUserMessage: true
1701
1663
  },
1702
1664
  gc: {
1703
1665
  algorithm: "truncate",
@@ -1867,11 +1829,7 @@ function mergeCompress(base, override) {
1867
1829
  lastSegmentSoftBlock: override.lastSegmentSoftBlock ?? base.lastSegmentSoftBlock,
1868
1830
  preserveRecentMessages: override.preserveRecentMessages ?? base.preserveRecentMessages,
1869
1831
  preserveRecentTokens: override.preserveRecentTokens ?? base.preserveRecentTokens,
1870
- preserveLastUserMessage: override.preserveLastUserMessage ?? base.preserveLastUserMessage,
1871
- stripProtectedReasoning: override.stripProtectedReasoning ?? base.stripProtectedReasoning,
1872
- stripProtectedReasoningThreshold: override.stripProtectedReasoningThreshold ?? base.stripProtectedReasoningThreshold,
1873
- stripProtectedReasoningProviders: Array.isArray(override.stripProtectedReasoningProviders) ? [...override.stripProtectedReasoningProviders] : base.stripProtectedReasoningProviders,
1874
- stripProtectedReasoningMinMessages: override.stripProtectedReasoningMinMessages ?? base.stripProtectedReasoningMinMessages
1832
+ preserveLastUserMessage: override.preserveLastUserMessage ?? base.preserveLastUserMessage
1875
1833
  };
1876
1834
  }
1877
1835
  function mergeCommands(base, override) {
@@ -2365,12 +2323,12 @@ function serializePruneMessagesState(messagesState) {
2365
2323
  markedForCleanup: Array.from(messagesState.markedForCleanup)
2366
2324
  };
2367
2325
  }
2368
- async function isSubAgentSession(client, sessionID) {
2326
+ async function getForkParentId(client, sessionID) {
2369
2327
  try {
2370
2328
  const result = await client.session.get({ path: { id: sessionID } });
2371
- return !!result.data?.parentID;
2329
+ return result.data?.parentID ?? null;
2372
2330
  } catch (error) {
2373
- return false;
2331
+ return null;
2374
2332
  }
2375
2333
  }
2376
2334
  function findLastCompactionTimestamp(messages) {
@@ -4407,6 +4365,205 @@ function rebuildCompressionState(state, messages, config, logger) {
4407
4365
  return rebuilt;
4408
4366
  }
4409
4367
 
4368
+ // lib/state/fork-transfer.ts
4369
+ function extractIdentity(message) {
4370
+ const id = message?.info?.id;
4371
+ if (typeof id !== "string" || id.length === 0) {
4372
+ return null;
4373
+ }
4374
+ if (isSyntheticMessage(message)) {
4375
+ return null;
4376
+ }
4377
+ const role = message.info.role;
4378
+ const created = message.info.time?.created;
4379
+ if (typeof created !== "number") {
4380
+ return null;
4381
+ }
4382
+ return { id, role, created };
4383
+ }
4384
+ function buildSharedPrefixMapping(parentMessages, forkMessages) {
4385
+ const parentIds = parentMessages.map(extractIdentity).filter((x) => x !== null);
4386
+ const forkIds = forkMessages.map(extractIdentity).filter((x) => x !== null);
4387
+ const mapping = /* @__PURE__ */ new Map();
4388
+ const limit = Math.min(parentIds.length, forkIds.length);
4389
+ for (let i = 0; i < limit; i++) {
4390
+ const p = parentIds[i];
4391
+ const f = forkIds[i];
4392
+ if (p.created === f.created && p.role === f.role) {
4393
+ mapping.set(p.id, f.id);
4394
+ } else {
4395
+ break;
4396
+ }
4397
+ }
4398
+ return mapping;
4399
+ }
4400
+ function remapBoundaryRef(ref, mapping, forkByRawId, parentByRef) {
4401
+ const parsed = parseBoundaryId(ref);
4402
+ if (!parsed) {
4403
+ return ref;
4404
+ }
4405
+ if (parsed.kind === "compressed-block") {
4406
+ return parsed.ref;
4407
+ }
4408
+ const parentRawId = parentByRef.get(parsed.ref);
4409
+ if (!parentRawId) {
4410
+ return ref;
4411
+ }
4412
+ const forkRawId = mapping.get(parentRawId);
4413
+ if (!forkRawId) {
4414
+ return ref;
4415
+ }
4416
+ const forkRef = forkByRawId.get(forkRawId);
4417
+ return forkRef ?? ref;
4418
+ }
4419
+ function translateBlock(block, mapping, forkByRawId, parentByRef) {
4420
+ const anchorForkId = mapping.get(block.anchorMessageId);
4421
+ if (!anchorForkId) {
4422
+ return null;
4423
+ }
4424
+ const compressForkId = mapping.get(block.compressMessageId);
4425
+ if (!compressForkId) {
4426
+ return null;
4427
+ }
4428
+ const translateList = (ids) => ids.map((id) => mapping.get(id)).filter((id) => id !== void 0);
4429
+ const preserveList = (ids) => [...new Set(ids)];
4430
+ return {
4431
+ ...block,
4432
+ anchorMessageId: anchorForkId,
4433
+ compressMessageId: compressForkId,
4434
+ directMessageIds: translateList(block.directMessageIds),
4435
+ effectiveMessageIds: translateList(block.effectiveMessageIds),
4436
+ directToolIds: preserveList(block.directToolIds),
4437
+ effectiveToolIds: preserveList(block.effectiveToolIds),
4438
+ startId: remapBoundaryRef(block.startId, mapping, forkByRawId, parentByRef),
4439
+ endId: remapBoundaryRef(block.endId, mapping, forkByRawId, parentByRef)
4440
+ };
4441
+ }
4442
+ function translateByMessageId(parentByMessageId, mapping, translatedBlockIds) {
4443
+ const result = /* @__PURE__ */ new Map();
4444
+ for (const [parentMsgId, entry] of Object.entries(parentByMessageId)) {
4445
+ if (!entry || typeof entry !== "object") {
4446
+ continue;
4447
+ }
4448
+ const forkMsgId = mapping.get(parentMsgId);
4449
+ if (!forkMsgId) {
4450
+ continue;
4451
+ }
4452
+ const allBlockIds = (entry.allBlockIds ?? []).filter((id) => translatedBlockIds.has(id));
4453
+ const activeBlockIds = (entry.activeBlockIds ?? []).filter(
4454
+ (id) => translatedBlockIds.has(id)
4455
+ );
4456
+ result.set(forkMsgId, {
4457
+ tokenCount: typeof entry.tokenCount === "number" ? entry.tokenCount : 0,
4458
+ allBlockIds,
4459
+ activeBlockIds
4460
+ });
4461
+ }
4462
+ return result;
4463
+ }
4464
+ async function recoverFromParentState(client, state, forkMessages, parentId, logger) {
4465
+ const parentState = await loadSessionState(parentId, logger);
4466
+ if (!parentState) {
4467
+ logger.info("fork-transfer: no parent state found, falling back to replay", {
4468
+ parentId
4469
+ });
4470
+ return 0;
4471
+ }
4472
+ const parentMessagesState = parentState.prune?.messages;
4473
+ const parentBlocks = parentMessagesState?.blocksById;
4474
+ if (!parentBlocks) {
4475
+ logger.info("fork-transfer: parent state has no blocks, falling back to replay", {
4476
+ parentId
4477
+ });
4478
+ return 0;
4479
+ }
4480
+ const parentBlockEntries = Object.entries(parentBlocks).filter(
4481
+ ([id, block]) => Number.isInteger(Number.parseInt(id, 10)) && Number.parseInt(id, 10) >= 1 && !!block
4482
+ );
4483
+ if (parentBlockEntries.length === 0) {
4484
+ logger.info("fork-transfer: parent state has no blocks, falling back to replay", {
4485
+ parentId
4486
+ });
4487
+ return 0;
4488
+ }
4489
+ let parentMessages;
4490
+ try {
4491
+ const response = await client.session.messages({ path: { id: parentId } });
4492
+ parentMessages = filterMessages(response?.data || response);
4493
+ } catch (error) {
4494
+ logger.warn("fork-transfer: failed to fetch parent messages, falling back to replay", {
4495
+ parentId,
4496
+ error: error?.message || String(error)
4497
+ });
4498
+ return 0;
4499
+ }
4500
+ const mapping = buildSharedPrefixMapping(parentMessages, forkMessages);
4501
+ if (mapping.size === 0) {
4502
+ logger.info("fork-transfer: no shared prefix detected, falling back to replay", {
4503
+ parentId
4504
+ });
4505
+ return 0;
4506
+ }
4507
+ assignMessageRefs(state, forkMessages);
4508
+ const forkByRawId = state.messageIds.byRawId;
4509
+ const parentByRef = new Map(Object.entries(parentState.messageIds?.byRef ?? {}));
4510
+ const translatedBlocks = /* @__PURE__ */ new Map();
4511
+ let skipped = 0;
4512
+ for (const [blockIdStr, block] of parentBlockEntries) {
4513
+ const blockId = Number.parseInt(blockIdStr, 10);
4514
+ const translated = translateBlock(block, mapping, forkByRawId, parentByRef);
4515
+ if (translated) {
4516
+ translatedBlocks.set(blockId, translated);
4517
+ } else {
4518
+ skipped++;
4519
+ }
4520
+ }
4521
+ if (translatedBlocks.size === 0) {
4522
+ logger.info("fork-transfer: no blocks translatable, falling back to replay", {
4523
+ parentId,
4524
+ parentBlockCount: parentBlockEntries.length,
4525
+ mappingSize: mapping.size
4526
+ });
4527
+ return 0;
4528
+ }
4529
+ const activeCount = Array.from(translatedBlocks.values()).filter((b) => b.active).length;
4530
+ if (activeCount === 0) {
4531
+ logger.info("fork-transfer: no active blocks translatable, falling back to replay", {
4532
+ parentId,
4533
+ recoveredBlocks: translatedBlocks.size,
4534
+ skippedBlocks: skipped,
4535
+ mappingSize: mapping.size
4536
+ });
4537
+ return 0;
4538
+ }
4539
+ const translatedByMessageId = translateByMessageId(
4540
+ parentMessagesState.byMessageId ?? {},
4541
+ mapping,
4542
+ new Set(translatedBlocks.keys())
4543
+ );
4544
+ state.prune.messages = loadPruneMessagesState({
4545
+ byMessageId: Object.fromEntries(translatedByMessageId),
4546
+ blocksById: Object.fromEntries(
4547
+ Array.from(translatedBlocks.entries()).map(
4548
+ ([id, block]) => [String(id), block]
4549
+ )
4550
+ ),
4551
+ activeBlockIds: [],
4552
+ activeByAnchorMessageId: {},
4553
+ nextBlockId: 1,
4554
+ nextRunId: 1,
4555
+ markedForCleanup: []
4556
+ });
4557
+ logger.info("fork-transfer: recovered compression state from parent", {
4558
+ parentId,
4559
+ recoveredBlocks: translatedBlocks.size,
4560
+ activeBlocks: activeCount,
4561
+ skippedBlocks: skipped,
4562
+ mappingSize: mapping.size
4563
+ });
4564
+ return activeCount;
4565
+ }
4566
+
4410
4567
  // lib/state/state.ts
4411
4568
  async function updatePerTurnState(state, logger, messages) {
4412
4569
  const lastCompactionTimestamp = findLastCompactionTimestamp(messages);
@@ -4595,16 +4752,22 @@ async function ensureSessionInitialized(client, state, sessionId, logger, messag
4595
4752
  }
4596
4753
  resetSessionState(state);
4597
4754
  state.sessionId = sessionId;
4598
- const isSubAgent = await isSubAgentSession(client, sessionId);
4599
- state.isSubAgent = isSubAgent;
4755
+ const parentId = await getForkParentId(client, sessionId);
4756
+ state.isSubAgent = parentId !== null;
4600
4757
  state.lastCompaction = findLastCompactionTimestamp(messages);
4601
4758
  state.currentTurn = countTurns(state, messages);
4602
4759
  state.nudges.turnNudgeAnchors = collectTurnNudgeAnchors(messages);
4603
4760
  const persisted = await loadSessionState(sessionId, logger);
4604
4761
  if (persisted === null) {
4605
4762
  if (config) {
4606
- const rebuilt = rebuildCompressionState(state, messages, config, logger);
4607
- if (rebuilt > 0) {
4763
+ let recovered = 0;
4764
+ if (parentId) {
4765
+ recovered = await recoverFromParentState(client, state, messages, parentId, logger);
4766
+ }
4767
+ if (recovered === 0) {
4768
+ recovered = rebuildCompressionState(state, messages, config, logger);
4769
+ }
4770
+ if (recovered > 0) {
4608
4771
  await saveSessionState(state, logger);
4609
4772
  }
4610
4773
  }
@@ -8304,76 +8467,6 @@ function stripStaleMetadata(messages) {
8304
8467
  });
8305
8468
  });
8306
8469
  }
8307
- function stripProtectedReasoning(messages, protectedTools, threshold, options) {
8308
- if (protectedTools.length === 0) {
8309
- return 0;
8310
- }
8311
- const allowedProviders = options?.allowedProviders;
8312
- if (allowedProviders !== void 0) {
8313
- if (allowedProviders.length === 0) {
8314
- return 0;
8315
- }
8316
- if (!allowedProviders.some((entry) => entry.trim() === "*")) {
8317
- const providerID = options?.providerID;
8318
- if (providerID === void 0 || !allowedProviders.some(
8319
- (entry) => providerID.toLowerCase().includes(entry.trim().toLowerCase())
8320
- )) {
8321
- return 0;
8322
- }
8323
- }
8324
- }
8325
- const minMessages = options?.minMessages;
8326
- if (minMessages !== void 0 && minMessages > 0 && messages.length < minMessages) {
8327
- return 0;
8328
- }
8329
- const lastUserMessage = getLastUserMessage(messages);
8330
- if (!lastUserMessage || lastUserMessage.info.role !== "user") {
8331
- return 0;
8332
- }
8333
- let lastUserIndex = -1;
8334
- for (let i = messages.length - 1; i >= 0; i--) {
8335
- if (messages[i] === lastUserMessage) {
8336
- lastUserIndex = i;
8337
- break;
8338
- }
8339
- }
8340
- if (lastUserIndex <= 0) {
8341
- return 0;
8342
- }
8343
- const protectedSet = new Set(protectedTools);
8344
- let removed = 0;
8345
- for (let i = 0; i < lastUserIndex; i++) {
8346
- const message = messages[i];
8347
- if (!message || message.info.role !== "assistant") {
8348
- continue;
8349
- }
8350
- const parts = Array.isArray(message.parts) ? message.parts : [];
8351
- let hasProtectedTool = false;
8352
- let reasoningLength = 0;
8353
- for (const part of parts) {
8354
- if (part.type === "tool") {
8355
- if (protectedSet.has(part.tool)) {
8356
- hasProtectedTool = true;
8357
- }
8358
- } else if (part.type === "reasoning") {
8359
- reasoningLength += part.text.length;
8360
- }
8361
- }
8362
- if (!hasProtectedTool) {
8363
- continue;
8364
- }
8365
- if (reasoningLength <= threshold) {
8366
- continue;
8367
- }
8368
- const filtered = parts.filter((part) => part.type !== "reasoning");
8369
- if (filtered.length === parts.length) {
8370
- continue;
8371
- }
8372
- message.parts = filtered;
8373
- removed += parts.length - filtered.length;
8374
- }
8375
- return removed;
8376
- }
8377
8470
 
8378
8471
  // lib/messages/priority.ts
8379
8472
  function buildPriorityMap(_config, _state, _messages) {
@@ -9490,7 +9583,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
9490
9583
  import { join as join3 } from "path";
9491
9584
  import { existsSync as existsSync3 } from "fs";
9492
9585
  import { homedir as homedir3 } from "os";
9493
- var LOG_VERSION = true ? "1.14.27-pr.370.111" : "dev";
9586
+ var LOG_VERSION = true ? "1.14.27-pr.376.109" : "dev";
9494
9587
  var LEVEL_RANK = {
9495
9588
  debug: 10,
9496
9589
  info: 20,
@@ -11432,7 +11525,6 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
11432
11525
  return;
11433
11526
  }
11434
11527
  const lastUserMessage = getLastUserMessage(messages);
11435
- const requestModel = lastUserMessage?.info?.model;
11436
11528
  let state;
11437
11529
  if (!lastUserMessage) {
11438
11530
  state = createSessionState();
@@ -11443,6 +11535,7 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
11443
11535
  messages,
11444
11536
  config
11445
11537
  );
11538
+ const requestModel = lastUserMessage.info.model;
11446
11539
  const requestModelLimit = registry4.resolveModelLimit(
11447
11540
  requestModel?.providerID,
11448
11541
  requestModel?.modelID
@@ -11507,30 +11600,6 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
11507
11600
  prune(state, logger, config, output.messages);
11508
11601
  truncateLargeToolOutputs(state, config, logger, output.messages);
11509
11602
  hideConsumedCompressCalls(state, output.messages);
11510
- if (config.compress.stripProtectedReasoning !== false) {
11511
- const removedReasoning = stripProtectedReasoning(
11512
- output.messages,
11513
- config.compress.protectedTools,
11514
- config.compress.stripProtectedReasoningThreshold ?? 0,
11515
- {
11516
- // Prefer this request's provider from the user-message
11517
- // model metadata; fall back to the cached identity pair
11518
- // (undefined on the first request of a fresh session →
11519
- // fail-closed).
11520
- providerID: requestModel?.providerID ?? state.modelProviderID,
11521
- allowedProviders: config.compress.stripProtectedReasoningProviders ?? [
11522
- "anthropic",
11523
- "gemini"
11524
- ],
11525
- minMessages: config.compress.stripProtectedReasoningMinMessages ?? 100
11526
- }
11527
- );
11528
- if (removedReasoning > 0) {
11529
- logger.debug("stripProtectedReasoning: removed reasoning parts from historical protected messages", {
11530
- removed: removedReasoning
11531
- });
11532
- }
11533
- }
11534
11603
  assignMessageRefs(state, output.messages);
11535
11604
  const compressionPriorities = buildPriorityMap(config, state, output.messages);
11536
11605
  prompts.reload();
@@ -11987,7 +12056,7 @@ var server = (async (ctx) => {
11987
12056
  }
11988
12057
  const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
11989
12058
  logger.info("ACP plugin initialized", {
11990
- version: true ? "1.14.27-pr.370.111" : "dev",
12059
+ version: true ? "1.14.27-pr.376.109" : "dev",
11991
12060
  workspace: ctx.directory,
11992
12061
  logLevel: logger.level,
11993
12062
  debug: config.debug,