opencode-acp 1.0.0 → 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/README.md +30 -13
- package/README.zh-CN.md +357 -0
- package/dist/index.js +194 -109
- package/dist/index.js.map +1 -1
- package/dist/lib/config-validation.d.ts +14 -0
- package/dist/lib/config-validation.d.ts.map +1 -0
- package/dist/lib/config.d.ts +1 -9
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/message-ids.d.ts +1 -1
- package/dist/lib/message-ids.d.ts.map +1 -1
- package/dist/lib/prompts/compress-message.d.ts +1 -1
- package/dist/lib/prompts/compress-message.d.ts.map +1 -1
- package/dist/lib/prompts/compress-range.d.ts +1 -1
- package/dist/lib/prompts/compress-range.d.ts.map +1 -1
- package/dist/lib/prompts/extensions/tool.d.ts +2 -2
- package/dist/lib/prompts/extensions/tool.d.ts.map +1 -1
- package/dist/lib/prompts/store.d.ts.map +1 -1
- package/dist/lib/state/state.d.ts.map +1 -1
- package/dist/lib/state/utils.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// lib/config.ts
|
|
2
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync } from "fs";
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync, copyFileSync } from "fs";
|
|
3
3
|
import { join, dirname } from "path";
|
|
4
4
|
import { homedir } from "os";
|
|
5
5
|
|
|
@@ -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
|
|
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
|
|
1284
|
-
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)) {
|
|
1269
|
+
if (strategies !== void 0) {
|
|
1270
|
+
if (typeof strategies !== "object" || strategies === null || Array.isArray(strategies)) {
|
|
1291
1271
|
errors.push({
|
|
1292
|
-
key: "strategies
|
|
1293
|
-
expected: "
|
|
1294
|
-
actual: typeof strategies
|
|
1272
|
+
key: "strategies",
|
|
1273
|
+
expected: "object",
|
|
1274
|
+
actual: typeof strategies
|
|
1295
1275
|
});
|
|
1296
|
-
}
|
|
1297
|
-
|
|
1298
|
-
if (
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
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
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
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:
|
|
1422
|
+
showCompression: true,
|
|
1393
1423
|
summaryBuffer: true,
|
|
1394
|
-
maxContextLimit:
|
|
1395
|
-
minContextLimit:
|
|
1424
|
+
maxContextLimit: "55%",
|
|
1425
|
+
minContextLimit: "45%",
|
|
1396
1426
|
nudgeFrequency: 5,
|
|
1397
1427
|
iterationNudgeThreshold: 15,
|
|
1398
1428
|
nudgeForce: "soft",
|
|
@@ -1416,12 +1446,14 @@ var defaultConfig = {
|
|
|
1416
1446
|
promotionThreshold: 5,
|
|
1417
1447
|
maxBlockAge: 15,
|
|
1418
1448
|
maxOldGenSummaryLength: 3e3,
|
|
1419
|
-
majorGcThresholdPercent: "
|
|
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");
|
|
1423
|
-
var GLOBAL_CONFIG_PATH_JSONC = join(GLOBAL_CONFIG_DIR, "
|
|
1424
|
-
var GLOBAL_CONFIG_PATH_JSON = join(GLOBAL_CONFIG_DIR, "
|
|
1453
|
+
var GLOBAL_CONFIG_PATH_JSONC = join(GLOBAL_CONFIG_DIR, "acp.jsonc");
|
|
1454
|
+
var GLOBAL_CONFIG_PATH_JSON = join(GLOBAL_CONFIG_DIR, "acp.json");
|
|
1455
|
+
var LEGACY_GLOBAL_CONFIG_PATH_JSONC = join(GLOBAL_CONFIG_DIR, "dcp.jsonc");
|
|
1456
|
+
var LEGACY_GLOBAL_CONFIG_PATH_JSON = join(GLOBAL_CONFIG_DIR, "dcp.json");
|
|
1425
1457
|
function findOpencodeDir(startDir) {
|
|
1426
1458
|
let current = startDir;
|
|
1427
1459
|
while (current !== "/") {
|
|
@@ -1438,21 +1470,25 @@ function findOpencodeDir(startDir) {
|
|
|
1438
1470
|
return null;
|
|
1439
1471
|
}
|
|
1440
1472
|
function getConfigPaths(ctx) {
|
|
1441
|
-
const global = existsSync(GLOBAL_CONFIG_PATH_JSONC) ? GLOBAL_CONFIG_PATH_JSONC : existsSync(GLOBAL_CONFIG_PATH_JSON) ? GLOBAL_CONFIG_PATH_JSON : null;
|
|
1473
|
+
const global = existsSync(GLOBAL_CONFIG_PATH_JSONC) ? GLOBAL_CONFIG_PATH_JSONC : existsSync(GLOBAL_CONFIG_PATH_JSON) ? GLOBAL_CONFIG_PATH_JSON : existsSync(LEGACY_GLOBAL_CONFIG_PATH_JSONC) ? LEGACY_GLOBAL_CONFIG_PATH_JSONC : existsSync(LEGACY_GLOBAL_CONFIG_PATH_JSON) ? LEGACY_GLOBAL_CONFIG_PATH_JSON : null;
|
|
1442
1474
|
let configDir = null;
|
|
1443
1475
|
const opencodeConfigDir = process.env.OPENCODE_CONFIG_DIR;
|
|
1444
1476
|
if (opencodeConfigDir) {
|
|
1445
|
-
const configJsonc = join(opencodeConfigDir, "
|
|
1446
|
-
const configJson = join(opencodeConfigDir, "
|
|
1447
|
-
|
|
1477
|
+
const configJsonc = join(opencodeConfigDir, "acp.jsonc");
|
|
1478
|
+
const configJson = join(opencodeConfigDir, "acp.json");
|
|
1479
|
+
const legacyJsonc = join(opencodeConfigDir, "dcp.jsonc");
|
|
1480
|
+
const legacyJson = join(opencodeConfigDir, "dcp.json");
|
|
1481
|
+
configDir = existsSync(configJsonc) ? configJsonc : existsSync(configJson) ? configJson : existsSync(legacyJsonc) ? legacyJsonc : existsSync(legacyJson) ? legacyJson : null;
|
|
1448
1482
|
}
|
|
1449
1483
|
let project = null;
|
|
1450
1484
|
if (ctx?.directory) {
|
|
1451
1485
|
const opencodeDir = findOpencodeDir(ctx.directory);
|
|
1452
1486
|
if (opencodeDir) {
|
|
1453
|
-
const projectJsonc = join(opencodeDir, "
|
|
1454
|
-
const projectJson = join(opencodeDir, "
|
|
1455
|
-
|
|
1487
|
+
const projectJsonc = join(opencodeDir, "acp.jsonc");
|
|
1488
|
+
const projectJson = join(opencodeDir, "acp.json");
|
|
1489
|
+
const legacyJsonc = join(opencodeDir, "dcp.jsonc");
|
|
1490
|
+
const legacyJson = join(opencodeDir, "dcp.json");
|
|
1491
|
+
project = existsSync(projectJsonc) ? projectJsonc : existsSync(projectJson) ? projectJson : existsSync(legacyJsonc) ? legacyJsonc : existsSync(legacyJson) ? legacyJson : null;
|
|
1456
1492
|
}
|
|
1457
1493
|
}
|
|
1458
1494
|
return { global, configDir, project };
|
|
@@ -1461,11 +1497,21 @@ function createDefaultConfig() {
|
|
|
1461
1497
|
if (!existsSync(GLOBAL_CONFIG_DIR)) {
|
|
1462
1498
|
mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
|
|
1463
1499
|
}
|
|
1464
|
-
|
|
1500
|
+
if (!existsSync(GLOBAL_CONFIG_PATH_JSONC)) {
|
|
1501
|
+
if (existsSync(LEGACY_GLOBAL_CONFIG_PATH_JSONC)) {
|
|
1502
|
+
copyFileSync(LEGACY_GLOBAL_CONFIG_PATH_JSONC, GLOBAL_CONFIG_PATH_JSONC);
|
|
1503
|
+
console.log("[ACP] Migrated config from dcp.jsonc to acp.jsonc");
|
|
1504
|
+
} else if (existsSync(LEGACY_GLOBAL_CONFIG_PATH_JSON)) {
|
|
1505
|
+
copyFileSync(LEGACY_GLOBAL_CONFIG_PATH_JSON, GLOBAL_CONFIG_PATH_JSONC);
|
|
1506
|
+
console.log("[ACP] Migrated config from dcp.json to acp.jsonc");
|
|
1507
|
+
} else {
|
|
1508
|
+
const configContent = `{
|
|
1465
1509
|
"$schema": "https://raw.githubusercontent.com/Opencode-DCP/opencode-dynamic-context-pruning/master/dcp.schema.json"
|
|
1466
1510
|
}
|
|
1467
1511
|
`;
|
|
1468
|
-
|
|
1512
|
+
writeFileSync(GLOBAL_CONFIG_PATH_JSONC, configContent, "utf-8");
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1469
1515
|
}
|
|
1470
1516
|
function loadConfigFile(configPath) {
|
|
1471
1517
|
let fileContent = "";
|
|
@@ -1627,7 +1673,21 @@ function scheduleParseWarning(ctx, title, message) {
|
|
|
1627
1673
|
function getConfig(ctx) {
|
|
1628
1674
|
let config = deepCloneConfig(defaultConfig);
|
|
1629
1675
|
const configPaths = getConfigPaths(ctx);
|
|
1630
|
-
if (!
|
|
1676
|
+
if (!existsSync(GLOBAL_CONFIG_PATH_JSONC) && !existsSync(GLOBAL_CONFIG_PATH_JSON)) {
|
|
1677
|
+
if (existsSync(GLOBAL_CONFIG_DIR) || existsSync(LEGACY_GLOBAL_CONFIG_PATH_JSONC) || existsSync(LEGACY_GLOBAL_CONFIG_PATH_JSON)) {
|
|
1678
|
+
if (!existsSync(GLOBAL_CONFIG_DIR)) {
|
|
1679
|
+
mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
|
|
1680
|
+
}
|
|
1681
|
+
if (existsSync(LEGACY_GLOBAL_CONFIG_PATH_JSONC)) {
|
|
1682
|
+
copyFileSync(LEGACY_GLOBAL_CONFIG_PATH_JSONC, GLOBAL_CONFIG_PATH_JSONC);
|
|
1683
|
+
console.log("[ACP] Migrated config from dcp.jsonc to acp.jsonc");
|
|
1684
|
+
} else if (existsSync(LEGACY_GLOBAL_CONFIG_PATH_JSON)) {
|
|
1685
|
+
copyFileSync(LEGACY_GLOBAL_CONFIG_PATH_JSON, GLOBAL_CONFIG_PATH_JSONC);
|
|
1686
|
+
console.log("[ACP] Migrated config from dcp.json to acp.jsonc");
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
if (!configPaths.global && !existsSync(GLOBAL_CONFIG_PATH_JSONC)) {
|
|
1631
1691
|
createDefaultConfig();
|
|
1632
1692
|
}
|
|
1633
1693
|
const layers = [
|
|
@@ -1871,8 +1931,8 @@ THE FORMAT OF COMPRESS
|
|
|
1871
1931
|
topic: string, // Short label (3-5 words) - e.g., "Auth System Exploration"
|
|
1872
1932
|
content: [ // One or more ranges to compress
|
|
1873
1933
|
{
|
|
1874
|
-
startId: string, // Boundary ID at range start:
|
|
1875
|
-
endId: string, // Boundary ID at range end:
|
|
1934
|
+
startId: string, // Boundary ID at range start: mNNNNN or bN
|
|
1935
|
+
endId: string, // Boundary ID at range end: mNNNNN or bN
|
|
1876
1936
|
summary: string // Complete technical summary replacing all content in range
|
|
1877
1937
|
}
|
|
1878
1938
|
]
|
|
@@ -1886,7 +1946,7 @@ THE FORMAT OF COMPRESS
|
|
|
1886
1946
|
topic: string, // Short label (3-5 words) for the overall batch
|
|
1887
1947
|
content: [ // One or more messages to compress independently
|
|
1888
1948
|
{
|
|
1889
|
-
messageId: string, // Raw message ID only:
|
|
1949
|
+
messageId: string, // Raw message ID only: mNNNNN (ignore metadata attributes like priority)
|
|
1890
1950
|
topic: string, // Short label (3-5 words) for this one message summary
|
|
1891
1951
|
summary: string // Complete technical summary replacing that one message
|
|
1892
1952
|
}
|
|
@@ -1895,16 +1955,16 @@ THE FORMAT OF COMPRESS
|
|
|
1895
1955
|
\`\`\``;
|
|
1896
1956
|
|
|
1897
1957
|
// lib/message-ids.ts
|
|
1898
|
-
var MESSAGE_REF_REGEX = /^m(\d{4})$/;
|
|
1958
|
+
var MESSAGE_REF_REGEX = /^m(\d{4,5})$/;
|
|
1899
1959
|
var BLOCK_REF_REGEX = /^b([1-9]\d*)$/;
|
|
1900
1960
|
var MESSAGE_ID_TAG_NAME = "dcp-message-id";
|
|
1901
|
-
var MESSAGE_REF_WIDTH =
|
|
1961
|
+
var MESSAGE_REF_WIDTH = 5;
|
|
1902
1962
|
var MESSAGE_REF_MIN_INDEX = 1;
|
|
1903
|
-
var MESSAGE_REF_MAX_INDEX =
|
|
1963
|
+
var MESSAGE_REF_MAX_INDEX = 99999;
|
|
1904
1964
|
function formatMessageRef(index) {
|
|
1905
1965
|
if (!Number.isInteger(index) || index < MESSAGE_REF_MIN_INDEX || index > MESSAGE_REF_MAX_INDEX) {
|
|
1906
1966
|
throw new Error(
|
|
1907
|
-
`Message ID index out of bounds: ${index}. Supported range is
|
|
1967
|
+
`Message ID index out of bounds: ${index}. Supported range is ${MESSAGE_REF_MIN_INDEX}-${MESSAGE_REF_MAX_INDEX}.`
|
|
1908
1968
|
);
|
|
1909
1969
|
}
|
|
1910
1970
|
return `m${index.toString().padStart(MESSAGE_REF_WIDTH, "0")}`;
|
|
@@ -2059,10 +2119,10 @@ function resolveBoundaryIds(context, state, startId, endId) {
|
|
|
2059
2119
|
const parsedStartId = parseBoundaryId(startId);
|
|
2060
2120
|
const parsedEndId = parseBoundaryId(endId);
|
|
2061
2121
|
if (parsedStartId === null) {
|
|
2062
|
-
issues.push("startId is invalid. Use an injected message ID (
|
|
2122
|
+
issues.push("startId is invalid. Use an injected message ID (mNNNNN) or block ID (bN).");
|
|
2063
2123
|
}
|
|
2064
2124
|
if (parsedEndId === null) {
|
|
2065
|
-
issues.push("endId is invalid. Use an injected message ID (
|
|
2125
|
+
issues.push("endId is invalid. Use an injected message ID (mNNNNN) or block ID (bN).");
|
|
2066
2126
|
}
|
|
2067
2127
|
if (issues.length > 0) {
|
|
2068
2128
|
throw new Error(
|
|
@@ -2517,16 +2577,16 @@ var ISSUE_TEMPLATES = {
|
|
|
2517
2577
|
"refer to protected messages and cannot be compressed."
|
|
2518
2578
|
],
|
|
2519
2579
|
"invalid-format": [
|
|
2520
|
-
"is invalid. Use an injected raw message ID of the form
|
|
2521
|
-
"are invalid. Use injected raw message IDs of the form
|
|
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."
|
|
2522
2582
|
],
|
|
2523
2583
|
"block-id": [
|
|
2524
|
-
"is invalid here. Block IDs like bN are not allowed; use an
|
|
2525
|
-
"are invalid here. Block IDs like bN are not allowed; use
|
|
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."
|
|
2526
2586
|
],
|
|
2527
2587
|
"not-in-context": [
|
|
2528
|
-
"is not available in the current conversation context. Choose an injected
|
|
2529
|
-
"are not available in the current conversation context. Choose injected
|
|
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."
|
|
2530
2590
|
],
|
|
2531
2591
|
protected: [
|
|
2532
2592
|
"refers to a protected message and cannot be compressed.",
|
|
@@ -2893,6 +2953,11 @@ function resetOnCompaction(state) {
|
|
|
2893
2953
|
turnNudgeAnchors: /* @__PURE__ */ new Set(),
|
|
2894
2954
|
iterationNudgeAnchors: /* @__PURE__ */ new Set()
|
|
2895
2955
|
};
|
|
2956
|
+
state.messageIds = {
|
|
2957
|
+
byRawId: /* @__PURE__ */ new Map(),
|
|
2958
|
+
byRef: /* @__PURE__ */ new Map(),
|
|
2959
|
+
nextRef: 1
|
|
2960
|
+
};
|
|
2896
2961
|
}
|
|
2897
2962
|
|
|
2898
2963
|
// lib/state/persistence.ts
|
|
@@ -2910,19 +2975,19 @@ var STORAGE_DIR = join2(
|
|
|
2910
2975
|
"plugin",
|
|
2911
2976
|
"acp"
|
|
2912
2977
|
);
|
|
2913
|
-
function migrateFromLegacyIfNeeded() {
|
|
2978
|
+
function migrateFromLegacyIfNeeded(logger) {
|
|
2914
2979
|
if (existsSyncSync(STORAGE_DIR)) return;
|
|
2915
2980
|
if (!existsSyncSync(LEGACY_STORAGE_DIR)) return;
|
|
2916
2981
|
try {
|
|
2917
2982
|
cpSync(LEGACY_STORAGE_DIR, STORAGE_DIR, { recursive: true });
|
|
2918
|
-
|
|
2983
|
+
logger.info(`[ACP] Migrated storage from ${LEGACY_STORAGE_DIR} \u2192 ${STORAGE_DIR}`);
|
|
2919
2984
|
} catch (e) {
|
|
2920
|
-
|
|
2985
|
+
logger.warn(`[ACP] Storage migration failed: ${e.message}`);
|
|
2921
2986
|
}
|
|
2922
2987
|
}
|
|
2923
|
-
async function ensureStorageDir() {
|
|
2988
|
+
async function ensureStorageDir(logger) {
|
|
2924
2989
|
if (!existsSync2(STORAGE_DIR)) {
|
|
2925
|
-
migrateFromLegacyIfNeeded();
|
|
2990
|
+
migrateFromLegacyIfNeeded(logger);
|
|
2926
2991
|
await fs.mkdir(STORAGE_DIR, { recursive: true });
|
|
2927
2992
|
}
|
|
2928
2993
|
}
|
|
@@ -2930,7 +2995,7 @@ function getSessionFilePath(sessionId) {
|
|
|
2930
2995
|
return join2(STORAGE_DIR, `${sessionId}.json`);
|
|
2931
2996
|
}
|
|
2932
2997
|
async function writePersistedSessionState(sessionId, state, logger) {
|
|
2933
|
-
await ensureStorageDir();
|
|
2998
|
+
await ensureStorageDir(logger);
|
|
2934
2999
|
const filePath = getSessionFilePath(sessionId);
|
|
2935
3000
|
const content = JSON.stringify(state, null, 2);
|
|
2936
3001
|
await fs.writeFile(filePath, content, "utf-8");
|
|
@@ -3265,6 +3330,17 @@ async function ensureSessionInitialized(client, state, sessionId, logger, messag
|
|
|
3265
3330
|
state.messageIds.byRef.delete(ref);
|
|
3266
3331
|
}
|
|
3267
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
|
+
}
|
|
3268
3344
|
}
|
|
3269
3345
|
if (persistedAny._persistedLastCompaction !== void 0) {
|
|
3270
3346
|
state.lastCompaction = Math.max(state.lastCompaction, persistedAny._persistedLastCompaction);
|
|
@@ -4254,7 +4330,7 @@ function buildSchema() {
|
|
|
4254
4330
|
),
|
|
4255
4331
|
content: tool.schema.array(
|
|
4256
4332
|
tool.schema.object({
|
|
4257
|
-
messageId: tool.schema.string().describe("Raw message ID to compress (e.g.
|
|
4333
|
+
messageId: tool.schema.string().describe("Raw message ID to compress (e.g. m00001)"),
|
|
4258
4334
|
topic: tool.schema.string().describe("Short label (3-5 words) for this one message summary"),
|
|
4259
4335
|
summary: tool.schema.string().describe("Complete technical summary replacing that one message")
|
|
4260
4336
|
})
|
|
@@ -4491,9 +4567,9 @@ function buildSchema2() {
|
|
|
4491
4567
|
content: tool2.schema.array(
|
|
4492
4568
|
tool2.schema.object({
|
|
4493
4569
|
startId: tool2.schema.string().describe(
|
|
4494
|
-
"Message or block ID marking the beginning of range (e.g.
|
|
4570
|
+
"Message or block ID marking the beginning of range (e.g. m00001, b2)"
|
|
4495
4571
|
),
|
|
4496
|
-
endId: tool2.schema.string().describe("Message or block ID marking the end of range (e.g.
|
|
4572
|
+
endId: tool2.schema.string().describe("Message or block ID marking the end of range (e.g. m00012, b5)"),
|
|
4497
4573
|
summary: tool2.schema.string().describe("Complete technical summary replacing all content in range")
|
|
4498
4574
|
})
|
|
4499
4575
|
).describe(
|
|
@@ -4888,7 +4964,7 @@ var Logger = class {
|
|
|
4888
4964
|
};
|
|
4889
4965
|
|
|
4890
4966
|
// lib/prompts/store.ts
|
|
4891
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, statSync as statSync2 } from "fs";
|
|
4967
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, statSync as statSync2, cpSync as cpSync2 } from "fs";
|
|
4892
4968
|
import { join as join4, dirname as dirname2 } from "path";
|
|
4893
4969
|
import { homedir as homedir4 } from "os";
|
|
4894
4970
|
|
|
@@ -4948,7 +5024,7 @@ Compressed block sections in context are clearly marked with a header:
|
|
|
4948
5024
|
|
|
4949
5025
|
- \`[Compressed conversation section]\`
|
|
4950
5026
|
|
|
4951
|
-
Compressed block IDs always use the \`bN\` form (never \`
|
|
5027
|
+
Compressed block IDs always use the \`bN\` form (never \`mNNNNN\`) and are represented in the same XML metadata tag format.
|
|
4952
5028
|
|
|
4953
5029
|
Rules:
|
|
4954
5030
|
|
|
@@ -4971,7 +5047,7 @@ When you use compressed block placeholders, write the surrounding summary text s
|
|
|
4971
5047
|
BOUNDARY IDS
|
|
4972
5048
|
You specify boundaries by ID using the injected IDs visible in the conversation:
|
|
4973
5049
|
|
|
4974
|
-
- \`
|
|
5050
|
+
- \`mNNNNN\` IDs identify raw messages
|
|
4975
5051
|
- \`bN\` IDs identify previously compressed blocks
|
|
4976
5052
|
|
|
4977
5053
|
Each message has an ID inside XML metadata tags like \`<dcp-message-id>...</dcp-message-id>\`.
|
|
@@ -5006,11 +5082,11 @@ If a message contains no significant technical decisions, code changes, or user
|
|
|
5006
5082
|
MESSAGE IDS
|
|
5007
5083
|
You specify individual raw messages by ID using the injected IDs visible in the conversation:
|
|
5008
5084
|
|
|
5009
|
-
- \`
|
|
5085
|
+
- \`mNNNNN\` IDs identify raw messages
|
|
5010
5086
|
|
|
5011
5087
|
Each message has an ID inside XML metadata tags like \`<dcp-message-id priority="high">m0007</dcp-message-id>\`.
|
|
5012
5088
|
The same ID tag appears in every tool output of the message it belongs to \u2014 each unique ID identifies one complete message.
|
|
5013
|
-
Treat these tags as message metadata only, not as content to summarize. Use only the inner \`
|
|
5089
|
+
Treat these tags as message metadata only, not as content to summarize. Use only the inner \`mNNNNN\` value as the \`messageId\`.
|
|
5014
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.
|
|
5015
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.
|
|
5016
5092
|
Messages marked as \`<dcp-message-id>BLOCKED</dcp-message-id>\` cannot be compressed.
|
|
@@ -5018,8 +5094,8 @@ Messages marked as \`<dcp-message-id>BLOCKED</dcp-message-id>\` cannot be compre
|
|
|
5018
5094
|
Rules:
|
|
5019
5095
|
|
|
5020
5096
|
- Pick each \`messageId\` directly from injected IDs visible in context.
|
|
5021
|
-
- Only use raw message IDs of the form \`
|
|
5022
|
-
- Ignore XML attributes such as \`priority\` when copying the ID; use only the inner \`
|
|
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.
|
|
5023
5099
|
- Do not invent IDs. Use only IDs that are present in context.
|
|
5024
5100
|
|
|
5025
5101
|
BATCHING
|
|
@@ -5228,12 +5304,21 @@ function findOpencodeDir2(startDir) {
|
|
|
5228
5304
|
}
|
|
5229
5305
|
function resolvePromptPaths(workingDirectory) {
|
|
5230
5306
|
const configHome = process.env.XDG_CONFIG_HOME || join4(homedir4(), ".config");
|
|
5231
|
-
const globalRoot = join4(configHome, "opencode", "
|
|
5307
|
+
const globalRoot = join4(configHome, "opencode", "acp-prompts");
|
|
5308
|
+
const legacyGlobalRoot = join4(configHome, "opencode", "dcp-prompts");
|
|
5309
|
+
if (!existsSync4(globalRoot) && existsSync4(legacyGlobalRoot)) {
|
|
5310
|
+
try {
|
|
5311
|
+
cpSync2(legacyGlobalRoot, globalRoot, { recursive: true });
|
|
5312
|
+
console.log("[ACP] Migrated prompts from dcp-prompts to acp-prompts");
|
|
5313
|
+
} catch (e) {
|
|
5314
|
+
console.warn(`[ACP] Prompts migration failed: ${e.message}`);
|
|
5315
|
+
}
|
|
5316
|
+
}
|
|
5232
5317
|
const defaultsDir = join4(globalRoot, "defaults");
|
|
5233
5318
|
const globalOverridesDir = join4(globalRoot, "overrides");
|
|
5234
|
-
const configDirOverridesDir = process.env.OPENCODE_CONFIG_DIR ? join4(process.env.OPENCODE_CONFIG_DIR, "
|
|
5319
|
+
const configDirOverridesDir = process.env.OPENCODE_CONFIG_DIR ? join4(process.env.OPENCODE_CONFIG_DIR, "acp-prompts", "overrides") : null;
|
|
5235
5320
|
const opencodeDir = findOpencodeDir2(workingDirectory);
|
|
5236
|
-
const projectOverridesDir = opencodeDir ? join4(opencodeDir, "
|
|
5321
|
+
const projectOverridesDir = opencodeDir ? join4(opencodeDir, "acp-prompts", "overrides") : null;
|
|
5237
5322
|
return {
|
|
5238
5323
|
defaultsDir,
|
|
5239
5324
|
globalOverridesDir,
|
|
@@ -5319,9 +5404,9 @@ function buildDefaultsReadmeContent() {
|
|
|
5319
5404
|
);
|
|
5320
5405
|
lines.push("");
|
|
5321
5406
|
lines.push("Override precedence (highest first):");
|
|
5322
|
-
lines.push("1. `.opencode/
|
|
5323
|
-
lines.push("2. `$OPENCODE_CONFIG_DIR/
|
|
5324
|
-
lines.push("3. `~/.config/opencode/
|
|
5407
|
+
lines.push("1. `.opencode/acp-prompts/overrides/` (project)");
|
|
5408
|
+
lines.push("2. `$OPENCODE_CONFIG_DIR/acp-prompts/overrides/` (config dir)");
|
|
5409
|
+
lines.push("3. `~/.config/opencode/acp-prompts/overrides/` (global)");
|
|
5325
5410
|
lines.push("");
|
|
5326
5411
|
lines.push("## Prompt Files");
|
|
5327
5412
|
lines.push("");
|