@githolon/dsl 0.108.7 → 0.110.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/assets/operation-contract.wasm +0 -0
  2. package/check_package.mjs +12 -15
  3. package/compile_package.mjs +17 -18
  4. package/compile_proof.mjs +16 -1
  5. package/dart/CHANGELOG.md +76 -1
  6. package/dart/README.md +25 -2
  7. package/dart/lib/src/admission_decision.dart +41 -0
  8. package/dart/lib/src/application_framework_calls.dart +52 -2
  9. package/dart/lib/src/application_model_adoption.dart +187 -0
  10. package/dart/lib/src/application_status.dart +73 -0
  11. package/dart/lib/src/console_enrolment.dart +271 -0
  12. package/dart/lib/src/console_http_io.dart +26 -0
  13. package/dart/lib/src/console_http_stub.dart +7 -0
  14. package/dart/lib/src/dev_cloud.dart +69 -22
  15. package/dart/lib/src/generated_call.dart +83 -23
  16. package/dart/lib/src/kernel_primitives.dart +119 -0
  17. package/dart/lib/src/nms_conformance_runtime.dart +1 -1
  18. package/dart/lib/src/nms_generated_runtime.dart +32 -10
  19. package/dart/lib/src/nms_test_environment.dart +1 -1
  20. package/dart/lib/src/node_runner_transport.dart +41 -46
  21. package/dart/lib/src/observed_session_directory.dart +70 -0
  22. package/dart/lib/src/operation_receipts.dart +112 -0
  23. package/dart/lib/src/peer_application_streams.dart +103 -0
  24. package/dart/lib/src/reception/dart_reception.dart +4280 -0
  25. package/dart/lib/src/reception/event_desk.dart +211 -0
  26. package/dart/lib/src/reception/extremum_read.dart +46 -0
  27. package/dart/lib/src/reception/local_copies.dart +371 -0
  28. package/dart/lib/src/reception/peer_office_connection.dart +11 -0
  29. package/dart/lib/src/reception/progress_desk.dart +267 -0
  30. package/dart/lib/src/reception/reception_contract.dart +11 -0
  31. package/dart/lib/src/reception/reception_session.dart +284 -0
  32. package/dart/lib/src/reception/sync_diagnostics.dart +26 -0
  33. package/dart/lib/src/reception/workspace_preparation_report.dart +126 -0
  34. package/dart/lib/src/replica_readiness.dart +123 -0
  35. package/dart/lib/src/runner/node-entry.mjs +23905 -15774
  36. package/dart/lib/src/runtime_v2_gateway.dart +56 -3
  37. package/dart/lib/src/runtime_v2_transport.dart +544 -3
  38. package/dart/lib/src/session_bound.dart +200 -311
  39. package/dart/lib/src/signing_identity_kernel.dart +84 -0
  40. package/dart/lib/src/sync_status_projection.dart +548 -0
  41. package/dart/lib/src/sync_status_reducer.dart +170 -0
  42. package/dart/lib/src/test_harness.dart +809 -1153
  43. package/dart/lib/src/testing/test_custody.dart +66 -0
  44. package/dart/lib/src/testing/test_peer.dart +328 -0
  45. package/dart/lib/src/typed_watch_registry.dart +169 -0
  46. package/dart/lib/src/workspace_open_progress.dart +27 -0
  47. package/dart/pubspec.lock +24 -1
  48. package/dart/pubspec.yaml +7 -2
  49. package/dart/pubspec_overrides.yaml +2 -0
  50. package/dart/test/admission_decision_test.dart +48 -0
  51. package/dart/test/background_model_adoption_test.dart +228 -0
  52. package/dart/test/bundle_version_pairing_test.dart +4 -4
  53. package/dart/test/child_authentication_repair_test.dart +359 -0
  54. package/dart/test/cloud_bound_local_shell_test.dart +119 -0
  55. package/dart/test/console_enrolment_refusal_test.dart +52 -0
  56. package/dart/test/console_enrolment_test.dart +227 -0
  57. package/dart/test/errors_test.dart +5 -5
  58. package/dart/test/event_plane_seam_test.dart +50 -164
  59. package/dart/test/extremum_read_test.dart +58 -0
  60. package/dart/test/fixtures/wedge/wedge-entry.mjs +3 -21
  61. package/dart/test/generated_query_test.dart +32 -0
  62. package/dart/test/harness_phase_timeout_test.dart +18 -23
  63. package/dart/test/kernel_author_after_read_test.dart +242 -0
  64. package/dart/test/kernel_fence_test.dart +137 -0
  65. package/dart/test/local_copies_test.dart +222 -0
  66. package/dart/test/model_adoption_test.dart +198 -0
  67. package/dart/test/node_peer_local_custody_test.dart +153 -0
  68. package/dart/test/offer_authentication_proof_test.dart +352 -52
  69. package/dart/test/operation_compatibility_test.dart +151 -0
  70. package/dart/test/platform_credential_effect_test.dart +386 -0
  71. package/dart/test/receiving_group_open_test.dart +213 -0
  72. package/dart/test/reception/workspace_preparation_report_test.dart +103 -0
  73. package/dart/test/reception_span_trace_test.dart +23 -0
  74. package/dart/test/replica_readiness_test.dart +156 -0
  75. package/dart/test/runtime_projections_test.dart +92 -0
  76. package/dart/test/runtime_v2_gateway_test.dart +131 -0
  77. package/dart/test/signing_identity_kernel_test.dart +75 -0
  78. package/dart/test/sync_status_preparation_report_test.dart +29 -0
  79. package/dart/test/sync_status_reducer_test.dart +419 -0
  80. package/dart/test/test_harness_test.dart +71 -95
  81. package/dart/test/typed_query_watch_test.dart +170 -0
  82. package/dart/test/typed_watch_registry_test.dart +249 -0
  83. package/dart_lints/README.md +18 -0
  84. package/dart_lints/lib/nomos_lints.dart +70 -0
  85. package/dart_lints/pubspec.lock +580 -0
  86. package/dart_lints/pubspec.yaml +12 -0
  87. package/dart_lints/test/reference_identity_test.dart +75 -0
  88. package/dart_types/CHANGELOG.md +18 -0
  89. package/dart_types/lib/nomos_kernel_protocol.dart +21 -1
  90. package/dart_types/lib/nomos_types.dart +9 -1
  91. package/dart_types/lib/src/admission_decision.dart +37 -0
  92. package/dart_types/lib/src/aggregate_meta.dart +1 -1
  93. package/dart_types/lib/src/approved_model.dart +65 -0
  94. package/dart_types/lib/src/custody.dart +10 -18
  95. package/dart_types/lib/src/error_info.dart +4 -4
  96. package/dart_types/lib/src/errors.dart +29 -29
  97. package/dart_types/lib/src/foreign_ref.dart +62 -33
  98. package/dart_types/lib/src/local_work_rejected.dart +3 -3
  99. package/dart_types/lib/src/nomos_ref.dart +3 -1
  100. package/dart_types/lib/src/principal.dart +2 -2
  101. package/dart_types/lib/src/proto/google/protobuf/any.pb.dart +163 -0
  102. package/dart_types/lib/src/proto/google/protobuf/any.pbenum.dart +11 -0
  103. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pb.dart +137 -0
  104. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pbenum.dart +11 -0
  105. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +20721 -10433
  106. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +555 -27
  107. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pb.dart +387 -0
  108. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pbenum.dart +46 -0
  109. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pb.dart +295 -0
  110. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pbenum.dart +139 -0
  111. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pb.dart +269 -0
  112. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pbenum.dart +103 -0
  113. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pb.dart +417 -0
  114. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pbenum.dart +62 -0
  115. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +9968 -0
  116. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pbenum.dart +133 -0
  117. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pb.dart +204 -0
  118. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pbenum.dart +54 -0
  119. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pb.dart +1550 -0
  120. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pbenum.dart +11 -0
  121. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pb.dart +710 -0
  122. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pbenum.dart +11 -0
  123. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pb.dart +845 -0
  124. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pbenum.dart +11 -0
  125. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pb.dart +198 -0
  126. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pbenum.dart +11 -0
  127. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pb.dart +369 -0
  128. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pbenum.dart +11 -0
  129. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pb.dart +609 -0
  130. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pbenum.dart +65 -0
  131. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +358 -0
  132. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pbenum.dart +100 -0
  133. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +2231 -0
  134. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +271 -0
  135. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pb.dart +1286 -0
  136. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pbenum.dart +11 -0
  137. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pb.dart +636 -0
  138. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pbenum.dart +116 -0
  139. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +1637 -0
  140. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pbenum.dart +175 -0
  141. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pb.dart +1269 -0
  142. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pbenum.dart +129 -0
  143. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pb.dart +2782 -0
  144. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pbenum.dart +97 -0
  145. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +2232 -8627
  146. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +0 -358
  147. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pb.dart +1639 -0
  148. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pbenum.dart +46 -0
  149. package/dart_types/lib/src/runtime_outcome.dart +133 -0
  150. package/dart_types/lib/src/subscriptions.dart +116 -23
  151. package/dart_types/pubspec.yaml +1 -1
  152. package/dart_types/test/extremum_subscriptions_test.dart +40 -0
  153. package/dart_types/test/foreign_ref_resolution_test.dart +75 -0
  154. package/dart_types/test/nomos_ref_address_test.dart +25 -0
  155. package/dart_types/test/ordered_subscriptions_test.dart +34 -0
  156. package/operation_contract_check.mjs +24 -0
  157. package/package.json +11 -6
  158. package/src/aggregate.ts +70 -10
  159. package/src/application_topology.ts +18 -11
  160. package/src/attested.ts +3 -0
  161. package/src/attested_core.ts +46 -3
  162. package/src/birth_meter_ensure.ts +19 -7
  163. package/src/births.ts +29 -4
  164. package/src/build_package.ts +140 -13
  165. package/src/business_birth.ts +181 -4
  166. package/src/codegen_call_proto.ts +1274 -0
  167. package/src/codegen_call_usda.ts +49 -6
  168. package/src/codegen_dart.ts +79 -44
  169. package/src/codegen_dart_client.ts +458 -800
  170. package/src/{codegen_dart_application.ts → codegen_dart_facade.ts} +331 -582
  171. package/src/codegen_dart_framework_calls.ts +320 -37
  172. package/src/codegen_dart_indexed_variant.ts +159 -0
  173. package/src/codegen_dart_scenario.ts +42 -13
  174. package/src/codegen_proof.ts +3 -3
  175. package/src/codegen_provider_dart.ts +5 -0
  176. package/src/codegen_query_proto.ts +158 -0
  177. package/src/codegen_refusal.ts +189 -0
  178. package/src/codegen_result_proto.ts +931 -0
  179. package/src/codegen_result_usda.ts +4 -0
  180. package/src/codegen_ts.ts +464 -616
  181. package/src/codegen_workspace_access.ts +105 -0
  182. package/src/compatibility_graph.ts +759 -0
  183. package/src/compatibility_usda.ts +264 -0
  184. package/src/compile_package_main.ts +1994 -1189
  185. package/src/custody_placement.ts +156 -0
  186. package/src/custody_placement_usda.ts +91 -0
  187. package/src/decl_shapes.ts +21 -2
  188. package/src/describe_model.ts +1 -0
  189. package/src/directive.ts +59 -2
  190. package/src/drivers.ts +51 -0
  191. package/src/engine_entry.ts +52 -8
  192. package/src/evolve_preview.ts +11 -4
  193. package/src/exists.ts +2 -0
  194. package/src/fields.ts +75 -11
  195. package/src/flow_gate.ts +2 -1
  196. package/src/framework/application_home.ts +170 -18
  197. package/src/framework/attested_receipts.ts +8 -3
  198. package/src/framework/auth_provider_law.ts +169 -9
  199. package/src/framework/birth_certificate_law.ts +4 -0
  200. package/src/framework/capability.ts +34 -3
  201. package/src/framework/child_authentication.ts +346 -0
  202. package/src/framework/console_governance.ts +295 -0
  203. package/src/framework/delegated_roles.ts +301 -0
  204. package/src/framework/domain_lifecycle.ts +26 -13
  205. package/src/framework/founding_genesis.ts +193 -0
  206. package/src/framework/framework_aggregates.ts +1 -1
  207. package/src/framework/genesis.ts +6 -1
  208. package/src/framework/governance_relations.ts +19 -0
  209. package/src/framework/home.ts +5 -734
  210. package/src/framework/legacy/child_auth_provider_epoch.ts +73 -0
  211. package/src/framework/model_authority_repair.ts +116 -0
  212. package/src/framework/model_authority_seed.ts +185 -0
  213. package/src/framework/model_transition.ts +211 -0
  214. package/src/framework/owned_custody.ts +1 -1
  215. package/src/framework/principal_enrollment.ts +302 -0
  216. package/src/framework/receiving_group.ts +857 -0
  217. package/src/framework/receiving_group_recovery.ts +314 -0
  218. package/src/framework/relations.ts +4 -8
  219. package/src/framework/signing_identity.ts +242 -95
  220. package/src/framework/source_roster.ts +11 -5
  221. package/src/framework/workspace_access.ts +149 -0
  222. package/src/framework/workspace_invariant.ts +73 -52
  223. package/src/framework/workspaces.ts +407 -40
  224. package/src/framework_call_definitions.ts +32 -3
  225. package/src/generated_client_bundle_boundary.ts +97 -0
  226. package/src/generated_client_names.ts +1 -0
  227. package/src/identity_usda.ts +27 -0
  228. package/src/index.ts +183 -12
  229. package/src/indexed_variant.ts +83 -0
  230. package/src/intent.ts +173 -45
  231. package/src/law_check.ts +8 -5
  232. package/src/law_dependency_index.ts +231 -0
  233. package/src/law_graph.ts +508 -71
  234. package/src/law_graph_usda.ts +170 -24
  235. package/src/law_identity.ts +119 -81
  236. package/src/law_source_map.ts +4 -1
  237. package/src/law_units.ts +498 -0
  238. package/src/law_value_type.ts +7 -3
  239. package/src/member_authentication.ts +302 -0
  240. package/src/null_meta.ts +1 -1
  241. package/src/openusd_compiled_layer.ts +132 -0
  242. package/src/operation_contract_check.ts +31 -0
  243. package/src/ordered_read.ts +2 -1
  244. package/src/package_config.ts +33 -12
  245. package/src/package_dependencies.ts +1 -0
  246. package/src/plan_source_slice.ts +30 -1
  247. package/src/protobuf_field_number.ts +36 -0
  248. package/src/provenance_wire.ts +6 -0
  249. package/src/query.ts +2 -1
  250. package/src/read.ts +50 -0
  251. package/src/relation.ts +4 -1
  252. package/src/relation_schema.ts +15 -2
  253. package/src/report.ts +2 -2
  254. package/src/sqlite_computation_bundle.ts +78 -0
  255. package/src/sqlite_computed.ts +102 -0
  256. package/src/sqlite_counts.ts +154 -0
  257. package/src/sqlite_layout.ts +823 -0
  258. package/src/sqlite_reads.ts +275 -0
  259. package/src/sqlite_samples.ts +185 -0
  260. package/src/stable_id_identity_bridge.ts +1 -1
  261. package/src/stable_ids_for_module.ts +16 -4
  262. package/src/test_call_usda.ts +6 -6
  263. package/src/usda_relationship_integrity.ts +20 -3
  264. package/src/usda_text.ts +21 -1
  265. package/src/value_object.ts +81 -11
  266. package/src/wire.ts +22 -0
  267. package/src/wire_encode.ts +227 -20
  268. package/src/workspace_sharding.ts +15 -6
  269. package/src/workspace_sharing.ts +51 -49
  270. package/src/zod_introspection.ts +2 -0
  271. package/compat_package.mjs +0 -46
  272. package/dart/lib/src/home.dart +0 -15
  273. package/dart/lib/src/runner/offline-install.mjs +0 -15367
  274. package/dart/lib/src/source_trust.dart +0 -51
  275. package/dart/lib/src/webview_bridge.dart +0 -6540
  276. package/dart/lib/src/workspace_name.dart +0 -30
  277. package/dart/test/acquire_session_credentials_test.dart +0 -150
  278. package/dart/test/acquire_session_test.dart +0 -534
  279. package/dart/test/adv_realm_interference_test.dart +0 -600
  280. package/dart/test/cloud_bound_topology_test.dart +0 -168
  281. package/dart/test/cluster_births_test.dart +0 -202
  282. package/dart/test/cluster_device_keyed_acceptance_test.dart +0 -105
  283. package/dart/test/cluster_local_autosync_test.dart +0 -123
  284. package/dart/test/cluster_scale_test.dart +0 -122
  285. package/dart/test/compat_test.dart +0 -61
  286. package/dart/test/dlq_session_surface_test.dart +0 -222
  287. package/dart/test/first_app_gauntlet_test.dart +0 -386
  288. package/dart/test/fixtures/wedge/fake-install.mjs +0 -11
  289. package/dart/test/four_layer_nested_topology_test.dart +0 -290
  290. package/dart/test/home_parity_test.dart +0 -13
  291. package/dart/test/local_sync_inert_test.dart +0 -107
  292. package/dart/test/node_runner_transport_test.dart +0 -78
  293. package/dart/test/parent_signer_recovery_test.dart +0 -155
  294. package/dart/test/residency_refusal_test.dart +0 -49
  295. package/dart/test/sealing_client_test.dart +0 -181
  296. package/dart/test/session_bound_test.dart +0 -285
  297. package/dart/test/stale_bundle_test.dart +0 -80
  298. package/dart/test/vo_read_typing_test.dart +0 -131
  299. package/dart/test/watch_after_offer_stall_test.dart +0 -100
  300. package/dart/test/webview_bridge_test.dart +0 -1930
  301. package/dart/test/workspace_name_test.dart +0 -24
  302. package/runtime/generated-application-host.js +0 -22860
  303. package/src/codegen_query_usda.ts +0 -214
  304. package/src/compat_manifest.ts +0 -26
  305. package/src/compat_package_main.ts +0 -224
  306. package/src/shape_compat.ts +0 -352
  307. package/src/usd_state.ts +0 -505
@@ -5,7 +5,14 @@ import 'package:fixnum/fixnum.dart' as fixnum;
5
5
  import 'package:meta/meta.dart' show internal;
6
6
  import 'package:nomos_types/nomos_kernel_protocol.dart' as nomos_protocol;
7
7
  import 'package:nomos_types/nomos_types.dart'
8
- show NomosErrorInfo, nomosErrorFromInfo;
8
+ show
9
+ NomosErrorInfo,
10
+ NomosRefusalReason,
11
+ NomosRefused,
12
+ NomosRuntimeFailure,
13
+ NomosRuntimeFailureKind,
14
+ NomosUnavailable,
15
+ nomosErrorFromInfo;
9
16
 
10
17
  import 'runtime_v2_transport.dart';
11
18
 
@@ -16,12 +23,11 @@ final class NmsRuntimeV2Gateway {
16
23
  NmsRuntimeV2Gateway(this._transport);
17
24
 
18
25
  final NomosRuntimeV2Transport _transport;
19
- int _sequence = 0;
20
26
 
21
27
  Future<nomos_protocol.RuntimeResponse> invoke(
22
28
  nomos_protocol.RuntimeRequest request,
23
29
  ) async {
24
- final requestId = fixnum.Int64(++_sequence);
30
+ final requestId = fixnum.Int64(_transport.nextRuntimeRequestId());
25
31
  request
26
32
  ..protocolVersion = 2
27
33
  ..requestId = requestId;
@@ -36,8 +42,55 @@ final class NmsRuntimeV2Gateway {
36
42
  'Runtime v2 response ${response.requestId} belongs to another request',
37
43
  );
38
44
  }
45
+ if (response.hasUnavailable()) {
46
+ throw NomosUnavailable.fromRuntime(response.unavailable);
47
+ }
48
+ if (response.hasRefused()) {
49
+ final refused = response.refused;
50
+ throw NomosRefused(
51
+ reason: switch (refused.reason) {
52
+ nomos_protocol.RuntimeRefusalReason
53
+ .RUNTIME_REFUSAL_REASON_INVALID_CREDENTIAL =>
54
+ NomosRefusalReason.invalidCredential,
55
+ nomos_protocol
56
+ .RuntimeRefusalReason.RUNTIME_REFUSAL_REASON_ACCESS_DENIED =>
57
+ NomosRefusalReason.accessDenied,
58
+ nomos_protocol
59
+ .RuntimeRefusalReason.RUNTIME_REFUSAL_REASON_PLACEMENT_POLICY =>
60
+ NomosRefusalReason.placementPolicy,
61
+ nomos_protocol
62
+ .RuntimeRefusalReason.RUNTIME_REFUSAL_REASON_SESSION_UNBOUND =>
63
+ NomosRefusalReason.sessionUnbound,
64
+ _ => NomosRefusalReason.unknown,
65
+ },
66
+ workspace: refused.workspace,
67
+ retryable: refused.retryable,
68
+ detail: refused.detail,
69
+ );
70
+ }
39
71
  if (response.hasFailure()) {
40
72
  final failure = response.failure;
73
+ if (failure.kind !=
74
+ nomos_protocol.RuntimeFailureKind.RUNTIME_FAILURE_KIND_UNSPECIFIED) {
75
+ throw NomosRuntimeFailure(
76
+ kind: switch (failure.kind) {
77
+ nomos_protocol.RuntimeFailureKind.RUNTIME_FAILURE_KIND_INTERNAL =>
78
+ NomosRuntimeFailureKind.internal,
79
+ nomos_protocol.RuntimeFailureKind.RUNTIME_FAILURE_KIND_PROTOCOL =>
80
+ NomosRuntimeFailureKind.protocol,
81
+ nomos_protocol.RuntimeFailureKind.RUNTIME_FAILURE_KIND_INTEGRITY =>
82
+ NomosRuntimeFailureKind.integrity,
83
+ nomos_protocol
84
+ .RuntimeFailureKind.RUNTIME_FAILURE_KIND_UNSUPPORTED =>
85
+ NomosRuntimeFailureKind.unsupported,
86
+ _ => NomosRuntimeFailureKind.unknown,
87
+ },
88
+ code: failure.code,
89
+ message: failure.message,
90
+ component: failure.component,
91
+ workspace: failure.hasWorkspace() ? failure.workspace : null,
92
+ );
93
+ }
41
94
  throw nomosErrorFromInfo(
42
95
  NomosErrorInfo(
43
96
  code: failure.code,
@@ -2,16 +2,32 @@
2
2
  library;
3
3
 
4
4
  import 'dart:async';
5
+ import 'dart:convert';
6
+ import 'dart:developer' as developer;
7
+ import 'dart:typed_data';
5
8
 
6
9
  import 'package:fixnum/fixnum.dart' as fixnum;
7
10
  import 'package:meta/meta.dart' show internal;
11
+ import 'package:crypto/crypto.dart' show sha256;
12
+ import 'package:nomos_peer_host/nomos_peer_host.dart'
13
+ show
14
+ NomosDeviceIdentityStore,
15
+ NomosLawPackageSource,
16
+ NomosProviderCredentialStore,
17
+ NomosSignIn;
8
18
  import 'package:nomos_types/nomos_kernel_protocol.dart' as nomos_protocol;
19
+ import 'package:nomos_types/nomos_types.dart' show NomosUnavailable;
20
+
21
+ import 'console_enrolment.dart';
22
+ import 'console_http_stub.dart' if (dart.library.io) 'console_http_io.dart'
23
+ as console_http;
9
24
 
10
25
  /// Binary generated-message path from an application host to the kernel.
11
26
  abstract interface class NomosKernelCallTransport {
12
27
  Future<nomos_protocol.CallResponse> callKernel({
13
28
  required String session,
14
29
  required nomos_protocol.CallRequest request,
30
+ required nomos_protocol.CredentialUse credentialUse,
15
31
  });
16
32
  }
17
33
 
@@ -21,6 +37,12 @@ abstract interface class NomosRuntimeV2Transport {
21
37
  Future<nomos_protocol.RuntimeResponse> invokeRuntime(
22
38
  nomos_protocol.RuntimeRequest request,
23
39
  );
40
+
41
+ /// ONE request-id space per transport. Every caller that frames a Runtime v2 request over this
42
+ /// transport (the reception's gateway, direct kernel calls, the native plane) must draw its id
43
+ /// here; two independent counters on one transport collided in flight ("request N is already
44
+ /// pending") the moment a child workspace session opened beside its parent.
45
+ int nextRuntimeRequestId();
24
46
  }
25
47
 
26
48
  /// Generated asynchronous runtime event lane. It is separate from invocation
@@ -29,10 +51,519 @@ abstract interface class NomosRuntimeV2EventTransport {
29
51
  Stream<nomos_protocol.RuntimeEvent> get runtimeEvents;
30
52
  }
31
53
 
54
+ /// What a genuine cloud acquisition needs when the peer reports that its live
55
+ /// identity proof is no longer usable. The platform credential provider owns
56
+ /// how this is obtained; generated clients and Reception only receive the
57
+ /// refreshed proof selected for a runtime session.
58
+ final class NomosSessionCredentials {
59
+ const NomosSessionCredentials({
60
+ required this.cloud,
61
+ this.authToken,
62
+ this.authorSecret,
63
+ });
64
+
65
+ final String cloud;
66
+ final String? authToken;
67
+ final String? authorSecret;
68
+ }
69
+
70
+ /// Platform-shell capability supplying renewable identity proof to a peer.
71
+ ///
72
+ /// This is deliberately not part of a generated application client and it is
73
+ /// not workspace authority. Firebase, an OS account provider, or another
74
+ /// platform service may produce a token; the kernel still decides whether the
75
+ /// proof is admissible. A child session aliases the same capability rather
76
+ /// than copying either its callback or its current token.
77
+ abstract interface class NomosPlatformCredentialProviders {
78
+ void registerCredentialProvider({
79
+ required String capability,
80
+ required String cloud,
81
+ required NomosSignIn signIn,
82
+ });
83
+
84
+ Future<NomosSessionCredentials> Function({String? deviceChallenge})?
85
+ credentialProviderForCapability(String capability);
86
+
87
+ /// The shell's durable device identity store, answering the runtime's
88
+ /// load/save effect. A shell that registers none refuses that effect typed.
89
+ void registerDeviceIdentityStore(NomosDeviceIdentityStore store);
90
+
91
+ /// Register the platform's durable ID-token cache. Native shells use
92
+ /// Keychain/Keystore; a shell without one simply asks its provider.
93
+ void registerProviderCredentialStore(NomosProviderCredentialStore store);
94
+
95
+ /// The application's compiled law package by identity, answering the
96
+ /// runtime's law-package effect when a workspace must stage the package it
97
+ /// was released against. A shell that registers none refuses that effect
98
+ /// typed; the runtime reports the adoption deferred.
99
+ void registerLawPackageSource(NomosLawPackageSource source);
100
+
101
+ /// Replace the shell's console transport (D22 enrolment). Native shells
102
+ /// default to `dart:io`; a shell without it must register one.
103
+ void registerConsolePost(NomosConsolePost post);
104
+ }
105
+
106
+ final class _NmsPlatformCredentialProvider {
107
+ _NmsPlatformCredentialProvider({required this.cloud, required this.signIn});
108
+
109
+ final String cloud;
110
+ final NomosSignIn signIn;
111
+
112
+ /// One flight per challenge: a burst of refreshes for the same proof shares
113
+ /// one provider call, while a device-bound request never rides a plain one.
114
+ final Map<String, Future<String>> _refreshFlights = {};
115
+
116
+ Future<NomosSessionCredentials> credentials({String? deviceChallenge}) async {
117
+ final key = deviceChallenge ?? '';
118
+ final token =
119
+ await (_refreshFlights[key] ?? _startRefresh(key, deviceChallenge));
120
+ return NomosSessionCredentials(cloud: cloud, authToken: token);
121
+ }
122
+
123
+ Future<String> _startRefresh(String key, String? deviceChallenge) {
124
+ final flight = () async {
125
+ final token =
126
+ (await signIn(deviceChallenge: deviceChallenge))?.trim() ?? '';
127
+ if (token.isEmpty) {
128
+ throw StateError(
129
+ 'Nomos could not refresh the signed-in identity for this peer session',
130
+ );
131
+ }
132
+ return token;
133
+ }();
134
+ _refreshFlights[key] = flight;
135
+ return flight.whenComplete(() {
136
+ if (identical(_refreshFlights[key], flight)) _refreshFlights.remove(key);
137
+ });
138
+ }
139
+ }
140
+
141
+ /// Reusable platform-shell implementation. Concrete native, WebView, browser
142
+ /// and Node connections mix this in; Reception merely uses the capability.
143
+ /// The map is keyed by the opaque capability identity issued by Runtime v2.
144
+ /// Session-to-capability ownership remains exclusively in the peer runtime;
145
+ /// this platform adapter therefore never mirrors or inherits session state.
146
+ mixin NmsPlatformCredentialProviderHost
147
+ implements NomosPlatformCredentialProviders {
148
+ final Map<String, _NmsPlatformCredentialProvider>
149
+ _nmsCredentialProvidersByCapability = {};
150
+ NomosDeviceIdentityStore? _nmsDeviceIdentityStore;
151
+ NomosProviderCredentialStore? _nmsProviderCredentialStore;
152
+
153
+ @override
154
+ void registerCredentialProvider({
155
+ required String capability,
156
+ required String cloud,
157
+ required NomosSignIn signIn,
158
+ }) {
159
+ _nmsCredentialProvidersByCapability[capability] =
160
+ _NmsPlatformCredentialProvider(cloud: cloud, signIn: signIn);
161
+ }
162
+
163
+ @override
164
+ Future<NomosSessionCredentials> Function({String? deviceChallenge})?
165
+ credentialProviderForCapability(String capability) {
166
+ final provider = _nmsCredentialProvidersByCapability[capability];
167
+ return provider == null ? null : provider.credentials;
168
+ }
169
+
170
+ @override
171
+ void registerDeviceIdentityStore(NomosDeviceIdentityStore store) {
172
+ _nmsDeviceIdentityStore = store;
173
+ }
174
+
175
+ @override
176
+ void registerProviderCredentialStore(NomosProviderCredentialStore store) {
177
+ _nmsProviderCredentialStore = store;
178
+ }
179
+
180
+ NomosLawPackageSource? _nmsLawPackageSource;
181
+
182
+ @override
183
+ void registerLawPackageSource(NomosLawPackageSource source) {
184
+ _nmsLawPackageSource = source;
185
+ }
186
+
187
+ NomosConsolePost _nmsConsolePost = console_http.nomosDefaultConsolePost;
188
+
189
+ @override
190
+ void registerConsolePost(NomosConsolePost post) {
191
+ _nmsConsolePost = post;
192
+ }
193
+
194
+ /// D22/D23: enrol this device's key with the application's console enroller.
195
+ /// Peer Office decides when and how often; this performs one attempt. The
196
+ /// provider is asked for a FRESH plain identity token (no device challenge,
197
+ /// never the durable cache: the console spends each token once), the request
198
+ /// is signed with the device secret this shell's identity store holds, and
199
+ /// the token goes only to the console. Neither the token nor the secret is
200
+ /// returned to the runtime.
201
+ Future<nomos_protocol.PeerEffectReceipt> performConsoleEnrolment(
202
+ nomos_protocol.PeerConsoleEnrolmentEffect effect,
203
+ ) async {
204
+ nomos_protocol.PeerEffectReceipt failed(String message) =>
205
+ nomos_protocol.PeerEffectReceipt(
206
+ effectId: effect.effectId,
207
+ failure: nomos_protocol.PeerEffectFailure(
208
+ reason: nomos_protocol
209
+ .PeerEffectFailureReason.PEER_EFFECT_FAILURE_REASON_TRANSPORT,
210
+ message: message,
211
+ ),
212
+ );
213
+ nomos_protocol.PeerEffectReceipt refused(int status, String message,
214
+ {String code = '', bool? retryable}) =>
215
+ nomos_protocol.PeerEffectReceipt(
216
+ effectId: effect.effectId,
217
+ consoleEnrolmentRefused: nomos_protocol.PeerConsoleEnrolmentRefused(
218
+ status: status,
219
+ message: message,
220
+ code: code,
221
+ retryable: retryable,
222
+ ),
223
+ );
224
+ final store = _nmsDeviceIdentityStore;
225
+ if (store == null) {
226
+ return failed(
227
+ 'this platform shell keeps no durable device identity store');
228
+ }
229
+ final provider = _nmsCredentialProvidersByCapability[effect.capability];
230
+ if (provider == null) {
231
+ return failed(
232
+ 'this platform shell has no identity provider for the session');
233
+ }
234
+ final Uint8List? secret;
235
+ try {
236
+ secret = await store.load(effect.deviceIdentity);
237
+ } catch (_) {
238
+ return failed(
239
+ 'the platform device identity store could not load the device identity');
240
+ }
241
+ if (secret == null || secret.length != 32) {
242
+ return failed('this device holds no usable identity to enrol');
243
+ }
244
+ final String token;
245
+ try {
246
+ token = (await provider.signIn(deviceChallenge: null))?.trim() ?? '';
247
+ } catch (_) {
248
+ return failed('the identity provider could not sign this person in');
249
+ }
250
+ if (token.isEmpty) {
251
+ return failed('the identity provider returned no identity token');
252
+ }
253
+ try {
254
+ final enrolled = await enrolWithConsole(
255
+ console: effect.console,
256
+ tenant: effect.tenant,
257
+ scope: effect.scope,
258
+ providerToken: token,
259
+ device: NomosConsoleEnrolmentDevice.fromSeed(secret),
260
+ workspaces: effect.workspaces,
261
+ post: _nmsConsolePost,
262
+ );
263
+ // The console's fan-out into the person's other workspaces: what it
264
+ // could not enrol stays visible (those workspaces refuse this device's
265
+ // writes until an enrolment lands there).
266
+ for (final entry in enrolled.refused.entries) {
267
+ developer.log(
268
+ 'console enrolment into ${entry.key} was refused: ${entry.value}',
269
+ name: 'nomos.console_enrolment');
270
+ }
271
+ return nomos_protocol.PeerEffectReceipt(
272
+ effectId: effect.effectId,
273
+ consoleEnrolled: nomos_protocol.PeerConsoleEnrolled(
274
+ principal: enrolled.principal,
275
+ keyHash: enrolled.keyHash,
276
+ heads: enrolled.workspaces.entries,
277
+ ),
278
+ );
279
+ } on NomosConsoleEnrolmentRefused catch (refusal) {
280
+ return refused(refusal.status, refusal.message,
281
+ code: refusal.code, retryable: refusal.retryable);
282
+ } catch (error) {
283
+ // Never echo the request: it carries the token.
284
+ return failed('the console could not be reached (${error.runtimeType})');
285
+ }
286
+ }
287
+
288
+ /// Hand the runtime the application's law package for the identity it
289
+ /// names. The runtime decides when a package is needed (a workspace that
290
+ /// must stage the approved model); this only performs the physical read.
291
+ /// A shell without a source, or without that package, answers typed.
292
+ Future<nomos_protocol.PeerEffectReceipt> performLawPackageEffect(
293
+ nomos_protocol.PeerLawPackageEffect effect,
294
+ ) async {
295
+ nomos_protocol.PeerEffectReceipt refused(String message) =>
296
+ nomos_protocol.PeerEffectReceipt(
297
+ effectId: effect.effectId,
298
+ failure: nomos_protocol.PeerEffectFailure(
299
+ reason: nomos_protocol
300
+ .PeerEffectFailureReason.PEER_EFFECT_FAILURE_REASON_TRANSPORT,
301
+ message: message,
302
+ ),
303
+ );
304
+ final source = _nmsLawPackageSource;
305
+ if (source == null) {
306
+ return refused(
307
+ 'this platform shell supplies no application law packages');
308
+ }
309
+ if (!RegExp(r'^[0-9a-f]{64}$').hasMatch(effect.lawSha256)) {
310
+ return refused('law package effect names no package identity');
311
+ }
312
+ try {
313
+ final bytes = await source.load(effect.lawSha256);
314
+ if (bytes == null || bytes.isEmpty) {
315
+ return refused(
316
+ 'this application holds no law package with identity ${effect.lawSha256}');
317
+ }
318
+ return nomos_protocol.PeerEffectReceipt(
319
+ effectId: effect.effectId,
320
+ lawPackage: nomos_protocol.PeerLawPackage(lawUsdz: bytes),
321
+ );
322
+ } catch (error) {
323
+ return refused('the application law package could not be read: $error');
324
+ }
325
+ }
326
+
327
+ /// Execute the opaque platform capability requested by Peer Office. The
328
+ /// runtime owns the decision to refresh (a refusal, or renewal ahead of
329
+ /// expiry), the device challenge it carries, and any subsequent offer retry.
330
+ Future<nomos_protocol.PeerEffectReceipt> performCredentialRefresh(
331
+ nomos_protocol.PeerCredentialRefreshEffect effect,
332
+ ) async {
333
+ try {
334
+ final registered = _nmsCredentialProvidersByCapability[effect.capability];
335
+ final provider = registered?.credentials;
336
+ final challenge =
337
+ effect.hasDeviceChallenge() ? effect.deviceChallenge : null;
338
+ final entry = registered == null
339
+ ? null
340
+ : sha256
341
+ .convert(utf8.encode(
342
+ 'nomos-provider-credential-v1\u0000${registered.cloud}'
343
+ '\u0000${effect.capability}\u0000${challenge ?? ''}',
344
+ ))
345
+ .toString();
346
+ String? token;
347
+ final durable = _nmsProviderCredentialStore;
348
+ if (effect.allowCached && durable != null && entry != null) {
349
+ try {
350
+ final rejected = effect.hasRejectedAuthTokenSha256()
351
+ ? effect.rejectedAuthTokenSha256
352
+ : const <int>[];
353
+ for (final candidate in await durable.loadCandidates(entry)) {
354
+ final value = candidate.trim();
355
+ if (value.isEmpty) continue;
356
+ final digest = sha256.convert(utf8.encode(value)).bytes;
357
+ if (rejected.length == digest.length) {
358
+ var same = true;
359
+ for (var i = 0; i < digest.length; i += 1) {
360
+ if (rejected[i] != digest[i]) {
361
+ same = false;
362
+ break;
363
+ }
364
+ }
365
+ if (same) continue;
366
+ }
367
+ token = value;
368
+ break;
369
+ }
370
+ } catch (_) {
371
+ // A damaged/unavailable cache never blocks the provider. The store
372
+ // exposes no token material through diagnostics.
373
+ }
374
+ }
375
+ NomosSessionCredentials? credentials;
376
+ if (token == null && provider != null) {
377
+ credentials = await provider(deviceChallenge: challenge);
378
+ token = credentials.authToken?.trim();
379
+ if (token != null &&
380
+ token.isNotEmpty &&
381
+ durable != null &&
382
+ entry != null) {
383
+ try {
384
+ await durable.retainCandidate(entry, token);
385
+ } catch (_) {
386
+ // The fresh proof remains usable for this process even when its
387
+ // durable cache cannot be updated.
388
+ }
389
+ }
390
+ }
391
+ final secret = credentials?.authorSecret?.trim();
392
+ return nomos_protocol.PeerEffectReceipt(
393
+ effectId: effect.effectId,
394
+ credentialRefreshed: nomos_protocol.PeerCredentialRefreshed(
395
+ authToken: token == null || token.isEmpty ? null : utf8.encode(token),
396
+ authorSecret:
397
+ secret == null || secret.isEmpty ? null : base64Decode(secret),
398
+ ),
399
+ );
400
+ } catch (_) {
401
+ return nomos_protocol.PeerEffectReceipt(
402
+ effectId: effect.effectId,
403
+ failure: nomos_protocol.PeerEffectFailure(
404
+ reason: nomos_protocol
405
+ .PeerEffectFailureReason.PEER_EFFECT_FAILURE_REASON_TRANSPORT,
406
+ message:
407
+ 'Platform credential provider could not refresh identity proof',
408
+ ),
409
+ );
410
+ }
411
+ }
412
+
413
+ /// Move one identity's secret between the runtime and this shell's durable
414
+ /// store. The runtime owns the sequence (load before the first signed offer,
415
+ /// save once a secret exists); this only performs the named physical effect.
416
+ /// A shell without a store answers typed; it never invents a secret.
417
+ Future<nomos_protocol.PeerEffectReceipt> performDeviceIdentityEffect(
418
+ nomos_protocol.PeerDeviceIdentityEffect effect,
419
+ ) async {
420
+ final store = _nmsDeviceIdentityStore;
421
+ nomos_protocol.PeerEffectReceipt refused(String message) =>
422
+ nomos_protocol.PeerEffectReceipt(
423
+ effectId: effect.effectId,
424
+ failure: nomos_protocol.PeerEffectFailure(
425
+ reason: nomos_protocol
426
+ .PeerEffectFailureReason.PEER_EFFECT_FAILURE_REASON_TRANSPORT,
427
+ message: message,
428
+ ),
429
+ );
430
+ if (store == null) {
431
+ return refused(
432
+ 'this platform shell keeps no durable device identity store');
433
+ }
434
+ if (effect.identity.isEmpty) {
435
+ return refused('device identity effect names no identity');
436
+ }
437
+ try {
438
+ Uint8List? secret;
439
+ nomos_protocol.DeviceRecoveryRecord? recovery;
440
+ if (effect.hasLoad()) {
441
+ secret = await store.load(effect.identity);
442
+ // Dart's protobuf bytes field elides empty values. Refuse corruption
443
+ // here so it cannot cross the wire disguised as an absent identity.
444
+ if (secret != null && secret.length != 32) {
445
+ return refused('the stored device identity is malformed');
446
+ }
447
+ } else if (effect.hasSave()) {
448
+ await store.save(
449
+ effect.identity, Uint8List.fromList(effect.save.secret));
450
+ } else if (effect.hasLoadRecovery()) {
451
+ final bytes =
452
+ await store.load('nomos-device-recovery-v1|${effect.identity}');
453
+ if (bytes != null) {
454
+ recovery = nomos_protocol.DeviceRecoveryRecord.fromBuffer(bytes);
455
+ }
456
+ } else if (effect.hasSaveRecovery()) {
457
+ if (!effect.saveRecovery.hasRecord()) {
458
+ return refused('device recovery save contains no record');
459
+ }
460
+ await store.save('nomos-device-recovery-v1|${effect.identity}',
461
+ effect.saveRecovery.record.writeToBuffer());
462
+ } else {
463
+ return refused('device identity effect has no operation');
464
+ }
465
+ return nomos_protocol.PeerEffectReceipt(
466
+ effectId: effect.effectId,
467
+ deviceIdentity: nomos_protocol.PeerDeviceIdentity(
468
+ secret: secret,
469
+ recovery: recovery,
470
+ ),
471
+ );
472
+ } catch (_) {
473
+ // Never print store exceptions: they may name the key path.
474
+ return refused(
475
+ 'the platform device identity store could not ${effect.hasSave() ? 'save' : 'load'} the device identity');
476
+ }
477
+ }
478
+
479
+ Future<nomos_protocol.RuntimeResponse> driveCredentialEffects(
480
+ nomos_protocol.RuntimeRequest request,
481
+ Future<nomos_protocol.RuntimeResponse> Function(
482
+ nomos_protocol.RuntimeRequest)
483
+ invokeFrame,
484
+ ) async {
485
+ var next = request;
486
+ for (;;) {
487
+ final response = await invokeFrame(next);
488
+ if (response.requestId != request.requestId ||
489
+ response.protocolVersion != 2) {
490
+ throw StateError(
491
+ 'Credential effect response belongs to another runtime request');
492
+ }
493
+ if (!response.hasCorrespondencePending() ||
494
+ (!response.correspondencePending.hasRefreshCredential() &&
495
+ !response.correspondencePending.hasDeviceIdentity() &&
496
+ !response.correspondencePending.hasLawPackage() &&
497
+ !response.correspondencePending.hasConsoleEnrolment() &&
498
+ !response.correspondencePending.hasWake())) return response;
499
+ final pending = response.correspondencePending;
500
+ final nomos_protocol.PeerEffectReceipt receipt;
501
+ if (pending.hasDeviceIdentity()) {
502
+ receipt = await performDeviceIdentityEffect(pending.deviceIdentity);
503
+ } else if (pending.hasLawPackage()) {
504
+ receipt = await performLawPackageEffect(pending.lawPackage);
505
+ } else if (pending.hasConsoleEnrolment()) {
506
+ receipt = await performConsoleEnrolment(pending.consoleEnrolment);
507
+ } else if (pending.hasWake()) {
508
+ // The shared runtime chooses the delay and checks its own clock.
509
+ // Waiting outside the serial Node frame lane keeps it available for
510
+ // credential-provider calls while authentication is pending.
511
+ await Future<void>.delayed(
512
+ Duration(milliseconds: pending.wake.delayMillis));
513
+ receipt = nomos_protocol.PeerEffectReceipt(
514
+ effectId: pending.wake.effectId,
515
+ woken: nomos_protocol.PeerWoken(),
516
+ );
517
+ } else {
518
+ receipt = await performCredentialRefresh(pending.refreshCredential);
519
+ }
520
+ next = nomos_protocol.RuntimeRequest(
521
+ protocolVersion: 2,
522
+ requestId: request.requestId,
523
+ continueCorrespondence: nomos_protocol.ContinueCorrespondenceRequest(
524
+ workId: pending.workId,
525
+ receipt: receipt,
526
+ ),
527
+ );
528
+ }
529
+ }
530
+
531
+ bool consumeCredentialRefreshEvent(
532
+ nomos_protocol.RuntimeEvent event,
533
+ NomosRuntimeV2Transport transport,
534
+ void Function(Object, StackTrace) onError,
535
+ ) {
536
+ if (!event.hasCredentialRefreshRequested()) return false;
537
+ final pending = event.credentialRefreshRequested.pending;
538
+ if (!pending.hasRefreshCredential()) {
539
+ throw StateError('Credential refresh event omitted its physical effect');
540
+ }
541
+ Future<void> complete() async {
542
+ final receipt = await performCredentialRefresh(pending.refreshCredential);
543
+ final response =
544
+ await transport.invokeRuntime(nomos_protocol.RuntimeRequest(
545
+ protocolVersion: 2,
546
+ requestId: fixnum.Int64(transport.nextRuntimeRequestId()),
547
+ continueCorrespondence: nomos_protocol.ContinueCorrespondenceRequest(
548
+ workId: pending.workId,
549
+ receipt: receipt,
550
+ ),
551
+ ));
552
+ if (response.hasFailure()) {
553
+ throw StateError(
554
+ '${response.failure.code}: ${response.failure.message}');
555
+ }
556
+ }
557
+
558
+ unawaited(complete().catchError(onError));
559
+ return true;
560
+ }
561
+ }
562
+
32
563
  /// A level-triggered generated event stream. Runtime readiness can arrive while
33
- /// a WebView script is loading, before [NomosBridge] exists; a broadcast stream
564
+ /// a WebView script is loading, before Reception exists; a broadcast stream
34
565
  /// would silently discard it and turn a successful boot into an infinite wait.
35
- /// Buffering ends when the first bridge subscribes, after which events remain a
566
+ /// Buffering ends when Reception first subscribes, after which events remain a
36
567
  /// normal broadcast stream.
37
568
  final class NmsBufferedRuntimeEvents {
38
569
  NmsBufferedRuntimeEvents() {
@@ -83,12 +614,16 @@ mixin NomosRuntimeV2KernelCalls
83
614
  implements NomosRuntimeV2Transport, NomosKernelCallTransport {
84
615
  int _nmsRuntimeRequestSequence = 0;
85
616
 
617
+ @override
618
+ int nextRuntimeRequestId() => ++_nmsRuntimeRequestSequence;
619
+
86
620
  @override
87
621
  Future<nomos_protocol.CallResponse> callKernel({
88
622
  required String session,
89
623
  required nomos_protocol.CallRequest request,
624
+ required nomos_protocol.CredentialUse credentialUse,
90
625
  }) async {
91
- final requestId = fixnum.Int64(++_nmsRuntimeRequestSequence);
626
+ final requestId = fixnum.Int64(nextRuntimeRequestId());
92
627
  final response = await invokeRuntime(
93
628
  nomos_protocol.RuntimeRequest(
94
629
  protocolVersion: 2,
@@ -96,6 +631,7 @@ mixin NomosRuntimeV2KernelCalls
96
631
  kernelCall: nomos_protocol.KernelCallRequest(
97
632
  session: session,
98
633
  request: request,
634
+ credentialUse: credentialUse,
99
635
  ),
100
636
  ),
101
637
  );
@@ -107,6 +643,11 @@ mixin NomosRuntimeV2KernelCalls
107
643
  if (response.hasFailure()) {
108
644
  throw StateError('${response.failure.code}: ${response.failure.message}');
109
645
  }
646
+ // Typed, as on the native transport: a copy still being fetched, opened or indexed is a
647
+ // retryable wait that reads sit out (replica_readiness.dart), never a bare error.
648
+ if (response.hasUnavailable()) {
649
+ throw NomosUnavailable.fromRuntime(response.unavailable);
650
+ }
110
651
  if (!response.hasKernelCall()) {
111
652
  throw StateError(
112
653
  'Runtime v2 returned ${response.whichOutcome().name} for a kernel call',