@agentshouse/mdruntime 0.1.0-ca6edc1abd618 → 0.1.0-ca8707258fdde

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 (220) hide show
  1. package/README.md +2 -2
  2. package/dist/acceptance.d.ts +26 -0
  3. package/dist/acceptance.d.ts.map +1 -0
  4. package/dist/acceptance.js +384 -0
  5. package/dist/adapter.d.ts +102 -70
  6. package/dist/adapter.d.ts.map +1 -1
  7. package/dist/attached-model.d.ts +3 -10
  8. package/dist/attached-model.d.ts.map +1 -1
  9. package/dist/attached-model.js +29 -47
  10. package/dist/bootstrap.d.ts +16 -14
  11. package/dist/bootstrap.d.ts.map +1 -1
  12. package/dist/bootstrap.js +272 -129
  13. package/dist/chain.d.ts +2 -1
  14. package/dist/chain.d.ts.map +1 -1
  15. package/dist/change.d.ts +1 -0
  16. package/dist/change.d.ts.map +1 -1
  17. package/dist/chat-types.d.ts +3 -0
  18. package/dist/chat-types.d.ts.map +1 -0
  19. package/dist/chat-types.js +2 -0
  20. package/dist/client.d.ts +20 -10
  21. package/dist/client.d.ts.map +1 -1
  22. package/dist/client.js +55 -6
  23. package/dist/collection.d.ts +7 -4
  24. package/dist/collection.d.ts.map +1 -1
  25. package/dist/collection.js +23 -17
  26. package/dist/compile.d.ts +6 -6
  27. package/dist/compile.d.ts.map +1 -1
  28. package/dist/compile.js +278 -282
  29. package/dist/connection.d.ts +22 -44
  30. package/dist/connection.d.ts.map +1 -1
  31. package/dist/connection.js +1600 -3577
  32. package/dist/context.d.ts +2 -0
  33. package/dist/context.d.ts.map +1 -0
  34. package/dist/context.js +7 -0
  35. package/dist/conversation-row.d.ts +33 -0
  36. package/dist/conversation-row.d.ts.map +1 -0
  37. package/dist/conversation-row.js +47 -0
  38. package/dist/conversation.d.ts +27 -18
  39. package/dist/conversation.d.ts.map +1 -1
  40. package/dist/conversation.js +484 -258
  41. package/dist/durability.d.ts +6 -1
  42. package/dist/durability.d.ts.map +1 -1
  43. package/dist/entry.d.ts +3 -2
  44. package/dist/entry.d.ts.map +1 -1
  45. package/dist/entry.js +18 -8
  46. package/dist/figures.d.ts +48 -0
  47. package/dist/figures.d.ts.map +1 -0
  48. package/dist/figures.js +197 -0
  49. package/dist/file.d.ts +2 -11
  50. package/dist/file.d.ts.map +1 -1
  51. package/dist/file.js +6 -4
  52. package/dist/host.d.ts +3 -7
  53. package/dist/host.d.ts.map +1 -1
  54. package/dist/host.js +29 -32
  55. package/dist/identity.d.ts +0 -1
  56. package/dist/identity.d.ts.map +1 -1
  57. package/dist/identity.js +0 -3
  58. package/dist/index.d.ts +20 -10
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +6 -1
  61. package/dist/install.d.ts +86 -0
  62. package/dist/install.d.ts.map +1 -0
  63. package/dist/install.js +537 -0
  64. package/dist/job.d.ts +33 -0
  65. package/dist/job.d.ts.map +1 -0
  66. package/dist/job.js +54 -0
  67. package/dist/live.d.ts +5 -4
  68. package/dist/live.d.ts.map +1 -1
  69. package/dist/live.js +62 -20
  70. package/dist/log.d.ts +1 -0
  71. package/dist/log.d.ts.map +1 -1
  72. package/dist/media.d.ts +8 -0
  73. package/dist/media.d.ts.map +1 -0
  74. package/dist/media.js +218 -0
  75. package/dist/memory.d.ts +12 -5
  76. package/dist/memory.d.ts.map +1 -1
  77. package/dist/memory.js +216 -85
  78. package/dist/messages.d.ts +27 -3
  79. package/dist/messages.d.ts.map +1 -1
  80. package/dist/messages.js +97 -19
  81. package/dist/model.d.ts +8 -10
  82. package/dist/model.d.ts.map +1 -1
  83. package/dist/model.js +5 -17
  84. package/dist/observation.d.ts +3 -2
  85. package/dist/observation.d.ts.map +1 -1
  86. package/dist/observation.js +8 -3
  87. package/dist/outcome.d.ts +14 -5
  88. package/dist/outcome.d.ts.map +1 -1
  89. package/dist/outcome.js +19 -2
  90. package/dist/overlay.d.ts +1 -0
  91. package/dist/overlay.d.ts.map +1 -1
  92. package/dist/overlay.js +11 -0
  93. package/dist/parts.d.ts +59 -0
  94. package/dist/parts.d.ts.map +1 -0
  95. package/dist/parts.js +118 -0
  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 +18 -207
  100. package/dist/query.d.ts.map +1 -1
  101. package/dist/query.js +3 -505
  102. package/dist/questions.d.ts +32 -0
  103. package/dist/questions.d.ts.map +1 -0
  104. package/dist/questions.js +118 -0
  105. package/dist/reach.d.ts +18 -11
  106. package/dist/reach.d.ts.map +1 -1
  107. package/dist/reach.js +206 -126
  108. package/dist/react.d.ts +5 -2
  109. package/dist/react.d.ts.map +1 -1
  110. package/dist/read.d.ts +7 -0
  111. package/dist/read.d.ts.map +1 -0
  112. package/dist/read.js +17 -0
  113. package/dist/readiness.d.ts +3 -17
  114. package/dist/readiness.d.ts.map +1 -1
  115. package/dist/readiness.js +4 -10
  116. package/dist/reads.d.ts +34 -10
  117. package/dist/reads.d.ts.map +1 -1
  118. package/dist/reads.js +114 -86
  119. package/dist/recovery.d.ts +1 -0
  120. package/dist/recovery.d.ts.map +1 -1
  121. package/dist/recovery.js +35 -1
  122. package/dist/refresh.d.ts +7 -5
  123. package/dist/refresh.d.ts.map +1 -1
  124. package/dist/refresh.js +33 -5
  125. package/dist/replica.d.ts +10 -11
  126. package/dist/replica.d.ts.map +1 -1
  127. package/dist/replica.js +52 -20
  128. package/dist/room-draft.d.ts +21 -5
  129. package/dist/room-draft.d.ts.map +1 -1
  130. package/dist/room-draft.js +98 -51
  131. package/dist/room-drafts.d.ts +29 -0
  132. package/dist/room-drafts.d.ts.map +1 -0
  133. package/dist/room-drafts.js +216 -0
  134. package/dist/rows.d.ts +18 -16
  135. package/dist/rows.d.ts.map +1 -1
  136. package/dist/rows.js +45 -153
  137. package/dist/save.d.ts +2 -0
  138. package/dist/save.d.ts.map +1 -1
  139. package/dist/save.js +22 -13
  140. package/dist/saves.d.ts +96 -0
  141. package/dist/saves.d.ts.map +1 -0
  142. package/dist/saves.js +999 -0
  143. package/dist/search.d.ts +12 -63
  144. package/dist/search.d.ts.map +1 -1
  145. package/dist/search.js +9 -52
  146. package/dist/seed.d.ts +16 -2
  147. package/dist/seed.d.ts.map +1 -1
  148. package/dist/seed.js +66 -0
  149. package/dist/shared.d.ts +0 -1
  150. package/dist/shared.d.ts.map +1 -1
  151. package/dist/shared.js +0 -10
  152. package/dist/socket/admission.d.ts +7 -0
  153. package/dist/socket/admission.d.ts.map +1 -0
  154. package/dist/socket/admission.js +39 -0
  155. package/dist/socket/answers.d.ts +6 -0
  156. package/dist/socket/answers.d.ts.map +1 -0
  157. package/dist/socket/answers.js +20 -0
  158. package/dist/socket/reads.d.ts +10 -0
  159. package/dist/socket/reads.d.ts.map +1 -0
  160. package/dist/socket/reads.js +31 -0
  161. package/dist/socket.d.ts +43 -0
  162. package/dist/socket.d.ts.map +1 -0
  163. package/dist/socket.js +96 -0
  164. package/dist/source.d.ts +61 -0
  165. package/dist/source.d.ts.map +1 -0
  166. package/dist/source.js +81 -0
  167. package/dist/status.d.ts +4 -5
  168. package/dist/status.d.ts.map +1 -1
  169. package/dist/status.js +3 -7
  170. package/dist/store/fences.d.ts +16 -0
  171. package/dist/store/fences.d.ts.map +1 -0
  172. package/dist/store/fences.js +26 -0
  173. package/dist/store/installs.d.ts +30 -0
  174. package/dist/store/installs.d.ts.map +1 -0
  175. package/dist/store/installs.js +34 -0
  176. package/dist/store/records.d.ts +212 -0
  177. package/dist/store/records.d.ts.map +1 -0
  178. package/dist/store/records.js +408 -0
  179. package/dist/store/room-held.d.ts +8 -0
  180. package/dist/store/room-held.d.ts.map +1 -0
  181. package/dist/store/room-held.js +17 -0
  182. package/dist/store/watchers.d.ts +15 -0
  183. package/dist/store/watchers.d.ts.map +1 -0
  184. package/dist/store/watchers.js +36 -0
  185. package/dist/store/writes.d.ts +24 -0
  186. package/dist/store/writes.d.ts.map +1 -0
  187. package/dist/store/writes.js +125 -0
  188. package/dist/store.d.ts +76 -158
  189. package/dist/store.d.ts.map +1 -1
  190. package/dist/store.js +117 -403
  191. package/dist/stored.d.ts +66 -55
  192. package/dist/stored.d.ts.map +1 -1
  193. package/dist/stored.js +343 -273
  194. package/dist/tables.d.ts +118 -65
  195. package/dist/tables.d.ts.map +1 -1
  196. package/dist/tables.js +199 -99
  197. package/dist/tallies.d.ts +18 -0
  198. package/dist/tallies.d.ts.map +1 -0
  199. package/dist/tallies.js +129 -0
  200. package/dist/transfer.d.ts +0 -5
  201. package/dist/transfer.d.ts.map +1 -1
  202. package/dist/validation.d.ts +3 -4
  203. package/dist/validation.d.ts.map +1 -1
  204. package/dist/validation.js +42 -100
  205. package/dist/windows.d.ts +1 -12
  206. package/dist/windows.d.ts.map +1 -1
  207. package/dist/windows.js +2 -75
  208. package/dist/wire.d.ts +23 -8
  209. package/dist/wire.d.ts.map +1 -1
  210. package/dist/wire.js +8 -2
  211. package/package.json +2 -2
  212. package/dist/fence.d.ts +0 -6
  213. package/dist/fence.d.ts.map +0 -1
  214. package/dist/fence.js +0 -10
  215. package/dist/fulltext.d.ts +0 -27
  216. package/dist/fulltext.d.ts.map +0 -1
  217. package/dist/fulltext.js +0 -368
  218. package/dist/words.d.ts +0 -19
  219. package/dist/words.d.ts.map +0 -1
  220. package/dist/words.js +0 -150
package/dist/memory.js CHANGED
@@ -1,7 +1,12 @@
1
+ import { documentFacts, parse } from '@agentshouse/mdmodel';
2
+ import { NO_ROOM_TYPES, schemaTypes } from './attached-model.js';
1
3
  import { sameChange } from './overlay.js';
4
+ const ATTACHMENTS = 'attachments';
5
+ function lastName(path) {
6
+ return path.slice(path.lastIndexOf('/') + 1);
7
+ }
2
8
  const FIXTURE_RETENTION = {
3
9
  history: { kind: 'duration', seconds: 1 },
4
- outcome: 604_800,
5
10
  refreshHorizon: 1,
6
11
  };
7
12
  function declaredRetention(value) {
@@ -48,13 +53,16 @@ export function createMemoryAdapter(options = {}) {
48
53
  const listeners = new Set();
49
54
  const modeObservers = new Set();
50
55
  const scripted = new Map();
51
- const unsealed = new Map();
52
- const sealed = new Set();
53
56
  const sentTurns = new Map();
54
57
  const historyPageSize = options.historyPageSize;
55
- let held = [];
58
+ const attached = new Map();
59
+ const linked = new WeakMap();
60
+ const carried = new Map();
56
61
  let expiring = [];
62
+ let openings = 0;
63
+ const declaredRooms = new Set();
57
64
  let replicated = [];
65
+ let heldCursor = null;
58
66
  let issued = 0;
59
67
  function scriptConversations(entries) {
60
68
  scripted.clear();
@@ -82,8 +90,48 @@ export function createMemoryAdapter(options = {}) {
82
90
  const wanted = ordinals.get(`${locator}\u0000${target}`);
83
91
  return held !== undefined && wanted !== undefined && held >= wanted;
84
92
  }
93
+ function heldBlob(room, path) {
94
+ return attached.get(`${room.locator}\u0000${path}`) ?? room.blobs?.[path];
95
+ }
96
+ function freePath(room, name) {
97
+ const dot = name.lastIndexOf('.');
98
+ const stem = dot > 0 ? name.slice(0, dot) : name;
99
+ const extension = dot > 0 ? name.slice(dot) : '';
100
+ for (let count = 1;; count += 1) {
101
+ const path = `${ATTACHMENTS}/${count === 1 ? stem : `${stem}-${count}`}${extension}`;
102
+ if (heldBlob(room, path) === undefined && room.documents?.[path] === undefined)
103
+ return path;
104
+ }
105
+ }
106
+ const saved = new WeakMap();
107
+ function factsAt(room, path, content) {
108
+ const types = schemaTypes(room.model?.kind === 'model' ? room.model.snapshot.schema : null, NO_ROOM_TYPES);
109
+ let held = saved.get(types);
110
+ if (held === undefined) {
111
+ held = new Map();
112
+ saved.set(types, held);
113
+ }
114
+ const key = `${path}\u0000${content}`;
115
+ if (!held.has(key)) {
116
+ const facts = documentFacts(path, parse(content, path), { types });
117
+ held.set(key, facts === null ? null : JSON.parse(JSON.stringify(facts)));
118
+ }
119
+ return held.get(key);
120
+ }
121
+ function factsIn(room, paths) {
122
+ const facts = {};
123
+ for (const path of paths) {
124
+ const content = room.documents?.[path];
125
+ const held = room.facts?.[path] ?? (content === undefined ? null : factsAt(room, path, content));
126
+ if (held !== null)
127
+ facts[path] = held;
128
+ }
129
+ return facts;
130
+ }
85
131
  function retain(entries) {
86
132
  for (const room of entries) {
133
+ for (const [path, content] of Object.entries(room.documents ?? {}))
134
+ factsAt(room, path, content);
87
135
  note(room.locator, room);
88
136
  const versions = served.get(room.locator) ?? new Map();
89
137
  versions.set(room.roomVersion, room);
@@ -116,10 +164,14 @@ export function createMemoryAdapter(options = {}) {
116
164
  roomVersion: room.roomVersion,
117
165
  ...(opening ? { models: modelsOf(room) } : {}),
118
166
  documents: named(room.documents ?? {}, taken),
167
+ facts: factsIn(room, taken),
119
168
  revisions: named(room.revisions ?? {}, [...taken, ...targets]),
120
169
  ...(room.fileIds === undefined
121
170
  ? {}
122
171
  : { fileIds: named(room.fileIds, [...taken, ...targets]) }),
172
+ ...(room.created === undefined
173
+ ? {}
174
+ : { created: named(room.created, [...taken, ...targets]) }),
123
175
  changes: named(room.changes ?? {}, [...taken, ...targets]),
124
176
  binaryFiles: named(room.binaryFiles ?? {}, targets),
125
177
  ...(opening && room.folders !== undefined ? { folders: room.folders } : {}),
@@ -136,18 +188,19 @@ export function createMemoryAdapter(options = {}) {
136
188
  ...(folderRemovals.length === 0 ? {} : { folderRemovals }),
137
189
  };
138
190
  }
139
- function advancement(room, from, at) {
191
+ function advancement(room, from, at, whole = false) {
140
192
  const current = room.documents ?? {};
141
193
  const previous = from.documents ?? {};
142
194
  const currentTargets = new Set(Object.keys(room.binaryFiles ?? {}));
143
195
  const previousTargets = new Set(Object.keys(from.binaryFiles ?? {}));
144
196
  const headMoved = (path) => room.revisions?.[path] !== from.revisions?.[path] ||
145
197
  room.fileIds?.[path] !== from.fileIds?.[path] ||
198
+ room.created?.[path] !== from.created?.[path] ||
146
199
  !sameChange(room.changes?.[path], from.changes?.[path]);
147
200
  const changed = Object.keys(current)
148
201
  .filter((path) => current[path] !== previous[path] || headMoved(path))
149
202
  .sort();
150
- const size = pageSize ?? changed.length;
203
+ const size = whole ? changed.length : (pageSize ?? changed.length);
151
204
  const taken = changed.slice(at, at + size);
152
205
  const next = at + taken.length;
153
206
  const opening = at === 0;
@@ -163,10 +216,14 @@ export function createMemoryAdapter(options = {}) {
163
216
  roomVersion: room.roomVersion,
164
217
  ...(models === undefined ? {} : { models }),
165
218
  documents: named(current, taken),
219
+ facts: factsIn(room, taken),
166
220
  revisions: named(room.revisions ?? {}, [...taken, ...targets]),
167
221
  ...(room.fileIds === undefined
168
222
  ? {}
169
223
  : { fileIds: named(room.fileIds, [...taken, ...targets]) }),
224
+ ...(room.created === undefined
225
+ ? {}
226
+ : { created: named(room.created, [...taken, ...targets]) }),
170
227
  changes: named(room.changes ?? {}, [...taken, ...targets]),
171
228
  binaryFiles: named(room.binaryFiles ?? {}, targets),
172
229
  ...(opening ? foldersMoved(room, from) : {}),
@@ -179,6 +236,26 @@ export function createMemoryAdapter(options = {}) {
179
236
  ...(pageToken === undefined ? {} : { pageToken }),
180
237
  };
181
238
  }
239
+ function carry(locator) {
240
+ const at = carried.get(locator);
241
+ const current = rooms.find((entry) => entry.locator === locator);
242
+ if (at === undefined || current === undefined || current.roomVersion === at)
243
+ return;
244
+ const from = served.get(locator)?.get(at);
245
+ carried.set(locator, current.roomVersion);
246
+ if (from === undefined)
247
+ return;
248
+ const page = advancement(current, from, 0, true);
249
+ for (const observer of observers) {
250
+ observer({
251
+ kind: 'room-change',
252
+ locator,
253
+ from: at,
254
+ roomVersion: current.roomVersion,
255
+ read: async () => page,
256
+ });
257
+ }
258
+ }
182
259
  function pageOf(conversation, after) {
183
260
  const messages = conversation.messages ?? [];
184
261
  const first = typeof after === 'string' ? messages.findIndex((message) => message.position > after) : 0;
@@ -193,16 +270,13 @@ export function createMemoryAdapter(options = {}) {
193
270
  : {}),
194
271
  };
195
272
  }
196
- function historiesOf(input) {
197
- const asked = input?.conversations;
198
- if (!Array.isArray(asked))
199
- return { kind: 'rejected', code: 'invalid_request', fields: {}, retryable: false };
273
+ function historiesOf(request) {
200
274
  const histories = [];
201
- for (const entry of asked) {
202
- const conversation = typeof entry.conversation === 'string' ? scripted.get(entry.conversation) : undefined;
275
+ for (const entry of request.conversations) {
276
+ const conversation = scripted.get(entry.conversation);
203
277
  if (conversation === undefined)
204
278
  continue;
205
- const outcome = conversation.outcomes?.['conversation.history'];
279
+ const outcome = conversation.outcomes?.history;
206
280
  if (outcome !== undefined)
207
281
  return outcome;
208
282
  histories.push(pageOf(conversation, entry.after));
@@ -213,15 +287,13 @@ export function createMemoryAdapter(options = {}) {
213
287
  function conversationAction(request) {
214
288
  const name = request.name;
215
289
  const input = (request.input ?? {});
216
- if (name === 'conversation.history')
217
- return historiesOf(request.input);
218
- if (name === 'conversation.create') {
290
+ if (name === 'conversations/create') {
219
291
  issued += 1;
220
292
  const conversation = `conversation-${issued}`;
221
293
  scripted.set(conversation, { conversation });
222
- return { kind: 'action', result: { conversation } };
294
+ return { kind: 'action', result: { conversation_id: conversation } };
223
295
  }
224
- const named = typeof input.conversation === 'string' ? input.conversation : '';
296
+ const named = typeof input.conversation_id === 'string' ? input.conversation_id : '';
225
297
  const conversation = scripted.get(named);
226
298
  if (conversation === undefined) {
227
299
  return {
@@ -234,34 +306,28 @@ export function createMemoryAdapter(options = {}) {
234
306
  const outcome = conversation.outcomes?.[name];
235
307
  if (outcome !== undefined)
236
308
  return outcome;
237
- if (name === 'conversation.send') {
309
+ if (name === 'conversations/send') {
238
310
  const operation = typeof input.operation_id === 'string' ? input.operation_id : null;
239
311
  const answered = operation === null ? undefined : sentTurns.get(operation);
240
312
  if (answered !== undefined)
241
- return { kind: 'action', result: { turn: answered } };
313
+ return { kind: 'action', result: { message_id: answered } };
242
314
  issued += 1;
243
315
  const turn = `turn-${issued}`;
244
- unsealed.set(turn, named);
245
316
  if (operation !== null)
246
317
  sentTurns.set(operation, turn);
247
- return { kind: 'action', result: { turn } };
318
+ return { kind: 'action', result: { message_id: turn } };
248
319
  }
249
- if (name === 'conversation.cancel') {
250
- const turn = typeof input.turn === 'string' ? input.turn : '';
251
- if (sealed.has(turn) || !unsealed.has(turn)) {
252
- return { kind: 'rejected', code: 'turn-sealed', fields: { turn }, retryable: false };
253
- }
254
- unsealed.delete(turn);
255
- return { kind: 'action', result: { turn, cancelled: true } };
256
- }
257
- if (name === 'conversation.delete')
320
+ if (name === 'conversations/delete')
258
321
  scripted.delete(named);
259
- return { kind: 'action', result: { conversation: named } };
322
+ return { kind: 'action', result: { conversation_id: named } };
260
323
  }
261
324
  function announceList() {
262
- const entries = [...scripted.values()].flatMap(({ conversation, listed }) => listed === undefined ? [] : [{ conversation, ...listed }]);
263
- for (const listener of listeners)
264
- listener({ kind: 'list', entries });
325
+ const entries = [...scripted.values()].flatMap(({ conversation, listed, messages = [] }) => listed === undefined
326
+ ? []
327
+ : [{ conversation, ...listed, newest: messages.at(-1)?.position ?? null }]);
328
+ for (const listener of listeners) {
329
+ listener({ kind: 'list', entries, whole: true, more: false, cursor: null });
330
+ }
265
331
  }
266
332
  function discovery() {
267
333
  return {
@@ -273,9 +339,42 @@ export function createMemoryAdapter(options = {}) {
273
339
  ...(options.member === undefined ? {} : { member: options.member }),
274
340
  };
275
341
  }
342
+ function discovered() {
343
+ return resolveScript(scripts.discover, discovery, undefined).then((answer) => {
344
+ if (answer.kind === 'discovery')
345
+ for (const room of answer.rooms)
346
+ note(room.locator, room);
347
+ return answer;
348
+ });
349
+ }
350
+ function opened(notify) {
351
+ const opening = ++openings;
352
+ notify({ kind: 'opened' });
353
+ void discovered()
354
+ .catch(() => ({ kind: 'transport-uncertainty' }))
355
+ .then((answer) => {
356
+ if (opening !== openings)
357
+ return;
358
+ if (answer.kind !== 'discovery') {
359
+ openings += 1;
360
+ carried.clear();
361
+ const revoked = answer.kind === 'revoked-authorization';
362
+ notify({ kind: 'closed', revoked, lost: !revoked, authorityChanged: false });
363
+ return;
364
+ }
365
+ declaredRooms.clear();
366
+ for (const room of answer.rooms)
367
+ declaredRooms.add(room.locator);
368
+ notify({ kind: 'discovered', discovery: answer });
369
+ });
370
+ }
371
+ function emitAll(event) {
372
+ for (const observer of observers)
373
+ observer(event);
374
+ }
276
375
  return {
277
- ...(options.exhaustiveDiscovery === undefined ? {} : { exhaustiveDiscovery: true }),
278
376
  ...(options.roomTypes === undefined ? {} : { roomTypes: options.roomTypes }),
377
+ ...(options.typedPaths === undefined ? {} : { typedPaths: options.typedPaths }),
279
378
  setRooms(next) {
280
379
  rooms = [...next];
281
380
  retain(rooms);
@@ -284,33 +383,37 @@ export function createMemoryAdapter(options = {}) {
284
383
  scriptConversations(next);
285
384
  },
286
385
  open() {
386
+ carried.clear();
287
387
  announceList();
288
- for (const observer of observers)
289
- observer({ kind: 'opened' });
388
+ opened(emitAll);
290
389
  },
291
- close({ revoked = false, lost = !revoked } = {}) {
292
- for (const observer of observers)
293
- observer({ kind: 'closed', revoked, lost });
390
+ close({ revoked = false, lost = !revoked, authorityChanged = false } = {}) {
391
+ openings += 1;
392
+ carried.clear();
393
+ emitAll({ kind: 'closed', revoked, lost, authorityChanged });
294
394
  },
295
395
  expire(locators) {
296
396
  expiring = [...locators];
297
397
  },
298
- notifyRoomVersion({ locator, roomVersion }) {
299
- for (const observer of observers)
300
- observer({ kind: 'room-version', locator, roomVersion });
398
+ notifyRoomVersion({ locator }) {
399
+ carry(locator);
301
400
  },
302
- notifyAuthorityChanged(scope) {
303
- for (const observer of observers) {
304
- observer(scope === undefined
305
- ? { kind: 'authority-changed' }
306
- : { kind: 'authority-changed', scope });
307
- }
401
+ notifyAuthorityChanged(locators) {
402
+ const named = new Set(locators ?? [...declaredRooms, ...rooms.map((room) => room.locator)]);
403
+ const listed = rooms.filter((room) => named.has(room.locator));
404
+ const left = [...named].filter((locator) => !listed.some((room) => room.locator === locator));
405
+ for (const room of listed)
406
+ declaredRooms.add(room.locator);
407
+ for (const locator of left)
408
+ declaredRooms.delete(locator);
409
+ emitAll({
410
+ kind: 'authority-changed',
411
+ rooms: listed.map(declared),
412
+ left,
413
+ ...(options.member === undefined ? {} : { member: options.member }),
414
+ });
308
415
  },
309
416
  notifyConversation(event) {
310
- if (event.kind === 'accepted-message') {
311
- sealed.add(event.message.turn);
312
- unsealed.delete(event.message.turn);
313
- }
314
417
  for (const listener of listeners)
315
418
  listener(event);
316
419
  },
@@ -319,21 +422,31 @@ export function createMemoryAdapter(options = {}) {
319
422
  observer(mode);
320
423
  },
321
424
  heldRooms() {
322
- return held;
425
+ return [...carried.keys()].sort();
323
426
  },
324
427
  replicatedConversations() {
325
428
  return replicated;
326
429
  },
430
+ heldConversationCursor() {
431
+ return heldCursor;
432
+ },
327
433
  conversations: {
328
434
  replicate(next) {
329
435
  replicated = [...next];
330
436
  },
437
+ hold(cursor) {
438
+ heldCursor = cursor;
439
+ },
331
440
  observe(notify) {
332
441
  listeners.add(notify);
333
442
  return () => {
334
443
  listeners.delete(notify);
335
444
  };
336
445
  },
446
+ history: (request) => Promise.resolve(historiesOf(request)),
447
+ ...(options.agents === undefined
448
+ ? {}
449
+ : { agents: () => Promise.resolve(structuredClone(options.agents)) }),
337
450
  },
338
451
  serviceMode: {
339
452
  observe(notify) {
@@ -345,44 +458,46 @@ export function createMemoryAdapter(options = {}) {
345
458
  },
346
459
  roomStream: {
347
460
  hold(asked) {
348
- held = asked.map((room) => room.locator);
349
- const expired = expiring.filter((locator) => held.includes(locator));
350
- expiring = [];
351
- const moved = asked.flatMap((room) => {
461
+ const named = asked.map((room) => room.locator);
462
+ const unknown = asked.flatMap((room) => {
352
463
  const current = rooms.find((entry) => entry.locator === room.locator);
464
+ const version = room.roomVersion;
353
465
  return current === undefined ||
354
- expired.includes(room.locator) ||
355
- current.roomVersion === room.roomVersion
466
+ (version !== null &&
467
+ (current.roomVersion === version || served.get(room.locator)?.has(version) === true))
356
468
  ? []
357
- : [{ locator: room.locator, roomVersion: current.roomVersion }];
469
+ : [room.locator];
358
470
  });
471
+ const expired = [
472
+ ...new Set([...expiring.filter((locator) => named.includes(locator)), ...unknown]),
473
+ ];
474
+ expiring = expiring.filter((locator) => !named.includes(locator));
359
475
  queueMicrotask(() => {
360
- for (const observer of observers) {
361
- for (const room of moved)
362
- observer({ kind: 'room-version', ...room });
363
- observer({ kind: 'opening', expired });
476
+ for (const room of asked) {
477
+ if (expired.includes(room.locator) || room.roomVersion === null)
478
+ carried.delete(room.locator);
479
+ else {
480
+ carried.set(room.locator, room.roomVersion);
481
+ carry(room.locator);
482
+ }
364
483
  }
484
+ emitAll({ kind: 'caught-up', expired });
365
485
  });
366
486
  },
367
- reopen() {
368
- queueMicrotask(() => {
369
- for (const observer of observers) {
370
- observer({ kind: 'closed', revoked: false, lost: false });
371
- }
372
- });
373
- },
374
- published() { },
375
487
  observe(notify) {
376
488
  observers.add(notify);
377
489
  queueMicrotask(() => {
378
490
  if (!observers.has(notify))
379
491
  return;
380
492
  if (options.unopened === true) {
381
- notify({ kind: 'closed', revoked: false, lost: true });
493
+ notify({ kind: 'closed', revoked: false, lost: true, authorityChanged: false });
382
494
  return;
383
495
  }
384
496
  announceList();
385
- notify({ kind: 'opened' });
497
+ opened((event) => {
498
+ if (observers.has(notify))
499
+ notify(event);
500
+ });
386
501
  });
387
502
  return () => {
388
503
  observers.delete(notify);
@@ -399,12 +514,7 @@ export function createMemoryAdapter(options = {}) {
399
514
  return resolveScript(scripts.authorize, () => ({}), undefined);
400
515
  },
401
516
  async discover(_authorization) {
402
- return resolveScript(scripts.discover, discovery, undefined).then((answer) => {
403
- if (answer.kind === 'discovery')
404
- for (const room of answer.rooms)
405
- note(room.locator, room);
406
- return answer;
407
- });
517
+ return discovered();
408
518
  },
409
519
  reached,
410
520
  follows(locator, roomVersion, next) {
@@ -486,18 +596,39 @@ export function createMemoryAdapter(options = {}) {
486
596
  const room = rooms.find((entry) => entry.locator === request.locator);
487
597
  if (room === undefined)
488
598
  return { kind: 'room-not-readable', locator: request.locator };
489
- const blob = room.blobs?.[request.path];
599
+ const blob = heldBlob(room, request.path);
490
600
  return blob === undefined
491
601
  ? { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false }
492
602
  : { kind: 'binary', body: blob.stream(), mediaType: blob.type };
493
603
  }, request);
494
604
  },
495
605
  async link(_authorization, request) {
496
- return resolveScript(scripts.link, () => ({ kind: 'rejected', code: 'unscripted-link', fields: {}, retryable: false }), request);
606
+ return resolveScript(scripts.link, () => {
607
+ const room = rooms.find((entry) => entry.locator === request.locator);
608
+ if (room === undefined)
609
+ return { kind: 'room-not-readable', locator: request.locator };
610
+ const blob = heldBlob(room, request.path);
611
+ if (blob === undefined) {
612
+ return { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false };
613
+ }
614
+ const url = linked.get(blob) ?? URL.createObjectURL(blob);
615
+ linked.set(blob, url);
616
+ return { kind: 'link', url, name: lastName(request.path), mediaType: blob.type };
617
+ }, request);
618
+ },
619
+ async attach(_authorization, request) {
620
+ return resolveScript(scripts.attach, () => {
621
+ const room = rooms.find((entry) => entry.locator === request.locator);
622
+ if (room === undefined)
623
+ return { kind: 'room-not-readable', locator: request.locator };
624
+ const path = freePath(room, request.name);
625
+ attached.set(`${room.locator}\u0000${path}`, request.blob);
626
+ return { kind: 'attached', path, revision: `attached-${attached.size}` };
627
+ }, request);
497
628
  },
498
629
  async action(_authorization, request) {
499
630
  return resolveScript(scripts.action, () => {
500
- if (request.name.startsWith('conversation.'))
631
+ if (request.name.startsWith('conversations/'))
501
632
  return conversationAction(request);
502
633
  return { kind: 'rejected', code: 'unscripted-action', fields: {}, retryable: false };
503
634
  }, request).then((answer) => {
@@ -1,6 +1,9 @@
1
1
  import type { AcceptedMessage } from './adapter.js';
2
+ import type { JobMarker } from './job.js';
2
3
  import type { Connection } from './tables.js';
3
4
  export declare const MESSAGE_TABLES: readonly string[];
5
+ export declare const ROWS = "WITH chosen AS (\n SELECT listed.value AS conversation,\n (SELECT id FROM conversation_messages WHERE conversation = listed.value AND json_extract(message, '$.role') = 'assistant' ORDER BY position DESC, identity DESC LIMIT 1) AS answer,\n (SELECT id FROM conversation_messages WHERE conversation = listed.value AND text <> '' ORDER BY position DESC, identity DESC LIMIT 1) AS said,\n (SELECT id FROM conversation_messages WHERE conversation = listed.value AND json_extract(message, '$.role') = 'house' ORDER BY position, identity LIMIT 1) AS opened,\n (SELECT id FROM conversation_messages WHERE conversation = listed.value AND json_extract(message, '$.role') = 'house' AND json_extract(message, '$.waiting') = 1 ORDER BY position, identity LIMIT 1) AS asking\n FROM json_each(?) AS listed\n )\n SELECT chosen.conversation AS conversation,\n substr(said.text, 1, 512) AS said_text,\n json_extract(said.message, '$.role') AS said_role,\n json_extract(said.message, '$.accepted_at') AS said_at,\n substr(json_extract(answer.message, '$.failure'), 1, 512) AS failure,\n json_extract(opened.message, '$.source') AS opened_source,\n json_extract(opened.message, '$.sender') AS opened_sender,\n asking.identity AS asking_identity,\n substr(asking.text, 1, 512) AS asking_text,\n json_extract(asking.message, '$.accepted_at') AS asking_at,\n json_extract(asking.message, '$.source') AS asking_source,\n json_extract(asking.message, '$.sender') AS asking_sender\n FROM chosen\n LEFT JOIN conversation_messages said ON said.id = chosen.said\n LEFT JOIN conversation_messages answer ON answer.id = chosen.answer\n LEFT JOIN conversation_messages opened ON opened.id = chosen.opened\n LEFT JOIN conversation_messages asking ON asking.id = chosen.asking";
6
+ export declare const JOB_MARKERS = "SELECT json_extract(part.value, '$.text') AS said,\n json_extract(message, '$.accepted_at') AS at\n FROM conversation_messages, json_each(message, '$.parts') AS part\n WHERE conversation = ? AND instr(message, '\"marker\":\"job\"') > 0\n AND json_extract(part.value, '$.type') = 'marker'\n AND json_extract(part.value, '$.marker') = 'job'\n ORDER BY position, identity, part.key";
4
7
  export type HeldConversation = {
5
8
  readonly conversation: string;
6
9
  readonly read: string | null | undefined;
@@ -10,6 +13,25 @@ export type MessageWindow = {
10
13
  readonly messages: AcceptedMessage[];
11
14
  readonly more: boolean;
12
15
  };
16
+ export type HeldSource = {
17
+ readonly source: unknown;
18
+ readonly sender: string | null;
19
+ };
20
+ export type HeldRow = {
21
+ readonly conversation: string;
22
+ readonly said: {
23
+ readonly text: string;
24
+ readonly role: string | null;
25
+ readonly at: string | null;
26
+ } | null;
27
+ readonly failure: string | null;
28
+ readonly opened: HeldSource | null;
29
+ readonly asking: (HeldSource & {
30
+ readonly identity: string;
31
+ readonly text: string;
32
+ readonly at: string | null;
33
+ }) | null;
34
+ };
13
35
  export type ConversationWrite = {
14
36
  readonly conversation: string;
15
37
  readonly messages: readonly AcceptedMessage[];
@@ -18,10 +40,12 @@ export type ConversationWrite = {
18
40
  readonly partial: boolean;
19
41
  };
20
42
  };
21
- export declare function heldConversations(connection: Connection): HeldConversation[];
43
+ export declare function heldConversations(connection: Connection, conversations: readonly string[] | null): HeldConversation[];
22
44
  export declare function keptMessages(connection: Connection, { conversation, messages, read }: ConversationWrite): null;
23
- export declare function messageWindow(connection: Connection, conversation: string, limit: number, before: readonly [string, string] | null): MessageWindow;
24
- export declare function messageCount(connection: Connection, conversation: string): number;
45
+ export declare function messageWindow(connection: Connection, conversation: string, limit: number, before: readonly [string, string] | null, phrase: string | null): MessageWindow;
46
+ export declare function messageCount(connection: Connection, conversation: string, phrase: string | null): number;
47
+ export declare function conversationRows(connection: Connection, conversations: readonly string[]): HeldRow[];
48
+ export declare function jobMarkers(connection: Connection, conversation: string): JobMarker[];
25
49
  export declare function turnArrived(connection: Connection, conversation: string, turn: string): boolean;
26
50
  export declare function droppedMessages(connection: Connection, conversations: readonly string[]): void;
27
51
  export declare function forgottenMessages(connection: Connection): void;
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,aAAa,CAAC;AAE1D,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAM3C,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,eAAe,EAAE,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAE7F,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;CAC3E,CAAC;AAMF,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,gBAAgB,EAAE,CAmB5E;AAED,wBAAgB,YAAY,CAC1B,UAAU,EAAE,UAAU,EACtB,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,iBAAiB,GAClD,IAAI,CAoCN;AAYD,wBAAgB,aAAa,CAC3B,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,GACvC,aAAa,CAUf;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAMjF;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAO/F;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAW9F;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAG9D"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"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"}