@talk2view/sdk 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/README.md +336 -69
  2. package/dist/assistant-ui/convert.d.ts +40 -0
  3. package/dist/assistant-ui/convert.d.ts.map +1 -0
  4. package/dist/assistant-ui/convert.js +113 -0
  5. package/dist/assistant-ui/convert.js.map +1 -0
  6. package/dist/assistant-ui/dictation.d.ts +39 -0
  7. package/dist/assistant-ui/dictation.d.ts.map +1 -0
  8. package/dist/assistant-ui/dictation.js +128 -0
  9. package/dist/assistant-ui/dictation.js.map +1 -0
  10. package/dist/assistant-ui/index.d.ts +43 -0
  11. package/dist/assistant-ui/index.d.ts.map +1 -0
  12. package/dist/assistant-ui/index.js +227 -0
  13. package/dist/assistant-ui/index.js.map +1 -0
  14. package/dist/auth.d.ts +38 -3
  15. package/dist/auth.d.ts.map +1 -1
  16. package/dist/auth.js +101 -16
  17. package/dist/auth.js.map +1 -1
  18. package/dist/chat/auth-gate.d.ts +42 -0
  19. package/dist/chat/auth-gate.d.ts.map +1 -0
  20. package/dist/chat/auth-gate.js +93 -0
  21. package/dist/chat/auth-gate.js.map +1 -0
  22. package/dist/chat/chat.d.ts +36 -0
  23. package/dist/chat/chat.d.ts.map +1 -0
  24. package/dist/chat/chat.js +124 -0
  25. package/dist/chat/chat.js.map +1 -0
  26. package/dist/chat/composer-extras.d.ts +23 -0
  27. package/dist/chat/composer-extras.d.ts.map +1 -0
  28. package/dist/chat/composer-extras.js +48 -0
  29. package/dist/chat/composer-extras.js.map +1 -0
  30. package/dist/chat/hooks/use-attachment-src.d.ts +2 -0
  31. package/dist/chat/hooks/use-attachment-src.d.ts.map +1 -0
  32. package/dist/chat/hooks/use-attachment-src.js +38 -0
  33. package/dist/chat/hooks/use-attachment-src.js.map +1 -0
  34. package/dist/chat/hooks/use-copy-to-clipboard.d.ts +8 -0
  35. package/dist/chat/hooks/use-copy-to-clipboard.d.ts.map +1 -0
  36. package/dist/chat/hooks/use-copy-to-clipboard.js +16 -0
  37. package/dist/chat/hooks/use-copy-to-clipboard.js.map +1 -0
  38. package/dist/chat/index.d.ts +32 -0
  39. package/dist/chat/index.d.ts.map +1 -0
  40. package/dist/chat/index.js +35 -0
  41. package/dist/chat/index.js.map +1 -0
  42. package/dist/chat/inject-styles.d.ts +2 -0
  43. package/dist/chat/inject-styles.d.ts.map +1 -0
  44. package/dist/chat/inject-styles.js +23 -0
  45. package/dist/chat/inject-styles.js.map +1 -0
  46. package/dist/chat/launcher.d.ts +37 -0
  47. package/dist/chat/launcher.d.ts.map +1 -0
  48. package/dist/chat/launcher.js +343 -0
  49. package/dist/chat/launcher.js.map +1 -0
  50. package/dist/chat/lib/cn.d.ts +19 -0
  51. package/dist/chat/lib/cn.d.ts.map +1 -0
  52. package/dist/chat/lib/cn.js +20 -0
  53. package/dist/chat/lib/cn.js.map +1 -0
  54. package/dist/chat/lib/dictation-phase.d.ts +9 -0
  55. package/dist/chat/lib/dictation-phase.d.ts.map +1 -0
  56. package/dist/chat/lib/dictation-phase.js +33 -0
  57. package/dist/chat/lib/dictation-phase.js.map +1 -0
  58. package/dist/chat/lib/guard.d.ts +4 -0
  59. package/dist/chat/lib/guard.d.ts.map +1 -0
  60. package/dist/chat/lib/guard.js +41 -0
  61. package/dist/chat/lib/guard.js.map +1 -0
  62. package/dist/chat/lib/launcher-variant.d.ts +33 -0
  63. package/dist/chat/lib/launcher-variant.d.ts.map +1 -0
  64. package/dist/chat/lib/launcher-variant.js +109 -0
  65. package/dist/chat/lib/launcher-variant.js.map +1 -0
  66. package/dist/chat/lib/portal-host.d.ts +13 -0
  67. package/dist/chat/lib/portal-host.d.ts.map +1 -0
  68. package/dist/chat/lib/portal-host.js +58 -0
  69. package/dist/chat/lib/portal-host.js.map +1 -0
  70. package/dist/chat/markdown.d.ts +32 -0
  71. package/dist/chat/markdown.d.ts.map +1 -0
  72. package/dist/chat/markdown.js +18 -0
  73. package/dist/chat/markdown.js.map +1 -0
  74. package/dist/chat/provider.d.ts +113 -0
  75. package/dist/chat/provider.d.ts.map +1 -0
  76. package/dist/chat/provider.js +176 -0
  77. package/dist/chat/provider.js.map +1 -0
  78. package/dist/chat/tool-fallback.d.ts +39 -0
  79. package/dist/chat/tool-fallback.d.ts.map +1 -0
  80. package/dist/chat/tool-fallback.js +167 -0
  81. package/dist/chat/tool-fallback.js.map +1 -0
  82. package/dist/chat/ui/avatar.d.ts +12 -0
  83. package/dist/chat/ui/avatar.d.ts.map +1 -0
  84. package/dist/chat/ui/avatar.js +24 -0
  85. package/dist/chat/ui/avatar.js.map +1 -0
  86. package/dist/chat/ui/button.d.ts +9 -0
  87. package/dist/chat/ui/button.d.ts.map +1 -0
  88. package/dist/chat/ui/button.js +35 -0
  89. package/dist/chat/ui/button.js.map +1 -0
  90. package/dist/chat/ui/collapsible.d.ts +6 -0
  91. package/dist/chat/ui/collapsible.d.ts.map +1 -0
  92. package/dist/chat/ui/collapsible.js +13 -0
  93. package/dist/chat/ui/collapsible.js.map +1 -0
  94. package/dist/chat/ui/dialog.d.ts +18 -0
  95. package/dist/chat/ui/dialog.d.ts.map +1 -0
  96. package/dist/chat/ui/dialog.js +42 -0
  97. package/dist/chat/ui/dialog.js.map +1 -0
  98. package/dist/chat/ui/input.d.ts +4 -0
  99. package/dist/chat/ui/input.d.ts.map +1 -0
  100. package/dist/chat/ui/input.js +8 -0
  101. package/dist/chat/ui/input.js.map +1 -0
  102. package/dist/chat/ui/skeleton.d.ts +3 -0
  103. package/dist/chat/ui/skeleton.d.ts.map +1 -0
  104. package/dist/chat/ui/skeleton.js +7 -0
  105. package/dist/chat/ui/skeleton.js.map +1 -0
  106. package/dist/chat/ui/textarea.d.ts +4 -0
  107. package/dist/chat/ui/textarea.d.ts.map +1 -0
  108. package/dist/chat/ui/textarea.js +7 -0
  109. package/dist/chat/ui/textarea.js.map +1 -0
  110. package/dist/chat/ui/tooltip.d.ts +7 -0
  111. package/dist/chat/ui/tooltip.d.ts.map +1 -0
  112. package/dist/chat/ui/tooltip.js +22 -0
  113. package/dist/chat/ui/tooltip.js.map +1 -0
  114. package/dist/chat/vendor/attachment.aui.d.ts +5 -0
  115. package/dist/chat/vendor/attachment.aui.d.ts.map +1 -0
  116. package/dist/chat/vendor/attachment.aui.js +79 -0
  117. package/dist/chat/vendor/attachment.aui.js.map +1 -0
  118. package/dist/chat/vendor/file.d.ts +41 -0
  119. package/dist/chat/vendor/file.d.ts.map +1 -0
  120. package/dist/chat/vendor/file.js +137 -0
  121. package/dist/chat/vendor/file.js.map +1 -0
  122. package/dist/chat/vendor/follow-up-suggestions.aui.d.ts +3 -0
  123. package/dist/chat/vendor/follow-up-suggestions.aui.d.ts.map +1 -0
  124. package/dist/chat/vendor/follow-up-suggestions.aui.js +45 -0
  125. package/dist/chat/vendor/follow-up-suggestions.aui.js.map +1 -0
  126. package/dist/chat/vendor/image.d.ts +47 -0
  127. package/dist/chat/vendor/image.d.ts.map +1 -0
  128. package/dist/chat/vendor/image.js +258 -0
  129. package/dist/chat/vendor/image.js.map +1 -0
  130. package/dist/chat/vendor/markdown-text.d.ts +8 -0
  131. package/dist/chat/vendor/markdown-text.d.ts.map +1 -0
  132. package/dist/chat/vendor/markdown-text.js +73 -0
  133. package/dist/chat/vendor/markdown-text.js.map +1 -0
  134. package/dist/chat/vendor/reasoning.aui.d.ts +22 -0
  135. package/dist/chat/vendor/reasoning.aui.d.ts.map +1 -0
  136. package/dist/chat/vendor/reasoning.aui.js +56 -0
  137. package/dist/chat/vendor/reasoning.aui.js.map +1 -0
  138. package/dist/chat/vendor/reasoning.d.ts +35 -0
  139. package/dist/chat/vendor/reasoning.d.ts.map +1 -0
  140. package/dist/chat/vendor/reasoning.js +112 -0
  141. package/dist/chat/vendor/reasoning.js.map +1 -0
  142. package/dist/chat/vendor/thread-list.aui.d.ts +37 -0
  143. package/dist/chat/vendor/thread-list.aui.d.ts.map +1 -0
  144. package/dist/chat/vendor/thread-list.aui.js +154 -0
  145. package/dist/chat/vendor/thread-list.aui.js.map +1 -0
  146. package/dist/chat/vendor/thread.aui.d.ts +47 -0
  147. package/dist/chat/vendor/thread.aui.d.ts.map +1 -0
  148. package/dist/chat/vendor/thread.aui.js +176 -0
  149. package/dist/chat/vendor/thread.aui.js.map +1 -0
  150. package/dist/chat/vendor/tool-fallback.aui.d.ts +39 -0
  151. package/dist/chat/vendor/tool-fallback.aui.d.ts.map +1 -0
  152. package/dist/chat/vendor/tool-fallback.aui.js +251 -0
  153. package/dist/chat/vendor/tool-fallback.aui.js.map +1 -0
  154. package/dist/chat/vendor/tool-group.aui.d.ts +34 -0
  155. package/dist/chat/vendor/tool-group.aui.d.ts.map +1 -0
  156. package/dist/chat/vendor/tool-group.aui.js +60 -0
  157. package/dist/chat/vendor/tool-group.aui.js.map +1 -0
  158. package/dist/chat/vendor/tooltip-icon-button.d.ts +8 -0
  159. package/dist/chat/vendor/tooltip-icon-button.d.ts.map +1 -0
  160. package/dist/chat/vendor/tooltip-icon-button.js +11 -0
  161. package/dist/chat/vendor/tooltip-icon-button.js.map +1 -0
  162. package/dist/chat/views/account.d.ts +6 -0
  163. package/dist/chat/views/account.d.ts.map +1 -0
  164. package/dist/chat/views/account.js +128 -0
  165. package/dist/chat/views/account.js.map +1 -0
  166. package/dist/chat/views/settings.d.ts +2 -0
  167. package/dist/chat/views/settings.d.ts.map +1 -0
  168. package/dist/chat/views/settings.js +104 -0
  169. package/dist/chat/views/settings.js.map +1 -0
  170. package/dist/chat/views/thread-list.d.ts +10 -0
  171. package/dist/chat/views/thread-list.d.ts.map +1 -0
  172. package/dist/chat/views/thread-list.js +21 -0
  173. package/dist/chat/views/thread-list.js.map +1 -0
  174. package/dist/chat/welcome.d.ts +10 -0
  175. package/dist/chat/welcome.d.ts.map +1 -0
  176. package/dist/chat/welcome.js +9 -0
  177. package/dist/chat/welcome.js.map +1 -0
  178. package/dist/chat-css.d.ts +1 -0
  179. package/dist/chat-css.js +1 -0
  180. package/dist/chat.css +2 -0
  181. package/dist/chat.isolated.css +3819 -0
  182. package/dist/chat.raw.css +3829 -0
  183. package/dist/client.d.ts +1 -1
  184. package/dist/client.d.ts.map +1 -1
  185. package/dist/client.js +4 -4
  186. package/dist/client.js.map +1 -1
  187. package/dist/errors.d.ts +5 -3
  188. package/dist/errors.d.ts.map +1 -1
  189. package/dist/errors.js +28 -6
  190. package/dist/errors.js.map +1 -1
  191. package/dist/index.d.ts +68 -13
  192. package/dist/index.d.ts.map +1 -1
  193. package/dist/index.js +236 -35
  194. package/dist/index.js.map +1 -1
  195. package/dist/react/T2VProvider.d.ts +2 -2
  196. package/dist/react/T2VProvider.d.ts.map +1 -1
  197. package/dist/react/T2VProvider.js +1 -1
  198. package/dist/react/T2VProvider.js.map +1 -1
  199. package/dist/react/index.d.ts +14 -14
  200. package/dist/react/index.d.ts.map +1 -1
  201. package/dist/react/index.js +7 -7
  202. package/dist/react/index.js.map +1 -1
  203. package/dist/react/usePartnerConfig.d.ts +1 -1
  204. package/dist/react/usePartnerConfig.d.ts.map +1 -1
  205. package/dist/react/usePartnerConfig.js +1 -1
  206. package/dist/react/usePartnerConfig.js.map +1 -1
  207. package/dist/react/useT2VAuth.d.ts +8 -1
  208. package/dist/react/useT2VAuth.d.ts.map +1 -1
  209. package/dist/react/useT2VAuth.js +25 -6
  210. package/dist/react/useT2VAuth.js.map +1 -1
  211. package/dist/react/useT2VChat.d.ts +3 -3
  212. package/dist/react/useT2VChat.d.ts.map +1 -1
  213. package/dist/react/useT2VChat.js +1 -1
  214. package/dist/react/useT2VChat.js.map +1 -1
  215. package/dist/react/useT2VTools.d.ts +1 -1
  216. package/dist/react/useT2VTools.d.ts.map +1 -1
  217. package/dist/react/useT2VTools.js +1 -1
  218. package/dist/react/useT2VTools.js.map +1 -1
  219. package/dist/react/useUserPreferences.d.ts +1 -1
  220. package/dist/react/useUserPreferences.d.ts.map +1 -1
  221. package/dist/react/useUserPreferences.js +1 -1
  222. package/dist/react/useUserPreferences.js.map +1 -1
  223. package/dist/sessions.d.ts +3 -3
  224. package/dist/sessions.d.ts.map +1 -1
  225. package/dist/sessions.js +6 -2
  226. package/dist/sessions.js.map +1 -1
  227. package/dist/skills.d.ts +2 -2
  228. package/dist/skills.d.ts.map +1 -1
  229. package/dist/streaming.d.ts +1 -1
  230. package/dist/streaming.d.ts.map +1 -1
  231. package/dist/tools.d.ts +2 -2
  232. package/dist/tools.d.ts.map +1 -1
  233. package/dist/types.d.ts +119 -2
  234. package/dist/types.d.ts.map +1 -1
  235. package/dist/ui/components/ApprovalCard.d.ts +1 -1
  236. package/dist/ui/components/ApprovalCard.d.ts.map +1 -1
  237. package/dist/ui/components/ChatHeader.js +1 -1
  238. package/dist/ui/components/ChatHeader.js.map +1 -1
  239. package/dist/ui/components/ChatPanel.d.ts +1 -1
  240. package/dist/ui/components/ChatPanel.d.ts.map +1 -1
  241. package/dist/ui/components/ChatPanel.js +14 -13
  242. package/dist/ui/components/ChatPanel.js.map +1 -1
  243. package/dist/ui/components/ChatWidget.d.ts +1 -1
  244. package/dist/ui/components/ChatWidget.d.ts.map +1 -1
  245. package/dist/ui/components/ChatWidget.js +2 -2
  246. package/dist/ui/components/ChatWidget.js.map +1 -1
  247. package/dist/ui/components/Composer.d.ts.map +1 -1
  248. package/dist/ui/components/Composer.js +13 -6
  249. package/dist/ui/components/Composer.js.map +1 -1
  250. package/dist/ui/components/LoginForm.d.ts.map +1 -1
  251. package/dist/ui/components/LoginForm.js +19 -5
  252. package/dist/ui/components/LoginForm.js.map +1 -1
  253. package/dist/ui/components/MarkdownRenderer.d.ts +4 -0
  254. package/dist/ui/components/MarkdownRenderer.d.ts.map +1 -1
  255. package/dist/ui/components/MarkdownRenderer.js +7 -35
  256. package/dist/ui/components/MarkdownRenderer.js.map +1 -1
  257. package/dist/ui/components/MessageActions.js +1 -1
  258. package/dist/ui/components/MessageActions.js.map +1 -1
  259. package/dist/ui/components/MessageBubble.d.ts +1 -1
  260. package/dist/ui/components/MessageBubble.d.ts.map +1 -1
  261. package/dist/ui/components/MessageBubble.js +9 -9
  262. package/dist/ui/components/MessageBubble.js.map +1 -1
  263. package/dist/ui/components/MessageList.js +2 -2
  264. package/dist/ui/components/MessageList.js.map +1 -1
  265. package/dist/ui/components/SettingsPanel.js +4 -4
  266. package/dist/ui/components/SettingsPanel.js.map +1 -1
  267. package/dist/ui/components/Talk2View.d.ts +1 -1
  268. package/dist/ui/components/Talk2View.d.ts.map +1 -1
  269. package/dist/ui/components/Talk2View.js +16 -12
  270. package/dist/ui/components/Talk2View.js.map +1 -1
  271. package/dist/ui/components/ThinkingBlock.js +2 -2
  272. package/dist/ui/components/ThinkingBlock.js.map +1 -1
  273. package/dist/ui/components/WelcomeScreen.js +2 -2
  274. package/dist/ui/components/WelcomeScreen.js.map +1 -1
  275. package/dist/ui/context.d.ts +4 -2
  276. package/dist/ui/context.d.ts.map +1 -1
  277. package/dist/ui/context.js.map +1 -1
  278. package/dist/ui/index.d.ts +27 -26
  279. package/dist/ui/index.d.ts.map +1 -1
  280. package/dist/ui/index.js +21 -20
  281. package/dist/ui/index.js.map +1 -1
  282. package/dist/ui/safeMarkdown.d.ts +17 -0
  283. package/dist/ui/safeMarkdown.d.ts.map +1 -0
  284. package/dist/ui/safeMarkdown.js +196 -0
  285. package/dist/ui/safeMarkdown.js.map +1 -0
  286. package/dist/ui/theme.d.ts +1 -1
  287. package/dist/ui/theme.d.ts.map +1 -1
  288. package/dist/ui/utils.d.ts +1 -1
  289. package/dist/ui/utils.d.ts.map +1 -1
  290. package/package.json +64 -5
package/README.md CHANGED
@@ -8,12 +8,51 @@ Add AI-powered natural language control to any application. Talk2View's SDK hand
8
8
  npm install @talk2view/sdk
9
9
  ```
10
10
 
11
+ ## Chat UI
12
+
13
+ Three lines to a working chat. `@talk2view/sdk/chat` is the branded Talk2View
14
+ chat, packaged: your app needs no Tailwind, no PostCSS, no shadcn and no copied
15
+ component files.
16
+
17
+ ```tsx
18
+ import { Talk2ViewChat } from '@talk2view/sdk/chat';
19
+ import '@talk2view/sdk/chat.css';
20
+
21
+ <Talk2ViewChat partnerKey="pk_live_…" tools={tools} welcome={{ suggestions: ['What can you do?'] }} />
22
+ ```
23
+
24
+ That is the full pane: sign-in, streaming replies, attachments, dictation,
25
+ Settings, earlier conversations and tool approvals. Give it a parent with a
26
+ height — it fills its box.
27
+
28
+ For a floating chat instead — a mark in the corner of the page, a popover on a
29
+ desktop, a full-screen sheet on a phone:
30
+
31
+ ```tsx
32
+ import { Talk2ViewChatLauncher } from '@talk2view/sdk/chat';
33
+ import '@talk2view/sdk/chat.css';
34
+
35
+ <Talk2ViewChatLauncher partnerKey="pk_live_…" tools={tools} label="Ask Talk2View" />
36
+ ```
37
+
38
+ Theming is the custom properties on `.t2v-chat` (`--radius`, `--primary`, the
39
+ rest); dark mode is `className="dark"`. The chat fetches nothing at runtime — no
40
+ fonts, no CDN — and uses your page's font unless you pass `fontFamily`.
41
+
42
+ **It is large: 361.8 KB gzip, excluding React.** A consumer who never imports
43
+ `/chat` pays none of it.
44
+
45
+ Everything else — the full prop table, theming tokens, the launcher in a hostile
46
+ page, CSP, Next.js — is in **[`docs/chat.md`](docs/chat.md)**. A runnable
47
+ example with no Tailwind in the host is in
48
+ [`examples/react-chat`](examples/react-chat).
49
+
11
50
  ## Quick Start
12
51
 
13
52
  ### React (recommended)
14
53
 
15
54
  ```tsx
16
- import { T2VProvider, ChatPanel } from '@talk2view/sdk/react';
55
+ import { Talk2View, ChatPanel } from '@talk2view/sdk/ui';
17
56
  import type { ClientTool } from '@talk2view/sdk';
18
57
 
19
58
  const tools: ClientTool[] = [
@@ -36,17 +75,26 @@ const tools: ClientTool[] = [
36
75
 
37
76
  function App() {
38
77
  return (
39
- <T2VProvider partnerKey="pk_live_..." baseUrl="https://api.talk2view.com">
40
- <ChatPanel tools={tools} systemPrompt="You control a canvas." />
41
- </T2VProvider>
78
+ <Talk2View partnerKey="pk_live_..." tools={tools} systemPrompt="You control a canvas.">
79
+ <ChatPanel />
80
+ </Talk2View>
42
81
  );
43
82
  }
44
83
  ```
45
84
 
46
- That's it. The `ChatPanel` handles login, tool registration, streaming chat, and tool execution automatically.
85
+ 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>`.
86
+
87
+ > `/ui` is the previous chat panel, and is still supported. It is far smaller
88
+ > than the packaged chat (39.9 KB gzip against 361.8) and has no assistant-ui in
89
+ > it. New integrations that want the current Talk2View chat should use
90
+ > [`@talk2view/sdk/chat`](#chat-ui) above.
91
+
92
+ A logged-out visitor gets an anonymous demo session automatically, so there is nothing to wire up before the first reply. Pass `allowAnonymous={false}` to `<ChatPanel>` to ask people to sign in first.
47
93
 
48
94
  ### Vanilla JavaScript
49
95
 
96
+ The package root exports a `Talk2View` **class** — the framework-agnostic client. It shares a name with the `<Talk2View>` provider component from `/ui` above; if you need both in one file, alias one of them.
97
+
50
98
  ```typescript
51
99
  import { Talk2View } from '@talk2view/sdk';
52
100
 
@@ -69,9 +117,10 @@ await t2v.tools.register([
69
117
  ]);
70
118
 
71
119
  // 3. Chat — tool calls are handled automatically
120
+ let reply = '';
72
121
  for await (const event of t2v.chat('What time is it?')) {
73
- if (event.type === 'text') process.stdout.write(event.content);
74
- if (event.type === 'done') console.log('\n');
122
+ if (event.type === 'text') reply += event.content;
123
+ if (event.type === 'done') console.log(reply);
75
124
  }
76
125
  ```
77
126
 
@@ -124,49 +173,79 @@ Tools with `return_direct: true` skip the AI's post-processing step. The tool re
124
173
 
125
174
  ## React API
126
175
 
127
- ### `<T2VProvider>`
176
+ There are four React entry points, and they do different jobs:
128
177
 
129
- Context provider that initializes the Talk2View client. Wrap your app (or the section that uses Talk2View) with this.
178
+ - **`@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`](docs/chat.md).
179
+ - **`@talk2view/sdk/ui`** — the previous chat panel, still supported: `<Talk2View>`, `<ChatPanel>`, `<ChatWidget>` and the components they are built from. 39.9 KB gzip against the packaged chat's 361.8, and no assistant-ui.
180
+ - **`@talk2view/sdk/react`** — headless: `<T2VProvider>` and hooks, for building your own UI.
181
+ - **`@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.
182
+
183
+ ### `<Talk2View>` (from `/ui`)
184
+
185
+ The root provider for the UI components. It creates the client, registers your tools, injects the theme, and holds the chat state every `/ui` component reads.
130
186
 
131
187
  ```tsx
132
- <T2VProvider
133
- partnerKey="pk_live_..." // Required
134
- baseUrl="https://api.talk2view.com" // Optional, defaults to localhost:8100
135
- model="gpt-4.1-mini" // Optional, uses server default
188
+ <Talk2View
189
+ partnerKey="pk_live_..." // Required
190
+ tools={myTools} // Optional — ClientTool[] (with execute) or schemas
191
+ systemPrompt="You are..." // Optional — sent with every message
192
+ baseUrl="https://engine.talk2view.com" // Optional, this is the default
193
+ model="gpt-4.1-mini" // Optional, falls back to your partner default
194
+ theme={{ accent: '#26C8B8' }} // Optional
195
+ debug={false} // Optional — log to the browser console
136
196
  >
137
197
  {children}
138
- </T2VProvider>
198
+ </Talk2View>
139
199
  ```
140
200
 
141
- ### `<ChatPanel>`
201
+ ### `<ChatPanel>` (from `/ui`)
142
202
 
143
- Self-contained chat UI with built-in login, tool registration, message list, and input. Drop this in and it works.
203
+ The full chat surface: header, message list, composer, sign-in, tool approvals. It takes its client and state from `<Talk2View>`, so it must be rendered inside one — on its own it throws `useTalk2View must be used within <Talk2View>`. Note that `tools` and `systemPrompt` belong on `<Talk2View>`, not here.
144
204
 
145
205
  ```tsx
146
206
  <ChatPanel
147
- tools={myTools} // ClientTool[] — tools with execute handlers
148
- systemPrompt="You are..." // Optional system prompt
149
- signupUrl="https://..." // Optional, link shown on login form
150
- className="my-chat" // Optional CSS class
151
- style={{ height: '100%' }} // Optional inline styles
207
+ welcome={{ heading: 'Ask about this document', suggestions: ['Summarise it'] }}
208
+ allowAnonymous={true} // Default. false = require sign-in before chatting
209
+ signupUrl="https://..." // Link shown on the sign-in form
210
+ resetPasswordUrl="https://..." // Adds a "Forgot password?" link
211
+ resetPasswordTarget="_blank" // Default. '_self' opens in your own app
212
+ describeToolActivity={(name, args) => (name === 'set_color' ? 'Recolouring' : null)}
213
+ isToolDestructive={(name) => name.startsWith('delete_')}
214
+ groupAssistantMessages={false}
152
215
  />
153
216
  ```
154
217
 
155
- If the user isn't logged in, `ChatPanel` shows a login form automatically. After login, it registers the provided tools and enables the chat input.
218
+ By default a logged-out visitor is signed into an anonymous demo session rather than being shown a login form. The sign-in form appears when `allowAnonymous` is `false`, when your partner account has anonymous access switched off or its daily cap is spent, or when the demo budget for that visitor runs out.
156
219
 
157
- ### `<LoginModal>`
220
+ ### `<LoginForm>` (from `/ui`)
158
221
 
159
- Standalone login form. Use this if you want to control placement separately from the chat UI.
222
+ The sign-in form on its own, if you want to place it yourself. It renders unconditionally — you decide when to show it (check `isAuthenticated` from `useTalk2View()`, the `/ui` hook).
160
223
 
161
224
  ```tsx
162
- <LoginModal
163
- signupUrl="https://talk2view.com/auth?signup" // Optional
164
- onSuccess={() => console.log('Logged in!')} // Optional callback
165
- className="my-login" // Optional CSS class
225
+ <LoginForm
226
+ heading="Sign in"
227
+ subheading="to keep your chat history"
228
+ defaultMode="login" // or 'signup'
229
+ signupUrl="https://talk2view.com/auth?signup"
230
+ resetPasswordUrl="https://..."
231
+ termsUrl="https://..."
232
+ privacyUrl="https://..."
166
233
  />
167
234
  ```
168
235
 
169
- Returns `null` if the user is already authenticated.
236
+ ### `<T2VProvider>` (from `/react`)
237
+
238
+ The headless provider. Use it when you are building your own UI on the hooks below; `<Talk2View>` already includes it, so you never need both.
239
+
240
+ ```tsx
241
+ <T2VProvider
242
+ partnerKey="pk_live_..." // Required
243
+ baseUrl="https://engine.talk2view.com" // Optional, this is the default
244
+ model="gpt-4.1-mini" // Optional, uses your partner default
245
+ >
246
+ {children}
247
+ </T2VProvider>
248
+ ```
170
249
 
171
250
  ### `useT2V()`
172
251
 
@@ -187,30 +266,45 @@ const {
187
266
  isLoading, // boolean
188
267
  error, // string | null
189
268
  login, // (email: string, password: string) => Promise<void>
190
- signup, // (email: string, password: string) => Promise<void>
269
+ signup, // (email, password) => Promise<{ user, confirmationRequired }>
191
270
  logout, // () => Promise<void>
192
271
  clearError, // () => void
272
+ signInWithGoogle, // () => Promise<void> — opens the Google sign-in popup
273
+ signInWithApple, // () => Promise<void> — opens the Apple sign-in popup
274
+ oauthLoading, // boolean — true while either popup is open
275
+ oauthProvider, // 'google' | 'apple' | null — which one
276
+ oauthProviders, // ('google' | 'apple')[] | null — popup sign-ins available on this website; null while loading
193
277
  } = useT2VAuth();
194
278
  ```
195
279
 
280
+ `signup()` resolves to a `SignupOutcome`: `{ user: User | null; confirmationRequired: boolean }`. When your project has email confirmation switched on, `confirmationRequired` is `true` and `user` is `null` until the person clicks the link in their inbox.
281
+
282
+ Google and Apple sign-in open a popup and work on any `https://` website, with nothing to configure — your users have Talk2View accounts, and the sign-in form is the same in every app. Your publishable key is public, though, so on a website you have **not** registered your users first see a Talk2View screen naming the site that is asking, and press Continue. Add your site under **Settings → Allowed websites** in the dashboard (the exact `https://` address, nothing after the domain) and that screen goes away; the SDK logs one line to the console when it applies. If your account is set to allow only registered websites, `<LoginForm>` hides the two buttons elsewhere. Popup sign-in never runs on `localhost`; use email and password while developing.
283
+
196
284
  ### `useT2VChat()`
197
285
 
198
286
  Chat state and message sending.
199
287
 
200
288
  ```tsx
201
289
  const {
202
- messages, // DisplayMessage[]
203
- isLoading, // boolean — true while streaming
204
- error, // string | null
205
- threadId, // string | null
206
- agentStatus, // { type: string; message: string } | null — real-time agent status
207
- todos, // string — agent's current todo/plan text
208
- sendMessage, // (content: string) => Promise<void>
209
- clearMessages, // () => void
210
- clearError, // () => void
290
+ messages, // DisplayMessage[]
291
+ isLoading, // boolean — true while streaming
292
+ error, // string | null
293
+ threadId, // string | null
294
+ agentStatus, // { status: string; message: string } | null
295
+ pendingApproval, // PendingApproval | null — a tool call awaiting a decision
296
+ alwaysAllowedTools, // ReadonlySet<string> — approved for the rest of the session
297
+ sendMessage, // (content, { attachments? }) => Promise<void>
298
+ approveToolCall, // (decision: HumanDecision) => Promise<void>
299
+ retryLastMessage, // () => Promise<void> — only meaningful while error is set
300
+ stop, // () => void — ends the reply, keeps the text so far
301
+ clearMessages, // () => void
302
+ clearError, // () => void
211
303
  } = useT2VChat({ systemPrompt: 'You are a helpful assistant.' });
212
304
  ```
213
305
 
306
+ When a tool is registered with `permission: true`, the agent pauses and `pendingApproval` fills in. Show it, then call `approveToolCall({ action: 'once' | 'always' | 'deny' })` — `'always'` adds the tool to `alwaysAllowedTools` for the rest of the session. `<ChatPanel>` already does all of this; you only need it when building your own UI.
307
+
214
308
  `DisplayMessage` shape:
215
309
  ```typescript
216
310
  {
@@ -218,7 +312,10 @@ const {
218
312
  role: 'user' | 'assistant'
219
313
  content: string
220
314
  timestamp: Date
221
- isStreaming?: boolean // true while the assistant is still generating
315
+ isStreaming?: boolean // true while the assistant is still generating
316
+ attachments?: Attachment[] // files sent with a user message
317
+ plan?: string // markdown checklist from the agent's planning tool
318
+ steps?: ToolStep[] // completed tool calls, rendered as inline steps
222
319
  }
223
320
  ```
224
321
 
@@ -236,6 +333,86 @@ const {
236
333
 
237
334
  ---
238
335
 
336
+ ## assistant-ui
337
+
338
+ assistant-ui already has a seam other backends plug into at the runtime level — `@assistant-ui/react-ai-sdk` for the Vercel AI SDK is the well-known example. `useTalk2ViewRuntime` is Talk2View's equivalent: it hands assistant-ui an `AssistantRuntime`, built on `useExternalStoreRuntime`, so the stock `<Thread />` (or the primitives) render Talk2View's messages, client tools, approvals and streaming — with no Talk2View-specific component in the tree.
339
+
340
+ This is a separate entry point from `/ui` and `/react`: `@assistant-ui/react` is an optional peer dependency, loaded only when you import `@talk2view/sdk/assistant-ui`. It never affects the root, `/react` or `/ui` bundles.
341
+
342
+ ### Install
343
+
344
+ ```bash
345
+ npm install @assistant-ui/react
346
+ npx shadcn@latest init --yes --defaults
347
+ npx shadcn@latest add @assistant-ui/thread --yes
348
+ ```
349
+
350
+ The `shadcn` commands generate the Thread's source into your own `src/components/` — they're your files, not a Talk2View package, so you can restyle or extend them freely. See `examples/react-assistant-ui` for a full app built this way, including the exact generated output.
351
+
352
+ One known wrinkle in the registry output at the time of writing: `tooltip-icon-button.tsx` passes `delayDuration={0}` to a `TooltipProvider` whose prop is `delay`. It is inert at runtime, but it fails `tsc`, so if your build type-checks (most Vite templates run `tsc -b && vite build`) change it to `delay={0}` — the example does.
353
+
354
+ ### `useTalk2ViewRuntime`
355
+
356
+ ```tsx
357
+ import { AssistantRuntimeProvider } from '@assistant-ui/react';
358
+ import { useTalk2ViewRuntime } from '@talk2view/sdk/assistant-ui';
359
+
360
+ function Chat() {
361
+ const runtime = useTalk2ViewRuntime({ partnerKey: 'pk_live_...', tools });
362
+ return (
363
+ <AssistantRuntimeProvider runtime={runtime}>
364
+ <Thread />
365
+ </AssistantRuntimeProvider>
366
+ );
367
+ }
368
+ ```
369
+
370
+ `useTalk2ViewRuntime(options)` takes the same connection options as `<T2VProvider>` (`partnerKey`, `baseUrl`, `model`, `debug`, `anonymousAutoStart`) plus `tools` and `systemPrompt`, and creates its own `Talk2View` client — a new one only when `partnerKey`, `baseUrl`, `debug` or `anonymousAutoStart` changes (`requestTimeout` is read once, at creation). `model` is sent with each message rather than baked into the client, so an end-user can switch models from a settings screen without losing the chat. `tools` are registered with the engine when their schemas change, compared by value, so an inline array is fine.
371
+
372
+ ### `useTalk2ViewRuntimeForClient`
373
+
374
+ For an app that already has a `Talk2View` client — for example from `<T2VProvider>`, so auth and chat state stay shared with the rest of the app — pass it in instead of creating a second one:
375
+
376
+ ```tsx
377
+ import { AssistantRuntimeProvider } from '@assistant-ui/react';
378
+ import { useT2V } from '@talk2view/sdk/react';
379
+ import { useTalk2ViewRuntimeForClient } from '@talk2view/sdk/assistant-ui';
380
+
381
+ function Chat() {
382
+ const { t2v } = useT2V(); // from <T2VProvider>
383
+ const runtime = useTalk2ViewRuntimeForClient(t2v, { tools });
384
+ return (
385
+ <AssistantRuntimeProvider runtime={runtime}>
386
+ <Thread />
387
+ </AssistantRuntimeProvider>
388
+ );
389
+ }
390
+ ```
391
+
392
+ ### What maps to what
393
+
394
+ | assistant-ui | Talk2View SDK |
395
+ | --- | --- |
396
+ | Streaming replies | `DisplayMessage`s from `t2v.messages`, converted per-message by `toThreadMessage()` |
397
+ | Client tools | `tools` registers schemas via `t2v.tools.register()` and handlers via `t2v.tools.handle()` — same as `<Talk2View tools>` |
398
+ | Tool approvals | A `PendingApproval` becomes assistant-ui's own approval card (`allow-once` / `allow-always` / `reject-once`, plus a free-form reason); the decision is sent through `t2v.approveToolCall()` |
399
+ | Attachments | assistant-ui's `AttachmentAdapter` uploads through `t2v.uploadAttachment()` |
400
+ | Stop | assistant-ui's cancel action calls `t2v.stop()` — or, while a tool call is waiting for a decision, denies it with the reason "Cancelled by the user" |
401
+ | Retry | assistant-ui's reload action on the **latest turn** — its reply, or the error card when it failed — calls `t2v.retryLastMessage()`; on an older reply it does nothing, because the SDK can only regenerate the last turn |
402
+
403
+ Four things do not map, on purpose:
404
+
405
+ - **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.
406
+ - **Sign-in UI.** `useTalk2ViewRuntime` only creates a client and a runtime; there is no `<LoginForm>` equivalent. A logged-out visitor gets an anonymous demo session automatically (same default as `<ChatPanel>`), and building your own sign-in UI, or gating the app before it mounts, is on the partner.
407
+ - **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 } } }} />`.
408
+ - **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.
409
+
410
+ ### Bundle size
411
+
412
+ `<Thread />` and its dependencies are the partner's own assistant-ui bundle, sized however assistant-ui and its registry components size it; `@talk2view/sdk/assistant-ui` itself adds about 1.9 KB gzipped on top (`dist/assistant-ui/{index,convert}.js`, bundled and minified with esbuild, `react`, `react-dom`, `@assistant-ui/react` and the SDK's own `Talk2View` client marked external since a partner already has them).
413
+
414
+ ---
415
+
239
416
  ## Core API (framework-agnostic)
240
417
 
241
418
  ### `Talk2View`
@@ -245,7 +422,7 @@ Main entry point. Use this directly for vanilla JS or non-React frameworks.
245
422
  ```typescript
246
423
  const t2v = new Talk2View({
247
424
  partnerKey: 'pk_live_...', // Required
248
- baseUrl: 'https://api.talk2view.com', // Optional
425
+ baseUrl: 'https://engine.talk2view.com', // Optional, this is the default
249
426
  model: 'gpt-4.1-mini', // Optional
250
427
  });
251
428
  ```
@@ -254,7 +431,7 @@ const t2v = new Talk2View({
254
431
 
255
432
  ```typescript
256
433
  await t2v.auth.login(email, password) // Returns User
257
- await t2v.auth.signup(email, password) // Returns User
434
+ await t2v.auth.signup(email, password) // Returns { user, confirmationRequired }
258
435
  await t2v.auth.logout()
259
436
  t2v.auth.getUser() // Returns User | null
260
437
  t2v.auth.isAuthenticated() // Returns boolean
@@ -358,6 +535,24 @@ for await (const event of t2v.chat('Zoom in please')) {
358
535
  }
359
536
  ```
360
537
 
538
+ #### `t2v.warmUp()`
539
+
540
+ A new end-user's first AI call is slower than the rest: the engine sets up their
541
+ account with the model proxy on first use, which takes a few seconds. Call
542
+ `warmUp()` at the first sign they are going to use the chat — their first
543
+ keystroke, a tap on the mic — and that setup happens while they are still typing.
544
+
545
+ ```ts
546
+ input.addEventListener('input', () => t2v.warmUp(), { once: true });
547
+ ```
548
+
549
+ It is safe to call repeatedly (one request per signed-in end-user), never
550
+ throws, and is a no-op against an engine that predates it. Don't call it when
551
+ the chat merely opens: for a logged-out visitor it starts an anonymous session,
552
+ which counts toward your daily anonymous cap.
553
+
554
+ `<ChatWidget>`, `<ChatPanel>` and the assistant-ui runtime already do this.
555
+
361
556
  #### `t2v.clearSession()`
362
557
 
363
558
  Reset the current session. Clears the thread ID so the next `chat()` call starts a fresh conversation.
@@ -379,8 +574,15 @@ const audioModels = await t2v.listAudioModels(); // Speech-to-text models
379
574
 
380
575
  Transcribe an audio file using the server's speech-to-text service:
381
576
 
577
+ Takes a `FormData`, with the fields the engine's `/v1/audio/transcriptions` endpoint accepts (`file`, `model`, and optionally `language`, `translate_to`, `translation_guidance`):
578
+
382
579
  ```typescript
383
- const result = await t2v.transcribe(audioBlob, 'whisper-1', 'en');
580
+ const form = new FormData();
581
+ form.append('file', audioBlob, 'recording.webm');
582
+ form.append('model', 'faster-whisper-large-turbo-gcp'); // one of listAudioModels()
583
+ form.append('language', 'en'); // optional
584
+
585
+ const result = await t2v.transcribe(form);
384
586
  console.log(result.text);
385
587
  ```
386
588
 
@@ -492,7 +694,7 @@ Use `enum` to restrict string values:
492
694
  The `execute` function receives the AI's arguments as a `Record<string, unknown>` and must return a `Promise<string>`. The returned string is sent back to the AI as the tool's result.
493
695
 
494
696
  - Return `JSON.stringify(...)` for structured results
495
- - Throw an error or return an error string if the tool fails — the SDK catches errors and sends them back to the AI as `{ result: "Error: ...", is_error: true }`
697
+ - Throw an error or return an error string if the tool fails — the SDK catches it and sends the message back to the AI as `{ result: '{"error":"..."}', is_error: true }`, so the agent can explain or try something else
496
698
 
497
699
  ---
498
700
 
@@ -523,22 +725,62 @@ To mitigate this:
523
725
 
524
726
  ---
525
727
 
728
+ ## Rendering model replies
729
+
730
+ 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 never loads remote content on its own:
731
+
732
+ - **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).
733
+ - **Images** become links (`Image: <alt> (<host>)`) that the end-user can choose to open.
734
+ - **Links** open in a new tab with `noopener noreferrer`, and only `http(s)` and `mailto` link destinations are kept.
735
+
736
+ If you build your own chat UI, render replies the same way:
737
+
738
+ ```tsx
739
+ import { renderSafeMarkdown } from '@talk2view/sdk/ui';
740
+
741
+ <div dangerouslySetInnerHTML={{ __html: renderSafeMarkdown(reply) }} />
742
+ ```
743
+
744
+ Never pass model output to `innerHTML` or a markdown library's raw HTML output without it.
745
+
746
+ Call it in the browser (client components): without a DOM it returns the reply as escaped plain text.
747
+
748
+ ## Chat sessions and deploys
749
+
750
+ A chat session lives in the engine's memory, so a Talk2View deploy, a restart, or eviction under load can end it. The SDK handles most of that for you:
751
+
752
+ - **Sending a message:** if the session is gone before the reply has started, the SDK opens a new one, re-registers your tools and resends the turn with the conversation so far — nothing from that turn is lost. It emits `sessionRecovered` with the new session id, which you can log.
753
+ - **Answering an approval:** the SDK can't safely resend the tool result, because your handler has already run. It emits one `error` event with `errorType: 'session_lost'` and clears the pending approval, so the end-user can send their message again.
754
+ - **Mid-reply or mid-tool-resume:** if the chat session is lost after the reply has started, or while a tool result is being sent back, the SDK reports `session_lost` and doesn't resend, because the tool handler may already have run.
755
+
756
+ ```ts
757
+ t2v.on('sessionRecovered', (sessionId) => console.info('reconnected', sessionId));
758
+ ```
759
+
760
+ This is why `sendMessage()` and `chat()` take the conversation history: the engine works out which messages are new, so a fresh session continues where the old one stopped.
761
+
526
762
  ## Error Handling
527
763
 
528
764
  The SDK throws typed errors:
529
765
 
530
- | Error Class | When |
531
- |-------------|------|
532
- | `AuthenticationError` | Invalid credentials, expired token |
533
- | `PartnerKeyError` | Invalid or inactive partner API key |
534
- | `SessionError` | Session not found or creation failed |
535
- | `NetworkError` | Network failure, server unreachable |
536
- | `T2VError` | Base class for all SDK errors |
766
+ | Error Class | When | `err.type` |
767
+ |-------------|------|------------|
768
+ | `AuthenticationError` | Bad credentials, expired or rejected token | `authentication_error` |
769
+ | `PartnerKeyError` | Unknown or inactive partner key | `partner_key_error` |
770
+ | `NetworkError` | Network failure, server unreachable, request timed out | `network_error` |
771
+ | `T2VError` | Base class — and what everything else arrives as | the engine's own type |
772
+ | `SessionError` | Reserved — see below | `session_not_found` |
773
+
774
+ Every error carries the engine's machine-readable `type`, so `err.type` is the precise test; the classes above are a convenience for the common cases. Types worth knowing that arrive as a plain `T2VError`: `origin_not_allowed` (403 — the request's Origin isn't on your key's allow-list), `not_found` (404), and `rate_limit_exceeded` (429).
537
775
 
538
- All errors extend `T2VError`, which exposes `message`, `type`, `statusCode`, and `code`:
776
+ Some failures never reach a `catch` at all, because they arrive inside the chat stream rather than as a rejected request: a spent anonymous demo budget comes through as an `error` event with `errorType: 'budget_exceeded'`, which the SDK turns into a `demoLimitReached` event rather than an error state. Listen for that instead of trying to catch it.
777
+
778
+ **On lost chat sessions:** `SessionError` is reserved for a dedicated engine type that isn't in use yet — today a chat session that no longer exists comes back as a `T2VError` with `type: 'not_found'` and `statusCode: 404`. You shouldn't need to catch it: the SDK recovers the session itself where that is safe, and otherwise reports an `error` event with `errorType: 'session_lost'`. See [Chat sessions and deploys](#chat-sessions-and-deploys).
779
+
780
+ All errors extend `T2VError`, which exposes `message`, `type`, `statusCode`, `code`, and `detail` (the server's raw diagnostic text, kept out of `message` so it never reaches an end-user by accident):
539
781
 
540
782
  ```typescript
541
- import { T2VError, AuthenticationError, NetworkError, SessionError } from '@talk2view/sdk';
783
+ import { T2VError, AuthenticationError, NetworkError, PartnerKeyError } from '@talk2view/sdk';
542
784
 
543
785
  try {
544
786
  await t2v.auth.login(email, password);
@@ -547,8 +789,8 @@ try {
547
789
  console.log('Bad credentials');
548
790
  } else if (err instanceof NetworkError) {
549
791
  console.log('Server unreachable');
550
- } else if (err instanceof SessionError) {
551
- console.log('Session issue:', err.message);
792
+ } else if (err instanceof PartnerKeyError) {
793
+ console.log('Check your partner key:', err.message);
552
794
  } else if (err instanceof T2VError) {
553
795
  // Access the error code for programmatic handling
554
796
  console.log(`Error [${err.code}]: ${err.message} (HTTP ${err.statusCode})`);
@@ -575,18 +817,25 @@ const { error: chatError } = useT2VChat({ systemPrompt: '...' });
575
817
  | Property | Type | Required | Default | Description |
576
818
  |----------|------|----------|---------|-------------|
577
819
  | `partnerKey` | `string` | Yes | — | Your partner API key |
578
- | `baseUrl` | `string` | No | `'http://localhost:8100'` | Talk2View API server URL |
820
+ | `baseUrl` | `string` | No | `'https://engine.talk2view.com'` | Talk2View engine URL |
579
821
  | `model` | `string` | No | Server default | LLM model to use |
580
822
  | `requestTimeout` | `number` | No | `30000` | HTTP request timeout in ms. Does not apply to SSE streams after connection. |
823
+ | `anonymousAutoStart` | `boolean` | No | `true` | Start an anonymous demo session on the first `chat()` when nobody is signed in. Set `false` to require sign-in. |
824
+ | `debug` | `boolean` | No | `false` | Log streaming events, tool calls, history and state changes to the console. |
581
825
 
582
826
  ---
583
827
 
584
828
  ## System Prompts
585
829
 
586
- System prompts set the AI's behavior and context for your application. Pass them to `ChatPanel` or `useT2VChat`:
830
+ System prompts set the AI's behavior and context for your application. Pass one to `<Talk2View>` (the UI components) or to `useT2VChat` (the hooks):
587
831
 
588
832
  ```tsx
589
- <ChatPanel systemPrompt="You are a medical imaging assistant. Use the provided tools to control the DICOM viewer." />
833
+ <Talk2View
834
+ partnerKey="pk_live_..."
835
+ systemPrompt="You are a medical imaging assistant. Use the provided tools to control the DICOM viewer."
836
+ >
837
+ <ChatPanel />
838
+ </Talk2View>
590
839
  ```
591
840
 
592
841
  Best practices:
@@ -603,7 +852,7 @@ Here's a complete example integrating Talk2View into a hypothetical drawing app:
603
852
 
604
853
  ```tsx
605
854
  import React, { useRef, useMemo } from 'react';
606
- import { T2VProvider, ChatPanel } from '@talk2view/sdk/react';
855
+ import { Talk2View, ChatPanel } from '@talk2view/sdk/ui';
607
856
  import type { ClientTool } from '@talk2view/sdk';
608
857
  import { Canvas } from './Canvas';
609
858
 
@@ -656,16 +905,18 @@ function App() {
656
905
  ], []);
657
906
 
658
907
  return (
659
- <T2VProvider partnerKey="pk_live_..." baseUrl="https://api.talk2view.com">
908
+ <Talk2View
909
+ partnerKey="pk_live_..."
910
+ tools={tools}
911
+ systemPrompt="You control a drawing canvas. Use the tools to draw shapes and manage the canvas."
912
+ >
660
913
  <div style={{ display: 'flex', height: '100vh' }}>
661
914
  <Canvas ref={canvasRef} style={{ flex: 1 }} />
662
- <ChatPanel
663
- tools={tools}
664
- systemPrompt="You control a drawing canvas. Use the tools to draw shapes and manage the canvas."
665
- style={{ width: 360 }}
666
- />
915
+ <div style={{ width: 360 }}>
916
+ <ChatPanel welcome={{ heading: 'What should I draw?' }} />
917
+ </div>
667
918
  </div>
668
- </T2VProvider>
919
+ </Talk2View>
669
920
  );
670
921
  }
671
922
  ```
@@ -694,16 +945,32 @@ import type {
694
945
  RegisterSkillsResponse,
695
946
  } from '@talk2view/sdk';
696
947
 
697
- // React types
948
+ // Headless React types
698
949
  import type {
699
950
  T2VProviderProps,
700
951
  UseT2VAuthResult,
701
952
  UseT2VChatResult,
702
953
  UseT2VToolsResult,
954
+ UsePartnerConfigResult,
955
+ UseUserPreferencesResult,
703
956
  DisplayMessage,
704
- ChatPanelProps,
705
- LoginModalProps,
957
+ ToolStep,
958
+ PendingApproval,
706
959
  } from '@talk2view/sdk/react';
960
+
961
+ // UI component types
962
+ import type {
963
+ Talk2ViewProps,
964
+ ChatPanelProps,
965
+ ChatWidgetProps,
966
+ } from '@talk2view/sdk/ui';
967
+
968
+ // assistant-ui runtime
969
+ import { useTalk2ViewRuntime, useTalk2ViewRuntimeForClient } from '@talk2view/sdk/assistant-ui';
970
+ import type {
971
+ UseTalk2ViewRuntimeOptions, // useTalk2ViewRuntime(options)
972
+ Talk2ViewRuntimeOptions, // useTalk2ViewRuntimeForClient(t2v, options)
973
+ } from '@talk2view/sdk/assistant-ui';
707
974
  ```
708
975
 
709
976
  ## Publishing
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Pure mapping from the SDK's chat state onto assistant-ui's message shape.
3
+ *
4
+ * assistant-ui renders `ThreadMessageLike`; the SDK keeps `DisplayMessage`.
5
+ * Everything here is a function of (message, surrounding state) so it can be
6
+ * unit-tested without React or a runtime.
7
+ */
8
+ import type { ThreadMessageLike } from '@assistant-ui/react';
9
+ import type { DisplayMessage, HumanDecision, PendingApproval } from '../types.js';
10
+ /** What a message needs to know about the thread around it. */
11
+ export interface ConvertContext {
12
+ /** True for the newest message — the only one that can be streaming or waiting on a decision. */
13
+ isLast: boolean;
14
+ /** A tool call waiting for the end-user; rendered as an approval gate on the last assistant message. */
15
+ pendingApproval: PendingApproval | null;
16
+ /** The SDK's error state; shown on the last assistant message. */
17
+ error: string | null;
18
+ }
19
+ type ThreadPart = Extract<ThreadMessageLike['content'], readonly unknown[]>[number];
20
+ /** Option ids the stock assistant-ui approval card sends back; we map them onto `HumanDecision`. */
21
+ export declare const APPROVAL_OPTION_IDS: {
22
+ readonly once: "allow-once";
23
+ readonly always: "allow-always";
24
+ readonly deny: "reject-once";
25
+ };
26
+ /** Stable id for a completed step: steps carry no id of their own. */
27
+ export declare function stepToolCallId(messageId: string, index: number): string;
28
+ /** The approval gate assistant-ui draws for a tool call that needs a decision. */
29
+ export declare function approvalPart(approval: PendingApproval): ThreadPart;
30
+ /** Convert one SDK message for assistant-ui. */
31
+ export declare function toThreadMessage(msg: DisplayMessage, ctx: ConvertContext): ThreadMessageLike;
32
+ /** Turn the stock approval card's answer into the SDK's `HumanDecision`. */
33
+ export declare function toHumanDecision(response: {
34
+ approved: boolean;
35
+ optionId?: string;
36
+ text?: string;
37
+ reason?: string;
38
+ }): HumanDecision;
39
+ export {};
40
+ //# sourceMappingURL=convert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../src/assistant-ui/convert.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAuB,MAAM,qBAAqB,CAAC;AAClF,OAAO,KAAK,EAAc,cAAc,EAAE,aAAa,EAAE,eAAe,EAAY,MAAM,aAAa,CAAC;AAExG,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC7B,iGAAiG;IACjG,MAAM,EAAE,OAAO,CAAC;IAChB,wGAAwG;IACxG,eAAe,EAAE,eAAe,GAAG,IAAI,CAAC;IACxC,kEAAkE;IAClE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAGD,KAAK,UAAU,GAAG,OAAO,CAAC,iBAAiB,CAAC,SAAS,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpF,oGAAoG;AACpG,eAAO,MAAM,mBAAmB;;;;CAItB,CAAC;AAQX,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvE;AAgBD,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,UAAU,CAelE;AAkCD,gDAAgD;AAChD,wBAAgB,eAAe,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,cAAc,GAAG,iBAAiB,CA0B3F;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,QAAQ,EAAE;IACxC,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,aAAa,CAShB"}