@agentshouse/mdruntime-adapter 0.1.0-cedab65f24a09 → 0.1.0-cf059d0c8e9c0

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