mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
fix(omp): honor hidden custom messages (#2280)
This commit is contained in:
@@ -260,6 +260,23 @@ describe("OMP agent client and session", () => {
|
||||
expect(omp.completedTurnCount()).toBe(1);
|
||||
});
|
||||
|
||||
test("omits live custom messages when display is false", async () => {
|
||||
const omp = new OmpHarness();
|
||||
await omp.start();
|
||||
|
||||
await expect(
|
||||
omp.runPromptAfterExtensionNotice("hello OMP", "model turn completed", false),
|
||||
).resolves.toMatchObject({ finalText: expect.stringContaining("model turn completed") });
|
||||
expect(omp.timeline()).toEqual([
|
||||
{ type: "user_message", text: "hello OMP", messageId: "user-1" },
|
||||
{
|
||||
type: "assistant_message",
|
||||
text: "model turn completed",
|
||||
messageId: "omp-assistant-1",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("does not complete a queued model turn from OMP's local-only hint", async () => {
|
||||
const omp = new OmpHarness();
|
||||
await omp.start();
|
||||
|
||||
@@ -66,6 +66,7 @@ import {
|
||||
} from "./provider-config.js";
|
||||
export { formatOmpVersionSupport, resolveOmpDiagnosticPaths } from "./provider-config.js";
|
||||
import { OmpSubagentCardTracker, type OmpSubagentCardScheduler } from "./subagent-card-tracker.js";
|
||||
import { shouldDisplayOmpCustomMessage } from "./custom-message.js";
|
||||
import { getUserMessageText } from "./message-history.js";
|
||||
import { materializeProviderImage } from "../provider-image-output.js";
|
||||
import { OmpCliRuntime } from "./cli-runtime.js";
|
||||
@@ -2064,15 +2065,17 @@ export class OmpAgentSession implements AgentSession {
|
||||
return;
|
||||
}
|
||||
if (event.message.role === "custom") {
|
||||
const text = getUserMessageText(event.message.content);
|
||||
if (text) {
|
||||
const advisorItem = mapOmpAdvisorMessageToToolCall(event.message, text);
|
||||
this.emit({
|
||||
type: "timeline",
|
||||
provider: this.provider,
|
||||
turnId,
|
||||
item: advisorItem ?? { type: "assistant_message", text },
|
||||
});
|
||||
if (shouldDisplayOmpCustomMessage(event.message)) {
|
||||
const text = getUserMessageText(event.message.content);
|
||||
if (text) {
|
||||
const advisorItem = mapOmpAdvisorMessageToToolCall(event.message, text);
|
||||
this.emit({
|
||||
type: "timeline",
|
||||
provider: this.provider,
|
||||
turnId,
|
||||
item: advisorItem ?? { type: "assistant_message", text },
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!this.activeTurnHasUserMessage) {
|
||||
this.completeTurn(turnId, []);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { OmpAgentMessage } from "./rpc-types.js";
|
||||
|
||||
type OmpCustomMessage = Extract<OmpAgentMessage, { role: "custom" }>;
|
||||
|
||||
export function shouldDisplayOmpCustomMessage(message: OmpCustomMessage): boolean {
|
||||
return Reflect.get(message, "display") !== false;
|
||||
}
|
||||
@@ -209,6 +209,54 @@ describe("OMP history mapper", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test("omits replayed custom messages only when display is false", async () => {
|
||||
await expect(
|
||||
collectHistory(
|
||||
[
|
||||
{ role: "user", content: "first prompt" },
|
||||
{ role: "custom", content: "hidden reminder", display: false },
|
||||
{ role: "custom", content: "visible explicit custom", display: true },
|
||||
{ role: "custom", content: "visible legacy custom" },
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "assistant reply" }],
|
||||
responseId: "assistant-history",
|
||||
},
|
||||
],
|
||||
[{ id: "entry-user-1", text: "first prompt" }],
|
||||
),
|
||||
).resolves.toEqual([
|
||||
{
|
||||
type: "timeline",
|
||||
provider: "omp",
|
||||
item: {
|
||||
type: "user_message",
|
||||
text: "first prompt",
|
||||
messageId: "entry-user-1",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "timeline",
|
||||
provider: "omp",
|
||||
item: { type: "assistant_message", text: "visible explicit custom" },
|
||||
},
|
||||
{
|
||||
type: "timeline",
|
||||
provider: "omp",
|
||||
item: { type: "assistant_message", text: "visible legacy custom" },
|
||||
},
|
||||
{
|
||||
type: "timeline",
|
||||
provider: "omp",
|
||||
item: {
|
||||
type: "assistant_message",
|
||||
text: "assistant reply",
|
||||
messageId: "assistant-history",
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("suppresses replayed raw todo tool calls through the OMP detail hook", async () => {
|
||||
await expect(
|
||||
collectHistory([
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { AgentStreamEvent, AgentTimelineItem, ToolCallDetail } from "../../agent-sdk-types.js";
|
||||
import type { OmpAgentMessage, OmpImageContent, OmpTextContent } from "./rpc-types.js";
|
||||
import { shouldDisplayOmpCustomMessage } from "./custom-message.js";
|
||||
import {
|
||||
extractTextFromToolResult,
|
||||
mapToolDetail,
|
||||
@@ -117,6 +118,9 @@ export class OmpHistoryMapper {
|
||||
private mapCustomMessage(
|
||||
message: Extract<OmpAgentMessage, { role: "custom" }>,
|
||||
): AgentStreamEvent[] {
|
||||
if (!shouldDisplayOmpCustomMessage(message)) {
|
||||
return [];
|
||||
}
|
||||
const text = getUserMessageText(message.content);
|
||||
const mappedEvent = text ? this.hooks.mapCustomMessage?.(message, text, this.provider) : null;
|
||||
if (mappedEvent) {
|
||||
|
||||
@@ -201,16 +201,25 @@ export class OmpHarness {
|
||||
return { completion };
|
||||
}
|
||||
|
||||
async runPromptAfterExtensionNotice(input: string, output: string): Promise<unknown> {
|
||||
async runPromptAfterExtensionNotice(
|
||||
input: string,
|
||||
output: string,
|
||||
display?: boolean,
|
||||
): Promise<unknown> {
|
||||
const session = this.requireSession();
|
||||
const promptStarted = this.omp.latestSession().nextPrompt();
|
||||
const run = session.run(input);
|
||||
await promptStarted;
|
||||
const runtime = this.omp.latestSession();
|
||||
const message = {
|
||||
role: "custom" as const,
|
||||
content: "extension inventory changed",
|
||||
...(display === undefined ? {} : { display }),
|
||||
};
|
||||
runtime.beginTurn();
|
||||
runtime.acceptPrompt(input, "user-1");
|
||||
runtime.acceptCustomMessage("extension inventory changed");
|
||||
runtime.finishTurn({ role: "custom", content: "extension inventory changed" });
|
||||
runtime.emit({ type: "message_end", message });
|
||||
runtime.finishTurn(message);
|
||||
runtime.beginTurn();
|
||||
runtime.streamAssistantText(output);
|
||||
runtime.finishTurn();
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
import { parseToolArgs, parseToolResult } from "./tool-call-detail.js";
|
||||
import { OmpAvailableCommandsUpdateEventSchema } from "./rpc-types.js";
|
||||
import { OmpAgentMessageSchema, OmpAvailableCommandsUpdateEventSchema } from "./rpc-types.js";
|
||||
import { mapOmpToolDetail } from "./tool-call-mapper.js";
|
||||
import { shouldDisplayOmpCustomMessage } from "./custom-message.js";
|
||||
|
||||
describe("OMP 17 RPC compatibility", () => {
|
||||
test("parses source-attributed command updates", () => {
|
||||
@@ -16,6 +17,20 @@ describe("OMP 17 RPC compatibility", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test("keeps non-false custom display metadata backward compatible", () => {
|
||||
const message = OmpAgentMessageSchema.parse({
|
||||
role: "custom",
|
||||
content: "visible custom message",
|
||||
display: null,
|
||||
});
|
||||
|
||||
expect(message).toMatchObject({ display: null });
|
||||
if (message.role !== "custom") {
|
||||
throw new Error("Expected a custom OMP message");
|
||||
}
|
||||
expect(shouldDisplayOmpCustomMessage(message)).toBe(true);
|
||||
});
|
||||
|
||||
test("maps subscribed custom tool events without assuming built-in names", () => {
|
||||
const event = {
|
||||
type: "tool_execution_start",
|
||||
|
||||
Reference in New Issue
Block a user