@nextclaw/ui 0.6.9 → 0.6.11

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 (83) hide show
  1. package/.eslintrc.cjs +10 -0
  2. package/CHANGELOG.md +15 -0
  3. package/dist/assets/{ChannelsList-DACqpUYZ.js → ChannelsList-C49JQ-Zt.js} +1 -1
  4. package/dist/assets/ChatPage-DIx05c6s.js +36 -0
  5. package/dist/assets/{DocBrowser-D7mjKkGe.js → DocBrowser-CpOosDEI.js} +1 -1
  6. package/dist/assets/{LogoBadge-BlDT-g9R.js → LogoBadge-CL_8ZPXU.js} +1 -1
  7. package/dist/assets/MarketplacePage-BOzko5s9.js +49 -0
  8. package/dist/assets/{ModelConfig-DwRU5qrw.js → ModelConfig-BZ4ZfaQB.js} +1 -1
  9. package/dist/assets/ProvidersList-fPpJ5gl6.js +1 -0
  10. package/dist/assets/{RuntimeConfig-C7BRLGSC.js → RuntimeConfig-Dt9pLB9P.js} +1 -1
  11. package/dist/assets/{SecretsConfig-D5xZh7VF.js → SecretsConfig-C1PU0Yy8.js} +2 -2
  12. package/dist/assets/{SessionsConfig-ovpj_otA.js → SessionsConfig-EskBOofQ.js} +2 -2
  13. package/dist/assets/{card-Bf4CtrW8.js → card-C7Gtw2Vs.js} +1 -1
  14. package/dist/assets/index-Cn6_2To7.js +8 -0
  15. package/dist/assets/index-nEYGCJTC.css +1 -0
  16. package/dist/assets/{input-CaKJyoWZ.js → input-oBvxsnV9.js} +1 -1
  17. package/dist/assets/{label-BaXSWTKI.js → label-C7F8lMpQ.js} +1 -1
  18. package/dist/assets/{page-layout-DA6PFRtQ.js → page-layout-DO8BlScF.js} +1 -1
  19. package/dist/assets/session-run-status-Kg0FwAPn.js +3 -0
  20. package/dist/assets/{switch-Cvd5wZs-.js → switch-C6a5GyZB.js} +1 -1
  21. package/dist/assets/{tabs-custom-0PybLkXs.js → tabs-custom-BatFap5k.js} +1 -1
  22. package/dist/assets/{useConfirmDialog-DdtpSju1.js → useConfirmDialog-zJzVKMdu.js} +2 -2
  23. package/dist/assets/{vendor-C--HHaLf.js → vendor-TlME1INH.js} +84 -84
  24. package/dist/index.html +3 -3
  25. package/package.json +4 -2
  26. package/src/App.tsx +1 -2
  27. package/src/api/config.ts +205 -202
  28. package/src/api/types.ts +54 -24
  29. package/src/components/chat/ChatConversationPanel.tsx +102 -121
  30. package/src/components/chat/ChatPage.tsx +165 -437
  31. package/src/components/chat/ChatSidebar.tsx +30 -36
  32. package/src/components/chat/ChatThread.tsx +73 -131
  33. package/src/components/chat/chat-input/ChatInputBarView.tsx +82 -0
  34. package/src/components/chat/chat-input/ChatInputBottomToolbar.tsx +71 -0
  35. package/src/components/chat/chat-input/components/ChatInputModelStateHint.tsx +39 -0
  36. package/src/components/chat/chat-input/components/ChatInputSelectedSkillsSection.tsx +31 -0
  37. package/src/components/chat/chat-input/components/ChatInputSlashPanelSection.tsx +112 -0
  38. package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputAttachButton.tsx +24 -0
  39. package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputModelSelector.tsx +58 -0
  40. package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputSendControls.tsx +56 -0
  41. package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputSessionTypeSelector.tsx +40 -0
  42. package/src/components/chat/chat-input/useChatInputBarController.ts +313 -0
  43. package/src/components/chat/chat-input.types.ts +15 -0
  44. package/src/components/chat/chat-page-data.ts +121 -0
  45. package/src/components/chat/chat-page-runtime.ts +221 -0
  46. package/src/components/chat/chat-session-route.ts +59 -0
  47. package/src/components/chat/chat-stream/nextbot-parsers.ts +52 -0
  48. package/src/components/chat/chat-stream/nextbot-runtime-agent.ts +413 -0
  49. package/src/components/chat/chat-stream/stream-event-adapter.ts +98 -0
  50. package/src/components/chat/chat-stream/transport.ts +159 -0
  51. package/src/components/chat/chat-stream/types.ts +76 -0
  52. package/src/components/chat/managers/chat-input.manager.ts +142 -0
  53. package/src/components/chat/managers/chat-run-status.manager.ts +32 -0
  54. package/src/components/chat/managers/chat-session-list.manager.ts +77 -0
  55. package/src/components/chat/managers/chat-stream-actions.manager.ts +34 -0
  56. package/src/components/chat/managers/chat-thread.manager.ts +86 -0
  57. package/src/components/chat/managers/chat-ui.manager.ts +65 -0
  58. package/src/components/chat/presenter/chat-presenter-context.tsx +25 -0
  59. package/src/components/chat/presenter/chat.presenter.ts +32 -0
  60. package/src/components/chat/stores/chat-input.store.ts +62 -0
  61. package/src/components/chat/stores/chat-run-status.store.ts +30 -0
  62. package/src/components/chat/stores/chat-session-list.store.ts +34 -0
  63. package/src/components/chat/stores/chat-thread.store.ts +52 -0
  64. package/src/components/chat/useChatRuntimeController.ts +134 -0
  65. package/src/components/chat/useChatSessionTypeState.ts +148 -0
  66. package/src/components/common/MaskedInput.tsx +1 -1
  67. package/src/components/config/ProviderForm.tsx +221 -14
  68. package/src/hooks/useConfig.ts +33 -2
  69. package/src/hooks/useObservable.ts +20 -0
  70. package/src/hooks/useWebSocket.ts +23 -1
  71. package/src/lib/chat-message.ts +2 -202
  72. package/src/lib/chat-runtime-utils.ts +250 -0
  73. package/src/lib/i18n.ts +11 -0
  74. package/tsconfig.json +2 -1
  75. package/vite.config.ts +2 -1
  76. package/dist/assets/ChatPage-iji0RkTR.js +0 -34
  77. package/dist/assets/MarketplacePage-CZq3jVgg.js +0 -49
  78. package/dist/assets/ProvidersList-DFxN3pjx.js +0 -1
  79. package/dist/assets/index-C_DhisNo.css +0 -1
  80. package/dist/assets/index-dKTqKCJo.js +0 -7
  81. package/dist/assets/session-run-status-CllIZxNf.js +0 -5
  82. package/src/components/chat/ChatInputBar.tsx +0 -590
  83. package/src/components/chat/useChatStreamController.ts +0 -591
package/dist/index.html CHANGED
@@ -6,9 +6,9 @@
6
6
  <link rel="icon" type="image/svg+xml" href="/logo.svg" />
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
8
  <title>NextClaw - 系统配置</title>
9
- <script type="module" crossorigin src="/assets/index-dKTqKCJo.js"></script>
10
- <link rel="modulepreload" crossorigin href="/assets/vendor-C--HHaLf.js">
11
- <link rel="stylesheet" crossorigin href="/assets/index-C_DhisNo.css">
9
+ <script type="module" crossorigin src="/assets/index-Cn6_2To7.js"></script>
10
+ <link rel="modulepreload" crossorigin href="/assets/vendor-TlME1INH.js">
11
+ <link rel="stylesheet" crossorigin href="/assets/index-nEYGCJTC.css">
12
12
  </head>
13
13
 
14
14
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/ui",
3
- "version": "0.6.9",
3
+ "version": "0.6.11",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -24,10 +24,12 @@
24
24
  "react-router-dom": "^7.13.0",
25
25
  "rehype-sanitize": "^6.0.0",
26
26
  "remark-gfm": "^4.0.1",
27
+ "rxjs": "^7.8.2",
27
28
  "sonner": "^1.7.1",
28
29
  "tailwind-merge": "^2.5.4",
29
30
  "zod": "^3.23.8",
30
- "zustand": "^5.0.2"
31
+ "zustand": "^5.0.2",
32
+ "@nextclaw/agent-chat": "0.1.1"
31
33
  },
32
34
  "devDependencies": {
33
35
  "@types/react": "^18.3.12",
package/src/App.tsx CHANGED
@@ -41,8 +41,7 @@ function AppContent() {
41
41
  <Routes>
42
42
  <Route path="/chat/skills" element={<Navigate to="/skills" replace />} />
43
43
  <Route path="/chat/cron" element={<Navigate to="/cron" replace />} />
44
- <Route path="/chat/:sessionId" element={<LazyRoute><ChatPage view="chat" /></LazyRoute>} />
45
- <Route path="/chat" element={<LazyRoute><ChatPage view="chat" /></LazyRoute>} />
44
+ <Route path="/chat/:sessionId?" element={<LazyRoute><ChatPage view="chat" /></LazyRoute>} />
46
45
  <Route path="/skills" element={<LazyRoute><ChatPage view="skills" /></LazyRoute>} />
47
46
  <Route path="/cron" element={<LazyRoute><ChatPage view="cron" /></LazyRoute>} />
48
47
  <Route path="/model" element={<LazyRoute><ModelConfigPage /></LazyRoute>} />
package/src/api/config.ts CHANGED
@@ -9,6 +9,7 @@ import type {
9
9
  ProviderConfigUpdate,
10
10
  ProviderConnectionTestRequest,
11
11
  ProviderConnectionTestResult,
12
+ ProviderAuthStartRequest,
12
13
  ProviderAuthStartResult,
13
14
  ProviderAuthPollRequest,
14
15
  ProviderAuthPollResult,
@@ -26,7 +27,12 @@ import type {
26
27
  SessionPatchUpdate,
27
28
  ChatTurnRequest,
28
29
  ChatTurnView,
30
+ ChatTurnStreamDeltaEvent,
31
+ ChatTurnStreamErrorEvent,
32
+ ChatTurnStreamReadyEvent,
33
+ ChatTurnStreamSessionEvent,
29
34
  ChatCapabilitiesView,
35
+ ChatSessionTypesView,
30
36
  ChatTurnStopRequest,
31
37
  ChatTurnStopResult,
32
38
  ChatRunListView,
@@ -35,10 +41,7 @@ import type {
35
41
  CronListView,
36
42
  CronEnableRequest,
37
43
  CronRunRequest,
38
- CronActionResult,
39
- ChatTurnStreamReadyEvent,
40
- ChatTurnStreamDeltaEvent,
41
- ChatTurnStreamSessionEvent
44
+ CronActionResult
42
45
  } from './types';
43
46
 
44
47
  // GET /api/app/meta
@@ -144,10 +147,13 @@ export async function testProviderConnection(
144
147
  }
145
148
 
146
149
  // POST /api/config/providers/:provider/auth/start
147
- export async function startProviderAuth(provider: string): Promise<ProviderAuthStartResult> {
150
+ export async function startProviderAuth(
151
+ provider: string,
152
+ data: ProviderAuthStartRequest = {}
153
+ ): Promise<ProviderAuthStartResult> {
148
154
  const response = await api.post<ProviderAuthStartResult>(
149
155
  `/api/config/providers/${provider}/auth/start`,
150
- {}
156
+ data
151
157
  );
152
158
  if (!response.ok) {
153
159
  throw new Error(response.error.message);
@@ -300,91 +306,24 @@ export async function sendChatTurn(data: ChatTurnRequest): Promise<ChatTurnView>
300
306
  return response.data;
301
307
  }
302
308
 
303
- // GET /api/chat/capabilities
304
- export async function fetchChatCapabilities(params?: { sessionKey?: string; agentId?: string }): Promise<ChatCapabilitiesView> {
305
- const query = new URLSearchParams();
306
- if (params?.sessionKey?.trim()) {
307
- query.set('sessionKey', params.sessionKey.trim());
308
- }
309
- if (params?.agentId?.trim()) {
310
- query.set('agentId', params.agentId.trim());
311
- }
312
- const suffix = query.toString();
313
- const response = await api.get<ChatCapabilitiesView>(suffix ? `/api/chat/capabilities?${suffix}` : '/api/chat/capabilities');
314
- if (!response.ok) {
315
- throw new Error(response.error.message);
316
- }
317
- return response.data;
318
- }
319
-
320
- // POST /api/chat/turn/stop
321
- export async function stopChatTurn(data: ChatTurnStopRequest): Promise<ChatTurnStopResult> {
322
- const response = await api.post<ChatTurnStopResult>('/api/chat/turn/stop', data);
323
- if (!response.ok) {
324
- throw new Error(response.error.message);
325
- }
326
- return response.data;
327
- }
328
-
329
- // GET /api/chat/runs
330
- export async function fetchChatRuns(params?: {
331
- sessionKey?: string;
332
- states?: ChatRunState[];
333
- limit?: number;
334
- }): Promise<ChatRunListView> {
335
- const query = new URLSearchParams();
336
- if (params?.sessionKey?.trim()) {
337
- query.set('sessionKey', params.sessionKey.trim());
338
- }
339
- if (Array.isArray(params?.states) && params.states.length > 0) {
340
- query.set('states', params.states.join(','));
341
- }
342
- if (typeof params?.limit === 'number' && Number.isFinite(params.limit)) {
343
- query.set('limit', String(Math.max(0, Math.trunc(params.limit))));
344
- }
345
- const suffix = query.toString();
346
- const response = await api.get<ChatRunListView>(suffix ? `/api/chat/runs?${suffix}` : '/api/chat/runs');
347
- if (!response.ok) {
348
- throw new Error(response.error.message);
349
- }
350
- return response.data;
351
- }
352
-
353
- // GET /api/chat/runs/:runId
354
- export async function fetchChatRun(runId: string): Promise<ChatRunView> {
355
- const response = await api.get<ChatRunView>(`/api/chat/runs/${encodeURIComponent(runId)}`);
356
- if (!response.ok) {
357
- throw new Error(response.error.message);
358
- }
359
- return response.data;
360
- }
361
-
362
- type ChatTurnStreamOptions = {
363
- signal?: AbortSignal;
364
- onReady?: (event: ChatTurnStreamReadyEvent) => void;
365
- onDelta?: (event: ChatTurnStreamDeltaEvent) => void;
366
- onSessionEvent?: (event: ChatTurnStreamSessionEvent) => void;
367
- };
368
-
369
- type SseParsedEvent = {
370
- event: string;
371
- data: string;
372
- };
373
-
374
- function parseSseFrame(frame: string): SseParsedEvent | null {
375
- const lines = frame.split(/\r?\n/);
376
- let event = 'message';
309
+ function parseSseFrame(frame: string): { event: string; data: string } | null {
310
+ const lines = frame.split('\n');
311
+ let event = '';
377
312
  const dataLines: string[] = [];
378
- for (const line of lines) {
313
+ for (const raw of lines) {
314
+ const line = raw.trimEnd();
315
+ if (!line || line.startsWith(':')) {
316
+ continue;
317
+ }
379
318
  if (line.startsWith('event:')) {
380
- event = line.slice('event:'.length).trim() || 'message';
319
+ event = line.slice(6).trim();
381
320
  continue;
382
321
  }
383
322
  if (line.startsWith('data:')) {
384
- dataLines.push(line.slice('data:'.length).trimStart());
323
+ dataLines.push(line.slice(5).trimStart());
385
324
  }
386
325
  }
387
- if (dataLines.length === 0) {
326
+ if (!event) {
388
327
  return null;
389
328
  }
390
329
  return {
@@ -393,178 +332,242 @@ function parseSseFrame(frame: string): SseParsedEvent | null {
393
332
  };
394
333
  }
395
334
 
396
- async function consumeChatTurnSseStream(
397
- response: Response,
398
- options: ChatTurnStreamOptions = {}
399
- ): Promise<ChatTurnView> {
335
+ async function readSseStream(params: {
336
+ path: string;
337
+ method: 'GET' | 'POST';
338
+ body?: unknown;
339
+ signal?: AbortSignal;
340
+ onReady: (event: ChatTurnStreamReadyEvent) => void;
341
+ onDelta: (event: ChatTurnStreamDeltaEvent) => void;
342
+ onSessionEvent: (event: ChatTurnStreamSessionEvent) => void;
343
+ }): Promise<{ sessionKey: string; reply: string }> {
344
+ const response = await fetch(`${API_BASE}${params.path}`, {
345
+ method: params.method,
346
+ headers: {
347
+ 'Content-Type': 'application/json',
348
+ Accept: 'text/event-stream'
349
+ },
350
+ ...(params.body !== undefined ? { body: JSON.stringify(params.body) } : {}),
351
+ ...(params.signal ? { signal: params.signal } : {})
352
+ });
353
+
400
354
  if (!response.ok) {
401
- let message = `chat stream failed (${response.status} ${response.statusText})`;
402
- try {
403
- const payload = await response.json() as { ok?: boolean; error?: { message?: string } };
404
- if (payload?.error?.message) {
405
- message = payload.error.message;
406
- }
407
- } catch {
408
- const text = await response.text().catch(() => '');
409
- if (text.trim()) {
410
- message = text.trim();
411
- }
412
- }
413
- throw new Error(message);
355
+ const text = await response.text();
356
+ const fallback = `HTTP ${response.status}`;
357
+ const trimmed = text.trim();
358
+ throw new Error(trimmed || fallback);
414
359
  }
415
360
 
416
- if (!response.body) {
417
- throw new Error('chat stream is not readable');
361
+ const reader = response.body?.getReader();
362
+ if (!reader) {
363
+ throw new Error('SSE response body unavailable');
418
364
  }
419
365
 
420
- const reader = response.body.getReader();
421
366
  const decoder = new TextDecoder();
422
367
  let buffer = '';
423
- let finalView: ChatTurnView | null = null;
368
+ let finalResult: { sessionKey: string; reply: string } | null = null;
369
+ let readySessionKey: string | null = null;
424
370
 
425
- const handleFrame = (frame: string) => {
371
+ const consumeFrame = (frame: string) => {
426
372
  const parsed = parseSseFrame(frame);
427
373
  if (!parsed) {
428
374
  return;
429
375
  }
430
- if (parsed.event === 'ready') {
376
+
377
+ let payload: unknown = undefined;
378
+ if (parsed.data) {
431
379
  try {
432
- const readyPayload = JSON.parse(parsed.data) as {
433
- sessionKey?: string;
434
- requestedAt?: string;
435
- runId?: string;
436
- stopSupported?: boolean;
437
- stopReason?: string;
438
- };
439
- options.onReady?.({
440
- event: 'ready',
441
- sessionKey: String(readyPayload.sessionKey ?? ''),
442
- requestedAt: String(readyPayload.requestedAt ?? ''),
443
- ...(typeof readyPayload.runId === 'string' && readyPayload.runId.trim().length > 0
444
- ? { runId: readyPayload.runId.trim() }
445
- : {}),
446
- ...(typeof readyPayload.stopSupported === 'boolean'
447
- ? { stopSupported: readyPayload.stopSupported }
448
- : {}),
449
- ...(typeof readyPayload.stopReason === 'string' && readyPayload.stopReason.trim().length > 0
450
- ? { stopReason: readyPayload.stopReason.trim() }
451
- : {})
452
- });
380
+ payload = JSON.parse(parsed.data);
453
381
  } catch {
454
- // ignore malformed ready event payload
382
+ payload = undefined;
455
383
  }
384
+ }
385
+
386
+ if (parsed.event === 'ready') {
387
+ const ready = (payload ?? {}) as ChatTurnStreamReadyEvent;
388
+ readySessionKey = typeof ready.sessionKey === 'string' && ready.sessionKey.trim() ? ready.sessionKey : readySessionKey;
389
+ params.onReady(ready);
456
390
  return;
457
391
  }
392
+
458
393
  if (parsed.event === 'delta') {
459
- try {
460
- const deltaPayload = JSON.parse(parsed.data) as { delta?: string };
461
- if (typeof deltaPayload.delta === 'string' && deltaPayload.delta.length > 0) {
462
- options.onDelta?.({
463
- event: 'delta',
464
- delta: deltaPayload.delta
465
- });
466
- }
467
- } catch {
468
- // ignore malformed delta event payload
469
- }
394
+ params.onDelta((payload ?? { delta: '' }) as ChatTurnStreamDeltaEvent);
470
395
  return;
471
396
  }
397
+
472
398
  if (parsed.event === 'session_event') {
473
- try {
474
- options.onSessionEvent?.({
475
- event: 'session_event',
476
- data: JSON.parse(parsed.data)
477
- });
478
- } catch {
479
- // ignore malformed session_event payload
480
- }
399
+ params.onSessionEvent({ data: payload as ChatTurnStreamSessionEvent['data'] });
481
400
  return;
482
401
  }
402
+
483
403
  if (parsed.event === 'final') {
484
- finalView = JSON.parse(parsed.data) as ChatTurnView;
404
+ const result = payload as ChatTurnView;
405
+ finalResult = {
406
+ sessionKey: typeof result?.sessionKey === 'string' && result.sessionKey.trim()
407
+ ? result.sessionKey
408
+ : (readySessionKey ?? ''),
409
+ reply: typeof result?.reply === 'string' ? result.reply : ''
410
+ };
485
411
  return;
486
412
  }
413
+
487
414
  if (parsed.event === 'error') {
488
- try {
489
- const errPayload = JSON.parse(parsed.data) as { message?: string };
490
- throw new Error(typeof errPayload.message === 'string' && errPayload.message ? errPayload.message : 'chat stream failed');
491
- } catch (error) {
492
- if (error instanceof Error) {
493
- throw error;
494
- }
495
- throw new Error('chat stream failed');
496
- }
415
+ const errorPayload = (payload ?? {}) as ChatTurnStreamErrorEvent;
416
+ throw new Error((errorPayload.message ?? '').trim() || 'chat stream failed');
497
417
  }
498
418
  };
499
419
 
500
- let streamDone = false;
501
- while (!streamDone) {
502
- const { done, value } = await reader.read();
503
- if (done) {
504
- streamDone = true;
505
- continue;
506
- }
507
- buffer += decoder.decode(value, { stream: true });
508
-
509
- let boundary = buffer.indexOf('\n\n');
510
- while (boundary !== -1) {
511
- const frame = buffer.slice(0, boundary).trim();
512
- buffer = buffer.slice(boundary + 2);
513
- if (frame) {
514
- handleFrame(frame);
420
+ try {
421
+ let isReading = true;
422
+ while (isReading) {
423
+ const { value, done } = await reader.read();
424
+ if (done) {
425
+ isReading = false;
426
+ continue;
515
427
  }
516
- boundary = buffer.indexOf('\n\n');
428
+ buffer += decoder.decode(value, { stream: true });
429
+ let boundary = buffer.indexOf('\n\n');
430
+ while (boundary !== -1) {
431
+ const frame = buffer.slice(0, boundary);
432
+ buffer = buffer.slice(boundary + 2);
433
+ consumeFrame(frame);
434
+ boundary = buffer.indexOf('\n\n');
435
+ }
436
+ }
437
+ if (buffer.trim()) {
438
+ consumeFrame(buffer);
517
439
  }
440
+ } finally {
441
+ reader.releaseLock();
518
442
  }
519
443
 
520
- const trailing = buffer.trim();
521
- if (trailing) {
522
- handleFrame(trailing);
444
+ if (finalResult) {
445
+ return finalResult;
523
446
  }
524
447
 
525
- if (!finalView) {
526
- throw new Error('chat stream ended without final result');
448
+ if (readySessionKey) {
449
+ return { sessionKey: readySessionKey, reply: '' };
527
450
  }
528
- return finalView;
451
+
452
+ throw new Error('chat stream ended without final event');
529
453
  }
530
454
 
455
+ // POST /api/chat/turn/stream
531
456
  export async function sendChatTurnStream(
532
457
  data: ChatTurnRequest,
533
- options: ChatTurnStreamOptions = {}
534
- ): Promise<ChatTurnView> {
535
- const response = await fetch(`${API_BASE}/api/chat/turn/stream`, {
458
+ params: {
459
+ signal?: AbortSignal;
460
+ onReady: (event: ChatTurnStreamReadyEvent) => void;
461
+ onDelta: (event: ChatTurnStreamDeltaEvent) => void;
462
+ onSessionEvent: (event: ChatTurnStreamSessionEvent) => void;
463
+ }
464
+ ): Promise<{ sessionKey: string; reply: string }> {
465
+ return readSseStream({
466
+ path: '/api/chat/turn/stream',
536
467
  method: 'POST',
537
- headers: {
538
- 'Content-Type': 'application/json',
539
- Accept: 'text/event-stream'
540
- },
541
- body: JSON.stringify(data),
542
- signal: options.signal
468
+ body: data,
469
+ signal: params.signal,
470
+ onReady: params.onReady,
471
+ onDelta: params.onDelta,
472
+ onSessionEvent: params.onSessionEvent
543
473
  });
544
- return consumeChatTurnSseStream(response, options);
545
474
  }
546
475
 
476
+ // GET /api/chat/runs/:runId/stream
547
477
  export async function streamChatRun(
548
- params: {
478
+ data: {
549
479
  runId: string;
550
480
  fromEventIndex?: number;
551
481
  },
552
- options: ChatTurnStreamOptions = {}
553
- ): Promise<ChatTurnView> {
482
+ params: {
483
+ signal?: AbortSignal;
484
+ onReady: (event: ChatTurnStreamReadyEvent) => void;
485
+ onDelta: (event: ChatTurnStreamDeltaEvent) => void;
486
+ onSessionEvent: (event: ChatTurnStreamSessionEvent) => void;
487
+ }
488
+ ): Promise<{ sessionKey: string; reply: string }> {
554
489
  const query = new URLSearchParams();
555
- if (typeof params.fromEventIndex === 'number' && Number.isFinite(params.fromEventIndex)) {
556
- query.set('fromEventIndex', String(Math.max(0, Math.trunc(params.fromEventIndex))));
490
+ if (typeof data.fromEventIndex === 'number' && Number.isFinite(data.fromEventIndex)) {
491
+ query.set('fromEventIndex', String(Math.max(0, Math.trunc(data.fromEventIndex))));
557
492
  }
558
493
  const suffix = query.toString();
559
- const url = `${API_BASE}/api/chat/runs/${encodeURIComponent(params.runId)}/stream${suffix ? `?${suffix}` : ''}`;
560
- const response = await fetch(url, {
494
+ const path = `/api/chat/runs/${encodeURIComponent(data.runId)}/stream${suffix ? `?${suffix}` : ''}`;
495
+ return readSseStream({
496
+ path,
561
497
  method: 'GET',
562
- headers: {
563
- Accept: 'text/event-stream'
564
- },
565
- signal: options.signal
498
+ signal: params.signal,
499
+ onReady: params.onReady,
500
+ onDelta: params.onDelta,
501
+ onSessionEvent: params.onSessionEvent
566
502
  });
567
- return consumeChatTurnSseStream(response, options);
503
+ }
504
+
505
+ // GET /api/chat/capabilities
506
+ export async function fetchChatCapabilities(params?: { sessionKey?: string; agentId?: string }): Promise<ChatCapabilitiesView> {
507
+ const query = new URLSearchParams();
508
+ if (params?.sessionKey?.trim()) {
509
+ query.set('sessionKey', params.sessionKey.trim());
510
+ }
511
+ if (params?.agentId?.trim()) {
512
+ query.set('agentId', params.agentId.trim());
513
+ }
514
+ const suffix = query.toString();
515
+ const response = await api.get<ChatCapabilitiesView>(suffix ? `/api/chat/capabilities?${suffix}` : '/api/chat/capabilities');
516
+ if (!response.ok) {
517
+ throw new Error(response.error.message);
518
+ }
519
+ return response.data;
520
+ }
521
+
522
+ // GET /api/chat/session-types
523
+ export async function fetchChatSessionTypes(): Promise<ChatSessionTypesView> {
524
+ const response = await api.get<ChatSessionTypesView>('/api/chat/session-types');
525
+ if (!response.ok) {
526
+ throw new Error(response.error.message);
527
+ }
528
+ return response.data;
529
+ }
530
+
531
+ // POST /api/chat/turn/stop
532
+ export async function stopChatTurn(data: ChatTurnStopRequest): Promise<ChatTurnStopResult> {
533
+ const response = await api.post<ChatTurnStopResult>('/api/chat/turn/stop', data);
534
+ if (!response.ok) {
535
+ throw new Error(response.error.message);
536
+ }
537
+ return response.data;
538
+ }
539
+
540
+ // GET /api/chat/runs
541
+ export async function fetchChatRuns(params?: {
542
+ sessionKey?: string;
543
+ states?: ChatRunState[];
544
+ limit?: number;
545
+ }): Promise<ChatRunListView> {
546
+ const query = new URLSearchParams();
547
+ if (params?.sessionKey?.trim()) {
548
+ query.set('sessionKey', params.sessionKey.trim());
549
+ }
550
+ if (Array.isArray(params?.states) && params.states.length > 0) {
551
+ query.set('states', params.states.join(','));
552
+ }
553
+ if (typeof params?.limit === 'number' && Number.isFinite(params.limit)) {
554
+ query.set('limit', String(Math.max(0, Math.trunc(params.limit))));
555
+ }
556
+ const suffix = query.toString();
557
+ const response = await api.get<ChatRunListView>(suffix ? `/api/chat/runs?${suffix}` : '/api/chat/runs');
558
+ if (!response.ok) {
559
+ throw new Error(response.error.message);
560
+ }
561
+ return response.data;
562
+ }
563
+
564
+ // GET /api/chat/runs/:runId
565
+ export async function fetchChatRun(runId: string): Promise<ChatRunView> {
566
+ const response = await api.get<ChatRunView>(`/api/chat/runs/${encodeURIComponent(runId)}`);
567
+ if (!response.ok) {
568
+ throw new Error(response.error.message);
569
+ }
570
+ return response.data;
568
571
  }
569
572
 
570
573
  // GET /api/cron