@agentshouse/mdruntime 0.1.0-c967d1ca66fee → 0.1.0-c97456d63baba
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 +257 -227
- package/README.md +11 -1
- package/dist/acquisition.d.ts +14 -0
- package/dist/acquisition.d.ts.map +1 -0
- package/dist/acquisition.js +24 -0
- package/dist/adapter.d.ts +69 -20
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attempt.d.ts +16 -0
- package/dist/attempt.d.ts.map +1 -0
- package/dist/attempt.js +70 -0
- package/dist/binary.d.ts +23 -0
- package/dist/binary.d.ts.map +1 -0
- package/dist/binary.js +8 -0
- package/dist/bootstrap.d.ts +2 -2
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +33 -14
- package/dist/change-set.d.ts +2 -0
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +39 -13
- package/dist/change.d.ts +11 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +26 -1
- package/dist/chat/acts.d.ts +33 -0
- package/dist/chat/acts.d.ts.map +1 -0
- package/dist/chat/acts.js +36 -0
- package/dist/chat/context.d.ts +3 -0
- package/dist/chat/context.d.ts.map +1 -0
- package/dist/chat/context.js +10 -0
- package/dist/chat/input.d.ts +9 -0
- package/dist/chat/input.d.ts.map +1 -0
- package/dist/chat/input.js +99 -0
- package/dist/chat/kit.d.ts +61 -0
- package/dist/chat/kit.d.ts.map +1 -0
- package/dist/chat/kit.js +123 -0
- package/dist/chat/lasting.d.ts +6 -0
- package/dist/chat/lasting.d.ts.map +1 -0
- package/dist/chat/lasting.js +19 -0
- package/dist/chat/list.d.ts +27 -0
- package/dist/chat/list.d.ts.map +1 -0
- package/dist/chat/list.js +63 -0
- package/dist/chat/markdown.d.ts +20 -0
- package/dist/chat/markdown.d.ts.map +1 -0
- package/dist/chat/markdown.js +188 -0
- package/dist/chat/message.d.ts +6 -0
- package/dist/chat/message.d.ts.map +1 -0
- package/dist/chat/message.js +192 -0
- package/dist/chat/messages.d.ts +22 -0
- package/dist/chat/messages.d.ts.map +1 -0
- package/dist/chat/messages.js +155 -0
- package/dist/chat/name.d.ts +6 -0
- package/dist/chat/name.d.ts.map +1 -0
- package/dist/chat/name.js +37 -0
- package/dist/chat/opened.d.ts +11 -0
- package/dist/chat/opened.d.ts.map +1 -0
- package/dist/chat/opened.js +17 -0
- package/dist/chat/overlay.d.ts +31 -0
- package/dist/chat/overlay.d.ts.map +1 -0
- package/dist/chat/overlay.js +97 -0
- package/dist/chat/parts.d.ts +57 -0
- package/dist/chat/parts.d.ts.map +1 -0
- package/dist/chat/parts.js +119 -0
- package/dist/chat/process.d.ts +20 -0
- package/dist/chat/process.d.ts.map +1 -0
- package/dist/chat/process.js +32 -0
- package/dist/chat/provider.d.ts +63 -0
- package/dist/chat/provider.d.ts.map +1 -0
- package/dist/chat/provider.js +629 -0
- package/dist/chat/question.d.ts +9 -0
- package/dist/chat/question.d.ts.map +1 -0
- package/dist/chat/question.js +80 -0
- package/dist/chat/questions.d.ts +31 -0
- package/dist/chat/questions.d.ts.map +1 -0
- package/dist/chat/questions.js +111 -0
- package/dist/chat/read.d.ts +5 -0
- package/dist/chat/read.d.ts.map +1 -0
- package/dist/chat/read.js +13 -0
- package/dist/chat/ready.d.ts +34 -0
- package/dist/chat/ready.d.ts.map +1 -0
- package/dist/chat/ready.js +91 -0
- package/dist/chat/style.d.ts +4 -0
- package/dist/chat/style.d.ts.map +1 -0
- package/dist/chat/style.js +302 -0
- package/dist/chat.d.ts +14 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +8 -0
- package/dist/client.d.ts +21 -10
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +14 -2
- package/dist/collection.d.ts +17 -3
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +230 -19
- package/dist/connection.d.ts +32 -12
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +1482 -351
- package/dist/conversation.d.ts +23 -9
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +372 -142
- package/dist/durability.d.ts +7 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/entry.d.ts +17 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +156 -0
- package/dist/expectation.d.ts +21 -0
- package/dist/expectation.d.ts.map +1 -0
- package/dist/expectation.js +70 -0
- package/dist/fence.d.ts +6 -0
- package/dist/fence.d.ts.map +1 -0
- package/dist/fence.js +10 -0
- package/dist/host.d.ts +13 -3
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +48 -11
- package/dist/identity.d.ts +4 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +12 -0
- package/dist/index.d.ts +13 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/memory.d.ts +5 -3
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +47 -13
- package/dist/model.d.ts +4 -2
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +10 -1
- package/dist/observation.d.ts +5 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +22 -11
- package/dist/outcome.d.ts +11 -1
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +7 -0
- package/dist/overlay.d.ts +14 -2
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +75 -62
- package/dist/query.d.ts +31 -3
- package/dist/query.d.ts.map +1 -1
- package/dist/reach.d.ts +8 -2
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +42 -8
- package/dist/react.d.ts +7 -4
- package/dist/react.d.ts.map +1 -1
- package/dist/read-side.d.ts +13 -6
- package/dist/read-side.d.ts.map +1 -1
- package/dist/read-side.js +291 -85
- package/dist/readiness.d.ts +53 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +33 -0
- package/dist/recovery.d.ts +25 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +197 -0
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +21 -25
- package/dist/replica.d.ts +6 -3
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +78 -45
- package/dist/room-draft.d.ts +20 -3
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +119 -1
- package/dist/rows.d.ts +6 -13
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +5 -13
- package/dist/save.d.ts +28 -0
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +343 -29
- package/dist/search.d.ts +28 -6
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +108 -45
- package/dist/shared.d.ts +11 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +368 -0
- package/dist/status.d.ts +4 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/store.d.ts +55 -21
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +424 -206
- package/dist/turn.d.ts +7 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +45 -0
- package/dist/wire.d.ts +137 -30
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +49 -29
- package/package.json +26 -14
- package/dist/attachment.d.ts +0 -3
- package/dist/attachment.d.ts.map +0 -1
- package/dist/attachment.js +0 -8
package/dist/conversation.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { conversationChangeSet } from './change-set.js';
|
|
2
2
|
import { runtimeError } from './failure.js';
|
|
3
3
|
import { declaredFile } from './file.js';
|
|
4
|
+
import { changeBatchIdentity, chatDraftRevision } from './identity.js';
|
|
5
|
+
import { OFFLINE } from './outcome.js';
|
|
4
6
|
const QUIET = {
|
|
5
7
|
turn: null,
|
|
6
8
|
questions: [],
|
|
@@ -20,6 +22,17 @@ export function unheldView(conversation) {
|
|
|
20
22
|
chatDraft: null,
|
|
21
23
|
};
|
|
22
24
|
}
|
|
25
|
+
export const SESSION_STARTED = 'Session started';
|
|
26
|
+
const NEW_CHAT = 'new:';
|
|
27
|
+
export function newChat(agent) {
|
|
28
|
+
return `${NEW_CHAT}${agent}`;
|
|
29
|
+
}
|
|
30
|
+
function creationOf(input) {
|
|
31
|
+
if (typeof input !== 'object' || input === null)
|
|
32
|
+
return null;
|
|
33
|
+
const { agent_id: agent, operation_id: operation } = input;
|
|
34
|
+
return typeof agent === 'string' && typeof operation === 'string' ? { agent, operation } : null;
|
|
35
|
+
}
|
|
23
36
|
function startedTurn(turn, operation) {
|
|
24
37
|
if (operation.op === 'begin')
|
|
25
38
|
return { turn, started: operation.at, parts: [], context: null };
|
|
@@ -76,11 +89,15 @@ function ordered(left, right) {
|
|
|
76
89
|
return left.identity < right.identity ? -1 : 1;
|
|
77
90
|
return 0;
|
|
78
91
|
}
|
|
79
|
-
function
|
|
92
|
+
function windowOf(held, arriving, limit) {
|
|
80
93
|
const byIdentity = new Map(held.map((message) => [message.identity, message]));
|
|
81
94
|
for (const message of arriving)
|
|
82
95
|
byIdentity.set(message.identity, message);
|
|
83
|
-
|
|
96
|
+
const next = [...byIdentity.values()].sort(ordered);
|
|
97
|
+
return limit === undefined ? next : next.slice(Math.max(0, next.length - limit));
|
|
98
|
+
}
|
|
99
|
+
function newestOf(arriving, from) {
|
|
100
|
+
return arriving.reduce((top, message) => (top === null || message.position > top ? message.position : top), from);
|
|
84
101
|
}
|
|
85
102
|
function acceptedMessage(value) {
|
|
86
103
|
if (typeof value !== 'object' || value === null)
|
|
@@ -139,14 +156,14 @@ function historyPages(result) {
|
|
|
139
156
|
}
|
|
140
157
|
return pages;
|
|
141
158
|
}
|
|
142
|
-
function
|
|
159
|
+
function answered(result, field) {
|
|
143
160
|
if (result.kind !== 'action')
|
|
144
161
|
return null;
|
|
145
162
|
const answer = result.result;
|
|
146
163
|
if (typeof answer !== 'object' || answer === null)
|
|
147
164
|
return null;
|
|
148
|
-
const
|
|
149
|
-
return typeof
|
|
165
|
+
const value = answer[field];
|
|
166
|
+
return typeof value === 'string' ? value : null;
|
|
150
167
|
}
|
|
151
168
|
function limitOf(options) {
|
|
152
169
|
const { limit } = options;
|
|
@@ -157,9 +174,35 @@ function limitOf(options) {
|
|
|
157
174
|
}
|
|
158
175
|
return limit;
|
|
159
176
|
}
|
|
177
|
+
const EXPIRED = { kind: 'rejected', code: 'expired', fields: {}, retryable: false };
|
|
178
|
+
function unwritten(draft) {
|
|
179
|
+
return draft.text === '' && draft.files.length === 0;
|
|
180
|
+
}
|
|
181
|
+
function pending(draft) {
|
|
182
|
+
return {
|
|
183
|
+
...(draft.submitted === undefined ? {} : { submitted: draft.submitted }),
|
|
184
|
+
...(draft.moves === undefined ? {} : { moves: draft.moves }),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function empty(draft) {
|
|
188
|
+
return unwritten(draft) && draft.submitted === undefined && draft.moves === undefined;
|
|
189
|
+
}
|
|
190
|
+
function withSubmitted(draft, submitted) {
|
|
191
|
+
const { submitted: _held, ...unsent } = draft;
|
|
192
|
+
return submitted.length === 0 ? unsent : { ...unsent, submitted };
|
|
193
|
+
}
|
|
194
|
+
function returned(draft, submission, others) {
|
|
195
|
+
return withSubmitted({
|
|
196
|
+
conversation: draft.conversation,
|
|
197
|
+
text: [submission.text, draft.text].filter((one) => one !== '').join('\n'),
|
|
198
|
+
files: [...submission.files, ...draft.files],
|
|
199
|
+
revision: chatDraftRevision(),
|
|
200
|
+
}, others);
|
|
201
|
+
}
|
|
160
202
|
export function createConversationStore(deps) {
|
|
161
|
-
const
|
|
203
|
+
const stored = new Set();
|
|
162
204
|
const reads = new Map();
|
|
205
|
+
const partials = new Set();
|
|
163
206
|
const drafts = new Map();
|
|
164
207
|
const sending = new Map();
|
|
165
208
|
const behind = new Set();
|
|
@@ -167,58 +210,63 @@ export function createConversationStore(deps) {
|
|
|
167
210
|
const lives = new Map();
|
|
168
211
|
const views = new Map();
|
|
169
212
|
const listing = new Map();
|
|
213
|
+
const creations = new Set();
|
|
170
214
|
const listWatchers = new Set();
|
|
171
215
|
let listKnown = false;
|
|
216
|
+
const agentWatchers = new Set();
|
|
172
217
|
let agentNames = new Map();
|
|
218
|
+
let retiredAgents = new Set();
|
|
173
219
|
let agentsRead = null;
|
|
174
220
|
let replicated = new Set();
|
|
175
221
|
let handedReplicated;
|
|
176
222
|
function liveOf(conversation) {
|
|
177
223
|
return lives.get(conversation) ?? QUIET;
|
|
178
224
|
}
|
|
179
|
-
function
|
|
180
|
-
const held = messages.get(conversation) ?? [];
|
|
181
|
-
const shown = limit === undefined ? held : held.slice(Math.max(0, held.length - limit));
|
|
182
|
-
return shown.map(({ identity, turn, text, message }) => ({
|
|
183
|
-
identity,
|
|
184
|
-
turn,
|
|
185
|
-
text,
|
|
186
|
-
message: structuredClone(message),
|
|
187
|
-
}));
|
|
188
|
-
}
|
|
189
|
-
function viewOf(conversation, limit, shown = shownMessages(conversation, limit)) {
|
|
225
|
+
function viewOf(conversation, held) {
|
|
190
226
|
const draft = drafts.get(conversation);
|
|
191
227
|
const facts = liveOf(conversation);
|
|
192
228
|
return {
|
|
193
229
|
conversation,
|
|
194
|
-
messages:
|
|
230
|
+
messages: held.map(({ identity, turn, text, message }) => ({
|
|
231
|
+
identity,
|
|
232
|
+
turn,
|
|
233
|
+
text,
|
|
234
|
+
message,
|
|
235
|
+
})),
|
|
195
236
|
turn: structuredClone(facts.turn),
|
|
196
237
|
questions: structuredClone(facts.questions),
|
|
197
238
|
process: facts.process === null ? null : { ...facts.process },
|
|
198
239
|
commands: structuredClone(facts.commands),
|
|
199
240
|
options: structuredClone(facts.options),
|
|
200
|
-
chatDraft: draft === undefined || draft
|
|
241
|
+
chatDraft: draft === undefined || unwritten(draft)
|
|
201
242
|
? null
|
|
202
|
-
: { text: draft.text, files: [...draft.files] },
|
|
243
|
+
: { text: draft.text, files: [...draft.files], revision: draft.revision },
|
|
203
244
|
};
|
|
204
245
|
}
|
|
205
|
-
function emit(conversation
|
|
246
|
+
function emit(conversation) {
|
|
206
247
|
if (deps.closed())
|
|
207
248
|
return;
|
|
208
249
|
for (const view of views.values()) {
|
|
209
|
-
if (view.conversation !== conversation)
|
|
250
|
+
if (view.conversation !== conversation || view.window === null)
|
|
210
251
|
continue;
|
|
211
|
-
if (messagesChanged)
|
|
212
|
-
view.messages = null;
|
|
213
252
|
const ready = [...view.watchers].filter((watcher) => watcher.ready);
|
|
214
253
|
if (ready.length === 0)
|
|
215
254
|
continue;
|
|
216
|
-
|
|
217
|
-
const value = viewOf(conversation, view.limit, view.messages);
|
|
255
|
+
const value = viewOf(conversation, view.window);
|
|
218
256
|
for (const watcher of ready)
|
|
219
257
|
watcher.notify(value);
|
|
220
258
|
}
|
|
221
259
|
}
|
|
260
|
+
function slide(conversation, arriving) {
|
|
261
|
+
for (const view of views.values()) {
|
|
262
|
+
if (view.conversation !== conversation || view.window === null)
|
|
263
|
+
continue;
|
|
264
|
+
view.window = arriving === null ? [] : windowOf(view.window, arriving, view.limit);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function changesOf(conversation, arriving) {
|
|
268
|
+
return arriving.map(({ identity, text }) => ({ conversation, identity, text }));
|
|
269
|
+
}
|
|
222
270
|
function stateOf(conversation) {
|
|
223
271
|
const facts = liveOf(conversation);
|
|
224
272
|
if (facts.questions.length > 0)
|
|
@@ -244,6 +292,21 @@ export function createConversationStore(deps) {
|
|
|
244
292
|
for (const watcher of listWatchers)
|
|
245
293
|
watcher(value);
|
|
246
294
|
}
|
|
295
|
+
function agents() {
|
|
296
|
+
return [...agentNames]
|
|
297
|
+
.filter(([identity]) => !retiredAgents.has(identity))
|
|
298
|
+
.map(([identity, name]) => ({ identity, name }))
|
|
299
|
+
.sort((left, right) => left.name === right.name
|
|
300
|
+
? left.identity.localeCompare(right.identity)
|
|
301
|
+
: left.name.localeCompare(right.name));
|
|
302
|
+
}
|
|
303
|
+
function emitAgents() {
|
|
304
|
+
if (deps.closed() || agentWatchers.size === 0)
|
|
305
|
+
return;
|
|
306
|
+
const value = agents();
|
|
307
|
+
for (const watcher of agentWatchers)
|
|
308
|
+
watcher(value);
|
|
309
|
+
}
|
|
247
310
|
function setLive(conversation, next) {
|
|
248
311
|
const before = stateOf(conversation);
|
|
249
312
|
const began = lives.get(conversation)?.turn?.started;
|
|
@@ -260,20 +323,38 @@ export function createConversationStore(deps) {
|
|
|
260
323
|
const declaration = deps.agents();
|
|
261
324
|
if (declaration === undefined)
|
|
262
325
|
return;
|
|
263
|
-
const
|
|
326
|
+
const held = await deps.documents(declaration);
|
|
327
|
+
if ('pending' in held)
|
|
328
|
+
return;
|
|
329
|
+
const { documents } = held;
|
|
264
330
|
const names = new Map();
|
|
331
|
+
const retired = new Set();
|
|
265
332
|
for (const document of documents) {
|
|
266
333
|
if (document.type !== declaration.type)
|
|
267
334
|
continue;
|
|
268
335
|
const identity = document.fields?.[declaration.identity];
|
|
269
336
|
const name = document.fields?.[declaration.name];
|
|
270
|
-
if (typeof identity
|
|
271
|
-
|
|
337
|
+
if (typeof identity !== 'string' || typeof name !== 'string')
|
|
338
|
+
continue;
|
|
339
|
+
names.set(identity, name);
|
|
340
|
+
const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
|
|
341
|
+
if (ended !== undefined && ended !== null)
|
|
342
|
+
retired.add(identity);
|
|
272
343
|
}
|
|
273
|
-
|
|
344
|
+
const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
|
|
345
|
+
if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
|
|
274
346
|
return;
|
|
275
347
|
agentNames = names;
|
|
276
|
-
|
|
348
|
+
retiredAgents = retired;
|
|
349
|
+
if (named)
|
|
350
|
+
emitList();
|
|
351
|
+
emitAgents();
|
|
352
|
+
const ended = [...drafts.keys()].filter(retiredChat);
|
|
353
|
+
if (ended.length > 0)
|
|
354
|
+
await remove(ended);
|
|
355
|
+
}
|
|
356
|
+
function retiredChat(conversation) {
|
|
357
|
+
return (conversation.startsWith(NEW_CHAT) && retiredAgents.has(conversation.slice(NEW_CHAT.length)));
|
|
277
358
|
}
|
|
278
359
|
function agentsReady() {
|
|
279
360
|
agentsRead ??= readAgents().catch(() => {
|
|
@@ -283,7 +364,7 @@ export function createConversationStore(deps) {
|
|
|
283
364
|
}
|
|
284
365
|
function live(conversation, change) {
|
|
285
366
|
setLive(conversation, { ...liveOf(conversation), ...change });
|
|
286
|
-
emit(conversation
|
|
367
|
+
emit(conversation);
|
|
287
368
|
}
|
|
288
369
|
function relayed(conversation, turn, operation) {
|
|
289
370
|
const held = liveOf(conversation).turn;
|
|
@@ -311,21 +392,25 @@ export function createConversationStore(deps) {
|
|
|
311
392
|
}
|
|
312
393
|
async function remove(leaving) {
|
|
313
394
|
await deps.durably(async (local) => {
|
|
314
|
-
await local.dropConversations(leaving);
|
|
315
395
|
const removed = [];
|
|
316
|
-
const changed = [];
|
|
317
396
|
for (const conversation of leaving) {
|
|
318
|
-
for (const
|
|
319
|
-
removed.push({ conversation, identity:
|
|
397
|
+
for (const identity of await local.messageIdentities(conversation)) {
|
|
398
|
+
removed.push({ conversation, identity, text: null });
|
|
320
399
|
}
|
|
321
|
-
|
|
400
|
+
}
|
|
401
|
+
await local.dropConversations(leaving);
|
|
402
|
+
const changed = [];
|
|
403
|
+
for (const conversation of leaving) {
|
|
404
|
+
if (stored.has(conversation) || drafts.has(conversation) || lives.has(conversation)) {
|
|
322
405
|
changed.push(conversation);
|
|
323
406
|
}
|
|
324
|
-
|
|
407
|
+
stored.delete(conversation);
|
|
325
408
|
reads.delete(conversation);
|
|
409
|
+
partials.delete(conversation);
|
|
326
410
|
drafts.delete(conversation);
|
|
327
411
|
behind.delete(conversation);
|
|
328
412
|
setLive(conversation, null);
|
|
413
|
+
slide(conversation, null);
|
|
329
414
|
}
|
|
330
415
|
if (changed.length > 0)
|
|
331
416
|
deps.publish(conversationChangeSet(removed, changed));
|
|
@@ -343,30 +428,25 @@ export function createConversationStore(deps) {
|
|
|
343
428
|
const advances = read !== undefined &&
|
|
344
429
|
!behind.has(conversation) &&
|
|
345
430
|
(read === null || message.position > read);
|
|
346
|
-
|
|
431
|
+
await sealed(local, conversation, new Set([message.turn]));
|
|
347
432
|
await local.keepMessages({
|
|
348
433
|
conversation,
|
|
349
434
|
messages: [message],
|
|
350
|
-
...(advances ? { read: message.position } : {}),
|
|
351
|
-
sealed,
|
|
435
|
+
...(advances ? { read: { at: message.position, partial: false } } : {}),
|
|
352
436
|
});
|
|
353
|
-
|
|
437
|
+
stored.add(conversation);
|
|
354
438
|
if (advances)
|
|
355
439
|
reads.set(conversation, message.position);
|
|
356
|
-
if (sealed)
|
|
357
|
-
drafts.delete(conversation);
|
|
358
440
|
sealTurns(conversation, new Set([message.turn]));
|
|
359
|
-
|
|
441
|
+
slide(conversation, [message]);
|
|
442
|
+
deps.publish(conversationChangeSet(changesOf(conversation, [message]), [conversation]));
|
|
360
443
|
});
|
|
361
444
|
}
|
|
362
445
|
async function readHistories(conversations) {
|
|
363
446
|
const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
|
|
364
|
-
const starts = new Map(conversations.map((one) => [one, reads.get(one)]));
|
|
365
|
-
const arriving = new Map();
|
|
366
|
-
const complete = new Set();
|
|
367
447
|
let asking = conversations.map((conversation) => ({
|
|
368
448
|
conversation,
|
|
369
|
-
after:
|
|
449
|
+
after: reads.get(conversation) ?? undefined,
|
|
370
450
|
}));
|
|
371
451
|
while (asking.length > 0) {
|
|
372
452
|
const result = await deps.call('conversation.history', {
|
|
@@ -378,46 +458,41 @@ export function createConversationStore(deps) {
|
|
|
378
458
|
const pages = historyPages(result);
|
|
379
459
|
if (pages === null)
|
|
380
460
|
return result;
|
|
381
|
-
const asked = new
|
|
461
|
+
const asked = new Map(asking.map(({ conversation, after }) => [conversation, after]));
|
|
382
462
|
const following = [];
|
|
383
463
|
for (const page of pages) {
|
|
384
464
|
if (!asked.has(page.conversation))
|
|
385
465
|
continue;
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
...page.messages,
|
|
389
|
-
]);
|
|
390
|
-
if (page.next === undefined)
|
|
391
|
-
complete.add(page.conversation);
|
|
392
|
-
else
|
|
466
|
+
await keepPage(page.conversation, page.messages, asked.get(page.conversation) ?? null, page.next, generations.get(page.conversation) ?? 0);
|
|
467
|
+
if (page.next !== undefined) {
|
|
393
468
|
following.push({ conversation: page.conversation, after: page.next });
|
|
469
|
+
}
|
|
394
470
|
}
|
|
395
471
|
asking = following;
|
|
396
472
|
}
|
|
397
|
-
for (const [conversation, held] of arriving) {
|
|
398
|
-
if (!complete.has(conversation))
|
|
399
|
-
continue;
|
|
400
|
-
await keepHistory(conversation, held, starts.get(conversation), generations.get(conversation) ?? 0);
|
|
401
|
-
}
|
|
402
473
|
return null;
|
|
403
474
|
}
|
|
404
|
-
function
|
|
475
|
+
function keepPage(conversation, arriving, from, next, generation) {
|
|
405
476
|
return deps.durably(async (local) => {
|
|
406
477
|
if (!replicated.has(conversation))
|
|
407
478
|
return;
|
|
408
|
-
const
|
|
409
|
-
const
|
|
410
|
-
const
|
|
411
|
-
await local
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
479
|
+
const whole = next === undefined;
|
|
480
|
+
const cursor = next ?? newestOf(arriving, from);
|
|
481
|
+
const partial = !whole && (partials.has(conversation) || !reads.has(conversation));
|
|
482
|
+
const sealing = await sealed(local, conversation, new Set(arriving.map((message) => message.turn)));
|
|
483
|
+
await local.keepMessages({ conversation, messages: arriving, read: { at: cursor, partial } });
|
|
484
|
+
stored.add(conversation);
|
|
485
|
+
reads.set(conversation, cursor);
|
|
486
|
+
if (partial)
|
|
487
|
+
partials.add(conversation);
|
|
488
|
+
else
|
|
489
|
+
partials.delete(conversation);
|
|
416
490
|
sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
|
|
417
|
-
if ((gaps.get(conversation) ?? 0) === generation)
|
|
491
|
+
if (whole && (gaps.get(conversation) ?? 0) === generation)
|
|
418
492
|
behind.delete(conversation);
|
|
419
|
-
|
|
420
|
-
|
|
493
|
+
slide(conversation, arriving);
|
|
494
|
+
if (arriving.length > 0 || sealing || whole) {
|
|
495
|
+
deps.publish(conversationChangeSet(changesOf(conversation, arriving), [conversation]));
|
|
421
496
|
}
|
|
422
497
|
});
|
|
423
498
|
}
|
|
@@ -430,7 +505,7 @@ export function createConversationStore(deps) {
|
|
|
430
505
|
if (held === undefined)
|
|
431
506
|
return;
|
|
432
507
|
setLive(conversation, { ...QUIET, turn: held.turn });
|
|
433
|
-
emit(conversation
|
|
508
|
+
emit(conversation);
|
|
434
509
|
return;
|
|
435
510
|
}
|
|
436
511
|
if (event.kind === 'turn') {
|
|
@@ -463,22 +538,126 @@ export function createConversationStore(deps) {
|
|
|
463
538
|
if (sealTurns(conversation, new Set([arriving.turn])))
|
|
464
539
|
emit(conversation);
|
|
465
540
|
}
|
|
541
|
+
async function kept(local, conversation, following) {
|
|
542
|
+
if (following === null || empty(following)) {
|
|
543
|
+
await local.dropChatDraft(conversation);
|
|
544
|
+
drafts.delete(conversation);
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
await local.keepChatDrafts([following]);
|
|
548
|
+
drafts.set(conversation, following);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
async function sealed(local, conversation, turns) {
|
|
552
|
+
const held = drafts.get(conversation);
|
|
553
|
+
const submitted = held?.submitted ?? [];
|
|
554
|
+
const left = submitted.filter((one) => one.turn === null || !turns.has(one.turn));
|
|
555
|
+
if (held === undefined || left.length === submitted.length)
|
|
556
|
+
return false;
|
|
557
|
+
await kept(local, conversation, withSubmitted(held, left));
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
560
|
+
function moved(agent, operation, to) {
|
|
561
|
+
const from = newChat(agent);
|
|
562
|
+
return deps.durably(async (local) => {
|
|
563
|
+
const held = drafts.get(from);
|
|
564
|
+
const earlier = (held?.moves ?? []).filter((one) => !deps.expired(one));
|
|
565
|
+
if (earlier.includes(operation))
|
|
566
|
+
return;
|
|
567
|
+
const blank = {
|
|
568
|
+
conversation: from,
|
|
569
|
+
text: '',
|
|
570
|
+
files: [],
|
|
571
|
+
revision: chatDraftRevision(),
|
|
572
|
+
};
|
|
573
|
+
const { moves: _answered, ...unsent } = held ?? blank;
|
|
574
|
+
const taken = drafts.has(to) || empty(unsent) ? [] : [{ ...unsent, conversation: to }];
|
|
575
|
+
const left = { ...(taken.length === 0 ? unsent : blank), moves: [...earlier, operation] };
|
|
576
|
+
await local.keepChatDrafts([left, ...taken]);
|
|
577
|
+
drafts.set(from, left);
|
|
578
|
+
for (const one of taken)
|
|
579
|
+
drafts.set(to, one);
|
|
580
|
+
deps.publish(conversationChangeSet([], [from, to]));
|
|
581
|
+
});
|
|
582
|
+
}
|
|
466
583
|
function withDraft(conversation, next) {
|
|
467
584
|
return deps.durably(async (local) => {
|
|
468
|
-
const following = next(drafts.get(conversation));
|
|
585
|
+
const following = await next(drafts.get(conversation), local);
|
|
469
586
|
if (following === undefined)
|
|
470
587
|
return;
|
|
471
|
-
|
|
472
|
-
await local.dropChatDraft(conversation);
|
|
473
|
-
drafts.delete(conversation);
|
|
474
|
-
}
|
|
475
|
-
else {
|
|
476
|
-
await local.keepChatDraft(following);
|
|
477
|
-
drafts.set(conversation, following);
|
|
478
|
-
}
|
|
588
|
+
await kept(local, conversation, following);
|
|
479
589
|
deps.publish(conversationChangeSet([], [conversation]));
|
|
480
590
|
});
|
|
481
591
|
}
|
|
592
|
+
async function submitted(conversation, operation, draft) {
|
|
593
|
+
let submission;
|
|
594
|
+
let earlier = false;
|
|
595
|
+
await withDraft(conversation, (current) => {
|
|
596
|
+
submission = current?.submitted?.find((one) => one.operation === operation);
|
|
597
|
+
earlier = submission !== undefined;
|
|
598
|
+
if (submission !== undefined)
|
|
599
|
+
return undefined;
|
|
600
|
+
const said = draft ?? current;
|
|
601
|
+
submission = {
|
|
602
|
+
operation,
|
|
603
|
+
text: said?.text ?? '',
|
|
604
|
+
files: [...(said?.files ?? [])],
|
|
605
|
+
turn: null,
|
|
606
|
+
};
|
|
607
|
+
return {
|
|
608
|
+
conversation,
|
|
609
|
+
...(draft === undefined || current === undefined
|
|
610
|
+
? { text: '', files: [], revision: chatDraftRevision() }
|
|
611
|
+
: { text: current.text, files: current.files, revision: current.revision }),
|
|
612
|
+
submitted: [...(current?.submitted ?? []), submission],
|
|
613
|
+
};
|
|
614
|
+
});
|
|
615
|
+
return { submission: submission, earlier };
|
|
616
|
+
}
|
|
617
|
+
function forgotten(conversation, submission) {
|
|
618
|
+
return withDraft(conversation, (current) => {
|
|
619
|
+
const held = current?.submitted ?? [];
|
|
620
|
+
if (current === undefined || !held.includes(submission))
|
|
621
|
+
return undefined;
|
|
622
|
+
return withSubmitted(current, held.filter((one) => one !== submission));
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
async function dispatched(conversation, submission) {
|
|
626
|
+
const dispatch = {};
|
|
627
|
+
sending.set(submission.operation, dispatch);
|
|
628
|
+
try {
|
|
629
|
+
const files = await Promise.all(submission.files.map(async (file) => ({ ...(await declaredFile(file)), blob: file.blob })));
|
|
630
|
+
const result = await invoke('conversation.send', conversation, {
|
|
631
|
+
text: submission.text,
|
|
632
|
+
files,
|
|
633
|
+
operation_id: submission.operation,
|
|
634
|
+
});
|
|
635
|
+
if (result.kind === 'revoked-authorization')
|
|
636
|
+
return result;
|
|
637
|
+
if (result.kind !== 'action' && result.retryable) {
|
|
638
|
+
deps.unanswered();
|
|
639
|
+
return result;
|
|
640
|
+
}
|
|
641
|
+
const turn = answered(result, 'turn');
|
|
642
|
+
await withDraft(conversation, async (current, local) => {
|
|
643
|
+
const held = current?.submitted?.find((one) => one.operation === submission.operation);
|
|
644
|
+
if (current === undefined || held === undefined || held.turn !== null)
|
|
645
|
+
return undefined;
|
|
646
|
+
const others = current.submitted.filter((one) => one !== held);
|
|
647
|
+
if (result.kind !== 'action')
|
|
648
|
+
return returned(current, held, others);
|
|
649
|
+
const arrived = turn !== null && (await local.turnArrived(conversation, turn));
|
|
650
|
+
return withSubmitted(current, turn === null || arrived
|
|
651
|
+
? others
|
|
652
|
+
: current.submitted.map((one) => (one === held ? { ...held, turn } : one)));
|
|
653
|
+
});
|
|
654
|
+
return result;
|
|
655
|
+
}
|
|
656
|
+
finally {
|
|
657
|
+
if (sending.get(submission.operation) === dispatch)
|
|
658
|
+
sending.delete(submission.operation);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
482
661
|
function invoke(name, conversation, extra = {}) {
|
|
483
662
|
return deps.call(name, { conversation, ...extra });
|
|
484
663
|
}
|
|
@@ -489,46 +668,34 @@ export function createConversationStore(deps) {
|
|
|
489
668
|
},
|
|
490
669
|
async read(conversation, options = {}) {
|
|
491
670
|
const limit = limitOf(options);
|
|
492
|
-
return deps.durably(() =>
|
|
671
|
+
return deps.durably(async (local) => structuredClone(viewOf(conversation, await local.messageWindow(conversation, limit))));
|
|
493
672
|
},
|
|
494
|
-
create(input) {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
const draft = drafts.get(conversation);
|
|
499
|
-
const pending = draft === undefined ? undefined : { draft };
|
|
500
|
-
if (pending !== undefined) {
|
|
501
|
-
sending.set(conversation, pending);
|
|
502
|
-
deps.publish(conversationChangeSet([], [conversation]));
|
|
503
|
-
}
|
|
504
|
-
try {
|
|
505
|
-
const files = await Promise.all((draft?.files ?? []).map(async (file) => ({
|
|
506
|
-
...(await declaredFile(file)),
|
|
507
|
-
blob: file.blob,
|
|
508
|
-
})));
|
|
509
|
-
const result = await invoke('conversation.send', conversation, {
|
|
510
|
-
text: draft?.text ?? '',
|
|
511
|
-
files,
|
|
512
|
-
});
|
|
513
|
-
const turn = turnOf(result);
|
|
514
|
-
if (turn === null || draft === undefined)
|
|
515
|
-
return result;
|
|
516
|
-
await withDraft(conversation, (current) => {
|
|
517
|
-
if (current !== draft)
|
|
518
|
-
return undefined;
|
|
519
|
-
const sealed = (messages.get(conversation) ?? []).some((message) => message.turn === turn);
|
|
520
|
-
return sealed ? null : { ...draft, turn };
|
|
521
|
-
});
|
|
673
|
+
async create(input) {
|
|
674
|
+
const result = await deps.call('conversation.create', input);
|
|
675
|
+
const conversation = answered(result, 'conversation');
|
|
676
|
+
if (conversation === null)
|
|
522
677
|
return result;
|
|
678
|
+
if (!listing.has(conversation))
|
|
679
|
+
creations.add(conversation);
|
|
680
|
+
const creation = creationOf(input);
|
|
681
|
+
if (creation !== null)
|
|
682
|
+
await moved(creation.agent, creation.operation, conversation);
|
|
683
|
+
return result;
|
|
684
|
+
},
|
|
685
|
+
async send(conversation, operation = changeBatchIdentity(), draft) {
|
|
686
|
+
if (deps.online()) {
|
|
687
|
+
const { submission, earlier } = await submitted(conversation, operation, draft);
|
|
688
|
+
if (!earlier || !deps.expired(operation))
|
|
689
|
+
return dispatched(conversation, submission);
|
|
690
|
+
await forgotten(conversation, submission);
|
|
691
|
+
return EXPIRED;
|
|
523
692
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
deps.publish(conversationChangeSet([], [conversation]));
|
|
529
|
-
}
|
|
530
|
-
}
|
|
693
|
+
if (draft !== undefined) {
|
|
694
|
+
await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
|
|
695
|
+
? undefined
|
|
696
|
+
: returned(current ?? { conversation, text: '', files: [], revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
|
|
531
697
|
}
|
|
698
|
+
return OFFLINE;
|
|
532
699
|
},
|
|
533
700
|
answer(conversation, answer) {
|
|
534
701
|
return invoke('conversation.answer', conversation, { answer });
|
|
@@ -543,7 +710,12 @@ export function createConversationStore(deps) {
|
|
|
543
710
|
const result = await invoke('conversation.cancel', conversation, { turn });
|
|
544
711
|
if (result.kind !== 'action')
|
|
545
712
|
return result;
|
|
546
|
-
await withDraft(conversation, (current) =>
|
|
713
|
+
await withDraft(conversation, (current) => {
|
|
714
|
+
const held = current?.submitted?.find((one) => one.turn === turn);
|
|
715
|
+
if (current === undefined || held === undefined)
|
|
716
|
+
return undefined;
|
|
717
|
+
return returned(current, held, current.submitted.filter((one) => one !== held));
|
|
718
|
+
});
|
|
547
719
|
return result;
|
|
548
720
|
},
|
|
549
721
|
delete(conversation) {
|
|
@@ -551,33 +723,43 @@ export function createConversationStore(deps) {
|
|
|
551
723
|
},
|
|
552
724
|
};
|
|
553
725
|
const chatDraft = {
|
|
554
|
-
write(conversation, draft) {
|
|
555
|
-
return withDraft(conversation, () => ({
|
|
726
|
+
write(conversation, draft, revision = chatDraftRevision()) {
|
|
727
|
+
return withDraft(conversation, (current) => ({
|
|
556
728
|
conversation,
|
|
557
729
|
text: draft.text,
|
|
558
730
|
files: [...draft.files],
|
|
559
|
-
|
|
731
|
+
revision,
|
|
732
|
+
...(current === undefined ? {} : pending(current)),
|
|
560
733
|
}));
|
|
561
734
|
},
|
|
562
735
|
discard(conversation) {
|
|
563
|
-
return withDraft(conversation, (current) =>
|
|
736
|
+
return withDraft(conversation, (current) => current === undefined
|
|
737
|
+
? undefined
|
|
738
|
+
: {
|
|
739
|
+
conversation,
|
|
740
|
+
text: '',
|
|
741
|
+
files: [],
|
|
742
|
+
revision: chatDraftRevision(),
|
|
743
|
+
...pending(current),
|
|
744
|
+
});
|
|
564
745
|
},
|
|
565
746
|
};
|
|
566
747
|
return {
|
|
567
748
|
surface,
|
|
568
749
|
chatDraft,
|
|
569
|
-
hydrate(held,
|
|
570
|
-
|
|
750
|
+
hydrate(held, kept) {
|
|
751
|
+
stored.clear();
|
|
571
752
|
reads.clear();
|
|
572
|
-
|
|
573
|
-
view.messages = null;
|
|
753
|
+
partials.clear();
|
|
574
754
|
for (const entry of held) {
|
|
575
|
-
|
|
755
|
+
stored.add(entry.conversation);
|
|
576
756
|
if (entry.read !== undefined)
|
|
577
757
|
reads.set(entry.conversation, entry.read);
|
|
758
|
+
if (entry.partial)
|
|
759
|
+
partials.add(entry.conversation);
|
|
578
760
|
}
|
|
579
761
|
drafts.clear();
|
|
580
|
-
for (const record of
|
|
762
|
+
for (const record of kept)
|
|
581
763
|
drafts.set(record.conversation, record);
|
|
582
764
|
},
|
|
583
765
|
async reconcile() {
|
|
@@ -585,8 +767,10 @@ export function createConversationStore(deps) {
|
|
|
585
767
|
return;
|
|
586
768
|
const named = new Set(listing.keys());
|
|
587
769
|
const leaving = [
|
|
588
|
-
...new Set([...replicated, ...
|
|
589
|
-
].filter((conversation) => !named.has(conversation)
|
|
770
|
+
...new Set([...replicated, ...stored, ...reads.keys(), ...drafts.keys()]),
|
|
771
|
+
].filter((conversation) => !named.has(conversation) &&
|
|
772
|
+
!creations.has(conversation) &&
|
|
773
|
+
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
|
|
590
774
|
for (const conversation of named) {
|
|
591
775
|
if (!replicated.has(conversation))
|
|
592
776
|
behind.add(conversation);
|
|
@@ -606,13 +790,44 @@ export function createConversationStore(deps) {
|
|
|
606
790
|
if (behind.size > 0)
|
|
607
791
|
progress.complete = false;
|
|
608
792
|
},
|
|
793
|
+
async resend(progress) {
|
|
794
|
+
for (const [conversation, held] of [...drafts]) {
|
|
795
|
+
for (const submission of held.submitted ?? []) {
|
|
796
|
+
if (submission.turn !== null || sending.has(submission.operation))
|
|
797
|
+
continue;
|
|
798
|
+
if (deps.closed())
|
|
799
|
+
return;
|
|
800
|
+
if (deps.expired(submission.operation)) {
|
|
801
|
+
await forgotten(conversation, submission);
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
const result = await dispatched(conversation, submission);
|
|
805
|
+
if (result.kind === 'revoked-authorization')
|
|
806
|
+
return;
|
|
807
|
+
if (result.kind === 'transport-uncertainty') {
|
|
808
|
+
progress.reachable = false;
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
if (result.kind !== 'action' && result.retryable) {
|
|
812
|
+
progress.complete = false;
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
},
|
|
609
818
|
current() {
|
|
610
819
|
return behind.size === 0;
|
|
611
820
|
},
|
|
821
|
+
known() {
|
|
822
|
+
return listKnown;
|
|
823
|
+
},
|
|
612
824
|
reconnected() {
|
|
613
825
|
for (const conversation of replicated)
|
|
614
826
|
gap(conversation);
|
|
615
827
|
},
|
|
828
|
+
reopened() {
|
|
829
|
+
sending.clear();
|
|
830
|
+
},
|
|
616
831
|
watch(conversation, options, deliver) {
|
|
617
832
|
const limit = limitOf(options);
|
|
618
833
|
const key = JSON.stringify([conversation, limit ?? null]);
|
|
@@ -620,18 +835,20 @@ export function createConversationStore(deps) {
|
|
|
620
835
|
conversation,
|
|
621
836
|
limit,
|
|
622
837
|
watchers: new Set(),
|
|
623
|
-
|
|
838
|
+
window: null,
|
|
624
839
|
};
|
|
625
840
|
views.set(key, view);
|
|
626
841
|
const watcher = { notify: deliver, ready: false };
|
|
627
842
|
view.watchers.add(watcher);
|
|
628
843
|
void deps
|
|
629
|
-
.durably(() =>
|
|
844
|
+
.durably(async (local) => {
|
|
845
|
+
view.window ??= await local.messageWindow(conversation, limit);
|
|
846
|
+
})
|
|
630
847
|
.then(() => {
|
|
631
848
|
if (deps.closed() || !view.watchers.has(watcher))
|
|
632
849
|
return;
|
|
633
850
|
watcher.ready = true;
|
|
634
|
-
watcher.notify(viewOf(conversation,
|
|
851
|
+
watcher.notify(viewOf(conversation, view.window ?? []));
|
|
635
852
|
})
|
|
636
853
|
.catch(() => { });
|
|
637
854
|
return () => {
|
|
@@ -651,6 +868,16 @@ export function createConversationStore(deps) {
|
|
|
651
868
|
listWatchers.delete(deliver);
|
|
652
869
|
};
|
|
653
870
|
},
|
|
871
|
+
watchAgents(deliver) {
|
|
872
|
+
agentWatchers.add(deliver);
|
|
873
|
+
void agentsReady().then(() => {
|
|
874
|
+
if (!deps.closed() && agentWatchers.has(deliver))
|
|
875
|
+
deliver(agents());
|
|
876
|
+
});
|
|
877
|
+
return () => {
|
|
878
|
+
agentWatchers.delete(deliver);
|
|
879
|
+
};
|
|
880
|
+
},
|
|
654
881
|
changed(set) {
|
|
655
882
|
for (const conversation of set.conversations)
|
|
656
883
|
emit(conversation);
|
|
@@ -668,12 +895,15 @@ export function createConversationStore(deps) {
|
|
|
668
895
|
if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
|
|
669
896
|
if (event.kind === 'list') {
|
|
670
897
|
listing.clear();
|
|
671
|
-
for (const entry of event.entries)
|
|
898
|
+
for (const entry of event.entries) {
|
|
672
899
|
listing.set(entry.conversation, { ...entry });
|
|
900
|
+
creations.delete(entry.conversation);
|
|
901
|
+
}
|
|
673
902
|
listKnown = true;
|
|
674
903
|
}
|
|
675
904
|
else if (event.kind === 'listed') {
|
|
676
905
|
listing.set(event.entry.conversation, { ...event.entry });
|
|
906
|
+
creations.delete(event.entry.conversation);
|
|
677
907
|
}
|
|
678
908
|
else {
|
|
679
909
|
listing.delete(event.conversation);
|
|
@@ -687,17 +917,17 @@ export function createConversationStore(deps) {
|
|
|
687
917
|
heard(event);
|
|
688
918
|
return false;
|
|
689
919
|
},
|
|
690
|
-
|
|
691
|
-
return [...
|
|
920
|
+
searchable() {
|
|
921
|
+
return [...(listKnown ? listing.keys() : stored)].sort();
|
|
692
922
|
},
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
return held === undefined ? null : { conversation, identity, text: held.text };
|
|
923
|
+
acquired(conversation) {
|
|
924
|
+
return reads.has(conversation) && !partials.has(conversation);
|
|
696
925
|
},
|
|
697
926
|
close() {
|
|
698
927
|
views.clear();
|
|
699
928
|
lives.clear();
|
|
700
929
|
listWatchers.clear();
|
|
930
|
+
agentWatchers.clear();
|
|
701
931
|
},
|
|
702
932
|
};
|
|
703
933
|
}
|