wave-agent-sdk 0.16.8 → 0.16.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.
Files changed (96) hide show
  1. package/dist/agent.d.ts.map +1 -1
  2. package/dist/agent.js +3 -0
  3. package/dist/constants/tools.d.ts +0 -1
  4. package/dist/constants/tools.d.ts.map +1 -1
  5. package/dist/constants/tools.js +0 -1
  6. package/dist/managers/aiManager.d.ts +0 -8
  7. package/dist/managers/aiManager.d.ts.map +1 -1
  8. package/dist/managers/aiManager.js +0 -45
  9. package/dist/managers/mcpManager.d.ts +5 -0
  10. package/dist/managers/mcpManager.d.ts.map +1 -1
  11. package/dist/managers/mcpManager.js +107 -12
  12. package/dist/managers/toolManager.d.ts +0 -6
  13. package/dist/managers/toolManager.d.ts.map +1 -1
  14. package/dist/managers/toolManager.js +1 -28
  15. package/dist/prompts/index.d.ts.map +1 -1
  16. package/dist/prompts/index.js +1 -12
  17. package/dist/services/authService.d.ts +10 -0
  18. package/dist/services/authService.d.ts.map +1 -1
  19. package/dist/services/authService.js +45 -0
  20. package/dist/services/configurationService.d.ts +1 -0
  21. package/dist/services/configurationService.d.ts.map +1 -1
  22. package/dist/services/configurationService.js +48 -15
  23. package/dist/services/initializationService.d.ts.map +1 -1
  24. package/dist/services/initializationService.js +11 -0
  25. package/dist/services/pluginLoader.d.ts.map +1 -1
  26. package/dist/services/pluginLoader.js +2 -2
  27. package/dist/services/remoteSettingsService.d.ts +21 -0
  28. package/dist/services/remoteSettingsService.d.ts.map +1 -0
  29. package/dist/services/remoteSettingsService.js +279 -0
  30. package/dist/telemetry/instrumentation.d.ts +5 -2
  31. package/dist/telemetry/instrumentation.d.ts.map +1 -1
  32. package/dist/telemetry/instrumentation.js +8 -4
  33. package/dist/tools/buildTool.d.ts +0 -2
  34. package/dist/tools/buildTool.d.ts.map +1 -1
  35. package/dist/tools/buildTool.js +0 -2
  36. package/dist/tools/cronCreateTool.d.ts.map +1 -1
  37. package/dist/tools/cronCreateTool.js +0 -1
  38. package/dist/tools/cronDeleteTool.d.ts.map +1 -1
  39. package/dist/tools/cronDeleteTool.js +0 -1
  40. package/dist/tools/cronListTool.d.ts.map +1 -1
  41. package/dist/tools/cronListTool.js +0 -1
  42. package/dist/tools/enterWorktreeTool.d.ts.map +1 -1
  43. package/dist/tools/enterWorktreeTool.js +0 -1
  44. package/dist/tools/exitWorktreeTool.d.ts.map +1 -1
  45. package/dist/tools/exitWorktreeTool.js +0 -1
  46. package/dist/tools/taskManagementTools.d.ts.map +1 -1
  47. package/dist/tools/taskManagementTools.js +0 -4
  48. package/dist/tools/types.d.ts +0 -15
  49. package/dist/tools/types.d.ts.map +1 -1
  50. package/dist/tools/webFetchTool.d.ts.map +1 -1
  51. package/dist/tools/webFetchTool.js +0 -1
  52. package/dist/types/configuration.d.ts +20 -0
  53. package/dist/types/configuration.d.ts.map +1 -1
  54. package/dist/types/mcp.d.ts +3 -1
  55. package/dist/types/mcp.d.ts.map +1 -1
  56. package/dist/utils/containerSetup.d.ts.map +1 -1
  57. package/dist/utils/containerSetup.js +10 -0
  58. package/dist/utils/mcpUtils.d.ts.map +1 -1
  59. package/dist/utils/mcpUtils.js +0 -1
  60. package/dist/utils/openaiClient.d.ts.map +1 -1
  61. package/dist/utils/openaiClient.js +4 -2
  62. package/package.json +1 -1
  63. package/src/agent.ts +3 -0
  64. package/src/constants/tools.ts +0 -1
  65. package/src/managers/aiManager.ts +0 -48
  66. package/src/managers/mcpManager.ts +122 -16
  67. package/src/managers/toolManager.ts +1 -32
  68. package/src/prompts/index.ts +0 -13
  69. package/src/services/authService.ts +56 -0
  70. package/src/services/configurationService.ts +56 -19
  71. package/src/services/initializationService.ts +13 -0
  72. package/src/services/pluginLoader.ts +2 -2
  73. package/src/services/remoteSettingsService.ts +314 -0
  74. package/src/telemetry/instrumentation.ts +12 -4
  75. package/src/tools/buildTool.ts +0 -4
  76. package/src/tools/cronCreateTool.ts +0 -1
  77. package/src/tools/cronDeleteTool.ts +0 -1
  78. package/src/tools/cronListTool.ts +0 -1
  79. package/src/tools/enterWorktreeTool.ts +0 -1
  80. package/src/tools/exitWorktreeTool.ts +0 -1
  81. package/src/tools/taskManagementTools.ts +0 -4
  82. package/src/tools/types.ts +0 -15
  83. package/src/tools/webFetchTool.ts +0 -1
  84. package/src/types/configuration.ts +23 -0
  85. package/src/types/mcp.ts +8 -1
  86. package/src/utils/containerSetup.ts +10 -0
  87. package/src/utils/mcpUtils.ts +0 -1
  88. package/src/utils/openaiClient.ts +5 -2
  89. package/dist/tools/toolSearchTool.d.ts +0 -15
  90. package/dist/tools/toolSearchTool.d.ts.map +0 -1
  91. package/dist/tools/toolSearchTool.js +0 -200
  92. package/dist/utils/isDeferredTool.d.ts +0 -19
  93. package/dist/utils/isDeferredTool.d.ts.map +0 -1
  94. package/dist/utils/isDeferredTool.js +0 -31
  95. package/src/tools/toolSearchTool.ts +0 -245
  96. package/src/utils/isDeferredTool.ts +0 -36
@@ -7,7 +7,6 @@ const CRON_LIST_PROMPT = `List all cron jobs scheduled via CronCreate in this se
7
7
 
8
8
  export const cronListTool: ToolPlugin = {
9
9
  name: CRON_LIST_TOOL_NAME,
10
- shouldDefer: true,
11
10
  config: {
12
11
  type: "function",
13
12
  function: {
@@ -48,7 +48,6 @@ export const ENTER_WORKTREE_TOOL_PROMPT = `Use this tool ONLY when the user expl
48
48
 
49
49
  export const enterWorktreeTool: ToolPlugin = {
50
50
  name: ENTER_WORKTREE_TOOL_NAME,
51
- shouldDefer: true,
52
51
  config: {
53
52
  type: "function",
54
53
  function: {
@@ -47,7 +47,6 @@ If called outside an EnterWorktree session, the tool is a **no-op**: it reports
47
47
 
48
48
  export const exitWorktreeTool: ToolPlugin = {
49
49
  name: EXIT_WORKTREE_TOOL_NAME,
50
- shouldDefer: true,
51
50
  config: {
52
51
  type: "function",
53
52
  function: {
@@ -9,7 +9,6 @@ import {
9
9
 
10
10
  export const taskCreateTool: ToolPlugin = {
11
11
  name: TASK_CREATE_TOOL_NAME,
12
- shouldDefer: true,
13
12
  config: {
14
13
  type: "function",
15
14
  function: {
@@ -144,7 +143,6 @@ NOTE that you should not use this tool if there is only one trivial task to do.
144
143
 
145
144
  export const taskGetTool: ToolPlugin = {
146
145
  name: TASK_GET_TOOL_NAME,
147
- shouldDefer: true,
148
146
  config: {
149
147
  type: "function",
150
148
  function: {
@@ -204,7 +202,6 @@ Returns full task details:
204
202
 
205
203
  export const taskUpdateTool: ToolPlugin = {
206
204
  name: TASK_UPDATE_TOOL_NAME,
207
- shouldDefer: true,
208
205
  config: {
209
206
  type: "function",
210
207
  function: {
@@ -559,7 +556,6 @@ Set up task dependencies:
559
556
 
560
557
  export const taskListTool: ToolPlugin = {
561
558
  name: TASK_LIST_TOOL_NAME,
562
- shouldDefer: true,
563
559
  config: {
564
560
  type: "function",
565
561
  function: {
@@ -31,21 +31,6 @@ export interface ToolPlugin {
31
31
  workdir?: string;
32
32
  isSubagent?: boolean;
33
33
  }) => string;
34
- /**
35
- * When true, this tool is deferred — it's not sent to the API until the model
36
- * discovers it via ToolSearch. MCP tools are always deferred.
37
- */
38
- shouldDefer?: boolean;
39
- /**
40
- * When true, this tool is never deferred — its full schema always appears in
41
- * the initial prompt even when tool search is enabled.
42
- */
43
- alwaysLoad?: boolean;
44
- /**
45
- * When true, this is an MCP tool (auto-set by McpManager). MCP tools are
46
- * always deferred unless they have alwaysLoad: true.
47
- */
48
- isMcp?: boolean;
49
34
  }
50
35
 
51
36
  export interface ToolResult {
@@ -103,7 +103,6 @@ const GITHUB_URL_ERROR =
103
103
 
104
104
  export const webFetchTool: ToolPlugin = {
105
105
  name: WEB_FETCH_TOOL_NAME,
106
- shouldDefer: true,
107
106
  config: {
108
107
  type: "function",
109
108
  function: {
@@ -44,6 +44,8 @@ export interface WaveConfiguration {
44
44
  autoMemoryEnabled?: boolean;
45
45
  /** Frequency of auto-memory extraction turns */
46
46
  autoMemoryFrequency?: number;
47
+ /** Persisted model selection (from /model command) */
48
+ model?: string;
47
49
  /** Model-specific configuration overrides */
48
50
  models?: Record<string, Partial<ModelConfig>>;
49
51
  /** Scoped marketplace declarations */
@@ -138,3 +140,24 @@ interface Logger {
138
140
  info: (...args: unknown[]) => void;
139
141
  debug: (...args: unknown[]) => void;
140
142
  }
143
+
144
+ export interface RemoteSettingsResponse {
145
+ uuid: string;
146
+ checksum: string;
147
+ settings: WaveConfiguration;
148
+ }
149
+
150
+ export interface RemoteSettingsCache {
151
+ uuid: string;
152
+ checksum: string;
153
+ settings: WaveConfiguration;
154
+ fetchedAt: string;
155
+ }
156
+
157
+ export interface RemoteSettingsFetchResult {
158
+ success: boolean;
159
+ settings?: WaveConfiguration | null;
160
+ checksum?: string;
161
+ error?: string;
162
+ notConfigured?: boolean;
163
+ }
package/src/types/mcp.ts CHANGED
@@ -26,7 +26,14 @@ export interface McpTool {
26
26
  export interface McpServerStatus {
27
27
  name: string;
28
28
  config: McpServerConfig;
29
- status: "disconnected" | "connected" | "connecting" | "error";
29
+ /** Pre-resolution URL with template variables (e.g. ${WAVE_SSO_TOKEN}) preserved for safe display */
30
+ originalUrl?: string;
31
+ status:
32
+ | "disconnected"
33
+ | "connected"
34
+ | "connecting"
35
+ | "reconnecting"
36
+ | "error";
30
37
  tools?: McpTool[];
31
38
  toolCount?: number;
32
39
  capabilities?: string[];
@@ -39,6 +39,7 @@ import type {
39
39
 
40
40
  import { logger } from "./globalLogger.js";
41
41
  import { authService } from "../services/authService.js";
42
+ import { remoteSettingsService } from "../services/remoteSettingsService.js";
42
43
 
43
44
  export interface AgentContainerSetupOptions {
44
45
  options: AgentOptions;
@@ -170,6 +171,15 @@ export function setupAgentContainer(
170
171
  }
171
172
  });
172
173
 
174
+ // Wire up auth change callback to refresh/clear remote settings
175
+ authService.onAuthChange((event) => {
176
+ if (event === "login") {
177
+ remoteSettingsService.refresh();
178
+ } else if (event === "logout") {
179
+ remoteSettingsService.clear();
180
+ }
181
+ });
182
+
173
183
  const lspManager = options.lspManager || new LspManager(container);
174
184
  container.register("LspManager", lspManager);
175
185
 
@@ -91,7 +91,6 @@ export function createMcpToolPlugin(
91
91
  return {
92
92
  name: prefixedName,
93
93
  config: mcpToolToOpenAITool(mcpTool, serverName),
94
- isMcp: true, // MCP tools are always deferred
95
94
  async execute(
96
95
  args: Record<string, unknown>,
97
96
  context?: ToolContext,
@@ -176,8 +176,11 @@ export class OpenAIClient {
176
176
  error.status = response.status;
177
177
  error.body = errorBody;
178
178
 
179
- if (response.status === 429 && attempt < maxRetries) {
180
- logger.warn("OpenAI API 429 Too Many Requests, retrying...", {
179
+ const retryableStatus =
180
+ response.status === 429 ||
181
+ (response.status >= 500 && response.status !== 501);
182
+ if (retryableStatus && attempt < maxRetries) {
183
+ logger.warn("OpenAI API error, retrying...", {
181
184
  attempt: attempt + 1,
182
185
  status: response.status,
183
186
  });
@@ -1,15 +0,0 @@
1
- /**
2
- * ToolSearchTool - Discovers deferred tool schemas on demand.
3
- *
4
- * When tool deferral is enabled, deferred tools are not sent to the API.
5
- * The model must call this tool to discover a deferred tool's full schema
6
- * before it can invoke it.
7
- *
8
- * Query formats:
9
- * - "select:ToolName" — direct selection by name (comma-separated for multiple)
10
- * - "notebook jupyter" — keyword search, up to max_results best matches
11
- * - "+slack send" — require "slack" in the name, rank by remaining terms
12
- */
13
- import { ToolPlugin } from "./types.js";
14
- export declare const toolSearchTool: ToolPlugin;
15
- //# sourceMappingURL=toolSearchTool.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"toolSearchTool.d.ts","sourceRoot":"","sources":["../../src/tools/toolSearchTool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AA4GjE,eAAO,MAAM,cAAc,EAAE,UAuH5B,CAAC"}
@@ -1,200 +0,0 @@
1
- /**
2
- * ToolSearchTool - Discovers deferred tool schemas on demand.
3
- *
4
- * When tool deferral is enabled, deferred tools are not sent to the API.
5
- * The model must call this tool to discover a deferred tool's full schema
6
- * before it can invoke it.
7
- *
8
- * Query formats:
9
- * - "select:ToolName" — direct selection by name (comma-separated for multiple)
10
- * - "notebook jupyter" — keyword search, up to max_results best matches
11
- * - "+slack send" — require "slack" in the name, rank by remaining terms
12
- */
13
- import { isDeferredTool, TOOL_SEARCH_TOOL_NAME, } from "../utils/isDeferredTool.js";
14
- function formatSchema(tool) {
15
- const desc = tool.config.function.description || "";
16
- const params = JSON.stringify(tool.config.function.parameters || {}, null, 2);
17
- return `${tool.name}: ${desc}\nParameters: ${params}`;
18
- }
19
- /**
20
- * Parse tool name into searchable parts (handles CamelCase and underscores).
21
- */
22
- function parseToolName(name) {
23
- return name
24
- .replace(/([a-z])([A-Z])/g, "$1 $2") // CamelCase to spaces
25
- .replace(/_/g, " ")
26
- .toLowerCase()
27
- .split(/\s+/)
28
- .filter(Boolean);
29
- }
30
- /**
31
- * Keyword search over deferred tools by name and description.
32
- * Matches Claude Code's scoring: required terms (+prefix) must all match,
33
- * optional terms contribute to ranking.
34
- */
35
- function keywordSearch(query, deferredTools, maxResults) {
36
- const queryLower = query.toLowerCase().trim();
37
- const queryTerms = queryLower.split(/\s+/).filter(Boolean);
38
- // Exact match fast path
39
- const exact = deferredTools.find((t) => t.name.toLowerCase() === queryLower);
40
- if (exact)
41
- return [exact];
42
- // Partition into required (+prefixed) and optional terms
43
- const requiredTerms = [];
44
- const optionalTerms = [];
45
- for (const term of queryTerms) {
46
- if (term.startsWith("+") && term.length > 1) {
47
- requiredTerms.push(term.slice(1));
48
- }
49
- else {
50
- optionalTerms.push(term);
51
- }
52
- }
53
- const allScoringTerms = requiredTerms.length > 0
54
- ? [...requiredTerms, ...optionalTerms]
55
- : queryTerms;
56
- // Pre-filter to tools matching ALL required terms
57
- let candidateTools = deferredTools;
58
- if (requiredTerms.length > 0) {
59
- candidateTools = deferredTools.filter((tool) => {
60
- const parts = parseToolName(tool.name);
61
- const desc = (tool.config.function.description || "").toLowerCase();
62
- return requiredTerms.every((term) => parts.includes(term) ||
63
- parts.some((p) => p.includes(term)) ||
64
- desc.includes(term));
65
- });
66
- }
67
- // Score each tool
68
- const scored = candidateTools
69
- .map((tool) => {
70
- const parts = parseToolName(tool.name);
71
- const desc = (tool.config.function.description || "").toLowerCase();
72
- let score = 0;
73
- for (const term of allScoringTerms) {
74
- // Exact part match (high weight)
75
- if (parts.includes(term)) {
76
- score += tool.isMcp ? 12 : 10;
77
- }
78
- else if (parts.some((p) => p.includes(term))) {
79
- score += tool.isMcp ? 6 : 5;
80
- }
81
- // Full name fallback
82
- if (tool.name.toLowerCase().includes(term) && score === 0) {
83
- score += 3;
84
- }
85
- // Description match
86
- if (desc.includes(term)) {
87
- score += 2;
88
- }
89
- }
90
- return { tool, score };
91
- })
92
- .filter((s) => s.score > 0)
93
- .sort((a, b) => b.score - a.score)
94
- .slice(0, maxResults)
95
- .map((s) => s.tool);
96
- return scored;
97
- }
98
- export const toolSearchTool = {
99
- name: TOOL_SEARCH_TOOL_NAME,
100
- config: {
101
- type: "function",
102
- function: {
103
- name: TOOL_SEARCH_TOOL_NAME,
104
- description: `Fetches full schema definitions for deferred tools so they can be called.
105
-
106
- Deferred tools appear by name and description in <available-deferred-tools> messages. The full parameter schema is NOT loaded yet — use this tool to fetch it before invoking a deferred tool. This tool takes a query, matches it against the deferred tool list, and returns the matched tools' complete JSONSchema definitions inside a <functions> block. Once a tool's schema appears in that result, it is callable exactly like any tool defined at the top of the prompt.
107
-
108
- Result format: each matched tool appears as one <function>{"description": "...", "name": "...", "parameters": {...}}`,
109
- parameters: {
110
- type: "object",
111
- properties: {
112
- query: {
113
- type: "string",
114
- description: 'Search query for finding deferred tools. Supports: "select:ToolName" for direct lookup, or keyword search like "notebook jupyter". Use "+term" to require a term (e.g. "+slack send").',
115
- },
116
- max_results: {
117
- type: "number",
118
- description: "Maximum number of results to return for keyword search (default: 5).",
119
- },
120
- },
121
- required: ["query"],
122
- additionalProperties: false,
123
- },
124
- },
125
- },
126
- shouldDefer: false, // Always available
127
- execute: async (args, context) => {
128
- const { query, max_results = 5 } = args;
129
- if (!query) {
130
- return {
131
- success: false,
132
- content: "",
133
- error: "Missing required 'query' parameter",
134
- };
135
- }
136
- if (!context.toolManager) {
137
- return {
138
- success: false,
139
- content: "",
140
- error: "ToolManager not available in context",
141
- };
142
- }
143
- const allTools = context.toolManager.list();
144
- const deferredTools = allTools.filter(isDeferredTool);
145
- // Handle select: prefix
146
- const selectMatch = query.match(/^select:(.+)$/i);
147
- if (selectMatch) {
148
- const requested = selectMatch[1]
149
- .split(",")
150
- .map((s) => s.trim())
151
- .filter(Boolean);
152
- const found = [];
153
- const missing = [];
154
- for (const toolName of requested) {
155
- const tool = deferredTools.find((t) => t.name === toolName) ??
156
- allTools.find((t) => t.name === toolName);
157
- if (tool) {
158
- if (!found.some((f) => f.name === tool.name))
159
- found.push(tool);
160
- }
161
- else {
162
- missing.push(toolName);
163
- }
164
- }
165
- if (found.length === 0) {
166
- return {
167
- success: false,
168
- content: "",
169
- error: `No matching deferred tools found for: ${missing.join(", ")}`,
170
- };
171
- }
172
- const result = found.map(formatSchema).join("\n\n---\n\n");
173
- const shortResult = `Discovered tools: ${found.map((t) => t.name).join(", ")}`;
174
- return {
175
- success: true,
176
- content: result,
177
- shortResult,
178
- };
179
- }
180
- // Keyword search
181
- const matches = keywordSearch(query, deferredTools, max_results);
182
- if (matches.length === 0) {
183
- return {
184
- success: false,
185
- content: "",
186
- error: `No matching deferred tools found for query: "${query}". Available deferred tools: ${getDeferredToolNamesList(deferredTools)}`,
187
- };
188
- }
189
- const result = matches.map(formatSchema).join("\n\n---\n\n");
190
- const shortResult = `Found ${matches.length} tools: ${matches.map((t) => t.name).join(", ")}`;
191
- return {
192
- success: true,
193
- content: result,
194
- shortResult,
195
- };
196
- },
197
- };
198
- function getDeferredToolNamesList(tools) {
199
- return tools.map((t) => t.name).join(", ");
200
- }
@@ -1,19 +0,0 @@
1
- /**
2
- * Determines if a tool should be deferred (not sent to the API until discovered).
3
- *
4
- * A tool is deferred if:
5
- * - It has shouldDefer: true
6
- * - It is an MCP tool (isMcp: true)
7
- *
8
- * A tool is NEVER deferred if:
9
- * - It has alwaysLoad: true
10
- * - It is the ToolSearch tool itself (must always be available)
11
- */
12
- import type { ToolPlugin } from "../tools/types.js";
13
- export declare const TOOL_SEARCH_TOOL_NAME = "ToolSearch";
14
- export declare function isDeferredTool(tool: ToolPlugin): boolean;
15
- /**
16
- * Get the list of deferred tool names from a tools array.
17
- */
18
- export declare function getDeferredToolNames(tools: ToolPlugin[]): string[];
19
- //# sourceMappingURL=isDeferredTool.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"isDeferredTool.d.ts","sourceRoot":"","sources":["../../src/utils/isDeferredTool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,eAAO,MAAM,qBAAqB,eAAe,CAAC;AAElD,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAYxD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,EAAE,CAElE"}
@@ -1,31 +0,0 @@
1
- /**
2
- * Determines if a tool should be deferred (not sent to the API until discovered).
3
- *
4
- * A tool is deferred if:
5
- * - It has shouldDefer: true
6
- * - It is an MCP tool (isMcp: true)
7
- *
8
- * A tool is NEVER deferred if:
9
- * - It has alwaysLoad: true
10
- * - It is the ToolSearch tool itself (must always be available)
11
- */
12
- export const TOOL_SEARCH_TOOL_NAME = "ToolSearch";
13
- export function isDeferredTool(tool) {
14
- // Never defer if explicitly marked as alwaysLoad
15
- if (tool.alwaysLoad === true)
16
- return false;
17
- // Never defer ToolSearch itself — the model needs it to discover other tools
18
- if (tool.name === TOOL_SEARCH_TOOL_NAME)
19
- return false;
20
- // MCP tools are always deferred (workflow-specific, potentially many)
21
- if (tool.isMcp === true)
22
- return true;
23
- // Defer if marked with shouldDefer flag
24
- return tool.shouldDefer === true;
25
- }
26
- /**
27
- * Get the list of deferred tool names from a tools array.
28
- */
29
- export function getDeferredToolNames(tools) {
30
- return tools.filter(isDeferredTool).map((t) => t.name);
31
- }