Skip to content

feat: agent background edits on notebooks#586

Open
emrberk wants to merge 10 commits into
feat/notebook-cell-toolbar-redesignfrom
feat/agent-passive-notebook-edits
Open

feat: agent background edits on notebooks#586
emrberk wants to merge 10 commits into
feat/notebook-cell-toolbar-redesignfrom
feat/agent-passive-notebook-edits

Conversation

@emrberk

@emrberk emrberk commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

What

Agents could previously only edit the notebook the user had open, and was stealing focus of the users while editing a notebook. This PR lets the MCP bridge and the built-in assistant work on any notebook in the background.

  • The agent edits a notebook in the background, without stealing the focus
  • The user gets notifications about the agent work, and can navigate to the last edit using the notification popper, or MCP pair popper
  • The agent has activate_notebook tool for navigating the user if requested

How it works

  • Dexie controller (notebookDexieController.ts): every op on an unmounted buffer is a queued read → pure transition → write on the persisted buffer row. No mounting, no hidden React tree.
  • Per-buffer FIFO queue (notebookBufferQueue.ts): all writers (agent ops, mount seed read, unmount flush, debounced persists) are strictly ordered per buffer — no lost updates at the mount/unmount boundaries.
  • Mount-claim protocol: when the user opens a buffer mid-edit, the claim + seed read enqueue atomically; in-flight agent ops fail with a typed mounted_mid_edit error telling the agent to re-sync and retry.
  • Headless run_cell (notebookHeadlessRun.ts): runs cells on unmounted notebooks with a verification outcome machine (notebook/cell deleted, user mounted mid-run, cell changed mid-run, superseded) — results are persisted and reported as unverified with a note when the run can't be attributed cleanly.
  • Freshness gating: mutating tools are rejected with STATE_STALE if the user edited the notebook after the agent's last read (per-buffer action sequence, baseline captured before the read). apply_notebook_state is deliberately not up-front gated — it's a wholesale PUT; it keeps its internal mid-apply staleness re-check.
  • activate_notebook tool: brings a buffer to the foreground and reveals a cell (used on user request; intentionally ungated — accepted risk).
  • Footer notifications: agent edits to non-active buffers surface as an unread-style "(new changes)" state on the MCP pill (cyan accent + blink) with a transient popper and a persistent row in the pair popover; cleared when the popover is opened or the user visits the buffer.

Testing

  • New unit tests: queue ordering, mount-claim races, headless run outcomes, freshness baselines, storage cap (max 10 notebooks' results), NOTICE mapping. Suite: 1,418 passing.
  • New e2e spec agentBackgroundEdits.spec.js + shared mcpFakeWebSocket.js test util.
  • Typecheck, lint, build green.

emrberk and others added 10 commits July 8, 2026 14:17
…CellType fallback ladder

Completes Phase 5's read-method carve-out. createModelToolsClient is the sole
client and always provides getCellBasics, so the per-field getCellSql/getCellType
fallbacks in dispatch and applyNotebookState were unreachable in production.
Removes the interface optionals, both method bodies, the orphaned findCellSoft
helper, and rewrites the tests that relied on the fallback onto getCellBasics.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KQfYWS6dFLNBxZ2ncyTNyW
…e-export cleanup

Un-exports six notebooks/ module symbols referenced only within their own file,
removes the always-false `activate` knob from the create/duplicate options and
both call sites, fixes four import-then-re-export cases by pointing consumers at
each symbol's real source, repairs a leaked comment fragment in bufferOwnership,
and removes the obsolete SIMPLIFY_PLAN.md (UNIFY_PLAN.md already gone).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KQfYWS6dFLNBxZ2ncyTNyW
On a mounted notebook, the live runCell wrapper inferred its result by
object-identity diff against the pre-run result. When a user's manual Run
superseded the agent's in-flight run, the cell then held the user's result,
which the wrapper misattributed to the agent — returning success:false/pending
with no unverified note for a write that had actually committed, so the agent
could re-run it and duplicate the DML.

Thread a `superseded` flag out of the live run (useCellExecution → provider
runCell) as a `CellRunOutcome`, and on supersession have the controller return
the SUPERSEDED_RUN_NOTE the headless path already emits, instead of reading the
newer run's result back. Adds Given/When/Then tests for the live runCell
supersede, normal-record, and no-op cases.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KQfYWS6dFLNBxZ2ncyTNyW
…tch (phase 5)

Lands phase 5 additively on top of the #1 supersede fix and the phase-6 sweep,
reconciled so both survive.

- NotebookController -> { bufferId, kind, mutate, readView, runCell }; drop the
  named ops and makeNotebookControllerOps; dispatch builds a transition and
  calls withBoundNotebook(ctrl.mutate) / readView directly
- strip ModelToolsClient to the quest + workspace surface; delete the
  isLiveController pre-checks (validation already lives in the transitions)
- move read serializers into notebookSnapshot; buildSnapshot reads via readView
- preserve the live runCell CellRunOutcome/superseded reporting from the #1 fix
  in the collapsed wrapper, plus its re-homed unknown_cell guard
- keep the sweep's NotebookToolError source import and the dropped activate knob

Original phase-5 authored on machine A; reconciled onto origin here.

Co-Authored-By: emrberk <emreberk5699@gmail.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KQfYWS6dFLNBxZ2ncyTNyW
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant