opencode-acp 1.18.2 → 1.18.3-pr.477.211
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -9
- package/README.zh-CN.md +15 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +359 -92
- package/dist/index.js.map +1 -1
- package/dist/lib/bili-proxy.d.ts +37 -0
- package/dist/lib/bili-proxy.d.ts.map +1 -1
- package/dist/lib/compress/decompress-logic.d.ts +36 -0
- package/dist/lib/compress/decompress-logic.d.ts.map +1 -1
- package/dist/lib/compress/decompress.d.ts.map +1 -1
- package/dist/lib/compress/quality-gate/rejection.d.ts +9 -1
- package/dist/lib/compress/quality-gate/rejection.d.ts.map +1 -1
- package/dist/lib/compress/range.d.ts.map +1 -1
- package/dist/lib/compress/search.d.ts.map +1 -1
- package/dist/lib/compress/types.d.ts.map +1 -1
- package/dist/lib/host-permissions.d.ts +20 -0
- package/dist/lib/host-permissions.d.ts.map +1 -1
- package/dist/lib/messages/inject/inject.d.ts.map +1 -1
- package/dist/lib/messages/truncate-tools.d.ts +1 -0
- package/dist/lib/messages/truncate-tools.d.ts.map +1 -1
- package/dist/lib/state/persistence.d.ts +5 -0
- package/dist/lib/state/persistence.d.ts.map +1 -1
- package/dist/lib/state/state.d.ts.map +1 -1
- package/dist/lib/state/types.d.ts +5 -0
- package/dist/lib/state/types.d.ts.map +1 -1
- package/dist/lib/usage-sanity.d.ts +60 -0
- package/dist/lib/usage-sanity.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2148,8 +2148,57 @@ Using previous/default values`
|
|
|
2148
2148
|
// lib/compress/range.ts
|
|
2149
2149
|
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
2150
2150
|
|
|
2151
|
+
// lib/bili-proxy.ts
|
|
2152
|
+
var BILI_PROXY_MARKER = "/bili/";
|
|
2153
|
+
function extractBaseURL(entry) {
|
|
2154
|
+
if (!entry || typeof entry !== "object") return void 0;
|
|
2155
|
+
const record = entry;
|
|
2156
|
+
const options = record.options;
|
|
2157
|
+
if (options && typeof options === "object") {
|
|
2158
|
+
const optionBaseURL = options.baseURL;
|
|
2159
|
+
if (typeof optionBaseURL === "string" && optionBaseURL.length > 0) {
|
|
2160
|
+
return optionBaseURL;
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
const topLevelBaseURL = record.baseURL;
|
|
2164
|
+
if (typeof topLevelBaseURL === "string" && topLevelBaseURL.length > 0) {
|
|
2165
|
+
return topLevelBaseURL;
|
|
2166
|
+
}
|
|
2167
|
+
return void 0;
|
|
2168
|
+
}
|
|
2169
|
+
function findBiliProxyProviders(provider) {
|
|
2170
|
+
if (!provider || typeof provider !== "object") return [];
|
|
2171
|
+
const matches = [];
|
|
2172
|
+
for (const [name, entry] of Object.entries(provider)) {
|
|
2173
|
+
const baseURL = extractBaseURL(entry);
|
|
2174
|
+
if (baseURL !== void 0 && baseURL.includes(BILI_PROXY_MARKER)) {
|
|
2175
|
+
matches.push({ provider: name, baseURL });
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
return matches;
|
|
2179
|
+
}
|
|
2180
|
+
var BILI_PROXY_ENV_VAR = "BILLION_CONTEXT_PROXY";
|
|
2181
|
+
var BILI_NATIVE_ENV_VAR = "BILLION_CONTEXT_NATIVE";
|
|
2182
|
+
function detectBiliEnvYield(env = process.env) {
|
|
2183
|
+
if (env[BILI_PROXY_ENV_VAR]) return "launcher";
|
|
2184
|
+
if (env[BILI_NATIVE_ENV_VAR]) return "native";
|
|
2185
|
+
return null;
|
|
2186
|
+
}
|
|
2187
|
+
function describeBiliEnvYield(source) {
|
|
2188
|
+
return source === "native" ? "BILLION_CONTEXT_NATIVE set (billion-context native mode)" : "BILLION_CONTEXT_PROXY set (billion-context launcher mode)";
|
|
2189
|
+
}
|
|
2190
|
+
function biliYieldLogMessage(source) {
|
|
2191
|
+
return source === "native" ? "[opencode-acp] disabled: BILLION_CONTEXT_NATIVE detected \u2014 billion-context native mode handles compression" : "[opencode-acp] disabled: BILLION_CONTEXT_PROXY detected \u2014 proxy handles compression";
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2151
2194
|
// lib/compress/types.ts
|
|
2152
2195
|
function resolveToolContext(factoryCtx, sessionID) {
|
|
2196
|
+
const biliYield = detectBiliEnvYield();
|
|
2197
|
+
if (biliYield !== null) {
|
|
2198
|
+
throw new Error(
|
|
2199
|
+
`ACP is disabled in this process \u2014 ${describeBiliEnvYield(biliYield)}. billion-context owns context compression; do not call ACP tools again.`
|
|
2200
|
+
);
|
|
2201
|
+
}
|
|
2153
2202
|
const state = factoryCtx.registry.get(sessionID);
|
|
2154
2203
|
if (!state) {
|
|
2155
2204
|
throw new Error(
|
|
@@ -2859,7 +2908,8 @@ function saveSessionState(sessionState, logger, sessionName) {
|
|
|
2859
2908
|
lastCompaction: sessionState.lastCompaction,
|
|
2860
2909
|
modelContextLimit: sessionState.modelContextLimit,
|
|
2861
2910
|
modelProviderID: sessionState.modelProviderID,
|
|
2862
|
-
modelID: sessionState.modelID
|
|
2911
|
+
modelID: sessionState.modelID,
|
|
2912
|
+
usageSanity: sessionState.usageSanity
|
|
2863
2913
|
};
|
|
2864
2914
|
const key = saveQueueKey(sessionState.sessionId, sessionState.storageDir);
|
|
2865
2915
|
let queue = saveQueues.get(key);
|
|
@@ -3714,18 +3764,32 @@ function adjustBoundariesForToolPairs(context, startIdx, endIdx) {
|
|
|
3714
3764
|
return { startIndex: newStartIdx, endIndex: newEndIdx };
|
|
3715
3765
|
}
|
|
3716
3766
|
function buildBoundaryRecoveryHint(context, state) {
|
|
3717
|
-
const
|
|
3767
|
+
const visibleIndexes = [];
|
|
3718
3768
|
for (const [messageRef, messageId] of state.messageIds.byRef) {
|
|
3719
|
-
if (context.rawMessagesById.has(messageId))
|
|
3720
|
-
|
|
3721
|
-
|
|
3769
|
+
if (!context.rawMessagesById.has(messageId)) continue;
|
|
3770
|
+
const idx = parseMessageRef(messageRef);
|
|
3771
|
+
if (idx !== null) visibleIndexes.push(idx);
|
|
3722
3772
|
}
|
|
3773
|
+
visibleIndexes.sort((a, b) => a - b);
|
|
3774
|
+
const renderSpan = (start, end) => start === end ? formatMessageRef(start) : `${formatMessageRef(start)}\u2013${formatMessageRef(end)}`;
|
|
3723
3775
|
const parts = [];
|
|
3724
|
-
if (
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3776
|
+
if (visibleIndexes.length > 0) {
|
|
3777
|
+
const spans = [];
|
|
3778
|
+
let spanStart = visibleIndexes[0];
|
|
3779
|
+
let spanPrev = visibleIndexes[0];
|
|
3780
|
+
for (let i = 1; i < visibleIndexes.length; i++) {
|
|
3781
|
+
const idx = visibleIndexes[i];
|
|
3782
|
+
if (idx !== spanPrev + 1) {
|
|
3783
|
+
spans.push(renderSpan(spanStart, spanPrev));
|
|
3784
|
+
spanStart = idx;
|
|
3785
|
+
}
|
|
3786
|
+
spanPrev = idx;
|
|
3787
|
+
}
|
|
3788
|
+
spans.push(renderSpan(spanStart, spanPrev));
|
|
3789
|
+
const MAX_SPANS = 8;
|
|
3790
|
+
const shown = spans.slice(0, MAX_SPANS);
|
|
3791
|
+
const overflow = spans.length - shown.length;
|
|
3792
|
+
parts.push(`Free ranges: ${shown.join(", ")}${overflow > 0 ? ` (+${overflow} more)` : ""}.`);
|
|
3729
3793
|
}
|
|
3730
3794
|
const blockCount = context.summaryByBlockId.size;
|
|
3731
3795
|
if (blockCount > 0) {
|
|
@@ -3734,7 +3798,7 @@ function buildBoundaryRecoveryHint(context, state) {
|
|
|
3734
3798
|
if (parts.length === 0) {
|
|
3735
3799
|
return "";
|
|
3736
3800
|
}
|
|
3737
|
-
return `${parts.join(" ")}
|
|
3801
|
+
return `${parts.join(" ")} Retry with IDs inside the free ranges.`;
|
|
3738
3802
|
}
|
|
3739
3803
|
function clampMessageRef(requested, context, state) {
|
|
3740
3804
|
if (state.messageIds.byRef.has(requested.ref)) return null;
|
|
@@ -5183,6 +5247,14 @@ async function ensureSessionInitialized(client, state, sessionId, logger, messag
|
|
|
5183
5247
|
state.modelProviderID = persisted.modelProviderID;
|
|
5184
5248
|
state.modelID = persisted.modelID;
|
|
5185
5249
|
}
|
|
5250
|
+
if (persisted.usageSanity) {
|
|
5251
|
+
const { reported, estimated } = persisted.usageSanity;
|
|
5252
|
+
if (Number.isFinite(reported) && Number.isFinite(estimated) && reported >= 0 && estimated >= 0) {
|
|
5253
|
+
state.usageSanity = { reported, estimated };
|
|
5254
|
+
} else {
|
|
5255
|
+
state.usageSanity = void 0;
|
|
5256
|
+
}
|
|
5257
|
+
}
|
|
5186
5258
|
const applied = applyPendingCompressionDurations(state);
|
|
5187
5259
|
if (applied > 0) {
|
|
5188
5260
|
await saveSessionState(state, logger);
|
|
@@ -6384,24 +6456,32 @@ function computeStats(plan) {
|
|
|
6384
6456
|
const retentionPct = originalTokens > 0 ? (summaryChars / (originalTokens * 4) * 100).toFixed(2) : "?";
|
|
6385
6457
|
return { originalTokens, summaryChars, ratio, retentionPct };
|
|
6386
6458
|
}
|
|
6387
|
-
function
|
|
6459
|
+
function coreReason(reason) {
|
|
6460
|
+
if (!reason) return "unknown";
|
|
6461
|
+
const candidates = [reason.indexOf(":"), reason.indexOf("(")].filter((i) => i >= 0);
|
|
6462
|
+
const cut = Math.min(...candidates);
|
|
6463
|
+
return (cut > 0 ? reason.slice(0, cut) : reason).trim() || "unknown";
|
|
6464
|
+
}
|
|
6465
|
+
function buildQualityRejectionError(plan, result, logger) {
|
|
6388
6466
|
const stats = computeStats(plan);
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6467
|
+
logger?.warn("quality gate rejected compression", {
|
|
6468
|
+
range: `${plan.startId}\u2013${plan.endId}`,
|
|
6469
|
+
reason: result.reason || "unknown",
|
|
6470
|
+
layer: result.layer ?? "unknown",
|
|
6471
|
+
originalTokens: stats.originalTokens,
|
|
6472
|
+
summaryChars: stats.summaryChars,
|
|
6473
|
+
ratio: stats.ratio,
|
|
6474
|
+
retentionPct: stats.retentionPct,
|
|
6475
|
+
rougeF1: formatMetric(result, "rougeF1"),
|
|
6476
|
+
top20Recall: formatMetric(result, "top20Recall")
|
|
6477
|
+
});
|
|
6399
6478
|
const message = `\u26A0\uFE0F COMPRESSION REJECTED \u2014 QUALITY GATE FAILURE
|
|
6400
6479
|
|
|
6401
6480
|
Range: ${plan.startId}\u2013${plan.endId}
|
|
6402
|
-
${
|
|
6481
|
+
Reason: ${coreReason(result.reason)}
|
|
6482
|
+
Original: ~${stats.originalTokens} tokens \xB7 Summary: ${stats.summaryChars} chars \xB7 Retention: ${stats.retentionPct}%
|
|
6403
6483
|
|
|
6404
|
-
Retry:
|
|
6484
|
+
Retry: write a more complete summary preserving critical details (file paths, decisions, exact values, errors), then call compress again on the same range. If you are confident the summary is adequate despite the metrics, pass "acknowledgeRisk": true.`;
|
|
6405
6485
|
return new Error(message);
|
|
6406
6486
|
}
|
|
6407
6487
|
|
|
@@ -6727,6 +6807,49 @@ ${guidance}
|
|
|
6727
6807
|
${afterClose}`;
|
|
6728
6808
|
}
|
|
6729
6809
|
|
|
6810
|
+
// lib/usage-sanity.ts
|
|
6811
|
+
var RELAY_INFLATION_RATIO = 1.5;
|
|
6812
|
+
var PAYLOAD_FLOOR_TOKENS = 5e3;
|
|
6813
|
+
function estimatePayloadTokens(messages) {
|
|
6814
|
+
let total = 0;
|
|
6815
|
+
for (const msg of messages) {
|
|
6816
|
+
if (isSyntheticMessage(msg)) continue;
|
|
6817
|
+
if (isIgnoredUserMessage(msg)) continue;
|
|
6818
|
+
total += estimateAllMessageTokensFast(msg);
|
|
6819
|
+
}
|
|
6820
|
+
return total;
|
|
6821
|
+
}
|
|
6822
|
+
function resolveCurrentUsage(state, messages, logger) {
|
|
6823
|
+
const reported = getCurrentTokenUsage(state, messages);
|
|
6824
|
+
if (reported === 0) {
|
|
6825
|
+
return { tokens: 0, source: "reported", inflationDetected: false };
|
|
6826
|
+
}
|
|
6827
|
+
const estimated = estimatePayloadTokens(messages) + (state.systemPromptTokens ?? 0);
|
|
6828
|
+
const prev = state.usageSanity;
|
|
6829
|
+
let inflationDetected = false;
|
|
6830
|
+
if (prev !== void 0 && prev.reported > 0 && prev.estimated >= PAYLOAD_FLOOR_TOKENS && estimated >= PAYLOAD_FLOOR_TOKENS) {
|
|
6831
|
+
const reportRatio = reported / prev.reported;
|
|
6832
|
+
const payloadRatio = estimated / prev.estimated;
|
|
6833
|
+
if (reportRatio > RELAY_INFLATION_RATIO && payloadRatio <= RELAY_INFLATION_RATIO) {
|
|
6834
|
+
inflationDetected = true;
|
|
6835
|
+
}
|
|
6836
|
+
}
|
|
6837
|
+
state.usageSanity = { reported, estimated };
|
|
6838
|
+
if (inflationDetected) {
|
|
6839
|
+
logger?.warn("Relay usage inflation detected \u2014 re-estimating context size", {
|
|
6840
|
+
session: state.sessionId,
|
|
6841
|
+
reported,
|
|
6842
|
+
previousReported: prev.reported,
|
|
6843
|
+
reportRatio: +(reported / prev.reported).toFixed(3),
|
|
6844
|
+
estimated,
|
|
6845
|
+
previousEstimated: prev.estimated,
|
|
6846
|
+
payloadRatio: +(estimated / prev.estimated).toFixed(3)
|
|
6847
|
+
});
|
|
6848
|
+
return { tokens: estimated, source: "reestimated", inflationDetected: true };
|
|
6849
|
+
}
|
|
6850
|
+
return { tokens: reported, source: "reported", inflationDetected: false };
|
|
6851
|
+
}
|
|
6852
|
+
|
|
6730
6853
|
// lib/messages/utils.ts
|
|
6731
6854
|
import { createHash as createHash2 } from "crypto";
|
|
6732
6855
|
var SUMMARY_ID_HASH_LENGTH = 16;
|
|
@@ -7077,7 +7200,7 @@ function isContextOverLimits(config, state, providerId, modelId, messages) {
|
|
|
7077
7200
|
);
|
|
7078
7201
|
const maxContextLimit = resolvedMaxContextLimit === void 0 ? void 0 : resolvedMaxContextLimit + summaryTokenExtension;
|
|
7079
7202
|
const minContextLimit = resolveContextTokenLimit(config, state, providerId, modelId, "min");
|
|
7080
|
-
const currentTokens =
|
|
7203
|
+
const currentTokens = resolveCurrentUsage(state, messages).tokens;
|
|
7081
7204
|
let overMaxLimit = maxContextLimit === void 0 ? false : currentTokens > maxContextLimit;
|
|
7082
7205
|
const overMinLimit = minContextLimit === void 0 ? false : currentTokens >= minContextLimit;
|
|
7083
7206
|
if (overMaxLimit) {
|
|
@@ -8063,7 +8186,8 @@ function createCompressRangeTool(factoryCtx) {
|
|
|
8063
8186
|
messageIds: plan.selection.messageIds,
|
|
8064
8187
|
messageTokenById: plan.selection.messageTokenById
|
|
8065
8188
|
},
|
|
8066
|
-
result
|
|
8189
|
+
result,
|
|
8190
|
+
ctx.logger
|
|
8067
8191
|
);
|
|
8068
8192
|
}
|
|
8069
8193
|
}
|
|
@@ -8381,6 +8505,29 @@ var resolveEffectiveCompressPermission = (basePermission, hostPermissions, agent
|
|
|
8381
8505
|
var hasExplicitToolPermission = (permissionConfig, tool6) => {
|
|
8382
8506
|
return permissionConfig ? Object.prototype.hasOwnProperty.call(permissionConfig, tool6) : false;
|
|
8383
8507
|
};
|
|
8508
|
+
var anyRuleResolves = (permissionConfig, tool6) => {
|
|
8509
|
+
if (hasExplicitToolPermission(permissionConfig, tool6)) {
|
|
8510
|
+
return true;
|
|
8511
|
+
}
|
|
8512
|
+
const match = findLastMatchingRule(
|
|
8513
|
+
getPermissionRules([permissionConfig]),
|
|
8514
|
+
(rule) => wildcardMatch(tool6, rule.permission)
|
|
8515
|
+
);
|
|
8516
|
+
return match !== void 0;
|
|
8517
|
+
};
|
|
8518
|
+
var applyAcpToolPermissions = (permissionConfig, compressPermission2) => {
|
|
8519
|
+
if (permissionConfig && hasExplicitToolPermission(permissionConfig, "compress")) {
|
|
8520
|
+
return permissionConfig;
|
|
8521
|
+
}
|
|
8522
|
+
const next = {
|
|
8523
|
+
...permissionConfig ?? {},
|
|
8524
|
+
compress: compressPermission2
|
|
8525
|
+
};
|
|
8526
|
+
if (!anyRuleResolves(permissionConfig, "acp_status")) {
|
|
8527
|
+
next.acp_status = "allow";
|
|
8528
|
+
}
|
|
8529
|
+
return next;
|
|
8530
|
+
};
|
|
8384
8531
|
|
|
8385
8532
|
// lib/compress-permission.ts
|
|
8386
8533
|
var compressPermission = (state, config) => {
|
|
@@ -9205,7 +9352,8 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
|
|
|
9205
9352
|
const allInProtectedZone = protectedRefs.size > 0 && unprotectedCompressible.length === 0;
|
|
9206
9353
|
const allBelowMin = contextRanges.compressible.length > 0 && recommendedRanges.length === 0;
|
|
9207
9354
|
const noExecutableCandidates = candidatesEnabled && nudgeAllowed && candidateMessages !== void 0 && candidatePlan.candidates.length === 0;
|
|
9208
|
-
const
|
|
9355
|
+
const noRangesAtAll = contextRanges.compressible.length === 0 && contextRanges.protected.length === 0;
|
|
9356
|
+
const nothingToCompress = (candidatesEnabled && candidateMessages !== void 0 ? noExecutableCandidates : allProtected || allInProtectedZone || allBelowMin) || noRangesAtAll;
|
|
9209
9357
|
const emergencyNoTargets = emergencyOverride && nothingToCompress;
|
|
9210
9358
|
const noticeCadenceMet = state.nudges.lastNudgeShownTokens === void 0 || growthSinceBaseline !== void 0 && growthSinceBaseline >= growthFloor;
|
|
9211
9359
|
let shouldInjectNudge = nudgeAllowed && !nothingToCompress;
|
|
@@ -9410,7 +9558,7 @@ ${rules}`;
|
|
|
9410
9558
|
} else if (nudgeAllowed && nothingToCompress) {
|
|
9411
9559
|
logger.info("Nudge suppressed: nothing to compress", {
|
|
9412
9560
|
session: state.sessionId,
|
|
9413
|
-
reason: allProtected ? "all_protected" : allInProtectedZone ? "in_protected_zone" : allBelowMin ? "below_effective_floor" : "no_executable_candidates",
|
|
9561
|
+
reason: allProtected ? "all_protected" : allInProtectedZone ? "in_protected_zone" : allBelowMin ? "below_effective_floor" : noRangesAtAll ? "no_compressible_ranges" : "no_executable_candidates",
|
|
9414
9562
|
currentTokens,
|
|
9415
9563
|
usagePct,
|
|
9416
9564
|
compressibleRanges: contextRanges.compressible.length
|
|
@@ -9823,6 +9971,81 @@ function computeRestoredMessages(messagesState, activeMessagesBefore) {
|
|
|
9823
9971
|
}
|
|
9824
9972
|
return { restoredMessageCount, restoredTokens };
|
|
9825
9973
|
}
|
|
9974
|
+
function collectRequiredDecompressMessageIds(messagesState, targets, options = {}) {
|
|
9975
|
+
const targetBlockIds = /* @__PURE__ */ new Set();
|
|
9976
|
+
for (const target of targets) {
|
|
9977
|
+
for (const block of target.blocks) {
|
|
9978
|
+
targetBlockIds.add(block.blockId);
|
|
9979
|
+
}
|
|
9980
|
+
}
|
|
9981
|
+
if (targetBlockIds.size === 0) {
|
|
9982
|
+
return [];
|
|
9983
|
+
}
|
|
9984
|
+
const consumedClosure = /* @__PURE__ */ new Set();
|
|
9985
|
+
const queue = [];
|
|
9986
|
+
for (const target of targets) {
|
|
9987
|
+
for (const block of target.blocks) {
|
|
9988
|
+
for (const id of block.consumedBlockIds ?? []) {
|
|
9989
|
+
if (!consumedClosure.has(id)) {
|
|
9990
|
+
queue.push(id);
|
|
9991
|
+
}
|
|
9992
|
+
}
|
|
9993
|
+
}
|
|
9994
|
+
}
|
|
9995
|
+
while (queue.length > 0) {
|
|
9996
|
+
const id = queue.shift();
|
|
9997
|
+
if (consumedClosure.has(id)) continue;
|
|
9998
|
+
consumedClosure.add(id);
|
|
9999
|
+
const consumed = messagesState.blocksById.get(id);
|
|
10000
|
+
if (consumed) {
|
|
10001
|
+
for (const nested of consumed.consumedBlockIds ?? []) {
|
|
10002
|
+
if (!consumedClosure.has(nested)) {
|
|
10003
|
+
queue.push(nested);
|
|
10004
|
+
}
|
|
10005
|
+
}
|
|
10006
|
+
}
|
|
10007
|
+
}
|
|
10008
|
+
let shieldedMessageIds = null;
|
|
10009
|
+
if (!options.full) {
|
|
10010
|
+
shieldedMessageIds = /* @__PURE__ */ new Set();
|
|
10011
|
+
for (const id of consumedClosure) {
|
|
10012
|
+
const block = messagesState.blocksById.get(id);
|
|
10013
|
+
if (!block || block.deactivatedByUser || block.deactivatedByUserDeep) continue;
|
|
10014
|
+
for (const messageId of block.effectiveMessageIds ?? []) {
|
|
10015
|
+
shieldedMessageIds.add(messageId);
|
|
10016
|
+
}
|
|
10017
|
+
}
|
|
10018
|
+
}
|
|
10019
|
+
const required = [];
|
|
10020
|
+
for (const [messageId, entry] of messagesState.byMessageId) {
|
|
10021
|
+
if (entry.activeBlockIds.length === 0) continue;
|
|
10022
|
+
let exclusivelyCoveredByTargets = true;
|
|
10023
|
+
for (const blockId of entry.activeBlockIds) {
|
|
10024
|
+
if (!targetBlockIds.has(blockId)) {
|
|
10025
|
+
exclusivelyCoveredByTargets = false;
|
|
10026
|
+
break;
|
|
10027
|
+
}
|
|
10028
|
+
}
|
|
10029
|
+
if (!exclusivelyCoveredByTargets) continue;
|
|
10030
|
+
if (shieldedMessageIds?.has(messageId)) continue;
|
|
10031
|
+
required.push(messageId);
|
|
10032
|
+
}
|
|
10033
|
+
required.sort();
|
|
10034
|
+
return required;
|
|
10035
|
+
}
|
|
10036
|
+
function checkDecompressSourceAvailability(messagesState, targets, options, presentInHistory) {
|
|
10037
|
+
const requiredMessageIds = collectRequiredDecompressMessageIds(messagesState, targets, options);
|
|
10038
|
+
const available = [];
|
|
10039
|
+
const missing = [];
|
|
10040
|
+
for (const messageId of requiredMessageIds) {
|
|
10041
|
+
if (presentInHistory.has(messageId)) {
|
|
10042
|
+
available.push(messageId);
|
|
10043
|
+
} else {
|
|
10044
|
+
missing.push(messageId);
|
|
10045
|
+
}
|
|
10046
|
+
}
|
|
10047
|
+
return { requiredMessageIds, availableMessageIds: available, missingMessageIds: missing };
|
|
10048
|
+
}
|
|
9826
10049
|
function computeReactivatedBlockIds(messagesState, activeBlockIdsBefore) {
|
|
9827
10050
|
return Array.from(messagesState.activeBlockIds).filter((blockId) => !activeBlockIdsBefore.has(blockId)).sort((a, b) => a - b);
|
|
9828
10051
|
}
|
|
@@ -10031,14 +10254,31 @@ function buildSchema2() {
|
|
|
10031
10254
|
};
|
|
10032
10255
|
}
|
|
10033
10256
|
function extractMessageId(m) {
|
|
10034
|
-
return m
|
|
10257
|
+
return m?.info?.id ?? "";
|
|
10258
|
+
}
|
|
10259
|
+
function serializeMessagePart(part) {
|
|
10260
|
+
if (!part || typeof part !== "object") return null;
|
|
10261
|
+
if (part.type === "text") {
|
|
10262
|
+
return typeof part.text === "string" && part.text.length > 0 ? part.text : null;
|
|
10263
|
+
}
|
|
10264
|
+
if (part.type === "tool") {
|
|
10265
|
+
const contents = extractToolContent(part);
|
|
10266
|
+
if (contents.length === 0) return null;
|
|
10267
|
+
const toolName = "tool" in part && typeof part.tool === "string" && part.tool.length > 0 ? part.tool : "tool";
|
|
10268
|
+
return `[${toolName}] ${contents.join("\n")}`;
|
|
10269
|
+
}
|
|
10270
|
+
return null;
|
|
10035
10271
|
}
|
|
10036
10272
|
function extractMessageText2(m) {
|
|
10037
|
-
const
|
|
10038
|
-
const
|
|
10039
|
-
const
|
|
10273
|
+
const role = m?.info?.role ?? "unknown";
|
|
10274
|
+
const parts = Array.isArray(m.parts) ? m.parts : [];
|
|
10275
|
+
const segments = [];
|
|
10276
|
+
for (const part of parts) {
|
|
10277
|
+
const serialized = serializeMessagePart(part);
|
|
10278
|
+
if (serialized !== null) segments.push(serialized);
|
|
10279
|
+
}
|
|
10040
10280
|
return `[${role}]
|
|
10041
|
-
${
|
|
10281
|
+
${segments.join("\n\n")}`;
|
|
10042
10282
|
}
|
|
10043
10283
|
function createDecompressTool(factoryCtx) {
|
|
10044
10284
|
return tool3({
|
|
@@ -10090,10 +10330,40 @@ function createDecompressTool(factoryCtx) {
|
|
|
10090
10330
|
const blockMessages = rawMessages.filter((m) => msgIdSet.has(extractMessageId(m)));
|
|
10091
10331
|
const lines2 = blockMessages.map(extractMessageText2);
|
|
10092
10332
|
const { writeFile: writeFile4 } = await import("fs/promises");
|
|
10093
|
-
|
|
10333
|
+
let fileContent;
|
|
10334
|
+
let sourceNote;
|
|
10335
|
+
if (lines2.length > 0) {
|
|
10336
|
+
fileContent = lines2.join("\n\n---\n\n");
|
|
10337
|
+
sourceNote = `${blockMessages.length} original message${blockMessages.length === 1 ? "" : "s"}`;
|
|
10338
|
+
} else {
|
|
10339
|
+
fileContent = targets[0]?.blocks[0]?.summary ?? "(no content available)";
|
|
10340
|
+
sourceNote = `no original messages found; wrote block summary (${targets.length} block${targets.length === 1 ? "" : "s"})`;
|
|
10341
|
+
}
|
|
10094
10342
|
await writeFile4(targetPath, fileContent, "utf-8");
|
|
10095
10343
|
const displayIds2 = targets.map((t) => `b${t.displayId}`).join(", ");
|
|
10096
|
-
return `Block(s) ${displayIds2} content (${
|
|
10344
|
+
return `Block(s) ${displayIds2} content (${sourceNote}, ${fileContent.length} chars) written to ${targetPath}. Block(s) stay compressed \u2014 context unchanged. Use read tool to access specific parts.`;
|
|
10345
|
+
}
|
|
10346
|
+
const availability = checkDecompressSourceAvailability(
|
|
10347
|
+
messagesState,
|
|
10348
|
+
targets,
|
|
10349
|
+
{ full: args.full === true },
|
|
10350
|
+
new Set(rawMessages.map((m) => m.info.id))
|
|
10351
|
+
);
|
|
10352
|
+
if (availability.missingMessageIds.length > 0) {
|
|
10353
|
+
const displayIds2 = targets.map((t) => `b${t.displayId}`).join(", ");
|
|
10354
|
+
const missingRefs = availability.missingMessageIds.map(
|
|
10355
|
+
(id) => ctx.state.messageIds.byRawId.get(id) ?? id
|
|
10356
|
+
);
|
|
10357
|
+
const shownRefs = missingRefs.slice(0, 10).join(", ");
|
|
10358
|
+
const extraCount = missingRefs.length - Math.min(missingRefs.length, 10);
|
|
10359
|
+
ctx.logger.warn("Decompress aborted: source messages missing from host history", {
|
|
10360
|
+
targetDisplayIds: displayIds2,
|
|
10361
|
+
full: args.full === true,
|
|
10362
|
+
requiredCount: availability.requiredMessageIds.length,
|
|
10363
|
+
missingCount: availability.missingMessageIds.length,
|
|
10364
|
+
missingRefs
|
|
10365
|
+
});
|
|
10366
|
+
return `Error: Cannot decompress ${displayIds2}: ${availability.missingMessageIds.length} of ${availability.requiredMessageIds.length} source message(s) are no longer present in session history (removed by native compaction or external deletion). Missing: ${shownRefs}` + (extraCount > 0 ? ` (+${extraCount} more)` : "") + `. Block(s) stay compressed \u2014 the summary remains available and nothing was changed. This is a missing-source condition, not a transient fetch failure.`;
|
|
10097
10367
|
}
|
|
10098
10368
|
const activeMessagesBefore = snapshotActiveMessages(messagesState);
|
|
10099
10369
|
const activeBlockIdsBefore = new Set(messagesState.activeBlockIds);
|
|
@@ -10906,7 +11176,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
|
10906
11176
|
import { join as join4 } from "path";
|
|
10907
11177
|
import { existsSync as existsSync4 } from "fs";
|
|
10908
11178
|
import { homedir as homedir3 } from "os";
|
|
10909
|
-
var LOG_VERSION = true ? "1.18.
|
|
11179
|
+
var LOG_VERSION = true ? "1.18.3-pr.477.211" : "dev";
|
|
10910
11180
|
var LEVEL_RANK = {
|
|
10911
11181
|
debug: 10,
|
|
10912
11182
|
info: 20,
|
|
@@ -11712,7 +11982,17 @@ var KEEP_PREFIX_CHARS = 2e3;
|
|
|
11712
11982
|
var KEEP_SUFFIX_CHARS = 2e3;
|
|
11713
11983
|
var PROTECT_RECENT_MESSAGES = 3;
|
|
11714
11984
|
var OUTPUT_RESERVE_TOKENS = 16384;
|
|
11985
|
+
var MAX_OVERHEAD_ERROR_SESSIONS = 1024;
|
|
11715
11986
|
var overheadErrorLogged = /* @__PURE__ */ new Set();
|
|
11987
|
+
function recordOverheadError(sessionKey) {
|
|
11988
|
+
if (overheadErrorLogged.has(sessionKey)) return false;
|
|
11989
|
+
if (overheadErrorLogged.size >= MAX_OVERHEAD_ERROR_SESSIONS) {
|
|
11990
|
+
const oldest = overheadErrorLogged.values().next().value;
|
|
11991
|
+
if (oldest !== void 0) overheadErrorLogged.delete(oldest);
|
|
11992
|
+
}
|
|
11993
|
+
overheadErrorLogged.add(sessionKey);
|
|
11994
|
+
return true;
|
|
11995
|
+
}
|
|
11716
11996
|
function parseGcThreshold(threshold, modelContextLimit) {
|
|
11717
11997
|
if (typeof threshold === "number") return threshold;
|
|
11718
11998
|
const str = threshold ?? "100%";
|
|
@@ -11730,8 +12010,7 @@ function truncateLargeToolOutputs(state, config, logger, messages) {
|
|
|
11730
12010
|
const threshold = Math.min(configuredThreshold, effective.limit - overhead);
|
|
11731
12011
|
if (threshold <= 0) {
|
|
11732
12012
|
const sessionKey = state.sessionId ?? "unknown";
|
|
11733
|
-
if (
|
|
11734
|
-
overheadErrorLogged.add(sessionKey);
|
|
12013
|
+
if (recordOverheadError(sessionKey)) {
|
|
11735
12014
|
logger.error("ACP: model context window too small to fit overhead", {
|
|
11736
12015
|
session: state.sessionId,
|
|
11737
12016
|
limit: effective.limit,
|
|
@@ -13118,7 +13397,7 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
|
|
|
13118
13397
|
saveSessionState(state, logger).catch(() => {
|
|
13119
13398
|
});
|
|
13120
13399
|
}
|
|
13121
|
-
const prePruneTokens =
|
|
13400
|
+
const prePruneTokens = resolveCurrentUsage(state, output.messages, logger).tokens;
|
|
13122
13401
|
const candidateMessages = config.compress.candidates === true ? output.messages.slice() : void 0;
|
|
13123
13402
|
prune(state, logger, config, output.messages);
|
|
13124
13403
|
hideConsumedCompressCalls(state, output.messages);
|
|
@@ -13167,7 +13446,7 @@ ${text}`);
|
|
|
13167
13446
|
if (state.sessionId && smartPlanEnabled === true) {
|
|
13168
13447
|
recordVisibleMessages(state.sessionId, output.messages);
|
|
13169
13448
|
}
|
|
13170
|
-
const postTokens =
|
|
13449
|
+
const postTokens = resolveCurrentUsage(state, output.messages, logger).tokens;
|
|
13171
13450
|
if (postTokens !== void 0 && effectiveLimit) {
|
|
13172
13451
|
const budget = effectiveLimit.limit - (state.systemPromptTokens ?? 0) - OUTPUT_RESERVE_TOKENS;
|
|
13173
13452
|
if (postTokens > budget) {
|
|
@@ -13380,36 +13659,6 @@ function configureClientAuth(client) {
|
|
|
13380
13659
|
return client;
|
|
13381
13660
|
}
|
|
13382
13661
|
|
|
13383
|
-
// lib/bili-proxy.ts
|
|
13384
|
-
var BILI_PROXY_MARKER = "/bili/";
|
|
13385
|
-
function extractBaseURL(entry) {
|
|
13386
|
-
if (!entry || typeof entry !== "object") return void 0;
|
|
13387
|
-
const record = entry;
|
|
13388
|
-
const options = record.options;
|
|
13389
|
-
if (options && typeof options === "object") {
|
|
13390
|
-
const optionBaseURL = options.baseURL;
|
|
13391
|
-
if (typeof optionBaseURL === "string" && optionBaseURL.length > 0) {
|
|
13392
|
-
return optionBaseURL;
|
|
13393
|
-
}
|
|
13394
|
-
}
|
|
13395
|
-
const topLevelBaseURL = record.baseURL;
|
|
13396
|
-
if (typeof topLevelBaseURL === "string" && topLevelBaseURL.length > 0) {
|
|
13397
|
-
return topLevelBaseURL;
|
|
13398
|
-
}
|
|
13399
|
-
return void 0;
|
|
13400
|
-
}
|
|
13401
|
-
function findBiliProxyProviders(provider) {
|
|
13402
|
-
if (!provider || typeof provider !== "object") return [];
|
|
13403
|
-
const matches = [];
|
|
13404
|
-
for (const [name, entry] of Object.entries(provider)) {
|
|
13405
|
-
const baseURL = extractBaseURL(entry);
|
|
13406
|
-
if (baseURL !== void 0 && baseURL.includes(BILI_PROXY_MARKER)) {
|
|
13407
|
-
matches.push({ provider: name, baseURL });
|
|
13408
|
-
}
|
|
13409
|
-
}
|
|
13410
|
-
return matches;
|
|
13411
|
-
}
|
|
13412
|
-
|
|
13413
13662
|
// lib/update.ts
|
|
13414
13663
|
import { readFile as readFile2, rm } from "fs/promises";
|
|
13415
13664
|
import { basename, dirname as dirname4, join as join7 } from "path";
|
|
@@ -13616,15 +13865,14 @@ var server = (async (ctx) => {
|
|
|
13616
13865
|
if (!config.enabled) {
|
|
13617
13866
|
return {};
|
|
13618
13867
|
}
|
|
13619
|
-
|
|
13620
|
-
|
|
13621
|
-
|
|
13622
|
-
);
|
|
13868
|
+
const setupBiliYield = detectBiliEnvYield();
|
|
13869
|
+
if (setupBiliYield) {
|
|
13870
|
+
console.log(biliYieldLogMessage(setupBiliYield));
|
|
13623
13871
|
return {};
|
|
13624
13872
|
}
|
|
13625
13873
|
const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
|
|
13626
13874
|
logger.info("ACP plugin initialized", {
|
|
13627
|
-
version: true ? "1.18.
|
|
13875
|
+
version: true ? "1.18.3-pr.477.211" : "dev",
|
|
13628
13876
|
workspace: ctx.directory,
|
|
13629
13877
|
logLevel: logger.level,
|
|
13630
13878
|
debug: config.debug,
|
|
@@ -13674,7 +13922,21 @@ var server = (async (ctx) => {
|
|
|
13674
13922
|
prompts
|
|
13675
13923
|
};
|
|
13676
13924
|
let disabledByBiliProxy = false;
|
|
13677
|
-
|
|
13925
|
+
let announcedEnvYieldSource;
|
|
13926
|
+
const noteEnvYield = (source) => {
|
|
13927
|
+
if (source === null || announcedEnvYieldSource === source) return;
|
|
13928
|
+
announcedEnvYieldSource = source;
|
|
13929
|
+
console.log(biliYieldLogMessage(source));
|
|
13930
|
+
};
|
|
13931
|
+
const guard = (fn) => (...args) => {
|
|
13932
|
+
if (disabledByBiliProxy) return Promise.resolve();
|
|
13933
|
+
const envYield = detectBiliEnvYield();
|
|
13934
|
+
if (envYield !== null) {
|
|
13935
|
+
noteEnvYield(envYield);
|
|
13936
|
+
return Promise.resolve();
|
|
13937
|
+
}
|
|
13938
|
+
return fn(...args);
|
|
13939
|
+
};
|
|
13678
13940
|
return {
|
|
13679
13941
|
"experimental.chat.system.transform": guard(
|
|
13680
13942
|
createSystemPromptHandler(registry4, logger, config, prompts)
|
|
@@ -13711,14 +13973,9 @@ var server = (async (ctx) => {
|
|
|
13711
13973
|
}
|
|
13712
13974
|
},
|
|
13713
13975
|
config: async (opencodeConfig) => {
|
|
13714
|
-
const
|
|
13715
|
-
|
|
13716
|
-
|
|
13717
|
-
console.log(
|
|
13718
|
-
"[opencode-acp] disabled: /bili/ proxy detected in provider baseURL (" + biliMatches.map((m) => m.provider).join(", ") + ") \u2014 proxy handles compression"
|
|
13719
|
-
);
|
|
13720
|
-
const permission = opencodeConfig.permission ?? {};
|
|
13721
|
-
opencodeConfig.permission = {
|
|
13976
|
+
const denyAcpTools = (opencodeConfig2) => {
|
|
13977
|
+
const permission = opencodeConfig2.permission ?? {};
|
|
13978
|
+
opencodeConfig2.permission = {
|
|
13722
13979
|
...permission,
|
|
13723
13980
|
compress: "deny",
|
|
13724
13981
|
decompress: "deny",
|
|
@@ -13726,6 +13983,20 @@ var server = (async (ctx) => {
|
|
|
13726
13983
|
acp_status: "deny",
|
|
13727
13984
|
acp_context_recap: "deny"
|
|
13728
13985
|
};
|
|
13986
|
+
};
|
|
13987
|
+
const biliMatches = findBiliProxyProviders(opencodeConfig.provider);
|
|
13988
|
+
disabledByBiliProxy = biliMatches.length > 0;
|
|
13989
|
+
const envYield = detectBiliEnvYield();
|
|
13990
|
+
if (envYield !== null) {
|
|
13991
|
+
noteEnvYield(envYield);
|
|
13992
|
+
denyAcpTools(opencodeConfig);
|
|
13993
|
+
return;
|
|
13994
|
+
}
|
|
13995
|
+
if (biliMatches.length > 0) {
|
|
13996
|
+
console.log(
|
|
13997
|
+
"[opencode-acp] disabled: /bili/ proxy detected in provider baseURL (" + biliMatches.map((m) => m.provider).join(", ") + ") \u2014 proxy handles compression"
|
|
13998
|
+
);
|
|
13999
|
+
denyAcpTools(opencodeConfig);
|
|
13729
14000
|
return;
|
|
13730
14001
|
}
|
|
13731
14002
|
if (config.compress.permission !== "deny" && compressDisabledByOpencode(opencodeConfig.permission)) {
|
|
@@ -13749,14 +14020,10 @@ var server = (async (ctx) => {
|
|
|
13749
14020
|
primary_tools: [...existingPrimaryTools, ...toolsToAdd]
|
|
13750
14021
|
};
|
|
13751
14022
|
}
|
|
13752
|
-
|
|
13753
|
-
|
|
13754
|
-
|
|
13755
|
-
|
|
13756
|
-
compress: config.compress.permission,
|
|
13757
|
-
acp_status: "allow"
|
|
13758
|
-
};
|
|
13759
|
-
}
|
|
14023
|
+
opencodeConfig.permission = applyAcpToolPermissions(
|
|
14024
|
+
opencodeConfig.permission,
|
|
14025
|
+
config.compress.permission
|
|
14026
|
+
);
|
|
13760
14027
|
hostPermissions.global = opencodeConfig.permission;
|
|
13761
14028
|
hostPermissions.agents = Object.fromEntries(
|
|
13762
14029
|
Object.entries(opencodeConfig.agent ?? {}).map(([name, agent]) => [
|