> ## Documentation Index
> Fetch the complete documentation index at: https://agentclientprotocol.com/llms.txt
> Use this file to discover all available pages before exploring further.

# v2 Diff File States

Author(s): [@benbrandt](https://github.com/benbrandt)

## Elevator pitch

> What are you proposing to change?

ACP v2 should replace the v1 `Diff` shape's ambiguous `oldText` / `newText`
fields with a structured diff content shape that can represent text patches,
non-text file changes, and file-level operations such as add, delete, modify,
move, and copy.

Every diff should carry structured file-change metadata. It can also carry
renderable Git `--patch` text in an optional top-level `patch` field.

## Status quo

> How do things work today and what problems does this cause? Why would we change things?

ACP v1 represents a diff as a path plus old and new text:

```json theme={null}
{
  "type": "diff",
  "path": "/home/user/project/src/config.json",
  "oldText": "{\n  \"debug\": false\n}",
  "newText": "{\n  \"debug\": true\n}"
}
```

That shape has several problems:

* `newText: ""` cannot distinguish a deleted file from an empty file without an extra flag.
* Moves and copies have no direct representation.
* Binary files, symlinks, and directory entries do not have useful `oldText` and `newText` values.
* Clients have to reconstruct or parse a diff to render changes.
* The protocol cannot express whether a file path was added, deleted, modified, moved, or copied.

The v1 [Represent deleted files in diff](/rfds/diff-delete) RFD is a useful
stop-gap for deletes, but v2 can use a cleaner breaking shape instead of adding
more flags to the v1 structure.

## What we propose to do about it

> What are you proposing to improve the situation?

ACP v2 should make `ToolCallContent` diffs carry a required `changes` array and
an optional `patch` object.

Use `patch` for changes that have useful renderable patch text:

```json theme={null}
{
  "type": "diff",
  "changes": [
    {
      "operation": "modify",
      "path": "/home/user/project/src/config.json",
      "fileType": "text",
      "mimeType": "application/json"
    }
  ],
  "patch": {
    "format": "git_patch",
    "text": "diff --git /home/user/project/src/config.json /home/user/project/src/config.json\n--- /home/user/project/src/config.json\n+++ /home/user/project/src/config.json\n@@ -1,3 +1,3 @@\n {\n-  \"debug\": false\n+  \"debug\": true\n }\n"
  }
}
```

`changes` is authoritative for affected paths and operations. `patch`, when
present, provides renderable text for some or all of those changes and MUST be
consistent with `changes`.

The patch format should be `git_patch`: one or more `diff --git` sections in
Git's [`--patch` (`-p`) text format](https://git-scm.com/docs/diff-generate-patch).
Paths MUST be absolute. Surrounding commit metadata and email envelopes MUST NOT
be included. Agents SHOULD provide `patch` whenever feasible. Clients MUST
handle diffs where `patch` is omitted or `null`.

Omit `patch` when patch text is not useful:

```json theme={null}
{
  "type": "diff",
  "changes": [
    {
      "operation": "modify",
      "path": "/home/user/project/assets/logo.png",
      "fileType": "binary",
      "mimeType": "image/png"
    }
  ]
}
```

Omitted and `null` `patch` values are equivalent and mean no renderable patch
text was provided.

The initial operation set should be:

* `add`
* `delete`
* `modify`
* `move`
* `copy`

`add`, `delete`, and `modify` use `path`. `move` and `copy` use `oldPath` and
`path`. All paths in protocol payloads are absolute.

The optional `fileType` field should describe the file entry when known:

* `text`
* `binary`
* `directory`
* `symlink`

The optional `mimeType` field should carry a MIME type when known. Omitted and
`null` values for `fileType` and `mimeType` mean the value is unknown.

## Shiny future

> How will things play out once this feature exists?

Clients can render text diffs without needing to synthesize patch text, and can
show file-level summaries without parsing patch syntax. Agents can report binary
updates, symlink changes, deletes, moves, and copies without forcing those cases
through text-only fields.

The v2 diff surface is also easier for SDKs to model because operations are
explicit enum variants rather than inferred states from optional text fields.

## Implementation details and plan

> Tell me more about your implementation. What is your detailed implementation plan?

1. Replace the v2 `Diff` struct's old text fields with a required `changes` array.
2. Add an optional `patch` field with `format` and `text`.
3. Use `git_patch` as the ACP-defined patch format, and document that Agents SHOULD provide it whenever feasible.
4. Add structured `DiffChange` operation variants for add, delete, modify, move, and copy, with path fields owned by each operation variant.
5. Add optional `fileType` and `mimeType` fields for non-text and unknown file content.
6. Preserve custom or future operations where the receiver can safely store, replay, forward, or display a generic summary.
7. Update generated schema and v2 protocol docs.

## Frequently asked questions

> What questions have arisen over the course of authoring this document or during subsequent discussions?

### What does `git_patch` mean?

Text changes use unified hunks; Git headers can also describe file operations.
This is more specific than a plain unified diff and contains only patch sections,
not surrounding commit metadata or email envelopes. The format is broadly
supported and can describe multiple files. ACP can add other patch formats later.

### Why is `patch` top-level instead of attached to individual changes?

Git `--patch` text can describe several files and several operation kinds at once.
Keeping `patch` top-level makes it renderable context for the whole diff content
item, while `changes` remains the structured source for paths and operations.

### Should patch text be restricted to some operation variants?

No. Git `--patch` text can represent adds, deletes, modifies, moves/renames, copies,
and some binary changes. If an Agent has useful patch text for a set of changes,
it can include `patch`; otherwise it can omit it.

### Why is `patch` optional if Agents SHOULD provide it?

Some changes have no useful patch text, especially same-path binary updates and
symlink target changes. Patch generation can also fail or be unavailable in a
given Agent implementation. Keeping `patch` optional lets clients rely on
structured `changes`, while the SHOULD encourages better rendering when patch
text is practical.

### What kind of `modify` does not have a patch?

Mostly in-place non-text updates, such as a binary file update at the same path
or a symlink target change. Text modifications should normally use
`patch` so clients have renderable text.

### Why include both patch text and structured changes?

Patch text gives clients an immediate baseline rendering path. Structured
changes give clients operation and path metadata without requiring patch
parsing, which matters for summaries, file trees, permissions, and non-text
indicators.

## Revision history

* 2026-07-20: Define `git_patch` as Git `--patch` text and rename `patch.diff` to `patch.text`
* 2026-07-02: Initial draft
