@gram-ai/elements 1.33.2 → 1.35.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.
- package/dist/compat-shims-CO9JXXV4.cjs.map +1 -1
- package/dist/compat-shims-DxtUrORi.js.map +1 -1
- package/dist/compat-shims.d.ts +9 -8
- package/dist/components/Chat/index.d.ts +2 -1
- package/dist/components/ChatHistory.d.ts +1 -1
- package/dist/components/FrontendTools/index.d.ts +1 -1
- package/dist/components/Replay.d.ts +1 -1
- package/dist/components/Replay.stories.d.ts +2 -2
- package/dist/components/ShadowRoot.d.ts +1 -1
- package/dist/components/ShareButton/index.d.ts +1 -1
- package/dist/components/ui/avatar.d.ts +3 -3
- package/dist/components/ui/button.d.ts +2 -2
- package/dist/components/ui/buttonVariants.d.ts +2 -2
- package/dist/components/ui/calendar.d.ts +2 -1
- package/dist/components/ui/collapsible.d.ts +3 -3
- package/dist/components/ui/dialog.d.ts +10 -10
- package/dist/components/ui/popover.d.ts +4 -4
- package/dist/components/ui/skeleton.d.ts +1 -1
- package/dist/components/ui/time-range-picker.d.ts +18 -1
- package/dist/components/ui/time-range-picker.test.d.ts +1 -0
- package/dist/components/ui/tool-ui.d.ts +7 -7
- package/dist/components/ui/tooltip.d.ts +4 -4
- package/dist/contexts/ConnectionStatusContext.d.ts +1 -1
- package/dist/contexts/ElementsProvider.d.ts +1 -1
- package/dist/contexts/ToolApprovalContext.d.ts +2 -2
- package/dist/contexts/ToolExecutionContext.d.ts +1 -1
- package/dist/contexts/portal-container.d.ts +1 -1
- package/dist/elements.cjs +1 -1
- package/dist/elements.css +1 -1
- package/dist/elements.js +19 -16
- package/dist/hooks/useDensity.d.ts +1 -1
- package/dist/hooks/useElements.d.ts +2 -1
- package/dist/hooks/useGramThreadListAdapter.d.ts +26 -0
- package/dist/hooks/useRadius.d.ts +1 -1
- package/dist/hooks/useThemeProps.d.ts +1 -1
- package/dist/hooks/useToolApproval.d.ts +2 -1
- package/dist/{index-reVrRxP1.js → index-BhIowiZF.js} +9744 -9493
- package/dist/index-BhIowiZF.js.map +1 -0
- package/dist/{index-DAWGW1Nj.cjs → index-D0jIGQr7.cjs} +43 -43
- package/dist/index-D0jIGQr7.cjs.map +1 -0
- package/dist/{index-CGoLfO5p.js → index-Dz13dSDa.js} +108 -51
- package/dist/index-Dz13dSDa.js.map +1 -0
- package/dist/index-PXd3rs95.cjs +194 -0
- package/dist/index-PXd3rs95.cjs.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/lib/errorTracking.d.ts +1 -1
- package/dist/lib/messageConverter.d.ts +58 -8
- package/dist/lib/models.d.ts +1 -1
- package/dist/lib/tools.d.ts +11 -10
- package/dist/lib/utils.d.ts +2 -0
- package/dist/plugins/generative-ui/catalog.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/accordion-wrapper.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/accordion.d.ts +4 -4
- package/dist/plugins/generative-ui/ui/action-button.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/alert-wrapper.d.ts +2 -1
- package/dist/plugins/generative-ui/ui/alert.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/avatar-wrapper.d.ts +2 -1
- package/dist/plugins/generative-ui/ui/avatar.d.ts +6 -6
- package/dist/plugins/generative-ui/ui/badge.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/button-wrapper.d.ts +2 -1
- package/dist/plugins/generative-ui/ui/button.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/card-wrapper.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/card.d.ts +7 -7
- package/dist/plugins/generative-ui/ui/checkbox-wrapper.d.ts +2 -1
- package/dist/plugins/generative-ui/ui/checkbox.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/data-table.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/dialog.d.ts +10 -10
- package/dist/plugins/generative-ui/ui/dropdown-menu.d.ts +15 -15
- package/dist/plugins/generative-ui/ui/grid.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/index.d.ts +57 -40
- package/dist/plugins/generative-ui/ui/input-wrapper.d.ts +2 -1
- package/dist/plugins/generative-ui/ui/input.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/label.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/list.d.ts +2 -1
- package/dist/plugins/generative-ui/ui/metric.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/pagination.d.ts +7 -7
- package/dist/plugins/generative-ui/ui/popover.d.ts +7 -7
- package/dist/plugins/generative-ui/ui/progress.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/radio-group.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/select-wrapper.d.ts +2 -1
- package/dist/plugins/generative-ui/ui/select.d.ts +10 -10
- package/dist/plugins/generative-ui/ui/separator.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/skeleton-wrapper.d.ts +2 -1
- package/dist/plugins/generative-ui/ui/skeleton.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/stack.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/switch.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/table.d.ts +8 -8
- package/dist/plugins/generative-ui/ui/tabs-wrapper.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/tabs.d.ts +4 -4
- package/dist/plugins/generative-ui/ui/text.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/textarea.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/tooltip.d.ts +4 -4
- package/dist/plugins.cjs +1 -1
- package/dist/plugins.js +1 -1
- package/dist/{profiler-noho3NG9.js → profiler-CtGKTWWP.js} +2 -2
- package/dist/{profiler-noho3NG9.js.map → profiler-CtGKTWWP.js.map} +1 -1
- package/dist/{profiler-B3tfiOx4.cjs → profiler-l7_HjTyw.cjs} +2 -2
- package/dist/{profiler-B3tfiOx4.cjs.map → profiler-l7_HjTyw.cjs.map} +1 -1
- package/dist/react-shim.cjs.map +1 -1
- package/dist/react-shim.d.ts +1 -1
- package/dist/react-shim.js +1 -4
- package/dist/react-shim.js.map +1 -1
- package/dist/server/bun.cjs.map +1 -1
- package/dist/server/bun.js.map +1 -1
- package/dist/server/express.cjs.map +1 -1
- package/dist/server/express.js.map +1 -1
- package/dist/server/fastify.cjs.map +1 -1
- package/dist/server/fastify.js.map +1 -1
- package/dist/server/hono.cjs.map +1 -1
- package/dist/server/hono.js.map +1 -1
- package/dist/server/nextjs.cjs.map +1 -1
- package/dist/server/nextjs.js.map +1 -1
- package/dist/server/tanstack-start.cjs.map +1 -1
- package/dist/server/tanstack-start.js.map +1 -1
- package/dist/{startRecording-7Oy6wM18.cjs → startRecording-DEw2Aeq4.cjs} +2 -2
- package/dist/{startRecording-7Oy6wM18.cjs.map → startRecording-DEw2Aeq4.cjs.map} +1 -1
- package/dist/{startRecording-mkmig-2n.js → startRecording-iYEL0-vr.js} +2 -2
- package/dist/{startRecording-mkmig-2n.js.map → startRecording-iYEL0-vr.js.map} +1 -1
- package/dist/types/index.d.ts +93 -4
- package/package.json +8 -9
- package/src/compat-shims.ts +16 -2
- package/src/components/Chat/index.tsx +4 -1
- package/src/components/Chat/stories/FrontendTools.stories.tsx +1 -1
- package/src/components/Chat/stories/ToolApproval.stories.tsx +2 -2
- package/src/components/Chat/stories/Tools.stories.tsx +13 -5
- package/src/components/ChatHistory.tsx +3 -1
- package/src/components/FrontendTools/index.tsx +1 -1
- package/src/components/MessageContent.tsx +1 -0
- package/src/components/Replay.stories.tsx +2 -3
- package/src/components/Replay.tsx +17 -10
- package/src/components/ShadowRoot.tsx +2 -2
- package/src/components/ShareButton/index.tsx +4 -2
- package/src/components/assistant-ui/assistant-modal.tsx +5 -3
- package/src/components/assistant-ui/attachment.tsx +1 -1
- package/src/components/assistant-ui/error-boundary.tsx +1 -1
- package/src/components/assistant-ui/markdown-text.tsx +1 -1
- package/src/components/assistant-ui/thread.tsx +256 -14
- package/src/components/assistant-ui/tool-mention-autocomplete.tsx +1 -1
- package/src/components/ui/avatar.tsx +3 -3
- package/src/components/ui/calendar.tsx +1 -1
- package/src/components/ui/collapsible.tsx +7 -3
- package/src/components/ui/dialog.tsx +18 -10
- package/src/components/ui/generative-ui.tsx +9 -4
- package/src/components/ui/popover.tsx +4 -4
- package/src/components/ui/skeleton.tsx +4 -1
- package/src/components/ui/time-range-picker.stories.tsx +164 -154
- package/src/components/ui/time-range-picker.test.ts +57 -0
- package/src/components/ui/time-range-picker.tsx +40 -9
- package/src/components/ui/tool-ui.tsx +18 -9
- package/src/components/ui/tooltip.tsx +4 -4
- package/src/contexts/ChatIdContext.tsx +1 -1
- package/src/contexts/ConnectionStatusContext.tsx +6 -5
- package/src/contexts/ElementsProvider.tsx +109 -37
- package/src/contexts/ReplayContext.ts +1 -1
- package/src/contexts/ToolApprovalContext.tsx +5 -1
- package/src/contexts/ToolExecutionContext.tsx +1 -1
- package/src/contexts/portal-container.tsx +1 -1
- package/src/hooks/useAuth.ts +2 -1
- package/src/hooks/useDensity.ts +1 -1
- package/src/hooks/useElements.ts +2 -1
- package/src/hooks/useFollowOnSuggestions.ts +3 -6
- package/src/hooks/useGramThreadListAdapter.tsx +118 -9
- package/src/hooks/useMCPTools.ts +2 -2
- package/src/hooks/useModel.ts +1 -3
- package/src/hooks/usePluginComponents.ts +3 -1
- package/src/hooks/useRadius.ts +1 -1
- package/src/hooks/useSession.ts +3 -1
- package/src/hooks/useThemeProps.ts +5 -5
- package/src/hooks/useToolApproval.ts +2 -1
- package/src/index.ts +16 -0
- package/src/lib/cassette.ts +21 -27
- package/src/lib/contextCompaction.test.ts +2 -2
- package/src/lib/contextCompaction.ts +20 -8
- package/src/lib/errorTracking.ts +1 -4
- package/src/lib/messageConverter.test.ts +11 -13
- package/src/lib/messageConverter.ts +105 -58
- package/src/lib/models.ts +19 -7
- package/src/lib/token.ts +2 -5
- package/src/lib/tool-mentions.ts +5 -2
- package/src/lib/tools.byte-cap.test.ts +1 -1
- package/src/lib/tools.test.ts +1 -1
- package/src/lib/tools.ts +15 -5
- package/src/lib/utils.ts +22 -2
- package/src/lib.d.ts +8 -1
- package/src/plugins/chart/chart.test.ts +3 -4
- package/src/plugins/chart/component.tsx +7 -6
- package/src/plugins/chart/ui/area-chart.tsx +1 -1
- package/src/plugins/chart/ui/line-chart.tsx +1 -1
- package/src/plugins/generative-ui/ui/accordion-wrapper.tsx +2 -2
- package/src/plugins/generative-ui/ui/accordion.tsx +4 -4
- package/src/plugins/generative-ui/ui/action-button.tsx +4 -2
- package/src/plugins/generative-ui/ui/alert-wrapper.tsx +1 -1
- package/src/plugins/generative-ui/ui/alert.tsx +7 -3
- package/src/plugins/generative-ui/ui/avatar-wrapper.tsx +5 -1
- package/src/plugins/generative-ui/ui/avatar.tsx +12 -6
- package/src/plugins/generative-ui/ui/badge.tsx +1 -1
- package/src/plugins/generative-ui/ui/button-wrapper.tsx +1 -1
- package/src/plugins/generative-ui/ui/button.tsx +1 -1
- package/src/plugins/generative-ui/ui/card-wrapper.tsx +1 -1
- package/src/plugins/generative-ui/ui/card.tsx +28 -7
- package/src/plugins/generative-ui/ui/checkbox-wrapper.tsx +1 -1
- package/src/plugins/generative-ui/ui/checkbox.tsx +1 -1
- package/src/plugins/generative-ui/ui/data-table.tsx +1 -1
- package/src/plugins/generative-ui/ui/dialog.tsx +15 -10
- package/src/plugins/generative-ui/ui/dropdown-menu.tsx +33 -15
- package/src/plugins/generative-ui/ui/grid.tsx +1 -1
- package/src/plugins/generative-ui/ui/index.ts +154 -40
- package/src/plugins/generative-ui/ui/input-wrapper.tsx +1 -1
- package/src/plugins/generative-ui/ui/input.tsx +5 -1
- package/src/plugins/generative-ui/ui/label.tsx +1 -1
- package/src/plugins/generative-ui/ui/list.tsx +5 -1
- package/src/plugins/generative-ui/ui/metric.tsx +2 -1
- package/src/plugins/generative-ui/ui/pagination.tsx +12 -7
- package/src/plugins/generative-ui/ui/popover.tsx +13 -7
- package/src/plugins/generative-ui/ui/progress.tsx +1 -1
- package/src/plugins/generative-ui/ui/radio-group.tsx +2 -2
- package/src/plugins/generative-ui/ui/select-wrapper.tsx +1 -1
- package/src/plugins/generative-ui/ui/select.tsx +14 -10
- package/src/plugins/generative-ui/ui/separator.tsx +1 -1
- package/src/plugins/generative-ui/ui/skeleton-wrapper.tsx +1 -1
- package/src/plugins/generative-ui/ui/skeleton.tsx +4 -1
- package/src/plugins/generative-ui/ui/stack.tsx +1 -1
- package/src/plugins/generative-ui/ui/switch.tsx +1 -1
- package/src/plugins/generative-ui/ui/table.tsx +29 -8
- package/src/plugins/generative-ui/ui/tabs-wrapper.tsx +5 -2
- package/src/plugins/generative-ui/ui/tabs.tsx +4 -4
- package/src/plugins/generative-ui/ui/text.tsx +1 -1
- package/src/plugins/generative-ui/ui/textarea.tsx +4 -1
- package/src/plugins/generative-ui/ui/tooltip.tsx +4 -4
- package/src/react-shim.ts +9 -4
- package/src/server/bun.ts +1 -1
- package/src/server/express.ts +1 -1
- package/src/server/fastify.ts +1 -1
- package/src/server/hono.ts +1 -1
- package/src/server/nextjs.ts +1 -1
- package/src/server/tanstack-start.ts +1 -1
- package/src/storybook.d.ts +5 -0
- package/src/types/index.ts +112 -4
- package/dist/index-BCV7Zf9E.cjs +0 -194
- package/dist/index-BCV7Zf9E.cjs.map +0 -1
- package/dist/index-CGoLfO5p.js.map +0 -1
- package/dist/index-DAWGW1Nj.cjs.map +0 -1
- package/dist/index-reVrRxP1.js.map +0 -1
package/src/hooks/useRadius.ts
CHANGED
|
@@ -34,7 +34,7 @@ type RadiusSize = "sm" | "md" | "lg" | "xl" | "full";
|
|
|
34
34
|
* Hook to get radius classes based on theme config
|
|
35
35
|
* Use: const r = useRadius(); then r('lg') returns the appropriate rounded class
|
|
36
36
|
*/
|
|
37
|
-
export const useRadius = () => {
|
|
37
|
+
export const useRadius = (): ((size: RadiusSize) => string) => {
|
|
38
38
|
const { config } = useElements();
|
|
39
39
|
const radius = config.theme?.radius ?? "soft";
|
|
40
40
|
|
package/src/hooks/useSession.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { GetSessionFn } from "@/types";
|
|
2
2
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
3
|
|
|
4
|
-
export function getChatSessionQueryKey(
|
|
4
|
+
export function getChatSessionQueryKey(
|
|
5
|
+
projectSlug: string,
|
|
6
|
+
): readonly ["chatSession", string] {
|
|
5
7
|
return ["chatSession", projectSlug] as const;
|
|
6
8
|
}
|
|
7
9
|
|
|
@@ -4,13 +4,13 @@ import { useElements } from "./useElements";
|
|
|
4
4
|
/**
|
|
5
5
|
* Hook to get theme-related props including dark mode class
|
|
6
6
|
*/
|
|
7
|
-
export const useThemeProps = ()
|
|
7
|
+
export const useThemeProps = (): {
|
|
8
|
+
readonly className: string | undefined;
|
|
9
|
+
} => {
|
|
8
10
|
const { config } = useElements();
|
|
9
|
-
const
|
|
11
|
+
const colorScheme = config.theme?.colorScheme ?? "light";
|
|
10
12
|
|
|
11
13
|
return useMemo(() => {
|
|
12
|
-
const { colorScheme = "light" } = theme;
|
|
13
|
-
|
|
14
14
|
const isDark =
|
|
15
15
|
colorScheme === "dark" ||
|
|
16
16
|
(colorScheme === "system" &&
|
|
@@ -20,5 +20,5 @@ export const useThemeProps = () => {
|
|
|
20
20
|
return {
|
|
21
21
|
className: isDark ? "dark" : undefined,
|
|
22
22
|
} as const;
|
|
23
|
-
}, [
|
|
23
|
+
}, [colorScheme]);
|
|
24
24
|
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { useContext } from "react";
|
|
2
2
|
import { ToolApprovalContext } from "@/contexts/contexts";
|
|
3
|
+
import type { ToolApprovalContextType } from "@/contexts/ToolApprovalContext";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Hook to access the tool approval context for managing human-in-the-loop
|
|
6
7
|
* tool execution approval.
|
|
7
8
|
*/
|
|
8
|
-
export const useToolApproval = () => {
|
|
9
|
+
export const useToolApproval = (): ToolApprovalContextType => {
|
|
9
10
|
const context = useContext(ToolApprovalContext);
|
|
10
11
|
if (!context) {
|
|
11
12
|
throw new Error(
|
package/src/index.ts
CHANGED
|
@@ -56,6 +56,8 @@ export type {
|
|
|
56
56
|
Dimension,
|
|
57
57
|
Dimensions,
|
|
58
58
|
ElementsConfig,
|
|
59
|
+
ElementsTransportContext,
|
|
60
|
+
ElementsTransportFactory,
|
|
59
61
|
ErrorTrackingConfigOption,
|
|
60
62
|
GetSessionFn,
|
|
61
63
|
HistoryConfig,
|
|
@@ -80,6 +82,20 @@ export type {
|
|
|
80
82
|
|
|
81
83
|
export { MODELS } from "./lib/models";
|
|
82
84
|
|
|
85
|
+
// Chat-message conversion — for consumers building a custom transport against
|
|
86
|
+
// the Gram chat service (e.g. the dashboard's server-assistant transport).
|
|
87
|
+
export {
|
|
88
|
+
convertGramMessagesToUIMessages,
|
|
89
|
+
convertGramMessagesToExported,
|
|
90
|
+
} from "@/lib/messageConverter";
|
|
91
|
+
|
|
92
|
+
export { sleep } from "@/lib/utils";
|
|
93
|
+
export type {
|
|
94
|
+
GramChat,
|
|
95
|
+
GramChatMessage,
|
|
96
|
+
GramChatOverview,
|
|
97
|
+
} from "@/lib/messageConverter";
|
|
98
|
+
|
|
83
99
|
export type { Plugin } from "./types/plugins";
|
|
84
100
|
|
|
85
101
|
// Time Range Picker
|
package/src/lib/cassette.ts
CHANGED
|
@@ -10,8 +10,14 @@
|
|
|
10
10
|
* 3. Pass it to `<Replay cassette={...}>` to play it back
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
createUIMessageStream,
|
|
15
|
+
type ChatTransport,
|
|
16
|
+
type UIMessage,
|
|
17
|
+
type UIMessageStreamWriter,
|
|
18
|
+
} from "ai";
|
|
14
19
|
import type { ThreadMessage } from "@assistant-ui/react";
|
|
20
|
+
import { sleep } from "@/lib/utils";
|
|
15
21
|
|
|
16
22
|
// ---------------------------------------------------------------------------
|
|
17
23
|
// Cassette types
|
|
@@ -94,6 +100,12 @@ export function recordCassette(messages: readonly ThreadMessage[]): Cassette {
|
|
|
94
100
|
});
|
|
95
101
|
break;
|
|
96
102
|
// Skip image, file, audio, source, data parts for now
|
|
103
|
+
case "image":
|
|
104
|
+
case "file":
|
|
105
|
+
case "audio":
|
|
106
|
+
case "source":
|
|
107
|
+
case "data":
|
|
108
|
+
break;
|
|
97
109
|
}
|
|
98
110
|
}
|
|
99
111
|
|
|
@@ -112,25 +124,6 @@ export function recordCassette(messages: readonly ThreadMessage[]): Cassette {
|
|
|
112
124
|
// Playback: Cassette → ChatTransport
|
|
113
125
|
// ---------------------------------------------------------------------------
|
|
114
126
|
|
|
115
|
-
/** Sleep that respects AbortSignal for clean cancellation. */
|
|
116
|
-
function sleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
117
|
-
return new Promise((resolve, reject) => {
|
|
118
|
-
if (signal?.aborted) {
|
|
119
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
const timeout = setTimeout(resolve, ms);
|
|
123
|
-
signal?.addEventListener(
|
|
124
|
-
"abort",
|
|
125
|
-
() => {
|
|
126
|
-
clearTimeout(timeout);
|
|
127
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
128
|
-
},
|
|
129
|
-
{ once: true },
|
|
130
|
-
);
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
127
|
/**
|
|
135
128
|
* Creates a ChatTransport that replays pre-recorded assistant messages
|
|
136
129
|
* from a cassette. Each call to `sendMessages` (triggered by a user message
|
|
@@ -153,7 +146,7 @@ export function createReplayTransport(
|
|
|
153
146
|
// Advance cursor past it (it should be pointing at a user message).
|
|
154
147
|
if (
|
|
155
148
|
cursor < cassette.messages.length &&
|
|
156
|
-
cassette.messages[cursor]
|
|
149
|
+
cassette.messages[cursor]?.role === "user"
|
|
157
150
|
) {
|
|
158
151
|
cursor++;
|
|
159
152
|
}
|
|
@@ -163,9 +156,10 @@ export function createReplayTransport(
|
|
|
163
156
|
const assistantMessages: CassetteMessage[] = [];
|
|
164
157
|
while (
|
|
165
158
|
cursor < cassette.messages.length &&
|
|
166
|
-
cassette.messages[cursor]
|
|
159
|
+
cassette.messages[cursor]?.role === "assistant"
|
|
167
160
|
) {
|
|
168
|
-
|
|
161
|
+
const m = cassette.messages[cursor];
|
|
162
|
+
if (m) assistantMessages.push(m);
|
|
169
163
|
cursor++;
|
|
170
164
|
}
|
|
171
165
|
|
|
@@ -204,10 +198,10 @@ export function createReplayTransport(
|
|
|
204
198
|
// Stream writing helpers
|
|
205
199
|
// ---------------------------------------------------------------------------
|
|
206
200
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
201
|
+
// Match the `write` shape of `UIMessageStreamWriter<UIMessage>` so callers can
|
|
202
|
+
// pass the writer they receive from `createUIMessageStream` directly. We only
|
|
203
|
+
// rely on `write` here, so this stays narrow on purpose.
|
|
204
|
+
type StreamWriter = Pick<UIMessageStreamWriter<UIMessage>, "write">;
|
|
211
205
|
|
|
212
206
|
async function writeReplayPart(
|
|
213
207
|
writer: StreamWriter,
|
|
@@ -38,8 +38,8 @@ describe("getModelContextLimit", () => {
|
|
|
38
38
|
expect(getModelContextLimit("anthropic/claude-sonnet-4.6")).toBe(1_000_000);
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
it("returns known mapping for Claude 4
|
|
42
|
-
expect(getModelContextLimit("anthropic/claude-sonnet-4")).toBe(
|
|
41
|
+
it("returns known mapping for Claude Sonnet 4", () => {
|
|
42
|
+
expect(getModelContextLimit("anthropic/claude-sonnet-4")).toBe(1_000_000);
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
it("returns DEFAULT_CONTEXT_LIMIT for unknown models", () => {
|
|
@@ -32,39 +32,51 @@ export const DEFAULT_CONTEXT_LIMIT = 200_000;
|
|
|
32
32
|
*/
|
|
33
33
|
const MODEL_CONTEXT_LIMITS: Partial<Record<KnownModelId, number>> = {
|
|
34
34
|
// Anthropic (1M tier where available, else 200K)
|
|
35
|
+
"anthropic/claude-opus-4.8": 1_000_000,
|
|
36
|
+
"anthropic/claude-opus-4.7": 1_000_000,
|
|
35
37
|
"anthropic/claude-opus-4.6": 1_000_000,
|
|
36
|
-
"anthropic/claude-opus-4.5":
|
|
37
|
-
"anthropic/claude-opus-4.1": 200_000,
|
|
38
|
+
"anthropic/claude-opus-4.5": 200_000,
|
|
38
39
|
"anthropic/claude-sonnet-4.6": 1_000_000,
|
|
39
40
|
"anthropic/claude-sonnet-4.5": 1_000_000,
|
|
40
|
-
"anthropic/claude-sonnet-4":
|
|
41
|
+
"anthropic/claude-sonnet-4": 1_000_000,
|
|
41
42
|
"anthropic/claude-haiku-4.5": 200_000,
|
|
42
43
|
|
|
43
44
|
// OpenAI
|
|
44
|
-
"openai/gpt-5.
|
|
45
|
+
"openai/gpt-5.5": 1_000_000,
|
|
46
|
+
"openai/gpt-5.5-pro": 1_000_000,
|
|
47
|
+
"openai/gpt-5.4": 1_000_000,
|
|
45
48
|
"openai/gpt-5.4-mini": 400_000,
|
|
49
|
+
"openai/gpt-5.4-nano": 400_000,
|
|
50
|
+
"openai/gpt-5.3-codex": 400_000,
|
|
46
51
|
"openai/gpt-5.1": 400_000,
|
|
47
|
-
"openai/gpt-5.1-codex": 400_000,
|
|
48
52
|
"openai/gpt-5": 400_000,
|
|
49
53
|
"openai/gpt-4.1": 1_000_000,
|
|
50
54
|
"openai/o4-mini": 200_000,
|
|
51
55
|
"openai/o3": 200_000,
|
|
52
56
|
|
|
53
57
|
// Google
|
|
58
|
+
"google/gemini-3.5-flash": 1_000_000,
|
|
54
59
|
"google/gemini-3.1-pro-preview": 1_000_000,
|
|
60
|
+
"google/gemini-3.1-flash-lite": 1_000_000,
|
|
55
61
|
"google/gemini-2.5-pro": 1_000_000,
|
|
56
62
|
"google/gemini-2.5-flash": 1_000_000,
|
|
57
63
|
|
|
58
64
|
// Others
|
|
65
|
+
"deepseek/deepseek-v4-pro": 1_000_000,
|
|
66
|
+
"deepseek/deepseek-v4-flash": 1_000_000,
|
|
59
67
|
"deepseek/deepseek-r1": 128_000,
|
|
60
68
|
"deepseek/deepseek-v3.2": 128_000,
|
|
61
69
|
"meta-llama/llama-4-maverick": 1_000_000,
|
|
62
|
-
"x-ai/grok-4":
|
|
70
|
+
"x-ai/grok-4.3": 1_000_000,
|
|
71
|
+
"x-ai/grok-4.20": 2_000_000,
|
|
72
|
+
"qwen/qwen3.7-max": 1_000_000,
|
|
63
73
|
"qwen/qwen3-coder": 256_000,
|
|
64
|
-
"moonshotai/kimi-k2.
|
|
74
|
+
"moonshotai/kimi-k2.6": 256_000,
|
|
75
|
+
"moonshotai/kimi-k2.5": 256_000,
|
|
76
|
+
"mistralai/mistral-medium-3-5": 256_000,
|
|
65
77
|
"mistralai/mistral-medium-3.1": 128_000,
|
|
66
78
|
"mistralai/codestral-2508": 256_000,
|
|
67
|
-
"mistralai/devstral-
|
|
79
|
+
"mistralai/devstral-2512": 256_000,
|
|
68
80
|
};
|
|
69
81
|
|
|
70
82
|
/**
|
package/src/lib/errorTracking.ts
CHANGED
|
@@ -75,10 +75,7 @@ export function initErrorTracking(config: ErrorTrackingConfig = {}): void {
|
|
|
75
75
|
* Track an error to Datadog RUM.
|
|
76
76
|
* Includes context about where the error originated.
|
|
77
77
|
*/
|
|
78
|
-
export function trackError(
|
|
79
|
-
error: Error | unknown,
|
|
80
|
-
context: ErrorContext,
|
|
81
|
-
): void {
|
|
78
|
+
export function trackError(error: unknown, context: ErrorContext): void {
|
|
82
79
|
if (!enabled || !initialized) {
|
|
83
80
|
return;
|
|
84
81
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
1
|
import { describe, expect, it } from "vitest";
|
|
3
2
|
import {
|
|
4
3
|
convertGramMessagesToExported,
|
|
@@ -6,6 +5,7 @@ import {
|
|
|
6
5
|
convertGramMessagePartsToUIMessageParts,
|
|
7
6
|
type GramChatMessage,
|
|
8
7
|
} from "./messageConverter";
|
|
8
|
+
import type { ToolCallMessagePart } from "@assistant-ui/react";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Helper to create a minimal GramChatMessage for testing.
|
|
@@ -41,10 +41,7 @@ describe("convertGramMessagePartsToUIMessageParts", () => {
|
|
|
41
41
|
tool_calls: makeToolCallsJSON([{ id: "tc_1", name: "search_deals" }]),
|
|
42
42
|
} as Partial<GramChatMessage> & { role: string });
|
|
43
43
|
|
|
44
|
-
const parts = convertGramMessagePartsToUIMessageParts(
|
|
45
|
-
msg as any,
|
|
46
|
-
new Map(),
|
|
47
|
-
);
|
|
44
|
+
const parts = convertGramMessagePartsToUIMessageParts(msg, new Map());
|
|
48
45
|
|
|
49
46
|
const toolParts = parts.filter((p) => p.type === "dynamic-tool");
|
|
50
47
|
expect(toolParts).toHaveLength(1);
|
|
@@ -66,11 +63,7 @@ describe("convertGramMessagePartsToUIMessageParts", () => {
|
|
|
66
63
|
]),
|
|
67
64
|
} as Partial<GramChatMessage> & { role: string });
|
|
68
65
|
|
|
69
|
-
const parts = convertGramMessagePartsToUIMessageParts(
|
|
70
|
-
msg as any,
|
|
71
|
-
new Map(),
|
|
72
|
-
seen,
|
|
73
|
-
);
|
|
66
|
+
const parts = convertGramMessagePartsToUIMessageParts(msg, new Map(), seen);
|
|
74
67
|
|
|
75
68
|
const toolParts = parts.filter((p) => p.type === "dynamic-tool");
|
|
76
69
|
expect(toolParts).toHaveLength(1);
|
|
@@ -230,10 +223,15 @@ describe("convertGramMessagesToExported - string content with tool calls", () =>
|
|
|
230
223
|
|
|
231
224
|
const result = convertGramMessagesToExported(messages);
|
|
232
225
|
const assistantEntry = result.messages.find(
|
|
233
|
-
(m)
|
|
226
|
+
(m): m is typeof m & { message: { role: "assistant" } } =>
|
|
227
|
+
m.message.role === "assistant",
|
|
234
228
|
)!;
|
|
235
|
-
const
|
|
236
|
-
|
|
229
|
+
const assistantMessage = assistantEntry.message;
|
|
230
|
+
if (assistantMessage.role !== "assistant") {
|
|
231
|
+
throw new Error("expected assistant message");
|
|
232
|
+
}
|
|
233
|
+
const toolCallParts = assistantMessage.content.filter(
|
|
234
|
+
(p): p is ToolCallMessagePart => p.type === "tool-call",
|
|
237
235
|
);
|
|
238
236
|
expect(toolCallParts).toHaveLength(1);
|
|
239
237
|
expect(toolCallParts[0]).toMatchObject({
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Message format converter for Gram API <-> assistant-ui.
|
|
5
3
|
*
|
|
@@ -19,23 +17,69 @@ import type {
|
|
|
19
17
|
ThreadAssistantMessagePart,
|
|
20
18
|
TextMessagePart,
|
|
21
19
|
} from "@assistant-ui/react";
|
|
22
|
-
import type {
|
|
23
|
-
Message,
|
|
24
|
-
UserMessage,
|
|
25
|
-
AssistantMessage,
|
|
26
|
-
ToolResponseMessage,
|
|
27
|
-
} from "@openrouter/sdk/models";
|
|
28
20
|
import { UIMessage } from "ai";
|
|
29
21
|
|
|
30
22
|
/**
|
|
31
|
-
*
|
|
32
|
-
|
|
23
|
+
* A single text part of a multi-modal chat message.
|
|
24
|
+
*/
|
|
25
|
+
export interface GramChatTextPart {
|
|
26
|
+
type: "text";
|
|
27
|
+
text: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A single image part of a multi-modal chat message. The wire shape mirrors the
|
|
32
|
+
* upstream OpenAI/OpenRouter chat schema (`image_url.url`) so the converter can
|
|
33
|
+
* read it without normalisation.
|
|
34
|
+
*/
|
|
35
|
+
export interface GramChatImagePart {
|
|
36
|
+
type: "image_url";
|
|
37
|
+
image_url?: { url?: string };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A single audio part of a multi-modal chat message. Mirrors the OpenAI/OpenRouter
|
|
42
|
+
* `input_audio.{data, format}` shape on the wire.
|
|
43
|
+
*/
|
|
44
|
+
export interface GramChatAudioPart {
|
|
45
|
+
type: "input_audio";
|
|
46
|
+
input_audio?: { data?: string; format?: string };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Content part of a multi-modal chat message.
|
|
33
51
|
*/
|
|
34
|
-
export type
|
|
52
|
+
export type GramChatContentPart =
|
|
53
|
+
| GramChatTextPart
|
|
54
|
+
| GramChatImagePart
|
|
55
|
+
| GramChatAudioPart;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Content of a chat message — either a plain string or an array of parts for
|
|
59
|
+
* multi-modal messages.
|
|
60
|
+
*/
|
|
61
|
+
export type GramChatContent = string | GramChatContentPart[];
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Represents a chat message from the Gram API. Only fields actually surfaced
|
|
65
|
+
* through Elements' public converters are modelled; provider-specific extras
|
|
66
|
+
* remain on the wire shape but are intentionally not part of the contract.
|
|
67
|
+
*
|
|
68
|
+
* `tool_calls` is the JSON-encoded string the Gram chat service stores on
|
|
69
|
+
* assistant rows; `tool_call_id` is the id the corresponding tool-response row
|
|
70
|
+
* carries when `role === "tool"`.
|
|
71
|
+
*/
|
|
72
|
+
export interface GramChatMessage {
|
|
35
73
|
id: string;
|
|
36
74
|
model: string;
|
|
37
75
|
created_at: Date | string;
|
|
38
|
-
|
|
76
|
+
role: "system" | "developer" | "user" | "assistant" | "tool";
|
|
77
|
+
content?: GramChatContent | null;
|
|
78
|
+
name?: string;
|
|
79
|
+
tool_calls?: string;
|
|
80
|
+
tool_call_id?: string;
|
|
81
|
+
reasoning?: string | null;
|
|
82
|
+
}
|
|
39
83
|
|
|
40
84
|
/**
|
|
41
85
|
* Represents a chat from the Gram API.
|
|
@@ -96,29 +140,23 @@ function buildUserContentParts(msg: GramChatMessage): ThreadUserMessagePart[] {
|
|
|
96
140
|
text: item.text,
|
|
97
141
|
});
|
|
98
142
|
break;
|
|
99
|
-
case "image_url":
|
|
143
|
+
case "image_url": {
|
|
144
|
+
const url = item.image_url?.url ?? "";
|
|
100
145
|
parts.push({
|
|
101
146
|
type: "image",
|
|
102
|
-
image:
|
|
103
|
-
string,
|
|
104
|
-
"Fixed by switching to Gram TS SDK."
|
|
105
|
-
>,
|
|
147
|
+
image: url,
|
|
106
148
|
});
|
|
107
149
|
break;
|
|
150
|
+
}
|
|
108
151
|
case "input_audio": {
|
|
109
|
-
const format =
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
>;
|
|
113
|
-
if (format === "mp3" || format === "wav") {
|
|
152
|
+
const format = item.input_audio?.format;
|
|
153
|
+
const data = item.input_audio?.data;
|
|
154
|
+
if ((format === "mp3" || format === "wav") && data) {
|
|
114
155
|
parts.push({
|
|
115
156
|
type: "audio",
|
|
116
157
|
audio: {
|
|
117
|
-
data
|
|
118
|
-
|
|
119
|
-
"Fixed by switching to Gram TS SDK."
|
|
120
|
-
>,
|
|
121
|
-
format: format,
|
|
158
|
+
data,
|
|
159
|
+
format,
|
|
122
160
|
},
|
|
123
161
|
});
|
|
124
162
|
}
|
|
@@ -155,12 +193,18 @@ function buildAssistantContentParts(
|
|
|
155
193
|
});
|
|
156
194
|
}
|
|
157
195
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
196
|
+
// Accept both the OpenAI/OpenRouter shape (`{ id, function: { name, arguments } }`)
|
|
197
|
+
// and the assistant-ui shape (`{ toolCallId, toolName, args }`). Tool calls
|
|
198
|
+
// arrive as JSON the server stored opaquely, so we model the union here.
|
|
199
|
+
type WireToolCall = {
|
|
200
|
+
id?: string;
|
|
201
|
+
toolCallId?: string;
|
|
202
|
+
function?: { name?: string; arguments?: string | Record<string, unknown> };
|
|
203
|
+
toolName?: string;
|
|
204
|
+
args?: string | Record<string, unknown>;
|
|
205
|
+
};
|
|
162
206
|
|
|
163
|
-
let toolCalls = tryParseJSON(
|
|
207
|
+
let toolCalls = tryParseJSON<WireToolCall[]>(msg.tool_calls || "[]");
|
|
164
208
|
if (!Array.isArray(toolCalls)) {
|
|
165
209
|
console.warn("Invalid tool_calls format, expected an array.");
|
|
166
210
|
toolCalls = [];
|
|
@@ -280,8 +324,10 @@ function convertGramMessageToThreadMessage(
|
|
|
280
324
|
* Converts an array of Gram ChatMessages to an ExportedMessageRepository.
|
|
281
325
|
* Creates parent-child relationships based on message order.
|
|
282
326
|
*
|
|
283
|
-
* Note:
|
|
284
|
-
*
|
|
327
|
+
* Note: system, developer, and tool messages are filtered out. assistant-ui's
|
|
328
|
+
* exported format only models user/assistant turns; system/developer rows are
|
|
329
|
+
* pre-prompt instructions the UI doesn't render, and tool rows are folded into
|
|
330
|
+
* the preceding assistant message as `tool-call` parts via `tool_calls`.
|
|
285
331
|
*/
|
|
286
332
|
export function convertGramMessagesToExported(
|
|
287
333
|
messages: GramChatMessage[],
|
|
@@ -294,8 +340,11 @@ export function convertGramMessagesToExported(
|
|
|
294
340
|
let prevId: string | null = null;
|
|
295
341
|
|
|
296
342
|
for (const msg of messages) {
|
|
297
|
-
|
|
298
|
-
|
|
343
|
+
if (
|
|
344
|
+
msg.role === "system" ||
|
|
345
|
+
msg.role === "developer" ||
|
|
346
|
+
msg.role === "tool"
|
|
347
|
+
) {
|
|
299
348
|
continue;
|
|
300
349
|
}
|
|
301
350
|
|
|
@@ -322,17 +371,17 @@ export function convertGramMessagesToUIMessages(messages: GramChatMessage[]): {
|
|
|
322
371
|
return { messages: [], headId: null };
|
|
323
372
|
}
|
|
324
373
|
|
|
325
|
-
const toolCallResults = new Map<string,
|
|
374
|
+
const toolCallResults = new Map<string, GramChatMessage>();
|
|
326
375
|
for (const msg of messages) {
|
|
327
376
|
if (msg.role !== "tool") {
|
|
328
377
|
continue;
|
|
329
378
|
}
|
|
330
|
-
const id =
|
|
379
|
+
const id = msg.tool_call_id;
|
|
331
380
|
if (typeof id !== "string") {
|
|
332
381
|
continue;
|
|
333
382
|
}
|
|
334
383
|
|
|
335
|
-
toolCallResults.set(id, msg
|
|
384
|
+
toolCallResults.set(id, msg);
|
|
336
385
|
}
|
|
337
386
|
|
|
338
387
|
const uiMessages: { parentId: string | null; message: UIMessage }[] = [];
|
|
@@ -415,9 +464,19 @@ export function convertGramMessagesToUIMessages(messages: GramChatMessage[]): {
|
|
|
415
464
|
};
|
|
416
465
|
}
|
|
417
466
|
|
|
467
|
+
/**
|
|
468
|
+
* Parsed shape of a single entry inside an assistant message's `tool_calls`
|
|
469
|
+
* JSON string. Mirrors the OpenAI/OpenRouter tool-call wire format.
|
|
470
|
+
*/
|
|
471
|
+
interface GramToolCall {
|
|
472
|
+
id: string;
|
|
473
|
+
type?: "function";
|
|
474
|
+
function?: { name?: string; arguments?: string | Record<string, unknown> };
|
|
475
|
+
}
|
|
476
|
+
|
|
418
477
|
export function convertGramMessagePartsToUIMessageParts(
|
|
419
|
-
msg:
|
|
420
|
-
toolResults: Map<string,
|
|
478
|
+
msg: GramChatMessage,
|
|
479
|
+
toolResults: Map<string, GramChatMessage>,
|
|
421
480
|
seenToolCallIds?: Set<string>,
|
|
422
481
|
): UIMessage["parts"] {
|
|
423
482
|
const uiparts: UIMessage["parts"] = [];
|
|
@@ -440,10 +499,7 @@ export function convertGramMessagePartsToUIMessageParts(
|
|
|
440
499
|
break;
|
|
441
500
|
}
|
|
442
501
|
case "image_url": {
|
|
443
|
-
const url =
|
|
444
|
-
string | undefined,
|
|
445
|
-
"Fixed by switching to Gram TS SDK."
|
|
446
|
-
>;
|
|
502
|
+
const url = p.image_url?.url;
|
|
447
503
|
if (!url) {
|
|
448
504
|
break;
|
|
449
505
|
}
|
|
@@ -456,10 +512,7 @@ export function convertGramMessagePartsToUIMessageParts(
|
|
|
456
512
|
break;
|
|
457
513
|
}
|
|
458
514
|
case "input_audio": {
|
|
459
|
-
const url =
|
|
460
|
-
string | undefined,
|
|
461
|
-
"Fixed by switching to Gram TS SDK."
|
|
462
|
-
>;
|
|
515
|
+
const url = p.input_audio?.data;
|
|
463
516
|
if (!url) {
|
|
464
517
|
break;
|
|
465
518
|
}
|
|
@@ -481,14 +534,8 @@ export function convertGramMessagePartsToUIMessageParts(
|
|
|
481
534
|
});
|
|
482
535
|
}
|
|
483
536
|
|
|
484
|
-
if (msg.role === "assistant" &&
|
|
485
|
-
|
|
486
|
-
string,
|
|
487
|
-
"Fixed by switching to Gram TS SDK."
|
|
488
|
-
>;
|
|
489
|
-
let toolCalls = tryParseJSON<AssistantMessage["toolCalls"]>(
|
|
490
|
-
toolCallsJSON || "[]",
|
|
491
|
-
);
|
|
537
|
+
if (msg.role === "assistant" && msg.tool_calls) {
|
|
538
|
+
let toolCalls = tryParseJSON<GramToolCall[]>(msg.tool_calls || "[]");
|
|
492
539
|
if (!Array.isArray(toolCalls)) {
|
|
493
540
|
console.warn("Invalid tool_calls format, expected an array.");
|
|
494
541
|
toolCalls = [];
|
|
@@ -526,7 +573,7 @@ function mediaTypeFromURL(url: string): string {
|
|
|
526
573
|
return match?.[1] || unspecified;
|
|
527
574
|
}
|
|
528
575
|
|
|
529
|
-
function tryParseJSON<T =
|
|
576
|
+
function tryParseJSON<T = unknown>(str: string): T | null {
|
|
530
577
|
try {
|
|
531
578
|
return JSON.parse(str) as T;
|
|
532
579
|
} catch {
|
package/src/lib/models.ts
CHANGED
|
@@ -1,31 +1,43 @@
|
|
|
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.
|
|
4
|
+
"anthropic/claude-opus-4.8",
|
|
5
|
+
"anthropic/claude-opus-4.7",
|
|
5
6
|
"anthropic/claude-sonnet-4.6",
|
|
6
7
|
"anthropic/claude-sonnet-4.5",
|
|
8
|
+
"anthropic/claude-opus-4.6",
|
|
7
9
|
"anthropic/claude-opus-4.5",
|
|
8
10
|
"anthropic/claude-haiku-4.5",
|
|
9
|
-
"anthropic/claude-opus-4.1",
|
|
10
11
|
"anthropic/claude-sonnet-4",
|
|
12
|
+
"openai/gpt-5.5",
|
|
13
|
+
"openai/gpt-5.5-pro",
|
|
11
14
|
"openai/gpt-5.4",
|
|
12
15
|
"openai/gpt-5.4-mini",
|
|
16
|
+
"openai/gpt-5.4-nano",
|
|
17
|
+
"openai/gpt-5.3-codex",
|
|
13
18
|
"openai/gpt-5.1",
|
|
14
|
-
"openai/gpt-5.1-codex",
|
|
15
19
|
"openai/gpt-5",
|
|
16
20
|
"openai/gpt-4.1",
|
|
17
21
|
"openai/o4-mini",
|
|
18
22
|
"openai/o3",
|
|
23
|
+
"google/gemini-3.5-flash",
|
|
19
24
|
"google/gemini-3.1-pro-preview",
|
|
25
|
+
"google/gemini-3.1-flash-lite",
|
|
20
26
|
"google/gemini-2.5-pro",
|
|
21
27
|
"google/gemini-2.5-flash",
|
|
22
|
-
"deepseek/deepseek-
|
|
28
|
+
"deepseek/deepseek-v4-pro",
|
|
29
|
+
"deepseek/deepseek-v4-flash",
|
|
23
30
|
"deepseek/deepseek-v3.2",
|
|
31
|
+
"deepseek/deepseek-r1",
|
|
24
32
|
"meta-llama/llama-4-maverick",
|
|
25
|
-
"x-ai/grok-4",
|
|
33
|
+
"x-ai/grok-4.3",
|
|
34
|
+
"x-ai/grok-4.20",
|
|
35
|
+
"qwen/qwen3.7-max",
|
|
26
36
|
"qwen/qwen3-coder",
|
|
37
|
+
"moonshotai/kimi-k2.6",
|
|
27
38
|
"moonshotai/kimi-k2.5",
|
|
28
|
-
"mistralai/mistral-medium-3
|
|
39
|
+
"mistralai/mistral-medium-3-5",
|
|
29
40
|
"mistralai/codestral-2508",
|
|
30
|
-
"mistralai/devstral-
|
|
41
|
+
"mistralai/devstral-2512",
|
|
42
|
+
"mistralai/mistral-medium-3.1",
|
|
31
43
|
] as const;
|
package/src/lib/token.ts
CHANGED
|
@@ -9,7 +9,7 @@ export function getTokenExpiry(token: string): number | null {
|
|
|
9
9
|
if (parts.length !== 3) return null;
|
|
10
10
|
|
|
11
11
|
// base64url → base64 → decode
|
|
12
|
-
let payload = parts[1]
|
|
12
|
+
let payload = parts[1]!.replace(/-/g, "+").replace(/_/g, "/");
|
|
13
13
|
while (payload.length % 4) payload += "=";
|
|
14
14
|
|
|
15
15
|
const json = atob(payload);
|
|
@@ -29,10 +29,7 @@ export function getTokenExpiry(token: string): number | null {
|
|
|
29
29
|
* of expiry. Fails open (returns false) for non-JWT tokens or tokens
|
|
30
30
|
* without an `exp` claim so they pass through unchanged.
|
|
31
31
|
*/
|
|
32
|
-
export function isTokenExpired(
|
|
33
|
-
token: string,
|
|
34
|
-
bufferMs: number = 30_000,
|
|
35
|
-
): boolean {
|
|
32
|
+
export function isTokenExpired(token: string, bufferMs = 30_000): boolean {
|
|
36
33
|
const exp = getTokenExpiry(token);
|
|
37
34
|
if (exp === null) return false; // fail-open for non-JWT tokens
|
|
38
35
|
return Date.now() >= exp * 1000 - bufferMs;
|