@talk2view/sdk 0.21.2 → 0.21.4
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 +124 -46
- package/dist/assistant-ui/index.d.ts.map +1 -1
- package/dist/assistant-ui/index.js +12 -7
- package/dist/assistant-ui/index.js.map +1 -1
- package/dist/auth.d.ts +23 -4
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +48 -22
- package/dist/auth.js.map +1 -1
- package/dist/chat/lib/use-conversations.d.ts.map +1 -1
- package/dist/chat/lib/use-conversations.js +23 -6
- package/dist/chat/lib/use-conversations.js.map +1 -1
- package/dist/chat/provider.d.ts +20 -2
- package/dist/chat/provider.d.ts.map +1 -1
- package/dist/chat/provider.js +3 -1
- package/dist/chat/provider.js.map +1 -1
- package/dist/chat/views/account.d.ts.map +1 -1
- package/dist/chat/views/account.js +22 -9
- package/dist/chat/views/account.js.map +1 -1
- package/dist/chat/voice-button.js +3 -3
- package/dist/chat/voice-button.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +5 -4
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +7 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +7 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +31 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +64 -10
- package/dist/index.js.map +1 -1
- package/dist/storage.d.ts +6 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +6 -0
- package/dist/storage.js.map +1 -1
- package/dist/tools.d.ts +2 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +23 -10
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +6 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/components/ChatPanel.d.ts +5 -1
- package/dist/ui/components/ChatPanel.d.ts.map +1 -1
- package/dist/ui/components/ChatPanel.js +5 -1
- package/dist/ui/components/ChatPanel.js.map +1 -1
- package/docs/chat.md +86 -15
- package/docs/voice.md +18 -8
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -8,10 +8,26 @@ Add AI-powered natural language control to any application. Talk2View's SDK hand
|
|
|
8
8
|
npm install @talk2view/sdk
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
You need a partner key.
|
|
12
|
-
**[
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
You need a partner key. Talk2View is in private beta:
|
|
12
|
+
**[request a partner key](https://talk2view.com/resources/contact?subject=partner-key)**.
|
|
13
|
+
You get one key, `pk_live_…`, a public identifier that is safe to ship in a
|
|
14
|
+
browser bundle; see [Partner Keys](#partner-keys) and the docs at
|
|
15
|
+
[docs.talk2view.com](https://docs.talk2view.com).
|
|
16
|
+
|
|
17
|
+
## Intended use and data handling
|
|
18
|
+
|
|
19
|
+
Talk2View is for education, research and general information. It is not a
|
|
20
|
+
medical device: it is not regulated or approved as one, and it must not be used
|
|
21
|
+
to diagnose or treat any condition or to make clinical decisions. Its answers
|
|
22
|
+
come from AI models and can be wrong.
|
|
23
|
+
|
|
24
|
+
Do not send protected health information (PHI), or any other data that can
|
|
25
|
+
identify a patient, through the SDK. What end-users type, attach or say, and
|
|
26
|
+
what your tools return, is passed to third-party AI providers, and Talk2View
|
|
27
|
+
has no safeguard that detects, redacts or blocks such data.
|
|
28
|
+
|
|
29
|
+
The [Terms of Use](https://talk2view.com/legal/terms-of-use) and the
|
|
30
|
+
[Privacy Policy](https://talk2view.com/legal/privacy-policy) say this in full.
|
|
15
31
|
|
|
16
32
|
## Chat UI
|
|
17
33
|
|
|
@@ -27,8 +43,8 @@ import '@talk2view/sdk/chat.css';
|
|
|
27
43
|
```
|
|
28
44
|
|
|
29
45
|
That is the full pane: sign-in, streaming replies, attachments, dictation,
|
|
30
|
-
Settings, earlier conversations
|
|
31
|
-
height — it fills its box.
|
|
46
|
+
Settings, earlier conversations (kept in the end-user's browser) and tool
|
|
47
|
+
approvals. Give it a parent with a height — it fills its box.
|
|
32
48
|
|
|
33
49
|
For a floating chat instead — a mark in the corner of the page, a popover on a
|
|
34
50
|
desktop, a full-screen sheet on a phone:
|
|
@@ -41,16 +57,20 @@ import '@talk2view/sdk/chat.css';
|
|
|
41
57
|
```
|
|
42
58
|
|
|
43
59
|
Theming is the custom properties on `.t2v-chat` (`--radius`, `--primary`, the
|
|
44
|
-
rest); dark mode is `className="dark"`. The chat fetches nothing at runtime
|
|
45
|
-
fonts, no CDN — and uses your page's font unless you
|
|
60
|
+
rest); dark mode is `className="dark"`. The chat fetches nothing at runtime but
|
|
61
|
+
your Talk2View engine — no fonts, no CDN — and uses your page's font unless you
|
|
62
|
+
pass `fontFamily`. Voice is the exception; see
|
|
63
|
+
[What loads at runtime](#what-loads-at-runtime).
|
|
46
64
|
|
|
47
|
-
**It is large:
|
|
65
|
+
**It is large: 373.3 KB gzip, excluding React.** A consumer who never imports
|
|
48
66
|
`/chat` pays none of it.
|
|
49
67
|
|
|
50
68
|
**Voice.** For partners with the realtime voice agent enabled, the launcher
|
|
51
69
|
shows a microphone beside the mark: press to talk, press again to hang up. The
|
|
52
70
|
agent uses the same tools, permissions and approval card as the chat. Standalone
|
|
53
|
-
it is `<VoiceButton client={t2v} />`; without a UI it is `t2v.voice`.
|
|
71
|
+
it is `<VoiceButton client={t2v} />`; without a UI it is `t2v.voice`. A call
|
|
72
|
+
downloads Daily's call engine from `https://c.daily.co`, so a Content Security
|
|
73
|
+
Policy has to allow it. See
|
|
54
74
|
**[`docs/voice.md`](https://github.com/Talk2View/talk2view-typescript/blob/main/docs/voice.md)**.
|
|
55
75
|
|
|
56
76
|
Everything else — the full prop table, theming tokens, the launcher in a hostile
|
|
@@ -105,11 +125,11 @@ function App() {
|
|
|
105
125
|
|
|
106
126
|
That's it. `<Talk2View>` registers your tools and holds the chat state; `<ChatPanel>` is the UI — sign-in, streaming replies, tool calls and approvals. Both come from `@talk2view/sdk/ui`, and `<ChatPanel>` must be inside `<Talk2View>`.
|
|
107
127
|
|
|
108
|
-
> `/ui` is smaller than the packaged chat (
|
|
128
|
+
> `/ui` is smaller than the packaged chat (40.9 KB gzip against 373.3, plus
|
|
109
129
|
> syntax highlighting loaded on demand) and has no assistant-ui in it, which is
|
|
110
130
|
> why it is still here.
|
|
111
131
|
|
|
112
|
-
A logged-out visitor
|
|
132
|
+
A logged-out visitor can chat as a guest, with no account, only where guest access is on for your partner key. It is off by default: switch it on under **Settings → Anonymous access** in the dashboard. Guest sign-in also needs a CAPTCHA check by default, which the SDK does not send, so ask Talk2View to set your key up for guests as well; Talk2View also sets your daily limit on new guests. Otherwise `<ChatPanel>` asks people to sign in. Pass `allowAnonymous={false}` to `<ChatPanel>` to always ask them to sign in first.
|
|
113
133
|
|
|
114
134
|
### Vanilla JavaScript
|
|
115
135
|
|
|
@@ -123,11 +143,7 @@ const t2v = new Talk2View({ partnerKey: 'pk_live_...' });
|
|
|
123
143
|
// 1. Authenticate
|
|
124
144
|
await t2v.auth.login('user@example.com', 'password');
|
|
125
145
|
|
|
126
|
-
// 2. Register tools
|
|
127
|
-
t2v.tools.handle('get_time', async () => {
|
|
128
|
-
return new Date().toISOString();
|
|
129
|
-
});
|
|
130
|
-
|
|
146
|
+
// 2. Register tools, then attach their handlers
|
|
131
147
|
await t2v.tools.register([
|
|
132
148
|
{
|
|
133
149
|
name: 'get_time',
|
|
@@ -136,6 +152,10 @@ await t2v.tools.register([
|
|
|
136
152
|
},
|
|
137
153
|
]);
|
|
138
154
|
|
|
155
|
+
t2v.tools.handle('get_time', async () => {
|
|
156
|
+
return new Date().toISOString();
|
|
157
|
+
});
|
|
158
|
+
|
|
139
159
|
// 3. Chat — tool calls are handled automatically
|
|
140
160
|
let reply = '';
|
|
141
161
|
for await (const event of t2v.chat('What time is it?')) {
|
|
@@ -150,7 +170,8 @@ for await (const event of t2v.chat('What time is it?')) {
|
|
|
150
170
|
|
|
151
171
|
This package follows semantic versioning, with one qualification that matters
|
|
152
172
|
while it is below 1.0: a **minor** version may change behaviour, and a
|
|
153
|
-
**patch** never changes the public surface
|
|
173
|
+
**patch** never removes or changes anything in the public surface, though it
|
|
174
|
+
may add to it — a new optional prop, say. Breaking changes to exports or to
|
|
154
175
|
prop shapes are called out in [CHANGELOG.md](CHANGELOG.md) under the release
|
|
155
176
|
that makes them.
|
|
156
177
|
|
|
@@ -165,23 +186,63 @@ anything.
|
|
|
165
186
|
typed surface needs them, not because they are meant to be constructed
|
|
166
187
|
directly. Use `Talk2View`.
|
|
167
188
|
|
|
189
|
+
## Supported environments
|
|
190
|
+
|
|
191
|
+
- **Node** `^22.14.0 || ^24.0.0 || >=26.0.0`, for builds, tests and server
|
|
192
|
+
rendering. That is the range the package's runtime dependencies declare;
|
|
193
|
+
outside it npm warns `EBADENGINE` on install.
|
|
194
|
+
- **ES modules only.** There is no CommonJS build. Node's `require()` of ES
|
|
195
|
+
modules, on by default in the versions above, loads it too.
|
|
196
|
+
- **React** 18 or 19, for `/react`, `/ui`, `/assistant-ui` and `/chat`. The
|
|
197
|
+
core client does not use React. The test suite runs on React 19.
|
|
198
|
+
- **Browsers.** The packaged chat uses `dvh` units and the `inert` attribute,
|
|
199
|
+
so it needs at least Chrome or Edge 108, Safari 15.5 or Firefox 112. Part of
|
|
200
|
+
its styling also needs CSS nesting, `:has()` and media-query range syntax:
|
|
201
|
+
Chrome and Edge 112, Safari 16.5, Firefox 121. The package's own JavaScript
|
|
202
|
+
is ES2022, not transpiled further. Those versions come from the package's own
|
|
203
|
+
code and stylesheet; its dependencies were not measured, and the browser test
|
|
204
|
+
suites run in current Chromium only.
|
|
205
|
+
- **Popup sign-in** (Google and Apple) works in a browser only: it opens a
|
|
206
|
+
window, so it throws in Node and during server rendering. It also needs an
|
|
207
|
+
`https://` website and never runs on `localhost`.
|
|
208
|
+
|
|
209
|
+
## What loads at runtime
|
|
210
|
+
|
|
211
|
+
What each entry point fetches once it is running, for your Content Security
|
|
212
|
+
Policy. "The engine" is your `baseUrl`, `https://engine.talk2view.com` unless
|
|
213
|
+
you set another.
|
|
214
|
+
|
|
215
|
+
| Entry point | Fetches |
|
|
216
|
+
|---|---|
|
|
217
|
+
| `@talk2view/sdk` | The engine only. Popup sign-in opens a window on the engine, which hands over to Google or Apple. |
|
|
218
|
+
| `/react`, `/assistant-ui` | The engine only. |
|
|
219
|
+
| `/chat` | The engine only: no fonts, no CDN, no images (an image in a reply becomes a link). Previews of the files an end-user attaches are `blob:` URLs, so a strict `img-src` needs `blob:`. |
|
|
220
|
+
| `/ui` (deprecated) | The engine, the IBM Plex fonts from Google Fonts (`fonts.googleapis.com`, `fonts.gstatic.com`) and the Talk2View logo from `db.talk2view.com`. Code highlighting compiles WebAssembly, which a strict CSP allows with `'wasm-unsafe-eval'`. |
|
|
221
|
+
| Voice: `t2v.voice`, `<VoiceButton>`, the launcher's microphone | The engine, and Daily's call engine from `https://c.daily.co`, which runs through `Function()`. |
|
|
222
|
+
|
|
223
|
+
So voice needs `https://c.daily.co` in `connect-src` and `'unsafe-eval'` in
|
|
224
|
+
`script-src`. Without either, no call starts. If that download fails, Daily's
|
|
225
|
+
library tries to send an error report to Daily's own error tracking (Sentry);
|
|
226
|
+
you can leave that blocked. The Pipecat libraries voice runs on come from your
|
|
227
|
+
own bundle, loaded on the first press, so a chat that never starts a call never
|
|
228
|
+
loads them. The call's audio travels over WebRTC, through the ICE servers the
|
|
229
|
+
engine hands out for each call.
|
|
230
|
+
[`docs/voice.md`](https://github.com/Talk2View/talk2view-typescript/blob/main/docs/voice.md)
|
|
231
|
+
has the rest.
|
|
232
|
+
|
|
168
233
|
## Concepts
|
|
169
234
|
|
|
170
235
|
### Partner Keys
|
|
171
236
|
|
|
172
|
-
Every request requires a partner API key (`X-T2V-Partner-Key` header).
|
|
173
|
-
|
|
174
|
-
- `pk_test_...` — test keys for development
|
|
175
|
-
- `pk_live_...` — production keys
|
|
237
|
+
Every request requires a partner API key (`X-T2V-Partner-Key` header). The SDK attaches this header automatically.
|
|
176
238
|
|
|
177
|
-
|
|
239
|
+
Each partner gets one key, `pk_live_…`, scoped to your application. Talk2View is in private beta: [request a partner key](https://talk2view.com/resources/contact?subject=partner-key).
|
|
178
240
|
|
|
179
|
-
**Partner keys are public identifiers**, not secrets. They identify your application but do not grant access to user data — every data-accessing request also requires a user JWT. It is safe to include partner keys in client-side bundles.
|
|
241
|
+
**Partner keys are public identifiers**, not secrets. They identify your application but do not grant access to user data — every data-accessing request also requires a user JWT. It is safe to include partner keys in client-side bundles, and there is no secret key to keep on a server.
|
|
180
242
|
|
|
181
243
|
Best practices:
|
|
182
|
-
- Inject
|
|
183
|
-
-
|
|
184
|
-
- To rotate a key, generate a new one at [dashboard.talk2view.com](https://dashboard.talk2view.com), update your app, then deactivate the old key
|
|
244
|
+
- Inject the key via an environment variable at build time (e.g. `VITE_T2V_PARTNER_KEY`)
|
|
245
|
+
- There is no self-serve key creation or rotation. To rotate your key, [contact Talk2View](https://talk2view.com/resources/contact). Rotation issues a new key and retires the old one within about a minute, with no planned overlap, so plan the switch with us
|
|
185
246
|
|
|
186
247
|
### Two-Tier Authentication
|
|
187
248
|
|
|
@@ -215,7 +276,7 @@ Tools with `return_direct: true` skip the AI's post-processing step. The tool re
|
|
|
215
276
|
There are four React entry points, and they do different jobs:
|
|
216
277
|
|
|
217
278
|
- **`@talk2view/sdk/chat`** — the branded Talk2View chat, packaged: `<Talk2ViewChat>` and `<Talk2ViewChatLauncher>`, one stylesheet, nothing to configure. Start here. See [Chat UI](#chat-ui) above and [`docs/chat.md`](https://github.com/Talk2View/talk2view-typescript/blob/main/docs/chat.md).
|
|
218
|
-
- **`@talk2view/sdk/ui`** — the previous chat panel, still supported: `<Talk2View>`, `<ChatPanel>`, `<ChatWidget>` and the components they are built from. 40.
|
|
279
|
+
- **`@talk2view/sdk/ui`** — the previous chat panel, still supported: `<Talk2View>`, `<ChatPanel>`, `<ChatWidget>` and the components they are built from. 40.9 KB gzip against the packaged chat's 373.3, and no assistant-ui.
|
|
219
280
|
- **`@talk2view/sdk/react`** — headless: `<T2VProvider>` and hooks, for building your own UI.
|
|
220
281
|
- **`@talk2view/sdk/assistant-ui`** — a runtime for [assistant-ui](https://www.assistant-ui.com): render the stock `<Thread />` on Talk2View. See [assistant-ui](#assistant-ui) below.
|
|
221
282
|
|
|
@@ -254,7 +315,11 @@ The full chat surface: header, message list, composer, sign-in, tool approvals.
|
|
|
254
315
|
/>
|
|
255
316
|
```
|
|
256
317
|
|
|
257
|
-
|
|
318
|
+
With `allowAnonymous` on (the default), a logged-out visitor sees the chat, and the SDK tries to start a guest session the first time it needs one. The sign-in form appears instead:
|
|
319
|
+
|
|
320
|
+
- when `allowAnonymous` is `false`;
|
|
321
|
+
- when the engine refuses the guest session: guest access is off for your key (the default), its daily guest limit is spent, or it requires a CAPTCHA, which the SDK does not send;
|
|
322
|
+
- when that visitor's guest allowance runs out.
|
|
258
323
|
|
|
259
324
|
### `<LoginForm>` (from `/ui`)
|
|
260
325
|
|
|
@@ -263,9 +328,9 @@ The sign-in form on its own, if you want to place it yourself. It renders uncond
|
|
|
263
328
|
```tsx
|
|
264
329
|
<LoginForm
|
|
265
330
|
heading="Sign in"
|
|
266
|
-
subheading="to keep
|
|
331
|
+
subheading="to keep chatting"
|
|
267
332
|
defaultMode="login" // or 'signup'
|
|
268
|
-
signupUrl="https://talk2view.com/
|
|
333
|
+
signupUrl="https://talk2view.com/sign-up"
|
|
269
334
|
resetPasswordUrl="https://..."
|
|
270
335
|
termsUrl="https://..."
|
|
271
336
|
privacyUrl="https://..."
|
|
@@ -443,7 +508,7 @@ function Chat() {
|
|
|
443
508
|
Four things do not map, on purpose:
|
|
444
509
|
|
|
445
510
|
- **Editing tool arguments before approval.** The SDK supports it (`HumanDecision.updatedInput`, used by `<ApprovalCard>` in `/ui`), but the stock assistant-ui approval card has no edit-and-resubmit affordance to drive it from — it can only approve, deny, or attach a free-form reason.
|
|
446
|
-
- **Sign-in UI.** `useTalk2ViewRuntime` only creates a client and a runtime; there is no `<LoginForm>` equivalent.
|
|
511
|
+
- **Sign-in UI.** `useTalk2ViewRuntime` only creates a client and a runtime; there is no `<LoginForm>` equivalent. Where your key allows guests, a logged-out visitor gets a guest session (same default as `<ChatPanel>`); where it does not, the message is not sent and the client emits `anonymousUnavailable`. Building your own sign-in UI, or gating the app before it mounts, is on the partner.
|
|
447
512
|
- **The agent's plan.** The planning checklist `<ChatPanel>` shows arrives as a `data` part named `plan` (`{ markdown }`). The stock Thread has no renderer for it, so it is silently not shown; register one to see it: `<MessagePrimitive.Parts components={{ data: { by_name: { plan: PlanView } } }} />`.
|
|
448
513
|
- **Editing and deleting sent messages.** The runtime supplies no `onEdit`/`onDelete`, so the stock Thread's pencil on user messages stays disabled. Remove it from your copy of the Thread if you'd rather not show it.
|
|
449
514
|
|
|
@@ -501,12 +566,7 @@ await t2v.tools.register([
|
|
|
501
566
|
},
|
|
502
567
|
]);
|
|
503
568
|
|
|
504
|
-
// Option B:
|
|
505
|
-
t2v.tools.handle('zoom_in', async () => {
|
|
506
|
-
myApp.zoomIn();
|
|
507
|
-
return JSON.stringify({ success: true });
|
|
508
|
-
});
|
|
509
|
-
|
|
569
|
+
// Option B: register the schemas, then attach the handlers
|
|
510
570
|
await t2v.tools.register([
|
|
511
571
|
{
|
|
512
572
|
name: 'zoom_in',
|
|
@@ -515,6 +575,11 @@ await t2v.tools.register([
|
|
|
515
575
|
},
|
|
516
576
|
]);
|
|
517
577
|
|
|
578
|
+
t2v.tools.handle('zoom_in', async () => {
|
|
579
|
+
myApp.zoomIn();
|
|
580
|
+
return JSON.stringify({ success: true });
|
|
581
|
+
});
|
|
582
|
+
|
|
518
583
|
// Check state
|
|
519
584
|
t2v.tools.getRegistered() // ClientToolSchema[]
|
|
520
585
|
t2v.tools.hasHandler('zoom_in') // boolean
|
|
@@ -525,6 +590,8 @@ t2v.tools.hasHandler('zoom_in') // boolean
|
|
|
525
590
|
Register user-defined skills — knowledge documents the AI agent can discover and load for specialised expertise. Skills are stored client-side and sent to the server per-session.
|
|
526
591
|
|
|
527
592
|
```typescript
|
|
593
|
+
// Fictional data. Never use real patient data.
|
|
594
|
+
|
|
528
595
|
// Add skills locally
|
|
529
596
|
t2v.skills.add({
|
|
530
597
|
name: 'radiology-workflow',
|
|
@@ -588,8 +655,9 @@ input.addEventListener('input', () => t2v.warmUp(), { once: true });
|
|
|
588
655
|
|
|
589
656
|
It is safe to call repeatedly (one request per signed-in end-user), never
|
|
590
657
|
throws, and is a no-op against an engine that predates it. Don't call it when
|
|
591
|
-
the chat merely opens: for a logged-out visitor it starts
|
|
592
|
-
|
|
658
|
+
the chat merely opens: for a logged-out visitor it starts a guest session
|
|
659
|
+
wherever your key allows guests, and each one counts toward your daily guest
|
|
660
|
+
limit.
|
|
593
661
|
|
|
594
662
|
`<ChatWidget>`, `<ChatPanel>` and the assistant-ui runtime already do this.
|
|
595
663
|
|
|
@@ -664,6 +732,7 @@ A tool needs a **schema** (so the AI knows when and how to call it) and an **exe
|
|
|
664
732
|
```typescript
|
|
665
733
|
import type { ClientTool } from '@talk2view/sdk';
|
|
666
734
|
|
|
735
|
+
// Fictional data. Never use real patient data.
|
|
667
736
|
const myTool: ClientTool = {
|
|
668
737
|
name: 'set_window_level',
|
|
669
738
|
description: 'Set the brightness and contrast of the medical image viewer',
|
|
@@ -747,8 +816,16 @@ Stored keys:
|
|
|
747
816
|
- `talk2view_refresh_token`
|
|
748
817
|
- `talk2view_user`
|
|
749
818
|
- `talk2view_user_api_key`
|
|
819
|
+
- `talk2view_is_anonymous`
|
|
820
|
+
|
|
821
|
+
On logout, all of these are cleared and a `talk2view_auth_cleared` event is dispatched on `window` for cross-component synchronization.
|
|
822
|
+
|
|
823
|
+
Logging out leaves the rest of what the SDK keeps in `localStorage`:
|
|
750
824
|
|
|
751
|
-
|
|
825
|
+
- `talk2view_preferences` — Settings choices: model, dictation (speech-to-text) model and language, font size.
|
|
826
|
+
- `t2v_chat_conversations_v1:<partner key>` — the packaged chat's earlier conversations, one bucket per end-user. See [where conversations are kept](https://github.com/Talk2View/talk2view-typescript/blob/main/docs/chat.md#conversations-are-kept-in-this-browser).
|
|
827
|
+
- `t2v_user_skills` and `t2v_disabled_skills` — skills saved in the chat's Skills view or with `t2v.skills.save()`. They are not divided by end-user: in the packaged chat, whoever signs in next on that browser sees them and sends them with their chats.
|
|
828
|
+
- `t2v-launcher-size`, `t2v-launcher-opened` and `t2v-launcher-colourway` — the launcher's size, whether it has been opened, and the colourway a visitor picked.
|
|
752
829
|
|
|
753
830
|
### Security Considerations
|
|
754
831
|
|
|
@@ -761,13 +838,13 @@ To mitigate this:
|
|
|
761
838
|
- **Always serve your application over HTTPS**
|
|
762
839
|
- **Sanitize user-generated content** to prevent XSS injection
|
|
763
840
|
|
|
764
|
-
**CSRF
|
|
841
|
+
**CORS and CSRF:** The engine accepts cross-origin requests from any website (`Access-Control-Allow-Origin: *`), so CORS protects neither your key nor your users. Your key needs no protecting: it is a public identifier. What protects user data is the end-user's sign-in token, which the SDK sends in the `Authorization` header, never in a cookie, so a page on another site cannot make the browser send it.
|
|
765
842
|
|
|
766
843
|
---
|
|
767
844
|
|
|
768
845
|
## Rendering model replies
|
|
769
846
|
|
|
770
|
-
Treat every model reply as untrusted. Prompt injection through web search, documents, email or tool results can make the model write anything. `<ChatPanel>` renders replies as markdown only, and
|
|
847
|
+
Treat every model reply as untrusted. Prompt injection through web search, documents, email or tool results can make the model write anything. `<ChatPanel>` renders replies as markdown only, and nothing in a reply loads remote content on its own:
|
|
771
848
|
|
|
772
849
|
- **Raw HTML** in a reply is shown as text, apart from a bare `<br>`, which is kept so a GFM table cell can break a line (a cell can't hold a literal newline).
|
|
773
850
|
- **Images** become links (`Image: <alt> (<host>)`) that the end-user can choose to open.
|
|
@@ -860,7 +937,7 @@ const { error: chatError } = useT2VChat({ systemPrompt: '...' });
|
|
|
860
937
|
| `baseUrl` | `string` | No | `'https://engine.talk2view.com'` | Talk2View engine URL |
|
|
861
938
|
| `model` | `string` | No | Server default | LLM model to use |
|
|
862
939
|
| `requestTimeout` | `number` | No | `30000` | HTTP request timeout in ms. Does not apply to SSE streams after connection. |
|
|
863
|
-
| `anonymousAutoStart` | `boolean` | No | `true` |
|
|
940
|
+
| `anonymousAutoStart` | `boolean` | No | `true` | When nobody is signed in, try to start a guest session the first time a call needs one (sending, uploading, transcribing, listing models, `warmUp()`, voice). It succeeds only where your key allows guests; a refusal emits `anonymousUnavailable`. Set `false` to require sign-in. |
|
|
864
941
|
| `debug` | `boolean` | No | `false` | Log streaming events, tool calls, history and state changes to the console. |
|
|
865
942
|
|
|
866
943
|
---
|
|
@@ -870,6 +947,7 @@ const { error: chatError } = useT2VChat({ systemPrompt: '...' });
|
|
|
870
947
|
System prompts set the AI's behavior and context for your application. Pass one to `<Talk2View>` (the UI components) or to `useT2VChat` (the hooks):
|
|
871
948
|
|
|
872
949
|
```tsx
|
|
950
|
+
// Fictional data. Never use real patient data.
|
|
873
951
|
<Talk2View
|
|
874
952
|
partnerKey="pk_live_..."
|
|
875
953
|
systemPrompt="You are a medical imaging assistant. Use the provided tools to control the DICOM viewer."
|
|
@@ -1019,8 +1097,8 @@ The source is at
|
|
|
1019
1097
|
[github.com/Talk2View/talk2view-typescript](https://github.com/Talk2View/talk2view-typescript).
|
|
1020
1098
|
Bugs and feature requests go in
|
|
1021
1099
|
[Issues](https://github.com/Talk2View/talk2view-typescript/issues); pull
|
|
1022
|
-
requests are welcome, and [CONTRIBUTING.md](CONTRIBUTING.md) says how
|
|
1023
|
-
|
|
1100
|
+
requests are welcome, and [CONTRIBUTING.md](CONTRIBUTING.md) says how to get
|
|
1101
|
+
set up and what a reviewer looks for.
|
|
1024
1102
|
|
|
1025
1103
|
Security problems do **not** go in an issue — see
|
|
1026
1104
|
[SECURITY.md](SECURITY.md).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/assistant-ui/index.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAEV,gBAAgB,EAIhB,8BAA8B,EAE/B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,KAAK,EAEV,UAAU,EACV,gBAAgB,EAGhB,SAAS,EACV,MAAM,aAAa,CAAC;AAErB,OAAO,EAAmC,KAAK,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AACjG,YAAY,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAExF,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACrF,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,+BAA+B,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAC1F,YAAY,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAEhE,gDAAgD;AAChD,MAAM,WAAW,uBAAuB;IACtC,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,KAAK,CAAC,EAAE,CAAC,gBAAgB,GAAG,UAAU,CAAC,EAAE,CAAC;IAC1C;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,yBAAyB,CAAC;IAChD;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,8BAA8B,CAAC;CAC7C;AAED,MAAM,WAAW,0BAA2B,SAAQ,SAAS,EAAE,uBAAuB;CAAG;AAKzF,0EAA0E;AAC1E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,gBAAgB,CA6BzF;AAED,kGAAkG;AAClG,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,SAAS,EACd,EACE,YAAY,EACZ,KAAK,EACL,KAAK,EACL,SAAS,EACT,WAAW,EAAE,kBAAyB,EACtC,UAAU,GACX,GAAE,uBAA4B,GAC9B,gBAAgB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/assistant-ui/index.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAEV,gBAAgB,EAIhB,8BAA8B,EAE/B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,KAAK,EAEV,UAAU,EACV,gBAAgB,EAGhB,SAAS,EACV,MAAM,aAAa,CAAC;AAErB,OAAO,EAAmC,KAAK,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AACjG,YAAY,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAExF,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACrF,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,+BAA+B,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAC1F,YAAY,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAEhE,gDAAgD;AAChD,MAAM,WAAW,uBAAuB;IACtC,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,KAAK,CAAC,EAAE,CAAC,gBAAgB,GAAG,UAAU,CAAC,EAAE,CAAC;IAC1C;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,yBAAyB,CAAC;IAChD;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,8BAA8B,CAAC;CAC7C;AAED,MAAM,WAAW,0BAA2B,SAAQ,SAAS,EAAE,uBAAuB;CAAG;AAKzF,0EAA0E;AAC1E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,gBAAgB,CA6BzF;AAED,kGAAkG;AAClG,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,SAAS,EACd,EACE,YAAY,EACZ,KAAK,EACL,KAAK,EACL,SAAS,EACT,WAAW,EAAE,kBAAyB,EACtC,UAAU,GACX,GAAE,uBAA4B,GAC9B,gBAAgB,CA8NlB"}
|
|
@@ -103,10 +103,12 @@ export function useTalk2ViewRuntimeForClient(t2v, { systemPrompt, tools, model,
|
|
|
103
103
|
registered.current = { t2v, key: schemaKey };
|
|
104
104
|
t2v.tools.register(schemas).catch(() => { });
|
|
105
105
|
}
|
|
106
|
-
// Handlers AFTER register()
|
|
107
|
-
//
|
|
108
|
-
// wiped. StrictMode's second effect run hid that
|
|
109
|
-
// production build runs once, and every tool answered
|
|
106
|
+
// Handlers AFTER register(). These schemas carry no `execute`, and
|
|
107
|
+
// register() used to rebuild the handler map from what it was given, so
|
|
108
|
+
// handlers added first were wiped. StrictMode's second effect run hid that
|
|
109
|
+
// in development; a production build runs once, and every tool answered
|
|
110
|
+
// "Unknown tool". register() keeps a listed tool's handler now, and this
|
|
111
|
+
// order is right either way.
|
|
110
112
|
// Re-added on every run, so a new `execute` closure always takes effect.
|
|
111
113
|
for (const tool of tools ?? []) {
|
|
112
114
|
if ('execute' in tool && tool.execute)
|
|
@@ -152,11 +154,14 @@ export function useTalk2ViewRuntimeForClient(t2v, { systemPrompt, tools, model,
|
|
|
152
154
|
const dictationAdapter = useMemo(() => (dictationEnabled ? createTalk2ViewDictationAdapter(t2v, () => dictationOptions.current) : null), [t2v, dictationEnabled]);
|
|
153
155
|
// A turn that fails before its first text chunk leaves no assistant message
|
|
154
156
|
// behind (the SDK drops the empty one), and assistant-ui can only show an
|
|
155
|
-
// error on an assistant message — so give it one to show it on.
|
|
157
|
+
// error on an assistant message — so give it one to show it on. An error
|
|
158
|
+
// with nothing on screen at all gets one too: an account whose session died
|
|
159
|
+
// takes its conversation with it, and the error is all that says why.
|
|
156
160
|
const threadMessages = useMemo(() => {
|
|
157
161
|
const last = messages[messages.length - 1];
|
|
158
|
-
if (error && last
|
|
159
|
-
|
|
162
|
+
if (error && last?.role !== 'assistant') {
|
|
163
|
+
const id = last ? `${last.id}:error` : 't2v-error';
|
|
164
|
+
return [...messages, { id, role: 'assistant', content: '', timestamp: new Date() }];
|
|
160
165
|
}
|
|
161
166
|
return messages;
|
|
162
167
|
}, [messages, error]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/assistant-ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC1E,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAU9D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AASpD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,+BAA+B,EAAkC,MAAM,gBAAgB,CAAC;AAGjG,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAErF,OAAO,EAAE,+BAA+B,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AA4C1F,oEAAoE;AACpE,MAAM,2BAA2B,GAAG,uBAAuB,CAAC;AAE5D,0EAA0E;AAC1E,MAAM,UAAU,mBAAmB,CAAC,OAAmC;IACrE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC;IAC/F,2EAA2E;IAC3E,wEAAwE;IACxE,yDAAyD;IACzD,4EAA4E;IAC5E,+EAA+E;IAC/E,+EAA+E;IAC/E,gBAAgB;IAChB,MAAM,GAAG,GAAG,OAAO,CACjB,GAAG,EAAE;QACH,MAAM,IAAI,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,uDAAuD;IACvD,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,kBAAkB,CAAC,CAC7E,CAAC;IACF,MAAM,OAAO,GAAG,4BAA4B,CAAC,GAAG,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC;IACvH,0EAA0E;IAC1E,yEAAyE;IACzE,+EAA+E;IAC/E,6EAA6E;IAC7E,2DAA2D;IAC3D,SAAS,CAAC,GAAG,EAAE;QACb,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACV,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,4BAA4B,CAC1C,GAAc,EACd,EACE,YAAY,EACZ,KAAK,EACL,KAAK,EACL,SAAS,EACT,WAAW,EAAE,kBAAkB,GAAG,IAAI,EACtC,UAAU,MACiB,EAAE;IAE/B,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAmB,GAAG,CAAC,QAAQ,CAAC,CAAC;IACzE,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC1D,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,GAAG,CAAC,KAAK,CAAC,CAAC;IAC7D,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,CAAyB,GAAG,CAAC,eAAe,CAAC,CAAC;IAEpG,SAAS,CAAC,GAAG,EAAE;QACb,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1B,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC5B,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpB,kBAAkB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG;YACb,GAAG,CAAC,EAAE,CAAC,gBAAgB,EAAE,WAAW,CAAC;YACrC,GAAG,CAAC,EAAE,CAAC,eAAe,EAAE,YAAY,CAAC;YACrC,GAAG,CAAC,EAAE,CAAC,aAAa,EAAE,QAAQ,CAAC;YAC/B,GAAG,CAAC,EAAE,CAAC,gBAAgB,EAAE,kBAAkB,CAAC;SAC7C,CAAC;QACF,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAC1C,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAEV,2EAA2E;IAC3E,uEAAuE;IACvE,4EAA4E;IAC5E,MAAM,OAAO,GAAG,OAAO,CACrB,GAAG,EAAE,CACH,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACzB,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,GAAG,IAAkB,CAAC;QAC5D,OAAO,MAA0B,CAAC;IACpC,CAAC,CAAC,EACJ,CAAC,KAAK,CAAC,CACR,CAAC;IACF,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE,MAAM,UAAU,GAAG,MAAM,CAAyC,IAAI,CAAC,CAAC;IACxE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACjC,IAAI,UAAU,CAAC,OAAO,EAAE,GAAG,KAAK,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC5E,UAAU,CAAC,OAAO,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;YAC7C,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC9C,CAAC;QACD,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/assistant-ui/index.ts"],"names":[],"mappings":"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"}
|
package/dist/auth.d.ts
CHANGED
|
@@ -10,10 +10,22 @@ export declare class T2VAuth {
|
|
|
10
10
|
private listeners;
|
|
11
11
|
private readonly onAuthCleared;
|
|
12
12
|
/**
|
|
13
|
-
* Cross-tab
|
|
14
|
-
* when localStorage changes in one tab.
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* Cross-tab auth sync. The native `storage` event fires in *other* tabs
|
|
14
|
+
* when localStorage changes in one tab.
|
|
15
|
+
*
|
|
16
|
+
* - When a sibling tab logs out (or a dead-refresh-token 401 clears auth),
|
|
17
|
+
* the access-token key is removed/nulled; this tab flips to logged-out too.
|
|
18
|
+
* - When a sibling tab signs a different end-user in, the user key changes to
|
|
19
|
+
* someone else. Every request this tab makes reads its token from storage,
|
|
20
|
+
* so it is already acting as them; announcing it is what makes the rest of
|
|
21
|
+
* this tab agree — above all, it takes the last account's conversation off
|
|
22
|
+
* the screen instead of replaying it under the new account.
|
|
23
|
+
* - When it signs the same end-user in again, that is most often a guest
|
|
24
|
+
* signing in to the account they converted to, which keeps the user id.
|
|
25
|
+
* Announcing it tells this tab it holds an account now, not a guest, so
|
|
26
|
+
* the conversation is treated as an account's from then on.
|
|
27
|
+
*
|
|
28
|
+
* A token refresh never writes the user key.
|
|
17
29
|
*/
|
|
18
30
|
private readonly onStorage;
|
|
19
31
|
/** Whether the window listeners below are currently registered. */
|
|
@@ -127,6 +139,13 @@ export declare class T2VAuth {
|
|
|
127
139
|
* Subscribe to auth state changes.
|
|
128
140
|
*/
|
|
129
141
|
onAuthStateChange(callback: AuthStateCallback): () => void;
|
|
142
|
+
/**
|
|
143
|
+
* Write a new session. The user goes last: another tab hears a sign-in as
|
|
144
|
+
* the user key changing (see {@link onStorage}) and reads the rest of the
|
|
145
|
+
* session there and then — whether this is a guest above all, which decides
|
|
146
|
+
* whether the conversation on its screen may follow — so everything it reads
|
|
147
|
+
* is written before it.
|
|
148
|
+
*/
|
|
130
149
|
private storeTokens;
|
|
131
150
|
private notifyListeners;
|
|
132
151
|
}
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAgB7C,OAAO,KAAK,EAAgB,aAAa,EAAgC,IAAI,EAAE,MAAM,YAAY,CAAC;AAElG,KAAK,iBAAiB,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;AAErD,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,OAAO,CAAC;AA+C/C,qBAAa,OAAO;IA2CN,OAAO,CAAC,QAAQ,CAAC,MAAM;IA1CnC,OAAO,CAAC,SAAS,CAAqC;IAGtD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAE5B;IAEF;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CASxB;IAEF,mEAAmE;IACnE,OAAO,CAAC,SAAS,CAAS;IAE1B,sEAAsE;IACtE,OAAO,CAAC,WAAW,CAAS;gBAEC,MAAM,EAAE,SAAS;IAO9C;;;;;;;;;;;;;;OAcG;IACH,MAAM,IAAI,IAAI;IASd;;;OAGG;IACH,OAAO,IAAI,IAAI;IAOf,oDAAoD;IAC9C,cAAc,CAAC,IAAI,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAcrE,gEAAgE;IAChE,WAAW,IAAI,OAAO;IAItB;;;;;;;OAOG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAiCrE;;OAEG;IACG,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB3D,OAAO,CAAC,UAAU,CAAC,CAAkB;IAIrC,OAAO,CAAC,cAAc,CAAyC;IAE/D;;;;;;OAMG;IACH,iBAAiB,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IA4C7C,+DAA+D;IACzD,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvC;;;;;OAKG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAIhC,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAoCxC,iBAAiB;IA4D/B,mDAAmD;IACnD,OAAO,CAAC,KAAK;IAkBb;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB7B;;;;OAIG;IACH,IAAI,UAAU,IAAI,OAAO,CAExB;IAED;;OAEG;IACH,OAAO,IAAI,IAAI,GAAG,IAAI;IAItB;;OAEG;IACH,eAAe,IAAI,OAAO;IAI1B;;;;;;;;;;;;;;;;;OAiBG;IACG,mBAAmB,CAAC,IAAI,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAiBpF;;OAEG;IACH,iBAAiB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,IAAI;IAO1D;;;;;;OAMG;IACH,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,eAAe;CAKxB"}
|
package/dist/auth.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Auth module — login, signup, refresh, logout, auth state management.
|
|
3
3
|
*/
|
|
4
4
|
import { AuthenticationError, T2VError } from './errors.js';
|
|
5
|
-
import { ACCESS_TOKEN_STORAGE_KEY, clearAuth, getAccessToken, getIsAnonymous, getUser, hasValidTokens, setAccessToken, setIsAnonymous, setUserApiKey, setRefreshToken, setUser, } from './storage.js';
|
|
5
|
+
import { ACCESS_TOKEN_STORAGE_KEY, USER_STORAGE_KEY, clearAuth, getAccessToken, getIsAnonymous, getUser, hasValidTokens, setAccessToken, setIsAnonymous, setUserApiKey, setRefreshToken, setUser, } from './storage.js';
|
|
6
6
|
// Refresh a token this many seconds before it actually expires, so a request
|
|
7
7
|
// fired right at the boundary doesn't race the expiry against clock skew.
|
|
8
8
|
const TOKEN_EXPIRY_SKEW_SECONDS = 30;
|
|
@@ -18,6 +18,17 @@ function isExpiringSoon(token, skewSeconds = TOKEN_EXPIRY_SKEW_SECONDS) {
|
|
|
18
18
|
return false;
|
|
19
19
|
return exp - Date.now() / 1000 <= skewSeconds;
|
|
20
20
|
}
|
|
21
|
+
/** The stored end-user, or null when there is none or it cannot be read. */
|
|
22
|
+
function parseUser(raw) {
|
|
23
|
+
if (!raw)
|
|
24
|
+
return null;
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(raw);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
21
32
|
function decodeJwtExp(token) {
|
|
22
33
|
const payloadPart = token.split('.')[1];
|
|
23
34
|
if (!payloadPart || typeof atob !== 'function')
|
|
@@ -40,15 +51,32 @@ export class T2VAuth {
|
|
|
40
51
|
this.notifyListeners(null);
|
|
41
52
|
};
|
|
42
53
|
/**
|
|
43
|
-
* Cross-tab
|
|
44
|
-
* when localStorage changes in one tab.
|
|
45
|
-
*
|
|
46
|
-
*
|
|
54
|
+
* Cross-tab auth sync. The native `storage` event fires in *other* tabs
|
|
55
|
+
* when localStorage changes in one tab.
|
|
56
|
+
*
|
|
57
|
+
* - When a sibling tab logs out (or a dead-refresh-token 401 clears auth),
|
|
58
|
+
* the access-token key is removed/nulled; this tab flips to logged-out too.
|
|
59
|
+
* - When a sibling tab signs a different end-user in, the user key changes to
|
|
60
|
+
* someone else. Every request this tab makes reads its token from storage,
|
|
61
|
+
* so it is already acting as them; announcing it is what makes the rest of
|
|
62
|
+
* this tab agree — above all, it takes the last account's conversation off
|
|
63
|
+
* the screen instead of replaying it under the new account.
|
|
64
|
+
* - When it signs the same end-user in again, that is most often a guest
|
|
65
|
+
* signing in to the account they converted to, which keeps the user id.
|
|
66
|
+
* Announcing it tells this tab it holds an account now, not a guest, so
|
|
67
|
+
* the conversation is treated as an account's from then on.
|
|
68
|
+
*
|
|
69
|
+
* A token refresh never writes the user key.
|
|
47
70
|
*/
|
|
48
71
|
onStorage = (event) => {
|
|
49
|
-
// Only react to our access-token key being cleared (removed → newValue null).
|
|
50
72
|
if (event.key === ACCESS_TOKEN_STORAGE_KEY && event.newValue === null) {
|
|
51
73
|
this.notifyListeners(null);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (event.key === USER_STORAGE_KEY && event.newValue !== null) {
|
|
77
|
+
const next = parseUser(event.newValue);
|
|
78
|
+
if (next)
|
|
79
|
+
this.notifyListeners(next);
|
|
52
80
|
}
|
|
53
81
|
};
|
|
54
82
|
/** Whether the window listeners below are currently registered. */
|
|
@@ -101,8 +129,7 @@ export class T2VAuth {
|
|
|
101
129
|
async startAnonymous(opts) {
|
|
102
130
|
const body = opts?.captchaToken ? { captcha_token: opts.captchaToken } : {};
|
|
103
131
|
const response = await this.client.request('/v1/auth/anonymous', { method: 'POST', body: JSON.stringify(body) }, false);
|
|
104
|
-
this.storeTokens(response);
|
|
105
|
-
setIsAnonymous(true);
|
|
132
|
+
this.storeTokens(response, true);
|
|
106
133
|
const user = response.user;
|
|
107
134
|
if (!user)
|
|
108
135
|
throw new AuthenticationError('Anonymous sign-in returned no user');
|
|
@@ -371,15 +398,7 @@ export class T2VAuth {
|
|
|
371
398
|
* Get the current user (from storage).
|
|
372
399
|
*/
|
|
373
400
|
getUser() {
|
|
374
|
-
|
|
375
|
-
if (!raw)
|
|
376
|
-
return null;
|
|
377
|
-
try {
|
|
378
|
-
return JSON.parse(raw);
|
|
379
|
-
}
|
|
380
|
-
catch {
|
|
381
|
-
return null;
|
|
382
|
-
}
|
|
401
|
+
return parseUser(getUser());
|
|
383
402
|
}
|
|
384
403
|
/**
|
|
385
404
|
* Check if a user is currently authenticated.
|
|
@@ -431,16 +450,23 @@ export class T2VAuth {
|
|
|
431
450
|
this.listeners.delete(callback);
|
|
432
451
|
};
|
|
433
452
|
}
|
|
434
|
-
|
|
453
|
+
/**
|
|
454
|
+
* Write a new session. The user goes last: another tab hears a sign-in as
|
|
455
|
+
* the user key changing (see {@link onStorage}) and reads the rest of the
|
|
456
|
+
* session there and then — whether this is a guest above all, which decides
|
|
457
|
+
* whether the conversation on its screen may follow — so everything it reads
|
|
458
|
+
* is written before it.
|
|
459
|
+
*/
|
|
460
|
+
storeTokens(response, anonymous = response.is_anonymous === true) {
|
|
461
|
+
setIsAnonymous(anonymous);
|
|
435
462
|
setAccessToken(response.access_token);
|
|
436
463
|
setRefreshToken(response.refresh_token);
|
|
437
|
-
if (response.user) {
|
|
438
|
-
setUser(response.user);
|
|
439
|
-
}
|
|
440
464
|
if (response.user_api_key) {
|
|
441
465
|
setUserApiKey(response.user_api_key);
|
|
442
466
|
}
|
|
443
|
-
|
|
467
|
+
if (response.user) {
|
|
468
|
+
setUser(response.user);
|
|
469
|
+
}
|
|
444
470
|
}
|
|
445
471
|
notifyListeners(user) {
|
|
446
472
|
for (const listener of this.listeners) {
|