From c36d398829bc6274b84704b9171985fb6e3331e6 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Wed, 11 Nov 2026 04:00:27 +0100 Subject: [PATCH] perf(server): read the subscription freshness head without fetching the transcript The freshness check read the head by fetching every row of the agent's history on each subscribe. The timeline head now carries its last committed sequence number, so the check reads only the head. --- .../server/src/server/agent/agent-manager.ts | 17 ++++------------- .../agent/timeline/agent-timeline.test.ts | 2 ++ .../server/agent/timeline/internal/backend.ts | 2 ++ .../agent/timeline/internal/memory-backend.ts | 1 + 4 files changed, 9 insertions(+), 13 deletions(-) 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,