@nextclaw/ui 0.10.5 → 0.11.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.
Files changed (89) hide show
  1. package/CHANGELOG.md +23 -2
  2. package/dist/assets/{ChannelsList-Nu7Ig6_-.js → ChannelsList-CVPqrxns.js} +4 -4
  3. package/dist/assets/ChatPage-BO1VUrAY.js +37 -0
  4. package/dist/assets/{DocBrowser-3CfKmJA6.js → DocBrowser-FBwg8iji.js} +1 -1
  5. package/dist/assets/{LogoBadge-DdthDJOp.js → LogoBadge-BCmJfRT8.js} +1 -1
  6. package/dist/assets/MarketplacePage-DWxXUOCx.js +49 -0
  7. package/dist/assets/{McpMarketplacePage-Dg8GSZh6.js → McpMarketplacePage-Bth9X_hu.js} +2 -2
  8. package/dist/assets/{ModelConfig-DyQ6cC92.js → ModelConfig-PkSp_ioc.js} +1 -1
  9. package/dist/assets/ProvidersList-DVDge8wa.js +1 -0
  10. package/dist/assets/RemoteAccessPage-BVkzfEaL.js +1 -0
  11. package/dist/assets/RuntimeConfig-ByJs3khh.js +1 -0
  12. package/dist/assets/{SearchConfig-R1BcCLWO.js → SearchConfig-KZUAqYJN.js} +1 -1
  13. package/dist/assets/{SecretsConfig-D-jZMHeY.js → SecretsConfig-qwB_Y_Ka.js} +2 -2
  14. package/dist/assets/SessionsConfig-CGCl4UTr.js +2 -0
  15. package/dist/assets/index-CrilScMo.css +1 -0
  16. package/dist/assets/{index-BulnQWr6.js → index-D41ntvb7.js} +6 -6
  17. package/dist/assets/{label-C7yzBvzK.js → label-7JEFhkur.js} +1 -1
  18. package/dist/assets/ncp-session-adapter-BOqhkrc-.js +1 -0
  19. package/dist/assets/{page-layout-DF0xpax2.js → page-layout-B7q511TE.js} +1 -1
  20. package/dist/assets/popover-CywJGmPr.js +1 -0
  21. package/dist/assets/security-config-zi2UxN5r.js +1 -0
  22. package/dist/assets/skeleton-qUJZQ03S.js +1 -0
  23. package/dist/assets/{status-dot-B9opOZ22.js → status-dot-BilwNdTT.js} +1 -1
  24. package/dist/assets/{switch-l1P0ev4D.js → switch-BLp2Pno1.js} +1 -1
  25. package/dist/assets/tabs-custom-CgIdQMGC.js +1 -0
  26. package/dist/assets/useConfirmDialog-BitswAkv.js +1 -0
  27. package/dist/assets/{vendor-CNhxtHCf.js → vendor-D_JxmsLV.js} +87 -87
  28. package/dist/index.html +3 -3
  29. package/package.json +4 -4
  30. package/src/App.test.tsx +42 -10
  31. package/src/App.tsx +5 -40
  32. package/src/api/api-base.test.ts +37 -0
  33. package/src/api/api-base.ts +0 -4
  34. package/src/api/config.ts +2 -270
  35. package/src/api/ncp-attachments.ts +12 -12
  36. package/src/api/types.ts +4 -121
  37. package/src/components/chat/ChatPage.tsx +1 -11
  38. package/src/components/chat/ChatSidebar.test.tsx +1 -50
  39. package/src/components/chat/ChatSidebar.tsx +0 -5
  40. package/src/components/chat/README.md +2 -0
  41. package/src/components/chat/adapters/chat-message.adapter.test.ts +39 -0
  42. package/src/components/chat/adapters/chat-message.adapter.ts +56 -0
  43. package/src/components/chat/chat-attachment-upload-limit.test.ts +41 -0
  44. package/src/components/chat/chat-composer-state.test.ts +4 -4
  45. package/src/components/chat/chat-composer-state.ts +1 -1
  46. package/src/components/chat/chat-session-display.ts +9 -0
  47. package/src/components/chat/chat-session-label.service.ts +3 -12
  48. package/src/components/chat/chat-session-preference-sync.test.ts +10 -13
  49. package/src/components/chat/chat-stream/types.ts +4 -57
  50. package/src/components/chat/containers/chat-input-bar.container.tsx +2 -2
  51. package/src/components/chat/ncp/NcpChatPage.tsx +3 -3
  52. package/src/components/chat/ncp/ncp-chat-input.manager.ts +1 -1
  53. package/src/components/chat/useHydratedNcpAgent.test.tsx +77 -0
  54. package/src/components/config/README.md +2 -0
  55. package/src/components/config/SessionsConfig.tsx +152 -132
  56. package/src/hooks/use-auth.test.ts +3 -3
  57. package/src/hooks/use-auth.ts +16 -4
  58. package/src/hooks/use-realtime-query-bridge.ts +0 -24
  59. package/src/hooks/useConfig.ts +10 -137
  60. package/src/lib/session-run-status.ts +1 -63
  61. package/src/vite-env.d.ts +1 -0
  62. package/vite.config.ts +4 -4
  63. package/dist/assets/ChatPage-CBCFSk4e.js +0 -38
  64. package/dist/assets/MarketplacePage-inGGiv1T.js +0 -49
  65. package/dist/assets/ProvidersList-B2T8Lc_i.js +0 -1
  66. package/dist/assets/RemoteAccessPage-C9LxgK-C.js +0 -1
  67. package/dist/assets/RuntimeConfig-Ey4VIqTW.js +0 -1
  68. package/dist/assets/SessionsConfig-Cawoh4_2.js +0 -2
  69. package/dist/assets/chat-message-BbuIK4dQ.js +0 -3
  70. package/dist/assets/index-kaPUhd-8.css +0 -1
  71. package/dist/assets/popover-DjaScZDJ.js +0 -1
  72. package/dist/assets/security-config-Bg2eriNx.js +0 -1
  73. package/dist/assets/skeleton-DycBJAJF.js +0 -1
  74. package/dist/assets/tabs-custom-BG9y2JhC.js +0 -1
  75. package/dist/assets/useConfirmDialog-DTducNfn.js +0 -1
  76. package/src/api/config.stream.test.ts +0 -115
  77. package/src/components/chat/chat-chain.test.ts +0 -22
  78. package/src/components/chat/chat-chain.ts +0 -23
  79. package/src/components/chat/chat-page-data.ts +0 -171
  80. package/src/components/chat/chat-page-runtime.ts +0 -190
  81. package/src/components/chat/chat-stream/nextbot-parsers.ts +0 -52
  82. package/src/components/chat/chat-stream/nextbot-runtime-agent.ts +0 -413
  83. package/src/components/chat/chat-stream/stream-event-adapter.ts +0 -98
  84. package/src/components/chat/chat-stream/transport.ts +0 -253
  85. package/src/components/chat/legacy/LegacyChatPage.tsx +0 -223
  86. package/src/components/chat/managers/chat-input.manager.ts +0 -228
  87. package/src/components/chat/managers/chat-thread.manager.ts +0 -87
  88. package/src/components/chat/presenter/chat.presenter.ts +0 -32
  89. package/src/components/chat/useChatRuntimeController.ts +0 -134
@@ -1,413 +0,0 @@
1
- import { EventType, type AgentEvent, type IAgent, type RunAgentInput } from '@nextclaw/agent-chat';
2
- import { Observable, type Subscribable } from 'rxjs';
3
- import {
4
- buildDeltaEvents,
5
- buildDeltaMessageId,
6
- buildSessionEventEvents,
7
- isAssistantSessionEvent,
8
- shouldCloseDeltaOnSessionEvent
9
- } from './stream-event-adapter';
10
- import { openResumeRunStream, openSendTurnStream, requestStopRun } from './transport';
11
- import type {
12
- ActiveRunState,
13
- NextbotAgentRunMetadata,
14
- StreamDeltaEvent,
15
- StreamReadyEvent,
16
- StreamSessionEvent
17
- } from './types';
18
- import { isAbortLikeError } from '@/lib/chat-runtime-utils';
19
-
20
- type SendRunMetadata = Extract<NextbotAgentRunMetadata, { mode: 'send' }>;
21
- type ResumeRunMetadata = Extract<NextbotAgentRunMetadata, { mode: 'resume' }>;
22
- type StreamResult = { sessionKey: string; reply: string };
23
- type EmitEvent = (event: AgentEvent) => void;
24
- type StreamRuntimeState = {
25
- deltaMessageId: string;
26
- deltaStarted: boolean;
27
- deltaClosed: boolean;
28
- hasAssistantSessionEvent: boolean;
29
- hasAssistantOutput: boolean;
30
- };
31
- type RunObservableParams = {
32
- metadata: NextbotAgentRunMetadata;
33
- clientRunId: string;
34
- abortController: AbortController;
35
- runState: ActiveRunState;
36
- };
37
-
38
- function createBackendRunId(): string {
39
- const now = Date.now().toString(36);
40
- const rand = Math.random().toString(36).slice(2, 10);
41
- return `run-${now}-${rand}`;
42
- }
43
-
44
- export type NextbotRunMetadataPayload =
45
- | {
46
- driver: 'nextbot-stream';
47
- kind: 'ready';
48
- sessionKey?: string;
49
- backendRunId?: string;
50
- stopSupported?: boolean;
51
- stopReason?: string;
52
- requestedAt?: string;
53
- }
54
- | {
55
- driver: 'nextbot-stream';
56
- kind: 'final';
57
- sessionKey: string;
58
- reply: string;
59
- hasAssistantSessionEvent: boolean;
60
- };
61
-
62
- function readRunMetadata(input: RunAgentInput): NextbotAgentRunMetadata | null {
63
- const raw = input.metadata;
64
- if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
65
- return null;
66
- }
67
- const record = raw as Record<string, unknown>;
68
- if (record.driver !== 'nextbot-stream') {
69
- return null;
70
- }
71
- const mode = record.mode;
72
- if (mode === 'send') {
73
- const payload = record.payload;
74
- if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
75
- return null;
76
- }
77
- const requestedSkills = Array.isArray(record.requestedSkills)
78
- ? record.requestedSkills.filter((item): item is string => typeof item === 'string')
79
- : [];
80
- return {
81
- driver: 'nextbot-stream',
82
- mode: 'send',
83
- payload: payload as SendRunMetadata['payload'],
84
- requestedSkills
85
- };
86
- }
87
- if (mode === 'resume') {
88
- const runId = typeof record.runId === 'string' ? record.runId.trim() : '';
89
- if (!runId) {
90
- return null;
91
- }
92
- const fromEventIndex =
93
- typeof record.fromEventIndex === 'number' && Number.isFinite(record.fromEventIndex)
94
- ? Math.max(0, Math.trunc(record.fromEventIndex))
95
- : undefined;
96
- const sessionKey = typeof record.sessionKey === 'string' ? record.sessionKey.trim() : '';
97
- const agentId = typeof record.agentId === 'string' ? record.agentId.trim() : '';
98
- const stopSupported = typeof record.stopSupported === 'boolean' ? record.stopSupported : undefined;
99
- const stopReason = typeof record.stopReason === 'string' ? record.stopReason.trim() : '';
100
- return {
101
- driver: 'nextbot-stream',
102
- mode: 'resume',
103
- runId,
104
- ...(typeof fromEventIndex === 'number' ? { fromEventIndex } : {}),
105
- ...(sessionKey ? { sessionKey } : {}),
106
- ...(agentId ? { agentId } : {}),
107
- ...(typeof stopSupported === 'boolean' ? { stopSupported } : {}),
108
- ...(stopReason ? { stopReason } : {})
109
- };
110
- }
111
- return null;
112
- }
113
-
114
- function buildInitialRunState(metadata: NextbotAgentRunMetadata): ActiveRunState {
115
- if (metadata.mode === 'send') {
116
- const backendRunId = metadata.payload.runId?.trim() || '';
117
- return {
118
- localRunId: 0,
119
- sessionKey: metadata.payload.sessionKey,
120
- ...(metadata.payload.agentId ? { agentId: metadata.payload.agentId } : {}),
121
- ...(backendRunId ? { backendRunId } : {}),
122
- backendStopSupported: Boolean(metadata.payload.stopSupported),
123
- ...(metadata.payload.stopReason ? { backendStopReason: metadata.payload.stopReason } : {})
124
- };
125
- }
126
- return {
127
- localRunId: 0,
128
- sessionKey: metadata.sessionKey ?? '',
129
- ...(metadata.agentId ? { agentId: metadata.agentId } : {}),
130
- backendRunId: metadata.runId,
131
- backendStopSupported: Boolean(metadata.stopSupported),
132
- ...(metadata.stopReason ? { backendStopReason: metadata.stopReason } : {})
133
- };
134
- }
135
-
136
- function withEnsuredSendRunId(metadata: NextbotAgentRunMetadata): NextbotAgentRunMetadata {
137
- if (metadata.mode !== 'send') {
138
- return metadata;
139
- }
140
- const runId = metadata.payload.runId?.trim();
141
- if (runId) {
142
- return metadata;
143
- }
144
- return {
145
- ...metadata,
146
- payload: {
147
- ...metadata.payload,
148
- runId: createBackendRunId()
149
- }
150
- };
151
- }
152
-
153
- function updateRunStateFromReady(runState: ActiveRunState, event: {
154
- sessionKey?: string;
155
- runId?: string;
156
- stopSupported?: boolean;
157
- stopReason?: string;
158
- }) {
159
- runState.backendRunId = event.runId?.trim() || runState.backendRunId;
160
- runState.backendStopSupported =
161
- typeof event.stopSupported === 'boolean' ? event.stopSupported : runState.backendStopSupported;
162
- if (event.stopReason?.trim()) {
163
- runState.backendStopReason = event.stopReason.trim();
164
- }
165
- if (event.sessionKey?.trim()) {
166
- runState.sessionKey = event.sessionKey.trim();
167
- }
168
- }
169
-
170
- function createStreamRuntimeState(): StreamRuntimeState {
171
- return {
172
- deltaMessageId: buildDeltaMessageId(),
173
- deltaStarted: false,
174
- deltaClosed: false,
175
- hasAssistantSessionEvent: false,
176
- hasAssistantOutput: false
177
- };
178
- }
179
-
180
- function buildReadyMetadata(event: StreamReadyEvent): NextbotRunMetadataPayload {
181
- return {
182
- driver: 'nextbot-stream',
183
- kind: 'ready',
184
- ...(event.sessionKey?.trim() ? { sessionKey: event.sessionKey.trim() } : {}),
185
- ...(event.runId?.trim() ? { backendRunId: event.runId.trim() } : {}),
186
- ...(typeof event.stopSupported === 'boolean' ? { stopSupported: event.stopSupported } : {}),
187
- ...(event.stopReason?.trim() ? { stopReason: event.stopReason.trim() } : {}),
188
- ...(event.requestedAt ? { requestedAt: event.requestedAt } : {})
189
- };
190
- }
191
-
192
- function buildFinalMetadata(result: StreamResult, hasAssistantSessionEvent: boolean): NextbotRunMetadataPayload {
193
- return {
194
- driver: 'nextbot-stream',
195
- kind: 'final',
196
- sessionKey: result.sessionKey,
197
- reply: result.reply,
198
- hasAssistantSessionEvent
199
- };
200
- }
201
-
202
- function emitRunMetadata(emit: EmitEvent, clientRunId: string, metadata: NextbotRunMetadataPayload) {
203
- emit({
204
- type: EventType.RUN_METADATA,
205
- runId: clientRunId,
206
- metadata
207
- });
208
- }
209
-
210
- function closeDelta(runtime: StreamRuntimeState, emit: EmitEvent) {
211
- if (!runtime.deltaStarted || runtime.deltaClosed) {
212
- return;
213
- }
214
- emit({ type: EventType.TEXT_END, messageId: runtime.deltaMessageId });
215
- runtime.deltaClosed = true;
216
- }
217
-
218
- function handleDelta(runtime: StreamRuntimeState, emit: EmitEvent, event: StreamDeltaEvent) {
219
- const events = buildDeltaEvents({
220
- messageId: runtime.deltaMessageId,
221
- delta: event.delta,
222
- started: runtime.deltaStarted
223
- });
224
- if (events.length > 0) {
225
- runtime.deltaStarted = true;
226
- runtime.deltaClosed = false;
227
- runtime.hasAssistantOutput = true;
228
- }
229
- for (const streamEvent of events) {
230
- emit(streamEvent);
231
- }
232
- }
233
-
234
- function handleSession(runtime: StreamRuntimeState, emit: EmitEvent, event: StreamSessionEvent) {
235
- if (shouldCloseDeltaOnSessionEvent(event.data)) {
236
- closeDelta(runtime, emit);
237
- runtime.deltaMessageId = buildDeltaMessageId();
238
- runtime.deltaStarted = false;
239
- runtime.deltaClosed = false;
240
- }
241
- if (isAssistantSessionEvent(event.data)) {
242
- runtime.hasAssistantSessionEvent = true;
243
- }
244
- for (const streamEvent of buildSessionEventEvents({
245
- event: event.data,
246
- messageId: runtime.deltaMessageId
247
- })) {
248
- emit(streamEvent);
249
- }
250
- }
251
-
252
- function emitFallbackReplyIfNeeded(runtime: StreamRuntimeState, emit: EmitEvent, reply: string) {
253
- const fallbackText = reply.trim();
254
- if (runtime.hasAssistantOutput || !fallbackText) {
255
- return;
256
- }
257
- const fallbackMessageId = buildDeltaMessageId();
258
- emit({ type: EventType.TEXT_START, messageId: fallbackMessageId });
259
- emit({ type: EventType.TEXT_DELTA, messageId: fallbackMessageId, delta: fallbackText });
260
- emit({ type: EventType.TEXT_END, messageId: fallbackMessageId });
261
- }
262
-
263
- export class NextbotRuntimeAgent implements IAgent {
264
- private activeAbortController: AbortController | null = null;
265
- private activeRunState: ActiveRunState | null = null;
266
-
267
- private buildMissingMetadataObservable = (clientRunId: string): Observable<AgentEvent> =>
268
- new Observable<AgentEvent>((subscriber) => {
269
- subscriber.next({
270
- type: EventType.RUN_ERROR,
271
- runId: clientRunId,
272
- error: 'nextbot runtime metadata is required'
273
- });
274
- subscriber.complete();
275
- });
276
-
277
- private openRunStream = (params: {
278
- metadata: NextbotAgentRunMetadata;
279
- signal: AbortSignal;
280
- onReady: (event: StreamReadyEvent) => void;
281
- onDelta: (event: StreamDeltaEvent) => void;
282
- onSessionEvent: (event: StreamSessionEvent) => void;
283
- }): Promise<StreamResult> => {
284
- const { metadata, signal, onReady, onDelta, onSessionEvent } = params;
285
- if (metadata.mode === 'send') {
286
- return openSendTurnStream({
287
- item: metadata.payload,
288
- requestedSkills: metadata.requestedSkills,
289
- signal,
290
- onReady,
291
- onDelta,
292
- onSessionEvent
293
- });
294
- }
295
- return openResumeRunStream({
296
- runId: (metadata as ResumeRunMetadata).runId,
297
- fromEventIndex: (metadata as ResumeRunMetadata).fromEventIndex,
298
- signal,
299
- onReady,
300
- onDelta,
301
- onSessionEvent
302
- });
303
- };
304
-
305
- private finalizeRunState = (abortController: AbortController, runState: ActiveRunState) => {
306
- if (this.activeAbortController === abortController) {
307
- this.activeAbortController = null;
308
- }
309
- if (this.activeRunState === runState) {
310
- this.activeRunState = null;
311
- }
312
- };
313
-
314
- private createRunObservable = ({
315
- metadata,
316
- clientRunId,
317
- abortController,
318
- runState
319
- }: RunObservableParams): Observable<AgentEvent> =>
320
- new Observable<AgentEvent>((subscriber) => {
321
- const runtime = createStreamRuntimeState();
322
- let disposed = false;
323
- const emit: EmitEvent = (event) => {
324
- if (!disposed) {
325
- subscriber.next(event);
326
- }
327
- };
328
-
329
- emit({ type: EventType.RUN_STARTED, runId: clientRunId });
330
-
331
- const streamTask = this.openRunStream({
332
- metadata,
333
- signal: abortController.signal,
334
- onReady: (event) => {
335
- if (this.activeRunState) {
336
- updateRunStateFromReady(this.activeRunState, event);
337
- }
338
- emitRunMetadata(emit, clientRunId, buildReadyMetadata(event));
339
- },
340
- onDelta: (event) => {
341
- handleDelta(runtime, emit, event);
342
- },
343
- onSessionEvent: (event) => {
344
- handleSession(runtime, emit, event);
345
- }
346
- });
347
-
348
- void streamTask
349
- .then((result) => {
350
- closeDelta(runtime, emit);
351
- emitFallbackReplyIfNeeded(runtime, emit, result.reply);
352
- emitRunMetadata(emit, clientRunId, buildFinalMetadata(result, runtime.hasAssistantSessionEvent));
353
- emit({
354
- type: EventType.RUN_FINISHED,
355
- runId: clientRunId
356
- });
357
- subscriber.complete();
358
- })
359
- .catch((error) => {
360
- closeDelta(runtime, emit);
361
- if (!isAbortLikeError(error)) {
362
- emit({
363
- type: EventType.RUN_ERROR,
364
- runId: clientRunId,
365
- error: error instanceof Error ? error.message : String(error)
366
- });
367
- }
368
- subscriber.complete();
369
- })
370
- .finally(() => {
371
- this.finalizeRunState(abortController, runState);
372
- });
373
-
374
- return () => {
375
- disposed = true;
376
- abortController.abort();
377
- if (this.activeAbortController === abortController) {
378
- this.activeAbortController = null;
379
- }
380
- };
381
- });
382
-
383
- abortRun = () => {
384
- const activeRunState = this.activeRunState;
385
- if (activeRunState?.backendStopSupported) {
386
- void requestStopRun(activeRunState);
387
- }
388
- this.activeAbortController?.abort();
389
- this.activeAbortController = null;
390
- this.activeRunState = null;
391
- };
392
-
393
- run = (input: RunAgentInput): Subscribable<AgentEvent> => {
394
- const metadata = readRunMetadata(input);
395
- const clientRunId = typeof input.runId === 'string' && input.runId.trim() ? input.runId : `run-${Date.now()}`;
396
- if (!metadata) {
397
- return this.buildMissingMetadataObservable(clientRunId);
398
- }
399
-
400
- const normalizedMetadata = withEnsuredSendRunId(metadata);
401
- this.abortRun();
402
- const abortController = new AbortController();
403
- this.activeAbortController = abortController;
404
- const runState = buildInitialRunState(normalizedMetadata);
405
- this.activeRunState = runState;
406
- return this.createRunObservable({
407
- metadata: normalizedMetadata,
408
- clientRunId,
409
- abortController,
410
- runState
411
- });
412
- };
413
- }
@@ -1,98 +0,0 @@
1
- import { EventType, type AgentEvent } from '@nextclaw/agent-chat';
2
- import type { SessionEventView } from '@/api/types';
3
-
4
- export function buildDeltaEvents(params: { messageId: string; delta: string; started: boolean }): AgentEvent[] {
5
- if (!params.delta) {
6
- return [];
7
- }
8
- if (params.started) {
9
- return [{ type: EventType.TEXT_DELTA, messageId: params.messageId, delta: params.delta }];
10
- }
11
- return [
12
- { type: EventType.TEXT_START, messageId: params.messageId },
13
- { type: EventType.TEXT_DELTA, messageId: params.messageId, delta: params.delta }
14
- ];
15
- }
16
-
17
- export function shouldCloseDeltaOnSessionEvent(event: SessionEventView): boolean {
18
- void event;
19
- return false;
20
- }
21
-
22
- export function isAssistantSessionEvent(event: SessionEventView): boolean {
23
- const role = event.message?.role?.toLowerCase().trim();
24
- return role === 'assistant';
25
- }
26
-
27
- export function buildSessionEventEvents(params: { event: SessionEventView; messageId: string }): AgentEvent[] {
28
- const { event, messageId } = params;
29
- const message = event.message;
30
- if (!message) {
31
- return [];
32
- }
33
- const role = message.role?.toLowerCase().trim();
34
- if (!role) {
35
- return [];
36
- }
37
-
38
- const events: AgentEvent[] = [];
39
-
40
- if (typeof message.reasoning_content === 'string' && message.reasoning_content.trim()) {
41
- events.push({ type: EventType.REASONING_START, messageId });
42
- events.push({
43
- type: EventType.REASONING_DELTA,
44
- messageId,
45
- delta: message.reasoning_content.trim()
46
- });
47
- events.push({ type: EventType.REASONING_END, messageId });
48
- }
49
-
50
- if (Array.isArray(message.tool_calls)) {
51
- for (let index = 0; index < message.tool_calls.length; index += 1) {
52
- const call = message.tool_calls[index];
53
- if (!call || typeof call !== 'object') {
54
- continue;
55
- }
56
- const callRecord = call as Record<string, unknown>;
57
- const fnValue = callRecord.function;
58
- const fn = typeof fnValue === 'object' && fnValue ? (fnValue as { name?: unknown; arguments?: unknown }) : null;
59
- const seqPrefix = typeof event.seq === 'number' ? String(event.seq) : 'unknown';
60
- const toolCallId = typeof callRecord.id === 'string' ? callRecord.id : `tool-${seqPrefix}-${index}`;
61
- const toolName = typeof fn?.name === 'string' ? fn.name : typeof callRecord.name === 'string' ? callRecord.name : 'tool';
62
- const rawArgs = fn?.arguments ?? callRecord.arguments ?? '';
63
- const args = typeof rawArgs === 'string' ? rawArgs : JSON.stringify(rawArgs ?? {});
64
- events.push({
65
- type: EventType.TOOL_CALL_START,
66
- messageId,
67
- toolCallId,
68
- toolName
69
- });
70
- events.push({
71
- type: EventType.TOOL_CALL_ARGS,
72
- toolCallId,
73
- args
74
- });
75
- events.push({
76
- type: EventType.TOOL_CALL_END,
77
- toolCallId
78
- });
79
- }
80
- }
81
-
82
- if (role === 'tool' || role === 'tool_result' || role === 'toolresult' || role === 'function') {
83
- const toolCallId = typeof message.tool_call_id === 'string' ? message.tool_call_id.trim() : '';
84
- if (toolCallId) {
85
- events.push({
86
- type: EventType.TOOL_CALL_RESULT,
87
- toolCallId,
88
- content: message.content
89
- });
90
- }
91
- }
92
-
93
- return events;
94
- }
95
-
96
- export function buildDeltaMessageId(): string {
97
- return `stream-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
98
- }