@elqnt/chat 3.3.0 → 3.5.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.
package/TIER2_AUTH.md ADDED
@@ -0,0 +1,258 @@
1
+ # Chat realtime tier-2 authorization — full reference
2
+
3
+ > Audience: agents and engineers touching the chat realtime path (`@elqnt/chat`
4
+ > realtime hooks, the `chat` Go service, or any app that mounts `useChat`).
5
+ > This is the deep reference; `SKILL.md` has the day-to-day usage. See also
6
+ > `packages/@elqnt/CONVENTIONS.md` → "Transport tiers" / "Security rule for
7
+ > tier 2".
8
+
9
+ ---
10
+
11
+ ## TL;DR
12
+
13
+ The chat **realtime** surface (SSE `/stream`, REST `/create` `/send` `/load`
14
+ `/typing` `/end` `/event`, and the WebSocket) is **tier 2**: clients reach the
15
+ `chat` Go service **directly, bypassing the API gateway** (gateways buffer SSE
16
+ and tie up proxy workers on long-lived connections). Because the gateway — and
17
+ its JWT check — is not in the path, **the chat service authorizes every
18
+ connection itself** by validating a short-lived signed token and deriving
19
+ `orgId` / `userId` / `product` from the token's claims.
20
+
21
+ - **Client:** pass `getToken` to `useChat` (or `token`). The package attaches it
22
+ as `?token=` on `/stream` (EventSource can't set headers) and as
23
+ `Authorization: Bearer` on the POSTs.
24
+ - **Server:** validates HS256 with the shared `JWT_SECRET`, audience
25
+ `eloquent-api`, issuer `eloquent-gateway`, expiry. Identity comes **only** from
26
+ the token — body/query `orgId`/`userId` are no longer trusted.
27
+ - **Enforcement:** on by default (`CHAT_AUTH_ENFORCE=true`); a kill-switch allows
28
+ a brief migration window.
29
+
30
+ ---
31
+
32
+ ## Why this exists (the bug it closes)
33
+
34
+ Before this change the realtime handlers authorized on `isOriginAllowed` **only**,
35
+ and that function **returned `true` when the `Origin` header was absent**
36
+ (`// Allow requests without Origin header`). `Origin` is a browser-only courtesy
37
+ header — absent on any server-to-server / CLI / script client and trivially
38
+ forged. So **any non-browser client could `create` / `send` / `stream` as any
39
+ `orgId`/`userId` it supplied in the request body or query**. That is an
40
+ authentication/authorization bypass on an internet-exposed service.
41
+
42
+ Tier 2's whole premise is that the *service* must close this, since the gateway
43
+ isn't there to. Origin is now kept only as **defense-in-depth (CORS)** — it never
44
+ authorizes, and a missing `Origin` no longer grants access.
45
+
46
+ ---
47
+
48
+ ## Architecture
49
+
50
+ ```
51
+ Browser / non-browser client
52
+ │ signed token: ?token= on /stream, Authorization: Bearer on POSTs
53
+ ▼ (DIRECT — no API gateway)
54
+ ┌──────────────────────────────────────────────────────────────┐
55
+ │ chat (Go service) │
56
+ │ ServeSSE / RESTHandler / ServeWS │
57
+ │ → TokenValidator.Authorize(r) │
58
+ │ • extract Bearer header OR ?token= │
59
+ │ • verify HS256 (JWT_SECRET), exp, aud, iss │
60
+ │ • reject alg=none / forged / expired / wrong aud|iss │
61
+ │ → derive orgId / userId / product FROM CLAIMS │
62
+ │ (body/query identity is ignored, except migration mode) │
63
+ │ → origin check kept as CORS defense-in-depth only │
64
+ └──────────────────────────────────────────────────────────────┘
65
+ ```
66
+
67
+ History (`useChatHistory`) is **tier 1** — gateway HTTP with a bearer JWT — and
68
+ is unchanged.
69
+
70
+ ---
71
+
72
+ ## The token (claim shape)
73
+
74
+ One signing path serves both tiers: tokens minted by the API gateway, by
75
+ `@elqnt/api-client/server`'s `generateServerToken`, or by an app's
76
+ `/api/gateway-token` route all validate here.
77
+
78
+ ```jsonc
79
+ {
80
+ "org_id": "<org-uuid>", // tenant — the security boundary
81
+ "user_id": "<user / visitor id>", // conversation identity
82
+ "email": "<email>",
83
+ "role": "user",
84
+ "scopes": ["chat:connect"], // (any scope set; chat does not gate on scope today)
85
+ "product": "eloquent", // routing
86
+ "iss": "eloquent-gateway", // always accepted; JWT_ISSUER also accepted
87
+ "aud": "eloquent-api", // must match JWT_AUDIENCE
88
+ "exp": <now + 1h> // short-lived
89
+ }
90
+ ```
91
+
92
+ Algorithm: **HS256**, signed with `JWT_SECRET` (shared across services via
93
+ `common.env`). `alg=none` and any non-HMAC method are rejected.
94
+
95
+ ---
96
+
97
+ ## Server: `TokenValidator`
98
+
99
+ `backend/services/chat/services/token_validator.go`.
100
+
101
+ | Method | Purpose |
102
+ |---|---|
103
+ | `NewTokenValidator(secret, audience, issuers, enforce, logger)` | Construct. `"eloquent-gateway"` is always an accepted issuer in addition to `issuers`. |
104
+ | `Validate(tokenString) (*ChatTokenClaims, error)` | Verify method/signature/exp/aud/iss; require non-empty `org_id`+`user_id`. |
105
+ | `ExtractToken(r) string` | `Authorization: Bearer …` **or** `?token=`. |
106
+ | `Authorize(r) (claims, ok)` | The one-call gate used by every handler. |
107
+
108
+ `Authorize` return contract:
109
+
110
+ | `claims` | `ok` | Meaning |
111
+ |---|---|---|
112
+ | non-nil | `true` | Authorized — use `claims.OrgID/UserID/Product`. |
113
+ | `nil` | `true` | No token, **but enforcement is OFF** (migration). Caller falls back to query identity and logs. |
114
+ | `nil` | `false` | **Reject (HTTP 401).** |
115
+
116
+ Fail-closed: when enforcing with no configured secret, `Authorize` returns
117
+ `ok=false` (never trusts an unverifiable token).
118
+
119
+ ### Wiring
120
+
121
+ - `ServeSSE` (`sse-handler.go`): after the origin/method checks, `Authorize`;
122
+ 401 on failure; then `orgID/userID = claims.OrgID/UserID`.
123
+ - `RESTHandler` (`rest-handler.go`): `authorizeREST(w, r)` runs CORS + OPTIONS +
124
+ POST + token in one place; each handler then calls
125
+ `applyTokenIdentity(claims, &req.OrgID, &req.UserID)` so body identity is
126
+ overwritten by the token (org-only for agent-join/leave/agent-context).
127
+ - `ServeWS` (`ws-handler.go`): `Authorize` before the upgrade; identity from
128
+ claims; legacy-org remap still applied to the derived org.
129
+
130
+ ---
131
+
132
+ ## Configuration (env)
133
+
134
+ | Var | Default | Meaning |
135
+ |---|---|---|
136
+ | `JWT_SECRET` | — (required to enforce) | HS256 signing secret (shared; `common.env`). |
137
+ | `JWT_ISSUER` | `eloquent` | Accepted issuer (in addition to the always-accepted `eloquent-gateway`). |
138
+ | `JWT_AUDIENCE` | `eloquent-api` | Required token audience. |
139
+ | `CHAT_AUTH_ENFORCE` | `true` | **Secure default.** `false` = migration window: validate-if-present, allow + log token-less connects. |
140
+
141
+ If `CHAT_AUTH_ENFORCE=true` and `JWT_SECRET` is empty, the service **refuses to
142
+ start** (rather than booting fail-open).
143
+
144
+ ---
145
+
146
+ ## Client: `@elqnt/chat`
147
+
148
+ `getToken` / `token` exist on `UseChatOptions`, `TransportConfig`, and the
149
+ low-level `stream-api` options. `getToken` is awaited **before every connect and
150
+ every REST send**, so a long-lived stream keeps working as short-lived tokens
151
+ roll over. A static `token` is also accepted but does not refresh.
152
+
153
+ ### Authenticated app (logged-in user)
154
+
155
+ ```tsx
156
+ import { useChat } from "@elqnt/chat/hooks";
157
+ import { getGatewayToken } from "@elqnt/api-client/browser";
158
+
159
+ const chat = useChat({
160
+ baseUrl, // the chat service endpoint (CHAT_SERVER_BASE_URL → …/api/v1/chat)
161
+ orgId, userId,
162
+ getToken: getGatewayToken, // session token from /api/gateway-token, scoped to the user's org/user
163
+ });
164
+ ```
165
+
166
+ ### Anonymous (public widget, marketing demo)
167
+
168
+ The browser must **never** choose the org. Mint the token in a **server action
169
+ bound to a server-trusted org** — Next.js encrypts bound arguments, so a browser
170
+ caller cannot substitute a different org. The visitor id (non-privileged
171
+ conversation identity) may come from the client.
172
+
173
+ ```ts
174
+ // actions/chat-token-actions.ts
175
+ "use server";
176
+ import * as jose from "jose";
177
+
178
+ export async function mintAnonChatToken(orgId: string, product: string, userId: string) {
179
+ const secret = new TextEncoder().encode(process.env.JWT_SECRET!);
180
+ return new jose.SignJWT({ org_id: orgId, user_id: userId, role: "user",
181
+ scopes: ["chat:connect"], product })
182
+ .setProtectedHeader({ alg: "HS256" })
183
+ .setIssuedAt().setIssuer("eloquent-gateway").setAudience("eloquent-api")
184
+ .setExpirationTime("1h").sign(secret);
185
+ }
186
+ ```
187
+
188
+ ```tsx
189
+ // server component — bind the TRUSTED org (resolved server-side from widgetId / DEMO_ORG_ID)
190
+ <WidgetRuntime getChatToken={mintAnonChatToken.bind(null, widget.orgId, "eloquent")} />
191
+
192
+ // client component
193
+ const chat = useChat({ baseUrl, orgId, userId: visitorId,
194
+ getToken: () => getChatToken(visitorId) }); // org is baked into the bound action
195
+ ```
196
+
197
+ > **Security rule:** the bound `orgId` must come from a trusted server source
198
+ > (a widget resolved server-side, a `DEMO_ORG_ID` env, a verified session) —
199
+ > never from a client-supplied query param or POST body.
200
+
201
+ ---
202
+
203
+ ## How the package attaches the token
204
+
205
+ | Surface | Mechanism |
206
+ |---|---|
207
+ | SSE `/stream` (EventSource, `transport/sse.ts`) | `?token=<jwt>` appended to the URL (headers impossible on EventSource). |
208
+ | SSE `/stream` (fetch fallback, `transport/sse-fetch.ts`) | `?token=` **and** `Authorization: Bearer` (fetch can set headers). |
209
+ | POST `/create` `/send` `/load` `/typing` `/end` `/event` | `Authorization: Bearer <jwt>` header. |
210
+
211
+ `resolveTransportToken(cfg)` (in `transport/types.ts`) prefers `getToken` and
212
+ falls back to `token`; it never throws (a failing provider → tokenless → the
213
+ service rejects when enforcing).
214
+
215
+ ---
216
+
217
+ ## Rollout / migration
218
+
219
+ 1. Deploy the service with `CHAT_AUTH_ENFORCE=false` (migration window): tokens
220
+ are validated when present, token-less connects are allowed + logged.
221
+ 2. Ship every client with `getToken` (web app done: home, admin tester, widget,
222
+ demo). Update any native / WhatsApp / server-to-server callers to send a
223
+ token too.
224
+ 3. Flip `CHAT_AUTH_ENFORCE=true` (the default) once clients are updated; remove
225
+ the override.
226
+
227
+ The kill-switch defaults secure — leaving it unset is the production posture.
228
+
229
+ ---
230
+
231
+ ## Verification
232
+
233
+ - `go build ./...` + `go vet` clean in `backend/services/chat`.
234
+ - `services/token_validator_test.go` — 11 tests: forged signature, `alg=none`,
235
+ expired, wrong audience/issuer, token-less rejection (enforce), migration-mode
236
+ allow, header-vs-query, and **identity derived from the token, not a spoofed
237
+ query**.
238
+ - `@elqnt/chat` builds (ESM/CJS/DTS); `eloquent-app` `tsc --noEmit` clean.
239
+
240
+ ---
241
+
242
+ ## File map
243
+
244
+ **Backend (`backend/services/chat/`)**
245
+ - `services/token_validator.go` — validator + `Authorize` gate *(new)*
246
+ - `services/token_validator_test.go` — tests *(new)*
247
+ - `handlers/sse-handler.go`, `handlers/rest-handler.go`, `handlers/ws-handler.go` — wired
248
+ - `main.go` — env + validator construction + kill-switch
249
+
250
+ **Package (`packages/@elqnt/chat/`)**
251
+ - `transport/types.ts` — `getToken`/`token` on `TransportConfig` + `resolveTransportToken`
252
+ - `transport/sse.ts`, `transport/sse-fetch.ts` — attach token
253
+ - `api/stream-api.ts` — `token`/`getToken` on the low-level functions
254
+ - `hooks/use-chat.ts` — `getToken`/`token` options, refreshed via ref
255
+
256
+ **App (`apps/eloquent-app/`)**
257
+ - `actions/chat-token-actions.ts` — anonymous bound-org minter *(new)*
258
+ - home chat wrapper + context, admin agent tester, widget runtime + page, demo page + component
@@ -43,6 +43,17 @@ interface StreamApiOptions {
43
43
  orgId: string;
44
44
  /** User ID */
45
45
  userId: string;
46
+ /**
47
+ * Tier-2 signed token (REQUIRED in production). The chat service authorizes
48
+ * the request from this token and derives orgId/userId/product from its
49
+ * claims (the body orgId/userId are not trusted for authorization). Pass a
50
+ * static `token`, or a `getToken` provider that is awaited per call so
51
+ * short-lived tokens refresh. See SKILL.md and @elqnt/api-client/server's
52
+ * generateServerToken for the claim shape.
53
+ */
54
+ token?: string;
55
+ /** Token provider, awaited per call. Takes precedence over `token`. */
56
+ getToken?: () => Promise<string | null | undefined> | string | null | undefined;
46
57
  }
47
58
  /**
48
59
  * Response from stream API calls
@@ -43,6 +43,17 @@ interface StreamApiOptions {
43
43
  orgId: string;
44
44
  /** User ID */
45
45
  userId: string;
46
+ /**
47
+ * Tier-2 signed token (REQUIRED in production). The chat service authorizes
48
+ * the request from this token and derives orgId/userId/product from its
49
+ * claims (the body orgId/userId are not trusted for authorization). Pass a
50
+ * static `token`, or a `getToken` provider that is awaited per call so
51
+ * short-lived tokens refresh. See SKILL.md and @elqnt/api-client/server's
52
+ * generateServerToken for the claim shape.
53
+ */
54
+ token?: string;
55
+ /** Token provider, awaited per call. Takes precedence over `token`. */
56
+ getToken?: () => Promise<string | null | undefined> | string | null | undefined;
46
57
  }
47
58
  /**
48
59
  * Response from stream API calls
package/dist/api/index.js CHANGED
@@ -1,4 +1,3 @@
1
- "use client";
2
1
  "use strict";
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -54,10 +53,22 @@ module.exports = __toCommonJS(api_exports);
54
53
  var import_browser2 = require("@elqnt/api-client/browser");
55
54
 
56
55
  // api/stream-api.ts
57
- async function streamFetch(url, body) {
56
+ async function resolveToken(opts) {
57
+ if (opts.getToken) {
58
+ try {
59
+ return await opts.getToken() ?? void 0;
60
+ } catch {
61
+ return void 0;
62
+ }
63
+ }
64
+ return opts.token ?? void 0;
65
+ }
66
+ async function streamFetch(url, body, token) {
67
+ const headers = { "Content-Type": "application/json" };
68
+ if (token) headers["Authorization"] = `Bearer ${token}`;
58
69
  const response = await fetch(url, {
59
70
  method: "POST",
60
- headers: { "Content-Type": "application/json" },
71
+ headers,
61
72
  body: JSON.stringify(body)
62
73
  });
63
74
  if (!response.ok) {
@@ -72,9 +83,11 @@ async function streamFetch(url, body) {
72
83
  }
73
84
  async function createChat(options) {
74
85
  const { baseUrl, orgId, userId, metadata } = options;
86
+ const token = await resolveToken(options);
75
87
  const result = await streamFetch(
76
88
  `${baseUrl}/create`,
77
- { orgId, userId, metadata }
89
+ { orgId, userId, metadata },
90
+ token
78
91
  );
79
92
  if (!result.success || !result.data?.chatKey) {
80
93
  throw new Error(result.error || "Failed to create chat");
@@ -83,6 +96,7 @@ async function createChat(options) {
83
96
  }
84
97
  async function sendChatMessage(options) {
85
98
  const { baseUrl, orgId, chatKey, userId, content, attachments, metadata } = options;
99
+ const token = await resolveToken(options);
86
100
  const message = {
87
101
  id: `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`,
88
102
  role: "user",
@@ -99,18 +113,19 @@ async function sendChatMessage(options) {
99
113
  userId,
100
114
  message,
101
115
  metadata
102
- });
116
+ }, token);
103
117
  if (!result.success) {
104
118
  throw new Error(result.error || "Failed to send message");
105
119
  }
106
120
  }
107
121
  async function loadChat(options) {
108
122
  const { baseUrl, orgId, chatKey, userId } = options;
123
+ const token = await resolveToken(options);
109
124
  const result = await streamFetch(`${baseUrl}/load`, {
110
125
  orgId,
111
126
  chatKey,
112
127
  userId
113
- });
128
+ }, token);
114
129
  if (!result.success || !result.data?.chat) {
115
130
  throw new Error(result.error || "Failed to load chat");
116
131
  }
@@ -118,37 +133,40 @@ async function loadChat(options) {
118
133
  }
119
134
  async function endChat(options) {
120
135
  const { baseUrl, orgId, chatKey, userId, reason } = options;
136
+ const token = await resolveToken(options);
121
137
  const result = await streamFetch(`${baseUrl}/end`, {
122
138
  orgId,
123
139
  chatKey,
124
140
  userId,
125
141
  data: reason ? { reason } : void 0
126
- });
142
+ }, token);
127
143
  if (!result.success) {
128
144
  throw new Error(result.error || "Failed to end chat");
129
145
  }
130
146
  }
131
147
  async function sendTypingIndicator(options) {
132
148
  const { baseUrl, orgId, chatKey, userId, typing } = options;
149
+ const token = await resolveToken(options);
133
150
  const result = await streamFetch(`${baseUrl}/typing`, {
134
151
  orgId,
135
152
  chatKey,
136
153
  userId,
137
154
  typing
138
- });
155
+ }, token);
139
156
  if (!result.success) {
140
157
  throw new Error(result.error || "Failed to send typing indicator");
141
158
  }
142
159
  }
143
160
  async function sendEvent(options) {
144
161
  const { baseUrl, orgId, chatKey, userId, type, data } = options;
162
+ const token = await resolveToken(options);
145
163
  const result = await streamFetch(`${baseUrl}/event`, {
146
164
  type,
147
165
  orgId,
148
166
  chatKey,
149
167
  userId,
150
168
  data
151
- });
169
+ }, token);
152
170
  if (!result.success) {
153
171
  throw new Error(result.error || "Failed to send event");
154
172
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../api/index.ts","../../api/stream-api.ts","../../api/memory.ts"],"sourcesContent":["/**\n * Chat API\n *\n * API functions for chat operations.\n *\n * ## Browser API (uses @elqnt/api-client)\n * ```typescript\n * import { getChatHistoryApi, getChatApi } from \"@elqnt/chat/api\";\n *\n * const history = await getChatHistoryApi({ baseUrl, orgId, limit: 10 });\n * const chat = await getChatApi(chatKey, { baseUrl, orgId });\n * ```\n *\n * ## Stream API (direct HTTP calls)\n * ```typescript\n * import { createChat, sendChatMessage, loadChat } from \"@elqnt/chat/api\";\n *\n * const chatKey = await createChat({ baseUrl, orgId, userId });\n * await sendChatMessage({ baseUrl, orgId, chatKey, userId, content: \"Hello!\" });\n * ```\n */\n\nimport { browserApiRequest } from \"@elqnt/api-client/browser\";\nimport type { ApiResponse, ApiClientOptions } from \"@elqnt/api-client\";\nimport type { ResponseMetadata } from \"@elqnt/types\";\nimport type { ChatSummary, Chat } from \"../models\";\n\n// Re-export types from models\nexport type { ChatSummary, Chat } from \"../models\";\n\n// Re-export stream API functions\nexport {\n createChat,\n sendChatMessage,\n loadChat,\n endChat,\n sendTypingIndicator,\n sendEvent,\n} from \"./stream-api\";\n\nexport type {\n StreamApiOptions,\n StreamApiResponse,\n CreateChatApiOptions,\n CreateChatResponseData,\n SendMessageApiOptions,\n LoadChatApiOptions,\n LoadChatResponseData,\n EndChatApiOptions,\n TypingApiOptions,\n SendEventApiOptions,\n} from \"./stream-api\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport interface ChatHistoryResponse {\n chats: ChatSummary[];\n total: number;\n hasMore: boolean;\n metadata: ResponseMetadata;\n}\n\nexport interface ChatResponse {\n chat: ChatSummary;\n metadata: ResponseMetadata;\n}\n\nexport interface UpdateChatResponse {\n chatKey: string;\n title: string;\n pinned: boolean;\n metadata: ResponseMetadata;\n}\n\nexport interface ActiveChatsResponse {\n chats: ChatSummary[];\n metadata: ResponseMetadata;\n}\n\nexport interface ActiveChatsCountResponse {\n count: number;\n metadata: ResponseMetadata;\n}\n\nexport interface WaitingChatsCountResponse {\n count: number;\n metadata: ResponseMetadata;\n}\n\nexport interface QueueResponse {\n queues: Queue[];\n metadata: ResponseMetadata;\n}\n\nexport interface Queue {\n id: string;\n name: string;\n agentId: string;\n waitingCount: number;\n activeCount: number;\n}\n\nexport interface AgentSession {\n agentId: string;\n userId: string;\n userEmail: string;\n status: string;\n connectedAt: string;\n lastActivityAt: string;\n}\n\nexport interface OnlineSessionsResponse {\n sessions: AgentSession[];\n metadata: ResponseMetadata;\n}\n\nexport interface AgentSessionResponse {\n session: AgentSession;\n metadata: ResponseMetadata;\n}\n\n// =============================================================================\n// CHAT HISTORY\n// =============================================================================\n\nexport async function getChatHistoryApi(\n options: ApiClientOptions & { limit?: number; offset?: number; skipCache?: boolean }\n): Promise<ApiResponse<ChatHistoryResponse>> {\n return browserApiRequest(\"/api/v1/chats\", {\n method: \"POST\",\n body: {\n limit: options.limit || 15,\n offset: options.offset || 0,\n ...(options.skipCache ? { skipCache: true } : {}),\n },\n ...options,\n });\n}\n\nexport async function getChatApi(\n chatKey: string,\n options: ApiClientOptions\n): Promise<ApiResponse<ChatResponse>> {\n return browserApiRequest(`/api/v1/chats/${chatKey}`, {\n method: \"GET\",\n ...options,\n });\n}\n\nexport async function updateChatApi(\n chatKey: string,\n updates: { title?: string; pinned?: boolean },\n options: ApiClientOptions\n): Promise<ApiResponse<UpdateChatResponse>> {\n return browserApiRequest(`/api/v1/chats/${chatKey}`, {\n method: \"PATCH\",\n body: updates,\n ...options,\n });\n}\n\nexport async function deleteChatApi(\n chatKey: string,\n options: ApiClientOptions\n): Promise<ApiResponse<{ success: boolean; metadata: ResponseMetadata }>> {\n return browserApiRequest(`/api/v1/chats/${chatKey}`, {\n method: \"DELETE\",\n ...options,\n });\n}\n\n// =============================================================================\n// ACTIVE CHATS\n// =============================================================================\n\nexport async function getActiveChatsCountApi(\n options: ApiClientOptions\n): Promise<ApiResponse<ActiveChatsCountResponse>> {\n return browserApiRequest(\"/api/v1/chats/active/count\", {\n method: \"GET\",\n ...options,\n });\n}\n\nexport async function getActiveChatsApi(\n options: ApiClientOptions & { pastHours?: number }\n): Promise<ApiResponse<ActiveChatsResponse>> {\n const params = new URLSearchParams();\n if (options.pastHours) params.set(\"pastHours\", String(options.pastHours));\n const queryString = params.toString();\n return browserApiRequest(`/api/v1/chats/active${queryString ? `?${queryString}` : \"\"}`, {\n method: \"GET\",\n ...options,\n });\n}\n\n// =============================================================================\n// PROJECT CHATS\n// =============================================================================\n\nexport async function updateProjectChatTitleApi(\n projectId: string,\n chatKey: string,\n title: string,\n options: ApiClientOptions\n): Promise<ApiResponse<{ success: boolean; metadata: ResponseMetadata }>> {\n return browserApiRequest(`/api/v1/projects/${projectId}/chats/${chatKey}/title`, {\n method: \"PUT\",\n body: { title },\n ...options,\n });\n}\n\nexport async function addChatToProjectApi(\n projectId: string,\n chatKey: string,\n options: ApiClientOptions\n): Promise<ApiResponse<{ success: boolean; metadata: ResponseMetadata }>> {\n return browserApiRequest(`/api/v1/projects/${projectId}/chats`, {\n method: \"POST\",\n body: { chatKey },\n ...options,\n });\n}\n\n// =============================================================================\n// WAITING CHATS\n// =============================================================================\n\nexport async function getWaitingChatsCountApi(\n options: ApiClientOptions\n): Promise<ApiResponse<WaitingChatsCountResponse>> {\n return browserApiRequest(\"/api/v1/chats/waiting/count\", {\n method: \"GET\",\n ...options,\n });\n}\n\nexport async function getChatsByUserApi(\n userEmail: string,\n options: ApiClientOptions\n): Promise<ApiResponse<ChatHistoryResponse>> {\n return browserApiRequest(`/api/v1/chats/user/${encodeURIComponent(userEmail)}`, {\n method: \"GET\",\n ...options,\n });\n}\n\n// =============================================================================\n// QUEUES\n// =============================================================================\n\nexport async function listQueuesApi(\n options: ApiClientOptions\n): Promise<ApiResponse<QueueResponse>> {\n return browserApiRequest(\"/api/v1/queues\", {\n method: \"GET\",\n ...options,\n });\n}\n\n// =============================================================================\n// AGENT SESSIONS\n// =============================================================================\n\nexport async function getOnlineSessionsApi(\n options: ApiClientOptions\n): Promise<ApiResponse<OnlineSessionsResponse>> {\n return browserApiRequest(\"/api/v1/agents/sessions/online\", {\n method: \"GET\",\n ...options,\n });\n}\n\nexport async function getAgentSessionApi(\n agentId: string,\n options: ApiClientOptions\n): Promise<ApiResponse<AgentSessionResponse>> {\n return browserApiRequest(`/api/v1/agents/sessions/${agentId}`, {\n method: \"GET\",\n ...options,\n });\n}\n\n// =============================================================================\n// MEMORY\n// =============================================================================\n\nexport {\n getProfileApi,\n patchProfileApi,\n replaceProfileApi,\n clearProfileApi,\n deleteContactApi,\n deleteNoteApi,\n getSummaryApi,\n clearSummaryApi,\n regenerateSummaryApi,\n} from \"./memory\";\n\nexport type { SummaryView } from \"./memory\";\n","/**\n * Stream API\n *\n * Low-level functions for chat streaming operations.\n * These are used internally by the transport layer but can also\n * be used directly for custom implementations.\n *\n * @example\n * ```typescript\n * import { createChat, sendChatMessage, loadChat } from \"@elqnt/chat/api\";\n *\n * // Create a new chat\n * const chatKey = await createChat({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * userId: \"user-456\",\n * });\n *\n * // Send a message\n * await sendChatMessage({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey,\n * userId: \"user-456\",\n * content: \"Hello!\",\n * });\n * ```\n */\n\nimport type { Chat, ChatMessage } from \"../models\";\n\n/**\n * Base options for all stream API calls\n */\nexport interface StreamApiOptions {\n /** Base URL for the chat server */\n baseUrl: string;\n /** Organization ID */\n orgId: string;\n /** User ID */\n userId: string;\n}\n\n/**\n * Response from stream API calls\n */\nexport interface StreamApiResponse<T = unknown> {\n success: boolean;\n data?: T;\n error?: string;\n}\n\n/**\n * Create chat options\n */\nexport interface CreateChatApiOptions extends StreamApiOptions {\n /** Optional metadata for the new chat */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Create chat response data\n */\nexport interface CreateChatResponseData {\n chatKey: string;\n}\n\n/**\n * Send message options\n */\nexport interface SendMessageApiOptions extends StreamApiOptions {\n /** Chat key */\n chatKey: string;\n /** Message content */\n content: string;\n /** Optional attachments */\n attachments?: unknown[];\n /** Optional message metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Load chat options\n */\nexport interface LoadChatApiOptions extends StreamApiOptions {\n /** Chat key to load */\n chatKey: string;\n}\n\n/**\n * Load chat response data\n */\nexport interface LoadChatResponseData {\n chat: Chat;\n}\n\n/**\n * End chat options\n */\nexport interface EndChatApiOptions extends StreamApiOptions {\n /** Chat key to end */\n chatKey: string;\n /** Optional end reason */\n reason?: string;\n}\n\n/**\n * Typing indicator options\n */\nexport interface TypingApiOptions extends StreamApiOptions {\n /** Chat key */\n chatKey: string;\n /** Whether user is typing */\n typing: boolean;\n}\n\n/**\n * Generic event options\n */\nexport interface SendEventApiOptions extends StreamApiOptions {\n /** Chat key */\n chatKey: string;\n /** Event type */\n type: string;\n /** Event data */\n data?: Record<string, unknown>;\n}\n\n/**\n * Internal fetch helper\n */\nasync function streamFetch<T>(\n url: string,\n body: Record<string, unknown>\n): Promise<StreamApiResponse<T>> {\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return {\n success: false,\n error: `API error: ${response.status} - ${errorText}`,\n };\n }\n\n const data = await response.json();\n return { success: true, data };\n}\n\n/**\n * Create a new chat session\n *\n * @param options - Create chat options\n * @returns Chat key of the created chat\n *\n * @example\n * ```typescript\n * const chatKey = await createChat({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * userId: \"user-456\",\n * metadata: { source: \"website\" },\n * });\n * ```\n */\nexport async function createChat(\n options: CreateChatApiOptions\n): Promise<string> {\n const { baseUrl, orgId, userId, metadata } = options;\n\n const result = await streamFetch<CreateChatResponseData>(\n `${baseUrl}/create`,\n { orgId, userId, metadata }\n );\n\n if (!result.success || !result.data?.chatKey) {\n throw new Error(result.error || \"Failed to create chat\");\n }\n\n return result.data.chatKey;\n}\n\n/**\n * Send a message in a chat\n *\n * @param options - Send message options\n *\n * @example\n * ```typescript\n * await sendChatMessage({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * content: \"Hello!\",\n * });\n * ```\n */\nexport async function sendChatMessage(\n options: SendMessageApiOptions\n): Promise<void> {\n const { baseUrl, orgId, chatKey, userId, content, attachments, metadata } =\n options;\n\n const message: Partial<ChatMessage> = {\n id: `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`,\n role: \"user\",\n content,\n time: Date.now(),\n status: \"sending\",\n senderId: userId,\n createdAt: Date.now(),\n attachments: attachments as ChatMessage[\"attachments\"],\n };\n\n const result = await streamFetch(`${baseUrl}/send`, {\n orgId,\n chatKey,\n userId,\n message,\n metadata,\n });\n\n if (!result.success) {\n throw new Error(result.error || \"Failed to send message\");\n }\n}\n\n/**\n * Load an existing chat\n *\n * @param options - Load chat options\n * @returns The loaded chat object\n *\n * @example\n * ```typescript\n * const chat = await loadChat({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * });\n * ```\n */\nexport async function loadChat(options: LoadChatApiOptions): Promise<Chat> {\n const { baseUrl, orgId, chatKey, userId } = options;\n\n const result = await streamFetch<LoadChatResponseData>(`${baseUrl}/load`, {\n orgId,\n chatKey,\n userId,\n });\n\n if (!result.success || !result.data?.chat) {\n throw new Error(result.error || \"Failed to load chat\");\n }\n\n return result.data.chat;\n}\n\n/**\n * End a chat session\n *\n * @param options - End chat options\n *\n * @example\n * ```typescript\n * await endChat({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * reason: \"User closed chat\",\n * });\n * ```\n */\nexport async function endChat(options: EndChatApiOptions): Promise<void> {\n const { baseUrl, orgId, chatKey, userId, reason } = options;\n\n const result = await streamFetch(`${baseUrl}/end`, {\n orgId,\n chatKey,\n userId,\n data: reason ? { reason } : undefined,\n });\n\n if (!result.success) {\n throw new Error(result.error || \"Failed to end chat\");\n }\n}\n\n/**\n * Send typing indicator\n *\n * @param options - Typing indicator options\n *\n * @example\n * ```typescript\n * // User started typing\n * await sendTypingIndicator({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * typing: true,\n * });\n *\n * // User stopped typing\n * await sendTypingIndicator({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * typing: false,\n * });\n * ```\n */\nexport async function sendTypingIndicator(\n options: TypingApiOptions\n): Promise<void> {\n const { baseUrl, orgId, chatKey, userId, typing } = options;\n\n const result = await streamFetch(`${baseUrl}/typing`, {\n orgId,\n chatKey,\n userId,\n typing,\n });\n\n if (!result.success) {\n throw new Error(result.error || \"Failed to send typing indicator\");\n }\n}\n\n/**\n * Send a generic event\n *\n * @param options - Event options\n *\n * @example\n * ```typescript\n * await sendEvent({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * type: \"skill_activate\",\n * data: { skillId: \"research\" },\n * });\n * ```\n */\nexport async function sendEvent(options: SendEventApiOptions): Promise<void> {\n const { baseUrl, orgId, chatKey, userId, type, data } = options;\n\n const result = await streamFetch(`${baseUrl}/event`, {\n type,\n orgId,\n chatKey,\n userId,\n data,\n });\n\n if (!result.success) {\n throw new Error(result.error || \"Failed to send event\");\n }\n}\n","import { browserApiRequest } from \"@elqnt/api-client/browser\";\nimport type { ApiClientOptions, ApiResponse } from \"@elqnt/api-client\";\nimport type { MemoryProfile } from \"../models\";\n\nexport interface SummaryView {\n chatKey: string;\n text: string;\n updatedAt: string;\n}\n\nexport const getProfileApi = (o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(\"/api/v1/memory/profile\", { method: \"GET\", ...o });\n\nexport const patchProfileApi = (patch: Partial<MemoryProfile>, o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(\"/api/v1/memory/profile\", { method: \"PATCH\", body: patch, ...o });\n\nexport const replaceProfileApi = (p: MemoryProfile, o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(\"/api/v1/memory/profile\", { method: \"PUT\", body: p, ...o });\n\nexport const clearProfileApi = (o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(\"/api/v1/memory/profile\", { method: \"DELETE\", ...o });\n\nexport const deleteContactApi = (name: string, o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(`/api/v1/memory/profile/contacts/${encodeURIComponent(name)}`, { method: \"DELETE\", ...o });\n\nexport const deleteNoteApi = (index: number, o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(`/api/v1/memory/profile/notes/${index}`, { method: \"DELETE\", ...o });\n\nexport const getSummaryApi = (chatKey: string, o: ApiClientOptions): Promise<ApiResponse<SummaryView>> =>\n browserApiRequest(`/api/v1/chats/${chatKey}/summary`, { method: \"GET\", ...o });\n\nexport const clearSummaryApi = (chatKey: string, o: ApiClientOptions): Promise<ApiResponse<void>> =>\n browserApiRequest(`/api/v1/chats/${chatKey}/summary`, { method: \"DELETE\", ...o });\n\nexport const regenerateSummaryApi = (chatKey: string, o: ApiClientOptions): Promise<ApiResponse<SummaryView>> =>\n browserApiRequest(`/api/v1/chats/${chatKey}/summary/regenerate`, { method: \"POST\", ...o });\n"],"mappings":"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","names":["import_browser"]}
1
+ {"version":3,"sources":["../../api/index.ts","../../api/stream-api.ts","../../api/memory.ts"],"sourcesContent":["/**\n * Chat API\n *\n * API functions for chat operations.\n *\n * ## Browser API (uses @elqnt/api-client)\n * ```typescript\n * import { getChatHistoryApi, getChatApi } from \"@elqnt/chat/api\";\n *\n * const history = await getChatHistoryApi({ baseUrl, orgId, limit: 10 });\n * const chat = await getChatApi(chatKey, { baseUrl, orgId });\n * ```\n *\n * ## Stream API (direct HTTP calls)\n * ```typescript\n * import { createChat, sendChatMessage, loadChat } from \"@elqnt/chat/api\";\n *\n * const chatKey = await createChat({ baseUrl, orgId, userId });\n * await sendChatMessage({ baseUrl, orgId, chatKey, userId, content: \"Hello!\" });\n * ```\n */\n\nimport { browserApiRequest } from \"@elqnt/api-client/browser\";\nimport type { ApiResponse, ApiClientOptions } from \"@elqnt/api-client\";\nimport type { ResponseMetadata } from \"@elqnt/types\";\nimport type { ChatSummary, Chat } from \"../models\";\n\n// Re-export types from models\nexport type { ChatSummary, Chat } from \"../models\";\n\n// Re-export stream API functions\nexport {\n createChat,\n sendChatMessage,\n loadChat,\n endChat,\n sendTypingIndicator,\n sendEvent,\n} from \"./stream-api\";\n\nexport type {\n StreamApiOptions,\n StreamApiResponse,\n CreateChatApiOptions,\n CreateChatResponseData,\n SendMessageApiOptions,\n LoadChatApiOptions,\n LoadChatResponseData,\n EndChatApiOptions,\n TypingApiOptions,\n SendEventApiOptions,\n} from \"./stream-api\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport interface ChatHistoryResponse {\n chats: ChatSummary[];\n total: number;\n hasMore: boolean;\n metadata: ResponseMetadata;\n}\n\nexport interface ChatResponse {\n chat: ChatSummary;\n metadata: ResponseMetadata;\n}\n\nexport interface UpdateChatResponse {\n chatKey: string;\n title: string;\n pinned: boolean;\n metadata: ResponseMetadata;\n}\n\nexport interface ActiveChatsResponse {\n chats: ChatSummary[];\n metadata: ResponseMetadata;\n}\n\nexport interface ActiveChatsCountResponse {\n count: number;\n metadata: ResponseMetadata;\n}\n\nexport interface WaitingChatsCountResponse {\n count: number;\n metadata: ResponseMetadata;\n}\n\nexport interface QueueResponse {\n queues: Queue[];\n metadata: ResponseMetadata;\n}\n\nexport interface Queue {\n id: string;\n name: string;\n agentId: string;\n waitingCount: number;\n activeCount: number;\n}\n\nexport interface AgentSession {\n agentId: string;\n userId: string;\n userEmail: string;\n status: string;\n connectedAt: string;\n lastActivityAt: string;\n}\n\nexport interface OnlineSessionsResponse {\n sessions: AgentSession[];\n metadata: ResponseMetadata;\n}\n\nexport interface AgentSessionResponse {\n session: AgentSession;\n metadata: ResponseMetadata;\n}\n\n// =============================================================================\n// CHAT HISTORY\n// =============================================================================\n\nexport async function getChatHistoryApi(\n options: ApiClientOptions & { limit?: number; offset?: number; skipCache?: boolean }\n): Promise<ApiResponse<ChatHistoryResponse>> {\n return browserApiRequest(\"/api/v1/chats\", {\n method: \"POST\",\n body: {\n limit: options.limit || 15,\n offset: options.offset || 0,\n ...(options.skipCache ? { skipCache: true } : {}),\n },\n ...options,\n });\n}\n\nexport async function getChatApi(\n chatKey: string,\n options: ApiClientOptions\n): Promise<ApiResponse<ChatResponse>> {\n return browserApiRequest(`/api/v1/chats/${chatKey}`, {\n method: \"GET\",\n ...options,\n });\n}\n\nexport async function updateChatApi(\n chatKey: string,\n updates: { title?: string; pinned?: boolean },\n options: ApiClientOptions\n): Promise<ApiResponse<UpdateChatResponse>> {\n return browserApiRequest(`/api/v1/chats/${chatKey}`, {\n method: \"PATCH\",\n body: updates,\n ...options,\n });\n}\n\nexport async function deleteChatApi(\n chatKey: string,\n options: ApiClientOptions\n): Promise<ApiResponse<{ success: boolean; metadata: ResponseMetadata }>> {\n return browserApiRequest(`/api/v1/chats/${chatKey}`, {\n method: \"DELETE\",\n ...options,\n });\n}\n\n// =============================================================================\n// ACTIVE CHATS\n// =============================================================================\n\nexport async function getActiveChatsCountApi(\n options: ApiClientOptions\n): Promise<ApiResponse<ActiveChatsCountResponse>> {\n return browserApiRequest(\"/api/v1/chats/active/count\", {\n method: \"GET\",\n ...options,\n });\n}\n\nexport async function getActiveChatsApi(\n options: ApiClientOptions & { pastHours?: number }\n): Promise<ApiResponse<ActiveChatsResponse>> {\n const params = new URLSearchParams();\n if (options.pastHours) params.set(\"pastHours\", String(options.pastHours));\n const queryString = params.toString();\n return browserApiRequest(`/api/v1/chats/active${queryString ? `?${queryString}` : \"\"}`, {\n method: \"GET\",\n ...options,\n });\n}\n\n// =============================================================================\n// PROJECT CHATS\n// =============================================================================\n\nexport async function updateProjectChatTitleApi(\n projectId: string,\n chatKey: string,\n title: string,\n options: ApiClientOptions\n): Promise<ApiResponse<{ success: boolean; metadata: ResponseMetadata }>> {\n return browserApiRequest(`/api/v1/projects/${projectId}/chats/${chatKey}/title`, {\n method: \"PUT\",\n body: { title },\n ...options,\n });\n}\n\nexport async function addChatToProjectApi(\n projectId: string,\n chatKey: string,\n options: ApiClientOptions\n): Promise<ApiResponse<{ success: boolean; metadata: ResponseMetadata }>> {\n return browserApiRequest(`/api/v1/projects/${projectId}/chats`, {\n method: \"POST\",\n body: { chatKey },\n ...options,\n });\n}\n\n// =============================================================================\n// WAITING CHATS\n// =============================================================================\n\nexport async function getWaitingChatsCountApi(\n options: ApiClientOptions\n): Promise<ApiResponse<WaitingChatsCountResponse>> {\n return browserApiRequest(\"/api/v1/chats/waiting/count\", {\n method: \"GET\",\n ...options,\n });\n}\n\nexport async function getChatsByUserApi(\n userEmail: string,\n options: ApiClientOptions\n): Promise<ApiResponse<ChatHistoryResponse>> {\n return browserApiRequest(`/api/v1/chats/user/${encodeURIComponent(userEmail)}`, {\n method: \"GET\",\n ...options,\n });\n}\n\n// =============================================================================\n// QUEUES\n// =============================================================================\n\nexport async function listQueuesApi(\n options: ApiClientOptions\n): Promise<ApiResponse<QueueResponse>> {\n return browserApiRequest(\"/api/v1/queues\", {\n method: \"GET\",\n ...options,\n });\n}\n\n// =============================================================================\n// AGENT SESSIONS\n// =============================================================================\n\nexport async function getOnlineSessionsApi(\n options: ApiClientOptions\n): Promise<ApiResponse<OnlineSessionsResponse>> {\n return browserApiRequest(\"/api/v1/agents/sessions/online\", {\n method: \"GET\",\n ...options,\n });\n}\n\nexport async function getAgentSessionApi(\n agentId: string,\n options: ApiClientOptions\n): Promise<ApiResponse<AgentSessionResponse>> {\n return browserApiRequest(`/api/v1/agents/sessions/${agentId}`, {\n method: \"GET\",\n ...options,\n });\n}\n\n// =============================================================================\n// MEMORY\n// =============================================================================\n\nexport {\n getProfileApi,\n patchProfileApi,\n replaceProfileApi,\n clearProfileApi,\n deleteContactApi,\n deleteNoteApi,\n getSummaryApi,\n clearSummaryApi,\n regenerateSummaryApi,\n} from \"./memory\";\n\nexport type { SummaryView } from \"./memory\";\n","/**\n * Stream API\n *\n * Low-level functions for chat streaming operations.\n * These are used internally by the transport layer but can also\n * be used directly for custom implementations.\n *\n * @example\n * ```typescript\n * import { createChat, sendChatMessage, loadChat } from \"@elqnt/chat/api\";\n *\n * // Create a new chat\n * const chatKey = await createChat({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * userId: \"user-456\",\n * });\n *\n * // Send a message\n * await sendChatMessage({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey,\n * userId: \"user-456\",\n * content: \"Hello!\",\n * });\n * ```\n */\n\nimport type { Chat, ChatMessage } from \"../models\";\n\n/**\n * Base options for all stream API calls\n */\nexport interface StreamApiOptions {\n /** Base URL for the chat server */\n baseUrl: string;\n /** Organization ID */\n orgId: string;\n /** User ID */\n userId: string;\n /**\n * Tier-2 signed token (REQUIRED in production). The chat service authorizes\n * the request from this token and derives orgId/userId/product from its\n * claims (the body orgId/userId are not trusted for authorization). Pass a\n * static `token`, or a `getToken` provider that is awaited per call so\n * short-lived tokens refresh. See SKILL.md and @elqnt/api-client/server's\n * generateServerToken for the claim shape.\n */\n token?: string;\n /** Token provider, awaited per call. Takes precedence over `token`. */\n getToken?: () => Promise<string | null | undefined> | string | null | undefined;\n}\n\n/** Resolve a per-call token from a static value or provider (never throws). */\nasync function resolveToken(opts: {\n token?: string;\n getToken?: StreamApiOptions[\"getToken\"];\n}): Promise<string | undefined> {\n if (opts.getToken) {\n try {\n return (await opts.getToken()) ?? undefined;\n } catch {\n return undefined;\n }\n }\n return opts.token ?? undefined;\n}\n\n/**\n * Response from stream API calls\n */\nexport interface StreamApiResponse<T = unknown> {\n success: boolean;\n data?: T;\n error?: string;\n}\n\n/**\n * Create chat options\n */\nexport interface CreateChatApiOptions extends StreamApiOptions {\n /** Optional metadata for the new chat */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Create chat response data\n */\nexport interface CreateChatResponseData {\n chatKey: string;\n}\n\n/**\n * Send message options\n */\nexport interface SendMessageApiOptions extends StreamApiOptions {\n /** Chat key */\n chatKey: string;\n /** Message content */\n content: string;\n /** Optional attachments */\n attachments?: unknown[];\n /** Optional message metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Load chat options\n */\nexport interface LoadChatApiOptions extends StreamApiOptions {\n /** Chat key to load */\n chatKey: string;\n}\n\n/**\n * Load chat response data\n */\nexport interface LoadChatResponseData {\n chat: Chat;\n}\n\n/**\n * End chat options\n */\nexport interface EndChatApiOptions extends StreamApiOptions {\n /** Chat key to end */\n chatKey: string;\n /** Optional end reason */\n reason?: string;\n}\n\n/**\n * Typing indicator options\n */\nexport interface TypingApiOptions extends StreamApiOptions {\n /** Chat key */\n chatKey: string;\n /** Whether user is typing */\n typing: boolean;\n}\n\n/**\n * Generic event options\n */\nexport interface SendEventApiOptions extends StreamApiOptions {\n /** Chat key */\n chatKey: string;\n /** Event type */\n type: string;\n /** Event data */\n data?: Record<string, unknown>;\n}\n\n/**\n * Internal fetch helper\n */\nasync function streamFetch<T>(\n url: string,\n body: Record<string, unknown>,\n token?: string\n): Promise<StreamApiResponse<T>> {\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (token) headers[\"Authorization\"] = `Bearer ${token}`;\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return {\n success: false,\n error: `API error: ${response.status} - ${errorText}`,\n };\n }\n\n const data = await response.json();\n return { success: true, data };\n}\n\n/**\n * Create a new chat session\n *\n * @param options - Create chat options\n * @returns Chat key of the created chat\n *\n * @example\n * ```typescript\n * const chatKey = await createChat({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * userId: \"user-456\",\n * metadata: { source: \"website\" },\n * });\n * ```\n */\nexport async function createChat(\n options: CreateChatApiOptions\n): Promise<string> {\n const { baseUrl, orgId, userId, metadata } = options;\n const token = await resolveToken(options);\n\n const result = await streamFetch<CreateChatResponseData>(\n `${baseUrl}/create`,\n { orgId, userId, metadata },\n token\n );\n\n if (!result.success || !result.data?.chatKey) {\n throw new Error(result.error || \"Failed to create chat\");\n }\n\n return result.data.chatKey;\n}\n\n/**\n * Send a message in a chat\n *\n * @param options - Send message options\n *\n * @example\n * ```typescript\n * await sendChatMessage({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * content: \"Hello!\",\n * });\n * ```\n */\nexport async function sendChatMessage(\n options: SendMessageApiOptions\n): Promise<void> {\n const { baseUrl, orgId, chatKey, userId, content, attachments, metadata } =\n options;\n const token = await resolveToken(options);\n\n const message: Partial<ChatMessage> = {\n id: `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`,\n role: \"user\",\n content,\n time: Date.now(),\n status: \"sending\",\n senderId: userId,\n createdAt: Date.now(),\n attachments: attachments as ChatMessage[\"attachments\"],\n };\n\n const result = await streamFetch(`${baseUrl}/send`, {\n orgId,\n chatKey,\n userId,\n message,\n metadata,\n }, token);\n\n if (!result.success) {\n throw new Error(result.error || \"Failed to send message\");\n }\n}\n\n/**\n * Load an existing chat\n *\n * @param options - Load chat options\n * @returns The loaded chat object\n *\n * @example\n * ```typescript\n * const chat = await loadChat({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * });\n * ```\n */\nexport async function loadChat(options: LoadChatApiOptions): Promise<Chat> {\n const { baseUrl, orgId, chatKey, userId } = options;\n const token = await resolveToken(options);\n\n const result = await streamFetch<LoadChatResponseData>(`${baseUrl}/load`, {\n orgId,\n chatKey,\n userId,\n }, token);\n\n if (!result.success || !result.data?.chat) {\n throw new Error(result.error || \"Failed to load chat\");\n }\n\n return result.data.chat;\n}\n\n/**\n * End a chat session\n *\n * @param options - End chat options\n *\n * @example\n * ```typescript\n * await endChat({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * reason: \"User closed chat\",\n * });\n * ```\n */\nexport async function endChat(options: EndChatApiOptions): Promise<void> {\n const { baseUrl, orgId, chatKey, userId, reason } = options;\n const token = await resolveToken(options);\n\n const result = await streamFetch(`${baseUrl}/end`, {\n orgId,\n chatKey,\n userId,\n data: reason ? { reason } : undefined,\n }, token);\n\n if (!result.success) {\n throw new Error(result.error || \"Failed to end chat\");\n }\n}\n\n/**\n * Send typing indicator\n *\n * @param options - Typing indicator options\n *\n * @example\n * ```typescript\n * // User started typing\n * await sendTypingIndicator({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * typing: true,\n * });\n *\n * // User stopped typing\n * await sendTypingIndicator({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * typing: false,\n * });\n * ```\n */\nexport async function sendTypingIndicator(\n options: TypingApiOptions\n): Promise<void> {\n const { baseUrl, orgId, chatKey, userId, typing } = options;\n const token = await resolveToken(options);\n\n const result = await streamFetch(`${baseUrl}/typing`, {\n orgId,\n chatKey,\n userId,\n typing,\n }, token);\n\n if (!result.success) {\n throw new Error(result.error || \"Failed to send typing indicator\");\n }\n}\n\n/**\n * Send a generic event\n *\n * @param options - Event options\n *\n * @example\n * ```typescript\n * await sendEvent({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * type: \"skill_activate\",\n * data: { skillId: \"research\" },\n * });\n * ```\n */\nexport async function sendEvent(options: SendEventApiOptions): Promise<void> {\n const { baseUrl, orgId, chatKey, userId, type, data } = options;\n const token = await resolveToken(options);\n\n const result = await streamFetch(`${baseUrl}/event`, {\n type,\n orgId,\n chatKey,\n userId,\n data,\n }, token);\n\n if (!result.success) {\n throw new Error(result.error || \"Failed to send event\");\n }\n}\n","import { browserApiRequest } from \"@elqnt/api-client/browser\";\nimport type { ApiClientOptions, ApiResponse } from \"@elqnt/api-client\";\nimport type { MemoryProfile } from \"../models\";\n\nexport interface SummaryView {\n chatKey: string;\n text: string;\n updatedAt: string;\n}\n\nexport const getProfileApi = (o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(\"/api/v1/memory/profile\", { method: \"GET\", ...o });\n\nexport const patchProfileApi = (patch: Partial<MemoryProfile>, o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(\"/api/v1/memory/profile\", { method: \"PATCH\", body: patch, ...o });\n\nexport const replaceProfileApi = (p: MemoryProfile, o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(\"/api/v1/memory/profile\", { method: \"PUT\", body: p, ...o });\n\nexport const clearProfileApi = (o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(\"/api/v1/memory/profile\", { method: \"DELETE\", ...o });\n\nexport const deleteContactApi = (name: string, o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(`/api/v1/memory/profile/contacts/${encodeURIComponent(name)}`, { method: \"DELETE\", ...o });\n\nexport const deleteNoteApi = (index: number, o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(`/api/v1/memory/profile/notes/${index}`, { method: \"DELETE\", ...o });\n\nexport const getSummaryApi = (chatKey: string, o: ApiClientOptions): Promise<ApiResponse<SummaryView>> =>\n browserApiRequest(`/api/v1/chats/${chatKey}/summary`, { method: \"GET\", ...o });\n\nexport const clearSummaryApi = (chatKey: string, o: ApiClientOptions): Promise<ApiResponse<void>> =>\n browserApiRequest(`/api/v1/chats/${chatKey}/summary`, { method: \"DELETE\", ...o });\n\nexport const regenerateSummaryApi = (chatKey: string, o: ApiClientOptions): Promise<ApiResponse<SummaryView>> =>\n browserApiRequest(`/api/v1/chats/${chatKey}/summary/regenerate`, { method: \"POST\", ...o });\n"],"mappings":"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","names":["import_browser"]}
@@ -1,13 +1,23 @@
1
- "use client";
2
-
3
1
  // api/index.ts
4
2
  import { browserApiRequest as browserApiRequest2 } from "@elqnt/api-client/browser";
5
3
 
6
4
  // api/stream-api.ts
7
- async function streamFetch(url, body) {
5
+ async function resolveToken(opts) {
6
+ if (opts.getToken) {
7
+ try {
8
+ return await opts.getToken() ?? void 0;
9
+ } catch {
10
+ return void 0;
11
+ }
12
+ }
13
+ return opts.token ?? void 0;
14
+ }
15
+ async function streamFetch(url, body, token) {
16
+ const headers = { "Content-Type": "application/json" };
17
+ if (token) headers["Authorization"] = `Bearer ${token}`;
8
18
  const response = await fetch(url, {
9
19
  method: "POST",
10
- headers: { "Content-Type": "application/json" },
20
+ headers,
11
21
  body: JSON.stringify(body)
12
22
  });
13
23
  if (!response.ok) {
@@ -22,9 +32,11 @@ async function streamFetch(url, body) {
22
32
  }
23
33
  async function createChat(options) {
24
34
  const { baseUrl, orgId, userId, metadata } = options;
35
+ const token = await resolveToken(options);
25
36
  const result = await streamFetch(
26
37
  `${baseUrl}/create`,
27
- { orgId, userId, metadata }
38
+ { orgId, userId, metadata },
39
+ token
28
40
  );
29
41
  if (!result.success || !result.data?.chatKey) {
30
42
  throw new Error(result.error || "Failed to create chat");
@@ -33,6 +45,7 @@ async function createChat(options) {
33
45
  }
34
46
  async function sendChatMessage(options) {
35
47
  const { baseUrl, orgId, chatKey, userId, content, attachments, metadata } = options;
48
+ const token = await resolveToken(options);
36
49
  const message = {
37
50
  id: `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`,
38
51
  role: "user",
@@ -49,18 +62,19 @@ async function sendChatMessage(options) {
49
62
  userId,
50
63
  message,
51
64
  metadata
52
- });
65
+ }, token);
53
66
  if (!result.success) {
54
67
  throw new Error(result.error || "Failed to send message");
55
68
  }
56
69
  }
57
70
  async function loadChat(options) {
58
71
  const { baseUrl, orgId, chatKey, userId } = options;
72
+ const token = await resolveToken(options);
59
73
  const result = await streamFetch(`${baseUrl}/load`, {
60
74
  orgId,
61
75
  chatKey,
62
76
  userId
63
- });
77
+ }, token);
64
78
  if (!result.success || !result.data?.chat) {
65
79
  throw new Error(result.error || "Failed to load chat");
66
80
  }
@@ -68,37 +82,40 @@ async function loadChat(options) {
68
82
  }
69
83
  async function endChat(options) {
70
84
  const { baseUrl, orgId, chatKey, userId, reason } = options;
85
+ const token = await resolveToken(options);
71
86
  const result = await streamFetch(`${baseUrl}/end`, {
72
87
  orgId,
73
88
  chatKey,
74
89
  userId,
75
90
  data: reason ? { reason } : void 0
76
- });
91
+ }, token);
77
92
  if (!result.success) {
78
93
  throw new Error(result.error || "Failed to end chat");
79
94
  }
80
95
  }
81
96
  async function sendTypingIndicator(options) {
82
97
  const { baseUrl, orgId, chatKey, userId, typing } = options;
98
+ const token = await resolveToken(options);
83
99
  const result = await streamFetch(`${baseUrl}/typing`, {
84
100
  orgId,
85
101
  chatKey,
86
102
  userId,
87
103
  typing
88
- });
104
+ }, token);
89
105
  if (!result.success) {
90
106
  throw new Error(result.error || "Failed to send typing indicator");
91
107
  }
92
108
  }
93
109
  async function sendEvent(options) {
94
110
  const { baseUrl, orgId, chatKey, userId, type, data } = options;
111
+ const token = await resolveToken(options);
95
112
  const result = await streamFetch(`${baseUrl}/event`, {
96
113
  type,
97
114
  orgId,
98
115
  chatKey,
99
116
  userId,
100
117
  data
101
- });
118
+ }, token);
102
119
  if (!result.success) {
103
120
  throw new Error(result.error || "Failed to send event");
104
121
  }