fix(server): avoid duplicating startup notices when timeline is already primed

This commit is contained in:
fdkevin
2026-11-07 17:19:29 +08:00
parent aac228e0a8
commit d765000f44
2 changed files with 45 additions and 3 deletions
@@ -12687,6 +12687,19 @@ test("commits startup notices once on create and after restored history", async
const created = await manager.createAgent({ provider: "codex", cwd: workdir }, undefined, {});
ids.push(created.id);
expect((await manager.getTimelineRows(created.id)).map((row) => row.item)).toEqual([notice]);
await manager.reloadAgentSession(created.id);
expect((await manager.getTimelineRows(created.id)).map((row) => row.item)).toEqual([notice]);
await manager.closeAgent(created.id);
await manager.resumeAgentFromPersistence(
{
provider: "codex",
sessionId: "existing-created",
metadata: { cwd: workdir },
},
undefined,
created.id,
);
expect((await manager.getTimelineRows(created.id)).map((row) => row.item)).toEqual([notice]);
const resumed = await manager.resumeAgentFromPersistence({
provider: "codex",
sessionId: "existing",
@@ -4029,9 +4029,11 @@ export class AgentManager {
// Legacy/imported chats need their existing history before startup rows.
await this.primeTimelineFromLegacyProviderHistory(managed, false, startupHistory);
} else {
for (const entry of buildImportedTimelineRows(session.initialTimeline)) {
this.recordTimeline(managed.id, entry.item, { timestamp: entry.timestamp });
}
await this.recordInitialTimelineIfMissing(
managed,
session.initialTimeline,
durableTimelineHasRows,
);
}
this.refreshSessionPersistence(managed);
}
@@ -4224,6 +4226,33 @@ export class AgentManager {
};
}
private async recordInitialTimelineIfMissing(
agent: ActiveManagedAgent,
initialTimeline: NonNullable<AgentSession["initialTimeline"]>,
durableTimelineHasRows: boolean,
): Promise<void> {
const existingItems: AgentTimelineItem[] = [...this.timelineStore.getItems(agent.id)];
if (existingItems.length === 0 && durableTimelineHasRows && this.durableTimelineStore) {
for (const row of await this.durableTimelineStore.getCommittedRows(agent.id)) {
existingItems.push(row.item);
}
}
for (const entry of buildImportedTimelineRows(initialTimeline)) {
const alreadyRecorded = existingItems.some(
(item) =>
item.type === "notification" &&
entry.item.type === "notification" &&
item.level === entry.item.level &&
item.message === entry.item.message,
);
if (alreadyRecorded) {
continue;
}
this.recordTimeline(agent.id, entry.item, { timestamp: entry.timestamp });
existingItems.push(entry.item);
}
}
private prepareAgentForClosure(
agent: LiveManagedAgent,
cancelReason: string,