| 1 | import { imageMimeType } from "@t3tools/shared/image"; |
| 2 | import type { |
| 3 | ChatFileAttachment as ContractChatFileAttachment, |
| 4 | ChatImageAttachment as ContractChatImageAttachment, |
| 5 | ChatUnknownAttachment as ContractChatUnknownAttachment, |
| 6 | MessageId, |
| 7 | OrchestrationV2Actor, |
| 8 | OrchestrationV2CreationSource, |
| 9 | OrchestrationV2PlanArtifact, |
| 10 | OrchestrationV2UserMessageInputIntent, |
| 11 | PlanId, |
| 12 | ProjectScript as ContractProjectScript, |
| 13 | ProviderInteractionMode, |
| 14 | RunId, |
| 15 | RuntimeMode, |
| 16 | ScheduledTaskId, |
| 17 | ThreadId, |
| 18 | } from "@t3tools/contracts"; |
| 19 | import type { |
| 20 | EnvironmentProject, |
| 21 | EnvironmentThreadShell, |
| 22 | ThreadRunSummary, |
| 23 | ThreadRuntimeSummary, |
| 24 | } from "@t3tools/client-runtime/state/shell"; |
| 25 | import type { ThreadCheckpointSummary } from "@t3tools/client-runtime/state/thread-checkpoints"; |
| 26 | import { videoMimeType } from "@t3tools/shared/video"; |
| 27 | |
| 28 | export { videoMimeType } from "@t3tools/shared/video"; |
| 29 | |
| 30 | export type SessionPhase = "disconnected" | "connecting" | "ready" | "running"; |
| 31 | export const DEFAULT_RUNTIME_MODE: RuntimeMode = "full-access"; |
| 32 | |
| 33 | export const DEFAULT_INTERACTION_MODE: ProviderInteractionMode = "default"; |
| 34 | export const DEFAULT_THREAD_TERMINAL_HEIGHT = 280; |
| 35 | export const DEFAULT_THREAD_TERMINAL_ID = "term-1"; |
| 36 | export const MAX_TERMINALS_PER_GROUP = 4; |
| 37 | export type ProjectScript = ContractProjectScript; |
| 38 | |
| 39 | export interface ThreadTerminalGroup { |
| 40 | id: string; |
| 41 | terminalIds: string[]; |
| 42 | splitDirection?: "horizontal" | "vertical"; |
| 43 | } |
| 44 | |
| 45 | export interface ChatImageAttachment extends ContractChatImageAttachment { |
| 46 | readonly previewUrl?: string; |
| 47 | } |
| 48 | |
| 49 | export interface ChatFileAttachment extends ContractChatFileAttachment { |
| 50 | readonly previewUrl?: string; |
| 51 | readonly downloadable?: boolean; |
| 52 | /** A page an agent published with `html_render`; its viewer hands it the app theme. */ |
| 53 | readonly htmlRender?: boolean; |
| 54 | } |
| 55 | |
| 56 | // Attachment types this build does not know pass through with the contract |
| 57 | // shape. The UI renders them as inert rows so a newer server cannot crash an |
| 58 | // older client. |
| 59 | export type ChatUnknownAttachment = ContractChatUnknownAttachment; |
| 60 | |
| 61 | export type ChatAttachment = ChatImageAttachment | ChatFileAttachment | ChatUnknownAttachment; |
| 62 | |
| 63 | // The union has an open member (`type: string`), so a literal comparison does |
| 64 | // not narrow. Use these guards wherever type-specific fields are read. |
| 65 | export function isImageAttachment(attachment: ChatAttachment): attachment is ChatImageAttachment { |
| 66 | // Messages sent before pictures were typed by content carry `file`; they are still |
| 67 | // pictures, and reading them as such is what lets them render instead of listing. Only |
| 68 | // `file` is reclassified: an attachment type this client does not know yet is not a |
| 69 | // picture by default, whatever its name says. |
| 70 | if (attachment.type === "image") return true; |
| 71 | return attachment.type === "file" && imageMimeType(attachment) !== null; |
| 72 | } |
| 73 | |
| 74 | export function isFileAttachment(attachment: ChatAttachment): attachment is ChatFileAttachment { |
| 75 | // Disjoint from `isImageAttachment` on purpose: a legacy `file` carrying an image reads as a |
| 76 | // picture, and callers filter both sets independently, so overlap renders it twice. |
| 77 | return attachment.type === "file" && !isImageAttachment(attachment); |
| 78 | } |
| 79 | |
| 80 | export function isVideoAttachment(attachment: ChatFileAttachment): boolean { |
| 81 | return videoMimeType(attachment) !== null; |
| 82 | } |
| 83 | |
| 84 | export function isBrowserPreviewAttachment(attachment: ChatFileAttachment): boolean { |
| 85 | const mimeType = attachment.mimeType.split(";", 1)[0]?.trim().toLowerCase(); |
| 86 | return ( |
| 87 | /\.(?:html?|pdf)$/i.test(attachment.name) || |
| 88 | mimeType === "application/pdf" || |
| 89 | mimeType === "text/html" |
| 90 | ); |
| 91 | } |
| 92 | |
| 93 | export interface ChatMessage { |
| 94 | readonly context?: import("@t3tools/contracts").OrchestrationMessageContext | undefined; |
| 95 | readonly id: MessageId; |
| 96 | readonly role: "user" | "assistant" | "system"; |
| 97 | readonly text: string; |
| 98 | readonly attachments?: ReadonlyArray<ChatAttachment> | undefined; |
| 99 | readonly runId: RunId | null; |
| 100 | readonly streaming: boolean; |
| 101 | readonly createdBy?: OrchestrationV2Actor; |
| 102 | readonly creationSource?: OrchestrationV2CreationSource; |
| 103 | readonly scheduledTaskId?: ScheduledTaskId; |
| 104 | readonly senderThreadId?: ThreadId; |
| 105 | readonly createdAt: string; |
| 106 | readonly updatedAt: string; |
| 107 | readonly inputIntent?: OrchestrationV2UserMessageInputIntent | undefined; |
| 108 | } |
| 109 | |
| 110 | export interface ProposedPlan { |
| 111 | readonly id: PlanId; |
| 112 | readonly runId: RunId | null; |
| 113 | readonly planMarkdown: string; |
| 114 | readonly status: OrchestrationV2PlanArtifact["status"]; |
| 115 | readonly createdAt: string; |
| 116 | readonly updatedAt: string; |
| 117 | } |
| 118 | export type TurnDiffFileChange = ThreadCheckpointSummary["files"][number]; |
| 119 | export type TurnDiffSummary = ThreadCheckpointSummary; |
| 120 | |
| 121 | export type Project = EnvironmentProject; |
| 122 | export type Thread = EnvironmentThreadShell; |
| 123 | export type ThreadShell = EnvironmentThreadShell; |
| 124 | |
| 125 | export type SidebarThreadSummary = EnvironmentThreadShell; |
| 126 | export type ThreadSession = ThreadRuntimeSummary; |