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