@agentshouse/mdruntime-adapter 0.1.0-ccd2ccb1f7214 → 0.1.0-ccdfb41af2abb

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 (70) hide show
  1. package/README.md +2 -2
  2. package/dist/actions.d.ts +3 -65
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +1 -337
  5. package/dist/adapter.d.ts.map +1 -1
  6. package/dist/adapter.js +248 -369
  7. package/dist/app-reference.d.ts +2 -0
  8. package/dist/app-reference.d.ts.map +1 -0
  9. package/dist/app-reference.js +2 -0
  10. package/dist/crockford.d.ts +4 -0
  11. package/dist/crockford.d.ts.map +1 -0
  12. package/dist/crockford.js +3 -0
  13. package/dist/discovery.d.ts +13 -12
  14. package/dist/discovery.d.ts.map +1 -1
  15. package/dist/discovery.js +62 -51
  16. package/dist/history.d.ts +12 -0
  17. package/dist/history.d.ts.map +1 -0
  18. package/dist/history.js +91 -0
  19. package/dist/house-model.d.ts +892 -0
  20. package/dist/house-model.d.ts.map +1 -0
  21. package/dist/house-model.js +482 -0
  22. package/dist/index.d.ts +10 -2
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +9 -1
  25. package/dist/model.d.ts +5 -2
  26. package/dist/model.d.ts.map +1 -1
  27. package/dist/model.js +10 -29
  28. package/dist/part.d.ts +14 -0
  29. package/dist/part.d.ts.map +1 -0
  30. package/dist/part.js +90 -0
  31. package/dist/read.d.ts +50 -63
  32. package/dist/read.d.ts.map +1 -1
  33. package/dist/read.js +242 -284
  34. package/dist/refusal.d.ts +0 -3
  35. package/dist/refusal.d.ts.map +1 -1
  36. package/dist/refusal.js +0 -5
  37. package/dist/room-stream.d.ts +19 -0
  38. package/dist/room-stream.d.ts.map +1 -0
  39. package/dist/room-stream.js +57 -0
  40. package/dist/room-types.d.ts +7 -0
  41. package/dist/room-types.d.ts.map +1 -0
  42. package/dist/room-types.js +125 -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 +84 -195
  48. package/dist/stream.d.ts +13 -14
  49. package/dist/stream.d.ts.map +1 -1
  50. package/dist/stream.js +169 -423
  51. package/dist/transport.d.ts +52 -39
  52. package/dist/transport.d.ts.map +1 -1
  53. package/dist/transport.js +55 -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 +18 -2
  58. package/dist/write.d.ts.map +1 -1
  59. package/dist/write.js +105 -70
  60. package/package.json +8 -4
  61. package/AGENTS.md +0 -299
  62. package/dist/gather.d.ts +0 -4
  63. package/dist/gather.d.ts.map +0 -1
  64. package/dist/gather.js +0 -45
  65. package/dist/handed.d.ts +0 -15
  66. package/dist/handed.d.ts.map +0 -1
  67. package/dist/handed.js +0 -38
  68. package/dist/referrer.d.ts +0 -7
  69. package/dist/referrer.d.ts.map +0 -1
  70. package/dist/referrer.js +0 -35
package/dist/stream.js CHANGED
@@ -1,106 +1,97 @@
1
- import { acceptedMessage } from './actions.js';
2
- import { sha256Hex } from './digest.js';
3
- import { binaryFileOf, headChange, identities } from './read.js';
4
- import { roomVersion } from './version.js';
5
- const HELD_FRAMES_MAX = 256;
1
+ import { ROW_WEIGHT, stepped, } from '@agentshouse/mdruntime';
2
+ import { acceptedMessage, cursorPosition, houseHistory } from './history.js';
3
+ import { streamedModels } from './model.js';
4
+ import { answered, answering, binaryFileOf, folded, forgotten, headChange, outlineOf, revised, } from './read.js';
5
+ import { houseDoorRoute, } from './transport.js';
6
+ import { DECIMAL, roomVersion } from './version.js';
6
7
  function turnOperation(frame) {
7
8
  const { type: _type, conversation_id: _conversation, turn_id: _turn, ...operation } = frame;
8
9
  return structuredClone(operation);
9
10
  }
11
+ async function houseAgents(transport) {
12
+ const answer = await transport.door(houseDoorRoute('agents/read'), {});
13
+ const agents = answer.status === 200 ? answer.body?.agents : null;
14
+ if (!Array.isArray(agents))
15
+ return null;
16
+ return agents.flatMap((one) => {
17
+ const { id, name, retired_at: retired } = (one ?? {});
18
+ return typeof id === 'string' && typeof name === 'string'
19
+ ? [{ agent: id, name, retired: retired !== null && retired !== undefined }]
20
+ : [];
21
+ });
22
+ }
10
23
  function listedConversation(listed) {
11
24
  return {
12
25
  conversation: listed.conversation_id,
13
26
  agent: listed.agent_id,
27
+ agentName: listed.agent_name,
28
+ agentRetired: listed.agent_retired,
14
29
  name: listed.name,
15
30
  created: listed.created_at,
16
31
  finished: listed.finished_at,
17
32
  outcome: listed.outcome,
33
+ waiting: listed.waiting,
34
+ newest: listed.newest === null ? null : cursorPosition(listed.newest),
18
35
  };
19
36
  }
20
37
  function position(value) {
21
- if (!/^(?:0|[1-9][0-9]*)$/.test(value))
38
+ if (!DECIMAL.test(value))
22
39
  return null;
23
40
  return BigInt(value);
24
41
  }
25
- function assembled(entries) {
26
- const whole = [];
27
- let open = null;
42
+ function* changed(tracked, entries, version, schemas) {
43
+ const answer = answering();
28
44
  for (const entry of entries) {
29
- if (entry.kind === 'piece') {
30
- if (open === null || open.entry.path !== entry.path || entry.text === undefined)
31
- return null;
32
- open.texts.push(entry.text);
33
- }
34
- else {
35
- if (open !== null)
36
- return null;
37
- if (entry.continued !== true) {
38
- whole.push(entry);
39
- continue;
40
- }
41
- if (entry.text === undefined)
42
- return null;
43
- open = { entry, texts: [entry.text] };
45
+ yield ROW_WEIGHT + (entry.text?.length ?? 0);
46
+ if (entry.kind === 'folder') {
47
+ folded(answer, entry.path, entry.removed === true);
48
+ continue;
44
49
  }
45
- if (entry.continued === true)
50
+ if (entry.path === tracked.schema)
51
+ schemas.set(entry.path, entry);
52
+ forgotten(answer, entry.path);
53
+ if (entry.kind === 'removed') {
54
+ answer.removals.add(entry.path);
46
55
  continue;
47
- const { continued: _continued, ...head } = open.entry;
48
- whole.push({ ...head, text: open.texts.join('') });
49
- open = null;
50
- }
51
- return open === null ? whole : null;
52
- }
53
- async function verified(entries) {
54
- try {
55
- for (const entry of entries) {
56
- if (entry.text === undefined || entry.sha256 === undefined)
57
- continue;
58
- if ((await sha256Hex(entry.text)) !== entry.sha256)
59
- return false;
60
56
  }
61
- return true;
62
- }
63
- catch {
64
- return false;
57
+ const binary = binaryFileOf(entry);
58
+ const outline = entry.text === undefined ? outlineOf(entry.outline) : undefined;
59
+ if (binary !== undefined) {
60
+ if (binary !== null)
61
+ answer.binaryFiles.set(entry.path, binary);
62
+ }
63
+ else if (outline !== undefined && outline !== null)
64
+ answer.outlines.set(entry.path, outline);
65
+ else
66
+ answer.documents.set(entry.path, entry.text ?? '');
67
+ const revision = entry.revision ?? binary?.sha256;
68
+ if (revision !== undefined)
69
+ revised(answer, entry.path, revision);
70
+ const head = headChange(entry);
71
+ if (head)
72
+ answer.changes.set(entry.path, head);
65
73
  }
74
+ return { kind: 'net-advancement', roomVersion: version, ...(yield* answered(answer)) };
66
75
  }
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
- };
76
+ async function changePage(tracked, entries, version, mode) {
77
+ const schemas = new Map();
78
+ const page = await stepped(changed(tracked, entries, version, schemas));
79
+ const models = await streamedModels(schemas, tracked, mode);
80
+ return models === undefined ? page : { ...page, models };
87
81
  }
88
- export function houseNotifications(transport, mode) {
82
+ export function houseNotifications(transport, mode, discovering) {
89
83
  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();
84
+ const staged = new Map();
85
+ let socket = null;
86
+ const tracking = new Map();
98
87
  const observers = new Set();
99
88
  const conversationObservers = new Set();
100
89
  const modeObservers = new Set();
101
90
  let replicated = [];
91
+ let listedCursor = null;
102
92
  const live = new Set();
103
93
  let opening = null;
94
+ let announced = null;
104
95
  const runtime = crypto.randomUUID();
105
96
  function emit(event) {
106
97
  for (const observer of observers)
@@ -113,6 +104,7 @@ export function houseNotifications(transport, mode) {
113
104
  event.kind === 'commands' ||
114
105
  event.kind === 'options' ||
115
106
  event.kind === 'starting' ||
107
+ event.kind === 'subagents' ||
116
108
  event.kind === 'link' ||
117
109
  event.kind === 'deliveries') {
118
110
  live.add(event.conversation);
@@ -122,12 +114,31 @@ export function houseNotifications(transport, mode) {
122
114
  }
123
115
  function reopened(next) {
124
116
  opening = next;
125
- acknowledging = next;
117
+ announced = null;
118
+ socket = next;
119
+ staged.clear();
126
120
  const gapped = new Set([...replicated, ...live]);
127
121
  live.clear();
128
122
  for (const conversation of gapped)
129
123
  emitConversation({ kind: 'gap', conversation });
130
124
  emit({ kind: 'opened' });
125
+ if (mode === 'site')
126
+ announce(next, null);
127
+ }
128
+ function announce(via, frame) {
129
+ void discovering.opened(frame).then((discovery) => {
130
+ if (socket !== via)
131
+ return;
132
+ if (discovery === null) {
133
+ via.close();
134
+ return;
135
+ }
136
+ announced = { kind: 'discovered', discovery };
137
+ emit(announced);
138
+ }, () => {
139
+ if (socket === via)
140
+ via.close();
141
+ });
131
142
  }
132
143
  function converse(frame) {
133
144
  const conversation = frame.conversation_id;
@@ -146,6 +157,7 @@ export function houseNotifications(transport, mode) {
146
157
  return;
147
158
  emitConversation({ kind: 'commands', conversation, commands: [] });
148
159
  emitConversation({ kind: 'options', conversation, options: [] });
160
+ emitConversation({ kind: 'subagents', conversation, subagents: [] });
149
161
  return;
150
162
  }
151
163
  if (frame.type === 'commands') {
@@ -164,6 +176,14 @@ export function houseNotifications(transport, mode) {
164
176
  emitConversation({ kind: 'starting', conversation, step: frame.step });
165
177
  return;
166
178
  }
179
+ if (frame.type === 'subagents') {
180
+ emitConversation({
181
+ kind: 'subagents',
182
+ conversation,
183
+ subagents: structuredClone(frame.subagents),
184
+ });
185
+ return;
186
+ }
167
187
  if (frame.type === 'link') {
168
188
  emitConversation({ kind: 'link', conversation, connected: frame.connected });
169
189
  return;
@@ -185,171 +205,30 @@ export function houseNotifications(transport, mode) {
185
205
  return;
186
206
  emitConversation({ kind: 'accepted-message', conversation, message });
187
207
  }
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);
208
+ function stage(frame, via) {
209
+ const held = staged.get(frame.carry);
210
+ if (frame.index !== (held?.parts ?? 0)) {
211
+ staged.delete(frame.carry);
221
212
  return;
222
213
  }
223
214
  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;
215
+ for (const [carry, other] of staged) {
216
+ if (other.authority === frame.authority)
217
+ staged.delete(carry);
218
+ }
245
219
  }
246
- kept.set(frame.carry, next);
220
+ const next = held ?? { authority: frame.authority, parts: 0, entries: [] };
221
+ next.entries.push(...frame.entries);
222
+ next.parts += 1;
223
+ staged.set(frame.carry, next);
247
224
  via?.received(frame.authority, frame.carry, frame.index);
248
225
  }
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);
226
+ function carried(frame) {
227
+ const held = staged.get(frame.carry);
228
+ staged.delete(frame.carry);
229
+ if ((held?.parts ?? 0) !== frame.parts)
270
230
  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
- };
231
+ publish(frame, held?.entries ?? []);
353
232
  }
354
233
  function handle(frame, via) {
355
234
  if (frame.type === 'signal') {
@@ -361,18 +240,31 @@ export function houseNotifications(transport, mode) {
361
240
  return;
362
241
  }
363
242
  if (frame.type === 'opening') {
243
+ if (via !== null && mode === 'credential')
244
+ announce(via, frame);
245
+ return;
246
+ }
247
+ if (frame.type === 'caught_up') {
364
248
  emit({
365
- kind: 'opening',
366
- expired: frame.expired.flatMap((authority) => locators.get(authority) ?? []),
249
+ kind: 'caught-up',
250
+ expired: frame.expired.flatMap((authority) => (tracking.get(authority) ?? []).map((tracked) => tracked.locator)),
367
251
  });
368
252
  return;
369
253
  }
370
254
  if (frame.type === 'authority_changed') {
371
- emit({ kind: 'authority-changed', scope: { authorities: frame.rooms } });
255
+ const change = discovering.changed(frame);
256
+ if (change !== null)
257
+ emit(change);
372
258
  return;
373
259
  }
374
260
  if (frame.type === 'conversations') {
375
- emitConversation({ kind: 'list', entries: frame.conversations.map(listedConversation) });
261
+ emitConversation({
262
+ kind: 'list',
263
+ entries: frame.conversations.map(listedConversation),
264
+ whole: frame.whole,
265
+ more: frame.more,
266
+ cursor: frame.cursor,
267
+ });
376
268
  return;
377
269
  }
378
270
  if (frame.type === 'conversation') {
@@ -388,6 +280,7 @@ export function houseNotifications(transport, mode) {
388
280
  frame.type === 'commands' ||
389
281
  frame.type === 'options' ||
390
282
  frame.type === 'starting' ||
283
+ frame.type === 'subagents' ||
391
284
  frame.type === 'link' ||
392
285
  frame.type === 'deliveries' ||
393
286
  frame.type === 'interactions' ||
@@ -396,57 +289,35 @@ export function houseNotifications(transport, mode) {
396
289
  return;
397
290
  }
398
291
  if (frame.type === 'part')
399
- return stage(frame, via);
400
- if (frame.type === 'carried')
401
- return carried(frame);
292
+ stage(frame, via);
293
+ else if (frame.type === 'carried')
294
+ carried(frame);
402
295
  }
403
296
  function publish(frame, entries) {
404
297
  const at = position(frame.position);
405
298
  const after = position(frame.after);
406
- if (at === null || after === null || after >= at) {
407
- interrupt(frame.authority);
299
+ if (at === null || after === null || after >= at)
408
300
  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;
301
+ const from = roomVersion({ position: frame.after, logEpoch: frame.log_epoch });
302
+ const version = roomVersion({ position: frame.position, logEpoch: frame.log_epoch });
303
+ for (const tracked of tracking.get(frame.authority) ?? []) {
304
+ emit({
305
+ kind: 'room-change',
306
+ locator: tracked.locator,
307
+ from,
308
+ roomVersion: version,
309
+ read: () => changePage(tracked, entries, version, mode),
435
310
  });
436
- return;
437
311
  }
438
- verifying += 1;
439
- receiving = receiving
440
- .then(() => handle(frame, via))
441
- .finally(() => {
442
- verifying -= 1;
443
- });
444
- });
312
+ }
313
+ stream.observe((frame) => handle(frame, socket));
445
314
  stream.opened(reopened);
446
- stream.closed(({ revoked, lost }) => {
315
+ stream.closed(({ revoked, lost, authorityChanged }) => {
447
316
  opening = null;
448
- acknowledging = null;
449
- emit({ kind: 'closed', revoked, lost });
317
+ announced = null;
318
+ socket = null;
319
+ staged.clear();
320
+ emit({ kind: 'closed', revoked, lost, authorityChanged });
450
321
  });
451
322
  return {
452
323
  runtime,
@@ -462,23 +333,30 @@ export function houseNotifications(transport, mode) {
462
333
  replicate(next) {
463
334
  replicated = [...next];
464
335
  },
336
+ hold(cursor) {
337
+ listedCursor = cursor;
338
+ },
465
339
  observe(notify) {
466
340
  conversationObservers.add(notify);
467
341
  return () => {
468
342
  conversationObservers.delete(notify);
469
343
  };
470
344
  },
345
+ ...(mode === 'credential'
346
+ ? {
347
+ agents: () => houseAgents(transport),
348
+ history: (request) => houseHistory(transport, request),
349
+ }
350
+ : {}),
471
351
  },
472
- observe(notify, port) {
352
+ observe(notify) {
473
353
  observers.add(notify);
474
- if (receipts === null) {
475
- receipts = port;
476
- loaded = load(port);
477
- }
478
- if (opening !== null) {
354
+ const replayed = opening === null ? [] : [{ kind: 'opened' }, announced];
355
+ if (replayed.length > 0) {
479
356
  queueMicrotask(() => {
480
- if (observers.has(notify))
481
- notify({ kind: 'opened' });
357
+ for (const event of replayed)
358
+ if (event !== null && observers.has(notify))
359
+ notify(event);
482
360
  });
483
361
  }
484
362
  return () => {
@@ -486,151 +364,19 @@ export function houseNotifications(transport, mode) {
486
364
  };
487
365
  },
488
366
  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
367
  opening = null;
526
- current?.close();
368
+ announced = null;
369
+ socket?.hold(rooms, runtime, listedCursor);
527
370
  },
528
371
  track(entries) {
529
- locators.clear();
372
+ tracking.clear();
530
373
  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);
374
+ tracking.set(entry.authority, [...(tracking.get(entry.authority) ?? []), entry]);
539
375
  }
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
- }
376
+ for (const [carry, held] of staged) {
377
+ if (!tracking.has(held.authority))
378
+ staged.delete(carry);
617
379
  }
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
380
  },
635
381
  };
636
382
  }