@agentshouse/mdruntime-adapter 0.1.0-cd23c45caa798 → 0.1.0-cd24cf2286fd6

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 (73) hide show
  1. package/README.md +2 -2
  2. package/dist/actions.d.ts +4 -72
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +7 -319
  5. package/dist/adapter.d.ts.map +1 -1
  6. package/dist/adapter.js +204 -417
  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/chunks.d.ts +38 -0
  11. package/dist/chunks.d.ts.map +1 -0
  12. package/dist/chunks.js +247 -0
  13. package/dist/crockford.d.ts +4 -0
  14. package/dist/crockford.d.ts.map +1 -0
  15. package/dist/crockford.js +3 -0
  16. package/dist/discovery.d.ts +22 -15
  17. package/dist/discovery.d.ts.map +1 -1
  18. package/dist/discovery.js +75 -56
  19. package/dist/history.d.ts +12 -0
  20. package/dist/history.d.ts.map +1 -0
  21. package/dist/history.js +92 -0
  22. package/dist/house-model.d.ts +641 -0
  23. package/dist/house-model.d.ts.map +1 -0
  24. package/dist/house-model.js +361 -0
  25. package/dist/index.d.ts +10 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +10 -1
  28. package/dist/model.d.ts +5 -4
  29. package/dist/model.d.ts.map +1 -1
  30. package/dist/model.js +34 -41
  31. package/dist/read.d.ts +31 -54
  32. package/dist/read.d.ts.map +1 -1
  33. package/dist/read.js +82 -300
  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-types.d.ts +7 -0
  38. package/dist/room-types.d.ts.map +1 -0
  39. package/dist/room-types.js +123 -0
  40. package/dist/search.d.ts +5 -0
  41. package/dist/search.d.ts.map +1 -0
  42. package/dist/search.js +47 -0
  43. package/dist/site.d.ts.map +1 -1
  44. package/dist/site.js +82 -185
  45. package/dist/space-schema.d.ts +7 -0
  46. package/dist/space-schema.d.ts.map +1 -0
  47. package/dist/space-schema.js +18 -0
  48. package/dist/stream.d.ts +16 -14
  49. package/dist/stream.d.ts.map +1 -1
  50. package/dist/stream.js +230 -408
  51. package/dist/transport.d.ts +50 -40
  52. package/dist/transport.d.ts.map +1 -1
  53. package/dist/transport.js +54 -11
  54. package/dist/upload.d.ts +12 -0
  55. package/dist/upload.d.ts.map +1 -0
  56. package/dist/upload.js +63 -0
  57. package/dist/write.d.ts +6 -1
  58. package/dist/write.d.ts.map +1 -1
  59. package/dist/write.js +28 -39
  60. package/package.json +8 -4
  61. package/AGENTS.md +0 -304
  62. package/dist/gather.d.ts +0 -4
  63. package/dist/gather.d.ts.map +0 -1
  64. package/dist/gather.js +0 -35
  65. package/dist/handed.d.ts +0 -15
  66. package/dist/handed.d.ts.map +0 -1
  67. package/dist/handed.js +0 -45
  68. package/dist/part.d.ts +0 -13
  69. package/dist/part.d.ts.map +0 -1
  70. package/dist/part.js +0 -80
  71. package/dist/referrer.d.ts +0 -7
  72. package/dist/referrer.d.ts.map +0 -1
  73. package/dist/referrer.js +0 -33
package/dist/stream.js CHANGED
@@ -1,112 +1,111 @@
1
- import { ROW_WEIGHT, } from '@agentshouse/mdruntime';
2
- import { acceptedMessage } from './actions.js';
3
- import { sha256Hex } from './digest.js';
4
- import { answered, answering, binaryFileOf, forgotten, headChange, revised } from './read.js';
5
- import { roomVersion } from './version.js';
6
- const HELD_FRAMES_MAX = 256;
1
+ import { ROW_WEIGHT, stepped, } from '@agentshouse/mdruntime';
2
+ import { acceptedMessage, cursorPosition, houseHistory } from './history.js';
3
+ import { keptSpaceSchemas, 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, logPosition, presented, 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, whole) {
79
+ const schemas = new Map();
80
+ const page = await stepped(changed(tracked, entries, version, schemas));
81
+ const models = await streamedModels(schemas, tracked, mode, spaces, whole);
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();
90
+ const replaying = new Map();
101
91
  const conversationObservers = new Set();
102
92
  const modeObservers = new Set();
103
93
  let replicated = [];
94
+ let listedCursor = null;
95
+ const unanswered = [];
96
+ const heldAtZero = new Set();
104
97
  const live = new Set();
105
98
  let opening = null;
99
+ let announced = null;
106
100
  const runtime = crypto.randomUUID();
107
101
  function emit(event) {
108
- for (const observer of observers)
109
- observer(event);
102
+ for (const observer of observers) {
103
+ const held = replaying.get(observer);
104
+ if (held === undefined)
105
+ observer(event);
106
+ else
107
+ held.push(event);
108
+ }
110
109
  }
111
110
  function emitConversation(event) {
112
111
  if (event.kind === 'turn' ||
@@ -115,6 +114,7 @@ export function houseNotifications(transport, mode) {
115
114
  event.kind === 'commands' ||
116
115
  event.kind === 'options' ||
117
116
  event.kind === 'starting' ||
117
+ event.kind === 'subagents' ||
118
118
  event.kind === 'link' ||
119
119
  event.kind === 'deliveries') {
120
120
  live.add(event.conversation);
@@ -124,12 +124,33 @@ export function houseNotifications(transport, mode) {
124
124
  }
125
125
  function reopened(next) {
126
126
  opening = next;
127
- acknowledging = next;
127
+ announced = null;
128
+ socket = next;
129
+ staged.clear();
130
+ unanswered.length = 0;
131
+ heldAtZero.clear();
128
132
  const gapped = new Set([...replicated, ...live]);
129
133
  live.clear();
130
134
  for (const conversation of gapped)
131
135
  emitConversation({ kind: 'gap', conversation });
132
136
  emit({ kind: 'opened' });
137
+ if (mode === 'site')
138
+ announce(next, null);
139
+ }
140
+ function announce(via, frame) {
141
+ void discovering.opened(frame).then((discovery) => {
142
+ if (socket !== via)
143
+ return;
144
+ if (discovery === null) {
145
+ via.close();
146
+ return;
147
+ }
148
+ announced = { kind: 'discovered', discovery };
149
+ emit(announced);
150
+ }, () => {
151
+ if (socket === via)
152
+ via.close();
153
+ });
133
154
  }
134
155
  function converse(frame) {
135
156
  const conversation = frame.conversation_id;
@@ -148,6 +169,7 @@ export function houseNotifications(transport, mode) {
148
169
  return;
149
170
  emitConversation({ kind: 'commands', conversation, commands: [] });
150
171
  emitConversation({ kind: 'options', conversation, options: [] });
172
+ emitConversation({ kind: 'subagents', conversation, subagents: [] });
151
173
  return;
152
174
  }
153
175
  if (frame.type === 'commands') {
@@ -166,6 +188,14 @@ export function houseNotifications(transport, mode) {
166
188
  emitConversation({ kind: 'starting', conversation, step: frame.step });
167
189
  return;
168
190
  }
191
+ if (frame.type === 'subagents') {
192
+ emitConversation({
193
+ kind: 'subagents',
194
+ conversation,
195
+ subagents: structuredClone(frame.subagents),
196
+ });
197
+ return;
198
+ }
169
199
  if (frame.type === 'link') {
170
200
  emitConversation({ kind: 'link', conversation, connected: frame.connected });
171
201
  return;
@@ -187,171 +217,31 @@ export function houseNotifications(transport, mode) {
187
217
  return;
188
218
  emitConversation({ kind: 'accepted-message', conversation, message });
189
219
  }
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);
220
+ function stage(frame, via) {
221
+ const held = staged.get(frame.carry);
222
+ if (frame.index !== (held?.parts ?? 0)) {
223
+ staged.delete(frame.carry);
223
224
  return;
224
225
  }
225
226
  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;
227
+ for (const [carry, other] of staged) {
228
+ if (other.authority === frame.authority)
229
+ staged.delete(carry);
230
+ }
247
231
  }
248
- kept.set(frame.carry, next);
232
+ const next = held ?? { authority: frame.authority, parts: 0, entries: [] };
233
+ keptSpaceSchemas(frame.entries, spaces);
234
+ next.entries.push(...frame.entries);
235
+ next.parts += 1;
236
+ staged.set(frame.carry, next);
249
237
  via?.received(frame.authority, frame.carry, frame.index);
250
238
  }
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);
239
+ function carried(frame) {
240
+ const held = staged.get(frame.carry);
241
+ staged.delete(frame.carry);
242
+ if ((held?.parts ?? 0) !== frame.parts)
272
243
  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);
291
- 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
- };
244
+ publish(frame, held?.entries ?? []);
355
245
  }
356
246
  function handle(frame, via) {
357
247
  if (frame.type === 'signal') {
@@ -363,18 +253,35 @@ export function houseNotifications(transport, mode) {
363
253
  return;
364
254
  }
365
255
  if (frame.type === 'opening') {
256
+ if (via !== null && mode === 'credential')
257
+ announce(via, frame);
258
+ return;
259
+ }
260
+ if (frame.type === 'caught_up') {
261
+ discovering.expired?.(frame.expired);
366
262
  emit({
367
- kind: 'opening',
368
- expired: frame.expired.flatMap((authority) => locators.get(authority) ?? []),
263
+ kind: 'caught-up',
264
+ expired: [
265
+ ...frame.expired.flatMap((authority) => (tracking.get(authority) ?? []).map((tracked) => tracked.locator)),
266
+ ...(unanswered.shift() ?? []),
267
+ ],
369
268
  });
370
269
  return;
371
270
  }
372
271
  if (frame.type === 'authority_changed') {
373
- emit({ kind: 'authority-changed', scope: { authorities: frame.rooms } });
272
+ const change = discovering.changed(frame);
273
+ if (change !== null)
274
+ emit(change);
374
275
  return;
375
276
  }
376
277
  if (frame.type === 'conversations') {
377
- emitConversation({ kind: 'list', entries: frame.conversations.map(listedConversation) });
278
+ emitConversation({
279
+ kind: 'list',
280
+ entries: frame.conversations.map(listedConversation),
281
+ whole: frame.whole,
282
+ more: frame.more,
283
+ cursor: frame.cursor,
284
+ });
378
285
  return;
379
286
  }
380
287
  if (frame.type === 'conversation') {
@@ -390,6 +297,7 @@ export function houseNotifications(transport, mode) {
390
297
  frame.type === 'commands' ||
391
298
  frame.type === 'options' ||
392
299
  frame.type === 'starting' ||
300
+ frame.type === 'subagents' ||
393
301
  frame.type === 'link' ||
394
302
  frame.type === 'deliveries' ||
395
303
  frame.type === 'interactions' ||
@@ -398,57 +306,39 @@ export function houseNotifications(transport, mode) {
398
306
  return;
399
307
  }
400
308
  if (frame.type === 'part')
401
- return stage(frame, via);
402
- if (frame.type === 'carried')
403
- return carried(frame);
309
+ stage(frame, via);
310
+ else if (frame.type === 'carried')
311
+ carried(frame);
404
312
  }
405
313
  function publish(frame, entries) {
406
314
  const at = position(frame.position);
407
315
  const after = position(frame.after);
408
- if (at === null || after === null || after >= at) {
409
- interrupt(frame.authority);
316
+ if (at === null || after === null || after >= at)
410
317
  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;
318
+ const from = after === 0n && !heldAtZero.has(frame.authority)
319
+ ? null
320
+ : roomVersion({ position: frame.after, logEpoch: frame.log_epoch });
321
+ const version = roomVersion({ position: frame.position, logEpoch: frame.log_epoch });
322
+ for (const tracked of tracking.get(frame.authority) ?? []) {
323
+ emit({
324
+ kind: 'room-change',
325
+ locator: tracked.locator,
326
+ from,
327
+ roomVersion: version,
328
+ read: () => changePage(tracked, entries, version, mode, spaces, from === null),
437
329
  });
438
- return;
439
330
  }
440
- verifying += 1;
441
- receiving = receiving
442
- .then(() => handle(frame, via))
443
- .finally(() => {
444
- verifying -= 1;
445
- });
446
- });
331
+ }
332
+ stream.observe((frame) => handle(frame, socket));
447
333
  stream.opened(reopened);
448
- stream.closed(({ revoked, lost }) => {
334
+ stream.closed(({ revoked, lost, authorityChanged }) => {
449
335
  opening = null;
450
- acknowledging = null;
451
- emit({ kind: 'closed', revoked, lost });
336
+ announced = null;
337
+ socket = null;
338
+ staged.clear();
339
+ unanswered.length = 0;
340
+ heldAtZero.clear();
341
+ emit({ kind: 'closed', revoked, lost, authorityChanged });
452
342
  });
453
343
  return {
454
344
  runtime,
@@ -464,23 +354,34 @@ export function houseNotifications(transport, mode) {
464
354
  replicate(next) {
465
355
  replicated = [...next];
466
356
  },
357
+ hold(cursor) {
358
+ listedCursor = cursor;
359
+ },
467
360
  observe(notify) {
468
361
  conversationObservers.add(notify);
469
362
  return () => {
470
363
  conversationObservers.delete(notify);
471
364
  };
472
365
  },
366
+ ...(mode === 'credential'
367
+ ? {
368
+ agents: () => houseAgents(transport),
369
+ history: (request) => houseHistory(transport, request),
370
+ }
371
+ : {}),
473
372
  },
474
- observe(notify, port) {
373
+ observe(notify) {
475
374
  observers.add(notify);
476
- if (receipts === null) {
477
- receipts = port;
478
- loaded = load(port);
479
- }
480
375
  if (opening !== null) {
376
+ const replayed = [{ kind: 'opened' }];
377
+ if (announced !== null)
378
+ replayed.push(announced);
379
+ replaying.set(notify, replayed);
481
380
  queueMicrotask(() => {
482
- if (observers.has(notify))
483
- notify({ kind: 'opened' });
381
+ replaying.delete(notify);
382
+ for (const event of replayed)
383
+ if (observers.has(notify))
384
+ notify(event);
484
385
  });
485
386
  }
486
387
  return () => {
@@ -488,138 +389,59 @@ export function houseNotifications(transport, mode) {
488
389
  };
489
390
  },
490
391
  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
392
  opening = null;
500
- if (current === null)
393
+ announced = null;
394
+ if (socket === null)
501
395
  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
- }
396
+ const entries = new Map([...tracking.values()].flat().map((tracked) => [tracked.locator, tracked]));
397
+ const named = rooms.flatMap((room) => {
398
+ const entry = entries.get(room.locator);
399
+ const at = room.roomVersion === null ? null : logPosition(room.roomVersion);
400
+ return entry === undefined || at === null ? [] : [{ entry, at }];
511
401
  });
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();
529
- },
530
- track(entries) {
531
- locators.clear();
532
- 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
- }
537
- }
538
- for (const authority of lines.keys()) {
539
- if (!locators.has(authority))
540
- lines.delete(authority);
402
+ const unheld = rooms.flatMap((room) => {
403
+ const entry = entries.get(room.locator);
404
+ return entry === undefined || room.roomVersion !== null ? [] : [entry];
405
+ });
406
+ for (const { entry, at } of named) {
407
+ if (at.position === '0')
408
+ heldAtZero.add(entry.authority);
409
+ else
410
+ heldAtZero.delete(entry.authority);
541
411
  }
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)
412
+ for (const entry of unheld)
413
+ heldAtZero.delete(entry.authority);
414
+ const site = mode === 'site';
415
+ unanswered.push(site ? unheld.map((entry) => entry.locator) : []);
416
+ socket.hold([
417
+ ...named.map(({ entry, at }) => presented(entry.authority, at)),
418
+ ...(site ? [] : unheld).map((entry) => presented(entry.authority, { position: '0', logEpoch: entry.logEpoch })),
419
+ ], runtime, listedCursor);
420
+ if (site)
552
421
  return;
553
- lines.set(authority, {
554
- ...line,
555
- frames: line.frames.filter((frame) => frame.position > held),
556
- held,
422
+ const made = unheld.filter((entry) => entry.position === '0');
423
+ queueMicrotask(() => {
424
+ for (const entry of made) {
425
+ const version = roomVersion(entry);
426
+ emit({
427
+ kind: 'room-change',
428
+ locator: entry.locator,
429
+ from: null,
430
+ roomVersion: version,
431
+ read: () => changePage(entry, [], version, mode, spaces, true),
432
+ });
433
+ }
557
434
  });
558
435
  },
559
- *advance(tracked, from, holds) {
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 null;
564
- const pending = line.frames.filter((frame) => frame.position > start);
565
- const first = pending[0];
566
- if (first === undefined || first.after > start)
567
- return null;
568
- const answer = answering();
569
- const schemas = new Map();
570
- for (const frame of pending) {
571
- for (const entry of frame.entries) {
572
- yield ROW_WEIGHT + (entry.text?.length ?? 0);
573
- if (entry.kind === 'edited' &&
574
- entry.revision !== undefined &&
575
- holds?.(entry.path, entry.revision) === true) {
576
- forgotten(answer, entry.path);
577
- continue;
578
- }
579
- if (tracked.schemas.includes(entry.path))
580
- schemas.set(entry.path, entry);
581
- if (entry.kind === 'removed') {
582
- forgotten(answer, entry.path);
583
- answer.removals.add(entry.path);
584
- continue;
585
- }
586
- const binary = binaryFileOf(entry);
587
- if (binary === null || (binary === undefined && entry.text === undefined))
588
- return null;
589
- if (mode === 'credential' && entry.revision === undefined)
590
- return null;
591
- const head = headChange(entry);
592
- if (head === null)
593
- return null;
594
- forgotten(answer, entry.path);
595
- if (binary === undefined)
596
- answer.documents.set(entry.path, entry.text);
597
- else
598
- answer.binaryFiles.set(entry.path, binary);
599
- const revision = entry.revision ?? binary?.sha256;
600
- if (revision !== undefined)
601
- revised(answer, entry.path, revision);
602
- if (head !== undefined)
603
- answer.changes.set(entry.path, head);
604
- }
436
+ track(entries) {
437
+ tracking.clear();
438
+ for (const entry of entries) {
439
+ tracking.set(entry.authority, [...(tracking.get(entry.authority) ?? []), entry]);
605
440
  }
606
- const last = pending.at(-1);
607
- const current = lines.get(tracked.authority);
608
- if (current?.logEpoch === line.logEpoch) {
609
- lines.set(tracked.authority, {
610
- ...current,
611
- frames: current.frames.filter((frame) => frame.position > start),
612
- held: current.held !== null && current.held > last.position ? current.held : last.position,
613
- });
441
+ for (const [carry, held] of staged) {
442
+ if (!tracking.has(held.authority))
443
+ staged.delete(carry);
614
444
  }
615
- return {
616
- page: {
617
- kind: 'net-advancement',
618
- roomVersion: roomVersion({ position: last.position.toString(), logEpoch: line.logEpoch }),
619
- ...(yield* answered(answer)),
620
- },
621
- schemas,
622
- };
623
445
  },
624
446
  };
625
447
  }