mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-14 13:22:37 +00:00
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 <system-reminder reason="rule_violation" ...>, while only the bare <system-reminder> 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>
This commit is contained in:
@@ -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: '<system-reminder reason="rule_violation" rule="ts-no-tiny-functions" path="builtin-defaults:ts-no-tiny-functions">\nAvoid tiny functions.\n</system-reminder>',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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 () => {
|
test("coalesces replayed subagent poll calls by target set", async () => {
|
||||||
const events = await collectHistory([
|
const events = await collectHistory([
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -360,8 +360,7 @@ function mapEntryMessage(entry: OmpSessionEntry): OmpAgentMessage | null {
|
|||||||
content.length === 1 &&
|
content.length === 1 &&
|
||||||
content[0]?.type === "text" &&
|
content[0]?.type === "text" &&
|
||||||
typeof content[0].text === "string" &&
|
typeof content[0].text === "string" &&
|
||||||
content[0].text.trimStart().startsWith("<system-reminder>") &&
|
isSystemReminder(content[0].text)
|
||||||
content[0].text.trimEnd().endsWith("</system-reminder>")
|
|
||||||
) {
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -370,6 +369,19 @@ function mapEntryMessage(entry: OmpSessionEntry): OmpAgentMessage | null {
|
|||||||
if (["user", "assistant", "toolResult", "custom", "bashExecution"].includes(message.role)) {
|
if (["user", "assistant", "toolResult", "custom", "bashExecution"].includes(message.role)) {
|
||||||
return message as unknown as OmpAgentMessage;
|
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);
|
return visibleFallback(message.role, message);
|
||||||
}
|
}
|
||||||
if (entry.type === "custom_message") {
|
if (entry.type === "custom_message") {
|
||||||
@@ -399,6 +411,13 @@ function mapCustomMessageEntry(entry: OmpSessionEntry): OmpAgentMessage | null {
|
|||||||
} as OmpAgentMessage;
|
} as OmpAgentMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OMP's rule-violation reminder carries attributes: <system-reminder reason="rule_violation" ...>
|
||||||
|
function isSystemReminder(text: string): boolean {
|
||||||
|
return (
|
||||||
|
/^<system-reminder[\s>]/.test(text.trimStart()) && text.trimEnd().endsWith("</system-reminder>")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function isControlEntryType(type: string): boolean {
|
function isControlEntryType(type: string): boolean {
|
||||||
return (
|
return (
|
||||||
type === "session" ||
|
type === "session" ||
|
||||||
@@ -410,6 +429,7 @@ function isControlEntryType(type: string): boolean {
|
|||||||
type === "system_prompt" ||
|
type === "system_prompt" ||
|
||||||
type === "model_change" ||
|
type === "model_change" ||
|
||||||
type === "thinking_level_change" ||
|
type === "thinking_level_change" ||
|
||||||
|
type === "ttsr_injection" ||
|
||||||
type === "tool_execution" ||
|
type === "tool_execution" ||
|
||||||
type.startsWith("tool_execution_")
|
type.startsWith("tool_execution_")
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user