@usepanacea/client 0.1.4 → 0.1.6
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/index.cjs +14 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -185,6 +185,19 @@ async function postEscalate(sessionId, reason, token, apiBase) {
|
|
|
185
185
|
if (!res.ok) return null;
|
|
186
186
|
return res.json();
|
|
187
187
|
}
|
|
188
|
+
async function createSession(token, apiBase, opts = {}) {
|
|
189
|
+
const res = await fetch(`${apiBase}/api/v1/sessions`, {
|
|
190
|
+
method: "POST",
|
|
191
|
+
headers: {
|
|
192
|
+
"Content-Type": "application/json",
|
|
193
|
+
Authorization: `Bearer ${token}`
|
|
194
|
+
},
|
|
195
|
+
body: JSON.stringify(opts)
|
|
196
|
+
});
|
|
197
|
+
if (!res.ok) return null;
|
|
198
|
+
const data = await res.json();
|
|
199
|
+
return data.sessionId ?? null;
|
|
200
|
+
}
|
|
188
201
|
|
|
189
202
|
// src/pageContext.ts
|
|
190
203
|
function collectPageContext() {
|
|
@@ -244,6 +257,7 @@ function onPageNavigate(callback) {
|
|
|
244
257
|
|
|
245
258
|
exports.TokenManager = TokenManager;
|
|
246
259
|
exports.collectPageContext = collectPageContext;
|
|
260
|
+
exports.createSession = createSession;
|
|
247
261
|
exports.fetchLiveMessages = fetchLiveMessages;
|
|
248
262
|
exports.fetchRecentSessionId = fetchRecentSessionId;
|
|
249
263
|
exports.fetchSessionTurns = fetchSessionTurns;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/token.ts","../src/api.ts","../src/pageContext.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["interface TokenState {\n token: string;\n /** unix ms */\n expiresAt: number;\n}\n\n/**\n * Manages the short-lived widget JWT (15 min TTL).\n * Auto-refreshes 60 s before expiry. Concurrent callers share one in-flight\n * refresh promise so the token is only fetched once even under parallel requests.\n */\nexport class TokenManager {\n private state: TokenState | null = null;\n private refreshPromise: Promise<string> | null = null;\n\n constructor(\n private readonly tenantId: string,\n private readonly apiBase: string,\n ) {}\n\n async getToken(): Promise<string> {\n if (!this.state || Date.now() >= this.state.expiresAt - 60_000) {\n if (!this.refreshPromise) {\n this.refreshPromise = this.refresh().finally(() => {\n this.refreshPromise = null;\n });\n }\n return this.refreshPromise;\n }\n return this.state.token;\n }\n\n private async refresh(): Promise<string> {\n const res = await fetch(`${this.apiBase}/api/v1/auth/widget-token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ tenantId: this.tenantId }),\n });\n\n if (!res.ok) {\n throw new Error(`Panacea: token refresh failed (${res.status})`);\n }\n\n const data = (await res.json()) as { token: string; expiresAt: string };\n this.state = {\n token: data.token,\n expiresAt: new Date(data.expiresAt).getTime(),\n };\n return this.state.token;\n }\n}\n","import type { QueryResponse, HistoryTurn, LiveMessage } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Query\n// ---------------------------------------------------------------------------\n\ninterface QueryOptions {\n question: string;\n sessionId: string | null;\n token: string;\n apiBase: string;\n /** Extra headers to include (e.g. x-customer-token). */\n extraHeaders?: Record<string, string>;\n /** Current page context — URL, title, headings, visible content. Forwarded\n * to the AI agent so it can answer page-aware questions. */\n pageContext?: Record<string, unknown>;\n}\n\n/**\n * Send a query via REST POST /api/v1/query.\n * Used as fallback when SSE is not available.\n */\nexport async function queryRest(opts: QueryOptions): Promise<QueryResponse> {\n const res = await fetch(`${opts.apiBase}/api/v1/query`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${opts.token}`,\n ...opts.extraHeaders,\n },\n body: JSON.stringify({\n question: opts.question,\n sessionId: opts.sessionId,\n pageContext: opts.pageContext,\n }),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => null);\n throw new Error(\n (err as { error?: { message?: string } })?.error?.message ??\n `Panacea: query failed (${res.status})`,\n );\n }\n\n return res.json() as Promise<QueryResponse>;\n}\n\n/**\n * Send a query via Server-Sent Events streaming.\n * Calls `onToken` with each streamed delta, then resolves with the final response.\n */\nexport async function querySSE(\n opts: QueryOptions,\n onToken: (delta: string) => void,\n): Promise<QueryResponse> {\n const res = await fetch(`${opts.apiBase}/api/v1/query`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${opts.token}`,\n Accept: \"text/event-stream\",\n ...opts.extraHeaders,\n },\n body: JSON.stringify({\n question: opts.question,\n sessionId: opts.sessionId,\n pageContext: opts.pageContext,\n }),\n });\n\n if (!res.ok || !res.body) {\n throw new Error(`Panacea: SSE query failed (${res.status})`);\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let finalResponse: QueryResponse | null = null;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue;\n const raw = line.slice(6).trim();\n if (!raw || raw === \"[DONE]\") continue;\n try {\n const frame = JSON.parse(raw) as {\n type: \"token\" | \"done\" | \"error\";\n delta?: string;\n answer?: string;\n sources?: QueryResponse[\"sources\"];\n confidence?: number;\n sessionId?: string;\n flaggedForReview?: boolean;\n escalated?: boolean;\n message?: string;\n };\n if (frame.type === \"token\" && frame.delta) {\n onToken(frame.delta);\n } else if (frame.type === \"done\") {\n finalResponse = {\n answer: frame.answer ?? \"\",\n sources: frame.sources ?? [],\n confidence: frame.confidence ?? 0,\n flaggedForReview: frame.flaggedForReview ?? false,\n sessionId: frame.sessionId ?? \"\",\n escalated: frame.escalated,\n };\n } else if (frame.type === \"error\") {\n throw new Error(frame.message ?? \"SSE stream error\");\n }\n } catch {\n // ignore malformed frames\n }\n }\n }\n\n if (!finalResponse) throw new Error(\"Panacea: SSE stream ended without done frame\");\n return finalResponse;\n}\n\n// ---------------------------------------------------------------------------\n// Session helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Fetch the most recent resumable session for an identified customer.\n * Returns the sessionId if found within the resume window, null otherwise.\n */\nexport async function fetchRecentSessionId(\n customerToken: string,\n widgetToken: string,\n apiBase: string,\n): Promise<string | null> {\n const url = new URL(`${apiBase}/api/v1/sessions/resume`);\n url.searchParams.set(\"customerToken\", customerToken);\n\n const res = await fetch(url.toString(), {\n headers: { Authorization: `Bearer ${widgetToken}` },\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as { session: { sessionId: string } | null };\n return data?.session?.sessionId ?? null;\n}\n\n/**\n * Fetch all turns for a session (read-only history view).\n */\nexport async function fetchSessionTurns(\n sessionId: string,\n widgetToken: string,\n apiBase: string,\n): Promise<HistoryTurn[]> {\n const res = await fetch(`${apiBase}/api/v1/sessions/${sessionId}/turns`, {\n headers: { Authorization: `Bearer ${widgetToken}` },\n });\n\n if (!res.ok) return [];\n\n const data = (await res.json()) as {\n data?: { turns?: HistoryTurn[] };\n turns?: HistoryTurn[];\n };\n return data?.data?.turns ?? data?.turns ?? [];\n}\n\n// ---------------------------------------------------------------------------\n// Reaction\n// ---------------------------------------------------------------------------\n\n/**\n * POST a reaction (thumbs up/down) for a turn.\n * D-013: explicit reaction capture via PATCH /api/v1/sessions/:id/reaction.\n */\nexport async function postReaction(\n sessionId: string,\n reaction: \"helpful\" | \"unhelpful\",\n citedPaths: string[],\n token: string,\n apiBase: string,\n): Promise<void> {\n await fetch(`${apiBase}/api/v1/sessions/${sessionId}/reaction`, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ reaction, citedPaths }),\n });\n}\n\n// ---------------------------------------------------------------------------\n// Live handover helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Send a customer message during a live handover session.\n */\nexport async function postCustomerMessage(\n escalationId: string,\n content: string,\n token: string,\n apiBase: string,\n): Promise<void> {\n await fetch(`${apiBase}/api/v1/inbox/${escalationId}/customer-message`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ content }),\n });\n}\n\n/**\n * Fetch live messages from a claimed escalation (human agent replies).\n * Used to poll for new messages during live handover.\n */\nexport async function fetchLiveMessages(\n escalationId: string,\n token: string,\n apiBase: string,\n): Promise<LiveMessage[]> {\n const res = await fetch(`${apiBase}/api/v1/inbox/${escalationId}/messages`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!res.ok) return [];\n\n const data = (await res.json()) as {\n data?: { messages?: LiveMessage[] };\n messages?: LiveMessage[];\n };\n return data?.data?.messages ?? data?.messages ?? [];\n}\n\n// ---------------------------------------------------------------------------\n// Page context\n// ---------------------------------------------------------------------------\n\n/**\n * Update the pageContext for a session (SPA navigation support).\n */\nexport async function patchPageContext(\n sessionId: string,\n ctx: Record<string, unknown>,\n token: string,\n apiBase: string,\n): Promise<void> {\n await fetch(`${apiBase}/api/v1/sessions/${sessionId}`, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ pageContext: ctx }),\n });\n}\n\n/**\n * Trigger escalation to a human agent.\n * Returns the raw JSON response or null on failure.\n */\nexport async function postEscalate(\n sessionId: string,\n reason: string,\n token: string,\n apiBase: string,\n): Promise<unknown> {\n const res = await fetch(`${apiBase}/api/v1/sessions/${sessionId}/escalate`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ reason }),\n });\n if (!res.ok) return null;\n return res.json();\n}\n","/**\n * Collects page context from the current browser document.\n * Reads URL, title, meta description, headings, and visible body content.\n *\n * Host apps can annotate any element with `data-panacea-content` to include\n * its text in the context, and set `data-panacea-summary=\"...\"` on any element\n * to supply a structured one-line summary (takes highest priority).\n *\n * Safe to call in any browser context — never throws, always returns an object.\n * Used by the widget (auto-collection) and optionally by host apps.\n */\nexport function collectPageContext(): Record<string, unknown> {\n const ctx: Record<string, unknown> = {\n url: window.location.href,\n title: document.title,\n };\n\n // Meta description\n const metaDesc = document.querySelector<HTMLMetaElement>('meta[name=\"description\"]');\n if (metaDesc?.content) ctx.description = metaDesc.content;\n\n // Open-graph title as fallback / additional context\n const ogTitle = document.querySelector<HTMLMetaElement>('meta[property=\"og:title\"]');\n if (ogTitle?.content && ogTitle.content !== document.title) ctx.ogTitle = ogTitle.content;\n\n const root =\n document.querySelector(\"main\") ?? document.querySelector('[role=\"main\"]') ?? document.body;\n\n // Visible headings from the main content area (h1 + h2, max 6)\n const headings = Array.from(root.querySelectorAll(\"h1, h2\"))\n .map((h) => h.textContent?.trim())\n .filter((t): t is string => Boolean(t) && t.length > 0)\n .slice(0, 6);\n if (headings.length) ctx.headings = headings;\n\n // Breadcrumb trail if present\n const breadcrumbEl = document.querySelector(\n '[aria-label=\"breadcrumb\"], [aria-label=\"Breadcrumb\"], nav[aria-label*=\"read\"]',\n );\n if (breadcrumbEl) {\n const crumb = breadcrumbEl.textContent?.replace(/\\s+/g, \" \").trim();\n if (crumb) ctx.breadcrumb = crumb;\n }\n\n // Explicit summary annotations — host app can tag any element with\n // data-panacea-summary to provide a concise, structured description.\n const summaryEls = Array.from(document.querySelectorAll(\"[data-panacea-summary]\"))\n .map((el) => el.getAttribute(\"data-panacea-summary\")?.trim())\n .filter((s): s is string => Boolean(s));\n if (summaryEls.length) ctx.summaries = summaryEls;\n\n // Explicit content annotations — any element tagged data-panacea-content\n // has its visible text included verbatim (useful for tables, stat cards, etc.)\n const contentEls = Array.from(document.querySelectorAll(\"[data-panacea-content]\"))\n .map((el) => el.textContent?.replace(/\\s+/g, \" \").trim())\n .filter((t): t is string => Boolean(t) && t.length > 0);\n if (contentEls.length) ctx.annotatedContent = contentEls.slice(0, 10);\n\n // Fallback: extract visible paragraph / list / table text from main area\n // when no explicit annotations are present. Truncated to keep prompt lean.\n if (!contentEls.length) {\n const textNodes = Array.from(\n root.querySelectorAll(\n \"p, li, td, th, [data-slot='card-title'], [class*='card-title'], dt, dd\",\n ),\n )\n .map((el) => el.textContent?.replace(/\\s+/g, \" \").trim())\n .filter((t): t is string => Boolean(t) && t.length > 3 && t.length < 300)\n // Deduplicate\n .filter((t, i, arr) => arr.indexOf(t) === i)\n .slice(0, 20);\n if (textNodes.length) ctx.pageContent = textNodes;\n }\n\n return ctx;\n}\n\n/**\n * Install a lightweight SPA navigation listener.\n * Monkey-patches `history.pushState` / `history.replaceState` and listens to\n * `popstate` to detect client-side route changes.\n *\n * @param callback - Called with fresh page context on every navigation.\n * @returns Cleanup function to remove all listeners.\n */\nexport function onPageNavigate(callback: (ctx: Record<string, unknown>) => void): () => void {\n const fire = () => {\n // Delay so the new page's DOM (title, headings, content) has time to render\n setTimeout(() => callback(collectPageContext()), 250);\n };\n\n window.addEventListener(\"popstate\", fire);\n\n // Patch pushState / replaceState (used by Next.js, React Router, etc.)\n const origPush = history.pushState.bind(history);\n const origReplace = history.replaceState.bind(history);\n\n history.pushState = function (...args) {\n origPush(...args);\n fire();\n };\n history.replaceState = function (...args) {\n origReplace(...args);\n fire();\n };\n\n return () => {\n window.removeEventListener(\"popstate\", fire);\n history.pushState = origPush;\n history.replaceState = origReplace;\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/token.ts","../src/api.ts","../src/pageContext.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["interface TokenState {\n token: string;\n /** unix ms */\n expiresAt: number;\n}\n\n/**\n * Manages the short-lived widget JWT (15 min TTL).\n * Auto-refreshes 60 s before expiry. Concurrent callers share one in-flight\n * refresh promise so the token is only fetched once even under parallel requests.\n */\nexport class TokenManager {\n private state: TokenState | null = null;\n private refreshPromise: Promise<string> | null = null;\n\n constructor(\n private readonly tenantId: string,\n private readonly apiBase: string,\n ) {}\n\n async getToken(): Promise<string> {\n if (!this.state || Date.now() >= this.state.expiresAt - 60_000) {\n if (!this.refreshPromise) {\n this.refreshPromise = this.refresh().finally(() => {\n this.refreshPromise = null;\n });\n }\n return this.refreshPromise;\n }\n return this.state.token;\n }\n\n private async refresh(): Promise<string> {\n const res = await fetch(`${this.apiBase}/api/v1/auth/widget-token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ tenantId: this.tenantId }),\n });\n\n if (!res.ok) {\n throw new Error(`Panacea: token refresh failed (${res.status})`);\n }\n\n const data = (await res.json()) as { token: string; expiresAt: string };\n this.state = {\n token: data.token,\n expiresAt: new Date(data.expiresAt).getTime(),\n };\n return this.state.token;\n }\n}\n","import type { QueryResponse, HistoryTurn, LiveMessage } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Query\n// ---------------------------------------------------------------------------\n\ninterface QueryOptions {\n question: string;\n sessionId: string | null;\n token: string;\n apiBase: string;\n /** Extra headers to include (e.g. x-customer-token). */\n extraHeaders?: Record<string, string>;\n /** Current page context — URL, title, headings, visible content. Forwarded\n * to the AI agent so it can answer page-aware questions. */\n pageContext?: Record<string, unknown>;\n}\n\n/**\n * Send a query via REST POST /api/v1/query.\n * Used as fallback when SSE is not available.\n */\nexport async function queryRest(opts: QueryOptions): Promise<QueryResponse> {\n const res = await fetch(`${opts.apiBase}/api/v1/query`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${opts.token}`,\n ...opts.extraHeaders,\n },\n body: JSON.stringify({\n question: opts.question,\n sessionId: opts.sessionId,\n pageContext: opts.pageContext,\n }),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => null);\n throw new Error(\n (err as { error?: { message?: string } })?.error?.message ??\n `Panacea: query failed (${res.status})`,\n );\n }\n\n return res.json() as Promise<QueryResponse>;\n}\n\n/**\n * Send a query via Server-Sent Events streaming.\n * Calls `onToken` with each streamed delta, then resolves with the final response.\n */\nexport async function querySSE(\n opts: QueryOptions,\n onToken: (delta: string) => void,\n): Promise<QueryResponse> {\n const res = await fetch(`${opts.apiBase}/api/v1/query`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${opts.token}`,\n Accept: \"text/event-stream\",\n ...opts.extraHeaders,\n },\n body: JSON.stringify({\n question: opts.question,\n sessionId: opts.sessionId,\n pageContext: opts.pageContext,\n }),\n });\n\n if (!res.ok || !res.body) {\n throw new Error(`Panacea: SSE query failed (${res.status})`);\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let finalResponse: QueryResponse | null = null;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue;\n const raw = line.slice(6).trim();\n if (!raw || raw === \"[DONE]\") continue;\n try {\n const frame = JSON.parse(raw) as {\n type: \"token\" | \"done\" | \"error\";\n delta?: string;\n answer?: string;\n sources?: QueryResponse[\"sources\"];\n confidence?: number;\n sessionId?: string;\n flaggedForReview?: boolean;\n escalated?: boolean;\n message?: string;\n };\n if (frame.type === \"token\" && frame.delta) {\n onToken(frame.delta);\n } else if (frame.type === \"done\") {\n finalResponse = {\n answer: frame.answer ?? \"\",\n sources: frame.sources ?? [],\n confidence: frame.confidence ?? 0,\n flaggedForReview: frame.flaggedForReview ?? false,\n sessionId: frame.sessionId ?? \"\",\n escalated: frame.escalated,\n };\n } else if (frame.type === \"error\") {\n throw new Error(frame.message ?? \"SSE stream error\");\n }\n } catch {\n // ignore malformed frames\n }\n }\n }\n\n if (!finalResponse) throw new Error(\"Panacea: SSE stream ended without done frame\");\n return finalResponse;\n}\n\n// ---------------------------------------------------------------------------\n// Session helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Fetch the most recent resumable session for an identified customer.\n * Returns the sessionId if found within the resume window, null otherwise.\n */\nexport async function fetchRecentSessionId(\n customerToken: string,\n widgetToken: string,\n apiBase: string,\n): Promise<string | null> {\n const url = new URL(`${apiBase}/api/v1/sessions/resume`);\n url.searchParams.set(\"customerToken\", customerToken);\n\n const res = await fetch(url.toString(), {\n headers: { Authorization: `Bearer ${widgetToken}` },\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as { session: { sessionId: string } | null };\n return data?.session?.sessionId ?? null;\n}\n\n/**\n * Fetch all turns for a session (read-only history view).\n */\nexport async function fetchSessionTurns(\n sessionId: string,\n widgetToken: string,\n apiBase: string,\n): Promise<HistoryTurn[]> {\n const res = await fetch(`${apiBase}/api/v1/sessions/${sessionId}/turns`, {\n headers: { Authorization: `Bearer ${widgetToken}` },\n });\n\n if (!res.ok) return [];\n\n const data = (await res.json()) as {\n data?: { turns?: HistoryTurn[] };\n turns?: HistoryTurn[];\n };\n return data?.data?.turns ?? data?.turns ?? [];\n}\n\n// ---------------------------------------------------------------------------\n// Reaction\n// ---------------------------------------------------------------------------\n\n/**\n * POST a reaction (thumbs up/down) for a turn.\n * D-013: explicit reaction capture via PATCH /api/v1/sessions/:id/reaction.\n */\nexport async function postReaction(\n sessionId: string,\n reaction: \"helpful\" | \"unhelpful\",\n citedPaths: string[],\n token: string,\n apiBase: string,\n): Promise<void> {\n await fetch(`${apiBase}/api/v1/sessions/${sessionId}/reaction`, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ reaction, citedPaths }),\n });\n}\n\n// ---------------------------------------------------------------------------\n// Live handover helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Send a customer message during a live handover session.\n */\nexport async function postCustomerMessage(\n escalationId: string,\n content: string,\n token: string,\n apiBase: string,\n): Promise<void> {\n await fetch(`${apiBase}/api/v1/inbox/${escalationId}/customer-message`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ content }),\n });\n}\n\n/**\n * Fetch live messages from a claimed escalation (human agent replies).\n * Used to poll for new messages during live handover.\n */\nexport async function fetchLiveMessages(\n escalationId: string,\n token: string,\n apiBase: string,\n): Promise<LiveMessage[]> {\n const res = await fetch(`${apiBase}/api/v1/inbox/${escalationId}/messages`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!res.ok) return [];\n\n const data = (await res.json()) as {\n data?: { messages?: LiveMessage[] };\n messages?: LiveMessage[];\n };\n return data?.data?.messages ?? data?.messages ?? [];\n}\n\n// ---------------------------------------------------------------------------\n// Page context\n// ---------------------------------------------------------------------------\n\n/**\n * Update the pageContext for a session (SPA navigation support).\n */\nexport async function patchPageContext(\n sessionId: string,\n ctx: Record<string, unknown>,\n token: string,\n apiBase: string,\n): Promise<void> {\n await fetch(`${apiBase}/api/v1/sessions/${sessionId}`, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ pageContext: ctx }),\n });\n}\n\n/**\n * Trigger escalation to a human agent.\n * Returns the raw JSON response or null on failure.\n */\nexport async function postEscalate(\n sessionId: string,\n reason: string,\n token: string,\n apiBase: string,\n): Promise<unknown> {\n const res = await fetch(`${apiBase}/api/v1/sessions/${sessionId}/escalate`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ reason }),\n });\n if (!res.ok) return null;\n return res.json();\n}\n\n/**\n * Create a new support session without sending a query first.\n * Returns the sessionId or null on failure.\n */\nexport async function createSession(\n token: string,\n apiBase: string,\n opts: { pageContext?: Record<string, unknown> } = {},\n): Promise<string | null> {\n const res = await fetch(`${apiBase}/api/v1/sessions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify(opts),\n });\n if (!res.ok) return null;\n const data = (await res.json()) as { sessionId?: string };\n return data.sessionId ?? null;\n}\n","/**\n * Collects page context from the current browser document.\n * Reads URL, title, meta description, headings, and visible body content.\n *\n * Host apps can annotate any element with `data-panacea-content` to include\n * its text in the context, and set `data-panacea-summary=\"...\"` on any element\n * to supply a structured one-line summary (takes highest priority).\n *\n * Safe to call in any browser context — never throws, always returns an object.\n * Used by the widget (auto-collection) and optionally by host apps.\n */\nexport function collectPageContext(): Record<string, unknown> {\n const ctx: Record<string, unknown> = {\n url: window.location.href,\n title: document.title,\n };\n\n // Meta description\n const metaDesc = document.querySelector<HTMLMetaElement>('meta[name=\"description\"]');\n if (metaDesc?.content) ctx.description = metaDesc.content;\n\n // Open-graph title as fallback / additional context\n const ogTitle = document.querySelector<HTMLMetaElement>('meta[property=\"og:title\"]');\n if (ogTitle?.content && ogTitle.content !== document.title) ctx.ogTitle = ogTitle.content;\n\n const root =\n document.querySelector(\"main\") ?? document.querySelector('[role=\"main\"]') ?? document.body;\n\n // Visible headings from the main content area (h1 + h2, max 6)\n const headings = Array.from(root.querySelectorAll(\"h1, h2\"))\n .map((h) => h.textContent?.trim())\n .filter((t): t is string => Boolean(t) && t.length > 0)\n .slice(0, 6);\n if (headings.length) ctx.headings = headings;\n\n // Breadcrumb trail if present\n const breadcrumbEl = document.querySelector(\n '[aria-label=\"breadcrumb\"], [aria-label=\"Breadcrumb\"], nav[aria-label*=\"read\"]',\n );\n if (breadcrumbEl) {\n const crumb = breadcrumbEl.textContent?.replace(/\\s+/g, \" \").trim();\n if (crumb) ctx.breadcrumb = crumb;\n }\n\n // Explicit summary annotations — host app can tag any element with\n // data-panacea-summary to provide a concise, structured description.\n const summaryEls = Array.from(document.querySelectorAll(\"[data-panacea-summary]\"))\n .map((el) => el.getAttribute(\"data-panacea-summary\")?.trim())\n .filter((s): s is string => Boolean(s));\n if (summaryEls.length) ctx.summaries = summaryEls;\n\n // Explicit content annotations — any element tagged data-panacea-content\n // has its visible text included verbatim (useful for tables, stat cards, etc.)\n const contentEls = Array.from(document.querySelectorAll(\"[data-panacea-content]\"))\n .map((el) => el.textContent?.replace(/\\s+/g, \" \").trim())\n .filter((t): t is string => Boolean(t) && t.length > 0);\n if (contentEls.length) ctx.annotatedContent = contentEls.slice(0, 10);\n\n // Fallback: extract visible paragraph / list / table text from main area\n // when no explicit annotations are present. Truncated to keep prompt lean.\n if (!contentEls.length) {\n const textNodes = Array.from(\n root.querySelectorAll(\n \"p, li, td, th, [data-slot='card-title'], [class*='card-title'], dt, dd\",\n ),\n )\n .map((el) => el.textContent?.replace(/\\s+/g, \" \").trim())\n .filter((t): t is string => Boolean(t) && t.length > 3 && t.length < 300)\n // Deduplicate\n .filter((t, i, arr) => arr.indexOf(t) === i)\n .slice(0, 20);\n if (textNodes.length) ctx.pageContent = textNodes;\n }\n\n return ctx;\n}\n\n/**\n * Install a lightweight SPA navigation listener.\n * Monkey-patches `history.pushState` / `history.replaceState` and listens to\n * `popstate` to detect client-side route changes.\n *\n * @param callback - Called with fresh page context on every navigation.\n * @returns Cleanup function to remove all listeners.\n */\nexport function onPageNavigate(callback: (ctx: Record<string, unknown>) => void): () => void {\n const fire = () => {\n // Delay so the new page's DOM (title, headings, content) has time to render\n setTimeout(() => callback(collectPageContext()), 250);\n };\n\n window.addEventListener(\"popstate\", fire);\n\n // Patch pushState / replaceState (used by Next.js, React Router, etc.)\n const origPush = history.pushState.bind(history);\n const origReplace = history.replaceState.bind(history);\n\n history.pushState = function (...args) {\n origPush(...args);\n fire();\n };\n history.replaceState = function (...args) {\n origReplace(...args);\n fire();\n };\n\n return () => {\n window.removeEventListener(\"popstate\", fire);\n history.pushState = origPush;\n history.replaceState = origReplace;\n };\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -28,6 +28,8 @@ interface Turn {
|
|
|
28
28
|
sources?: Source[];
|
|
29
29
|
flaggedForReview?: boolean;
|
|
30
30
|
reaction?: "helpful" | "unhelpful" | null;
|
|
31
|
+
/** ISO timestamp when this turn was created */
|
|
32
|
+
timestamp?: string;
|
|
31
33
|
/** True when this answer triggered an auto-escalation */
|
|
32
34
|
escalated?: boolean;
|
|
33
35
|
/** True when this message came from a live human agent */
|
|
@@ -130,6 +132,13 @@ declare function patchPageContext(sessionId: string, ctx: Record<string, unknown
|
|
|
130
132
|
* Returns the raw JSON response or null on failure.
|
|
131
133
|
*/
|
|
132
134
|
declare function postEscalate(sessionId: string, reason: string, token: string, apiBase: string): Promise<unknown>;
|
|
135
|
+
/**
|
|
136
|
+
* Create a new support session without sending a query first.
|
|
137
|
+
* Returns the sessionId or null on failure.
|
|
138
|
+
*/
|
|
139
|
+
declare function createSession(token: string, apiBase: string, opts?: {
|
|
140
|
+
pageContext?: Record<string, unknown>;
|
|
141
|
+
}): Promise<string | null>;
|
|
133
142
|
|
|
134
143
|
/**
|
|
135
144
|
* Collects page context from the current browser document.
|
|
@@ -153,4 +162,4 @@ declare function collectPageContext(): Record<string, unknown>;
|
|
|
153
162
|
*/
|
|
154
163
|
declare function onPageNavigate(callback: (ctx: Record<string, unknown>) => void): () => void;
|
|
155
164
|
|
|
156
|
-
export { type EscalateResponse, type HistorySession, type HistoryTurn, type LiveMessage, type QueryResponse, type Source, TokenManager, type Turn, collectPageContext, fetchLiveMessages, fetchRecentSessionId, fetchSessionTurns, onPageNavigate, patchPageContext, postCustomerMessage, postEscalate, postReaction, queryRest, querySSE };
|
|
165
|
+
export { type EscalateResponse, type HistorySession, type HistoryTurn, type LiveMessage, type QueryResponse, type Source, TokenManager, type Turn, collectPageContext, createSession, fetchLiveMessages, fetchRecentSessionId, fetchSessionTurns, onPageNavigate, patchPageContext, postCustomerMessage, postEscalate, postReaction, queryRest, querySSE };
|
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ interface Turn {
|
|
|
28
28
|
sources?: Source[];
|
|
29
29
|
flaggedForReview?: boolean;
|
|
30
30
|
reaction?: "helpful" | "unhelpful" | null;
|
|
31
|
+
/** ISO timestamp when this turn was created */
|
|
32
|
+
timestamp?: string;
|
|
31
33
|
/** True when this answer triggered an auto-escalation */
|
|
32
34
|
escalated?: boolean;
|
|
33
35
|
/** True when this message came from a live human agent */
|
|
@@ -130,6 +132,13 @@ declare function patchPageContext(sessionId: string, ctx: Record<string, unknown
|
|
|
130
132
|
* Returns the raw JSON response or null on failure.
|
|
131
133
|
*/
|
|
132
134
|
declare function postEscalate(sessionId: string, reason: string, token: string, apiBase: string): Promise<unknown>;
|
|
135
|
+
/**
|
|
136
|
+
* Create a new support session without sending a query first.
|
|
137
|
+
* Returns the sessionId or null on failure.
|
|
138
|
+
*/
|
|
139
|
+
declare function createSession(token: string, apiBase: string, opts?: {
|
|
140
|
+
pageContext?: Record<string, unknown>;
|
|
141
|
+
}): Promise<string | null>;
|
|
133
142
|
|
|
134
143
|
/**
|
|
135
144
|
* Collects page context from the current browser document.
|
|
@@ -153,4 +162,4 @@ declare function collectPageContext(): Record<string, unknown>;
|
|
|
153
162
|
*/
|
|
154
163
|
declare function onPageNavigate(callback: (ctx: Record<string, unknown>) => void): () => void;
|
|
155
164
|
|
|
156
|
-
export { type EscalateResponse, type HistorySession, type HistoryTurn, type LiveMessage, type QueryResponse, type Source, TokenManager, type Turn, collectPageContext, fetchLiveMessages, fetchRecentSessionId, fetchSessionTurns, onPageNavigate, patchPageContext, postCustomerMessage, postEscalate, postReaction, queryRest, querySSE };
|
|
165
|
+
export { type EscalateResponse, type HistorySession, type HistoryTurn, type LiveMessage, type QueryResponse, type Source, TokenManager, type Turn, collectPageContext, createSession, fetchLiveMessages, fetchRecentSessionId, fetchSessionTurns, onPageNavigate, patchPageContext, postCustomerMessage, postEscalate, postReaction, queryRest, querySSE };
|
package/dist/index.js
CHANGED
|
@@ -183,6 +183,19 @@ async function postEscalate(sessionId, reason, token, apiBase) {
|
|
|
183
183
|
if (!res.ok) return null;
|
|
184
184
|
return res.json();
|
|
185
185
|
}
|
|
186
|
+
async function createSession(token, apiBase, opts = {}) {
|
|
187
|
+
const res = await fetch(`${apiBase}/api/v1/sessions`, {
|
|
188
|
+
method: "POST",
|
|
189
|
+
headers: {
|
|
190
|
+
"Content-Type": "application/json",
|
|
191
|
+
Authorization: `Bearer ${token}`
|
|
192
|
+
},
|
|
193
|
+
body: JSON.stringify(opts)
|
|
194
|
+
});
|
|
195
|
+
if (!res.ok) return null;
|
|
196
|
+
const data = await res.json();
|
|
197
|
+
return data.sessionId ?? null;
|
|
198
|
+
}
|
|
186
199
|
|
|
187
200
|
// src/pageContext.ts
|
|
188
201
|
function collectPageContext() {
|
|
@@ -240,6 +253,6 @@ function onPageNavigate(callback) {
|
|
|
240
253
|
};
|
|
241
254
|
}
|
|
242
255
|
|
|
243
|
-
export { TokenManager, collectPageContext, fetchLiveMessages, fetchRecentSessionId, fetchSessionTurns, onPageNavigate, patchPageContext, postCustomerMessage, postEscalate, postReaction, queryRest, querySSE };
|
|
256
|
+
export { TokenManager, collectPageContext, createSession, fetchLiveMessages, fetchRecentSessionId, fetchSessionTurns, onPageNavigate, patchPageContext, postCustomerMessage, postEscalate, postReaction, queryRest, querySSE };
|
|
244
257
|
//# sourceMappingURL=index.js.map
|
|
245
258
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/token.ts","../src/api.ts","../src/pageContext.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["interface TokenState {\n token: string;\n /** unix ms */\n expiresAt: number;\n}\n\n/**\n * Manages the short-lived widget JWT (15 min TTL).\n * Auto-refreshes 60 s before expiry. Concurrent callers share one in-flight\n * refresh promise so the token is only fetched once even under parallel requests.\n */\nexport class TokenManager {\n private state: TokenState | null = null;\n private refreshPromise: Promise<string> | null = null;\n\n constructor(\n private readonly tenantId: string,\n private readonly apiBase: string,\n ) {}\n\n async getToken(): Promise<string> {\n if (!this.state || Date.now() >= this.state.expiresAt - 60_000) {\n if (!this.refreshPromise) {\n this.refreshPromise = this.refresh().finally(() => {\n this.refreshPromise = null;\n });\n }\n return this.refreshPromise;\n }\n return this.state.token;\n }\n\n private async refresh(): Promise<string> {\n const res = await fetch(`${this.apiBase}/api/v1/auth/widget-token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ tenantId: this.tenantId }),\n });\n\n if (!res.ok) {\n throw new Error(`Panacea: token refresh failed (${res.status})`);\n }\n\n const data = (await res.json()) as { token: string; expiresAt: string };\n this.state = {\n token: data.token,\n expiresAt: new Date(data.expiresAt).getTime(),\n };\n return this.state.token;\n }\n}\n","import type { QueryResponse, HistoryTurn, LiveMessage } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Query\n// ---------------------------------------------------------------------------\n\ninterface QueryOptions {\n question: string;\n sessionId: string | null;\n token: string;\n apiBase: string;\n /** Extra headers to include (e.g. x-customer-token). */\n extraHeaders?: Record<string, string>;\n /** Current page context — URL, title, headings, visible content. Forwarded\n * to the AI agent so it can answer page-aware questions. */\n pageContext?: Record<string, unknown>;\n}\n\n/**\n * Send a query via REST POST /api/v1/query.\n * Used as fallback when SSE is not available.\n */\nexport async function queryRest(opts: QueryOptions): Promise<QueryResponse> {\n const res = await fetch(`${opts.apiBase}/api/v1/query`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${opts.token}`,\n ...opts.extraHeaders,\n },\n body: JSON.stringify({\n question: opts.question,\n sessionId: opts.sessionId,\n pageContext: opts.pageContext,\n }),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => null);\n throw new Error(\n (err as { error?: { message?: string } })?.error?.message ??\n `Panacea: query failed (${res.status})`,\n );\n }\n\n return res.json() as Promise<QueryResponse>;\n}\n\n/**\n * Send a query via Server-Sent Events streaming.\n * Calls `onToken` with each streamed delta, then resolves with the final response.\n */\nexport async function querySSE(\n opts: QueryOptions,\n onToken: (delta: string) => void,\n): Promise<QueryResponse> {\n const res = await fetch(`${opts.apiBase}/api/v1/query`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${opts.token}`,\n Accept: \"text/event-stream\",\n ...opts.extraHeaders,\n },\n body: JSON.stringify({\n question: opts.question,\n sessionId: opts.sessionId,\n pageContext: opts.pageContext,\n }),\n });\n\n if (!res.ok || !res.body) {\n throw new Error(`Panacea: SSE query failed (${res.status})`);\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let finalResponse: QueryResponse | null = null;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue;\n const raw = line.slice(6).trim();\n if (!raw || raw === \"[DONE]\") continue;\n try {\n const frame = JSON.parse(raw) as {\n type: \"token\" | \"done\" | \"error\";\n delta?: string;\n answer?: string;\n sources?: QueryResponse[\"sources\"];\n confidence?: number;\n sessionId?: string;\n flaggedForReview?: boolean;\n escalated?: boolean;\n message?: string;\n };\n if (frame.type === \"token\" && frame.delta) {\n onToken(frame.delta);\n } else if (frame.type === \"done\") {\n finalResponse = {\n answer: frame.answer ?? \"\",\n sources: frame.sources ?? [],\n confidence: frame.confidence ?? 0,\n flaggedForReview: frame.flaggedForReview ?? false,\n sessionId: frame.sessionId ?? \"\",\n escalated: frame.escalated,\n };\n } else if (frame.type === \"error\") {\n throw new Error(frame.message ?? \"SSE stream error\");\n }\n } catch {\n // ignore malformed frames\n }\n }\n }\n\n if (!finalResponse) throw new Error(\"Panacea: SSE stream ended without done frame\");\n return finalResponse;\n}\n\n// ---------------------------------------------------------------------------\n// Session helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Fetch the most recent resumable session for an identified customer.\n * Returns the sessionId if found within the resume window, null otherwise.\n */\nexport async function fetchRecentSessionId(\n customerToken: string,\n widgetToken: string,\n apiBase: string,\n): Promise<string | null> {\n const url = new URL(`${apiBase}/api/v1/sessions/resume`);\n url.searchParams.set(\"customerToken\", customerToken);\n\n const res = await fetch(url.toString(), {\n headers: { Authorization: `Bearer ${widgetToken}` },\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as { session: { sessionId: string } | null };\n return data?.session?.sessionId ?? null;\n}\n\n/**\n * Fetch all turns for a session (read-only history view).\n */\nexport async function fetchSessionTurns(\n sessionId: string,\n widgetToken: string,\n apiBase: string,\n): Promise<HistoryTurn[]> {\n const res = await fetch(`${apiBase}/api/v1/sessions/${sessionId}/turns`, {\n headers: { Authorization: `Bearer ${widgetToken}` },\n });\n\n if (!res.ok) return [];\n\n const data = (await res.json()) as {\n data?: { turns?: HistoryTurn[] };\n turns?: HistoryTurn[];\n };\n return data?.data?.turns ?? data?.turns ?? [];\n}\n\n// ---------------------------------------------------------------------------\n// Reaction\n// ---------------------------------------------------------------------------\n\n/**\n * POST a reaction (thumbs up/down) for a turn.\n * D-013: explicit reaction capture via PATCH /api/v1/sessions/:id/reaction.\n */\nexport async function postReaction(\n sessionId: string,\n reaction: \"helpful\" | \"unhelpful\",\n citedPaths: string[],\n token: string,\n apiBase: string,\n): Promise<void> {\n await fetch(`${apiBase}/api/v1/sessions/${sessionId}/reaction`, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ reaction, citedPaths }),\n });\n}\n\n// ---------------------------------------------------------------------------\n// Live handover helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Send a customer message during a live handover session.\n */\nexport async function postCustomerMessage(\n escalationId: string,\n content: string,\n token: string,\n apiBase: string,\n): Promise<void> {\n await fetch(`${apiBase}/api/v1/inbox/${escalationId}/customer-message`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ content }),\n });\n}\n\n/**\n * Fetch live messages from a claimed escalation (human agent replies).\n * Used to poll for new messages during live handover.\n */\nexport async function fetchLiveMessages(\n escalationId: string,\n token: string,\n apiBase: string,\n): Promise<LiveMessage[]> {\n const res = await fetch(`${apiBase}/api/v1/inbox/${escalationId}/messages`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!res.ok) return [];\n\n const data = (await res.json()) as {\n data?: { messages?: LiveMessage[] };\n messages?: LiveMessage[];\n };\n return data?.data?.messages ?? data?.messages ?? [];\n}\n\n// ---------------------------------------------------------------------------\n// Page context\n// ---------------------------------------------------------------------------\n\n/**\n * Update the pageContext for a session (SPA navigation support).\n */\nexport async function patchPageContext(\n sessionId: string,\n ctx: Record<string, unknown>,\n token: string,\n apiBase: string,\n): Promise<void> {\n await fetch(`${apiBase}/api/v1/sessions/${sessionId}`, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ pageContext: ctx }),\n });\n}\n\n/**\n * Trigger escalation to a human agent.\n * Returns the raw JSON response or null on failure.\n */\nexport async function postEscalate(\n sessionId: string,\n reason: string,\n token: string,\n apiBase: string,\n): Promise<unknown> {\n const res = await fetch(`${apiBase}/api/v1/sessions/${sessionId}/escalate`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ reason }),\n });\n if (!res.ok) return null;\n return res.json();\n}\n","/**\n * Collects page context from the current browser document.\n * Reads URL, title, meta description, headings, and visible body content.\n *\n * Host apps can annotate any element with `data-panacea-content` to include\n * its text in the context, and set `data-panacea-summary=\"...\"` on any element\n * to supply a structured one-line summary (takes highest priority).\n *\n * Safe to call in any browser context — never throws, always returns an object.\n * Used by the widget (auto-collection) and optionally by host apps.\n */\nexport function collectPageContext(): Record<string, unknown> {\n const ctx: Record<string, unknown> = {\n url: window.location.href,\n title: document.title,\n };\n\n // Meta description\n const metaDesc = document.querySelector<HTMLMetaElement>('meta[name=\"description\"]');\n if (metaDesc?.content) ctx.description = metaDesc.content;\n\n // Open-graph title as fallback / additional context\n const ogTitle = document.querySelector<HTMLMetaElement>('meta[property=\"og:title\"]');\n if (ogTitle?.content && ogTitle.content !== document.title) ctx.ogTitle = ogTitle.content;\n\n const root =\n document.querySelector(\"main\") ?? document.querySelector('[role=\"main\"]') ?? document.body;\n\n // Visible headings from the main content area (h1 + h2, max 6)\n const headings = Array.from(root.querySelectorAll(\"h1, h2\"))\n .map((h) => h.textContent?.trim())\n .filter((t): t is string => Boolean(t) && t.length > 0)\n .slice(0, 6);\n if (headings.length) ctx.headings = headings;\n\n // Breadcrumb trail if present\n const breadcrumbEl = document.querySelector(\n '[aria-label=\"breadcrumb\"], [aria-label=\"Breadcrumb\"], nav[aria-label*=\"read\"]',\n );\n if (breadcrumbEl) {\n const crumb = breadcrumbEl.textContent?.replace(/\\s+/g, \" \").trim();\n if (crumb) ctx.breadcrumb = crumb;\n }\n\n // Explicit summary annotations — host app can tag any element with\n // data-panacea-summary to provide a concise, structured description.\n const summaryEls = Array.from(document.querySelectorAll(\"[data-panacea-summary]\"))\n .map((el) => el.getAttribute(\"data-panacea-summary\")?.trim())\n .filter((s): s is string => Boolean(s));\n if (summaryEls.length) ctx.summaries = summaryEls;\n\n // Explicit content annotations — any element tagged data-panacea-content\n // has its visible text included verbatim (useful for tables, stat cards, etc.)\n const contentEls = Array.from(document.querySelectorAll(\"[data-panacea-content]\"))\n .map((el) => el.textContent?.replace(/\\s+/g, \" \").trim())\n .filter((t): t is string => Boolean(t) && t.length > 0);\n if (contentEls.length) ctx.annotatedContent = contentEls.slice(0, 10);\n\n // Fallback: extract visible paragraph / list / table text from main area\n // when no explicit annotations are present. Truncated to keep prompt lean.\n if (!contentEls.length) {\n const textNodes = Array.from(\n root.querySelectorAll(\n \"p, li, td, th, [data-slot='card-title'], [class*='card-title'], dt, dd\",\n ),\n )\n .map((el) => el.textContent?.replace(/\\s+/g, \" \").trim())\n .filter((t): t is string => Boolean(t) && t.length > 3 && t.length < 300)\n // Deduplicate\n .filter((t, i, arr) => arr.indexOf(t) === i)\n .slice(0, 20);\n if (textNodes.length) ctx.pageContent = textNodes;\n }\n\n return ctx;\n}\n\n/**\n * Install a lightweight SPA navigation listener.\n * Monkey-patches `history.pushState` / `history.replaceState` and listens to\n * `popstate` to detect client-side route changes.\n *\n * @param callback - Called with fresh page context on every navigation.\n * @returns Cleanup function to remove all listeners.\n */\nexport function onPageNavigate(callback: (ctx: Record<string, unknown>) => void): () => void {\n const fire = () => {\n // Delay so the new page's DOM (title, headings, content) has time to render\n setTimeout(() => callback(collectPageContext()), 250);\n };\n\n window.addEventListener(\"popstate\", fire);\n\n // Patch pushState / replaceState (used by Next.js, React Router, etc.)\n const origPush = history.pushState.bind(history);\n const origReplace = history.replaceState.bind(history);\n\n history.pushState = function (...args) {\n origPush(...args);\n fire();\n };\n history.replaceState = function (...args) {\n origReplace(...args);\n fire();\n };\n\n return () => {\n window.removeEventListener(\"popstate\", fire);\n history.pushState = origPush;\n history.replaceState = origReplace;\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/token.ts","../src/api.ts","../src/pageContext.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["interface TokenState {\n token: string;\n /** unix ms */\n expiresAt: number;\n}\n\n/**\n * Manages the short-lived widget JWT (15 min TTL).\n * Auto-refreshes 60 s before expiry. Concurrent callers share one in-flight\n * refresh promise so the token is only fetched once even under parallel requests.\n */\nexport class TokenManager {\n private state: TokenState | null = null;\n private refreshPromise: Promise<string> | null = null;\n\n constructor(\n private readonly tenantId: string,\n private readonly apiBase: string,\n ) {}\n\n async getToken(): Promise<string> {\n if (!this.state || Date.now() >= this.state.expiresAt - 60_000) {\n if (!this.refreshPromise) {\n this.refreshPromise = this.refresh().finally(() => {\n this.refreshPromise = null;\n });\n }\n return this.refreshPromise;\n }\n return this.state.token;\n }\n\n private async refresh(): Promise<string> {\n const res = await fetch(`${this.apiBase}/api/v1/auth/widget-token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ tenantId: this.tenantId }),\n });\n\n if (!res.ok) {\n throw new Error(`Panacea: token refresh failed (${res.status})`);\n }\n\n const data = (await res.json()) as { token: string; expiresAt: string };\n this.state = {\n token: data.token,\n expiresAt: new Date(data.expiresAt).getTime(),\n };\n return this.state.token;\n }\n}\n","import type { QueryResponse, HistoryTurn, LiveMessage } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Query\n// ---------------------------------------------------------------------------\n\ninterface QueryOptions {\n question: string;\n sessionId: string | null;\n token: string;\n apiBase: string;\n /** Extra headers to include (e.g. x-customer-token). */\n extraHeaders?: Record<string, string>;\n /** Current page context — URL, title, headings, visible content. Forwarded\n * to the AI agent so it can answer page-aware questions. */\n pageContext?: Record<string, unknown>;\n}\n\n/**\n * Send a query via REST POST /api/v1/query.\n * Used as fallback when SSE is not available.\n */\nexport async function queryRest(opts: QueryOptions): Promise<QueryResponse> {\n const res = await fetch(`${opts.apiBase}/api/v1/query`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${opts.token}`,\n ...opts.extraHeaders,\n },\n body: JSON.stringify({\n question: opts.question,\n sessionId: opts.sessionId,\n pageContext: opts.pageContext,\n }),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => null);\n throw new Error(\n (err as { error?: { message?: string } })?.error?.message ??\n `Panacea: query failed (${res.status})`,\n );\n }\n\n return res.json() as Promise<QueryResponse>;\n}\n\n/**\n * Send a query via Server-Sent Events streaming.\n * Calls `onToken` with each streamed delta, then resolves with the final response.\n */\nexport async function querySSE(\n opts: QueryOptions,\n onToken: (delta: string) => void,\n): Promise<QueryResponse> {\n const res = await fetch(`${opts.apiBase}/api/v1/query`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${opts.token}`,\n Accept: \"text/event-stream\",\n ...opts.extraHeaders,\n },\n body: JSON.stringify({\n question: opts.question,\n sessionId: opts.sessionId,\n pageContext: opts.pageContext,\n }),\n });\n\n if (!res.ok || !res.body) {\n throw new Error(`Panacea: SSE query failed (${res.status})`);\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let finalResponse: QueryResponse | null = null;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue;\n const raw = line.slice(6).trim();\n if (!raw || raw === \"[DONE]\") continue;\n try {\n const frame = JSON.parse(raw) as {\n type: \"token\" | \"done\" | \"error\";\n delta?: string;\n answer?: string;\n sources?: QueryResponse[\"sources\"];\n confidence?: number;\n sessionId?: string;\n flaggedForReview?: boolean;\n escalated?: boolean;\n message?: string;\n };\n if (frame.type === \"token\" && frame.delta) {\n onToken(frame.delta);\n } else if (frame.type === \"done\") {\n finalResponse = {\n answer: frame.answer ?? \"\",\n sources: frame.sources ?? [],\n confidence: frame.confidence ?? 0,\n flaggedForReview: frame.flaggedForReview ?? false,\n sessionId: frame.sessionId ?? \"\",\n escalated: frame.escalated,\n };\n } else if (frame.type === \"error\") {\n throw new Error(frame.message ?? \"SSE stream error\");\n }\n } catch {\n // ignore malformed frames\n }\n }\n }\n\n if (!finalResponse) throw new Error(\"Panacea: SSE stream ended without done frame\");\n return finalResponse;\n}\n\n// ---------------------------------------------------------------------------\n// Session helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Fetch the most recent resumable session for an identified customer.\n * Returns the sessionId if found within the resume window, null otherwise.\n */\nexport async function fetchRecentSessionId(\n customerToken: string,\n widgetToken: string,\n apiBase: string,\n): Promise<string | null> {\n const url = new URL(`${apiBase}/api/v1/sessions/resume`);\n url.searchParams.set(\"customerToken\", customerToken);\n\n const res = await fetch(url.toString(), {\n headers: { Authorization: `Bearer ${widgetToken}` },\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as { session: { sessionId: string } | null };\n return data?.session?.sessionId ?? null;\n}\n\n/**\n * Fetch all turns for a session (read-only history view).\n */\nexport async function fetchSessionTurns(\n sessionId: string,\n widgetToken: string,\n apiBase: string,\n): Promise<HistoryTurn[]> {\n const res = await fetch(`${apiBase}/api/v1/sessions/${sessionId}/turns`, {\n headers: { Authorization: `Bearer ${widgetToken}` },\n });\n\n if (!res.ok) return [];\n\n const data = (await res.json()) as {\n data?: { turns?: HistoryTurn[] };\n turns?: HistoryTurn[];\n };\n return data?.data?.turns ?? data?.turns ?? [];\n}\n\n// ---------------------------------------------------------------------------\n// Reaction\n// ---------------------------------------------------------------------------\n\n/**\n * POST a reaction (thumbs up/down) for a turn.\n * D-013: explicit reaction capture via PATCH /api/v1/sessions/:id/reaction.\n */\nexport async function postReaction(\n sessionId: string,\n reaction: \"helpful\" | \"unhelpful\",\n citedPaths: string[],\n token: string,\n apiBase: string,\n): Promise<void> {\n await fetch(`${apiBase}/api/v1/sessions/${sessionId}/reaction`, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ reaction, citedPaths }),\n });\n}\n\n// ---------------------------------------------------------------------------\n// Live handover helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Send a customer message during a live handover session.\n */\nexport async function postCustomerMessage(\n escalationId: string,\n content: string,\n token: string,\n apiBase: string,\n): Promise<void> {\n await fetch(`${apiBase}/api/v1/inbox/${escalationId}/customer-message`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ content }),\n });\n}\n\n/**\n * Fetch live messages from a claimed escalation (human agent replies).\n * Used to poll for new messages during live handover.\n */\nexport async function fetchLiveMessages(\n escalationId: string,\n token: string,\n apiBase: string,\n): Promise<LiveMessage[]> {\n const res = await fetch(`${apiBase}/api/v1/inbox/${escalationId}/messages`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!res.ok) return [];\n\n const data = (await res.json()) as {\n data?: { messages?: LiveMessage[] };\n messages?: LiveMessage[];\n };\n return data?.data?.messages ?? data?.messages ?? [];\n}\n\n// ---------------------------------------------------------------------------\n// Page context\n// ---------------------------------------------------------------------------\n\n/**\n * Update the pageContext for a session (SPA navigation support).\n */\nexport async function patchPageContext(\n sessionId: string,\n ctx: Record<string, unknown>,\n token: string,\n apiBase: string,\n): Promise<void> {\n await fetch(`${apiBase}/api/v1/sessions/${sessionId}`, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ pageContext: ctx }),\n });\n}\n\n/**\n * Trigger escalation to a human agent.\n * Returns the raw JSON response or null on failure.\n */\nexport async function postEscalate(\n sessionId: string,\n reason: string,\n token: string,\n apiBase: string,\n): Promise<unknown> {\n const res = await fetch(`${apiBase}/api/v1/sessions/${sessionId}/escalate`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ reason }),\n });\n if (!res.ok) return null;\n return res.json();\n}\n\n/**\n * Create a new support session without sending a query first.\n * Returns the sessionId or null on failure.\n */\nexport async function createSession(\n token: string,\n apiBase: string,\n opts: { pageContext?: Record<string, unknown> } = {},\n): Promise<string | null> {\n const res = await fetch(`${apiBase}/api/v1/sessions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify(opts),\n });\n if (!res.ok) return null;\n const data = (await res.json()) as { sessionId?: string };\n return data.sessionId ?? null;\n}\n","/**\n * Collects page context from the current browser document.\n * Reads URL, title, meta description, headings, and visible body content.\n *\n * Host apps can annotate any element with `data-panacea-content` to include\n * its text in the context, and set `data-panacea-summary=\"...\"` on any element\n * to supply a structured one-line summary (takes highest priority).\n *\n * Safe to call in any browser context — never throws, always returns an object.\n * Used by the widget (auto-collection) and optionally by host apps.\n */\nexport function collectPageContext(): Record<string, unknown> {\n const ctx: Record<string, unknown> = {\n url: window.location.href,\n title: document.title,\n };\n\n // Meta description\n const metaDesc = document.querySelector<HTMLMetaElement>('meta[name=\"description\"]');\n if (metaDesc?.content) ctx.description = metaDesc.content;\n\n // Open-graph title as fallback / additional context\n const ogTitle = document.querySelector<HTMLMetaElement>('meta[property=\"og:title\"]');\n if (ogTitle?.content && ogTitle.content !== document.title) ctx.ogTitle = ogTitle.content;\n\n const root =\n document.querySelector(\"main\") ?? document.querySelector('[role=\"main\"]') ?? document.body;\n\n // Visible headings from the main content area (h1 + h2, max 6)\n const headings = Array.from(root.querySelectorAll(\"h1, h2\"))\n .map((h) => h.textContent?.trim())\n .filter((t): t is string => Boolean(t) && t.length > 0)\n .slice(0, 6);\n if (headings.length) ctx.headings = headings;\n\n // Breadcrumb trail if present\n const breadcrumbEl = document.querySelector(\n '[aria-label=\"breadcrumb\"], [aria-label=\"Breadcrumb\"], nav[aria-label*=\"read\"]',\n );\n if (breadcrumbEl) {\n const crumb = breadcrumbEl.textContent?.replace(/\\s+/g, \" \").trim();\n if (crumb) ctx.breadcrumb = crumb;\n }\n\n // Explicit summary annotations — host app can tag any element with\n // data-panacea-summary to provide a concise, structured description.\n const summaryEls = Array.from(document.querySelectorAll(\"[data-panacea-summary]\"))\n .map((el) => el.getAttribute(\"data-panacea-summary\")?.trim())\n .filter((s): s is string => Boolean(s));\n if (summaryEls.length) ctx.summaries = summaryEls;\n\n // Explicit content annotations — any element tagged data-panacea-content\n // has its visible text included verbatim (useful for tables, stat cards, etc.)\n const contentEls = Array.from(document.querySelectorAll(\"[data-panacea-content]\"))\n .map((el) => el.textContent?.replace(/\\s+/g, \" \").trim())\n .filter((t): t is string => Boolean(t) && t.length > 0);\n if (contentEls.length) ctx.annotatedContent = contentEls.slice(0, 10);\n\n // Fallback: extract visible paragraph / list / table text from main area\n // when no explicit annotations are present. Truncated to keep prompt lean.\n if (!contentEls.length) {\n const textNodes = Array.from(\n root.querySelectorAll(\n \"p, li, td, th, [data-slot='card-title'], [class*='card-title'], dt, dd\",\n ),\n )\n .map((el) => el.textContent?.replace(/\\s+/g, \" \").trim())\n .filter((t): t is string => Boolean(t) && t.length > 3 && t.length < 300)\n // Deduplicate\n .filter((t, i, arr) => arr.indexOf(t) === i)\n .slice(0, 20);\n if (textNodes.length) ctx.pageContent = textNodes;\n }\n\n return ctx;\n}\n\n/**\n * Install a lightweight SPA navigation listener.\n * Monkey-patches `history.pushState` / `history.replaceState` and listens to\n * `popstate` to detect client-side route changes.\n *\n * @param callback - Called with fresh page context on every navigation.\n * @returns Cleanup function to remove all listeners.\n */\nexport function onPageNavigate(callback: (ctx: Record<string, unknown>) => void): () => void {\n const fire = () => {\n // Delay so the new page's DOM (title, headings, content) has time to render\n setTimeout(() => callback(collectPageContext()), 250);\n };\n\n window.addEventListener(\"popstate\", fire);\n\n // Patch pushState / replaceState (used by Next.js, React Router, etc.)\n const origPush = history.pushState.bind(history);\n const origReplace = history.replaceState.bind(history);\n\n history.pushState = function (...args) {\n origPush(...args);\n fire();\n };\n history.replaceState = function (...args) {\n origReplace(...args);\n fire();\n };\n\n return () => {\n window.removeEventListener(\"popstate\", fire);\n history.pushState = origPush;\n history.replaceState = origReplace;\n };\n}\n"]}
|