dewee-webchat 0.4.2 → 0.5.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -7
- package/astro/DeweeChat.astro +2 -2
- package/dist/{chunk-UGHJZAKL.js → chunk-M6J3IXEI.js} +351 -50
- package/dist/{chunk-SAUDVPFH.js → chunk-WHHQ6FKK.js} +1 -1
- package/dist/dewee-webchat.js +34 -27
- package/dist/{handler-DunrbnSD.d.ts → handler-B-c6tDr7.d.ts} +50 -5
- package/dist/index.d.ts +33 -5
- package/dist/index.js +2 -2
- package/dist/page-context-H3ZROFAV.js +137 -0
- package/dist/{protocol-CLbGE9Ns.d.ts → protocol-BbjGSfZa.d.ts} +21 -2
- package/dist/react.d.ts +3 -1
- package/dist/react.js +9 -7
- package/dist/server-identity.d.ts +2 -2
- package/dist/server.d.ts +7 -4
- package/dist/server.js +631 -61
- package/dist/vue.d.ts +60 -13
- package/dist/vue.js +10 -5
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as AgentInfo } from './protocol-
|
|
1
|
+
import { A as AgentInfo } from './protocol-BbjGSfZa.js';
|
|
2
2
|
|
|
3
3
|
interface VisitorClaims {
|
|
4
4
|
/** Visitor id (random, 22 chars; derived from the principal for a signed-in user). */
|
|
@@ -89,6 +89,42 @@ declare class CcpError extends Error {
|
|
|
89
89
|
} | undefined);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
/** A cached identity session. The sessionToken is a bearer credential and must be protected. */
|
|
93
|
+
interface StoredIdentitySession {
|
|
94
|
+
sessionToken: string;
|
|
95
|
+
visitorId: string;
|
|
96
|
+
deadline: number;
|
|
97
|
+
margin: number;
|
|
98
|
+
origin?: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Storage shared by handler instances. Subject keys are HMAC digests scoped to the integration.
|
|
102
|
+
* Implementations must protect session tokens and expire entries by deadline. Revisions must be
|
|
103
|
+
* unique and never reused after expiry or eviction. Every method must be atomic across instances:
|
|
104
|
+
* read creates a revision even for an absent subject; commit compares and advances it; takeSubject
|
|
105
|
+
* advances it even when empty; deleteToken compares tokens within the supplied namespace and advances
|
|
106
|
+
* each affected revision. This fences an in-flight exchange against logout and concurrent exchanges.
|
|
107
|
+
*/
|
|
108
|
+
interface IdentitySessionStore {
|
|
109
|
+
read(subjectKey: string, agent: string): Promise<{
|
|
110
|
+
revision: string;
|
|
111
|
+
session: StoredIdentitySession | null;
|
|
112
|
+
}>;
|
|
113
|
+
/** Atomically verify that no subject mutation has occurred since read. */
|
|
114
|
+
check(subjectKey: string, revision: string): Promise<boolean>;
|
|
115
|
+
/** Atomically compare and advance the subject revision when committing a session. */
|
|
116
|
+
commit(subjectKey: string, agent: string, revision: string, session: StoredIdentitySession): Promise<boolean>;
|
|
117
|
+
/** Remove only entries whose current token equals this token, not a newer replacement. */
|
|
118
|
+
deleteToken(namespace: string, token: string): Promise<void>;
|
|
119
|
+
/** Atomically advance the revision and remove and return all entries, even when none exist. */
|
|
120
|
+
takeSubject(subjectKey: string): Promise<Array<{
|
|
121
|
+
agent: string;
|
|
122
|
+
session: StoredIdentitySession;
|
|
123
|
+
}>>;
|
|
124
|
+
}
|
|
125
|
+
/** Bounded per-handler default; a shared adapter can be provided for multi-instance hosts. */
|
|
126
|
+
declare function memoryIdentitySessionStore(limit?: number): IdentitySessionStore;
|
|
127
|
+
|
|
92
128
|
/**
|
|
93
129
|
* A signed-in user of the host app, as `identity.authenticate` reports it. Scopes are not part of
|
|
94
130
|
* the session exchange: report scope changes with `lifecycle({ event: "scopes_changed" })` from
|
|
@@ -170,6 +206,8 @@ interface DeweeIdentitySdk {
|
|
|
170
206
|
* to one agent, and the gateway derives the user from it.
|
|
171
207
|
*/
|
|
172
208
|
interface DeweeIdentityOptions {
|
|
209
|
+
/** Optional protected shared store for identity-session reuse across handler instances. */
|
|
210
|
+
sessionStore?: IdentitySessionStore;
|
|
173
211
|
/** `deweeIdentitySdk` from `dewee-webchat/server/identity`. */
|
|
174
212
|
sdk: DeweeIdentitySdk;
|
|
175
213
|
/**
|
|
@@ -237,8 +275,10 @@ interface DeweeHandlerOptions {
|
|
|
237
275
|
token?: string;
|
|
238
276
|
/** Legacy gateway transport. */
|
|
239
277
|
tenantId?: string;
|
|
240
|
-
/** HMAC secret for visitor tokens (
|
|
278
|
+
/** HMAC secret for visitor tokens (at least 32 random UTF-8 bytes). */
|
|
241
279
|
secret?: string;
|
|
280
|
+
/** Legacy gateway only: permit a process-local secret for development. Sessions reset on restart. */
|
|
281
|
+
allowEphemeralSecret?: boolean;
|
|
242
282
|
/** Legacy gateway transport: agents visitors may talk to. The first is the default. `"*"` = every active agent. */
|
|
243
283
|
agents?: Array<string | AgentInfo> | "*";
|
|
244
284
|
defaultAgent?: string;
|
|
@@ -246,6 +286,10 @@ interface DeweeHandlerOptions {
|
|
|
246
286
|
allowAgentSwitch?: boolean;
|
|
247
287
|
/** Cross-origin sites allowed to call this backend. Same-origin is always allowed. */
|
|
248
288
|
allowedOrigins?: string[] | "*";
|
|
289
|
+
/** Additional header names allowed in cross-origin requests. */
|
|
290
|
+
allowedHeaders?: string[];
|
|
291
|
+
/** HTTPS directory prefixes from which the backend may proxy images; disabled when unset. */
|
|
292
|
+
imageSources?: string[];
|
|
249
293
|
/**
|
|
250
294
|
* Legacy gateway transport: let visitors bring their own gateway URL + token (sent per request, never stored).
|
|
251
295
|
* Meant for playgrounds. `allowedHosts` restricts which gateways may be targeted.
|
|
@@ -295,8 +339,9 @@ interface DeweeHandler {
|
|
|
295
339
|
* and IP (without it, the origin of the request the session was issued for is used). `token` is the
|
|
296
340
|
* widget's reuse hint (`reuseHint()`) or visitor token, sent along by your logout page: its identity
|
|
297
341
|
* session is revoked too when it belongs to `subject`, so a host that keeps no memory between
|
|
298
|
-
* requests still ends the exact session. Best effort: never throws
|
|
299
|
-
*
|
|
342
|
+
* requests still ends the exact session. Best effort: never throws; a store failure is logged and
|
|
343
|
+
* the exact supplied token is still attempted. Without a shared store, other instances' sessions
|
|
344
|
+
* end at expiry (`sessionTtlSeconds`). Without `identity` it does nothing.
|
|
300
345
|
*/
|
|
301
346
|
revoke(subject: string, request?: Request, token?: string): Promise<void>;
|
|
302
347
|
}
|
|
@@ -304,4 +349,4 @@ declare function createDeweeHandler(options: DeweeHandlerOptions): DeweeHandler;
|
|
|
304
349
|
/** Reads options from environment variables (process.env, Workers env, import.meta.env…). */
|
|
305
350
|
declare function optionsFromEnv(env: Record<string, string | undefined>, overrides?: Partial<DeweeHandlerOptions>): DeweeHandlerOptions;
|
|
306
351
|
|
|
307
|
-
export { type AuthenticatedUser as A, CcpError as C, type DeweeIdentitySdk as D, type IdentityPrivateJwk as I, type MemoryRateLimits as M, type RateLimitBucket as R, type VisitorClaims as V, type CcpOptions as a, type DeweeHandler as b, type DeweeHandlerOptions as c, type DeweeIdentityOptions as d, type DeweeTransport as e, type
|
|
352
|
+
export { type AuthenticatedUser as A, CcpError as C, type DeweeIdentitySdk as D, type IdentityPrivateJwk as I, type MemoryRateLimits as M, type RateLimitBucket as R, type StoredIdentitySession as S, type VisitorClaims as V, type CcpOptions as a, type DeweeHandler as b, type DeweeHandlerOptions as c, type DeweeIdentityOptions as d, type DeweeTransport as e, type IdentitySessionStore as f, type IdentitySigner as g, type RateLimiter as h, createDeweeHandler as i, memoryRateLimiter as j, memoryIdentitySessionStore as m, optionsFromEnv as o, signVisitorToken as s, verifyVisitorToken as v };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { d as ActionSensitivity, P as PublicConfig, S as SessionResponse, L as LinkResponse, H as HistoryMessage, e as ChatRequestBody, C as ChatStreamEvent, R as Remediation } from './protocol-
|
|
2
|
-
export { a as ActionManifestEntry, b as AdmissionErrorCode, A as AgentInfo, I as IdentityErrorCode, f as LinkTarget, T as ToolCallView } from './protocol-
|
|
1
|
+
import { d as ActionSensitivity, P as PublicConfig, S as SessionResponse, L as LinkResponse, H as HistoryMessage, e as ChatRequestBody, C as ChatStreamEvent, R as Remediation, g as PageContextAttachment } from './protocol-BbjGSfZa.js';
|
|
2
|
+
export { a as ActionManifestEntry, b as AdmissionErrorCode, A as AgentInfo, I as IdentityErrorCode, f as LinkTarget, T as ToolCallView } from './protocol-BbjGSfZa.js';
|
|
3
3
|
|
|
4
4
|
type ActionResultStatus = "ok" | "denied" | "error";
|
|
5
5
|
interface ActionResult {
|
|
@@ -83,6 +83,8 @@ declare class ApiClient {
|
|
|
83
83
|
private session;
|
|
84
84
|
private config;
|
|
85
85
|
private sessionPromise;
|
|
86
|
+
private supersededSessions;
|
|
87
|
+
private sessionGeneration;
|
|
86
88
|
constructor(o: ApiClientOptions);
|
|
87
89
|
private url;
|
|
88
90
|
private get storageKey();
|
|
@@ -96,12 +98,15 @@ declare class ApiClient {
|
|
|
96
98
|
private request;
|
|
97
99
|
private headers;
|
|
98
100
|
private parse;
|
|
99
|
-
|
|
101
|
+
/** Obtain a short-lived, URL-bound image capability without exposing the visitor bearer in src. */
|
|
102
|
+
imageUrl(source: string, signal?: AbortSignal): Promise<string>;
|
|
103
|
+
getConfig(force?: boolean, probePageContext?: boolean): Promise<PublicConfig>;
|
|
100
104
|
/** When each signed-in session should be refreshed, on this browser's clock. */
|
|
101
105
|
private refreshAt;
|
|
102
106
|
/**
|
|
103
107
|
* Session reuse. A signed-in session follows its schedule (see `scheduleRefresh`). An anonymous
|
|
104
|
-
* token is refreshed once less than 10% of its lifetime (at most 24 h, at least 30 s
|
|
108
|
+
* token is refreshed once less than 10% of its lifetime (at most 24 h, at least 30 s, and no more
|
|
109
|
+
* than half the lifetime) remains; a
|
|
105
110
|
* stored token from before `obtainedAt` existed uses the 24 h margin. When the backend reports the
|
|
106
111
|
* lifetime (`expiresIn`), it is counted from receipt, so a skewed browser clock does not matter.
|
|
107
112
|
*/
|
|
@@ -160,6 +165,14 @@ interface DeweeChatConfig {
|
|
|
160
165
|
suggestions?: string[];
|
|
161
166
|
/** Image URL used for the header avatar (and launcher when `launcherAvatar`). */
|
|
162
167
|
avatar?: string;
|
|
168
|
+
/** HTTPS origins from which Markdown images may load, in addition to the page's own origin. */
|
|
169
|
+
imageOrigins?: string[];
|
|
170
|
+
/** Explicit host opt-in; the visitor must also choose to share the current page. */
|
|
171
|
+
pageContext?: "off" | "metadata" | "content";
|
|
172
|
+
contextSelector?: string;
|
|
173
|
+
excludeSelectors?: string[];
|
|
174
|
+
/** Supplies host-filtered content. Visitors still preview and opt in on each message. */
|
|
175
|
+
contextProvider?: () => Promise<Partial<PageContextAttachment>>;
|
|
163
176
|
launcherAvatar?: boolean;
|
|
164
177
|
position?: "bottom-right" | "bottom-left";
|
|
165
178
|
/** "floating" (launcher + popup) or "inline" (fills its container). */
|
|
@@ -238,12 +251,17 @@ declare class DeweeChatElement extends ElementBase {
|
|
|
238
251
|
private api;
|
|
239
252
|
private apiKey;
|
|
240
253
|
private server;
|
|
254
|
+
private sharedPage?;
|
|
255
|
+
private preparingPage;
|
|
256
|
+
private pagePending;
|
|
257
|
+
private pageGeneration;
|
|
241
258
|
private agentId;
|
|
242
259
|
private conversationId;
|
|
243
260
|
private messages;
|
|
244
261
|
private running;
|
|
245
262
|
private jobAccessLaunch;
|
|
246
263
|
private activated;
|
|
264
|
+
private probedPage;
|
|
247
265
|
private strings;
|
|
248
266
|
private mql;
|
|
249
267
|
private registry;
|
|
@@ -330,6 +348,11 @@ declare class DeweeChatElement extends ElementBase {
|
|
|
330
348
|
private applyConfig;
|
|
331
349
|
private renderShell;
|
|
332
350
|
private autosize;
|
|
351
|
+
private updatePageChoice;
|
|
352
|
+
private captureCurrentPage;
|
|
353
|
+
private pageFingerprint;
|
|
354
|
+
private renderPagePreview;
|
|
355
|
+
private choosePage;
|
|
333
356
|
private updateSendButton;
|
|
334
357
|
/** Disables sending for `ms` and announces it; the visible countdown is not read out every second. */
|
|
335
358
|
private startCooldown;
|
|
@@ -367,7 +390,12 @@ declare class DeweeChatElement extends ElementBase {
|
|
|
367
390
|
}
|
|
368
391
|
|
|
369
392
|
type Doc = Document;
|
|
370
|
-
|
|
393
|
+
interface MarkdownOptions {
|
|
394
|
+
imageOrigins?: string[];
|
|
395
|
+
imageProxy?: (source: string, signal: AbortSignal) => Promise<string>;
|
|
396
|
+
deferImages?: boolean;
|
|
397
|
+
}
|
|
398
|
+
declare function renderMarkdown(doc: Doc, src: string, options?: MarkdownOptions): DocumentFragment;
|
|
371
399
|
|
|
372
400
|
/** Registers the `<dewee-chat>` custom element (idempotent, SSR-safe). */
|
|
373
401
|
declare function defineDeweeChat(tag?: string): void;
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// src/widget/page-context.ts
|
|
2
|
+
var BLOCKED = "script,style,noscript,template,svg,canvas,iframe,form,input,textarea,select,option,button,[contenteditable]:not([contenteditable='false']),[hidden],[aria-hidden='true'],[inert],dewee-chat";
|
|
3
|
+
var MAX_CONTENT = 12e3;
|
|
4
|
+
function safePageUrl(href) {
|
|
5
|
+
try {
|
|
6
|
+
const url = new URL(href);
|
|
7
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") return void 0;
|
|
8
|
+
url.username = "";
|
|
9
|
+
url.password = "";
|
|
10
|
+
url.hash = "";
|
|
11
|
+
for (const key of [...url.searchParams.keys()]) {
|
|
12
|
+
const words = key.replace(/(\p{Lu}+)(\p{Lu}\p{Ll})/gu, "$1 $2").replace(/([\p{Ll}\p{N}])(\p{Lu})/gu, "$1 $2").replace(/(\p{L})(\p{N})/gu, "$1 $2").replace(/(\p{N})(\p{L})/gu, "$1 $2").toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean);
|
|
13
|
+
const joined = words.join("");
|
|
14
|
+
const qualifiers = ["api", "access", "auth", "oauth", "secret", "private", "client", "session", "signing", "verification", "reset", "recovery", "activation", "confirmation", "invite", "license", "encryption"];
|
|
15
|
+
const secret = words.some((word, index) => ["token", "secret", "password", "passwd", "pwd", "auth", "authorization", "cookie", "apikey", "session", "sid", "sig", "signature", "otp", "jwt", "credential", "credentials", "nonce", "bearer", "ticket"].includes(word) || ["code", "key", "state"].includes(word) && (words.length === 1 || index > 0 && qualifiers.includes(words[index - 1]))) || /token|secret|passw|apikey|credential|sessid|session|authorization|samlresponse/.test(joined);
|
|
16
|
+
if (secret) url.searchParams.set(key, "[REDACTED]");
|
|
17
|
+
}
|
|
18
|
+
return new TextEncoder().encode(url.href).length <= 2048 ? url.href : void 0;
|
|
19
|
+
} catch {
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function clip(text, length) {
|
|
24
|
+
return Array.from(text).slice(0, length).join("");
|
|
25
|
+
}
|
|
26
|
+
function limitBytes(context) {
|
|
27
|
+
if (context.content && new TextEncoder().encode(JSON.stringify(context)).length > 65536) {
|
|
28
|
+
const points = Array.from(context.content.text);
|
|
29
|
+
let low = 0, high = points.length;
|
|
30
|
+
while (low < high) {
|
|
31
|
+
const mid = Math.ceil((low + high) / 2);
|
|
32
|
+
context.content.text = points.slice(0, mid).join("");
|
|
33
|
+
context.content.truncated = true;
|
|
34
|
+
if (new TextEncoder().encode(JSON.stringify(context)).length <= 65536) low = mid;
|
|
35
|
+
else high = mid - 1;
|
|
36
|
+
}
|
|
37
|
+
context.content.text = points.slice(0, low).join("");
|
|
38
|
+
context.content.truncated = true;
|
|
39
|
+
}
|
|
40
|
+
return context;
|
|
41
|
+
}
|
|
42
|
+
function providedPageContext(raw, base, mode) {
|
|
43
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new TypeError("Invalid page context");
|
|
44
|
+
const value = raw;
|
|
45
|
+
const result = { version: 1, url: base.url, captured_at: base.captured_at };
|
|
46
|
+
for (const [key, max] of [["title", 300], ["description", 1e3]]) {
|
|
47
|
+
if (typeof value[key] === "string") result[key] = clip(value[key].replace(/\s+/g, " ").trim(), max);
|
|
48
|
+
}
|
|
49
|
+
if (mode === "content") {
|
|
50
|
+
if (typeof value.selection === "string") result.selection = clip(value.selection, 2e3);
|
|
51
|
+
if (value.content && typeof value.content === "object" && !Array.isArray(value.content)) {
|
|
52
|
+
const content = value.content;
|
|
53
|
+
if (typeof content.text !== "string" || content.format !== "text" && content.format !== "markdown") throw new TypeError("Invalid page content");
|
|
54
|
+
const text = clip(content.text, MAX_CONTENT);
|
|
55
|
+
result.content = { format: content.format, text, truncated: content.truncated === true || Array.from(content.text).length > MAX_CONTENT };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return limitBytes(result);
|
|
59
|
+
}
|
|
60
|
+
function visible(el, excludes) {
|
|
61
|
+
for (let node = el; node; node = node.parentElement) {
|
|
62
|
+
if (node.matches(BLOCKED) || excludes.some((selector) => node.matches(selector))) return false;
|
|
63
|
+
if (node instanceof HTMLDialogElement && !node.open) return false;
|
|
64
|
+
if (node instanceof HTMLDetailsElement && !node.open) {
|
|
65
|
+
const summary = [...node.children].find((child) => child.tagName === "SUMMARY");
|
|
66
|
+
if (!summary || !el || summary !== el && !summary.contains(el)) return false;
|
|
67
|
+
}
|
|
68
|
+
if (node.hasAttribute("popover")) {
|
|
69
|
+
try {
|
|
70
|
+
if (!node.matches(":popover-open")) return false;
|
|
71
|
+
} catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const style = getComputedStyle(node);
|
|
76
|
+
if (style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse" || style.opacity === "0" || style.contentVisibility === "hidden") return false;
|
|
77
|
+
}
|
|
78
|
+
if (el && typeof el.checkVisibility === "function" && !el.checkVisibility({ checkOpacity: true, checkVisibilityCSS: true })) return false;
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
function capturePageContext(doc, options) {
|
|
82
|
+
const url = safePageUrl(doc.location.href);
|
|
83
|
+
if (!url) return void 0;
|
|
84
|
+
const excludes = options.excludeSelectors ?? [];
|
|
85
|
+
for (const selector of excludes) doc.documentElement.matches(selector);
|
|
86
|
+
const root = doc.querySelector(options.selector || "main, article, [role='main']");
|
|
87
|
+
const context = { version: 1, url, captured_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
88
|
+
const title = clip(doc.title.replace(/\s+/g, " ").trim(), 300);
|
|
89
|
+
const description = clip((doc.querySelector('meta[name="description" i]')?.getAttribute("content") ?? "").replace(/\s+/g, " ").trim(), 1e3);
|
|
90
|
+
if (title) context.title = title;
|
|
91
|
+
if (description) context.description = description;
|
|
92
|
+
if (options.mode !== "content" || !root || !visible(root, excludes)) return context;
|
|
93
|
+
const walker = doc.createTreeWalker(root, NodeFilter.SHOW_TEXT);
|
|
94
|
+
const chunks = [];
|
|
95
|
+
let size = 0;
|
|
96
|
+
let visited = 0;
|
|
97
|
+
let truncated = false;
|
|
98
|
+
while (walker.nextNode()) {
|
|
99
|
+
if (++visited > 5e3) {
|
|
100
|
+
truncated = true;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
const node = walker.currentNode;
|
|
104
|
+
if (!visible(node.parentElement, excludes)) continue;
|
|
105
|
+
const text2 = (node.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
106
|
+
if (!text2) continue;
|
|
107
|
+
const remaining = MAX_CONTENT - size;
|
|
108
|
+
const part = clip(text2, remaining);
|
|
109
|
+
chunks.push(part);
|
|
110
|
+
size += Array.from(part).length + 1;
|
|
111
|
+
if (Array.from(text2).length > remaining || size >= MAX_CONTENT) {
|
|
112
|
+
truncated = true;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const text = clip(chunks.join("\n"), MAX_CONTENT);
|
|
117
|
+
if (text) context.content = { format: "text", text, truncated };
|
|
118
|
+
const selection = doc.getSelection();
|
|
119
|
+
if (selection && !selection.isCollapsed && selection.rangeCount) {
|
|
120
|
+
const range = selection.getRangeAt(0);
|
|
121
|
+
const ancestor = range.commonAncestorContainer;
|
|
122
|
+
const element = ancestor.nodeType === Node.ELEMENT_NODE ? ancestor : ancestor.parentElement;
|
|
123
|
+
if (element && root.contains(element) && visible(element, excludes)) {
|
|
124
|
+
const nodes = root.querySelectorAll("*");
|
|
125
|
+
if (nodes.length <= 5e3 && ![...nodes].some((el) => range.intersectsNode(el) && !visible(el, excludes))) {
|
|
126
|
+
const selected = clip(selection.toString().trim(), 2e3);
|
|
127
|
+
if (selected) context.selection = selected;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return limitBytes(context);
|
|
132
|
+
}
|
|
133
|
+
export {
|
|
134
|
+
capturePageContext,
|
|
135
|
+
providedPageContext,
|
|
136
|
+
safePageUrl
|
|
137
|
+
};
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/** A transient, untrusted attachment for one chat turn. Never stored in browser history. */
|
|
2
|
+
interface PageContextAttachment {
|
|
3
|
+
version: 1;
|
|
4
|
+
url: string;
|
|
5
|
+
title?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
captured_at?: string;
|
|
8
|
+
selection?: string;
|
|
9
|
+
content?: {
|
|
10
|
+
format: "text" | "markdown";
|
|
11
|
+
text: string;
|
|
12
|
+
truncated: boolean;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
1
16
|
/** Server-Sent Events emitted by `POST {basePath}/chat`. */
|
|
2
17
|
type ChatStreamEvent = {
|
|
3
18
|
type: "start";
|
|
@@ -76,6 +91,8 @@ interface PublicConfig {
|
|
|
76
91
|
turnstileSiteKey?: string;
|
|
77
92
|
maxMessageLength: number;
|
|
78
93
|
version: string;
|
|
94
|
+
/** Attachment versions advertised by the connected runtime. */
|
|
95
|
+
pageContextVersions?: number[];
|
|
79
96
|
/** Present (true) when the backend signs users in through Dewee: the widget then asks `/session` on every page load. */
|
|
80
97
|
identity?: boolean;
|
|
81
98
|
/**
|
|
@@ -108,7 +125,7 @@ interface SessionResponse {
|
|
|
108
125
|
*/
|
|
109
126
|
reuseHint?: string;
|
|
110
127
|
/**
|
|
111
|
-
*
|
|
128
|
+
* Milliseconds until `expiresAt`, counted
|
|
112
129
|
* from when the response is received. The widget refreshes from this rather than `expiresAt`, so a
|
|
113
130
|
* skewed browser clock does not matter.
|
|
114
131
|
*/
|
|
@@ -165,6 +182,8 @@ interface ChatRequestBody {
|
|
|
165
182
|
/** Hash from the `start` event of the last completed turn; the manifest is re-sent to the agent only when it differs. */
|
|
166
183
|
actionsHash?: string;
|
|
167
184
|
pageContext?: unknown;
|
|
185
|
+
/** Ephemeral, untrusted content for this turn; distinct from the legacy page-action context. */
|
|
186
|
+
page_context?: PageContextAttachment;
|
|
168
187
|
}
|
|
169
188
|
|
|
170
|
-
export { type AgentInfo as A, type ChatStreamEvent as C, type ErrorBody as E, type HistoryMessage as H, type IdentityErrorCode as I, type LinkResponse as L, type PublicConfig as P, type Remediation as R, type SessionResponse as S, type ToolCallView as T, type ActionManifestEntry as a, type AdmissionErrorCode as b, ADMISSION_ERROR_CODES as c, type ActionSensitivity as d, type ChatRequestBody as e, type LinkTarget as f, isAdmissionErrorCode as i };
|
|
189
|
+
export { type AgentInfo as A, type ChatStreamEvent as C, type ErrorBody as E, type HistoryMessage as H, type IdentityErrorCode as I, type LinkResponse as L, type PublicConfig as P, type Remediation as R, type SessionResponse as S, type ToolCallView as T, type ActionManifestEntry as a, type AdmissionErrorCode as b, ADMISSION_ERROR_CODES as c, type ActionSensitivity as d, type ChatRequestBody as e, type LinkTarget as f, type PageContextAttachment as g, isAdmissionErrorCode as i };
|
package/dist/react.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as react from 'react';
|
|
|
2
2
|
import { CSSProperties } from 'react';
|
|
3
3
|
import { DeweeChatConfig, ActionEventDetail, DeweeChatElement } from './index.js';
|
|
4
4
|
export { ActionConfirmHook, DomActionsRoot, PageAction, PageActionContext, RequestHeadersOption } from './index.js';
|
|
5
|
-
import './protocol-
|
|
5
|
+
import './protocol-BbjGSfZa.js';
|
|
6
6
|
|
|
7
7
|
interface DeweeChatProps extends Partial<DeweeChatConfig> {
|
|
8
8
|
className?: string;
|
|
@@ -15,6 +15,8 @@ interface DeweeChatProps extends Partial<DeweeChatConfig> {
|
|
|
15
15
|
onClose?: (e: CustomEvent) => void;
|
|
16
16
|
/** Page action telemetry (`dewee:action`, detail: ActionEventDetail). */
|
|
17
17
|
onAction?: (e: CustomEvent<ActionEventDetail>) => void;
|
|
18
|
+
onContextReady?: (e: CustomEvent) => void;
|
|
19
|
+
onContextError?: (e: CustomEvent) => void;
|
|
18
20
|
}
|
|
19
21
|
declare const DeweeChat: react.ForwardRefExoticComponent<DeweeChatProps & react.RefAttributes<DeweeChatElement | null>>;
|
|
20
22
|
|
package/dist/react.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defineDeweeChat
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-M6J3IXEI.js";
|
|
4
|
+
import "./chunk-WHHQ6FKK.js";
|
|
5
5
|
|
|
6
6
|
// src/react.ts
|
|
7
7
|
import { createElement, forwardRef, useEffect, useImperativeHandle, useRef } from "react";
|
|
@@ -12,23 +12,25 @@ var EVENTS = {
|
|
|
12
12
|
onError: "dewee:error",
|
|
13
13
|
onOpen: "dewee:open",
|
|
14
14
|
onClose: "dewee:close",
|
|
15
|
-
onAction: "dewee:action"
|
|
15
|
+
onAction: "dewee:action",
|
|
16
|
+
onContextReady: "dewee:context-ready",
|
|
17
|
+
onContextError: "dewee:context-error"
|
|
16
18
|
};
|
|
17
19
|
var DeweeChat = forwardRef(function DeweeChat2(props, ref) {
|
|
18
20
|
const el = useRef(null);
|
|
19
|
-
const { className, style, onReady, onSend, onReply, onError, onOpen, onClose, onAction, actions, confirm, getPageContext, headers, fetch, domActionsRoot, ...config } = props;
|
|
20
|
-
const handlers = { onReady, onSend, onReply, onError, onOpen, onClose, onAction };
|
|
21
|
+
const { className, style, onReady, onSend, onReply, onError, onOpen, onClose, onAction, onContextReady, onContextError, actions, confirm, getPageContext, headers, fetch, domActionsRoot, contextProvider, imageOrigins, ...config } = props;
|
|
22
|
+
const handlers = { onReady, onSend, onReply, onError, onOpen, onClose, onAction, onContextReady, onContextError };
|
|
21
23
|
const handlersRef = useRef(handlers);
|
|
22
24
|
handlersRef.current = handlers;
|
|
23
25
|
useImperativeHandle(ref, () => el.current, []);
|
|
24
|
-
const configKey = JSON.stringify(config);
|
|
26
|
+
const configKey = JSON.stringify({ ...config, imageOrigins: imageOrigins ?? [] });
|
|
25
27
|
useEffect(() => {
|
|
26
28
|
defineDeweeChat();
|
|
27
29
|
el.current?.configure(JSON.parse(configKey));
|
|
28
30
|
}, [configKey]);
|
|
29
31
|
useEffect(() => {
|
|
30
32
|
defineDeweeChat();
|
|
31
|
-
el.current?.configure({ actions, confirm, getPageContext, headers, fetch, domActionsRoot });
|
|
33
|
+
el.current?.configure({ actions, confirm, getPageContext, headers, fetch, domActionsRoot, contextProvider });
|
|
32
34
|
});
|
|
33
35
|
useEffect(() => {
|
|
34
36
|
const node = el.current;
|
package/dist/server.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { A as AuthenticatedUser, C as CcpError, a as CcpOptions, b as DeweeHandler, c as DeweeHandlerOptions, d as DeweeIdentityOptions, D as DeweeIdentitySdk, e as DeweeTransport, I as IdentityPrivateJwk, f as IdentitySigner, M as MemoryRateLimits, R as RateLimitBucket,
|
|
1
|
+
export { A as AuthenticatedUser, C as CcpError, a as CcpOptions, b as DeweeHandler, c as DeweeHandlerOptions, d as DeweeIdentityOptions, D as DeweeIdentitySdk, e as DeweeTransport, I as IdentityPrivateJwk, f as IdentitySessionStore, g as IdentitySigner, M as MemoryRateLimits, R as RateLimitBucket, h as RateLimiter, S as StoredIdentitySession, V as VisitorClaims, i as createDeweeHandler, m as memoryIdentitySessionStore, j as memoryRateLimiter, o as optionsFromEnv, s as signVisitorToken, v as verifyVisitorToken } from './handler-B-c6tDr7.js';
|
|
2
2
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
3
|
-
import { R as Remediation, H as HistoryMessage, A as AgentInfo, C as ChatStreamEvent, a as ActionManifestEntry, b as AdmissionErrorCode } from './protocol-
|
|
4
|
-
export { c as ADMISSION_ERROR_CODES, d as ActionSensitivity, e as ChatRequestBody, E as ErrorBody, I as IdentityErrorCode, L as LinkResponse, f as LinkTarget, P as PublicConfig, S as SessionResponse, i as isAdmissionErrorCode } from './protocol-
|
|
3
|
+
import { R as Remediation, H as HistoryMessage, A as AgentInfo, C as ChatStreamEvent, a as ActionManifestEntry, b as AdmissionErrorCode } from './protocol-BbjGSfZa.js';
|
|
4
|
+
export { c as ADMISSION_ERROR_CODES, d as ActionSensitivity, e as ChatRequestBody, E as ErrorBody, I as IdentityErrorCode, L as LinkResponse, f as LinkTarget, P as PublicConfig, S as SessionResponse, i as isAdmissionErrorCode } from './protocol-BbjGSfZa.js';
|
|
5
5
|
|
|
6
6
|
type FetchHandler = (request: Request) => Promise<Response>;
|
|
7
7
|
/** Wraps a fetch-style handler for `http.createServer`, Express (`app.all("/api/dewee/*", ...)`), etc. */
|
|
@@ -29,8 +29,9 @@ declare class GatewayError extends Error {
|
|
|
29
29
|
code?: string | undefined;
|
|
30
30
|
remediation?: Remediation | undefined;
|
|
31
31
|
retryAfterMs?: unknown | undefined;
|
|
32
|
+
reason?: unknown | undefined;
|
|
32
33
|
/** `retryAfterMs` is the gateway's raw value for retryable refusals (`RATE_LIMITED`), unvalidated. */
|
|
33
|
-
constructor(message: string, code?: string | undefined, remediation?: Remediation | undefined, retryAfterMs?: unknown | undefined);
|
|
34
|
+
constructor(message: string, code?: string | undefined, remediation?: Remediation | undefined, retryAfterMs?: unknown | undefined, reason?: unknown | undefined);
|
|
34
35
|
}
|
|
35
36
|
/** One authenticated gateway connection. Short-lived: one per chat turn or lookup. */
|
|
36
37
|
declare class GatewayConnection {
|
|
@@ -39,6 +40,7 @@ declare class GatewayConnection {
|
|
|
39
40
|
private pending;
|
|
40
41
|
private listeners;
|
|
41
42
|
private closed;
|
|
43
|
+
pageContextVersions: number[];
|
|
42
44
|
private constructor();
|
|
43
45
|
static connect(creds: GatewayCredentials, actor: GatewayActor, timeoutMs?: number): Promise<GatewayConnection>;
|
|
44
46
|
call<T = any>(method: string, params: unknown, timeoutMs?: number): Promise<T>;
|
|
@@ -59,6 +61,7 @@ interface StreamChatOptions {
|
|
|
59
61
|
agent: string;
|
|
60
62
|
sessionKey: string;
|
|
61
63
|
message: string;
|
|
64
|
+
pageContext?: unknown;
|
|
62
65
|
signal?: AbortSignal;
|
|
63
66
|
timeoutMs?: number;
|
|
64
67
|
}
|