@yushaw/sanqian-chat 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -0
- package/dist/core/index.d.mts +180 -6
- package/dist/core/index.d.ts +180 -6
- package/dist/core/index.js +548 -64
- package/dist/core/index.mjs +534 -63
- package/dist/main/index.d.mts +320 -5
- package/dist/main/index.d.ts +320 -5
- package/dist/main/index.js +466 -39
- package/dist/main/index.mjs +454 -38
- package/dist/preload/index.d.ts +88 -0
- package/dist/preload/index.js +5 -1
- package/dist/renderer/index.d.mts +609 -18
- package/dist/renderer/index.d.ts +609 -18
- package/dist/renderer/index.js +7870 -537
- package/dist/renderer/index.mjs +7854 -538
- package/package.json +16 -4
- package/src/renderer/styles/baseStyles.ts +74 -0
- package/src/renderer/styles/chat.css +3674 -0
- package/src/renderer/styles/chatCss.ts +11 -0
- package/src/renderer/styles/coreCss.ts +3237 -0
- package/src/renderer/styles/preflightCss.ts +454 -0
- package/src/renderer/styles/safe.css +3227 -0
- package/src/renderer/styles/safeCss.ts +10 -0
- package/src/renderer/styles/tailwind.css +683 -0
- package/src/renderer/styles/variables.css +704 -0
package/dist/core/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,16 +17,514 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/core/index.ts
|
|
21
31
|
var core_exports = {};
|
|
22
32
|
__export(core_exports, {
|
|
23
|
-
|
|
33
|
+
createChatAdapter: () => createChatAdapter,
|
|
34
|
+
createSdkAdapter: () => createSdkAdapter,
|
|
35
|
+
mergeConsecutiveAssistantMessages: () => mergeConsecutiveAssistantMessages,
|
|
36
|
+
parseToolCalls: () => parseToolCalls
|
|
24
37
|
});
|
|
25
38
|
module.exports = __toCommonJS(core_exports);
|
|
26
39
|
|
|
40
|
+
// src/core/history.ts
|
|
41
|
+
function safeParseArgs(value) {
|
|
42
|
+
if (!value) return void 0;
|
|
43
|
+
if (typeof value === "object") return value;
|
|
44
|
+
if (typeof value === "string") {
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(value);
|
|
47
|
+
} catch {
|
|
48
|
+
return void 0;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return void 0;
|
|
52
|
+
}
|
|
53
|
+
function parseToolCalls(toolCalls) {
|
|
54
|
+
if (!toolCalls) return void 0;
|
|
55
|
+
let calls = toolCalls;
|
|
56
|
+
if (typeof calls === "string") {
|
|
57
|
+
try {
|
|
58
|
+
calls = JSON.parse(calls);
|
|
59
|
+
} catch {
|
|
60
|
+
return void 0;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!Array.isArray(calls)) return void 0;
|
|
64
|
+
return calls.map((tc) => {
|
|
65
|
+
const fn = tc.function || void 0;
|
|
66
|
+
const name = fn?.name || tc.name || tc.tool_name || "";
|
|
67
|
+
const rawArgs = fn?.arguments ?? tc.args ?? tc.arguments;
|
|
68
|
+
const args = safeParseArgs(rawArgs);
|
|
69
|
+
const id = tc.id || tc.tool_call_id || tc.call_id;
|
|
70
|
+
return {
|
|
71
|
+
id,
|
|
72
|
+
name,
|
|
73
|
+
args,
|
|
74
|
+
status: "completed",
|
|
75
|
+
result: tc.result
|
|
76
|
+
};
|
|
77
|
+
}).filter((tc) => tc.name || tc.id);
|
|
78
|
+
}
|
|
79
|
+
function getNumericMessageId(message) {
|
|
80
|
+
if (typeof message.message_id === "number") {
|
|
81
|
+
return message.message_id;
|
|
82
|
+
}
|
|
83
|
+
if (typeof message.id === "string" && /^\d+$/.test(message.id)) {
|
|
84
|
+
return Number(message.id);
|
|
85
|
+
}
|
|
86
|
+
return void 0;
|
|
87
|
+
}
|
|
88
|
+
function mergeConsecutiveAssistantMessages(rawMessages) {
|
|
89
|
+
const result = [];
|
|
90
|
+
let i = 0;
|
|
91
|
+
while (i < rawMessages.length) {
|
|
92
|
+
const msg = rawMessages[i];
|
|
93
|
+
const numericMessageId = getNumericMessageId(msg);
|
|
94
|
+
const messageId = numericMessageId ? `history-${numericMessageId}` : msg.id || `history-${i}`;
|
|
95
|
+
if (msg.role === "assistant") {
|
|
96
|
+
const consecutiveAssistantMsgs = [msg];
|
|
97
|
+
const toolMessages = [];
|
|
98
|
+
let j = i + 1;
|
|
99
|
+
while (j < rawMessages.length) {
|
|
100
|
+
if (rawMessages[j].role === "assistant") {
|
|
101
|
+
consecutiveAssistantMsgs.push(rawMessages[j]);
|
|
102
|
+
j++;
|
|
103
|
+
} else if (rawMessages[j].role === "tool") {
|
|
104
|
+
toolMessages.push(rawMessages[j]);
|
|
105
|
+
j++;
|
|
106
|
+
} else {
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const blocks = [];
|
|
111
|
+
let blockTime = Date.now();
|
|
112
|
+
let fallbackToolCalls;
|
|
113
|
+
let lastEffectiveToolCalls;
|
|
114
|
+
for (let k = 0; k < consecutiveAssistantMsgs.length; k++) {
|
|
115
|
+
const assistantMsg = consecutiveAssistantMsgs[k];
|
|
116
|
+
const msgToolCalls = parseToolCalls(assistantMsg.toolCalls || assistantMsg.tool_calls);
|
|
117
|
+
const isLastAssistant = k === consecutiveAssistantMsgs.length - 1;
|
|
118
|
+
const fallbackCalls = isLastAssistant && toolMessages.length > 0 ? toolMessages.map((toolMessage, idx) => ({
|
|
119
|
+
id: (toolMessage.tool_call_id || void 0) ?? `tool-${idx}`,
|
|
120
|
+
name: "",
|
|
121
|
+
args: void 0,
|
|
122
|
+
status: "completed",
|
|
123
|
+
result: toolMessage.content
|
|
124
|
+
})) : void 0;
|
|
125
|
+
const effectiveToolCalls = msgToolCalls && msgToolCalls.length > 0 ? msgToolCalls : fallbackCalls;
|
|
126
|
+
if (!msgToolCalls || msgToolCalls.length === 0) {
|
|
127
|
+
if (effectiveToolCalls && effectiveToolCalls.length > 0) {
|
|
128
|
+
fallbackToolCalls = effectiveToolCalls;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
lastEffectiveToolCalls = effectiveToolCalls;
|
|
132
|
+
const hasToolCalls = effectiveToolCalls && effectiveToolCalls.length > 0;
|
|
133
|
+
if (assistantMsg.thinking && assistantMsg.thinking.trim()) {
|
|
134
|
+
blocks.push({
|
|
135
|
+
type: "thinking",
|
|
136
|
+
content: assistantMsg.thinking,
|
|
137
|
+
timestamp: blockTime++,
|
|
138
|
+
isIntermediate: true
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
const shouldDeferText = Boolean(hasToolCalls && isLastAssistant && toolMessages.length === 0);
|
|
142
|
+
const hasContent = Boolean(assistantMsg.content && assistantMsg.content.trim());
|
|
143
|
+
if (hasContent && !shouldDeferText) {
|
|
144
|
+
blocks.push({
|
|
145
|
+
type: "text",
|
|
146
|
+
content: assistantMsg.content,
|
|
147
|
+
timestamp: blockTime++,
|
|
148
|
+
isIntermediate: hasToolCalls || !isLastAssistant
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (effectiveToolCalls) {
|
|
152
|
+
const toolResultBlocks = [];
|
|
153
|
+
for (const tc of effectiveToolCalls) {
|
|
154
|
+
const toolIndex = effectiveToolCalls.indexOf(tc);
|
|
155
|
+
blocks.push({
|
|
156
|
+
type: "tool_call",
|
|
157
|
+
content: "",
|
|
158
|
+
timestamp: blockTime++,
|
|
159
|
+
toolName: tc.name,
|
|
160
|
+
toolArgs: tc.args,
|
|
161
|
+
toolCallId: tc.id,
|
|
162
|
+
toolStatus: "completed",
|
|
163
|
+
isIntermediate: true
|
|
164
|
+
});
|
|
165
|
+
const toolResultById = tc.id ? toolMessages.find((tm) => tm.tool_call_id === tc.id) : void 0;
|
|
166
|
+
const toolResultByIndex = toolMessages[toolIndex];
|
|
167
|
+
const toolResult = toolResultById || toolResultByIndex;
|
|
168
|
+
const resultContent = tc.result ?? toolResult?.content;
|
|
169
|
+
const toolResultId = toolResult?.tool_call_id || void 0;
|
|
170
|
+
if (resultContent) {
|
|
171
|
+
toolResultBlocks.push({
|
|
172
|
+
type: "tool_result",
|
|
173
|
+
content: resultContent,
|
|
174
|
+
timestamp: blockTime++,
|
|
175
|
+
toolName: tc.name,
|
|
176
|
+
toolCallId: tc.id || toolResultId,
|
|
177
|
+
isIntermediate: true
|
|
178
|
+
});
|
|
179
|
+
tc.result = resultContent;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
blocks.push(...toolResultBlocks);
|
|
183
|
+
}
|
|
184
|
+
if (hasContent && shouldDeferText) {
|
|
185
|
+
blocks.push({
|
|
186
|
+
type: "text",
|
|
187
|
+
content: assistantMsg.content,
|
|
188
|
+
timestamp: blockTime++,
|
|
189
|
+
isIntermediate: false
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (consecutiveAssistantMsgs.length > 1) {
|
|
194
|
+
let contentMessages = consecutiveAssistantMsgs.filter(
|
|
195
|
+
(m) => !m.toolCalls && !m.tool_calls
|
|
196
|
+
);
|
|
197
|
+
if (contentMessages.length === 0) {
|
|
198
|
+
contentMessages = [consecutiveAssistantMsgs[consecutiveAssistantMsgs.length - 1]];
|
|
199
|
+
}
|
|
200
|
+
const mergedContent = contentMessages.map((m) => m.content).filter((c) => c && c.trim()).join("\n\n");
|
|
201
|
+
const mergedToolCalls = consecutiveAssistantMsgs.flatMap((m) => parseToolCalls(m.toolCalls || m.tool_calls) || []);
|
|
202
|
+
const effectiveMergedToolCalls = mergedToolCalls.length > 0 ? mergedToolCalls : fallbackToolCalls || [];
|
|
203
|
+
for (const toolMsg of toolMessages) {
|
|
204
|
+
const toolCallId = toolMsg.tool_call_id;
|
|
205
|
+
const matchingToolCall = effectiveMergedToolCalls.find(
|
|
206
|
+
(tc) => toolCallId && tc.id === toolCallId || !tc.result
|
|
207
|
+
);
|
|
208
|
+
if (matchingToolCall) {
|
|
209
|
+
matchingToolCall.result = toolMsg.content;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const thinkingParts = consecutiveAssistantMsgs.map((m) => m.thinking).filter((t) => t && t.trim());
|
|
213
|
+
const mergedThinking = thinkingParts.length > 1 ? thinkingParts.join("\n\u2500\u2500\u2500\u2500\u2500\n") : thinkingParts[0] || "";
|
|
214
|
+
const lastAssistantMsg = consecutiveAssistantMsgs[consecutiveAssistantMsgs.length - 1];
|
|
215
|
+
const lastToolMsg = toolMessages[toolMessages.length - 1];
|
|
216
|
+
const lastMsgId = Math.max(
|
|
217
|
+
getNumericMessageId(lastAssistantMsg) || 0,
|
|
218
|
+
lastToolMsg ? getNumericMessageId(lastToolMsg) || 0 : 0
|
|
219
|
+
);
|
|
220
|
+
const mergedMessageId = lastMsgId > 0 ? `history-${lastMsgId}` : messageId;
|
|
221
|
+
result.push({
|
|
222
|
+
id: mergedMessageId,
|
|
223
|
+
role: "assistant",
|
|
224
|
+
content: mergedContent,
|
|
225
|
+
timestamp: msg.timestamp || msg.created_at || (/* @__PURE__ */ new Date()).toISOString(),
|
|
226
|
+
toolCalls: effectiveMergedToolCalls.length > 0 ? effectiveMergedToolCalls : void 0,
|
|
227
|
+
thinking: mergedThinking || void 0,
|
|
228
|
+
filePaths: msg.filePaths,
|
|
229
|
+
blocks: blocks.length > 0 ? blocks : void 0,
|
|
230
|
+
isComplete: true
|
|
231
|
+
});
|
|
232
|
+
i = j;
|
|
233
|
+
} else {
|
|
234
|
+
result.push({
|
|
235
|
+
id: messageId,
|
|
236
|
+
role: "assistant",
|
|
237
|
+
content: msg.content || "",
|
|
238
|
+
timestamp: msg.timestamp || msg.created_at || (/* @__PURE__ */ new Date()).toISOString(),
|
|
239
|
+
toolCalls: lastEffectiveToolCalls,
|
|
240
|
+
thinking: msg.thinking || void 0,
|
|
241
|
+
filePaths: msg.filePaths,
|
|
242
|
+
blocks: blocks.length > 0 ? blocks : void 0,
|
|
243
|
+
isComplete: true
|
|
244
|
+
});
|
|
245
|
+
i++;
|
|
246
|
+
}
|
|
247
|
+
} else if (msg.role === "tool") {
|
|
248
|
+
i++;
|
|
249
|
+
} else {
|
|
250
|
+
result.push({
|
|
251
|
+
id: messageId,
|
|
252
|
+
role: msg.role,
|
|
253
|
+
content: msg.content || "",
|
|
254
|
+
timestamp: msg.timestamp || msg.created_at || (/* @__PURE__ */ new Date()).toISOString(),
|
|
255
|
+
toolCalls: parseToolCalls(msg.toolCalls || msg.tool_calls),
|
|
256
|
+
thinking: msg.thinking || void 0,
|
|
257
|
+
filePaths: msg.filePaths
|
|
258
|
+
});
|
|
259
|
+
i++;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return result;
|
|
263
|
+
}
|
|
264
|
+
|
|
27
265
|
// src/core/adapter.ts
|
|
266
|
+
function createChatAdapter(config) {
|
|
267
|
+
let sdkInstance = null;
|
|
268
|
+
let sdkPromise = null;
|
|
269
|
+
let connectionStatus = "disconnected";
|
|
270
|
+
const connectionListeners = /* @__PURE__ */ new Set();
|
|
271
|
+
let currentRunId = null;
|
|
272
|
+
let reconnectAcquired = false;
|
|
273
|
+
const updateStatus = (status, error, errorCode) => {
|
|
274
|
+
connectionStatus = status;
|
|
275
|
+
connectionListeners.forEach((cb) => cb(status, error, errorCode));
|
|
276
|
+
};
|
|
277
|
+
const ensureSdk = async () => {
|
|
278
|
+
if (sdkInstance) return sdkInstance;
|
|
279
|
+
if (sdkPromise) return sdkPromise;
|
|
280
|
+
sdkPromise = (async () => {
|
|
281
|
+
const { SanqianSDK: SDK } = await import("@yushaw/sanqian-sdk");
|
|
282
|
+
const tools = config.tools?.map((t) => ({
|
|
283
|
+
name: t.name,
|
|
284
|
+
description: t.description,
|
|
285
|
+
parameters: t.parameters,
|
|
286
|
+
handler: t.handler
|
|
287
|
+
})).filter((t) => !!t.handler) || [];
|
|
288
|
+
sdkInstance = new SDK({
|
|
289
|
+
appName: config.appName,
|
|
290
|
+
appVersion: config.appVersion,
|
|
291
|
+
tools
|
|
292
|
+
});
|
|
293
|
+
return sdkInstance;
|
|
294
|
+
})();
|
|
295
|
+
return sdkPromise;
|
|
296
|
+
};
|
|
297
|
+
return {
|
|
298
|
+
async connect() {
|
|
299
|
+
if (reconnectAcquired) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
updateStatus("connecting");
|
|
303
|
+
try {
|
|
304
|
+
const sdk = await ensureSdk();
|
|
305
|
+
sdk.on("connected", () => updateStatus("connected"));
|
|
306
|
+
sdk.on("disconnected", () => {
|
|
307
|
+
if (reconnectAcquired && connectionStatus !== "disconnected") {
|
|
308
|
+
updateStatus("reconnecting");
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
sdk.on("error", (err) => updateStatus("error", err.message, "CONNECTION_FAILED"));
|
|
312
|
+
sdk.acquireReconnect();
|
|
313
|
+
reconnectAcquired = true;
|
|
314
|
+
await sdk.ensureReady();
|
|
315
|
+
updateStatus("connected");
|
|
316
|
+
} catch (e) {
|
|
317
|
+
updateStatus("error", e instanceof Error ? e.message : "Connection failed", "CONNECTION_FAILED");
|
|
318
|
+
throw e;
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
async disconnect() {
|
|
322
|
+
if (sdkInstance) {
|
|
323
|
+
if (reconnectAcquired) {
|
|
324
|
+
sdkInstance.releaseReconnect();
|
|
325
|
+
reconnectAcquired = false;
|
|
326
|
+
}
|
|
327
|
+
sdkInstance.removeAllListeners();
|
|
328
|
+
await sdkInstance.disconnect();
|
|
329
|
+
sdkInstance = null;
|
|
330
|
+
sdkPromise = null;
|
|
331
|
+
}
|
|
332
|
+
updateStatus("disconnected");
|
|
333
|
+
},
|
|
334
|
+
isConnected() {
|
|
335
|
+
return sdkInstance?.isConnected() ?? false;
|
|
336
|
+
},
|
|
337
|
+
getConnectionStatus() {
|
|
338
|
+
return connectionStatus;
|
|
339
|
+
},
|
|
340
|
+
onConnectionChange(callback) {
|
|
341
|
+
connectionListeners.add(callback);
|
|
342
|
+
callback(connectionStatus);
|
|
343
|
+
return () => connectionListeners.delete(callback);
|
|
344
|
+
},
|
|
345
|
+
async listConversations(options) {
|
|
346
|
+
const sdk = await ensureSdk();
|
|
347
|
+
await sdk.ensureReady();
|
|
348
|
+
const result = await sdk.listConversations({ agentId: config.agentId, ...options });
|
|
349
|
+
return {
|
|
350
|
+
conversations: result.conversations.map((c) => ({
|
|
351
|
+
id: c.conversation_id,
|
|
352
|
+
title: c.title || "Untitled",
|
|
353
|
+
createdAt: c.created_at || "",
|
|
354
|
+
updatedAt: c.updated_at || "",
|
|
355
|
+
messageCount: c.message_count,
|
|
356
|
+
agentId: c.agent_id || null
|
|
357
|
+
})),
|
|
358
|
+
total: result.total
|
|
359
|
+
};
|
|
360
|
+
},
|
|
361
|
+
async getConversation(id, options) {
|
|
362
|
+
const sdk = await ensureSdk();
|
|
363
|
+
await sdk.ensureReady();
|
|
364
|
+
const detail = await sdk.getConversation(id, { messageLimit: options?.messageLimit });
|
|
365
|
+
let rawMessages = detail.messages || [];
|
|
366
|
+
const sdkWithHistory = sdk;
|
|
367
|
+
if (typeof sdkWithHistory.getMessages === "function") {
|
|
368
|
+
try {
|
|
369
|
+
const history = await sdkWithHistory.getMessages(id, { limit: options?.messageLimit });
|
|
370
|
+
if (history?.messages && history.messages.length > 0) {
|
|
371
|
+
rawMessages = history.messages;
|
|
372
|
+
}
|
|
373
|
+
} catch (e) {
|
|
374
|
+
console.warn("[sanqian-chat] getMessages failed, fallback to getConversation:", e);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const normalizedMessages = mergeConsecutiveAssistantMessages(
|
|
378
|
+
rawMessages.map((m, i) => ({
|
|
379
|
+
id: m.id || `msg-${i}`,
|
|
380
|
+
role: m.role,
|
|
381
|
+
content: m.content,
|
|
382
|
+
created_at: m.created_at || void 0,
|
|
383
|
+
tool_calls: m.tool_calls || m.toolCalls,
|
|
384
|
+
tool_call_id: m.tool_call_id ?? null,
|
|
385
|
+
thinking: m.thinking || void 0,
|
|
386
|
+
message_id: m.message_id,
|
|
387
|
+
filePaths: m.file_paths || m.filePaths
|
|
388
|
+
}))
|
|
389
|
+
);
|
|
390
|
+
return {
|
|
391
|
+
id: detail.conversation_id,
|
|
392
|
+
title: detail.title || "Untitled",
|
|
393
|
+
createdAt: detail.created_at || "",
|
|
394
|
+
updatedAt: detail.updated_at || "",
|
|
395
|
+
messageCount: detail.message_count,
|
|
396
|
+
agentId: detail.agent_id || null,
|
|
397
|
+
messages: normalizedMessages
|
|
398
|
+
};
|
|
399
|
+
},
|
|
400
|
+
async deleteConversation(id) {
|
|
401
|
+
const sdk = await ensureSdk();
|
|
402
|
+
await sdk.ensureReady();
|
|
403
|
+
await sdk.deleteConversation(id);
|
|
404
|
+
},
|
|
405
|
+
async chatStream(messages, conversationId, onEvent, options) {
|
|
406
|
+
const sdk = await ensureSdk();
|
|
407
|
+
await sdk.ensureReady();
|
|
408
|
+
const agentId = options?.agentId ?? config.agentId;
|
|
409
|
+
const sdkMessages = messages.map((m) => ({ role: m.role, content: m.content }));
|
|
410
|
+
const stream = sdk.chatStream(
|
|
411
|
+
agentId,
|
|
412
|
+
sdkMessages,
|
|
413
|
+
{
|
|
414
|
+
conversationId,
|
|
415
|
+
persistHistory: config.persistHistory ?? false
|
|
416
|
+
}
|
|
417
|
+
);
|
|
418
|
+
return processStreamEvents(stream, onEvent, sdk, (id) => {
|
|
419
|
+
currentRunId = id;
|
|
420
|
+
});
|
|
421
|
+
},
|
|
422
|
+
sendHitlResponse(response, runId) {
|
|
423
|
+
if (!sdkInstance) return;
|
|
424
|
+
const id = runId || currentRunId;
|
|
425
|
+
if (id) {
|
|
426
|
+
sdkInstance.sendHitlResponse(id, response);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
function processStreamEvents(stream, onEvent, sdk, setCurrentRunId) {
|
|
432
|
+
const controller = new AbortController();
|
|
433
|
+
const signal = controller.signal;
|
|
434
|
+
let currentRunId = null;
|
|
435
|
+
(async () => {
|
|
436
|
+
try {
|
|
437
|
+
for await (const event of stream) {
|
|
438
|
+
if (signal.aborted) break;
|
|
439
|
+
switch (event.type) {
|
|
440
|
+
case "start":
|
|
441
|
+
if (event.run_id) {
|
|
442
|
+
currentRunId = event.run_id;
|
|
443
|
+
setCurrentRunId(event.run_id);
|
|
444
|
+
onEvent({ type: "start", run_id: event.run_id, conversationId: event.conversationId });
|
|
445
|
+
}
|
|
446
|
+
break;
|
|
447
|
+
case "text":
|
|
448
|
+
onEvent({ type: "text", content: event.content || "" });
|
|
449
|
+
break;
|
|
450
|
+
case "thinking":
|
|
451
|
+
onEvent({ type: "thinking", content: event.content || "" });
|
|
452
|
+
break;
|
|
453
|
+
case "tool_call":
|
|
454
|
+
if (event.tool_call) {
|
|
455
|
+
onEvent({ type: "tool_call", tool_call: event.tool_call });
|
|
456
|
+
}
|
|
457
|
+
break;
|
|
458
|
+
case "tool_args_chunk":
|
|
459
|
+
onEvent({
|
|
460
|
+
type: "tool_args_chunk",
|
|
461
|
+
tool_call_id: event.tool_call_id || "",
|
|
462
|
+
tool_name: event.tool_name,
|
|
463
|
+
chunk: event.chunk || ""
|
|
464
|
+
});
|
|
465
|
+
break;
|
|
466
|
+
case "tool_args":
|
|
467
|
+
onEvent({
|
|
468
|
+
type: "tool_args",
|
|
469
|
+
tool_call_id: event.tool_call_id || "",
|
|
470
|
+
tool_name: event.tool_name,
|
|
471
|
+
args: event.args || {}
|
|
472
|
+
});
|
|
473
|
+
break;
|
|
474
|
+
case "tool_result":
|
|
475
|
+
onEvent({ type: "tool_result", tool_call_id: event.tool_call_id || "", result: event.result });
|
|
476
|
+
break;
|
|
477
|
+
case "done":
|
|
478
|
+
onEvent({ type: "done", conversationId: event.conversationId || "", title: event.title });
|
|
479
|
+
currentRunId = null;
|
|
480
|
+
setCurrentRunId(null);
|
|
481
|
+
break;
|
|
482
|
+
case "cancelled":
|
|
483
|
+
onEvent({ type: "cancelled", run_id: event.run_id });
|
|
484
|
+
currentRunId = null;
|
|
485
|
+
setCurrentRunId(null);
|
|
486
|
+
break;
|
|
487
|
+
case "error":
|
|
488
|
+
onEvent({ type: "error", error: event.error || "Unknown error" });
|
|
489
|
+
currentRunId = null;
|
|
490
|
+
setCurrentRunId(null);
|
|
491
|
+
break;
|
|
492
|
+
default: {
|
|
493
|
+
const evt = event;
|
|
494
|
+
if (evt.type === "interrupt") {
|
|
495
|
+
currentRunId = evt.run_id || null;
|
|
496
|
+
setCurrentRunId(evt.run_id || null);
|
|
497
|
+
onEvent({
|
|
498
|
+
type: "interrupt",
|
|
499
|
+
interrupt_type: evt.interrupt_type || "",
|
|
500
|
+
interrupt_payload: evt.interrupt_payload,
|
|
501
|
+
run_id: evt.run_id
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
} catch (e) {
|
|
509
|
+
if (!signal.aborted) {
|
|
510
|
+
onEvent({ type: "error", error: e instanceof Error ? e.message : "Stream error" });
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
})();
|
|
514
|
+
return {
|
|
515
|
+
cancel: () => {
|
|
516
|
+
controller.abort();
|
|
517
|
+
const sdkWithCancel = sdk;
|
|
518
|
+
if (currentRunId && typeof sdkWithCancel.cancelRun === "function") {
|
|
519
|
+
try {
|
|
520
|
+
sdkWithCancel.cancelRun(currentRunId);
|
|
521
|
+
} catch (e) {
|
|
522
|
+
console.warn("[chat adapter] Failed to cancel run:", e);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
}
|
|
28
528
|
function createSdkAdapter(config) {
|
|
29
529
|
let connectionStatus = "disconnected";
|
|
30
530
|
const connectionListeners = /* @__PURE__ */ new Set();
|
|
@@ -47,6 +547,10 @@ function createSdkAdapter(config) {
|
|
|
47
547
|
}
|
|
48
548
|
},
|
|
49
549
|
async disconnect() {
|
|
550
|
+
const sdk = config.getSdk();
|
|
551
|
+
if (sdk) {
|
|
552
|
+
await sdk.disconnect();
|
|
553
|
+
}
|
|
50
554
|
updateStatus("disconnected");
|
|
51
555
|
},
|
|
52
556
|
isConnected() {
|
|
@@ -72,7 +576,8 @@ function createSdkAdapter(config) {
|
|
|
72
576
|
title: c.title || "Untitled",
|
|
73
577
|
createdAt: c.created_at || "",
|
|
74
578
|
updatedAt: c.updated_at || "",
|
|
75
|
-
messageCount: c.message_count
|
|
579
|
+
messageCount: c.message_count,
|
|
580
|
+
agentId: c.agent_id || null
|
|
76
581
|
})),
|
|
77
582
|
total: result.total
|
|
78
583
|
};
|
|
@@ -81,18 +586,39 @@ function createSdkAdapter(config) {
|
|
|
81
586
|
const sdk = config.getSdk();
|
|
82
587
|
if (!sdk) throw new Error("SDK not ready");
|
|
83
588
|
const detail = await sdk.getConversation(id, { messageLimit: options?.messageLimit });
|
|
589
|
+
let rawMessages = detail.messages || [];
|
|
590
|
+
const sdkWithHistory = sdk;
|
|
591
|
+
if (typeof sdkWithHistory.getMessages === "function") {
|
|
592
|
+
try {
|
|
593
|
+
const history = await sdkWithHistory.getMessages(id, { limit: options?.messageLimit });
|
|
594
|
+
if (history?.messages && history.messages.length > 0) {
|
|
595
|
+
rawMessages = history.messages;
|
|
596
|
+
}
|
|
597
|
+
} catch (e) {
|
|
598
|
+
console.warn("[sanqian-chat] getMessages failed, fallback to getConversation:", e);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
const normalizedMessages = mergeConsecutiveAssistantMessages(
|
|
602
|
+
rawMessages.map((m, i) => ({
|
|
603
|
+
id: m.id || `msg-${i}`,
|
|
604
|
+
role: m.role,
|
|
605
|
+
content: m.content,
|
|
606
|
+
created_at: m.created_at || void 0,
|
|
607
|
+
tool_calls: m.tool_calls || m.toolCalls,
|
|
608
|
+
tool_call_id: m.tool_call_id ?? null,
|
|
609
|
+
thinking: m.thinking || void 0,
|
|
610
|
+
message_id: m.message_id,
|
|
611
|
+
filePaths: m.file_paths || m.filePaths
|
|
612
|
+
}))
|
|
613
|
+
);
|
|
84
614
|
return {
|
|
85
615
|
id: detail.conversation_id,
|
|
86
616
|
title: detail.title || "Untitled",
|
|
87
617
|
createdAt: detail.created_at || "",
|
|
88
618
|
updatedAt: detail.updated_at || "",
|
|
89
619
|
messageCount: detail.message_count,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
role: m.role,
|
|
93
|
-
content: m.content,
|
|
94
|
-
timestamp: m.created_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
95
|
-
}))
|
|
620
|
+
agentId: detail.agent_id || null,
|
|
621
|
+
messages: normalizedMessages
|
|
96
622
|
};
|
|
97
623
|
},
|
|
98
624
|
async deleteConversation(id) {
|
|
@@ -100,65 +626,20 @@ function createSdkAdapter(config) {
|
|
|
100
626
|
if (!sdk) throw new Error("SDK not ready");
|
|
101
627
|
await sdk.deleteConversation(id);
|
|
102
628
|
},
|
|
103
|
-
async chatStream(messages, conversationId, onEvent) {
|
|
629
|
+
async chatStream(messages, conversationId, onEvent, options) {
|
|
104
630
|
const sdk = config.getSdk();
|
|
105
|
-
const agentId = config.getAgentId();
|
|
631
|
+
const agentId = options?.agentId ?? config.getAgentId();
|
|
106
632
|
if (!sdk || !agentId) throw new Error("SDK or agent not ready");
|
|
107
633
|
await sdk.ensureReady();
|
|
108
634
|
const sdkMessages = messages.map((m) => ({ role: m.role, content: m.content }));
|
|
109
|
-
const stream = sdk.chatStream(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
case "text":
|
|
118
|
-
onEvent({ type: "text", content: event.content || "" });
|
|
119
|
-
break;
|
|
120
|
-
case "thinking":
|
|
121
|
-
onEvent({ type: "thinking", content: event.content || "" });
|
|
122
|
-
break;
|
|
123
|
-
case "tool_call":
|
|
124
|
-
if (event.tool_call) {
|
|
125
|
-
onEvent({ type: "tool_call", tool_call: event.tool_call });
|
|
126
|
-
}
|
|
127
|
-
break;
|
|
128
|
-
case "tool_result":
|
|
129
|
-
onEvent({ type: "tool_result", tool_call_id: event.tool_call_id || "", result: event.result });
|
|
130
|
-
break;
|
|
131
|
-
case "done":
|
|
132
|
-
onEvent({ type: "done", conversationId: event.conversationId || "", title: event.title });
|
|
133
|
-
break;
|
|
134
|
-
case "error":
|
|
135
|
-
onEvent({ type: "error", error: event.error || "Unknown error" });
|
|
136
|
-
break;
|
|
137
|
-
default:
|
|
138
|
-
const evt = event;
|
|
139
|
-
if (evt.type === "interrupt") {
|
|
140
|
-
currentRunId = evt.run_id || null;
|
|
141
|
-
onEvent({
|
|
142
|
-
type: "interrupt",
|
|
143
|
-
interrupt_type: evt.interrupt_type || "",
|
|
144
|
-
interrupt_payload: evt.interrupt_payload,
|
|
145
|
-
run_id: evt.run_id
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
} catch (e) {
|
|
152
|
-
if (!signal.aborted) {
|
|
153
|
-
onEvent({ type: "error", error: e instanceof Error ? e.message : "Stream error" });
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
})();
|
|
157
|
-
return {
|
|
158
|
-
cancel: () => {
|
|
159
|
-
controller.abort();
|
|
160
|
-
}
|
|
161
|
-
};
|
|
635
|
+
const stream = sdk.chatStream(
|
|
636
|
+
agentId,
|
|
637
|
+
sdkMessages,
|
|
638
|
+
{ conversationId }
|
|
639
|
+
);
|
|
640
|
+
return processStreamEvents(stream, onEvent, sdk, (id) => {
|
|
641
|
+
currentRunId = id;
|
|
642
|
+
});
|
|
162
643
|
},
|
|
163
644
|
sendHitlResponse(response, runId) {
|
|
164
645
|
const sdk = config.getSdk();
|
|
@@ -172,5 +653,8 @@ function createSdkAdapter(config) {
|
|
|
172
653
|
}
|
|
173
654
|
// Annotate the CommonJS export names for ESM import in node:
|
|
174
655
|
0 && (module.exports = {
|
|
175
|
-
|
|
656
|
+
createChatAdapter,
|
|
657
|
+
createSdkAdapter,
|
|
658
|
+
mergeConsecutiveAssistantMessages,
|
|
659
|
+
parseToolCalls
|
|
176
660
|
});
|