@agentshouse/mdruntime 0.1.0-cd26b960b7262 → 0.1.0-cd30853a1b15e
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 +21 -19
- package/dist/acceptance.d.ts +0 -3
- package/dist/acceptance.d.ts.map +1 -1
- package/dist/acceptance.js +72 -158
- package/dist/act-lanes.d.ts +30 -0
- package/dist/act-lanes.d.ts.map +1 -0
- package/dist/act-lanes.js +225 -0
- package/dist/actions.d.ts +8 -2
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +118 -67
- package/dist/adapter.d.ts +81 -56
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +0 -1
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +0 -5
- package/dist/authored.d.ts +88 -0
- package/dist/authored.d.ts.map +1 -0
- package/dist/authored.js +345 -0
- package/dist/bootstrap.d.ts +1 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +6 -19
- package/dist/change-set.d.ts +4 -0
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +6 -0
- package/dist/change.d.ts +4 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +3 -0
- package/dist/chat-types.d.ts +0 -2
- package/dist/chat-types.d.ts.map +1 -1
- package/dist/chat-types.js +0 -2
- package/dist/client.d.ts +10 -19
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +30 -18
- package/dist/collection.d.ts +25 -4
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +19 -2
- package/dist/column.d.ts +5 -0
- package/dist/column.d.ts.map +1 -0
- package/dist/column.js +9 -0
- package/dist/compile.d.ts +2 -0
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +17 -5
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +31 -24
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +1 -1
- package/dist/conversation-live.d.ts +26 -12
- package/dist/conversation-live.d.ts.map +1 -1
- package/dist/conversation-live.js +23 -12
- package/dist/conversation-row.d.ts +8 -1
- package/dist/conversation-row.d.ts.map +1 -1
- package/dist/conversation-row.js +20 -7
- package/dist/conversation.d.ts +32 -10
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +317 -232
- package/dist/declaration.d.ts +10 -0
- package/dist/declaration.d.ts.map +1 -0
- package/dist/declaration.js +24 -0
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +19 -44
- package/dist/durability.d.ts +4 -0
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +4 -0
- package/dist/entry.js +1 -1
- package/dist/expectation.d.ts +25 -14
- package/dist/expectation.d.ts.map +1 -1
- package/dist/expectation.js +51 -20
- package/dist/failure.d.ts +3 -0
- package/dist/failure.d.ts.map +1 -1
- package/dist/failure.js +6 -0
- package/dist/figures.d.ts.map +1 -1
- package/dist/figures.js +4 -2
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +2 -1
- package/dist/host.d.ts +18 -2
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +55 -15
- package/dist/index.d.ts +15 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -7
- package/dist/install.d.ts +10 -6
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +92 -109
- package/dist/lifecycle.d.ts +1 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +54 -40
- package/dist/listing.d.ts +11 -12
- package/dist/listing.d.ts.map +1 -1
- package/dist/listing.js +48 -72
- package/dist/live.d.ts +5 -3
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +74 -31
- package/dist/loaded.d.ts +5 -0
- package/dist/loaded.d.ts.map +1 -0
- package/dist/loaded.js +40 -0
- package/dist/locks.d.ts +2 -4
- package/dist/locks.d.ts.map +1 -1
- package/dist/locks.js +10 -39
- package/dist/lost.d.ts.map +1 -1
- package/dist/lost.js +11 -23
- package/dist/media.d.ts +5 -0
- package/dist/media.d.ts.map +1 -1
- package/dist/media.js +65 -14
- package/dist/memory.d.ts +7 -3
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +56 -26
- package/dist/messages.d.ts +12 -6
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +25 -27
- package/dist/model.d.ts +0 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +0 -5
- package/dist/models.d.ts +4 -8
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +97 -139
- package/dist/observation.d.ts +27 -12
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +173 -69
- package/dist/opened.d.ts +1 -1
- package/dist/opened.d.ts.map +1 -1
- package/dist/opened.js +2 -2
- package/dist/opening.d.ts.map +1 -1
- package/dist/opening.js +5 -9
- package/dist/outcome.d.ts +7 -0
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +23 -2
- package/dist/overlay.d.ts +6 -2
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +37 -24
- package/dist/page-metadata.d.ts +8 -0
- package/dist/page-metadata.d.ts.map +1 -0
- package/dist/page-metadata.js +19 -0
- package/dist/parts.d.ts +3 -59
- package/dist/parts.d.ts.map +1 -1
- package/dist/parts.js +10 -123
- package/dist/preparation.d.ts +27 -6
- package/dist/preparation.d.ts.map +1 -1
- package/dist/preparation.js +154 -66
- package/dist/presence.d.ts +58 -0
- package/dist/presence.d.ts.map +1 -0
- package/dist/presence.js +231 -0
- package/dist/queries.d.ts +2 -2
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +66 -30
- package/dist/questions.d.ts +1 -5
- package/dist/questions.d.ts.map +1 -1
- package/dist/questions.js +1 -7
- package/dist/queue.d.ts +40 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +480 -0
- package/dist/reach.d.ts +2 -3
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +83 -25
- package/dist/react.d.ts +8 -5
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +28 -5
- package/dist/readiness.d.ts +9 -3
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +3 -5
- package/dist/reads.d.ts +4 -2
- package/dist/reads.d.ts.map +1 -1
- package/dist/reads.js +11 -4
- package/dist/recovery.d.ts +3 -1
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +29 -7
- package/dist/refresh.js +2 -2
- package/dist/refusal.d.ts.map +1 -1
- package/dist/refusal.js +5 -5
- package/dist/replica.d.ts +5 -11
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +14 -16
- package/dist/retried.d.ts +30 -0
- package/dist/retried.d.ts.map +1 -0
- package/dist/retried.js +72 -0
- package/dist/room-draft.d.ts +30 -5
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +270 -63
- package/dist/room-drafts.d.ts.map +1 -1
- package/dist/room-drafts.js +8 -5
- package/dist/room-list.d.ts +10 -0
- package/dist/room-list.d.ts.map +1 -0
- package/dist/room-list.js +45 -0
- package/dist/rows.d.ts +2 -1
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +9 -10
- package/dist/runtime.d.ts +9 -18
- package/dist/runtime.d.ts.map +1 -1
- package/dist/save.d.ts +42 -90
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +314 -223
- package/dist/saves.d.ts +2 -40
- package/dist/saves.d.ts.map +1 -1
- package/dist/saves.js +124 -703
- package/dist/seed.d.ts.map +1 -1
- package/dist/seed.js +4 -4
- package/dist/shown.d.ts +7 -5
- package/dist/shown.d.ts.map +1 -1
- package/dist/shown.js +75 -58
- package/dist/socket/lanes.d.ts +12 -4
- package/dist/socket/lanes.d.ts.map +1 -1
- package/dist/socket/lanes.js +34 -8
- package/dist/socket.d.ts +3 -6
- package/dist/socket.d.ts.map +1 -1
- package/dist/socket.js +3 -14
- package/dist/source.d.ts +5 -7
- package/dist/source.d.ts.map +1 -1
- package/dist/source.js +13 -2
- package/dist/store/installs.d.ts +2 -2
- package/dist/store/installs.d.ts.map +1 -1
- package/dist/store/installs.js +10 -4
- package/dist/store/readiness.d.ts +5 -4
- package/dist/store/readiness.d.ts.map +1 -1
- package/dist/store/readiness.js +28 -12
- package/dist/store/records.d.ts +17 -6
- package/dist/store/records.d.ts.map +1 -1
- package/dist/store/records.js +56 -22
- package/dist/store/rooms-shown.d.ts +8 -0
- package/dist/store/rooms-shown.d.ts.map +1 -0
- package/dist/store/rooms-shown.js +13 -0
- package/dist/store.d.ts +23 -14
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +46 -17
- package/dist/stored.d.ts +24 -13
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +80 -29
- package/dist/surface.d.ts +5 -3
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +23 -21
- package/dist/sync.d.ts +1 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +89 -85
- package/dist/tables.d.ts +16 -56
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +86 -327
- package/dist/tallies.d.ts.map +1 -1
- package/dist/tallies.js +1 -2
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +4 -10
- package/dist/wire.d.ts +45 -7
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +15 -4
- package/package.json +3 -2
- package/dist/attempt.d.ts +0 -16
- package/dist/attempt.d.ts.map +0 -1
- package/dist/attempt.js +0 -70
- package/dist/job.d.ts +0 -33
- package/dist/job.d.ts.map +0 -1
- package/dist/job.js +0 -54
- package/dist/log.d.ts +0 -6
- package/dist/log.d.ts.map +0 -1
- package/dist/log.js +0 -14
- package/dist/socket/rooms.d.ts +0 -44
- package/dist/socket/rooms.d.ts.map +0 -1
- package/dist/socket/rooms.js +0 -90
- package/dist/store/fences.d.ts +0 -16
- package/dist/store/fences.d.ts.map +0 -1
- package/dist/store/fences.js +0 -26
package/dist/conversation.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { cursorOf, decodedCursor } from '@agentshouse/mdmodel';
|
|
2
|
+
import { applied, isTurn, standingState, } from '@agentshouse/session-content';
|
|
2
3
|
import { conversationChangeSet } from './change-set.js';
|
|
3
4
|
import { FACTS, QUIET, } from './conversation-live.js';
|
|
4
5
|
import { heldFacts } from './conversation-row.js';
|
|
5
|
-
import { runtimeError } from './failure.js';
|
|
6
|
+
import { runtimeError, uncaught } from './failure.js';
|
|
6
7
|
import { refusedForSpace } from './durability.js';
|
|
7
8
|
import { changeBatchIdentity, chatDraftRevision } from './identity.js';
|
|
8
9
|
import { OFFLINE } from './outcome.js';
|
|
9
|
-
import {
|
|
10
|
+
import { runningLine } from './parts.js';
|
|
11
|
+
import { record, text } from './read.js';
|
|
10
12
|
import { openQuestion, questionAsked } from './questions.js';
|
|
11
13
|
import { untilAnswered } from './recovery.js';
|
|
14
|
+
import { retrying } from './retried.js';
|
|
12
15
|
import { capped, FROM_START, messageOrder, precedes } from './windows.js';
|
|
13
16
|
export function unheldView(conversation) {
|
|
14
17
|
return {
|
|
@@ -21,15 +24,19 @@ export function unheldView(conversation) {
|
|
|
21
24
|
commands: [],
|
|
22
25
|
options: [],
|
|
23
26
|
starting: null,
|
|
24
|
-
|
|
27
|
+
state: null,
|
|
25
28
|
link: null,
|
|
29
|
+
deliveryWaiting: [],
|
|
26
30
|
deliveryUnknown: [],
|
|
27
31
|
chatDraft: null,
|
|
28
|
-
|
|
32
|
+
dismissedSuggestion: null,
|
|
33
|
+
pins: [],
|
|
29
34
|
};
|
|
30
35
|
}
|
|
31
36
|
export const SESSION_STARTED = 'Session started';
|
|
32
37
|
const NEW_CHAT = 'new:';
|
|
38
|
+
const LISTING = 'listing';
|
|
39
|
+
export const CATCHING_UP = 'catching-up';
|
|
33
40
|
export const HISTORY_CONVERSATIONS = 64;
|
|
34
41
|
export function newChat(agent) {
|
|
35
42
|
return `${NEW_CHAT}${agent}`;
|
|
@@ -42,52 +49,15 @@ function creationOf(input) {
|
|
|
42
49
|
}
|
|
43
50
|
function startedTurn(turn, operation) {
|
|
44
51
|
if (operation.op === 'begin')
|
|
45
|
-
return { turn, started: operation.at, parts: []
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
turn,
|
|
49
|
-
started: null,
|
|
50
|
-
parts: [],
|
|
51
|
-
context: { used: operation.used, window: operation.window },
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
return operation.op === 'start' && operation.index === 0
|
|
55
|
-
? { turn, started: null, parts: [structuredClone(operation.part)], context: null }
|
|
56
|
-
: null;
|
|
57
|
-
}
|
|
58
|
-
function changedFields(part, operation) {
|
|
59
|
-
if (operation.op === 'append') {
|
|
60
|
-
return { text: `${String(part.text ?? '')}${operation.text}` };
|
|
61
|
-
}
|
|
62
|
-
if (operation.op === 'status') {
|
|
63
|
-
return { status: operation.status, result: structuredClone(operation.result) };
|
|
64
|
-
}
|
|
65
|
-
if (operation.op === 'count')
|
|
66
|
-
return { count: operation.count };
|
|
67
|
-
return { entries: structuredClone(operation.entries) };
|
|
52
|
+
return { turn, started: operation.at, parts: [] };
|
|
53
|
+
const parts = applied([], operation);
|
|
54
|
+
return parts === null ? null : { turn, started: null, parts };
|
|
68
55
|
}
|
|
69
56
|
function appliedTurn(held, operation) {
|
|
70
57
|
if (operation.op === 'begin')
|
|
71
58
|
return { ...held, started: operation.at };
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
if (operation.op === 'start') {
|
|
76
|
-
if (operation.index !== 0 && operation.index !== held.parts.length)
|
|
77
|
-
return null;
|
|
78
|
-
return {
|
|
79
|
-
...held,
|
|
80
|
-
parts: [...held.parts.slice(0, operation.index), structuredClone(operation.part)],
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
const part = held.parts[operation.index];
|
|
84
|
-
if (typeof part !== 'object' || part === null)
|
|
85
|
-
return null;
|
|
86
|
-
const changed = { ...part, ...changedFields(part, operation) };
|
|
87
|
-
return {
|
|
88
|
-
...held,
|
|
89
|
-
parts: held.parts.map((kept, at) => (at === operation.index ? changed : kept)),
|
|
90
|
-
};
|
|
59
|
+
const parts = applied(held.parts, operation);
|
|
60
|
+
return parts === null ? null : { ...held, parts };
|
|
91
61
|
}
|
|
92
62
|
function windowOf(held, arriving, limit) {
|
|
93
63
|
const byIdentity = new Map(held.map((message) => [message.identity, message]));
|
|
@@ -110,7 +80,7 @@ function frameOf(asked, messages, more, total) {
|
|
|
110
80
|
};
|
|
111
81
|
}
|
|
112
82
|
function newestOf(arriving, from) {
|
|
113
|
-
return arriving.reduce((top, message) => (top === null || message.
|
|
83
|
+
return arriving.reduce((top, message) => (top === null || message.change > top ? message.change : top), from);
|
|
114
84
|
}
|
|
115
85
|
function acceptedMessage(value) {
|
|
116
86
|
if (typeof value !== 'object' || value === null)
|
|
@@ -119,6 +89,7 @@ function acceptedMessage(value) {
|
|
|
119
89
|
if (typeof record.identity !== 'string' ||
|
|
120
90
|
typeof record.turn !== 'string' ||
|
|
121
91
|
typeof record.position !== 'string' ||
|
|
92
|
+
typeof record.change !== 'string' ||
|
|
122
93
|
typeof record.text !== 'string') {
|
|
123
94
|
return null;
|
|
124
95
|
}
|
|
@@ -126,6 +97,7 @@ function acceptedMessage(value) {
|
|
|
126
97
|
identity: record.identity,
|
|
127
98
|
turn: record.turn,
|
|
128
99
|
position: record.position,
|
|
100
|
+
change: record.change,
|
|
129
101
|
text: record.text,
|
|
130
102
|
message: structuredClone(record.message),
|
|
131
103
|
};
|
|
@@ -227,17 +199,17 @@ function withSubmitted(draft, submitted) {
|
|
|
227
199
|
const { submitted: _held, ...unsent } = draft;
|
|
228
200
|
return submitted.length === 0 ? unsent : { ...unsent, submitted };
|
|
229
201
|
}
|
|
230
|
-
function
|
|
231
|
-
return
|
|
202
|
+
export function joinedDraft(first, second) {
|
|
203
|
+
return [first, second].filter((one) => one !== '').join('\n');
|
|
232
204
|
}
|
|
233
205
|
function returned(draft, submission, others) {
|
|
234
206
|
return withSubmitted({
|
|
235
207
|
conversation: draft.conversation,
|
|
236
|
-
|
|
208
|
+
text: joinedDraft(submission.text, draft.text),
|
|
237
209
|
revision: chatDraftRevision(),
|
|
238
210
|
}, others);
|
|
239
211
|
}
|
|
240
|
-
function viewOf(alive, conversation, held, frame, draft, ended,
|
|
212
|
+
function viewOf(alive, conversation, held, frame, { draft, ended, dismissed, pins }) {
|
|
241
213
|
const facts = alive.liveOf(conversation);
|
|
242
214
|
return {
|
|
243
215
|
conversation,
|
|
@@ -255,26 +227,41 @@ function viewOf(alive, conversation, held, frame, draft, ended, marked) {
|
|
|
255
227
|
commands: structuredClone(facts.commands),
|
|
256
228
|
options: structuredClone(facts.options),
|
|
257
229
|
starting: facts.starting,
|
|
258
|
-
|
|
230
|
+
state: sessionOf(facts, held, ended),
|
|
259
231
|
link: facts.link === null ? null : { ...facts.link },
|
|
232
|
+
deliveryWaiting: [...facts.deliveryWaiting],
|
|
260
233
|
deliveryUnknown: [...facts.deliveryUnknown],
|
|
261
234
|
chatDraft: draft === null || unwritten(draft) ? null : { text: draft.text, revision: draft.revision },
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
: jobsOf(marked.filter(({ at }) => ended === null || at > ended), facts.turn),
|
|
235
|
+
dismissedSuggestion: dismissed,
|
|
236
|
+
pins: [...pins],
|
|
265
237
|
};
|
|
266
238
|
}
|
|
239
|
+
function sessionOf(facts, held, ended) {
|
|
240
|
+
if (facts.state !== null)
|
|
241
|
+
return structuredClone(facts.state);
|
|
242
|
+
const saved = [...held]
|
|
243
|
+
.reverse()
|
|
244
|
+
.map(({ message }) => record(message))
|
|
245
|
+
.find(({ content }) => isTurn(content));
|
|
246
|
+
const at = saved === undefined ? null : text(saved, 'accepted_at');
|
|
247
|
+
if (saved === undefined || !isTurn(saved.content) || at === null)
|
|
248
|
+
return null;
|
|
249
|
+
const down = facts.process?.running === false;
|
|
250
|
+
return structuredClone(standingState(saved.content.state, at, down ? at : ended));
|
|
251
|
+
}
|
|
267
252
|
function emit(alive, conversation) {
|
|
268
253
|
if (alive.deps.closed())
|
|
269
254
|
return;
|
|
270
255
|
for (const view of alive.views()) {
|
|
271
|
-
const
|
|
256
|
+
const held = view.held.now();
|
|
257
|
+
const { window } = held;
|
|
272
258
|
if (view.conversation !== conversation || window === null)
|
|
273
259
|
continue;
|
|
274
260
|
const ready = view.ready();
|
|
275
261
|
if (ready.length === 0)
|
|
276
262
|
continue;
|
|
277
|
-
const
|
|
263
|
+
const frame = sharedFrame(view, window);
|
|
264
|
+
const value = viewOf(alive, conversation, window, frame, held);
|
|
278
265
|
for (const watcher of ready)
|
|
279
266
|
watcher.notify(value);
|
|
280
267
|
}
|
|
@@ -285,23 +272,18 @@ function sharedFrame(view, window) {
|
|
|
285
272
|
}
|
|
286
273
|
async function slide(alive, local, conversation, arriving) {
|
|
287
274
|
let count;
|
|
288
|
-
let marked;
|
|
289
275
|
for (const view of alive.views()) {
|
|
290
276
|
const { held } = view;
|
|
291
277
|
const { window, total } = held.now();
|
|
292
278
|
if (view.conversation !== conversation || window === null)
|
|
293
279
|
continue;
|
|
294
280
|
if (arriving === null) {
|
|
295
|
-
held.set({ window: [],
|
|
281
|
+
held.set({ window: [], more: false, total: total === null ? null : 0 });
|
|
296
282
|
continue;
|
|
297
283
|
}
|
|
298
284
|
const joining = arriving.filter((message) => precedes(message, view.asked.before));
|
|
299
285
|
const next = windowOf(window, joining, view.asked.limit);
|
|
300
286
|
held.set({ window: next.messages, more: held.now().more || next.dropped });
|
|
301
|
-
if (arriving.length > 0) {
|
|
302
|
-
marked ??= await local.jobMarkers(conversation);
|
|
303
|
-
held.set({ marked });
|
|
304
|
-
}
|
|
305
287
|
if (held.now().total !== null && arriving.length > 0) {
|
|
306
288
|
count ??= await local.messageCount(conversation);
|
|
307
289
|
held.set({ total: count });
|
|
@@ -325,66 +307,90 @@ function rowOf(alive, conversation) {
|
|
|
325
307
|
};
|
|
326
308
|
}
|
|
327
309
|
async function entries(alive) {
|
|
328
|
-
const
|
|
329
|
-
if (
|
|
310
|
+
const listing = await heldListing(alive);
|
|
311
|
+
if (listing === null)
|
|
312
|
+
return null;
|
|
313
|
+
const list = listing.conversations;
|
|
314
|
+
const agents = new Map(listing.agents.map((agent) => [agent.id, agent]));
|
|
315
|
+
if (list.some((entry) => !agents.has(entry.agent)))
|
|
330
316
|
return null;
|
|
331
317
|
const held = list.length === 0
|
|
332
318
|
? []
|
|
333
319
|
: await alive.deps.durably((local) => local.conversationRows(list.map(({ conversation }) => conversation)));
|
|
334
320
|
const rows = new Map(held.map((one) => [one.conversation, one]));
|
|
335
|
-
return list.map((entry) =>
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
321
|
+
return list.map((entry) => {
|
|
322
|
+
const agent = agents.get(entry.agent);
|
|
323
|
+
return {
|
|
324
|
+
conversation: entry.conversation,
|
|
325
|
+
agent: { identity: agent.id, name: agent.name, retired: agent.retired_at !== null },
|
|
326
|
+
name: entry.name,
|
|
327
|
+
created: entry.created,
|
|
328
|
+
finished: entry.finished,
|
|
329
|
+
outcome: entry.outcome,
|
|
330
|
+
waiting: entry.waiting,
|
|
331
|
+
subject: entry.subject,
|
|
332
|
+
...rowOf(alive, entry.conversation),
|
|
333
|
+
...heldFacts(rows.get(entry.conversation), entry),
|
|
334
|
+
};
|
|
335
|
+
});
|
|
346
336
|
}
|
|
347
|
-
async function
|
|
348
|
-
const
|
|
349
|
-
if (
|
|
350
|
-
return
|
|
351
|
-
return !alive.known() && alive.deps.stream() !== undefined && alive.deps.online()
|
|
337
|
+
async function heldListing(alive) {
|
|
338
|
+
const { conversationList, agentList } = await (await alive.deps.local()).facts();
|
|
339
|
+
if (conversationList !== null && agentList !== null)
|
|
340
|
+
return { conversations: conversationList, agents: agentList };
|
|
341
|
+
return !alive.known() && alive.deps.stream() !== undefined && alive.deps.online()
|
|
342
|
+
? null
|
|
343
|
+
: { conversations: [], agents: [] };
|
|
352
344
|
}
|
|
353
345
|
async function endedIn(alive, conversation) {
|
|
354
|
-
const
|
|
355
|
-
return
|
|
346
|
+
const listing = await heldListing(alive);
|
|
347
|
+
return (listing?.conversations.find((entry) => entry.conversation === conversation)?.processEnded ??
|
|
348
|
+
null);
|
|
349
|
+
}
|
|
350
|
+
function upserted(kept, set, key) {
|
|
351
|
+
const held = new Map(kept.map((one) => [key(one), one]));
|
|
352
|
+
for (const one of set)
|
|
353
|
+
held.set(key(one), { ...one });
|
|
354
|
+
return held;
|
|
356
355
|
}
|
|
357
356
|
function listChanged(alive, change) {
|
|
358
357
|
void alive.deps
|
|
359
358
|
.durably(async (local) => {
|
|
360
|
-
const { conversationList, conversationCursor } = await local.facts();
|
|
361
|
-
const
|
|
362
|
-
const
|
|
363
|
-
for (const entry of change.set) {
|
|
364
|
-
held.set(entry.conversation, { ...entry });
|
|
359
|
+
const { conversationList, agentList, conversationCursor } = await local.facts();
|
|
360
|
+
const held = upserted(change.replacing ? [] : (conversationList ?? []), change.set, (entry) => entry.conversation);
|
|
361
|
+
for (const entry of change.set)
|
|
365
362
|
alive.created(entry.conversation);
|
|
366
|
-
}
|
|
367
363
|
if (change.gone !== null)
|
|
368
364
|
held.delete(change.gone);
|
|
369
365
|
const list = [...held.values()];
|
|
366
|
+
const agents = [
|
|
367
|
+
...upserted(change.replacing ? [] : (agentList ?? []), change.agents, (agent) => agent.id).values(),
|
|
368
|
+
];
|
|
370
369
|
if (JSON.stringify(list) !== JSON.stringify(conversationList)) {
|
|
371
370
|
await local.fact('conversationList', list);
|
|
372
371
|
}
|
|
372
|
+
if (JSON.stringify(agents) !== JSON.stringify(agentList)) {
|
|
373
|
+
await local.fact('agentList', agents);
|
|
374
|
+
}
|
|
373
375
|
if (change.cursor !== undefined) {
|
|
374
376
|
if (change.cursor !== conversationCursor) {
|
|
375
377
|
await local.fact('conversationCursor', change.cursor);
|
|
376
378
|
}
|
|
377
379
|
alive.deps.stream()?.hold(change.cursor);
|
|
378
380
|
}
|
|
379
|
-
return list;
|
|
381
|
+
return { conversations: list, agents };
|
|
380
382
|
})
|
|
381
|
-
.catch(async () =>
|
|
382
|
-
.
|
|
383
|
+
.catch(async () => {
|
|
384
|
+
const { conversationList, agentList } = await (await alive.deps.local()).facts();
|
|
385
|
+
return { conversations: conversationList ?? [], agents: agentList ?? [] };
|
|
386
|
+
})
|
|
387
|
+
.then((listing) => {
|
|
383
388
|
if (alive.deps.closed() || change.paged === true)
|
|
384
389
|
return;
|
|
390
|
+
const list = listing.conversations;
|
|
385
391
|
endedAnew(alive, list);
|
|
386
392
|
if (change.cursor !== undefined) {
|
|
387
|
-
alive.held.set({ listKnown: true });
|
|
393
|
+
alive.held.set({ listKnown: true, openingListed: true });
|
|
388
394
|
settleBehind(alive, list);
|
|
389
395
|
openingEnded(alive);
|
|
390
396
|
alive.deps.settled();
|
|
@@ -392,14 +398,15 @@ function listChanged(alive, change) {
|
|
|
392
398
|
emitList(alive);
|
|
393
399
|
if (!alive.known())
|
|
394
400
|
return;
|
|
395
|
-
retiredDrafts(alive,
|
|
396
|
-
const { replicated } = alive.held.now();
|
|
397
|
-
if (change.cursor === undefined
|
|
398
|
-
|
|
399
|
-
list.some(({ conversation }) => !replicated.has(conversation))
|
|
401
|
+
retiredDrafts(alive, listing);
|
|
402
|
+
const { replicated, openingRead } = alive.held.now();
|
|
403
|
+
if (change.cursor === undefined
|
|
404
|
+
? list.length !== replicated.size ||
|
|
405
|
+
list.some(({ conversation }) => !replicated.has(conversation))
|
|
406
|
+
: openingRead) {
|
|
400
407
|
alive.deps.again();
|
|
401
408
|
}
|
|
402
|
-
},
|
|
409
|
+
}, uncaught);
|
|
403
410
|
}
|
|
404
411
|
function endedAnew(alive, list) {
|
|
405
412
|
const entries = new Map(list.map((entry) => [entry.conversation, entry]));
|
|
@@ -412,20 +419,23 @@ function endedAnew(alive, list) {
|
|
|
412
419
|
emit(alive, view.conversation);
|
|
413
420
|
}
|
|
414
421
|
}
|
|
415
|
-
function
|
|
416
|
-
const reading =
|
|
422
|
+
function shownListing(alive, work) {
|
|
423
|
+
const reading = heldListing(alive);
|
|
417
424
|
alive.inOrder(async () => {
|
|
418
|
-
const
|
|
419
|
-
|
|
425
|
+
const listing = await reading.catch((error) => {
|
|
426
|
+
uncaught(error);
|
|
427
|
+
return null;
|
|
428
|
+
});
|
|
429
|
+
if (listing === null || alive.deps.closed())
|
|
420
430
|
return;
|
|
421
|
-
work(
|
|
431
|
+
work(listing);
|
|
422
432
|
});
|
|
423
433
|
}
|
|
424
434
|
async function told(alive) {
|
|
425
435
|
try {
|
|
426
436
|
while (alive.held.now().listOwed && !alive.deps.closed()) {
|
|
427
437
|
alive.held.set({ listOwed: false });
|
|
428
|
-
const value = await untilAnswered(() => entries(alive), alive.closing);
|
|
438
|
+
const value = await untilAnswered(retrying(alive.deps.retries, 'conversations', LISTING, () => entries(alive)), alive.closing, () => true, alive.deps.recovery);
|
|
429
439
|
if (alive.deps.closed())
|
|
430
440
|
return;
|
|
431
441
|
if (value === null)
|
|
@@ -463,57 +473,48 @@ export function conversationsLearned(alive) {
|
|
|
463
473
|
return;
|
|
464
474
|
if (alive.listeners.waiting())
|
|
465
475
|
tellList(alive);
|
|
466
|
-
if (!alive.agentListeners.waiting())
|
|
476
|
+
if (!alive.agentListeners.waiting() && !alive.listedListeners.waiting())
|
|
467
477
|
return;
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
478
|
+
shownListing(alive, (listing) => {
|
|
479
|
+
alive.agentListeners.welcome(agentsOf(listing));
|
|
480
|
+
alive.listedListeners.welcome(structuredClone(listing.agents));
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
function agentsOf(listing) {
|
|
484
|
+
return listing.agents
|
|
485
|
+
.filter((agent) => agent.retired_at === null)
|
|
486
|
+
.map((agent) => ({
|
|
487
|
+
identity: agent.id,
|
|
488
|
+
name: agent.name,
|
|
489
|
+
cli: agent.cli,
|
|
490
|
+
model: agent.model,
|
|
491
|
+
effort: agent.effort,
|
|
492
|
+
environment: {
|
|
493
|
+
identity: agent.environment,
|
|
494
|
+
name: agent.environment_name,
|
|
495
|
+
state: agent.environment_connected ? 'online' : 'offline',
|
|
496
|
+
},
|
|
497
|
+
}))
|
|
480
498
|
.sort((left, right) => left.name === right.name
|
|
481
499
|
? left.identity.localeCompare(right.identity)
|
|
482
500
|
: left.name.localeCompare(right.name));
|
|
483
501
|
}
|
|
484
|
-
function readAgents(alive) {
|
|
485
|
-
const stream = alive.deps.stream();
|
|
486
|
-
if (stream?.agents === undefined || !alive.deps.online() || alive.held.now().agentsReading)
|
|
487
|
-
return;
|
|
488
|
-
const { agents: read } = stream;
|
|
489
|
-
const { agentListeners } = alive;
|
|
490
|
-
alive.held.set({ agentsReading: true });
|
|
491
|
-
void alive.deps.recovery
|
|
492
|
-
.attempt('agents', async () => {
|
|
493
|
-
if (!alive.deps.online() || (!agentListeners.waiting() && !agentListeners.told())) {
|
|
494
|
-
return 'done';
|
|
495
|
-
}
|
|
496
|
-
const listed = await read();
|
|
497
|
-
if (listed === null)
|
|
498
|
-
return 'done';
|
|
499
|
-
alive.held.set({ listedAgents: listed });
|
|
500
|
-
emitAgents(alive);
|
|
501
|
-
return 'done';
|
|
502
|
-
})
|
|
503
|
-
.finally(() => {
|
|
504
|
-
alive.held.set({ agentsReading: false });
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
502
|
function emitAgents(alive) {
|
|
508
|
-
|
|
503
|
+
const { agentListeners, listedListeners } = alive;
|
|
504
|
+
if (alive.deps.closed() || (!agentListeners.told() && !listedListeners.told()))
|
|
509
505
|
return;
|
|
510
|
-
|
|
511
|
-
const value =
|
|
506
|
+
shownListing(alive, (listing) => {
|
|
507
|
+
const value = agentsOf(listing);
|
|
512
508
|
const shown = JSON.stringify(value);
|
|
513
|
-
if (shown
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
509
|
+
if (shown !== alive.held.now().agentsShown) {
|
|
510
|
+
alive.held.set({ agentsShown: shown });
|
|
511
|
+
agentListeners.tell(value);
|
|
512
|
+
}
|
|
513
|
+
const listed = JSON.stringify(listing.agents);
|
|
514
|
+
if (listed !== alive.held.now().listedShown) {
|
|
515
|
+
alive.held.set({ listedShown: listed });
|
|
516
|
+
listedListeners.tell(structuredClone(listing.agents));
|
|
517
|
+
}
|
|
517
518
|
});
|
|
518
519
|
}
|
|
519
520
|
function setLive(alive, conversation, next) {
|
|
@@ -527,11 +528,11 @@ function setLive(alive, conversation, next) {
|
|
|
527
528
|
after.asked !== before.asked)
|
|
528
529
|
tellSoon(alive);
|
|
529
530
|
}
|
|
530
|
-
function retiredChat(conversation,
|
|
531
|
+
function retiredChat(conversation, listing) {
|
|
531
532
|
if (!conversation.startsWith(NEW_CHAT))
|
|
532
533
|
return false;
|
|
533
534
|
const agent = conversation.slice(NEW_CHAT.length);
|
|
534
|
-
return
|
|
535
|
+
return listing.agents.some((one) => one.id === agent && one.retired_at !== null);
|
|
535
536
|
}
|
|
536
537
|
function live(alive, conversation, change) {
|
|
537
538
|
alive.factsHeard(conversation, FACTS.filter((one) => one in change));
|
|
@@ -550,8 +551,9 @@ function openingEnded(alive) {
|
|
|
550
551
|
commands: facts.has('commands') ? QUIET.commands : held.commands,
|
|
551
552
|
options: facts.has('options') ? QUIET.options : held.options,
|
|
552
553
|
starting: facts.has('starting') ? QUIET.starting : held.starting,
|
|
553
|
-
|
|
554
|
+
state: facts.has('state') ? QUIET.state : held.state,
|
|
554
555
|
link: facts.has('link') ? QUIET.link : held.link,
|
|
556
|
+
deliveryWaiting: facts.has('deliveryWaiting') ? QUIET.deliveryWaiting : held.deliveryWaiting,
|
|
555
557
|
deliveryUnknown: facts.has('deliveryUnknown') ? QUIET.deliveryUnknown : held.deliveryUnknown,
|
|
556
558
|
});
|
|
557
559
|
emit(alive, conversation);
|
|
@@ -583,19 +585,13 @@ function handReplicated(alive) {
|
|
|
583
585
|
}
|
|
584
586
|
async function remove(alive, leaving) {
|
|
585
587
|
await alive.deps.durably(async (local) => {
|
|
586
|
-
const kept = new Set(
|
|
587
|
-
...(await local.conversations(leaving)).map(({ conversation }) => conversation),
|
|
588
|
-
...(await local.chatDrafts())
|
|
589
|
-
.filter(({ conversation }) => leaving.includes(conversation))
|
|
590
|
-
.map(({ conversation }) => conversation),
|
|
591
|
-
]);
|
|
592
|
-
await local.dropConversations(leaving);
|
|
588
|
+
const kept = new Set(await local.dropConversations(leaving));
|
|
593
589
|
const changed = [];
|
|
594
590
|
for (const conversation of leaving) {
|
|
595
591
|
if (kept.has(conversation) || alive.liveHeld(conversation) !== undefined) {
|
|
596
592
|
changed.push(conversation);
|
|
597
593
|
}
|
|
598
|
-
|
|
594
|
+
heldInViews(alive, conversation, { draft: null, dismissed: null });
|
|
599
595
|
alive.forget(conversation);
|
|
600
596
|
setLive(alive, conversation, null);
|
|
601
597
|
await slide(alive, local, conversation, null);
|
|
@@ -624,7 +620,7 @@ async function readsOf(local, conversations) {
|
|
|
624
620
|
return new Map((await local.conversations(conversations)).map((held) => [held.conversation, held]));
|
|
625
621
|
}
|
|
626
622
|
function retiredDrafts(alive, list) {
|
|
627
|
-
if (!list.some((
|
|
623
|
+
if (!list.agents.some((agent) => agent.retired_at !== null))
|
|
628
624
|
return;
|
|
629
625
|
void alive.deps
|
|
630
626
|
.local()
|
|
@@ -634,7 +630,7 @@ function retiredDrafts(alive, list) {
|
|
|
634
630
|
held.some(({ conversation }) => retiredChat(conversation, list))) {
|
|
635
631
|
alive.deps.again();
|
|
636
632
|
}
|
|
637
|
-
},
|
|
633
|
+
}, uncaught);
|
|
638
634
|
}
|
|
639
635
|
function settleBehind(alive, list) {
|
|
640
636
|
void alive.deps
|
|
@@ -660,21 +656,19 @@ function settleBehind(alive, list) {
|
|
|
660
656
|
alive.deps.settled();
|
|
661
657
|
if (owed)
|
|
662
658
|
alive.deps.again();
|
|
663
|
-
},
|
|
659
|
+
}, uncaught);
|
|
664
660
|
}
|
|
665
661
|
function keep(alive, conversation, message) {
|
|
666
662
|
return alive.deps.durably(async (local) => {
|
|
667
663
|
if (!alive.held.now().replicated.has(conversation))
|
|
668
664
|
return;
|
|
669
665
|
const read = (await readsOf(local, [conversation])).get(conversation)?.read;
|
|
670
|
-
const advances = read !== undefined &&
|
|
671
|
-
!alive.behind(conversation) &&
|
|
672
|
-
(read === null || message.position > read);
|
|
666
|
+
const advances = read !== undefined && !alive.behind(conversation) && (read === null || message.change > read);
|
|
673
667
|
await sealed(alive, local, conversation, new Set([message.turn]));
|
|
674
668
|
await local.keepMessages({
|
|
675
669
|
conversation,
|
|
676
670
|
messages: [message],
|
|
677
|
-
...(advances ? { read: { at: message.
|
|
671
|
+
...(advances ? { read: { at: message.change, partial: false } } : {}),
|
|
678
672
|
});
|
|
679
673
|
sealTurns(alive, conversation, new Set([message.turn]));
|
|
680
674
|
await slide(alive, local, conversation, [message]);
|
|
@@ -786,8 +780,8 @@ function heard(alive, event) {
|
|
|
786
780
|
live(alive, conversation, { starting: event.step });
|
|
787
781
|
return;
|
|
788
782
|
}
|
|
789
|
-
if (event.kind === '
|
|
790
|
-
live(alive, conversation, {
|
|
783
|
+
if (event.kind === 'state') {
|
|
784
|
+
live(alive, conversation, { state: structuredClone(event.state) });
|
|
791
785
|
return;
|
|
792
786
|
}
|
|
793
787
|
if (event.kind === 'link') {
|
|
@@ -795,13 +789,16 @@ function heard(alive, event) {
|
|
|
795
789
|
return;
|
|
796
790
|
}
|
|
797
791
|
if (event.kind === 'deliveries') {
|
|
798
|
-
live(alive, conversation, {
|
|
792
|
+
live(alive, conversation, {
|
|
793
|
+
deliveryWaiting: [...event.waiting],
|
|
794
|
+
deliveryUnknown: [...event.unknown],
|
|
795
|
+
});
|
|
799
796
|
return;
|
|
800
797
|
}
|
|
801
798
|
const arriving = acceptedMessage(event.message);
|
|
802
799
|
if (arriving === null)
|
|
803
800
|
return;
|
|
804
|
-
alive.heardAt(conversation, arriving.
|
|
801
|
+
alive.heardAt(conversation, arriving.change);
|
|
805
802
|
if (alive.held.now().replicated.has(conversation)) {
|
|
806
803
|
if (alive.deps.full()) {
|
|
807
804
|
alive.gapOpened(conversation);
|
|
@@ -810,6 +807,8 @@ function heard(alive, event) {
|
|
|
810
807
|
void keep(alive, conversation, arriving).catch((error) => {
|
|
811
808
|
if (refusedForSpace(error))
|
|
812
809
|
alive.gapOpened(conversation);
|
|
810
|
+
else
|
|
811
|
+
uncaught(error);
|
|
813
812
|
});
|
|
814
813
|
return;
|
|
815
814
|
}
|
|
@@ -817,19 +816,27 @@ function heard(alive, event) {
|
|
|
817
816
|
emit(alive, conversation);
|
|
818
817
|
}
|
|
819
818
|
async function kept(alive, local, conversation, following) {
|
|
820
|
-
const
|
|
821
|
-
if (
|
|
819
|
+
const draft = following === null || empty(following) ? null : following;
|
|
820
|
+
if (draft === null)
|
|
822
821
|
await local.dropChatDraft(conversation);
|
|
823
822
|
else
|
|
824
|
-
await local.keepChatDrafts([
|
|
825
|
-
|
|
823
|
+
await local.keepChatDrafts([draft]);
|
|
824
|
+
heldInViews(alive, conversation, { draft });
|
|
826
825
|
}
|
|
827
|
-
function
|
|
826
|
+
function heldInViews(alive, conversation, held) {
|
|
828
827
|
for (const view of alive.views()) {
|
|
829
828
|
if (view.conversation === conversation)
|
|
830
|
-
view.held.set(
|
|
829
|
+
view.held.set(held);
|
|
831
830
|
}
|
|
832
831
|
}
|
|
832
|
+
async function factOf(local, name) {
|
|
833
|
+
return Object.fromEntries((await local.conversationFacts())
|
|
834
|
+
.filter((fact) => fact.name === name)
|
|
835
|
+
.map(({ conversation, value }) => [conversation, value]));
|
|
836
|
+
}
|
|
837
|
+
async function dismissedIn(local, conversation) {
|
|
838
|
+
return (await factOf(local, 'dismissedSuggestion'))[conversation] ?? null;
|
|
839
|
+
}
|
|
833
840
|
async function draftAt(local, conversation) {
|
|
834
841
|
return (await local.chatDraft(conversation)) ?? undefined;
|
|
835
842
|
}
|
|
@@ -860,9 +867,9 @@ function moved(alive, agent, operation, to) {
|
|
|
860
867
|
: [{ ...unsent, conversation: to }];
|
|
861
868
|
const left = { ...(taken.length === 0 ? unsent : blank), moves: [...earlier, operation] };
|
|
862
869
|
await local.keepChatDrafts([left, ...taken]);
|
|
863
|
-
|
|
870
|
+
heldInViews(alive, from, { draft: left });
|
|
864
871
|
for (const one of taken)
|
|
865
|
-
|
|
872
|
+
heldInViews(alive, to, { draft: one });
|
|
866
873
|
alive.deps.publish(conversationChangeSet([from, to]));
|
|
867
874
|
});
|
|
868
875
|
}
|
|
@@ -893,13 +900,10 @@ async function submitted(alive, conversation, operation, draft) {
|
|
|
893
900
|
});
|
|
894
901
|
return submission;
|
|
895
902
|
}
|
|
896
|
-
async function dispatched(alive, conversation, submission) {
|
|
903
|
+
async function dispatched(alive, conversation, submission, resent = false) {
|
|
897
904
|
const dispatch = alive.sendingBegan(submission.operation);
|
|
898
905
|
try {
|
|
899
|
-
const result = await invoke(alive, 'conversations/send', conversation, {
|
|
900
|
-
operation_id: submission.operation,
|
|
901
|
-
text: submission.text,
|
|
902
|
-
});
|
|
906
|
+
const result = await invoke(alive, 'conversations/send', conversation, submission.operation, { text: submission.text }, { step: 'sending', instance: `message ${submission.operation}` });
|
|
903
907
|
dispatch.set({ answered: result.kind !== 'transport-uncertainty' });
|
|
904
908
|
if (result.kind === 'revoked-authorization')
|
|
905
909
|
return result;
|
|
@@ -907,6 +911,7 @@ async function dispatched(alive, conversation, submission) {
|
|
|
907
911
|
alive.deps.unanswered();
|
|
908
912
|
return result;
|
|
909
913
|
}
|
|
914
|
+
alive.deps.heard(submission.operation, resent);
|
|
910
915
|
const turn = answered(result, 'message_id');
|
|
911
916
|
await withDraft(alive, conversation, async (current, local) => {
|
|
912
917
|
const held = current?.submitted?.find((one) => one.operation === submission.operation);
|
|
@@ -926,8 +931,8 @@ async function dispatched(alive, conversation, submission) {
|
|
|
926
931
|
alive.sendingEnded(submission.operation, dispatch);
|
|
927
932
|
}
|
|
928
933
|
}
|
|
929
|
-
function invoke(alive, name, conversation, extra = {
|
|
930
|
-
return alive.deps.call(name, { conversation_id: conversation, ...extra });
|
|
934
|
+
function invoke(alive, name, conversation, operation, extra = {}, attempt) {
|
|
935
|
+
return alive.deps.call(name, { conversation_id: conversation, operation_id: operation, ...extra }, attempt);
|
|
931
936
|
}
|
|
932
937
|
export function conversationSurface(alive) {
|
|
933
938
|
return {
|
|
@@ -948,8 +953,12 @@ export function conversationSurface(alive) {
|
|
|
948
953
|
return alive.deps.durably(async (local) => {
|
|
949
954
|
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before, phrase);
|
|
950
955
|
const total = asked.total ? await local.messageCount(conversation, phrase) : null;
|
|
951
|
-
|
|
952
|
-
|
|
956
|
+
return structuredClone(viewOf(alive, conversation, messages, frameOf(asked, messages, more, total), {
|
|
957
|
+
draft: (await local.chatDraft(conversation)) ?? null,
|
|
958
|
+
ended: await endedIn(alive, conversation),
|
|
959
|
+
dismissed: await dismissedIn(local, conversation),
|
|
960
|
+
pins: (await local.facts()).chatPins,
|
|
961
|
+
}));
|
|
953
962
|
});
|
|
954
963
|
},
|
|
955
964
|
async create(input) {
|
|
@@ -957,7 +966,7 @@ export function conversationSurface(alive) {
|
|
|
957
966
|
const conversation = answered(result, 'conversation_id');
|
|
958
967
|
if (conversation === null)
|
|
959
968
|
return result;
|
|
960
|
-
const listed = (await
|
|
969
|
+
const listed = (await heldListing(alive))?.conversations ?? [];
|
|
961
970
|
if (!listed.some((entry) => entry.conversation === conversation)) {
|
|
962
971
|
alive.creating(conversation);
|
|
963
972
|
}
|
|
@@ -986,40 +995,90 @@ export function conversationSurface(alive) {
|
|
|
986
995
|
}
|
|
987
996
|
return OFFLINE;
|
|
988
997
|
},
|
|
989
|
-
answer(conversation, answer) {
|
|
990
|
-
return invoke(alive, 'conversations/interaction/answer', conversation,
|
|
991
|
-
|
|
992
|
-
|
|
998
|
+
answer(conversation, answer, operation) {
|
|
999
|
+
return invoke(alive, 'conversations/interaction/answer', conversation, operation, answer);
|
|
1000
|
+
},
|
|
1001
|
+
interrupt(conversation, operation) {
|
|
1002
|
+
return invoke(alive, 'conversations/interrupt', conversation, operation);
|
|
1003
|
+
},
|
|
1004
|
+
kill(conversation, operation) {
|
|
1005
|
+
return invoke(alive, 'conversations/kill', conversation, operation);
|
|
1006
|
+
},
|
|
1007
|
+
delete(conversation, operation) {
|
|
1008
|
+
return invoke(alive, 'conversations/delete', conversation, operation);
|
|
1009
|
+
},
|
|
1010
|
+
takeOut(conversation, message, operation) {
|
|
1011
|
+
return invoke(alive, 'conversations/take-out', conversation, operation, {
|
|
1012
|
+
message_id: message,
|
|
1013
|
+
});
|
|
1014
|
+
},
|
|
1015
|
+
decide({ request, room, message }, decision, operation) {
|
|
1016
|
+
return alive.deps.call('agent-functions/decide', {
|
|
1017
|
+
room_ref: room,
|
|
1018
|
+
request,
|
|
1019
|
+
...(message === undefined ? {} : { message }),
|
|
1020
|
+
decision,
|
|
1021
|
+
operation_id: operation,
|
|
1022
|
+
});
|
|
1023
|
+
},
|
|
1024
|
+
rename(conversation, name, operation) {
|
|
1025
|
+
return invoke(alive, 'conversations/rename', conversation, operation, { name });
|
|
1026
|
+
},
|
|
1027
|
+
option(conversation, option, value, operation) {
|
|
1028
|
+
return invoke(alive, 'conversations/option/set', conversation, operation, { option, value });
|
|
1029
|
+
},
|
|
1030
|
+
retry(conversation, message, operation) {
|
|
1031
|
+
return invoke(alive, 'conversations/retry', conversation, operation, {
|
|
1032
|
+
message_id: message,
|
|
993
1033
|
});
|
|
994
1034
|
},
|
|
995
|
-
|
|
996
|
-
|
|
1035
|
+
async pin(figures) {
|
|
1036
|
+
const pins = [...figures];
|
|
1037
|
+
await alive.deps.durably((local) => local.fact('chatPins', pins));
|
|
1038
|
+
const views = [...alive.views()];
|
|
1039
|
+
for (const view of views)
|
|
1040
|
+
view.held.set({ pins });
|
|
1041
|
+
for (const conversation of new Set(views.map((view) => view.conversation))) {
|
|
1042
|
+
emit(alive, conversation);
|
|
1043
|
+
}
|
|
997
1044
|
},
|
|
998
|
-
|
|
999
|
-
|
|
1045
|
+
async openTurn(conversation, turn) {
|
|
1046
|
+
await alive.deps.durably((local) => local.keepConversationFact({ conversation, name: 'openedTurn', value: turn }));
|
|
1047
|
+
tellList(alive);
|
|
1000
1048
|
},
|
|
1001
|
-
|
|
1002
|
-
return
|
|
1049
|
+
dismissSuggestion(conversation, suggestion) {
|
|
1050
|
+
return alive.deps.durably(async (local) => {
|
|
1051
|
+
if ((await dismissedIn(local, conversation)) === suggestion)
|
|
1052
|
+
return;
|
|
1053
|
+
await local.keepConversationFact({
|
|
1054
|
+
conversation,
|
|
1055
|
+
name: 'dismissedSuggestion',
|
|
1056
|
+
value: suggestion,
|
|
1057
|
+
});
|
|
1058
|
+
heldInViews(alive, conversation, { dismissed: suggestion });
|
|
1059
|
+
alive.deps.publish(conversationChangeSet([conversation]));
|
|
1060
|
+
});
|
|
1003
1061
|
},
|
|
1004
1062
|
};
|
|
1005
1063
|
}
|
|
1064
|
+
function rewritten(alive, conversation, text, revision = chatDraftRevision()) {
|
|
1065
|
+
return withDraft(alive, conversation, (current) => ({
|
|
1066
|
+
conversation,
|
|
1067
|
+
text: text(current?.text ?? ''),
|
|
1068
|
+
revision,
|
|
1069
|
+
...(current === undefined ? {} : pending(current)),
|
|
1070
|
+
}));
|
|
1071
|
+
}
|
|
1006
1072
|
export function chatDraftSurface(alive) {
|
|
1007
1073
|
return {
|
|
1008
|
-
write(conversation, draft, revision
|
|
1009
|
-
return
|
|
1010
|
-
conversation,
|
|
1011
|
-
text: draft.text,
|
|
1012
|
-
revision,
|
|
1013
|
-
...(current === undefined ? {} : pending(current)),
|
|
1014
|
-
}));
|
|
1074
|
+
write(conversation, draft, revision) {
|
|
1075
|
+
return rewritten(alive, conversation, () => draft.text, revision);
|
|
1015
1076
|
},
|
|
1016
1077
|
prepend(conversation, draft) {
|
|
1017
|
-
return
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
...(current === undefined ? {} : pending(current)),
|
|
1022
|
-
}));
|
|
1078
|
+
return rewritten(alive, conversation, (held) => joinedDraft(draft.text, held));
|
|
1079
|
+
},
|
|
1080
|
+
append(conversation, draft) {
|
|
1081
|
+
return rewritten(alive, conversation, (held) => joinedDraft(held, draft.text));
|
|
1023
1082
|
},
|
|
1024
1083
|
discard(conversation) {
|
|
1025
1084
|
return withDraft(alive, conversation, (current) => current === undefined
|
|
@@ -1029,31 +1088,34 @@ export function chatDraftSurface(alive) {
|
|
|
1029
1088
|
};
|
|
1030
1089
|
}
|
|
1031
1090
|
export async function conversationsOpened(alive, local) {
|
|
1032
|
-
const { conversationList, conversationCursor } = await local.facts();
|
|
1033
|
-
if (conversationList === null || alive.known())
|
|
1091
|
+
const { conversationList, agentList, conversationCursor } = await local.facts();
|
|
1092
|
+
if (conversationList === null || agentList === null || alive.known())
|
|
1034
1093
|
return;
|
|
1035
1094
|
alive.deps.stream()?.hold(conversationCursor);
|
|
1036
1095
|
conversationsLearned(alive);
|
|
1037
1096
|
}
|
|
1097
|
+
export function reconcileOpening(alive) {
|
|
1098
|
+
alive.held.set({ openingRead: true });
|
|
1099
|
+
return reconcileConversations(alive);
|
|
1100
|
+
}
|
|
1038
1101
|
export async function reconcileConversations(alive) {
|
|
1039
1102
|
if (!alive.known())
|
|
1040
1103
|
return;
|
|
1041
1104
|
const local = await alive.deps.local();
|
|
1042
|
-
const [held,
|
|
1105
|
+
const [held, stored, { conversationList, agentList }] = await Promise.all([
|
|
1043
1106
|
local.conversations(),
|
|
1044
|
-
local.
|
|
1107
|
+
local.storedConversations(),
|
|
1045
1108
|
local.facts(),
|
|
1046
1109
|
]);
|
|
1047
1110
|
const list = conversationList ?? [];
|
|
1111
|
+
const listing = { conversations: list, agents: agentList ?? [] };
|
|
1048
1112
|
const entries = new Map(list.map((entry) => [entry.conversation, entry]));
|
|
1049
1113
|
const named = new Set(entries.keys());
|
|
1050
1114
|
const reads = new Map(held.map((one) => [one.conversation, one]));
|
|
1051
1115
|
const { replicated } = alive.held.now();
|
|
1052
|
-
const leaving = [
|
|
1053
|
-
...new Set([...replicated, ...reads.keys(), ...kept.map(({ conversation }) => conversation)]),
|
|
1054
|
-
].filter((conversation) => !named.has(conversation) &&
|
|
1116
|
+
const leaving = [...new Set([...replicated, ...stored])].filter((conversation) => !named.has(conversation) &&
|
|
1055
1117
|
!alive.isCreating(conversation) &&
|
|
1056
|
-
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation,
|
|
1118
|
+
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation, listing)));
|
|
1057
1119
|
for (const conversation of named) {
|
|
1058
1120
|
if (!replicated.has(conversation) &&
|
|
1059
1121
|
missed(alive, conversation, reads.get(conversation), entries.get(conversation))) {
|
|
@@ -1088,6 +1150,17 @@ export async function catchUpConversations(alive, progress) {
|
|
|
1088
1150
|
if (!alive.current())
|
|
1089
1151
|
progress.complete = false;
|
|
1090
1152
|
}
|
|
1153
|
+
export async function holdsSubmission(alive, operation) {
|
|
1154
|
+
if (alive.sending(operation))
|
|
1155
|
+
return true;
|
|
1156
|
+
const local = await alive.deps.local();
|
|
1157
|
+
for (const { conversation } of await local.chatDrafts()) {
|
|
1158
|
+
const held = (await local.chatDraft(conversation))?.submitted;
|
|
1159
|
+
if (held?.some((one) => one.operation === operation && one.turn === null) === true)
|
|
1160
|
+
return true;
|
|
1161
|
+
}
|
|
1162
|
+
return false;
|
|
1163
|
+
}
|
|
1091
1164
|
export async function resendConversations(alive, progress) {
|
|
1092
1165
|
const local = await alive.deps.local();
|
|
1093
1166
|
const tried = new Set();
|
|
@@ -1099,7 +1172,7 @@ export async function resendConversations(alive, progress) {
|
|
|
1099
1172
|
if (alive.deps.closed())
|
|
1100
1173
|
return;
|
|
1101
1174
|
tried.add(submission.operation);
|
|
1102
|
-
const result = await dispatched(alive, conversation, submission);
|
|
1175
|
+
const result = await dispatched(alive, conversation, submission, true);
|
|
1103
1176
|
if (result.kind === 'revoked-authorization')
|
|
1104
1177
|
return;
|
|
1105
1178
|
if (result.kind === 'transport-uncertainty') {
|
|
@@ -1119,23 +1192,25 @@ export function watchConversation(alive, conversation, options, deliver) {
|
|
|
1119
1192
|
const { view, watcher } = alive.viewJoined(key, conversation, asked, deliver);
|
|
1120
1193
|
const { held } = view;
|
|
1121
1194
|
const reading = new AbortController();
|
|
1122
|
-
void untilAnswered(() => alive.deps.durably(async (local) => {
|
|
1195
|
+
void untilAnswered(retrying(alive.deps.retries, 'reading', `window ${key}`, () => alive.deps.durably(async (local) => {
|
|
1123
1196
|
if (held.now().window !== null)
|
|
1124
1197
|
return;
|
|
1125
1198
|
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
|
|
1126
1199
|
held.set({ draft: (await local.chatDraft(conversation)) ?? null });
|
|
1127
1200
|
held.set({ ended: await endedIn(alive, conversation) });
|
|
1201
|
+
held.set({ dismissed: await dismissedIn(local, conversation) });
|
|
1202
|
+
held.set({ pins: (await local.facts()).chatPins });
|
|
1128
1203
|
held.set({ window: messages });
|
|
1129
|
-
held.set({ marked: await local.jobMarkers(conversation) });
|
|
1130
1204
|
held.set({ more });
|
|
1131
1205
|
held.set({ total: asked.total ? await local.messageCount(conversation) : null });
|
|
1132
|
-
}), reading.signal)
|
|
1206
|
+
})), reading.signal)
|
|
1133
1207
|
.then(() => {
|
|
1134
1208
|
if (alive.deps.closed() || !view.admit(watcher))
|
|
1135
1209
|
return;
|
|
1136
|
-
const
|
|
1137
|
-
const shown = window ?? [];
|
|
1138
|
-
|
|
1210
|
+
const now = held.now();
|
|
1211
|
+
const shown = now.window ?? [];
|
|
1212
|
+
const frame = sharedFrame(view, shown);
|
|
1213
|
+
watcher.notify(viewOf(alive, conversation, shown, frame, now));
|
|
1139
1214
|
})
|
|
1140
1215
|
.catch(() => { });
|
|
1141
1216
|
return () => {
|
|
@@ -1151,9 +1226,13 @@ export function watchConversationList(alive, deliver) {
|
|
|
1151
1226
|
export function watchConversationAgents(alive, deliver) {
|
|
1152
1227
|
alive.agentListeners.join(deliver);
|
|
1153
1228
|
conversationsLearned(alive);
|
|
1154
|
-
readAgents(alive);
|
|
1155
1229
|
return () => alive.agentListeners.leave(deliver);
|
|
1156
1230
|
}
|
|
1231
|
+
export function watchListedAgents(alive, deliver) {
|
|
1232
|
+
alive.listedListeners.join(deliver);
|
|
1233
|
+
conversationsLearned(alive);
|
|
1234
|
+
return () => alive.listedListeners.leave(deliver);
|
|
1235
|
+
}
|
|
1157
1236
|
export function conversationsChanged(alive, set) {
|
|
1158
1237
|
for (const conversation of set.conversations)
|
|
1159
1238
|
emit(alive, conversation);
|
|
@@ -1165,26 +1244,32 @@ export function receiveConversationEvent(alive, event) {
|
|
|
1165
1244
|
if (event.whole)
|
|
1166
1245
|
alive.relistBegun();
|
|
1167
1246
|
if (alive.relisting())
|
|
1168
|
-
alive.relistHeard(event.entries);
|
|
1247
|
+
alive.relistHeard(event.entries, event.agents);
|
|
1169
1248
|
if (event.more && alive.relisting())
|
|
1170
1249
|
return;
|
|
1171
1250
|
const whole = alive.relistTaken();
|
|
1172
1251
|
listChanged(alive, {
|
|
1173
1252
|
replacing: whole !== null,
|
|
1174
|
-
set: whole === null ? event.entries : [...whole.values()],
|
|
1253
|
+
set: whole === null ? event.entries : [...whole.conversations.values()],
|
|
1254
|
+
agents: whole === null ? event.agents : [...whole.agents.values()],
|
|
1175
1255
|
gone: null,
|
|
1176
1256
|
...(event.more ? { paged: true } : { cursor: event.cursor }),
|
|
1177
1257
|
});
|
|
1178
1258
|
return;
|
|
1179
1259
|
}
|
|
1180
1260
|
if (event.kind === 'listed') {
|
|
1181
|
-
alive.relistHeard([event.entry]);
|
|
1182
|
-
listChanged(alive, { replacing: false, set: [event.entry], gone: null });
|
|
1261
|
+
alive.relistHeard([event.entry], []);
|
|
1262
|
+
listChanged(alive, { replacing: false, set: [event.entry], agents: [], gone: null });
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
if (event.kind === 'agent') {
|
|
1266
|
+
alive.relistHeard([], [event.agent]);
|
|
1267
|
+
listChanged(alive, { replacing: false, set: [], agents: [event.agent], gone: null });
|
|
1183
1268
|
return;
|
|
1184
1269
|
}
|
|
1185
1270
|
if (event.kind === 'unlisted') {
|
|
1186
1271
|
alive.relistDropped(event.conversation);
|
|
1187
|
-
listChanged(alive, { replacing: false, set: [], gone: event.conversation });
|
|
1272
|
+
listChanged(alive, { replacing: false, set: [], agents: [], gone: event.conversation });
|
|
1188
1273
|
return;
|
|
1189
1274
|
}
|
|
1190
1275
|
heard(alive, event);
|