@agentshouse/mdruntime 0.1.0-c0d767f8f799a → 0.1.0-c0ffc146f48dd
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 +117 -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 +25 -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 +16 -8
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +86 -9
- 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 +27 -15
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +2976 -1372
- package/dist/conversation.d.ts +24 -11
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +411 -194
- 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 +10 -6
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +199 -101
- 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 +32 -2
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +41 -6
- package/dist/reach.d.ts +13 -4
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +209 -74
- 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 -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 +18 -15
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +136 -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 +22 -4
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +154 -67
- 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 +23 -42
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +28 -182
- 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 +34 -5
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +10 -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 -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/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/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,
|
|
@@ -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
|
}
|
|
@@ -209,21 +256,28 @@ export function createConversationStore(deps) {
|
|
|
209
256
|
const catching = new Map();
|
|
210
257
|
const gaps = new Map();
|
|
211
258
|
const lives = new Map();
|
|
259
|
+
const unsent = new Map();
|
|
212
260
|
const views = new Map();
|
|
213
261
|
const listing = new Map();
|
|
262
|
+
let relisting = null;
|
|
263
|
+
let listCursor = null;
|
|
214
264
|
const creations = new Set();
|
|
215
265
|
const listWatchers = new Set();
|
|
266
|
+
const unlisted = new Set();
|
|
216
267
|
let listKnown = false;
|
|
268
|
+
let listHeld = false;
|
|
269
|
+
let listKept = false;
|
|
217
270
|
const agentWatchers = new Set();
|
|
218
|
-
|
|
219
|
-
let
|
|
220
|
-
let
|
|
271
|
+
const unnamed = new Set();
|
|
272
|
+
let agentsShown = null;
|
|
273
|
+
let listedAgents = null;
|
|
274
|
+
let agentsReading = false;
|
|
221
275
|
let replicated = new Set();
|
|
222
276
|
let handedReplicated;
|
|
223
277
|
function liveOf(conversation) {
|
|
224
278
|
return lives.get(conversation) ?? QUIET;
|
|
225
279
|
}
|
|
226
|
-
function viewOf(conversation, held) {
|
|
280
|
+
function viewOf(conversation, held, frame) {
|
|
227
281
|
const draft = drafts.get(conversation);
|
|
228
282
|
const facts = liveOf(conversation);
|
|
229
283
|
return {
|
|
@@ -234,14 +288,19 @@ export function createConversationStore(deps) {
|
|
|
234
288
|
text,
|
|
235
289
|
message,
|
|
236
290
|
})),
|
|
291
|
+
...frame,
|
|
237
292
|
turn: structuredClone(facts.turn),
|
|
238
293
|
questions: structuredClone(facts.questions),
|
|
239
294
|
process: facts.process === null ? null : { ...facts.process },
|
|
240
295
|
commands: structuredClone(facts.commands),
|
|
241
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],
|
|
242
301
|
chatDraft: draft === undefined || unwritten(draft)
|
|
243
302
|
? null
|
|
244
|
-
: { text: draft.text,
|
|
303
|
+
: { text: draft.text, revision: draft.revision },
|
|
245
304
|
};
|
|
246
305
|
}
|
|
247
306
|
function emit(conversation) {
|
|
@@ -253,21 +312,36 @@ export function createConversationStore(deps) {
|
|
|
253
312
|
const ready = [...view.watchers].filter((watcher) => watcher.ready);
|
|
254
313
|
if (ready.length === 0)
|
|
255
314
|
continue;
|
|
256
|
-
const value = viewOf(conversation, view.window);
|
|
315
|
+
const value = viewOf(conversation, view.window, sharedFrame(view, view.window));
|
|
257
316
|
for (const watcher of ready)
|
|
258
317
|
watcher.notify(value);
|
|
259
318
|
}
|
|
260
319
|
}
|
|
261
|
-
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;
|
|
262
325
|
for (const view of views.values()) {
|
|
263
326
|
if (view.conversation !== conversation || view.window === null)
|
|
264
327
|
continue;
|
|
265
|
-
|
|
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
|
+
}
|
|
266
343
|
}
|
|
267
344
|
}
|
|
268
|
-
function changesOf(conversation, arriving) {
|
|
269
|
-
return arriving.map(({ identity, text }) => ({ conversation, identity, text }));
|
|
270
|
-
}
|
|
271
345
|
function stateOf(conversation) {
|
|
272
346
|
const facts = liveOf(conversation);
|
|
273
347
|
if (facts.questions.length > 0)
|
|
@@ -277,34 +351,110 @@ export function createConversationStore(deps) {
|
|
|
277
351
|
function entries() {
|
|
278
352
|
return [...listing.values()].map((entry) => ({
|
|
279
353
|
conversation: entry.conversation,
|
|
280
|
-
agent: { identity: entry.agent, name:
|
|
354
|
+
agent: { identity: entry.agent, name: entry.agentName },
|
|
281
355
|
name: entry.name,
|
|
282
356
|
created: entry.created,
|
|
283
357
|
finished: entry.finished,
|
|
284
358
|
outcome: entry.outcome,
|
|
359
|
+
waiting: entry.waiting,
|
|
285
360
|
state: stateOf(entry.conversation),
|
|
286
361
|
started: lives.get(entry.conversation)?.turn?.started ?? null,
|
|
287
362
|
}));
|
|
288
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
|
+
}
|
|
289
386
|
function emitList() {
|
|
290
|
-
if (deps.closed()
|
|
387
|
+
if (deps.closed())
|
|
291
388
|
return;
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
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
|
+
}
|
|
295
418
|
}
|
|
296
419
|
function agents() {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
.
|
|
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 }))
|
|
300
429
|
.sort((left, right) => left.name === right.name
|
|
301
430
|
? left.identity.localeCompare(right.identity)
|
|
302
431
|
: left.name.localeCompare(right.name));
|
|
303
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
|
+
}
|
|
304
450
|
function emitAgents() {
|
|
305
451
|
if (deps.closed() || agentWatchers.size === 0)
|
|
306
452
|
return;
|
|
307
453
|
const value = agents();
|
|
454
|
+
const shown = JSON.stringify(value);
|
|
455
|
+
if (shown === agentsShown)
|
|
456
|
+
return;
|
|
457
|
+
agentsShown = shown;
|
|
308
458
|
for (const watcher of agentWatchers)
|
|
309
459
|
watcher(value);
|
|
310
460
|
}
|
|
@@ -320,53 +470,42 @@ export function createConversationStore(deps) {
|
|
|
320
470
|
if (stateOf(conversation) !== before || next?.turn?.started !== began)
|
|
321
471
|
emitList();
|
|
322
472
|
}
|
|
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
473
|
function retiredChat(conversation) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
agentsRead = null;
|
|
363
|
-
});
|
|
364
|
-
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);
|
|
365
478
|
}
|
|
366
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);
|
|
367
483
|
setLive(conversation, { ...liveOf(conversation), ...change });
|
|
368
484
|
emit(conversation);
|
|
369
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
|
+
}
|
|
370
509
|
function relayed(conversation, turn, operation) {
|
|
371
510
|
const held = liveOf(conversation).turn;
|
|
372
511
|
const next = held?.turn === turn ? appliedTurn(held, operation) : startedTurn(turn, operation);
|
|
@@ -393,12 +532,6 @@ export function createConversationStore(deps) {
|
|
|
393
532
|
}
|
|
394
533
|
async function remove(leaving) {
|
|
395
534
|
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
535
|
await local.dropConversations(leaving);
|
|
403
536
|
const changed = [];
|
|
404
537
|
for (const conversation of leaving) {
|
|
@@ -410,13 +543,28 @@ export function createConversationStore(deps) {
|
|
|
410
543
|
partials.delete(conversation);
|
|
411
544
|
drafts.delete(conversation);
|
|
412
545
|
behind.delete(conversation);
|
|
546
|
+
unsent.delete(conversation);
|
|
413
547
|
setLive(conversation, null);
|
|
414
|
-
slide(conversation, null);
|
|
548
|
+
await slide(local, conversation, null);
|
|
415
549
|
}
|
|
416
550
|
if (changed.length > 0)
|
|
417
|
-
deps.publish(conversationChangeSet(
|
|
551
|
+
deps.publish(conversationChangeSet(changed));
|
|
418
552
|
});
|
|
419
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
|
+
}
|
|
420
568
|
function gap(conversation) {
|
|
421
569
|
gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
|
|
422
570
|
behind.add(conversation);
|
|
@@ -439,18 +587,19 @@ export function createConversationStore(deps) {
|
|
|
439
587
|
if (advances)
|
|
440
588
|
reads.set(conversation, message.position);
|
|
441
589
|
sealTurns(conversation, new Set([message.turn]));
|
|
442
|
-
slide(conversation, [message]);
|
|
443
|
-
deps.publish(conversationChangeSet(
|
|
590
|
+
await slide(local, conversation, [message]);
|
|
591
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
444
592
|
});
|
|
445
593
|
}
|
|
446
594
|
async function readHistories(conversations, ended) {
|
|
447
595
|
const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
|
|
448
|
-
let
|
|
596
|
+
let waiting = conversations.map((conversation) => ({
|
|
449
597
|
conversation,
|
|
450
598
|
after: reads.get(conversation) ?? undefined,
|
|
451
599
|
}));
|
|
452
|
-
while (
|
|
453
|
-
const
|
|
600
|
+
while (waiting.length > 0) {
|
|
601
|
+
const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
|
|
602
|
+
const result = await deps.history({
|
|
454
603
|
conversations: asking.map(({ conversation, after }) => ({
|
|
455
604
|
conversation,
|
|
456
605
|
...(after === undefined ? {} : { after }),
|
|
@@ -473,7 +622,7 @@ export function createConversationStore(deps) {
|
|
|
473
622
|
if (!following.some((next) => next.conversation === conversation))
|
|
474
623
|
ended(conversation);
|
|
475
624
|
}
|
|
476
|
-
|
|
625
|
+
waiting = [...waiting.slice(HISTORY_CONVERSATIONS), ...following];
|
|
477
626
|
}
|
|
478
627
|
return null;
|
|
479
628
|
}
|
|
@@ -514,9 +663,9 @@ export function createConversationStore(deps) {
|
|
|
514
663
|
sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
|
|
515
664
|
if (whole && (gaps.get(conversation) ?? 0) === generation)
|
|
516
665
|
behind.delete(conversation);
|
|
517
|
-
slide(conversation, arriving);
|
|
666
|
+
await slide(local, conversation, arriving);
|
|
518
667
|
if (arriving.length > 0 || sealing || whole) {
|
|
519
|
-
deps.publish(conversationChangeSet(
|
|
668
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
520
669
|
}
|
|
521
670
|
});
|
|
522
671
|
}
|
|
@@ -525,11 +674,8 @@ export function createConversationStore(deps) {
|
|
|
525
674
|
if (event.kind === 'gap') {
|
|
526
675
|
if (replicated.has(conversation))
|
|
527
676
|
gap(conversation);
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
return;
|
|
531
|
-
setLive(conversation, { ...QUIET, turn: held.turn });
|
|
532
|
-
emit(conversation);
|
|
677
|
+
if (lives.has(conversation))
|
|
678
|
+
unsent.set(conversation, new Set(FACTS));
|
|
533
679
|
return;
|
|
534
680
|
}
|
|
535
681
|
if (event.kind === 'turn') {
|
|
@@ -552,11 +698,35 @@ export function createConversationStore(deps) {
|
|
|
552
698
|
live(conversation, { options: structuredClone(event.options) });
|
|
553
699
|
return;
|
|
554
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
|
+
}
|
|
555
717
|
const arriving = acceptedMessage(event.message);
|
|
556
718
|
if (arriving === null)
|
|
557
719
|
return;
|
|
720
|
+
noted(conversation, arriving);
|
|
558
721
|
if (replicated.has(conversation)) {
|
|
559
|
-
|
|
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
|
+
});
|
|
560
730
|
return;
|
|
561
731
|
}
|
|
562
732
|
if (sealTurns(conversation, new Set([arriving.turn])))
|
|
@@ -591,7 +761,6 @@ export function createConversationStore(deps) {
|
|
|
591
761
|
const blank = {
|
|
592
762
|
conversation: from,
|
|
593
763
|
text: '',
|
|
594
|
-
files: [],
|
|
595
764
|
revision: chatDraftRevision(),
|
|
596
765
|
};
|
|
597
766
|
const { moves: _answered, ...unsent } = held ?? blank;
|
|
@@ -601,7 +770,7 @@ export function createConversationStore(deps) {
|
|
|
601
770
|
drafts.set(from, left);
|
|
602
771
|
for (const one of taken)
|
|
603
772
|
drafts.set(to, one);
|
|
604
|
-
deps.publish(conversationChangeSet([
|
|
773
|
+
deps.publish(conversationChangeSet([from, to]));
|
|
605
774
|
});
|
|
606
775
|
}
|
|
607
776
|
function withDraft(conversation, next) {
|
|
@@ -610,7 +779,7 @@ export function createConversationStore(deps) {
|
|
|
610
779
|
if (following === undefined)
|
|
611
780
|
return;
|
|
612
781
|
await kept(local, conversation, following);
|
|
613
|
-
deps.publish(conversationChangeSet([
|
|
782
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
614
783
|
});
|
|
615
784
|
}
|
|
616
785
|
async function submitted(conversation, operation, draft) {
|
|
@@ -622,17 +791,12 @@ export function createConversationStore(deps) {
|
|
|
622
791
|
if (submission !== undefined)
|
|
623
792
|
return undefined;
|
|
624
793
|
const said = draft ?? current;
|
|
625
|
-
submission = {
|
|
626
|
-
operation,
|
|
627
|
-
text: said?.text ?? '',
|
|
628
|
-
files: [...(said?.files ?? [])],
|
|
629
|
-
turn: null,
|
|
630
|
-
};
|
|
794
|
+
submission = { operation, text: said?.text ?? '', turn: null };
|
|
631
795
|
return {
|
|
632
796
|
conversation,
|
|
633
797
|
...(draft === undefined || current === undefined
|
|
634
|
-
? { text: '',
|
|
635
|
-
: { text: current.text,
|
|
798
|
+
? { text: '', revision: chatDraftRevision() }
|
|
799
|
+
: { text: current.text, revision: current.revision }),
|
|
636
800
|
submitted: [...(current?.submitted ?? []), submission],
|
|
637
801
|
};
|
|
638
802
|
});
|
|
@@ -647,22 +811,21 @@ export function createConversationStore(deps) {
|
|
|
647
811
|
});
|
|
648
812
|
}
|
|
649
813
|
async function dispatched(conversation, submission) {
|
|
650
|
-
const dispatch = {};
|
|
814
|
+
const dispatch = { answered: false };
|
|
651
815
|
sending.set(submission.operation, dispatch);
|
|
652
816
|
try {
|
|
653
|
-
const
|
|
654
|
-
const result = await invoke('conversation.send', conversation, {
|
|
655
|
-
text: submission.text,
|
|
656
|
-
files,
|
|
817
|
+
const result = await invoke('conversations/send', conversation, {
|
|
657
818
|
operation_id: submission.operation,
|
|
819
|
+
text: submission.text,
|
|
658
820
|
});
|
|
821
|
+
dispatch.answered = result.kind !== 'transport-uncertainty';
|
|
659
822
|
if (result.kind === 'revoked-authorization')
|
|
660
823
|
return result;
|
|
661
824
|
if (result.kind !== 'action' && result.retryable) {
|
|
662
825
|
deps.unanswered();
|
|
663
826
|
return result;
|
|
664
827
|
}
|
|
665
|
-
const turn = answered(result, '
|
|
828
|
+
const turn = answered(result, 'message_id');
|
|
666
829
|
await withDraft(conversation, async (current, local) => {
|
|
667
830
|
const held = current?.submitted?.find((one) => one.operation === submission.operation);
|
|
668
831
|
if (current === undefined || held === undefined || held.turn !== null)
|
|
@@ -682,21 +845,32 @@ export function createConversationStore(deps) {
|
|
|
682
845
|
sending.delete(submission.operation);
|
|
683
846
|
}
|
|
684
847
|
}
|
|
685
|
-
function invoke(name, conversation, extra = {}) {
|
|
686
|
-
return deps.call(name, { conversation, ...extra });
|
|
848
|
+
function invoke(name, conversation, extra = { operation_id: changeBatchIdentity() }) {
|
|
849
|
+
return deps.call(name, { conversation_id: conversation, ...extra });
|
|
687
850
|
}
|
|
688
851
|
const surface = {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
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
|
+
});
|
|
692
862
|
},
|
|
693
863
|
async read(conversation, options = {}) {
|
|
694
|
-
const
|
|
695
|
-
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
|
+
});
|
|
696
870
|
},
|
|
697
871
|
async create(input) {
|
|
698
|
-
const result = await deps.call('
|
|
699
|
-
const conversation = answered(result, '
|
|
872
|
+
const result = await deps.call('conversations/create', input);
|
|
873
|
+
const conversation = answered(result, 'conversation_id');
|
|
700
874
|
if (conversation === null)
|
|
701
875
|
return result;
|
|
702
876
|
if (!listing.has(conversation))
|
|
@@ -707,8 +881,17 @@ export function createConversationStore(deps) {
|
|
|
707
881
|
return result;
|
|
708
882
|
},
|
|
709
883
|
async send(conversation, operation = changeBatchIdentity(), draft) {
|
|
884
|
+
if (deps.full())
|
|
885
|
+
return DURABLE_WRITE;
|
|
710
886
|
if (deps.online()) {
|
|
711
|
-
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;
|
|
712
895
|
if (!earlier || !deps.expired(operation))
|
|
713
896
|
return dispatched(conversation, submission);
|
|
714
897
|
await forgotten(conversation, submission);
|
|
@@ -717,33 +900,24 @@ export function createConversationStore(deps) {
|
|
|
717
900
|
if (draft !== undefined) {
|
|
718
901
|
await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
|
|
719
902
|
? undefined
|
|
720
|
-
: returned(current ?? { conversation, text: '',
|
|
903
|
+
: returned(current ?? { conversation, text: '', revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
|
|
721
904
|
}
|
|
722
905
|
return OFFLINE;
|
|
723
906
|
},
|
|
724
907
|
answer(conversation, answer) {
|
|
725
|
-
return invoke('
|
|
908
|
+
return invoke('conversations/interaction/answer', conversation, {
|
|
909
|
+
operation_id: changeBatchIdentity(),
|
|
910
|
+
...answer,
|
|
911
|
+
});
|
|
726
912
|
},
|
|
727
913
|
interrupt(conversation) {
|
|
728
|
-
return invoke('
|
|
914
|
+
return invoke('conversations/interrupt', conversation);
|
|
729
915
|
},
|
|
730
916
|
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;
|
|
917
|
+
return invoke('conversations/kill', conversation);
|
|
744
918
|
},
|
|
745
919
|
delete(conversation) {
|
|
746
|
-
return invoke('
|
|
920
|
+
return invoke('conversations/delete', conversation);
|
|
747
921
|
},
|
|
748
922
|
};
|
|
749
923
|
const chatDraft = {
|
|
@@ -751,27 +925,36 @@ export function createConversationStore(deps) {
|
|
|
751
925
|
return withDraft(conversation, (current) => ({
|
|
752
926
|
conversation,
|
|
753
927
|
text: draft.text,
|
|
754
|
-
files: [...draft.files],
|
|
755
928
|
revision,
|
|
756
929
|
...(current === undefined ? {} : pending(current)),
|
|
757
930
|
}));
|
|
758
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
|
+
},
|
|
759
940
|
discard(conversation) {
|
|
760
941
|
return withDraft(conversation, (current) => current === undefined
|
|
761
942
|
? undefined
|
|
762
|
-
: {
|
|
763
|
-
conversation,
|
|
764
|
-
text: '',
|
|
765
|
-
files: [],
|
|
766
|
-
revision: chatDraftRevision(),
|
|
767
|
-
...pending(current),
|
|
768
|
-
});
|
|
943
|
+
: { conversation, text: '', revision: chatDraftRevision(), ...pending(current) });
|
|
769
944
|
},
|
|
770
945
|
};
|
|
771
946
|
return {
|
|
772
947
|
surface,
|
|
773
948
|
chatDraft,
|
|
774
|
-
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
|
+
}
|
|
775
958
|
stored.clear();
|
|
776
959
|
reads.clear();
|
|
777
960
|
partials.clear();
|
|
@@ -785,6 +968,8 @@ export function createConversationStore(deps) {
|
|
|
785
968
|
drafts.clear();
|
|
786
969
|
for (const record of kept)
|
|
787
970
|
drafts.set(record.conversation, record);
|
|
971
|
+
if (listHeld)
|
|
972
|
+
learned();
|
|
788
973
|
},
|
|
789
974
|
async reconcile() {
|
|
790
975
|
if (!listKnown)
|
|
@@ -796,7 +981,7 @@ export function createConversationStore(deps) {
|
|
|
796
981
|
!creations.has(conversation) &&
|
|
797
982
|
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
|
|
798
983
|
for (const conversation of named) {
|
|
799
|
-
if (!replicated.has(conversation))
|
|
984
|
+
if (!replicated.has(conversation) && missed(conversation))
|
|
800
985
|
behind.add(conversation);
|
|
801
986
|
}
|
|
802
987
|
replicated = named;
|
|
@@ -805,6 +990,11 @@ export function createConversationStore(deps) {
|
|
|
805
990
|
await remove(leaving);
|
|
806
991
|
},
|
|
807
992
|
async catchUp(progress) {
|
|
993
|
+
if (deps.full()) {
|
|
994
|
+
if (behind.size > 0)
|
|
995
|
+
progress.complete = false;
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
808
998
|
const owed = (conversation) => behind.has(conversation) && replicated.has(conversation);
|
|
809
999
|
const read = async (conversations) => {
|
|
810
1000
|
if (conversations.length === 0 || deps.closed())
|
|
@@ -853,40 +1043,48 @@ export function createConversationStore(deps) {
|
|
|
853
1043
|
known() {
|
|
854
1044
|
return listKnown;
|
|
855
1045
|
},
|
|
856
|
-
reconnected() {
|
|
857
|
-
for (const conversation of replicated)
|
|
858
|
-
gap(conversation);
|
|
859
|
-
},
|
|
860
1046
|
reopened() {
|
|
861
|
-
|
|
1047
|
+
relisting = null;
|
|
1048
|
+
for (const [operation, dispatch] of sending)
|
|
1049
|
+
if (!dispatch.answered)
|
|
1050
|
+
sending.delete(operation);
|
|
862
1051
|
},
|
|
863
1052
|
lost() {
|
|
864
1053
|
catching.clear();
|
|
865
1054
|
},
|
|
866
1055
|
watch(conversation, options, deliver) {
|
|
867
|
-
const
|
|
868
|
-
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]);
|
|
869
1058
|
const view = views.get(key) ?? {
|
|
870
1059
|
conversation,
|
|
871
|
-
|
|
1060
|
+
asked,
|
|
872
1061
|
watchers: new Set(),
|
|
873
1062
|
window: null,
|
|
1063
|
+
more: false,
|
|
1064
|
+
total: null,
|
|
874
1065
|
};
|
|
875
1066
|
views.set(key, view);
|
|
876
1067
|
const watcher = { notify: deliver, ready: false };
|
|
877
1068
|
view.watchers.add(watcher);
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
view.window
|
|
881
|
-
|
|
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)
|
|
882
1078
|
.then(() => {
|
|
883
1079
|
if (deps.closed() || !view.watchers.has(watcher))
|
|
884
1080
|
return;
|
|
885
1081
|
watcher.ready = true;
|
|
886
|
-
|
|
1082
|
+
const window = view.window ?? [];
|
|
1083
|
+
watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
|
|
887
1084
|
})
|
|
888
1085
|
.catch(() => { });
|
|
889
1086
|
return () => {
|
|
1087
|
+
reading.abort();
|
|
890
1088
|
if (!view.watchers.delete(watcher))
|
|
891
1089
|
return;
|
|
892
1090
|
if (view.watchers.size === 0)
|
|
@@ -894,60 +1092,76 @@ export function createConversationStore(deps) {
|
|
|
894
1092
|
};
|
|
895
1093
|
},
|
|
896
1094
|
watchList(deliver) {
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
if (!deps.closed() && listWatchers.has(deliver))
|
|
900
|
-
deliver(entries());
|
|
901
|
-
});
|
|
1095
|
+
unlisted.add(deliver);
|
|
1096
|
+
learned();
|
|
902
1097
|
return () => {
|
|
1098
|
+
unlisted.delete(deliver);
|
|
903
1099
|
listWatchers.delete(deliver);
|
|
904
1100
|
};
|
|
905
1101
|
},
|
|
906
1102
|
watchAgents(deliver) {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
deliver(agents());
|
|
911
|
-
});
|
|
1103
|
+
unnamed.add(deliver);
|
|
1104
|
+
learned();
|
|
1105
|
+
readAgents();
|
|
912
1106
|
return () => {
|
|
1107
|
+
unnamed.delete(deliver);
|
|
913
1108
|
agentWatchers.delete(deliver);
|
|
914
1109
|
};
|
|
915
1110
|
},
|
|
1111
|
+
learned,
|
|
916
1112
|
changed(set) {
|
|
917
1113
|
for (const conversation of set.conversations)
|
|
918
1114
|
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
1115
|
},
|
|
927
1116
|
receive(event) {
|
|
928
1117
|
if (deps.closed())
|
|
929
1118
|
return false;
|
|
930
1119
|
if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
|
|
931
1120
|
if (event.kind === 'list') {
|
|
932
|
-
|
|
1121
|
+
if (event.whole)
|
|
1122
|
+
relisting ??= new Map();
|
|
933
1123
|
for (const entry of event.entries) {
|
|
934
|
-
|
|
935
|
-
|
|
1124
|
+
if (relisting === null)
|
|
1125
|
+
listed(entry);
|
|
1126
|
+
else
|
|
1127
|
+
relisting.set(entry.conversation, { ...entry });
|
|
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;
|
|
936
1136
|
}
|
|
1137
|
+
listCursor = event.cursor;
|
|
1138
|
+
deps.stream()?.hold(event.cursor);
|
|
937
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();
|
|
938
1149
|
}
|
|
939
1150
|
else if (event.kind === 'listed') {
|
|
940
|
-
|
|
941
|
-
|
|
1151
|
+
listed(event.entry);
|
|
1152
|
+
relisting?.set(event.entry.conversation, { ...event.entry });
|
|
942
1153
|
}
|
|
943
1154
|
else {
|
|
944
1155
|
listing.delete(event.conversation);
|
|
1156
|
+
relisting?.delete(event.conversation);
|
|
945
1157
|
}
|
|
1158
|
+
keepList();
|
|
946
1159
|
emitList();
|
|
947
|
-
return (event.kind !== 'list' &&
|
|
1160
|
+
return ((event.kind !== 'list' &&
|
|
948
1161
|
listKnown &&
|
|
949
1162
|
(listing.size !== replicated.size ||
|
|
950
|
-
[...listing.keys()].some((conversation) => !replicated.has(conversation))))
|
|
1163
|
+
[...listing.keys()].some((conversation) => !replicated.has(conversation)))) ||
|
|
1164
|
+
(listKnown && [...drafts.keys()].some(retiredChat)));
|
|
951
1165
|
}
|
|
952
1166
|
heard(event);
|
|
953
1167
|
return false;
|
|
@@ -961,8 +1175,11 @@ export function createConversationStore(deps) {
|
|
|
961
1175
|
close() {
|
|
962
1176
|
views.clear();
|
|
963
1177
|
lives.clear();
|
|
1178
|
+
unsent.clear();
|
|
964
1179
|
listWatchers.clear();
|
|
1180
|
+
unlisted.clear();
|
|
965
1181
|
agentWatchers.clear();
|
|
1182
|
+
unnamed.clear();
|
|
966
1183
|
},
|
|
967
1184
|
};
|
|
968
1185
|
}
|