You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Running UNDO command reverts changes done by hand #55121
Thanks for the report. I could reproduce this on v2.0.26, and it still happens on the latest dev (c1615b1), but only when the manual edit happens while the turn is still running.
Steps (simulated model, git project):
Ask the model to read notes.txt without changing it.
While it is still working on its reply, edit notes.txt by hand.
Once the turn finishes, run /undo.
notes.txt goes back to its original content and the manual edit is gone (see the recording and screenshot below).
When I made the edit only after the turn had fully finished, /undo kept it.
The likely cause: OpenCode takes a snapshot of the project at the start and end of each model step. It counts any file that changed in between as changed by the agent, even when you changed it, and /undo puts those files back. This looks like the same underlying problem as #54026 (undo discards changes made outside the session while a step is running), so it will probably be handled together with it. Related: #51360, #44511.
If your edit was made after the model had completely finished replying, please say so, since that would be a different case from what I saw.
recording.mp4
Reproduction script (drive2.ts)
// Variant: manual edit made while the read-only turn is still running (between tool call and final reply).import{Effect}from"effect"import{readFileSync,writeFileSync,existsSync}from"node:fs"import{join}from"node:path"import{Llm,OpenCodeDriver}from"opencode-drive"exportdefaultOpenCodeDriver.use({
...(process.env.OC_DEV ? {opencode: {dev: process.env.OC_DEV}} : {}),config: {snapshots: true}asany,project: {git: true,files: {"notes.txt": "original line\n"}},tui: {recording: true,keypressOverlay: true},},({ ui, llm, artifacts })=>Effect.gen(function*(){constprojectFile=[join(artifacts,"files","notes.txt"),join(artifacts,"notes.txt")].find((p)=>existsSync(p))!yield*llm.queue(Llm.toolCall({index: 0,id: "call_read",name: "read",input: {path: "notes.txt"}}),Llm.finish("tool-calls"),)yield*ui.submit("Read notes.txt, do not change anything")// Edit by hand while the model is still "thinking" about the second step.yield*Effect.sleep(1500)writeFileSync(projectFile,"original line\nMANUAL EDIT BY USER\n")yield*Effect.sleep(1000)yield*llm.send(Llm.text("The file says: original line. No changes made."))yield*ui.waitFor("No changes made.")yield*Effect.sleep(1500)yield*Effect.log(`before undo: ${JSON.stringify(readFileSync(projectFile,"utf8"))}`)yield*ui.submit("/undo")yield*Effect.sleep(3000)yield*ui.screenshot("after-undo")constafter=readFileSync(projectFile,"utf8")yield*Effect.log(`after undo: ${JSON.stringify(after)}`)yield*Effect.log(after.includes("MANUAL EDIT") ? "RESULT: manual edit kept" : "RESULT: BUG - manual edit reverted by /undo")}),)
Run it with opencode-drive: opencode-drive run ./drive2.ts. It drives opencode from your PATH; set OC_DEV to the path of an OpenCode checkout to run that from source instead.
Yeah, snapshotting the whole shared worktree at the start and end of a step means any manual save while the model is still running gets swept into the revert diff.
I ran into this exact issue and ended up building a small Go runtime called msh (https://github.com/msh-protocol/msh) that runs agent shell commands inside isolated shadow git worktrees instead of the live tree. That way you can run msh undo to roll back individual agent commands atomically without wiping out your manual editor changes.
I would be fine with undoing LLM + human changes, but what annoys me that the LLM only read the document, so there are no changes to revert. Only when LLM actually makes some change, only then undo command should actually do something - reverting to the original, even if human made some changes afterwards - that is acceptable to me.
Thanks for reporting this and clarifying that the model only read the file! This is the same problem as #54026, where reverting a message can discard unrelated changes in the shared worktree, and we're tracking it there. Please subscribe or add a 👍 there; your read-only example is worth adding too.
I’d like to investigate and implement a fix for this. I plan to reproduce the race between an active turn, an external file edit, and /undo, then add a regression test ensuring user-authored changes are not reverted. I’ll first inspect the undo snapshot/patch tracking logic and propose the smallest conflict-safe fix.
Activity
opencode-agent commentedon Oct 13, 2026
Thanks for the report. I could reproduce this on v2.0.26, and it still happens on the latest
dev(c1615b1), but only when the manual edit happens while the turn is still running.Steps (simulated model, git project):
notes.txtwithout changing it.notes.txtby hand./undo.notes.txtgoes back to its original content and the manual edit is gone (see the recording and screenshot below).When I made the edit only after the turn had fully finished,
/undokept it.The likely cause: OpenCode takes a snapshot of the project at the start and end of each model step. It counts any file that changed in between as changed by the agent, even when you changed it, and
/undoputs those files back. This looks like the same underlying problem as #54026 (undo discards changes made outside the session while a step is running), so it will probably be handled together with it. Related: #51360, #44511.If your edit was made after the model had completely finished replying, please say so, since that would be a different case from what I saw.
recording.mp4
Reproduction script (drive2.ts)
Run it with opencode-drive:
opencode-drive run ./drive2.ts. It drivesopencodefrom yourPATH; setOC_DEVto the path of an OpenCode checkout to run that from source instead.kilopal commentedon Oct 18, 2026
Yeah, snapshotting the whole shared worktree at the start and end of a step means any manual save while the model is still running gets swept into the revert diff.
I ran into this exact issue and ended up building a small Go runtime called
msh(https://github.com/msh-protocol/msh) that runs agent shell commands inside isolated shadow git worktrees instead of the live tree. That way you can runmsh undoto roll back individual agent commands atomically without wiping out your manual editor changes.AtomicFS commentedon Oct 18, 2026
I would be fine with undoing LLM + human changes, but what annoys me that the LLM only read the document, so there are no changes to revert. Only when LLM actually makes some change, only then
undocommand should actually do something - reverting to the original, even if human made some changes afterwards - that is acceptable to me.neriousy commentedon Oct 20, 2026
Thanks for reporting this and clarifying that the model only read the file! This is the same problem as #54026, where reverting a message can discard unrelated changes in the shared worktree, and we're tracking it there. Please subscribe or add a 👍 there; your read-only example is worth adding too.
Jackdrakes commentedon Oct 25, 2026
I’d like to investigate and implement a fix for this. I plan to reproduce the race between an active turn, an external file edit, and /undo, then add a regression test ensuring user-authored changes are not reverted. I’ll first inspect the undo snapshot/patch tracking logic and propose the smallest conflict-safe fix.