// Production CSS is part of the behavior under test because row height depends on it.import "../index.css"; import { EventId, ORCHESTRATION_WS_METHODS, EnvironmentId, type MessageId, type OrchestrationEvent, type OrchestrationReadModel, type ProjectId, type ServerConfig, type ServerLifecycleWelcomePayload, type ThreadId, type TurnId, WS_METHODS, OrchestrationSessionStatus, DEFAULT_SERVER_SETTINGS,} from "@t3tools/contracts";import { scopedProjectKey, scopedThreadKey, scopeProjectRef, scopeThreadRef,} from "@t3tools/client-runtime";import { RouterProvider, createMemoryHistory } from "@tanstack/react-router";import { HttpResponse, http, ws } from "msw";import { setupWorker } from "msw/browser";import { page } from "vitest/browser";import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";import { render } from "vitest-browser-react"; import { useComposerDraftStore, DraftId } from "../composerDraftStore";import { INLINE_TERMINAL_CONTEXT_PLACEHOLDER, type TerminalContextDraft, removeInlineTerminalContextPlaceholder,} from "../lib/terminalContext";import { isMacPlatform } from "../lib/utils";import { __resetLocalApiForTests } from "../localApi";import { AppAtomRegistryProvider } from "../rpc/atomRegistry";import { getServerConfig } from "../rpc/serverState";import { getRouter } from "../router";import { selectBootstrapCompleteForActiveEnvironment, useStore } from "../store";import { useTerminalStateStore } from "../terminalStateStore";import { useUiStateStore } from "../uiStateStore";import { createAuthenticatedSessionHandlers } from "../../test/authHttpHandlers";import { BrowserWsRpcHarness, type NormalizedWsRpcRequestBody } from "../../test/wsRpcHarness";import { estimateTimelineMessageHeight } from "./timelineHeight";import { DEFAULT_CLIENT_SETTINGS } from "@t3tools/contracts/settings"; vi.mock("../lib/gitStatusState", () => ({ useGitStatus: () => ({ data: null, error: null, cause: null, isPending: false }), useGitStatuses: () => new Map(), refreshGitStatus: () => Promise.resolve(null), resetGitStatusStateForTests: () => undefined,})); const THREAD_ID = "thread-browser-test" as ThreadId;const PROJECT_ID = "project-1" as ProjectId;const LOCAL_ENVIRONMENT_ID = EnvironmentId.makeUnsafe("environment-local");const THREAD_REF = scopeThreadRef(LOCAL_ENVIRONMENT_ID, THREAD_ID);const THREAD_KEY = scopedThreadKey(THREAD_REF);const UUID_ROUTE_RE = /^\/draft\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;const PROJECT_DRAFT_KEY = `${LOCAL_ENVIRONMENT_ID}:${PROJECT_ID}`;const PROJECT_KEY = scopedProjectKey(scopeProjectRef(LOCAL_ENVIRONMENT_ID, PROJECT_ID));const NOW_ISO = "2026-03-04T12:00:00.000Z";const BASE_TIME_MS = Date.parse(NOW_ISO);const ATTACHMENT_SVG = "<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'></svg>"; interface TestFixture { snapshot: OrchestrationReadModel; serverConfig: ServerConfig; welcome: ServerLifecycleWelcomePayload;} let fixture: TestFixture;const rpcHarness = new BrowserWsRpcHarness();const wsRequests = rpcHarness.requests;let customWsRpcResolver: ((body: NormalizedWsRpcRequestBody) => unknown | undefined) | null = null;const wsLink = ws.link(/ws(s)?:\/\/.*/); interface ViewportSpec { name: string; width: number; height: number; textTolerancePx: number; attachmentTolerancePx: number;} const DEFAULT_VIEWPORT: ViewportSpec = { name: "desktop", width: 960, height: 1_100, textTolerancePx: 44, attachmentTolerancePx: 56,};const WIDE_FOOTER_VIEWPORT: ViewportSpec = { name: "wide-footer", width: 1_400, height: 1_100, textTolerancePx: 44, attachmentTolerancePx: 56,};const COMPACT_FOOTER_VIEWPORT: ViewportSpec = { name: "compact-footer", width: 430, height: 932, textTolerancePx: 56, attachmentTolerancePx: 56,};const TEXT_VIEWPORT_MATRIX = [ DEFAULT_VIEWPORT, { name: "tablet", width: 720, height: 1_024, textTolerancePx: 44, attachmentTolerancePx: 56 }, { name: "mobile", width: 430, height: 932, textTolerancePx: 56, attachmentTolerancePx: 56 }, { name: "narrow", width: 320, height: 700, textTolerancePx: 84, attachmentTolerancePx: 56 },] as const satisfies readonly ViewportSpec[];const ATTACHMENT_VIEWPORT_MATRIX = [ { ...DEFAULT_VIEWPORT, attachmentTolerancePx: 120 }, { name: "mobile", width: 430, height: 932, textTolerancePx: 56, attachmentTolerancePx: 120 }, { name: "narrow", width: 320, height: 700, textTolerancePx: 84, attachmentTolerancePx: 120 },] as const satisfies readonly ViewportSpec[]; interface UserRowMeasurement { measuredRowHeightPx: number; timelineWidthMeasuredPx: number; renderedInVirtualizedRegion: boolean;} interface MountedChatView { [Symbol.asyncDispose]: () => Promise<void>; cleanup: () => Promise<void>; measureUserRow: (targetMessageId: MessageId) => Promise<UserRowMeasurement>; setViewport: (viewport: ViewportSpec) => Promise<void>; setContainerSize: (viewport: Pick<ViewportSpec, "width" | "height">) => Promise<void>; router: ReturnType<typeof getRouter>;} function isoAt(offsetSeconds: number): string { return new Date(BASE_TIME_MS + offsetSeconds * 1_000).toISOString();} function createBaseServerConfig(): ServerConfig { return { environment: { environmentId: EnvironmentId.makeUnsafe("environment-local"), label: "Local environment", platform: { os: "darwin" as const, arch: "arm64" as const }, serverVersion: "0.0.0-test", capabilities: { repositoryIdentity: true }, }, auth: { policy: "loopback-browser", bootstrapMethods: ["one-time-token"], sessionMethods: ["browser-session-cookie", "bearer-session-token"], sessionCookieName: "t3_session", }, cwd: "/repo/project", keybindingsConfigPath: "/repo/project/.t3code-keybindings.json", keybindings: [], issues: [], providers: [ { provider: "codex", enabled: true, installed: true, version: "0.116.0", status: "ready", auth: { status: "authenticated" }, checkedAt: NOW_ISO, models: [], }, ], availableEditors: [], observability: { logsDirectoryPath: "/repo/project/.t3/logs", localTracingEnabled: true, otlpTracesEnabled: false, otlpMetricsEnabled: false, }, settings: { ...DEFAULT_SERVER_SETTINGS, ...DEFAULT_CLIENT_SETTINGS, }, };} function createUserMessage(options: { id: MessageId; text: string; offsetSeconds: number; attachments?: Array<{ type: "image"; id: string; name: string; mimeType: string; sizeBytes: number; }>;}) { return { id: options.id, role: "user" as const, text: options.text, ...(options.attachments ? { attachments: options.attachments } : {}), turnId: null, streaming: false, createdAt: isoAt(options.offsetSeconds), updatedAt: isoAt(options.offsetSeconds + 1), };} function createAssistantMessage(options: { id: MessageId; text: string; offsetSeconds: number }) { return { id: options.id, role: "assistant" as const, text: options.text, turnId: null, streaming: false, createdAt: isoAt(options.offsetSeconds), updatedAt: isoAt(options.offsetSeconds + 1), };} function createTerminalContext(input: { id: string; terminalLabel: string; lineStart: number; lineEnd: number; text: string;}): TerminalContextDraft { return { id: input.id, threadId: THREAD_ID, terminalId: `terminal-${input.id}`, terminalLabel: input.terminalLabel, lineStart: input.lineStart, lineEnd: input.lineEnd, text: input.text, createdAt: NOW_ISO, };} function createSnapshotForTargetUser(options: { targetMessageId: MessageId; targetText: string; targetAttachmentCount?: number; sessionStatus?: OrchestrationSessionStatus;}): OrchestrationReadModel { const messages: Array<OrchestrationReadModel["threads"][number]["messages"][number]> = []; for (let index = 0; index < 22; index += 1) { const isTarget = index === 3; const userId = `msg-user-${index}` as MessageId; const assistantId = `msg-assistant-${index}` as MessageId; const attachments = isTarget && (options.targetAttachmentCount ?? 0) > 0 ? Array.from({ length: options.targetAttachmentCount ?? 0 }, (_, attachmentIndex) => ({ type: "image" as const, id: `attachment-${attachmentIndex + 1}`, name: `attachment-${attachmentIndex + 1}.png`, mimeType: "image/png", sizeBytes: 128, previewUrl: `/attachments/attachment-${attachmentIndex + 1}`, })) : undefined; messages.push( createUserMessage({ id: isTarget ? options.targetMessageId : userId, text: isTarget ? options.targetText : `filler user message ${index}`, offsetSeconds: messages.length * 3, ...(attachments ? { attachments } : {}), }), ); messages.push( createAssistantMessage({ id: assistantId, text: `assistant filler ${index}`, offsetSeconds: messages.length * 3, }), ); } return { snapshotSequence: 1, projects: [ { id: PROJECT_ID, title: "Project", workspaceRoot: "/repo/project", defaultModelSelection: { provider: "codex", model: "gpt-5", }, scripts: [], createdAt: NOW_ISO, updatedAt: NOW_ISO, deletedAt: null, }, ], threads: [ { id: THREAD_ID, projectId: PROJECT_ID, title: "Browser test thread", modelSelection: { provider: "codex", model: "gpt-5", }, interactionMode: "default", runtimeMode: "full-access", branch: "main", worktreePath: null, latestTurn: null, createdAt: NOW_ISO, updatedAt: NOW_ISO, archivedAt: null, deletedAt: null, messages, activities: [], proposedPlans: [], checkpoints: [], session: { threadId: THREAD_ID, status: options.sessionStatus ?? "ready", providerName: "codex", runtimeMode: "full-access", activeTurnId: null, lastError: null, updatedAt: NOW_ISO, }, }, ], updatedAt: NOW_ISO, };} function buildFixture(snapshot: OrchestrationReadModel): TestFixture { return { snapshot, serverConfig: createBaseServerConfig(), welcome: { environment: { environmentId: EnvironmentId.makeUnsafe("environment-local"), label: "Local environment", platform: { os: "darwin" as const, arch: "arm64" as const }, serverVersion: "0.0.0-test", capabilities: { repositoryIdentity: true }, }, cwd: "/repo/project", projectName: "Project", bootstrapProjectId: PROJECT_ID, bootstrapThreadId: THREAD_ID, }, };} function addThreadToSnapshot( snapshot: OrchestrationReadModel, threadId: ThreadId,): OrchestrationReadModel { return { ...snapshot, snapshotSequence: snapshot.snapshotSequence + 1, threads: [ ...snapshot.threads, { id: threadId, projectId: PROJECT_ID, title: "New thread", modelSelection: { provider: "codex", model: "gpt-5", }, interactionMode: "default", runtimeMode: "full-access", branch: "main", worktreePath: null, latestTurn: null, createdAt: NOW_ISO, updatedAt: NOW_ISO, archivedAt: null, deletedAt: null, messages: [], activities: [], proposedPlans: [], checkpoints: [], session: { threadId, status: "ready", providerName: "codex", runtimeMode: "full-access", activeTurnId: null, lastError: null, updatedAt: NOW_ISO, }, }, ], };} function createThreadCreatedEvent(threadId: ThreadId, sequence: number): OrchestrationEvent { return { sequence, eventId: EventId.makeUnsafe(`event-thread-created-${sequence}`), aggregateKind: "thread", aggregateId: threadId, occurredAt: NOW_ISO, commandId: null, causationEventId: null, correlationId: null, metadata: {}, type: "thread.created", payload: { threadId, projectId: PROJECT_ID, title: "New thread", modelSelection: { provider: "codex", model: "gpt-5", }, runtimeMode: "full-access", interactionMode: "default", branch: "main", worktreePath: null, createdAt: NOW_ISO, updatedAt: NOW_ISO, }, };} function createThreadSessionSetEvent(threadId: ThreadId, sequence: number): OrchestrationEvent { return { sequence, eventId: EventId.makeUnsafe(`event-thread-session-set-${sequence}`), aggregateKind: "thread", aggregateId: threadId, occurredAt: NOW_ISO, commandId: null, causationEventId: null, correlationId: null, metadata: {}, type: "thread.session-set", payload: { threadId, session: { threadId, status: "running", providerName: "codex", runtimeMode: "full-access", activeTurnId: `turn-${threadId}` as TurnId, lastError: null, updatedAt: NOW_ISO, }, }, };} function sendOrchestrationDomainEvent(event: OrchestrationEvent): void { rpcHarness.emitStreamValue(WS_METHODS.subscribeOrchestrationDomainEvents, event);} async function waitForWsClient(): Promise<void> { await vi.waitFor( () => { expect( wsRequests.some( (request) => request._tag === WS_METHODS.subscribeOrchestrationDomainEvents, ), ).toBe(true); expect( wsRequests.some((request) => request._tag === WS_METHODS.subscribeServerLifecycle), ).toBe(true); expect(wsRequests.some((request) => request._tag === WS_METHODS.subscribeServerConfig)).toBe( true, ); }, { timeout: 8_000, interval: 16 }, );} function threadRefFor(threadId: ThreadId) { return scopeThreadRef(LOCAL_ENVIRONMENT_ID, threadId);} function threadKeyFor(threadId: ThreadId): string { return scopedThreadKey(threadRefFor(threadId));} function composerDraftFor(target: string) { const { draftsByThreadKey } = useComposerDraftStore.getState(); return draftsByThreadKey[target] ?? draftsByThreadKey[threadKeyFor(target as ThreadId)];} function draftIdFromPath(pathname: string) { const segments = pathname.split("/"); const draftId = segments[segments.length - 1]; if (!draftId) { throw new Error(`Expected thread path, received "${pathname}".`); }Lines 1–500 of 3692. Load more
// Production CSS is part of the behavior under test because row height depends on it. import "../index.css"; import { EventId, ORCHESTRATION_WS_METHODS, EnvironmentId, type MessageId, type OrchestrationEvent, type OrchestrationReadModel, type ProjectId, type ServerConfig, type ServerLifecycleWelcomePayload, type ThreadId, type TurnId, WS_METHODS, OrchestrationSessionStatus, DEFAULT_SERVER_SETTINGS, } from "@t3tools/contracts"; import { scopedProjectKey, scopedThreadKey, scopeProjectRef, scopeThreadRef, } from "@t3tools/client-runtime"; import { RouterProvider, createMemoryHistory } from "@tanstack/react-router"; import { HttpResponse, http, ws } from "msw"; import { setupWorker } from "msw/browser"; import { page } from "vitest/browser"; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { render } from "vitest-browser-react"; import { useComposerDraftStore, DraftId } from "../composerDraftStore"; import { INLINE_TERMINAL_CONTEXT_PLACEHOLDER, type TerminalContextDraft, removeInlineTerminalContextPlaceholder, } from "../lib/terminalContext"; import { isMacPlatform } from "../lib/utils"; import { __resetLocalApiForTests } from "../localApi"; import { AppAtomRegistryProvider } from "../rpc/atomRegistry"; import { getServerConfig } from "../rpc/serverState"; import { getRouter } from "../router"; import { selectBootstrapCompleteForActiveEnvironment, useStore } from "../store"; import { useTerminalStateStore } from "../terminalStateStore"; import { useUiStateStore } from "../uiStateStore"; import { createAuthenticatedSessionHandlers } from "../../test/authHttpHandlers"; import { BrowserWsRpcHarness, type NormalizedWsRpcRequestBody } from "../../test/wsRpcHarness"; import { estimateTimelineMessageHeight } from "./timelineHeight"; import { DEFAULT_CLIENT_SETTINGS } from "@t3tools/contracts/settings"; vi.mock("../lib/gitStatusState", () => ({ useGitStatus: () => ({ data: null, error: null, cause: null, isPending: false }), useGitStatuses: () => new Map(), refreshGitStatus: () => Promise.resolve(null), resetGitStatusStateForTests: () => undefined, })); const THREAD_ID = "thread-browser-test" as ThreadId; const PROJECT_ID = "project-1" as ProjectId; const LOCAL_ENVIRONMENT_ID = EnvironmentId.makeUnsafe("environment-local"); const THREAD_REF = scopeThreadRef(LOCAL_ENVIRONMENT_ID, THREAD_ID); const THREAD_KEY = scopedThreadKey(THREAD_REF); const UUID_ROUTE_RE = /^\/draft\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/; const PROJECT_DRAFT_KEY = `${LOCAL_ENVIRONMENT_ID}:${PROJECT_ID}`; const PROJECT_KEY = scopedProjectKey(scopeProjectRef(LOCAL_ENVIRONMENT_ID, PROJECT_ID)); const NOW_ISO = "2026-03-04T12:00:00.000Z"; const BASE_TIME_MS = Date.parse(NOW_ISO); const ATTACHMENT_SVG = "<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'></svg>"; interface TestFixture { snapshot: OrchestrationReadModel; serverConfig: ServerConfig; welcome: ServerLifecycleWelcomePayload; } let fixture: TestFixture; const rpcHarness = new BrowserWsRpcHarness(); const wsRequests = rpcHarness.requests; let customWsRpcResolver: ((body: NormalizedWsRpcRequestBody) => unknown | undefined) | null = null; const wsLink = ws.link(/ws(s)?:\/\/.*/); interface ViewportSpec { name: string; width: number; height: number; textTolerancePx: number; attachmentTolerancePx: number; } const DEFAULT_VIEWPORT: ViewportSpec = { name: "desktop", width: 960, height: 1_100, textTolerancePx: 44, attachmentTolerancePx: 56, }; const WIDE_FOOTER_VIEWPORT: ViewportSpec = { name: "wide-footer", width: 1_400, height: 1_100, textTolerancePx: 44, attachmentTolerancePx: 56, }; const COMPACT_FOOTER_VIEWPORT: ViewportSpec = { name: "compact-footer", width: 430, height: 932, textTolerancePx: 56, attachmentTolerancePx: 56, }; const TEXT_VIEWPORT_MATRIX = [ DEFAULT_VIEWPORT, { name: "tablet", width: 720, height: 1_024, textTolerancePx: 44, attachmentTolerancePx: 56 }, { name: "mobile", width: 430, height: 932, textTolerancePx: 56, attachmentTolerancePx: 56 }, { name: "narrow", width: 320, height: 700, textTolerancePx: 84, attachmentTolerancePx: 56 }, ] as const satisfies readonly ViewportSpec[]; const ATTACHMENT_VIEWPORT_MATRIX = [ { ...DEFAULT_VIEWPORT, attachmentTolerancePx: 120 }, { name: "mobile", width: 430, height: 932, textTolerancePx: 56, attachmentTolerancePx: 120 }, { name: "narrow", width: 320, height: 700, textTolerancePx: 84, attachmentTolerancePx: 120 }, ] as const satisfies readonly ViewportSpec[]; interface UserRowMeasurement { measuredRowHeightPx: number; timelineWidthMeasuredPx: number; renderedInVirtualizedRegion: boolean; } interface MountedChatView { [Symbol.asyncDispose]: () => Promise<void>; cleanup: () => Promise<void>; measureUserRow: (targetMessageId: MessageId) => Promise<UserRowMeasurement>; setViewport: (viewport: ViewportSpec) => Promise<void>; setContainerSize: (viewport: Pick<ViewportSpec, "width" | "height">) => Promise<void>; router: ReturnType<typeof getRouter>; } function isoAt(offsetSeconds: number): string { return new Date(BASE_TIME_MS + offsetSeconds * 1_000).toISOString(); } function createBaseServerConfig(): ServerConfig { return { environment: { environmentId: EnvironmentId.makeUnsafe("environment-local"), label: "Local environment", platform: { os: "darwin" as const, arch: "arm64" as const }, serverVersion: "0.0.0-test", capabilities: { repositoryIdentity: true }, }, auth: { policy: "loopback-browser", bootstrapMethods: ["one-time-token"], sessionMethods: ["browser-session-cookie", "bearer-session-token"], sessionCookieName: "t3_session", }, cwd: "/repo/project", keybindingsConfigPath: "/repo/project/.t3code-keybindings.json", keybindings: [], issues: [], providers: [ { provider: "codex", enabled: true, installed: true, version: "0.116.0", status: "ready", auth: { status: "authenticated" }, checkedAt: NOW_ISO, models: [], }, ], availableEditors: [], observability: { logsDirectoryPath: "/repo/project/.t3/logs", localTracingEnabled: true, otlpTracesEnabled: false, otlpMetricsEnabled: false, }, settings: { ...DEFAULT_SERVER_SETTINGS, ...DEFAULT_CLIENT_SETTINGS, }, }; } function createUserMessage(options: { id: MessageId; text: string; offsetSeconds: number; attachments?: Array<{ type: "image"; id: string; name: string; mimeType: string; sizeBytes: number; }>; }) { return { id: options.id, role: "user" as const, text: options.text, ...(options.attachments ? { attachments: options.attachments } : {}), turnId: null, streaming: false, createdAt: isoAt(options.offsetSeconds), updatedAt: isoAt(options.offsetSeconds + 1), }; } function createAssistantMessage(options: { id: MessageId; text: string; offsetSeconds: number }) { return { id: options.id, role: "assistant" as const, text: options.text, turnId: null, streaming: false, createdAt: isoAt(options.offsetSeconds), updatedAt: isoAt(options.offsetSeconds + 1), }; } function createTerminalContext(input: { id: string; terminalLabel: string; lineStart: number; lineEnd: number; text: string; }): TerminalContextDraft { return { id: input.id, threadId: THREAD_ID, terminalId: `terminal-${input.id}`, terminalLabel: input.terminalLabel, lineStart: input.lineStart, lineEnd: input.lineEnd, text: input.text, createdAt: NOW_ISO, }; } function createSnapshotForTargetUser(options: { targetMessageId: MessageId; targetText: string; targetAttachmentCount?: number; sessionStatus?: OrchestrationSessionStatus; }): OrchestrationReadModel { const messages: Array<OrchestrationReadModel["threads"][number]["messages"][number]> = []; for (let index = 0; index < 22; index += 1) { const isTarget = index === 3; const userId = `msg-user-${index}` as MessageId; const assistantId = `msg-assistant-${index}` as MessageId; const attachments = isTarget && (options.targetAttachmentCount ?? 0) > 0 ? Array.from({ length: options.targetAttachmentCount ?? 0 }, (_, attachmentIndex) => ({ type: "image" as const, id: `attachment-${attachmentIndex + 1}`, name: `attachment-${attachmentIndex + 1}.png`, mimeType: "image/png", sizeBytes: 128, previewUrl: `/attachments/attachment-${attachmentIndex + 1}`, })) : undefined; messages.push( createUserMessage({ id: isTarget ? options.targetMessageId : userId, text: isTarget ? options.targetText : `filler user message ${index}`, offsetSeconds: messages.length * 3, ...(attachments ? { attachments } : {}), }), ); messages.push( createAssistantMessage({ id: assistantId, text: `assistant filler ${index}`, offsetSeconds: messages.length * 3, }), ); } return { snapshotSequence: 1, projects: [ { id: PROJECT_ID, title: "Project", workspaceRoot: "/repo/project", defaultModelSelection: { provider: "codex", model: "gpt-5", }, scripts: [], createdAt: NOW_ISO, updatedAt: NOW_ISO, deletedAt: null, }, ], threads: [ { id: THREAD_ID, projectId: PROJECT_ID, title: "Browser test thread", modelSelection: { provider: "codex", model: "gpt-5", }, interactionMode: "default", runtimeMode: "full-access", branch: "main", worktreePath: null, latestTurn: null, createdAt: NOW_ISO, updatedAt: NOW_ISO, archivedAt: null, deletedAt: null, messages, activities: [], proposedPlans: [], checkpoints: [], session: { threadId: THREAD_ID, status: options.sessionStatus ?? "ready", providerName: "codex", runtimeMode: "full-access", activeTurnId: null, lastError: null, updatedAt: NOW_ISO, }, }, ], updatedAt: NOW_ISO, }; } function buildFixture(snapshot: OrchestrationReadModel): TestFixture { return { snapshot, serverConfig: createBaseServerConfig(), welcome: { environment: { environmentId: EnvironmentId.makeUnsafe("environment-local"), label: "Local environment", platform: { os: "darwin" as const, arch: "arm64" as const }, serverVersion: "0.0.0-test", capabilities: { repositoryIdentity: true }, }, cwd: "/repo/project", projectName: "Project", bootstrapProjectId: PROJECT_ID, bootstrapThreadId: THREAD_ID, }, }; } function addThreadToSnapshot( snapshot: OrchestrationReadModel, threadId: ThreadId, ): OrchestrationReadModel { return { ...snapshot, snapshotSequence: snapshot.snapshotSequence + 1, threads: [ ...snapshot.threads, { id: threadId, projectId: PROJECT_ID, title: "New thread", modelSelection: { provider: "codex", model: "gpt-5", }, interactionMode: "default", runtimeMode: "full-access", branch: "main", worktreePath: null, latestTurn: null, createdAt: NOW_ISO, updatedAt: NOW_ISO, archivedAt: null, deletedAt: null, messages: [], activities: [], proposedPlans: [], checkpoints: [], session: { threadId, status: "ready", providerName: "codex", runtimeMode: "full-access", activeTurnId: null, lastError: null, updatedAt: NOW_ISO, }, }, ], }; } function createThreadCreatedEvent(threadId: ThreadId, sequence: number): OrchestrationEvent { return { sequence, eventId: EventId.makeUnsafe(`event-thread-created-${sequence}`), aggregateKind: "thread", aggregateId: threadId, occurredAt: NOW_ISO, commandId: null, causationEventId: null, correlationId: null, metadata: {}, type: "thread.created", payload: { threadId, projectId: PROJECT_ID, title: "New thread", modelSelection: { provider: "codex", model: "gpt-5", }, runtimeMode: "full-access", interactionMode: "default", branch: "main", worktreePath: null, createdAt: NOW_ISO, updatedAt: NOW_ISO, }, }; } function createThreadSessionSetEvent(threadId: ThreadId, sequence: number): OrchestrationEvent { return { sequence, eventId: EventId.makeUnsafe(`event-thread-session-set-${sequence}`), aggregateKind: "thread", aggregateId: threadId, occurredAt: NOW_ISO, commandId: null, causationEventId: null, correlationId: null, metadata: {}, type: "thread.session-set", payload: { threadId, session: { threadId, status: "running", providerName: "codex", runtimeMode: "full-access", activeTurnId: `turn-${threadId}` as TurnId, lastError: null, updatedAt: NOW_ISO, }, }, }; } function sendOrchestrationDomainEvent(event: OrchestrationEvent): void { rpcHarness.emitStreamValue(WS_METHODS.subscribeOrchestrationDomainEvents, event); } async function waitForWsClient(): Promise<void> { await vi.waitFor( () => { expect( wsRequests.some( (request) => request._tag === WS_METHODS.subscribeOrchestrationDomainEvents, ), ).toBe(true); expect( wsRequests.some((request) => request._tag === WS_METHODS.subscribeServerLifecycle), ).toBe(true); expect(wsRequests.some((request) => request._tag === WS_METHODS.subscribeServerConfig)).toBe( true, ); }, { timeout: 8_000, interval: 16 }, ); } function threadRefFor(threadId: ThreadId) { return scopeThreadRef(LOCAL_ENVIRONMENT_ID, threadId); } function threadKeyFor(threadId: ThreadId): string { return scopedThreadKey(threadRefFor(threadId)); } function composerDraftFor(target: string) { const { draftsByThreadKey } = useComposerDraftStore.getState(); return draftsByThreadKey[target] ?? draftsByThreadKey[threadKeyFor(target as ThreadId)]; } function draftIdFromPath(pathname: string) { const segments = pathname.split("/"); const draftId = segments[segments.length - 1]; if (!draftId) { throw new Error(`Expected thread path, received "${pathname}".`); }