@agentshouse/mdruntime-adapter 0.1.0-cd673d02b6e50 → 0.1.0-cd7e4bb187177

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,91 @@
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 = [];
104
86
  const live = new Set();
105
87
  let opening = null;
88
+ let announced = null;
106
89
  const runtime = crypto.randomUUID();
107
90
  function emit(event) {
108
91
  for (const observer of observers)
@@ -115,6 +98,7 @@ export function houseNotifications(transport, mode) {
115
98
  event.kind === 'commands' ||
116
99
  event.kind === 'options' ||
117
100
  event.kind === 'starting' ||
101
+ event.kind === 'subagents' ||
118
102
  event.kind === 'link' ||
119
103
  event.kind === 'deliveries') {
120
104
  live.add(event.conversation);
@@ -124,12 +108,31 @@ export function houseNotifications(transport, mode) {
124
108
  }
125
109
  function reopened(next) {
126
110
  opening = next;
127
- acknowledging = next;
111
+ announced = null;
112
+ socket = next;
113
+ staged.clear();
128
114
  const gapped = new Set([...replicated, ...live]);
129
115
  live.clear();
130
116
  for (const conversation of gapped)
131
117
  emitConversation({ kind: 'gap', conversation });
132
118
  emit({ kind: 'opened' });
119
+ if (mode === 'site')
120
+ announce(next, null);
121
+ }
122
+ function announce(via, frame) {
123
+ void discovering.opened(frame).then((discovery) => {
124
+ if (socket !== via)
125
+ return;
126
+ if (discovery === null) {
127
+ via.close();
128
+ return;
129
+ }
130
+ announced = { kind: 'discovered', discovery };
131
+ emit(announced);
132
+ }, () => {
133
+ if (socket === via)
134
+ via.close();
135
+ });
133
136
  }
134
137
  function converse(frame) {
135
138
  const conversation = frame.conversation_id;
@@ -148,6 +151,7 @@ export function houseNotifications(transport, mode) {
148
151
  return;
149
152
  emitConversation({ kind: 'commands', conversation, commands: [] });
150
153
  emitConversation({ kind: 'options', conversation, options: [] });
154
+ emitConversation({ kind: 'subagents', conversation, subagents: [] });
151
155
  return;
152
156
  }
153
157
  if (frame.type === 'commands') {
@@ -166,6 +170,14 @@ export function houseNotifications(transport, mode) {
166
170
  emitConversation({ kind: 'starting', conversation, step: frame.step });
167
171
  return;
168
172
  }
173
+ if (frame.type === 'subagents') {
174
+ emitConversation({
175
+ kind: 'subagents',
176
+ conversation,
177
+ subagents: structuredClone(frame.subagents),
178
+ });
179
+ return;
180
+ }
169
181
  if (frame.type === 'link') {
170
182
  emitConversation({ kind: 'link', conversation, connected: frame.connected });
171
183
  return;
@@ -187,171 +199,30 @@ export function houseNotifications(transport, mode) {
187
199
  return;
188
200
  emitConversation({ kind: 'accepted-message', conversation, message });
189
201
  }
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);
202
+ function stage(frame, via) {
203
+ const held = staged.get(frame.carry);
204
+ if (frame.index !== (held?.parts ?? 0)) {
205
+ staged.delete(frame.carry);
223
206
  return;
224
207
  }
225
208
  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;
209
+ for (const [carry, other] of staged) {
210
+ if (other.authority === frame.authority)
211
+ staged.delete(carry);
212
+ }
247
213
  }
248
- kept.set(frame.carry, next);
214
+ const next = held ?? { authority: frame.authority, parts: 0, entries: [] };
215
+ next.entries.push(...frame.entries);
216
+ next.parts += 1;
217
+ staged.set(frame.carry, next);
249
218
  via?.received(frame.authority, frame.carry, frame.index);
250
219
  }
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);
220
+ function carried(frame) {
221
+ const held = staged.get(frame.carry);
222
+ staged.delete(frame.carry);
223
+ if ((held?.parts ?? 0) !== frame.parts)
272
224
  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
- };
225
+ publish(frame, held?.entries ?? []);
355
226
  }
356
227
  function handle(frame, via) {
357
228
  if (frame.type === 'signal') {
@@ -363,14 +234,21 @@ export function houseNotifications(transport, mode) {
363
234
  return;
364
235
  }
365
236
  if (frame.type === 'opening') {
237
+ if (via !== null && mode === 'credential')
238
+ announce(via, frame);
239
+ return;
240
+ }
241
+ if (frame.type === 'caught_up') {
366
242
  emit({
367
- kind: 'opening',
368
- expired: frame.expired.flatMap((authority) => locators.get(authority) ?? []),
243
+ kind: 'caught-up',
244
+ expired: frame.expired.flatMap((authority) => (tracking.get(authority) ?? []).map((tracked) => tracked.locator)),
369
245
  });
370
246
  return;
371
247
  }
372
248
  if (frame.type === 'authority_changed') {
373
- emit({ kind: 'authority-changed', scope: { authorities: frame.rooms } });
249
+ const change = discovering.changed(frame);
250
+ if (change !== null)
251
+ emit(change);
374
252
  return;
375
253
  }
376
254
  if (frame.type === 'conversations') {
@@ -390,6 +268,7 @@ export function houseNotifications(transport, mode) {
390
268
  frame.type === 'commands' ||
391
269
  frame.type === 'options' ||
392
270
  frame.type === 'starting' ||
271
+ frame.type === 'subagents' ||
393
272
  frame.type === 'link' ||
394
273
  frame.type === 'deliveries' ||
395
274
  frame.type === 'interactions' ||
@@ -398,56 +277,34 @@ export function houseNotifications(transport, mode) {
398
277
  return;
399
278
  }
400
279
  if (frame.type === 'part')
401
- return stage(frame, via);
402
- if (frame.type === 'carried')
403
- return carried(frame);
280
+ stage(frame, via);
281
+ else if (frame.type === 'carried')
282
+ carried(frame);
404
283
  }
405
284
  function publish(frame, entries) {
406
285
  const at = position(frame.position);
407
286
  const after = position(frame.after);
408
- if (at === null || after === null || after >= at) {
409
- interrupt(frame.authority);
287
+ if (at === null || after === null || after >= at)
410
288
  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;
289
+ const from = roomVersion({ position: frame.after, logEpoch: frame.log_epoch });
290
+ const version = roomVersion({ position: frame.position, logEpoch: frame.log_epoch });
291
+ for (const tracked of tracking.get(frame.authority) ?? []) {
292
+ emit({
293
+ kind: 'room-change',
294
+ locator: tracked.locator,
295
+ from,
296
+ roomVersion: version,
297
+ read: () => changePage(tracked, entries, version, mode),
437
298
  });
438
- return;
439
299
  }
440
- verifying += 1;
441
- receiving = receiving
442
- .then(() => handle(frame, via))
443
- .finally(() => {
444
- verifying -= 1;
445
- });
446
- });
300
+ }
301
+ stream.observe((frame) => handle(frame, socket));
447
302
  stream.opened(reopened);
448
303
  stream.closed(({ revoked, lost }) => {
449
304
  opening = null;
450
- acknowledging = null;
305
+ announced = null;
306
+ socket = null;
307
+ staged.clear();
451
308
  emit({ kind: 'closed', revoked, lost });
452
309
  });
453
310
  return {
@@ -470,17 +327,21 @@ export function houseNotifications(transport, mode) {
470
327
  conversationObservers.delete(notify);
471
328
  };
472
329
  },
330
+ ...(mode === 'credential'
331
+ ? {
332
+ agents: () => houseAgents(transport),
333
+ history: (request) => houseHistory(transport, request),
334
+ }
335
+ : {}),
473
336
  },
474
- observe(notify, port) {
337
+ observe(notify) {
475
338
  observers.add(notify);
476
- if (receipts === null) {
477
- receipts = port;
478
- loaded = load(port);
479
- }
480
- if (opening !== null) {
339
+ const replayed = opening === null ? [] : [{ kind: 'opened' }, announced];
340
+ if (replayed.length > 0) {
481
341
  queueMicrotask(() => {
482
- if (observers.has(notify))
483
- notify({ kind: 'opened' });
342
+ for (const event of replayed)
343
+ if (event !== null && observers.has(notify))
344
+ notify(event);
484
345
  });
485
346
  }
486
347
  return () => {
@@ -488,153 +349,19 @@ export function houseNotifications(transport, mode) {
488
349
  };
489
350
  },
490
351
  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
- 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
352
  opening = null;
528
- current?.close();
353
+ announced = null;
354
+ socket?.hold(rooms, runtime);
529
355
  },
530
356
  track(entries) {
531
- locators.clear();
357
+ tracking.clear();
532
358
  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);
359
+ tracking.set(entry.authority, [...(tracking.get(entry.authority) ?? []), entry]);
541
360
  }
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
- }
361
+ for (const [carry, held] of staged) {
362
+ if (!tracking.has(held.authority))
363
+ staged.delete(carry);
620
364
  }
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
365
  },
639
366
  };
640
367
  }