Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 17 additions & 29 deletions apps/web/src/composerDraftStore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,10 @@ describe("composerDraftStore terminal contexts", () => {
expect(draft?.terminalContexts.map((context) => context.id)).toEqual(["ctx-2", "ctx-1"]);
});

it("omits terminal context text from persisted drafts", () => {
it("does not persist terminal context chips", () => {
useComposerDraftStore
.getState()
.setPrompt(threadRef, `${INLINE_TERMINAL_CONTEXT_PLACEHOLDER} keep this`);
useComposerDraftStore
.getState()
.addTerminalContext(threadRef, makeTerminalContext({ id: "ctx-persist" }));
Expand All @@ -481,27 +484,19 @@ describe("composerDraftStore terminal contexts", () => {
};
};
const persistedState = persistApi.getOptions().partialize(useComposerDraftStore.getState()) as {
draftsByThreadKey?: Record<string, { terminalContexts?: Array<Record<string, unknown>> }>;
draftsByThreadKey?: Record<
string,
{ prompt?: string; terminalContexts?: Array<Record<string, unknown>> }
>;
};
const persistedDraft =
persistedState.draftsByThreadKey?.[threadKeyFor(threadId, TEST_ENVIRONMENT_ID)];

expect(
persistedState.draftsByThreadKey?.[threadKeyFor(threadId, TEST_ENVIRONMENT_ID)]
?.terminalContexts?.[0],
"Expected terminal context metadata to be persisted.",
).toMatchObject({
id: "ctx-persist",
terminalId: "default",
terminalLabel: "Terminal 1",
lineStart: 4,
lineEnd: 5,
});
expect(
persistedState.draftsByThreadKey?.[threadKeyFor(threadId, TEST_ENVIRONMENT_ID)]
?.terminalContexts?.[0]?.text,
).toBeUndefined();
expect(persistedDraft?.terminalContexts).toBeUndefined();
expect(persistedDraft?.prompt).toBe(" keep this");
});

it("hydrates persisted terminal contexts without in-memory snapshot text", () => {
it("drops persisted terminal context chips on hydrate", () => {
const persistApi = useComposerDraftStore.persist as unknown as {
getOptions: () => {
merge: (
Expand All @@ -514,7 +509,7 @@ describe("composerDraftStore terminal contexts", () => {
{
draftsByThreadId: {
[threadId]: {
prompt: INLINE_TERMINAL_CONTEXT_PLACEHOLDER,
prompt: `${INLINE_TERMINAL_CONTEXT_PLACEHOLDER} keep this`,
attachments: [],
terminalContexts: [
{
Expand All @@ -535,16 +530,9 @@ describe("composerDraftStore terminal contexts", () => {
useComposerDraftStore.getInitialState(),
);

expect(mergedState.draftsByThreadKey[threadKeyFor(threadId)]?.terminalContexts).toMatchObject([
{
id: "ctx-rehydrated",
terminalId: "default",
terminalLabel: "Terminal 1",
lineStart: 4,
lineEnd: 5,
text: "",
},
]);
const hydrated = mergedState.draftsByThreadKey[threadKeyFor(threadId)];
expect(hydrated?.terminalContexts).toEqual([]);
expect(hydrated?.prompt).toBe(" keep this");
});

it("sanitizes malformed persisted drafts during merge", () => {
Expand Down
87 changes: 7 additions & 80 deletions apps/web/src/composerDraftStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1177,51 +1177,6 @@ function normalizePersistedElementContextDraft(
};
}

function normalizePersistedTerminalContextDraft(
value: unknown,
): PersistedTerminalContextDraft | null {
if (!value || typeof value !== "object") {
return null;
}
const candidate = value as Record<string, unknown>;
const id = candidate.id;
const threadId = candidate.threadId;
const createdAt = candidate.createdAt;
const lineStart = candidate.lineStart;
const lineEnd = candidate.lineEnd;
if (
typeof id !== "string" ||
id.length === 0 ||
typeof threadId !== "string" ||
threadId.length === 0 ||
typeof createdAt !== "string" ||
createdAt.length === 0 ||
typeof lineStart !== "number" ||
!Number.isFinite(lineStart) ||
typeof lineEnd !== "number" ||
!Number.isFinite(lineEnd)
) {
return null;
}
const terminalId = typeof candidate.terminalId === "string" ? candidate.terminalId.trim() : "";
const terminalLabel =
typeof candidate.terminalLabel === "string" ? candidate.terminalLabel.trim() : "";
if (terminalId.length === 0 || terminalLabel.length === 0) {
return null;
}
const normalizedLineStart = Math.max(1, Math.floor(lineStart));
const normalizedLineEnd = Math.max(normalizedLineStart, Math.floor(lineEnd));
return {
id,
threadId: threadId as ThreadId,
createdAt,
terminalId,
terminalLabel,
lineStart: normalizedLineStart,
lineEnd: normalizedLineEnd,
};
}

function normalizeDraftThreadEnvMode(
value: unknown,
fallbackWorktreePath: string | null,
Expand Down Expand Up @@ -1694,12 +1649,6 @@ function normalizePersistedDraftsByThreadId(
return normalized ? [normalized] : [];
})
: [];
const terminalContexts = Array.isArray(draftCandidate.terminalContexts)
? draftCandidate.terminalContexts.flatMap((entry) => {
const normalized = normalizePersistedTerminalContextDraft(entry);
return normalized ? [normalized] : [];
})
: [];
const elementContexts = Array.isArray(draftCandidate.elementContexts)
? draftCandidate.elementContexts.flatMap((entry) => {
const normalized = normalizePersistedElementContextDraft(entry);
Expand All @@ -1716,10 +1665,7 @@ function normalizePersistedDraftsByThreadId(
draftCandidate.interactionMode === "plan" || draftCandidate.interactionMode === "default"
? draftCandidate.interactionMode
: null;
const prompt = ensureInlineTerminalContextPlaceholders(
promptCandidate,
terminalContexts.length,
);
const prompt = stripInlineTerminalContextPlaceholders(promptCandidate);
// If the draft already has the v3 shape, use it directly
const legacyDraftCandidate = draftValue as LegacyPersistedComposerThreadDraftState;
let modelSelectionByProvider: Partial<Record<ProviderInstanceId, ModelSelection>> = {};
Expand Down Expand Up @@ -1769,9 +1715,8 @@ function normalizePersistedDraftsByThreadId(
const hasModelData =
Object.keys(modelSelectionByProvider).length > 0 || activeProvider !== null;
if (
promptCandidate.length === 0 &&
prompt.length === 0 &&
attachments.length === 0 &&
terminalContexts.length === 0 &&
elementContexts.length === 0 &&
reviewComments.length === 0 &&
!hasModelData &&
Expand All @@ -1795,7 +1740,6 @@ function normalizePersistedDraftsByThreadId(
nextDraftsByThreadKey[normalizedThreadKey] = {
prompt,
attachments,
...(terminalContexts.length > 0 ? { terminalContexts } : {}),
...(elementContexts.length > 0 ? { elementContexts } : {}),
...(reviewComments.length > 0 ? { reviewComments } : {}),
...(hasModelData
Expand Down Expand Up @@ -1899,10 +1843,10 @@ function partializeComposerDraftStoreState(
}
const hasModelData =
Object.keys(draft.modelSelectionByProvider).length > 0 || draft.activeProvider !== null;
const persistablePrompt = stripInlineTerminalContextPlaceholders(draft.prompt);
if (
draft.prompt.length === 0 &&
persistablePrompt.length === 0 &&
draft.persistedAttachments.length === 0 &&
draft.terminalContexts.length === 0 &&
draft.elementContexts.length === 0 &&
draft.previewAnnotations.length === 0 &&
draft.reviewComments.length === 0 &&
Expand All @@ -1913,21 +1857,8 @@ function partializeComposerDraftStoreState(
continue;
}
const persistedDraft: DeepMutable<PersistedComposerThreadDraftState> = {
prompt: draft.prompt,
prompt: persistablePrompt,
attachments: draft.persistedAttachments,
...(draft.terminalContexts.length > 0
? {
terminalContexts: draft.terminalContexts.map((context) => ({
id: context.id,
threadId: context.threadId,
createdAt: context.createdAt,
terminalId: context.terminalId,
terminalLabel: context.terminalLabel,
lineStart: context.lineStart,
lineEnd: context.lineEnd,
})),
}
: {}),
...(draft.elementContexts.length > 0
? {
elementContexts: draft.elementContexts.map((context) => ({
Expand Down Expand Up @@ -2193,15 +2124,11 @@ function toHydratedThreadDraft(
const activeProvider = normalizeProviderInstanceId(persistedDraft.activeProvider) ?? null;

return {
prompt: persistedDraft.prompt,
prompt: stripInlineTerminalContextPlaceholders(persistedDraft.prompt),
images: hydrateImagesFromPersisted(persistedDraft.attachments),
nonPersistedImageIds: [],
persistedAttachments: [...persistedDraft.attachments],
terminalContexts:
persistedDraft.terminalContexts?.map((context) => ({
...context,
text: "",
})) ?? [],
terminalContexts: [],
elementContexts:
persistedDraft.elementContexts?.map((context) => ({
...context,
Expand Down
Loading