April 14-15, 2026ACP schema deep diveTwo commits, one discovery

AI tech developments, April 14-15, 2026: the two-commit day a Mac app discovered ACP's image block is flat, and PDFs have no content type at all

Every April 14-15, 2026 roundup lists GPT-Rosalind, Gemini 3.1 Flash-Lite, GTC's agentic stack, and the 97 million MCP installs. None of them talks about the protocol-shape problem a desktop client app hits the moment a user drags a PDF onto its chat window. Fazm hit that problem on exactly those two days, shipped 33 commits around it, and its fix, at line 1408 of one TypeScript file, is a ten-line diff that tells you something about the Agent Client Protocol you will not find in any news roundup.

F
Fazm
14 min read
4.9from 33 commits across April 14-15, 2026
Commit 865249d9: the ACP schema fix, ±10 lines
Commit b8f0d01d: imagePath becomes attachments[]
ChatAttachmentHelper: 254 lines, one shared drop path
v2.3.2: Fixed AI responses leaking between pop-out chat windows

The anchor fact in ten lines

At 14:30:00 PDT on 2026-04-15, commit 865249d9 landed in /Users/matthewdi/fazm/acp-bridge/src/index.ts, lines 1408 to 1425. The before side is the shape every Anthropic API tutorial uses. The after side is what the Agent Client Protocol actually accepts. The difference is not in a release note. The difference is right here.

ACP schema fix, Apr 15 2026 14:30 PDT

// acp-bridge/src/index.ts  (before commit 865249d9)
// Fazm shipped the Anthropic Messages API shape, because that is
// the shape every Anthropic tutorial and every LLM-generated
// example uses. It is the obvious default.

if (mime.startsWith("image/")) {
  promptBlocks.push({
    type: "image",
    source: {
      type: "base64",
      media_type: mime,
      data: fileData.toString("base64"),
    },
  });
} else if (mime === "application/pdf") {
  promptBlocks.push({
    type: "document",
    source: {
      type: "base64",
      media_type: mime,
      data: fileData.toString("base64"),
    },
  });
}

// Result: the ACP agent silently drops both blocks.
// No error. No warning. The model just never "sees" the file.
0% fewer schema lies
0Commits in 3h on Apr 14 eve
0Lines in ChatAttachmentHelper
0Line of acp-bridge fix
0 protocolsreconciled in one diff

What the rest of the internet covered on April 14-15, 2026

The top ten results for this keyword all talk about roughly the same ten things. That is the definition of a SERP gap: not the absence of coverage, but the absence of a specific, verifiable, client-side artifact that shows what the news cycle meant in practice for an app someone actually has to build. Here is the news cycle on one side, and the Fazm plumbing on the other.

What shipped in the cloud vs what shipped on the Mac

GPT-Rosalind
Gemini 3.1 Flash-Lite
Llama 4 Maverick
Codestral 2
OpenAI Agents SDK
Fazm v2.3.2
attachments[]
flat image block
PDF text reference
per-message buffers

Step one, April 14 at 17:23 PDT: a single string becomes an array

Seven lines added, one removed. That is the surface area of commit b8f0d01d in acp-bridge/src/protocol.ts, and it is the moment every downstream piece of this page becomes possible. You cannot drag three files onto a chat bar if your wire protocol has one optional imagePath string.

acp-bridge/src/protocol.ts

Two minutes later: the Swift side gets its model

At 17:25:31 PDT, commit 063a12b2 adds a ChatAttachment struct to Desktop/Sources/Providers/ChatProvider.swift and extends ChatMessage with an attachments: [ChatAttachment] field. Thirty-seven lines added, two changed. Note the bridgeDict property at the bottom. That is the exact shape that will cross into the TypeScript bridge as a QueryAttachment.

Desktop/Sources/Providers/ChatProvider.swift

The rest of the evening, to the minute

Ten checkpoints between 10:36 PDT on April 14 and 17:18 PDT on April 15. Each one has a commit hash and a real file path. All verifiable with git show <hash> inside /Users/matthewdi/fazm.

1

April 14, 10:36 PDT — Sparkle critical-update floor

Commit 6df1c4e3 flips the appcast sparkle:criticalUpdate gate to minimum version 2.1.0. Anyone below that number is force-upgraded before the attachments work lands, because a v1.x client would send imagePath to a bridge that no longer understands it.

2

April 14, 12:54 PDT — Per-window model selection

Commits 96b7bfab, 542ea6ff, 0e16d45b, and 238e86f2 give each detached chat window its own selectedModel. You can run Opus in one pop-out and Sonnet in another, which matters when the April 14-15 news cycle is about cost-sensitive fast models (Gemini 3.1 Flash-Lite at $0.25 per M input) alongside heavy reasoning models (GPT-5.4, Opus 4.7).

3

April 14, 17:22 PDT — Conversation history in Settings

Commits fa5244bb, 4966a950, b5f5332a add a Conversation History section to Settings, plus the settings search index. Without it, session resume (added minutes later in ba58c841 and fef37fb5) has no user-visible surface.

4

April 14, 17:23 PDT — imagePath → attachments[]

Commit b8f0d01d in /Users/matthewdi/fazm/acp-bridge/src/protocol.ts replaces a single optional string field with a QueryAttachment[] array. Seven lines added, one removed. This is the protocol-level moment that unlocks everything else in this list.

5

April 14, 17:25 PDT — ChatAttachment Swift model

Commit 063a12b2 in Desktop/Sources/Providers/ChatProvider.swift adds a ChatAttachment struct (id, path, name, mimeType, optional thumbnailData) and extends ChatMessage with an attachments: [ChatAttachment] field. +37 lines, -2 lines, one file.

6

April 14, 17:26–18:02 PDT — Floating bar attachment UI

Commits a85f660b, 063a12b2, 25ffa48f, 578712bc, e468ef82 wire the floating control bar's AskAIInputView to ChatAttachmentHelper for drag, drop, paste, and follow-up send. The same sequence (caceb915, 00610a9d at 17:29) adds file and image paste to FazmTextEditor.

7

April 14, 18:46 PDT — ChatAttachmentHelper shared utility

Commit 09546b7b creates Desktop/Sources/FloatingControlBar/ChatAttachmentViews.swift with 254 new lines: addFiles(from:to:), handleDrop(_:to:), addPastedImage(_:to:), generateThumbnail(from:maxSize:), and mimeTypeForExtension(_:). Every chat input surface in the app now drops files through the same function.

8

April 14, 20:09–20:14 PDT — Binary and large-file hardening

Commits 21ce1fb3, 66634116, f3a59ea8, 181db6c7, bdd59d7f add size limits and a path-fallback for large or binary drops, prevent reading binary file attachments into memory, and teach the dropped-item parser to handle URL and Data types. NSLog replaces AppLog for drop failures so Console.app shows them.

9

April 15, 14:30 PDT — The ACP schema fix

Commit 865249d9 in /Users/matthewdi/fazm/acp-bridge/src/index.ts lines 1408-1425 discovers, documents, and patches the two protocol gotchas this whole page is about. The diff is small. The discovery is the point.

10

April 15, 17:12–17:18 PDT — Per-message streaming buffers

Commits 21875930, 4c615476, 2e9ed681, c894bbff, db469333, 28f82d64 refactor the streaming buffer to key on message ID and the tool-executor callbacks to key on sessionKey. Fixes the v2.3.2 CHANGELOG line 'Fixed AI responses leaking between pop-out chat windows when streaming simultaneously.'

The two shapes, drawn

Images cross the protocol boundary as bytes. PDFs cross as a sentence. Each sequence below is what happens inside one query turn, from the moment the user lets go of the mouse to the first token in the response stream.

Dragged image (April 15 2026 path)

UserSwift appacp-bridgeACP agentdrag image onto chatChatAttachmentHelper.handleDropQueryMessage { attachments: [...] }readFileSync + base64{ type: 'image', data, mimeType }stream tokensstream tokensrender in message bubble

Dragged PDF (April 15 2026 path)

UserSwift appacp-bridgeACP agentdrag PDF onto chatQueryMessage { attachments: [...pdf] }mime == pdf, no ACP document type{ type: 'text', text: '[Attached PDF: ... at path: ...]' }Read tool call on pathfile bytes from diskstream tokensstream tokens

What actually prints in the log

Both flows produce log output that pins the exact file path and the exact line number in acp-bridge/src/index.ts where the block was constructed. The image path hits line 1412. The PDF path hits line 1421. Everything after that is model-side.

Drop an image
Drop a PDF

ACP vs Anthropic Messages API, one row at a time

This is not a marketing comparison. It is a schema reference. Both are valid protocols. The point is that if you write client code against one and send to the other, neither side tells you. The blocks just get silently discarded.

FeatureAnthropic Messages APIACP (what Fazm speaks)
Image block shapenested: {type, source: {type, media_type, data}}flat: {type, data, mimeType}
PDF block typedocument block with source.type=base64no document type; text reference + Read tool
Base64 bloat for PDFs~33% encoding overhead on the wirezero (path handed to Read tool on disk)
Tool-surface integrationmodel extracts PDF text server-sidemodel's own Read tool opens file on disk
Where this is verifiableAnthropic Messages API documentationFazm acp-bridge/src/index.ts L1408 (Apr 15)

What you get on top of the schema fix

The April 14-15 window is not just the ACP schema patch. It is also the day per-window model selection, per-message streaming buffers, and session-ID-based conversation resume landed. Together they make a Mac app that can treat a news cycle full of new models, new modalities, and new agent frameworks as something routable instead of something to wait on.

Flat ACP image block

ACP wants {type:'image', data, mimeType}. Not the Anthropic Messages API's nested source object. Commit 865249d9 is where Fazm figured that out in production.

No document type in ACP

PDFs ride across the bridge as a text block with a path reference. The model uses its Read tool on the on-disk file. Zero base64 bloat.

33 commits in 3 hours

April 14, 17:20 to 20:30 PDT. One protocol change, one Swift model, one shared helper, every input surface wired up, large-file hardening. No open PR, no design doc, no ticket.

Per-window models

Each detached chat window remembers its own selectedModel. Run Opus in one window, Sonnet in another. Shipped April 14 at 12:54 PDT.

Per-message buffers

Each streaming message has its own buffer, keyed by message ID. No more token bleed between pop-out windows streaming at the same time.

v2.3.2

Fixed AI responses leaking between pop-out chat windows when streaming simultaneously.

/Users/matthewdi/fazm/CHANGELOG.json, v2.3.2, 2026-04-16

Verify every claim on this page in under five minutes

The evidence is not in a blog screenshot or a tweet. It is in specific commits, specific files, and specific line numbers you can open and inspect. If any row fails, this page is wrong and should be corrected.

Reader verification checklist

  • Drag an image onto the floating bar, a detached chat window, or a follow-up input
  • Drag a PDF from Finder onto any of the same surfaces
  • Paste a screenshot from the clipboard into a chat input
  • Attach a text file, Markdown, or source file by drag or paste
  • Run Opus in one pop-out window and Sonnet in another, same prompt
  • Stream two answers at once across two pop-out windows without token bleed
  • Verify the bridge shape: git show 865249d9 inside /Users/matthewdi/fazm
  • Verify the protocol migration: git show b8f0d01d for the imagePath -> attachments[] diff

Curious about the ACP layer underneath Fazm?

Book a 20-minute walkthrough of how Fazm bridges Swift to the Claude Agent SDK on a Mac, including the attachment and streaming fixes that shipped April 14-15, 2026.

Book a call

Frequently asked questions

What actually shipped on April 14-15, 2026 in the wider AI news cycle?

The SERP-visible headlines for those two days were: OpenAI's 'Trusted access for the next era of cyber defense' program on April 14, OpenAI's evolution of the Agents SDK on April 15, Google's Gemini 3.1 Flash-Lite with 2.5x faster response times at $0.25 per million input tokens, NVIDIA GTC 2026's agentic-AI stack (NeMoCLAW, OpenCLAW), and Anthropic's Model Context Protocol crossing 97 million installs in March. Llama 4 Maverick and Codestral 2 (Apache 2.0) were also tracking strongly in the open-weights ledger. Every one of those is covered in five competing roundups. The one thing those roundups do not cover is the protocol plumbing a desktop client app has to write to actually feed one of those new multimodal models a user's dragged-in image or PDF.

What is the 'flat image block' thing this page talks about?

The Agent Client Protocol (ACP) content block for an image is flat: {type: 'image', data: <base64>, mimeType: <mime>}. The Anthropic Messages API content block for the same image is nested: {type: 'image', source: {type: 'base64', media_type: <mime>, data: <base64>}}. Both are valid in their own universe, and every reasonable engineer writes the Anthropic shape first because that is the one documented everywhere. On April 15, 2026 at 14:30:00 PDT, commit 865249d9 in /Users/matthewdi/fazm/acp-bridge/src/index.ts (lines 1408 to 1425) swaps the second for the first. Before that commit, a dragged image would silently arrive as an opaque content block the ACP agent would not decode. After it, the bytes land as a proper image in the model's context.

What does 'ACP has no document content type' mean for PDFs?

The Anthropic Messages API accepts {type: 'document', source: {type: 'base64', media_type: 'application/pdf', data: <base64>}} and the model will extract the PDF text on its side. The Agent Client Protocol, as of the version Fazm uses on April 15, 2026, has no 'document' content type at all. So the commit 865249d9 fix for PDFs is not a schema adjustment, it is a workaround: inline a text block that describes the attachment as '[Attached PDF: <name> (<size>MB) at path: <path>] Please use the Read tool to read this PDF file.' and let the model's own Read tool open the file from disk. A 1.4 MB PDF does not cross the protocol boundary as bytes; it crosses as a reference and a tool call. Readers can verify by running git show 865249d9 inside /Users/matthewdi/fazm and looking at the diff at acp-bridge/src/index.ts lines 1408-1425.

Why did the attachments refactor itself happen on April 14?

Because on April 14, 2026 the news cycle was already heavy with multimodal capability expansion (GPT-Rosalind for life sciences, Gemini 3.1 Flash-Lite, open-source Llama 4 Maverick), and a field named imagePath?: string is not a shape that survives models that take multi-file, multi-mime input. At 17:23:30 PDT on April 14, commit b8f0d01d replaced a single optional imagePath string on QueryMessage in /Users/matthewdi/fazm/acp-bridge/src/protocol.ts with an optional attachments: QueryAttachment[] where QueryAttachment is {path: string; name: string; mimeType: string}. In the next 85 minutes, the Swift side followed: commit 063a12b2 at 17:25:31 added a ChatAttachment struct to ChatProvider.swift (39 lines added, 2 changed), a101320a at 17:24 added the attachments parameter to sendMessage in ChatProvider, and 09546b7b at 18:46:20 created ChatAttachmentHelper in /Users/matthewdi/fazm/Desktop/Sources/FloatingControlBar/ChatAttachmentViews.swift (254 new lines) to centralize drag-and-drop, paste, thumbnail generation, and temp-file writes for every input surface in the app.

How many commits were in the April 14 evening attachment push?

Thirty-three commits between 17:20 and 20:30 PDT on April 14, 2026, ending with an evening cluster that hardened the drop code (size limits and path fallback for large or binary files in commit 21ce1fb3, a fix for URL and Data drop types in 181db6c7, binary-attachment memory protection in 66634116). The night also shipped auto-resume for the main chat session after an ACP restart (commit fef37fb5), session-ID-based conversation history (ba58c841), and conversation-history list UI in Settings (b5f5332a, fa5244bb). Readers can verify by running git log --pretty=format:'%h %ad %s' --date=format:'%Y-%m-%d %H:%M' --since='2026-04-14 17:00' --until='2026-04-14 21:00' in /Users/matthewdi/fazm.

What is the streaming-buffer-per-message fix on April 15 and why is it on this page?

At 17:12 to 17:18 PDT on April 15, 2026, commits 21875930 (Refactor streaming buffers to be per-message), 4c615476 (Refactor streaming buffer logic to use per-message state), 2e9ed681 (Add per-session callbacks for quick replies and follow-ups), and 28f82d64 (Unregister ChatToolExecutor callbacks on detached window close) landed. Together they fixed the v2.3.2 CHANGELOG line 'Fixed AI responses leaking between pop-out chat windows when streaming simultaneously.' It matters for a news page because the April 14-15 window is also when users started running multiple pop-out chat windows in parallel, one per detached task, each on its own model (Opus in one, Sonnet in another), and the shared-buffer bug would splice tokens across windows. The fix makes each message its own streaming buffer keyed by message ID, and each session its own callback target keyed by sessionKey. That is the concurrency primitive a multi-model, multi-window agent surface needs before you can take a news cycle full of new models seriously.

Does Fazm actually use ACP in production, or is it just a name?

Production. The bridge binary at /Users/matthewdi/fazm/acp-bridge/src/index.ts is a long-running Node process the Swift app launches at startup. It speaks ACP (Agent Client Protocol) to the Claude Agent SDK on one side and a custom line-delimited JSON protocol to the Swift side on the other. QueryMessage, the message type this whole page is about, is the Swift-to-bridge shape; promptBlocks inside handleQuery (the function containing the April 15 fix at lines 1408-1425) is the bridge-to-ACP shape. If you want to read the bridge in one file, it is 2000+ lines of TypeScript at that single index.ts path.

Does this page have anything to do with MCP crossing 97 million installs in March?

Indirectly. The ACP shape for images and the ACP lack-of-document-type for PDFs is a live reminder that 'everyone speaks the same protocol now' is not true yet, even in April 2026. MCP solved the tool-calling surface: which tools does this model have, what do their signatures look like. ACP solves the agent-runtime surface: how does a desktop app stream tokens from a long-lived agent session, attach files to a prompt, interrupt a turn, and resume a session. They are adjacent, not the same, and the fact that a 1.4 MB PDF has to travel from your Finder to your Mac's Claude session as 'please use the Read tool on this path' rather than as a binary content block is the most concrete piece of evidence you will see this month.

How do I reproduce the schema mismatch on my own?

Clone the Fazm bridge (or study the public @zed-industries/agent-client-protocol types) and write a minimal ACP agent that accepts promptBlocks. Send it a block shaped {type: 'image', source: {type: 'base64', media_type: 'image/png', data: '<base64>'}} (the Anthropic Messages API shape) and watch it silently drop the block. Then send {type: 'image', data: '<base64>', mimeType: 'image/png'} (the ACP shape) and watch the image arrive. Now try {type: 'document', source: {type: 'base64', media_type: 'application/pdf', data: '<base64>'}} and watch it get dropped too, because 'document' is not a valid ACP block type at all.

Why does the fix inline the PDF file path instead of uploading the bytes?

Two reasons. One, the model on the other end of the ACP session already has a Read tool (Fazm exposes the full Claude Agent SDK filesystem toolset), so pointing it at /absolute/path/to/doc.pdf is cheaper than sending the whole file as base64 across the protocol boundary. Two, base64-encoded PDFs balloon by about 33 percent, and the app was already getting drops for large attachments (commit 21ce1fb3 on April 14 at 20:09 PDT added explicit size limits and a path-fallback path). A text reference plus a tool call sidesteps both problems and keeps the round trip consistent whether the PDF is 40 KB or 40 MB.

Is there a changelog entry users can see that maps to this work?

The user-visible release notes for v2.3.2 on 2026-04-16 in /Users/matthewdi/fazm/CHANGELOG.json say: 'Tightened privacy language in onboarding and system prompts to accurately say local-first instead of nothing leaves your device,' 'Fixed AI responses leaking between pop-out chat windows when streaming simultaneously,' and 'Fixed onboarding chat splitting AI messages into multiple bubbles.' The second line is the streaming-buffer-per-message fix on April 15. The attachment refactor on April 14 went out in that same v2.3.2 build but is not individually called out, which is the normal pattern for plumbing-layer changes: users never see imagePath become attachments, they just see that drag-and-drop now works across the floating bar, detached chat windows, and follow-up inputs.

fazm.AI Computer Agent for macOS
© 2026 fazm. All rights reserved.

How did this page land for you?

React to reveal totals

Comments ()

Leave a comment to see what others are saying.

Public and anonymous. No signup.