@extrachill/chat 0.2.0

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/CHANGELOG.md +24 -0
  2. package/README.md +154 -0
  3. package/css/chat.css +552 -0
  4. package/dist/Chat.d.ts +73 -0
  5. package/dist/Chat.d.ts.map +1 -0
  6. package/dist/Chat.js +50 -0
  7. package/dist/api.d.ts +68 -0
  8. package/dist/api.d.ts.map +1 -0
  9. package/dist/api.js +93 -0
  10. package/dist/components/AvailabilityGate.d.ts +19 -0
  11. package/dist/components/AvailabilityGate.d.ts.map +1 -0
  12. package/dist/components/AvailabilityGate.js +32 -0
  13. package/dist/components/ChatInput.d.ts +21 -0
  14. package/dist/components/ChatInput.d.ts.map +1 -0
  15. package/dist/components/ChatInput.js +52 -0
  16. package/dist/components/ChatMessage.d.ts +23 -0
  17. package/dist/components/ChatMessage.d.ts.map +1 -0
  18. package/dist/components/ChatMessage.js +34 -0
  19. package/dist/components/ChatMessages.d.ts +28 -0
  20. package/dist/components/ChatMessages.d.ts.map +1 -0
  21. package/dist/components/ChatMessages.js +121 -0
  22. package/dist/components/ErrorBoundary.d.ts +27 -0
  23. package/dist/components/ErrorBoundary.d.ts.map +1 -0
  24. package/dist/components/ErrorBoundary.js +34 -0
  25. package/dist/components/SessionSwitcher.d.ts +25 -0
  26. package/dist/components/SessionSwitcher.d.ts.map +1 -0
  27. package/dist/components/SessionSwitcher.js +44 -0
  28. package/dist/components/ToolMessage.d.ts +34 -0
  29. package/dist/components/ToolMessage.d.ts.map +1 -0
  30. package/dist/components/ToolMessage.js +39 -0
  31. package/dist/components/TypingIndicator.d.ts +16 -0
  32. package/dist/components/TypingIndicator.d.ts.map +1 -0
  33. package/dist/components/TypingIndicator.js +14 -0
  34. package/dist/components/index.d.ts +9 -0
  35. package/dist/components/index.d.ts.map +1 -0
  36. package/dist/components/index.js +8 -0
  37. package/dist/hooks/index.d.ts +2 -0
  38. package/dist/hooks/index.d.ts.map +1 -0
  39. package/dist/hooks/index.js +1 -0
  40. package/dist/hooks/useChat.d.ts +102 -0
  41. package/dist/hooks/useChat.d.ts.map +1 -0
  42. package/dist/hooks/useChat.js +192 -0
  43. package/dist/index.d.ts +15 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +16 -0
  46. package/dist/normalizer.d.ts +24 -0
  47. package/dist/normalizer.d.ts.map +1 -0
  48. package/dist/normalizer.js +96 -0
  49. package/dist/types/adapter.d.ts +151 -0
  50. package/dist/types/adapter.d.ts.map +1 -0
  51. package/dist/types/adapter.js +11 -0
  52. package/dist/types/api.d.ts +137 -0
  53. package/dist/types/api.d.ts.map +1 -0
  54. package/dist/types/api.js +8 -0
  55. package/dist/types/index.d.ts +4 -0
  56. package/dist/types/index.d.ts.map +1 -0
  57. package/dist/types/index.js +1 -0
  58. package/dist/types/message.d.ts +62 -0
  59. package/dist/types/message.d.ts.map +1 -0
  60. package/dist/types/message.js +7 -0
  61. package/dist/types/session.d.ts +59 -0
  62. package/dist/types/session.d.ts.map +1 -0
  63. package/dist/types/session.js +7 -0
  64. package/package.json +61 -0
  65. package/src/Chat.tsx +157 -0
  66. package/src/api.ts +173 -0
  67. package/src/components/AvailabilityGate.tsx +85 -0
  68. package/src/components/ChatInput.tsx +114 -0
  69. package/src/components/ChatMessage.tsx +85 -0
  70. package/src/components/ChatMessages.tsx +193 -0
  71. package/src/components/ErrorBoundary.tsx +66 -0
  72. package/src/components/SessionSwitcher.tsx +129 -0
  73. package/src/components/ToolMessage.tsx +112 -0
  74. package/src/components/TypingIndicator.tsx +36 -0
  75. package/src/components/index.ts +8 -0
  76. package/src/hooks/index.ts +1 -0
  77. package/src/hooks/useChat.ts +310 -0
  78. package/src/index.ts +79 -0
  79. package/src/normalizer.ts +112 -0
  80. package/src/types/api.ts +146 -0
  81. package/src/types/index.ts +26 -0
  82. package/src/types/message.ts +66 -0
  83. package/src/types/session.ts +50 -0
@@ -0,0 +1,310 @@
1
+ import { useState, useCallback, useRef, useEffect } from 'react';
2
+ import type { ChatMessage } from '../types/message.ts';
3
+ import type { ChatSession } from '../types/session.ts';
4
+ import type { ChatAvailability } from '../types/session.ts';
5
+ import type { FetchFn, ChatApiConfig } from '../api.ts';
6
+ import {
7
+ sendMessage as apiSendMessage,
8
+ continueResponse as apiContinueResponse,
9
+ listSessions as apiListSessions,
10
+ loadSession as apiLoadSession,
11
+ deleteSession as apiDeleteSession,
12
+ } from '../api.ts';
13
+
14
+ /**
15
+ * Configuration for the useChat hook.
16
+ */
17
+ export interface UseChatOptions {
18
+ /**
19
+ * Base path for the chat REST endpoints.
20
+ * e.g. '/datamachine/v1/chat'
21
+ */
22
+ basePath: string;
23
+ /**
24
+ * Fetch function for API calls. Must accept { path, method?, data? }
25
+ * and return parsed JSON. @wordpress/api-fetch works directly.
26
+ */
27
+ fetchFn: FetchFn;
28
+ /**
29
+ * Agent ID to scope the chat to.
30
+ */
31
+ agentId?: number;
32
+ /**
33
+ * Initial messages to hydrate state with (e.g. server-rendered).
34
+ */
35
+ initialMessages?: ChatMessage[];
36
+ /**
37
+ * Initial session ID (e.g. from server-rendered state).
38
+ */
39
+ initialSessionId?: string;
40
+ /**
41
+ * Maximum number of continuation turns before stopping.
42
+ * Defaults to 20.
43
+ */
44
+ maxContinueTurns?: number;
45
+ /**
46
+ * Called when a new message is added to the conversation.
47
+ */
48
+ onMessage?: (message: ChatMessage) => void;
49
+ /**
50
+ * Called when an error occurs.
51
+ */
52
+ onError?: (error: Error) => void;
53
+ }
54
+
55
+ /**
56
+ * Return value of the useChat hook.
57
+ */
58
+ export interface UseChatReturn {
59
+ /** All messages in the current conversation. */
60
+ messages: ChatMessage[];
61
+ /** Whether a message is being sent/processed. */
62
+ isLoading: boolean;
63
+ /** Current continuation turn count (0 when not processing). */
64
+ turnCount: number;
65
+ /** Current availability state. */
66
+ availability: ChatAvailability;
67
+ /** Active session ID. */
68
+ sessionId: string | null;
69
+ /** List of sessions. */
70
+ sessions: ChatSession[];
71
+ /** Whether sessions are loading. */
72
+ sessionsLoading: boolean;
73
+ /** Send a user message. */
74
+ sendMessage: (content: string) => void;
75
+ /** Switch to a different session. */
76
+ switchSession: (sessionId: string) => void;
77
+ /** Create a new session. */
78
+ newSession: () => void;
79
+ /** Delete a session. */
80
+ deleteSession: (sessionId: string) => void;
81
+ /** Clear the current session's messages locally. */
82
+ clearSession: () => void;
83
+ /** Refresh the session list. */
84
+ refreshSessions: () => void;
85
+ }
86
+
87
+ let messageIdCounter = 0;
88
+ function generateMessageId(): string {
89
+ return `msg_${Date.now()}_${++messageIdCounter}`;
90
+ }
91
+
92
+ /**
93
+ * Core state orchestrator for the chat UI.
94
+ *
95
+ * Manages messages, sessions, continuation loops, and availability
96
+ * by calling the standard chat REST endpoints directly.
97
+ *
98
+ * @example
99
+ * ```tsx
100
+ * import apiFetch from '@wordpress/api-fetch';
101
+ *
102
+ * const chat = useChat({
103
+ * basePath: '/datamachine/v1/chat',
104
+ * fetchFn: apiFetch,
105
+ * agentId: 5,
106
+ * });
107
+ *
108
+ * return (
109
+ * <>
110
+ * <ChatMessages messages={chat.messages} />
111
+ * <TypingIndicator visible={chat.isLoading} />
112
+ * <ChatInput onSend={chat.sendMessage} disabled={chat.isLoading} />
113
+ * </>
114
+ * );
115
+ * ```
116
+ */
117
+ export function useChat({
118
+ basePath,
119
+ fetchFn,
120
+ agentId,
121
+ initialMessages,
122
+ initialSessionId,
123
+ maxContinueTurns = 20,
124
+ onMessage,
125
+ onError,
126
+ }: UseChatOptions): UseChatReturn {
127
+ const [messages, setMessages] = useState<ChatMessage[]>(initialMessages ?? []);
128
+ const [isLoading, setIsLoading] = useState(false);
129
+ const [turnCount, setTurnCount] = useState(0);
130
+ const [availability, setAvailability] = useState<ChatAvailability>({ status: 'ready' });
131
+ const [sessionId, setSessionId] = useState<string | null>(initialSessionId ?? null);
132
+ const [sessions, setSessions] = useState<ChatSession[]>([]);
133
+ const [sessionsLoading, setSessionsLoading] = useState(false);
134
+
135
+ // Build API config from props
136
+ const configRef = useRef<ChatApiConfig>({ basePath, fetchFn, agentId });
137
+ configRef.current = { basePath, fetchFn, agentId };
138
+
139
+ const sessionIdRef = useRef(sessionId);
140
+ sessionIdRef.current = sessionId;
141
+
142
+ // Load sessions on mount
143
+ useEffect(() => {
144
+ const loadSessions = async () => {
145
+ setSessionsLoading(true);
146
+ try {
147
+ const list = await apiListSessions(configRef.current);
148
+ setSessions(list);
149
+ } catch (err) {
150
+ // Sessions not available — degrade gracefully
151
+ onError?.(err instanceof Error ? err : new Error(String(err)));
152
+ } finally {
153
+ setSessionsLoading(false);
154
+ }
155
+ };
156
+
157
+ loadSessions();
158
+ // eslint-disable-next-line react-hooks/exhaustive-deps
159
+ }, []);
160
+
161
+ const sendMessage = useCallback(async (content: string) => {
162
+ if (isLoading) return;
163
+
164
+ // Optimistically add user message
165
+ const userMessage: ChatMessage = {
166
+ id: generateMessageId(),
167
+ role: 'user',
168
+ content,
169
+ timestamp: new Date().toISOString(),
170
+ };
171
+
172
+ setMessages((prev) => [...prev, userMessage]);
173
+ onMessage?.(userMessage);
174
+ setIsLoading(true);
175
+ setTurnCount(0);
176
+
177
+ try {
178
+ const result = await apiSendMessage(
179
+ configRef.current,
180
+ content,
181
+ sessionIdRef.current ?? undefined,
182
+ );
183
+
184
+ // Update session ID (may be newly created)
185
+ setSessionId(result.sessionId);
186
+ sessionIdRef.current = result.sessionId;
187
+
188
+ // Replace all messages with the full normalized conversation
189
+ setMessages(result.messages);
190
+
191
+ // Handle multi-turn continuation
192
+ if (!result.completed && !result.maxTurnsReached) {
193
+ let completed = false;
194
+ let turns = 0;
195
+
196
+ while (!completed && turns < maxContinueTurns) {
197
+ turns++;
198
+ setTurnCount(turns);
199
+
200
+ const continuation = await apiContinueResponse(
201
+ configRef.current,
202
+ result.sessionId,
203
+ );
204
+
205
+ setMessages((prev) => [...prev, ...continuation.messages]);
206
+ for (const msg of continuation.messages) {
207
+ onMessage?.(msg);
208
+ }
209
+
210
+ completed = continuation.completed || continuation.maxTurnsReached;
211
+ }
212
+ }
213
+
214
+ // Refresh sessions list after a message
215
+ apiListSessions(configRef.current)
216
+ .then(setSessions)
217
+ .catch(() => { /* ignore */ });
218
+
219
+ } catch (err) {
220
+ const error = err instanceof Error ? err : new Error(String(err));
221
+ onError?.(error);
222
+
223
+ // Check if it's an auth error
224
+ if (error.message.includes('403') || error.message.includes('rest_forbidden')) {
225
+ setAvailability({ status: 'login-required' });
226
+ }
227
+
228
+ // Add error as assistant message so it's visible in the UI
229
+ const errorMessage: ChatMessage = {
230
+ id: generateMessageId(),
231
+ role: 'assistant',
232
+ content: `Sorry, something went wrong: ${error.message}`,
233
+ timestamp: new Date().toISOString(),
234
+ };
235
+ setMessages((prev) => [...prev, errorMessage]);
236
+ } finally {
237
+ setIsLoading(false);
238
+ setTurnCount(0);
239
+ }
240
+ }, [isLoading, maxContinueTurns, onMessage, onError]);
241
+
242
+ const switchSession = useCallback(async (newSessionId: string) => {
243
+ setSessionId(newSessionId);
244
+ sessionIdRef.current = newSessionId;
245
+ setIsLoading(true);
246
+
247
+ try {
248
+ const loaded = await apiLoadSession(configRef.current, newSessionId);
249
+ setMessages(loaded);
250
+ } catch (err) {
251
+ onError?.(err instanceof Error ? err : new Error(String(err)));
252
+ setMessages([]);
253
+ } finally {
254
+ setIsLoading(false);
255
+ }
256
+ }, [onError]);
257
+
258
+ const newSession = useCallback(() => {
259
+ setSessionId(null);
260
+ sessionIdRef.current = null;
261
+ setMessages([]);
262
+ }, []);
263
+
264
+ const deleteSessionHandler = useCallback(async (targetSessionId: string) => {
265
+ try {
266
+ await apiDeleteSession(configRef.current, targetSessionId);
267
+ setSessions((prev) => prev.filter((s) => s.id !== targetSessionId));
268
+
269
+ if (sessionIdRef.current === targetSessionId) {
270
+ setSessionId(null);
271
+ sessionIdRef.current = null;
272
+ setMessages([]);
273
+ }
274
+ } catch (err) {
275
+ onError?.(err instanceof Error ? err : new Error(String(err)));
276
+ }
277
+ }, [onError]);
278
+
279
+ const clearSession = useCallback(() => {
280
+ setMessages([]);
281
+ }, []);
282
+
283
+ const refreshSessions = useCallback(async () => {
284
+ setSessionsLoading(true);
285
+ try {
286
+ const list = await apiListSessions(configRef.current);
287
+ setSessions(list);
288
+ } catch (err) {
289
+ onError?.(err instanceof Error ? err : new Error(String(err)));
290
+ } finally {
291
+ setSessionsLoading(false);
292
+ }
293
+ }, [onError]);
294
+
295
+ return {
296
+ messages,
297
+ isLoading,
298
+ turnCount,
299
+ availability,
300
+ sessionId,
301
+ sessions,
302
+ sessionsLoading,
303
+ sendMessage,
304
+ switchSession,
305
+ newSession,
306
+ deleteSession: deleteSessionHandler,
307
+ clearSession,
308
+ refreshSessions,
309
+ };
310
+ }
package/src/index.ts ADDED
@@ -0,0 +1,79 @@
1
+ // Types
2
+ export type {
3
+ MessageRole,
4
+ ToolCall,
5
+ ToolResultMeta,
6
+ ChatMessage,
7
+ ContentFormat,
8
+ ChatSession,
9
+ ChatAvailability,
10
+ ChatInitialState,
11
+ RawMessage,
12
+ RawSession,
13
+ SessionMetadata,
14
+ } from './types/index.ts';
15
+
16
+ // API
17
+ export type { FetchFn, FetchOptions, ChatApiConfig, SendResult, ContinueResult } from './api.ts';
18
+ export {
19
+ sendMessage,
20
+ continueResponse,
21
+ listSessions,
22
+ loadSession,
23
+ deleteSession,
24
+ } from './api.ts';
25
+
26
+ // Normalizer
27
+ export { normalizeMessage, normalizeConversation, normalizeSession } from './normalizer.ts';
28
+
29
+ // Components
30
+ export {
31
+ ChatMessage as ChatMessageComponent,
32
+ type ChatMessageProps,
33
+ } from './components/ChatMessage.tsx';
34
+
35
+ export {
36
+ ChatMessages,
37
+ type ChatMessagesProps,
38
+ } from './components/ChatMessages.tsx';
39
+
40
+ export {
41
+ ChatInput,
42
+ type ChatInputProps,
43
+ } from './components/ChatInput.tsx';
44
+
45
+ export {
46
+ ToolMessage,
47
+ type ToolMessageProps,
48
+ type ToolGroup,
49
+ } from './components/ToolMessage.tsx';
50
+
51
+ export {
52
+ TypingIndicator,
53
+ type TypingIndicatorProps,
54
+ } from './components/TypingIndicator.tsx';
55
+
56
+ export {
57
+ SessionSwitcher,
58
+ type SessionSwitcherProps,
59
+ } from './components/SessionSwitcher.tsx';
60
+
61
+ export {
62
+ ErrorBoundary,
63
+ type ErrorBoundaryProps,
64
+ } from './components/ErrorBoundary.tsx';
65
+
66
+ export {
67
+ AvailabilityGate,
68
+ type AvailabilityGateProps,
69
+ } from './components/AvailabilityGate.tsx';
70
+
71
+ // Hook
72
+ export {
73
+ useChat,
74
+ type UseChatOptions,
75
+ type UseChatReturn,
76
+ } from './hooks/useChat.ts';
77
+
78
+ // Composed
79
+ export { Chat, type ChatProps } from './Chat.tsx';
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Normalize raw backend messages into the ChatMessage format
3
+ * used by the UI components.
4
+ *
5
+ * The backend stores tool calls and results as regular messages
6
+ * with metadata.type = 'tool_call' | 'tool_result'. This normalizer
7
+ * maps them into the package's role-based message model.
8
+ */
9
+
10
+ import type { ChatMessage, ToolCall } from './types/message.ts';
11
+ import type { ChatSession } from './types/session.ts';
12
+ import type { RawMessage, RawSession } from './types/api.ts';
13
+
14
+ let idCounter = 0;
15
+ function generateId(): string {
16
+ return `msg_${Date.now()}_${++idCounter}`;
17
+ }
18
+
19
+ /**
20
+ * Normalize a raw backend message into a ChatMessage.
21
+ */
22
+ export function normalizeMessage(raw: RawMessage, index: number): ChatMessage {
23
+ const type = raw.metadata?.type ?? 'text';
24
+ const timestamp = raw.metadata?.timestamp ?? new Date().toISOString();
25
+
26
+ if (type === 'tool_call') {
27
+ const toolCalls: ToolCall[] = [];
28
+
29
+ // Extract from metadata (single tool call)
30
+ if (raw.metadata?.tool_name) {
31
+ toolCalls.push({
32
+ id: `tc_${index}_${raw.metadata.tool_name}`,
33
+ name: raw.metadata.tool_name,
34
+ parameters: raw.metadata.parameters ?? {},
35
+ });
36
+ }
37
+
38
+ // Also check top-level tool_calls array
39
+ if (raw.tool_calls?.length) {
40
+ for (const tc of raw.tool_calls) {
41
+ // Avoid duplicates
42
+ if (!toolCalls.some((t) => t.name === tc.tool_name)) {
43
+ toolCalls.push({
44
+ id: `tc_${index}_${tc.tool_name}`,
45
+ name: tc.tool_name,
46
+ parameters: tc.parameters ?? {},
47
+ });
48
+ }
49
+ }
50
+ }
51
+
52
+ return {
53
+ id: generateId(),
54
+ role: 'tool_call',
55
+ content: raw.content,
56
+ timestamp,
57
+ toolCalls,
58
+ };
59
+ }
60
+
61
+ if (type === 'tool_result') {
62
+ return {
63
+ id: generateId(),
64
+ role: 'tool_result',
65
+ content: raw.content,
66
+ timestamp,
67
+ toolResult: {
68
+ toolName: raw.metadata?.tool_name ?? 'unknown',
69
+ success: raw.metadata?.success ?? false,
70
+ },
71
+ };
72
+ }
73
+
74
+ // Regular text message (user or assistant)
75
+ const message: ChatMessage = {
76
+ id: generateId(),
77
+ role: raw.role === 'user' ? 'user' : 'assistant',
78
+ content: raw.content,
79
+ timestamp,
80
+ };
81
+
82
+ // Assistant messages may carry tool_calls at the top level
83
+ if (raw.role === 'assistant' && raw.tool_calls?.length) {
84
+ message.toolCalls = raw.tool_calls.map((tc, i) => ({
85
+ id: `tc_${index}_${i}_${tc.tool_name}`,
86
+ name: tc.tool_name,
87
+ parameters: tc.parameters ?? {},
88
+ }));
89
+ }
90
+
91
+ return message;
92
+ }
93
+
94
+ /**
95
+ * Normalize a full conversation array.
96
+ */
97
+ export function normalizeConversation(raw: RawMessage[]): ChatMessage[] {
98
+ return raw.map(normalizeMessage);
99
+ }
100
+
101
+ /**
102
+ * Normalize a raw session into a ChatSession.
103
+ */
104
+ export function normalizeSession(raw: RawSession): ChatSession {
105
+ return {
106
+ id: raw.session_id,
107
+ title: raw.title ?? raw.first_message ?? undefined,
108
+ createdAt: raw.created_at,
109
+ updatedAt: raw.updated_at,
110
+ messageCount: raw.message_count,
111
+ };
112
+ }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * REST API contract types.
3
+ *
4
+ * These describe the expected shape of the chat REST endpoints.
5
+ * Any backend that wants to work with this package implements
6
+ * these same endpoints and response shapes.
7
+ */
8
+
9
+ /**
10
+ * A raw message as stored/returned by the backend.
11
+ * The package normalizes these into ChatMessage before rendering.
12
+ */
13
+ export interface RawMessage {
14
+ role: 'user' | 'assistant';
15
+ content: string;
16
+ metadata?: {
17
+ timestamp?: string;
18
+ type?: 'text' | 'tool_call' | 'tool_result';
19
+ tool_name?: string;
20
+ parameters?: Record<string, unknown>;
21
+ success?: boolean;
22
+ error?: string;
23
+ turn?: number;
24
+ tool_data?: Record<string, unknown>;
25
+ };
26
+ tool_calls?: Array<{
27
+ tool_name: string;
28
+ parameters: Record<string, unknown>;
29
+ }>;
30
+ }
31
+
32
+ /**
33
+ * POST /chat — Send a message.
34
+ */
35
+ export interface SendRequest {
36
+ message: string;
37
+ session_id?: string;
38
+ agent_id?: number;
39
+ }
40
+
41
+ export interface SendResponse {
42
+ success: boolean;
43
+ data: {
44
+ session_id: string;
45
+ response: string;
46
+ tool_calls: Array<{
47
+ tool_name: string;
48
+ parameters: Record<string, unknown>;
49
+ }>;
50
+ conversation: RawMessage[];
51
+ metadata: SessionMetadata;
52
+ completed: boolean;
53
+ max_turns: number;
54
+ turn_number: number;
55
+ max_turns_reached: boolean;
56
+ warning?: string;
57
+ };
58
+ }
59
+
60
+ /**
61
+ * POST /chat/continue — Continue a multi-turn response.
62
+ */
63
+ export interface ContinueRequest {
64
+ session_id: string;
65
+ }
66
+
67
+ export interface ContinueResponse {
68
+ success: boolean;
69
+ data: {
70
+ session_id: string;
71
+ new_messages: RawMessage[];
72
+ final_content: string;
73
+ tool_calls: Array<{
74
+ tool_name: string;
75
+ parameters: Record<string, unknown>;
76
+ }>;
77
+ completed: boolean;
78
+ turn_number: number;
79
+ max_turns: number;
80
+ max_turns_reached: boolean;
81
+ };
82
+ }
83
+
84
+ /**
85
+ * GET /chat/sessions — List sessions.
86
+ */
87
+ export interface ListSessionsResponse {
88
+ success: boolean;
89
+ data: {
90
+ sessions: RawSession[];
91
+ total: number;
92
+ limit: number;
93
+ offset: number;
94
+ };
95
+ }
96
+
97
+ export interface RawSession {
98
+ session_id: string;
99
+ title: string | null;
100
+ context: string;
101
+ first_message: string | null;
102
+ message_count: number;
103
+ created_at: string;
104
+ updated_at: string;
105
+ }
106
+
107
+ /**
108
+ * GET /chat/{session_id} — Get a single session.
109
+ */
110
+ export interface GetSessionResponse {
111
+ success: boolean;
112
+ data: {
113
+ session_id: string;
114
+ conversation: RawMessage[];
115
+ metadata: SessionMetadata;
116
+ };
117
+ }
118
+
119
+ /**
120
+ * DELETE /chat/{session_id} — Delete a session.
121
+ */
122
+ export interface DeleteSessionResponse {
123
+ success: boolean;
124
+ data: {
125
+ session_id: string;
126
+ deleted: boolean;
127
+ };
128
+ }
129
+
130
+ /**
131
+ * Session metadata shape from the backend.
132
+ */
133
+ export interface SessionMetadata {
134
+ status?: 'pending' | 'processing' | 'completed' | 'error';
135
+ started_at?: string;
136
+ last_activity?: string;
137
+ message_count?: number;
138
+ current_turn?: number;
139
+ has_pending_tools?: boolean;
140
+ token_usage?: {
141
+ prompt_tokens: number;
142
+ completion_tokens: number;
143
+ total_tokens: number;
144
+ };
145
+ error_message?: string;
146
+ }
@@ -0,0 +1,26 @@
1
+ export type {
2
+ MessageRole,
3
+ ToolCall,
4
+ ToolResultMeta,
5
+ ChatMessage,
6
+ ContentFormat,
7
+ } from './message.ts';
8
+
9
+ export type {
10
+ ChatSession,
11
+ ChatAvailability,
12
+ ChatInitialState,
13
+ } from './session.ts';
14
+
15
+ export type {
16
+ RawMessage,
17
+ RawSession,
18
+ SendRequest,
19
+ SendResponse,
20
+ ContinueRequest,
21
+ ContinueResponse,
22
+ ListSessionsResponse,
23
+ GetSessionResponse,
24
+ DeleteSessionResponse,
25
+ SessionMetadata,
26
+ } from './api.ts';