@agentshouse/mdruntime 0.1.0-c89fa07b05d4b → 0.1.0-c8bedb8b11a62

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 (64) hide show
  1. package/AGENTS.md +24 -21
  2. package/dist/acquisition.d.ts +14 -0
  3. package/dist/acquisition.d.ts.map +1 -0
  4. package/dist/acquisition.js +24 -0
  5. package/dist/adapter.d.ts +9 -7
  6. package/dist/adapter.d.ts.map +1 -1
  7. package/dist/chat/kit.d.ts +2 -1
  8. package/dist/chat/kit.d.ts.map +1 -1
  9. package/dist/chat/kit.js +2 -2
  10. package/dist/chat/list.d.ts +2 -1
  11. package/dist/chat/list.d.ts.map +1 -1
  12. package/dist/chat/list.js +4 -3
  13. package/dist/chat/messages.d.ts +1 -0
  14. package/dist/chat/messages.d.ts.map +1 -1
  15. package/dist/chat/messages.js +4 -0
  16. package/dist/chat/ready.d.ts +1 -0
  17. package/dist/chat/ready.d.ts.map +1 -1
  18. package/dist/chat/ready.js +2 -2
  19. package/dist/client.d.ts +5 -1
  20. package/dist/client.d.ts.map +1 -1
  21. package/dist/client.js +2 -0
  22. package/dist/connection.d.ts +13 -2
  23. package/dist/connection.d.ts.map +1 -1
  24. package/dist/connection.js +401 -20
  25. package/dist/conversation.d.ts +1 -0
  26. package/dist/conversation.d.ts.map +1 -1
  27. package/dist/conversation.js +3 -0
  28. package/dist/durability.d.ts +7 -1
  29. package/dist/durability.d.ts.map +1 -1
  30. package/dist/host.d.ts +10 -2
  31. package/dist/host.d.ts.map +1 -1
  32. package/dist/host.js +35 -6
  33. package/dist/index.d.ts +3 -2
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/memory.d.ts +2 -1
  36. package/dist/memory.d.ts.map +1 -1
  37. package/dist/memory.js +6 -1
  38. package/dist/model.d.ts +3 -2
  39. package/dist/model.d.ts.map +1 -1
  40. package/dist/model.js +1 -1
  41. package/dist/query.d.ts +2 -1
  42. package/dist/query.d.ts.map +1 -1
  43. package/dist/reach.d.ts +6 -2
  44. package/dist/reach.d.ts.map +1 -1
  45. package/dist/reach.js +15 -3
  46. package/dist/react.d.ts +2 -0
  47. package/dist/react.d.ts.map +1 -1
  48. package/dist/readiness.d.ts +53 -0
  49. package/dist/readiness.d.ts.map +1 -0
  50. package/dist/readiness.js +33 -0
  51. package/dist/room-draft.d.ts +1 -1
  52. package/dist/room-draft.d.ts.map +1 -1
  53. package/dist/search.d.ts +1 -1
  54. package/dist/search.d.ts.map +1 -1
  55. package/dist/search.js +4 -6
  56. package/dist/status.d.ts +4 -0
  57. package/dist/status.d.ts.map +1 -1
  58. package/dist/store.d.ts +7 -0
  59. package/dist/store.d.ts.map +1 -1
  60. package/dist/store.js +32 -9
  61. package/dist/wire.d.ts +136 -32
  62. package/dist/wire.d.ts.map +1 -1
  63. package/dist/wire.js +48 -31
  64. package/package.json +1 -1
@@ -21,6 +21,8 @@ import { applyReturned, compositionOf, confirmedOf, landed, readReclassified, re
21
21
  import { binaryBase, changedParts, changesOf, held, heldFile, joinedDraft, readParts, returnedBy, returnedInto, staleWrite, underneath, withdrawnDrafts, } from './room-draft.js';
22
22
  import { same, saving, STARTING, transferring, } from './status.js';
23
23
  import { detectStorageLoss, nextGeneration } from './durability.js';
24
+ import { demanded, narrower, WHOLE_DEMAND, wider } from './acquisition.js';
25
+ import { readinessOf, SETTLED_ROOMS, } from './readiness.js';
24
26
  import { FencedMap } from './fence.js';
25
27
  import { tally } from './shared.js';
26
28
  import { inChunks, ROW_WEIGHT, yielded } from './turn.js';
@@ -166,8 +168,18 @@ export function composeRuntime(options) {
166
168
  const memberListeners = new Set();
167
169
  const prepared = new Set();
168
170
  const projecting = new Set();
169
- const demanded = new Set();
171
+ const unpreparedDemand = new Set();
172
+ const unindexable = new Set();
170
173
  let preparing = false;
174
+ let demand = WHOLE_DEMAND;
175
+ let narrowing = null;
176
+ let targets = {};
177
+ let persistence = options.shell.persistence;
178
+ let capacity = null;
179
+ const paused = new Set();
180
+ let rechecking = null;
181
+ const readinessWatches = new Set();
182
+ let readinessQueued = false;
171
183
  const catching = new Set();
172
184
  const transfers = new FencedMap(isFenced);
173
185
  const conversations = createConversationStore({
@@ -267,9 +279,16 @@ export function composeRuntime(options) {
267
279
  returned: structuredClone(record.returned),
268
280
  });
269
281
  }
282
+ for (const [room, refusal] of sourceRefusals)
283
+ attentionRequired.push({ room, refusal: structuredClone(refusal) });
270
284
  attentionRequired.sort((left, right) => {
271
285
  if (left.room !== right.room)
272
286
  return left.room < right.room ? -1 : 1;
287
+ if ('refusal' in left || 'refusal' in right) {
288
+ if ('refusal' in left && 'refusal' in right)
289
+ return 0;
290
+ return 'refusal' in left ? -1 : 1;
291
+ }
273
292
  if (left.path !== right.path)
274
293
  return left.path < right.path ? -1 : 1;
275
294
  return 0;
@@ -318,15 +337,18 @@ export function composeRuntime(options) {
318
337
  }
319
338
  function emitRooms() {
320
339
  readSide.recover();
340
+ queueReadiness();
321
341
  const value = listed();
322
342
  for (const listener of roomListeners)
323
343
  listener(value);
324
344
  }
325
345
  function emitRefusal() {
346
+ queueReadiness();
326
347
  for (const listener of refusalListeners)
327
348
  listener(refusal);
328
349
  }
329
350
  function emitStorageLoss() {
351
+ queueReadiness();
330
352
  for (const listener of storageLossListeners)
331
353
  listener(storageLoss);
332
354
  }
@@ -343,7 +365,8 @@ export function composeRuntime(options) {
343
365
  function show() {
344
366
  if (closed)
345
367
  return;
346
- const next = status.state === 'synced' && !conversations.current()
368
+ queueReadiness();
369
+ const next = status.state === 'synced' && demand.conversations && !conversations.current()
347
370
  ? { ...status, state: 'syncing' }
348
371
  : status;
349
372
  if (same(shown, next))
@@ -420,13 +443,19 @@ export function composeRuntime(options) {
420
443
  }
421
444
  conversations.changed(mergeChangeSets(entered));
422
445
  readSide.recover();
446
+ queueReadiness();
423
447
  }
424
448
  function unprepared() {
425
- return [...demanded, ...replicas.keys()].find((locator) => !prepared.has(locator));
449
+ return [...unpreparedDemand, ...replicas.keys()].find((locator) => !prepared.has(locator) && !unindexable.has(locator));
450
+ }
451
+ function unbuilt(locator, error) {
452
+ unindexable.add(locator);
453
+ journal.record(`index failed ${locator} ${error instanceof Error ? error.name : 'unknown'}`);
454
+ queueReadiness();
426
455
  }
427
456
  function prepare(locators = []) {
428
457
  for (const locator of locators)
429
- demanded.add(locator);
458
+ unpreparedDemand.add(locator);
430
459
  if (preparing)
431
460
  return;
432
461
  preparing = true;
@@ -436,8 +465,8 @@ export function composeRuntime(options) {
436
465
  await yielded();
437
466
  if (closed)
438
467
  return;
439
- demanded.delete(waiting);
440
- await prepareReplica(waiting);
468
+ unpreparedDemand.delete(waiting);
469
+ await prepareReplica(waiting).catch((error) => unbuilt(waiting, error));
441
470
  }
442
471
  }
443
472
  finally {
@@ -495,6 +524,292 @@ export function composeRuntime(options) {
495
524
  if (waiting.length > 0)
496
525
  prepare(waiting.map(({ room }) => room));
497
526
  }
527
+ function acquires(room) {
528
+ return (demanded(demand, room) ||
529
+ (replicas.get(room.locator)?.queue.length ?? 0) > 0 ||
530
+ queuesWithoutReplica.has(room.locator));
531
+ }
532
+ function acquiredLocators() {
533
+ return [...discovered.values()]
534
+ .filter(acquires)
535
+ .map((room) => room.locator)
536
+ .sort();
537
+ }
538
+ function heldDemand(acquired) {
539
+ if (demand.whole)
540
+ return null;
541
+ return { rooms: acquired.map((room) => room.locator), conversations: demand.conversations };
542
+ }
543
+ function applyDemand(next) {
544
+ const before = acquiredLocators();
545
+ const conversed = demand.conversations;
546
+ demand = next;
547
+ const after = acquiredLocators();
548
+ queueReadiness();
549
+ if (!conversed && next.conversations)
550
+ scheduleCurrency();
551
+ if (JSON.stringify(before) === JSON.stringify(after) && conversed === next.conversations)
552
+ return;
553
+ journal.record(`acquisition ${next.whole ? 'whole' : after.length}`);
554
+ if (options.adapter.roomStream === undefined)
555
+ void sync('unstreamed');
556
+ else if (socketOpen)
557
+ options.adapter.roomStream.reopen();
558
+ }
559
+ function acquire(next) {
560
+ if (closed)
561
+ return;
562
+ if (narrowing !== null)
563
+ clearTimeout(narrowing);
564
+ narrowing = null;
565
+ const widening = wider(next, demand);
566
+ if (!widening && !narrower(next, demand))
567
+ return;
568
+ if (widening || !roomsListed) {
569
+ applyDemand(next);
570
+ return;
571
+ }
572
+ // A narrower demand waits five seconds, so a tab that reloads returns before the stream reopens twice.
573
+ narrowing = setTimeout(() => {
574
+ narrowing = null;
575
+ applyDemand(next);
576
+ }, 5_000);
577
+ }
578
+ function requiredRooms(reach) {
579
+ if (reach === null)
580
+ return { listed: roomsListed, rooms: [...rooms.keys()].sort() };
581
+ const named = [...new Set(reach.rooms)].sort();
582
+ if (roomsListed)
583
+ return { listed: true, rooms: named.filter((room) => rooms.has(room)) };
584
+ return { listed: named.every((room) => replicas.has(room)), rooms: named };
585
+ }
586
+ function roomState(locator, search) {
587
+ if (sourceRefusals.has(locator))
588
+ return 'refused';
589
+ const listedRoom = discovered.get(locator);
590
+ if (roomsListed &&
591
+ (listedRoom === undefined ||
592
+ listedRoom.replica.admission !== 'admitted' ||
593
+ unreadable.has(locator)))
594
+ return 'unadmitted';
595
+ if (!replicas.has(locator))
596
+ return 'acquiring';
597
+ if (!prepared.has(locator))
598
+ return 'preparing';
599
+ if (search && !searchIndex.rooms().has(locator))
600
+ return 'indexing';
601
+ return 'complete';
602
+ }
603
+ function conversationCoverage(reach, search) {
604
+ const declared = reach === null || reach.conversations;
605
+ if (!declared || options.adapter.conversations === undefined || declaredAgents() === undefined)
606
+ return 'undeclared';
607
+ if (!conversations.known() || !conversations.current())
608
+ return 'acquiring';
609
+ if (!search)
610
+ return 'complete';
611
+ const unindexed = conversations
612
+ .searchable()
613
+ .filter((one) => conversations.acquired(one) && !searchIndex.conversations().has(one));
614
+ if (unindexed.length === 0)
615
+ return 'complete';
616
+ index([], unindexed);
617
+ return 'indexing';
618
+ }
619
+ function connectionOf() {
620
+ if (socketOpen)
621
+ return 'connected';
622
+ if (status.providerUnreachable)
623
+ return 'unreachable';
624
+ return options.adapter.roomStream === undefined && status.lastCompleted !== null
625
+ ? 'connected'
626
+ : 'connecting';
627
+ }
628
+ function refusedIn(locators) {
629
+ return locators.flatMap((room) => {
630
+ const held = sourceRefusals.get(room);
631
+ if (held === undefined)
632
+ return [];
633
+ const { file, location } = held.fields;
634
+ return [
635
+ {
636
+ room,
637
+ file: typeof file === 'string' ? file : '',
638
+ rule: held.code,
639
+ location: typeof location === 'string' ? location : null,
640
+ },
641
+ ];
642
+ });
643
+ }
644
+ function readinessFor(port, reach) {
645
+ const required = requiredRooms(reach);
646
+ const target = targets[port.target];
647
+ const search = target?.search === true;
648
+ const coverage = required.rooms.map((room) => ({ room, state: roomState(room, search) }));
649
+ const building = (state) => coverage
650
+ .filter((room) => room.state === state && !unindexable.has(room.room))
651
+ .map(({ room }) => room);
652
+ const preparing = building('preparing');
653
+ if (preparing.length > 0)
654
+ prepare(preparing);
655
+ const indexing = building('indexing');
656
+ if (indexing.length > 0)
657
+ index(indexing, []);
658
+ return readinessOf({
659
+ code: port.code,
660
+ coverage: {
661
+ listed: required.listed,
662
+ rooms: coverage,
663
+ conversations: conversationCoverage(reach, search),
664
+ search: search ? 'required' : 'unused',
665
+ },
666
+ persistence,
667
+ connection: connectionOf(),
668
+ house: refusal !== null ? 'refused' : writesHeld() ? 'write-halted' : 'open',
669
+ capacity: capacity !== null &&
670
+ coverage.some(({ room, state }) => paused.has(room) || !SETTLED_ROOMS.has(state))
671
+ ? capacity
672
+ : port.capacity,
673
+ refused: refusedIn(required.rooms),
674
+ lost: storageLoss !== null,
675
+ established: target?.established === true,
676
+ });
677
+ }
678
+ function keepTarget(name, change) {
679
+ const held = targets[name] ?? { established: false, search: false };
680
+ const next = { ...held, ...change };
681
+ if (next.established === held.established && next.search === held.search)
682
+ return;
683
+ targets = { ...targets, [name]: next };
684
+ queueReadiness();
685
+ void durably((local) => local.keepTargets(targets)).catch(() => {
686
+ journal.record(`target record failed ${name}`);
687
+ });
688
+ }
689
+ function deliverReadiness(watch) {
690
+ if (!readinessWatches.has(watch))
691
+ return;
692
+ const readiness = readinessFor(watch.port, watch.reach);
693
+ if (readiness.ready && targets[watch.port.target]?.established !== true) {
694
+ keepTarget(watch.port.target, { established: true });
695
+ }
696
+ const shown = JSON.stringify(readiness);
697
+ if (shown === watch.last)
698
+ return;
699
+ watch.last = shown;
700
+ watch.deliver(structuredClone(readiness));
701
+ }
702
+ function queueReadiness() {
703
+ if (readinessQueued || closed || readinessWatches.size === 0)
704
+ return;
705
+ readinessQueued = true;
706
+ queueMicrotask(() => {
707
+ readinessQueued = false;
708
+ if (closed)
709
+ return;
710
+ for (const watch of [...readinessWatches])
711
+ deliverReadiness(watch);
712
+ });
713
+ }
714
+ function watchReadiness(port, reach, deliver) {
715
+ const watch = { port, reach, deliver, last: null };
716
+ readinessWatches.add(watch);
717
+ void openLocal().then(() => deliverReadiness(watch), () => deliverReadiness(watch));
718
+ return () => {
719
+ readinessWatches.delete(watch);
720
+ };
721
+ }
722
+ function searching(target) {
723
+ if (targets[target]?.search !== true)
724
+ keepTarget(target, { search: true });
725
+ }
726
+ function layerBytes(layer) {
727
+ let bytes = 0;
728
+ for (const [path, text] of Object.entries(layer.documents))
729
+ bytes += (path.length + text.length) * 2;
730
+ return bytes;
731
+ }
732
+ function grownBy(next, previous) {
733
+ if (previous === undefined)
734
+ return layerBytes(next);
735
+ const documents = Object.fromEntries(Object.entries(next.documents).filter(([path, text]) => previous.documents[path] !== text));
736
+ return layerBytes({ ...next, documents });
737
+ }
738
+ function exceeded(error) {
739
+ return (typeof error === 'object' &&
740
+ error !== null &&
741
+ error.name === 'QuotaExceededError');
742
+ }
743
+ async function estimated() {
744
+ const estimate = options.shell.estimate === undefined ? null : await options.shell.estimate();
745
+ return estimate === null ? null : Math.max(0, estimate.quota - estimate.usage);
746
+ }
747
+ function pause(locator, cause) {
748
+ paused.add(locator);
749
+ capacity = cause;
750
+ journal.record(`capacity ${cause.cause} ${locator} ${cause.needed} ${cause.available ?? '-'}`);
751
+ queueReadiness();
752
+ if (rechecking !== null)
753
+ return;
754
+ // A paused preparation measures the browser's space again each minute, so space freed elsewhere resumes it without a retry.
755
+ rechecking = setInterval(() => {
756
+ void recheck();
757
+ }, 60_000);
758
+ }
759
+ async function recheck() {
760
+ const held = capacity;
761
+ if (held === null || closed)
762
+ return;
763
+ const available = await estimated().catch(() => null);
764
+ if (available === null || available < (held.needed ?? 0))
765
+ return;
766
+ if (held.cause === 'quota-exceeded' && (held.available === null || available <= held.available))
767
+ return;
768
+ resume();
769
+ }
770
+ function resume() {
771
+ if (rechecking !== null)
772
+ clearInterval(rechecking);
773
+ rechecking = null;
774
+ if (paused.size === 0 && capacity === null)
775
+ return;
776
+ paused.clear();
777
+ capacity = null;
778
+ journal.record('capacity resumed');
779
+ queueReadiness();
780
+ if (!closed && authorization !== undefined)
781
+ void sync('unstreamed');
782
+ }
783
+ async function reclaimed(local) {
784
+ const unreferenced = [...transfers.keys()].filter((locator) => {
785
+ const room = discovered.get(locator);
786
+ return room === undefined || !acquires(room);
787
+ });
788
+ for (const locator of unreferenced) {
789
+ await local.abandon(locator);
790
+ transfers.delete(locator);
791
+ }
792
+ if (unreferenced.length > 0)
793
+ journal.record(`capacity reclaimed ${unreferenced.length}`);
794
+ return unreferenced.length > 0;
795
+ }
796
+ async function admitted(local, locator, needed) {
797
+ if (paused.has(locator))
798
+ return false;
799
+ let available = await estimated().catch(() => null);
800
+ if (available === null || available >= needed)
801
+ return true;
802
+ if (await reclaimed(local))
803
+ available = await estimated().catch(() => null);
804
+ if (available === null || available >= needed)
805
+ return true;
806
+ pause(locator, { cause: 'insufficient-space', needed, available });
807
+ return false;
808
+ }
809
+ async function exceededOn(locator, bytes) {
810
+ const available = await estimated().catch(() => null);
811
+ pause(locator, { cause: 'quota-exceeded', needed: bytes, available });
812
+ }
498
813
  async function rowsQuery(plan) {
499
814
  const before = covered(plan);
500
815
  awaitPreparation(before);
@@ -522,10 +837,12 @@ export function composeRuntime(options) {
522
837
  }
523
838
  }
524
839
  function indexNext() {
525
- const room = [...roomsToIndex].find((locator) => !searchIndex.rooms().has(locator));
840
+ const room = [...roomsToIndex].find((locator) => !searchIndex.rooms().has(locator) && !unindexable.has(locator));
526
841
  if (room !== undefined) {
527
842
  roomsToIndex.delete(room);
528
- return () => (prepared.has(room) ? searchIndex.indexRoom(room) : undefined);
843
+ return () => prepared.has(room)
844
+ ? searchIndex.indexRoom(room).catch((error) => unbuilt(room, error))
845
+ : undefined;
529
846
  }
530
847
  const conversation = [...conversationsToIndex].find((one) => !searchIndex.conversations().has(one));
531
848
  if (conversation === undefined)
@@ -546,6 +863,7 @@ export function composeRuntime(options) {
546
863
  for (let unit = indexNext(); unit !== null && !closed; unit = indexNext()) {
547
864
  await yielded();
548
865
  await unit();
866
+ queueReadiness();
549
867
  }
550
868
  }
551
869
  finally {
@@ -719,6 +1037,8 @@ export function composeRuntime(options) {
719
1037
  unreadable.delete(locator);
720
1038
  stale.delete(locator);
721
1039
  repairable.delete(locator);
1040
+ unindexable.delete(locator);
1041
+ paused.delete(locator);
722
1042
  scopedRefusals.delete(locator);
723
1043
  sourceRefusals.delete(locator);
724
1044
  declarationRefusals.delete(locator);
@@ -772,7 +1092,7 @@ export function composeRuntime(options) {
772
1092
  return model;
773
1093
  }
774
1094
  async function hydrate(opened) {
775
- const [records, queues, kept, presented, held, chatDrafts, lastCompleted, declared, named, known, staged,] = await Promise.all([
1095
+ const [records, queues, kept, presented, held, chatDrafts, lastCompleted, declared, named, known, staged, heldTargets,] = await Promise.all([
776
1096
  opened.all(),
777
1097
  opened.queues(),
778
1098
  opened.roomDrafts(),
@@ -784,7 +1104,9 @@ export function composeRuntime(options) {
784
1104
  opened.capabilities(),
785
1105
  opened.member(),
786
1106
  opened.transfers(),
1107
+ opened.targets(),
787
1108
  ]);
1109
+ targets = heldTargets;
788
1110
  transfers.clear();
789
1111
  for (const record of staged)
790
1112
  transfers.set(record.locator, record);
@@ -1030,7 +1352,18 @@ export function composeRuntime(options) {
1030
1352
  const accepted = pendings.get(batch.id)?.accepted;
1031
1353
  return (accepted !== undefined && options.adapter.reached(room.locator, roomVersion, accepted));
1032
1354
  });
1033
- const set = (await settle(local, room.locator, prior, confirmed, queue.filter((batch) => !accepted.includes(batch)), { room: true, ...(stored === undefined ? {} : { stored }) }))();
1355
+ let settled;
1356
+ try {
1357
+ settled = await settle(local, room.locator, prior, confirmed, queue.filter((batch) => !accepted.includes(batch)), { room: true, ...(stored === undefined ? {} : { stored }) });
1358
+ }
1359
+ catch (error) {
1360
+ if (!exceeded(error))
1361
+ throw error;
1362
+ await exceededOn(room.locator, grownBy(confirmed.record, prior?.confirmed.record));
1363
+ caughtUpTo(room.locator);
1364
+ return true;
1365
+ }
1366
+ const set = settled();
1034
1367
  if (stored !== undefined) {
1035
1368
  transfers.delete(room.locator);
1036
1369
  catching.delete(room.locator);
@@ -1038,6 +1371,7 @@ export function composeRuntime(options) {
1038
1371
  for (const batch of accepted)
1039
1372
  pendings.delete(batch.id);
1040
1373
  stale.delete(room.locator);
1374
+ unindexable.delete(room.locator);
1041
1375
  if (unreadable.delete(room.locator))
1042
1376
  scopedRefusals.delete(room.locator);
1043
1377
  sourceRefusals.delete(room.locator);
@@ -1446,7 +1780,18 @@ export function composeRuntime(options) {
1446
1780
  stage: (record, page) => exclusive(async () => {
1447
1781
  if (!live())
1448
1782
  return false;
1449
- await local.stage(record, page);
1783
+ const bytes = layerBytes(page);
1784
+ if (!(await admitted(local, record.locator, bytes)))
1785
+ return false;
1786
+ try {
1787
+ await local.stage(record, page);
1788
+ }
1789
+ catch (error) {
1790
+ if (!exceeded(error))
1791
+ throw error;
1792
+ await exceededOn(record.locator, bytes);
1793
+ return false;
1794
+ }
1450
1795
  transfers.set(record.locator, record);
1451
1796
  readSide.recover();
1452
1797
  return true;
@@ -1496,7 +1841,7 @@ export function composeRuntime(options) {
1496
1841
  }
1497
1842
  async function advance(room, local, authorized, live) {
1498
1843
  await referenced(room.locator);
1499
- if (!live())
1844
+ if (!live() || paused.has(room.locator))
1500
1845
  return unconfirmed;
1501
1846
  const held = replicas.get(room.locator);
1502
1847
  if (held !== undefined) {
@@ -1589,6 +1934,7 @@ export function composeRuntime(options) {
1589
1934
  unreadable.add(room.locator);
1590
1935
  list(room, false);
1591
1936
  emitRooms();
1937
+ moveTo({ ...status, ...attention() });
1592
1938
  journal.record(`source refused ${room.locator} ${refusal.rule} ${refusal.file}`);
1593
1939
  }
1594
1940
  function leaving(batch, result) {
@@ -1976,15 +2322,16 @@ export function composeRuntime(options) {
1976
2322
  });
1977
2323
  }
1978
2324
  async function synchronize(listed, local, progress, attempt, live) {
1979
- const prepared = await Promise.allSettled(listed.map((room) => prepareRoom(room, local, progress)));
2325
+ const acquired = listed.filter(acquires);
2326
+ const prepared = await Promise.allSettled(acquired.map((room) => prepareRoom(room, local, progress)));
1980
2327
  if (!live())
1981
2328
  return;
1982
- const ready = listed.filter((_, index) => {
2329
+ const ready = acquired.filter((_, index) => {
1983
2330
  const outcome = prepared[index];
1984
2331
  return outcome?.status === 'fulfilled' && outcome.value;
1985
2332
  });
1986
2333
  if (attempt === 'opened') {
1987
- options.adapter.roomStream?.hold(heldRooms(ready));
2334
+ options.adapter.roomStream?.hold(heldRooms(ready), heldDemand(acquired));
1988
2335
  handed = true;
1989
2336
  }
1990
2337
  discovering?.release();
@@ -2449,7 +2796,8 @@ export function composeRuntime(options) {
2449
2796
  if (status.providerUnreachable)
2450
2797
  conversations.reconnected();
2451
2798
  const reading = { complete: true, reachable: true };
2452
- await conversations.catchUp(reading);
2799
+ if (demand.conversations)
2800
+ await conversations.catchUp(reading);
2453
2801
  if (progress.reachable && reading.reachable)
2454
2802
  await conversations.resend(reading);
2455
2803
  if (!reading.reachable) {
@@ -2512,7 +2860,7 @@ export function composeRuntime(options) {
2512
2860
  return;
2513
2861
  const room = discovered.get(locator);
2514
2862
  const held = replicas.get(locator);
2515
- if (room === undefined || room.replica.admission !== 'admitted')
2863
+ if (room === undefined || room.replica.admission !== 'admitted' || !acquires(room))
2516
2864
  return;
2517
2865
  if (held !== undefined &&
2518
2866
  (held.confirmed.record.replica.selection !== room.replica.selection ||
@@ -2589,6 +2937,7 @@ export function composeRuntime(options) {
2589
2937
  haltedRefusal = false;
2590
2938
  if (mode !== serviceMode) {
2591
2939
  serviceMode = mode;
2940
+ queueReadiness();
2592
2941
  journal.record(`service mode ${mode}`);
2593
2942
  for (const listener of serviceModeListeners)
2594
2943
  listener(mode);
@@ -2601,6 +2950,8 @@ export function composeRuntime(options) {
2601
2950
  if (stream === undefined || withdrawConversations !== undefined)
2602
2951
  return;
2603
2952
  withdrawConversations = stream.observe((event) => {
2953
+ if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted')
2954
+ queueReadiness();
2604
2955
  if (conversations.receive(event)) {
2605
2956
  void ready.then(scheduleCurrency);
2606
2957
  return;
@@ -2623,7 +2974,7 @@ export function composeRuntime(options) {
2623
2974
  if (!live())
2624
2975
  return;
2625
2976
  await conversations.reconcile();
2626
- if (conversations.current()) {
2977
+ if (conversations.current() || !demand.conversations) {
2627
2978
  show();
2628
2979
  return;
2629
2980
  }
@@ -2641,6 +2992,11 @@ export function composeRuntime(options) {
2641
2992
  if (closed)
2642
2993
  return;
2643
2994
  closed = true;
2995
+ if (narrowing !== null)
2996
+ clearTimeout(narrowing);
2997
+ if (rechecking !== null)
2998
+ clearInterval(rechecking);
2999
+ readinessWatches.clear();
2644
3000
  withdrawStream?.();
2645
3001
  withdrawStream = undefined;
2646
3002
  withdrawConversations?.();
@@ -2781,6 +3137,9 @@ export function composeRuntime(options) {
2781
3137
  presentationListeners.delete(listener);
2782
3138
  };
2783
3139
  }
3140
+ if (topic === 'readiness') {
3141
+ return watchReadiness({ target: '', code: 'stored', capacity: null }, null, deliver);
3142
+ }
2784
3143
  if (topic === 'rooms') {
2785
3144
  roomListeners.add(deliver);
2786
3145
  deliver(listed());
@@ -2947,7 +3306,13 @@ export function composeRuntime(options) {
2947
3306
  return journal.export();
2948
3307
  },
2949
3308
  persistence() {
2950
- return options.shell.persistentStorage;
3309
+ return persistence === 'granted';
3310
+ },
3311
+ async retry() {
3312
+ journal.record('retry');
3313
+ unindexable.clear();
3314
+ queueReadiness();
3315
+ resume();
2951
3316
  },
2952
3317
  observe: observing(watch),
2953
3318
  async reauthorize() {
@@ -2974,6 +3339,22 @@ export function composeRuntime(options) {
2974
3339
  query: (plan) => rowsQuery(plan),
2975
3340
  watchPlan,
2976
3341
  search: searched,
3342
+ searching,
3343
+ watchReadiness,
3344
+ };
3345
+ return {
3346
+ md,
3347
+ stop,
3348
+ lose: loseRooms,
3349
+ watch,
3350
+ acquire,
3351
+ persisted(answer) {
3352
+ if (answer === persistence)
3353
+ return;
3354
+ persistence = answer;
3355
+ journal.record(`persistence ${answer}`);
3356
+ queueReadiness();
3357
+ },
3358
+ within: (reach, port) => narrowed(reachable, reach, port),
2977
3359
  };
2978
- return { md, stop, lose: loseRooms, watch, within: (reach) => narrowed(reachable, reach) };
2979
3360
  }
@@ -86,6 +86,7 @@ export type ConversationStore = {
86
86
  catchUp(progress: CycleProgress): Promise<void>;
87
87
  resend(progress: CycleProgress): Promise<void>;
88
88
  current(): boolean;
89
+ known(): boolean;
89
90
  reconnected(): void;
90
91
  reopened(): void;
91
92
  watch(conversation: string, options: ConversationOptions, deliver: (view: ConversationView) => void): () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../src/conversation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../src/conversation.ts"],"names":[],"mappings":"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"}
@@ -761,6 +761,9 @@ export function createConversationStore(deps) {
761
761
  current() {
762
762
  return behind.size === 0;
763
763
  },
764
+ known() {
765
+ return listKnown;
766
+ },
764
767
  reconnected() {
765
768
  for (const conversation of replicated)
766
769
  gap(conversation);