diff --git a/.principal-views/architecture.canvas b/.principal-views/architecture.canvas new file mode 100644 index 000000000000..0d5d35bcfb77 --- /dev/null +++ b/.principal-views/architecture.canvas @@ -0,0 +1,238 @@ +{ + "nodes": [ + { + "id": "web-client", + "type": "text", + "text": "Web Client\n\nReact/Vite UI\nSession UX\nConversation rendering", + "x": 100, + "y": 300, + "width": 200, + "height": 120, + "color": "#3b82f6" + }, + { + "id": "websocket-server", + "type": "text", + "text": "WebSocket Server\n\nCommand routing\nPush messages\nHTTP endpoints", + "x": 400, + "y": 300, + "width": 200, + "height": 120, + "color": "#8b5cf6" + }, + { + "id": "orchestration-engine", + "type": "text", + "text": "Orchestration Engine\n\nEvent sourcing\nCommand dispatch\nIdempotency", + "x": 700, + "y": 243, + "width": 200, + "height": 120, + "color": "#06b6d4" + }, + { + "id": "provider-session", + "type": "text", + "text": "Provider Session\n\nCodex app-server\nJSON-RPC over stdio\nProcess lifecycle", + "x": 1000, + "y": 200, + "width": 200, + "height": 120, + "color": "#22c55e" + }, + { + "id": "runtime-ingestion", + "type": "text", + "text": "Runtime Ingestion\n\nEvent classification\nMessage buffering\nDomain synthesis", + "x": 1000, + "y": 400, + "width": 200, + "height": 120, + "color": "#f59e0b" + }, + { + "id": "terminal-manager", + "type": "text", + "text": "Terminal Manager\n\nPTY sessions\nShell execution\nI/O streaming", + "x": 700, + "y": 450, + "width": 200, + "height": 120, + "color": "#ec4899" + }, + { + "id": "event-store", + "type": "text", + "text": "Event Store\n\nSQLite persistence\nEvent append\nProjections", + "x": 700, + "y": 75, + "width": 200, + "height": 100, + "color": "#64748b" + }, + { + "id": "codex-process", + "type": "text", + "text": "codex app-server\n\nExternal process\nJSON-RPC protocol", + "x": 1300, + "y": 200, + "width": 180, + "height": 100, + "color": "#94a3b8" + }, + { + "id": "group-server", + "type": "group", + "label": "Server (apps/server)", + "x": 380, + "y": 30, + "width": 860, + "height": 560, + "color": "#1e293b" + }, + { + "id": "group-external", + "type": "group", + "label": "External", + "x": 1280, + "y": 180, + "width": 220, + "height": 140, + "color": "#374151" + } + ], + "edges": [ + { + "id": "client-to-ws", + "fromNode": "web-client", + "toNode": "websocket-server", + "fromSide": "right", + "toSide": "left", + "label": "WebSocket", + "pv": { + "edgeType": "data-flow" + } + }, + { + "id": "ws-to-orchestration", + "fromNode": "websocket-server", + "toNode": "orchestration-engine", + "fromSide": "right", + "toSide": "left", + "label": "commands", + "pv": { + "edgeType": "command" + } + }, + { + "id": "orchestration-to-provider", + "fromNode": "orchestration-engine", + "toNode": "provider-session", + "fromSide": "right", + "toSide": "left", + "label": "start/stop/turn", + "pv": { + "edgeType": "command" + } + }, + { + "id": "provider-to-codex", + "fromNode": "provider-session", + "toNode": "codex-process", + "fromSide": "right", + "toSide": "left", + "label": "JSON-RPC", + "pv": { + "edgeType": "external" + } + }, + { + "id": "provider-to-ingestion", + "fromNode": "provider-session", + "toNode": "runtime-ingestion", + "fromSide": "bottom", + "toSide": "top", + "label": "raw events", + "pv": { + "edgeType": "event" + } + }, + { + "id": "ingestion-to-orchestration", + "fromNode": "runtime-ingestion", + "toNode": "orchestration-engine", + "fromSide": "left", + "toSide": "bottom", + "label": "domain events", + "pv": { + "edgeType": "event" + } + }, + { + "id": "orchestration-to-ws", + "fromNode": "orchestration-engine", + "toNode": "websocket-server", + "fromSide": "left", + "toSide": "right", + "label": "push", + "pv": { + "edgeType": "event" + } + }, + { + "id": "ws-to-terminal", + "fromNode": "websocket-server", + "toNode": "terminal-manager", + "fromSide": "bottom", + "toSide": "left", + "label": "terminal I/O", + "pv": { + "edgeType": "data-flow" + } + }, + { + "id": "orchestration-to-store", + "fromNode": "orchestration-engine", + "toNode": "event-store", + "fromSide": "top", + "toSide": "bottom", + "label": "persist", + "pv": { + "edgeType": "persist" + } + } + ], + "pv": { + "name": "T3 Code Architecture", + "version": "1.0.0", + "description": "High-level architecture of T3 Code server components and data flow", + "markdown": ".principal-views/architecture.md", + "edgeTypes": { + "data-flow": { + "style": "solid", + "color": "#3b82f6", + "width": 2 + }, + "command": { + "style": "solid", + "color": "#8b5cf6", + "width": 2 + }, + "event": { + "style": "dashed", + "color": "#22c55e", + "width": 2 + }, + "persist": { + "style": "solid", + "color": "#64748b", + "width": 2 + }, + "external": { + "style": "dotted", + "color": "#94a3b8", + "width": 2 + } + } + } +} \ No newline at end of file diff --git a/.principal-views/architecture.md b/.principal-views/architecture.md new file mode 100644 index 000000000000..4d09632e41f2 --- /dev/null +++ b/.principal-views/architecture.md @@ -0,0 +1,47 @@ +# T3 Code Architecture + +High-level architecture overview of the T3 Code server and its subsystems. + +## Overview + +T3 Code is a minimal web GUI for code agents. The server wraps provider processes (currently Codex app-server) and exposes them via WebSocket to a React frontend. + +## Core Components + +### Web Client (apps/web) +React/Vite UI that connects to the server via WebSocket. Handles session UX, conversation rendering, and client-side state management. + +### WebSocket Server +Entry point for all client communication. Routes incoming commands to appropriate handlers and pushes domain events back to connected clients. Also serves HTTP endpoints for static assets. + +### Orchestration Engine +Event-sourcing command processor. Commands enter a queue, go through idempotency checks, and produce events that are atomically committed. Ensures reliable, replayable state transitions. + +### Provider Session +Manages the lifecycle of provider processes (codex app-server). Handles process spawning, JSON-RPC communication over stdio, turn execution, and graceful shutdown. + +### Runtime Ingestion +Bridges raw provider events into domain events. Classifies incoming events, buffers streaming content, and synthesizes high-level events for the orchestration layer. + +### Terminal Manager +PTY session management for shell execution. Spawns pseudo-terminal processes, streams I/O between client and shell, handles resize events. + +### Event Store +SQLite-backed persistence for orchestration events. Provides append-only event storage, read model projections, and receipt tracking for idempotency. + +## Data Flow + +1. Client sends command via WebSocket +2. WebSocket server routes to Orchestration Engine +3. Orchestration Engine dispatches to Provider Session +4. Provider Session communicates with codex app-server via JSON-RPC +5. Raw events flow through Runtime Ingestion +6. Domain events are committed and pushed back to client + +## Key Files + +- `apps/server/src/wsServer.ts` - WebSocket/HTTP server +- `apps/server/src/orchestration/Layers/OrchestrationEngine.ts` - Command processing +- `apps/server/src/codexAppServerManager.ts` - Provider process management +- `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts` - Event ingestion +- `apps/server/src/terminal/Layers/Manager.ts` - Terminal sessions diff --git a/.principal-views/ipc-architecture/ipc-architecture.canvas b/.principal-views/ipc-architecture/ipc-architecture.canvas new file mode 100644 index 000000000000..ad4e83051d14 --- /dev/null +++ b/.principal-views/ipc-architecture/ipc-architecture.canvas @@ -0,0 +1,253 @@ +{ + "nodes": [ + { + "id": "react-renderer", + "type": "text", + "text": "React Renderer\n\nBrowser context\nwindow.desktopBridge\nWsTransport client", + "x": 100, + "y": 250, + "width": 180, + "height": 120, + "color": "#3b82f6" + }, + { + "id": "preload-script", + "type": "text", + "text": "Preload Script\n\ncontextBridge\nipcRenderer.invoke()\nsatisfies DesktopBridge", + "x": 350, + "y": 150, + "width": 180, + "height": 120, + "color": "#8b5cf6" + }, + { + "id": "electron-main", + "type": "text", + "text": "Electron Main\n\nipcMain.handle()\nChild process mgmt\nAuth token generation", + "x": 600, + "y": 150, + "width": 180, + "height": 120, + "color": "#22c55e" + }, + { + "id": "node-server", + "type": "text", + "text": "Node.js Server\n\nWebSocket handler\nEffect/Schema validation\nRPC + Push channels", + "x": 600, + "y": 350, + "width": 180, + "height": 120, + "color": "#f59e0b" + }, + { + "id": "codex-process", + "type": "text", + "text": "codex app-server\n\nJSON-RPC over stdio\nExternal process", + "x": 900, + "y": 350, + "width": 180, + "height": 100, + "color": "#94a3b8" + }, + { + "id": "desktop-bridge-interface", + "type": "text", + "text": "DesktopBridge\n\nType-safe interface\nOne-directional only\n(renderer side)", + "x": 350, + "y": 0, + "width": 180, + "height": 100, + "color": "#64748b" + }, + { + "id": "ws-contracts", + "type": "text", + "text": "WebSocket Contracts\n\nEffect/Schema\nFull type-safety\nRuntime validation", + "x": 350, + "y": 450, + "width": 180, + "height": 100, + "color": "#06b6d4" + }, + { + "id": "ipc-handlers", + "type": "text", + "text": "IPC Handlers\n\nManual validation\nunknown params\nNo shared types", + "x": 600, + "y": 0, + "width": 180, + "height": 100, + "color": "#ef4444" + }, + { + "id": "group-electron", + "type": "group", + "label": "Electron Process (apps/desktop)", + "x": 330, + "y": -20, + "width": 470, + "height": 310, + "color": "#1e293b" + }, + { + "id": "group-server", + "type": "group", + "label": "Server Process (apps/server)", + "x": 580, + "y": 330, + "width": 520, + "height": 160, + "color": "#374151" + }, + { + "id": "group-contracts", + "type": "group", + "label": "Type Contracts (packages/contracts)", + "x": 330, + "y": 430, + "width": 220, + "height": 140, + "color": "#0f766e" + } + ], + "edges": [ + { + "id": "renderer-to-preload", + "fromNode": "react-renderer", + "toNode": "preload-script", + "fromSide": "right", + "toSide": "left", + "label": "desktopBridge.*", + "pv": { + "edgeType": "ipc" + } + }, + { + "id": "preload-to-main", + "fromNode": "preload-script", + "toNode": "electron-main", + "fromSide": "right", + "toSide": "left", + "label": "ipcRenderer.invoke", + "pv": { + "edgeType": "ipc" + } + }, + { + "id": "renderer-to-server", + "fromNode": "react-renderer", + "toNode": "node-server", + "fromSide": "right", + "toSide": "left", + "label": "WebSocket\n(token auth)", + "pv": { + "edgeType": "websocket" + } + }, + { + "id": "main-spawns-server", + "fromNode": "electron-main", + "toNode": "node-server", + "fromSide": "bottom", + "toSide": "top", + "label": "spawn child", + "pv": { + "edgeType": "process" + } + }, + { + "id": "server-to-codex", + "fromNode": "node-server", + "toNode": "codex-process", + "fromSide": "right", + "toSide": "left", + "label": "JSON-RPC stdio", + "pv": { + "edgeType": "external" + } + }, + { + "id": "preload-uses-interface", + "fromNode": "preload-script", + "toNode": "desktop-bridge-interface", + "fromSide": "top", + "toSide": "bottom", + "label": "satisfies", + "pv": { + "edgeType": "type-check" + } + }, + { + "id": "handlers-no-types", + "fromNode": "electron-main", + "toNode": "ipc-handlers", + "fromSide": "top", + "toSide": "bottom", + "label": "unknown", + "pv": { + "edgeType": "weak-type" + } + }, + { + "id": "ws-uses-contracts", + "fromNode": "node-server", + "toNode": "ws-contracts", + "fromSide": "left", + "toSide": "right", + "label": "Schema.decode", + "pv": { + "edgeType": "type-check" + } + }, + { + "id": "renderer-uses-contracts", + "fromNode": "react-renderer", + "toNode": "ws-contracts", + "fromSide": "bottom", + "toSide": "left", + "label": "typed API", + "pv": { + "edgeType": "type-check" + } + } + ], + "pv": { + "name": "IPC Architecture", + "version": "1.0.0", + "description": "Inter-process communication architecture showing Electron IPC and WebSocket layers with their type-safety characteristics", + "markdown": ".principal-views/ipc-architecture/ipc-architecture.md", + "edgeTypes": { + "ipc": { + "style": "solid", + "color": "#8b5cf6", + "width": 2 + }, + "websocket": { + "style": "solid", + "color": "#3b82f6", + "width": 3 + }, + "process": { + "style": "dashed", + "color": "#22c55e", + "width": 2 + }, + "external": { + "style": "dotted", + "color": "#94a3b8", + "width": 2 + }, + "type-check": { + "style": "dashed", + "color": "#06b6d4", + "width": 1 + }, + "weak-type": { + "style": "dotted", + "color": "#ef4444", + "width": 1 + } + } + } +} diff --git a/.principal-views/ipc-architecture/ipc-architecture.md b/.principal-views/ipc-architecture/ipc-architecture.md new file mode 100644 index 000000000000..3da570b87452 --- /dev/null +++ b/.principal-views/ipc-architecture/ipc-architecture.md @@ -0,0 +1,97 @@ +# IPC Architecture + +T3 Code uses a multi-layered inter-process communication architecture to connect the React UI with backend services while maintaining security through process isolation. + +## Problem + +Desktop applications need secure communication between: +1. The browser-based UI (renderer process) +2. Native OS capabilities (Electron main process) +3. Backend services (Node.js server) +4. External AI providers (codex app-server) + +Each boundary has different security and type-safety requirements. + +## Architecture Layers + +### Layer 1: Electron IPC (UI to Desktop) + +Handles native desktop operations that require OS-level access: +- Folder picker dialogs +- Confirmation dialogs +- Native context menus +- Opening external URLs +- Auto-update management + +**Type Safety**: One-directional. The preload script uses `satisfies DesktopBridge` to ensure the exposed API matches the interface, but the main process handlers accept `unknown` parameters and perform manual runtime validation. + +### Layer 2: WebSocket RPC (UI to Server) + +Handles all application logic through a WebSocket connection: +- Terminal session management +- Project file operations +- Git operations +- Orchestration commands +- Provider session control + +**Type Safety**: Full bidirectional. Uses Effect/Schema for runtime validation on both client and server. Request/response types are defined in `packages/contracts/src/ws.ts`. + +### Layer 3: Process Management (Electron to Server) + +The Electron main process spawns and manages the Node.js server: +- Random port allocation on loopback interface +- 24-byte auth token generation +- Auto-restart with exponential backoff on crashes +- Log capture to rotating files + +### Layer 4: External Provider (Server to Codex) + +The server communicates with `codex app-server` via JSON-RPC over stdio: +- One process per provider session +- Structured event streaming +- Process lifecycle management + +## Design Choices + +### Why Two IPC Mechanisms? + +1. **Electron IPC** is required for native OS operations that can't run in a browser context +2. **WebSocket** enables the same UI to work in both desktop and web modes with identical backend communication + +### Why Minimal Type Safety on Electron IPC? + +The Electron IPC surface is small (8 methods) and stable. The team chose to invest type-safety effort in the WebSocket layer which has 25+ RPC methods and evolves more frequently. + +### Security Model + +- Context isolation enabled (renderer can't access Node.js APIs) +- Token-based authentication on WebSocket connections +- URL validation before opening external links +- Sandbox mode for preload scripts + +## Common Workflows + +### Desktop Startup +1. Electron main generates auth token and allocates port +2. Server spawned as child process with token in environment +3. Main creates browser window with WebSocket URL in preload environment +4. Renderer connects to server via WebSocket with token auth + +### Native Dialog +1. Renderer calls `window.desktopBridge.pickFolder()` +2. Preload forwards via `ipcRenderer.invoke()` +3. Main handles with `ipcMain.handle()`, shows native dialog +4. Result flows back through same path + +### Server RPC +1. Renderer calls `nativeApi.git.status()` +2. WsTransport sends tagged JSON message +3. Server validates with Effect/Schema, executes handler +4. Response sent back, client resolves promise + +## Error Handling + +- **WebSocket disconnect**: Auto-reconnect with exponential backoff (500ms to 8s) +- **Server crash**: Electron restarts child process with backoff (500ms to 10s) +- **Invalid IPC message**: Logged and ignored (doesn't crash handler) +- **Schema validation failure**: Error response sent to client diff --git a/.principal-views/library.yaml b/.principal-views/library.yaml new file mode 100644 index 000000000000..cfd6f29bc3b3 --- /dev/null +++ b/.principal-views/library.yaml @@ -0,0 +1,3 @@ +name: t3code +version: 1.0.0 +description: T3 Code instrumentation library for provider session telemetry diff --git a/.principal-views/orchestration-engine/orchestration-engine-workflow/command-dispatch.workflow.json b/.principal-views/orchestration-engine/orchestration-engine-workflow/command-dispatch.workflow.json new file mode 100644 index 000000000000..6c7972b98852 --- /dev/null +++ b/.principal-views/orchestration-engine/orchestration-engine-workflow/command-dispatch.workflow.json @@ -0,0 +1,71 @@ +{ + "version": "1.0.0", + "canvas": ".principal-views/orchestration-engine/orchestration-engine.otel.canvas", + "name": "Command Dispatch", + "description": "Orchestration command processing through event sourcing pipeline", + "spanPattern": "orchestration.command.dispatch", + "mode": "span-tree", + "status": "draft", + "scenarios": [ + { + "id": "success", + "priority": 1, + "description": "Command processed and events committed successfully", + "template": { + "events": { + "command.dispatch.started": "Dispatching {{command.type}} command {{command.id}} for {{aggregate.kind}} {{aggregate.id}}", + "command.receipt.checked": "Command {{command.id}}: receipt found={{receipt.found}}, status={{receipt.status}}", + "command.decision.started": "Command {{command.id}}: starting decision for {{command.type}} at sequence {{readmodel.sequence}}", + "command.decision.completed": "Command {{command.id}}: decision produced {{event.count}} event(s)", + "command.transaction.started": "Command {{command.id}}: starting transaction for {{event.count}} event(s)", + "command.event.appended": "Command {{command.id}}: appended event {{event.id}} ({{event.type}}) at sequence {{event.sequence}}", + "command.transaction.committed": "Command {{command.id}}: committed {{event.count}} event(s), final sequence {{final.sequence}}", + "command.dispatch.completed": "Command {{command.id}} completed at sequence {{result.sequence}} in {{duration.ms}}ms" + }, + "summary": "Command {{command.id}} committed at sequence {{result.sequence}}" + } + }, + { + "id": "idempotent-hit", + "priority": 2, + "description": "Command already processed, returning cached result", + "template": { + "events": { + "command.dispatch.started": "Dispatching {{command.type}} command {{command.id}} for {{aggregate.kind}} {{aggregate.id}}", + "command.receipt.checked": "Command {{command.id}}: receipt found={{receipt.found}}, status={{receipt.status}}", + "command.idempotent.hit": "Command {{command.id}}: idempotent hit, returning cached sequence {{cached.sequence}}" + }, + "summary": "Command {{command.id}} idempotent hit (sequence {{cached.sequence}})" + } + }, + { + "id": "previously-rejected", + "priority": 3, + "description": "Command was previously rejected", + "template": { + "events": { + "command.dispatch.started": "Dispatching {{command.type}} command {{command.id}} for {{aggregate.kind}} {{aggregate.id}}", + "command.receipt.checked": "Command {{command.id}}: receipt found={{receipt.found}}, status={{receipt.status}}", + "command.previously.rejected": "Command {{command.id}}: previously rejected - {{rejection.detail}}" + }, + "summary": "Command {{command.id}} previously rejected" + } + }, + { + "id": "invariant-rejected", + "priority": 4, + "description": "Command rejected due to invariant violation", + "template": { + "events": { + "command.dispatch.started": "Dispatching {{command.type}} command {{command.id}} for {{aggregate.kind}} {{aggregate.id}}", + "command.receipt.checked": "Command {{command.id}}: receipt found={{receipt.found}}, status={{receipt.status}}", + "command.decision.started": "Command {{command.id}}: starting decision for {{command.type}} at sequence {{readmodel.sequence}}", + "command.invariant.violated": "Command {{command.id}}: invariant violated for {{command.type}} - {{violation.detail}}", + "command.dispatch.failed": "Command {{command.id}}: dispatch failed - {{error.type}}: {{error.message}}", + "readmodel.reconciliation.started": "Command {{command.id}}: reconciling read model from sequence {{from.sequence}}" + }, + "summary": "Command {{command.id}} rejected: {{violation.detail}}" + } + } + ] +} diff --git a/.principal-views/orchestration-engine/orchestration-engine.md b/.principal-views/orchestration-engine/orchestration-engine.md new file mode 100644 index 000000000000..b5543e6254f3 --- /dev/null +++ b/.principal-views/orchestration-engine/orchestration-engine.md @@ -0,0 +1,33 @@ +# Orchestration Engine + +Telemetry schema for T3 Code's event sourcing command processing engine. + +## Overview + +The Orchestration Engine processes commands through an event sourcing pattern: + +- **Command Dispatch**: Commands enter a queue with deferred results +- **Idempotency Check**: Receipt lookup prevents duplicate processing +- **Decision Phase**: Business logic determines events to emit +- **Transaction**: Atomic event append, projection, and receipt recording +- **Publication**: Events broadcast to subscribers + +## Workflows + +### command-dispatch + +Tracks the complete command processing lifecycle: +1. Command queued for processing +2. Receipt lookup (idempotency check) +3. Decision phase (business logic) +4. Transaction commit (events + receipt) +5. Event publication and result resolution + +Scenarios: success, idempotent-hit, invariant-rejected, previously-rejected + +## Key Files + +- `apps/server/src/orchestration/Layers/OrchestrationEngine.ts` - Command processing and dispatch +- `apps/server/src/persistence/Layers/OrchestrationEventStore.ts` - Event persistence +- `apps/server/src/orchestration/decider.ts` - Business logic decisions +- `apps/server/src/orchestration/projector.ts` - Read model projection diff --git a/.principal-views/orchestration-engine/orchestration-engine.otel.canvas b/.principal-views/orchestration-engine/orchestration-engine.otel.canvas new file mode 100644 index 000000000000..35e65c5e5bf9 --- /dev/null +++ b/.principal-views/orchestration-engine/orchestration-engine.otel.canvas @@ -0,0 +1,609 @@ +{ + "nodes": [ + { + "id": "command-dispatch-started", + "type": "text", + "text": "Command Dispatch Started", + "x": 100, + "y": 50, + "width": 200, + "height": 100, + "color": "#3b82f6", + "pv": { + "status": "draft", + "event": { + "name": "command.dispatch.started", + "attributes": { + "command.id": { + "type": "string", + "description": "Unique command identifier", + "required": true + }, + "command.type": { + "type": "string", + "description": "Command type (e.g., thread.message.add)", + "required": true + }, + "aggregate.kind": { + "type": "string", + "description": "Aggregate type: project or thread", + "required": true + }, + "aggregate.id": { + "type": "string", + "description": "Project or thread ID", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/Layers/OrchestrationEngine.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "command-receipt-checked", + "type": "text", + "text": "Receipt Checked", + "x": 100, + "y": 200, + "width": 200, + "height": 100, + "color": "#3b82f6", + "pv": { + "status": "draft", + "event": { + "name": "command.receipt.checked", + "attributes": { + "command.id": { + "type": "string", + "description": "Command identifier", + "required": true + }, + "receipt.found": { + "type": "boolean", + "description": "Whether an existing receipt was found", + "required": true + }, + "receipt.status": { + "type": "string", + "description": "Receipt status if found: accepted or rejected", + "required": false + } + } + }, + "references": ["apps/server/src/orchestration/Layers/OrchestrationEngine.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "command-idempotent-hit", + "type": "text", + "text": "Idempotent Hit", + "x": 400, + "y": 200, + "width": 200, + "height": 100, + "color": "#22c55e", + "pv": { + "status": "draft", + "event": { + "name": "command.idempotent.hit", + "attributes": { + "command.id": { + "type": "string", + "description": "Command identifier", + "required": true + }, + "cached.sequence": { + "type": "integer", + "description": "Cached result sequence number", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/Layers/OrchestrationEngine.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "command-previously-rejected", + "type": "text", + "text": "Previously Rejected", + "x": 400, + "y": 350, + "width": 200, + "height": 100, + "color": "#f59e0b", + "pv": { + "status": "draft", + "event": { + "name": "command.previously.rejected", + "attributes": { + "command.id": { + "type": "string", + "description": "Command identifier", + "required": true + }, + "rejection.detail": { + "type": "string", + "description": "Original rejection reason", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/Layers/OrchestrationEngine.ts"], + "otel": { + "kind": "event", + "category": "warning" + } + } + }, + { + "id": "command-decision-started", + "type": "text", + "text": "Decision Started", + "x": 100, + "y": 350, + "width": 200, + "height": 100, + "color": "#8b5cf6", + "pv": { + "status": "draft", + "event": { + "name": "command.decision.started", + "attributes": { + "command.id": { + "type": "string", + "description": "Command identifier", + "required": true + }, + "command.type": { + "type": "string", + "description": "Command type", + "required": true + }, + "readmodel.sequence": { + "type": "integer", + "description": "Current read model sequence", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/decider.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "command-decision-completed", + "type": "text", + "text": "Decision Completed", + "x": 100, + "y": 500, + "width": 200, + "height": 100, + "color": "#8b5cf6", + "pv": { + "status": "draft", + "event": { + "name": "command.decision.completed", + "attributes": { + "command.id": { + "type": "string", + "description": "Command identifier", + "required": true + }, + "event.count": { + "type": "integer", + "description": "Number of events produced", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/decider.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "command-transaction-started", + "type": "text", + "text": "Transaction Started", + "x": 100, + "y": 650, + "width": 200, + "height": 100, + "color": "#06b6d4", + "pv": { + "status": "draft", + "event": { + "name": "command.transaction.started", + "attributes": { + "command.id": { + "type": "string", + "description": "Command identifier", + "required": true + }, + "event.count": { + "type": "integer", + "description": "Number of events to persist", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/Layers/OrchestrationEngine.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "command-event-appended", + "type": "text", + "text": "Event Appended", + "x": 100, + "y": 800, + "width": 200, + "height": 100, + "color": "#06b6d4", + "pv": { + "status": "draft", + "event": { + "name": "command.event.appended", + "attributes": { + "command.id": { + "type": "string", + "description": "Command identifier", + "required": true + }, + "event.id": { + "type": "string", + "description": "Persisted event ID", + "required": true + }, + "event.type": { + "type": "string", + "description": "Event type", + "required": true + }, + "event.sequence": { + "type": "integer", + "description": "Assigned sequence number", + "required": true + } + } + }, + "references": ["apps/server/src/persistence/Layers/OrchestrationEventStore.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "command-transaction-committed", + "type": "text", + "text": "Transaction Committed", + "x": 100, + "y": 950, + "width": 200, + "height": 100, + "color": "#22c55e", + "pv": { + "status": "draft", + "event": { + "name": "command.transaction.committed", + "attributes": { + "command.id": { + "type": "string", + "description": "Command identifier", + "required": true + }, + "final.sequence": { + "type": "integer", + "description": "Final sequence number after commit", + "required": true + }, + "event.count": { + "type": "integer", + "description": "Number of events committed", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/Layers/OrchestrationEngine.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "command-dispatch-completed", + "type": "text", + "text": "Dispatch Completed", + "x": 100, + "y": 1100, + "width": 200, + "height": 100, + "color": "#22c55e", + "pv": { + "status": "draft", + "event": { + "name": "command.dispatch.completed", + "attributes": { + "command.id": { + "type": "string", + "description": "Command identifier", + "required": true + }, + "result.sequence": { + "type": "integer", + "description": "Result sequence number", + "required": true + }, + "duration.ms": { + "type": "number", + "description": "Total dispatch duration", + "required": false + } + } + }, + "references": ["apps/server/src/orchestration/Layers/OrchestrationEngine.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "command-invariant-violated", + "type": "text", + "text": "Invariant Violated", + "x": 400, + "y": 500, + "width": 200, + "height": 100, + "color": "#ef4444", + "pv": { + "status": "draft", + "event": { + "name": "command.invariant.violated", + "attributes": { + "command.id": { + "type": "string", + "description": "Command identifier", + "required": true + }, + "command.type": { + "type": "string", + "description": "Command type that violated invariant", + "required": true + }, + "violation.detail": { + "type": "string", + "description": "Invariant violation message", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/Layers/OrchestrationEngine.ts"], + "otel": { + "kind": "event", + "category": "error" + } + } + }, + { + "id": "command-dispatch-failed", + "type": "text", + "text": "Dispatch Failed", + "x": 400, + "y": 650, + "width": 200, + "height": 100, + "color": "#ef4444", + "pv": { + "status": "draft", + "event": { + "name": "command.dispatch.failed", + "attributes": { + "command.id": { + "type": "string", + "description": "Command identifier", + "required": true + }, + "error.type": { + "type": "string", + "description": "Error type classification", + "required": true + }, + "error.message": { + "type": "string", + "description": "Error message", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/Layers/OrchestrationEngine.ts"], + "otel": { + "kind": "event", + "category": "error" + } + } + }, + { + "id": "readmodel-reconciliation-started", + "type": "text", + "text": "Reconciliation Started", + "x": 400, + "y": 800, + "width": 200, + "height": 100, + "color": "#f59e0b", + "pv": { + "status": "draft", + "event": { + "name": "readmodel.reconciliation.started", + "attributes": { + "command.id": { + "type": "string", + "description": "Command that triggered reconciliation", + "required": true + }, + "from.sequence": { + "type": "integer", + "description": "Sequence to reconcile from", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/Layers/OrchestrationEngine.ts"], + "otel": { + "kind": "event", + "category": "warning" + } + } + } + ], + "edges": [ + { + "id": "dispatch-to-receipt", + "fromNode": "command-dispatch-started", + "toNode": "command-receipt-checked", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "receipt-to-hit", + "fromNode": "command-receipt-checked", + "toNode": "command-idempotent-hit", + "fromSide": "right", + "toSide": "left", + "pv": { "edgeType": "branch" } + }, + { + "id": "receipt-to-rejected", + "fromNode": "command-receipt-checked", + "toNode": "command-previously-rejected", + "fromSide": "right", + "toSide": "left", + "pv": { "edgeType": "error" } + }, + { + "id": "receipt-to-decision", + "fromNode": "command-receipt-checked", + "toNode": "command-decision-started", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "decision-started-to-completed", + "fromNode": "command-decision-started", + "toNode": "command-decision-completed", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "decision-to-invariant", + "fromNode": "command-decision-started", + "toNode": "command-invariant-violated", + "fromSide": "right", + "toSide": "left", + "pv": { "edgeType": "error" } + }, + { + "id": "decision-to-transaction", + "fromNode": "command-decision-completed", + "toNode": "command-transaction-started", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "transaction-to-append", + "fromNode": "command-transaction-started", + "toNode": "command-event-appended", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "append-to-commit", + "fromNode": "command-event-appended", + "toNode": "command-transaction-committed", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "commit-to-completed", + "fromNode": "command-transaction-committed", + "toNode": "command-dispatch-completed", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "invariant-to-failed", + "fromNode": "command-invariant-violated", + "toNode": "command-dispatch-failed", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "error" } + }, + { + "id": "failed-to-reconciliation", + "fromNode": "command-dispatch-failed", + "toNode": "readmodel-reconciliation-started", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "recovery" } + } + ], + "pv": { + "name": "Orchestration Engine", + "version": "1.0.0", + "description": "Telemetry schema for event sourcing command processing", + "markdown": ".principal-views/orchestration-engine/orchestration-engine.md", + "edgeTypes": { + "sequence": { + "style": "solid", + "color": "#64748b", + "width": 2 + }, + "branch": { + "style": "dashed", + "color": "#22c55e", + "width": 2 + }, + "error": { + "style": "dashed", + "color": "#ef4444", + "width": 2 + }, + "recovery": { + "style": "dashed", + "color": "#f59e0b", + "width": 2 + } + } + } +} diff --git a/.principal-views/provider-session/provider-session-workflow/send-turn.workflow.json b/.principal-views/provider-session/provider-session-workflow/send-turn.workflow.json new file mode 100644 index 000000000000..43a5b5aa3b18 --- /dev/null +++ b/.principal-views/provider-session/provider-session-workflow/send-turn.workflow.json @@ -0,0 +1,50 @@ +{ + "version": "1.0.0", + "canvas": ".principal-views/provider-session/provider-session.otel.canvas", + "name": "Send Turn", + "description": "Provider turn submission from request through completion", + "spanPattern": "provider.turn.send", + "mode": "span-tree", + "status": "draft", + "scenarios": [ + { + "id": "success", + "priority": 1, + "description": "Turn completed successfully", + "template": { + "events": { + "turn.start.requested": "Sending turn for thread {{thread.id}} (text: {{has.text.input}}, attachments: {{attachment.count}}, model: {{model}}, mode: {{interaction.mode}})", + "turn.started": "Turn {{turn.id}} started on thread {{thread.id}}", + "turn.completed": "Turn {{turn.id}} completed on {{thread.id}} with status {{status}} in {{duration.ms}}ms" + }, + "summary": "Turn {{turn.id}} completed" + } + }, + { + "id": "failure", + "priority": 2, + "description": "Turn failed with error", + "template": { + "events": { + "turn.start.requested": "Sending turn for thread {{thread.id}} (text: {{has.text.input}}, attachments: {{attachment.count}}, model: {{model}})", + "turn.started": "Turn {{turn.id}} started on thread {{thread.id}}", + "turn.failed": "Turn {{turn.id}} failed on {{thread.id}}: {{error.message}} (will retry: {{will.retry}})" + }, + "summary": "Turn {{turn.id}} failed" + } + }, + { + "id": "interrupted", + "priority": 3, + "description": "Turn was interrupted by user", + "template": { + "events": { + "turn.start.requested": "Sending turn for thread {{thread.id}} (text: {{has.text.input}}, attachments: {{attachment.count}})", + "turn.started": "Turn {{turn.id}} started on thread {{thread.id}}", + "turn.interrupted": "Turn {{turn.id}} interrupted on thread {{thread.id}}" + }, + "summary": "Turn {{turn.id}} interrupted" + } + } + ] +} diff --git a/.principal-views/provider-session/provider-session-workflow/session-start.workflow.json b/.principal-views/provider-session/provider-session-workflow/session-start.workflow.json new file mode 100644 index 000000000000..f59c4c702197 --- /dev/null +++ b/.principal-views/provider-session/provider-session-workflow/session-start.workflow.json @@ -0,0 +1,57 @@ +{ + "version": "1.0.0", + "canvas": ".principal-views/provider-session/provider-session.otel.canvas", + "name": "Session Start", + "description": "Provider session initialization from process spawn through thread open to ready state", + "spanPattern": "provider.session.start", + "mode": "span-tree", + "status": "draft", + "scenarios": [ + { + "id": "success", + "priority": 1, + "description": "Session started successfully without fallback", + "template": { + "events": { + "session.start.initiated": "Starting {{provider.name}} session for thread {{thread.id}} ({{runtime.mode}} mode, model: {{model}}, cwd: {{cwd}}, resume: {{has.resume.cursor}})", + "session.process.spawned": "Thread {{thread.id}}: Spawned process PID {{process.pid}} from {{binary.path}}", + "session.thread.open.requested": "Thread {{thread.id}}: Opening via {{method}} (resume ID: {{resume.thread.id}})", + "session.thread.open.resolved": "Thread {{thread.id}}: Provider thread {{provider.thread.id}} opened via {{method}}", + "session.thread.opened.direct": "Thread {{thread.id}} opened directly without fallback", + "session.ready": "Thread {{thread.id}} ready: provider {{provider.thread.id}}, account {{account.type}}, spark {{spark.enabled}}, took {{duration.ms}}ms" + }, + "summary": "Session {{thread.id}} ready" + } + }, + { + "id": "resume-fallback", + "priority": 2, + "description": "Resume failed but recovered by starting fresh thread", + "template": { + "events": { + "session.start.initiated": "Starting {{provider.name}} session for thread {{thread.id}} ({{runtime.mode}} mode)", + "session.process.spawned": "Thread {{thread.id}}: Spawned process PID {{process.pid}}", + "session.thread.open.requested": "Thread {{thread.id}}: Attempting to resume {{resume.thread.id}} via {{method}}", + "session.thread.resume.fallback": "Thread {{thread.id}}: Resume of {{resume.thread.id}} failed: {{error.message}}, falling back", + "session.thread.open.resolved": "Thread {{thread.id}}: Fresh provider thread {{provider.thread.id}} opened via {{method}}", + "session.ready": "Thread {{thread.id}} ready after fallback: provider {{provider.thread.id}}, account {{account.type}}, took {{duration.ms}}ms" + }, + "summary": "Session {{thread.id}} ready via fallback" + } + }, + { + "id": "failure", + "priority": 3, + "description": "Session failed to start", + "template": { + "events": { + "session.start.initiated": "Starting {{provider.name}} session for thread {{thread.id}}", + "session.process.spawned": "Thread {{thread.id}}: Spawned process PID {{process.pid}}", + "session.thread.open.requested": "Thread {{thread.id}}: Opening via {{method}}", + "session.start.failed": "Thread {{thread.id}} failed at {{error.stage}}: {{error.message}}" + }, + "summary": "Session {{thread.id}} failed" + } + } + ] +} diff --git a/.principal-views/provider-session/provider-session-workflow/session-stop.workflow.json b/.principal-views/provider-session/provider-session-workflow/session-stop.workflow.json new file mode 100644 index 000000000000..7badc8533ba1 --- /dev/null +++ b/.principal-views/provider-session/provider-session-workflow/session-stop.workflow.json @@ -0,0 +1,35 @@ +{ + "version": "1.0.0", + "canvas": ".principal-views/provider-session/provider-session.otel.canvas", + "name": "Session Stop", + "description": "Provider session cleanup and termination", + "spanPattern": "provider.session.stop", + "mode": "span-tree", + "status": "draft", + "scenarios": [ + { + "id": "graceful", + "priority": 1, + "description": "Session stopped gracefully on request", + "template": { + "events": { + "session.stop.requested": "Stopping session for thread {{thread.id}}", + "session.closed": "Session {{thread.id}} closed: {{reason}}" + }, + "summary": "Session {{thread.id}} stopped" + } + }, + { + "id": "unexpected-exit", + "priority": 2, + "description": "Process exited unexpectedly", + "template": { + "events": { + "session.process.exited": "Process for {{thread.id}} exited (code: {{exit.code}}, signal: {{exit.signal}}, expected: {{expected}})", + "session.closed": "Session {{thread.id}} closed: {{reason}}" + }, + "summary": "Session terminated unexpectedly" + } + } + ] +} diff --git a/.principal-views/provider-session/provider-session.md b/.principal-views/provider-session/provider-session.md new file mode 100644 index 000000000000..bf9e31a93147 --- /dev/null +++ b/.principal-views/provider-session/provider-session.md @@ -0,0 +1,48 @@ +# Provider Session Lifecycle + +Telemetry schema for T3 Code's provider session management. + +## Overview + +The Provider Session Lifecycle tracks agent sessions through the Codex app-server JSON-RPC protocol. It covers: + +- **Session Start**: Spawning the app-server process, initializing JSON-RPC, reading account info, and opening/resuming threads +- **Turn Submission**: Sending user input to the provider, model resolution, and turn state tracking +- **Session Stop**: Graceful cleanup, process termination, and state persistence + +## Workflows + +### session-start + +Tracks the full session initialization flow: +1. Process spawn and JSON-RPC initialization +2. Account snapshot capture +3. Thread open (fresh start or resume with fallback) +4. Ready state transition + +Scenarios: success, failure, resume-success, resume-fallback, timeout + +### send-turn + +Tracks turn submission and completion: +1. Input validation +2. Model resolution +3. Turn start RPC call +4. Turn completion (success/failure/interrupt) + +Scenarios: success, failure, interrupted + +### session-stop + +Tracks session cleanup: +1. Pending request cancellation +2. Process termination +3. State persistence +4. Resource cleanup + +Scenarios: success, forced-exit + +## Key Files + +- `apps/server/src/codexAppServerManager.ts` - Core session/turn management +- `apps/server/src/provider/Layers/ProviderService.ts` - Cross-provider orchestration diff --git a/.principal-views/provider-session/provider-session.otel.canvas b/.principal-views/provider-session/provider-session.otel.canvas new file mode 100644 index 000000000000..a12f096ea3ab --- /dev/null +++ b/.principal-views/provider-session/provider-session.otel.canvas @@ -0,0 +1,807 @@ +{ + "nodes": [ + { + "id": "session-start-initiated", + "type": "text", + "text": "Session Start Initiated", + "x": 100, + "y": 50, + "width": 200, + "height": 100, + "color": "#3b82f6", + "pv": { + "status": "draft", + "event": { + "name": "session.start.initiated", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier for the session", + "required": true + }, + "provider.name": { + "type": "string", + "description": "Provider name (e.g., codex)", + "required": true + }, + "runtime.mode": { + "type": "string", + "description": "Runtime mode: approval-required or full-access", + "required": true + }, + "cwd": { + "type": "string", + "description": "Working directory for the session", + "required": false + }, + "model": { + "type": "string", + "description": "Requested model slug", + "required": false + }, + "has.resume.cursor": { + "type": "boolean", + "description": "Whether resuming an existing thread", + "required": true + } + } + }, + "references": ["apps/server/src/codexAppServerManager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "session-process-spawned", + "type": "text", + "text": "Process Spawned", + "x": 100, + "y": 200, + "width": 200, + "height": 100, + "color": "#3b82f6", + "pv": { + "status": "draft", + "event": { + "name": "session.process.spawned", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "process.pid": { + "type": "integer", + "description": "Child process PID", + "required": false + }, + "binary.path": { + "type": "string", + "description": "Path to codex binary", + "required": false + } + } + }, + "references": ["apps/server/src/codexAppServerManager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "session-thread-open-requested", + "type": "text", + "text": "Thread Open Requested", + "x": 100, + "y": 350, + "width": 200, + "height": 100, + "color": "#3b82f6", + "pv": { + "status": "draft", + "event": { + "name": "session.thread.open.requested", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "method": { + "type": "string", + "description": "RPC method: thread/start or thread/resume", + "required": true + }, + "resume.thread.id": { + "type": "string", + "description": "Provider thread ID to resume", + "required": false + } + } + }, + "references": ["apps/server/src/codexAppServerManager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "session-thread-open-resolved", + "type": "text", + "text": "Thread Open Resolved", + "x": 100, + "y": 500, + "width": 200, + "height": 100, + "color": "#22c55e", + "pv": { + "status": "draft", + "event": { + "name": "session.thread.open.resolved", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "method": { + "type": "string", + "description": "RPC method that succeeded", + "required": true + }, + "provider.thread.id": { + "type": "string", + "description": "Provider-assigned thread ID", + "required": true + } + } + }, + "references": ["apps/server/src/codexAppServerManager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "session-thread-opened-direct", + "type": "text", + "text": "Thread Opened Direct", + "x": 100, + "y": 650, + "width": 200, + "height": 100, + "color": "#22c55e", + "pv": { + "status": "draft", + "event": { + "name": "session.thread.opened.direct", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + } + } + }, + "references": ["apps/server/src/codexAppServerManager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "session-ready", + "type": "text", + "text": "Session Ready", + "x": 100, + "y": 800, + "width": 200, + "height": 100, + "color": "#22c55e", + "pv": { + "status": "draft", + "event": { + "name": "session.ready", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "provider.thread.id": { + "type": "string", + "description": "Provider-assigned thread ID", + "required": true + }, + "account.type": { + "type": "string", + "description": "Account type: apiKey, chatgpt, unknown", + "required": false + }, + "spark.enabled": { + "type": "boolean", + "description": "Whether spark model is enabled", + "required": false + }, + "duration.ms": { + "type": "number", + "description": "Time to reach ready state", + "required": false + } + } + }, + "references": ["apps/server/src/codexAppServerManager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "session-start-failed", + "type": "text", + "text": "Session Start Failed", + "x": 450, + "y": 350, + "width": 200, + "height": 100, + "color": "#ef4444", + "pv": { + "status": "draft", + "event": { + "name": "session.start.failed", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "error.message": { + "type": "string", + "description": "Error message", + "required": true + }, + "error.stage": { + "type": "string", + "description": "Stage where failure occurred", + "required": false + } + } + }, + "references": ["apps/server/src/codexAppServerManager.ts"], + "otel": { + "kind": "event", + "category": "error" + } + } + }, + { + "id": "session-thread-resume-fallback", + "type": "text", + "text": "Resume Fallback", + "x": 450, + "y": 500, + "width": 200, + "height": 100, + "color": "#f59e0b", + "pv": { + "status": "draft", + "event": { + "name": "session.thread.resume.fallback", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "resume.thread.id": { + "type": "string", + "description": "Thread ID that failed to resume", + "required": true + }, + "error.message": { + "type": "string", + "description": "Resume failure reason", + "required": true + } + } + }, + "references": ["apps/server/src/codexAppServerManager.ts"], + "otel": { + "kind": "event", + "category": "warning" + } + } + }, + { + "id": "turn-start-requested", + "type": "text", + "text": "Turn Start Requested", + "x": 800, + "y": 50, + "width": 200, + "height": 100, + "color": "#8b5cf6", + "pv": { + "status": "draft", + "event": { + "name": "turn.start.requested", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "has.text.input": { + "type": "boolean", + "description": "Whether turn has text input", + "required": true + }, + "attachment.count": { + "type": "integer", + "description": "Number of attachments", + "required": true + }, + "model": { + "type": "string", + "description": "Model slug for this turn", + "required": false + }, + "interaction.mode": { + "type": "string", + "description": "Interaction mode: default or plan", + "required": false + } + } + }, + "references": ["apps/server/src/codexAppServerManager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "turn-started", + "type": "text", + "text": "Turn Started", + "x": 800, + "y": 200, + "width": 200, + "height": 100, + "color": "#8b5cf6", + "pv": { + "status": "draft", + "event": { + "name": "turn.started", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "turn.id": { + "type": "string", + "description": "Turn identifier assigned by provider", + "required": true + } + } + }, + "references": ["apps/server/src/codexAppServerManager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "turn-completed", + "type": "text", + "text": "Turn Completed", + "x": 800, + "y": 350, + "width": 200, + "height": 100, + "color": "#22c55e", + "pv": { + "status": "draft", + "event": { + "name": "turn.completed", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "turn.id": { + "type": "string", + "description": "Turn identifier", + "required": true + }, + "status": { + "type": "string", + "description": "Turn completion status", + "required": true + }, + "duration.ms": { + "type": "number", + "description": "Turn duration in milliseconds", + "required": false + } + } + }, + "references": ["apps/server/src/codexAppServerManager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "turn-failed", + "type": "text", + "text": "Turn Failed", + "x": 1100, + "y": 200, + "width": 200, + "height": 100, + "color": "#ef4444", + "pv": { + "status": "draft", + "event": { + "name": "turn.failed", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "turn.id": { + "type": "string", + "description": "Turn identifier", + "required": false + }, + "error.message": { + "type": "string", + "description": "Error message", + "required": true + }, + "will.retry": { + "type": "boolean", + "description": "Whether provider will retry", + "required": false + } + } + }, + "references": ["apps/server/src/codexAppServerManager.ts"], + "otel": { + "kind": "event", + "category": "error" + } + } + }, + { + "id": "turn-interrupted", + "type": "text", + "text": "Turn Interrupted", + "x": 1100, + "y": 350, + "width": 200, + "height": 100, + "color": "#f59e0b", + "pv": { + "status": "draft", + "event": { + "name": "turn.interrupted", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "turn.id": { + "type": "string", + "description": "Turn identifier", + "required": true + } + } + }, + "references": ["apps/server/src/codexAppServerManager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "session-stop-requested", + "type": "text", + "text": "Stop Requested", + "x": 1400, + "y": 50, + "width": 200, + "height": 100, + "color": "#64748b", + "pv": { + "status": "draft", + "event": { + "name": "session.stop.requested", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + } + } + }, + "references": ["apps/server/src/codexAppServerManager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "session-closed", + "type": "text", + "text": "Session Closed", + "x": 1400, + "y": 200, + "width": 200, + "height": 100, + "color": "#64748b", + "pv": { + "status": "draft", + "event": { + "name": "session.closed", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "reason": { + "type": "string", + "description": "Reason for closure: requested, process-exit, error", + "required": true + } + } + }, + "references": ["apps/server/src/codexAppServerManager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "session-process-exited", + "type": "text", + "text": "Process Exited", + "x": 1400, + "y": 350, + "width": 200, + "height": 100, + "color": "#ef4444", + "pv": { + "status": "draft", + "event": { + "name": "session.process.exited", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "exit.code": { + "type": "integer", + "description": "Process exit code", + "required": false + }, + "exit.signal": { + "type": "string", + "description": "Signal that caused exit", + "required": false + }, + "expected": { + "type": "boolean", + "description": "Whether exit was expected", + "required": true + } + } + }, + "references": ["apps/server/src/codexAppServerManager.ts"], + "otel": { + "kind": "event", + "category": "error" + } + } + } + ], + "edges": [ + { + "id": "start-to-spawn", + "fromNode": "session-start-initiated", + "toNode": "session-process-spawned", + "fromSide": "bottom", + "toSide": "top", + "pv": { + "edgeType": "sequence" + } + }, + { + "id": "spawn-to-thread", + "fromNode": "session-process-spawned", + "toNode": "session-thread-open-requested", + "fromSide": "bottom", + "toSide": "top", + "pv": { + "edgeType": "sequence" + } + }, + { + "id": "thread-to-resolved", + "fromNode": "session-thread-open-requested", + "toNode": "session-thread-open-resolved", + "fromSide": "bottom", + "toSide": "top", + "pv": { + "edgeType": "sequence" + } + }, + { + "id": "resolved-to-direct", + "fromNode": "session-thread-open-resolved", + "toNode": "session-thread-opened-direct", + "fromSide": "bottom", + "toSide": "top", + "pv": { + "edgeType": "sequence" + } + }, + { + "id": "direct-to-ready", + "fromNode": "session-thread-opened-direct", + "toNode": "session-ready", + "fromSide": "bottom", + "toSide": "top", + "pv": { + "edgeType": "sequence" + } + }, + { + "id": "thread-to-failed", + "fromNode": "session-thread-open-requested", + "toNode": "session-start-failed", + "fromSide": "right", + "toSide": "left", + "pv": { + "edgeType": "error" + } + }, + { + "id": "thread-to-fallback", + "fromNode": "session-thread-open-requested", + "toNode": "session-thread-resume-fallback", + "fromSide": "right", + "toSide": "left", + "pv": { + "edgeType": "fallback" + } + }, + { + "id": "fallback-to-resolved", + "fromNode": "session-thread-resume-fallback", + "toNode": "session-thread-open-resolved", + "fromSide": "bottom", + "toSide": "right", + "pv": { + "edgeType": "fallback" + } + }, + { + "id": "fallback-resolved-to-ready", + "fromNode": "session-thread-open-resolved", + "toNode": "session-ready", + "fromSide": "right", + "toSide": "right", + "pv": { + "edgeType": "fallback" + } + }, + { + "id": "turn-request-to-started", + "fromNode": "turn-start-requested", + "toNode": "turn-started", + "fromSide": "bottom", + "toSide": "top", + "pv": { + "edgeType": "sequence" + } + }, + { + "id": "turn-started-to-completed", + "fromNode": "turn-started", + "toNode": "turn-completed", + "fromSide": "bottom", + "toSide": "top", + "pv": { + "edgeType": "sequence" + } + }, + { + "id": "turn-started-to-failed", + "fromNode": "turn-started", + "toNode": "turn-failed", + "fromSide": "right", + "toSide": "left", + "pv": { + "edgeType": "error" + } + }, + { + "id": "turn-started-to-interrupted", + "fromNode": "turn-started", + "toNode": "turn-interrupted", + "fromSide": "right", + "toSide": "left", + "pv": { + "edgeType": "interrupt" + } + }, + { + "id": "stop-to-closed", + "fromNode": "session-stop-requested", + "toNode": "session-closed", + "fromSide": "bottom", + "toSide": "top", + "pv": { + "edgeType": "sequence" + } + }, + { + "id": "closed-to-exited", + "fromNode": "session-closed", + "toNode": "session-process-exited", + "fromSide": "bottom", + "toSide": "top", + "pv": { + "edgeType": "sequence" + } + } + ], + "pv": { + "name": "Provider Session", + "version": "1.0.0", + "description": "Telemetry schema for T3 Code provider session lifecycle", + "markdown": ".principal-views/provider-session/provider-session.md", + "edgeTypes": { + "sequence": { + "style": "solid", + "color": "#64748b", + "width": 2 + }, + "error": { + "style": "dashed", + "color": "#ef4444", + "width": 2 + }, + "fallback": { + "style": "dashed", + "color": "#f59e0b", + "width": 2 + }, + "interrupt": { + "style": "dashed", + "color": "#f59e0b", + "width": 2 + } + } + } +} diff --git a/.principal-views/runtime-ingestion/runtime-ingestion-workflow/event-ingestion.workflow.json b/.principal-views/runtime-ingestion/runtime-ingestion-workflow/event-ingestion.workflow.json new file mode 100644 index 000000000000..cb49cdd77807 --- /dev/null +++ b/.principal-views/runtime-ingestion/runtime-ingestion-workflow/event-ingestion.workflow.json @@ -0,0 +1,81 @@ +{ + "version": "1.0.0", + "canvas": ".principal-views/runtime-ingestion/runtime-ingestion.otel.canvas", + "name": "Event Ingestion", + "description": "Provider runtime event processing from reception to domain event emission", + "spanPattern": "ingestion.event.lifecycle", + "mode": "span-tree", + "status": "draft", + "scenarios": [ + { + "id": "message-event", + "priority": 1, + "description": "Message event processed and domain event emitted", + "template": { + "events": { + "ingestion.event.received": "Thread {{thread.id}}: received {{event.type}} event for turn {{turn.id}}", + "ingestion.event.classified": "Thread {{thread.id}}: {{event.type}} classified as {{event.category}}", + "ingestion.message.buffered": "Thread {{thread.id}}: message {{message.id}} buffered ({{content.type}}, {{buffer.size}} bytes)", + "ingestion.message.complete": "Thread {{thread.id}}: message {{message.id}} complete ({{content.type}})", + "ingestion.domain.event.emitted": "Thread {{thread.id}}: emitted {{domain.event.type}} from {{source.event.type}}" + }, + "summary": "Thread {{thread.id}} message event processed" + } + }, + { + "id": "tool-event", + "priority": 2, + "description": "Tool invocation event processed", + "template": { + "events": { + "ingestion.event.received": "Thread {{thread.id}}: received {{event.type}} event", + "ingestion.event.classified": "Thread {{thread.id}}: {{event.type}} classified as {{event.category}}", + "ingestion.tool.started": "Thread {{thread.id}}: tool {{tool.name}} started ({{tool.id}})", + "ingestion.tool.completed": "Thread {{thread.id}}: tool {{tool.name}} completed with {{tool.status}} ({{tool.id}})", + "ingestion.domain.event.emitted": "Thread {{thread.id}}: emitted {{domain.event.type}} from {{source.event.type}}" + }, + "summary": "Thread {{thread.id}} tool {{tool.name}} processed" + } + }, + { + "id": "turn-complete", + "priority": 3, + "description": "Turn completed and finalized", + "template": { + "events": { + "ingestion.event.received": "Thread {{thread.id}}: received {{event.type}} event for turn {{turn.id}}", + "ingestion.event.classified": "Thread {{thread.id}}: {{event.type}} classified as {{event.category}}", + "ingestion.domain.event.emitted": "Thread {{thread.id}}: emitted {{domain.event.type}} from {{source.event.type}}", + "ingestion.turn.finalized": "Thread {{thread.id}}: turn {{turn.id}} finalized ({{message.count}} messages, {{tool.count}} tools)" + }, + "summary": "Thread {{thread.id}} turn {{turn.id}} finalized" + } + }, + { + "id": "event-dropped", + "priority": 4, + "description": "Event dropped without processing", + "template": { + "events": { + "ingestion.event.received": "Thread {{thread.id}}: received {{event.type}} event", + "ingestion.event.classified": "Thread {{thread.id}}: {{event.type}} classified as {{event.category}}", + "ingestion.event.dropped": "Thread {{thread.id}}: dropped {{event.type}} - {{drop.reason}}" + }, + "summary": "Thread {{thread.id}} event dropped: {{drop.reason}}" + } + }, + { + "id": "error-event", + "priority": 5, + "description": "Error event received from provider", + "template": { + "events": { + "ingestion.event.received": "Thread {{thread.id}}: received {{event.type}} event", + "ingestion.event.classified": "Thread {{thread.id}}: {{event.type}} classified as {{event.category}}", + "ingestion.error.received": "Thread {{thread.id}}: error received - {{error.type}}: {{error.message}}" + }, + "summary": "Thread {{thread.id}} error: {{error.message}}" + } + } + ] +} diff --git a/.principal-views/runtime-ingestion/runtime-ingestion.md b/.principal-views/runtime-ingestion/runtime-ingestion.md new file mode 100644 index 000000000000..ee64282360c1 --- /dev/null +++ b/.principal-views/runtime-ingestion/runtime-ingestion.md @@ -0,0 +1,30 @@ +# Provider Runtime Ingestion + +Telemetry schema for T3 Code's provider runtime event ingestion and domain event synthesis. + +## Overview + +The Runtime Ingestion layer bridges raw provider events into domain-level orchestration events: + +- **Event Reception**: Raw events received from provider process +- **Event Processing**: Events classified, validated, and transformed +- **Session Updates**: Session state updated based on events +- **Message Buffering**: Streaming content accumulated until complete +- **Turn Finalization**: Turn results synthesized when complete + +## Workflows + +### event-ingestion + +Tracks provider event processing from reception to domain event emission: +1. Raw event received from provider +2. Event type identified and routed +3. Session state updated +4. Domain event emitted +5. Subscribers notified + +Scenarios: message-event, tool-event, turn-complete, error-event + +## Key Files + +- `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts` - Event ingestion and domain synthesis diff --git a/.principal-views/runtime-ingestion/runtime-ingestion.otel.canvas b/.principal-views/runtime-ingestion/runtime-ingestion.otel.canvas new file mode 100644 index 000000000000..8418f9124569 --- /dev/null +++ b/.principal-views/runtime-ingestion/runtime-ingestion.otel.canvas @@ -0,0 +1,509 @@ +{ + "nodes": [ + { + "id": "ingestion-event-received", + "type": "text", + "text": "Event Received", + "x": 100, + "y": 50, + "width": 200, + "height": 100, + "color": "#3b82f6", + "pv": { + "status": "draft", + "event": { + "name": "ingestion.event.received", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "event.type": { + "type": "string", + "description": "Provider event type", + "required": true + }, + "turn.id": { + "type": "string", + "description": "Current turn identifier", + "required": false + } + } + }, + "references": ["apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "ingestion-event-classified", + "type": "text", + "text": "Event Classified", + "x": 100, + "y": 200, + "width": 200, + "height": 100, + "color": "#3b82f6", + "pv": { + "status": "draft", + "event": { + "name": "ingestion.event.classified", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "event.type": { + "type": "string", + "description": "Provider event type", + "required": true + }, + "event.category": { + "type": "string", + "description": "Classification: message, tool, status, error", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "ingestion-message-buffered", + "type": "text", + "text": "Message Buffered", + "x": 100, + "y": 350, + "width": 200, + "height": 100, + "color": "#8b5cf6", + "pv": { + "status": "draft", + "event": { + "name": "ingestion.message.buffered", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "message.id": { + "type": "string", + "description": "Message identifier", + "required": true + }, + "content.type": { + "type": "string", + "description": "Content type: text, tool_use, tool_result", + "required": true + }, + "buffer.size": { + "type": "integer", + "description": "Current buffer size in bytes", + "required": false + } + } + }, + "references": ["apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "ingestion-tool-started", + "type": "text", + "text": "Tool Started", + "x": 400, + "y": 350, + "width": 200, + "height": 100, + "color": "#06b6d4", + "pv": { + "status": "draft", + "event": { + "name": "ingestion.tool.started", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "tool.id": { + "type": "string", + "description": "Tool invocation identifier", + "required": true + }, + "tool.name": { + "type": "string", + "description": "Tool name", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "ingestion-tool-completed", + "type": "text", + "text": "Tool Completed", + "x": 400, + "y": 500, + "width": 200, + "height": 100, + "color": "#06b6d4", + "pv": { + "status": "draft", + "event": { + "name": "ingestion.tool.completed", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "tool.id": { + "type": "string", + "description": "Tool invocation identifier", + "required": true + }, + "tool.name": { + "type": "string", + "description": "Tool name", + "required": true + }, + "tool.status": { + "type": "string", + "description": "Tool result status: success, error", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "ingestion-message-complete", + "type": "text", + "text": "Message Complete", + "x": 100, + "y": 500, + "width": 200, + "height": 100, + "color": "#8b5cf6", + "pv": { + "status": "draft", + "event": { + "name": "ingestion.message.complete", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "message.id": { + "type": "string", + "description": "Message identifier", + "required": true + }, + "content.type": { + "type": "string", + "description": "Final content type", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "ingestion-domain-event-emitted", + "type": "text", + "text": "Domain Event Emitted", + "x": 250, + "y": 650, + "width": 200, + "height": 100, + "color": "#22c55e", + "pv": { + "status": "draft", + "event": { + "name": "ingestion.domain.event.emitted", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "domain.event.type": { + "type": "string", + "description": "Domain event type emitted", + "required": true + }, + "source.event.type": { + "type": "string", + "description": "Original provider event type", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "ingestion-turn-finalized", + "type": "text", + "text": "Turn Finalized", + "x": 250, + "y": 800, + "width": 200, + "height": 100, + "color": "#22c55e", + "pv": { + "status": "draft", + "event": { + "name": "ingestion.turn.finalized", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "turn.id": { + "type": "string", + "description": "Turn identifier", + "required": true + }, + "message.count": { + "type": "integer", + "description": "Number of messages in turn", + "required": true + }, + "tool.count": { + "type": "integer", + "description": "Number of tool invocations", + "required": false + } + } + }, + "references": ["apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "ingestion-event-dropped", + "type": "text", + "text": "Event Dropped", + "x": 550, + "y": 200, + "width": 200, + "height": 100, + "color": "#f59e0b", + "pv": { + "status": "draft", + "event": { + "name": "ingestion.event.dropped", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "event.type": { + "type": "string", + "description": "Dropped event type", + "required": true + }, + "drop.reason": { + "type": "string", + "description": "Reason for dropping event", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts"], + "otel": { + "kind": "event", + "category": "warning" + } + } + }, + { + "id": "ingestion-error-received", + "type": "text", + "text": "Error Received", + "x": 550, + "y": 350, + "width": 200, + "height": 100, + "color": "#ef4444", + "pv": { + "status": "draft", + "event": { + "name": "ingestion.error.received", + "attributes": { + "thread.id": { + "type": "string", + "description": "Thread identifier", + "required": true + }, + "error.type": { + "type": "string", + "description": "Error classification", + "required": true + }, + "error.message": { + "type": "string", + "description": "Error message", + "required": true + } + } + }, + "references": ["apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts"], + "otel": { + "kind": "event", + "category": "error" + } + } + } + ], + "edges": [ + { + "id": "received-to-classified", + "fromNode": "ingestion-event-received", + "toNode": "ingestion-event-classified", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "classified-to-buffered", + "fromNode": "ingestion-event-classified", + "toNode": "ingestion-message-buffered", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "branch" } + }, + { + "id": "classified-to-tool", + "fromNode": "ingestion-event-classified", + "toNode": "ingestion-tool-started", + "fromSide": "right", + "toSide": "top", + "pv": { "edgeType": "branch" } + }, + { + "id": "classified-to-dropped", + "fromNode": "ingestion-event-classified", + "toNode": "ingestion-event-dropped", + "fromSide": "right", + "toSide": "left", + "pv": { "edgeType": "warning" } + }, + { + "id": "classified-to-error", + "fromNode": "ingestion-event-classified", + "toNode": "ingestion-error-received", + "fromSide": "right", + "toSide": "left", + "pv": { "edgeType": "error" } + }, + { + "id": "buffered-to-complete", + "fromNode": "ingestion-message-buffered", + "toNode": "ingestion-message-complete", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "tool-started-to-completed", + "fromNode": "ingestion-tool-started", + "toNode": "ingestion-tool-completed", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "message-to-emitted", + "fromNode": "ingestion-message-complete", + "toNode": "ingestion-domain-event-emitted", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "tool-to-emitted", + "fromNode": "ingestion-tool-completed", + "toNode": "ingestion-domain-event-emitted", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "emitted-to-finalized", + "fromNode": "ingestion-domain-event-emitted", + "toNode": "ingestion-turn-finalized", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + } + ], + "pv": { + "name": "Provider Runtime Ingestion", + "version": "1.0.0", + "description": "Telemetry schema for provider runtime event ingestion and domain event synthesis", + "markdown": ".principal-views/runtime-ingestion/runtime-ingestion.md", + "edgeTypes": { + "sequence": { + "style": "solid", + "color": "#64748b", + "width": 2 + }, + "branch": { + "style": "dashed", + "color": "#22c55e", + "width": 2 + }, + "warning": { + "style": "dashed", + "color": "#f59e0b", + "width": 2 + }, + "error": { + "style": "dashed", + "color": "#ef4444", + "width": 2 + } + } + } +} diff --git a/.principal-views/terminal-session/terminal-session-workflow/session-lifecycle.workflow.json b/.principal-views/terminal-session/terminal-session-workflow/session-lifecycle.workflow.json new file mode 100644 index 000000000000..c42c6e987fa7 --- /dev/null +++ b/.principal-views/terminal-session/terminal-session-workflow/session-lifecycle.workflow.json @@ -0,0 +1,55 @@ +{ + "version": "1.0.0", + "canvas": ".principal-views/terminal-session/terminal-session.otel.canvas", + "name": "Session Lifecycle", + "description": "Terminal session from open request through I/O to close", + "spanPattern": "terminal.session.lifecycle", + "mode": "span-tree", + "status": "draft", + "scenarios": [ + { + "id": "success", + "priority": 1, + "description": "Terminal opened, used, and closed normally (may include resize events)", + "template": { + "events": { + "terminal.open.requested": "Terminal {{terminal.id}}: open requested in {{cwd}} (preference: {{shell.preference}})", + "terminal.shell.resolved": "Terminal {{terminal.id}}: shell resolved to {{shell.name}} at {{shell.path}}", + "terminal.pty.spawned": "Terminal {{terminal.id}}: PTY spawned PID {{process.pid}} ({{cols}}x{{rows}})", + "terminal.session.ready": "Terminal {{terminal.id}}: ready (PID {{process.pid}}, {{duration.ms}}ms)", + "terminal.data.received": "Terminal {{terminal.id}}: {{data.direction}} data ({{data.size}} bytes)", + "terminal.resized": "Terminal {{terminal.id}}: resized to {{cols}}x{{rows}}", + "terminal.session.closed": "Terminal {{terminal.id}}: closed ({{close.reason}}, exit code {{exit.code}})" + }, + "summary": "Terminal {{terminal.id}} session completed" + } + }, + { + "id": "shell-error", + "priority": 3, + "description": "Shell resolution failed", + "template": { + "events": { + "terminal.open.requested": "Terminal {{terminal.id}}: open requested in {{cwd}}", + "terminal.shell.error": "Terminal {{terminal.id}}: shell error - {{error.message}}", + "terminal.open.failed": "Terminal {{terminal.id}}: open failed at {{error.stage}} - {{error.message}}" + }, + "summary": "Terminal {{terminal.id}} shell error" + } + }, + { + "id": "spawn-error", + "priority": 4, + "description": "PTY spawn failed", + "template": { + "events": { + "terminal.open.requested": "Terminal {{terminal.id}}: open requested in {{cwd}}", + "terminal.shell.resolved": "Terminal {{terminal.id}}: shell resolved to {{shell.name}} at {{shell.path}}", + "terminal.spawn.error": "Terminal {{terminal.id}}: spawn error for {{shell.path}} - {{error.message}}", + "terminal.open.failed": "Terminal {{terminal.id}}: open failed at {{error.stage}} - {{error.message}}" + }, + "summary": "Terminal {{terminal.id}} spawn error" + } + } + ] +} diff --git a/.principal-views/terminal-session/terminal-session.md b/.principal-views/terminal-session/terminal-session.md new file mode 100644 index 000000000000..13054b6aeb38 --- /dev/null +++ b/.principal-views/terminal-session/terminal-session.md @@ -0,0 +1,30 @@ +# Terminal Session Lifecycle + +Telemetry schema for T3 Code's PTY terminal session management. + +## Overview + +The Terminal Manager handles pseudo-terminal sessions for shell execution: + +- **Session Open**: Terminal requested with shell and working directory +- **Shell Resolution**: Appropriate shell binary resolved +- **PTY Spawn**: Pseudo-terminal process spawned +- **Data Flow**: Input/output streamed between client and PTY +- **Session Close**: Terminal process terminated and cleaned up + +## Workflows + +### session-lifecycle + +Tracks complete terminal session from open to close: +1. Terminal open requested +2. Shell binary resolved +3. PTY process spawned +4. Session ready for I/O +5. Session closed (exit or disconnect) + +Scenarios: success, shell-error, spawn-error, disconnect + +## Key Files + +- `apps/server/src/terminal/Layers/Manager.ts` - Terminal session management diff --git a/.principal-views/terminal-session/terminal-session.otel.canvas b/.principal-views/terminal-session/terminal-session.otel.canvas new file mode 100644 index 000000000000..3d2cedb81ad4 --- /dev/null +++ b/.principal-views/terminal-session/terminal-session.otel.canvas @@ -0,0 +1,497 @@ +{ + "nodes": [ + { + "id": "terminal-open-requested", + "type": "text", + "text": "Open Requested", + "x": 100, + "y": 50, + "width": 200, + "height": 100, + "color": "#3b82f6", + "pv": { + "status": "draft", + "event": { + "name": "terminal.open.requested", + "attributes": { + "terminal.id": { + "type": "string", + "description": "Terminal session identifier", + "required": true + }, + "cwd": { + "type": "string", + "description": "Working directory", + "required": true + }, + "shell.preference": { + "type": "string", + "description": "Preferred shell if specified", + "required": false + } + } + }, + "references": ["apps/server/src/terminal/Layers/Manager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "terminal-shell-resolved", + "type": "text", + "text": "Shell Resolved", + "x": 100, + "y": 200, + "width": 200, + "height": 100, + "color": "#3b82f6", + "pv": { + "status": "draft", + "event": { + "name": "terminal.shell.resolved", + "attributes": { + "terminal.id": { + "type": "string", + "description": "Terminal session identifier", + "required": true + }, + "shell.path": { + "type": "string", + "description": "Resolved shell binary path", + "required": true + }, + "shell.name": { + "type": "string", + "description": "Shell name (bash, zsh, etc)", + "required": true + } + } + }, + "references": ["apps/server/src/terminal/Layers/Manager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "terminal-pty-spawned", + "type": "text", + "text": "PTY Spawned", + "x": 100, + "y": 350, + "width": 200, + "height": 100, + "color": "#8b5cf6", + "pv": { + "status": "draft", + "event": { + "name": "terminal.pty.spawned", + "attributes": { + "terminal.id": { + "type": "string", + "description": "Terminal session identifier", + "required": true + }, + "process.pid": { + "type": "integer", + "description": "PTY process ID", + "required": true + }, + "cols": { + "type": "integer", + "description": "Terminal columns", + "required": true + }, + "rows": { + "type": "integer", + "description": "Terminal rows", + "required": true + } + } + }, + "references": ["apps/server/src/terminal/Layers/Manager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "terminal-session-ready", + "type": "text", + "text": "Session Ready", + "x": 100, + "y": 500, + "width": 200, + "height": 100, + "color": "#22c55e", + "pv": { + "status": "draft", + "event": { + "name": "terminal.session.ready", + "attributes": { + "terminal.id": { + "type": "string", + "description": "Terminal session identifier", + "required": true + }, + "process.pid": { + "type": "integer", + "description": "PTY process ID", + "required": true + }, + "duration.ms": { + "type": "number", + "description": "Time to ready state", + "required": false + } + } + }, + "references": ["apps/server/src/terminal/Layers/Manager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "terminal-data-received", + "type": "text", + "text": "Data Received", + "x": 100, + "y": 650, + "width": 200, + "height": 100, + "color": "#06b6d4", + "pv": { + "status": "draft", + "event": { + "name": "terminal.data.received", + "attributes": { + "terminal.id": { + "type": "string", + "description": "Terminal session identifier", + "required": true + }, + "data.size": { + "type": "integer", + "description": "Data size in bytes", + "required": true + }, + "data.direction": { + "type": "string", + "description": "Direction: input or output", + "required": true + } + } + }, + "references": ["apps/server/src/terminal/Layers/Manager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "terminal-resized", + "type": "text", + "text": "Terminal Resized", + "x": 100, + "y": 800, + "width": 200, + "height": 100, + "color": "#06b6d4", + "pv": { + "status": "draft", + "event": { + "name": "terminal.resized", + "attributes": { + "terminal.id": { + "type": "string", + "description": "Terminal session identifier", + "required": true + }, + "cols": { + "type": "integer", + "description": "New column count", + "required": true + }, + "rows": { + "type": "integer", + "description": "New row count", + "required": true + } + } + }, + "references": ["apps/server/src/terminal/Layers/Manager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "terminal-session-closed", + "type": "text", + "text": "Session Closed", + "x": 100, + "y": 950, + "width": 200, + "height": 100, + "color": "#22c55e", + "pv": { + "status": "draft", + "event": { + "name": "terminal.session.closed", + "attributes": { + "terminal.id": { + "type": "string", + "description": "Terminal session identifier", + "required": true + }, + "exit.code": { + "type": "integer", + "description": "Process exit code", + "required": false + }, + "close.reason": { + "type": "string", + "description": "Reason: exit, disconnect, error", + "required": true + } + } + }, + "references": ["apps/server/src/terminal/Layers/Manager.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "terminal-shell-error", + "type": "text", + "text": "Shell Error", + "x": 400, + "y": 200, + "width": 200, + "height": 100, + "color": "#ef4444", + "pv": { + "status": "draft", + "event": { + "name": "terminal.shell.error", + "attributes": { + "terminal.id": { + "type": "string", + "description": "Terminal session identifier", + "required": true + }, + "error.message": { + "type": "string", + "description": "Shell resolution error", + "required": true + } + } + }, + "references": ["apps/server/src/terminal/Layers/Manager.ts"], + "otel": { + "kind": "event", + "category": "error" + } + } + }, + { + "id": "terminal-spawn-error", + "type": "text", + "text": "Spawn Error", + "x": 400, + "y": 350, + "width": 200, + "height": 100, + "color": "#ef4444", + "pv": { + "status": "draft", + "event": { + "name": "terminal.spawn.error", + "attributes": { + "terminal.id": { + "type": "string", + "description": "Terminal session identifier", + "required": true + }, + "shell.path": { + "type": "string", + "description": "Attempted shell path", + "required": true + }, + "error.message": { + "type": "string", + "description": "Spawn error message", + "required": true + } + } + }, + "references": ["apps/server/src/terminal/Layers/Manager.ts"], + "otel": { + "kind": "event", + "category": "error" + } + } + }, + { + "id": "terminal-open-failed", + "type": "text", + "text": "Open Failed", + "x": 400, + "y": 500, + "width": 200, + "height": 100, + "color": "#ef4444", + "pv": { + "status": "draft", + "event": { + "name": "terminal.open.failed", + "attributes": { + "terminal.id": { + "type": "string", + "description": "Terminal session identifier", + "required": true + }, + "error.stage": { + "type": "string", + "description": "Stage where failure occurred", + "required": true + }, + "error.message": { + "type": "string", + "description": "Failure message", + "required": true + } + } + }, + "references": ["apps/server/src/terminal/Layers/Manager.ts"], + "otel": { + "kind": "event", + "category": "error" + } + } + } + ], + "edges": [ + { + "id": "requested-to-resolved", + "fromNode": "terminal-open-requested", + "toNode": "terminal-shell-resolved", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "requested-to-shell-error", + "fromNode": "terminal-open-requested", + "toNode": "terminal-shell-error", + "fromSide": "right", + "toSide": "left", + "pv": { "edgeType": "error" } + }, + { + "id": "resolved-to-spawned", + "fromNode": "terminal-shell-resolved", + "toNode": "terminal-pty-spawned", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "resolved-to-spawn-error", + "fromNode": "terminal-shell-resolved", + "toNode": "terminal-spawn-error", + "fromSide": "right", + "toSide": "left", + "pv": { "edgeType": "error" } + }, + { + "id": "spawned-to-ready", + "fromNode": "terminal-pty-spawned", + "toNode": "terminal-session-ready", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "ready-to-data", + "fromNode": "terminal-session-ready", + "toNode": "terminal-data-received", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "data-to-resized", + "fromNode": "terminal-data-received", + "toNode": "terminal-resized", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "branch" } + }, + { + "id": "data-to-closed", + "fromNode": "terminal-data-received", + "toNode": "terminal-session-closed", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "resized-to-closed", + "fromNode": "terminal-resized", + "toNode": "terminal-session-closed", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "shell-error-to-failed", + "fromNode": "terminal-shell-error", + "toNode": "terminal-open-failed", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "error" } + }, + { + "id": "spawn-error-to-failed", + "fromNode": "terminal-spawn-error", + "toNode": "terminal-open-failed", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "error" } + } + ], + "pv": { + "name": "Terminal Session Lifecycle", + "version": "1.0.0", + "description": "Telemetry schema for PTY terminal session management", + "markdown": ".principal-views/terminal-session/terminal-session.md", + "edgeTypes": { + "sequence": { + "style": "solid", + "color": "#64748b", + "width": 2 + }, + "branch": { + "style": "dashed", + "color": "#22c55e", + "width": 2 + }, + "error": { + "style": "dashed", + "color": "#ef4444", + "width": 2 + } + } + } +} diff --git a/.principal-views/websocket-routing/websocket-routing-workflow/request-lifecycle.workflow.json b/.principal-views/websocket-routing/websocket-routing-workflow/request-lifecycle.workflow.json new file mode 100644 index 000000000000..efa35edc903f --- /dev/null +++ b/.principal-views/websocket-routing/websocket-routing-workflow/request-lifecycle.workflow.json @@ -0,0 +1,68 @@ +{ + "version": "1.0.0", + "canvas": ".principal-views/websocket-routing/websocket-routing.otel.canvas", + "name": "Request Lifecycle", + "description": "WebSocket request processing from reception through routing to response", + "spanPattern": "ws.request.lifecycle", + "mode": "span-tree", + "status": "draft", + "scenarios": [ + { + "id": "success", + "priority": 1, + "description": "Request processed successfully and response sent", + "template": { + "events": { + "ws.request.received": "Request {{request.id}} received on connection {{connection.id}} ({{message.size}} bytes)", + "ws.request.parsed": "Request {{request.id}}: parsed method {{method}} (has params: {{has.params}})", + "ws.request.routed": "Request {{request.id}}: routed {{method}} to handler {{handler.name}}", + "ws.handler.executed": "Request {{request.id}}: handler {{handler.name}} executed in {{duration.ms}}ms", + "ws.response.sent": "Request {{request.id}}: response sent ({{response.size}} bytes, total {{duration.ms}}ms)" + }, + "summary": "Request {{request.id}} completed successfully" + } + }, + { + "id": "parse-error", + "priority": 2, + "description": "Request failed to parse", + "template": { + "events": { + "ws.request.received": "Request {{request.id}} received on connection {{connection.id}}", + "ws.parse.failed": "Request {{request.id}}: parse failed - {{error.message}}", + "ws.error.response.sent": "Request {{request.id}}: error response sent (code {{error.code}}): {{error.message}}" + }, + "summary": "Request {{request.id}} parse error" + } + }, + { + "id": "method-not-found", + "priority": 3, + "description": "Request method not recognized", + "template": { + "events": { + "ws.request.received": "Request {{request.id}} received on connection {{connection.id}}", + "ws.request.parsed": "Request {{request.id}}: parsed method {{method}}", + "ws.method.not.found": "Request {{request.id}}: method {{method}} not found", + "ws.error.response.sent": "Request {{request.id}}: error response sent (code {{error.code}}): {{error.message}}" + }, + "summary": "Request {{request.id}} method not found: {{method}}" + } + }, + { + "id": "handler-error", + "priority": 4, + "description": "Handler threw an error during execution", + "template": { + "events": { + "ws.request.received": "Request {{request.id}} received on connection {{connection.id}}", + "ws.request.parsed": "Request {{request.id}}: parsed method {{method}}", + "ws.request.routed": "Request {{request.id}}: routed {{method}} to handler {{handler.name}}", + "ws.handler.failed": "Request {{request.id}}: handler {{handler.name}} failed - {{error.type}}: {{error.message}}", + "ws.error.response.sent": "Request {{request.id}}: error response sent (code {{error.code}}): {{error.message}}" + }, + "summary": "Request {{request.id}} handler error: {{error.message}}" + } + } + ] +} diff --git a/.principal-views/websocket-routing/websocket-routing.md b/.principal-views/websocket-routing/websocket-routing.md new file mode 100644 index 000000000000..1bc39beadbd3 --- /dev/null +++ b/.principal-views/websocket-routing/websocket-routing.md @@ -0,0 +1,29 @@ +# WebSocket Command Routing + +Telemetry schema for T3 Code's WebSocket server request routing and response handling. + +## Overview + +The WebSocket server handles bidirectional communication between web clients and the server: + +- **Request Reception**: Incoming WebSocket messages parsed as commands +- **Command Routing**: Messages routed to appropriate handlers based on method +- **Response Handling**: Results serialized and sent back to clients +- **Error Recovery**: Graceful handling of malformed requests and handler failures + +## Workflows + +### request-lifecycle + +Tracks complete request processing from reception to response: +1. Message received from WebSocket connection +2. Message parsed and validated +3. Request routed to handler +4. Handler executes and returns result +5. Response sent to client + +Scenarios: success, validation-error, handler-error + +## Key Files + +- `apps/server/src/wsServer.ts` - WebSocket server and request routing diff --git a/.principal-views/websocket-routing/websocket-routing.otel.canvas b/.principal-views/websocket-routing/websocket-routing.otel.canvas new file mode 100644 index 000000000000..cfec1773073e --- /dev/null +++ b/.principal-views/websocket-routing/websocket-routing.otel.canvas @@ -0,0 +1,441 @@ +{ + "nodes": [ + { + "id": "ws-request-received", + "type": "text", + "text": "Request Received", + "x": 100, + "y": 50, + "width": 200, + "height": 100, + "color": "#3b82f6", + "pv": { + "status": "draft", + "event": { + "name": "ws.request.received", + "attributes": { + "request.id": { + "type": "string", + "description": "Unique request identifier", + "required": true + }, + "connection.id": { + "type": "string", + "description": "WebSocket connection identifier", + "required": true + }, + "message.size": { + "type": "integer", + "description": "Size of raw message in bytes", + "required": false + } + } + }, + "references": ["apps/server/src/wsServer.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "ws-request-parsed", + "type": "text", + "text": "Request Parsed", + "x": 100, + "y": 200, + "width": 200, + "height": 100, + "color": "#3b82f6", + "pv": { + "status": "draft", + "event": { + "name": "ws.request.parsed", + "attributes": { + "request.id": { + "type": "string", + "description": "Request identifier", + "required": true + }, + "method": { + "type": "string", + "description": "RPC method name", + "required": true + }, + "has.params": { + "type": "boolean", + "description": "Whether request includes parameters", + "required": true + } + } + }, + "references": ["apps/server/src/wsServer.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "ws-request-routed", + "type": "text", + "text": "Request Routed", + "x": 100, + "y": 350, + "width": 200, + "height": 100, + "color": "#8b5cf6", + "pv": { + "status": "draft", + "event": { + "name": "ws.request.routed", + "attributes": { + "request.id": { + "type": "string", + "description": "Request identifier", + "required": true + }, + "method": { + "type": "string", + "description": "RPC method name", + "required": true + }, + "handler.name": { + "type": "string", + "description": "Name of handler function", + "required": true + } + } + }, + "references": ["apps/server/src/wsServer.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "ws-handler-executed", + "type": "text", + "text": "Handler Executed", + "x": 100, + "y": 500, + "width": 200, + "height": 100, + "color": "#8b5cf6", + "pv": { + "status": "draft", + "event": { + "name": "ws.handler.executed", + "attributes": { + "request.id": { + "type": "string", + "description": "Request identifier", + "required": true + }, + "handler.name": { + "type": "string", + "description": "Handler function name", + "required": true + }, + "duration.ms": { + "type": "number", + "description": "Handler execution time", + "required": false + } + } + }, + "references": ["apps/server/src/wsServer.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "ws-response-sent", + "type": "text", + "text": "Response Sent", + "x": 100, + "y": 650, + "width": 200, + "height": 100, + "color": "#22c55e", + "pv": { + "status": "draft", + "event": { + "name": "ws.response.sent", + "attributes": { + "request.id": { + "type": "string", + "description": "Request identifier", + "required": true + }, + "response.size": { + "type": "integer", + "description": "Size of response in bytes", + "required": false + }, + "duration.ms": { + "type": "number", + "description": "Total request-response time", + "required": false + } + } + }, + "references": ["apps/server/src/wsServer.ts"], + "otel": { + "kind": "event", + "category": "lifecycle" + } + } + }, + { + "id": "ws-parse-failed", + "type": "text", + "text": "Parse Failed", + "x": 400, + "y": 200, + "width": 200, + "height": 100, + "color": "#ef4444", + "pv": { + "status": "draft", + "event": { + "name": "ws.parse.failed", + "attributes": { + "request.id": { + "type": "string", + "description": "Request identifier", + "required": true + }, + "error.message": { + "type": "string", + "description": "Parse error message", + "required": true + } + } + }, + "references": ["apps/server/src/wsServer.ts"], + "otel": { + "kind": "event", + "category": "error" + } + } + }, + { + "id": "ws-method-not-found", + "type": "text", + "text": "Method Not Found", + "x": 400, + "y": 350, + "width": 200, + "height": 100, + "color": "#f59e0b", + "pv": { + "status": "draft", + "event": { + "name": "ws.method.not.found", + "attributes": { + "request.id": { + "type": "string", + "description": "Request identifier", + "required": true + }, + "method": { + "type": "string", + "description": "Unknown method name", + "required": true + } + } + }, + "references": ["apps/server/src/wsServer.ts"], + "otel": { + "kind": "event", + "category": "warning" + } + } + }, + { + "id": "ws-handler-failed", + "type": "text", + "text": "Handler Failed", + "x": 400, + "y": 500, + "width": 200, + "height": 100, + "color": "#ef4444", + "pv": { + "status": "draft", + "event": { + "name": "ws.handler.failed", + "attributes": { + "request.id": { + "type": "string", + "description": "Request identifier", + "required": true + }, + "handler.name": { + "type": "string", + "description": "Handler that failed", + "required": true + }, + "error.type": { + "type": "string", + "description": "Error classification", + "required": true + }, + "error.message": { + "type": "string", + "description": "Error message", + "required": true + } + } + }, + "references": ["apps/server/src/wsServer.ts"], + "otel": { + "kind": "event", + "category": "error" + } + } + }, + { + "id": "ws-error-response-sent", + "type": "text", + "text": "Error Response Sent", + "x": 400, + "y": 650, + "width": 200, + "height": 100, + "color": "#ef4444", + "pv": { + "status": "draft", + "event": { + "name": "ws.error.response.sent", + "attributes": { + "request.id": { + "type": "string", + "description": "Request identifier", + "required": true + }, + "error.code": { + "type": "integer", + "description": "RPC error code", + "required": true + }, + "error.message": { + "type": "string", + "description": "Error message sent to client", + "required": true + } + } + }, + "references": ["apps/server/src/wsServer.ts"], + "otel": { + "kind": "event", + "category": "error" + } + } + } + ], + "edges": [ + { + "id": "received-to-parsed", + "fromNode": "ws-request-received", + "toNode": "ws-request-parsed", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "received-to-parse-failed", + "fromNode": "ws-request-received", + "toNode": "ws-parse-failed", + "fromSide": "right", + "toSide": "left", + "pv": { "edgeType": "error" } + }, + { + "id": "parsed-to-routed", + "fromNode": "ws-request-parsed", + "toNode": "ws-request-routed", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "parsed-to-not-found", + "fromNode": "ws-request-parsed", + "toNode": "ws-method-not-found", + "fromSide": "right", + "toSide": "left", + "pv": { "edgeType": "error" } + }, + { + "id": "routed-to-executed", + "fromNode": "ws-request-routed", + "toNode": "ws-handler-executed", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "routed-to-handler-failed", + "fromNode": "ws-request-routed", + "toNode": "ws-handler-failed", + "fromSide": "right", + "toSide": "left", + "pv": { "edgeType": "error" } + }, + { + "id": "executed-to-response", + "fromNode": "ws-handler-executed", + "toNode": "ws-response-sent", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "sequence" } + }, + { + "id": "parse-failed-to-error-response", + "fromNode": "ws-parse-failed", + "toNode": "ws-error-response-sent", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "error" } + }, + { + "id": "not-found-to-error-response", + "fromNode": "ws-method-not-found", + "toNode": "ws-error-response-sent", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "error" } + }, + { + "id": "handler-failed-to-error-response", + "fromNode": "ws-handler-failed", + "toNode": "ws-error-response-sent", + "fromSide": "bottom", + "toSide": "top", + "pv": { "edgeType": "error" } + } + ], + "pv": { + "name": "WebSocket Command Routing", + "version": "1.0.0", + "description": "Telemetry schema for WebSocket request routing and response handling", + "markdown": ".principal-views/websocket-routing/websocket-routing.md", + "edgeTypes": { + "sequence": { + "style": "solid", + "color": "#64748b", + "width": 2 + }, + "error": { + "style": "dashed", + "color": "#ef4444", + "width": 2 + } + } + } +}