mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-23 17:52:49 +00:00
fix(omp): an OMP run with a message Paseo does not render now finishes instead of staying running (#6112)
* fix(omp): finish the turn when OMP's run contains a developer message OMP 18.5 injects hidden developer messages into a run, such as the rule-violation reminder after a tool call matches a built-in rule. The agent_end frame then failed schema validation and was dropped, so the turn stayed running until it was cancelled. Closes #6104 * fix(omp): parse every OMP message role so agent_end is never dropped OMP's RPC AgentMessage also defines pythonExecution, hookMessage, branchSummary, compactionSummary and fileMention. A prompt that @-mentions a file puts a fileMention message in agent_end, the frame failed validation and was dropped, and the turn stayed running. --------- Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
@@ -227,6 +227,95 @@ describe("OMP CLI runtime", () => {
|
||||
expect(eventTypes).toEqual(["notice"]);
|
||||
});
|
||||
|
||||
test("emits agent_end for a run that contains an OMP developer message", async () => {
|
||||
const child = createOmpChild();
|
||||
const session = await createRuntime(child).startSession({ cwd: "/workspace/project" });
|
||||
const eventTypes: string[] = [];
|
||||
session.onEvent((event) => eventTypes.push(event.type));
|
||||
|
||||
// OMP 18.5 injects a developer reminder mid-run when a tool call matches a rule.
|
||||
const reminder = {
|
||||
role: "developer",
|
||||
content: [
|
||||
{ type: "text", text: '<system-reminder reason="rule_violation">…</system-reminder>' },
|
||||
],
|
||||
attribution: "agent",
|
||||
timestamp: 2,
|
||||
};
|
||||
child.stdout.write(`${JSON.stringify({ type: "message_end", message: reminder })}\n`);
|
||||
child.stdout.write(
|
||||
`${JSON.stringify({
|
||||
type: "agent_end",
|
||||
messages: [
|
||||
{ role: "user", content: [{ type: "text", text: "Write tiny.ts" }], timestamp: 1 },
|
||||
reminder,
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "Done" }],
|
||||
stopReason: "stop",
|
||||
timestamp: 3,
|
||||
},
|
||||
],
|
||||
})}\n`,
|
||||
);
|
||||
|
||||
expect(eventTypes).toEqual(["message_end", "agent_end"]);
|
||||
});
|
||||
|
||||
test("emits agent_end for a run that contains every OMP message role Paseo does not render", async () => {
|
||||
const child = createOmpChild();
|
||||
const session = await createRuntime(child).startSession({ cwd: "/workspace/project" });
|
||||
const eventTypes: string[] = [];
|
||||
session.onEvent((event) => eventTypes.push(event.type));
|
||||
|
||||
// A prompt that @-mentions a file makes OMP add a fileMention message to the run.
|
||||
const fileMention = {
|
||||
role: "fileMention",
|
||||
files: [{ path: "notes.md", content: "# Notes\n", lineCount: 1 }],
|
||||
timestamp: 2,
|
||||
};
|
||||
child.stdout.write(`${JSON.stringify({ type: "message_end", message: fileMention })}\n`);
|
||||
child.stdout.write(
|
||||
`${JSON.stringify({
|
||||
type: "agent_end",
|
||||
messages: [
|
||||
{ role: "user", content: [{ type: "text", text: "Read @notes.md" }], timestamp: 1 },
|
||||
fileMention,
|
||||
{
|
||||
role: "pythonExecution",
|
||||
code: "print(1)",
|
||||
output: "1\n",
|
||||
cancelled: false,
|
||||
truncated: false,
|
||||
timestamp: 3,
|
||||
},
|
||||
{
|
||||
role: "hookMessage",
|
||||
customType: "hook",
|
||||
content: "hook output",
|
||||
display: false,
|
||||
timestamp: 4,
|
||||
},
|
||||
{ role: "branchSummary", summary: "Earlier branch", fromId: "entry-1", timestamp: 5 },
|
||||
{
|
||||
role: "compactionSummary",
|
||||
summary: "Earlier turns",
|
||||
tokensBefore: 1000,
|
||||
timestamp: 6,
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "Done" }],
|
||||
stopReason: "stop",
|
||||
timestamp: 7,
|
||||
},
|
||||
],
|
||||
})}\n`,
|
||||
);
|
||||
|
||||
expect(eventTypes).toEqual(["message_end", "agent_end"]);
|
||||
});
|
||||
|
||||
test("lists commands through get_available_commands", async () => {
|
||||
const child = createOmpChild();
|
||||
const commandTypes: string[] = [];
|
||||
|
||||
@@ -41,6 +41,13 @@ const OmpUserMessageSchema = z
|
||||
content: z.union([z.string(), z.array(z.union([OmpTextContentSchema, OmpImageContentSchema]))]),
|
||||
})
|
||||
.passthrough();
|
||||
// OMP injects hidden developer messages, such as rule-violation reminders, into a run.
|
||||
const OmpDeveloperMessageSchema = z
|
||||
.object({
|
||||
role: z.literal("developer"),
|
||||
content: z.union([z.string(), z.array(z.union([OmpTextContentSchema, OmpImageContentSchema]))]),
|
||||
})
|
||||
.passthrough();
|
||||
const OmpCustomMessageSchema = z
|
||||
.object({
|
||||
role: z.literal("custom"),
|
||||
@@ -79,13 +86,30 @@ const OmpBashExecutionMessageSchema = z
|
||||
timestamp: z.number(),
|
||||
})
|
||||
.passthrough();
|
||||
// Roles Paseo does not render. They are listed so that a frame carrying one, such as
|
||||
// agent_end, still parses; every role in OMP's RPC AgentMessage must appear in the union.
|
||||
const OmpPythonExecutionMessageSchema = z
|
||||
.object({ role: z.literal("pythonExecution") })
|
||||
.passthrough();
|
||||
const OmpHookMessageSchema = z.object({ role: z.literal("hookMessage") }).passthrough();
|
||||
const OmpBranchSummaryMessageSchema = z.object({ role: z.literal("branchSummary") }).passthrough();
|
||||
const OmpCompactionSummaryMessageSchema = z
|
||||
.object({ role: z.literal("compactionSummary") })
|
||||
.passthrough();
|
||||
const OmpFileMentionMessageSchema = z.object({ role: z.literal("fileMention") }).passthrough();
|
||||
|
||||
export const OmpAgentMessageSchema = z.discriminatedUnion("role", [
|
||||
OmpUserMessageSchema,
|
||||
OmpDeveloperMessageSchema,
|
||||
OmpCustomMessageSchema,
|
||||
OmpAssistantMessageSchema,
|
||||
OmpToolResultMessageSchema,
|
||||
OmpBashExecutionMessageSchema,
|
||||
OmpPythonExecutionMessageSchema,
|
||||
OmpHookMessageSchema,
|
||||
OmpBranchSummaryMessageSchema,
|
||||
OmpCompactionSummaryMessageSchema,
|
||||
OmpFileMentionMessageSchema,
|
||||
]);
|
||||
|
||||
export const OmpModelThinkingSchema = z
|
||||
|
||||
Reference in New Issue
Block a user