@agentshouse/mdruntime 0.1.0-ca68e8b4bbebc → 0.1.0-ca69b8fe1fa9f
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 +142 -70
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +5 -1
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +38 -4
- 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 -11
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +181 -103
- 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 -16
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +11 -89
- 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 +19 -10
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +86 -9
- package/dist/collection.d.ts +11 -21
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +39 -291
- package/dist/compile.d.ts +11 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +589 -0
- package/dist/connection.d.ts +28 -17
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +3147 -1396
- 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 +29 -14
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +423 -198
- 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 +2 -5
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +5 -0
- package/dist/entry.d.ts +5 -2
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +31 -27
- 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 +3 -10
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +65 -39
- 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 +19 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- 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 +12 -6
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +202 -103
- 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 +7 -3
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +7 -13
- 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 +17 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +100 -5
- 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 +45 -7
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +39 -0
- package/dist/overlay.d.ts +2 -5
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +87 -172
- 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 +16 -190
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -470
- package/dist/reach.d.ts +17 -8
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +219 -90
- package/dist/react.d.ts +4 -3
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +2 -14
- package/dist/readiness.d.ts +14 -17
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +2 -8
- package/dist/recovery.d.ts +3 -2
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +34 -7
- package/dist/refresh.d.ts +18 -15
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +155 -58
- package/dist/replica.d.ts +56 -33
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +301 -191
- package/dist/room-draft.d.ts +23 -4
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +158 -71
- package/dist/rows.d.ts +22 -30
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +63 -114
- package/dist/save.d.ts +53 -14
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +529 -185
- package/dist/search.d.ts +12 -82
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +10 -207
- package/dist/seed.d.ts.map +1 -1
- package/dist/seed.js +18 -4
- package/dist/shared.d.ts +7 -3
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +119 -66
- package/dist/status.d.ts +4 -3
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +3 -1
- package/dist/store.d.ts +92 -98
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +330 -675
- package/dist/stored.d.ts +162 -0
- package/dist/stored.d.ts.map +1 -0
- package/dist/stored.js +543 -0
- package/dist/tables.d.ts +353 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +588 -0
- package/dist/transfer.d.ts +0 -5
- package/dist/transfer.d.ts.map +1 -1
- 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 +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 +34 -5
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +10 -2
- package/package.json +18 -19
- package/AGENTS.md +0 -299
- package/dist/acquisition.d.ts +0 -14
- package/dist/acquisition.d.ts.map +0 -1
- package/dist/acquisition.js +0 -24
- package/dist/chat/acts.d.ts +0 -33
- 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 -61
- 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 -27
- package/dist/chat/list.d.ts.map +0 -1
- package/dist/chat/list.js +0 -63
- package/dist/chat/markdown.d.ts +0 -20
- package/dist/chat/markdown.d.ts.map +0 -1
- package/dist/chat/markdown.js +0 -188
- 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 -22
- package/dist/chat/messages.d.ts.map +0 -1
- package/dist/chat/messages.js +0 -155
- 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 -629
- 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 -34
- 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 -25
- package/dist/read-side.d.ts.map +0 -1
- package/dist/read-side.js +0 -642
- package/dist/reads.d.ts +0 -11
- package/dist/reads.d.ts.map +0 -1
- package/dist/reads.js +0 -98
package/dist/conversation.js
CHANGED
|
@@ -1,15 +1,32 @@
|
|
|
1
|
+
import { cursorOf, decodedCursor } from '@agentshouse/mdmodel';
|
|
1
2
|
import { conversationChangeSet } from './change-set.js';
|
|
2
3
|
import { runtimeError } from './failure.js';
|
|
3
|
-
import {
|
|
4
|
+
import { refusedForSpace } from './durability.js';
|
|
4
5
|
import { changeBatchIdentity, chatDraftRevision } from './identity.js';
|
|
5
6
|
import { OFFLINE } from './outcome.js';
|
|
7
|
+
import { untilAnswered } from './recovery.js';
|
|
8
|
+
import { capped, FROM_START, messageOrder, precedes } from './windows.js';
|
|
6
9
|
const QUIET = {
|
|
7
10
|
turn: null,
|
|
8
11
|
questions: [],
|
|
9
12
|
process: null,
|
|
10
13
|
commands: [],
|
|
11
14
|
options: [],
|
|
15
|
+
starting: null,
|
|
16
|
+
subagents: [],
|
|
17
|
+
link: null,
|
|
18
|
+
deliveryUnknown: [],
|
|
12
19
|
};
|
|
20
|
+
const FACTS = [
|
|
21
|
+
'questions',
|
|
22
|
+
'process',
|
|
23
|
+
'commands',
|
|
24
|
+
'options',
|
|
25
|
+
'starting',
|
|
26
|
+
'subagents',
|
|
27
|
+
'link',
|
|
28
|
+
'deliveryUnknown',
|
|
29
|
+
];
|
|
13
30
|
export function unheldView(conversation) {
|
|
14
31
|
return {
|
|
15
32
|
conversation,
|
|
@@ -17,13 +34,19 @@ export function unheldView(conversation) {
|
|
|
17
34
|
turn: null,
|
|
18
35
|
questions: [],
|
|
19
36
|
process: null,
|
|
37
|
+
processEnded: null,
|
|
20
38
|
commands: [],
|
|
21
39
|
options: [],
|
|
40
|
+
starting: null,
|
|
41
|
+
subagents: [],
|
|
42
|
+
link: null,
|
|
43
|
+
deliveryUnknown: [],
|
|
22
44
|
chatDraft: null,
|
|
23
45
|
};
|
|
24
46
|
}
|
|
25
47
|
export const SESSION_STARTED = 'Session started';
|
|
26
48
|
const NEW_CHAT = 'new:';
|
|
49
|
+
export const HISTORY_CONVERSATIONS = 64;
|
|
27
50
|
export function newChat(agent) {
|
|
28
51
|
return `${NEW_CHAT}${agent}`;
|
|
29
52
|
}
|
|
@@ -82,19 +105,25 @@ function appliedTurn(held, operation) {
|
|
|
82
105
|
parts: held.parts.map((kept, at) => (at === operation.index ? changed : kept)),
|
|
83
106
|
};
|
|
84
107
|
}
|
|
85
|
-
function ordered(left, right) {
|
|
86
|
-
if (left.position !== right.position)
|
|
87
|
-
return left.position < right.position ? -1 : 1;
|
|
88
|
-
if (left.identity !== right.identity)
|
|
89
|
-
return left.identity < right.identity ? -1 : 1;
|
|
90
|
-
return 0;
|
|
91
|
-
}
|
|
92
108
|
function windowOf(held, arriving, limit) {
|
|
93
109
|
const byIdentity = new Map(held.map((message) => [message.identity, message]));
|
|
94
110
|
for (const message of arriving)
|
|
95
111
|
byIdentity.set(message.identity, message);
|
|
96
|
-
const next = [...byIdentity.values()].sort(
|
|
97
|
-
|
|
112
|
+
const next = [...byIdentity.values()].sort(messageOrder);
|
|
113
|
+
const start = Math.max(0, next.length - limit);
|
|
114
|
+
return { messages: next.slice(start), dropped: start > 0 };
|
|
115
|
+
}
|
|
116
|
+
function frameOf(asked, messages, more, total) {
|
|
117
|
+
const oldest = messages[0];
|
|
118
|
+
const next = !more
|
|
119
|
+
? undefined
|
|
120
|
+
: oldest === undefined
|
|
121
|
+
? (asked.after ?? FROM_START)
|
|
122
|
+
: cursorOf([oldest.position, oldest.identity]);
|
|
123
|
+
return {
|
|
124
|
+
...(next === undefined ? {} : { next }),
|
|
125
|
+
...(total === null ? {} : { total }),
|
|
126
|
+
};
|
|
98
127
|
}
|
|
99
128
|
function newestOf(arriving, from) {
|
|
100
129
|
return arriving.reduce((top, message) => (top === null || message.position > top ? message.position : top), from);
|
|
@@ -165,18 +194,42 @@ function answered(result, field) {
|
|
|
165
194
|
const value = answer[field];
|
|
166
195
|
return typeof value === 'string' ? value : null;
|
|
167
196
|
}
|
|
168
|
-
function
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (
|
|
197
|
+
function beforeOf(after) {
|
|
198
|
+
if (after === undefined)
|
|
199
|
+
return null;
|
|
200
|
+
const decoded = typeof after === 'string' ? decodedCursor(after) : null;
|
|
201
|
+
if (decoded?.length === 0)
|
|
202
|
+
return null;
|
|
203
|
+
const [position, identity] = decoded ?? [];
|
|
204
|
+
if (decoded?.length !== 2 || typeof position !== 'string' || typeof identity !== 'string') {
|
|
205
|
+
throw runtimeError('invalid-after', `conversation after must be the next an earlier view returned, got ${String(after)}`);
|
|
206
|
+
}
|
|
207
|
+
return [position, identity];
|
|
208
|
+
}
|
|
209
|
+
function askedOf(options) {
|
|
210
|
+
const { limit, after, total } = options;
|
|
211
|
+
if (limit !== undefined && (!Number.isInteger(limit) || limit < 0)) {
|
|
173
212
|
throw runtimeError('invalid-limit', `conversation limit must be a non-negative integer, got ${String(limit)}`);
|
|
174
213
|
}
|
|
175
|
-
return limit;
|
|
214
|
+
return { limit: capped(limit), after, before: beforeOf(after), total: total === true };
|
|
215
|
+
}
|
|
216
|
+
function phraseOf(text) {
|
|
217
|
+
if (text === undefined)
|
|
218
|
+
return null;
|
|
219
|
+
if (typeof text !== 'string' || text === '') {
|
|
220
|
+
throw runtimeError('invalid-text', 'a conversation is found by a phrase');
|
|
221
|
+
}
|
|
222
|
+
return text;
|
|
176
223
|
}
|
|
177
224
|
const EXPIRED = { kind: 'rejected', code: 'expired', fields: {}, retryable: false };
|
|
225
|
+
const DURABLE_WRITE = {
|
|
226
|
+
kind: 'rejected',
|
|
227
|
+
code: 'durable-write',
|
|
228
|
+
fields: {},
|
|
229
|
+
retryable: true,
|
|
230
|
+
};
|
|
178
231
|
function unwritten(draft) {
|
|
179
|
-
return draft.text === ''
|
|
232
|
+
return draft.text === '';
|
|
180
233
|
}
|
|
181
234
|
function pending(draft) {
|
|
182
235
|
return {
|
|
@@ -191,11 +244,13 @@ function withSubmitted(draft, submitted) {
|
|
|
191
244
|
const { submitted: _held, ...unsent } = draft;
|
|
192
245
|
return submitted.length === 0 ? unsent : { ...unsent, submitted };
|
|
193
246
|
}
|
|
247
|
+
function prepended(draft, said) {
|
|
248
|
+
return { text: [said.text, draft.text].filter((one) => one !== '').join('\n') };
|
|
249
|
+
}
|
|
194
250
|
function returned(draft, submission, others) {
|
|
195
251
|
return withSubmitted({
|
|
196
252
|
conversation: draft.conversation,
|
|
197
|
-
|
|
198
|
-
files: [...submission.files, ...draft.files],
|
|
253
|
+
...prepended(draft, submission),
|
|
199
254
|
revision: chatDraftRevision(),
|
|
200
255
|
}, others);
|
|
201
256
|
}
|
|
@@ -209,21 +264,28 @@ export function createConversationStore(deps) {
|
|
|
209
264
|
const catching = new Map();
|
|
210
265
|
const gaps = new Map();
|
|
211
266
|
const lives = new Map();
|
|
267
|
+
const unsent = new Map();
|
|
212
268
|
const views = new Map();
|
|
213
269
|
const listing = new Map();
|
|
270
|
+
let relisting = null;
|
|
271
|
+
let listCursor = null;
|
|
214
272
|
const creations = new Set();
|
|
215
273
|
const listWatchers = new Set();
|
|
274
|
+
const unlisted = new Set();
|
|
216
275
|
let listKnown = false;
|
|
276
|
+
let listHeld = false;
|
|
277
|
+
let listKept = false;
|
|
217
278
|
const agentWatchers = new Set();
|
|
218
|
-
|
|
219
|
-
let
|
|
220
|
-
let
|
|
279
|
+
const unnamed = new Set();
|
|
280
|
+
let agentsShown = null;
|
|
281
|
+
let listedAgents = null;
|
|
282
|
+
let agentsReading = false;
|
|
221
283
|
let replicated = new Set();
|
|
222
284
|
let handedReplicated;
|
|
223
285
|
function liveOf(conversation) {
|
|
224
286
|
return lives.get(conversation) ?? QUIET;
|
|
225
287
|
}
|
|
226
|
-
function viewOf(conversation, held) {
|
|
288
|
+
function viewOf(conversation, held, frame) {
|
|
227
289
|
const draft = drafts.get(conversation);
|
|
228
290
|
const facts = liveOf(conversation);
|
|
229
291
|
return {
|
|
@@ -234,14 +296,20 @@ export function createConversationStore(deps) {
|
|
|
234
296
|
text,
|
|
235
297
|
message,
|
|
236
298
|
})),
|
|
299
|
+
...frame,
|
|
237
300
|
turn: structuredClone(facts.turn),
|
|
238
301
|
questions: structuredClone(facts.questions),
|
|
239
302
|
process: facts.process === null ? null : { ...facts.process },
|
|
303
|
+
processEnded: listing.get(conversation)?.processEnded ?? null,
|
|
240
304
|
commands: structuredClone(facts.commands),
|
|
241
305
|
options: structuredClone(facts.options),
|
|
306
|
+
starting: facts.starting,
|
|
307
|
+
subagents: structuredClone(facts.subagents),
|
|
308
|
+
link: facts.link === null ? null : { ...facts.link },
|
|
309
|
+
deliveryUnknown: [...facts.deliveryUnknown],
|
|
242
310
|
chatDraft: draft === undefined || unwritten(draft)
|
|
243
311
|
? null
|
|
244
|
-
: { text: draft.text,
|
|
312
|
+
: { text: draft.text, revision: draft.revision },
|
|
245
313
|
};
|
|
246
314
|
}
|
|
247
315
|
function emit(conversation) {
|
|
@@ -253,21 +321,36 @@ export function createConversationStore(deps) {
|
|
|
253
321
|
const ready = [...view.watchers].filter((watcher) => watcher.ready);
|
|
254
322
|
if (ready.length === 0)
|
|
255
323
|
continue;
|
|
256
|
-
const value = viewOf(conversation, view.window);
|
|
324
|
+
const value = viewOf(conversation, view.window, sharedFrame(view, view.window));
|
|
257
325
|
for (const watcher of ready)
|
|
258
326
|
watcher.notify(value);
|
|
259
327
|
}
|
|
260
328
|
}
|
|
261
|
-
function
|
|
329
|
+
function sharedFrame(view, window) {
|
|
330
|
+
return frameOf(view.asked, window, view.more, view.total);
|
|
331
|
+
}
|
|
332
|
+
async function slide(local, conversation, arriving) {
|
|
333
|
+
let count;
|
|
262
334
|
for (const view of views.values()) {
|
|
263
335
|
if (view.conversation !== conversation || view.window === null)
|
|
264
336
|
continue;
|
|
265
|
-
|
|
337
|
+
if (arriving === null) {
|
|
338
|
+
view.window = [];
|
|
339
|
+
view.more = false;
|
|
340
|
+
if (view.total !== null)
|
|
341
|
+
view.total = 0;
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
const joining = arriving.filter((message) => precedes(message, view.asked.before));
|
|
345
|
+
const next = windowOf(view.window, joining, view.asked.limit);
|
|
346
|
+
view.window = next.messages;
|
|
347
|
+
view.more ||= next.dropped;
|
|
348
|
+
if (view.total !== null && arriving.length > 0) {
|
|
349
|
+
count ??= await local.messageCount(conversation);
|
|
350
|
+
view.total = count;
|
|
351
|
+
}
|
|
266
352
|
}
|
|
267
353
|
}
|
|
268
|
-
function changesOf(conversation, arriving) {
|
|
269
|
-
return arriving.map(({ identity, text }) => ({ conversation, identity, text }));
|
|
270
|
-
}
|
|
271
354
|
function stateOf(conversation) {
|
|
272
355
|
const facts = liveOf(conversation);
|
|
273
356
|
if (facts.questions.length > 0)
|
|
@@ -277,34 +360,113 @@ export function createConversationStore(deps) {
|
|
|
277
360
|
function entries() {
|
|
278
361
|
return [...listing.values()].map((entry) => ({
|
|
279
362
|
conversation: entry.conversation,
|
|
280
|
-
agent: { identity: entry.agent, name:
|
|
363
|
+
agent: { identity: entry.agent, name: entry.agentName },
|
|
281
364
|
name: entry.name,
|
|
282
365
|
created: entry.created,
|
|
283
366
|
finished: entry.finished,
|
|
284
367
|
outcome: entry.outcome,
|
|
368
|
+
waiting: entry.waiting,
|
|
285
369
|
state: stateOf(entry.conversation),
|
|
286
370
|
started: lives.get(entry.conversation)?.turn?.started ?? null,
|
|
287
371
|
}));
|
|
288
372
|
}
|
|
373
|
+
function keepList() {
|
|
374
|
+
if (!listKnown || listKept)
|
|
375
|
+
return;
|
|
376
|
+
listKept = true;
|
|
377
|
+
queueMicrotask(() => {
|
|
378
|
+
listKept = false;
|
|
379
|
+
if (deps.closed())
|
|
380
|
+
return;
|
|
381
|
+
const entries = [...listing.values()];
|
|
382
|
+
const cursor = listCursor;
|
|
383
|
+
void deps
|
|
384
|
+
.durably(async (local) => {
|
|
385
|
+
await local.fact('conversationList', entries);
|
|
386
|
+
await local.fact('conversationCursor', cursor);
|
|
387
|
+
})
|
|
388
|
+
.catch(() => { });
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
function listed(entry) {
|
|
392
|
+
const ended = listing.get(entry.conversation)?.processEnded ?? null;
|
|
393
|
+
listing.set(entry.conversation, { ...entry });
|
|
394
|
+
creations.delete(entry.conversation);
|
|
395
|
+
if (entry.processEnded !== ended)
|
|
396
|
+
emit(entry.conversation);
|
|
397
|
+
}
|
|
289
398
|
function emitList() {
|
|
290
|
-
if (deps.closed()
|
|
399
|
+
if (deps.closed())
|
|
291
400
|
return;
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
watcher
|
|
401
|
+
if (listWatchers.size > 0) {
|
|
402
|
+
const value = entries();
|
|
403
|
+
for (const watcher of listWatchers)
|
|
404
|
+
watcher(value);
|
|
405
|
+
}
|
|
406
|
+
emitAgents();
|
|
407
|
+
learned();
|
|
408
|
+
}
|
|
409
|
+
function learned() {
|
|
410
|
+
if (deps.closed())
|
|
411
|
+
return;
|
|
412
|
+
if (!listKnown && !listHeld && deps.stream() !== undefined && deps.online())
|
|
413
|
+
return;
|
|
414
|
+
if (unlisted.size > 0) {
|
|
415
|
+
const value = entries();
|
|
416
|
+
for (const watcher of [...unlisted]) {
|
|
417
|
+
unlisted.delete(watcher);
|
|
418
|
+
listWatchers.add(watcher);
|
|
419
|
+
watcher(value);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (unnamed.size > 0) {
|
|
423
|
+
const value = agents();
|
|
424
|
+
for (const watcher of [...unnamed]) {
|
|
425
|
+
unnamed.delete(watcher);
|
|
426
|
+
agentWatchers.add(watcher);
|
|
427
|
+
watcher(value);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
295
430
|
}
|
|
296
431
|
function agents() {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
.
|
|
432
|
+
const named = new Map();
|
|
433
|
+
for (const agent of listedAgents ?? [])
|
|
434
|
+
named.set(agent.agent, agent);
|
|
435
|
+
for (const entry of listing.values()) {
|
|
436
|
+
named.set(entry.agent, { name: entry.agentName, retired: entry.agentRetired });
|
|
437
|
+
}
|
|
438
|
+
return [...named]
|
|
439
|
+
.filter(([, agent]) => !agent.retired)
|
|
440
|
+
.map(([identity, agent]) => ({ identity, name: agent.name }))
|
|
300
441
|
.sort((left, right) => left.name === right.name
|
|
301
442
|
? left.identity.localeCompare(right.identity)
|
|
302
443
|
: left.name.localeCompare(right.name));
|
|
303
444
|
}
|
|
445
|
+
function readAgents() {
|
|
446
|
+
const stream = deps.stream();
|
|
447
|
+
if (stream?.agents === undefined || !deps.online() || agentsReading)
|
|
448
|
+
return;
|
|
449
|
+
agentsReading = true;
|
|
450
|
+
void stream
|
|
451
|
+
.agents()
|
|
452
|
+
.then((read) => {
|
|
453
|
+
if (read === null)
|
|
454
|
+
return;
|
|
455
|
+
listedAgents = read;
|
|
456
|
+
emitAgents();
|
|
457
|
+
}, () => { })
|
|
458
|
+
.finally(() => {
|
|
459
|
+
agentsReading = false;
|
|
460
|
+
});
|
|
461
|
+
}
|
|
304
462
|
function emitAgents() {
|
|
305
463
|
if (deps.closed() || agentWatchers.size === 0)
|
|
306
464
|
return;
|
|
307
465
|
const value = agents();
|
|
466
|
+
const shown = JSON.stringify(value);
|
|
467
|
+
if (shown === agentsShown)
|
|
468
|
+
return;
|
|
469
|
+
agentsShown = shown;
|
|
308
470
|
for (const watcher of agentWatchers)
|
|
309
471
|
watcher(value);
|
|
310
472
|
}
|
|
@@ -320,53 +482,42 @@ export function createConversationStore(deps) {
|
|
|
320
482
|
if (stateOf(conversation) !== before || next?.turn?.started !== began)
|
|
321
483
|
emitList();
|
|
322
484
|
}
|
|
323
|
-
async function readAgents() {
|
|
324
|
-
const declaration = deps.agents();
|
|
325
|
-
if (declaration === undefined)
|
|
326
|
-
return;
|
|
327
|
-
const held = await deps.documents(declaration);
|
|
328
|
-
if ('pending' in held)
|
|
329
|
-
return;
|
|
330
|
-
const { documents } = held;
|
|
331
|
-
const names = new Map();
|
|
332
|
-
const retired = new Set();
|
|
333
|
-
for (const document of documents) {
|
|
334
|
-
if (document.type !== declaration.type)
|
|
335
|
-
continue;
|
|
336
|
-
const identity = document.fields?.[declaration.identity];
|
|
337
|
-
const name = document.fields?.[declaration.name];
|
|
338
|
-
if (typeof identity !== 'string' || typeof name !== 'string')
|
|
339
|
-
continue;
|
|
340
|
-
names.set(identity, name);
|
|
341
|
-
const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
|
|
342
|
-
if (ended !== undefined && ended !== null)
|
|
343
|
-
retired.add(identity);
|
|
344
|
-
}
|
|
345
|
-
const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
|
|
346
|
-
if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
|
|
347
|
-
return;
|
|
348
|
-
agentNames = names;
|
|
349
|
-
retiredAgents = retired;
|
|
350
|
-
if (named)
|
|
351
|
-
emitList();
|
|
352
|
-
emitAgents();
|
|
353
|
-
const ended = [...drafts.keys()].filter(retiredChat);
|
|
354
|
-
if (ended.length > 0)
|
|
355
|
-
await remove(ended);
|
|
356
|
-
}
|
|
357
485
|
function retiredChat(conversation) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
agentsRead = null;
|
|
363
|
-
});
|
|
364
|
-
return agentsRead;
|
|
486
|
+
if (!conversation.startsWith(NEW_CHAT))
|
|
487
|
+
return false;
|
|
488
|
+
const agent = conversation.slice(NEW_CHAT.length);
|
|
489
|
+
return [...listing.values()].some((entry) => entry.agent === agent && entry.agentRetired);
|
|
365
490
|
}
|
|
366
491
|
function live(conversation, change) {
|
|
492
|
+
const owed = unsent.get(conversation);
|
|
493
|
+
for (const fact of FACTS.filter((one) => one in change))
|
|
494
|
+
owed?.delete(fact);
|
|
367
495
|
setLive(conversation, { ...liveOf(conversation), ...change });
|
|
368
496
|
emit(conversation);
|
|
369
497
|
}
|
|
498
|
+
function openingEnded() {
|
|
499
|
+
const owed = [...unsent];
|
|
500
|
+
unsent.clear();
|
|
501
|
+
for (const [conversation, facts] of owed) {
|
|
502
|
+
const held = lives.get(conversation);
|
|
503
|
+
if (held === undefined || facts.size === 0)
|
|
504
|
+
continue;
|
|
505
|
+
setLive(conversation, {
|
|
506
|
+
turn: held.turn,
|
|
507
|
+
questions: facts.has('questions') ? QUIET.questions : held.questions,
|
|
508
|
+
process: facts.has('process') ? QUIET.process : held.process,
|
|
509
|
+
commands: facts.has('commands') ? QUIET.commands : held.commands,
|
|
510
|
+
options: facts.has('options') ? QUIET.options : held.options,
|
|
511
|
+
starting: facts.has('starting') ? QUIET.starting : held.starting,
|
|
512
|
+
subagents: facts.has('subagents') ? QUIET.subagents : held.subagents,
|
|
513
|
+
link: facts.has('link') ? QUIET.link : held.link,
|
|
514
|
+
deliveryUnknown: facts.has('deliveryUnknown')
|
|
515
|
+
? QUIET.deliveryUnknown
|
|
516
|
+
: held.deliveryUnknown,
|
|
517
|
+
});
|
|
518
|
+
emit(conversation);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
370
521
|
function relayed(conversation, turn, operation) {
|
|
371
522
|
const held = liveOf(conversation).turn;
|
|
372
523
|
const next = held?.turn === turn ? appliedTurn(held, operation) : startedTurn(turn, operation);
|
|
@@ -393,12 +544,6 @@ export function createConversationStore(deps) {
|
|
|
393
544
|
}
|
|
394
545
|
async function remove(leaving) {
|
|
395
546
|
await deps.durably(async (local) => {
|
|
396
|
-
const removed = [];
|
|
397
|
-
for (const conversation of leaving) {
|
|
398
|
-
for (const identity of await local.messageIdentities(conversation)) {
|
|
399
|
-
removed.push({ conversation, identity, text: null });
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
547
|
await local.dropConversations(leaving);
|
|
403
548
|
const changed = [];
|
|
404
549
|
for (const conversation of leaving) {
|
|
@@ -410,13 +555,28 @@ export function createConversationStore(deps) {
|
|
|
410
555
|
partials.delete(conversation);
|
|
411
556
|
drafts.delete(conversation);
|
|
412
557
|
behind.delete(conversation);
|
|
558
|
+
unsent.delete(conversation);
|
|
413
559
|
setLive(conversation, null);
|
|
414
|
-
slide(conversation, null);
|
|
560
|
+
await slide(local, conversation, null);
|
|
415
561
|
}
|
|
416
562
|
if (changed.length > 0)
|
|
417
|
-
deps.publish(conversationChangeSet(
|
|
563
|
+
deps.publish(conversationChangeSet(changed));
|
|
418
564
|
});
|
|
419
565
|
}
|
|
566
|
+
function missed(conversation) {
|
|
567
|
+
const read = reads.get(conversation);
|
|
568
|
+
const newest = listing.get(conversation)?.newest;
|
|
569
|
+
return (read === undefined ||
|
|
570
|
+
newest === undefined ||
|
|
571
|
+
partials.has(conversation) ||
|
|
572
|
+
(newest !== null && (read === null || newest > read)));
|
|
573
|
+
}
|
|
574
|
+
function noted(conversation, message) {
|
|
575
|
+
const entry = listing.get(conversation);
|
|
576
|
+
if (entry === undefined || (entry.newest !== null && entry.newest >= message.position))
|
|
577
|
+
return;
|
|
578
|
+
listing.set(conversation, { ...entry, newest: message.position });
|
|
579
|
+
}
|
|
420
580
|
function gap(conversation) {
|
|
421
581
|
gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
|
|
422
582
|
behind.add(conversation);
|
|
@@ -439,18 +599,19 @@ export function createConversationStore(deps) {
|
|
|
439
599
|
if (advances)
|
|
440
600
|
reads.set(conversation, message.position);
|
|
441
601
|
sealTurns(conversation, new Set([message.turn]));
|
|
442
|
-
slide(conversation, [message]);
|
|
443
|
-
deps.publish(conversationChangeSet(
|
|
602
|
+
await slide(local, conversation, [message]);
|
|
603
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
444
604
|
});
|
|
445
605
|
}
|
|
446
606
|
async function readHistories(conversations, ended) {
|
|
447
607
|
const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
|
|
448
|
-
let
|
|
608
|
+
let waiting = conversations.map((conversation) => ({
|
|
449
609
|
conversation,
|
|
450
610
|
after: reads.get(conversation) ?? undefined,
|
|
451
611
|
}));
|
|
452
|
-
while (
|
|
453
|
-
const
|
|
612
|
+
while (waiting.length > 0) {
|
|
613
|
+
const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
|
|
614
|
+
const result = await deps.history({
|
|
454
615
|
conversations: asking.map(({ conversation, after }) => ({
|
|
455
616
|
conversation,
|
|
456
617
|
...(after === undefined ? {} : { after }),
|
|
@@ -473,7 +634,7 @@ export function createConversationStore(deps) {
|
|
|
473
634
|
if (!following.some((next) => next.conversation === conversation))
|
|
474
635
|
ended(conversation);
|
|
475
636
|
}
|
|
476
|
-
|
|
637
|
+
waiting = [...waiting.slice(HISTORY_CONVERSATIONS), ...following];
|
|
477
638
|
}
|
|
478
639
|
return null;
|
|
479
640
|
}
|
|
@@ -514,9 +675,9 @@ export function createConversationStore(deps) {
|
|
|
514
675
|
sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
|
|
515
676
|
if (whole && (gaps.get(conversation) ?? 0) === generation)
|
|
516
677
|
behind.delete(conversation);
|
|
517
|
-
slide(conversation, arriving);
|
|
678
|
+
await slide(local, conversation, arriving);
|
|
518
679
|
if (arriving.length > 0 || sealing || whole) {
|
|
519
|
-
deps.publish(conversationChangeSet(
|
|
680
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
520
681
|
}
|
|
521
682
|
});
|
|
522
683
|
}
|
|
@@ -525,11 +686,8 @@ export function createConversationStore(deps) {
|
|
|
525
686
|
if (event.kind === 'gap') {
|
|
526
687
|
if (replicated.has(conversation))
|
|
527
688
|
gap(conversation);
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
return;
|
|
531
|
-
setLive(conversation, { ...QUIET, turn: held.turn });
|
|
532
|
-
emit(conversation);
|
|
689
|
+
if (lives.has(conversation))
|
|
690
|
+
unsent.set(conversation, new Set(FACTS));
|
|
533
691
|
return;
|
|
534
692
|
}
|
|
535
693
|
if (event.kind === 'turn') {
|
|
@@ -552,11 +710,35 @@ export function createConversationStore(deps) {
|
|
|
552
710
|
live(conversation, { options: structuredClone(event.options) });
|
|
553
711
|
return;
|
|
554
712
|
}
|
|
713
|
+
if (event.kind === 'starting') {
|
|
714
|
+
live(conversation, { starting: event.step });
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
if (event.kind === 'subagents') {
|
|
718
|
+
live(conversation, { subagents: structuredClone(event.subagents) });
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
if (event.kind === 'link') {
|
|
722
|
+
live(conversation, { link: { connected: event.connected } });
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
if (event.kind === 'deliveries') {
|
|
726
|
+
live(conversation, { deliveryUnknown: [...event.unknown] });
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
555
729
|
const arriving = acceptedMessage(event.message);
|
|
556
730
|
if (arriving === null)
|
|
557
731
|
return;
|
|
732
|
+
noted(conversation, arriving);
|
|
558
733
|
if (replicated.has(conversation)) {
|
|
559
|
-
|
|
734
|
+
if (deps.full()) {
|
|
735
|
+
gap(conversation);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
void keep(conversation, arriving).catch((error) => {
|
|
739
|
+
if (refusedForSpace(error))
|
|
740
|
+
gap(conversation);
|
|
741
|
+
});
|
|
560
742
|
return;
|
|
561
743
|
}
|
|
562
744
|
if (sealTurns(conversation, new Set([arriving.turn])))
|
|
@@ -591,7 +773,6 @@ export function createConversationStore(deps) {
|
|
|
591
773
|
const blank = {
|
|
592
774
|
conversation: from,
|
|
593
775
|
text: '',
|
|
594
|
-
files: [],
|
|
595
776
|
revision: chatDraftRevision(),
|
|
596
777
|
};
|
|
597
778
|
const { moves: _answered, ...unsent } = held ?? blank;
|
|
@@ -601,7 +782,7 @@ export function createConversationStore(deps) {
|
|
|
601
782
|
drafts.set(from, left);
|
|
602
783
|
for (const one of taken)
|
|
603
784
|
drafts.set(to, one);
|
|
604
|
-
deps.publish(conversationChangeSet([
|
|
785
|
+
deps.publish(conversationChangeSet([from, to]));
|
|
605
786
|
});
|
|
606
787
|
}
|
|
607
788
|
function withDraft(conversation, next) {
|
|
@@ -610,7 +791,7 @@ export function createConversationStore(deps) {
|
|
|
610
791
|
if (following === undefined)
|
|
611
792
|
return;
|
|
612
793
|
await kept(local, conversation, following);
|
|
613
|
-
deps.publish(conversationChangeSet([
|
|
794
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
614
795
|
});
|
|
615
796
|
}
|
|
616
797
|
async function submitted(conversation, operation, draft) {
|
|
@@ -622,17 +803,12 @@ export function createConversationStore(deps) {
|
|
|
622
803
|
if (submission !== undefined)
|
|
623
804
|
return undefined;
|
|
624
805
|
const said = draft ?? current;
|
|
625
|
-
submission = {
|
|
626
|
-
operation,
|
|
627
|
-
text: said?.text ?? '',
|
|
628
|
-
files: [...(said?.files ?? [])],
|
|
629
|
-
turn: null,
|
|
630
|
-
};
|
|
806
|
+
submission = { operation, text: said?.text ?? '', turn: null };
|
|
631
807
|
return {
|
|
632
808
|
conversation,
|
|
633
809
|
...(draft === undefined || current === undefined
|
|
634
|
-
? { text: '',
|
|
635
|
-
: { text: current.text,
|
|
810
|
+
? { text: '', revision: chatDraftRevision() }
|
|
811
|
+
: { text: current.text, revision: current.revision }),
|
|
636
812
|
submitted: [...(current?.submitted ?? []), submission],
|
|
637
813
|
};
|
|
638
814
|
});
|
|
@@ -647,22 +823,21 @@ export function createConversationStore(deps) {
|
|
|
647
823
|
});
|
|
648
824
|
}
|
|
649
825
|
async function dispatched(conversation, submission) {
|
|
650
|
-
const dispatch = {};
|
|
826
|
+
const dispatch = { answered: false };
|
|
651
827
|
sending.set(submission.operation, dispatch);
|
|
652
828
|
try {
|
|
653
|
-
const
|
|
654
|
-
const result = await invoke('conversation.send', conversation, {
|
|
655
|
-
text: submission.text,
|
|
656
|
-
files,
|
|
829
|
+
const result = await invoke('conversations/send', conversation, {
|
|
657
830
|
operation_id: submission.operation,
|
|
831
|
+
text: submission.text,
|
|
658
832
|
});
|
|
833
|
+
dispatch.answered = result.kind !== 'transport-uncertainty';
|
|
659
834
|
if (result.kind === 'revoked-authorization')
|
|
660
835
|
return result;
|
|
661
836
|
if (result.kind !== 'action' && result.retryable) {
|
|
662
837
|
deps.unanswered();
|
|
663
838
|
return result;
|
|
664
839
|
}
|
|
665
|
-
const turn = answered(result, '
|
|
840
|
+
const turn = answered(result, 'message_id');
|
|
666
841
|
await withDraft(conversation, async (current, local) => {
|
|
667
842
|
const held = current?.submitted?.find((one) => one.operation === submission.operation);
|
|
668
843
|
if (current === undefined || held === undefined || held.turn !== null)
|
|
@@ -682,21 +857,34 @@ export function createConversationStore(deps) {
|
|
|
682
857
|
sending.delete(submission.operation);
|
|
683
858
|
}
|
|
684
859
|
}
|
|
685
|
-
function invoke(name, conversation, extra = {}) {
|
|
686
|
-
return deps.call(name, { conversation, ...extra });
|
|
860
|
+
function invoke(name, conversation, extra = { operation_id: changeBatchIdentity() }) {
|
|
861
|
+
return deps.call(name, { conversation_id: conversation, ...extra });
|
|
687
862
|
}
|
|
688
863
|
const surface = {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
864
|
+
list() {
|
|
865
|
+
return new Promise((settle) => {
|
|
866
|
+
const answer = (listed) => {
|
|
867
|
+
unlisted.delete(answer);
|
|
868
|
+
listWatchers.delete(answer);
|
|
869
|
+
settle(listed);
|
|
870
|
+
};
|
|
871
|
+
unlisted.add(answer);
|
|
872
|
+
learned();
|
|
873
|
+
});
|
|
692
874
|
},
|
|
693
875
|
async read(conversation, options = {}) {
|
|
694
|
-
const
|
|
695
|
-
|
|
876
|
+
const { text, ...windowed } = options;
|
|
877
|
+
const asked = askedOf(windowed);
|
|
878
|
+
const phrase = phraseOf(text);
|
|
879
|
+
return deps.durably(async (local) => {
|
|
880
|
+
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before, phrase);
|
|
881
|
+
const total = asked.total ? await local.messageCount(conversation, phrase) : null;
|
|
882
|
+
return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total)));
|
|
883
|
+
});
|
|
696
884
|
},
|
|
697
885
|
async create(input) {
|
|
698
|
-
const result = await deps.call('
|
|
699
|
-
const conversation = answered(result, '
|
|
886
|
+
const result = await deps.call('conversations/create', input);
|
|
887
|
+
const conversation = answered(result, 'conversation_id');
|
|
700
888
|
if (conversation === null)
|
|
701
889
|
return result;
|
|
702
890
|
if (!listing.has(conversation))
|
|
@@ -707,8 +895,17 @@ export function createConversationStore(deps) {
|
|
|
707
895
|
return result;
|
|
708
896
|
},
|
|
709
897
|
async send(conversation, operation = changeBatchIdentity(), draft) {
|
|
898
|
+
if (deps.full())
|
|
899
|
+
return DURABLE_WRITE;
|
|
710
900
|
if (deps.online()) {
|
|
711
|
-
const
|
|
901
|
+
const held = await submitted(conversation, operation, draft).catch((error) => {
|
|
902
|
+
if (refusedForSpace(error))
|
|
903
|
+
return null;
|
|
904
|
+
throw error;
|
|
905
|
+
});
|
|
906
|
+
if (held === null)
|
|
907
|
+
return DURABLE_WRITE;
|
|
908
|
+
const { submission, earlier } = held;
|
|
712
909
|
if (!earlier || !deps.expired(operation))
|
|
713
910
|
return dispatched(conversation, submission);
|
|
714
911
|
await forgotten(conversation, submission);
|
|
@@ -717,33 +914,24 @@ export function createConversationStore(deps) {
|
|
|
717
914
|
if (draft !== undefined) {
|
|
718
915
|
await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
|
|
719
916
|
? undefined
|
|
720
|
-
: returned(current ?? { conversation, text: '',
|
|
917
|
+
: returned(current ?? { conversation, text: '', revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
|
|
721
918
|
}
|
|
722
919
|
return OFFLINE;
|
|
723
920
|
},
|
|
724
921
|
answer(conversation, answer) {
|
|
725
|
-
return invoke('
|
|
922
|
+
return invoke('conversations/interaction/answer', conversation, {
|
|
923
|
+
operation_id: changeBatchIdentity(),
|
|
924
|
+
...answer,
|
|
925
|
+
});
|
|
726
926
|
},
|
|
727
927
|
interrupt(conversation) {
|
|
728
|
-
return invoke('
|
|
928
|
+
return invoke('conversations/interrupt', conversation);
|
|
729
929
|
},
|
|
730
930
|
kill(conversation) {
|
|
731
|
-
return invoke('
|
|
732
|
-
},
|
|
733
|
-
async cancel(conversation, turn) {
|
|
734
|
-
const result = await invoke('conversation.cancel', conversation, { turn });
|
|
735
|
-
if (result.kind !== 'action')
|
|
736
|
-
return result;
|
|
737
|
-
await withDraft(conversation, (current) => {
|
|
738
|
-
const held = current?.submitted?.find((one) => one.turn === turn);
|
|
739
|
-
if (current === undefined || held === undefined)
|
|
740
|
-
return undefined;
|
|
741
|
-
return returned(current, held, current.submitted.filter((one) => one !== held));
|
|
742
|
-
});
|
|
743
|
-
return result;
|
|
931
|
+
return invoke('conversations/kill', conversation);
|
|
744
932
|
},
|
|
745
933
|
delete(conversation) {
|
|
746
|
-
return invoke('
|
|
934
|
+
return invoke('conversations/delete', conversation);
|
|
747
935
|
},
|
|
748
936
|
};
|
|
749
937
|
const chatDraft = {
|
|
@@ -751,27 +939,36 @@ export function createConversationStore(deps) {
|
|
|
751
939
|
return withDraft(conversation, (current) => ({
|
|
752
940
|
conversation,
|
|
753
941
|
text: draft.text,
|
|
754
|
-
files: [...draft.files],
|
|
755
942
|
revision,
|
|
756
943
|
...(current === undefined ? {} : pending(current)),
|
|
757
944
|
}));
|
|
758
945
|
},
|
|
946
|
+
prepend(conversation, draft) {
|
|
947
|
+
return withDraft(conversation, (current) => ({
|
|
948
|
+
conversation,
|
|
949
|
+
...(current === undefined ? { text: draft.text } : prepended(current, draft)),
|
|
950
|
+
revision: chatDraftRevision(),
|
|
951
|
+
...(current === undefined ? {} : pending(current)),
|
|
952
|
+
}));
|
|
953
|
+
},
|
|
759
954
|
discard(conversation) {
|
|
760
955
|
return withDraft(conversation, (current) => current === undefined
|
|
761
956
|
? undefined
|
|
762
|
-
: {
|
|
763
|
-
conversation,
|
|
764
|
-
text: '',
|
|
765
|
-
files: [],
|
|
766
|
-
revision: chatDraftRevision(),
|
|
767
|
-
...pending(current),
|
|
768
|
-
});
|
|
957
|
+
: { conversation, text: '', revision: chatDraftRevision(), ...pending(current) });
|
|
769
958
|
},
|
|
770
959
|
};
|
|
771
960
|
return {
|
|
772
961
|
surface,
|
|
773
962
|
chatDraft,
|
|
774
|
-
hydrate(held, kept) {
|
|
963
|
+
hydrate(held, kept, list, cursor) {
|
|
964
|
+
if (list !== null && !listKnown) {
|
|
965
|
+
listing.clear();
|
|
966
|
+
for (const entry of list)
|
|
967
|
+
listing.set(entry.conversation, { ...entry });
|
|
968
|
+
listHeld = true;
|
|
969
|
+
listCursor = cursor;
|
|
970
|
+
deps.stream()?.hold(cursor);
|
|
971
|
+
}
|
|
775
972
|
stored.clear();
|
|
776
973
|
reads.clear();
|
|
777
974
|
partials.clear();
|
|
@@ -785,6 +982,8 @@ export function createConversationStore(deps) {
|
|
|
785
982
|
drafts.clear();
|
|
786
983
|
for (const record of kept)
|
|
787
984
|
drafts.set(record.conversation, record);
|
|
985
|
+
if (listHeld)
|
|
986
|
+
learned();
|
|
788
987
|
},
|
|
789
988
|
async reconcile() {
|
|
790
989
|
if (!listKnown)
|
|
@@ -796,7 +995,7 @@ export function createConversationStore(deps) {
|
|
|
796
995
|
!creations.has(conversation) &&
|
|
797
996
|
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
|
|
798
997
|
for (const conversation of named) {
|
|
799
|
-
if (!replicated.has(conversation))
|
|
998
|
+
if (!replicated.has(conversation) && missed(conversation))
|
|
800
999
|
behind.add(conversation);
|
|
801
1000
|
}
|
|
802
1001
|
replicated = named;
|
|
@@ -805,6 +1004,11 @@ export function createConversationStore(deps) {
|
|
|
805
1004
|
await remove(leaving);
|
|
806
1005
|
},
|
|
807
1006
|
async catchUp(progress) {
|
|
1007
|
+
if (deps.full()) {
|
|
1008
|
+
if (behind.size > 0)
|
|
1009
|
+
progress.complete = false;
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
808
1012
|
const owed = (conversation) => behind.has(conversation) && replicated.has(conversation);
|
|
809
1013
|
const read = async (conversations) => {
|
|
810
1014
|
if (conversations.length === 0 || deps.closed())
|
|
@@ -853,40 +1057,48 @@ export function createConversationStore(deps) {
|
|
|
853
1057
|
known() {
|
|
854
1058
|
return listKnown;
|
|
855
1059
|
},
|
|
856
|
-
reconnected() {
|
|
857
|
-
for (const conversation of replicated)
|
|
858
|
-
gap(conversation);
|
|
859
|
-
},
|
|
860
1060
|
reopened() {
|
|
861
|
-
|
|
1061
|
+
relisting = null;
|
|
1062
|
+
for (const [operation, dispatch] of sending)
|
|
1063
|
+
if (!dispatch.answered)
|
|
1064
|
+
sending.delete(operation);
|
|
862
1065
|
},
|
|
863
1066
|
lost() {
|
|
864
1067
|
catching.clear();
|
|
865
1068
|
},
|
|
866
1069
|
watch(conversation, options, deliver) {
|
|
867
|
-
const
|
|
868
|
-
const key = JSON.stringify([conversation, limit ?? null]);
|
|
1070
|
+
const asked = askedOf(options);
|
|
1071
|
+
const key = JSON.stringify([conversation, asked.limit, asked.after ?? null, asked.total]);
|
|
869
1072
|
const view = views.get(key) ?? {
|
|
870
1073
|
conversation,
|
|
871
|
-
|
|
1074
|
+
asked,
|
|
872
1075
|
watchers: new Set(),
|
|
873
1076
|
window: null,
|
|
1077
|
+
more: false,
|
|
1078
|
+
total: null,
|
|
874
1079
|
};
|
|
875
1080
|
views.set(key, view);
|
|
876
1081
|
const watcher = { notify: deliver, ready: false };
|
|
877
1082
|
view.watchers.add(watcher);
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
view.window
|
|
881
|
-
|
|
1083
|
+
const reading = new AbortController();
|
|
1084
|
+
void untilAnswered(() => deps.durably(async (local) => {
|
|
1085
|
+
if (view.window !== null)
|
|
1086
|
+
return;
|
|
1087
|
+
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
|
|
1088
|
+
view.window = messages;
|
|
1089
|
+
view.more = more;
|
|
1090
|
+
view.total = asked.total ? await local.messageCount(conversation) : null;
|
|
1091
|
+
}), reading.signal)
|
|
882
1092
|
.then(() => {
|
|
883
1093
|
if (deps.closed() || !view.watchers.has(watcher))
|
|
884
1094
|
return;
|
|
885
1095
|
watcher.ready = true;
|
|
886
|
-
|
|
1096
|
+
const window = view.window ?? [];
|
|
1097
|
+
watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
|
|
887
1098
|
})
|
|
888
1099
|
.catch(() => { });
|
|
889
1100
|
return () => {
|
|
1101
|
+
reading.abort();
|
|
890
1102
|
if (!view.watchers.delete(watcher))
|
|
891
1103
|
return;
|
|
892
1104
|
if (view.watchers.size === 0)
|
|
@@ -894,75 +1106,88 @@ export function createConversationStore(deps) {
|
|
|
894
1106
|
};
|
|
895
1107
|
},
|
|
896
1108
|
watchList(deliver) {
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
if (!deps.closed() && listWatchers.has(deliver))
|
|
900
|
-
deliver(entries());
|
|
901
|
-
});
|
|
1109
|
+
unlisted.add(deliver);
|
|
1110
|
+
learned();
|
|
902
1111
|
return () => {
|
|
1112
|
+
unlisted.delete(deliver);
|
|
903
1113
|
listWatchers.delete(deliver);
|
|
904
1114
|
};
|
|
905
1115
|
},
|
|
906
1116
|
watchAgents(deliver) {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
deliver(agents());
|
|
911
|
-
});
|
|
1117
|
+
unnamed.add(deliver);
|
|
1118
|
+
learned();
|
|
1119
|
+
readAgents();
|
|
912
1120
|
return () => {
|
|
1121
|
+
unnamed.delete(deliver);
|
|
913
1122
|
agentWatchers.delete(deliver);
|
|
914
1123
|
};
|
|
915
1124
|
},
|
|
1125
|
+
learned,
|
|
916
1126
|
changed(set) {
|
|
917
1127
|
for (const conversation of set.conversations)
|
|
918
1128
|
emit(conversation);
|
|
919
|
-
const declaration = deps.agents();
|
|
920
|
-
if (declaration !== undefined &&
|
|
921
|
-
set.documents.some((change) => change.locator === declaration.locator)) {
|
|
922
|
-
agentsRead = readAgents().catch(() => {
|
|
923
|
-
agentsRead = null;
|
|
924
|
-
});
|
|
925
|
-
}
|
|
926
1129
|
},
|
|
927
1130
|
receive(event) {
|
|
928
1131
|
if (deps.closed())
|
|
929
1132
|
return false;
|
|
930
1133
|
if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
|
|
931
1134
|
if (event.kind === 'list') {
|
|
932
|
-
|
|
1135
|
+
if (event.whole)
|
|
1136
|
+
relisting ??= new Map();
|
|
933
1137
|
for (const entry of event.entries) {
|
|
934
|
-
|
|
935
|
-
|
|
1138
|
+
if (relisting === null)
|
|
1139
|
+
listed(entry);
|
|
1140
|
+
else
|
|
1141
|
+
relisting.set(entry.conversation, { ...entry });
|
|
1142
|
+
}
|
|
1143
|
+
if (event.more)
|
|
1144
|
+
return false;
|
|
1145
|
+
if (relisting !== null) {
|
|
1146
|
+
listing.clear();
|
|
1147
|
+
for (const entry of relisting.values())
|
|
1148
|
+
listed(entry);
|
|
1149
|
+
relisting = null;
|
|
936
1150
|
}
|
|
1151
|
+
listCursor = event.cursor;
|
|
1152
|
+
deps.stream()?.hold(event.cursor);
|
|
937
1153
|
listKnown = true;
|
|
1154
|
+
for (const conversation of replicated) {
|
|
1155
|
+
if (!listing.has(conversation))
|
|
1156
|
+
continue;
|
|
1157
|
+
if (!missed(conversation))
|
|
1158
|
+
behind.delete(conversation);
|
|
1159
|
+
else if (!behind.has(conversation))
|
|
1160
|
+
gap(conversation);
|
|
1161
|
+
}
|
|
1162
|
+
openingEnded();
|
|
938
1163
|
}
|
|
939
1164
|
else if (event.kind === 'listed') {
|
|
940
|
-
|
|
941
|
-
|
|
1165
|
+
listed(event.entry);
|
|
1166
|
+
relisting?.set(event.entry.conversation, { ...event.entry });
|
|
942
1167
|
}
|
|
943
1168
|
else {
|
|
944
1169
|
listing.delete(event.conversation);
|
|
1170
|
+
relisting?.delete(event.conversation);
|
|
945
1171
|
}
|
|
1172
|
+
keepList();
|
|
946
1173
|
emitList();
|
|
947
|
-
return (event.kind !== 'list' &&
|
|
1174
|
+
return ((event.kind !== 'list' &&
|
|
948
1175
|
listKnown &&
|
|
949
1176
|
(listing.size !== replicated.size ||
|
|
950
|
-
[...listing.keys()].some((conversation) => !replicated.has(conversation))))
|
|
1177
|
+
[...listing.keys()].some((conversation) => !replicated.has(conversation)))) ||
|
|
1178
|
+
(listKnown && [...drafts.keys()].some(retiredChat)));
|
|
951
1179
|
}
|
|
952
1180
|
heard(event);
|
|
953
1181
|
return false;
|
|
954
1182
|
},
|
|
955
|
-
searchable() {
|
|
956
|
-
return [...(listKnown ? listing.keys() : stored)].sort();
|
|
957
|
-
},
|
|
958
|
-
acquired(conversation) {
|
|
959
|
-
return reads.has(conversation) && !partials.has(conversation);
|
|
960
|
-
},
|
|
961
1183
|
close() {
|
|
962
1184
|
views.clear();
|
|
963
1185
|
lives.clear();
|
|
1186
|
+
unsent.clear();
|
|
964
1187
|
listWatchers.clear();
|
|
1188
|
+
unlisted.clear();
|
|
965
1189
|
agentWatchers.clear();
|
|
1190
|
+
unnamed.clear();
|
|
966
1191
|
},
|
|
967
1192
|
};
|
|
968
1193
|
}
|