@agentshouse/mdruntime-adapter 0.1.0-cedb8e57837a7 → 0.1.0-cf1ebfb67feeb

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 +4 -69
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +7 -357
  5. package/dist/adapter.d.ts.map +1 -1
  6. package/dist/adapter.js +204 -411
  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 +21 -13
  14. package/dist/discovery.d.ts.map +1 -1
  15. package/dist/discovery.js +73 -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 +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 +35 -59
  32. package/dist/read.d.ts.map +1 -1
  33. package/dist/read.js +178 -266
  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 +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 +83 -193
  48. package/dist/stream.d.ts +13 -13
  49. package/dist/stream.d.ts.map +1 -1
  50. package/dist/stream.js +166 -411
  51. package/dist/transport.d.ts +54 -40
  52. package/dist/transport.d.ts.map +1 -1
  53. package/dist/transport.js +54 -11
  54. package/dist/upload.d.ts +12 -0
  55. package/dist/upload.d.ts.map +1 -0
  56. package/dist/upload.js +63 -0
  57. package/dist/write.d.ts +6 -1
  58. package/dist/write.d.ts.map +1 -1
  59. package/dist/write.js +28 -39
  60. package/package.json +8 -4
  61. package/AGENTS.md +0 -304
  62. package/dist/gather.d.ts +0 -4
  63. package/dist/gather.d.ts.map +0 -1
  64. package/dist/gather.js +0 -35
  65. package/dist/handed.d.ts +0 -15
  66. package/dist/handed.d.ts.map +0 -1
  67. package/dist/handed.js +0 -45
  68. package/dist/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,108 +1,95 @@
1
- import { ROW_WEIGHT, } from '@agentshouse/mdruntime';
2
- import { acceptedMessage } from './actions.js';
3
- import { sha256Hex } from './digest.js';
4
- import { answered, answering, binaryFileOf, forgotten, headChange, revised } from './read.js';
5
- import { roomVersion } from './version.js';
6
- const HELD_FRAMES_MAX = 256;
1
+ import { ROW_WEIGHT, stepped, } from '@agentshouse/mdruntime';
2
+ import { acceptedMessage, cursorPosition, houseHistory } from './history.js';
3
+ import { 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';
7
7
  function turnOperation(frame) {
8
8
  const { type: _type, conversation_id: _conversation, turn_id: _turn, ...operation } = frame;
9
9
  return structuredClone(operation);
10
10
  }
11
+ async function houseAgents(transport) {
12
+ const answer = await transport.door(houseDoorRoute('agents/read'), {});
13
+ const agents = answer.status === 200 ? answer.body?.agents : null;
14
+ if (!Array.isArray(agents))
15
+ return null;
16
+ return agents.flatMap((one) => {
17
+ const { id, name, retired_at: retired } = (one ?? {});
18
+ return typeof id === 'string' && typeof name === 'string'
19
+ ? [{ agent: id, name, retired: retired !== null && retired !== undefined }]
20
+ : [];
21
+ });
22
+ }
11
23
  function listedConversation(listed) {
12
24
  return {
13
25
  conversation: listed.conversation_id,
14
26
  agent: listed.agent_id,
27
+ agentName: listed.agent_name,
28
+ agentRetired: listed.agent_retired,
15
29
  name: listed.name,
16
30
  created: listed.created_at,
17
31
  finished: listed.finished_at,
18
32
  outcome: listed.outcome,
19
33
  waiting: listed.waiting,
34
+ newest: listed.newest === null ? null : cursorPosition(listed.newest),
35
+ processEnded: listed.process_ended_at,
20
36
  };
21
37
  }
22
38
  function position(value) {
23
- if (!/^(?:0|[1-9][0-9]*)$/.test(value))
39
+ if (!DECIMAL.test(value))
24
40
  return null;
25
41
  return BigInt(value);
26
42
  }
27
- function assembled(entries) {
28
- const whole = [];
29
- let open = null;
43
+ function* changed(tracked, entries, version, schemas) {
44
+ const answer = answering();
30
45
  for (const entry of entries) {
31
- if (entry.kind === 'piece') {
32
- if (open === null || open.entry.path !== entry.path || entry.text === undefined)
33
- return null;
34
- open.texts.push(entry.text);
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] };
46
+ yield ROW_WEIGHT + (entry.text?.length ?? 0);
47
+ if (entry.kind === 'folder') {
48
+ folded(answer, entry.path, entry.removed === true);
49
+ continue;
46
50
  }
47
- if (entry.continued === true)
51
+ if (entry.path === tracked.schema)
52
+ schemas.set(entry.path, entry);
53
+ forgotten(answer, entry.path);
54
+ if (entry.kind === 'removed') {
55
+ answer.removals.add(entry.path);
48
56
  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
57
  }
63
- return true;
64
- }
65
- catch {
66
- return false;
58
+ const binary = binaryFileOf(entry);
59
+ if (binary === undefined)
60
+ answer.documents.set(entry.path, entry.text ?? '');
61
+ else if (binary !== null)
62
+ answer.binaryFiles.set(entry.path, binary);
63
+ if (binary === undefined && entry.facts !== undefined)
64
+ answer.facts.set(entry.path, entry.facts);
65
+ const revision = entry.revision ?? binary?.sha256;
66
+ if (revision !== undefined)
67
+ revised(answer, entry.path, revision);
68
+ const head = headChange(entry);
69
+ if (head)
70
+ answer.changes.set(entry.path, head);
67
71
  }
72
+ return { kind: 'net-advancement', roomVersion: version, ...(yield* answered(answer)) };
68
73
  }
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
- };
74
+ async function changePage(tracked, entries, version, mode) {
75
+ const schemas = new Map();
76
+ const page = await stepped(changed(tracked, entries, version, schemas));
77
+ const models = await streamedModels(schemas, tracked, mode);
78
+ return models === undefined ? page : { ...page, models };
89
79
  }
90
- export function houseNotifications(transport, mode) {
80
+ export function houseNotifications(transport, mode, discovering) {
91
81
  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();
82
+ const staged = new Map();
83
+ let socket = null;
84
+ const tracking = new Map();
100
85
  const observers = new Set();
101
86
  const conversationObservers = new Set();
102
87
  const modeObservers = new Set();
103
88
  let replicated = [];
89
+ let listedCursor = null;
104
90
  const live = new Set();
105
91
  let opening = null;
92
+ let announced = null;
106
93
  const runtime = crypto.randomUUID();
107
94
  function emit(event) {
108
95
  for (const observer of observers)
@@ -115,6 +102,7 @@ export function houseNotifications(transport, mode) {
115
102
  event.kind === 'commands' ||
116
103
  event.kind === 'options' ||
117
104
  event.kind === 'starting' ||
105
+ event.kind === 'subagents' ||
118
106
  event.kind === 'link' ||
119
107
  event.kind === 'deliveries') {
120
108
  live.add(event.conversation);
@@ -124,12 +112,31 @@ export function houseNotifications(transport, mode) {
124
112
  }
125
113
  function reopened(next) {
126
114
  opening = next;
127
- acknowledging = next;
115
+ announced = null;
116
+ socket = next;
117
+ staged.clear();
128
118
  const gapped = new Set([...replicated, ...live]);
129
119
  live.clear();
130
120
  for (const conversation of gapped)
131
121
  emitConversation({ kind: 'gap', conversation });
132
122
  emit({ kind: 'opened' });
123
+ if (mode === 'site')
124
+ announce(next, null);
125
+ }
126
+ function announce(via, frame) {
127
+ void discovering.opened(frame).then((discovery) => {
128
+ if (socket !== via)
129
+ return;
130
+ if (discovery === null) {
131
+ via.close();
132
+ return;
133
+ }
134
+ announced = { kind: 'discovered', discovery };
135
+ emit(announced);
136
+ }, () => {
137
+ if (socket === via)
138
+ via.close();
139
+ });
133
140
  }
134
141
  function converse(frame) {
135
142
  const conversation = frame.conversation_id;
@@ -148,6 +155,7 @@ export function houseNotifications(transport, mode) {
148
155
  return;
149
156
  emitConversation({ kind: 'commands', conversation, commands: [] });
150
157
  emitConversation({ kind: 'options', conversation, options: [] });
158
+ emitConversation({ kind: 'subagents', conversation, subagents: [] });
151
159
  return;
152
160
  }
153
161
  if (frame.type === 'commands') {
@@ -166,6 +174,14 @@ export function houseNotifications(transport, mode) {
166
174
  emitConversation({ kind: 'starting', conversation, step: frame.step });
167
175
  return;
168
176
  }
177
+ if (frame.type === 'subagents') {
178
+ emitConversation({
179
+ kind: 'subagents',
180
+ conversation,
181
+ subagents: structuredClone(frame.subagents),
182
+ });
183
+ return;
184
+ }
169
185
  if (frame.type === 'link') {
170
186
  emitConversation({ kind: 'link', conversation, connected: frame.connected });
171
187
  return;
@@ -187,171 +203,30 @@ export function houseNotifications(transport, mode) {
187
203
  return;
188
204
  emitConversation({ kind: 'accepted-message', conversation, message });
189
205
  }
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);
206
+ function stage(frame, via) {
207
+ const held = staged.get(frame.carry);
208
+ if (frame.index !== (held?.parts ?? 0)) {
209
+ staged.delete(frame.carry);
223
210
  return;
224
211
  }
225
212
  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;
213
+ for (const [carry, other] of staged) {
214
+ if (other.authority === frame.authority)
215
+ staged.delete(carry);
216
+ }
247
217
  }
248
- kept.set(frame.carry, next);
218
+ const next = held ?? { authority: frame.authority, parts: 0, entries: [] };
219
+ next.entries.push(...frame.entries);
220
+ next.parts += 1;
221
+ staged.set(frame.carry, next);
249
222
  via?.received(frame.authority, frame.carry, frame.index);
250
223
  }
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);
224
+ function carried(frame) {
225
+ const held = staged.get(frame.carry);
226
+ staged.delete(frame.carry);
227
+ if ((held?.parts ?? 0) !== frame.parts)
291
228
  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
- };
229
+ publish(frame, held?.entries ?? []);
355
230
  }
356
231
  function handle(frame, via) {
357
232
  if (frame.type === 'signal') {
@@ -363,18 +238,31 @@ export function houseNotifications(transport, mode) {
363
238
  return;
364
239
  }
365
240
  if (frame.type === 'opening') {
241
+ if (via !== null && mode === 'credential')
242
+ announce(via, frame);
243
+ return;
244
+ }
245
+ if (frame.type === 'caught_up') {
366
246
  emit({
367
- kind: 'opening',
368
- expired: frame.expired.flatMap((authority) => locators.get(authority) ?? []),
247
+ kind: 'caught-up',
248
+ expired: frame.expired.flatMap((authority) => (tracking.get(authority) ?? []).map((tracked) => tracked.locator)),
369
249
  });
370
250
  return;
371
251
  }
372
252
  if (frame.type === 'authority_changed') {
373
- emit({ kind: 'authority-changed', scope: { authorities: frame.rooms } });
253
+ const change = discovering.changed(frame);
254
+ if (change !== null)
255
+ emit(change);
374
256
  return;
375
257
  }
376
258
  if (frame.type === 'conversations') {
377
- emitConversation({ kind: 'list', entries: frame.conversations.map(listedConversation) });
259
+ emitConversation({
260
+ kind: 'list',
261
+ entries: frame.conversations.map(listedConversation),
262
+ whole: frame.whole,
263
+ more: frame.more,
264
+ cursor: frame.cursor,
265
+ });
378
266
  return;
379
267
  }
380
268
  if (frame.type === 'conversation') {
@@ -390,6 +278,7 @@ export function houseNotifications(transport, mode) {
390
278
  frame.type === 'commands' ||
391
279
  frame.type === 'options' ||
392
280
  frame.type === 'starting' ||
281
+ frame.type === 'subagents' ||
393
282
  frame.type === 'link' ||
394
283
  frame.type === 'deliveries' ||
395
284
  frame.type === 'interactions' ||
@@ -398,57 +287,35 @@ export function houseNotifications(transport, mode) {
398
287
  return;
399
288
  }
400
289
  if (frame.type === 'part')
401
- return stage(frame, via);
402
- if (frame.type === 'carried')
403
- return carried(frame);
290
+ stage(frame, via);
291
+ else if (frame.type === 'carried')
292
+ carried(frame);
404
293
  }
405
294
  function publish(frame, entries) {
406
295
  const at = position(frame.position);
407
296
  const after = position(frame.after);
408
- if (at === null || after === null || after >= at) {
409
- interrupt(frame.authority);
297
+ if (at === null || after === null || after >= at)
410
298
  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;
299
+ const from = roomVersion({ position: frame.after, logEpoch: frame.log_epoch });
300
+ const version = roomVersion({ position: frame.position, logEpoch: frame.log_epoch });
301
+ for (const tracked of tracking.get(frame.authority) ?? []) {
302
+ emit({
303
+ kind: 'room-change',
304
+ locator: tracked.locator,
305
+ from,
306
+ roomVersion: version,
307
+ read: () => changePage(tracked, entries, version, mode),
437
308
  });
438
- return;
439
309
  }
440
- verifying += 1;
441
- receiving = receiving
442
- .then(() => handle(frame, via))
443
- .finally(() => {
444
- verifying -= 1;
445
- });
446
- });
310
+ }
311
+ stream.observe((frame) => handle(frame, socket));
447
312
  stream.opened(reopened);
448
- stream.closed(({ revoked, lost }) => {
313
+ stream.closed(({ revoked, lost, authorityChanged }) => {
449
314
  opening = null;
450
- acknowledging = null;
451
- emit({ kind: 'closed', revoked, lost });
315
+ announced = null;
316
+ socket = null;
317
+ staged.clear();
318
+ emit({ kind: 'closed', revoked, lost, authorityChanged });
452
319
  });
453
320
  return {
454
321
  runtime,
@@ -464,23 +331,30 @@ export function houseNotifications(transport, mode) {
464
331
  replicate(next) {
465
332
  replicated = [...next];
466
333
  },
334
+ hold(cursor) {
335
+ listedCursor = cursor;
336
+ },
467
337
  observe(notify) {
468
338
  conversationObservers.add(notify);
469
339
  return () => {
470
340
  conversationObservers.delete(notify);
471
341
  };
472
342
  },
343
+ ...(mode === 'credential'
344
+ ? {
345
+ agents: () => houseAgents(transport),
346
+ history: (request) => houseHistory(transport, request),
347
+ }
348
+ : {}),
473
349
  },
474
- observe(notify, port) {
350
+ observe(notify) {
475
351
  observers.add(notify);
476
- if (receipts === null) {
477
- receipts = port;
478
- loaded = load(port);
479
- }
480
- if (opening !== null) {
352
+ const replayed = opening === null ? [] : [{ kind: 'opened' }, announced];
353
+ if (replayed.length > 0) {
481
354
  queueMicrotask(() => {
482
- if (observers.has(notify))
483
- notify({ kind: 'opened' });
355
+ for (const event of replayed)
356
+ if (event !== null && observers.has(notify))
357
+ notify(event);
484
358
  });
485
359
  }
486
360
  return () => {
@@ -488,138 +362,19 @@ export function houseNotifications(transport, mode) {
488
362
  };
489
363
  },
490
364
  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
365
  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();
366
+ announced = null;
367
+ socket?.hold(rooms, runtime, listedCursor);
529
368
  },
530
369
  track(entries) {
531
- locators.clear();
370
+ tracking.clear();
532
371
  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
- }
372
+ tracking.set(entry.authority, [...(tracking.get(entry.authority) ?? []), entry]);
537
373
  }
538
- for (const authority of lines.keys()) {
539
- if (!locators.has(authority))
540
- lines.delete(authority);
374
+ for (const [carry, held] of staged) {
375
+ if (!tracking.has(held.authority))
376
+ staged.delete(carry);
541
377
  }
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
- *advance(tracked, from, holds) {
560
- const line = lines.get(tracked.authority);
561
- const start = position(from.position);
562
- if (line === undefined || start === null || line.logEpoch !== from.logEpoch)
563
- return null;
564
- const pending = line.frames.filter((frame) => frame.position > start);
565
- const first = pending[0];
566
- if (first === undefined || first.after > start)
567
- return null;
568
- const answer = answering();
569
- const schemas = new Map();
570
- for (const frame of pending) {
571
- for (const entry of frame.entries) {
572
- yield ROW_WEIGHT + (entry.text?.length ?? 0);
573
- if (entry.kind === 'edited' &&
574
- entry.revision !== undefined &&
575
- holds?.(entry.path, entry.revision) === true) {
576
- forgotten(answer, entry.path);
577
- continue;
578
- }
579
- if (tracked.schemas.includes(entry.path))
580
- schemas.set(entry.path, entry);
581
- if (entry.kind === 'removed') {
582
- forgotten(answer, entry.path);
583
- answer.removals.add(entry.path);
584
- continue;
585
- }
586
- const binary = binaryFileOf(entry);
587
- if (binary === null || (binary === undefined && entry.text === undefined))
588
- return null;
589
- if (mode === 'credential' && entry.revision === undefined)
590
- return null;
591
- const head = headChange(entry);
592
- if (head === null)
593
- return null;
594
- forgotten(answer, entry.path);
595
- if (binary === undefined)
596
- answer.documents.set(entry.path, entry.text);
597
- else
598
- answer.binaryFiles.set(entry.path, binary);
599
- const revision = entry.revision ?? binary?.sha256;
600
- if (revision !== undefined)
601
- revised(answer, entry.path, revision);
602
- if (head !== undefined)
603
- answer.changes.set(entry.path, head);
604
- }
605
- }
606
- const last = pending.at(-1);
607
- const current = lines.get(tracked.authority);
608
- if (current?.logEpoch === line.logEpoch) {
609
- lines.set(tracked.authority, {
610
- ...current,
611
- frames: current.frames.filter((frame) => frame.position > start),
612
- held: current.held !== null && current.held > last.position ? current.held : last.position,
613
- });
614
- }
615
- return {
616
- page: {
617
- kind: 'net-advancement',
618
- roomVersion: roomVersion({ position: last.position.toString(), logEpoch: line.logEpoch }),
619
- ...(yield* answered(answer)),
620
- },
621
- schemas,
622
- };
623
378
  },
624
379
  };
625
380
  }