@slices.sh/ai-chat 0.1.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 (393) hide show
  1. package/LICENSE +21 -0
  2. package/dist/exports.json +794 -0
  3. package/dist/fixture/fixture-approval.d.ts +4 -0
  4. package/dist/fixture/fixture-approval.js +59 -0
  5. package/dist/fixture/fixture-artifacts.d.ts +2 -0
  6. package/dist/fixture/fixture-artifacts.js +48 -0
  7. package/dist/fixture/fixture-question.d.ts +5 -0
  8. package/dist/fixture/fixture-question.js +93 -0
  9. package/dist/fixture/fixture-script.d.ts +35 -0
  10. package/dist/fixture/fixture-script.js +280 -0
  11. package/dist/fixture/fixture-selection.d.ts +5 -0
  12. package/dist/fixture/fixture-selection.js +20 -0
  13. package/dist/fixture/fixture.d.ts +20 -0
  14. package/dist/fixture/fixture.js +75 -0
  15. package/dist/fixture/provider.d.ts +15 -0
  16. package/dist/fixture/provider.js +14 -0
  17. package/dist/headless/always-allow.d.ts +2 -0
  18. package/dist/headless/always-allow.js +24 -0
  19. package/dist/headless/answer.d.ts +6 -0
  20. package/dist/headless/answer.js +29 -0
  21. package/dist/headless/approval-error.d.ts +2 -0
  22. package/dist/headless/approval-error.js +16 -0
  23. package/dist/headless/approval-reason.d.ts +2 -0
  24. package/dist/headless/approval-reason.js +13 -0
  25. package/dist/headless/approval-receipt.d.ts +4 -0
  26. package/dist/headless/approval-receipt.js +13 -0
  27. package/dist/headless/approve.d.ts +4 -0
  28. package/dist/headless/approve.js +15 -0
  29. package/dist/headless/branch-navigation.d.ts +2 -0
  30. package/dist/headless/branch-navigation.js +11 -0
  31. package/dist/headless/branch-next.d.ts +2 -0
  32. package/dist/headless/branch-next.js +21 -0
  33. package/dist/headless/branch-page.d.ts +5 -0
  34. package/dist/headless/branch-page.js +13 -0
  35. package/dist/headless/branch-previous.d.ts +2 -0
  36. package/dist/headless/branch-previous.js +21 -0
  37. package/dist/headless/cancel-edit.d.ts +2 -0
  38. package/dist/headless/cancel-edit.js +10 -0
  39. package/dist/headless/cancel-queue-edit.d.ts +2 -0
  40. package/dist/headless/cancel-queue-edit.js +15 -0
  41. package/dist/headless/cancel-queued.d.ts +2 -0
  42. package/dist/headless/cancel-queued.js +15 -0
  43. package/dist/headless/chat-dictation/confirm.d.ts +2 -0
  44. package/dist/headless/chat-dictation/confirm.js +14 -0
  45. package/dist/headless/chat-dictation/index.d.ts +12 -0
  46. package/dist/headless/chat-dictation/index.js +9 -0
  47. package/dist/headless/chat-dictation/input.d.ts +3 -0
  48. package/dist/headless/chat-dictation/input.js +42 -0
  49. package/dist/headless/chat-dictation/root.d.ts +3 -0
  50. package/dist/headless/chat-dictation/root.js +42 -0
  51. package/dist/headless/chat-dictation/text.d.ts +3 -0
  52. package/dist/headless/chat-dictation/text.js +14 -0
  53. package/dist/headless/chat-dictation/voice.d.ts +3 -0
  54. package/dist/headless/chat-dictation/voice.js +35 -0
  55. package/dist/headless/citation-content.d.ts +3 -0
  56. package/dist/headless/citation-content.js +19 -0
  57. package/dist/headless/citation-description.d.ts +2 -0
  58. package/dist/headless/citation-description.js +12 -0
  59. package/dist/headless/citation-root.d.ts +2 -0
  60. package/dist/headless/citation-root.js +13 -0
  61. package/dist/headless/citation-title.d.ts +2 -0
  62. package/dist/headless/citation-title.js +12 -0
  63. package/dist/headless/citation-trigger.d.ts +2 -0
  64. package/dist/headless/citation-trigger.js +12 -0
  65. package/dist/headless/clear-answer.d.ts +5 -0
  66. package/dist/headless/clear-answer.js +19 -0
  67. package/dist/headless/composer-root.d.ts +14 -0
  68. package/dist/headless/composer-root.js +12 -0
  69. package/dist/headless/composer-standalone.d.ts +3 -0
  70. package/dist/headless/composer-standalone.js +10 -0
  71. package/dist/headless/composer.d.ts +3 -0
  72. package/dist/headless/composer.js +24 -0
  73. package/dist/headless/context-usage.d.ts +3 -0
  74. package/dist/headless/context-usage.js +13 -0
  75. package/dist/headless/contexts.d.ts +77 -0
  76. package/dist/headless/contexts.js +53 -0
  77. package/dist/headless/continue.d.ts +4 -0
  78. package/dist/headless/continue.js +26 -0
  79. package/dist/headless/copy.d.ts +11 -0
  80. package/dist/headless/copy.js +21 -0
  81. package/dist/headless/deny.d.ts +4 -0
  82. package/dist/headless/deny.js +15 -0
  83. package/dist/headless/edit-queued.d.ts +2 -0
  84. package/dist/headless/edit-queued.js +15 -0
  85. package/dist/headless/edit.d.ts +2 -0
  86. package/dist/headless/edit.js +17 -0
  87. package/dist/headless/edited-attachments.d.ts +5 -0
  88. package/dist/headless/edited-attachments.js +11 -0
  89. package/dist/headless/edited-input.d.ts +2 -0
  90. package/dist/headless/edited-input.js +28 -0
  91. package/dist/headless/empty.d.ts +3 -0
  92. package/dist/headless/empty.js +12 -0
  93. package/dist/headless/failed.d.ts +15 -0
  94. package/dist/headless/failed.js +17 -0
  95. package/dist/headless/feedback-error.d.ts +2 -0
  96. package/dist/headless/feedback-error.js +11 -0
  97. package/dist/headless/feedback-root.d.ts +2 -0
  98. package/dist/headless/feedback-root.js +24 -0
  99. package/dist/headless/feedback.d.ts +4 -0
  100. package/dist/headless/feedback.js +18 -0
  101. package/dist/headless/file-audio.d.ts +2 -0
  102. package/dist/headless/file-audio.js +16 -0
  103. package/dist/headless/file-context.d.ts +4 -0
  104. package/dist/headless/file-context.js +7 -0
  105. package/dist/headless/file-img.d.ts +2 -0
  106. package/dist/headless/file-img.js +21 -0
  107. package/dist/headless/file-kind-gate.d.ts +5 -0
  108. package/dist/headless/file-kind-gate.js +11 -0
  109. package/dist/headless/file-link.d.ts +3 -0
  110. package/dist/headless/file-link.js +14 -0
  111. package/dist/headless/file-root.d.ts +5 -0
  112. package/dist/headless/file-root.js +13 -0
  113. package/dist/headless/file-unavailable.d.ts +2 -0
  114. package/dist/headless/file-unavailable.js +12 -0
  115. package/dist/headless/file-video.d.ts +2 -0
  116. package/dist/headless/file-video.js +16 -0
  117. package/dist/headless/follow-ups.d.ts +5 -0
  118. package/dist/headless/follow-ups.js +20 -0
  119. package/dist/headless/forget-approval.d.ts +2 -0
  120. package/dist/headless/forget-approval.js +21 -0
  121. package/dist/headless/gate-content.d.ts +2 -0
  122. package/dist/headless/gate-content.js +6 -0
  123. package/dist/headless/index.d.ts +249 -0
  124. package/dist/headless/index.js +251 -0
  125. package/dist/headless/input.d.ts +11 -0
  126. package/dist/headless/input.js +18 -0
  127. package/dist/headless/jump-to-latest.d.ts +3 -0
  128. package/dist/headless/jump-to-latest.js +14 -0
  129. package/dist/headless/message-edit-root.d.ts +2 -0
  130. package/dist/headless/message-edit-root.js +13 -0
  131. package/dist/headless/message-editing.d.ts +2 -0
  132. package/dist/headless/message-editing.js +10 -0
  133. package/dist/headless/message-editor.d.ts +2 -0
  134. package/dist/headless/message-editor.js +19 -0
  135. package/dist/headless/message-footer.d.ts +3 -0
  136. package/dist/headless/message-footer.js +15 -0
  137. package/dist/headless/message-reading.d.ts +2 -0
  138. package/dist/headless/message-reading.js +10 -0
  139. package/dist/headless/message.d.ts +11 -0
  140. package/dist/headless/message.js +34 -0
  141. package/dist/headless/messages.d.ts +9 -0
  142. package/dist/headless/messages.js +19 -0
  143. package/dist/headless/model-picker.d.ts +2 -0
  144. package/dist/headless/model-picker.js +14 -0
  145. package/dist/headless/model.d.ts +4 -0
  146. package/dist/headless/model.js +13 -0
  147. package/dist/headless/models.d.ts +5 -0
  148. package/dist/headless/models.js +11 -0
  149. package/dist/headless/optional-tools.d.ts +7 -0
  150. package/dist/headless/optional-tools.js +12 -0
  151. package/dist/headless/other-answer.d.ts +5 -0
  152. package/dist/headless/other-answer.js +32 -0
  153. package/dist/headless/part-approval.d.ts +2 -0
  154. package/dist/headless/part-approval.js +33 -0
  155. package/dist/headless/part-file.d.ts +6 -0
  156. package/dist/headless/part-file.js +11 -0
  157. package/dist/headless/part-question.d.ts +2 -0
  158. package/dist/headless/part-question.js +57 -0
  159. package/dist/headless/part-reasoning.d.ts +15 -0
  160. package/dist/headless/part-reasoning.js +26 -0
  161. package/dist/headless/part-source.d.ts +5 -0
  162. package/dist/headless/part-source.js +20 -0
  163. package/dist/headless/part-text.d.ts +16 -0
  164. package/dist/headless/part-text.js +29 -0
  165. package/dist/headless/part-tool.d.ts +16 -0
  166. package/dist/headless/part-tool.js +14 -0
  167. package/dist/headless/parts.d.ts +11 -0
  168. package/dist/headless/parts.js +11 -0
  169. package/dist/headless/question-back.d.ts +2 -0
  170. package/dist/headless/question-back.js +16 -0
  171. package/dist/headless/question-error.d.ts +2 -0
  172. package/dist/headless/question-error.js +14 -0
  173. package/dist/headless/question-invalid.d.ts +8 -0
  174. package/dist/headless/question-invalid.js +18 -0
  175. package/dist/headless/question-item.d.ts +8 -0
  176. package/dist/headless/question-item.js +51 -0
  177. package/dist/headless/question-next.d.ts +3 -0
  178. package/dist/headless/question-next.js +16 -0
  179. package/dist/headless/question-other.d.ts +5 -0
  180. package/dist/headless/question-other.js +22 -0
  181. package/dist/headless/question-progress.d.ts +8 -0
  182. package/dist/headless/question-progress.js +14 -0
  183. package/dist/headless/question-receipt.d.ts +11 -0
  184. package/dist/headless/question-receipt.js +29 -0
  185. package/dist/headless/questions.d.ts +3 -0
  186. package/dist/headless/questions.js +13 -0
  187. package/dist/headless/queue-editing.d.ts +4 -0
  188. package/dist/headless/queue-editing.js +11 -0
  189. package/dist/headless/queue-item.d.ts +2 -0
  190. package/dist/headless/queue-item.js +10 -0
  191. package/dist/headless/queue-items.d.ts +2 -0
  192. package/dist/headless/queue-items.js +11 -0
  193. package/dist/headless/queue-preview.d.ts +6 -0
  194. package/dist/headless/queue-preview.js +11 -0
  195. package/dist/headless/queued-input.d.ts +2 -0
  196. package/dist/headless/queued-input.js +27 -0
  197. package/dist/headless/queued.d.ts +17 -0
  198. package/dist/headless/queued.js +16 -0
  199. package/dist/headless/read-aloud.d.ts +5 -0
  200. package/dist/headless/read-aloud.js +19 -0
  201. package/dist/headless/reasoning-disclosure.d.ts +3 -0
  202. package/dist/headless/reasoning-disclosure.js +17 -0
  203. package/dist/headless/reasoning-timer.d.ts +5 -0
  204. package/dist/headless/reasoning-timer.js +13 -0
  205. package/dist/headless/references.d.ts +6 -0
  206. package/dist/headless/references.js +13 -0
  207. package/dist/headless/regenerate.d.ts +3 -0
  208. package/dist/headless/regenerate.js +22 -0
  209. package/dist/headless/remembered-approval.d.ts +2 -0
  210. package/dist/headless/remembered-approval.js +14 -0
  211. package/dist/headless/root.d.ts +30 -0
  212. package/dist/headless/root.js +99 -0
  213. package/dist/headless/save-edit.d.ts +2 -0
  214. package/dist/headless/save-edit.js +16 -0
  215. package/dist/headless/save-queued.d.ts +2 -0
  216. package/dist/headless/save-queued.js +15 -0
  217. package/dist/headless/send-now.d.ts +2 -0
  218. package/dist/headless/send-now.js +17 -0
  219. package/dist/headless/settings.d.ts +2 -0
  220. package/dist/headless/settings.js +10 -0
  221. package/dist/headless/skip-questions.d.ts +6 -0
  222. package/dist/headless/skip-questions.js +18 -0
  223. package/dist/headless/source-context.d.ts +5 -0
  224. package/dist/headless/source-context.js +9 -0
  225. package/dist/headless/source-link.d.ts +2 -0
  226. package/dist/headless/source-link.js +13 -0
  227. package/dist/headless/source-primitives.d.ts +1 -0
  228. package/dist/headless/source-primitives.js +7 -0
  229. package/dist/headless/source-root.d.ts +5 -0
  230. package/dist/headless/source-root.js +9 -0
  231. package/dist/headless/source-sheet-close.d.ts +2 -0
  232. package/dist/headless/source-sheet-close.js +12 -0
  233. package/dist/headless/source-sheet-content.d.ts +3 -0
  234. package/dist/headless/source-sheet-content.js +16 -0
  235. package/dist/headless/source-sheet-description.d.ts +2 -0
  236. package/dist/headless/source-sheet-description.js +12 -0
  237. package/dist/headless/source-sheet-root.d.ts +2 -0
  238. package/dist/headless/source-sheet-root.js +13 -0
  239. package/dist/headless/source-sheet-title.d.ts +2 -0
  240. package/dist/headless/source-sheet-title.js +12 -0
  241. package/dist/headless/source-sheet-trigger.d.ts +2 -0
  242. package/dist/headless/source-sheet-trigger.js +12 -0
  243. package/dist/headless/source-unavailable.d.ts +2 -0
  244. package/dist/headless/source-unavailable.js +9 -0
  245. package/dist/headless/sources.d.ts +14 -0
  246. package/dist/headless/sources.js +12 -0
  247. package/dist/headless/speaker.d.ts +13 -0
  248. package/dist/headless/speaker.js +10 -0
  249. package/dist/headless/step-boundary.d.ts +4 -0
  250. package/dist/headless/step-boundary.js +16 -0
  251. package/dist/headless/stop.d.ts +10 -0
  252. package/dist/headless/stop.js +18 -0
  253. package/dist/headless/stopped.d.ts +11 -0
  254. package/dist/headless/stopped.js +17 -0
  255. package/dist/headless/submit-answers.d.ts +3 -0
  256. package/dist/headless/submit-answers.js +12 -0
  257. package/dist/headless/submit.d.ts +23 -0
  258. package/dist/headless/submit.js +24 -0
  259. package/dist/headless/suggestion.d.ts +10 -0
  260. package/dist/headless/suggestion.js +14 -0
  261. package/dist/headless/suggestions.d.ts +8 -0
  262. package/dist/headless/suggestions.js +12 -0
  263. package/dist/headless/thread.d.ts +3 -0
  264. package/dist/headless/thread.js +25 -0
  265. package/dist/headless/timing.d.ts +5 -0
  266. package/dist/headless/timing.js +13 -0
  267. package/dist/headless/toggle-tool.d.ts +4 -0
  268. package/dist/headless/toggle-tool.js +15 -0
  269. package/dist/headless/tool-error.d.ts +4 -0
  270. package/dist/headless/tool-error.js +13 -0
  271. package/dist/headless/tool-input.d.ts +4 -0
  272. package/dist/headless/tool-input.js +13 -0
  273. package/dist/headless/tool-output.d.ts +4 -0
  274. package/dist/headless/tool-output.js +13 -0
  275. package/dist/headless/tool-renderer.d.ts +13 -0
  276. package/dist/headless/tool-renderer.js +15 -0
  277. package/dist/headless/transcript.d.ts +23 -0
  278. package/dist/headless/transcript.js +24 -0
  279. package/dist/headless/try-again.d.ts +2 -0
  280. package/dist/headless/try-again.js +24 -0
  281. package/dist/headless/unavailable.d.ts +8 -0
  282. package/dist/headless/unavailable.js +9 -0
  283. package/dist/headless/usage.d.ts +13 -0
  284. package/dist/headless/usage.js +15 -0
  285. package/dist/headless/use-merged-ref.d.ts +2 -0
  286. package/dist/headless/use-merged-ref.js +17 -0
  287. package/dist/headless/use-question.d.ts +3 -0
  288. package/dist/headless/use-question.js +9 -0
  289. package/dist/headless/waiting.d.ts +13 -0
  290. package/dist/headless/waiting.js +12 -0
  291. package/dist/headless/writing.d.ts +13 -0
  292. package/dist/headless/writing.js +12 -0
  293. package/dist/hooks/chat-refusal.d.ts +18 -0
  294. package/dist/hooks/chat-refusal.js +44 -0
  295. package/dist/hooks/turn-mailbox.d.ts +14 -0
  296. package/dist/hooks/turn-mailbox.js +114 -0
  297. package/dist/hooks/use-branches.d.ts +10 -0
  298. package/dist/hooks/use-branches.js +69 -0
  299. package/dist/hooks/use-chat-instance.d.ts +28 -0
  300. package/dist/hooks/use-chat-instance.js +44 -0
  301. package/dist/hooks/use-chat-settings.d.ts +11 -0
  302. package/dist/hooks/use-chat-settings.js +33 -0
  303. package/dist/hooks/use-chat-sink.d.ts +19 -0
  304. package/dist/hooks/use-chat-sink.js +56 -0
  305. package/dist/hooks/use-composer.d.ts +33 -0
  306. package/dist/hooks/use-composer.js +45 -0
  307. package/dist/hooks/use-context-usage.d.ts +8 -0
  308. package/dist/hooks/use-context-usage.js +14 -0
  309. package/dist/hooks/use-copied.d.ts +14 -0
  310. package/dist/hooks/use-copied.js +22 -0
  311. package/dist/hooks/use-draft.d.ts +6 -0
  312. package/dist/hooks/use-draft.js +38 -0
  313. package/dist/hooks/use-feedback.d.ts +8 -0
  314. package/dist/hooks/use-feedback.js +28 -0
  315. package/dist/hooks/use-follow.d.ts +15 -0
  316. package/dist/hooks/use-follow.js +30 -0
  317. package/dist/hooks/use-media-load.d.ts +5 -0
  318. package/dist/hooks/use-media-load.js +12 -0
  319. package/dist/hooks/use-media.d.ts +3 -0
  320. package/dist/hooks/use-media.js +9 -0
  321. package/dist/hooks/use-question-check.d.ts +11 -0
  322. package/dist/hooks/use-question-check.js +29 -0
  323. package/dist/hooks/use-question-draft.d.ts +34 -0
  324. package/dist/hooks/use-question-draft.js +57 -0
  325. package/dist/hooks/use-question-steps.d.ts +18 -0
  326. package/dist/hooks/use-question-steps.js +31 -0
  327. package/dist/hooks/use-question-write.d.ts +8 -0
  328. package/dist/hooks/use-question-write.js +25 -0
  329. package/dist/hooks/use-read-aloud.d.ts +8 -0
  330. package/dist/hooks/use-read-aloud.js +60 -0
  331. package/dist/hooks/use-reasoning.d.ts +8 -0
  332. package/dist/hooks/use-reasoning.js +32 -0
  333. package/dist/hooks/use-remembered-approvals.d.ts +9 -0
  334. package/dist/hooks/use-remembered-approvals.js +40 -0
  335. package/dist/hooks/use-reply.d.ts +3 -0
  336. package/dist/hooks/use-reply.js +9 -0
  337. package/dist/hooks/use-report-missing-configuration.d.ts +9 -0
  338. package/dist/hooks/use-report-missing-configuration.js +14 -0
  339. package/dist/hooks/use-retry-delay.d.ts +5 -0
  340. package/dist/hooks/use-retry-delay.js +17 -0
  341. package/dist/hooks/use-sent-draft.d.ts +8 -0
  342. package/dist/hooks/use-sent-draft.js +25 -0
  343. package/dist/hooks/use-source-disclosure.d.ts +5 -0
  344. package/dist/hooks/use-source-disclosure.js +9 -0
  345. package/dist/hooks/use-sources.d.ts +3 -0
  346. package/dist/hooks/use-sources.js +9 -0
  347. package/dist/hooks/use-tool-approval-memory.d.ts +8 -0
  348. package/dist/hooks/use-tool-approval-memory.js +16 -0
  349. package/dist/hooks/use-tool-approval.d.ts +10 -0
  350. package/dist/hooks/use-tool-approval.js +26 -0
  351. package/dist/hooks/use-turn-queue.d.ts +32 -0
  352. package/dist/hooks/use-turn-queue.js +33 -0
  353. package/dist/index.d.ts +10 -0
  354. package/dist/index.js +5 -0
  355. package/dist/next.d.ts +43 -0
  356. package/dist/next.js +10 -0
  357. package/dist/services/approval-memory.d.ts +9 -0
  358. package/dist/services/approval-memory.js +32 -0
  359. package/dist/services/approval.d.ts +14 -0
  360. package/dist/services/approval.js +37 -0
  361. package/dist/services/branches.d.ts +27 -0
  362. package/dist/services/branches.js +131 -0
  363. package/dist/services/chat-session.d.ts +20 -0
  364. package/dist/services/chat-session.js +61 -0
  365. package/dist/services/choices.d.ts +31 -0
  366. package/dist/services/choices.js +39 -0
  367. package/dist/services/continue.d.ts +8 -0
  368. package/dist/services/continue.js +24 -0
  369. package/dist/services/errors.d.ts +52 -0
  370. package/dist/services/errors.js +54 -0
  371. package/dist/services/media.d.ts +9 -0
  372. package/dist/services/media.js +19 -0
  373. package/dist/services/messages.d.ts +44 -0
  374. package/dist/services/messages.js +0 -0
  375. package/dist/services/optional.d.ts +7 -0
  376. package/dist/services/optional.js +7 -0
  377. package/dist/services/ports.d.ts +31 -0
  378. package/dist/services/ports.js +0 -0
  379. package/dist/services/questions.d.ts +67 -0
  380. package/dist/services/questions.js +119 -0
  381. package/dist/services/request.d.ts +26 -0
  382. package/dist/services/request.js +85 -0
  383. package/dist/services/selection.d.ts +10 -0
  384. package/dist/services/selection.js +49 -0
  385. package/dist/services/sources.d.ts +11 -0
  386. package/dist/services/sources.js +29 -0
  387. package/dist/services/tools.d.ts +11 -0
  388. package/dist/services/tools.js +0 -0
  389. package/dist/services/turn.d.ts +84 -0
  390. package/dist/services/turn.js +0 -0
  391. package/dist/services/types.d.ts +23 -0
  392. package/dist/services/types.js +15 -0
  393. package/package.json +822 -0
@@ -0,0 +1,9 @@
1
+ type Memory = {
2
+ snapshot(): readonly string[];
3
+ watch(listener: () => void): () => void;
4
+ remember(tool: string): void;
5
+ forget(tool: string): void;
6
+ };
7
+ /** Consent belongs to this Chat instance, never a user-wide storage key or another tab. */
8
+ export declare function toolApprovalMemory(chat: object): Memory;
9
+ export {};
@@ -0,0 +1,32 @@
1
+ const memories = /* @__PURE__ */ new WeakMap();
2
+ const EMPTY = [];
3
+ function toolApprovalMemory(chat) {
4
+ const existing = memories.get(chat);
5
+ if (existing) return existing;
6
+ let names = EMPTY;
7
+ const listeners = /* @__PURE__ */ new Set();
8
+ const update = (next) => {
9
+ names = next;
10
+ for (const listener of [...listeners]) listener();
11
+ };
12
+ const memory = {
13
+ snapshot: () => names,
14
+ watch: (listener) => {
15
+ listeners.add(listener);
16
+ return () => {
17
+ listeners.delete(listener);
18
+ };
19
+ },
20
+ remember: (tool) => {
21
+ if (!names.includes(tool)) update([...names, tool]);
22
+ },
23
+ forget: (tool) => {
24
+ if (names.includes(tool)) update(names.filter((name) => name !== tool));
25
+ }
26
+ };
27
+ memories.set(chat, memory);
28
+ return memory;
29
+ }
30
+ export {
31
+ toolApprovalMemory
32
+ };
@@ -0,0 +1,14 @@
1
+ import type { Chat } from "@ai-sdk/react";
2
+ import type { AiChatMessage } from "./messages";
3
+ type ApprovalChat = Pick<Chat<AiChatMessage>, "messages" | "addToolApprovalResponse">;
4
+ /** One decision per native approval id, even when two views share a chat. */
5
+ export declare function respondToApproval(chat: ApprovalChat, id: string, approved: boolean): Promise<void>;
6
+ /** A decision was just made, so the SDK may be about to continue the turn on its own. */
7
+ export declare function approvalDecided(messages: AiChatMessage[]): boolean;
8
+ /**
9
+ * A follow-up cannot overtake a tool decision. A decision already made holds the next send only
10
+ * while its turn is still running: once the turn is over there is nothing left to wait for, and
11
+ * a turn that died after the approval must not hold every later message forever.
12
+ */
13
+ export declare function awaitingApproval(messages: AiChatMessage[], running?: boolean): boolean;
14
+ export {};
@@ -0,0 +1,37 @@
1
+ import { isToolUIPart } from "ai";
2
+ const claims = /* @__PURE__ */ new WeakMap();
3
+ async function respondToApproval(chat, id, approved) {
4
+ let pending = claims.get(chat);
5
+ if (!pending) {
6
+ pending = /* @__PURE__ */ new Set();
7
+ claims.set(chat, pending);
8
+ }
9
+ if (pending.has(id)) return;
10
+ const exists = chat.messages.some(
11
+ (message) => message.parts.some(
12
+ (part) => isToolUIPart(part) && part.state === "approval-requested" && part.approval.id === id
13
+ )
14
+ );
15
+ if (!exists) return;
16
+ pending.add(id);
17
+ try {
18
+ await chat.addToolApprovalResponse({ id, approved });
19
+ } finally {
20
+ pending.delete(id);
21
+ }
22
+ }
23
+ function approvalDecided(messages) {
24
+ const last = messages.at(-1);
25
+ return last?.role === "assistant" && last.parts.some((part) => isToolUIPart(part) && part.state === "approval-responded");
26
+ }
27
+ function awaitingApproval(messages, running = false) {
28
+ const last = messages.at(-1);
29
+ return last?.role === "assistant" && last.parts.some(
30
+ (part) => isToolUIPart(part) && (part.state === "approval-requested" || running && part.state === "approval-responded")
31
+ );
32
+ }
33
+ export {
34
+ approvalDecided,
35
+ awaitingApproval,
36
+ respondToApproval
37
+ };
@@ -0,0 +1,27 @@
1
+ import type { AiChatMessage } from "./messages";
2
+ export type BranchPage = {
3
+ id: string;
4
+ index: number;
5
+ total: number;
6
+ anchored: boolean;
7
+ };
8
+ export type BranchLocation = {
9
+ at: number;
10
+ role: AiChatMessage["role"];
11
+ page: BranchPage;
12
+ parentId: string | null;
13
+ originId: string;
14
+ };
15
+ export type ConversationBranches = {
16
+ snapshot: () => readonly BranchLocation[];
17
+ watch: (listener: () => void) => () => void;
18
+ fork: (messages: readonly AiChatMessage[], messageId: string) => boolean;
19
+ page: (messages: readonly AiChatMessage[], messageId: string) => BranchPage | null;
20
+ move: (messages: readonly AiChatMessage[], groupId: string, delta: number) => AiChatMessage[] | null;
21
+ };
22
+ /** Snapshots and branch choices belong to this Chat object, never to a global/localStorage key. */
23
+ export declare function conversationBranches(chat: object): ConversationBranches;
24
+ /** Preserve files and custom data parts in their original positions when replacing prose. */
25
+ export declare function editedParts(message: AiChatMessage, text: string): AiChatMessage["parts"];
26
+ /** A missing assistant after refusal keeps its previous versions on the preceding user row. */
27
+ export declare function branchPage(messages: readonly AiChatMessage[], messageId: string, locations: readonly BranchLocation[]): BranchPage | null;
@@ -0,0 +1,131 @@
1
+ const memories = /* @__PURE__ */ new WeakMap();
2
+ const clone = (messages) => structuredClone(messages);
3
+ function samePrefix(messages, prefix) {
4
+ return prefix.length <= messages.length && prefix.every((message, index) => JSON.stringify(message) === JSON.stringify(messages[index]));
5
+ }
6
+ function conversationBranches(chat) {
7
+ const existing = memories.get(chat);
8
+ if (existing) return existing;
9
+ const groups = /* @__PURE__ */ new Map();
10
+ let active = /* @__PURE__ */ new Map();
11
+ let locations = [];
12
+ let serial = 0;
13
+ const listeners = /* @__PURE__ */ new Set();
14
+ const emit = () => {
15
+ locations = [...active].flatMap(([id, index]) => {
16
+ const group = groups.get(id);
17
+ return group && group.versions.length > 1 ? [
18
+ {
19
+ at: group.at,
20
+ role: group.role,
21
+ parentId: group.prefix.at(-1)?.id ?? null,
22
+ originId: group.originId,
23
+ page: { id, index: index + 1, total: group.versions.length, anchored: true }
24
+ }
25
+ ] : [];
26
+ });
27
+ for (const listener of [...listeners]) listener();
28
+ };
29
+ const version = (messages) => ({
30
+ messages: clone(messages),
31
+ lineage: new Map(active)
32
+ });
33
+ const save = (messages) => {
34
+ const current = version(messages);
35
+ for (const [id, index] of active) {
36
+ const group = groups.get(id);
37
+ if (group) group.versions[index] = current;
38
+ }
39
+ };
40
+ const memory = {
41
+ snapshot: () => locations,
42
+ watch: (listener) => {
43
+ listeners.add(listener);
44
+ return () => {
45
+ listeners.delete(listener);
46
+ };
47
+ },
48
+ fork: (messages, messageId) => {
49
+ const at = messages.findIndex((message2) => message2.id === messageId);
50
+ const message = messages[at];
51
+ if (!message || !["user", "assistant"].includes(message.role)) return false;
52
+ save(messages);
53
+ let group = [...groups.values()].find(
54
+ (group2) => group2.at === at && group2.role === message.role && samePrefix(messages, group2.prefix)
55
+ );
56
+ if (!group) {
57
+ group = {
58
+ id: `branch-${++serial}`,
59
+ originId: message.id,
60
+ at,
61
+ role: message.role,
62
+ prefix: clone(messages.slice(0, at)),
63
+ versions: []
64
+ };
65
+ groups.set(group.id, group);
66
+ active.set(group.id, 0);
67
+ group.versions.push(version(messages));
68
+ } else if (!active.has(group.id)) {
69
+ const matching = group.versions.findIndex(
70
+ (version2) => JSON.stringify(version2.messages[at]) === JSON.stringify(message)
71
+ );
72
+ const current = matching < 0 ? group.versions.length : matching;
73
+ active.set(group.id, current);
74
+ group.versions[current] = version(messages);
75
+ }
76
+ for (const id of active.keys()) {
77
+ const each = groups.get(id);
78
+ if (each && each.at >= at) active.delete(id);
79
+ }
80
+ active.set(group.id, group.versions.length);
81
+ group.versions.push(version(messages));
82
+ emit();
83
+ return true;
84
+ },
85
+ page: (messages, messageId) => branchPage(messages, messageId, locations),
86
+ move: (messages, groupId, delta) => {
87
+ const group = groups.get(groupId);
88
+ const current = active.get(groupId);
89
+ if (!group || current === void 0) return null;
90
+ const next = Math.max(0, Math.min(group.versions.length - 1, current + delta));
91
+ if (next === current) return null;
92
+ save(messages);
93
+ const target = group.versions[next];
94
+ active = new Map(target.lineage);
95
+ active.set(groupId, next);
96
+ const restored = clone(target.messages);
97
+ emit();
98
+ return restored;
99
+ }
100
+ };
101
+ memories.set(chat, memory);
102
+ return memory;
103
+ }
104
+ function editedParts(message, text) {
105
+ if (!text) return message.parts.filter((part) => part.type !== "text");
106
+ let written = false;
107
+ const parts = message.parts.flatMap((part) => {
108
+ if (part.type !== "text") return [part];
109
+ if (written) return [];
110
+ written = true;
111
+ return [{ ...part, text }];
112
+ });
113
+ return written ? parts : [...parts, { type: "text", text }];
114
+ }
115
+ function branchPage(messages, messageId, locations) {
116
+ const at = messages.findIndex((message) => message.id === messageId);
117
+ if (at < 0) return null;
118
+ const relevant = locations.filter(
119
+ (location) => (location.at === 0 || messages[location.at - 1]?.id === location.parentId) && (location.role !== "user" || messages[location.at]?.id === location.originId)
120
+ );
121
+ const refused = relevant.find(
122
+ (location) => location.role === "assistant" && location.at === at + 1 && messages[at].role === "user" && messages.length === at + 1
123
+ );
124
+ if (refused) return { ...refused.page, anchored: false };
125
+ return relevant.find((location) => location.at === at && location.role === messages[at].role)?.page ?? null;
126
+ }
127
+ export {
128
+ branchPage,
129
+ conversationBranches,
130
+ editedParts
131
+ };
@@ -0,0 +1,20 @@
1
+ import type { ChatTransport } from "ai";
2
+ import type { AiChatClient } from "./ports";
3
+ import type { AiChatMessage } from "./messages";
4
+ import type { ChatChoices, ChatSelection } from "./choices";
5
+ export type ChatSettingsState = {
6
+ choices: ChatChoices | null;
7
+ selection: ChatSelection;
8
+ };
9
+ export type ChatSettings = {
10
+ snapshot(): ChatSettingsState;
11
+ watch(listener: () => void): () => void;
12
+ load(): Promise<void>;
13
+ model(id: string): void;
14
+ toggle(id: string): void;
15
+ };
16
+ /** Bind one conversation's catalog, controls and transport; custom Chats opt in explicitly. */
17
+ export declare function chatSession(client: AiChatClient, name: string): {
18
+ transport: ChatTransport<AiChatMessage>;
19
+ settings: ChatSettings | null;
20
+ };
@@ -0,0 +1,61 @@
1
+ const EMPTY = { choices: null, selection: {} };
2
+ function chatSession(client, name) {
3
+ const transport = client.transportFor(name);
4
+ if (!client.choicesFor) return { transport, settings: null };
5
+ let state = EMPTY;
6
+ let loading;
7
+ const listeners = /* @__PURE__ */ new Set();
8
+ const update = (next) => {
9
+ state = next;
10
+ for (const listener of [...listeners]) listener();
11
+ };
12
+ const settings = {
13
+ snapshot: () => state,
14
+ watch(listener) {
15
+ listeners.add(listener);
16
+ return () => {
17
+ listeners.delete(listener);
18
+ };
19
+ },
20
+ load() {
21
+ return loading ??= Promise.resolve().then(() => client.choicesFor(name)).then((choices) => {
22
+ update({ choices, selection: choices ? {
23
+ model: "",
24
+ ...choices.tools ? { tools: choices.tools.filter((tool) => tool.defaultEnabled).map((tool) => tool.id) } : {}
25
+ } : {} });
26
+ }).catch(() => {
27
+ });
28
+ },
29
+ model(id) {
30
+ if (!state.choices?.models?.some((model) => model.id === id)) return;
31
+ update({ ...state, selection: { ...state.selection, model: id } });
32
+ },
33
+ toggle(id) {
34
+ if (!state.choices?.tools?.some((tool) => tool.id === id)) return;
35
+ const active = state.selection.tools ?? [];
36
+ update({ ...state, selection: { ...state.selection, tools: active.includes(id) ? active.filter((tool) => tool !== id) : [...active, id] } });
37
+ }
38
+ };
39
+ const turns = /* @__PURE__ */ new Map();
40
+ return {
41
+ settings,
42
+ transport: {
43
+ ...transport,
44
+ reconnectToStream: (request) => transport.reconnectToStream(request),
45
+ sendMessages(request) {
46
+ const user = request.messages.findLast((message) => message.role === "user");
47
+ const key = `${request.chatId}\0${user?.id ?? ""}`;
48
+ const newTurn = request.trigger === "regenerate-message" || request.messages.at(-1)?.role === "user";
49
+ const selection = !newTurn && turns.has(key) ? turns.get(key) : {
50
+ ...state.selection,
51
+ ...state.selection.tools ? { tools: [...state.selection.tools] } : {}
52
+ };
53
+ turns.set(key, selection);
54
+ return transport.sendMessages({ ...request, body: { ...request.body, selection } });
55
+ }
56
+ }
57
+ };
58
+ }
59
+ export {
60
+ chatSession
61
+ };
@@ -0,0 +1,31 @@
1
+ import type { ChatConfig, ChatModel } from "./turn";
2
+ export type ChatModelOption = {
3
+ label: string;
4
+ model: ChatModel;
5
+ contextWindow?: number;
6
+ };
7
+ export type ChatToolOption = {
8
+ id: string;
9
+ label: string;
10
+ defaultEnabled?: boolean;
11
+ };
12
+ export type ChatSelection = {
13
+ model?: string;
14
+ tools?: readonly string[];
15
+ };
16
+ export type ChatChoices = {
17
+ /** The empty id selects the existing default model. Null means no picker. */
18
+ models: readonly {
19
+ id: string;
20
+ label: string;
21
+ contextWindow: number | null;
22
+ }[] | null;
23
+ tools: readonly {
24
+ id: string;
25
+ label: string;
26
+ defaultEnabled: boolean;
27
+ }[] | null;
28
+ };
29
+ export declare function contextWindowOf(value: number | undefined): number | null;
30
+ /** Publish labels and limits only, never model instances, tools, credentials or prompts. */
31
+ export declare function choicesOf(config: ChatConfig): ChatChoices | null;
@@ -0,0 +1,39 @@
1
+ function contextWindowOf(value) {
2
+ return value !== void 0 && Number.isSafeInteger(value) && value > 0 ? value : null;
3
+ }
4
+ function choicesOf(config) {
5
+ const models = Object.entries(config.models ?? {}).filter(
6
+ ([id]) => id.length > 0 && id.length <= 128
7
+ );
8
+ const seen = /* @__PURE__ */ new Set();
9
+ const tools = (config.toolChoices ?? []).filter(({ id }) => {
10
+ if (!id || seen.has(id) || !Object.hasOwn(config.tools ?? {}, id))
11
+ return false;
12
+ seen.add(id);
13
+ return true;
14
+ }).map(({ id, label, defaultEnabled = true }) => ({
15
+ id,
16
+ label,
17
+ defaultEnabled
18
+ }));
19
+ if (!models.length && !tools.length) return null;
20
+ return {
21
+ models: models.length ? [
22
+ {
23
+ id: "",
24
+ label: typeof config.model === "string" ? config.model : config.model.modelId,
25
+ contextWindow: contextWindowOf(config.contextWindow)
26
+ },
27
+ ...models.map(([id, value]) => ({
28
+ id,
29
+ label: value.label,
30
+ contextWindow: contextWindowOf(value.contextWindow)
31
+ }))
32
+ ] : null,
33
+ tools: tools.length ? tools : null
34
+ };
35
+ }
36
+ export {
37
+ choicesOf,
38
+ contextWindowOf
39
+ };
@@ -0,0 +1,8 @@
1
+ import type { AiChatMessage } from "./messages";
2
+ /** Continue only client questions here; server-executed tools have their own step loop. */
3
+ export declare function shouldContinueChat(input: {
4
+ messages: AiChatMessage[];
5
+ }): boolean;
6
+ /** What holds a queued follow-up back besides a running turn: the person, or a tool still at work. */
7
+ export type Blocker = "question" | "approval" | "tool";
8
+ export declare function blockedBy(messages: AiChatMessage[], running: boolean): Blocker | null;
@@ -0,0 +1,24 @@
1
+ import {
2
+ isToolUIPart,
3
+ lastAssistantMessageIsCompleteWithApprovalResponses,
4
+ lastAssistantMessageIsCompleteWithToolCalls
5
+ } from "ai";
6
+ import { awaitingApproval } from "./approval.js";
7
+ import { awaitingQuestion, isSkippedQuestion, questionInput } from "./questions.js";
8
+ function shouldContinueChat(input) {
9
+ if (lastAssistantMessageIsCompleteWithApprovalResponses(input)) return true;
10
+ const parts = input.messages.at(-1)?.parts ?? [];
11
+ const start = parts.reduce((index, part, next) => part.type === "step-start" ? next : index, -1);
12
+ const questions = parts.slice(start + 1).filter((part) => isToolUIPart(part) && questionInput(part) !== null);
13
+ if (questions.some(isSkippedQuestion)) return false;
14
+ return questions.length > 0 && lastAssistantMessageIsCompleteWithToolCalls(input);
15
+ }
16
+ function blockedBy(messages, running) {
17
+ if (awaitingQuestion(messages)) return "question";
18
+ if (awaitingApproval(messages)) return "approval";
19
+ return awaitingApproval(messages, running) ? "tool" : null;
20
+ }
21
+ export {
22
+ blockedBy,
23
+ shouldContinueChat
24
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Every way a turn can be refused, and nothing else.
3
+ *
4
+ * A closed union because the screen branches on it: a quota refusal wants an upgrade link,
5
+ * an outage wants a retry, and an unknown chat wants a developer to open `config.ts`. One
6
+ * "chat failed" banner for all three would be three lies.
7
+ */
8
+ export type ChatErrorSlug =
9
+ /** No session where the rules want one. */
10
+ "ai-chat/unauthorized"
11
+ /** The rules refused this actor for this chat. */
12
+ | "ai-chat/forbidden"
13
+ /** No entry under that key. Never a fall back to another chat. */
14
+ | "ai-chat/unknown-chat"
15
+ /** The body is not a turn. */
16
+ | "ai-chat/invalid-request" | "ai-chat/rate-limited"
17
+ /** Refused before the model ran: the plan, not the rules. */
18
+ | "ai-chat/quota-exceeded"
19
+ /** The provider is unreachable or misconfigured. */
20
+ | "ai-chat/model-unavailable"
21
+ /** The client went away mid-turn. */
22
+ | "ai-chat/aborted"
23
+ /** A failure this contract does not model, so an unexpected one has somewhere honest to land. */
24
+ | "ai-chat/unavailable";
25
+ export declare const CHAT_ERROR_SLUGS: readonly ["ai-chat/unauthorized", "ai-chat/forbidden", "ai-chat/unknown-chat", "ai-chat/invalid-request", "ai-chat/rate-limited", "ai-chat/quota-exceeded", "ai-chat/model-unavailable", "ai-chat/aborted", "ai-chat/unavailable"];
26
+ export declare class ChatError extends Error {
27
+ readonly slug: ChatErrorSlug;
28
+ /** For an operator. Never rendered, never sent to a browser. */
29
+ readonly detail?: string;
30
+ /** Authoritative earliest retry, as Unix milliseconds. */
31
+ readonly retryAt?: number;
32
+ constructor(slug: ChatErrorSlug, message: string, detail?: string, retryAt?: number);
33
+ }
34
+ export declare function isChatError(value: unknown): value is ChatError;
35
+ /**
36
+ * The one place a slug becomes a status, so a route and a client cannot disagree.
37
+ *
38
+ * 499 is nginx's rather than the RFC's, and it is still the right one: a turn the client
39
+ * abandoned is not a server error, and the alternatives are lying with 200 or alarming with
40
+ * 500.
41
+ */
42
+ export declare const statusForChatError: Record<ChatErrorSlug, number>;
43
+ export declare function isChatErrorSlug(value: unknown): value is ChatErrorSlug;
44
+ /**
45
+ * A refusal as a route says it, which is the same text wherever it lands.
46
+ *
47
+ * Before a stream starts it is the body of a refused request, and the SDK's transport makes
48
+ * it the message of the `APICallError` a chat then holds; once a stream has started it is
49
+ * the `errorText` of an `error` chunk, which the SDK makes the message of an `Error`. One
50
+ * shape for both is what lets a screen read one slug out of either.
51
+ */
52
+ export declare function refusalText(error: ChatError): string;
@@ -0,0 +1,54 @@
1
+ const CHAT_ERROR_SLUGS = [
2
+ "ai-chat/unauthorized",
3
+ "ai-chat/forbidden",
4
+ "ai-chat/unknown-chat",
5
+ "ai-chat/invalid-request",
6
+ "ai-chat/rate-limited",
7
+ "ai-chat/quota-exceeded",
8
+ "ai-chat/model-unavailable",
9
+ "ai-chat/aborted",
10
+ "ai-chat/unavailable"
11
+ ];
12
+ class ChatError extends Error {
13
+ slug;
14
+ /** For an operator. Never rendered, never sent to a browser. */
15
+ detail;
16
+ /** Authoritative earliest retry, as Unix milliseconds. */
17
+ retryAt;
18
+ constructor(slug, message, detail, retryAt) {
19
+ super(message);
20
+ this.name = "ChatError";
21
+ this.slug = slug;
22
+ this.detail = detail;
23
+ if (typeof retryAt === "number" && Number.isFinite(retryAt) && retryAt > 0 && retryAt <= 864e13)
24
+ this.retryAt = retryAt;
25
+ }
26
+ }
27
+ function isChatError(value) {
28
+ return value instanceof ChatError;
29
+ }
30
+ const statusForChatError = {
31
+ "ai-chat/unauthorized": 401,
32
+ "ai-chat/forbidden": 403,
33
+ "ai-chat/unknown-chat": 404,
34
+ "ai-chat/invalid-request": 400,
35
+ "ai-chat/rate-limited": 429,
36
+ "ai-chat/quota-exceeded": 402,
37
+ "ai-chat/model-unavailable": 502,
38
+ "ai-chat/aborted": 499,
39
+ "ai-chat/unavailable": 503
40
+ };
41
+ function isChatErrorSlug(value) {
42
+ return typeof value === "string" && CHAT_ERROR_SLUGS.includes(value);
43
+ }
44
+ function refusalText(error) {
45
+ return JSON.stringify({ error: error.slug, message: error.message, retryAt: error.retryAt });
46
+ }
47
+ export {
48
+ CHAT_ERROR_SLUGS,
49
+ ChatError,
50
+ isChatError,
51
+ isChatErrorSlug,
52
+ refusalText,
53
+ statusForChatError
54
+ };
@@ -0,0 +1,9 @@
1
+ import type { FileUIPart } from "ai";
2
+ export type ChatMediaKind = "image" | "audio" | "video" | "file";
3
+ export type ChatMedia = {
4
+ file: FileUIPart;
5
+ url: string | null;
6
+ kind: ChatMediaKind;
7
+ };
8
+ /** A message is untrusted content. Data URLs can only contain the declared passive format. */
9
+ export declare function mediaOf(file: FileUIPart): ChatMedia;
@@ -0,0 +1,19 @@
1
+ const images = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp", "image/gif", "image/avif", "image/bmp"]);
2
+ function mediaOf(file) {
3
+ const mime = file.mediaType.toLowerCase().split(";")[0].trim();
4
+ const kind = images.has(mime) ? "image" : mime.startsWith("audio/") ? "audio" : mime.startsWith("video/") ? "video" : "file";
5
+ let url = null;
6
+ try {
7
+ const parsed = new URL(file.url);
8
+ if ((parsed.protocol === "https:" || parsed.protocol === "http:") && !parsed.username && !parsed.password)
9
+ url = parsed.href;
10
+ if (parsed.protocol === "blob:") url = file.url;
11
+ if (parsed.protocol === "data:" && kind !== "file" && file.url.startsWith(`data:${mime};base64,`) && /^[A-Za-z0-9+/]*={0,2}$/.test(file.url.slice(file.url.indexOf(",") + 1)))
12
+ url = file.url;
13
+ } catch {
14
+ }
15
+ return { file, url, kind };
16
+ }
17
+ export {
18
+ mediaOf
19
+ };
@@ -0,0 +1,44 @@
1
+ import type { FinishReason, LanguageModelUsage, UIMessage } from "ai";
2
+ /**
3
+ * What a route writes on a message beside its parts.
4
+ *
5
+ * The one thing about a message the AI SDK leaves to the app: its parts, their order and
6
+ * their states are the SDK's own `UIMessage`, and this types the `metadata` beside them the
7
+ * way the SDK's message-metadata guide does. Both fields are optional because both are
8
+ * genuinely unknown for some backends and some turns, and a meter that guessed would be a
9
+ * number somebody bills against. → docs/rules/capability-honesty.md
10
+ */
11
+ export type AiChatMetadata = {
12
+ /** Suggested next prompts supplied by the application, never fabricated by the UI. */
13
+ followUps?: string[];
14
+ finishReason?: FinishReason;
15
+ /** Explicit user intent on a follow-up, kept alongside the native message. */
16
+ intent?: "continue";
17
+ continues?: string;
18
+ /** The model that answered, as the route was configured with it. */
19
+ model?: string;
20
+ modelChoice?: string;
21
+ /** Host-declared model capacity, not an estimate derived from text. */
22
+ contextWindow?: number;
23
+ /** Input tokens of the last model request, never the sum of a tool loop. */
24
+ contextTokens?: number | null;
25
+ /** What the turn cost, as the provider counted it at the SDK's `finish` part. */
26
+ usage?: LanguageModelUsage;
27
+ /** Server-observed milliseconds from starting the model call; never inferred from tokens. */
28
+ timing?: {
29
+ firstTokenMs?: number;
30
+ durationMs?: number;
31
+ };
32
+ };
33
+ export type AiChatMessage = UIMessage<AiChatMetadata>;
34
+ /** One of a message's parts, whichever of the SDK's kinds it is. */
35
+ export type AiChatPart = AiChatMessage["parts"][number];
36
+ /** A host-owned feedback write. A resolved promise means accepted, not merely clicked. */
37
+ export type ChatFeedback = {
38
+ chatId: string;
39
+ messageId: string;
40
+ rating: "up" | "down" | null;
41
+ };
42
+ export type ChatFeedbackOptions = {
43
+ onFeedback?: (feedback: ChatFeedback) => void | Promise<void>;
44
+ };
File without changes
@@ -0,0 +1,7 @@
1
+ import type { AiChatClient } from "./types";
2
+ /**
3
+ * The client of the config the screen targets, or null when the service has no such config.
4
+ * Hooks import only this, so a screen builds, and runs on a fixture, before a service is set up.
5
+ * The registry (`services/index.ts` in the app) is where configs are found.
6
+ */
7
+ export declare function getAiChat(config?: string): AiChatClient | null;
@@ -0,0 +1,7 @@
1
+ import { clientOf } from "@slices.sh/core";
2
+ function getAiChat(config) {
3
+ return clientOf("ai-chat", config);
4
+ }
5
+ export {
6
+ getAiChat
7
+ };