#region DataModel [C:3] [TYPE ADR] [SEMANTICS data-model,agent-chat,schema] @defgroup DataModel Data structures for 035-agent-chat-context — frontend TypeScript DTOs and backend Python counterparts. @RATIONALE Updated for Path B (/agent page), Gradio payload safety, two-layer RBAC, and permission_denied event type. ## 1. UIContext — Navigation Context Payload ### Source Derived from URL query parameters on `/agent` page entry. Static for visit duration. EnvId syncable via `updateActiveEnv`. ### TypeScript (`frontend/src/lib/models/AgentChatTypes.ts`) ```typescript export interface UIContext { objectType: "dashboard" | "dataset" | "migration" | null; objectId: string | null; objectName: string | null; // max 256 chars envId: string | null; // synced with EnvSelector via updateActiveEnv route: string; // source page route (e.g. "/dashboards/42") contextVersion: number; // currently 1 } ``` ### Python validation (`backend/src/agent/_context.py` — NEW) ```python # UIContext validation rules: # objectType: must be one of ("dashboard","dataset","migration",None) # objectId: must be numeric string or None # objectName: max 256 chars # envId: string or None # route: string, max 512 chars # contextVersion: positive integer ≤ 1 # Max payload size: 4 KB (413 Payload Too Large if exceeded) ``` --- ## 2. ConfirmMetadataV2 — SSE confirm_required Event ```json { "metadata": { "type": "confirm_required", "thread_id": "uuid", "tool_name": "deploy_dashboard", "tool_args": {"env_id": "prod", "dashboard_id": 42}, "risk": "write", "risk_level": "guarded", "dangerous": false, "env_context": "prod", "prompt": "⚠️ Изменение данных в production", "requires_confirmation": true } } ``` **Env resolution rule** (canonical): 1. `tool_args.env_id` (tool-targeted environment) 2. Submit-time `envId` parameter (position 5 in Gradio args) 3. `UIContext.envId` (from URL params) 4. `null` (no environment) **Env normalization**: `env_id` containing "prod"/"production" → `"prod"`; "stag"/"test" → `"staging"`; "dev"/"local" → `"dev"`. Unknown values → `null`. --- ## 3. PermissionDeniedMetadata — SSE permission_denied Event (NEW) ```json { "metadata": { "type": "permission_denied", "tool_name": "deploy_dashboard", "required_role": "admin", "user_role": "analyst", "alternatives": [ {"action": "get_health_summary", "prompt": "Запросить отчёт о состоянии дашборда"} ] } } ``` **Distinct from confirm_required**: This event BYPASSES HITL checkpoint. Frontend renders permission-denied card with «Закрыть» button — no confirm. Emitted at invocation-time guard (AgentChat.ToolFilter.InvocationGuard). --- ## 4. New SSE Event Types | Event | When | Fields | |-------|------|--------| | `permission_denied` | Tool invocation rejected by RBAC guard | tool_name, required_role, user_role, alternatives | | `tool_retry` | Read-only tool auto-retrying | tool, attempt, max_attempts | | `tool_timeout` | Tool exceeded 30s timeout | tool, timeout_seconds, is_write_tool, retryable | ### `tool_timeout` (extended) ```json {"metadata":{"type":"tool_timeout","tool":"superset_explore_database","timeout_seconds":30,"is_write_tool":false,"retryable":true}} ``` For write tools (`is_write_tool=true`): `retryable=false`, frontend shows "Operation status unknown — check Superset" (no retry button). --- ## 5. AgentChatModel — New Atoms (v2) ```typescript // ADD to AgentChatModel class: uiContext: UIContext | null = $state(null); dangerousCountdown: number = $state(0); dangerousCountdownActive: boolean = $state(false); _activeEnvId: string | null = $state(null); permissionAlternatives: Array<{ action: string; prompt: string }> | null = $state(null); // ADD derived: isProdContext: boolean = $derived(...); showProdWarning: boolean = $derived(...); contextPillLabel: string = $derived(...); // updateActiveEnv now syncs BOTH atoms: updateActiveEnv(envId: string | null): void { this._activeEnvId = envId; if (this.uiContext) this.uiContext.envId = envId; } ``` **REMOVED**: `toolAffinity` atom (debug data must come from backend SSE, not frontend reconstruction). --- ## 6. Existing DB Models — No Changes No new tables, no schema migrations. Context not persisted in conversations. #endregion DataModel