@nextclaw/ui 0.6.10 → 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 (80) hide show
  1. package/.eslintrc.cjs +10 -0
  2. package/CHANGELOG.md +9 -0
  3. package/dist/assets/{ChannelsList-TyMb5Mgz.js → ChannelsList-C49JQ-Zt.js} +1 -1
  4. package/dist/assets/ChatPage-DIx05c6s.js +36 -0
  5. package/dist/assets/{DocBrowser-CNtrA0ps.js → DocBrowser-CpOosDEI.js} +1 -1
  6. package/dist/assets/{LogoBadge-BLqiOM5D.js → LogoBadge-CL_8ZPXU.js} +1 -1
  7. package/dist/assets/MarketplacePage-BOzko5s9.js +49 -0
  8. package/dist/assets/{ModelConfig-CCsQ8KFq.js → ModelConfig-BZ4ZfaQB.js} +1 -1
  9. package/dist/assets/ProvidersList-fPpJ5gl6.js +1 -0
  10. package/dist/assets/{RuntimeConfig-BO6s-ls-.js → RuntimeConfig-Dt9pLB9P.js} +1 -1
  11. package/dist/assets/{SecretsConfig-mayFdxpM.js → SecretsConfig-C1PU0Yy8.js} +2 -2
  12. package/dist/assets/{SessionsConfig-DAIczdBj.js → SessionsConfig-EskBOofQ.js} +2 -2
  13. package/dist/assets/{card-BP5YnL-G.js → card-C7Gtw2Vs.js} +1 -1
  14. package/dist/assets/index-Cn6_2To7.js +8 -0
  15. package/dist/assets/{index-BUiahmWm.css → index-nEYGCJTC.css} +1 -1
  16. package/dist/assets/{input-B1D2QX0O.js → input-oBvxsnV9.js} +1 -1
  17. package/dist/assets/{label-DW0j-fXA.js → label-C7F8lMpQ.js} +1 -1
  18. package/dist/assets/{page-layout-Ch-H9gD-.js → page-layout-DO8BlScF.js} +1 -1
  19. package/dist/assets/session-run-status-Kg0FwAPn.js +3 -0
  20. package/dist/assets/{switch-_cZHlGKB.js → switch-C6a5GyZB.js} +1 -1
  21. package/dist/assets/{tabs-custom-ARxqYYjG.js → tabs-custom-BatFap5k.js} +1 -1
  22. package/dist/assets/{useConfirmDialog-BaU7nIat.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 +199 -200
  28. package/src/api/types.ts +36 -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/hooks/useConfig.ts +31 -1
  68. package/src/hooks/useObservable.ts +20 -0
  69. package/src/lib/chat-message.ts +2 -202
  70. package/src/lib/chat-runtime-utils.ts +250 -0
  71. package/src/lib/i18n.ts +9 -0
  72. package/tsconfig.json +2 -1
  73. package/vite.config.ts +2 -1
  74. package/dist/assets/ChatPage-CQerYqvy.js +0 -34
  75. package/dist/assets/MarketplacePage-CotZxxNe.js +0 -49
  76. package/dist/assets/ProvidersList-BYYX5K_g.js +0 -1
  77. package/dist/assets/index-D6_5HaDl.js +0 -7
  78. package/dist/assets/session-run-status-BUYsQeWs.js +0 -5
  79. package/src/components/chat/ChatInputBar.tsx +0 -590
  80. 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-D6_5HaDl.js"></script>
10
- <link rel="modulepreload" crossorigin href="/assets/vendor-C--HHaLf.js">
11
- <link rel="stylesheet" crossorigin href="/assets/index-BUiahmWm.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.10",
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
@@ -27,7 +27,12 @@ import type {
27
27
  SessionPatchUpdate,
28
28
  ChatTurnRequest,
29
29
  ChatTurnView,
30
+ ChatTurnStreamDeltaEvent,
31
+ ChatTurnStreamErrorEvent,
32
+ ChatTurnStreamReadyEvent,
33
+ ChatTurnStreamSessionEvent,
30
34
  ChatCapabilitiesView,
35
+ ChatSessionTypesView,
31
36
  ChatTurnStopRequest,
32
37
  ChatTurnStopResult,
33
38
  ChatRunListView,
@@ -36,10 +41,7 @@ import type {
36
41
  CronListView,
37
42
  CronEnableRequest,
38
43
  CronRunRequest,
39
- CronActionResult,
40
- ChatTurnStreamReadyEvent,
41
- ChatTurnStreamDeltaEvent,
42
- ChatTurnStreamSessionEvent
44
+ CronActionResult
43
45
  } from './types';
44
46
 
45
47
  // GET /api/app/meta
@@ -304,91 +306,24 @@ export async function sendChatTurn(data: ChatTurnRequest): Promise<ChatTurnView>
304
306
  return response.data;
305
307
  }
306
308
 
307
- // GET /api/chat/capabilities
308
- export async function fetchChatCapabilities(params?: { sessionKey?: string; agentId?: string }): Promise<ChatCapabilitiesView> {
309
- const query = new URLSearchParams();
310
- if (params?.sessionKey?.trim()) {
311
- query.set('sessionKey', params.sessionKey.trim());
312
- }
313
- if (params?.agentId?.trim()) {
314
- query.set('agentId', params.agentId.trim());
315
- }
316
- const suffix = query.toString();
317
- const response = await api.get<ChatCapabilitiesView>(suffix ? `/api/chat/capabilities?${suffix}` : '/api/chat/capabilities');
318
- if (!response.ok) {
319
- throw new Error(response.error.message);
320
- }
321
- return response.data;
322
- }
323
-
324
- // POST /api/chat/turn/stop
325
- export async function stopChatTurn(data: ChatTurnStopRequest): Promise<ChatTurnStopResult> {
326
- const response = await api.post<ChatTurnStopResult>('/api/chat/turn/stop', data);
327
- if (!response.ok) {
328
- throw new Error(response.error.message);
329
- }
330
- return response.data;
331
- }
332
-
333
- // GET /api/chat/runs
334
- export async function fetchChatRuns(params?: {
335
- sessionKey?: string;
336
- states?: ChatRunState[];
337
- limit?: number;
338
- }): Promise<ChatRunListView> {
339
- const query = new URLSearchParams();
340
- if (params?.sessionKey?.trim()) {
341
- query.set('sessionKey', params.sessionKey.trim());
342
- }
343
- if (Array.isArray(params?.states) && params.states.length > 0) {
344
- query.set('states', params.states.join(','));
345
- }
346
- if (typeof params?.limit === 'number' && Number.isFinite(params.limit)) {
347
- query.set('limit', String(Math.max(0, Math.trunc(params.limit))));
348
- }
349
- const suffix = query.toString();
350
- const response = await api.get<ChatRunListView>(suffix ? `/api/chat/runs?${suffix}` : '/api/chat/runs');
351
- if (!response.ok) {
352
- throw new Error(response.error.message);
353
- }
354
- return response.data;
355
- }
356
-
357
- // GET /api/chat/runs/:runId
358
- export async function fetchChatRun(runId: string): Promise<ChatRunView> {
359
- const response = await api.get<ChatRunView>(`/api/chat/runs/${encodeURIComponent(runId)}`);
360
- if (!response.ok) {
361
- throw new Error(response.error.message);
362
- }
363
- return response.data;
364
- }
365
-
366
- type ChatTurnStreamOptions = {
367
- signal?: AbortSignal;
368
- onReady?: (event: ChatTurnStreamReadyEvent) => void;
369
- onDelta?: (event: ChatTurnStreamDeltaEvent) => void;
370
- onSessionEvent?: (event: ChatTurnStreamSessionEvent) => void;
371
- };
372
-
373
- type SseParsedEvent = {
374
- event: string;
375
- data: string;
376
- };
377
-
378
- function parseSseFrame(frame: string): SseParsedEvent | null {
379
- const lines = frame.split(/\r?\n/);
380
- let event = 'message';
309
+ function parseSseFrame(frame: string): { event: string; data: string } | null {
310
+ const lines = frame.split('\n');
311
+ let event = '';
381
312
  const dataLines: string[] = [];
382
- for (const line of lines) {
313
+ for (const raw of lines) {
314
+ const line = raw.trimEnd();
315
+ if (!line || line.startsWith(':')) {
316
+ continue;
317
+ }
383
318
  if (line.startsWith('event:')) {
384
- event = line.slice('event:'.length).trim() || 'message';
319
+ event = line.slice(6).trim();
385
320
  continue;
386
321
  }
387
322
  if (line.startsWith('data:')) {
388
- dataLines.push(line.slice('data:'.length).trimStart());
323
+ dataLines.push(line.slice(5).trimStart());
389
324
  }
390
325
  }
391
- if (dataLines.length === 0) {
326
+ if (!event) {
392
327
  return null;
393
328
  }
394
329
  return {
@@ -397,178 +332,242 @@ function parseSseFrame(frame: string): SseParsedEvent | null {
397
332
  };
398
333
  }
399
334
 
400
- async function consumeChatTurnSseStream(
401
- response: Response,
402
- options: ChatTurnStreamOptions = {}
403
- ): 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
+
404
354
  if (!response.ok) {
405
- let message = `chat stream failed (${response.status} ${response.statusText})`;
406
- try {
407
- const payload = await response.json() as { ok?: boolean; error?: { message?: string } };
408
- if (payload?.error?.message) {
409
- message = payload.error.message;
410
- }
411
- } catch {
412
- const text = await response.text().catch(() => '');
413
- if (text.trim()) {
414
- message = text.trim();
415
- }
416
- }
417
- 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);
418
359
  }
419
360
 
420
- if (!response.body) {
421
- 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');
422
364
  }
423
365
 
424
- const reader = response.body.getReader();
425
366
  const decoder = new TextDecoder();
426
367
  let buffer = '';
427
- let finalView: ChatTurnView | null = null;
368
+ let finalResult: { sessionKey: string; reply: string } | null = null;
369
+ let readySessionKey: string | null = null;
428
370
 
429
- const handleFrame = (frame: string) => {
371
+ const consumeFrame = (frame: string) => {
430
372
  const parsed = parseSseFrame(frame);
431
373
  if (!parsed) {
432
374
  return;
433
375
  }
434
- if (parsed.event === 'ready') {
376
+
377
+ let payload: unknown = undefined;
378
+ if (parsed.data) {
435
379
  try {
436
- const readyPayload = JSON.parse(parsed.data) as {
437
- sessionKey?: string;
438
- requestedAt?: string;
439
- runId?: string;
440
- stopSupported?: boolean;
441
- stopReason?: string;
442
- };
443
- options.onReady?.({
444
- event: 'ready',
445
- sessionKey: String(readyPayload.sessionKey ?? ''),
446
- requestedAt: String(readyPayload.requestedAt ?? ''),
447
- ...(typeof readyPayload.runId === 'string' && readyPayload.runId.trim().length > 0
448
- ? { runId: readyPayload.runId.trim() }
449
- : {}),
450
- ...(typeof readyPayload.stopSupported === 'boolean'
451
- ? { stopSupported: readyPayload.stopSupported }
452
- : {}),
453
- ...(typeof readyPayload.stopReason === 'string' && readyPayload.stopReason.trim().length > 0
454
- ? { stopReason: readyPayload.stopReason.trim() }
455
- : {})
456
- });
380
+ payload = JSON.parse(parsed.data);
457
381
  } catch {
458
- // ignore malformed ready event payload
382
+ payload = undefined;
459
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);
460
390
  return;
461
391
  }
392
+
462
393
  if (parsed.event === 'delta') {
463
- try {
464
- const deltaPayload = JSON.parse(parsed.data) as { delta?: string };
465
- if (typeof deltaPayload.delta === 'string' && deltaPayload.delta.length > 0) {
466
- options.onDelta?.({
467
- event: 'delta',
468
- delta: deltaPayload.delta
469
- });
470
- }
471
- } catch {
472
- // ignore malformed delta event payload
473
- }
394
+ params.onDelta((payload ?? { delta: '' }) as ChatTurnStreamDeltaEvent);
474
395
  return;
475
396
  }
397
+
476
398
  if (parsed.event === 'session_event') {
477
- try {
478
- options.onSessionEvent?.({
479
- event: 'session_event',
480
- data: JSON.parse(parsed.data)
481
- });
482
- } catch {
483
- // ignore malformed session_event payload
484
- }
399
+ params.onSessionEvent({ data: payload as ChatTurnStreamSessionEvent['data'] });
485
400
  return;
486
401
  }
402
+
487
403
  if (parsed.event === 'final') {
488
- 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
+ };
489
411
  return;
490
412
  }
413
+
491
414
  if (parsed.event === 'error') {
492
- try {
493
- const errPayload = JSON.parse(parsed.data) as { message?: string };
494
- throw new Error(typeof errPayload.message === 'string' && errPayload.message ? errPayload.message : 'chat stream failed');
495
- } catch (error) {
496
- if (error instanceof Error) {
497
- throw error;
498
- }
499
- throw new Error('chat stream failed');
500
- }
415
+ const errorPayload = (payload ?? {}) as ChatTurnStreamErrorEvent;
416
+ throw new Error((errorPayload.message ?? '').trim() || 'chat stream failed');
501
417
  }
502
418
  };
503
419
 
504
- let streamDone = false;
505
- while (!streamDone) {
506
- const { done, value } = await reader.read();
507
- if (done) {
508
- streamDone = true;
509
- continue;
510
- }
511
- buffer += decoder.decode(value, { stream: true });
512
-
513
- let boundary = buffer.indexOf('\n\n');
514
- while (boundary !== -1) {
515
- const frame = buffer.slice(0, boundary).trim();
516
- buffer = buffer.slice(boundary + 2);
517
- if (frame) {
518
- 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;
427
+ }
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');
519
435
  }
520
- boundary = buffer.indexOf('\n\n');
521
436
  }
437
+ if (buffer.trim()) {
438
+ consumeFrame(buffer);
439
+ }
440
+ } finally {
441
+ reader.releaseLock();
522
442
  }
523
443
 
524
- const trailing = buffer.trim();
525
- if (trailing) {
526
- handleFrame(trailing);
444
+ if (finalResult) {
445
+ return finalResult;
527
446
  }
528
447
 
529
- if (!finalView) {
530
- throw new Error('chat stream ended without final result');
448
+ if (readySessionKey) {
449
+ return { sessionKey: readySessionKey, reply: '' };
531
450
  }
532
- return finalView;
451
+
452
+ throw new Error('chat stream ended without final event');
533
453
  }
534
454
 
455
+ // POST /api/chat/turn/stream
535
456
  export async function sendChatTurnStream(
536
457
  data: ChatTurnRequest,
537
- options: ChatTurnStreamOptions = {}
538
- ): Promise<ChatTurnView> {
539
- 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',
540
467
  method: 'POST',
541
- headers: {
542
- 'Content-Type': 'application/json',
543
- Accept: 'text/event-stream'
544
- },
545
- body: JSON.stringify(data),
546
- signal: options.signal
468
+ body: data,
469
+ signal: params.signal,
470
+ onReady: params.onReady,
471
+ onDelta: params.onDelta,
472
+ onSessionEvent: params.onSessionEvent
547
473
  });
548
- return consumeChatTurnSseStream(response, options);
549
474
  }
550
475
 
476
+ // GET /api/chat/runs/:runId/stream
551
477
  export async function streamChatRun(
552
- params: {
478
+ data: {
553
479
  runId: string;
554
480
  fromEventIndex?: number;
555
481
  },
556
- options: ChatTurnStreamOptions = {}
557
- ): 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 }> {
558
489
  const query = new URLSearchParams();
559
- if (typeof params.fromEventIndex === 'number' && Number.isFinite(params.fromEventIndex)) {
560
- 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))));
561
492
  }
562
493
  const suffix = query.toString();
563
- const url = `${API_BASE}/api/chat/runs/${encodeURIComponent(params.runId)}/stream${suffix ? `?${suffix}` : ''}`;
564
- const response = await fetch(url, {
494
+ const path = `/api/chat/runs/${encodeURIComponent(data.runId)}/stream${suffix ? `?${suffix}` : ''}`;
495
+ return readSseStream({
496
+ path,
565
497
  method: 'GET',
566
- headers: {
567
- Accept: 'text/event-stream'
568
- },
569
- signal: options.signal
498
+ signal: params.signal,
499
+ onReady: params.onReady,
500
+ onDelta: params.onDelta,
501
+ onSessionEvent: params.onSessionEvent
570
502
  });
571
- 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;
572
571
  }
573
572
 
574
573
  // GET /api/cron
package/src/api/types.ts CHANGED
@@ -146,6 +146,8 @@ export type SessionEntryView = {
146
146
  updatedAt: string;
147
147
  label?: string;
148
148
  preferredModel?: string;
149
+ sessionType: string;
150
+ sessionTypeMutable: boolean;
149
151
  messageCount: number;
150
152
  lastRole?: string;
151
153
  lastTimestamp?: string;
@@ -177,6 +179,8 @@ export type SessionHistoryView = {
177
179
  key: string;
178
180
  totalMessages: number;
179
181
  totalEvents: number;
182
+ sessionType: string;
183
+ sessionTypeMutable: boolean;
180
184
  metadata: Record<string, unknown>;
181
185
  messages: SessionMessageView[];
182
186
  events: SessionEventView[];
@@ -185,6 +189,7 @@ export type SessionHistoryView = {
185
189
  export type SessionPatchUpdate = {
186
190
  label?: string | null;
187
191
  preferredModel?: string | null;
192
+ sessionType?: string | null;
188
193
  clearHistory?: boolean;
189
194
  };
190
195
 
@@ -208,11 +213,42 @@ export type ChatTurnView = {
208
213
  durationMs: number;
209
214
  };
210
215
 
216
+ export type ChatTurnStreamReadyEvent = {
217
+ sessionKey: string;
218
+ requestedAt?: string;
219
+ runId?: string;
220
+ stopSupported?: boolean;
221
+ stopReason?: string;
222
+ };
223
+
224
+ export type ChatTurnStreamDeltaEvent = {
225
+ delta: string;
226
+ };
227
+
228
+ export type ChatTurnStreamSessionEvent = {
229
+ data: SessionEventView;
230
+ };
231
+
232
+ export type ChatTurnStreamErrorEvent = {
233
+ code?: string;
234
+ message?: string;
235
+ };
236
+
211
237
  export type ChatCapabilitiesView = {
212
238
  stopSupported: boolean;
213
239
  stopReason?: string;
214
240
  };
215
241
 
242
+ export type ChatSessionTypeOptionView = {
243
+ value: string;
244
+ label: string;
245
+ };
246
+
247
+ export type ChatSessionTypesView = {
248
+ defaultType: string;
249
+ options: ChatSessionTypeOptionView[];
250
+ };
251
+
216
252
  export type ChatCommandOptionView = {
217
253
  name: string;
218
254
  description: string;
@@ -267,30 +303,6 @@ export type ChatRunListView = {
267
303
  total: number;
268
304
  };
269
305
 
270
- export type ChatTurnStreamReadyEvent = {
271
- event: "ready";
272
- sessionKey: string;
273
- requestedAt: string;
274
- runId?: string;
275
- stopSupported?: boolean;
276
- stopReason?: string;
277
- };
278
-
279
- export type ChatTurnStreamDeltaEvent = {
280
- event: "delta";
281
- delta: string;
282
- };
283
-
284
- export type ChatTurnStreamSessionEvent = {
285
- event: "session_event";
286
- data: SessionEventView;
287
- };
288
-
289
- export type ChatTurnStreamFinalEvent = {
290
- event: "final";
291
- data: ChatTurnView;
292
- };
293
-
294
306
  export type CronScheduleView =
295
307
  | { kind: "at"; atMs?: number | null }
296
308
  | { kind: "every"; everyMs?: number | null }