import { CommandId, MessageId, ThreadId, type ModelSelection, type ProjectId, type ProviderInteractionMode, type RuntimeMode, } from "@t3tools/contracts"; import { assistantCitationsToPlainText } from "@t3tools/shared/assistantCitations"; import type { UploadedMobileAttachment } from "./attachmentUpload"; export function deriveThreadTitleFromPrompt(value: string): string { const trimmed = assistantCitationsToPlainText(value).trim(); if (trimmed.length === 0) { return "New thread"; } const compact = trimmed.replace(/\s+/g, " "); return compact.length <= 72 ? compact : `${compact.slice(0, 69).trimEnd()}...`; } export interface ProjectThreadStartTurnSpec { readonly projectId: ProjectId; readonly projectCwd: string; readonly threadId: string; readonly commandId: string; readonly messageId: string; readonly createdAt: string; readonly text: string; /** Wire attachments from `prepareTurnAttachments`, in composer order. */ readonly uploadedAttachments: ReadonlyArray; readonly modelSelection: ModelSelection; readonly runtimeMode: RuntimeMode; readonly interactionMode: ProviderInteractionMode; readonly workspaceMode: "local" | "worktree"; readonly branch: string | null; readonly worktreePath: string | null; readonly startFromOrigin: boolean; /** Generated temp branch for worktree mode; unused for local mode. */ readonly worktreeBranchName: string; } /** * Single source of the `thread.turn.start` bootstrap payload used to create a * thread from a project draft — shared by the immediate send path and the * offline outbox drain so both deliver identical commands. */ export function buildProjectThreadStartTurnInput(spec: ProjectThreadStartTurnSpec) { const title = deriveThreadTitleFromPrompt(spec.text); const isWorktree = spec.workspaceMode === "worktree"; return { commandId: CommandId.make(spec.commandId), threadId: ThreadId.make(spec.threadId), message: { messageId: MessageId.make(spec.messageId), role: "user" as const, text: spec.text, attachments: spec.uploadedAttachments, }, modelSelection: spec.modelSelection, titleSeed: title, runtimeMode: spec.runtimeMode, interactionMode: spec.interactionMode, bootstrap: { createThread: { projectId: spec.projectId, title, modelSelection: spec.modelSelection, runtimeMode: spec.runtimeMode, interactionMode: spec.interactionMode, branch: spec.branch, worktreePath: isWorktree ? null : spec.worktreePath, createdAt: spec.createdAt, }, ...(isWorktree ? { prepareWorktree: { projectCwd: spec.projectCwd, baseBranch: spec.branch!, branch: spec.worktreeBranchName, ...(spec.startFromOrigin ? { startFromOrigin: true } : {}), }, runSetupScript: true, } : {}), }, createdAt: spec.createdAt, }; }