"""Durable Agent OS runtime records for PMFlow AI.""" from __future__ import annotations from typing import Any, Optional from sqlalchemy import BigInteger, Boolean, Float, ForeignKey, Integer, Text, TIMESTAMP, UniqueConstraint, VARCHAR from sqlalchemy.orm import Mapped, mapped_column from .base import Base from .types import JSONB class PMFlowAgentRun(Base): """Persisted execution container for one PMFlow AI agent turn. User-facing continuity lives in PMFlowAIThread/PMFlowAIMessage. Runs are immutable execution records attached to a thread message. """ __tablename__ = "pmflow_agent_runs" __table_args__ = ( UniqueConstraint("tenant_id", "public_run_id", name="uq_pmflow_agent_runs_tenant_public_run_id"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) public_run_id: Mapped[str] = mapped_column(VARCHAR(80), nullable=False, index=True) thread_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("pmflow_ai_threads.id", ondelete="SET NULL"), nullable=True, index=True) user_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("users.id"), nullable=True, index=True) qa_session_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("qa_sessions.id"), nullable=True, index=True) workspace_scope: Mapped[Optional[str]] = mapped_column(VARCHAR(80), nullable=True, index=True) surface: Mapped[Optional[str]] = mapped_column(VARCHAR(80), nullable=True) presentation_mode: Mapped[Optional[str]] = mapped_column(VARCHAR(40), nullable=True) source_channel: Mapped[Optional[str]] = mapped_column(VARCHAR(40), nullable=True, index=True) source_ref: Mapped[Optional[str]] = mapped_column(VARCHAR(160), nullable=True, index=True) title: Mapped[Optional[str]] = mapped_column(VARCHAR(240), nullable=True) goal: Mapped[str] = mapped_column(Text, nullable=False, default="") status: Mapped[str] = mapped_column(VARCHAR(32), nullable=False, default="active", index=True) active_object_refs: Mapped[Any] = mapped_column(JSONB, nullable=True, default=list) current_step_id: Mapped[Optional[str]] = mapped_column(VARCHAR(80), nullable=True) final_answer: Mapped[Optional[str]] = mapped_column(Text, nullable=True) last_error: Mapped[Optional[str]] = mapped_column(Text, nullable=True) run_metadata: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) started_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True, index=True) completed_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True, index=True) class PMFlowAIThread(Base): """User-visible PMFlow AI conversation/thread.""" __tablename__ = "pmflow_ai_threads" __table_args__ = ( UniqueConstraint("tenant_id", "public_thread_id", name="uq_pmflow_ai_threads_tenant_public_thread_id"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) public_thread_id: Mapped[str] = mapped_column(VARCHAR(80), nullable=False, index=True) user_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("users.id"), nullable=True, index=True) qa_session_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("qa_sessions.id", ondelete="SET NULL"), nullable=True, index=True) workspace_scope: Mapped[Optional[str]] = mapped_column(VARCHAR(80), nullable=True, index=True) surface: Mapped[Optional[str]] = mapped_column(VARCHAR(80), nullable=True) title: Mapped[str] = mapped_column(VARCHAR(240), nullable=False, default="PMFlow AI") status: Mapped[str] = mapped_column(VARCHAR(32), nullable=False, default="active", index=True) current_object_refs: Mapped[Any] = mapped_column(JSONB, nullable=True, default=list) last_run_public_id: Mapped[Optional[str]] = mapped_column(VARCHAR(80), nullable=True, index=True) last_message_preview: Mapped[Optional[str]] = mapped_column(Text, nullable=True) message_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0) thread_metadata: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) started_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True, index=True) last_message_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True, index=True) class PMFlowAIMessage(Base): """A user or assistant message inside a PMFlow AI thread.""" __tablename__ = "pmflow_ai_messages" __table_args__ = ( UniqueConstraint("tenant_id", "thread_id", "public_message_id", name="uq_pmflow_ai_messages_tenant_thread_public_message"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) thread_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("pmflow_ai_threads.id", ondelete="CASCADE"), nullable=False, index=True) public_message_id: Mapped[str] = mapped_column(VARCHAR(80), nullable=False, index=True) role: Mapped[str] = mapped_column(VARCHAR(24), nullable=False, index=True) message_index: Mapped[int] = mapped_column(Integer, nullable=False, default=0, index=True) run_public_id: Mapped[Optional[str]] = mapped_column(VARCHAR(80), nullable=True, index=True) qa_record_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("qa_records.id", ondelete="SET NULL"), nullable=True, index=True) content: Mapped[str] = mapped_column(Text, nullable=False, default="") message_payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) sent_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True, index=True) class PMFlowAgentStep(Base): """A durable step observed in an Agent OS run.""" __tablename__ = "pmflow_agent_steps" __table_args__ = ( UniqueConstraint("tenant_id", "run_id", "public_step_id", name="uq_pmflow_agent_steps_tenant_run_public_step"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) run_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("pmflow_agent_runs.id", ondelete="CASCADE"), nullable=False, index=True) public_step_id: Mapped[str] = mapped_column(VARCHAR(80), nullable=False, index=True) step_index: Mapped[int] = mapped_column(Integer, nullable=False, default=0, index=True) step_kind: Mapped[str] = mapped_column(VARCHAR(64), nullable=False, default="runtime_step", index=True) status: Mapped[str] = mapped_column(VARCHAR(32), nullable=False, default="completed", index=True) input_summary: Mapped[Optional[str]] = mapped_column(Text, nullable=True) output_summary: Mapped[Optional[str]] = mapped_column(Text, nullable=True) tool_name: Mapped[Optional[str]] = mapped_column(VARCHAR(120), nullable=True, index=True) duration_ms: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) step_payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) started_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True) completed_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True) class PMFlowAgentObservation(Base): """Facts and gaps observed by a step or tool during a run.""" __tablename__ = "pmflow_agent_observations" __table_args__ = ( UniqueConstraint( "tenant_id", "run_id", "public_observation_id", name="uq_pmflow_agent_observations_tenant_run_public_observation", ), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) run_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("pmflow_agent_runs.id", ondelete="CASCADE"), nullable=False, index=True) step_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("pmflow_agent_steps.id", ondelete="SET NULL"), nullable=True, index=True) public_observation_id: Mapped[str] = mapped_column(VARCHAR(80), nullable=False, index=True) source_kind: Mapped[str] = mapped_column(VARCHAR(80), nullable=False, default="agent_runtime", index=True) customer_safe_summary: Mapped[Optional[str]] = mapped_column(Text, nullable=True) confidence: Mapped[Optional[str]] = mapped_column(VARCHAR(32), nullable=True) facts: Mapped[Any] = mapped_column(JSONB, nullable=True, default=list) gaps: Mapped[Any] = mapped_column(JSONB, nullable=True, default=list) raw_payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) observed_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True, index=True) class PMFlowAgentArtifact(Base): """Durable artifacts created by an Agent OS run.""" __tablename__ = "pmflow_agent_artifacts" __table_args__ = ( UniqueConstraint("tenant_id", "run_id", "public_artifact_id", name="uq_pmflow_agent_artifacts_tenant_run_public_artifact"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) run_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("pmflow_agent_runs.id", ondelete="CASCADE"), nullable=False, index=True) step_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("pmflow_agent_steps.id", ondelete="SET NULL"), nullable=True, index=True) public_artifact_id: Mapped[str] = mapped_column(VARCHAR(80), nullable=False, index=True) artifact_kind: Mapped[str] = mapped_column(VARCHAR(80), nullable=False, index=True) title: Mapped[Optional[str]] = mapped_column(VARCHAR(240), nullable=True) status: Mapped[str] = mapped_column(VARCHAR(32), nullable=False, default="created", index=True) payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) customer_safe_payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) created_by_step_id: Mapped[Optional[str]] = mapped_column(VARCHAR(80), nullable=True, index=True) class PMFlowAgentVerification(Base): """Verification result for a run, step or artifact.""" __tablename__ = "pmflow_agent_verifications" __table_args__ = ( UniqueConstraint( "tenant_id", "run_id", "public_verification_id", name="uq_pmflow_agent_verifications_tenant_run_public_verification", ), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) run_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("pmflow_agent_runs.id", ondelete="CASCADE"), nullable=False, index=True) step_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("pmflow_agent_steps.id", ondelete="SET NULL"), nullable=True, index=True) public_verification_id: Mapped[str] = mapped_column(VARCHAR(80), nullable=False, index=True) target_ref: Mapped[Optional[str]] = mapped_column(VARCHAR(160), nullable=True, index=True) result: Mapped[str] = mapped_column(VARCHAR(32), nullable=False, default="unknown", index=True) retry_would_change_state: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) findings: Mapped[Any] = mapped_column(JSONB, nullable=True, default=list) verification_payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) verified_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True, index=True) class PMFlowAgentMemory(Base): """Durable run memory and learning references produced by PMFlow AI.""" __tablename__ = "pmflow_agent_memories" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) run_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("pmflow_agent_runs.id", ondelete="CASCADE"), nullable=False, index=True) memory_kind: Mapped[str] = mapped_column(VARCHAR(80), nullable=False, index=True) scope_ref: Mapped[Optional[str]] = mapped_column(VARCHAR(160), nullable=True, index=True) outcome_signal: Mapped[Optional[str]] = mapped_column(VARCHAR(80), nullable=True, index=True) content: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) memory_payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) class PMFlowAIContextMemory(Base): """Layered long-context memory for PMFlow AI conversations. This is separate from PMFlowAgentMemory because it is not bound to one run. It stores durable semantic compactions that can be retrieved by thread, user, workspace or project scope. """ __tablename__ = "pmflow_ai_context_memories" __table_args__ = ( UniqueConstraint( "tenant_id", "memory_scope", "scope_ref", "memory_key", name="uq_pmflow_ai_context_memories_scope_key", ), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) public_memory_id: Mapped[str] = mapped_column(VARCHAR(80), nullable=False, index=True) memory_scope: Mapped[str] = mapped_column(VARCHAR(40), nullable=False, index=True) scope_ref: Mapped[str] = mapped_column(VARCHAR(160), nullable=False, index=True) memory_key: Mapped[str] = mapped_column(VARCHAR(120), nullable=False, index=True) source_thread_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("pmflow_ai_threads.id", ondelete="SET NULL"), nullable=True, index=True) source_run_public_id: Mapped[Optional[str]] = mapped_column(VARCHAR(80), nullable=True, index=True) status: Mapped[str] = mapped_column(VARCHAR(32), nullable=False, default="active", index=True) confidence_score: Mapped[float] = mapped_column(Float, nullable=False, default=0.6, index=True) supersedes_memory_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("pmflow_ai_context_memories.id", ondelete="SET NULL"), nullable=True, index=True) invalidation_reason: Mapped[Optional[str]] = mapped_column(Text, nullable=True) summary: Mapped[str] = mapped_column(Text, nullable=False, default="") durable_facts: Mapped[Any] = mapped_column(JSONB, nullable=True, default=list) open_loops: Mapped[Any] = mapped_column(JSONB, nullable=True, default=list) decisions: Mapped[Any] = mapped_column(JSONB, nullable=True, default=list) preferences: Mapped[Any] = mapped_column(JSONB, nullable=True, default=list) object_refs: Mapped[Any] = mapped_column(JSONB, nullable=True, default=list) source_message_range: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) confidence: Mapped[Optional[str]] = mapped_column(VARCHAR(32), nullable=True) token_estimate: Mapped[int] = mapped_column(Integer, nullable=False, default=0) compaction_payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) last_compacted_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True, index=True) last_validated_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True, index=True) last_accessed_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True, index=True) class PMFlowAIContextMemoryCompactionJob(Base): """Durable background job for long-context memory compaction.""" __tablename__ = "pmflow_ai_context_memory_compaction_jobs" __table_args__ = ( UniqueConstraint( "tenant_id", "public_job_id", name="uq_pmflow_ai_context_memory_compaction_jobs_public_job", ), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) public_job_id: Mapped[str] = mapped_column(VARCHAR(80), nullable=False, index=True) thread_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("pmflow_ai_threads.id", ondelete="SET NULL"), nullable=True, index=True) user_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("users.id"), nullable=True, index=True) qa_session_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("qa_sessions.id", ondelete="SET NULL"), nullable=True, index=True) run_public_id: Mapped[Optional[str]] = mapped_column(VARCHAR(80), nullable=True, index=True) status: Mapped[str] = mapped_column(VARCHAR(32), nullable=False, default="queued", index=True) attempt_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0) max_attempts: Mapped[int] = mapped_column(Integer, nullable=False, default=2) priority: Mapped[int] = mapped_column(Integer, nullable=False, default=0, index=True) job_payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) result_payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) last_error: Mapped[Optional[str]] = mapped_column(Text, nullable=True) superseded_by_job_id: Mapped[Optional[int]] = mapped_column( BigInteger, ForeignKey("pmflow_ai_context_memory_compaction_jobs.id", ondelete="SET NULL"), nullable=True, index=True, ) queued_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True, index=True) started_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True, index=True) completed_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True, index=True) next_retry_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True, index=True) class PMFlowAIContextAssembly(Base): """Sanitized context assembly trace for one PMFlow AI answer run.""" __tablename__ = "pmflow_ai_context_assemblies" __table_args__ = ( UniqueConstraint("tenant_id", "public_assembly_id", name="uq_pmflow_ai_context_assemblies_public_id"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) public_assembly_id: Mapped[str] = mapped_column(VARCHAR(80), nullable=False, index=True) thread_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("pmflow_ai_threads.id", ondelete="SET NULL"), nullable=True, index=True) user_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("users.id"), nullable=True, index=True) qa_session_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("qa_sessions.id", ondelete="SET NULL"), nullable=True, index=True) run_public_id: Mapped[Optional[str]] = mapped_column(VARCHAR(80), nullable=True, index=True) context_packet_version: Mapped[Optional[str]] = mapped_column(VARCHAR(80), nullable=True) user_question_preview: Mapped[Optional[str]] = mapped_column(Text, nullable=True) assembly_payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) observability_payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) used_context_payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) retrieval_critic_payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) grounding_payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) replay_payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) assembled_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True, index=True) class PMFlowAITopicWorkspace(Base): """Durable cross-thread semantic workspace for one ongoing PMFlow AI topic.""" __tablename__ = "pmflow_ai_topic_workspaces" __table_args__ = ( UniqueConstraint("tenant_id", "public_topic_id", name="uq_pmflow_ai_topic_workspaces_public_id"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) public_topic_id: Mapped[str] = mapped_column(VARCHAR(80), nullable=False, index=True) user_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("users.id"), nullable=True, index=True) workspace_scope: Mapped[str] = mapped_column(VARCHAR(80), nullable=False, default="space", index=True) status: Mapped[str] = mapped_column(VARCHAR(32), nullable=False, default="active", index=True) topic_signature: Mapped[str] = mapped_column(VARCHAR(80), nullable=False, index=True) topic_summary: Mapped[str] = mapped_column(Text, nullable=False, default="") current_objective: Mapped[str] = mapped_column(Text, nullable=False, default="") known_assumptions: Mapped[Any] = mapped_column(JSONB, nullable=True, default=list) prior_proposals: Mapped[Any] = mapped_column(JSONB, nullable=True, default=list) open_questions: Mapped[Any] = mapped_column(JSONB, nullable=True, default=list) monetization_hypothesis: Mapped[Optional[str]] = mapped_column(Text, nullable=True) next_actionable_package: Mapped[Optional[str]] = mapped_column(Text, nullable=True) linked_thread_ids: Mapped[Any] = mapped_column(JSONB, nullable=True, default=list) linked_run_ids: Mapped[Any] = mapped_column(JSONB, nullable=True, default=list) linked_memory_ids: Mapped[Any] = mapped_column(JSONB, nullable=True, default=list) confidence_score: Mapped[float] = mapped_column(Float, nullable=False, default=0.6, index=True) topic_payload: Mapped[Any] = mapped_column(JSONB, nullable=True, default=dict) last_activated_at: Mapped[Optional[TIMESTAMP]] = mapped_column(TIMESTAMP, nullable=True, index=True)