Repository navigation
fix(v2): validate context patches by provenance instead of blanket AiMessage.make (#455) - #470
Closed
ranxianglei wants to merge 2 commits into
Closed
ranxianglei wants to merge 2 commits into
ranxianglei wants to merge 2 commits into
Conversation
…Message.make (#455) validateFinalMessages re-ran public AiMessage.make over every assembled outgoing message. On @opencode/ai >= 2.0.18 media is an Asset-instance nominal type only the host's own module copy satisfies, so ACP's copy rejected valid host data and rolled back the whole patch for any attachment-bearing message (accepted=false, invalid-schema). Gate validation on message provenance: skip host-owned messages from nominal re-validation (trusted-valid); run full AiMessage.make only for ACP-composed insertions (isAcpOwnedId) and a new delta check otherwise. The delta check validates only ACP-authored parts (prt_dcp_text_*/prt_dcp_summary_*), requiring well-formed text; provider parts ignored. Protective invariants unchanged: duplicate-message-id, duplicate-call-id, invalid-tool-pair, invalid-order still fire (re-tested). Tests T1.1-T1.5b simulate the 2.0.18 media rejection version-independently via a spy on Message.make; verified to fail when blanket validation is reintroduced. Closes #455
- reuse isAcpAuthoredPartId() in the transformed-side provenance gate instead of duplicating the prt_dcp_text_/prt_dcp_summary_ prefix check (DRY; behavior-identical, adds undefined-id safety) - correct the validateFinalMessages doc comment: the curated barrel is lib/v2/projection.ts (a file), not '../lib/v2/projection' No behavior change.
Owner
Author
[bot] 🏷 结论:PR #470 不应合并——它修的是已退役的 V2 移植代码。按 #442 决议,本仓库对 OpenCode 2.x 统一走 billion-context、 核对到的事实
为什么不该合并(路由依据)
流程偏差(如实指出) 处置与待办
中文摘要:PR #470 修的是已退役的 V2 移植代码( |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #455
What
validateFinalMessagesinlib/v2/projection/patch.tsre-ran the publicAiMessage.make(...)over every assembled outgoing message before accepting a V2 context patch. On@opencode/ai >= 2.0.18,MediaPart.mediais anAsset-instance nominal type (Schema.declare((value) => value instanceof Asset)) that only the host's own module copy satisfies — so ACP's copy rejected the host's own media instances and rolled back the whole patch for any attachment-bearing message (accepted === false,invalid-schema). ACP pruning / protected-content retention / compression blocks were silently dropped and the turn proceeded unpruned.Fix
Validate by provenance, not by nominal schema:
replacements ∪ insertions— exactly the messages ACP mutated or created (the only path by which ACP touches a message).validateFinalMessagesskips host-owned messages from nominal re-validation (trusted-valid, carried through untouched).isAcpOwnedId) → fullAiMessage.make(all parts are ACP text; safe).validateAcpAuthoredParts: every part whose id isprt_dcp_text_*/prt_dcp_summary_*must be well-formed text; provider parts (media/metadata/tool results) are ignored.New exported helpers
isAcpAuthoredPartId+validateAcpAuthoredParts;validateFinalMessagesis now exported for unit tests. The curated../lib/v2/projectionbarrel deliberately does not re-export them, so the public surface is unchanged.Behavior change (old → new)
AiMessage.make→ rejectedinvalid-schemaon 2.0.18+ → whole patch rolled backAiMessage.makeAiMessage.make)AiMessage.makeover the clone (trips on provider parts)duplicate-message-id,duplicate-call-id,invalid-tool-pair,invalid-order)This restores precisely the invariant blanket validation was written for — ACP never authors a malformed part — without rejecting host-owned data.
Tests
tests/v2-context-patch.test.ts(+6, now 35 in file). The cross-module-instanceinstanceof Assetrejection cannot be reproduced naturally in one process, so a spy on the sharedMessage.makesimulates it version-independently (throws when handed a message containing the foreign media part by identity):accepted === true;makenever handed the host media message.finalMessagesby object identity.invalid-schema; id discriminator).duplicate-message-id; orphaned tool result / removed-but-present call →invalid-tool-pair.invalid-order.Regression proof: reintroducing the blanket loop makes T1.1/T1.2/T1.3 fail; restoring the fix makes them pass.
Full suite: 1422/1423 pass. The single failure is pre-existing and unrelated —
tests/soft-block.test.tshardcodes/tmp/opencode-dcp-dangerous-${pid}andmkdirSyncs at module load, which crashes under a read-only/tmpsandbox regardless of this change (fails identically on the base branch here). Candidate follow-up:fs.mkdtemp(os.tmpdir()).Base branch note
Targets
2026-09-17_v2-repeated-system-text(the V2 integration line), notmaster— the entire V2 port (~1400 commits) is WIP and not yet on master, so a master-targeted PR would show the whole V2 subsystem as new files. This fix lives entirely inlib/v2/. Please redirect the base if you have a different V2 integration target.