@agentshouse/mdruntime 0.1.0-c1db74d14e5fa → 0.1.0-c1ffd2bc60355
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/AGENTS.md +268 -251
- package/README.md +9 -8
- package/dist/adapter.d.ts +76 -40
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +4 -1
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +13 -3
- 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 +26 -7
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +148 -78
- package/dist/chain.d.ts +11 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +64 -0
- package/dist/change-set.d.ts +9 -16
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +11 -88
- package/dist/change.d.ts +31 -1
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +10 -1
- package/dist/chat/earlier.d.ts +10 -0
- package/dist/chat/earlier.d.ts.map +1 -0
- package/dist/chat/earlier.js +54 -0
- package/dist/chat/house.d.ts +25 -0
- package/dist/chat/house.d.ts.map +1 -0
- package/dist/chat/house.js +38 -0
- package/dist/chat/input.d.ts +3 -1
- package/dist/chat/input.d.ts.map +1 -1
- package/dist/chat/input.js +11 -18
- package/dist/chat/kit.d.ts +2 -1
- package/dist/chat/kit.d.ts.map +1 -1
- package/dist/chat/kit.js +3 -3
- package/dist/chat/list.d.ts +3 -1
- package/dist/chat/list.d.ts.map +1 -1
- package/dist/chat/list.js +47 -6
- package/dist/chat/markdown.d.ts +2 -2
- package/dist/chat/markdown.d.ts.map +1 -1
- package/dist/chat/markdown.js +80 -39
- package/dist/chat/message.d.ts +1 -0
- package/dist/chat/message.d.ts.map +1 -1
- package/dist/chat/message.js +73 -11
- package/dist/chat/messages.d.ts +15 -1
- package/dist/chat/messages.d.ts.map +1 -1
- package/dist/chat/messages.js +82 -23
- package/dist/chat/name.d.ts +1 -0
- package/dist/chat/name.d.ts.map +1 -1
- package/dist/chat/name.js +1 -1
- package/dist/chat/opened.d.ts +1 -0
- package/dist/chat/opened.d.ts.map +1 -1
- package/dist/chat/opened.js +2 -0
- package/dist/chat/overlay.d.ts +1 -0
- package/dist/chat/overlay.d.ts.map +1 -1
- package/dist/chat/overlay.js +11 -5
- package/dist/chat/parts.d.ts +1 -0
- package/dist/chat/parts.d.ts.map +1 -1
- package/dist/chat/parts.js +3 -2
- package/dist/chat/pieces.d.ts +62 -0
- package/dist/chat/pieces.d.ts.map +1 -0
- package/dist/chat/pieces.js +1110 -0
- package/dist/chat/provider.d.ts +9 -2
- package/dist/chat/provider.d.ts.map +1 -1
- package/dist/chat/provider.js +121 -106
- package/dist/chat/question.d.ts +1 -1
- package/dist/chat/question.d.ts.map +1 -1
- package/dist/chat/question.js +7 -18
- package/dist/chat/ready.d.ts +5 -1
- package/dist/chat/ready.d.ts.map +1 -1
- package/dist/chat/ready.js +7 -5
- package/dist/chat/room-file.d.ts +8 -0
- package/dist/chat/room-file.d.ts.map +1 -0
- package/dist/chat/room-file.js +72 -0
- package/dist/chat/steady.d.ts +3 -0
- package/dist/chat/steady.d.ts.map +1 -0
- package/dist/chat/steady.js +26 -0
- package/dist/chat/style.d.ts.map +1 -1
- package/dist/chat/style.js +6 -1
- package/dist/chat.d.ts +3 -2
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +2 -2
- package/dist/client.d.ts +15 -6
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +47 -8
- package/dist/collection.d.ts +6 -19
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +30 -287
- package/dist/compile.d.ts +16 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +721 -0
- package/dist/connection.d.ts +25 -12
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +2615 -946
- package/dist/conversation.d.ts +18 -3
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +341 -107
- 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 +3 -5
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +5 -0
- package/dist/entry.d.ts +3 -1
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +18 -23
- package/dist/file.d.ts +2 -10
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +3 -120
- package/dist/fulltext.d.ts +27 -0
- package/dist/fulltext.d.ts.map +1 -0
- package/dist/fulltext.js +368 -0
- package/dist/host.d.ts +10 -8
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +95 -18
- package/dist/identity.d.ts +0 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +0 -3
- package/dist/index.d.ts +15 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/live.d.ts +22 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +123 -0
- package/dist/memory.d.ts +8 -3
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +43 -25
- package/dist/messages.d.ts +28 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +90 -0
- package/dist/model.d.ts +3 -2
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +1 -1
- 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 +14 -0
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +93 -3
- 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 +34 -4
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +25 -0
- package/dist/overlay.d.ts +3 -5
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +95 -156
- package/dist/query.d.ts +33 -2
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +48 -7
- package/dist/reach.d.ts +6 -5
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +170 -62
- package/dist/react.d.ts +3 -3
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +7 -17
- package/dist/readiness.d.ts +20 -9
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +6 -6
- package/dist/reads.d.ts +11 -0
- package/dist/reads.d.ts.map +1 -0
- package/dist/reads.js +100 -0
- package/dist/recovery.d.ts +2 -2
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +8 -7
- package/dist/refresh.d.ts +16 -15
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +112 -45
- package/dist/replica.d.ts +57 -33
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +295 -177
- package/dist/room-draft.d.ts +17 -2
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +65 -25
- package/dist/rows.d.ts +29 -29
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +113 -62
- package/dist/save.d.ts +51 -14
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +518 -180
- package/dist/search.d.ts +32 -43
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +37 -168
- 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 +8 -3
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +129 -66
- package/dist/store.d.ts +99 -88
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +335 -641
- package/dist/stored.d.ts +145 -0
- package/dist/stored.d.ts.map +1 -0
- package/dist/stored.js +567 -0
- package/dist/tables.d.ts +354 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +624 -0
- package/dist/turn.d.ts +5 -3
- package/dist/turn.d.ts.map +1 -1
- package/dist/turn.js +51 -21
- package/dist/validation.d.ts +30 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +162 -0
- package/dist/windows.d.ts +22 -0
- package/dist/windows.d.ts.map +1 -0
- package/dist/windows.js +89 -0
- package/dist/wire.d.ts +163 -33
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +54 -32
- package/dist/words.d.ts +19 -0
- package/dist/words.d.ts.map +1 -0
- package/dist/words.js +150 -0
- package/package.json +11 -5
- package/dist/acquisition.d.ts +0 -14
- package/dist/acquisition.d.ts.map +0 -1
- package/dist/acquisition.js +0 -24
- package/dist/read-side.d.ts +0 -25
- package/dist/read-side.d.ts.map +0 -1
- package/dist/read-side.js +0 -639
package/dist/conversation.js
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import { conversationChangeSet } from './change-set.js';
|
|
2
2
|
import { runtimeError } from './failure.js';
|
|
3
|
+
import { refusedForSpace } from './durability.js';
|
|
3
4
|
import { declaredFile } from './file.js';
|
|
4
5
|
import { changeBatchIdentity, chatDraftRevision } from './identity.js';
|
|
5
6
|
import { OFFLINE } from './outcome.js';
|
|
7
|
+
import { capped, cursorOf, decodedCursor, FROM_START, messageOrder, precedes } from './windows.js';
|
|
6
8
|
const QUIET = {
|
|
7
9
|
turn: null,
|
|
8
10
|
questions: [],
|
|
9
11
|
process: null,
|
|
10
12
|
commands: [],
|
|
11
13
|
options: [],
|
|
14
|
+
starting: null,
|
|
15
|
+
link: null,
|
|
16
|
+
deliveryUnknown: [],
|
|
12
17
|
};
|
|
18
|
+
const FACTS = [
|
|
19
|
+
'questions',
|
|
20
|
+
'process',
|
|
21
|
+
'commands',
|
|
22
|
+
'options',
|
|
23
|
+
'starting',
|
|
24
|
+
'link',
|
|
25
|
+
'deliveryUnknown',
|
|
26
|
+
];
|
|
13
27
|
export function unheldView(conversation) {
|
|
14
28
|
return {
|
|
15
29
|
conversation,
|
|
@@ -19,10 +33,26 @@ export function unheldView(conversation) {
|
|
|
19
33
|
process: null,
|
|
20
34
|
commands: [],
|
|
21
35
|
options: [],
|
|
36
|
+
starting: null,
|
|
37
|
+
link: null,
|
|
38
|
+
deliveryUnknown: [],
|
|
22
39
|
chatDraft: null,
|
|
23
40
|
};
|
|
24
41
|
}
|
|
25
42
|
export const SESSION_STARTED = 'Session started';
|
|
43
|
+
const NEW_CHAT = 'new:';
|
|
44
|
+
export const HISTORY_CONVERSATIONS = 64;
|
|
45
|
+
export function newChat(agent) {
|
|
46
|
+
return `${NEW_CHAT}${agent}`;
|
|
47
|
+
}
|
|
48
|
+
function creationOf(input) {
|
|
49
|
+
if (typeof input !== 'object' || input === null)
|
|
50
|
+
return null;
|
|
51
|
+
const { agent_id: agent, operation_id: operation, replaces, } = input;
|
|
52
|
+
return typeof agent === 'string' && typeof operation === 'string' && replaces === undefined
|
|
53
|
+
? { agent, operation }
|
|
54
|
+
: null;
|
|
55
|
+
}
|
|
26
56
|
function startedTurn(turn, operation) {
|
|
27
57
|
if (operation.op === 'begin')
|
|
28
58
|
return { turn, started: operation.at, parts: [], context: null };
|
|
@@ -72,19 +102,25 @@ function appliedTurn(held, operation) {
|
|
|
72
102
|
parts: held.parts.map((kept, at) => (at === operation.index ? changed : kept)),
|
|
73
103
|
};
|
|
74
104
|
}
|
|
75
|
-
function ordered(left, right) {
|
|
76
|
-
if (left.position !== right.position)
|
|
77
|
-
return left.position < right.position ? -1 : 1;
|
|
78
|
-
if (left.identity !== right.identity)
|
|
79
|
-
return left.identity < right.identity ? -1 : 1;
|
|
80
|
-
return 0;
|
|
81
|
-
}
|
|
82
105
|
function windowOf(held, arriving, limit) {
|
|
83
106
|
const byIdentity = new Map(held.map((message) => [message.identity, message]));
|
|
84
107
|
for (const message of arriving)
|
|
85
108
|
byIdentity.set(message.identity, message);
|
|
86
|
-
const next = [...byIdentity.values()].sort(
|
|
87
|
-
|
|
109
|
+
const next = [...byIdentity.values()].sort(messageOrder);
|
|
110
|
+
const start = Math.max(0, next.length - limit);
|
|
111
|
+
return { messages: next.slice(start), dropped: start > 0 };
|
|
112
|
+
}
|
|
113
|
+
function frameOf(asked, messages, more, total) {
|
|
114
|
+
const oldest = messages[0];
|
|
115
|
+
const next = !more
|
|
116
|
+
? undefined
|
|
117
|
+
: oldest === undefined
|
|
118
|
+
? (asked.after ?? FROM_START)
|
|
119
|
+
: cursorOf([oldest.position, oldest.identity]);
|
|
120
|
+
return {
|
|
121
|
+
...(next === undefined ? {} : { next }),
|
|
122
|
+
...(total === null ? {} : { total }),
|
|
123
|
+
};
|
|
88
124
|
}
|
|
89
125
|
function newestOf(arriving, from) {
|
|
90
126
|
return arriving.reduce((top, message) => (top === null || message.position > top ? message.position : top), from);
|
|
@@ -146,40 +182,64 @@ function historyPages(result) {
|
|
|
146
182
|
}
|
|
147
183
|
return pages;
|
|
148
184
|
}
|
|
149
|
-
function
|
|
185
|
+
function answered(result, field) {
|
|
150
186
|
if (result.kind !== 'action')
|
|
151
187
|
return null;
|
|
152
188
|
const answer = result.result;
|
|
153
189
|
if (typeof answer !== 'object' || answer === null)
|
|
154
190
|
return null;
|
|
155
|
-
const
|
|
156
|
-
return typeof
|
|
191
|
+
const value = answer[field];
|
|
192
|
+
return typeof value === 'string' ? value : null;
|
|
157
193
|
}
|
|
158
|
-
function
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (
|
|
194
|
+
function beforeOf(after) {
|
|
195
|
+
if (after === undefined)
|
|
196
|
+
return null;
|
|
197
|
+
const decoded = typeof after === 'string' ? decodedCursor(after) : null;
|
|
198
|
+
if (decoded?.length === 0)
|
|
199
|
+
return null;
|
|
200
|
+
const [position, identity] = decoded ?? [];
|
|
201
|
+
if (decoded?.length !== 2 || typeof position !== 'string' || typeof identity !== 'string') {
|
|
202
|
+
throw runtimeError('invalid-after', `conversation after must be the next an earlier view returned, got ${String(after)}`);
|
|
203
|
+
}
|
|
204
|
+
return [position, identity];
|
|
205
|
+
}
|
|
206
|
+
function askedOf(options) {
|
|
207
|
+
const { limit, after, total } = options;
|
|
208
|
+
if (limit !== undefined && (!Number.isInteger(limit) || limit < 0)) {
|
|
163
209
|
throw runtimeError('invalid-limit', `conversation limit must be a non-negative integer, got ${String(limit)}`);
|
|
164
210
|
}
|
|
165
|
-
return limit;
|
|
211
|
+
return { limit: capped(limit), after, before: beforeOf(after), total: total === true };
|
|
166
212
|
}
|
|
167
213
|
const EXPIRED = { kind: 'rejected', code: 'expired', fields: {}, retryable: false };
|
|
214
|
+
const DURABLE_WRITE = {
|
|
215
|
+
kind: 'rejected',
|
|
216
|
+
code: 'durable-write',
|
|
217
|
+
fields: {},
|
|
218
|
+
retryable: true,
|
|
219
|
+
};
|
|
168
220
|
function unwritten(draft) {
|
|
169
|
-
return draft.text === ''
|
|
221
|
+
return draft.text === '';
|
|
170
222
|
}
|
|
171
223
|
function pending(draft) {
|
|
172
|
-
return
|
|
224
|
+
return {
|
|
225
|
+
...(draft.submitted === undefined ? {} : { submitted: draft.submitted }),
|
|
226
|
+
...(draft.moves === undefined ? {} : { moves: draft.moves }),
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
function empty(draft) {
|
|
230
|
+
return unwritten(draft) && draft.submitted === undefined && draft.moves === undefined;
|
|
173
231
|
}
|
|
174
232
|
function withSubmitted(draft, submitted) {
|
|
175
233
|
const { submitted: _held, ...unsent } = draft;
|
|
176
234
|
return submitted.length === 0 ? unsent : { ...unsent, submitted };
|
|
177
235
|
}
|
|
236
|
+
function prepended(draft, said) {
|
|
237
|
+
return { text: [said.text, draft.text].filter((one) => one !== '').join('\n') };
|
|
238
|
+
}
|
|
178
239
|
function returned(draft, submission, others) {
|
|
179
240
|
return withSubmitted({
|
|
180
241
|
conversation: draft.conversation,
|
|
181
|
-
|
|
182
|
-
files: [...submission.files, ...draft.files],
|
|
242
|
+
...prepended(draft, submission),
|
|
183
243
|
revision: chatDraftRevision(),
|
|
184
244
|
}, others);
|
|
185
245
|
}
|
|
@@ -190,10 +250,13 @@ export function createConversationStore(deps) {
|
|
|
190
250
|
const drafts = new Map();
|
|
191
251
|
const sending = new Map();
|
|
192
252
|
const behind = new Set();
|
|
253
|
+
const catching = new Map();
|
|
193
254
|
const gaps = new Map();
|
|
194
255
|
const lives = new Map();
|
|
256
|
+
const unsent = new Map();
|
|
195
257
|
const views = new Map();
|
|
196
258
|
const listing = new Map();
|
|
259
|
+
const creations = new Set();
|
|
197
260
|
const listWatchers = new Set();
|
|
198
261
|
let listKnown = false;
|
|
199
262
|
const agentWatchers = new Set();
|
|
@@ -205,7 +268,7 @@ export function createConversationStore(deps) {
|
|
|
205
268
|
function liveOf(conversation) {
|
|
206
269
|
return lives.get(conversation) ?? QUIET;
|
|
207
270
|
}
|
|
208
|
-
function viewOf(conversation, held) {
|
|
271
|
+
function viewOf(conversation, held, frame) {
|
|
209
272
|
const draft = drafts.get(conversation);
|
|
210
273
|
const facts = liveOf(conversation);
|
|
211
274
|
return {
|
|
@@ -216,14 +279,18 @@ export function createConversationStore(deps) {
|
|
|
216
279
|
text,
|
|
217
280
|
message,
|
|
218
281
|
})),
|
|
282
|
+
...frame,
|
|
219
283
|
turn: structuredClone(facts.turn),
|
|
220
284
|
questions: structuredClone(facts.questions),
|
|
221
285
|
process: facts.process === null ? null : { ...facts.process },
|
|
222
286
|
commands: structuredClone(facts.commands),
|
|
223
287
|
options: structuredClone(facts.options),
|
|
288
|
+
starting: facts.starting,
|
|
289
|
+
link: facts.link === null ? null : { ...facts.link },
|
|
290
|
+
deliveryUnknown: [...facts.deliveryUnknown],
|
|
224
291
|
chatDraft: draft === undefined || unwritten(draft)
|
|
225
292
|
? null
|
|
226
|
-
: { text: draft.text,
|
|
293
|
+
: { text: draft.text, revision: draft.revision },
|
|
227
294
|
};
|
|
228
295
|
}
|
|
229
296
|
function emit(conversation) {
|
|
@@ -235,21 +302,36 @@ export function createConversationStore(deps) {
|
|
|
235
302
|
const ready = [...view.watchers].filter((watcher) => watcher.ready);
|
|
236
303
|
if (ready.length === 0)
|
|
237
304
|
continue;
|
|
238
|
-
const value = viewOf(conversation, view.window);
|
|
305
|
+
const value = viewOf(conversation, view.window, sharedFrame(view, view.window));
|
|
239
306
|
for (const watcher of ready)
|
|
240
307
|
watcher.notify(value);
|
|
241
308
|
}
|
|
242
309
|
}
|
|
243
|
-
function
|
|
310
|
+
function sharedFrame(view, window) {
|
|
311
|
+
return frameOf(view.asked, window, view.more, view.total);
|
|
312
|
+
}
|
|
313
|
+
async function slide(local, conversation, arriving) {
|
|
314
|
+
let count;
|
|
244
315
|
for (const view of views.values()) {
|
|
245
316
|
if (view.conversation !== conversation || view.window === null)
|
|
246
317
|
continue;
|
|
247
|
-
|
|
318
|
+
if (arriving === null) {
|
|
319
|
+
view.window = [];
|
|
320
|
+
view.more = false;
|
|
321
|
+
if (view.total !== null)
|
|
322
|
+
view.total = 0;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const joining = arriving.filter((message) => precedes(message, view.asked.before));
|
|
326
|
+
const next = windowOf(view.window, joining, view.asked.limit);
|
|
327
|
+
view.window = next.messages;
|
|
328
|
+
view.more ||= next.dropped;
|
|
329
|
+
if (view.total !== null && arriving.length > 0) {
|
|
330
|
+
count ??= await local.messageCount(conversation);
|
|
331
|
+
view.total = count;
|
|
332
|
+
}
|
|
248
333
|
}
|
|
249
334
|
}
|
|
250
|
-
function changesOf(conversation, arriving) {
|
|
251
|
-
return arriving.map(({ identity, text }) => ({ conversation, identity, text }));
|
|
252
|
-
}
|
|
253
335
|
function stateOf(conversation) {
|
|
254
336
|
const facts = liveOf(conversation);
|
|
255
337
|
if (facts.questions.length > 0)
|
|
@@ -264,6 +346,7 @@ export function createConversationStore(deps) {
|
|
|
264
346
|
created: entry.created,
|
|
265
347
|
finished: entry.finished,
|
|
266
348
|
outcome: entry.outcome,
|
|
349
|
+
waiting: entry.waiting,
|
|
267
350
|
state: stateOf(entry.conversation),
|
|
268
351
|
started: lives.get(entry.conversation)?.turn?.started ?? null,
|
|
269
352
|
}));
|
|
@@ -306,23 +389,30 @@ export function createConversationStore(deps) {
|
|
|
306
389
|
const declaration = deps.agents();
|
|
307
390
|
if (declaration === undefined)
|
|
308
391
|
return;
|
|
309
|
-
const held = await deps.documents(declaration);
|
|
310
|
-
if ('pending' in held)
|
|
311
|
-
return;
|
|
312
|
-
const { documents } = held;
|
|
313
392
|
const names = new Map();
|
|
314
393
|
const retired = new Set();
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
394
|
+
let covered = null;
|
|
395
|
+
for (let after;;) {
|
|
396
|
+
const read = await deps.documents(declaration, after);
|
|
397
|
+
if ('pending' in read)
|
|
398
|
+
return;
|
|
399
|
+
const coverage = JSON.stringify(read.coverage);
|
|
400
|
+
if (covered !== null && coverage !== covered)
|
|
401
|
+
return readAgents();
|
|
402
|
+
covered = coverage;
|
|
403
|
+
for (const document of read.documents) {
|
|
404
|
+
const identity = document.fields?.[declaration.identity];
|
|
405
|
+
const name = document.fields?.[declaration.name];
|
|
406
|
+
if (typeof identity !== 'string' || typeof name !== 'string')
|
|
407
|
+
continue;
|
|
408
|
+
names.set(identity, name);
|
|
409
|
+
const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
|
|
410
|
+
if (ended !== undefined && ended !== null)
|
|
411
|
+
retired.add(identity);
|
|
412
|
+
}
|
|
413
|
+
if (read.next === undefined)
|
|
414
|
+
break;
|
|
415
|
+
after = read.next;
|
|
326
416
|
}
|
|
327
417
|
const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
|
|
328
418
|
if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
|
|
@@ -332,6 +422,12 @@ export function createConversationStore(deps) {
|
|
|
332
422
|
if (named)
|
|
333
423
|
emitList();
|
|
334
424
|
emitAgents();
|
|
425
|
+
const ended = [...drafts.keys()].filter(retiredChat);
|
|
426
|
+
if (ended.length > 0)
|
|
427
|
+
await remove(ended);
|
|
428
|
+
}
|
|
429
|
+
function retiredChat(conversation) {
|
|
430
|
+
return (conversation.startsWith(NEW_CHAT) && retiredAgents.has(conversation.slice(NEW_CHAT.length)));
|
|
335
431
|
}
|
|
336
432
|
function agentsReady() {
|
|
337
433
|
agentsRead ??= readAgents().catch(() => {
|
|
@@ -340,9 +436,34 @@ export function createConversationStore(deps) {
|
|
|
340
436
|
return agentsRead;
|
|
341
437
|
}
|
|
342
438
|
function live(conversation, change) {
|
|
439
|
+
const owed = unsent.get(conversation);
|
|
440
|
+
for (const fact of FACTS.filter((one) => one in change))
|
|
441
|
+
owed?.delete(fact);
|
|
343
442
|
setLive(conversation, { ...liveOf(conversation), ...change });
|
|
344
443
|
emit(conversation);
|
|
345
444
|
}
|
|
445
|
+
function openingEnded() {
|
|
446
|
+
const owed = [...unsent];
|
|
447
|
+
unsent.clear();
|
|
448
|
+
for (const [conversation, facts] of owed) {
|
|
449
|
+
const held = lives.get(conversation);
|
|
450
|
+
if (held === undefined || facts.size === 0)
|
|
451
|
+
continue;
|
|
452
|
+
setLive(conversation, {
|
|
453
|
+
turn: held.turn,
|
|
454
|
+
questions: facts.has('questions') ? QUIET.questions : held.questions,
|
|
455
|
+
process: facts.has('process') ? QUIET.process : held.process,
|
|
456
|
+
commands: facts.has('commands') ? QUIET.commands : held.commands,
|
|
457
|
+
options: facts.has('options') ? QUIET.options : held.options,
|
|
458
|
+
starting: facts.has('starting') ? QUIET.starting : held.starting,
|
|
459
|
+
link: facts.has('link') ? QUIET.link : held.link,
|
|
460
|
+
deliveryUnknown: facts.has('deliveryUnknown')
|
|
461
|
+
? QUIET.deliveryUnknown
|
|
462
|
+
: held.deliveryUnknown,
|
|
463
|
+
});
|
|
464
|
+
emit(conversation);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
346
467
|
function relayed(conversation, turn, operation) {
|
|
347
468
|
const held = liveOf(conversation).turn;
|
|
348
469
|
const next = held?.turn === turn ? appliedTurn(held, operation) : startedTurn(turn, operation);
|
|
@@ -369,12 +490,6 @@ export function createConversationStore(deps) {
|
|
|
369
490
|
}
|
|
370
491
|
async function remove(leaving) {
|
|
371
492
|
await deps.durably(async (local) => {
|
|
372
|
-
const removed = [];
|
|
373
|
-
for (const conversation of leaving) {
|
|
374
|
-
for (const identity of await local.messageIdentities(conversation)) {
|
|
375
|
-
removed.push({ conversation, identity, text: null });
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
493
|
await local.dropConversations(leaving);
|
|
379
494
|
const changed = [];
|
|
380
495
|
for (const conversation of leaving) {
|
|
@@ -386,11 +501,12 @@ export function createConversationStore(deps) {
|
|
|
386
501
|
partials.delete(conversation);
|
|
387
502
|
drafts.delete(conversation);
|
|
388
503
|
behind.delete(conversation);
|
|
504
|
+
unsent.delete(conversation);
|
|
389
505
|
setLive(conversation, null);
|
|
390
|
-
slide(conversation, null);
|
|
506
|
+
await slide(local, conversation, null);
|
|
391
507
|
}
|
|
392
508
|
if (changed.length > 0)
|
|
393
|
-
deps.publish(conversationChangeSet(
|
|
509
|
+
deps.publish(conversationChangeSet(changed));
|
|
394
510
|
});
|
|
395
511
|
}
|
|
396
512
|
function gap(conversation) {
|
|
@@ -415,17 +531,18 @@ export function createConversationStore(deps) {
|
|
|
415
531
|
if (advances)
|
|
416
532
|
reads.set(conversation, message.position);
|
|
417
533
|
sealTurns(conversation, new Set([message.turn]));
|
|
418
|
-
slide(conversation, [message]);
|
|
419
|
-
deps.publish(conversationChangeSet(
|
|
534
|
+
await slide(local, conversation, [message]);
|
|
535
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
420
536
|
});
|
|
421
537
|
}
|
|
422
|
-
async function readHistories(conversations) {
|
|
538
|
+
async function readHistories(conversations, ended) {
|
|
423
539
|
const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
|
|
424
|
-
let
|
|
540
|
+
let waiting = conversations.map((conversation) => ({
|
|
425
541
|
conversation,
|
|
426
542
|
after: reads.get(conversation) ?? undefined,
|
|
427
543
|
}));
|
|
428
|
-
while (
|
|
544
|
+
while (waiting.length > 0) {
|
|
545
|
+
const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
|
|
429
546
|
const result = await deps.call('conversation.history', {
|
|
430
547
|
conversations: asking.map(({ conversation, after }) => ({
|
|
431
548
|
conversation,
|
|
@@ -445,10 +562,33 @@ export function createConversationStore(deps) {
|
|
|
445
562
|
following.push({ conversation: page.conversation, after: page.next });
|
|
446
563
|
}
|
|
447
564
|
}
|
|
448
|
-
asking
|
|
565
|
+
for (const { conversation } of asking) {
|
|
566
|
+
if (!following.some((next) => next.conversation === conversation))
|
|
567
|
+
ended(conversation);
|
|
568
|
+
}
|
|
569
|
+
waiting = [...waiting.slice(HISTORY_CONVERSATIONS), ...following];
|
|
449
570
|
}
|
|
450
571
|
return null;
|
|
451
572
|
}
|
|
573
|
+
function caughtUp(conversations) {
|
|
574
|
+
const ends = new Map();
|
|
575
|
+
for (const conversation of conversations) {
|
|
576
|
+
const held = new Promise((end) => ends.set(conversation, end));
|
|
577
|
+
catching.set(conversation, held);
|
|
578
|
+
void held.then(() => {
|
|
579
|
+
if (catching.get(conversation) === held)
|
|
580
|
+
catching.delete(conversation);
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
const ended = (conversation) => ends.get(conversation)?.();
|
|
584
|
+
const read = readHistories(conversations, ended);
|
|
585
|
+
const endAll = () => {
|
|
586
|
+
for (const conversation of conversations)
|
|
587
|
+
ended(conversation);
|
|
588
|
+
};
|
|
589
|
+
read.then(endAll, endAll);
|
|
590
|
+
return read;
|
|
591
|
+
}
|
|
452
592
|
function keepPage(conversation, arriving, from, next, generation) {
|
|
453
593
|
return deps.durably(async (local) => {
|
|
454
594
|
if (!replicated.has(conversation))
|
|
@@ -467,9 +607,9 @@ export function createConversationStore(deps) {
|
|
|
467
607
|
sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
|
|
468
608
|
if (whole && (gaps.get(conversation) ?? 0) === generation)
|
|
469
609
|
behind.delete(conversation);
|
|
470
|
-
slide(conversation, arriving);
|
|
610
|
+
await slide(local, conversation, arriving);
|
|
471
611
|
if (arriving.length > 0 || sealing || whole) {
|
|
472
|
-
deps.publish(conversationChangeSet(
|
|
612
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
473
613
|
}
|
|
474
614
|
});
|
|
475
615
|
}
|
|
@@ -478,11 +618,8 @@ export function createConversationStore(deps) {
|
|
|
478
618
|
if (event.kind === 'gap') {
|
|
479
619
|
if (replicated.has(conversation))
|
|
480
620
|
gap(conversation);
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
return;
|
|
484
|
-
setLive(conversation, { ...QUIET, turn: held.turn });
|
|
485
|
-
emit(conversation);
|
|
621
|
+
if (lives.has(conversation))
|
|
622
|
+
unsent.set(conversation, new Set(FACTS));
|
|
486
623
|
return;
|
|
487
624
|
}
|
|
488
625
|
if (event.kind === 'turn') {
|
|
@@ -505,23 +642,42 @@ export function createConversationStore(deps) {
|
|
|
505
642
|
live(conversation, { options: structuredClone(event.options) });
|
|
506
643
|
return;
|
|
507
644
|
}
|
|
645
|
+
if (event.kind === 'starting') {
|
|
646
|
+
live(conversation, { starting: event.step });
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
if (event.kind === 'link') {
|
|
650
|
+
live(conversation, { link: { connected: event.connected } });
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
if (event.kind === 'deliveries') {
|
|
654
|
+
live(conversation, { deliveryUnknown: [...event.unknown] });
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
508
657
|
const arriving = acceptedMessage(event.message);
|
|
509
658
|
if (arriving === null)
|
|
510
659
|
return;
|
|
511
660
|
if (replicated.has(conversation)) {
|
|
512
|
-
|
|
661
|
+
if (deps.full()) {
|
|
662
|
+
gap(conversation);
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
void keep(conversation, arriving).catch((error) => {
|
|
666
|
+
if (refusedForSpace(error))
|
|
667
|
+
gap(conversation);
|
|
668
|
+
});
|
|
513
669
|
return;
|
|
514
670
|
}
|
|
515
671
|
if (sealTurns(conversation, new Set([arriving.turn])))
|
|
516
672
|
emit(conversation);
|
|
517
673
|
}
|
|
518
674
|
async function kept(local, conversation, following) {
|
|
519
|
-
if (following === null || (
|
|
675
|
+
if (following === null || empty(following)) {
|
|
520
676
|
await local.dropChatDraft(conversation);
|
|
521
677
|
drafts.delete(conversation);
|
|
522
678
|
}
|
|
523
679
|
else {
|
|
524
|
-
await local.
|
|
680
|
+
await local.keepChatDrafts([following]);
|
|
525
681
|
drafts.set(conversation, following);
|
|
526
682
|
}
|
|
527
683
|
}
|
|
@@ -534,13 +690,35 @@ export function createConversationStore(deps) {
|
|
|
534
690
|
await kept(local, conversation, withSubmitted(held, left));
|
|
535
691
|
return true;
|
|
536
692
|
}
|
|
693
|
+
function moved(agent, operation, to) {
|
|
694
|
+
const from = newChat(agent);
|
|
695
|
+
return deps.durably(async (local) => {
|
|
696
|
+
const held = drafts.get(from);
|
|
697
|
+
const earlier = (held?.moves ?? []).filter((one) => !deps.expired(one));
|
|
698
|
+
if (earlier.includes(operation))
|
|
699
|
+
return;
|
|
700
|
+
const blank = {
|
|
701
|
+
conversation: from,
|
|
702
|
+
text: '',
|
|
703
|
+
revision: chatDraftRevision(),
|
|
704
|
+
};
|
|
705
|
+
const { moves: _answered, ...unsent } = held ?? blank;
|
|
706
|
+
const taken = drafts.has(to) || empty(unsent) ? [] : [{ ...unsent, conversation: to }];
|
|
707
|
+
const left = { ...(taken.length === 0 ? unsent : blank), moves: [...earlier, operation] };
|
|
708
|
+
await local.keepChatDrafts([left, ...taken]);
|
|
709
|
+
drafts.set(from, left);
|
|
710
|
+
for (const one of taken)
|
|
711
|
+
drafts.set(to, one);
|
|
712
|
+
deps.publish(conversationChangeSet([from, to]));
|
|
713
|
+
});
|
|
714
|
+
}
|
|
537
715
|
function withDraft(conversation, next) {
|
|
538
716
|
return deps.durably(async (local) => {
|
|
539
717
|
const following = await next(drafts.get(conversation), local);
|
|
540
718
|
if (following === undefined)
|
|
541
719
|
return;
|
|
542
720
|
await kept(local, conversation, following);
|
|
543
|
-
deps.publish(conversationChangeSet([
|
|
721
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
544
722
|
});
|
|
545
723
|
}
|
|
546
724
|
async function submitted(conversation, operation, draft) {
|
|
@@ -552,17 +730,12 @@ export function createConversationStore(deps) {
|
|
|
552
730
|
if (submission !== undefined)
|
|
553
731
|
return undefined;
|
|
554
732
|
const said = draft ?? current;
|
|
555
|
-
submission = {
|
|
556
|
-
operation,
|
|
557
|
-
text: said?.text ?? '',
|
|
558
|
-
files: [...(said?.files ?? [])],
|
|
559
|
-
turn: null,
|
|
560
|
-
};
|
|
733
|
+
submission = { operation, text: said?.text ?? '', turn: null };
|
|
561
734
|
return {
|
|
562
735
|
conversation,
|
|
563
736
|
...(draft === undefined || current === undefined
|
|
564
|
-
? { text: '',
|
|
565
|
-
: { text: current.text,
|
|
737
|
+
? { text: '', revision: chatDraftRevision() }
|
|
738
|
+
: { text: current.text, revision: current.revision }),
|
|
566
739
|
submitted: [...(current?.submitted ?? []), submission],
|
|
567
740
|
};
|
|
568
741
|
});
|
|
@@ -580,10 +753,8 @@ export function createConversationStore(deps) {
|
|
|
580
753
|
const dispatch = {};
|
|
581
754
|
sending.set(submission.operation, dispatch);
|
|
582
755
|
try {
|
|
583
|
-
const files = await Promise.all(submission.files.map(async (file) => ({ ...(await declaredFile(file)), blob: file.blob })));
|
|
584
756
|
const result = await invoke('conversation.send', conversation, {
|
|
585
757
|
text: submission.text,
|
|
586
|
-
files,
|
|
587
758
|
operation_id: submission.operation,
|
|
588
759
|
});
|
|
589
760
|
if (result.kind === 'revoked-authorization')
|
|
@@ -592,7 +763,7 @@ export function createConversationStore(deps) {
|
|
|
592
763
|
deps.unanswered();
|
|
593
764
|
return result;
|
|
594
765
|
}
|
|
595
|
-
const turn =
|
|
766
|
+
const turn = answered(result, 'turn');
|
|
596
767
|
await withDraft(conversation, async (current, local) => {
|
|
597
768
|
const held = current?.submitted?.find((one) => one.operation === submission.operation);
|
|
598
769
|
if (current === undefined || held === undefined || held.turn !== null)
|
|
@@ -621,15 +792,37 @@ export function createConversationStore(deps) {
|
|
|
621
792
|
return entries();
|
|
622
793
|
},
|
|
623
794
|
async read(conversation, options = {}) {
|
|
624
|
-
const
|
|
625
|
-
return deps.durably(async (local) =>
|
|
795
|
+
const asked = askedOf(options);
|
|
796
|
+
return deps.durably(async (local) => {
|
|
797
|
+
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
|
|
798
|
+
const total = asked.total ? await local.messageCount(conversation) : null;
|
|
799
|
+
return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total)));
|
|
800
|
+
});
|
|
626
801
|
},
|
|
627
|
-
create(input) {
|
|
628
|
-
|
|
802
|
+
async create(input) {
|
|
803
|
+
const result = await deps.call('conversation.create', input);
|
|
804
|
+
const conversation = answered(result, 'conversation');
|
|
805
|
+
if (conversation === null)
|
|
806
|
+
return result;
|
|
807
|
+
if (!listing.has(conversation))
|
|
808
|
+
creations.add(conversation);
|
|
809
|
+
const creation = creationOf(input);
|
|
810
|
+
if (creation !== null)
|
|
811
|
+
await moved(creation.agent, creation.operation, conversation);
|
|
812
|
+
return result;
|
|
629
813
|
},
|
|
630
814
|
async send(conversation, operation = changeBatchIdentity(), draft) {
|
|
815
|
+
if (deps.full())
|
|
816
|
+
return DURABLE_WRITE;
|
|
631
817
|
if (deps.online()) {
|
|
632
|
-
const
|
|
818
|
+
const held = await submitted(conversation, operation, draft).catch((error) => {
|
|
819
|
+
if (refusedForSpace(error))
|
|
820
|
+
return null;
|
|
821
|
+
throw error;
|
|
822
|
+
});
|
|
823
|
+
if (held === null)
|
|
824
|
+
return DURABLE_WRITE;
|
|
825
|
+
const { submission, earlier } = held;
|
|
633
826
|
if (!earlier || !deps.expired(operation))
|
|
634
827
|
return dispatched(conversation, submission);
|
|
635
828
|
await forgotten(conversation, submission);
|
|
@@ -638,10 +831,19 @@ export function createConversationStore(deps) {
|
|
|
638
831
|
if (draft !== undefined) {
|
|
639
832
|
await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
|
|
640
833
|
? undefined
|
|
641
|
-
: returned(current ?? { conversation, text: '',
|
|
834
|
+
: returned(current ?? { conversation, text: '', revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
|
|
642
835
|
}
|
|
643
836
|
return OFFLINE;
|
|
644
837
|
},
|
|
838
|
+
async attach(room, file) {
|
|
839
|
+
if (!deps.online())
|
|
840
|
+
return OFFLINE;
|
|
841
|
+
return deps.call('conversation.attach', {
|
|
842
|
+
room,
|
|
843
|
+
...(await declaredFile(file)),
|
|
844
|
+
blob: file.blob,
|
|
845
|
+
});
|
|
846
|
+
},
|
|
645
847
|
answer(conversation, answer) {
|
|
646
848
|
return invoke('conversation.answer', conversation, { answer });
|
|
647
849
|
},
|
|
@@ -672,21 +874,22 @@ export function createConversationStore(deps) {
|
|
|
672
874
|
return withDraft(conversation, (current) => ({
|
|
673
875
|
conversation,
|
|
674
876
|
text: draft.text,
|
|
675
|
-
files: [...draft.files],
|
|
676
877
|
revision,
|
|
677
|
-
...(current
|
|
878
|
+
...(current === undefined ? {} : pending(current)),
|
|
879
|
+
}));
|
|
880
|
+
},
|
|
881
|
+
prepend(conversation, draft) {
|
|
882
|
+
return withDraft(conversation, (current) => ({
|
|
883
|
+
conversation,
|
|
884
|
+
...(current === undefined ? { text: draft.text } : prepended(current, draft)),
|
|
885
|
+
revision: chatDraftRevision(),
|
|
886
|
+
...(current === undefined ? {} : pending(current)),
|
|
678
887
|
}));
|
|
679
888
|
},
|
|
680
889
|
discard(conversation) {
|
|
681
890
|
return withDraft(conversation, (current) => current === undefined
|
|
682
891
|
? undefined
|
|
683
|
-
: {
|
|
684
|
-
conversation,
|
|
685
|
-
text: '',
|
|
686
|
-
files: [],
|
|
687
|
-
revision: chatDraftRevision(),
|
|
688
|
-
...pending(current),
|
|
689
|
-
});
|
|
892
|
+
: { conversation, text: '', revision: chatDraftRevision(), ...pending(current) });
|
|
690
893
|
},
|
|
691
894
|
};
|
|
692
895
|
return {
|
|
@@ -713,7 +916,9 @@ export function createConversationStore(deps) {
|
|
|
713
916
|
const named = new Set(listing.keys());
|
|
714
917
|
const leaving = [
|
|
715
918
|
...new Set([...replicated, ...stored, ...reads.keys(), ...drafts.keys()]),
|
|
716
|
-
].filter((conversation) => !named.has(conversation)
|
|
919
|
+
].filter((conversation) => !named.has(conversation) &&
|
|
920
|
+
!creations.has(conversation) &&
|
|
921
|
+
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
|
|
717
922
|
for (const conversation of named) {
|
|
718
923
|
if (!replicated.has(conversation))
|
|
719
924
|
behind.add(conversation);
|
|
@@ -724,12 +929,25 @@ export function createConversationStore(deps) {
|
|
|
724
929
|
await remove(leaving);
|
|
725
930
|
},
|
|
726
931
|
async catchUp(progress) {
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
932
|
+
if (deps.full()) {
|
|
933
|
+
if (behind.size > 0)
|
|
934
|
+
progress.complete = false;
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
const owed = (conversation) => behind.has(conversation) && replicated.has(conversation);
|
|
938
|
+
const read = async (conversations) => {
|
|
939
|
+
if (conversations.length === 0 || deps.closed())
|
|
940
|
+
return;
|
|
941
|
+
const failed = await caughtUp(conversations);
|
|
730
942
|
if (failed?.kind === 'transport-uncertainty')
|
|
731
943
|
progress.reachable = false;
|
|
732
|
-
}
|
|
944
|
+
};
|
|
945
|
+
const asking = [...behind].filter(owed).sort();
|
|
946
|
+
const awaited = asking.filter((conversation) => catching.has(conversation));
|
|
947
|
+
await Promise.all([
|
|
948
|
+
read(asking.filter((conversation) => !catching.has(conversation))),
|
|
949
|
+
Promise.all(awaited.map((conversation) => catching.get(conversation))).then(() => read(awaited.filter((conversation) => owed(conversation) && !catching.has(conversation)))),
|
|
950
|
+
]);
|
|
733
951
|
if (behind.size > 0)
|
|
734
952
|
progress.complete = false;
|
|
735
953
|
},
|
|
@@ -771,27 +989,38 @@ export function createConversationStore(deps) {
|
|
|
771
989
|
reopened() {
|
|
772
990
|
sending.clear();
|
|
773
991
|
},
|
|
992
|
+
lost() {
|
|
993
|
+
catching.clear();
|
|
994
|
+
},
|
|
774
995
|
watch(conversation, options, deliver) {
|
|
775
|
-
const
|
|
776
|
-
const key = JSON.stringify([conversation, limit ?? null]);
|
|
996
|
+
const asked = askedOf(options);
|
|
997
|
+
const key = JSON.stringify([conversation, asked.limit, asked.after ?? null, asked.total]);
|
|
777
998
|
const view = views.get(key) ?? {
|
|
778
999
|
conversation,
|
|
779
|
-
|
|
1000
|
+
asked,
|
|
780
1001
|
watchers: new Set(),
|
|
781
1002
|
window: null,
|
|
1003
|
+
more: false,
|
|
1004
|
+
total: null,
|
|
782
1005
|
};
|
|
783
1006
|
views.set(key, view);
|
|
784
1007
|
const watcher = { notify: deliver, ready: false };
|
|
785
1008
|
view.watchers.add(watcher);
|
|
786
1009
|
void deps
|
|
787
1010
|
.durably(async (local) => {
|
|
788
|
-
view.window
|
|
1011
|
+
if (view.window !== null)
|
|
1012
|
+
return;
|
|
1013
|
+
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
|
|
1014
|
+
view.window = messages;
|
|
1015
|
+
view.more = more;
|
|
1016
|
+
view.total = asked.total ? await local.messageCount(conversation) : null;
|
|
789
1017
|
})
|
|
790
1018
|
.then(() => {
|
|
791
1019
|
if (deps.closed() || !view.watchers.has(watcher))
|
|
792
1020
|
return;
|
|
793
1021
|
watcher.ready = true;
|
|
794
|
-
|
|
1022
|
+
const window = view.window ?? [];
|
|
1023
|
+
watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
|
|
795
1024
|
})
|
|
796
1025
|
.catch(() => { });
|
|
797
1026
|
return () => {
|
|
@@ -838,12 +1067,16 @@ export function createConversationStore(deps) {
|
|
|
838
1067
|
if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
|
|
839
1068
|
if (event.kind === 'list') {
|
|
840
1069
|
listing.clear();
|
|
841
|
-
for (const entry of event.entries)
|
|
1070
|
+
for (const entry of event.entries) {
|
|
842
1071
|
listing.set(entry.conversation, { ...entry });
|
|
1072
|
+
creations.delete(entry.conversation);
|
|
1073
|
+
}
|
|
843
1074
|
listKnown = true;
|
|
1075
|
+
openingEnded();
|
|
844
1076
|
}
|
|
845
1077
|
else if (event.kind === 'listed') {
|
|
846
1078
|
listing.set(event.entry.conversation, { ...event.entry });
|
|
1079
|
+
creations.delete(event.entry.conversation);
|
|
847
1080
|
}
|
|
848
1081
|
else {
|
|
849
1082
|
listing.delete(event.conversation);
|
|
@@ -866,6 +1099,7 @@ export function createConversationStore(deps) {
|
|
|
866
1099
|
close() {
|
|
867
1100
|
views.clear();
|
|
868
1101
|
lives.clear();
|
|
1102
|
+
unsent.clear();
|
|
869
1103
|
listWatchers.clear();
|
|
870
1104
|
agentWatchers.clear();
|
|
871
1105
|
},
|