From 2dbfd1e0f8bebbb803d9e522f845bbdec88ffe2d Mon Sep 17 00:00:00 2001 From: "paseo-bot[bot]" <266920839+paseo-bot[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 10:02:12 +0200 Subject: [PATCH] fix(omp): reopened chats no longer show extra "Custom message" rows (#6198) After a daemon restart, an OMP chat replays from its session file. Three kinds of entries that the live timeline never shows were replayed as collapsed "Custom message" rows: - the rule-violation reminder OMP adds after a tool call matches one of its rules, a developer message that opens with , while only the bare tag was hidden - the ttsr_injection entry OMP records alongside that reminder - the fileMention message an @-mentioned file adds, and the other OMP message roles the live timeline drops Closes #6131 Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com> --- .../providers/omp/history-mapper.test.ts | 84 +++++++++++++++++++ .../src/server/agent/providers/omp/history.ts | 24 +++++- 2 files changed, 106 insertions(+), 2 deletions(-) diff --git a/packages/server/src/server/agent/providers/omp/history-mapper.test.ts b/packages/server/src/server/agent/providers/omp/history-mapper.test.ts index a747d3bee0..61e6d0e73d 100644 --- a/packages/server/src/server/agent/providers/omp/history-mapper.test.ts +++ b/packages/server/src/server/agent/providers/omp/history-mapper.test.ts @@ -252,6 +252,90 @@ describe("OMP history mapper", () => { ]); }); + test("replays a rule-violation run with the same rows the live timeline showed", async () => { + const dir = mkdtempSync(join(tmpdir(), "omp-rule-reminder-history-")); + const sessionFile = join(dir, "session.jsonl"); + writeFileSync( + sessionFile, + [ + { type: "session", id: "root" }, + { + type: "message", + id: "prompt", + parentId: "root", + message: { + role: "user", + content: "Read @notes.md, then write tiny.ts", + }, + }, + { + type: "message", + id: "mention", + parentId: "prompt", + message: { + role: "fileMention", + files: [ + { + path: "notes.md", + content: "[notes.md#468D]\n1:# notes", + lineCount: 1, + }, + ], + }, + }, + { + type: "ttsr_injection", + id: "injection", + parentId: "mention", + injectedRules: ["ts-no-tiny-functions"], + }, + { + type: "message", + id: "rule-reminder", + parentId: "injection", + message: { + role: "developer", + content: [ + { + type: "text", + text: '\nAvoid tiny functions.\n', + }, + ], + }, + }, + { + type: "message", + id: "answer", + parentId: "rule-reminder", + message: { + role: "assistant", + content: [{ type: "text", text: "FINAL_ANSWER" }], + }, + }, + ] + .map((entry) => JSON.stringify(entry)) + .join("\n"), + ); + const events: AgentStreamEvent[] = []; + for await (const event of streamOmpHistory({ + sessionFile, + provider: "omp", + })) + events.push(event); + expect(events.map((event) => event.item)).toEqual([ + { + type: "user_message", + text: "Read @notes.md, then write tiny.ts", + messageId: "prompt", + }, + { + type: "assistant_message", + text: "FINAL_ANSWER", + messageId: "omp-history-assistant-1", + }, + ]); + }); + test("coalesces replayed subagent poll calls by target set", async () => { const events = await collectHistory([ { diff --git a/packages/server/src/server/agent/providers/omp/history.ts b/packages/server/src/server/agent/providers/omp/history.ts index adca06a45d..2e86b9670c 100644 --- a/packages/server/src/server/agent/providers/omp/history.ts +++ b/packages/server/src/server/agent/providers/omp/history.ts @@ -360,8 +360,7 @@ function mapEntryMessage(entry: OmpSessionEntry): OmpAgentMessage | null { content.length === 1 && content[0]?.type === "text" && typeof content[0].text === "string" && - content[0].text.trimStart().startsWith("") && - content[0].text.trimEnd().endsWith("") + isSystemReminder(content[0].text) ) { return null; } @@ -370,6 +369,19 @@ function mapEntryMessage(entry: OmpSessionEntry): OmpAgentMessage | null { if (["user", "assistant", "toolResult", "custom", "bashExecution"].includes(message.role)) { return message as unknown as OmpAgentMessage; } + // OMP roles the live timeline does not render either, such as the fileMention that carries + // an @-mentioned file's content. + if ( + [ + "pythonExecution", + "hookMessage", + "branchSummary", + "compactionSummary", + "fileMention", + ].includes(message.role) + ) { + return null; + } return visibleFallback(message.role, message); } if (entry.type === "custom_message") { @@ -399,6 +411,13 @@ function mapCustomMessageEntry(entry: OmpSessionEntry): OmpAgentMessage | null { } as OmpAgentMessage; } +// OMP's rule-violation reminder carries attributes: +function isSystemReminder(text: string): boolean { + return ( + /^]/.test(text.trimStart()) && text.trimEnd().endsWith("") + ); +} + function isControlEntryType(type: string): boolean { return ( type === "session" || @@ -410,6 +429,7 @@ function isControlEntryType(type: string): boolean { type === "system_prompt" || type === "model_change" || type === "thinking_level_change" || + type === "ttsr_injection" || type === "tool_execution" || type.startsWith("tool_execution_") );