@agentshouse/mdruntime 0.1.0-cd3486bd400ed → 0.1.0-cd37f80239af0

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 (201) hide show
  1. package/README.md +21 -9
  2. package/dist/adapter.d.ts +149 -46
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/attached-model.d.ts +5 -1
  5. package/dist/attached-model.d.ts.map +1 -1
  6. package/dist/attached-model.js +38 -4
  7. package/dist/attempt.d.ts.map +1 -1
  8. package/dist/attempt.js +14 -1
  9. package/dist/binary.d.ts +18 -0
  10. package/dist/binary.d.ts.map +1 -0
  11. package/dist/binary.js +8 -0
  12. package/dist/bootstrap.d.ts +27 -11
  13. package/dist/bootstrap.d.ts.map +1 -1
  14. package/dist/bootstrap.js +184 -84
  15. package/dist/chain.d.ts +11 -0
  16. package/dist/chain.d.ts.map +1 -0
  17. package/dist/chain.js +64 -0
  18. package/dist/change-set.d.ts +9 -14
  19. package/dist/change-set.d.ts.map +1 -1
  20. package/dist/change-set.js +11 -62
  21. package/dist/change.d.ts +41 -0
  22. package/dist/change.d.ts.map +1 -1
  23. package/dist/change.js +34 -0
  24. package/dist/client.d.ts +31 -13
  25. package/dist/client.d.ts.map +1 -1
  26. package/dist/client.js +103 -7
  27. package/dist/collection.d.ts +10 -7
  28. package/dist/collection.d.ts.map +1 -1
  29. package/dist/collection.js +38 -79
  30. package/dist/compile.d.ts +16 -0
  31. package/dist/compile.d.ts.map +1 -0
  32. package/dist/compile.js +786 -0
  33. package/dist/connection.d.ts +50 -16
  34. package/dist/connection.d.ts.map +1 -1
  35. package/dist/connection.js +3786 -931
  36. package/dist/conversation.d.ts +37 -16
  37. package/dist/conversation.d.ts.map +1 -1
  38. package/dist/conversation.js +700 -257
  39. package/dist/digest.d.ts +7 -0
  40. package/dist/digest.d.ts.map +1 -0
  41. package/dist/digest.js +113 -0
  42. package/dist/durability.d.ts +4 -1
  43. package/dist/durability.d.ts.map +1 -1
  44. package/dist/durability.js +5 -0
  45. package/dist/entry.d.ts +20 -0
  46. package/dist/entry.d.ts.map +1 -0
  47. package/dist/entry.js +151 -0
  48. package/dist/fence.d.ts +6 -0
  49. package/dist/fence.d.ts.map +1 -0
  50. package/dist/fence.js +10 -0
  51. package/dist/file.d.ts +2 -19
  52. package/dist/file.d.ts.map +1 -1
  53. package/dist/file.js +7 -122
  54. package/dist/fulltext.d.ts +27 -0
  55. package/dist/fulltext.d.ts.map +1 -0
  56. package/dist/fulltext.js +370 -0
  57. package/dist/host.d.ts +13 -3
  58. package/dist/host.d.ts.map +1 -1
  59. package/dist/host.js +130 -17
  60. package/dist/identity.d.ts +2 -0
  61. package/dist/identity.d.ts.map +1 -1
  62. package/dist/identity.js +6 -0
  63. package/dist/index.d.ts +17 -9
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +7 -0
  66. package/dist/live.d.ts +22 -0
  67. package/dist/live.d.ts.map +1 -0
  68. package/dist/live.js +123 -0
  69. package/dist/media.d.ts +8 -0
  70. package/dist/media.d.ts.map +1 -0
  71. package/dist/media.js +218 -0
  72. package/dist/memory.d.ts +17 -7
  73. package/dist/memory.d.ts.map +1 -1
  74. package/dist/memory.js +231 -91
  75. package/dist/messages.d.ts +28 -0
  76. package/dist/messages.d.ts.map +1 -0
  77. package/dist/messages.js +90 -0
  78. package/dist/model.d.ts +9 -2
  79. package/dist/model.d.ts.map +1 -1
  80. package/dist/model.js +8 -2
  81. package/dist/name.d.ts +4 -0
  82. package/dist/name.d.ts.map +1 -0
  83. package/dist/name.js +122 -0
  84. package/dist/observation.d.ts +20 -2
  85. package/dist/observation.d.ts.map +1 -1
  86. package/dist/observation.js +113 -12
  87. package/dist/opened.d.ts +22 -0
  88. package/dist/opened.d.ts.map +1 -0
  89. package/dist/opened.js +107 -0
  90. package/dist/outcome.d.ts +52 -4
  91. package/dist/outcome.d.ts.map +1 -1
  92. package/dist/outcome.js +46 -0
  93. package/dist/overlay.d.ts +15 -5
  94. package/dist/overlay.d.ts.map +1 -1
  95. package/dist/overlay.js +129 -177
  96. package/dist/paths.d.ts +1 -0
  97. package/dist/paths.d.ts.map +1 -1
  98. package/dist/paths.js +4 -0
  99. package/dist/query.d.ts +68 -4
  100. package/dist/query.d.ts.map +1 -1
  101. package/dist/query.js +48 -7
  102. package/dist/reach.d.ts +18 -4
  103. package/dist/reach.d.ts.map +1 -1
  104. package/dist/reach.js +224 -55
  105. package/dist/react.d.ts +9 -6
  106. package/dist/react.d.ts.map +1 -1
  107. package/dist/react.js +7 -17
  108. package/dist/readiness.d.ts +52 -0
  109. package/dist/readiness.d.ts.map +1 -0
  110. package/dist/readiness.js +27 -0
  111. package/dist/recovery.d.ts +26 -0
  112. package/dist/recovery.d.ts.map +1 -0
  113. package/dist/recovery.js +224 -0
  114. package/dist/refresh.d.ts +17 -14
  115. package/dist/refresh.d.ts.map +1 -1
  116. package/dist/refresh.js +136 -56
  117. package/dist/replica.d.ts +58 -33
  118. package/dist/replica.d.ts.map +1 -1
  119. package/dist/replica.js +307 -159
  120. package/dist/room-draft.d.ts +51 -4
  121. package/dist/room-draft.d.ts.map +1 -1
  122. package/dist/room-draft.js +210 -18
  123. package/dist/rows.d.ts +37 -35
  124. package/dist/rows.d.ts.map +1 -1
  125. package/dist/rows.js +173 -102
  126. package/dist/save.d.ts +70 -3
  127. package/dist/save.d.ts.map +1 -1
  128. package/dist/save.js +805 -147
  129. package/dist/search.d.ts +39 -28
  130. package/dist/search.d.ts.map +1 -1
  131. package/dist/search.js +37 -105
  132. package/dist/seed.d.ts +16 -0
  133. package/dist/seed.d.ts.map +1 -0
  134. package/dist/seed.js +75 -0
  135. package/dist/shared.d.ts +15 -0
  136. package/dist/shared.d.ts.map +1 -0
  137. package/dist/shared.js +421 -0
  138. package/dist/status.d.ts +5 -0
  139. package/dist/status.d.ts.map +1 -1
  140. package/dist/status.js +3 -1
  141. package/dist/store.d.ts +107 -80
  142. package/dist/store.d.ts.map +1 -1
  143. package/dist/store.js +325 -461
  144. package/dist/stored.d.ts +130 -0
  145. package/dist/stored.d.ts.map +1 -0
  146. package/dist/stored.js +560 -0
  147. package/dist/tables.d.ts +325 -0
  148. package/dist/tables.d.ts.map +1 -0
  149. package/dist/tables.js +578 -0
  150. package/dist/transfer.d.ts +0 -5
  151. package/dist/transfer.d.ts.map +1 -1
  152. package/dist/turn.d.ts +9 -0
  153. package/dist/turn.d.ts.map +1 -0
  154. package/dist/turn.js +75 -0
  155. package/dist/validation.d.ts +29 -0
  156. package/dist/validation.d.ts.map +1 -0
  157. package/dist/validation.js +101 -0
  158. package/dist/windows.d.ts +22 -0
  159. package/dist/windows.d.ts.map +1 -0
  160. package/dist/windows.js +89 -0
  161. package/dist/wire.d.ts +165 -30
  162. package/dist/wire.d.ts.map +1 -1
  163. package/dist/wire.js +56 -29
  164. package/dist/words.d.ts +19 -0
  165. package/dist/words.d.ts.map +1 -0
  166. package/dist/words.js +156 -0
  167. package/package.json +26 -15
  168. package/AGENTS.md +0 -290
  169. package/dist/attachment.d.ts +0 -3
  170. package/dist/attachment.d.ts.map +0 -1
  171. package/dist/attachment.js +0 -8
  172. package/dist/chat/acts.d.ts +0 -34
  173. package/dist/chat/acts.d.ts.map +0 -1
  174. package/dist/chat/acts.js +0 -36
  175. package/dist/chat/messages.d.ts +0 -21
  176. package/dist/chat/messages.d.ts.map +0 -1
  177. package/dist/chat/messages.js +0 -151
  178. package/dist/chat/overlay.d.ts +0 -31
  179. package/dist/chat/overlay.d.ts.map +0 -1
  180. package/dist/chat/overlay.js +0 -97
  181. package/dist/chat/parts.d.ts +0 -57
  182. package/dist/chat/parts.d.ts.map +0 -1
  183. package/dist/chat/parts.js +0 -119
  184. package/dist/chat/process.d.ts +0 -20
  185. package/dist/chat/process.d.ts.map +0 -1
  186. package/dist/chat/process.js +0 -32
  187. package/dist/chat/provider.d.ts +0 -57
  188. package/dist/chat/provider.d.ts.map +0 -1
  189. package/dist/chat/provider.js +0 -637
  190. package/dist/chat/questions.d.ts +0 -31
  191. package/dist/chat/questions.d.ts.map +0 -1
  192. package/dist/chat/questions.js +0 -111
  193. package/dist/chat/read.d.ts +0 -5
  194. package/dist/chat/read.d.ts.map +0 -1
  195. package/dist/chat/read.js +0 -13
  196. package/dist/chat.d.ts +0 -7
  197. package/dist/chat.d.ts.map +0 -1
  198. package/dist/chat.js +0 -2
  199. package/dist/read-side.d.ts +0 -18
  200. package/dist/read-side.d.ts.map +0 -1
  201. package/dist/read-side.js +0 -433
@@ -0,0 +1,370 @@
1
+ import { parse } from '@agentshouse/mdmodel';
2
+ import { selected } from './compile.js';
3
+ import { markdownPath } from './rows.js';
4
+ import { indexed, matchClauses, matched, matchExpression, queryTerms, snippet, } from './words.js';
5
+ const TOKENIZER = "porter unicode61 remove_diacritics 2 categories 'L* N* Co M* S*' separators '$+<=>^`|~'";
6
+ const CONTENTLESS = `content='', contentless_delete=1, detail=full, tokenize="${TOKENIZER}"`;
7
+ const DOCUMENTS = 'replica_search';
8
+ const MESSAGES = 'conversation_search';
9
+ export const SEARCH_TABLES = [
10
+ `CREATE VIRTUAL TABLE ${DOCUMENTS} USING fts5(path, fields, sections, preamble, ${CONTENTLESS})`,
11
+ `CREATE VIRTUAL TABLE ${MESSAGES} USING fts5(text, ${CONTENTLESS})`,
12
+ ];
13
+ const CHUNKS = 4096;
14
+ const CHUNK_CHARACTERS = 32_768;
15
+ const CANDIDATES = 5_000;
16
+ const LEAST_WINDOW = 32;
17
+ const SNIPPET_REACH = 80;
18
+ function scalars(value) {
19
+ if (value === null || value === undefined)
20
+ return [];
21
+ if (Array.isArray(value))
22
+ return value.flatMap(scalars);
23
+ if (typeof value === 'object')
24
+ return Object.values(value).flatMap(scalars);
25
+ return [String(value)];
26
+ }
27
+ function partsOf(path, content) {
28
+ if (!markdownPath(path))
29
+ return null;
30
+ const read = parse(content, path);
31
+ if (!read.ok)
32
+ return null;
33
+ return {
34
+ fields: scalars(read.document.fields).join('\n'),
35
+ sections: Object.entries(read.document.sections)
36
+ .map(([heading, text]) => `${heading}\n${text}`)
37
+ .join('\n'),
38
+ preamble: read.document.preamble,
39
+ path,
40
+ };
41
+ }
42
+ function chunked(text) {
43
+ const chunks = [];
44
+ let start = 0;
45
+ do {
46
+ let end = Math.min(text.length, start + CHUNK_CHARACTERS);
47
+ if (end < text.length) {
48
+ const space = text.lastIndexOf(' ', end);
49
+ if (space > start + CHUNK_CHARACTERS / 2)
50
+ end = space;
51
+ else if (/[\ud800-\udbff]/u.test(text[end - 1]))
52
+ end -= 1;
53
+ }
54
+ chunks.push(text.slice(start, end));
55
+ start = end;
56
+ } while (start < text.length && chunks.length < CHUNKS);
57
+ return chunks;
58
+ }
59
+ function rowOf(id, chunk) {
60
+ return id * CHUNKS + chunk;
61
+ }
62
+ export function indexDocument(connection, id, path, content) {
63
+ const parts = partsOf(path, content);
64
+ if (parts === null)
65
+ return;
66
+ chunked(parts.sections).forEach((sections, chunk) => {
67
+ const head = chunk === 0;
68
+ connection.run(`INSERT INTO ${DOCUMENTS} (rowid, path, fields, sections, preamble) VALUES (?, ?, ?, ?, ?)`, [
69
+ rowOf(id, chunk),
70
+ head ? indexed(parts.path) : null,
71
+ head ? indexed(parts.fields) : null,
72
+ indexed(sections),
73
+ head ? indexed(parts.preamble) : null,
74
+ ]);
75
+ });
76
+ }
77
+ export function indexMessage(connection, id, text) {
78
+ chunked(text).forEach((chunk, at) => {
79
+ connection.run(`INSERT INTO ${MESSAGES} (rowid, text) VALUES (?, ?)`, [
80
+ rowOf(id, at),
81
+ indexed(chunk),
82
+ ]);
83
+ });
84
+ }
85
+ function unindexed(connection, table, ids) {
86
+ for (const id of ids) {
87
+ connection.run(`DELETE FROM ${table} WHERE rowid >= ? AND rowid < ?`, [
88
+ rowOf(id, 0),
89
+ rowOf(id + 1, 0),
90
+ ]);
91
+ }
92
+ }
93
+ export function unindexDocuments(connection, ids) {
94
+ unindexed(connection, DOCUMENTS, ids);
95
+ }
96
+ export function unindexMessages(connection, ids) {
97
+ unindexed(connection, MESSAGES, ids);
98
+ }
99
+ export function unindexAll(connection) {
100
+ for (const table of [DOCUMENTS, MESSAGES]) {
101
+ connection.run(`INSERT INTO ${table} (${table}) VALUES ('delete-all')`);
102
+ }
103
+ }
104
+ function pair(left, right) {
105
+ if (left[0] !== right[0])
106
+ return left[0] < right[0] ? -1 : 1;
107
+ if (left[1] !== right[1])
108
+ return left[1] < right[1] ? -1 : 1;
109
+ return 0;
110
+ }
111
+ function byRank(left, right) {
112
+ return right.score - left.score || pair(left.tie, right.tie);
113
+ }
114
+ function beyond(hit, cursor) {
115
+ if (cursor.length === 0)
116
+ return true;
117
+ return (hit.score < cursor[0] || (hit.score === cursor[0] && pair(hit.tie, [cursor[1], cursor[2]]) > 0));
118
+ }
119
+ function rankedWithin(connection, listed, sources, cursor, needed) {
120
+ const { table } = listed;
121
+ let window = Math.max(needed * 2, LEAST_WINDOW);
122
+ for (;;) {
123
+ const rows = [];
124
+ const boundaries = [];
125
+ for (const source of sources) {
126
+ const found = connection.rows(`SELECT rowid, bm25(${table}) AS score FROM ${table} WHERE ${table} MATCH ? ${source.sql} ORDER BY score, rowid LIMIT ?`, [source.expression, ...source.bind, window]);
127
+ for (const row of found)
128
+ rows.push([Number(row.rowid), Number(row.score)]);
129
+ if (found.length === window)
130
+ boundaries.push(-Number(found.at(-1).score));
131
+ }
132
+ rows.sort((left, right) => left[1] - right[1] || left[0] - right[0]);
133
+ const best = new Map();
134
+ for (const [row, score] of rows) {
135
+ const id = Math.floor(row / CHUNKS);
136
+ if (!best.has(id))
137
+ best.set(id, { chunk: row % CHUNKS, score: -score });
138
+ }
139
+ const ties = listed.ties([...best.keys()]);
140
+ const kept = [...best]
141
+ .flatMap(([id, held]) => {
142
+ const tie = ties.get(id);
143
+ return tie === undefined ? [] : [{ id, ...held, tie }];
144
+ })
145
+ .sort(byRank)
146
+ .filter((hit) => beyond(hit, cursor))
147
+ .slice(0, needed);
148
+ const last = kept.at(-1);
149
+ if (boundaries.length === 0)
150
+ return kept;
151
+ if (kept.length === needed &&
152
+ last !== undefined &&
153
+ boundaries.every((boundary) => boundary < last.score)) {
154
+ return kept;
155
+ }
156
+ window *= 4;
157
+ }
158
+ }
159
+ function reaches(connection, listed, expression, reach) {
160
+ const { table, scope } = listed;
161
+ const [counted] = connection.rows(`SELECT count(*) AS held FROM (SELECT rowid FROM ${table} WHERE ${table} MATCH ? AND ${scope.sql} LIMIT ?)`, [expression, ...scope.bind, reach + 1]);
162
+ return Number(counted?.held ?? 0) > reach;
163
+ }
164
+ function candidates(connection, listed, clauses, cursor, needed) {
165
+ const { table, scope } = listed;
166
+ const expression = matchExpression(clauses);
167
+ const within = { sql: `AND ${scope.sql}`, bind: scope.bind };
168
+ let reach = CANDIDATES;
169
+ for (;;) {
170
+ const [edge] = connection.rows(`SELECT rowid FROM ${table} WHERE ${table} MATCH ? ${within.sql} ORDER BY rowid LIMIT 1 OFFSET ?`, [expression, ...within.bind, reach]);
171
+ const floor = edge === undefined ? null : Number(edge.rowid);
172
+ const sources = [
173
+ floor === null
174
+ ? { expression, ...within }
175
+ : { expression, sql: `${within.sql} AND rowid < ?`, bind: [...within.bind, floor] },
176
+ ];
177
+ if (floor !== null) {
178
+ const common = new Set();
179
+ const selective = [];
180
+ const frequent = [];
181
+ for (const clause of clauses) {
182
+ if (clause.kind === 'phrase')
183
+ continue;
184
+ if (clause.kind !== 'term' && common.has(clause.term)) {
185
+ frequent.push(clause);
186
+ continue;
187
+ }
188
+ if (reaches(connection, listed, clause.text, reach)) {
189
+ if (clause.kind === 'term')
190
+ common.add(clause.term);
191
+ frequent.push(clause);
192
+ }
193
+ else
194
+ selective.push(clause);
195
+ }
196
+ const phrase = clauses.find((clause) => clause.kind === 'phrase');
197
+ if (phrase !== undefined) {
198
+ const rare = !clauses
199
+ .filter((clause) => clause.kind === 'term')
200
+ .every((clause) => common.has(clause.term)) ||
201
+ !reaches(connection, listed, phrase.text, reach);
202
+ (rare ? selective : frequent).push(phrase);
203
+ }
204
+ if (selective.length > 0) {
205
+ const chosen = `(${matchExpression(selective)})`;
206
+ const tail = { sql: `${within.sql} AND rowid >= ?`, bind: [...within.bind, floor] };
207
+ if (frequent.length === 0)
208
+ sources.push({ ...tail, expression: chosen });
209
+ else {
210
+ const rest = `(${matchExpression(frequent)})`;
211
+ sources.push({ ...tail, expression: `${chosen} AND ${rest}` }, { ...tail, expression: `${chosen} NOT ${rest}` });
212
+ }
213
+ }
214
+ }
215
+ const kept = rankedWithin(connection, listed, sources, cursor, needed);
216
+ if (floor === null || kept.length >= needed)
217
+ return kept;
218
+ reach *= 4;
219
+ }
220
+ }
221
+ function windowOf(connection, listed, clauses, cursor, limit) {
222
+ if (cursor === null || listed === null || clauses === null)
223
+ return { kept: [], cursor: null };
224
+ const found = candidates(connection, listed, clauses, cursor, limit + 1);
225
+ if (found.length <= limit)
226
+ return { kept: found, cursor: null };
227
+ const kept = found.slice(0, limit);
228
+ const last = kept.at(-1);
229
+ return { kept, cursor: last === undefined ? cursor : [last.score, ...last.tie] };
230
+ }
231
+ function totalOf(connection, listed, clauses) {
232
+ if (listed === null || clauses === null)
233
+ return 0;
234
+ const { table, scope } = listed;
235
+ const [counted] = connection.rows(`SELECT count(DISTINCT rowid / ${CHUNKS}) AS held FROM ${table} WHERE ${table} MATCH ? AND ${scope.sql}`, [matchExpression(clauses), ...scope.bind]);
236
+ return Number(counted?.held ?? 0);
237
+ }
238
+ function ids(found) {
239
+ return JSON.stringify(found);
240
+ }
241
+ function documentsListed(connection, documents) {
242
+ if (documents.rooms.length === 0)
243
+ return null;
244
+ const filter = selected(connection, documents.plan, documents.rooms);
245
+ const outside = {
246
+ sql: "NOT EXISTS (SELECT 1 FROM json_each(?) r WHERE d.key = r.value ->> 0 AND (d.path = r.value ->> 1 OR (d.path >= (r.value ->> 1) || '/' AND d.path < (r.value ->> 1) || '0')))",
247
+ bind: [
248
+ JSON.stringify(documents.beneath.flatMap(({ key, roots }) => roots.map((root) => [key, root]))),
249
+ ],
250
+ };
251
+ const conditions = [filter, outside];
252
+ const locators = new Map(documents.rooms.map(({ key, locator }) => [key, locator]));
253
+ return {
254
+ table: DOCUMENTS,
255
+ scope: {
256
+ sql: `EXISTS (SELECT 1 FROM replica_entries d WHERE d.id = ${DOCUMENTS}.rowid / ${CHUNKS} AND d.type IS NOT NULL AND ${conditions.map(({ sql }) => `(${sql})`).join(' AND ')})`,
257
+ bind: conditions.flatMap(({ bind }) => bind),
258
+ },
259
+ ties(found) {
260
+ const tied = new Map();
261
+ for (const row of connection.rows('SELECT id, key, path FROM replica_entries WHERE id IN (SELECT value FROM json_each(?))', [ids(found)])) {
262
+ const locator = locators.get(Number(row.key));
263
+ if (locator !== undefined)
264
+ tied.set(Number(row.id), [locator, String(row.path)]);
265
+ }
266
+ return tied;
267
+ },
268
+ };
269
+ }
270
+ function messagesListed(connection, conversations) {
271
+ if (conversations.length === 0)
272
+ return null;
273
+ return {
274
+ table: MESSAGES,
275
+ scope: {
276
+ sql: `EXISTS (SELECT 1 FROM conversation_messages m WHERE m.id = ${MESSAGES}.rowid / ${CHUNKS} AND m.conversation IN (SELECT value FROM json_each(?)))`,
277
+ bind: [JSON.stringify(conversations)],
278
+ },
279
+ ties(found) {
280
+ return new Map(connection
281
+ .rows('SELECT id, conversation, identity FROM conversation_messages WHERE id IN (SELECT value FROM json_each(?))', [ids(found)])
282
+ .map((row) => [Number(row.id), [String(row.conversation), String(row.identity)]]));
283
+ },
284
+ };
285
+ }
286
+ function cut(candidates, terms) {
287
+ for (const [part, text] of candidates) {
288
+ const found = snippet(text, terms, SNIPPET_REACH);
289
+ if (found !== null)
290
+ return { snippet: { part, text: found.text, at: [...found.at] } };
291
+ }
292
+ return {};
293
+ }
294
+ function documentHit(connection, hit, terms) {
295
+ const [row] = connection.rows('SELECT type, content FROM replica_entries WHERE id = ?', [hit.id]);
296
+ if (row === undefined)
297
+ return null;
298
+ const [room, path] = hit.tie;
299
+ const parts = typeof row.content === 'string' ? partsOf(path, row.content) : null;
300
+ const whole = parts === null
301
+ ? []
302
+ : [
303
+ ['fields', parts.fields],
304
+ ['sections', parts.sections],
305
+ ['preamble', parts.preamble],
306
+ ['path', parts.path],
307
+ ];
308
+ const matches = {};
309
+ for (const [part, text] of whole) {
310
+ for (const term of matched(text, terms))
311
+ (matches[term] ??= []).push(part);
312
+ }
313
+ const sections = parts === null ? [] : chunked(parts.sections);
314
+ const chunk = parts === null
315
+ ? []
316
+ : hit.chunk === 0
317
+ ? [
318
+ ['fields', parts.fields],
319
+ ['sections', sections[0] ?? ''],
320
+ ['preamble', parts.preamble],
321
+ ['path', parts.path],
322
+ ]
323
+ : [['sections', sections[hit.chunk] ?? '']];
324
+ return {
325
+ room,
326
+ path,
327
+ type: String(row.type),
328
+ score: hit.score,
329
+ matches,
330
+ ...cut(chunk, terms),
331
+ };
332
+ }
333
+ function messageHit(connection, hit, terms) {
334
+ const [row] = connection.rows('SELECT text FROM conversation_messages WHERE id = ?', [hit.id]);
335
+ const text = typeof row?.text === 'string' ? row.text : '';
336
+ const matches = {};
337
+ for (const term of matched(text, terms))
338
+ matches[term] = ['text'];
339
+ const [conversation, identity] = hit.tie;
340
+ return {
341
+ conversation,
342
+ identity,
343
+ score: hit.score,
344
+ matches,
345
+ ...cut([['text', chunked(text)[hit.chunk] ?? '']], terms),
346
+ };
347
+ }
348
+ export function searched(connection, ask) {
349
+ const clauses = matchClauses(ask.text);
350
+ const terms = queryTerms(ask.text);
351
+ const documents = ask.documents === null ? null : documentsListed(connection, ask.documents);
352
+ const messages = messagesListed(connection, ask.conversations);
353
+ const [documentsAfter, messagesAfter] = ask.after ?? [[], []];
354
+ const documentWindow = windowOf(connection, documents, clauses, documentsAfter, ask.limit);
355
+ const messageWindow = windowOf(connection, messages, clauses, messagesAfter, ask.limit);
356
+ const more = documentWindow.cursor !== null || messageWindow.cursor !== null;
357
+ return {
358
+ documents: documentWindow.kept.flatMap((hit) => documentHit(connection, hit, terms) ?? []),
359
+ messages: messageWindow.kept.map((hit) => messageHit(connection, hit, terms)),
360
+ ...(more ? { next: JSON.stringify([documentWindow.cursor, messageWindow.cursor]) } : {}),
361
+ ...(ask.total
362
+ ? {
363
+ total: {
364
+ documents: totalOf(connection, documents, clauses),
365
+ messages: totalOf(connection, messages, clauses),
366
+ },
367
+ }
368
+ : {}),
369
+ };
370
+ }
package/dist/host.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { type MdRuntimeOptions } from './connection.js';
2
2
  import type { PortReach } from './reach.js';
3
+ import type { CodeAvailability, Persistence, Readiness, SearchObligation, Space } from './readiness.js';
4
+ import type { EntrySeed } from './seed.js';
3
5
  import { type RuntimePort } from './wire.js';
4
6
  export type MdRuntimeHostOptions = MdRuntimeOptions & {
5
7
  deploymentFacts?: unknown;
@@ -7,14 +9,22 @@ export type MdRuntimeHostOptions = MdRuntimeOptions & {
7
9
  export type AttachedPort = {
8
10
  detach(): void;
9
11
  narrow(reach: PortReach): void;
12
+ readiness(deliver: (readiness: Readiness) => void): () => void;
13
+ answered(queries: readonly unknown[], done: () => void): void;
14
+ code(code: CodeAvailability, space: Space | null): void;
15
+ search(search: SearchObligation): void;
10
16
  };
11
17
  export type MdRuntimeHost = {
12
- attach(port: RuntimePort, app: string, release?: string, reach?: PortReach): AttachedPort;
13
- endAuthorization(): Promise<void>;
18
+ attach(port: RuntimePort, app: string, release?: string, reach?: PortReach, code?: CodeAvailability, search?: SearchObligation): AttachedPort;
19
+ seed(entry: EntrySeed): void;
20
+ persisted(answer: Persistence): void;
21
+ endAuthorization(holder?: string): Promise<void>;
14
22
  stop(): Promise<void>;
15
23
  };
16
24
  export declare function serveMdRuntime(options: MdRuntimeHostOptions): MdRuntimeHost;
25
+ export { entrySeedOf, type EntrySeed } from './seed.js';
17
26
  export type { PortReach, ReachedDocument } from './reach.js';
18
- export { STORE_SCHEMA } from './store.js';
27
+ export type { CodeAvailability, Persistence, SearchObligation, Space } from './readiness.js';
28
+ export { createRecovery, retryDelay, type Attempt, type Attempted, type AttemptOptions, type Recovery, type RecoveryOptions, } from './recovery.js';
19
29
  export type { RuntimePort } from './wire.js';
20
30
  //# sourceMappingURL=host.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,gBAAgB,EAEtB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAI5C,OAAO,EAGL,KAAK,WAAW,EAOjB,MAAM,WAAW,CAAC;AAInB,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,GAAG;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEpF,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,IAAI,IAAI,CAAC;IACf,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;IAC1F,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB,CAAC;AA8EF,wBAAgB,cAAc,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAkH3E;AAED,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,gBAAgB,EAItB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,KAAK,EACV,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,gBAAgB,EAChB,KAAK,EACN,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EAGL,KAAK,WAAW,EAOjB,MAAM,WAAW,CAAC;AAInB,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,GAAG;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEpF,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,IAAI,IAAI,CAAC;IACf,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,OAAO,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC/D,QAAQ,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC9D,IAAI,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC;IACxD,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,CACJ,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,SAAS,EACjB,IAAI,CAAC,EAAE,gBAAgB,EACvB,MAAM,CAAC,EAAE,gBAAgB,GACxB,YAAY,CAAC;IAChB,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IAC7B,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IACrC,gBAAgB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB,CAAC;AAoIF,wBAAgB,cAAc,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAoL3E;AAED,OAAO,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AACxD,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7D,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC7F,OAAO,EACL,cAAc,EACd,UAAU,EACV,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
package/dist/host.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { composeRuntime, } from './connection.js';
2
+ import { queryKey } from './query.js';
3
+ import { untilAnswered } from './recovery.js';
2
4
  import { described } from './failure.js';
3
- import { removeRuntimeStore } from './store.js';
4
- import { AUTHORIZATION_ENDED, request, WIRE_REACH, } from './wire.js';
5
+ import { AUTHORIZATION_ENDED, request, WIRE, } from './wire.js';
5
6
  const NARROWED = new Set(['within', 'whole']);
6
7
  function answered(md, app, writtenBy, member, input) {
7
8
  switch (member) {
@@ -15,16 +16,34 @@ function answered(md, app, writtenBy, member, input) {
15
16
  return md.types(input[0]);
16
17
  case 'save':
17
18
  return md.save(input[0], input[1], writtenBy);
19
+ case 'saveParts':
20
+ return md.saveParts(input[0], input[1], writtenBy);
18
21
  case 'history':
19
22
  return md.history(input[0], input[1], input[2]);
23
+ case 'resolve':
24
+ return md.resolve(input[0], input[1], input[2]);
25
+ case 'binaryFile.read':
26
+ return md.binaryFile.read(input[0], input[1], input[2]);
27
+ case 'binaryFile.link':
28
+ return md.binaryFile.link(input[0], input[1], input[2]);
29
+ case 'binaryFile.show':
30
+ return md.binaryFile.show(input[0], input[1], input[2]);
31
+ case 'binaryFile.attach':
32
+ return md.binaryFile.attach(input[0], input[1]);
33
+ case 'binaryFile.part':
34
+ return md.binaryFile.part(input[0], input[1]);
20
35
  case 'call':
21
36
  return md.call(input[0], input[1]);
22
37
  case 'reached':
23
38
  return md.reached(input[0], input[1], input[2]);
24
39
  case 'log':
25
40
  return md.log();
41
+ case 'cleanUp':
42
+ return md.cleanUp();
26
43
  case 'persistence':
27
44
  return md.persistence();
45
+ case 'retry':
46
+ return md.retry();
28
47
  case 'reauthorize':
29
48
  return md.reauthorize();
30
49
  case 'roomDraft.list':
@@ -32,13 +51,15 @@ function answered(md, app, writtenBy, member, input) {
32
51
  case 'roomDraft.read':
33
52
  return md.roomDraft.read(input[0], input[1]);
34
53
  case 'roomDraft.write':
35
- return md.roomDraft.write(input[0], input[1], input[2], input[3]);
54
+ return md.roomDraft.write(input[0], input[1], input[2], input[3], input[4]);
36
55
  case 'roomDraft.discard':
37
56
  return md.roomDraft.discard(input[0], input[1], input[2]);
38
57
  case 'presentation.read':
39
58
  return md.presentation.read(app);
40
59
  case 'presentation.write':
41
60
  return md.presentation.write(app, input[0]);
61
+ case 'presentation.edit':
62
+ return md.presentation.edit(app, input[0]);
42
63
  case 'conversation.list':
43
64
  return md.conversation.list();
44
65
  case 'conversation.read':
@@ -46,23 +67,39 @@ function answered(md, app, writtenBy, member, input) {
46
67
  case 'conversation.create':
47
68
  return md.conversation.create(input[0]);
48
69
  case 'conversation.send':
49
- return md.conversation.send(input[0]);
70
+ return md.conversation.send(input[0], input[1], input[2]);
50
71
  case 'conversation.answer':
51
72
  return md.conversation.answer(input[0], input[1]);
52
73
  case 'conversation.interrupt':
53
74
  return md.conversation.interrupt(input[0]);
54
75
  case 'conversation.kill':
55
76
  return md.conversation.kill(input[0]);
56
- case 'conversation.cancel':
57
- return md.conversation.cancel(input[0], input[1]);
58
77
  case 'conversation.delete':
59
78
  return md.conversation.delete(input[0]);
60
79
  case 'chatDraft.write':
61
80
  return md.chatDraft.write(input[0], input[1], input[2]);
81
+ case 'chatDraft.prepend':
82
+ return md.chatDraft.prepend(input[0], input[1]);
62
83
  case 'chatDraft.discard':
63
84
  return md.chatDraft.discard(input[0]);
64
85
  }
65
86
  }
87
+ function attempted(work) {
88
+ try {
89
+ return Promise.resolve(work());
90
+ }
91
+ catch (error) {
92
+ return Promise.reject(error);
93
+ }
94
+ }
95
+ function answeredResult(value) {
96
+ return typeof value === 'object' && value !== null && !('pending' in value);
97
+ }
98
+ function answeredMessage(value) {
99
+ if (typeof value !== 'object' || value === null)
100
+ return false;
101
+ return !('result' in value) || answeredResult(value.result);
102
+ }
66
103
  export function serveMdRuntime(options) {
67
104
  const deploymentFacts = structuredClone(options.deploymentFacts);
68
105
  const closing = new AbortController();
@@ -76,17 +113,31 @@ export function serveMdRuntime(options) {
76
113
  const { md } = runtime;
77
114
  const attachments = new Map();
78
115
  let authorized = true;
116
+ let opening = null;
117
+ const opened = () => (opening ??= untilAnswered(() => md.loaded(), closing.signal));
79
118
  const watchFacts = (deliver) => {
80
119
  deliver(structuredClone(deploymentFacts));
81
120
  return () => { };
82
121
  };
83
- function attach(port, app, release, reach) {
122
+ function attach(port, app, release, reach, code = 'stored', search = 'unused') {
84
123
  if (!authorized)
85
124
  throw new Error(AUTHORIZATION_ENDED);
86
125
  const writtenBy = release === undefined ? undefined : { app, release };
87
- const view = runtime.within(reach ?? null);
126
+ let facts = { target: app, code, space: null, search };
127
+ const view = runtime.within(reach ?? null, facts);
88
128
  const observations = new Map();
129
+ const followed = new Set();
130
+ const answeredKeys = new Set();
131
+ let awaited = null;
89
132
  let serving = true;
133
+ const answeredQuery = (query) => {
134
+ answeredKeys.add(queryKey(query));
135
+ if (awaited === null || !awaited.keys.every((key) => answeredKeys.has(key)))
136
+ return;
137
+ const { done } = awaited;
138
+ awaited = null;
139
+ done();
140
+ };
90
141
  const send = (message) => {
91
142
  port.postMessage(message);
92
143
  };
@@ -97,6 +148,8 @@ export function serveMdRuntime(options) {
97
148
  for (const end of observations.values())
98
149
  end();
99
150
  observations.clear();
151
+ for (const end of [...followed])
152
+ end();
100
153
  attachments.delete(detach);
101
154
  };
102
155
  const observe = (id, topic, input) => {
@@ -106,10 +159,26 @@ export function serveMdRuntime(options) {
106
159
  return;
107
160
  send(resolved ? { kind: 'event', id, value } : { kind: 'result', id, value });
108
161
  resolved = true;
162
+ if (topic === 'query' && answeredMessage(value))
163
+ answeredQuery(input[0]);
109
164
  };
110
165
  observations.set(id, topic === 'deployment-facts'
111
166
  ? watchFacts(deliver)
112
- : (NARROWED.has(WIRE_REACH.topics[topic]) ? view : runtime).watch(topic, topic === 'presentation' ? [app] : input, deliver));
167
+ : (NARROWED.has(WIRE.topics[topic]) ? view : runtime).watch(topic, topic === 'presentation' ? [app] : input, deliver));
168
+ };
169
+ const reply = async (id, query, pending) => {
170
+ try {
171
+ const value = await pending;
172
+ if (!serving)
173
+ return;
174
+ send({ kind: 'result', id, value });
175
+ if (query !== undefined && answeredResult(value))
176
+ answeredQuery(query);
177
+ }
178
+ catch (error) {
179
+ if (serving)
180
+ send({ kind: 'failure', id, ...described(error) });
181
+ }
113
182
  };
114
183
  const serve = async (message) => {
115
184
  if (message.kind === 'hello')
@@ -120,21 +189,24 @@ export function serveMdRuntime(options) {
120
189
  return;
121
190
  }
122
191
  try {
123
- if (message.kind !== 'observe' || message.topic !== 'room-drafts')
192
+ if (message.kind !== 'observe')
124
193
  await md.loaded();
194
+ else if (message.topic !== 'room-drafts')
195
+ await opened();
125
196
  if (message.kind === 'observe') {
126
197
  if (serving)
127
198
  observe(message.id, message.topic, message.input);
128
199
  return;
129
200
  }
130
- const value = await answered(NARROWED.has(WIRE_REACH.members[message.member]) ? view.md : md, app, writtenBy, message.member, message.input);
131
- if (serving)
132
- send({ kind: 'result', id: message.id, value });
133
201
  }
134
202
  catch (error) {
135
203
  if (serving)
136
204
  send({ kind: 'failure', id: message.id, ...described(error) });
205
+ return;
137
206
  }
207
+ const { id, member, input } = message;
208
+ const pending = attempted(() => answered(NARROWED.has(WIRE.members[member].reach) ? view.md : md, app, writtenBy, member, input));
209
+ return reply(id, member === 'query' ? input[0] : undefined, pending);
138
210
  };
139
211
  port.addEventListener('message', (event) => {
140
212
  if (!serving)
@@ -153,23 +225,64 @@ export function serveMdRuntime(options) {
153
225
  if (serving)
154
226
  view.narrow(next);
155
227
  },
228
+ code(next, space) {
229
+ if (!serving)
230
+ return;
231
+ facts = { ...facts, code: next, space };
232
+ view.facts(facts);
233
+ },
234
+ search(next) {
235
+ if (!serving)
236
+ return;
237
+ facts = { ...facts, search: next };
238
+ view.facts(facts);
239
+ },
240
+ answered(queries, done) {
241
+ const keys = queries.map(queryKey);
242
+ if (keys.every((key) => answeredKeys.has(key)))
243
+ done();
244
+ else
245
+ awaited = { keys, done };
246
+ },
247
+ readiness(deliver) {
248
+ if (!serving)
249
+ return () => { };
250
+ const stop = view.watch('readiness', [], (value) => {
251
+ if (serving)
252
+ deliver(value);
253
+ });
254
+ const end = () => {
255
+ followed.delete(end);
256
+ stop();
257
+ };
258
+ followed.add(end);
259
+ return end;
260
+ },
156
261
  };
157
262
  }
158
263
  return {
159
264
  attach,
160
- async endAuthorization() {
265
+ seed(entry) {
266
+ runtime.seed(entry);
267
+ },
268
+ persisted(answer) {
269
+ runtime.persisted(answer);
270
+ },
271
+ async endAuthorization(holder) {
161
272
  authorized = false;
162
273
  for (const [detach, send] of [...attachments]) {
163
274
  send({ kind: 'ended' });
164
275
  detach();
165
276
  }
166
277
  close();
167
- await removeRuntimeStore();
168
- await options.shell.dropGeneration();
278
+ if (!(await options.database.ask({ kind: 'authored.end', holder: holder ?? null }))) {
279
+ await options.shell.dropGeneration();
280
+ }
169
281
  },
170
282
  stop() {
171
283
  return runtime.stop();
172
284
  },
173
285
  };
174
286
  }
175
- export { STORE_SCHEMA } from './store.js';
287
+ export { entrySeedOf } from './seed.js';
288
+ export { createRecovery, retryDelay, } from './recovery.js';