@agentshouse/mdruntime-adapter 0.1.0-cc710a8825617 → 0.1.0-cc7da2d4669a3

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 (67) hide show
  1. package/README.md +2 -2
  2. package/dist/actions.d.ts +3 -65
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +3 -336
  5. package/dist/adapter.d.ts.map +1 -1
  6. package/dist/adapter.js +233 -355
  7. package/dist/app-reference.d.ts +2 -0
  8. package/dist/app-reference.d.ts.map +1 -0
  9. package/dist/app-reference.js +2 -0
  10. package/dist/crockford.d.ts +4 -0
  11. package/dist/crockford.d.ts.map +1 -0
  12. package/dist/crockford.js +3 -0
  13. package/dist/discovery.d.ts +13 -12
  14. package/dist/discovery.d.ts.map +1 -1
  15. package/dist/discovery.js +62 -51
  16. package/dist/history.d.ts +12 -0
  17. package/dist/history.d.ts.map +1 -0
  18. package/dist/history.js +91 -0
  19. package/dist/house-model.d.ts +2915 -0
  20. package/dist/house-model.d.ts.map +1 -0
  21. package/dist/house-model.js +1603 -0
  22. package/dist/index.d.ts +10 -2
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +9 -1
  25. package/dist/model.d.ts +5 -2
  26. package/dist/model.d.ts.map +1 -1
  27. package/dist/model.js +10 -29
  28. package/dist/part.d.ts +14 -0
  29. package/dist/part.d.ts.map +1 -0
  30. package/dist/part.js +90 -0
  31. package/dist/read.d.ts +46 -63
  32. package/dist/read.d.ts.map +1 -1
  33. package/dist/read.js +216 -283
  34. package/dist/refusal.d.ts +0 -3
  35. package/dist/refusal.d.ts.map +1 -1
  36. package/dist/refusal.js +0 -5
  37. package/dist/room-stream.d.ts +19 -0
  38. package/dist/room-stream.d.ts.map +1 -0
  39. package/dist/room-stream.js +57 -0
  40. package/dist/room-types.d.ts +7 -0
  41. package/dist/room-types.d.ts.map +1 -0
  42. package/dist/room-types.js +126 -0
  43. package/dist/site.d.ts.map +1 -1
  44. package/dist/site.js +104 -198
  45. package/dist/stream.d.ts +14 -15
  46. package/dist/stream.d.ts.map +1 -1
  47. package/dist/stream.js +165 -423
  48. package/dist/transport.d.ts +62 -43
  49. package/dist/transport.d.ts.map +1 -1
  50. package/dist/transport.js +52 -11
  51. package/dist/upload.d.ts +12 -0
  52. package/dist/upload.d.ts.map +1 -0
  53. package/dist/upload.js +63 -0
  54. package/dist/write.d.ts +16 -1
  55. package/dist/write.d.ts.map +1 -1
  56. package/dist/write.js +101 -56
  57. package/package.json +8 -4
  58. package/AGENTS.md +0 -302
  59. package/dist/gather.d.ts +0 -4
  60. package/dist/gather.d.ts.map +0 -1
  61. package/dist/gather.js +0 -45
  62. package/dist/handed.d.ts +0 -15
  63. package/dist/handed.d.ts.map +0 -1
  64. package/dist/handed.js +0 -38
  65. package/dist/referrer.d.ts +0 -7
  66. package/dist/referrer.d.ts.map +0 -1
  67. package/dist/referrer.js +0 -35
package/dist/stream.js CHANGED
@@ -1,106 +1,91 @@
1
- import { acceptedMessage } from './actions.js';
2
- import { sha256Hex } from './digest.js';
3
- import { binaryFileOf, headChange, identities } from './read.js';
4
- import { roomVersion } from './version.js';
5
- const HELD_FRAMES_MAX = 256;
1
+ import { ROW_WEIGHT, stepped, } from '@agentshouse/mdruntime';
2
+ import { acceptedMessage, cursorPosition, houseHistory } from './history.js';
3
+ import { streamedModels } from './model.js';
4
+ import { answered, answering, binaryFileOf, folded, forgotten, headChange, revised, } from './read.js';
5
+ import { houseDoorRoute, } from './transport.js';
6
+ import { DECIMAL, roomVersion } from './version.js';
6
7
  function turnOperation(frame) {
7
8
  const { type: _type, conversation_id: _conversation, turn_id: _turn, ...operation } = frame;
8
9
  return structuredClone(operation);
9
10
  }
11
+ async function houseAgents(transport) {
12
+ const answer = await transport.door(houseDoorRoute('agents/read'), {});
13
+ const agents = answer.status === 200 ? answer.body?.agents : null;
14
+ if (!Array.isArray(agents))
15
+ return null;
16
+ return agents.flatMap((one) => {
17
+ const { id, name, retired_at: retired } = (one ?? {});
18
+ return typeof id === 'string' && typeof name === 'string'
19
+ ? [{ agent: id, name, retired: retired !== null && retired !== undefined }]
20
+ : [];
21
+ });
22
+ }
10
23
  function listedConversation(listed) {
11
24
  return {
12
25
  conversation: listed.conversation_id,
13
26
  agent: listed.agent_id,
27
+ agentName: listed.agent_name,
28
+ agentRetired: listed.agent_retired,
14
29
  name: listed.name,
15
30
  created: listed.created_at,
16
31
  finished: listed.finished_at,
17
32
  outcome: listed.outcome,
33
+ waiting: listed.waiting,
34
+ newest: listed.newest === null ? null : cursorPosition(listed.newest),
18
35
  };
19
36
  }
20
37
  function position(value) {
21
- if (!/^(?:0|[1-9][0-9]*)$/.test(value))
38
+ if (!DECIMAL.test(value))
22
39
  return null;
23
40
  return BigInt(value);
24
41
  }
25
- function assembled(entries) {
26
- const whole = [];
27
- let open = null;
42
+ function* changed(tracked, entries, version, schemas) {
43
+ const answer = answering();
28
44
  for (const entry of entries) {
29
- if (entry.kind === 'piece') {
30
- if (open === null || open.entry.path !== entry.path || entry.text === undefined)
31
- return null;
32
- open.texts.push(entry.text);
33
- }
34
- else {
35
- if (open !== null)
36
- return null;
37
- if (entry.continued !== true) {
38
- whole.push(entry);
39
- continue;
40
- }
41
- if (entry.text === undefined)
42
- return null;
43
- open = { entry, texts: [entry.text] };
45
+ yield ROW_WEIGHT + (entry.text?.length ?? 0);
46
+ if (entry.kind === 'folder') {
47
+ folded(answer, entry.path, entry.removed === true);
48
+ continue;
44
49
  }
45
- if (entry.continued === true)
50
+ if (entry.path === tracked.schema)
51
+ schemas.set(entry.path, entry);
52
+ forgotten(answer, entry.path);
53
+ if (entry.kind === 'removed') {
54
+ answer.removals.add(entry.path);
46
55
  continue;
47
- const { continued: _continued, ...head } = open.entry;
48
- whole.push({ ...head, text: open.texts.join('') });
49
- open = null;
50
- }
51
- return open === null ? whole : null;
52
- }
53
- async function verified(entries) {
54
- try {
55
- for (const entry of entries) {
56
- if (entry.text === undefined || entry.sha256 === undefined)
57
- continue;
58
- if ((await sha256Hex(entry.text)) !== entry.sha256)
59
- return false;
60
56
  }
61
- return true;
62
- }
63
- catch {
64
- return false;
57
+ const binary = binaryFileOf(entry);
58
+ if (binary === undefined)
59
+ answer.documents.set(entry.path, entry.text ?? '');
60
+ else if (binary !== null)
61
+ answer.binaryFiles.set(entry.path, binary);
62
+ const revision = entry.revision ?? binary?.sha256;
63
+ if (revision !== undefined)
64
+ revised(answer, entry.path, revision);
65
+ const head = headChange(entry);
66
+ if (head)
67
+ answer.changes.set(entry.path, head);
65
68
  }
69
+ return { kind: 'net-advancement', roomVersion: version, ...(yield* answered(answer)) };
66
70
  }
67
- function resumed(kept, frame) {
68
- const last = frame.entries.at(-1);
69
- const point = { parts: frame.index + 1, from: frame.from, target: frame.target };
70
- if (last?.continued !== true)
71
- return { ...point, handled: frame.handled, piece: null };
72
- const text = last.text?.length ?? 0;
73
- if (last.kind !== 'piece') {
74
- return last.sha256 === undefined
75
- ? kept.resume
76
- : { ...point, handled: frame.handled, piece: { sha256: last.sha256, offset: text } };
77
- }
78
- const piece = kept.resume?.piece ?? null;
79
- if (piece === null)
80
- return kept.resume;
81
- const pieces = frame.entries.reduce((sum, entry) => sum + (entry.kind === 'piece' ? (entry.text?.length ?? 0) : 0), 0);
82
- return {
83
- ...point,
84
- handled: frame.handled,
85
- piece: { sha256: piece.sha256, offset: piece.offset + pieces },
86
- };
71
+ async function changePage(tracked, entries, version, mode) {
72
+ const schemas = new Map();
73
+ const page = await stepped(changed(tracked, entries, version, schemas));
74
+ const models = await streamedModels(schemas, tracked, mode);
75
+ return models === undefined ? page : { ...page, models };
87
76
  }
88
- export function houseNotifications(transport, mode) {
77
+ export function houseNotifications(transport, mode, discovering) {
89
78
  const stream = transport.stream;
90
- const lines = new Map();
91
- const kept = new Map();
92
- let receipts = null;
93
- let loaded = Promise.resolve();
94
- let acknowledging = null;
95
- let receiving = Promise.resolve();
96
- let verifying = 0;
97
- const locators = new Map();
79
+ const staged = new Map();
80
+ let socket = null;
81
+ const tracking = new Map();
98
82
  const observers = new Set();
99
83
  const conversationObservers = new Set();
100
84
  const modeObservers = new Set();
101
85
  let replicated = [];
102
86
  const live = new Set();
103
87
  let opening = null;
88
+ let announced = null;
104
89
  const runtime = crypto.randomUUID();
105
90
  function emit(event) {
106
91
  for (const observer of observers)
@@ -111,7 +96,10 @@ export function houseNotifications(transport, mode) {
111
96
  event.kind === 'questions' ||
112
97
  event.kind === 'process' ||
113
98
  event.kind === 'commands' ||
114
- event.kind === 'options') {
99
+ event.kind === 'options' ||
100
+ event.kind === 'starting' ||
101
+ event.kind === 'link' ||
102
+ event.kind === 'deliveries') {
115
103
  live.add(event.conversation);
116
104
  }
117
105
  for (const observer of conversationObservers)
@@ -119,12 +107,31 @@ export function houseNotifications(transport, mode) {
119
107
  }
120
108
  function reopened(next) {
121
109
  opening = next;
122
- acknowledging = next;
110
+ announced = null;
111
+ socket = next;
112
+ staged.clear();
123
113
  const gapped = new Set([...replicated, ...live]);
124
114
  live.clear();
125
115
  for (const conversation of gapped)
126
116
  emitConversation({ kind: 'gap', conversation });
127
117
  emit({ kind: 'opened' });
118
+ if (mode === 'site')
119
+ announce(next, null);
120
+ }
121
+ function announce(via, frame) {
122
+ void discovering.opened(frame).then((discovery) => {
123
+ if (socket !== via)
124
+ return;
125
+ if (discovery === null) {
126
+ via.close();
127
+ return;
128
+ }
129
+ announced = { kind: 'discovered', discovery };
130
+ emit(announced);
131
+ }, () => {
132
+ if (socket === via)
133
+ via.close();
134
+ });
128
135
  }
129
136
  function converse(frame) {
130
137
  const conversation = frame.conversation_id;
@@ -157,6 +164,18 @@ export function houseNotifications(transport, mode) {
157
164
  emitConversation({ kind: 'options', conversation, options: structuredClone(frame.options) });
158
165
  return;
159
166
  }
167
+ if (frame.type === 'starting') {
168
+ emitConversation({ kind: 'starting', conversation, step: frame.step });
169
+ return;
170
+ }
171
+ if (frame.type === 'link') {
172
+ emitConversation({ kind: 'link', conversation, connected: frame.connected });
173
+ return;
174
+ }
175
+ if (frame.type === 'deliveries') {
176
+ emitConversation({ kind: 'deliveries', conversation, unknown: [...frame.unknown] });
177
+ return;
178
+ }
160
179
  if (frame.type === 'interactions') {
161
180
  emitConversation({
162
181
  kind: 'questions',
@@ -170,171 +189,30 @@ export function houseNotifications(transport, mode) {
170
189
  return;
171
190
  emitConversation({ kind: 'accepted-message', conversation, message });
172
191
  }
173
- function announce(authority, at) {
174
- const version = roomVersion(at);
175
- for (const locator of locators.get(authority) ?? []) {
176
- emit({ kind: 'room-version', locator, roomVersion: version });
177
- }
178
- }
179
- function interrupt(authority) {
180
- const line = lines.get(authority);
181
- if (line !== undefined)
182
- lines.set(authority, { ...line, frames: [] });
183
- }
184
- async function forget(carries) {
185
- const held = carries.filter((carry) => kept.delete(carry));
186
- if (held.length > 0)
187
- await receipts?.drop(held).catch(() => { });
188
- }
189
- async function lost(authority, carry) {
190
- interrupt(authority);
191
- await forget([carry]);
192
- }
193
- async function stage(frame, via) {
194
- await loaded;
195
- const port = receipts;
196
- if (port === null)
197
- return;
198
- const held = kept.get(frame.carry);
199
- if (held === undefined && frame.index !== 0) {
200
- interrupt(frame.authority);
201
- return;
202
- }
203
- if (held !== undefined &&
204
- (held.fenced !== null || frame.index > held.parts || frame.index < (held.resume?.parts ?? 0))) {
205
- await lost(frame.authority, frame.carry);
192
+ function stage(frame, via) {
193
+ const held = staged.get(frame.carry);
194
+ if (frame.index !== (held?.parts ?? 0)) {
195
+ staged.delete(frame.carry);
206
196
  return;
207
197
  }
208
198
  if (held === undefined) {
209
- await forget([...kept.values()]
210
- .filter((other) => other.authority === frame.authority && other.fenced === null)
211
- .map((other) => other.carry));
212
- }
213
- const start = held ?? {
214
- authority: frame.authority,
215
- carry: frame.carry,
216
- logEpoch: lines.get(frame.authority)?.logEpoch ?? null,
217
- after: frame.after,
218
- parts: 0,
219
- resume: null,
220
- fenced: null,
221
- };
222
- const next = { ...start, parts: frame.index + 1, resume: resumed(start, frame) };
223
- const part = { entries: frame.entries };
224
- try {
225
- await port.keep(frame.carry, next, { index: frame.index, value: part });
226
- }
227
- catch {
228
- await lost(frame.authority, frame.carry);
229
- return;
199
+ for (const [carry, other] of staged) {
200
+ if (other.authority === frame.authority)
201
+ staged.delete(carry);
202
+ }
230
203
  }
231
- kept.set(frame.carry, next);
204
+ const next = held ?? { authority: frame.authority, parts: 0, entries: [] };
205
+ next.entries.push(...frame.entries);
206
+ next.parts += 1;
207
+ staged.set(frame.carry, next);
232
208
  via?.received(frame.authority, frame.carry, frame.index);
233
209
  }
234
- async function keptEntries(carry, parts) {
235
- if (parts === 0)
236
- return [];
237
- if (receipts === null)
238
- return null;
239
- const read = (await receipts.parts(carry, parts));
240
- if (read.length !== parts)
241
- return null;
242
- const entries = assembled(read.flatMap((part) => part.entries));
243
- return entries !== null && (await verified(entries)) ? entries : null;
244
- }
245
- async function carried(frame) {
246
- await loaded;
247
- const held = kept.get(frame.carry);
248
- if (frame.parts > 0 && held?.parts !== frame.parts) {
249
- await lost(frame.authority, frame.carry);
250
- return;
251
- }
252
- const entries = await keptEntries(frame.carry, frame.parts).catch(() => null);
253
- if (entries === null) {
254
- await lost(frame.authority, frame.carry);
210
+ function carried(frame) {
211
+ const held = staged.get(frame.carry);
212
+ staged.delete(frame.carry);
213
+ if ((held?.parts ?? 0) !== frame.parts)
255
214
  return;
256
- }
257
- const fenced = {
258
- ...(held ?? {
259
- authority: frame.authority,
260
- carry: frame.carry,
261
- logEpoch: frame.log_epoch,
262
- after: frame.after,
263
- parts: 0,
264
- resume: null,
265
- }),
266
- after: frame.after,
267
- fenced: { position: frame.position, logEpoch: frame.log_epoch },
268
- };
269
- try {
270
- await receipts?.keep(frame.carry, fenced);
271
- }
272
- catch {
273
- await lost(frame.authority, frame.carry);
274
- return;
275
- }
276
- kept.set(frame.carry, fenced);
277
- publish(frame, entries);
278
- }
279
- async function load(port) {
280
- try {
281
- for (const { key, head } of await port.heads())
282
- kept.set(key, head);
283
- }
284
- catch {
285
- kept.clear();
286
- }
287
- }
288
- async function restored(room) {
289
- const line = lines.get(room.authority);
290
- let at = room.position;
291
- const chain = [];
292
- for (;;) {
293
- const next = [...kept.values()].find((carry) => carry.authority === room.authority &&
294
- carry.after === at &&
295
- carry.fenced?.logEpoch === room.log_epoch);
296
- if (next?.fenced == null)
297
- break;
298
- const entries = await keptEntries(next.carry, next.parts).catch(() => null);
299
- if (entries === null)
300
- break;
301
- chain.push({ carry: next, entries });
302
- at = next.fenced.position;
303
- }
304
- const partial = [...kept.values()].find((carry) => carry.authority === room.authority &&
305
- carry.fenced === null &&
306
- carry.logEpoch === room.log_epoch &&
307
- carry.after === at &&
308
- carry.resume !== null &&
309
- carry.resume.parts > 0);
310
- const stale = [...kept.values()].filter((carry) => carry.authority === room.authority &&
311
- carry !== partial &&
312
- !chain.some((link) => link.carry === carry));
313
- await forget(stale.map((carry) => carry.carry));
314
- if (line !== undefined && line.logEpoch === room.log_epoch && chain.length > 0) {
315
- const frames = chain.map(({ carry, entries }) => ({
316
- after: BigInt(carry.after),
317
- position: BigInt(carry.fenced.position),
318
- entries,
319
- }));
320
- lines.set(room.authority, { ...line, frames: [...line.frames, ...frames] });
321
- }
322
- return {
323
- ...room,
324
- position: at,
325
- ...(partial?.resume == null
326
- ? {}
327
- : {
328
- carry: {
329
- id: partial.carry,
330
- parts: partial.resume.parts,
331
- from: partial.resume.from,
332
- target: partial.resume.target,
333
- handled: partial.resume.handled,
334
- ...(partial.resume.piece === null ? {} : { piece: partial.resume.piece }),
335
- },
336
- }),
337
- };
215
+ publish(frame, held?.entries ?? []);
338
216
  }
339
217
  function handle(frame, via) {
340
218
  if (frame.type === 'signal') {
@@ -346,12 +224,23 @@ export function houseNotifications(transport, mode) {
346
224
  return;
347
225
  }
348
226
  if (frame.type === 'opening') {
227
+ if (via !== null && mode === 'credential')
228
+ announce(via, frame);
229
+ return;
230
+ }
231
+ if (frame.type === 'caught_up') {
349
232
  emit({
350
- kind: 'opening',
351
- expired: frame.expired.flatMap((authority) => locators.get(authority) ?? []),
233
+ kind: 'caught-up',
234
+ expired: frame.expired.flatMap((authority) => (tracking.get(authority) ?? []).map((tracked) => tracked.locator)),
352
235
  });
353
236
  return;
354
237
  }
238
+ if (frame.type === 'authority_changed') {
239
+ const change = discovering.changed(frame);
240
+ if (change !== null)
241
+ emit(change);
242
+ return;
243
+ }
355
244
  if (frame.type === 'conversations') {
356
245
  emitConversation({ kind: 'list', entries: frame.conversations.map(listedConversation) });
357
246
  return;
@@ -368,63 +257,44 @@ export function houseNotifications(transport, mode) {
368
257
  frame.type === 'process' ||
369
258
  frame.type === 'commands' ||
370
259
  frame.type === 'options' ||
260
+ frame.type === 'starting' ||
261
+ frame.type === 'link' ||
262
+ frame.type === 'deliveries' ||
371
263
  frame.type === 'interactions' ||
372
264
  frame.type === 'message') {
373
265
  converse(frame);
374
266
  return;
375
267
  }
376
268
  if (frame.type === 'part')
377
- return stage(frame, via);
378
- if (frame.type === 'carried')
379
- return carried(frame);
269
+ stage(frame, via);
270
+ else if (frame.type === 'carried')
271
+ carried(frame);
380
272
  }
381
273
  function publish(frame, entries) {
382
274
  const at = position(frame.position);
383
275
  const after = position(frame.after);
384
- if (at === null || after === null || after >= at) {
385
- interrupt(frame.authority);
276
+ if (at === null || after === null || after >= at)
386
277
  return;
387
- }
388
- const line = lines.get(frame.authority);
389
- if (line?.logEpoch === frame.log_epoch) {
390
- if (line.held !== null && at <= line.held)
391
- return;
392
- const last = line.frames.at(-1);
393
- if (last !== undefined && at <= last.position)
394
- return;
395
- const held = { after, position: at, entries };
396
- const next = last !== undefined && after <= last.position ? [...line.frames, held] : [held];
397
- lines.set(frame.authority, { ...line, frames: next.length > HELD_FRAMES_MAX ? [] : next });
398
- }
399
- else {
400
- interrupt(frame.authority);
401
- }
402
- announce(frame.authority, { position: frame.position, logEpoch: frame.log_epoch });
403
- }
404
- stream.observe((frame) => {
405
- const via = acknowledging;
406
- if (verifying === 0) {
407
- const handled = handle(frame, via);
408
- if (handled === undefined)
409
- return;
410
- verifying += 1;
411
- receiving = handled.finally(() => {
412
- verifying -= 1;
278
+ const from = roomVersion({ position: frame.after, logEpoch: frame.log_epoch });
279
+ const version = roomVersion({ position: frame.position, logEpoch: frame.log_epoch });
280
+ for (const tracked of tracking.get(frame.authority) ?? []) {
281
+ emit({
282
+ kind: 'room-change',
283
+ locator: tracked.locator,
284
+ from,
285
+ roomVersion: version,
286
+ read: () => changePage(tracked, entries, version, mode),
413
287
  });
414
- return;
415
288
  }
416
- verifying += 1;
417
- receiving = receiving
418
- .then(() => handle(frame, via))
419
- .finally(() => {
420
- verifying -= 1;
421
- });
422
- });
289
+ }
290
+ stream.observe((frame) => handle(frame, socket));
423
291
  stream.opened(reopened);
424
- stream.closed((revoked) => {
292
+ stream.closed(({ revoked, lost }) => {
425
293
  opening = null;
426
- acknowledging = null;
427
- emit({ kind: 'closed', revoked });
294
+ announced = null;
295
+ socket = null;
296
+ staged.clear();
297
+ emit({ kind: 'closed', revoked, lost });
428
298
  });
429
299
  return {
430
300
  runtime,
@@ -446,169 +316,41 @@ export function houseNotifications(transport, mode) {
446
316
  conversationObservers.delete(notify);
447
317
  };
448
318
  },
319
+ ...(mode === 'credential'
320
+ ? {
321
+ agents: () => houseAgents(transport),
322
+ history: (request) => houseHistory(transport, request),
323
+ }
324
+ : {}),
449
325
  },
450
- observe(notify, port) {
326
+ observe(notify) {
451
327
  observers.add(notify);
452
- if (receipts === null) {
453
- receipts = port;
454
- loaded = load(port);
455
- }
456
- if (opening !== null) {
328
+ const replayed = opening === null ? [] : [{ kind: 'opened' }, announced];
329
+ if (replayed.length > 0) {
457
330
  queueMicrotask(() => {
458
- if (observers.has(notify))
459
- notify({ kind: 'opened' });
331
+ for (const event of replayed)
332
+ if (event !== null && observers.has(notify))
333
+ notify(event);
460
334
  });
461
335
  }
462
336
  return () => {
463
337
  observers.delete(notify);
464
338
  };
465
339
  },
466
- send(rooms, demand) {
467
- for (const room of rooms) {
468
- const line = lines.get(room.authority);
469
- const held = position(room.position);
470
- if (line === undefined || held === null || line.logEpoch !== room.log_epoch)
471
- continue;
472
- lines.set(room.authority, { ...line, frames: [], held });
473
- }
474
- const current = opening;
475
- opening = null;
476
- if (current === null)
477
- return;
478
- void loaded
479
- .then(() => Promise.all(rooms.map(restored)))
480
- .then((presented) => {
481
- current.hold(presented, runtime, demand);
482
- for (const [index, room] of presented.entries()) {
483
- if (room.position === rooms[index].position)
484
- continue;
485
- announce(room.authority, { position: room.position, logEpoch: room.log_epoch });
486
- }
487
- });
488
- },
489
- published(locator, at) {
490
- const authority = [...locators].find(([, held]) => held.includes(locator))?.[0];
491
- const reached = position(at.position);
492
- if (authority === undefined || reached === null)
493
- return;
494
- void forget([...kept.values()]
495
- .filter((carry) => carry.authority === authority &&
496
- carry.fenced !== null &&
497
- carry.fenced.logEpoch === at.logEpoch &&
498
- BigInt(carry.fenced.position) <= reached)
499
- .map((carry) => carry.carry));
500
- },
501
- reopen() {
502
- const current = opening;
340
+ send(rooms) {
503
341
  opening = null;
504
- current?.close();
342
+ announced = null;
343
+ socket?.hold(rooms, runtime);
505
344
  },
506
345
  track(entries) {
507
- locators.clear();
346
+ tracking.clear();
508
347
  for (const entry of entries) {
509
- locators.set(entry.authority, [...(locators.get(entry.authority) ?? []), entry.locator]);
510
- if (lines.get(entry.authority)?.logEpoch !== entry.logEpoch) {
511
- lines.set(entry.authority, { logEpoch: entry.logEpoch, frames: [], held: null });
512
- }
513
- }
514
- for (const authority of lines.keys()) {
515
- if (!locators.has(authority))
516
- lines.delete(authority);
348
+ tracking.set(entry.authority, [...(tracking.get(entry.authority) ?? []), entry]);
517
349
  }
518
- void loaded.then(() => forget([...kept.values()]
519
- .filter((carry) => !locators.has(carry.authority))
520
- .map((carry) => carry.carry)));
521
- },
522
- reach(authority, at) {
523
- const line = lines.get(authority);
524
- const held = position(at.position);
525
- if (line === undefined || held === null || line.logEpoch !== at.logEpoch)
526
- return;
527
- if (line.held !== null && held <= line.held)
528
- return;
529
- lines.set(authority, {
530
- ...line,
531
- frames: line.frames.filter((frame) => frame.position > held),
532
- held,
533
- });
534
- },
535
- advance(tracked, from, holds) {
536
- const line = lines.get(tracked.authority);
537
- const start = position(from.position);
538
- if (line === undefined || start === null || line.logEpoch !== from.logEpoch)
539
- return null;
540
- const pending = line.frames.filter((frame) => frame.position > start);
541
- const first = pending[0];
542
- if (first === undefined || first.after > start)
543
- return null;
544
- const documents = {};
545
- const revisions = {};
546
- const changes = {};
547
- const binaryFiles = {};
548
- const removals = [];
549
- const applied = [];
550
- const schemas = new Map();
551
- const forget = (path) => {
552
- delete documents[path];
553
- delete revisions[path];
554
- delete changes[path];
555
- delete binaryFiles[path];
556
- const removed = removals.indexOf(path);
557
- if (removed !== -1)
558
- removals.splice(removed, 1);
559
- };
560
- for (const frame of pending) {
561
- for (const entry of frame.entries) {
562
- if (entry.kind === 'edited' &&
563
- entry.revision !== undefined &&
564
- holds?.(entry.path, entry.revision) === true) {
565
- forget(entry.path);
566
- continue;
567
- }
568
- applied.push(entry);
569
- if (tracked.schemas.includes(entry.path))
570
- schemas.set(entry.path, entry);
571
- if (entry.kind === 'removed') {
572
- forget(entry.path);
573
- removals.push(entry.path);
574
- continue;
575
- }
576
- const binary = binaryFileOf(entry);
577
- if (binary === null || (binary === undefined && entry.text === undefined))
578
- return null;
579
- if (mode === 'credential' && entry.revision === undefined)
580
- return null;
581
- const head = headChange(entry);
582
- if (head === null)
583
- return null;
584
- forget(entry.path);
585
- if (binary === undefined)
586
- documents[entry.path] = entry.text;
587
- else
588
- binaryFiles[entry.path] = binary;
589
- const revision = entry.revision ?? binary?.sha256;
590
- if (revision !== undefined)
591
- revisions[entry.path] = revision;
592
- if (head !== undefined)
593
- changes[entry.path] = head;
594
- }
350
+ for (const [carry, held] of staged) {
351
+ if (!tracking.has(held.authority))
352
+ staged.delete(carry);
595
353
  }
596
- const last = pending.at(-1);
597
- lines.set(tracked.authority, { ...line, frames: pending, held: last.position });
598
- return {
599
- page: {
600
- kind: 'net-advancement',
601
- roomVersion: roomVersion({ position: last.position.toString(), logEpoch: line.logEpoch }),
602
- documents,
603
- revisions,
604
- ...identities(revisions),
605
- changes,
606
- binaryFiles,
607
- removals: removals.sort(),
608
- },
609
- entries: applied,
610
- schemas,
611
- };
612
354
  },
613
355
  };
614
356
  }