opencode-acp 1.18.1-pr.412.173 → 1.18.1-pr.416.175

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
@@ -2479,6 +2479,163 @@ import { existsSync as existsSync2 } from "fs";
2479
2479
  import { homedir as homedir2 } from "os";
2480
2480
  import { isAbsolute, join as join2 } from "path";
2481
2481
 
2482
+ // lib/message-ids.ts
2483
+ var MESSAGE_REF_REGEX = /^m(\d{4,5})$/;
2484
+ var BLOCK_REF_REGEX = /^b([1-9]\d*)$/;
2485
+ var MESSAGE_ID_TAG_NAME = "dcp-message-id";
2486
+ var MESSAGE_REF_WIDTH = 5;
2487
+ var MESSAGE_REF_MIN_INDEX = 1;
2488
+ var MESSAGE_REF_MAX_INDEX = 99999;
2489
+ function formatMessageRef(index) {
2490
+ if (!Number.isInteger(index) || index < MESSAGE_REF_MIN_INDEX || index > MESSAGE_REF_MAX_INDEX) {
2491
+ throw new Error(
2492
+ `Message ID index out of bounds: ${index}. Supported range is ${MESSAGE_REF_MIN_INDEX}-${MESSAGE_REF_MAX_INDEX}.`
2493
+ );
2494
+ }
2495
+ return `m${index.toString().padStart(MESSAGE_REF_WIDTH, "0")}`;
2496
+ }
2497
+ function formatBlockRef(blockId) {
2498
+ if (!Number.isInteger(blockId) || blockId < 1) {
2499
+ throw new Error(`Invalid block ID: ${blockId}`);
2500
+ }
2501
+ return `b${blockId}`;
2502
+ }
2503
+ function parseMessageRef(ref) {
2504
+ const normalized = ref.trim().toLowerCase();
2505
+ const match = normalized.match(MESSAGE_REF_REGEX);
2506
+ if (!match) {
2507
+ return null;
2508
+ }
2509
+ const index = Number.parseInt(match[1], 10);
2510
+ if (!Number.isInteger(index)) {
2511
+ return null;
2512
+ }
2513
+ if (index < MESSAGE_REF_MIN_INDEX || index > MESSAGE_REF_MAX_INDEX) {
2514
+ return null;
2515
+ }
2516
+ return index;
2517
+ }
2518
+ function migrateMessageRef(ref) {
2519
+ const index = parseMessageRef(ref);
2520
+ return index === null ? ref : formatMessageRef(index);
2521
+ }
2522
+ function parseBlockRef(ref) {
2523
+ const normalized = ref.trim().toLowerCase();
2524
+ const match = normalized.match(BLOCK_REF_REGEX);
2525
+ if (!match) {
2526
+ return null;
2527
+ }
2528
+ const id = Number.parseInt(match[1], 10);
2529
+ return Number.isInteger(id) ? id : null;
2530
+ }
2531
+ function parseBoundaryId(id) {
2532
+ const normalized = id.trim().toLowerCase();
2533
+ const messageIndex = parseMessageRef(normalized);
2534
+ if (messageIndex !== null) {
2535
+ return {
2536
+ kind: "message",
2537
+ ref: formatMessageRef(messageIndex),
2538
+ index: messageIndex
2539
+ };
2540
+ }
2541
+ const blockId = parseBlockRef(normalized);
2542
+ if (blockId !== null) {
2543
+ return {
2544
+ kind: "compressed-block",
2545
+ ref: formatBlockRef(blockId),
2546
+ blockId
2547
+ };
2548
+ }
2549
+ return null;
2550
+ }
2551
+ function escapeXmlAttribute(value) {
2552
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2553
+ }
2554
+ function formatMessageIdTag(ref, attributes) {
2555
+ const serializedAttributes = Object.entries(attributes || {}).sort(([left], [right]) => left.localeCompare(right)).map(([name, value]) => {
2556
+ if (name.trim().length === 0 || typeof value !== "string" || value.length === 0) {
2557
+ return "";
2558
+ }
2559
+ return ` ${name}="${escapeXmlAttribute(value)}"`;
2560
+ }).join("");
2561
+ return `
2562
+ <${MESSAGE_ID_TAG_NAME}${serializedAttributes}>${ref}</${MESSAGE_ID_TAG_NAME}>`;
2563
+ }
2564
+ function formatTokenSize(tokens) {
2565
+ if (tokens < 1e3) return String(tokens);
2566
+ if (tokens < 1e4) return `${(tokens / 1e3).toFixed(1)}K`;
2567
+ return `${Math.round(tokens / 1e3)}K`;
2568
+ }
2569
+ function classifyMessageType(parts) {
2570
+ let hasTool = false;
2571
+ let hasText = false;
2572
+ let hasReasoning = false;
2573
+ const toolNames = [];
2574
+ for (const part of parts) {
2575
+ if (part.type === "tool") {
2576
+ hasTool = true;
2577
+ if (typeof part.tool === "string" && !toolNames.includes(part.tool)) {
2578
+ toolNames.push(part.tool);
2579
+ }
2580
+ } else if (part.type === "text") {
2581
+ hasText = true;
2582
+ } else if (part.type === "reasoning") {
2583
+ hasReasoning = true;
2584
+ }
2585
+ }
2586
+ if (hasTool) {
2587
+ return toolNames.length > 0 ? `tool:${toolNames.join(",")}` : "tool";
2588
+ }
2589
+ if (hasReasoning && !hasText) return "reasoning";
2590
+ return "text";
2591
+ }
2592
+ function assignMessageRefs(state, messages) {
2593
+ let assigned = 0;
2594
+ let skippedSubAgentPrompt = false;
2595
+ for (const message of messages) {
2596
+ if (isIgnoredUserMessage(message)) {
2597
+ continue;
2598
+ }
2599
+ if (state.isSubAgent && !skippedSubAgentPrompt && message.info.role === "user") {
2600
+ skippedSubAgentPrompt = true;
2601
+ continue;
2602
+ }
2603
+ const rawMessageId = message.info.id;
2604
+ if (typeof rawMessageId !== "string" || rawMessageId.length === 0) {
2605
+ continue;
2606
+ }
2607
+ if (rawMessageId.startsWith("msg_dcp_summary_") || rawMessageId.startsWith("msg_dcp_text_")) {
2608
+ continue;
2609
+ }
2610
+ const existingRef = state.messageIds.byRawId.get(rawMessageId);
2611
+ if (existingRef) {
2612
+ if (state.messageIds.byRef.get(existingRef) !== rawMessageId) {
2613
+ state.messageIds.byRef.set(existingRef, rawMessageId);
2614
+ }
2615
+ continue;
2616
+ }
2617
+ const ref = allocateNextMessageRef(state);
2618
+ state.messageIds.byRawId.set(rawMessageId, ref);
2619
+ state.messageIds.byRef.set(ref, rawMessageId);
2620
+ assigned++;
2621
+ }
2622
+ return assigned;
2623
+ }
2624
+ function allocateNextMessageRef(state) {
2625
+ let candidate = Number.isInteger(state.messageIds.nextRef) ? Math.max(MESSAGE_REF_MIN_INDEX, state.messageIds.nextRef) : MESSAGE_REF_MIN_INDEX;
2626
+ while (candidate <= MESSAGE_REF_MAX_INDEX) {
2627
+ const ref = formatMessageRef(candidate);
2628
+ if (!state.messageIds.byRef.has(ref)) {
2629
+ state.messageIds.nextRef = candidate + 1;
2630
+ return ref;
2631
+ }
2632
+ candidate++;
2633
+ }
2634
+ throw new Error(
2635
+ `Message ID alias capacity exceeded. Cannot allocate more than ${formatMessageRef(MESSAGE_REF_MAX_INDEX)} aliases in this session.`
2636
+ );
2637
+ }
2638
+
2482
2639
  // lib/state/utils.ts
2483
2640
  var isMessageCompacted = (state, msg) => {
2484
2641
  if (!isMessageWithInfo(msg)) {
@@ -2630,8 +2787,10 @@ function loadPruneMessagesState(persisted) {
2630
2787
  mode: block.mode === "range" || block.mode === "message" ? block.mode : void 0,
2631
2788
  topic: typeof block.topic === "string" ? block.topic : "",
2632
2789
  batchTopic: typeof block.batchTopic === "string" ? block.batchTopic : typeof block.topic === "string" ? block.topic : "",
2633
- startId: typeof block.startId === "string" ? block.startId : "",
2634
- endId: typeof block.endId === "string" ? block.endId : "",
2790
+ // [Issue #415] Migrate legacy 4-digit boundary refs (pre-1.1.0 state)
2791
+ // to canonical 5-digit form, matching the messageIds ref migration.
2792
+ startId: typeof block.startId === "string" ? migrateMessageRef(block.startId) : "",
2793
+ endId: typeof block.endId === "string" ? migrateMessageRef(block.endId) : "",
2635
2794
  anchorMessageId: typeof block.anchorMessageId === "string" ? block.anchorMessageId : "",
2636
2795
  compressMessageId: typeof block.compressMessageId === "string" ? block.compressMessageId : "",
2637
2796
  compressCallId: typeof block.compressCallId === "string" ? block.compressCallId : void 0,
@@ -2904,24 +3063,12 @@ function drainSaveQueue(key) {
2904
3063
  });
2905
3064
  }
2906
3065
  async function loadSessionState(sessionId, logger, storageDir) {
2907
- const filePath = getSessionFilePath(sessionId, storageDir);
2908
- let content;
2909
3066
  try {
2910
- content = await fs.readFile(filePath, "utf-8");
2911
- } catch (error) {
2912
- if (error?.code === "ENOENT") {
2913
- return null;
2914
- }
2915
- if (error?.code === "EISDIR" || error?.code === "ENOTDIR") {
2916
- logger.warn("Invalid session state file location, ignoring", {
2917
- sessionId,
2918
- error: error?.message
2919
- });
3067
+ const filePath = getSessionFilePath(sessionId, storageDir);
3068
+ if (!existsSync2(filePath)) {
2920
3069
  return null;
2921
3070
  }
2922
- throw error;
2923
- }
2924
- try {
3071
+ const content = await fs.readFile(filePath, "utf-8");
2925
3072
  const state = JSON.parse(content);
2926
3073
  const hasPruneMessages = state?.prune?.messages && typeof state.prune.messages === "object";
2927
3074
  const hasNudgeFormat = state?.nudges && typeof state.nudges === "object";
@@ -2998,159 +3145,6 @@ import { existsSync as existsSync3 } from "fs";
2998
3145
  import { join as join3 } from "path";
2999
3146
  import { cwd } from "process";
3000
3147
 
3001
- // lib/message-ids.ts
3002
- var MESSAGE_REF_REGEX = /^m(\d{4,5})$/;
3003
- var BLOCK_REF_REGEX = /^b([1-9]\d*)$/;
3004
- var MESSAGE_ID_TAG_NAME = "dcp-message-id";
3005
- var MESSAGE_REF_WIDTH = 5;
3006
- var MESSAGE_REF_MIN_INDEX = 1;
3007
- var MESSAGE_REF_MAX_INDEX = 99999;
3008
- function formatMessageRef(index) {
3009
- if (!Number.isInteger(index) || index < MESSAGE_REF_MIN_INDEX || index > MESSAGE_REF_MAX_INDEX) {
3010
- throw new Error(
3011
- `Message ID index out of bounds: ${index}. Supported range is ${MESSAGE_REF_MIN_INDEX}-${MESSAGE_REF_MAX_INDEX}.`
3012
- );
3013
- }
3014
- return `m${index.toString().padStart(MESSAGE_REF_WIDTH, "0")}`;
3015
- }
3016
- function formatBlockRef(blockId) {
3017
- if (!Number.isInteger(blockId) || blockId < 1) {
3018
- throw new Error(`Invalid block ID: ${blockId}`);
3019
- }
3020
- return `b${blockId}`;
3021
- }
3022
- function parseMessageRef(ref) {
3023
- const normalized = ref.trim().toLowerCase();
3024
- const match = normalized.match(MESSAGE_REF_REGEX);
3025
- if (!match) {
3026
- return null;
3027
- }
3028
- const index = Number.parseInt(match[1], 10);
3029
- if (!Number.isInteger(index)) {
3030
- return null;
3031
- }
3032
- if (index < MESSAGE_REF_MIN_INDEX || index > MESSAGE_REF_MAX_INDEX) {
3033
- return null;
3034
- }
3035
- return index;
3036
- }
3037
- function parseBlockRef(ref) {
3038
- const normalized = ref.trim().toLowerCase();
3039
- const match = normalized.match(BLOCK_REF_REGEX);
3040
- if (!match) {
3041
- return null;
3042
- }
3043
- const id = Number.parseInt(match[1], 10);
3044
- return Number.isInteger(id) ? id : null;
3045
- }
3046
- function parseBoundaryId(id) {
3047
- const normalized = id.trim().toLowerCase();
3048
- const messageIndex = parseMessageRef(normalized);
3049
- if (messageIndex !== null) {
3050
- return {
3051
- kind: "message",
3052
- ref: formatMessageRef(messageIndex),
3053
- index: messageIndex
3054
- };
3055
- }
3056
- const blockId = parseBlockRef(normalized);
3057
- if (blockId !== null) {
3058
- return {
3059
- kind: "compressed-block",
3060
- ref: formatBlockRef(blockId),
3061
- blockId
3062
- };
3063
- }
3064
- return null;
3065
- }
3066
- function escapeXmlAttribute(value) {
3067
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
3068
- }
3069
- function formatMessageIdTag(ref, attributes) {
3070
- const serializedAttributes = Object.entries(attributes || {}).sort(([left], [right]) => left.localeCompare(right)).map(([name, value]) => {
3071
- if (name.trim().length === 0 || typeof value !== "string" || value.length === 0) {
3072
- return "";
3073
- }
3074
- return ` ${name}="${escapeXmlAttribute(value)}"`;
3075
- }).join("");
3076
- return `
3077
- <${MESSAGE_ID_TAG_NAME}${serializedAttributes}>${ref}</${MESSAGE_ID_TAG_NAME}>`;
3078
- }
3079
- function formatTokenSize(tokens) {
3080
- if (tokens < 1e3) return String(tokens);
3081
- if (tokens < 1e4) return `${(tokens / 1e3).toFixed(1)}K`;
3082
- return `${Math.round(tokens / 1e3)}K`;
3083
- }
3084
- function classifyMessageType(parts) {
3085
- let hasTool = false;
3086
- let hasText = false;
3087
- let hasReasoning = false;
3088
- const toolNames = [];
3089
- for (const part of parts) {
3090
- if (part.type === "tool") {
3091
- hasTool = true;
3092
- if (typeof part.tool === "string" && !toolNames.includes(part.tool)) {
3093
- toolNames.push(part.tool);
3094
- }
3095
- } else if (part.type === "text") {
3096
- hasText = true;
3097
- } else if (part.type === "reasoning") {
3098
- hasReasoning = true;
3099
- }
3100
- }
3101
- if (hasTool) {
3102
- return toolNames.length > 0 ? `tool:${toolNames.join(",")}` : "tool";
3103
- }
3104
- if (hasReasoning && !hasText) return "reasoning";
3105
- return "text";
3106
- }
3107
- function assignMessageRefs(state, messages) {
3108
- let assigned = 0;
3109
- let skippedSubAgentPrompt = false;
3110
- for (const message of messages) {
3111
- if (isIgnoredUserMessage(message)) {
3112
- continue;
3113
- }
3114
- if (state.isSubAgent && !skippedSubAgentPrompt && message.info.role === "user") {
3115
- skippedSubAgentPrompt = true;
3116
- continue;
3117
- }
3118
- const rawMessageId = message.info.id;
3119
- if (typeof rawMessageId !== "string" || rawMessageId.length === 0) {
3120
- continue;
3121
- }
3122
- if (rawMessageId.startsWith("msg_dcp_summary_") || rawMessageId.startsWith("msg_dcp_text_")) {
3123
- continue;
3124
- }
3125
- const existingRef = state.messageIds.byRawId.get(rawMessageId);
3126
- if (existingRef) {
3127
- if (state.messageIds.byRef.get(existingRef) !== rawMessageId) {
3128
- state.messageIds.byRef.set(existingRef, rawMessageId);
3129
- }
3130
- continue;
3131
- }
3132
- const ref = allocateNextMessageRef(state);
3133
- state.messageIds.byRawId.set(rawMessageId, ref);
3134
- state.messageIds.byRef.set(ref, rawMessageId);
3135
- assigned++;
3136
- }
3137
- return assigned;
3138
- }
3139
- function allocateNextMessageRef(state) {
3140
- let candidate = Number.isInteger(state.messageIds.nextRef) ? Math.max(MESSAGE_REF_MIN_INDEX, state.messageIds.nextRef) : MESSAGE_REF_MIN_INDEX;
3141
- while (candidate <= MESSAGE_REF_MAX_INDEX) {
3142
- const ref = formatMessageRef(candidate);
3143
- if (!state.messageIds.byRef.has(ref)) {
3144
- state.messageIds.nextRef = candidate + 1;
3145
- return ref;
3146
- }
3147
- candidate++;
3148
- }
3149
- throw new Error(
3150
- `Message ID alias capacity exceeded. Cannot allocate more than ${formatMessageRef(MESSAGE_REF_MAX_INDEX)} aliases in this session.`
3151
- );
3152
- }
3153
-
3154
3148
  // lib/compress/state.ts
3155
3149
  var DEFAULT_PROMOTION_THRESHOLD = 5;
3156
3150
  var COMPRESSED_BLOCK_HEADER = "[Compressed conversation section]";
@@ -4622,7 +4616,11 @@ function restoreForkCompressionState(state, forkMessages, parent, parentMessages
4622
4616
  directMessageIds,
4623
4617
  directToolIds,
4624
4618
  effectiveMessageIds,
4625
- effectiveToolIds
4619
+ effectiveToolIds,
4620
+ // [Issue #415] The parent record is raw persisted JSON; legacy
4621
+ // 4-digit boundary refs must be normalized like every other ref.
4622
+ startId: typeof block.startId === "string" ? migrateMessageRef(block.startId) : block.startId,
4623
+ endId: typeof block.endId === "string" ? migrateMessageRef(block.endId) : block.endId
4626
4624
  });
4627
4625
  }
4628
4626
  const activeParentIds = new Set(parent.prune.messages.activeBlockIds);
@@ -5035,22 +5033,6 @@ async function ensureSessionInitialized(client, state, sessionId, logger, messag
5035
5033
  if (state.sessionId === sessionId) {
5036
5034
  return;
5037
5035
  }
5038
- try {
5039
- await runSessionInitialization(
5040
- client,
5041
- state,
5042
- sessionId,
5043
- logger,
5044
- messages,
5045
- config,
5046
- projectDir
5047
- );
5048
- } catch (error) {
5049
- state.sessionId = null;
5050
- throw error;
5051
- }
5052
- }
5053
- async function runSessionInitialization(client, state, sessionId, logger, messages, config, projectDir) {
5054
5036
  resetSessionState(state);
5055
5037
  state.sessionId = sessionId;
5056
5038
  state.storageDir = config?.storagePath ? resolveStorageDir(config.storagePath, projectDir ?? cwd()) : void 0;
@@ -9906,7 +9888,9 @@ function isLiveBlock(block) {
9906
9888
  return block.active && !block.deactivatedByUser && !block.deactivatedByUserDeep;
9907
9889
  }
9908
9890
  function rangeKey(startId, endId) {
9909
- return `${startId}::${endId}`;
9891
+ const s = typeof startId === "string" ? migrateMessageRef(startId) : startId;
9892
+ const e = typeof endId === "string" ? migrateMessageRef(endId) : endId;
9893
+ return `${s}::${e}`;
9910
9894
  }
9911
9895
  function rewriteCompressInput(part, liveKeys) {
9912
9896
  if (part.type !== "tool") return null;
@@ -10572,7 +10556,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
10572
10556
  import { join as join4 } from "path";
10573
10557
  import { existsSync as existsSync4 } from "fs";
10574
10558
  import { homedir as homedir3 } from "os";
10575
- var LOG_VERSION = true ? "1.18.1-pr.412.173" : "dev";
10559
+ var LOG_VERSION = true ? "1.18.1-pr.416.175" : "dev";
10576
10560
  var LEVEL_RANK = {
10577
10561
  debug: 10,
10578
10562
  info: 20,
@@ -13256,7 +13240,7 @@ var server = (async (ctx) => {
13256
13240
  }
13257
13241
  const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
13258
13242
  logger.info("ACP plugin initialized", {
13259
- version: true ? "1.18.1-pr.412.173" : "dev",
13243
+ version: true ? "1.18.1-pr.416.175" : "dev",
13260
13244
  workspace: ctx.directory,
13261
13245
  logLevel: logger.level,
13262
13246
  debug: config.debug,