@agentshouse/mdruntime 0.1.0-cb95ca2c884cf → 0.1.0-cba3c6127c178
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.
- package/README.md +19 -16
- package/dist/adapter.d.ts +149 -57
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +7 -10
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +40 -41
- package/dist/attempt.d.ts.map +1 -1
- package/dist/attempt.js +14 -1
- package/dist/binary.d.ts +0 -5
- package/dist/binary.d.ts.map +1 -1
- package/dist/binary.js +1 -1
- package/dist/bootstrap.d.ts +31 -11
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +323 -89
- package/dist/chain.d.ts +12 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +64 -0
- package/dist/change-set.d.ts +9 -14
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +11 -63
- package/dist/change.d.ts +31 -1
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +10 -1
- package/dist/client.d.ts +32 -16
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +100 -12
- package/dist/collection.d.ts +11 -7
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +38 -79
- package/dist/compile.d.ts +11 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +586 -0
- package/dist/connection.d.ts +47 -17
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +3695 -982
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/{chat/context.js → context.js} +1 -4
- package/dist/conversation.d.ts +39 -17
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +691 -259
- package/dist/digest.d.ts +7 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +113 -0
- package/dist/durability.d.ts +10 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +5 -0
- package/dist/entry.d.ts +20 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +160 -0
- package/dist/fence.d.ts +6 -0
- package/dist/fence.d.ts.map +1 -0
- package/dist/fence.js +10 -0
- package/dist/file.d.ts +2 -19
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +7 -122
- package/dist/host.d.ts +11 -3
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +126 -19
- package/dist/index.d.ts +20 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/job.d.ts +14 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/job.js +28 -0
- package/dist/live.d.ts +23 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +127 -0
- package/dist/media.d.ts +8 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +218 -0
- package/dist/memory.d.ts +18 -5
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +246 -94
- package/dist/messages.d.ts +28 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +89 -0
- package/dist/model.d.ts +11 -9
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +8 -5
- package/dist/name.d.ts +4 -0
- package/dist/name.d.ts.map +1 -0
- package/dist/name.js +122 -0
- package/dist/observation.d.ts +22 -4
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +120 -14
- package/dist/opened.d.ts +22 -0
- package/dist/opened.d.ts.map +1 -0
- package/dist/opened.js +107 -0
- package/dist/outcome.d.ts +49 -7
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +40 -0
- package/dist/overlay.d.ts +3 -4
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +100 -182
- package/dist/paths.d.ts +1 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +4 -0
- package/dist/query.d.ts +40 -183
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -464
- package/dist/reach.d.ts +18 -6
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +223 -80
- package/dist/react.d.ts +10 -6
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +7 -17
- package/dist/readiness.d.ts +50 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +27 -0
- package/dist/recovery.d.ts +26 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +224 -0
- package/dist/refresh.d.ts +17 -14
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +139 -51
- package/dist/replica.d.ts +58 -33
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +315 -154
- package/dist/room-draft.d.ts +37 -6
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +161 -74
- package/dist/rows.d.ts +30 -37
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +59 -126
- package/dist/save.d.ts +52 -9
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +543 -173
- package/dist/search.d.ts +15 -55
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +10 -121
- package/dist/seed.d.ts +16 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +75 -0
- package/dist/shared.d.ts +15 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +421 -0
- package/dist/status.d.ts +5 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +3 -1
- package/dist/store.d.ts +104 -76
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +327 -442
- package/dist/stored.d.ts +138 -0
- package/dist/stored.d.ts.map +1 -0
- package/dist/stored.js +576 -0
- package/dist/tables.d.ts +326 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +587 -0
- package/dist/transfer.d.ts +0 -5
- package/dist/transfer.d.ts.map +1 -1
- package/dist/turn.d.ts +9 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +75 -0
- package/dist/validation.d.ts +29 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +104 -0
- package/dist/windows.d.ts +11 -0
- package/dist/windows.d.ts.map +1 -0
- package/dist/windows.js +16 -0
- package/dist/wire.d.ts +173 -32
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +58 -31
- package/package.json +22 -19
- package/AGENTS.md +0 -299
- package/dist/chat/acts.d.ts +0 -34
- package/dist/chat/acts.d.ts.map +0 -1
- package/dist/chat/acts.js +0 -36
- package/dist/chat/context.d.ts +0 -3
- package/dist/chat/context.d.ts.map +0 -1
- package/dist/chat/input.d.ts +0 -9
- package/dist/chat/input.d.ts.map +0 -1
- package/dist/chat/input.js +0 -99
- package/dist/chat/kit.d.ts +0 -60
- package/dist/chat/kit.d.ts.map +0 -1
- package/dist/chat/kit.js +0 -123
- package/dist/chat/lasting.d.ts +0 -6
- package/dist/chat/lasting.d.ts.map +0 -1
- package/dist/chat/lasting.js +0 -19
- package/dist/chat/list.d.ts +0 -20
- package/dist/chat/list.d.ts.map +0 -1
- package/dist/chat/list.js +0 -59
- package/dist/chat/markdown.d.ts +0 -19
- package/dist/chat/markdown.d.ts.map +0 -1
- package/dist/chat/markdown.js +0 -187
- package/dist/chat/message.d.ts +0 -6
- package/dist/chat/message.d.ts.map +0 -1
- package/dist/chat/message.js +0 -192
- package/dist/chat/messages.d.ts +0 -21
- package/dist/chat/messages.d.ts.map +0 -1
- package/dist/chat/messages.js +0 -151
- package/dist/chat/name.d.ts +0 -6
- package/dist/chat/name.d.ts.map +0 -1
- package/dist/chat/name.js +0 -37
- package/dist/chat/opened.d.ts +0 -11
- package/dist/chat/opened.d.ts.map +0 -1
- package/dist/chat/opened.js +0 -17
- package/dist/chat/overlay.d.ts +0 -31
- package/dist/chat/overlay.d.ts.map +0 -1
- package/dist/chat/overlay.js +0 -97
- package/dist/chat/parts.d.ts +0 -57
- package/dist/chat/parts.d.ts.map +0 -1
- package/dist/chat/parts.js +0 -119
- package/dist/chat/process.d.ts +0 -20
- package/dist/chat/process.d.ts.map +0 -1
- package/dist/chat/process.js +0 -32
- package/dist/chat/provider.d.ts +0 -63
- package/dist/chat/provider.d.ts.map +0 -1
- package/dist/chat/provider.js +0 -645
- package/dist/chat/question.d.ts +0 -9
- package/dist/chat/question.d.ts.map +0 -1
- package/dist/chat/question.js +0 -80
- package/dist/chat/questions.d.ts +0 -31
- package/dist/chat/questions.d.ts.map +0 -1
- package/dist/chat/questions.js +0 -111
- package/dist/chat/read.d.ts +0 -5
- package/dist/chat/read.d.ts.map +0 -1
- package/dist/chat/read.js +0 -13
- package/dist/chat/ready.d.ts +0 -33
- package/dist/chat/ready.d.ts.map +0 -1
- package/dist/chat/ready.js +0 -91
- package/dist/chat/style.d.ts +0 -4
- package/dist/chat/style.d.ts.map +0 -1
- package/dist/chat/style.js +0 -302
- package/dist/chat.d.ts +0 -14
- package/dist/chat.d.ts.map +0 -1
- package/dist/chat.js +0 -8
- package/dist/read-side.d.ts +0 -18
- package/dist/read-side.d.ts.map +0 -1
- package/dist/read-side.js +0 -440
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: 1,
|
|
5
10
|
refreshHorizon: 1,
|
|
6
11
|
};
|
|
7
12
|
function declaredRetention(value) {
|
|
@@ -15,12 +20,7 @@ function declaredRetention(value) {
|
|
|
15
20
|
}
|
|
16
21
|
const MODEL_ABSENT = { kind: 'model-unavailable', reason: 'not-readable' };
|
|
17
22
|
function modelsOf(room) {
|
|
18
|
-
return {
|
|
19
|
-
collection: room.model ?? MODEL_ABSENT,
|
|
20
|
-
...(room.typedSelection === undefined
|
|
21
|
-
? {}
|
|
22
|
-
: { selection: room.selectionModel ?? MODEL_ABSENT }),
|
|
23
|
-
};
|
|
23
|
+
return { collection: room.model ?? MODEL_ABSENT };
|
|
24
24
|
}
|
|
25
25
|
function changedModels(room, from) {
|
|
26
26
|
const now = modelsOf(room);
|
|
@@ -53,12 +53,16 @@ export function createMemoryAdapter(options = {}) {
|
|
|
53
53
|
const listeners = new Set();
|
|
54
54
|
const modeObservers = new Set();
|
|
55
55
|
const scripted = new Map();
|
|
56
|
-
const
|
|
57
|
-
const sealed = new Set();
|
|
56
|
+
const sentTurns = new Map();
|
|
58
57
|
const historyPageSize = options.historyPageSize;
|
|
59
|
-
|
|
58
|
+
const attached = new Map();
|
|
59
|
+
const linked = new WeakMap();
|
|
60
|
+
const carried = new Map();
|
|
60
61
|
let expiring = [];
|
|
62
|
+
let openings = 0;
|
|
63
|
+
const declaredRooms = new Set();
|
|
61
64
|
let replicated = [];
|
|
65
|
+
let heldCursor = null;
|
|
62
66
|
let issued = 0;
|
|
63
67
|
function scriptConversations(entries) {
|
|
64
68
|
scripted.clear();
|
|
@@ -86,8 +90,48 @@ export function createMemoryAdapter(options = {}) {
|
|
|
86
90
|
const wanted = ordinals.get(`${locator}\u0000${target}`);
|
|
87
91
|
return held !== undefined && wanted !== undefined && held >= wanted;
|
|
88
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
|
+
}
|
|
89
131
|
function retain(entries) {
|
|
90
132
|
for (const room of entries) {
|
|
133
|
+
for (const [path, content] of Object.entries(room.documents ?? {}))
|
|
134
|
+
factsAt(room, path, content);
|
|
91
135
|
note(room.locator, room);
|
|
92
136
|
const versions = served.get(room.locator) ?? new Map();
|
|
93
137
|
versions.set(room.roomVersion, room);
|
|
@@ -120,23 +164,39 @@ export function createMemoryAdapter(options = {}) {
|
|
|
120
164
|
roomVersion: room.roomVersion,
|
|
121
165
|
...(opening ? { models: modelsOf(room) } : {}),
|
|
122
166
|
documents: named(room.documents ?? {}, taken),
|
|
167
|
+
facts: factsIn(room, taken),
|
|
123
168
|
revisions: named(room.revisions ?? {}, [...taken, ...targets]),
|
|
169
|
+
...(room.fileIds === undefined
|
|
170
|
+
? {}
|
|
171
|
+
: { fileIds: named(room.fileIds, [...taken, ...targets]) }),
|
|
124
172
|
changes: named(room.changes ?? {}, [...taken, ...targets]),
|
|
125
173
|
binaryFiles: named(room.binaryFiles ?? {}, targets),
|
|
174
|
+
...(opening && room.folders !== undefined ? { folders: room.folders } : {}),
|
|
126
175
|
...(next < paths.length ? { pageToken: taken.at(-1) } : {}),
|
|
127
176
|
};
|
|
128
177
|
}
|
|
129
|
-
function
|
|
178
|
+
function foldersMoved(room, from) {
|
|
179
|
+
const now = new Set(room.folders ?? []);
|
|
180
|
+
const before = new Set(from.folders ?? []);
|
|
181
|
+
const folders = [...now].filter((folder) => !before.has(folder)).sort();
|
|
182
|
+
const folderRemovals = [...before].filter((folder) => !now.has(folder)).sort();
|
|
183
|
+
return {
|
|
184
|
+
...(folders.length === 0 ? {} : { folders }),
|
|
185
|
+
...(folderRemovals.length === 0 ? {} : { folderRemovals }),
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function advancement(room, from, at, whole = false) {
|
|
130
189
|
const current = room.documents ?? {};
|
|
131
190
|
const previous = from.documents ?? {};
|
|
132
191
|
const currentTargets = new Set(Object.keys(room.binaryFiles ?? {}));
|
|
133
192
|
const previousTargets = new Set(Object.keys(from.binaryFiles ?? {}));
|
|
134
193
|
const headMoved = (path) => room.revisions?.[path] !== from.revisions?.[path] ||
|
|
194
|
+
room.fileIds?.[path] !== from.fileIds?.[path] ||
|
|
135
195
|
!sameChange(room.changes?.[path], from.changes?.[path]);
|
|
136
196
|
const changed = Object.keys(current)
|
|
137
197
|
.filter((path) => current[path] !== previous[path] || headMoved(path))
|
|
138
198
|
.sort();
|
|
139
|
-
const size = pageSize ?? changed.length;
|
|
199
|
+
const size = whole ? changed.length : (pageSize ?? changed.length);
|
|
140
200
|
const taken = changed.slice(at, at + size);
|
|
141
201
|
const next = at + taken.length;
|
|
142
202
|
const opening = at === 0;
|
|
@@ -152,9 +212,14 @@ export function createMemoryAdapter(options = {}) {
|
|
|
152
212
|
roomVersion: room.roomVersion,
|
|
153
213
|
...(models === undefined ? {} : { models }),
|
|
154
214
|
documents: named(current, taken),
|
|
215
|
+
facts: factsIn(room, taken),
|
|
155
216
|
revisions: named(room.revisions ?? {}, [...taken, ...targets]),
|
|
217
|
+
...(room.fileIds === undefined
|
|
218
|
+
? {}
|
|
219
|
+
: { fileIds: named(room.fileIds, [...taken, ...targets]) }),
|
|
156
220
|
changes: named(room.changes ?? {}, [...taken, ...targets]),
|
|
157
221
|
binaryFiles: named(room.binaryFiles ?? {}, targets),
|
|
222
|
+
...(opening ? foldersMoved(room, from) : {}),
|
|
158
223
|
removals: opening
|
|
159
224
|
? [
|
|
160
225
|
...Object.keys(previous).filter((path) => !(path in current) && !currentTargets.has(path)),
|
|
@@ -164,6 +229,26 @@ export function createMemoryAdapter(options = {}) {
|
|
|
164
229
|
...(pageToken === undefined ? {} : { pageToken }),
|
|
165
230
|
};
|
|
166
231
|
}
|
|
232
|
+
function carry(locator) {
|
|
233
|
+
const at = carried.get(locator);
|
|
234
|
+
const current = rooms.find((entry) => entry.locator === locator);
|
|
235
|
+
if (at === undefined || current === undefined || current.roomVersion === at)
|
|
236
|
+
return;
|
|
237
|
+
const from = served.get(locator)?.get(at);
|
|
238
|
+
carried.set(locator, current.roomVersion);
|
|
239
|
+
if (from === undefined)
|
|
240
|
+
return;
|
|
241
|
+
const page = advancement(current, from, 0, true);
|
|
242
|
+
for (const observer of observers) {
|
|
243
|
+
observer({
|
|
244
|
+
kind: 'room-change',
|
|
245
|
+
locator,
|
|
246
|
+
from: at,
|
|
247
|
+
roomVersion: current.roomVersion,
|
|
248
|
+
read: async () => page,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
167
252
|
function pageOf(conversation, after) {
|
|
168
253
|
const messages = conversation.messages ?? [];
|
|
169
254
|
const first = typeof after === 'string' ? messages.findIndex((message) => message.position > after) : 0;
|
|
@@ -178,16 +263,13 @@ export function createMemoryAdapter(options = {}) {
|
|
|
178
263
|
: {}),
|
|
179
264
|
};
|
|
180
265
|
}
|
|
181
|
-
function historiesOf(
|
|
182
|
-
const asked = input?.conversations;
|
|
183
|
-
if (!Array.isArray(asked))
|
|
184
|
-
return { kind: 'rejected', code: 'invalid_request', fields: {}, retryable: false };
|
|
266
|
+
function historiesOf(request) {
|
|
185
267
|
const histories = [];
|
|
186
|
-
for (const entry of
|
|
187
|
-
const conversation =
|
|
268
|
+
for (const entry of request.conversations) {
|
|
269
|
+
const conversation = scripted.get(entry.conversation);
|
|
188
270
|
if (conversation === undefined)
|
|
189
271
|
continue;
|
|
190
|
-
const outcome = conversation.outcomes?.
|
|
272
|
+
const outcome = conversation.outcomes?.history;
|
|
191
273
|
if (outcome !== undefined)
|
|
192
274
|
return outcome;
|
|
193
275
|
histories.push(pageOf(conversation, entry.after));
|
|
@@ -198,15 +280,13 @@ export function createMemoryAdapter(options = {}) {
|
|
|
198
280
|
function conversationAction(request) {
|
|
199
281
|
const name = request.name;
|
|
200
282
|
const input = (request.input ?? {});
|
|
201
|
-
if (name === '
|
|
202
|
-
return historiesOf(request.input);
|
|
203
|
-
if (name === 'conversation.create') {
|
|
283
|
+
if (name === 'conversations/create') {
|
|
204
284
|
issued += 1;
|
|
205
285
|
const conversation = `conversation-${issued}`;
|
|
206
286
|
scripted.set(conversation, { conversation });
|
|
207
|
-
return { kind: 'action', result: { conversation } };
|
|
287
|
+
return { kind: 'action', result: { conversation_id: conversation } };
|
|
208
288
|
}
|
|
209
|
-
const named = typeof input.
|
|
289
|
+
const named = typeof input.conversation_id === 'string' ? input.conversation_id : '';
|
|
210
290
|
const conversation = scripted.get(named);
|
|
211
291
|
if (conversation === undefined) {
|
|
212
292
|
return {
|
|
@@ -219,28 +299,28 @@ export function createMemoryAdapter(options = {}) {
|
|
|
219
299
|
const outcome = conversation.outcomes?.[name];
|
|
220
300
|
if (outcome !== undefined)
|
|
221
301
|
return outcome;
|
|
222
|
-
if (name === '
|
|
302
|
+
if (name === 'conversations/send') {
|
|
303
|
+
const operation = typeof input.operation_id === 'string' ? input.operation_id : null;
|
|
304
|
+
const answered = operation === null ? undefined : sentTurns.get(operation);
|
|
305
|
+
if (answered !== undefined)
|
|
306
|
+
return { kind: 'action', result: { message_id: answered } };
|
|
223
307
|
issued += 1;
|
|
224
308
|
const turn = `turn-${issued}`;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if (name === 'conversation.cancel') {
|
|
229
|
-
const turn = typeof input.turn === 'string' ? input.turn : '';
|
|
230
|
-
if (sealed.has(turn) || !unsealed.has(turn)) {
|
|
231
|
-
return { kind: 'rejected', code: 'turn-sealed', fields: { turn }, retryable: false };
|
|
232
|
-
}
|
|
233
|
-
unsealed.delete(turn);
|
|
234
|
-
return { kind: 'action', result: { turn, cancelled: true } };
|
|
309
|
+
if (operation !== null)
|
|
310
|
+
sentTurns.set(operation, turn);
|
|
311
|
+
return { kind: 'action', result: { message_id: turn } };
|
|
235
312
|
}
|
|
236
|
-
if (name === '
|
|
313
|
+
if (name === 'conversations/delete')
|
|
237
314
|
scripted.delete(named);
|
|
238
|
-
return { kind: 'action', result: {
|
|
315
|
+
return { kind: 'action', result: { conversation_id: named } };
|
|
239
316
|
}
|
|
240
317
|
function announceList() {
|
|
241
|
-
const entries = [...scripted.values()].flatMap(({ conversation, listed }) => listed === undefined
|
|
242
|
-
|
|
243
|
-
|
|
318
|
+
const entries = [...scripted.values()].flatMap(({ conversation, listed, messages = [] }) => listed === undefined
|
|
319
|
+
? []
|
|
320
|
+
: [{ conversation, ...listed, newest: messages.at(-1)?.position ?? null }]);
|
|
321
|
+
for (const listener of listeners) {
|
|
322
|
+
listener({ kind: 'list', entries, whole: true, more: false, cursor: null });
|
|
323
|
+
}
|
|
244
324
|
}
|
|
245
325
|
function discovery() {
|
|
246
326
|
return {
|
|
@@ -252,7 +332,42 @@ export function createMemoryAdapter(options = {}) {
|
|
|
252
332
|
...(options.member === undefined ? {} : { member: options.member }),
|
|
253
333
|
};
|
|
254
334
|
}
|
|
335
|
+
function discovered() {
|
|
336
|
+
return resolveScript(scripts.discover, discovery, undefined).then((answer) => {
|
|
337
|
+
if (answer.kind === 'discovery')
|
|
338
|
+
for (const room of answer.rooms)
|
|
339
|
+
note(room.locator, room);
|
|
340
|
+
return answer;
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
function opened(notify) {
|
|
344
|
+
const opening = ++openings;
|
|
345
|
+
notify({ kind: 'opened' });
|
|
346
|
+
void discovered()
|
|
347
|
+
.catch(() => ({ kind: 'transport-uncertainty' }))
|
|
348
|
+
.then((answer) => {
|
|
349
|
+
if (opening !== openings)
|
|
350
|
+
return;
|
|
351
|
+
if (answer.kind !== 'discovery') {
|
|
352
|
+
openings += 1;
|
|
353
|
+
carried.clear();
|
|
354
|
+
const revoked = answer.kind === 'revoked-authorization';
|
|
355
|
+
notify({ kind: 'closed', revoked, lost: !revoked, authorityChanged: false });
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
declaredRooms.clear();
|
|
359
|
+
for (const room of answer.rooms)
|
|
360
|
+
declaredRooms.add(room.locator);
|
|
361
|
+
notify({ kind: 'discovered', discovery: answer });
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
function emitAll(event) {
|
|
365
|
+
for (const observer of observers)
|
|
366
|
+
observer(event);
|
|
367
|
+
}
|
|
255
368
|
return {
|
|
369
|
+
...(options.roomTypes === undefined ? {} : { roomTypes: options.roomTypes }),
|
|
370
|
+
...(options.typedPaths === undefined ? {} : { typedPaths: options.typedPaths }),
|
|
256
371
|
setRooms(next) {
|
|
257
372
|
rooms = [...next];
|
|
258
373
|
retain(rooms);
|
|
@@ -261,26 +376,37 @@ export function createMemoryAdapter(options = {}) {
|
|
|
261
376
|
scriptConversations(next);
|
|
262
377
|
},
|
|
263
378
|
open() {
|
|
379
|
+
carried.clear();
|
|
264
380
|
announceList();
|
|
265
|
-
|
|
266
|
-
observer({ kind: 'opened' });
|
|
381
|
+
opened(emitAll);
|
|
267
382
|
},
|
|
268
|
-
close(revoked = false) {
|
|
269
|
-
|
|
270
|
-
|
|
383
|
+
close({ revoked = false, lost = !revoked, authorityChanged = false } = {}) {
|
|
384
|
+
openings += 1;
|
|
385
|
+
carried.clear();
|
|
386
|
+
emitAll({ kind: 'closed', revoked, lost, authorityChanged });
|
|
271
387
|
},
|
|
272
388
|
expire(locators) {
|
|
273
389
|
expiring = [...locators];
|
|
274
390
|
},
|
|
275
|
-
notifyRoomVersion({ locator
|
|
276
|
-
|
|
277
|
-
|
|
391
|
+
notifyRoomVersion({ locator }) {
|
|
392
|
+
carry(locator);
|
|
393
|
+
},
|
|
394
|
+
notifyAuthorityChanged(locators) {
|
|
395
|
+
const named = new Set(locators ?? [...declaredRooms, ...rooms.map((room) => room.locator)]);
|
|
396
|
+
const listed = rooms.filter((room) => named.has(room.locator));
|
|
397
|
+
const left = [...named].filter((locator) => !listed.some((room) => room.locator === locator));
|
|
398
|
+
for (const room of listed)
|
|
399
|
+
declaredRooms.add(room.locator);
|
|
400
|
+
for (const locator of left)
|
|
401
|
+
declaredRooms.delete(locator);
|
|
402
|
+
emitAll({
|
|
403
|
+
kind: 'authority-changed',
|
|
404
|
+
rooms: listed.map(declared),
|
|
405
|
+
left,
|
|
406
|
+
...(options.member === undefined ? {} : { member: options.member }),
|
|
407
|
+
});
|
|
278
408
|
},
|
|
279
409
|
notifyConversation(event) {
|
|
280
|
-
if (event.kind === 'accepted-message') {
|
|
281
|
-
sealed.add(event.message.turn);
|
|
282
|
-
unsealed.delete(event.message.turn);
|
|
283
|
-
}
|
|
284
410
|
for (const listener of listeners)
|
|
285
411
|
listener(event);
|
|
286
412
|
},
|
|
@@ -289,21 +415,31 @@ export function createMemoryAdapter(options = {}) {
|
|
|
289
415
|
observer(mode);
|
|
290
416
|
},
|
|
291
417
|
heldRooms() {
|
|
292
|
-
return
|
|
418
|
+
return [...carried.keys()].sort();
|
|
293
419
|
},
|
|
294
420
|
replicatedConversations() {
|
|
295
421
|
return replicated;
|
|
296
422
|
},
|
|
423
|
+
heldConversationCursor() {
|
|
424
|
+
return heldCursor;
|
|
425
|
+
},
|
|
297
426
|
conversations: {
|
|
298
427
|
replicate(next) {
|
|
299
428
|
replicated = [...next];
|
|
300
429
|
},
|
|
430
|
+
hold(cursor) {
|
|
431
|
+
heldCursor = cursor;
|
|
432
|
+
},
|
|
301
433
|
observe(notify) {
|
|
302
434
|
listeners.add(notify);
|
|
303
435
|
return () => {
|
|
304
436
|
listeners.delete(notify);
|
|
305
437
|
};
|
|
306
438
|
},
|
|
439
|
+
history: (request) => Promise.resolve(historiesOf(request)),
|
|
440
|
+
...(options.agents === undefined
|
|
441
|
+
? {}
|
|
442
|
+
: { agents: () => Promise.resolve(structuredClone(options.agents)) }),
|
|
307
443
|
},
|
|
308
444
|
serviceMode: {
|
|
309
445
|
observe(notify) {
|
|
@@ -315,29 +451,30 @@ export function createMemoryAdapter(options = {}) {
|
|
|
315
451
|
},
|
|
316
452
|
roomStream: {
|
|
317
453
|
hold(asked) {
|
|
318
|
-
|
|
319
|
-
const
|
|
320
|
-
expiring = [];
|
|
321
|
-
const moved = asked.flatMap((room) => {
|
|
454
|
+
const named = asked.map((room) => room.locator);
|
|
455
|
+
const unknown = asked.flatMap((room) => {
|
|
322
456
|
const current = rooms.find((entry) => entry.locator === room.locator);
|
|
457
|
+
const version = room.roomVersion;
|
|
323
458
|
return current === undefined ||
|
|
324
|
-
|
|
325
|
-
|
|
459
|
+
(version !== null &&
|
|
460
|
+
(current.roomVersion === version || served.get(room.locator)?.has(version) === true))
|
|
326
461
|
? []
|
|
327
|
-
: [
|
|
462
|
+
: [room.locator];
|
|
328
463
|
});
|
|
464
|
+
const expired = [
|
|
465
|
+
...new Set([...expiring.filter((locator) => named.includes(locator)), ...unknown]),
|
|
466
|
+
];
|
|
467
|
+
expiring = expiring.filter((locator) => !named.includes(locator));
|
|
329
468
|
queueMicrotask(() => {
|
|
330
|
-
for (const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
469
|
+
for (const room of asked) {
|
|
470
|
+
if (expired.includes(room.locator) || room.roomVersion === null)
|
|
471
|
+
carried.delete(room.locator);
|
|
472
|
+
else {
|
|
473
|
+
carried.set(room.locator, room.roomVersion);
|
|
474
|
+
carry(room.locator);
|
|
475
|
+
}
|
|
334
476
|
}
|
|
335
|
-
|
|
336
|
-
},
|
|
337
|
-
reopen() {
|
|
338
|
-
queueMicrotask(() => {
|
|
339
|
-
for (const observer of observers)
|
|
340
|
-
observer({ kind: 'closed', revoked: false });
|
|
477
|
+
emitAll({ kind: 'caught-up', expired });
|
|
341
478
|
});
|
|
342
479
|
},
|
|
343
480
|
observe(notify) {
|
|
@@ -346,11 +483,14 @@ export function createMemoryAdapter(options = {}) {
|
|
|
346
483
|
if (!observers.has(notify))
|
|
347
484
|
return;
|
|
348
485
|
if (options.unopened === true) {
|
|
349
|
-
notify({ kind: 'closed', revoked: false });
|
|
486
|
+
notify({ kind: 'closed', revoked: false, lost: true, authorityChanged: false });
|
|
350
487
|
return;
|
|
351
488
|
}
|
|
352
489
|
announceList();
|
|
353
|
-
|
|
490
|
+
opened((event) => {
|
|
491
|
+
if (observers.has(notify))
|
|
492
|
+
notify(event);
|
|
493
|
+
});
|
|
354
494
|
});
|
|
355
495
|
return () => {
|
|
356
496
|
observers.delete(notify);
|
|
@@ -367,12 +507,7 @@ export function createMemoryAdapter(options = {}) {
|
|
|
367
507
|
return resolveScript(scripts.authorize, () => ({}), undefined);
|
|
368
508
|
},
|
|
369
509
|
async discover(_authorization) {
|
|
370
|
-
return
|
|
371
|
-
if (answer.kind === 'discovery')
|
|
372
|
-
for (const room of answer.rooms)
|
|
373
|
-
note(room.locator, room);
|
|
374
|
-
return answer;
|
|
375
|
-
});
|
|
510
|
+
return discovered();
|
|
376
511
|
},
|
|
377
512
|
reached,
|
|
378
513
|
follows(locator, roomVersion, next) {
|
|
@@ -429,7 +564,13 @@ export function createMemoryAdapter(options = {}) {
|
|
|
429
564
|
retryable: false,
|
|
430
565
|
affected: {},
|
|
431
566
|
};
|
|
432
|
-
}, request).then((answer) =>
|
|
567
|
+
}, request).then((answer) => {
|
|
568
|
+
const destination = request.changeBatch.between?.destination.room;
|
|
569
|
+
if (answer.kind === 'accepted' && answer.destination !== undefined && destination) {
|
|
570
|
+
note(destination, answer.destination);
|
|
571
|
+
}
|
|
572
|
+
return note(request.locator, answer);
|
|
573
|
+
});
|
|
433
574
|
},
|
|
434
575
|
async history(_authorization, request) {
|
|
435
576
|
return resolveScript(scripts.history, () => {
|
|
@@ -445,31 +586,42 @@ export function createMemoryAdapter(options = {}) {
|
|
|
445
586
|
},
|
|
446
587
|
async binary(_authorization, request) {
|
|
447
588
|
return resolveScript(scripts.binary, () => {
|
|
448
|
-
if (request.kind === 'reference') {
|
|
449
|
-
return { kind: 'rejected', code: 'unscripted-binary', fields: {}, retryable: false };
|
|
450
|
-
}
|
|
451
589
|
const room = rooms.find((entry) => entry.locator === request.locator);
|
|
452
590
|
if (room === undefined)
|
|
453
591
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
454
|
-
const blob = room
|
|
592
|
+
const blob = heldBlob(room, request.path);
|
|
455
593
|
return blob === undefined
|
|
456
594
|
? { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false }
|
|
457
|
-
: { kind: 'binary', blob };
|
|
595
|
+
: { kind: 'binary', body: blob.stream(), mediaType: blob.type };
|
|
596
|
+
}, request);
|
|
597
|
+
},
|
|
598
|
+
async link(_authorization, request) {
|
|
599
|
+
return resolveScript(scripts.link, () => {
|
|
600
|
+
const room = rooms.find((entry) => entry.locator === request.locator);
|
|
601
|
+
if (room === undefined)
|
|
602
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
603
|
+
const blob = heldBlob(room, request.path);
|
|
604
|
+
if (blob === undefined) {
|
|
605
|
+
return { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false };
|
|
606
|
+
}
|
|
607
|
+
const url = linked.get(blob) ?? URL.createObjectURL(blob);
|
|
608
|
+
linked.set(blob, url);
|
|
609
|
+
return { kind: 'link', url, name: lastName(request.path), mediaType: blob.type };
|
|
458
610
|
}, request);
|
|
459
611
|
},
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
return
|
|
468
|
-
|
|
612
|
+
async attach(_authorization, request) {
|
|
613
|
+
return resolveScript(scripts.attach, () => {
|
|
614
|
+
const room = rooms.find((entry) => entry.locator === request.locator);
|
|
615
|
+
if (room === undefined)
|
|
616
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
617
|
+
const path = freePath(room, request.name);
|
|
618
|
+
attached.set(`${room.locator}\u0000${path}`, request.blob);
|
|
619
|
+
return { kind: 'attached', path, revision: `attached-${attached.size}` };
|
|
620
|
+
}, request);
|
|
469
621
|
},
|
|
470
622
|
async action(_authorization, request) {
|
|
471
623
|
return resolveScript(scripts.action, () => {
|
|
472
|
-
if (request.name.startsWith('
|
|
624
|
+
if (request.name.startsWith('conversations/'))
|
|
473
625
|
return conversationAction(request);
|
|
474
626
|
return { kind: 'rejected', code: 'unscripted-action', fields: {}, retryable: false };
|
|
475
627
|
}, request).then((answer) => {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AcceptedMessage } from './adapter.js';
|
|
2
|
+
import type { Connection } from './tables.js';
|
|
3
|
+
export declare const MESSAGE_TABLES: readonly string[];
|
|
4
|
+
export type HeldConversation = {
|
|
5
|
+
readonly conversation: string;
|
|
6
|
+
readonly read: string | null | undefined;
|
|
7
|
+
readonly partial: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type MessageWindow = {
|
|
10
|
+
readonly messages: AcceptedMessage[];
|
|
11
|
+
readonly more: boolean;
|
|
12
|
+
};
|
|
13
|
+
export type ConversationWrite = {
|
|
14
|
+
readonly conversation: string;
|
|
15
|
+
readonly messages: readonly AcceptedMessage[];
|
|
16
|
+
readonly read?: {
|
|
17
|
+
readonly at: string | null;
|
|
18
|
+
readonly partial: boolean;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export declare function heldConversations(connection: Connection): HeldConversation[];
|
|
22
|
+
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, phrase: string | null): MessageWindow;
|
|
24
|
+
export declare function messageCount(connection: Connection, conversation: string, phrase: string | null): number;
|
|
25
|
+
export declare function turnArrived(connection: Connection, conversation: string, turn: string): boolean;
|
|
26
|
+
export declare function droppedMessages(connection: Connection, conversations: readonly string[]): void;
|
|
27
|
+
export declare function forgottenMessages(connection: Connection): void;
|
|
28
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,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,CA+BN;AAkBD,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,EACxC,MAAM,EAAE,MAAM,GAAG,IAAI,GACpB,aAAa,CAWf;AAED,wBAAgB,YAAY,CAC1B,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GAAG,IAAI,GACpB,MAAM,CAOR;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,CAK9F;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAG9D"}
|
package/dist/messages.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { sql } from '@agentshouse/mdmodel';
|
|
2
|
+
export const MESSAGE_TABLES = [
|
|
3
|
+
'CREATE TABLE conversation_reads (conversation TEXT PRIMARY KEY, read TEXT, partial INTEGER NOT NULL) WITHOUT ROWID',
|
|
4
|
+
'CREATE TABLE conversation_messages (id INTEGER PRIMARY KEY, conversation TEXT NOT NULL, identity TEXT NOT NULL, turn TEXT NOT NULL, position TEXT NOT NULL, message TEXT NOT NULL, text TEXT NOT NULL)',
|
|
5
|
+
'CREATE UNIQUE INDEX conversation_messages_identity ON conversation_messages (conversation, identity)',
|
|
6
|
+
'CREATE INDEX conversation_messages_position ON conversation_messages (conversation, position, identity)',
|
|
7
|
+
'CREATE INDEX conversation_messages_turn ON conversation_messages (conversation, turn)',
|
|
8
|
+
];
|
|
9
|
+
function text(value) {
|
|
10
|
+
return typeof value === 'string' ? value : String(value);
|
|
11
|
+
}
|
|
12
|
+
export function heldConversations(connection) {
|
|
13
|
+
const held = new Map();
|
|
14
|
+
for (const row of connection.rows('SELECT conversation, read, partial FROM conversation_reads')) {
|
|
15
|
+
held.set(text(row.conversation), {
|
|
16
|
+
conversation: text(row.conversation),
|
|
17
|
+
read: row.read === null ? null : text(row.read),
|
|
18
|
+
partial: Number(row.partial) === 1,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
for (const row of connection.rows('SELECT DISTINCT conversation FROM conversation_messages WHERE conversation NOT IN (SELECT conversation FROM conversation_reads)')) {
|
|
22
|
+
held.set(text(row.conversation), {
|
|
23
|
+
conversation: text(row.conversation),
|
|
24
|
+
read: undefined,
|
|
25
|
+
partial: true,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return [...held.values()];
|
|
29
|
+
}
|
|
30
|
+
export function keptMessages(connection, { conversation, messages, read }) {
|
|
31
|
+
for (const message of messages) {
|
|
32
|
+
const [held] = connection.rows('SELECT id FROM conversation_messages WHERE conversation = ? AND identity = ?', [conversation, message.identity]);
|
|
33
|
+
const record = [
|
|
34
|
+
message.turn,
|
|
35
|
+
message.position,
|
|
36
|
+
JSON.stringify(message.message ?? null),
|
|
37
|
+
message.text,
|
|
38
|
+
];
|
|
39
|
+
if (held === undefined) {
|
|
40
|
+
connection.run('INSERT INTO conversation_messages (conversation, identity, turn, position, message, text) VALUES (?, ?, ?, ?, ?, ?)', [conversation, message.identity, ...record]);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
connection.run('UPDATE conversation_messages SET turn = ?, position = ?, message = ?, text = ? WHERE id = ?', [...record, held.id]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (read !== undefined) {
|
|
47
|
+
connection.run('INSERT OR REPLACE INTO conversation_reads (conversation, read, partial) VALUES (?, ?, ?)', [conversation, read.at, read.partial ? 1 : 0]);
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
function accepted(row) {
|
|
52
|
+
return {
|
|
53
|
+
identity: text(row.identity),
|
|
54
|
+
turn: text(row.turn),
|
|
55
|
+
position: text(row.position),
|
|
56
|
+
text: text(row.text),
|
|
57
|
+
message: JSON.parse(text(row.message)),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function holding(phrase) {
|
|
61
|
+
return phrase === null
|
|
62
|
+
? { sql: '', bind: [] }
|
|
63
|
+
: { sql: ` AND ${sql.HOLDS_PHRASE}(text, ?)`, bind: [phrase.toLowerCase()] };
|
|
64
|
+
}
|
|
65
|
+
export function messageWindow(connection, conversation, limit, before, phrase) {
|
|
66
|
+
const held = holding(phrase);
|
|
67
|
+
const rows = connection.rows(`SELECT identity, turn, position, message, text FROM conversation_messages WHERE conversation = ?${held.sql}${before === null ? '' : ' AND (position < ? OR (position = ? AND identity < ?))'} ORDER BY position DESC, identity DESC LIMIT ?`, before === null
|
|
68
|
+
? [conversation, ...held.bind, limit + 1]
|
|
69
|
+
: [conversation, ...held.bind, before[0], before[0], before[1], limit + 1]);
|
|
70
|
+
return { messages: rows.slice(0, limit).map(accepted).reverse(), more: rows.length > limit };
|
|
71
|
+
}
|
|
72
|
+
export function messageCount(connection, conversation, phrase) {
|
|
73
|
+
const held = holding(phrase);
|
|
74
|
+
const [counted] = connection.rows(`SELECT count(*) AS held FROM conversation_messages WHERE conversation = ?${held.sql}`, [conversation, ...held.bind]);
|
|
75
|
+
return Number(counted?.held ?? 0);
|
|
76
|
+
}
|
|
77
|
+
export function turnArrived(connection, conversation, turn) {
|
|
78
|
+
return (connection.rows('SELECT 1 FROM conversation_messages WHERE conversation = ? AND turn = ? LIMIT 1', [conversation, turn]).length > 0);
|
|
79
|
+
}
|
|
80
|
+
export function droppedMessages(connection, conversations) {
|
|
81
|
+
for (const conversation of conversations) {
|
|
82
|
+
connection.run('DELETE FROM conversation_messages WHERE conversation = ?', [conversation]);
|
|
83
|
+
connection.run('DELETE FROM conversation_reads WHERE conversation = ?', [conversation]);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function forgottenMessages(connection) {
|
|
87
|
+
connection.run('DELETE FROM conversation_messages');
|
|
88
|
+
connection.run('DELETE FROM conversation_reads');
|
|
89
|
+
}
|