Skip to content

feat: restore checkpoint after any AI response turn#2356

Draft
Basit-Balogun10 wants to merge 8 commits into
PostHog:mainfrom
Basit-Balogun10:claude/gracious-hawking-38aade
Draft

feat: restore checkpoint after any AI response turn#2356
Basit-Balogun10 wants to merge 8 commits into
PostHog:mainfrom
Basit-Balogun10:claude/gracious-hawking-38aade

Conversation

@Basit-Balogun10
Copy link
Copy Markdown

Problem

After an AI turn completes, there's no way to roll back to the git state captured at the end of that turn. If the agent goes down a wrong path you have to manually reset the branch or throw away work — there's no checkpoint-based undo.

Tracked in #724 / #2328.

Changes

Core restore flow

  • New checkpoint tRPC router (routers/checkpoint.ts) with a restore procedure that:
    1. Runs RevertCheckpointSaga to reset the worktree to the saved git state
    2. Truncates the session .jsonl to the restore point so the session replays correctly
    3. Collects any orphaned checkpoint refs from the discarded lines and deletes them — no accumulation of stale refs from abandoned future turns
  • getSessionInfo(taskRunId) added to AgentService to expose { sessionId, repoPath } without leaking internal types

Per-turn restore button

  • buildConversationItems now tracks lastCheckpointId per turn — set when a _posthog/git_checkpoint notification is seen, cleared on each new turn start
  • AgentMessage gets a restore button in the top-right corner: active when the turn has a checkpoint, disabled (with tooltip) when it doesn't. Only shown on completed turns.
  • SessionUpdateView forwards the new showRestoreButton / onRestoreCheckpoint props down

Checkpoint timeline modal (mod+shift+h)

  • CheckpointTimelineModal — command-palette-style dialog listing every checkpoint in the session, newest first, with the first 120 chars of the user message that started that turn and a relative timestamp
  • Parses events: AcpMessage[] directly (Option A — no new store or API surface, scoped to the current session by definition)
  • Shortcut registered as "checkpoint-timeline" in CONFIGURABLE_SHORTCUT_IDS / DEFAULT_KEYBINDINGS / KEYBOARD_SHORTCUTS so it's user-remappable via the keybindings store — built on top of feat: configurable keyboard shortcuts #2321

Restore confirmation

  • RestoreCheckpointDialog — confirmation dialog with an amber warning before the restore runs, so accidental clicks don't immediately drop work
  • useRestoreCheckpoint hook wires the full flow: opens the confirmation dialog → calls the tRPC mutation → truncates the in-memory events via sessionStoreSetters.truncateEventsToCheckpoint → shows a success/error toast

How did you test this?

  • Ran pnpm --filter code typecheck against tsconfig.node.json and tsconfig.web.json — both exit 0 (pre-existing @posthog/platform / @posthog/agent module errors are unrelated to this change)
  • Ran pnpm lint — biome passes clean
  • Manually traced the restore flow end-to-end: checkpoint tRPC router logic, JSONL truncation returning orphaned IDs, git ref deletion
  • Verified buildConversationItems correctly sets lastCheckpointId for turns that have a _posthog/git_checkpoint notification and null for those that don't
  • Checked CheckpointTimelineModal parses a sample events array with multiple turns and renders entries newest-first with correct snippets

Users can now remap any of the 17 configurable shortcuts via Settings >
Shortcuts (or the ⌘/ sheet). Custom bindings fully replace all defaults
(including alternates) and multiple custom combos per action are supported.
Bindings persist across sessions via electronStorage.

- Add `configurable` flag + `DEFAULT_KEYBINDINGS` map to keyboard-shortcuts.ts
- New `keybindingsStore` (persist + electronStorage) with array-based custom combos,
  conflict detection helper, and individual/bulk reset
- New `useShortcut(id)` hook — reactive Zustand selector, feeds useHotkeys
- New `Keycap` component extracted to avoid circular imports
- New `ShortcutRecorder` component: click + to enter recording mode, captures
  keydown, shows conflict toast, per-binding × remove, per-shortcut ↩ reset
- Update all useHotkeys call sites (GlobalEventHandlers, SpaceSwitcher,
  usePanelKeyboardShortcuts, ExternalAppsOpener) to use useShortcut()
- KeyboardShortcutsSheet: configurable rows render ShortcutRecorder instead of
  static keycaps; "Reset all shortcuts" button shown when customisations exist

Generated-By: PostHog Code
Task-Id: 80405bf7-239f-4b60-a1cf-5a4777fb7218
Bare letter keys (e.g. just "k") would fire every time that character is
typed anywhere in the app. Require at least mod/ctrl/alt to be held.

Generated-By: PostHog Code
Task-Id: 80405bf7-239f-4b60-a1cf-5a4777fb7218
24 tests covering resolveKey, addKeybinding, removeKeybinding,
resetShortcut, resetAll, getKey, and findConflict — including
conflict detection against comma-separated default alternates.

Generated-By: PostHog Code
Task-Id: 80405bf7-239f-4b60-a1cf-5a4777fb7218
- KeyboardShortcutsSheet header now reads the "shortcuts" key via
  useShortcut() so the trigger keycap updates when remapped
- ExternalAppsOpener dropdown labels for open-in-editor and copy-path
  now derive from useShortcut() + formatHotkeyParts() instead of
  hardcoded Mac-only symbols

test(e2e): add Playwright shortcut sheet tests

Covers sheet open/close, category sections, hover controls, recording
mode entry/cancellation, bare-key rejection, saving bindings, conflict
detection, removing bindings, per-shortcut reset, and reset-all.

Generated-By: PostHog Code
Task-Id: 80405bf7-239f-4b60-a1cf-5a4777fb7218
Hardcoded Cmd glyphs were leaking onto Windows in the send-messages
dropdown and the tiptap paste hint, and two handlers were gated on
metaKey only so the corresponding shortcut never fired on Windows
(mod+1..9 task switching, Cmd/Ctrl-click multi-select in the inbox).

Generated-By: PostHog Code
Task-Id: 80405bf7-239f-4b60-a1cf-5a4777fb7218
- Add prompt-history-prev/next to CONFIGURABLE_SHORTCUT_IDS and
  DEFAULT_KEYBINDINGS so they appear in the shortcuts sheet and
  can be rebound like any other shortcut
- Add tiptapEventToCombo() — accepts shift-only combos (no Ctrl/Meta
  required) so shift+up/down can be matched against live bindings
- Fix eventToCombo() to normalise Arrow-prefixed key names (ArrowUp to up)
- Wire useTiptapEditor to resolve prompt-history keys from the store
  instead of hardcoding event.shiftKey
- Fix paste hint toast to show the live paste-as-file binding instead
  of the hardcoded mod+shift+v string
- Fix noStaticElementInteractions lint on recording modal backdrop
- Rewrite E2E shortcut tests to match the current recording modal UI
  (chips + right-click context menu) rather than the old hover-button
  and inline-input design
- Deduplicate in updateKeybinding — conflict detection excludes the
  shortcut being edited so editing one binding to match another on the
  same shortcut could produce ["ctrl+q","ctrl+q"], duplicate React keys
  and broken chip reconciliation
- Remove ArrowUp/Down gate around prompt-history navigation so custom
  non-arrow bindings (e.g. Ctrl+K) actually fire when pressed, not just
  when the physical key is an arrow
- Remove obvious section-divider comments and redundant JSX labels
  (Header, Scrollable list, Sticky footer); keep non-obvious rationale
  comments (window-level capture, backdrop dismiss, canAddMore budget,
  dedup note, ArrowKey gate explanation)
- Add checkpoint tRPC router with restore procedure that reverts git state,
  truncates session JSONL to the restore point, and deletes orphaned
  checkpoint refs for abandoned future turns
- Track lastCheckpointId per turn in buildConversationItems so each
  completed agent turn knows its git ref
- Show per-turn restore button in AgentMessage (disabled with tooltip when
  no checkpoint exists for that turn)
- Add CheckpointTimelineModal (mod+shift+h) — command-palette-style list of
  all checkpoints in the session, newest first, with user message snippet
  and relative timestamp; shortcut is user-remappable via keybindings store
- Add RestoreCheckpointDialog with confirmation warning before reverting
- Add useRestoreCheckpoint hook to wire restore flow end-to-end
- Register checkpoint-timeline as a configurable shortcut

Closes PostHog#2328
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