@agentforge-io/chat-sdk 2.0.19 → 2.0.20
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/dist/entities.d.ts +20 -6
- package/dist/react.d.ts +12 -26
- package/dist/react.js +47 -44
- package/dist/session.js +7 -10
- package/package.json +1 -1
package/dist/entities.d.ts
CHANGED
|
@@ -19,25 +19,39 @@ export type ChatRole = 'user' | 'assistant' | 'system';
|
|
|
19
19
|
* Future kinds extend this union without breaking back-compat: the
|
|
20
20
|
* View checks `kind` and falls back to plain text when it's unknown.
|
|
21
21
|
*/
|
|
22
|
+
export interface ChatApprovalCopy {
|
|
23
|
+
title: string;
|
|
24
|
+
body: string;
|
|
25
|
+
approveLabel: string;
|
|
26
|
+
approveBusyLabel: string;
|
|
27
|
+
denyLabel: string;
|
|
28
|
+
denyBusyLabel: string;
|
|
29
|
+
approvedPill: string;
|
|
30
|
+
deniedPill: string;
|
|
31
|
+
readOnlyHint: string;
|
|
32
|
+
blockedTitle: string;
|
|
33
|
+
blockedBody: string;
|
|
34
|
+
expiresPrefix: string;
|
|
35
|
+
}
|
|
22
36
|
export type ChatMessageMetadata = {
|
|
23
37
|
kind: 'awaiting_approval';
|
|
24
38
|
approvalId: string;
|
|
25
39
|
toolName: string;
|
|
26
40
|
expiresAt: string;
|
|
27
|
-
/** Friendly connector display name surfaced by the server when
|
|
28
|
-
* available (e.g. `'Granola'`). Lets the View render a
|
|
29
|
-
* natural-language card without the host having to map slugs
|
|
30
|
-
* to product names. */
|
|
31
41
|
connectorName?: string;
|
|
32
|
-
/** First line of the tool's description from the connector
|
|
33
|
-
* definition. Used as a one-line "what will happen" hint. */
|
|
34
42
|
toolDescription?: string;
|
|
43
|
+
/** Server-authored microcopy for the approval bubble. Generated
|
|
44
|
+
* on every pause by an AI copywriter on the backend so language
|
|
45
|
+
* and tone match the live conversation. View layers render it
|
|
46
|
+
* verbatim; when missing, fall back to a minimal meta render. */
|
|
47
|
+
copy?: ChatApprovalCopy;
|
|
35
48
|
} | {
|
|
36
49
|
kind: 'tool_blocked';
|
|
37
50
|
toolName: string;
|
|
38
51
|
reason?: string;
|
|
39
52
|
connectorName?: string;
|
|
40
53
|
toolDescription?: string;
|
|
54
|
+
copy?: ChatApprovalCopy;
|
|
41
55
|
} | {
|
|
42
56
|
kind: string;
|
|
43
57
|
[k: string]: unknown;
|
package/dist/react.d.ts
CHANGED
|
@@ -37,38 +37,30 @@ export type ApprovalActionHandler = (args: {
|
|
|
37
37
|
/**
|
|
38
38
|
* Localizable copy for the approval / blocked bubbles. Pass via the
|
|
39
39
|
* `approvalCopy` prop on `<ChatWidget>` to drop the defaults (Spanish,
|
|
40
|
-
*
|
|
40
|
+
* Server-authored approval microcopy. The bubble renders whatever the
|
|
41
|
+
* server places in `metadata.copy` — title, body, button labels, etc.
|
|
42
|
+
* That copy is generated by the AgentForge backend on every pause via a
|
|
43
|
+
* dedicated Haiku call, so its language and tone always match the
|
|
44
|
+
* conversation the user is having. There is no client-side dictionary
|
|
45
|
+
* to keep in sync.
|
|
41
46
|
*
|
|
42
|
-
*
|
|
43
|
-
* with — typically a friendly connector name like `'Granola'`. When
|
|
44
|
-
* the server didn't supply it, the function is called with `undefined`
|
|
45
|
-
* and the implementation should pick a sensible generic line. The
|
|
46
|
-
* widget itself never references the raw tool slug in copy.
|
|
47
|
+
* The shape mirrors `ApprovalCopyBundle` in `@agentforge-io/core`.
|
|
47
48
|
*/
|
|
48
49
|
export interface ApprovalCopy {
|
|
49
50
|
title: string;
|
|
50
|
-
body:
|
|
51
|
-
connectorName?: string;
|
|
52
|
-
toolDescription?: string;
|
|
53
|
-
}) => string;
|
|
51
|
+
body: string;
|
|
54
52
|
approveLabel: string;
|
|
55
53
|
approveBusyLabel: string;
|
|
56
54
|
denyLabel: string;
|
|
57
55
|
denyBusyLabel: string;
|
|
58
56
|
approvedPill: string;
|
|
59
57
|
deniedPill: string;
|
|
60
|
-
/** Shown when the host didn't wire `onApprovalAction` — the visitor
|
|
61
|
-
* can't decide from inside the widget so we tell them where to go. */
|
|
62
58
|
readOnlyHint: string;
|
|
63
|
-
/** Used by the `expires in …` countdown. Receives minutes/seconds
|
|
64
|
-
* and returns the formatted string. */
|
|
65
|
-
expiresIn: (mins: number, secs: number) => string;
|
|
66
|
-
/** Block bubble title + body. */
|
|
67
59
|
blockedTitle: string;
|
|
68
|
-
blockedBody:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
blockedBody: string;
|
|
61
|
+
/** Prefix shown before the countdown, e.g. "Expires in". The seconds
|
|
62
|
+
* are appended client-side because they tick locally. */
|
|
63
|
+
expiresPrefix: string;
|
|
72
64
|
}
|
|
73
65
|
export interface ChatWidgetProps {
|
|
74
66
|
/** Public chat token (`aft_*`) issued from the admin UI. */
|
|
@@ -97,12 +89,6 @@ export interface ChatWidgetProps {
|
|
|
97
89
|
* hints.
|
|
98
90
|
*/
|
|
99
91
|
onApprovalAction?: ApprovalActionHandler;
|
|
100
|
-
/**
|
|
101
|
-
* Override any subset of the approval-card copy. Keys you don't
|
|
102
|
-
* provide fall back to the Spanish defaults in `DEFAULT_APPROVAL_COPY`.
|
|
103
|
-
* Use this for translations or to match your product's voice.
|
|
104
|
-
*/
|
|
105
|
-
approvalCopy?: Partial<ApprovalCopy>;
|
|
106
92
|
}
|
|
107
93
|
/**
|
|
108
94
|
* Drop-in chat widget. Owns its own `ChatSession` and re-renders on every
|
package/dist/react.js
CHANGED
|
@@ -154,37 +154,34 @@ function renderMarkdown(src) {
|
|
|
154
154
|
return out.join('');
|
|
155
155
|
}
|
|
156
156
|
/**
|
|
157
|
-
*
|
|
158
|
-
*
|
|
157
|
+
* Minimal fallback used when the server didn't supply `metadata.copy`
|
|
158
|
+
* (older backend, copywriter timeout, missing API key, etc.). Intentionally
|
|
159
|
+
* terse and meta-only — the goal is to avoid lying about language. The
|
|
160
|
+
* server is the source of truth for natural copy.
|
|
159
161
|
*/
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
:
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
(connectorName
|
|
178
|
-
? `No puedo usar ${connectorName} en esta cuenta.`
|
|
179
|
-
: 'No puedo usar esa herramienta en esta cuenta.'),
|
|
180
|
-
};
|
|
162
|
+
function fallbackCopy(ctx) {
|
|
163
|
+
const target = ctx.connectorName ?? ctx.toolName ?? '';
|
|
164
|
+
return {
|
|
165
|
+
title: target || 'Permission required',
|
|
166
|
+
body: target ? `${target} → ✓ / ✗` : 'Permission required',
|
|
167
|
+
approveLabel: '✓',
|
|
168
|
+
approveBusyLabel: '…',
|
|
169
|
+
denyLabel: '✗',
|
|
170
|
+
denyBusyLabel: '…',
|
|
171
|
+
approvedPill: '✓',
|
|
172
|
+
deniedPill: '✗',
|
|
173
|
+
readOnlyHint: '',
|
|
174
|
+
blockedTitle: target || 'Blocked',
|
|
175
|
+
blockedBody: ctx.reason ?? (target ? `${target} blocked` : 'Blocked'),
|
|
176
|
+
expiresPrefix: '⏱',
|
|
177
|
+
};
|
|
178
|
+
}
|
|
181
179
|
/**
|
|
182
180
|
* Drop-in chat widget. Owns its own `ChatSession` and re-renders on every
|
|
183
181
|
* SDK event. Consumers don't need to read `session.getState()` themselves.
|
|
184
182
|
*/
|
|
185
183
|
function ChatWidget(props) {
|
|
186
|
-
const { token, apiBaseUrl, inline = false, position, browserSessionId, resumeConversationId, stream, className, style, onApprovalAction,
|
|
187
|
-
const copy = { ...DEFAULT_APPROVAL_COPY, ...(approvalCopy ?? {}) };
|
|
184
|
+
const { token, apiBaseUrl, inline = false, position, browserSessionId, resumeConversationId, stream, className, style, onApprovalAction, } = props;
|
|
188
185
|
const [session, setSession] = (0, react_1.useState)(null);
|
|
189
186
|
const [status, setStatus] = (0, react_1.useState)('idle');
|
|
190
187
|
const [agent, setAgent] = (0, react_1.useState)();
|
|
@@ -304,7 +301,7 @@ function ChatWidget(props) {
|
|
|
304
301
|
};
|
|
305
302
|
return ((0, jsx_runtime_1.jsxs)("div", { className: rootClass, style: rootStyle, "data-style-id": styleId, children: [!inline && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "af-toggle", "aria-label": open ? 'Close chat' : 'Open chat', "aria-expanded": open, onClick: () => setOpen((v) => !v), children: open ? (0, jsx_runtime_1.jsx)(CloseIcon, {}) : (0, jsx_runtime_1.jsx)(ChatIcon, {}) })), (0, jsx_runtime_1.jsxs)("div", { className: `af-panel ${open ? 'af-open' : ''}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "af-header", children: [theme?.avatarUrl ? (
|
|
306
303
|
// eslint-disable-next-line @next/next/no-img-element
|
|
307
|
-
(0, jsx_runtime_1.jsx)("img", { className: "af-header-avatar", src: theme.avatarUrl, alt: "" })) : null, (0, jsx_runtime_1.jsxs)("div", { className: "af-header-info", children: [(0, jsx_runtime_1.jsx)("div", { className: "af-header-title", children: theme?.title ?? agent?.name ?? 'Chat' }), (0, jsx_runtime_1.jsx)("div", { className: "af-header-subtitle", children: status === 'loading' ? 'Loading…' : agent?.description ?? '' })] }), !inline && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "af-close", onClick: () => setOpen(false), "aria-label": "Close chat", children: (0, jsx_runtime_1.jsx)(CloseIcon, {}) }))] }), (0, jsx_runtime_1.jsx)("div", { className: "af-messages", ref: messagesRef, children: messages.map((m) => ((0, jsx_runtime_1.jsx)(MessageBubble, { message: m,
|
|
304
|
+
(0, jsx_runtime_1.jsx)("img", { className: "af-header-avatar", src: theme.avatarUrl, alt: "" })) : null, (0, jsx_runtime_1.jsxs)("div", { className: "af-header-info", children: [(0, jsx_runtime_1.jsx)("div", { className: "af-header-title", children: theme?.title ?? agent?.name ?? 'Chat' }), (0, jsx_runtime_1.jsx)("div", { className: "af-header-subtitle", children: status === 'loading' ? 'Loading…' : agent?.description ?? '' })] }), !inline && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "af-close", onClick: () => setOpen(false), "aria-label": "Close chat", children: (0, jsx_runtime_1.jsx)(CloseIcon, {}) }))] }), (0, jsx_runtime_1.jsx)("div", { className: "af-messages", ref: messagesRef, children: messages.map((m) => ((0, jsx_runtime_1.jsx)(MessageBubble, { message: m, onApprovalAction: onApprovalAction, onContinue: () => {
|
|
308
305
|
// After a successful Approve, kick the next turn so the
|
|
309
306
|
// gate's fast-path consumes the approval and the tool
|
|
310
307
|
// actually runs. `silent: true` keeps the literal
|
|
@@ -318,13 +315,13 @@ function ChatWidget(props) {
|
|
|
318
315
|
}, 250);
|
|
319
316
|
} }, m.id))) }), lastError && status === 'error' && ((0, jsx_runtime_1.jsx)("div", { className: "af-error", children: lastError })), (0, jsx_runtime_1.jsxs)("div", { className: "af-input-row", children: [(0, jsx_runtime_1.jsx)("textarea", { className: "af-input", value: draft, onChange: (e) => setDraft(e.target.value), onKeyDown: onKeyDown, placeholder: "Type a message\u2026", rows: 1, disabled: status === 'ended' || status === 'loading' || status === 'idle' }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "af-send", onClick: handleSend, disabled: sendDisabled, "aria-label": "Send message", children: (0, jsx_runtime_1.jsx)(SendIcon, {}) })] }), (0, jsx_runtime_1.jsx)("div", { className: "af-footer", children: "Powered by AgentForge" })] })] }));
|
|
320
317
|
}
|
|
321
|
-
function MessageBubble({ message,
|
|
318
|
+
function MessageBubble({ message, onApprovalAction, onContinue, }) {
|
|
322
319
|
const kind = message.metadata?.kind;
|
|
323
320
|
if (kind === 'awaiting_approval') {
|
|
324
|
-
return ((0, jsx_runtime_1.jsx)(ApprovalBubble, { message: message,
|
|
321
|
+
return ((0, jsx_runtime_1.jsx)(ApprovalBubble, { message: message, onAction: onApprovalAction, onContinue: onContinue }));
|
|
325
322
|
}
|
|
326
323
|
if (kind === 'tool_blocked') {
|
|
327
|
-
return (0, jsx_runtime_1.jsx)(BlockedBubble, { message: message
|
|
324
|
+
return (0, jsx_runtime_1.jsx)(BlockedBubble, { message: message });
|
|
328
325
|
}
|
|
329
326
|
const cls = `af-msg af-msg-${message.role}${message.role === 'assistant' && message.isStreaming
|
|
330
327
|
? message.content
|
|
@@ -351,12 +348,16 @@ function MessageBubble({ message, copy, onApprovalAction, onContinue, }) {
|
|
|
351
348
|
* so the chat auto-sends "continue" and the gate's fast-path consumes
|
|
352
349
|
* the row on the next turn.
|
|
353
350
|
*/
|
|
354
|
-
function ApprovalBubble({ message,
|
|
351
|
+
function ApprovalBubble({ message, onAction, onContinue, }) {
|
|
355
352
|
const meta = message.metadata;
|
|
356
353
|
const [busy, setBusy] = (0, react_1.useState)(null);
|
|
357
354
|
const [decided, setDecided] = (0, react_1.useState)(null);
|
|
358
355
|
const [err, setErr] = (0, react_1.useState)(null);
|
|
359
|
-
const
|
|
356
|
+
const copy = meta?.copy ?? fallbackCopy({
|
|
357
|
+
connectorName: meta?.connectorName,
|
|
358
|
+
toolName: meta?.toolName,
|
|
359
|
+
});
|
|
360
|
+
const remaining = useExpiresIn(meta?.expiresAt, copy.expiresPrefix);
|
|
360
361
|
if (!meta)
|
|
361
362
|
return null;
|
|
362
363
|
const act = async (action) => {
|
|
@@ -377,28 +378,27 @@ function ApprovalBubble({ message, copy, onAction, onContinue, }) {
|
|
|
377
378
|
setBusy(null);
|
|
378
379
|
}
|
|
379
380
|
};
|
|
380
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "af-msg af-msg-approval", children: [(0, jsx_runtime_1.jsx)("div", { className: "af-approval-title", children: copy.title }), (0, jsx_runtime_1.jsx)("div", { className: "af-approval-body", children: copy.body({
|
|
381
|
-
connectorName: meta.connectorName,
|
|
382
|
-
toolDescription: meta.toolDescription,
|
|
383
|
-
}) }), remaining && !decided && ((0, jsx_runtime_1.jsx)("div", { className: "af-approval-meta", children: remaining })), decided && ((0, jsx_runtime_1.jsx)("div", { className: `af-approval-pill af-approval-pill-${decided}`, children: decided === 'approved' ? copy.approvedPill : copy.deniedPill })), !decided && onAction && ((0, jsx_runtime_1.jsxs)("div", { className: "af-approval-actions", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "af-approval-btn af-approval-approve", disabled: busy !== null, onClick: () => act('approve'), children: busy === 'approve' ? copy.approveBusyLabel : copy.approveLabel }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "af-approval-btn af-approval-deny", disabled: busy !== null, onClick: () => act('deny'), children: busy === 'deny' ? copy.denyBusyLabel : copy.denyLabel })] })), !decided && !onAction && ((0, jsx_runtime_1.jsx)("div", { className: "af-approval-hint", children: copy.readOnlyHint })), err && (0, jsx_runtime_1.jsx)("div", { className: "af-approval-error", children: err })] }));
|
|
381
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "af-msg af-msg-approval", children: [(0, jsx_runtime_1.jsx)("div", { className: "af-approval-title", children: copy.title }), (0, jsx_runtime_1.jsx)("div", { className: "af-approval-body", children: copy.body }), remaining && !decided && ((0, jsx_runtime_1.jsx)("div", { className: "af-approval-meta", children: remaining })), decided && ((0, jsx_runtime_1.jsx)("div", { className: `af-approval-pill af-approval-pill-${decided}`, children: decided === 'approved' ? copy.approvedPill : copy.deniedPill })), !decided && onAction && ((0, jsx_runtime_1.jsxs)("div", { className: "af-approval-actions", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "af-approval-btn af-approval-approve", disabled: busy !== null, onClick: () => act('approve'), children: busy === 'approve' ? copy.approveBusyLabel : copy.approveLabel }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "af-approval-btn af-approval-deny", disabled: busy !== null, onClick: () => act('deny'), children: busy === 'deny' ? copy.denyBusyLabel : copy.denyLabel })] })), !decided && !onAction && ((0, jsx_runtime_1.jsx)("div", { className: "af-approval-hint", children: copy.readOnlyHint })), err && (0, jsx_runtime_1.jsx)("div", { className: "af-approval-error", children: err })] }));
|
|
384
382
|
}
|
|
385
383
|
/**
|
|
386
384
|
* Terminal "tool blocked" bubble. No action — just informs the visitor
|
|
387
385
|
* that the tool the agent tried isn't available.
|
|
388
386
|
*/
|
|
389
|
-
function BlockedBubble({ message,
|
|
387
|
+
function BlockedBubble({ message, }) {
|
|
390
388
|
const meta = message.metadata;
|
|
391
389
|
if (!meta)
|
|
392
390
|
return null;
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
391
|
+
const copy = meta.copy ?? fallbackCopy({
|
|
392
|
+
connectorName: meta.connectorName,
|
|
393
|
+
toolName: meta.toolName,
|
|
394
|
+
reason: meta.reason,
|
|
395
|
+
});
|
|
396
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "af-msg af-msg-blocked", children: [(0, jsx_runtime_1.jsx)("div", { className: "af-blocked-title", children: copy.blockedTitle }), (0, jsx_runtime_1.jsx)("div", { className: "af-blocked-body", children: copy.blockedBody })] }));
|
|
397
397
|
}
|
|
398
398
|
/** Live "expires in" countdown. Returns null after the deadline.
|
|
399
|
-
* The
|
|
400
|
-
* the hook
|
|
401
|
-
function useExpiresIn(iso,
|
|
399
|
+
* The prefix label is server-authored so the language matches the
|
|
400
|
+
* rest of the bubble; the hook just appends the localised digits. */
|
|
401
|
+
function useExpiresIn(iso, prefix) {
|
|
402
402
|
const [, force] = (0, react_1.useState)(0);
|
|
403
403
|
(0, react_1.useEffect)(() => {
|
|
404
404
|
if (!iso)
|
|
@@ -414,7 +414,10 @@ function useExpiresIn(iso, format) {
|
|
|
414
414
|
const totalSeconds = Math.floor(ms / 1000);
|
|
415
415
|
const m = Math.floor(totalSeconds / 60);
|
|
416
416
|
const s = totalSeconds % 60;
|
|
417
|
-
|
|
417
|
+
const tail = m >= 60
|
|
418
|
+
? `${Math.floor(m / 60)}h ${m % 60}m`
|
|
419
|
+
: `${m}m ${s.toString().padStart(2, '0')}s`;
|
|
420
|
+
return `${prefix} ${tail}`.trim();
|
|
418
421
|
}
|
|
419
422
|
function ChatIcon() {
|
|
420
423
|
return ((0, jsx_runtime_1.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": true, children: (0, jsx_runtime_1.jsx)("path", { d: "M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" }) }));
|
package/dist/session.js
CHANGED
|
@@ -214,12 +214,12 @@ class ChatSession {
|
|
|
214
214
|
expiresAt: c.expiresAt,
|
|
215
215
|
connectorName: c.connectorName,
|
|
216
216
|
toolDescription: c.toolDescription,
|
|
217
|
+
copy: c.copy,
|
|
217
218
|
};
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
: `Necesito tu permiso para continuar.`);
|
|
219
|
+
// Plain-text fallback for legacy clients that don't render
|
|
220
|
+
// metadata.kind. Capable widgets ignore `content` and read
|
|
221
|
+
// `metadata.copy` directly.
|
|
222
|
+
assistant.content = full || c.copy?.body || (c.connectorName ?? '');
|
|
223
223
|
this.updateMessage(assistant);
|
|
224
224
|
continue;
|
|
225
225
|
}
|
|
@@ -231,13 +231,10 @@ class ChatSession {
|
|
|
231
231
|
reason: c.reason,
|
|
232
232
|
connectorName: c.connectorName,
|
|
233
233
|
toolDescription: c.toolDescription,
|
|
234
|
+
copy: c.copy,
|
|
234
235
|
};
|
|
235
236
|
assistant.content =
|
|
236
|
-
full ||
|
|
237
|
-
c.reason ||
|
|
238
|
-
(c.connectorName
|
|
239
|
-
? `No puedo usar ${c.connectorName} en esta cuenta.`
|
|
240
|
-
: `No puedo usar esa herramienta en esta cuenta.`);
|
|
237
|
+
full || c.copy?.blockedBody || c.reason || (c.connectorName ?? '');
|
|
241
238
|
this.updateMessage(assistant);
|
|
242
239
|
continue;
|
|
243
240
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentforge-io/chat-sdk",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.20",
|
|
4
4
|
"description": "Framework-free chat session SDK for AgentForge public chat tokens. Headless — no DOM. Drop into any frontend (React, Vue, Svelte, vanilla) and listen for events.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|