@githolon/dsl 0.107.0 → 0.107.2
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.
- package/dart/CHANGELOG.md +38 -0
- package/dart/lib/src/runner/node-entry.mjs +6329 -5902
- package/dart/lib/src/runner/offline-install.mjs +393 -149
- package/dart/lib/src/webview_bridge.dart +662 -502
- package/dart/pubspec.yaml +1 -1
- package/dart/test/webview_bridge_test.dart +39 -2
- package/package.json +1 -1
- package/runtime/generated-application-host.js +6329 -5902
- package/src/compile_package_main.ts +2 -2
- package/src/framework/capability.ts +9 -3
- package/src/framework/domain_lifecycle.ts +8 -60
- package/src/framework/governance_relations.ts +10 -0
- package/src/framework/workspaces.ts +2 -1
- package/src/role.ts +13 -7
|
@@ -104,56 +104,62 @@ final class NomosBirthCertificate {
|
|
|
104
104
|
Map<String, Object?> wire,
|
|
105
105
|
String camel,
|
|
106
106
|
String snake,
|
|
107
|
-
) =>
|
|
107
|
+
) =>
|
|
108
|
+
(wire[camel] ?? wire[snake] ?? '').toString();
|
|
108
109
|
|
|
109
110
|
static List<int> _bytes(
|
|
110
111
|
Map<String, Object?> wire,
|
|
111
112
|
String camel,
|
|
112
113
|
String snake,
|
|
113
|
-
) =>
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
) =>
|
|
115
|
+
((wire[camel] ?? wire[snake]) as List? ?? const <Object?>[])
|
|
116
|
+
.map((value) => (value as num).toInt())
|
|
117
|
+
.toList(growable: false);
|
|
116
118
|
|
|
117
119
|
factory NomosBirthCertificate.fromWire(
|
|
118
120
|
Map<String, Object?> wire,
|
|
119
|
-
) =>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
121
|
+
) =>
|
|
122
|
+
NomosBirthCertificate(
|
|
123
|
+
child: _string(wire, 'child', 'child'),
|
|
124
|
+
founder: _string(wire, 'founder', 'founder'),
|
|
125
|
+
verified: wire['verified'] == true,
|
|
126
|
+
parentKey: _bytes(wire, 'parentKey', 'parent_key'),
|
|
127
|
+
parentId: _string(wire, 'parentId', 'parent_id'),
|
|
128
|
+
epoch: (wire['epoch'] as num?)?.toInt() ?? 0,
|
|
129
|
+
signature: _bytes(wire, 'signature', 'signature'),
|
|
130
|
+
verifiedVia: (wire['verifiedVia'] ?? wire['verified_via'])?.toString(),
|
|
131
|
+
idpToken: (wire['idpToken'] ?? wire['idp_token'])?.toString(),
|
|
132
|
+
capturedClock:
|
|
133
|
+
((wire['capturedClock'] ?? wire['captured_clock']) as num?)
|
|
134
|
+
?.toInt(),
|
|
135
|
+
authProviderIssuer:
|
|
136
|
+
(wire['authProviderIssuer'] ?? wire['auth_provider_issuer'])
|
|
137
|
+
?.toString(),
|
|
138
|
+
authProviderJwks:
|
|
139
|
+
(wire['authProviderJwks'] ?? wire['auth_provider_jwks'])
|
|
140
|
+
?.toString(),
|
|
141
|
+
authProviderAudience:
|
|
142
|
+
(wire['authProviderAudience'] ?? wire['auth_provider_audience'])
|
|
143
|
+
?.toString(),
|
|
144
|
+
);
|
|
140
145
|
|
|
141
146
|
Map<String, Object?> toWire() => <String, Object?>{
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
147
|
+
'child': child,
|
|
148
|
+
'founder': founder,
|
|
149
|
+
'verified': verified,
|
|
150
|
+
'parentKey': parentKey,
|
|
151
|
+
'parentId': parentId,
|
|
152
|
+
'epoch': epoch,
|
|
153
|
+
'signature': signature,
|
|
154
|
+
if (verifiedVia != null) 'verifiedVia': verifiedVia,
|
|
155
|
+
if (idpToken != null) 'idpToken': idpToken,
|
|
156
|
+
if (capturedClock != null) 'capturedClock': capturedClock,
|
|
157
|
+
if (authProviderIssuer != null)
|
|
158
|
+
'authProviderIssuer': authProviderIssuer,
|
|
159
|
+
if (authProviderJwks != null) 'authProviderJwks': authProviderJwks,
|
|
160
|
+
if (authProviderAudience != null)
|
|
161
|
+
'authProviderAudience': authProviderAudience,
|
|
162
|
+
};
|
|
157
163
|
}
|
|
158
164
|
|
|
159
165
|
/// Facts signed by [NomosBridge.signBirthCert]. The secret remains a separate argument and never
|
|
@@ -188,20 +194,21 @@ final class NomosBirthCertificateSpec {
|
|
|
188
194
|
final String? authProviderAudience;
|
|
189
195
|
|
|
190
196
|
Map<String, Object?> toWire() => <String, Object?>{
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
197
|
+
'child': child,
|
|
198
|
+
'founder': founder,
|
|
199
|
+
'verified': verified,
|
|
200
|
+
'parentKey': parentKey,
|
|
201
|
+
'parentId': parentId,
|
|
202
|
+
'epoch': epoch,
|
|
203
|
+
if (verifiedVia != null) 'verifiedVia': verifiedVia,
|
|
204
|
+
if (idpToken != null) 'idpToken': idpToken,
|
|
205
|
+
if (capturedClock != null) 'capturedClock': capturedClock,
|
|
206
|
+
if (authProviderIssuer != null)
|
|
207
|
+
'authProviderIssuer': authProviderIssuer,
|
|
208
|
+
if (authProviderJwks != null) 'authProviderJwks': authProviderJwks,
|
|
209
|
+
if (authProviderAudience != null)
|
|
210
|
+
'authProviderAudience': authProviderAudience,
|
|
211
|
+
};
|
|
205
212
|
}
|
|
206
213
|
|
|
207
214
|
/// The protocol version the runner and the bridge agree on (`webview-runner.mjs` `WEBVIEW_BRIDGE_VERSION`).
|
|
@@ -268,6 +275,119 @@ class TransferProgress {
|
|
|
268
275
|
);
|
|
269
276
|
}
|
|
270
277
|
|
|
278
|
+
enum KernelProgressOperation { unspecified, restoreMain }
|
|
279
|
+
|
|
280
|
+
enum KernelProgressPhase {
|
|
281
|
+
unspecified,
|
|
282
|
+
checkCustody,
|
|
283
|
+
readChain,
|
|
284
|
+
initializeEngine,
|
|
285
|
+
readTimeline,
|
|
286
|
+
hydrateLaw,
|
|
287
|
+
recoveryPrescan,
|
|
288
|
+
verifyIntents,
|
|
289
|
+
promoteCustody,
|
|
290
|
+
rebuildFrontier,
|
|
291
|
+
complete,
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
enum KernelProgressUnit { unspecified, intent, lawPackage }
|
|
295
|
+
|
|
296
|
+
enum KernelLawHydrationStep {
|
|
297
|
+
unspecified,
|
|
298
|
+
readClosure,
|
|
299
|
+
composeStage,
|
|
300
|
+
deriveSchemas,
|
|
301
|
+
loadPolicyShell,
|
|
302
|
+
packageComplete,
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/// Typed, authority-free progress from a long-running kernel operation.
|
|
306
|
+
///
|
|
307
|
+
/// Unlike a spinner, [fraction] is derived from work the canonical kernel has
|
|
308
|
+
/// actually completed. This is ephemeral execution telemetry: it cannot enter
|
|
309
|
+
/// custody or authorize an effect.
|
|
310
|
+
final class KernelProgress {
|
|
311
|
+
const KernelProgress({
|
|
312
|
+
required this.sequence,
|
|
313
|
+
required this.operation,
|
|
314
|
+
required this.phase,
|
|
315
|
+
required this.completed,
|
|
316
|
+
required this.total,
|
|
317
|
+
required this.unit,
|
|
318
|
+
required this.terminal,
|
|
319
|
+
required this.lawHydrationStep,
|
|
320
|
+
required this.lawPackageSha256,
|
|
321
|
+
required this.hostObservedElapsedMicros,
|
|
322
|
+
required this.hostObservedPhaseElapsedMicros,
|
|
323
|
+
required this.kernelAllocatorLiveBytes,
|
|
324
|
+
required this.kernelAllocatorPeakBytes,
|
|
325
|
+
required this.processResidentBytes,
|
|
326
|
+
required this.processPeakResidentBytes,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
final int sequence;
|
|
330
|
+
final KernelProgressOperation operation;
|
|
331
|
+
final KernelProgressPhase phase;
|
|
332
|
+
final int completed;
|
|
333
|
+
final int total;
|
|
334
|
+
final KernelProgressUnit unit;
|
|
335
|
+
final bool terminal;
|
|
336
|
+
final KernelLawHydrationStep lawHydrationStep;
|
|
337
|
+
final String lawPackageSha256;
|
|
338
|
+
final int hostObservedElapsedMicros;
|
|
339
|
+
final int hostObservedPhaseElapsedMicros;
|
|
340
|
+
final int kernelAllocatorLiveBytes;
|
|
341
|
+
final int kernelAllocatorPeakBytes;
|
|
342
|
+
final int processResidentBytes;
|
|
343
|
+
final int processPeakResidentBytes;
|
|
344
|
+
|
|
345
|
+
int get unaccountedResidentBytes {
|
|
346
|
+
final remainder = processResidentBytes - kernelAllocatorLiveBytes;
|
|
347
|
+
return remainder > 0 ? remainder : 0;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
double? get fraction =>
|
|
351
|
+
(total <= 0) ? null : (completed / total).clamp(0.0, 1.0);
|
|
352
|
+
|
|
353
|
+
factory KernelProgress.fromMessage(Map<String, Object?> message) =>
|
|
354
|
+
KernelProgress(
|
|
355
|
+
sequence: _wireInteger(message['sequence']) ?? 0,
|
|
356
|
+
operation: KernelProgressOperation.values.firstWhere(
|
|
357
|
+
(value) => value.name == message['operation'],
|
|
358
|
+
orElse: () => KernelProgressOperation.unspecified,
|
|
359
|
+
),
|
|
360
|
+
phase: KernelProgressPhase.values.firstWhere(
|
|
361
|
+
(value) => value.name == message['phase'],
|
|
362
|
+
orElse: () => KernelProgressPhase.unspecified,
|
|
363
|
+
),
|
|
364
|
+
completed: _wireInteger(message['completed']) ?? 0,
|
|
365
|
+
total: _wireInteger(message['total']) ?? 0,
|
|
366
|
+
unit: KernelProgressUnit.values.firstWhere(
|
|
367
|
+
(value) => value.name == message['unit'],
|
|
368
|
+
orElse: () => KernelProgressUnit.unspecified,
|
|
369
|
+
),
|
|
370
|
+
terminal: message['terminal'] == true,
|
|
371
|
+
lawHydrationStep: KernelLawHydrationStep.values.firstWhere(
|
|
372
|
+
(value) => value.name == message['lawHydrationStep'],
|
|
373
|
+
orElse: () => KernelLawHydrationStep.unspecified,
|
|
374
|
+
),
|
|
375
|
+
lawPackageSha256: message['lawPackageSha256']?.toString() ?? '',
|
|
376
|
+
hostObservedElapsedMicros:
|
|
377
|
+
_wireInteger(message['hostObservedElapsedMicros']) ?? 0,
|
|
378
|
+
hostObservedPhaseElapsedMicros:
|
|
379
|
+
_wireInteger(message['hostObservedPhaseElapsedMicros']) ?? 0,
|
|
380
|
+
kernelAllocatorLiveBytes:
|
|
381
|
+
_wireInteger(message['kernelAllocatorLiveBytes']) ?? 0,
|
|
382
|
+
kernelAllocatorPeakBytes:
|
|
383
|
+
_wireInteger(message['kernelAllocatorPeakBytes']) ?? 0,
|
|
384
|
+
processResidentBytes:
|
|
385
|
+
_wireInteger(message['processResidentBytes']) ?? 0,
|
|
386
|
+
processPeakResidentBytes:
|
|
387
|
+
_wireInteger(message['processPeakResidentBytes']) ?? 0,
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
271
391
|
int? _wireInteger(Object? value) => value is num ? value.toInt() : null;
|
|
272
392
|
|
|
273
393
|
/// One opaque point in this workspace's verified history. Applications use it as the handle passed to a
|
|
@@ -339,17 +459,18 @@ class SyncStatus {
|
|
|
339
459
|
DateTime? lastSyncedAt,
|
|
340
460
|
String? lastError,
|
|
341
461
|
bool clearError = false,
|
|
342
|
-
}) =>
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
462
|
+
}) =>
|
|
463
|
+
SyncStatus(
|
|
464
|
+
phase: phase ?? this.phase,
|
|
465
|
+
ahead: ahead ?? this.ahead,
|
|
466
|
+
behind: behind ?? this.behind,
|
|
467
|
+
saved: saved ?? this.saved,
|
|
468
|
+
onMain: onMain ?? this.onMain,
|
|
469
|
+
deadLetters: deadLetters ?? this.deadLetters,
|
|
470
|
+
transfer: clearTransfer ? null : (transfer ?? this.transfer),
|
|
471
|
+
lastSyncedAt: lastSyncedAt ?? this.lastSyncedAt,
|
|
472
|
+
lastError: clearError ? null : (lastError ?? this.lastError),
|
|
473
|
+
);
|
|
353
474
|
}
|
|
354
475
|
|
|
355
476
|
bool _sameTransferProgress(TransferProgress? left, TransferProgress? right) =>
|
|
@@ -389,10 +510,10 @@ class NomosCreated {
|
|
|
389
510
|
/// The canonical [NomosRef] view — the `{workspace,type,id}` triple, followable/serializable
|
|
390
511
|
/// (`nomos://<workspace>/<type>/<id>`). One reference, everywhere.
|
|
391
512
|
NomosRef<Object?> get ref => NmsInternalNomosRefs.create<Object?>(
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
513
|
+
workspace: workspace,
|
|
514
|
+
type: type,
|
|
515
|
+
id: id,
|
|
516
|
+
);
|
|
396
517
|
@override
|
|
397
518
|
String toString() => id;
|
|
398
519
|
}
|
|
@@ -419,10 +540,10 @@ class NomosBbox {
|
|
|
419
540
|
double? minLat,
|
|
420
541
|
double? maxLng,
|
|
421
542
|
double? maxLat,
|
|
422
|
-
})
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
543
|
+
}) : minX = (minX ?? minLng)!,
|
|
544
|
+
minY = (minY ?? minLat)!,
|
|
545
|
+
maxX = (maxX ?? maxLng)!,
|
|
546
|
+
maxY = (maxY ?? maxLat)!;
|
|
426
547
|
|
|
427
548
|
/// Deprecated geographic accessors — spatial axes are generic. Prefer [minX]/[minY]/[maxX]/[maxY].
|
|
428
549
|
@Deprecated(
|
|
@@ -443,16 +564,16 @@ class NomosBbox {
|
|
|
443
564
|
double get maxLat => maxY;
|
|
444
565
|
|
|
445
566
|
Map<String, Object?> toJson() => {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
567
|
+
'minX': minX,
|
|
568
|
+
'minY': minY,
|
|
569
|
+
'maxX': maxX,
|
|
570
|
+
'maxY': maxY,
|
|
571
|
+
// Geographic aliases so a pre-genericisation runner (which reads minLng/…) still works.
|
|
572
|
+
'minLng': minX,
|
|
573
|
+
'minLat': minY,
|
|
574
|
+
'maxLng': maxX,
|
|
575
|
+
'maxLat': maxY,
|
|
576
|
+
};
|
|
456
577
|
}
|
|
457
578
|
|
|
458
579
|
/// One member's structural compat verdict — `ok`, plus the exact break [reason] when not.
|
|
@@ -513,9 +634,8 @@ class NomosCompat {
|
|
|
513
634
|
final install = (m['install'] as Map?)?.cast<String, Object?>() ?? const {};
|
|
514
635
|
return NomosCompat(
|
|
515
636
|
generatedFor: (m['generatedFor'] as String?) ?? '',
|
|
516
|
-
workspaceRuns:
|
|
517
|
-
.map((e) => '$e')
|
|
518
|
-
.toList(),
|
|
637
|
+
workspaceRuns:
|
|
638
|
+
((m['workspaceRuns'] as List?) ?? const []).map((e) => '$e').toList(),
|
|
519
639
|
matches: m['matches'] == true,
|
|
520
640
|
offerKnown: m['offerKnown'] == true,
|
|
521
641
|
structurallyCompatible: m['structurallyCompatible'] as bool?,
|
|
@@ -554,13 +674,13 @@ class NomosScopeKey {
|
|
|
554
674
|
this.workspace,
|
|
555
675
|
});
|
|
556
676
|
Map<String, Object?> toJson() => {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
677
|
+
'scope': scope,
|
|
678
|
+
'epoch': epoch,
|
|
679
|
+
'key': key,
|
|
680
|
+
if (ownedKeySubject != null) 'ownedKeySubject': ownedKeySubject,
|
|
681
|
+
if (ownedCustody != null) 'ownedCustody': ownedCustody,
|
|
682
|
+
if (workspace != null) 'workspace': workspace,
|
|
683
|
+
};
|
|
564
684
|
}
|
|
565
685
|
|
|
566
686
|
/// The entire app-facing multi-device surface for owned business state. The request is deliberately an
|
|
@@ -790,7 +910,7 @@ class BirthIncomplete implements Exception {
|
|
|
790
910
|
return 'BirthIncomplete: ${failedBirths.length} child workspace(s) did NOT come up — $list. '
|
|
791
911
|
'The parent offer admitted, but the child was not fully born, so opening it would poll head:null '
|
|
792
912
|
'until timeout. ${lawUnavailable ? "The child's law package is not resolvable from the parent's "
|
|
793
|
-
|
|
913
|
+
"custody — deploy it to the parent (POST /v2/workspaces/<parent>/domains) BEFORE birthing." : "See the outcome's reason/code."}';
|
|
794
914
|
}
|
|
795
915
|
}
|
|
796
916
|
|
|
@@ -852,14 +972,13 @@ class NomosEncryption {
|
|
|
852
972
|
final subjects = rows.isEmpty
|
|
853
973
|
? const <String, dynamic>{}
|
|
854
974
|
: ((rows.first['data'] as Map?)?['subjects'] as Map?) ??
|
|
855
|
-
|
|
975
|
+
const <String, dynamic>{};
|
|
856
976
|
final out = <String>[];
|
|
857
977
|
subjects.forEach((subj, leafRaw) {
|
|
858
978
|
Map? leaf;
|
|
859
979
|
try {
|
|
860
|
-
leaf =
|
|
861
|
-
? jsonDecode(leafRaw) as Map
|
|
862
|
-
: (leafRaw as Map);
|
|
980
|
+
leaf =
|
|
981
|
+
leafRaw is String ? jsonDecode(leafRaw) as Map : (leafRaw as Map);
|
|
863
982
|
} catch (_) {
|
|
864
983
|
leaf = null;
|
|
865
984
|
}
|
|
@@ -986,7 +1105,8 @@ class NomosEncryption {
|
|
|
986
1105
|
},
|
|
987
1106
|
actor: actor,
|
|
988
1107
|
domainHash: domainHash,
|
|
989
|
-
))
|
|
1108
|
+
))
|
|
1109
|
+
.head;
|
|
990
1110
|
return NomosEpochResult(head: head, epoch: epoch, roster: members);
|
|
991
1111
|
}
|
|
992
1112
|
|
|
@@ -1060,13 +1180,14 @@ class NomosEncryption {
|
|
|
1060
1180
|
Map<String, String>? memberKeys,
|
|
1061
1181
|
List<String>? expectRoster,
|
|
1062
1182
|
String? openedAt,
|
|
1063
|
-
}) =>
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1183
|
+
}) =>
|
|
1184
|
+
_advance(
|
|
1185
|
+
_defScope(scope),
|
|
1186
|
+
scopeKey: scopeKey,
|
|
1187
|
+
memberKeys: memberKeys,
|
|
1188
|
+
expectRoster: expectRoster,
|
|
1189
|
+
openedAt: openedAt,
|
|
1190
|
+
);
|
|
1070
1191
|
}
|
|
1071
1192
|
|
|
1072
1193
|
/// The result of [NomosBridge.offerCreates]: the new local [head], every aggregate the plan minted, and —
|
|
@@ -1091,8 +1212,9 @@ final class NomosOperationStatus {
|
|
|
1091
1212
|
/// adopted that shared position. Callers never inspect workspaces, URLs, credentials, headers or Git heads.
|
|
1092
1213
|
final class NomosOperationReceipt {
|
|
1093
1214
|
NomosOperationReceipt._()
|
|
1094
|
-
|
|
1095
|
-
|
|
1215
|
+
: _status =
|
|
1216
|
+
const NomosOperationStatus(NomosOperationPhase.locallyAccepted),
|
|
1217
|
+
_changes = StreamController<NomosOperationStatus>.broadcast();
|
|
1096
1218
|
|
|
1097
1219
|
NomosOperationStatus _status;
|
|
1098
1220
|
final StreamController<NomosOperationStatus> _changes;
|
|
@@ -1307,12 +1429,14 @@ class NomosBridge {
|
|
|
1307
1429
|
[]; // recent span events — so BOOT spans (emitted during connect, before the UI subscribes) survive
|
|
1308
1430
|
final StreamController<TransferProgress> _transferEvents =
|
|
1309
1431
|
StreamController<TransferProgress>.broadcast();
|
|
1432
|
+
final StreamController<KernelProgress> _kernelProgressEvents =
|
|
1433
|
+
StreamController<KernelProgress>.broadcast();
|
|
1310
1434
|
// The DERIVED app-ready sync state. seeded-on-listen with a metrics poll; updated on sync/signal/transfer.
|
|
1311
1435
|
late final StreamController<SyncStatus> _syncStatusCtrl =
|
|
1312
1436
|
StreamController<SyncStatus>.broadcast(
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1437
|
+
onListen: _startStatusPoll,
|
|
1438
|
+
onCancel: _stopStatusPollIfUnused,
|
|
1439
|
+
);
|
|
1316
1440
|
SyncStatus _status = const SyncStatus();
|
|
1317
1441
|
final Map<String, SyncStatus> _statusBySession = {};
|
|
1318
1442
|
final Map<String, StreamController<SyncStatus>> _syncStatusBySession = {};
|
|
@@ -1349,7 +1473,7 @@ class NomosBridge {
|
|
|
1349
1473
|
// child sessions. The callback receives only the typed review object; child
|
|
1350
1474
|
// addresses, archives, credentials and lifecycle directives remain private.
|
|
1351
1475
|
final Map<String, Future<void> Function(DomainClientModelAdoptionRequired)>
|
|
1352
|
-
|
|
1476
|
+
_sessionModelChangeHandlers = {};
|
|
1353
1477
|
// [acquireSession]'s per-workspace single-flight: workspace → the in-flight open every concurrent
|
|
1354
1478
|
// acquirer joins (removed when it completes; a live session then hits the fast path instead).
|
|
1355
1479
|
final Map<String, Future<NomosSession>> _acquiring = {};
|
|
@@ -1621,6 +1745,12 @@ class NomosBridge {
|
|
|
1621
1745
|
);
|
|
1622
1746
|
return;
|
|
1623
1747
|
}
|
|
1748
|
+
if (event == 'kernelProgress') {
|
|
1749
|
+
if (!_kernelProgressEvents.isClosed) {
|
|
1750
|
+
_kernelProgressEvents.add(KernelProgress.fromMessage(m));
|
|
1751
|
+
}
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1624
1754
|
if (event == 'trace') {
|
|
1625
1755
|
// the realtime cloud flamepath: { event:'trace', trace:{at, oid, edge, clients} } — one per admission.
|
|
1626
1756
|
if (!_flameEvents.isClosed) _flameEvents.add(m);
|
|
@@ -1801,8 +1931,8 @@ class NomosBridge {
|
|
|
1801
1931
|
final push = (result['pushError'] as Map?)?.cast<String, Object?>();
|
|
1802
1932
|
final status = push?['status'];
|
|
1803
1933
|
if (status == 401 || status == 403) return true;
|
|
1804
|
-
final text =
|
|
1805
|
-
.toLowerCase();
|
|
1934
|
+
final text =
|
|
1935
|
+
'${message['error'] ?? ''} ${push?['error'] ?? ''}'.toLowerCase();
|
|
1806
1936
|
return text.contains('unauthenticated') ||
|
|
1807
1937
|
text.contains('token expired') ||
|
|
1808
1938
|
text.contains('invalid token');
|
|
@@ -1899,21 +2029,20 @@ class NomosBridge {
|
|
|
1899
2029
|
const [])
|
|
1900
2030
|
.map((e) => (e as Map).cast<String, Object?>())
|
|
1901
2031
|
.map((w) {
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
.toList();
|
|
2032
|
+
final ws = w['workspace']?.toString() ?? '';
|
|
2033
|
+
// `connected` is the app's own connect() truth; `parked`/resident come from the runner realm.
|
|
2034
|
+
w['state'] = (w['parked'] == true)
|
|
2035
|
+
? 'parked'
|
|
2036
|
+
: (connected.contains(ws) ? 'connected' : 'resident');
|
|
2037
|
+
return w;
|
|
2038
|
+
}).toList();
|
|
1910
2039
|
}
|
|
1911
2040
|
|
|
1912
2041
|
/// Framework-private plane persistence primitive. It snapshots exact local custody even when [workspace]
|
|
1913
2042
|
/// was born in the kernel but no Dart runner session has ever acquired it. This does not connect, mount a
|
|
1914
2043
|
/// tenant session, fetch from cloud, or grant authority; the serving realm exports its resident plane view.
|
|
1915
2044
|
Future<({String workspace, String head, String? snapshot, bool unchanged})>
|
|
1916
|
-
|
|
2045
|
+
nomosInternalSnapshotWorkspace(String workspace, {String? ifHead}) async {
|
|
1917
2046
|
final r = _asMap(
|
|
1918
2047
|
await _call('snapshotWorkspace', {
|
|
1919
2048
|
'workspace': workspace,
|
|
@@ -1995,8 +2124,8 @@ class NomosBridge {
|
|
|
1995
2124
|
if (authorSecret != null && authorSecret.isNotEmpty) {
|
|
1996
2125
|
_eventPlane._sessionAuthorSecrets[ws] = authorSecret;
|
|
1997
2126
|
}
|
|
1998
|
-
_eventPlane._lastRecovery =
|
|
1999
|
-
?.cast<String, Object?>();
|
|
2127
|
+
_eventPlane._lastRecovery =
|
|
2128
|
+
(r['recovery'] as Map?)?.cast<String, Object?>();
|
|
2000
2129
|
if (_eventPlane._syncStatusCtrl.hasListener) {
|
|
2001
2130
|
unawaited(_eventPlane._pollMetricsOnce());
|
|
2002
2131
|
}
|
|
@@ -2032,12 +2161,13 @@ class NomosBridge {
|
|
|
2032
2161
|
Future<Map<String, Object?>> resolveCustodyBreak(
|
|
2033
2162
|
String decision, {
|
|
2034
2163
|
String? session,
|
|
2035
|
-
}) async =>
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2164
|
+
}) async =>
|
|
2165
|
+
_asMap(
|
|
2166
|
+
await _call('resolveCustodyBreak', {
|
|
2167
|
+
'decision': decision,
|
|
2168
|
+
if (session != null) 'session': session,
|
|
2169
|
+
}),
|
|
2170
|
+
);
|
|
2041
2171
|
|
|
2042
2172
|
/// The parked salvaged intents (re-offerable local work an `adopt` rescued) — metadata only.
|
|
2043
2173
|
Future<List<Map<String, Object?>>> salvagedIntents({String? session}) async {
|
|
@@ -2054,23 +2184,25 @@ class NomosBridge {
|
|
|
2054
2184
|
Future<Map<String, Object?>> reofferSalvaged({
|
|
2055
2185
|
String? id,
|
|
2056
2186
|
String? session,
|
|
2057
|
-
}) async =>
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2187
|
+
}) async =>
|
|
2188
|
+
_asMap(
|
|
2189
|
+
await _call('reofferSalvaged', {
|
|
2190
|
+
if (id != null) 'id': id,
|
|
2191
|
+
if (session != null) 'session': session,
|
|
2192
|
+
}),
|
|
2193
|
+
);
|
|
2063
2194
|
|
|
2064
2195
|
/// Discard one parked salvaged intent — the app's explicit choice.
|
|
2065
2196
|
Future<Map<String, Object?>> discardSalvaged(
|
|
2066
2197
|
String id, {
|
|
2067
2198
|
String? session,
|
|
2068
|
-
}) async =>
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2199
|
+
}) async =>
|
|
2200
|
+
_asMap(
|
|
2201
|
+
await _call('discardSalvaged', {
|
|
2202
|
+
'id': id,
|
|
2203
|
+
if (session != null) 'session': session,
|
|
2204
|
+
}),
|
|
2205
|
+
);
|
|
2074
2206
|
|
|
2075
2207
|
// ── THE DEAD-LETTER QUEUE (refused-but-legitimate work is NEVER lost) — the Dart mirror of the
|
|
2076
2208
|
// JS holon's DLQ surface (`deadLetters()` / `retryDeadLetter()` / `discardDeadLetter()`). A
|
|
@@ -2095,23 +2227,25 @@ class NomosBridge {
|
|
|
2095
2227
|
Future<Map<String, Object?>> retryDeadLetter(
|
|
2096
2228
|
String id, {
|
|
2097
2229
|
String? session,
|
|
2098
|
-
}) async =>
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2230
|
+
}) async =>
|
|
2231
|
+
_asMap(
|
|
2232
|
+
await _call('retryDeadLetter', {
|
|
2233
|
+
'id': id,
|
|
2234
|
+
if (session != null) 'session': session,
|
|
2235
|
+
}),
|
|
2236
|
+
);
|
|
2104
2237
|
|
|
2105
2238
|
/// Discard one dead letter — the app's EXPLICIT choice to drop the work. `{ok}`.
|
|
2106
2239
|
Future<Map<String, Object?>> discardDeadLetter(
|
|
2107
2240
|
String id, {
|
|
2108
2241
|
String? session,
|
|
2109
|
-
}) async =>
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2242
|
+
}) async =>
|
|
2243
|
+
_asMap(
|
|
2244
|
+
await _call('discardDeadLetter', {
|
|
2245
|
+
'id': id,
|
|
2246
|
+
if (session != null) 'session': session,
|
|
2247
|
+
}),
|
|
2248
|
+
);
|
|
2115
2249
|
|
|
2116
2250
|
/// SALVAGE — best-effort extraction of this holon's local-only authored work (un-synced pending
|
|
2117
2251
|
/// writes + the DLQ), each with its full `changeB64` bytes: `{pendingWrites, deadLetters}`. The
|
|
@@ -2126,13 +2260,14 @@ class NomosBridge {
|
|
|
2126
2260
|
List<Map<String, Object?>> pendingWrites = const [],
|
|
2127
2261
|
List<Map<String, Object?>> deadLetters = const [],
|
|
2128
2262
|
String? session,
|
|
2129
|
-
}) async =>
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2263
|
+
}) async =>
|
|
2264
|
+
_asMap(
|
|
2265
|
+
await _call('absorbSalvage', {
|
|
2266
|
+
'pendingWrites': pendingWrites,
|
|
2267
|
+
'deadLetters': deadLetters,
|
|
2268
|
+
if (session != null) 'session': session,
|
|
2269
|
+
}),
|
|
2270
|
+
);
|
|
2136
2271
|
|
|
2137
2272
|
/// The salvage report from the most recent `connect(recover: true)` — `{ salvagedWrites, requeuedDeadLetters,
|
|
2138
2273
|
/// unsalvageable }`, or null if the connection didn't recover. Show "recovered N writes" / surface
|
|
@@ -2218,18 +2353,19 @@ class NomosBridge {
|
|
|
2218
2353
|
String? session,
|
|
2219
2354
|
String? authorSecret,
|
|
2220
2355
|
NomosBirthCertificate? birthCertificate,
|
|
2221
|
-
}) =>
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2356
|
+
}) =>
|
|
2357
|
+
offerOutcome(
|
|
2358
|
+
domain: domain,
|
|
2359
|
+
directiveId: directiveId,
|
|
2360
|
+
payload: payload,
|
|
2361
|
+
actor: actor,
|
|
2362
|
+
domainHash: domainHash,
|
|
2363
|
+
nomosConflictSnapshots: nomosConflictSnapshots,
|
|
2364
|
+
force: force,
|
|
2365
|
+
session: session,
|
|
2366
|
+
authorSecret: authorSecret,
|
|
2367
|
+
birthCertificate: birthCertificate,
|
|
2368
|
+
);
|
|
2233
2369
|
|
|
2234
2370
|
/// THE ONE OUTCOME-RETURNING OFFER LANE — [offerDirective] answering the full typed [NomosOfferOutcome]
|
|
2235
2371
|
/// (head + law-minted `created` NomosRefs + births), for EVERY generated method (create or mutate). A pure
|
|
@@ -2343,8 +2479,7 @@ class NomosBridge {
|
|
|
2343
2479
|
}
|
|
2344
2480
|
}
|
|
2345
2481
|
final sourceSession = session ?? _sessionName;
|
|
2346
|
-
final birthAuthorSecret =
|
|
2347
|
-
authorSecret ??
|
|
2482
|
+
final birthAuthorSecret = authorSecret ??
|
|
2348
2483
|
(sourceSession == null
|
|
2349
2484
|
? null
|
|
2350
2485
|
: _eventPlane._sessionAuthorSecrets[sourceSession]);
|
|
@@ -2403,8 +2538,7 @@ class NomosBridge {
|
|
|
2403
2538
|
}) async {
|
|
2404
2539
|
if (!RegExp(
|
|
2405
2540
|
r"no active installed law declares domain '",
|
|
2406
|
-
).hasMatch(e.toString()))
|
|
2407
|
-
return e;
|
|
2541
|
+
).hasMatch(e.toString())) return e;
|
|
2408
2542
|
final self = this;
|
|
2409
2543
|
final sessionWs = self is NomosSession ? self.workspace : null;
|
|
2410
2544
|
var activeKeys = const <String>[];
|
|
@@ -2557,43 +2691,41 @@ class NomosBridge {
|
|
|
2557
2691
|
return ready(session(workspace));
|
|
2558
2692
|
}
|
|
2559
2693
|
// SINGLE-FLIGHT: one in-flight open per workspace per plane; concurrent callers join it.
|
|
2560
|
-
return plane._acquiring
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
rethrow;
|
|
2584
|
-
}
|
|
2585
|
-
await acquireSession(
|
|
2586
|
-
required,
|
|
2587
|
-
ifAbsentCredentials: ifAbsentCredentials,
|
|
2588
|
-
authorSecret: authorSecret,
|
|
2589
|
-
inheritedPrincipal: sourcePrincipal,
|
|
2590
|
-
);
|
|
2591
|
-
}
|
|
2694
|
+
return plane._acquiring.putIfAbsent(workspace, () async {
|
|
2695
|
+
// RESIDENCY IS THE KERNEL'S TO DEMAND AND OURS TO SATISFY. Replaying a chain can reach a
|
|
2696
|
+
// colocated read whose source workspace is not mounted; the kernel refuses TYPED, naming it.
|
|
2697
|
+
// Mount the named sibling — which consults local custody first and only then clones — and
|
|
2698
|
+
// retry. Bounded, and each name is satisfied at most once, so a genuinely missing workspace
|
|
2699
|
+
// fails with the kernel's own refusal instead of spinning.
|
|
2700
|
+
final satisfied = <String>{};
|
|
2701
|
+
for (var round = 0;; round++) {
|
|
2702
|
+
try {
|
|
2703
|
+
return await _acquireSessionOnce(
|
|
2704
|
+
workspace,
|
|
2705
|
+
ready: ready,
|
|
2706
|
+
inheritPrincipal: inheritPrincipal,
|
|
2707
|
+
ifAbsentCredentials: ifAbsentCredentials,
|
|
2708
|
+
authorSecret: authorSecret,
|
|
2709
|
+
);
|
|
2710
|
+
} catch (error) {
|
|
2711
|
+
final required = nomosInternalResidencyRequiredWorkspace(error);
|
|
2712
|
+
if (required == null ||
|
|
2713
|
+
required == workspace ||
|
|
2714
|
+
!satisfied.add(required) ||
|
|
2715
|
+
round >= 8) {
|
|
2716
|
+
rethrow;
|
|
2592
2717
|
}
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2718
|
+
await acquireSession(
|
|
2719
|
+
required,
|
|
2720
|
+
ifAbsentCredentials: ifAbsentCredentials,
|
|
2721
|
+
authorSecret: authorSecret,
|
|
2722
|
+
inheritedPrincipal: sourcePrincipal,
|
|
2723
|
+
);
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
}).whenComplete(() {
|
|
2727
|
+
plane._acquiring.remove(workspace);
|
|
2728
|
+
});
|
|
2597
2729
|
}
|
|
2598
2730
|
|
|
2599
2731
|
Future<NomosSession> _acquireSessionOnce(
|
|
@@ -2765,16 +2897,17 @@ class NomosBridge {
|
|
|
2765
2897
|
List<Map<String, Object?>>? nomosConflictSnapshots,
|
|
2766
2898
|
bool? force,
|
|
2767
2899
|
String? session,
|
|
2768
|
-
}) =>
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2900
|
+
}) =>
|
|
2901
|
+
offerDirective(
|
|
2902
|
+
domain: domain,
|
|
2903
|
+
directiveId: directiveId,
|
|
2904
|
+
payload: payload,
|
|
2905
|
+
actor: actor,
|
|
2906
|
+
domainHash: domainHash,
|
|
2907
|
+
nomosConflictSnapshots: nomosConflictSnapshots,
|
|
2908
|
+
force: force,
|
|
2909
|
+
session: session,
|
|
2910
|
+
);
|
|
2778
2911
|
|
|
2779
2912
|
/// Resolve the generated law's standard OSGi Contract Namespace requirements before bind.
|
|
2780
2913
|
Future<void> assertKernelCoherence(
|
|
@@ -2900,14 +3033,15 @@ class NomosBridge {
|
|
|
2900
3033
|
List<String>? actions,
|
|
2901
3034
|
String domain = '',
|
|
2902
3035
|
String directiveId = 'undo',
|
|
2903
|
-
}) async =>
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
3036
|
+
}) async =>
|
|
3037
|
+
_asMap(
|
|
3038
|
+
await _call('undoMostRecent', {
|
|
3039
|
+
'scope': scope,
|
|
3040
|
+
if (actions != null) 'actions': actions,
|
|
3041
|
+
'domain': domain,
|
|
3042
|
+
'directiveId': directiveId,
|
|
3043
|
+
}),
|
|
3044
|
+
);
|
|
2911
3045
|
|
|
2912
3046
|
/// REDO the most-recent undo (strike the strike → its target re-applies). Same [scope]/[domain]/[directiveId]
|
|
2913
3047
|
/// as [undoMostRecent]. Returns `{ok, redone, head}`.
|
|
@@ -2915,13 +3049,14 @@ class NomosBridge {
|
|
|
2915
3049
|
String scope = 'all',
|
|
2916
3050
|
String domain = '',
|
|
2917
3051
|
String directiveId = 'undo',
|
|
2918
|
-
}) async =>
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
3052
|
+
}) async =>
|
|
3053
|
+
_asMap(
|
|
3054
|
+
await _call('redoMostRecent', {
|
|
3055
|
+
'scope': scope,
|
|
3056
|
+
'domain': domain,
|
|
3057
|
+
'directiveId': directiveId,
|
|
3058
|
+
}),
|
|
3059
|
+
);
|
|
2925
3060
|
|
|
2926
3061
|
/// Framework-private business correction seam. Generated clients bind the domain, its actions and actor;
|
|
2927
3062
|
/// application code supplies only the correction kind and its mandatory reason.
|
|
@@ -2932,16 +3067,17 @@ class NomosBridge {
|
|
|
2932
3067
|
required String reason,
|
|
2933
3068
|
String? domainHash,
|
|
2934
3069
|
String? actor,
|
|
2935
|
-
}) async =>
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
3070
|
+
}) async =>
|
|
3071
|
+
_asMap(
|
|
3072
|
+
await _call('correctMostRecent', {
|
|
3073
|
+
'kind': kind,
|
|
3074
|
+
'domain': domain,
|
|
3075
|
+
'actions': actions,
|
|
3076
|
+
'reason': reason,
|
|
3077
|
+
if (domainHash != null) 'domainHash': domainHash,
|
|
3078
|
+
if (actor != null) 'actor': actor,
|
|
3079
|
+
}),
|
|
3080
|
+
);
|
|
2945
3081
|
|
|
2946
3082
|
/// Framework-private exact-history adoption seam. Generated clients expose this as
|
|
2947
3083
|
/// `model.adopt()` and, only when prior business outcomes differ, a chained
|
|
@@ -3043,12 +3179,13 @@ class NomosBridge {
|
|
|
3043
3179
|
String? base,
|
|
3044
3180
|
int? limit,
|
|
3045
3181
|
String? session,
|
|
3046
|
-
}) =>
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3182
|
+
}) =>
|
|
3183
|
+
_watchStream(
|
|
3184
|
+
kind: 'activity',
|
|
3185
|
+
base: base,
|
|
3186
|
+
limit: limit,
|
|
3187
|
+
session: session,
|
|
3188
|
+
);
|
|
3052
3189
|
|
|
3053
3190
|
/// Mint an ed25519 DEVICE KEY in-kernel → `{secret, public}` (base64). The SECRET stays on this device
|
|
3054
3191
|
/// (persist it in secure storage); pass it to [connect] as `authorSecret` (or per-offer `signWith`) so the
|
|
@@ -3071,16 +3208,17 @@ class NomosBridge {
|
|
|
3071
3208
|
Future<NomosBirthCertificate> signBirthCert(
|
|
3072
3209
|
NomosBirthCertificateSpec spec,
|
|
3073
3210
|
String deviceSecret,
|
|
3074
|
-
) async =>
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3211
|
+
) async =>
|
|
3212
|
+
NomosBirthCertificate.fromWire(
|
|
3213
|
+
_asMap(
|
|
3214
|
+
_asMap(
|
|
3215
|
+
await _call('signBirthCert', {
|
|
3216
|
+
'fields': spec.toWire(),
|
|
3217
|
+
'deviceSecret': deviceSecret,
|
|
3218
|
+
}),
|
|
3219
|
+
)['cert'],
|
|
3220
|
+
),
|
|
3221
|
+
);
|
|
3084
3222
|
|
|
3085
3223
|
/// MINT THE KEYLESS ENROLL-ONCE DELEGATION (`enrollFirstDevice.delegationCert`, the verified-first-device
|
|
3086
3224
|
/// lane). The device SELF-signs it; its authority is the carried `idpToken` (the kernel re-verifies that to
|
|
@@ -3093,12 +3231,11 @@ class NomosBridge {
|
|
|
3093
3231
|
String deviceSecret,
|
|
3094
3232
|
) async =>
|
|
3095
3233
|
_asMap(
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
as Map<String, Object?>? ??
|
|
3234
|
+
await _call('mintKeylessDelegation', {
|
|
3235
|
+
'spec': spec,
|
|
3236
|
+
'deviceSecret': deviceSecret,
|
|
3237
|
+
}),
|
|
3238
|
+
)['cert'] as Map<String, Object?>? ??
|
|
3102
3239
|
const {};
|
|
3103
3240
|
|
|
3104
3241
|
/// ENROLL THIS DEVICE — the LIVE gate: bind a device key's PUBLIC half to `principal` on-chain as a
|
|
@@ -3109,13 +3246,14 @@ class NomosBridge {
|
|
|
3109
3246
|
required String principal,
|
|
3110
3247
|
required String publicKey,
|
|
3111
3248
|
String? authToken,
|
|
3112
|
-
}) async =>
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3249
|
+
}) async =>
|
|
3250
|
+
_asMap(
|
|
3251
|
+
await _call('enrollDevice', {
|
|
3252
|
+
'principal': principal,
|
|
3253
|
+
'publicKey': publicKey,
|
|
3254
|
+
if (authToken != null) 'authToken': authToken,
|
|
3255
|
+
}),
|
|
3256
|
+
);
|
|
3119
3257
|
|
|
3120
3258
|
/// Derive the device key's base64 PUBLIC half from its base64 SECRET seed, inside the LOCAL kernel
|
|
3121
3259
|
/// (the secret never leaves the device). The recovery lanes use this when only the secret was
|
|
@@ -3139,16 +3277,17 @@ class NomosBridge {
|
|
|
3139
3277
|
String? publicKey,
|
|
3140
3278
|
String? authToken,
|
|
3141
3279
|
bool remote = false,
|
|
3142
|
-
}) async =>
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3280
|
+
}) async =>
|
|
3281
|
+
_asMap(
|
|
3282
|
+
await _call('parentSignerEnrollment', {
|
|
3283
|
+
'parent': parent,
|
|
3284
|
+
'principal': principal,
|
|
3285
|
+
if (keyHash != null) 'keyHash': keyHash,
|
|
3286
|
+
if (publicKey != null) 'publicKey': publicKey,
|
|
3287
|
+
if (authToken != null) 'authToken': authToken,
|
|
3288
|
+
if (remote) 'remote': true,
|
|
3289
|
+
}),
|
|
3290
|
+
);
|
|
3152
3291
|
|
|
3153
3292
|
/// IDEMPOTENT PARENT-SIGNER RECOVERY — ensure this device key is a live `signer` on `parent`,
|
|
3154
3293
|
/// lawfully restoring the tuple a parent custody REBIRTH lost (the device key + the born home stay
|
|
@@ -3171,19 +3310,20 @@ class NomosBridge {
|
|
|
3171
3310
|
String? authToken,
|
|
3172
3311
|
String? idToken,
|
|
3173
3312
|
String? verifiedVia,
|
|
3174
|
-
}) async =>
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3313
|
+
}) async =>
|
|
3314
|
+
_asMap(
|
|
3315
|
+
await _call('ensureParentSignerEnrollment', {
|
|
3316
|
+
'parent': parent,
|
|
3317
|
+
'principal': principal,
|
|
3318
|
+
if (publicKey != null) 'publicKey': publicKey,
|
|
3319
|
+
if (authorSecret != null) 'authorSecret': authorSecret,
|
|
3320
|
+
if (domain != null) 'domain': domain,
|
|
3321
|
+
if (directiveId != null) 'directiveId': directiveId,
|
|
3322
|
+
if (authToken != null) 'authToken': authToken,
|
|
3323
|
+
if (idToken != null) 'idToken': idToken,
|
|
3324
|
+
if (verifiedVia != null) 'verifiedVia': verifiedVia,
|
|
3325
|
+
}),
|
|
3326
|
+
);
|
|
3187
3327
|
|
|
3188
3328
|
/// AUTHORING PREFLIGHT — evaluate the SAME premises the kernel's authorization gate judges,
|
|
3189
3329
|
/// READ-ONLY, and return a typed verdict WITHOUT authoring — so the app catches a doomed write
|
|
@@ -3199,16 +3339,17 @@ class NomosBridge {
|
|
|
3199
3339
|
String? publicKey,
|
|
3200
3340
|
String? keyHash,
|
|
3201
3341
|
String? authToken,
|
|
3202
|
-
}) async =>
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3342
|
+
}) async =>
|
|
3343
|
+
_asMap(
|
|
3344
|
+
await _call('canAuthor', {
|
|
3345
|
+
'directiveId': directiveId,
|
|
3346
|
+
if (ws != null) 'ws': ws,
|
|
3347
|
+
if (principal != null) 'principal': principal,
|
|
3348
|
+
if (publicKey != null) 'publicKey': publicKey,
|
|
3349
|
+
if (keyHash != null) 'keyHash': keyHash,
|
|
3350
|
+
if (authToken != null) 'authToken': authToken,
|
|
3351
|
+
}),
|
|
3352
|
+
);
|
|
3212
3353
|
|
|
3213
3354
|
/// VERIFY_CHAIN — the kernel's from-genesis self-verification of THIS holon's folded ledger (read-only:
|
|
3214
3355
|
/// contiguity → every intent re-admitted through the ONE gate against the state folded from the prefix). For
|
|
@@ -3222,7 +3363,8 @@ class NomosBridge {
|
|
|
3222
3363
|
/// receives only the resulting business application.
|
|
3223
3364
|
Future<Map<String, Object?>> nomosInternalBirthReceivingGroup(
|
|
3224
3365
|
Map<String, Object?> spec,
|
|
3225
|
-
) async =>
|
|
3366
|
+
) async =>
|
|
3367
|
+
_asMap(await _call('nomosInternalBirthReceivingGroup', spec));
|
|
3226
3368
|
|
|
3227
3369
|
/// Sign a cross-custody SHARE attestation `{secret, asserterWorkspace, object, relation, subject,
|
|
3228
3370
|
/// attestedAt}` → `{publicKey, signature}`. This is what drives `recordShare` into an invitee's home
|
|
@@ -3230,7 +3372,8 @@ class NomosBridge {
|
|
|
3230
3372
|
/// with `subject`, and the invitee's home gate verifies the signature before recording it.
|
|
3231
3373
|
Future<Map<String, Object?>> attestationSign(
|
|
3232
3374
|
Map<String, Object?> args,
|
|
3233
|
-
) async =>
|
|
3375
|
+
) async =>
|
|
3376
|
+
_asMap(await _call('attestationSign', {'args': args}));
|
|
3234
3377
|
|
|
3235
3378
|
/// Issue a ROLE-DELEGATION cert (the `.delegatedRole()` lane, kernel `delegationCertSign` op). A platform
|
|
3236
3379
|
/// signs — with its grantor secret `signerSecret` (base64) — a cert granting `relation` to `subject`,
|
|
@@ -3247,18 +3390,19 @@ class NomosBridge {
|
|
|
3247
3390
|
required String subject,
|
|
3248
3391
|
int epoch = 1,
|
|
3249
3392
|
List<Object?>? certChain,
|
|
3250
|
-
}) async =>
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3393
|
+
}) async =>
|
|
3394
|
+
_asMap(
|
|
3395
|
+
await _call('issueDelegationCert', {
|
|
3396
|
+
'args': {
|
|
3397
|
+
'signerSecret': signerSecret,
|
|
3398
|
+
'grantorId': grantorId,
|
|
3399
|
+
'relation': relation,
|
|
3400
|
+
'subject': subject,
|
|
3401
|
+
'epoch': epoch,
|
|
3402
|
+
if (certChain != null) 'certChain': certChain,
|
|
3403
|
+
},
|
|
3404
|
+
}),
|
|
3405
|
+
);
|
|
3262
3406
|
|
|
3263
3407
|
/// A declared query's rows from the local projection. `keys` (E2E sealing) injects the reader's held
|
|
3264
3408
|
/// scope keys so encrypted fields AEAD-open in-wasm; absent ⇒ they stay opaque envelopes (today's
|
|
@@ -3318,21 +3462,21 @@ class NomosBridge {
|
|
|
3318
3462
|
List<NomosScopeKey>? keys,
|
|
3319
3463
|
String? principal,
|
|
3320
3464
|
String? session,
|
|
3321
|
-
]) async =>
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3465
|
+
]) async =>
|
|
3466
|
+
_asRows(
|
|
3467
|
+
await _call('queryById', {
|
|
3468
|
+
'id': id,
|
|
3469
|
+
...?_readAuthArgs(keys, principal),
|
|
3470
|
+
if (session != null) 'session': session,
|
|
3471
|
+
}),
|
|
3472
|
+
);
|
|
3328
3473
|
|
|
3329
3474
|
/// Compare an immutable publication pin with the stable series' one compiled
|
|
3330
3475
|
/// current pointer. This is an O(current-state) check: it neither resolves the
|
|
3331
3476
|
/// pinned publication nor reconstructs aggregate history.
|
|
3332
|
-
Future<ReferenceVersionStatus<TVersion>>
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
>(VersionedNomosRef<TVersion, TSeries> reference) async {
|
|
3477
|
+
Future<ReferenceVersionStatus<TVersion>>
|
|
3478
|
+
checkReferenceVersion<TVersion, TSeries>(
|
|
3479
|
+
VersionedNomosRef<TVersion, TSeries> reference) async {
|
|
3336
3480
|
final result = _asMap(
|
|
3337
3481
|
await _call('checkReferenceVersions', {
|
|
3338
3482
|
'refs': [
|
|
@@ -3390,15 +3534,16 @@ class NomosBridge {
|
|
|
3390
3534
|
List<NomosScopeKey>? keys,
|
|
3391
3535
|
String? principal,
|
|
3392
3536
|
String? session,
|
|
3393
|
-
]) async =>
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3537
|
+
]) async =>
|
|
3538
|
+
_asRows(
|
|
3539
|
+
await _call('queryAt', {
|
|
3540
|
+
'commit': moment.id,
|
|
3541
|
+
'queryId': queryId,
|
|
3542
|
+
'params': params,
|
|
3543
|
+
...?_readAuthArgs(keys, principal),
|
|
3544
|
+
if (session != null) 'session': session,
|
|
3545
|
+
}),
|
|
3546
|
+
);
|
|
3402
3547
|
|
|
3403
3548
|
/// Read one aggregate from the verified world at [moment]. `keys`/`principal`: see [queryAt].
|
|
3404
3549
|
Future<List<Map<String, dynamic>>> queryByIdAt(
|
|
@@ -3407,14 +3552,15 @@ class NomosBridge {
|
|
|
3407
3552
|
List<NomosScopeKey>? keys,
|
|
3408
3553
|
String? principal,
|
|
3409
3554
|
String? session,
|
|
3410
|
-
]) async =>
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3555
|
+
]) async =>
|
|
3556
|
+
_asRows(
|
|
3557
|
+
await _call('queryByIdAt', {
|
|
3558
|
+
'commit': moment.id,
|
|
3559
|
+
'id': id,
|
|
3560
|
+
...?_readAuthArgs(keys, principal),
|
|
3561
|
+
if (session != null) 'session': session,
|
|
3562
|
+
}),
|
|
3563
|
+
);
|
|
3418
3564
|
|
|
3419
3565
|
/// Maintained historic count/sum values at [moment]. These are the temporal counterparts of [count]/[sum].
|
|
3420
3566
|
Future<int> countAt(
|
|
@@ -3424,14 +3570,13 @@ class NomosBridge {
|
|
|
3424
3570
|
String? session,
|
|
3425
3571
|
]) async =>
|
|
3426
3572
|
((_asMap(
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
as num)
|
|
3573
|
+
await _call('countAt', {
|
|
3574
|
+
'commit': moment.id,
|
|
3575
|
+
'countId': countId,
|
|
3576
|
+
'group': group,
|
|
3577
|
+
if (session != null) 'session': session,
|
|
3578
|
+
}),
|
|
3579
|
+
)['count']) as num)
|
|
3435
3580
|
.toInt();
|
|
3436
3581
|
|
|
3437
3582
|
Future<int> sumAt(
|
|
@@ -3441,14 +3586,13 @@ class NomosBridge {
|
|
|
3441
3586
|
String? session,
|
|
3442
3587
|
]) async =>
|
|
3443
3588
|
((_asMap(
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
as num)
|
|
3589
|
+
await _call('sumAt', {
|
|
3590
|
+
'commit': moment.id,
|
|
3591
|
+
'sumId': sumId,
|
|
3592
|
+
'group': group,
|
|
3593
|
+
if (session != null) 'session': session,
|
|
3594
|
+
}),
|
|
3595
|
+
)['sum']) as num)
|
|
3452
3596
|
.toInt();
|
|
3453
3597
|
|
|
3454
3598
|
/// The read auth sidecar (private reads + E2E): `keys`/`principal` as top-level call args, or null when
|
|
@@ -3564,14 +3708,15 @@ class NomosBridge {
|
|
|
3564
3708
|
required String domain,
|
|
3565
3709
|
required String domainHash,
|
|
3566
3710
|
required String recoveryPrincipal,
|
|
3567
|
-
}) async =>
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3711
|
+
}) async =>
|
|
3712
|
+
_asMap(
|
|
3713
|
+
await _call('cryptoOwnedProtectIdentity', {
|
|
3714
|
+
'scope': scope,
|
|
3715
|
+
'domain': domain,
|
|
3716
|
+
'domainHash': domainHash,
|
|
3717
|
+
'recoveryPrincipal': recoveryPrincipal,
|
|
3718
|
+
}),
|
|
3719
|
+
);
|
|
3575
3720
|
|
|
3576
3721
|
/// Prepare the private framework payload behind generated share/unshare/transfer methods.
|
|
3577
3722
|
Future<Map<String, Object?>> nomosPrepareOwnedRecordAction<TAggregate>({
|
|
@@ -3579,17 +3724,18 @@ class NomosBridge {
|
|
|
3579
3724
|
required String scope,
|
|
3580
3725
|
required NomosRef<TAggregate> record,
|
|
3581
3726
|
required String peer,
|
|
3582
|
-
}) async =>
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3727
|
+
}) async =>
|
|
3728
|
+
_asMap(
|
|
3729
|
+
await _call('cryptoOwnedPrepareRecordAction', {
|
|
3730
|
+
'action': action,
|
|
3731
|
+
'scope': scope,
|
|
3732
|
+
'record': {
|
|
3733
|
+
'type': NmsInternalNomosRefs.type(record),
|
|
3734
|
+
'id': NmsInternalNomosRefs.id(record),
|
|
3735
|
+
},
|
|
3736
|
+
'peer': peer,
|
|
3737
|
+
}),
|
|
3738
|
+
);
|
|
3593
3739
|
|
|
3594
3740
|
/// FRAMEWORK-INTERNAL business birth: derive the unborn child's owned key from its deterministic address.
|
|
3595
3741
|
Future<NomosScopeKey> nomosOwnedBirthScopeKey(
|
|
@@ -3618,34 +3764,35 @@ class NomosBridge {
|
|
|
3618
3764
|
}
|
|
3619
3765
|
|
|
3620
3766
|
/// FRAMEWORK-INTERNAL: issue an opaque request for this session's current device.
|
|
3621
|
-
Future<String> nomosOwnedDeviceRequest(String scope) async =>
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
.toString();
|
|
3767
|
+
Future<String> nomosOwnedDeviceRequest(String scope) async => (_asMap(
|
|
3768
|
+
await _call('cryptoOwnedDeviceRequest', {'scope': scope}),
|
|
3769
|
+
)['request'] ??
|
|
3770
|
+
'')
|
|
3771
|
+
.toString();
|
|
3627
3772
|
|
|
3628
3773
|
/// FRAMEWORK-INTERNAL: validate an opaque request and prepare the atomic signer + custody offer.
|
|
3629
3774
|
Future<Map<String, Object?>> nomosPrepareOwnedDeviceApproval(
|
|
3630
3775
|
String request,
|
|
3631
3776
|
String scope,
|
|
3632
|
-
) async =>
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3777
|
+
) async =>
|
|
3778
|
+
_asMap(
|
|
3779
|
+
await _call('cryptoOwnedPrepareApproval', {
|
|
3780
|
+
'request': request,
|
|
3781
|
+
'scope': scope,
|
|
3782
|
+
}),
|
|
3783
|
+
);
|
|
3638
3784
|
|
|
3639
3785
|
/// Wrap a scope key TO a recipient's public key (committer side): `{hpkeEpk, ct}` base64 ciphertext.
|
|
3640
3786
|
Future<Map<String, Object?>> cryptoWrapKey({
|
|
3641
3787
|
required String recipientPub,
|
|
3642
3788
|
required String scopeKey,
|
|
3643
|
-
}) async =>
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3789
|
+
}) async =>
|
|
3790
|
+
_asMap(
|
|
3791
|
+
await _call('cryptoWrapKey', {
|
|
3792
|
+
'recipientPub': recipientPub,
|
|
3793
|
+
'scopeKey': scopeKey,
|
|
3794
|
+
}),
|
|
3795
|
+
);
|
|
3649
3796
|
|
|
3650
3797
|
/// Unwrap an on-chain WrappedKey with this device's secret → the base64 scope key (reader side).
|
|
3651
3798
|
Future<String> cryptoUnwrapKey({
|
|
@@ -3669,13 +3816,14 @@ class NomosBridge {
|
|
|
3669
3816
|
String spatialId,
|
|
3670
3817
|
NomosBbox bbox, [
|
|
3671
3818
|
String? session,
|
|
3672
|
-
]) async =>
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3819
|
+
]) async =>
|
|
3820
|
+
_asRows(
|
|
3821
|
+
await _call('spatialWithin', {
|
|
3822
|
+
'spatialId': spatialId,
|
|
3823
|
+
'bbox': bbox.toJson(),
|
|
3824
|
+
if (session != null) 'session': session,
|
|
3825
|
+
}),
|
|
3826
|
+
);
|
|
3679
3827
|
|
|
3680
3828
|
/// A maintained O(1) count / sum.
|
|
3681
3829
|
Future<int> count(
|
|
@@ -3684,23 +3832,21 @@ class NomosBridge {
|
|
|
3684
3832
|
String? session,
|
|
3685
3833
|
]) async =>
|
|
3686
3834
|
((_asMap(
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
as num)
|
|
3835
|
+
await _call('count', {
|
|
3836
|
+
'countId': countId,
|
|
3837
|
+
'group': group,
|
|
3838
|
+
if (session != null) 'session': session,
|
|
3839
|
+
}),
|
|
3840
|
+
)['count']) as num)
|
|
3694
3841
|
.toInt();
|
|
3695
3842
|
Future<int> sum(String sumId, [String group = '', String? session]) async =>
|
|
3696
3843
|
((_asMap(
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
as num)
|
|
3844
|
+
await _call('sum', {
|
|
3845
|
+
'sumId': sumId,
|
|
3846
|
+
'group': group,
|
|
3847
|
+
if (session != null) 'session': session,
|
|
3848
|
+
}),
|
|
3849
|
+
)['sum']) as num)
|
|
3704
3850
|
.toInt();
|
|
3705
3851
|
|
|
3706
3852
|
/// THE PENDING-CONFLICTS READ (Surfaced merge conflicts — the DX layer): the MAINTAINED,
|
|
@@ -3712,41 +3858,46 @@ class NomosBridge {
|
|
|
3712
3858
|
/// `PendingConflictRow` and pairs them with the designated `resolve…` helpers.)
|
|
3713
3859
|
Future<List<Map<String, dynamic>>> pendingConflicts([
|
|
3714
3860
|
String? session,
|
|
3715
|
-
]) async =>
|
|
3716
|
-
|
|
3717
|
-
|
|
3861
|
+
]) async =>
|
|
3862
|
+
_asRows(
|
|
3863
|
+
await _call(
|
|
3864
|
+
'pendingConflicts', {if (session != null) 'session': session}),
|
|
3865
|
+
);
|
|
3718
3866
|
|
|
3719
3867
|
/// Framework-filtered pending conflicts for one generated aggregate type. This is the
|
|
3720
3868
|
/// primitive generated clients use; application code never filters the heterogeneous queue.
|
|
3721
3869
|
Future<List<Map<String, dynamic>>> pendingConflictsFor(
|
|
3722
3870
|
String aggregateType, [
|
|
3723
3871
|
String? session,
|
|
3724
|
-
]) async =>
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3872
|
+
]) async =>
|
|
3873
|
+
_asRows(
|
|
3874
|
+
await _call('pendingConflicts', {
|
|
3875
|
+
'aggregateType': aggregateType,
|
|
3876
|
+
if (session != null) 'session': session,
|
|
3877
|
+
}),
|
|
3878
|
+
);
|
|
3730
3879
|
|
|
3731
3880
|
/// Counts-only workspace summary over the maintained conflict index.
|
|
3732
3881
|
Future<Map<String, Object?>> pendingConflictSummary([
|
|
3733
3882
|
String? session,
|
|
3734
|
-
]) async =>
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3883
|
+
]) async =>
|
|
3884
|
+
_asMap(
|
|
3885
|
+
await _call('pendingConflictSummary', {
|
|
3886
|
+
if (session != null) 'session': session,
|
|
3887
|
+
}),
|
|
3888
|
+
);
|
|
3739
3889
|
|
|
3740
3890
|
/// Push local writes → the session branch → cloud admission, and pull canonical main back.
|
|
3741
3891
|
Future<Map<String, Object?>> sync({
|
|
3742
3892
|
bool admit = true,
|
|
3743
3893
|
String? session,
|
|
3744
|
-
}) async =>
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3894
|
+
}) async =>
|
|
3895
|
+
_asMap(
|
|
3896
|
+
await _call('sync', {
|
|
3897
|
+
'admit': admit,
|
|
3898
|
+
if (session != null) 'session': session,
|
|
3899
|
+
}),
|
|
3900
|
+
);
|
|
3750
3901
|
|
|
3751
3902
|
/// PERSIST: a base64 snapshot of the whole local holon (the bare nomos.git incl. un-synced writes + the
|
|
3752
3903
|
/// DLQ). Store it on disk and pass it back as `connect(restoreFrom:)` next launch — the app then boots from
|
|
@@ -3792,6 +3943,11 @@ class NomosBridge {
|
|
|
3792
3943
|
Stream<TransferProgress> get transferProgress =>
|
|
3793
3944
|
_eventPlane._transferEvents.stream;
|
|
3794
3945
|
|
|
3946
|
+
/// Live typed progress from the canonical kernel while a long synchronous
|
|
3947
|
+
/// operation is in flight (currently cold custody verification/replay).
|
|
3948
|
+
Stream<KernelProgress> get kernelProgress =>
|
|
3949
|
+
_eventPlane._kernelProgressEvents.stream;
|
|
3950
|
+
|
|
3795
3951
|
/// THE APP-READY SYNC STATE — a truthful aggregation of sync rounds, the doorbell, transfer progress, and a
|
|
3796
3952
|
/// periodic metrics poll. Map straight to a sync-state page. Starts a lightweight 4s metrics poll while
|
|
3797
3953
|
/// listened to (stops on cancel). [currentStatus] is the latest snapshot for a non-stream read.
|
|
@@ -3809,16 +3965,16 @@ class NomosBridge {
|
|
|
3809
3965
|
: _eventPlane._currentSessionStatus(session);
|
|
3810
3966
|
}
|
|
3811
3967
|
|
|
3812
|
-
Stream<SyncStatus> _sessionSyncStatus(String session) =>
|
|
3813
|
-
._syncStatusBySession
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3968
|
+
Stream<SyncStatus> _sessionSyncStatus(String session) =>
|
|
3969
|
+
_eventPlane._syncStatusBySession
|
|
3970
|
+
.putIfAbsent(
|
|
3971
|
+
session,
|
|
3972
|
+
() => StreamController<SyncStatus>.broadcast(
|
|
3973
|
+
onListen: () => _eventPlane._startSessionStatusPoll(session),
|
|
3974
|
+
onCancel: _eventPlane._stopStatusPollIfUnused,
|
|
3975
|
+
),
|
|
3976
|
+
)
|
|
3977
|
+
.stream;
|
|
3822
3978
|
|
|
3823
3979
|
SyncStatus _currentSessionStatus(String session) =>
|
|
3824
3980
|
_eventPlane._statusBySession[session] ?? const SyncStatus();
|
|
@@ -4016,14 +4172,13 @@ class NomosBridge {
|
|
|
4016
4172
|
// Counters prove custody debt, but they do not prove that a failed or
|
|
4017
4173
|
// offline transport recovered. Only a subsequent successful sync/link
|
|
4018
4174
|
// event may clear those lifecycle states.
|
|
4019
|
-
final phase =
|
|
4020
|
-
current.transfer != null ||
|
|
4175
|
+
final phase = current.transfer != null ||
|
|
4021
4176
|
current.phase == SyncPhase.error ||
|
|
4022
4177
|
current.phase == SyncPhase.offline
|
|
4023
4178
|
? current.phase
|
|
4024
4179
|
: (behind > 0
|
|
4025
|
-
|
|
4026
|
-
|
|
4180
|
+
? SyncPhase.behind
|
|
4181
|
+
: (ahead > 0 ? SyncPhase.syncing : SyncPhase.idle));
|
|
4027
4182
|
_pushSessionStatus(
|
|
4028
4183
|
session,
|
|
4029
4184
|
current.copyWith(
|
|
@@ -4139,36 +4294,39 @@ class NomosBridge {
|
|
|
4139
4294
|
String? session,
|
|
4140
4295
|
String? principal,
|
|
4141
4296
|
int? intervalMs,
|
|
4142
|
-
]) =>
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4297
|
+
]) =>
|
|
4298
|
+
_watchStream(
|
|
4299
|
+
kind: 'query',
|
|
4300
|
+
queryId: queryId,
|
|
4301
|
+
params: params,
|
|
4302
|
+
session: session,
|
|
4303
|
+
principal: principal,
|
|
4304
|
+
intervalMs: intervalMs,
|
|
4305
|
+
);
|
|
4150
4306
|
Stream<List<dynamic>> watchById(
|
|
4151
4307
|
String id, [
|
|
4152
4308
|
String? session,
|
|
4153
4309
|
String? principal,
|
|
4154
|
-
]) =>
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4310
|
+
]) =>
|
|
4311
|
+
_watchStream(
|
|
4312
|
+
kind: 'byId',
|
|
4313
|
+
id: id,
|
|
4314
|
+
session: session,
|
|
4315
|
+
principal: principal,
|
|
4316
|
+
);
|
|
4160
4317
|
|
|
4161
4318
|
/// Reactive SPATIAL read: re-emits the R*Tree probe's rows whenever they change locally.
|
|
4162
4319
|
Stream<List<dynamic>> watchSpatial(
|
|
4163
4320
|
String spatialId,
|
|
4164
4321
|
NomosBbox bbox, [
|
|
4165
4322
|
String? session,
|
|
4166
|
-
]) =>
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4323
|
+
]) =>
|
|
4324
|
+
_watchStream(
|
|
4325
|
+
kind: 'spatial',
|
|
4326
|
+
spatialId: spatialId,
|
|
4327
|
+
bbox: bbox,
|
|
4328
|
+
session: session,
|
|
4329
|
+
);
|
|
4172
4330
|
|
|
4173
4331
|
/// Reactive PENDING-CONFLICTS read: re-emits the maintained Surfaced "decisions needed"
|
|
4174
4332
|
/// rows whenever the index changes (derived, not drained — a resolution write clears its row).
|
|
@@ -4181,12 +4339,13 @@ class NomosBridge {
|
|
|
4181
4339
|
Future<Map<String, Object?>> checkCompatibility(
|
|
4182
4340
|
Map<String, Object?> requirement, [
|
|
4183
4341
|
Map<String, Object?>? offer,
|
|
4184
|
-
]) async =>
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4342
|
+
]) async =>
|
|
4343
|
+
_asMap(
|
|
4344
|
+
await _call('checkCompatibility', {
|
|
4345
|
+
'requirement': requirement,
|
|
4346
|
+
if (offer != null) 'offer': offer,
|
|
4347
|
+
}),
|
|
4348
|
+
);
|
|
4190
4349
|
|
|
4191
4350
|
/// THE COMPAT SUPERPOWER (#72b): compute the LIVE per-member verdict for this client's baked
|
|
4192
4351
|
/// [compat] manifest + structural [requirement] against the workspace's on-ledger offer. The
|
|
@@ -4432,6 +4591,7 @@ class NomosBridge {
|
|
|
4432
4591
|
if (!_flameEvents.isClosed) await _flameEvents.close();
|
|
4433
4592
|
if (!_spanEvents.isClosed) await _spanEvents.close();
|
|
4434
4593
|
if (!_transferEvents.isClosed) await _transferEvents.close();
|
|
4594
|
+
if (!_kernelProgressEvents.isClosed) await _kernelProgressEvents.close();
|
|
4435
4595
|
final sessionStatuses = _syncStatusBySession.values.toList(growable: false);
|
|
4436
4596
|
_syncStatusBySession.clear();
|
|
4437
4597
|
for (final status in sessionStatuses) {
|
|
@@ -4514,8 +4674,7 @@ class NomosSession extends NomosBridge {
|
|
|
4514
4674
|
final modelChange = parent._sessionModelChangeHandlers[this.workspace];
|
|
4515
4675
|
final opened = parent.acquireSession(
|
|
4516
4676
|
workspace,
|
|
4517
|
-
ifAbsentCredentials:
|
|
4518
|
-
ifAbsentCredentials ??
|
|
4677
|
+
ifAbsentCredentials: ifAbsentCredentials ??
|
|
4519
4678
|
(retained == null ? null : retained.credentials),
|
|
4520
4679
|
authorSecret:
|
|
4521
4680
|
authorSecret ?? parent._sessionAuthorSecrets[this.workspace],
|
|
@@ -4617,14 +4776,15 @@ class NomosSession extends NomosBridge {
|
|
|
4617
4776
|
String? authToken,
|
|
4618
4777
|
bool allowUnborn = false,
|
|
4619
4778
|
String? authorSecret,
|
|
4620
|
-
}) =>
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4779
|
+
}) =>
|
|
4780
|
+
parent.follow(
|
|
4781
|
+
ref,
|
|
4782
|
+
cloud: cloud,
|
|
4783
|
+
clientId: clientId,
|
|
4784
|
+
authToken: authToken,
|
|
4785
|
+
allowUnborn: allowUnborn,
|
|
4786
|
+
authorSecret: authorSecret ?? parent._sessionAuthorSecrets[workspace],
|
|
4787
|
+
);
|
|
4628
4788
|
|
|
4629
4789
|
@override
|
|
4630
4790
|
Future<Object?> _call(String op, [Map<String, Object?> args = const {}]) =>
|
|
@@ -4717,11 +4877,11 @@ class _BridgeForeignWorkspaceReader implements NomosForeignWorkspaceReader {
|
|
|
4717
4877
|
// workspace reads empty (→ ForeignNotFound) rather than throwing. A genuinely unreachable cloud throws,
|
|
4718
4878
|
// which resolveResult classifies as ForeignUnreachable.
|
|
4719
4879
|
Future<void> _ensureMounted() => _mounting ??= _bridge.connect(
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4880
|
+
cloud: _cloud,
|
|
4881
|
+
workspace: _workspace,
|
|
4882
|
+
session: _workspace,
|
|
4883
|
+
allowUnborn: true,
|
|
4884
|
+
);
|
|
4725
4885
|
|
|
4726
4886
|
@override
|
|
4727
4887
|
Future<List<Map<String, dynamic>>> queryById(String id) async {
|