fix(consult): drop traces, budget checkpoints, silent summary inherit
BUG-729: dropped history rounds leave an omission marker in the model-visible summary slot. BUG-730: checkpoint threshold is 0.4 of historyBudgetChars (128k still 16,000). BUG-731: session_full new chat copies owned context_summary on the server; clients send only continued_from_session_id.
This commit is contained in:
@@ -1,7 +1,14 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import test from "node:test";
|
||||
import { chatSessionCreateSchema, chatSessionMetadataPatchSchema, chatSessionWriteSchema, writeChatSession, type ChatSessionWrite } from "../src/lib/chat-session-write-contract.ts";
|
||||
import {
|
||||
chatSessionCreateInsertRow,
|
||||
chatSessionCreateSchema,
|
||||
chatSessionMetadataPatchSchema,
|
||||
chatSessionWriteSchema,
|
||||
writeChatSession,
|
||||
type ChatSessionWrite,
|
||||
} from "../src/lib/chat-session-write-contract.ts";
|
||||
import { metadataUpdateValues } from "../src/lib/session-metadata-update.ts";
|
||||
import { homeSurface } from "./home-surface.ts";
|
||||
|
||||
@@ -41,6 +48,61 @@ test("create schema keeps the client-generated session id after transcript limit
|
||||
assert.equal(parsed.messages.length, 0);
|
||||
});
|
||||
|
||||
test("create schema accepts continued_from_session_id and rejects client-supplied context_summary", () => {
|
||||
const parsed = chatSessionCreateSchema.parse({
|
||||
id: sessionId,
|
||||
...createValues,
|
||||
continued_from_session_id: "22222222-2222-4222-8222-222222222222",
|
||||
});
|
||||
assert.equal(parsed.continued_from_session_id, "22222222-2222-4222-8222-222222222222");
|
||||
assert.equal("context_summary" in parsed, false);
|
||||
assert.equal("context_summary" in chatSessionCreateSchema.shape, false);
|
||||
|
||||
const withSummary = chatSessionCreateSchema.safeParse({
|
||||
id: sessionId,
|
||||
...createValues,
|
||||
context_summary: { version: 1, text: "injected" },
|
||||
});
|
||||
assert.equal(withSummary.success, false);
|
||||
|
||||
const badId = chatSessionCreateSchema.safeParse({
|
||||
id: sessionId,
|
||||
...createValues,
|
||||
continued_from_session_id: "not-a-uuid",
|
||||
});
|
||||
assert.equal(badId.success, false);
|
||||
});
|
||||
|
||||
test("create insert copies an owned summary and omits a missing one", () => {
|
||||
const source = {
|
||||
version: 1 as const,
|
||||
text: "已问过的问题\n事业时机",
|
||||
throughRequestId: "a1",
|
||||
throughMessageIndex: 3,
|
||||
messageCount: 4,
|
||||
updatedAt: "2026-09-15T00:00:00.000Z",
|
||||
};
|
||||
const copied = chatSessionCreateInsertRow({
|
||||
id: sessionId,
|
||||
userId: "owner",
|
||||
values: createValues,
|
||||
inheritedSummary: source,
|
||||
updatedAt: "2026-09-16T00:00:00.000Z",
|
||||
});
|
||||
assert.deepEqual(copied.context_summary, source);
|
||||
assert.equal("continued_from_session_id" in copied, false);
|
||||
|
||||
const skipped = chatSessionCreateInsertRow({
|
||||
id: sessionId,
|
||||
userId: "owner",
|
||||
values: createValues,
|
||||
inheritedSummary: null,
|
||||
updatedAt: "2026-09-16T00:00:00.000Z",
|
||||
});
|
||||
assert.equal("context_summary" in skipped, false);
|
||||
assert.equal(skipped.user_id, "owner");
|
||||
});
|
||||
|
||||
test("create schema rejects a non-empty transcript", () => {
|
||||
// Former value: create accepted up to CHAT_SESSION_MAX_MESSAGES and was a
|
||||
// history-import path. Create is now only an empty session.
|
||||
@@ -122,6 +184,19 @@ test("metadata patch accepts pin and archive fields without a transcript", () =>
|
||||
assert.deepEqual(chatSessionMetadataPatchSchema.parse({ archived_at: null }), { archived_at: null });
|
||||
});
|
||||
|
||||
test("create write may carry continued_from_session_id and never context_summary", async () => {
|
||||
const calls: Array<Record<string, unknown>> = [];
|
||||
await writeChatSession(sessionId, {
|
||||
...createValues,
|
||||
continued_from_session_id: "22222222-2222-4222-8222-222222222222",
|
||||
}, "create", async (_url, init) => {
|
||||
calls.push(JSON.parse(String(init?.body)));
|
||||
return Response.json({ ok: true }, { status: 201 });
|
||||
});
|
||||
assert.equal(calls[0]?.continued_from_session_id, "22222222-2222-4222-8222-222222222222");
|
||||
assert.equal("context_summary" in (calls[0] ?? {}), false);
|
||||
});
|
||||
|
||||
test("chat session writes use same-origin API instead of browser-to-Supabase requests", async () => {
|
||||
const calls: Array<{ url: string; init?: RequestInit }> = [];
|
||||
await writeChatSession(sessionId, metadataPatch, "update", async (url, init) => {
|
||||
@@ -205,8 +280,17 @@ test("session API owns create and update while answer UI keeps sync failures out
|
||||
assert.match(itemRoute, /readChatSessionJson/);
|
||||
assert.match(collectionRoute, /ChatSessionBodyTooLargeError/);
|
||||
assert.match(itemRoute, /ChatSessionBodyTooLargeError/);
|
||||
// Former value: `const { id, ...values } = parsed.data` trusted the client clock.
|
||||
assert.match(collectionRoute, /const \{ id, updated_at: _ignoredClientClock, \.\.\.values \} = parsed\.data/);
|
||||
// Former value: `const { id, updated_at: _ignoredClientClock, ...values } = parsed.data`
|
||||
// New value: also peel `continued_from_session_id` so it is never inserted as a column.
|
||||
assert.match(
|
||||
collectionRoute,
|
||||
/const \{ id, updated_at: _ignoredClientClock, continued_from_session_id: continuedFromSessionId, \.\.\.values \} = parsed\.data/,
|
||||
);
|
||||
assert.match(collectionRoute, /resolveInheritedContextSummary/);
|
||||
assert.match(collectionRoute, /chatSessionCreateInsertRow/);
|
||||
assert.match(collectionRoute, /\.eq\("user_id", user\.id\)/);
|
||||
assert.match(collectionRoute, /status: 201/);
|
||||
assert.doesNotMatch(collectionRoute, /context_summary: parsed/);
|
||||
assert.match(contract, /function limitTranscriptSize<Output extends \{ messages: Array<\{ text: string; thinkingText\?: string; thinkingSections\?: unknown \}> \}>/);
|
||||
assert.match(contract, /\): z\.ZodType<Output> \{/);
|
||||
assert.match(page, /chartSnapshotForSession/);
|
||||
|
||||
Reference in New Issue
Block a user