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}