@wingman-ai/gateway 0.4.2 → 0.4.4

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 (160) hide show
  1. package/README.md +14 -0
  2. package/dist/agent/config/mcpClientManager.cjs +104 -1
  3. package/dist/agent/config/mcpClientManager.d.ts +30 -0
  4. package/dist/agent/config/mcpClientManager.js +104 -1
  5. package/dist/agent/config/modelFactory.cjs +10 -0
  6. package/dist/agent/config/modelFactory.js +10 -0
  7. package/dist/agent/config/xaiImageModel.cjs +242 -0
  8. package/dist/agent/config/xaiImageModel.d.ts +33 -0
  9. package/dist/agent/config/xaiImageModel.js +202 -0
  10. package/dist/agent/tests/mcpClientManager.test.cjs +116 -0
  11. package/dist/agent/tests/mcpClientManager.test.js +117 -1
  12. package/dist/agent/tests/mcpResourceTools.test.cjs +101 -0
  13. package/dist/agent/tests/mcpResourceTools.test.d.ts +1 -0
  14. package/dist/agent/tests/mcpResourceTools.test.js +95 -0
  15. package/dist/agent/tests/modelFactory.test.cjs +16 -2
  16. package/dist/agent/tests/modelFactory.test.js +16 -2
  17. package/dist/agent/tests/xaiImageModel.test.cjs +194 -0
  18. package/dist/agent/tests/xaiImageModel.test.d.ts +1 -0
  19. package/dist/agent/tests/xaiImageModel.test.js +188 -0
  20. package/dist/agent/tools/mcp_resources.cjs +111 -0
  21. package/dist/agent/tools/mcp_resources.d.ts +3 -0
  22. package/dist/agent/tools/mcp_resources.js +77 -0
  23. package/dist/bench/adapters/commandAdapter.cjs +93 -0
  24. package/dist/bench/adapters/commandAdapter.d.ts +6 -0
  25. package/dist/bench/adapters/commandAdapter.js +59 -0
  26. package/dist/bench/adapters/helpers.cjs +170 -0
  27. package/dist/bench/adapters/helpers.d.ts +7 -0
  28. package/dist/bench/adapters/helpers.js +133 -0
  29. package/dist/bench/adapters/index.cjs +41 -0
  30. package/dist/bench/adapters/index.d.ts +2 -0
  31. package/dist/bench/adapters/index.js +7 -0
  32. package/dist/bench/adapters/wingmanCliAdapter.cjs +100 -0
  33. package/dist/bench/adapters/wingmanCliAdapter.d.ts +6 -0
  34. package/dist/bench/adapters/wingmanCliAdapter.js +66 -0
  35. package/dist/bench/cleanup.cjs +122 -0
  36. package/dist/bench/cleanup.d.ts +9 -0
  37. package/dist/bench/cleanup.js +85 -0
  38. package/dist/bench/config.cjs +190 -0
  39. package/dist/bench/config.d.ts +2 -0
  40. package/dist/bench/config.js +156 -0
  41. package/dist/bench/index.cjs +43 -0
  42. package/dist/bench/index.d.ts +3 -0
  43. package/dist/bench/index.js +3 -0
  44. package/dist/bench/official.cjs +616 -0
  45. package/dist/bench/official.d.ts +80 -0
  46. package/dist/bench/official.js +546 -0
  47. package/dist/bench/officialCli.cjs +204 -0
  48. package/dist/bench/officialCli.d.ts +5 -0
  49. package/dist/bench/officialCli.js +170 -0
  50. package/dist/bench/process.cjs +78 -0
  51. package/dist/bench/process.d.ts +14 -0
  52. package/dist/bench/process.js +44 -0
  53. package/dist/bench/runner.cjs +237 -0
  54. package/dist/bench/runner.d.ts +7 -0
  55. package/dist/bench/runner.js +197 -0
  56. package/dist/bench/scoring.cjs +171 -0
  57. package/dist/bench/scoring.d.ts +9 -0
  58. package/dist/bench/scoring.js +137 -0
  59. package/dist/bench/types.cjs +18 -0
  60. package/dist/bench/types.d.ts +200 -0
  61. package/dist/bench/types.js +0 -0
  62. package/dist/bench/validator.cjs +92 -0
  63. package/dist/bench/validator.d.ts +2 -0
  64. package/dist/bench/validator.js +58 -0
  65. package/dist/cli/config/schema.cjs +36 -1
  66. package/dist/cli/config/schema.d.ts +46 -0
  67. package/dist/cli/config/schema.js +36 -1
  68. package/dist/cli/config/warnings.cjs +119 -51
  69. package/dist/cli/config/warnings.js +119 -51
  70. package/dist/cli/core/agentInvoker.cjs +9 -2
  71. package/dist/cli/core/agentInvoker.d.ts +1 -0
  72. package/dist/cli/core/agentInvoker.js +9 -2
  73. package/dist/cli/core/imagePersistence.cjs +17 -1
  74. package/dist/cli/core/imagePersistence.d.ts +2 -0
  75. package/dist/cli/core/imagePersistence.js +13 -3
  76. package/dist/cli/core/sessionManager.cjs +2 -0
  77. package/dist/cli/core/sessionManager.js +3 -1
  78. package/dist/cli/types.d.ts +18 -0
  79. package/dist/gateway/adapters/teams.cjs +419 -0
  80. package/dist/gateway/adapters/teams.d.ts +47 -0
  81. package/dist/gateway/adapters/teams.js +361 -0
  82. package/dist/gateway/http/sms.cjs +286 -0
  83. package/dist/gateway/http/sms.d.ts +4 -0
  84. package/dist/gateway/http/sms.js +249 -0
  85. package/dist/gateway/server.cjs +54 -3
  86. package/dist/gateway/server.d.ts +2 -0
  87. package/dist/gateway/server.js +54 -3
  88. package/dist/gateway/sms/commands.cjs +116 -0
  89. package/dist/gateway/sms/commands.d.ts +15 -0
  90. package/dist/gateway/sms/commands.js +79 -0
  91. package/dist/gateway/sms/control.cjs +118 -0
  92. package/dist/gateway/sms/control.d.ts +18 -0
  93. package/dist/gateway/sms/control.js +84 -0
  94. package/dist/gateway/sms/policyStore.cjs +198 -0
  95. package/dist/gateway/sms/policyStore.d.ts +37 -0
  96. package/dist/gateway/sms/policyStore.js +161 -0
  97. package/dist/providers/registry.cjs +1 -0
  98. package/dist/providers/registry.js +1 -0
  99. package/dist/tests/cli-config-warnings.test.cjs +41 -0
  100. package/dist/tests/cli-config-warnings.test.js +41 -0
  101. package/dist/tests/cli-init.test.cjs +32 -26
  102. package/dist/tests/cli-init.test.js +32 -26
  103. package/dist/tests/gateway-http-security.test.cjs +21 -0
  104. package/dist/tests/gateway-http-security.test.js +21 -0
  105. package/dist/tests/gateway-origin-policy.test.cjs +22 -0
  106. package/dist/tests/gateway-origin-policy.test.js +22 -0
  107. package/dist/tests/gateway.test.cjs +57 -0
  108. package/dist/tests/gateway.test.js +57 -0
  109. package/dist/tests/imagePersistence.test.cjs +26 -0
  110. package/dist/tests/imagePersistence.test.js +27 -1
  111. package/dist/tests/run-terminal-bench-official-script.test.cjs +61 -0
  112. package/dist/tests/run-terminal-bench-official-script.test.d.ts +1 -0
  113. package/dist/tests/run-terminal-bench-official-script.test.js +55 -0
  114. package/dist/tests/sessions-api.test.cjs +69 -1
  115. package/dist/tests/sessions-api.test.js +70 -2
  116. package/dist/tests/sms-api.test.cjs +183 -0
  117. package/dist/tests/sms-api.test.d.ts +1 -0
  118. package/dist/tests/sms-api.test.js +177 -0
  119. package/dist/tests/sms-commands.test.cjs +90 -0
  120. package/dist/tests/sms-commands.test.d.ts +1 -0
  121. package/dist/tests/sms-commands.test.js +84 -0
  122. package/dist/tests/sms-policy-store.test.cjs +69 -0
  123. package/dist/tests/sms-policy-store.test.d.ts +1 -0
  124. package/dist/tests/sms-policy-store.test.js +63 -0
  125. package/dist/tests/teams-adapter.test.cjs +58 -0
  126. package/dist/tests/teams-adapter.test.d.ts +1 -0
  127. package/dist/tests/teams-adapter.test.js +52 -0
  128. package/dist/tests/terminal-bench-adapters-helpers.test.cjs +64 -0
  129. package/dist/tests/terminal-bench-adapters-helpers.test.d.ts +1 -0
  130. package/dist/tests/terminal-bench-adapters-helpers.test.js +58 -0
  131. package/dist/tests/terminal-bench-cleanup.test.cjs +93 -0
  132. package/dist/tests/terminal-bench-cleanup.test.d.ts +1 -0
  133. package/dist/tests/terminal-bench-cleanup.test.js +87 -0
  134. package/dist/tests/terminal-bench-config.test.cjs +62 -0
  135. package/dist/tests/terminal-bench-config.test.d.ts +1 -0
  136. package/dist/tests/terminal-bench-config.test.js +56 -0
  137. package/dist/tests/terminal-bench-official.test.cjs +194 -0
  138. package/dist/tests/terminal-bench-official.test.d.ts +1 -0
  139. package/dist/tests/terminal-bench-official.test.js +188 -0
  140. package/dist/tests/terminal-bench-runner.test.cjs +82 -0
  141. package/dist/tests/terminal-bench-runner.test.d.ts +1 -0
  142. package/dist/tests/terminal-bench-runner.test.js +76 -0
  143. package/dist/tests/terminal-bench-scoring.test.cjs +128 -0
  144. package/dist/tests/terminal-bench-scoring.test.d.ts +1 -0
  145. package/dist/tests/terminal-bench-scoring.test.js +122 -0
  146. package/dist/tools/mcp-fal-ai.cjs +1 -1
  147. package/dist/tools/mcp-fal-ai.js +1 -1
  148. package/dist/webui/assets/index-Cyg_Hs57.css +11 -0
  149. package/dist/webui/assets/{index-BMekSELC.js → index-DZXLLjaA.js} +109 -109
  150. package/dist/webui/index.html +2 -2
  151. package/package.json +11 -2
  152. package/templates/agents/game-dev/agent.md +110 -63
  153. package/templates/agents/game-dev/art-director.md +106 -0
  154. package/templates/agents/game-dev/game-designer.md +87 -0
  155. package/templates/agents/game-dev/scene-engineer.md +474 -0
  156. package/dist/webui/assets/index-Cwkg4DKj.css +0 -11
  157. package/templates/agents/game-dev/art-generation.md +0 -38
  158. package/templates/agents/game-dev/asset-refinement.md +0 -17
  159. package/templates/agents/game-dev/planning-idea.md +0 -17
  160. package/templates/agents/game-dev/ui-specialist.md +0 -17
@@ -0,0 +1,202 @@
1
+ import { BaseChatModel } from "@langchain/core/language_models/chat_models";
2
+ import { AIMessage, AIMessageChunk } from "@langchain/core/messages";
3
+ import { ChatGenerationChunk } from "@langchain/core/outputs";
4
+ function _define_property(obj, key, value) {
5
+ if (key in obj) Object.defineProperty(obj, key, {
6
+ value: value,
7
+ enumerable: true,
8
+ configurable: true,
9
+ writable: true
10
+ });
11
+ else obj[key] = value;
12
+ return obj;
13
+ }
14
+ const DEFAULT_XAI_BASE_URL = "https://api.x.ai/v1";
15
+ const IMAGE_MODEL_NAME_PATTERN = /^grok-imagine-image(?:[-:._].*)?$/i;
16
+ const XAI_IMAGE_PROMPT_MAX_CHARS = 7900;
17
+ function isNativeXAIImageModel(modelName) {
18
+ return IMAGE_MODEL_NAME_PATTERN.test(modelName.trim());
19
+ }
20
+ function normalizeContentText(content) {
21
+ if ("string" == typeof content) return content.trim();
22
+ if (!Array.isArray(content)) return "";
23
+ const parts = content.map((part)=>{
24
+ if (!part || "object" != typeof part || Array.isArray(part)) return "";
25
+ const record = part;
26
+ return "text" === record.type && "string" == typeof record.text ? record.text.trim() : "";
27
+ }).filter(Boolean);
28
+ return parts.join("\n").trim();
29
+ }
30
+ function normalizeRole(message) {
31
+ const type = "string" == typeof message.type ? message.type : "function" == typeof message._getType ? String(message._getType() ?? "").toLowerCase() : "";
32
+ return type.toLowerCase();
33
+ }
34
+ function resolveAdditionalKwargs(message) {
35
+ const direct = message.additional_kwargs;
36
+ if (direct && "object" == typeof direct && !Array.isArray(direct)) return direct;
37
+ const kwargs = message.kwargs;
38
+ if (kwargs && "object" == typeof kwargs && !Array.isArray(kwargs)) {
39
+ const nested = kwargs.additional_kwargs;
40
+ if (nested && "object" == typeof nested && !Array.isArray(nested)) return nested;
41
+ }
42
+ }
43
+ function isHiddenMiddlewarePrompt(message, text) {
44
+ const additionalKwargs = resolveAdditionalKwargs(message);
45
+ const uiHidden = additionalKwargs?.ui_hidden === true || additionalKwargs?.uiHidden === true;
46
+ if (uiHidden) return true;
47
+ const source = "string" == typeof additionalKwargs?.source ? additionalKwargs.source.toLowerCase() : "";
48
+ if ("additional-message-middleware" === source) return true;
49
+ const normalized = text.toLowerCase();
50
+ return normalized.includes("# confidentiality (internal)") && normalized.includes("current date time (utc)");
51
+ }
52
+ function clampPromptLength(prompt) {
53
+ if (prompt.length <= XAI_IMAGE_PROMPT_MAX_CHARS) return prompt;
54
+ return prompt.slice(0, XAI_IMAGE_PROMPT_MAX_CHARS);
55
+ }
56
+ function buildPrompt(messages) {
57
+ const systemParts = [];
58
+ const visibleUserParts = [];
59
+ const fallbackUserParts = [];
60
+ for (const message of messages){
61
+ const text = normalizeContentText(message.content);
62
+ if (!text) continue;
63
+ const role = normalizeRole(message);
64
+ if ("system" === role) {
65
+ systemParts.push(text);
66
+ continue;
67
+ }
68
+ if ("human" === role || "user" === role) {
69
+ fallbackUserParts.push(text);
70
+ if (!isHiddenMiddlewarePrompt(message, text)) visibleUserParts.push(text);
71
+ }
72
+ }
73
+ const latestUserPrompt = visibleUserParts[visibleUserParts.length - 1] || fallbackUserParts[fallbackUserParts.length - 1];
74
+ if (!latestUserPrompt) throw new Error("xAI image generation requires a user prompt.");
75
+ const systemPrefix = systemParts.join("\n\n").trim();
76
+ if (!systemPrefix) return clampPromptLength(latestUserPrompt);
77
+ const userSection = `User request:\n${latestUserPrompt}`;
78
+ const withSystem = `System instructions:\n${systemPrefix}\n\n${userSection}`;
79
+ if (withSystem.length <= XAI_IMAGE_PROMPT_MAX_CHARS) return withSystem;
80
+ const systemPrefixLabel = "System instructions:\n";
81
+ const userSectionPrefix = `\n\n${userSection}`;
82
+ const availableSystemChars = XAI_IMAGE_PROMPT_MAX_CHARS - systemPrefixLabel.length - userSectionPrefix.length;
83
+ if (availableSystemChars <= 0) return clampPromptLength(userSection);
84
+ const truncatedSystem = systemPrefix.slice(0, availableSystemChars);
85
+ return `${systemPrefixLabel}${truncatedSystem}${userSectionPrefix}`;
86
+ }
87
+ async function readErrorResponse(response) {
88
+ const body = await response.text();
89
+ if (!body.trim()) return `xAI image generation request failed with status ${response.status}.`;
90
+ try {
91
+ const parsed = JSON.parse(body);
92
+ const errorRecord = parsed.error && "object" == typeof parsed.error ? parsed.error : void 0;
93
+ const message = "string" == typeof errorRecord?.message && errorRecord.message || "string" == typeof parsed.message && parsed.message || "string" == typeof parsed.error && parsed.error;
94
+ if (message) return `xAI image generation failed: ${message}`;
95
+ } catch {}
96
+ return `xAI image generation failed: ${body}`;
97
+ }
98
+ class NativeXAIImageModel extends BaseChatModel {
99
+ bindTools(_tools, _options) {
100
+ return this;
101
+ }
102
+ _llmType() {
103
+ return "xai-image-native";
104
+ }
105
+ getLsParams() {
106
+ return {
107
+ ls_provider: "xai",
108
+ ls_model_name: this.model,
109
+ ls_model_type: "chat"
110
+ };
111
+ }
112
+ invocationParams() {
113
+ return {
114
+ provider: "xai",
115
+ model: this.model,
116
+ baseURL: this.baseURL,
117
+ responseFormat: this.responseFormat,
118
+ size: this.size
119
+ };
120
+ }
121
+ async _generate(messages, options, _runManager) {
122
+ if (!this.apiKey) throw new Error("Missing xAI credentials. Configure XAI_API_KEY before using grok-imagine-image.");
123
+ const prompt = buildPrompt(messages);
124
+ const payload = {
125
+ model: this.model,
126
+ prompt,
127
+ response_format: this.responseFormat
128
+ };
129
+ if (this.size) payload.size = this.size;
130
+ const response = await this.caller.callWithOptions({
131
+ signal: options.signal
132
+ }, async ()=>fetch(`${this.baseURL}/images/generations`, {
133
+ method: "POST",
134
+ headers: {
135
+ Authorization: `Bearer ${this.apiKey}`,
136
+ "Content-Type": "application/json"
137
+ },
138
+ body: JSON.stringify(payload),
139
+ signal: options.signal
140
+ }));
141
+ if (!response.ok) throw new Error(await readErrorResponse(response));
142
+ const data = await response.json();
143
+ const first = Array.isArray(data.data) ? data.data[0] : void 0;
144
+ if (!first) throw new Error("xAI image generation returned no image data.");
145
+ const imageUrl = "string" == typeof first.url && first.url.trim() ? first.url.trim() : "string" == typeof first.b64_json && first.b64_json.trim() ? `data:${first.mime_type || "image/png"};base64,${first.b64_json.trim()}` : "";
146
+ if (!imageUrl) throw new Error("xAI image generation response was missing both url and b64_json.");
147
+ const confirmationText = "Image generated.";
148
+ const message = new AIMessage({
149
+ content: [
150
+ {
151
+ type: "text",
152
+ text: confirmationText
153
+ },
154
+ {
155
+ type: "output_image",
156
+ image_url: imageUrl
157
+ }
158
+ ],
159
+ response_metadata: {
160
+ model: this.model,
161
+ revised_prompt: first.revised_prompt
162
+ }
163
+ });
164
+ return {
165
+ generations: [
166
+ {
167
+ text: confirmationText,
168
+ message
169
+ }
170
+ ],
171
+ llmOutput: {
172
+ model: this.model
173
+ }
174
+ };
175
+ }
176
+ async *_streamResponseChunks(messages, options, runManager) {
177
+ const result = await this._generate(messages, options, runManager);
178
+ const generation = result.generations[0];
179
+ if (!generation) return;
180
+ const aiMessage = generation.message;
181
+ const chunk = new AIMessageChunk({
182
+ content: aiMessage.content,
183
+ additional_kwargs: aiMessage.additional_kwargs,
184
+ response_metadata: aiMessage.response_metadata
185
+ });
186
+ if (generation.text) await runManager?.handleLLMNewToken(generation.text);
187
+ yield new ChatGenerationChunk({
188
+ text: generation.text || "",
189
+ message: chunk,
190
+ generationInfo: generation.generationInfo
191
+ });
192
+ }
193
+ constructor(fields){
194
+ super(fields), _define_property(this, "model", void 0), _define_property(this, "apiKey", void 0), _define_property(this, "baseURL", void 0), _define_property(this, "size", void 0), _define_property(this, "responseFormat", void 0);
195
+ this.model = fields.model;
196
+ this.apiKey = fields.apiKey;
197
+ this.baseURL = (fields.baseURL || DEFAULT_XAI_BASE_URL).replace(/\/+$/, "");
198
+ this.size = fields.size;
199
+ this.responseFormat = fields.responseFormat || "url";
200
+ }
201
+ }
202
+ export { DEFAULT_XAI_BASE_URL, NativeXAIImageModel, isNativeXAIImageModel };
@@ -167,6 +167,122 @@ const getClientConfig = (manager)=>manager.buildClientConfig();
167
167
  (0, external_vitest_namespaceObject.expect)(server.env.INVARIANT_API_URL).toBe("https://explorer.invariantlabs.ai");
168
168
  (0, external_vitest_namespaceObject.expect)(server.env.GUARDRAILS_API_URL).toBe("https://explorer.invariantlabs.ai");
169
169
  });
170
+ (0, external_vitest_namespaceObject.it)("keeps MCP multimodal output handling in artifact mode", ()=>{
171
+ const configs = [
172
+ {
173
+ servers: [
174
+ {
175
+ name: "fal-ai",
176
+ transport: "stdio",
177
+ command: "bun",
178
+ args: [
179
+ "run",
180
+ "src/tools/mcp-fal-ai.ts"
181
+ ]
182
+ }
183
+ ]
184
+ }
185
+ ];
186
+ const manager = new mcpClientManager_cjs_namespaceObject.MCPClientManager(configs, testLogger);
187
+ const clientConfig = getClientConfig(manager);
188
+ (0, external_vitest_namespaceObject.expect)(clientConfig.outputHandling).toEqual({
189
+ image: "artifact",
190
+ audio: "artifact",
191
+ resource: "artifact"
192
+ });
193
+ });
194
+ (0, external_vitest_namespaceObject.it)("lists resources from MCP client", async ()=>{
195
+ const listResources = external_vitest_namespaceObject.vi.fn().mockResolvedValue({
196
+ fal: [
197
+ {
198
+ uri: "fal://jobs/123",
199
+ name: "Job 123",
200
+ description: "Generated asset",
201
+ mimeType: "application/json"
202
+ }
203
+ ]
204
+ });
205
+ const manager = new mcpClientManager_cjs_namespaceObject.MCPClientManager([], testLogger);
206
+ manager.client = {
207
+ listResources
208
+ };
209
+ const result = await manager.listResources([
210
+ "fal"
211
+ ]);
212
+ (0, external_vitest_namespaceObject.expect)(listResources).toHaveBeenCalledWith("fal");
213
+ (0, external_vitest_namespaceObject.expect)(result).toEqual({
214
+ fal: [
215
+ {
216
+ uri: "fal://jobs/123",
217
+ name: "Job 123",
218
+ description: "Generated asset",
219
+ mimeType: "application/json"
220
+ }
221
+ ]
222
+ });
223
+ });
224
+ (0, external_vitest_namespaceObject.it)("lists resource templates from MCP client", async ()=>{
225
+ const listResourceTemplates = external_vitest_namespaceObject.vi.fn().mockResolvedValue({
226
+ fal: [
227
+ {
228
+ uriTemplate: "fal://jobs/{jobId}",
229
+ name: "Fal Job",
230
+ description: "Job by ID",
231
+ mimeType: "application/json"
232
+ }
233
+ ]
234
+ });
235
+ const manager = new mcpClientManager_cjs_namespaceObject.MCPClientManager([], testLogger);
236
+ manager.client = {
237
+ listResourceTemplates
238
+ };
239
+ const result = await manager.listResourceTemplates([
240
+ "fal"
241
+ ]);
242
+ (0, external_vitest_namespaceObject.expect)(listResourceTemplates).toHaveBeenCalledWith("fal");
243
+ (0, external_vitest_namespaceObject.expect)(result).toEqual({
244
+ fal: [
245
+ {
246
+ uriTemplate: "fal://jobs/{jobId}",
247
+ name: "Fal Job",
248
+ description: "Job by ID",
249
+ mimeType: "application/json"
250
+ }
251
+ ]
252
+ });
253
+ });
254
+ (0, external_vitest_namespaceObject.it)("reads resources from MCP client", async ()=>{
255
+ const readResource = external_vitest_namespaceObject.vi.fn().mockResolvedValue([
256
+ {
257
+ uri: "fal://jobs/123",
258
+ mimeType: "application/json",
259
+ text: '{"status":"completed"}'
260
+ }
261
+ ]);
262
+ const manager = new mcpClientManager_cjs_namespaceObject.MCPClientManager([], testLogger);
263
+ manager.client = {
264
+ readResource
265
+ };
266
+ const result = await manager.readResource("fal", "fal://jobs/123");
267
+ (0, external_vitest_namespaceObject.expect)(readResource).toHaveBeenCalledWith("fal", "fal://jobs/123");
268
+ (0, external_vitest_namespaceObject.expect)(result).toEqual([
269
+ {
270
+ uri: "fal://jobs/123",
271
+ mimeType: "application/json",
272
+ text: '{"status":"completed"}'
273
+ }
274
+ ]);
275
+ });
276
+ (0, external_vitest_namespaceObject.it)("calls close on cleanup", async ()=>{
277
+ const close = external_vitest_namespaceObject.vi.fn().mockResolvedValue(void 0);
278
+ const manager = new mcpClientManager_cjs_namespaceObject.MCPClientManager([], testLogger);
279
+ manager.client = {
280
+ close
281
+ };
282
+ await manager.cleanup();
283
+ (0, external_vitest_namespaceObject.expect)(close).toHaveBeenCalledTimes(1);
284
+ (0, external_vitest_namespaceObject.expect)(manager.client).toBeNull();
285
+ });
170
286
  });
171
287
  for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
172
288
  Object.defineProperty(exports, '__esModule', {
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from "vitest";
1
+ import { describe, expect, it, vi } from "vitest";
2
2
  import { MCPClientManager } from "../config/mcpClientManager.js";
3
3
  const testLogger = {
4
4
  debug: ()=>{},
@@ -165,4 +165,120 @@ describe("MCPClientManager runtime env", ()=>{
165
165
  expect(server.env.INVARIANT_API_URL).toBe("https://explorer.invariantlabs.ai");
166
166
  expect(server.env.GUARDRAILS_API_URL).toBe("https://explorer.invariantlabs.ai");
167
167
  });
168
+ it("keeps MCP multimodal output handling in artifact mode", ()=>{
169
+ const configs = [
170
+ {
171
+ servers: [
172
+ {
173
+ name: "fal-ai",
174
+ transport: "stdio",
175
+ command: "bun",
176
+ args: [
177
+ "run",
178
+ "src/tools/mcp-fal-ai.ts"
179
+ ]
180
+ }
181
+ ]
182
+ }
183
+ ];
184
+ const manager = new MCPClientManager(configs, testLogger);
185
+ const clientConfig = getClientConfig(manager);
186
+ expect(clientConfig.outputHandling).toEqual({
187
+ image: "artifact",
188
+ audio: "artifact",
189
+ resource: "artifact"
190
+ });
191
+ });
192
+ it("lists resources from MCP client", async ()=>{
193
+ const listResources = vi.fn().mockResolvedValue({
194
+ fal: [
195
+ {
196
+ uri: "fal://jobs/123",
197
+ name: "Job 123",
198
+ description: "Generated asset",
199
+ mimeType: "application/json"
200
+ }
201
+ ]
202
+ });
203
+ const manager = new MCPClientManager([], testLogger);
204
+ manager.client = {
205
+ listResources
206
+ };
207
+ const result = await manager.listResources([
208
+ "fal"
209
+ ]);
210
+ expect(listResources).toHaveBeenCalledWith("fal");
211
+ expect(result).toEqual({
212
+ fal: [
213
+ {
214
+ uri: "fal://jobs/123",
215
+ name: "Job 123",
216
+ description: "Generated asset",
217
+ mimeType: "application/json"
218
+ }
219
+ ]
220
+ });
221
+ });
222
+ it("lists resource templates from MCP client", async ()=>{
223
+ const listResourceTemplates = vi.fn().mockResolvedValue({
224
+ fal: [
225
+ {
226
+ uriTemplate: "fal://jobs/{jobId}",
227
+ name: "Fal Job",
228
+ description: "Job by ID",
229
+ mimeType: "application/json"
230
+ }
231
+ ]
232
+ });
233
+ const manager = new MCPClientManager([], testLogger);
234
+ manager.client = {
235
+ listResourceTemplates
236
+ };
237
+ const result = await manager.listResourceTemplates([
238
+ "fal"
239
+ ]);
240
+ expect(listResourceTemplates).toHaveBeenCalledWith("fal");
241
+ expect(result).toEqual({
242
+ fal: [
243
+ {
244
+ uriTemplate: "fal://jobs/{jobId}",
245
+ name: "Fal Job",
246
+ description: "Job by ID",
247
+ mimeType: "application/json"
248
+ }
249
+ ]
250
+ });
251
+ });
252
+ it("reads resources from MCP client", async ()=>{
253
+ const readResource = vi.fn().mockResolvedValue([
254
+ {
255
+ uri: "fal://jobs/123",
256
+ mimeType: "application/json",
257
+ text: '{"status":"completed"}'
258
+ }
259
+ ]);
260
+ const manager = new MCPClientManager([], testLogger);
261
+ manager.client = {
262
+ readResource
263
+ };
264
+ const result = await manager.readResource("fal", "fal://jobs/123");
265
+ expect(readResource).toHaveBeenCalledWith("fal", "fal://jobs/123");
266
+ expect(result).toEqual([
267
+ {
268
+ uri: "fal://jobs/123",
269
+ mimeType: "application/json",
270
+ text: '{"status":"completed"}'
271
+ }
272
+ ]);
273
+ });
274
+ it("calls close on cleanup", async ()=>{
275
+ const close = vi.fn().mockResolvedValue(void 0);
276
+ const manager = new MCPClientManager([], testLogger);
277
+ manager.client = {
278
+ close
279
+ };
280
+ await manager.cleanup();
281
+ expect(close).toHaveBeenCalledTimes(1);
282
+ expect(manager.client).toBeNull();
283
+ });
168
284
  });
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ var __webpack_exports__ = {};
3
+ const external_vitest_namespaceObject = require("vitest");
4
+ const mcp_resources_cjs_namespaceObject = require("../tools/mcp_resources.cjs");
5
+ const createManagerStub = ()=>{
6
+ const listResources = external_vitest_namespaceObject.vi.fn();
7
+ const listResourceTemplates = external_vitest_namespaceObject.vi.fn();
8
+ const readResource = external_vitest_namespaceObject.vi.fn();
9
+ const manager = {
10
+ listResources,
11
+ listResourceTemplates,
12
+ readResource
13
+ };
14
+ return {
15
+ manager,
16
+ listResources,
17
+ listResourceTemplates,
18
+ readResource
19
+ };
20
+ };
21
+ (0, external_vitest_namespaceObject.describe)("mcp resource tools", ()=>{
22
+ (0, external_vitest_namespaceObject.it)("lists resources and templates", async ()=>{
23
+ const { manager, listResources, listResourceTemplates } = createManagerStub();
24
+ listResources.mockResolvedValue({
25
+ fal: [
26
+ {
27
+ uri: "fal://jobs/1",
28
+ name: "Job 1",
29
+ mimeType: "application/json"
30
+ }
31
+ ]
32
+ });
33
+ listResourceTemplates.mockResolvedValue({
34
+ fal: [
35
+ {
36
+ uriTemplate: "fal://jobs/{id}",
37
+ name: "Job by id"
38
+ }
39
+ ]
40
+ });
41
+ const [listTool] = (0, mcp_resources_cjs_namespaceObject.createMCPResourceTools)(manager);
42
+ const output = await listTool.invoke({
43
+ includeTemplates: true
44
+ });
45
+ const parsed = JSON.parse(String(output));
46
+ (0, external_vitest_namespaceObject.expect)(listResources).toHaveBeenCalledWith([]);
47
+ (0, external_vitest_namespaceObject.expect)(listResourceTemplates).toHaveBeenCalledWith([]);
48
+ (0, external_vitest_namespaceObject.expect)(parsed.resourceCount).toBe(1);
49
+ (0, external_vitest_namespaceObject.expect)(parsed.resourceTemplateCount).toBe(1);
50
+ (0, external_vitest_namespaceObject.expect)(parsed.resources.fal[0].uri).toBe("fal://jobs/1");
51
+ });
52
+ (0, external_vitest_namespaceObject.it)("reads resource content without inlining binary blobs by default", async ()=>{
53
+ const { manager, readResource } = createManagerStub();
54
+ readResource.mockResolvedValue([
55
+ {
56
+ uri: "fal://jobs/1",
57
+ mimeType: "application/json",
58
+ text: "abcdefghijklmnopqrstuvwxyz",
59
+ blob: "YWJjZA=="
60
+ }
61
+ ]);
62
+ const tools = (0, mcp_resources_cjs_namespaceObject.createMCPResourceTools)(manager);
63
+ const readTool = tools.find((entry)=>"mcp_read_resource" === entry.name);
64
+ if (!readTool) throw new Error("mcp_read_resource tool missing");
65
+ const output = await readTool.invoke({
66
+ server: "fal",
67
+ uri: "fal://jobs/1",
68
+ maxTextChars: 10
69
+ });
70
+ const parsed = JSON.parse(String(output));
71
+ (0, external_vitest_namespaceObject.expect)(readResource).toHaveBeenCalledWith("fal", "fal://jobs/1");
72
+ (0, external_vitest_namespaceObject.expect)(parsed.content[0].text).toBe("abcdefghij");
73
+ (0, external_vitest_namespaceObject.expect)(parsed.content[0].truncated).toBe(true);
74
+ (0, external_vitest_namespaceObject.expect)(parsed.content[0].blobLength).toBe(8);
75
+ (0, external_vitest_namespaceObject.expect)(parsed.content[0].blob).toBeUndefined();
76
+ });
77
+ (0, external_vitest_namespaceObject.it)("can include binary blobs when requested", async ()=>{
78
+ const { manager, readResource } = createManagerStub();
79
+ readResource.mockResolvedValue([
80
+ {
81
+ uri: "fal://jobs/2",
82
+ mimeType: "image/png",
83
+ blob: "YWJjZA=="
84
+ }
85
+ ]);
86
+ const tools = (0, mcp_resources_cjs_namespaceObject.createMCPResourceTools)(manager);
87
+ const readTool = tools.find((entry)=>"mcp_read_resource" === entry.name);
88
+ if (!readTool) throw new Error("mcp_read_resource tool missing");
89
+ const output = await readTool.invoke({
90
+ server: "fal",
91
+ uri: "fal://jobs/2",
92
+ includeBinary: true
93
+ });
94
+ const parsed = JSON.parse(String(output));
95
+ (0, external_vitest_namespaceObject.expect)(parsed.content[0].blob).toBe("YWJjZA==");
96
+ });
97
+ });
98
+ for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
99
+ Object.defineProperty(exports, '__esModule', {
100
+ value: true
101
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,95 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { createMCPResourceTools } from "../tools/mcp_resources.js";
3
+ const createManagerStub = ()=>{
4
+ const listResources = vi.fn();
5
+ const listResourceTemplates = vi.fn();
6
+ const readResource = vi.fn();
7
+ const manager = {
8
+ listResources,
9
+ listResourceTemplates,
10
+ readResource
11
+ };
12
+ return {
13
+ manager,
14
+ listResources,
15
+ listResourceTemplates,
16
+ readResource
17
+ };
18
+ };
19
+ describe("mcp resource tools", ()=>{
20
+ it("lists resources and templates", async ()=>{
21
+ const { manager, listResources, listResourceTemplates } = createManagerStub();
22
+ listResources.mockResolvedValue({
23
+ fal: [
24
+ {
25
+ uri: "fal://jobs/1",
26
+ name: "Job 1",
27
+ mimeType: "application/json"
28
+ }
29
+ ]
30
+ });
31
+ listResourceTemplates.mockResolvedValue({
32
+ fal: [
33
+ {
34
+ uriTemplate: "fal://jobs/{id}",
35
+ name: "Job by id"
36
+ }
37
+ ]
38
+ });
39
+ const [listTool] = createMCPResourceTools(manager);
40
+ const output = await listTool.invoke({
41
+ includeTemplates: true
42
+ });
43
+ const parsed = JSON.parse(String(output));
44
+ expect(listResources).toHaveBeenCalledWith([]);
45
+ expect(listResourceTemplates).toHaveBeenCalledWith([]);
46
+ expect(parsed.resourceCount).toBe(1);
47
+ expect(parsed.resourceTemplateCount).toBe(1);
48
+ expect(parsed.resources.fal[0].uri).toBe("fal://jobs/1");
49
+ });
50
+ it("reads resource content without inlining binary blobs by default", async ()=>{
51
+ const { manager, readResource } = createManagerStub();
52
+ readResource.mockResolvedValue([
53
+ {
54
+ uri: "fal://jobs/1",
55
+ mimeType: "application/json",
56
+ text: "abcdefghijklmnopqrstuvwxyz",
57
+ blob: "YWJjZA=="
58
+ }
59
+ ]);
60
+ const tools = createMCPResourceTools(manager);
61
+ const readTool = tools.find((entry)=>"mcp_read_resource" === entry.name);
62
+ if (!readTool) throw new Error("mcp_read_resource tool missing");
63
+ const output = await readTool.invoke({
64
+ server: "fal",
65
+ uri: "fal://jobs/1",
66
+ maxTextChars: 10
67
+ });
68
+ const parsed = JSON.parse(String(output));
69
+ expect(readResource).toHaveBeenCalledWith("fal", "fal://jobs/1");
70
+ expect(parsed.content[0].text).toBe("abcdefghij");
71
+ expect(parsed.content[0].truncated).toBe(true);
72
+ expect(parsed.content[0].blobLength).toBe(8);
73
+ expect(parsed.content[0].blob).toBeUndefined();
74
+ });
75
+ it("can include binary blobs when requested", async ()=>{
76
+ const { manager, readResource } = createManagerStub();
77
+ readResource.mockResolvedValue([
78
+ {
79
+ uri: "fal://jobs/2",
80
+ mimeType: "image/png",
81
+ blob: "YWJjZA=="
82
+ }
83
+ ]);
84
+ const tools = createMCPResourceTools(manager);
85
+ const readTool = tools.find((entry)=>"mcp_read_resource" === entry.name);
86
+ if (!readTool) throw new Error("mcp_read_resource tool missing");
87
+ const output = await readTool.invoke({
88
+ server: "fal",
89
+ uri: "fal://jobs/2",
90
+ includeBinary: true
91
+ });
92
+ const parsed = JSON.parse(String(output));
93
+ expect(parsed.content[0].blob).toBe("YWJjZA==");
94
+ });
95
+ });