opencode-acp 1.18.4 → 1.18.5-pr.416.226
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 +527 -280
- package/dist/index.js.map +1 -1
- package/dist/lib/compress/decompress.d.ts.map +1 -1
- package/dist/lib/compress/hide-consumed.d.ts.map +1 -1
- package/dist/lib/compress/tofile-target.d.ts +31 -0
- package/dist/lib/compress/tofile-target.d.ts.map +1 -0
- package/dist/lib/config-validation.d.ts.map +1 -1
- package/dist/lib/config.d.ts +13 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/message-ids.d.ts +8 -0
- package/dist/lib/message-ids.d.ts.map +1 -1
- package/dist/lib/messages/sticky-truncation.d.ts +55 -0
- package/dist/lib/messages/sticky-truncation.d.ts.map +1 -0
- package/dist/lib/messages/truncate-tools.d.ts.map +1 -1
- package/dist/lib/state/persistence.d.ts +2 -0
- package/dist/lib/state/persistence.d.ts.map +1 -1
- package/dist/lib/state/rebuild.d.ts.map +1 -1
- package/dist/lib/state/state.d.ts.map +1 -1
- package/dist/lib/state/types.d.ts +10 -0
- package/dist/lib/state/types.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
|
@@ -921,6 +921,7 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
921
921
|
"compress.images.minBytes",
|
|
922
922
|
"compress.images.ttlDays",
|
|
923
923
|
"compress.completionReserveTokens",
|
|
924
|
+
"compress.emergencyTruncation",
|
|
924
925
|
"gc",
|
|
925
926
|
"gc.algorithm",
|
|
926
927
|
"gc.promotionThreshold",
|
|
@@ -1350,6 +1351,13 @@ function validateConfigTypes(config) {
|
|
|
1350
1351
|
actual: typeof compress.completionReserveTokens
|
|
1351
1352
|
});
|
|
1352
1353
|
}
|
|
1354
|
+
if (compress.emergencyTruncation !== void 0 && typeof compress.emergencyTruncation !== "boolean") {
|
|
1355
|
+
errors.push({
|
|
1356
|
+
key: "compress.emergencyTruncation",
|
|
1357
|
+
expected: "boolean",
|
|
1358
|
+
actual: typeof compress.emergencyTruncation
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1353
1361
|
if (typeof compress.completionReserveTokens === "number" && compress.completionReserveTokens < 0) {
|
|
1354
1362
|
errors.push({
|
|
1355
1363
|
key: "compress.completionReserveTokens",
|
|
@@ -1422,6 +1430,7 @@ function validateConfigTypes(config) {
|
|
|
1422
1430
|
const OVERRIDE_FIELD_TYPES = {
|
|
1423
1431
|
showCompression: "boolean",
|
|
1424
1432
|
summaryBuffer: "boolean",
|
|
1433
|
+
emergencyTruncation: "boolean",
|
|
1425
1434
|
protectTags: "boolean",
|
|
1426
1435
|
protectUserMessages: "boolean",
|
|
1427
1436
|
lastSegmentSoftBlock: "boolean",
|
|
@@ -1809,6 +1818,7 @@ var defaultConfig = {
|
|
|
1809
1818
|
permission: "allow",
|
|
1810
1819
|
showCompression: true,
|
|
1811
1820
|
summaryBuffer: true,
|
|
1821
|
+
emergencyTruncation: false,
|
|
1812
1822
|
candidates: false,
|
|
1813
1823
|
maxContextLimit: "80%",
|
|
1814
1824
|
minContextLimit: "80%",
|
|
@@ -2017,7 +2027,8 @@ function mergeCompress(base, override) {
|
|
|
2017
2027
|
minBytes: override.images?.minBytes ?? base.images?.minBytes ?? DEFAULT_COMPRESS_IMAGES.minBytes,
|
|
2018
2028
|
ttlDays: override.images?.ttlDays ?? base.images?.ttlDays ?? DEFAULT_COMPRESS_IMAGES.ttlDays
|
|
2019
2029
|
},
|
|
2020
|
-
completionReserveTokens: override.completionReserveTokens ?? base.completionReserveTokens
|
|
2030
|
+
completionReserveTokens: override.completionReserveTokens ?? base.completionReserveTokens,
|
|
2031
|
+
emergencyTruncation: override.emergencyTruncation ?? base.emergencyTruncation
|
|
2021
2032
|
};
|
|
2022
2033
|
}
|
|
2023
2034
|
function mergeCommands(base, override) {
|
|
@@ -2585,6 +2596,209 @@ import { existsSync as existsSync2 } from "fs";
|
|
|
2585
2596
|
import { homedir as homedir2 } from "os";
|
|
2586
2597
|
import { isAbsolute, join as join2 } from "path";
|
|
2587
2598
|
|
|
2599
|
+
// lib/message-ids.ts
|
|
2600
|
+
var MESSAGE_REF_REGEX = /^m(\d{4,5})$/;
|
|
2601
|
+
var BLOCK_REF_REGEX = /^b([1-9]\d*)$/;
|
|
2602
|
+
var MESSAGE_ID_TAG_NAME = "dcp-message-id";
|
|
2603
|
+
var MESSAGE_REF_WIDTH = 5;
|
|
2604
|
+
var MESSAGE_REF_MIN_INDEX = 1;
|
|
2605
|
+
var MESSAGE_REF_MAX_INDEX = 99999;
|
|
2606
|
+
function formatMessageRef(index) {
|
|
2607
|
+
if (!Number.isInteger(index) || index < MESSAGE_REF_MIN_INDEX || index > MESSAGE_REF_MAX_INDEX) {
|
|
2608
|
+
throw new Error(
|
|
2609
|
+
`Message ID index out of bounds: ${index}. Supported range is ${MESSAGE_REF_MIN_INDEX}-${MESSAGE_REF_MAX_INDEX}.`
|
|
2610
|
+
);
|
|
2611
|
+
}
|
|
2612
|
+
return `m${index.toString().padStart(MESSAGE_REF_WIDTH, "0")}`;
|
|
2613
|
+
}
|
|
2614
|
+
function formatBlockRef(blockId) {
|
|
2615
|
+
if (!Number.isInteger(blockId) || blockId < 1) {
|
|
2616
|
+
throw new Error(`Invalid block ID: ${blockId}`);
|
|
2617
|
+
}
|
|
2618
|
+
return `b${blockId}`;
|
|
2619
|
+
}
|
|
2620
|
+
function parseMessageRef(ref) {
|
|
2621
|
+
const normalized = ref.trim().toLowerCase();
|
|
2622
|
+
const match = normalized.match(MESSAGE_REF_REGEX);
|
|
2623
|
+
if (!match) {
|
|
2624
|
+
return null;
|
|
2625
|
+
}
|
|
2626
|
+
const index = Number.parseInt(match[1], 10);
|
|
2627
|
+
if (!Number.isInteger(index)) {
|
|
2628
|
+
return null;
|
|
2629
|
+
}
|
|
2630
|
+
if (index < MESSAGE_REF_MIN_INDEX || index > MESSAGE_REF_MAX_INDEX) {
|
|
2631
|
+
return null;
|
|
2632
|
+
}
|
|
2633
|
+
return index;
|
|
2634
|
+
}
|
|
2635
|
+
function migrateMessageRef(ref) {
|
|
2636
|
+
const index = parseMessageRef(ref);
|
|
2637
|
+
return index === null ? ref : formatMessageRef(index);
|
|
2638
|
+
}
|
|
2639
|
+
function parseBlockRef(ref) {
|
|
2640
|
+
const normalized = ref.trim().toLowerCase();
|
|
2641
|
+
const match = normalized.match(BLOCK_REF_REGEX);
|
|
2642
|
+
if (!match) {
|
|
2643
|
+
return null;
|
|
2644
|
+
}
|
|
2645
|
+
const id = Number.parseInt(match[1], 10);
|
|
2646
|
+
return Number.isInteger(id) ? id : null;
|
|
2647
|
+
}
|
|
2648
|
+
function parseBoundaryId(id) {
|
|
2649
|
+
const normalized = id.trim().toLowerCase();
|
|
2650
|
+
const messageIndex = parseMessageRef(normalized);
|
|
2651
|
+
if (messageIndex !== null) {
|
|
2652
|
+
return {
|
|
2653
|
+
kind: "message",
|
|
2654
|
+
ref: formatMessageRef(messageIndex),
|
|
2655
|
+
index: messageIndex
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2658
|
+
const blockId = parseBlockRef(normalized);
|
|
2659
|
+
if (blockId !== null) {
|
|
2660
|
+
return {
|
|
2661
|
+
kind: "compressed-block",
|
|
2662
|
+
ref: formatBlockRef(blockId),
|
|
2663
|
+
blockId
|
|
2664
|
+
};
|
|
2665
|
+
}
|
|
2666
|
+
return null;
|
|
2667
|
+
}
|
|
2668
|
+
function escapeXmlAttribute(value) {
|
|
2669
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
2670
|
+
}
|
|
2671
|
+
function formatMessageIdTag(ref, attributes) {
|
|
2672
|
+
const serializedAttributes = Object.entries(attributes || {}).sort(([left], [right]) => left.localeCompare(right)).map(([name, value]) => {
|
|
2673
|
+
if (name.trim().length === 0 || typeof value !== "string" || value.length === 0) {
|
|
2674
|
+
return "";
|
|
2675
|
+
}
|
|
2676
|
+
return ` ${name}="${escapeXmlAttribute(value)}"`;
|
|
2677
|
+
}).join("");
|
|
2678
|
+
return `
|
|
2679
|
+
<${MESSAGE_ID_TAG_NAME}${serializedAttributes}>${ref}</${MESSAGE_ID_TAG_NAME}>`;
|
|
2680
|
+
}
|
|
2681
|
+
function formatTokenSize(tokens) {
|
|
2682
|
+
if (tokens < 1e3) return String(tokens);
|
|
2683
|
+
if (tokens < 1e4) return `${(tokens / 1e3).toFixed(1)}K`;
|
|
2684
|
+
return `${Math.round(tokens / 1e3)}K`;
|
|
2685
|
+
}
|
|
2686
|
+
function classifyMessageType(parts) {
|
|
2687
|
+
let hasTool = false;
|
|
2688
|
+
let hasText = false;
|
|
2689
|
+
let hasReasoning = false;
|
|
2690
|
+
const toolNames = [];
|
|
2691
|
+
for (const part of parts) {
|
|
2692
|
+
if (part.type === "tool") {
|
|
2693
|
+
hasTool = true;
|
|
2694
|
+
if (typeof part.tool === "string" && !toolNames.includes(part.tool)) {
|
|
2695
|
+
toolNames.push(part.tool);
|
|
2696
|
+
}
|
|
2697
|
+
} else if (part.type === "text") {
|
|
2698
|
+
hasText = true;
|
|
2699
|
+
} else if (part.type === "reasoning") {
|
|
2700
|
+
hasReasoning = true;
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
if (hasTool) {
|
|
2704
|
+
return toolNames.length > 0 ? `tool:${toolNames.join(",")}` : "tool";
|
|
2705
|
+
}
|
|
2706
|
+
if (hasReasoning && !hasText) return "reasoning";
|
|
2707
|
+
return "text";
|
|
2708
|
+
}
|
|
2709
|
+
function assignMessageRefs(state, messages) {
|
|
2710
|
+
let assigned = 0;
|
|
2711
|
+
let skippedSubAgentPrompt = false;
|
|
2712
|
+
for (const message of messages) {
|
|
2713
|
+
if (isIgnoredUserMessage(message)) {
|
|
2714
|
+
continue;
|
|
2715
|
+
}
|
|
2716
|
+
if (state.isSubAgent && !skippedSubAgentPrompt && message.info.role === "user") {
|
|
2717
|
+
skippedSubAgentPrompt = true;
|
|
2718
|
+
continue;
|
|
2719
|
+
}
|
|
2720
|
+
const rawMessageId = message.info.id;
|
|
2721
|
+
if (typeof rawMessageId !== "string" || rawMessageId.length === 0) {
|
|
2722
|
+
continue;
|
|
2723
|
+
}
|
|
2724
|
+
if (rawMessageId.startsWith("msg_dcp_summary_") || rawMessageId.startsWith("msg_dcp_text_")) {
|
|
2725
|
+
continue;
|
|
2726
|
+
}
|
|
2727
|
+
const existingRef = state.messageIds.byRawId.get(rawMessageId);
|
|
2728
|
+
if (existingRef) {
|
|
2729
|
+
if (state.messageIds.byRef.get(existingRef) !== rawMessageId) {
|
|
2730
|
+
state.messageIds.byRef.set(existingRef, rawMessageId);
|
|
2731
|
+
}
|
|
2732
|
+
continue;
|
|
2733
|
+
}
|
|
2734
|
+
const ref = allocateNextMessageRef(state);
|
|
2735
|
+
state.messageIds.byRawId.set(rawMessageId, ref);
|
|
2736
|
+
state.messageIds.byRef.set(ref, rawMessageId);
|
|
2737
|
+
assigned++;
|
|
2738
|
+
}
|
|
2739
|
+
return assigned;
|
|
2740
|
+
}
|
|
2741
|
+
function repairNonMonotonicMessageRefs(state, messages) {
|
|
2742
|
+
const eligible = messages.filter((message) => {
|
|
2743
|
+
if (isIgnoredUserMessage(message)) return false;
|
|
2744
|
+
const id = message.info.id;
|
|
2745
|
+
return typeof id === "string" && id.length > 0 && !id.startsWith("msg_dcp_summary_") && !id.startsWith("msg_dcp_text_");
|
|
2746
|
+
});
|
|
2747
|
+
let previous = 0;
|
|
2748
|
+
let corrupted = false;
|
|
2749
|
+
for (const message of eligible) {
|
|
2750
|
+
const ref = state.messageIds.byRawId.get(message.info.id);
|
|
2751
|
+
const parsed = ref ? parseMessageRef(ref) : null;
|
|
2752
|
+
if (parsed === null) continue;
|
|
2753
|
+
if (parsed <= previous) {
|
|
2754
|
+
corrupted = true;
|
|
2755
|
+
break;
|
|
2756
|
+
}
|
|
2757
|
+
previous = parsed;
|
|
2758
|
+
}
|
|
2759
|
+
if (!corrupted) return false;
|
|
2760
|
+
const oldRawIds = [...state.messageIds.byRawId.keys()];
|
|
2761
|
+
state.messageIds.byRawId.clear();
|
|
2762
|
+
state.messageIds.byRef.clear();
|
|
2763
|
+
state.messageIds.nextRef = 1;
|
|
2764
|
+
for (const message of eligible) {
|
|
2765
|
+
const rawId = message.info.id;
|
|
2766
|
+
const ref = allocateNextMessageRef(state);
|
|
2767
|
+
state.messageIds.byRawId.set(rawId, ref);
|
|
2768
|
+
state.messageIds.byRef.set(ref, rawId);
|
|
2769
|
+
}
|
|
2770
|
+
for (const rawId of oldRawIds) {
|
|
2771
|
+
if (state.messageIds.byRawId.has(rawId)) continue;
|
|
2772
|
+
const ref = allocateNextMessageRef(state);
|
|
2773
|
+
state.messageIds.byRawId.set(rawId, ref);
|
|
2774
|
+
state.messageIds.byRef.set(ref, rawId);
|
|
2775
|
+
}
|
|
2776
|
+
for (const block of state.prune.messages.blocksById.values()) {
|
|
2777
|
+
const ids = [.../* @__PURE__ */ new Set([...block.effectiveMessageIds, ...block.directMessageIds])].filter((id) => state.messageIds.byRawId.has(id)).sort(
|
|
2778
|
+
(left, right) => (parseMessageRef(state.messageIds.byRawId.get(left)) ?? 0) - (parseMessageRef(state.messageIds.byRawId.get(right)) ?? 0)
|
|
2779
|
+
);
|
|
2780
|
+
if (ids.length === 0) continue;
|
|
2781
|
+
block.startId = state.messageIds.byRawId.get(ids[0]);
|
|
2782
|
+
block.endId = state.messageIds.byRawId.get(ids[ids.length - 1]);
|
|
2783
|
+
}
|
|
2784
|
+
state.prune.messages.structureVersion = (state.prune.messages.structureVersion ?? 0) + 1;
|
|
2785
|
+
return true;
|
|
2786
|
+
}
|
|
2787
|
+
function allocateNextMessageRef(state) {
|
|
2788
|
+
let candidate = Number.isInteger(state.messageIds.nextRef) ? Math.max(MESSAGE_REF_MIN_INDEX, state.messageIds.nextRef) : MESSAGE_REF_MIN_INDEX;
|
|
2789
|
+
while (candidate <= MESSAGE_REF_MAX_INDEX) {
|
|
2790
|
+
const ref = formatMessageRef(candidate);
|
|
2791
|
+
if (!state.messageIds.byRef.has(ref)) {
|
|
2792
|
+
state.messageIds.nextRef = candidate + 1;
|
|
2793
|
+
return ref;
|
|
2794
|
+
}
|
|
2795
|
+
candidate++;
|
|
2796
|
+
}
|
|
2797
|
+
throw new Error(
|
|
2798
|
+
`Message ID alias capacity exceeded. Cannot allocate more than ${formatMessageRef(MESSAGE_REF_MAX_INDEX)} aliases in this session.`
|
|
2799
|
+
);
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2588
2802
|
// lib/state/utils.ts
|
|
2589
2803
|
var isMessageCompacted = (state, msg) => {
|
|
2590
2804
|
if (!isMessageWithInfo(msg)) {
|
|
@@ -2736,8 +2950,10 @@ function loadPruneMessagesState(persisted) {
|
|
|
2736
2950
|
mode: block.mode === "range" || block.mode === "message" ? block.mode : void 0,
|
|
2737
2951
|
topic: typeof block.topic === "string" ? block.topic : "",
|
|
2738
2952
|
batchTopic: typeof block.batchTopic === "string" ? block.batchTopic : typeof block.topic === "string" ? block.topic : "",
|
|
2739
|
-
|
|
2740
|
-
|
|
2953
|
+
// [Issue #415] Migrate legacy 4-digit boundary refs (pre-1.1.0 state)
|
|
2954
|
+
// to canonical 5-digit form, matching the messageIds ref migration.
|
|
2955
|
+
startId: typeof block.startId === "string" ? migrateMessageRef(block.startId) : "",
|
|
2956
|
+
endId: typeof block.endId === "string" ? migrateMessageRef(block.endId) : "",
|
|
2741
2957
|
anchorMessageId: typeof block.anchorMessageId === "string" ? block.anchorMessageId : "",
|
|
2742
2958
|
compressMessageId: typeof block.compressMessageId === "string" ? block.compressMessageId : "",
|
|
2743
2959
|
compressCallId: typeof block.compressCallId === "string" ? block.compressCallId : void 0,
|
|
@@ -2955,7 +3171,8 @@ function saveSessionState(sessionState, logger, sessionName) {
|
|
|
2955
3171
|
modelContextLimit: sessionState.modelContextLimit,
|
|
2956
3172
|
modelProviderID: sessionState.modelProviderID,
|
|
2957
3173
|
modelID: sessionState.modelID,
|
|
2958
|
-
imageArchive: sessionState.imageArchive
|
|
3174
|
+
imageArchive: sessionState.imageArchive,
|
|
3175
|
+
stickyTruncations: sessionState.stickyTruncations
|
|
2959
3176
|
};
|
|
2960
3177
|
const key = saveQueueKey(sessionState.sessionId, sessionState.storageDir);
|
|
2961
3178
|
let queue = saveQueues.get(key);
|
|
@@ -2963,8 +3180,8 @@ function saveSessionState(sessionState, logger, sessionName) {
|
|
|
2963
3180
|
queue = { pending: [], waiters: [], draining: false };
|
|
2964
3181
|
saveQueues.set(key, queue);
|
|
2965
3182
|
}
|
|
2966
|
-
const promise = new Promise((
|
|
2967
|
-
queue.waiters.push({ resolve:
|
|
3183
|
+
const promise = new Promise((resolve3, reject) => {
|
|
3184
|
+
queue.waiters.push({ resolve: resolve3, reject });
|
|
2968
3185
|
});
|
|
2969
3186
|
queue.pending.push({
|
|
2970
3187
|
sessionId: sessionState.sessionId,
|
|
@@ -3088,205 +3305,6 @@ import { existsSync as existsSync3 } from "fs";
|
|
|
3088
3305
|
import { join as join3 } from "path";
|
|
3089
3306
|
import { cwd } from "process";
|
|
3090
3307
|
|
|
3091
|
-
// lib/message-ids.ts
|
|
3092
|
-
var MESSAGE_REF_REGEX = /^m(\d{4,5})$/;
|
|
3093
|
-
var BLOCK_REF_REGEX = /^b([1-9]\d*)$/;
|
|
3094
|
-
var MESSAGE_ID_TAG_NAME = "dcp-message-id";
|
|
3095
|
-
var MESSAGE_REF_WIDTH = 5;
|
|
3096
|
-
var MESSAGE_REF_MIN_INDEX = 1;
|
|
3097
|
-
var MESSAGE_REF_MAX_INDEX = 99999;
|
|
3098
|
-
function formatMessageRef(index) {
|
|
3099
|
-
if (!Number.isInteger(index) || index < MESSAGE_REF_MIN_INDEX || index > MESSAGE_REF_MAX_INDEX) {
|
|
3100
|
-
throw new Error(
|
|
3101
|
-
`Message ID index out of bounds: ${index}. Supported range is ${MESSAGE_REF_MIN_INDEX}-${MESSAGE_REF_MAX_INDEX}.`
|
|
3102
|
-
);
|
|
3103
|
-
}
|
|
3104
|
-
return `m${index.toString().padStart(MESSAGE_REF_WIDTH, "0")}`;
|
|
3105
|
-
}
|
|
3106
|
-
function formatBlockRef(blockId) {
|
|
3107
|
-
if (!Number.isInteger(blockId) || blockId < 1) {
|
|
3108
|
-
throw new Error(`Invalid block ID: ${blockId}`);
|
|
3109
|
-
}
|
|
3110
|
-
return `b${blockId}`;
|
|
3111
|
-
}
|
|
3112
|
-
function parseMessageRef(ref) {
|
|
3113
|
-
const normalized = ref.trim().toLowerCase();
|
|
3114
|
-
const match = normalized.match(MESSAGE_REF_REGEX);
|
|
3115
|
-
if (!match) {
|
|
3116
|
-
return null;
|
|
3117
|
-
}
|
|
3118
|
-
const index = Number.parseInt(match[1], 10);
|
|
3119
|
-
if (!Number.isInteger(index)) {
|
|
3120
|
-
return null;
|
|
3121
|
-
}
|
|
3122
|
-
if (index < MESSAGE_REF_MIN_INDEX || index > MESSAGE_REF_MAX_INDEX) {
|
|
3123
|
-
return null;
|
|
3124
|
-
}
|
|
3125
|
-
return index;
|
|
3126
|
-
}
|
|
3127
|
-
function parseBlockRef(ref) {
|
|
3128
|
-
const normalized = ref.trim().toLowerCase();
|
|
3129
|
-
const match = normalized.match(BLOCK_REF_REGEX);
|
|
3130
|
-
if (!match) {
|
|
3131
|
-
return null;
|
|
3132
|
-
}
|
|
3133
|
-
const id = Number.parseInt(match[1], 10);
|
|
3134
|
-
return Number.isInteger(id) ? id : null;
|
|
3135
|
-
}
|
|
3136
|
-
function parseBoundaryId(id) {
|
|
3137
|
-
const normalized = id.trim().toLowerCase();
|
|
3138
|
-
const messageIndex = parseMessageRef(normalized);
|
|
3139
|
-
if (messageIndex !== null) {
|
|
3140
|
-
return {
|
|
3141
|
-
kind: "message",
|
|
3142
|
-
ref: formatMessageRef(messageIndex),
|
|
3143
|
-
index: messageIndex
|
|
3144
|
-
};
|
|
3145
|
-
}
|
|
3146
|
-
const blockId = parseBlockRef(normalized);
|
|
3147
|
-
if (blockId !== null) {
|
|
3148
|
-
return {
|
|
3149
|
-
kind: "compressed-block",
|
|
3150
|
-
ref: formatBlockRef(blockId),
|
|
3151
|
-
blockId
|
|
3152
|
-
};
|
|
3153
|
-
}
|
|
3154
|
-
return null;
|
|
3155
|
-
}
|
|
3156
|
-
function escapeXmlAttribute(value) {
|
|
3157
|
-
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
3158
|
-
}
|
|
3159
|
-
function formatMessageIdTag(ref, attributes) {
|
|
3160
|
-
const serializedAttributes = Object.entries(attributes || {}).sort(([left], [right]) => left.localeCompare(right)).map(([name, value]) => {
|
|
3161
|
-
if (name.trim().length === 0 || typeof value !== "string" || value.length === 0) {
|
|
3162
|
-
return "";
|
|
3163
|
-
}
|
|
3164
|
-
return ` ${name}="${escapeXmlAttribute(value)}"`;
|
|
3165
|
-
}).join("");
|
|
3166
|
-
return `
|
|
3167
|
-
<${MESSAGE_ID_TAG_NAME}${serializedAttributes}>${ref}</${MESSAGE_ID_TAG_NAME}>`;
|
|
3168
|
-
}
|
|
3169
|
-
function formatTokenSize(tokens) {
|
|
3170
|
-
if (tokens < 1e3) return String(tokens);
|
|
3171
|
-
if (tokens < 1e4) return `${(tokens / 1e3).toFixed(1)}K`;
|
|
3172
|
-
return `${Math.round(tokens / 1e3)}K`;
|
|
3173
|
-
}
|
|
3174
|
-
function classifyMessageType(parts) {
|
|
3175
|
-
let hasTool = false;
|
|
3176
|
-
let hasText = false;
|
|
3177
|
-
let hasReasoning = false;
|
|
3178
|
-
const toolNames = [];
|
|
3179
|
-
for (const part of parts) {
|
|
3180
|
-
if (part.type === "tool") {
|
|
3181
|
-
hasTool = true;
|
|
3182
|
-
if (typeof part.tool === "string" && !toolNames.includes(part.tool)) {
|
|
3183
|
-
toolNames.push(part.tool);
|
|
3184
|
-
}
|
|
3185
|
-
} else if (part.type === "text") {
|
|
3186
|
-
hasText = true;
|
|
3187
|
-
} else if (part.type === "reasoning") {
|
|
3188
|
-
hasReasoning = true;
|
|
3189
|
-
}
|
|
3190
|
-
}
|
|
3191
|
-
if (hasTool) {
|
|
3192
|
-
return toolNames.length > 0 ? `tool:${toolNames.join(",")}` : "tool";
|
|
3193
|
-
}
|
|
3194
|
-
if (hasReasoning && !hasText) return "reasoning";
|
|
3195
|
-
return "text";
|
|
3196
|
-
}
|
|
3197
|
-
function assignMessageRefs(state, messages) {
|
|
3198
|
-
let assigned = 0;
|
|
3199
|
-
let skippedSubAgentPrompt = false;
|
|
3200
|
-
for (const message of messages) {
|
|
3201
|
-
if (isIgnoredUserMessage(message)) {
|
|
3202
|
-
continue;
|
|
3203
|
-
}
|
|
3204
|
-
if (state.isSubAgent && !skippedSubAgentPrompt && message.info.role === "user") {
|
|
3205
|
-
skippedSubAgentPrompt = true;
|
|
3206
|
-
continue;
|
|
3207
|
-
}
|
|
3208
|
-
const rawMessageId = message.info.id;
|
|
3209
|
-
if (typeof rawMessageId !== "string" || rawMessageId.length === 0) {
|
|
3210
|
-
continue;
|
|
3211
|
-
}
|
|
3212
|
-
if (rawMessageId.startsWith("msg_dcp_summary_") || rawMessageId.startsWith("msg_dcp_text_")) {
|
|
3213
|
-
continue;
|
|
3214
|
-
}
|
|
3215
|
-
const existingRef = state.messageIds.byRawId.get(rawMessageId);
|
|
3216
|
-
if (existingRef) {
|
|
3217
|
-
if (state.messageIds.byRef.get(existingRef) !== rawMessageId) {
|
|
3218
|
-
state.messageIds.byRef.set(existingRef, rawMessageId);
|
|
3219
|
-
}
|
|
3220
|
-
continue;
|
|
3221
|
-
}
|
|
3222
|
-
const ref = allocateNextMessageRef(state);
|
|
3223
|
-
state.messageIds.byRawId.set(rawMessageId, ref);
|
|
3224
|
-
state.messageIds.byRef.set(ref, rawMessageId);
|
|
3225
|
-
assigned++;
|
|
3226
|
-
}
|
|
3227
|
-
return assigned;
|
|
3228
|
-
}
|
|
3229
|
-
function repairNonMonotonicMessageRefs(state, messages) {
|
|
3230
|
-
const eligible = messages.filter((message) => {
|
|
3231
|
-
if (isIgnoredUserMessage(message)) return false;
|
|
3232
|
-
const id = message.info.id;
|
|
3233
|
-
return typeof id === "string" && id.length > 0 && !id.startsWith("msg_dcp_summary_") && !id.startsWith("msg_dcp_text_");
|
|
3234
|
-
});
|
|
3235
|
-
let previous = 0;
|
|
3236
|
-
let corrupted = false;
|
|
3237
|
-
for (const message of eligible) {
|
|
3238
|
-
const ref = state.messageIds.byRawId.get(message.info.id);
|
|
3239
|
-
const parsed = ref ? parseMessageRef(ref) : null;
|
|
3240
|
-
if (parsed === null) continue;
|
|
3241
|
-
if (parsed <= previous) {
|
|
3242
|
-
corrupted = true;
|
|
3243
|
-
break;
|
|
3244
|
-
}
|
|
3245
|
-
previous = parsed;
|
|
3246
|
-
}
|
|
3247
|
-
if (!corrupted) return false;
|
|
3248
|
-
const oldRawIds = [...state.messageIds.byRawId.keys()];
|
|
3249
|
-
state.messageIds.byRawId.clear();
|
|
3250
|
-
state.messageIds.byRef.clear();
|
|
3251
|
-
state.messageIds.nextRef = 1;
|
|
3252
|
-
for (const message of eligible) {
|
|
3253
|
-
const rawId = message.info.id;
|
|
3254
|
-
const ref = allocateNextMessageRef(state);
|
|
3255
|
-
state.messageIds.byRawId.set(rawId, ref);
|
|
3256
|
-
state.messageIds.byRef.set(ref, rawId);
|
|
3257
|
-
}
|
|
3258
|
-
for (const rawId of oldRawIds) {
|
|
3259
|
-
if (state.messageIds.byRawId.has(rawId)) continue;
|
|
3260
|
-
const ref = allocateNextMessageRef(state);
|
|
3261
|
-
state.messageIds.byRawId.set(rawId, ref);
|
|
3262
|
-
state.messageIds.byRef.set(ref, rawId);
|
|
3263
|
-
}
|
|
3264
|
-
for (const block of state.prune.messages.blocksById.values()) {
|
|
3265
|
-
const ids = [.../* @__PURE__ */ new Set([...block.effectiveMessageIds, ...block.directMessageIds])].filter((id) => state.messageIds.byRawId.has(id)).sort(
|
|
3266
|
-
(left, right) => (parseMessageRef(state.messageIds.byRawId.get(left)) ?? 0) - (parseMessageRef(state.messageIds.byRawId.get(right)) ?? 0)
|
|
3267
|
-
);
|
|
3268
|
-
if (ids.length === 0) continue;
|
|
3269
|
-
block.startId = state.messageIds.byRawId.get(ids[0]);
|
|
3270
|
-
block.endId = state.messageIds.byRawId.get(ids[ids.length - 1]);
|
|
3271
|
-
}
|
|
3272
|
-
state.prune.messages.structureVersion = (state.prune.messages.structureVersion ?? 0) + 1;
|
|
3273
|
-
return true;
|
|
3274
|
-
}
|
|
3275
|
-
function allocateNextMessageRef(state) {
|
|
3276
|
-
let candidate = Number.isInteger(state.messageIds.nextRef) ? Math.max(MESSAGE_REF_MIN_INDEX, state.messageIds.nextRef) : MESSAGE_REF_MIN_INDEX;
|
|
3277
|
-
while (candidate <= MESSAGE_REF_MAX_INDEX) {
|
|
3278
|
-
const ref = formatMessageRef(candidate);
|
|
3279
|
-
if (!state.messageIds.byRef.has(ref)) {
|
|
3280
|
-
state.messageIds.nextRef = candidate + 1;
|
|
3281
|
-
return ref;
|
|
3282
|
-
}
|
|
3283
|
-
candidate++;
|
|
3284
|
-
}
|
|
3285
|
-
throw new Error(
|
|
3286
|
-
`Message ID alias capacity exceeded. Cannot allocate more than ${formatMessageRef(MESSAGE_REF_MAX_INDEX)} aliases in this session.`
|
|
3287
|
-
);
|
|
3288
|
-
}
|
|
3289
|
-
|
|
3290
3308
|
// lib/compress/state.ts
|
|
3291
3309
|
var DEFAULT_PROMOTION_THRESHOLD = 5;
|
|
3292
3310
|
var COMPRESSED_BLOCK_HEADER = "[Compressed conversation section]";
|
|
@@ -4758,7 +4776,11 @@ function restoreForkCompressionState(state, forkMessages, parent, parentMessages
|
|
|
4758
4776
|
directMessageIds,
|
|
4759
4777
|
directToolIds,
|
|
4760
4778
|
effectiveMessageIds,
|
|
4761
|
-
effectiveToolIds
|
|
4779
|
+
effectiveToolIds,
|
|
4780
|
+
// [Issue #415] The parent record is raw persisted JSON; legacy
|
|
4781
|
+
// 4-digit boundary refs must be normalized like every other ref.
|
|
4782
|
+
startId: typeof block.startId === "string" ? migrateMessageRef(block.startId) : block.startId,
|
|
4783
|
+
endId: typeof block.endId === "string" ? migrateMessageRef(block.endId) : block.endId
|
|
4762
4784
|
});
|
|
4763
4785
|
}
|
|
4764
4786
|
const activeParentIds = new Set(parent.prune.messages.activeBlockIds);
|
|
@@ -5124,7 +5146,8 @@ function createSessionState() {
|
|
|
5124
5146
|
qualityGateRetryPending: false,
|
|
5125
5147
|
noContextLimitWarned: false,
|
|
5126
5148
|
imageArchive: {},
|
|
5127
|
-
imagePruneDone: false
|
|
5149
|
+
imagePruneDone: false,
|
|
5150
|
+
stickyTruncations: {}
|
|
5128
5151
|
};
|
|
5129
5152
|
}
|
|
5130
5153
|
function resetSessionState(state) {
|
|
@@ -5170,6 +5193,7 @@ function resetSessionState(state) {
|
|
|
5170
5193
|
state.noContextLimitWarned = false;
|
|
5171
5194
|
state.imageArchive = {};
|
|
5172
5195
|
state.imagePruneDone = false;
|
|
5196
|
+
state.stickyTruncations = {};
|
|
5173
5197
|
}
|
|
5174
5198
|
async function ensureSessionInitialized(client, state, sessionId, logger, messages, config, projectDir) {
|
|
5175
5199
|
if (state.sessionId === sessionId) {
|
|
@@ -5198,6 +5222,13 @@ async function ensureSessionInitialized(client, state, sessionId, logger, messag
|
|
|
5198
5222
|
if (parentSessionId) {
|
|
5199
5223
|
try {
|
|
5200
5224
|
const parent = await loadSessionState(parentSessionId, logger);
|
|
5225
|
+
if (parent?.stickyTruncations) {
|
|
5226
|
+
for (const [callID, output] of Object.entries(parent.stickyTruncations)) {
|
|
5227
|
+
if (typeof output === "string") {
|
|
5228
|
+
state.stickyTruncations[callID] = output;
|
|
5229
|
+
}
|
|
5230
|
+
}
|
|
5231
|
+
}
|
|
5201
5232
|
const response = parent ? await client.session.messages({ path: { id: parentSessionId } }) : void 0;
|
|
5202
5233
|
const parentMessages = Array.isArray(response?.data) ? response.data : Array.isArray(response) ? response : [];
|
|
5203
5234
|
if (parent && parentMessages.length > 0) {
|
|
@@ -5263,6 +5294,14 @@ async function ensureSessionInitialized(client, state, sessionId, logger, messag
|
|
|
5263
5294
|
}
|
|
5264
5295
|
}
|
|
5265
5296
|
}
|
|
5297
|
+
const persistedSticky = persisted.stickyTruncations;
|
|
5298
|
+
if (persistedSticky && typeof persistedSticky === "object") {
|
|
5299
|
+
for (const [callID, output] of Object.entries(persistedSticky)) {
|
|
5300
|
+
if (typeof output === "string") {
|
|
5301
|
+
state.stickyTruncations[callID] = output;
|
|
5302
|
+
}
|
|
5303
|
+
}
|
|
5304
|
+
}
|
|
5266
5305
|
const persistedAny = persisted;
|
|
5267
5306
|
if (persistedAny._persistedMessageIds) {
|
|
5268
5307
|
state.messageIds = {
|
|
@@ -10254,6 +10293,102 @@ function extractTextContent(msg) {
|
|
|
10254
10293
|
return textParts.join(" ").replace(/\s+/g, " ").trim();
|
|
10255
10294
|
}
|
|
10256
10295
|
|
|
10296
|
+
// lib/compress/tofile-target.ts
|
|
10297
|
+
import * as fsp from "fs/promises";
|
|
10298
|
+
import { homedir as homedir3, tmpdir } from "os";
|
|
10299
|
+
import { dirname as dirname2, join as join5, relative, resolve } from "path";
|
|
10300
|
+
async function resolveSafeToFileTarget(targetPath, options) {
|
|
10301
|
+
const osTmp = tmpdir();
|
|
10302
|
+
const allowedDirs = options?.allowedDirs ?? [osTmp, join5(homedir3(), ".cache", "opencode")];
|
|
10303
|
+
const dirList = allowedDirs.join(" or ");
|
|
10304
|
+
if (typeof targetPath !== "string" || targetPath.length === 0) {
|
|
10305
|
+
return {
|
|
10306
|
+
ok: false,
|
|
10307
|
+
error: `Error: toFile path must be under ${dirList}. Got: ${targetPath}`
|
|
10308
|
+
};
|
|
10309
|
+
}
|
|
10310
|
+
const resolvedPath = resolve(targetPath);
|
|
10311
|
+
const lexicallyAllowed = allowedDirs.some((dir) => {
|
|
10312
|
+
const rel = relative(resolve(dir), resolvedPath);
|
|
10313
|
+
return rel === "" || !rel.startsWith("..") && !rel.startsWith("/") && !isWinAbsolute(rel);
|
|
10314
|
+
});
|
|
10315
|
+
if (!lexicallyAllowed) {
|
|
10316
|
+
return {
|
|
10317
|
+
ok: false,
|
|
10318
|
+
error: `Error: toFile path must be under ${dirList}. Got: ${targetPath}`
|
|
10319
|
+
};
|
|
10320
|
+
}
|
|
10321
|
+
let realAncestor = null;
|
|
10322
|
+
const tail = [];
|
|
10323
|
+
let current = resolvedPath;
|
|
10324
|
+
while (true) {
|
|
10325
|
+
try {
|
|
10326
|
+
realAncestor = await fsp.realpath(current);
|
|
10327
|
+
break;
|
|
10328
|
+
} catch (err) {
|
|
10329
|
+
const code = err.code;
|
|
10330
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") {
|
|
10331
|
+
return {
|
|
10332
|
+
ok: false,
|
|
10333
|
+
error: `Error: cannot validate toFile path: ${err.message}`
|
|
10334
|
+
};
|
|
10335
|
+
}
|
|
10336
|
+
const parent = dirname2(current);
|
|
10337
|
+
if (parent === current) {
|
|
10338
|
+
return {
|
|
10339
|
+
ok: false,
|
|
10340
|
+
error: `Error: cannot validate toFile path: no existing ancestor found`
|
|
10341
|
+
};
|
|
10342
|
+
}
|
|
10343
|
+
tail.unshift(basename(current));
|
|
10344
|
+
current = parent;
|
|
10345
|
+
}
|
|
10346
|
+
}
|
|
10347
|
+
const physicalPath = tail.length > 0 ? join5(realAncestor, ...tail) : realAncestor;
|
|
10348
|
+
const realAllowedDirs = [];
|
|
10349
|
+
for (const dir of allowedDirs) {
|
|
10350
|
+
try {
|
|
10351
|
+
realAllowedDirs.push(await fsp.realpath(resolve(dir)));
|
|
10352
|
+
} catch {
|
|
10353
|
+
}
|
|
10354
|
+
}
|
|
10355
|
+
const physicallyAllowed = realAllowedDirs.some((dir) => containsOrIs(dir, physicalPath));
|
|
10356
|
+
if (!physicallyAllowed) {
|
|
10357
|
+
return {
|
|
10358
|
+
ok: false,
|
|
10359
|
+
error: `Error: toFile path resolves outside the allowed directories via a symlink. Got: ${targetPath}`
|
|
10360
|
+
};
|
|
10361
|
+
}
|
|
10362
|
+
try {
|
|
10363
|
+
const stat = await fsp.lstat(resolvedPath);
|
|
10364
|
+
if (stat.isSymbolicLink()) {
|
|
10365
|
+
return {
|
|
10366
|
+
ok: false,
|
|
10367
|
+
error: `Error: toFile target exists as a symlink and will not be followed. Got: ${targetPath}`
|
|
10368
|
+
};
|
|
10369
|
+
}
|
|
10370
|
+
} catch (err) {
|
|
10371
|
+
const code = err.code;
|
|
10372
|
+
if (code !== "ENOENT") {
|
|
10373
|
+
return {
|
|
10374
|
+
ok: false,
|
|
10375
|
+
error: `Error: cannot validate toFile path: ${err.message}`
|
|
10376
|
+
};
|
|
10377
|
+
}
|
|
10378
|
+
}
|
|
10379
|
+
return { ok: true, filePath: resolvedPath };
|
|
10380
|
+
}
|
|
10381
|
+
function isWinAbsolute(p) {
|
|
10382
|
+
return /^[a-zA-Z]:[\\/]/.test(p);
|
|
10383
|
+
}
|
|
10384
|
+
function basename(p) {
|
|
10385
|
+
return p.split(/[\\/]/).filter(Boolean).pop() ?? p;
|
|
10386
|
+
}
|
|
10387
|
+
function containsOrIs(root, p) {
|
|
10388
|
+
const rel = relative(root, p);
|
|
10389
|
+
return rel === "" || !rel.startsWith("..") && !rel.startsWith("/") && !isWinAbsolute(rel);
|
|
10390
|
+
}
|
|
10391
|
+
|
|
10257
10392
|
// lib/compress/decompress.ts
|
|
10258
10393
|
async function prepareDecompressSession(ctx, toolCtx) {
|
|
10259
10394
|
await toolCtx.ask({
|
|
@@ -10287,7 +10422,13 @@ function resolveTargets(args, state, rawMessages, logger) {
|
|
|
10287
10422
|
if (mode.mode === "block") {
|
|
10288
10423
|
return resolveSingleBlockTarget(messagesState, args.blockId);
|
|
10289
10424
|
}
|
|
10290
|
-
return resolveRangeTarget(
|
|
10425
|
+
return resolveRangeTarget(
|
|
10426
|
+
state,
|
|
10427
|
+
rawMessages,
|
|
10428
|
+
args.startId,
|
|
10429
|
+
args.endId,
|
|
10430
|
+
logger
|
|
10431
|
+
);
|
|
10291
10432
|
}
|
|
10292
10433
|
function resolveSingleBlockTarget(messagesState, blockIdArg) {
|
|
10293
10434
|
const targetBlockId = parseBlockIdArg(blockIdArg);
|
|
@@ -10399,11 +10540,21 @@ IMPORTANT:
|
|
|
10399
10540
|
- Do NOT call this tool in parallel with compress \u2014 their state mutations may conflict.`;
|
|
10400
10541
|
function buildSchema2() {
|
|
10401
10542
|
return {
|
|
10402
|
-
blockId: tool3.schema.string().optional().describe(
|
|
10403
|
-
|
|
10404
|
-
|
|
10405
|
-
|
|
10406
|
-
|
|
10543
|
+
blockId: tool3.schema.string().optional().describe(
|
|
10544
|
+
'Block reference to decompress (e.g., "b0", "b2"). Mutually exclusive with startId/endId.'
|
|
10545
|
+
),
|
|
10546
|
+
startId: tool3.schema.string().optional().describe(
|
|
10547
|
+
'Range start: message ref (e.g., "m00150") or block ref (e.g., "b2"). Used with endId.'
|
|
10548
|
+
),
|
|
10549
|
+
endId: tool3.schema.string().optional().describe(
|
|
10550
|
+
'Range end: message ref (e.g., "m00200") or block ref (e.g., "b5"). Used with startId.'
|
|
10551
|
+
),
|
|
10552
|
+
toFile: tool3.schema.string().optional().describe(
|
|
10553
|
+
"If provided, writes restored content to this file path instead of inflating context. Block stays compressed. Path must be under /tmp or ~/.cache/opencode/. Example: '/tmp/block52.txt'"
|
|
10554
|
+
),
|
|
10555
|
+
full: tool3.schema.boolean().optional().describe(
|
|
10556
|
+
"If true, restores ALL content down to original messages (multi-level decompress). Default: false \u2014 restores one tier up (e.g., decompressing a T2 block restores T1 summaries, not raw messages). Use full:true only when you need the exact original content and have context budget for it."
|
|
10557
|
+
)
|
|
10407
10558
|
};
|
|
10408
10559
|
}
|
|
10409
10560
|
function extractMessageId(m) {
|
|
@@ -10444,7 +10595,12 @@ function createDecompressTool(factoryCtx) {
|
|
|
10444
10595
|
const contextUsageBefore = effectiveLimitBefore ? Math.round(
|
|
10445
10596
|
getCurrentTokenUsage(ctx.state, rawMessages) / effectiveLimitBefore.limit * 100
|
|
10446
10597
|
) : void 0;
|
|
10447
|
-
const resolved = resolveTargets(
|
|
10598
|
+
const resolved = resolveTargets(
|
|
10599
|
+
args,
|
|
10600
|
+
ctx.state,
|
|
10601
|
+
rawMessages,
|
|
10602
|
+
ctx.logger
|
|
10603
|
+
);
|
|
10448
10604
|
if (!resolved.ok) {
|
|
10449
10605
|
return resolved.error;
|
|
10450
10606
|
}
|
|
@@ -10460,19 +10616,9 @@ function createDecompressTool(factoryCtx) {
|
|
|
10460
10616
|
}
|
|
10461
10617
|
if (args.toFile) {
|
|
10462
10618
|
const targetPath = args.toFile;
|
|
10463
|
-
const
|
|
10464
|
-
|
|
10465
|
-
|
|
10466
|
-
os.tmpdir() + "/",
|
|
10467
|
-
path.join(os.homedir(), ".cache", "opencode") + "/"
|
|
10468
|
-
];
|
|
10469
|
-
const resolvedPath = path.resolve(targetPath);
|
|
10470
|
-
const isAllowed = allowedDirs.some((dir) => {
|
|
10471
|
-
const rel = path.relative(dir, resolvedPath);
|
|
10472
|
-
return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
10473
|
-
});
|
|
10474
|
-
if (!isAllowed) {
|
|
10475
|
-
return `Error: toFile path must be under ${os.tmpdir()} or ~/.cache/opencode/. Got: ${targetPath}`;
|
|
10619
|
+
const safe = await resolveSafeToFileTarget(targetPath);
|
|
10620
|
+
if (!safe.ok) {
|
|
10621
|
+
return safe.error;
|
|
10476
10622
|
}
|
|
10477
10623
|
const msgIdSet = /* @__PURE__ */ new Set();
|
|
10478
10624
|
for (const block of activeBlocks) {
|
|
@@ -10482,7 +10628,8 @@ function createDecompressTool(factoryCtx) {
|
|
|
10482
10628
|
}
|
|
10483
10629
|
const blockMessages = rawMessages.filter((m) => msgIdSet.has(extractMessageId(m)));
|
|
10484
10630
|
const lines2 = blockMessages.map(extractMessageText2);
|
|
10485
|
-
const
|
|
10631
|
+
const fsp2 = await import("fs/promises");
|
|
10632
|
+
const { constants } = await import("fs");
|
|
10486
10633
|
let fileContent;
|
|
10487
10634
|
let sourceNote;
|
|
10488
10635
|
if (lines2.length > 0) {
|
|
@@ -10492,9 +10639,19 @@ function createDecompressTool(factoryCtx) {
|
|
|
10492
10639
|
fileContent = targets[0]?.blocks[0]?.summary ?? "(no content available)";
|
|
10493
10640
|
sourceNote = `no original messages found; wrote block summary (${targets.length} block${targets.length === 1 ? "" : "s"})`;
|
|
10494
10641
|
}
|
|
10495
|
-
|
|
10642
|
+
const flags = constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC | (process.platform === "win32" ? 0 : constants.O_NOFOLLOW);
|
|
10643
|
+
try {
|
|
10644
|
+
const handle = await fsp2.open(safe.filePath, flags, 384);
|
|
10645
|
+
try {
|
|
10646
|
+
await handle.write(Buffer.from(fileContent, "utf-8"));
|
|
10647
|
+
} finally {
|
|
10648
|
+
await handle.close();
|
|
10649
|
+
}
|
|
10650
|
+
} catch (err) {
|
|
10651
|
+
return `Error: toFile write failed: ${err.message}`;
|
|
10652
|
+
}
|
|
10496
10653
|
const displayIds2 = targets.map((t) => `b${t.displayId}`).join(", ");
|
|
10497
|
-
return `Block(s) ${displayIds2} content (${sourceNote}, ${fileContent.length} chars) written to ${
|
|
10654
|
+
return `Block(s) ${displayIds2} content (${sourceNote}, ${fileContent.length} chars) written to ${safe.filePath}. Block(s) stay compressed \u2014 context unchanged. Use read tool to access specific parts.`;
|
|
10498
10655
|
}
|
|
10499
10656
|
const availability = checkDecompressSourceAvailability(
|
|
10500
10657
|
messagesState,
|
|
@@ -10591,7 +10748,9 @@ function isLiveBlock(block) {
|
|
|
10591
10748
|
return block.active && !block.deactivatedByUser && !block.deactivatedByUserDeep;
|
|
10592
10749
|
}
|
|
10593
10750
|
function rangeKey(startId, endId) {
|
|
10594
|
-
|
|
10751
|
+
const s = typeof startId === "string" ? migrateMessageRef(startId) : startId;
|
|
10752
|
+
const e = typeof endId === "string" ? migrateMessageRef(endId) : endId;
|
|
10753
|
+
return `${s}::${e}`;
|
|
10595
10754
|
}
|
|
10596
10755
|
function rewriteCompressInput(part, liveKeys) {
|
|
10597
10756
|
if (part.type !== "tool") return null;
|
|
@@ -11326,10 +11485,10 @@ function hideFailedCompressCalls(messages) {
|
|
|
11326
11485
|
|
|
11327
11486
|
// lib/logger.ts
|
|
11328
11487
|
import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
11329
|
-
import { join as
|
|
11488
|
+
import { join as join6 } from "path";
|
|
11330
11489
|
import { existsSync as existsSync5 } from "fs";
|
|
11331
|
-
import { homedir as
|
|
11332
|
-
var LOG_VERSION = true ? "1.18.
|
|
11490
|
+
import { homedir as homedir4 } from "os";
|
|
11491
|
+
var LOG_VERSION = true ? "1.18.5-pr.416.226" : "dev";
|
|
11333
11492
|
var LEVEL_RANK = {
|
|
11334
11493
|
debug: 10,
|
|
11335
11494
|
info: 20,
|
|
@@ -11349,8 +11508,8 @@ var Logger = class {
|
|
|
11349
11508
|
constructor(enabled, level) {
|
|
11350
11509
|
const resolved = level !== void 0 && LEVEL_RANK[level] !== void 0 ? level : void 0;
|
|
11351
11510
|
this.level = resolved ?? (enabled ? "debug" : "warn");
|
|
11352
|
-
const configHome = process.env.XDG_CONFIG_HOME ||
|
|
11353
|
-
this.logDir =
|
|
11511
|
+
const configHome = process.env.XDG_CONFIG_HOME || join6(homedir4(), ".config");
|
|
11512
|
+
this.logDir = join6(configHome, "opencode", "logs", "acp");
|
|
11354
11513
|
}
|
|
11355
11514
|
/** Back-compat view of the old flag: true only at full debug verbosity. */
|
|
11356
11515
|
get enabled() {
|
|
@@ -11412,11 +11571,11 @@ var Logger = class {
|
|
|
11412
11571
|
const dataStr = this.formatData(data);
|
|
11413
11572
|
const logLine = `${timestamp} ${level.padEnd(5)} ${component}: ${message}${dataStr ? " | " + dataStr : ""} | v=${LOG_VERSION}
|
|
11414
11573
|
`;
|
|
11415
|
-
const dailyLogDir =
|
|
11574
|
+
const dailyLogDir = join6(this.logDir, "daily");
|
|
11416
11575
|
if (!existsSync5(dailyLogDir)) {
|
|
11417
11576
|
await mkdir2(dailyLogDir, { recursive: true });
|
|
11418
11577
|
}
|
|
11419
|
-
const logFile =
|
|
11578
|
+
const logFile = join6(dailyLogDir, `${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.log`);
|
|
11420
11579
|
await writeFile2(logFile, logLine, { flag: "a" });
|
|
11421
11580
|
} catch (error) {
|
|
11422
11581
|
}
|
|
@@ -11527,7 +11686,7 @@ var Logger = class {
|
|
|
11527
11686
|
async saveContext(sessionId, messages) {
|
|
11528
11687
|
if (this.level !== "debug") return;
|
|
11529
11688
|
try {
|
|
11530
|
-
const contextDir =
|
|
11689
|
+
const contextDir = join6(this.logDir, "context", sessionId);
|
|
11531
11690
|
if (!existsSync5(contextDir)) {
|
|
11532
11691
|
await mkdir2(contextDir, { recursive: true });
|
|
11533
11692
|
}
|
|
@@ -11535,9 +11694,9 @@ var Logger = class {
|
|
|
11535
11694
|
(msg) => msg.parts && msg.parts.length > 0
|
|
11536
11695
|
);
|
|
11537
11696
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
11538
|
-
const contextFile =
|
|
11697
|
+
const contextFile = join6(contextDir, `${timestamp}.json`);
|
|
11539
11698
|
await writeFile2(contextFile, JSON.stringify(minimized, null, 2));
|
|
11540
|
-
const versionFile =
|
|
11699
|
+
const versionFile = join6(contextDir, "_version");
|
|
11541
11700
|
if (!existsSync5(versionFile)) {
|
|
11542
11701
|
await writeFile2(versionFile, LOG_VERSION);
|
|
11543
11702
|
}
|
|
@@ -11548,8 +11707,8 @@ var Logger = class {
|
|
|
11548
11707
|
|
|
11549
11708
|
// lib/prompts/store.ts
|
|
11550
11709
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync3, statSync as statSync3 } from "fs";
|
|
11551
|
-
import { join as
|
|
11552
|
-
import { homedir as
|
|
11710
|
+
import { join as join7, dirname as dirname3 } from "path";
|
|
11711
|
+
import { homedir as homedir5 } from "os";
|
|
11553
11712
|
|
|
11554
11713
|
// lib/prompts/system.ts
|
|
11555
11714
|
var ACP_STATUS_TOOL_LINE_RANGES = `- \`acp_status\` \u2014 Context status with compressible ranges. No args = overview + ranges. \`scope:"uncompressed"\` for range view; add \`view:"messages"\` for per-message listing with \`tool\`/\`sort\` filters. \`scope:"compressed"\` for block details.`;
|
|
@@ -11856,7 +12015,7 @@ function createBundledRuntimePrompts(candidatesEnabled) {
|
|
|
11856
12015
|
function findOpencodeDir2(startDir) {
|
|
11857
12016
|
let current = startDir;
|
|
11858
12017
|
while (current !== "/") {
|
|
11859
|
-
const candidate =
|
|
12018
|
+
const candidate = join7(current, ".opencode");
|
|
11860
12019
|
if (existsSync6(candidate)) {
|
|
11861
12020
|
try {
|
|
11862
12021
|
if (statSync3(candidate).isDirectory()) {
|
|
@@ -11865,7 +12024,7 @@ function findOpencodeDir2(startDir) {
|
|
|
11865
12024
|
} catch {
|
|
11866
12025
|
}
|
|
11867
12026
|
}
|
|
11868
|
-
const parent =
|
|
12027
|
+
const parent = dirname3(current);
|
|
11869
12028
|
if (parent === current) {
|
|
11870
12029
|
break;
|
|
11871
12030
|
}
|
|
@@ -11874,13 +12033,13 @@ function findOpencodeDir2(startDir) {
|
|
|
11874
12033
|
return null;
|
|
11875
12034
|
}
|
|
11876
12035
|
function resolvePromptPaths(workingDirectory) {
|
|
11877
|
-
const configHome = process.env.XDG_CONFIG_HOME ||
|
|
11878
|
-
const globalRoot =
|
|
11879
|
-
const defaultsDir =
|
|
11880
|
-
const globalOverridesDir =
|
|
11881
|
-
const configDirOverridesDir = process.env.OPENCODE_CONFIG_DIR ?
|
|
12036
|
+
const configHome = process.env.XDG_CONFIG_HOME || join7(homedir5(), ".config");
|
|
12037
|
+
const globalRoot = join7(configHome, "opencode", "acp-prompts");
|
|
12038
|
+
const defaultsDir = join7(globalRoot, "defaults");
|
|
12039
|
+
const globalOverridesDir = join7(globalRoot, "overrides");
|
|
12040
|
+
const configDirOverridesDir = process.env.OPENCODE_CONFIG_DIR ? join7(process.env.OPENCODE_CONFIG_DIR, "acp-prompts", "overrides") : null;
|
|
11882
12041
|
const opencodeDir = findOpencodeDir2(workingDirectory);
|
|
11883
|
-
const projectOverridesDir = opencodeDir ?
|
|
12042
|
+
const projectOverridesDir = opencodeDir ? join7(opencodeDir, "acp-prompts", "overrides") : null;
|
|
11884
12043
|
return {
|
|
11885
12044
|
defaultsDir,
|
|
11886
12045
|
globalOverridesDir,
|
|
@@ -12053,16 +12212,16 @@ var PromptStore = class {
|
|
|
12053
12212
|
const candidates = [];
|
|
12054
12213
|
if (this.paths.projectOverridesDir) {
|
|
12055
12214
|
candidates.push({
|
|
12056
|
-
path:
|
|
12215
|
+
path: join7(this.paths.projectOverridesDir, fileName)
|
|
12057
12216
|
});
|
|
12058
12217
|
}
|
|
12059
12218
|
if (this.paths.configDirOverridesDir) {
|
|
12060
12219
|
candidates.push({
|
|
12061
|
-
path:
|
|
12220
|
+
path: join7(this.paths.configDirOverridesDir, fileName)
|
|
12062
12221
|
});
|
|
12063
12222
|
}
|
|
12064
12223
|
candidates.push({
|
|
12065
|
-
path:
|
|
12224
|
+
path: join7(this.paths.globalOverridesDir, fileName)
|
|
12066
12225
|
});
|
|
12067
12226
|
return candidates;
|
|
12068
12227
|
}
|
|
@@ -12086,7 +12245,7 @@ var PromptStore = class {
|
|
|
12086
12245
|
const managedContent = buildDefaultPromptFileContent(
|
|
12087
12246
|
bundledEditable || bundledPrompts[definition.runtimeField]
|
|
12088
12247
|
);
|
|
12089
|
-
const filePath =
|
|
12248
|
+
const filePath = join7(this.paths.defaultsDir, definition.fileName);
|
|
12090
12249
|
try {
|
|
12091
12250
|
const existing = readFileIfExists(filePath);
|
|
12092
12251
|
if (existing === managedContent) {
|
|
@@ -12100,7 +12259,7 @@ var PromptStore = class {
|
|
|
12100
12259
|
});
|
|
12101
12260
|
}
|
|
12102
12261
|
}
|
|
12103
|
-
const readmePath =
|
|
12262
|
+
const readmePath = join7(this.paths.defaultsDir, DEFAULTS_README_FILE);
|
|
12104
12263
|
const readmeContent = buildDefaultsReadmeContent();
|
|
12105
12264
|
try {
|
|
12106
12265
|
const existing = readFileIfExists(readmePath);
|
|
@@ -12184,6 +12343,7 @@ function truncateLargeToolOutputs(state, config, logger, messages) {
|
|
|
12184
12343
|
const part = parts[pi];
|
|
12185
12344
|
if (part?.type !== "tool") continue;
|
|
12186
12345
|
if (part.state?.status !== "completed") continue;
|
|
12346
|
+
if (part.tool === "compress") continue;
|
|
12187
12347
|
const content = extractCompletedToolOutput(part);
|
|
12188
12348
|
if (content === void 0) continue;
|
|
12189
12349
|
if (content === "[Old tool result content cleared]") continue;
|
|
@@ -12224,6 +12384,74 @@ function truncateLargeToolOutputs(state, config, logger, messages) {
|
|
|
12224
12384
|
}
|
|
12225
12385
|
}
|
|
12226
12386
|
|
|
12387
|
+
// lib/messages/sticky-truncation.ts
|
|
12388
|
+
function collectToolParts(messages) {
|
|
12389
|
+
const parts = [];
|
|
12390
|
+
for (const message of messages) {
|
|
12391
|
+
const messageParts = Array.isArray(message.parts) ? message.parts : [];
|
|
12392
|
+
for (const part of messageParts) {
|
|
12393
|
+
if (part?.type === "tool") {
|
|
12394
|
+
parts.push(part);
|
|
12395
|
+
}
|
|
12396
|
+
}
|
|
12397
|
+
}
|
|
12398
|
+
return parts;
|
|
12399
|
+
}
|
|
12400
|
+
function applyStickyTruncations(state, messages, logger) {
|
|
12401
|
+
const frozen = state.stickyTruncations;
|
|
12402
|
+
if (Object.keys(frozen).length === 0) return;
|
|
12403
|
+
let replayed = 0;
|
|
12404
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12405
|
+
for (const part of collectToolParts(messages)) {
|
|
12406
|
+
const callID = typeof part.callID === "string" ? part.callID : void 0;
|
|
12407
|
+
if (!callID) continue;
|
|
12408
|
+
seen.add(callID);
|
|
12409
|
+
const recorded = frozen[callID];
|
|
12410
|
+
if (recorded === void 0) continue;
|
|
12411
|
+
if (!part.state) continue;
|
|
12412
|
+
if (part.state.output === recorded) continue;
|
|
12413
|
+
part.state.output = recorded;
|
|
12414
|
+
replayed++;
|
|
12415
|
+
}
|
|
12416
|
+
for (const callID of Object.keys(frozen)) {
|
|
12417
|
+
if (!seen.has(callID)) delete frozen[callID];
|
|
12418
|
+
}
|
|
12419
|
+
if (replayed > 0) {
|
|
12420
|
+
logger.debug("Sticky truncation: replayed frozen tool outputs", {
|
|
12421
|
+
replayed,
|
|
12422
|
+
frozen: Object.keys(frozen).length
|
|
12423
|
+
});
|
|
12424
|
+
}
|
|
12425
|
+
}
|
|
12426
|
+
function snapshotToolOutputs(messages) {
|
|
12427
|
+
const snapshot = /* @__PURE__ */ new Map();
|
|
12428
|
+
for (const part of collectToolParts(messages)) {
|
|
12429
|
+
const callID = typeof part.callID === "string" ? part.callID : void 0;
|
|
12430
|
+
if (!callID) continue;
|
|
12431
|
+
const content = extractCompletedToolOutput(part);
|
|
12432
|
+
if (content === void 0) continue;
|
|
12433
|
+
snapshot.set(callID, content);
|
|
12434
|
+
}
|
|
12435
|
+
return snapshot;
|
|
12436
|
+
}
|
|
12437
|
+
function recordStickyTruncations(state, messages, before, logger) {
|
|
12438
|
+
let recorded = 0;
|
|
12439
|
+
for (const part of collectToolParts(messages)) {
|
|
12440
|
+
const callID = typeof part.callID === "string" ? part.callID : void 0;
|
|
12441
|
+
if (!callID) continue;
|
|
12442
|
+
if (typeof part.state?.output !== "string") continue;
|
|
12443
|
+
const prior = before.get(callID);
|
|
12444
|
+
if (prior === void 0 || prior === part.state.output) continue;
|
|
12445
|
+
if (state.stickyTruncations[callID] === part.state.output) continue;
|
|
12446
|
+
state.stickyTruncations[callID] = part.state.output;
|
|
12447
|
+
recorded++;
|
|
12448
|
+
}
|
|
12449
|
+
if (recorded > 0) {
|
|
12450
|
+
logger.info("Sticky truncation: froze truncated tool outputs", { recorded });
|
|
12451
|
+
}
|
|
12452
|
+
return recorded;
|
|
12453
|
+
}
|
|
12454
|
+
|
|
12227
12455
|
// lib/messages/enforce-budget.ts
|
|
12228
12456
|
var DEFAULT_COMPLETION_RESERVE_TOKENS = 32768;
|
|
12229
12457
|
var TRUNCATION_MARKER2 = "[truncated for context space";
|
|
@@ -12574,7 +12802,7 @@ ${report}`;
|
|
|
12574
12802
|
// lib/commands/export.ts
|
|
12575
12803
|
import * as fs2 from "fs/promises";
|
|
12576
12804
|
import { existsSync as existsSync7 } from "fs";
|
|
12577
|
-
import { dirname as
|
|
12805
|
+
import { dirname as dirname4, isAbsolute as isAbsolute2, join as join8, resolve as resolve2 } from "path";
|
|
12578
12806
|
var ALL_TIERS = [1, 2, 3];
|
|
12579
12807
|
var TIER_NAMES = {
|
|
12580
12808
|
1: "Tier 1 \u2014 Capture",
|
|
@@ -12687,7 +12915,7 @@ function parseTiers(raw) {
|
|
|
12687
12915
|
}
|
|
12688
12916
|
function resolveDefaultOutputPath(sessionId, cwd2) {
|
|
12689
12917
|
const short = (sessionId || "session").slice(0, 8);
|
|
12690
|
-
return
|
|
12918
|
+
return join8(cwd2, ".opencode", `acp-export-${short}.md`);
|
|
12691
12919
|
}
|
|
12692
12920
|
function formatTokens2(n) {
|
|
12693
12921
|
if (!Number.isFinite(n) || n <= 0) return "0";
|
|
@@ -12815,9 +13043,9 @@ async function handleExportCommand(ctx, args) {
|
|
|
12815
13043
|
return;
|
|
12816
13044
|
}
|
|
12817
13045
|
const cwd2 = ctx.workingDirectory || process.cwd();
|
|
12818
|
-
const targetPath = options.outputPath !== "" ? isAbsolute2(options.outputPath) ? options.outputPath :
|
|
13046
|
+
const targetPath = options.outputPath !== "" ? isAbsolute2(options.outputPath) ? options.outputPath : resolve2(cwd2, options.outputPath) : resolveDefaultOutputPath(sessionId, cwd2);
|
|
12819
13047
|
try {
|
|
12820
|
-
const dir =
|
|
13048
|
+
const dir = dirname4(targetPath);
|
|
12821
13049
|
if (!existsSync7(dir)) {
|
|
12822
13050
|
await fs2.mkdir(dir, { recursive: true });
|
|
12823
13051
|
}
|
|
@@ -13594,13 +13822,32 @@ ${text}`);
|
|
|
13594
13822
|
prePruneTokens,
|
|
13595
13823
|
candidateMessages
|
|
13596
13824
|
);
|
|
13597
|
-
|
|
13598
|
-
state,
|
|
13825
|
+
const emergencyTruncationEnabled = applyCompressOverrides(
|
|
13599
13826
|
config,
|
|
13600
|
-
|
|
13601
|
-
|
|
13602
|
-
);
|
|
13603
|
-
|
|
13827
|
+
dropReasoningModel?.providerID ?? state.modelProviderID,
|
|
13828
|
+
dropReasoningModel?.modelID ?? state.modelID
|
|
13829
|
+
).compress.emergencyTruncation;
|
|
13830
|
+
if (emergencyTruncationEnabled === true) {
|
|
13831
|
+
applyStickyTruncations(state, output.messages, logger);
|
|
13832
|
+
const toolOutputsBefore = snapshotToolOutputs(output.messages);
|
|
13833
|
+
truncateLargeToolOutputs(
|
|
13834
|
+
state,
|
|
13835
|
+
config,
|
|
13836
|
+
logger,
|
|
13837
|
+
output.messages.filter((message) => !isSyntheticMessage(message))
|
|
13838
|
+
);
|
|
13839
|
+
enforceContextBudget(state, config, logger, output.messages);
|
|
13840
|
+
const frozenCount = recordStickyTruncations(
|
|
13841
|
+
state,
|
|
13842
|
+
output.messages,
|
|
13843
|
+
toolOutputsBefore,
|
|
13844
|
+
logger
|
|
13845
|
+
);
|
|
13846
|
+
if (frozenCount > 0) {
|
|
13847
|
+
saveSessionState(state, logger).catch(() => {
|
|
13848
|
+
});
|
|
13849
|
+
}
|
|
13850
|
+
}
|
|
13604
13851
|
injectMessageIds(state, config, output.messages, compressionPriorities);
|
|
13605
13852
|
hideFailedCompressCalls(output.messages);
|
|
13606
13853
|
stripStaleMetadata(output.messages);
|
|
@@ -13828,7 +14075,7 @@ function configureClientAuth(client) {
|
|
|
13828
14075
|
|
|
13829
14076
|
// lib/update.ts
|
|
13830
14077
|
import { readFile as readFile2, rm } from "fs/promises";
|
|
13831
|
-
import { basename, dirname as
|
|
14078
|
+
import { basename as basename2, dirname as dirname5, join as join9 } from "path";
|
|
13832
14079
|
import { fileURLToPath } from "url";
|
|
13833
14080
|
var PACKAGE_NAME = "opencode-acp";
|
|
13834
14081
|
function startAutoUpdate(ctx, enabled, logger) {
|
|
@@ -13875,7 +14122,7 @@ async function checkAutoUpdate(signal, logger) {
|
|
|
13875
14122
|
logger?.info("Auto-update skipped: package dir not found");
|
|
13876
14123
|
return { updated: false };
|
|
13877
14124
|
}
|
|
13878
|
-
const pkg = await readPackageJson(
|
|
14125
|
+
const pkg = await readPackageJson(join9(packageDir, "package.json"));
|
|
13879
14126
|
if (!pkg?.name || !pkg.version) {
|
|
13880
14127
|
logger?.info("Auto-update skipped: package.json unreadable", { packageDir });
|
|
13881
14128
|
return { updated: false };
|
|
@@ -13914,21 +14161,21 @@ async function checkAutoUpdate(signal, logger) {
|
|
|
13914
14161
|
return { updated: true, name: pkg.name, current: pkg.version, latest };
|
|
13915
14162
|
}
|
|
13916
14163
|
async function findPackageDir(name) {
|
|
13917
|
-
let dir =
|
|
14164
|
+
let dir = dirname5(fileURLToPath(import.meta.url));
|
|
13918
14165
|
for (; ; ) {
|
|
13919
|
-
const pkg = await readPackageJson(
|
|
14166
|
+
const pkg = await readPackageJson(join9(dir, "package.json"));
|
|
13920
14167
|
if (pkg?.name === name) return dir;
|
|
13921
|
-
const parent =
|
|
14168
|
+
const parent = dirname5(dir);
|
|
13922
14169
|
if (parent === dir) return void 0;
|
|
13923
14170
|
dir = parent;
|
|
13924
14171
|
}
|
|
13925
14172
|
}
|
|
13926
14173
|
async function updateTarget(packageDir, name) {
|
|
13927
|
-
const packageParent =
|
|
13928
|
-
const nodeModulesDir =
|
|
13929
|
-
if (
|
|
13930
|
-
const wrapperDir =
|
|
13931
|
-
const wrapperPkg = await readPackageJson(
|
|
14174
|
+
const packageParent = dirname5(packageDir);
|
|
14175
|
+
const nodeModulesDir = basename2(packageParent).startsWith("@") ? dirname5(packageParent) : packageParent;
|
|
14176
|
+
if (basename2(nodeModulesDir) !== "node_modules") return void 0;
|
|
14177
|
+
const wrapperDir = dirname5(nodeModulesDir);
|
|
14178
|
+
const wrapperPkg = await readPackageJson(join9(wrapperDir, "package.json"));
|
|
13932
14179
|
const spec = wrapperSpec(wrapperDir, name) ?? wrapperPkg?.dependencies?.[name];
|
|
13933
14180
|
if (!spec || !isAutoUpdatableSpec(spec)) return void 0;
|
|
13934
14181
|
return { removeDir: wrapperDir, spec };
|
|
@@ -13936,13 +14183,13 @@ async function updateTarget(packageDir, name) {
|
|
|
13936
14183
|
function wrapperSpec(wrapperDir, name) {
|
|
13937
14184
|
if (name.startsWith("@")) {
|
|
13938
14185
|
const [scope, pkg] = name.split("/");
|
|
13939
|
-
if (!scope || !pkg ||
|
|
14186
|
+
if (!scope || !pkg || basename2(dirname5(wrapperDir)) !== scope) return void 0;
|
|
13940
14187
|
const prefix2 = `${pkg}@`;
|
|
13941
|
-
const base2 =
|
|
14188
|
+
const base2 = basename2(wrapperDir);
|
|
13942
14189
|
return base2.startsWith(prefix2) ? base2.slice(prefix2.length) : void 0;
|
|
13943
14190
|
}
|
|
13944
14191
|
const prefix = `${name}@`;
|
|
13945
|
-
const base =
|
|
14192
|
+
const base = basename2(wrapperDir);
|
|
13946
14193
|
return base.startsWith(prefix) ? base.slice(prefix.length) : void 0;
|
|
13947
14194
|
}
|
|
13948
14195
|
function isAutoUpdatableSpec(spec) {
|
|
@@ -14039,7 +14286,7 @@ var server = (async (ctx) => {
|
|
|
14039
14286
|
}
|
|
14040
14287
|
const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
|
|
14041
14288
|
logger.info("ACP plugin initialized", {
|
|
14042
|
-
version: true ? "1.18.
|
|
14289
|
+
version: true ? "1.18.5-pr.416.226" : "dev",
|
|
14043
14290
|
workspace: ctx.directory,
|
|
14044
14291
|
logLevel: logger.level,
|
|
14045
14292
|
debug: config.debug,
|