dewee-webchat 0.3.0 → 0.4.1-beta.2
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 +237 -22
- package/dist/{chunk-ZTVGWFXL.js → chunk-2NRPUV6T.js} +580 -42
- package/dist/{chunk-W6A4HVFB.js → chunk-PUIN5ZCQ.js} +52 -7
- package/dist/dewee-webchat.js +36 -29
- package/dist/handler-DunrbnSD.d.ts +307 -0
- package/dist/index.d.ts +91 -7
- package/dist/index.js +2 -2
- package/dist/protocol-CLbGE9Ns.d.ts +170 -0
- package/dist/react.d.ts +1 -1
- package/dist/react.js +2 -2
- package/dist/server-identity.d.ts +6 -0
- package/dist/server-identity.js +6 -0
- package/dist/server.d.ts +33 -75
- package/dist/server.js +1507 -41
- package/dist/vue.d.ts +1 -1
- package/dist/vue.js +2 -2
- package/package.json +13 -1
- package/dist/protocol-B72Ry-fY.d.ts +0 -92
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { A as AgentInfo } from './protocol-CLbGE9Ns.js';
|
|
2
|
+
|
|
3
|
+
interface VisitorClaims {
|
|
4
|
+
/** Visitor id (random, 22 chars; derived from the principal for a signed-in user). */
|
|
5
|
+
v: string;
|
|
6
|
+
/** Expiry, epoch ms. */
|
|
7
|
+
exp: number;
|
|
8
|
+
/**
|
|
9
|
+
* Signed-in users only: the Dewee identity session token. In the signed token it is encrypted
|
|
10
|
+
* (AES-256-GCM), so the browser only ever holds an opaque value; these claims carry it decrypted.
|
|
11
|
+
*/
|
|
12
|
+
ids?: string;
|
|
13
|
+
/** Signed-in users only: the agent the identity session is bound to. */
|
|
14
|
+
ida?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Signed-in users only: a keyed digest of the host's subject (see {@link subjectDigest}). Sealed
|
|
17
|
+
* together with `ids`, so the browser never sees the subject or its digest.
|
|
18
|
+
*/
|
|
19
|
+
sub?: string;
|
|
20
|
+
/**
|
|
21
|
+
* CCP transport only: the CCP edge session token (`dws1.…`). In the signed token it is encrypted
|
|
22
|
+
* (AES-256-GCM, its own key label), so the browser never holds the raw value.
|
|
23
|
+
*/
|
|
24
|
+
es?: string;
|
|
25
|
+
/** CCP transport only: expiry of `es` as CCP reported it, epoch ms on this server's clock. */
|
|
26
|
+
ese?: number;
|
|
27
|
+
}
|
|
28
|
+
/** Signs a visitor bearer token. */
|
|
29
|
+
declare function signVisitorToken(secret: string, claims: VisitorClaims): Promise<string>;
|
|
30
|
+
/** Verifies a visitor bearer token. A reuse hint (or any token with a purpose label) is refused. */
|
|
31
|
+
declare function verifyVisitorToken(secret: string, token: string): Promise<VisitorClaims | null>;
|
|
32
|
+
/**
|
|
33
|
+
* Rate-limit buckets: `session` (new visitor sessions, per IP), `chat` (messages, per visitor and IP),
|
|
34
|
+
* and with signed-in users `preauth` (calls to the host's `authenticate`, per IP), `verified`
|
|
35
|
+
* (signed-in `/session` calls, per user) and `link` (link starts, per user).
|
|
36
|
+
*/
|
|
37
|
+
type RateLimitBucket = "session" | "chat" | "preauth" | "verified" | "link";
|
|
38
|
+
/** Returns true when the key may proceed. Implementations may be async (KV, DO, Redis…). */
|
|
39
|
+
type RateLimiter = (key: string, bucket: RateLimitBucket) => boolean | Promise<boolean>;
|
|
40
|
+
/**
|
|
41
|
+
* `[max, windowMs]` per bucket. `preauth` defaults to 120 per minute; `verified` and `link` fall back to
|
|
42
|
+
* the `session` limit when not set.
|
|
43
|
+
*/
|
|
44
|
+
interface MemoryRateLimits {
|
|
45
|
+
chat: [number, number];
|
|
46
|
+
session: [number, number];
|
|
47
|
+
preauth?: [number, number];
|
|
48
|
+
verified?: [number, number];
|
|
49
|
+
link?: [number, number];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Fixed-window in-memory limiter. Good enough for a single Node process; on serverless
|
|
53
|
+
* runtimes each isolate keeps its own counters, so pass a shared limiter for hard limits.
|
|
54
|
+
*/
|
|
55
|
+
declare function memoryRateLimiter(limits: MemoryRateLimits): RateLimiter;
|
|
56
|
+
|
|
57
|
+
/** Validated CCP endpoint options (see {@link parseCcpOptions}). */
|
|
58
|
+
interface CcpOptions {
|
|
59
|
+
/**
|
|
60
|
+
* The full ingress base the CCP "Website chat" page prints as `DEWEE_CCP_CHAT_URL`, e.g.
|
|
61
|
+
* `https://app.dewee.sh/api/public/webchat/v1/wci_…`. Must be https, except plain http to a
|
|
62
|
+
* loopback host (`localhost`, `127.0.0.1`, `[::1]`) for local development.
|
|
63
|
+
*/
|
|
64
|
+
chatUrl: string;
|
|
65
|
+
/** `DEWEE_WEBCHAT_INTEGRATION_ID` (`wci_…`); must equal the id at the end of `chatUrl`. */
|
|
66
|
+
integrationId: string;
|
|
67
|
+
/** `DEWEE_WEBCHAT_INTEGRATION_SECRET` (`dwc_…`). Server-only; never sent to a browser or another origin. */
|
|
68
|
+
integrationSecret: string;
|
|
69
|
+
/** Custom fetch (tests, instrumented hosts). Defaults to the global `fetch`. */
|
|
70
|
+
fetch?: typeof fetch;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* A refusal or failure of the ingress. `code` is the contract's public code (`integration_unauthorized`,
|
|
74
|
+
* `rate_limited`, an identity code…), or a local one: `ccp_unreachable` (network), `ccp_redirect`
|
|
75
|
+
* (a redirect was refused), `ccp_bad_response` (not the contract's shape). `message` is CCP's text and
|
|
76
|
+
* is for logs only; it is never sent to a browser.
|
|
77
|
+
*/
|
|
78
|
+
declare class CcpError extends Error {
|
|
79
|
+
status: number;
|
|
80
|
+
code: string;
|
|
81
|
+
retryAfterSeconds?: number | undefined;
|
|
82
|
+
remediation?: {
|
|
83
|
+
action: string;
|
|
84
|
+
audience: string;
|
|
85
|
+
} | undefined;
|
|
86
|
+
constructor(status: number, code: string, message: string, retryAfterSeconds?: number | undefined, remediation?: {
|
|
87
|
+
action: string;
|
|
88
|
+
audience: string;
|
|
89
|
+
} | undefined);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* A signed-in user of the host app, as `identity.authenticate` reports it. Scopes are not part of
|
|
94
|
+
* the session exchange: report scope changes with `lifecycle({ event: "scopes_changed" })` from
|
|
95
|
+
* `@dewee/identity`.
|
|
96
|
+
*/
|
|
97
|
+
interface AuthenticatedUser {
|
|
98
|
+
/** Your stable, opaque user id. Never an email address (an id containing "@" is refused). */
|
|
99
|
+
subject: string;
|
|
100
|
+
}
|
|
101
|
+
/** A private JWK whose public half is published in the application's `jwks_url`. */
|
|
102
|
+
interface IdentityPrivateJwk {
|
|
103
|
+
kty: string;
|
|
104
|
+
kid: string;
|
|
105
|
+
d: string;
|
|
106
|
+
alg?: string;
|
|
107
|
+
[key: string]: unknown;
|
|
108
|
+
}
|
|
109
|
+
/** Partner assertion signer, the shape `@dewee/identity` takes. */
|
|
110
|
+
type IdentitySigner = {
|
|
111
|
+
applicationId: string;
|
|
112
|
+
issuer: string;
|
|
113
|
+
secret: string;
|
|
114
|
+
} | {
|
|
115
|
+
applicationId: string;
|
|
116
|
+
issuer: string;
|
|
117
|
+
privateKeyJwk: IdentityPrivateJwk;
|
|
118
|
+
algorithm?: "RS256" | "ES256" | "EdDSA";
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* The two `@dewee/identity` calls the handler uses. Pass `deweeIdentitySdk` from
|
|
122
|
+
* `dewee-webchat/server/identity`; it is a separate entry so that only hosts that sign users in
|
|
123
|
+
* need the SDK installed and bundled.
|
|
124
|
+
*/
|
|
125
|
+
interface DeweeIdentitySdk {
|
|
126
|
+
exchangeSession(opts: {
|
|
127
|
+
gatewayUrl: string;
|
|
128
|
+
allowInsecureHttp?: boolean;
|
|
129
|
+
signer: IdentitySigner;
|
|
130
|
+
subject: string;
|
|
131
|
+
agent: string;
|
|
132
|
+
ttlSeconds?: number;
|
|
133
|
+
}): Promise<{
|
|
134
|
+
sessionToken: string;
|
|
135
|
+
principalId: string;
|
|
136
|
+
expiresAt: Date;
|
|
137
|
+
}>;
|
|
138
|
+
startLink(opts: {
|
|
139
|
+
gatewayUrl: string;
|
|
140
|
+
allowInsecureHttp?: boolean;
|
|
141
|
+
sessionToken: string;
|
|
142
|
+
idempotencyKey?: string;
|
|
143
|
+
}): Promise<{
|
|
144
|
+
proofCode: string;
|
|
145
|
+
expiresAt: Date;
|
|
146
|
+
deepLinks: Array<{
|
|
147
|
+
channelType: string;
|
|
148
|
+
channelInstanceId: string;
|
|
149
|
+
name: string;
|
|
150
|
+
displayName?: string;
|
|
151
|
+
}>;
|
|
152
|
+
}>;
|
|
153
|
+
/**
|
|
154
|
+
* Signs a partner assertion. Used by `transport: "ccp"`, where the website signs and CCP exchanges.
|
|
155
|
+
* `deweeIdentitySdk` provides it.
|
|
156
|
+
*/
|
|
157
|
+
createAssertion?(opts: {
|
|
158
|
+
purpose: "session";
|
|
159
|
+
subject: string;
|
|
160
|
+
applicationId: string;
|
|
161
|
+
issuer: string;
|
|
162
|
+
secret?: string;
|
|
163
|
+
privateKeyJwk?: IdentityPrivateJwk;
|
|
164
|
+
algorithm?: "RS256" | "ES256" | "EdDSA";
|
|
165
|
+
}): Promise<string>;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Verified signed-in users through a Dewee identity session exchange. The host passes one
|
|
169
|
+
* server-side callback; the handler exchanges the user for a short-lived Dewee identity session bound
|
|
170
|
+
* to one agent, and the gateway derives the user from it.
|
|
171
|
+
*/
|
|
172
|
+
interface DeweeIdentityOptions {
|
|
173
|
+
/** `deweeIdentitySdk` from `dewee-webchat/server/identity`. */
|
|
174
|
+
sdk: DeweeIdentitySdk;
|
|
175
|
+
/**
|
|
176
|
+
* Reads your own login (cookie, header…). Return null for a visitor who is not signed in. Throwing
|
|
177
|
+
* means the login could not be read: the visitor gets `503 SOURCE_UNAVAILABLE`, never an anonymous
|
|
178
|
+
* session. Keep it cheap or cached: it runs on every `/session` (every page load) and, with
|
|
179
|
+
* `rebindEachRequest`, on every `/chat`, `/history` and `/link`, behind a generous per-IP cap only.
|
|
180
|
+
*/
|
|
181
|
+
authenticate(req: Request): Promise<AuthenticatedUser | null>;
|
|
182
|
+
/** Dewee application id (`dewee identity setup` prints it). */
|
|
183
|
+
appId: string;
|
|
184
|
+
/** The issuer configured on the application, exactly. */
|
|
185
|
+
issuer: string;
|
|
186
|
+
/** Optional check: must equal `dewee:<appId>`, the audience Dewee issued the application. */
|
|
187
|
+
audience?: string;
|
|
188
|
+
/** Assertion secret of an `hmac` application. Exactly one of this or `privateKeyJwk`. */
|
|
189
|
+
assertionSecret?: string;
|
|
190
|
+
/** Private signing key of a `jwks` application (RS256, ES256 or EdDSA, with `kid`). */
|
|
191
|
+
privateKeyJwk?: IdentityPrivateJwk;
|
|
192
|
+
/** Algorithm of `privateKeyJwk`; defaults to its `alg`. */
|
|
193
|
+
algorithm?: "RS256" | "ES256" | "EdDSA";
|
|
194
|
+
/**
|
|
195
|
+
* The agent identity sessions are bound to. Default: `defaultAgent`, else the first of `agents`
|
|
196
|
+
* (with `transport: "ccp"`: the integration's default agent).
|
|
197
|
+
*/
|
|
198
|
+
agent?: string;
|
|
199
|
+
/**
|
|
200
|
+
* Refuse visitors who are not signed in (no anonymous fallback). Default false: a visitor for whom
|
|
201
|
+
* `authenticate` returns null chats anonymously exactly as without this option.
|
|
202
|
+
*/
|
|
203
|
+
required?: boolean;
|
|
204
|
+
/**
|
|
205
|
+
* Check the signed-in user again on every `/chat`, `/history` and `/link`: `authenticate(req)` must
|
|
206
|
+
* return the same subject the session was issued for, else the request is `401 IDENTITY_REQUIRED`
|
|
207
|
+
* (the widget then asks `/session` again); if it throws, `503 SOURCE_UNAVAILABLE`. Recommended when
|
|
208
|
+
* your login is a cookie: a widget token replayed under another user's cookie is then useless.
|
|
209
|
+
* Default false.
|
|
210
|
+
*/
|
|
211
|
+
rebindEachRequest?: boolean;
|
|
212
|
+
/** Identity session lifetime, 60 to 43200 seconds. Default: the gateway default (3600). */
|
|
213
|
+
sessionTtlSeconds?: number;
|
|
214
|
+
/** Allow plain http to a gateway that is not loopback (private networks only). */
|
|
215
|
+
allowInsecureHttp?: boolean;
|
|
216
|
+
}
|
|
217
|
+
/** How this backend reaches Dewee. */
|
|
218
|
+
type DeweeTransport = "ccp" | "legacy-gateway";
|
|
219
|
+
interface DeweeHandlerOptions {
|
|
220
|
+
/**
|
|
221
|
+
* `"ccp"`: the CCP-managed public webchat ingress, configured with `ccp` (recommended).
|
|
222
|
+
* `"legacy-gateway"`: a direct Gateway WebSocket with `gatewayUrl` + `token` (deprecated).
|
|
223
|
+
* Default: `"ccp"` when `ccp` is set, else `"legacy-gateway"`. Setting both `ccp` and the gateway
|
|
224
|
+
* options is a startup error, so a credential is never sent to an origin it was not meant for.
|
|
225
|
+
*/
|
|
226
|
+
transport?: DeweeTransport;
|
|
227
|
+
/**
|
|
228
|
+
* The CCP integration (`DEWEE_CCP_CHAT_URL`, `DEWEE_WEBCHAT_INTEGRATION_ID`,
|
|
229
|
+
* `DEWEE_WEBCHAT_INTEGRATION_SECRET` from the CCP "Website chat" page). The integration decides the
|
|
230
|
+
* agents, sign-in mode and whether reasoning and tool activity are shown, so `agents`,
|
|
231
|
+
* `defaultAgent`, `tenantId` and `allowClientCredentials` do not apply to it.
|
|
232
|
+
*/
|
|
233
|
+
ccp?: CcpOptions;
|
|
234
|
+
/** Legacy gateway transport: Gateway runtime origin. Required unless every request brings its own credentials. */
|
|
235
|
+
gatewayUrl?: string;
|
|
236
|
+
/** Legacy gateway transport: Gateway token or operator API key (server-side only). */
|
|
237
|
+
token?: string;
|
|
238
|
+
/** Legacy gateway transport. */
|
|
239
|
+
tenantId?: string;
|
|
240
|
+
/** HMAC secret for visitor tokens (>= 32 random chars). */
|
|
241
|
+
secret?: string;
|
|
242
|
+
/** Legacy gateway transport: agents visitors may talk to. The first is the default. `"*"` = every active agent. */
|
|
243
|
+
agents?: Array<string | AgentInfo> | "*";
|
|
244
|
+
defaultAgent?: string;
|
|
245
|
+
/** Let the widget switch between allowed agents. Default: true when more than one agent. */
|
|
246
|
+
allowAgentSwitch?: boolean;
|
|
247
|
+
/** Cross-origin sites allowed to call this backend. Same-origin is always allowed. */
|
|
248
|
+
allowedOrigins?: string[] | "*";
|
|
249
|
+
/**
|
|
250
|
+
* Legacy gateway transport: let visitors bring their own gateway URL + token (sent per request, never stored).
|
|
251
|
+
* Meant for playgrounds. `allowedHosts` restricts which gateways may be targeted.
|
|
252
|
+
*/
|
|
253
|
+
allowClientCredentials?: boolean | {
|
|
254
|
+
allowedHosts?: string[];
|
|
255
|
+
};
|
|
256
|
+
/** Cloudflare Turnstile. When set, issuing a visitor session requires a valid challenge. */
|
|
257
|
+
turnstile?: {
|
|
258
|
+
siteKey: string;
|
|
259
|
+
secretKey: string;
|
|
260
|
+
};
|
|
261
|
+
/**
|
|
262
|
+
* Custom limiter, or false to disable. Default (in memory): 20 messages/min per visitor and IP,
|
|
263
|
+
* 10 new sessions/10 min per IP and, with `identity`, 120 `authenticate` calls/min per IP,
|
|
264
|
+
* 30 signed-in sessions/10 min per user and 5 link starts/10 min per user. Defense in depth: the
|
|
265
|
+
* Dewee gateway owns the shared message limit and refuses with `RATE_LIMITED` (forwarded typed).
|
|
266
|
+
*/
|
|
267
|
+
rateLimit?: RateLimiter | false;
|
|
268
|
+
maxMessageLength?: number;
|
|
269
|
+
/** Prefix for the per-visitor gateway user id. */
|
|
270
|
+
userIdPrefix?: string;
|
|
271
|
+
sessionTtlMs?: number;
|
|
272
|
+
/** Max duration of one agent run. */
|
|
273
|
+
timeoutMs?: number;
|
|
274
|
+
/** Observe traffic (logging/analytics). Errors thrown here are ignored. */
|
|
275
|
+
onChat?: (info: {
|
|
276
|
+
visitorId: string;
|
|
277
|
+
agent: string;
|
|
278
|
+
message: string;
|
|
279
|
+
reply?: string;
|
|
280
|
+
error?: string;
|
|
281
|
+
}) => void;
|
|
282
|
+
/**
|
|
283
|
+
* Verified signed-in users through Dewee (needs the `@dewee/identity` package). Absent: every
|
|
284
|
+
* visitor is anonymous, exactly as before. Cannot be combined with `allowClientCredentials`.
|
|
285
|
+
*/
|
|
286
|
+
identity?: DeweeIdentityOptions;
|
|
287
|
+
}
|
|
288
|
+
/** The backend: one function for every route, plus the logout hook of signed-in users. */
|
|
289
|
+
interface DeweeHandler {
|
|
290
|
+
(request: Request): Promise<Response>;
|
|
291
|
+
/**
|
|
292
|
+
* Call from your logout: ends the Dewee identity sessions this backend holds for `subject` (every
|
|
293
|
+
* bound agent) and forgets them, so the next `/session` exchanges again. With `transport: "ccp"` they
|
|
294
|
+
* are revoked at CCP (`identity/revoke`). Pass the logout request so CCP sees the visitor's origin
|
|
295
|
+
* and IP (without it, the origin of the request the session was issued for is used). `token` is the
|
|
296
|
+
* widget's reuse hint (`reuseHint()`) or visitor token, sent along by your logout page: its identity
|
|
297
|
+
* 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. Other sessions held only by
|
|
299
|
+
* other instances end at their expiry (`sessionTtlSeconds`). Without `identity` it does nothing.
|
|
300
|
+
*/
|
|
301
|
+
revoke(subject: string, request?: Request, token?: string): Promise<void>;
|
|
302
|
+
}
|
|
303
|
+
declare function createDeweeHandler(options: DeweeHandlerOptions): DeweeHandler;
|
|
304
|
+
/** Reads options from environment variables (process.env, Workers env, import.meta.env…). */
|
|
305
|
+
declare function optionsFromEnv(env: Record<string, string | undefined>, overrides?: Partial<DeweeHandlerOptions>): DeweeHandlerOptions;
|
|
306
|
+
|
|
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 IdentitySigner as f, type RateLimiter as g, createDeweeHandler as h, memoryRateLimiter as m, optionsFromEnv as o, signVisitorToken as s, verifyVisitorToken as v };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { a as ActionManifestEntry, A as AgentInfo, 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 } from './protocol-CLbGE9Ns.js';
|
|
2
|
+
export { a as ActionManifestEntry, b as AdmissionErrorCode, A as AgentInfo, I as IdentityErrorCode, f as LinkTarget, T as ToolCallView } from './protocol-CLbGE9Ns.js';
|
|
3
3
|
|
|
4
4
|
type ActionResultStatus = "ok" | "denied" | "error";
|
|
5
5
|
interface ActionResult {
|
|
@@ -39,9 +39,19 @@ type ActionConfirmHook = (action: PageActionInfo & {
|
|
|
39
39
|
}, args: Record<string, unknown>) => boolean | Promise<boolean>;
|
|
40
40
|
type ActionCallStatus = "pending" | "awaiting-confirmation" | "running" | "done" | "denied" | "error";
|
|
41
41
|
|
|
42
|
+
/** The only launch data allowed to reach the browser. Never an identity session. */
|
|
43
|
+
interface JobAccessLaunch {
|
|
44
|
+
bootstrap_url: string;
|
|
45
|
+
launch_token: string;
|
|
46
|
+
expires_at: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
42
49
|
/** Extra request headers: a static map, or a function evaluated before every request. */
|
|
43
50
|
type RequestHeadersOption = Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
|
|
44
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Headers the widget owns; `headers` cannot override them (compared case-insensitively). The
|
|
53
|
+
* session bearer token, signed-in or anonymous, travels in `authorization`.
|
|
54
|
+
*/
|
|
45
55
|
declare const RESERVED_HEADERS: readonly string[];
|
|
46
56
|
interface ApiClientOptions {
|
|
47
57
|
/** Backend base URL, e.g. "/api/dewee" or "https://example.com/api/dewee". */
|
|
@@ -59,7 +69,10 @@ interface ApiClientOptions {
|
|
|
59
69
|
declare class ApiError extends Error {
|
|
60
70
|
status: number;
|
|
61
71
|
code?: string | undefined;
|
|
62
|
-
|
|
72
|
+
remediation?: Remediation | undefined;
|
|
73
|
+
retryAfterMs?: number | undefined;
|
|
74
|
+
/** `retryAfterMs`: how long to wait before sending again (`RATE_LIMITED`). */
|
|
75
|
+
constructor(message: string, status: number, code?: string | undefined, remediation?: Remediation | undefined, retryAfterMs?: number | undefined);
|
|
63
76
|
}
|
|
64
77
|
declare class ApiClient {
|
|
65
78
|
private o;
|
|
@@ -69,17 +82,56 @@ declare class ApiClient {
|
|
|
69
82
|
constructor(o: ApiClientOptions);
|
|
70
83
|
private url;
|
|
71
84
|
private get storageKey();
|
|
72
|
-
|
|
73
|
-
|
|
85
|
+
private get reuseKey();
|
|
86
|
+
/**
|
|
87
|
+
* The tab's reuse hint for signed-in sessions, or null (none sent by this backend). A logout page can
|
|
88
|
+
* send it to the backend's logout route, whose `handler.revoke(subject, request, hint)` then ends
|
|
89
|
+
* exactly that session.
|
|
90
|
+
*/
|
|
91
|
+
reuseHint(): string | null;
|
|
74
92
|
private request;
|
|
75
93
|
private headers;
|
|
76
94
|
private parse;
|
|
77
95
|
getConfig(force?: boolean): Promise<PublicConfig>;
|
|
96
|
+
/** When each signed-in session should be refreshed, on this browser's clock. */
|
|
97
|
+
private refreshAt;
|
|
98
|
+
/**
|
|
99
|
+
* Session reuse. A signed-in session follows its schedule (see `scheduleRefresh`). An anonymous
|
|
100
|
+
* token is refreshed once less than 10% of its lifetime (at most 24 h, at least 30 s) remains; a
|
|
101
|
+
* stored token from before `obtainedAt` existed uses the 24 h margin. When the backend reports the
|
|
102
|
+
* lifetime (`expiresIn`), it is counted from receipt, so a skewed browser clock does not matter.
|
|
103
|
+
*/
|
|
104
|
+
private fresh;
|
|
105
|
+
/**
|
|
106
|
+
* A signed-in session is refreshed `min(60s, lifetime/2)` before it ends, with the lifetime the
|
|
107
|
+
* backend reports (`expiresIn`) counted from receipt, so a short lifetime is not refreshed on every
|
|
108
|
+
* request and a skewed browser clock does not matter.
|
|
109
|
+
*/
|
|
110
|
+
private scheduleRefresh;
|
|
111
|
+
/** The stored anonymous visitor session, if any. Signed-in sessions are never stored. */
|
|
112
|
+
private stored;
|
|
78
113
|
/** Returns a valid visitor session, creating or refreshing it when needed. */
|
|
79
114
|
ensureSession(): Promise<SessionResponse>;
|
|
115
|
+
private loadSession;
|
|
80
116
|
private createSession;
|
|
117
|
+
/** Drops the session; the next request creates a new one. A stored anonymous visitor is kept for a signed-in session. */
|
|
81
118
|
forgetSession(): void;
|
|
119
|
+
/**
|
|
120
|
+
* Asks the backend for a new session now (after a sign-in or sign-out on the host page). The
|
|
121
|
+
* stored anonymous visitor id is kept, so an anonymous visitor's history survives.
|
|
122
|
+
*/
|
|
123
|
+
renewSession(): Promise<SessionResponse>;
|
|
124
|
+
/** Requests a single-use launch ticket. Does not refresh or swap the signed-in session after refusal. */
|
|
125
|
+
jobAccess(signal: AbortSignal): Promise<JobAccessLaunch>;
|
|
126
|
+
/** Starts a web-first link of a chat account for the signed-in user (identity backends only). */
|
|
127
|
+
link(): Promise<LinkResponse>;
|
|
82
128
|
history(agent: string, conversationId: string): Promise<HistoryMessage[]>;
|
|
129
|
+
/**
|
|
130
|
+
* Sends a request with the current session. A signed-in session the backend refuses (expired,
|
|
131
|
+
* revoked, or another user signed in) is dropped and renewed once, as chat() does. An anonymous
|
|
132
|
+
* visitor's 401 is final, and its stored visitor is kept.
|
|
133
|
+
*/
|
|
134
|
+
private signedInRequest;
|
|
83
135
|
/** Sends a message and calls `onEvent` for every streamed event. Resolves when the run ends. */
|
|
84
136
|
chat(input: ChatRequestBody & {
|
|
85
137
|
agent: string;
|
|
@@ -177,6 +229,7 @@ declare class DeweeChatElement extends ElementBase {
|
|
|
177
229
|
private conversationId;
|
|
178
230
|
private messages;
|
|
179
231
|
private running;
|
|
232
|
+
private jobAccessLaunch;
|
|
180
233
|
private activated;
|
|
181
234
|
private strings;
|
|
182
235
|
private mql;
|
|
@@ -184,6 +237,8 @@ declare class DeweeChatElement extends ElementBase {
|
|
|
184
237
|
private propActionNames;
|
|
185
238
|
private hooks;
|
|
186
239
|
private acks;
|
|
240
|
+
/** Pause after a RATE_LIMITED refusal; send stays disabled until it ends. */
|
|
241
|
+
private cooldown;
|
|
187
242
|
private els;
|
|
188
243
|
constructor();
|
|
189
244
|
/** Current effective configuration (attributes merged with `configure()` overrides). */
|
|
@@ -222,6 +277,25 @@ declare class DeweeChatElement extends ElementBase {
|
|
|
222
277
|
private showNotice;
|
|
223
278
|
/** Stops the running reply. */
|
|
224
279
|
stop(): void;
|
|
280
|
+
/**
|
|
281
|
+
* Call after the visitor signs in or out on the host page: the next message uses a session for
|
|
282
|
+
* the current user, and the conversation is reloaded for them.
|
|
283
|
+
*/
|
|
284
|
+
renewSession(): Promise<void>;
|
|
285
|
+
/**
|
|
286
|
+
* The tab's signed-in reuse hint, or null. Not a credential: send it to your logout route for
|
|
287
|
+
* `handler.revoke(subject, request, hint)`, which then ends exactly that session.
|
|
288
|
+
*/
|
|
289
|
+
reuseHint(): string | null;
|
|
290
|
+
/**
|
|
291
|
+
* Web-first linking for a signed-in user: shows a proof code and the chat apps to send it to
|
|
292
|
+
* ("Continue on Telegram"). The user never types an account id. Resolves null on failure (the
|
|
293
|
+
* error is shown in the chat and emitted as `dewee:error`).
|
|
294
|
+
*/
|
|
295
|
+
startLink(): Promise<LinkResponse | null>;
|
|
296
|
+
/** Opens the hosted permission review; the verified session stays on the backend. */
|
|
297
|
+
openJobAccess(): Promise<void>;
|
|
298
|
+
private renderLink;
|
|
225
299
|
/** Starts a fresh conversation with the current agent. */
|
|
226
300
|
newConversation(): void;
|
|
227
301
|
connectedCallback(): void;
|
|
@@ -236,6 +310,10 @@ declare class DeweeChatElement extends ElementBase {
|
|
|
236
310
|
private renderShell;
|
|
237
311
|
private autosize;
|
|
238
312
|
private updateSendButton;
|
|
313
|
+
/** Disables sending for `ms` and announces it; the visible countdown is not read out every second. */
|
|
314
|
+
private startCooldown;
|
|
315
|
+
private renderCooldown;
|
|
316
|
+
private endCooldown;
|
|
239
317
|
private setRunning;
|
|
240
318
|
private renderSubtitle;
|
|
241
319
|
/** Loads backend config and the persisted conversation once. */
|
|
@@ -257,6 +335,12 @@ declare class DeweeChatElement extends ElementBase {
|
|
|
257
335
|
private nearBottom;
|
|
258
336
|
private scrollToBottom;
|
|
259
337
|
private showError;
|
|
338
|
+
/**
|
|
339
|
+
* An admission refusal: the visitor's message never reached the agent, so it goes back to an empty
|
|
340
|
+
* composer to edit or resend. `POLICY_DENIED` is shown next to that message instead of as an empty
|
|
341
|
+
* reply. Returns true when the error was shown here.
|
|
342
|
+
*/
|
|
343
|
+
private refuseInline;
|
|
260
344
|
/** Renders a Turnstile challenge in light DOM (slotted) and resolves with its token. */
|
|
261
345
|
private challenge;
|
|
262
346
|
}
|
|
@@ -274,4 +358,4 @@ declare global {
|
|
|
274
358
|
}
|
|
275
359
|
}
|
|
276
360
|
|
|
277
|
-
export { type ActionCallStatus, type ActionCallView, type ActionConfirmHook, type ActionEventDetail, type ActionResult, type ActionResultStatus, ActionSensitivity, ApiClient, type ApiClientOptions, ApiError, ChatRequestBody, ChatStreamEvent, type DeweeChatConfig, DeweeChatElement, HistoryMessage, type JsonSchema, type PageAction, type PageActionContext, type PageActionInfo, PublicConfig, RESERVED_HEADERS, type RequestHeadersOption, SessionResponse, defineDeweeChat, mount, renderMarkdown };
|
|
361
|
+
export { type ActionCallStatus, type ActionCallView, type ActionConfirmHook, type ActionEventDetail, type ActionResult, type ActionResultStatus, ActionSensitivity, ApiClient, type ApiClientOptions, ApiError, ChatRequestBody, ChatStreamEvent, type DeweeChatConfig, DeweeChatElement, HistoryMessage, type JsonSchema, LinkResponse, type PageAction, type PageActionContext, type PageActionInfo, PublicConfig, RESERVED_HEADERS, Remediation, type RequestHeadersOption, SessionResponse, defineDeweeChat, mount, renderMarkdown };
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/** Server-Sent Events emitted by `POST {basePath}/chat`. */
|
|
2
|
+
type ChatStreamEvent = {
|
|
3
|
+
type: "start";
|
|
4
|
+
runId?: string;
|
|
5
|
+
conversationId: string;
|
|
6
|
+
agent: string;
|
|
7
|
+
actionsHash?: string;
|
|
8
|
+
} | {
|
|
9
|
+
type: "thinking";
|
|
10
|
+
text: string;
|
|
11
|
+
} | {
|
|
12
|
+
type: "delta";
|
|
13
|
+
text: string;
|
|
14
|
+
} | {
|
|
15
|
+
type: "tool_call";
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
arguments?: unknown;
|
|
19
|
+
} | {
|
|
20
|
+
type: "tool_result";
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
isError: boolean;
|
|
24
|
+
result?: string;
|
|
25
|
+
} | {
|
|
26
|
+
type: "done";
|
|
27
|
+
content: string;
|
|
28
|
+
usage?: Record<string, number>;
|
|
29
|
+
} | {
|
|
30
|
+
type: "error";
|
|
31
|
+
message: string;
|
|
32
|
+
code?: string;
|
|
33
|
+
remediation?: Remediation;
|
|
34
|
+
retryAfterMs?: number;
|
|
35
|
+
};
|
|
36
|
+
/** The seven typed identity errors of the Dewee gateway, forwarded as `error.code` for signed-in users. */
|
|
37
|
+
declare const IDENTITY_ERROR_CODES: readonly ["IDENTITY_REQUIRED", "IDENTITY_LINK_REQUIRED", "IDENTITY_CONFLICT", "LINK_EXPIRED", "LINK_REVOKED", "PERMISSION_DENIED", "SOURCE_UNAVAILABLE"];
|
|
38
|
+
type IdentityErrorCode = (typeof IDENTITY_ERROR_CODES)[number];
|
|
39
|
+
/**
|
|
40
|
+
* Admission refusals of the gateway for `chat.send`, forwarded as `error.code`: `POLICY_DENIED` (the
|
|
41
|
+
* message is outside the policy, e.g. too long; not retryable) and `RATE_LIMITED` (rate limit, a run
|
|
42
|
+
* still answering the previous message, or the counter backend unavailable; carries `retryAfterMs`).
|
|
43
|
+
* `CHALLENGE_REQUIRED` is reserved for the gateway challenge protocol and carries no payload yet.
|
|
44
|
+
*/
|
|
45
|
+
declare const ADMISSION_ERROR_CODES: readonly ["POLICY_DENIED", "RATE_LIMITED", "CHALLENGE_REQUIRED"];
|
|
46
|
+
type AdmissionErrorCode = (typeof ADMISSION_ERROR_CODES)[number];
|
|
47
|
+
declare function isAdmissionErrorCode(code: unknown): code is AdmissionErrorCode;
|
|
48
|
+
/** What to do about an identity error: `action` (e.g. `reauthenticate`, `retry_later`) and who acts (`end_user`, `operator`, `integrator`). */
|
|
49
|
+
interface Remediation {
|
|
50
|
+
action: string;
|
|
51
|
+
audience: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Error body of every JSON route. `remediation` is present for identity errors only, `retryAfterMs`
|
|
55
|
+
* for `RATE_LIMITED` refusals of the gateway (also sent as the `Retry-After` header, in seconds).
|
|
56
|
+
*/
|
|
57
|
+
interface ErrorBody {
|
|
58
|
+
error: {
|
|
59
|
+
message: string;
|
|
60
|
+
code: string;
|
|
61
|
+
remediation?: Remediation;
|
|
62
|
+
retryAfterMs?: number;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
interface AgentInfo {
|
|
66
|
+
id: string;
|
|
67
|
+
name?: string;
|
|
68
|
+
description?: string;
|
|
69
|
+
}
|
|
70
|
+
/** Response of `GET {basePath}/config` — safe to expose publicly. */
|
|
71
|
+
interface PublicConfig {
|
|
72
|
+
agents: AgentInfo[];
|
|
73
|
+
defaultAgent: string;
|
|
74
|
+
allowAgentSwitch: boolean;
|
|
75
|
+
allowClientCredentials: boolean;
|
|
76
|
+
turnstileSiteKey?: string;
|
|
77
|
+
maxMessageLength: number;
|
|
78
|
+
version: string;
|
|
79
|
+
/** Present (true) when the backend signs users in through Dewee: the widget then asks `/session` on every page load. */
|
|
80
|
+
identity?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Present (true) when a returning visitor's `/session` refresh (a valid anonymous `token` in the
|
|
83
|
+
* body) needs no Turnstile token (ccp transport); only new visitors are challenged then. With
|
|
84
|
+
* `identity` the widget still challenges: a signed-in user's `/session` always needs a token.
|
|
85
|
+
*/
|
|
86
|
+
refreshWithoutTurnstile?: boolean;
|
|
87
|
+
}
|
|
88
|
+
/** Response of `POST {basePath}/session`. */
|
|
89
|
+
interface SessionResponse {
|
|
90
|
+
visitorId: string;
|
|
91
|
+
token: string;
|
|
92
|
+
/**
|
|
93
|
+
* When the widget should come back to `/session` (refreshing with `token`). With the ccp transport
|
|
94
|
+
* this is when the CCP edge session sealed in the token ends, earlier than the token itself.
|
|
95
|
+
*/
|
|
96
|
+
expiresAt: number;
|
|
97
|
+
/**
|
|
98
|
+
* Present (true) when the session carries a verified Dewee identity. Such sessions are short-lived and
|
|
99
|
+
* are used from memory only.
|
|
100
|
+
*/
|
|
101
|
+
verified?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* ccp transport, verified sessions only: an opaque token that lets a later page load of this tab
|
|
104
|
+
* reuse the session instead of exchanging a new one. It is no bearer token (every route but
|
|
105
|
+
* `/session` refuses it), and `/session` uses it only for the user the host authenticates at that
|
|
106
|
+
* moment. The widget keeps it in `sessionStorage` and sends it back as `reuseHint`; it stores
|
|
107
|
+
* nothing when the answer has none (legacy gateway, PHP).
|
|
108
|
+
*/
|
|
109
|
+
reuseHint?: string;
|
|
110
|
+
/**
|
|
111
|
+
* Verified sessions, and every session of the ccp transport: milliseconds until `expiresAt`, counted
|
|
112
|
+
* from when the response is received. The widget refreshes from this rather than `expiresAt`, so a
|
|
113
|
+
* skewed browser clock does not matter.
|
|
114
|
+
*/
|
|
115
|
+
expiresIn?: number;
|
|
116
|
+
}
|
|
117
|
+
/** A chat account the proof code of a web-first link can be sent to. */
|
|
118
|
+
interface LinkTarget {
|
|
119
|
+
/** e.g. `telegram`, `discord`, `zalo`. */
|
|
120
|
+
channelType: string;
|
|
121
|
+
channelInstanceId: string;
|
|
122
|
+
name: string;
|
|
123
|
+
displayName?: string;
|
|
124
|
+
}
|
|
125
|
+
/** Response of `POST {basePath}/link`. */
|
|
126
|
+
interface LinkResponse {
|
|
127
|
+
/** Show it exactly as given; the user sends it in a direct chat from the account to link. */
|
|
128
|
+
proofCode: string;
|
|
129
|
+
/** Epoch ms. */
|
|
130
|
+
expiresAt: number;
|
|
131
|
+
deepLinks: LinkTarget[];
|
|
132
|
+
}
|
|
133
|
+
interface ToolCallView {
|
|
134
|
+
id: string;
|
|
135
|
+
name: string;
|
|
136
|
+
arguments?: unknown;
|
|
137
|
+
result?: string;
|
|
138
|
+
isError?: boolean;
|
|
139
|
+
status: "running" | "done" | "error";
|
|
140
|
+
}
|
|
141
|
+
/** A transcript message as returned by `GET {basePath}/history`. */
|
|
142
|
+
interface HistoryMessage {
|
|
143
|
+
role: "user" | "assistant";
|
|
144
|
+
content: string;
|
|
145
|
+
thinking?: string;
|
|
146
|
+
toolCalls?: ToolCallView[];
|
|
147
|
+
createdAt?: string;
|
|
148
|
+
}
|
|
149
|
+
/** How much an agent-invoked page action can change. `mutate`/`destructive` always need user approval. */
|
|
150
|
+
type ActionSensitivity = "read" | "navigate" | "mutate" | "destructive";
|
|
151
|
+
/** What the backend (and the agent) learns about a page action. Handlers never leave the browser. */
|
|
152
|
+
interface ActionManifestEntry {
|
|
153
|
+
name: string;
|
|
154
|
+
description: string;
|
|
155
|
+
/** JSON Schema subset describing `args` (object). */
|
|
156
|
+
parameters: Record<string, unknown>;
|
|
157
|
+
sensitivity: ActionSensitivity;
|
|
158
|
+
}
|
|
159
|
+
/** Body of `POST {basePath}/chat`. `actions`/`actionsHash`/`pageContext` are optional (page actions). */
|
|
160
|
+
interface ChatRequestBody {
|
|
161
|
+
message: string;
|
|
162
|
+
agent?: string;
|
|
163
|
+
conversationId?: string;
|
|
164
|
+
actions?: ActionManifestEntry[];
|
|
165
|
+
/** Hash from the `start` event of the last completed turn; the manifest is re-sent to the agent only when it differs. */
|
|
166
|
+
actionsHash?: string;
|
|
167
|
+
pageContext?: unknown;
|
|
168
|
+
}
|
|
169
|
+
|
|
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 };
|
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, PageAction, PageActionContext, RequestHeadersOption } from './index.js';
|
|
5
|
-
import './protocol-
|
|
5
|
+
import './protocol-CLbGE9Ns.js';
|
|
6
6
|
|
|
7
7
|
interface DeweeChatProps extends Partial<DeweeChatConfig> {
|
|
8
8
|
className?: string;
|
package/dist/react.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defineDeweeChat
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-2NRPUV6T.js";
|
|
4
|
+
import "./chunk-PUIN5ZCQ.js";
|
|
5
5
|
|
|
6
6
|
// src/react.ts
|
|
7
7
|
import { createElement, forwardRef, useEffect, useImperativeHandle, useRef } from "react";
|