91 lines · 3.0 KB · TypeScript
RawDownload
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<UploadedMobileAttachment>;  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,  };}