import { BestAgentError } from "@best-agent/foundation";
import {
  PLAN_TODO_KINDS,
  type ActionId,
  type ApprovalGrantScope,
  type ApprovalRequest,
  type ArtifactRef,
  type ArtifactObligation,
  type ArtifactObligationConstraint,
  type AssetId,
  type AskUserAnswers,
  type AskUserQuestion,
  type CapabilityRequest,
  type ClosurePolicy,
  type FailureRecoverability,
  type FailureScope,
  type GuardianAssessment,
  type ItemId,
  type PhaseResultEnvelope,
  type PhaseResultPhaseName,
  type PlanProjection,
  type PlanStatus,
  type PlanTodo,
  type PlanTodoStatus,
  type PromptContentPart,
  type ProviderConfig,
  type ProviderDecisionTransport,
  type ProviderDecisionTransportMode,
  type ProviderKind,
  type ProviderReplayConfig,
  type ProviderTurnActionPolicy,
  type RunPhaseName,
  type SessionUsageOperation,
  type ThreadEvidenceRecord,
  type ThreadId,
  type ThreadMetadata,
  type ThreadMode,
  type ToolEffect,
  type ToolExecutionClass,
  type ToolFailureKind,
  type ToolInputValue,
  type ToolReplayPolicy,
  type ToolCallRef,
  type ToolResultEnvelope,
  type ToolResultItem,
  type ToolResultOutcome,
  type TurnId,
  type VerdictStatus,
} from "@best-agent/protocol";
import { z } from "zod";
import type { RunCheckpoint } from "../core/thread-store.js";
import { isPersistableProviderToolName } from "../provider/provider-tool-names.js";
import type { ConversationToolCall } from "../provider/provider-conversation.js";
import type {
  ConversationMessage,
  ModelProvider,
  ProviderCacheControl,
  ProviderDecideInput,
  ProviderToolOption,
} from "../provider/model-provider.js";
import type { RequiredToolConstraint } from "./provider-turn/tool-constraint-types.js";

export type PendingProviderToolCall = {
  providerToolName?: string;
  replayToolInputAssetId?: AssetId;
  replayToolInputDigest?: string;
  toolCallId: ItemId;
  toolInput: ToolInputValue;
  toolName: string;
};

export type CompletedProviderToolCall = PendingProviderToolCall & {
  errorCode?: string;
  failureKind?: ToolFailureKind;
  /** @deprecated use inputStableDigest. */
  inputDigest?: string;
  inputStableDigest?: string;
  outcome: ToolResultOutcome;
  replayPolicy?: ToolReplayPolicy;
  toolOutput: string;
};

export type AdvancedPhaseLoopState = {
  closurePolicy: ClosurePolicy;
  hostProfileName: string;
  summaryProfileName: string;
  threadMode: ThreadMode;
};

export type RuntimeToolResumeContext = {
  activeSkillIds: string[];
  allowPhaseResult?: boolean;
  allowPlanUpdate?: boolean;
  advancedPhase?: AdvancedPhaseLoopState;
  artifactObligations?: ArtifactObligation[];
  approvalTurnId?: TurnId;
  availableTools: ProviderToolOption[];
  cacheControl?: ProviderCacheControl;
  completedToolCalls?: CompletedProviderToolCall[];
  customLoopId?: string;
  decisionTransport?: ProviderDecisionTransport;
  fixedRouteConfigOverride?: boolean;
  messages: ConversationMessage[];
  mode: "provider-tool-loop" | "tool-plan" | "custom-loop";
  objective?: string;
  pendingToolCalls?: PendingProviderToolCall[];
  phaseName?: RunPhaseName;
  phaseResultPhaseName?: PhaseResultPhaseName;
  profileName: string;
  providerTurnBudgeted?: boolean;
  providerTurnCount?: number;
  routeConfigOverride?: ProviderConfig;
  repairAttempt?: number;
  executionFailureCount?: number;
  requiredToolConstraint?: RequiredToolConstraint;
  systemPrompt?: string;
  toolCallId: ItemId;
  toolInput: ToolInputValue;
  toolName: string;
  providerToolName?: string;
  toolStepCount: number;
  reasoning?: string;
};

export type ToolResumeContext = RuntimeToolResumeContext;

export type PersistedProviderToolOption = Omit<ProviderToolOption, "effect"> & {
  effect?: ToolEffect;
};

export type PersistedProviderDecideCheckpointRequest = Omit<
  ProviderDecideCheckpointRequest,
  "tools"
> & {
  tools: PersistedProviderToolOption[];
};

export type PersistedToolResumeContext = Omit<
  RuntimeToolResumeContext,
  "availableTools" | "routeConfigOverride"
> & {
  activeReplayToolInputAssetId?: AssetId;
  activeReplayToolInputDigest?: string;
  availableTools: PersistedProviderToolOption[];
  routeConfigOverride?: ProviderReplayConfig;
};

export type ApprovalResolution = {
  decision: "allow" | "reject" | "expired";
  scope: ApprovalGrantScope;
};

export type ApprovalGrantActionSource = "legacy" | "raw-request-digest";

export type ActionResolution =
  | {
      answers: AskUserAnswers;
      status: "answered";
    }
  | {
      status: "expired";
    };

export type ApprovalCheckpointPayload = PersistedToolResumeContext & {
  approval: ApprovalRequest;
  approvalGrantAction?: string;
  approvalGrantActionSource?: ApprovalGrantActionSource;
  kind: "approval";
  request: CapabilityRequest;
  resolution?: ApprovalResolution;
};

export type ActionCheckpointPayload = PersistedToolResumeContext & {
  actionId: ActionId;
  kind: "action";
  questions: AskUserQuestion[];
  resolution?: ActionResolution;
};

export type ToolOutputCheckpointPayload = PersistedToolResumeContext & {
  evidenceRecords?: ThreadEvidenceRecord[];
  kind: "tool-output";
  toolEnvelope?: ToolResultEnvelope;
  toolInputStableDigest?: string;
  toolOutput: string;
  toolResultItem?: ToolResultItem;
};

export type ToolDispatchCheckpointPayload = PersistedToolResumeContext & {
  executionClass: ToolExecutionClass;
  idempotent: boolean;
  kind: "tool-dispatch";
  replayPolicy?: ToolReplayPolicy;
  toolInputStableDigest?: string;
};

export type SubagentSpawnCheckpointPayload = {
  childThreadId?: ThreadId;
  kind: "subagent-spawn";
};

export type SubagentJoinCheckpointPayload = {
  childThreadIds: ThreadId[];
  kind: "subagent-join";
};

export type ResponseOutputCheckpointPayload = {
  activeSkillIds: string[];
  agentText: string;
  kind: "response-output";
  planProjection?: PlanProjection;
  reasoningSummary?: string;
  reasoningText?: string;
};

export type VerificationVerdictCheckpointPayload = {
  activeSkillIds: string[];
  advancedPhase: AdvancedPhaseLoopState;
  evidenceId?: string;
  kind: "verification-verdict";
  phaseOutput: string;
  phaseResult?: PhaseResultEnvelope;
  sourceItemId: ItemId;
  verdictId: string;
};

export type PhaseTransitionCheckpointPayload = {
  activeSkillIds: string[];
  kind: "phase-transition";
  nextPhaseName: RunPhaseName;
  reopened?: boolean;
  state: AdvancedPhaseLoopState;
  transitionMessage?: string;
};

export type PendingCompactionFollowUpMarker = {
  id: string;
  operation: SessionUsageOperation;
};

export type ProviderCorrectionTrace = {
  code:
    | "PROVIDER_NO_ACTION"
    | "PROVIDER_DECISION_INVALID"
    | "TERMINAL_OUTPUT_INVALID"
    | "PROMPT_DELIVERABLE_MISSING";
  id: string;
  error?: {
    errorCode: string;
    message: string;
    recoverability: FailureRecoverability;
    scope: FailureScope;
  };
};

export type ProviderRequestReplayContext = {
  activeSkillIds?: string[];
  advancedPhase?: AdvancedPhaseLoopState;
  availableTools?: PersistedProviderToolOption[];
  compactionFollowUpMarker?: PendingCompactionFollowUpMarker;
  downgradeEligible?: boolean;
  escalationLevel?: number;
  objective?: string;
  payloadDecisionTransport?: ProviderDecisionTransport;
  phaseName?: RunPhaseName;
  profileName?: string;
  providerTurnBudgeted?: boolean;
  providerTurnCount?: number;
  correctionTrace?: ProviderCorrectionTrace;
  requiredToolConstraint?: RequiredToolConstraint;
  routeConfigOverride?: ProviderReplayConfig;
  toolStepCount?: number;
};

export type ProviderDecideCheckpointRequest = Omit<
  ProviderDecideInput,
  "actionPolicy" | "stream" | "toolNameBindings"
> & {
  actionPolicy?: ProviderTurnActionPolicy;
};
export type ProviderGenerateCheckpointRequest = Omit<
  Parameters<ModelProvider["generate"]>[0],
  "stream" | "toolNameBindings"
>;

export type ProviderRequestCheckpointPayload = {
  artifactObligations?: ArtifactObligation[];
  decisionTransport?: ProviderDecisionTransport;
  kind: "provider-request";
  model?: string;
  operation: SessionUsageOperation;
  providerConfig?: ProviderReplayConfig;
  providerKind?: ProviderKind;
  replayContext?: ProviderRequestReplayContext;
  correctionTrace?: ProviderCorrectionTrace;
  request?: PersistedProviderDecideCheckpointRequest | ProviderGenerateCheckpointRequest;
  requestKind?: "decide" | "generate";
};

export type ToolResumeCheckpointPayload =
  | ApprovalCheckpointPayload
  | ActionCheckpointPayload
  | ToolOutputCheckpointPayload;

type RunCheckpointKind = RunCheckpoint["kind"];

const ProviderKindSchema = z.enum([
  "openai",
  "openai-responses",
  "anthropic",
  "google",
  "xai",
  "xai-responses",
  "deepseek",
  "moonshotai",
  "aihubmix",
  "aihubmix-responses",
  "zenmux",
  "customProvider",
]);
const ProviderCompatibilityModeSchema = z.enum(["auto", "compatible", "native"]);
const LegacyProviderDecisionTransportSchema = z.enum(["native_tools", "strict_json_envelope"]);
const LegacyProviderDecisionTransportModeSchema = z.enum([
  "auto",
  "native_tools",
  "strict_json_envelope",
]);

function normalizePersistedDecisionTransport(
  _value: z.infer<typeof LegacyProviderDecisionTransportSchema>,
): ProviderDecisionTransport {
  return "native_tools";
}

function normalizePersistedDecisionTransportMode(
  value: z.infer<typeof LegacyProviderDecisionTransportModeSchema>,
): ProviderDecisionTransportMode {
  return value === "auto" ? "auto" : normalizePersistedDecisionTransport(value);
}

const ProviderDecisionTransportSchema: z.ZodType<ProviderDecisionTransport> =
  LegacyProviderDecisionTransportSchema.transform(normalizePersistedDecisionTransport);
const ProviderDecisionTransportModeSchema: z.ZodType<ProviderDecisionTransportMode> =
  LegacyProviderDecisionTransportModeSchema.transform(normalizePersistedDecisionTransportMode);
const ToolInstructionEvidenceSchema = z.strictObject({
  end: z.number(),
  parser: z.string(),
  start: z.number(),
  text: z.string(),
});
const ToolPolicyConflictSchema = z.strictObject({
  evidence: z.array(ToolInstructionEvidenceSchema),
  kind: z.enum([
    "allow-only-conflict",
    "allow-only-forbidden",
    "required-forbidden",
    "required-not-allowed",
  ]),
  toolNames: z.array(z.string()),
});
const ProviderTurnActionPolicySchema: z.ZodType<ProviderTurnActionPolicy> = z.strictObject({
  allowedActions: z.array(z.enum(["phase_result", "plan_update", "respond", "tool"])),
  directResponseAllowed: z.boolean(),
  required: z
    .union([
      z.strictObject({
        kind: z.literal("phase_result"),
        phaseName: z.string(),
        reason: z.string(),
      }),
      z.strictObject({
        kind: z.literal("plan_update"),
        reason: z.string(),
      }),
      z.strictObject({
        conflicts: z.array(ToolPolicyConflictSchema).optional(),
        evidence: z.array(ToolInstructionEvidenceSchema).optional(),
        kind: z.literal("tool"),
        reason: z.string(),
        requiredAllToolNames: z.array(z.string()).optional(),
        requiredAnyToolGroups: z.array(z.array(z.string())).optional(),
        requiredAnyToolNames: z.array(z.string()).optional(),
        selectableToolNames: z.array(z.string()).optional(),
        toolNames: z.array(z.string()),
      }),
    ])
    .optional(),
});
const ProviderReasoningEffortSchema = z.enum(["none", "minimal", "low", "medium", "high", "xhigh"]);
const FailureRecoverabilitySchema = z.enum([
  "retryable",
  "repairable",
  "blocked",
  "interrupted",
  "terminal",
  "unknown",
]);
const FailureScopeSchema = z.enum([
  "turn",
  "provider",
  "tool",
  "approval",
  "workflow",
  "child-thread",
]);
const SessionUsageOperationSchema = z.enum([
  "compaction",
  "decide",
  "deep-research-subquestion",
  "deep-research-synthesis",
  "generate",
  "memory-refresh",
  "manual-compact-followup",
  "post-compact-followup",
  "repair",
  "tool-followup",
  "steer",
]);
const RunPhaseNameSchema = z.enum(["inspect", "plan", "change", "verify", "summarize", "wait"]);
const PhaseResultPhaseNameSchema = z.enum([
  "inspect",
  "plan",
  "change",
  "explore",
  "build",
  "verify",
]);
const PlanStatusSchema: z.ZodType<PlanStatus> = z.enum([
  "draft",
  "review_ready",
  "approved",
  "rejected",
  "superseded",
  "executing",
  "completed",
]);
const PlanTodoStatusSchema: z.ZodType<PlanTodoStatus> = z.enum([
  "pending",
  "in_progress",
  "blocked",
  "done",
  "cancelled",
]);
const VerdictStatusSchema: z.ZodType<VerdictStatus> = z.enum([
  "satisfied",
  "unsatisfied",
  "inconclusive",
  "blocked",
]);
const ToolResultOutcomeSchema = z.enum(["success", "error"]);
const ToolFailureKindSchema = z.enum([
  "interrupted",
  "denied",
  "validation-failed",
  "dispatch-failed",
  "execution-failed",
  "heartbeat-timeout",
  "stale-result-rejected",
  "replay-disallowed",
  "verification-failed",
]);
const ToolInputValueSchema: z.ZodType<ToolInputValue> = z.lazy(() =>
  z.union([
    z.string(),
    z.number(),
    z.boolean(),
    z.null(),
    z.array(ToolInputValueSchema),
    z.record(z.string(), ToolInputValueSchema),
  ]),
);
const ProviderToolNameSchema = z.string().refine(isPersistableProviderToolName);
const PersistedProviderToolNameSchema = z.preprocess(
  (value) =>
    typeof value === "string" && isPersistableProviderToolName(value) ? value : undefined,
  ProviderToolNameSchema.optional(),
);
const ActionIdSchema = z.string().transform((value): ActionId => value as ActionId);
const AssetIdSchema = z.string().transform((value): AssetId => value as AssetId);
const ItemIdSchema = z.string().transform((value): ItemId => value as ItemId);
const ThreadIdSchema = z.string().transform((value): ThreadId => value as ThreadId);
const TurnIdSchema = z.string().transform((value): TurnId => value as TurnId);
const ApprovalIdSchema = z
  .string()
  .transform((value): ApprovalRequest["id"] => value as ApprovalRequest["id"]);
const PromptContentPartSchema: z.ZodType<PromptContentPart> = z.union([
  z.strictObject({
    text: z.string(),
    type: z.literal("text"),
  }),
  z.strictObject({
    filename: z.string().optional(),
    image: z.string().optional(),
    mediaType: z.string().optional(),
    path: z.string().optional(),
    type: z.literal("image"),
  }),
  z.strictObject({
    data: z.string().optional(),
    filename: z.string().optional(),
    mediaType: z.string().optional(),
    path: z.string().optional(),
    type: z.literal("file"),
  }),
]);
const ConversationToolCallSchema: z.ZodType<ConversationToolCall> = z.strictObject({
  input: ToolInputValueSchema,
  inputDigest: z.string().optional(),
  inputStableDigest: z.string().optional(),
  providerToolName: PersistedProviderToolNameSchema,
  toolCallId: z.string(),
  toolName: z.string(),
});
const ConversationMessageSchema: z.ZodType<ConversationMessage> = z.union([
  z.strictObject({
    content: z.string(),
    contentParts: z.array(PromptContentPartSchema).optional(),
    role: z.literal("user"),
  }),
  z.strictObject({
    content: z.string(),
    role: z.literal("assistant"),
    toolCalls: z.array(ConversationToolCallSchema).optional(),
  }),
  z.strictObject({
    code: z.string(),
    content: z.string(),
    role: z.literal("runtime"),
    severity: z.enum(["warning", "error"]),
  }),
  z.strictObject({
    content: z.string(),
    errorCode: z.string().optional(),
    failureKind: ToolFailureKindSchema.optional(),
    outcome: ToolResultOutcomeSchema,
    providerToolName: PersistedProviderToolNameSchema,
    role: z.literal("tool"),
    toolCallId: z.string(),
    toolName: z.string(),
  }),
]);
const ProviderCacheControlSchema: z.ZodType<ProviderCacheControl> = z.strictObject({
  stablePrefixText: z.string().optional(),
  stablePrefixTokens: z.number().optional(),
});
const ProviderToolOptionSchema: z.ZodType<PersistedProviderToolOption> = z.object({
  description: z.string(),
  effect: z.enum(["read", "search", "write", "execute", "verify", "process"]).optional(),
  inputSchema: z.unknown().optional(),
  name: z.string(),
});
const ClosurePolicySchema: z.ZodType<ClosurePolicy> = z.strictObject({
  deterministicSufficient: z.boolean(),
  humanVerdictRequired: z.boolean(),
  modelVerdictAllowed: z.boolean(),
  onInconclusive: z.enum(["reopen", "block", "escalate", "expand"]),
  requiredAgreement: z.enum([
    "any-one",
    "deterministic-and-human",
    "deterministic-and-model",
    "human-and-model",
    "all-allowed",
  ]),
});
const AdvancedPhaseLoopStateSchema: z.ZodType<AdvancedPhaseLoopState> = z.strictObject({
  closurePolicy: ClosurePolicySchema,
  hostProfileName: z.string(),
  summaryProfileName: z.string(),
  threadMode: z.enum(["default", "plan"]),
});
const PlanTodoSchema: z.ZodType<PlanTodo> = z.strictObject({
  content: z.string(),
  dependsOn: z.array(z.string()),
  id: z.string(),
  kind: z.enum(PLAN_TODO_KINDS),
  owner: z.string(),
  status: PlanTodoStatusSchema,
});
const PlanProjectionSchema: z.ZodType<PlanProjection> = z.strictObject({
  acceptanceCriteria: z.array(z.string()),
  affectedPaths: z.array(z.string()),
  approvedRevision: z.number().optional(),
  evidenceRefs: z.array(z.string()),
  graphId: z.string(),
  graphRevision: z.number(),
  openQuestions: z.array(z.string()),
  rejectedRevision: z.number().optional(),
  risks: z.array(z.string()),
  rootNodeId: z.string().optional(),
  status: PlanStatusSchema,
  summary: z.string(),
  threadId: ThreadIdSchema,
  title: z.string(),
  todos: z.array(PlanTodoSchema),
  updatedAt: z.string(),
});
const PhaseResultCandidateVerdictSchema: z.ZodType<
  NonNullable<PhaseResultEnvelope["candidateVerdict"]>
> = z.strictObject({
  blockingReason: z.string().optional(),
  status: VerdictStatusSchema,
  summary: z.string(),
});
const PhaseResultEnvelopeSchema: z.ZodType<PhaseResultEnvelope> = z.strictObject({
  artifactRefs: z.array(z.string()).optional(),
  candidateVerdict: PhaseResultCandidateVerdictSchema.optional(),
  phaseName: PhaseResultPhaseNameSchema,
  source: z
    .enum(["native_tools", "runtime", "strict_json_envelope"])
    .transform((source): PhaseResultEnvelope["source"] =>
      source === "strict_json_envelope" ? "native_tools" : source,
    ),
  summary: z.string(),
  supportsEvidenceIds: z.array(z.string()),
});
const ProviderGenerateCheckpointRequestBaseSchema = z.strictObject({
  cacheControl: ProviderCacheControlSchema.optional(),
  messages: z.array(ConversationMessageSchema),
  systemPrompt: z.string().optional(),
});
const ProviderGenerateCheckpointRequestSchema: z.ZodType<ProviderGenerateCheckpointRequest> =
  ProviderGenerateCheckpointRequestBaseSchema;
const ProviderDecideCheckpointRequestSchema: z.ZodType<PersistedProviderDecideCheckpointRequest> =
  ProviderGenerateCheckpointRequestBaseSchema.extend({
    actionPolicy: ProviderTurnActionPolicySchema.optional(),
    allowPhaseResult: z.boolean().optional(),
    allowPlanUpdate: z.boolean().optional(),
    decisionTransport: ProviderDecisionTransportSchema,
    phaseResultPhaseName: PhaseResultPhaseNameSchema.optional(),
    toolUsePolicy: z
      // TODO: drop tool-first-implementation after legacy checkpoints are migrated.
      .enum(["answer-when-sufficient", "tool-first-implementation", "required-tool-only"])
      .optional(),
    tools: z.array(ProviderToolOptionSchema),
  });
const ProviderCorrectionTraceSchema: z.ZodType<ProviderCorrectionTrace> = z
  .strictObject({
    code: z.enum([
      "PROVIDER_NO_ACTION",
      "PROVIDER_DECISION_INVALID",
      "TERMINAL_OUTPUT_INVALID",
      "PROMPT_DELIVERABLE_MISSING",
    ]),
    error: z
      .strictObject({
        errorCode: z.string(),
        message: z.string(),
        recoverability: FailureRecoverabilitySchema,
        scope: FailureScopeSchema,
      })
      .optional(),
    id: z.string(),
    prompt: z.string().optional(),
  })
  .transform(({ prompt: _legacyPrompt, ...value }) => value);
const ArtifactSizeLimitSchema = z.strictObject({
  bytes: z.number(),
  inclusive: z.boolean(),
});
const ArtifactObligationConstraintSchema: z.ZodType<ArtifactObligationConstraint> = z.union([
  z.strictObject({
    bytes: z.number(),
    inclusive: z.boolean(),
    kind: z.literal("max-bytes"),
  }),
  z.strictObject({
    allowedSiblingBasenames: z.array(z.string()),
    directory: z.string(),
    kind: z.literal("exclusive-directory"),
  }),
]);
const ArtifactObligationSchema: z.ZodType<ArtifactObligation> = z
  .strictObject({
    allowedSiblingBasenames: z.array(z.string()).optional(),
    constraints: z.array(ArtifactObligationConstraintSchema).optional(),
    exclusiveDirectory: z.string().optional(),
    initialDigest: z.string().optional(),
    kind: z.enum([
      "write-target",
      "modify-target",
      "run-command",
      "collect-evidence",
      "structured-answer",
    ]),
    maxBytes: z.union([z.number(), ArtifactSizeLimitSchema]).optional(),
    obligationId: z.string(),
    required: z.boolean(),
    target: z.string().optional(),
    verifier: z.enum([
      "exists",
      "changed",
      "command-zero-exit",
      "evidence-present",
      "structured-output",
      "semantic-only",
    ]),
  })
  .transform((obligation): ArtifactObligation => {
    const constraints = [
      ...(obligation.constraints ?? []),
      ...legacyArtifactObligationConstraints(obligation),
    ];
    return {
      ...(constraints.length > 0 ? { constraints } : {}),
      ...(obligation.initialDigest ? { initialDigest: obligation.initialDigest } : {}),
      kind: obligation.kind,
      obligationId: obligation.obligationId,
      required: obligation.required,
      ...(obligation.target ? { target: obligation.target } : {}),
      verifier: obligation.verifier,
    };
  });
function legacyArtifactObligationConstraints(input: {
  allowedSiblingBasenames?: string[];
  exclusiveDirectory?: string;
  maxBytes?: number | { bytes: number; inclusive: boolean };
}): ArtifactObligationConstraint[] {
  const constraints: ArtifactObligationConstraint[] = [];
  if (input.maxBytes !== undefined) {
    constraints.push(
      typeof input.maxBytes === "number"
        ? { kind: "max-bytes", bytes: input.maxBytes, inclusive: true }
        : {
            kind: "max-bytes",
            bytes: input.maxBytes.bytes,
            inclusive: input.maxBytes.inclusive,
          },
    );
  }
  if (input.exclusiveDirectory && input.allowedSiblingBasenames) {
    constraints.push({
      kind: "exclusive-directory",
      directory: input.exclusiveDirectory,
      allowedSiblingBasenames: input.allowedSiblingBasenames,
    });
  }
  return constraints;
}
const ProviderReplayConfigShape = {
  baseURL: z.string().optional(),
  compatibilityMode: ProviderCompatibilityModeSchema.optional(),
  decisionTransport: ProviderDecisionTransportModeSchema.optional(),
  kind: ProviderKindSchema,
  maxInputTokens: z.number().optional(),
  maxOutputTokens: z.number().optional(),
  model: z.string(),
  reasoningEffort: ProviderReasoningEffortSchema.optional(),
  systemPrompt: z.string().optional(),
  timeoutMs: z.number().optional(),
};
const ProviderReplayConfigInputSchema: z.ZodType<ProviderReplayConfig> = z
  .strictObject({
    apiKey: z.string().optional(),
    ...ProviderReplayConfigShape,
  })
  .transform(({ apiKey: _apiKey, ...config }): ProviderReplayConfig => config);
const PendingCompactionFollowUpMarkerSchema: z.ZodType<PendingCompactionFollowUpMarker> =
  z.strictObject({
    id: z.string(),
    operation: SessionUsageOperationSchema,
  });
const ToolReplayPolicySchema: z.ZodType<ToolReplayPolicy> = z.enum([
  "auto-if-idempotent",
  "repair-only",
  "never",
]);
const ToolExecutionClassSchema: z.ZodType<ToolExecutionClass> = z.enum([
  "read-only",
  "workspace-mutation",
  "external-side-effect",
]);
const PendingProviderToolCallSchema: z.ZodType<PendingProviderToolCall> = z.strictObject({
  providerToolName: PersistedProviderToolNameSchema,
  replayToolInputAssetId: AssetIdSchema.optional(),
  replayToolInputDigest: z.string().optional(),
  toolCallId: ItemIdSchema,
  toolInput: ToolInputValueSchema,
  toolName: z.string(),
});
const CompletedProviderToolCallSchema: z.ZodType<CompletedProviderToolCall> = z
  .strictObject({
    providerToolName: PersistedProviderToolNameSchema,
    replayToolInputAssetId: AssetIdSchema.optional(),
    replayToolInputDigest: z.string().optional(),
    errorCode: z.string().optional(),
    failureKind: ToolFailureKindSchema.optional(),
    inputDigest: z.string().optional(),
    inputStableDigest: z.string().optional(),
    outcome: ToolResultOutcomeSchema,
    replayPolicy: ToolReplayPolicySchema.optional(),
    toolCallId: ItemIdSchema,
    toolInput: ToolInputValueSchema,
    toolName: z.string(),
    toolOutput: z.string(),
  })
  .transform(
    ({ inputDigest, inputStableDigest, ...call }): CompletedProviderToolCall => ({
      ...call,
      ...((inputStableDigest ?? inputDigest)
        ? { inputStableDigest: inputStableDigest ?? inputDigest }
        : {}),
    }),
  );
function uniqueStrings(values: readonly string[]): string[] {
  return [...new Set(values)].filter((value) => value.length > 0);
}

function normalizePersistedRequiredAnyToolGroups(input: {
  requiredAnyToolGroups?: string[][];
  requiredAnyToolNames?: string[];
}): string[][] {
  const normalizedGroups: string[][] = [];

  for (const group of [
    ...(input.requiredAnyToolGroups ?? []),
    ...(input.requiredAnyToolNames ? [input.requiredAnyToolNames] : []),
  ]) {
    const normalizedGroup = uniqueStrings(group);
    if (
      normalizedGroup.length > 1 &&
      !normalizedGroups.some(
        (existingGroup) =>
          existingGroup.length === normalizedGroup.length &&
          existingGroup.every((toolName, index) => toolName === normalizedGroup[index]),
      )
    ) {
      normalizedGroups.push(normalizedGroup);
    }
  }

  return normalizedGroups;
}

function buildAggregateRequiredToolNames(input: {
  requiredAllToolNames: readonly string[];
  requiredAnyToolGroups: readonly string[][];
  requiredToolNames: readonly string[];
}): string[] {
  const aggregate: string[] = [];
  const add = (toolName: string | undefined) => {
    if (toolName && !aggregate.includes(toolName)) {
      aggregate.push(toolName);
    }
  };

  for (const toolName of input.requiredToolNames) {
    add(toolName);
  }
  for (const toolName of input.requiredAllToolNames) {
    add(toolName);
  }
  for (const group of input.requiredAnyToolGroups) {
    if (!aggregate.some((toolName) => group.includes(toolName))) {
      add(group[0]);
    }
  }

  return aggregate;
}

const RequiredToolConstraintSchema: z.ZodType<RequiredToolConstraint> = z
  .strictObject({
    allowedToolNames: z.array(z.string()).optional(),
    conflicts: z.array(ToolPolicyConflictSchema).optional(),
    evidence: z.array(ToolInstructionEvidenceSchema).optional(),
    forbiddenToolNames: z.array(z.string()),
    primaryToolName: z.string().optional(),
    reason: z.enum([
      "explicit-exclusive-tool-set",
      "explicit-forbidden-tool",
      "explicit-required-tool",
      "repo-wide-coverage",
      "requested-subagent",
    ]),
    requiredAllToolNames: z.array(z.string()).optional(),
    requiredAnyToolGroups: z.array(z.array(z.string())).optional(),
    requiredAnyToolNames: z.array(z.string()).optional(),
    requiredReleasePolicy: z.enum(["after-required-tools-succeeded", "none"]),
    requiredToolNames: z.array(z.string()),
    restrictionScope: z.enum(["turn", "until-required-satisfied"]),
    selectableToolNames: z.array(z.string()).optional(),
    visibleToolNames: z.array(z.string()).optional(),
  })
  .transform((constraint): RequiredToolConstraint => {
    const legacyAllowedToolNames = constraint.allowedToolNames;
    const requiredAnyToolGroups = normalizePersistedRequiredAnyToolGroups(constraint);
    const legacyRequiredAnyToolNames = new Set(requiredAnyToolGroups.flat());
    const requiredAllToolNames = constraint.requiredAllToolNames
      ? uniqueStrings(constraint.requiredAllToolNames)
      : uniqueStrings(
          constraint.requiredToolNames.filter(
            (toolName) => !legacyRequiredAnyToolNames.has(toolName),
          ),
        );
    const requiredToolNames = buildAggregateRequiredToolNames({
      requiredAllToolNames,
      requiredAnyToolGroups,
      requiredToolNames: constraint.requiredToolNames,
    });
    return {
      ...(constraint.conflicts ? { conflicts: constraint.conflicts } : {}),
      ...(constraint.evidence ? { evidence: constraint.evidence } : {}),
      forbiddenToolNames: constraint.forbiddenToolNames,
      ...(constraint.primaryToolName ? { primaryToolName: constraint.primaryToolName } : {}),
      reason: constraint.reason,
      ...(requiredAllToolNames.length > 0 ? { requiredAllToolNames } : {}),
      ...(requiredAnyToolGroups.length > 0 ? { requiredAnyToolGroups } : {}),
      requiredReleasePolicy: constraint.requiredReleasePolicy,
      requiredToolNames,
      restrictionScope: constraint.restrictionScope,
      ...((constraint.selectableToolNames ?? legacyAllowedToolNames)
        ? { selectableToolNames: constraint.selectableToolNames ?? legacyAllowedToolNames }
        : {}),
      ...((constraint.visibleToolNames ?? legacyAllowedToolNames)
        ? { visibleToolNames: constraint.visibleToolNames ?? legacyAllowedToolNames }
        : {}),
    };
  });
const ArtifactRefSchema: z.ZodType<ArtifactRef> = z
  .strictObject({
    assetId: z.string(),
    contentType: z.string().optional(),
    kind: z.enum(["attachment", "artifact"]).optional(),
    name: z.string().optional(),
  })
  .transform(
    (ref): ArtifactRef => ({
      assetId: ref.assetId as AssetId,
      ...(ref.contentType ? { contentType: ref.contentType } : {}),
      ...(ref.kind ? { kind: ref.kind } : {}),
      ...(ref.name ? { name: ref.name } : {}),
    }),
  );
const ToolCallRefSchema: z.ZodType<ToolCallRef> = z
  .strictObject({
    attemptId: z.string().optional(),
    runId: z.string().optional(),
    stepId: z.string().optional(),
    threadId: z.string(),
    toolCallId: z.string(),
    turnId: z.string(),
  })
  .transform(
    (ref): ToolCallRef => ({
      ...(ref.attemptId ? { attemptId: ref.attemptId as ToolCallRef["attemptId"] } : {}),
      ...(ref.runId ? { runId: ref.runId as ToolCallRef["runId"] } : {}),
      ...(ref.stepId ? { stepId: ref.stepId as ToolCallRef["stepId"] } : {}),
      threadId: ref.threadId as ToolCallRef["threadId"],
      toolCallId: ref.toolCallId as ToolCallRef["toolCallId"],
      turnId: ref.turnId as ToolCallRef["turnId"],
    }),
  );
const ToolResultPresentationSchema = z.strictObject({
  kind: z.enum(["inline", "artifact", "mixed"]),
  title: z.string().optional(),
});
const ToolResultItemSchema: z.ZodType<ToolResultItem> = z.strictObject({
  callId: ItemIdSchema,
  createdAt: z.string(),
  errorCode: z.string().optional(),
  failureKind: ToolFailureKindSchema.optional(),
  id: ItemIdSchema,
  kind: z.literal("tool-result"),
  outcome: ToolResultOutcomeSchema,
  output: z.string(),
  structuredContent: z.record(z.string(), z.unknown()).optional(),
  threadId: ThreadIdSchema,
  toolCallRef: ToolCallRefSchema.optional(),
  toolName: z.string(),
  traceAssetId: AssetIdSchema.optional(),
  turnId: TurnIdSchema,
});
const ToolResultEnvelopeSchema: z.ZodType<ToolResultEnvelope> = z
  .strictObject({
    artifactRefs: z.array(ArtifactRefSchema).optional(),
    evidencePayload: z.record(z.string(), z.unknown()).optional(),
    failureKind: ToolFailureKindSchema.optional(),
    inputDigest: z.string().optional(),
    inputRawDigest: z.string().optional(),
    inputStableDigest: z.string().optional(),
    outcome: ToolResultOutcomeSchema,
    presentation: ToolResultPresentationSchema.optional(),
    rawText: z.string().optional(),
    stderrSummary: z.string().optional(),
    stdoutSummary: z.string().optional(),
    summary: z.string(),
    toolCallId: ItemIdSchema,
    toolCallRef: ToolCallRefSchema.optional(),
    toolName: z.string(),
    touchedFilePaths: z.array(z.string()).optional(),
    verificationHints: z.array(z.string()).optional(),
  })
  .refine((envelope) => envelope.inputRawDigest || envelope.inputDigest, {
    message: "Tool result envelope requires inputRawDigest.",
  })
  .transform(
    ({ inputDigest, inputRawDigest, ...envelope }): ToolResultEnvelope => ({
      ...envelope,
      inputRawDigest: inputRawDigest ?? inputDigest!,
    }),
  );
const ThreadEvidenceRecordSchema: z.ZodType<ThreadEvidenceRecord> = z
  .strictObject({
    artifactIds: z.array(z.string()).optional(),
    artifactRefs: z.array(ArtifactRefSchema).optional(),
    attemptId: z.string().optional(),
    confidence: z.number(),
    contradictsEvidenceIds: z.array(z.string()).optional(),
    createdAt: z.string(),
    diagnosticCode: z.string().optional(),
    evidenceId: z.string(),
    freshness: z.string(),
    kind: z.enum([
      "tool-outcome",
      "verification-verdict",
      "artifact-fact",
      "workspace-fact",
      "research-finding",
      "handoff-fact",
    ]),
    payload: z.record(z.string(), z.unknown()),
    relatedFilePaths: z.array(z.string()).optional(),
    runId: z.string().optional(),
    sourceItemId: z.string().optional(),
    sourceToolName: z.string().optional(),
    stepId: z.string().optional(),
    summary: z.string(),
    supportsClaimIds: z.array(z.string()).optional(),
    threadId: z.string(),
    toolCallRef: ToolCallRefSchema.optional(),
    turnId: z.string(),
  })
  .transform(
    (record): ThreadEvidenceRecord => ({
      ...(record.artifactIds
        ? { artifactIds: record.artifactIds.map((artifactId) => artifactId as AssetId) }
        : {}),
      ...(record.artifactRefs ? { artifactRefs: record.artifactRefs } : {}),
      ...(record.attemptId
        ? { attemptId: record.attemptId as ThreadEvidenceRecord["attemptId"] }
        : {}),
      confidence: record.confidence,
      ...(record.contradictsEvidenceIds
        ? { contradictsEvidenceIds: record.contradictsEvidenceIds }
        : {}),
      createdAt: record.createdAt,
      ...(record.diagnosticCode ? { diagnosticCode: record.diagnosticCode } : {}),
      evidenceId: record.evidenceId,
      freshness: record.freshness,
      kind: record.kind,
      payload: record.payload,
      ...(record.relatedFilePaths ? { relatedFilePaths: record.relatedFilePaths } : {}),
      ...(record.runId ? { runId: record.runId } : {}),
      ...(record.sourceItemId
        ? { sourceItemId: record.sourceItemId as ThreadEvidenceRecord["sourceItemId"] }
        : {}),
      ...(record.sourceToolName ? { sourceToolName: record.sourceToolName } : {}),
      ...(record.stepId ? { stepId: record.stepId as ThreadEvidenceRecord["stepId"] } : {}),
      summary: record.summary,
      ...(record.supportsClaimIds ? { supportsClaimIds: record.supportsClaimIds } : {}),
      threadId: record.threadId as ThreadEvidenceRecord["threadId"],
      ...(record.toolCallRef ? { toolCallRef: record.toolCallRef } : {}),
      turnId: record.turnId as ThreadEvidenceRecord["turnId"],
    }),
  );
const ApprovalGrantScopeSchema: z.ZodType<ApprovalGrantScope> = z.enum([
  "once",
  "session",
  "thread",
  "workspace",
]);
const CapabilityKindSchema = z.enum([
  "tool",
  "filesystem",
  "shell",
  "network",
  "mcp",
  "plugin-hook",
]);
const CapabilityRequestBaseSchema = z.strictObject({
  approvalScopes: z.array(ApprovalGrantScopeSchema).optional(),
  kind: CapabilityKindSchema.optional(),
  reason: z.string().optional(),
});
const CapabilityRequestSchema: z.ZodType<CapabilityRequest> = z.union([
  CapabilityRequestBaseSchema.extend({
    group: z.literal("tool"),
    toolName: z.string(),
  }),
  CapabilityRequestBaseSchema.extend({
    action: z.enum(["read", "write"]),
    group: z.literal("filesystem"),
    path: z.string(),
    roots: z.array(z.string()).optional(),
  }),
  CapabilityRequestBaseSchema.extend({
    argv: z.array(z.string()),
    cwd: z.string(),
    group: z.literal("shell"),
  }),
  CapabilityRequestBaseSchema.extend({
    group: z.literal("network"),
    host: z.string(),
  }),
]);
const GuardianAssessmentSchema: z.ZodType<GuardianAssessment> = z.strictObject({
  evidence: z.array(z.string()),
  rationale: z.string(),
  riskLevel: z.enum(["low", "medium", "high"]),
  riskScore: z.number(),
});
const ApprovalRequestSchema: z.ZodType<ApprovalRequest> = z.strictObject({
  action: z.string(),
  availableScopes: z.array(ApprovalGrantScopeSchema),
  group: z.enum(["tool", "filesystem", "shell", "network"]),
  guardianAssessment: GuardianAssessmentSchema.optional(),
  id: ApprovalIdSchema,
  kind: CapabilityKindSchema,
  reason: z.string().optional(),
  requestedAt: z.string(),
  reviewMode: z.enum(["direct", "guardian"]),
  riskClass: z.enum(["low", "medium", "high"]),
  sandboxProfile: z.enum(["none", "no-network", "no-write", "no-network-no-write"]),
  threadId: ThreadIdSchema,
  toolCallId: ItemIdSchema,
  toolCallRef: ToolCallRefSchema.optional(),
  turnId: TurnIdSchema,
});
const ApprovalResolutionSchema: z.ZodType<ApprovalResolution> = z.strictObject({
  decision: z.enum(["allow", "reject", "expired"]),
  scope: ApprovalGrantScopeSchema,
});
const AskUserOptionSchema = z.strictObject({
  description: z.string().optional(),
  label: z.string(),
});
const AskUserQuestionSchema: z.ZodType<AskUserQuestion> = z.union([
  z.strictObject({
    allowCustom: z.boolean().optional(),
    header: z.string().optional(),
    id: z.string(),
    kind: z.literal("choice").optional(),
    multiSelect: z.boolean().optional(),
    options: z.array(AskUserOptionSchema),
    question: z.string(),
  }),
  z.strictObject({
    header: z.string().optional(),
    id: z.string(),
    kind: z.literal("input"),
    multiline: z.boolean().optional(),
    placeholder: z.string().optional(),
    question: z.string(),
  }),
]);
const ActionResolutionSchema: z.ZodType<ActionResolution> = z.union([
  z.strictObject({
    answers: z.record(z.string(), z.union([z.string(), z.array(z.string())])),
    status: z.literal("answered"),
  }),
  z.strictObject({
    status: z.literal("expired"),
  }),
]);
const PersistedToolResumeContextShape = {
  activeSkillIds: z.array(z.string()),
  activeReplayToolInputAssetId: AssetIdSchema.optional(),
  activeReplayToolInputDigest: z.string().optional(),
  allowPhaseResult: z.boolean().optional(),
  allowPlanUpdate: z.boolean().optional(),
  advancedPhase: AdvancedPhaseLoopStateSchema.optional(),
  artifactObligations: z.array(ArtifactObligationSchema).optional(),
  approvalTurnId: TurnIdSchema.optional(),
  availableTools: z.array(ProviderToolOptionSchema),
  cacheControl: ProviderCacheControlSchema.optional(),
  completedToolCalls: z.array(CompletedProviderToolCallSchema).optional(),
  customLoopId: z.string().optional(),
  decisionTransport: ProviderDecisionTransportSchema.optional(),
  fixedRouteConfigOverride: z.boolean().optional(),
  messages: z.array(ConversationMessageSchema),
  mode: z.enum(["provider-tool-loop", "tool-plan", "custom-loop"]),
  objective: z.string().optional(),
  pendingToolCalls: z.array(PendingProviderToolCallSchema).optional(),
  phaseName: RunPhaseNameSchema.optional(),
  phaseResultPhaseName: PhaseResultPhaseNameSchema.optional(),
  profileName: z.string(),
  providerTurnBudgeted: z.boolean().optional(),
  providerTurnCount: z.number().optional(),
  reasoning: z.string().optional(),
  repairAttempt: z.number().optional(),
  executionFailureCount: z.number().optional(),
  requiredToolConstraint: RequiredToolConstraintSchema.optional(),
  routeConfigOverride: ProviderReplayConfigInputSchema.optional(),
  systemPrompt: z.string().optional(),
  toolCallId: ItemIdSchema,
  toolInput: ToolInputValueSchema,
  toolName: z.string(),
  providerToolName: PersistedProviderToolNameSchema,
  toolStepCount: z.number(),
};
const ApprovalCheckpointPayloadSchema: z.ZodType<ApprovalCheckpointPayload> = z.strictObject({
  ...PersistedToolResumeContextShape,
  approval: ApprovalRequestSchema,
  approvalGrantAction: z.string().optional(),
  approvalGrantActionSource: z.enum(["legacy", "raw-request-digest"]).optional(),
  kind: z.literal("approval"),
  request: CapabilityRequestSchema,
  resolution: ApprovalResolutionSchema.optional(),
});
const ActionCheckpointPayloadSchema: z.ZodType<ActionCheckpointPayload> = z.strictObject({
  ...PersistedToolResumeContextShape,
  actionId: ActionIdSchema,
  kind: z.literal("action"),
  questions: z.array(AskUserQuestionSchema),
  resolution: ActionResolutionSchema.optional(),
});
const ToolOutputCheckpointPayloadSchema: z.ZodType<ToolOutputCheckpointPayload> = z.strictObject({
  ...PersistedToolResumeContextShape,
  evidenceRecords: z.array(ThreadEvidenceRecordSchema).optional(),
  kind: z.literal("tool-output"),
  toolEnvelope: ToolResultEnvelopeSchema.optional(),
  toolInputStableDigest: z.string().optional(),
  toolOutput: z.string(),
  toolResultItem: ToolResultItemSchema.optional(),
});
const ToolDispatchCheckpointPayloadSchema: z.ZodType<ToolDispatchCheckpointPayload> =
  z.strictObject({
    ...PersistedToolResumeContextShape,
    executionClass: ToolExecutionClassSchema,
    idempotent: z.boolean(),
    kind: z.literal("tool-dispatch"),
    replayPolicy: ToolReplayPolicySchema.optional(),
    toolInputStableDigest: z.string().optional(),
  });
const SubagentSpawnCheckpointPayloadSchema: z.ZodType<SubagentSpawnCheckpointPayload> =
  z.strictObject({
    childThreadId: ThreadIdSchema.optional(),
    kind: z.literal("subagent-spawn"),
  });
const SubagentJoinCheckpointPayloadSchema: z.ZodType<SubagentJoinCheckpointPayload> =
  z.strictObject({
    childThreadIds: z.array(ThreadIdSchema),
    kind: z.literal("subagent-join"),
  });
const ResponseOutputCheckpointPayloadSchema: z.ZodType<ResponseOutputCheckpointPayload> =
  z.strictObject({
    activeSkillIds: z.array(z.string()),
    agentText: z.string(),
    kind: z.literal("response-output"),
    planProjection: PlanProjectionSchema.optional(),
    reasoningSummary: z.string().optional(),
    reasoningText: z.string().optional(),
  });
const VerificationVerdictCheckpointPayloadSchema: z.ZodType<VerificationVerdictCheckpointPayload> =
  z.strictObject({
    activeSkillIds: z.array(z.string()),
    advancedPhase: AdvancedPhaseLoopStateSchema,
    evidenceId: z.string().optional(),
    kind: z.literal("verification-verdict"),
    phaseOutput: z.string(),
    phaseResult: PhaseResultEnvelopeSchema.optional(),
    sourceItemId: ItemIdSchema,
    verdictId: z.string(),
  });
const PhaseTransitionCheckpointPayloadSchema: z.ZodType<PhaseTransitionCheckpointPayload> =
  z.strictObject({
    activeSkillIds: z.array(z.string()),
    kind: z.literal("phase-transition"),
    nextPhaseName: RunPhaseNameSchema,
    reopened: z.boolean().optional(),
    state: AdvancedPhaseLoopStateSchema,
    transitionMessage: z.string().optional(),
  });
const ProviderRequestReplayContextSchema: z.ZodType<ProviderRequestReplayContext> = z.strictObject({
  activeSkillIds: z.array(z.string()).optional(),
  advancedPhase: AdvancedPhaseLoopStateSchema.optional(),
  availableTools: z.array(ProviderToolOptionSchema).optional(),
  compactionFollowUpMarker: PendingCompactionFollowUpMarkerSchema.optional(),
  downgradeEligible: z.boolean().optional(),
  escalationLevel: z.number().optional(),
  objective: z.string().optional(),
  payloadDecisionTransport: ProviderDecisionTransportSchema.optional(),
  phaseName: RunPhaseNameSchema.optional(),
  profileName: z.string().optional(),
  providerTurnBudgeted: z.boolean().optional(),
  providerTurnCount: z.number().optional(),
  correctionTrace: ProviderCorrectionTraceSchema.optional(),
  requiredToolConstraint: RequiredToolConstraintSchema.optional(),
  routeConfigOverride: ProviderReplayConfigInputSchema.optional(),
  toolStepCount: z.number().optional(),
});
const ProviderRequestCheckpointPayloadBaseSchema = z.strictObject({
  artifactObligations: z.array(ArtifactObligationSchema).optional(),
  decisionTransport: ProviderDecisionTransportSchema.optional(),
  kind: z.literal("provider-request"),
  model: z.string().optional(),
  operation: SessionUsageOperationSchema,
  providerConfig: ProviderReplayConfigInputSchema.optional(),
  providerKind: ProviderKindSchema.optional(),
  replayContext: ProviderRequestReplayContextSchema.optional(),
  correctionTrace: ProviderCorrectionTraceSchema.optional(),
});
const ProviderRequestCheckpointPayloadSchema: z.ZodType<ProviderRequestCheckpointPayload> = z.union(
  [
    ProviderRequestCheckpointPayloadBaseSchema.extend({
      request: z.undefined().optional(),
      requestKind: z.undefined().optional(),
    }),
    ProviderRequestCheckpointPayloadBaseSchema.extend({
      request: ProviderDecideCheckpointRequestSchema,
      requestKind: z.literal("decide"),
    }),
    ProviderRequestCheckpointPayloadBaseSchema.extend({
      request: ProviderGenerateCheckpointRequestSchema,
      requestKind: z.literal("generate"),
    }),
  ],
);

const RUN_CHECKPOINT_ALLOWED_PREDECESSORS: Partial<
  Record<RunCheckpointKind, readonly RunCheckpointKind[]>
> = {
  "provider-request": [
    "phase-transition",
    "provider-request",
    "tool-output",
    "verification-verdict",
  ],
};

function toThreadMetadata(payload: object): ThreadMetadata {
  const metadata: ThreadMetadata = {};
  for (const [key, value] of Object.entries(payload)) {
    metadata[key] = value;
  }
  return metadata;
}

function encodeCheckpointPayload<T extends object>(
  schema: z.ZodType<T>,
  payload: T,
): ThreadMetadata {
  return toThreadMetadata(schema.parse(payload));
}

export function serializeApprovalCheckpointPayload(
  payload: ApprovalCheckpointPayload,
): ThreadMetadata {
  return encodeCheckpointPayload(ApprovalCheckpointPayloadSchema, payload);
}

export function serializeActionCheckpointPayload(payload: ActionCheckpointPayload): ThreadMetadata {
  return encodeCheckpointPayload(ActionCheckpointPayloadSchema, payload);
}

export function serializeToolOutputCheckpointPayload(
  payload: ToolOutputCheckpointPayload,
): ThreadMetadata {
  return encodeCheckpointPayload(ToolOutputCheckpointPayloadSchema, payload);
}

export function serializeToolDispatchCheckpointPayload(
  payload: ToolDispatchCheckpointPayload,
): ThreadMetadata {
  return encodeCheckpointPayload(ToolDispatchCheckpointPayloadSchema, payload);
}

export function serializeSubagentSpawnCheckpointPayload(
  payload: SubagentSpawnCheckpointPayload,
): ThreadMetadata {
  return encodeCheckpointPayload(SubagentSpawnCheckpointPayloadSchema, payload);
}

export function serializeSubagentJoinCheckpointPayload(
  payload: SubagentJoinCheckpointPayload,
): ThreadMetadata {
  return encodeCheckpointPayload(SubagentJoinCheckpointPayloadSchema, payload);
}

export function serializeResponseOutputCheckpointPayload(
  payload: ResponseOutputCheckpointPayload,
): ThreadMetadata {
  return encodeCheckpointPayload(ResponseOutputCheckpointPayloadSchema, payload);
}

export function serializeVerificationVerdictCheckpointPayload(
  payload: VerificationVerdictCheckpointPayload,
): ThreadMetadata {
  return encodeCheckpointPayload(VerificationVerdictCheckpointPayloadSchema, payload);
}

export function serializePhaseTransitionCheckpointPayload(
  payload: PhaseTransitionCheckpointPayload,
): ThreadMetadata {
  return encodeCheckpointPayload(PhaseTransitionCheckpointPayloadSchema, payload);
}

export function serializeProviderRequestCheckpointPayload(
  payload: ProviderRequestCheckpointPayload,
): ThreadMetadata {
  return encodeCheckpointPayload(ProviderRequestCheckpointPayloadSchema, payload);
}

export function parseApprovalCheckpointPayload(
  checkpoint: RunCheckpoint,
): ApprovalCheckpointPayload {
  const parsed = ApprovalCheckpointPayloadSchema.safeParse(checkpoint.payload);
  if (!parsed.success) {
    throw new BestAgentError(
      "RUN_CHECKPOINT_INVALID",
      `Checkpoint ${checkpoint.id} has invalid approval payload.`,
    );
  }
  return parsed.data;
}

export function parseActionCheckpointPayload(checkpoint: RunCheckpoint): ActionCheckpointPayload {
  const parsed = ActionCheckpointPayloadSchema.safeParse(checkpoint.payload);
  if (!parsed.success) {
    throw new BestAgentError(
      "RUN_CHECKPOINT_INVALID",
      `Checkpoint ${checkpoint.id} has invalid action payload.`,
    );
  }
  return parsed.data;
}

export function parseToolOutputCheckpointPayload(
  checkpoint: RunCheckpoint,
): ToolOutputCheckpointPayload {
  const parsed = ToolOutputCheckpointPayloadSchema.safeParse(checkpoint.payload);
  if (!parsed.success) {
    throw new BestAgentError(
      "RUN_CHECKPOINT_INVALID",
      `Checkpoint ${checkpoint.id} has invalid tool-output payload.`,
    );
  }
  return parsed.data;
}

export function parseToolDispatchCheckpointPayload(
  checkpoint: RunCheckpoint,
): ToolDispatchCheckpointPayload {
  const parsed = ToolDispatchCheckpointPayloadSchema.safeParse(checkpoint.payload);
  if (!parsed.success) {
    throw new BestAgentError(
      "RUN_CHECKPOINT_INVALID",
      `Checkpoint ${checkpoint.id} has invalid tool-dispatch payload.`,
    );
  }
  return parsed.data;
}

export function parseSubagentSpawnCheckpointPayload(
  checkpoint: RunCheckpoint,
): SubagentSpawnCheckpointPayload {
  const parsed = SubagentSpawnCheckpointPayloadSchema.safeParse(checkpoint.payload);
  if (!parsed.success) {
    throw new BestAgentError(
      "RUN_CHECKPOINT_INVALID",
      `Checkpoint ${checkpoint.id} has invalid subagent-spawn payload.`,
    );
  }
  return parsed.data;
}

export function parseSubagentJoinCheckpointPayload(
  checkpoint: RunCheckpoint,
): SubagentJoinCheckpointPayload {
  const parsed = SubagentJoinCheckpointPayloadSchema.safeParse(checkpoint.payload);
  if (!parsed.success) {
    throw new BestAgentError(
      "RUN_CHECKPOINT_INVALID",
      `Checkpoint ${checkpoint.id} has invalid subagent-join payload.`,
    );
  }
  return parsed.data;
}

export function parseResponseOutputCheckpointPayload(
  checkpoint: RunCheckpoint,
): ResponseOutputCheckpointPayload {
  const parsed = ResponseOutputCheckpointPayloadSchema.safeParse(checkpoint.payload);
  if (!parsed.success) {
    throw new BestAgentError(
      "RUN_CHECKPOINT_INVALID",
      `Checkpoint ${checkpoint.id} has invalid response-output payload.`,
    );
  }
  return parsed.data;
}

export function parseVerificationVerdictCheckpointPayload(
  checkpoint: RunCheckpoint,
): VerificationVerdictCheckpointPayload {
  const parsed = VerificationVerdictCheckpointPayloadSchema.safeParse(checkpoint.payload);
  if (!parsed.success) {
    throw new BestAgentError(
      "RUN_CHECKPOINT_INVALID",
      `Checkpoint ${checkpoint.id} has invalid verification-verdict payload.`,
    );
  }
  return parsed.data;
}

export function parsePhaseTransitionCheckpointPayload(
  checkpoint: RunCheckpoint,
): PhaseTransitionCheckpointPayload {
  const parsed = PhaseTransitionCheckpointPayloadSchema.safeParse(checkpoint.payload);
  if (!parsed.success) {
    throw new BestAgentError(
      "RUN_CHECKPOINT_INVALID",
      `Checkpoint ${checkpoint.id} has invalid phase-transition payload.`,
    );
  }
  return parsed.data;
}

export function parseProviderRequestCheckpointPayload(
  checkpoint: RunCheckpoint,
): ProviderRequestCheckpointPayload {
  const parsed = ProviderRequestCheckpointPayloadSchema.safeParse(checkpoint.payload);
  if (!parsed.success) {
    throw new BestAgentError(
      "RUN_CHECKPOINT_INVALID",
      `Checkpoint ${checkpoint.id} has invalid provider-request payload.`,
    );
  }
  return parsed.data;
}

export function parseToolResumeCheckpointPayload(
  checkpoint: RunCheckpoint,
): ToolResumeCheckpointPayload {
  if (checkpoint.kind === "approval-wait") {
    return parseApprovalCheckpointPayload(checkpoint);
  }
  if (checkpoint.kind === "action-wait") {
    return parseActionCheckpointPayload(checkpoint);
  }
  return parseToolOutputCheckpointPayload(checkpoint);
}

export function canRunCheckpointSupersede(
  pendingKind: RunCheckpointKind,
  nextKind: RunCheckpointKind,
): boolean {
  return RUN_CHECKPOINT_ALLOWED_PREDECESSORS[nextKind]?.includes(pendingKind) === true;
}
