@agentshouse/mdruntime 0.1.0-ccd81b825bedf → 0.1.0-cce1d81ef3d6d
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 +18 -15
- package/dist/adapter.d.ts +118 -66
- 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 -10
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +180 -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 +15 -8
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +95 -11
- package/dist/collection.d.ts +10 -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 +786 -0
- package/dist/connection.d.ts +25 -14
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +2926 -1342
- package/dist/conversation.d.ts +25 -11
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +451 -199
- 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 +18 -23
- package/dist/file.d.ts +2 -19
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +7 -122
- package/dist/fulltext.d.ts +27 -0
- package/dist/fulltext.d.ts.map +1 -0
- package/dist/fulltext.js +370 -0
- package/dist/host.d.ts +3 -8
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +56 -24
- package/dist/identity.d.ts +0 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +0 -3
- package/dist/index.d.ts +15 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/live.d.ts +22 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +123 -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 +14 -7
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +201 -102
- package/dist/messages.d.ts +28 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +90 -0
- package/dist/model.d.ts +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 +15 -0
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +93 -3
- package/dist/opened.d.ts +22 -0
- package/dist/opened.d.ts.map +1 -0
- package/dist/opened.js +107 -0
- package/dist/outcome.d.ts +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 +33 -2
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +42 -7
- package/dist/reach.d.ts +13 -4
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +208 -73
- package/dist/react.d.ts +1 -2
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +2 -14
- package/dist/readiness.d.ts +14 -15
- 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 +16 -14
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +115 -58
- package/dist/replica.d.ts +55 -33
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +297 -191
- package/dist/room-draft.d.ts +21 -3
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +102 -28
- package/dist/rows.d.ts +38 -30
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +173 -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 +25 -42
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +30 -169
- 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 +91 -97
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +306 -665
- package/dist/stored.d.ts +130 -0
- package/dist/stored.d.ts.map +1 -0
- package/dist/stored.js +560 -0
- package/dist/tables.d.ts +325 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +578 -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 +101 -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 +33 -5
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +9 -2
- package/dist/words.d.ts +19 -0
- package/dist/words.d.ts.map +1 -0
- package/dist/words.js +156 -0
- package/package.json +18 -19
- package/AGENTS.md +0 -305
- 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/context.js +0 -10
- 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/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,
|
|
@@ -19,19 +35,26 @@ export function unheldView(conversation) {
|
|
|
19
35
|
process: null,
|
|
20
36
|
commands: [],
|
|
21
37
|
options: [],
|
|
38
|
+
starting: null,
|
|
39
|
+
subagents: [],
|
|
40
|
+
link: null,
|
|
41
|
+
deliveryUnknown: [],
|
|
22
42
|
chatDraft: null,
|
|
23
43
|
};
|
|
24
44
|
}
|
|
25
45
|
export const SESSION_STARTED = 'Session started';
|
|
26
46
|
const NEW_CHAT = 'new:';
|
|
47
|
+
export const HISTORY_CONVERSATIONS = 64;
|
|
27
48
|
export function newChat(agent) {
|
|
28
49
|
return `${NEW_CHAT}${agent}`;
|
|
29
50
|
}
|
|
30
51
|
function creationOf(input) {
|
|
31
52
|
if (typeof input !== 'object' || input === null)
|
|
32
53
|
return null;
|
|
33
|
-
const { agent_id: agent, operation_id: operation } = input;
|
|
34
|
-
return typeof agent === 'string' && typeof operation === 'string'
|
|
54
|
+
const { agent_id: agent, operation_id: operation, replaces, } = input;
|
|
55
|
+
return typeof agent === 'string' && typeof operation === 'string' && replaces === undefined
|
|
56
|
+
? { agent, operation }
|
|
57
|
+
: null;
|
|
35
58
|
}
|
|
36
59
|
function startedTurn(turn, operation) {
|
|
37
60
|
if (operation.op === 'begin')
|
|
@@ -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,34 @@ 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 };
|
|
176
215
|
}
|
|
177
216
|
const EXPIRED = { kind: 'rejected', code: 'expired', fields: {}, retryable: false };
|
|
217
|
+
const DURABLE_WRITE = {
|
|
218
|
+
kind: 'rejected',
|
|
219
|
+
code: 'durable-write',
|
|
220
|
+
fields: {},
|
|
221
|
+
retryable: true,
|
|
222
|
+
};
|
|
178
223
|
function unwritten(draft) {
|
|
179
|
-
return draft.text === ''
|
|
224
|
+
return draft.text === '';
|
|
180
225
|
}
|
|
181
226
|
function pending(draft) {
|
|
182
227
|
return {
|
|
@@ -191,11 +236,13 @@ function withSubmitted(draft, submitted) {
|
|
|
191
236
|
const { submitted: _held, ...unsent } = draft;
|
|
192
237
|
return submitted.length === 0 ? unsent : { ...unsent, submitted };
|
|
193
238
|
}
|
|
239
|
+
function prepended(draft, said) {
|
|
240
|
+
return { text: [said.text, draft.text].filter((one) => one !== '').join('\n') };
|
|
241
|
+
}
|
|
194
242
|
function returned(draft, submission, others) {
|
|
195
243
|
return withSubmitted({
|
|
196
244
|
conversation: draft.conversation,
|
|
197
|
-
|
|
198
|
-
files: [...submission.files, ...draft.files],
|
|
245
|
+
...prepended(draft, submission),
|
|
199
246
|
revision: chatDraftRevision(),
|
|
200
247
|
}, others);
|
|
201
248
|
}
|
|
@@ -206,23 +253,31 @@ export function createConversationStore(deps) {
|
|
|
206
253
|
const drafts = new Map();
|
|
207
254
|
const sending = new Map();
|
|
208
255
|
const behind = new Set();
|
|
256
|
+
const catching = new Map();
|
|
209
257
|
const gaps = new Map();
|
|
210
258
|
const lives = new Map();
|
|
259
|
+
const unsent = new Map();
|
|
211
260
|
const views = new Map();
|
|
212
261
|
const listing = new Map();
|
|
262
|
+
let relisting = null;
|
|
263
|
+
let listCursor = null;
|
|
213
264
|
const creations = new Set();
|
|
214
265
|
const listWatchers = new Set();
|
|
266
|
+
const unlisted = new Set();
|
|
215
267
|
let listKnown = false;
|
|
268
|
+
let listHeld = false;
|
|
269
|
+
let listKept = false;
|
|
216
270
|
const agentWatchers = new Set();
|
|
217
|
-
|
|
218
|
-
let
|
|
219
|
-
let
|
|
271
|
+
const unnamed = new Set();
|
|
272
|
+
let agentsShown = null;
|
|
273
|
+
let listedAgents = null;
|
|
274
|
+
let agentsReading = false;
|
|
220
275
|
let replicated = new Set();
|
|
221
276
|
let handedReplicated;
|
|
222
277
|
function liveOf(conversation) {
|
|
223
278
|
return lives.get(conversation) ?? QUIET;
|
|
224
279
|
}
|
|
225
|
-
function viewOf(conversation, held) {
|
|
280
|
+
function viewOf(conversation, held, frame) {
|
|
226
281
|
const draft = drafts.get(conversation);
|
|
227
282
|
const facts = liveOf(conversation);
|
|
228
283
|
return {
|
|
@@ -233,14 +288,19 @@ export function createConversationStore(deps) {
|
|
|
233
288
|
text,
|
|
234
289
|
message,
|
|
235
290
|
})),
|
|
291
|
+
...frame,
|
|
236
292
|
turn: structuredClone(facts.turn),
|
|
237
293
|
questions: structuredClone(facts.questions),
|
|
238
294
|
process: facts.process === null ? null : { ...facts.process },
|
|
239
295
|
commands: structuredClone(facts.commands),
|
|
240
296
|
options: structuredClone(facts.options),
|
|
297
|
+
starting: facts.starting,
|
|
298
|
+
subagents: structuredClone(facts.subagents),
|
|
299
|
+
link: facts.link === null ? null : { ...facts.link },
|
|
300
|
+
deliveryUnknown: [...facts.deliveryUnknown],
|
|
241
301
|
chatDraft: draft === undefined || unwritten(draft)
|
|
242
302
|
? null
|
|
243
|
-
: { text: draft.text,
|
|
303
|
+
: { text: draft.text, revision: draft.revision },
|
|
244
304
|
};
|
|
245
305
|
}
|
|
246
306
|
function emit(conversation) {
|
|
@@ -252,21 +312,36 @@ export function createConversationStore(deps) {
|
|
|
252
312
|
const ready = [...view.watchers].filter((watcher) => watcher.ready);
|
|
253
313
|
if (ready.length === 0)
|
|
254
314
|
continue;
|
|
255
|
-
const value = viewOf(conversation, view.window);
|
|
315
|
+
const value = viewOf(conversation, view.window, sharedFrame(view, view.window));
|
|
256
316
|
for (const watcher of ready)
|
|
257
317
|
watcher.notify(value);
|
|
258
318
|
}
|
|
259
319
|
}
|
|
260
|
-
function
|
|
320
|
+
function sharedFrame(view, window) {
|
|
321
|
+
return frameOf(view.asked, window, view.more, view.total);
|
|
322
|
+
}
|
|
323
|
+
async function slide(local, conversation, arriving) {
|
|
324
|
+
let count;
|
|
261
325
|
for (const view of views.values()) {
|
|
262
326
|
if (view.conversation !== conversation || view.window === null)
|
|
263
327
|
continue;
|
|
264
|
-
|
|
328
|
+
if (arriving === null) {
|
|
329
|
+
view.window = [];
|
|
330
|
+
view.more = false;
|
|
331
|
+
if (view.total !== null)
|
|
332
|
+
view.total = 0;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
const joining = arriving.filter((message) => precedes(message, view.asked.before));
|
|
336
|
+
const next = windowOf(view.window, joining, view.asked.limit);
|
|
337
|
+
view.window = next.messages;
|
|
338
|
+
view.more ||= next.dropped;
|
|
339
|
+
if (view.total !== null && arriving.length > 0) {
|
|
340
|
+
count ??= await local.messageCount(conversation);
|
|
341
|
+
view.total = count;
|
|
342
|
+
}
|
|
265
343
|
}
|
|
266
344
|
}
|
|
267
|
-
function changesOf(conversation, arriving) {
|
|
268
|
-
return arriving.map(({ identity, text }) => ({ conversation, identity, text }));
|
|
269
|
-
}
|
|
270
345
|
function stateOf(conversation) {
|
|
271
346
|
const facts = liveOf(conversation);
|
|
272
347
|
if (facts.questions.length > 0)
|
|
@@ -276,34 +351,110 @@ export function createConversationStore(deps) {
|
|
|
276
351
|
function entries() {
|
|
277
352
|
return [...listing.values()].map((entry) => ({
|
|
278
353
|
conversation: entry.conversation,
|
|
279
|
-
agent: { identity: entry.agent, name:
|
|
354
|
+
agent: { identity: entry.agent, name: entry.agentName },
|
|
280
355
|
name: entry.name,
|
|
281
356
|
created: entry.created,
|
|
282
357
|
finished: entry.finished,
|
|
283
358
|
outcome: entry.outcome,
|
|
359
|
+
waiting: entry.waiting,
|
|
284
360
|
state: stateOf(entry.conversation),
|
|
285
361
|
started: lives.get(entry.conversation)?.turn?.started ?? null,
|
|
286
362
|
}));
|
|
287
363
|
}
|
|
364
|
+
function keepList() {
|
|
365
|
+
if (!listKnown || listKept)
|
|
366
|
+
return;
|
|
367
|
+
listKept = true;
|
|
368
|
+
queueMicrotask(() => {
|
|
369
|
+
listKept = false;
|
|
370
|
+
if (deps.closed())
|
|
371
|
+
return;
|
|
372
|
+
const entries = [...listing.values()];
|
|
373
|
+
const cursor = listCursor;
|
|
374
|
+
void deps
|
|
375
|
+
.durably(async (local) => {
|
|
376
|
+
await local.fact('conversationList', entries);
|
|
377
|
+
await local.fact('conversationCursor', cursor);
|
|
378
|
+
})
|
|
379
|
+
.catch(() => { });
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
function listed(entry) {
|
|
383
|
+
listing.set(entry.conversation, { ...entry });
|
|
384
|
+
creations.delete(entry.conversation);
|
|
385
|
+
}
|
|
288
386
|
function emitList() {
|
|
289
|
-
if (deps.closed()
|
|
387
|
+
if (deps.closed())
|
|
290
388
|
return;
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
watcher
|
|
389
|
+
if (listWatchers.size > 0) {
|
|
390
|
+
const value = entries();
|
|
391
|
+
for (const watcher of listWatchers)
|
|
392
|
+
watcher(value);
|
|
393
|
+
}
|
|
394
|
+
emitAgents();
|
|
395
|
+
learned();
|
|
396
|
+
}
|
|
397
|
+
function learned() {
|
|
398
|
+
if (deps.closed())
|
|
399
|
+
return;
|
|
400
|
+
if (!listKnown && !listHeld && deps.stream() !== undefined && deps.online())
|
|
401
|
+
return;
|
|
402
|
+
if (unlisted.size > 0) {
|
|
403
|
+
const value = entries();
|
|
404
|
+
for (const watcher of [...unlisted]) {
|
|
405
|
+
unlisted.delete(watcher);
|
|
406
|
+
listWatchers.add(watcher);
|
|
407
|
+
watcher(value);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (unnamed.size > 0) {
|
|
411
|
+
const value = agents();
|
|
412
|
+
for (const watcher of [...unnamed]) {
|
|
413
|
+
unnamed.delete(watcher);
|
|
414
|
+
agentWatchers.add(watcher);
|
|
415
|
+
watcher(value);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
294
418
|
}
|
|
295
419
|
function agents() {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
.
|
|
420
|
+
const named = new Map();
|
|
421
|
+
for (const agent of listedAgents ?? [])
|
|
422
|
+
named.set(agent.agent, agent);
|
|
423
|
+
for (const entry of listing.values()) {
|
|
424
|
+
named.set(entry.agent, { name: entry.agentName, retired: entry.agentRetired });
|
|
425
|
+
}
|
|
426
|
+
return [...named]
|
|
427
|
+
.filter(([, agent]) => !agent.retired)
|
|
428
|
+
.map(([identity, agent]) => ({ identity, name: agent.name }))
|
|
299
429
|
.sort((left, right) => left.name === right.name
|
|
300
430
|
? left.identity.localeCompare(right.identity)
|
|
301
431
|
: left.name.localeCompare(right.name));
|
|
302
432
|
}
|
|
433
|
+
function readAgents() {
|
|
434
|
+
const stream = deps.stream();
|
|
435
|
+
if (stream?.agents === undefined || !deps.online() || agentsReading)
|
|
436
|
+
return;
|
|
437
|
+
agentsReading = true;
|
|
438
|
+
void stream
|
|
439
|
+
.agents()
|
|
440
|
+
.then((read) => {
|
|
441
|
+
if (read === null)
|
|
442
|
+
return;
|
|
443
|
+
listedAgents = read;
|
|
444
|
+
emitAgents();
|
|
445
|
+
}, () => { })
|
|
446
|
+
.finally(() => {
|
|
447
|
+
agentsReading = false;
|
|
448
|
+
});
|
|
449
|
+
}
|
|
303
450
|
function emitAgents() {
|
|
304
451
|
if (deps.closed() || agentWatchers.size === 0)
|
|
305
452
|
return;
|
|
306
453
|
const value = agents();
|
|
454
|
+
const shown = JSON.stringify(value);
|
|
455
|
+
if (shown === agentsShown)
|
|
456
|
+
return;
|
|
457
|
+
agentsShown = shown;
|
|
307
458
|
for (const watcher of agentWatchers)
|
|
308
459
|
watcher(value);
|
|
309
460
|
}
|
|
@@ -319,53 +470,42 @@ export function createConversationStore(deps) {
|
|
|
319
470
|
if (stateOf(conversation) !== before || next?.turn?.started !== began)
|
|
320
471
|
emitList();
|
|
321
472
|
}
|
|
322
|
-
async function readAgents() {
|
|
323
|
-
const declaration = deps.agents();
|
|
324
|
-
if (declaration === undefined)
|
|
325
|
-
return;
|
|
326
|
-
const held = await deps.documents(declaration);
|
|
327
|
-
if ('pending' in held)
|
|
328
|
-
return;
|
|
329
|
-
const { documents } = held;
|
|
330
|
-
const names = new Map();
|
|
331
|
-
const retired = new Set();
|
|
332
|
-
for (const document of documents) {
|
|
333
|
-
if (document.type !== declaration.type)
|
|
334
|
-
continue;
|
|
335
|
-
const identity = document.fields?.[declaration.identity];
|
|
336
|
-
const name = document.fields?.[declaration.name];
|
|
337
|
-
if (typeof identity !== 'string' || typeof name !== 'string')
|
|
338
|
-
continue;
|
|
339
|
-
names.set(identity, name);
|
|
340
|
-
const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
|
|
341
|
-
if (ended !== undefined && ended !== null)
|
|
342
|
-
retired.add(identity);
|
|
343
|
-
}
|
|
344
|
-
const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
|
|
345
|
-
if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
|
|
346
|
-
return;
|
|
347
|
-
agentNames = names;
|
|
348
|
-
retiredAgents = retired;
|
|
349
|
-
if (named)
|
|
350
|
-
emitList();
|
|
351
|
-
emitAgents();
|
|
352
|
-
const ended = [...drafts.keys()].filter(retiredChat);
|
|
353
|
-
if (ended.length > 0)
|
|
354
|
-
await remove(ended);
|
|
355
|
-
}
|
|
356
473
|
function retiredChat(conversation) {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
agentsRead = null;
|
|
362
|
-
});
|
|
363
|
-
return agentsRead;
|
|
474
|
+
if (!conversation.startsWith(NEW_CHAT))
|
|
475
|
+
return false;
|
|
476
|
+
const agent = conversation.slice(NEW_CHAT.length);
|
|
477
|
+
return [...listing.values()].some((entry) => entry.agent === agent && entry.agentRetired);
|
|
364
478
|
}
|
|
365
479
|
function live(conversation, change) {
|
|
480
|
+
const owed = unsent.get(conversation);
|
|
481
|
+
for (const fact of FACTS.filter((one) => one in change))
|
|
482
|
+
owed?.delete(fact);
|
|
366
483
|
setLive(conversation, { ...liveOf(conversation), ...change });
|
|
367
484
|
emit(conversation);
|
|
368
485
|
}
|
|
486
|
+
function openingEnded() {
|
|
487
|
+
const owed = [...unsent];
|
|
488
|
+
unsent.clear();
|
|
489
|
+
for (const [conversation, facts] of owed) {
|
|
490
|
+
const held = lives.get(conversation);
|
|
491
|
+
if (held === undefined || facts.size === 0)
|
|
492
|
+
continue;
|
|
493
|
+
setLive(conversation, {
|
|
494
|
+
turn: held.turn,
|
|
495
|
+
questions: facts.has('questions') ? QUIET.questions : held.questions,
|
|
496
|
+
process: facts.has('process') ? QUIET.process : held.process,
|
|
497
|
+
commands: facts.has('commands') ? QUIET.commands : held.commands,
|
|
498
|
+
options: facts.has('options') ? QUIET.options : held.options,
|
|
499
|
+
starting: facts.has('starting') ? QUIET.starting : held.starting,
|
|
500
|
+
subagents: facts.has('subagents') ? QUIET.subagents : held.subagents,
|
|
501
|
+
link: facts.has('link') ? QUIET.link : held.link,
|
|
502
|
+
deliveryUnknown: facts.has('deliveryUnknown')
|
|
503
|
+
? QUIET.deliveryUnknown
|
|
504
|
+
: held.deliveryUnknown,
|
|
505
|
+
});
|
|
506
|
+
emit(conversation);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
369
509
|
function relayed(conversation, turn, operation) {
|
|
370
510
|
const held = liveOf(conversation).turn;
|
|
371
511
|
const next = held?.turn === turn ? appliedTurn(held, operation) : startedTurn(turn, operation);
|
|
@@ -392,12 +532,6 @@ export function createConversationStore(deps) {
|
|
|
392
532
|
}
|
|
393
533
|
async function remove(leaving) {
|
|
394
534
|
await deps.durably(async (local) => {
|
|
395
|
-
const removed = [];
|
|
396
|
-
for (const conversation of leaving) {
|
|
397
|
-
for (const identity of await local.messageIdentities(conversation)) {
|
|
398
|
-
removed.push({ conversation, identity, text: null });
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
535
|
await local.dropConversations(leaving);
|
|
402
536
|
const changed = [];
|
|
403
537
|
for (const conversation of leaving) {
|
|
@@ -409,13 +543,28 @@ export function createConversationStore(deps) {
|
|
|
409
543
|
partials.delete(conversation);
|
|
410
544
|
drafts.delete(conversation);
|
|
411
545
|
behind.delete(conversation);
|
|
546
|
+
unsent.delete(conversation);
|
|
412
547
|
setLive(conversation, null);
|
|
413
|
-
slide(conversation, null);
|
|
548
|
+
await slide(local, conversation, null);
|
|
414
549
|
}
|
|
415
550
|
if (changed.length > 0)
|
|
416
|
-
deps.publish(conversationChangeSet(
|
|
551
|
+
deps.publish(conversationChangeSet(changed));
|
|
417
552
|
});
|
|
418
553
|
}
|
|
554
|
+
function missed(conversation) {
|
|
555
|
+
const read = reads.get(conversation);
|
|
556
|
+
const newest = listing.get(conversation)?.newest;
|
|
557
|
+
return (read === undefined ||
|
|
558
|
+
newest === undefined ||
|
|
559
|
+
partials.has(conversation) ||
|
|
560
|
+
(newest !== null && (read === null || newest > read)));
|
|
561
|
+
}
|
|
562
|
+
function noted(conversation, message) {
|
|
563
|
+
const entry = listing.get(conversation);
|
|
564
|
+
if (entry === undefined || (entry.newest !== null && entry.newest >= message.position))
|
|
565
|
+
return;
|
|
566
|
+
listing.set(conversation, { ...entry, newest: message.position });
|
|
567
|
+
}
|
|
419
568
|
function gap(conversation) {
|
|
420
569
|
gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
|
|
421
570
|
behind.add(conversation);
|
|
@@ -438,18 +587,19 @@ export function createConversationStore(deps) {
|
|
|
438
587
|
if (advances)
|
|
439
588
|
reads.set(conversation, message.position);
|
|
440
589
|
sealTurns(conversation, new Set([message.turn]));
|
|
441
|
-
slide(conversation, [message]);
|
|
442
|
-
deps.publish(conversationChangeSet(
|
|
590
|
+
await slide(local, conversation, [message]);
|
|
591
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
443
592
|
});
|
|
444
593
|
}
|
|
445
|
-
async function readHistories(conversations) {
|
|
594
|
+
async function readHistories(conversations, ended) {
|
|
446
595
|
const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
|
|
447
|
-
let
|
|
596
|
+
let waiting = conversations.map((conversation) => ({
|
|
448
597
|
conversation,
|
|
449
598
|
after: reads.get(conversation) ?? undefined,
|
|
450
599
|
}));
|
|
451
|
-
while (
|
|
452
|
-
const
|
|
600
|
+
while (waiting.length > 0) {
|
|
601
|
+
const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
|
|
602
|
+
const result = await deps.history({
|
|
453
603
|
conversations: asking.map(({ conversation, after }) => ({
|
|
454
604
|
conversation,
|
|
455
605
|
...(after === undefined ? {} : { after }),
|
|
@@ -468,10 +618,33 @@ export function createConversationStore(deps) {
|
|
|
468
618
|
following.push({ conversation: page.conversation, after: page.next });
|
|
469
619
|
}
|
|
470
620
|
}
|
|
471
|
-
asking
|
|
621
|
+
for (const { conversation } of asking) {
|
|
622
|
+
if (!following.some((next) => next.conversation === conversation))
|
|
623
|
+
ended(conversation);
|
|
624
|
+
}
|
|
625
|
+
waiting = [...waiting.slice(HISTORY_CONVERSATIONS), ...following];
|
|
472
626
|
}
|
|
473
627
|
return null;
|
|
474
628
|
}
|
|
629
|
+
function caughtUp(conversations) {
|
|
630
|
+
const ends = new Map();
|
|
631
|
+
for (const conversation of conversations) {
|
|
632
|
+
const held = new Promise((end) => ends.set(conversation, end));
|
|
633
|
+
catching.set(conversation, held);
|
|
634
|
+
void held.then(() => {
|
|
635
|
+
if (catching.get(conversation) === held)
|
|
636
|
+
catching.delete(conversation);
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
const ended = (conversation) => ends.get(conversation)?.();
|
|
640
|
+
const read = readHistories(conversations, ended);
|
|
641
|
+
const endAll = () => {
|
|
642
|
+
for (const conversation of conversations)
|
|
643
|
+
ended(conversation);
|
|
644
|
+
};
|
|
645
|
+
read.then(endAll, endAll);
|
|
646
|
+
return read;
|
|
647
|
+
}
|
|
475
648
|
function keepPage(conversation, arriving, from, next, generation) {
|
|
476
649
|
return deps.durably(async (local) => {
|
|
477
650
|
if (!replicated.has(conversation))
|
|
@@ -490,9 +663,9 @@ export function createConversationStore(deps) {
|
|
|
490
663
|
sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
|
|
491
664
|
if (whole && (gaps.get(conversation) ?? 0) === generation)
|
|
492
665
|
behind.delete(conversation);
|
|
493
|
-
slide(conversation, arriving);
|
|
666
|
+
await slide(local, conversation, arriving);
|
|
494
667
|
if (arriving.length > 0 || sealing || whole) {
|
|
495
|
-
deps.publish(conversationChangeSet(
|
|
668
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
496
669
|
}
|
|
497
670
|
});
|
|
498
671
|
}
|
|
@@ -501,11 +674,8 @@ export function createConversationStore(deps) {
|
|
|
501
674
|
if (event.kind === 'gap') {
|
|
502
675
|
if (replicated.has(conversation))
|
|
503
676
|
gap(conversation);
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
return;
|
|
507
|
-
setLive(conversation, { ...QUIET, turn: held.turn });
|
|
508
|
-
emit(conversation);
|
|
677
|
+
if (lives.has(conversation))
|
|
678
|
+
unsent.set(conversation, new Set(FACTS));
|
|
509
679
|
return;
|
|
510
680
|
}
|
|
511
681
|
if (event.kind === 'turn') {
|
|
@@ -528,11 +698,35 @@ export function createConversationStore(deps) {
|
|
|
528
698
|
live(conversation, { options: structuredClone(event.options) });
|
|
529
699
|
return;
|
|
530
700
|
}
|
|
701
|
+
if (event.kind === 'starting') {
|
|
702
|
+
live(conversation, { starting: event.step });
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
if (event.kind === 'subagents') {
|
|
706
|
+
live(conversation, { subagents: structuredClone(event.subagents) });
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
if (event.kind === 'link') {
|
|
710
|
+
live(conversation, { link: { connected: event.connected } });
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
if (event.kind === 'deliveries') {
|
|
714
|
+
live(conversation, { deliveryUnknown: [...event.unknown] });
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
531
717
|
const arriving = acceptedMessage(event.message);
|
|
532
718
|
if (arriving === null)
|
|
533
719
|
return;
|
|
720
|
+
noted(conversation, arriving);
|
|
534
721
|
if (replicated.has(conversation)) {
|
|
535
|
-
|
|
722
|
+
if (deps.full()) {
|
|
723
|
+
gap(conversation);
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
void keep(conversation, arriving).catch((error) => {
|
|
727
|
+
if (refusedForSpace(error))
|
|
728
|
+
gap(conversation);
|
|
729
|
+
});
|
|
536
730
|
return;
|
|
537
731
|
}
|
|
538
732
|
if (sealTurns(conversation, new Set([arriving.turn])))
|
|
@@ -567,7 +761,6 @@ export function createConversationStore(deps) {
|
|
|
567
761
|
const blank = {
|
|
568
762
|
conversation: from,
|
|
569
763
|
text: '',
|
|
570
|
-
files: [],
|
|
571
764
|
revision: chatDraftRevision(),
|
|
572
765
|
};
|
|
573
766
|
const { moves: _answered, ...unsent } = held ?? blank;
|
|
@@ -577,7 +770,7 @@ export function createConversationStore(deps) {
|
|
|
577
770
|
drafts.set(from, left);
|
|
578
771
|
for (const one of taken)
|
|
579
772
|
drafts.set(to, one);
|
|
580
|
-
deps.publish(conversationChangeSet([
|
|
773
|
+
deps.publish(conversationChangeSet([from, to]));
|
|
581
774
|
});
|
|
582
775
|
}
|
|
583
776
|
function withDraft(conversation, next) {
|
|
@@ -586,7 +779,7 @@ export function createConversationStore(deps) {
|
|
|
586
779
|
if (following === undefined)
|
|
587
780
|
return;
|
|
588
781
|
await kept(local, conversation, following);
|
|
589
|
-
deps.publish(conversationChangeSet([
|
|
782
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
590
783
|
});
|
|
591
784
|
}
|
|
592
785
|
async function submitted(conversation, operation, draft) {
|
|
@@ -598,17 +791,12 @@ export function createConversationStore(deps) {
|
|
|
598
791
|
if (submission !== undefined)
|
|
599
792
|
return undefined;
|
|
600
793
|
const said = draft ?? current;
|
|
601
|
-
submission = {
|
|
602
|
-
operation,
|
|
603
|
-
text: said?.text ?? '',
|
|
604
|
-
files: [...(said?.files ?? [])],
|
|
605
|
-
turn: null,
|
|
606
|
-
};
|
|
794
|
+
submission = { operation, text: said?.text ?? '', turn: null };
|
|
607
795
|
return {
|
|
608
796
|
conversation,
|
|
609
797
|
...(draft === undefined || current === undefined
|
|
610
|
-
? { text: '',
|
|
611
|
-
: { text: current.text,
|
|
798
|
+
? { text: '', revision: chatDraftRevision() }
|
|
799
|
+
: { text: current.text, revision: current.revision }),
|
|
612
800
|
submitted: [...(current?.submitted ?? []), submission],
|
|
613
801
|
};
|
|
614
802
|
});
|
|
@@ -623,22 +811,21 @@ export function createConversationStore(deps) {
|
|
|
623
811
|
});
|
|
624
812
|
}
|
|
625
813
|
async function dispatched(conversation, submission) {
|
|
626
|
-
const dispatch = {};
|
|
814
|
+
const dispatch = { answered: false };
|
|
627
815
|
sending.set(submission.operation, dispatch);
|
|
628
816
|
try {
|
|
629
|
-
const
|
|
630
|
-
const result = await invoke('conversation.send', conversation, {
|
|
631
|
-
text: submission.text,
|
|
632
|
-
files,
|
|
817
|
+
const result = await invoke('conversations/send', conversation, {
|
|
633
818
|
operation_id: submission.operation,
|
|
819
|
+
text: submission.text,
|
|
634
820
|
});
|
|
821
|
+
dispatch.answered = result.kind !== 'transport-uncertainty';
|
|
635
822
|
if (result.kind === 'revoked-authorization')
|
|
636
823
|
return result;
|
|
637
824
|
if (result.kind !== 'action' && result.retryable) {
|
|
638
825
|
deps.unanswered();
|
|
639
826
|
return result;
|
|
640
827
|
}
|
|
641
|
-
const turn = answered(result, '
|
|
828
|
+
const turn = answered(result, 'message_id');
|
|
642
829
|
await withDraft(conversation, async (current, local) => {
|
|
643
830
|
const held = current?.submitted?.find((one) => one.operation === submission.operation);
|
|
644
831
|
if (current === undefined || held === undefined || held.turn !== null)
|
|
@@ -658,21 +845,32 @@ export function createConversationStore(deps) {
|
|
|
658
845
|
sending.delete(submission.operation);
|
|
659
846
|
}
|
|
660
847
|
}
|
|
661
|
-
function invoke(name, conversation, extra = {}) {
|
|
662
|
-
return deps.call(name, { conversation, ...extra });
|
|
848
|
+
function invoke(name, conversation, extra = { operation_id: changeBatchIdentity() }) {
|
|
849
|
+
return deps.call(name, { conversation_id: conversation, ...extra });
|
|
663
850
|
}
|
|
664
851
|
const surface = {
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
852
|
+
list() {
|
|
853
|
+
return new Promise((settle) => {
|
|
854
|
+
const answer = (listed) => {
|
|
855
|
+
unlisted.delete(answer);
|
|
856
|
+
listWatchers.delete(answer);
|
|
857
|
+
settle(listed);
|
|
858
|
+
};
|
|
859
|
+
unlisted.add(answer);
|
|
860
|
+
learned();
|
|
861
|
+
});
|
|
668
862
|
},
|
|
669
863
|
async read(conversation, options = {}) {
|
|
670
|
-
const
|
|
671
|
-
return deps.durably(async (local) =>
|
|
864
|
+
const asked = askedOf(options);
|
|
865
|
+
return deps.durably(async (local) => {
|
|
866
|
+
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
|
|
867
|
+
const total = asked.total ? await local.messageCount(conversation) : null;
|
|
868
|
+
return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total)));
|
|
869
|
+
});
|
|
672
870
|
},
|
|
673
871
|
async create(input) {
|
|
674
|
-
const result = await deps.call('
|
|
675
|
-
const conversation = answered(result, '
|
|
872
|
+
const result = await deps.call('conversations/create', input);
|
|
873
|
+
const conversation = answered(result, 'conversation_id');
|
|
676
874
|
if (conversation === null)
|
|
677
875
|
return result;
|
|
678
876
|
if (!listing.has(conversation))
|
|
@@ -683,8 +881,17 @@ export function createConversationStore(deps) {
|
|
|
683
881
|
return result;
|
|
684
882
|
},
|
|
685
883
|
async send(conversation, operation = changeBatchIdentity(), draft) {
|
|
884
|
+
if (deps.full())
|
|
885
|
+
return DURABLE_WRITE;
|
|
686
886
|
if (deps.online()) {
|
|
687
|
-
const
|
|
887
|
+
const held = await submitted(conversation, operation, draft).catch((error) => {
|
|
888
|
+
if (refusedForSpace(error))
|
|
889
|
+
return null;
|
|
890
|
+
throw error;
|
|
891
|
+
});
|
|
892
|
+
if (held === null)
|
|
893
|
+
return DURABLE_WRITE;
|
|
894
|
+
const { submission, earlier } = held;
|
|
688
895
|
if (!earlier || !deps.expired(operation))
|
|
689
896
|
return dispatched(conversation, submission);
|
|
690
897
|
await forgotten(conversation, submission);
|
|
@@ -693,33 +900,24 @@ export function createConversationStore(deps) {
|
|
|
693
900
|
if (draft !== undefined) {
|
|
694
901
|
await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
|
|
695
902
|
? undefined
|
|
696
|
-
: returned(current ?? { conversation, text: '',
|
|
903
|
+
: returned(current ?? { conversation, text: '', revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
|
|
697
904
|
}
|
|
698
905
|
return OFFLINE;
|
|
699
906
|
},
|
|
700
907
|
answer(conversation, answer) {
|
|
701
|
-
return invoke('
|
|
908
|
+
return invoke('conversations/interaction/answer', conversation, {
|
|
909
|
+
operation_id: changeBatchIdentity(),
|
|
910
|
+
...answer,
|
|
911
|
+
});
|
|
702
912
|
},
|
|
703
913
|
interrupt(conversation) {
|
|
704
|
-
return invoke('
|
|
914
|
+
return invoke('conversations/interrupt', conversation);
|
|
705
915
|
},
|
|
706
916
|
kill(conversation) {
|
|
707
|
-
return invoke('
|
|
708
|
-
},
|
|
709
|
-
async cancel(conversation, turn) {
|
|
710
|
-
const result = await invoke('conversation.cancel', conversation, { turn });
|
|
711
|
-
if (result.kind !== 'action')
|
|
712
|
-
return result;
|
|
713
|
-
await withDraft(conversation, (current) => {
|
|
714
|
-
const held = current?.submitted?.find((one) => one.turn === turn);
|
|
715
|
-
if (current === undefined || held === undefined)
|
|
716
|
-
return undefined;
|
|
717
|
-
return returned(current, held, current.submitted.filter((one) => one !== held));
|
|
718
|
-
});
|
|
719
|
-
return result;
|
|
917
|
+
return invoke('conversations/kill', conversation);
|
|
720
918
|
},
|
|
721
919
|
delete(conversation) {
|
|
722
|
-
return invoke('
|
|
920
|
+
return invoke('conversations/delete', conversation);
|
|
723
921
|
},
|
|
724
922
|
};
|
|
725
923
|
const chatDraft = {
|
|
@@ -727,27 +925,36 @@ export function createConversationStore(deps) {
|
|
|
727
925
|
return withDraft(conversation, (current) => ({
|
|
728
926
|
conversation,
|
|
729
927
|
text: draft.text,
|
|
730
|
-
files: [...draft.files],
|
|
731
928
|
revision,
|
|
732
929
|
...(current === undefined ? {} : pending(current)),
|
|
733
930
|
}));
|
|
734
931
|
},
|
|
932
|
+
prepend(conversation, draft) {
|
|
933
|
+
return withDraft(conversation, (current) => ({
|
|
934
|
+
conversation,
|
|
935
|
+
...(current === undefined ? { text: draft.text } : prepended(current, draft)),
|
|
936
|
+
revision: chatDraftRevision(),
|
|
937
|
+
...(current === undefined ? {} : pending(current)),
|
|
938
|
+
}));
|
|
939
|
+
},
|
|
735
940
|
discard(conversation) {
|
|
736
941
|
return withDraft(conversation, (current) => current === undefined
|
|
737
942
|
? undefined
|
|
738
|
-
: {
|
|
739
|
-
conversation,
|
|
740
|
-
text: '',
|
|
741
|
-
files: [],
|
|
742
|
-
revision: chatDraftRevision(),
|
|
743
|
-
...pending(current),
|
|
744
|
-
});
|
|
943
|
+
: { conversation, text: '', revision: chatDraftRevision(), ...pending(current) });
|
|
745
944
|
},
|
|
746
945
|
};
|
|
747
946
|
return {
|
|
748
947
|
surface,
|
|
749
948
|
chatDraft,
|
|
750
|
-
hydrate(held, kept) {
|
|
949
|
+
hydrate(held, kept, list, cursor) {
|
|
950
|
+
if (list !== null && !listKnown) {
|
|
951
|
+
listing.clear();
|
|
952
|
+
for (const entry of list)
|
|
953
|
+
listing.set(entry.conversation, { ...entry });
|
|
954
|
+
listHeld = true;
|
|
955
|
+
listCursor = cursor;
|
|
956
|
+
deps.stream()?.hold(cursor);
|
|
957
|
+
}
|
|
751
958
|
stored.clear();
|
|
752
959
|
reads.clear();
|
|
753
960
|
partials.clear();
|
|
@@ -761,6 +968,8 @@ export function createConversationStore(deps) {
|
|
|
761
968
|
drafts.clear();
|
|
762
969
|
for (const record of kept)
|
|
763
970
|
drafts.set(record.conversation, record);
|
|
971
|
+
if (listHeld)
|
|
972
|
+
learned();
|
|
764
973
|
},
|
|
765
974
|
async reconcile() {
|
|
766
975
|
if (!listKnown)
|
|
@@ -772,7 +981,7 @@ export function createConversationStore(deps) {
|
|
|
772
981
|
!creations.has(conversation) &&
|
|
773
982
|
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
|
|
774
983
|
for (const conversation of named) {
|
|
775
|
-
if (!replicated.has(conversation))
|
|
984
|
+
if (!replicated.has(conversation) && missed(conversation))
|
|
776
985
|
behind.add(conversation);
|
|
777
986
|
}
|
|
778
987
|
replicated = named;
|
|
@@ -781,12 +990,25 @@ export function createConversationStore(deps) {
|
|
|
781
990
|
await remove(leaving);
|
|
782
991
|
},
|
|
783
992
|
async catchUp(progress) {
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
993
|
+
if (deps.full()) {
|
|
994
|
+
if (behind.size > 0)
|
|
995
|
+
progress.complete = false;
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
const owed = (conversation) => behind.has(conversation) && replicated.has(conversation);
|
|
999
|
+
const read = async (conversations) => {
|
|
1000
|
+
if (conversations.length === 0 || deps.closed())
|
|
1001
|
+
return;
|
|
1002
|
+
const failed = await caughtUp(conversations);
|
|
787
1003
|
if (failed?.kind === 'transport-uncertainty')
|
|
788
1004
|
progress.reachable = false;
|
|
789
|
-
}
|
|
1005
|
+
};
|
|
1006
|
+
const asking = [...behind].filter(owed).sort();
|
|
1007
|
+
const awaited = asking.filter((conversation) => catching.has(conversation));
|
|
1008
|
+
await Promise.all([
|
|
1009
|
+
read(asking.filter((conversation) => !catching.has(conversation))),
|
|
1010
|
+
Promise.all(awaited.map((conversation) => catching.get(conversation))).then(() => read(awaited.filter((conversation) => owed(conversation) && !catching.has(conversation)))),
|
|
1011
|
+
]);
|
|
790
1012
|
if (behind.size > 0)
|
|
791
1013
|
progress.complete = false;
|
|
792
1014
|
},
|
|
@@ -821,37 +1043,48 @@ export function createConversationStore(deps) {
|
|
|
821
1043
|
known() {
|
|
822
1044
|
return listKnown;
|
|
823
1045
|
},
|
|
824
|
-
reconnected() {
|
|
825
|
-
for (const conversation of replicated)
|
|
826
|
-
gap(conversation);
|
|
827
|
-
},
|
|
828
1046
|
reopened() {
|
|
829
|
-
|
|
1047
|
+
relisting = null;
|
|
1048
|
+
for (const [operation, dispatch] of sending)
|
|
1049
|
+
if (!dispatch.answered)
|
|
1050
|
+
sending.delete(operation);
|
|
1051
|
+
},
|
|
1052
|
+
lost() {
|
|
1053
|
+
catching.clear();
|
|
830
1054
|
},
|
|
831
1055
|
watch(conversation, options, deliver) {
|
|
832
|
-
const
|
|
833
|
-
const key = JSON.stringify([conversation, limit ?? null]);
|
|
1056
|
+
const asked = askedOf(options);
|
|
1057
|
+
const key = JSON.stringify([conversation, asked.limit, asked.after ?? null, asked.total]);
|
|
834
1058
|
const view = views.get(key) ?? {
|
|
835
1059
|
conversation,
|
|
836
|
-
|
|
1060
|
+
asked,
|
|
837
1061
|
watchers: new Set(),
|
|
838
1062
|
window: null,
|
|
1063
|
+
more: false,
|
|
1064
|
+
total: null,
|
|
839
1065
|
};
|
|
840
1066
|
views.set(key, view);
|
|
841
1067
|
const watcher = { notify: deliver, ready: false };
|
|
842
1068
|
view.watchers.add(watcher);
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
view.window
|
|
846
|
-
|
|
1069
|
+
const reading = new AbortController();
|
|
1070
|
+
void untilAnswered(() => deps.durably(async (local) => {
|
|
1071
|
+
if (view.window !== null)
|
|
1072
|
+
return;
|
|
1073
|
+
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
|
|
1074
|
+
view.window = messages;
|
|
1075
|
+
view.more = more;
|
|
1076
|
+
view.total = asked.total ? await local.messageCount(conversation) : null;
|
|
1077
|
+
}), reading.signal)
|
|
847
1078
|
.then(() => {
|
|
848
1079
|
if (deps.closed() || !view.watchers.has(watcher))
|
|
849
1080
|
return;
|
|
850
1081
|
watcher.ready = true;
|
|
851
|
-
|
|
1082
|
+
const window = view.window ?? [];
|
|
1083
|
+
watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
|
|
852
1084
|
})
|
|
853
1085
|
.catch(() => { });
|
|
854
1086
|
return () => {
|
|
1087
|
+
reading.abort();
|
|
855
1088
|
if (!view.watchers.delete(watcher))
|
|
856
1089
|
return;
|
|
857
1090
|
if (view.watchers.size === 0)
|
|
@@ -859,60 +1092,76 @@ export function createConversationStore(deps) {
|
|
|
859
1092
|
};
|
|
860
1093
|
},
|
|
861
1094
|
watchList(deliver) {
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
if (!deps.closed() && listWatchers.has(deliver))
|
|
865
|
-
deliver(entries());
|
|
866
|
-
});
|
|
1095
|
+
unlisted.add(deliver);
|
|
1096
|
+
learned();
|
|
867
1097
|
return () => {
|
|
1098
|
+
unlisted.delete(deliver);
|
|
868
1099
|
listWatchers.delete(deliver);
|
|
869
1100
|
};
|
|
870
1101
|
},
|
|
871
1102
|
watchAgents(deliver) {
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
deliver(agents());
|
|
876
|
-
});
|
|
1103
|
+
unnamed.add(deliver);
|
|
1104
|
+
learned();
|
|
1105
|
+
readAgents();
|
|
877
1106
|
return () => {
|
|
1107
|
+
unnamed.delete(deliver);
|
|
878
1108
|
agentWatchers.delete(deliver);
|
|
879
1109
|
};
|
|
880
1110
|
},
|
|
1111
|
+
learned,
|
|
881
1112
|
changed(set) {
|
|
882
1113
|
for (const conversation of set.conversations)
|
|
883
1114
|
emit(conversation);
|
|
884
|
-
const declaration = deps.agents();
|
|
885
|
-
if (declaration !== undefined &&
|
|
886
|
-
set.documents.some((change) => change.locator === declaration.locator)) {
|
|
887
|
-
agentsRead = readAgents().catch(() => {
|
|
888
|
-
agentsRead = null;
|
|
889
|
-
});
|
|
890
|
-
}
|
|
891
1115
|
},
|
|
892
1116
|
receive(event) {
|
|
893
1117
|
if (deps.closed())
|
|
894
1118
|
return false;
|
|
895
1119
|
if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
|
|
896
1120
|
if (event.kind === 'list') {
|
|
897
|
-
|
|
1121
|
+
if (event.whole)
|
|
1122
|
+
relisting ??= new Map();
|
|
898
1123
|
for (const entry of event.entries) {
|
|
899
|
-
|
|
900
|
-
|
|
1124
|
+
if (relisting === null)
|
|
1125
|
+
listed(entry);
|
|
1126
|
+
else
|
|
1127
|
+
relisting.set(entry.conversation, { ...entry });
|
|
901
1128
|
}
|
|
1129
|
+
if (event.more)
|
|
1130
|
+
return false;
|
|
1131
|
+
if (relisting !== null) {
|
|
1132
|
+
listing.clear();
|
|
1133
|
+
for (const entry of relisting.values())
|
|
1134
|
+
listed(entry);
|
|
1135
|
+
relisting = null;
|
|
1136
|
+
}
|
|
1137
|
+
listCursor = event.cursor;
|
|
1138
|
+
deps.stream()?.hold(event.cursor);
|
|
902
1139
|
listKnown = true;
|
|
1140
|
+
for (const conversation of replicated) {
|
|
1141
|
+
if (!listing.has(conversation))
|
|
1142
|
+
continue;
|
|
1143
|
+
if (!missed(conversation))
|
|
1144
|
+
behind.delete(conversation);
|
|
1145
|
+
else if (!behind.has(conversation))
|
|
1146
|
+
gap(conversation);
|
|
1147
|
+
}
|
|
1148
|
+
openingEnded();
|
|
903
1149
|
}
|
|
904
1150
|
else if (event.kind === 'listed') {
|
|
905
|
-
|
|
906
|
-
|
|
1151
|
+
listed(event.entry);
|
|
1152
|
+
relisting?.set(event.entry.conversation, { ...event.entry });
|
|
907
1153
|
}
|
|
908
1154
|
else {
|
|
909
1155
|
listing.delete(event.conversation);
|
|
1156
|
+
relisting?.delete(event.conversation);
|
|
910
1157
|
}
|
|
1158
|
+
keepList();
|
|
911
1159
|
emitList();
|
|
912
|
-
return (event.kind !== 'list' &&
|
|
1160
|
+
return ((event.kind !== 'list' &&
|
|
913
1161
|
listKnown &&
|
|
914
1162
|
(listing.size !== replicated.size ||
|
|
915
|
-
[...listing.keys()].some((conversation) => !replicated.has(conversation))))
|
|
1163
|
+
[...listing.keys()].some((conversation) => !replicated.has(conversation)))) ||
|
|
1164
|
+
(listKnown && [...drafts.keys()].some(retiredChat)));
|
|
916
1165
|
}
|
|
917
1166
|
heard(event);
|
|
918
1167
|
return false;
|
|
@@ -926,8 +1175,11 @@ export function createConversationStore(deps) {
|
|
|
926
1175
|
close() {
|
|
927
1176
|
views.clear();
|
|
928
1177
|
lives.clear();
|
|
1178
|
+
unsent.clear();
|
|
929
1179
|
listWatchers.clear();
|
|
1180
|
+
unlisted.clear();
|
|
930
1181
|
agentWatchers.clear();
|
|
1182
|
+
unnamed.clear();
|
|
931
1183
|
},
|
|
932
1184
|
};
|
|
933
1185
|
}
|