@gram-ai/elements 1.27.4 → 1.27.6

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 (278) hide show
  1. package/README.md +72 -60
  2. package/README.typedoc.md +6 -6
  3. package/bin/cli.js +74 -74
  4. package/dist/compat-shims-CO9JXXV4.cjs.map +1 -1
  5. package/dist/{compat-shims-BPJ7Q68c.js → compat-shims-DxtUrORi.js} +4 -2
  6. package/dist/compat-shims-DxtUrORi.js.map +1 -0
  7. package/dist/components/ShareButton/index.d.ts +2 -2
  8. package/dist/components/assistant-ui/message-feedback.d.ts +1 -1
  9. package/dist/components/assistant-ui/tooltip-icon-button.d.ts +2 -2
  10. package/dist/components/ui/avatar.d.ts +2 -2
  11. package/dist/components/ui/button.d.ts +1 -1
  12. package/dist/components/ui/calendar.d.ts +1 -1
  13. package/dist/components/ui/collapsible.d.ts +1 -1
  14. package/dist/components/ui/dialog.d.ts +4 -4
  15. package/dist/components/ui/popover.d.ts +2 -2
  16. package/dist/components/ui/skeleton.d.ts +1 -1
  17. package/dist/components/ui/time-range-picker.d.ts +1 -1
  18. package/dist/components/ui/tool-ui.d.ts +7 -7
  19. package/dist/components/ui/tooltip.d.ts +2 -2
  20. package/dist/contexts/ConnectionStatusContext.d.ts +1 -1
  21. package/dist/elements.cjs +1 -1
  22. package/dist/elements.css +1 -1
  23. package/dist/elements.js +2 -2
  24. package/dist/hooks/useDensity.d.ts +73 -73
  25. package/dist/hooks/useMCPTools.d.ts +1 -1
  26. package/dist/hooks/useRadius.d.ts +1 -1
  27. package/dist/{index-KSX4Qjip.cjs → index-A17b62wR.cjs} +10 -10
  28. package/dist/index-A17b62wR.cjs.map +1 -0
  29. package/dist/{index-BpJstUh1.cjs → index-C4bFBGfl.cjs} +4 -4
  30. package/dist/{index-BpJstUh1.cjs.map → index-C4bFBGfl.cjs.map} +1 -1
  31. package/dist/{index-CUitXazZ.js → index-D93pV0_o.js} +55 -55
  32. package/dist/{index-CUitXazZ.js.map → index-D93pV0_o.js.map} +1 -1
  33. package/dist/{index-D0bAYNQy.js → index-Dm2wLFTN.js} +304 -282
  34. package/dist/index-Dm2wLFTN.js.map +1 -0
  35. package/dist/lib/cassette.d.ts +4 -4
  36. package/dist/lib/errorTracking.d.ts +1 -1
  37. package/dist/lib/messageConverter.d.ts +1 -1
  38. package/dist/lib/models.d.ts +1 -1
  39. package/dist/plugins/chart/ui/bar-chart.d.ts +1 -1
  40. package/dist/plugins/generative-ui/ui/accordion-wrapper.d.ts +2 -2
  41. package/dist/plugins/generative-ui/ui/accordion.d.ts +1 -1
  42. package/dist/plugins/generative-ui/ui/action-button.d.ts +2 -2
  43. package/dist/plugins/generative-ui/ui/alert-wrapper.d.ts +1 -1
  44. package/dist/plugins/generative-ui/ui/alert.d.ts +4 -4
  45. package/dist/plugins/generative-ui/ui/avatar.d.ts +5 -5
  46. package/dist/plugins/generative-ui/ui/badge.d.ts +2 -2
  47. package/dist/plugins/generative-ui/ui/button-wrapper.d.ts +2 -2
  48. package/dist/plugins/generative-ui/ui/button.d.ts +2 -2
  49. package/dist/plugins/generative-ui/ui/card-wrapper.d.ts +2 -2
  50. package/dist/plugins/generative-ui/ui/card.d.ts +8 -8
  51. package/dist/plugins/generative-ui/ui/checkbox.d.ts +1 -1
  52. package/dist/plugins/generative-ui/ui/data-table.d.ts +2 -2
  53. package/dist/plugins/generative-ui/ui/dialog.d.ts +3 -3
  54. package/dist/plugins/generative-ui/ui/dropdown-menu.d.ts +3 -3
  55. package/dist/plugins/generative-ui/ui/grid.d.ts +3 -3
  56. package/dist/plugins/generative-ui/ui/input-wrapper.d.ts +1 -1
  57. package/dist/plugins/generative-ui/ui/input.d.ts +2 -2
  58. package/dist/plugins/generative-ui/ui/label.d.ts +1 -1
  59. package/dist/plugins/generative-ui/ui/metric.d.ts +3 -3
  60. package/dist/plugins/generative-ui/ui/pagination.d.ts +6 -6
  61. package/dist/plugins/generative-ui/ui/popover.d.ts +4 -4
  62. package/dist/plugins/generative-ui/ui/progress.d.ts +2 -2
  63. package/dist/plugins/generative-ui/ui/radio-group.d.ts +1 -1
  64. package/dist/plugins/generative-ui/ui/select.d.ts +2 -2
  65. package/dist/plugins/generative-ui/ui/separator.d.ts +1 -1
  66. package/dist/plugins/generative-ui/ui/skeleton.d.ts +1 -1
  67. package/dist/plugins/generative-ui/ui/stack.d.ts +6 -6
  68. package/dist/plugins/generative-ui/ui/switch.d.ts +2 -2
  69. package/dist/plugins/generative-ui/ui/table.d.ts +9 -9
  70. package/dist/plugins/generative-ui/ui/tabs-wrapper.d.ts +1 -1
  71. package/dist/plugins/generative-ui/ui/tabs.d.ts +1 -1
  72. package/dist/plugins/generative-ui/ui/text.d.ts +3 -3
  73. package/dist/plugins/generative-ui/ui/textarea.d.ts +2 -2
  74. package/dist/plugins/generative-ui/ui/tooltip.d.ts +1 -1
  75. package/dist/plugins.cjs +1 -1
  76. package/dist/plugins.js +1 -1
  77. package/dist/{profiler-CyzxBxVz.cjs → profiler-Cbbf4eEX.cjs} +2 -2
  78. package/dist/{profiler-CyzxBxVz.cjs.map → profiler-Cbbf4eEX.cjs.map} +1 -1
  79. package/dist/{profiler-BFkhZRxj.js → profiler-mca4IXaY.js} +2 -2
  80. package/dist/{profiler-BFkhZRxj.js.map → profiler-mca4IXaY.js.map} +1 -1
  81. package/dist/react-shim.js +1 -1
  82. package/dist/server/express.cjs.map +1 -1
  83. package/dist/server/express.js.map +1 -1
  84. package/dist/{startRecording-C-PPAs_Z.js → startRecording-BCafdS7B.js} +2 -2
  85. package/dist/{startRecording-C-PPAs_Z.js.map → startRecording-BCafdS7B.js.map} +1 -1
  86. package/dist/{startRecording-Dq92sEHf.cjs → startRecording-Eb5f7wqP.cjs} +2 -2
  87. package/dist/{startRecording-Dq92sEHf.cjs.map → startRecording-Eb5f7wqP.cjs.map} +1 -1
  88. package/dist/types/index.d.ts +4 -4
  89. package/package.json +1 -5
  90. package/src/compat-plugin.ts +14 -14
  91. package/src/compat-shims.ts +33 -31
  92. package/src/compat.test.ts +48 -48
  93. package/src/compat.ts +6 -6
  94. package/src/components/Chat/index.tsx +17 -17
  95. package/src/components/Chat/stories/Charts.stories.tsx +98 -98
  96. package/src/components/Chat/stories/Composer.stories.tsx +15 -15
  97. package/src/components/Chat/stories/ConnectionConfiguration.stories.tsx +44 -44
  98. package/src/components/Chat/stories/CustomComponents.stories.tsx +17 -17
  99. package/src/components/Chat/stories/Density.stories.tsx +20 -20
  100. package/src/components/Chat/stories/ErrorBoundary.stories.tsx +47 -47
  101. package/src/components/Chat/stories/FrontendTools.stories.tsx +39 -39
  102. package/src/components/Chat/stories/GenerativeUI.stories.tsx +48 -48
  103. package/src/components/Chat/stories/MessageFeedback.stories.tsx +52 -52
  104. package/src/components/Chat/stories/Modal.stories.tsx +28 -28
  105. package/src/components/Chat/stories/Model.stories.tsx +11 -11
  106. package/src/components/Chat/stories/Radius.stories.tsx +20 -20
  107. package/src/components/Chat/stories/Sidecar.stories.tsx +13 -13
  108. package/src/components/Chat/stories/StyleIsolation.stories.tsx +11 -11
  109. package/src/components/Chat/stories/Theme.stories.tsx +25 -25
  110. package/src/components/Chat/stories/Thread.stories.tsx +25 -25
  111. package/src/components/Chat/stories/ToolApproval.stories.tsx +55 -55
  112. package/src/components/Chat/stories/ToolMentions.stories.tsx +17 -17
  113. package/src/components/Chat/stories/Tools.stories.tsx +88 -88
  114. package/src/components/Chat/stories/Variants.stories.tsx +32 -32
  115. package/src/components/Chat/stories/Welcome.stories.tsx +14 -14
  116. package/src/components/ChatHistory.tsx +7 -7
  117. package/src/components/FrontendTools/index.tsx +5 -5
  118. package/src/components/Replay.stories.tsx +157 -157
  119. package/src/components/Replay.tsx +76 -73
  120. package/src/components/ShadowRoot.tsx +40 -40
  121. package/src/components/ShareButton/index.tsx +32 -32
  122. package/src/components/assistant-ui/assistant-modal.tsx +92 -87
  123. package/src/components/assistant-ui/assistant-sidecar.tsx +35 -35
  124. package/src/components/assistant-ui/attachment.tsx +80 -80
  125. package/src/components/assistant-ui/connection-status-indicator.tsx +33 -33
  126. package/src/components/assistant-ui/error-boundary.tsx +34 -34
  127. package/src/components/assistant-ui/follow-on-suggestions.tsx +26 -26
  128. package/src/components/assistant-ui/markdown-text.tsx +69 -69
  129. package/src/components/assistant-ui/mentioned-tools-badges.tsx +38 -38
  130. package/src/components/assistant-ui/message-feedback.tsx +57 -50
  131. package/src/components/assistant-ui/reasoning.tsx +83 -83
  132. package/src/components/assistant-ui/thread-list.tsx +45 -45
  133. package/src/components/assistant-ui/thread.tsx +278 -278
  134. package/src/components/assistant-ui/tool-fallback.tsx +37 -37
  135. package/src/components/assistant-ui/tool-group.tsx +26 -26
  136. package/src/components/assistant-ui/tool-mention-autocomplete.tsx +122 -122
  137. package/src/components/assistant-ui/tooltip-icon-button.tsx +18 -18
  138. package/src/components/ui/avatar.tsx +12 -12
  139. package/src/components/ui/button.tsx +12 -12
  140. package/src/components/ui/buttonVariants.ts +17 -17
  141. package/src/components/ui/calendar.tsx +106 -106
  142. package/src/components/ui/charts.stories.tsx +56 -56
  143. package/src/components/ui/collapsible.tsx +5 -5
  144. package/src/components/ui/dialog.tsx +30 -30
  145. package/src/components/ui/generative-ui.stories.tsx +200 -200
  146. package/src/components/ui/generative-ui.tsx +26 -26
  147. package/src/components/ui/popover.tsx +14 -14
  148. package/src/components/ui/skeleton.tsx +5 -5
  149. package/src/components/ui/time-range-picker.stories.tsx +80 -80
  150. package/src/components/ui/time-range-picker.tsx +248 -246
  151. package/src/components/ui/tool-ui.stories.tsx +37 -37
  152. package/src/components/ui/tool-ui.tsx +221 -215
  153. package/src/components/ui/tooltip.tsx +15 -15
  154. package/src/constants/tailwind.ts +1 -1
  155. package/src/contexts/ChatIdContext.tsx +7 -7
  156. package/src/contexts/ConnectionStatusContext.tsx +64 -64
  157. package/src/contexts/ElementsProvider.tsx +214 -213
  158. package/src/contexts/ReplayContext.ts +3 -3
  159. package/src/contexts/ToolApprovalContext.tsx +54 -54
  160. package/src/contexts/ToolExecutionContext.tsx +34 -34
  161. package/src/contexts/contexts.ts +7 -7
  162. package/src/contexts/portal-container-context.ts +2 -2
  163. package/src/contexts/portal-container.tsx +7 -7
  164. package/src/embedded.ts +1 -1
  165. package/src/global.css +25 -25
  166. package/src/hooks/useAuth.ts +72 -72
  167. package/src/hooks/useDensity.ts +79 -79
  168. package/src/hooks/useElements.ts +6 -6
  169. package/src/hooks/useExpanded.ts +12 -12
  170. package/src/hooks/useFollowOnSuggestions.ts +83 -83
  171. package/src/hooks/useGramThreadListAdapter.tsx +99 -99
  172. package/src/hooks/useMCPTools.ts +47 -47
  173. package/src/hooks/useModel.ts +14 -14
  174. package/src/hooks/usePluginComponents.ts +11 -11
  175. package/src/hooks/usePortalContainer.ts +5 -5
  176. package/src/hooks/useRadius.ts +23 -23
  177. package/src/hooks/useRecordCassette.ts +34 -34
  178. package/src/hooks/useSession.ts +11 -11
  179. package/src/hooks/useThemeProps.ts +13 -13
  180. package/src/hooks/useThreadId.ts +4 -4
  181. package/src/hooks/useToolApproval.ts +7 -7
  182. package/src/hooks/useToolMentions.ts +40 -40
  183. package/src/index.ts +26 -26
  184. package/src/lib/api.test.ts +61 -61
  185. package/src/lib/api.ts +4 -3
  186. package/src/lib/auth.ts +13 -13
  187. package/src/lib/cassette.ts +84 -84
  188. package/src/lib/easing.ts +1 -1
  189. package/src/lib/errorTracking.config.ts +5 -5
  190. package/src/lib/errorTracking.ts +29 -29
  191. package/src/lib/generative-ui.ts +7 -7
  192. package/src/lib/humanize.ts +3 -3
  193. package/src/lib/messageConverter.test.ts +130 -127
  194. package/src/lib/messageConverter.ts +196 -196
  195. package/src/lib/models.ts +28 -20
  196. package/src/lib/token.test.ts +56 -56
  197. package/src/lib/token.ts +14 -14
  198. package/src/lib/tool-mentions.ts +45 -45
  199. package/src/lib/tools.ts +66 -62
  200. package/src/lib/utils.ts +5 -5
  201. package/src/lib.d.ts +1 -1
  202. package/src/plugins/README.md +5 -5
  203. package/src/plugins/chart/catalog.ts +18 -18
  204. package/src/plugins/chart/chart.test.ts +31 -31
  205. package/src/plugins/chart/component.tsx +34 -34
  206. package/src/plugins/chart/index.ts +4 -4
  207. package/src/plugins/chart/ui/area-chart.tsx +42 -42
  208. package/src/plugins/chart/ui/bar-chart.tsx +46 -46
  209. package/src/plugins/chart/ui/donut-chart.tsx +48 -48
  210. package/src/plugins/chart/ui/index.ts +7 -7
  211. package/src/plugins/chart/ui/line-chart.tsx +43 -43
  212. package/src/plugins/chart/ui/pie-chart.tsx +44 -44
  213. package/src/plugins/chart/ui/radar-chart.tsx +33 -33
  214. package/src/plugins/chart/ui/scatter-chart.tsx +43 -43
  215. package/src/plugins/components/MacOSWindowFrame.tsx +15 -15
  216. package/src/plugins/components/PluginLoadingState.tsx +10 -10
  217. package/src/plugins/components/index.ts +1 -1
  218. package/src/plugins/generative-ui/catalog.ts +54 -54
  219. package/src/plugins/generative-ui/component.tsx +85 -85
  220. package/src/plugins/generative-ui/index.ts +4 -4
  221. package/src/plugins/generative-ui/ui/accordion-wrapper.tsx +16 -16
  222. package/src/plugins/generative-ui/ui/accordion.tsx +16 -16
  223. package/src/plugins/generative-ui/ui/action-button.tsx +28 -28
  224. package/src/plugins/generative-ui/ui/alert-wrapper.tsx +8 -8
  225. package/src/plugins/generative-ui/ui/alert.tsx +20 -20
  226. package/src/plugins/generative-ui/ui/avatar-wrapper.tsx +7 -7
  227. package/src/plugins/generative-ui/ui/avatar.tsx +30 -30
  228. package/src/plugins/generative-ui/ui/badge.tsx +22 -22
  229. package/src/plugins/generative-ui/ui/button-wrapper.tsx +12 -12
  230. package/src/plugins/generative-ui/ui/button.tsx +28 -28
  231. package/src/plugins/generative-ui/ui/card-wrapper.tsx +8 -8
  232. package/src/plugins/generative-ui/ui/card.tsx +27 -27
  233. package/src/plugins/generative-ui/ui/checkbox-wrapper.tsx +9 -9
  234. package/src/plugins/generative-ui/ui/checkbox.tsx +9 -9
  235. package/src/plugins/generative-ui/ui/data-table.tsx +8 -8
  236. package/src/plugins/generative-ui/ui/dialog.tsx +31 -31
  237. package/src/plugins/generative-ui/ui/dropdown-menu.tsx +44 -44
  238. package/src/plugins/generative-ui/ui/grid.tsx +12 -12
  239. package/src/plugins/generative-ui/ui/index.ts +40 -40
  240. package/src/plugins/generative-ui/ui/input-wrapper.tsx +11 -11
  241. package/src/plugins/generative-ui/ui/input.tsx +9 -9
  242. package/src/plugins/generative-ui/ui/label.tsx +8 -8
  243. package/src/plugins/generative-ui/ui/list.tsx +11 -11
  244. package/src/plugins/generative-ui/ui/metric.tsx +23 -23
  245. package/src/plugins/generative-ui/ui/pagination.tsx +28 -28
  246. package/src/plugins/generative-ui/ui/popover.tsx +21 -21
  247. package/src/plugins/generative-ui/ui/progress.tsx +13 -13
  248. package/src/plugins/generative-ui/ui/radio-group.tsx +12 -12
  249. package/src/plugins/generative-ui/ui/select-wrapper.tsx +7 -7
  250. package/src/plugins/generative-ui/ui/select.tsx +37 -37
  251. package/src/plugins/generative-ui/ui/separator.tsx +9 -9
  252. package/src/plugins/generative-ui/ui/skeleton-wrapper.tsx +10 -10
  253. package/src/plugins/generative-ui/ui/skeleton.tsx +5 -5
  254. package/src/plugins/generative-ui/ui/stack.tsx +28 -28
  255. package/src/plugins/generative-ui/ui/switch.tsx +11 -11
  256. package/src/plugins/generative-ui/ui/table.tsx +32 -32
  257. package/src/plugins/generative-ui/ui/tabs-wrapper.tsx +11 -11
  258. package/src/plugins/generative-ui/ui/tabs.tsx +26 -26
  259. package/src/plugins/generative-ui/ui/text.tsx +12 -12
  260. package/src/plugins/generative-ui/ui/textarea.tsx +7 -7
  261. package/src/plugins/generative-ui/ui/tooltip.tsx +12 -12
  262. package/src/plugins/index.ts +7 -7
  263. package/src/react-shim.ts +6 -6
  264. package/src/server/bun.ts +12 -12
  265. package/src/server/core.ts +25 -25
  266. package/src/server/express.ts +17 -15
  267. package/src/server/fastify.ts +14 -14
  268. package/src/server/hono.ts +9 -9
  269. package/src/server/nextjs.ts +12 -12
  270. package/src/server/tanstack-start.ts +12 -12
  271. package/src/server.ts +27 -27
  272. package/src/storybook.d.ts +4 -4
  273. package/src/types/index.ts +124 -124
  274. package/src/types/plugins.ts +7 -7
  275. package/src/vite-env.d.ts +12 -12
  276. package/dist/compat-shims-BPJ7Q68c.js.map +0 -1
  277. package/dist/index-D0bAYNQy.js.map +0 -1
  278. package/dist/index-KSX4Qjip.cjs.map +0 -1
package/src/lib/models.ts CHANGED
@@ -1,23 +1,31 @@
1
1
  // List of openrouter models available to the user
2
2
  // This list should be updated to match the model whitelist on the backend side.
3
3
  export const MODELS = [
4
- 'anthropic/claude-opus-4.6',
5
- 'anthropic/claude-sonnet-4.5',
6
- 'anthropic/claude-haiku-4.5',
7
- 'anthropic/claude-sonnet-4',
8
- 'anthropic/claude-opus-4.5',
9
- 'openai/gpt-4o',
10
- 'openai/gpt-4o-mini',
11
- 'openai/gpt-5.1-codex',
12
- 'openai/gpt-5',
13
- 'openai/gpt-5.1',
14
- 'openai/gpt-4.1',
15
- 'anthropic/claude-3.7-sonnet',
16
- 'anthropic/claude-opus-4',
17
- 'google/gemini-2.5-pro-preview',
18
- 'google/gemini-3-pro-preview',
19
- 'moonshotai/kimi-k2',
20
- 'mistralai/mistral-medium-3',
21
- 'mistralai/mistral-medium-3.1',
22
- 'mistralai/codestral-2501',
23
- ] as const
4
+ "anthropic/claude-opus-4.6",
5
+ "anthropic/claude-sonnet-4.6",
6
+ "anthropic/claude-sonnet-4.5",
7
+ "anthropic/claude-opus-4.5",
8
+ "anthropic/claude-haiku-4.5",
9
+ "anthropic/claude-opus-4.1",
10
+ "anthropic/claude-sonnet-4",
11
+ "openai/gpt-5.4",
12
+ "openai/gpt-5.4-mini",
13
+ "openai/gpt-5.1",
14
+ "openai/gpt-5.1-codex",
15
+ "openai/gpt-5",
16
+ "openai/gpt-4.1",
17
+ "openai/o4-mini",
18
+ "openai/o3",
19
+ "google/gemini-3.1-pro-preview",
20
+ "google/gemini-2.5-pro",
21
+ "google/gemini-2.5-flash",
22
+ "deepseek/deepseek-r1",
23
+ "deepseek/deepseek-v3.2",
24
+ "meta-llama/llama-4-maverick",
25
+ "x-ai/grok-4",
26
+ "qwen/qwen3-coder",
27
+ "moonshotai/kimi-k2.5",
28
+ "mistralai/mistral-medium-3.1",
29
+ "mistralai/codestral-2508",
30
+ "mistralai/devstral-small",
31
+ ] as const;
@@ -1,79 +1,79 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { getTokenExpiry, isTokenExpired } from './token'
1
+ import { describe, expect, it } from "vitest";
2
+ import { getTokenExpiry, isTokenExpired } from "./token";
3
3
 
4
4
  /** Helper: build a JWT with a given payload (no signature verification needed). */
5
5
  function makeJwt(payload: Record<string, unknown>): string {
6
- const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' }))
7
- const body = btoa(JSON.stringify(payload))
8
- return `${header}.${body}.fake-signature`
6
+ const header = btoa(JSON.stringify({ alg: "HS256", typ: "JWT" }));
7
+ const body = btoa(JSON.stringify(payload));
8
+ return `${header}.${body}.fake-signature`;
9
9
  }
10
10
 
11
- describe('getTokenExpiry', () => {
12
- it('returns exp for a valid JWT', () => {
13
- const exp = 1700000000
14
- expect(getTokenExpiry(makeJwt({ exp }))).toBe(exp)
15
- })
11
+ describe("getTokenExpiry", () => {
12
+ it("returns exp for a valid JWT", () => {
13
+ const exp = 1700000000;
14
+ expect(getTokenExpiry(makeJwt({ exp }))).toBe(exp);
15
+ });
16
16
 
17
- it('returns null when exp is missing', () => {
18
- expect(getTokenExpiry(makeJwt({ sub: 'user' }))).toBeNull()
19
- })
17
+ it("returns null when exp is missing", () => {
18
+ expect(getTokenExpiry(makeJwt({ sub: "user" }))).toBeNull();
19
+ });
20
20
 
21
- it('returns null for a non-JWT string', () => {
22
- expect(getTokenExpiry('not-a-jwt')).toBeNull()
23
- })
21
+ it("returns null for a non-JWT string", () => {
22
+ expect(getTokenExpiry("not-a-jwt")).toBeNull();
23
+ });
24
24
 
25
- it('returns null for an empty string', () => {
26
- expect(getTokenExpiry('')).toBeNull()
27
- })
25
+ it("returns null for an empty string", () => {
26
+ expect(getTokenExpiry("")).toBeNull();
27
+ });
28
28
 
29
- it('returns null when payload is not valid JSON', () => {
29
+ it("returns null when payload is not valid JSON", () => {
30
30
  // Two dots but the middle segment is not valid base64 JSON
31
- expect(getTokenExpiry('a.!!!.b')).toBeNull()
32
- })
31
+ expect(getTokenExpiry("a.!!!.b")).toBeNull();
32
+ });
33
33
 
34
- it('handles base64url characters (- and _)', () => {
34
+ it("handles base64url characters (- and _)", () => {
35
35
  // Manually craft a payload with base64url-specific chars
36
- const payload = { exp: 1700000000 }
37
- const json = JSON.stringify(payload)
36
+ const payload = { exp: 1700000000 };
37
+ const json = JSON.stringify(payload);
38
38
  const b64 = btoa(json)
39
- .replace(/\+/g, '-')
40
- .replace(/\//g, '_')
41
- .replace(/=+$/, '')
42
- const token = `header.${b64}.sig`
43
- expect(getTokenExpiry(token)).toBe(1700000000)
44
- })
45
- })
39
+ .replace(/\+/g, "-")
40
+ .replace(/\//g, "_")
41
+ .replace(/=+$/, "");
42
+ const token = `header.${b64}.sig`;
43
+ expect(getTokenExpiry(token)).toBe(1700000000);
44
+ });
45
+ });
46
46
 
47
- describe('isTokenExpired', () => {
48
- it('returns true for an expired token', () => {
47
+ describe("isTokenExpired", () => {
48
+ it("returns true for an expired token", () => {
49
49
  // exp = 1 second ago
50
- const exp = Math.floor(Date.now() / 1000) - 1
51
- expect(isTokenExpired(makeJwt({ exp }))).toBe(true)
52
- })
50
+ const exp = Math.floor(Date.now() / 1000) - 1;
51
+ expect(isTokenExpired(makeJwt({ exp }))).toBe(true);
52
+ });
53
53
 
54
- it('returns true when token is within the buffer window', () => {
54
+ it("returns true when token is within the buffer window", () => {
55
55
  // exp = 20 seconds from now (within 30s default buffer)
56
- const exp = Math.floor(Date.now() / 1000) + 20
57
- expect(isTokenExpired(makeJwt({ exp }))).toBe(true)
58
- })
56
+ const exp = Math.floor(Date.now() / 1000) + 20;
57
+ expect(isTokenExpired(makeJwt({ exp }))).toBe(true);
58
+ });
59
59
 
60
- it('returns false when token is well outside the buffer', () => {
60
+ it("returns false when token is well outside the buffer", () => {
61
61
  // exp = 5 minutes from now
62
- const exp = Math.floor(Date.now() / 1000) + 300
63
- expect(isTokenExpired(makeJwt({ exp }))).toBe(false)
64
- })
62
+ const exp = Math.floor(Date.now() / 1000) + 300;
63
+ expect(isTokenExpired(makeJwt({ exp }))).toBe(false);
64
+ });
65
65
 
66
- it('respects a custom buffer', () => {
66
+ it("respects a custom buffer", () => {
67
67
  // exp = 20 seconds from now, buffer = 10s → should NOT be expired
68
- const exp = Math.floor(Date.now() / 1000) + 20
69
- expect(isTokenExpired(makeJwt({ exp }), 10_000)).toBe(false)
70
- })
68
+ const exp = Math.floor(Date.now() / 1000) + 20;
69
+ expect(isTokenExpired(makeJwt({ exp }), 10_000)).toBe(false);
70
+ });
71
71
 
72
- it('returns false (fail-open) for a non-JWT string', () => {
73
- expect(isTokenExpired('opaque-session-token')).toBe(false)
74
- })
72
+ it("returns false (fail-open) for a non-JWT string", () => {
73
+ expect(isTokenExpired("opaque-session-token")).toBe(false);
74
+ });
75
75
 
76
- it('returns false (fail-open) for an empty string', () => {
77
- expect(isTokenExpired('')).toBe(false)
78
- })
79
- })
76
+ it("returns false (fail-open) for an empty string", () => {
77
+ expect(isTokenExpired("")).toBe(false);
78
+ });
79
+ });
package/src/lib/token.ts CHANGED
@@ -5,22 +5,22 @@
5
5
  */
6
6
  export function getTokenExpiry(token: string): number | null {
7
7
  try {
8
- const parts = token.split('.')
9
- if (parts.length !== 3) return null
8
+ const parts = token.split(".");
9
+ if (parts.length !== 3) return null;
10
10
 
11
11
  // base64url → base64 → decode
12
- let payload = parts[1].replace(/-/g, '+').replace(/_/g, '/')
13
- while (payload.length % 4) payload += '='
12
+ let payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
13
+ while (payload.length % 4) payload += "=";
14
14
 
15
- const json = atob(payload)
16
- const parsed = JSON.parse(json)
15
+ const json = atob(payload);
16
+ const parsed = JSON.parse(json);
17
17
 
18
- if (typeof parsed.exp === 'number') {
19
- return parsed.exp
18
+ if (typeof parsed.exp === "number") {
19
+ return parsed.exp;
20
20
  }
21
- return null
21
+ return null;
22
22
  } catch {
23
- return null
23
+ return null;
24
24
  }
25
25
  }
26
26
 
@@ -31,9 +31,9 @@ export function getTokenExpiry(token: string): number | null {
31
31
  */
32
32
  export function isTokenExpired(
33
33
  token: string,
34
- bufferMs: number = 30_000
34
+ bufferMs: number = 30_000,
35
35
  ): boolean {
36
- const exp = getTokenExpiry(token)
37
- if (exp === null) return false // fail-open for non-JWT tokens
38
- return Date.now() >= exp * 1000 - bufferMs
36
+ const exp = getTokenExpiry(token);
37
+ if (exp === null) return false; // fail-open for non-JWT tokens
38
+ return Date.now() >= exp * 1000 - bufferMs;
39
39
  }
@@ -1,106 +1,106 @@
1
- export type ToolRecord = Record<string, unknown> | undefined
1
+ export type ToolRecord = Record<string, unknown> | undefined;
2
2
 
3
3
  export interface MentionableTool {
4
- id: string
5
- name: string
6
- description?: string
4
+ id: string;
5
+ name: string;
6
+ description?: string;
7
7
  }
8
8
 
9
9
  export interface MentionContext {
10
- isInMention: boolean
11
- query: string
12
- atPosition: number
10
+ isInMention: boolean;
11
+ query: string;
12
+ atPosition: number;
13
13
  }
14
14
 
15
- const MENTION_PATTERN = /@(\w+)/g
15
+ const MENTION_PATTERN = /@(\w+)/g;
16
16
 
17
17
  export function toolSetToMentionableTools(
18
- tools: ToolRecord
18
+ tools: ToolRecord,
19
19
  ): MentionableTool[] {
20
- if (!tools) return []
20
+ if (!tools) return [];
21
21
 
22
22
  return Object.entries(tools).map(([name, tool]) => ({
23
23
  id: name,
24
24
  name,
25
25
  description:
26
- typeof tool === 'object' && tool !== null && 'description' in tool
27
- ? String((tool as { description?: unknown }).description ?? '')
26
+ typeof tool === "object" && tool !== null && "description" in tool
27
+ ? String((tool as { description?: unknown }).description ?? "")
28
28
  : undefined,
29
- }))
29
+ }));
30
30
  }
31
31
 
32
32
  export function parseMentionedTools(text: string, tools: ToolRecord): string[] {
33
- if (!tools || !text) return []
33
+ if (!tools || !text) return [];
34
34
 
35
- const toolNames = Object.keys(tools)
36
- const mentions: string[] = []
37
- let match: RegExpExecArray | null
35
+ const toolNames = Object.keys(tools);
36
+ const mentions: string[] = [];
37
+ let match: RegExpExecArray | null;
38
38
 
39
- MENTION_PATTERN.lastIndex = 0
39
+ MENTION_PATTERN.lastIndex = 0;
40
40
  while ((match = MENTION_PATTERN.exec(text)) !== null) {
41
- mentions.push(match[1].toLowerCase())
41
+ mentions.push(match[1].toLowerCase());
42
42
  }
43
43
 
44
44
  const matchedToolIds = toolNames.filter((name) =>
45
- mentions.includes(name.toLowerCase())
46
- )
45
+ mentions.includes(name.toLowerCase()),
46
+ );
47
47
 
48
- return [...new Set(matchedToolIds)]
48
+ return [...new Set(matchedToolIds)];
49
49
  }
50
50
 
51
51
  export function detectMentionContext(
52
52
  text: string,
53
- cursorPosition: number
53
+ cursorPosition: number,
54
54
  ): MentionContext {
55
- const textBeforeCursor = text.slice(0, cursorPosition)
56
- const lastAtSymbol = textBeforeCursor.lastIndexOf('@')
55
+ const textBeforeCursor = text.slice(0, cursorPosition);
56
+ const lastAtSymbol = textBeforeCursor.lastIndexOf("@");
57
57
 
58
58
  if (lastAtSymbol === -1) {
59
- return { isInMention: false, query: '', atPosition: -1 }
59
+ return { isInMention: false, query: "", atPosition: -1 };
60
60
  }
61
61
 
62
- const textAfterAt = textBeforeCursor.slice(lastAtSymbol + 1)
62
+ const textAfterAt = textBeforeCursor.slice(lastAtSymbol + 1);
63
63
 
64
- if (textAfterAt.includes(' ') || textAfterAt.includes('\n')) {
65
- return { isInMention: false, query: '', atPosition: -1 }
64
+ if (textAfterAt.includes(" ") || textAfterAt.includes("\n")) {
65
+ return { isInMention: false, query: "", atPosition: -1 };
66
66
  }
67
67
 
68
68
  return {
69
69
  isInMention: true,
70
70
  query: textAfterAt.toLowerCase(),
71
71
  atPosition: lastAtSymbol,
72
- }
72
+ };
73
73
  }
74
74
 
75
75
  export function filterToolsByQuery(
76
76
  tools: MentionableTool[],
77
- query: string
77
+ query: string,
78
78
  ): MentionableTool[] {
79
- if (!query) return tools
79
+ if (!query) return tools;
80
80
 
81
- const queryLower = query.toLowerCase()
81
+ const queryLower = query.toLowerCase();
82
82
 
83
83
  return tools.filter((tool) => {
84
- const nameMatch = tool.name.toLowerCase().includes(queryLower)
85
- const descMatch = tool.description?.toLowerCase().includes(queryLower)
86
- return nameMatch || descMatch
87
- })
84
+ const nameMatch = tool.name.toLowerCase().includes(queryLower);
85
+ const descMatch = tool.description?.toLowerCase().includes(queryLower);
86
+ return nameMatch || descMatch;
87
+ });
88
88
  }
89
89
 
90
90
  export function insertToolMention(
91
91
  text: string,
92
92
  toolName: string,
93
93
  atPosition: number,
94
- cursorPosition: number
94
+ cursorPosition: number,
95
95
  ): { text: string; cursorPosition: number } {
96
- const beforeMention = text.slice(0, atPosition)
97
- const afterCursor = text.slice(cursorPosition)
98
- const newText = `${beforeMention}@${toolName} ${afterCursor}`
99
- const newCursorPosition = atPosition + toolName.length + 2
100
- return { text: newText, cursorPosition: newCursorPosition }
96
+ const beforeMention = text.slice(0, atPosition);
97
+ const afterCursor = text.slice(cursorPosition);
98
+ const newText = `${beforeMention}@${toolName} ${afterCursor}`;
99
+ const newCursorPosition = atPosition + toolName.length + 2;
100
+ return { text: newText, cursorPosition: newCursorPosition };
101
101
  }
102
102
 
103
103
  export function removeToolMention(text: string, toolName: string): string {
104
- const pattern = new RegExp(`@${toolName}\\s?`, 'gi')
105
- return text.replace(pattern, '')
104
+ const pattern = new RegExp(`@${toolName}\\s?`, "gi");
105
+ return text.replace(pattern, "");
106
106
  }