@agentshouse/mdruntime 0.1.0-c0bed82db8d73 → 0.1.0-c0c1e81da9166

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 (174) hide show
  1. package/README.md +3 -2
  2. package/dist/adapter.d.ts +101 -71
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/attached-model.d.ts +4 -10
  5. package/dist/attached-model.d.ts.map +1 -1
  6. package/dist/attached-model.js +35 -46
  7. package/dist/bootstrap.d.ts +16 -15
  8. package/dist/bootstrap.d.ts.map +1 -1
  9. package/dist/bootstrap.js +273 -131
  10. package/dist/chain.d.ts +2 -1
  11. package/dist/chain.d.ts.map +1 -1
  12. package/dist/change.d.ts +1 -0
  13. package/dist/change.d.ts.map +1 -1
  14. package/dist/chat-types.d.ts +3 -0
  15. package/dist/chat-types.d.ts.map +1 -0
  16. package/dist/chat-types.js +2 -0
  17. package/dist/client.d.ts +15 -9
  18. package/dist/client.d.ts.map +1 -1
  19. package/dist/client.js +54 -6
  20. package/dist/collection.d.ts +7 -4
  21. package/dist/collection.d.ts.map +1 -1
  22. package/dist/collection.js +24 -19
  23. package/dist/compile.d.ts +6 -6
  24. package/dist/compile.d.ts.map +1 -1
  25. package/dist/compile.js +278 -282
  26. package/dist/connection.d.ts +16 -11
  27. package/dist/connection.d.ts.map +1 -1
  28. package/dist/connection.js +2312 -1988
  29. package/dist/context.d.ts +2 -0
  30. package/dist/context.d.ts.map +1 -0
  31. package/dist/context.js +7 -0
  32. package/dist/conversation-row.d.ts +33 -0
  33. package/dist/conversation-row.d.ts.map +1 -0
  34. package/dist/conversation-row.js +47 -0
  35. package/dist/conversation.d.ts +27 -18
  36. package/dist/conversation.d.ts.map +1 -1
  37. package/dist/conversation.js +489 -260
  38. package/dist/durability.d.ts +6 -1
  39. package/dist/durability.d.ts.map +1 -1
  40. package/dist/entry.d.ts +3 -2
  41. package/dist/entry.d.ts.map +1 -1
  42. package/dist/entry.js +18 -8
  43. package/dist/figures.d.ts +48 -0
  44. package/dist/figures.d.ts.map +1 -0
  45. package/dist/figures.js +197 -0
  46. package/dist/file.d.ts +2 -11
  47. package/dist/file.d.ts.map +1 -1
  48. package/dist/file.js +6 -4
  49. package/dist/host.d.ts +3 -7
  50. package/dist/host.d.ts.map +1 -1
  51. package/dist/host.js +26 -29
  52. package/dist/identity.d.ts +0 -1
  53. package/dist/identity.d.ts.map +1 -1
  54. package/dist/identity.js +0 -3
  55. package/dist/index.d.ts +16 -9
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +6 -1
  58. package/dist/job.d.ts +33 -0
  59. package/dist/job.d.ts.map +1 -0
  60. package/dist/job.js +54 -0
  61. package/dist/live.d.ts +5 -4
  62. package/dist/live.d.ts.map +1 -1
  63. package/dist/live.js +62 -20
  64. package/dist/media.d.ts +8 -0
  65. package/dist/media.d.ts.map +1 -0
  66. package/dist/media.js +218 -0
  67. package/dist/memory.d.ts +13 -6
  68. package/dist/memory.d.ts.map +1 -1
  69. package/dist/memory.js +218 -91
  70. package/dist/messages.d.ts +27 -3
  71. package/dist/messages.d.ts.map +1 -1
  72. package/dist/messages.js +97 -19
  73. package/dist/model.d.ts +8 -10
  74. package/dist/model.d.ts.map +1 -1
  75. package/dist/model.js +6 -18
  76. package/dist/observation.d.ts +3 -2
  77. package/dist/observation.d.ts.map +1 -1
  78. package/dist/observation.js +8 -3
  79. package/dist/outcome.d.ts +13 -5
  80. package/dist/outcome.d.ts.map +1 -1
  81. package/dist/outcome.js +16 -2
  82. package/dist/overlay.d.ts +1 -0
  83. package/dist/overlay.d.ts.map +1 -1
  84. package/dist/overlay.js +11 -0
  85. package/dist/parts.d.ts +59 -0
  86. package/dist/parts.d.ts.map +1 -0
  87. package/dist/parts.js +118 -0
  88. package/dist/paths.d.ts +1 -0
  89. package/dist/paths.d.ts.map +1 -1
  90. package/dist/paths.js +4 -0
  91. package/dist/query.d.ts +18 -207
  92. package/dist/query.d.ts.map +1 -1
  93. package/dist/query.js +3 -505
  94. package/dist/questions.d.ts +32 -0
  95. package/dist/questions.d.ts.map +1 -0
  96. package/dist/questions.js +118 -0
  97. package/dist/reach.d.ts +18 -11
  98. package/dist/reach.d.ts.map +1 -1
  99. package/dist/reach.js +201 -126
  100. package/dist/react.d.ts +3 -1
  101. package/dist/react.d.ts.map +1 -1
  102. package/dist/read.d.ts +7 -0
  103. package/dist/read.d.ts.map +1 -0
  104. package/dist/read.js +17 -0
  105. package/dist/readiness.d.ts +3 -17
  106. package/dist/readiness.d.ts.map +1 -1
  107. package/dist/readiness.js +4 -10
  108. package/dist/recovery.d.ts +1 -0
  109. package/dist/recovery.d.ts.map +1 -1
  110. package/dist/recovery.js +35 -1
  111. package/dist/refresh.d.ts +7 -5
  112. package/dist/refresh.d.ts.map +1 -1
  113. package/dist/refresh.js +34 -10
  114. package/dist/replica.d.ts +10 -9
  115. package/dist/replica.d.ts.map +1 -1
  116. package/dist/replica.js +53 -21
  117. package/dist/room-draft.d.ts +21 -5
  118. package/dist/room-draft.d.ts.map +1 -1
  119. package/dist/room-draft.js +98 -51
  120. package/dist/rows.d.ts +18 -16
  121. package/dist/rows.d.ts.map +1 -1
  122. package/dist/rows.js +45 -153
  123. package/dist/save.d.ts +2 -0
  124. package/dist/save.d.ts.map +1 -1
  125. package/dist/save.js +22 -13
  126. package/dist/search.d.ts +12 -63
  127. package/dist/search.d.ts.map +1 -1
  128. package/dist/search.js +9 -52
  129. package/dist/shared.d.ts +0 -1
  130. package/dist/shared.d.ts.map +1 -1
  131. package/dist/shared.js +0 -10
  132. package/dist/source.d.ts +61 -0
  133. package/dist/source.d.ts.map +1 -0
  134. package/dist/source.js +81 -0
  135. package/dist/status.d.ts +4 -5
  136. package/dist/status.d.ts.map +1 -1
  137. package/dist/status.js +3 -7
  138. package/dist/store.d.ts +104 -62
  139. package/dist/store.d.ts.map +1 -1
  140. package/dist/store.js +159 -161
  141. package/dist/stored.d.ts +66 -55
  142. package/dist/stored.d.ts.map +1 -1
  143. package/dist/stored.js +343 -273
  144. package/dist/tables.d.ts +110 -65
  145. package/dist/tables.d.ts.map +1 -1
  146. package/dist/tables.js +188 -99
  147. package/dist/tallies.d.ts +18 -0
  148. package/dist/tallies.d.ts.map +1 -0
  149. package/dist/tallies.js +129 -0
  150. package/dist/transfer.d.ts +0 -5
  151. package/dist/transfer.d.ts.map +1 -1
  152. package/dist/validation.d.ts +3 -4
  153. package/dist/validation.d.ts.map +1 -1
  154. package/dist/validation.js +42 -100
  155. package/dist/windows.d.ts +1 -12
  156. package/dist/windows.d.ts.map +1 -1
  157. package/dist/windows.js +2 -75
  158. package/dist/wire.d.ts +18 -8
  159. package/dist/wire.d.ts.map +1 -1
  160. package/dist/wire.js +6 -2
  161. package/package.json +2 -3
  162. package/AGENTS.md +0 -312
  163. package/dist/fence.d.ts +0 -6
  164. package/dist/fence.d.ts.map +0 -1
  165. package/dist/fence.js +0 -10
  166. package/dist/fulltext.d.ts +0 -27
  167. package/dist/fulltext.d.ts.map +0 -1
  168. package/dist/fulltext.js +0 -368
  169. package/dist/reads.d.ts +0 -11
  170. package/dist/reads.d.ts.map +0 -1
  171. package/dist/reads.js +0 -100
  172. package/dist/words.d.ts +0 -19
  173. package/dist/words.d.ts.map +0 -1
  174. package/dist/words.js +0 -150
package/dist/tables.js CHANGED
@@ -1,11 +1,16 @@
1
+ import { sql } from '@agentshouse/mdmodel';
1
2
  import { answerQuery } from './compile.js';
2
- import { searched, SEARCH_TABLES } from './fulltext.js';
3
- import { droppedMessages, forgottenMessages, heldConversations, keptMessages, messageCount, messageWindow, MESSAGE_TABLES, turnArrived, } from './messages.js';
4
- import { abandon, release, commit, counts, ended, entries, erase, fenceRooms, files, opened, rederive, referrers, refreshing, REPLICA_TABLES, keepPart, stage, transferPart, typed, typedWithin, unrefresh, walk, } from './stored.js';
3
+ import { conversationRows, droppedMessages, forgottenMessages, heldConversations, jobMarkers, keptMessages, messageCount, messageWindow, MESSAGE_TABLES, turnArrived, } from './messages.js';
4
+ import { conversationTallies, roomTallies, } from './tallies.js';
5
+ import { abandon, release, commit, covering, demoted, ended, entries, erase, facts, fenceRooms, folders, heldLocators, heldRoom, heldRooms, offlineMode, opened, overlaid, presentation, rederive, referrers, refreshing, REPLICA_TABLES, resume, stage, typed, stagedLocators, typedWithin, unrefresh, } from './stored.js';
5
6
  const STATEMENTS_HELD = 256;
6
7
  export function sqliteConnection(engine, database) {
7
8
  const decoder = new TextDecoder();
8
9
  const held = new Map();
10
+ database.createFunction(sql.HOLDS_PHRASE, sql.holdsPhrase, {
11
+ arity: 2,
12
+ deterministic: true,
13
+ });
9
14
  function prepared(sql) {
10
15
  if (sql.includes(';'))
11
16
  return null;
@@ -72,7 +77,7 @@ const TABLES = [
72
77
  ['authored_queues', 'locator TEXT PRIMARY KEY, ids TEXT NOT NULL'],
73
78
  [
74
79
  'authored_pending',
75
- 'locator TEXT NOT NULL, id TEXT NOT NULL, batch TEXT NOT NULL, state TEXT NOT NULL, files TEXT NOT NULL, PRIMARY KEY (locator, id)',
80
+ 'locator TEXT NOT NULL, id TEXT NOT NULL, batch TEXT NOT NULL, state TEXT NOT NULL, files TEXT NOT NULL, paths TEXT NOT NULL, moving INTEGER NOT NULL, PRIMARY KEY (locator, id)',
76
81
  ],
77
82
  [
78
83
  'authored_room_drafts',
@@ -89,38 +94,49 @@ export const RUNTIME_TABLES = [
89
94
  'CREATE INDEX opened_files_name ON opened_files (locator, path, revision)',
90
95
  ...REPLICA_TABLES,
91
96
  ...MESSAGE_TABLES,
92
- ...SEARCH_TABLES,
93
97
  ];
94
98
  const OWNERS = ['authored_pending', 'authored_room_drafts', 'authored_calls'];
95
99
  const AUTHORED_KEYS = OWNERS.map((table) => `SELECT value FROM ${table}, json_each(${table}.files)`).join(' UNION ');
96
100
  const UNOWNED_PARTS = `DELETE FROM file_parts WHERE key NOT IN (${AUTHORED_KEYS} UNION SELECT key FROM opened_files)`;
97
101
  const READS = new Set([
98
- 'authored.queues',
102
+ 'authored.status',
103
+ 'authored.moving',
104
+ 'authored.pending',
105
+ 'authored.batch',
106
+ 'authored.order',
107
+ 'authored.queued',
99
108
  'authored.roomDrafts',
109
+ 'authored.chatDraft.read',
100
110
  'authored.chatDrafts',
101
111
  'authored.calls',
102
112
  'authored.fences',
113
+ 'authored.unsent',
103
114
  'authored.fact',
104
115
  'authored.file',
105
116
  'replica.open',
117
+ 'replica.rooms',
118
+ 'replica.room',
119
+ 'replica.covering',
120
+ 'replica.offline',
121
+ 'replica.held',
122
+ 'replica.facts',
123
+ 'replica.presentation',
106
124
  'replica.query',
125
+ 'replica.tallies',
107
126
  'replica.entries',
108
127
  'replica.typed',
109
128
  'replica.referrers',
110
- 'replica.walk',
111
- 'replica.files',
129
+ 'replica.folders',
112
130
  'replica.typedWithin',
113
- 'replica.counts',
114
- 'replica.search',
115
- 'replica.received',
116
- 'replica.receivedParts',
117
- 'replica.transferPart',
118
131
  'opened.find',
119
132
  'file.part',
120
133
  'conversation.held',
121
134
  'conversation.window',
122
135
  'conversation.count',
123
136
  'conversation.turn',
137
+ 'conversation.rows',
138
+ 'conversation.jobs',
139
+ 'conversation.tallies',
124
140
  ]);
125
141
  const KINDS = new Set([
126
142
  ...READS,
@@ -132,19 +148,19 @@ const KINDS = new Set([
132
148
  'authored.claim',
133
149
  'authored.end',
134
150
  'replica.stage',
135
- 'replica.keepPart',
151
+ 'replica.resume',
136
152
  'replica.refresh',
137
153
  'replica.unrefresh',
138
154
  'replica.commit',
139
155
  'replica.fence',
140
156
  'replica.erase',
141
157
  'replica.abandon',
158
+ 'replica.unfence',
142
159
  'replica.release',
143
160
  'replica.rederive',
161
+ 'replica.demote',
144
162
  'replica.fact',
145
163
  'replica.present',
146
- 'replica.receive',
147
- 'replica.unreceive',
148
164
  'opened.begin',
149
165
  'opened.part',
150
166
  'opened.keep',
@@ -225,29 +241,71 @@ function unowned(connection) {
225
241
  connection.run(UNOWNED_PARTS);
226
242
  }
227
243
  function queued(connection) {
244
+ return connection.rows('SELECT locator, ids FROM authored_queues').map((row) => ({
245
+ locator: text(row.locator),
246
+ ids: JSON.parse(text(row.ids)),
247
+ }));
248
+ }
249
+ function heldWork(connection, locator) {
250
+ const room = heldRoom(connection, locator);
251
+ const pending = new Map(connection
252
+ .rows('SELECT id, batch, state FROM authored_pending WHERE locator = ?', [locator])
253
+ .map((row) => [
254
+ text(row.id),
255
+ { id: text(row.id), batch: text(row.batch), state: text(row.state) },
256
+ ]));
228
257
  return {
229
- queues: connection.rows('SELECT locator, ids FROM authored_queues').map((row) => ({
230
- locator: text(row.locator),
231
- ids: JSON.parse(text(row.ids)),
232
- })),
233
- pending: connection
234
- .rows('SELECT locator, id, batch, state FROM authored_pending')
235
- .map((row) => ({
236
- locator: text(row.locator),
237
- id: text(row.id),
238
- batch: text(row.batch),
239
- state: text(row.state),
240
- })),
258
+ room: room === null ? null : { ...room, overlaid: overlaid(connection, room.key) },
259
+ queue: heldOrder(connection, locator).map((id) => {
260
+ const held = pending.get(id);
261
+ if (held === undefined)
262
+ throw new Error(`Missing Pending Change ${locator}/${id}`);
263
+ return held;
264
+ }),
265
+ };
266
+ }
267
+ function status(connection) {
268
+ const [completed] = connection.rows("SELECT value FROM replica_facts WHERE name = 'lastCompleted'");
269
+ return {
270
+ queued: texts(connection.rows('SELECT locator FROM authored_queues'), 'locator'),
271
+ lastCompleted: completed === undefined ? null : text(completed.value),
272
+ changes: connection
273
+ .rows('SELECT p.locator, c.value AS path FROM authored_pending p, json_each(p.paths) c')
274
+ .map((row) => ({ locator: text(row.locator), path: text(row.path) })),
275
+ resets: connection
276
+ .rows(`SELECT locator, json_extract(state, '$.submission.reset') AS reset FROM authored_pending
277
+ WHERE json_extract(state, '$.submission.state') = 'reset'`)
278
+ .map((row) => ({ locator: text(row.locator), reset: text(row.reset) })),
279
+ returned: texts(connection.rows(`SELECT record FROM authored_room_drafts WHERE json_type(record, '$.returned') = 'object'`), 'record'),
280
+ };
281
+ }
282
+ function pendingOf(connection, locator, id) {
283
+ const [held] = connection.rows('SELECT state FROM authored_pending WHERE locator = ? AND id = ?', [locator, id]);
284
+ return {
285
+ state: held === undefined ? null : text(held.state),
286
+ room: connection.rows('SELECT 1 FROM replica_rooms WHERE locator = ?', [locator]).length > 0,
241
287
  };
242
288
  }
289
+ function roomDraftRecords(connection, locator, paths) {
290
+ if (locator === null) {
291
+ return texts(connection.rows('SELECT record FROM authored_room_drafts'), 'record');
292
+ }
293
+ if (paths === null) {
294
+ return texts(connection.rows('SELECT record FROM authored_room_drafts WHERE locator = ?', [locator]), 'record');
295
+ }
296
+ return texts(connection.rows('SELECT record FROM authored_room_drafts WHERE locator = ? AND path IN (SELECT value FROM json_each(?))', [locator, JSON.stringify(paths)]), 'record');
297
+ }
298
+ function chatDraftRecord(connection, conversation) {
299
+ const [held] = connection.rows('SELECT record FROM authored_chat_drafts WHERE conversation = ?', [
300
+ conversation,
301
+ ]);
302
+ return held === undefined ? null : text(held.record);
303
+ }
243
304
  function heldOrder(connection, locator) {
244
305
  const [held] = connection.rows('SELECT ids FROM authored_queues WHERE locator = ?', [locator]);
245
306
  return held === undefined ? [] : JSON.parse(text(held.ids));
246
307
  }
247
- function keptRows(connection, { locator, order, added, founded, roomDrafts, consumedRoomDrafts, amendments }) {
248
- for (const { id, amendment } of amendments ?? []) {
249
- amended(connection, { kind: 'authored.amend', locator, id, amendment });
250
- }
308
+ function keptRows(connection, { locator, order, added, founded, roomDrafts, consumedRoomDrafts }) {
251
309
  for (const path of consumedRoomDrafts ?? []) {
252
310
  connection.run('DELETE FROM authored_room_drafts WHERE locator = ? AND path = ?', [
253
311
  locator,
@@ -274,19 +332,26 @@ function keptRows(connection, { locator, order, added, founded, roomDrafts, cons
274
332
  connection.run('DELETE FROM authored_pending WHERE locator = ? AND id = ?', [locator, id]);
275
333
  }
276
334
  }
277
- for (const { id, batch, state, files } of added ?? []) {
335
+ for (const { id, batch, state, files, paths, moving } of added ?? []) {
278
336
  if (before.has(id) || !after.has(id))
279
337
  continue;
280
- connection.run('INSERT OR REPLACE INTO authored_pending (locator, id, batch, state, files) VALUES (?, ?, ?, ?, ?)', [locator, id, batch, state, owned(files)]);
338
+ connection.run('INSERT OR REPLACE INTO authored_pending (locator, id, batch, state, files, paths, moving) VALUES (?, ?, ?, ?, ?, ?, ?)', [locator, id, batch, state, owned(files), JSON.stringify(paths), moving ? 1 : 0]);
281
339
  }
282
- for (const { id, batch, files } of founded ?? []) {
340
+ for (const { id, batch, files, paths, moving } of founded ?? []) {
283
341
  const [held] = connection.rows('SELECT files FROM authored_pending WHERE locator = ? AND id = ?', [locator, id]);
284
342
  if (held === undefined || !before.has(id))
285
343
  continue;
286
- connection.run('UPDATE authored_pending SET batch = ?, files = ? WHERE locator = ? AND id = ?', [batch, owned([...JSON.parse(text(held.files)), ...files]), locator, id]);
344
+ connection.run('UPDATE authored_pending SET batch = ?, files = ?, paths = ?, moving = ? WHERE locator = ? AND id = ?', [
345
+ batch,
346
+ owned([...JSON.parse(text(held.files)), ...files]),
347
+ JSON.stringify(paths),
348
+ moving ? 1 : 0,
349
+ locator,
350
+ id,
351
+ ]);
287
352
  }
288
353
  }
289
- async function kept(connection, { writes, files, whole, discards }) {
354
+ async function kept(connection, { writes, files, whole }) {
290
355
  const refused = writes.find(({ locator }) => fenced(connection, locator));
291
356
  if (refused !== undefined && whole)
292
357
  return refused.locator;
@@ -295,8 +360,6 @@ async function kept(connection, { writes, files, whole, discards }) {
295
360
  if (!fenced(connection, write.locator))
296
361
  keptRows(connection, write);
297
362
  }
298
- for (const locator of discards ?? [])
299
- roomWork(connection, locator);
300
363
  unowned(connection);
301
364
  return refused?.locator ?? null;
302
365
  }
@@ -366,11 +429,15 @@ function fence(connection, locators) {
366
429
  return fenceRooms(connection, locators);
367
430
  }
368
431
  function erased(connection, { locator, limit }) {
369
- const done = erase(connection, locator, limit);
370
- if (locator !== null && !done.remaining) {
432
+ return erase(connection, locator, limit);
433
+ }
434
+ function unfenced(connection, locators) {
435
+ const erased = locators.filter((locator) => connection.rows('SELECT 1 FROM replica_erasing WHERE locator = ? LIMIT 1', [locator])
436
+ .length === 0);
437
+ for (const locator of erased) {
371
438
  connection.run('DELETE FROM authored_fences WHERE locator = ?', [locator]);
372
439
  }
373
- return done;
440
+ return erased;
374
441
  }
375
442
  function claim(connection, holder) {
376
443
  const held = fact(connection, 'holder');
@@ -449,6 +516,16 @@ function keptOpened(connection, { key, sha256, byteLength, parts }) {
449
516
  unowned(connection);
450
517
  return true;
451
518
  }
519
+ const UNSENT = [
520
+ ['pending-change', 'authored_pending'],
521
+ ['call', 'authored_calls'],
522
+ ['room-draft', 'authored_room_drafts'],
523
+ ['chat-draft', 'authored_chat_drafts'],
524
+ ];
525
+ function unsent(connection) {
526
+ const [held] = connection.rows(`SELECT ${UNSENT.map(([kind, table]) => `(SELECT count(*) FROM ${table}) AS "${kind}"`).join(', ')}`);
527
+ return UNSENT.map(([kind]) => ({ kind, count: count(held?.[kind]) })).filter((work) => work.count > 0);
528
+ }
452
529
  function cleaned(connection) {
453
530
  connection.run('DELETE FROM opened_files');
454
531
  const [freed] = connection.rows(`SELECT coalesce(sum(length(bytes)), 0) AS bytes FROM file_parts WHERE key NOT IN (${AUTHORED_KEYS})`);
@@ -458,47 +535,51 @@ function cleaned(connection) {
458
535
  async function committed(connection, { authored, rooms }) {
459
536
  const refused = authored === undefined ? null : await kept(connection, authored);
460
537
  if (refused !== null && authored?.whole === true) {
461
- return { fenced: refused, counts: {}, changed: {}, indexed: {} };
538
+ return { fenced: refused, changed: {}, indexed: {} };
462
539
  }
463
540
  return {
464
541
  fenced: refused,
465
542
  ...commit(connection, rooms.filter(({ locator }) => !fenced(connection, locator))),
466
543
  };
467
544
  }
468
- function receivedParts(connection, key, count) {
469
- return connection
470
- .rows('SELECT value FROM replica_received_parts WHERE key = ? AND part < ? ORDER BY part', [
471
- key,
472
- count,
473
- ])
474
- .map((row) => JSON.parse(text(row.value)));
475
- }
476
- function receive(connection, { key, head, part }) {
477
- connection.run('INSERT OR REPLACE INTO replica_received (key, head) VALUES (?, ?)', [key, head]);
478
- if (part !== undefined) {
479
- connection.run('INSERT OR REPLACE INTO replica_received_parts (key, part, value) VALUES (?, ?, ?)', [key, part.index, part.value]);
480
- }
481
- return null;
482
- }
483
- function unreceive(connection, keys) {
484
- for (const key of keys) {
485
- connection.run('DELETE FROM replica_received WHERE key = ?', [key]);
486
- connection.run('DELETE FROM replica_received_parts WHERE key = ?', [key]);
487
- }
488
- return null;
489
- }
490
545
  export function answerRuntime(connection, request, spool) {
491
546
  switch (request.kind) {
492
- case 'authored.queues':
493
- return queued(connection);
547
+ case 'authored.status':
548
+ return status(connection);
549
+ case 'authored.moving':
550
+ return {
551
+ queues: queued(connection),
552
+ pending: connection
553
+ .rows('SELECT locator, id, batch, state FROM authored_pending WHERE moving = 1 ORDER BY locator')
554
+ .map((row) => ({
555
+ locator: text(row.locator),
556
+ id: text(row.id),
557
+ batch: text(row.batch),
558
+ state: text(row.state),
559
+ })),
560
+ };
561
+ case 'authored.pending':
562
+ return pendingOf(connection, request.locator, request.id);
563
+ case 'authored.batch': {
564
+ const [held] = connection.rows('SELECT batch FROM authored_pending WHERE locator = ? AND id = ?', [request.locator, request.id]);
565
+ return held === undefined ? null : text(held.batch);
566
+ }
567
+ case 'authored.order':
568
+ return heldOrder(connection, request.locator);
569
+ case 'authored.queued':
570
+ return texts(connection.rows('SELECT locator FROM authored_queues'), 'locator');
494
571
  case 'authored.roomDrafts':
495
- return texts(connection.rows('SELECT record FROM authored_room_drafts'), 'record');
572
+ return roomDraftRecords(connection, request.locator, request.paths);
573
+ case 'authored.chatDraft.read':
574
+ return chatDraftRecord(connection, request.conversation);
496
575
  case 'authored.chatDrafts':
497
576
  return texts(connection.rows('SELECT record FROM authored_chat_drafts'), 'record');
498
577
  case 'authored.calls':
499
578
  return texts(connection.rows('SELECT record FROM authored_calls'), 'record');
500
579
  case 'authored.fences':
501
580
  return texts(connection.rows('SELECT locator FROM authored_fences'), 'locator');
581
+ case 'authored.unsent':
582
+ return unsent(connection);
502
583
  case 'authored.fact':
503
584
  return fact(connection, request.name);
504
585
  case 'authored.file':
@@ -519,39 +600,43 @@ export function answerRuntime(connection, request, spool) {
519
600
  return end(connection, request.holder);
520
601
  case 'replica.open':
521
602
  return opened(connection);
603
+ case 'replica.rooms':
604
+ return heldRooms(connection, request.locators);
605
+ case 'replica.room':
606
+ return heldWork(connection, request.locator);
607
+ case 'replica.covering':
608
+ return {
609
+ rooms: covering(connection),
610
+ queues: queued(connection),
611
+ };
612
+ case 'replica.offline':
613
+ return offlineMode(connection, request.locator);
614
+ case 'replica.held':
615
+ return { held: heldLocators(connection), staged: stagedLocators(connection) };
616
+ case 'replica.demote':
617
+ return demoted(connection);
618
+ case 'replica.facts':
619
+ return facts(connection);
620
+ case 'replica.presentation':
621
+ return presentation(connection, request.app);
522
622
  case 'replica.query':
523
623
  return answerQuery(connection, request.plan, request.rooms, request.budget);
624
+ case 'replica.tallies':
625
+ return roomTallies(connection, request.totals, request.rooms, request.since);
524
626
  case 'replica.entries':
525
627
  return entries(connection, request.key, request.view, request.paths);
526
628
  case 'replica.typed':
527
629
  return typed(connection, request.key, request.view, request.paths);
528
630
  case 'replica.referrers':
529
- return referrers(connection, request.key, request.view, request.targets, request.kinds);
530
- case 'replica.walk':
531
- return walk(connection, request.key, request.view, request.after, request.bytes);
532
- case 'replica.files':
533
- return files(connection, request.key, request.view, request.after, request.limit);
631
+ return referrers(connection, request.key, request.view, request.targets);
632
+ case 'replica.folders':
633
+ return folders(connection, request.key, request.view, request.paths);
534
634
  case 'replica.typedWithin':
535
635
  return typedWithin(connection, request.key, request.view, request.roots, request.within);
536
- case 'replica.counts':
537
- return counts(connection, request.rooms);
538
- case 'replica.search': {
539
- const { kind: _kind, ...ask } = request;
540
- return searched(connection, ask);
541
- }
542
- case 'replica.received':
543
- return connection.rows('SELECT key, head FROM replica_received').map((row) => ({
544
- key: text(row.key),
545
- head: JSON.parse(text(row.head)),
546
- }));
547
- case 'replica.receivedParts':
548
- return receivedParts(connection, request.key, request.count);
549
636
  case 'replica.stage':
550
637
  return fenced(connection, request.locator) ? null : stage(connection, request);
551
- case 'replica.transferPart':
552
- return transferPart(connection, request.locator, request.token);
553
- case 'replica.keepPart':
554
- return fenced(connection, request.locator) ? null : keepPart(connection, request);
638
+ case 'replica.resume':
639
+ return fenced(connection, request.locator) ? null : resume(connection, request);
555
640
  case 'replica.refresh':
556
641
  return refreshing(connection, request);
557
642
  case 'replica.unrefresh':
@@ -563,11 +648,13 @@ export function answerRuntime(connection, request, spool) {
563
648
  case 'replica.erase':
564
649
  return erased(connection, request);
565
650
  case 'replica.abandon':
566
- return abandon(connection, request.locator, request.kept);
651
+ return abandon(connection, request.locator);
652
+ case 'replica.unfence':
653
+ return unfenced(connection, request.locators);
567
654
  case 'replica.release':
568
655
  return release(connection, request.key);
569
656
  case 'replica.rederive':
570
- return rederive(connection, request.key, request.limit);
657
+ return rederive(connection, request.key, request.composed, request.bytes, request.ms, request.floor);
571
658
  case 'replica.fact':
572
659
  connection.run('INSERT OR REPLACE INTO replica_facts (name, value) VALUES (?, ?)', [
573
660
  request.name,
@@ -580,10 +667,6 @@ export function answerRuntime(connection, request, spool) {
580
667
  request.value,
581
668
  ]);
582
669
  return null;
583
- case 'replica.receive':
584
- return receive(connection, request);
585
- case 'replica.unreceive':
586
- return unreceive(connection, request.keys);
587
670
  case 'opened.find':
588
671
  return openedFile(connection, request);
589
672
  case 'opened.begin':
@@ -599,17 +682,23 @@ export function answerRuntime(connection, request, spool) {
599
682
  case 'file.part':
600
683
  return filePart(connection, request.key, request.part);
601
684
  case 'conversation.held':
602
- return heldConversations(connection);
685
+ return heldConversations(connection, request.conversations);
603
686
  case 'conversation.keep': {
604
687
  const { kind: _kind, ...write } = request;
605
688
  return keptMessages(connection, write);
606
689
  }
607
690
  case 'conversation.window':
608
- return messageWindow(connection, request.conversation, request.limit, request.before);
691
+ return messageWindow(connection, request.conversation, request.limit, request.before, request.phrase);
609
692
  case 'conversation.count':
610
- return messageCount(connection, request.conversation);
693
+ return messageCount(connection, request.conversation, request.phrase);
611
694
  case 'conversation.turn':
612
695
  return turnArrived(connection, request.conversation, request.turn);
696
+ case 'conversation.rows':
697
+ return conversationRows(connection, request.conversations);
698
+ case 'conversation.jobs':
699
+ return jobMarkers(connection, request.conversation);
700
+ case 'conversation.tallies':
701
+ return conversationTallies(connection, request.conversations, request.since);
613
702
  case 'conversation.drop':
614
703
  chatDraft(connection, {
615
704
  kind: 'authored.chatDraft',
@@ -0,0 +1,18 @@
1
+ import { type QueryRoom } from './compile.js';
2
+ import type { GroupTotal, Plan } from './query.js';
3
+ import type { Connection } from './tables.js';
4
+ export type RoomTally = {
5
+ readonly key: number;
6
+ readonly documents: GroupTotal;
7
+ readonly files: GroupTotal;
8
+ readonly documentTimes: readonly (readonly [string | null, string | null])[];
9
+ readonly chatTimes: readonly string[];
10
+ readonly messageTimes: readonly string[];
11
+ };
12
+ export type ConversationTally = {
13
+ readonly conversation: string;
14
+ readonly said: readonly string[];
15
+ };
16
+ export declare function roomTallies(connection: Connection, totals: Plan, rooms: readonly QueryRoom[], since: string): RoomTally[];
17
+ export declare function conversationTallies(connection: Connection, conversations: readonly string[] | null, since: string): ConversationTally[];
18
+ //# sourceMappingURL=tallies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tallies.d.ts","sourceRoot":"","sources":["../src/tallies.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAE5E,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,aAAa,CAAC;AAI1D,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC;IAC7E,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAAE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAmHpG,wBAAgB,WAAW,CACzB,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,IAAI,EACZ,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,KAAK,EAAE,MAAM,GACZ,SAAS,EAAE,CAwBb;AA0BD,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,UAAU,EACtB,aAAa,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,EACvC,KAAK,EAAE,MAAM,GACZ,iBAAiB,EAAE,CAerB"}
@@ -0,0 +1,129 @@
1
+ import { instantKey, sql } from '@agentshouse/mdmodel';
2
+ import { CHAT_MESSAGE_TYPE, CHAT_TYPE } from './chat-types.js';
3
+ import { answerQuery, VISIBLE, within } from './compile.js';
4
+ import { HOUSE_DIRECTORY } from './figures.js';
5
+ const NONE = { count: 0, size: 0 };
6
+ const { all, fragment, joined } = sql;
7
+ function instant(value) {
8
+ return typeof value === 'string' ? value : null;
9
+ }
10
+ function held(alias, keys) {
11
+ const reserved = within(alias, HOUSE_DIRECTORY);
12
+ return all([
13
+ fragment(`${alias}.key IN (SELECT value FROM json_each(?))`, [JSON.stringify(keys)]),
14
+ fragment(VISIBLE(alias)),
15
+ fragment(`NOT ${reserved.sql}`, reserved.bind),
16
+ ]);
17
+ }
18
+ function valued(alias, document, field) {
19
+ return joined([
20
+ fragment(`${alias}.doc = ${document}.id AND `),
21
+ sql.grouped(sql.placed(alias, field, 'exact')),
22
+ fragment(` AND ${alias}.item = -1`),
23
+ ], '');
24
+ }
25
+ function inboxed(card) {
26
+ return joined([
27
+ fragment('EXISTS (SELECT 1 FROM replica_values i WHERE '),
28
+ valued('i', card, '/inbox'),
29
+ fragment(" AND i.kind = 'string')"),
30
+ ], '');
31
+ }
32
+ function rowsOf(connection, query) {
33
+ return connection.rows(query.sql, query.bind);
34
+ }
35
+ function documentTimes(connection, keys, since) {
36
+ return rowsOf(connection, joined([
37
+ fragment('SELECT d.key AS key, d.created_instant AS created, d.changed_instant AS changed FROM replica_entries d WHERE '),
38
+ held('d', keys),
39
+ fragment(" AND d.binary IS NULL AND COALESCE(d.type, '') NOT IN (?, ?) AND (d.created_instant >= ? OR d.changed_instant >= ?)", [CHAT_TYPE, CHAT_MESSAGE_TYPE, since, since]),
40
+ ], ''));
41
+ }
42
+ function chatTimes(connection, keys, since) {
43
+ return rowsOf(connection, joined([
44
+ fragment('SELECT d.key AS key, d.created_instant AS at FROM replica_entries d WHERE '),
45
+ held('d', keys),
46
+ fragment(' AND d.type = ? AND d.created_instant >= ? AND ', [CHAT_TYPE, since]),
47
+ inboxed('d'),
48
+ ], ''));
49
+ }
50
+ function messageTimes(connection, keys, since) {
51
+ return rowsOf(connection, joined([
52
+ fragment('SELECT m.key AS key, s.instant AS at FROM replica_entries m JOIN replica_values s ON '),
53
+ valued('s', 'm', '/sent_at'),
54
+ fragment(' AND s.instant >= ? JOIN replica_values r ON ', [since]),
55
+ valued('r', 'm', '/direction'),
56
+ fragment(" AND r.kind = 'string' AND r.value = 'in' WHERE "),
57
+ held('m', keys),
58
+ fragment(` AND m.type = ? AND EXISTS (SELECT 1 FROM replica_entries c WHERE c.key = m.key AND c.type = ? AND ${VISIBLE('c')} AND c.folder = m.folder AND `, [CHAT_MESSAGE_TYPE, CHAT_TYPE]),
59
+ inboxed('c'),
60
+ fragment(')'),
61
+ ], ''));
62
+ }
63
+ function byKey(rows, read) {
64
+ const kept = new Map();
65
+ for (const row of rows) {
66
+ const key = Number(row.key);
67
+ const list = kept.get(key) ?? [];
68
+ list.push(read(row));
69
+ kept.set(key, list);
70
+ }
71
+ return kept;
72
+ }
73
+ export function roomTallies(connection, totals, rooms, since) {
74
+ const keys = rooms.map(({ key }) => key);
75
+ const from = instantKey(since);
76
+ const answered = answerQuery(connection, totals, rooms);
77
+ const summed = new Map(('groups' in answered ? answered.groups : []).map((group) => [String(group.key[0]), group]));
78
+ const documents = byKey(documentTimes(connection, keys, from), (row) => [instant(row.created), instant(row.changed)]);
79
+ const chats = byKey(chatTimes(connection, keys, from), (row) => instant(row.at));
80
+ const messages = byKey(messageTimes(connection, keys, from), (row) => instant(row.at));
81
+ return rooms.map(({ locator, key }) => {
82
+ const total = summed.get(locator);
83
+ return {
84
+ key,
85
+ documents: total?.documents ?? NONE,
86
+ files: total?.files ?? NONE,
87
+ documentTimes: documents.get(key) ?? [],
88
+ chatTimes: chats.get(key) ?? [],
89
+ messageTimes: messages.get(key) ?? [],
90
+ };
91
+ });
92
+ }
93
+ const ACCEPTED = "message ->> '$.accepted_at'";
94
+ const NEWER = 'ORDER BY n.position DESC, n.identity DESC LIMIT 1';
95
+ const SAID = `WITH RECURSIVE
96
+ named(conversation) AS (SELECT value FROM json_each(?)),
97
+ walk(id, conversation, position, identity, at) AS (
98
+ SELECT m.id, m.conversation, m.position, m.identity, m.${ACCEPTED}
99
+ FROM named JOIN conversation_messages m ON m.id = (
100
+ SELECT n.id FROM conversation_messages n WHERE n.conversation = named.conversation ${NEWER}
101
+ )
102
+ UNION ALL
103
+ SELECT m.id, m.conversation, m.position, m.identity, m.${ACCEPTED}
104
+ FROM walk JOIN conversation_messages m ON m.id = (
105
+ SELECT n.id FROM conversation_messages n WHERE n.conversation = walk.conversation
106
+ AND (n.position < walk.position OR (n.position = walk.position AND n.identity < walk.identity))
107
+ ${NEWER}
108
+ )
109
+ WHERE julianday(walk.at) >= julianday(?)
110
+ )
111
+ SELECT walk.conversation AS conversation, walk.at AS at
112
+ FROM walk JOIN conversation_messages m ON m.id = walk.id
113
+ WHERE m.message ->> '$.role' IN ('user', 'assistant') AND julianday(walk.at) >= julianday(?)`;
114
+ export function conversationTallies(connection, conversations, since) {
115
+ const named = conversations ??
116
+ connection
117
+ .rows('SELECT DISTINCT conversation FROM conversation_messages')
118
+ .map((row) => String(row.conversation));
119
+ const from = instantKey(since);
120
+ const said = new Map();
121
+ for (const row of connection.rows(SAID, [JSON.stringify(named), since, since])) {
122
+ const at = instantKey(row.at);
123
+ if (at === null || at < from)
124
+ continue;
125
+ const conversation = String(row.conversation);
126
+ said.set(conversation, [...(said.get(conversation) ?? []), at]);
127
+ }
128
+ return named.map((conversation) => ({ conversation, said: said.get(conversation) ?? [] }));
129
+ }
@@ -1,10 +1,5 @@
1
- import type { Refusal } from '@agentshouse/mdmodel';
2
1
  export type Unconfirmed = {
3
2
  kind: 'unconfirmed';
4
3
  };
5
- export type Refused = {
6
- kind: 'refused';
7
- refusal: Refusal;
8
- };
9
4
  export declare const unconfirmed: Unconfirmed;
10
5
  //# sourceMappingURL=transfer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transfer.d.ts","sourceRoot":"","sources":["../src/transfer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEpD,MAAM,MAAM,WAAW,GAAG;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAElD,MAAM,MAAM,OAAO,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAE5D,eAAO,MAAM,WAAW,EAAE,WAAqC,CAAC"}
1
+ {"version":3,"file":"transfer.d.ts","sourceRoot":"","sources":["../src/transfer.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAElD,eAAO,MAAM,WAAW,EAAE,WAAqC,CAAC"}
@@ -19,12 +19,11 @@ export type Changed = {
19
19
  readonly was: string | null | undefined;
20
20
  readonly now: string | null | undefined;
21
21
  };
22
- type CleanSource = {
22
+ type Neighbourhood = {
23
23
  readonly lookup: Lookup;
24
24
  readonly texts: (paths: readonly string[]) => Promise<ReadonlyMap<string, string | null>>;
25
- readonly referrers: ((targets: readonly string[]) => Promise<readonly string[]>) | null;
25
+ readonly referrers: (targets: readonly string[]) => Promise<readonly string[]>;
26
26
  };
27
- export declare function sameComposition(left: Composition, right: Composition): boolean;
28
- export declare function changedCleanly(changes: readonly Changed[], previous: Composition, composition: Composition, source: CleanSource): Promise<boolean>;
27
+ export declare function validatedNeighbourhood(changes: readonly Changed[], composition: Composition, source: Neighbourhood): Promise<Validation>;
29
28
  export {};
30
29
  //# sourceMappingURL=validation.d.ts.map