@agentshouse/mdruntime-adapter 0.1.0-ce3ced044cab5 → 0.1.0-ce3f1a73f440b

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.
package/dist/stream.js CHANGED
@@ -1,108 +1,92 @@
1
- import { ROW_WEIGHT, } from '@agentshouse/mdruntime';
2
- import { acceptedMessage } from './actions.js';
3
- import { sha256Hex } from './digest.js';
1
+ import { ROW_WEIGHT, stepped, } from '@agentshouse/mdruntime';
2
+ import { acceptedMessage, cursorPosition, houseHistory } from './history.js';
3
+ import { streamedModels } from './model.js';
4
4
  import { answered, answering, binaryFileOf, folded, forgotten, headChange, revised, } from './read.js';
5
- import { roomVersion } from './version.js';
6
- const HELD_FRAMES_MAX = 256;
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
+ 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);
67
68
  }
69
+ return { kind: 'net-advancement', roomVersion: version, ...(yield* answered(answer)) };
68
70
  }
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
- };
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 };
89
76
  }
90
- export function houseNotifications(transport, mode) {
77
+ export function houseNotifications(transport, mode, discovering) {
91
78
  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();
79
+ const staged = new Map();
80
+ let socket = null;
81
+ const tracking = new Map();
100
82
  const observers = new Set();
101
83
  const conversationObservers = new Set();
102
84
  const modeObservers = new Set();
103
85
  let replicated = [];
86
+ let listedCursor = null;
104
87
  const live = new Set();
105
88
  let opening = null;
89
+ let announced = null;
106
90
  const runtime = crypto.randomUUID();
107
91
  function emit(event) {
108
92
  for (const observer of observers)
@@ -115,6 +99,7 @@ export function houseNotifications(transport, mode) {
115
99
  event.kind === 'commands' ||
116
100
  event.kind === 'options' ||
117
101
  event.kind === 'starting' ||
102
+ event.kind === 'subagents' ||
118
103
  event.kind === 'link' ||
119
104
  event.kind === 'deliveries') {
120
105
  live.add(event.conversation);
@@ -124,12 +109,31 @@ export function houseNotifications(transport, mode) {
124
109
  }
125
110
  function reopened(next) {
126
111
  opening = next;
127
- acknowledging = next;
112
+ announced = null;
113
+ socket = next;
114
+ staged.clear();
128
115
  const gapped = new Set([...replicated, ...live]);
129
116
  live.clear();
130
117
  for (const conversation of gapped)
131
118
  emitConversation({ kind: 'gap', conversation });
132
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
+ });
133
137
  }
134
138
  function converse(frame) {
135
139
  const conversation = frame.conversation_id;
@@ -148,6 +152,7 @@ export function houseNotifications(transport, mode) {
148
152
  return;
149
153
  emitConversation({ kind: 'commands', conversation, commands: [] });
150
154
  emitConversation({ kind: 'options', conversation, options: [] });
155
+ emitConversation({ kind: 'subagents', conversation, subagents: [] });
151
156
  return;
152
157
  }
153
158
  if (frame.type === 'commands') {
@@ -166,6 +171,14 @@ export function houseNotifications(transport, mode) {
166
171
  emitConversation({ kind: 'starting', conversation, step: frame.step });
167
172
  return;
168
173
  }
174
+ if (frame.type === 'subagents') {
175
+ emitConversation({
176
+ kind: 'subagents',
177
+ conversation,
178
+ subagents: structuredClone(frame.subagents),
179
+ });
180
+ return;
181
+ }
169
182
  if (frame.type === 'link') {
170
183
  emitConversation({ kind: 'link', conversation, connected: frame.connected });
171
184
  return;
@@ -187,171 +200,30 @@ export function houseNotifications(transport, mode) {
187
200
  return;
188
201
  emitConversation({ kind: 'accepted-message', conversation, message });
189
202
  }
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);
203
+ function stage(frame, via) {
204
+ const held = staged.get(frame.carry);
205
+ if (frame.index !== (held?.parts ?? 0)) {
206
+ staged.delete(frame.carry);
223
207
  return;
224
208
  }
225
209
  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;
210
+ for (const [carry, other] of staged) {
211
+ if (other.authority === frame.authority)
212
+ staged.delete(carry);
213
+ }
247
214
  }
248
- 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);
249
219
  via?.received(frame.authority, frame.carry, frame.index);
250
220
  }
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);
221
+ function carried(frame) {
222
+ const held = staged.get(frame.carry);
223
+ staged.delete(frame.carry);
224
+ if ((held?.parts ?? 0) !== frame.parts)
291
225
  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
- };
226
+ publish(frame, held?.entries ?? []);
355
227
  }
356
228
  function handle(frame, via) {
357
229
  if (frame.type === 'signal') {
@@ -363,18 +235,31 @@ export function houseNotifications(transport, mode) {
363
235
  return;
364
236
  }
365
237
  if (frame.type === 'opening') {
238
+ if (via !== null && mode === 'credential')
239
+ announce(via, frame);
240
+ return;
241
+ }
242
+ if (frame.type === 'caught_up') {
366
243
  emit({
367
- kind: 'opening',
368
- 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)),
369
246
  });
370
247
  return;
371
248
  }
372
249
  if (frame.type === 'authority_changed') {
373
- emit({ kind: 'authority-changed', scope: { authorities: frame.rooms } });
250
+ const change = discovering.changed(frame);
251
+ if (change !== null)
252
+ emit(change);
374
253
  return;
375
254
  }
376
255
  if (frame.type === 'conversations') {
377
- 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
+ });
378
263
  return;
379
264
  }
380
265
  if (frame.type === 'conversation') {
@@ -390,6 +275,7 @@ export function houseNotifications(transport, mode) {
390
275
  frame.type === 'commands' ||
391
276
  frame.type === 'options' ||
392
277
  frame.type === 'starting' ||
278
+ frame.type === 'subagents' ||
393
279
  frame.type === 'link' ||
394
280
  frame.type === 'deliveries' ||
395
281
  frame.type === 'interactions' ||
@@ -398,56 +284,34 @@ export function houseNotifications(transport, mode) {
398
284
  return;
399
285
  }
400
286
  if (frame.type === 'part')
401
- return stage(frame, via);
402
- if (frame.type === 'carried')
403
- return carried(frame);
287
+ stage(frame, via);
288
+ else if (frame.type === 'carried')
289
+ carried(frame);
404
290
  }
405
291
  function publish(frame, entries) {
406
292
  const at = position(frame.position);
407
293
  const after = position(frame.after);
408
- if (at === null || after === null || after >= at) {
409
- interrupt(frame.authority);
294
+ if (at === null || after === null || after >= at)
410
295
  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;
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),
437
305
  });
438
- return;
439
306
  }
440
- verifying += 1;
441
- receiving = receiving
442
- .then(() => handle(frame, via))
443
- .finally(() => {
444
- verifying -= 1;
445
- });
446
- });
307
+ }
308
+ stream.observe((frame) => handle(frame, socket));
447
309
  stream.opened(reopened);
448
310
  stream.closed(({ revoked, lost }) => {
449
311
  opening = null;
450
- acknowledging = null;
312
+ announced = null;
313
+ socket = null;
314
+ staged.clear();
451
315
  emit({ kind: 'closed', revoked, lost });
452
316
  });
453
317
  return {
@@ -464,23 +328,30 @@ export function houseNotifications(transport, mode) {
464
328
  replicate(next) {
465
329
  replicated = [...next];
466
330
  },
331
+ hold(cursor) {
332
+ listedCursor = cursor;
333
+ },
467
334
  observe(notify) {
468
335
  conversationObservers.add(notify);
469
336
  return () => {
470
337
  conversationObservers.delete(notify);
471
338
  };
472
339
  },
340
+ ...(mode === 'credential'
341
+ ? {
342
+ agents: () => houseAgents(transport),
343
+ history: (request) => houseHistory(transport, request),
344
+ }
345
+ : {}),
473
346
  },
474
- observe(notify, port) {
347
+ observe(notify) {
475
348
  observers.add(notify);
476
- if (receipts === null) {
477
- receipts = port;
478
- loaded = load(port);
479
- }
480
- if (opening !== null) {
349
+ const replayed = opening === null ? [] : [{ kind: 'opened' }, announced];
350
+ if (replayed.length > 0) {
481
351
  queueMicrotask(() => {
482
- if (observers.has(notify))
483
- notify({ kind: 'opened' });
352
+ for (const event of replayed)
353
+ if (event !== null && observers.has(notify))
354
+ notify(event);
484
355
  });
485
356
  }
486
357
  return () => {
@@ -488,153 +359,19 @@ export function houseNotifications(transport, mode) {
488
359
  };
489
360
  },
490
361
  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
362
  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();
363
+ announced = null;
364
+ socket?.hold(rooms, runtime, listedCursor);
529
365
  },
530
366
  track(entries) {
531
- locators.clear();
367
+ tracking.clear();
532
368
  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
- }
369
+ tracking.set(entry.authority, [...(tracking.get(entry.authority) ?? []), entry]);
537
370
  }
538
- for (const authority of lines.keys()) {
539
- if (!locators.has(authority))
540
- lines.delete(authority);
371
+ for (const [carry, held] of staged) {
372
+ if (!tracking.has(held.authority))
373
+ staged.delete(carry);
541
374
  }
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 (entry.path === tracked.schema)
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
375
  },
639
376
  };
640
377
  }