[FEATURE] Let a host complete a deferred Edit or Write, and accept the result it supplies on resume

Status Open
Reported on v2.1.273
Maintainer reply None cached
Activity 0 comments · opened Oct 2, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

I'm building a Temporal plugin for the Claude Agent SDK (temporalio/ai-integrations#33). Each tool call that changes something runs once, as its own durable step outside the model step. The PreToolUse hook returns defer, the host runs the call, and the next run resumes the session with the call's result as the tool_result for that tool_use_id. This works for Bash and MCP tools.

It does not work for Edit:

  1. Claude reads notes.txt (Read runs normally), then calls Edit on it (old_string: "hello", new_string: "bye"). The hook returns defer, and the run stops with stop_reason: "tool_deferred".
  2. The host runs exactly that call: it resumes a copy of the session in which the hook allows only that call. Claude Code returns "The file ... has been updated successfully", and the file changes.
  3. The host resumes the session and supplies that result for the Edit's tool_use_id.

Claude receives this instead of the supplied result:

<tool_use_error>File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.</tool_use_error>

On resume, Claude Code checks the deferred Edit again, and the check fails because the edit itself changed the file. So Claude is told the edit failed, although it happened.

Write does the same when it writes over a file Claude has read. A deferred Write that creates a new file is delivered normally.

Proposed Solution

Let the host complete a deferred call itself. When a session resumes with a tool_result already supplied for the deferred tool_use_id, Claude Code would accept that result as the call's result and not check the call again.

If this should be opt-in, the host could say so, for example through the PreToolUse hook or an option on resume.

Then Edit and Write could run once, outside the model step, like Bash and MCP tools already can.

Alternative Solutions

  1. The documented flow: on resume, the hook returns allow and the tool runs in the resumed session. Then the edit runs inside the next model step, not in its own step, so a retry of that step can run it again.
  2. What the plugin does today: Edit and Write stay inside the model step, and only Bash and MCP tools run as their own steps. Their calls and results are still recorded, but a retried step can run a file edit again.

Priority

High - Significant impact on productivity

Feature Category

Developer tools/SDK

Use Case Example

  1. A coding agent runs in a Temporal Workflow with the plugin.
  2. Claude calls Edit on a config file. The hook defers it.
  3. The Workflow can wait for a person to approve the edit, then runs it once, as its own step. Temporal records the result.
  4. The next model step resumes with that result, and Claude continues.
  5. If a later step fails and is retried, the edit does not run again, because its result is already recorded.

Bash commands and MCP tools already work like this in the plugin.

Additional Context

A test that reproduces it, with no credentials (the real engine against a local fake Messages API):
https://github.com/osamastro7-droid/ai-integrations/blob/615e8ffffc45f3ddd10a578c94c25a795d1198df/python/claude_agent_sdk/tests/test_engine_tools.py#L483

To run it: on the claude-agent-sdk branch of osamastro7-droid/ai-integrations, in python/claude_agent_sdk, run make sync && uv run pytest tests/test_engine_tools.py -k edit_cannot_run_apart

Checked on Claude Code 2.1.273 (Linux, macOS and Windows), 2.1.286 and 2.1.287 (Linux). The Write cases were checked on 2.1.286 and 2.1.287.

Related: #16712 added defer. There, the deferred tool runs on resume. This request is for the case where the host has already run the call and supplies its result.

View original on GitHub ↗