@knocklabs/agent-toolkit 0.1.9 → 0.1.10

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 CHANGED
@@ -11,6 +11,7 @@
11
11
  - [Model Context Protocol (MCP)](#model-context-protocol-mcp)
12
12
  - [AI SDK](#ai-sdk)
13
13
  - [OpenAI](#openai)
14
+ - [Langchain](#langchain)
14
15
 
15
16
  ## Getting started
16
17
 
@@ -23,6 +24,7 @@ Using the Knock agent toolkit allows you to build powerful agent systems that ar
23
24
  The Knock Agent Toolkit provides three main entry points:
24
25
 
25
26
  - `@knocklabs/agent-toolkit/ai-sdk`: Helpers for integrating with Vercel's AI SDK.
27
+ - `@knocklabs/agent-tookkit/langchain`: Helpers for integrating with [Langchain's JS SDK](https://github.com/langchain-ai/langchainjs).
26
28
  - `@knocklabs/agent-toolkit/openai`: Helpers for integrating with the OpenAI SDK.
27
29
  - `@knocklabs/agent-toolkit/modelcontextprotocol`: Low level helpers for integrating with the Model Context Protocol (MCP).
28
30
 
@@ -66,6 +68,12 @@ npx -y @knocklabs/agent-toolkit -p local-mcp --tools "workflows.*"
66
68
  npx -y @knocklabs/agent-toolkit -p local-mcp --tools "workflows.triggerWorkflow"
67
69
  ```
68
70
 
71
+ If you wish to enable workflows-as-tools within the MCP server, you must set the `--workflows` flag to pass in a list of approved workflow keys to expose. This ensures that you keep the number of tools exposed to your MCP client to a minimum.
72
+
73
+ ```
74
+ npx -y @knocklabs/agent-toolkit -p local-mcp --workflows comment-created activate-account
75
+ ```
76
+
69
77
  It's also possible to pass `environment`, `userId`, and `tenant` to the local MCP server to set default values. Use the `--help` flag to view additional server options.
70
78
 
71
79
  ### AI SDK
@@ -84,7 +92,6 @@ npm install @knocklabs/agent-toolkit
84
92
  import { createKnockToolkit } from "@knocklabs/agent-toolkit/ai-sdk";
85
93
  import { openai } from "@ai-sdk/openai";
86
94
  import { streamText } from "ai";
87
- import { auth } from "@clerk/nextjs/server";
88
95
  import { systemPrompt } from "@/lib/ai/prompts";
89
96
 
90
97
  export const maxDuration = 30;
@@ -132,25 +139,89 @@ import OpenAI from "openai";
132
139
 
133
140
  const openai = new OpenAI();
134
141
 
135
- const toolkit = await createKnockToolkit({
136
- serviceToken: "kst_12345",
137
- permissions: {
138
- // Set the permissions of the tools to expose
139
- workflows: { read: true, run: true, manage: true },
140
- },
141
- });
142
-
143
- const completion = await openai.chat.completions.create({
144
- model: "gpt-4o",
145
- messages,
146
- // The tools given here are determined by the `permissions`
147
- // list above in the configuration. For instance, here we're only
148
- // allowing the workflows
149
- tools: toolkit.getAllTools(),
150
- });
151
-
152
- // Execute the tool calls
153
- const toolMessages = await Promise.all(
154
- message.tool_calls.map((tc) => toolkit.handleToolCall(tc))
155
- );
142
+ async function main() {
143
+ const toolkit = await createKnockToolkit({
144
+ serviceToken: "kst_12345",
145
+ permissions: {
146
+ // Set the permissions of the tools to expose
147
+ workflows: { read: true, run: true, manage: true },
148
+ },
149
+ });
150
+
151
+ const completion = await openai.chat.completions.create({
152
+ model: "gpt-4o",
153
+ messages,
154
+ // The tools given here are determined by the `permissions`
155
+ // list above in the configuration. For instance, here we're only
156
+ // allowing the workflows
157
+ tools: toolkit.getAllTools(),
158
+ });
159
+
160
+ // Execute the tool calls
161
+ const toolMessages = await Promise.all(
162
+ message.tool_calls.map((tc) => toolkit.handleToolCall(tc))
163
+ );
164
+ }
165
+
166
+ main();
167
+ ```
168
+
169
+ ### Langchain
170
+
171
+ The agent toolkit provides a `createKnockToolkit` under the `/langchain` path for easily integrating into the Lanchain JS SDK and returning tools ready for use.
172
+
173
+ 1. Install the package:
174
+
175
+ ```
176
+ npm install @knocklabs/agent-toolkit
177
+ ```
178
+
179
+ 2. Import the `createKnockToolkit` helper, configure it, and use it in your LLM calling:
180
+
181
+ ```typescript
182
+ import { createKnockToolkit } from "@knocklabs/agent-toolkit/langchain";
183
+ import { ChatOpenAI } from "@langchain/openai";
184
+ import { HumanMessage, SystemMessage } from "@langchain/core/messages";
185
+ import { LangChainAdapter } from "ai";
186
+
187
+ const systemPrompt = `You are a helpful assistant.`;
188
+
189
+ export const maxDuration = 30;
190
+
191
+ export async function POST(req: Request) {
192
+ const { prompt } = await req.json();
193
+ // Optional - get the auth context from the request
194
+ const authContext = await auth.protect();
195
+
196
+ // Instantiate a new Knock toolkit
197
+ const toolkit = await createKnockToolkit({
198
+ serviceToken: "kst_12345",
199
+ permissions: {
200
+ // (optional but recommended): Set the permissions of the tools to expose
201
+ workflows: { read: true, run: true, manage: true },
202
+ },
203
+ });
204
+
205
+ const model = new ChatOpenAI({ model: "gpt-4o", temperature: 0 });
206
+
207
+ const modelWithTools = model.bindTools(toolkit.getAllTools());
208
+
209
+ const messages = [new SystemMessage(systemPrompt), new HumanMessage(prompt)];
210
+ const aiMessage = await modelWithTools.invoke(messages);
211
+ messages.push(aiMessage);
212
+
213
+ for (const toolCall of aiMessage.tool_calls || []) {
214
+ // Call the selected tool by its `name`
215
+ const selectedTool = toolkit.getToolMap()[toolCall.name];
216
+ const toolMessage = await selectedTool.invoke(toolCall);
217
+
218
+ messages.push(toolMessage);
219
+ }
220
+
221
+ // To simplify the setup, this example uses the ai-sdk langchain adapter
222
+ // to stream the results back to the /langchain page.
223
+ // For more details, see: https://sdk.vercel.ai/providers/adapters/langchain
224
+ const stream = await modelWithTools.stream(messages);
225
+ return LangChainAdapter.toDataStreamResponse(stream);
226
+ }
156
227
  ```
@@ -1,6 +1,28 @@
1
1
  import { ToolSet, Tool } from 'ai';
2
- import { T as ToolkitConfig, a as ToolCategory } from '../types-BJFe1DAl.js';
2
+ import { a as DeferredToolCall, b as DeferredToolCallConfig, K as KnockOutboundWebhookEvent, D as DeferredToolCallInteractionResult } from '../types-CrTRlRnE.js';
3
+ import { ToolkitConfig, ToolCategory } from '../types.js';
4
+ import { ToolInvocation } from '@ai-sdk/ui-utils';
5
+ import '@knocklabs/node/dist/src/resources/messages/interfaces';
3
6
 
7
+ /**
8
+ * Convert a deferred tool call to a tool invocation. Useful when building an assistant
9
+ * response from a deferred tool call.
10
+ *
11
+ * @param deferredToolCall - The deferred tool call to convert.
12
+ * @param result - The result of the tool call.
13
+ * @returns The tool invocation.
14
+ */
15
+ declare function deferredToolCallToToolInvocation(deferredToolCall: DeferredToolCall, result: unknown): ToolInvocation;
16
+
17
+ type KnockToolkit = {
18
+ getAllTools: () => ToolSet;
19
+ getTools: (category: ToolCategory) => ToolSet;
20
+ getToolMap: () => Record<string, Tool>;
21
+ requireHumanInput: (toolsToWrap: ToolSet, inputConfig: DeferredToolCallConfig) => ToolSet;
22
+ resumeToolExecution: (toolInteraction: DeferredToolCall) => Promise<unknown>;
23
+ wrappedToolsRequiringInput: () => Map<string, Tool>;
24
+ handleMessageInteraction: (event: KnockOutboundWebhookEvent) => DeferredToolCallInteractionResult | null;
25
+ };
4
26
  /**
5
27
  * Creates a Knock toolkit for use with the AI SDK.
6
28
  *
@@ -24,29 +46,6 @@ import { T as ToolkitConfig, a as ToolCategory } from '../types-BJFe1DAl.js';
24
46
  * @param config - The configuration to use for the toolkit
25
47
  * @returns A toolkit for use with the AI SDK
26
48
  */
27
- declare const createKnockToolkit: (config: ToolkitConfig) => Promise<{
28
- /**
29
- * Get all tools for all categories. When the `config.permissions.workflows.run` is set, then workflow triggers for
30
- * the specified workflows will be included in the returned tools.
31
- *
32
- * @returns A promise that resolves to a set of tools
33
- */
34
- getAllTools: () => Promise<ToolSet>;
35
- /**
36
- * Get all tools for a specific category. When trying to get tools for the `workflows` category and the run permission is set,
37
- * the workflow triggers for the specified workflows will be included in the returned tools.
38
- *
39
- * @param category - The category of tools to get
40
- * @returns An array of tools for the given category
41
- */
42
- getTools: (category: ToolCategory) => Promise<ToolSet>;
43
- /**
44
- * Get a map of all tools by method name. When the `config.permissions.workflows.run` is set, then workflow triggers for
45
- * the specified workflows will be included in the returned tools.
46
- *
47
- * @returns A map of all tools by method name
48
- */
49
- getToolMap: () => Promise<Record<string, Tool>>;
50
- }>;
49
+ declare const createKnockToolkit: (config: ToolkitConfig) => Promise<KnockToolkit>;
51
50
 
52
- export { createKnockToolkit };
51
+ export { createKnockToolkit, deferredToolCallToToolInvocation };
@@ -1,10 +1,16 @@
1
+ import {
2
+ handleMessageInteraction,
3
+ triggerHumanInTheLoopWorkflow,
4
+ wrapToolDescription
5
+ } from "../chunk-6O7VMHBN.js";
1
6
  import {
2
7
  createKnockClient
3
8
  } from "../chunk-OMZBTWDH.js";
4
9
  import {
5
10
  getToolMap,
6
11
  getToolsByPermissionsInCategories
7
- } from "../chunk-6UHXLKAV.js";
12
+ } from "../chunk-25NTCHCG.js";
13
+ import "../chunk-G3PMV62Z.js";
8
14
 
9
15
  // src/ai-sdk/tool-converter.ts
10
16
  import { tool } from "ai";
@@ -26,6 +32,18 @@ var knockToolsToToolSet = (knockClient, config, knockTools) => {
26
32
  );
27
33
  };
28
34
 
35
+ // src/ai-sdk/helpers.ts
36
+ function deferredToolCallToToolInvocation(deferredToolCall, result) {
37
+ return {
38
+ args: deferredToolCall.args,
39
+ toolName: deferredToolCall.method,
40
+ toolCallId: deferredToolCall.extra?.toolCallId,
41
+ state: "result",
42
+ step: 0,
43
+ result
44
+ };
45
+ }
46
+
29
47
  // src/ai-sdk/index.ts
30
48
  var createKnockToolkit = async (config) => {
31
49
  const knockClient = createKnockClient(config);
@@ -35,14 +53,15 @@ var createKnockToolkit = async (config) => {
35
53
  );
36
54
  const allTools = Object.values(allowedToolsByCategory).flat();
37
55
  const toolsByMethod = getToolMap(allTools);
38
- return {
56
+ const wrappedToolsRequiringInput = /* @__PURE__ */ new Map();
57
+ return Promise.resolve({
39
58
  /**
40
59
  * Get all tools for all categories. When the `config.permissions.workflows.run` is set, then workflow triggers for
41
60
  * the specified workflows will be included in the returned tools.
42
61
  *
43
62
  * @returns A promise that resolves to a set of tools
44
63
  */
45
- getAllTools: async () => {
64
+ getAllTools: () => {
46
65
  return knockToolsToToolSet(knockClient, config, allTools);
47
66
  },
48
67
  /**
@@ -52,7 +71,7 @@ var createKnockToolkit = async (config) => {
52
71
  * @param category - The category of tools to get
53
72
  * @returns An array of tools for the given category
54
73
  */
55
- getTools: async (category) => {
74
+ getTools: (category) => {
56
75
  return knockToolsToToolSet(
57
76
  knockClient,
58
77
  config,
@@ -65,7 +84,7 @@ var createKnockToolkit = async (config) => {
65
84
  *
66
85
  * @returns A map of all tools by method name
67
86
  */
68
- getToolMap: async () => {
87
+ getToolMap: () => {
69
88
  return Object.entries(toolsByMethod).reduce(
70
89
  (acc, [method, tool2]) => ({
71
90
  ...acc,
@@ -73,10 +92,89 @@ var createKnockToolkit = async (config) => {
73
92
  }),
74
93
  {}
75
94
  );
76
- }
77
- };
95
+ },
96
+ /**
97
+ * Wraps one or more tools to require human input.
98
+ *
99
+ * @param toolsToWrap - The tools to wrap
100
+ * @param inputConfig - The configuration to use for the HITL request
101
+ */
102
+ requireHumanInput: (toolsToWrap, inputConfig) => {
103
+ const wrappedTools = {};
104
+ for (const [method, toolToWrap] of Object.entries(toolsToWrap)) {
105
+ wrappedToolsRequiringInput.set(method, { ...toolToWrap });
106
+ const wrappedTool = {
107
+ ...toolToWrap,
108
+ description: wrapToolDescription(toolToWrap.description ?? ""),
109
+ execute: async (input, options) => {
110
+ const toolExecution = {
111
+ method,
112
+ args: input,
113
+ extra: {
114
+ toolCallId: options.toolCallId
115
+ }
116
+ };
117
+ await triggerHumanInTheLoopWorkflow({
118
+ knockClient,
119
+ config,
120
+ toolCall: toolExecution,
121
+ inputConfig
122
+ });
123
+ return {
124
+ type: "tool-status",
125
+ status: "pending-input",
126
+ toolCallId: options.toolCallId
127
+ };
128
+ }
129
+ };
130
+ wrappedTools[method] = wrappedTool;
131
+ }
132
+ return wrappedTools;
133
+ },
134
+ /**
135
+ * Returns any tools that were wrapped with `requireHumanInput`.
136
+ *
137
+ * @returns A map of wrapped tools that require human input
138
+ */
139
+ wrappedToolsRequiringInput: () => wrappedToolsRequiringInput,
140
+ /**
141
+ * Resumes the execution of a tool that required human input.
142
+ *
143
+ * @param toolInteraction - The tool interaction to resume
144
+ * @returns A promise that resolves to the result of the tool execution
145
+ */
146
+ resumeToolExecution: async (toolInteraction) => {
147
+ const tool2 = wrappedToolsRequiringInput.get(toolInteraction.method);
148
+ if (!tool2) {
149
+ throw new Error(
150
+ `Tool "${toolInteraction.method}" not found. Did you forget to wrap the tool with requireHumanInput?`
151
+ );
152
+ }
153
+ if (!tool2.execute) {
154
+ throw new Error(
155
+ `Tool "${toolInteraction.method}" does not have an execute method. Nothing to resume.`
156
+ );
157
+ }
158
+ const options = toolInteraction.extra;
159
+ const result = await tool2.execute(toolInteraction.args, options);
160
+ return {
161
+ type: "tool-status",
162
+ status: "completed",
163
+ toolCallId: options.toolCallId,
164
+ result
165
+ };
166
+ },
167
+ /**
168
+ * Handles a message interaction event by parsing it into a well-known format.
169
+ *
170
+ * @param event - The message interaction event
171
+ * @returns An deferred tool call, or null if the event is not a message interaction
172
+ */
173
+ handleMessageInteraction
174
+ });
78
175
  };
79
176
  export {
80
- createKnockToolkit
177
+ createKnockToolkit,
178
+ deferredToolCallToToolInvocation
81
179
  };
82
180
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/ai-sdk/tool-converter.ts","../../src/ai-sdk/index.ts"],"sourcesContent":["import type { Tool } from \"ai\";\nimport { tool } from \"ai\";\nimport { z } from \"zod\";\n\nimport { KnockClient } from \"../lib/knock-client.js\";\nimport { KnockTool } from \"../lib/knock-tool.js\";\nimport { Config } from \"../types.js\";\n\n/**\n * Convert a KnockTool to an AI Tool, ready to pass to the AI SDK.\n */\nconst knockToolToAiTool = (\n knockClient: KnockClient,\n config: Config,\n knockTool: KnockTool\n): Tool => {\n return tool({\n description: knockTool.description,\n parameters: knockTool.parameters ?? z.object({}),\n execute: knockTool.bindExecute(knockClient, config),\n });\n};\n\n/**\n * Convert a list of KnockTools to an AI ToolSet, ready to pass to the AI SDK.\n */\nconst knockToolsToToolSet = (\n knockClient: KnockClient,\n config: Config,\n knockTools: KnockTool[]\n) => {\n return knockTools.reduce(\n (acc, tool) => {\n acc[tool.method] = knockToolToAiTool(knockClient, config, tool);\n return acc;\n },\n {} as Record<string, Tool>\n );\n};\n\nexport { knockToolToAiTool, knockToolsToToolSet };\n","import { Tool, ToolSet } from \"ai\";\n\nimport { createKnockClient } from \"../lib/knock-client.js\";\nimport { getToolMap, getToolsByPermissionsInCategories } from \"../lib/utils.js\";\nimport { ToolCategory, ToolkitConfig } from \"../types.js\";\n\nimport { knockToolsToToolSet, knockToolToAiTool } from \"./tool-converter.js\";\n\n/**\n * Creates a Knock toolkit for use with the AI SDK.\n *\n * You can filter the set of tools that are available by setting the `config.permissions` property.\n *\n * When the `config.permissions.workflows.run` is set, then workflow triggers for\n * the specified workflows will be included in the returned tools.\n *\n * You can also specify a list of workflow keys to include in the returned tools, should you wish to\n * limit the set of workflows that are available.\n *\n * @example\n * ```ts\n * const toolkit = await createKnockToolkit({\n * permissions: {\n * workflows: { read: true },\n * },\n * });\n * ```\n *\n * @param config - The configuration to use for the toolkit\n * @returns A toolkit for use with the AI SDK\n */\nconst createKnockToolkit = async (config: ToolkitConfig) => {\n const knockClient = createKnockClient(config);\n const allowedToolsByCategory = await getToolsByPermissionsInCategories(\n knockClient,\n config\n );\n const allTools = Object.values(allowedToolsByCategory).flat();\n const toolsByMethod = getToolMap(allTools);\n\n return {\n /**\n * Get all tools for all categories. When the `config.permissions.workflows.run` is set, then workflow triggers for\n * the specified workflows will be included in the returned tools.\n *\n * @returns A promise that resolves to a set of tools\n */\n getAllTools: async (): Promise<ToolSet> => {\n return knockToolsToToolSet(knockClient, config, allTools);\n },\n\n /**\n * Get all tools for a specific category. When trying to get tools for the `workflows` category and the run permission is set,\n * the workflow triggers for the specified workflows will be included in the returned tools.\n *\n * @param category - The category of tools to get\n * @returns An array of tools for the given category\n */\n getTools: async (category: ToolCategory): Promise<ToolSet> => {\n return knockToolsToToolSet(\n knockClient,\n config,\n allowedToolsByCategory[category]\n );\n },\n\n /**\n * Get a map of all tools by method name. When the `config.permissions.workflows.run` is set, then workflow triggers for\n * the specified workflows will be included in the returned tools.\n *\n * @returns A map of all tools by method name\n */\n getToolMap: async (): Promise<Record<string, Tool>> => {\n return Object.entries(toolsByMethod).reduce(\n (acc, [method, tool]) => ({\n ...acc,\n [method]: knockToolToAiTool(knockClient, config, tool),\n }),\n {} as Record<string, Tool>\n );\n },\n };\n};\n\nexport { createKnockToolkit };\n"],"mappings":";;;;;;;;;AACA,SAAS,YAAY;AACrB,SAAS,SAAS;AASlB,IAAM,oBAAoB,CACxB,aACA,QACA,cACS;AACT,SAAO,KAAK;AAAA,IACV,aAAa,UAAU;AAAA,IACvB,YAAY,UAAU,cAAc,EAAE,OAAO,CAAC,CAAC;AAAA,IAC/C,SAAS,UAAU,YAAY,aAAa,MAAM;AAAA,EACpD,CAAC;AACH;AAKA,IAAM,sBAAsB,CAC1B,aACA,QACA,eACG;AACH,SAAO,WAAW;AAAA,IAChB,CAAC,KAAKA,UAAS;AACb,UAAIA,MAAK,MAAM,IAAI,kBAAkB,aAAa,QAAQA,KAAI;AAC9D,aAAO;AAAA,IACT;AAAA,IACA,CAAC;AAAA,EACH;AACF;;;ACPA,IAAM,qBAAqB,OAAO,WAA0B;AAC1D,QAAM,cAAc,kBAAkB,MAAM;AAC5C,QAAM,yBAAyB,MAAM;AAAA,IACnC;AAAA,IACA;AAAA,EACF;AACA,QAAM,WAAW,OAAO,OAAO,sBAAsB,EAAE,KAAK;AAC5D,QAAM,gBAAgB,WAAW,QAAQ;AAEzC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOL,aAAa,YAA8B;AACzC,aAAO,oBAAoB,aAAa,QAAQ,QAAQ;AAAA,IAC1D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,UAAU,OAAO,aAA6C;AAC5D,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,uBAAuB,QAAQ;AAAA,MACjC;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,YAAY,YAA2C;AACrD,aAAO,OAAO,QAAQ,aAAa,EAAE;AAAA,QACnC,CAAC,KAAK,CAAC,QAAQC,KAAI,OAAO;AAAA,UACxB,GAAG;AAAA,UACH,CAAC,MAAM,GAAG,kBAAkB,aAAa,QAAQA,KAAI;AAAA,QACvD;AAAA,QACA,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;","names":["tool","tool"]}
1
+ {"version":3,"sources":["../../src/ai-sdk/tool-converter.ts","../../src/ai-sdk/helpers.ts","../../src/ai-sdk/index.ts"],"sourcesContent":["import type { Tool } from \"ai\";\nimport { tool } from \"ai\";\nimport { z } from \"zod\";\n\nimport { KnockClient } from \"../lib/knock-client.js\";\nimport { KnockTool } from \"../lib/knock-tool.js\";\nimport { Config } from \"../types.js\";\n\n/**\n * Convert a KnockTool to an AI Tool, ready to pass to the AI SDK.\n */\nconst knockToolToAiTool = (\n knockClient: KnockClient,\n config: Config,\n knockTool: KnockTool\n): Tool => {\n return tool({\n description: knockTool.description,\n parameters: knockTool.parameters ?? z.object({}),\n execute: knockTool.bindExecute(knockClient, config),\n });\n};\n\n/**\n * Convert a list of KnockTools to an AI ToolSet, ready to pass to the AI SDK.\n */\nconst knockToolsToToolSet = (\n knockClient: KnockClient,\n config: Config,\n knockTools: KnockTool[]\n) => {\n return knockTools.reduce(\n (acc, tool) => {\n acc[tool.method] = knockToolToAiTool(knockClient, config, tool);\n return acc;\n },\n {} as Record<string, Tool>\n );\n};\n\nexport { knockToolToAiTool, knockToolsToToolSet };\n","import { ToolInvocation } from \"@ai-sdk/ui-utils\";\n\nimport { DeferredToolCall } from \"@/lib/human-in-the-loop/types\";\n\n/**\n * Convert a deferred tool call to a tool invocation. Useful when building an assistant\n * response from a deferred tool call.\n *\n * @param deferredToolCall - The deferred tool call to convert.\n * @param result - The result of the tool call.\n * @returns The tool invocation.\n */\nfunction deferredToolCallToToolInvocation(\n deferredToolCall: DeferredToolCall,\n result: unknown\n): ToolInvocation {\n return {\n args: deferredToolCall.args,\n toolName: deferredToolCall.method,\n toolCallId: deferredToolCall.extra?.toolCallId as string,\n state: \"result\",\n step: 0,\n result,\n };\n}\n\nexport { deferredToolCallToToolInvocation };\n","import { Tool, ToolExecutionOptions, ToolSet } from \"ai\";\n\nimport {\n handleMessageInteraction,\n triggerHumanInTheLoopWorkflow,\n wrapToolDescription,\n} from \"../lib/human-in-the-loop/index.js\";\nimport {\n DeferredToolCall,\n DeferredToolCallConfig,\n KnockOutboundWebhookEvent,\n DeferredToolCallInteractionResult,\n} from \"../lib/human-in-the-loop/types.js\";\nimport { createKnockClient } from \"../lib/knock-client.js\";\nimport { getToolMap, getToolsByPermissionsInCategories } from \"../lib/utils.js\";\nimport { ToolCategory, ToolkitConfig } from \"../types.js\";\n\nimport { knockToolsToToolSet, knockToolToAiTool } from \"./tool-converter.js\";\n\ntype KnockToolkit = {\n getAllTools: () => ToolSet;\n getTools: (category: ToolCategory) => ToolSet;\n getToolMap: () => Record<string, Tool>;\n requireHumanInput: (\n toolsToWrap: ToolSet,\n inputConfig: DeferredToolCallConfig\n ) => ToolSet;\n resumeToolExecution: (toolInteraction: DeferredToolCall) => Promise<unknown>;\n wrappedToolsRequiringInput: () => Map<string, Tool>;\n handleMessageInteraction: (\n event: KnockOutboundWebhookEvent\n ) => DeferredToolCallInteractionResult | null;\n};\n\n/**\n * Creates a Knock toolkit for use with the AI SDK.\n *\n * You can filter the set of tools that are available by setting the `config.permissions` property.\n *\n * When the `config.permissions.workflows.run` is set, then workflow triggers for\n * the specified workflows will be included in the returned tools.\n *\n * You can also specify a list of workflow keys to include in the returned tools, should you wish to\n * limit the set of workflows that are available.\n *\n * @example\n * ```ts\n * const toolkit = await createKnockToolkit({\n * permissions: {\n * workflows: { read: true },\n * },\n * });\n * ```\n *\n * @param config - The configuration to use for the toolkit\n * @returns A toolkit for use with the AI SDK\n */\nconst createKnockToolkit = async (\n config: ToolkitConfig\n): Promise<KnockToolkit> => {\n const knockClient = createKnockClient(config);\n const allowedToolsByCategory = await getToolsByPermissionsInCategories(\n knockClient,\n config\n );\n const allTools = Object.values(allowedToolsByCategory).flat();\n const toolsByMethod = getToolMap(allTools);\n\n const wrappedToolsRequiringInput = new Map<string, Tool>();\n\n return Promise.resolve({\n /**\n * Get all tools for all categories. When the `config.permissions.workflows.run` is set, then workflow triggers for\n * the specified workflows will be included in the returned tools.\n *\n * @returns A promise that resolves to a set of tools\n */\n getAllTools: (): ToolSet => {\n return knockToolsToToolSet(knockClient, config, allTools);\n },\n\n /**\n * Get all tools for a specific category. When trying to get tools for the `workflows` category and the run permission is set,\n * the workflow triggers for the specified workflows will be included in the returned tools.\n *\n * @param category - The category of tools to get\n * @returns An array of tools for the given category\n */\n getTools: (category: ToolCategory): ToolSet => {\n return knockToolsToToolSet(\n knockClient,\n config,\n allowedToolsByCategory[category]\n );\n },\n\n /**\n * Get a map of all tools by method name. When the `config.permissions.workflows.run` is set, then workflow triggers for\n * the specified workflows will be included in the returned tools.\n *\n * @returns A map of all tools by method name\n */\n getToolMap: (): Record<string, Tool> => {\n return Object.entries(toolsByMethod).reduce(\n (acc, [method, tool]) => ({\n ...acc,\n [method]: knockToolToAiTool(knockClient, config, tool),\n }),\n {} as Record<string, Tool>\n );\n },\n\n /**\n * Wraps one or more tools to require human input.\n *\n * @param toolsToWrap - The tools to wrap\n * @param inputConfig - The configuration to use for the HITL request\n */\n requireHumanInput: (\n toolsToWrap: ToolSet,\n inputConfig: DeferredToolCallConfig\n ): ToolSet => {\n const wrappedTools: Record<string, Tool> = {};\n\n for (const [method, toolToWrap] of Object.entries(toolsToWrap)) {\n // Keep a reference to the original tool so we can use it later\n wrappedToolsRequiringInput.set(method, { ...toolToWrap });\n\n const wrappedTool = {\n ...toolToWrap,\n description: wrapToolDescription(toolToWrap.description ?? \"\"),\n execute: async (input: any, options: ToolExecutionOptions) => {\n const toolExecution = {\n method,\n args: input,\n extra: {\n toolCallId: options.toolCallId,\n },\n };\n\n await triggerHumanInTheLoopWorkflow({\n knockClient,\n config,\n toolCall: toolExecution,\n inputConfig,\n });\n\n // TODO: Consider injecting a hook here to allow the AI SDK to react to the tool call being deferred\n return {\n type: \"tool-status\",\n status: \"pending-input\",\n toolCallId: options.toolCallId,\n };\n },\n };\n\n wrappedTools[method] = wrappedTool;\n }\n\n return wrappedTools;\n },\n\n /**\n * Returns any tools that were wrapped with `requireHumanInput`.\n *\n * @returns A map of wrapped tools that require human input\n */\n wrappedToolsRequiringInput: () => wrappedToolsRequiringInput,\n\n /**\n * Resumes the execution of a tool that required human input.\n *\n * @param toolInteraction - The tool interaction to resume\n * @returns A promise that resolves to the result of the tool execution\n */\n resumeToolExecution: async (toolInteraction: DeferredToolCall) => {\n const tool = wrappedToolsRequiringInput.get(toolInteraction.method);\n\n if (!tool) {\n throw new Error(\n `Tool \"${toolInteraction.method}\" not found. Did you forget to wrap the tool with requireHumanInput?`\n );\n }\n\n if (!tool.execute) {\n throw new Error(\n `Tool \"${toolInteraction.method}\" does not have an execute method. Nothing to resume.`\n );\n }\n\n const options = toolInteraction.extra as unknown as ToolExecutionOptions;\n const result = await tool.execute(toolInteraction.args, options);\n\n // Return two message objects to represent the tool call and the call result having\n // finished. Note: this can result in duplicate tool call IDs being present in the message history.\n return {\n type: \"tool-status\",\n status: \"completed\",\n toolCallId: options.toolCallId,\n result,\n };\n },\n\n /**\n * Handles a message interaction event by parsing it into a well-known format.\n *\n * @param event - The message interaction event\n * @returns An deferred tool call, or null if the event is not a message interaction\n */\n handleMessageInteraction,\n });\n};\n\nexport * from \"./helpers.js\";\nexport { createKnockToolkit };\n"],"mappings":"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","names":["tool","tool"]}