diff --git a/packages/server/src/server/agent/agent-manager.ts b/packages/server/src/server/agent/agent-manager.ts index 0ac7e7d25c..c8a6b89c55 100644 --- a/packages/server/src/server/agent/agent-manager.ts +++ b/packages/server/src/server/agent/agent-manager.ts @@ -85,6 +85,7 @@ import { AgentTimeline, type TimelineAppendResult, type TimelineCommit, + type TimelineHead, type TimelineEntry, type TimelineIngestResult, type TimelineRetirementCommit, @@ -1449,20 +1450,10 @@ export class AgentManager { return await this.timeline.readRows({ agentId: id }); } - /** - * The timeline head of a loaded agent, or null when this agent is not loaded. Never loads. - * `seq` is the last committed sequence number, a retirement's included. - */ - async getLoadedTimelineHead( - id: string, - ): Promise<{ epoch: string; seq: number; retirementCount: number } | null> { + /** The timeline head of a loaded agent, or null when this agent is not loaded. Never loads. */ + async getLoadedTimelineHead(id: string): Promise { if (!this.getAgent(id)) return null; - const page = await this.timeline.fetch({ agentId: id }, { direction: "tail", limit: 0 }); - return { - epoch: page.epoch, - seq: page.window.nextSeq - 1, - retirementCount: page.retirementCount, - }; + return await this.timeline.describe({ agentId: id }); } async fetchTimeline( diff --git a/packages/server/src/server/agent/timeline/agent-timeline.test.ts b/packages/server/src/server/agent/timeline/agent-timeline.test.ts index a846e22731..c43b9491f4 100644 --- a/packages/server/src/server/agent/timeline/agent-timeline.test.ts +++ b/packages/server/src/server/agent/timeline/agent-timeline.test.ts @@ -208,6 +208,7 @@ describe.each( expect(await timeline.describe(child)).toBeNull(); expect(await timeline.describe(root("a"))).toEqual({ epoch: "parent", + seq: 1, size: 1, retirementCount: 0, rewind: null, @@ -765,6 +766,7 @@ describe.each(TIMELINE_CONTRACT_BACKENDS)( expect(texts(backend.retiredRows(scope))).toEqual(["b", "tool_call", "B"]); expect(await timeline.describe(scope)).toEqual({ epoch: "e", + seq: 8, size: 4, retirementCount: 1, rewind: null, diff --git a/packages/server/src/server/agent/timeline/internal/backend.ts b/packages/server/src/server/agent/timeline/internal/backend.ts index 098897ef36..d47062fcb5 100644 --- a/packages/server/src/server/agent/timeline/internal/backend.ts +++ b/packages/server/src/server/agent/timeline/internal/backend.ts @@ -32,6 +32,8 @@ export interface TimelineSeed { export interface TimelineHead { epoch: string; + /** The last sequence number committed, a retirement's included; 0 before any commit. */ + seq: number; /** Projected (displayed) row count. */ size: number; /** Rewinds applied since the history was created. */ diff --git a/packages/server/src/server/agent/timeline/internal/memory-backend.ts b/packages/server/src/server/agent/timeline/internal/memory-backend.ts index 7117a8d863..53894fb211 100644 --- a/packages/server/src/server/agent/timeline/internal/memory-backend.ts +++ b/packages/server/src/server/agent/timeline/internal/memory-backend.ts @@ -283,6 +283,7 @@ export class MemoryTimelineBackend implements AgentTimelineBackend { private describe(state: MemoryTimelineState): TimelineHead { return { epoch: state.epoch, + seq: state.nextSeq - 1, size: state.projection.size, retirementCount: state.retirementCount, rewind: state.rewind ? { ...state.rewind } : null,