@wingman-ai/gateway 0.1.5 → 0.2.0

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 (66) hide show
  1. package/.wingman/agents/README.md +7 -0
  2. package/.wingman/agents/coding/agent.md +1 -0
  3. package/.wingman/agents/main/agent.md +1 -0
  4. package/.wingman/agents/researcher/agent.md +1 -0
  5. package/.wingman/agents/stock-trader/agent.md +1 -0
  6. package/dist/agent/config/agentConfig.cjs +12 -1
  7. package/dist/agent/config/agentConfig.d.ts +14 -0
  8. package/dist/agent/config/agentConfig.js +12 -1
  9. package/dist/agent/config/agentLoader.cjs +37 -1
  10. package/dist/agent/config/agentLoader.d.ts +2 -1
  11. package/dist/agent/config/agentLoader.js +37 -1
  12. package/dist/agent/config/modelFactory.cjs +2 -1
  13. package/dist/agent/config/modelFactory.js +2 -1
  14. package/dist/agent/config/toolRegistry.cjs +6 -4
  15. package/dist/agent/config/toolRegistry.d.ts +1 -0
  16. package/dist/agent/config/toolRegistry.js +6 -4
  17. package/dist/agent/middleware/additional-messages.cjs +8 -1
  18. package/dist/agent/middleware/additional-messages.d.ts +1 -0
  19. package/dist/agent/middleware/additional-messages.js +8 -1
  20. package/dist/agent/tests/agentConfig.test.cjs +25 -0
  21. package/dist/agent/tests/agentConfig.test.js +25 -0
  22. package/dist/agent/tests/agentLoader.test.cjs +18 -0
  23. package/dist/agent/tests/agentLoader.test.js +18 -0
  24. package/dist/agent/tests/modelFactory.test.cjs +13 -0
  25. package/dist/agent/tests/modelFactory.test.js +14 -1
  26. package/dist/agent/tests/toolRegistry.test.cjs +15 -0
  27. package/dist/agent/tests/toolRegistry.test.js +15 -0
  28. package/dist/agent/tools/code_search.cjs +1 -1
  29. package/dist/agent/tools/code_search.js +1 -1
  30. package/dist/agent/tools/command_execute.cjs +1 -1
  31. package/dist/agent/tools/command_execute.js +1 -1
  32. package/dist/agent/tools/ui_registry.d.ts +3 -3
  33. package/dist/cli/core/agentInvoker.cjs +212 -21
  34. package/dist/cli/core/agentInvoker.d.ts +55 -20
  35. package/dist/cli/core/agentInvoker.js +197 -21
  36. package/dist/cli/core/sessionManager.cjs +93 -4
  37. package/dist/cli/core/sessionManager.d.ts +1 -1
  38. package/dist/cli/core/sessionManager.js +93 -4
  39. package/dist/gateway/http/agents.cjs +121 -10
  40. package/dist/gateway/http/agents.js +121 -10
  41. package/dist/gateway/index.cjs +2 -2
  42. package/dist/gateway/server.cjs +55 -17
  43. package/dist/gateway/server.js +55 -17
  44. package/dist/gateway/types.d.ts +9 -1
  45. package/dist/tests/additionalMessageMiddleware.test.cjs +26 -0
  46. package/dist/tests/additionalMessageMiddleware.test.js +26 -0
  47. package/dist/tests/agentInvokerAttachments.test.cjs +123 -0
  48. package/dist/tests/agentInvokerAttachments.test.js +123 -0
  49. package/dist/tests/agentInvokerWorkdir.test.cjs +100 -0
  50. package/dist/tests/agentInvokerWorkdir.test.d.ts +1 -0
  51. package/dist/tests/agentInvokerWorkdir.test.js +72 -0
  52. package/dist/tests/agents-api.test.cjs +232 -0
  53. package/dist/tests/agents-api.test.d.ts +1 -0
  54. package/dist/tests/agents-api.test.js +226 -0
  55. package/dist/tests/gateway.test.cjs +21 -0
  56. package/dist/tests/gateway.test.js +21 -0
  57. package/dist/tests/sessionMessageAttachments.test.cjs +59 -0
  58. package/dist/tests/sessionMessageAttachments.test.js +59 -0
  59. package/dist/types/agents.d.ts +5 -0
  60. package/dist/webui/assets/index-BytPznA_.css +1 -0
  61. package/dist/webui/assets/index-u_5qlVip.js +176 -0
  62. package/dist/webui/index.html +2 -2
  63. package/package.json +3 -3
  64. package/.wingman/agents/wingman/agent.json +0 -12
  65. package/dist/webui/assets/index-CyE7T5pV.js +0 -162
  66. package/dist/webui/assets/index-DMEHdune.css +0 -1
@@ -33,8 +33,79 @@ const external_node_fs_namespaceObject = require("node:fs");
33
33
  const external_node_path_namespaceObject = require("node:path");
34
34
  const external_js_yaml_namespaceObject = require("js-yaml");
35
35
  const voice_cjs_namespaceObject = require("../../types/voice.cjs");
36
+ const hasOwn = (value, key)=>Boolean(value && Object.prototype.hasOwnProperty.call(value, key));
37
+ const getPromptTrainingFromPayload = (payload)=>{
38
+ if (hasOwn(payload, "promptTraining")) return payload.promptTraining;
39
+ if (hasOwn(payload, "promptRefinement")) return payload.promptRefinement;
40
+ };
41
+ const mapPromptTrainingFields = (value)=>({
42
+ promptTraining: value,
43
+ promptRefinement: value
44
+ });
45
+ const mapSubAgentForResponse = (sub)=>({
46
+ id: sub.name,
47
+ displayName: sub.name,
48
+ description: sub.description,
49
+ tools: sub.tools || [],
50
+ model: sub.model,
51
+ prompt: sub.systemPrompt,
52
+ ...mapPromptTrainingFields(sub.promptRefinement)
53
+ });
54
+ const normalizeSubAgents = (rawSubAgents)=>{
55
+ if (null == rawSubAgents) return {
56
+ ok: true,
57
+ value: []
58
+ };
59
+ if (!Array.isArray(rawSubAgents)) return {
60
+ ok: false,
61
+ error: "Invalid subAgents: expected an array"
62
+ };
63
+ const availableTools = (0, toolRegistry_cjs_namespaceObject.getAvailableTools)();
64
+ const normalized = [];
65
+ for(let index = 0; index < rawSubAgents.length; index += 1){
66
+ const item = rawSubAgents[index];
67
+ if (!item || "object" != typeof item) return {
68
+ ok: false,
69
+ error: `Invalid subAgents[${index}]: expected an object`
70
+ };
71
+ const name = (item.id || item.name || "").trim();
72
+ if (!name || !/^[a-zA-Z0-9_-]+$/.test(name)) return {
73
+ ok: false,
74
+ error: `Invalid subAgents[${index}].id`
75
+ };
76
+ const prompt = (item.prompt ?? item.systemPrompt ?? "").trim();
77
+ if (!prompt) return {
78
+ ok: false,
79
+ error: `Invalid subAgents[${index}].prompt`
80
+ };
81
+ const description = (item.description || "").trim();
82
+ if (!description) return {
83
+ ok: false,
84
+ error: `Invalid subAgents[${index}].description`
85
+ };
86
+ const promptTraining = getPromptTrainingFromPayload(item);
87
+ if (null != promptTraining && "boolean" != typeof promptTraining && ("object" != typeof promptTraining || Array.isArray(promptTraining))) return {
88
+ ok: false,
89
+ error: `Invalid subAgents[${index}].promptTraining`
90
+ };
91
+ const tools = Array.isArray(item.tools) ? item.tools.filter((tool)=>availableTools.includes(tool)) : [];
92
+ const sub = {
93
+ name,
94
+ description,
95
+ tools,
96
+ model: item.model?.trim() || void 0,
97
+ systemPrompt: prompt
98
+ };
99
+ if (null != promptTraining) sub.promptRefinement = promptTraining;
100
+ normalized.push(sub);
101
+ }
102
+ return {
103
+ ok: true,
104
+ value: normalized
105
+ };
106
+ };
36
107
  const buildAgentMarkdown = (params)=>{
37
- const { id, description, tools, model, prompt, voice } = params;
108
+ const { id, description, tools, model, prompt, voice, promptRefinement, subAgents } = params;
38
109
  const metadata = {
39
110
  name: id,
40
111
  description: description || "New Wingman agent",
@@ -42,6 +113,8 @@ const buildAgentMarkdown = (params)=>{
42
113
  };
43
114
  if (model) metadata.model = model;
44
115
  if (voice) metadata.voice = voice;
116
+ if (void 0 !== promptRefinement) metadata.promptRefinement = promptRefinement;
117
+ if (subAgents && subAgents.length > 0) metadata.subAgents = subAgents;
45
118
  return serializeAgentMarkdown(metadata, prompt || "You are a Wingman agent.");
46
119
  };
47
120
  const parseAgentMarkdown = (content)=>{
@@ -79,13 +152,8 @@ const handleAgentsApi = async (ctx, req, url)=>{
79
152
  tools: agent.tools || [],
80
153
  model: agent.model,
81
154
  voice: agent.voice,
82
- subAgents: agent.subAgents?.map((sub)=>({
83
- id: sub.name,
84
- displayName: sub.name,
85
- description: sub.description,
86
- tools: sub.tools || [],
87
- model: sub.model
88
- })) || []
155
+ ...mapPromptTrainingFields(agent.promptRefinement),
156
+ subAgents: agent.subAgents?.map((sub)=>mapSubAgentForResponse(sub)) || []
89
157
  }));
90
158
  return new Response(JSON.stringify({
91
159
  agents,
@@ -112,6 +180,15 @@ const handleAgentsApi = async (ctx, req, url)=>{
112
180
  status: 400
113
181
  });
114
182
  const tools = Array.isArray(body.tools) ? body.tools.filter((tool)=>(0, toolRegistry_cjs_namespaceObject.getAvailableTools)().includes(tool)) : [];
183
+ const promptTraining = getPromptTrainingFromPayload(body);
184
+ if (null != promptTraining && "boolean" != typeof promptTraining && ("object" != typeof promptTraining || Array.isArray(promptTraining))) return new Response("Invalid promptTraining configuration", {
185
+ status: 400
186
+ });
187
+ const rawSubAgents = hasOwn(body, "subAgents") ? body.subAgents : body.subagents;
188
+ const subAgentsResult = normalizeSubAgents(rawSubAgents);
189
+ if (!subAgentsResult.ok) return new Response(subAgentsResult.error, {
190
+ status: 400
191
+ });
115
192
  const agentsDir = (0, external_node_path_namespaceObject.join)(ctx.resolveConfigDirPath(), "agents", id);
116
193
  if ((0, external_node_fs_namespaceObject.existsSync)(agentsDir)) return new Response("Agent already exists", {
117
194
  status: 409
@@ -125,7 +202,9 @@ const handleAgentsApi = async (ctx, req, url)=>{
125
202
  tools,
126
203
  model: body.model,
127
204
  prompt: body.prompt,
128
- voice: parsedVoice
205
+ voice: parsedVoice,
206
+ promptRefinement: null === promptTraining ? void 0 : promptTraining,
207
+ subAgents: subAgentsResult.value
129
208
  });
130
209
  (0, external_node_fs_namespaceObject.writeFileSync)((0, external_node_path_namespaceObject.join)(agentsDir, "agent.md"), agentMarkdown);
131
210
  const agentList = config.agents?.list || [];
@@ -149,7 +228,9 @@ const handleAgentsApi = async (ctx, req, url)=>{
149
228
  description: body.description,
150
229
  tools,
151
230
  model: body.model,
152
- voice: parsedVoice
231
+ voice: parsedVoice,
232
+ ...mapPromptTrainingFields(null === promptTraining ? void 0 : promptTraining),
233
+ subAgents: subAgentsResult.value.map((sub)=>mapSubAgentForResponse(sub))
153
234
  }, null, 2), {
154
235
  headers: {
155
236
  "Content-Type": "application/json"
@@ -178,6 +259,8 @@ const handleAgentsApi = async (ctx, req, url)=>{
178
259
  tools: agentConfig.tools || [],
179
260
  model: agentConfig.model,
180
261
  voice: agentConfig.voice,
262
+ ...mapPromptTrainingFields(agentConfig.promptRefinement),
263
+ subAgents: agentConfig.subAgents?.map((sub)=>mapSubAgentForResponse(sub)) || [],
181
264
  prompt: agentConfig.systemPrompt
182
265
  }, null, 2), {
183
266
  headers: {
@@ -204,6 +287,18 @@ const handleAgentsApi = async (ctx, req, url)=>{
204
287
  const nextModel = body.model ?? agentConfig.model;
205
288
  const nextPrompt = body.prompt ?? agentConfig.systemPrompt;
206
289
  const nextVoice = void 0 === parsedVoice ? agentConfig.voice : parsedVoice;
290
+ const bodyPromptTraining = getPromptTrainingFromPayload(body);
291
+ if (null != bodyPromptTraining && "boolean" != typeof bodyPromptTraining && ("object" != typeof bodyPromptTraining || Array.isArray(bodyPromptTraining))) return new Response("Invalid promptTraining configuration", {
292
+ status: 400
293
+ });
294
+ const nextPromptRefinement = void 0 === bodyPromptTraining ? agentConfig.promptRefinement : null === bodyPromptTraining ? void 0 : bodyPromptTraining;
295
+ const hasSubAgents = hasOwn(body, "subAgents") || hasOwn(body, "subagents");
296
+ const rawSubAgents = hasOwn(body, "subAgents") ? body.subAgents : body.subagents;
297
+ const subAgentsResult = normalizeSubAgents(rawSubAgents);
298
+ if (!subAgentsResult.ok) return new Response(subAgentsResult.error, {
299
+ status: 400
300
+ });
301
+ const nextSubAgents = hasSubAgents ? subAgentsResult.value : agentConfig.subAgents || [];
207
302
  const agentsDir = (0, external_node_path_namespaceObject.join)(ctx.resolveConfigDirPath(), "agents", agentId);
208
303
  const agentJsonPath = (0, external_node_path_namespaceObject.join)(agentsDir, "agent.json");
209
304
  const agentMarkdownPath = (0, external_node_path_namespaceObject.join)(agentsDir, "agent.md");
@@ -223,6 +318,13 @@ const handleAgentsApi = async (ctx, req, url)=>{
223
318
  parsed.systemPrompt = nextPrompt;
224
319
  if (nextVoice) parsed.voice = nextVoice;
225
320
  else delete parsed.voice;
321
+ if (void 0 !== nextPromptRefinement) parsed.promptRefinement = nextPromptRefinement;
322
+ else delete parsed.promptRefinement;
323
+ if (nextSubAgents.length > 0) parsed.subAgents = nextSubAgents;
324
+ else {
325
+ delete parsed.subAgents;
326
+ delete parsed.subagents;
327
+ }
226
328
  (0, external_node_fs_namespaceObject.writeFileSync)(agentJsonPath, JSON.stringify(parsed, null, 2));
227
329
  } else if (hasMarkdown) {
228
330
  const raw = (0, external_node_fs_namespaceObject.readFileSync)(agentMarkdownPath, "utf-8");
@@ -234,6 +336,13 @@ const handleAgentsApi = async (ctx, req, url)=>{
234
336
  else delete metadata.model;
235
337
  if (nextVoice) metadata.voice = nextVoice;
236
338
  else delete metadata.voice;
339
+ if (void 0 !== nextPromptRefinement) metadata.promptRefinement = nextPromptRefinement;
340
+ else delete metadata.promptRefinement;
341
+ if (nextSubAgents.length > 0) metadata.subAgents = nextSubAgents;
342
+ else {
343
+ delete metadata.subAgents;
344
+ delete metadata.subagents;
345
+ }
237
346
  const updatedMarkdown = serializeAgentMarkdown(metadata, nextPrompt);
238
347
  (0, external_node_fs_namespaceObject.writeFileSync)(agentMarkdownPath, updatedMarkdown);
239
348
  }
@@ -260,6 +369,8 @@ const handleAgentsApi = async (ctx, req, url)=>{
260
369
  tools,
261
370
  model: nextModel,
262
371
  voice: nextVoice,
372
+ ...mapPromptTrainingFields(nextPromptRefinement),
373
+ subAgents: nextSubAgents.map((sub)=>mapSubAgentForResponse(sub)),
263
374
  prompt: nextPrompt
264
375
  }, null, 2), {
265
376
  headers: {
@@ -5,8 +5,79 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
5
  import { join } from "node:path";
6
6
  import { dump, load } from "js-yaml";
7
7
  import { AgentVoiceConfigSchema } from "../../types/voice.js";
8
+ const hasOwn = (value, key)=>Boolean(value && Object.prototype.hasOwnProperty.call(value, key));
9
+ const getPromptTrainingFromPayload = (payload)=>{
10
+ if (hasOwn(payload, "promptTraining")) return payload.promptTraining;
11
+ if (hasOwn(payload, "promptRefinement")) return payload.promptRefinement;
12
+ };
13
+ const mapPromptTrainingFields = (value)=>({
14
+ promptTraining: value,
15
+ promptRefinement: value
16
+ });
17
+ const mapSubAgentForResponse = (sub)=>({
18
+ id: sub.name,
19
+ displayName: sub.name,
20
+ description: sub.description,
21
+ tools: sub.tools || [],
22
+ model: sub.model,
23
+ prompt: sub.systemPrompt,
24
+ ...mapPromptTrainingFields(sub.promptRefinement)
25
+ });
26
+ const normalizeSubAgents = (rawSubAgents)=>{
27
+ if (null == rawSubAgents) return {
28
+ ok: true,
29
+ value: []
30
+ };
31
+ if (!Array.isArray(rawSubAgents)) return {
32
+ ok: false,
33
+ error: "Invalid subAgents: expected an array"
34
+ };
35
+ const availableTools = getAvailableTools();
36
+ const normalized = [];
37
+ for(let index = 0; index < rawSubAgents.length; index += 1){
38
+ const item = rawSubAgents[index];
39
+ if (!item || "object" != typeof item) return {
40
+ ok: false,
41
+ error: `Invalid subAgents[${index}]: expected an object`
42
+ };
43
+ const name = (item.id || item.name || "").trim();
44
+ if (!name || !/^[a-zA-Z0-9_-]+$/.test(name)) return {
45
+ ok: false,
46
+ error: `Invalid subAgents[${index}].id`
47
+ };
48
+ const prompt = (item.prompt ?? item.systemPrompt ?? "").trim();
49
+ if (!prompt) return {
50
+ ok: false,
51
+ error: `Invalid subAgents[${index}].prompt`
52
+ };
53
+ const description = (item.description || "").trim();
54
+ if (!description) return {
55
+ ok: false,
56
+ error: `Invalid subAgents[${index}].description`
57
+ };
58
+ const promptTraining = getPromptTrainingFromPayload(item);
59
+ if (null != promptTraining && "boolean" != typeof promptTraining && ("object" != typeof promptTraining || Array.isArray(promptTraining))) return {
60
+ ok: false,
61
+ error: `Invalid subAgents[${index}].promptTraining`
62
+ };
63
+ const tools = Array.isArray(item.tools) ? item.tools.filter((tool)=>availableTools.includes(tool)) : [];
64
+ const sub = {
65
+ name,
66
+ description,
67
+ tools,
68
+ model: item.model?.trim() || void 0,
69
+ systemPrompt: prompt
70
+ };
71
+ if (null != promptTraining) sub.promptRefinement = promptTraining;
72
+ normalized.push(sub);
73
+ }
74
+ return {
75
+ ok: true,
76
+ value: normalized
77
+ };
78
+ };
8
79
  const buildAgentMarkdown = (params)=>{
9
- const { id, description, tools, model, prompt, voice } = params;
80
+ const { id, description, tools, model, prompt, voice, promptRefinement, subAgents } = params;
10
81
  const metadata = {
11
82
  name: id,
12
83
  description: description || "New Wingman agent",
@@ -14,6 +85,8 @@ const buildAgentMarkdown = (params)=>{
14
85
  };
15
86
  if (model) metadata.model = model;
16
87
  if (voice) metadata.voice = voice;
88
+ if (void 0 !== promptRefinement) metadata.promptRefinement = promptRefinement;
89
+ if (subAgents && subAgents.length > 0) metadata.subAgents = subAgents;
17
90
  return serializeAgentMarkdown(metadata, prompt || "You are a Wingman agent.");
18
91
  };
19
92
  const parseAgentMarkdown = (content)=>{
@@ -51,13 +124,8 @@ const handleAgentsApi = async (ctx, req, url)=>{
51
124
  tools: agent.tools || [],
52
125
  model: agent.model,
53
126
  voice: agent.voice,
54
- subAgents: agent.subAgents?.map((sub)=>({
55
- id: sub.name,
56
- displayName: sub.name,
57
- description: sub.description,
58
- tools: sub.tools || [],
59
- model: sub.model
60
- })) || []
127
+ ...mapPromptTrainingFields(agent.promptRefinement),
128
+ subAgents: agent.subAgents?.map((sub)=>mapSubAgentForResponse(sub)) || []
61
129
  }));
62
130
  return new Response(JSON.stringify({
63
131
  agents,
@@ -84,6 +152,15 @@ const handleAgentsApi = async (ctx, req, url)=>{
84
152
  status: 400
85
153
  });
86
154
  const tools = Array.isArray(body.tools) ? body.tools.filter((tool)=>getAvailableTools().includes(tool)) : [];
155
+ const promptTraining = getPromptTrainingFromPayload(body);
156
+ if (null != promptTraining && "boolean" != typeof promptTraining && ("object" != typeof promptTraining || Array.isArray(promptTraining))) return new Response("Invalid promptTraining configuration", {
157
+ status: 400
158
+ });
159
+ const rawSubAgents = hasOwn(body, "subAgents") ? body.subAgents : body.subagents;
160
+ const subAgentsResult = normalizeSubAgents(rawSubAgents);
161
+ if (!subAgentsResult.ok) return new Response(subAgentsResult.error, {
162
+ status: 400
163
+ });
87
164
  const agentsDir = join(ctx.resolveConfigDirPath(), "agents", id);
88
165
  if (existsSync(agentsDir)) return new Response("Agent already exists", {
89
166
  status: 409
@@ -97,7 +174,9 @@ const handleAgentsApi = async (ctx, req, url)=>{
97
174
  tools,
98
175
  model: body.model,
99
176
  prompt: body.prompt,
100
- voice: parsedVoice
177
+ voice: parsedVoice,
178
+ promptRefinement: null === promptTraining ? void 0 : promptTraining,
179
+ subAgents: subAgentsResult.value
101
180
  });
102
181
  writeFileSync(join(agentsDir, "agent.md"), agentMarkdown);
103
182
  const agentList = config.agents?.list || [];
@@ -121,7 +200,9 @@ const handleAgentsApi = async (ctx, req, url)=>{
121
200
  description: body.description,
122
201
  tools,
123
202
  model: body.model,
124
- voice: parsedVoice
203
+ voice: parsedVoice,
204
+ ...mapPromptTrainingFields(null === promptTraining ? void 0 : promptTraining),
205
+ subAgents: subAgentsResult.value.map((sub)=>mapSubAgentForResponse(sub))
125
206
  }, null, 2), {
126
207
  headers: {
127
208
  "Content-Type": "application/json"
@@ -150,6 +231,8 @@ const handleAgentsApi = async (ctx, req, url)=>{
150
231
  tools: agentConfig.tools || [],
151
232
  model: agentConfig.model,
152
233
  voice: agentConfig.voice,
234
+ ...mapPromptTrainingFields(agentConfig.promptRefinement),
235
+ subAgents: agentConfig.subAgents?.map((sub)=>mapSubAgentForResponse(sub)) || [],
153
236
  prompt: agentConfig.systemPrompt
154
237
  }, null, 2), {
155
238
  headers: {
@@ -176,6 +259,18 @@ const handleAgentsApi = async (ctx, req, url)=>{
176
259
  const nextModel = body.model ?? agentConfig.model;
177
260
  const nextPrompt = body.prompt ?? agentConfig.systemPrompt;
178
261
  const nextVoice = void 0 === parsedVoice ? agentConfig.voice : parsedVoice;
262
+ const bodyPromptTraining = getPromptTrainingFromPayload(body);
263
+ if (null != bodyPromptTraining && "boolean" != typeof bodyPromptTraining && ("object" != typeof bodyPromptTraining || Array.isArray(bodyPromptTraining))) return new Response("Invalid promptTraining configuration", {
264
+ status: 400
265
+ });
266
+ const nextPromptRefinement = void 0 === bodyPromptTraining ? agentConfig.promptRefinement : null === bodyPromptTraining ? void 0 : bodyPromptTraining;
267
+ const hasSubAgents = hasOwn(body, "subAgents") || hasOwn(body, "subagents");
268
+ const rawSubAgents = hasOwn(body, "subAgents") ? body.subAgents : body.subagents;
269
+ const subAgentsResult = normalizeSubAgents(rawSubAgents);
270
+ if (!subAgentsResult.ok) return new Response(subAgentsResult.error, {
271
+ status: 400
272
+ });
273
+ const nextSubAgents = hasSubAgents ? subAgentsResult.value : agentConfig.subAgents || [];
179
274
  const agentsDir = join(ctx.resolveConfigDirPath(), "agents", agentId);
180
275
  const agentJsonPath = join(agentsDir, "agent.json");
181
276
  const agentMarkdownPath = join(agentsDir, "agent.md");
@@ -195,6 +290,13 @@ const handleAgentsApi = async (ctx, req, url)=>{
195
290
  parsed.systemPrompt = nextPrompt;
196
291
  if (nextVoice) parsed.voice = nextVoice;
197
292
  else delete parsed.voice;
293
+ if (void 0 !== nextPromptRefinement) parsed.promptRefinement = nextPromptRefinement;
294
+ else delete parsed.promptRefinement;
295
+ if (nextSubAgents.length > 0) parsed.subAgents = nextSubAgents;
296
+ else {
297
+ delete parsed.subAgents;
298
+ delete parsed.subagents;
299
+ }
198
300
  writeFileSync(agentJsonPath, JSON.stringify(parsed, null, 2));
199
301
  } else if (hasMarkdown) {
200
302
  const raw = readFileSync(agentMarkdownPath, "utf-8");
@@ -206,6 +308,13 @@ const handleAgentsApi = async (ctx, req, url)=>{
206
308
  else delete metadata.model;
207
309
  if (nextVoice) metadata.voice = nextVoice;
208
310
  else delete metadata.voice;
311
+ if (void 0 !== nextPromptRefinement) metadata.promptRefinement = nextPromptRefinement;
312
+ else delete metadata.promptRefinement;
313
+ if (nextSubAgents.length > 0) metadata.subAgents = nextSubAgents;
314
+ else {
315
+ delete metadata.subAgents;
316
+ delete metadata.subagents;
317
+ }
209
318
  const updatedMarkdown = serializeAgentMarkdown(metadata, nextPrompt);
210
319
  writeFileSync(agentMarkdownPath, updatedMarkdown);
211
320
  }
@@ -232,6 +341,8 @@ const handleAgentsApi = async (ctx, req, url)=>{
232
341
  tools,
233
342
  model: nextModel,
234
343
  voice: nextVoice,
344
+ ...mapPromptTrainingFields(nextPromptRefinement),
345
+ subAgents: nextSubAgents.map((sub)=>mapSubAgentForResponse(sub)),
235
346
  prompt: nextPrompt
236
347
  }, null, 2), {
237
348
  headers: {
@@ -91,9 +91,9 @@ var __webpack_exports__ = {};
91
91
  "GatewayDaemon",
92
92
  "default",
93
93
  "NodeManager",
94
- "GatewayRpcClient",
95
- "BroadcastGroupManager",
96
94
  "GatewayAuth",
95
+ "BroadcastGroupManager",
96
+ "GatewayRpcClient",
97
97
  "GatewayServer"
98
98
  ].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>_types_js__rspack_import_7[__rspack_import_key];
99
99
  __webpack_require__.d(__webpack_exports__, __rspack_reexport);
@@ -64,6 +64,21 @@ function _define_property(obj, key, value) {
64
64
  else obj[key] = value;
65
65
  return obj;
66
66
  }
67
+ const API_CORS_HEADERS = {
68
+ "Access-Control-Allow-Origin": "*",
69
+ "Access-Control-Allow-Methods": "GET,POST,PUT,DELETE,OPTIONS",
70
+ "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Wingman-Token, X-Wingman-Password",
71
+ "Access-Control-Max-Age": "600"
72
+ };
73
+ function withApiCors(response) {
74
+ const headers = new Headers(response.headers);
75
+ for (const [key, value] of Object.entries(API_CORS_HEADERS))headers.set(key, value);
76
+ return new Response(response.body, {
77
+ status: response.status,
78
+ statusText: response.statusText,
79
+ headers
80
+ });
81
+ }
67
82
  class GatewayServer {
68
83
  async start() {
69
84
  if (void 0 === globalThis.Bun) throw new Error("Gateway server requires Bun runtime. Start with `bun ./bin/wingman gateway start`.");
@@ -381,7 +396,10 @@ class GatewayServer {
381
396
  this.broadcastSessionEvent(sessionKey, sessionMessage, ws);
382
397
  this.broadcastToClients(sessionMessage, {
383
398
  exclude: ws,
384
- clientType: "webui",
399
+ clientTypes: [
400
+ "webui",
401
+ "desktop"
402
+ ],
385
403
  skipSessionId: sessionKey
386
404
  });
387
405
  const outputManager = new outputManager_cjs_namespaceObject.OutputManager("interactive");
@@ -621,14 +639,18 @@ class GatewayServer {
621
639
  }
622
640
  broadcastToClients(message, options) {
623
641
  let sent = 0;
624
- for (const ws of this.connectedClients)if (!options?.exclude || ws !== options.exclude) {
625
- if (!options?.clientType || ws.data.clientType === options.clientType) {
626
- if (options?.skipSessionId) {
627
- const subscribers = this.sessionSubscriptions.get(options.skipSessionId);
628
- if (subscribers?.has(ws)) continue;
642
+ for (const ws of this.connectedClients){
643
+ if (!options?.exclude || ws !== options.exclude) {
644
+ if (!options?.clientType || ws.data.clientType === options.clientType) {
645
+ if (!options?.clientTypes || !(options.clientTypes.length > 0) || options.clientTypes.includes(ws.data.clientType || "")) {
646
+ if (options?.skipSessionId) {
647
+ const subscribers = this.sessionSubscriptions.get(options.skipSessionId);
648
+ if (subscribers?.has(ws)) continue;
649
+ }
650
+ this.sendMessage(ws, message);
651
+ sent++;
652
+ }
629
653
  }
630
- this.sendMessage(ws, message);
631
- sent++;
632
654
  }
633
655
  }
634
656
  return sent;
@@ -877,6 +899,9 @@ class GatewayServer {
877
899
  const webhookResponse = await (0, webhooks_cjs_namespaceObject.handleWebhookInvoke)(ctx, this.webhookStore, req, url);
878
900
  if (webhookResponse) return webhookResponse;
879
901
  if (url.pathname.startsWith("/api/")) {
902
+ if ("OPTIONS" === req.method) return withApiCors(new Response(null, {
903
+ status: 204
904
+ }));
880
905
  if ("/api/config" === url.pathname) {
881
906
  const agents = this.wingmanConfig.agents?.list?.map((agent)=>({
882
907
  id: agent.id,
@@ -884,7 +909,7 @@ class GatewayServer {
884
909
  default: agent.default
885
910
  })) || [];
886
911
  const defaultAgentId = this.router.selectAgent();
887
- return new Response(JSON.stringify({
912
+ return withApiCors(new Response(JSON.stringify({
888
913
  gatewayHost: this.config.host,
889
914
  gatewayPort: this.config.port,
890
915
  requireAuth: this.auth.isAuthRequired(),
@@ -897,15 +922,15 @@ class GatewayServer {
897
922
  headers: {
898
923
  "Content-Type": "application/json"
899
924
  }
900
- });
925
+ }));
901
926
  }
902
927
  const apiResponse = await (0, webhooks_cjs_namespaceObject.handleWebhooksApi)(ctx, this.webhookStore, req, url) || await (0, routines_cjs_namespaceObject.handleRoutinesApi)(ctx, this.routineStore, req, url) || await (0, agents_cjs_namespaceObject.handleAgentsApi)(ctx, req, url) || await (0, providers_cjs_namespaceObject.handleProvidersApi)(ctx, req, url) || await (0, voice_cjs_namespaceObject.handleVoiceApi)(ctx, req, url) || await (0, fs_cjs_namespaceObject.handleFsApi)(ctx, req, url) || await (0, sessions_cjs_namespaceObject.handleSessionsApi)(ctx, req, url);
903
- if (apiResponse) return apiResponse;
904
- if ("/api/health" === url.pathname) return this.handleHealthCheck();
905
- if ("/api/stats" === url.pathname) return this.handleStats();
906
- return new Response("Not Found", {
928
+ if (apiResponse) return withApiCors(apiResponse);
929
+ if ("/api/health" === url.pathname) return withApiCors(this.handleHealthCheck());
930
+ if ("/api/stats" === url.pathname) return withApiCors(this.handleStats());
931
+ return withApiCors(new Response("Not Found", {
907
932
  status: 404
908
- });
933
+ }));
909
934
  }
910
935
  if ("GET" !== req.method) return new Response("Method Not Allowed", {
911
936
  status: 405
@@ -1158,11 +1183,20 @@ class GatewayServer {
1158
1183
  }
1159
1184
  function buildAttachmentPreview(attachments) {
1160
1185
  if (!attachments || 0 === attachments.length) return "Attachment";
1186
+ let hasFile = false;
1161
1187
  let hasAudio = false;
1162
1188
  let hasImage = false;
1163
- for (const attachment of attachments)if (isAudioAttachment(attachment)) hasAudio = true;
1164
- else hasImage = true;
1189
+ for (const attachment of attachments){
1190
+ if (isFileAttachment(attachment)) {
1191
+ hasFile = true;
1192
+ continue;
1193
+ }
1194
+ if (isAudioAttachment(attachment)) hasAudio = true;
1195
+ else hasImage = true;
1196
+ }
1165
1197
  const count = attachments.length;
1198
+ if (hasFile && (hasAudio || hasImage)) return count > 1 ? "File and media attachments" : "File and media attachment";
1199
+ if (hasFile) return count > 1 ? "File attachments" : "File attachment";
1166
1200
  if (hasAudio && hasImage) return count > 1 ? "Media attachments" : "Media attachment";
1167
1201
  if (hasAudio) return count > 1 ? "Audio attachments" : "Audio attachment";
1168
1202
  return count > 1 ? "Image attachments" : "Image attachment";
@@ -1173,6 +1207,10 @@ function isAudioAttachment(attachment) {
1173
1207
  if (attachment.dataUrl?.startsWith("data:audio/")) return true;
1174
1208
  return false;
1175
1209
  }
1210
+ function isFileAttachment(attachment) {
1211
+ if ("file" === attachment.kind) return true;
1212
+ return "string" == typeof attachment.textContent;
1213
+ }
1176
1214
  exports.GatewayServer = __webpack_exports__.GatewayServer;
1177
1215
  for(var __rspack_i in __webpack_exports__)if (-1 === [
1178
1216
  "GatewayServer"