@agentshouse/mdruntime-adapter 0.1.0-ccda1a2da11c3 → 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 (67) hide show
  1. package/README.md +2 -2
  2. package/dist/actions.d.ts +3 -71
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +1 -317
  5. package/dist/adapter.d.ts.map +1 -1
  6. package/dist/adapter.js +171 -336
  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 +12 -12
  14. package/dist/discovery.d.ts.map +1 -1
  15. package/dist/discovery.js +44 -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 -3
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +9 -2
  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 +12 -11
  29. package/dist/part.d.ts.map +1 -1
  30. package/dist/part.js +66 -56
  31. package/dist/read.d.ts +19 -56
  32. package/dist/read.d.ts.map +1 -1
  33. package/dist/read.js +138 -297
  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 +2 -1
  41. package/dist/room-types.d.ts.map +1 -1
  42. package/dist/room-types.js +62 -3
  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 +79 -177
  48. package/dist/stream.d.ts +13 -14
  49. package/dist/stream.d.ts.map +1 -1
  50. package/dist/stream.js +168 -426
  51. package/dist/transport.d.ts +48 -38
  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 +6 -1
  58. package/dist/write.d.ts.map +1 -1
  59. package/dist/write.js +19 -32
  60. package/package.json +8 -4
  61. package/AGENTS.md +0 -309
  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/referrer.d.ts +0 -9
  66. package/dist/referrer.d.ts.map +0 -1
  67. package/dist/referrer.js +0 -41
package/dist/stream.js CHANGED
@@ -1,108 +1,97 @@
1
- import { ROW_WEIGHT, } from '@agentshouse/mdruntime';
2
- import { acceptedMessage } from './actions.js';
3
- import { sha256Hex } from './digest.js';
4
- import { answered, answering, binaryFileOf, folded, 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 { 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';
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),
20
35
  };
21
36
  }
22
37
  function position(value) {
23
- if (!/^(?:0|[1-9][0-9]*)$/.test(value))
38
+ if (!DECIMAL.test(value))
24
39
  return null;
25
40
  return BigInt(value);
26
41
  }
27
- function assembled(entries) {
28
- const whole = [];
29
- let open = null;
42
+ function* changed(tracked, entries, version, schemas) {
43
+ const answer = answering();
30
44
  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);
35
- }
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] };
45
+ yield ROW_WEIGHT + (entry.text?.length ?? 0);
46
+ if (entry.kind === 'folder') {
47
+ folded(answer, entry.path, entry.removed === true);
48
+ continue;
46
49
  }
47
- 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);
48
55
  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
56
  }
63
- return true;
64
- }
65
- catch {
66
- 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);
67
73
  }
74
+ return { kind: 'net-advancement', roomVersion: version, ...(yield* answered(answer)) };
68
75
  }
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
- };
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 };
89
81
  }
90
- export function houseNotifications(transport, mode) {
82
+ export function houseNotifications(transport, mode, discovering) {
91
83
  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();
84
+ const staged = new Map();
85
+ let socket = null;
86
+ const tracking = new Map();
100
87
  const observers = new Set();
101
88
  const conversationObservers = new Set();
102
89
  const modeObservers = new Set();
103
90
  let replicated = [];
91
+ let listedCursor = null;
104
92
  const live = new Set();
105
93
  let opening = null;
94
+ let announced = null;
106
95
  const runtime = crypto.randomUUID();
107
96
  function emit(event) {
108
97
  for (const observer of observers)
@@ -115,6 +104,7 @@ export function houseNotifications(transport, mode) {
115
104
  event.kind === 'commands' ||
116
105
  event.kind === 'options' ||
117
106
  event.kind === 'starting' ||
107
+ event.kind === 'subagents' ||
118
108
  event.kind === 'link' ||
119
109
  event.kind === 'deliveries') {
120
110
  live.add(event.conversation);
@@ -124,12 +114,31 @@ export function houseNotifications(transport, mode) {
124
114
  }
125
115
  function reopened(next) {
126
116
  opening = next;
127
- acknowledging = next;
117
+ announced = null;
118
+ socket = next;
119
+ staged.clear();
128
120
  const gapped = new Set([...replicated, ...live]);
129
121
  live.clear();
130
122
  for (const conversation of gapped)
131
123
  emitConversation({ kind: 'gap', conversation });
132
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
+ });
133
142
  }
134
143
  function converse(frame) {
135
144
  const conversation = frame.conversation_id;
@@ -148,6 +157,7 @@ export function houseNotifications(transport, mode) {
148
157
  return;
149
158
  emitConversation({ kind: 'commands', conversation, commands: [] });
150
159
  emitConversation({ kind: 'options', conversation, options: [] });
160
+ emitConversation({ kind: 'subagents', conversation, subagents: [] });
151
161
  return;
152
162
  }
153
163
  if (frame.type === 'commands') {
@@ -166,6 +176,14 @@ export function houseNotifications(transport, mode) {
166
176
  emitConversation({ kind: 'starting', conversation, step: frame.step });
167
177
  return;
168
178
  }
179
+ if (frame.type === 'subagents') {
180
+ emitConversation({
181
+ kind: 'subagents',
182
+ conversation,
183
+ subagents: structuredClone(frame.subagents),
184
+ });
185
+ return;
186
+ }
169
187
  if (frame.type === 'link') {
170
188
  emitConversation({ kind: 'link', conversation, connected: frame.connected });
171
189
  return;
@@ -187,171 +205,30 @@ export function houseNotifications(transport, mode) {
187
205
  return;
188
206
  emitConversation({ kind: 'accepted-message', conversation, message });
189
207
  }
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);
208
+ function stage(frame, via) {
209
+ const held = staged.get(frame.carry);
210
+ if (frame.index !== (held?.parts ?? 0)) {
211
+ staged.delete(frame.carry);
223
212
  return;
224
213
  }
225
214
  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;
215
+ for (const [carry, other] of staged) {
216
+ if (other.authority === frame.authority)
217
+ staged.delete(carry);
218
+ }
247
219
  }
248
- 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);
249
224
  via?.received(frame.authority, frame.carry, frame.index);
250
225
  }
251
- async function keptEntries(carry, parts) {
252
- if (parts === 0)
253
- return [];
254
- if (receipts === null)
255
- return null;
256
- const read = (await receipts.parts(carry, parts));
257
- if (read.length !== parts)
258
- return null;
259
- const entries = assembled(read.flatMap((part) => part.entries));
260
- return entries !== null && (await verified(entries)) ? entries : null;
261
- }
262
- async function carried(frame) {
263
- await loaded;
264
- const held = kept.get(frame.carry);
265
- if (frame.parts > 0 && held?.parts !== frame.parts) {
266
- await lost(frame.authority, frame.carry);
267
- return;
268
- }
269
- const entries = await keptEntries(frame.carry, frame.parts).catch(() => null);
270
- if (entries === null) {
271
- await lost(frame.authority, frame.carry);
272
- return;
273
- }
274
- const fenced = {
275
- ...(held ?? {
276
- authority: frame.authority,
277
- carry: frame.carry,
278
- logEpoch: frame.log_epoch,
279
- after: frame.after,
280
- parts: 0,
281
- resume: null,
282
- }),
283
- after: frame.after,
284
- fenced: { position: frame.position, logEpoch: frame.log_epoch },
285
- };
286
- try {
287
- await receipts?.keep(frame.carry, fenced);
288
- }
289
- catch {
290
- await lost(frame.authority, frame.carry);
226
+ function carried(frame) {
227
+ const held = staged.get(frame.carry);
228
+ staged.delete(frame.carry);
229
+ if ((held?.parts ?? 0) !== frame.parts)
291
230
  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
- };
231
+ publish(frame, held?.entries ?? []);
355
232
  }
356
233
  function handle(frame, via) {
357
234
  if (frame.type === 'signal') {
@@ -363,18 +240,31 @@ export function houseNotifications(transport, mode) {
363
240
  return;
364
241
  }
365
242
  if (frame.type === 'opening') {
243
+ if (via !== null && mode === 'credential')
244
+ announce(via, frame);
245
+ return;
246
+ }
247
+ if (frame.type === 'caught_up') {
366
248
  emit({
367
- kind: 'opening',
368
- 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)),
369
251
  });
370
252
  return;
371
253
  }
372
254
  if (frame.type === 'authority_changed') {
373
- emit({ kind: 'authority-changed', scope: { authorities: frame.rooms } });
255
+ const change = discovering.changed(frame);
256
+ if (change !== null)
257
+ emit(change);
374
258
  return;
375
259
  }
376
260
  if (frame.type === 'conversations') {
377
- 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
+ });
378
268
  return;
379
269
  }
380
270
  if (frame.type === 'conversation') {
@@ -390,6 +280,7 @@ export function houseNotifications(transport, mode) {
390
280
  frame.type === 'commands' ||
391
281
  frame.type === 'options' ||
392
282
  frame.type === 'starting' ||
283
+ frame.type === 'subagents' ||
393
284
  frame.type === 'link' ||
394
285
  frame.type === 'deliveries' ||
395
286
  frame.type === 'interactions' ||
@@ -398,57 +289,35 @@ export function houseNotifications(transport, mode) {
398
289
  return;
399
290
  }
400
291
  if (frame.type === 'part')
401
- return stage(frame, via);
402
- if (frame.type === 'carried')
403
- return carried(frame);
292
+ stage(frame, via);
293
+ else if (frame.type === 'carried')
294
+ carried(frame);
404
295
  }
405
296
  function publish(frame, entries) {
406
297
  const at = position(frame.position);
407
298
  const after = position(frame.after);
408
- if (at === null || after === null || after >= at) {
409
- interrupt(frame.authority);
299
+ if (at === null || after === null || after >= at)
410
300
  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;
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),
437
310
  });
438
- return;
439
311
  }
440
- verifying += 1;
441
- receiving = receiving
442
- .then(() => handle(frame, via))
443
- .finally(() => {
444
- verifying -= 1;
445
- });
446
- });
312
+ }
313
+ stream.observe((frame) => handle(frame, socket));
447
314
  stream.opened(reopened);
448
- stream.closed(({ revoked, lost }) => {
315
+ stream.closed(({ revoked, lost, authorityChanged }) => {
449
316
  opening = null;
450
- acknowledging = null;
451
- emit({ kind: 'closed', revoked, lost });
317
+ announced = null;
318
+ socket = null;
319
+ staged.clear();
320
+ emit({ kind: 'closed', revoked, lost, authorityChanged });
452
321
  });
453
322
  return {
454
323
  runtime,
@@ -464,23 +333,30 @@ export function houseNotifications(transport, mode) {
464
333
  replicate(next) {
465
334
  replicated = [...next];
466
335
  },
336
+ hold(cursor) {
337
+ listedCursor = cursor;
338
+ },
467
339
  observe(notify) {
468
340
  conversationObservers.add(notify);
469
341
  return () => {
470
342
  conversationObservers.delete(notify);
471
343
  };
472
344
  },
345
+ ...(mode === 'credential'
346
+ ? {
347
+ agents: () => houseAgents(transport),
348
+ history: (request) => houseHistory(transport, request),
349
+ }
350
+ : {}),
473
351
  },
474
- observe(notify, port) {
352
+ observe(notify) {
475
353
  observers.add(notify);
476
- if (receipts === null) {
477
- receipts = port;
478
- loaded = load(port);
479
- }
480
- if (opening !== null) {
354
+ const replayed = opening === null ? [] : [{ kind: 'opened' }, announced];
355
+ if (replayed.length > 0) {
481
356
  queueMicrotask(() => {
482
- if (observers.has(notify))
483
- notify({ kind: 'opened' });
357
+ for (const event of replayed)
358
+ if (event !== null && observers.has(notify))
359
+ notify(event);
484
360
  });
485
361
  }
486
362
  return () => {
@@ -488,153 +364,19 @@ export function houseNotifications(transport, mode) {
488
364
  };
489
365
  },
490
366
  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
367
  opening = null;
500
- if (current === null)
501
- return;
502
- void loaded
503
- .then(() => Promise.all(rooms.map(restored)))
504
- .then((presented) => {
505
- current.hold(presented, runtime);
506
- for (const [index, room] of presented.entries()) {
507
- if (room.position === rooms[index].position)
508
- continue;
509
- announce(room.authority, { position: room.position, logEpoch: room.log_epoch });
510
- }
511
- });
512
- },
513
- published(locator, at) {
514
- const authority = [...locators].find(([, held]) => held.includes(locator))?.[0];
515
- const reached = position(at.position);
516
- if (authority === undefined || reached === null)
517
- return;
518
- void forget([...kept.values()]
519
- .filter((carry) => carry.authority === authority &&
520
- carry.fenced !== null &&
521
- carry.fenced.logEpoch === at.logEpoch &&
522
- BigInt(carry.fenced.position) <= reached)
523
- .map((carry) => carry.carry));
524
- },
525
- reopen() {
526
- const current = opening;
527
- opening = null;
528
- current?.close();
368
+ announced = null;
369
+ socket?.hold(rooms, runtime, listedCursor);
529
370
  },
530
371
  track(entries) {
531
- locators.clear();
372
+ tracking.clear();
532
373
  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
- }
374
+ tracking.set(entry.authority, [...(tracking.get(entry.authority) ?? []), entry]);
537
375
  }
538
- for (const authority of lines.keys()) {
539
- if (!locators.has(authority))
540
- lines.delete(authority);
376
+ for (const [carry, held] of staged) {
377
+ if (!tracking.has(held.authority))
378
+ staged.delete(carry);
541
379
  }
542
- void loaded.then(() => forget([...kept.values()]
543
- .filter((carry) => !locators.has(carry.authority))
544
- .map((carry) => carry.carry)));
545
- },
546
- reach(authority, at) {
547
- const line = lines.get(authority);
548
- const held = position(at.position);
549
- if (line === undefined || held === null || line.logEpoch !== at.logEpoch)
550
- return;
551
- if (line.held !== null && held <= line.held)
552
- return;
553
- lines.set(authority, {
554
- ...line,
555
- frames: line.frames.filter((frame) => frame.position > held),
556
- held,
557
- });
558
- },
559
- edited(tracked, from) {
560
- const line = lines.get(tracked.authority);
561
- const start = position(from.position);
562
- if (line === undefined || start === null || line.logEpoch !== from.logEpoch)
563
- return [];
564
- return [
565
- ...new Set(line.frames
566
- .filter((frame) => frame.position > start)
567
- .flatMap((frame) => frame.entries.flatMap((entry) => entry.kind === 'edited' && entry.revision !== undefined ? [entry.path] : []))),
568
- ];
569
- },
570
- *advance(tracked, from, holds) {
571
- const line = lines.get(tracked.authority);
572
- const start = position(from.position);
573
- if (line === undefined || start === null || line.logEpoch !== from.logEpoch)
574
- return null;
575
- const pending = line.frames.filter((frame) => frame.position > start);
576
- const first = pending[0];
577
- if (first === undefined || first.after > start)
578
- return null;
579
- const answer = answering();
580
- const schemas = new Map();
581
- for (const frame of pending) {
582
- for (const entry of frame.entries) {
583
- yield ROW_WEIGHT + (entry.text?.length ?? 0);
584
- if (entry.kind === 'folder') {
585
- folded(answer, entry.path, entry.removed === true);
586
- continue;
587
- }
588
- if (entry.kind === 'edited' &&
589
- entry.revision !== undefined &&
590
- holds?.(entry.path, entry.revision) === true) {
591
- forgotten(answer, entry.path);
592
- continue;
593
- }
594
- if (tracked.schemas.includes(entry.path))
595
- schemas.set(entry.path, entry);
596
- if (entry.kind === 'removed') {
597
- forgotten(answer, entry.path);
598
- answer.removals.add(entry.path);
599
- continue;
600
- }
601
- const binary = binaryFileOf(entry);
602
- if (binary === null || (binary === undefined && entry.text === undefined))
603
- return null;
604
- if (mode === 'credential' && entry.revision === undefined)
605
- return null;
606
- const head = headChange(entry);
607
- if (head === null)
608
- return null;
609
- forgotten(answer, entry.path);
610
- if (binary === undefined)
611
- answer.documents.set(entry.path, entry.text);
612
- else
613
- answer.binaryFiles.set(entry.path, binary);
614
- const revision = entry.revision ?? binary?.sha256;
615
- if (revision !== undefined)
616
- revised(answer, entry.path, revision);
617
- if (head !== undefined)
618
- answer.changes.set(entry.path, head);
619
- }
620
- }
621
- const last = pending.at(-1);
622
- const current = lines.get(tracked.authority);
623
- if (current?.logEpoch === line.logEpoch) {
624
- lines.set(tracked.authority, {
625
- ...current,
626
- frames: current.frames.filter((frame) => frame.position > start),
627
- held: current.held !== null && current.held > last.position ? current.held : last.position,
628
- });
629
- }
630
- return {
631
- page: {
632
- kind: 'net-advancement',
633
- roomVersion: roomVersion({ position: last.position.toString(), logEpoch: line.logEpoch }),
634
- ...(yield* answered(answer)),
635
- },
636
- schemas,
637
- };
638
380
  },
639
381
  };
640
382
  }