opencode-acp 1.13.7 → 1.13.9-dev.1
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 +14 -0
- package/README.zh-CN.md +14 -0
- package/dist/index.js +1 -161
- package/dist/index.js.map +1 -1
- package/dist/lib/compress/message.d.ts.map +1 -1
- package/dist/lib/compress/protected-content.d.ts +1 -1
- package/dist/lib/compress/protected-content.d.ts.map +1 -1
- package/dist/lib/compress/range.d.ts.map +1 -1
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/messages/index.d.ts +0 -1
- package/dist/lib/messages/index.d.ts.map +1 -1
- package/dist/lib/state/state.d.ts.map +1 -1
- package/dist/lib/state/types.d.ts +0 -1
- package/dist/lib/state/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/lib/messages/inject/subagent-results.d.ts +0 -4
- package/dist/lib/messages/inject/subagent-results.d.ts.map +0 -1
- package/dist/lib/subagents/subagent-results.d.ts +0 -5
- package/dist/lib/subagents/subagent-results.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -475,6 +475,20 @@ For the complete list with root cause analysis, see the [bug tracker](https://gi
|
|
|
475
475
|
|
|
476
476
|
## Changelog
|
|
477
477
|
|
|
478
|
+
### v1.13.9-dev.1 — Remove Subagent History Rewriting (PR #180)
|
|
479
|
+
|
|
480
|
+
**Problem**: `injectExtendedSubAgentResults` rewrote historical `<task_result>` tool outputs in the parent agent's message history on every transform run when `experimental.allowSubAgents: true`. The `subAgentResultCache` was cleared on every parent↔child session switch and was never persisted, so each transform run re-fetched the subagent session and produced a new historical message body — invalidating the provider prefix cache (observed: ~56% hit rate vs healthy 96–98%, prefix frozen at ~22K tokens).
|
|
481
|
+
|
|
482
|
+
**Fix**: PR #180 — Removed `injectExtendedSubAgentResults` from the message-transform pipeline and `appendProtectedTools`. Deleted `lib/messages/inject/subagent-results.ts` (82 lines) and `lib/subagents/subagent-results.ts` (74 lines). Dropped `subAgentResultCache` field from `SessionState`. The rewrite was redundant: OpenCode natively appends a `state="completed"` message with the full subagent result immediately after the `task` call completes. `experimental.allowSubAgents` still controls whether ACP runs inside subagent sessions — only the parent-history rewriting is gone. Dual-agent reviewed (both APPROVE).
|
|
483
|
+
|
|
484
|
+
Files: `lib/hooks.ts`, `lib/compress/protected-content.ts`, `lib/compress/{message,range}.ts`, `lib/state/{state,types}.ts`, `lib/messages/index.ts`, `AGENTS.md`. 851 tests pass.
|
|
485
|
+
|
|
486
|
+
### v1.13.8-dev.1 — Dev Prerelease Sync (master @ v1.13.7)
|
|
487
|
+
|
|
488
|
+
**Purpose**: Sync the `dev` npm tag with v1.13.7 stable. Content is identical to v1.13.7 — no new code changes. This brings `opencode-acp@dev` up to parity with `opencode-acp@latest` (1.13.7).
|
|
489
|
+
|
|
490
|
+
Files: `package.json`, `README.md`, `README.zh-CN.md`. 851 tests pass (no source changes).
|
|
491
|
+
|
|
478
492
|
### v1.13.7 — Per-Session State + Inactive Block Fixes + Preserve First User Message (PRs #184, #193, #196)
|
|
479
493
|
|
|
480
494
|
**Problem**: Three bugs since v1.13.6. (1) **Subagent state isolation failure** (PR #184): ACP stored a single global `SessionState` per plugin instance. When subagent (child) sessions ran interleaved with the parent, the child's state overwrote the parent's `modelContextLimit` — losing the 1M context window and falling back to the 6K adaptive floor, causing over-aggressive nudging in the parent. The `compressionTiming` tracker was also shared across sessions, risking cross-session collision. (2) **Inactive block opacity** (PR #193): `decompress` rejected inactive blocks with "not active — may have already been decompressed", and `acp_status` hid consumed/inactive blocks entirely. Users could not decompress blocks that were GC'd or consumed by secondary compression, and could not see them in status output. (3) **Zero-user session freeze** (PR #196): When compression pruned all user-role messages (all fell inside compressed ranges), zhipuai-lb rejected the request with HTTP 400 code 1214 (`"messages 参数非法"`, `isRetryable: false`), freezing the session. The v1.13.2 `preserve-last-user` fix searched the messages array for a pruned user to restore — but after OpenCode compaction removes pruned messages from the array, the search finds nothing and zero-user requests slip through.
|
package/README.zh-CN.md
CHANGED
|
@@ -443,6 +443,20 @@ ACP 在首次启动时自动将配置从 `dcp.jsonc` 迁移到 `acp.jsonc`,将
|
|
|
443
443
|
|
|
444
444
|
## 更新日志
|
|
445
445
|
|
|
446
|
+
### v1.13.9-dev.1 — 移除子代理历史重写(PR #180)
|
|
447
|
+
|
|
448
|
+
**问题**:`injectExtendedSubAgentResults` 在 `experimental.allowSubAgents: true` 时,每次消息变换都会重写父代理历史中的 `<task_result>` 工具输出。`subAgentResultCache` 在每次父↔子会话切换时被清空且从不持久化,导致每次变换都重新获取子代理会话并生成新的历史消息体 —— 使 provider prefix cache 失效(观察到的命中率约 56%,健康水平为 96-98%,prefix 冻结在约 22K tokens)。
|
|
449
|
+
|
|
450
|
+
**修复**:PR #180 —— 从消息变换管道和 `appendProtectedTools` 中移除 `injectExtendedSubAgentResults`。删除 `lib/messages/inject/subagent-results.ts`(82 行)和 `lib/subagents/subagent-results.ts`(74 行)。从 `SessionState` 中移除 `subAgentResultCache` 字段。重写是冗余的:OpenCode 原生在 `task` 调用完成后立即追加一条 `state="completed"` 消息(含完整子代理结果)。`experimental.allowSubAgents` 仍然控制 ACP 是否在子代理会话中运行 —— 只是移除了父历史重写。经双 Agent 审查(均 APPROVE)。
|
|
451
|
+
|
|
452
|
+
文件:`lib/hooks.ts`、`lib/compress/protected-content.ts`、`lib/compress/{message,range}.ts`、`lib/state/{state,types}.ts`、`lib/messages/index.ts`、`AGENTS.md`。851 项测试通过。
|
|
453
|
+
|
|
454
|
+
### v1.13.8-dev.1 — Dev 预发布同步(master @ v1.13.7)
|
|
455
|
+
|
|
456
|
+
**目的**:将 npm `dev` 标签同步到 v1.13.7 稳定版。内容与 v1.13.7 完全相同 —— 无新代码变更。使 `opencode-acp@dev` 与 `opencode-acp@latest`(1.13.7)保持一致。
|
|
457
|
+
|
|
458
|
+
文件:`package.json`、`README.md`、`README.zh-CN.md`。851 项测试通过(无源码变更)。
|
|
459
|
+
|
|
446
460
|
### v1.13.7 — 每会话状态隔离 + 失活块修复 + 保留首条用户消息(PR #184、#193、#196)
|
|
447
461
|
|
|
448
462
|
**问题**:v1.13.6 之后的三个 bug。(1)**子代理状态隔离失败**(PR #184):ACP 为每个插件实例存储单个全局 `SessionState`。当子代理(child)会话与父会话交替运行时,子会话的状态覆盖了父会话的 `modelContextLimit` —— 丢失 1M 上下文窗口,回退到 6K 自适应下限,导致父会话中过度触发压缩提醒。`compressionTiming` 追踪器也跨会话共享,存在跨会话碰撞风险。(2)**失活块不可见**(PR #193):`decompress` 拒绝失活块("not active — may have already been decompressed"),`acp_status` 完全隐藏被消费/失活的块。用户无法解压被 GC 回收或被二次压缩消费的块,也无法在状态输出中看到它们。(3)**零用户消息会话冻结**(PR #196):当压缩剪枝了所有 user 角色消息(全部落在压缩范围内)时,zhipuai-lb 拒绝请求(HTTP 400,code 1214,`"messages 参数非法"`,`isRetryable: false`),冻结会话。v1.13.2 的 `preserve-last-user` 修复在消息数组中搜索被剪枝的用户消息来恢复 —— 但 OpenCode 压缩移除被剪枝的消息后,搜索找不到任何内容,零用户请求仍然漏过。
|
package/dist/index.js
CHANGED
|
@@ -2851,64 +2851,6 @@ function isToolNameProtected(toolName, patterns) {
|
|
|
2851
2851
|
return globPatterns.some((pattern) => matchesGlob(toolName, pattern));
|
|
2852
2852
|
}
|
|
2853
2853
|
|
|
2854
|
-
// lib/subagents/subagent-results.ts
|
|
2855
|
-
var SUB_AGENT_RESULT_BLOCK_REGEX = /(<task_result>\s*)([\s\S]*?)(\s*<\/task_result>)/i;
|
|
2856
|
-
function getSubAgentId(part) {
|
|
2857
|
-
const sessionId = part?.state?.metadata?.sessionId;
|
|
2858
|
-
if (typeof sessionId !== "string") {
|
|
2859
|
-
return null;
|
|
2860
|
-
}
|
|
2861
|
-
const value = sessionId.trim();
|
|
2862
|
-
return value.length > 0 ? value : null;
|
|
2863
|
-
}
|
|
2864
|
-
function buildSubagentResultText(messages) {
|
|
2865
|
-
const assistantMessages = messages.filter((message) => message.info.role === "assistant");
|
|
2866
|
-
if (assistantMessages.length === 0) {
|
|
2867
|
-
return "";
|
|
2868
|
-
}
|
|
2869
|
-
const lastAssistant = assistantMessages[assistantMessages.length - 1];
|
|
2870
|
-
const lastText = getLastTextPart(lastAssistant);
|
|
2871
|
-
if (assistantMessages.length < 2) {
|
|
2872
|
-
return lastText;
|
|
2873
|
-
}
|
|
2874
|
-
const secondToLastAssistant = assistantMessages[assistantMessages.length - 2];
|
|
2875
|
-
if (!assistantMessageHasCompressTool(secondToLastAssistant)) {
|
|
2876
|
-
return lastText;
|
|
2877
|
-
}
|
|
2878
|
-
const secondToLastText = getLastTextPart(secondToLastAssistant);
|
|
2879
|
-
return [secondToLastText, lastText].filter((text) => text.length > 0).join("\n\n");
|
|
2880
|
-
}
|
|
2881
|
-
function mergeSubagentResult(output, subAgentResultText) {
|
|
2882
|
-
if (!subAgentResultText || typeof output !== "string") {
|
|
2883
|
-
return output;
|
|
2884
|
-
}
|
|
2885
|
-
return output.replace(
|
|
2886
|
-
SUB_AGENT_RESULT_BLOCK_REGEX,
|
|
2887
|
-
(_match, openTag, _body, closeTag) => `${openTag}${subAgentResultText}${closeTag}`
|
|
2888
|
-
);
|
|
2889
|
-
}
|
|
2890
|
-
function getLastTextPart(message) {
|
|
2891
|
-
const parts = Array.isArray(message.parts) ? message.parts : [];
|
|
2892
|
-
for (let index = parts.length - 1; index >= 0; index--) {
|
|
2893
|
-
const part = parts[index];
|
|
2894
|
-
if (part.type !== "text" || typeof part.text !== "string") {
|
|
2895
|
-
continue;
|
|
2896
|
-
}
|
|
2897
|
-
const text = part.text.trim();
|
|
2898
|
-
if (!text) {
|
|
2899
|
-
continue;
|
|
2900
|
-
}
|
|
2901
|
-
return text;
|
|
2902
|
-
}
|
|
2903
|
-
return "";
|
|
2904
|
-
}
|
|
2905
|
-
function assistantMessageHasCompressTool(message) {
|
|
2906
|
-
const parts = Array.isArray(message.parts) ? message.parts : [];
|
|
2907
|
-
return parts.some(
|
|
2908
|
-
(part) => part.type === "tool" && part.tool === "compress" && part.state?.status === "completed"
|
|
2909
|
-
);
|
|
2910
|
-
}
|
|
2911
|
-
|
|
2912
2854
|
// lib/compress/protected-content.ts
|
|
2913
2855
|
function appendProtectedUserMessages(summary, selection, searchContext, state, enabled) {
|
|
2914
2856
|
if (!enabled) return summary;
|
|
@@ -2975,7 +2917,7 @@ function extractProtectedPromptInfo(text) {
|
|
|
2975
2917
|
}
|
|
2976
2918
|
return protectedTexts;
|
|
2977
2919
|
}
|
|
2978
|
-
async function appendProtectedTools(client, state,
|
|
2920
|
+
async function appendProtectedTools(client, state, summary, selection, searchContext, protectedTools, protectedFilePatterns = []) {
|
|
2979
2921
|
const protectedOutputs = [];
|
|
2980
2922
|
for (const messageId of selection.messageIds) {
|
|
2981
2923
|
const existingCompressionEntry = state.prune.messages.byMessageId.get(messageId);
|
|
@@ -3000,38 +2942,6 @@ async function appendProtectedTools(client, state, allowSubAgents, summary, sele
|
|
|
3000
2942
|
if (part.state?.status === "completed" && part.state?.output) {
|
|
3001
2943
|
output = typeof part.state.output === "string" ? part.state.output : JSON.stringify(part.state.output);
|
|
3002
2944
|
}
|
|
3003
|
-
if (allowSubAgents && part.tool === "task" && part.state?.status === "completed" && typeof part.state?.output === "string") {
|
|
3004
|
-
const cachedSubAgentResult = state.subAgentResultCache.get(part.callID);
|
|
3005
|
-
if (cachedSubAgentResult !== void 0) {
|
|
3006
|
-
if (cachedSubAgentResult) {
|
|
3007
|
-
output = mergeSubagentResult(
|
|
3008
|
-
part.state.output,
|
|
3009
|
-
cachedSubAgentResult
|
|
3010
|
-
);
|
|
3011
|
-
}
|
|
3012
|
-
} else {
|
|
3013
|
-
const subAgentSessionId = getSubAgentId(part);
|
|
3014
|
-
if (subAgentSessionId) {
|
|
3015
|
-
let subAgentResultText = "";
|
|
3016
|
-
try {
|
|
3017
|
-
const subAgentMessages = await fetchSessionMessages(
|
|
3018
|
-
client,
|
|
3019
|
-
subAgentSessionId
|
|
3020
|
-
);
|
|
3021
|
-
subAgentResultText = buildSubagentResultText(subAgentMessages);
|
|
3022
|
-
} catch {
|
|
3023
|
-
subAgentResultText = "";
|
|
3024
|
-
}
|
|
3025
|
-
if (subAgentResultText) {
|
|
3026
|
-
state.subAgentResultCache.set(part.callID, subAgentResultText);
|
|
3027
|
-
output = mergeSubagentResult(
|
|
3028
|
-
part.state.output,
|
|
3029
|
-
subAgentResultText
|
|
3030
|
-
);
|
|
3031
|
-
}
|
|
3032
|
-
}
|
|
3033
|
-
}
|
|
3034
|
-
}
|
|
3035
2945
|
if (output) {
|
|
3036
2946
|
protectedOutputs.push(`
|
|
3037
2947
|
### ${title}
|
|
@@ -4487,7 +4397,6 @@ function createSessionState() {
|
|
|
4487
4397
|
pendingByCallId: /* @__PURE__ */ new Map()
|
|
4488
4398
|
},
|
|
4489
4399
|
toolParameters: /* @__PURE__ */ new Map(),
|
|
4490
|
-
subAgentResultCache: /* @__PURE__ */ new Map(),
|
|
4491
4400
|
toolIdList: [],
|
|
4492
4401
|
messageIds: {
|
|
4493
4402
|
byRawId: /* @__PURE__ */ new Map(),
|
|
@@ -4527,7 +4436,6 @@ function resetSessionState(state) {
|
|
|
4527
4436
|
totalPruneTokens: 0
|
|
4528
4437
|
};
|
|
4529
4438
|
state.toolParameters.clear();
|
|
4530
|
-
state.subAgentResultCache.clear();
|
|
4531
4439
|
state.toolIdList = [];
|
|
4532
4440
|
state.messageIds = {
|
|
4533
4441
|
byRawId: /* @__PURE__ */ new Map(),
|
|
@@ -6535,7 +6443,6 @@ function createCompressMessageTool(factoryCtx) {
|
|
|
6535
6443
|
const summaryWithTools = await appendProtectedTools(
|
|
6536
6444
|
ctx.client,
|
|
6537
6445
|
ctx.state,
|
|
6538
|
-
ctx.config.experimental.allowSubAgents,
|
|
6539
6446
|
summaryWithPromptInfo,
|
|
6540
6447
|
plan.selection,
|
|
6541
6448
|
searchContext,
|
|
@@ -6780,7 +6687,6 @@ function createCompressRangeTool(factoryCtx) {
|
|
|
6780
6687
|
const summaryWithTools = await appendProtectedTools(
|
|
6781
6688
|
ctx.client,
|
|
6782
6689
|
ctx.state,
|
|
6783
|
-
ctx.config.experimental.allowSubAgents,
|
|
6784
6690
|
summaryWithPromptInfo,
|
|
6785
6691
|
plan.selection,
|
|
6786
6692
|
searchContext,
|
|
@@ -8608,65 +8514,6 @@ var injectMessageIds = (state, config, messages, compressionPriorities) => {
|
|
|
8608
8514
|
}
|
|
8609
8515
|
};
|
|
8610
8516
|
|
|
8611
|
-
// lib/messages/inject/subagent-results.ts
|
|
8612
|
-
async function fetchSubAgentMessages(client, sessionId) {
|
|
8613
|
-
const response = await client.session.messages({
|
|
8614
|
-
path: { id: sessionId }
|
|
8615
|
-
});
|
|
8616
|
-
return filterMessages(response?.data || response);
|
|
8617
|
-
}
|
|
8618
|
-
var injectExtendedSubAgentResults = async (client, state, logger, messages, allowSubAgents) => {
|
|
8619
|
-
if (!allowSubAgents) {
|
|
8620
|
-
return;
|
|
8621
|
-
}
|
|
8622
|
-
for (const message of messages) {
|
|
8623
|
-
const parts = Array.isArray(message.parts) ? message.parts : [];
|
|
8624
|
-
for (const part of parts) {
|
|
8625
|
-
if (part.type !== "tool" || part.tool !== "task" || !part.callID) {
|
|
8626
|
-
continue;
|
|
8627
|
-
}
|
|
8628
|
-
if (state.prune.tools.has(part.callID)) {
|
|
8629
|
-
continue;
|
|
8630
|
-
}
|
|
8631
|
-
if (part.state?.status !== "completed" || typeof part.state.output !== "string") {
|
|
8632
|
-
continue;
|
|
8633
|
-
}
|
|
8634
|
-
const cachedResult = state.subAgentResultCache.get(part.callID);
|
|
8635
|
-
if (cachedResult !== void 0) {
|
|
8636
|
-
if (cachedResult) {
|
|
8637
|
-
part.state.output = stripHallucinationsFromString(
|
|
8638
|
-
mergeSubagentResult(part.state.output, cachedResult)
|
|
8639
|
-
);
|
|
8640
|
-
}
|
|
8641
|
-
continue;
|
|
8642
|
-
}
|
|
8643
|
-
const subAgentSessionId = getSubAgentId(part);
|
|
8644
|
-
if (!subAgentSessionId) {
|
|
8645
|
-
continue;
|
|
8646
|
-
}
|
|
8647
|
-
let subAgentMessages = [];
|
|
8648
|
-
try {
|
|
8649
|
-
subAgentMessages = await fetchSubAgentMessages(client, subAgentSessionId);
|
|
8650
|
-
} catch (error) {
|
|
8651
|
-
logger.warn("Failed to fetch subagent session for output expansion", {
|
|
8652
|
-
subAgentSessionId,
|
|
8653
|
-
callID: part.callID,
|
|
8654
|
-
error: error instanceof Error ? error.message : String(error)
|
|
8655
|
-
});
|
|
8656
|
-
continue;
|
|
8657
|
-
}
|
|
8658
|
-
const subAgentResultText = buildSubagentResultText(subAgentMessages);
|
|
8659
|
-
if (!subAgentResultText) {
|
|
8660
|
-
continue;
|
|
8661
|
-
}
|
|
8662
|
-
state.subAgentResultCache.set(part.callID, subAgentResultText);
|
|
8663
|
-
part.state.output = stripHallucinationsFromString(
|
|
8664
|
-
mergeSubagentResult(part.state.output, subAgentResultText)
|
|
8665
|
-
);
|
|
8666
|
-
}
|
|
8667
|
-
}
|
|
8668
|
-
};
|
|
8669
|
-
|
|
8670
8517
|
// lib/messages/reasoning-strip.ts
|
|
8671
8518
|
function stripStaleMetadata(messages) {
|
|
8672
8519
|
const lastUserMessage = getLastUserMessage(messages);
|
|
@@ -11777,13 +11624,6 @@ function createChatMessageTransformHandler(client, registry3, logger, config, pr
|
|
|
11777
11624
|
const prePruneTokens = getCurrentTokenUsage(state, output.messages);
|
|
11778
11625
|
prune(state, logger, config, output.messages);
|
|
11779
11626
|
assignMessageRefs(state, output.messages);
|
|
11780
|
-
await injectExtendedSubAgentResults(
|
|
11781
|
-
client,
|
|
11782
|
-
state,
|
|
11783
|
-
logger,
|
|
11784
|
-
output.messages,
|
|
11785
|
-
config.experimental.allowSubAgents
|
|
11786
|
-
);
|
|
11787
11627
|
const compressionPriorities = buildPriorityMap(config, state, output.messages);
|
|
11788
11628
|
prompts.reload();
|
|
11789
11629
|
injectCompressNudges(
|