diff --git a/apps/website/content/docs/langgraph/guides/time-travel.mdx b/apps/website/content/docs/langgraph/guides/time-travel.mdx
index c73b7ccf4..7012e1ba7 100644
--- a/apps/website/content/docs/langgraph/guides/time-travel.mdx
+++ b/apps/website/content/docs/langgraph/guides/time-travel.mdx
@@ -36,7 +36,7 @@ The example resolves its connection details at runtime from the host that serves
### Reading the checkpoint history
-The component injects the agent and derives the timeline from it. `langGraphHistory()` is LangGraph's own view of the thread: an array of `ThreadState` snapshots. `selectedIndex` is local interface state, holding the row the user clicked last.
+The component injects the agent and derives the timeline from it. `langGraphHistory()` is LangGraph's own view of the thread: an array of `ThreadState` snapshots. `selectedCheckpointId` is local interface state, holding the checkpoint the user clicked last. New checkpoints can move existing rows, so selection follows the identifier across history refreshes.
diff --git a/cockpit/langgraph/time-travel/angular/e2e/time-travel.spec.ts b/cockpit/langgraph/time-travel/angular/e2e/time-travel.spec.ts
index d37ef4b04..09bf1da67 100644
--- a/cockpit/langgraph/time-travel/angular/e2e/time-travel.spec.ts
+++ b/cockpit/langgraph/time-travel/angular/e2e/time-travel.spec.ts
@@ -83,4 +83,5 @@ test('time-travel: fork starts at the selected checkpoint, not the thread tip',
'Try a different approach from here.',
'This reply belongs to the checkpoint fork.',
]);
+ await expect(page.locator('.row--active .id')).toHaveText(checkpointId);
});
diff --git a/cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts b/cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts
index 7bc007547..c5bf6877b 100644
--- a/cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts
+++ b/cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts
@@ -155,10 +155,16 @@ import { ExampleChatLayoutComponent } from '@threadplane/example-layouts';
No checkpoints yet. Send a message to begin.
}
- @for (state of checkpoints(); track $index; let i = $index) {
-
+ @for (state of checkpoints(); track state.checkpoint?.checkpoint_id ?? $index; let i = $index) {
+
-
+
{{ i + 1 }}
@@ -175,14 +181,14 @@ import { ExampleChatLayoutComponent } from '@threadplane/example-layouts';
@@ -199,8 +205,8 @@ export class TimeTravelComponent {
// #region history
protected readonly agent = injectAgent();
- /** Index of the currently selected checkpoint in the sidebar. */
- protected readonly selectedIndex = signal(-1);
+ /** Selection survives new checkpoints being prepended to the history. */
+ protected readonly selectedCheckpointId = signal(null);
/** Checkpoint history derived from the agent. */
protected readonly checkpoints = computed(
@@ -226,9 +232,9 @@ export class TimeTravelComponent {
* `setBranch()` records the identifier in the agent's `branch()` signal.
* It starts no run: it is a pointer the interface can read back.
*/
- protected select(state: ThreadState, index: number): void {
+ protected select(state: ThreadState): void {
if (state.checkpoint?.checkpoint_id) {
- this.selectedIndex.set(index);
+ this.selectedCheckpointId.set(state.checkpoint.checkpoint_id);
this.agent.setBranch(state.checkpoint.checkpoint_id);
}
}
@@ -240,10 +246,10 @@ export class TimeTravelComponent {
* rather than at its tip, so the new run hangs off the chosen checkpoint
* and the original path stays intact.
*/
- protected fork(state: ThreadState, index: number): void {
+ protected fork(state: ThreadState): void {
const checkpointId = state.checkpoint?.checkpoint_id;
if (!checkpointId) return;
- this.selectedIndex.set(index);
+ this.selectedCheckpointId.set(checkpointId);
void this.agent.submit(
{ message: 'Try a different approach from here.' },
{ checkpointId },
diff --git a/scripts/react-parity/baseline.json b/scripts/react-parity/baseline.json
index 203dda8ee..bebab6c3b 100644
--- a/scripts/react-parity/baseline.json
+++ b/scripts/react-parity/baseline.json
@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
- "baselineHead": "2082b01664a220ab30473d5a991a79154fba7a25",
+ "baselineHead": "82c5469743a8fc3484d395f03372dc6c6975e9b2",
"sourceState": {
"modified": [],
"untracked": []
@@ -2536,7 +2536,7 @@
"id": "doc:apps/website/content/docs/langgraph/guides/time-travel.mdx",
"kind": "doc",
"path": "apps/website/content/docs/langgraph/guides/time-travel.mdx",
- "sha256": "34ea8b1fbb52db5ba0a49e679104c8ca78c4ef476edcd058b201cac301d88022"
+ "sha256": "ddd916b992331e56abe4bf72b0f074dd7649bd521e1702c12a078bb1a6c228ba"
},
{
"id": "doc:apps/website/content/docs/middleware/api/client-tool-helpers.mdx",