import * as z from 'zod/v4';

export const PROTOCOL_VERSION = 1 as const;

const prefixedId = (prefix: string) => z.string().regex(new RegExp(`^${prefix}_[0-9a-f-]{36}$`));
export const RunIdSchema = prefixedId('run');
export const WorkerIdSchema = prefixedId('wrk');
export const AssignmentIdSchema = prefixedId('asg');
export const MessageIdSchema = prefixedId('msg');
export const EventIdSchema = prefixedId('evt');
export const ExecutionIdSchema = prefixedId('exe');
export const CommandIdSchema = prefixedId('cmd');

export type RunId = z.infer<typeof RunIdSchema>;
export type WorkerId = z.infer<typeof WorkerIdSchema>;
export type AssignmentId = z.infer<typeof AssignmentIdSchema>;
export type MessageId = z.infer<typeof MessageIdSchema>;
export type EventId = z.infer<typeof EventIdSchema>;
export type ExecutionId = z.infer<typeof ExecutionIdSchema>;
export type CommandId = z.infer<typeof CommandIdSchema>;

export const RunStateSchema = z.enum(['active', 'completing', 'completed', 'failed', 'cancelled']);
export type RunState = z.infer<typeof RunStateSchema>;

export const WorkerStateSchema = z.enum([
  'created', 'launch_requested', 'launching', 'running', 'waiting',
  'completed', 'failed', 'cancelled', 'launch_failed',
]);
export type WorkerState = z.infer<typeof WorkerStateSchema>;

export const WorkerDispositionSchema = z.enum([
  'running',
  'awaiting_execution',
  'awaiting_human',
  'awaiting_dependency',
  'paused',
  'terminal',
]);
export type WorkerDisposition = z.infer<typeof WorkerDispositionSchema>;

export const ContinuationModeSchema = z.enum(['manual', 'auto']);
export type ContinuationMode = z.infer<typeof ContinuationModeSchema>;

const optionalThresholdSchema = z.number().int().positive().nullable();
export const ContinuationPolicySchema = z.object({
  mode: ContinuationModeSchema.default('manual'),
  idleGraceMs: z.number().int().nonnegative().max(300_000).default(3_000),
  activeKeepaliveMinMs: z.number().int().positive().max(24 * 60 * 60_000).default(20 * 60_000),
  activeKeepaliveMaxMs: z.number().int().positive().max(24 * 60 * 60_000).default(21 * 60_000),
  minContinuationSpacingMs: z.number().int().nonnegative().max(24 * 60 * 60_000).default(15 * 60_000),
  idleMayBypassMinSpacing: z.boolean().default(true),
  deadmanFallbackEnabled: z.boolean().default(false),
  deadmanThresholdMs: z.number().int().positive().max(7 * 24 * 60 * 60_000).default(30 * 60_000),
  requireProgressHeartbeat: z.boolean().default(false),
  stallWarningAfterNoProgressAttempts: optionalThresholdSchema.default(10),
  stallPauseAfterNoProgressAttempts: optionalThresholdSchema.default(20),
  stallWarningAfterNoProgressMs: optionalThresholdSchema.default(30 * 60_000),
  stallPauseAfterNoProgressMs: optionalThresholdSchema.default(60 * 60_000),
}).superRefine((policy, ctx) => {
  if (policy.activeKeepaliveMinMs > policy.activeKeepaliveMaxMs) {
    ctx.addIssue({ code: 'custom', path: ['activeKeepaliveMinMs'], message: 'Keepalive minimum cannot exceed maximum.' });
  }
  if (
    policy.stallWarningAfterNoProgressAttempts !== null
    && policy.stallPauseAfterNoProgressAttempts !== null
    && policy.stallWarningAfterNoProgressAttempts > policy.stallPauseAfterNoProgressAttempts
  ) {
    ctx.addIssue({
      code: 'custom',
      path: ['stallWarningAfterNoProgressAttempts'],
      message: 'Warning attempt threshold cannot exceed pause attempt threshold.',
    });
  }
  if (
    policy.stallWarningAfterNoProgressMs !== null
    && policy.stallPauseAfterNoProgressMs !== null
    && policy.stallWarningAfterNoProgressMs > policy.stallPauseAfterNoProgressMs
  ) {
    ctx.addIssue({
      code: 'custom',
      path: ['stallWarningAfterNoProgressMs'],
      message: 'Warning time threshold cannot exceed pause time threshold.',
    });
  }
});
export type ContinuationPolicy = z.infer<typeof ContinuationPolicySchema>;
export const ContinuationPolicyPatchSchema = z.object({
  mode: ContinuationModeSchema.optional(),
  idleGraceMs: z.number().int().nonnegative().max(300_000).optional(),
  activeKeepaliveMinMs: z.number().int().positive().max(24 * 60 * 60_000).optional(),
  activeKeepaliveMaxMs: z.number().int().positive().max(24 * 60 * 60_000).optional(),
  minContinuationSpacingMs: z.number().int().nonnegative().max(24 * 60 * 60_000).optional(),
  idleMayBypassMinSpacing: z.boolean().optional(),
  deadmanFallbackEnabled: z.boolean().optional(),
  deadmanThresholdMs: z.number().int().positive().max(7 * 24 * 60 * 60_000).optional(),
  requireProgressHeartbeat: z.boolean().optional(),
  stallWarningAfterNoProgressAttempts: optionalThresholdSchema.optional(),
  stallPauseAfterNoProgressAttempts: optionalThresholdSchema.optional(),
  stallWarningAfterNoProgressMs: optionalThresholdSchema.optional(),
  stallPauseAfterNoProgressMs: optionalThresholdSchema.optional(),
});
export type ContinuationPolicyPatch = z.infer<typeof ContinuationPolicyPatchSchema>;
export const DEFAULT_CONTINUATION_POLICY: ContinuationPolicy = ContinuationPolicySchema.parse({});

export const WorkerPauseReasonSchema = z.enum(['user', 'stall', 'error']);
export type WorkerPauseReason = z.infer<typeof WorkerPauseReasonSchema>;

export const HumanWaitSchema = z.object({
  reason: z.string().min(1),
  request: z.string().min(1),
  requestedAt: z.string().datetime(),
  choices: z.array(z.string().min(1)).default([]),
  contextRefs: z.array(z.string().min(1)).default([]),
});
export type HumanWait = z.infer<typeof HumanWaitSchema>;

export const DependencyWaitSchema = z.object({
  reason: z.string().min(1),
  dependencies: z.array(WorkerIdSchema).default([]),
  requestedAt: z.string().datetime(),
});
export type DependencyWait = z.infer<typeof DependencyWaitSchema>;

export const WorkerContinuityStateSchema = z.object({
  workerId: WorkerIdSchema,
  disposition: WorkerDispositionSchema,
  policyOverride: ContinuationPolicySchema.nullable(),
  humanWait: HumanWaitSchema.nullable(),
  dependencyWait: DependencyWaitSchema.nullable(),
  pauseReason: WorkerPauseReasonSchema.nullable(),
  pauseAfterCurrentTurn: z.boolean(),
  resumeAfterCurrentTurn: z.boolean(),
  pendingExecutionId: ExecutionIdSchema.nullable(),
  idleGraceDueAt: z.string().datetime().nullable(),
  activeKeepaliveDueAt: z.string().datetime().nullable(),
  wakeNotBefore: z.string().datetime().nullable(),
  pendingWakeReasons: z.array(z.string().min(1).max(160)).max(32),
  lastContinuationSubmittedAt: z.string().datetime().nullable(),
  lastRoutineSteerAt: z.string().datetime().nullable(),
  deadmanRecoveryCount: z.number().int().nonnegative(),
  lastDeadmanRecoveryAt: z.string().datetime().nullable(),
  lastProgressCursor: z.number().int().positive().nullable(),
  lastProgressAt: z.string().datetime().nullable(),
  consecutiveExecutionAttemptsWithoutProgress: z.number().int().nonnegative(),
  stallWarningAt: z.string().datetime().nullable(),
  stallPausedAt: z.string().datetime().nullable(),
  updatedAt: z.string().datetime(),
});
export type WorkerContinuityState = z.infer<typeof WorkerContinuityStateSchema>;

export const ExecutionAttemptReasonSchema = z.enum(['initial', 'auto_resume', 'manual_resume', 'followup', 'recovery']);
export type ExecutionAttemptReason = z.infer<typeof ExecutionAttemptReasonSchema>;

export const ExecutionAttemptStateSchema = z.enum([
  'scheduled', 'submitted', 'generating', 'idle', 'completed', 'blocked', 'failed', 'cancelled',
]);
export type ExecutionAttemptState = z.infer<typeof ExecutionAttemptStateSchema>;

export const ExecutionAttemptOutcomeSchema = z.enum([
  'worker_completed',
  'awaiting_human',
  'awaiting_dependency',
  'paused',
  'message_delivery_timeout',
  'host_ui_changed',
  'ambiguous_submission',
  'executor_unavailable',
  'cancelled',
  'failed',
]);
export type ExecutionAttemptOutcome = z.infer<typeof ExecutionAttemptOutcomeSchema>;

export const ExecutionAttemptSchema = z.object({
  executionId: ExecutionIdSchema,
  runId: RunIdSchema,
  workerId: WorkerIdSchema,
  conversationId: z.string().min(1).nullable(),
  sequence: z.number().int().positive(),
  reason: ExecutionAttemptReasonSchema,
  resumeOfExecutionId: ExecutionIdSchema.nullable(),
  state: ExecutionAttemptStateSchema,
  scheduledAt: z.string().datetime(),
  submittedAt: z.string().datetime().nullable(),
  generatingAt: z.string().datetime().nullable(),
  idleAt: z.string().datetime().nullable(),
  terminalAt: z.string().datetime().nullable(),
  progressCursorAtStart: z.number().int().positive().nullable(),
  lastProgressCursor: z.number().int().positive().nullable(),
  wakeReasons: z.array(z.string().min(1).max(160)).max(32),
  continuationCommandId: CommandIdSchema.nullable(),
  idempotencyKey: z.string().min(1),
  outcome: ExecutionAttemptOutcomeSchema.nullable(),
  errorCode: z.string().min(1).nullable(),
  errorDetail: z.string().min(1).nullable(),
});
export type ExecutionAttempt = z.infer<typeof ExecutionAttemptSchema>;

export const CONTINUATION_STATE_FIELD_LIMITS = { summary: 2_000, current: 4_000, next: 4_000 } as const;
export const ContinuationStateStatusSchema = z.enum(['working', 'complete', 'blocked']);
export const ContinuationStateSchema = z.object({
  status: ContinuationStateStatusSchema,
  summary: z.string().min(1).max(CONTINUATION_STATE_FIELD_LIMITS.summary),
  current: z.string().min(1).max(CONTINUATION_STATE_FIELD_LIMITS.current),
  next: z.string().min(1).max(CONTINUATION_STATE_FIELD_LIMITS.next),
}).superRefine((value, ctx) => {
  const isNone = value.next.trim().toLowerCase() === 'none';
  if (value.status === 'complete' && !isNone) {
    ctx.addIssue({ code: 'custom', path: ['next'], message: 'Complete continuation state must use next: none.' });
  }
  if (value.status !== 'complete' && isNone) {
    ctx.addIssue({ code: 'custom', path: ['next'], message: 'Working or blocked continuation state must identify a next action.' });
  }
});
export type ContinuationState = z.infer<typeof ContinuationStateSchema>;
export type ContinuationStateParseResult =
  | { success: true; value: ContinuationState }
  | { success: false; error: string };

export function parseContinuationState(text: string): ContinuationStateParseResult {
  const normalized = text.replace(/\r\n/g, '\n');
  const openMatches = normalized.match(/<continuation-state>/g) ?? [];
  const closeMatches = normalized.match(/<\/continuation-state>/g) ?? [];
  if (openMatches.length !== 1 || closeMatches.length !== 1) {
    const count = Math.max(openMatches.length, closeMatches.length);
    return { success: false, error: `Expected exactly one <continuation-state> block, found ${count}.` };
  }
  const match = normalized.match(/<continuation-state>\n([\s\S]*?)\n<\/continuation-state>\s*$/);
  if (!match) return { success: false, error: 'Continuation-state block must be the final non-whitespace output.' };
  const body = match[1];
  if (body === undefined) return { success: false, error: 'Continuation-state block body is missing.' };
  const lines = body.split('\n');
  if (lines.length !== 4) return { success: false, error: 'Continuation-state block must contain exactly four fields.' };
  const expected = ['status', 'summary', 'current', 'next'] as const;
  const parsed: Record<string, string> = {};
  for (let index = 0; index < expected.length; index += 1) {
    const field = expected[index];
    const line = lines[index];
    if (!field || line === undefined || !line.startsWith(`${field}: `)) {
      return { success: false, error: `Continuation-state field ${field ?? index} is missing or out of order.` };
    }
    const value = line.slice(field.length + 2).trim();
    if (!value) return { success: false, error: `Continuation-state field ${field} is empty.` };
    parsed[field] = value;
  }
  const result = ContinuationStateSchema.safeParse(parsed);
  return result.success
    ? { success: true, value: result.data }
    : { success: false, error: result.error.issues.map((issue) => issue.message).join(' ') };
}

export const ExecutorSchema = z.enum(['chatgpt-web']);
export type Executor = z.infer<typeof ExecutorSchema>;

export const AssignmentSchema = z.object({
  assignmentId: AssignmentIdSchema,
  objective: z.string().min(1),
  constraints: z.array(z.string()).default([]),
  acceptanceCriteria: z.array(z.string()).default([]),
  dependencies: z.array(WorkerIdSchema).default([]),
  allowedScope: z.array(z.string()).default([]),
  forbiddenScope: z.array(z.string()).default([]),
  artifacts: z.array(z.string()).default([]),
});
export type Assignment = z.infer<typeof AssignmentSchema>;

export const RunSchema = z.object({
  runId: RunIdSchema,
  title: z.string().min(1),
  taskName: z.string().min(1).optional(),
  rootConversationName: z.string().min(1).optional(),
  rootWorkerId: WorkerIdSchema,
  state: RunStateSchema,
  createdAt: z.string().datetime(),
  updatedAt: z.string().datetime(),
});
export type Run = z.infer<typeof RunSchema>;

export const WorkerSchema = z.object({
  workerId: WorkerIdSchema,
  runId: RunIdSchema,
  name: z.string().regex(/^\/root(?:\/[a-zA-Z0-9._-]+)*$/),
  displayName: z.string().min(1).optional(),
  taskSummary: z.string().min(1).optional(),
  parentWorkerId: WorkerIdSchema.nullable(),
  executor: ExecutorSchema,
  state: WorkerStateSchema,
  assignmentId: AssignmentIdSchema,
  executionId: ExecutionIdSchema.nullable(),
  createdAt: z.string().datetime(),
  updatedAt: z.string().datetime(),
});
export type Worker = z.infer<typeof WorkerSchema>;

export const MessageTypeSchema = z.enum(['question', 'answer', 'information', 'blocker', 'review_request', 'correction']);
export const MessageDeliverySchema = z.enum(['record_only', 'next_turn', 'steer_now']);
export type MessageDelivery = z.infer<typeof MessageDeliverySchema>;
export const MessagePrioritySchema = z.enum(['normal', 'corrective']);
export type MessagePriority = z.infer<typeof MessagePrioritySchema>;
export const MessageDeliveryStateSchema = z.enum(['recorded', 'pending', 'dispatching', 'retry_wait', 'delivered', 'ambiguous']);
export type MessageDeliveryState = z.infer<typeof MessageDeliveryStateSchema>;
export const MessageSchema = z.object({
  messageId: MessageIdSchema,
  runId: RunIdSchema,
  fromWorkerId: WorkerIdSchema,
  toWorkerId: WorkerIdSchema,
  type: MessageTypeSchema,
  body: z.string().min(1),
  delivery: MessageDeliverySchema,
  priority: MessagePrioritySchema,
  idempotencyKey: z.string().min(1).nullable(),
  deliveryState: MessageDeliveryStateSchema,
  notBefore: z.string().datetime().nullable(),
  deliveryCommandId: CommandIdSchema.nullable(),
  deliveryAttempts: z.number().int().nonnegative(),
  deliveredAt: z.string().datetime().nullable(),
  lastDeliveryError: z.string().nullable(),
  createdAt: z.string().datetime(),
});
export type Message = z.infer<typeof MessageSchema>;

export const EventTypeSchema = z.enum([
  'run.created', 'run.state_changed', 'run.metadata_changed', 'worker.created', 'worker.launch_requested',
  'worker.bound', 'worker.state_changed', 'worker.message', 'worker.progress',
  'message.delivery_deferred', 'message.delivery_dispatched', 'message.delivery_delivered', 'message.delivery_failed', 'message.delivery_ambiguous',
  'worker.completed', 'worker.failed', 'worker.awaiting_human', 'worker.awaiting_dependency', 'worker.resumed',
  'executor.connected', 'executor.disconnected', 'executor.command_acked', 'conversation.created', 'conversation.state_changed',
  'execution.scheduled', 'execution.submitted', 'execution.generating', 'execution.idle',
  'execution.resume_scheduled', 'execution.resumed', 'execution.paused', 'execution.blocked',
  'execution.stalled', 'execution.completed', 'execution.failed',
  'continuation.policy_changed', 'continuation.warning', 'continuation.paused_for_stall',
  'continuation.wake_queued', 'continuation.wake_cancelled', 'continuation.keepalive_scheduled',
  'continuation.deadman_recovery_requested', 'continuation.deadman_recovery_fired',
]);
export type EventType = z.infer<typeof EventTypeSchema>;

export const EventSchema = z.object({
  eventId: EventIdSchema,
  runId: RunIdSchema,
  cursor: z.number().int().positive(),
  type: EventTypeSchema,
  workerId: WorkerIdSchema.optional(),
  timestamp: z.string().datetime(),
  payload: z.record(z.string(), z.unknown()).default({}),
});
export type OrchestratorEvent = z.infer<typeof EventSchema>;

const executorBase = {
  protocolVersion: z.literal(PROTOCOL_VERSION),
  commandId: CommandIdSchema,
  workerId: WorkerIdSchema,
  idempotencyKey: z.string().min(1),
  deadline: z.string().datetime(),
};

export const ExecutorCommandSchema = z.discriminatedUnion('type', [
  z.object({ ...executorBase, type: z.literal('conversation.create'), bootstrap: z.string().min(1) }),
  z.object({ ...executorBase, type: z.literal('conversation.send'), prompt: z.string().min(1) }),
  z.object({ ...executorBase, type: z.literal('conversation.steer'), prompt: z.string().min(1), reason: z.enum(['checkpoint', 'corrective']).default('checkpoint') }),
  z.object({ ...executorBase, type: z.literal('conversation.inspect') }),
  z.object({ ...executorBase, type: z.literal('conversation.close') }),
]);
export type ExecutorCommand = z.infer<typeof ExecutorCommandSchema>;

export const OrchestratorErrorCodeSchema = z.enum([
  'NOT_FOUND', 'INVALID_STATE', 'INVALID_PARENT', 'WRONG_RUN', 'IDEMPOTENCY_CONFLICT',
  'UNAUTHORIZED', 'HOST_UI_CHANGED', 'EXECUTOR_UNAVAILABLE', 'AMBIGUOUS_SUBMISSION',
  'MESSAGE_DELIVERY_TIMEOUT', 'ALREADY_RUNNING', 'CONTINUATION_BLOCKED', 'INTERNAL_ERROR',
]);
export type OrchestratorErrorCode = z.infer<typeof OrchestratorErrorCodeSchema>;

export const ConversationStateSchema = z.enum(['loading', 'ready', 'generating', 'idle', 'error', 'closed']);
export type ConversationState = z.infer<typeof ConversationStateSchema>;

export const ExecutorCommandStatusSchema = z.enum(['pending', 'sent', 'acked', 'completed', 'failed']);
export type ExecutorCommandStatus = z.infer<typeof ExecutorCommandStatusSchema>;

export const ExecutorCommandRecordSchema = z.object({
  command: ExecutorCommandSchema,
  status: ExecutorCommandStatusSchema,
  attempts: z.number().int().nonnegative(),
  executorId: z.string().min(1).nullable(),
  createdAt: z.string().datetime(),
  updatedAt: z.string().datetime(),
  result: z.record(z.string(), z.unknown()).nullable(),
  error: z.string().nullable(),
});
export type ExecutorCommandRecord = z.infer<typeof ExecutorCommandRecordSchema>;

export const ExecutorHelloFrameSchema = z.object({
  protocolVersion: z.literal(PROTOCOL_VERSION),
  type: z.literal('executor.hello'),
  executorId: z.string().min(1),
  token: z.string().min(16),
  browser: z.enum(['firefox', 'chromium']),
  extensionVersion: z.string().min(1),
});

export const ExecutorHeartbeatFrameSchema = z.object({
  protocolVersion: z.literal(PROTOCOL_VERSION),
  type: z.literal('executor.heartbeat'),
  executorId: z.string().min(1),
  timestamp: z.string().datetime(),
});

export const ExecutorCommandAckFrameSchema = z.object({
  protocolVersion: z.literal(PROTOCOL_VERSION),
  type: z.literal('command.ack'),
  executorId: z.string().min(1),
  commandId: CommandIdSchema,
});

export const ExecutorCommandResultFrameSchema = z.object({
  protocolVersion: z.literal(PROTOCOL_VERSION),
  type: z.literal('command.result'),
  executorId: z.string().min(1),
  commandId: CommandIdSchema,
  success: z.boolean(),
  result: z.record(z.string(), z.unknown()).default({}),
  error: z.string().optional(),
});

export const ExecutorConversationStateFrameSchema = z.object({
  protocolVersion: z.literal(PROTOCOL_VERSION),
  type: z.literal('conversation.state'),
  executorId: z.string().min(1),
  workerId: WorkerIdSchema,
  state: ConversationStateSchema,
  tabId: z.number().int().nonnegative().optional(),
  conversationId: z.string().min(1).optional(),
  url: z.string().url().optional(),
  error: z.string().optional(),
  assistantOutputTail: z.string().max(16_384).nullable().optional(),
  assistantOutputTruncated: z.boolean().optional(),
});

export const OperatorActionSchema = z.enum(['pause', 'continue_now', 'cancel', 'set_auto_resume']);
export const ExecutorOperatorActionFrameSchema = z.object({
  protocolVersion: z.literal(PROTOCOL_VERSION),
  type: z.literal('operator.action'),
  executorId: z.string().min(1),
  requestId: z.string().min(1).max(256),
  workerId: WorkerIdSchema,
  action: OperatorActionSchema,
  enabled: z.boolean().optional(),
  overrideBlock: z.boolean().optional(),
  idempotencyKey: z.string().min(1).max(512).optional(),
}).superRefine((value, ctx) => {
  if (value.action === 'set_auto_resume' && value.enabled === undefined) {
    ctx.addIssue({ code: 'custom', path: ['enabled'], message: 'enabled is required for set_auto_resume.' });
  }
});

export const ExecutorClientFrameSchema = z.discriminatedUnion('type', [
  ExecutorHelloFrameSchema,
  ExecutorHeartbeatFrameSchema,
  ExecutorCommandAckFrameSchema,
  ExecutorCommandResultFrameSchema,
  ExecutorConversationStateFrameSchema,
  ExecutorOperatorActionFrameSchema,
]);
export type ExecutorClientFrame = z.infer<typeof ExecutorClientFrameSchema>;

export const ExecutorReadyFrameSchema = z.object({
  protocolVersion: z.literal(PROTOCOL_VERSION),
  type: z.literal('executor.ready'),
  executorId: z.string().min(1),
  heartbeatIntervalMs: z.number().int().positive(),
});

export const ExecutorCommandFrameSchema = z.object({
  protocolVersion: z.literal(PROTOCOL_VERSION),
  type: z.literal('executor.command'),
  command: ExecutorCommandSchema,
});

export const WorkerContinuityViewFrameSchema = z.object({
  workerId: WorkerIdSchema,
  displayName: z.string().min(1).optional(),
  assignmentTitle: z.string().min(1).optional(),
  disposition: WorkerDispositionSchema,
  policyMode: ContinuationModeSchema,
  attemptNumber: z.number().int().positive().nullable(),
  autoResumeCount: z.number().int().nonnegative(),
  lastProgressAt: z.string().datetime().nullable(),
  needsUser: z.boolean(),
  isWorking: z.boolean(),
  isResuming: z.boolean(),
});
export type WorkerContinuityViewFrame = z.infer<typeof WorkerContinuityViewFrameSchema>;

export const ExecutorOperatorResultFrameSchema = z.object({
  protocolVersion: z.literal(PROTOCOL_VERSION),
  type: z.literal('operator.result'),
  executorId: z.string().min(1),
  requestId: z.string().min(1).max(256),
  workerId: WorkerIdSchema,
  success: z.boolean(),
  errorCode: z.string().min(1).optional(),
  error: z.string().min(1).optional(),
  view: WorkerContinuityViewFrameSchema.optional(),
});

export const ExecutorWorkerContinuityFrameSchema = z.object({
  protocolVersion: z.literal(PROTOCOL_VERSION),
  type: z.literal('worker.continuity'),
  executorId: z.string().min(1),
  view: WorkerContinuityViewFrameSchema,
});

export const ExecutorServerFrameSchema = z.discriminatedUnion('type', [
  ExecutorReadyFrameSchema,
  ExecutorCommandFrameSchema,
  ExecutorOperatorResultFrameSchema,
  ExecutorWorkerContinuityFrameSchema,
]);
export type ExecutorServerFrame = z.infer<typeof ExecutorServerFrameSchema>;

export const ConversationBindingSchema = z.object({
  workerId: WorkerIdSchema,
  executorId: z.string().min(1),
  tabId: z.number().int().nonnegative().nullable(),
  conversationId: z.string().min(1).nullable(),
  url: z.string().url().nullable(),
  state: ConversationStateSchema,
  updatedAt: z.string().datetime(),
});
export type ConversationBinding = z.infer<typeof ConversationBindingSchema>;
