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",