envelope.mojo (7552B)
1 from std.collections import Dict, Optional 2 3 from json import Value, loads 4 from json.deserialize import Deserializable, get_string 5 6 from hyf_core.metadata import hyf_protocol_version 7 from hyf_core.operation_context import ( 8 OperationContext, 9 context_selects_v2_any, 10 is_corrected_operation, 11 operation_context_selects_v2, 12 parse_operation_context, 13 ) 14 from hyf_core.request_context import ( 15 RequestContext, 16 default_request_context, 17 parse_request_context, 18 ) 19 from hyf_stdio.errors import WireError 20 21 22 def _require_object(value: Value, context: String) raises: 23 if not value.is_object(): 24 raise Error(context + " must be a JSON object") 25 26 27 def _require_non_empty(value: String, field_name: String) raises: 28 if value == "": 29 raise Error( 30 "request envelope field '" + field_name + "' must not be empty" 31 ) 32 33 34 def _require_request_keys(value: Value) raises: 35 for key in value.object_keys(): 36 if ( 37 key != "version" 38 and key != "request_id" 39 and key != "trace_id" 40 and key != "capability" 41 and key != "context" 42 and key != "input" 43 ): 44 raise Error( 45 "request envelope contains unexpected field '" + key + "'" 46 ) 47 48 49 def _has_key(value: Value, key: String) -> Bool: 50 for candidate in value.object_keys(): 51 if candidate == key: 52 return True 53 return False 54 55 56 def _first_duplicate_root_key(value: Value) raises -> Optional[String]: 57 # ADR-0026 D46 CR04: decoded-key identity across every raw object entry, so 58 # escaped equivalents (`\u0063ontext`) and repeated values are both found 59 # rather than only the first `value[key]` lookup. 60 var seen = Dict[String, Bool]() 61 for item in value.object_items(): 62 var key = String(item[0]) 63 if key in seen: 64 return Optional[String](key) 65 seen[key] = True 66 return None 67 68 69 def envelope_targets_corrected_v2(json: Value) raises -> Bool: 70 """True when an envelope contains a corrected-op capability value or a 71 duplicate-aware v2 context selector. 72 73 Scans every raw root entry, so a duplicated `capability` or `context` key 74 cannot hide a corrected-operation value or a v2 selector behind an earlier 75 legacy entry. 76 """ 77 for item in json.object_items(): 78 if item[0] == "capability" and item[1].is_string(): 79 if is_corrected_operation(String(item[1].string_value())): 80 return True 81 elif item[0] == "context": 82 if context_selects_v2_any(item[1]): 83 return True 84 return False 85 86 87 def _require_protocol_version(json: Value) raises -> Int: 88 if not _has_key(json, "version"): 89 raise Error("request envelope field 'version' is required") 90 91 var version = json["version"] 92 if not version.is_int(): 93 raise Error("request envelope field 'version' must be an integer") 94 95 var version_value = Int(version.int_value()) 96 if version_value != hyf_protocol_version(): 97 raise Error( 98 "request envelope version " 99 + String(version_value) 100 + " is unsupported; expected " 101 + String(hyf_protocol_version()) 102 ) 103 return version_value 104 105 106 def _parse_optional_trace_id(json: Value) raises -> Optional[String]: 107 if not _has_key(json, "trace_id"): 108 return None 109 110 var trace_id = get_string(json, "trace_id") 111 _require_non_empty(trace_id, "trace_id") 112 return String(trace_id) 113 114 115 def _require_input_value(json: Value) raises -> Value: 116 if not _has_key(json, "input"): 117 raise Error("request envelope field 'input' is required") 118 119 var input = json["input"] 120 if not input.is_object(): 121 raise Error("request envelope field 'input' must be a JSON object") 122 return input.clone() 123 124 125 @fieldwise_init 126 struct WireRequest(Copyable, Deserializable, Movable): 127 var version: Int 128 var request_id: String 129 var trace_id: Optional[String] 130 var capability: String 131 var context: RequestContext 132 var input: Value 133 # ADR-0025 D45 CB01: present only for a recognized hyf_ops_v2 request to one 134 # of the three corrected operations. Legacy requests leave this None and 135 # keep the unchanged legacy context. 136 var operation_context: Optional[OperationContext] 137 138 @staticmethod 139 def from_json(json: Value) raises -> Self: 140 _require_object(json, "request envelope") 141 # ADR-0026 D46 CR04: reject ambiguous duplicate top-level keys before 142 # first-wins lookup/selector dispatch for any envelope that targets a 143 # corrected-operation capability or a v2 context selector. Unrelated 144 # legacy capabilities keep their existing admission behavior. 145 if envelope_targets_corrected_v2(json): 146 var duplicate = _first_duplicate_root_key(json) 147 if duplicate: 148 raise Error( 149 "request envelope contains duplicate top-level field '" 150 + duplicate.value() 151 + "'" 152 ) 153 _require_request_keys(json) 154 var version = _require_protocol_version(json) 155 156 var request_id = get_string(json, "request_id") 157 _require_non_empty(request_id, "request_id") 158 159 var trace_id = _parse_optional_trace_id(json) 160 161 var capability = get_string(json, "capability") 162 _require_non_empty(capability, "capability") 163 164 var context_json = Value(None) 165 if _has_key(json, "context"): 166 context_json = json["context"].clone() 167 168 var context = default_request_context() 169 var operation_context: Optional[OperationContext] = None 170 if is_corrected_operation(capability) and operation_context_selects_v2( 171 context_json 172 ): 173 operation_context = parse_operation_context( 174 context_json.clone(), capability 175 ) 176 else: 177 context = parse_request_context(context_json) 178 var input = _require_input_value(json) 179 180 return Self( 181 version=version, 182 request_id=request_id, 183 trace_id=trace_id^, 184 capability=capability, 185 context=context^, 186 input=input^, 187 operation_context=operation_context^, 188 ) 189 190 191 @fieldwise_init 192 struct WireSuccessResponse(Copyable, Movable): 193 var version: Int 194 var request_id: String 195 var trace_id: Optional[String] 196 var output: Value 197 var meta: Optional[Value] 198 199 def to_json_value(self) raises -> Value: 200 var value = loads("{}") 201 value.set("version", Value(self.version)) 202 value.set("request_id", Value(String(self.request_id))) 203 if self.trace_id: 204 value.set("trace_id", Value(String(self.trace_id.value()))) 205 value.set("ok", Value(True)) 206 value.set("output", self.output.clone()) 207 if self.meta: 208 value.set("meta", self.meta.value().clone()) 209 return value^ 210 211 212 @fieldwise_init 213 struct WireErrorResponse(Copyable, Movable): 214 var version: Int 215 var request_id: String 216 var trace_id: Optional[String] 217 var error: WireError 218 219 def to_json_value(self) raises -> Value: 220 var value = loads("{}") 221 value.set("version", Value(self.version)) 222 value.set("request_id", Value(String(self.request_id))) 223 if self.trace_id: 224 value.set("trace_id", Value(String(self.trace_id.value()))) 225 value.set("ok", Value(False)) 226 value.set("error", self.error.to_json_value()) 227 return value^