// Pure helper for shaping the Claude message list, split out of runtime.mjs so // it's exercisable in Node tests (test/context.test.mjs) without the SDK/ws deps. // trimContext drops assistant turns from BOTH ends of the message list. The API // requires the first turn to be `user`; and with extended thinking on (the // runtime always sends `thinking:{adaptive}`) the conversation also can't END on // an assistant turn — there's no prefill under thinking, so a trailing assistant // turn is a hard "assistant message prefill" 400. A trailing assistant turn shows // up whenever the newest message we can see is our own reply (e.g. a redelivery // race where a message got processed after we'd already answered); trimming it // lets the request through instead of erroring. Returns a new array. export function trimContext(messages) { const out = [...(messages || [])]; while (out.length && out[0].role === "assistant") out.shift(); while (out.length && out[out.length - 1].role === "assistant") out.pop(); return out; } // scrubSelfNotes removes the "[note to self: …]" history annotation from a // reply about to POST. The note is appended to past assistant turns (see // buildContext in runtime.mjs) so the model knows which of its old replies // carried buttons — but the model reads those turns as its own words and, // despite the system-prompt rule, eventually imitates the pattern into a live // reply. The note is for the model, never for members. export function scrubSelfNotes(text) { return (text || "").replace(/\s*\[note to self:[^\]]*\]/g, "").trim(); }