opencode-acp 1.18.2 → 1.18.3-pr.475.210
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 +276 -76
- 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/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/truncate-tools.d.ts +1 -0
- package/dist/lib/messages/truncate-tools.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,21 @@ The model decides <em>when</em> and <em>what</em> to compress — not a hard lim
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
## Community
|
|
14
|
+
|
|
15
|
+
QQ Group:
|
|
16
|
+
1056132097 (full)
|
|
17
|
+
1108730198 (open)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
> ⚠️ **Using OpenCode 2.x?** This extension uses the OpenCode **V1** plugin
|
|
22
|
+
> API and does **not** load on OpenCode 2.x. For OpenCode 2.x the recommended
|
|
23
|
+
> context manager is [billion-context](https://github.com/ranxianglei/billion-context):
|
|
24
|
+
> `bili opencode` (launcher) or `bili plugin install opencode` (native plugin,
|
|
25
|
+
> no launcher). This extension remains fully supported on OpenCode **1.x**.
|
|
26
|
+
|
|
27
|
+
---
|
|
13
28
|
|
|
14
29
|
## 📄 Paper / Preprint
|
|
15
30
|
|
|
@@ -33,14 +48,6 @@ A production-scale longitudinal study: 4.5 months, three hosts, 174,327 model ca
|
|
|
33
48
|
|
|
34
49
|
---
|
|
35
50
|
|
|
36
|
-
## Community
|
|
37
|
-
|
|
38
|
-
Discussion, help, and updates on QQ — one group covers all three projects (`billion-context`, `billion-context-pi`, `opencode-acp`):
|
|
39
|
-
|
|
40
|
-
**QQ Group: 1056132097**
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
51
|
## Why ACP
|
|
45
52
|
|
|
46
53
|
ACP hands all context-management authority to the model itself — not relying on
|
|
@@ -67,7 +74,8 @@ Pick by your client:
|
|
|
67
74
|
| Client | Use |
|
|
68
75
|
|---|---|
|
|
69
76
|
| **pi** | [`billion-context-pi`](https://github.com/ranxianglei/billion-context-pi) (in-process extension) |
|
|
70
|
-
| **opencode** | [`opencode-acp`](https://github.com/ranxianglei/opencode-acp) (in-process extension) |
|
|
77
|
+
| **opencode 1.x** | [`opencode-acp`](https://github.com/ranxianglei/opencode-acp) (in-process extension) |
|
|
78
|
+
| **opencode 2.0+** | [`billion-context`](https://github.com/ranxianglei/billion-context) — `bili opencode` (built-in V2 plugin) or `bili plugin install opencode` (self-spawning native plugin, no launcher); `opencode-acp` does not load on 2.x |
|
|
71
79
|
| **omp** | [`billion-context`](https://github.com/ranxianglei/billion-context) via `bili omp` (built-in plugin) |
|
|
72
80
|
| **everything else** (no context hook) | [`billion-context`](https://github.com/ranxianglei/billion-context) — `bili <client>` (launcher, preferred) or `/bili/` prefix |
|
|
73
81
|
|
|
@@ -101,6 +109,11 @@ across all other sessions.
|
|
|
101
109
|
|
|
102
110
|
## Installation
|
|
103
111
|
|
|
112
|
+
> ⚠️ This installs the **V1** plugin — it targets OpenCode **1.x**. On
|
|
113
|
+
> OpenCode **2.x** the plugin will not load; use
|
|
114
|
+
> [billion-context](https://github.com/ranxianglei/billion-context) instead
|
|
115
|
+
> (`bili opencode` or `bili plugin install opencode`).
|
|
116
|
+
|
|
104
117
|
```bash
|
|
105
118
|
opencode plugin opencode-acp@stable --global
|
|
106
119
|
```
|
package/README.zh-CN.md
CHANGED
|
@@ -10,6 +10,17 @@
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
## 社区
|
|
14
|
+
|
|
15
|
+
QQ群:
|
|
16
|
+
1056132097(已满)
|
|
17
|
+
1108730198(未满)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
> ⚠️ **你用的是 OpenCode 2.x?** 本扩展使用 OpenCode **V1** 插件 API,在 OpenCode 2.x 上**不会加载**。OpenCode 2.x 推荐的上下文管理方案是 [billion-context](https://github.com/ranxianglei/billion-context):`bili opencode`(启动器)或 `bili plugin install opencode`(原生插件,免启动器)。本扩展在 OpenCode **1.x** 上继续完整可用。
|
|
22
|
+
|
|
23
|
+
---
|
|
13
24
|
|
|
14
25
|
## 📄 论文 / 预印本
|
|
15
26
|
|
|
@@ -33,14 +44,6 @@
|
|
|
33
44
|
|
|
34
45
|
---
|
|
35
46
|
|
|
36
|
-
## 社区
|
|
37
|
-
|
|
38
|
-
交流、求助与更新都在 QQ——同一个群覆盖三个项目(`billion-context`、`billion-context-pi`、`opencode-acp`):
|
|
39
|
-
|
|
40
|
-
**QQ 群:1056132097**
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
47
|
## 为什么选择 ACP
|
|
45
48
|
|
|
46
49
|
ACP 将上下文管理的所有权限全部交给模型自己,而不依靠外部模型或各种复杂的机制去做上下文管理。它是迄今为止,市面上对上下文管理最好的实现。
|
|
@@ -59,7 +62,8 @@ ACP 将上下文管理的所有权限全部交给模型自己,而不依靠外
|
|
|
59
62
|
| 客户端 | 用这个 |
|
|
60
63
|
|---|---|
|
|
61
64
|
| **pi** | [`billion-context-pi`](https://github.com/ranxianglei/billion-context-pi)(进程内扩展) |
|
|
62
|
-
| **opencode** | [`opencode-acp`](https://github.com/ranxianglei/opencode-acp)(进程内扩展) |
|
|
65
|
+
| **opencode 1.x** | [`opencode-acp`](https://github.com/ranxianglei/opencode-acp)(进程内扩展) |
|
|
66
|
+
| **opencode 2.0+** | [`billion-context`](https://github.com/ranxianglei/billion-context) —— `bili opencode`(内置 V2 插件)或 `bili plugin install opencode`(自拉起原生插件,免启动器);`opencode-acp` 在 2.x 上不加载 |
|
|
63
67
|
| **omp** | [`billion-context`](https://github.com/ranxianglei/billion-context),`bili omp`(内置插件) |
|
|
64
68
|
| **其余所有**(没有上下文 hook) | [`billion-context`](https://github.com/ranxianglei/billion-context) —— `bili <client>`(启动器,优先)或 `/bili/` 前缀 |
|
|
65
69
|
|
|
@@ -88,6 +92,8 @@ ACP 将上下文管理的所有权限全部交给模型自己,而不依靠外
|
|
|
88
92
|
|
|
89
93
|
## 安装
|
|
90
94
|
|
|
95
|
+
> ⚠️ 这里安装的是 **V1** 插件 —— 面向 OpenCode **1.x**。在 OpenCode **2.x** 上该插件不会加载;请改用 [billion-context](https://github.com/ranxianglei/billion-context)(`bili opencode` 或 `bili plugin install opencode`)。
|
|
96
|
+
|
|
91
97
|
```bash
|
|
92
98
|
opencode plugin opencode-acp@stable --global
|
|
93
99
|
```
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAU,MAAM,EAAE,MAAM,qBAAqB,CAAA;AA6BzD,QAAA,MAAM,MAAM,EAAE,MA4OK,CAAA;AAEnB,eAAe,MAAM,CAAA"}
|
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(
|
|
@@ -6384,24 +6433,32 @@ function computeStats(plan) {
|
|
|
6384
6433
|
const retentionPct = originalTokens > 0 ? (summaryChars / (originalTokens * 4) * 100).toFixed(2) : "?";
|
|
6385
6434
|
return { originalTokens, summaryChars, ratio, retentionPct };
|
|
6386
6435
|
}
|
|
6387
|
-
function
|
|
6436
|
+
function coreReason(reason) {
|
|
6437
|
+
if (!reason) return "unknown";
|
|
6438
|
+
const candidates = [reason.indexOf(":"), reason.indexOf("(")].filter((i) => i >= 0);
|
|
6439
|
+
const cut = Math.min(...candidates);
|
|
6440
|
+
return (cut > 0 ? reason.slice(0, cut) : reason).trim() || "unknown";
|
|
6441
|
+
}
|
|
6442
|
+
function buildQualityRejectionError(plan, result, logger) {
|
|
6388
6443
|
const stats = computeStats(plan);
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6444
|
+
logger?.warn("quality gate rejected compression", {
|
|
6445
|
+
range: `${plan.startId}\u2013${plan.endId}`,
|
|
6446
|
+
reason: result.reason || "unknown",
|
|
6447
|
+
layer: result.layer ?? "unknown",
|
|
6448
|
+
originalTokens: stats.originalTokens,
|
|
6449
|
+
summaryChars: stats.summaryChars,
|
|
6450
|
+
ratio: stats.ratio,
|
|
6451
|
+
retentionPct: stats.retentionPct,
|
|
6452
|
+
rougeF1: formatMetric(result, "rougeF1"),
|
|
6453
|
+
top20Recall: formatMetric(result, "top20Recall")
|
|
6454
|
+
});
|
|
6399
6455
|
const message = `\u26A0\uFE0F COMPRESSION REJECTED \u2014 QUALITY GATE FAILURE
|
|
6400
6456
|
|
|
6401
6457
|
Range: ${plan.startId}\u2013${plan.endId}
|
|
6402
|
-
${
|
|
6458
|
+
Reason: ${coreReason(result.reason)}
|
|
6459
|
+
Original: ~${stats.originalTokens} tokens \xB7 Summary: ${stats.summaryChars} chars \xB7 Retention: ${stats.retentionPct}%
|
|
6403
6460
|
|
|
6404
|
-
Retry:
|
|
6461
|
+
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
6462
|
return new Error(message);
|
|
6406
6463
|
}
|
|
6407
6464
|
|
|
@@ -8063,7 +8120,8 @@ function createCompressRangeTool(factoryCtx) {
|
|
|
8063
8120
|
messageIds: plan.selection.messageIds,
|
|
8064
8121
|
messageTokenById: plan.selection.messageTokenById
|
|
8065
8122
|
},
|
|
8066
|
-
result
|
|
8123
|
+
result,
|
|
8124
|
+
ctx.logger
|
|
8067
8125
|
);
|
|
8068
8126
|
}
|
|
8069
8127
|
}
|
|
@@ -8381,6 +8439,29 @@ var resolveEffectiveCompressPermission = (basePermission, hostPermissions, agent
|
|
|
8381
8439
|
var hasExplicitToolPermission = (permissionConfig, tool6) => {
|
|
8382
8440
|
return permissionConfig ? Object.prototype.hasOwnProperty.call(permissionConfig, tool6) : false;
|
|
8383
8441
|
};
|
|
8442
|
+
var anyRuleResolves = (permissionConfig, tool6) => {
|
|
8443
|
+
if (hasExplicitToolPermission(permissionConfig, tool6)) {
|
|
8444
|
+
return true;
|
|
8445
|
+
}
|
|
8446
|
+
const match = findLastMatchingRule(
|
|
8447
|
+
getPermissionRules([permissionConfig]),
|
|
8448
|
+
(rule) => wildcardMatch(tool6, rule.permission)
|
|
8449
|
+
);
|
|
8450
|
+
return match !== void 0;
|
|
8451
|
+
};
|
|
8452
|
+
var applyAcpToolPermissions = (permissionConfig, compressPermission2) => {
|
|
8453
|
+
if (permissionConfig && hasExplicitToolPermission(permissionConfig, "compress")) {
|
|
8454
|
+
return permissionConfig;
|
|
8455
|
+
}
|
|
8456
|
+
const next = {
|
|
8457
|
+
...permissionConfig ?? {},
|
|
8458
|
+
compress: compressPermission2
|
|
8459
|
+
};
|
|
8460
|
+
if (!anyRuleResolves(permissionConfig, "acp_status")) {
|
|
8461
|
+
next.acp_status = "allow";
|
|
8462
|
+
}
|
|
8463
|
+
return next;
|
|
8464
|
+
};
|
|
8384
8465
|
|
|
8385
8466
|
// lib/compress-permission.ts
|
|
8386
8467
|
var compressPermission = (state, config) => {
|
|
@@ -9823,6 +9904,81 @@ function computeRestoredMessages(messagesState, activeMessagesBefore) {
|
|
|
9823
9904
|
}
|
|
9824
9905
|
return { restoredMessageCount, restoredTokens };
|
|
9825
9906
|
}
|
|
9907
|
+
function collectRequiredDecompressMessageIds(messagesState, targets, options = {}) {
|
|
9908
|
+
const targetBlockIds = /* @__PURE__ */ new Set();
|
|
9909
|
+
for (const target of targets) {
|
|
9910
|
+
for (const block of target.blocks) {
|
|
9911
|
+
targetBlockIds.add(block.blockId);
|
|
9912
|
+
}
|
|
9913
|
+
}
|
|
9914
|
+
if (targetBlockIds.size === 0) {
|
|
9915
|
+
return [];
|
|
9916
|
+
}
|
|
9917
|
+
const consumedClosure = /* @__PURE__ */ new Set();
|
|
9918
|
+
const queue = [];
|
|
9919
|
+
for (const target of targets) {
|
|
9920
|
+
for (const block of target.blocks) {
|
|
9921
|
+
for (const id of block.consumedBlockIds ?? []) {
|
|
9922
|
+
if (!consumedClosure.has(id)) {
|
|
9923
|
+
queue.push(id);
|
|
9924
|
+
}
|
|
9925
|
+
}
|
|
9926
|
+
}
|
|
9927
|
+
}
|
|
9928
|
+
while (queue.length > 0) {
|
|
9929
|
+
const id = queue.shift();
|
|
9930
|
+
if (consumedClosure.has(id)) continue;
|
|
9931
|
+
consumedClosure.add(id);
|
|
9932
|
+
const consumed = messagesState.blocksById.get(id);
|
|
9933
|
+
if (consumed) {
|
|
9934
|
+
for (const nested of consumed.consumedBlockIds ?? []) {
|
|
9935
|
+
if (!consumedClosure.has(nested)) {
|
|
9936
|
+
queue.push(nested);
|
|
9937
|
+
}
|
|
9938
|
+
}
|
|
9939
|
+
}
|
|
9940
|
+
}
|
|
9941
|
+
let shieldedMessageIds = null;
|
|
9942
|
+
if (!options.full) {
|
|
9943
|
+
shieldedMessageIds = /* @__PURE__ */ new Set();
|
|
9944
|
+
for (const id of consumedClosure) {
|
|
9945
|
+
const block = messagesState.blocksById.get(id);
|
|
9946
|
+
if (!block || block.deactivatedByUser || block.deactivatedByUserDeep) continue;
|
|
9947
|
+
for (const messageId of block.effectiveMessageIds ?? []) {
|
|
9948
|
+
shieldedMessageIds.add(messageId);
|
|
9949
|
+
}
|
|
9950
|
+
}
|
|
9951
|
+
}
|
|
9952
|
+
const required = [];
|
|
9953
|
+
for (const [messageId, entry] of messagesState.byMessageId) {
|
|
9954
|
+
if (entry.activeBlockIds.length === 0) continue;
|
|
9955
|
+
let exclusivelyCoveredByTargets = true;
|
|
9956
|
+
for (const blockId of entry.activeBlockIds) {
|
|
9957
|
+
if (!targetBlockIds.has(blockId)) {
|
|
9958
|
+
exclusivelyCoveredByTargets = false;
|
|
9959
|
+
break;
|
|
9960
|
+
}
|
|
9961
|
+
}
|
|
9962
|
+
if (!exclusivelyCoveredByTargets) continue;
|
|
9963
|
+
if (shieldedMessageIds?.has(messageId)) continue;
|
|
9964
|
+
required.push(messageId);
|
|
9965
|
+
}
|
|
9966
|
+
required.sort();
|
|
9967
|
+
return required;
|
|
9968
|
+
}
|
|
9969
|
+
function checkDecompressSourceAvailability(messagesState, targets, options, presentInHistory) {
|
|
9970
|
+
const requiredMessageIds = collectRequiredDecompressMessageIds(messagesState, targets, options);
|
|
9971
|
+
const available = [];
|
|
9972
|
+
const missing = [];
|
|
9973
|
+
for (const messageId of requiredMessageIds) {
|
|
9974
|
+
if (presentInHistory.has(messageId)) {
|
|
9975
|
+
available.push(messageId);
|
|
9976
|
+
} else {
|
|
9977
|
+
missing.push(messageId);
|
|
9978
|
+
}
|
|
9979
|
+
}
|
|
9980
|
+
return { requiredMessageIds, availableMessageIds: available, missingMessageIds: missing };
|
|
9981
|
+
}
|
|
9826
9982
|
function computeReactivatedBlockIds(messagesState, activeBlockIdsBefore) {
|
|
9827
9983
|
return Array.from(messagesState.activeBlockIds).filter((blockId) => !activeBlockIdsBefore.has(blockId)).sort((a, b) => a - b);
|
|
9828
9984
|
}
|
|
@@ -10031,14 +10187,31 @@ function buildSchema2() {
|
|
|
10031
10187
|
};
|
|
10032
10188
|
}
|
|
10033
10189
|
function extractMessageId(m) {
|
|
10034
|
-
return m
|
|
10190
|
+
return m?.info?.id ?? "";
|
|
10191
|
+
}
|
|
10192
|
+
function serializeMessagePart(part) {
|
|
10193
|
+
if (!part || typeof part !== "object") return null;
|
|
10194
|
+
if (part.type === "text") {
|
|
10195
|
+
return typeof part.text === "string" && part.text.length > 0 ? part.text : null;
|
|
10196
|
+
}
|
|
10197
|
+
if (part.type === "tool") {
|
|
10198
|
+
const contents = extractToolContent(part);
|
|
10199
|
+
if (contents.length === 0) return null;
|
|
10200
|
+
const toolName = "tool" in part && typeof part.tool === "string" && part.tool.length > 0 ? part.tool : "tool";
|
|
10201
|
+
return `[${toolName}] ${contents.join("\n")}`;
|
|
10202
|
+
}
|
|
10203
|
+
return null;
|
|
10035
10204
|
}
|
|
10036
10205
|
function extractMessageText2(m) {
|
|
10037
|
-
const
|
|
10038
|
-
const
|
|
10039
|
-
const
|
|
10206
|
+
const role = m?.info?.role ?? "unknown";
|
|
10207
|
+
const parts = Array.isArray(m.parts) ? m.parts : [];
|
|
10208
|
+
const segments = [];
|
|
10209
|
+
for (const part of parts) {
|
|
10210
|
+
const serialized = serializeMessagePart(part);
|
|
10211
|
+
if (serialized !== null) segments.push(serialized);
|
|
10212
|
+
}
|
|
10040
10213
|
return `[${role}]
|
|
10041
|
-
${
|
|
10214
|
+
${segments.join("\n\n")}`;
|
|
10042
10215
|
}
|
|
10043
10216
|
function createDecompressTool(factoryCtx) {
|
|
10044
10217
|
return tool3({
|
|
@@ -10090,10 +10263,40 @@ function createDecompressTool(factoryCtx) {
|
|
|
10090
10263
|
const blockMessages = rawMessages.filter((m) => msgIdSet.has(extractMessageId(m)));
|
|
10091
10264
|
const lines2 = blockMessages.map(extractMessageText2);
|
|
10092
10265
|
const { writeFile: writeFile4 } = await import("fs/promises");
|
|
10093
|
-
|
|
10266
|
+
let fileContent;
|
|
10267
|
+
let sourceNote;
|
|
10268
|
+
if (lines2.length > 0) {
|
|
10269
|
+
fileContent = lines2.join("\n\n---\n\n");
|
|
10270
|
+
sourceNote = `${blockMessages.length} original message${blockMessages.length === 1 ? "" : "s"}`;
|
|
10271
|
+
} else {
|
|
10272
|
+
fileContent = targets[0]?.blocks[0]?.summary ?? "(no content available)";
|
|
10273
|
+
sourceNote = `no original messages found; wrote block summary (${targets.length} block${targets.length === 1 ? "" : "s"})`;
|
|
10274
|
+
}
|
|
10094
10275
|
await writeFile4(targetPath, fileContent, "utf-8");
|
|
10095
10276
|
const displayIds2 = targets.map((t) => `b${t.displayId}`).join(", ");
|
|
10096
|
-
return `Block(s) ${displayIds2} content (${
|
|
10277
|
+
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.`;
|
|
10278
|
+
}
|
|
10279
|
+
const availability = checkDecompressSourceAvailability(
|
|
10280
|
+
messagesState,
|
|
10281
|
+
targets,
|
|
10282
|
+
{ full: args.full === true },
|
|
10283
|
+
new Set(rawMessages.map((m) => m.info.id))
|
|
10284
|
+
);
|
|
10285
|
+
if (availability.missingMessageIds.length > 0) {
|
|
10286
|
+
const displayIds2 = targets.map((t) => `b${t.displayId}`).join(", ");
|
|
10287
|
+
const missingRefs = availability.missingMessageIds.map(
|
|
10288
|
+
(id) => ctx.state.messageIds.byRawId.get(id) ?? id
|
|
10289
|
+
);
|
|
10290
|
+
const shownRefs = missingRefs.slice(0, 10).join(", ");
|
|
10291
|
+
const extraCount = missingRefs.length - Math.min(missingRefs.length, 10);
|
|
10292
|
+
ctx.logger.warn("Decompress aborted: source messages missing from host history", {
|
|
10293
|
+
targetDisplayIds: displayIds2,
|
|
10294
|
+
full: args.full === true,
|
|
10295
|
+
requiredCount: availability.requiredMessageIds.length,
|
|
10296
|
+
missingCount: availability.missingMessageIds.length,
|
|
10297
|
+
missingRefs
|
|
10298
|
+
});
|
|
10299
|
+
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
10300
|
}
|
|
10098
10301
|
const activeMessagesBefore = snapshotActiveMessages(messagesState);
|
|
10099
10302
|
const activeBlockIdsBefore = new Set(messagesState.activeBlockIds);
|
|
@@ -10906,7 +11109,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
|
10906
11109
|
import { join as join4 } from "path";
|
|
10907
11110
|
import { existsSync as existsSync4 } from "fs";
|
|
10908
11111
|
import { homedir as homedir3 } from "os";
|
|
10909
|
-
var LOG_VERSION = true ? "1.18.
|
|
11112
|
+
var LOG_VERSION = true ? "1.18.3-pr.475.210" : "dev";
|
|
10910
11113
|
var LEVEL_RANK = {
|
|
10911
11114
|
debug: 10,
|
|
10912
11115
|
info: 20,
|
|
@@ -11712,7 +11915,17 @@ var KEEP_PREFIX_CHARS = 2e3;
|
|
|
11712
11915
|
var KEEP_SUFFIX_CHARS = 2e3;
|
|
11713
11916
|
var PROTECT_RECENT_MESSAGES = 3;
|
|
11714
11917
|
var OUTPUT_RESERVE_TOKENS = 16384;
|
|
11918
|
+
var MAX_OVERHEAD_ERROR_SESSIONS = 1024;
|
|
11715
11919
|
var overheadErrorLogged = /* @__PURE__ */ new Set();
|
|
11920
|
+
function recordOverheadError(sessionKey) {
|
|
11921
|
+
if (overheadErrorLogged.has(sessionKey)) return false;
|
|
11922
|
+
if (overheadErrorLogged.size >= MAX_OVERHEAD_ERROR_SESSIONS) {
|
|
11923
|
+
const oldest = overheadErrorLogged.values().next().value;
|
|
11924
|
+
if (oldest !== void 0) overheadErrorLogged.delete(oldest);
|
|
11925
|
+
}
|
|
11926
|
+
overheadErrorLogged.add(sessionKey);
|
|
11927
|
+
return true;
|
|
11928
|
+
}
|
|
11716
11929
|
function parseGcThreshold(threshold, modelContextLimit) {
|
|
11717
11930
|
if (typeof threshold === "number") return threshold;
|
|
11718
11931
|
const str = threshold ?? "100%";
|
|
@@ -11730,8 +11943,7 @@ function truncateLargeToolOutputs(state, config, logger, messages) {
|
|
|
11730
11943
|
const threshold = Math.min(configuredThreshold, effective.limit - overhead);
|
|
11731
11944
|
if (threshold <= 0) {
|
|
11732
11945
|
const sessionKey = state.sessionId ?? "unknown";
|
|
11733
|
-
if (
|
|
11734
|
-
overheadErrorLogged.add(sessionKey);
|
|
11946
|
+
if (recordOverheadError(sessionKey)) {
|
|
11735
11947
|
logger.error("ACP: model context window too small to fit overhead", {
|
|
11736
11948
|
session: state.sessionId,
|
|
11737
11949
|
limit: effective.limit,
|
|
@@ -13380,36 +13592,6 @@ function configureClientAuth(client) {
|
|
|
13380
13592
|
return client;
|
|
13381
13593
|
}
|
|
13382
13594
|
|
|
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
13595
|
// lib/update.ts
|
|
13414
13596
|
import { readFile as readFile2, rm } from "fs/promises";
|
|
13415
13597
|
import { basename, dirname as dirname4, join as join7 } from "path";
|
|
@@ -13616,15 +13798,14 @@ var server = (async (ctx) => {
|
|
|
13616
13798
|
if (!config.enabled) {
|
|
13617
13799
|
return {};
|
|
13618
13800
|
}
|
|
13619
|
-
|
|
13620
|
-
|
|
13621
|
-
|
|
13622
|
-
);
|
|
13801
|
+
const setupBiliYield = detectBiliEnvYield();
|
|
13802
|
+
if (setupBiliYield) {
|
|
13803
|
+
console.log(biliYieldLogMessage(setupBiliYield));
|
|
13623
13804
|
return {};
|
|
13624
13805
|
}
|
|
13625
13806
|
const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
|
|
13626
13807
|
logger.info("ACP plugin initialized", {
|
|
13627
|
-
version: true ? "1.18.
|
|
13808
|
+
version: true ? "1.18.3-pr.475.210" : "dev",
|
|
13628
13809
|
workspace: ctx.directory,
|
|
13629
13810
|
logLevel: logger.level,
|
|
13630
13811
|
debug: config.debug,
|
|
@@ -13674,7 +13855,21 @@ var server = (async (ctx) => {
|
|
|
13674
13855
|
prompts
|
|
13675
13856
|
};
|
|
13676
13857
|
let disabledByBiliProxy = false;
|
|
13677
|
-
|
|
13858
|
+
let announcedEnvYieldSource;
|
|
13859
|
+
const noteEnvYield = (source) => {
|
|
13860
|
+
if (source === null || announcedEnvYieldSource === source) return;
|
|
13861
|
+
announcedEnvYieldSource = source;
|
|
13862
|
+
console.log(biliYieldLogMessage(source));
|
|
13863
|
+
};
|
|
13864
|
+
const guard = (fn) => (...args) => {
|
|
13865
|
+
if (disabledByBiliProxy) return Promise.resolve();
|
|
13866
|
+
const envYield = detectBiliEnvYield();
|
|
13867
|
+
if (envYield !== null) {
|
|
13868
|
+
noteEnvYield(envYield);
|
|
13869
|
+
return Promise.resolve();
|
|
13870
|
+
}
|
|
13871
|
+
return fn(...args);
|
|
13872
|
+
};
|
|
13678
13873
|
return {
|
|
13679
13874
|
"experimental.chat.system.transform": guard(
|
|
13680
13875
|
createSystemPromptHandler(registry4, logger, config, prompts)
|
|
@@ -13711,14 +13906,9 @@ var server = (async (ctx) => {
|
|
|
13711
13906
|
}
|
|
13712
13907
|
},
|
|
13713
13908
|
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 = {
|
|
13909
|
+
const denyAcpTools = (opencodeConfig2) => {
|
|
13910
|
+
const permission = opencodeConfig2.permission ?? {};
|
|
13911
|
+
opencodeConfig2.permission = {
|
|
13722
13912
|
...permission,
|
|
13723
13913
|
compress: "deny",
|
|
13724
13914
|
decompress: "deny",
|
|
@@ -13726,6 +13916,20 @@ var server = (async (ctx) => {
|
|
|
13726
13916
|
acp_status: "deny",
|
|
13727
13917
|
acp_context_recap: "deny"
|
|
13728
13918
|
};
|
|
13919
|
+
};
|
|
13920
|
+
const biliMatches = findBiliProxyProviders(opencodeConfig.provider);
|
|
13921
|
+
disabledByBiliProxy = biliMatches.length > 0;
|
|
13922
|
+
const envYield = detectBiliEnvYield();
|
|
13923
|
+
if (envYield !== null) {
|
|
13924
|
+
noteEnvYield(envYield);
|
|
13925
|
+
denyAcpTools(opencodeConfig);
|
|
13926
|
+
return;
|
|
13927
|
+
}
|
|
13928
|
+
if (biliMatches.length > 0) {
|
|
13929
|
+
console.log(
|
|
13930
|
+
"[opencode-acp] disabled: /bili/ proxy detected in provider baseURL (" + biliMatches.map((m) => m.provider).join(", ") + ") \u2014 proxy handles compression"
|
|
13931
|
+
);
|
|
13932
|
+
denyAcpTools(opencodeConfig);
|
|
13729
13933
|
return;
|
|
13730
13934
|
}
|
|
13731
13935
|
if (config.compress.permission !== "deny" && compressDisabledByOpencode(opencodeConfig.permission)) {
|
|
@@ -13749,14 +13953,10 @@ var server = (async (ctx) => {
|
|
|
13749
13953
|
primary_tools: [...existingPrimaryTools, ...toolsToAdd]
|
|
13750
13954
|
};
|
|
13751
13955
|
}
|
|
13752
|
-
|
|
13753
|
-
|
|
13754
|
-
|
|
13755
|
-
|
|
13756
|
-
compress: config.compress.permission,
|
|
13757
|
-
acp_status: "allow"
|
|
13758
|
-
};
|
|
13759
|
-
}
|
|
13956
|
+
opencodeConfig.permission = applyAcpToolPermissions(
|
|
13957
|
+
opencodeConfig.permission,
|
|
13958
|
+
config.compress.permission
|
|
13959
|
+
);
|
|
13760
13960
|
hostPermissions.global = opencodeConfig.permission;
|
|
13761
13961
|
hostPermissions.agents = Object.fromEntries(
|
|
13762
13962
|
Object.entries(opencodeConfig.agent ?? {}).map(([name, agent]) => [
|