@extrachill/chat 0.3.1 → 0.5.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.
- package/CHANGELOG.md +36 -0
- package/css/chat.css +189 -2
- package/dist/Chat.d.ts +10 -1
- package/dist/Chat.d.ts.map +1 -1
- package/dist/Chat.js +3 -2
- package/dist/api.d.ts +28 -2
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +25 -2
- package/dist/components/ChatInput.d.ts +11 -4
- package/dist/components/ChatInput.d.ts.map +1 -1
- package/dist/components/ChatInput.js +68 -10
- package/dist/components/ChatMessage.d.ts +1 -0
- package/dist/components/ChatMessage.d.ts.map +1 -1
- package/dist/components/ChatMessage.js +23 -3
- package/dist/hooks/useChat.d.ts +28 -4
- package/dist/hooks/useChat.d.ts.map +1 -1
- package/dist/hooks/useChat.js +70 -8
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/normalizer.d.ts.map +1 -1
- package/dist/normalizer.js +84 -1
- package/dist/types/api.d.ts +25 -1
- package/dist/types/api.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/message.d.ts +23 -0
- package/dist/types/message.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Chat.tsx +15 -0
- package/src/api.ts +39 -0
- package/src/components/ChatInput.tsx +173 -28
- package/src/components/ChatMessage.tsx +102 -5
- package/src/hooks/useChat.ts +120 -10
- package/src/index.ts +3 -1
- package/src/normalizer.ts +88 -3
- package/src/types/api.ts +26 -1
- package/src/types/index.ts +2 -0
- package/src/types/message.ts +24 -0
package/dist/hooks/useChat.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChatMessage } from '../types/message.ts';
|
|
1
|
+
import type { ChatMessage, ToolCall } from '../types/message.ts';
|
|
2
2
|
import type { ChatSession } from '../types/session.ts';
|
|
3
3
|
import type { ChatAvailability } from '../types/session.ts';
|
|
4
4
|
import type { FetchFn } from '../api.ts';
|
|
@@ -41,6 +41,23 @@ export interface UseChatOptions {
|
|
|
41
41
|
* Called when an error occurs.
|
|
42
42
|
*/
|
|
43
43
|
onError?: (error: Error) => void;
|
|
44
|
+
/**
|
|
45
|
+
* Called after each turn when tool calls are present in the response.
|
|
46
|
+
* Use this to react to tool executions (e.g. invalidate caches,
|
|
47
|
+
* apply diffs to the editor, update external state).
|
|
48
|
+
*/
|
|
49
|
+
onToolCalls?: (toolCalls: ToolCall[]) => void;
|
|
50
|
+
/**
|
|
51
|
+
* Arbitrary metadata forwarded to the backend with each message.
|
|
52
|
+
* Use for context scoping (e.g. `{ selected_pipeline_id: 42 }`,
|
|
53
|
+
* `{ post_id: 100, context: 'editor' }`).
|
|
54
|
+
*/
|
|
55
|
+
metadata?: Record<string, unknown>;
|
|
56
|
+
/**
|
|
57
|
+
* Optional context filter for session listing.
|
|
58
|
+
* Only sessions created in the matching context are shown.
|
|
59
|
+
*/
|
|
60
|
+
sessionContext?: string;
|
|
44
61
|
}
|
|
45
62
|
/**
|
|
46
63
|
* Return value of the useChat hook.
|
|
@@ -56,12 +73,19 @@ export interface UseChatReturn {
|
|
|
56
73
|
availability: ChatAvailability;
|
|
57
74
|
/** Active session ID. */
|
|
58
75
|
sessionId: string | null;
|
|
76
|
+
/**
|
|
77
|
+
* The session ID that initiated the current request.
|
|
78
|
+
* Use to avoid stale loading indicators when the user switches
|
|
79
|
+
* sessions while a request is in flight.
|
|
80
|
+
* Null when idle.
|
|
81
|
+
*/
|
|
82
|
+
processingSessionId: string | null;
|
|
59
83
|
/** List of sessions. */
|
|
60
84
|
sessions: ChatSession[];
|
|
61
85
|
/** Whether sessions are loading. */
|
|
62
86
|
sessionsLoading: boolean;
|
|
63
|
-
/** Send a user message. */
|
|
64
|
-
sendMessage: (content: string) => void;
|
|
87
|
+
/** Send a user message (with optional file attachments). */
|
|
88
|
+
sendMessage: (content: string, files?: File[]) => void;
|
|
65
89
|
/** Switch to a different session. */
|
|
66
90
|
switchSession: (sessionId: string) => void;
|
|
67
91
|
/** Create a new session. */
|
|
@@ -98,5 +122,5 @@ export interface UseChatReturn {
|
|
|
98
122
|
* );
|
|
99
123
|
* ```
|
|
100
124
|
*/
|
|
101
|
-
export declare function useChat({ basePath, fetchFn, agentId, initialMessages, initialSessionId, maxContinueTurns, onMessage, onError, }: UseChatOptions): UseChatReturn;
|
|
125
|
+
export declare function useChat({ basePath, fetchFn, agentId, initialMessages, initialSessionId, maxContinueTurns, onMessage, onError, onToolCalls, metadata, sessionContext, }: UseChatOptions): UseChatReturn;
|
|
102
126
|
//# sourceMappingURL=useChat.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/hooks/useChat.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/hooks/useChat.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAAE,OAAO,EAAiC,MAAM,WAAW,CAAC;AASxE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,eAAe,CAAC,EAAE,WAAW,EAAE,CAAC;IAChC;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAC3C;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,gDAAgD;IAChD,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,iDAAiD;IACjD,SAAS,EAAE,OAAO,CAAC;IACnB,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,YAAY,EAAE,gBAAgB,CAAC;IAC/B,yBAAyB;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;;OAKG;IACH,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,wBAAwB;IACxB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,oCAAoC;IACpC,eAAe,EAAE,OAAO,CAAC;IACzB,4DAA4D;IAC5D,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IACvD,qCAAqC;IACrC,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,4BAA4B;IAC5B,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,wBAAwB;IACxB,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,oDAAoD;IACpD,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,gCAAgC;IAChC,eAAe,EAAE,MAAM,IAAI,CAAC;CAC5B;AAyBD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,OAAO,CAAC,EACvB,QAAQ,EACR,OAAO,EACP,OAAO,EACP,eAAe,EACf,gBAAgB,EAChB,gBAAqB,EACrB,SAAS,EACT,OAAO,EACP,WAAW,EACX,QAAQ,EACR,cAAc,GACd,EAAE,cAAc,GAAG,aAAa,CA0QhC"}
|
package/dist/hooks/useChat.js
CHANGED
|
@@ -53,12 +53,13 @@ function toError(err) {
|
|
|
53
53
|
* );
|
|
54
54
|
* ```
|
|
55
55
|
*/
|
|
56
|
-
export function useChat({ basePath, fetchFn, agentId, initialMessages, initialSessionId, maxContinueTurns = 20, onMessage, onError, }) {
|
|
56
|
+
export function useChat({ basePath, fetchFn, agentId, initialMessages, initialSessionId, maxContinueTurns = 20, onMessage, onError, onToolCalls, metadata, sessionContext, }) {
|
|
57
57
|
const [messages, setMessages] = useState(initialMessages ?? []);
|
|
58
58
|
const [isLoading, setIsLoading] = useState(false);
|
|
59
59
|
const [turnCount, setTurnCount] = useState(0);
|
|
60
60
|
const [availability, setAvailability] = useState({ status: 'ready' });
|
|
61
61
|
const [sessionId, setSessionId] = useState(initialSessionId ?? null);
|
|
62
|
+
const [processingSessionId, setProcessingSessionId] = useState(null);
|
|
62
63
|
const [sessions, setSessions] = useState([]);
|
|
63
64
|
const [sessionsLoading, setSessionsLoading] = useState(false);
|
|
64
65
|
// Build API config from props
|
|
@@ -66,12 +67,21 @@ export function useChat({ basePath, fetchFn, agentId, initialMessages, initialSe
|
|
|
66
67
|
configRef.current = { basePath, fetchFn, agentId };
|
|
67
68
|
const sessionIdRef = useRef(sessionId);
|
|
68
69
|
sessionIdRef.current = sessionId;
|
|
70
|
+
// Refs for latest callback/metadata values (avoid stale closures).
|
|
71
|
+
const onToolCallsRef = useRef(onToolCalls);
|
|
72
|
+
onToolCallsRef.current = onToolCalls;
|
|
73
|
+
const metadataRef = useRef(metadata);
|
|
74
|
+
metadataRef.current = metadata;
|
|
75
|
+
const sessionContextRef = useRef(sessionContext);
|
|
76
|
+
sessionContextRef.current = sessionContext;
|
|
77
|
+
// Guard against concurrent session creation.
|
|
78
|
+
const isCreatingRef = useRef(false);
|
|
69
79
|
// Load sessions on mount
|
|
70
80
|
useEffect(() => {
|
|
71
81
|
const loadSessions = async () => {
|
|
72
82
|
setSessionsLoading(true);
|
|
73
83
|
try {
|
|
74
|
-
const list = await apiListSessions(configRef.current);
|
|
84
|
+
const list = await apiListSessions(configRef.current, 20, sessionContextRef.current);
|
|
75
85
|
setSessions(list);
|
|
76
86
|
}
|
|
77
87
|
catch (err) {
|
|
@@ -85,27 +95,74 @@ export function useChat({ basePath, fetchFn, agentId, initialMessages, initialSe
|
|
|
85
95
|
loadSessions();
|
|
86
96
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
87
97
|
}, []);
|
|
88
|
-
|
|
89
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Collect tool calls from a list of messages and fire the onToolCalls callback.
|
|
100
|
+
*/
|
|
101
|
+
const fireToolCalls = useCallback((msgs) => {
|
|
102
|
+
const cb = onToolCallsRef.current;
|
|
103
|
+
if (!cb)
|
|
90
104
|
return;
|
|
105
|
+
const allToolCalls = [];
|
|
106
|
+
for (const msg of msgs) {
|
|
107
|
+
if (msg.toolCalls?.length) {
|
|
108
|
+
allToolCalls.push(...msg.toolCalls);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (allToolCalls.length > 0) {
|
|
112
|
+
cb(allToolCalls);
|
|
113
|
+
}
|
|
114
|
+
}, []);
|
|
115
|
+
const sendMessage = useCallback(async (content, files) => {
|
|
116
|
+
if (isLoading || isCreatingRef.current)
|
|
117
|
+
return;
|
|
118
|
+
// Build optimistic attachment previews from local files.
|
|
119
|
+
let optimisticAttachments;
|
|
120
|
+
let sendAttachments;
|
|
121
|
+
if (files?.length) {
|
|
122
|
+
optimisticAttachments = files.map((file) => ({
|
|
123
|
+
type: file.type.startsWith('image/') ? 'image'
|
|
124
|
+
: file.type.startsWith('video/') ? 'video'
|
|
125
|
+
: 'file',
|
|
126
|
+
url: URL.createObjectURL(file),
|
|
127
|
+
filename: file.name,
|
|
128
|
+
mimeType: file.type,
|
|
129
|
+
size: file.size,
|
|
130
|
+
}));
|
|
131
|
+
sendAttachments = files.map((file) => ({
|
|
132
|
+
filename: file.name,
|
|
133
|
+
mime_type: file.type,
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
91
136
|
// Optimistically add user message
|
|
92
137
|
const userMessage = {
|
|
93
138
|
id: generateMessageId(),
|
|
94
139
|
role: 'user',
|
|
95
140
|
content,
|
|
96
141
|
timestamp: new Date().toISOString(),
|
|
142
|
+
attachments: optimisticAttachments,
|
|
97
143
|
};
|
|
144
|
+
// Guard against concurrent session creation.
|
|
145
|
+
if (!sessionIdRef.current) {
|
|
146
|
+
isCreatingRef.current = true;
|
|
147
|
+
}
|
|
98
148
|
setMessages((prev) => [...prev, userMessage]);
|
|
99
149
|
onMessage?.(userMessage);
|
|
100
150
|
setIsLoading(true);
|
|
101
151
|
setTurnCount(0);
|
|
152
|
+
// Track which session initiated the request.
|
|
153
|
+
const initiatingSessionId = sessionIdRef.current;
|
|
154
|
+
setProcessingSessionId(initiatingSessionId);
|
|
102
155
|
try {
|
|
103
|
-
const result = await apiSendMessage(configRef.current, content, sessionIdRef.current ?? undefined);
|
|
156
|
+
const result = await apiSendMessage(configRef.current, content, sessionIdRef.current ?? undefined, sendAttachments, metadataRef.current);
|
|
157
|
+
isCreatingRef.current = false;
|
|
104
158
|
// Update session ID (may be newly created)
|
|
105
159
|
setSessionId(result.sessionId);
|
|
106
160
|
sessionIdRef.current = result.sessionId;
|
|
161
|
+
setProcessingSessionId(result.sessionId);
|
|
107
162
|
// Replace all messages with the full normalized conversation
|
|
108
163
|
setMessages(result.messages);
|
|
164
|
+
// Fire tool call callback for the initial response.
|
|
165
|
+
fireToolCalls(result.messages);
|
|
109
166
|
// Handle multi-turn continuation
|
|
110
167
|
if (!result.completed && !result.maxTurnsReached) {
|
|
111
168
|
let completed = false;
|
|
@@ -118,15 +175,18 @@ export function useChat({ basePath, fetchFn, agentId, initialMessages, initialSe
|
|
|
118
175
|
for (const msg of continuation.messages) {
|
|
119
176
|
onMessage?.(msg);
|
|
120
177
|
}
|
|
178
|
+
// Fire tool call callback for each continuation turn.
|
|
179
|
+
fireToolCalls(continuation.messages);
|
|
121
180
|
completed = continuation.completed || continuation.maxTurnsReached;
|
|
122
181
|
}
|
|
123
182
|
}
|
|
124
183
|
// Refresh sessions list after a message
|
|
125
|
-
apiListSessions(configRef.current)
|
|
184
|
+
apiListSessions(configRef.current, 20, sessionContextRef.current)
|
|
126
185
|
.then(setSessions)
|
|
127
186
|
.catch(() => { });
|
|
128
187
|
}
|
|
129
188
|
catch (err) {
|
|
189
|
+
isCreatingRef.current = false;
|
|
130
190
|
const error = toError(err);
|
|
131
191
|
onError?.(error);
|
|
132
192
|
// Check if it's an auth error
|
|
@@ -145,8 +205,9 @@ export function useChat({ basePath, fetchFn, agentId, initialMessages, initialSe
|
|
|
145
205
|
finally {
|
|
146
206
|
setIsLoading(false);
|
|
147
207
|
setTurnCount(0);
|
|
208
|
+
setProcessingSessionId(null);
|
|
148
209
|
}
|
|
149
|
-
}, [isLoading, maxContinueTurns, onMessage, onError]);
|
|
210
|
+
}, [isLoading, maxContinueTurns, onMessage, onError, fireToolCalls]);
|
|
150
211
|
const switchSession = useCallback(async (newSessionId) => {
|
|
151
212
|
setSessionId(newSessionId);
|
|
152
213
|
sessionIdRef.current = newSessionId;
|
|
@@ -188,7 +249,7 @@ export function useChat({ basePath, fetchFn, agentId, initialMessages, initialSe
|
|
|
188
249
|
const refreshSessions = useCallback(async () => {
|
|
189
250
|
setSessionsLoading(true);
|
|
190
251
|
try {
|
|
191
|
-
const list = await apiListSessions(configRef.current);
|
|
252
|
+
const list = await apiListSessions(configRef.current, 20, sessionContextRef.current);
|
|
192
253
|
setSessions(list);
|
|
193
254
|
}
|
|
194
255
|
catch (err) {
|
|
@@ -204,6 +265,7 @@ export function useChat({ basePath, fetchFn, agentId, initialMessages, initialSe
|
|
|
204
265
|
turnCount,
|
|
205
266
|
availability,
|
|
206
267
|
sessionId,
|
|
268
|
+
processingSessionId,
|
|
207
269
|
sessions,
|
|
208
270
|
sessionsLoading,
|
|
209
271
|
sendMessage,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type { MessageRole, ToolCall, ToolResultMeta, ChatMessage, ContentFormat, ChatSession, ChatAvailability, ChatInitialState, RawMessage, RawSession, SessionMetadata, } from './types/index.ts';
|
|
2
|
-
export type { FetchFn, FetchOptions, ChatApiConfig, SendResult, ContinueResult } from './api.ts';
|
|
1
|
+
export type { MessageRole, ToolCall, ToolResultMeta, MediaAttachment, ChatMessage, ContentFormat, ChatSession, ChatAvailability, ChatInitialState, RawAttachment, RawMessage, RawSession, SessionMetadata, } from './types/index.ts';
|
|
2
|
+
export type { FetchFn, FetchOptions, ChatApiConfig, SendResult, ContinueResult, SendAttachment } from './api.ts';
|
|
3
3
|
export { sendMessage, continueResponse, listSessions, loadSession, deleteSession, } from './api.ts';
|
|
4
4
|
export { normalizeMessage, normalizeConversation, normalizeSession } from './normalizer.ts';
|
|
5
5
|
export { markdownToHtml } from './markdown.ts';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACX,WAAW,EACX,QAAQ,EACR,cAAc,EACd,WAAW,EACX,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,eAAe,GACf,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACX,WAAW,EACX,QAAQ,EACR,cAAc,EACd,eAAe,EACf,WAAW,EACX,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,UAAU,EACV,eAAe,GACf,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AACjH,OAAO,EACN,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,aAAa,GACb,MAAM,UAAU,CAAC;AAGlB,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAG5F,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EACN,WAAW,IAAI,oBAAoB,EACnC,KAAK,gBAAgB,GACrB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACN,YAAY,EACZ,KAAK,iBAAiB,GACtB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EACN,SAAS,EACT,KAAK,cAAc,GACnB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACN,WAAW,EACX,KAAK,gBAAgB,EACrB,KAAK,SAAS,GACd,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACN,eAAe,EACf,KAAK,oBAAoB,GACzB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EACN,eAAe,EACf,KAAK,oBAAoB,GACzB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EACN,aAAa,EACb,KAAK,kBAAkB,GACvB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACN,gBAAgB,EAChB,KAAK,qBAAqB,GAC1B,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EACN,OAAO,EACP,KAAK,cAAc,EACnB,KAAK,aAAa,GAClB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/normalizer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../src/normalizer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../src/normalizer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAA6B,MAAM,oBAAoB,CAAC;AACjF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAiB,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAO5E;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CA4E5E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,WAAW,EAAE,CAEtE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,GAAG,WAAW,CAQ7D"}
|
package/dist/normalizer.js
CHANGED
|
@@ -63,9 +63,14 @@ export function normalizeMessage(raw, index) {
|
|
|
63
63
|
const message = {
|
|
64
64
|
id: generateId(),
|
|
65
65
|
role: raw.role === 'user' ? 'user' : 'assistant',
|
|
66
|
-
content: raw.content,
|
|
66
|
+
content: extractTextContent(raw.content),
|
|
67
67
|
timestamp,
|
|
68
68
|
};
|
|
69
|
+
// Extract attachments from metadata (user uploads and tool-produced media).
|
|
70
|
+
const attachments = normalizeAttachments(raw);
|
|
71
|
+
if (attachments.length > 0) {
|
|
72
|
+
message.attachments = attachments;
|
|
73
|
+
}
|
|
69
74
|
// Assistant messages may carry tool_calls at the top level
|
|
70
75
|
if (raw.role === 'assistant' && raw.tool_calls?.length) {
|
|
71
76
|
message.toolCalls = raw.tool_calls.map((tc, i) => ({
|
|
@@ -94,3 +99,81 @@ export function normalizeSession(raw) {
|
|
|
94
99
|
messageCount: raw.message_count,
|
|
95
100
|
};
|
|
96
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Extract text content from a message that may be multi-modal.
|
|
104
|
+
*
|
|
105
|
+
* When the backend sends multi-modal content, `content` is an array of
|
|
106
|
+
* content blocks. We extract the text portion for display.
|
|
107
|
+
*/
|
|
108
|
+
function extractTextContent(content) {
|
|
109
|
+
if (typeof content === 'string')
|
|
110
|
+
return content;
|
|
111
|
+
if (Array.isArray(content)) {
|
|
112
|
+
const textParts = content
|
|
113
|
+
.filter((block) => block.type === 'text' && typeof block.text === 'string')
|
|
114
|
+
.map((block) => block.text);
|
|
115
|
+
return textParts.join('\n');
|
|
116
|
+
}
|
|
117
|
+
return '';
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Normalize a single raw attachment into a MediaAttachment.
|
|
121
|
+
*/
|
|
122
|
+
function normalizeRawAttachment(raw) {
|
|
123
|
+
if (!raw.url)
|
|
124
|
+
return null;
|
|
125
|
+
const mimeType = raw.mime_type ?? '';
|
|
126
|
+
let type = 'file';
|
|
127
|
+
if (raw.type === 'image' || raw.type === 'video' || raw.type === 'file') {
|
|
128
|
+
type = raw.type;
|
|
129
|
+
}
|
|
130
|
+
else if (mimeType.startsWith('image/')) {
|
|
131
|
+
type = 'image';
|
|
132
|
+
}
|
|
133
|
+
else if (mimeType.startsWith('video/')) {
|
|
134
|
+
type = 'video';
|
|
135
|
+
}
|
|
136
|
+
const attachment = {
|
|
137
|
+
type,
|
|
138
|
+
url: raw.url,
|
|
139
|
+
};
|
|
140
|
+
if (raw.alt)
|
|
141
|
+
attachment.alt = raw.alt;
|
|
142
|
+
if (raw.filename)
|
|
143
|
+
attachment.filename = raw.filename;
|
|
144
|
+
if (mimeType)
|
|
145
|
+
attachment.mimeType = mimeType;
|
|
146
|
+
if (raw.size)
|
|
147
|
+
attachment.size = raw.size;
|
|
148
|
+
if (raw.media_id)
|
|
149
|
+
attachment.mediaId = raw.media_id;
|
|
150
|
+
if (raw.thumbnail_url)
|
|
151
|
+
attachment.thumbnailUrl = raw.thumbnail_url;
|
|
152
|
+
return attachment;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Extract all attachments from a raw message.
|
|
156
|
+
*
|
|
157
|
+
* Checks metadata.attachments (user uploads) and metadata.media
|
|
158
|
+
* (tool-produced media) for renderable media.
|
|
159
|
+
*/
|
|
160
|
+
function normalizeAttachments(raw) {
|
|
161
|
+
const attachments = [];
|
|
162
|
+
// User-uploaded attachments.
|
|
163
|
+
if (raw.metadata?.attachments) {
|
|
164
|
+
for (const rawAtt of raw.metadata.attachments) {
|
|
165
|
+
const att = normalizeRawAttachment(rawAtt);
|
|
166
|
+
if (att)
|
|
167
|
+
attachments.push(att);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Tool-produced media.
|
|
171
|
+
if (raw.metadata?.media) {
|
|
172
|
+
for (const rawMedia of raw.metadata.media) {
|
|
173
|
+
const att = normalizeRawAttachment(rawMedia);
|
|
174
|
+
if (att)
|
|
175
|
+
attachments.push(att);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return attachments;
|
|
179
|
+
}
|
package/dist/types/api.d.ts
CHANGED
|
@@ -9,18 +9,35 @@
|
|
|
9
9
|
* A raw message as stored/returned by the backend.
|
|
10
10
|
* The package normalizes these into ChatMessage before rendering.
|
|
11
11
|
*/
|
|
12
|
+
/**
|
|
13
|
+
* A raw media attachment as returned by the backend.
|
|
14
|
+
*/
|
|
15
|
+
export interface RawAttachment {
|
|
16
|
+
type?: string;
|
|
17
|
+
url?: string;
|
|
18
|
+
alt?: string;
|
|
19
|
+
filename?: string;
|
|
20
|
+
mime_type?: string;
|
|
21
|
+
size?: number;
|
|
22
|
+
media_id?: number;
|
|
23
|
+
thumbnail_url?: string;
|
|
24
|
+
}
|
|
12
25
|
export interface RawMessage {
|
|
13
26
|
role: 'user' | 'assistant';
|
|
14
27
|
content: string;
|
|
15
28
|
metadata?: {
|
|
16
29
|
timestamp?: string;
|
|
17
|
-
type?: 'text' | 'tool_call' | 'tool_result';
|
|
30
|
+
type?: 'text' | 'multimodal' | 'tool_call' | 'tool_result';
|
|
18
31
|
tool_name?: string;
|
|
19
32
|
parameters?: Record<string, unknown>;
|
|
20
33
|
success?: boolean;
|
|
21
34
|
error?: string;
|
|
22
35
|
turn?: number;
|
|
23
36
|
tool_data?: Record<string, unknown>;
|
|
37
|
+
/** Attachments sent with the message. */
|
|
38
|
+
attachments?: RawAttachment[];
|
|
39
|
+
/** Media produced by tool results. */
|
|
40
|
+
media?: RawAttachment[];
|
|
24
41
|
};
|
|
25
42
|
tool_calls?: Array<{
|
|
26
43
|
tool_name: string;
|
|
@@ -34,6 +51,13 @@ export interface SendRequest {
|
|
|
34
51
|
message: string;
|
|
35
52
|
session_id?: string;
|
|
36
53
|
agent_id?: number;
|
|
54
|
+
/** Media attachments to send with the message. */
|
|
55
|
+
attachments?: Array<{
|
|
56
|
+
url?: string;
|
|
57
|
+
media_id?: number;
|
|
58
|
+
mime_type?: string;
|
|
59
|
+
filename?: string;
|
|
60
|
+
}>;
|
|
37
61
|
}
|
|
38
62
|
export interface SendResponse {
|
|
39
63
|
success: boolean;
|
package/dist/types/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/types/api.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE;QACV,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/types/api.ts"],"names":[],"mappings":"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"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { MessageRole, ToolCall, ToolResultMeta, ChatMessage, ContentFormat, } from './message.ts';
|
|
1
|
+
export type { MessageRole, ToolCall, ToolResultMeta, MediaAttachment, ChatMessage, ContentFormat, } from './message.ts';
|
|
2
2
|
export type { ChatSession, ChatAvailability, ChatInitialState, } from './session.ts';
|
|
3
|
-
export type { RawMessage, RawSession, SendRequest, SendResponse, ContinueRequest, ContinueResponse, ListSessionsResponse, GetSessionResponse, DeleteSessionResponse, SessionMetadata, } from './api.ts';
|
|
3
|
+
export type { RawAttachment, RawMessage, RawSession, SendRequest, SendResponse, ContinueRequest, ContinueResponse, ListSessionsResponse, GetSessionResponse, DeleteSessionResponse, SessionMetadata, } from './api.ts';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,WAAW,EACX,QAAQ,EACR,cAAc,EACd,WAAW,EACX,aAAa,GACb,MAAM,cAAc,CAAC;AAEtB,YAAY,EACX,WAAW,EACX,gBAAgB,EAChB,gBAAgB,GAChB,MAAM,cAAc,CAAC;AAEtB,YAAY,EACX,UAAU,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,GACf,MAAM,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,WAAW,EACX,QAAQ,EACR,cAAc,EACd,eAAe,EACf,WAAW,EACX,aAAa,GACb,MAAM,cAAc,CAAC;AAEtB,YAAY,EACX,WAAW,EACX,gBAAgB,EAChB,gBAAgB,GAChB,MAAM,cAAc,CAAC;AAEtB,YAAY,EACX,aAAa,EACb,UAAU,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,GACf,MAAM,UAAU,CAAC"}
|
package/dist/types/message.d.ts
CHANGED
|
@@ -34,6 +34,27 @@ export interface ToolResultMeta {
|
|
|
34
34
|
/** Whether the tool call succeeded. */
|
|
35
35
|
success: boolean;
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* A media attachment on a chat message (image, video, or file).
|
|
39
|
+
*/
|
|
40
|
+
export interface MediaAttachment {
|
|
41
|
+
/** Media type. */
|
|
42
|
+
type: 'image' | 'video' | 'file';
|
|
43
|
+
/** Public URL of the media. */
|
|
44
|
+
url: string;
|
|
45
|
+
/** Alt text or description. */
|
|
46
|
+
alt?: string;
|
|
47
|
+
/** Original filename. */
|
|
48
|
+
filename?: string;
|
|
49
|
+
/** MIME type (e.g. 'image/jpeg'). */
|
|
50
|
+
mimeType?: string;
|
|
51
|
+
/** File size in bytes. */
|
|
52
|
+
size?: number;
|
|
53
|
+
/** WordPress media library attachment ID. */
|
|
54
|
+
mediaId?: number;
|
|
55
|
+
/** Thumbnail URL for previews. */
|
|
56
|
+
thumbnailUrl?: string;
|
|
57
|
+
}
|
|
37
58
|
/**
|
|
38
59
|
* A single message in a chat conversation.
|
|
39
60
|
*/
|
|
@@ -50,6 +71,8 @@ export interface ChatMessage {
|
|
|
50
71
|
toolCalls?: ToolCall[];
|
|
51
72
|
/** Tool result metadata (only on tool_result messages). */
|
|
52
73
|
toolResult?: ToolResultMeta;
|
|
74
|
+
/** Media attachments (images, videos, files) on this message. */
|
|
75
|
+
attachments?: MediaAttachment[];
|
|
53
76
|
}
|
|
54
77
|
/**
|
|
55
78
|
* Content format hint for how message content should be rendered.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../src/types/message.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,aAAa,CAAC;AAExF;;GAEG;AACH,MAAM,WAAW,QAAQ;IACxB,+DAA+D;IAC/D,EAAE,EAAE,MAAM,CAAC;IACX,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,yBAAyB;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,iCAAiC;IACjC,IAAI,EAAE,WAAW,CAAC;IAClB,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../src/types/message.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,aAAa,CAAC;AAExF;;GAEG;AACH,MAAM,WAAW,QAAQ;IACxB,+DAA+D;IAC/D,EAAE,EAAE,MAAM,CAAC;IACX,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,kBAAkB;IAClB,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;IACjC,+BAA+B;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,+BAA+B;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,yBAAyB;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,iCAAiC;IACjC,IAAI,EAAE,WAAW,CAAC;IAClB,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,iEAAiE;IACjE,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;CAChC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC"}
|
package/package.json
CHANGED
package/src/Chat.tsx
CHANGED
|
@@ -52,6 +52,15 @@ export interface ChatProps {
|
|
|
52
52
|
showSessions?: boolean;
|
|
53
53
|
/** Label shown during multi-turn processing. */
|
|
54
54
|
processingLabel?: (turnCount: number) => string;
|
|
55
|
+
/** Whether to show the attachment button in the input. Defaults to true. */
|
|
56
|
+
allowAttachments?: boolean;
|
|
57
|
+
/** Accepted file types for attachments. Defaults to 'image/*,video/*'. */
|
|
58
|
+
acceptFileTypes?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Arbitrary metadata forwarded to the backend with each message.
|
|
61
|
+
* Use for client-side context injection (e.g. `{ client_context: { tab: 'compose', postId: 123 } }`).
|
|
62
|
+
*/
|
|
63
|
+
metadata?: Record<string, unknown>;
|
|
55
64
|
}
|
|
56
65
|
|
|
57
66
|
/**
|
|
@@ -95,6 +104,9 @@ export function Chat({
|
|
|
95
104
|
className,
|
|
96
105
|
showSessions = true,
|
|
97
106
|
processingLabel,
|
|
107
|
+
allowAttachments = true,
|
|
108
|
+
acceptFileTypes,
|
|
109
|
+
metadata,
|
|
98
110
|
}: ChatProps) {
|
|
99
111
|
const chat = useChat({
|
|
100
112
|
basePath,
|
|
@@ -105,6 +117,7 @@ export function Chat({
|
|
|
105
117
|
maxContinueTurns,
|
|
106
118
|
onError,
|
|
107
119
|
onMessage,
|
|
120
|
+
metadata,
|
|
108
121
|
});
|
|
109
122
|
|
|
110
123
|
const baseClass = 'ec-chat';
|
|
@@ -149,6 +162,8 @@ export function Chat({
|
|
|
149
162
|
onSend={chat.sendMessage}
|
|
150
163
|
disabled={chat.isLoading}
|
|
151
164
|
placeholder={placeholder}
|
|
165
|
+
allowAttachments={allowAttachments}
|
|
166
|
+
accept={acceptFileTypes}
|
|
152
167
|
/>
|
|
153
168
|
</AvailabilityGate>
|
|
154
169
|
</div>
|
package/src/api.ts
CHANGED
|
@@ -31,7 +31,12 @@ import { normalizeConversation, normalizeMessage, normalizeSession } from './nor
|
|
|
31
31
|
export interface FetchOptions {
|
|
32
32
|
path: string;
|
|
33
33
|
method?: string;
|
|
34
|
+
/** JSON body (mutually exclusive with formData). */
|
|
34
35
|
data?: Record<string, unknown>;
|
|
36
|
+
/** FormData body for file uploads (mutually exclusive with data). */
|
|
37
|
+
formData?: FormData;
|
|
38
|
+
/** Additional HTTP headers. */
|
|
39
|
+
headers?: Record<string, string>;
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
export type FetchFn = (options: FetchOptions) => Promise<unknown>;
|
|
@@ -60,22 +65,51 @@ export interface ContinueResult {
|
|
|
60
65
|
maxTurnsReached: boolean;
|
|
61
66
|
}
|
|
62
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Attachment metadata to send with a message.
|
|
70
|
+
*/
|
|
71
|
+
export interface SendAttachment {
|
|
72
|
+
url?: string;
|
|
73
|
+
media_id?: number;
|
|
74
|
+
mime_type?: string;
|
|
75
|
+
filename?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
63
78
|
/**
|
|
64
79
|
* Send a user message (create or continue a session).
|
|
80
|
+
*
|
|
81
|
+
* When attachments are provided, they are included in the JSON body
|
|
82
|
+
* as structured metadata (not as file uploads — files should already
|
|
83
|
+
* be in the WordPress media library or accessible by URL).
|
|
84
|
+
*
|
|
85
|
+
* @param metadata - Arbitrary key-value pairs forwarded to the backend
|
|
86
|
+
* alongside the message (e.g. `{ selected_pipeline_id: 42 }` or
|
|
87
|
+
* `{ post_id: 100, context: 'editor' }`). The backend can use these
|
|
88
|
+
* to scope the AI's behavior. Not persisted as message content.
|
|
65
89
|
*/
|
|
66
90
|
export async function sendMessage(
|
|
67
91
|
config: ChatApiConfig,
|
|
68
92
|
content: string,
|
|
69
93
|
sessionId?: string,
|
|
94
|
+
attachments?: SendAttachment[],
|
|
95
|
+
metadata?: Record<string, unknown>,
|
|
70
96
|
): Promise<SendResult> {
|
|
71
97
|
const body: Record<string, unknown> = { message: content };
|
|
72
98
|
if (sessionId) body.session_id = sessionId;
|
|
73
99
|
if (config.agentId) body.agent_id = config.agentId;
|
|
100
|
+
if (attachments?.length) body.attachments = attachments;
|
|
101
|
+
if (metadata) Object.assign(body, metadata);
|
|
102
|
+
|
|
103
|
+
// Generate a unique request ID for idempotent request handling.
|
|
104
|
+
const requestId = typeof crypto !== 'undefined' && crypto.randomUUID
|
|
105
|
+
? crypto.randomUUID()
|
|
106
|
+
: `req_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
74
107
|
|
|
75
108
|
const raw = await config.fetchFn({
|
|
76
109
|
path: config.basePath,
|
|
77
110
|
method: 'POST',
|
|
78
111
|
data: body,
|
|
112
|
+
headers: { 'X-Request-ID': requestId },
|
|
79
113
|
}) as SendResponse;
|
|
80
114
|
|
|
81
115
|
if (!raw.success) {
|
|
@@ -118,13 +152,18 @@ export async function continueResponse(
|
|
|
118
152
|
|
|
119
153
|
/**
|
|
120
154
|
* List sessions for the current user.
|
|
155
|
+
*
|
|
156
|
+
* @param context - Optional context filter (e.g. 'chat', 'editor', 'pipeline').
|
|
157
|
+
* Only sessions created in the matching context are returned.
|
|
121
158
|
*/
|
|
122
159
|
export async function listSessions(
|
|
123
160
|
config: ChatApiConfig,
|
|
124
161
|
limit = 20,
|
|
162
|
+
context?: string,
|
|
125
163
|
): Promise<ChatSession[]> {
|
|
126
164
|
const params = new URLSearchParams({ limit: String(limit) });
|
|
127
165
|
if (config.agentId) params.set('agent_id', String(config.agentId));
|
|
166
|
+
if (context) params.set('context', context);
|
|
128
167
|
|
|
129
168
|
const raw = await config.fetchFn({
|
|
130
169
|
path: `${config.basePath}/sessions?${params.toString()}`,
|