@gram-ai/elements 1.27.3 → 1.27.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -60
- package/README.typedoc.md +6 -6
- package/bin/cli.js +74 -74
- package/dist/compat-shims-CO9JXXV4.cjs.map +1 -1
- package/dist/{compat-shims-BPJ7Q68c.js → compat-shims-DxtUrORi.js} +4 -2
- package/dist/compat-shims-DxtUrORi.js.map +1 -0
- package/dist/components/ShareButton/index.d.ts +2 -2
- package/dist/components/assistant-ui/message-feedback.d.ts +1 -1
- package/dist/components/assistant-ui/tooltip-icon-button.d.ts +2 -2
- package/dist/components/ui/avatar.d.ts +2 -2
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/calendar.d.ts +1 -1
- package/dist/components/ui/collapsible.d.ts +1 -1
- package/dist/components/ui/dialog.d.ts +4 -4
- package/dist/components/ui/popover.d.ts +2 -2
- package/dist/components/ui/skeleton.d.ts +1 -1
- package/dist/components/ui/time-range-picker.d.ts +4 -2
- package/dist/components/ui/tool-ui.d.ts +7 -7
- package/dist/components/ui/tooltip.d.ts +2 -2
- package/dist/contexts/ConnectionStatusContext.d.ts +1 -1
- package/dist/elements.cjs +1 -1
- package/dist/elements.css +1 -1
- package/dist/elements.js +2 -2
- package/dist/hooks/useDensity.d.ts +73 -73
- package/dist/hooks/useMCPTools.d.ts +1 -1
- package/dist/hooks/useRadius.d.ts +1 -1
- package/dist/{index-BpJstUh1.cjs → index-C4bFBGfl.cjs} +4 -4
- package/dist/{index-BpJstUh1.cjs.map → index-C4bFBGfl.cjs.map} +1 -1
- package/dist/{index-CUitXazZ.js → index-D93pV0_o.js} +55 -55
- package/dist/{index-CUitXazZ.js.map → index-D93pV0_o.js.map} +1 -1
- package/dist/{index-DBrhzauj.js → index-DuCQRbcQ.js} +6386 -6337
- package/dist/index-DuCQRbcQ.js.map +1 -0
- package/dist/{index-DxfW52oA.cjs → index-y_PNN5vK.cjs} +64 -46
- package/dist/index-y_PNN5vK.cjs.map +1 -0
- package/dist/lib/cassette.d.ts +4 -4
- package/dist/lib/errorTracking.d.ts +1 -1
- package/dist/lib/messageConverter.d.ts +1 -1
- package/dist/lib/models.d.ts +1 -1
- package/dist/plugins/chart/ui/bar-chart.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/accordion-wrapper.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/accordion.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/action-button.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/alert-wrapper.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/alert.d.ts +4 -4
- package/dist/plugins/generative-ui/ui/avatar.d.ts +5 -5
- package/dist/plugins/generative-ui/ui/badge.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/button-wrapper.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/button.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/card-wrapper.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/card.d.ts +8 -8
- package/dist/plugins/generative-ui/ui/checkbox.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/data-table.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/dialog.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/dropdown-menu.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/grid.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/input-wrapper.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/input.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/label.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/metric.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/pagination.d.ts +6 -6
- package/dist/plugins/generative-ui/ui/popover.d.ts +4 -4
- package/dist/plugins/generative-ui/ui/progress.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/radio-group.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/select.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/separator.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/skeleton.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/stack.d.ts +6 -6
- package/dist/plugins/generative-ui/ui/switch.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/table.d.ts +9 -9
- package/dist/plugins/generative-ui/ui/tabs-wrapper.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/tabs.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/text.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/textarea.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/tooltip.d.ts +1 -1
- package/dist/plugins.cjs +1 -1
- package/dist/plugins.js +1 -1
- package/dist/{profiler-D6ndqfsd.js → profiler-FpBY9eRv.js} +2 -2
- package/dist/{profiler-D6ndqfsd.js.map → profiler-FpBY9eRv.js.map} +1 -1
- package/dist/{profiler-DhnzZ34c.cjs → profiler-_mthyjvo.cjs} +2 -2
- package/dist/{profiler-DhnzZ34c.cjs.map → profiler-_mthyjvo.cjs.map} +1 -1
- package/dist/react-shim.js +1 -1
- package/dist/server/express.cjs.map +1 -1
- package/dist/server/express.js.map +1 -1
- package/dist/{startRecording-BwXmdmy1.cjs → startRecording-NJcpiHw-.cjs} +2 -2
- package/dist/{startRecording-BwXmdmy1.cjs.map → startRecording-NJcpiHw-.cjs.map} +1 -1
- package/dist/{startRecording-B_9CRZ_P.js → startRecording-r5MXQ2Dm.js} +2 -2
- package/dist/{startRecording-B_9CRZ_P.js.map → startRecording-r5MXQ2Dm.js.map} +1 -1
- package/dist/types/index.d.ts +2 -2
- package/package.json +1 -5
- package/src/compat-plugin.ts +14 -14
- package/src/compat-shims.ts +33 -31
- package/src/compat.test.ts +48 -48
- package/src/compat.ts +6 -6
- package/src/components/Chat/index.tsx +17 -17
- package/src/components/Chat/stories/Charts.stories.tsx +98 -98
- package/src/components/Chat/stories/Composer.stories.tsx +15 -15
- package/src/components/Chat/stories/ConnectionConfiguration.stories.tsx +44 -44
- package/src/components/Chat/stories/CustomComponents.stories.tsx +17 -17
- package/src/components/Chat/stories/Density.stories.tsx +20 -20
- package/src/components/Chat/stories/ErrorBoundary.stories.tsx +47 -47
- package/src/components/Chat/stories/FrontendTools.stories.tsx +39 -39
- package/src/components/Chat/stories/GenerativeUI.stories.tsx +48 -48
- package/src/components/Chat/stories/MessageFeedback.stories.tsx +52 -52
- package/src/components/Chat/stories/Modal.stories.tsx +28 -28
- package/src/components/Chat/stories/Model.stories.tsx +11 -11
- package/src/components/Chat/stories/Radius.stories.tsx +20 -20
- package/src/components/Chat/stories/Sidecar.stories.tsx +13 -13
- package/src/components/Chat/stories/StyleIsolation.stories.tsx +11 -11
- package/src/components/Chat/stories/Theme.stories.tsx +25 -25
- package/src/components/Chat/stories/Thread.stories.tsx +25 -25
- package/src/components/Chat/stories/ToolApproval.stories.tsx +55 -55
- package/src/components/Chat/stories/ToolMentions.stories.tsx +17 -17
- package/src/components/Chat/stories/Tools.stories.tsx +88 -88
- package/src/components/Chat/stories/Variants.stories.tsx +32 -32
- package/src/components/Chat/stories/Welcome.stories.tsx +14 -14
- package/src/components/ChatHistory.tsx +7 -7
- package/src/components/FrontendTools/index.tsx +5 -5
- package/src/components/Replay.stories.tsx +157 -157
- package/src/components/Replay.tsx +76 -73
- package/src/components/ShadowRoot.tsx +40 -40
- package/src/components/ShareButton/index.tsx +32 -32
- package/src/components/assistant-ui/assistant-modal.tsx +92 -87
- package/src/components/assistant-ui/assistant-sidecar.tsx +35 -35
- package/src/components/assistant-ui/attachment.tsx +80 -80
- package/src/components/assistant-ui/connection-status-indicator.tsx +33 -33
- package/src/components/assistant-ui/error-boundary.tsx +34 -34
- package/src/components/assistant-ui/follow-on-suggestions.tsx +26 -26
- package/src/components/assistant-ui/markdown-text.tsx +69 -69
- package/src/components/assistant-ui/mentioned-tools-badges.tsx +38 -38
- package/src/components/assistant-ui/message-feedback.tsx +74 -61
- package/src/components/assistant-ui/reasoning.tsx +83 -83
- package/src/components/assistant-ui/thread-list.tsx +45 -45
- package/src/components/assistant-ui/thread.tsx +278 -278
- package/src/components/assistant-ui/tool-fallback.tsx +37 -37
- package/src/components/assistant-ui/tool-group.tsx +26 -26
- package/src/components/assistant-ui/tool-mention-autocomplete.tsx +122 -122
- package/src/components/assistant-ui/tooltip-icon-button.tsx +18 -18
- package/src/components/ui/avatar.tsx +12 -12
- package/src/components/ui/button.tsx +12 -12
- package/src/components/ui/buttonVariants.ts +17 -17
- package/src/components/ui/calendar.tsx +106 -106
- package/src/components/ui/charts.stories.tsx +56 -56
- package/src/components/ui/collapsible.tsx +5 -5
- package/src/components/ui/dialog.tsx +30 -30
- package/src/components/ui/generative-ui.stories.tsx +200 -200
- package/src/components/ui/generative-ui.tsx +26 -26
- package/src/components/ui/popover.tsx +14 -14
- package/src/components/ui/skeleton.tsx +5 -5
- package/src/components/ui/time-range-picker.stories.tsx +80 -80
- package/src/components/ui/time-range-picker.tsx +272 -235
- package/src/components/ui/tool-ui.stories.tsx +37 -37
- package/src/components/ui/tool-ui.tsx +221 -215
- package/src/components/ui/tooltip.tsx +15 -15
- package/src/constants/tailwind.ts +1 -1
- package/src/contexts/ChatIdContext.tsx +7 -7
- package/src/contexts/ConnectionStatusContext.tsx +64 -64
- package/src/contexts/ElementsProvider.tsx +222 -211
- package/src/contexts/ReplayContext.ts +3 -3
- package/src/contexts/ToolApprovalContext.tsx +54 -54
- package/src/contexts/ToolExecutionContext.tsx +34 -34
- package/src/contexts/contexts.ts +7 -7
- package/src/contexts/portal-container-context.ts +2 -2
- package/src/contexts/portal-container.tsx +7 -7
- package/src/embedded.ts +1 -1
- package/src/global.css +25 -25
- package/src/hooks/useAuth.ts +72 -72
- package/src/hooks/useDensity.ts +79 -79
- package/src/hooks/useElements.ts +6 -6
- package/src/hooks/useExpanded.ts +12 -12
- package/src/hooks/useFollowOnSuggestions.ts +87 -82
- package/src/hooks/useGramThreadListAdapter.tsx +99 -99
- package/src/hooks/useMCPTools.ts +47 -47
- package/src/hooks/useModel.ts +14 -14
- package/src/hooks/usePluginComponents.ts +11 -11
- package/src/hooks/usePortalContainer.ts +5 -5
- package/src/hooks/useRadius.ts +23 -23
- package/src/hooks/useRecordCassette.ts +34 -34
- package/src/hooks/useSession.ts +11 -11
- package/src/hooks/useThemeProps.ts +13 -13
- package/src/hooks/useThreadId.ts +4 -4
- package/src/hooks/useToolApproval.ts +7 -7
- package/src/hooks/useToolMentions.ts +40 -40
- package/src/index.ts +26 -26
- package/src/lib/api.test.ts +61 -61
- package/src/lib/api.ts +4 -3
- package/src/lib/auth.ts +13 -13
- package/src/lib/cassette.ts +84 -84
- package/src/lib/easing.ts +1 -1
- package/src/lib/errorTracking.config.ts +5 -5
- package/src/lib/errorTracking.ts +29 -29
- package/src/lib/generative-ui.ts +7 -7
- package/src/lib/humanize.ts +3 -3
- package/src/lib/messageConverter.test.ts +130 -127
- package/src/lib/messageConverter.ts +196 -196
- package/src/lib/models.ts +21 -20
- package/src/lib/token.test.ts +56 -56
- package/src/lib/token.ts +14 -14
- package/src/lib/tool-mentions.ts +45 -45
- package/src/lib/tools.ts +66 -62
- package/src/lib/utils.ts +5 -5
- package/src/lib.d.ts +1 -1
- package/src/plugins/README.md +5 -5
- package/src/plugins/chart/catalog.ts +18 -18
- package/src/plugins/chart/chart.test.ts +31 -31
- package/src/plugins/chart/component.tsx +34 -34
- package/src/plugins/chart/index.ts +4 -4
- package/src/plugins/chart/ui/area-chart.tsx +42 -42
- package/src/plugins/chart/ui/bar-chart.tsx +46 -46
- package/src/plugins/chart/ui/donut-chart.tsx +48 -48
- package/src/plugins/chart/ui/index.ts +7 -7
- package/src/plugins/chart/ui/line-chart.tsx +43 -43
- package/src/plugins/chart/ui/pie-chart.tsx +44 -44
- package/src/plugins/chart/ui/radar-chart.tsx +33 -33
- package/src/plugins/chart/ui/scatter-chart.tsx +43 -43
- package/src/plugins/components/MacOSWindowFrame.tsx +15 -15
- package/src/plugins/components/PluginLoadingState.tsx +10 -10
- package/src/plugins/components/index.ts +1 -1
- package/src/plugins/generative-ui/catalog.ts +54 -54
- package/src/plugins/generative-ui/component.tsx +85 -85
- package/src/plugins/generative-ui/index.ts +4 -4
- package/src/plugins/generative-ui/ui/accordion-wrapper.tsx +16 -16
- package/src/plugins/generative-ui/ui/accordion.tsx +16 -16
- package/src/plugins/generative-ui/ui/action-button.tsx +28 -28
- package/src/plugins/generative-ui/ui/alert-wrapper.tsx +8 -8
- package/src/plugins/generative-ui/ui/alert.tsx +20 -20
- package/src/plugins/generative-ui/ui/avatar-wrapper.tsx +7 -7
- package/src/plugins/generative-ui/ui/avatar.tsx +30 -30
- package/src/plugins/generative-ui/ui/badge.tsx +22 -22
- package/src/plugins/generative-ui/ui/button-wrapper.tsx +12 -12
- package/src/plugins/generative-ui/ui/button.tsx +28 -28
- package/src/plugins/generative-ui/ui/card-wrapper.tsx +8 -8
- package/src/plugins/generative-ui/ui/card.tsx +27 -27
- package/src/plugins/generative-ui/ui/checkbox-wrapper.tsx +9 -9
- package/src/plugins/generative-ui/ui/checkbox.tsx +9 -9
- package/src/plugins/generative-ui/ui/data-table.tsx +8 -8
- package/src/plugins/generative-ui/ui/dialog.tsx +31 -31
- package/src/plugins/generative-ui/ui/dropdown-menu.tsx +44 -44
- package/src/plugins/generative-ui/ui/grid.tsx +12 -12
- package/src/plugins/generative-ui/ui/index.ts +40 -40
- package/src/plugins/generative-ui/ui/input-wrapper.tsx +11 -11
- package/src/plugins/generative-ui/ui/input.tsx +9 -9
- package/src/plugins/generative-ui/ui/label.tsx +8 -8
- package/src/plugins/generative-ui/ui/list.tsx +11 -11
- package/src/plugins/generative-ui/ui/metric.tsx +23 -23
- package/src/plugins/generative-ui/ui/pagination.tsx +28 -28
- package/src/plugins/generative-ui/ui/popover.tsx +21 -21
- package/src/plugins/generative-ui/ui/progress.tsx +13 -13
- package/src/plugins/generative-ui/ui/radio-group.tsx +12 -12
- package/src/plugins/generative-ui/ui/select-wrapper.tsx +7 -7
- package/src/plugins/generative-ui/ui/select.tsx +37 -37
- package/src/plugins/generative-ui/ui/separator.tsx +9 -9
- package/src/plugins/generative-ui/ui/skeleton-wrapper.tsx +10 -10
- package/src/plugins/generative-ui/ui/skeleton.tsx +5 -5
- package/src/plugins/generative-ui/ui/stack.tsx +28 -28
- package/src/plugins/generative-ui/ui/switch.tsx +11 -11
- package/src/plugins/generative-ui/ui/table.tsx +32 -32
- package/src/plugins/generative-ui/ui/tabs-wrapper.tsx +11 -11
- package/src/plugins/generative-ui/ui/tabs.tsx +26 -26
- package/src/plugins/generative-ui/ui/text.tsx +12 -12
- package/src/plugins/generative-ui/ui/textarea.tsx +7 -7
- package/src/plugins/generative-ui/ui/tooltip.tsx +12 -12
- package/src/plugins/index.ts +7 -7
- package/src/react-shim.ts +6 -6
- package/src/server/bun.ts +12 -12
- package/src/server/core.ts +25 -25
- package/src/server/express.ts +17 -15
- package/src/server/fastify.ts +14 -14
- package/src/server/hono.ts +9 -9
- package/src/server/nextjs.ts +12 -12
- package/src/server/tanstack-start.ts +12 -12
- package/src/server.ts +27 -27
- package/src/storybook.d.ts +4 -4
- package/src/types/index.ts +122 -122
- package/src/types/plugins.ts +7 -7
- package/src/vite-env.d.ts +12 -12
- package/dist/compat-shims-BPJ7Q68c.js.map +0 -1
- package/dist/index-DBrhzauj.js.map +0 -1
- package/dist/index-DxfW52oA.cjs.map +0 -1
package/src/lib/models.ts
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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-5.4",
|
|
10
|
+
"openai/gpt-4o",
|
|
11
|
+
"openai/gpt-4o-mini",
|
|
12
|
+
"openai/gpt-5.1-codex",
|
|
13
|
+
"openai/gpt-5",
|
|
14
|
+
"openai/gpt-5.1",
|
|
15
|
+
"openai/gpt-4.1",
|
|
16
|
+
"anthropic/claude-3.7-sonnet",
|
|
17
|
+
"anthropic/claude-opus-4",
|
|
18
|
+
"google/gemini-2.5-pro-preview",
|
|
19
|
+
"google/gemini-3-pro-preview",
|
|
20
|
+
"moonshotai/kimi-k2",
|
|
21
|
+
"mistralai/mistral-medium-3",
|
|
22
|
+
"mistralai/mistral-medium-3.1",
|
|
23
|
+
"mistralai/codestral-2501",
|
|
24
|
+
] as const;
|
package/src/lib/token.test.ts
CHANGED
|
@@ -1,79 +1,79 @@
|
|
|
1
|
-
import { describe, expect, it } from
|
|
2
|
-
import { getTokenExpiry, isTokenExpired } from
|
|
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:
|
|
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(
|
|
12
|
-
it(
|
|
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(
|
|
18
|
-
expect(getTokenExpiry(makeJwt({ sub:
|
|
19
|
-
})
|
|
17
|
+
it("returns null when exp is missing", () => {
|
|
18
|
+
expect(getTokenExpiry(makeJwt({ sub: "user" }))).toBeNull();
|
|
19
|
+
});
|
|
20
20
|
|
|
21
|
-
it(
|
|
22
|
-
expect(getTokenExpiry(
|
|
23
|
-
})
|
|
21
|
+
it("returns null for a non-JWT string", () => {
|
|
22
|
+
expect(getTokenExpiry("not-a-jwt")).toBeNull();
|
|
23
|
+
});
|
|
24
24
|
|
|
25
|
-
it(
|
|
26
|
-
expect(getTokenExpiry(
|
|
27
|
-
})
|
|
25
|
+
it("returns null for an empty string", () => {
|
|
26
|
+
expect(getTokenExpiry("")).toBeNull();
|
|
27
|
+
});
|
|
28
28
|
|
|
29
|
-
it(
|
|
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(
|
|
32
|
-
})
|
|
31
|
+
expect(getTokenExpiry("a.!!!.b")).toBeNull();
|
|
32
|
+
});
|
|
33
33
|
|
|
34
|
-
it(
|
|
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(
|
|
48
|
-
it(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
73
|
-
expect(isTokenExpired(
|
|
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(
|
|
77
|
-
expect(isTokenExpired(
|
|
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,
|
|
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 ===
|
|
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
|
}
|
package/src/lib/tool-mentions.ts
CHANGED
|
@@ -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 ===
|
|
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:
|
|
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(
|
|
65
|
-
return { isInMention: false, query:
|
|
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?`,
|
|
105
|
-
return text.replace(pattern,
|
|
104
|
+
const pattern = new RegExp(`@${toolName}\\s?`, "gi");
|
|
105
|
+
return text.replace(pattern, "");
|
|
106
106
|
}
|
package/src/lib/tools.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type { ToolsFilter } from
|
|
1
|
+
import type { ToolsFilter } from "@/types";
|
|
2
2
|
import {
|
|
3
3
|
AssistantToolProps,
|
|
4
4
|
Tool,
|
|
5
5
|
makeAssistantTool,
|
|
6
|
-
} from
|
|
7
|
-
import { JSONSchema7, ToolSet, type ToolCallOptions } from
|
|
8
|
-
import { FC } from
|
|
9
|
-
import z from
|
|
6
|
+
} from "@assistant-ui/react";
|
|
7
|
+
import { JSONSchema7, ToolSet, type ToolCallOptions } from "ai";
|
|
8
|
+
import { FC } from "react";
|
|
9
|
+
import z from "zod";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Converts from assistant-ui tool format to the AI SDK tool shape
|
|
@@ -21,9 +21,9 @@ export const toAISDKTools = (tools: Record<string, Tool>) => {
|
|
|
21
21
|
? z.toJSONSchema(tool.parameters)
|
|
22
22
|
: tool.parameters) as JSONSchema7,
|
|
23
23
|
},
|
|
24
|
-
])
|
|
25
|
-
)
|
|
26
|
-
}
|
|
24
|
+
]),
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Returns only frontend tools that are enabled
|
|
@@ -31,10 +31,10 @@ export const toAISDKTools = (tools: Record<string, Tool>) => {
|
|
|
31
31
|
export const getEnabledTools = (tools: Record<string, Tool>) => {
|
|
32
32
|
return Object.fromEntries(
|
|
33
33
|
Object.entries(tools).filter(
|
|
34
|
-
([, tool]) => !tool.disabled && tool.type !==
|
|
35
|
-
)
|
|
36
|
-
)
|
|
37
|
-
}
|
|
34
|
+
([, tool]) => !tool.disabled && tool.type !== "backend",
|
|
35
|
+
),
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* A frontend tool is a tool that is defined by the user and can be used in the chat.
|
|
@@ -42,37 +42,37 @@ export const getEnabledTools = (tools: Record<string, Tool>) => {
|
|
|
42
42
|
export type FrontendTool<TArgs extends Record<string, unknown>, TResult> = FC<
|
|
43
43
|
AssistantToolProps<TArgs, TResult>
|
|
44
44
|
> & {
|
|
45
|
-
unstable_tool: AssistantToolProps<TArgs, TResult
|
|
46
|
-
}
|
|
45
|
+
unstable_tool: AssistantToolProps<TArgs, TResult>;
|
|
46
|
+
};
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* Module-level approval config that gets set by ElementsProvider at runtime.
|
|
50
50
|
* This allows defineFrontendTool to check approval status during execute.
|
|
51
51
|
*/
|
|
52
52
|
let approvalConfig: {
|
|
53
|
-
helpers: ApprovalHelpers
|
|
54
|
-
requiresApproval: (toolName: string) => boolean
|
|
55
|
-
} | null = null
|
|
53
|
+
helpers: ApprovalHelpers;
|
|
54
|
+
requiresApproval: (toolName: string) => boolean;
|
|
55
|
+
} | null = null;
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Sets the approval configuration. Called by ElementsProvider.
|
|
59
59
|
*/
|
|
60
60
|
export function setFrontendToolApprovalConfig(
|
|
61
61
|
helpers: ApprovalHelpers,
|
|
62
|
-
toolsRequiringApproval: ToolsFilter
|
|
62
|
+
toolsRequiringApproval: ToolsFilter,
|
|
63
63
|
): void {
|
|
64
|
-
const requiresApproval = createRequiresApprovalFn(toolsRequiringApproval)
|
|
64
|
+
const requiresApproval = createRequiresApprovalFn(toolsRequiringApproval);
|
|
65
65
|
approvalConfig = {
|
|
66
66
|
helpers,
|
|
67
67
|
requiresApproval,
|
|
68
|
-
}
|
|
68
|
+
};
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
72
|
* Clears the approval configuration. Called when ElementsProvider unmounts.
|
|
73
73
|
*/
|
|
74
74
|
export function clearFrontendToolApprovalConfig(): void {
|
|
75
|
-
approvalConfig = null
|
|
75
|
+
approvalConfig = null;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
|
@@ -80,18 +80,18 @@ export function clearFrontendToolApprovalConfig(): void {
|
|
|
80
80
|
* Handles both array and function-based configurations.
|
|
81
81
|
*/
|
|
82
82
|
function createRequiresApprovalFn(
|
|
83
|
-
toolsRequiringApproval: ToolsFilter | undefined
|
|
83
|
+
toolsRequiringApproval: ToolsFilter | undefined,
|
|
84
84
|
): (toolName: string) => boolean {
|
|
85
85
|
if (!toolsRequiringApproval) {
|
|
86
|
-
return () => false
|
|
86
|
+
return () => false;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
if (typeof toolsRequiringApproval ===
|
|
90
|
-
return (toolName: string) => toolsRequiringApproval({ toolName })
|
|
89
|
+
if (typeof toolsRequiringApproval === "function") {
|
|
90
|
+
return (toolName: string) => toolsRequiringApproval({ toolName });
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
const approvalSet = new Set(toolsRequiringApproval)
|
|
94
|
-
return (toolName: string) => approvalSet.has(toolName)
|
|
93
|
+
const approvalSet = new Set(toolsRequiringApproval);
|
|
94
|
+
return (toolName: string) => approvalSet.has(toolName);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
/**
|
|
@@ -102,42 +102,46 @@ export const defineFrontendTool = <
|
|
|
102
102
|
TResult,
|
|
103
103
|
>(
|
|
104
104
|
tool: Tool,
|
|
105
|
-
name: string
|
|
105
|
+
name: string,
|
|
106
106
|
): FrontendTool<TArgs, TResult> => {
|
|
107
107
|
type ToolExecutionContext = Parameters<
|
|
108
|
-
NonNullable<Tool<Record<string, unknown>, void>[
|
|
109
|
-
>[1]
|
|
108
|
+
NonNullable<Tool<Record<string, unknown>, void>["execute"]>
|
|
109
|
+
>[1];
|
|
110
110
|
return makeAssistantTool({
|
|
111
111
|
...tool,
|
|
112
112
|
execute: async (args: TArgs, context: ToolExecutionContext) => {
|
|
113
113
|
// Check if this tool requires approval at runtime
|
|
114
114
|
if (approvalConfig?.requiresApproval(name)) {
|
|
115
|
-
const { helpers } = approvalConfig
|
|
116
|
-
const toolCallId = context.toolCallId ??
|
|
115
|
+
const { helpers } = approvalConfig;
|
|
116
|
+
const toolCallId = context.toolCallId ?? "";
|
|
117
117
|
|
|
118
118
|
// Check if already approved (user chose "Approve always" previously)
|
|
119
119
|
if (!helpers.isToolApproved(name)) {
|
|
120
|
-
const approved = await helpers.requestApproval(
|
|
120
|
+
const approved = await helpers.requestApproval(
|
|
121
|
+
name,
|
|
122
|
+
toolCallId,
|
|
123
|
+
args,
|
|
124
|
+
);
|
|
121
125
|
|
|
122
126
|
if (!approved) {
|
|
123
127
|
return {
|
|
124
128
|
content: [
|
|
125
129
|
{
|
|
126
|
-
type:
|
|
130
|
+
type: "text",
|
|
127
131
|
text: `Tool "${name}" execution was denied by the user. Please acknowledge this and continue without using this tool's result.`,
|
|
128
132
|
},
|
|
129
133
|
],
|
|
130
134
|
isError: true,
|
|
131
|
-
} as TResult
|
|
135
|
+
} as TResult;
|
|
132
136
|
}
|
|
133
137
|
}
|
|
134
138
|
}
|
|
135
139
|
|
|
136
|
-
return tool.execute?.(args, context)
|
|
140
|
+
return tool.execute?.(args, context);
|
|
137
141
|
},
|
|
138
142
|
toolName: name,
|
|
139
|
-
} as AssistantToolProps<TArgs, TResult>)
|
|
140
|
-
}
|
|
143
|
+
} as AssistantToolProps<TArgs, TResult>);
|
|
144
|
+
};
|
|
141
145
|
|
|
142
146
|
/**
|
|
143
147
|
* Helpers for requesting and tracking tool approval state.
|
|
@@ -146,10 +150,10 @@ export interface ApprovalHelpers {
|
|
|
146
150
|
requestApproval: (
|
|
147
151
|
toolName: string,
|
|
148
152
|
toolCallId: string,
|
|
149
|
-
args: unknown
|
|
150
|
-
) => Promise<boolean
|
|
151
|
-
isToolApproved: (toolName: string) => boolean
|
|
152
|
-
whitelistTool: (toolName: string) => void
|
|
153
|
+
args: unknown,
|
|
154
|
+
) => Promise<boolean>;
|
|
155
|
+
isToolApproved: (toolName: string) => boolean;
|
|
156
|
+
whitelistTool: (toolName: string) => void;
|
|
153
157
|
}
|
|
154
158
|
|
|
155
159
|
/**
|
|
@@ -158,10 +162,10 @@ export interface ApprovalHelpers {
|
|
|
158
162
|
export function wrapToolsWithApproval(
|
|
159
163
|
tools: ToolSet,
|
|
160
164
|
toolsRequiringApproval: ToolsFilter | undefined,
|
|
161
|
-
approvalHelpers: ApprovalHelpers
|
|
165
|
+
approvalHelpers: ApprovalHelpers,
|
|
162
166
|
): ToolSet {
|
|
163
167
|
if (!toolsRequiringApproval) {
|
|
164
|
-
return tools
|
|
168
|
+
return tools;
|
|
165
169
|
}
|
|
166
170
|
|
|
167
171
|
// Handle empty array case
|
|
@@ -169,20 +173,20 @@ export function wrapToolsWithApproval(
|
|
|
169
173
|
Array.isArray(toolsRequiringApproval) &&
|
|
170
174
|
toolsRequiringApproval.length === 0
|
|
171
175
|
) {
|
|
172
|
-
return tools
|
|
176
|
+
return tools;
|
|
173
177
|
}
|
|
174
178
|
|
|
175
|
-
const requiresApproval = createRequiresApprovalFn(toolsRequiringApproval)
|
|
179
|
+
const requiresApproval = createRequiresApprovalFn(toolsRequiringApproval);
|
|
176
180
|
|
|
177
181
|
return Object.fromEntries(
|
|
178
182
|
Object.entries(tools).map(([name, tool]) => {
|
|
179
183
|
if (!requiresApproval(name)) {
|
|
180
|
-
return [name, tool]
|
|
184
|
+
return [name, tool];
|
|
181
185
|
}
|
|
182
186
|
|
|
183
|
-
const originalExecute = tool.execute
|
|
187
|
+
const originalExecute = tool.execute;
|
|
184
188
|
if (!originalExecute) {
|
|
185
|
-
return [name, tool]
|
|
189
|
+
return [name, tool];
|
|
186
190
|
}
|
|
187
191
|
|
|
188
192
|
return [
|
|
@@ -192,36 +196,36 @@ export function wrapToolsWithApproval(
|
|
|
192
196
|
execute: async (args: unknown, options?: ToolCallOptions) => {
|
|
193
197
|
const opts = (options ?? {}) as Parameters<
|
|
194
198
|
typeof originalExecute
|
|
195
|
-
>[1]
|
|
199
|
+
>[1];
|
|
196
200
|
// Extract toolCallId from options
|
|
197
201
|
const toolCallId =
|
|
198
|
-
(opts as { toolCallId?: string }).toolCallId ??
|
|
202
|
+
(opts as { toolCallId?: string }).toolCallId ?? "";
|
|
199
203
|
|
|
200
204
|
// Check if already approved (user chose "Approve always" previously)
|
|
201
205
|
if (approvalHelpers.isToolApproved(name)) {
|
|
202
206
|
return originalExecute(
|
|
203
207
|
args,
|
|
204
|
-
opts as Parameters<typeof originalExecute>[1]
|
|
205
|
-
)
|
|
208
|
+
opts as Parameters<typeof originalExecute>[1],
|
|
209
|
+
);
|
|
206
210
|
}
|
|
207
211
|
|
|
208
212
|
// Request approval using the actual toolCallId from the stream
|
|
209
213
|
const approved = await approvalHelpers.requestApproval(
|
|
210
214
|
name,
|
|
211
215
|
toolCallId,
|
|
212
|
-
args
|
|
213
|
-
)
|
|
216
|
+
args,
|
|
217
|
+
);
|
|
214
218
|
|
|
215
219
|
if (!approved) {
|
|
216
220
|
return {
|
|
217
221
|
content: [
|
|
218
222
|
{
|
|
219
|
-
type:
|
|
223
|
+
type: "text",
|
|
220
224
|
text: `Tool "${name}" execution was denied by the user. Please acknowledge this and continue without using this tool's result.`,
|
|
221
225
|
},
|
|
222
226
|
],
|
|
223
227
|
isError: true,
|
|
224
|
-
}
|
|
228
|
+
};
|
|
225
229
|
}
|
|
226
230
|
|
|
227
231
|
// Note: Tool is marked as approved via the UI when user clicks "Approve always"
|
|
@@ -229,11 +233,11 @@ export function wrapToolsWithApproval(
|
|
|
229
233
|
|
|
230
234
|
return originalExecute(
|
|
231
235
|
args,
|
|
232
|
-
opts as Parameters<typeof originalExecute>[1]
|
|
233
|
-
)
|
|
236
|
+
opts as Parameters<typeof originalExecute>[1],
|
|
237
|
+
);
|
|
234
238
|
},
|
|
235
239
|
},
|
|
236
|
-
]
|
|
237
|
-
})
|
|
238
|
-
) as ToolSet
|
|
240
|
+
];
|
|
241
|
+
}),
|
|
242
|
+
) as ToolSet;
|
|
239
243
|
}
|