diff --git a/.changeset/many-pumpkins-double.md b/.changeset/many-pumpkins-double.md
new file mode 100644
index 00000000000..e46b7ab49be
--- /dev/null
+++ b/.changeset/many-pumpkins-double.md
@@ -0,0 +1,10 @@
+---
+'@clerk/agent-toolkit': patch
+---
+
+Introduce `@clerk/agent-toolkit` package. The Clerk Agent Toolkit enables popular agent frameworks, including Vercel's AI SDK and LangChain, to integrate with Clerk using tools (also known as function calling).
+
+This package exposes a subset of Clerk's functionality to agent frameworks, allowing you to build powerful agentic systems capable of managing users, user data, organizations, and more.
+
+**Please note:** All relevant information and instructions on how to set it up can be found in the package's README. It's an early developer preview and can't be considered stable yet.
+
diff --git a/.changeset/metal-gorillas-fetch.md b/.changeset/metal-gorillas-fetch.md
new file mode 100644
index 00000000000..94396a997e9
--- /dev/null
+++ b/.changeset/metal-gorillas-fetch.md
@@ -0,0 +1,5 @@
+---
+'@clerk/backend': patch
+---
+
+Adds an internal `raw()` getter method to the `User` resource that returns the raw JSON payload of the request that instantiated the resource.
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 0a153507ba3..c60c1ed50e5 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -65,3 +65,6 @@ actions:
integration:
- integration/**
+
+agent-toolkit:
+ - packages/agent-toolkit/**
diff --git a/packages/agent-toolkit/LICENSE b/packages/agent-toolkit/LICENSE
new file mode 100644
index 00000000000..49e46cae156
--- /dev/null
+++ b/packages/agent-toolkit/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 Clerk, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/agent-toolkit/README.md b/packages/agent-toolkit/README.md
new file mode 100644
index 00000000000..dc2788ccaf4
--- /dev/null
+++ b/packages/agent-toolkit/README.md
@@ -0,0 +1,230 @@
+
+
+
+
+
+
+
+
+
@clerk/agent-toolkit
+
+
+
+
+[](https://clerk.com/discord)
+[](https://clerk.com/docs?utm_source=github&utm_medium=clerk_agent_toolkit)
+[](https://twitter.com/intent/follow?screen_name=ClerkDev)
+
+[Changelog](https://github.com/clerk/javascript/blob/main/packages/agent-toolkit/CHANGELOG.md)
+·
+[Report a Bug](https://github.com/clerk/javascript/issues/new?assignees=&labels=needs-triage&projects=&template=BUG_REPORT.yml)
+·
+[Request a Feature](https://feedback.clerk.com/roadmap)
+·
+[Get Help](https://clerk.com/contact/support?utm_source=github&utm_medium=clerk_agent_toolkit)
+
+
+
+> [!IMPORTANT]
+>
+> Agent behavior is typically non-deterministic. Ensure you thoroughly test your integration and evaluate your application's performance. Additionally, consider scoping this toolkit's tools to specific users to limit resource access.
+>
+> If your app's code path is predetermined, it's always preferable to call APIs directly instead of using agents and tool calling.
+>
+> This SDK is recommended for testing purposes only unless you are confident in the agent's behavior and have implemented necessary security measures such as guardrails and best practices.
+
+## Table of Contents
+
+1. [Getting Started](#getting-started)
+2. [Public APIs](#public-apis)
+3. [Use with Vercel's AI SDK](#use-with-vercels-ai-sdk)
+4. [Use with Langchain](#use-with-langchain)
+5. [Advanced Usage](#advanced-usage)
+6. [Support](#support)
+
+## Getting Started
+
+Use this SDK to integrate [Clerk](https://clerk.com/?utm_source=github&utm_medium=clerk_agent_toolkit) into your agentic workflows. The Clerk Agent Toolkit enables popular agent frameworks, including Vercel's AI SDK and LangChain, to integrate with Clerk using tools (also known as function calling).
+
+This package exposes a subset of Clerk's functionality to agent frameworks, allowing you to build powerful agentic systems capable of managing users, user data, organizations, and more.
+
+## Public APIs
+
+The Clerk Agent Toolkit package provides two main import paths:
+
+- `@clerk/agent-toolkit/ai-sdk`: Helpers for integrating with Vercel's AI SDK.
+- `@clerk/agent-toolkit/langchain`: Helpers for integrating with Langchain.
+
+The toolkit offers the same tools and core APIs across frameworks, but their public interfaces may vary slightly to align with each framework's design:
+
+- `createClerkToolkit(options)`: Instantiates a new Clerk toolkit.
+- `toolkit.injectSessionClaims(systemPrompt)`: Injects session claims (`userId`, `sessionId`, `orgId`, etc.) into the system prompt, making them accessible to the AI model.
+- `toolkit.users()`: Provides tools for managing users.
+- `toolkit.organizations()`: Provides tools for managing organizations.
+- `toolkit.allTools()`: Returns all available tools.
+- `toolkit.toolMap()`: **(Langchain only)** Returns an object mapping available tools, useful for calling tools by name.
+
+For more details on each tool, refer to the framework-specific directories or the [Clerk Backend API documentation](https://clerk.com/docs/reference/backend-api).
+
+### Prerequisites
+
+- `ai-sdk`: `"^3.4.7 || ^4.0.0"`, or `langchain`: `"^0.3.6"`
+- An existing Clerk application. [Create your account for free](https://dashboard.clerk.com/sign-up?utm_source=github&utm_medium=clerk_agent_toolkit).
+- An API key for an AI model compatible with Langchain
+
+### Example Repository
+
+- [Clerk AI SDK Example](https://github.com/clerk/agent-toolkit-example)
+
+## Using Vercel's AI SDK
+
+1. Install the Clerk Agent Toolkit package:
+
+ ```shell
+ npm install @clerk/agent-toolkit
+ ```
+
+2. Set the Clerk secret key as an environment variable in your project. Ensure you also configure any required LLM model keys.
+
+ ```
+ CLERK_SECRET_KEY=sk_
+ ```
+
+3. Import the helper from the `/ai-sdk` path, instantiate a new Clerk `toolkit`, and use it in your agent function:
+
+```typescript
+// Import the helper from the ai-sdk path
+import { createClerkToolkit } from '@clerk/agent-toolkit/ai-sdk';
+import { openai } from '@ai-sdk/openai';
+import { streamText } from 'ai';
+import { auth } from '@clerk/nextjs/server';
+import { systemPrompt } from '@/lib/ai/prompts';
+
+export const maxDuration = 30;
+
+export async function POST(req: Request) {
+ const { messages } = await req.json();
+ // Optional - get the userId from the request
+ const { userId } = await auth.protect();
+
+ // Instantiate a new Clerk toolkit
+ // Optional - scope the toolkit to a specific user
+ const toolkit = await createClerkToolkit({ context: { userId } });
+
+ const result = streamText({
+ model: openai('gpt-4o'),
+ messages,
+ // Optional - inject session claims into the system prompt
+ system: toolkit.injectSessionClaims(systemPrompt),
+ tools: {
+ // Provide the tools you want to use
+ ...toolkit.users(),
+ ...toolkit.organizations(),
+ },
+ });
+
+ return result.toDataStreamResponse();
+}
+```
+
+## Using Langchain
+
+1. Install the Clerk Agent Toolkit package:
+
+ ```shell
+ npm install @clerk/agent-toolkit
+ ```
+
+2. Set the Clerk secret key as an environment variable:
+
+ ```shell
+ CLERK_SECRET_KEY=sk_
+ ```
+
+3. Import the helper from the `/langchain` path, instantiate a new Clerk `toolkit`, and use it in your agent function:
+
+```typescript
+// Import the helper from the langchain path
+import { createClerkToolkit } from '@clerk/agent-toolkit/langchain';
+import { ChatOpenAI } from '@langchain/openai';
+import { auth } from '@clerk/nextjs/server';
+import { HumanMessage, SystemMessage } from '@langchain/core/messages';
+import { LangChainAdapter } from 'ai';
+import { systemPrompt } from '@/lib/ai/prompts';
+
+export const maxDuration = 30;
+
+export async function POST(req: Request) {
+ const { prompt } = await req.json();
+ const { userId } = await auth.protect();
+
+ // Instantiate a new Clerk toolkit
+ // Optional - scope the toolkit to a specific user
+ const toolkit = await createClerkToolkit({ context: { userId } });
+
+ const model = new ChatOpenAI({ model: 'gpt-4o', temperature: 0 });
+
+ // Bind the tools you want to use to the model
+ const modelWithTools = model.bindTools(toolkit.users());
+
+ const messages = [new SystemMessage(toolkit.injectSessionClaims(systemPrompt)), new HumanMessage(prompt)];
+ const aiMessage = await modelWithTools.invoke(messages);
+ messages.push(aiMessage);
+
+ for (const toolCall of aiMessage.tool_calls || []) {
+ // Call the selected tool
+ const selectedTool = toolkit.toolMap()[toolCall.name];
+ const toolMessage = await selectedTool.invoke(toolCall);
+ messages.push(toolMessage);
+ }
+
+ // To simplify the setup, this example uses the ai-sdk langchain adapter
+ // to stream the results back to the /langchain page.
+ // For more details, see: https://sdk.vercel.ai/providers/adapters/langchain
+ const stream = await modelWithTools.stream(messages);
+ return LangChainAdapter.toDataStreamResponse(stream);
+}
+```
+
+## Advanced Usage
+
+### Using a Custom `clerkClient`
+
+If you need to set the Clerk secret key dynamically or use different Clerk instances, pass a custom `clerkClient`. Install `@clerk/backend` into your project and call the `createClerkClient` function:
+
+```typescript
+import { createClerkToolkit } from '@clerk/agent-toolkit/ai-sdk';
+import { createClerkClient } from '@clerk/backend';
+
+export async function POST(req: Request) {
+ // Create a new Clerk client
+ const clerkClient = createClerkClient({ secretKey: 'sk_' });
+
+ // Instantiate a new Clerk toolkit with the custom client
+ const toolkit = await createClerkToolkit({ clerkClient });
+
+ // Use the toolkit as usual
+ const result = streamText({
+ model: openai('gpt-4o'),
+ messages,
+ tools: toolkit.users(),
+ });
+}
+```
+
+## Support
+
+You can get in touch with us in any of the following ways:
+
+- Join our official community [Discord server](https://clerk.com/discord)
+- On [our support page](https://clerk.com/contact/support?utm_source=github&utm_medium=clerk_agent_toolkit)
+
+## Contributing
+
+We're open to all community contributions! If you'd like to contribute in any way, please read [our contribution guidelines](https://github.com/clerk/javascript/blob/main/docs/CONTRIBUTING.md) and [code of conduct](https://github.com/clerk/javascript/blob/main/docs/CODE_OF_CONDUCT.md).
+
+## License
+
+This project is licensed under the **MIT license**.
+
+See [LICENSE](https://github.com/clerk/javascript/blob/main/packages/agent-toolkit/LICENSE) for more information.
diff --git a/packages/agent-toolkit/package.json b/packages/agent-toolkit/package.json
new file mode 100644
index 00000000000..4dbe07f5960
--- /dev/null
+++ b/packages/agent-toolkit/package.json
@@ -0,0 +1,67 @@
+{
+ "name": "@clerk/agent-toolkit",
+ "version": "0.0.1",
+ "private": true,
+ "description": "Clerk Toolkit for AI Agents",
+ "homepage": "https://clerk.com/",
+ "bugs": {
+ "url": "https://github.com/clerk/javascript/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/clerk/javascript.git",
+ "directory": "packages/agent-toolkit"
+ },
+ "license": "MIT",
+ "author": "Clerk",
+ "sideEffects": false,
+ "type": "module",
+ "exports": {
+ "./langchain": {
+ "types": "./dist/langchain/index.d.ts",
+ "import": "./dist/langchain/index.js"
+ },
+ "./ai-sdk": {
+ "types": "./dist/ai-sdk/index.d.ts",
+ "import": "./dist/ai-sdk/index.js"
+ }
+ },
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "tsup --env.NODE_ENV production",
+ "clean": "rimraf ./dist",
+ "dev": "tsup --watch",
+ "lint": "eslint src",
+ "lint:attw": "attw --pack . --ignore-rules cjs-resolves-to-esm no-resolution",
+ "lint:publint": "publint"
+ },
+ "dependencies": {
+ "@clerk/backend": "workspace:^",
+ "@clerk/shared": "workspace:^",
+ "@clerk/types": "workspace:^",
+ "zod": "^3.24.1"
+ },
+ "devDependencies": {
+ "esbuild-plugin-file-path-extensions": "^2.1.4"
+ },
+ "peerDependencies": {
+ "@langchain/core": "^0.3.6",
+ "ai": "^3.4.7 || ^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@langchain/core": {
+ "optional": true
+ },
+ "ai": {
+ "optional": true
+ }
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/agent-toolkit/src/ai-sdk/adapter.ts b/packages/agent-toolkit/src/ai-sdk/adapter.ts
new file mode 100644
index 00000000000..646e35c4e0a
--- /dev/null
+++ b/packages/agent-toolkit/src/ai-sdk/adapter.ts
@@ -0,0 +1,15 @@
+import type { Tool } from 'ai';
+import { tool } from 'ai';
+
+import type { SdkAdapter } from '../lib/types';
+
+/**
+ * Converts a `ClerkTool` to an AI SDK `Tool`.
+ */
+export const adapter: SdkAdapter = (clerkClient, context, clerkTool) => {
+ return tool({
+ description: clerkTool.description,
+ parameters: clerkTool.parameters,
+ execute: clerkTool.bindRunnable(clerkClient, context),
+ });
+};
diff --git a/packages/agent-toolkit/src/ai-sdk/index.ts b/packages/agent-toolkit/src/ai-sdk/index.ts
new file mode 100644
index 00000000000..8901551349a
--- /dev/null
+++ b/packages/agent-toolkit/src/ai-sdk/index.ts
@@ -0,0 +1,48 @@
+import { clerkClient as _clerkClient } from '../lib/clerk-client';
+import { defaultToolkitContext } from '../lib/constants';
+import { injectSessionClaims } from '../lib/inject-session-claims';
+import { flatTools, tools } from '../lib/tools';
+import type { ClerkToolkitBase, CreateClerkToolkitParams } from '../lib/types';
+import { shallowTransform } from '../lib/utils';
+import { adapter } from './adapter';
+
+export type ClerkToolkit = ClerkToolkitBase & {
+ /**
+ * Returns an object with all the tools from all categories in the Clerk toolkit.
+ *
+ * Most LLM providers recommend that for each LLM call, the number of available tools should be kept to a minimum,
+ * usually around 10-20 tools. This increases the LLM's accuracy when picking the right tool.
+ *
+ * As a result, we also recommend to use the fine-grained tool categories, for example, `toolkit.users` instead.
+ */
+ allTools: () => { [key in keyof typeof flatTools]: ReturnType };
+} & {
+ [key in keyof typeof tools]: () => { [tool in keyof (typeof tools)[key]]: ReturnType };
+};
+
+/**
+ * Creates a Clerk toolkit with the given parameters.
+ * The toolkit is a collection of tools that can be used to augment the AI's capabilities,
+ * For more details, refer to the [package's docs](https://github.com/clerk/javascript/blob/main/packages/agent-toolkit/README.md).
+ */
+export const createClerkToolkit = async (params: CreateClerkToolkitParams = {}): Promise => {
+ const clerkClient = params.clerkClient || _clerkClient;
+ const context = params.context || defaultToolkitContext;
+
+ const adaptedTools = shallowTransform(tools, toolSection => {
+ return () =>
+ shallowTransform(toolSection, t => {
+ return adapter(clerkClient, context, t);
+ });
+ });
+
+ const allTools = () => {
+ return shallowTransform(flatTools, t => adapter(clerkClient, context, t));
+ };
+
+ return {
+ ...adaptedTools,
+ allTools,
+ injectSessionClaims: injectSessionClaims(context),
+ };
+};
diff --git a/packages/agent-toolkit/src/global.d.ts b/packages/agent-toolkit/src/global.d.ts
new file mode 100644
index 00000000000..1ae75219e34
--- /dev/null
+++ b/packages/agent-toolkit/src/global.d.ts
@@ -0,0 +1,6 @@
+declare global {
+ const PACKAGE_NAME: string;
+ const PACKAGE_VERSION: string;
+}
+
+export {};
diff --git a/packages/agent-toolkit/src/langchain/adapter.ts b/packages/agent-toolkit/src/langchain/adapter.ts
new file mode 100644
index 00000000000..77b5866b795
--- /dev/null
+++ b/packages/agent-toolkit/src/langchain/adapter.ts
@@ -0,0 +1,16 @@
+import type { StructuredTool } from '@langchain/core/tools';
+import { tool } from '@langchain/core/tools';
+
+import type { SdkAdapter } from '../lib/types';
+
+/**
+ * Converts a `ClerkTool` to a LangChain `StructuredTool`.
+ * For more details, take a look at the LangChain docs https://js.langchain.com/docs/how_to/custom_tools
+ */
+export const adapter: SdkAdapter = (clerkClient, context, clerkTool) => {
+ return tool(clerkTool.bindRunnable(clerkClient, context), {
+ name: clerkTool.name,
+ description: clerkTool.description,
+ schema: clerkTool.parameters,
+ });
+};
diff --git a/packages/agent-toolkit/src/langchain/index.ts b/packages/agent-toolkit/src/langchain/index.ts
new file mode 100644
index 00000000000..0882a2e0d3e
--- /dev/null
+++ b/packages/agent-toolkit/src/langchain/index.ts
@@ -0,0 +1,53 @@
+import { clerkClient as _clerkClient } from '../lib/clerk-client';
+import { defaultToolkitContext } from '../lib/constants';
+import { injectSessionClaims } from '../lib/inject-session-claims';
+import { flatTools, tools } from '../lib/tools';
+import type { ClerkToolkitBase, CreateClerkToolkitParams } from '../lib/types';
+import { shallowTransform } from '../lib/utils';
+import { adapter } from './adapter';
+
+export type ClerkToolkit = ClerkToolkitBase & {
+ /**
+ * Returns an array containing all tools from all categories in the Clerk toolkit.
+ *
+ * Most LLM providers recommend that for each LLM call, the number of available tools should be kept to a minimum,
+ * usually around 10-20 tools. This increases the LLM's accuracy when picking the right tool.
+ *
+ * As a result, we also recommend to use the fine-grained tool categories, for example, `toolkit.users` instead.
+ */
+ allTools: () => Array>;
+ /**
+ * Returns an object with all the tools from all categories in the Clerk toolkit.
+ * Useful when using tool calling with Langchain messages (e.g. `tool_calls`).
+ */
+ toolMap: () => { [key in keyof typeof flatTools]: ReturnType };
+} & {
+ [key in keyof typeof tools]: () => Array>;
+};
+
+/**
+ * Creates a Clerk toolkit with the given parameters.
+ * The toolkit is a collection of tools that can be used to augment the AI's capabilities,
+ * For more details, refer to the [package's docs](https://github.com/clerk/javascript/blob/main/packages/agent-toolkit/README.md).
+ */
+export const createClerkToolkit = async (params: CreateClerkToolkitParams = {}): Promise => {
+ const clerkClient = params.clerkClient || _clerkClient;
+ const context = params.context || defaultToolkitContext;
+
+ const adaptedTools = shallowTransform(tools, toolSection => {
+ return () => Object.values(toolSection).map(t => adapter(clerkClient, context, t));
+ });
+
+ const allTools = () => {
+ return Object.values(flatTools).map(t => adapter(clerkClient, context, t));
+ };
+
+ const toolMap = shallowTransform(flatTools, t => adapter(clerkClient, context, t));
+
+ return {
+ ...adaptedTools,
+ allTools,
+ toolMap: () => toolMap,
+ injectSessionClaims: injectSessionClaims(context),
+ };
+};
diff --git a/packages/agent-toolkit/src/lib/clerk-client.ts b/packages/agent-toolkit/src/lib/clerk-client.ts
new file mode 100644
index 00000000000..4a5cf365380
--- /dev/null
+++ b/packages/agent-toolkit/src/lib/clerk-client.ts
@@ -0,0 +1,23 @@
+import { createClerkClient } from '@clerk/backend';
+import { apiUrlFromPublishableKey } from '@clerk/shared/apiUrlFromPublishableKey';
+import { getEnvVariable } from '@clerk/shared/getEnvVariable';
+
+const API_VERSION = getEnvVariable('CLERK_API_VERSION') || 'v1';
+const SECRET_KEY = getEnvVariable('CLERK_SECRET_KEY') || '';
+const PUBLISHABLE_KEY = getEnvVariable('CLERK_PUBLISHABLE_KEY') || '';
+const API_URL = getEnvVariable('CLERK_API_URL') || apiUrlFromPublishableKey(PUBLISHABLE_KEY);
+const JWT_KEY = getEnvVariable('CLERK_JWT_KEY') || '';
+const SDK_METADATA = {
+ name: PACKAGE_NAME,
+ version: PACKAGE_VERSION,
+ environment: getEnvVariable('NODE_ENV'),
+};
+
+export const clerkClient = createClerkClient({
+ secretKey: SECRET_KEY,
+ apiUrl: API_URL,
+ apiVersion: API_VERSION,
+ jwtKey: JWT_KEY,
+ userAgent: `${PACKAGE_NAME}@${PACKAGE_VERSION}`,
+ sdkMetadata: SDK_METADATA,
+});
diff --git a/packages/agent-toolkit/src/lib/clerk-tool.ts b/packages/agent-toolkit/src/lib/clerk-tool.ts
new file mode 100644
index 00000000000..8c82b5c6d82
--- /dev/null
+++ b/packages/agent-toolkit/src/lib/clerk-tool.ts
@@ -0,0 +1,55 @@
+import type { ClerkClient } from '@clerk/backend';
+import type { ZodObject } from 'zod';
+
+import type { ToolkitContext } from './types';
+
+export interface ClerkTool {
+ /**
+ * The name of the tool. This can be used to reference the tool in the code.
+ * A descriptive LLM-readable string.
+ */
+ name: string;
+ /**
+ * A descriptive prompt explaining the tool's purpose, usage and input parameters.
+ * Ths is intended to be used by the underlying LLM.
+ * To avoid duplication, the description can reference the parameters by using the `$parameters` prefix.
+ */
+ description: string;
+ /**
+ * The Zod schema for the input parameters of the tool
+ */
+ parameters: ZodObject;
+ /**
+ * The actual implementation of the tool.
+ */
+ bindRunnable: (clerkClient: ClerkClient, context: ToolkitContext) => (input: any) => Promise;
+}
+
+const trimLines = (text: string) =>
+ text
+ .split('\n')
+ .map(l => l.trim())
+ .filter(Boolean)
+ .join('\n');
+
+export const ClerkTool = (params: ClerkTool): ClerkTool => {
+ const schemaEntries = Object.entries(params.parameters.shape);
+ const args =
+ schemaEntries.length === 0
+ ? 'Takes no arguments'
+ : Object.entries(params.parameters.shape)
+ .map(([key, value]) => {
+ return `- ${key}: ${(value as any).description || ''}`;
+ })
+ .join('\n');
+
+ const description = trimLines(`
+ Tool name:
+ ${params.name}
+ Description:
+ ${params.description}.
+ Arguments:
+ ${args}
+ `);
+ return { ...params, description };
+};
diff --git a/packages/agent-toolkit/src/lib/constants.ts b/packages/agent-toolkit/src/lib/constants.ts
new file mode 100644
index 00000000000..871ea9fdcca
--- /dev/null
+++ b/packages/agent-toolkit/src/lib/constants.ts
@@ -0,0 +1,5 @@
+import type { ToolkitContext } from './types';
+
+export const defaultToolkitContext: ToolkitContext = {
+ allowPrivateMetadata: false,
+};
diff --git a/packages/agent-toolkit/src/lib/inject-session-claims.ts b/packages/agent-toolkit/src/lib/inject-session-claims.ts
new file mode 100644
index 00000000000..db31cf5892e
--- /dev/null
+++ b/packages/agent-toolkit/src/lib/inject-session-claims.ts
@@ -0,0 +1,18 @@
+import type { ToolkitContext } from './types';
+
+export const injectSessionClaims = (context: ToolkitContext) => (prompt: string) => {
+ if (!context.sessionClaims && !context.userId) {
+ return prompt;
+ }
+
+ const lines = [];
+ lines.push(`# Signed-in user info:`);
+ lines.push(
+ context.sessionClaims?.sub || context.userId
+ ? `Your userId is ${context.sessionClaims?.sub || context.userId}`
+ : '',
+ );
+ lines.push(context.sessionClaims?.sid ? `Your sessionId is ${context.sessionClaims?.sid}` : '');
+ lines.push(prompt);
+ return lines.filter(Boolean).join('\n');
+};
diff --git a/packages/agent-toolkit/src/lib/tools/index.ts b/packages/agent-toolkit/src/lib/tools/index.ts
new file mode 100644
index 00000000000..a7714fe5291
--- /dev/null
+++ b/packages/agent-toolkit/src/lib/tools/index.ts
@@ -0,0 +1,15 @@
+import { users } from './user-tools';
+
+export const tools = {
+ /**
+ * Tools for interacting with users.
+ * This is a wrapper around the `clerkClient.users` API.
+ * For more information, see the [Clerk API documentation](https://clerk.com/docs/reference/backend-api/tag/Users).
+ */
+ users,
+} as const;
+
+// Just to help with types later on
+export const flatTools = {
+ ...users,
+};
diff --git a/packages/agent-toolkit/src/lib/tools/user-tools.ts b/packages/agent-toolkit/src/lib/tools/user-tools.ts
new file mode 100644
index 00000000000..76676aba762
--- /dev/null
+++ b/packages/agent-toolkit/src/lib/tools/user-tools.ts
@@ -0,0 +1,52 @@
+import { z } from 'zod';
+
+import { ClerkTool } from '../clerk-tool';
+import { prunePrivateData } from '../utils';
+
+const getUserId = ClerkTool({
+ name: 'getUserId',
+ description: `
+ Get the userId of the current user if signed in, otherwise return null.
+ `,
+ parameters: z.object({}),
+ bindRunnable: (clerkClient, context) => () => {
+ return Promise.resolve(context.userId || null);
+ },
+});
+
+const getUser = ClerkTool({
+ name: 'getUser',
+ description: `
+ Retrieves information about a single User by their userId, if the userId is valid.
+ `,
+ parameters: z.object({
+ userId: z.string().describe('(string): The userId of the User to retrieve'),
+ }),
+ bindRunnable: (clerkClient, context) => async params => {
+ const res = await clerkClient.users.getUser(context.userId || params.userId);
+ return prunePrivateData(context, res.raw);
+ },
+});
+
+const updateUserPublicMetadata = ClerkTool({
+ name: 'updateUserPublicMetadata',
+ description: `
+ Updates the public metadata associated with the specified user by merging existing values with the provided parameters.
+ A "deep" merge will be performed - "deep" means that any nested JSON objects will be merged as well. You can remove metadata keys at any level by setting their value to null.
+ `,
+ parameters: z.object({
+ userId: z.string().describe('(string): The userId of the User to update'),
+ metadata: z.record(z.string(), z.any()).describe('(Record): The new public metadata to set'),
+ }),
+ bindRunnable: (clerkClient, context) => async params => {
+ const { userId, metadata } = params;
+ const res = await clerkClient.users.updateUserMetadata(context.userId || userId, { publicMetadata: metadata });
+ return prunePrivateData(context, res.raw);
+ },
+});
+
+export const users = {
+ getUserId,
+ getUser,
+ updateUserPublicMetadata,
+} as const;
diff --git a/packages/agent-toolkit/src/lib/types.ts b/packages/agent-toolkit/src/lib/types.ts
new file mode 100644
index 00000000000..75b4a41cb4c
--- /dev/null
+++ b/packages/agent-toolkit/src/lib/types.ts
@@ -0,0 +1,43 @@
+import type { ClerkClient } from '@clerk/backend';
+import type { SignedInAuthObject } from '@clerk/backend/internal';
+
+import type { ClerkTool } from './clerk-tool';
+
+export type SdkAdapter = (clerkClient: ClerkClient, context: ToolkitContext, clerkTool: ClerkTool) => T;
+
+export type ToolkitContext = {
+ /**
+ * The userId of the currently signed-in user.
+ * This is used to scope the tools to a specific user.
+ */
+ userId?: string;
+ /**
+ * All JWT claims of the current session.
+ * This is used to scope the tools to a specific session or to make the LLM
+ * aware of the sessions details.
+ */
+ sessionClaims?: SignedInAuthObject['sessionClaims'];
+ /**
+ * Whether to explicitly allow private metadata access.
+ * By default, private metadata are pruned from all resources, before
+ * the resources become available to the LLM. This is important to help avoid
+ * leaking sensitive information to carefully crafted user prompts.
+ *
+ * @default false
+ */
+ allowPrivateMetadata?: boolean;
+};
+
+export type CreateClerkToolkitParams = {
+ clerkClient?: ClerkClient;
+ context?: ToolkitContext;
+};
+
+export type ClerkToolkitBase = {
+ /**
+ * Augment the system prompt with data about the current session.
+ * This usually contains the userId, the sessionId, the organizationId, etc.
+ * This property uses the data passed to `createClerkToolkit`.
+ */
+ injectSessionClaims: (prompt: string) => string;
+};
diff --git a/packages/agent-toolkit/src/lib/utils.ts b/packages/agent-toolkit/src/lib/utils.ts
new file mode 100644
index 00000000000..2400130332f
--- /dev/null
+++ b/packages/agent-toolkit/src/lib/utils.ts
@@ -0,0 +1,66 @@
+import type { ToolkitContext } from './types';
+
+// A helper type that maps T to a new type with every leaf replaced by R.
+type DeepTransform =
+ T extends Array ? DeepTransform[] : T extends object ? { [K in keyof T]: DeepTransform } : R;
+
+/**
+ * Recursively transforms every value in an object (or array) by applying a function.
+ *
+ * The result has the same structure as the input object,
+ but each leaf value is replaced with the return type R of the transform function.
+ */
+export function deepTransform(input: T, transformFn: (value: any) => R): DeepTransform {
+ if (Array.isArray(input)) {
+ // Recursively transform each element of the array.
+ return input.map(item => deepTransform(item, transformFn)) as DeepTransform;
+ } else if (input !== null && typeof input === 'object') {
+ // Recursively transform each property of the object.
+ const result: any = {};
+ for (const key in input) {
+ if (Object.prototype.hasOwnProperty.call(input, key)) {
+ result[key] = deepTransform((input as any)[key], transformFn);
+ }
+ }
+ return result as DeepTransform;
+ } else {
+ // For non-objects, apply the transform function.
+ return transformFn(input) as DeepTransform;
+ }
+}
+
+/**
+ * A mapped type that replaces every property in T with type R.
+ */
+type ShallowTransform = {
+ [K in keyof T]: R;
+};
+
+/**
+ * Transforms the top-level values of an object using a transformation function.
+ *
+ */
+export function shallowTransform(
+ input: T,
+ transformFn: (value: T[keyof T]) => R,
+): ShallowTransform {
+ const result = {} as ShallowTransform;
+ for (const key in input) {
+ if (Object.prototype.hasOwnProperty.call(input, key)) {
+ const typedKey = key as keyof T;
+ result[typedKey] = transformFn(input[typedKey]);
+ }
+ }
+ return result;
+}
+
+export const prunePrivateData = (context: ToolkitContext, o?: Record | null) => {
+ if (context.allowPrivateMetadata) {
+ return o;
+ }
+
+ if (o && o.private_metadata) {
+ delete o.private_metadata;
+ }
+ return o;
+};
diff --git a/packages/agent-toolkit/tsconfig.json b/packages/agent-toolkit/tsconfig.json
new file mode 100644
index 00000000000..675dd819dd5
--- /dev/null
+++ b/packages/agent-toolkit/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "moduleResolution": "Bundler",
+ "module": "ESNext",
+ "sourceMap": false,
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "allowJs": true,
+ "target": "ES2022",
+ "emitDeclarationOnly": true,
+ "declaration": true,
+ "declarationMap": true,
+ "outDir": "dist",
+ "resolveJsonModule": true,
+ "declarationDir": "dist/types"
+ },
+ "include": ["src"]
+}
diff --git a/packages/agent-toolkit/tsconfig.test.json b/packages/agent-toolkit/tsconfig.test.json
new file mode 100644
index 00000000000..5635d6cd1b7
--- /dev/null
+++ b/packages/agent-toolkit/tsconfig.test.json
@@ -0,0 +1,6 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "sourceMap": true
+ }
+}
diff --git a/packages/agent-toolkit/tsup.config.ts b/packages/agent-toolkit/tsup.config.ts
new file mode 100644
index 00000000000..d4517b22845
--- /dev/null
+++ b/packages/agent-toolkit/tsup.config.ts
@@ -0,0 +1,25 @@
+import { defineConfig } from 'tsup';
+import { esbuildPluginFilePathExtensions } from 'esbuild-plugin-file-path-extensions';
+
+import { name, version } from './package.json';
+
+export default defineConfig(overrideOptions => {
+ const isProd = overrideOptions.env?.NODE_ENV === 'production';
+
+ return {
+ entry: ['src/ai-sdk/index.ts', 'src/langchain/index.ts'],
+ dts: true,
+ // onSuccess: 'tsc',
+ minify: false,
+ clean: true,
+ sourcemap: true,
+ format: ['esm'],
+ // legacyOutput: true,
+ define: {
+ PACKAGE_NAME: `"${name}"`,
+ PACKAGE_VERSION: `"${version}"`,
+ __DEV__: `${!isProd}`,
+ },
+ esbuildPlugins: [esbuildPluginFilePathExtensions({ esmExtension: 'js' })],
+ };
+});
diff --git a/packages/backend/src/api/resources/User.ts b/packages/backend/src/api/resources/User.ts
index f70b7da959a..5da7b6a97ef 100644
--- a/packages/backend/src/api/resources/User.ts
+++ b/packages/backend/src/api/resources/User.ts
@@ -6,6 +6,12 @@ import { SamlAccount } from './SamlAccount';
import { Web3Wallet } from './Web3Wallet';
export class User {
+ private _raw: UserJSON | null = null;
+
+ public get raw(): UserJSON | null {
+ return this._raw;
+ }
+
constructor(
readonly id: string,
readonly passwordEnabled: boolean,
@@ -42,7 +48,7 @@ export class User {
) {}
static fromJSON(data: UserJSON): User {
- return new User(
+ const res = new User(
data.id,
data.password_enabled,
data.totp_enabled,
@@ -76,6 +82,8 @@ export class User {
data.delete_self_enabled,
data.legal_accepted_at,
);
+ res._raw = data;
+ return res;
}
get primaryEmailAddress() {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 481318c285b..1a50e9f6e78 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -302,6 +302,31 @@ importers:
specifier: catalog:repo
version: 8.3.0
+ packages/agent-toolkit:
+ dependencies:
+ '@clerk/backend':
+ specifier: workspace:^
+ version: link:../backend
+ '@clerk/shared':
+ specifier: workspace:^
+ version: link:../shared
+ '@clerk/types':
+ specifier: workspace:^
+ version: link:../types
+ '@langchain/core':
+ specifier: ^0.3.6
+ version: 0.3.39
+ ai:
+ specifier: ^3.4.7 || ^4.0.0
+ version: 4.1.34(react@18.3.1)(zod@3.24.1)
+ zod:
+ specifier: ^3.24.1
+ version: 3.24.1
+ devDependencies:
+ esbuild-plugin-file-path-extensions:
+ specifier: ^2.1.4
+ version: 2.1.4
+
packages/astro:
dependencies:
'@clerk/backend':
@@ -542,7 +567,7 @@ importers:
version: 8.2.2
next:
specifier: ^14.2.23
- version: 14.2.23(@babel/core@7.26.7)(@playwright/test@1.44.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ version: 14.2.23(@babel/core@7.26.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.44.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
type-fest:
specifier: ^4.33.0
version: 4.33.0
@@ -711,7 +736,7 @@ importers:
version: 4.2.2
next:
specifier: ^14.2.23
- version: 14.2.23(@babel/core@7.26.7)(@playwright/test@1.44.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ version: 14.2.23(@babel/core@7.26.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.44.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
packages/nuxt:
dependencies:
@@ -1151,6 +1176,40 @@ packages:
'@adobe/css-tools@4.4.0':
resolution: {integrity: sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==}
+ '@ai-sdk/provider-utils@2.1.6':
+ resolution: {integrity: sha512-Pfyaj0QZS22qyVn5Iz7IXcJ8nKIKlu2MeSAdKJzTwkAks7zdLaKVB+396Rqcp1bfQnxl7vaduQVMQiXUrgK8Gw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.0.0
+ peerDependenciesMeta:
+ zod:
+ optional: true
+
+ '@ai-sdk/provider@1.0.7':
+ resolution: {integrity: sha512-q1PJEZ0qD9rVR+8JFEd01/QM++csMT5UVwYXSN2u54BrVw/D8TZLTeg2FEfKK00DgAx0UtWd8XOhhwITP9BT5g==}
+ engines: {node: '>=18'}
+
+ '@ai-sdk/react@1.1.11':
+ resolution: {integrity: sha512-vfjZ7w2M+Me83HTMMrnnrmXotz39UDCMd27YQSrvt2f1YCLPloVpLhP+Y9TLZeFE/QiiRCrPYLDQm6aQJYJ9PQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ react: ^18 || ^19 || ^19.0.0-rc
+ zod: ^3.0.0
+ peerDependenciesMeta:
+ react:
+ optional: true
+ zod:
+ optional: true
+
+ '@ai-sdk/ui-utils@1.1.11':
+ resolution: {integrity: sha512-1SC9W4VZLcJtxHRv4Y0aX20EFeaEP6gUvVqoKLBBtMLOgtcZrv/F/HQRjGavGugiwlS3dsVza4X+E78fiwtlTA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.0.0
+ peerDependenciesMeta:
+ zod:
+ optional: true
+
'@alcalzone/ansi-tokenize@0.1.3':
resolution: {integrity: sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw==}
engines: {node: '>=14.13.1'}
@@ -1977,6 +2036,9 @@ packages:
'@bundled-es-modules/tough-cookie@0.1.6':
resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==}
+ '@cfworker/json-schema@4.1.1':
+ resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==}
+
'@changesets/apply-release-plan@7.0.5':
resolution: {integrity: sha512-1cWCk+ZshEkSVEZrm2fSj1Gz8sYvxgUL4Q78+1ZZqeqfuevPTPk033/yUZ3df8BKMohkqqHfzj0HOOrG0KtXTw==}
@@ -3290,6 +3352,10 @@ packages:
'@kwsites/promise-deferred@1.1.1':
resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==}
+ '@langchain/core@0.3.39':
+ resolution: {integrity: sha512-muXs4asy1A7qDtcdznxqyBfxf4N6qxofY/S0c95vbsWa0r9YAE2PttHIjcuxSy1q2jUiTkpCcgFEjNJRQRVhEw==}
+ engines: {node: '>=18'}
+
'@leichtgewicht/ip-codec@2.0.4':
resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==}
@@ -3583,6 +3649,10 @@ packages:
'@open-draft/until@2.1.0':
resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==}
+ '@opentelemetry/api@1.9.0':
+ resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
+ engines: {node: '>=8.0.0'}
+
'@oslojs/encoding@1.1.0':
resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==}
@@ -5383,6 +5453,9 @@ packages:
'@types/debug@4.1.12':
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
+ '@types/diff-match-patch@1.0.36':
+ resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==}
+
'@types/diff@6.0.0':
resolution: {integrity: sha512-dhVCYGv3ZSbzmQaBSagrv1WJ6rXCdkyTcDyoNu1MD8JohI7pR7k8wdZEm+mvdxRKXyHVwckFzWU1vJc+Z29MlA==}
@@ -5572,6 +5645,9 @@ packages:
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+ '@types/uuid@10.0.0':
+ resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==}
+
'@types/webextension-polyfill@0.10.7':
resolution: {integrity: sha512-10ql7A0qzBmFB+F+qAke/nP1PIonS0TXZAOMVOxEUsm+lGSW6uwVcISFNa0I4Oyj0884TZVWGGMIWeXOVSNFHw==}
@@ -6088,6 +6164,18 @@ packages:
resolution: {integrity: sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==}
engines: {node: '>=12'}
+ ai@4.1.34:
+ resolution: {integrity: sha512-9IB5duz6VbXvjibqNrvKz6++PwE8Ui5UfbOC9/CtcQN5Z9sudUQErss+maj7ptoPysD2NPjj99e0Hp183Cz5LQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ react: ^18 || ^19 || ^19.0.0-rc
+ zod: ^3.0.0
+ peerDependenciesMeta:
+ react:
+ optional: true
+ zod:
+ optional: true
+
ajv-formats@2.1.1:
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
peerDependencies:
@@ -7122,6 +7210,9 @@ packages:
resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==}
engines: {node: ^14.18.0 || >=16.10.0}
+ console-table-printer@2.12.1:
+ resolution: {integrity: sha512-wKGOQRRvdnd89pCeH96e2Fn4wkbenSP6LMHfjfyNLMbGuHEFbMqQNuxXqd0oXG9caIOQ1FTvc5Uijp9/4jujnQ==}
+
content-disposition@0.5.4:
resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
engines: {node: '>= 0.6'}
@@ -7717,6 +7808,9 @@ packages:
didyoumean@1.2.2:
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
+ diff-match-patch@1.0.5:
+ resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==}
+
diff-sequences@29.6.3:
resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -8250,6 +8344,10 @@ packages:
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
engines: {node: '>=0.8.x'}
+ eventsource-parser@3.0.0:
+ resolution: {integrity: sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==}
+ engines: {node: '>=18.0.0'}
+
exec-async@2.2.0:
resolution: {integrity: sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==}
@@ -9968,6 +10066,9 @@ packages:
resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
engines: {node: '>=0.10.0'}
+ js-tiktoken@1.0.18:
+ resolution: {integrity: sha512-hFYx4xYf6URgcttcGvGuOBJhTxPYZ2R5eIesqCaNRJmYH8sNmsfTeWg4yu//7u1VD/qIUkgKJTpGom9oHXmB4g==}
+
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@@ -10096,6 +10197,11 @@ packages:
engines: {node: '>=6'}
hasBin: true
+ jsondiffpatch@0.6.0:
+ resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+
jsonfile@4.0.0:
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
@@ -10161,6 +10267,14 @@ packages:
kolorist@1.8.0:
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
+ langsmith@0.3.7:
+ resolution: {integrity: sha512-wakN1hxGkm1JR2PpAV7fiT7oC99LKcgxiuUrYGZWPbuj7Y8EPF19F7VNr4B+hA219bfaeWTa4Lxy2YrtPSKnQA==}
+ peerDependencies:
+ openai: '*'
+ peerDependenciesMeta:
+ openai:
+ optional: true
+
language-subtag-registry@0.3.23:
resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==}
@@ -10968,6 +11082,10 @@ packages:
resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==}
hasBin: true
+ mustache@4.2.0:
+ resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==}
+ hasBin: true
+
mute-stream@2.0.0:
resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==}
engines: {node: ^18.17.0 || >=20.5.0}
@@ -11462,6 +11580,10 @@ packages:
resolution: {integrity: sha512-T8BatKGY+k5rU+Q/GTYgrEf2r4xRMevAN5mtXc2aPc4rS1j3s+vWTaO2Wag94neXuCAUAs8cxBL9EeB5EA6diw==}
engines: {node: '>=16'}
+ p-queue@6.6.2:
+ resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==}
+ engines: {node: '>=8'}
+
p-queue@8.1.0:
resolution: {integrity: sha512-mxLDbbGIBEXTJL0zEx8JIylaj3xQ7Z/7eEVjcF9fJX4DBiH9oqe+oahYnlKKxm0Ci9TlWTyhSHgygxMxjIB2jw==}
engines: {node: '>=18'}
@@ -11474,6 +11596,10 @@ packages:
resolution: {integrity: sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA==}
engines: {node: '>=16.17'}
+ p-timeout@3.2.0:
+ resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==}
+ engines: {node: '>=8'}
+
p-timeout@5.1.0:
resolution: {integrity: sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==}
engines: {node: '>=12'}
@@ -12953,6 +13079,9 @@ packages:
simple-swizzle@0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
+ simple-wcswidth@1.0.1:
+ resolution: {integrity: sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg==}
+
sirv@1.0.19:
resolution: {integrity: sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==}
engines: {node: '>= 10'}
@@ -13572,6 +13701,10 @@ packages:
throttleit@1.0.1:
resolution: {integrity: sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==}
+ throttleit@2.1.0:
+ resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==}
+ engines: {node: '>=18'}
+
through2@2.0.5:
resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
@@ -14275,6 +14408,10 @@ packages:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
engines: {node: '>= 0.4.0'}
+ uuid@10.0.0:
+ resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
+ hasBin: true
+
uuid@7.0.3:
resolution: {integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==}
hasBin: true
@@ -15078,6 +15215,37 @@ snapshots:
'@adobe/css-tools@4.4.0': {}
+ '@ai-sdk/provider-utils@2.1.6(zod@3.24.1)':
+ dependencies:
+ '@ai-sdk/provider': 1.0.7
+ eventsource-parser: 3.0.0
+ nanoid: 3.3.8
+ secure-json-parse: 2.7.0
+ optionalDependencies:
+ zod: 3.24.1
+
+ '@ai-sdk/provider@1.0.7':
+ dependencies:
+ json-schema: 0.4.0
+
+ '@ai-sdk/react@1.1.11(react@18.3.1)(zod@3.24.1)':
+ dependencies:
+ '@ai-sdk/provider-utils': 2.1.6(zod@3.24.1)
+ '@ai-sdk/ui-utils': 1.1.11(zod@3.24.1)
+ swr: 2.2.5(react@18.3.1)
+ throttleit: 2.1.0
+ optionalDependencies:
+ react: 18.3.1
+ zod: 3.24.1
+
+ '@ai-sdk/ui-utils@1.1.11(zod@3.24.1)':
+ dependencies:
+ '@ai-sdk/provider': 1.0.7
+ '@ai-sdk/provider-utils': 2.1.6(zod@3.24.1)
+ zod-to-json-schema: 3.24.1(zod@3.24.1)
+ optionalDependencies:
+ zod: 3.24.1
+
'@alcalzone/ansi-tokenize@0.1.3':
dependencies:
ansi-styles: 6.2.1
@@ -16144,6 +16312,8 @@ snapshots:
'@types/tough-cookie': 4.0.5
tough-cookie: 4.1.4
+ '@cfworker/json-schema@4.1.1': {}
+
'@changesets/apply-release-plan@7.0.5':
dependencies:
'@changesets/config': 3.0.3
@@ -17710,6 +17880,23 @@ snapshots:
'@kwsites/promise-deferred@1.1.1': {}
+ '@langchain/core@0.3.39':
+ dependencies:
+ '@cfworker/json-schema': 4.1.1
+ ansi-styles: 5.2.0
+ camelcase: 6.3.0
+ decamelize: 1.2.0
+ js-tiktoken: 1.0.18
+ langsmith: 0.3.7
+ mustache: 4.2.0
+ p-queue: 6.6.2
+ p-retry: 4.6.2
+ uuid: 10.0.0
+ zod: 3.24.1
+ zod-to-json-schema: 3.24.1(zod@3.24.1)
+ transitivePeerDependencies:
+ - openai
+
'@leichtgewicht/ip-codec@2.0.4': {}
'@manypkg/find-root@1.1.0':
@@ -18227,6 +18414,8 @@ snapshots:
'@open-draft/until@2.1.0': {}
+ '@opentelemetry/api@1.9.0': {}
+
'@oslojs/encoding@1.1.0': {}
'@parcel/watcher-android-arm64@2.5.1':
@@ -21325,6 +21514,8 @@ snapshots:
dependencies:
'@types/ms': 0.7.34
+ '@types/diff-match-patch@1.0.36': {}
+
'@types/diff@6.0.0': {}
'@types/estree@1.0.5': {}
@@ -21530,6 +21721,8 @@ snapshots:
'@types/unist@3.0.3': {}
+ '@types/uuid@10.0.0': {}
+
'@types/webextension-polyfill@0.10.7': {}
'@types/webpack-env@1.18.8': {}
@@ -22340,6 +22533,18 @@ snapshots:
clean-stack: 4.2.0
indent-string: 5.0.0
+ ai@4.1.34(react@18.3.1)(zod@3.24.1):
+ dependencies:
+ '@ai-sdk/provider': 1.0.7
+ '@ai-sdk/provider-utils': 2.1.6(zod@3.24.1)
+ '@ai-sdk/react': 1.1.11(react@18.3.1)(zod@3.24.1)
+ '@ai-sdk/ui-utils': 1.1.11(zod@3.24.1)
+ '@opentelemetry/api': 1.9.0
+ jsondiffpatch: 0.6.0
+ optionalDependencies:
+ react: 18.3.1
+ zod: 3.24.1
+
ajv-formats@2.1.1(ajv@8.17.1):
optionalDependencies:
ajv: 8.17.1
@@ -23581,6 +23786,10 @@ snapshots:
consola@3.4.0: {}
+ console-table-printer@2.12.1:
+ dependencies:
+ simple-wcswidth: 1.0.1
+
content-disposition@0.5.4:
dependencies:
safe-buffer: 5.2.1
@@ -24203,6 +24412,8 @@ snapshots:
didyoumean@1.2.2: {}
+ diff-match-patch@1.0.5: {}
+
diff-sequences@29.6.3: {}
diff@4.0.2:
@@ -24930,6 +25141,8 @@ snapshots:
events@3.3.0: {}
+ eventsource-parser@3.0.0: {}
+
exec-async@2.2.0: {}
execa@1.0.0:
@@ -27151,6 +27364,10 @@ snapshots:
js-levenshtein@1.1.6: {}
+ js-tiktoken@1.0.18:
+ dependencies:
+ base64-js: 1.5.1
+
js-tokens@4.0.0: {}
js-tokens@9.0.1: {}
@@ -27354,6 +27571,12 @@ snapshots:
json5@2.2.3: {}
+ jsondiffpatch@0.6.0:
+ dependencies:
+ '@types/diff-match-patch': 1.0.36
+ chalk: 5.4.1
+ diff-match-patch: 1.0.5
+
jsonfile@4.0.0:
optionalDependencies:
graceful-fs: 4.2.11
@@ -27430,6 +27653,16 @@ snapshots:
kolorist@1.8.0: {}
+ langsmith@0.3.7:
+ dependencies:
+ '@types/uuid': 10.0.0
+ chalk: 4.1.2
+ console-table-printer: 2.12.1
+ p-queue: 6.6.2
+ p-retry: 4.6.2
+ semver: 7.7.1
+ uuid: 10.0.0
+
language-subtag-registry@0.3.23: {}
language-tags@1.0.9:
@@ -28527,6 +28760,8 @@ snapshots:
dns-packet: 5.6.0
thunky: 1.1.0
+ mustache@4.2.0: {}
+
mute-stream@2.0.0: {}
mv@2.1.1:
@@ -28565,7 +28800,7 @@ snapshots:
nested-error-stacks@2.1.1: {}
- next@14.2.23(@babel/core@7.26.7)(@playwright/test@1.44.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ next@14.2.23(@babel/core@7.26.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.44.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@next/env': 14.2.23
'@swc/helpers': 0.5.5
@@ -28586,6 +28821,7 @@ snapshots:
'@next/swc-win32-arm64-msvc': 14.2.23
'@next/swc-win32-ia32-msvc': 14.2.23
'@next/swc-win32-x64-msvc': 14.2.23
+ '@opentelemetry/api': 1.9.0
'@playwright/test': 1.44.1
transitivePeerDependencies:
- '@babel/core'
@@ -29247,6 +29483,11 @@ snapshots:
p-map@6.0.0: {}
+ p-queue@6.6.2:
+ dependencies:
+ eventemitter3: 4.0.7
+ p-timeout: 3.2.0
+
p-queue@8.1.0:
dependencies:
eventemitter3: 5.0.1
@@ -29263,6 +29504,10 @@ snapshots:
is-network-error: 1.1.0
retry: 0.13.1
+ p-timeout@3.2.0:
+ dependencies:
+ p-finally: 1.0.0
+
p-timeout@5.1.0: {}
p-timeout@6.1.3: {}
@@ -30950,6 +31195,8 @@ snapshots:
is-arrayish: 0.3.2
optional: true
+ simple-wcswidth@1.0.1: {}
+
sirv@1.0.19:
dependencies:
'@polka/url': 1.0.0-next.28
@@ -31670,6 +31917,8 @@ snapshots:
throttleit@1.0.1: {}
+ throttleit@2.1.0: {}
+
through2@2.0.5:
dependencies:
readable-stream: 2.3.8
@@ -32347,6 +32596,8 @@ snapshots:
utils-merge@1.0.1: {}
+ uuid@10.0.0: {}
+
uuid@7.0.3: {}
uuid@8.3.2: {}
diff --git a/scripts/canary.mjs b/scripts/canary.mjs
index 5c47d167f30..37fb0a3f847 100755
--- a/scripts/canary.mjs
+++ b/scripts/canary.mjs
@@ -10,6 +10,7 @@ const snapshot = `---
'@clerk/clerk-js': patch
'@clerk/backend': patch
'@clerk/fastify': patch
+'@clerk/agent-toolkit': patch
'@clerk/nextjs': patch
'@clerk/shared': patch
'@clerk/themes': patch
diff --git a/scripts/snapshot.mjs b/scripts/snapshot.mjs
index 8b65c77e882..72928b03f41 100755
--- a/scripts/snapshot.mjs
+++ b/scripts/snapshot.mjs
@@ -10,6 +10,7 @@ const snapshot = `---
'@clerk/clerk-js': patch
'@clerk/backend': patch
'@clerk/fastify': patch
+'@clerk/agent-toolkit': patch
'@clerk/nextjs': patch
'@clerk/shared': patch
'@clerk/themes': patch