@agentshouse/mdruntime-adapter 0.1.0-cd673d02b6e50 → 0.1.0-cd75dfaf57319

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