Q143: earlier messages come from the database, page by page, back to the first one

Review limit (5) reached; the last finding (exhaustion latched) is fixed
by taking it from each latest hasMore.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-08 23:52:22 -04:00
1 parent b18eb9ad14
commit 075dea725a
12 files changed
+213 -15

No files matched your search

+4
View File
@@ -72,6 +72,10 @@ export const platformContracts = {
approvalDrifts: z.strictObject({ orgId: id }),
runRequests: z.strictObject({ orgId: id, runId: id }),
chatThreadHistory: z.strictObject({ orgId: id, threadId: id }),
chatSpaceHistory: z.union([
z.strictObject({ orgId: id, spaceId: id, beforeMessageId: id.nullable(), limit: z.number().int().min(1).max(500) }),
z.strictObject({ orgId: id, spaceId: id, beforeMessageId: id.nullable(), fromMessageId: id }),
]),
search: z.strictObject({
orgId: id, employeeId: id, query: z.string(), entityTypes: z.array(z.enum(SEARCH_ENTITY_TYPES)).optional(), limit: z.number().optional(),
}) satisfies z.ZodType<C.SearchInput>,
+2 -1
View File
@@ -28,7 +28,7 @@ import { SprintService } from "../services/sprint-service";
import { ComposioConnectionService } from "../services/composio/composio-connection-service";
import { ConnectorGrantsService } from "../services/composio/connector-grants";
import { approvalDrifts } from "../services/approval-drift";
import { threadHistory } from "../services/chat-history";
import { spaceHistory, threadHistory } from "../services/chat-history";
import { requestsForRun } from "../services/provider-request-store";
import { setGoalAuditor } from "../services/goal-audit-inputs";
import { IntegrationKeyStore } from "../services/integration-key-store";
@@ -415,6 +415,7 @@ export function registerIpcHandlers(deps: IpcDeps): void {
handle("grantConnector", (input) => mutate(() => new ConnectorGrantsService(deps.connection.db).grantFromBoard(input)));
handle("runRequests", (input) => ok(requestsForRun(deps.connection.db, input.orgId, input.runId)));
handle("chatThreadHistory", (input) => ok(threadHistory(deps.connection.db, input.orgId, input.threadId)));
handle("chatSpaceHistory", (input) => ok(spaceHistory(deps.connection.db, input.orgId, input.spaceId, input.beforeMessageId, "limit" in input ? { limit: input.limit } : { fromMessageId: input.fromMessageId })));
handle("approvalDrifts", (input) => {
try {
return ok(approvalDrifts(deps.connection.db, input.orgId));
+45 -1
View File
@@ -1,5 +1,5 @@
import type { DatabaseSync } from "node:sqlite";
import type { ThreadHistory } from "../../shared/chat-domain";
import type { SpaceHistoryPage, ThreadHistory } from "../../shared/chat-domain";
import { camel } from "./snapshot-service";
/** Every message of one thread with its attachments, mentions and commands, however far back it started. */
@@ -14,3 +14,47 @@ export function threadHistory(db: DatabaseSync, orgId: string, threadId: string)
slashInvocations: rows("select s.* from chat_slash_invocations s join chat_messages m on m.id = s.message_id where m.thread_id = ?"),
};
}
type Row = Record<string, unknown>;
/** Which older messages to return: the next page before the cursor, or every message from a loaded one up to the cursor. */
export type HistoryScope = { limit: number } | { fromMessageId: string };
/** A conversation's messages before a loaded one (or its newest when none is loaded), in time then insertion order, with what they carry. */
export function spaceHistory(db: DatabaseSync, orgId: string, spaceId: string, beforeMessageId: string | null, scope: HistoryScope): SpaceHistoryPage {
if (!db.prepare("select id from chat_spaces where id = ? and org_id = ?").get(spaceId, orgId)) throw new Error("Chat space was not found.");
const position = (messageId: string, what: string) => {
const row = db.prepare("select created_at, rowid from chat_messages where id = ? and space_id = ? and org_id = ?")
.get(messageId, spaceId, orgId) as { created_at: string; rowid: number } | undefined;
if (!row) throw new Error(`The ${what} is not in this conversation.`);
return [row.created_at, row.rowid] as const;
};
const conditions = ["space_id = ?"];
const params: Array<string | number> = [spaceId];
if (beforeMessageId !== null) {
conditions.push("(created_at, rowid) < (?, ?)");
params.push(...position(beforeMessageId, "earliest loaded message"));
}
if ("fromMessageId" in scope) {
conditions.push("(created_at, rowid) >= (?, ?)");
params.push(...position(scope.fromMessageId, "oldest loaded message"));
}
const limit = "limit" in scope ? scope.limit : -1;
const rows = db.prepare(`select *, rowid as seq from chat_messages where ${conditions.join(" and ")} order by created_at desc, rowid desc limit ?`)
.all(...params, limit === -1 ? -1 : limit + 1) as Row[];
const page = (limit === -1 ? rows : rows.slice(0, limit)).reverse();
const oldest = page[0];
const hasMore = limit !== -1 ? rows.length > limit : oldest !== undefined && db.prepare(`
select 1 from chat_messages where space_id = ? and (created_at, rowid) < (?, ?) limit 1
`).get(spaceId, oldest.created_at as string, oldest.seq as number) !== undefined;
const ids = JSON.stringify(page.map((row) => row.id));
const carried = <T>(sql: string) => (db.prepare(sql).all(ids) as Row[]).map((row) => camel(row) as T);
return {
messages: page.map(({ seq: _seq, ...row }) => camel(row) as unknown as SpaceHistoryPage["messages"][number]),
attachments: carried("select * from chat_attachments where message_id in (select value from json_each(?)) order by message_id"),
mentions: carried("select * from chat_mentions where message_id in (select value from json_each(?)) order by message_id, offset_start"),
slashInvocations: carried("select * from chat_slash_invocations where message_id in (select value from json_each(?)) order by message_id"),
threads: carried("select * from chat_threads where id in (select thread_id from chat_messages where id in (select value from json_each(?)))"),
hasMore,
};
}