opencode-acp 1.18.3 → 1.18.4-pr.480.215
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 +45 -34
- package/README.zh-CN.md +43 -33
- package/dist/index.js +287 -53
- package/dist/index.js.map +1 -1
- package/dist/lib/compress/image-archive.d.ts +43 -0
- package/dist/lib/compress/image-archive.d.ts.map +1 -0
- package/dist/lib/compress/range.d.ts.map +1 -1
- package/dist/lib/config-validation.d.ts.map +1 -1
- package/dist/lib/config.d.ts +36 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/messages/inject/utils.d.ts.map +1 -1
- package/dist/lib/state/persistence.d.ts +3 -1
- package/dist/lib/state/persistence.d.ts.map +1 -1
- package/dist/lib/state/state.d.ts.map +1 -1
- package/dist/lib/state/types.d.ts +24 -0
- package/dist/lib/state/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -916,6 +916,10 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
916
916
|
"compress.reasoning",
|
|
917
917
|
"compress.reasoning.drop",
|
|
918
918
|
"compress.reasoning.threshold",
|
|
919
|
+
"compress.images",
|
|
920
|
+
"compress.images.enabled",
|
|
921
|
+
"compress.images.minBytes",
|
|
922
|
+
"compress.images.ttlDays",
|
|
919
923
|
"compress.completionReserveTokens",
|
|
920
924
|
"gc",
|
|
921
925
|
"gc.algorithm",
|
|
@@ -1308,6 +1312,37 @@ function validateConfigTypes(config) {
|
|
|
1308
1312
|
}
|
|
1309
1313
|
}
|
|
1310
1314
|
}
|
|
1315
|
+
if (compress.images !== void 0) {
|
|
1316
|
+
if (typeof compress.images !== "object" || compress.images === null || Array.isArray(compress.images)) {
|
|
1317
|
+
errors.push({
|
|
1318
|
+
key: "compress.images",
|
|
1319
|
+
expected: "object { enabled?: boolean; minBytes?: integer (>= 0); ttlDays?: number (> 0) }",
|
|
1320
|
+
actual: JSON.stringify(compress.images)
|
|
1321
|
+
});
|
|
1322
|
+
} else {
|
|
1323
|
+
if (compress.images.enabled !== void 0 && typeof compress.images.enabled !== "boolean") {
|
|
1324
|
+
errors.push({
|
|
1325
|
+
key: "compress.images.enabled",
|
|
1326
|
+
expected: "boolean",
|
|
1327
|
+
actual: typeof compress.images.enabled
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1330
|
+
if (compress.images.minBytes !== void 0 && (typeof compress.images.minBytes !== "number" || !Number.isInteger(compress.images.minBytes) || compress.images.minBytes < 0)) {
|
|
1331
|
+
errors.push({
|
|
1332
|
+
key: "compress.images.minBytes",
|
|
1333
|
+
expected: "integer (>= 0)",
|
|
1334
|
+
actual: JSON.stringify(compress.images.minBytes)
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
if (compress.images.ttlDays !== void 0 && (typeof compress.images.ttlDays !== "number" || !Number.isFinite(compress.images.ttlDays) || compress.images.ttlDays <= 0)) {
|
|
1338
|
+
errors.push({
|
|
1339
|
+
key: "compress.images.ttlDays",
|
|
1340
|
+
expected: "number (> 0)",
|
|
1341
|
+
actual: JSON.stringify(compress.images.ttlDays)
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1311
1346
|
if (compress.completionReserveTokens !== void 0 && typeof compress.completionReserveTokens !== "number") {
|
|
1312
1347
|
errors.push({
|
|
1313
1348
|
key: "compress.completionReserveTokens",
|
|
@@ -1695,6 +1730,11 @@ var DEFAULT_COMPRESS_REASONING = {
|
|
|
1695
1730
|
drop: true,
|
|
1696
1731
|
threshold: 2048
|
|
1697
1732
|
};
|
|
1733
|
+
var DEFAULT_COMPRESS_IMAGES = {
|
|
1734
|
+
enabled: true,
|
|
1735
|
+
minBytes: 16384,
|
|
1736
|
+
ttlDays: 7
|
|
1737
|
+
};
|
|
1698
1738
|
var DEFAULT_PROTECTED_TOOLS = [
|
|
1699
1739
|
"task",
|
|
1700
1740
|
"skill",
|
|
@@ -1793,7 +1833,8 @@ var defaultConfig = {
|
|
|
1793
1833
|
preserveRecentMessages: 5,
|
|
1794
1834
|
preserveRecentTokens: 5e3,
|
|
1795
1835
|
preserveLastUserMessage: true,
|
|
1796
|
-
reasoning: { ...DEFAULT_COMPRESS_REASONING }
|
|
1836
|
+
reasoning: { ...DEFAULT_COMPRESS_REASONING },
|
|
1837
|
+
images: { ...DEFAULT_COMPRESS_IMAGES }
|
|
1797
1838
|
},
|
|
1798
1839
|
gc: {
|
|
1799
1840
|
algorithm: "truncate",
|
|
@@ -1971,6 +2012,11 @@ function mergeCompress(base, override) {
|
|
|
1971
2012
|
drop: override.reasoning?.drop ?? base.reasoning?.drop ?? DEFAULT_COMPRESS_REASONING.drop,
|
|
1972
2013
|
threshold: override.reasoning?.threshold ?? base.reasoning?.threshold ?? DEFAULT_COMPRESS_REASONING.threshold
|
|
1973
2014
|
},
|
|
2015
|
+
images: {
|
|
2016
|
+
enabled: override.images?.enabled ?? base.images?.enabled ?? DEFAULT_COMPRESS_IMAGES.enabled,
|
|
2017
|
+
minBytes: override.images?.minBytes ?? base.images?.minBytes ?? DEFAULT_COMPRESS_IMAGES.minBytes,
|
|
2018
|
+
ttlDays: override.images?.ttlDays ?? base.images?.ttlDays ?? DEFAULT_COMPRESS_IMAGES.ttlDays
|
|
2019
|
+
},
|
|
1974
2020
|
completionReserveTokens: override.completionReserveTokens ?? base.completionReserveTokens
|
|
1975
2021
|
};
|
|
1976
2022
|
}
|
|
@@ -2908,7 +2954,8 @@ function saveSessionState(sessionState, logger, sessionName) {
|
|
|
2908
2954
|
lastCompaction: sessionState.lastCompaction,
|
|
2909
2955
|
modelContextLimit: sessionState.modelContextLimit,
|
|
2910
2956
|
modelProviderID: sessionState.modelProviderID,
|
|
2911
|
-
modelID: sessionState.modelID
|
|
2957
|
+
modelID: sessionState.modelID,
|
|
2958
|
+
imageArchive: sessionState.imageArchive
|
|
2912
2959
|
};
|
|
2913
2960
|
const key = saveQueueKey(sessionState.sessionId, sessionState.storageDir);
|
|
2914
2961
|
let queue = saveQueues.get(key);
|
|
@@ -5075,7 +5122,9 @@ function createSessionState() {
|
|
|
5075
5122
|
systemPromptTokens: void 0,
|
|
5076
5123
|
storageDir: void 0,
|
|
5077
5124
|
qualityGateRetryPending: false,
|
|
5078
|
-
noContextLimitWarned: false
|
|
5125
|
+
noContextLimitWarned: false,
|
|
5126
|
+
imageArchive: {},
|
|
5127
|
+
imagePruneDone: false
|
|
5079
5128
|
};
|
|
5080
5129
|
}
|
|
5081
5130
|
function resetSessionState(state) {
|
|
@@ -5119,6 +5168,8 @@ function resetSessionState(state) {
|
|
|
5119
5168
|
state.storageDir = void 0;
|
|
5120
5169
|
state.qualityGateRetryPending = false;
|
|
5121
5170
|
state.noContextLimitWarned = false;
|
|
5171
|
+
state.imageArchive = {};
|
|
5172
|
+
state.imagePruneDone = false;
|
|
5122
5173
|
}
|
|
5123
5174
|
async function ensureSessionInitialized(client, state, sessionId, logger, messages, config, projectDir) {
|
|
5124
5175
|
if (state.sessionId === sessionId) {
|
|
@@ -5197,8 +5248,21 @@ async function ensureSessionInitialized(client, state, sessionId, logger, messag
|
|
|
5197
5248
|
state.nudges.compressBaselineSet = persisted.nudges.compressBaselineSet ?? false;
|
|
5198
5249
|
state.stats = {
|
|
5199
5250
|
pruneTokenCounter: persisted.stats?.pruneTokenCounter || 0,
|
|
5200
|
-
totalPruneTokens: persisted.stats?.totalPruneTokens || 0
|
|
5251
|
+
totalPruneTokens: persisted.stats?.totalPruneTokens || 0,
|
|
5252
|
+
imagesArchived: persisted.stats?.imagesArchived,
|
|
5253
|
+
imageBytesArchived: persisted.stats?.imageBytesArchived
|
|
5201
5254
|
};
|
|
5255
|
+
if (persisted.imageArchive && typeof persisted.imageArchive === "object") {
|
|
5256
|
+
for (const [rawId, entries] of Object.entries(persisted.imageArchive)) {
|
|
5257
|
+
if (!Array.isArray(entries)) continue;
|
|
5258
|
+
const valid = entries.filter(
|
|
5259
|
+
(e) => !!e && typeof e.path === "string" && typeof e.mediaType === "string" && typeof e.bytes === "number"
|
|
5260
|
+
);
|
|
5261
|
+
if (valid.length > 0) {
|
|
5262
|
+
state.imageArchive[rawId] = valid;
|
|
5263
|
+
}
|
|
5264
|
+
}
|
|
5265
|
+
}
|
|
5202
5266
|
const persistedAny = persisted;
|
|
5203
5267
|
if (persistedAny._persistedMessageIds) {
|
|
5204
5268
|
state.messageIds = {
|
|
@@ -6611,6 +6675,156 @@ function buildPhantomSkipNotice(identification) {
|
|
|
6611
6675
|
return `Skipped ${identification.phantomIndices.length} already-compressed ${noun} (${skippedNums}) \u2014 they are no-ops; the remaining entries were compressed.`;
|
|
6612
6676
|
}
|
|
6613
6677
|
|
|
6678
|
+
// lib/compress/image-archive.ts
|
|
6679
|
+
import { createHash as createHash2 } from "crypto";
|
|
6680
|
+
import {
|
|
6681
|
+
existsSync as existsSync4,
|
|
6682
|
+
mkdirSync as mkdirSync2,
|
|
6683
|
+
readdirSync,
|
|
6684
|
+
rmdirSync,
|
|
6685
|
+
statSync as statSync2,
|
|
6686
|
+
unlinkSync,
|
|
6687
|
+
writeFileSync as writeFileSync2
|
|
6688
|
+
} from "fs";
|
|
6689
|
+
import { join as join4 } from "path";
|
|
6690
|
+
var DATA_URL_REGEX = /^data:(image\/[a-z0-9.+-]+);base64,(.+)$/is;
|
|
6691
|
+
var IMAGE_EXTENSIONS = {
|
|
6692
|
+
"image/png": "png",
|
|
6693
|
+
"image/jpeg": "jpg",
|
|
6694
|
+
"image/jpg": "jpg",
|
|
6695
|
+
"image/gif": "gif",
|
|
6696
|
+
"image/webp": "webp",
|
|
6697
|
+
"image/avif": "avif",
|
|
6698
|
+
"image/bmp": "bmp"
|
|
6699
|
+
};
|
|
6700
|
+
function sha8(input) {
|
|
6701
|
+
return createHash2("sha256").update(input).digest("hex").slice(0, 8);
|
|
6702
|
+
}
|
|
6703
|
+
function parseImageDataUrl(url) {
|
|
6704
|
+
if (typeof url !== "string") return void 0;
|
|
6705
|
+
const match = DATA_URL_REGEX.exec(url);
|
|
6706
|
+
if (!match) return void 0;
|
|
6707
|
+
return { mediaType: match[1].toLowerCase(), base64: match[2] };
|
|
6708
|
+
}
|
|
6709
|
+
function extForMediaType(mediaType) {
|
|
6710
|
+
return IMAGE_EXTENSIONS[mediaType.toLowerCase()];
|
|
6711
|
+
}
|
|
6712
|
+
function archiveDirName(sessionId) {
|
|
6713
|
+
let name = sessionId.replace(/[^a-zA-Z0-9_-]/g, "-").slice(0, 100);
|
|
6714
|
+
if (name !== sessionId) {
|
|
6715
|
+
name = `${name}-${sha8(sessionId)}`;
|
|
6716
|
+
}
|
|
6717
|
+
return name || "-";
|
|
6718
|
+
}
|
|
6719
|
+
function archiveRoot(storageDir, sessionId) {
|
|
6720
|
+
return join4(storageDir ?? getDefaultStorageDir(), "images", archiveDirName(sessionId));
|
|
6721
|
+
}
|
|
6722
|
+
function archiveSessionImages(state, messages, images, logger) {
|
|
6723
|
+
if (!images.enabled || !state.sessionId) return 0;
|
|
6724
|
+
const root = archiveRoot(state.storageDir, state.sessionId);
|
|
6725
|
+
let archived = 0;
|
|
6726
|
+
for (const message of messages) {
|
|
6727
|
+
const rawId = message.info.id;
|
|
6728
|
+
if (!rawId) continue;
|
|
6729
|
+
const parts = Array.isArray(message.parts) ? message.parts : [];
|
|
6730
|
+
for (let partIndex = 0; partIndex < parts.length; partIndex++) {
|
|
6731
|
+
const part = parts[partIndex];
|
|
6732
|
+
if (part.type !== "file") continue;
|
|
6733
|
+
const parsed = parseImageDataUrl(part.url);
|
|
6734
|
+
if (!parsed) continue;
|
|
6735
|
+
const ext = extForMediaType(parsed.mediaType);
|
|
6736
|
+
if (!ext) continue;
|
|
6737
|
+
const fileName = `${sha8(rawId)}-p${partIndex}-${sha8(parsed.base64)}.${ext}`;
|
|
6738
|
+
const filePath = join4(root, fileName);
|
|
6739
|
+
const existing = state.imageArchive[rawId]?.find((e) => e.path === filePath);
|
|
6740
|
+
if (existing) continue;
|
|
6741
|
+
let bytes;
|
|
6742
|
+
try {
|
|
6743
|
+
if (existsSync4(filePath)) {
|
|
6744
|
+
bytes = statSync2(filePath).size;
|
|
6745
|
+
} else {
|
|
6746
|
+
const buffer = Buffer.from(parsed.base64, "base64");
|
|
6747
|
+
bytes = buffer.byteLength;
|
|
6748
|
+
if (bytes < images.minBytes) continue;
|
|
6749
|
+
mkdirSync2(root, { recursive: true });
|
|
6750
|
+
writeFileSync2(filePath, buffer, { mode: 384 });
|
|
6751
|
+
}
|
|
6752
|
+
} catch (error) {
|
|
6753
|
+
logger.warn(
|
|
6754
|
+
`[#478] failed to archive image ${filePath}: ${error instanceof Error ? error.message : String(error)}`
|
|
6755
|
+
);
|
|
6756
|
+
continue;
|
|
6757
|
+
}
|
|
6758
|
+
const entry = {
|
|
6759
|
+
path: filePath,
|
|
6760
|
+
mediaType: parsed.mediaType,
|
|
6761
|
+
bytes
|
|
6762
|
+
};
|
|
6763
|
+
if (!state.imageArchive[rawId]) {
|
|
6764
|
+
state.imageArchive[rawId] = [];
|
|
6765
|
+
}
|
|
6766
|
+
state.imageArchive[rawId].push(entry);
|
|
6767
|
+
state.stats.imagesArchived = (state.stats.imagesArchived ?? 0) + 1;
|
|
6768
|
+
state.stats.imageBytesArchived = (state.stats.imageBytesArchived ?? 0) + bytes;
|
|
6769
|
+
archived++;
|
|
6770
|
+
}
|
|
6771
|
+
}
|
|
6772
|
+
return archived;
|
|
6773
|
+
}
|
|
6774
|
+
function pruneImageArchive(state, images, logger) {
|
|
6775
|
+
if (state.imagePruneDone || !state.sessionId) return;
|
|
6776
|
+
state.imagePruneDone = true;
|
|
6777
|
+
try {
|
|
6778
|
+
const root = archiveRoot(state.storageDir, state.sessionId);
|
|
6779
|
+
if (!existsSync4(root)) return;
|
|
6780
|
+
const cutoff = Date.now() - images.ttlDays * 864e5;
|
|
6781
|
+
const prunedPaths = /* @__PURE__ */ new Set();
|
|
6782
|
+
for (const name of readdirSync(root)) {
|
|
6783
|
+
const filePath = join4(root, name);
|
|
6784
|
+
try {
|
|
6785
|
+
if (statSync2(filePath).mtimeMs < cutoff) {
|
|
6786
|
+
unlinkSync(filePath);
|
|
6787
|
+
prunedPaths.add(filePath);
|
|
6788
|
+
}
|
|
6789
|
+
} catch {
|
|
6790
|
+
}
|
|
6791
|
+
}
|
|
6792
|
+
if (prunedPaths.size > 0) {
|
|
6793
|
+
for (const [rawId, entries] of Object.entries(state.imageArchive)) {
|
|
6794
|
+
const kept = entries.filter((e) => !prunedPaths.has(e.path));
|
|
6795
|
+
if (kept.length === 0) {
|
|
6796
|
+
delete state.imageArchive[rawId];
|
|
6797
|
+
} else if (kept.length !== entries.length) {
|
|
6798
|
+
state.imageArchive[rawId] = kept;
|
|
6799
|
+
}
|
|
6800
|
+
}
|
|
6801
|
+
logger.info(`[#478] TTL sweep removed ${prunedPaths.size} expired archived image(s)`);
|
|
6802
|
+
}
|
|
6803
|
+
if (readdirSync(root).length === 0) {
|
|
6804
|
+
rmdirSync(root);
|
|
6805
|
+
}
|
|
6806
|
+
} catch (error) {
|
|
6807
|
+
logger.warn(
|
|
6808
|
+
`[#478] image TTL sweep failed: ${error instanceof Error ? error.message : String(error)}`
|
|
6809
|
+
);
|
|
6810
|
+
}
|
|
6811
|
+
}
|
|
6812
|
+
function appendArchivedImagePointers(summary, selection, state, enabled) {
|
|
6813
|
+
if (!enabled) return summary;
|
|
6814
|
+
const lines = [];
|
|
6815
|
+
for (const messageId of selection.messageIds) {
|
|
6816
|
+
const entries = state.imageArchive[messageId];
|
|
6817
|
+
if (!entries || entries.length === 0) continue;
|
|
6818
|
+
const ref = state.messageIds.byRawId.get(messageId) ?? messageId;
|
|
6819
|
+
for (const entry of entries) {
|
|
6820
|
+
lines.push(`- ${ref}: ${entry.path} (${entry.mediaType}, ${entry.bytes} bytes)`);
|
|
6821
|
+
}
|
|
6822
|
+
}
|
|
6823
|
+
if (lines.length === 0) return summary;
|
|
6824
|
+
const heading = "\n\nImages captured in this range were archived to disk (their pixels are NOT summarized). Re-read them with your file tools if you need the pixels again:";
|
|
6825
|
+
return summary + heading + "\n" + lines.join("\n");
|
|
6826
|
+
}
|
|
6827
|
+
|
|
6614
6828
|
// lib/compress/keep-markers.ts
|
|
6615
6829
|
var KEEP_REGEX = /\[\[KEEP:(m\d+)\]\]/g;
|
|
6616
6830
|
var REF_REGEX = /\[\[REF:(m\d+)\|([^\]]+)\]\]/g;
|
|
@@ -6785,12 +6999,12 @@ ${afterClose}`;
|
|
|
6785
6999
|
}
|
|
6786
7000
|
|
|
6787
7001
|
// lib/messages/utils.ts
|
|
6788
|
-
import { createHash as
|
|
7002
|
+
import { createHash as createHash3 } from "crypto";
|
|
6789
7003
|
var SUMMARY_ID_HASH_LENGTH = 16;
|
|
6790
7004
|
var DCP_PAIRED_TAG_REGEX = /<(?:dcp|acp)[^>]*>[\s\S]*?<\/(?:dcp|acp)[^>]*>/gi;
|
|
6791
7005
|
var DCP_UNPAIRED_TAG_REGEX = /<\/?(?:dcp|acp)[^>]*>/gi;
|
|
6792
7006
|
var generateStableId = (prefix, seed) => {
|
|
6793
|
-
const hash =
|
|
7007
|
+
const hash = createHash3("sha256").update(seed).digest("hex").slice(0, SUMMARY_ID_HASH_LENGTH);
|
|
6794
7008
|
return `${prefix}_${hash}`;
|
|
6795
7009
|
};
|
|
6796
7010
|
var createSyntheticMessage = (baseMessage, content, stableSeed, role = "user") => {
|
|
@@ -7203,7 +7417,8 @@ function resolveMinNudgeFloorTokens(config, modelContextLimit, providerId, model
|
|
|
7203
7417
|
}
|
|
7204
7418
|
var OVERRIDE_BLANKET_APPLY_EXCLUDE = /* @__PURE__ */ new Set([
|
|
7205
7419
|
"maxContextLimit",
|
|
7206
|
-
"reasoning"
|
|
7420
|
+
"reasoning",
|
|
7421
|
+
"images"
|
|
7207
7422
|
]);
|
|
7208
7423
|
function applyCompressOverrides(config, providerId, modelId) {
|
|
7209
7424
|
const overrides = resolveCompressOverrides(config, providerId, modelId);
|
|
@@ -8057,7 +8272,12 @@ function createCompressRangeTool(factoryCtx) {
|
|
|
8057
8272
|
entry: plan.entry,
|
|
8058
8273
|
selection: plan.selection,
|
|
8059
8274
|
anchorMessageId: plan.anchorMessageId,
|
|
8060
|
-
finalSummary:
|
|
8275
|
+
finalSummary: appendArchivedImagePointers(
|
|
8276
|
+
completedSummary.expandedSummary,
|
|
8277
|
+
plan.selection,
|
|
8278
|
+
ctx.state,
|
|
8279
|
+
ctx.config.compress.images?.enabled !== false
|
|
8280
|
+
),
|
|
8061
8281
|
consumedBlockIds: mergeConsumedBlockIds
|
|
8062
8282
|
});
|
|
8063
8283
|
}
|
|
@@ -11106,10 +11326,10 @@ function hideFailedCompressCalls(messages) {
|
|
|
11106
11326
|
|
|
11107
11327
|
// lib/logger.ts
|
|
11108
11328
|
import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
11109
|
-
import { join as
|
|
11110
|
-
import { existsSync as
|
|
11329
|
+
import { join as join5 } from "path";
|
|
11330
|
+
import { existsSync as existsSync5 } from "fs";
|
|
11111
11331
|
import { homedir as homedir3 } from "os";
|
|
11112
|
-
var LOG_VERSION = true ? "1.18.
|
|
11332
|
+
var LOG_VERSION = true ? "1.18.4-pr.480.215" : "dev";
|
|
11113
11333
|
var LEVEL_RANK = {
|
|
11114
11334
|
debug: 10,
|
|
11115
11335
|
info: 20,
|
|
@@ -11129,8 +11349,8 @@ var Logger = class {
|
|
|
11129
11349
|
constructor(enabled, level) {
|
|
11130
11350
|
const resolved = level !== void 0 && LEVEL_RANK[level] !== void 0 ? level : void 0;
|
|
11131
11351
|
this.level = resolved ?? (enabled ? "debug" : "warn");
|
|
11132
|
-
const configHome = process.env.XDG_CONFIG_HOME ||
|
|
11133
|
-
this.logDir =
|
|
11352
|
+
const configHome = process.env.XDG_CONFIG_HOME || join5(homedir3(), ".config");
|
|
11353
|
+
this.logDir = join5(configHome, "opencode", "logs", "acp");
|
|
11134
11354
|
}
|
|
11135
11355
|
/** Back-compat view of the old flag: true only at full debug verbosity. */
|
|
11136
11356
|
get enabled() {
|
|
@@ -11140,7 +11360,7 @@ var Logger = class {
|
|
|
11140
11360
|
return LEVEL_RANK[level] >= LEVEL_RANK[this.level];
|
|
11141
11361
|
}
|
|
11142
11362
|
async ensureLogDir() {
|
|
11143
|
-
if (!
|
|
11363
|
+
if (!existsSync5(this.logDir)) {
|
|
11144
11364
|
await mkdir2(this.logDir, { recursive: true });
|
|
11145
11365
|
}
|
|
11146
11366
|
}
|
|
@@ -11192,11 +11412,11 @@ var Logger = class {
|
|
|
11192
11412
|
const dataStr = this.formatData(data);
|
|
11193
11413
|
const logLine = `${timestamp} ${level.padEnd(5)} ${component}: ${message}${dataStr ? " | " + dataStr : ""} | v=${LOG_VERSION}
|
|
11194
11414
|
`;
|
|
11195
|
-
const dailyLogDir =
|
|
11196
|
-
if (!
|
|
11415
|
+
const dailyLogDir = join5(this.logDir, "daily");
|
|
11416
|
+
if (!existsSync5(dailyLogDir)) {
|
|
11197
11417
|
await mkdir2(dailyLogDir, { recursive: true });
|
|
11198
11418
|
}
|
|
11199
|
-
const logFile =
|
|
11419
|
+
const logFile = join5(dailyLogDir, `${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.log`);
|
|
11200
11420
|
await writeFile2(logFile, logLine, { flag: "a" });
|
|
11201
11421
|
} catch (error) {
|
|
11202
11422
|
}
|
|
@@ -11307,18 +11527,18 @@ var Logger = class {
|
|
|
11307
11527
|
async saveContext(sessionId, messages) {
|
|
11308
11528
|
if (this.level !== "debug") return;
|
|
11309
11529
|
try {
|
|
11310
|
-
const contextDir =
|
|
11311
|
-
if (!
|
|
11530
|
+
const contextDir = join5(this.logDir, "context", sessionId);
|
|
11531
|
+
if (!existsSync5(contextDir)) {
|
|
11312
11532
|
await mkdir2(contextDir, { recursive: true });
|
|
11313
11533
|
}
|
|
11314
11534
|
const minimized = this.minimizeForDebug(messages).filter(
|
|
11315
11535
|
(msg) => msg.parts && msg.parts.length > 0
|
|
11316
11536
|
);
|
|
11317
11537
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
11318
|
-
const contextFile =
|
|
11538
|
+
const contextFile = join5(contextDir, `${timestamp}.json`);
|
|
11319
11539
|
await writeFile2(contextFile, JSON.stringify(minimized, null, 2));
|
|
11320
|
-
const versionFile =
|
|
11321
|
-
if (!
|
|
11540
|
+
const versionFile = join5(contextDir, "_version");
|
|
11541
|
+
if (!existsSync5(versionFile)) {
|
|
11322
11542
|
await writeFile2(versionFile, LOG_VERSION);
|
|
11323
11543
|
}
|
|
11324
11544
|
} catch (error) {
|
|
@@ -11327,8 +11547,8 @@ var Logger = class {
|
|
|
11327
11547
|
};
|
|
11328
11548
|
|
|
11329
11549
|
// lib/prompts/store.ts
|
|
11330
|
-
import { existsSync as
|
|
11331
|
-
import { join as
|
|
11550
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync3, statSync as statSync3 } from "fs";
|
|
11551
|
+
import { join as join6, dirname as dirname2 } from "path";
|
|
11332
11552
|
import { homedir as homedir4 } from "os";
|
|
11333
11553
|
|
|
11334
11554
|
// lib/prompts/system.ts
|
|
@@ -11636,10 +11856,10 @@ function createBundledRuntimePrompts(candidatesEnabled) {
|
|
|
11636
11856
|
function findOpencodeDir2(startDir) {
|
|
11637
11857
|
let current = startDir;
|
|
11638
11858
|
while (current !== "/") {
|
|
11639
|
-
const candidate =
|
|
11640
|
-
if (
|
|
11859
|
+
const candidate = join6(current, ".opencode");
|
|
11860
|
+
if (existsSync6(candidate)) {
|
|
11641
11861
|
try {
|
|
11642
|
-
if (
|
|
11862
|
+
if (statSync3(candidate).isDirectory()) {
|
|
11643
11863
|
return candidate;
|
|
11644
11864
|
}
|
|
11645
11865
|
} catch {
|
|
@@ -11654,13 +11874,13 @@ function findOpencodeDir2(startDir) {
|
|
|
11654
11874
|
return null;
|
|
11655
11875
|
}
|
|
11656
11876
|
function resolvePromptPaths(workingDirectory) {
|
|
11657
|
-
const configHome = process.env.XDG_CONFIG_HOME ||
|
|
11658
|
-
const globalRoot =
|
|
11659
|
-
const defaultsDir =
|
|
11660
|
-
const globalOverridesDir =
|
|
11661
|
-
const configDirOverridesDir = process.env.OPENCODE_CONFIG_DIR ?
|
|
11877
|
+
const configHome = process.env.XDG_CONFIG_HOME || join6(homedir4(), ".config");
|
|
11878
|
+
const globalRoot = join6(configHome, "opencode", "acp-prompts");
|
|
11879
|
+
const defaultsDir = join6(globalRoot, "defaults");
|
|
11880
|
+
const globalOverridesDir = join6(globalRoot, "overrides");
|
|
11881
|
+
const configDirOverridesDir = process.env.OPENCODE_CONFIG_DIR ? join6(process.env.OPENCODE_CONFIG_DIR, "acp-prompts", "overrides") : null;
|
|
11662
11882
|
const opencodeDir = findOpencodeDir2(workingDirectory);
|
|
11663
|
-
const projectOverridesDir = opencodeDir ?
|
|
11883
|
+
const projectOverridesDir = opencodeDir ? join6(opencodeDir, "acp-prompts", "overrides") : null;
|
|
11664
11884
|
return {
|
|
11665
11885
|
defaultsDir,
|
|
11666
11886
|
globalOverridesDir,
|
|
@@ -11760,7 +11980,7 @@ function buildDefaultsReadmeContent() {
|
|
|
11760
11980
|
`;
|
|
11761
11981
|
}
|
|
11762
11982
|
function readFileIfExists(filePath) {
|
|
11763
|
-
if (!
|
|
11983
|
+
if (!existsSync6(filePath)) {
|
|
11764
11984
|
return null;
|
|
11765
11985
|
}
|
|
11766
11986
|
try {
|
|
@@ -11833,23 +12053,23 @@ var PromptStore = class {
|
|
|
11833
12053
|
const candidates = [];
|
|
11834
12054
|
if (this.paths.projectOverridesDir) {
|
|
11835
12055
|
candidates.push({
|
|
11836
|
-
path:
|
|
12056
|
+
path: join6(this.paths.projectOverridesDir, fileName)
|
|
11837
12057
|
});
|
|
11838
12058
|
}
|
|
11839
12059
|
if (this.paths.configDirOverridesDir) {
|
|
11840
12060
|
candidates.push({
|
|
11841
|
-
path:
|
|
12061
|
+
path: join6(this.paths.configDirOverridesDir, fileName)
|
|
11842
12062
|
});
|
|
11843
12063
|
}
|
|
11844
12064
|
candidates.push({
|
|
11845
|
-
path:
|
|
12065
|
+
path: join6(this.paths.globalOverridesDir, fileName)
|
|
11846
12066
|
});
|
|
11847
12067
|
return candidates;
|
|
11848
12068
|
}
|
|
11849
12069
|
ensureDefaultFiles() {
|
|
11850
12070
|
try {
|
|
11851
|
-
|
|
11852
|
-
|
|
12071
|
+
mkdirSync3(this.paths.defaultsDir, { recursive: true });
|
|
12072
|
+
mkdirSync3(this.paths.globalOverridesDir, { recursive: true });
|
|
11853
12073
|
} catch {
|
|
11854
12074
|
this.logger.warn("Failed to initialize prompt directories", {
|
|
11855
12075
|
defaultsDir: this.paths.defaultsDir,
|
|
@@ -11866,13 +12086,13 @@ var PromptStore = class {
|
|
|
11866
12086
|
const managedContent = buildDefaultPromptFileContent(
|
|
11867
12087
|
bundledEditable || bundledPrompts[definition.runtimeField]
|
|
11868
12088
|
);
|
|
11869
|
-
const filePath =
|
|
12089
|
+
const filePath = join6(this.paths.defaultsDir, definition.fileName);
|
|
11870
12090
|
try {
|
|
11871
12091
|
const existing = readFileIfExists(filePath);
|
|
11872
12092
|
if (existing === managedContent) {
|
|
11873
12093
|
continue;
|
|
11874
12094
|
}
|
|
11875
|
-
|
|
12095
|
+
writeFileSync3(filePath, managedContent, "utf-8");
|
|
11876
12096
|
} catch {
|
|
11877
12097
|
this.logger.warn("Failed to write default prompt file", {
|
|
11878
12098
|
key: definition.key,
|
|
@@ -11880,12 +12100,12 @@ var PromptStore = class {
|
|
|
11880
12100
|
});
|
|
11881
12101
|
}
|
|
11882
12102
|
}
|
|
11883
|
-
const readmePath =
|
|
12103
|
+
const readmePath = join6(this.paths.defaultsDir, DEFAULTS_README_FILE);
|
|
11884
12104
|
const readmeContent = buildDefaultsReadmeContent();
|
|
11885
12105
|
try {
|
|
11886
12106
|
const existing = readFileIfExists(readmePath);
|
|
11887
12107
|
if (existing !== readmeContent) {
|
|
11888
|
-
|
|
12108
|
+
writeFileSync3(readmePath, readmeContent, "utf-8");
|
|
11889
12109
|
}
|
|
11890
12110
|
} catch {
|
|
11891
12111
|
this.logger.warn("Failed to write defaults README", {
|
|
@@ -12353,8 +12573,8 @@ ${report}`;
|
|
|
12353
12573
|
|
|
12354
12574
|
// lib/commands/export.ts
|
|
12355
12575
|
import * as fs2 from "fs/promises";
|
|
12356
|
-
import { existsSync as
|
|
12357
|
-
import { dirname as dirname3, isAbsolute as isAbsolute2, join as
|
|
12576
|
+
import { existsSync as existsSync7 } from "fs";
|
|
12577
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join7, resolve } from "path";
|
|
12358
12578
|
var ALL_TIERS = [1, 2, 3];
|
|
12359
12579
|
var TIER_NAMES = {
|
|
12360
12580
|
1: "Tier 1 \u2014 Capture",
|
|
@@ -12467,7 +12687,7 @@ function parseTiers(raw) {
|
|
|
12467
12687
|
}
|
|
12468
12688
|
function resolveDefaultOutputPath(sessionId, cwd2) {
|
|
12469
12689
|
const short = (sessionId || "session").slice(0, 8);
|
|
12470
|
-
return
|
|
12690
|
+
return join7(cwd2, ".opencode", `acp-export-${short}.md`);
|
|
12471
12691
|
}
|
|
12472
12692
|
function formatTokens2(n) {
|
|
12473
12693
|
if (!Number.isFinite(n) || n <= 0) return "0";
|
|
@@ -12598,11 +12818,11 @@ async function handleExportCommand(ctx, args) {
|
|
|
12598
12818
|
const targetPath = options.outputPath !== "" ? isAbsolute2(options.outputPath) ? options.outputPath : resolve(cwd2, options.outputPath) : resolveDefaultOutputPath(sessionId, cwd2);
|
|
12599
12819
|
try {
|
|
12600
12820
|
const dir = dirname3(targetPath);
|
|
12601
|
-
if (!
|
|
12821
|
+
if (!existsSync7(dir)) {
|
|
12602
12822
|
await fs2.mkdir(dir, { recursive: true });
|
|
12603
12823
|
}
|
|
12604
12824
|
const flag = options.append ? "a" : "w";
|
|
12605
|
-
if (options.append &&
|
|
12825
|
+
if (options.append && existsSync7(targetPath)) {
|
|
12606
12826
|
await fs2.appendFile(targetPath, `
|
|
12607
12827
|
|
|
12608
12828
|
---
|
|
@@ -13317,6 +13537,20 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
|
|
|
13317
13537
|
});
|
|
13318
13538
|
cacheSystemPromptTokens(state, output.messages);
|
|
13319
13539
|
assignMessageRefs(state, output.messages);
|
|
13540
|
+
const imagesConfig = config.compress.images ?? DEFAULT_COMPRESS_IMAGES;
|
|
13541
|
+
if (imagesConfig.enabled) {
|
|
13542
|
+
const archivedImages = archiveSessionImages(
|
|
13543
|
+
state,
|
|
13544
|
+
output.messages,
|
|
13545
|
+
imagesConfig,
|
|
13546
|
+
logger
|
|
13547
|
+
);
|
|
13548
|
+
if (archivedImages > 0) {
|
|
13549
|
+
saveSessionState(state, logger).catch(() => {
|
|
13550
|
+
});
|
|
13551
|
+
}
|
|
13552
|
+
pruneImageArchive(state, imagesConfig, logger);
|
|
13553
|
+
}
|
|
13320
13554
|
const activeBlockCountBefore = state.prune.messages.activeBlockIds.size;
|
|
13321
13555
|
const compressionStateChanged = syncCompressionBlocks(state, logger, output.messages);
|
|
13322
13556
|
if (compressionStateChanged || state.prune.messages.activeBlockIds.size !== activeBlockCountBefore) {
|
|
@@ -13594,7 +13828,7 @@ function configureClientAuth(client) {
|
|
|
13594
13828
|
|
|
13595
13829
|
// lib/update.ts
|
|
13596
13830
|
import { readFile as readFile2, rm } from "fs/promises";
|
|
13597
|
-
import { basename, dirname as dirname4, join as
|
|
13831
|
+
import { basename, dirname as dirname4, join as join8 } from "path";
|
|
13598
13832
|
import { fileURLToPath } from "url";
|
|
13599
13833
|
var PACKAGE_NAME = "opencode-acp";
|
|
13600
13834
|
function startAutoUpdate(ctx, enabled, logger) {
|
|
@@ -13641,7 +13875,7 @@ async function checkAutoUpdate(signal, logger) {
|
|
|
13641
13875
|
logger?.info("Auto-update skipped: package dir not found");
|
|
13642
13876
|
return { updated: false };
|
|
13643
13877
|
}
|
|
13644
|
-
const pkg = await readPackageJson(
|
|
13878
|
+
const pkg = await readPackageJson(join8(packageDir, "package.json"));
|
|
13645
13879
|
if (!pkg?.name || !pkg.version) {
|
|
13646
13880
|
logger?.info("Auto-update skipped: package.json unreadable", { packageDir });
|
|
13647
13881
|
return { updated: false };
|
|
@@ -13682,7 +13916,7 @@ async function checkAutoUpdate(signal, logger) {
|
|
|
13682
13916
|
async function findPackageDir(name) {
|
|
13683
13917
|
let dir = dirname4(fileURLToPath(import.meta.url));
|
|
13684
13918
|
for (; ; ) {
|
|
13685
|
-
const pkg = await readPackageJson(
|
|
13919
|
+
const pkg = await readPackageJson(join8(dir, "package.json"));
|
|
13686
13920
|
if (pkg?.name === name) return dir;
|
|
13687
13921
|
const parent = dirname4(dir);
|
|
13688
13922
|
if (parent === dir) return void 0;
|
|
@@ -13694,7 +13928,7 @@ async function updateTarget(packageDir, name) {
|
|
|
13694
13928
|
const nodeModulesDir = basename(packageParent).startsWith("@") ? dirname4(packageParent) : packageParent;
|
|
13695
13929
|
if (basename(nodeModulesDir) !== "node_modules") return void 0;
|
|
13696
13930
|
const wrapperDir = dirname4(nodeModulesDir);
|
|
13697
|
-
const wrapperPkg = await readPackageJson(
|
|
13931
|
+
const wrapperPkg = await readPackageJson(join8(wrapperDir, "package.json"));
|
|
13698
13932
|
const spec = wrapperSpec(wrapperDir, name) ?? wrapperPkg?.dependencies?.[name];
|
|
13699
13933
|
if (!spec || !isAutoUpdatableSpec(spec)) return void 0;
|
|
13700
13934
|
return { removeDir: wrapperDir, spec };
|
|
@@ -13805,7 +14039,7 @@ var server = (async (ctx) => {
|
|
|
13805
14039
|
}
|
|
13806
14040
|
const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
|
|
13807
14041
|
logger.info("ACP plugin initialized", {
|
|
13808
|
-
version: true ? "1.18.
|
|
14042
|
+
version: true ? "1.18.4-pr.480.215" : "dev",
|
|
13809
14043
|
workspace: ctx.directory,
|
|
13810
14044
|
logLevel: logger.level,
|
|
13811
14045
|
debug: config.debug,
|