Skip to main content

chasm/schema/
versions.rs

1// Copyright (c) 2024-2026 Nervosys LLC
2// SPDX-License-Identifier: AGPL-3.0-only
3//! Versioned schema definitions for every known AI chat provider.
4//!
5//! Each function returns a [`ProviderSchema`] describing the exact data layout
6//! for a specific provider at a specific version. These are the ground-truth
7//! definitions that the schema registry and ontology layer build upon.
8
9use crate::schema::types::*;
10use std::collections::HashMap;
11
12// ============================================================================
13// VS Code Copilot Chat — JSON format (0.25.x – 0.36.x)
14// ============================================================================
15
16/// VS Code Copilot Chat JSON format (extensions 0.25.0 – 0.36.99).
17///
18/// Sessions stored as single JSON objects in individual files under
19/// `{workspaceStorage}/{hash}/chatSessions/{uuid}.json`.
20/// Metadata stored in `state.vscdb` SQLite key-value store.
21pub fn copilot_json_v3() -> ProviderSchema {
22    ProviderSchema {
23        version: SchemaVersion::new("copilot", FormatType::Json, 3, "Copilot Chat JSON v3"),
24        extension_version_min: Some("0.25.0".into()),
25        extension_version_max: Some("0.36.99".into()),
26        host_version_min: Some("1.98.0".into()),
27        introduced: Some("2025-02".into()),
28        deprecated: Some("2026-01".into()),
29        storage: StorageLocation {
30            description: "VS Code workspace storage, one JSON file per session".into(),
31            path_pattern: "{APPDATA}/Code/User/workspaceStorage/{hash}/chatSessions/{uuid}.json"
32                .into(),
33            platform_paths: HashMap::from([
34                (
35                    "windows".into(),
36                    "%APPDATA%/Code/User/workspaceStorage/{hash}/chatSessions/".into(),
37                ),
38                (
39                    "macos".into(),
40                    "~/Library/Application Support/Code/User/workspaceStorage/{hash}/chatSessions/"
41                        .into(),
42                ),
43                (
44                    "linux".into(),
45                    "~/.config/Code/User/workspaceStorage/{hash}/chatSessions/".into(),
46                ),
47            ]),
48            storage_type: StorageType::Hybrid,
49            file_extensions: vec![".json".into()],
50        },
51        session_schema: SessionFormatSchema {
52            description: "Single JSON object containing the full session state".into(),
53            format: FormatType::Json,
54            fields: copilot_session_fields_v3(),
55            nested_objects: copilot_nested_objects_v3(),
56            example: Some(serde_json::json!({
57                "version": 3,
58                "sessionId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
59                "creationDate": 1700000000000_i64,
60                "lastMessageDate": 1700001000000_i64,
61                "isImported": false,
62                "initialLocation": "panel",
63                "requests": []
64            })),
65        },
66        db_keys: copilot_db_keys_json(),
67        notes: vec![
68            "Legacy JSON format used from Copilot Chat 0.25.x through 0.36.x".into(),
69            "Session version field is always 3".into(),
70            "Response format is {\"value\": [{\"value\": \"text\"}, ...]}".into(),
71        ],
72        breaking_changes: vec![],
73        tags: vec![
74            "vscode".into(),
75            "copilot".into(),
76            "json".into(),
77            "file-based".into(),
78            "hybrid-storage".into(),
79        ],
80    }
81}
82
83// ============================================================================
84// VS Code Copilot Chat — JSONL format (0.37.x+)
85// ============================================================================
86
87/// VS Code Copilot Chat JSONL event-sourced format (extensions 0.37.0+).
88///
89/// Sessions stored as JSONL files with event-sourced updates:
90/// - kind 0: Full session snapshot (first line)
91/// - kind 1: Incremental request update
92/// - kind 2: Incremental response update
93pub fn copilot_jsonl_v1() -> ProviderSchema {
94    ProviderSchema {
95        version: SchemaVersion::new("copilot", FormatType::Jsonl, 1, "Copilot Chat JSONL v1"),
96        extension_version_min: Some("0.37.0".into()),
97        extension_version_max: None,
98        host_version_min: Some("1.109.0".into()),
99        introduced: Some("2026-01".into()),
100        deprecated: None,
101        storage: StorageLocation {
102            description: "VS Code workspace storage, one JSONL file per session (event-sourced)"
103                .into(),
104            path_pattern: "{APPDATA}/Code/User/workspaceStorage/{hash}/chatSessions/{uuid}.jsonl"
105                .into(),
106            platform_paths: HashMap::from([
107                (
108                    "windows".into(),
109                    "%APPDATA%/Code/User/workspaceStorage/{hash}/chatSessions/".into(),
110                ),
111                (
112                    "macos".into(),
113                    "~/Library/Application Support/Code/User/workspaceStorage/{hash}/chatSessions/"
114                        .into(),
115                ),
116                (
117                    "linux".into(),
118                    "~/.config/Code/User/workspaceStorage/{hash}/chatSessions/".into(),
119                ),
120            ]),
121            storage_type: StorageType::Hybrid,
122            file_extensions: vec![".jsonl".into()],
123        },
124        session_schema: SessionFormatSchema {
125            description: "JSONL event-sourced format. First line is kind:0 (full snapshot). \
126                          Subsequent lines are kind:1 (request update) or kind:2 (response update)."
127                .into(),
128            format: FormatType::Jsonl,
129            fields: copilot_jsonl_event_fields(),
130            nested_objects: copilot_nested_objects_jsonl(),
131            example: Some(serde_json::json!({
132                "kind": 0,
133                "data": {
134                    "version": 3,
135                    "sessionId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
136                    "creationDate": 1700000000000_i64,
137                    "lastMessageDate": 1700001000000_i64,
138                    "initialLocation": "panel",
139                    "requests": []
140                }
141            })),
142        },
143        db_keys: copilot_db_keys_jsonl(),
144        notes: vec![
145            "JSONL event-sourced format introduced with Copilot Chat 0.37.x (Jan 2026)".into(),
146            "kind:0 = full session snapshot, kind:1 = request update, kind:2 = response update"
147                .into(),
148            "Response format changed to array of parts: [{\"kind\":\"\",\"value\":\"text\"}]".into(),
149            "modelState field added: {\"value\": 0|1|2, \"completedAt\": timestamp}".into(),
150            "Timing field changed from startTime/endTime to created/lastRequestStarted/lastRequestEnded".into(),
151        ],
152        breaking_changes: vec![
153            "File extension changed from .json to .jsonl".into(),
154            "Response format: object → array of typed parts".into(),
155            "Timing field names renamed (startTime→created, endTime→lastRequestEnded)".into(),
156            "Added modelState field (required for VS Code to show session as non-empty)".into(),
157            "Index format changed from array of UUIDs to map of UUID→entry objects".into(),
158        ],
159        tags: vec![
160            "vscode".into(),
161            "copilot".into(),
162            "jsonl".into(),
163            "event-sourced".into(),
164            "file-based".into(),
165            "hybrid-storage".into(),
166        ],
167    }
168}
169
170// ============================================================================
171// Cursor IDE
172// ============================================================================
173
174/// Cursor IDE chat session schema.
175///
176/// Cursor stores sessions in a format similar to VS Code Copilot Chat JSON v3,
177/// but in Cursor's own app data directory.
178pub fn cursor_v1() -> ProviderSchema {
179    ProviderSchema {
180        version: SchemaVersion::new("cursor", FormatType::Json, 1, "Cursor Chat v1"),
181        extension_version_min: None,
182        extension_version_max: None,
183        host_version_min: None,
184        introduced: Some("2024-01".into()),
185        deprecated: None,
186        storage: StorageLocation {
187            description: "Cursor app data, one JSON file per session in chatSessions/".into(),
188            path_pattern: "{APPDATA}/Cursor/User/workspaceStorage/{hash}/chatSessions/{uuid}.json"
189                .into(),
190            platform_paths: HashMap::from([
191                (
192                    "windows".into(),
193                    "%APPDATA%/Cursor/User/workspaceStorage/{hash}/chatSessions/".into(),
194                ),
195                (
196                    "macos".into(),
197                    "~/Library/Application Support/Cursor/User/workspaceStorage/{hash}/chatSessions/"
198                        .into(),
199                ),
200                (
201                    "linux".into(),
202                    "~/.config/Cursor/User/workspaceStorage/{hash}/chatSessions/".into(),
203                ),
204            ]),
205            storage_type: StorageType::FilePerSession,
206            file_extensions: vec![".json".into()],
207        },
208        session_schema: SessionFormatSchema {
209            description: "JSON format similar to Copilot Chat v3, stored in Cursor's workspace storage".into(),
210            format: FormatType::Json,
211            fields: cursor_session_fields(),
212            nested_objects: HashMap::new(),
213            example: None,
214        },
215        db_keys: vec![],
216        notes: vec![
217            "Cursor uses a VS Code fork with similar session format".into(),
218            "Sessions stored in Cursor's own workspaceStorage directory".into(),
219            "Field names and structure closely match Copilot Chat JSON v3".into(),
220        ],
221        breaking_changes: vec![],
222        tags: vec![
223            "cursor".into(),
224            "json".into(),
225            "file-based".into(),
226            "vscode-fork".into(),
227        ],
228    }
229}
230
231// ============================================================================
232// Claude Code (Anthropic)
233// ============================================================================
234
235/// Claude Code CLI session schema.
236///
237/// Claude Code stores sessions as JSONL files in `~/.claude/projects/`.
238pub fn claude_code_v1() -> ProviderSchema {
239    ProviderSchema {
240        version: SchemaVersion::new("claude-code", FormatType::Jsonl, 1, "Claude Code v1"),
241        extension_version_min: None,
242        extension_version_max: None,
243        host_version_min: None,
244        introduced: Some("2025-03".into()),
245        deprecated: None,
246        storage: StorageLocation {
247            description: "Claude Code stores JSONL sessions in ~/.claude/projects/{project}/".into(),
248            path_pattern: "{HOME}/.claude/projects/{project_hash}/{uuid}.jsonl".into(),
249            platform_paths: HashMap::from([
250                ("windows".into(), "%USERPROFILE%/.claude/projects/".into()),
251                ("macos".into(), "~/.claude/projects/".into()),
252                ("linux".into(), "~/.claude/projects/".into()),
253            ]),
254            storage_type: StorageType::FilePerSession,
255            file_extensions: vec![".jsonl".into()],
256        },
257        session_schema: SessionFormatSchema {
258            description: "JSONL format with one message event per line. Each line has type, role, content fields.".into(),
259            format: FormatType::Jsonl,
260            fields: claude_code_fields(),
261            nested_objects: HashMap::new(),
262            example: Some(serde_json::json!({
263                "type": "human",
264                "message": {"role": "user", "content": "Hello"},
265                "timestamp": "2025-06-15T10:30:00Z"
266            })),
267        },
268        db_keys: vec![],
269        notes: vec![
270            "Claude Code CLI stores sessions as JSONL in ~/.claude/projects/".into(),
271            "Each line is a message event with type (human/assistant/system/tool_use/tool_result)".into(),
272            "Project identified by hash of project path".into(),
273        ],
274        breaking_changes: vec![],
275        tags: vec![
276            "anthropic".into(),
277            "claude-code".into(),
278            "cli".into(),
279            "jsonl".into(),
280            "file-based".into(),
281        ],
282    }
283}
284
285// ============================================================================
286// Codex CLI (OpenAI)
287// ============================================================================
288
289/// OpenAI Codex CLI session schema.
290///
291/// Codex CLI stores sessions as JSONL in `~/.codex/sessions/`.
292pub fn codex_cli_v1() -> ProviderSchema {
293    ProviderSchema {
294        version: SchemaVersion::new("codex-cli", FormatType::Jsonl, 1, "Codex CLI v1"),
295        extension_version_min: None,
296        extension_version_max: None,
297        host_version_min: None,
298        introduced: Some("2025-05".into()),
299        deprecated: None,
300        storage: StorageLocation {
301            description: "Codex CLI stores JSONL sessions in ~/.codex/sessions/".into(),
302            path_pattern: "{HOME}/.codex/sessions/{uuid}.jsonl".into(),
303            platform_paths: HashMap::from([
304                ("windows".into(), "%USERPROFILE%/.codex/sessions/".into()),
305                ("macos".into(), "~/.codex/sessions/".into()),
306                ("linux".into(), "~/.codex/sessions/".into()),
307            ]),
308            storage_type: StorageType::FilePerSession,
309            file_extensions: vec![".jsonl".into()],
310        },
311        session_schema: SessionFormatSchema {
312            description: "JSONL format with OpenAI message events".into(),
313            format: FormatType::Jsonl,
314            fields: codex_cli_fields(),
315            nested_objects: HashMap::new(),
316            example: None,
317        },
318        db_keys: vec![],
319        notes: vec![
320            "OpenAI Codex CLI stores sessions as JSONL in ~/.codex/sessions/".into(),
321            "Uses OpenAI API message format (role/content pairs)".into(),
322        ],
323        breaking_changes: vec![],
324        tags: vec![
325            "openai".into(),
326            "codex".into(),
327            "cli".into(),
328            "jsonl".into(),
329            "file-based".into(),
330        ],
331    }
332}
333
334// ============================================================================
335// Gemini CLI (Google)
336// ============================================================================
337
338/// Google Gemini CLI session schema.
339///
340/// Gemini CLI stores sessions as JSON in `~/.gemini/tmp/`.
341pub fn gemini_cli_v1() -> ProviderSchema {
342    ProviderSchema {
343        version: SchemaVersion::new("gemini-cli", FormatType::Json, 1, "Gemini CLI v1"),
344        extension_version_min: None,
345        extension_version_max: None,
346        host_version_min: None,
347        introduced: Some("2025-06".into()),
348        deprecated: None,
349        storage: StorageLocation {
350            description: "Gemini CLI stores JSON sessions in ~/.gemini/tmp/".into(),
351            path_pattern: "{HOME}/.gemini/tmp/{session_id}.json".into(),
352            platform_paths: HashMap::from([
353                ("windows".into(), "%USERPROFILE%/.gemini/tmp/".into()),
354                ("macos".into(), "~/.gemini/tmp/".into()),
355                ("linux".into(), "~/.gemini/tmp/".into()),
356            ]),
357            storage_type: StorageType::FilePerSession,
358            file_extensions: vec![".json".into()],
359        },
360        session_schema: SessionFormatSchema {
361            description: "JSON format with Google Gemini API message structure".into(),
362            format: FormatType::Json,
363            fields: gemini_cli_fields(),
364            nested_objects: HashMap::new(),
365            example: None,
366        },
367        db_keys: vec![],
368        notes: vec![
369            "Google Gemini CLI stores sessions as JSON in ~/.gemini/tmp/".into(),
370            "Uses Gemini API message format (contents with parts)".into(),
371        ],
372        breaking_changes: vec![],
373        tags: vec![
374            "google".into(),
375            "gemini".into(),
376            "cli".into(),
377            "json".into(),
378            "file-based".into(),
379        ],
380    }
381}
382
383// ============================================================================
384// Continue.dev
385// ============================================================================
386
387/// Continue.dev VS Code extension session schema.
388///
389/// Continue stores sessions in its own format within VS Code's storage.
390pub fn continue_dev_v1() -> ProviderSchema {
391    ProviderSchema {
392        version: SchemaVersion::new("continue-dev", FormatType::Json, 1, "Continue.dev v1"),
393        extension_version_min: None,
394        extension_version_max: None,
395        host_version_min: None,
396        introduced: Some("2024-01".into()),
397        deprecated: None,
398        storage: StorageLocation {
399            description: "Continue.dev stores sessions in ~/.continue/sessions/".into(),
400            path_pattern: "{HOME}/.continue/sessions/{session_id}.json".into(),
401            platform_paths: HashMap::from([
402                (
403                    "windows".into(),
404                    "%USERPROFILE%/.continue/sessions/".into(),
405                ),
406                ("macos".into(), "~/.continue/sessions/".into()),
407                ("linux".into(), "~/.continue/sessions/".into()),
408            ]),
409            storage_type: StorageType::FilePerSession,
410            file_extensions: vec![".json".into()],
411        },
412        session_schema: SessionFormatSchema {
413            description: "JSON format with Continue.dev's session structure".into(),
414            format: FormatType::Json,
415            fields: continue_dev_fields(),
416            nested_objects: HashMap::new(),
417            example: None,
418        },
419        db_keys: vec![],
420        notes: vec![
421            "Continue.dev extension stores sessions in ~/.continue/sessions/".into(),
422            "Uses a chat-model-agnostic format with provider metadata".into(),
423        ],
424        breaking_changes: vec![],
425        tags: vec![
426            "continuedev".into(),
427            "vscode-extension".into(),
428            "json".into(),
429            "file-based".into(),
430        ],
431    }
432}
433
434// ============================================================================
435// OpenAI API (generic)
436// ============================================================================
437
438/// Generic OpenAI API-compatible conversation format.
439///
440/// Used by Ollama, vLLM, LM Studio, LocalAI, Jan, GPT4All, Llamafile,
441/// Text Generation WebUI, and cloud providers (Groq, Together, Fireworks, etc.)
442pub fn openai_api_v1() -> ProviderSchema {
443    ProviderSchema {
444        version: SchemaVersion::new("openai-api", FormatType::OpenAiApi, 1, "OpenAI API v1"),
445        extension_version_min: None,
446        extension_version_max: None,
447        host_version_min: None,
448        introduced: Some("2023-03".into()),
449        deprecated: None,
450        storage: StorageLocation {
451            description: "OpenAI-compatible API — conversations are ephemeral unless saved by the client".into(),
452            path_pattern: "N/A — API-based, no local storage".into(),
453            platform_paths: HashMap::new(),
454            storage_type: StorageType::CloudApi,
455            file_extensions: vec![],
456        },
457        session_schema: SessionFormatSchema {
458            description: "OpenAI Chat Completions API format (messages array with role/content)".into(),
459            format: FormatType::OpenAiApi,
460            fields: openai_api_fields(),
461            nested_objects: openai_api_nested_objects(),
462            example: Some(serde_json::json!({
463                "model": "gpt-4o",
464                "messages": [
465                    {"role": "system", "content": "You are a helpful assistant."},
466                    {"role": "user", "content": "Hello!"},
467                    {"role": "assistant", "content": "Hi! How can I help?"}
468                ],
469                "temperature": 0.7
470            })),
471        },
472        db_keys: vec![],
473        notes: vec![
474            "Standard OpenAI Chat Completions API format".into(),
475            "Used by: Ollama, vLLM, LM Studio, LocalAI, Jan, GPT4All, Llamafile, TextGen WebUI".into(),
476            "Also used by cloud providers: OpenAI, Groq, Together, Fireworks, DeepSeek".into(),
477            "Conversations are request/response pairs — no persistent session storage".into(),
478        ],
479        breaking_changes: vec![],
480        tags: vec![
481            "openai".into(),
482            "api".into(),
483            "cloud".into(),
484            "local".into(),
485            "universal".into(),
486        ],
487    }
488}
489
490// ============================================================================
491// Helper: Build all schemas
492// ============================================================================
493
494/// Build and return all known provider schemas.
495pub fn build_all_provider_schemas() -> Vec<ProviderSchema> {
496    vec![
497        copilot_json_v3(),
498        copilot_jsonl_v1(),
499        cursor_v1(),
500        claude_code_v1(),
501        codex_cli_v1(),
502        gemini_cli_v1(),
503        continue_dev_v1(),
504        openai_api_v1(),
505    ]
506}
507
508// ============================================================================
509// Field definitions — Copilot Chat JSON v3
510// ============================================================================
511
512fn copilot_session_fields_v3() -> Vec<FieldSchema> {
513    vec![
514        FieldSchema {
515            name: "version".into(),
516            serialized_name: None,
517            data_type: DataType::Integer,
518            required: true,
519            default_value: Some(serde_json::json!(3)),
520            description: "Session format version (always 3)".into(),
521            constraints: vec![FieldConstraint::Enum {
522                values: vec![serde_json::json!(3)],
523            }],
524            semantic_tag: Some("schema_version".into()),
525            since_version: None,
526            removed_in: None,
527        },
528        FieldSchema {
529            name: "sessionId".into(),
530            serialized_name: Some("session_id".into()),
531            data_type: DataType::Uuid,
532            required: false,
533            default_value: None,
534            description: "Unique session identifier (UUID). May be absent in file; use filename."
535                .into(),
536            constraints: vec![],
537            semantic_tag: Some("session_id".into()),
538            since_version: None,
539            removed_in: None,
540        },
541        FieldSchema {
542            name: "creationDate".into(),
543            serialized_name: Some("creation_date".into()),
544            data_type: DataType::Timestamp,
545            required: true,
546            default_value: Some(serde_json::json!(0)),
547            description: "Session creation timestamp (milliseconds since epoch)".into(),
548            constraints: vec![],
549            semantic_tag: Some("created_at".into()),
550            since_version: None,
551            removed_in: None,
552        },
553        FieldSchema {
554            name: "lastMessageDate".into(),
555            serialized_name: Some("last_message_date".into()),
556            data_type: DataType::Timestamp,
557            required: true,
558            default_value: Some(serde_json::json!(0)),
559            description: "Last message timestamp (milliseconds since epoch)".into(),
560            constraints: vec![],
561            semantic_tag: Some("updated_at".into()),
562            since_version: None,
563            removed_in: None,
564        },
565        FieldSchema {
566            name: "isImported".into(),
567            serialized_name: Some("is_imported".into()),
568            data_type: DataType::Boolean,
569            required: false,
570            default_value: Some(serde_json::json!(false)),
571            description: "Whether this session was imported from another source".into(),
572            constraints: vec![],
573            semantic_tag: Some("is_imported".into()),
574            since_version: None,
575            removed_in: None,
576        },
577        FieldSchema {
578            name: "initialLocation".into(),
579            serialized_name: Some("initial_location".into()),
580            data_type: DataType::Enum(vec![
581                "panel".into(),
582                "terminal".into(),
583                "notebook".into(),
584                "editor".into(),
585            ]),
586            required: true,
587            default_value: Some(serde_json::json!("panel")),
588            description: "Where the session was initiated in VS Code".into(),
589            constraints: vec![],
590            semantic_tag: Some("session_location".into()),
591            since_version: None,
592            removed_in: None,
593        },
594        FieldSchema {
595            name: "customTitle".into(),
596            serialized_name: Some("custom_title".into()),
597            data_type: DataType::Optional(Box::new(DataType::String)),
598            required: false,
599            default_value: None,
600            description: "User-set title for the session".into(),
601            constraints: vec![],
602            semantic_tag: Some("title".into()),
603            since_version: None,
604            removed_in: None,
605        },
606        FieldSchema {
607            name: "requesterUsername".into(),
608            serialized_name: Some("requester_username".into()),
609            data_type: DataType::Optional(Box::new(DataType::String)),
610            required: false,
611            default_value: None,
612            description: "Display name of the user who initiated the session".into(),
613            constraints: vec![],
614            semantic_tag: Some("user_name".into()),
615            since_version: None,
616            removed_in: None,
617        },
618        FieldSchema {
619            name: "responderUsername".into(),
620            serialized_name: Some("responder_username".into()),
621            data_type: DataType::Optional(Box::new(DataType::String)),
622            required: false,
623            default_value: None,
624            description: "Display name of the AI responder".into(),
625            constraints: vec![],
626            semantic_tag: Some("assistant_name".into()),
627            since_version: None,
628            removed_in: None,
629        },
630        FieldSchema {
631            name: "requests".into(),
632            serialized_name: None,
633            data_type: DataType::Array(Box::new(DataType::Object("ChatRequest".into()))),
634            required: true,
635            default_value: Some(serde_json::json!([])),
636            description: "Array of chat request/response pairs".into(),
637            constraints: vec![],
638            semantic_tag: Some("messages".into()),
639            since_version: None,
640            removed_in: None,
641        },
642    ]
643}
644
645fn copilot_nested_objects_v3() -> HashMap<String, Vec<FieldSchema>> {
646    let mut map = HashMap::new();
647
648    map.insert(
649        "ChatRequest".into(),
650        vec![
651            FieldSchema {
652                name: "timestamp".into(),
653                serialized_name: None,
654                data_type: DataType::Optional(Box::new(DataType::Timestamp)),
655                required: false,
656                default_value: None,
657                description: "Request timestamp (milliseconds since epoch)".into(),
658                constraints: vec![],
659                semantic_tag: Some("message_timestamp".into()),
660                since_version: None,
661                removed_in: None,
662            },
663            FieldSchema {
664                name: "message".into(),
665                serialized_name: None,
666                data_type: DataType::Optional(Box::new(DataType::Object("ChatMessage".into()))),
667                required: false,
668                default_value: None,
669                description: "The user's message".into(),
670                constraints: vec![],
671                semantic_tag: Some("user_message".into()),
672                since_version: None,
673                removed_in: None,
674            },
675            FieldSchema {
676                name: "response".into(),
677                serialized_name: None,
678                data_type: DataType::Optional(Box::new(DataType::Json)),
679                required: false,
680                default_value: None,
681                description: "AI response. Legacy format: {\"value\": [{\"value\": \"text\"}]}".into(),
682                constraints: vec![],
683                semantic_tag: Some("assistant_response".into()),
684                since_version: None,
685                removed_in: None,
686            },
687            FieldSchema {
688                name: "requestId".into(),
689                serialized_name: Some("request_id".into()),
690                data_type: DataType::Optional(Box::new(DataType::Uuid)),
691                required: false,
692                default_value: None,
693                description: "Unique request identifier".into(),
694                constraints: vec![],
695                semantic_tag: Some("request_id".into()),
696                since_version: None,
697                removed_in: None,
698            },
699            FieldSchema {
700                name: "modelId".into(),
701                serialized_name: Some("model_id".into()),
702                data_type: DataType::Optional(Box::new(DataType::String)),
703                required: false,
704                default_value: None,
705                description: "LLM model used for this request (e.g., 'gpt-4o', 'claude-3.5-sonnet')".into(),
706                constraints: vec![],
707                semantic_tag: Some("model_id".into()),
708                since_version: None,
709                removed_in: None,
710            },
711            FieldSchema {
712                name: "agent".into(),
713                serialized_name: None,
714                data_type: DataType::Optional(Box::new(DataType::Json)),
715                required: false,
716                default_value: None,
717                description: "Agent information (for agent-mode sessions)".into(),
718                constraints: vec![],
719                semantic_tag: Some("agent".into()),
720                since_version: Some("0.29.0".into()),
721                removed_in: None,
722            },
723            FieldSchema {
724                name: "isCanceled".into(),
725                serialized_name: Some("is_canceled".into()),
726                data_type: DataType::Optional(Box::new(DataType::Boolean)),
727                required: false,
728                default_value: None,
729                description: "Whether the request was canceled by the user".into(),
730                constraints: vec![],
731                semantic_tag: Some("is_canceled".into()),
732                since_version: None,
733                removed_in: None,
734            },
735            FieldSchema {
736                name: "variableData".into(),
737                serialized_name: Some("variable_data".into()),
738                data_type: DataType::Optional(Box::new(DataType::Json)),
739                required: false,
740                default_value: None,
741                description: "Context variables (files, selections, terminal output)".into(),
742                constraints: vec![],
743                semantic_tag: Some("context".into()),
744                since_version: None,
745                removed_in: None,
746            },
747        ],
748    );
749
750    map.insert(
751        "ChatMessage".into(),
752        vec![
753            FieldSchema {
754                name: "text".into(),
755                serialized_name: None,
756                data_type: DataType::Optional(Box::new(DataType::String)),
757                required: false,
758                default_value: None,
759                description: "Message text content".into(),
760                constraints: vec![],
761                semantic_tag: Some("message_text".into()),
762                since_version: None,
763                removed_in: None,
764            },
765            FieldSchema {
766                name: "parts".into(),
767                serialized_name: None,
768                data_type: DataType::Optional(Box::new(DataType::Array(Box::new(DataType::Json)))),
769                required: false,
770                default_value: None,
771                description: "Complex message parts (for multi-modal messages)".into(),
772                constraints: vec![],
773                semantic_tag: Some("message_parts".into()),
774                since_version: None,
775                removed_in: None,
776            },
777        ],
778    );
779
780    map
781}
782
783// ============================================================================
784// Field definitions — Copilot Chat JSONL v1
785// ============================================================================
786
787fn copilot_jsonl_event_fields() -> Vec<FieldSchema> {
788    vec![
789        FieldSchema {
790            name: "kind".into(),
791            serialized_name: None,
792            data_type: DataType::Enum(vec!["0".into(), "1".into(), "2".into()]),
793            required: true,
794            default_value: None,
795            description: "Event kind: 0=full snapshot, 1=request update, 2=response update".into(),
796            constraints: vec![],
797            semantic_tag: Some("event_type".into()),
798            since_version: Some("0.37.0".into()),
799            removed_in: None,
800        },
801        FieldSchema {
802            name: "data".into(),
803            serialized_name: None,
804            data_type: DataType::Json,
805            required: true,
806            default_value: None,
807            description: "Event payload. For kind:0, the full session object. For kind:1/2, update deltas.".into(),
808            constraints: vec![],
809            semantic_tag: Some("event_data".into()),
810            since_version: Some("0.37.0".into()),
811            removed_in: None,
812        },
813    ]
814}
815
816fn copilot_nested_objects_jsonl() -> HashMap<String, Vec<FieldSchema>> {
817    let mut map = copilot_nested_objects_v3();
818
819    // Add modelState to ChatRequest (new in JSONL)
820    if let Some(request_fields) = map.get_mut("ChatRequest") {
821        request_fields.push(FieldSchema {
822            name: "modelState".into(),
823            serialized_name: Some("model_state".into()),
824            data_type: DataType::Optional(Box::new(DataType::Object("ModelState".into()))),
825            required: false,
826            default_value: None,
827            description: "Model processing state. value: 0=Pending, 1=Complete, 2=Cancelled".into(),
828            constraints: vec![],
829            semantic_tag: Some("model_state".into()),
830            since_version: Some("0.37.0".into()),
831            removed_in: None,
832        });
833        request_fields.push(FieldSchema {
834            name: "timeSpentWaiting".into(),
835            serialized_name: Some("time_spent_waiting".into()),
836            data_type: DataType::Optional(Box::new(DataType::Integer)),
837            required: false,
838            default_value: None,
839            description: "Time spent waiting for response (milliseconds)".into(),
840            constraints: vec![],
841            semantic_tag: Some("latency".into()),
842            since_version: Some("0.37.0".into()),
843            removed_in: None,
844        });
845    }
846
847    // Add ModelState object
848    map.insert(
849        "ModelState".into(),
850        vec![
851            FieldSchema {
852                name: "value".into(),
853                serialized_name: None,
854                data_type: DataType::Enum(vec!["0".into(), "1".into(), "2".into()]),
855                required: true,
856                default_value: None,
857                description: "0=Pending, 1=Complete, 2=Cancelled".into(),
858                constraints: vec![],
859                semantic_tag: Some("completion_state".into()),
860                since_version: Some("0.37.0".into()),
861                removed_in: None,
862            },
863            FieldSchema {
864                name: "completedAt".into(),
865                serialized_name: Some("completed_at".into()),
866                data_type: DataType::Optional(Box::new(DataType::Timestamp)),
867                required: false,
868                default_value: None,
869                description: "Timestamp when the model finished processing".into(),
870                constraints: vec![],
871                semantic_tag: Some("completed_at".into()),
872                since_version: Some("0.37.0".into()),
873                removed_in: None,
874            },
875        ],
876    );
877
878    map
879}
880
881// ============================================================================
882// DB Key schemas — Copilot (shared fields + version-specific)
883// ============================================================================
884
885fn copilot_db_keys_json() -> Vec<DbKeySchema> {
886    vec![
887        DbKeySchema {
888            key: "chat.ChatSessionStore.index".into(),
889            description: "Session index mapping session IDs to metadata. Old format uses array of UUID strings.".into(),
890            value_type: DataType::Json,
891            value_fields: vec![
892                FieldSchema {
893                    name: "version".into(),
894                    serialized_name: None,
895                    data_type: DataType::Integer,
896                    required: true,
897                    default_value: Some(serde_json::json!(1)),
898                    description: "Index version".into(),
899                    constraints: vec![],
900                    semantic_tag: Some("index_version".into()),
901                    since_version: None,
902                    removed_in: None,
903                },
904                FieldSchema {
905                    name: "entries".into(),
906                    serialized_name: None,
907                    data_type: DataType::Array(Box::new(DataType::Uuid)),
908                    required: true,
909                    default_value: None,
910                    description: "Array of session UUID strings (old format)".into(),
911                    constraints: vec![],
912                    semantic_tag: Some("session_index".into()),
913                    since_version: None,
914                    removed_in: Some("0.37.0".into()),
915                },
916            ],
917            required: true,
918            since_version: None,
919            removed_in: None,
920            renamed_to: None,
921        },
922        DbKeySchema {
923            key: "memento/interactive-session".into(),
924            description: "Session input history and active session state".into(),
925            value_type: DataType::Json,
926            value_fields: vec![],
927            required: false,
928            since_version: None,
929            removed_in: None,
930            renamed_to: None,
931        },
932        DbKeySchema {
933            key: "memento/interactive-session-view-copilot".into(),
934            description: "Current active session view state (which session is open)".into(),
935            value_type: DataType::Json,
936            value_fields: vec![],
937            required: false,
938            since_version: None,
939            removed_in: None,
940            renamed_to: None,
941        },
942    ]
943}
944
945fn copilot_db_keys_jsonl() -> Vec<DbKeySchema> {
946    vec![
947        DbKeySchema {
948            key: "chat.ChatSessionStore.index".into(),
949            description: "Session index mapping session IDs to rich metadata entries.".into(),
950            value_type: DataType::Json,
951            value_fields: copilot_index_entry_fields(),
952            required: true,
953            since_version: Some("0.37.0".into()),
954            removed_in: None,
955            renamed_to: None,
956        },
957        DbKeySchema {
958            key: "agentSessions.model.cache".into(),
959            description: "Model cache that drives Chat sidebar visibility. Sessions MUST have entries here to appear in VS Code UI.".into(),
960            value_type: DataType::Array(Box::new(DataType::Object("ModelCacheEntry".into()))),
961            value_fields: copilot_model_cache_fields(),
962            required: true,
963            since_version: Some("0.37.0".into()),
964            removed_in: None,
965            renamed_to: None,
966        },
967        DbKeySchema {
968            key: "agentSessions.state.cache".into(),
969            description: "Session read/archived state for UI indicators".into(),
970            value_type: DataType::Array(Box::new(DataType::Object("StateCacheEntry".into()))),
971            value_fields: copilot_state_cache_fields(),
972            required: false,
973            since_version: Some("0.37.0".into()),
974            removed_in: None,
975            renamed_to: None,
976        },
977        DbKeySchema {
978            key: "memento/interactive-session".into(),
979            description: "Session input history and active session state".into(),
980            value_type: DataType::Json,
981            value_fields: vec![],
982            required: false,
983            since_version: None,
984            removed_in: None,
985            renamed_to: None,
986        },
987        DbKeySchema {
988            key: "memento/interactive-session-view-copilot".into(),
989            description: "Current active session view state (which session is open)".into(),
990            value_type: DataType::Json,
991            value_fields: vec![],
992            required: false,
993            since_version: None,
994            removed_in: None,
995            renamed_to: None,
996        },
997    ]
998}
999
1000fn copilot_index_entry_fields() -> Vec<FieldSchema> {
1001    vec![
1002        FieldSchema {
1003            name: "version".into(),
1004            serialized_name: None,
1005            data_type: DataType::Integer,
1006            required: true,
1007            default_value: Some(serde_json::json!(1)),
1008            description: "Index format version".into(),
1009            constraints: vec![],
1010            semantic_tag: Some("index_version".into()),
1011            since_version: None,
1012            removed_in: None,
1013        },
1014        FieldSchema {
1015            name: "entries".into(),
1016            serialized_name: None,
1017            data_type: DataType::Object("Map<UUID, IndexEntry>".into()),
1018            required: true,
1019            default_value: None,
1020            description: "Map of session UUID → IndexEntry objects (new format)".into(),
1021            constraints: vec![],
1022            semantic_tag: Some("session_index".into()),
1023            since_version: Some("0.37.0".into()),
1024            removed_in: None,
1025        },
1026    ]
1027}
1028
1029fn copilot_model_cache_fields() -> Vec<FieldSchema> {
1030    vec![
1031        FieldSchema {
1032            name: "providerType".into(),
1033            serialized_name: Some("provider_type".into()),
1034            data_type: DataType::String,
1035            required: true,
1036            default_value: Some(serde_json::json!("local")),
1037            description: "Always 'local' for local sessions".into(),
1038            constraints: vec![],
1039            semantic_tag: None,
1040            since_version: Some("0.37.0".into()),
1041            removed_in: None,
1042        },
1043        FieldSchema {
1044            name: "providerLabel".into(),
1045            serialized_name: Some("provider_label".into()),
1046            data_type: DataType::String,
1047            required: true,
1048            default_value: Some(serde_json::json!("Local")),
1049            description: "Always 'Local' for local sessions".into(),
1050            constraints: vec![],
1051            semantic_tag: None,
1052            since_version: Some("0.37.0".into()),
1053            removed_in: None,
1054        },
1055        FieldSchema {
1056            name: "resource".into(),
1057            serialized_name: None,
1058            data_type: DataType::Uri,
1059            required: true,
1060            default_value: None,
1061            description: "Resource URI: vscode-chat-session://local/{base64(sessionId)}".into(),
1062            constraints: vec![FieldConstraint::Pattern {
1063                pattern: "^vscode-chat-session://local/[A-Za-z0-9+/=]+$".into(),
1064            }],
1065            semantic_tag: Some("resource_uri".into()),
1066            since_version: Some("0.37.0".into()),
1067            removed_in: None,
1068        },
1069        FieldSchema {
1070            name: "icon".into(),
1071            serialized_name: None,
1072            data_type: DataType::String,
1073            required: false,
1074            default_value: Some(serde_json::json!("vm")),
1075            description: "Icon identifier for the UI".into(),
1076            constraints: vec![],
1077            semantic_tag: None,
1078            since_version: Some("0.37.0".into()),
1079            removed_in: None,
1080        },
1081        FieldSchema {
1082            name: "label".into(),
1083            serialized_name: None,
1084            data_type: DataType::String,
1085            required: true,
1086            default_value: None,
1087            description: "Session title displayed in sidebar".into(),
1088            constraints: vec![],
1089            semantic_tag: Some("title".into()),
1090            since_version: Some("0.37.0".into()),
1091            removed_in: None,
1092        },
1093        FieldSchema {
1094            name: "status".into(),
1095            serialized_name: None,
1096            data_type: DataType::Integer,
1097            required: true,
1098            default_value: Some(serde_json::json!(1)),
1099            description: "Status: 1 = valid".into(),
1100            constraints: vec![],
1101            semantic_tag: None,
1102            since_version: Some("0.37.0".into()),
1103            removed_in: None,
1104        },
1105        FieldSchema {
1106            name: "timing".into(),
1107            serialized_name: None,
1108            data_type: DataType::Object("ChatSessionTiming".into()),
1109            required: true,
1110            default_value: None,
1111            description: "Session timing (created, lastRequestStarted, lastRequestEnded)".into(),
1112            constraints: vec![],
1113            semantic_tag: Some("timing".into()),
1114            since_version: Some("0.37.0".into()),
1115            removed_in: None,
1116        },
1117        FieldSchema {
1118            name: "isEmpty".into(),
1119            serialized_name: Some("is_empty".into()),
1120            data_type: DataType::Boolean,
1121            required: true,
1122            default_value: Some(serde_json::json!(false)),
1123            description: "Whether the session has no requests. MUST be false for VS Code to load.".into(),
1124            constraints: vec![],
1125            semantic_tag: Some("is_empty".into()),
1126            since_version: Some("0.37.0".into()),
1127            removed_in: None,
1128        },
1129        FieldSchema {
1130            name: "lastResponseState".into(),
1131            serialized_name: Some("last_response_state".into()),
1132            data_type: DataType::Enum(vec![
1133                "0".into(),
1134                "1".into(),
1135                "2".into(),
1136                "3".into(),
1137                "4".into(),
1138            ]),
1139            required: true,
1140            default_value: Some(serde_json::json!(1)),
1141            description: "0=Pending, 1=Complete, 2=Cancelled, 3=Failed, 4=NeedsInput".into(),
1142            constraints: vec![],
1143            semantic_tag: Some("response_state".into()),
1144            since_version: Some("0.37.0".into()),
1145            removed_in: None,
1146        },
1147    ]
1148}
1149
1150fn copilot_state_cache_fields() -> Vec<FieldSchema> {
1151    vec![
1152        FieldSchema {
1153            name: "resource".into(),
1154            serialized_name: None,
1155            data_type: DataType::Uri,
1156            required: true,
1157            default_value: None,
1158            description: "Resource URI (same as model cache)".into(),
1159            constraints: vec![],
1160            semantic_tag: Some("resource_uri".into()),
1161            since_version: Some("0.37.0".into()),
1162            removed_in: None,
1163        },
1164        FieldSchema {
1165            name: "read".into(),
1166            serialized_name: None,
1167            data_type: DataType::Optional(Box::new(DataType::Timestamp)),
1168            required: false,
1169            default_value: None,
1170            description: "Timestamp when the session was last read by the user".into(),
1171            constraints: vec![],
1172            semantic_tag: Some("last_read".into()),
1173            since_version: Some("0.37.0".into()),
1174            removed_in: None,
1175        },
1176    ]
1177}
1178
1179// ============================================================================
1180// Field definitions — Cursor
1181// ============================================================================
1182
1183fn cursor_session_fields() -> Vec<FieldSchema> {
1184    // Cursor uses a format very similar to Copilot JSON v3
1185    let mut fields = copilot_session_fields_v3();
1186    // Cursor may have additional fields
1187    fields.push(FieldSchema {
1188        name: "workspaceId".into(),
1189        serialized_name: Some("workspace_id".into()),
1190        data_type: DataType::Optional(Box::new(DataType::String)),
1191        required: false,
1192        default_value: None,
1193        description: "Cursor workspace identifier".into(),
1194        constraints: vec![],
1195        semantic_tag: Some("workspace_id".into()),
1196        since_version: None,
1197        removed_in: None,
1198    });
1199    fields
1200}
1201
1202// ============================================================================
1203// Field definitions — Claude Code
1204// ============================================================================
1205
1206fn claude_code_fields() -> Vec<FieldSchema> {
1207    vec![
1208        FieldSchema {
1209            name: "type".into(),
1210            serialized_name: None,
1211            data_type: DataType::Enum(vec![
1212                "human".into(),
1213                "assistant".into(),
1214                "system".into(),
1215                "tool_use".into(),
1216                "tool_result".into(),
1217            ]),
1218            required: true,
1219            default_value: None,
1220            description: "Message event type".into(),
1221            constraints: vec![],
1222            semantic_tag: Some("message_role".into()),
1223            since_version: None,
1224            removed_in: None,
1225        },
1226        FieldSchema {
1227            name: "message".into(),
1228            serialized_name: None,
1229            data_type: DataType::Object("ClaudeMessage".into()),
1230            required: true,
1231            default_value: None,
1232            description: "Message content object with role and content fields".into(),
1233            constraints: vec![],
1234            semantic_tag: Some("message".into()),
1235            since_version: None,
1236            removed_in: None,
1237        },
1238        FieldSchema {
1239            name: "timestamp".into(),
1240            serialized_name: None,
1241            data_type: DataType::String,
1242            required: false,
1243            default_value: None,
1244            description: "ISO 8601 timestamp".into(),
1245            constraints: vec![],
1246            semantic_tag: Some("message_timestamp".into()),
1247            since_version: None,
1248            removed_in: None,
1249        },
1250        FieldSchema {
1251            name: "costUSD".into(),
1252            serialized_name: Some("cost_usd".into()),
1253            data_type: DataType::Optional(Box::new(DataType::Float)),
1254            required: false,
1255            default_value: None,
1256            description: "Cost in USD for this message".into(),
1257            constraints: vec![],
1258            semantic_tag: Some("cost".into()),
1259            since_version: None,
1260            removed_in: None,
1261        },
1262        FieldSchema {
1263            name: "durationMs".into(),
1264            serialized_name: Some("duration_ms".into()),
1265            data_type: DataType::Optional(Box::new(DataType::Integer)),
1266            required: false,
1267            default_value: None,
1268            description: "Processing duration in milliseconds".into(),
1269            constraints: vec![],
1270            semantic_tag: Some("latency".into()),
1271            since_version: None,
1272            removed_in: None,
1273        },
1274    ]
1275}
1276
1277// ============================================================================
1278// Field definitions — Codex CLI
1279// ============================================================================
1280
1281fn codex_cli_fields() -> Vec<FieldSchema> {
1282    vec![
1283        FieldSchema {
1284            name: "role".into(),
1285            serialized_name: None,
1286            data_type: DataType::Enum(vec![
1287                "system".into(),
1288                "user".into(),
1289                "assistant".into(),
1290                "tool".into(),
1291            ]),
1292            required: true,
1293            default_value: None,
1294            description: "Message role (OpenAI format)".into(),
1295            constraints: vec![],
1296            semantic_tag: Some("message_role".into()),
1297            since_version: None,
1298            removed_in: None,
1299        },
1300        FieldSchema {
1301            name: "content".into(),
1302            serialized_name: None,
1303            data_type: DataType::String,
1304            required: true,
1305            default_value: None,
1306            description: "Message content text".into(),
1307            constraints: vec![],
1308            semantic_tag: Some("message_text".into()),
1309            since_version: None,
1310            removed_in: None,
1311        },
1312        FieldSchema {
1313            name: "timestamp".into(),
1314            serialized_name: None,
1315            data_type: DataType::Optional(Box::new(DataType::Timestamp)),
1316            required: false,
1317            default_value: None,
1318            description: "Message timestamp".into(),
1319            constraints: vec![],
1320            semantic_tag: Some("message_timestamp".into()),
1321            since_version: None,
1322            removed_in: None,
1323        },
1324    ]
1325}
1326
1327// ============================================================================
1328// Field definitions — Gemini CLI
1329// ============================================================================
1330
1331fn gemini_cli_fields() -> Vec<FieldSchema> {
1332    vec![
1333        FieldSchema {
1334            name: "role".into(),
1335            serialized_name: None,
1336            data_type: DataType::Enum(vec!["user".into(), "model".into()]),
1337            required: true,
1338            default_value: None,
1339            description: "Message role (Gemini uses 'model' for assistant)".into(),
1340            constraints: vec![],
1341            semantic_tag: Some("message_role".into()),
1342            since_version: None,
1343            removed_in: None,
1344        },
1345        FieldSchema {
1346            name: "parts".into(),
1347            serialized_name: None,
1348            data_type: DataType::Array(Box::new(DataType::Object("GeminiPart".into()))),
1349            required: true,
1350            default_value: None,
1351            description: "Content parts array (Gemini multi-part format)".into(),
1352            constraints: vec![],
1353            semantic_tag: Some("message_parts".into()),
1354            since_version: None,
1355            removed_in: None,
1356        },
1357    ]
1358}
1359
1360// ============================================================================
1361// Field definitions — Continue.dev
1362// ============================================================================
1363
1364fn continue_dev_fields() -> Vec<FieldSchema> {
1365    vec![
1366        FieldSchema {
1367            name: "sessionId".into(),
1368            serialized_name: Some("session_id".into()),
1369            data_type: DataType::Uuid,
1370            required: true,
1371            default_value: None,
1372            description: "Session identifier".into(),
1373            constraints: vec![],
1374            semantic_tag: Some("session_id".into()),
1375            since_version: None,
1376            removed_in: None,
1377        },
1378        FieldSchema {
1379            name: "title".into(),
1380            serialized_name: None,
1381            data_type: DataType::Optional(Box::new(DataType::String)),
1382            required: false,
1383            default_value: None,
1384            description: "Session title".into(),
1385            constraints: vec![],
1386            semantic_tag: Some("title".into()),
1387            since_version: None,
1388            removed_in: None,
1389        },
1390        FieldSchema {
1391            name: "dateCreated".into(),
1392            serialized_name: Some("date_created".into()),
1393            data_type: DataType::String,
1394            required: true,
1395            default_value: None,
1396            description: "ISO 8601 creation date".into(),
1397            constraints: vec![],
1398            semantic_tag: Some("created_at".into()),
1399            since_version: None,
1400            removed_in: None,
1401        },
1402        FieldSchema {
1403            name: "history".into(),
1404            serialized_name: None,
1405            data_type: DataType::Array(Box::new(DataType::Object("ContinueMessage".into()))),
1406            required: true,
1407            default_value: None,
1408            description: "Array of history step objects".into(),
1409            constraints: vec![],
1410            semantic_tag: Some("messages".into()),
1411            since_version: None,
1412            removed_in: None,
1413        },
1414    ]
1415}
1416
1417// ============================================================================
1418// Field definitions — OpenAI API (generic)
1419// ============================================================================
1420
1421fn openai_api_fields() -> Vec<FieldSchema> {
1422    vec![
1423        FieldSchema {
1424            name: "model".into(),
1425            serialized_name: None,
1426            data_type: DataType::String,
1427            required: true,
1428            default_value: None,
1429            description: "Model identifier (e.g., 'gpt-4o', 'llama3.3')".into(),
1430            constraints: vec![],
1431            semantic_tag: Some("model_id".into()),
1432            since_version: None,
1433            removed_in: None,
1434        },
1435        FieldSchema {
1436            name: "messages".into(),
1437            serialized_name: None,
1438            data_type: DataType::Array(Box::new(DataType::Object("OpenAIMessage".into()))),
1439            required: true,
1440            default_value: None,
1441            description: "Array of message objects with role and content".into(),
1442            constraints: vec![],
1443            semantic_tag: Some("messages".into()),
1444            since_version: None,
1445            removed_in: None,
1446        },
1447        FieldSchema {
1448            name: "temperature".into(),
1449            serialized_name: None,
1450            data_type: DataType::Optional(Box::new(DataType::Float)),
1451            required: false,
1452            default_value: Some(serde_json::json!(1.0)),
1453            description: "Sampling temperature (0.0 – 2.0)".into(),
1454            constraints: vec![
1455                FieldConstraint::Min {
1456                    value: serde_json::json!(0.0),
1457                },
1458                FieldConstraint::Max {
1459                    value: serde_json::json!(2.0),
1460                },
1461            ],
1462            semantic_tag: Some("temperature".into()),
1463            since_version: None,
1464            removed_in: None,
1465        },
1466        FieldSchema {
1467            name: "tools".into(),
1468            serialized_name: None,
1469            data_type: DataType::Optional(Box::new(DataType::Array(Box::new(DataType::Object(
1470                "Tool".into(),
1471            ))))),
1472            required: false,
1473            default_value: None,
1474            description: "Available tools/functions for function calling".into(),
1475            constraints: vec![],
1476            semantic_tag: Some("tools".into()),
1477            since_version: None,
1478            removed_in: None,
1479        },
1480        FieldSchema {
1481            name: "stream".into(),
1482            serialized_name: None,
1483            data_type: DataType::Optional(Box::new(DataType::Boolean)),
1484            required: false,
1485            default_value: Some(serde_json::json!(false)),
1486            description: "Whether to stream the response".into(),
1487            constraints: vec![],
1488            semantic_tag: Some("streaming".into()),
1489            since_version: None,
1490            removed_in: None,
1491        },
1492    ]
1493}
1494
1495fn openai_api_nested_objects() -> HashMap<String, Vec<FieldSchema>> {
1496    let mut map = HashMap::new();
1497
1498    map.insert(
1499        "OpenAIMessage".into(),
1500        vec![
1501            FieldSchema {
1502                name: "role".into(),
1503                serialized_name: None,
1504                data_type: DataType::Enum(vec![
1505                    "system".into(),
1506                    "user".into(),
1507                    "assistant".into(),
1508                    "tool".into(),
1509                ]),
1510                required: true,
1511                default_value: None,
1512                description: "Message role".into(),
1513                constraints: vec![],
1514                semantic_tag: Some("message_role".into()),
1515                since_version: None,
1516                removed_in: None,
1517            },
1518            FieldSchema {
1519                name: "content".into(),
1520                serialized_name: None,
1521                data_type: DataType::String,
1522                required: true,
1523                default_value: None,
1524                description: "Message content".into(),
1525                constraints: vec![],
1526                semantic_tag: Some("message_text".into()),
1527                since_version: None,
1528                removed_in: None,
1529            },
1530            FieldSchema {
1531                name: "tool_calls".into(),
1532                serialized_name: None,
1533                data_type: DataType::Optional(Box::new(DataType::Array(Box::new(DataType::Object(
1534                    "ToolCall".into(),
1535                ))))),
1536                required: false,
1537                default_value: None,
1538                description: "Tool/function calls made by the assistant".into(),
1539                constraints: vec![],
1540                semantic_tag: Some("tool_calls".into()),
1541                since_version: None,
1542                removed_in: None,
1543            },
1544        ],
1545    );
1546
1547    map
1548}