2251 lines · 79.0 KB · TypeScript
RawDownload
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.