import { ApprovalRequestId, type AssistantDeliveryMode, CommandId, MessageId, type OrchestrationEvent, type OrchestrationMessage, type OrchestrationProposedPlanId, CheckpointRef, isToolLifecycleItemType, ThreadId, type ThreadTokenUsageSnapshot, TurnId, type OrchestrationCheckpointSummary, type OrchestrationProposedPlan, type OrchestrationThread, type OrchestrationThreadActivity, type ProviderRuntimeEvent, } from "@t3tools/contracts"; import * as Cache from "effect/Cache"; import * as Cause from "effect/Cause"; import * as Duration from "effect/Duration"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Stream from "effect/Stream"; import { makeDrainableWorker } from "@t3tools/shared/DrainableWorker"; import { ProviderService } from "../../provider/Services/ProviderService.ts"; import { ProjectionTurnRepository } from "../../persistence/Services/ProjectionTurns.ts"; import { ProjectionTurnRepositoryLive } from "../../persistence/Layers/ProjectionTurns.ts"; import { isGitRepository } from "../../git/Utils.ts"; import { OrchestrationEngineService } from "../Services/OrchestrationEngine.ts"; import { ProjectionSnapshotQuery } from "../Services/ProjectionSnapshotQuery.ts"; import { ProviderRuntimeIngestionService, type ProviderRuntimeIngestionShape, } from "../Services/ProviderRuntimeIngestion.ts"; import { ServerSettingsService } from "../../serverSettings.ts"; const providerTurnKey = (threadId: ThreadId, turnId: TurnId) => `${threadId}:${turnId}`; const providerCommandId = (event: ProviderRuntimeEvent, tag: string): CommandId => CommandId.make(`provider:${event.eventId}:${tag}:${crypto.randomUUID()}`); interface AssistantSegmentState { baseKey: string; nextSegmentIndex: number; activeMessageId: MessageId | null; } const TURN_MESSAGE_IDS_BY_TURN_CACHE_CAPACITY = 10_000; const TURN_MESSAGE_IDS_BY_TURN_TTL = Duration.minutes(120); const BUFFERED_MESSAGE_TEXT_BY_MESSAGE_ID_CACHE_CAPACITY = 20_000; const BUFFERED_MESSAGE_TEXT_BY_MESSAGE_ID_TTL = Duration.minutes(120); const BUFFERED_PROPOSED_PLAN_BY_ID_CACHE_CAPACITY = 10_000; const BUFFERED_PROPOSED_PLAN_BY_ID_TTL = Duration.minutes(120); const MAX_BUFFERED_ASSISTANT_CHARS = 24_000; const STRICT_PROVIDER_LIFECYCLE_GUARD = process.env.T3CODE_STRICT_PROVIDER_LIFECYCLE_GUARD !== "0"; type TurnStartRequestedDomainEvent = Extract< OrchestrationEvent, { type: "thread.turn-start-requested" } >; type RuntimeIngestionInput = | { source: "runtime"; event: ProviderRuntimeEvent; } | { source: "domain"; event: TurnStartRequestedDomainEvent; }; function toTurnId(value: TurnId | string | undefined): TurnId | undefined { return value === undefined ? undefined : TurnId.make(String(value)); } function toApprovalRequestId(value: string | undefined): ApprovalRequestId | undefined { return value === undefined ? undefined : ApprovalRequestId.make(value); } function sameId(left: string | null | undefined, right: string | null | undefined): boolean { if (left === null || left === undefined || right === null || right === undefined) { return false; } return left === right; } function hasAssistantMessageForTurn( messages: ReadonlyArray, turnId: TurnId, options?: { readonly streamingOnly?: boolean }, ): boolean { for (let index = 0; index < messages.length; index += 1) { const message = messages[index]; if (!message) { continue; } if (message.role !== "assistant" || message.turnId !== turnId) { continue; } if (options?.streamingOnly === true && !message.streaming) { continue; } return true; } return false; } function findMessageById( messages: ReadonlyArray, messageId: MessageId, ): OrchestrationMessage | undefined { for (let index = 0; index < messages.length; index += 1) { const message = messages[index]; if (message?.id === messageId) { return message; } } return undefined; } function findProposedPlanById( proposedPlans: ReadonlyArray< Pick >, planId: string, ): | Pick | undefined { for (let index = 0; index < proposedPlans.length; index += 1) { const proposedPlan = proposedPlans[index]; if (proposedPlan?.id === planId) { return proposedPlan; } } return undefined; } function hasCheckpointForTurn( checkpoints: ReadonlyArray, turnId: TurnId, ): boolean { for (let index = 0; index < checkpoints.length; index += 1) { if (checkpoints[index]?.turnId === turnId) { return true; } } return false; } function maxCheckpointTurnCount( checkpoints: ReadonlyArray, ): number { let maxTurnCount = 0; for (let index = 0; index < checkpoints.length; index += 1) { const checkpoint = checkpoints[index]; if (checkpoint && checkpoint.checkpointTurnCount > maxTurnCount) { maxTurnCount = checkpoint.checkpointTurnCount; } } return maxTurnCount; } function truncateDetail(value: string, limit = 180): string { return value.length > limit ? `${value.slice(0, limit - 3)}...` : value; } function normalizeProposedPlanMarkdown(planMarkdown: string | undefined): string | undefined { const trimmed = planMarkdown?.trim(); if (!trimmed) { return undefined; } return trimmed; } function hasRenderableAssistantText(text: string | undefined): boolean { return (text?.trim().length ?? 0) > 0; } function proposedPlanIdForTurn(threadId: ThreadId, turnId: TurnId): string { return `plan:${threadId}:turn:${turnId}`; } function proposedPlanIdFromEvent(event: ProviderRuntimeEvent, threadId: ThreadId): string { const turnId = toTurnId(event.turnId); if (turnId) { return proposedPlanIdForTurn(threadId, turnId); } if (event.itemId) { return `plan:${threadId}:item:${event.itemId}`; } return `plan:${threadId}:event:${event.eventId}`; } function assistantSegmentBaseKeyFromEvent(event: ProviderRuntimeEvent): string { return String(event.itemId ?? event.turnId ?? event.eventId); } function assistantSegmentMessageId(baseKey: string, segmentIndex: number): MessageId { return MessageId.make( segmentIndex === 0 ? `assistant:${baseKey}` : `assistant:${baseKey}:segment:${segmentIndex}`, ); } function buildContextWindowActivityPayload( event: ProviderRuntimeEvent, ): ThreadTokenUsageSnapshot | undefined { if (event.type !== "thread.token-usage.updated" || event.payload.usage.usedTokens <= 0) { return undefined; } return event.payload.usage; } function normalizeRuntimeTurnState( value: string | undefined, ): "completed" | "failed" | "interrupted" | "cancelled" { switch (value) { case "failed": case "interrupted": case "cancelled": case "completed": return value; default: return "completed"; } } function orchestrationSessionStatusFromRuntimeState( state: "starting" | "running" | "waiting" | "ready" | "interrupted" | "stopped" | "error", ): "starting" | "running" | "ready" | "interrupted" | "stopped" | "error" { switch (state) { case "starting": return "starting"; case "running": case "waiting": return "running"; case "ready": return "ready"; case "interrupted": return "interrupted"; case "stopped": return "stopped"; case "error": return "error"; } } function requestKindFromCanonicalRequestType( requestType: string | undefined, ): "command" | "file-read" | "file-change" | undefined { switch (requestType) { case "command_execution_approval": case "exec_command_approval": return "command"; case "file_read_approval": return "file-read"; case "file_change_approval": case "apply_patch_approval": return "file-change"; default: return undefined; } } function runtimeEventToActivities( event: ProviderRuntimeEvent, ): ReadonlyArray { const maybeSequence = (() => { const eventWithSequence = event as ProviderRuntimeEvent & { sessionSequence?: number }; return eventWithSequence.sessionSequence !== undefined ? { sequence: eventWithSequence.sessionSequence } : {}; })(); switch (event.type) { case "request.opened": { if (event.payload.requestType === "tool_user_input") { return []; } const requestKind = requestKindFromCanonicalRequestType(event.payload.requestType); return [ { id: event.eventId, createdAt: event.createdAt, tone: "approval", kind: "approval.requested", summary: requestKind === "command" ? "Command approval requested" : requestKind === "file-read" ? "File-read approval requested" : requestKind === "file-change" ? "File-change approval requested" : "Approval requested", payload: { requestId: toApprovalRequestId(event.requestId), ...(requestKind ? { requestKind } : {}), requestType: event.payload.requestType, ...(event.payload.detail ? { detail: truncateDetail(event.payload.detail) } : {}), }, turnId: toTurnId(event.turnId) ?? null, ...maybeSequence, }, ]; } case "request.resolved": { if (event.payload.requestType === "tool_user_input") { return []; } const requestKind = requestKindFromCanonicalRequestType(event.payload.requestType); return [ { id: event.eventId, createdAt: event.createdAt, tone: "approval", kind: "approval.resolved", summary: "Approval resolved", payload: { requestId: toApprovalRequestId(event.requestId), ...(requestKind ? { requestKind } : {}), requestType: event.payload.requestType, ...(event.payload.decision ? { decision: event.payload.decision } : {}), }, turnId: toTurnId(event.turnId) ?? null, ...maybeSequence, }, ]; } case "runtime.error": { return [ { id: event.eventId, createdAt: event.createdAt, tone: "error", kind: "runtime.error", summary: "Runtime error", payload: { message: truncateDetail(event.payload.message), }, turnId: toTurnId(event.turnId) ?? null, ...maybeSequence, }, ]; } case "runtime.warning": { return [ { id: event.eventId, createdAt: event.createdAt, tone: "info", kind: "runtime.warning", summary: "Runtime warning", payload: { message: truncateDetail(event.payload.message), ...(event.payload.detail !== undefined ? { detail: event.payload.detail } : {}), }, turnId: toTurnId(event.turnId) ?? null, ...maybeSequence, }, ]; } case "turn.plan.updated": { return [ { id: event.eventId, createdAt: event.createdAt, tone: "info", kind: "turn.plan.updated", summary: "Plan updated", payload: { plan: event.payload.plan, ...(event.payload.explanation !== undefined ? { explanation: event.payload.explanation } : {}), }, turnId: toTurnId(event.turnId) ?? null, ...maybeSequence, }, ]; } case "user-input.requested": { return [ { id: event.eventId, createdAt: event.createdAt, tone: "info", kind: "user-input.requested", summary: "User input requested", payload: { ...(event.requestId ? { requestId: event.requestId } : {}), questions: event.payload.questions, }, turnId: toTurnId(event.turnId) ?? null, ...maybeSequence, }, ]; } case "user-input.resolved": { return [ { id: event.eventId, createdAt: event.createdAt, tone: "info", kind: "user-input.resolved", summary: "User input submitted", payload: { ...(event.requestId ? { requestId: event.requestId } : {}), answers: event.payload.answers, }, turnId: toTurnId(event.turnId) ?? null, ...maybeSequence, }, ]; } case "task.started": { return [ { id: event.eventId, createdAt: event.createdAt, tone: "info", kind: "task.started", summary: event.payload.taskType === "plan" ? "Plan task started" : event.payload.taskType ? `${event.payload.taskType} task started` : "Task started", payload: { taskId: event.payload.taskId, ...(event.payload.taskType ? { taskType: event.payload.taskType } : {}), ...(event.payload.description ? { detail: truncateDetail(event.payload.description) } : {}), }, turnId: toTurnId(event.turnId) ?? null, ...maybeSequence, }, ]; } case "task.progress": { return [ { id: event.eventId, createdAt: event.createdAt, tone: "info", kind: "task.progress", summary: "Reasoning update", payload: { taskId: event.payload.taskId, detail: truncateDetail(event.payload.summary ?? event.payload.description), ...(event.payload.summary ? { summary: truncateDetail(event.payload.summary) } : {}), ...(event.payload.lastToolName ? { lastToolName: event.payload.lastToolName } : {}), ...(event.payload.usage !== undefined ? { usage: event.payload.usage } : {}), }, turnId: toTurnId(event.turnId) ?? null, ...maybeSequence, }, ]; } case "task.completed": { return [ { id: event.eventId, createdAt: event.createdAt, tone: event.payload.status === "failed" ? "error" : "info", kind: "task.completed", summary: event.payload.status === "failed" ? "Task failed" : event.payload.status === "stopped" ? "Task stopped" : "Task completed", payload: { taskId: event.payload.taskId, status: event.payload.status, ...(event.payload.summary ? { detail: truncateDetail(event.payload.summary) } : {}), ...(event.payload.usage !== undefined ? { usage: event.payload.usage } : {}), }, turnId: toTurnId(event.turnId) ?? null, ...maybeSequence, }, ]; } case "thread.state.changed": { if (event.payload.state !== "compacted") { return []; } return [ { id: event.eventId, createdAt: event.createdAt, tone: "info", kind: "context-compaction", summary: "Context compacted", payload: { state: event.payload.state, ...(event.payload.detail !== undefined ? { detail: event.payload.detail } : {}), }, turnId: toTurnId(event.turnId) ?? null, ...maybeSequence, }, ]; } case "thread.token-usage.updated": { const payload = buildContextWindowActivityPayload(event); if (!payload) { return []; } return [ { id: event.eventId, createdAt: event.createdAt, tone: "info", kind: "context-window.updated", summary: "Context window updated", payload, turnId: toTurnId(event.turnId) ?? null, ...maybeSequence, }, ]; } case "item.updated": { if (!isToolLifecycleItemType(event.payload.itemType)) { return []; } return [ { id: event.eventId, createdAt: event.createdAt, tone: "tool", kind: "tool.updated", summary: event.payload.title ?? "Tool updated", payload: { itemType: event.payload.itemType, ...(event.payload.status ? { status: event.payload.status } : {}), ...(event.payload.detail ? { detail: truncateDetail(event.payload.detail) } : {}), ...(event.payload.data !== undefined ? { data: event.payload.data } : {}), }, turnId: toTurnId(event.turnId) ?? null, ...maybeSequence, }, ]; } case "item.completed": { if (!isToolLifecycleItemType(event.payload.itemType)) { return []; } return [ { id: event.eventId, createdAt: event.createdAt, tone: "tool", kind: "tool.completed", summary: event.payload.title ?? "Tool", payload: { itemType: event.payload.itemType, ...(event.payload.detail ? { detail: truncateDetail(event.payload.detail) } : {}), ...(event.payload.data !== undefined ? { data: event.payload.data } : {}), }, turnId: toTurnId(event.turnId) ?? null, ...maybeSequence, }, ]; } case "item.started": { if (!isToolLifecycleItemType(event.payload.itemType)) { return []; } return [ { id: event.eventId, createdAt: event.createdAt, tone: "tool", kind: "tool.started", summary: `${event.payload.title ?? "Tool"} started`, payload: { itemType: event.payload.itemType, ...(event.payload.detail ? { detail: truncateDetail(event.payload.detail) } : {}), }, turnId: toTurnId(event.turnId) ?? null, ...maybeSequence, }, ]; } default: break; } return []; } const make = Effect.gen(function* () { const orchestrationEngine = yield* OrchestrationEngineService; const projectionSnapshotQuery = yield* ProjectionSnapshotQuery; const providerService = yield* ProviderService; const projectionTurnRepository = yield* ProjectionTurnRepository; const serverSettingsService = yield* ServerSettingsService; const turnMessageIdsByTurnKey = yield* Cache.make>({ capacity: TURN_MESSAGE_IDS_BY_TURN_CACHE_CAPACITY, timeToLive: TURN_MESSAGE_IDS_BY_TURN_TTL, lookup: () => Effect.succeed(new Set()), }); const bufferedAssistantTextByMessageId = yield* Cache.make({ capacity: BUFFERED_MESSAGE_TEXT_BY_MESSAGE_ID_CACHE_CAPACITY, timeToLive: BUFFERED_MESSAGE_TEXT_BY_MESSAGE_ID_TTL, lookup: () => Effect.succeed(""), }); const assistantSegmentStateByTurnKey = yield* Cache.make({ capacity: TURN_MESSAGE_IDS_BY_TURN_CACHE_CAPACITY, timeToLive: TURN_MESSAGE_IDS_BY_TURN_TTL, lookup: () => Effect.die( new Error("assistant segment state should be read through getOption before initialization"), ), }); const bufferedProposedPlanById = yield* Cache.make({ capacity: BUFFERED_PROPOSED_PLAN_BY_ID_CACHE_CAPACITY, timeToLive: BUFFERED_PROPOSED_PLAN_BY_ID_TTL, lookup: () => Effect.succeed({ text: "", createdAt: "" }), }); const resolveThreadDetail = Effect.fn("resolveThreadDetail")(function* (threadId: ThreadId) { return yield* projectionSnapshotQuery .getThreadDetailById(threadId) .pipe(Effect.map(Option.getOrUndefined)); }); const resolveThreadShell = Effect.fn("resolveThreadShell")(function* (threadId: ThreadId) { return yield* projectionSnapshotQuery .getThreadShellById(threadId) .pipe(Effect.map(Option.getOrUndefined)); }); const rememberAssistantMessageId = (threadId: ThreadId, turnId: TurnId, messageId: MessageId) => Cache.getOption(turnMessageIdsByTurnKey, providerTurnKey(threadId, turnId)).pipe( Effect.flatMap((existingIds) => Cache.set( turnMessageIdsByTurnKey, providerTurnKey(threadId, turnId), Option.match(existingIds, { onNone: () => new Set([messageId]), onSome: (ids) => { const nextIds = new Set(ids); nextIds.add(messageId); return nextIds; }, }), ), ), ); const forgetAssistantMessageId = (threadId: ThreadId, turnId: TurnId, messageId: MessageId) => Cache.getOption(turnMessageIdsByTurnKey, providerTurnKey(threadId, turnId)).pipe( Effect.flatMap((existingIds) => Option.match(existingIds, { onNone: () => Effect.void, onSome: (ids) => { const nextIds = new Set(ids); nextIds.delete(messageId); if (nextIds.size === 0) { return Cache.invalidate(turnMessageIdsByTurnKey, providerTurnKey(threadId, turnId)); } return Cache.set(turnMessageIdsByTurnKey, providerTurnKey(threadId, turnId), nextIds); }, }), ), ); const getAssistantMessageIdsForTurn = (threadId: ThreadId, turnId: TurnId) => Cache.getOption(turnMessageIdsByTurnKey, providerTurnKey(threadId, turnId)).pipe( Effect.map((existingIds) => Option.getOrElse(existingIds, (): Set => new Set()), ), ); const clearAssistantMessageIdsForTurn = (threadId: ThreadId, turnId: TurnId) => Cache.invalidate(turnMessageIdsByTurnKey, providerTurnKey(threadId, turnId)); const getAssistantSegmentStateForTurn = (threadId: ThreadId, turnId: TurnId) => Cache.getOption(assistantSegmentStateByTurnKey, providerTurnKey(threadId, turnId)); const setAssistantSegmentStateForTurn = ( threadId: ThreadId, turnId: TurnId, state: AssistantSegmentState, ) => Cache.set(assistantSegmentStateByTurnKey, providerTurnKey(threadId, turnId), state); const clearAssistantSegmentStateForTurn = (threadId: ThreadId, turnId: TurnId) => Cache.invalidate(assistantSegmentStateByTurnKey, providerTurnKey(threadId, turnId)); const getActiveAssistantMessageIdForTurn = (threadId: ThreadId, turnId: TurnId) => getAssistantSegmentStateForTurn(threadId, turnId).pipe( Effect.map((state) => Option.flatMap(state, (entry) => entry.activeMessageId ? Option.some(entry.activeMessageId) : Option.none(), ), ), ); const startAssistantSegmentForTurn = (input: { threadId: ThreadId; turnId: TurnId; baseKey: string; }) => getAssistantSegmentStateForTurn(input.threadId, input.turnId).pipe( Effect.flatMap((existingState) => Effect.gen(function* () { const nextState = Option.match(existingState, { onNone: () => ({ baseKey: input.baseKey, nextSegmentIndex: 1, activeMessageId: assistantSegmentMessageId(input.baseKey, 0), }), onSome: (state) => { const segmentIndex = state.baseKey === input.baseKey ? state.nextSegmentIndex : 0; const messageId = assistantSegmentMessageId(input.baseKey, segmentIndex); return { baseKey: input.baseKey, nextSegmentIndex: state.baseKey === input.baseKey ? state.nextSegmentIndex + 1 : 1, activeMessageId: messageId, } satisfies AssistantSegmentState; }, }); yield* setAssistantSegmentStateForTurn(input.threadId, input.turnId, nextState); return nextState.activeMessageId!; }), ), ); const getOrCreateAssistantMessageId = (input: { threadId: ThreadId; event: ProviderRuntimeEvent; turnId?: TurnId; }) => Effect.gen(function* () { if (!input.turnId) { return assistantSegmentMessageId(assistantSegmentBaseKeyFromEvent(input.event), 0); } const activeMessageId = yield* getActiveAssistantMessageIdForTurn( input.threadId, input.turnId, ); if (Option.isSome(activeMessageId)) { return activeMessageId.value; } return yield* startAssistantSegmentForTurn({ threadId: input.threadId, turnId: input.turnId, baseKey: assistantSegmentBaseKeyFromEvent(input.event), }); }); const appendBufferedAssistantText = (messageId: MessageId, delta: string) => Cache.getOption(bufferedAssistantTextByMessageId, messageId).pipe( Effect.flatMap((existingText) => Effect.gen(function* () { const nextText = Option.match(existingText, { onNone: () => delta, onSome: (text) => `${text}${delta}`, }); if (nextText.length <= MAX_BUFFERED_ASSISTANT_CHARS) { yield* Cache.set(bufferedAssistantTextByMessageId, messageId, nextText); return ""; } // Safety valve: flush full buffered text as an assistant delta to cap memory. yield* Cache.invalidate(bufferedAssistantTextByMessageId, messageId); return nextText; }), ), ); const takeBufferedAssistantText = (messageId: MessageId) => Cache.getOption(bufferedAssistantTextByMessageId, messageId).pipe( Effect.flatMap((existingText) => Cache.invalidate(bufferedAssistantTextByMessageId, messageId).pipe( Effect.as(Option.getOrElse(existingText, () => "")), ), ), ); const clearBufferedAssistantText = (messageId: MessageId) => Cache.invalidate(bufferedAssistantTextByMessageId, messageId); const appendBufferedProposedPlan = (planId: string, delta: string, createdAt: string) => Cache.getOption(bufferedProposedPlanById, planId).pipe( Effect.flatMap((existingEntry) => { const existing = Option.getOrUndefined(existingEntry); return Cache.set(bufferedProposedPlanById, planId, { text: `${existing?.text ?? ""}${delta}`, createdAt: existing?.createdAt && existing.createdAt.length > 0 ? existing.createdAt : createdAt, }); }), ); const takeBufferedProposedPlan = (planId: string) => Cache.getOption(bufferedProposedPlanById, planId).pipe( Effect.flatMap((existingEntry) => Cache.invalidate(bufferedProposedPlanById, planId).pipe( Effect.as(Option.getOrUndefined(existingEntry)), ), ), ); const clearBufferedProposedPlan = (planId: string) => Cache.invalidate(bufferedProposedPlanById, planId); const clearAssistantMessageState = (messageId: MessageId) => clearBufferedAssistantText(messageId); const flushBufferedAssistantMessage = (input: { event: ProviderRuntimeEvent; threadId: ThreadId; messageId: MessageId; turnId?: TurnId; createdAt: string; commandTag: string; }) => Effect.gen(function* () { const bufferedText = yield* takeBufferedAssistantText(input.messageId); if (!hasRenderableAssistantText(bufferedText)) { return false; } yield* orchestrationEngine.dispatch({ type: "thread.message.assistant.delta", commandId: providerCommandId(input.event, input.commandTag), threadId: input.threadId, messageId: input.messageId, delta: bufferedText, ...(input.turnId ? { turnId: input.turnId } : {}), createdAt: input.createdAt, }); return true; }); const flushBufferedAssistantMessagesForTurn = (input: { event: ProviderRuntimeEvent; threadId: ThreadId; turnId: TurnId; createdAt: string; commandTag: string; }) => Effect.gen(function* () { const assistantMessageIds = yield* getAssistantMessageIdsForTurn( input.threadId, input.turnId, ); const flushedMessageIds = new Set(); yield* Effect.forEach( assistantMessageIds, (messageId) => flushBufferedAssistantMessage({ event: input.event, threadId: input.threadId, messageId, turnId: input.turnId, createdAt: input.createdAt, commandTag: input.commandTag, }).pipe( Effect.tap((flushed) => flushed ? Effect.sync(() => flushedMessageIds.add(messageId)) : Effect.void, ), ), { concurrency: 1 }, ).pipe(Effect.asVoid); return flushedMessageIds; }); const finalizeAssistantMessage = (input: { event: ProviderRuntimeEvent; threadId: ThreadId; messageId: MessageId; turnId?: TurnId; createdAt: string; commandTag: string; finalDeltaCommandTag: string; fallbackText?: string; hasProjectedMessage?: boolean; }) => Effect.gen(function* () { const bufferedText = yield* takeBufferedAssistantText(input.messageId); const text = bufferedText.length > 0 ? bufferedText : (input.fallbackText?.trim().length ?? 0) > 0 ? input.fallbackText! : ""; const hasRenderableText = hasRenderableAssistantText(text); if (hasRenderableText) { yield* orchestrationEngine.dispatch({ type: "thread.message.assistant.delta", commandId: providerCommandId(input.event, input.finalDeltaCommandTag), threadId: input.threadId, messageId: input.messageId, delta: text, ...(input.turnId ? { turnId: input.turnId } : {}), createdAt: input.createdAt, }); } if (input.hasProjectedMessage || hasRenderableText) { yield* orchestrationEngine.dispatch({ type: "thread.message.assistant.complete", commandId: providerCommandId(input.event, input.commandTag), threadId: input.threadId, messageId: input.messageId, ...(input.turnId ? { turnId: input.turnId } : {}), createdAt: input.createdAt, }); } yield* clearAssistantMessageState(input.messageId); }); const finalizeActiveAssistantSegmentForTurn = (input: { event: ProviderRuntimeEvent; threadId: ThreadId; turnId: TurnId; createdAt: string; commandTag: string; finalDeltaCommandTag: string; hasProjectedMessage: boolean; flushedMessageIds?: ReadonlySet; }) => Effect.gen(function* () { const activeMessageId = yield* getActiveAssistantMessageIdForTurn( input.threadId, input.turnId, ); if (Option.isNone(activeMessageId)) { return; } yield* finalizeAssistantMessage({ event: input.event, threadId: input.threadId, messageId: activeMessageId.value, turnId: input.turnId, createdAt: input.createdAt, commandTag: input.commandTag, finalDeltaCommandTag: input.finalDeltaCommandTag, hasProjectedMessage: input.hasProjectedMessage || (input.flushedMessageIds?.has(activeMessageId.value) ?? false), }); yield* forgetAssistantMessageId(input.threadId, input.turnId, activeMessageId.value); const state = yield* getAssistantSegmentStateForTurn(input.threadId, input.turnId); if (Option.isSome(state)) { yield* setAssistantSegmentStateForTurn(input.threadId, input.turnId, { ...state.value, activeMessageId: null, }); } }); const upsertProposedPlan = (input: { event: ProviderRuntimeEvent; threadId: ThreadId; threadProposedPlans: ReadonlyArray<{ id: string; createdAt: string; implementedAt: string | null; implementationThreadId: ThreadId | null; }>; planId: string; turnId?: TurnId; planMarkdown: string | undefined; createdAt: string; updatedAt: string; }) => Effect.gen(function* () { const planMarkdown = normalizeProposedPlanMarkdown(input.planMarkdown); if (!planMarkdown) { return; } const existingPlan = findProposedPlanById(input.threadProposedPlans, input.planId); yield* orchestrationEngine.dispatch({ type: "thread.proposed-plan.upsert", commandId: providerCommandId(input.event, "proposed-plan-upsert"), threadId: input.threadId, proposedPlan: { id: input.planId, turnId: input.turnId ?? null, planMarkdown, implementedAt: existingPlan?.implementedAt ?? null, implementationThreadId: existingPlan?.implementationThreadId ?? null, createdAt: existingPlan?.createdAt ?? input.createdAt, updatedAt: input.updatedAt, }, createdAt: input.updatedAt, }); }); const finalizeBufferedProposedPlan = (input: { event: ProviderRuntimeEvent; threadId: ThreadId; threadProposedPlans: ReadonlyArray<{ id: string; createdAt: string; implementedAt: string | null; implementationThreadId: ThreadId | null; }>; planId: string; turnId?: TurnId; fallbackMarkdown?: string; updatedAt: string; }) => Effect.gen(function* () { const bufferedPlan = yield* takeBufferedProposedPlan(input.planId); const bufferedMarkdown = normalizeProposedPlanMarkdown(bufferedPlan?.text); const fallbackMarkdown = normalizeProposedPlanMarkdown(input.fallbackMarkdown); const planMarkdown = bufferedMarkdown ?? fallbackMarkdown; if (!planMarkdown) { return; } yield* upsertProposedPlan({ event: input.event, threadId: input.threadId, threadProposedPlans: input.threadProposedPlans, planId: input.planId, ...(input.turnId ? { turnId: input.turnId } : {}), planMarkdown, createdAt: bufferedPlan?.createdAt && bufferedPlan.createdAt.length > 0 ? bufferedPlan.createdAt : input.updatedAt, updatedAt: input.updatedAt, }); yield* clearBufferedProposedPlan(input.planId); }); const clearTurnStateForSession = (threadId: ThreadId) => Effect.gen(function* () { const prefix = `${threadId}:`; const proposedPlanPrefix = `plan:${threadId}:`; const turnKeys = Array.from(yield* Cache.keys(turnMessageIdsByTurnKey)); const assistantSegmentKeys = Array.from(yield* Cache.keys(assistantSegmentStateByTurnKey)); const proposedPlanKeys = Array.from(yield* Cache.keys(bufferedProposedPlanById)); yield* Effect.forEach( turnKeys, (key) => Effect.gen(function* () { if (!key.startsWith(prefix)) { return; } const messageIds = yield* Cache.getOption(turnMessageIdsByTurnKey, key); if (Option.isSome(messageIds)) { yield* Effect.forEach(messageIds.value, clearAssistantMessageState, { concurrency: 1, }).pipe(Effect.asVoid); } yield* Cache.invalidate(turnMessageIdsByTurnKey, key); }), { concurrency: 1 }, ).pipe(Effect.asVoid); yield* Effect.forEach( assistantSegmentKeys, (key) => key.startsWith(prefix) ? Cache.invalidate(assistantSegmentStateByTurnKey, key) : Effect.void, { concurrency: 1 }, ).pipe(Effect.asVoid); yield* Effect.forEach( proposedPlanKeys, (key) => key.startsWith(proposedPlanPrefix) ? Cache.invalidate(bufferedProposedPlanById, key) : Effect.void, { concurrency: 1 }, ).pipe(Effect.asVoid); }); const getSourceProposedPlanReferenceForPendingTurnStart = Effect.fn( "getSourceProposedPlanReferenceForPendingTurnStart", )(function* (threadId: ThreadId) { const pendingTurnStart = yield* projectionTurnRepository.getPendingTurnStartByThreadId({ threadId, }); if (Option.isNone(pendingTurnStart)) { return null; } const sourceThreadId = pendingTurnStart.value.sourceProposedPlanThreadId; const sourcePlanId = pendingTurnStart.value.sourceProposedPlanId; if (sourceThreadId === null || sourcePlanId === null) { return null; } return { sourceThreadId, sourcePlanId, } as const; }); const getExpectedProviderTurnIdForThread = Effect.fn("getExpectedProviderTurnIdForThread")( function* (threadId: ThreadId) { const sessions = yield* providerService.listSessions(); const session = sessions.find((entry) => entry.threadId === threadId); return session?.activeTurnId; }, ); const getSourceProposedPlanReferenceForAcceptedTurnStart = Effect.fn( "getSourceProposedPlanReferenceForAcceptedTurnStart", )(function* (threadId: ThreadId, eventTurnId: TurnId | undefined) { if (eventTurnId === undefined) { return null; } const expectedTurnId = yield* getExpectedProviderTurnIdForThread(threadId); if (!sameId(expectedTurnId, eventTurnId)) { return null; } return yield* getSourceProposedPlanReferenceForPendingTurnStart(threadId); }); const markSourceProposedPlanImplemented = Effect.fn("markSourceProposedPlanImplemented")( function* ( sourceThreadId: ThreadId, sourcePlanId: OrchestrationProposedPlanId, implementationThreadId: ThreadId, implementedAt: string, ) { const sourceThread = yield* resolveThreadDetail(sourceThreadId); const sourcePlan = sourceThread?.proposedPlans.find((entry) => entry.id === sourcePlanId); if (!sourceThread || !sourcePlan || sourcePlan.implementedAt !== null) { return; } yield* orchestrationEngine.dispatch({ type: "thread.proposed-plan.upsert", commandId: CommandId.make( `provider:source-proposed-plan-implemented:${implementationThreadId}:${crypto.randomUUID()}`, ), threadId: sourceThread.id, proposedPlan: { ...sourcePlan, implementedAt, implementationThreadId, updatedAt: implementedAt, }, createdAt: implementedAt, }); }, ); const processRuntimeEvent = (event: ProviderRuntimeEvent) => Effect.gen(function* () { const thread = yield* resolveThreadShell(event.threadId); if (!thread) return; let loadedThreadDetail: OrchestrationThread | null | undefined; const getLoadedThreadDetail = () => Effect.gen(function* () { if (loadedThreadDetail !== undefined) { return loadedThreadDetail; } loadedThreadDetail = (yield* resolveThreadDetail(thread.id)) ?? null; return loadedThreadDetail; }); const now = event.createdAt; const eventTurnId = toTurnId(event.turnId); const activeTurnId = thread.session?.activeTurnId ?? null; const conflictsWithActiveTurn = activeTurnId !== null && eventTurnId !== undefined && !sameId(activeTurnId, eventTurnId); const missingTurnForActiveTurn = activeTurnId !== null && eventTurnId === undefined; const shouldApplyThreadLifecycle = (() => { if (!STRICT_PROVIDER_LIFECYCLE_GUARD) { return true; } switch (event.type) { case "session.exited": return true; case "session.started": case "thread.started": return true; case "turn.started": return !conflictsWithActiveTurn; case "turn.completed": if (conflictsWithActiveTurn || missingTurnForActiveTurn) { return false; } // Only the active turn may close the lifecycle state. if (activeTurnId !== null && eventTurnId !== undefined) { return sameId(activeTurnId, eventTurnId); } // If no active turn is tracked, accept completion scoped to this thread. return true; default: return true; } })(); const acceptedTurnStartedSourcePlan = event.type === "turn.started" && shouldApplyThreadLifecycle ? yield* getSourceProposedPlanReferenceForAcceptedTurnStart(thread.id, eventTurnId) : null; if ( event.type === "session.started" || event.type === "session.state.changed" || event.type === "session.exited" || event.type === "thread.started" || event.type === "turn.started" || event.type === "turn.completed" ) { const nextActiveTurnId = event.type === "turn.started" ? (eventTurnId ?? null) : event.type === "turn.completed" || event.type === "session.exited" ? null : activeTurnId; const status = (() => { switch (event.type) { case "session.state.changed": return orchestrationSessionStatusFromRuntimeState(event.payload.state); case "turn.started": return "running"; case "session.exited": return "stopped"; case "turn.completed": return normalizeRuntimeTurnState(event.payload.state) === "failed" ? "error" : "ready"; case "session.started": case "thread.started": // Provider thread/session start notifications can arrive during an // active turn; preserve turn-running state in that case. return activeTurnId !== null ? "running" : "ready"; } })(); const lastError = event.type === "session.state.changed" && event.payload.state === "error" ? (event.payload.reason ?? thread.session?.lastError ?? "Provider session error") : event.type === "turn.completed" && normalizeRuntimeTurnState(event.payload.state) === "failed" ? (event.payload.errorMessage ?? thread.session?.lastError ?? "Turn failed") : status === "ready" ? null : (thread.session?.lastError ?? null); if (shouldApplyThreadLifecycle) { if (event.type === "turn.started" && acceptedTurnStartedSourcePlan !== null) { yield* markSourceProposedPlanImplemented( acceptedTurnStartedSourcePlan.sourceThreadId, acceptedTurnStartedSourcePlan.sourcePlanId, thread.id, now, ).pipe( Effect.catchCause((cause) => Effect.logWarning( "provider runtime ingestion failed to mark source proposed plan", { eventId: event.eventId, eventType: event.type, cause: Cause.pretty(cause), }, ), ), ); } yield* orchestrationEngine.dispatch({ type: "thread.session.set", commandId: providerCommandId(event, "thread-session-set"), threadId: thread.id, session: { threadId: thread.id, status, providerName: event.provider, ...(event.providerInstanceId !== undefined ? { providerInstanceId: event.providerInstanceId } : {}), runtimeMode: thread.session?.runtimeMode ?? "full-access", activeTurnId: nextActiveTurnId, lastError, updatedAt: now, }, createdAt: now, }); } } const assistantDelta = event.type === "content.delta" && event.payload.streamKind === "assistant_text" ? event.payload.delta : undefined; const proposedPlanDelta = event.type === "turn.proposed.delta" ? event.payload.delta : undefined; if (assistantDelta && assistantDelta.length > 0) { const turnId = toTurnId(event.turnId); const assistantMessageId = yield* getOrCreateAssistantMessageId({ threadId: thread.id, event, ...(turnId ? { turnId } : {}), }); if (turnId) { yield* rememberAssistantMessageId(thread.id, turnId, assistantMessageId); } const assistantDeliveryMode: AssistantDeliveryMode = yield* Effect.map( serverSettingsService.getSettings, (settings) => (settings.enableAssistantStreaming ? "streaming" : "buffered"), ); if (assistantDeliveryMode === "buffered") { const spillChunk = yield* appendBufferedAssistantText(assistantMessageId, assistantDelta); if (spillChunk.length > 0) { yield* orchestrationEngine.dispatch({ type: "thread.message.assistant.delta", commandId: providerCommandId(event, "assistant-delta-buffer-spill"), threadId: thread.id, messageId: assistantMessageId, delta: spillChunk, ...(turnId ? { turnId } : {}), createdAt: now, }); } } else { yield* orchestrationEngine.dispatch({ type: "thread.message.assistant.delta", commandId: providerCommandId(event, "assistant-delta"), threadId: thread.id, messageId: assistantMessageId, delta: assistantDelta, ...(turnId ? { turnId } : {}), createdAt: now, }); } } const pauseForUserTurnId = event.type === "request.opened" || event.type === "user-input.requested" ? toTurnId(event.turnId) : undefined; if (pauseForUserTurnId) { const detailedThread = yield* getLoadedThreadDetail(); const assistantDeliveryMode: AssistantDeliveryMode = yield* Effect.map( serverSettingsService.getSettings, (settings) => (settings.enableAssistantStreaming ? "streaming" : "buffered"), ); const flushedMessageIds = assistantDeliveryMode === "buffered" ? yield* flushBufferedAssistantMessagesForTurn({ event, threadId: thread.id, turnId: pauseForUserTurnId, createdAt: now, commandTag: event.type === "request.opened" ? "assistant-delta-flush-on-request-opened" : "assistant-delta-flush-on-user-input-requested", }) : new Set(); yield* finalizeActiveAssistantSegmentForTurn({ event, threadId: thread.id, turnId: pauseForUserTurnId, createdAt: now, commandTag: event.type === "request.opened" ? "assistant-complete-on-request-opened" : "assistant-complete-on-user-input-requested", finalDeltaCommandTag: event.type === "request.opened" ? "assistant-delta-finalize-on-request-opened" : "assistant-delta-finalize-on-user-input-requested", hasProjectedMessage: detailedThread !== null && hasAssistantMessageForTurn(detailedThread.messages, pauseForUserTurnId, { streamingOnly: true, }), flushedMessageIds, }); } if (proposedPlanDelta && proposedPlanDelta.length > 0) { const planId = proposedPlanIdFromEvent(event, thread.id); yield* appendBufferedProposedPlan(planId, proposedPlanDelta, now); } const assistantCompletion = event.type === "item.completed" && event.payload.itemType === "assistant_message" ? { messageId: MessageId.make( `assistant:${event.itemId ?? event.turnId ?? event.eventId}`, ), fallbackText: event.payload.detail, } : undefined; const proposedPlanCompletion = event.type === "turn.proposed.completed" ? { planId: proposedPlanIdFromEvent(event, thread.id), turnId: toTurnId(event.turnId), planMarkdown: event.payload.planMarkdown, } : undefined; if (assistantCompletion) { const detailedThread = yield* getLoadedThreadDetail(); const messages = detailedThread?.messages ?? []; const turnId = toTurnId(event.turnId); const activeAssistantMessageId = turnId ? yield* getActiveAssistantMessageIdForTurn(thread.id, turnId) : Option.none(); const hasAssistantMessagesForTurn = turnId !== undefined ? hasAssistantMessageForTurn(messages, turnId) : false; const assistantMessageId = Option.getOrElse( activeAssistantMessageId, () => assistantCompletion.messageId, ); const existingAssistantMessage = findMessageById(messages, assistantMessageId); const shouldApplyFallbackCompletionText = !existingAssistantMessage || existingAssistantMessage.text.length === 0; const shouldSkipRedundantCompletion = Option.isNone(activeAssistantMessageId) && turnId !== undefined && hasAssistantMessagesForTurn && (assistantCompletion.fallbackText?.trim().length ?? 0) === 0; if (!shouldSkipRedundantCompletion) { if (turnId && Option.isNone(activeAssistantMessageId)) { yield* rememberAssistantMessageId(thread.id, turnId, assistantMessageId); } yield* finalizeAssistantMessage({ event, threadId: thread.id, messageId: assistantMessageId, ...(turnId ? { turnId } : {}), createdAt: now, commandTag: "assistant-complete", finalDeltaCommandTag: "assistant-delta-finalize", hasProjectedMessage: existingAssistantMessage !== undefined, ...(assistantCompletion.fallbackText !== undefined && shouldApplyFallbackCompletionText ? { fallbackText: assistantCompletion.fallbackText } : {}), }); if (turnId) { yield* forgetAssistantMessageId(thread.id, turnId, assistantMessageId); } } if (turnId) { yield* clearAssistantSegmentStateForTurn(thread.id, turnId); } } if (proposedPlanCompletion) { const detailedThread = yield* getLoadedThreadDetail(); yield* finalizeBufferedProposedPlan({ event, threadId: thread.id, threadProposedPlans: detailedThread?.proposedPlans ?? [], planId: proposedPlanCompletion.planId, ...(proposedPlanCompletion.turnId ? { turnId: proposedPlanCompletion.turnId } : {}), fallbackMarkdown: proposedPlanCompletion.planMarkdown, updatedAt: now, }); } if (event.type === "turn.completed") { const detailedThread = yield* getLoadedThreadDetail(); const messages = detailedThread?.messages ?? []; const proposedPlans = detailedThread?.proposedPlans ?? []; const turnId = toTurnId(event.turnId); if (turnId) { const assistantMessageIds = yield* getAssistantMessageIdsForTurn(thread.id, turnId); yield* Effect.forEach( assistantMessageIds, (assistantMessageId) => finalizeAssistantMessage({ event, threadId: thread.id, messageId: assistantMessageId, turnId, createdAt: now, commandTag: "assistant-complete-finalize", finalDeltaCommandTag: "assistant-delta-finalize-fallback", hasProjectedMessage: findMessageById(messages, assistantMessageId) !== undefined, }), { concurrency: 1 }, ).pipe(Effect.asVoid); yield* clearAssistantMessageIdsForTurn(thread.id, turnId); yield* clearAssistantSegmentStateForTurn(thread.id, turnId); yield* finalizeBufferedProposedPlan({ event, threadId: thread.id, threadProposedPlans: proposedPlans, planId: proposedPlanIdForTurn(thread.id, turnId), turnId, updatedAt: now, }); } } if (event.type === "session.exited") { yield* clearTurnStateForSession(thread.id); } if (event.type === "runtime.error") { const runtimeErrorMessage = event.payload.message; const shouldApplyRuntimeError = !STRICT_PROVIDER_LIFECYCLE_GUARD ? true : activeTurnId === null || eventTurnId === undefined || sameId(activeTurnId, eventTurnId); if (shouldApplyRuntimeError) { yield* orchestrationEngine.dispatch({ type: "thread.session.set", commandId: providerCommandId(event, "runtime-error-session-set"), threadId: thread.id, session: { threadId: thread.id, status: "error", providerName: event.provider, ...(event.providerInstanceId !== undefined ? { providerInstanceId: event.providerInstanceId } : {}), runtimeMode: thread.session?.runtimeMode ?? "full-access", activeTurnId: eventTurnId ?? null, lastError: runtimeErrorMessage, updatedAt: now, }, createdAt: now, }); } } if (event.type === "thread.metadata.updated" && event.payload.name) { yield* orchestrationEngine.dispatch({ type: "thread.meta.update", commandId: providerCommandId(event, "thread-meta-update"), threadId: thread.id, title: event.payload.name, }); } if (event.type === "turn.diff.updated") { const turnId = toTurnId(event.turnId); const checkpointContext = turnId ? yield* projectionSnapshotQuery .getThreadCheckpointContext(thread.id) .pipe(Effect.map(Option.getOrUndefined)) : undefined; const workspaceCwd = checkpointContext?.worktreePath ?? checkpointContext?.workspaceRoot ?? undefined; if (turnId && checkpointContext && workspaceCwd && isGitRepository(workspaceCwd)) { // Skip if a checkpoint already exists for this turn. A real // (non-placeholder) capture from CheckpointReactor should not // be clobbered, and dispatching a duplicate placeholder for the // same turnId would produce an unstable checkpointTurnCount. if (hasCheckpointForTurn(checkpointContext.checkpoints, turnId)) { // Already tracked; no-op. } else { const assistantMessageId = MessageId.make( `assistant:${event.itemId ?? event.turnId ?? event.eventId}`, ); yield* orchestrationEngine.dispatch({ type: "thread.turn.diff.complete", commandId: providerCommandId(event, "thread-turn-diff-complete"), threadId: thread.id, turnId, completedAt: now, checkpointRef: CheckpointRef.make(`provider-diff:${event.eventId}`), status: "missing", files: [], assistantMessageId, checkpointTurnCount: maxCheckpointTurnCount(checkpointContext.checkpoints) + 1, createdAt: now, }); } } } const activities = runtimeEventToActivities(event); yield* Effect.forEach(activities, (activity) => orchestrationEngine.dispatch({ type: "thread.activity.append", commandId: providerCommandId(event, "thread-activity-append"), threadId: thread.id, activity, createdAt: activity.createdAt, }), ).pipe(Effect.asVoid); }); const processDomainEvent = (_event: TurnStartRequestedDomainEvent) => Effect.void; const processInput = (input: RuntimeIngestionInput) => input.source === "runtime" ? processRuntimeEvent(input.event) : processDomainEvent(input.event); const processInputSafely = (input: RuntimeIngestionInput) => processInput(input).pipe( Effect.catchCause((cause) => { if (Cause.hasInterruptsOnly(cause)) { return Effect.failCause(cause); } return Effect.logWarning("provider runtime ingestion failed to process event", { source: input.source, eventId: input.event.eventId, eventType: input.event.type, cause: Cause.pretty(cause), }); }), ); const worker = yield* makeDrainableWorker(processInputSafely); const start: ProviderRuntimeIngestionShape["start"] = () => Effect.gen(function* () { yield* Effect.forkScoped( Stream.runForEach(providerService.streamEvents, (event) => worker.enqueue({ source: "runtime", event }), ), ); yield* Effect.forkScoped( Stream.runForEach(orchestrationEngine.streamDomainEvents, (event) => { if (event.type !== "thread.turn-start-requested") { return Effect.void; } return worker.enqueue({ source: "domain", event }); }), ); }); return { start, drain: worker.drain, } satisfies ProviderRuntimeIngestionShape; }); export const ProviderRuntimeIngestionLive = Layer.effect( ProviderRuntimeIngestionService, make, ).pipe(Layer.provide(ProjectionTurnRepositoryLive));