fix: hide injected timestamp prefixes in chat ui

This commit is contained in:
Tyler Yust 2026-03-12 01:10:50 -07:00
parent 5343de3bf6
commit c0e5e8db22
2 changed files with 38 additions and 2 deletions

View File

@ -120,6 +120,32 @@ Hello from user`;
});
});
describe("timestamp prefix stripping", () => {
it("strips a leading injected timestamp prefix", () => {
expect(stripInboundMetadata("[Wed 2026-03-11 23:51 PDT] hello")).toBe("hello");
});
it("strips timestamp prefix with UTC timezone", () => {
expect(stripInboundMetadata("[Thu 2026-03-12 07:00 UTC] what time is it?")).toBe(
"what time is it?",
);
});
it("leaves non timestamp brackets alone", () => {
expect(stripInboundMetadata("[some note] hello")).toBe("[some note] hello");
});
it("strips timestamp prefix and inbound metadata blocks together", () => {
const input = `[Wed 2026-03-11 23:51 PDT] Conversation info (untrusted metadata):
\`\`\`json
{"message_id":"msg-1","sender":"+1555"}
\`\`\`
Hello`;
expect(stripInboundMetadata(input)).toBe("Hello");
});
});
describe("extractInboundSenderLabel", () => {
it("returns the sender label block when present", () => {
const input = `${CONV_BLOCK}\n\n${SENDER_BLOCK}\n\nHello from user`;

View File

@ -7,8 +7,13 @@
* etc.) directly to the stored user message content so the LLM can access
* them. These blocks are AI-facing only and must never surface in user-visible
* chat history.
*
* Also strips the timestamp prefix injected by `injectTimestamp` so UI surfaces
* do not show AI-facing envelope metadata as user text.
*/
const LEADING_TIMESTAMP_PREFIX_RE = /^\[[A-Za-z]{3} \d{4}-\d{2}-\d{2} \d{2}:\d{2}[^\]]*\] */;
/**
* Sentinel strings that identify the start of an injected metadata block.
* Must stay in sync with `buildInboundUserContextPrefix` in `inbound-meta.ts`.
@ -121,11 +126,16 @@ function stripTrailingUntrustedContextSuffix(lines: string[]): string[] {
* (fast path zero allocation).
*/
export function stripInboundMetadata(text: string): string {
if (!text || !SENTINEL_FAST_RE.test(text)) {
if (!text) {
return text;
}
const lines = text.split("\n");
const withoutTimestamp = text.replace(LEADING_TIMESTAMP_PREFIX_RE, "");
if (!SENTINEL_FAST_RE.test(withoutTimestamp)) {
return withoutTimestamp;
}
const lines = withoutTimestamp.split("\n");
const result: string[] = [];
let inMetaBlock = false;
let inFencedJson = false;