Time travel lets you inspect earlier states and replay alternate execution paths. agent() exposes the full checkpoint history and branch navigation through Angular Signals. Use it to debug agent decisions, explore alternate paths, and build undo/redo experiences.
Use cases
Debug agent decisions, explore alternate paths, and build undo/redo experiences for your users. Time travel works with any LangGraph agent that persists checkpoints to a thread.
How checkpointing works
Time travel depends on checkpointing on the agent side. LangGraph automatically saves a checkpoint after every node execution when you compile your graph with a checkpointer.
from langgraph.graph import END, START, MessagesState, StateGraphfrom langgraph.checkpoint.memory import MemorySaverfrom langchain_openai import ChatOpenAIllm = ChatOpenAI(model="gpt-5-mini")def call_model(state: MessagesState) -> dict: response = llm.invoke(state["messages"]) return {"messages": [response]}builder = StateGraph(MessagesState)builder.add_node("call_model", call_model)builder.add_edge(START, "call_model")builder.add_edge("call_model", END)# Compile with a checkpointer to enable time travelcheckpointer = MemorySaver()graph = builder.compile(checkpointer=checkpointer)# Run the graph with a thread IDconfig = {"configurable": {"thread_id": "user_123"}}result = graph.invoke( {"messages": [("user", "What is LangGraph?")]}, config=config,)# Browse checkpoint history server-sidefor state in graph.get_state_history(config): print(f"Step: {state.metadata.get('step', '?')}") print(f" Checkpoint: {state.config['configurable']['checkpoint_id']}") print(f" Messages: {len(state.values.get('messages', []))}")# Replay from a specific checkpointpast_config = { "configurable": { "thread_id": "user_123", "checkpoint_id": "<checkpoint-id-from-history>", }}past_state = graph.get_state(past_config)
Browsing execution history
The history() signal contains runtime-neutral AgentCheckpoint entries for the thread. For LangGraph-specific checkpoint metadata, langGraphHistory() exposes the raw ThreadState[]. The framework loads this history with threads.getHistory() when a thread is selected and refreshes it after a run completes.
Each runtime-neutral checkpoint exposes id, label, and values. Each raw ThreadState entry exposes checkpoint, parent_checkpoint, metadata, created_at, and the full values snapshot, giving you complete visibility into every step of execution.
Forking from a checkpoint
Submit with a specific checkpoint to branch execution from an earlier state. This creates a new branch in the thread graph while leaving the original path intact.
forkFromCheckpoint(index: number) { const checkpoint = this.agent.langGraphHistory()[index]?.checkpoint; if (!checkpoint) return; this.agent.submit( { message: 'Try a different approach' }, { checkpoint } );}// Fork with a completely different inputretryWithAlternative(index: number, newInput: string) { const checkpoint = this.agent.langGraphHistory()[index]?.checkpoint; if (!checkpoint) return; this.agent.submit( { message: newInput }, { checkpoint } );}
Branch navigation
Use branch(), setBranch(), and experimentalBranchTree() to navigate between execution branches. Branches are automatically created when you fork from a checkpoint, and the branch tree is derived from raw ThreadState.parent_checkpoint relationships.
// Current branch identifierconst activeBranch = computed(() => agent.branch());// Full branch tree for custom time-travel UIsconst branchTree = computed(() => agent.experimentalBranchTree());// Switch to a different branchselectBranch(branchId: string) { agent.setBranch(branchId);}
Building a history UI
Expose checkpoint history directly in your component to let users scrub through execution steps or rewind to any earlier state.
Diff two checkpoints to understand exactly what changed between execution steps. This is useful for understanding tool call results, message additions, or state mutations.
Use the comparison result to render a diff view, highlight changed fields in your UI, or log what the agent modified during a specific step.
Replaying with modified input
Combine forking with new input to explore how the agent would have responded differently. This is the core of the undo/redo experience.
@Component({ selector: 'app-replay', templateUrl: './replay.component.html', changeDetection: ChangeDetectionStrategy.OnPush,})export class ReplayComponent { readonly agent = inject(AgentService).agent; readonly history = computed(() => this.agent.history()); readonly rawHistory = computed(() => this.agent.langGraphHistory()); readonly canUndo = computed(() => this.history().length > 1); undo() { const history = this.history(); if (history.length < 2) return; // Go back one step const previousCheckpoint = this.rawHistory()[history.length - 2]?.checkpoint; if (!previousCheckpoint) return; this.agent.submit({}, { checkpoint: previousCheckpoint, }); } replayWith(index: number, newMessage: string) { const checkpoint = this.rawHistory()[index]?.checkpoint; if (!checkpoint) return; this.agent.submit( { message: newMessage }, { checkpoint } ); }}
Debugging workflow
Time travel is most useful during development. Inspect why an agent chose a particular path by comparing adjacent checkpoints, then fork to test alternatives without restarting the conversation. Combine history() with Angular DevTools to watch checkpoint arrays update in real time as the agent streams.