make single owner of prompt copy/equality

This commit is contained in:
Simon Klee
2026-04-18 18:35:10 +02:00
parent b20118fdbd
commit af668b33d4
3 changed files with 29 additions and 23 deletions

View File

@@ -46,14 +46,14 @@ export type PromptMove = {
apply: boolean
}
function copy(prompt: RunPrompt): RunPrompt {
export function promptCopy(prompt: RunPrompt): RunPrompt {
return {
text: prompt.text,
parts: structuredClone(prompt.parts),
}
}
function same(a: RunPrompt, b: RunPrompt): boolean {
export function promptSame(a: RunPrompt, b: RunPrompt): boolean {
return a.text === b.text && JSON.stringify(a.parts) === JSON.stringify(b.parts)
}
@@ -171,10 +171,10 @@ export function promptCycle(
}
export function createPromptHistory(items?: RunPrompt[]): PromptHistoryState {
const list = (items ?? []).filter((item) => item.text.trim().length > 0).map(copy)
const list = (items ?? []).filter((item) => item.text.trim().length > 0).map(promptCopy)
const next: RunPrompt[] = []
for (const item of list) {
if (next.length > 0 && same(next[next.length - 1], item)) {
if (next.length > 0 && promptSame(next[next.length - 1], item)) {
continue
}
@@ -193,8 +193,8 @@ export function pushPromptHistory(state: PromptHistoryState, prompt: RunPrompt):
return state
}
const next = copy(prompt)
if (state.items[state.items.length - 1] && same(state.items[state.items.length - 1], next)) {
const next = promptCopy(prompt)
if (state.items[state.items.length - 1] && promptSame(state.items[state.items.length - 1], next)) {
return {
...state,
index: null,

View File

@@ -5,6 +5,7 @@
// the current model so the footer can pre-select it.
import path from "path"
import { fileURLToPath } from "url"
import { promptCopy, promptSame } from "./prompt.shared"
import type { RunInput, RunPrompt } from "./types"
const LIMIT = 200
@@ -23,17 +24,6 @@ export type RunSession = {
turns: Turn[]
}
function copy(prompt: RunPrompt): RunPrompt {
return {
text: prompt.text,
parts: structuredClone(prompt.parts),
}
}
function same(a: RunPrompt, b: RunPrompt): boolean {
return a.text === b.text && JSON.stringify(a.parts) === JSON.stringify(b.parts)
}
function fileName(url: string, filename?: string) {
if (filename) {
return filename
@@ -175,11 +165,11 @@ export function sessionHistory(session: RunSession, limit = LIMIT): RunPrompt[]
continue
}
if (out[out.length - 1] && same(out[out.length - 1], turn.prompt)) {
if (out[out.length - 1] && promptSame(out[out.length - 1], turn.prompt)) {
continue
}
out.push(copy(turn.prompt))
out.push(promptCopy(turn.prompt))
}
return out.slice(-limit)

View File

@@ -133,18 +133,34 @@ describe("run session shared", () => {
})
})
test("dedupes consecutive history entries and drops blank prompts", () => {
test("dedupes consecutive history entries, drops blanks, and copies prompt parts", () => {
const parts = [
{
type: "agent" as const,
name: "scan",
source: {
start: 0,
end: 5,
value: "@scan",
},
},
]
const session: RunSession = {
first: false,
turns: [
{ prompt: { text: "one", parts: [] }, provider: "openai", model: "gpt-5", variant: "high" },
{ prompt: { text: "one", parts: [] }, provider: "openai", model: "gpt-5", variant: "high" },
{ prompt: { text: "one", parts }, provider: "openai", model: "gpt-5", variant: "high" },
{ prompt: { text: "one", parts: structuredClone(parts) }, provider: "openai", model: "gpt-5", variant: "high" },
{ prompt: { text: " ", parts: [] }, provider: "openai", model: "gpt-5", variant: "high" },
{ prompt: { text: "two", parts: [] }, provider: "openai", model: "gpt-5", variant: undefined },
],
}
expect(sessionHistory(session).map((item) => item.text)).toEqual(["one", "two"])
const out = sessionHistory(session)
expect(out.map((item) => item.text)).toEqual(["one", "two"])
expect(out[0]?.parts).toEqual(parts)
expect(out[0]?.parts).not.toBe(parts)
expect(out[0]?.parts[0]).not.toBe(parts[0])
})
test("returns the latest matching variant for the active model", () => {