opencode-acp 1.0.1 → 1.1.0

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
@@ -862,20 +862,7 @@ var ParseErrorCode;
862
862
  ParseErrorCode2[ParseErrorCode2["InvalidCharacter"] = 16] = "InvalidCharacter";
863
863
  })(ParseErrorCode || (ParseErrorCode = {}));
864
864
 
865
- // lib/config.ts
866
- var DEFAULT_PROTECTED_TOOLS = [
867
- "task",
868
- "skill",
869
- "todowrite",
870
- "todoread",
871
- "compress",
872
- "batch",
873
- "plan_enter",
874
- "plan_exit",
875
- "write",
876
- "edit"
877
- ];
878
- var COMPRESS_DEFAULT_PROTECTED_TOOLS = ["task", "skill", "todowrite", "todoread"];
865
+ // lib/config-validation.ts
879
866
  var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
880
867
  "$schema",
881
868
  "enabled",
@@ -1267,7 +1254,7 @@ function validateConfigTypes(config) {
1267
1254
  }
1268
1255
  if (gc.majorGcThresholdPercent !== void 0) {
1269
1256
  const isValidNumber = typeof gc.majorGcThresholdPercent === "number";
1270
- const isPercentString = typeof gc.majorGcThresholdPercent === "string" && gc.majorGcThresholdPercent.endsWith("%");
1257
+ const isPercentString = typeof gc.majorGcThresholdPercent === "string" && /^\d+(?:\.\d+)?%$/.test(gc.majorGcThresholdPercent);
1271
1258
  if (!isValidNumber && !isPercentString) {
1272
1259
  errors.push({
1273
1260
  key: "gc.majorGcThresholdPercent",
@@ -1279,54 +1266,97 @@ function validateConfigTypes(config) {
1279
1266
  }
1280
1267
  }
1281
1268
  const strategies = config.strategies;
1282
- if (strategies) {
1283
- if (strategies.deduplication?.enabled !== void 0 && typeof strategies.deduplication.enabled !== "boolean") {
1269
+ if (strategies !== void 0) {
1270
+ if (typeof strategies !== "object" || strategies === null || Array.isArray(strategies)) {
1284
1271
  errors.push({
1285
- key: "strategies.deduplication.enabled",
1286
- expected: "boolean",
1287
- actual: typeof strategies.deduplication.enabled
1288
- });
1289
- }
1290
- if (strategies.deduplication?.protectedTools !== void 0 && !Array.isArray(strategies.deduplication.protectedTools)) {
1291
- errors.push({
1292
- key: "strategies.deduplication.protectedTools",
1293
- expected: "string[]",
1294
- actual: typeof strategies.deduplication.protectedTools
1272
+ key: "strategies",
1273
+ expected: "object",
1274
+ actual: typeof strategies
1295
1275
  });
1296
- }
1297
- if (strategies.purgeErrors) {
1298
- if (strategies.purgeErrors.enabled !== void 0 && typeof strategies.purgeErrors.enabled !== "boolean") {
1299
- errors.push({
1300
- key: "strategies.purgeErrors.enabled",
1301
- expected: "boolean",
1302
- actual: typeof strategies.purgeErrors.enabled
1303
- });
1304
- }
1305
- if (strategies.purgeErrors.turns !== void 0 && typeof strategies.purgeErrors.turns !== "number") {
1306
- errors.push({
1307
- key: "strategies.purgeErrors.turns",
1308
- expected: "number",
1309
- actual: typeof strategies.purgeErrors.turns
1310
- });
1311
- }
1312
- if (typeof strategies.purgeErrors.turns === "number" && strategies.purgeErrors.turns < 1) {
1313
- errors.push({
1314
- key: "strategies.purgeErrors.turns",
1315
- expected: "positive number (>= 1)",
1316
- actual: `${strategies.purgeErrors.turns} (will be clamped to 1)`
1317
- });
1276
+ } else {
1277
+ const dedup = strategies.deduplication;
1278
+ if (dedup !== void 0) {
1279
+ if (typeof dedup !== "object" || dedup === null || Array.isArray(dedup)) {
1280
+ errors.push({
1281
+ key: "strategies.deduplication",
1282
+ expected: "object",
1283
+ actual: typeof dedup
1284
+ });
1285
+ } else {
1286
+ if (dedup.enabled !== void 0 && typeof dedup.enabled !== "boolean") {
1287
+ errors.push({
1288
+ key: "strategies.deduplication.enabled",
1289
+ expected: "boolean",
1290
+ actual: typeof dedup.enabled
1291
+ });
1292
+ }
1293
+ if (dedup.protectedTools !== void 0 && !Array.isArray(dedup.protectedTools)) {
1294
+ errors.push({
1295
+ key: "strategies.deduplication.protectedTools",
1296
+ expected: "string[]",
1297
+ actual: typeof dedup.protectedTools
1298
+ });
1299
+ }
1300
+ }
1318
1301
  }
1319
- if (strategies.purgeErrors.protectedTools !== void 0 && !Array.isArray(strategies.purgeErrors.protectedTools)) {
1320
- errors.push({
1321
- key: "strategies.purgeErrors.protectedTools",
1322
- expected: "string[]",
1323
- actual: typeof strategies.purgeErrors.protectedTools
1324
- });
1302
+ const purge = strategies.purgeErrors;
1303
+ if (purge !== void 0) {
1304
+ if (typeof purge !== "object" || purge === null || Array.isArray(purge)) {
1305
+ errors.push({
1306
+ key: "strategies.purgeErrors",
1307
+ expected: "object",
1308
+ actual: typeof purge
1309
+ });
1310
+ } else {
1311
+ if (purge.enabled !== void 0 && typeof purge.enabled !== "boolean") {
1312
+ errors.push({
1313
+ key: "strategies.purgeErrors.enabled",
1314
+ expected: "boolean",
1315
+ actual: typeof purge.enabled
1316
+ });
1317
+ }
1318
+ if (purge.turns !== void 0 && typeof purge.turns !== "number") {
1319
+ errors.push({
1320
+ key: "strategies.purgeErrors.turns",
1321
+ expected: "number",
1322
+ actual: typeof purge.turns
1323
+ });
1324
+ }
1325
+ if (typeof purge.turns === "number" && purge.turns < 1) {
1326
+ errors.push({
1327
+ key: "strategies.purgeErrors.turns",
1328
+ expected: "positive number (>= 1)",
1329
+ actual: `${purge.turns} (will be clamped to 1)`
1330
+ });
1331
+ }
1332
+ if (purge.protectedTools !== void 0 && !Array.isArray(purge.protectedTools)) {
1333
+ errors.push({
1334
+ key: "strategies.purgeErrors.protectedTools",
1335
+ expected: "string[]",
1336
+ actual: typeof purge.protectedTools
1337
+ });
1338
+ }
1339
+ }
1325
1340
  }
1326
1341
  }
1327
1342
  }
1328
1343
  return errors;
1329
1344
  }
1345
+
1346
+ // lib/config.ts
1347
+ var DEFAULT_PROTECTED_TOOLS = [
1348
+ "task",
1349
+ "skill",
1350
+ "todowrite",
1351
+ "todoread",
1352
+ "compress",
1353
+ "batch",
1354
+ "plan_enter",
1355
+ "plan_exit",
1356
+ "write",
1357
+ "edit"
1358
+ ];
1359
+ var COMPRESS_DEFAULT_PROTECTED_TOOLS = ["task", "skill", "todowrite", "todoread"];
1330
1360
  function showConfigWarnings(ctx, configPath, configData, isProject) {
1331
1361
  const invalidKeys = getInvalidConfigKeys(configData);
1332
1362
  const typeErrors = validateConfigTypes(configData);
@@ -1389,10 +1419,10 @@ var defaultConfig = {
1389
1419
  compress: {
1390
1420
  mode: "range",
1391
1421
  permission: "allow",
1392
- showCompression: false,
1422
+ showCompression: true,
1393
1423
  summaryBuffer: true,
1394
- maxContextLimit: 1e5,
1395
- minContextLimit: 5e4,
1424
+ maxContextLimit: "55%",
1425
+ minContextLimit: "45%",
1396
1426
  nudgeFrequency: 5,
1397
1427
  iterationNudgeThreshold: 15,
1398
1428
  nudgeForce: "soft",
@@ -1416,7 +1446,7 @@ var defaultConfig = {
1416
1446
  promotionThreshold: 5,
1417
1447
  maxBlockAge: 15,
1418
1448
  maxOldGenSummaryLength: 3e3,
1419
- majorGcThresholdPercent: "55%"
1449
+ majorGcThresholdPercent: "100%"
1420
1450
  }
1421
1451
  };
1422
1452
  var GLOBAL_CONFIG_DIR = process.env.XDG_CONFIG_HOME ? join(process.env.XDG_CONFIG_HOME, "opencode") : join(homedir(), ".config", "opencode");
@@ -1901,8 +1931,8 @@ THE FORMAT OF COMPRESS
1901
1931
  topic: string, // Short label (3-5 words) - e.g., "Auth System Exploration"
1902
1932
  content: [ // One or more ranges to compress
1903
1933
  {
1904
- startId: string, // Boundary ID at range start: mNNNN or bN
1905
- endId: string, // Boundary ID at range end: mNNNN or bN
1934
+ startId: string, // Boundary ID at range start: mNNNNN or bN
1935
+ endId: string, // Boundary ID at range end: mNNNNN or bN
1906
1936
  summary: string // Complete technical summary replacing all content in range
1907
1937
  }
1908
1938
  ]
@@ -1916,7 +1946,7 @@ THE FORMAT OF COMPRESS
1916
1946
  topic: string, // Short label (3-5 words) for the overall batch
1917
1947
  content: [ // One or more messages to compress independently
1918
1948
  {
1919
- messageId: string, // Raw message ID only: mNNNN (ignore metadata attributes like priority)
1949
+ messageId: string, // Raw message ID only: mNNNNN (ignore metadata attributes like priority)
1920
1950
  topic: string, // Short label (3-5 words) for this one message summary
1921
1951
  summary: string // Complete technical summary replacing that one message
1922
1952
  }
@@ -1925,16 +1955,16 @@ THE FORMAT OF COMPRESS
1925
1955
  \`\`\``;
1926
1956
 
1927
1957
  // lib/message-ids.ts
1928
- var MESSAGE_REF_REGEX = /^m(\d{4})$/;
1958
+ var MESSAGE_REF_REGEX = /^m(\d{4,5})$/;
1929
1959
  var BLOCK_REF_REGEX = /^b([1-9]\d*)$/;
1930
1960
  var MESSAGE_ID_TAG_NAME = "dcp-message-id";
1931
- var MESSAGE_REF_WIDTH = 4;
1961
+ var MESSAGE_REF_WIDTH = 5;
1932
1962
  var MESSAGE_REF_MIN_INDEX = 1;
1933
- var MESSAGE_REF_MAX_INDEX = 9999;
1963
+ var MESSAGE_REF_MAX_INDEX = 99999;
1934
1964
  function formatMessageRef(index) {
1935
1965
  if (!Number.isInteger(index) || index < MESSAGE_REF_MIN_INDEX || index > MESSAGE_REF_MAX_INDEX) {
1936
1966
  throw new Error(
1937
- `Message ID index out of bounds: ${index}. Supported range is 0-${MESSAGE_REF_MAX_INDEX}.`
1967
+ `Message ID index out of bounds: ${index}. Supported range is ${MESSAGE_REF_MIN_INDEX}-${MESSAGE_REF_MAX_INDEX}.`
1938
1968
  );
1939
1969
  }
1940
1970
  return `m${index.toString().padStart(MESSAGE_REF_WIDTH, "0")}`;
@@ -2089,10 +2119,10 @@ function resolveBoundaryIds(context, state, startId, endId) {
2089
2119
  const parsedStartId = parseBoundaryId(startId);
2090
2120
  const parsedEndId = parseBoundaryId(endId);
2091
2121
  if (parsedStartId === null) {
2092
- issues.push("startId is invalid. Use an injected message ID (mNNNN) or block ID (bN).");
2122
+ issues.push("startId is invalid. Use an injected message ID (mNNNNN) or block ID (bN).");
2093
2123
  }
2094
2124
  if (parsedEndId === null) {
2095
- issues.push("endId is invalid. Use an injected message ID (mNNNN) or block ID (bN).");
2125
+ issues.push("endId is invalid. Use an injected message ID (mNNNNN) or block ID (bN).");
2096
2126
  }
2097
2127
  if (issues.length > 0) {
2098
2128
  throw new Error(
@@ -2547,16 +2577,16 @@ var ISSUE_TEMPLATES = {
2547
2577
  "refer to protected messages and cannot be compressed."
2548
2578
  ],
2549
2579
  "invalid-format": [
2550
- "is invalid. Use an injected raw message ID of the form mNNNN.",
2551
- "are invalid. Use injected raw message IDs of the form mNNNN."
2580
+ "is invalid. Use an injected raw message ID of the form mNNNNN.",
2581
+ "are invalid. Use injected raw message IDs of the form mNNNNN."
2552
2582
  ],
2553
2583
  "block-id": [
2554
- "is invalid here. Block IDs like bN are not allowed; use an mNNNN message ID instead.",
2555
- "are invalid here. Block IDs like bN are not allowed; use mNNNN message IDs instead."
2584
+ "is invalid here. Block IDs like bN are not allowed; use an mNNNNN message ID instead.",
2585
+ "are invalid here. Block IDs like bN are not allowed; use mNNNNN message IDs instead."
2556
2586
  ],
2557
2587
  "not-in-context": [
2558
- "is not available in the current conversation context. Choose an injected mNNNN ID visible in context.",
2559
- "are not available in the current conversation context. Choose injected mNNNN IDs visible in context."
2588
+ "is not available in the current conversation context. Choose an injected mNNNNN ID visible in context.",
2589
+ "are not available in the current conversation context. Choose injected mNNNNN IDs visible in context."
2560
2590
  ],
2561
2591
  protected: [
2562
2592
  "refers to a protected message and cannot be compressed.",
@@ -2923,6 +2953,11 @@ function resetOnCompaction(state) {
2923
2953
  turnNudgeAnchors: /* @__PURE__ */ new Set(),
2924
2954
  iterationNudgeAnchors: /* @__PURE__ */ new Set()
2925
2955
  };
2956
+ state.messageIds = {
2957
+ byRawId: /* @__PURE__ */ new Map(),
2958
+ byRef: /* @__PURE__ */ new Map(),
2959
+ nextRef: 1
2960
+ };
2926
2961
  }
2927
2962
 
2928
2963
  // lib/state/persistence.ts
@@ -3295,6 +3330,17 @@ async function ensureSessionInitialized(client, state, sessionId, logger, messag
3295
3330
  state.messageIds.byRef.delete(ref);
3296
3331
  }
3297
3332
  }
3333
+ for (const [rawId, oldRef] of state.messageIds.byRawId) {
3334
+ const parsed = parseMessageRef(oldRef);
3335
+ if (parsed !== null) {
3336
+ const newRef = formatMessageRef(parsed);
3337
+ if (newRef !== oldRef) {
3338
+ state.messageIds.byRawId.set(rawId, newRef);
3339
+ state.messageIds.byRef.delete(oldRef);
3340
+ state.messageIds.byRef.set(newRef, rawId);
3341
+ }
3342
+ }
3343
+ }
3298
3344
  }
3299
3345
  if (persistedAny._persistedLastCompaction !== void 0) {
3300
3346
  state.lastCompaction = Math.max(state.lastCompaction, persistedAny._persistedLastCompaction);
@@ -4284,7 +4330,7 @@ function buildSchema() {
4284
4330
  ),
4285
4331
  content: tool.schema.array(
4286
4332
  tool.schema.object({
4287
- messageId: tool.schema.string().describe("Raw message ID to compress (e.g. m0001)"),
4333
+ messageId: tool.schema.string().describe("Raw message ID to compress (e.g. m00001)"),
4288
4334
  topic: tool.schema.string().describe("Short label (3-5 words) for this one message summary"),
4289
4335
  summary: tool.schema.string().describe("Complete technical summary replacing that one message")
4290
4336
  })
@@ -4521,9 +4567,9 @@ function buildSchema2() {
4521
4567
  content: tool2.schema.array(
4522
4568
  tool2.schema.object({
4523
4569
  startId: tool2.schema.string().describe(
4524
- "Message or block ID marking the beginning of range (e.g. m0001, b2)"
4570
+ "Message or block ID marking the beginning of range (e.g. m00001, b2)"
4525
4571
  ),
4526
- endId: tool2.schema.string().describe("Message or block ID marking the end of range (e.g. m0012, b5)"),
4572
+ endId: tool2.schema.string().describe("Message or block ID marking the end of range (e.g. m00012, b5)"),
4527
4573
  summary: tool2.schema.string().describe("Complete technical summary replacing all content in range")
4528
4574
  })
4529
4575
  ).describe(
@@ -4978,7 +5024,7 @@ Compressed block sections in context are clearly marked with a header:
4978
5024
 
4979
5025
  - \`[Compressed conversation section]\`
4980
5026
 
4981
- Compressed block IDs always use the \`bN\` form (never \`mNNNN\`) and are represented in the same XML metadata tag format.
5027
+ Compressed block IDs always use the \`bN\` form (never \`mNNNNN\`) and are represented in the same XML metadata tag format.
4982
5028
 
4983
5029
  Rules:
4984
5030
 
@@ -5001,7 +5047,7 @@ When you use compressed block placeholders, write the surrounding summary text s
5001
5047
  BOUNDARY IDS
5002
5048
  You specify boundaries by ID using the injected IDs visible in the conversation:
5003
5049
 
5004
- - \`mNNNN\` IDs identify raw messages
5050
+ - \`mNNNNN\` IDs identify raw messages
5005
5051
  - \`bN\` IDs identify previously compressed blocks
5006
5052
 
5007
5053
  Each message has an ID inside XML metadata tags like \`<dcp-message-id>...</dcp-message-id>\`.
@@ -5036,11 +5082,11 @@ If a message contains no significant technical decisions, code changes, or user
5036
5082
  MESSAGE IDS
5037
5083
  You specify individual raw messages by ID using the injected IDs visible in the conversation:
5038
5084
 
5039
- - \`mNNNN\` IDs identify raw messages
5085
+ - \`mNNNNN\` IDs identify raw messages
5040
5086
 
5041
5087
  Each message has an ID inside XML metadata tags like \`<dcp-message-id priority="high">m0007</dcp-message-id>\`.
5042
5088
  The same ID tag appears in every tool output of the message it belongs to \u2014 each unique ID identifies one complete message.
5043
- Treat these tags as message metadata only, not as content to summarize. Use only the inner \`mNNNN\` value as the \`messageId\`.
5089
+ Treat these tags as message metadata only, not as content to summarize. Use only the inner \`mNNNNN\` value as the \`messageId\`.
5044
5090
  The \`priority\` attribute indicates relative context cost. You MUST compress high-priority messages when their full text is no longer necessary for the active task.
5045
5091
  If prior compress-tool results are present, always compress and summarize them minimally only as part of a broader compression pass. Do not invoke the compress tool solely to re-compress an earlier compression result.
5046
5092
  Messages marked as \`<dcp-message-id>BLOCKED</dcp-message-id>\` cannot be compressed.
@@ -5048,8 +5094,8 @@ Messages marked as \`<dcp-message-id>BLOCKED</dcp-message-id>\` cannot be compre
5048
5094
  Rules:
5049
5095
 
5050
5096
  - Pick each \`messageId\` directly from injected IDs visible in context.
5051
- - Only use raw message IDs of the form \`mNNNN\`.
5052
- - Ignore XML attributes such as \`priority\` when copying the ID; use only the inner \`mNNNN\` value.
5097
+ - Only use raw message IDs of the form \`mNNNNN\`.
5098
+ - Ignore XML attributes such as \`priority\` when copying the ID; use only the inner \`mNNNNN\` value.
5053
5099
  - Do not invent IDs. Use only IDs that are present in context.
5054
5100
 
5055
5101
  BATCHING