#region AgentChat.DataModel [C:3] [TYPE ADR] [SEMANTICS data-model,agent-chat,final] @BRIEF Final data model — structured JSON metadata, PostgreSQL checkpointer, dual-identity RBAC, no custom WebSocket types. ## 1. Streaming Metadata Protocol Each `event.data` is a `ChatMessage` with optional structured `metadata`: ```typescript interface StreamMetadata { type?: "stream_token" | "tool_start" | "tool_end" | "tool_error" | "confirm_required" | "confirm_resolved" | "error"; token?: string; tool?: string; input?: Record; output?: Record; error?: string; prompt?: string; thread_id?: string; result?: "confirmed" | "denied"; code?: string; detail?: string; } ``` No `agent-ws.ts`. No custom WebSocket types. No emoji prefix parsing. ## 2. SQLAlchemy Tables: `agent_conversations`, `agent_messages`. Legacy `assistant_messages` preserved. ## 3. Pydantic Schemas `ConversationItem`, `ConversationListResponse`, `MessageItem`, `HistoryResponse`, `DeleteResponse`. ## 4. TypeScript DTOs `frontend/src/types/agent.ts` — matches Pydantic schemas. Includes `StreamMetadata`. ## 4a. Frontend Screen-State Derivations `AgentChatModel` owns operator-only derived state. These fields are not persisted and do not cross the API boundary: ```typescript type AgentProcessStepState = "done" | "active" | "blocked" | "idle"; interface AgentProcessStep { id: "context" | "reasoning" | "tools" | "confirmation" | "result"; label: string; state: AgentProcessStepState; } ``` Derived state: - `hasSilentStream`: `streaming` with no token, no tool call, no confirmation/checkpoint. - `userFacingStatusTitle` / `userFacingStatusDetail`: operator-readable status copy. - `processSteps`: UI process strip; currently inferred from stream/tool/confirmation/error state. - `cleanVisibleMessageText(rawText)`: strips hidden runtime/prefetch/upload context before rendering or retrying. ## 5. PostgreSQL Checkpointer LangGraph checkpointer: `langgraph-checkpoint-postgres`. Thread ID = conversation_id. Survives container restarts. Same PostgreSQL instance as FastAPI. ## 6. Dual Identity RBAC ``` Authorization: Bearer {service_jwt} X-User-JWT: {user_jwt} ``` Audit: `{service_actor: "agent", user_actor: user_id}`. ## 7. Hidden Runtime Context The backend may prepend hidden runtime context to the LLM-facing message. This is not a persisted message field. ```text [RUNTIME CONTEXT] Current datetime: Environment: Rules: interpret now/start/duration from current datetime... Dashboards: compact bounded prefetch... [/RUNTIME CONTEXT] ``` Persistence invariant: `agent_messages.text`, title generation input, and frontend-rendered user bubbles use the visible user text only. #endregion AgentChat.DataModel