| … | … | @@ -902,8 +942,35 @@ function structuredFileChanges(toolCall: AcpToolCallState) { | |
| 953 | + | return typeof diff.oldText === "string" && typeof diff.newText === "string" | |
| 954 | + | ? { oldStr: diff.oldText, newStr: diff.newText } | |
| 955 | + | : undefined; | |
| 956 | + | } | |
🟡 Medium server/adapter.ts:950
A response containing a text/status block and one snippet diff is projected as a located diffStr, losing the snippet’s oldStr/newStr unknown-location handling. acpToolCallSnippetEdit checks the total content length, so it misses the snippet and lets acpToolCallDiffPatch return patch.text; filter to diff entries before checking cardinality.
| 1 | if (!Array.isArray(content)) return undefined; |
| 2 | const diffs = content.flatMap((entry) => { |
| 3 | const diff = unknownRecord(entry); |
| 4 | return diff?.type === "diff" ? [diff] : []; |
| 5 | }); |
| 6 | if (diffs.length !== 1) return undefined; |
| 7 | const diff = diffs[0]; |
| 8 | if (unknownRecord(diff.patch)?.format !== "snippet") return undefined; |
| 9 | return typeof diff.oldText === "string" && typeof diff.newText === "string" |
| 10 | ? { oldStr: diff.oldText, newStr: diff.newText } |
| 11 | : undefined; |
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
| 1 | In file @packages/provider-acp/src/server/adapter.ts around lines 950-956: |
| 2 | |
| 3 | A response containing a text/status block and one `snippet` diff is projected as a located `diffStr`, losing the snippet’s `oldStr`/`newStr` unknown-location handling. `acpToolCallSnippetEdit` checks the total content length, so it misses the snippet and lets `acpToolCallDiffPatch` return `patch.text`; filter to diff entries before checking cardinality. |
Note
🤖 Claude Opus 5.5 responding on behalf of Theo
Real, fixed in the latest commit. acpToolCallSnippetEdit now counts only diff entries, so a status block beside the snippet no longer hides it. The existing snippet test has a row with a text block, which fails without the fix.
5ebe1e1Open thread| … | … | @@ -189,14 +239,66 @@ export function makeAntigravityAcpAdapterFlavor( | |
| 251 | + | // A message may name another thread's stored upload; copy only this lineage's uploads. | |
| 252 | + | const owner = toSafeThreadAttachmentSegment(threadId); | |
| 253 | + | const ownsAll = attachments.every( | |
| 254 | + | (attachment) => parseThreadSegmentFromAttachmentId(attachment.id) === owner, | |
🔴 Critical Adapters/AntigravityAdapterV2.ts:254
A foreign thread's upload can be copied into this thread's allowed root, bypassing upload isolation. Distinct thread IDs can produce the same owner because toSafeThreadAttachmentSegment normalizes and truncates IDs; ownsAll then skips the history lookup, and the matching segment is trusted as ownership. Authorize each attachment using full thread ownership or history rather than the sanitized segment.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
| 1 | In file @apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.ts around line 254: |
| 2 | |
| 3 | A foreign thread's upload can be copied into this thread's allowed root, bypassing upload isolation. Distinct thread IDs can produce the same `owner` because `toSafeThreadAttachmentSegment` normalizes and truncates IDs; `ownsAll` then skips the history lookup, and the matching segment is trusted as ownership. Authorize each attachment using full thread ownership or history rather than the sanitized segment. |
Note
🤖 Claude Opus 5.5 responding on behalf of Theo
Not fixing in this PR. A collision needs a client to create a thread whose ID sanitizes to the target thread's segment, and then name the target's stored upload ID. Upload IDs contain a random UUID, so only a client that can already read the target thread knows one. Generated thread IDs also keep at least 28 random hex characters in the segment. The attachment store has no non-lossy owner record, so stronger checks would mean adding one, which is out of scope here. Main currently lets every thread's agent read every upload, so this PR narrows access either way.
Understood, thanks.