Ctrl K
ComunidadGuías…

api/models/knowledge_fs.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""Dify-owned control-plane persistence for the KnowledgeFS product.

These models intentionally have no dependency on Dify Dataset or Document
models. KnowledgeFS data-plane objects remain owned by KnowledgeFS; these rows
only describe Dify authorization, registration, and durable lifecycle intent.
"""

from __future__ import annotations

from datetime import datetime
from enum import StrEnum
from typing import NotRequired, TypedDict

import sqlalchemy as sa
from sqlalchemy import DateTime, Index, String, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column

from .base import DefaultFieldsDCMixin, TypeBase
from .types import EnumText, LongText, StringUUID

KnowledgeFSAllowedActions = list[str]


class KnowledgeFSCommandEnvelope(TypedDict):
    schema_version: int
    idempotency_key: str
    expected_revision: int


class KnowledgeFSModelSelectionIntentPayload(TypedDict):
    pluginId: str
    provider: str
    model: str


class KnowledgeFSRerankIntentPayload(TypedDict):
    enabled: bool
    model: NotRequired[KnowledgeFSModelSelectionIntentPayload]


class KnowledgeFSScoreThresholdIntentPayload(TypedDict):
    enabled: bool
    stage: str
    value: NotRequired[float]


class KnowledgeFSRetrievalProfileIntentPayload(TypedDict):
    defaultMode: str
    reasoningModel: KnowledgeFSModelSelectionIntentPayload
    rerank: KnowledgeFSRerankIntentPayload
    scoreThreshold: KnowledgeFSScoreThresholdIntentPayload
    topK: int


class KnowledgeFSProvisionCommandPayload(KnowledgeFSCommandEnvelope):
    provisioning_key: str
    name: str
    icon: str | None
    description: str | None
    slug: str
    model_intent: NotRequired[KnowledgeFSModelSelectionIntentPayload]
    profile_intent: NotRequired[KnowledgeFSRetrievalProfileIntentPayload]
    # Local authorization snapshot; never sent to the KnowledgeFS data plane.
    initial_visibility: NotRequired[str]
    initial_member_account_ids: NotRequired[list[str]]


class KnowledgeFSMetadataUpdateCommandPayload(KnowledgeFSCommandEnvelope):
    metadata: dict[str, object]


class KnowledgeFSDeleteCommandPayload(KnowledgeFSCommandEnvelope):
    knowledge_space_id: str | None
    provisioning_key: str


class KnowledgeFSRevokeCommandPayload(KnowledgeFSCommandEnvelope):
    event_id: str
    grant_id: str
    knowledge_space_id: str
    principal: str
    reason_code: str
    revoke_sequence: int


class KnowledgeFSRepairCommandPayload(KnowledgeFSCommandEnvelope):
    repair_reason: str


KnowledgeFSCommandPayload = (
    KnowledgeFSProvisionCommandPayload
    | KnowledgeFSMetadataUpdateCommandPayload
    | KnowledgeFSDeleteCommandPayload
    | KnowledgeFSRevokeCommandPayload
    | KnowledgeFSRepairCommandPayload
)


class KnowledgeFSCapabilityAuthzRevisionSummary(TypedDict):
    membership_epoch: int
    space_acl_epoch: int
    external_access_epoch: int
    credential_revision: int | None


class KnowledgeFSCapabilityClaimsSummary(TypedDict):
    action: str
    actor: str
    authz_revision: KnowledgeFSCapabilityAuthzRevisionSummary
    caller_kind: str
    content_policy_revision: int
    content_scope_ids: list[str]
    control_space_id: str
    expires_at: str
    grant_id: str
    issued_at: str
    namespace_id: str
    operation_id: str
    resource_id: str
    resource_parent_id: str | None
    resource_type: str
    subject: str


class KnowledgeFSCapabilityReservationSummary(TypedDict):
    """Sanitized, exact input bound before a capability can be signed."""

    action: str
    actor: str
    authz_revision: KnowledgeFSCapabilityAuthzRevisionSummary
    caller_kind: str
    content_policy_revision: int
    content_scope_ids: list[str]
    control_space_id: str
    grant_id: str
    namespace_id: str
    operation_id: str
    resource_id: str
    resource_parent_id: str | None
    resource_type: str
    subject: str
    trace_id: str


class KnowledgeFSControlSpaceVisibility(StrEnum):
    ONLY_ME = "only_me"
    ALL_TEAM_MEMBERS = "all_team_members"
    PARTIAL_MEMBERS = "partial_members"


class KnowledgeFSControlSpaceState(StrEnum):
    PROVISIONING = "provisioning"
    ACTIVE = "active"
    DELETING = "deleting"
    DELETED = "deleted"
    ERROR = "error"


class KnowledgeFSControlSpacePermissionRole(StrEnum):
    OWNER = "owner"
    EDITOR = "editor"
    VIEWER = "viewer"


class KnowledgeFSControlSpacePermissionStatus(StrEnum):
    ACTIVE = "active"
    REVOKED = "revoked"


class KnowledgeFSApiCredentialStatus(StrEnum):
    ACTIVE = "active"
    REVOKED = "revoked"
    EXPIRED = "expired"


class KnowledgeFSAppSpaceJoinType(StrEnum):
    AGENT = "agent"
    WORKFLOW = "workflow"


class KnowledgeFSAppSpaceJoinStatus(StrEnum):
    ACTIVE = "active"
    REVOKED = "revoked"


class KnowledgeFSCapabilityIssuanceReservationStatus(StrEnum):
    RESERVED = "reserved"
    ISSUED = "issued"
    FAILED = "failed"


class KnowledgeFSLifecycleOperation(StrEnum):
    PROVISION = "provision"
    METADATA_UPDATE = "metadata_update"
    DELETE = "delete"
    REVOKE = "revoke"
    REPAIR = "repair"


class KnowledgeFSLifecycleOutboxStatus(StrEnum):
    PENDING = "pending"
    PROCESSING = "processing"
    SUCCEEDED = "succeeded"
    RETRY = "retry"
    DEAD_LETTER = "dead_letter"


class KnowledgeFSStagedUploadStatus(StrEnum):
    UPLOADED = "uploaded"
    CLAIMING = "claiming"
    CLAIMED = "claimed"
    FAILED = "failed"
    ABORTED = "aborted"
    EXPIRED = "expired"


class KnowledgeFSUpgradeJobStatus(StrEnum):
    QUEUED = "queued"
    RUNNING = "running"
    SUCCEEDED = "succeeded"
    FAILED = "failed"


class KnowledgeFSUpgradeStage(StrEnum):
    VALIDATING = "validating"
    WAITING_FOR_SPACE = "waiting_for_space"
    CREATING_SOURCES = "creating_sources"
    SUBMITTING_DOCUMENTS = "submitting_documents"
    MIGRATING_ACCESS = "migrating_access"
    FINALIZING = "finalizing"
    COMPLETED = "completed"


class KnowledgeFSUpgradeItemStatus(StrEnum):
    PENDING = "pending"
    PROCESSING = "processing"
    SUCCEEDED = "succeeded"
    FAILED = "failed"


class KnowledgeFSUpgradeFileLeaseStatus(StrEnum):
    ACTIVE = "active"
    RELEASED = "released"
    EXPIRED = "expired"


class KnowledgeFSControlSpace(DefaultFieldsDCMixin, TypeBase):
    """Dify product resource registered to at most one KnowledgeFS Space."""

    __tablename__ = "knowledge_fs_control_spaces"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="kfs_control_space_pkey"),
        UniqueConstraint("tenant_id", "id", name="kfs_control_space_tenant_id_uq"),
        UniqueConstraint("provisioning_key", name="kfs_control_space_provisioning_key_uq"),
        sa.ForeignKeyConstraint(
            ["tenant_id"],
            ["tenants.id"],
            name="kfs_control_space_workspace_fk",
            ondelete="RESTRICT",
        ),
        Index(
            "kfs_control_space_tenant_space_uq",
            "tenant_id",
            "knowledge_space_id",
            unique=True,
            postgresql_where=sa.text("knowledge_space_id IS NOT NULL"),
        ),
        Index("kfs_control_space_state_updated_idx", "state", "updated_at"),
        Index("kfs_control_space_tenant_state_updated_idx", "tenant_id", "state", "updated_at"),
        Index("kfs_control_space_tenant_owner_state_idx", "tenant_id", "owner_account_id", "state"),
        sa.CheckConstraint(
            "resource_version >= 0",
            name=sa.schema.conv("kfs_control_space_resource_version_ck"),
        ),
        sa.CheckConstraint("attempt_count >= 0", name=sa.schema.conv("kfs_control_space_attempt_count_ck")),
        sa.CheckConstraint(
            "state != 'active' OR knowledge_space_id IS NOT NULL",
            name=sa.schema.conv("kfs_control_space_active_registration_ck"),
        ),
        sa.CheckConstraint(
            "deletion_irreversible_at IS NULL OR state IN ('deleting', 'deleted', 'error')",
            name=sa.schema.conv("kfs_control_space_irreversible_state_ck"),
        ),
        sa.CheckConstraint(
            "knowledge_space_revision >= 0",
            name=sa.schema.conv("kfs_control_space_remote_revision_ck"),
        ),
    )

    tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    owner_account_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    provisioning_key: Mapped[str] = mapped_column(String(255), nullable=False)
    knowledge_space_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    knowledge_space_revision: Mapped[int] = mapped_column(
        sa.BigInteger,
        nullable=False,
        server_default=sa.text("0"),
        default=0,
    )
    visibility: Mapped[KnowledgeFSControlSpaceVisibility] = mapped_column(
        EnumText(KnowledgeFSControlSpaceVisibility, length=32),
        nullable=False,
        server_default=sa.text("'only_me'"),
        default=KnowledgeFSControlSpaceVisibility.ONLY_ME,
    )
    icon_background: Mapped[str] = mapped_column(
        String(7),
        nullable=False,
        server_default=sa.text("'#F0F9FF'"),
        default="#F0F9FF",
    )
    lifecycle_operation_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    state: Mapped[KnowledgeFSControlSpaceState] = mapped_column(
        EnumText(KnowledgeFSControlSpaceState, length=32),
        nullable=False,
        server_default=sa.text("'provisioning'"),
        default=KnowledgeFSControlSpaceState.PROVISIONING,
    )
    resource_version: Mapped[int] = mapped_column(
        sa.BigInteger,
        nullable=False,
        server_default=sa.text("0"),
        default=0,
    )
    attempt_count: Mapped[int] = mapped_column(
        sa.Integer,
        nullable=False,
        server_default=sa.text("0"),
        default=0,
    )
    last_attempt_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    last_error_code: Mapped[str | None] = mapped_column(String(255), nullable=True, default=None)
    last_error_message: Mapped[str | None] = mapped_column(LongText, nullable=True, default=None)
    last_synced_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    deletion_irreversible_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)


class KnowledgeFSSpaceTagBinding(DefaultFieldsDCMixin, TypeBase):
    """Dify-owned tag binding for one KnowledgeFS control-space."""

    __tablename__ = "knowledge_fs_space_tag_bindings"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="kfs_space_tag_binding_pkey"),
        UniqueConstraint(
            "tenant_id",
            "control_space_id",
            "tag_id",
            name="kfs_space_tag_binding_identity_uq",
        ),
        sa.ForeignKeyConstraint(
            ["tenant_id", "control_space_id"],
            ["knowledge_fs_control_spaces.tenant_id", "knowledge_fs_control_spaces.id"],
            name="kfs_space_tag_binding_space_fk",
            ondelete="CASCADE",
        ),
        sa.ForeignKeyConstraint(
            ["tag_id"],
            ["tags.id"],
            name="kfs_space_tag_binding_tag_fk",
            ondelete="CASCADE",
        ),
        Index("kfs_space_tag_binding_tag_idx", "tenant_id", "tag_id"),
    )

    tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    control_space_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    tag_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    created_by: Mapped[str] = mapped_column(StringUUID, nullable=False)


class KnowledgeFSControlSpacePermission(DefaultFieldsDCMixin, TypeBase):
    """Account-level product authorization for one control-space."""

    __tablename__ = "knowledge_fs_control_space_permissions"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="kfs_control_space_permission_pkey"),
        UniqueConstraint(
            "tenant_id",
            "control_space_id",
            "account_id",
            name="kfs_control_space_permission_identity_uq",
        ),
        sa.ForeignKeyConstraint(
            ["tenant_id", "control_space_id"],
            ["knowledge_fs_control_spaces.tenant_id", "knowledge_fs_control_spaces.id"],
            name="kfs_control_space_permission_space_fk",
            ondelete="RESTRICT",
        ),
        Index("kfs_control_space_permission_account_idx", "tenant_id", "account_id", "status"),
        sa.CheckConstraint(
            "revision >= 0",
            name=sa.schema.conv("kfs_control_space_permission_revision_ck"),
        ),
    )

    tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    control_space_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    account_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    role: Mapped[KnowledgeFSControlSpacePermissionRole] = mapped_column(
        EnumText(KnowledgeFSControlSpacePermissionRole, length=32), nullable=False
    )
    status: Mapped[KnowledgeFSControlSpacePermissionStatus] = mapped_column(
        EnumText(KnowledgeFSControlSpacePermissionStatus, length=32),
        nullable=False,
        server_default=sa.text("'active'"),
        default=KnowledgeFSControlSpacePermissionStatus.ACTIVE,
    )
    revision: Mapped[int] = mapped_column(sa.BigInteger, nullable=False, server_default=sa.text("0"), default=0)
    granted_by_account_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    revoked_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    revoked_by_account_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)


class KnowledgeFSExternalAccessPolicy(DefaultFieldsDCMixin, TypeBase):
    """Fail-closed caller-channel policy for a control-space."""

    __tablename__ = "knowledge_fs_external_access_policies"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="kfs_external_access_policy_pkey"),
        UniqueConstraint("tenant_id", "control_space_id", name="kfs_external_access_policy_space_uq"),
        sa.ForeignKeyConstraint(
            ["tenant_id", "control_space_id"],
            ["knowledge_fs_control_spaces.tenant_id", "knowledge_fs_control_spaces.id"],
            name="kfs_external_access_policy_space_fk",
            ondelete="RESTRICT",
        ),
        sa.CheckConstraint("revision >= 0", name=sa.schema.conv("kfs_external_access_policy_revision_ck")),
    )

    tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    control_space_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    service_api_enabled: Mapped[bool] = mapped_column(
        sa.Boolean, nullable=False, server_default=sa.text("false"), default=False
    )
    agent_enabled: Mapped[bool] = mapped_column(
        sa.Boolean, nullable=False, server_default=sa.text("false"), default=False
    )
    workflow_enabled: Mapped[bool] = mapped_column(
        sa.Boolean, nullable=False, server_default=sa.text("false"), default=False
    )
    mcp_enabled: Mapped[bool] = mapped_column(
        sa.Boolean, nullable=False, server_default=sa.text("false"), default=False
    )
    revision: Mapped[int] = mapped_column(sa.BigInteger, nullable=False, server_default=sa.text("0"), default=0)
    updated_by_account_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)


class KnowledgeFSApiCredential(DefaultFieldsDCMixin, TypeBase):
    """Hashed, resource-bound KnowledgeFS API credential metadata."""

    __tablename__ = "knowledge_fs_api_credentials"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="kfs_api_credential_pkey"),
        UniqueConstraint("credential_hash", name="kfs_api_credential_hash_uq"),
        sa.ForeignKeyConstraint(
            ["tenant_id", "control_space_id"],
            ["knowledge_fs_control_spaces.tenant_id", "knowledge_fs_control_spaces.id"],
            name="kfs_api_credential_space_fk",
            ondelete="RESTRICT",
        ),
        Index("kfs_api_credential_tenant_space_status_idx", "tenant_id", "control_space_id", "status"),
        Index("kfs_api_credential_tenant_prefix_idx", "tenant_id", "credential_prefix"),
        sa.CheckConstraint("revision >= 0", name=sa.schema.conv("kfs_api_credential_revision_ck")),
    )

    tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    control_space_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    credential_hash: Mapped[str] = mapped_column(String(255), nullable=False)
    credential_prefix: Mapped[str] = mapped_column(String(32), nullable=False)
    credential_last4: Mapped[str] = mapped_column(String(4), nullable=False)
    principal: Mapped[str] = mapped_column(String(255), nullable=False)
    allowed_actions: Mapped[KnowledgeFSAllowedActions] = mapped_column(sa.JSON, nullable=False)
    status: Mapped[KnowledgeFSApiCredentialStatus] = mapped_column(
        EnumText(KnowledgeFSApiCredentialStatus, length=32),
        nullable=False,
        server_default=sa.text("'active'"),
        default=KnowledgeFSApiCredentialStatus.ACTIVE,
    )
    revision: Mapped[int] = mapped_column(sa.BigInteger, nullable=False, server_default=sa.text("0"), default=0)
    expires_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    last_used_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    created_by_account_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    revoked_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    revoked_by_account_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    revoke_reason: Mapped[str | None] = mapped_column(String(255), nullable=True, default=None)


class AppKnowledgeFSSpaceJoin(DefaultFieldsDCMixin, TypeBase):
    """Explicit Agent or Workflow app authorization for a control-space."""

    __tablename__ = "app_knowledge_fs_space_joins"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="app_kfs_space_join_pkey"),
        UniqueConstraint(
            "tenant_id",
            "app_id",
            "control_space_id",
            "join_type",
            name="app_kfs_space_join_identity_uq",
        ),
        sa.ForeignKeyConstraint(
            ["tenant_id", "control_space_id"],
            ["knowledge_fs_control_spaces.tenant_id", "knowledge_fs_control_spaces.id"],
            name="app_kfs_space_join_space_fk",
            ondelete="RESTRICT",
        ),
        Index("app_kfs_space_join_app_status_idx", "tenant_id", "app_id", "status"),
        sa.CheckConstraint("revision >= 0", name=sa.schema.conv("app_kfs_space_join_revision_ck")),
    )

    tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    control_space_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    app_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    join_type: Mapped[KnowledgeFSAppSpaceJoinType] = mapped_column(
        EnumText(KnowledgeFSAppSpaceJoinType, length=32), nullable=False
    )
    status: Mapped[KnowledgeFSAppSpaceJoinStatus] = mapped_column(
        EnumText(KnowledgeFSAppSpaceJoinStatus, length=32),
        nullable=False,
        server_default=sa.text("'active'"),
        default=KnowledgeFSAppSpaceJoinStatus.ACTIVE,
    )
    revision: Mapped[int] = mapped_column(sa.BigInteger, nullable=False, server_default=sa.text("0"), default=0)
    created_by_account_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    revoked_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    revoked_by_account_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)


class KnowledgeFSAuthorizationRevision(DefaultFieldsDCMixin, TypeBase):
    """Monotonic authorization epochs included in future Capability claims."""

    __tablename__ = "knowledge_fs_authorization_revisions"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="kfs_authorization_revision_pkey"),
        UniqueConstraint("tenant_id", "control_space_id", name="kfs_authorization_revision_space_uq"),
        sa.ForeignKeyConstraint(
            ["tenant_id", "control_space_id"],
            ["knowledge_fs_control_spaces.tenant_id", "knowledge_fs_control_spaces.id"],
            name="kfs_authorization_revision_space_fk",
            ondelete="RESTRICT",
        ),
        sa.CheckConstraint(
            "membership_epoch >= 0",
            name=sa.schema.conv("kfs_authorization_membership_epoch_ck"),
        ),
        sa.CheckConstraint(
            "space_acl_epoch >= 0",
            name=sa.schema.conv("kfs_authorization_space_acl_epoch_ck"),
        ),
        sa.CheckConstraint(
            "external_access_epoch >= 0",
            name=sa.schema.conv("kfs_authorization_external_access_epoch_ck"),
        ),
        sa.CheckConstraint(
            "content_policy_revision >= 0",
            name=sa.schema.conv("kfs_authorization_content_policy_revision_ck"),
        ),
        sa.CheckConstraint(
            "revoke_sequence >= 0",
            name=sa.schema.conv("kfs_authorization_revoke_sequence_ck"),
        ),
    )

    tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    control_space_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    membership_epoch: Mapped[int] = mapped_column(sa.BigInteger, nullable=False, server_default=sa.text("0"), default=0)
    space_acl_epoch: Mapped[int] = mapped_column(sa.BigInteger, nullable=False, server_default=sa.text("0"), default=0)
    external_access_epoch: Mapped[int] = mapped_column(
        sa.BigInteger, nullable=False, server_default=sa.text("0"), default=0
    )
    content_policy_revision: Mapped[int] = mapped_column(
        sa.BigInteger, nullable=False, server_default=sa.text("0"), default=0
    )
    revoke_sequence: Mapped[int] = mapped_column(sa.BigInteger, nullable=False, server_default=sa.text("0"), default=0)


class KnowledgeFSCapabilityIssuanceAudit(DefaultFieldsDCMixin, TypeBase):
    """Sanitized durable evidence for a Capability v2 issuance."""

    __tablename__ = "knowledge_fs_capability_issuance_audits"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="kfs_capability_issuance_audit_pkey"),
        UniqueConstraint("jti_hash", name="kfs_capability_issuance_audit_jti_hash_uq"),
        sa.ForeignKeyConstraint(
            ["tenant_id", "control_space_id"],
            ["knowledge_fs_control_spaces.tenant_id", "knowledge_fs_control_spaces.id"],
            name="kfs_capability_issuance_audit_space_fk",
            ondelete="RESTRICT",
        ),
        Index(
            "kfs_capability_issuance_audit_space_created_idx",
            "tenant_id",
            "control_space_id",
            "created_at",
        ),
        Index("kfs_capability_issuance_audit_trace_idx", "tenant_id", "trace_id"),
    )

    tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    control_space_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True)
    trace_id: Mapped[str] = mapped_column(String(128), nullable=False)
    jti_hash: Mapped[str] = mapped_column(String(80), nullable=False)
    claims_summary: Mapped[KnowledgeFSCapabilityClaimsSummary] = mapped_column(sa.JSON, nullable=False)


class KnowledgeFSCapabilityIssuanceReservation(DefaultFieldsDCMixin, TypeBase):
    """Durable authorization fence written before signing starts.

    The summary intentionally excludes bearer tokens, signing material, and raw JTI values.
    Revocation producers scan these rows as well as completed issuance audits, closing the
    authorization-to-signing race without holding a database transaction across signing.
    """

    __tablename__ = "knowledge_fs_capability_issuance_reservations"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="kfs_capability_issuance_reservation_pkey"),
        UniqueConstraint(
            "tenant_id",
            "grant_id",
            name="kfs_capability_issuance_reservation_grant_uq",
        ),
        sa.ForeignKeyConstraint(
            ["tenant_id", "control_space_id"],
            ["knowledge_fs_control_spaces.tenant_id", "knowledge_fs_control_spaces.id"],
            name="kfs_capability_issuance_reservation_space_fk",
            ondelete="RESTRICT",
        ),
        Index(
            "kfs_capability_issuance_reservation_subject_idx",
            "tenant_id",
            "control_space_id",
            "subject",
            "caller_kind",
        ),
        Index(
            "kfs_capability_issuance_reservation_trace_idx",
            "tenant_id",
            "trace_id",
        ),
        sa.CheckConstraint(
            "row_version >= 0",
            name=sa.schema.conv("kfs_capability_issuance_reservation_version_ck"),
        ),
        sa.CheckConstraint(
            "(status = 'reserved' AND issued_at IS NULL AND token_expires_at IS NULL "
            "AND failed_at IS NULL AND failure_code IS NULL AND cleanup_after IS NULL) OR "
            "(status = 'issued' AND issued_at IS NOT NULL AND token_expires_at IS NOT NULL "
            "AND failed_at IS NULL AND failure_code IS NULL AND cleanup_after IS NOT NULL) OR "
            "(status = 'failed' AND issued_at IS NULL AND token_expires_at IS NULL "
            "AND failed_at IS NOT NULL AND failure_code IS NOT NULL AND cleanup_after IS NOT NULL)",
            name=sa.schema.conv("kfs_capability_issuance_reservation_status_fields_ck"),
        ),
        sa.CheckConstraint(
            "token_expires_at IS NULL OR cleanup_after >= token_expires_at",
            name=sa.schema.conv("kfs_capability_issuance_reservation_cleanup_window_ck"),
        ),
    )

    tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    control_space_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    grant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    trace_id: Mapped[str] = mapped_column(String(128), nullable=False)
    subject: Mapped[str] = mapped_column(String(255), nullable=False)
    caller_kind: Mapped[str] = mapped_column(String(32), nullable=False)
    request_summary: Mapped[KnowledgeFSCapabilityReservationSummary] = mapped_column(sa.JSON, nullable=False)
    status: Mapped[KnowledgeFSCapabilityIssuanceReservationStatus] = mapped_column(
        EnumText(KnowledgeFSCapabilityIssuanceReservationStatus, length=16),
        nullable=False,
        server_default=sa.text("'reserved'"),
        default=KnowledgeFSCapabilityIssuanceReservationStatus.RESERVED,
    )
    issued_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    token_expires_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    failed_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    failure_code: Mapped[str | None] = mapped_column(String(128), nullable=True, default=None)
    cleanup_after: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    row_version: Mapped[int] = mapped_column(sa.BigInteger, nullable=False, server_default=sa.text("0"), default=0)


class KnowledgeFSStagedUpload(DefaultFieldsDCMixin, TypeBase):
    """Workspace-owned document bytes waiting to be claimed by one KnowledgeFS Space.

    The raw file is persisted through Dify's shared ``UploadFile`` storage path. This
    row owns the short-lived, single-claim lifecycle and the durable identifiers needed
    to resume a claim after an upstream timeout without uploading the bytes again.
    """

    __tablename__ = "knowledge_fs_staged_uploads"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="kfs_staged_upload_pkey"),
        UniqueConstraint("upload_file_id", name="kfs_staged_upload_file_uq"),
        sa.ForeignKeyConstraint(
            ["tenant_id"],
            ["tenants.id"],
            name="kfs_staged_upload_workspace_fk",
            ondelete="RESTRICT",
        ),
        sa.ForeignKeyConstraint(
            ["upload_file_id"],
            ["upload_files.id"],
            name="kfs_staged_upload_file_fk",
            ondelete="RESTRICT",
        ),
        sa.ForeignKeyConstraint(
            ["tenant_id", "control_space_id"],
            ["knowledge_fs_control_spaces.tenant_id", "knowledge_fs_control_spaces.id"],
            name="kfs_staged_upload_space_fk",
            ondelete="RESTRICT",
        ),
        Index(
            "kfs_staged_upload_owner_status_expiry_idx",
            "tenant_id",
            "account_id",
            "status",
            "expires_at",
            "id",
        ),
        Index(
            "kfs_staged_upload_expiry_idx",
            "status",
            "expires_at",
            "id",
        ),
        sa.CheckConstraint("size_bytes > 0", name=sa.schema.conv("kfs_staged_upload_size_ck")),
        sa.CheckConstraint("row_version >= 0", name=sa.schema.conv("kfs_staged_upload_version_ck")),
        sa.CheckConstraint(
            "status IN ('uploaded', 'claiming', 'claimed', 'failed', 'aborted', 'expired')",
            name=sa.schema.conv("kfs_staged_upload_status_ck"),
        ),
        sa.CheckConstraint(
            "(status = 'claimed' AND control_space_id IS NOT NULL "
            "AND knowledge_space_id IS NOT NULL AND upload_session_id IS NOT NULL "
            "AND document_asset_id IS NOT NULL AND compilation_job_id IS NOT NULL "
            "AND claimed_at IS NOT NULL) OR "
            "(status != 'claimed' AND claimed_at IS NULL)",
            name=sa.schema.conv("kfs_staged_upload_claimed_fields_ck"),
        ),
        sa.CheckConstraint(
            "(upload_session_id IS NULL AND knowledge_space_id IS NULL) OR "
            "(upload_session_id IS NOT NULL AND knowledge_space_id IS NOT NULL "
            "AND control_space_id IS NOT NULL)",
            name=sa.schema.conv("kfs_staged_upload_session_scope_ck"),
        ),
    )

    tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    account_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    upload_file_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    file_name: Mapped[str] = mapped_column(String(512), nullable=False)
    content_type: Mapped[str] = mapped_column(String(255), nullable=False)
    size_bytes: Mapped[int] = mapped_column(sa.BigInteger, nullable=False)
    checksum_sha256_base64: Mapped[str] = mapped_column(String(255), nullable=False)
    expires_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
    status: Mapped[KnowledgeFSStagedUploadStatus] = mapped_column(
        EnumText(KnowledgeFSStagedUploadStatus, length=16),
        nullable=False,
        server_default=sa.text("'uploaded'"),
        default=KnowledgeFSStagedUploadStatus.UPLOADED,
    )
    control_space_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    knowledge_space_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    upload_session_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    document_asset_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    compilation_job_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    claimed_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    last_error_code: Mapped[str | None] = mapped_column(String(128), nullable=True, default=None)
    row_version: Mapped[int] = mapped_column(sa.BigInteger, nullable=False, server_default=sa.text("0"), default=0)


class KnowledgeFSLifecycleOutbox(DefaultFieldsDCMixin, TypeBase):
    """Durable command snapshot; it is never a product read-model source.

    Provision payload writers must snapshot name, icon, description, slug,
    model/profile intent, schema version, idempotency key, and expected
    revision before enabling the later P1B dispatcher.
    """

    __tablename__ = "knowledge_fs_lifecycle_outbox"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="kfs_lifecycle_outbox_pkey"),
        UniqueConstraint("tenant_id", "operation_id", name="kfs_lifecycle_outbox_operation_uq"),
        UniqueConstraint("tenant_id", "idempotency_key", name="kfs_lifecycle_outbox_idempotency_uq"),
        sa.ForeignKeyConstraint(
            ["tenant_id", "control_space_id"],
            ["knowledge_fs_control_spaces.tenant_id", "knowledge_fs_control_spaces.id"],
            name="kfs_lifecycle_outbox_space_fk",
            ondelete="RESTRICT",
        ),
        Index("kfs_lifecycle_outbox_dispatch_idx", "status", "next_attempt_at", "id"),
        Index("kfs_lifecycle_outbox_space_created_idx", "tenant_id", "control_space_id", "created_at"),
        sa.CheckConstraint(
            "command_schema_version >= 1",
            name=sa.schema.conv("kfs_lifecycle_outbox_schema_version_ck"),
        ),
        sa.CheckConstraint(
            "expected_control_space_version >= 0",
            name=sa.schema.conv("kfs_lifecycle_outbox_expected_version_ck"),
        ),
        sa.CheckConstraint(
            "attempt_count >= 0",
            name=sa.schema.conv("kfs_lifecycle_outbox_attempt_count_ck"),
        ),
        sa.CheckConstraint(
            "status IN ('pending', 'processing', 'succeeded', 'retry', 'dead_letter')",
            name=sa.schema.conv("kfs_lifecycle_outbox_status_ck"),
        ),
        sa.CheckConstraint(
            "(status = 'processing' AND lease_owner IS NOT NULL AND lease_expires_at IS NOT NULL) "
            "OR (status != 'processing' AND lease_owner IS NULL AND lease_expires_at IS NULL)",
            name=sa.schema.conv("kfs_lifecycle_outbox_lease_state_ck"),
        ),
        sa.CheckConstraint(
            "(status IN ('succeeded', 'dead_letter') AND completed_at IS NOT NULL) "
            "OR (status NOT IN ('succeeded', 'dead_letter') AND completed_at IS NULL)",
            name=sa.schema.conv("kfs_lifecycle_outbox_terminal_state_ck"),
        ),
    )

    tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    control_space_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    operation_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    idempotency_key: Mapped[str] = mapped_column(String(255), nullable=False)
    operation: Mapped[KnowledgeFSLifecycleOperation] = mapped_column(
        EnumText(KnowledgeFSLifecycleOperation, length=32), nullable=False
    )
    command_payload: Mapped[KnowledgeFSCommandPayload] = mapped_column(sa.JSON, nullable=False)
    expected_control_space_version: Mapped[int] = mapped_column(sa.BigInteger, nullable=False)
    expected_knowledge_space_revision: Mapped[int | None] = mapped_column(sa.BigInteger, nullable=True, default=None)
    command_schema_version: Mapped[int] = mapped_column(
        sa.Integer, nullable=False, server_default=sa.text("1"), default=1
    )
    status: Mapped[KnowledgeFSLifecycleOutboxStatus] = mapped_column(
        EnumText(KnowledgeFSLifecycleOutboxStatus, length=32),
        nullable=False,
        server_default=sa.text("'pending'"),
        default=KnowledgeFSLifecycleOutboxStatus.PENDING,
    )
    attempt_count: Mapped[int] = mapped_column(sa.Integer, nullable=False, server_default=sa.text("0"), default=0)
    next_attempt_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    last_attempt_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    lease_owner: Mapped[str | None] = mapped_column(String(255), nullable=True, default=None)
    lease_expires_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    last_error_code: Mapped[str | None] = mapped_column(String(255), nullable=True, default=None)
    last_error_message: Mapped[str | None] = mapped_column(LongText, nullable=True, default=None)
    completed_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    retain_until: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)


class KnowledgeFSUpgradeJob(DefaultFieldsDCMixin, TypeBase):
    """Immutable legacy Dataset snapshot and resumable upgrade progress."""

    __tablename__ = "knowledge_fs_upgrade_jobs"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="kfs_upgrade_job_pkey"),
        UniqueConstraint("tenant_id", "idempotency_key", name="kfs_upgrade_job_idempotency_uq"),
        Index(
            "kfs_upgrade_job_active_dataset_uq",
            "tenant_id",
            "old_dataset_id",
            unique=True,
            postgresql_where=sa.text("status IN ('queued', 'running')"),
            sqlite_where=sa.text("status IN ('queued', 'running')"),
        ),
        sa.ForeignKeyConstraint(
            ["tenant_id"],
            ["tenants.id"],
            name="kfs_upgrade_job_workspace_fk",
            ondelete="RESTRICT",
        ),
        sa.ForeignKeyConstraint(
            ["tenant_id", "new_control_space_id"],
            ["knowledge_fs_control_spaces.tenant_id", "knowledge_fs_control_spaces.id"],
            name="kfs_upgrade_job_space_fk",
            ondelete="RESTRICT",
        ),
        Index("kfs_upgrade_job_dataset_created_idx", "tenant_id", "old_dataset_id", "created_at"),
        Index("kfs_upgrade_job_status_updated_idx", "status", "updated_at"),
        sa.CheckConstraint("total_documents >= 0", name=sa.schema.conv("kfs_upgrade_job_document_total_ck")),
        sa.CheckConstraint("completed_documents >= 0", name=sa.schema.conv("kfs_upgrade_job_document_done_ck")),
        sa.CheckConstraint("total_sources >= 0", name=sa.schema.conv("kfs_upgrade_job_source_total_ck")),
        sa.CheckConstraint("completed_sources >= 0", name=sa.schema.conv("kfs_upgrade_job_source_done_ck")),
        sa.CheckConstraint("attempt_count >= 0", name=sa.schema.conv("kfs_upgrade_job_attempt_count_ck")),
    )

    tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    old_dataset_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    requested_by_account_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    owner_account_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    idempotency_key: Mapped[str] = mapped_column(String(255), nullable=False)
    snapshot_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
    config_snapshot: Mapped[dict[str, object]] = mapped_column(sa.JSON, nullable=False)
    permission_snapshot: Mapped[dict[str, object]] = mapped_column(sa.JSON, nullable=False)
    app_binding_snapshot: Mapped[list[dict[str, object]]] = mapped_column(sa.JSON, nullable=False)
    tag_ids_snapshot: Mapped[list[str]] = mapped_column(sa.JSON, nullable=False)
    status: Mapped[KnowledgeFSUpgradeJobStatus] = mapped_column(
        EnumText(KnowledgeFSUpgradeJobStatus, length=16),
        nullable=False,
        server_default=sa.text("'queued'"),
        default=KnowledgeFSUpgradeJobStatus.QUEUED,
    )
    stage: Mapped[KnowledgeFSUpgradeStage] = mapped_column(
        EnumText(KnowledgeFSUpgradeStage, length=32),
        nullable=False,
        server_default=sa.text("'validating'"),
        default=KnowledgeFSUpgradeStage.VALIDATING,
    )
    new_control_space_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    resolved_configuration: Mapped[dict[str, object] | None] = mapped_column(sa.JSON, nullable=True, default=None)
    total_documents: Mapped[int] = mapped_column(sa.Integer, nullable=False, default=0, server_default=sa.text("0"))
    completed_documents: Mapped[int] = mapped_column(sa.Integer, nullable=False, default=0, server_default=sa.text("0"))
    total_sources: Mapped[int] = mapped_column(sa.Integer, nullable=False, default=0, server_default=sa.text("0"))
    completed_sources: Mapped[int] = mapped_column(sa.Integer, nullable=False, default=0, server_default=sa.text("0"))
    attempt_count: Mapped[int] = mapped_column(sa.Integer, nullable=False, default=0, server_default=sa.text("0"))
    celery_task_id: Mapped[str | None] = mapped_column(String(255), nullable=True, default=None)
    # Independent delivery lease: parent success does not imply document state reconciliation finished.
    reconciliation_task_id: Mapped[str | None] = mapped_column(String(255), nullable=True, default=None)
    reconciliation_started_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    reconciliation_lease_expires_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    last_error_code: Mapped[str | None] = mapped_column(String(128), nullable=True, default=None)
    last_error_message: Mapped[str | None] = mapped_column(LongText, nullable=True, default=None)
    completed_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)


class KnowledgeFSUpgradeDocument(DefaultFieldsDCMixin, TypeBase):
    """One legacy document captured at click time, independent of later Dataset updates."""

    __tablename__ = "knowledge_fs_upgrade_documents"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="kfs_upgrade_document_pkey"),
        UniqueConstraint("job_id", "old_document_id", name="kfs_upgrade_document_identity_uq"),
        sa.ForeignKeyConstraint(
            ["job_id"],
            ["knowledge_fs_upgrade_jobs.id"],
            name="kfs_upgrade_document_job_fk",
            ondelete="CASCADE",
        ),
        Index("kfs_upgrade_document_dispatch_idx", "job_id", "status", "id"),
        sa.CheckConstraint(
            "state_reconcile_attempt_count >= 0",
            name=sa.schema.conv("kfs_upgrade_document_reconcile_attempt_ck"),
        ),
    )

    job_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    old_document_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    data_source_type: Mapped[str] = mapped_column(String(32), nullable=False)
    data_source_info: Mapped[dict[str, object]] = mapped_column(sa.JSON, nullable=False)
    metadata_snapshot: Mapped[dict[str, object]] = mapped_column(sa.JSON, nullable=False)
    desired_enabled: Mapped[bool] = mapped_column(sa.Boolean, nullable=False)
    legacy_archived: Mapped[bool] = mapped_column(sa.Boolean, nullable=False)
    legacy_indexing_status: Mapped[str] = mapped_column(String(32), nullable=False)
    legacy_display_status: Mapped[str | None] = mapped_column(String(32), nullable=True, default=None)
    old_upload_file_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    source_key: Mapped[str | None] = mapped_column(String(255), nullable=True, default=None)
    status: Mapped[KnowledgeFSUpgradeItemStatus] = mapped_column(
        EnumText(KnowledgeFSUpgradeItemStatus, length=16),
        nullable=False,
        server_default=sa.text("'pending'"),
        default=KnowledgeFSUpgradeItemStatus.PENDING,
    )
    staged_upload_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    new_document_asset_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    new_logical_document_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    compilation_job_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    state_reconcile_attempt_count: Mapped[int] = mapped_column(
        sa.Integer, nullable=False, server_default=sa.text("0"), default=0
    )
    state_reconciled_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    # Persist availability separately: metadata retries must neither delay disabling nor undo later user edits.
    availability_reconciled_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    state_reconcile_error: Mapped[str | None] = mapped_column(LongText, nullable=True, default=None)
    last_error_code: Mapped[str | None] = mapped_column(String(128), nullable=True, default=None)
    last_error_message: Mapped[str | None] = mapped_column(LongText, nullable=True, default=None)


class KnowledgeFSUpgradeSource(DefaultFieldsDCMixin, TypeBase):
    """One deduplicated Source definition derived from the Dataset snapshot."""

    __tablename__ = "knowledge_fs_upgrade_sources"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="kfs_upgrade_source_pkey"),
        UniqueConstraint("job_id", "source_key", name="kfs_upgrade_source_identity_uq"),
        sa.ForeignKeyConstraint(
            ["job_id"],
            ["knowledge_fs_upgrade_jobs.id"],
            name="kfs_upgrade_source_job_fk",
            ondelete="CASCADE",
        ),
        Index("kfs_upgrade_source_dispatch_idx", "job_id", "status", "id"),
    )

    job_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    source_key: Mapped[str] = mapped_column(String(255), nullable=False)
    payload_snapshot: Mapped[dict[str, object]] = mapped_column(sa.JSON, nullable=False)
    status: Mapped[KnowledgeFSUpgradeItemStatus] = mapped_column(
        EnumText(KnowledgeFSUpgradeItemStatus, length=16),
        nullable=False,
        server_default=sa.text("'pending'"),
        default=KnowledgeFSUpgradeItemStatus.PENDING,
    )
    new_connection_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    new_source_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    initial_sync_task_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None)
    last_error_code: Mapped[str | None] = mapped_column(String(128), nullable=True, default=None)
    last_error_message: Mapped[str | None] = mapped_column(LongText, nullable=True, default=None)


class KnowledgeFSUpgradeFileLease(DefaultFieldsDCMixin, TypeBase):
    """Temporary physical-retention lease for a legacy uploaded source file."""

    __tablename__ = "knowledge_fs_upgrade_file_leases"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="kfs_upgrade_file_lease_pkey"),
        UniqueConstraint("job_id", "old_upload_file_id", name="kfs_upgrade_file_lease_identity_uq"),
        sa.ForeignKeyConstraint(
            ["job_id"],
            ["knowledge_fs_upgrade_jobs.id"],
            name="kfs_upgrade_file_lease_job_fk",
            ondelete="CASCADE",
        ),
        Index("kfs_upgrade_file_lease_active_idx", "old_upload_file_id", "status", "expires_at"),
    )

    job_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    old_upload_file_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    expires_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
    status: Mapped[KnowledgeFSUpgradeFileLeaseStatus] = mapped_column(
        EnumText(KnowledgeFSUpgradeFileLeaseStatus, length=16),
        nullable=False,
        server_default=sa.text("'active'"),
        default=KnowledgeFSUpgradeFileLeaseStatus.ACTIVE,
    )
    released_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    cleanup_requested_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)


__all__ = [
    "AppKnowledgeFSSpaceJoin",
    "KnowledgeFSAllowedActions",
    "KnowledgeFSApiCredential",
    "KnowledgeFSApiCredentialStatus",
    "KnowledgeFSAppSpaceJoinStatus",
    "KnowledgeFSAppSpaceJoinType",
    "KnowledgeFSAuthorizationRevision",
    "KnowledgeFSCapabilityAuthzRevisionSummary",
    "KnowledgeFSCapabilityClaimsSummary",
    "KnowledgeFSCapabilityIssuanceAudit",
    "KnowledgeFSCapabilityIssuanceReservation",
    "KnowledgeFSCapabilityIssuanceReservationStatus",
    "KnowledgeFSCapabilityReservationSummary",
    "KnowledgeFSCommandEnvelope",
    "KnowledgeFSCommandPayload",
    "KnowledgeFSControlSpace",
    "KnowledgeFSControlSpacePermission",
    "KnowledgeFSControlSpacePermissionRole",
    "KnowledgeFSControlSpacePermissionStatus",
    "KnowledgeFSControlSpaceState",
    "KnowledgeFSControlSpaceVisibility",
    "KnowledgeFSDeleteCommandPayload",
    "KnowledgeFSExternalAccessPolicy",
    "KnowledgeFSLifecycleOperation",
    "KnowledgeFSLifecycleOutbox",
    "KnowledgeFSLifecycleOutboxStatus",
    "KnowledgeFSMetadataUpdateCommandPayload",
    "KnowledgeFSModelSelectionIntentPayload",
    "KnowledgeFSProvisionCommandPayload",
    "KnowledgeFSRepairCommandPayload",
    "KnowledgeFSRerankIntentPayload",
    "KnowledgeFSRetrievalProfileIntentPayload",
    "KnowledgeFSRevokeCommandPayload",
    "KnowledgeFSScoreThresholdIntentPayload",
    "KnowledgeFSStagedUpload",
    "KnowledgeFSStagedUploadStatus",
    "KnowledgeFSUpgradeDocument",
    "KnowledgeFSUpgradeFileLease",
    "KnowledgeFSUpgradeFileLeaseStatus",
    "KnowledgeFSUpgradeItemStatus",
    "KnowledgeFSUpgradeJob",
    "KnowledgeFSUpgradeJobStatus",
    "KnowledgeFSUpgradeSource",
    "KnowledgeFSUpgradeStage",
]