opencode-acp 1.14.22-pr.327.45 → 1.14.22-pr.330.48
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 +2 -1
- package/README.zh-CN.md +2 -2
- package/dist/index.js +86 -22
- package/dist/index.js.map +1 -1
- package/dist/lib/messages/inject/inject.d.ts.map +1 -1
- package/dist/lib/messages/inject/utils.d.ts +45 -5
- package/dist/lib/messages/inject/utils.d.ts.map +1 -1
- package/dist/lib/state/model-limits.d.ts +2 -2
- package/dist/lib/update.d.ts +15 -0
- package/dist/lib/update.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -278,7 +278,8 @@ Each level overrides the previous, so project settings take priority over global
|
|
|
278
278
|
"$schema": "https://raw.githubusercontent.com/ranxianglei/opencode-acp/master/dcp.schema.json",
|
|
279
279
|
// Enable or disable the plugin
|
|
280
280
|
"enabled": true,
|
|
281
|
-
// Automatically update npm-installed ACP when a newer
|
|
281
|
+
// Automatically update npm-installed ACP when a newer version is available
|
|
282
|
+
// on the installed dist-tag/spec (@stable follows stable, @latest follows latest).
|
|
282
283
|
// Version-locked plugin specs are not updated.
|
|
283
284
|
"autoUpdate": true,
|
|
284
285
|
// Enable INFO/DEBUG logging + per-request snapshots to ~/.config/opencode/logs/acp/
|
package/README.zh-CN.md
CHANGED
|
@@ -233,8 +233,8 @@ ACP 使用自己的配置文件,按以下顺序搜索:
|
|
|
233
233
|
"$schema": "https://raw.githubusercontent.com/ranxianglei/opencode-acp/master/dcp.schema.json",
|
|
234
234
|
// Enable or disable the plugin
|
|
235
235
|
"enabled": true,
|
|
236
|
-
//
|
|
237
|
-
//
|
|
236
|
+
// 自动更新 npm 安装的 ACP:跟踪安装所用 dist-tag/规范(@stable 跟随 stable,@latest 跟随 latest)。
|
|
237
|
+
// 版本锁定的规范不会被更新。
|
|
238
238
|
"autoUpdate": true,
|
|
239
239
|
// Enable INFO/DEBUG logging + per-request snapshots to ~/.config/opencode/logs/acp/
|
|
240
240
|
// (WARN/ERROR are always logged to daily/<date>.log)
|
package/dist/index.js
CHANGED
|
@@ -7186,7 +7186,9 @@ function refNum(ref) {
|
|
|
7186
7186
|
function buildCompressibleRanges(messages, state, protectedTools = [], protectedFilePatterns = [], protectedZoneRefs) {
|
|
7187
7187
|
const msgInfo = [];
|
|
7188
7188
|
const protectedMsgInfo = [];
|
|
7189
|
-
|
|
7189
|
+
const lastUserRefIdx = [];
|
|
7190
|
+
for (let mi = 0; mi < messages.length; mi++) {
|
|
7191
|
+
const msg = messages[mi];
|
|
7190
7192
|
if (isSyntheticMessage(msg)) continue;
|
|
7191
7193
|
const ref = state.messageIds.byRawId.get(msg.info.id);
|
|
7192
7194
|
if (!ref) continue;
|
|
@@ -7221,15 +7223,27 @@ function buildCompressibleRanges(messages, state, protectedTools = [], protected
|
|
|
7221
7223
|
}
|
|
7222
7224
|
let tokens = 0;
|
|
7223
7225
|
let isTool = false;
|
|
7226
|
+
let hasMeaningfulPart = false;
|
|
7224
7227
|
for (const part of msg.parts || []) {
|
|
7225
7228
|
if (part.type === "text" && typeof part.text === "string") {
|
|
7226
7229
|
tokens += Math.round(part.text.length / 4);
|
|
7230
|
+
if (part.text.trim().length > 0) hasMeaningfulPart = true;
|
|
7227
7231
|
} else if (part.type !== "text" && part.type !== "reasoning") {
|
|
7228
7232
|
tokens += Math.round(JSON.stringify(part).length / 4);
|
|
7229
7233
|
isTool = true;
|
|
7234
|
+
hasMeaningfulPart = true;
|
|
7230
7235
|
}
|
|
7231
7236
|
}
|
|
7232
|
-
|
|
7237
|
+
if (msg.info.role === "user" && !isIgnoredUserMessage(msg)) {
|
|
7238
|
+
lastUserRefIdx.length = 0;
|
|
7239
|
+
lastUserRefIdx.push(msgInfo.length);
|
|
7240
|
+
}
|
|
7241
|
+
msgInfo.push({ ref, refNum: rn, tokens, effectiveTokens: 0, meaningful: hasMeaningfulPart, isTool, isUser: msg.info.role === "user" });
|
|
7242
|
+
}
|
|
7243
|
+
const lastUserIdx = lastUserRefIdx.length > 0 ? lastUserRefIdx[0] : -1;
|
|
7244
|
+
for (let i = 0; i < msgInfo.length; i++) {
|
|
7245
|
+
const info = msgInfo[i];
|
|
7246
|
+
info.effectiveTokens = i !== lastUserIdx && info.meaningful ? info.tokens : 0;
|
|
7233
7247
|
}
|
|
7234
7248
|
const groups = [];
|
|
7235
7249
|
let cur = null;
|
|
@@ -7255,6 +7269,7 @@ function buildCompressibleRanges(messages, state, protectedTools = [], protected
|
|
|
7255
7269
|
endRef: info.ref,
|
|
7256
7270
|
count: 1,
|
|
7257
7271
|
tokens: info.tokens,
|
|
7272
|
+
effectiveTokens: info.effectiveTokens,
|
|
7258
7273
|
toolPct: info.isTool ? 100 : 0,
|
|
7259
7274
|
textPct: info.isTool ? 0 : 100
|
|
7260
7275
|
};
|
|
@@ -7262,6 +7277,7 @@ function buildCompressibleRanges(messages, state, protectedTools = [], protected
|
|
|
7262
7277
|
cur.endRef = info.ref;
|
|
7263
7278
|
cur.count++;
|
|
7264
7279
|
cur.tokens += info.tokens;
|
|
7280
|
+
cur.effectiveTokens += info.effectiveTokens;
|
|
7265
7281
|
if (info.isTool) {
|
|
7266
7282
|
cur.toolPct = Math.round((cur.toolPct * (cur.count - 1) + 100) / cur.count);
|
|
7267
7283
|
} else {
|
|
@@ -7304,6 +7320,11 @@ function buildCompressibleRanges(messages, state, protectedTools = [], protected
|
|
|
7304
7320
|
protected: protectedGroups
|
|
7305
7321
|
};
|
|
7306
7322
|
}
|
|
7323
|
+
var EFFECTIVE_MIN_COMPRESSIBLE_TOKENS = 1250;
|
|
7324
|
+
function resolveEffectiveFloor(config) {
|
|
7325
|
+
const minChars = config.compress?.minCompressRange ?? 5e3;
|
|
7326
|
+
return minChars > 0 ? Math.floor(minChars / 4) : 0;
|
|
7327
|
+
}
|
|
7307
7328
|
function filterRecommendedRanges(compressible, _protectedRanges, options) {
|
|
7308
7329
|
const { logger } = options;
|
|
7309
7330
|
const log = logger?.debug.bind(logger);
|
|
@@ -7311,12 +7332,22 @@ function filterRecommendedRanges(compressible, _protectedRanges, options) {
|
|
|
7311
7332
|
log?.("filterRecommendedRanges: no compressible ranges, returning empty");
|
|
7312
7333
|
return [];
|
|
7313
7334
|
}
|
|
7314
|
-
const
|
|
7315
|
-
|
|
7335
|
+
const floor = options.minEffectiveTokens ?? EFFECTIVE_MIN_COMPRESSIBLE_TOKENS;
|
|
7336
|
+
const kept = compressible.filter((r) => {
|
|
7337
|
+
const effective = r.effectiveTokens ?? r.tokens;
|
|
7338
|
+
return effective > 0 && effective >= floor;
|
|
7339
|
+
});
|
|
7340
|
+
const result = kept.map(
|
|
7341
|
+
(r, i) => i === kept.length - 1 ? { ...r, dangerous: true } : r
|
|
7316
7342
|
);
|
|
7317
|
-
log?.("filterRecommendedRanges:
|
|
7343
|
+
log?.("filterRecommendedRanges: effective-token floor applied", {
|
|
7318
7344
|
inputRanges: compressible.length,
|
|
7319
|
-
outputRanges: result.length
|
|
7345
|
+
outputRanges: result.length,
|
|
7346
|
+
floor,
|
|
7347
|
+
dropped: compressible.filter((r) => {
|
|
7348
|
+
const effective = r.effectiveTokens ?? r.tokens;
|
|
7349
|
+
return effective <= 0 || effective < floor;
|
|
7350
|
+
}).map((r) => `${r.startRef}\u2013${r.endRef} (${r.effectiveTokens ?? r.tokens} eff tokens)`)
|
|
7320
7351
|
});
|
|
7321
7352
|
return result;
|
|
7322
7353
|
}
|
|
@@ -7325,8 +7356,10 @@ function formatCompressibleRanges(ranges, protectedRanges) {
|
|
|
7325
7356
|
if (!protectedRanges || protectedRanges.length === 0) {
|
|
7326
7357
|
if (ranges.length === 0) return "";
|
|
7327
7358
|
const lines2 = ranges.map((r) => {
|
|
7359
|
+
const eff = r.effectiveTokens ?? r.tokens;
|
|
7360
|
+
const size = eff < r.tokens ? `${fmt(eff)} effective of ${fmt(r.tokens)}` : fmt(r.tokens);
|
|
7328
7361
|
const suffix = r.dangerous ? " \u26A0\uFE0F NOT recommended unless you are certain. If you MUST compress this, pass `dangerous: true`." : "";
|
|
7329
|
-
return ` ${r.startRef}\u2013${r.endRef} ${r.count} msgs ${
|
|
7362
|
+
return ` ${r.startRef}\u2013${r.endRef} ${r.count} msgs ${size} [tool ${r.toolPct}% | text ${r.textPct}%]${suffix}`;
|
|
7330
7363
|
});
|
|
7331
7364
|
return `Compressible ranges (oldest first):
|
|
7332
7365
|
${lines2.join("\n")}`;
|
|
@@ -7342,7 +7375,7 @@ ${lines2.join("\n")}`;
|
|
|
7342
7375
|
tokens: r.tokens,
|
|
7343
7376
|
toolPct: r.toolPct,
|
|
7344
7377
|
textPct: r.textPct,
|
|
7345
|
-
compressibleTokens: r.tokens,
|
|
7378
|
+
compressibleTokens: r.effectiveTokens ?? r.tokens,
|
|
7346
7379
|
compressibleCount: r.count,
|
|
7347
7380
|
protectedTokens: 0,
|
|
7348
7381
|
protectedCount: 0,
|
|
@@ -7631,14 +7664,18 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
|
|
|
7631
7664
|
const recommendedRanges = filterRecommendedRanges(
|
|
7632
7665
|
unprotectedCompressible,
|
|
7633
7666
|
contextRanges.protected,
|
|
7634
|
-
{ logger }
|
|
7667
|
+
{ logger, minEffectiveTokens: resolveEffectiveFloor(config) }
|
|
7635
7668
|
);
|
|
7636
7669
|
const hasRecommendations = recommendedRanges.length > 0;
|
|
7637
7670
|
const allProtected = contextRanges.compressible.length === 0 && contextRanges.protected.length > 0;
|
|
7638
7671
|
const allInProtectedZone = protectedRefs.size > 0 && unprotectedCompressible.length === 0;
|
|
7639
|
-
const
|
|
7640
|
-
const
|
|
7641
|
-
|
|
7672
|
+
const allBelowMin = contextRanges.compressible.length > 0 && recommendedRanges.length === 0;
|
|
7673
|
+
const nothingToCompress = allProtected || allInProtectedZone || allBelowMin;
|
|
7674
|
+
const emergencyNoTargets = emergencyOverride && nothingToCompress;
|
|
7675
|
+
const noticeCadenceMet = state.nudges.lastNudgeShownTokens === void 0 || growthSinceBaseline !== void 0 && growthSinceBaseline >= growthFloor;
|
|
7676
|
+
const shouldInjectNudge = nudgeAllowed && !nothingToCompress;
|
|
7677
|
+
const shouldInjectNotice = emergencyNoTargets && noticeCadenceMet;
|
|
7678
|
+
let shouldInject = shouldInjectNudge || shouldInjectNotice;
|
|
7642
7679
|
if (shouldInjectNudge) {
|
|
7643
7680
|
applyAnchoredNudges(state, config, messages, prompts, compressionPriorities, currentTokens, modelContextLimit, suffixMessage);
|
|
7644
7681
|
}
|
|
@@ -7772,8 +7809,19 @@ ${formatCompressibleRanges(recommendedRanges, contextRanges.protected)}`;
|
|
|
7772
7809
|
Use \`acp_status({scope:"uncompressed"})\` to re-fetch compressible ranges after compressing, or \`acp_status\` for compressed block details.`;
|
|
7773
7810
|
appendToLastTextPart(suffixMessage, breakdown);
|
|
7774
7811
|
}
|
|
7775
|
-
if (effectiveTipsVariant === "maxLimit") {
|
|
7812
|
+
if (effectiveTipsVariant === "maxLimit" && !emergencyNoTargets) {
|
|
7776
7813
|
tipsText = "\n\n\u26A0\uFE0F Context limit reached \u2014 compress now. Prioritize consumed tool outputs.\n\n" + HOW_TO_COMPRESS_RULES + '\n\n{ "topic": "...", "content": [{ "startId": "<ID>", "endId": "<ID>", "summary": "..." }] }\n\nOnly use IDs from visible messages above. Compress older work first.';
|
|
7814
|
+
} else if (shouldInjectNotice) {
|
|
7815
|
+
const emergencyPct = currentTokens !== void 0 && modelContextLimit !== void 0 && modelContextLimit > 0 ? Math.round(currentTokens / modelContextLimit * 100) : void 0;
|
|
7816
|
+
tipsText = `
|
|
7817
|
+
|
|
7818
|
+
\u{1F6A8} Context is critically full${emergencyPct !== void 0 ? ` (${emergencyPct}% of limit)` : ""} and there is nothing left that can be safely compressed.
|
|
7819
|
+
Do NOT retry compress on the same ranges \u2014 they will keep failing.
|
|
7820
|
+
You cannot execute user commands yourself. Act now via your reply/message tool:
|
|
7821
|
+
- Inform the user that context is full and compression is exhausted
|
|
7822
|
+
- Recommend they run /acp export (archives compression summaries to a file), then /compact or start a new session
|
|
7823
|
+
- Alternatively, ask them to relax protected-tool / preserve-recent settings so compression becomes possible
|
|
7824
|
+
Then stop retrying and await the user's response.`;
|
|
7777
7825
|
}
|
|
7778
7826
|
state.nudges.lastNudgeShownTokens = currentTokens;
|
|
7779
7827
|
{
|
|
@@ -9026,7 +9074,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
|
9026
9074
|
import { join as join3 } from "path";
|
|
9027
9075
|
import { existsSync as existsSync3 } from "fs";
|
|
9028
9076
|
import { homedir as homedir3 } from "os";
|
|
9029
|
-
var LOG_VERSION = true ? "1.14.22-pr.
|
|
9077
|
+
var LOG_VERSION = true ? "1.14.22-pr.330.48" : "dev";
|
|
9030
9078
|
var Logger = class {
|
|
9031
9079
|
logDir;
|
|
9032
9080
|
enabled;
|
|
@@ -11243,12 +11291,14 @@ async function checkAutoUpdate(signal) {
|
|
|
11243
11291
|
if (!packageDir) return { updated: false };
|
|
11244
11292
|
const pkg = await readPackageJson(join6(packageDir, "package.json"));
|
|
11245
11293
|
if (!pkg?.name || !pkg.version) return { updated: false };
|
|
11246
|
-
const
|
|
11294
|
+
const target = await updateTarget(packageDir, pkg.name);
|
|
11295
|
+
if (!target) return { updated: false };
|
|
11296
|
+
const tag = specUpdateTag(target.spec);
|
|
11297
|
+
if (!tag) return { updated: false };
|
|
11298
|
+
const latest = await fetchLatestVersion(pkg.name, tag, signal);
|
|
11247
11299
|
if (!latest || !isVersionNewer(latest, pkg.version)) return { updated: false };
|
|
11248
|
-
const removeDir = await updateRemoveDir(packageDir, pkg.name);
|
|
11249
|
-
if (!removeDir) return { updated: false };
|
|
11250
11300
|
try {
|
|
11251
|
-
await rm(removeDir, { recursive: true, force: true });
|
|
11301
|
+
await rm(target.removeDir, { recursive: true, force: true });
|
|
11252
11302
|
} catch {
|
|
11253
11303
|
return {
|
|
11254
11304
|
updated: false,
|
|
@@ -11270,7 +11320,7 @@ async function findPackageDir(name) {
|
|
|
11270
11320
|
dir = parent;
|
|
11271
11321
|
}
|
|
11272
11322
|
}
|
|
11273
|
-
async function
|
|
11323
|
+
async function updateTarget(packageDir, name) {
|
|
11274
11324
|
const packageParent = dirname4(packageDir);
|
|
11275
11325
|
const nodeModulesDir = basename(packageParent).startsWith("@") ? dirname4(packageParent) : packageParent;
|
|
11276
11326
|
if (basename(nodeModulesDir) !== "node_modules") return void 0;
|
|
@@ -11278,7 +11328,7 @@ async function updateRemoveDir(packageDir, name) {
|
|
|
11278
11328
|
const wrapperPkg = await readPackageJson(join6(wrapperDir, "package.json"));
|
|
11279
11329
|
const spec = wrapperSpec(wrapperDir, name) ?? wrapperPkg?.dependencies?.[name];
|
|
11280
11330
|
if (!spec || !isAutoUpdatableSpec(spec)) return void 0;
|
|
11281
|
-
return wrapperDir;
|
|
11331
|
+
return { removeDir: wrapperDir, spec };
|
|
11282
11332
|
}
|
|
11283
11333
|
function wrapperSpec(wrapperDir, name) {
|
|
11284
11334
|
if (name.startsWith("@")) {
|
|
@@ -11299,8 +11349,22 @@ function isAutoUpdatableSpec(spec) {
|
|
|
11299
11349
|
if (/^[~^]/.test(value)) return true;
|
|
11300
11350
|
if (/^(?:>=|>|<=|<)/.test(value)) return true;
|
|
11301
11351
|
if (/\s+(?:\|\||-|[<>=])\s+/.test(value)) return true;
|
|
11352
|
+
if (isDistTag(value)) return true;
|
|
11302
11353
|
return false;
|
|
11303
11354
|
}
|
|
11355
|
+
function specUpdateTag(spec) {
|
|
11356
|
+
const value = spec.trim();
|
|
11357
|
+
if (!isAutoUpdatableSpec(value)) return void 0;
|
|
11358
|
+
if (value === "*") return "latest";
|
|
11359
|
+
if (isDistTag(value)) return value;
|
|
11360
|
+
return "latest";
|
|
11361
|
+
}
|
|
11362
|
+
function isDistTag(value) {
|
|
11363
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value)) return false;
|
|
11364
|
+
if (parseVersion(value)) return false;
|
|
11365
|
+
if (/\.x(\.|$)/i.test(value)) return false;
|
|
11366
|
+
return true;
|
|
11367
|
+
}
|
|
11304
11368
|
async function readPackageJson(path) {
|
|
11305
11369
|
try {
|
|
11306
11370
|
const data = JSON.parse(await readFile2(path, "utf-8"));
|
|
@@ -11309,10 +11373,10 @@ async function readPackageJson(path) {
|
|
|
11309
11373
|
return void 0;
|
|
11310
11374
|
}
|
|
11311
11375
|
}
|
|
11312
|
-
async function fetchLatestVersion(name, signal) {
|
|
11376
|
+
async function fetchLatestVersion(name, tag, signal) {
|
|
11313
11377
|
try {
|
|
11314
11378
|
const response = await fetch(
|
|
11315
|
-
`https://registry.npmjs.org/${encodeURIComponent(name)}
|
|
11379
|
+
`https://registry.npmjs.org/${encodeURIComponent(name)}/${encodeURIComponent(tag)}`,
|
|
11316
11380
|
{
|
|
11317
11381
|
signal
|
|
11318
11382
|
}
|