fix: sanitize user-facing errors and strip final tags
Co-authored-by: Drake Thomsen <drake.thomsen@example.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import { runEmbeddedPiAgent } from "../../agents/pi-embedded.js";
|
||||
import {
|
||||
isCompactionFailureError,
|
||||
isContextOverflowError,
|
||||
sanitizeUserFacingText,
|
||||
} from "../../agents/pi-embedded-helpers.js";
|
||||
import {
|
||||
resolveAgentIdFromSessionKey,
|
||||
@@ -106,7 +107,10 @@ export async function runAgentTurnWithFallback(params: {
|
||||
if (isSilentReplyText(text, SILENT_REPLY_TOKEN)) {
|
||||
return { skip: true };
|
||||
}
|
||||
return { text, skip: false };
|
||||
if (!text) return { skip: true };
|
||||
const sanitized = sanitizeUserFacingText(text);
|
||||
if (!sanitized.trim()) return { skip: true };
|
||||
return { text: sanitized, skip: false };
|
||||
};
|
||||
const handlePartialForTyping = async (payload: ReplyPayload): Promise<string | undefined> => {
|
||||
const { text, skip } = normalizeStreamingText(payload);
|
||||
@@ -366,6 +370,7 @@ export async function runAgentTurnWithFallback(params: {
|
||||
/context.*overflow|too large|context window/i.test(message);
|
||||
const isCompactionFailure = isCompactionFailureError(message);
|
||||
const isSessionCorruption = /function call turn comes immediately after/i.test(message);
|
||||
const isRoleOrderingError = /incorrect role information|roles must alternate/i.test(message);
|
||||
|
||||
if (
|
||||
isCompactionFailure &&
|
||||
@@ -427,7 +432,9 @@ export async function runAgentTurnWithFallback(params: {
|
||||
payload: {
|
||||
text: isContextOverflow
|
||||
? "⚠️ Context overflow — prompt too large for this model. Try a shorter message or a larger-context model."
|
||||
: `⚠️ Agent failed before reply: ${message}. Check gateway logs for details.`,
|
||||
: isRoleOrderingError
|
||||
? "⚠️ Message ordering conflict - please try again. If this persists, use /new to start a fresh session."
|
||||
: `⚠️ Agent failed before reply: ${message}. Check gateway logs for details.`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -213,4 +213,31 @@ describe("runReplyAgent typing (heartbeat)", () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
it("returns friendly message for role ordering errors thrown as exceptions", async () => {
|
||||
runEmbeddedPiAgentMock.mockImplementationOnce(async () => {
|
||||
throw new Error("400 Incorrect role information");
|
||||
});
|
||||
|
||||
const { run } = createMinimalRun({});
|
||||
const res = await run();
|
||||
|
||||
expect(res).toMatchObject({
|
||||
text: expect.stringContaining("Message ordering conflict"),
|
||||
});
|
||||
expect(res).toMatchObject({
|
||||
text: expect.not.stringContaining("400"),
|
||||
});
|
||||
});
|
||||
it("returns friendly message for 'roles must alternate' errors thrown as exceptions", async () => {
|
||||
runEmbeddedPiAgentMock.mockImplementationOnce(async () => {
|
||||
throw new Error('messages: roles must alternate between "user" and "assistant"');
|
||||
});
|
||||
|
||||
const { run } = createMinimalRun({});
|
||||
const res = await run();
|
||||
|
||||
expect(res).toMatchObject({
|
||||
text: expect.stringContaining("Message ordering conflict"),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { stripHeartbeatToken } from "../heartbeat.js";
|
||||
import { HEARTBEAT_TOKEN, isSilentReplyText, SILENT_REPLY_TOKEN } from "../tokens.js";
|
||||
import type { ReplyPayload } from "../types.js";
|
||||
import { sanitizeUserFacingText } from "../../agents/pi-embedded-helpers.js";
|
||||
import {
|
||||
resolveResponsePrefixTemplate,
|
||||
type ResponsePrefixContext,
|
||||
@@ -42,6 +43,11 @@ export function normalizeReplyPayload(
|
||||
text = stripped.text;
|
||||
}
|
||||
|
||||
if (text) {
|
||||
text = sanitizeUserFacingText(text);
|
||||
}
|
||||
if (!text?.trim() && !hasMedia) return null;
|
||||
|
||||
// Resolve template variables in responsePrefix if context is provided
|
||||
const effectivePrefix = opts.responsePrefixContext
|
||||
? resolveResponsePrefixTemplate(opts.responsePrefix, opts.responsePrefixContext)
|
||||
|
||||
Reference in New Issue
Block a user