@agentshouse/mdruntime 0.1.0-cefbc8bb8a1c0 → 0.1.0-cf04ede9fb796

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 (282) hide show
  1. package/README.md +2 -2
  2. package/dist/acceptance.d.ts +6 -0
  3. package/dist/acceptance.d.ts.map +1 -0
  4. package/dist/acceptance.js +389 -0
  5. package/dist/actions.d.ts +20 -0
  6. package/dist/actions.d.ts.map +1 -0
  7. package/dist/actions.js +251 -0
  8. package/dist/adapter.d.ts +86 -67
  9. package/dist/adapter.d.ts.map +1 -1
  10. package/dist/attached-model.d.ts +3 -10
  11. package/dist/attached-model.d.ts.map +1 -1
  12. package/dist/attached-model.js +21 -56
  13. package/dist/bootstrap.d.ts +10 -9
  14. package/dist/bootstrap.d.ts.map +1 -1
  15. package/dist/bootstrap.js +211 -81
  16. package/dist/cell.d.ts +6 -0
  17. package/dist/cell.d.ts.map +1 -0
  18. package/dist/cell.js +9 -0
  19. package/dist/chain.d.ts +2 -1
  20. package/dist/chain.d.ts.map +1 -1
  21. package/dist/change.d.ts +1 -0
  22. package/dist/change.d.ts.map +1 -1
  23. package/dist/chat-types.d.ts +5 -0
  24. package/dist/chat-types.d.ts.map +1 -0
  25. package/dist/chat-types.js +13 -0
  26. package/dist/client.d.ts +21 -6
  27. package/dist/client.d.ts.map +1 -1
  28. package/dist/client.js +3 -1
  29. package/dist/collection.d.ts +2 -4
  30. package/dist/collection.d.ts.map +1 -1
  31. package/dist/collection.js +1 -17
  32. package/dist/compile.d.ts +6 -6
  33. package/dist/compile.d.ts.map +1 -1
  34. package/dist/compile.js +263 -319
  35. package/dist/connection.d.ts +5 -143
  36. package/dist/connection.d.ts.map +1 -1
  37. package/dist/connection.js +102 -5311
  38. package/dist/context.d.ts +2 -0
  39. package/dist/context.d.ts.map +1 -0
  40. package/dist/context.js +7 -0
  41. package/dist/conversation-live.d.ts +136 -0
  42. package/dist/conversation-live.d.ts.map +1 -0
  43. package/dist/conversation-live.js +248 -0
  44. package/dist/conversation-row.d.ts +33 -0
  45. package/dist/conversation-row.d.ts.map +1 -0
  46. package/dist/conversation-row.js +53 -0
  47. package/dist/conversation.d.ts +41 -45
  48. package/dist/conversation.d.ts.map +1 -1
  49. package/dist/conversation.js +859 -870
  50. package/dist/discovery.d.ts +10 -0
  51. package/dist/discovery.d.ts.map +1 -0
  52. package/dist/discovery.js +175 -0
  53. package/dist/durability.d.ts +15 -1
  54. package/dist/durability.d.ts.map +1 -1
  55. package/dist/entry.d.ts +2 -2
  56. package/dist/entry.d.ts.map +1 -1
  57. package/dist/entry.js +17 -7
  58. package/dist/figures.d.ts +49 -0
  59. package/dist/figures.d.ts.map +1 -0
  60. package/dist/figures.js +197 -0
  61. package/dist/holdings.d.ts +29 -0
  62. package/dist/holdings.d.ts.map +1 -0
  63. package/dist/holdings.js +95 -0
  64. package/dist/host.d.ts +4 -7
  65. package/dist/host.d.ts.map +1 -1
  66. package/dist/host.js +23 -30
  67. package/dist/identity.d.ts +0 -1
  68. package/dist/identity.d.ts.map +1 -1
  69. package/dist/identity.js +0 -3
  70. package/dist/index.d.ts +25 -8
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +7 -1
  73. package/dist/install.d.ts +56 -0
  74. package/dist/install.d.ts.map +1 -0
  75. package/dist/install.js +556 -0
  76. package/dist/job.d.ts +33 -0
  77. package/dist/job.d.ts.map +1 -0
  78. package/dist/job.js +54 -0
  79. package/dist/lifecycle.d.ts +7 -0
  80. package/dist/lifecycle.d.ts.map +1 -0
  81. package/dist/lifecycle.js +268 -0
  82. package/dist/listing.d.ts +53 -0
  83. package/dist/listing.d.ts.map +1 -0
  84. package/dist/listing.js +176 -0
  85. package/dist/live.d.ts +5 -4
  86. package/dist/live.d.ts.map +1 -1
  87. package/dist/live.js +63 -22
  88. package/dist/locks.d.ts +9 -0
  89. package/dist/locks.d.ts.map +1 -0
  90. package/dist/locks.js +61 -0
  91. package/dist/log.d.ts +1 -0
  92. package/dist/log.d.ts.map +1 -1
  93. package/dist/lost.d.ts +5 -0
  94. package/dist/lost.d.ts.map +1 -0
  95. package/dist/lost.js +61 -0
  96. package/dist/memory.d.ts +11 -4
  97. package/dist/memory.d.ts.map +1 -1
  98. package/dist/memory.js +172 -82
  99. package/dist/messages.d.ts +27 -3
  100. package/dist/messages.d.ts.map +1 -1
  101. package/dist/messages.js +97 -19
  102. package/dist/model.d.ts +2 -10
  103. package/dist/model.d.ts.map +1 -1
  104. package/dist/model.js +0 -18
  105. package/dist/models.d.ts +14 -0
  106. package/dist/models.d.ts.map +1 -0
  107. package/dist/models.js +195 -0
  108. package/dist/observation.d.ts +2 -2
  109. package/dist/observation.d.ts.map +1 -1
  110. package/dist/observation.js +7 -2
  111. package/dist/opening.d.ts +4 -0
  112. package/dist/opening.d.ts.map +1 -0
  113. package/dist/opening.js +57 -0
  114. package/dist/outcome.d.ts +4 -4
  115. package/dist/outcome.d.ts.map +1 -1
  116. package/dist/outcome.js +3 -0
  117. package/dist/overlay.d.ts +1 -0
  118. package/dist/overlay.d.ts.map +1 -1
  119. package/dist/overlay.js +11 -0
  120. package/dist/parts.d.ts +60 -0
  121. package/dist/parts.d.ts.map +1 -0
  122. package/dist/parts.js +126 -0
  123. package/dist/paths.d.ts +1 -0
  124. package/dist/paths.d.ts.map +1 -1
  125. package/dist/paths.js +22 -0
  126. package/dist/preparation.d.ts +20 -0
  127. package/dist/preparation.d.ts.map +1 -0
  128. package/dist/preparation.js +226 -0
  129. package/dist/queries.d.ts +16 -0
  130. package/dist/queries.d.ts.map +1 -0
  131. package/dist/queries.js +272 -0
  132. package/dist/query.d.ts +14 -209
  133. package/dist/query.d.ts.map +1 -1
  134. package/dist/query.js +3 -505
  135. package/dist/questions.d.ts +34 -0
  136. package/dist/questions.d.ts.map +1 -0
  137. package/dist/questions.js +122 -0
  138. package/dist/reach.d.ts +15 -13
  139. package/dist/reach.d.ts.map +1 -1
  140. package/dist/reach.js +183 -137
  141. package/dist/react.d.ts +10 -2
  142. package/dist/react.d.ts.map +1 -1
  143. package/dist/read.d.ts +7 -0
  144. package/dist/read.d.ts.map +1 -0
  145. package/dist/read.js +17 -0
  146. package/dist/readiness.d.ts +2 -16
  147. package/dist/readiness.d.ts.map +1 -1
  148. package/dist/readiness.js +4 -10
  149. package/dist/reads.d.ts +36 -10
  150. package/dist/reads.d.ts.map +1 -1
  151. package/dist/reads.js +158 -78
  152. package/dist/recovery.d.ts.map +1 -1
  153. package/dist/recovery.js +9 -1
  154. package/dist/refresh.d.ts +7 -5
  155. package/dist/refresh.d.ts.map +1 -1
  156. package/dist/refresh.js +33 -5
  157. package/dist/refusal.d.ts +5 -0
  158. package/dist/refusal.d.ts.map +1 -0
  159. package/dist/refusal.js +56 -0
  160. package/dist/replica.d.ts +9 -9
  161. package/dist/replica.d.ts.map +1 -1
  162. package/dist/replica.js +52 -20
  163. package/dist/room-draft.d.ts +4 -3
  164. package/dist/room-draft.d.ts.map +1 -1
  165. package/dist/room-draft.js +65 -52
  166. package/dist/room-drafts.d.ts +24 -0
  167. package/dist/room-drafts.d.ts.map +1 -0
  168. package/dist/room-drafts.js +216 -0
  169. package/dist/rows.d.ts +14 -21
  170. package/dist/rows.d.ts.map +1 -1
  171. package/dist/rows.js +48 -184
  172. package/dist/runtime.d.ts +43 -0
  173. package/dist/runtime.d.ts.map +1 -0
  174. package/dist/runtime.js +1 -0
  175. package/dist/save.d.ts +5 -1
  176. package/dist/save.d.ts.map +1 -1
  177. package/dist/save.js +19 -13
  178. package/dist/saves.d.ts +70 -0
  179. package/dist/saves.d.ts.map +1 -0
  180. package/dist/saves.js +1008 -0
  181. package/dist/search.d.ts +12 -63
  182. package/dist/search.d.ts.map +1 -1
  183. package/dist/search.js +9 -52
  184. package/dist/seed.d.ts +11 -2
  185. package/dist/seed.d.ts.map +1 -1
  186. package/dist/seed.js +71 -0
  187. package/dist/shared.d.ts +1 -2
  188. package/dist/shared.d.ts.map +1 -1
  189. package/dist/shared.js +9 -15
  190. package/dist/shown.d.ts +31 -0
  191. package/dist/shown.d.ts.map +1 -0
  192. package/dist/shown.js +295 -0
  193. package/dist/socket/admission.d.ts +7 -0
  194. package/dist/socket/admission.d.ts.map +1 -0
  195. package/dist/socket/admission.js +39 -0
  196. package/dist/socket/answers.d.ts +6 -0
  197. package/dist/socket/answers.d.ts.map +1 -0
  198. package/dist/socket/answers.js +20 -0
  199. package/dist/socket/lanes.d.ts +20 -0
  200. package/dist/socket/lanes.d.ts.map +1 -0
  201. package/dist/socket/lanes.js +77 -0
  202. package/dist/socket/reads.d.ts +10 -0
  203. package/dist/socket/reads.d.ts.map +1 -0
  204. package/dist/socket/reads.js +31 -0
  205. package/dist/socket/rooms.d.ts +44 -0
  206. package/dist/socket/rooms.d.ts.map +1 -0
  207. package/dist/socket/rooms.js +90 -0
  208. package/dist/socket/session.d.ts +39 -0
  209. package/dist/socket/session.d.ts.map +1 -0
  210. package/dist/socket/session.js +23 -0
  211. package/dist/socket.d.ts +70 -0
  212. package/dist/socket.d.ts.map +1 -0
  213. package/dist/socket.js +175 -0
  214. package/dist/source.d.ts +61 -0
  215. package/dist/source.d.ts.map +1 -0
  216. package/dist/source.js +81 -0
  217. package/dist/status.d.ts +0 -6
  218. package/dist/status.d.ts.map +1 -1
  219. package/dist/status.js +0 -6
  220. package/dist/store/fences.d.ts +16 -0
  221. package/dist/store/fences.d.ts.map +1 -0
  222. package/dist/store/fences.js +26 -0
  223. package/dist/store/installs.d.ts +30 -0
  224. package/dist/store/installs.d.ts.map +1 -0
  225. package/dist/store/installs.js +34 -0
  226. package/dist/store/readiness.d.ts +10 -0
  227. package/dist/store/readiness.d.ts.map +1 -0
  228. package/dist/store/readiness.js +52 -0
  229. package/dist/store/records.d.ts +212 -0
  230. package/dist/store/records.d.ts.map +1 -0
  231. package/dist/store/records.js +431 -0
  232. package/dist/store/room-held.d.ts +8 -0
  233. package/dist/store/room-held.d.ts.map +1 -0
  234. package/dist/store/room-held.js +17 -0
  235. package/dist/store/space.d.ts +11 -0
  236. package/dist/store/space.d.ts.map +1 -0
  237. package/dist/store/space.js +43 -0
  238. package/dist/store/watchers.d.ts +17 -0
  239. package/dist/store/watchers.d.ts.map +1 -0
  240. package/dist/store/watchers.js +57 -0
  241. package/dist/store/writes.d.ts +24 -0
  242. package/dist/store/writes.d.ts.map +1 -0
  243. package/dist/store/writes.js +125 -0
  244. package/dist/store.d.ts +107 -157
  245. package/dist/store.d.ts.map +1 -1
  246. package/dist/store.js +143 -395
  247. package/dist/stored.d.ts +58 -37
  248. package/dist/stored.d.ts.map +1 -1
  249. package/dist/stored.js +276 -207
  250. package/dist/surface.d.ts +88 -0
  251. package/dist/surface.d.ts.map +1 -0
  252. package/dist/surface.js +223 -0
  253. package/dist/sync.d.ts +19 -0
  254. package/dist/sync.d.ts.map +1 -0
  255. package/dist/sync.js +532 -0
  256. package/dist/tables.d.ts +120 -55
  257. package/dist/tables.d.ts.map +1 -1
  258. package/dist/tables.js +212 -93
  259. package/dist/tallies.d.ts +18 -0
  260. package/dist/tallies.d.ts.map +1 -0
  261. package/dist/tallies.js +129 -0
  262. package/dist/transfer.d.ts +0 -5
  263. package/dist/transfer.d.ts.map +1 -1
  264. package/dist/validation.d.ts +3 -4
  265. package/dist/validation.d.ts.map +1 -1
  266. package/dist/validation.js +42 -100
  267. package/dist/windows.d.ts +1 -12
  268. package/dist/windows.d.ts.map +1 -1
  269. package/dist/windows.js +2 -75
  270. package/dist/wire.d.ts +17 -5
  271. package/dist/wire.d.ts.map +1 -1
  272. package/dist/wire.js +7 -1
  273. package/package.json +2 -2
  274. package/dist/fence.d.ts +0 -6
  275. package/dist/fence.d.ts.map +0 -1
  276. package/dist/fence.js +0 -10
  277. package/dist/fulltext.d.ts +0 -27
  278. package/dist/fulltext.d.ts.map +0 -1
  279. package/dist/fulltext.js +0 -370
  280. package/dist/words.d.ts +0 -19
  281. package/dist/words.d.ts.map +0 -1
  282. package/dist/words.js +0 -156
package/dist/tables.js CHANGED
@@ -1,11 +1,17 @@
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, folders, opened, rederive, referrers, refreshing, REPLICA_TABLES, stage, 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 { keptRead, READ_TABLES, readsOfOtherBuildsForgotten, storedRead, } from './reads.js';
5
+ import { conversationTallies, roomTallies, } from './tallies.js';
6
+ 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
7
  const STATEMENTS_HELD = 256;
6
8
  export function sqliteConnection(engine, database) {
7
9
  const decoder = new TextDecoder();
8
10
  const held = new Map();
11
+ database.createFunction(sql.HOLDS_PHRASE, sql.holdsPhrase, {
12
+ arity: 2,
13
+ deterministic: true,
14
+ });
9
15
  function prepared(sql) {
10
16
  if (sql.includes(';'))
11
17
  return null;
@@ -72,7 +78,7 @@ const TABLES = [
72
78
  ['authored_queues', 'locator TEXT PRIMARY KEY, ids TEXT NOT NULL'],
73
79
  [
74
80
  '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)',
81
+ '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
82
  ],
77
83
  [
78
84
  'authored_room_drafts',
@@ -89,38 +95,53 @@ export const RUNTIME_TABLES = [
89
95
  'CREATE INDEX opened_files_name ON opened_files (locator, path, revision)',
90
96
  ...REPLICA_TABLES,
91
97
  ...MESSAGE_TABLES,
92
- ...SEARCH_TABLES,
98
+ ...READ_TABLES,
93
99
  ];
94
100
  const OWNERS = ['authored_pending', 'authored_room_drafts', 'authored_calls'];
95
101
  const AUTHORED_KEYS = OWNERS.map((table) => `SELECT value FROM ${table}, json_each(${table}.files)`).join(' UNION ');
96
102
  const UNOWNED_PARTS = `DELETE FROM file_parts WHERE key NOT IN (${AUTHORED_KEYS} UNION SELECT key FROM opened_files)`;
103
+ const CLEANABLE_BYTES = `SELECT coalesce(sum(length(bytes)), 0) AS bytes FROM file_parts WHERE key NOT IN (${AUTHORED_KEYS})`;
97
104
  const READS = new Set([
98
- 'authored.queues',
105
+ 'authored.status',
106
+ 'authored.moving',
107
+ 'authored.pending',
108
+ 'authored.batch',
109
+ 'authored.order',
110
+ 'authored.queued',
99
111
  'authored.roomDrafts',
112
+ 'authored.chatDraft.read',
100
113
  'authored.chatDrafts',
101
114
  'authored.calls',
102
115
  'authored.fences',
116
+ 'authored.unsent',
103
117
  'authored.fact',
104
118
  'authored.file',
105
119
  'replica.open',
120
+ 'replica.rooms',
121
+ 'replica.room',
122
+ 'replica.covering',
123
+ 'replica.offline',
124
+ 'replica.held',
125
+ 'replica.facts',
126
+ 'replica.presentation',
106
127
  'replica.query',
128
+ 'replica.tallies',
107
129
  'replica.entries',
108
130
  'replica.typed',
109
131
  'replica.referrers',
110
- 'replica.walk',
111
- 'replica.files',
112
132
  'replica.folders',
113
133
  'replica.typedWithin',
114
- 'replica.counts',
115
- 'replica.search',
116
- 'replica.received',
117
- 'replica.receivedParts',
118
134
  'opened.find',
135
+ 'opened.held',
119
136
  'file.part',
120
137
  'conversation.held',
121
138
  'conversation.window',
122
139
  'conversation.count',
123
140
  'conversation.turn',
141
+ 'conversation.rows',
142
+ 'conversation.jobs',
143
+ 'conversation.tallies',
144
+ 'read.stored',
124
145
  ]);
125
146
  const KINDS = new Set([
126
147
  ...READS,
@@ -132,18 +153,19 @@ const KINDS = new Set([
132
153
  'authored.claim',
133
154
  'authored.end',
134
155
  'replica.stage',
156
+ 'replica.resume',
135
157
  'replica.refresh',
136
158
  'replica.unrefresh',
137
159
  'replica.commit',
138
160
  'replica.fence',
139
161
  'replica.erase',
140
162
  'replica.abandon',
163
+ 'replica.unfence',
141
164
  'replica.release',
142
165
  'replica.rederive',
166
+ 'replica.demote',
143
167
  'replica.fact',
144
168
  'replica.present',
145
- 'replica.receive',
146
- 'replica.unreceive',
147
169
  'opened.begin',
148
170
  'opened.part',
149
171
  'opened.keep',
@@ -152,6 +174,8 @@ const KINDS = new Set([
152
174
  'conversation.keep',
153
175
  'conversation.drop',
154
176
  'space.probe',
177
+ 'read.keep',
178
+ 'read.builds',
155
179
  ]);
156
180
  export function isRuntime(request) {
157
181
  return KINDS.has(request.kind);
@@ -224,29 +248,71 @@ function unowned(connection) {
224
248
  connection.run(UNOWNED_PARTS);
225
249
  }
226
250
  function queued(connection) {
251
+ return connection.rows('SELECT locator, ids FROM authored_queues').map((row) => ({
252
+ locator: text(row.locator),
253
+ ids: JSON.parse(text(row.ids)),
254
+ }));
255
+ }
256
+ function heldWork(connection, locator) {
257
+ const room = heldRoom(connection, locator);
258
+ const pending = new Map(connection
259
+ .rows('SELECT id, batch, state FROM authored_pending WHERE locator = ?', [locator])
260
+ .map((row) => [
261
+ text(row.id),
262
+ { id: text(row.id), batch: text(row.batch), state: text(row.state) },
263
+ ]));
227
264
  return {
228
- queues: connection.rows('SELECT locator, ids FROM authored_queues').map((row) => ({
229
- locator: text(row.locator),
230
- ids: JSON.parse(text(row.ids)),
231
- })),
232
- pending: connection
233
- .rows('SELECT locator, id, batch, state FROM authored_pending')
234
- .map((row) => ({
235
- locator: text(row.locator),
236
- id: text(row.id),
237
- batch: text(row.batch),
238
- state: text(row.state),
239
- })),
265
+ room: room === null ? null : { ...room, overlaid: overlaid(connection, room.key) },
266
+ queue: heldOrder(connection, locator).map((id) => {
267
+ const held = pending.get(id);
268
+ if (held === undefined)
269
+ throw new Error(`Missing Pending Change ${locator}/${id}`);
270
+ return held;
271
+ }),
240
272
  };
241
273
  }
274
+ function status(connection) {
275
+ const [completed] = connection.rows("SELECT value FROM replica_facts WHERE name = 'lastCompleted'");
276
+ return {
277
+ queued: texts(connection.rows('SELECT locator FROM authored_queues'), 'locator'),
278
+ lastCompleted: completed === undefined ? null : text(completed.value),
279
+ changes: connection
280
+ .rows('SELECT p.locator, c.value AS path FROM authored_pending p, json_each(p.paths) c')
281
+ .map((row) => ({ locator: text(row.locator), path: text(row.path) })),
282
+ resets: connection
283
+ .rows(`SELECT locator, json_extract(state, '$.submission.reset') AS reset FROM authored_pending
284
+ WHERE json_extract(state, '$.submission.state') = 'reset'`)
285
+ .map((row) => ({ locator: text(row.locator), reset: text(row.reset) })),
286
+ returned: texts(connection.rows(`SELECT record FROM authored_room_drafts WHERE json_type(record, '$.returned') = 'object'`), 'record'),
287
+ };
288
+ }
289
+ function pendingOf(connection, locator, id) {
290
+ const [held] = connection.rows('SELECT state FROM authored_pending WHERE locator = ? AND id = ?', [locator, id]);
291
+ return {
292
+ state: held === undefined ? null : text(held.state),
293
+ room: connection.rows('SELECT 1 FROM replica_rooms WHERE locator = ?', [locator]).length > 0,
294
+ };
295
+ }
296
+ function roomDraftRecords(connection, locator, paths) {
297
+ if (locator === null) {
298
+ return texts(connection.rows('SELECT record FROM authored_room_drafts'), 'record');
299
+ }
300
+ if (paths === null) {
301
+ return texts(connection.rows('SELECT record FROM authored_room_drafts WHERE locator = ?', [locator]), 'record');
302
+ }
303
+ 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');
304
+ }
305
+ function chatDraftRecord(connection, conversation) {
306
+ const [held] = connection.rows('SELECT record FROM authored_chat_drafts WHERE conversation = ?', [
307
+ conversation,
308
+ ]);
309
+ return held === undefined ? null : text(held.record);
310
+ }
242
311
  function heldOrder(connection, locator) {
243
312
  const [held] = connection.rows('SELECT ids FROM authored_queues WHERE locator = ?', [locator]);
244
313
  return held === undefined ? [] : JSON.parse(text(held.ids));
245
314
  }
246
- function keptRows(connection, { locator, order, added, founded, roomDrafts, consumedRoomDrafts, amendments }) {
247
- for (const { id, amendment } of amendments ?? []) {
248
- amended(connection, { kind: 'authored.amend', locator, id, amendment });
249
- }
315
+ function keptRows(connection, { locator, order, added, founded, roomDrafts, consumedRoomDrafts }) {
250
316
  for (const path of consumedRoomDrafts ?? []) {
251
317
  connection.run('DELETE FROM authored_room_drafts WHERE locator = ? AND path = ?', [
252
318
  locator,
@@ -273,19 +339,26 @@ function keptRows(connection, { locator, order, added, founded, roomDrafts, cons
273
339
  connection.run('DELETE FROM authored_pending WHERE locator = ? AND id = ?', [locator, id]);
274
340
  }
275
341
  }
276
- for (const { id, batch, state, files } of added ?? []) {
342
+ for (const { id, batch, state, files, paths, moving } of added ?? []) {
277
343
  if (before.has(id) || !after.has(id))
278
344
  continue;
279
- connection.run('INSERT OR REPLACE INTO authored_pending (locator, id, batch, state, files) VALUES (?, ?, ?, ?, ?)', [locator, id, batch, state, owned(files)]);
345
+ 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]);
280
346
  }
281
- for (const { id, batch, files } of founded ?? []) {
347
+ for (const { id, batch, files, paths, moving } of founded ?? []) {
282
348
  const [held] = connection.rows('SELECT files FROM authored_pending WHERE locator = ? AND id = ?', [locator, id]);
283
349
  if (held === undefined || !before.has(id))
284
350
  continue;
285
- connection.run('UPDATE authored_pending SET batch = ?, files = ? WHERE locator = ? AND id = ?', [batch, owned([...JSON.parse(text(held.files)), ...files]), locator, id]);
351
+ connection.run('UPDATE authored_pending SET batch = ?, files = ?, paths = ?, moving = ? WHERE locator = ? AND id = ?', [
352
+ batch,
353
+ owned([...JSON.parse(text(held.files)), ...files]),
354
+ JSON.stringify(paths),
355
+ moving ? 1 : 0,
356
+ locator,
357
+ id,
358
+ ]);
286
359
  }
287
360
  }
288
- async function kept(connection, { writes, files, whole, discards }) {
361
+ async function kept(connection, { writes, files, whole }) {
289
362
  const refused = writes.find(({ locator }) => fenced(connection, locator));
290
363
  if (refused !== undefined && whole)
291
364
  return refused.locator;
@@ -294,8 +367,6 @@ async function kept(connection, { writes, files, whole, discards }) {
294
367
  if (!fenced(connection, write.locator))
295
368
  keptRows(connection, write);
296
369
  }
297
- for (const locator of discards ?? [])
298
- roomWork(connection, locator);
299
370
  unowned(connection);
300
371
  return refused?.locator ?? null;
301
372
  }
@@ -365,11 +436,15 @@ function fence(connection, locators) {
365
436
  return fenceRooms(connection, locators);
366
437
  }
367
438
  function erased(connection, { locator, limit }) {
368
- const done = erase(connection, locator, limit);
369
- if (locator !== null && !done.remaining) {
439
+ return erase(connection, locator, limit);
440
+ }
441
+ function unfenced(connection, locators) {
442
+ const erased = locators.filter((locator) => connection.rows('SELECT 1 FROM replica_erasing WHERE locator = ? LIMIT 1', [locator])
443
+ .length === 0);
444
+ for (const locator of erased) {
370
445
  connection.run('DELETE FROM authored_fences WHERE locator = ?', [locator]);
371
446
  }
372
- return done;
447
+ return erased;
373
448
  }
374
449
  function claim(connection, holder) {
375
450
  const held = fact(connection, 'holder');
@@ -448,56 +523,82 @@ function keptOpened(connection, { key, sha256, byteLength, parts }) {
448
523
  unowned(connection);
449
524
  return true;
450
525
  }
526
+ const UNSENT = [
527
+ ['pending-change', 'authored_pending'],
528
+ ['call', 'authored_calls'],
529
+ ['room-draft', 'authored_room_drafts'],
530
+ ['chat-draft', 'authored_chat_drafts'],
531
+ ];
532
+ function unsent(connection) {
533
+ const [held] = connection.rows(`SELECT ${UNSENT.map(([kind, table]) => `(SELECT count(*) FROM ${table}) AS "${kind}"`).join(', ')}`);
534
+ return UNSENT.map(([kind]) => ({ kind, count: count(held?.[kind]) })).filter((work) => work.count > 0);
535
+ }
536
+ function cleanable(connection) {
537
+ const [held] = connection.rows(CLEANABLE_BYTES);
538
+ return count(held?.bytes);
539
+ }
451
540
  function cleaned(connection) {
452
541
  connection.run('DELETE FROM opened_files');
453
- const [freed] = connection.rows(`SELECT coalesce(sum(length(bytes)), 0) AS bytes FROM file_parts WHERE key NOT IN (${AUTHORED_KEYS})`);
542
+ const freed = cleanable(connection);
454
543
  unowned(connection);
455
- return count(freed?.bytes);
544
+ return freed;
545
+ }
546
+ function openedHeld(connection) {
547
+ return {
548
+ rooms: connection
549
+ .rows('SELECT locator, count(*) AS files, sum(byte_length) AS bytes FROM opened_files WHERE byte_length IS NOT NULL GROUP BY locator ORDER BY locator')
550
+ .map((row) => ({ room: text(row.locator), count: count(row.files), size: count(row.bytes) })),
551
+ cleanable: cleanable(connection),
552
+ };
456
553
  }
457
554
  async function committed(connection, { authored, rooms }) {
458
555
  const refused = authored === undefined ? null : await kept(connection, authored);
459
556
  if (refused !== null && authored?.whole === true) {
460
- return { fenced: refused, counts: {}, changed: {}, indexed: {} };
557
+ return { fenced: refused, changed: {}, indexed: {} };
461
558
  }
462
559
  return {
463
560
  fenced: refused,
464
561
  ...commit(connection, rooms.filter(({ locator }) => !fenced(connection, locator))),
465
562
  };
466
563
  }
467
- function receivedParts(connection, key, count) {
468
- return connection
469
- .rows('SELECT value FROM replica_received_parts WHERE key = ? AND part < ? ORDER BY part', [
470
- key,
471
- count,
472
- ])
473
- .map((row) => JSON.parse(text(row.value)));
474
- }
475
- function receive(connection, { key, head, part }) {
476
- connection.run('INSERT OR REPLACE INTO replica_received (key, head) VALUES (?, ?)', [key, head]);
477
- if (part !== undefined) {
478
- connection.run('INSERT OR REPLACE INTO replica_received_parts (key, part, value) VALUES (?, ?, ?)', [key, part.index, part.value]);
479
- }
480
- return null;
481
- }
482
- function unreceive(connection, keys) {
483
- for (const key of keys) {
484
- connection.run('DELETE FROM replica_received WHERE key = ?', [key]);
485
- connection.run('DELETE FROM replica_received_parts WHERE key = ?', [key]);
486
- }
487
- return null;
488
- }
489
564
  export function answerRuntime(connection, request, spool) {
490
565
  switch (request.kind) {
491
- case 'authored.queues':
492
- return queued(connection);
566
+ case 'authored.status':
567
+ return status(connection);
568
+ case 'authored.moving':
569
+ return {
570
+ queues: queued(connection),
571
+ pending: connection
572
+ .rows('SELECT locator, id, batch, state FROM authored_pending WHERE moving = 1 ORDER BY locator')
573
+ .map((row) => ({
574
+ locator: text(row.locator),
575
+ id: text(row.id),
576
+ batch: text(row.batch),
577
+ state: text(row.state),
578
+ })),
579
+ };
580
+ case 'authored.pending':
581
+ return pendingOf(connection, request.locator, request.id);
582
+ case 'authored.batch': {
583
+ const [held] = connection.rows('SELECT batch FROM authored_pending WHERE locator = ? AND id = ?', [request.locator, request.id]);
584
+ return held === undefined ? null : text(held.batch);
585
+ }
586
+ case 'authored.order':
587
+ return heldOrder(connection, request.locator);
588
+ case 'authored.queued':
589
+ return texts(connection.rows('SELECT locator FROM authored_queues'), 'locator');
493
590
  case 'authored.roomDrafts':
494
- return texts(connection.rows('SELECT record FROM authored_room_drafts'), 'record');
591
+ return roomDraftRecords(connection, request.locator, request.paths);
592
+ case 'authored.chatDraft.read':
593
+ return chatDraftRecord(connection, request.conversation);
495
594
  case 'authored.chatDrafts':
496
595
  return texts(connection.rows('SELECT record FROM authored_chat_drafts'), 'record');
497
596
  case 'authored.calls':
498
597
  return texts(connection.rows('SELECT record FROM authored_calls'), 'record');
499
598
  case 'authored.fences':
500
599
  return texts(connection.rows('SELECT locator FROM authored_fences'), 'locator');
600
+ case 'authored.unsent':
601
+ return unsent(connection);
501
602
  case 'authored.fact':
502
603
  return fact(connection, request.name);
503
604
  case 'authored.file':
@@ -518,37 +619,43 @@ export function answerRuntime(connection, request, spool) {
518
619
  return end(connection, request.holder);
519
620
  case 'replica.open':
520
621
  return opened(connection);
622
+ case 'replica.rooms':
623
+ return heldRooms(connection, request.locators);
624
+ case 'replica.room':
625
+ return heldWork(connection, request.locator);
626
+ case 'replica.covering':
627
+ return {
628
+ rooms: covering(connection),
629
+ queues: queued(connection),
630
+ };
631
+ case 'replica.offline':
632
+ return offlineMode(connection, request.locator);
633
+ case 'replica.held':
634
+ return { held: heldLocators(connection), staged: stagedLocators(connection) };
635
+ case 'replica.demote':
636
+ return demoted(connection);
637
+ case 'replica.facts':
638
+ return facts(connection);
639
+ case 'replica.presentation':
640
+ return presentation(connection, request.app);
521
641
  case 'replica.query':
522
642
  return answerQuery(connection, request.plan, request.rooms, request.budget);
643
+ case 'replica.tallies':
644
+ return roomTallies(connection, request.totals, request.rooms, request.since);
523
645
  case 'replica.entries':
524
646
  return entries(connection, request.key, request.view, request.paths);
525
647
  case 'replica.typed':
526
648
  return typed(connection, request.key, request.view, request.paths);
527
649
  case 'replica.referrers':
528
- return referrers(connection, request.key, request.view, request.targets, request.kinds);
529
- case 'replica.walk':
530
- return walk(connection, request.key, request.view, request.after, request.bytes);
531
- case 'replica.files':
532
- return files(connection, request.key, request.view, request.after, request.limit);
650
+ return referrers(connection, request.key, request.view, request.targets);
533
651
  case 'replica.folders':
534
652
  return folders(connection, request.key, request.view, request.paths);
535
653
  case 'replica.typedWithin':
536
654
  return typedWithin(connection, request.key, request.view, request.roots, request.within);
537
- case 'replica.counts':
538
- return counts(connection, request.rooms);
539
- case 'replica.search': {
540
- const { kind: _kind, ...ask } = request;
541
- return searched(connection, ask);
542
- }
543
- case 'replica.received':
544
- return connection.rows('SELECT key, head FROM replica_received').map((row) => ({
545
- key: text(row.key),
546
- head: JSON.parse(text(row.head)),
547
- }));
548
- case 'replica.receivedParts':
549
- return receivedParts(connection, request.key, request.count);
550
655
  case 'replica.stage':
551
656
  return fenced(connection, request.locator) ? null : stage(connection, request);
657
+ case 'replica.resume':
658
+ return fenced(connection, request.locator) ? null : resume(connection, request);
552
659
  case 'replica.refresh':
553
660
  return refreshing(connection, request);
554
661
  case 'replica.unrefresh':
@@ -561,6 +668,8 @@ export function answerRuntime(connection, request, spool) {
561
668
  return erased(connection, request);
562
669
  case 'replica.abandon':
563
670
  return abandon(connection, request.locator);
671
+ case 'replica.unfence':
672
+ return unfenced(connection, request.locators);
564
673
  case 'replica.release':
565
674
  return release(connection, request.key);
566
675
  case 'replica.rederive':
@@ -577,10 +686,6 @@ export function answerRuntime(connection, request, spool) {
577
686
  request.value,
578
687
  ]);
579
688
  return null;
580
- case 'replica.receive':
581
- return receive(connection, request);
582
- case 'replica.unreceive':
583
- return unreceive(connection, request.keys);
584
689
  case 'opened.find':
585
690
  return openedFile(connection, request);
586
691
  case 'opened.begin':
@@ -593,20 +698,28 @@ export function answerRuntime(connection, request, spool) {
593
698
  return dropped(connection, request.key);
594
699
  case 'opened.clean':
595
700
  return cleaned(connection);
701
+ case 'opened.held':
702
+ return openedHeld(connection);
596
703
  case 'file.part':
597
704
  return filePart(connection, request.key, request.part);
598
705
  case 'conversation.held':
599
- return heldConversations(connection);
706
+ return heldConversations(connection, request.conversations);
600
707
  case 'conversation.keep': {
601
708
  const { kind: _kind, ...write } = request;
602
709
  return keptMessages(connection, write);
603
710
  }
604
711
  case 'conversation.window':
605
- return messageWindow(connection, request.conversation, request.limit, request.before);
712
+ return messageWindow(connection, request.conversation, request.limit, request.before, request.phrase);
606
713
  case 'conversation.count':
607
- return messageCount(connection, request.conversation);
714
+ return messageCount(connection, request.conversation, request.phrase);
608
715
  case 'conversation.turn':
609
716
  return turnArrived(connection, request.conversation, request.turn);
717
+ case 'conversation.rows':
718
+ return conversationRows(connection, request.conversations);
719
+ case 'conversation.jobs':
720
+ return jobMarkers(connection, request.conversation);
721
+ case 'conversation.tallies':
722
+ return conversationTallies(connection, request.conversations, request.since);
610
723
  case 'conversation.drop':
611
724
  chatDraft(connection, {
612
725
  kind: 'authored.chatDraft',
@@ -617,5 +730,11 @@ export function answerRuntime(connection, request, spool) {
617
730
  return null;
618
731
  case 'space.probe':
619
732
  return spool.probe(PROBE_BYTES).then(() => null);
733
+ case 'read.stored':
734
+ return storedRead(connection, request);
735
+ case 'read.keep':
736
+ return keptRead(connection, request, request.answer);
737
+ case 'read.builds':
738
+ return readsOfOtherBuildsForgotten(connection, request.build);
620
739
  }
621
740
  }
@@ -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"}