@agentshouse/mdruntime 0.1.0-c967d1ca66fee → 0.1.0-c97456d63baba

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 (183) hide show
  1. package/AGENTS.md +257 -227
  2. package/README.md +11 -1
  3. package/dist/acquisition.d.ts +14 -0
  4. package/dist/acquisition.d.ts.map +1 -0
  5. package/dist/acquisition.js +24 -0
  6. package/dist/adapter.d.ts +69 -20
  7. package/dist/adapter.d.ts.map +1 -1
  8. package/dist/attempt.d.ts +16 -0
  9. package/dist/attempt.d.ts.map +1 -0
  10. package/dist/attempt.js +70 -0
  11. package/dist/binary.d.ts +23 -0
  12. package/dist/binary.d.ts.map +1 -0
  13. package/dist/binary.js +8 -0
  14. package/dist/bootstrap.d.ts +2 -2
  15. package/dist/bootstrap.d.ts.map +1 -1
  16. package/dist/bootstrap.js +33 -14
  17. package/dist/change-set.d.ts +2 -0
  18. package/dist/change-set.d.ts.map +1 -1
  19. package/dist/change-set.js +39 -13
  20. package/dist/change.d.ts +11 -0
  21. package/dist/change.d.ts.map +1 -1
  22. package/dist/change.js +26 -1
  23. package/dist/chat/acts.d.ts +33 -0
  24. package/dist/chat/acts.d.ts.map +1 -0
  25. package/dist/chat/acts.js +36 -0
  26. package/dist/chat/context.d.ts +3 -0
  27. package/dist/chat/context.d.ts.map +1 -0
  28. package/dist/chat/context.js +10 -0
  29. package/dist/chat/input.d.ts +9 -0
  30. package/dist/chat/input.d.ts.map +1 -0
  31. package/dist/chat/input.js +99 -0
  32. package/dist/chat/kit.d.ts +61 -0
  33. package/dist/chat/kit.d.ts.map +1 -0
  34. package/dist/chat/kit.js +123 -0
  35. package/dist/chat/lasting.d.ts +6 -0
  36. package/dist/chat/lasting.d.ts.map +1 -0
  37. package/dist/chat/lasting.js +19 -0
  38. package/dist/chat/list.d.ts +27 -0
  39. package/dist/chat/list.d.ts.map +1 -0
  40. package/dist/chat/list.js +63 -0
  41. package/dist/chat/markdown.d.ts +20 -0
  42. package/dist/chat/markdown.d.ts.map +1 -0
  43. package/dist/chat/markdown.js +188 -0
  44. package/dist/chat/message.d.ts +6 -0
  45. package/dist/chat/message.d.ts.map +1 -0
  46. package/dist/chat/message.js +192 -0
  47. package/dist/chat/messages.d.ts +22 -0
  48. package/dist/chat/messages.d.ts.map +1 -0
  49. package/dist/chat/messages.js +155 -0
  50. package/dist/chat/name.d.ts +6 -0
  51. package/dist/chat/name.d.ts.map +1 -0
  52. package/dist/chat/name.js +37 -0
  53. package/dist/chat/opened.d.ts +11 -0
  54. package/dist/chat/opened.d.ts.map +1 -0
  55. package/dist/chat/opened.js +17 -0
  56. package/dist/chat/overlay.d.ts +31 -0
  57. package/dist/chat/overlay.d.ts.map +1 -0
  58. package/dist/chat/overlay.js +97 -0
  59. package/dist/chat/parts.d.ts +57 -0
  60. package/dist/chat/parts.d.ts.map +1 -0
  61. package/dist/chat/parts.js +119 -0
  62. package/dist/chat/process.d.ts +20 -0
  63. package/dist/chat/process.d.ts.map +1 -0
  64. package/dist/chat/process.js +32 -0
  65. package/dist/chat/provider.d.ts +63 -0
  66. package/dist/chat/provider.d.ts.map +1 -0
  67. package/dist/chat/provider.js +629 -0
  68. package/dist/chat/question.d.ts +9 -0
  69. package/dist/chat/question.d.ts.map +1 -0
  70. package/dist/chat/question.js +80 -0
  71. package/dist/chat/questions.d.ts +31 -0
  72. package/dist/chat/questions.d.ts.map +1 -0
  73. package/dist/chat/questions.js +111 -0
  74. package/dist/chat/read.d.ts +5 -0
  75. package/dist/chat/read.d.ts.map +1 -0
  76. package/dist/chat/read.js +13 -0
  77. package/dist/chat/ready.d.ts +34 -0
  78. package/dist/chat/ready.d.ts.map +1 -0
  79. package/dist/chat/ready.js +91 -0
  80. package/dist/chat/style.d.ts +4 -0
  81. package/dist/chat/style.d.ts.map +1 -0
  82. package/dist/chat/style.js +302 -0
  83. package/dist/chat.d.ts +14 -0
  84. package/dist/chat.d.ts.map +1 -0
  85. package/dist/chat.js +8 -0
  86. package/dist/client.d.ts +21 -10
  87. package/dist/client.d.ts.map +1 -1
  88. package/dist/client.js +14 -2
  89. package/dist/collection.d.ts +17 -3
  90. package/dist/collection.d.ts.map +1 -1
  91. package/dist/collection.js +230 -19
  92. package/dist/connection.d.ts +32 -12
  93. package/dist/connection.d.ts.map +1 -1
  94. package/dist/connection.js +1482 -351
  95. package/dist/conversation.d.ts +23 -9
  96. package/dist/conversation.d.ts.map +1 -1
  97. package/dist/conversation.js +372 -142
  98. package/dist/durability.d.ts +7 -1
  99. package/dist/durability.d.ts.map +1 -1
  100. package/dist/entry.d.ts +17 -0
  101. package/dist/entry.d.ts.map +1 -0
  102. package/dist/entry.js +156 -0
  103. package/dist/expectation.d.ts +21 -0
  104. package/dist/expectation.d.ts.map +1 -0
  105. package/dist/expectation.js +70 -0
  106. package/dist/fence.d.ts +6 -0
  107. package/dist/fence.d.ts.map +1 -0
  108. package/dist/fence.js +10 -0
  109. package/dist/host.d.ts +13 -3
  110. package/dist/host.d.ts.map +1 -1
  111. package/dist/host.js +48 -11
  112. package/dist/identity.d.ts +4 -0
  113. package/dist/identity.d.ts.map +1 -1
  114. package/dist/identity.js +12 -0
  115. package/dist/index.d.ts +13 -7
  116. package/dist/index.d.ts.map +1 -1
  117. package/dist/index.js +4 -0
  118. package/dist/memory.d.ts +5 -3
  119. package/dist/memory.d.ts.map +1 -1
  120. package/dist/memory.js +47 -13
  121. package/dist/model.d.ts +4 -2
  122. package/dist/model.d.ts.map +1 -1
  123. package/dist/model.js +10 -1
  124. package/dist/observation.d.ts +5 -2
  125. package/dist/observation.d.ts.map +1 -1
  126. package/dist/observation.js +22 -11
  127. package/dist/outcome.d.ts +11 -1
  128. package/dist/outcome.d.ts.map +1 -1
  129. package/dist/outcome.js +7 -0
  130. package/dist/overlay.d.ts +14 -2
  131. package/dist/overlay.d.ts.map +1 -1
  132. package/dist/overlay.js +75 -62
  133. package/dist/query.d.ts +31 -3
  134. package/dist/query.d.ts.map +1 -1
  135. package/dist/reach.d.ts +8 -2
  136. package/dist/reach.d.ts.map +1 -1
  137. package/dist/reach.js +42 -8
  138. package/dist/react.d.ts +7 -4
  139. package/dist/react.d.ts.map +1 -1
  140. package/dist/read-side.d.ts +13 -6
  141. package/dist/read-side.d.ts.map +1 -1
  142. package/dist/read-side.js +291 -85
  143. package/dist/readiness.d.ts +53 -0
  144. package/dist/readiness.d.ts.map +1 -0
  145. package/dist/readiness.js +33 -0
  146. package/dist/recovery.d.ts +25 -0
  147. package/dist/recovery.d.ts.map +1 -0
  148. package/dist/recovery.js +197 -0
  149. package/dist/refresh.d.ts.map +1 -1
  150. package/dist/refresh.js +21 -25
  151. package/dist/replica.d.ts +6 -3
  152. package/dist/replica.d.ts.map +1 -1
  153. package/dist/replica.js +78 -45
  154. package/dist/room-draft.d.ts +20 -3
  155. package/dist/room-draft.d.ts.map +1 -1
  156. package/dist/room-draft.js +119 -1
  157. package/dist/rows.d.ts +6 -13
  158. package/dist/rows.d.ts.map +1 -1
  159. package/dist/rows.js +5 -13
  160. package/dist/save.d.ts +28 -0
  161. package/dist/save.d.ts.map +1 -1
  162. package/dist/save.js +343 -29
  163. package/dist/search.d.ts +28 -6
  164. package/dist/search.d.ts.map +1 -1
  165. package/dist/search.js +108 -45
  166. package/dist/shared.d.ts +11 -0
  167. package/dist/shared.d.ts.map +1 -0
  168. package/dist/shared.js +368 -0
  169. package/dist/status.d.ts +4 -0
  170. package/dist/status.d.ts.map +1 -1
  171. package/dist/store.d.ts +55 -21
  172. package/dist/store.d.ts.map +1 -1
  173. package/dist/store.js +424 -206
  174. package/dist/turn.d.ts +7 -0
  175. package/dist/turn.d.ts.map +1 -0
  176. package/dist/turn.js +45 -0
  177. package/dist/wire.d.ts +137 -30
  178. package/dist/wire.d.ts.map +1 -1
  179. package/dist/wire.js +49 -29
  180. package/package.json +26 -14
  181. package/dist/attachment.d.ts +0 -3
  182. package/dist/attachment.d.ts.map +0 -1
  183. package/dist/attachment.js +0 -8
package/dist/save.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { definitionOf } from './model.js';
2
2
  import { apply, ATTACHMENT, links, parse, pointerSegments, references, relative, relativize, retarget, serialize, target, } from '@agentshouse/mdmodel';
3
- import { immutable, } from './change.js';
4
- import { collectionOf, documentsOf, governing, validateCollection, } from './collection.js';
3
+ import { immutable, queuedFile, departure, } from './change.js';
4
+ import { available, changedCleanly, collectionOf, documentsOf, governing, referring, validateCollection, } from './collection.js';
5
5
  import { changeBatchIdentity } from './identity.js';
6
6
  import { overlay } from './overlay.js';
7
7
  import { covered, normalize } from './paths.js';
@@ -122,7 +122,10 @@ function rewrites(changes, visible) {
122
122
  for (const change of changes) {
123
123
  if (change.kind === 'move')
124
124
  moves.set(change.path, change.to);
125
- if (change.kind === 'remove' || change.kind === 'replace' || change.kind === 'create') {
125
+ if (change.kind === 'remove' ||
126
+ change.kind === 'replace' ||
127
+ change.kind === 'create' ||
128
+ change.kind === 'binary') {
126
129
  settled.add(change.path);
127
130
  }
128
131
  if (change.kind === 'field')
@@ -174,7 +177,7 @@ export function providerRewritten(batch, visible) {
174
177
  ...anticipated(batch.changes, batch.preconditions, rewritten.changes, visible),
175
178
  };
176
179
  }
177
- function incoming(collection, removed) {
180
+ function incoming(collection, removed, candidates) {
178
181
  const found = new Map();
179
182
  const note = (target, file) => {
180
183
  if (!removed.has(target))
@@ -183,7 +186,13 @@ function incoming(collection, removed) {
183
186
  referrers.add(file);
184
187
  found.set(target, referrers);
185
188
  };
186
- for (const [path, document] of documentsOf(collection)) {
189
+ const read = candidates === null
190
+ ? documentsOf(collection)
191
+ : [...new Set(candidates)].sort().flatMap((path) => {
192
+ const document = collection.document(path);
193
+ return document === undefined ? [] : [[path, document]];
194
+ });
195
+ for (const [path, document] of read) {
187
196
  const definition = definitionOf(governing(collection.composition, path), document.type);
188
197
  for (const { value, to } of definition === undefined ? [] : references(document, definition)) {
189
198
  if (to.includes(ATTACHMENT))
@@ -214,11 +223,14 @@ function affected(change) {
214
223
  return change.kind === 'move' ? [change.path, change.to] : [change.path];
215
224
  }
216
225
  function guard(changes, input) {
217
- const existing = new Set([
218
- ...Object.keys(input.visible.documents),
219
- ...input.visible.binaryTargets,
220
- ]);
221
- for (const change of changes) {
226
+ const overridden = new Map();
227
+ const existing = {
228
+ has: (path) => overridden.get(path) ??
229
+ (Object.hasOwn(input.visible.documents, path) || Object.hasOwn(input.visible.binaries, path)),
230
+ add: (path) => void overridden.set(path, true),
231
+ delete: (path) => void overridden.set(path, false),
232
+ };
233
+ for (const [index, change] of changes.entries()) {
222
234
  for (const path of affected(change)) {
223
235
  const normalized = normalize(path);
224
236
  if (normalized === null || normalized === '')
@@ -236,12 +248,26 @@ function guard(changes, input) {
236
248
  existing.add(change.path);
237
249
  continue;
238
250
  }
251
+ if (change.kind === 'binary') {
252
+ if (input.visible.documents[change.path] !== undefined ||
253
+ (existing.has(change.path) &&
254
+ input.bases.some((base) => base.kind === 'absent' && base.path === change.path))) {
255
+ return refuse('path-collision', change.path);
256
+ }
257
+ if (!input.local &&
258
+ existing.has(change.path) &&
259
+ input.visible.revisions[departure(changes, index)] === undefined) {
260
+ return refuse('unknown-revision', change.path);
261
+ }
262
+ existing.add(change.path);
263
+ continue;
264
+ }
239
265
  if (change.kind === 'move') {
240
266
  if (!existing.has(change.path))
241
267
  return refuse('path-absent', change.path);
242
268
  if (existing.has(change.to))
243
269
  return refuse('path-collision', change.to);
244
- if (!input.local && unrevisionedDocument(input.visible, change.path)) {
270
+ if (unrevised(input, change.path)) {
245
271
  return refuse('unknown-revision', change.path);
246
272
  }
247
273
  existing.delete(change.path);
@@ -251,7 +277,7 @@ function guard(changes, input) {
251
277
  if (!existing.has(change.path))
252
278
  return refuse('path-absent', change.path);
253
279
  if (change.kind === 'remove') {
254
- if (!input.local && unrevisionedDocument(input.visible, change.path)) {
280
+ if (unrevised(input, change.path)) {
255
281
  return refuse('unknown-revision', change.path);
256
282
  }
257
283
  existing.delete(change.path);
@@ -269,8 +295,12 @@ function guard(changes, input) {
269
295
  }
270
296
  return null;
271
297
  }
272
- function unrevisionedDocument(visible, path) {
273
- return visible.revisions[path] === undefined && !visible.binaryTargets.includes(path);
298
+ function unrevised(input, path) {
299
+ return (!input.local && input.queued?.has(path) !== true && input.visible.revisions[path] === undefined);
300
+ }
301
+ function identity(precondition) {
302
+ const part = precondition.kind === 'field' || precondition.kind === 'section' ? precondition.name : '';
303
+ return `${precondition.kind} ${precondition.path} ${part}`;
274
304
  }
275
305
  function existence(visible, path) {
276
306
  const revision = visible.revisions[path];
@@ -289,11 +319,10 @@ function preconditionsOf(changes, visible, bases) {
289
319
  const preconditions = [];
290
320
  const seen = new Set();
291
321
  const add = (precondition) => {
292
- const part = precondition.kind === 'field' || precondition.kind === 'section' ? precondition.name : '';
293
- const identity = `${precondition.kind} ${precondition.path} ${part}`;
294
- if (seen.has(identity))
322
+ const held = identity(precondition);
323
+ if (seen.has(held))
295
324
  return;
296
- seen.add(identity);
325
+ seen.add(held);
297
326
  preconditions.push(precondition);
298
327
  };
299
328
  for (const change of changes) {
@@ -328,6 +357,13 @@ function preconditionsOf(changes, visible, bases) {
328
357
  add({ kind: 'absent', path: change.path });
329
358
  continue;
330
359
  }
360
+ if (change.kind === 'binary') {
361
+ const revision = visible.revisions[change.path];
362
+ add(visible.binaries[change.path] === undefined || revision === undefined
363
+ ? { kind: 'absent', path: change.path }
364
+ : { kind: 'revision', path: change.path, revision });
365
+ continue;
366
+ }
331
367
  add(existence(visible, change.path));
332
368
  if (change.kind === 'move')
333
369
  add({ kind: 'absent', path: change.to });
@@ -337,10 +373,10 @@ function preconditionsOf(changes, visible, bases) {
337
373
  function depth(path) {
338
374
  return path.split('/').length;
339
375
  }
340
- function exceeded(limits, changes, resulting) {
341
- const written = changes.map((change) => (change.kind === 'move' ? change.to : change.path));
376
+ function exceeded(limits, changes, effective, resulting) {
377
+ const written = effective.map((change) => (change.kind === 'move' ? change.to : change.path));
342
378
  const created = changes
343
- .filter((change) => change.kind === 'create' || change.kind === 'move')
379
+ .filter((change) => change.kind === 'create' || change.kind === 'move' || change.kind === 'binary')
344
380
  .map((change) => (change.kind === 'move' ? change.to : change.path));
345
381
  for (const limit of limits) {
346
382
  const value = limit.value;
@@ -354,7 +390,7 @@ function exceeded(limits, changes, resulting) {
354
390
  return refuse('provider-limit', undefined, limit.name);
355
391
  }
356
392
  if (limit.name === 'document-size') {
357
- const large = written.find((path) => (resulting.documents[path] ?? '').length > value);
393
+ const large = written.find((path) => new TextEncoder().encode(resulting.documents[path] ?? '').byteLength > value);
358
394
  if (large !== undefined)
359
395
  return refuse('provider-limit', large, limit.name);
360
396
  }
@@ -423,6 +459,8 @@ function relativeChanges(input) {
423
459
  });
424
460
  }
425
461
  function unmodeledType(collection, composition) {
462
+ if (available(composition))
463
+ return false;
426
464
  for (const [path, document] of documentsOf(collection)) {
427
465
  if (document.type !== undefined &&
428
466
  document.type !== 'any' &&
@@ -431,6 +469,270 @@ function unmodeledType(collection, composition) {
431
469
  }
432
470
  return false;
433
471
  }
472
+ function unqueueable(file) {
473
+ return { kind: 'refused', refusal: { code: 'unknown-revision', retryable: false, file } };
474
+ }
475
+ function isPart(change) {
476
+ return change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble';
477
+ }
478
+ function touched(change) {
479
+ return change.kind === 'move' ? [change.path, change.to] : [change.path];
480
+ }
481
+ function partApplied(content, path, change) {
482
+ const parsed = parse(content, path);
483
+ if (!parsed.ok)
484
+ return null;
485
+ const document = parsed.document;
486
+ const applied = apply(document, change.kind === 'field'
487
+ ? {
488
+ kind: 'field',
489
+ name: change.name,
490
+ value: change.value,
491
+ prior: document.fields[change.name],
492
+ }
493
+ : change.kind === 'section'
494
+ ? {
495
+ kind: 'section',
496
+ name: change.name,
497
+ value: change.value,
498
+ prior: document.sections[change.name],
499
+ }
500
+ : { kind: 'preamble', value: change.value, prior: document.preamble });
501
+ return applied.outcome === 'applied' ? serialize(applied.document) : null;
502
+ }
503
+ function folded(changes, change, appending) {
504
+ const held = changes.find((item) => item.path === change.path && (item.kind === 'create' || item.kind === 'replace'));
505
+ const whole = held === undefined ? -1 : changes.indexOf(held);
506
+ const occupied = changes.some((item) => !isPart(item) && item !== held && touched(item).includes(change.path));
507
+ if (isPart(change)) {
508
+ const same = changes.findIndex((item) => item.kind === change.kind &&
509
+ item.path === change.path &&
510
+ (change.kind === 'preamble' || ('name' in item && item.name === change.name)));
511
+ if (same !== -1) {
512
+ changes[same] = change;
513
+ return 'folded';
514
+ }
515
+ if (held === undefined) {
516
+ if (!appending)
517
+ return 'skipped';
518
+ if (occupied)
519
+ return 'refused';
520
+ changes.push(change);
521
+ return 'appended';
522
+ }
523
+ if ('content' in held) {
524
+ const content = partApplied(held.content, held.path, change);
525
+ if (content === null)
526
+ return 'refused';
527
+ changes[whole] = { ...held, content };
528
+ return 'folded';
529
+ }
530
+ if (change.kind === 'preamble')
531
+ return 'refused';
532
+ changes[whole] =
533
+ change.kind === 'field'
534
+ ? { ...held, fields: { ...held.fields, [change.name]: change.value } }
535
+ : { ...held, sections: { ...held.sections, [change.name]: change.value } };
536
+ return 'folded';
537
+ }
538
+ if (change.kind === 'replace') {
539
+ if (held !== undefined) {
540
+ if (!('content' in held))
541
+ return 'refused';
542
+ changes[whole] = { ...held, content: change.content };
543
+ return 'folded';
544
+ }
545
+ const parts = changes.filter((item) => isPart(item) && item.path === change.path);
546
+ if (parts.length === 0 && !appending)
547
+ return 'skipped';
548
+ if (occupied)
549
+ return 'refused';
550
+ changes.splice(0, changes.length, ...changes.filter((item) => !parts.includes(item)), change);
551
+ return 'appended';
552
+ }
553
+ if (changes.some((item) => touched(change).some((path) => touched(item).includes(path))))
554
+ return 'refused';
555
+ changes.push(change);
556
+ return 'appended';
557
+ }
558
+ function relinked(batch, move, before, after, appending) {
559
+ const derived = rewrites([move], after);
560
+ if (derived.kind === 'refused')
561
+ return derived;
562
+ const changes = [...batch.changes];
563
+ const appended = [];
564
+ let changed = false;
565
+ for (const change of derived.changes) {
566
+ if (change === move)
567
+ continue;
568
+ const outcome = folded(changes, change, appending);
569
+ if (outcome === 'refused')
570
+ return unqueueable(change.path);
571
+ if (outcome === 'appended')
572
+ appended.push(change);
573
+ if (outcome !== 'skipped')
574
+ changed = true;
575
+ }
576
+ if (!changed)
577
+ return { kind: 'relinked', batch };
578
+ const held = new Set(batch.preconditions.map(identity));
579
+ return {
580
+ kind: 'relinked',
581
+ batch: immutable({
582
+ ...batch,
583
+ id: changeBatchIdentity(),
584
+ changes,
585
+ preconditions: [
586
+ ...batch.preconditions,
587
+ ...preconditionsOf(appended, before, []).filter((precondition) => !held.has(identity(precondition))),
588
+ ],
589
+ }),
590
+ };
591
+ }
592
+ function leading(changes, at) {
593
+ const from = departure(changes, at);
594
+ return new Set(changes.filter((change, index) => index < at && change.kind === 'move' && departure(changes, index) === from));
595
+ }
596
+ function created(batch, at) {
597
+ const file = batch.changes[at];
598
+ return (leading(batch.changes, at).size === 0 &&
599
+ batch.preconditions.some((precondition) => precondition.kind === 'absent' && precondition.path === file.path));
600
+ }
601
+ function withdrawnFile(batch, change, at) {
602
+ const file = batch.changes[at];
603
+ const from = departure(batch.changes, at);
604
+ const moves = leading(batch.changes, at);
605
+ const fresh = created(batch, at);
606
+ const vacated = new Set([...moves].map((move) => move.to));
607
+ if (fresh)
608
+ vacated.add(file.path);
609
+ const replaced = fresh
610
+ ? change.kind === 'remove'
611
+ ? []
612
+ : [{ ...file, path: change.to }]
613
+ : change.kind === 'remove'
614
+ ? [{ kind: 'remove', path: from }]
615
+ : change.to === from
616
+ ? [{ ...file, path: from }]
617
+ : [
618
+ { kind: 'move', path: from, to: change.to },
619
+ { ...file, path: change.to },
620
+ ];
621
+ const added = change.kind === 'move' && (fresh || change.to !== from)
622
+ ? [{ kind: 'absent', path: change.to }]
623
+ : [];
624
+ const changes = batch.changes.flatMap((item) => item === file ? replaced : item.kind === 'move' && moves.has(item) ? [] : [item]);
625
+ if (changes.length === 0)
626
+ return null;
627
+ const preconditions = [
628
+ ...added,
629
+ ...batch.preconditions.filter((precondition) => precondition.kind !== 'absent' || !vacated.has(precondition.path)),
630
+ ];
631
+ return immutable({ ...batch, id: changeBatchIdentity(), changes, preconditions });
632
+ }
633
+ export function requeued(queue, changes, visible, changeable, state) {
634
+ const next = [...queue];
635
+ const origins = new Map();
636
+ const withdrawn = new Map();
637
+ const owners = new Set();
638
+ const queued = new Set();
639
+ const kept = [];
640
+ const originOf = (batch) => origins.get(batch) ?? batch.id;
641
+ const open = (batch) => origins.has(batch) || changeable(batch);
642
+ for (const change of changes) {
643
+ const held = visible.binaries[change.path];
644
+ if ((change.kind !== 'move' && change.kind !== 'remove') || held?.state !== 'pending') {
645
+ kept.push(change);
646
+ continue;
647
+ }
648
+ let index = next.length - 1;
649
+ while (index >= 0 && queuedFile(next[index], change.path, held.localId) === -1)
650
+ index -= 1;
651
+ const owner = next[index];
652
+ if (owner === undefined || !open(owner))
653
+ return unqueueable(change.path);
654
+ const following = next.slice(index);
655
+ const rebuilt = [];
656
+ let before = change.kind === 'move' ? state(next.slice(0, index)) : visible;
657
+ for (const [offset, batch] of following.entries()) {
658
+ let current = batch;
659
+ if (change.kind === 'move') {
660
+ const after = state(next.slice(0, index + offset + 1));
661
+ const appending = offset === 0 && created(batch, queuedFile(batch, change.path, held.localId));
662
+ const relinking = relinked(batch, change, before, after, appending);
663
+ if (relinking.kind === 'refused')
664
+ return relinking;
665
+ current = relinking.batch;
666
+ before = after;
667
+ }
668
+ if (current !== batch && !open(batch))
669
+ return unqueueable(change.path);
670
+ rebuilt.push(offset === 0
671
+ ? withdrawnFile(current, change, queuedFile(current, change.path, held.localId))
672
+ : current);
673
+ }
674
+ for (const [offset, batch] of following.entries()) {
675
+ const replaced = rebuilt[offset];
676
+ if (replaced === batch)
677
+ continue;
678
+ const origin = originOf(batch);
679
+ withdrawn.set(origin, replaced);
680
+ if (replaced !== null)
681
+ origins.set(replaced, origin);
682
+ }
683
+ owners.add(originOf(owner));
684
+ next.splice(index, following.length, ...rebuilt.filter((batch) => batch !== null));
685
+ queued.add(change.path);
686
+ }
687
+ return {
688
+ kind: 'requeued',
689
+ queue: withdrawn.size === 0 ? queue : next,
690
+ changes: kept,
691
+ queued,
692
+ withdrawn,
693
+ owners,
694
+ };
695
+ }
696
+ function covering(precondition) {
697
+ return precondition.kind === 'field' ||
698
+ precondition.kind === 'section' ||
699
+ precondition.kind === 'preamble'
700
+ ? identity(precondition)
701
+ : `path ${precondition.path}`;
702
+ }
703
+ export function joined(queue, owner, extra) {
704
+ const later = new Set(queue.slice(queue.indexOf(owner) + 1).flatMap((batch) => batch.changes.flatMap(touched)));
705
+ const clash = extra.changes.flatMap(touched).find((path) => later.has(path));
706
+ if (clash !== undefined)
707
+ return unqueueable(clash);
708
+ const changes = [...owner.changes];
709
+ for (const change of extra.changes) {
710
+ if (folded(changes, change, true) === 'refused')
711
+ return unqueueable(change.path);
712
+ }
713
+ const held = new Set(owner.preconditions.map(covering));
714
+ const written = new Set(owner.changes.flatMap((change) => change.kind === 'create' || change.kind === 'replace' ? [change.path] : []));
715
+ const preconditions = [
716
+ ...owner.preconditions,
717
+ ...extra.preconditions.filter((precondition) => {
718
+ const key = covering(precondition);
719
+ return (!held.has(key) && (key === `path ${precondition.path}` || !written.has(precondition.path)));
720
+ }),
721
+ ];
722
+ const batch = immutable({
723
+ ...owner,
724
+ ...(extra.validation === undefined ? {} : { validation: extra.validation }),
725
+ id: changeBatchIdentity(),
726
+ changes,
727
+ preconditions,
728
+ });
729
+ return { kind: 'joined', batch, queue: queue.map((item) => (item === owner ? batch : item)) };
730
+ }
731
+ export function limitedIn(queue, batch, limits, state) {
732
+ const at = queue.indexOf(batch);
733
+ const limited = exceeded(limits, batch.changes, providerRewritten(batch, state(queue.slice(0, at))).changes, state(queue.slice(0, at + 1)));
734
+ return limited?.kind === 'refused' ? limited.refusal : null;
735
+ }
434
736
  export function buildBatch(input) {
435
737
  const changes = relativeChanges(input);
436
738
  const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
@@ -449,9 +751,16 @@ export function buildBatch(input) {
449
751
  const resulting = overlay(input.visible, [
450
752
  { id: '', baseRoomVersion: input.baseRoomVersion, ...predicted },
451
753
  ]);
754
+ const clean = input.clean !== undefined &&
755
+ input.clean.text === input.visible.documents &&
756
+ input.clean.binaries === input.visible.binaries &&
757
+ available(input.composition)
758
+ ? input.clean
759
+ : null;
452
760
  const removed = new Set(changes.filter((change) => change.kind === 'remove').map((change) => change.path));
453
761
  if (removed.size > 0) {
454
- const referenced = incoming(collectionOf(resulting.documents, input.composition), removed);
762
+ const referrers = clean === null ? null : referring(clean, input.composition, removed);
763
+ const referenced = incoming(collectionOf(resulting.documents, input.composition), removed, referrers === null ? null : [...referrers, ...predicted.changes.flatMap(affected)]);
455
764
  if (referenced !== null)
456
765
  return referenced;
457
766
  }
@@ -461,15 +770,20 @@ export function buildBatch(input) {
461
770
  const parsed = parse(content);
462
771
  return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
463
772
  };
464
- const typedPaths = [...new Set(predicted.changes.flatMap(affected))].filter((path) => typed(input.visible.documents[path]) || typed(resulting.documents[path]));
773
+ const touched = [...new Set(predicted.changes.flatMap(affected))];
774
+ const typedPaths = touched.filter((path) => typed(input.visible.documents[path]) || typed(resulting.documents[path]));
465
775
  const needsModel = typedPaths.length > 0;
466
776
  const withoutModel = typedPaths.find((path) => governing(input.composition, path).status === 'unavailable');
467
777
  if (withoutModel !== undefined)
468
778
  return refuse('model-unavailable', withoutModel);
469
- const validationText = needsModel
470
- ? resulting.documents
471
- : Object.fromEntries(Object.entries(resulting.documents).filter(([path]) => predicted.changes.some((change) => affected(change).includes(path))));
472
- const verdict = validateCollection(validationText, resulting.binaryTargets, input.composition);
779
+ const verdict = clean !== null &&
780
+ changedCleanly({ text: resulting.documents, binaries: resulting.binaries }, clean, input.composition)
781
+ ? { verdict: 'accept' }
782
+ : validateCollection(needsModel
783
+ ? resulting.documents
784
+ : Object.fromEntries(touched.flatMap((path) => resulting.documents[path] === undefined
785
+ ? []
786
+ : [[path, resulting.documents[path]]])), Object.keys(resulting.binaries), input.composition);
473
787
  if (verdict.verdict === 'refuse') {
474
788
  return {
475
789
  kind: 'refused',
@@ -482,7 +796,7 @@ export function buildBatch(input) {
482
796
  },
483
797
  };
484
798
  }
485
- const limited = exceeded(input.providerLimits, changes, resulting);
799
+ const limited = exceeded(input.providerLimits, changes, predicted.changes, resulting);
486
800
  if (limited !== null)
487
801
  return limited;
488
802
  const validated = typedPaths
package/dist/search.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ChangeSet } from './change-set.js';
2
- import { type Filter } from './query.js';
2
+ import { type CoveredRoom, type Filter, type PendingRoom } from './query.js';
3
3
  import type { Open } from './open.js';
4
4
  export type DocumentPart = Open<'fields' | 'sections' | 'preamble' | 'path'>;
5
5
  export type SearchRequest = {
@@ -21,10 +21,26 @@ export type MessageHit = {
21
21
  score: number;
22
22
  matches: Readonly<Record<string, readonly Open<'text'>[]>>;
23
23
  };
24
- export type SearchResult = {
24
+ export type SearchCoverage = {
25
+ rooms: readonly CoveredRoom[];
26
+ conversations: readonly string[];
27
+ index: string;
28
+ overlay: readonly string[];
29
+ };
30
+ export type PendingConversation = {
31
+ conversation: string;
32
+ awaits: Open<'index'>;
33
+ };
34
+ export type SearchPending = {
35
+ pending: readonly (PendingRoom | PendingConversation)[];
36
+ };
37
+ export type SearchHits = {
25
38
  documents: readonly DocumentHit[];
26
39
  messages: readonly MessageHit[];
27
40
  };
41
+ export type SearchResult = (SearchHits & {
42
+ coverage: SearchCoverage;
43
+ }) | SearchPending;
28
44
  export type SearchedDocument = {
29
45
  room: string;
30
46
  path: string;
@@ -39,10 +55,8 @@ export type SearchedMessage = {
39
55
  text: string;
40
56
  };
41
57
  export type SearchSource = {
42
- documents(): readonly SearchedDocument[];
58
+ room(room: string): readonly string[];
43
59
  document(room: string, path: string): SearchedDocument | null;
44
- messages(): readonly SearchedMessage[];
45
- message(conversation: string, identity: string): SearchedMessage | null;
46
60
  };
47
61
  export type SearchPlan = {
48
62
  text: string;
@@ -51,11 +65,19 @@ export type SearchPlan = {
51
65
  limit: number | null;
52
66
  };
53
67
  export type SearchIndex = {
68
+ generation(): string;
69
+ rooms(): ReadonlySet<string>;
70
+ conversations(): ReadonlySet<string>;
71
+ indexRoom(room: string): Promise<void>;
72
+ indexMessages(conversation: string, messages: readonly SearchedMessage[], whole: boolean): void;
73
+ forget(rooms: readonly string[]): void;
54
74
  changed(set: ChangeSet): void;
75
+ changes(): number;
76
+ caughtUp(): Promise<void>;
55
77
  search(plan: SearchPlan, documents: readonly {
56
78
  room: string;
57
79
  path: string;
58
- }[] | null): SearchResult;
80
+ }[] | null, conversations: readonly string[]): SearchHits;
59
81
  };
60
82
  export declare function searchPlanOf(input: unknown): SearchPlan;
61
83
  export declare function createSearchIndex(source: SearchSource): SearchIndex;
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"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"}