@haklex/rich-agent-core 0.0.94
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/dist/agent-executor.d.ts +19 -0
- package/dist/agent-executor.d.ts.map +1 -0
- package/dist/diff-engine.d.ts +8 -0
- package/dist/diff-engine.d.ts.map +1 -0
- package/dist/document-context.d.ts +4 -0
- package/dist/document-context.d.ts.map +1 -0
- package/dist/document-tools.d.ts +5 -0
- package/dist/document-tools.d.ts.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +1143 -0
- package/dist/initialState.d.ts +63 -0
- package/dist/initialState.d.ts.map +1 -0
- package/dist/messages-engine/base/BaseEveryUserContentProvider.d.ts +11 -0
- package/dist/messages-engine/base/BaseEveryUserContentProvider.d.ts.map +1 -0
- package/dist/messages-engine/base/BaseFirstUserContentProvider.d.ts +7 -0
- package/dist/messages-engine/base/BaseFirstUserContentProvider.d.ts.map +1 -0
- package/dist/messages-engine/base/BaseLastUserContentProvider.d.ts +13 -0
- package/dist/messages-engine/base/BaseLastUserContentProvider.d.ts.map +1 -0
- package/dist/messages-engine/base/BaseMessageEngineProcessor.d.ts +8 -0
- package/dist/messages-engine/base/BaseMessageEngineProcessor.d.ts.map +1 -0
- package/dist/messages-engine/base/BaseSystemRoleProvider.d.ts +7 -0
- package/dist/messages-engine/base/BaseSystemRoleProvider.d.ts.map +1 -0
- package/dist/messages-engine/base/BaseSystemRootProvider.d.ts +8 -0
- package/dist/messages-engine/base/BaseSystemRootProvider.d.ts.map +1 -0
- package/dist/messages-engine/base/constants.d.ts +4 -0
- package/dist/messages-engine/base/constants.d.ts.map +1 -0
- package/dist/messages-engine/base/messageContextUtils.d.ts +15 -0
- package/dist/messages-engine/base/messageContextUtils.d.ts.map +1 -0
- package/dist/messages-engine/engine.d.ts +9 -0
- package/dist/messages-engine/engine.d.ts.map +1 -0
- package/dist/messages-engine/helpers.d.ts +16 -0
- package/dist/messages-engine/helpers.d.ts.map +1 -0
- package/dist/messages-engine/index.d.ts +5 -0
- package/dist/messages-engine/index.d.ts.map +1 -0
- package/dist/messages-engine/processors.d.ts +8 -0
- package/dist/messages-engine/processors.d.ts.map +1 -0
- package/dist/protocol.d.ts +121 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/review-engine.d.ts +12 -0
- package/dist/review-engine.d.ts.map +1 -0
- package/dist/review-types.d.ts +27 -0
- package/dist/review-types.d.ts.map +1 -0
- package/dist/selectors.d.ts +8 -0
- package/dist/selectors.d.ts.map +1 -0
- package/dist/snapshot.d.ts +9 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/store-actions.d.ts +46 -0
- package/dist/store-actions.d.ts.map +1 -0
- package/dist/store-types.d.ts +5 -0
- package/dist/store-types.d.ts.map +1 -0
- package/dist/store-utils.d.ts +2 -0
- package/dist/store-utils.d.ts.map +1 -0
- package/dist/store.d.ts +9 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/types.d.ts +47 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +48 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1143 @@
|
|
|
1
|
+
var __typeError = (msg) => {
|
|
2
|
+
throw TypeError(msg);
|
|
3
|
+
};
|
|
4
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
5
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
6
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
7
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
8
|
+
var _get, _set;
|
|
9
|
+
import { createDefaultRegistry, deserializeNodesFromXml, serializeToXml } from "@haklex/rich-litexml";
|
|
10
|
+
import { nanoid } from "nanoid";
|
|
11
|
+
import { createStore } from "zustand/vanilla";
|
|
12
|
+
function extractText(node) {
|
|
13
|
+
const n = node;
|
|
14
|
+
if (n.text) return n.text;
|
|
15
|
+
if (n.children) return n.children.map(extractText).join("");
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
function createDocumentTools(snapshot, operations) {
|
|
19
|
+
const registry = createDefaultRegistry();
|
|
20
|
+
const insertNodeTool = {
|
|
21
|
+
name: "insert_node",
|
|
22
|
+
description: "Insert one or more block nodes at a position relative to an existing block. The xml parameter should contain XML elements like <p>, <h2>, <ul>, <codeblock>, <img />, etc.",
|
|
23
|
+
parameters: {
|
|
24
|
+
type: "object",
|
|
25
|
+
properties: {
|
|
26
|
+
position: {
|
|
27
|
+
type: "object",
|
|
28
|
+
properties: {
|
|
29
|
+
type: { type: "string", enum: ["after", "before", "root"] },
|
|
30
|
+
blockId: { type: "string" },
|
|
31
|
+
index: { type: "number" }
|
|
32
|
+
},
|
|
33
|
+
required: ["type"]
|
|
34
|
+
},
|
|
35
|
+
xml: { type: "string", description: "XML string containing block elements to insert" }
|
|
36
|
+
},
|
|
37
|
+
required: ["position", "xml"]
|
|
38
|
+
},
|
|
39
|
+
describeCall: (params) => {
|
|
40
|
+
const p = params;
|
|
41
|
+
const pos = p.position;
|
|
42
|
+
return pos?.blockId ? `inserting ${pos.type} block "${pos.blockId}"` : "inserting node";
|
|
43
|
+
},
|
|
44
|
+
execute: async (params) => {
|
|
45
|
+
const { position, xml } = params;
|
|
46
|
+
if (!xml || typeof xml !== "string") {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
error: {
|
|
50
|
+
error: "invalid_xml",
|
|
51
|
+
message: 'Missing or invalid "xml" parameter. Must be an XML string.'
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (position.type !== "root" && !snapshot.getBlock(position.blockId)) {
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
error: {
|
|
59
|
+
error: "block_not_found",
|
|
60
|
+
blockId: position.blockId,
|
|
61
|
+
message: `Block "${position.blockId}" not found.`
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
let nodes;
|
|
66
|
+
try {
|
|
67
|
+
nodes = deserializeNodesFromXml(xml, registry);
|
|
68
|
+
} catch {
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
error: { error: "xml_parse_error", message: "Failed to parse XML string." }
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (nodes.length === 0) {
|
|
75
|
+
return { ok: false, error: { error: "empty_xml", message: "XML produced no nodes." } };
|
|
76
|
+
}
|
|
77
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
78
|
+
const pos = i === 0 ? position : { ...position, _insertIndex: i };
|
|
79
|
+
operations.push({ op: "insert", position: pos, node: nodes[i] });
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
ok: true,
|
|
83
|
+
content: `Inserted ${nodes.length} node(s) ${position.type} block "${position.blockId ?? "root"}"`
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const replaceNodeTool = {
|
|
88
|
+
name: "replace_node",
|
|
89
|
+
description: "Replace an existing block node by its blockId with new XML content. The xml should contain exactly one block element.",
|
|
90
|
+
parameters: {
|
|
91
|
+
type: "object",
|
|
92
|
+
properties: {
|
|
93
|
+
blockId: { type: "string" },
|
|
94
|
+
xml: { type: "string", description: "XML string containing one block element" }
|
|
95
|
+
},
|
|
96
|
+
required: ["blockId", "xml"]
|
|
97
|
+
},
|
|
98
|
+
describeCall: (params) => {
|
|
99
|
+
const p = params;
|
|
100
|
+
return p.blockId ? `replacing block "${p.blockId}"` : "replacing node";
|
|
101
|
+
},
|
|
102
|
+
execute: async (params) => {
|
|
103
|
+
const { blockId, xml } = params;
|
|
104
|
+
if (!xml || typeof xml !== "string") {
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
error: { error: "invalid_xml", message: 'Missing or invalid "xml" parameter.' }
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const existing = snapshot.getBlock(blockId);
|
|
111
|
+
if (!existing) {
|
|
112
|
+
return {
|
|
113
|
+
ok: false,
|
|
114
|
+
error: { error: "block_not_found", blockId, message: `Block "${blockId}" not found.` }
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
let nodes;
|
|
118
|
+
try {
|
|
119
|
+
nodes = deserializeNodesFromXml(xml, registry);
|
|
120
|
+
} catch {
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
error: { error: "xml_parse_error", message: "Failed to parse XML string." }
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (nodes.length === 0) {
|
|
127
|
+
return { ok: false, error: { error: "empty_xml", message: "XML produced no nodes." } };
|
|
128
|
+
}
|
|
129
|
+
const primaryNode = { ...nodes[0], $: { ...nodes[0].$, blockId } };
|
|
130
|
+
operations.push({ op: "replace", blockId, node: primaryNode });
|
|
131
|
+
for (let i = 1; i < nodes.length; i++) {
|
|
132
|
+
operations.push({ op: "insert", position: { type: "after", blockId }, node: nodes[i] });
|
|
133
|
+
}
|
|
134
|
+
return { ok: true, content: `Replaced block "${blockId}" (${nodes.length} node(s))` };
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const deleteNodeTool = {
|
|
138
|
+
name: "delete_node",
|
|
139
|
+
description: "Delete an existing block node by its blockId",
|
|
140
|
+
parameters: {
|
|
141
|
+
type: "object",
|
|
142
|
+
properties: { blockId: { type: "string" } },
|
|
143
|
+
required: ["blockId"]
|
|
144
|
+
},
|
|
145
|
+
describeCall: (params) => {
|
|
146
|
+
const p = params;
|
|
147
|
+
return p.blockId ? `deleting block "${p.blockId}"` : "deleting node";
|
|
148
|
+
},
|
|
149
|
+
execute: async (params) => {
|
|
150
|
+
const { blockId } = params;
|
|
151
|
+
if (!snapshot.getBlock(blockId)) {
|
|
152
|
+
return {
|
|
153
|
+
ok: false,
|
|
154
|
+
error: { error: "block_not_found", blockId, message: `Block "${blockId}" not found.` }
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
operations.push({ op: "delete", blockId });
|
|
158
|
+
return { ok: true, content: `Deleted block "${blockId}"` };
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
const searchDocumentTool = {
|
|
162
|
+
name: "search_document",
|
|
163
|
+
description: "Search for blocks in the document by text content or block type",
|
|
164
|
+
parameters: {
|
|
165
|
+
type: "object",
|
|
166
|
+
properties: {
|
|
167
|
+
query: { type: "string" },
|
|
168
|
+
blockType: { type: "string" }
|
|
169
|
+
},
|
|
170
|
+
required: ["query"]
|
|
171
|
+
},
|
|
172
|
+
describeCall: (params) => {
|
|
173
|
+
const p = params;
|
|
174
|
+
const parts = [];
|
|
175
|
+
if (p.query) parts.push(`"${p.query}"`);
|
|
176
|
+
if (p.blockType) parts.push(`type=${p.blockType}`);
|
|
177
|
+
return `searching ${parts.join(", ") || "document"}`;
|
|
178
|
+
},
|
|
179
|
+
execute: async (params) => {
|
|
180
|
+
const { query, blockType } = params;
|
|
181
|
+
const matches = [];
|
|
182
|
+
for (const blockId of snapshot.blockIds) {
|
|
183
|
+
const block = snapshot.getBlock(blockId);
|
|
184
|
+
const nodeType = block.type ?? "unknown";
|
|
185
|
+
if (blockType && nodeType !== blockType) continue;
|
|
186
|
+
const text = extractText(block);
|
|
187
|
+
if (query && !text.toLowerCase().includes(query.toLowerCase())) continue;
|
|
188
|
+
matches.push({ blockId, nodeType, textContent: text });
|
|
189
|
+
}
|
|
190
|
+
return { ok: true, content: JSON.stringify(matches) };
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
return [insertNodeTool, replaceNodeTool, deleteNodeTool, searchDocumentTool];
|
|
194
|
+
}
|
|
195
|
+
function appendText(base, next) {
|
|
196
|
+
if (!next) return base;
|
|
197
|
+
if (!base) return next;
|
|
198
|
+
return `${base}
|
|
199
|
+
|
|
200
|
+
${next}`;
|
|
201
|
+
}
|
|
202
|
+
function normalizeMessageList(value) {
|
|
203
|
+
if (!value) return [];
|
|
204
|
+
return Array.isArray(value) ? value : [value];
|
|
205
|
+
}
|
|
206
|
+
function getLastItem(items) {
|
|
207
|
+
return items.length > 0 ? items[items.length - 1] : void 0;
|
|
208
|
+
}
|
|
209
|
+
function createInitialDraft(context) {
|
|
210
|
+
return {
|
|
211
|
+
systemMessages: [],
|
|
212
|
+
messages: [...context.messages]
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function draftToPreparedMessages(draft) {
|
|
216
|
+
return {
|
|
217
|
+
systemMessages: draft.systemMessages,
|
|
218
|
+
preambleMessages: draft.messages,
|
|
219
|
+
turns: []
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
class MessagesEngine {
|
|
223
|
+
constructor(processors) {
|
|
224
|
+
this.processors = processors;
|
|
225
|
+
}
|
|
226
|
+
process(context) {
|
|
227
|
+
const draft = this.processors.reduce(
|
|
228
|
+
(nextDraft, processor) => processor.process(nextDraft, context),
|
|
229
|
+
createInitialDraft(context)
|
|
230
|
+
);
|
|
231
|
+
return draftToPreparedMessages(draft);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function buildMessages(preparedMessages) {
|
|
235
|
+
return [
|
|
236
|
+
...preparedMessages.systemMessages,
|
|
237
|
+
...preparedMessages.preambleMessages,
|
|
238
|
+
...preparedMessages.turns
|
|
239
|
+
];
|
|
240
|
+
}
|
|
241
|
+
class BaseMessageEngineProcessor {
|
|
242
|
+
process(draft, context) {
|
|
243
|
+
if (!this.shouldProcess(context, draft)) {
|
|
244
|
+
return draft;
|
|
245
|
+
}
|
|
246
|
+
return this.doProcess(this.cloneDraft(draft), context);
|
|
247
|
+
}
|
|
248
|
+
shouldProcess(_context, _draft) {
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
cloneDraft(draft) {
|
|
252
|
+
return {
|
|
253
|
+
systemMessages: [...draft.systemMessages],
|
|
254
|
+
messages: draft.messages.map(
|
|
255
|
+
(message) => message.role === "user" || message.role === "system" || message.role === "assistant" ? { ...message } : message.role === "assistant_tool_call" ? { ...message, toolCalls: [...message.toolCalls] } : { ...message }
|
|
256
|
+
)
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const SYSTEM_CONTEXT_START = "<!-- SYSTEM CONTEXT (NOT PART OF USER QUERY) -->";
|
|
261
|
+
const SYSTEM_CONTEXT_END = "<!-- END SYSTEM CONTEXT -->";
|
|
262
|
+
const CONTEXT_INSTRUCTION = `<context.instruction>following part contains context information injected by the system. Please follow these instructions:
|
|
263
|
+
|
|
264
|
+
1. Always prioritize handling user-visible content.
|
|
265
|
+
2. the context is only required when user's queries rely on it.
|
|
266
|
+
</context.instruction>`;
|
|
267
|
+
function getUserMessageLocations(draft) {
|
|
268
|
+
const locations = [];
|
|
269
|
+
for (let i = 0; i < draft.messages.length; i++) {
|
|
270
|
+
if (draft.messages[i].role === "user") {
|
|
271
|
+
locations.push({ scope: "messages", index: i });
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return locations;
|
|
275
|
+
}
|
|
276
|
+
function getUserMessage(draft, location) {
|
|
277
|
+
return draft.messages[location.index];
|
|
278
|
+
}
|
|
279
|
+
function setUserMessage(draft, location, message) {
|
|
280
|
+
draft.messages[location.index] = message;
|
|
281
|
+
}
|
|
282
|
+
function hasSystemContextWrapper(content) {
|
|
283
|
+
return content.includes(SYSTEM_CONTEXT_START) && content.includes(SYSTEM_CONTEXT_END);
|
|
284
|
+
}
|
|
285
|
+
function createContextBlock(content, contextType) {
|
|
286
|
+
return `<${contextType}>
|
|
287
|
+
${content}
|
|
288
|
+
</${contextType}>`;
|
|
289
|
+
}
|
|
290
|
+
function wrapWithSystemContext(content, contextType) {
|
|
291
|
+
return `${SYSTEM_CONTEXT_START}
|
|
292
|
+
${CONTEXT_INSTRUCTION}
|
|
293
|
+
<${contextType}>
|
|
294
|
+
${content}
|
|
295
|
+
</${contextType}>
|
|
296
|
+
${SYSTEM_CONTEXT_END}`;
|
|
297
|
+
}
|
|
298
|
+
function insertIntoExistingWrapper(existingContent, newContextBlock) {
|
|
299
|
+
const endMarkerIndex = existingContent.lastIndexOf(SYSTEM_CONTEXT_END);
|
|
300
|
+
if (endMarkerIndex === -1) {
|
|
301
|
+
return appendText(existingContent, newContextBlock);
|
|
302
|
+
}
|
|
303
|
+
const beforeEnd = existingContent.slice(0, endMarkerIndex);
|
|
304
|
+
const afterEnd = existingContent.slice(endMarkerIndex);
|
|
305
|
+
return `${beforeEnd}${newContextBlock}
|
|
306
|
+
${afterEnd}`;
|
|
307
|
+
}
|
|
308
|
+
function appendSystemContextToUserMessage(message, content, contextType) {
|
|
309
|
+
const currentContent = message.content;
|
|
310
|
+
if (hasSystemContextWrapper(currentContent)) {
|
|
311
|
+
return {
|
|
312
|
+
...message,
|
|
313
|
+
content: insertIntoExistingWrapper(currentContent, createContextBlock(content, contextType))
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
...message,
|
|
318
|
+
content: appendText(currentContent, wrapWithSystemContext(content, contextType))
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
class BaseEveryUserContentProvider extends BaseMessageEngineProcessor {
|
|
322
|
+
doProcess(draft, context) {
|
|
323
|
+
const locations = getUserMessageLocations(draft);
|
|
324
|
+
const lastIndex = locations.length - 1;
|
|
325
|
+
for (let i = 0; i < locations.length; i++) {
|
|
326
|
+
const location = locations[i];
|
|
327
|
+
const message = getUserMessage(draft, location);
|
|
328
|
+
const nextContent = this.buildContentForMessage(message, i, i === lastIndex, context);
|
|
329
|
+
if (!nextContent) continue;
|
|
330
|
+
setUserMessage(
|
|
331
|
+
draft,
|
|
332
|
+
location,
|
|
333
|
+
appendSystemContextToUserMessage(message, nextContent.content, nextContent.contextType)
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
return draft;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
class BaseFirstUserContentProvider extends BaseMessageEngineProcessor {
|
|
340
|
+
doProcess(draft, context) {
|
|
341
|
+
const messages = this.buildMessages(context);
|
|
342
|
+
if (!messages) return draft;
|
|
343
|
+
const firstUserIndex = draft.messages.findIndex((message) => message.role === "user");
|
|
344
|
+
if (firstUserIndex === -1) return draft;
|
|
345
|
+
draft.messages.splice(firstUserIndex, 0, ...normalizeMessageList(messages));
|
|
346
|
+
return draft;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
class BaseLastUserContentProvider extends BaseMessageEngineProcessor {
|
|
350
|
+
doProcess(draft, context) {
|
|
351
|
+
const locations = getUserMessageLocations(draft);
|
|
352
|
+
const lastLocation = getLastItem(locations);
|
|
353
|
+
if (!lastLocation) return draft;
|
|
354
|
+
const nextContent = this.buildContent(context);
|
|
355
|
+
if (!nextContent) return draft;
|
|
356
|
+
const message = getUserMessage(draft, lastLocation);
|
|
357
|
+
setUserMessage(
|
|
358
|
+
draft,
|
|
359
|
+
lastLocation,
|
|
360
|
+
appendSystemContextToUserMessage(message, nextContent.content, nextContent.contextType)
|
|
361
|
+
);
|
|
362
|
+
return draft;
|
|
363
|
+
}
|
|
364
|
+
hasExistingSystemContext(draft) {
|
|
365
|
+
const locations = getUserMessageLocations(draft);
|
|
366
|
+
const lastLocation = getLastItem(locations);
|
|
367
|
+
if (!lastLocation) return false;
|
|
368
|
+
return hasSystemContextWrapper(getUserMessage(draft, lastLocation).content);
|
|
369
|
+
}
|
|
370
|
+
wrapWithSystemContext(content, contextType) {
|
|
371
|
+
return wrapWithSystemContext(content, contextType);
|
|
372
|
+
}
|
|
373
|
+
createContextBlock(content, contextType) {
|
|
374
|
+
return createContextBlock(content, contextType);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
class BaseSystemRoleProvider extends BaseMessageEngineProcessor {
|
|
378
|
+
doProcess(draft, context) {
|
|
379
|
+
const content = this.buildContent(context)?.trim();
|
|
380
|
+
if (!content) return draft;
|
|
381
|
+
const lastSystemMessage = getLastItem(draft.systemMessages);
|
|
382
|
+
if (!lastSystemMessage) {
|
|
383
|
+
draft.systemMessages = [{ role: "system", content }];
|
|
384
|
+
return draft;
|
|
385
|
+
}
|
|
386
|
+
draft.systemMessages[draft.systemMessages.length - 1] = {
|
|
387
|
+
...lastSystemMessage,
|
|
388
|
+
content: appendText(lastSystemMessage.content, content)
|
|
389
|
+
};
|
|
390
|
+
return draft;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
class BaseSystemRootProvider extends BaseMessageEngineProcessor {
|
|
394
|
+
doProcess(draft, context) {
|
|
395
|
+
const messages = this.buildMessages(context);
|
|
396
|
+
if (!messages) return draft;
|
|
397
|
+
draft.systemMessages = normalizeMessageList(messages);
|
|
398
|
+
return draft;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function describeToolCall(tool, params) {
|
|
402
|
+
if (tool?.describeCall) {
|
|
403
|
+
try {
|
|
404
|
+
return tool.describeCall(params);
|
|
405
|
+
} catch {
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
const firstVal = Object.values(params)[0];
|
|
409
|
+
return firstVal !== void 0 ? String(firstVal).slice(0, 40) : void 0;
|
|
410
|
+
}
|
|
411
|
+
function splitSteps(text) {
|
|
412
|
+
return text.split(/\n{2,}/).map((s) => s.trim()).filter(Boolean);
|
|
413
|
+
}
|
|
414
|
+
let groupCounter = 0;
|
|
415
|
+
function nextGroupId() {
|
|
416
|
+
return `tcg-${++groupCounter}-${Date.now()}`;
|
|
417
|
+
}
|
|
418
|
+
let thinkingCounter = 0;
|
|
419
|
+
function nextThinkingId() {
|
|
420
|
+
return `th-${++thinkingCounter}-${Date.now()}`;
|
|
421
|
+
}
|
|
422
|
+
function toolConfigToSchema(tool) {
|
|
423
|
+
return {
|
|
424
|
+
name: tool.name,
|
|
425
|
+
description: tool.description,
|
|
426
|
+
parameters: tool.parameters
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
function createAgentExecutor(config) {
|
|
430
|
+
const { provider, snapshot, store, signal, onOperationsChanged } = config;
|
|
431
|
+
const operations = [];
|
|
432
|
+
let lastOpsLength = 0;
|
|
433
|
+
const documentTools = createDocumentTools(snapshot, operations);
|
|
434
|
+
const allTools = [...documentTools, ...config.tools];
|
|
435
|
+
const toolMap = new Map(allTools.map((t) => [t.name, t]));
|
|
436
|
+
const toolSchemas = allTools.map(toolConfigToSchema);
|
|
437
|
+
async function executeTool(name, args) {
|
|
438
|
+
const tool = toolMap.get(name);
|
|
439
|
+
if (!tool) {
|
|
440
|
+
return { ok: false, error: { error: "unknown_tool", message: `Tool "${name}" not found` } };
|
|
441
|
+
}
|
|
442
|
+
const params = JSON.parse(args);
|
|
443
|
+
return tool.execute(params);
|
|
444
|
+
}
|
|
445
|
+
async function run(initialMessages) {
|
|
446
|
+
const { addBubble, setStatus, updateLastBubble } = store.getState();
|
|
447
|
+
setStatus("running");
|
|
448
|
+
const turns = [];
|
|
449
|
+
const maxTurns = 20;
|
|
450
|
+
for (let turn = 0; turn < maxTurns; turn++) {
|
|
451
|
+
signal?.throwIfAborted();
|
|
452
|
+
const messages = buildMessages({
|
|
453
|
+
...initialMessages,
|
|
454
|
+
turns
|
|
455
|
+
});
|
|
456
|
+
let textAccum = "";
|
|
457
|
+
let thinkingAccum = "";
|
|
458
|
+
let hasThinking = false;
|
|
459
|
+
let thinkingId = "";
|
|
460
|
+
const toolCalls = [];
|
|
461
|
+
setStatus("thinking");
|
|
462
|
+
addBubble({ type: "assistant", content: "", streaming: true });
|
|
463
|
+
for await (const chunk of provider.chat(messages, toolSchemas)) {
|
|
464
|
+
signal?.throwIfAborted();
|
|
465
|
+
if (chunk.type === "thinking") {
|
|
466
|
+
thinkingAccum += chunk.text;
|
|
467
|
+
if (!hasThinking) {
|
|
468
|
+
hasThinking = true;
|
|
469
|
+
thinkingId = nextThinkingId();
|
|
470
|
+
updateLastBubble({
|
|
471
|
+
type: "thinking",
|
|
472
|
+
content: chunk.text,
|
|
473
|
+
id: thinkingId,
|
|
474
|
+
rawText: chunk.text,
|
|
475
|
+
steps: [],
|
|
476
|
+
isStreaming: true
|
|
477
|
+
});
|
|
478
|
+
} else {
|
|
479
|
+
updateLastBubble({
|
|
480
|
+
type: "thinking",
|
|
481
|
+
content: thinkingAccum,
|
|
482
|
+
id: thinkingId,
|
|
483
|
+
rawText: thinkingAccum,
|
|
484
|
+
steps: [],
|
|
485
|
+
isStreaming: true
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
if (chunk.type === "text") {
|
|
491
|
+
if (hasThinking && textAccum === "") {
|
|
492
|
+
addBubble({ type: "assistant", content: chunk.text, streaming: true });
|
|
493
|
+
setStatus("writing");
|
|
494
|
+
} else if (textAccum === "") {
|
|
495
|
+
setStatus("writing");
|
|
496
|
+
updateLastBubble({ type: "assistant", content: chunk.text, streaming: true });
|
|
497
|
+
} else {
|
|
498
|
+
updateLastBubble({
|
|
499
|
+
type: "assistant",
|
|
500
|
+
content: textAccum + chunk.text,
|
|
501
|
+
streaming: true
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
textAccum += chunk.text;
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
if (chunk.type === "tool_call") {
|
|
508
|
+
toolCalls.push({ id: chunk.id, name: chunk.name, arguments: chunk.arguments });
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (hasThinking) {
|
|
512
|
+
const { bubbles } = store.getState();
|
|
513
|
+
const thinkingIdx = bubbles.findIndex(
|
|
514
|
+
(b) => b.type === "thinking" && "id" in b && b.id === thinkingId
|
|
515
|
+
);
|
|
516
|
+
if (thinkingIdx !== -1) {
|
|
517
|
+
const nextBubbles = [...bubbles];
|
|
518
|
+
nextBubbles[thinkingIdx] = {
|
|
519
|
+
type: "thinking",
|
|
520
|
+
content: thinkingAccum,
|
|
521
|
+
id: thinkingId,
|
|
522
|
+
rawText: thinkingAccum,
|
|
523
|
+
steps: splitSteps(thinkingAccum),
|
|
524
|
+
isStreaming: false
|
|
525
|
+
};
|
|
526
|
+
store.setState({ bubbles: nextBubbles });
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
updateLastBubble({ type: "assistant", content: textAccum, streaming: false });
|
|
530
|
+
if (toolCalls.length === 0) break;
|
|
531
|
+
turns.push({ role: "assistant_tool_call", toolCalls });
|
|
532
|
+
setStatus("calling_tool");
|
|
533
|
+
const groupId = nextGroupId();
|
|
534
|
+
const items = [];
|
|
535
|
+
for (const tc of toolCalls) {
|
|
536
|
+
let params;
|
|
537
|
+
try {
|
|
538
|
+
params = JSON.parse(tc.arguments);
|
|
539
|
+
} catch {
|
|
540
|
+
params = {};
|
|
541
|
+
}
|
|
542
|
+
items.push({
|
|
543
|
+
id: tc.id,
|
|
544
|
+
toolName: tc.name,
|
|
545
|
+
description: describeToolCall(toolMap.get(tc.name), params),
|
|
546
|
+
params,
|
|
547
|
+
status: "pending"
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
addBubble({ type: "tool_call_group", id: groupId, items });
|
|
551
|
+
const { updateToolCallItem } = store.getState();
|
|
552
|
+
for (let i = 0; i < toolCalls.length; i++) {
|
|
553
|
+
const tc = toolCalls[i];
|
|
554
|
+
updateToolCallItem(groupId, tc.id, {
|
|
555
|
+
status: "running",
|
|
556
|
+
startedAt: Date.now()
|
|
557
|
+
});
|
|
558
|
+
try {
|
|
559
|
+
JSON.parse(tc.arguments);
|
|
560
|
+
} catch (e) {
|
|
561
|
+
updateToolCallItem(groupId, tc.id, {
|
|
562
|
+
status: "error",
|
|
563
|
+
error: `JSON parse error: ${e.message}`,
|
|
564
|
+
finishedAt: Date.now()
|
|
565
|
+
});
|
|
566
|
+
turns.push({
|
|
567
|
+
role: "tool_result",
|
|
568
|
+
toolCallId: tc.id,
|
|
569
|
+
content: `JSON parse error: ${e.message}`,
|
|
570
|
+
isError: true
|
|
571
|
+
});
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
const result = await executeTool(tc.name, tc.arguments);
|
|
575
|
+
const content = result.ok ? result.content : JSON.stringify(result.error);
|
|
576
|
+
updateToolCallItem(groupId, tc.id, {
|
|
577
|
+
status: result.ok ? "completed" : "error",
|
|
578
|
+
result: result.ok ? result.content : void 0,
|
|
579
|
+
resultPreview: result.ok ? result.content.slice(0, 80) : void 0,
|
|
580
|
+
error: !result.ok ? content : void 0,
|
|
581
|
+
finishedAt: Date.now()
|
|
582
|
+
});
|
|
583
|
+
turns.push({
|
|
584
|
+
role: "tool_result",
|
|
585
|
+
toolCallId: tc.id,
|
|
586
|
+
content,
|
|
587
|
+
isError: !result.ok
|
|
588
|
+
});
|
|
589
|
+
if (operations.length > lastOpsLength) {
|
|
590
|
+
lastOpsLength = operations.length;
|
|
591
|
+
onOperationsChanged?.(operations);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
setStatus("done");
|
|
596
|
+
return { operations };
|
|
597
|
+
}
|
|
598
|
+
return { run };
|
|
599
|
+
}
|
|
600
|
+
function createSnapshot(editorState) {
|
|
601
|
+
const root = editorState.root;
|
|
602
|
+
const children = root.children ?? [];
|
|
603
|
+
const blockMap = /* @__PURE__ */ new Map();
|
|
604
|
+
const blockIds = [];
|
|
605
|
+
for (const child of children) {
|
|
606
|
+
const blockId = child.$?.blockId;
|
|
607
|
+
if (blockId) {
|
|
608
|
+
blockMap.set(blockId, child);
|
|
609
|
+
blockIds.push(blockId);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return {
|
|
613
|
+
raw: editorState,
|
|
614
|
+
blockIds,
|
|
615
|
+
getBlock: (blockId) => blockMap.get(blockId)
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
function compareBlockContent(a, b) {
|
|
619
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
620
|
+
}
|
|
621
|
+
function makeDiffState(entries) {
|
|
622
|
+
return {
|
|
623
|
+
entries,
|
|
624
|
+
getByBlockId(blockId) {
|
|
625
|
+
return entries.find((e) => {
|
|
626
|
+
if (e.op.op === "replace" || e.op.op === "delete") return e.op.blockId === blockId;
|
|
627
|
+
if (e.op.op === "insert" && e.op.position.type !== "root")
|
|
628
|
+
return e.op.position.blockId === blockId;
|
|
629
|
+
return false;
|
|
630
|
+
});
|
|
631
|
+
},
|
|
632
|
+
getPending() {
|
|
633
|
+
return entries.filter((e) => e.status === "pending");
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
function createDiffEngine(operations, editorState) {
|
|
638
|
+
const snap = createSnapshot(editorState);
|
|
639
|
+
const entries = operations.map((op) => {
|
|
640
|
+
let originalNode;
|
|
641
|
+
if (op.op === "replace" || op.op === "delete") {
|
|
642
|
+
originalNode = snap.getBlock(op.blockId);
|
|
643
|
+
}
|
|
644
|
+
return {
|
|
645
|
+
id: nanoid(8),
|
|
646
|
+
op,
|
|
647
|
+
status: "pending",
|
|
648
|
+
originalNode
|
|
649
|
+
};
|
|
650
|
+
});
|
|
651
|
+
return makeDiffState(entries);
|
|
652
|
+
}
|
|
653
|
+
function updateEntry(state, entryId, status) {
|
|
654
|
+
const entries = state.entries.map((e) => e.id === entryId ? { ...e, status } : e);
|
|
655
|
+
return makeDiffState(entries);
|
|
656
|
+
}
|
|
657
|
+
function acceptDiff(state, entryId) {
|
|
658
|
+
return updateEntry(state, entryId, "accepted");
|
|
659
|
+
}
|
|
660
|
+
function rejectDiff(state, entryId) {
|
|
661
|
+
return updateEntry(state, entryId, "rejected");
|
|
662
|
+
}
|
|
663
|
+
function acceptAllDiffs(state) {
|
|
664
|
+
const entries = state.entries.map(
|
|
665
|
+
(e) => e.status === "pending" ? { ...e, status: "accepted" } : e
|
|
666
|
+
);
|
|
667
|
+
return makeDiffState(entries);
|
|
668
|
+
}
|
|
669
|
+
function rejectAllDiffs(state) {
|
|
670
|
+
const entries = state.entries.map(
|
|
671
|
+
(e) => e.status === "pending" ? { ...e, status: "rejected" } : e
|
|
672
|
+
);
|
|
673
|
+
return makeDiffState(entries);
|
|
674
|
+
}
|
|
675
|
+
function buildDocumentContext(editorState, options) {
|
|
676
|
+
const registry = createDefaultRegistry();
|
|
677
|
+
return serializeToXml(editorState, registry, { compact: options.compact ?? true });
|
|
678
|
+
}
|
|
679
|
+
function createInitialAgentStoreState() {
|
|
680
|
+
return {
|
|
681
|
+
status: "idle",
|
|
682
|
+
bubbles: [],
|
|
683
|
+
diffState: null,
|
|
684
|
+
reviewState: null
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
function getBlockId(node) {
|
|
688
|
+
return node.$?.blockId;
|
|
689
|
+
}
|
|
690
|
+
function resolveRootInsertIndex(index, length) {
|
|
691
|
+
const resolved = index ?? length;
|
|
692
|
+
return Math.min(Math.max(resolved, 0), length);
|
|
693
|
+
}
|
|
694
|
+
function getChildBlockId(children, index) {
|
|
695
|
+
const child = children[index];
|
|
696
|
+
return child ? getBlockId(child) : void 0;
|
|
697
|
+
}
|
|
698
|
+
function getInsertAnchors(children, position) {
|
|
699
|
+
if (position.type === "root") {
|
|
700
|
+
const index2 = resolveRootInsertIndex(position.index, children.length);
|
|
701
|
+
const anchorBeforeId = getChildBlockId(children, index2 - 1);
|
|
702
|
+
const anchorAfterId = getChildBlockId(children, index2);
|
|
703
|
+
return {
|
|
704
|
+
targetBlockId: anchorAfterId ?? anchorBeforeId,
|
|
705
|
+
anchorBeforeId,
|
|
706
|
+
anchorAfterId
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
const index = children.findIndex((child) => getBlockId(child) === position.blockId);
|
|
710
|
+
if (index === -1) {
|
|
711
|
+
return {
|
|
712
|
+
targetBlockId: position.blockId,
|
|
713
|
+
anchorBeforeId: position.type === "after" ? position.blockId : void 0,
|
|
714
|
+
anchorAfterId: position.type === "before" ? position.blockId : void 0
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
if (position.type === "after") {
|
|
718
|
+
return {
|
|
719
|
+
targetBlockId: position.blockId,
|
|
720
|
+
anchorBeforeId: getChildBlockId(children, index),
|
|
721
|
+
anchorAfterId: getChildBlockId(children, index + 1)
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
return {
|
|
725
|
+
targetBlockId: position.blockId,
|
|
726
|
+
anchorBeforeId: getChildBlockId(children, index - 1),
|
|
727
|
+
anchorAfterId: getChildBlockId(children, index)
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
function cloneChildren(root) {
|
|
731
|
+
return [...root.children ?? []].map((c) => ({ ...c }));
|
|
732
|
+
}
|
|
733
|
+
function snapshotChildren(snapshot) {
|
|
734
|
+
return snapshot.root.children ?? [];
|
|
735
|
+
}
|
|
736
|
+
function snapshotHasBlock(snapshot, blockId) {
|
|
737
|
+
return snapshotChildren(snapshot).some((child) => getBlockId(child) === blockId);
|
|
738
|
+
}
|
|
739
|
+
function canApplyOpToSnapshot(snapshot, op) {
|
|
740
|
+
if (op.op === "insert") {
|
|
741
|
+
return op.position.type === "root" || snapshotHasBlock(snapshot, op.position.blockId);
|
|
742
|
+
}
|
|
743
|
+
return snapshotHasBlock(snapshot, op.blockId);
|
|
744
|
+
}
|
|
745
|
+
function applyOpsToSnapshot(base, ops) {
|
|
746
|
+
let children = cloneChildren(base.root);
|
|
747
|
+
for (const op of ops) {
|
|
748
|
+
if (op.op === "insert") {
|
|
749
|
+
if (!op.node?.type) continue;
|
|
750
|
+
const pos = op.position;
|
|
751
|
+
if (pos.type === "root") {
|
|
752
|
+
const idx = resolveRootInsertIndex(pos.index, children.length);
|
|
753
|
+
children.splice(idx, 0, op.node);
|
|
754
|
+
} else {
|
|
755
|
+
const idx = children.findIndex((c) => getBlockId(c) === pos.blockId);
|
|
756
|
+
if (idx === -1) continue;
|
|
757
|
+
const insertIdx = pos.type === "after" ? idx + 1 : idx;
|
|
758
|
+
children.splice(insertIdx, 0, op.node);
|
|
759
|
+
}
|
|
760
|
+
} else if (op.op === "replace") {
|
|
761
|
+
if (!op.node?.type) continue;
|
|
762
|
+
const idx = children.findIndex((c) => getBlockId(c) === op.blockId);
|
|
763
|
+
if (idx === -1) continue;
|
|
764
|
+
children[idx] = op.node;
|
|
765
|
+
} else if (op.op === "delete") {
|
|
766
|
+
children = children.filter((c) => getBlockId(c) !== op.blockId);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
return {
|
|
770
|
+
root: { ...base.root, children }
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
function fingerprint(node) {
|
|
774
|
+
return JSON.stringify(node);
|
|
775
|
+
}
|
|
776
|
+
function extractTouchedBlockIds(entries) {
|
|
777
|
+
const ids = /* @__PURE__ */ new Set();
|
|
778
|
+
for (const entry of entries) {
|
|
779
|
+
if (entry.targetBlockId) ids.add(entry.targetBlockId);
|
|
780
|
+
}
|
|
781
|
+
return [...ids];
|
|
782
|
+
}
|
|
783
|
+
function updateResolvedBatch(state, batchId, status) {
|
|
784
|
+
return {
|
|
785
|
+
...state,
|
|
786
|
+
batches: state.batches.map(
|
|
787
|
+
(batch) => batch.id === batchId ? {
|
|
788
|
+
...batch,
|
|
789
|
+
status,
|
|
790
|
+
entries: batch.entries.map((entry) => ({ ...entry, status }))
|
|
791
|
+
} : batch
|
|
792
|
+
)
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
function batchEntriesMatch(left, right, predicate) {
|
|
796
|
+
for (const leftEntry of left.entries) {
|
|
797
|
+
for (const rightEntry of right.entries) {
|
|
798
|
+
if (predicate(leftEntry, rightEntry)) {
|
|
799
|
+
return true;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
return false;
|
|
804
|
+
}
|
|
805
|
+
function createReviewBatch(ops, baseSnapshot, baseRevision) {
|
|
806
|
+
const previewSnapshot = applyOpsToSnapshot(baseSnapshot, ops);
|
|
807
|
+
const root = baseSnapshot.root;
|
|
808
|
+
const baseChildren = root.children ?? [];
|
|
809
|
+
const blockMap = /* @__PURE__ */ new Map();
|
|
810
|
+
for (const child of baseChildren) {
|
|
811
|
+
const id = getBlockId(child);
|
|
812
|
+
if (id) blockMap.set(id, child);
|
|
813
|
+
}
|
|
814
|
+
const entries = ops.filter((op) => {
|
|
815
|
+
if (op.op === "insert" || op.op === "replace") return !!op.node?.type;
|
|
816
|
+
return true;
|
|
817
|
+
}).map((op) => {
|
|
818
|
+
let targetBlockId;
|
|
819
|
+
let anchorBeforeId;
|
|
820
|
+
let anchorAfterId;
|
|
821
|
+
let fp = "";
|
|
822
|
+
if (op.op === "replace" || op.op === "delete") {
|
|
823
|
+
targetBlockId = op.blockId;
|
|
824
|
+
const orig = blockMap.get(op.blockId);
|
|
825
|
+
if (orig) fp = fingerprint(orig);
|
|
826
|
+
} else if (op.op === "insert") {
|
|
827
|
+
({ targetBlockId, anchorBeforeId, anchorAfterId } = getInsertAnchors(
|
|
828
|
+
baseChildren,
|
|
829
|
+
op.position
|
|
830
|
+
));
|
|
831
|
+
}
|
|
832
|
+
return {
|
|
833
|
+
id: nanoid(8),
|
|
834
|
+
op,
|
|
835
|
+
targetBlockId,
|
|
836
|
+
anchorBeforeId,
|
|
837
|
+
anchorAfterId,
|
|
838
|
+
originalFingerprint: fp,
|
|
839
|
+
status: "pending"
|
|
840
|
+
};
|
|
841
|
+
});
|
|
842
|
+
return {
|
|
843
|
+
id: nanoid(8),
|
|
844
|
+
baseRevision,
|
|
845
|
+
baseSnapshot,
|
|
846
|
+
previewSnapshot,
|
|
847
|
+
status: "pending",
|
|
848
|
+
entries,
|
|
849
|
+
touchedBlockIds: extractTouchedBlockIds(entries)
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
function acceptBatch(state, batchId) {
|
|
853
|
+
return updateResolvedBatch(state, batchId, "accepted");
|
|
854
|
+
}
|
|
855
|
+
function rejectBatch(state, batchId) {
|
|
856
|
+
return updateResolvedBatch(state, batchId, "rejected");
|
|
857
|
+
}
|
|
858
|
+
function batchesConflict(left, right) {
|
|
859
|
+
return batchEntriesMatch(left, right, entriesConflict);
|
|
860
|
+
}
|
|
861
|
+
function batchesAreOrderDependent(left, right) {
|
|
862
|
+
return batchEntriesMatch(left, right, entriesAreOrderDependent);
|
|
863
|
+
}
|
|
864
|
+
function entriesConflict(left, right) {
|
|
865
|
+
const leftOp = left.op;
|
|
866
|
+
const rightOp = right.op;
|
|
867
|
+
if (leftOp.op === "insert" && rightOp.op === "insert") {
|
|
868
|
+
return false;
|
|
869
|
+
}
|
|
870
|
+
if (leftOp.op === "delete") {
|
|
871
|
+
return conflictsWithDeletedBlock(leftOp.blockId, right);
|
|
872
|
+
}
|
|
873
|
+
if (rightOp.op === "delete") {
|
|
874
|
+
return conflictsWithDeletedBlock(rightOp.blockId, left);
|
|
875
|
+
}
|
|
876
|
+
if (leftOp.op === "replace" && rightOp.op === "replace") {
|
|
877
|
+
return leftOp.blockId === rightOp.blockId;
|
|
878
|
+
}
|
|
879
|
+
return false;
|
|
880
|
+
}
|
|
881
|
+
function entriesAreOrderDependent(left, right) {
|
|
882
|
+
if (left.op.op !== "insert" || right.op.op !== "insert") {
|
|
883
|
+
return false;
|
|
884
|
+
}
|
|
885
|
+
return left.anchorBeforeId === right.anchorBeforeId && left.anchorAfterId === right.anchorAfterId;
|
|
886
|
+
}
|
|
887
|
+
function conflictsWithDeletedBlock(deletedBlockId, entry) {
|
|
888
|
+
const op = entry.op;
|
|
889
|
+
if (op.op === "delete" || op.op === "replace") {
|
|
890
|
+
return op.blockId === deletedBlockId;
|
|
891
|
+
}
|
|
892
|
+
return entry.targetBlockId === deletedBlockId || entry.anchorBeforeId === deletedBlockId || entry.anchorAfterId === deletedBlockId;
|
|
893
|
+
}
|
|
894
|
+
function isFinalBatchStatus(status) {
|
|
895
|
+
return status === "accepted" || status === "rejected";
|
|
896
|
+
}
|
|
897
|
+
function getInitialBatchStatus(batch, documentRevision) {
|
|
898
|
+
return batch.baseRevision < documentRevision ? "conflicted" : "pending";
|
|
899
|
+
}
|
|
900
|
+
function updateStatusUnlessConflicted(statuses, batchId, status) {
|
|
901
|
+
if (statuses.get(batchId) === "conflicted") {
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
statuses.set(batchId, status);
|
|
905
|
+
}
|
|
906
|
+
function rebaseBatch(batch, baseSnapshot, baseRevision) {
|
|
907
|
+
const ops = batch.entries.map((entry) => entry.op);
|
|
908
|
+
if (!ops.every((op) => canApplyOpToSnapshot(baseSnapshot, op))) {
|
|
909
|
+
return batch;
|
|
910
|
+
}
|
|
911
|
+
const rebased = createReviewBatch(ops, baseSnapshot, baseRevision);
|
|
912
|
+
return {
|
|
913
|
+
...rebased,
|
|
914
|
+
id: batch.id
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
function reconcileReviewBatches(state) {
|
|
918
|
+
const nextStatuses = /* @__PURE__ */ new Map();
|
|
919
|
+
const activeBatches = state.batches.filter((batch) => !isFinalBatchStatus(batch.status));
|
|
920
|
+
const currentRevisionBatches = activeBatches.filter(
|
|
921
|
+
(batch) => batch.baseRevision === state.documentRevision
|
|
922
|
+
);
|
|
923
|
+
for (const batch of activeBatches) {
|
|
924
|
+
nextStatuses.set(batch.id, getInitialBatchStatus(batch, state.documentRevision));
|
|
925
|
+
}
|
|
926
|
+
for (let i = 0; i < currentRevisionBatches.length; i++) {
|
|
927
|
+
for (let j = i + 1; j < currentRevisionBatches.length; j++) {
|
|
928
|
+
const left = currentRevisionBatches[i];
|
|
929
|
+
const right = currentRevisionBatches[j];
|
|
930
|
+
if (batchesConflict(left, right)) {
|
|
931
|
+
nextStatuses.set(left.id, "conflicted");
|
|
932
|
+
nextStatuses.set(right.id, "conflicted");
|
|
933
|
+
continue;
|
|
934
|
+
}
|
|
935
|
+
if (batchesAreOrderDependent(left, right)) {
|
|
936
|
+
updateStatusUnlessConflicted(nextStatuses, left.id, "order_dependent");
|
|
937
|
+
updateStatusUnlessConflicted(nextStatuses, right.id, "order_dependent");
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
return {
|
|
942
|
+
...state,
|
|
943
|
+
batches: state.batches.map(
|
|
944
|
+
(batch) => isFinalBatchStatus(batch.status) ? batch : { ...batch, status: nextStatuses.get(batch.id) ?? batch.status }
|
|
945
|
+
)
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
function acceptAndRebaseBatch(state, batchId) {
|
|
949
|
+
const acceptedState = acceptBatch(state, batchId);
|
|
950
|
+
const acceptedBatch = acceptedState.batches.find((batch) => batch.id === batchId);
|
|
951
|
+
if (!acceptedBatch) {
|
|
952
|
+
return state;
|
|
953
|
+
}
|
|
954
|
+
const nextRevision = state.documentRevision + 1;
|
|
955
|
+
const nextState = {
|
|
956
|
+
...acceptedState,
|
|
957
|
+
documentRevision: nextRevision,
|
|
958
|
+
batches: acceptedState.batches.map((batch) => {
|
|
959
|
+
if (batch.id === batchId || isFinalBatchStatus(batch.status)) {
|
|
960
|
+
return batch;
|
|
961
|
+
}
|
|
962
|
+
return rebaseBatch(batch, acceptedBatch.previewSnapshot, nextRevision);
|
|
963
|
+
})
|
|
964
|
+
};
|
|
965
|
+
return reconcileReviewBatches(nextState);
|
|
966
|
+
}
|
|
967
|
+
function resolveReviewEntry(state, batchId, entryId, resolution) {
|
|
968
|
+
return {
|
|
969
|
+
...state,
|
|
970
|
+
batches: state.batches.map((batch) => {
|
|
971
|
+
if (batch.id !== batchId) return batch;
|
|
972
|
+
const entries = batch.entries.map(
|
|
973
|
+
(entry) => entry.id === entryId ? { ...entry, status: resolution } : entry
|
|
974
|
+
);
|
|
975
|
+
const allResolved = entries.every((e) => e.status === "accepted" || e.status === "rejected");
|
|
976
|
+
let batchStatus = batch.status;
|
|
977
|
+
if (allResolved) {
|
|
978
|
+
const allRejected = entries.every((e) => e.status === "rejected");
|
|
979
|
+
batchStatus = allRejected ? "rejected" : "accepted";
|
|
980
|
+
}
|
|
981
|
+
return { ...batch, entries, status: batchStatus };
|
|
982
|
+
})
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
function detectConflicts(state) {
|
|
986
|
+
return reconcileReviewBatches(state);
|
|
987
|
+
}
|
|
988
|
+
const agentStoreSelectors = {
|
|
989
|
+
bubbles: (state) => state.bubbles,
|
|
990
|
+
diffState: (state) => state.diffState,
|
|
991
|
+
reviewState: (state) => state.reviewState,
|
|
992
|
+
status: (state) => state.status
|
|
993
|
+
};
|
|
994
|
+
function createAgentStoreSlice(set, get, api) {
|
|
995
|
+
return new AgentStoreActionImpl(set, get, api);
|
|
996
|
+
}
|
|
997
|
+
class AgentStoreActionImpl {
|
|
998
|
+
constructor(set, get, api) {
|
|
999
|
+
__privateAdd(this, _get);
|
|
1000
|
+
__privateAdd(this, _set);
|
|
1001
|
+
this.addBubble = (bubble) => {
|
|
1002
|
+
__privateGet(this, _set).call(this, (state) => ({ bubbles: [...state.bubbles, bubble] }));
|
|
1003
|
+
};
|
|
1004
|
+
this.reset = () => {
|
|
1005
|
+
__privateGet(this, _set).call(this, createInitialAgentStoreState());
|
|
1006
|
+
};
|
|
1007
|
+
this.setDiffState = (diffState) => {
|
|
1008
|
+
__privateGet(this, _set).call(this, { diffState });
|
|
1009
|
+
};
|
|
1010
|
+
this.addReviewBatch = (batch) => {
|
|
1011
|
+
__privateGet(this, _set).call(this, (state) => {
|
|
1012
|
+
const current = state.reviewState ?? { documentRevision: 0, batches: [] };
|
|
1013
|
+
const next = {
|
|
1014
|
+
...current,
|
|
1015
|
+
batches: [...current.batches, batch]
|
|
1016
|
+
};
|
|
1017
|
+
return { reviewState: reconcileReviewBatches(next) };
|
|
1018
|
+
});
|
|
1019
|
+
};
|
|
1020
|
+
this.acceptReviewBatch = (batchId) => {
|
|
1021
|
+
__privateGet(this, _set).call(this, (state) => {
|
|
1022
|
+
if (!state.reviewState) return {};
|
|
1023
|
+
return { reviewState: acceptAndRebaseBatch(state.reviewState, batchId) };
|
|
1024
|
+
});
|
|
1025
|
+
};
|
|
1026
|
+
this.acceptReviewEntry = (batchId, entryId) => {
|
|
1027
|
+
__privateGet(this, _set).call(this, (state) => {
|
|
1028
|
+
if (!state.reviewState) return {};
|
|
1029
|
+
return { reviewState: resolveReviewEntry(state.reviewState, batchId, entryId, "accepted") };
|
|
1030
|
+
});
|
|
1031
|
+
};
|
|
1032
|
+
this.rejectReviewBatch = (batchId) => {
|
|
1033
|
+
__privateGet(this, _set).call(this, (state) => {
|
|
1034
|
+
if (!state.reviewState) return {};
|
|
1035
|
+
return { reviewState: reconcileReviewBatches(rejectBatch(state.reviewState, batchId)) };
|
|
1036
|
+
});
|
|
1037
|
+
};
|
|
1038
|
+
this.rejectReviewEntry = (batchId, entryId) => {
|
|
1039
|
+
__privateGet(this, _set).call(this, (state) => {
|
|
1040
|
+
if (!state.reviewState) return {};
|
|
1041
|
+
return { reviewState: resolveReviewEntry(state.reviewState, batchId, entryId, "rejected") };
|
|
1042
|
+
});
|
|
1043
|
+
};
|
|
1044
|
+
this.setReviewState = (reviewState) => {
|
|
1045
|
+
__privateGet(this, _set).call(this, { reviewState });
|
|
1046
|
+
};
|
|
1047
|
+
this.setStatus = (status) => {
|
|
1048
|
+
__privateGet(this, _set).call(this, { status });
|
|
1049
|
+
};
|
|
1050
|
+
this.updateToolCallItem = (groupId, itemId, patch) => {
|
|
1051
|
+
__privateGet(this, _set).call(this, (state) => {
|
|
1052
|
+
const idx = state.bubbles.findIndex((b) => b.type === "tool_call_group" && b.id === groupId);
|
|
1053
|
+
if (idx === -1) return {};
|
|
1054
|
+
const group = state.bubbles[idx];
|
|
1055
|
+
const itemIdx = group.items.findIndex((item) => item.id === itemId);
|
|
1056
|
+
if (itemIdx === -1) return {};
|
|
1057
|
+
const nextItems = [...group.items];
|
|
1058
|
+
nextItems[itemIdx] = { ...nextItems[itemIdx], ...patch };
|
|
1059
|
+
const nextBubbles = [...state.bubbles];
|
|
1060
|
+
nextBubbles[idx] = { ...group, items: nextItems };
|
|
1061
|
+
return { bubbles: nextBubbles };
|
|
1062
|
+
});
|
|
1063
|
+
};
|
|
1064
|
+
this.updateLastBubble = (bubble) => {
|
|
1065
|
+
const { bubbles } = __privateGet(this, _get).call(this);
|
|
1066
|
+
if (bubbles.length === 0) return;
|
|
1067
|
+
const nextBubbles = [...bubbles];
|
|
1068
|
+
nextBubbles[nextBubbles.length - 1] = bubble;
|
|
1069
|
+
__privateGet(this, _set).call(this, { bubbles: nextBubbles });
|
|
1070
|
+
};
|
|
1071
|
+
__privateSet(this, _set, set);
|
|
1072
|
+
__privateSet(this, _get, get);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
_get = new WeakMap();
|
|
1076
|
+
_set = new WeakMap();
|
|
1077
|
+
const flattenActions = (actions) => {
|
|
1078
|
+
const result = {};
|
|
1079
|
+
for (const action of actions) {
|
|
1080
|
+
let current = action;
|
|
1081
|
+
while (current && current !== Object.prototype) {
|
|
1082
|
+
const keys = Object.getOwnPropertyNames(current);
|
|
1083
|
+
for (const key of keys) {
|
|
1084
|
+
if (key === "constructor") continue;
|
|
1085
|
+
if (key in result) continue;
|
|
1086
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
1087
|
+
if (!descriptor) continue;
|
|
1088
|
+
if (typeof descriptor.value === "function") {
|
|
1089
|
+
result[key] = descriptor.value.bind(action);
|
|
1090
|
+
} else {
|
|
1091
|
+
Object.defineProperty(result, key, {
|
|
1092
|
+
...descriptor,
|
|
1093
|
+
configurable: true,
|
|
1094
|
+
enumerable: true
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
current = Object.getPrototypeOf(current);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
return result;
|
|
1102
|
+
};
|
|
1103
|
+
const createAgentStoreState = (...params) => ({
|
|
1104
|
+
...createInitialAgentStoreState(),
|
|
1105
|
+
...flattenActions([createAgentStoreSlice(...params)])
|
|
1106
|
+
});
|
|
1107
|
+
function createAgentStore() {
|
|
1108
|
+
return createStore()(createAgentStoreState);
|
|
1109
|
+
}
|
|
1110
|
+
export {
|
|
1111
|
+
BaseEveryUserContentProvider,
|
|
1112
|
+
BaseFirstUserContentProvider,
|
|
1113
|
+
BaseFirstUserContentProvider as BaseFirstUserMessageProvider,
|
|
1114
|
+
BaseLastUserContentProvider,
|
|
1115
|
+
BaseMessageEngineProcessor,
|
|
1116
|
+
BaseSystemRoleProvider as BaseSystemMessageProvider,
|
|
1117
|
+
BaseSystemRoleProvider,
|
|
1118
|
+
BaseSystemRootProvider,
|
|
1119
|
+
MessagesEngine,
|
|
1120
|
+
acceptAllDiffs,
|
|
1121
|
+
acceptAndRebaseBatch,
|
|
1122
|
+
acceptBatch,
|
|
1123
|
+
acceptDiff,
|
|
1124
|
+
agentStoreSelectors,
|
|
1125
|
+
applyOpsToSnapshot,
|
|
1126
|
+
buildDocumentContext,
|
|
1127
|
+
compareBlockContent,
|
|
1128
|
+
createAgentExecutor,
|
|
1129
|
+
createAgentStore,
|
|
1130
|
+
createAgentStoreSlice,
|
|
1131
|
+
createDiffEngine,
|
|
1132
|
+
createDocumentTools,
|
|
1133
|
+
createInitialAgentStoreState,
|
|
1134
|
+
createReviewBatch,
|
|
1135
|
+
createSnapshot,
|
|
1136
|
+
detectConflicts,
|
|
1137
|
+
flattenActions,
|
|
1138
|
+
reconcileReviewBatches,
|
|
1139
|
+
rejectAllDiffs,
|
|
1140
|
+
rejectBatch,
|
|
1141
|
+
rejectDiff,
|
|
1142
|
+
resolveReviewEntry
|
|
1143
|
+
};
|