@agentshouse/mdruntime-adapter 0.1.0-cda5b3be3317e → 0.1.0-cdcec0737ac0e

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