Q158: run history and run output come from the database, not the snapshot window

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-09 02:31:43 -04:00
1 parent a075f931d5
commit 9d166e3fc9
18 files changed
+222 -35

No files matched your search

@@ -48,6 +48,10 @@ export const organizationContracts = {
setTaskBlocked: z.strictObject({ orgId: id, taskId: id, reason: z.string().nullable() }) satisfies z.ZodType<C.SetTaskBlockedInput>,
taskDeleteImpact: z.strictObject({ orgId: id, taskId: id }),
cancelQueuedRun: z.strictObject({ orgId: id, runId: id }),
runHistory: z.union([
z.strictObject({ orgId: id, beforeRunId: id.nullable(), limit: z.number().int().min(1).max(500) }),
z.strictObject({ orgId: id, beforeRunId: id.nullable(), from: z.strictObject({ createdAt: z.string(), seq: z.number().int() }) }),
]),
updateTaskStatus: z.strictObject({ orgId: id, taskId: id, status: z.enum(TASK_STATUS) }) satisfies z.ZodType<C.UpdateTaskStatusInput>,
runTask: z.strictObject({ orgId: id, taskId: id }) satisfies z.ZodType<C.RunTaskInput>,
createGoal: z.strictObject({ orgId: id, title: z.string(), description: z.string(), ownerAgentId: nullableId }) satisfies z.ZodType<C.CreateGoalInput>,
+2
View File
@@ -38,6 +38,7 @@ import { normalizeExternalBrowserUrl } from "./external-url";
import { resumeGoal } from "../services/goal-review-service";
import { stopAllRuns, stopRun } from "../services/run-control";
import { cancelQueuedRun } from "../services/run-cancel";
import { runHistory } from "../services/run-history";
export interface IpcDeps {
connection: DatabaseConnection;
@@ -168,6 +169,7 @@ export function registerIpcHandlers(deps: IpcDeps): void {
handle("setTaskBlocked", (input) => mutate(() => organization.setTaskBlocked(input)));
handle("taskDeleteImpact", (input) => ok(organization.taskDeleteImpact(input.orgId, input.taskId)));
handle("cancelQueuedRun", (input) => mutate(() => cancelQueuedRun(deps.connection.db, input.orgId, input.runId)));
handle("runHistory", (input) => ok(runHistory(deps.connection.db, input.orgId, input.beforeRunId, "limit" in input ? { limit: input.limit } : { from: input.from })));
handle("deleteTask", (input) => mutate(() => organization.deleteTask(input)));
handle("updateTaskStatus", (input) => mutate(() => organization.updateTaskStatus(input.orgId, input.taskId, input.status)));
handle("runTask", async (input) => {
+2 -1
View File
@@ -40,7 +40,8 @@ export type RequestPurpose = "agent_turn" | "goal_audit" | "model_check" | "manu
export function requestsForRun(db: DatabaseSync, orgId: string, runId: string): RunRequestRecord[] {
return db.prepare(`
select id, purpose, model, state, error_message as errorMessage, input_tokens as inputTokens, output_tokens as outputTokens,
cost_cents as costCents, created_at as createdAt, completed_at as completedAt
cost_cents as costCents, created_at as createdAt, completed_at as completedAt,
input_char_count as inputCharCount, output_char_count as outputCharCount, response_text as responseText
from provider_requests where org_id = ? and task_run_id = ? order by rowid
`).all(orgId, runId) as unknown as RunRequestRecord[];
}
+35
View File
@@ -0,0 +1,35 @@
import type { DatabaseSync } from "node:sqlite";
import type { RunCursor, RunHistoryPage, TaskRunRecord } from "../../shared/domain";
import { camel } from "./snapshot-service";
/** Which older runs to return: the next page before the cursor, or every run from a position (which survives its run's deletion) up to it. */
export type RunHistoryScope = { limit: number } | { from: RunCursor };
/** Runs before a loaded one (or the newest when none is loaded), oldest first, in time then insertion order, and whether older ones exist. */
export function runHistory(db: DatabaseSync, orgId: string, beforeRunId: string | null, scope: RunHistoryScope): RunHistoryPage {
const conditions = ["org_id = ?"];
const params: Array<string | number> = [orgId];
let boundary: RunCursor | null = null;
if (beforeRunId !== null) {
const before = db.prepare("select created_at, rowid from task_runs where id = ? and org_id = ?").get(beforeRunId, orgId) as { created_at: string; rowid: number } | undefined;
if (!before) throw new Error("The oldest loaded run was not found.");
conditions.push("(created_at, rowid) < (?, ?)");
params.push(before.created_at, before.rowid);
boundary = { createdAt: before.created_at, seq: before.rowid };
}
if ("from" in scope) {
conditions.push("(created_at, rowid) >= (?, ?)");
params.push(scope.from.createdAt, scope.from.seq);
}
const limit = "limit" in scope ? scope.limit : -1;
const rows = db.prepare(`select *, rowid as seq from task_runs where ${conditions.join(" and ")} order by created_at desc, rowid desc limit ?`)
.all(...params, limit === -1 ? -1 : limit + 1) as Array<Record<string, unknown>>;
const page = (limit === -1 ? rows : rows.slice(0, limit)).reverse();
const first = page[0];
const oldest = first ? { createdAt: first.created_at as string, seq: first.seq as number } : null;
// A range is measured from its pinned position, which holds even when the runs at it were deleted.
const hasMore = "limit" in scope ? rows.length > limit : db.prepare(`
select 1 from task_runs where org_id = ? and (created_at, rowid) < (?, ?) limit 1
`).get(orgId, scope.from.createdAt, scope.from.seq) !== undefined;
return { runs: page.map(({ seq: _seq, ...row }) => camel(row) as unknown as TaskRunRecord), hasMore, oldest, boundary };
}
+1 -1
View File
@@ -95,7 +95,7 @@ export class SnapshotService {
workspaces,
goals: list(this.db, "goals", "created_at asc"),
projects: list(this.db, "projects", "created_at asc"),
taskRuns: recentList<AppSnapshot["taskRuns"][number]>(this.db, "task_runs", "created_at desc", 200).reverse(),
taskRuns: recentList<AppSnapshot["taskRuns"][number]>(this.db, "task_runs", "created_at desc, rowid desc", 200).reverse(),
approvals: list(this.db, "approvals", "created_at desc"),
chatSpaces: list(this.db, "chat_spaces", "created_at asc"),
chatMemberships: list(this.db, "chat_memberships", "space_id asc, principal_type asc, principal_id asc"),