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