@agentshouse/mdruntime 0.1.0-cebcd51f70ed1 → 0.1.0-cec6690d5712e
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/AGENTS.md +82 -27
- package/README.md +13 -3
- package/dist/adapter.d.ts +13 -3
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +15 -2
- package/dist/attempt.d.ts +16 -0
- package/dist/attempt.d.ts.map +1 -0
- package/dist/attempt.js +57 -0
- package/dist/bootstrap.d.ts +17 -5
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +88 -35
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +13 -7
- package/dist/change.d.ts +4 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/chat/acts.d.ts +34 -0
- package/dist/chat/acts.d.ts.map +1 -0
- package/dist/chat/acts.js +36 -0
- package/dist/chat/context.d.ts +3 -0
- package/dist/chat/context.d.ts.map +1 -0
- package/dist/chat/context.js +10 -0
- package/dist/chat/input.d.ts +9 -0
- package/dist/chat/input.d.ts.map +1 -0
- package/dist/chat/input.js +99 -0
- package/dist/chat/kit.d.ts +60 -0
- package/dist/chat/kit.d.ts.map +1 -0
- package/dist/chat/kit.js +123 -0
- package/dist/chat/lasting.d.ts +6 -0
- package/dist/chat/lasting.d.ts.map +1 -0
- package/dist/chat/lasting.js +19 -0
- package/dist/chat/list.d.ts +20 -0
- package/dist/chat/list.d.ts.map +1 -0
- package/dist/chat/list.js +59 -0
- package/dist/chat/markdown.d.ts +19 -0
- package/dist/chat/markdown.d.ts.map +1 -0
- package/dist/chat/markdown.js +187 -0
- package/dist/chat/message.d.ts +6 -0
- package/dist/chat/message.d.ts.map +1 -0
- package/dist/chat/message.js +192 -0
- package/dist/chat/messages.d.ts +21 -0
- package/dist/chat/messages.d.ts.map +1 -0
- package/dist/chat/messages.js +151 -0
- package/dist/chat/name.d.ts +6 -0
- package/dist/chat/name.d.ts.map +1 -0
- package/dist/chat/name.js +37 -0
- package/dist/chat/opened.d.ts +11 -0
- package/dist/chat/opened.d.ts.map +1 -0
- package/dist/chat/opened.js +17 -0
- package/dist/chat/overlay.d.ts +31 -0
- package/dist/chat/overlay.d.ts.map +1 -0
- package/dist/chat/overlay.js +97 -0
- package/dist/chat/parts.d.ts +57 -0
- package/dist/chat/parts.d.ts.map +1 -0
- package/dist/chat/parts.js +119 -0
- package/dist/chat/process.d.ts +20 -0
- package/dist/chat/process.d.ts.map +1 -0
- package/dist/chat/process.js +32 -0
- package/dist/chat/provider.d.ts +63 -0
- package/dist/chat/provider.d.ts.map +1 -0
- package/dist/chat/provider.js +645 -0
- package/dist/chat/question.d.ts +9 -0
- package/dist/chat/question.d.ts.map +1 -0
- package/dist/chat/question.js +80 -0
- package/dist/chat/questions.d.ts +31 -0
- package/dist/chat/questions.d.ts.map +1 -0
- package/dist/chat/questions.js +111 -0
- package/dist/chat/read.d.ts +5 -0
- package/dist/chat/read.d.ts.map +1 -0
- package/dist/chat/read.js +13 -0
- package/dist/chat/ready.d.ts +33 -0
- package/dist/chat/ready.d.ts.map +1 -0
- package/dist/chat/ready.js +91 -0
- package/dist/chat/style.d.ts +4 -0
- package/dist/chat/style.d.ts.map +1 -0
- package/dist/chat/style.js +302 -0
- package/dist/chat.d.ts +14 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +8 -0
- package/dist/client.d.ts +9 -5
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +61 -15
- package/dist/collection.d.ts +6 -11
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +56 -19
- package/dist/connection.d.ts +14 -7
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +239 -102
- package/dist/conversation.d.ts +11 -3
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +47 -7
- package/dist/expectation.d.ts +21 -0
- package/dist/expectation.d.ts.map +1 -0
- package/dist/expectation.js +70 -0
- package/dist/failure.d.ts +13 -0
- package/dist/failure.d.ts.map +1 -0
- package/dist/failure.js +19 -0
- package/dist/host.d.ts +1 -0
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +5 -8
- package/dist/identity.d.ts +2 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +10 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/memory.d.ts +2 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +31 -11
- package/dist/model.d.ts +10 -2
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +2 -0
- package/dist/open.d.ts +2 -0
- package/dist/open.d.ts.map +1 -0
- package/dist/open.js +1 -0
- package/dist/outcome.d.ts +22 -3
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +35 -1
- package/dist/overlay.d.ts +1 -1
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +19 -15
- package/dist/query.d.ts +2 -0
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +5 -31
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +21 -19
- package/dist/react.d.ts +25 -16
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +8 -6
- package/dist/refresh.d.ts +10 -0
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +33 -24
- package/dist/replica.d.ts +5 -5
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +44 -29
- package/dist/room-draft.d.ts +8 -8
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +13 -22
- package/dist/rows.d.ts +2 -0
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +7 -1
- package/dist/save.d.ts +3 -1
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +38 -21
- package/dist/search.d.ts +3 -2
- package/dist/search.d.ts.map +1 -1
- package/dist/status.d.ts +2 -2
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +0 -8
- package/dist/store.d.ts +25 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +206 -90
- package/dist/wire.d.ts +5 -3
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +13 -2
- package/package.json +17 -9
package/dist/connection.js
CHANGED
|
@@ -3,17 +3,20 @@ import { cached, modelTypes, NO_MODEL, sameModel, } from './model.js';
|
|
|
3
3
|
import { observing } from './observation.js';
|
|
4
4
|
import { filterOf, identityOf, planOf, } from './query.js';
|
|
5
5
|
import { createReadSide } from './read-side.js';
|
|
6
|
+
import { markdownPath, roomOfKey } from './rows.js';
|
|
6
7
|
import { createSearchIndex, searchPlanOf, } from './search.js';
|
|
7
8
|
import { bootstrapRoom } from './bootstrap.js';
|
|
8
9
|
import { changeSet, mergeChangeSets } from './change-set.js';
|
|
9
10
|
import { createConversationStore, } from './conversation.js';
|
|
10
|
-
import {
|
|
11
|
+
import { validRoot } from './collection.js';
|
|
12
|
+
import { runtimeError } from './failure.js';
|
|
11
13
|
import { createLog } from './log.js';
|
|
14
|
+
import { calledResult, historyAnswer, undeclared, UNCERTAIN, } from './outcome.js';
|
|
12
15
|
import { refreshRoom } from './refresh.js';
|
|
13
16
|
import { buildBatch } from './save.js';
|
|
14
|
-
import { applyReturned, compositionOf,
|
|
15
|
-
import { changedParts, changesOf, held, readParts, returnedInto, underneath, } from './room-draft.js';
|
|
16
|
-
import {
|
|
17
|
+
import { applyReturned, compositionOf, confirmedOf, landed, readReclassified, replicaOf, sameMembership, withdrawals, } from './replica.js';
|
|
18
|
+
import { changedParts, changesOf, held, readParts, returnedBy, returnedInto, staleWrite, underneath, } from './room-draft.js';
|
|
19
|
+
import { same, saving, STARTING, transferring, } from './status.js';
|
|
17
20
|
import { detectStorageLoss, nextGeneration } from './durability.js';
|
|
18
21
|
import { roomDraftRevision } from './identity.js';
|
|
19
22
|
import { beneath, normalize } from './paths.js';
|
|
@@ -34,6 +37,7 @@ function declaredSelection(room) {
|
|
|
34
37
|
selection.roots.every((other, at) => at === index || !within(other, root)));
|
|
35
38
|
}
|
|
36
39
|
export function composeRuntime(options) {
|
|
40
|
+
const localSaves = options.adapter.localSaves === true;
|
|
37
41
|
let authorization = options.authorization;
|
|
38
42
|
let opening;
|
|
39
43
|
let store;
|
|
@@ -44,6 +48,7 @@ export function composeRuntime(options) {
|
|
|
44
48
|
let limits = [];
|
|
45
49
|
let capabilities = [];
|
|
46
50
|
const rooms = new Map();
|
|
51
|
+
const counted = new Map();
|
|
47
52
|
const unreadable = new Set();
|
|
48
53
|
const models = new Map();
|
|
49
54
|
const liveModels = new Map();
|
|
@@ -82,6 +87,8 @@ export function composeRuntime(options) {
|
|
|
82
87
|
let withdrawStream;
|
|
83
88
|
const announced = new Map();
|
|
84
89
|
let underway = null;
|
|
90
|
+
let streamed = null;
|
|
91
|
+
let handed = false;
|
|
85
92
|
const submitting = new Map();
|
|
86
93
|
const answering = new Map();
|
|
87
94
|
let complete = false;
|
|
@@ -104,9 +111,14 @@ export function composeRuntime(options) {
|
|
|
104
111
|
let haltedRefusal = false;
|
|
105
112
|
let openSignals = 0;
|
|
106
113
|
const serviceModeListeners = new Set();
|
|
114
|
+
let member = null;
|
|
115
|
+
const memberListeners = new Set();
|
|
116
|
+
const materialized = new Set();
|
|
117
|
+
let everyRoom = false;
|
|
118
|
+
const transfers = new Map();
|
|
107
119
|
const conversations = createConversationStore({
|
|
108
120
|
agents: declaredAgents,
|
|
109
|
-
documents: (locator) =>
|
|
121
|
+
documents: (locator) => rowsQuery(planOf({ rooms: [locator], select: 'all' }, roomFacts())),
|
|
110
122
|
call: (name, input) => invokeAction(name, input),
|
|
111
123
|
durably,
|
|
112
124
|
stream: () => options.adapter.conversations,
|
|
@@ -182,7 +194,7 @@ export function composeRuntime(options) {
|
|
|
182
194
|
}
|
|
183
195
|
}
|
|
184
196
|
function pending() {
|
|
185
|
-
return [...replicas.values()].some((replica) => replica.queue.length > 0);
|
|
197
|
+
return !localSaves && [...replicas.values()].some((replica) => replica.queue.length > 0);
|
|
186
198
|
}
|
|
187
199
|
function attention() {
|
|
188
200
|
const attentionRequired = [];
|
|
@@ -211,8 +223,24 @@ export function composeRuntime(options) {
|
|
|
211
223
|
}
|
|
212
224
|
return { attentionRequired, reset };
|
|
213
225
|
}
|
|
226
|
+
function documentsIn(locator) {
|
|
227
|
+
const replica = replicas.get(locator);
|
|
228
|
+
if (replica === undefined)
|
|
229
|
+
return 0;
|
|
230
|
+
const roots = replica.confirmed.record.typedSelection?.roots ?? [];
|
|
231
|
+
let count = 0;
|
|
232
|
+
for (const path of Object.keys(replica.visible.documents)) {
|
|
233
|
+
if (markdownPath(path) && !roots.some((root) => beneath(root, path)))
|
|
234
|
+
count += 1;
|
|
235
|
+
}
|
|
236
|
+
return count;
|
|
237
|
+
}
|
|
214
238
|
function listed() {
|
|
215
|
-
return structuredClone([...rooms.values()])
|
|
239
|
+
return structuredClone([...rooms.values()].map((room) => {
|
|
240
|
+
const documents = documentsIn(room.locator);
|
|
241
|
+
counted.set(room.locator, documents);
|
|
242
|
+
return { ...room, documents };
|
|
243
|
+
}));
|
|
216
244
|
}
|
|
217
245
|
function roomFacts(reach = null) {
|
|
218
246
|
const held = [...rooms.values()]
|
|
@@ -244,6 +272,10 @@ export function composeRuntime(options) {
|
|
|
244
272
|
for (const listener of storageLossListeners)
|
|
245
273
|
listener(storageLoss);
|
|
246
274
|
}
|
|
275
|
+
function emitMember() {
|
|
276
|
+
for (const listener of memberListeners)
|
|
277
|
+
listener(member);
|
|
278
|
+
}
|
|
247
279
|
function moveTo(next) {
|
|
248
280
|
if (closed)
|
|
249
281
|
return;
|
|
@@ -263,6 +295,8 @@ export function composeRuntime(options) {
|
|
|
263
295
|
listener(shown);
|
|
264
296
|
}
|
|
265
297
|
function entryOf(batch) {
|
|
298
|
+
if (localSaves)
|
|
299
|
+
return Object.freeze({ batch, state: 'local' });
|
|
266
300
|
const state = pendings.get(batch.id);
|
|
267
301
|
if (state?.accepted !== undefined) {
|
|
268
302
|
return Object.freeze({ batch, state: 'accepted', roomVersion: state.accepted });
|
|
@@ -283,12 +317,47 @@ export function composeRuntime(options) {
|
|
|
283
317
|
}
|
|
284
318
|
}
|
|
285
319
|
function applyChangeSet(set) {
|
|
320
|
+
const held = everyRoom
|
|
321
|
+
? set
|
|
322
|
+
: { ...set, documents: set.documents.filter(({ locator }) => materialized.has(locator)) };
|
|
323
|
+
readSide.apply(held);
|
|
324
|
+
searchIndex.changed(held);
|
|
325
|
+
}
|
|
326
|
+
function materialize(locators) {
|
|
327
|
+
if (everyRoom)
|
|
328
|
+
return;
|
|
329
|
+
if (locators === null)
|
|
330
|
+
everyRoom = true;
|
|
331
|
+
const sets = [];
|
|
332
|
+
for (const locator of locators ?? [...replicas.keys()]) {
|
|
333
|
+
if (materialized.has(locator))
|
|
334
|
+
continue;
|
|
335
|
+
materialized.add(locator);
|
|
336
|
+
const replica = replicas.get(locator);
|
|
337
|
+
if (replica !== undefined)
|
|
338
|
+
sets.push(changeSet(locator, null, replica));
|
|
339
|
+
}
|
|
340
|
+
if (sets.length === 0)
|
|
341
|
+
return;
|
|
342
|
+
const set = mergeChangeSets(sets);
|
|
286
343
|
readSide.apply(set);
|
|
287
344
|
searchIndex.changed(set);
|
|
288
345
|
}
|
|
346
|
+
function reachedRooms(plan) {
|
|
347
|
+
if (plan.rooms !== null)
|
|
348
|
+
return plan.rooms;
|
|
349
|
+
if (plan.reach === null)
|
|
350
|
+
return null;
|
|
351
|
+
return [...plan.reach.rooms, ...plan.reach.documents.map(roomOfKey)];
|
|
352
|
+
}
|
|
353
|
+
function rowsQuery(plan) {
|
|
354
|
+
materialize(reachedRooms(plan));
|
|
355
|
+
return readSide.query(plan);
|
|
356
|
+
}
|
|
289
357
|
function publishChangeSet(set) {
|
|
290
358
|
applyChangeSet(set);
|
|
291
|
-
if (set.rooms.length > 0
|
|
359
|
+
if (set.rooms.length > 0 ||
|
|
360
|
+
[...new Set(set.documents.map(({ locator }) => locator))].some((locator) => rooms.has(locator) && counted.get(locator) !== documentsIn(locator)))
|
|
292
361
|
emitRooms();
|
|
293
362
|
for (const locator of set.queues)
|
|
294
363
|
emitQueue(locator);
|
|
@@ -362,10 +431,7 @@ export function composeRuntime(options) {
|
|
|
362
431
|
model: revoked,
|
|
363
432
|
selectionModel: revoked,
|
|
364
433
|
};
|
|
365
|
-
|
|
366
|
-
replicas.set(locator, read.kind === 'staged'
|
|
367
|
-
? replicaOf(read.confirmed, before.queue, before)
|
|
368
|
-
: { ...before, confirmed: { ...before.confirmed, record } });
|
|
434
|
+
replicas.set(locator, replicaOf(confirmedOf(record, before.confirmed), before.queue, before));
|
|
369
435
|
}
|
|
370
436
|
rooms.set(locator, {
|
|
371
437
|
...room,
|
|
@@ -382,7 +448,8 @@ export function composeRuntime(options) {
|
|
|
382
448
|
}
|
|
383
449
|
finally {
|
|
384
450
|
refusal = {
|
|
385
|
-
reason,
|
|
451
|
+
code: reason,
|
|
452
|
+
retryable: false,
|
|
386
453
|
reauthorizationOffered: reason === 'revoked-authorization',
|
|
387
454
|
refusal: provider,
|
|
388
455
|
};
|
|
@@ -432,7 +499,7 @@ export function composeRuntime(options) {
|
|
|
432
499
|
emitRooms();
|
|
433
500
|
}
|
|
434
501
|
function unlistedHeld(listed) {
|
|
435
|
-
return [...new Set([...rooms.keys(), ...roomDrafts.keys()])].filter((locator) => !listed.has(locator));
|
|
502
|
+
return [...new Set([...rooms.keys(), ...roomDrafts.keys(), ...transfers.keys()])].filter((locator) => !listed.has(locator));
|
|
436
503
|
}
|
|
437
504
|
async function dropUnlisted(listed, local) {
|
|
438
505
|
if (unlistedHeld(listed).length === 0)
|
|
@@ -451,6 +518,7 @@ export function composeRuntime(options) {
|
|
|
451
518
|
replicas.delete(locator);
|
|
452
519
|
rooms.delete(locator);
|
|
453
520
|
roomDrafts.delete(locator);
|
|
521
|
+
transfers.delete(locator);
|
|
454
522
|
unreadable.delete(locator);
|
|
455
523
|
stale.delete(locator);
|
|
456
524
|
scopedRefusals.delete(locator);
|
|
@@ -487,7 +555,7 @@ export function composeRuntime(options) {
|
|
|
487
555
|
return model;
|
|
488
556
|
}
|
|
489
557
|
async function hydrate(opened) {
|
|
490
|
-
const [records, queues, kept, presented, held, chatDrafts, lastCompleted, declared, named] = await Promise.all([
|
|
558
|
+
const [records, queues, kept, presented, held, chatDrafts, lastCompleted, declared, named, known, staged,] = await Promise.all([
|
|
491
559
|
opened.all(),
|
|
492
560
|
opened.queues(),
|
|
493
561
|
opened.roomDrafts(),
|
|
@@ -497,9 +565,15 @@ export function composeRuntime(options) {
|
|
|
497
565
|
opened.lastCompleted(),
|
|
498
566
|
opened.providerLimits(),
|
|
499
567
|
opened.capabilities(),
|
|
568
|
+
opened.member(),
|
|
569
|
+
opened.transfers(),
|
|
500
570
|
]);
|
|
571
|
+
transfers.clear();
|
|
572
|
+
for (const record of staged)
|
|
573
|
+
transfers.set(record.locator, record);
|
|
501
574
|
limits = declared;
|
|
502
575
|
capabilities = named;
|
|
576
|
+
member = known;
|
|
503
577
|
roomDrafts.clear();
|
|
504
578
|
for (const record of kept)
|
|
505
579
|
rememberRoomDraft(record);
|
|
@@ -519,14 +593,7 @@ export function composeRuntime(options) {
|
|
|
519
593
|
const model = restored(modelKey(record.locator, 'collection'), record.model);
|
|
520
594
|
const selectionModel = restored(modelKey(record.locator, 'typed-selection'), record.selectionModel);
|
|
521
595
|
const prior = previousReplicas.get(record.locator);
|
|
522
|
-
const
|
|
523
|
-
const read = readConfirmed(retyped, prior?.confirmed);
|
|
524
|
-
const confirmed = read.kind === 'staged'
|
|
525
|
-
? read.confirmed
|
|
526
|
-
: {
|
|
527
|
-
record: retyped,
|
|
528
|
-
collection: parseCollection(retyped.documents, compositionOf(retyped)),
|
|
529
|
-
};
|
|
596
|
+
const confirmed = confirmedOf({ ...record, model, selectionModel }, prior?.confirmed);
|
|
530
597
|
replicas.set(record.locator, replicaOf(confirmed, queuedBatches.get(record.locator) ?? [], prior));
|
|
531
598
|
const existing = rooms.get(record.locator);
|
|
532
599
|
if (existing === undefined)
|
|
@@ -547,10 +614,6 @@ export function composeRuntime(options) {
|
|
|
547
614
|
},
|
|
548
615
|
});
|
|
549
616
|
}
|
|
550
|
-
if (read.kind === 'refused') {
|
|
551
|
-
refusedSource(record, read);
|
|
552
|
-
declarationRefusals.add(record.locator);
|
|
553
|
-
}
|
|
554
617
|
}
|
|
555
618
|
return {
|
|
556
619
|
lastCompleted,
|
|
@@ -563,6 +626,8 @@ export function composeRuntime(options) {
|
|
|
563
626
|
applyChangeSet(read);
|
|
564
627
|
if (replicas.size > 0)
|
|
565
628
|
emitRooms();
|
|
629
|
+
if (member !== null)
|
|
630
|
+
emitMember();
|
|
566
631
|
for (const locator of locators)
|
|
567
632
|
emitQueue(locator);
|
|
568
633
|
publishRoomDrafts(false);
|
|
@@ -610,10 +675,14 @@ export function composeRuntime(options) {
|
|
|
610
675
|
return state !== undefined && state.submission.state !== 'submitted' && !state.lost;
|
|
611
676
|
}
|
|
612
677
|
async function settle(local, locator, before, confirmed, queue, settling = {}) {
|
|
613
|
-
const withdrawn = withdrawals(confirmed, queue, withdrawable);
|
|
678
|
+
const withdrawn = withdrawals(confirmed, queue, withdrawable, localSaves);
|
|
614
679
|
const kept = queue.filter((batch) => !withdrawn.includes(batch));
|
|
680
|
+
const unqueued = withdrawn.map((batch) => ({
|
|
681
|
+
batch,
|
|
682
|
+
returned: localSaves || landed(batch, confirmed) ? null : returnedBy('withdrawn'),
|
|
683
|
+
}));
|
|
615
684
|
const returning = [
|
|
616
|
-
...
|
|
685
|
+
...[...unqueued].reverse(),
|
|
617
686
|
...(settling.leaving === undefined ? [] : [settling.leaving]),
|
|
618
687
|
];
|
|
619
688
|
const written = new Map();
|
|
@@ -634,6 +703,7 @@ export function composeRuntime(options) {
|
|
|
634
703
|
await local.write({
|
|
635
704
|
next: confirmed.record,
|
|
636
705
|
previous: before?.confirmed.record ?? null,
|
|
706
|
+
...(settling.stored === undefined ? {} : { stored: settling.stored }),
|
|
637
707
|
queue: kept,
|
|
638
708
|
previousQueue: before?.queue ?? [],
|
|
639
709
|
roomDrafts: [...written.values()],
|
|
@@ -644,8 +714,10 @@ export function composeRuntime(options) {
|
|
|
644
714
|
}
|
|
645
715
|
for (const record of written.values())
|
|
646
716
|
rememberRoomDraft(record);
|
|
647
|
-
for (const batch of
|
|
648
|
-
|
|
717
|
+
for (const { batch, returned } of unqueued) {
|
|
718
|
+
const outcome = localSaves ? 'dropped' : returned === null ? 'landed' : 'withdrawn';
|
|
719
|
+
journal.record(`outcome ${locator} ${batch.id} ${outcome}`);
|
|
720
|
+
}
|
|
649
721
|
replicas.set(locator, replicaOf(confirmed, kept, before));
|
|
650
722
|
return changeSet(locator, before ?? null, replicas.get(locator) ?? null, {
|
|
651
723
|
room: settling.room,
|
|
@@ -684,7 +756,8 @@ export function composeRuntime(options) {
|
|
|
684
756
|
selection: taken(answers?.selection, held.selection, 'typed-selection'),
|
|
685
757
|
};
|
|
686
758
|
}
|
|
687
|
-
function publish(local, room, confirmed,
|
|
759
|
+
function publish(local, room, confirmed, landing = {}) {
|
|
760
|
+
const { base, stored } = landing;
|
|
688
761
|
return exclusive(async () => {
|
|
689
762
|
if (!continuing())
|
|
690
763
|
return true;
|
|
@@ -701,7 +774,9 @@ export function composeRuntime(options) {
|
|
|
701
774
|
const accepted = pendings.get(batch.id)?.accepted;
|
|
702
775
|
return (accepted !== undefined && options.adapter.reached(room.locator, roomVersion, accepted));
|
|
703
776
|
});
|
|
704
|
-
const set = await settle(local, room.locator, prior, confirmed, queue.filter((batch) => !accepted.includes(batch)), { room: true });
|
|
777
|
+
const set = await settle(local, room.locator, prior, confirmed, queue.filter((batch) => !accepted.includes(batch)), { room: true, ...(stored === undefined ? {} : { stored }) });
|
|
778
|
+
if (stored !== undefined)
|
|
779
|
+
transfers.delete(room.locator);
|
|
705
780
|
for (const batch of accepted)
|
|
706
781
|
pendings.delete(batch.id);
|
|
707
782
|
stale.delete(room.locator);
|
|
@@ -720,22 +795,23 @@ export function composeRuntime(options) {
|
|
|
720
795
|
const replica = replicas.get(locator);
|
|
721
796
|
const room = rooms.get(locator);
|
|
722
797
|
if (replica === undefined || room === undefined) {
|
|
723
|
-
throw
|
|
798
|
+
throw runtimeError('room-not-held', `No Local Replica for ${locator}`);
|
|
724
799
|
}
|
|
725
800
|
const saved = [];
|
|
726
801
|
for (const { path, revision } of named) {
|
|
727
802
|
const record = roomDrafts.get(locator)?.get(path);
|
|
728
803
|
if (record === undefined || record.revision !== revision) {
|
|
729
|
-
return { outcome: 'refused',
|
|
804
|
+
return { outcome: 'refused', code: 'stale', retryable: false, file: path };
|
|
730
805
|
}
|
|
731
806
|
if (!saved.some((draft) => draft.path === path))
|
|
732
807
|
saved.push({ path, parts: record.parts });
|
|
733
808
|
}
|
|
734
809
|
const changed = saved.flatMap(({ path, parts }) => changedParts(path, underneath(parts, path, replica.visible)));
|
|
735
810
|
if (changed.length > 0)
|
|
736
|
-
return { outcome: 'refused',
|
|
811
|
+
return { outcome: 'refused', code: 'changed-underneath', retryable: false, parts: changed };
|
|
737
812
|
const parts = saved.map(({ path, parts }) => changesOf(path, parts));
|
|
738
813
|
const built = buildBatch({
|
|
814
|
+
local: localSaves,
|
|
739
815
|
baseRoomVersion: replica.confirmed.record.roomVersion,
|
|
740
816
|
composition: compositionFor(replica.confirmed.record),
|
|
741
817
|
operations: room.operations,
|
|
@@ -762,10 +838,11 @@ export function composeRuntime(options) {
|
|
|
762
838
|
});
|
|
763
839
|
}
|
|
764
840
|
catch {
|
|
765
|
-
return { outcome: 'refused',
|
|
841
|
+
return { outcome: 'refused', code: 'durable-write', retryable: true };
|
|
766
842
|
}
|
|
767
843
|
pendings.set(built.batch.id, { drafts: saved, submission: { state: 'waiting' }, lost: false });
|
|
768
|
-
|
|
844
|
+
if (!localSaves)
|
|
845
|
+
recorded = false;
|
|
769
846
|
for (const path of consumed)
|
|
770
847
|
forgetRoomDraft(locator, path);
|
|
771
848
|
replicas.set(locator, replicaOf(replica.confirmed, batches, replica));
|
|
@@ -773,7 +850,7 @@ export function composeRuntime(options) {
|
|
|
773
850
|
queue: true,
|
|
774
851
|
drafts: consumed,
|
|
775
852
|
}));
|
|
776
|
-
moveTo({ ...saving(status), ...attention() });
|
|
853
|
+
moveTo({ ...(localSaves ? status : saving(status)), ...attention() });
|
|
777
854
|
submitLater(locator);
|
|
778
855
|
return { outcome: 'saved', identifier: built.batch.id };
|
|
779
856
|
}
|
|
@@ -801,16 +878,15 @@ export function composeRuntime(options) {
|
|
|
801
878
|
}
|
|
802
879
|
async function readHistory(locator, path, query = {}) {
|
|
803
880
|
if (closed || refusal !== null)
|
|
804
|
-
return
|
|
881
|
+
return UNCERTAIN;
|
|
805
882
|
try {
|
|
806
883
|
await ensureAuthorization();
|
|
807
884
|
}
|
|
808
885
|
catch {
|
|
809
|
-
return
|
|
810
|
-
}
|
|
811
|
-
if (closed || authorization === undefined || refusal !== null) {
|
|
812
|
-
return { kind: 'transport-uncertainty' };
|
|
886
|
+
return UNCERTAIN;
|
|
813
887
|
}
|
|
888
|
+
if (closed || authorization === undefined || refusal !== null)
|
|
889
|
+
return UNCERTAIN;
|
|
814
890
|
const transfer = ['transfer', 'history', locator, path];
|
|
815
891
|
if (query.revision !== undefined)
|
|
816
892
|
transfer.push(query.revision);
|
|
@@ -832,11 +908,11 @@ export function composeRuntime(options) {
|
|
|
832
908
|
}
|
|
833
909
|
if (result.kind === 'revoked-authorization')
|
|
834
910
|
await refuse('revoked-authorization');
|
|
835
|
-
return result;
|
|
911
|
+
return historyAnswer(result);
|
|
836
912
|
}
|
|
837
913
|
catch {
|
|
838
914
|
journal.record('outcome history transport-uncertainty');
|
|
839
|
-
return
|
|
915
|
+
return UNCERTAIN;
|
|
840
916
|
}
|
|
841
917
|
}
|
|
842
918
|
function declaredAgents() {
|
|
@@ -858,18 +934,17 @@ export function composeRuntime(options) {
|
|
|
858
934
|
async function invokeAction(name, input) {
|
|
859
935
|
await ready;
|
|
860
936
|
if (closed || refusal !== null)
|
|
861
|
-
return
|
|
937
|
+
return UNCERTAIN;
|
|
862
938
|
if (declaredAction(name) === undefined)
|
|
863
|
-
return
|
|
939
|
+
return undeclared(name);
|
|
864
940
|
try {
|
|
865
941
|
await ensureAuthorization();
|
|
866
942
|
}
|
|
867
943
|
catch {
|
|
868
|
-
return
|
|
869
|
-
}
|
|
870
|
-
if (closed || authorization === undefined || refusal !== null) {
|
|
871
|
-
return { kind: 'transport-uncertainty' };
|
|
944
|
+
return UNCERTAIN;
|
|
872
945
|
}
|
|
946
|
+
if (closed || authorization === undefined || refusal !== null)
|
|
947
|
+
return UNCERTAIN;
|
|
873
948
|
journal.record(`transfer action ${name}`);
|
|
874
949
|
try {
|
|
875
950
|
const result = await options.adapter.action(authorization, { name, input });
|
|
@@ -883,11 +958,11 @@ export function composeRuntime(options) {
|
|
|
883
958
|
journal.record(`outcome action ${result.kind}`);
|
|
884
959
|
if (result.kind === 'rejected')
|
|
885
960
|
journal.record(`refusal rejected ${result.code}`);
|
|
886
|
-
return result;
|
|
961
|
+
return calledResult(result);
|
|
887
962
|
}
|
|
888
963
|
catch {
|
|
889
964
|
journal.record('outcome action transport-uncertainty');
|
|
890
|
-
return
|
|
965
|
+
return UNCERTAIN;
|
|
891
966
|
}
|
|
892
967
|
}
|
|
893
968
|
function continuing() {
|
|
@@ -918,14 +993,33 @@ export function composeRuntime(options) {
|
|
|
918
993
|
throw error;
|
|
919
994
|
}
|
|
920
995
|
}
|
|
921
|
-
|
|
996
|
+
function staging(local) {
|
|
997
|
+
return {
|
|
998
|
+
held: (locator) => transfers.get(locator) ?? null,
|
|
999
|
+
staged: (record) => local.staged(record),
|
|
1000
|
+
stage: (record, page) => exclusive(async () => {
|
|
1001
|
+
if (!continuing())
|
|
1002
|
+
return false;
|
|
1003
|
+
await local.stage(record, page);
|
|
1004
|
+
transfers.set(record.locator, record);
|
|
1005
|
+
return true;
|
|
1006
|
+
}),
|
|
1007
|
+
abandon: (locator) => exclusive(async () => {
|
|
1008
|
+
if (!continuing())
|
|
1009
|
+
return;
|
|
1010
|
+
await local.abandon(locator);
|
|
1011
|
+
transfers.delete(locator);
|
|
1012
|
+
}),
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
async function pullBootstrap(authorized, room, local) {
|
|
922
1016
|
if (!continuing())
|
|
923
1017
|
return unconfirmed;
|
|
924
1018
|
try {
|
|
925
1019
|
const result = await bootstrapRoom(options.adapter, authorized, room, {
|
|
926
1020
|
collection: modelOf(room.locator),
|
|
927
1021
|
selection: modelOf(room.locator, 'typed-selection'),
|
|
928
|
-
}, (models, held) => remodel(room.locator, models, held), continuing);
|
|
1022
|
+
}, (models, held) => remodel(room.locator, models, held), continuing, staging(local));
|
|
929
1023
|
const detail = result.kind === 'staged' ? result.confirmed.record.roomVersion : result.kind;
|
|
930
1024
|
journal.record(`transfer bootstrap ${room.locator} ${detail}`);
|
|
931
1025
|
return result;
|
|
@@ -953,7 +1047,7 @@ export function composeRuntime(options) {
|
|
|
953
1047
|
return null;
|
|
954
1048
|
}
|
|
955
1049
|
if (refreshed.kind === 'advanced') {
|
|
956
|
-
if (!(await publish(local, room, refreshed.confirmed, held.confirmed))) {
|
|
1050
|
+
if (!(await publish(local, room, refreshed.confirmed, { base: held.confirmed }))) {
|
|
957
1051
|
const now = replicas.get(room.locator)?.confirmed.record.roomVersion;
|
|
958
1052
|
const reached = now !== undefined &&
|
|
959
1053
|
options.adapter.reached(room.locator, now, refreshed.confirmed.record.roomVersion);
|
|
@@ -965,12 +1059,12 @@ export function composeRuntime(options) {
|
|
|
965
1059
|
if (refreshed.kind !== 'beyond-horizon')
|
|
966
1060
|
return refreshed;
|
|
967
1061
|
}
|
|
968
|
-
const bootstrapped = await pullBootstrap(authorized, room);
|
|
1062
|
+
const bootstrapped = await pullBootstrap(authorized, room, local);
|
|
969
1063
|
if (!continuing())
|
|
970
1064
|
return unconfirmed;
|
|
971
1065
|
if (bootstrapped.kind !== 'staged')
|
|
972
1066
|
return bootstrapped;
|
|
973
|
-
await publish(local, room, bootstrapped.confirmed);
|
|
1067
|
+
await publish(local, room, bootstrapped.confirmed, { stored: bootstrapped.stored });
|
|
974
1068
|
return null;
|
|
975
1069
|
}
|
|
976
1070
|
async function withhold(withheld, room, local, progress) {
|
|
@@ -1002,6 +1096,7 @@ export function composeRuntime(options) {
|
|
|
1002
1096
|
function refusedSource(room, { refusal }) {
|
|
1003
1097
|
sourceRefusals.set(room.locator, {
|
|
1004
1098
|
code: refusal.rule,
|
|
1099
|
+
retryable: false,
|
|
1005
1100
|
fields: {
|
|
1006
1101
|
file: refusal.file,
|
|
1007
1102
|
...(refusal.location === undefined ? {} : { location: refusal.location }),
|
|
@@ -1016,8 +1111,16 @@ export function composeRuntime(options) {
|
|
|
1016
1111
|
}
|
|
1017
1112
|
function leaving(batch, result) {
|
|
1018
1113
|
if (result.kind === 'expired')
|
|
1019
|
-
return { batch, returned:
|
|
1020
|
-
return {
|
|
1114
|
+
return { batch, returned: returnedBy('expired') };
|
|
1115
|
+
return {
|
|
1116
|
+
batch,
|
|
1117
|
+
returned: {
|
|
1118
|
+
kind: result.kind,
|
|
1119
|
+
code: result.code,
|
|
1120
|
+
fields: result.fields,
|
|
1121
|
+
retryable: result.retryable,
|
|
1122
|
+
},
|
|
1123
|
+
};
|
|
1021
1124
|
}
|
|
1022
1125
|
function logAccepted(locator, submitted, accepted) {
|
|
1023
1126
|
journal.record(`outcome ${locator} ${submitted.id} accepted ${accepted.roomVersion}` +
|
|
@@ -1035,7 +1138,7 @@ export function composeRuntime(options) {
|
|
|
1035
1138
|
logAccepted(room.locator, submitted, accepted);
|
|
1036
1139
|
const applied = options.adapter.reached(room.locator, held, accepted.roomVersion)
|
|
1037
1140
|
? { kind: 'staged', confirmed: current.confirmed }
|
|
1038
|
-
: applyReturned(current.confirmed, accepted.documents, accepted.revisions, accepted.removals, accepted.attachments, options.adapter.follows(room.locator, held, accepted.roomVersion)
|
|
1141
|
+
: applyReturned(current.confirmed, accepted.documents, accepted.revisions, accepted.changes, accepted.removals, accepted.attachments, options.adapter.follows(room.locator, held, accepted.roomVersion)
|
|
1039
1142
|
? accepted.roomVersion
|
|
1040
1143
|
: held, accepted.roomVersion);
|
|
1041
1144
|
if (applied.kind !== 'staged') {
|
|
@@ -1181,7 +1284,8 @@ export function composeRuntime(options) {
|
|
|
1181
1284
|
return;
|
|
1182
1285
|
}
|
|
1183
1286
|
const set = await settle(local, room.locator, current, current.confirmed, current.queue.filter((batch) => batch.id !== head.id), {
|
|
1184
|
-
leaving: result.kind === 'expired' && landed(head, current.confirmed)
|
|
1287
|
+
leaving: (result.kind === 'expired' && landed(head, current.confirmed)) ||
|
|
1288
|
+
(result.kind === 'conflicted' && landed(head, current.confirmed, result.affected))
|
|
1185
1289
|
? { ...left, returned: null }
|
|
1186
1290
|
: left,
|
|
1187
1291
|
});
|
|
@@ -1242,7 +1346,7 @@ export function composeRuntime(options) {
|
|
|
1242
1346
|
...(attached === 'collection' ? { model: next } : { selectionModel: next }),
|
|
1243
1347
|
};
|
|
1244
1348
|
const reclassified = sameMembership(declared, held.confirmed.record)
|
|
1245
|
-
?
|
|
1349
|
+
? { kind: 'staged', confirmed: confirmedOf(declared, held.confirmed) }
|
|
1246
1350
|
: readReclassified(declared, held.confirmed);
|
|
1247
1351
|
const record = reclassified.kind === 'staged'
|
|
1248
1352
|
? declared
|
|
@@ -1250,7 +1354,9 @@ export function composeRuntime(options) {
|
|
|
1250
1354
|
...declared,
|
|
1251
1355
|
typedSelection: held.confirmed.record.typedSelection,
|
|
1252
1356
|
};
|
|
1253
|
-
const read = reclassified.kind === 'deferred'
|
|
1357
|
+
const read = reclassified.kind === 'deferred'
|
|
1358
|
+
? { kind: 'staged', confirmed: confirmedOf(record, held.confirmed) }
|
|
1359
|
+
: reclassified;
|
|
1254
1360
|
await local.write({
|
|
1255
1361
|
next: record,
|
|
1256
1362
|
previous: held.confirmed.record,
|
|
@@ -1282,7 +1388,11 @@ export function composeRuntime(options) {
|
|
|
1282
1388
|
});
|
|
1283
1389
|
}
|
|
1284
1390
|
async function scoped(result, room, local) {
|
|
1285
|
-
scopedRefusals.set(room.locator, {
|
|
1391
|
+
scopedRefusals.set(room.locator, {
|
|
1392
|
+
code: result.code,
|
|
1393
|
+
fields: result.fields,
|
|
1394
|
+
retryable: result.retryable,
|
|
1395
|
+
});
|
|
1286
1396
|
let scopedAway = room;
|
|
1287
1397
|
if (result.scope === 'model')
|
|
1288
1398
|
await retainModel(local, room, 'collection', {
|
|
@@ -1330,8 +1440,10 @@ export function composeRuntime(options) {
|
|
|
1330
1440
|
const outcome = prepared[index];
|
|
1331
1441
|
return outcome?.status === 'fulfilled' && outcome.value;
|
|
1332
1442
|
});
|
|
1333
|
-
if (attempt === 'opened')
|
|
1443
|
+
if (attempt === 'opened') {
|
|
1334
1444
|
options.adapter.roomStream?.hold(heldRooms(ready));
|
|
1445
|
+
handed = true;
|
|
1446
|
+
}
|
|
1335
1447
|
const loaded = await Promise.allSettled(ready
|
|
1336
1448
|
.filter((room) => attempt === 'unstreamed' || !replicas.has(room.locator))
|
|
1337
1449
|
.map((room) => loadRoom(room, local, progress)));
|
|
@@ -1397,14 +1509,14 @@ export function composeRuntime(options) {
|
|
|
1397
1509
|
async function reload(room, local, progress) {
|
|
1398
1510
|
if (discoveryPaused || authorization === undefined)
|
|
1399
1511
|
return;
|
|
1400
|
-
const bootstrapped = await pullBootstrap(authorization, room);
|
|
1512
|
+
const bootstrapped = await pullBootstrap(authorization, room, local);
|
|
1401
1513
|
if (!continuing())
|
|
1402
1514
|
return;
|
|
1403
1515
|
if (bootstrapped.kind !== 'staged') {
|
|
1404
1516
|
await withhold(bootstrapped, room, local, progress);
|
|
1405
1517
|
return;
|
|
1406
1518
|
}
|
|
1407
|
-
await publish(local, room, bootstrapped.confirmed);
|
|
1519
|
+
await publish(local, room, bootstrapped.confirmed, { stored: bootstrapped.stored });
|
|
1408
1520
|
await caughtUp(room, local, progress);
|
|
1409
1521
|
}
|
|
1410
1522
|
async function discoverRooms(local, progress, attempt) {
|
|
@@ -1434,15 +1546,16 @@ export function composeRuntime(options) {
|
|
|
1434
1546
|
}
|
|
1435
1547
|
progress.reachable = false;
|
|
1436
1548
|
progress.complete = false;
|
|
1437
|
-
if (attempt === 'opened') {
|
|
1438
|
-
options.adapter.roomStream?.hold(heldRooms([...discovered.values()]));
|
|
1439
|
-
}
|
|
1440
1549
|
notifyReady();
|
|
1441
1550
|
return;
|
|
1442
1551
|
}
|
|
1443
1552
|
if (result.kind === 'contract-unavailable') {
|
|
1444
1553
|
journal.record(`connection contract unavailable ${result.code}`);
|
|
1445
|
-
await refuse('missing-required-guarantee', {
|
|
1554
|
+
await refuse('missing-required-guarantee', {
|
|
1555
|
+
code: result.code,
|
|
1556
|
+
fields: result.fields,
|
|
1557
|
+
retryable: result.retryable,
|
|
1558
|
+
});
|
|
1446
1559
|
notifyReady();
|
|
1447
1560
|
return;
|
|
1448
1561
|
}
|
|
@@ -1483,6 +1596,12 @@ export function composeRuntime(options) {
|
|
|
1483
1596
|
limits = result.providerLimits;
|
|
1484
1597
|
await local.declare(limits, capabilities);
|
|
1485
1598
|
}
|
|
1599
|
+
const signedIn = result.member ?? null;
|
|
1600
|
+
if (JSON.stringify(signedIn) !== JSON.stringify(member)) {
|
|
1601
|
+
await local.keepMember(signedIn);
|
|
1602
|
+
member = signedIn;
|
|
1603
|
+
emitMember();
|
|
1604
|
+
}
|
|
1486
1605
|
await applyDiscovery(result.rooms, local);
|
|
1487
1606
|
await synchronize(result.rooms, local, progress, attempt);
|
|
1488
1607
|
}
|
|
@@ -1512,9 +1631,12 @@ export function composeRuntime(options) {
|
|
|
1512
1631
|
}
|
|
1513
1632
|
const progress = { complete: true, reachable: true };
|
|
1514
1633
|
underway = attempt === 'opened' ? progress : null;
|
|
1634
|
+
if (attempt === 'opened')
|
|
1635
|
+
streamed = progress;
|
|
1515
1636
|
complete = false;
|
|
1516
1637
|
recorded = false;
|
|
1517
1638
|
moveTo(transferring(status));
|
|
1639
|
+
handed = false;
|
|
1518
1640
|
try {
|
|
1519
1641
|
const local = await openLocal();
|
|
1520
1642
|
if (closed)
|
|
@@ -1526,16 +1648,22 @@ export function composeRuntime(options) {
|
|
|
1526
1648
|
}
|
|
1527
1649
|
catch {
|
|
1528
1650
|
progress.complete = false;
|
|
1529
|
-
if (attempt === 'opened')
|
|
1530
|
-
|
|
1531
|
-
}
|
|
1651
|
+
if (attempt === 'opened' && !handed)
|
|
1652
|
+
progress.reachable = false;
|
|
1532
1653
|
}
|
|
1533
1654
|
finally {
|
|
1534
1655
|
notifyReady();
|
|
1535
1656
|
}
|
|
1536
|
-
if (attempt
|
|
1657
|
+
if (attempt !== 'opened') {
|
|
1658
|
+
await finishOpening(progress);
|
|
1537
1659
|
return;
|
|
1538
|
-
|
|
1660
|
+
}
|
|
1661
|
+
if (handed || closed || progress.reachable)
|
|
1662
|
+
return;
|
|
1663
|
+
underway = null;
|
|
1664
|
+
journal.record('connection reopen');
|
|
1665
|
+
await rest(progress);
|
|
1666
|
+
options.adapter.roomStream?.reopen();
|
|
1539
1667
|
});
|
|
1540
1668
|
}
|
|
1541
1669
|
function closedSocket(revoked) {
|
|
@@ -1545,9 +1673,14 @@ export function composeRuntime(options) {
|
|
|
1545
1673
|
void refuse('revoked-authorization');
|
|
1546
1674
|
return;
|
|
1547
1675
|
}
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1676
|
+
complete = false;
|
|
1677
|
+
if (streamed !== null)
|
|
1678
|
+
streamed.complete = false;
|
|
1679
|
+
moveTo({
|
|
1680
|
+
...status,
|
|
1681
|
+
state: status.state === 'synced' ? 'syncing' : status.state,
|
|
1682
|
+
providerUnreachable: status.providerUnreachable || !opened,
|
|
1683
|
+
});
|
|
1551
1684
|
}
|
|
1552
1685
|
function applyOpening(expired) {
|
|
1553
1686
|
return task(async () => {
|
|
@@ -1602,6 +1735,8 @@ export function composeRuntime(options) {
|
|
|
1602
1735
|
}
|
|
1603
1736
|
}
|
|
1604
1737
|
function submitLater(locator) {
|
|
1738
|
+
if (localSaves)
|
|
1739
|
+
return;
|
|
1605
1740
|
const running = submitting.get(locator);
|
|
1606
1741
|
if (running !== undefined) {
|
|
1607
1742
|
running.again = true;
|
|
@@ -1681,7 +1816,7 @@ export function composeRuntime(options) {
|
|
|
1681
1816
|
}
|
|
1682
1817
|
}
|
|
1683
1818
|
moveTo({
|
|
1684
|
-
state: pending() ? 'pending' : synced ? 'synced' : 'syncing',
|
|
1819
|
+
state: pending() ? 'pending' : synced && complete ? 'synced' : 'syncing',
|
|
1685
1820
|
lastCompleted,
|
|
1686
1821
|
providerUnreachable: !progress.reachable,
|
|
1687
1822
|
...attention(),
|
|
@@ -1790,20 +1925,15 @@ export function composeRuntime(options) {
|
|
|
1790
1925
|
show();
|
|
1791
1926
|
return;
|
|
1792
1927
|
}
|
|
1793
|
-
const
|
|
1794
|
-
|
|
1795
|
-
moveTo(transferring(resting));
|
|
1928
|
+
const progress = { complete: false, reachable: !status.providerUnreachable };
|
|
1929
|
+
moveTo(transferring(status));
|
|
1796
1930
|
try {
|
|
1797
1931
|
await conversations.catchUp(progress);
|
|
1798
1932
|
}
|
|
1799
1933
|
catch {
|
|
1800
1934
|
progress.reachable = false;
|
|
1801
1935
|
}
|
|
1802
|
-
|
|
1803
|
-
if (!closed) {
|
|
1804
|
-
moveTo(settled(resting, progress, resting.lastCompleted, pending(), attention()));
|
|
1805
|
-
}
|
|
1806
|
-
}
|
|
1936
|
+
await rest(progress);
|
|
1807
1937
|
}
|
|
1808
1938
|
function close() {
|
|
1809
1939
|
if (closed)
|
|
@@ -1825,6 +1955,7 @@ export function composeRuntime(options) {
|
|
|
1825
1955
|
storageLossListeners.clear();
|
|
1826
1956
|
statusListeners.clear();
|
|
1827
1957
|
serviceModeListeners.clear();
|
|
1958
|
+
memberListeners.clear();
|
|
1828
1959
|
readSide.close();
|
|
1829
1960
|
queueSubscriptions.clear();
|
|
1830
1961
|
roomDraftListeners.clear();
|
|
@@ -1856,13 +1987,16 @@ export function composeRuntime(options) {
|
|
|
1856
1987
|
return planOf(input, roomFacts(reach), planReach(reach));
|
|
1857
1988
|
}
|
|
1858
1989
|
function watchPlan(plan, deliver) {
|
|
1990
|
+
materialize(reachedRooms(plan));
|
|
1859
1991
|
return readSide.watch(plan, identityOf(plan), deliver);
|
|
1860
1992
|
}
|
|
1861
1993
|
async function searched(input, reach) {
|
|
1862
1994
|
const plan = searchPlanOf(input);
|
|
1863
1995
|
const selected = plan.documents === undefined && reach === null
|
|
1864
1996
|
? null
|
|
1865
|
-
: (await
|
|
1997
|
+
: (await rowsQuery(filterOf(plan.documents ?? {}, roomFacts(reach), planReach(reach)))).documents;
|
|
1998
|
+
if (selected === null)
|
|
1999
|
+
materialize(null);
|
|
1866
2000
|
return searchIndex.search(reach === null || reach.conversations ? plan : { ...plan, conversations: [] }, selected);
|
|
1867
2001
|
}
|
|
1868
2002
|
const watch = (topic, input, deliver) => {
|
|
@@ -1888,6 +2022,8 @@ export function composeRuntime(options) {
|
|
|
1888
2022
|
}
|
|
1889
2023
|
if (topic === 'conversations')
|
|
1890
2024
|
return conversations.watchList(deliver);
|
|
2025
|
+
if (topic === 'agents')
|
|
2026
|
+
return conversations.watchAgents(deliver);
|
|
1891
2027
|
if (topic === 'presentation') {
|
|
1892
2028
|
const listener = {
|
|
1893
2029
|
app: input[0],
|
|
@@ -1942,6 +2078,13 @@ export function composeRuntime(options) {
|
|
|
1942
2078
|
serviceModeListeners.delete(deliver);
|
|
1943
2079
|
};
|
|
1944
2080
|
}
|
|
2081
|
+
if (topic === 'member') {
|
|
2082
|
+
memberListeners.add(deliver);
|
|
2083
|
+
deliver(member);
|
|
2084
|
+
return () => {
|
|
2085
|
+
memberListeners.delete(deliver);
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
1945
2088
|
if (topic === 'queue') {
|
|
1946
2089
|
const queue = { locator: input[0], notify: deliver };
|
|
1947
2090
|
queueSubscriptions.add(queue);
|
|
@@ -1959,7 +2102,7 @@ export function composeRuntime(options) {
|
|
|
1959
2102
|
rooms() {
|
|
1960
2103
|
return listed();
|
|
1961
2104
|
},
|
|
1962
|
-
query: ((input) =>
|
|
2105
|
+
query: ((input) => rowsQuery(planned(input, null))),
|
|
1963
2106
|
search: (input) => searched(input, null),
|
|
1964
2107
|
types(locator) {
|
|
1965
2108
|
return {
|
|
@@ -1988,14 +2131,11 @@ export function composeRuntime(options) {
|
|
|
1988
2131
|
return exclusive(async () => {
|
|
1989
2132
|
const parts = readParts(input);
|
|
1990
2133
|
if (path === '' || normalize(path) !== path)
|
|
1991
|
-
throw
|
|
2134
|
+
throw runtimeError('room-draft-malformed');
|
|
1992
2135
|
const local = await openLocal();
|
|
1993
2136
|
const current = roomDrafts.get(room)?.get(path);
|
|
1994
2137
|
if ((current?.revision ?? null) !== revision) {
|
|
1995
|
-
return
|
|
1996
|
-
outcome: 'stale',
|
|
1997
|
-
current: current === undefined ? null : roomDraftOf(current),
|
|
1998
|
-
};
|
|
2138
|
+
return staleWrite(current === undefined ? null : roomDraftOf(current));
|
|
1999
2139
|
}
|
|
2000
2140
|
const record = {
|
|
2001
2141
|
locator: room,
|
|
@@ -2015,10 +2155,7 @@ export function composeRuntime(options) {
|
|
|
2015
2155
|
const local = await openLocal();
|
|
2016
2156
|
const current = roomDrafts.get(room)?.get(path);
|
|
2017
2157
|
if (current === undefined || current.revision !== revision) {
|
|
2018
|
-
return
|
|
2019
|
-
outcome: 'stale',
|
|
2020
|
-
current: current === undefined ? null : roomDraftOf(current),
|
|
2021
|
-
};
|
|
2158
|
+
return staleWrite(current === undefined ? null : roomDraftOf(current));
|
|
2022
2159
|
}
|
|
2023
2160
|
await local.discardRoomDraft(room, path);
|
|
2024
2161
|
forgetRoomDraft(room, path);
|
|
@@ -2086,7 +2223,7 @@ export function composeRuntime(options) {
|
|
|
2086
2223
|
md,
|
|
2087
2224
|
watch,
|
|
2088
2225
|
plan: planned,
|
|
2089
|
-
query: (plan) =>
|
|
2226
|
+
query: (plan) => rowsQuery(plan),
|
|
2090
2227
|
watchPlan,
|
|
2091
2228
|
search: searched,
|
|
2092
2229
|
};
|