import * as Effect from "effect/Effect";import * as Schema from "effect/Schema";import * as SchemaIssue from "effect/SchemaIssue";import * as SchemaTransformation from "effect/SchemaTransformation";import * as Struct from "effect/Struct";import { ProviderOptionSelections } from "./model.ts";import { RepositoryIdentity, ThreadEnvMode } from "./environment.ts";import { ApprovalRequestId, CheckpointRef, ClientSurface, CommandId, EventId, IsoDateTime, MessageId, NonNegativeInt, PositiveInt, ProjectId, ProviderItemId, ThreadId, TrimmedNonEmptyString, TrimmedString, TurnId,} from "./baseSchemas.ts";import { ProviderInstanceId } from "./providerInstance.ts";import { PullRequestActor, PullRequestChecksState, PullRequestMergeability, PullRequestReviewDecision, PullRequestState,} from "./pullRequest.ts"; export const ORCHESTRATION_WS_METHODS = { dispatchCommand: "orchestration.dispatchCommand", getWorkflowScript: "orchestration.getWorkflowScript", getTurnDiff: "orchestration.getTurnDiff", getFullThreadDiff: "orchestration.getFullThreadDiff", searchThreads: "orchestration.searchThreads", getArchivedShellSnapshot: "orchestration.getArchivedShellSnapshot", subscribeShell: "orchestration.subscribeShell", subscribeThread: "orchestration.subscribeThread",} as const; export const ProviderApprovalPolicy = Schema.Literals([ "untrusted", "on-failure", "on-request", "never",]);export type ProviderApprovalPolicy = typeof ProviderApprovalPolicy.Type;export const ProviderSandboxMode = Schema.Literals([ "read-only", "workspace-write", "danger-full-access",]);export type ProviderSandboxMode = typeof ProviderSandboxMode.Type; /** * `ModelSelection` — selection of a model on a configured provider instance. * * The routing key is `instanceId` (a user-defined slug identifying one * configured provider instance). Drivers, credentials, working-directory * bindings, and any other per-instance state are recovered from the * runtime registry via the instance id. * * Wire legacy: persisted selections produced before the driver/instance * split carried a `provider: <driver-id>` field instead. The schema absorbs * that shape via a pre-decoding transform — `{provider, model}` is promoted * to `{instanceId: defaultInstanceIdForDriver(provider), model}`. No * post-decode compatibility code lives in the runtime; the transform is the * only compat surface. */const ModelSelectionWire = Schema.Struct({ instanceId: ProviderInstanceId, model: TrimmedNonEmptyString, options: Schema.optionalKey(ProviderOptionSelections),}); // Source shape for persisted legacy payloads. Fields are typed as// `Schema.Unknown` so malformed drafts still make it into the transform and// fail validation through the target schema (with proper error messages)// rather than at the source-struct layer where the error is less actionable.const ModelSelectionSource = Schema.Struct({ provider: Schema.optional(Schema.Unknown), instanceId: Schema.optional(Schema.Unknown), model: Schema.Unknown, options: Schema.optional(Schema.Unknown),}); export const ModelSelection = ModelSelectionSource.pipe( Schema.decodeTo( ModelSelectionWire, SchemaTransformation.transformOrFail({ decode: (raw) => { // Resolve the routing key: prefer an explicit `instanceId`; fall // back to promoting the legacy `provider` slug (the canonical // `defaultInstanceIdForDriver` mapping) so persisted rollout-era // payloads decode without data loss. The target schema brands the // string as `ProviderInstanceId`. const instanceIdSource = raw.instanceId !== undefined ? raw.instanceId : typeof raw.provider === "string" ? raw.provider : undefined; const base: Record<string, unknown> = { instanceId: instanceIdSource, model: raw.model, }; if (raw.options !== undefined) base.options = raw.options; return Effect.succeed(base as typeof ModelSelectionWire.Encoded); }, encode: (value) => { const base: Record<string, unknown> = { model: value.model, instanceId: value.instanceId, }; if (value.options !== undefined) base.options = value.options; return Effect.succeed(base as typeof ModelSelectionSource.Encoded); }, }), ),);export type ModelSelection = typeof ModelSelection.Type; export const RuntimeMode = Schema.Literals([ "approval-required", "auto-accept-edits", "auto", "full-access",]);export type RuntimeMode = typeof RuntimeMode.Type;export const DEFAULT_RUNTIME_MODE: RuntimeMode = "full-access";export const ProviderInteractionMode = Schema.Literals(["default", "plan"]);export type ProviderInteractionMode = typeof ProviderInteractionMode.Type;export const DEFAULT_PROVIDER_INTERACTION_MODE: ProviderInteractionMode = "default";export const ProviderRequestKind = Schema.Literals([ "command", "file-read", "file-change", "mcp-elicitation",]);export type ProviderRequestKind = typeof ProviderRequestKind.Type;export const AssistantDeliveryMode = Schema.Literals(["buffered", "streaming"]);export type AssistantDeliveryMode = typeof AssistantDeliveryMode.Type;export const ProviderApprovalDecision = Schema.Literals([ "accept", "acceptForSession", "acceptAlways", "decline", "cancel",]);export type ProviderApprovalDecision = typeof ProviderApprovalDecision.Type;export const ProviderApprovalOption = Schema.Struct({ decision: ProviderApprovalDecision, label: TrimmedNonEmptyString, /** Provider-supplied caution shown next to the option, such as a prompt injection warning. */ warning: Schema.optional(TrimmedNonEmptyString),});export type ProviderApprovalOption = typeof ProviderApprovalOption.Type;export const ProviderUserInputAnswers = Schema.Record(Schema.String, Schema.Unknown);export type ProviderUserInputAnswers = typeof ProviderUserInputAnswers.Type; export const PROVIDER_SEND_TURN_MAX_INPUT_CHARS = 120_000;export const PROVIDER_SEND_TURN_MAX_ATTACHMENTS = 8;export const PROVIDER_SEND_TURN_MAX_IMAGE_BYTES = 10 * 1024 * 1024;export const PROVIDER_SEND_TURN_MAX_FILE_BYTES = 50 * 1024 * 1024;export const PROVIDER_SEND_TURN_SUPPORTED_IMAGE_MIME_TYPES = [ "image/gif", "image/jpeg", "image/png", "image/webp",] as const;const PROVIDER_SEND_TURN_SUPPORTED_IMAGE_MIME_TYPE_SET = new Set<string>( PROVIDER_SEND_TURN_SUPPORTED_IMAGE_MIME_TYPES,); /** Whether a pasted or picked image mime type can be sent on a provider turn. */export function isProviderSendTurnSupportedImageMimeType(mimeType: string): boolean { return PROVIDER_SEND_TURN_SUPPORTED_IMAGE_MIME_TYPE_SET.has(mimeType.toLowerCase());}const PROVIDER_SEND_TURN_MAX_IMAGE_DATA_URL_CHARS = 14_000_000;const CHAT_ATTACHMENT_ID_MAX_CHARS = 128;// Correlation id is command id by design in this model.export const CorrelationId = CommandId;export type CorrelationId = typeof CorrelationId.Type; const ChatAttachmentId = TrimmedNonEmptyString.check( Schema.isMaxLength(CHAT_ATTACHMENT_ID_MAX_CHARS), Schema.isPattern(/^[a-z0-9_-]+$/i),);export type ChatAttachmentId = typeof ChatAttachmentId.Type; export const SNAP_SHOT_ACCESSIBLE_TEXT_MAX_CHARS = 32_000;export const SNAP_SHOT_ACCESSIBILITY_MAX_NODES = 10_000;export const SNAP_SHOT_ACCESSIBILITY_MAX_SERIALIZED_CHARS = 32_000; const SnapShotAccessibilityBounds = Schema.Struct({ x: NonNegativeInt, y: NonNegativeInt, width: PositiveInt, height: PositiveInt,}); const SnapShotAccessibilityState = Schema.Struct({ active: Schema.optional(Schema.Boolean), busy: Schema.optional(Schema.Boolean), checked: Schema.optional(Schema.Literals(["on", "off", "mixed"])), editable: Schema.optional(Schema.Boolean), enabled: Schema.optional(Schema.Boolean), expanded: Schema.optional(Schema.Boolean), focused: Schema.optional(Schema.Boolean), selected: Schema.optional(Schema.Boolean), visible: Schema.optional(Schema.Boolean),}); export interface SnapShotAccessibilityNode { readonly role: string; readonly name?: string; readonly value?: string; readonly description?: string; readonly bounds: typeof SnapShotAccessibilityBounds.Type | null; readonly state?: typeof SnapShotAccessibilityState.Type; readonly actions?: Array<string>; readonly children: Array<SnapShotAccessibilityNode>;} export const SnapShotAccessibilityNode: Schema.Codec<SnapShotAccessibilityNode> = Schema.Struct({ role: TrimmedNonEmptyString.check(Schema.isMaxLength(100)), name: Schema.optionalKey(TrimmedNonEmptyString.check(Schema.isMaxLength(1_000))), value: Schema.optionalKey(TrimmedNonEmptyString.check(Schema.isMaxLength(8_000))), description: Schema.optionalKey(TrimmedNonEmptyString.check(Schema.isMaxLength(2_000))), bounds: Schema.NullOr(SnapShotAccessibilityBounds), state: Schema.optionalKey(SnapShotAccessibilityState), actions: Schema.optionalKey( Schema.mutable(Schema.Array(TrimmedNonEmptyString.check(Schema.isMaxLength(100)))).check( Schema.isMaxLength(32), ), ), children: Schema.mutable( Schema.Array( Schema.suspend((): Schema.Codec<SnapShotAccessibilityNode> => SnapShotAccessibilityNode), ), ).check(Schema.isMaxLength(SNAP_SHOT_ACCESSIBILITY_MAX_NODES)),}); const SnapShotAccessibilityWire = Schema.Union([ Schema.Struct({ format: Schema.Literal("flat-text"), text: TrimmedNonEmptyString.check(Schema.isMaxLength(SNAP_SHOT_ACCESSIBLE_TEXT_MAX_CHARS)), truncated: Schema.Boolean, }), Schema.Struct({ format: Schema.Literal("element-tree"), coordinateSpace: Schema.Literal("captured-image"), imageSize: Schema.Struct({ width: PositiveInt, height: PositiveInt }), truncated: Schema.Boolean, root: SnapShotAccessibilityNode, }),]);export const SnapShotAccessibility = SnapShotAccessibilityWire.check( Schema.makeFilter((accessibility: typeof SnapShotAccessibilityWire.Type) => { if (accessibility.format === "flat-text") return undefined; let nodes = 0; const stack = [accessibility.root]; while (stack.length > 0) { const node = stack.pop()!; nodes += 1; if (nodes > SNAP_SHOT_ACCESSIBILITY_MAX_NODES) { return `Accessibility trees must not exceed ${SNAP_SHOT_ACCESSIBILITY_MAX_NODES} nodes.`; } stack.push(...node.children); } return ( JSON.stringify(accessibility).length <= SNAP_SHOT_ACCESSIBILITY_MAX_SERIALIZED_CHARS || `Accessibility trees must not exceed ${SNAP_SHOT_ACCESSIBILITY_MAX_SERIALIZED_CHARS} serialized characters.` ); }),);export type SnapShotAccessibility = typeof SnapShotAccessibility.Type; export const SnapShotSource = Schema.Struct({ kind: Schema.Literal("snap-shot"), capturedAt: IsoDateTime, appName: TrimmedNonEmptyString.check(Schema.isMaxLength(255)), windowTitle: TrimmedString.check(Schema.isMaxLength(1_000)), accessibleText: Schema.optional( TrimmedNonEmptyString.check(Schema.isMaxLength(SNAP_SHOT_ACCESSIBLE_TEXT_MAX_CHARS)), ), accessibility: Schema.optional(SnapShotAccessibility), appIdentifier: Schema.optional(TrimmedNonEmptyString.check(Schema.isMaxLength(255))), appIconDataUrl: Schema.optional( TrimmedNonEmptyString.check( Schema.isMaxLength(100_000), Schema.isPattern(/^data:image\/png;base64,/i), ), ),});export type SnapShotSource = typeof SnapShotSource.Type; export const ChatImageAttachment = Schema.Struct({ type: Schema.Literal("image"), id: ChatAttachmentId, name: TrimmedNonEmptyString.check(Schema.isMaxLength(255)), mimeType: TrimmedNonEmptyString.check(Schema.isMaxLength(100), Schema.isPattern(/^image\//i)), sizeBytes: NonNegativeInt.check(Schema.isLessThanOrEqualTo(PROVIDER_SEND_TURN_MAX_IMAGE_BYTES)), source: Schema.optional(SnapShotSource),});export type ChatImageAttachment = typeof ChatImageAttachment.Type; export const ChatFileAttachment = Schema.Struct({ type: Schema.Literal("file"), id: ChatAttachmentId, name: TrimmedNonEmptyString.check(Schema.isMaxLength(255)), mimeType: TrimmedNonEmptyString.check(Schema.isMaxLength(100)), sizeBytes: NonNegativeInt.check( Schema.isGreaterThanOrEqualTo(1), Schema.isLessThanOrEqualTo(PROVIDER_SEND_TURN_MAX_FILE_BYTES), ),});export type ChatFileAttachment = typeof ChatFileAttachment.Type; /** * Catch-all for attachment types this build does not know. Attachments ride on * persisted events and thread streams, so a newer server or client must be able * to introduce a type without making older readers fail to decode the whole * message. Decoders keep the shared base fields; consumers skip these or render * them as unsupported. Mirrors how `OrchestrationThreadActivity` keeps `kind` * open. The known discriminators are excluded so a malformed image or file * attachment fails its own schema instead of sliding through here with its * size and mime constraints unchecked. */export const ChatUnknownAttachment = Schema.Struct({ type: TrimmedNonEmptyString.check( Schema.isMaxLength(50), Schema.isPattern(/^(?!(?:image|file)$)/), ), id: ChatAttachmentId, name: TrimmedNonEmptyString.check(Schema.isMaxLength(255)), mimeType: TrimmedNonEmptyString.check(Schema.isMaxLength(100)), sizeBytes: NonNegativeInt,});export type ChatUnknownAttachment = typeof ChatUnknownAttachment.Type; const UploadChatImageAttachment = Schema.Struct({ type: Schema.Literal("image"), name: TrimmedNonEmptyString.check(Schema.isMaxLength(255)), mimeType: TrimmedNonEmptyString.check(Schema.isMaxLength(100), Schema.isPattern(/^image\//i)), sizeBytes: NonNegativeInt.check(Schema.isLessThanOrEqualTo(PROVIDER_SEND_TURN_MAX_IMAGE_BYTES)), dataUrl: TrimmedNonEmptyString.check( Schema.isMaxLength(PROVIDER_SEND_TURN_MAX_IMAGE_DATA_URL_CHARS), ), source: Schema.optional(SnapShotSource),});export type UploadChatImageAttachment = typeof UploadChatImageAttachment.Type; export const ChatAttachment = Schema.Union([ ChatImageAttachment, ChatFileAttachment, ChatUnknownAttachment,]);export type ChatAttachment = typeof ChatAttachment.Type; export const UserInputAttachments = Schema.Record( Schema.String, Schema.Array(Schema.Union([ChatImageAttachment, ChatFileAttachment])).pipe( Schema.check(Schema.isMaxLength(PROVIDER_SEND_TURN_MAX_ATTACHMENTS)), ),);export type UserInputAttachments = typeof UserInputAttachments.Type; export const UserInputAttachmentAnswerPayload = Schema.Struct({ requestId: ApprovalRequestId, questionTextById: Schema.optional(Schema.Record(Schema.String, Schema.String)), answers: ProviderUserInputAnswers, attachmentsByQuestionId: UserInputAttachments,});export type UserInputAttachmentAnswerPayload = typeof UserInputAttachmentAnswerPayload.Type;const UploadChatAttachment = Schema.Union([UploadChatImageAttachment]);export type UploadChatAttachment = typeof UploadChatAttachment.Type; export const ProjectScriptIcon = Schema.Literals([ "play", "test", "lint", "configure", "build", "debug",]);export type ProjectScriptIcon = typeof ProjectScriptIcon.Type; export const ProjectScript = Schema.Struct({ id: TrimmedNonEmptyString, name: TrimmedNonEmptyString, command: TrimmedNonEmptyString, icon: ProjectScriptIcon, runOnWorktreeCreate: Schema.Boolean, /** * URL to open in the in-app browser preview when this script runs (or * when the user explicitly requests a preview). Optional; only honored on * the desktop build. */ previewUrl: Schema.optional(TrimmedNonEmptyString), /** * When true, automatically open the preview panel pointed at `previewUrl` * the moment this script starts. Ignored without `previewUrl` or on web. */ autoOpenPreview: Schema.optional(Schema.Boolean),});export type ProjectScript = typeof ProjectScript.Type; export const ProjectFaviconPath = TrimmedNonEmptyString.check( Schema.isMaxLength(1024), Schema.isPattern(/\.(?:avif|gif|ico|jpe?g|png|svg|webp)$/i),);export type ProjectFaviconPath = typeof ProjectFaviconPath.Type; export const ProjectIconColor = Schema.Literals([ "gray", "red", "orange", "amber", "yellow", "lime", "green", "emerald", "teal", "cyan", "sky", "blue", "indigo", "violet", "purple", "fuchsia", "pink", "rose",]);export type ProjectIconColor = typeof ProjectIconColor.Type; const ProjectLucideIconName = TrimmedNonEmptyString.check( Schema.isMaxLength(64), Schema.isPattern(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),); const ProjectEmoji = TrimmedNonEmptyString.check(Schema.isMaxLength(32)); export const ProjectIconOverride = Schema.Union([ Schema.Struct({ kind: Schema.Literal("lucide"), name: ProjectLucideIconName, color: ProjectIconColor, }), Schema.Struct({ kind: Schema.Literal("emoji"), emoji: ProjectEmoji, }),]);export type ProjectIconOverride = typeof ProjectIconOverride.Type; export const OrchestrationProject = Schema.Struct({ id: ProjectId, title: TrimmedNonEmptyString, workspaceRoot: TrimmedNonEmptyString, repositoryIdentity: Schema.optional(Schema.NullOr(RepositoryIdentity)), defaultModelSelection: Schema.NullOr(ModelSelection), // Per-project override for where new threads start. Null/absent means // "no override": clients fall back to t3.json, then the global setting. defaultThreadEnvMode: Schema.optional(Schema.NullOr(ThreadEnvMode)), // Opt-in because background sync performs network I/O and may move the checkout. // Optional on the wire so cached snapshots from older servers still decode. autoPull: Schema.optional(Schema.Boolean), // Optional on the wire so cached snapshots from older servers still decode. faviconPath: Schema.optional(Schema.NullOr(ProjectFaviconPath)), projectIcon: Schema.optional(Schema.NullOr(ProjectIconOverride)), scripts: Schema.Array(ProjectScript), createdAt: IsoDateTime, updatedAt: IsoDateTime, deletedAt: Schema.NullOr(IsoDateTime),});export type OrchestrationProject = typeof OrchestrationProject.Type; export const OrchestrationMessageRole = Schema.Literals(["user", "assistant", "system"]);export type OrchestrationMessageRole = typeof OrchestrationMessageRole.Type; export const OrchestrationMessage = Schema.Struct({ id: MessageId, role: OrchestrationMessageRole, text: Schema.String, attachments: Schema.optional(Schema.Array(ChatAttachment)), turnId: Schema.NullOr(TurnId), streaming: Schema.Boolean, createdAt: IsoDateTime, updatedAt: IsoDateTime,});export type OrchestrationMessage = typeof OrchestrationMessage.Type; export const OrchestrationProposedPlanId = TrimmedNonEmptyString;export type OrchestrationProposedPlanId = typeof OrchestrationProposedPlanId.Type; Lines 1–500 of 2251. Load more
import * as Effect from "effect/Effect"; import * as Schema from "effect/Schema"; import * as SchemaIssue from "effect/SchemaIssue"; import * as SchemaTransformation from "effect/SchemaTransformation"; import * as Struct from "effect/Struct"; import { ProviderOptionSelections } from "./model.ts"; import { RepositoryIdentity, ThreadEnvMode } from "./environment.ts"; import { ApprovalRequestId, CheckpointRef, ClientSurface, CommandId, EventId, IsoDateTime, MessageId, NonNegativeInt, PositiveInt, ProjectId, ProviderItemId, ThreadId, TrimmedNonEmptyString, TrimmedString, TurnId, } from "./baseSchemas.ts"; import { ProviderInstanceId } from "./providerInstance.ts"; import { PullRequestActor, PullRequestChecksState, PullRequestMergeability, PullRequestReviewDecision, PullRequestState, } from "./pullRequest.ts"; export const ORCHESTRATION_WS_METHODS = { dispatchCommand: "orchestration.dispatchCommand", getWorkflowScript: "orchestration.getWorkflowScript", getTurnDiff: "orchestration.getTurnDiff", getFullThreadDiff: "orchestration.getFullThreadDiff", searchThreads: "orchestration.searchThreads", getArchivedShellSnapshot: "orchestration.getArchivedShellSnapshot", subscribeShell: "orchestration.subscribeShell", subscribeThread: "orchestration.subscribeThread", } as const; export const ProviderApprovalPolicy = Schema.Literals([ "untrusted", "on-failure", "on-request", "never", ]); export type ProviderApprovalPolicy = typeof ProviderApprovalPolicy.Type; export const ProviderSandboxMode = Schema.Literals([ "read-only", "workspace-write", "danger-full-access", ]); export type ProviderSandboxMode = typeof ProviderSandboxMode.Type; /** * `ModelSelection` — selection of a model on a configured provider instance. * * The routing key is `instanceId` (a user-defined slug identifying one * configured provider instance). Drivers, credentials, working-directory * bindings, and any other per-instance state are recovered from the * runtime registry via the instance id. * * Wire legacy: persisted selections produced before the driver/instance * split carried a `provider: <driver-id>` field instead. The schema absorbs * that shape via a pre-decoding transform — `{provider, model}` is promoted * to `{instanceId: defaultInstanceIdForDriver(provider), model}`. No * post-decode compatibility code lives in the runtime; the transform is the * only compat surface. */ const ModelSelectionWire = Schema.Struct({ instanceId: ProviderInstanceId, model: TrimmedNonEmptyString, options: Schema.optionalKey(ProviderOptionSelections), }); // Source shape for persisted legacy payloads. Fields are typed as // `Schema.Unknown` so malformed drafts still make it into the transform and // fail validation through the target schema (with proper error messages) // rather than at the source-struct layer where the error is less actionable. const ModelSelectionSource = Schema.Struct({ provider: Schema.optional(Schema.Unknown), instanceId: Schema.optional(Schema.Unknown), model: Schema.Unknown, options: Schema.optional(Schema.Unknown), }); export const ModelSelection = ModelSelectionSource.pipe( Schema.decodeTo( ModelSelectionWire, SchemaTransformation.transformOrFail({ decode: (raw) => { // Resolve the routing key: prefer an explicit `instanceId`; fall // back to promoting the legacy `provider` slug (the canonical // `defaultInstanceIdForDriver` mapping) so persisted rollout-era // payloads decode without data loss. The target schema brands the // string as `ProviderInstanceId`. const instanceIdSource = raw.instanceId !== undefined ? raw.instanceId : typeof raw.provider === "string" ? raw.provider : undefined; const base: Record<string, unknown> = { instanceId: instanceIdSource, model: raw.model, }; if (raw.options !== undefined) base.options = raw.options; return Effect.succeed(base as typeof ModelSelectionWire.Encoded); }, encode: (value) => { const base: Record<string, unknown> = { model: value.model, instanceId: value.instanceId, }; if (value.options !== undefined) base.options = value.options; return Effect.succeed(base as typeof ModelSelectionSource.Encoded); }, }), ), ); export type ModelSelection = typeof ModelSelection.Type; export const RuntimeMode = Schema.Literals([ "approval-required", "auto-accept-edits", "auto", "full-access", ]); export type RuntimeMode = typeof RuntimeMode.Type; export const DEFAULT_RUNTIME_MODE: RuntimeMode = "full-access"; export const ProviderInteractionMode = Schema.Literals(["default", "plan"]); export type ProviderInteractionMode = typeof ProviderInteractionMode.Type; export const DEFAULT_PROVIDER_INTERACTION_MODE: ProviderInteractionMode = "default"; export const ProviderRequestKind = Schema.Literals([ "command", "file-read", "file-change", "mcp-elicitation", ]); export type ProviderRequestKind = typeof ProviderRequestKind.Type; export const AssistantDeliveryMode = Schema.Literals(["buffered", "streaming"]); export type AssistantDeliveryMode = typeof AssistantDeliveryMode.Type; export const ProviderApprovalDecision = Schema.Literals([ "accept", "acceptForSession", "acceptAlways", "decline", "cancel", ]); export type ProviderApprovalDecision = typeof ProviderApprovalDecision.Type; export const ProviderApprovalOption = Schema.Struct({ decision: ProviderApprovalDecision, label: TrimmedNonEmptyString, /** Provider-supplied caution shown next to the option, such as a prompt injection warning. */ warning: Schema.optional(TrimmedNonEmptyString), }); export type ProviderApprovalOption = typeof ProviderApprovalOption.Type; export const ProviderUserInputAnswers = Schema.Record(Schema.String, Schema.Unknown); export type ProviderUserInputAnswers = typeof ProviderUserInputAnswers.Type; export const PROVIDER_SEND_TURN_MAX_INPUT_CHARS = 120_000; export const PROVIDER_SEND_TURN_MAX_ATTACHMENTS = 8; export const PROVIDER_SEND_TURN_MAX_IMAGE_BYTES = 10 * 1024 * 1024; export const PROVIDER_SEND_TURN_MAX_FILE_BYTES = 50 * 1024 * 1024; export const PROVIDER_SEND_TURN_SUPPORTED_IMAGE_MIME_TYPES = [ "image/gif", "image/jpeg", "image/png", "image/webp", ] as const; const PROVIDER_SEND_TURN_SUPPORTED_IMAGE_MIME_TYPE_SET = new Set<string>( PROVIDER_SEND_TURN_SUPPORTED_IMAGE_MIME_TYPES, ); /** Whether a pasted or picked image mime type can be sent on a provider turn. */ export function isProviderSendTurnSupportedImageMimeType(mimeType: string): boolean { return PROVIDER_SEND_TURN_SUPPORTED_IMAGE_MIME_TYPE_SET.has(mimeType.toLowerCase()); } const PROVIDER_SEND_TURN_MAX_IMAGE_DATA_URL_CHARS = 14_000_000; const CHAT_ATTACHMENT_ID_MAX_CHARS = 128; // Correlation id is command id by design in this model. export const CorrelationId = CommandId; export type CorrelationId = typeof CorrelationId.Type; const ChatAttachmentId = TrimmedNonEmptyString.check( Schema.isMaxLength(CHAT_ATTACHMENT_ID_MAX_CHARS), Schema.isPattern(/^[a-z0-9_-]+$/i), ); export type ChatAttachmentId = typeof ChatAttachmentId.Type; export const SNAP_SHOT_ACCESSIBLE_TEXT_MAX_CHARS = 32_000; export const SNAP_SHOT_ACCESSIBILITY_MAX_NODES = 10_000; export const SNAP_SHOT_ACCESSIBILITY_MAX_SERIALIZED_CHARS = 32_000; const SnapShotAccessibilityBounds = Schema.Struct({ x: NonNegativeInt, y: NonNegativeInt, width: PositiveInt, height: PositiveInt, }); const SnapShotAccessibilityState = Schema.Struct({ active: Schema.optional(Schema.Boolean), busy: Schema.optional(Schema.Boolean), checked: Schema.optional(Schema.Literals(["on", "off", "mixed"])), editable: Schema.optional(Schema.Boolean), enabled: Schema.optional(Schema.Boolean), expanded: Schema.optional(Schema.Boolean), focused: Schema.optional(Schema.Boolean), selected: Schema.optional(Schema.Boolean), visible: Schema.optional(Schema.Boolean), }); export interface SnapShotAccessibilityNode { readonly role: string; readonly name?: string; readonly value?: string; readonly description?: string; readonly bounds: typeof SnapShotAccessibilityBounds.Type | null; readonly state?: typeof SnapShotAccessibilityState.Type; readonly actions?: Array<string>; readonly children: Array<SnapShotAccessibilityNode>; } export const SnapShotAccessibilityNode: Schema.Codec<SnapShotAccessibilityNode> = Schema.Struct({ role: TrimmedNonEmptyString.check(Schema.isMaxLength(100)), name: Schema.optionalKey(TrimmedNonEmptyString.check(Schema.isMaxLength(1_000))), value: Schema.optionalKey(TrimmedNonEmptyString.check(Schema.isMaxLength(8_000))), description: Schema.optionalKey(TrimmedNonEmptyString.check(Schema.isMaxLength(2_000))), bounds: Schema.NullOr(SnapShotAccessibilityBounds), state: Schema.optionalKey(SnapShotAccessibilityState), actions: Schema.optionalKey( Schema.mutable(Schema.Array(TrimmedNonEmptyString.check(Schema.isMaxLength(100)))).check( Schema.isMaxLength(32), ), ), children: Schema.mutable( Schema.Array( Schema.suspend((): Schema.Codec<SnapShotAccessibilityNode> => SnapShotAccessibilityNode), ), ).check(Schema.isMaxLength(SNAP_SHOT_ACCESSIBILITY_MAX_NODES)), }); const SnapShotAccessibilityWire = Schema.Union([ Schema.Struct({ format: Schema.Literal("flat-text"), text: TrimmedNonEmptyString.check(Schema.isMaxLength(SNAP_SHOT_ACCESSIBLE_TEXT_MAX_CHARS)), truncated: Schema.Boolean, }), Schema.Struct({ format: Schema.Literal("element-tree"), coordinateSpace: Schema.Literal("captured-image"), imageSize: Schema.Struct({ width: PositiveInt, height: PositiveInt }), truncated: Schema.Boolean, root: SnapShotAccessibilityNode, }), ]); export const SnapShotAccessibility = SnapShotAccessibilityWire.check( Schema.makeFilter((accessibility: typeof SnapShotAccessibilityWire.Type) => { if (accessibility.format === "flat-text") return undefined; let nodes = 0; const stack = [accessibility.root]; while (stack.length > 0) { const node = stack.pop()!; nodes += 1; if (nodes > SNAP_SHOT_ACCESSIBILITY_MAX_NODES) { return `Accessibility trees must not exceed ${SNAP_SHOT_ACCESSIBILITY_MAX_NODES} nodes.`; } stack.push(...node.children); } return ( JSON.stringify(accessibility).length <= SNAP_SHOT_ACCESSIBILITY_MAX_SERIALIZED_CHARS || `Accessibility trees must not exceed ${SNAP_SHOT_ACCESSIBILITY_MAX_SERIALIZED_CHARS} serialized characters.` ); }), ); export type SnapShotAccessibility = typeof SnapShotAccessibility.Type; export const SnapShotSource = Schema.Struct({ kind: Schema.Literal("snap-shot"), capturedAt: IsoDateTime, appName: TrimmedNonEmptyString.check(Schema.isMaxLength(255)), windowTitle: TrimmedString.check(Schema.isMaxLength(1_000)), accessibleText: Schema.optional( TrimmedNonEmptyString.check(Schema.isMaxLength(SNAP_SHOT_ACCESSIBLE_TEXT_MAX_CHARS)), ), accessibility: Schema.optional(SnapShotAccessibility), appIdentifier: Schema.optional(TrimmedNonEmptyString.check(Schema.isMaxLength(255))), appIconDataUrl: Schema.optional( TrimmedNonEmptyString.check( Schema.isMaxLength(100_000), Schema.isPattern(/^data:image\/png;base64,/i), ), ), }); export type SnapShotSource = typeof SnapShotSource.Type; export const ChatImageAttachment = Schema.Struct({ type: Schema.Literal("image"), id: ChatAttachmentId, name: TrimmedNonEmptyString.check(Schema.isMaxLength(255)), mimeType: TrimmedNonEmptyString.check(Schema.isMaxLength(100), Schema.isPattern(/^image\//i)), sizeBytes: NonNegativeInt.check(Schema.isLessThanOrEqualTo(PROVIDER_SEND_TURN_MAX_IMAGE_BYTES)), source: Schema.optional(SnapShotSource), }); export type ChatImageAttachment = typeof ChatImageAttachment.Type; export const ChatFileAttachment = Schema.Struct({ type: Schema.Literal("file"), id: ChatAttachmentId, name: TrimmedNonEmptyString.check(Schema.isMaxLength(255)), mimeType: TrimmedNonEmptyString.check(Schema.isMaxLength(100)), sizeBytes: NonNegativeInt.check( Schema.isGreaterThanOrEqualTo(1), Schema.isLessThanOrEqualTo(PROVIDER_SEND_TURN_MAX_FILE_BYTES), ), }); export type ChatFileAttachment = typeof ChatFileAttachment.Type; /** * Catch-all for attachment types this build does not know. Attachments ride on * persisted events and thread streams, so a newer server or client must be able * to introduce a type without making older readers fail to decode the whole * message. Decoders keep the shared base fields; consumers skip these or render * them as unsupported. Mirrors how `OrchestrationThreadActivity` keeps `kind` * open. The known discriminators are excluded so a malformed image or file * attachment fails its own schema instead of sliding through here with its * size and mime constraints unchecked. */ export const ChatUnknownAttachment = Schema.Struct({ type: TrimmedNonEmptyString.check( Schema.isMaxLength(50), Schema.isPattern(/^(?!(?:image|file)$)/), ), id: ChatAttachmentId, name: TrimmedNonEmptyString.check(Schema.isMaxLength(255)), mimeType: TrimmedNonEmptyString.check(Schema.isMaxLength(100)), sizeBytes: NonNegativeInt, }); export type ChatUnknownAttachment = typeof ChatUnknownAttachment.Type; const UploadChatImageAttachment = Schema.Struct({ type: Schema.Literal("image"), name: TrimmedNonEmptyString.check(Schema.isMaxLength(255)), mimeType: TrimmedNonEmptyString.check(Schema.isMaxLength(100), Schema.isPattern(/^image\//i)), sizeBytes: NonNegativeInt.check(Schema.isLessThanOrEqualTo(PROVIDER_SEND_TURN_MAX_IMAGE_BYTES)), dataUrl: TrimmedNonEmptyString.check( Schema.isMaxLength(PROVIDER_SEND_TURN_MAX_IMAGE_DATA_URL_CHARS), ), source: Schema.optional(SnapShotSource), }); export type UploadChatImageAttachment = typeof UploadChatImageAttachment.Type; export const ChatAttachment = Schema.Union([ ChatImageAttachment, ChatFileAttachment, ChatUnknownAttachment, ]); export type ChatAttachment = typeof ChatAttachment.Type; export const UserInputAttachments = Schema.Record( Schema.String, Schema.Array(Schema.Union([ChatImageAttachment, ChatFileAttachment])).pipe( Schema.check(Schema.isMaxLength(PROVIDER_SEND_TURN_MAX_ATTACHMENTS)), ), ); export type UserInputAttachments = typeof UserInputAttachments.Type; export const UserInputAttachmentAnswerPayload = Schema.Struct({ requestId: ApprovalRequestId, questionTextById: Schema.optional(Schema.Record(Schema.String, Schema.String)), answers: ProviderUserInputAnswers, attachmentsByQuestionId: UserInputAttachments, }); export type UserInputAttachmentAnswerPayload = typeof UserInputAttachmentAnswerPayload.Type; const UploadChatAttachment = Schema.Union([UploadChatImageAttachment]); export type UploadChatAttachment = typeof UploadChatAttachment.Type; export const ProjectScriptIcon = Schema.Literals([ "play", "test", "lint", "configure", "build", "debug", ]); export type ProjectScriptIcon = typeof ProjectScriptIcon.Type; export const ProjectScript = Schema.Struct({ id: TrimmedNonEmptyString, name: TrimmedNonEmptyString, command: TrimmedNonEmptyString, icon: ProjectScriptIcon, runOnWorktreeCreate: Schema.Boolean, /** * URL to open in the in-app browser preview when this script runs (or * when the user explicitly requests a preview). Optional; only honored on * the desktop build. */ previewUrl: Schema.optional(TrimmedNonEmptyString), /** * When true, automatically open the preview panel pointed at `previewUrl` * the moment this script starts. Ignored without `previewUrl` or on web. */ autoOpenPreview: Schema.optional(Schema.Boolean), }); export type ProjectScript = typeof ProjectScript.Type; export const ProjectFaviconPath = TrimmedNonEmptyString.check( Schema.isMaxLength(1024), Schema.isPattern(/\.(?:avif|gif|ico|jpe?g|png|svg|webp)$/i), ); export type ProjectFaviconPath = typeof ProjectFaviconPath.Type; export const ProjectIconColor = Schema.Literals([ "gray", "red", "orange", "amber", "yellow", "lime", "green", "emerald", "teal", "cyan", "sky", "blue", "indigo", "violet", "purple", "fuchsia", "pink", "rose", ]); export type ProjectIconColor = typeof ProjectIconColor.Type; const ProjectLucideIconName = TrimmedNonEmptyString.check( Schema.isMaxLength(64), Schema.isPattern(/^[a-z0-9]+(?:-[a-z0-9]+)*$/), ); const ProjectEmoji = TrimmedNonEmptyString.check(Schema.isMaxLength(32)); export const ProjectIconOverride = Schema.Union([ Schema.Struct({ kind: Schema.Literal("lucide"), name: ProjectLucideIconName, color: ProjectIconColor, }), Schema.Struct({ kind: Schema.Literal("emoji"), emoji: ProjectEmoji, }), ]); export type ProjectIconOverride = typeof ProjectIconOverride.Type; export const OrchestrationProject = Schema.Struct({ id: ProjectId, title: TrimmedNonEmptyString, workspaceRoot: TrimmedNonEmptyString, repositoryIdentity: Schema.optional(Schema.NullOr(RepositoryIdentity)), defaultModelSelection: Schema.NullOr(ModelSelection), // Per-project override for where new threads start. Null/absent means // "no override": clients fall back to t3.json, then the global setting. defaultThreadEnvMode: Schema.optional(Schema.NullOr(ThreadEnvMode)), // Opt-in because background sync performs network I/O and may move the checkout. // Optional on the wire so cached snapshots from older servers still decode. autoPull: Schema.optional(Schema.Boolean), // Optional on the wire so cached snapshots from older servers still decode. faviconPath: Schema.optional(Schema.NullOr(ProjectFaviconPath)), projectIcon: Schema.optional(Schema.NullOr(ProjectIconOverride)), scripts: Schema.Array(ProjectScript), createdAt: IsoDateTime, updatedAt: IsoDateTime, deletedAt: Schema.NullOr(IsoDateTime), }); export type OrchestrationProject = typeof OrchestrationProject.Type; export const OrchestrationMessageRole = Schema.Literals(["user", "assistant", "system"]); export type OrchestrationMessageRole = typeof OrchestrationMessageRole.Type; export const OrchestrationMessage = Schema.Struct({ id: MessageId, role: OrchestrationMessageRole, text: Schema.String, attachments: Schema.optional(Schema.Array(ChatAttachment)), turnId: Schema.NullOr(TurnId), streaming: Schema.Boolean, createdAt: IsoDateTime, updatedAt: IsoDateTime, }); export type OrchestrationMessage = typeof OrchestrationMessage.Type; export const OrchestrationProposedPlanId = TrimmedNonEmptyString; export type OrchestrationProposedPlanId = typeof OrchestrationProposedPlanId.Type;