import { CheckpointRef, DEFAULT_MODEL_BY_PROVIDER, EventId, MessageId, ProjectId, ThreadId, TurnId, type OrchestrationEvent, type OrchestrationReadModel,} from "@t3tools/contracts";import { describe, expect, it } from "vitest"; import { applyOrchestrationEvent, applyOrchestrationEvents, syncServerReadModel, type AppState,} from "./store";import { DEFAULT_INTERACTION_MODE, DEFAULT_RUNTIME_MODE, type Thread } from "./types"; function makeThread(overrides: Partial<Thread> = {}): Thread { return { id: ThreadId.makeUnsafe("thread-1"), codexThreadId: null, projectId: ProjectId.makeUnsafe("project-1"), title: "Thread", modelSelection: { provider: "codex", model: "gpt-5-codex", }, runtimeMode: DEFAULT_RUNTIME_MODE, interactionMode: DEFAULT_INTERACTION_MODE, session: null, messages: [], turnDiffSummaries: [], activities: [], proposedPlans: [], error: null, createdAt: "2026-02-13T00:00:00.000Z", archivedAt: null, latestTurn: null, branch: null, worktreePath: null, ...overrides, };} function makeState(thread: Thread): AppState { const threadIdsByProjectId: AppState["threadIdsByProjectId"] = { [thread.projectId]: [thread.id], }; return { projects: [ { id: ProjectId.makeUnsafe("project-1"), name: "Project", cwd: "/tmp/project", defaultModelSelection: { provider: "codex", model: "gpt-5-codex", }, scripts: [], }, ], threads: [thread], sidebarThreadsById: {}, threadIdsByProjectId, bootstrapComplete: true, };} function makeEvent<T extends OrchestrationEvent["type"]>( type: T, payload: Extract<OrchestrationEvent, { type: T }>["payload"], overrides: Partial<Extract<OrchestrationEvent, { type: T }>> = {},): Extract<OrchestrationEvent, { type: T }> { const sequence = overrides.sequence ?? 1; return { sequence, eventId: EventId.makeUnsafe(`event-${sequence}`), aggregateKind: "thread", aggregateId: "threadId" in payload ? payload.threadId : "projectId" in payload ? payload.projectId : ProjectId.makeUnsafe("project-1"), occurredAt: "2026-02-27T00:00:00.000Z", commandId: null, causationEventId: null, correlationId: null, metadata: {}, type, payload, ...overrides, } as Extract<OrchestrationEvent, { type: T }>;} function makeReadModelThread(overrides: Partial<OrchestrationReadModel["threads"][number]>) { return { id: ThreadId.makeUnsafe("thread-1"), projectId: ProjectId.makeUnsafe("project-1"), title: "Thread", modelSelection: { provider: "codex", model: "gpt-5.3-codex", }, runtimeMode: DEFAULT_RUNTIME_MODE, interactionMode: DEFAULT_INTERACTION_MODE, branch: null, worktreePath: null, latestTurn: null, createdAt: "2026-02-27T00:00:00.000Z", updatedAt: "2026-02-27T00:00:00.000Z", archivedAt: null, deletedAt: null, messages: [], activities: [], proposedPlans: [], checkpoints: [], session: null, ...overrides, } satisfies OrchestrationReadModel["threads"][number];} function makeReadModel(thread: OrchestrationReadModel["threads"][number]): OrchestrationReadModel { return { snapshotSequence: 1, updatedAt: "2026-02-27T00:00:00.000Z", projects: [ { id: ProjectId.makeUnsafe("project-1"), title: "Project", workspaceRoot: "/tmp/project", defaultModelSelection: { provider: "codex", model: "gpt-5.3-codex", }, createdAt: "2026-02-27T00:00:00.000Z", updatedAt: "2026-02-27T00:00:00.000Z", deletedAt: null, scripts: [], }, ], threads: [thread], };} function makeReadModelProject( overrides: Partial<OrchestrationReadModel["projects"][number]>,): OrchestrationReadModel["projects"][number] { return { id: ProjectId.makeUnsafe("project-1"), title: "Project", workspaceRoot: "/tmp/project", defaultModelSelection: { provider: "codex", model: "gpt-5.3-codex", }, createdAt: "2026-02-27T00:00:00.000Z", updatedAt: "2026-02-27T00:00:00.000Z", deletedAt: null, scripts: [], ...overrides, };} describe("store read model sync", () => { it("marks bootstrap complete after snapshot sync", () => { const initialState: AppState = { ...makeState(makeThread()), bootstrapComplete: false, }; const next = syncServerReadModel(initialState, makeReadModel(makeReadModelThread({}))); expect(next.bootstrapComplete).toBe(true); }); it("preserves claude model slugs without an active session", () => { const initialState = makeState(makeThread()); const readModel = makeReadModel( makeReadModelThread({ modelSelection: { provider: "claudeAgent", model: "claude-opus-4-6", }, }), ); const next = syncServerReadModel(initialState, readModel); expect(next.threads[0]?.modelSelection.model).toBe("claude-opus-4-6"); }); it("resolves claude aliases when session provider is claudeAgent", () => { const initialState = makeState(makeThread()); const readModel = makeReadModel( makeReadModelThread({ modelSelection: { provider: "claudeAgent", model: "sonnet", }, session: { threadId: ThreadId.makeUnsafe("thread-1"), status: "ready", providerName: "claudeAgent", runtimeMode: "approval-required", activeTurnId: null, lastError: null, updatedAt: "2026-02-27T00:00:00.000Z", }, }), ); const next = syncServerReadModel(initialState, readModel); expect(next.threads[0]?.modelSelection.model).toBe("claude-sonnet-4-6"); }); it("preserves project and thread updatedAt timestamps from the read model", () => { const initialState = makeState(makeThread()); const readModel = makeReadModel( makeReadModelThread({ updatedAt: "2026-02-27T00:05:00.000Z", }), ); const next = syncServerReadModel(initialState, readModel); expect(next.projects[0]?.updatedAt).toBe("2026-02-27T00:00:00.000Z"); expect(next.threads[0]?.updatedAt).toBe("2026-02-27T00:05:00.000Z"); }); it("maps archivedAt from the read model", () => { const initialState = makeState(makeThread()); const archivedAt = "2026-02-28T00:00:00.000Z"; const next = syncServerReadModel( initialState, makeReadModel( makeReadModelThread({ archivedAt, }), ), ); expect(next.threads[0]?.archivedAt).toBe(archivedAt); }); it("replaces projects using snapshot order during recovery", () => { const project1 = ProjectId.makeUnsafe("project-1"); const project2 = ProjectId.makeUnsafe("project-2"); const project3 = ProjectId.makeUnsafe("project-3"); const initialState: AppState = { projects: [ { id: project2, name: "Project 2", cwd: "/tmp/project-2", defaultModelSelection: { provider: "codex", model: DEFAULT_MODEL_BY_PROVIDER.codex, }, scripts: [], }, { id: project1, name: "Project 1", cwd: "/tmp/project-1", defaultModelSelection: { provider: "codex", model: DEFAULT_MODEL_BY_PROVIDER.codex, }, scripts: [], }, ], threads: [], sidebarThreadsById: {}, threadIdsByProjectId: {}, bootstrapComplete: true, }; const readModel: OrchestrationReadModel = { snapshotSequence: 2, updatedAt: "2026-02-27T00:00:00.000Z", projects: [ makeReadModelProject({ id: project1, title: "Project 1", workspaceRoot: "/tmp/project-1", }), makeReadModelProject({ id: project2, title: "Project 2", workspaceRoot: "/tmp/project-2", }), makeReadModelProject({ id: project3, title: "Project 3", workspaceRoot: "/tmp/project-3", }), ], threads: [], }; const next = syncServerReadModel(initialState, readModel); expect(next.projects.map((project) => project.id)).toEqual([project1, project2, project3]); });}); describe("incremental orchestration updates", () => { it("does not mark bootstrap complete for incremental events", () => { const state: AppState = { ...makeState(makeThread()), bootstrapComplete: false, }; const next = applyOrchestrationEvent( state, makeEvent("thread.meta-updated", { threadId: ThreadId.makeUnsafe("thread-1"), title: "Updated title", updatedAt: "2026-02-27T00:00:01.000Z", }), ); expect(next.bootstrapComplete).toBe(false); }); it("preserves state identity for no-op project and thread deletes", () => { const thread = makeThread(); const state = makeState(thread); const nextAfterProjectDelete = applyOrchestrationEvent( state, makeEvent("project.deleted", { projectId: ProjectId.makeUnsafe("project-missing"), deletedAt: "2026-02-27T00:00:01.000Z", }), ); const nextAfterThreadDelete = applyOrchestrationEvent( state, makeEvent("thread.deleted", { threadId: ThreadId.makeUnsafe("thread-missing"), deletedAt: "2026-02-27T00:00:01.000Z", }), ); expect(nextAfterProjectDelete).toBe(state); expect(nextAfterThreadDelete).toBe(state); }); it("reuses an existing project row when project.created arrives with a new id for the same cwd", () => { const originalProjectId = ProjectId.makeUnsafe("project-1"); const recreatedProjectId = ProjectId.makeUnsafe("project-2"); const state: AppState = { projects: [ { id: originalProjectId, name: "Project", cwd: "/tmp/project", defaultModelSelection: { provider: "codex", model: DEFAULT_MODEL_BY_PROVIDER.codex, }, scripts: [], }, ], threads: [], sidebarThreadsById: {}, threadIdsByProjectId: {}, bootstrapComplete: true, }; const next = applyOrchestrationEvent( state, makeEvent("project.created", { projectId: recreatedProjectId, title: "Project Recreated", workspaceRoot: "/tmp/project", defaultModelSelection: { provider: "codex", model: DEFAULT_MODEL_BY_PROVIDER.codex, }, scripts: [], createdAt: "2026-02-27T00:00:01.000Z", updatedAt: "2026-02-27T00:00:01.000Z", }), ); expect(next.projects).toHaveLength(1); expect(next.projects[0]?.id).toBe(recreatedProjectId); expect(next.projects[0]?.cwd).toBe("/tmp/project"); expect(next.projects[0]?.name).toBe("Project Recreated"); }); it("removes stale project index entries when thread.created recreates a thread under a new project", () => { const originalProjectId = ProjectId.makeUnsafe("project-1"); const recreatedProjectId = ProjectId.makeUnsafe("project-2"); const threadId = ThreadId.makeUnsafe("thread-1"); const thread = makeThread({ id: threadId, projectId: originalProjectId, }); const state: AppState = { projects: [ { id: originalProjectId, name: "Project 1", cwd: "/tmp/project-1", defaultModelSelection: { provider: "codex", model: DEFAULT_MODEL_BY_PROVIDER.codex, }, scripts: [], }, { id: recreatedProjectId, name: "Project 2", cwd: "/tmp/project-2", defaultModelSelection: { provider: "codex", model: DEFAULT_MODEL_BY_PROVIDER.codex, }, scripts: [], }, ], threads: [thread], sidebarThreadsById: {}, threadIdsByProjectId: { [originalProjectId]: [threadId], }, bootstrapComplete: true, }; const next = applyOrchestrationEvent( state, makeEvent("thread.created", { threadId, projectId: recreatedProjectId, title: "Recovered thread", modelSelection: { provider: "codex", model: DEFAULT_MODEL_BY_PROVIDER.codex, }, runtimeMode: DEFAULT_RUNTIME_MODE, interactionMode: DEFAULT_INTERACTION_MODE, branch: null, worktreePath: null, createdAt: "2026-02-27T00:00:01.000Z", updatedAt: "2026-02-27T00:00:01.000Z", }), ); expect(next.threads).toHaveLength(1); expect(next.threads[0]?.projectId).toBe(recreatedProjectId); expect(next.threadIdsByProjectId[originalProjectId]).toBeUndefined(); expect(next.threadIdsByProjectId[recreatedProjectId]).toEqual([threadId]); }); it("updates only the affected thread for message events", () => { const thread1 = makeThread({ id: ThreadId.makeUnsafe("thread-1"), messages: [ { id: MessageId.makeUnsafe("message-1"), role: "assistant", text: "hello", turnId: TurnId.makeUnsafe("turn-1"), createdAt: "2026-02-27T00:00:00.000Z", completedAt: "2026-02-27T00:00:00.000Z", streaming: false, }, ], }); const thread2 = makeThread({ id: ThreadId.makeUnsafe("thread-2") }); const state: AppState = { ...makeState(thread1), threads: [thread1, thread2], }; const next = applyOrchestrationEvent( state, makeEvent("thread.message-sent", { threadId: thread1.id, messageId: MessageId.makeUnsafe("message-1"), role: "assistant", text: " world", turnId: TurnId.makeUnsafe("turn-1"), streaming: true, createdAt: "2026-02-27T00:00:01.000Z", updatedAt: "2026-02-27T00:00:01.000Z", }), ); expect(next.threads[0]?.messages[0]?.text).toBe("hello world"); expect(next.threads[0]?.latestTurn?.state).toBe("running"); expect(next.threads[1]).toBe(thread2); });Lines 1–500 of 816. Load more
import { CheckpointRef, DEFAULT_MODEL_BY_PROVIDER, EventId, MessageId, ProjectId, ThreadId, TurnId, type OrchestrationEvent, type OrchestrationReadModel, } from "@t3tools/contracts"; import { describe, expect, it } from "vitest"; import { applyOrchestrationEvent, applyOrchestrationEvents, syncServerReadModel, type AppState, } from "./store"; import { DEFAULT_INTERACTION_MODE, DEFAULT_RUNTIME_MODE, type Thread } from "./types"; function makeThread(overrides: Partial<Thread> = {}): Thread { return { id: ThreadId.makeUnsafe("thread-1"), codexThreadId: null, projectId: ProjectId.makeUnsafe("project-1"), title: "Thread", modelSelection: { provider: "codex", model: "gpt-5-codex", }, runtimeMode: DEFAULT_RUNTIME_MODE, interactionMode: DEFAULT_INTERACTION_MODE, session: null, messages: [], turnDiffSummaries: [], activities: [], proposedPlans: [], error: null, createdAt: "2026-02-13T00:00:00.000Z", archivedAt: null, latestTurn: null, branch: null, worktreePath: null, ...overrides, }; } function makeState(thread: Thread): AppState { const threadIdsByProjectId: AppState["threadIdsByProjectId"] = { [thread.projectId]: [thread.id], }; return { projects: [ { id: ProjectId.makeUnsafe("project-1"), name: "Project", cwd: "/tmp/project", defaultModelSelection: { provider: "codex", model: "gpt-5-codex", }, scripts: [], }, ], threads: [thread], sidebarThreadsById: {}, threadIdsByProjectId, bootstrapComplete: true, }; } function makeEvent<T extends OrchestrationEvent["type"]>( type: T, payload: Extract<OrchestrationEvent, { type: T }>["payload"], overrides: Partial<Extract<OrchestrationEvent, { type: T }>> = {}, ): Extract<OrchestrationEvent, { type: T }> { const sequence = overrides.sequence ?? 1; return { sequence, eventId: EventId.makeUnsafe(`event-${sequence}`), aggregateKind: "thread", aggregateId: "threadId" in payload ? payload.threadId : "projectId" in payload ? payload.projectId : ProjectId.makeUnsafe("project-1"), occurredAt: "2026-02-27T00:00:00.000Z", commandId: null, causationEventId: null, correlationId: null, metadata: {}, type, payload, ...overrides, } as Extract<OrchestrationEvent, { type: T }>; } function makeReadModelThread(overrides: Partial<OrchestrationReadModel["threads"][number]>) { return { id: ThreadId.makeUnsafe("thread-1"), projectId: ProjectId.makeUnsafe("project-1"), title: "Thread", modelSelection: { provider: "codex", model: "gpt-5.3-codex", }, runtimeMode: DEFAULT_RUNTIME_MODE, interactionMode: DEFAULT_INTERACTION_MODE, branch: null, worktreePath: null, latestTurn: null, createdAt: "2026-02-27T00:00:00.000Z", updatedAt: "2026-02-27T00:00:00.000Z", archivedAt: null, deletedAt: null, messages: [], activities: [], proposedPlans: [], checkpoints: [], session: null, ...overrides, } satisfies OrchestrationReadModel["threads"][number]; } function makeReadModel(thread: OrchestrationReadModel["threads"][number]): OrchestrationReadModel { return { snapshotSequence: 1, updatedAt: "2026-02-27T00:00:00.000Z", projects: [ { id: ProjectId.makeUnsafe("project-1"), title: "Project", workspaceRoot: "/tmp/project", defaultModelSelection: { provider: "codex", model: "gpt-5.3-codex", }, createdAt: "2026-02-27T00:00:00.000Z", updatedAt: "2026-02-27T00:00:00.000Z", deletedAt: null, scripts: [], }, ], threads: [thread], }; } function makeReadModelProject( overrides: Partial<OrchestrationReadModel["projects"][number]>, ): OrchestrationReadModel["projects"][number] { return { id: ProjectId.makeUnsafe("project-1"), title: "Project", workspaceRoot: "/tmp/project", defaultModelSelection: { provider: "codex", model: "gpt-5.3-codex", }, createdAt: "2026-02-27T00:00:00.000Z", updatedAt: "2026-02-27T00:00:00.000Z", deletedAt: null, scripts: [], ...overrides, }; } describe("store read model sync", () => { it("marks bootstrap complete after snapshot sync", () => { const initialState: AppState = { ...makeState(makeThread()), bootstrapComplete: false, }; const next = syncServerReadModel(initialState, makeReadModel(makeReadModelThread({}))); expect(next.bootstrapComplete).toBe(true); }); it("preserves claude model slugs without an active session", () => { const initialState = makeState(makeThread()); const readModel = makeReadModel( makeReadModelThread({ modelSelection: { provider: "claudeAgent", model: "claude-opus-4-6", }, }), ); const next = syncServerReadModel(initialState, readModel); expect(next.threads[0]?.modelSelection.model).toBe("claude-opus-4-6"); }); it("resolves claude aliases when session provider is claudeAgent", () => { const initialState = makeState(makeThread()); const readModel = makeReadModel( makeReadModelThread({ modelSelection: { provider: "claudeAgent", model: "sonnet", }, session: { threadId: ThreadId.makeUnsafe("thread-1"), status: "ready", providerName: "claudeAgent", runtimeMode: "approval-required", activeTurnId: null, lastError: null, updatedAt: "2026-02-27T00:00:00.000Z", }, }), ); const next = syncServerReadModel(initialState, readModel); expect(next.threads[0]?.modelSelection.model).toBe("claude-sonnet-4-6"); }); it("preserves project and thread updatedAt timestamps from the read model", () => { const initialState = makeState(makeThread()); const readModel = makeReadModel( makeReadModelThread({ updatedAt: "2026-02-27T00:05:00.000Z", }), ); const next = syncServerReadModel(initialState, readModel); expect(next.projects[0]?.updatedAt).toBe("2026-02-27T00:00:00.000Z"); expect(next.threads[0]?.updatedAt).toBe("2026-02-27T00:05:00.000Z"); }); it("maps archivedAt from the read model", () => { const initialState = makeState(makeThread()); const archivedAt = "2026-02-28T00:00:00.000Z"; const next = syncServerReadModel( initialState, makeReadModel( makeReadModelThread({ archivedAt, }), ), ); expect(next.threads[0]?.archivedAt).toBe(archivedAt); }); it("replaces projects using snapshot order during recovery", () => { const project1 = ProjectId.makeUnsafe("project-1"); const project2 = ProjectId.makeUnsafe("project-2"); const project3 = ProjectId.makeUnsafe("project-3"); const initialState: AppState = { projects: [ { id: project2, name: "Project 2", cwd: "/tmp/project-2", defaultModelSelection: { provider: "codex", model: DEFAULT_MODEL_BY_PROVIDER.codex, }, scripts: [], }, { id: project1, name: "Project 1", cwd: "/tmp/project-1", defaultModelSelection: { provider: "codex", model: DEFAULT_MODEL_BY_PROVIDER.codex, }, scripts: [], }, ], threads: [], sidebarThreadsById: {}, threadIdsByProjectId: {}, bootstrapComplete: true, }; const readModel: OrchestrationReadModel = { snapshotSequence: 2, updatedAt: "2026-02-27T00:00:00.000Z", projects: [ makeReadModelProject({ id: project1, title: "Project 1", workspaceRoot: "/tmp/project-1", }), makeReadModelProject({ id: project2, title: "Project 2", workspaceRoot: "/tmp/project-2", }), makeReadModelProject({ id: project3, title: "Project 3", workspaceRoot: "/tmp/project-3", }), ], threads: [], }; const next = syncServerReadModel(initialState, readModel); expect(next.projects.map((project) => project.id)).toEqual([project1, project2, project3]); }); }); describe("incremental orchestration updates", () => { it("does not mark bootstrap complete for incremental events", () => { const state: AppState = { ...makeState(makeThread()), bootstrapComplete: false, }; const next = applyOrchestrationEvent( state, makeEvent("thread.meta-updated", { threadId: ThreadId.makeUnsafe("thread-1"), title: "Updated title", updatedAt: "2026-02-27T00:00:01.000Z", }), ); expect(next.bootstrapComplete).toBe(false); }); it("preserves state identity for no-op project and thread deletes", () => { const thread = makeThread(); const state = makeState(thread); const nextAfterProjectDelete = applyOrchestrationEvent( state, makeEvent("project.deleted", { projectId: ProjectId.makeUnsafe("project-missing"), deletedAt: "2026-02-27T00:00:01.000Z", }), ); const nextAfterThreadDelete = applyOrchestrationEvent( state, makeEvent("thread.deleted", { threadId: ThreadId.makeUnsafe("thread-missing"), deletedAt: "2026-02-27T00:00:01.000Z", }), ); expect(nextAfterProjectDelete).toBe(state); expect(nextAfterThreadDelete).toBe(state); }); it("reuses an existing project row when project.created arrives with a new id for the same cwd", () => { const originalProjectId = ProjectId.makeUnsafe("project-1"); const recreatedProjectId = ProjectId.makeUnsafe("project-2"); const state: AppState = { projects: [ { id: originalProjectId, name: "Project", cwd: "/tmp/project", defaultModelSelection: { provider: "codex", model: DEFAULT_MODEL_BY_PROVIDER.codex, }, scripts: [], }, ], threads: [], sidebarThreadsById: {}, threadIdsByProjectId: {}, bootstrapComplete: true, }; const next = applyOrchestrationEvent( state, makeEvent("project.created", { projectId: recreatedProjectId, title: "Project Recreated", workspaceRoot: "/tmp/project", defaultModelSelection: { provider: "codex", model: DEFAULT_MODEL_BY_PROVIDER.codex, }, scripts: [], createdAt: "2026-02-27T00:00:01.000Z", updatedAt: "2026-02-27T00:00:01.000Z", }), ); expect(next.projects).toHaveLength(1); expect(next.projects[0]?.id).toBe(recreatedProjectId); expect(next.projects[0]?.cwd).toBe("/tmp/project"); expect(next.projects[0]?.name).toBe("Project Recreated"); }); it("removes stale project index entries when thread.created recreates a thread under a new project", () => { const originalProjectId = ProjectId.makeUnsafe("project-1"); const recreatedProjectId = ProjectId.makeUnsafe("project-2"); const threadId = ThreadId.makeUnsafe("thread-1"); const thread = makeThread({ id: threadId, projectId: originalProjectId, }); const state: AppState = { projects: [ { id: originalProjectId, name: "Project 1", cwd: "/tmp/project-1", defaultModelSelection: { provider: "codex", model: DEFAULT_MODEL_BY_PROVIDER.codex, }, scripts: [], }, { id: recreatedProjectId, name: "Project 2", cwd: "/tmp/project-2", defaultModelSelection: { provider: "codex", model: DEFAULT_MODEL_BY_PROVIDER.codex, }, scripts: [], }, ], threads: [thread], sidebarThreadsById: {}, threadIdsByProjectId: { [originalProjectId]: [threadId], }, bootstrapComplete: true, }; const next = applyOrchestrationEvent( state, makeEvent("thread.created", { threadId, projectId: recreatedProjectId, title: "Recovered thread", modelSelection: { provider: "codex", model: DEFAULT_MODEL_BY_PROVIDER.codex, }, runtimeMode: DEFAULT_RUNTIME_MODE, interactionMode: DEFAULT_INTERACTION_MODE, branch: null, worktreePath: null, createdAt: "2026-02-27T00:00:01.000Z", updatedAt: "2026-02-27T00:00:01.000Z", }), ); expect(next.threads).toHaveLength(1); expect(next.threads[0]?.projectId).toBe(recreatedProjectId); expect(next.threadIdsByProjectId[originalProjectId]).toBeUndefined(); expect(next.threadIdsByProjectId[recreatedProjectId]).toEqual([threadId]); }); it("updates only the affected thread for message events", () => { const thread1 = makeThread({ id: ThreadId.makeUnsafe("thread-1"), messages: [ { id: MessageId.makeUnsafe("message-1"), role: "assistant", text: "hello", turnId: TurnId.makeUnsafe("turn-1"), createdAt: "2026-02-27T00:00:00.000Z", completedAt: "2026-02-27T00:00:00.000Z", streaming: false, }, ], }); const thread2 = makeThread({ id: ThreadId.makeUnsafe("thread-2") }); const state: AppState = { ...makeState(thread1), threads: [thread1, thread2], }; const next = applyOrchestrationEvent( state, makeEvent("thread.message-sent", { threadId: thread1.id, messageId: MessageId.makeUnsafe("message-1"), role: "assistant", text: " world", turnId: TurnId.makeUnsafe("turn-1"), streaming: true, createdAt: "2026-02-27T00:00:01.000Z", updatedAt: "2026-02-27T00:00:01.000Z", }), ); expect(next.threads[0]?.messages[0]?.text).toBe("hello world"); expect(next.threads[0]?.latestTurn?.state).toBe("running"); expect(next.threads[1]).toBe(thread2); });