Skip to main content

harn_vm/session_bundle/
schema.rs

1use serde_json::{json, Value as JsonValue};
2
3use super::{
4    SessionBundleError, SESSION_BUNDLE_SCHEMA_ID, SESSION_BUNDLE_SCHEMA_VERSION,
5    SESSION_BUNDLE_TYPE,
6};
7
8pub fn session_bundle_schema() -> JsonValue {
9    json!({
10        "$schema": "https://json-schema.org/draft/2020-12/schema",
11        "$id": SESSION_BUNDLE_SCHEMA_ID,
12        "title": "Harn Session Bundle v1",
13        "type": "object",
14        "additionalProperties": true,
15        "required": [
16            "_type",
17            "schema_version",
18            "bundle_id",
19            "created_at",
20            "producer",
21            "source",
22            "runtime",
23            "transcript",
24            "tools",
25            "permissions",
26            "replay",
27            "redaction",
28            "attachments"
29        ],
30        "properties": {
31            "_type": {"const": SESSION_BUNDLE_TYPE},
32            "schema_version": {"type": "integer", "minimum": 1, "maximum": SESSION_BUNDLE_SCHEMA_VERSION},
33            "bundle_id": {"type": "string", "minLength": 1},
34            "created_at": {"type": "string", "minLength": 1},
35            "producer": {
36                "type": "object",
37                "required": ["name", "version", "schema_id"],
38                "additionalProperties": true,
39                "properties": {
40                    "name": {"type": "string"},
41                    "version": {"type": "string"},
42                    "schema_id": {"type": "string"}
43                }
44            },
45            "source": {
46                "type": "object",
47                "required": ["kind", "run_record_id", "workflow_id", "task", "status"],
48                "additionalProperties": true,
49                "properties": {
50                    "kind": {"type": "string"},
51                    "run_record_id": {"type": "string"},
52                    "workflow_id": {"type": "string"},
53                    "workflow_name": {"type": ["string", "null"]},
54                    "task": {"type": "string"},
55                    "status": {"type": "string"},
56                    "started_at": {"type": "string"},
57                    "finished_at": {"type": ["string", "null"]},
58                    "persisted_path": {"type": ["string", "null"]},
59                    "root_run_id": {"type": ["string", "null"]},
60                    "parent_run_id": {"type": ["string", "null"]},
61                    "child_run_count": {"type": "integer", "minimum": 0}
62                }
63            },
64            "runtime": {
65                "type": "object",
66                "required": ["harn_version", "provider_models"],
67                "additionalProperties": true,
68                "properties": {
69                    "harn_version": {"type": "string"},
70                    "provider_models": {"type": "array", "items": {"type": "string"}},
71                    "usage": {"type": ["object", "null"]},
72                    "metadata": {"type": "object"}
73                }
74            },
75            "workspace": {"type": ["object", "null"]},
76            "transcript": {
77                "type": "object",
78                "required": ["sections"],
79                "additionalProperties": true,
80                "properties": {
81                    "sections": {
82                        "type": "array",
83                        "items": {"$ref": "#/$defs/transcript_section"}
84                    }
85                }
86            },
87            "tools": {
88                "type": "object",
89                "required": ["schemas", "calls"],
90                "additionalProperties": true,
91                "properties": {
92                    "schemas": {"type": "array", "items": {"$ref": "#/$defs/json_entry"}},
93                    "calls": {"type": "array", "items": {"type": "object"}}
94                }
95            },
96            "permissions": {"type": "array", "items": {"type": "object"}},
97            "replay": {
98                "type": "object",
99                "required": ["event_log_pointers", "transitions", "checkpoints", "trace_spans", "deterministic_events"],
100                "additionalProperties": true,
101                "properties": {
102                    "replay_fixture": {"type": ["object", "null"]},
103                    "run_record": {"type": ["object", "null"]},
104                    "observability": {"type": ["object", "null"]},
105                    "verification_outcomes": {"type": "array", "items": {"type": "object"}},
106                    "worker_snapshots": {"type": "array", "items": {"$ref": "#/$defs/worker_snapshot"}},
107                    "event_log_pointers": {"type": "array", "items": {"type": "object"}},
108                    "transitions": {"type": "array", "items": {"type": "object"}},
109                    "checkpoints": {"type": "array", "items": {"type": "object"}},
110                    "trace_spans": {"type": "array", "items": {"type": "object"}},
111                    "deterministic_events": {"type": "array", "items": {"$ref": "#/$defs/json_entry"}}
112                }
113            },
114            "redaction": {
115                "type": "object",
116                "required": ["mode", "policy", "placeholder", "entries", "unsafe_secret_markers_rejected"],
117                "additionalProperties": true,
118                "properties": {
119                    "mode": {"enum": ["local", "sanitized", "replay_only"]},
120                    "policy": {"type": "string"},
121                    "placeholder": {"type": "string"},
122                    "entries": {"type": "array", "items": {"type": "object"}},
123                    "unsafe_secret_markers_rejected": {"type": "boolean"}
124                }
125            },
126            "attachments": {"type": "array", "items": {"type": "object"}},
127            "metadata": {"type": "object"}
128        },
129        "$defs": {
130            "json_entry": {
131                "type": "object",
132                "required": ["source", "index", "value"],
133                "additionalProperties": true,
134                "properties": {
135                    "source": {"type": "string"},
136                    "index": {"type": "integer", "minimum": 0},
137                    "value": true
138                }
139            },
140            "transcript_section": {
141                "type": "object",
142                "required": ["id", "label", "scope", "location", "messages", "events", "assets", "metadata"],
143                "additionalProperties": true,
144                "properties": {
145                    "id": {"type": "string"},
146                    "label": {"type": "string"},
147                    "scope": {"type": "string"},
148                    "location": {"type": "string"},
149                    "summary": {"type": ["string", "null"]},
150                    "messages": {"type": "array", "items": true},
151                    "events": {"type": "array", "items": true},
152                    "assets": {"type": "array", "items": true},
153                    "metadata": {"type": "object"}
154                }
155            },
156            "worker_snapshot": {
157                "type": "object",
158                "required": ["worker_id", "worker_name", "status", "snapshot_ref", "value"],
159                "additionalProperties": true,
160                "properties": {
161                    "worker_id": {"type": "string"},
162                    "worker_name": {"type": "string"},
163                    "status": {"type": "string"},
164                    "snapshot_ref": {"type": "string"},
165                    "source_path": {"type": ["string", "null"]},
166                    "value": {"type": "object"}
167                }
168            }
169        }
170    })
171}
172
173pub fn session_bundle_schema_pretty() -> Result<String, SessionBundleError> {
174    serde_json::to_string_pretty(&session_bundle_schema())
175        .map(|schema| format!("{schema}\n"))
176        .map_err(|error| SessionBundleError::Encode(error.to_string()))
177}