@agentshouse/mdruntime 0.1.0-alpha.9 → 0.1.0-c0105dbd22806
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/AGENTS.md +299 -0
- package/README.md +54 -0
- package/dist/adapter.d.ts +246 -40
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +1 -10
- package/dist/attached-model.d.ts +11 -0
- package/dist/attached-model.d.ts.map +1 -0
- package/dist/attached-model.js +47 -0
- package/dist/attempt.d.ts +16 -0
- package/dist/attempt.d.ts.map +1 -0
- package/dist/attempt.js +70 -0
- package/dist/binary.d.ts +23 -0
- package/dist/binary.d.ts.map +1 -0
- package/dist/binary.js +8 -0
- package/dist/bootstrap.d.ts +17 -7
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +97 -41
- package/dist/change-set.d.ts +33 -0
- package/dist/change-set.d.ts.map +1 -0
- package/dist/change-set.js +90 -0
- package/dist/change.d.ts +24 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +31 -5
- package/dist/chat/acts.d.ts +34 -0
- package/dist/chat/acts.d.ts.map +1 -0
- package/dist/chat/acts.js +36 -0
- package/dist/chat/context.d.ts +3 -0
- package/dist/chat/context.d.ts.map +1 -0
- package/dist/chat/context.js +10 -0
- package/dist/chat/input.d.ts +9 -0
- package/dist/chat/input.d.ts.map +1 -0
- package/dist/chat/input.js +99 -0
- package/dist/chat/kit.d.ts +60 -0
- package/dist/chat/kit.d.ts.map +1 -0
- package/dist/chat/kit.js +123 -0
- package/dist/chat/lasting.d.ts +6 -0
- package/dist/chat/lasting.d.ts.map +1 -0
- package/dist/chat/lasting.js +19 -0
- package/dist/chat/list.d.ts +26 -0
- package/dist/chat/list.d.ts.map +1 -0
- package/dist/chat/list.js +62 -0
- package/dist/chat/markdown.d.ts +20 -0
- package/dist/chat/markdown.d.ts.map +1 -0
- package/dist/chat/markdown.js +188 -0
- package/dist/chat/message.d.ts +6 -0
- package/dist/chat/message.d.ts.map +1 -0
- package/dist/chat/message.js +192 -0
- package/dist/chat/messages.d.ts +21 -0
- package/dist/chat/messages.d.ts.map +1 -0
- package/dist/chat/messages.js +151 -0
- package/dist/chat/name.d.ts +6 -0
- package/dist/chat/name.d.ts.map +1 -0
- package/dist/chat/name.js +37 -0
- package/dist/chat/opened.d.ts +11 -0
- package/dist/chat/opened.d.ts.map +1 -0
- package/dist/chat/opened.js +17 -0
- package/dist/chat/overlay.d.ts +31 -0
- package/dist/chat/overlay.d.ts.map +1 -0
- package/dist/chat/overlay.js +97 -0
- package/dist/chat/parts.d.ts +57 -0
- package/dist/chat/parts.d.ts.map +1 -0
- package/dist/chat/parts.js +119 -0
- package/dist/chat/process.d.ts +20 -0
- package/dist/chat/process.d.ts.map +1 -0
- package/dist/chat/process.js +32 -0
- package/dist/chat/provider.d.ts +63 -0
- package/dist/chat/provider.d.ts.map +1 -0
- package/dist/chat/provider.js +645 -0
- package/dist/chat/question.d.ts +9 -0
- package/dist/chat/question.d.ts.map +1 -0
- package/dist/chat/question.js +80 -0
- package/dist/chat/questions.d.ts +31 -0
- package/dist/chat/questions.d.ts.map +1 -0
- package/dist/chat/questions.js +111 -0
- package/dist/chat/read.d.ts +5 -0
- package/dist/chat/read.d.ts.map +1 -0
- package/dist/chat/read.js +13 -0
- package/dist/chat/ready.d.ts +33 -0
- package/dist/chat/ready.d.ts.map +1 -0
- package/dist/chat/ready.js +91 -0
- package/dist/chat/style.d.ts +4 -0
- package/dist/chat/style.d.ts.map +1 -0
- package/dist/chat/style.js +302 -0
- package/dist/chat.d.ts +14 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +8 -0
- package/dist/client.d.ts +49 -33
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +164 -183
- package/dist/collection.d.ts +9 -17
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +83 -89
- package/dist/connection.d.ts +66 -48
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +1964 -1173
- package/dist/conversation.d.ts +65 -24
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +675 -334
- package/dist/durability.js +6 -6
- package/dist/entry.d.ts +17 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +156 -0
- package/dist/expectation.d.ts +21 -0
- package/dist/expectation.d.ts.map +1 -0
- package/dist/expectation.js +70 -0
- package/dist/failure.d.ts +13 -0
- package/dist/failure.d.ts.map +1 -0
- package/dist/failure.js +19 -0
- package/dist/fence.d.ts +6 -0
- package/dist/fence.d.ts.map +1 -0
- package/dist/fence.js +10 -0
- package/dist/file.d.ts +2 -2
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +48 -71
- package/dist/host.d.ts +15 -3
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +109 -127
- package/dist/identity.d.ts +4 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +22 -9
- package/dist/index.d.ts +17 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/log.js +5 -6
- package/dist/memory.d.ts +19 -8
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +304 -216
- package/dist/model.d.ts +16 -30
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +21 -21
- package/dist/observation.d.ts +27 -0
- package/dist/observation.d.ts.map +1 -0
- package/dist/observation.js +95 -0
- package/dist/open.d.ts +2 -0
- package/dist/open.d.ts.map +1 -0
- package/dist/open.js +1 -0
- package/dist/outcome.d.ts +37 -31
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +36 -18
- package/dist/overlay.d.ts +22 -4
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +236 -54
- package/dist/paths.d.ts +0 -2
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +6 -29
- package/dist/query.d.ts +219 -10
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +462 -15
- package/dist/reach.d.ts +42 -0
- package/dist/reach.d.ts.map +1 -0
- package/dist/reach.js +294 -0
- package/dist/react.d.ts +41 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +64 -0
- package/dist/read-side.d.ts +18 -0
- package/dist/read-side.d.ts.map +1 -0
- package/dist/read-side.js +440 -0
- package/dist/refresh.d.ts +15 -5
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +64 -41
- package/dist/replica.d.ts +28 -5
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +249 -40
- package/dist/room-draft.d.ts +144 -0
- package/dist/room-draft.d.ts.map +1 -0
- package/dist/room-draft.js +438 -0
- package/dist/rows.d.ts +46 -0
- package/dist/rows.d.ts.map +1 -0
- package/dist/rows.js +145 -0
- package/dist/save.d.ts +33 -5
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +630 -366
- package/dist/search.d.ts +62 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +125 -0
- package/dist/status.d.ts +6 -8
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +8 -20
- package/dist/store.d.ts +89 -34
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +513 -132
- package/dist/transfer.d.ts +5 -0
- package/dist/transfer.d.ts.map +1 -1
- package/dist/transfer.js +1 -5
- package/dist/wire.d.ts +59 -19
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +88 -105
- package/package.json +34 -16
- package/dist/adapter.js.map +0 -1
- package/dist/bootstrap.js.map +0 -1
- package/dist/change.js.map +0 -1
- package/dist/client.js.map +0 -1
- package/dist/collection.js.map +0 -1
- package/dist/connection.js.map +0 -1
- package/dist/conversation.js.map +0 -1
- package/dist/durability.js.map +0 -1
- package/dist/file.js.map +0 -1
- package/dist/host.js.map +0 -1
- package/dist/identity.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/log.js.map +0 -1
- package/dist/memory.js.map +0 -1
- package/dist/model.js.map +0 -1
- package/dist/outcome.js.map +0 -1
- package/dist/overlay.js.map +0 -1
- package/dist/paths.js.map +0 -1
- package/dist/query.js.map +0 -1
- package/dist/refresh.js.map +0 -1
- package/dist/replica.js.map +0 -1
- package/dist/save.js.map +0 -1
- package/dist/status.js.map +0 -1
- package/dist/store.js.map +0 -1
- package/dist/transfer.js.map +0 -1
- package/dist/wire.js.map +0 -1
package/dist/conversation.js
CHANGED
|
@@ -1,401 +1,742 @@
|
|
|
1
|
+
import { conversationChangeSet } from './change-set.js';
|
|
2
|
+
import { runtimeError } from './failure.js';
|
|
1
3
|
import { declaredFile } from './file.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const answer = result.result;
|
|
12
|
-
if (typeof answer !== 'object' || answer === null) return null;
|
|
13
|
-
const record = answer;
|
|
14
|
-
if (!Array.isArray(record.messages)) return null;
|
|
15
|
-
const messages = [];
|
|
16
|
-
for (const entry of record.messages){
|
|
17
|
-
const carried = carriedMessage(entry);
|
|
18
|
-
if (carried !== null) messages.push(carried);
|
|
19
|
-
}
|
|
4
|
+
import { chatDraftRevision } from './identity.js';
|
|
5
|
+
const QUIET = {
|
|
6
|
+
turn: null,
|
|
7
|
+
questions: [],
|
|
8
|
+
process: null,
|
|
9
|
+
commands: [],
|
|
10
|
+
options: [],
|
|
11
|
+
};
|
|
12
|
+
export function unheldView(conversation) {
|
|
20
13
|
return {
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
conversation,
|
|
15
|
+
messages: [],
|
|
16
|
+
turn: null,
|
|
17
|
+
questions: [],
|
|
18
|
+
process: null,
|
|
19
|
+
commands: [],
|
|
20
|
+
options: [],
|
|
21
|
+
chatDraft: null,
|
|
23
22
|
};
|
|
24
23
|
}
|
|
25
|
-
function
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
function startedTurn(turn, operation) {
|
|
25
|
+
if (operation.op === 'begin')
|
|
26
|
+
return { turn, started: operation.at, parts: [], context: null };
|
|
27
|
+
if (operation.op === 'context') {
|
|
28
|
+
return {
|
|
29
|
+
turn,
|
|
30
|
+
started: null,
|
|
31
|
+
parts: [],
|
|
32
|
+
context: { used: operation.used, window: operation.window },
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return operation.op === 'start' && operation.index === 0
|
|
36
|
+
? { turn, started: null, parts: [structuredClone(operation.part)], context: null }
|
|
37
|
+
: null;
|
|
31
38
|
}
|
|
32
|
-
function
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
function changedFields(part, operation) {
|
|
40
|
+
if (operation.op === 'append') {
|
|
41
|
+
return { text: `${String(part.text ?? '')}${operation.text}` };
|
|
42
|
+
}
|
|
43
|
+
if (operation.op === 'status') {
|
|
44
|
+
return { status: operation.status, result: structuredClone(operation.result) };
|
|
45
|
+
}
|
|
46
|
+
if (operation.op === 'count')
|
|
47
|
+
return { count: operation.count };
|
|
48
|
+
return { entries: structuredClone(operation.entries) };
|
|
49
|
+
}
|
|
50
|
+
function appliedTurn(held, operation) {
|
|
51
|
+
if (operation.op === 'begin')
|
|
52
|
+
return { ...held, started: operation.at };
|
|
53
|
+
if (operation.op === 'context') {
|
|
54
|
+
return { ...held, context: { used: operation.used, window: operation.window } };
|
|
55
|
+
}
|
|
56
|
+
if (operation.op === 'start') {
|
|
57
|
+
if (operation.index !== 0 && operation.index !== held.parts.length)
|
|
58
|
+
return null;
|
|
59
|
+
return {
|
|
60
|
+
...held,
|
|
61
|
+
parts: [...held.parts.slice(0, operation.index), structuredClone(operation.part)],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const part = held.parts[operation.index];
|
|
65
|
+
if (typeof part !== 'object' || part === null)
|
|
66
|
+
return null;
|
|
67
|
+
const changed = { ...part, ...changedFields(part, operation) };
|
|
41
68
|
return {
|
|
42
69
|
...held,
|
|
43
|
-
|
|
44
|
-
...carried,
|
|
45
|
-
...page.messages,
|
|
46
|
-
...live
|
|
47
|
-
],
|
|
48
|
-
cursor: overlap === -1 ? page.cursor : held.cursor
|
|
70
|
+
parts: held.parts.map((kept, at) => (at === operation.index ? changed : kept)),
|
|
49
71
|
};
|
|
50
72
|
}
|
|
51
|
-
function
|
|
52
|
-
|
|
73
|
+
function ordered(left, right) {
|
|
74
|
+
if (left.position !== right.position)
|
|
75
|
+
return left.position < right.position ? -1 : 1;
|
|
76
|
+
if (left.identity !== right.identity)
|
|
77
|
+
return left.identity < right.identity ? -1 : 1;
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
function merged(held, arriving) {
|
|
81
|
+
const byIdentity = new Map(held.map((message) => [message.identity, message]));
|
|
82
|
+
for (const message of arriving)
|
|
83
|
+
byIdentity.set(message.identity, message);
|
|
84
|
+
return [...byIdentity.values()].sort(ordered);
|
|
85
|
+
}
|
|
86
|
+
function acceptedMessage(value) {
|
|
87
|
+
if (typeof value !== 'object' || value === null)
|
|
88
|
+
return null;
|
|
89
|
+
const record = value;
|
|
90
|
+
if (typeof record.identity !== 'string' ||
|
|
91
|
+
typeof record.turn !== 'string' ||
|
|
92
|
+
typeof record.position !== 'string' ||
|
|
93
|
+
typeof record.text !== 'string') {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
53
96
|
return {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
cursor: page.cursor
|
|
97
|
+
identity: record.identity,
|
|
98
|
+
turn: record.turn,
|
|
99
|
+
position: record.position,
|
|
100
|
+
text: record.text,
|
|
101
|
+
message: structuredClone(record.message),
|
|
60
102
|
};
|
|
61
103
|
}
|
|
62
|
-
function
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const messages
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
104
|
+
function historyPage(value) {
|
|
105
|
+
if (typeof value !== 'object' || value === null)
|
|
106
|
+
return null;
|
|
107
|
+
const record = value;
|
|
108
|
+
if (typeof record.conversation !== 'string' || !Array.isArray(record.messages))
|
|
109
|
+
return null;
|
|
110
|
+
if (record.next !== undefined && typeof record.next !== 'string')
|
|
111
|
+
return null;
|
|
112
|
+
const messages = [];
|
|
113
|
+
for (const entry of record.messages) {
|
|
114
|
+
const message = acceptedMessage(entry);
|
|
115
|
+
if (message === null)
|
|
116
|
+
return null;
|
|
117
|
+
messages.push(message);
|
|
118
|
+
}
|
|
75
119
|
return {
|
|
76
|
-
|
|
77
|
-
messages
|
|
120
|
+
conversation: record.conversation,
|
|
121
|
+
messages,
|
|
122
|
+
...(record.next === undefined ? {} : { next: record.next }),
|
|
78
123
|
};
|
|
79
124
|
}
|
|
125
|
+
function historyPages(result) {
|
|
126
|
+
if (result.kind !== 'action')
|
|
127
|
+
return null;
|
|
128
|
+
const answer = result.result;
|
|
129
|
+
if (typeof answer !== 'object' || answer === null)
|
|
130
|
+
return null;
|
|
131
|
+
const { histories } = answer;
|
|
132
|
+
if (!Array.isArray(histories))
|
|
133
|
+
return null;
|
|
134
|
+
const pages = [];
|
|
135
|
+
for (const entry of histories) {
|
|
136
|
+
const page = historyPage(entry);
|
|
137
|
+
if (page === null)
|
|
138
|
+
return null;
|
|
139
|
+
pages.push(page);
|
|
140
|
+
}
|
|
141
|
+
return pages;
|
|
142
|
+
}
|
|
143
|
+
function turnOf(result) {
|
|
144
|
+
if (result.kind !== 'action')
|
|
145
|
+
return null;
|
|
146
|
+
const answer = result.result;
|
|
147
|
+
if (typeof answer !== 'object' || answer === null)
|
|
148
|
+
return null;
|
|
149
|
+
const record = answer;
|
|
150
|
+
return typeof record.turn === 'string' ? record.turn : null;
|
|
151
|
+
}
|
|
152
|
+
function limitOf(options) {
|
|
153
|
+
const { limit } = options;
|
|
154
|
+
if (limit === undefined)
|
|
155
|
+
return undefined;
|
|
156
|
+
if (!Number.isInteger(limit) || limit < 0) {
|
|
157
|
+
throw runtimeError('invalid-limit', `conversation limit must be a non-negative integer, got ${String(limit)}`);
|
|
158
|
+
}
|
|
159
|
+
return limit;
|
|
160
|
+
}
|
|
80
161
|
export function createConversationStore(deps) {
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
162
|
+
const messages = new Map();
|
|
163
|
+
const reads = new Map();
|
|
164
|
+
const drafts = new Map();
|
|
165
|
+
const sending = new Map();
|
|
166
|
+
const behind = new Set();
|
|
167
|
+
const gaps = new Map();
|
|
168
|
+
const lives = new Map();
|
|
169
|
+
const views = new Map();
|
|
170
|
+
const listing = new Map();
|
|
171
|
+
const listWatchers = new Set();
|
|
172
|
+
let listKnown = false;
|
|
173
|
+
const agentWatchers = new Set();
|
|
174
|
+
let agentNames = new Map();
|
|
175
|
+
let retiredAgents = new Set();
|
|
176
|
+
let agentsRead = null;
|
|
177
|
+
let replicated = new Set();
|
|
178
|
+
let handedReplicated;
|
|
179
|
+
function liveOf(conversation) {
|
|
180
|
+
return lives.get(conversation) ?? QUIET;
|
|
94
181
|
}
|
|
95
|
-
function
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
182
|
+
function shownMessages(conversation, limit) {
|
|
183
|
+
const held = messages.get(conversation) ?? [];
|
|
184
|
+
const shown = limit === undefined ? held : held.slice(Math.max(0, held.length - limit));
|
|
185
|
+
return shown.map(({ identity, turn, text, message }) => ({
|
|
186
|
+
identity,
|
|
187
|
+
turn,
|
|
188
|
+
text,
|
|
189
|
+
message: structuredClone(message),
|
|
190
|
+
}));
|
|
101
191
|
}
|
|
102
|
-
function viewOf(conversation) {
|
|
103
|
-
const
|
|
104
|
-
const
|
|
192
|
+
function viewOf(conversation, limit, shown = shownMessages(conversation, limit)) {
|
|
193
|
+
const draft = drafts.get(conversation);
|
|
194
|
+
const facts = liveOf(conversation);
|
|
105
195
|
return {
|
|
106
196
|
conversation,
|
|
107
|
-
messages:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
]
|
|
116
|
-
}
|
|
197
|
+
messages: shown,
|
|
198
|
+
turn: structuredClone(facts.turn),
|
|
199
|
+
questions: structuredClone(facts.questions),
|
|
200
|
+
process: facts.process === null ? null : { ...facts.process },
|
|
201
|
+
commands: structuredClone(facts.commands),
|
|
202
|
+
options: structuredClone(facts.options),
|
|
203
|
+
chatDraft: draft === undefined || draft.turn !== null || sending.get(conversation)?.draft === draft
|
|
204
|
+
? null
|
|
205
|
+
: { text: draft.text, files: [...draft.files], revision: draft.revision },
|
|
117
206
|
};
|
|
118
207
|
}
|
|
119
|
-
function emit(conversation) {
|
|
120
|
-
if (deps.closed())
|
|
121
|
-
|
|
122
|
-
|
|
208
|
+
function emit(conversation, messagesChanged = true) {
|
|
209
|
+
if (deps.closed())
|
|
210
|
+
return;
|
|
211
|
+
for (const view of views.values()) {
|
|
212
|
+
if (view.conversation !== conversation)
|
|
213
|
+
continue;
|
|
214
|
+
if (messagesChanged)
|
|
215
|
+
view.messages = null;
|
|
216
|
+
const ready = [...view.watchers].filter((watcher) => watcher.ready);
|
|
217
|
+
if (ready.length === 0)
|
|
218
|
+
continue;
|
|
219
|
+
view.messages ??= shownMessages(conversation, view.limit);
|
|
220
|
+
const value = viewOf(conversation, view.limit, view.messages);
|
|
221
|
+
for (const watcher of ready)
|
|
222
|
+
watcher.notify(value);
|
|
123
223
|
}
|
|
124
224
|
}
|
|
125
|
-
function
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
225
|
+
function stateOf(conversation) {
|
|
226
|
+
const facts = liveOf(conversation);
|
|
227
|
+
if (facts.questions.length > 0)
|
|
228
|
+
return 'asking';
|
|
229
|
+
return facts.turn === null ? 'idle' : 'working';
|
|
230
|
+
}
|
|
231
|
+
function entries() {
|
|
232
|
+
return [...listing.values()].map((entry) => ({
|
|
233
|
+
conversation: entry.conversation,
|
|
234
|
+
agent: { identity: entry.agent, name: agentNames.get(entry.agent) ?? null },
|
|
235
|
+
name: entry.name,
|
|
236
|
+
created: entry.created,
|
|
237
|
+
finished: entry.finished,
|
|
238
|
+
outcome: entry.outcome,
|
|
239
|
+
state: stateOf(entry.conversation),
|
|
240
|
+
started: lives.get(entry.conversation)?.turn?.started ?? null,
|
|
241
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
function emitList() {
|
|
244
|
+
if (deps.closed() || listWatchers.size === 0)
|
|
245
|
+
return;
|
|
246
|
+
const value = entries();
|
|
247
|
+
for (const watcher of listWatchers)
|
|
248
|
+
watcher(value);
|
|
249
|
+
}
|
|
250
|
+
function agents() {
|
|
251
|
+
return [...agentNames]
|
|
252
|
+
.filter(([identity]) => !retiredAgents.has(identity))
|
|
253
|
+
.map(([identity, name]) => ({ identity, name }))
|
|
254
|
+
.sort((left, right) => left.name === right.name
|
|
255
|
+
? left.identity.localeCompare(right.identity)
|
|
256
|
+
: left.name.localeCompare(right.name));
|
|
257
|
+
}
|
|
258
|
+
function emitAgents() {
|
|
259
|
+
if (deps.closed() || agentWatchers.size === 0)
|
|
260
|
+
return;
|
|
261
|
+
const value = agents();
|
|
262
|
+
for (const watcher of agentWatchers)
|
|
263
|
+
watcher(value);
|
|
264
|
+
}
|
|
265
|
+
function setLive(conversation, next) {
|
|
266
|
+
const before = stateOf(conversation);
|
|
267
|
+
const began = lives.get(conversation)?.turn?.started;
|
|
268
|
+
if (next === null)
|
|
269
|
+
lives.delete(conversation);
|
|
270
|
+
else
|
|
271
|
+
lives.set(conversation, next);
|
|
272
|
+
if (!listing.has(conversation))
|
|
273
|
+
return;
|
|
274
|
+
if (stateOf(conversation) !== before || next?.turn?.started !== began)
|
|
275
|
+
emitList();
|
|
149
276
|
}
|
|
150
|
-
async function
|
|
151
|
-
const
|
|
152
|
-
if (
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
277
|
+
async function readAgents() {
|
|
278
|
+
const declaration = deps.agents();
|
|
279
|
+
if (declaration === undefined)
|
|
280
|
+
return;
|
|
281
|
+
const { documents } = await deps.documents(declaration.locator);
|
|
282
|
+
const names = new Map();
|
|
283
|
+
const retired = new Set();
|
|
284
|
+
for (const document of documents) {
|
|
285
|
+
if (document.type !== declaration.type)
|
|
286
|
+
continue;
|
|
287
|
+
const identity = document.fields?.[declaration.identity];
|
|
288
|
+
const name = document.fields?.[declaration.name];
|
|
289
|
+
if (typeof identity !== 'string' || typeof name !== 'string')
|
|
290
|
+
continue;
|
|
291
|
+
names.set(identity, name);
|
|
292
|
+
const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
|
|
293
|
+
if (ended !== undefined && ended !== null)
|
|
294
|
+
retired.add(identity);
|
|
295
|
+
}
|
|
296
|
+
const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
|
|
297
|
+
if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
|
|
298
|
+
return;
|
|
299
|
+
agentNames = names;
|
|
300
|
+
retiredAgents = retired;
|
|
301
|
+
if (named)
|
|
302
|
+
emitList();
|
|
303
|
+
emitAgents();
|
|
304
|
+
}
|
|
305
|
+
function agentsReady() {
|
|
306
|
+
agentsRead ??= readAgents().catch(() => {
|
|
307
|
+
agentsRead = null;
|
|
308
|
+
});
|
|
309
|
+
return agentsRead;
|
|
310
|
+
}
|
|
311
|
+
function live(conversation, change) {
|
|
312
|
+
setLive(conversation, { ...liveOf(conversation), ...change });
|
|
313
|
+
emit(conversation, false);
|
|
314
|
+
}
|
|
315
|
+
function relayed(conversation, turn, operation) {
|
|
316
|
+
const held = liveOf(conversation).turn;
|
|
317
|
+
const next = held?.turn === turn ? appliedTurn(held, operation) : startedTurn(turn, operation);
|
|
318
|
+
if (next !== null)
|
|
319
|
+
live(conversation, { turn: next });
|
|
320
|
+
}
|
|
321
|
+
function sealTurns(conversation, turns) {
|
|
322
|
+
const held = lives.get(conversation);
|
|
323
|
+
if (held?.turn == null || !turns.has(held.turn.turn))
|
|
324
|
+
return false;
|
|
325
|
+
setLive(conversation, { ...held, turn: null });
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
function handReplicated() {
|
|
329
|
+
const stream = deps.stream();
|
|
330
|
+
if (stream === undefined)
|
|
331
|
+
return;
|
|
332
|
+
const held = [...replicated].sort();
|
|
333
|
+
const named = JSON.stringify(held);
|
|
334
|
+
if (named === handedReplicated)
|
|
335
|
+
return;
|
|
336
|
+
handedReplicated = named;
|
|
337
|
+
stream.replicate(held);
|
|
338
|
+
}
|
|
339
|
+
async function remove(leaving) {
|
|
340
|
+
await deps.durably(async (local) => {
|
|
341
|
+
await local.dropConversations(leaving);
|
|
342
|
+
const removed = [];
|
|
343
|
+
const changed = [];
|
|
344
|
+
for (const conversation of leaving) {
|
|
345
|
+
for (const message of messages.get(conversation) ?? []) {
|
|
346
|
+
removed.push({ conversation, identity: message.identity });
|
|
347
|
+
}
|
|
348
|
+
if (messages.has(conversation) || drafts.has(conversation) || lives.has(conversation)) {
|
|
349
|
+
changed.push(conversation);
|
|
350
|
+
}
|
|
351
|
+
messages.delete(conversation);
|
|
352
|
+
reads.delete(conversation);
|
|
353
|
+
drafts.delete(conversation);
|
|
354
|
+
behind.delete(conversation);
|
|
355
|
+
setLive(conversation, null);
|
|
356
|
+
}
|
|
357
|
+
if (changed.length > 0)
|
|
358
|
+
deps.publish(conversationChangeSet(removed, changed));
|
|
165
359
|
});
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
360
|
+
}
|
|
361
|
+
function gap(conversation) {
|
|
362
|
+
gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
|
|
363
|
+
behind.add(conversation);
|
|
364
|
+
}
|
|
365
|
+
function keep(conversation, message) {
|
|
366
|
+
return deps.durably(async (local) => {
|
|
367
|
+
if (!replicated.has(conversation))
|
|
368
|
+
return;
|
|
369
|
+
const read = reads.get(conversation);
|
|
370
|
+
const advances = read !== undefined &&
|
|
371
|
+
!behind.has(conversation) &&
|
|
372
|
+
(read === null || message.position > read);
|
|
373
|
+
const sealed = drafts.get(conversation)?.turn === message.turn;
|
|
374
|
+
await local.keepMessages({
|
|
174
375
|
conversation,
|
|
175
|
-
|
|
376
|
+
messages: [message],
|
|
377
|
+
...(advances ? { read: message.position } : {}),
|
|
378
|
+
sealed,
|
|
176
379
|
});
|
|
177
|
-
|
|
380
|
+
messages.set(conversation, merged(messages.get(conversation) ?? [], [message]));
|
|
381
|
+
if (advances)
|
|
382
|
+
reads.set(conversation, message.position);
|
|
383
|
+
if (sealed)
|
|
384
|
+
drafts.delete(conversation);
|
|
385
|
+
sealTurns(conversation, new Set([message.turn]));
|
|
386
|
+
deps.publish(conversationChangeSet([{ conversation, identity: message.identity }], [conversation]));
|
|
178
387
|
});
|
|
179
|
-
emit(conversation);
|
|
180
388
|
}
|
|
181
|
-
async function
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
389
|
+
async function readHistories(conversations) {
|
|
390
|
+
const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
|
|
391
|
+
const starts = new Map(conversations.map((one) => [one, reads.get(one)]));
|
|
392
|
+
const arriving = new Map();
|
|
393
|
+
const complete = new Set();
|
|
394
|
+
let asking = conversations.map((conversation) => ({
|
|
395
|
+
conversation,
|
|
396
|
+
after: starts.get(conversation) ?? undefined,
|
|
397
|
+
}));
|
|
398
|
+
while (asking.length > 0) {
|
|
399
|
+
const result = await deps.call('conversation.history', {
|
|
400
|
+
conversations: asking.map(({ conversation, after }) => ({
|
|
401
|
+
conversation,
|
|
402
|
+
...(after === undefined ? {} : { after }),
|
|
403
|
+
})),
|
|
404
|
+
});
|
|
405
|
+
const pages = historyPages(result);
|
|
406
|
+
if (pages === null)
|
|
407
|
+
return result;
|
|
408
|
+
const asked = new Set(asking.map(({ conversation }) => conversation));
|
|
409
|
+
const following = [];
|
|
410
|
+
for (const page of pages) {
|
|
411
|
+
if (!asked.has(page.conversation))
|
|
412
|
+
continue;
|
|
413
|
+
arriving.set(page.conversation, [
|
|
414
|
+
...(arriving.get(page.conversation) ?? []),
|
|
415
|
+
...page.messages,
|
|
416
|
+
]);
|
|
417
|
+
if (page.next === undefined)
|
|
418
|
+
complete.add(page.conversation);
|
|
419
|
+
else
|
|
420
|
+
following.push({ conversation: page.conversation, after: page.next });
|
|
421
|
+
}
|
|
422
|
+
asking = following;
|
|
423
|
+
}
|
|
424
|
+
for (const [conversation, held] of arriving) {
|
|
425
|
+
if (!complete.has(conversation))
|
|
426
|
+
continue;
|
|
427
|
+
await keepHistory(conversation, held, starts.get(conversation), generations.get(conversation) ?? 0);
|
|
428
|
+
}
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
function keepHistory(conversation, arriving, from, generation) {
|
|
432
|
+
return deps.durably(async (local) => {
|
|
433
|
+
if (!replicated.has(conversation))
|
|
434
|
+
return;
|
|
435
|
+
const newest = arriving.reduce((top, message) => (top === null || message.position > top ? message.position : top), from ?? null);
|
|
436
|
+
const turn = drafts.get(conversation)?.turn;
|
|
437
|
+
const sealed = arriving.some((message) => message.turn === turn);
|
|
438
|
+
await local.keepMessages({ conversation, messages: arriving, read: newest, sealed });
|
|
439
|
+
messages.set(conversation, merged(messages.get(conversation) ?? [], arriving));
|
|
440
|
+
reads.set(conversation, newest);
|
|
441
|
+
if (sealed)
|
|
442
|
+
drafts.delete(conversation);
|
|
443
|
+
sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
|
|
444
|
+
if ((gaps.get(conversation) ?? 0) === generation)
|
|
445
|
+
behind.delete(conversation);
|
|
446
|
+
if (arriving.length > 0 || sealed) {
|
|
447
|
+
deps.publish(conversationChangeSet(arriving.map((message) => ({ conversation, identity: message.identity })), [conversation]));
|
|
448
|
+
}
|
|
185
449
|
});
|
|
186
|
-
emit(conversation);
|
|
187
450
|
}
|
|
188
|
-
function
|
|
189
|
-
|
|
451
|
+
function heard(event) {
|
|
452
|
+
const { conversation } = event;
|
|
453
|
+
if (event.kind === 'gap') {
|
|
454
|
+
if (replicated.has(conversation))
|
|
455
|
+
gap(conversation);
|
|
456
|
+
const held = lives.get(conversation);
|
|
457
|
+
if (held === undefined)
|
|
458
|
+
return;
|
|
459
|
+
setLive(conversation, { ...QUIET, turn: held.turn });
|
|
460
|
+
emit(conversation, false);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (event.kind === 'turn') {
|
|
464
|
+
relayed(conversation, event.turn, event.operation);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
if (event.kind === 'questions') {
|
|
468
|
+
live(conversation, { questions: structuredClone(event.questions) });
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (event.kind === 'process') {
|
|
472
|
+
live(conversation, { process: { running: event.running } });
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
if (event.kind === 'commands') {
|
|
476
|
+
live(conversation, { commands: structuredClone(event.commands) });
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
if (event.kind === 'options') {
|
|
480
|
+
live(conversation, { options: structuredClone(event.options) });
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
const arriving = acceptedMessage(event.message);
|
|
484
|
+
if (arriving === null)
|
|
485
|
+
return;
|
|
486
|
+
if (replicated.has(conversation)) {
|
|
487
|
+
void keep(conversation, arriving).catch(() => { });
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
if (sealTurns(conversation, new Set([arriving.turn])))
|
|
491
|
+
emit(conversation);
|
|
190
492
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
493
|
+
function withDraft(conversation, next) {
|
|
494
|
+
return deps.durably(async (local) => {
|
|
495
|
+
const following = next(drafts.get(conversation));
|
|
496
|
+
if (following === undefined)
|
|
497
|
+
return;
|
|
498
|
+
if (following === null) {
|
|
499
|
+
await local.dropChatDraft(conversation);
|
|
500
|
+
drafts.delete(conversation);
|
|
197
501
|
}
|
|
502
|
+
else {
|
|
503
|
+
await local.keepChatDraft(following);
|
|
504
|
+
drafts.set(conversation, following);
|
|
505
|
+
}
|
|
506
|
+
deps.publish(conversationChangeSet([], [conversation]));
|
|
198
507
|
});
|
|
199
|
-
const page = historyPage(result);
|
|
200
|
-
if (page === null) return result;
|
|
201
|
-
const landed = await keep(conversation, (carried)=>{
|
|
202
|
-
if (issued.get(conversation) !== generation) return null;
|
|
203
|
-
if (cursor !== null) return carried.cursor === cursor ? older(carried, page) : null;
|
|
204
|
-
return newest(carried, page, live.get(conversation) ?? NO_TURNS);
|
|
205
|
-
});
|
|
206
|
-
if (landed && cursor === null) live.delete(conversation);
|
|
207
|
-
return result;
|
|
208
508
|
}
|
|
209
509
|
function invoke(name, conversation, extra = {}) {
|
|
210
|
-
return deps.call(name, {
|
|
211
|
-
conversation,
|
|
212
|
-
...extra
|
|
213
|
-
});
|
|
510
|
+
return deps.call(name, { conversation, ...extra });
|
|
214
511
|
}
|
|
215
512
|
const surface = {
|
|
216
|
-
list
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const documents = deps.documents().documents.filter((document)=>document.room === declaration.locator);
|
|
220
|
-
const entries = [];
|
|
221
|
-
for (const card of documents){
|
|
222
|
-
if (card.type !== declaration.type) continue;
|
|
223
|
-
const identity = card.fields[declaration.identity];
|
|
224
|
-
if (typeof identity !== 'string') continue;
|
|
225
|
-
const agent = documents.find((document)=>document.backlinks.some((backlink)=>backlink.path === card.path && backlink.field === declaration.agent));
|
|
226
|
-
entries.push({
|
|
227
|
-
conversation: identity,
|
|
228
|
-
card,
|
|
229
|
-
agent: agent ?? null
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
entries.sort((left, right)=>left.conversation < right.conversation ? -1 : 1);
|
|
233
|
-
return Promise.resolve(entries);
|
|
234
|
-
},
|
|
235
|
-
read (conversation) {
|
|
236
|
-
return deps.durably(()=>Promise.resolve(viewOf(conversation)));
|
|
237
|
-
},
|
|
238
|
-
older (conversation) {
|
|
239
|
-
const cursor = heldOf(conversation).cursor;
|
|
240
|
-
if (cursor === null) return Promise.resolve(null);
|
|
241
|
-
return fill(conversation, cursor);
|
|
513
|
+
async list() {
|
|
514
|
+
await agentsReady();
|
|
515
|
+
return entries();
|
|
242
516
|
},
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
files: [
|
|
247
|
-
...supplied.files
|
|
248
|
-
],
|
|
249
|
-
turn: null
|
|
250
|
-
}));
|
|
517
|
+
async read(conversation, options = {}) {
|
|
518
|
+
const limit = limitOf(options);
|
|
519
|
+
return deps.durably(() => Promise.resolve(viewOf(conversation, limit)));
|
|
251
520
|
},
|
|
252
|
-
|
|
253
|
-
return dropInstruction(conversation);
|
|
254
|
-
},
|
|
255
|
-
create (input) {
|
|
521
|
+
create(input) {
|
|
256
522
|
return deps.call('conversation.create', input);
|
|
257
523
|
},
|
|
258
|
-
async send
|
|
259
|
-
const
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
524
|
+
async send(conversation) {
|
|
525
|
+
const draft = drafts.get(conversation);
|
|
526
|
+
const pending = draft === undefined ? undefined : { draft };
|
|
527
|
+
if (pending !== undefined) {
|
|
528
|
+
sending.set(conversation, pending);
|
|
529
|
+
deps.publish(conversationChangeSet([], [conversation]));
|
|
530
|
+
}
|
|
531
|
+
try {
|
|
532
|
+
const files = await Promise.all((draft?.files ?? []).map(async (file) => ({
|
|
533
|
+
...(await declaredFile(file)),
|
|
534
|
+
blob: file.blob,
|
|
535
|
+
})));
|
|
536
|
+
const result = await invoke('conversation.send', conversation, {
|
|
537
|
+
text: draft?.text ?? '',
|
|
538
|
+
files,
|
|
539
|
+
});
|
|
540
|
+
const turn = turnOf(result);
|
|
541
|
+
if (turn === null || draft === undefined)
|
|
542
|
+
return result;
|
|
543
|
+
await withDraft(conversation, (current) => {
|
|
544
|
+
if (current !== draft)
|
|
545
|
+
return undefined;
|
|
546
|
+
const sealed = (messages.get(conversation) ?? []).some((message) => message.turn === turn);
|
|
547
|
+
return sealed ? null : { ...draft, turn };
|
|
548
|
+
});
|
|
549
|
+
return result;
|
|
550
|
+
}
|
|
551
|
+
finally {
|
|
552
|
+
if (pending !== undefined && sending.get(conversation) === pending) {
|
|
553
|
+
sending.delete(conversation);
|
|
554
|
+
if (drafts.get(conversation) === draft) {
|
|
555
|
+
deps.publish(conversationChangeSet([], [conversation]));
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
272
559
|
},
|
|
273
|
-
answer
|
|
274
|
-
return invoke('conversation.answer', conversation, {
|
|
275
|
-
answer
|
|
276
|
-
});
|
|
560
|
+
answer(conversation, answer) {
|
|
561
|
+
return invoke('conversation.answer', conversation, { answer });
|
|
277
562
|
},
|
|
278
|
-
interrupt
|
|
563
|
+
interrupt(conversation) {
|
|
279
564
|
return invoke('conversation.interrupt', conversation);
|
|
280
565
|
},
|
|
281
|
-
kill
|
|
566
|
+
kill(conversation) {
|
|
282
567
|
return invoke('conversation.kill', conversation);
|
|
283
568
|
},
|
|
284
|
-
async cancel
|
|
285
|
-
const result = await invoke('conversation.cancel', conversation, {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
if (instructionOf(conversation).turn === turn) {
|
|
290
|
-
await keepInstruction(conversation, (stored)=>({
|
|
291
|
-
...stored,
|
|
292
|
-
turn: null
|
|
293
|
-
}));
|
|
294
|
-
}
|
|
569
|
+
async cancel(conversation, turn) {
|
|
570
|
+
const result = await invoke('conversation.cancel', conversation, { turn });
|
|
571
|
+
if (result.kind !== 'action')
|
|
572
|
+
return result;
|
|
573
|
+
await withDraft(conversation, (current) => current === undefined || current.turn !== turn ? undefined : { ...current, turn: null });
|
|
295
574
|
return result;
|
|
296
575
|
},
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
576
|
+
delete(conversation) {
|
|
577
|
+
return invoke('conversation.delete', conversation);
|
|
578
|
+
},
|
|
579
|
+
};
|
|
580
|
+
const chatDraft = {
|
|
581
|
+
write(conversation, draft, revision = chatDraftRevision()) {
|
|
582
|
+
return withDraft(conversation, () => ({
|
|
583
|
+
conversation,
|
|
584
|
+
text: draft.text,
|
|
585
|
+
files: [...draft.files],
|
|
586
|
+
turn: null,
|
|
587
|
+
revision,
|
|
588
|
+
}));
|
|
589
|
+
},
|
|
590
|
+
discard(conversation) {
|
|
591
|
+
return withDraft(conversation, (current) => (current === undefined ? undefined : null));
|
|
592
|
+
},
|
|
312
593
|
};
|
|
313
594
|
return {
|
|
314
595
|
surface,
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
596
|
+
chatDraft,
|
|
597
|
+
hydrate(held, stored) {
|
|
598
|
+
messages.clear();
|
|
599
|
+
reads.clear();
|
|
600
|
+
for (const view of views.values())
|
|
601
|
+
view.messages = null;
|
|
602
|
+
for (const entry of held) {
|
|
603
|
+
messages.set(entry.conversation, [...entry.messages].sort(ordered));
|
|
604
|
+
if (entry.read !== undefined)
|
|
605
|
+
reads.set(entry.conversation, entry.read);
|
|
323
606
|
}
|
|
324
|
-
|
|
325
|
-
for (const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
607
|
+
drafts.clear();
|
|
608
|
+
for (const record of stored)
|
|
609
|
+
drafts.set(record.conversation, record);
|
|
610
|
+
},
|
|
611
|
+
async reconcile() {
|
|
612
|
+
if (!listKnown)
|
|
613
|
+
return;
|
|
614
|
+
const named = new Set(listing.keys());
|
|
615
|
+
const leaving = [
|
|
616
|
+
...new Set([...replicated, ...messages.keys(), ...reads.keys(), ...drafts.keys()]),
|
|
617
|
+
].filter((conversation) => !named.has(conversation));
|
|
618
|
+
for (const conversation of named) {
|
|
619
|
+
if (!replicated.has(conversation))
|
|
620
|
+
behind.add(conversation);
|
|
621
|
+
}
|
|
622
|
+
replicated = named;
|
|
623
|
+
handReplicated();
|
|
624
|
+
if (leaving.length > 0)
|
|
625
|
+
await remove(leaving);
|
|
626
|
+
},
|
|
627
|
+
async catchUp(progress) {
|
|
628
|
+
const reading = [...behind].filter((conversation) => replicated.has(conversation)).sort();
|
|
629
|
+
if (reading.length > 0 && !deps.closed()) {
|
|
630
|
+
const failed = await readHistories(reading);
|
|
631
|
+
if (failed?.kind === 'transport-uncertainty')
|
|
632
|
+
progress.reachable = false;
|
|
331
633
|
}
|
|
634
|
+
if (behind.size > 0)
|
|
635
|
+
progress.complete = false;
|
|
332
636
|
},
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
637
|
+
current() {
|
|
638
|
+
return behind.size === 0;
|
|
639
|
+
},
|
|
640
|
+
reconnected() {
|
|
641
|
+
for (const conversation of replicated)
|
|
642
|
+
gap(conversation);
|
|
643
|
+
},
|
|
644
|
+
watch(conversation, options, deliver) {
|
|
645
|
+
const limit = limitOf(options);
|
|
646
|
+
const key = JSON.stringify([conversation, limit ?? null]);
|
|
647
|
+
const view = views.get(key) ?? {
|
|
336
648
|
conversation,
|
|
337
|
-
|
|
338
|
-
|
|
649
|
+
limit,
|
|
650
|
+
watchers: new Set(),
|
|
651
|
+
messages: null,
|
|
339
652
|
};
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
return ()=>{
|
|
353
|
-
if (!
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
358
|
-
hand();
|
|
653
|
+
views.set(key, view);
|
|
654
|
+
const watcher = { notify: deliver, ready: false };
|
|
655
|
+
view.watchers.add(watcher);
|
|
656
|
+
void deps
|
|
657
|
+
.durably(() => Promise.resolve())
|
|
658
|
+
.then(() => {
|
|
659
|
+
if (deps.closed() || !view.watchers.has(watcher))
|
|
660
|
+
return;
|
|
661
|
+
watcher.ready = true;
|
|
662
|
+
watcher.notify(viewOf(conversation, limit));
|
|
663
|
+
})
|
|
664
|
+
.catch(() => { });
|
|
665
|
+
return () => {
|
|
666
|
+
if (!view.watchers.delete(watcher))
|
|
667
|
+
return;
|
|
668
|
+
if (view.watchers.size === 0)
|
|
669
|
+
views.delete(key);
|
|
359
670
|
};
|
|
360
671
|
},
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
672
|
+
watchList(deliver) {
|
|
673
|
+
listWatchers.add(deliver);
|
|
674
|
+
void agentsReady().then(() => {
|
|
675
|
+
if (!deps.closed() && listWatchers.has(deliver))
|
|
676
|
+
deliver(entries());
|
|
677
|
+
});
|
|
678
|
+
return () => {
|
|
679
|
+
listWatchers.delete(deliver);
|
|
680
|
+
};
|
|
681
|
+
},
|
|
682
|
+
watchAgents(deliver) {
|
|
683
|
+
agentWatchers.add(deliver);
|
|
684
|
+
void agentsReady().then(() => {
|
|
685
|
+
if (!deps.closed() && agentWatchers.has(deliver))
|
|
686
|
+
deliver(agents());
|
|
687
|
+
});
|
|
688
|
+
return () => {
|
|
689
|
+
agentWatchers.delete(deliver);
|
|
690
|
+
};
|
|
691
|
+
},
|
|
692
|
+
changed(set) {
|
|
693
|
+
for (const conversation of set.conversations)
|
|
694
|
+
emit(conversation);
|
|
695
|
+
const declaration = deps.agents();
|
|
696
|
+
if (declaration !== undefined &&
|
|
697
|
+
set.documents.some((change) => change.locator === declaration.locator)) {
|
|
698
|
+
agentsRead = readAgents().catch(() => {
|
|
699
|
+
agentsRead = null;
|
|
367
700
|
});
|
|
368
|
-
emit(event.conversation);
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
if (event.kind === 'session-summary') {
|
|
372
|
-
const summary = structuredClone(event.summary);
|
|
373
|
-
void keep(event.conversation, (carried)=>({
|
|
374
|
-
...carried,
|
|
375
|
-
summary
|
|
376
|
-
})).catch(()=>{});
|
|
377
|
-
return;
|
|
378
701
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
702
|
+
},
|
|
703
|
+
receive(event) {
|
|
704
|
+
if (deps.closed())
|
|
705
|
+
return false;
|
|
706
|
+
if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
|
|
707
|
+
if (event.kind === 'list') {
|
|
708
|
+
listing.clear();
|
|
709
|
+
for (const entry of event.entries)
|
|
710
|
+
listing.set(entry.conversation, { ...entry });
|
|
711
|
+
listKnown = true;
|
|
712
|
+
}
|
|
713
|
+
else if (event.kind === 'listed') {
|
|
714
|
+
listing.set(event.entry.conversation, { ...event.entry });
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
listing.delete(event.conversation);
|
|
718
|
+
}
|
|
719
|
+
emitList();
|
|
720
|
+
return (event.kind !== 'list' &&
|
|
721
|
+
listKnown &&
|
|
722
|
+
(listing.size !== replicated.size ||
|
|
723
|
+
[...listing.keys()].some((conversation) => !replicated.has(conversation))));
|
|
384
724
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
725
|
+
heard(event);
|
|
726
|
+
return false;
|
|
727
|
+
},
|
|
728
|
+
messages() {
|
|
729
|
+
return [...messages].flatMap(([conversation, held]) => held.map(({ identity, text }) => ({ conversation, identity, text })));
|
|
730
|
+
},
|
|
731
|
+
message(conversation, identity) {
|
|
732
|
+
const held = messages.get(conversation)?.find((message) => message.identity === identity);
|
|
733
|
+
return held === undefined ? null : { conversation, identity, text: held.text };
|
|
734
|
+
},
|
|
735
|
+
close() {
|
|
736
|
+
views.clear();
|
|
737
|
+
lives.clear();
|
|
738
|
+
listWatchers.clear();
|
|
739
|
+
agentWatchers.clear();
|
|
392
740
|
},
|
|
393
|
-
close () {
|
|
394
|
-
viewers.clear();
|
|
395
|
-
growing.clear();
|
|
396
|
-
live.clear();
|
|
397
|
-
}
|
|
398
741
|
};
|
|
399
742
|
}
|
|
400
|
-
|
|
401
|
-
//# sourceMappingURL=conversation.js.map
|