@agentshouse/mdruntime-adapter 0.1.0-ca80960fd750d → 0.1.0-ca9b5ec924eff

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 (66) hide show
  1. package/README.md +2 -2
  2. package/dist/actions.d.ts +5 -59
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +11 -289
  5. package/dist/adapter.d.ts.map +1 -1
  6. package/dist/adapter.js +157 -358
  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/chunks.d.ts +38 -0
  11. package/dist/chunks.d.ts.map +1 -0
  12. package/dist/chunks.js +247 -0
  13. package/dist/crockford.d.ts +4 -0
  14. package/dist/crockford.d.ts.map +1 -0
  15. package/dist/crockford.js +3 -0
  16. package/dist/discovery.d.ts +22 -15
  17. package/dist/discovery.d.ts.map +1 -1
  18. package/dist/discovery.js +75 -56
  19. package/dist/history.d.ts +12 -0
  20. package/dist/history.d.ts.map +1 -0
  21. package/dist/history.js +92 -0
  22. package/dist/house-model.d.ts +682 -0
  23. package/dist/house-model.d.ts.map +1 -0
  24. package/dist/house-model.js +394 -0
  25. package/dist/index.d.ts +9 -2
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +9 -2
  28. package/dist/model.d.ts +5 -8
  29. package/dist/model.d.ts.map +1 -1
  30. package/dist/model.js +32 -46
  31. package/dist/read.d.ts +21 -59
  32. package/dist/read.d.ts.map +1 -1
  33. package/dist/read.js +50 -330
  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-types.d.ts +2 -3
  38. package/dist/room-types.d.ts.map +1 -1
  39. package/dist/room-types.js +49 -12
  40. package/dist/search.d.ts +5 -0
  41. package/dist/search.d.ts.map +1 -0
  42. package/dist/search.js +47 -0
  43. package/dist/site.d.ts.map +1 -1
  44. package/dist/site.js +75 -167
  45. package/dist/space-schema.d.ts +7 -0
  46. package/dist/space-schema.d.ts.map +1 -0
  47. package/dist/space-schema.js +18 -0
  48. package/dist/stream.d.ts +16 -15
  49. package/dist/stream.d.ts.map +1 -1
  50. package/dist/stream.js +233 -423
  51. package/dist/transport.d.ts +47 -36
  52. package/dist/transport.d.ts.map +1 -1
  53. package/dist/transport.js +54 -11
  54. package/dist/upload.d.ts.map +1 -1
  55. package/dist/upload.js +2 -2
  56. package/dist/write.d.ts +6 -1
  57. package/dist/write.d.ts.map +1 -1
  58. package/dist/write.js +18 -25
  59. package/package.json +7 -3
  60. package/AGENTS.md +0 -312
  61. package/dist/gather.d.ts +0 -4
  62. package/dist/gather.d.ts.map +0 -1
  63. package/dist/gather.js +0 -35
  64. package/dist/part.d.ts +0 -15
  65. package/dist/part.d.ts.map +0 -1
  66. package/dist/part.js +0 -82
package/dist/stream.js CHANGED
@@ -1,112 +1,114 @@
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 { keptSpaceSchemas, streamedModels } from './model.js';
4
+ import { answered, answering, binaryFileOf, createdOf, folded, forgotten, headChange, revised, } from './read.js';
5
+ import { houseDoorRoute, } from './transport.js';
6
+ import { DECIMAL, logPosition, presented, 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/list'), {});
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);
46
+ yield ROW_WEIGHT + (entry.text?.length ?? 0);
47
+ if (entry.kind === 'folder') {
48
+ folded(answer, entry.path, entry.removed === true);
49
+ continue;
35
50
  }
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] };
51
+ if (entry.path === tracked.schema)
52
+ schemas.set(entry.path, entry);
53
+ forgotten(answer, entry.path);
54
+ if (entry.space !== undefined) {
55
+ answer.removals.add(entry.path);
56
+ continue;
46
57
  }
47
- if (entry.continued === true)
58
+ if (entry.kind === 'removed') {
59
+ answer.removals.add(entry.path);
48
60
  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
61
  }
63
- return true;
64
- }
65
- catch {
66
- return false;
62
+ const binary = binaryFileOf(entry);
63
+ if (binary === undefined)
64
+ answer.documents.set(entry.path, entry.text ?? '');
65
+ else if (binary !== null)
66
+ answer.binaryFiles.set(entry.path, binary);
67
+ if (binary === undefined && entry.facts !== undefined)
68
+ answer.facts.set(entry.path, entry.facts);
69
+ const revision = entry.revision ?? binary?.sha256;
70
+ if (revision !== undefined)
71
+ revised(answer, entry.path, revision);
72
+ const head = headChange(entry);
73
+ if (head)
74
+ answer.changes.set(entry.path, head);
75
+ const created = createdOf(entry);
76
+ if (created)
77
+ answer.created.set(entry.path, created);
67
78
  }
79
+ return { kind: 'net-advancement', roomVersion: version, ...(yield* answered(answer)) };
68
80
  }
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
- };
81
+ async function changePage(tracked, entries, version, mode, spaces, whole) {
82
+ const schemas = new Map();
83
+ const page = await stepped(changed(tracked, entries, version, schemas));
84
+ const models = await streamedModels(schemas, tracked, mode, spaces, whole);
85
+ return models === undefined ? page : { ...page, models };
89
86
  }
90
- export function houseNotifications(transport, mode) {
87
+ export function houseNotifications(transport, mode, discovering, spaces) {
91
88
  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();
89
+ const staged = new Map();
90
+ let socket = null;
91
+ const tracking = new Map();
100
92
  const observers = new Set();
93
+ const replaying = new Map();
101
94
  const conversationObservers = new Set();
102
95
  const modeObservers = new Set();
103
96
  let replicated = [];
97
+ let listedCursor = null;
98
+ const unanswered = [];
99
+ const heldAtZero = new Set();
104
100
  const live = new Set();
105
101
  let opening = null;
102
+ let announced = null;
106
103
  const runtime = crypto.randomUUID();
107
104
  function emit(event) {
108
- for (const observer of observers)
109
- observer(event);
105
+ for (const observer of observers) {
106
+ const held = replaying.get(observer);
107
+ if (held === undefined)
108
+ observer(event);
109
+ else
110
+ held.push(event);
111
+ }
110
112
  }
111
113
  function emitConversation(event) {
112
114
  if (event.kind === 'turn' ||
@@ -115,6 +117,7 @@ export function houseNotifications(transport, mode) {
115
117
  event.kind === 'commands' ||
116
118
  event.kind === 'options' ||
117
119
  event.kind === 'starting' ||
120
+ event.kind === 'subagents' ||
118
121
  event.kind === 'link' ||
119
122
  event.kind === 'deliveries') {
120
123
  live.add(event.conversation);
@@ -124,12 +127,33 @@ export function houseNotifications(transport, mode) {
124
127
  }
125
128
  function reopened(next) {
126
129
  opening = next;
127
- acknowledging = next;
130
+ announced = null;
131
+ socket = next;
132
+ staged.clear();
133
+ unanswered.length = 0;
134
+ heldAtZero.clear();
128
135
  const gapped = new Set([...replicated, ...live]);
129
136
  live.clear();
130
137
  for (const conversation of gapped)
131
138
  emitConversation({ kind: 'gap', conversation });
132
139
  emit({ kind: 'opened' });
140
+ if (mode === 'site')
141
+ announce(next, null);
142
+ }
143
+ function announce(via, frame) {
144
+ void discovering.opened(frame).then((discovery) => {
145
+ if (socket !== via)
146
+ return;
147
+ if (discovery === null) {
148
+ via.close();
149
+ return;
150
+ }
151
+ announced = { kind: 'discovered', discovery };
152
+ emit(announced);
153
+ }, () => {
154
+ if (socket === via)
155
+ via.close();
156
+ });
133
157
  }
134
158
  function converse(frame) {
135
159
  const conversation = frame.conversation_id;
@@ -148,6 +172,7 @@ export function houseNotifications(transport, mode) {
148
172
  return;
149
173
  emitConversation({ kind: 'commands', conversation, commands: [] });
150
174
  emitConversation({ kind: 'options', conversation, options: [] });
175
+ emitConversation({ kind: 'subagents', conversation, subagents: [] });
151
176
  return;
152
177
  }
153
178
  if (frame.type === 'commands') {
@@ -166,6 +191,14 @@ export function houseNotifications(transport, mode) {
166
191
  emitConversation({ kind: 'starting', conversation, step: frame.step });
167
192
  return;
168
193
  }
194
+ if (frame.type === 'subagents') {
195
+ emitConversation({
196
+ kind: 'subagents',
197
+ conversation,
198
+ subagents: structuredClone(frame.subagents),
199
+ });
200
+ return;
201
+ }
169
202
  if (frame.type === 'link') {
170
203
  emitConversation({ kind: 'link', conversation, connected: frame.connected });
171
204
  return;
@@ -187,171 +220,31 @@ export function houseNotifications(transport, mode) {
187
220
  return;
188
221
  emitConversation({ kind: 'accepted-message', conversation, message });
189
222
  }
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);
223
+ function stage(frame, via) {
224
+ const held = staged.get(frame.carry);
225
+ if (frame.index !== (held?.parts ?? 0)) {
226
+ staged.delete(frame.carry);
223
227
  return;
224
228
  }
225
229
  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;
230
+ for (const [carry, other] of staged) {
231
+ if (other.authority === frame.authority)
232
+ staged.delete(carry);
233
+ }
247
234
  }
248
- kept.set(frame.carry, next);
235
+ const next = held ?? { authority: frame.authority, parts: 0, entries: [] };
236
+ keptSpaceSchemas(frame.entries, spaces);
237
+ next.entries.push(...frame.entries);
238
+ next.parts += 1;
239
+ staged.set(frame.carry, next);
249
240
  via?.received(frame.authority, frame.carry, frame.index);
250
241
  }
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);
242
+ function carried(frame) {
243
+ const held = staged.get(frame.carry);
244
+ staged.delete(frame.carry);
245
+ if ((held?.parts ?? 0) !== frame.parts)
267
246
  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);
291
- 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
- };
247
+ publish(frame, held?.entries ?? []);
355
248
  }
356
249
  function handle(frame, via) {
357
250
  if (frame.type === 'signal') {
@@ -363,18 +256,35 @@ export function houseNotifications(transport, mode) {
363
256
  return;
364
257
  }
365
258
  if (frame.type === 'opening') {
259
+ if (via !== null && mode === 'credential')
260
+ announce(via, frame);
261
+ return;
262
+ }
263
+ if (frame.type === 'caught_up') {
264
+ discovering.expired?.(frame.expired);
366
265
  emit({
367
- kind: 'opening',
368
- expired: frame.expired.flatMap((authority) => locators.get(authority) ?? []),
266
+ kind: 'caught-up',
267
+ expired: [
268
+ ...frame.expired.flatMap((authority) => (tracking.get(authority) ?? []).map((tracked) => tracked.locator)),
269
+ ...(unanswered.shift() ?? []),
270
+ ],
369
271
  });
370
272
  return;
371
273
  }
372
274
  if (frame.type === 'authority_changed') {
373
- emit({ kind: 'authority-changed', scope: { authorities: frame.rooms } });
275
+ const change = discovering.changed(frame);
276
+ if (change !== null)
277
+ emit(change);
374
278
  return;
375
279
  }
376
280
  if (frame.type === 'conversations') {
377
- emitConversation({ kind: 'list', entries: frame.conversations.map(listedConversation) });
281
+ emitConversation({
282
+ kind: 'list',
283
+ entries: frame.conversations.map(listedConversation),
284
+ whole: frame.whole,
285
+ more: frame.more,
286
+ cursor: frame.cursor,
287
+ });
378
288
  return;
379
289
  }
380
290
  if (frame.type === 'conversation') {
@@ -390,6 +300,7 @@ export function houseNotifications(transport, mode) {
390
300
  frame.type === 'commands' ||
391
301
  frame.type === 'options' ||
392
302
  frame.type === 'starting' ||
303
+ frame.type === 'subagents' ||
393
304
  frame.type === 'link' ||
394
305
  frame.type === 'deliveries' ||
395
306
  frame.type === 'interactions' ||
@@ -398,57 +309,39 @@ export function houseNotifications(transport, mode) {
398
309
  return;
399
310
  }
400
311
  if (frame.type === 'part')
401
- return stage(frame, via);
402
- if (frame.type === 'carried')
403
- return carried(frame);
312
+ stage(frame, via);
313
+ else if (frame.type === 'carried')
314
+ carried(frame);
404
315
  }
405
316
  function publish(frame, entries) {
406
317
  const at = position(frame.position);
407
318
  const after = position(frame.after);
408
- if (at === null || after === null || after >= at) {
409
- interrupt(frame.authority);
319
+ if (at === null || after === null || after >= at)
410
320
  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;
321
+ const from = after === 0n && !heldAtZero.has(frame.authority)
322
+ ? null
323
+ : roomVersion({ position: frame.after, logEpoch: frame.log_epoch });
324
+ const version = roomVersion({ position: frame.position, logEpoch: frame.log_epoch });
325
+ for (const tracked of tracking.get(frame.authority) ?? []) {
326
+ emit({
327
+ kind: 'room-change',
328
+ locator: tracked.locator,
329
+ from,
330
+ roomVersion: version,
331
+ read: () => changePage(tracked, entries, version, mode, spaces, from === null),
437
332
  });
438
- return;
439
333
  }
440
- verifying += 1;
441
- receiving = receiving
442
- .then(() => handle(frame, via))
443
- .finally(() => {
444
- verifying -= 1;
445
- });
446
- });
334
+ }
335
+ stream.observe((frame) => handle(frame, socket));
447
336
  stream.opened(reopened);
448
- stream.closed(({ revoked, lost }) => {
337
+ stream.closed(({ revoked, lost, authorityChanged }) => {
449
338
  opening = null;
450
- acknowledging = null;
451
- emit({ kind: 'closed', revoked, lost });
339
+ announced = null;
340
+ socket = null;
341
+ staged.clear();
342
+ unanswered.length = 0;
343
+ heldAtZero.clear();
344
+ emit({ kind: 'closed', revoked, lost, authorityChanged });
452
345
  });
453
346
  return {
454
347
  runtime,
@@ -464,23 +357,34 @@ export function houseNotifications(transport, mode) {
464
357
  replicate(next) {
465
358
  replicated = [...next];
466
359
  },
360
+ hold(cursor) {
361
+ listedCursor = cursor;
362
+ },
467
363
  observe(notify) {
468
364
  conversationObservers.add(notify);
469
365
  return () => {
470
366
  conversationObservers.delete(notify);
471
367
  };
472
368
  },
369
+ ...(mode === 'credential'
370
+ ? {
371
+ agents: () => houseAgents(transport),
372
+ history: (request) => houseHistory(transport, request),
373
+ }
374
+ : {}),
473
375
  },
474
- observe(notify, port) {
376
+ observe(notify) {
475
377
  observers.add(notify);
476
- if (receipts === null) {
477
- receipts = port;
478
- loaded = load(port);
479
- }
480
378
  if (opening !== null) {
379
+ const replayed = [{ kind: 'opened' }];
380
+ if (announced !== null)
381
+ replayed.push(announced);
382
+ replaying.set(notify, replayed);
481
383
  queueMicrotask(() => {
482
- if (observers.has(notify))
483
- notify({ kind: 'opened' });
384
+ replaying.delete(notify);
385
+ for (const event of replayed)
386
+ if (observers.has(notify))
387
+ notify(event);
484
388
  });
485
389
  }
486
390
  return () => {
@@ -488,153 +392,59 @@ export function houseNotifications(transport, mode) {
488
392
  };
489
393
  },
490
394
  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
395
  opening = null;
500
- if (current === null)
396
+ announced = null;
397
+ if (socket === null)
501
398
  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
- }
399
+ const entries = new Map([...tracking.values()].flat().map((tracked) => [tracked.locator, tracked]));
400
+ const named = rooms.flatMap((room) => {
401
+ const entry = entries.get(room.locator);
402
+ const at = room.roomVersion === null ? null : logPosition(room.roomVersion);
403
+ return entry === undefined || at === null ? [] : [{ entry, at }];
511
404
  });
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();
529
- },
530
- track(entries) {
531
- locators.clear();
532
- 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
- }
537
- }
538
- for (const authority of lines.keys()) {
539
- if (!locators.has(authority))
540
- lines.delete(authority);
405
+ const unheld = rooms.flatMap((room) => {
406
+ const entry = entries.get(room.locator);
407
+ return entry === undefined || room.roomVersion !== null ? [] : [entry];
408
+ });
409
+ for (const { entry, at } of named) {
410
+ if (at.position === '0')
411
+ heldAtZero.add(entry.authority);
412
+ else
413
+ heldAtZero.delete(entry.authority);
541
414
  }
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)
415
+ for (const entry of unheld)
416
+ heldAtZero.delete(entry.authority);
417
+ const site = mode === 'site';
418
+ unanswered.push(site ? unheld.map((entry) => entry.locator) : []);
419
+ socket.hold([
420
+ ...named.map(({ entry, at }) => presented(entry.authority, at)),
421
+ ...(site ? [] : unheld).map((entry) => presented(entry.authority, { position: '0', logEpoch: entry.logEpoch })),
422
+ ], runtime, listedCursor);
423
+ if (site)
550
424
  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,
425
+ const made = unheld.filter((entry) => entry.position === '0');
426
+ queueMicrotask(() => {
427
+ for (const entry of made) {
428
+ const version = roomVersion(entry);
429
+ emit({
430
+ kind: 'room-change',
431
+ locator: entry.locator,
432
+ from: null,
433
+ roomVersion: version,
434
+ read: () => changePage(entry, [], version, mode, spaces, true),
435
+ });
436
+ }
557
437
  });
558
438
  },
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
- }
439
+ track(entries) {
440
+ tracking.clear();
441
+ for (const entry of entries) {
442
+ tracking.set(entry.authority, [...(tracking.get(entry.authority) ?? []), entry]);
620
443
  }
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
- });
444
+ for (const [carry, held] of staged) {
445
+ if (!tracking.has(held.authority))
446
+ staged.delete(carry);
629
447
  }
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
448
  },
639
449
  };
640
450
  }