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_")
);