@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.
@@ -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
- createSdkAdapter: () => createSdkAdapter
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
- messages: (detail.messages || []).map((m, i) => ({
91
- id: `msg-${i}`,
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(agentId, sdkMessages, { conversationId });
110
- const controller = new AbortController();
111
- const signal = controller.signal;
112
- (async () => {
113
- try {
114
- for await (const event of stream) {
115
- if (signal.aborted) break;
116
- switch (event.type) {
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
- createSdkAdapter
656
+ createChatAdapter,
657
+ createSdkAdapter,
658
+ mergeConsecutiveAssistantMessages,
659
+ parseToolCalls
176
660
  });