opencode-acp 1.18.1-pr.416.178 → 1.18.1-pr.432.180
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 +274 -359
- 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/hooks.d.ts +1 -1
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/message-ids.d.ts +0 -8
- package/dist/lib/message-ids.d.ts.map +1 -1
- package/dist/lib/messages/index.d.ts +1 -1
- package/dist/lib/messages/index.d.ts.map +1 -1
- package/dist/lib/messages/utils.d.ts +4 -0
- package/dist/lib/messages/utils.d.ts.map +1 -1
- package/dist/lib/state/rebuild.d.ts.map +1 -1
- package/dist/lib/state/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/lib/compress/tofile-target.d.ts +0 -31
- package/dist/lib/compress/tofile-target.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -2479,163 +2479,6 @@ import { existsSync as existsSync2 } from "fs";
|
|
|
2479
2479
|
import { homedir as homedir2 } from "os";
|
|
2480
2480
|
import { isAbsolute, join as join2 } from "path";
|
|
2481
2481
|
|
|
2482
|
-
// lib/message-ids.ts
|
|
2483
|
-
var MESSAGE_REF_REGEX = /^m(\d{4,5})$/;
|
|
2484
|
-
var BLOCK_REF_REGEX = /^b([1-9]\d*)$/;
|
|
2485
|
-
var MESSAGE_ID_TAG_NAME = "dcp-message-id";
|
|
2486
|
-
var MESSAGE_REF_WIDTH = 5;
|
|
2487
|
-
var MESSAGE_REF_MIN_INDEX = 1;
|
|
2488
|
-
var MESSAGE_REF_MAX_INDEX = 99999;
|
|
2489
|
-
function formatMessageRef(index) {
|
|
2490
|
-
if (!Number.isInteger(index) || index < MESSAGE_REF_MIN_INDEX || index > MESSAGE_REF_MAX_INDEX) {
|
|
2491
|
-
throw new Error(
|
|
2492
|
-
`Message ID index out of bounds: ${index}. Supported range is ${MESSAGE_REF_MIN_INDEX}-${MESSAGE_REF_MAX_INDEX}.`
|
|
2493
|
-
);
|
|
2494
|
-
}
|
|
2495
|
-
return `m${index.toString().padStart(MESSAGE_REF_WIDTH, "0")}`;
|
|
2496
|
-
}
|
|
2497
|
-
function formatBlockRef(blockId) {
|
|
2498
|
-
if (!Number.isInteger(blockId) || blockId < 1) {
|
|
2499
|
-
throw new Error(`Invalid block ID: ${blockId}`);
|
|
2500
|
-
}
|
|
2501
|
-
return `b${blockId}`;
|
|
2502
|
-
}
|
|
2503
|
-
function parseMessageRef(ref) {
|
|
2504
|
-
const normalized = ref.trim().toLowerCase();
|
|
2505
|
-
const match = normalized.match(MESSAGE_REF_REGEX);
|
|
2506
|
-
if (!match) {
|
|
2507
|
-
return null;
|
|
2508
|
-
}
|
|
2509
|
-
const index = Number.parseInt(match[1], 10);
|
|
2510
|
-
if (!Number.isInteger(index)) {
|
|
2511
|
-
return null;
|
|
2512
|
-
}
|
|
2513
|
-
if (index < MESSAGE_REF_MIN_INDEX || index > MESSAGE_REF_MAX_INDEX) {
|
|
2514
|
-
return null;
|
|
2515
|
-
}
|
|
2516
|
-
return index;
|
|
2517
|
-
}
|
|
2518
|
-
function migrateMessageRef(ref) {
|
|
2519
|
-
const index = parseMessageRef(ref);
|
|
2520
|
-
return index === null ? ref : formatMessageRef(index);
|
|
2521
|
-
}
|
|
2522
|
-
function parseBlockRef(ref) {
|
|
2523
|
-
const normalized = ref.trim().toLowerCase();
|
|
2524
|
-
const match = normalized.match(BLOCK_REF_REGEX);
|
|
2525
|
-
if (!match) {
|
|
2526
|
-
return null;
|
|
2527
|
-
}
|
|
2528
|
-
const id = Number.parseInt(match[1], 10);
|
|
2529
|
-
return Number.isInteger(id) ? id : null;
|
|
2530
|
-
}
|
|
2531
|
-
function parseBoundaryId(id) {
|
|
2532
|
-
const normalized = id.trim().toLowerCase();
|
|
2533
|
-
const messageIndex = parseMessageRef(normalized);
|
|
2534
|
-
if (messageIndex !== null) {
|
|
2535
|
-
return {
|
|
2536
|
-
kind: "message",
|
|
2537
|
-
ref: formatMessageRef(messageIndex),
|
|
2538
|
-
index: messageIndex
|
|
2539
|
-
};
|
|
2540
|
-
}
|
|
2541
|
-
const blockId = parseBlockRef(normalized);
|
|
2542
|
-
if (blockId !== null) {
|
|
2543
|
-
return {
|
|
2544
|
-
kind: "compressed-block",
|
|
2545
|
-
ref: formatBlockRef(blockId),
|
|
2546
|
-
blockId
|
|
2547
|
-
};
|
|
2548
|
-
}
|
|
2549
|
-
return null;
|
|
2550
|
-
}
|
|
2551
|
-
function escapeXmlAttribute(value) {
|
|
2552
|
-
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
2553
|
-
}
|
|
2554
|
-
function formatMessageIdTag(ref, attributes) {
|
|
2555
|
-
const serializedAttributes = Object.entries(attributes || {}).sort(([left], [right]) => left.localeCompare(right)).map(([name, value]) => {
|
|
2556
|
-
if (name.trim().length === 0 || typeof value !== "string" || value.length === 0) {
|
|
2557
|
-
return "";
|
|
2558
|
-
}
|
|
2559
|
-
return ` ${name}="${escapeXmlAttribute(value)}"`;
|
|
2560
|
-
}).join("");
|
|
2561
|
-
return `
|
|
2562
|
-
<${MESSAGE_ID_TAG_NAME}${serializedAttributes}>${ref}</${MESSAGE_ID_TAG_NAME}>`;
|
|
2563
|
-
}
|
|
2564
|
-
function formatTokenSize(tokens) {
|
|
2565
|
-
if (tokens < 1e3) return String(tokens);
|
|
2566
|
-
if (tokens < 1e4) return `${(tokens / 1e3).toFixed(1)}K`;
|
|
2567
|
-
return `${Math.round(tokens / 1e3)}K`;
|
|
2568
|
-
}
|
|
2569
|
-
function classifyMessageType(parts) {
|
|
2570
|
-
let hasTool = false;
|
|
2571
|
-
let hasText = false;
|
|
2572
|
-
let hasReasoning = false;
|
|
2573
|
-
const toolNames = [];
|
|
2574
|
-
for (const part of parts) {
|
|
2575
|
-
if (part.type === "tool") {
|
|
2576
|
-
hasTool = true;
|
|
2577
|
-
if (typeof part.tool === "string" && !toolNames.includes(part.tool)) {
|
|
2578
|
-
toolNames.push(part.tool);
|
|
2579
|
-
}
|
|
2580
|
-
} else if (part.type === "text") {
|
|
2581
|
-
hasText = true;
|
|
2582
|
-
} else if (part.type === "reasoning") {
|
|
2583
|
-
hasReasoning = true;
|
|
2584
|
-
}
|
|
2585
|
-
}
|
|
2586
|
-
if (hasTool) {
|
|
2587
|
-
return toolNames.length > 0 ? `tool:${toolNames.join(",")}` : "tool";
|
|
2588
|
-
}
|
|
2589
|
-
if (hasReasoning && !hasText) return "reasoning";
|
|
2590
|
-
return "text";
|
|
2591
|
-
}
|
|
2592
|
-
function assignMessageRefs(state, messages) {
|
|
2593
|
-
let assigned = 0;
|
|
2594
|
-
let skippedSubAgentPrompt = false;
|
|
2595
|
-
for (const message of messages) {
|
|
2596
|
-
if (isIgnoredUserMessage(message)) {
|
|
2597
|
-
continue;
|
|
2598
|
-
}
|
|
2599
|
-
if (state.isSubAgent && !skippedSubAgentPrompt && message.info.role === "user") {
|
|
2600
|
-
skippedSubAgentPrompt = true;
|
|
2601
|
-
continue;
|
|
2602
|
-
}
|
|
2603
|
-
const rawMessageId = message.info.id;
|
|
2604
|
-
if (typeof rawMessageId !== "string" || rawMessageId.length === 0) {
|
|
2605
|
-
continue;
|
|
2606
|
-
}
|
|
2607
|
-
if (rawMessageId.startsWith("msg_dcp_summary_") || rawMessageId.startsWith("msg_dcp_text_")) {
|
|
2608
|
-
continue;
|
|
2609
|
-
}
|
|
2610
|
-
const existingRef = state.messageIds.byRawId.get(rawMessageId);
|
|
2611
|
-
if (existingRef) {
|
|
2612
|
-
if (state.messageIds.byRef.get(existingRef) !== rawMessageId) {
|
|
2613
|
-
state.messageIds.byRef.set(existingRef, rawMessageId);
|
|
2614
|
-
}
|
|
2615
|
-
continue;
|
|
2616
|
-
}
|
|
2617
|
-
const ref = allocateNextMessageRef(state);
|
|
2618
|
-
state.messageIds.byRawId.set(rawMessageId, ref);
|
|
2619
|
-
state.messageIds.byRef.set(ref, rawMessageId);
|
|
2620
|
-
assigned++;
|
|
2621
|
-
}
|
|
2622
|
-
return assigned;
|
|
2623
|
-
}
|
|
2624
|
-
function allocateNextMessageRef(state) {
|
|
2625
|
-
let candidate = Number.isInteger(state.messageIds.nextRef) ? Math.max(MESSAGE_REF_MIN_INDEX, state.messageIds.nextRef) : MESSAGE_REF_MIN_INDEX;
|
|
2626
|
-
while (candidate <= MESSAGE_REF_MAX_INDEX) {
|
|
2627
|
-
const ref = formatMessageRef(candidate);
|
|
2628
|
-
if (!state.messageIds.byRef.has(ref)) {
|
|
2629
|
-
state.messageIds.nextRef = candidate + 1;
|
|
2630
|
-
return ref;
|
|
2631
|
-
}
|
|
2632
|
-
candidate++;
|
|
2633
|
-
}
|
|
2634
|
-
throw new Error(
|
|
2635
|
-
`Message ID alias capacity exceeded. Cannot allocate more than ${formatMessageRef(MESSAGE_REF_MAX_INDEX)} aliases in this session.`
|
|
2636
|
-
);
|
|
2637
|
-
}
|
|
2638
|
-
|
|
2639
2482
|
// lib/state/utils.ts
|
|
2640
2483
|
var isMessageCompacted = (state, msg) => {
|
|
2641
2484
|
if (!isMessageWithInfo(msg)) {
|
|
@@ -2787,10 +2630,8 @@ function loadPruneMessagesState(persisted) {
|
|
|
2787
2630
|
mode: block.mode === "range" || block.mode === "message" ? block.mode : void 0,
|
|
2788
2631
|
topic: typeof block.topic === "string" ? block.topic : "",
|
|
2789
2632
|
batchTopic: typeof block.batchTopic === "string" ? block.batchTopic : typeof block.topic === "string" ? block.topic : "",
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
startId: typeof block.startId === "string" ? migrateMessageRef(block.startId) : "",
|
|
2793
|
-
endId: typeof block.endId === "string" ? migrateMessageRef(block.endId) : "",
|
|
2633
|
+
startId: typeof block.startId === "string" ? block.startId : "",
|
|
2634
|
+
endId: typeof block.endId === "string" ? block.endId : "",
|
|
2794
2635
|
anchorMessageId: typeof block.anchorMessageId === "string" ? block.anchorMessageId : "",
|
|
2795
2636
|
compressMessageId: typeof block.compressMessageId === "string" ? block.compressMessageId : "",
|
|
2796
2637
|
compressCallId: typeof block.compressCallId === "string" ? block.compressCallId : void 0,
|
|
@@ -3020,8 +2861,8 @@ function saveSessionState(sessionState, logger, sessionName) {
|
|
|
3020
2861
|
queue = { pending: [], waiters: [], draining: false };
|
|
3021
2862
|
saveQueues.set(key, queue);
|
|
3022
2863
|
}
|
|
3023
|
-
const promise = new Promise((
|
|
3024
|
-
queue.waiters.push({ resolve:
|
|
2864
|
+
const promise = new Promise((resolve2, reject) => {
|
|
2865
|
+
queue.waiters.push({ resolve: resolve2, reject });
|
|
3025
2866
|
});
|
|
3026
2867
|
queue.pending.push({
|
|
3027
2868
|
sessionId: sessionState.sessionId,
|
|
@@ -3145,6 +2986,159 @@ import { existsSync as existsSync3 } from "fs";
|
|
|
3145
2986
|
import { join as join3 } from "path";
|
|
3146
2987
|
import { cwd } from "process";
|
|
3147
2988
|
|
|
2989
|
+
// lib/message-ids.ts
|
|
2990
|
+
var MESSAGE_REF_REGEX = /^m(\d{4,5})$/;
|
|
2991
|
+
var BLOCK_REF_REGEX = /^b([1-9]\d*)$/;
|
|
2992
|
+
var MESSAGE_ID_TAG_NAME = "dcp-message-id";
|
|
2993
|
+
var MESSAGE_REF_WIDTH = 5;
|
|
2994
|
+
var MESSAGE_REF_MIN_INDEX = 1;
|
|
2995
|
+
var MESSAGE_REF_MAX_INDEX = 99999;
|
|
2996
|
+
function formatMessageRef(index) {
|
|
2997
|
+
if (!Number.isInteger(index) || index < MESSAGE_REF_MIN_INDEX || index > MESSAGE_REF_MAX_INDEX) {
|
|
2998
|
+
throw new Error(
|
|
2999
|
+
`Message ID index out of bounds: ${index}. Supported range is ${MESSAGE_REF_MIN_INDEX}-${MESSAGE_REF_MAX_INDEX}.`
|
|
3000
|
+
);
|
|
3001
|
+
}
|
|
3002
|
+
return `m${index.toString().padStart(MESSAGE_REF_WIDTH, "0")}`;
|
|
3003
|
+
}
|
|
3004
|
+
function formatBlockRef(blockId) {
|
|
3005
|
+
if (!Number.isInteger(blockId) || blockId < 1) {
|
|
3006
|
+
throw new Error(`Invalid block ID: ${blockId}`);
|
|
3007
|
+
}
|
|
3008
|
+
return `b${blockId}`;
|
|
3009
|
+
}
|
|
3010
|
+
function parseMessageRef(ref) {
|
|
3011
|
+
const normalized = ref.trim().toLowerCase();
|
|
3012
|
+
const match = normalized.match(MESSAGE_REF_REGEX);
|
|
3013
|
+
if (!match) {
|
|
3014
|
+
return null;
|
|
3015
|
+
}
|
|
3016
|
+
const index = Number.parseInt(match[1], 10);
|
|
3017
|
+
if (!Number.isInteger(index)) {
|
|
3018
|
+
return null;
|
|
3019
|
+
}
|
|
3020
|
+
if (index < MESSAGE_REF_MIN_INDEX || index > MESSAGE_REF_MAX_INDEX) {
|
|
3021
|
+
return null;
|
|
3022
|
+
}
|
|
3023
|
+
return index;
|
|
3024
|
+
}
|
|
3025
|
+
function parseBlockRef(ref) {
|
|
3026
|
+
const normalized = ref.trim().toLowerCase();
|
|
3027
|
+
const match = normalized.match(BLOCK_REF_REGEX);
|
|
3028
|
+
if (!match) {
|
|
3029
|
+
return null;
|
|
3030
|
+
}
|
|
3031
|
+
const id = Number.parseInt(match[1], 10);
|
|
3032
|
+
return Number.isInteger(id) ? id : null;
|
|
3033
|
+
}
|
|
3034
|
+
function parseBoundaryId(id) {
|
|
3035
|
+
const normalized = id.trim().toLowerCase();
|
|
3036
|
+
const messageIndex = parseMessageRef(normalized);
|
|
3037
|
+
if (messageIndex !== null) {
|
|
3038
|
+
return {
|
|
3039
|
+
kind: "message",
|
|
3040
|
+
ref: formatMessageRef(messageIndex),
|
|
3041
|
+
index: messageIndex
|
|
3042
|
+
};
|
|
3043
|
+
}
|
|
3044
|
+
const blockId = parseBlockRef(normalized);
|
|
3045
|
+
if (blockId !== null) {
|
|
3046
|
+
return {
|
|
3047
|
+
kind: "compressed-block",
|
|
3048
|
+
ref: formatBlockRef(blockId),
|
|
3049
|
+
blockId
|
|
3050
|
+
};
|
|
3051
|
+
}
|
|
3052
|
+
return null;
|
|
3053
|
+
}
|
|
3054
|
+
function escapeXmlAttribute(value) {
|
|
3055
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
3056
|
+
}
|
|
3057
|
+
function formatMessageIdTag(ref, attributes) {
|
|
3058
|
+
const serializedAttributes = Object.entries(attributes || {}).sort(([left], [right]) => left.localeCompare(right)).map(([name, value]) => {
|
|
3059
|
+
if (name.trim().length === 0 || typeof value !== "string" || value.length === 0) {
|
|
3060
|
+
return "";
|
|
3061
|
+
}
|
|
3062
|
+
return ` ${name}="${escapeXmlAttribute(value)}"`;
|
|
3063
|
+
}).join("");
|
|
3064
|
+
return `
|
|
3065
|
+
<${MESSAGE_ID_TAG_NAME}${serializedAttributes}>${ref}</${MESSAGE_ID_TAG_NAME}>`;
|
|
3066
|
+
}
|
|
3067
|
+
function formatTokenSize(tokens) {
|
|
3068
|
+
if (tokens < 1e3) return String(tokens);
|
|
3069
|
+
if (tokens < 1e4) return `${(tokens / 1e3).toFixed(1)}K`;
|
|
3070
|
+
return `${Math.round(tokens / 1e3)}K`;
|
|
3071
|
+
}
|
|
3072
|
+
function classifyMessageType(parts) {
|
|
3073
|
+
let hasTool = false;
|
|
3074
|
+
let hasText = false;
|
|
3075
|
+
let hasReasoning = false;
|
|
3076
|
+
const toolNames = [];
|
|
3077
|
+
for (const part of parts) {
|
|
3078
|
+
if (part.type === "tool") {
|
|
3079
|
+
hasTool = true;
|
|
3080
|
+
if (typeof part.tool === "string" && !toolNames.includes(part.tool)) {
|
|
3081
|
+
toolNames.push(part.tool);
|
|
3082
|
+
}
|
|
3083
|
+
} else if (part.type === "text") {
|
|
3084
|
+
hasText = true;
|
|
3085
|
+
} else if (part.type === "reasoning") {
|
|
3086
|
+
hasReasoning = true;
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
if (hasTool) {
|
|
3090
|
+
return toolNames.length > 0 ? `tool:${toolNames.join(",")}` : "tool";
|
|
3091
|
+
}
|
|
3092
|
+
if (hasReasoning && !hasText) return "reasoning";
|
|
3093
|
+
return "text";
|
|
3094
|
+
}
|
|
3095
|
+
function assignMessageRefs(state, messages) {
|
|
3096
|
+
let assigned = 0;
|
|
3097
|
+
let skippedSubAgentPrompt = false;
|
|
3098
|
+
for (const message of messages) {
|
|
3099
|
+
if (isIgnoredUserMessage(message)) {
|
|
3100
|
+
continue;
|
|
3101
|
+
}
|
|
3102
|
+
if (state.isSubAgent && !skippedSubAgentPrompt && message.info.role === "user") {
|
|
3103
|
+
skippedSubAgentPrompt = true;
|
|
3104
|
+
continue;
|
|
3105
|
+
}
|
|
3106
|
+
const rawMessageId = message.info.id;
|
|
3107
|
+
if (typeof rawMessageId !== "string" || rawMessageId.length === 0) {
|
|
3108
|
+
continue;
|
|
3109
|
+
}
|
|
3110
|
+
if (rawMessageId.startsWith("msg_dcp_summary_") || rawMessageId.startsWith("msg_dcp_text_")) {
|
|
3111
|
+
continue;
|
|
3112
|
+
}
|
|
3113
|
+
const existingRef = state.messageIds.byRawId.get(rawMessageId);
|
|
3114
|
+
if (existingRef) {
|
|
3115
|
+
if (state.messageIds.byRef.get(existingRef) !== rawMessageId) {
|
|
3116
|
+
state.messageIds.byRef.set(existingRef, rawMessageId);
|
|
3117
|
+
}
|
|
3118
|
+
continue;
|
|
3119
|
+
}
|
|
3120
|
+
const ref = allocateNextMessageRef(state);
|
|
3121
|
+
state.messageIds.byRawId.set(rawMessageId, ref);
|
|
3122
|
+
state.messageIds.byRef.set(ref, rawMessageId);
|
|
3123
|
+
assigned++;
|
|
3124
|
+
}
|
|
3125
|
+
return assigned;
|
|
3126
|
+
}
|
|
3127
|
+
function allocateNextMessageRef(state) {
|
|
3128
|
+
let candidate = Number.isInteger(state.messageIds.nextRef) ? Math.max(MESSAGE_REF_MIN_INDEX, state.messageIds.nextRef) : MESSAGE_REF_MIN_INDEX;
|
|
3129
|
+
while (candidate <= MESSAGE_REF_MAX_INDEX) {
|
|
3130
|
+
const ref = formatMessageRef(candidate);
|
|
3131
|
+
if (!state.messageIds.byRef.has(ref)) {
|
|
3132
|
+
state.messageIds.nextRef = candidate + 1;
|
|
3133
|
+
return ref;
|
|
3134
|
+
}
|
|
3135
|
+
candidate++;
|
|
3136
|
+
}
|
|
3137
|
+
throw new Error(
|
|
3138
|
+
`Message ID alias capacity exceeded. Cannot allocate more than ${formatMessageRef(MESSAGE_REF_MAX_INDEX)} aliases in this session.`
|
|
3139
|
+
);
|
|
3140
|
+
}
|
|
3141
|
+
|
|
3148
3142
|
// lib/compress/state.ts
|
|
3149
3143
|
var DEFAULT_PROMOTION_THRESHOLD = 5;
|
|
3150
3144
|
var COMPRESSED_BLOCK_HEADER = "[Compressed conversation section]";
|
|
@@ -4616,11 +4610,7 @@ function restoreForkCompressionState(state, forkMessages, parent, parentMessages
|
|
|
4616
4610
|
directMessageIds,
|
|
4617
4611
|
directToolIds,
|
|
4618
4612
|
effectiveMessageIds,
|
|
4619
|
-
effectiveToolIds
|
|
4620
|
-
// [Issue #415] The parent record is raw persisted JSON; legacy
|
|
4621
|
-
// 4-digit boundary refs must be normalized like every other ref.
|
|
4622
|
-
startId: typeof block.startId === "string" ? migrateMessageRef(block.startId) : block.startId,
|
|
4623
|
-
endId: typeof block.endId === "string" ? migrateMessageRef(block.endId) : block.endId
|
|
4613
|
+
effectiveToolIds
|
|
4624
4614
|
});
|
|
4625
4615
|
}
|
|
4626
4616
|
const activeParentIds = new Set(parent.prune.messages.activeBlockIds);
|
|
@@ -6832,6 +6822,31 @@ function buildToolIdList(state, messages) {
|
|
|
6832
6822
|
var stripHallucinationsFromString = (text) => {
|
|
6833
6823
|
return text.replace(DCP_PAIRED_TAG_REGEX, "").replace(DCP_UNPAIRED_TAG_REGEX, "");
|
|
6834
6824
|
};
|
|
6825
|
+
var LEAKED_TRAILING_REF_REGEX = /(?:^|\n)[ \t]*(?:m(\d{4,5})|b([1-9]\d*))\b/g;
|
|
6826
|
+
var stripLeakedTrailingRefs = (text, bounds) => {
|
|
6827
|
+
if (typeof text !== "string" || text.length === 0) {
|
|
6828
|
+
return text;
|
|
6829
|
+
}
|
|
6830
|
+
const hasMessageBound = typeof bounds.nextMessageRef === "number" && Number.isInteger(bounds.nextMessageRef);
|
|
6831
|
+
const hasBlockBound = typeof bounds.nextBlockRef === "number" && Number.isInteger(bounds.nextBlockRef);
|
|
6832
|
+
if (!hasMessageBound && !hasBlockBound) {
|
|
6833
|
+
return text;
|
|
6834
|
+
}
|
|
6835
|
+
const pattern = new RegExp(LEAKED_TRAILING_REF_REGEX.source, "g");
|
|
6836
|
+
let match;
|
|
6837
|
+
while ((match = pattern.exec(text)) !== null) {
|
|
6838
|
+
if (match[1] !== void 0) {
|
|
6839
|
+
if (hasMessageBound && Number(match[1]) >= bounds.nextMessageRef) {
|
|
6840
|
+
return text.slice(0, match.index).replace(/[ \t\r\n]*$/, "");
|
|
6841
|
+
}
|
|
6842
|
+
} else if (match[2] !== void 0) {
|
|
6843
|
+
if (hasBlockBound && Number(match[2]) >= bounds.nextBlockRef) {
|
|
6844
|
+
return text.slice(0, match.index).replace(/[ \t\r\n]*$/, "");
|
|
6845
|
+
}
|
|
6846
|
+
}
|
|
6847
|
+
}
|
|
6848
|
+
return text;
|
|
6849
|
+
};
|
|
6835
6850
|
var stripHallucinations = (messages) => {
|
|
6836
6851
|
for (const message of messages) {
|
|
6837
6852
|
for (const part of message.parts) {
|
|
@@ -9598,102 +9613,6 @@ function extractTextContent(msg) {
|
|
|
9598
9613
|
return textParts.join(" ").replace(/\s+/g, " ").trim();
|
|
9599
9614
|
}
|
|
9600
9615
|
|
|
9601
|
-
// lib/compress/tofile-target.ts
|
|
9602
|
-
import * as fsp from "fs/promises";
|
|
9603
|
-
import { homedir as homedir3, tmpdir } from "os";
|
|
9604
|
-
import { dirname as dirname2, join as join4, relative, resolve } from "path";
|
|
9605
|
-
async function resolveSafeToFileTarget(targetPath, options) {
|
|
9606
|
-
const osTmp = tmpdir();
|
|
9607
|
-
const allowedDirs = options?.allowedDirs ?? [osTmp, join4(homedir3(), ".cache", "opencode")];
|
|
9608
|
-
const dirList = allowedDirs.join(" or ");
|
|
9609
|
-
if (typeof targetPath !== "string" || targetPath.length === 0) {
|
|
9610
|
-
return {
|
|
9611
|
-
ok: false,
|
|
9612
|
-
error: `Error: toFile path must be under ${dirList}. Got: ${targetPath}`
|
|
9613
|
-
};
|
|
9614
|
-
}
|
|
9615
|
-
const resolvedPath = resolve(targetPath);
|
|
9616
|
-
const lexicallyAllowed = allowedDirs.some((dir) => {
|
|
9617
|
-
const rel = relative(resolve(dir), resolvedPath);
|
|
9618
|
-
return rel === "" || !rel.startsWith("..") && !rel.startsWith("/") && !isWinAbsolute(rel);
|
|
9619
|
-
});
|
|
9620
|
-
if (!lexicallyAllowed) {
|
|
9621
|
-
return {
|
|
9622
|
-
ok: false,
|
|
9623
|
-
error: `Error: toFile path must be under ${dirList}. Got: ${targetPath}`
|
|
9624
|
-
};
|
|
9625
|
-
}
|
|
9626
|
-
let realAncestor = null;
|
|
9627
|
-
const tail = [];
|
|
9628
|
-
let current = resolvedPath;
|
|
9629
|
-
while (true) {
|
|
9630
|
-
try {
|
|
9631
|
-
realAncestor = await fsp.realpath(current);
|
|
9632
|
-
break;
|
|
9633
|
-
} catch (err) {
|
|
9634
|
-
const code = err.code;
|
|
9635
|
-
if (code !== "ENOENT" && code !== "ENOTDIR") {
|
|
9636
|
-
return {
|
|
9637
|
-
ok: false,
|
|
9638
|
-
error: `Error: cannot validate toFile path: ${err.message}`
|
|
9639
|
-
};
|
|
9640
|
-
}
|
|
9641
|
-
const parent = dirname2(current);
|
|
9642
|
-
if (parent === current) {
|
|
9643
|
-
return {
|
|
9644
|
-
ok: false,
|
|
9645
|
-
error: `Error: cannot validate toFile path: no existing ancestor found`
|
|
9646
|
-
};
|
|
9647
|
-
}
|
|
9648
|
-
tail.unshift(basename(current));
|
|
9649
|
-
current = parent;
|
|
9650
|
-
}
|
|
9651
|
-
}
|
|
9652
|
-
const physicalPath = tail.length > 0 ? join4(realAncestor, ...tail) : realAncestor;
|
|
9653
|
-
const realAllowedDirs = [];
|
|
9654
|
-
for (const dir of allowedDirs) {
|
|
9655
|
-
try {
|
|
9656
|
-
realAllowedDirs.push(await fsp.realpath(resolve(dir)));
|
|
9657
|
-
} catch {
|
|
9658
|
-
}
|
|
9659
|
-
}
|
|
9660
|
-
const physicallyAllowed = realAllowedDirs.some((dir) => containsOrIs(dir, physicalPath));
|
|
9661
|
-
if (!physicallyAllowed) {
|
|
9662
|
-
return {
|
|
9663
|
-
ok: false,
|
|
9664
|
-
error: `Error: toFile path resolves outside the allowed directories via a symlink. Got: ${targetPath}`
|
|
9665
|
-
};
|
|
9666
|
-
}
|
|
9667
|
-
try {
|
|
9668
|
-
const stat = await fsp.lstat(resolvedPath);
|
|
9669
|
-
if (stat.isSymbolicLink()) {
|
|
9670
|
-
return {
|
|
9671
|
-
ok: false,
|
|
9672
|
-
error: `Error: toFile target exists as a symlink and will not be followed. Got: ${targetPath}`
|
|
9673
|
-
};
|
|
9674
|
-
}
|
|
9675
|
-
} catch (err) {
|
|
9676
|
-
const code = err.code;
|
|
9677
|
-
if (code !== "ENOENT") {
|
|
9678
|
-
return {
|
|
9679
|
-
ok: false,
|
|
9680
|
-
error: `Error: cannot validate toFile path: ${err.message}`
|
|
9681
|
-
};
|
|
9682
|
-
}
|
|
9683
|
-
}
|
|
9684
|
-
return { ok: true, filePath: resolvedPath };
|
|
9685
|
-
}
|
|
9686
|
-
function isWinAbsolute(p) {
|
|
9687
|
-
return /^[a-zA-Z]:[\\/]/.test(p);
|
|
9688
|
-
}
|
|
9689
|
-
function basename(p) {
|
|
9690
|
-
return p.split(/[\\/]/).filter(Boolean).pop() ?? p;
|
|
9691
|
-
}
|
|
9692
|
-
function containsOrIs(root, p) {
|
|
9693
|
-
const rel = relative(root, p);
|
|
9694
|
-
return rel === "" || !rel.startsWith("..") && !rel.startsWith("/") && !isWinAbsolute(rel);
|
|
9695
|
-
}
|
|
9696
|
-
|
|
9697
9616
|
// lib/compress/decompress.ts
|
|
9698
9617
|
async function prepareDecompressSession(ctx, toolCtx) {
|
|
9699
9618
|
await toolCtx.ask({
|
|
@@ -9727,13 +9646,7 @@ function resolveTargets(args, state, rawMessages, logger) {
|
|
|
9727
9646
|
if (mode.mode === "block") {
|
|
9728
9647
|
return resolveSingleBlockTarget(messagesState, args.blockId);
|
|
9729
9648
|
}
|
|
9730
|
-
return resolveRangeTarget(
|
|
9731
|
-
state,
|
|
9732
|
-
rawMessages,
|
|
9733
|
-
args.startId,
|
|
9734
|
-
args.endId,
|
|
9735
|
-
logger
|
|
9736
|
-
);
|
|
9649
|
+
return resolveRangeTarget(state, rawMessages, args.startId, args.endId, logger);
|
|
9737
9650
|
}
|
|
9738
9651
|
function resolveSingleBlockTarget(messagesState, blockIdArg) {
|
|
9739
9652
|
const targetBlockId = parseBlockIdArg(blockIdArg);
|
|
@@ -9845,21 +9758,11 @@ IMPORTANT:
|
|
|
9845
9758
|
- Do NOT call this tool in parallel with compress \u2014 their state mutations may conflict.`;
|
|
9846
9759
|
function buildSchema2() {
|
|
9847
9760
|
return {
|
|
9848
|
-
blockId: tool3.schema.string().optional().describe(
|
|
9849
|
-
|
|
9850
|
-
),
|
|
9851
|
-
|
|
9852
|
-
|
|
9853
|
-
),
|
|
9854
|
-
endId: tool3.schema.string().optional().describe(
|
|
9855
|
-
'Range end: message ref (e.g., "m00200") or block ref (e.g., "b5"). Used with startId.'
|
|
9856
|
-
),
|
|
9857
|
-
toFile: tool3.schema.string().optional().describe(
|
|
9858
|
-
"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'"
|
|
9859
|
-
),
|
|
9860
|
-
full: tool3.schema.boolean().optional().describe(
|
|
9861
|
-
"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."
|
|
9862
|
-
)
|
|
9761
|
+
blockId: tool3.schema.string().optional().describe('Block reference to decompress (e.g., "b0", "b2"). Mutually exclusive with startId/endId.'),
|
|
9762
|
+
startId: tool3.schema.string().optional().describe('Range start: message ref (e.g., "m00150") or block ref (e.g., "b2"). Used with endId.'),
|
|
9763
|
+
endId: tool3.schema.string().optional().describe('Range end: message ref (e.g., "m00200") or block ref (e.g., "b5"). Used with startId.'),
|
|
9764
|
+
toFile: tool3.schema.string().optional().describe("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'"),
|
|
9765
|
+
full: tool3.schema.boolean().optional().describe("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.")
|
|
9863
9766
|
};
|
|
9864
9767
|
}
|
|
9865
9768
|
function extractMessageId(m) {
|
|
@@ -9883,12 +9786,7 @@ function createDecompressTool(factoryCtx) {
|
|
|
9883
9786
|
const contextUsageBefore = effectiveLimitBefore ? Math.round(
|
|
9884
9787
|
getCurrentTokenUsage(ctx.state, rawMessages) / effectiveLimitBefore.limit * 100
|
|
9885
9788
|
) : void 0;
|
|
9886
|
-
const resolved = resolveTargets(
|
|
9887
|
-
args,
|
|
9888
|
-
ctx.state,
|
|
9889
|
-
rawMessages,
|
|
9890
|
-
ctx.logger
|
|
9891
|
-
);
|
|
9789
|
+
const resolved = resolveTargets(args, ctx.state, rawMessages, ctx.logger);
|
|
9892
9790
|
if (!resolved.ok) {
|
|
9893
9791
|
return resolved.error;
|
|
9894
9792
|
}
|
|
@@ -9904,9 +9802,19 @@ function createDecompressTool(factoryCtx) {
|
|
|
9904
9802
|
}
|
|
9905
9803
|
if (args.toFile) {
|
|
9906
9804
|
const targetPath = args.toFile;
|
|
9907
|
-
const
|
|
9908
|
-
|
|
9909
|
-
|
|
9805
|
+
const os = await import("os");
|
|
9806
|
+
const path = await import("path");
|
|
9807
|
+
const allowedDirs = [
|
|
9808
|
+
os.tmpdir() + "/",
|
|
9809
|
+
path.join(os.homedir(), ".cache", "opencode") + "/"
|
|
9810
|
+
];
|
|
9811
|
+
const resolvedPath = path.resolve(targetPath);
|
|
9812
|
+
const isAllowed = allowedDirs.some((dir) => {
|
|
9813
|
+
const rel = path.relative(dir, resolvedPath);
|
|
9814
|
+
return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
9815
|
+
});
|
|
9816
|
+
if (!isAllowed) {
|
|
9817
|
+
return `Error: toFile path must be under ${os.tmpdir()} or ~/.cache/opencode/. Got: ${targetPath}`;
|
|
9910
9818
|
}
|
|
9911
9819
|
const msgIdSet = /* @__PURE__ */ new Set();
|
|
9912
9820
|
for (const block of activeBlocks) {
|
|
@@ -9916,22 +9824,11 @@ function createDecompressTool(factoryCtx) {
|
|
|
9916
9824
|
}
|
|
9917
9825
|
const blockMessages = rawMessages.filter((m) => msgIdSet.has(extractMessageId(m)));
|
|
9918
9826
|
const lines2 = blockMessages.map(extractMessageText2);
|
|
9919
|
-
const
|
|
9920
|
-
const { constants } = await import("fs");
|
|
9827
|
+
const { writeFile: writeFile4 } = await import("fs/promises");
|
|
9921
9828
|
const fileContent = lines2.length > 0 ? lines2.join("\n\n---\n\n") : targets[0]?.blocks[0]?.summary ?? "(no content available)";
|
|
9922
|
-
|
|
9923
|
-
try {
|
|
9924
|
-
const handle = await fsp2.open(safe.filePath, flags, 384);
|
|
9925
|
-
try {
|
|
9926
|
-
await handle.write(Buffer.from(fileContent, "utf-8"));
|
|
9927
|
-
} finally {
|
|
9928
|
-
await handle.close();
|
|
9929
|
-
}
|
|
9930
|
-
} catch (err) {
|
|
9931
|
-
return `Error: toFile write failed: ${err.message}`;
|
|
9932
|
-
}
|
|
9829
|
+
await writeFile4(targetPath, fileContent, "utf-8");
|
|
9933
9830
|
const displayIds2 = targets.map((t) => `b${t.displayId}`).join(", ");
|
|
9934
|
-
return `Block(s) ${displayIds2} content (${blockMessages.length} messages, ${fileContent.length} chars) written to ${
|
|
9831
|
+
return `Block(s) ${displayIds2} content (${blockMessages.length} messages, ${fileContent.length} chars) written to ${targetPath}. Block(s) stay compressed \u2014 context unchanged. Use read tool to access specific parts.`;
|
|
9935
9832
|
}
|
|
9936
9833
|
const activeMessagesBefore = snapshotActiveMessages(messagesState);
|
|
9937
9834
|
const activeBlockIdsBefore = new Set(messagesState.activeBlockIds);
|
|
@@ -10006,9 +9903,7 @@ function isLiveBlock(block) {
|
|
|
10006
9903
|
return block.active && !block.deactivatedByUser && !block.deactivatedByUserDeep;
|
|
10007
9904
|
}
|
|
10008
9905
|
function rangeKey(startId, endId) {
|
|
10009
|
-
|
|
10010
|
-
const e = typeof endId === "string" ? migrateMessageRef(endId) : endId;
|
|
10011
|
-
return `${s}::${e}`;
|
|
9906
|
+
return `${startId}::${endId}`;
|
|
10012
9907
|
}
|
|
10013
9908
|
function rewriteCompressInput(part, liveKeys) {
|
|
10014
9909
|
if (part.type !== "tool") return null;
|
|
@@ -10671,10 +10566,10 @@ function hideFailedCompressCalls(messages) {
|
|
|
10671
10566
|
|
|
10672
10567
|
// lib/logger.ts
|
|
10673
10568
|
import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
10674
|
-
import { join as
|
|
10569
|
+
import { join as join4 } from "path";
|
|
10675
10570
|
import { existsSync as existsSync4 } from "fs";
|
|
10676
|
-
import { homedir as
|
|
10677
|
-
var LOG_VERSION = true ? "1.18.1-pr.
|
|
10571
|
+
import { homedir as homedir3 } from "os";
|
|
10572
|
+
var LOG_VERSION = true ? "1.18.1-pr.432.180" : "dev";
|
|
10678
10573
|
var LEVEL_RANK = {
|
|
10679
10574
|
debug: 10,
|
|
10680
10575
|
info: 20,
|
|
@@ -10694,8 +10589,8 @@ var Logger = class {
|
|
|
10694
10589
|
constructor(enabled, level) {
|
|
10695
10590
|
const resolved = level !== void 0 && LEVEL_RANK[level] !== void 0 ? level : void 0;
|
|
10696
10591
|
this.level = resolved ?? (enabled ? "debug" : "warn");
|
|
10697
|
-
const configHome = process.env.XDG_CONFIG_HOME ||
|
|
10698
|
-
this.logDir =
|
|
10592
|
+
const configHome = process.env.XDG_CONFIG_HOME || join4(homedir3(), ".config");
|
|
10593
|
+
this.logDir = join4(configHome, "opencode", "logs", "acp");
|
|
10699
10594
|
}
|
|
10700
10595
|
/** Back-compat view of the old flag: true only at full debug verbosity. */
|
|
10701
10596
|
get enabled() {
|
|
@@ -10757,11 +10652,11 @@ var Logger = class {
|
|
|
10757
10652
|
const dataStr = this.formatData(data);
|
|
10758
10653
|
const logLine = `${timestamp} ${level.padEnd(5)} ${component}: ${message}${dataStr ? " | " + dataStr : ""} | v=${LOG_VERSION}
|
|
10759
10654
|
`;
|
|
10760
|
-
const dailyLogDir =
|
|
10655
|
+
const dailyLogDir = join4(this.logDir, "daily");
|
|
10761
10656
|
if (!existsSync4(dailyLogDir)) {
|
|
10762
10657
|
await mkdir2(dailyLogDir, { recursive: true });
|
|
10763
10658
|
}
|
|
10764
|
-
const logFile =
|
|
10659
|
+
const logFile = join4(dailyLogDir, `${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.log`);
|
|
10765
10660
|
await writeFile2(logFile, logLine, { flag: "a" });
|
|
10766
10661
|
} catch (error) {
|
|
10767
10662
|
}
|
|
@@ -10872,7 +10767,7 @@ var Logger = class {
|
|
|
10872
10767
|
async saveContext(sessionId, messages) {
|
|
10873
10768
|
if (this.level !== "debug") return;
|
|
10874
10769
|
try {
|
|
10875
|
-
const contextDir =
|
|
10770
|
+
const contextDir = join4(this.logDir, "context", sessionId);
|
|
10876
10771
|
if (!existsSync4(contextDir)) {
|
|
10877
10772
|
await mkdir2(contextDir, { recursive: true });
|
|
10878
10773
|
}
|
|
@@ -10880,9 +10775,9 @@ var Logger = class {
|
|
|
10880
10775
|
(msg) => msg.parts && msg.parts.length > 0
|
|
10881
10776
|
);
|
|
10882
10777
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
10883
|
-
const contextFile =
|
|
10778
|
+
const contextFile = join4(contextDir, `${timestamp}.json`);
|
|
10884
10779
|
await writeFile2(contextFile, JSON.stringify(minimized, null, 2));
|
|
10885
|
-
const versionFile =
|
|
10780
|
+
const versionFile = join4(contextDir, "_version");
|
|
10886
10781
|
if (!existsSync4(versionFile)) {
|
|
10887
10782
|
await writeFile2(versionFile, LOG_VERSION);
|
|
10888
10783
|
}
|
|
@@ -10893,8 +10788,8 @@ var Logger = class {
|
|
|
10893
10788
|
|
|
10894
10789
|
// lib/prompts/store.ts
|
|
10895
10790
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, statSync as statSync2 } from "fs";
|
|
10896
|
-
import { join as
|
|
10897
|
-
import { homedir as
|
|
10791
|
+
import { join as join5, dirname as dirname2 } from "path";
|
|
10792
|
+
import { homedir as homedir4 } from "os";
|
|
10898
10793
|
|
|
10899
10794
|
// lib/prompts/system.ts
|
|
10900
10795
|
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.`;
|
|
@@ -11201,7 +11096,7 @@ function createBundledRuntimePrompts(candidatesEnabled) {
|
|
|
11201
11096
|
function findOpencodeDir2(startDir) {
|
|
11202
11097
|
let current = startDir;
|
|
11203
11098
|
while (current !== "/") {
|
|
11204
|
-
const candidate =
|
|
11099
|
+
const candidate = join5(current, ".opencode");
|
|
11205
11100
|
if (existsSync5(candidate)) {
|
|
11206
11101
|
try {
|
|
11207
11102
|
if (statSync2(candidate).isDirectory()) {
|
|
@@ -11210,7 +11105,7 @@ function findOpencodeDir2(startDir) {
|
|
|
11210
11105
|
} catch {
|
|
11211
11106
|
}
|
|
11212
11107
|
}
|
|
11213
|
-
const parent =
|
|
11108
|
+
const parent = dirname2(current);
|
|
11214
11109
|
if (parent === current) {
|
|
11215
11110
|
break;
|
|
11216
11111
|
}
|
|
@@ -11219,13 +11114,13 @@ function findOpencodeDir2(startDir) {
|
|
|
11219
11114
|
return null;
|
|
11220
11115
|
}
|
|
11221
11116
|
function resolvePromptPaths(workingDirectory) {
|
|
11222
|
-
const configHome = process.env.XDG_CONFIG_HOME ||
|
|
11223
|
-
const globalRoot =
|
|
11224
|
-
const defaultsDir =
|
|
11225
|
-
const globalOverridesDir =
|
|
11226
|
-
const configDirOverridesDir = process.env.OPENCODE_CONFIG_DIR ?
|
|
11117
|
+
const configHome = process.env.XDG_CONFIG_HOME || join5(homedir4(), ".config");
|
|
11118
|
+
const globalRoot = join5(configHome, "opencode", "acp-prompts");
|
|
11119
|
+
const defaultsDir = join5(globalRoot, "defaults");
|
|
11120
|
+
const globalOverridesDir = join5(globalRoot, "overrides");
|
|
11121
|
+
const configDirOverridesDir = process.env.OPENCODE_CONFIG_DIR ? join5(process.env.OPENCODE_CONFIG_DIR, "acp-prompts", "overrides") : null;
|
|
11227
11122
|
const opencodeDir = findOpencodeDir2(workingDirectory);
|
|
11228
|
-
const projectOverridesDir = opencodeDir ?
|
|
11123
|
+
const projectOverridesDir = opencodeDir ? join5(opencodeDir, "acp-prompts", "overrides") : null;
|
|
11229
11124
|
return {
|
|
11230
11125
|
defaultsDir,
|
|
11231
11126
|
globalOverridesDir,
|
|
@@ -11398,16 +11293,16 @@ var PromptStore = class {
|
|
|
11398
11293
|
const candidates = [];
|
|
11399
11294
|
if (this.paths.projectOverridesDir) {
|
|
11400
11295
|
candidates.push({
|
|
11401
|
-
path:
|
|
11296
|
+
path: join5(this.paths.projectOverridesDir, fileName)
|
|
11402
11297
|
});
|
|
11403
11298
|
}
|
|
11404
11299
|
if (this.paths.configDirOverridesDir) {
|
|
11405
11300
|
candidates.push({
|
|
11406
|
-
path:
|
|
11301
|
+
path: join5(this.paths.configDirOverridesDir, fileName)
|
|
11407
11302
|
});
|
|
11408
11303
|
}
|
|
11409
11304
|
candidates.push({
|
|
11410
|
-
path:
|
|
11305
|
+
path: join5(this.paths.globalOverridesDir, fileName)
|
|
11411
11306
|
});
|
|
11412
11307
|
return candidates;
|
|
11413
11308
|
}
|
|
@@ -11431,7 +11326,7 @@ var PromptStore = class {
|
|
|
11431
11326
|
const managedContent = buildDefaultPromptFileContent(
|
|
11432
11327
|
bundledEditable || bundledPrompts[definition.runtimeField]
|
|
11433
11328
|
);
|
|
11434
|
-
const filePath =
|
|
11329
|
+
const filePath = join5(this.paths.defaultsDir, definition.fileName);
|
|
11435
11330
|
try {
|
|
11436
11331
|
const existing = readFileIfExists(filePath);
|
|
11437
11332
|
if (existing === managedContent) {
|
|
@@ -11445,7 +11340,7 @@ var PromptStore = class {
|
|
|
11445
11340
|
});
|
|
11446
11341
|
}
|
|
11447
11342
|
}
|
|
11448
|
-
const readmePath =
|
|
11343
|
+
const readmePath = join5(this.paths.defaultsDir, DEFAULTS_README_FILE);
|
|
11449
11344
|
const readmeContent = buildDefaultsReadmeContent();
|
|
11450
11345
|
try {
|
|
11451
11346
|
const existing = readFileIfExists(readmePath);
|
|
@@ -11910,7 +11805,7 @@ ${report}`;
|
|
|
11910
11805
|
// lib/commands/export.ts
|
|
11911
11806
|
import * as fs2 from "fs/promises";
|
|
11912
11807
|
import { existsSync as existsSync6 } from "fs";
|
|
11913
|
-
import { dirname as
|
|
11808
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join6, resolve } from "path";
|
|
11914
11809
|
var ALL_TIERS = [1, 2, 3];
|
|
11915
11810
|
var TIER_NAMES = {
|
|
11916
11811
|
1: "Tier 1 \u2014 Capture",
|
|
@@ -12023,7 +11918,7 @@ function parseTiers(raw) {
|
|
|
12023
11918
|
}
|
|
12024
11919
|
function resolveDefaultOutputPath(sessionId, cwd2) {
|
|
12025
11920
|
const short = (sessionId || "session").slice(0, 8);
|
|
12026
|
-
return
|
|
11921
|
+
return join6(cwd2, ".opencode", `acp-export-${short}.md`);
|
|
12027
11922
|
}
|
|
12028
11923
|
function formatTokens2(n) {
|
|
12029
11924
|
if (!Number.isFinite(n) || n <= 0) return "0";
|
|
@@ -12151,9 +12046,9 @@ async function handleExportCommand(ctx, args) {
|
|
|
12151
12046
|
return;
|
|
12152
12047
|
}
|
|
12153
12048
|
const cwd2 = ctx.workingDirectory || process.cwd();
|
|
12154
|
-
const targetPath = options.outputPath !== "" ? isAbsolute2(options.outputPath) ? options.outputPath :
|
|
12049
|
+
const targetPath = options.outputPath !== "" ? isAbsolute2(options.outputPath) ? options.outputPath : resolve(cwd2, options.outputPath) : resolveDefaultOutputPath(sessionId, cwd2);
|
|
12155
12050
|
try {
|
|
12156
|
-
const dir =
|
|
12051
|
+
const dir = dirname3(targetPath);
|
|
12157
12052
|
if (!existsSync6(dir)) {
|
|
12158
12053
|
await fs2.mkdir(dir, { recursive: true });
|
|
12159
12054
|
}
|
|
@@ -13014,9 +12909,29 @@ function createCommandExecuteHandler(client, registry4, logger, config, workingD
|
|
|
13014
12909
|
}
|
|
13015
12910
|
};
|
|
13016
12911
|
}
|
|
13017
|
-
function createTextCompleteHandler() {
|
|
13018
|
-
return async (
|
|
13019
|
-
output.text
|
|
12912
|
+
function createTextCompleteHandler(registry4, logger) {
|
|
12913
|
+
return async (input, output) => {
|
|
12914
|
+
if (typeof output.text !== "string" || output.text.length === 0) {
|
|
12915
|
+
return;
|
|
12916
|
+
}
|
|
12917
|
+
let text = stripHallucinationsFromString(output.text);
|
|
12918
|
+
const state = typeof input.sessionID === "string" ? registry4.get(input.sessionID) : void 0;
|
|
12919
|
+
if (state) {
|
|
12920
|
+
const stripped = stripLeakedTrailingRefs(text, {
|
|
12921
|
+
nextMessageRef: Number.isInteger(state.messageIds.nextRef) ? state.messageIds.nextRef : null,
|
|
12922
|
+
nextBlockRef: Number.isInteger(state.prune?.messages?.nextBlockId) ? state.prune.messages.nextBlockId : null
|
|
12923
|
+
});
|
|
12924
|
+
if (stripped !== text) {
|
|
12925
|
+
logger.warn("Stripped leaked trailing ACP ref from assistant output", {
|
|
12926
|
+
sessionID: input.sessionID,
|
|
12927
|
+
messageID: input.messageID,
|
|
12928
|
+
removedChars: text.length - stripped.length,
|
|
12929
|
+
removedTailPreview: text.slice(stripped.length, stripped.length + 200)
|
|
12930
|
+
});
|
|
12931
|
+
text = stripped;
|
|
12932
|
+
}
|
|
12933
|
+
}
|
|
12934
|
+
output.text = text;
|
|
13020
12935
|
};
|
|
13021
12936
|
}
|
|
13022
12937
|
function createEventHandler(registry4, logger) {
|
|
@@ -13146,7 +13061,7 @@ function findBiliProxyProviders(provider) {
|
|
|
13146
13061
|
|
|
13147
13062
|
// lib/update.ts
|
|
13148
13063
|
import { readFile as readFile2, rm } from "fs/promises";
|
|
13149
|
-
import { basename
|
|
13064
|
+
import { basename, dirname as dirname4, join as join7 } from "path";
|
|
13150
13065
|
import { fileURLToPath } from "url";
|
|
13151
13066
|
var PACKAGE_NAME = "opencode-acp";
|
|
13152
13067
|
function startAutoUpdate(ctx, enabled, logger) {
|
|
@@ -13193,7 +13108,7 @@ async function checkAutoUpdate(signal, logger) {
|
|
|
13193
13108
|
logger?.info("Auto-update skipped: package dir not found");
|
|
13194
13109
|
return { updated: false };
|
|
13195
13110
|
}
|
|
13196
|
-
const pkg = await readPackageJson(
|
|
13111
|
+
const pkg = await readPackageJson(join7(packageDir, "package.json"));
|
|
13197
13112
|
if (!pkg?.name || !pkg.version) {
|
|
13198
13113
|
logger?.info("Auto-update skipped: package.json unreadable", { packageDir });
|
|
13199
13114
|
return { updated: false };
|
|
@@ -13232,21 +13147,21 @@ async function checkAutoUpdate(signal, logger) {
|
|
|
13232
13147
|
return { updated: true, name: pkg.name, current: pkg.version, latest };
|
|
13233
13148
|
}
|
|
13234
13149
|
async function findPackageDir(name) {
|
|
13235
|
-
let dir =
|
|
13150
|
+
let dir = dirname4(fileURLToPath(import.meta.url));
|
|
13236
13151
|
for (; ; ) {
|
|
13237
|
-
const pkg = await readPackageJson(
|
|
13152
|
+
const pkg = await readPackageJson(join7(dir, "package.json"));
|
|
13238
13153
|
if (pkg?.name === name) return dir;
|
|
13239
|
-
const parent =
|
|
13154
|
+
const parent = dirname4(dir);
|
|
13240
13155
|
if (parent === dir) return void 0;
|
|
13241
13156
|
dir = parent;
|
|
13242
13157
|
}
|
|
13243
13158
|
}
|
|
13244
13159
|
async function updateTarget(packageDir, name) {
|
|
13245
|
-
const packageParent =
|
|
13246
|
-
const nodeModulesDir =
|
|
13247
|
-
if (
|
|
13248
|
-
const wrapperDir =
|
|
13249
|
-
const wrapperPkg = await readPackageJson(
|
|
13160
|
+
const packageParent = dirname4(packageDir);
|
|
13161
|
+
const nodeModulesDir = basename(packageParent).startsWith("@") ? dirname4(packageParent) : packageParent;
|
|
13162
|
+
if (basename(nodeModulesDir) !== "node_modules") return void 0;
|
|
13163
|
+
const wrapperDir = dirname4(nodeModulesDir);
|
|
13164
|
+
const wrapperPkg = await readPackageJson(join7(wrapperDir, "package.json"));
|
|
13250
13165
|
const spec = wrapperSpec(wrapperDir, name) ?? wrapperPkg?.dependencies?.[name];
|
|
13251
13166
|
if (!spec || !isAutoUpdatableSpec(spec)) return void 0;
|
|
13252
13167
|
return { removeDir: wrapperDir, spec };
|
|
@@ -13254,13 +13169,13 @@ async function updateTarget(packageDir, name) {
|
|
|
13254
13169
|
function wrapperSpec(wrapperDir, name) {
|
|
13255
13170
|
if (name.startsWith("@")) {
|
|
13256
13171
|
const [scope, pkg] = name.split("/");
|
|
13257
|
-
if (!scope || !pkg ||
|
|
13172
|
+
if (!scope || !pkg || basename(dirname4(wrapperDir)) !== scope) return void 0;
|
|
13258
13173
|
const prefix2 = `${pkg}@`;
|
|
13259
|
-
const base2 =
|
|
13174
|
+
const base2 = basename(wrapperDir);
|
|
13260
13175
|
return base2.startsWith(prefix2) ? base2.slice(prefix2.length) : void 0;
|
|
13261
13176
|
}
|
|
13262
13177
|
const prefix = `${name}@`;
|
|
13263
|
-
const base =
|
|
13178
|
+
const base = basename(wrapperDir);
|
|
13264
13179
|
return base.startsWith(prefix) ? base.slice(prefix.length) : void 0;
|
|
13265
13180
|
}
|
|
13266
13181
|
function isAutoUpdatableSpec(spec) {
|
|
@@ -13358,7 +13273,7 @@ var server = (async (ctx) => {
|
|
|
13358
13273
|
}
|
|
13359
13274
|
const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
|
|
13360
13275
|
logger.info("ACP plugin initialized", {
|
|
13361
|
-
version: true ? "1.18.1-pr.
|
|
13276
|
+
version: true ? "1.18.1-pr.432.180" : "dev",
|
|
13362
13277
|
workspace: ctx.directory,
|
|
13363
13278
|
logLevel: logger.level,
|
|
13364
13279
|
debug: config.debug,
|
|
@@ -13423,7 +13338,7 @@ var server = (async (ctx) => {
|
|
|
13423
13338
|
hostPermissions
|
|
13424
13339
|
)
|
|
13425
13340
|
),
|
|
13426
|
-
"experimental.text.complete": guard(createTextCompleteHandler()),
|
|
13341
|
+
"experimental.text.complete": guard(createTextCompleteHandler(registry4, logger)),
|
|
13427
13342
|
"command.execute.before": guard(
|
|
13428
13343
|
createCommandExecuteHandler(
|
|
13429
13344
|
ctx.client,
|