@wingman-ai/gateway 0.1.4 → 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 (67) 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 +8 -2
  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/tests/uiRegistryTools.test.cjs +15 -0
  29. package/dist/agent/tests/uiRegistryTools.test.js +15 -0
  30. package/dist/agent/tools/code_search.cjs +1 -1
  31. package/dist/agent/tools/code_search.js +1 -1
  32. package/dist/agent/tools/command_execute.cjs +1 -1
  33. package/dist/agent/tools/command_execute.js +1 -1
  34. package/dist/agent/tools/ui_registry.d.ts +3 -3
  35. package/dist/cli/core/agentInvoker.cjs +212 -21
  36. package/dist/cli/core/agentInvoker.d.ts +55 -20
  37. package/dist/cli/core/agentInvoker.js +197 -21
  38. package/dist/cli/core/sessionManager.cjs +93 -4
  39. package/dist/cli/core/sessionManager.d.ts +1 -1
  40. package/dist/cli/core/sessionManager.js +93 -4
  41. package/dist/gateway/http/agents.cjs +121 -10
  42. package/dist/gateway/http/agents.js +121 -10
  43. package/dist/gateway/server.cjs +55 -17
  44. package/dist/gateway/server.js +55 -17
  45. package/dist/gateway/types.d.ts +9 -1
  46. package/dist/tests/additionalMessageMiddleware.test.cjs +26 -0
  47. package/dist/tests/additionalMessageMiddleware.test.js +26 -0
  48. package/dist/tests/agentInvokerAttachments.test.cjs +123 -0
  49. package/dist/tests/agentInvokerAttachments.test.js +123 -0
  50. package/dist/tests/agentInvokerWorkdir.test.cjs +100 -0
  51. package/dist/tests/agentInvokerWorkdir.test.d.ts +1 -0
  52. package/dist/tests/agentInvokerWorkdir.test.js +72 -0
  53. package/dist/tests/agents-api.test.cjs +232 -0
  54. package/dist/tests/agents-api.test.d.ts +1 -0
  55. package/dist/tests/agents-api.test.js +226 -0
  56. package/dist/tests/gateway.test.cjs +24 -0
  57. package/dist/tests/gateway.test.js +24 -0
  58. package/dist/tests/sessionMessageAttachments.test.cjs +59 -0
  59. package/dist/tests/sessionMessageAttachments.test.js +59 -0
  60. package/dist/types/agents.d.ts +5 -0
  61. package/dist/webui/assets/{index-DTK8ignm.css → index-BytPznA_.css} +1 -1
  62. package/dist/webui/assets/index-u_5qlVip.js +176 -0
  63. package/dist/webui/index.html +2 -2
  64. package/package.json +7 -7
  65. package/skills/ui-registry/registry.json +90 -0
  66. package/.wingman/agents/wingman/agent.json +0 -12
  67. package/dist/webui/assets/index-CZt-NLM_.js +0 -178
@@ -23,6 +23,10 @@ Each agent configuration file follows this schema:
23
23
  "systemPrompt": "Detailed instructions for the agent...",
24
24
  "tools": ["tool1", "tool2"],
25
25
  "model": "provider:model-name",
26
+ "promptRefinement": {
27
+ "enabled": true,
28
+ "instructionsPath": "/memories/agents/agent-name/instructions.md"
29
+ },
26
30
  "subagents": [
27
31
  {
28
32
  "name": "subagent-1",
@@ -48,6 +52,9 @@ Each agent configuration file follows this schema:
48
52
  - **model**: Override the default model (format: `provider:model-name`)
49
53
  - Anthropic: `anthropic:claude-opus-4-5`, `anthropic:claude-sonnet-4-5-20250929`
50
54
  - OpenAI: `openai:gpt-4o`, `openai:gpt-4-turbo`
55
+ - **promptRefinement**: Allow the agent to maintain a durable prompt overlay under `/memories/`
56
+ - `enabled`: Set true to allow updates from explicit user feedback
57
+ - `instructionsPath`: Optional override for the overlay file location (virtual path)
51
58
  - **subagents**: Array of subagent configurations (see [Hierarchical Agents](#hierarchical-agents) below)
52
59
  - Subagents may also set their own `model` to override the parent/default model
53
60
 
@@ -7,6 +7,7 @@ tools:
7
7
  - command_execute
8
8
  - git_status
9
9
  model: openai:gpt-5.2-codex
10
+ promptRefinement: true
10
11
  subAgents:
11
12
  - name: researcher
12
13
  description: Research subagent
@@ -7,6 +7,7 @@ tools:
7
7
  - web_crawler
8
8
  - command_execute
9
9
  model: openai:gpt-5-mini
10
+ promptRefinement: true
10
11
  ---
11
12
 
12
13
  You are the primary Wingman agent. Handle a wide range of coding, research, and reasoning tasks directly.
@@ -4,6 +4,7 @@ description: A general-purpose internet researcher for topics, documentation, an
4
4
  tools:
5
5
  - internet_search
6
6
  - web_crawler
7
+ promptRefinement: true
7
8
  ---
8
9
 
9
10
  You are a general-purpose research agent for Wingman. Your job is to find, verify, and summarize information from the public web for any topic, including software documentation and APIs used by the coding agent.
@@ -6,6 +6,7 @@ tools:
6
6
  - web_crawler
7
7
  model: xai:grok-4-1-fast-reasoning
8
8
  mcpUseGlobal: true
9
+ promptRefinement: true
9
10
  subAgents:
10
11
  - name: goal-translator
11
12
  description: "Translates goal + deadline into an aggressiveness profile with feasibility notes."
@@ -69,6 +70,9 @@ Top rules:
69
70
  - If data is stale or incomplete, I prefer NO TRADE and explain why.
70
71
  - I separate FACTS (tool outputs) from INFERENCES (reasoning) inside the Decision Packet.
71
72
  - Do not dump raw tool output, internal file paths, or tool call IDs in the response; summarize only what is relevant.
73
+ - Use exact section headers shown below (no variants like "No-Trade Live Reason"). Omit any section that is not applicable.
74
+ - Keep it readable: limit each section to 1-3 bullets; Facts/Inferences to max 5 bullets each.
75
+ - Use plain-English explanations for any "no trade" or veto; avoid jargon-heavy phrases.
72
76
 
73
77
  Primary data sources:
74
78
  - Finnhub MCP tools for quotes, candles, fundamentals, earnings, news, peers, and option chains.
@@ -199,7 +203,7 @@ Orders to Place:
199
203
  - (only if user explicitly requested live-trade instructions)
200
204
 
201
205
  No-Trade Reason:
202
- - (only when applicable)
206
+ - (only when applicable; one plain-English sentence)
203
207
 
204
208
  Assumptions:
205
209
  - ...
@@ -222,7 +226,9 @@ Style:
222
226
  - First line must be the Goal Acknowledgement.
223
227
  - Second line must be Direct Answer in plain language (1-3 sentences).
224
228
  - Keep decisions concise; no fluff.
225
- - If no trade, still return a complete Decision Packet with reason.
229
+ - Avoid long lists, raw arrays, or full tool outputs. Summarize the top signals only.
230
+ - If no trade, include No-Trade Reason and keep the rest concise.
231
+ - Concise, actionable trades for the user to make, if any.
226
232
 
227
233
  Daily brief mode:
228
234
  - If prompt is minimal/blank, produce a Decision Packet with no trades and a short market regime assessment.
@@ -45,6 +45,16 @@ const AvailableToolNames = external_zod_namespaceObject.z["enum"]([
45
45
  "ui_registry_get",
46
46
  "ui_present"
47
47
  ]);
48
+ const PromptRefinementSchema = external_zod_namespaceObject.z.preprocess((value)=>{
49
+ if (void 0 === value) return;
50
+ if ("boolean" == typeof value) return {
51
+ enabled: value
52
+ };
53
+ return value;
54
+ }, external_zod_namespaceObject.z.object({
55
+ enabled: external_zod_namespaceObject.z.boolean().optional().default(true).describe("Whether prompt refinement is enabled for this agent"),
56
+ instructionsPath: external_zod_namespaceObject.z.string().min(1).optional().describe("Path (virtual) to store the agent's prompt refinement overlay (defaults under /memories/)")
57
+ }).strict());
48
58
  const BaseAgentConfigSchema = external_zod_namespaceObject.z.object({
49
59
  name: external_zod_namespaceObject.z.string().min(1).describe("Unique agent name (e.g., 'data-analyst')"),
50
60
  description: external_zod_namespaceObject.z.string().min(1).describe("Action-oriented description of what the agent does (helps root agent decide when to delegate)"),
@@ -57,7 +67,8 @@ const BaseAgentConfigSchema = external_zod_namespaceObject.z.object({
57
67
  toolHooks: types_cjs_namespaceObject.HooksConfigSchema.optional().describe("Agent-specific tool hooks configuration"),
58
68
  mcp: mcp_cjs_namespaceObject.MCPServersConfigSchema.optional().describe("Agent-specific MCP server configurations"),
59
69
  mcpUseGlobal: external_zod_namespaceObject.z.boolean().optional().default(false).describe("Whether this agent should also load global MCP servers from wingman.config.json"),
60
- voice: voice_cjs_namespaceObject.AgentVoiceConfigSchema.optional().describe("Agent-specific voice configuration")
70
+ voice: voice_cjs_namespaceObject.AgentVoiceConfigSchema.optional().describe("Agent-specific voice configuration"),
71
+ promptRefinement: PromptRefinementSchema.optional().describe("Optional per-agent prompt refinement settings")
61
72
  });
62
73
  const AgentConfigSchema = BaseAgentConfigSchema.extend({
63
74
  subAgents: external_zod_namespaceObject.z.array(BaseAgentConfigSchema).optional().describe("List of sub-agents that this agent can delegate to (each may include its own model override)")
@@ -15,6 +15,11 @@ export declare const AvailableToolNames: z.ZodEnum<{
15
15
  ui_present: "ui_present";
16
16
  }>;
17
17
  export type AvailableToolName = z.infer<typeof AvailableToolNames>;
18
+ declare const PromptRefinementSchema: z.ZodPipe<z.ZodTransform<{} | null | undefined, unknown>, z.ZodObject<{
19
+ enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
20
+ instructionsPath: z.ZodOptional<z.ZodString>;
21
+ }, z.core.$strict>>;
22
+ export type PromptRefinementConfig = z.infer<typeof PromptRefinementSchema>;
18
23
  export declare const AgentConfigSchema: z.ZodObject<{
19
24
  name: z.ZodString;
20
25
  description: z.ZodString;
@@ -98,6 +103,10 @@ export declare const AgentConfigSchema: z.ZodObject<{
98
103
  optimizeStreamingLatency: z.ZodOptional<z.ZodNumber>;
99
104
  }, z.core.$strip>>;
100
105
  }, z.core.$strip>>;
106
+ promptRefinement: z.ZodOptional<z.ZodPipe<z.ZodTransform<{} | null | undefined, unknown>, z.ZodObject<{
107
+ enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
108
+ instructionsPath: z.ZodOptional<z.ZodString>;
109
+ }, z.core.$strict>>>;
101
110
  subAgents: z.ZodOptional<z.ZodArray<z.ZodObject<{
102
111
  name: z.ZodString;
103
112
  description: z.ZodString;
@@ -181,6 +190,10 @@ export declare const AgentConfigSchema: z.ZodObject<{
181
190
  optimizeStreamingLatency: z.ZodOptional<z.ZodNumber>;
182
191
  }, z.core.$strip>>;
183
192
  }, z.core.$strip>>;
193
+ promptRefinement: z.ZodOptional<z.ZodPipe<z.ZodTransform<{} | null | undefined, unknown>, z.ZodObject<{
194
+ enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
195
+ instructionsPath: z.ZodOptional<z.ZodString>;
196
+ }, z.core.$strict>>>;
184
197
  }, z.core.$strip>>>;
185
198
  }, z.core.$strip>;
186
199
  export type WingmanAgentConfig = z.infer<typeof AgentConfigSchema>;
@@ -194,3 +207,4 @@ export declare function validateAgentConfig(config: unknown): {
194
207
  success: false;
195
208
  error: string;
196
209
  };
210
+ export {};
@@ -14,6 +14,16 @@ const AvailableToolNames = z["enum"]([
14
14
  "ui_registry_get",
15
15
  "ui_present"
16
16
  ]);
17
+ const PromptRefinementSchema = z.preprocess((value)=>{
18
+ if (void 0 === value) return;
19
+ if ("boolean" == typeof value) return {
20
+ enabled: value
21
+ };
22
+ return value;
23
+ }, z.object({
24
+ enabled: z.boolean().optional().default(true).describe("Whether prompt refinement is enabled for this agent"),
25
+ instructionsPath: z.string().min(1).optional().describe("Path (virtual) to store the agent's prompt refinement overlay (defaults under /memories/)")
26
+ }).strict());
17
27
  const BaseAgentConfigSchema = z.object({
18
28
  name: z.string().min(1).describe("Unique agent name (e.g., 'data-analyst')"),
19
29
  description: z.string().min(1).describe("Action-oriented description of what the agent does (helps root agent decide when to delegate)"),
@@ -26,7 +36,8 @@ const BaseAgentConfigSchema = z.object({
26
36
  toolHooks: HooksConfigSchema.optional().describe("Agent-specific tool hooks configuration"),
27
37
  mcp: MCPServersConfigSchema.optional().describe("Agent-specific MCP server configurations"),
28
38
  mcpUseGlobal: z.boolean().optional().default(false).describe("Whether this agent should also load global MCP servers from wingman.config.json"),
29
- voice: AgentVoiceConfigSchema.optional().describe("Agent-specific voice configuration")
39
+ voice: AgentVoiceConfigSchema.optional().describe("Agent-specific voice configuration"),
40
+ promptRefinement: PromptRefinementSchema.optional().describe("Optional per-agent prompt refinement settings")
30
41
  });
31
42
  const AgentConfigSchema = BaseAgentConfigSchema.extend({
32
43
  subAgents: z.array(BaseAgentConfigSchema).optional().describe("List of sub-agents that this agent can delegate to (each may include its own model override)")
@@ -44,6 +44,31 @@ function _define_property(obj, key, value) {
44
44
  return obj;
45
45
  }
46
46
  const logger = (0, external_logger_cjs_namespaceObject.createLogger)();
47
+ const PROMPT_REFINEMENT_MARKER = "[[wingman:prompt-refinement]]";
48
+ const normalizePromptRefinementPath = (agentName, rawPath)=>{
49
+ const fallback = `/memories/agents/${agentName}/instructions.md`;
50
+ if (!rawPath) return fallback;
51
+ const trimmed = rawPath.trim();
52
+ if (!trimmed) return fallback;
53
+ if (trimmed.startsWith("/memories/")) return trimmed;
54
+ if (trimmed.startsWith("/")) return `/memories${trimmed}`;
55
+ return `/memories/${trimmed.replace(/^\/+/, "")}`;
56
+ };
57
+ const buildPromptRefinementInstructions = (instructionsPath)=>[
58
+ PROMPT_REFINEMENT_MARKER,
59
+ "Prompt Refinement:",
60
+ `- Maintain a durable overlay at ${instructionsPath} for stable preferences and corrections.`,
61
+ "- Read it at the start of each session and follow it in addition to this system prompt.",
62
+ '- Only update it when the user gives explicit, lasting feedback (e.g., "always", "never", "prefer").',
63
+ "- Keep entries short, stable, and avoid task-specific details.",
64
+ "- If the file doesn't exist, create it with a short header and bullet list."
65
+ ].join("\n");
66
+ const applyPromptRefinement = (systemPrompt, agentName, config)=>{
67
+ if (!config?.enabled) return systemPrompt;
68
+ if (systemPrompt.includes(PROMPT_REFINEMENT_MARKER)) return systemPrompt;
69
+ const instructionsPath = normalizePromptRefinementPath(agentName, config.instructionsPath);
70
+ return `${systemPrompt.trim()}\n\n${buildPromptRefinementInstructions(instructionsPath)}`;
71
+ };
47
72
  class AgentLoader {
48
73
  resolveConfigPath(...segments) {
49
74
  const baseDir = (0, external_node_path_namespaceObject.isAbsolute)(this.configDir) ? this.configDir : (0, external_node_path_namespaceObject.join)(this.workspace, this.configDir);
@@ -180,6 +205,10 @@ class AgentLoader {
180
205
  description: config.description,
181
206
  systemPrompt: config.systemPrompt
182
207
  };
208
+ if (config.promptRefinement) {
209
+ agent.promptRefinement = config.promptRefinement;
210
+ agent.systemPrompt = applyPromptRefinement(agent.systemPrompt, config.name, config.promptRefinement);
211
+ }
183
212
  const dynamicUiEnabled = this.wingmanConfig?.gateway?.dynamicUiEnabled !== false;
184
213
  const skillsDirectory = this.wingmanConfig?.skills?.skillsDirectory || "skills";
185
214
  const buildToolOptions = (source)=>{
@@ -188,6 +217,7 @@ class AgentLoader {
188
217
  if (source.mcpUseGlobal && this.wingmanConfig?.mcp) mcpConfigs.push(this.wingmanConfig.mcp);
189
218
  return {
190
219
  workspace: this.workspace,
220
+ executionWorkspace: this.executionWorkspace,
191
221
  blockedCommands: source.blockedCommands,
192
222
  allowScriptExecution: source.allowScriptExecution,
193
223
  timeout: source.commandTimeout,
@@ -224,6 +254,10 @@ class AgentLoader {
224
254
  description: subagent.description,
225
255
  systemPrompt: subagent.systemPrompt
226
256
  };
257
+ if (subagent.promptRefinement) {
258
+ sub.promptRefinement = subagent.promptRefinement;
259
+ sub.systemPrompt = applyPromptRefinement(sub.systemPrompt, subagent.name, subagent.promptRefinement);
260
+ }
227
261
  if (subagent.tools && subagent.tools.length > 0) sub.tools = await (0, external_toolRegistry_cjs_namespaceObject.createTools)(subagent.tools, buildToolOptions(subagent));
228
262
  const subUiTools = await (0, external_toolRegistry_cjs_namespaceObject.createTools)(external_toolRegistry_cjs_namespaceObject.UI_TOOL_NAMES, buildToolOptions(subagent));
229
263
  if (subUiTools.length > 0) if (sub.tools && sub.tools.length > 0) {
@@ -246,13 +280,15 @@ class AgentLoader {
246
280
  }
247
281
  return agent;
248
282
  }
249
- constructor(configDir = external_agentConfig_cjs_namespaceObject.WingmanDirectory, workspace = process.cwd(), wingmanConfig){
283
+ constructor(configDir = external_agentConfig_cjs_namespaceObject.WingmanDirectory, workspace = process.cwd(), wingmanConfig, executionWorkspace = workspace){
250
284
  _define_property(this, "configDir", void 0);
251
285
  _define_property(this, "workspace", void 0);
252
286
  _define_property(this, "wingmanConfig", void 0);
287
+ _define_property(this, "executionWorkspace", void 0);
253
288
  this.configDir = configDir;
254
289
  this.workspace = workspace;
255
290
  this.wingmanConfig = wingmanConfig;
291
+ this.executionWorkspace = executionWorkspace;
256
292
  }
257
293
  }
258
294
  exports.AgentLoader = __webpack_exports__.AgentLoader;
@@ -8,7 +8,8 @@ export declare class AgentLoader {
8
8
  private configDir;
9
9
  private workspace;
10
10
  private wingmanConfig?;
11
- constructor(configDir?: string, workspace?: string, wingmanConfig?: WingmanConfigType | undefined);
11
+ private executionWorkspace;
12
+ constructor(configDir?: string, workspace?: string, wingmanConfig?: WingmanConfigType | undefined, executionWorkspace?: string);
12
13
  private resolveConfigPath;
13
14
  loadAllAgentConfigs(): WingmanAgentConfig[];
14
15
  /**
@@ -16,6 +16,31 @@ function _define_property(obj, key, value) {
16
16
  return obj;
17
17
  }
18
18
  const logger = createLogger();
19
+ const PROMPT_REFINEMENT_MARKER = "[[wingman:prompt-refinement]]";
20
+ const normalizePromptRefinementPath = (agentName, rawPath)=>{
21
+ const fallback = `/memories/agents/${agentName}/instructions.md`;
22
+ if (!rawPath) return fallback;
23
+ const trimmed = rawPath.trim();
24
+ if (!trimmed) return fallback;
25
+ if (trimmed.startsWith("/memories/")) return trimmed;
26
+ if (trimmed.startsWith("/")) return `/memories${trimmed}`;
27
+ return `/memories/${trimmed.replace(/^\/+/, "")}`;
28
+ };
29
+ const buildPromptRefinementInstructions = (instructionsPath)=>[
30
+ PROMPT_REFINEMENT_MARKER,
31
+ "Prompt Refinement:",
32
+ `- Maintain a durable overlay at ${instructionsPath} for stable preferences and corrections.`,
33
+ "- Read it at the start of each session and follow it in addition to this system prompt.",
34
+ '- Only update it when the user gives explicit, lasting feedback (e.g., "always", "never", "prefer").',
35
+ "- Keep entries short, stable, and avoid task-specific details.",
36
+ "- If the file doesn't exist, create it with a short header and bullet list."
37
+ ].join("\n");
38
+ const applyPromptRefinement = (systemPrompt, agentName, config)=>{
39
+ if (!config?.enabled) return systemPrompt;
40
+ if (systemPrompt.includes(PROMPT_REFINEMENT_MARKER)) return systemPrompt;
41
+ const instructionsPath = normalizePromptRefinementPath(agentName, config.instructionsPath);
42
+ return `${systemPrompt.trim()}\n\n${buildPromptRefinementInstructions(instructionsPath)}`;
43
+ };
19
44
  class AgentLoader {
20
45
  resolveConfigPath(...segments) {
21
46
  const baseDir = isAbsolute(this.configDir) ? this.configDir : join(this.workspace, this.configDir);
@@ -152,6 +177,10 @@ class AgentLoader {
152
177
  description: config.description,
153
178
  systemPrompt: config.systemPrompt
154
179
  };
180
+ if (config.promptRefinement) {
181
+ agent.promptRefinement = config.promptRefinement;
182
+ agent.systemPrompt = applyPromptRefinement(agent.systemPrompt, config.name, config.promptRefinement);
183
+ }
155
184
  const dynamicUiEnabled = this.wingmanConfig?.gateway?.dynamicUiEnabled !== false;
156
185
  const skillsDirectory = this.wingmanConfig?.skills?.skillsDirectory || "skills";
157
186
  const buildToolOptions = (source)=>{
@@ -160,6 +189,7 @@ class AgentLoader {
160
189
  if (source.mcpUseGlobal && this.wingmanConfig?.mcp) mcpConfigs.push(this.wingmanConfig.mcp);
161
190
  return {
162
191
  workspace: this.workspace,
192
+ executionWorkspace: this.executionWorkspace,
163
193
  blockedCommands: source.blockedCommands,
164
194
  allowScriptExecution: source.allowScriptExecution,
165
195
  timeout: source.commandTimeout,
@@ -196,6 +226,10 @@ class AgentLoader {
196
226
  description: subagent.description,
197
227
  systemPrompt: subagent.systemPrompt
198
228
  };
229
+ if (subagent.promptRefinement) {
230
+ sub.promptRefinement = subagent.promptRefinement;
231
+ sub.systemPrompt = applyPromptRefinement(sub.systemPrompt, subagent.name, subagent.promptRefinement);
232
+ }
199
233
  if (subagent.tools && subagent.tools.length > 0) sub.tools = await createTools(subagent.tools, buildToolOptions(subagent));
200
234
  const subUiTools = await createTools(UI_TOOL_NAMES, buildToolOptions(subagent));
201
235
  if (subUiTools.length > 0) if (sub.tools && sub.tools.length > 0) {
@@ -218,13 +252,15 @@ class AgentLoader {
218
252
  }
219
253
  return agent;
220
254
  }
221
- constructor(configDir = WingmanDirectory, workspace = process.cwd(), wingmanConfig){
255
+ constructor(configDir = WingmanDirectory, workspace = process.cwd(), wingmanConfig, executionWorkspace = workspace){
222
256
  _define_property(this, "configDir", void 0);
223
257
  _define_property(this, "workspace", void 0);
224
258
  _define_property(this, "wingmanConfig", void 0);
259
+ _define_property(this, "executionWorkspace", void 0);
225
260
  this.configDir = configDir;
226
261
  this.workspace = workspace;
227
262
  this.wingmanConfig = wingmanConfig;
263
+ this.executionWorkspace = executionWorkspace;
228
264
  }
229
265
  }
230
266
  export { AgentLoader };
@@ -103,7 +103,8 @@ class ModelFactory {
103
103
  const token = (0, credentials_cjs_namespaceObject.resolveProviderToken)("openai").token;
104
104
  const params = {
105
105
  model,
106
- temperature: 1
106
+ temperature: 1,
107
+ useResponsesApi: true
107
108
  };
108
109
  if (token) params.apiKey = token;
109
110
  return new openai_namespaceObject.ChatOpenAI(params);
@@ -75,7 +75,8 @@ class ModelFactory {
75
75
  const token = resolveProviderToken("openai").token;
76
76
  const params = {
77
77
  model,
78
- temperature: 1
78
+ temperature: 1,
79
+ useResponsesApi: true
79
80
  };
80
81
  if (token) params.apiKey = token;
81
82
  return new ChatOpenAI(params);
@@ -45,12 +45,14 @@ const UI_TOOL_NAMES = [
45
45
  "ui_present"
46
46
  ];
47
47
  function createTool(name, options = {}) {
48
- const { workspace = process.cwd(), blockedCommands, allowScriptExecution = true, timeout = 300000, searchConfig = {
48
+ const { workspace = process.cwd(), executionWorkspace, blockedCommands, allowScriptExecution = true, timeout = 300000, searchConfig = {
49
49
  provider: "duckduckgo",
50
50
  maxResults: 5
51
51
  }, skillsDirectory = "skills", dynamicUiEnabled = true } = options;
52
+ const runtimeWorkspace = executionWorkspace || workspace;
52
53
  logger.debug(`Creating tool: ${name}`, {
53
54
  workspace,
55
+ executionWorkspace: runtimeWorkspace,
54
56
  blockedCommands,
55
57
  allowScriptExecution,
56
58
  timeout,
@@ -62,13 +64,13 @@ function createTool(name, options = {}) {
62
64
  case "web_crawler":
63
65
  return web_crawler_cjs_namespaceObject.webCrawler;
64
66
  case "command_execute":
65
- return (0, command_execute_cjs_namespaceObject.createCommandExecuteTool)(workspace, process.env, blockedCommands, allowScriptExecution, timeout);
67
+ return (0, command_execute_cjs_namespaceObject.createCommandExecuteTool)(runtimeWorkspace, process.env, blockedCommands, allowScriptExecution, timeout);
66
68
  case "think":
67
69
  return (0, think_cjs_namespaceObject.createThinkingTool)();
68
70
  case "code_search":
69
- return (0, code_search_cjs_namespaceObject.createCodeSearchTool)(workspace);
71
+ return (0, code_search_cjs_namespaceObject.createCodeSearchTool)(runtimeWorkspace);
70
72
  case "git_status":
71
- return (0, git_status_cjs_namespaceObject.createGitStatusTool)(workspace);
73
+ return (0, git_status_cjs_namespaceObject.createGitStatusTool)(runtimeWorkspace);
72
74
  case "ui_registry_list":
73
75
  return (0, ui_registry_cjs_namespaceObject.createUiRegistryListTool)(workspace, skillsDirectory);
74
76
  case "ui_registry_get":
@@ -4,6 +4,7 @@ import type { SearchConfig } from "../../cli/config/schema.js";
4
4
  import type { MCPServersConfig } from "@/types/mcp.js";
5
5
  export interface ToolOptions {
6
6
  workspace?: string;
7
+ executionWorkspace?: string;
7
8
  blockedCommands?: string[];
8
9
  allowScriptExecution?: boolean;
9
10
  timeout?: number;
@@ -14,12 +14,14 @@ const UI_TOOL_NAMES = [
14
14
  "ui_present"
15
15
  ];
16
16
  function createTool(name, options = {}) {
17
- const { workspace = process.cwd(), blockedCommands, allowScriptExecution = true, timeout = 300000, searchConfig = {
17
+ const { workspace = process.cwd(), executionWorkspace, blockedCommands, allowScriptExecution = true, timeout = 300000, searchConfig = {
18
18
  provider: "duckduckgo",
19
19
  maxResults: 5
20
20
  }, skillsDirectory = "skills", dynamicUiEnabled = true } = options;
21
+ const runtimeWorkspace = executionWorkspace || workspace;
21
22
  logger.debug(`Creating tool: ${name}`, {
22
23
  workspace,
24
+ executionWorkspace: runtimeWorkspace,
23
25
  blockedCommands,
24
26
  allowScriptExecution,
25
27
  timeout,
@@ -31,13 +33,13 @@ function createTool(name, options = {}) {
31
33
  case "web_crawler":
32
34
  return webCrawler;
33
35
  case "command_execute":
34
- return createCommandExecuteTool(workspace, process.env, blockedCommands, allowScriptExecution, timeout);
36
+ return createCommandExecuteTool(runtimeWorkspace, process.env, blockedCommands, allowScriptExecution, timeout);
35
37
  case "think":
36
38
  return createThinkingTool();
37
39
  case "code_search":
38
- return createCodeSearchTool(workspace);
40
+ return createCodeSearchTool(runtimeWorkspace);
39
41
  case "git_status":
40
- return createGitStatusTool(workspace);
42
+ return createGitStatusTool(runtimeWorkspace);
41
43
  case "ui_registry_list":
42
44
  return createUiRegistryListTool(workspace, skillsDirectory);
43
45
  case "ui_registry_get":
@@ -44,13 +44,18 @@ const buildOutputLocationMessage = (context)=>{
44
44
  if (!targetPath) return null;
45
45
  const locationLabel = context.workdir ? "session output directory" : "default output directory";
46
46
  const safePath = toSafeRelativePath(context.workspaceRoot, targetPath);
47
- const locationLine = safePath ? `- Use the ${locationLabel}: ${safePath}` : `- Use the ${locationLabel} (path hidden).`;
47
+ const virtualPath = context.outputVirtualPath?.trim();
48
+ const locationLine = safePath ? `- Use the ${locationLabel}: ${safePath}` : virtualPath ? `- Use the ${locationLabel}: ${virtualPath}` : `- Use the ${locationLabel} (path hidden).`;
48
49
  return [
49
50
  "** Output Location **",
50
51
  locationLine,
51
52
  "- If the user asks for a location, provide a relative path and avoid absolute paths or usernames."
52
53
  ].join("\n");
53
54
  };
55
+ const buildWorkingDirectoryMessage = (context)=>{
56
+ if (!context.workspaceRoot) return null;
57
+ return "** Working Directory **\n- Treat `./` as the current working directory for file and tool operations in this session.\n- Use relative paths such as `./test.md`; do not prepend the working directory absolute path.";
58
+ };
54
59
  const additionalMessageMiddleware = (context = {})=>({
55
60
  name: "additional-message-middleware",
56
61
  [external_langchain_namespaceObject.MIDDLEWARE_BRAND]: true,
@@ -63,6 +68,8 @@ const additionalMessageMiddleware = (context = {})=>({
63
68
  ];
64
69
  const outputLocation = buildOutputLocationMessage(context);
65
70
  if (outputLocation) lines.push(outputLocation);
71
+ const workingDirectory = buildWorkingDirectoryMessage(context);
72
+ if (workingDirectory) lines.push(workingDirectory);
66
73
  lines.push("** Long-term memory **\n- Use /memories/ for durable notes across threads.\n- Store stable preferences, project context, decisions, and research notes.\n- Avoid transient logs; keep entries concise and organized.\n- Suggested paths: /memories/preferences.md, /memories/projects/<name>/context.md, /memories/projects/<name>/decisions.md");
67
74
  if (false === context.dynamicUiEnabled) lines.push("** Dynamic UI **\n- Dynamic UI rendering is disabled for this gateway.\n- Respond with plain text and avoid calling UI presentation tools.");
68
75
  else {
@@ -3,6 +3,7 @@ type AdditionalMessageContext = {
3
3
  workspaceRoot?: string | null;
4
4
  workdir?: string | null;
5
5
  defaultOutputDir?: string | null;
6
+ outputVirtualPath?: string | null;
6
7
  dynamicUiEnabled?: boolean;
7
8
  skillsDirectory?: string;
8
9
  };
@@ -16,13 +16,18 @@ const buildOutputLocationMessage = (context)=>{
16
16
  if (!targetPath) return null;
17
17
  const locationLabel = context.workdir ? "session output directory" : "default output directory";
18
18
  const safePath = toSafeRelativePath(context.workspaceRoot, targetPath);
19
- const locationLine = safePath ? `- Use the ${locationLabel}: ${safePath}` : `- Use the ${locationLabel} (path hidden).`;
19
+ const virtualPath = context.outputVirtualPath?.trim();
20
+ const locationLine = safePath ? `- Use the ${locationLabel}: ${safePath}` : virtualPath ? `- Use the ${locationLabel}: ${virtualPath}` : `- Use the ${locationLabel} (path hidden).`;
20
21
  return [
21
22
  "** Output Location **",
22
23
  locationLine,
23
24
  "- If the user asks for a location, provide a relative path and avoid absolute paths or usernames."
24
25
  ].join("\n");
25
26
  };
27
+ const buildWorkingDirectoryMessage = (context)=>{
28
+ if (!context.workspaceRoot) return null;
29
+ return "** Working Directory **\n- Treat `./` as the current working directory for file and tool operations in this session.\n- Use relative paths such as `./test.md`; do not prepend the working directory absolute path.";
30
+ };
26
31
  const additionalMessageMiddleware = (context = {})=>({
27
32
  name: "additional-message-middleware",
28
33
  [MIDDLEWARE_BRAND]: true,
@@ -35,6 +40,8 @@ const additionalMessageMiddleware = (context = {})=>({
35
40
  ];
36
41
  const outputLocation = buildOutputLocationMessage(context);
37
42
  if (outputLocation) lines.push(outputLocation);
43
+ const workingDirectory = buildWorkingDirectoryMessage(context);
44
+ if (workingDirectory) lines.push(workingDirectory);
38
45
  lines.push("** Long-term memory **\n- Use /memories/ for durable notes across threads.\n- Store stable preferences, project context, decisions, and research notes.\n- Avoid transient logs; keep entries concise and organized.\n- Suggested paths: /memories/preferences.md, /memories/projects/<name>/context.md, /memories/projects/<name>/decisions.md");
39
46
  if (false === context.dynamicUiEnabled) lines.push("** Dynamic UI **\n- Dynamic UI rendering is disabled for this gateway.\n- Respond with plain text and avoid calling UI presentation tools.");
40
47
  else {
@@ -102,6 +102,31 @@ const agentConfig_cjs_namespaceObject = require("../config/agentConfig.cjs");
102
102
  (0, external_vitest_namespaceObject.expect)(parsed.allowScriptExecution).toBe(true);
103
103
  (0, external_vitest_namespaceObject.expect)(parsed.commandTimeout).toBe(300000);
104
104
  });
105
+ (0, external_vitest_namespaceObject.it)("should accept prompt refinement configuration", ()=>{
106
+ const config = {
107
+ name: "refiner",
108
+ description: "Refines its prompt",
109
+ systemPrompt: "You are a refiner",
110
+ promptRefinement: true
111
+ };
112
+ const result = (0, agentConfig_cjs_namespaceObject.validateAgentConfig)(config);
113
+ (0, external_vitest_namespaceObject.expect)(result.success).toBe(true);
114
+ if (result.success) (0, external_vitest_namespaceObject.expect)(result.data.promptRefinement?.enabled).toBe(true);
115
+ const configWithPath = {
116
+ name: "refiner-2",
117
+ description: "Refines its prompt with path",
118
+ systemPrompt: "You are a refiner",
119
+ promptRefinement: {
120
+ instructionsPath: "/memories/agents/refiner-2/instructions.md"
121
+ }
122
+ };
123
+ const resultWithPath = (0, agentConfig_cjs_namespaceObject.validateAgentConfig)(configWithPath);
124
+ (0, external_vitest_namespaceObject.expect)(resultWithPath.success).toBe(true);
125
+ if (resultWithPath.success) {
126
+ (0, external_vitest_namespaceObject.expect)(resultWithPath.data.promptRefinement?.instructionsPath).toBe("/memories/agents/refiner-2/instructions.md");
127
+ (0, external_vitest_namespaceObject.expect)(resultWithPath.data.promptRefinement?.enabled).toBe(true);
128
+ }
129
+ });
105
130
  });
106
131
  (0, external_vitest_namespaceObject.describe)("Tool names enum", ()=>{
107
132
  (0, external_vitest_namespaceObject.it)("should accept all valid tool names", ()=>{
@@ -100,6 +100,31 @@ describe("Agent Configuration Schema", ()=>{
100
100
  expect(parsed.allowScriptExecution).toBe(true);
101
101
  expect(parsed.commandTimeout).toBe(300000);
102
102
  });
103
+ it("should accept prompt refinement configuration", ()=>{
104
+ const config = {
105
+ name: "refiner",
106
+ description: "Refines its prompt",
107
+ systemPrompt: "You are a refiner",
108
+ promptRefinement: true
109
+ };
110
+ const result = validateAgentConfig(config);
111
+ expect(result.success).toBe(true);
112
+ if (result.success) expect(result.data.promptRefinement?.enabled).toBe(true);
113
+ const configWithPath = {
114
+ name: "refiner-2",
115
+ description: "Refines its prompt with path",
116
+ systemPrompt: "You are a refiner",
117
+ promptRefinement: {
118
+ instructionsPath: "/memories/agents/refiner-2/instructions.md"
119
+ }
120
+ };
121
+ const resultWithPath = validateAgentConfig(configWithPath);
122
+ expect(resultWithPath.success).toBe(true);
123
+ if (resultWithPath.success) {
124
+ expect(resultWithPath.data.promptRefinement?.instructionsPath).toBe("/memories/agents/refiner-2/instructions.md");
125
+ expect(resultWithPath.data.promptRefinement?.enabled).toBe(true);
126
+ }
127
+ });
103
128
  });
104
129
  describe("Tool names enum", ()=>{
105
130
  it("should accept all valid tool names", ()=>{
@@ -227,6 +227,24 @@ Markdown agent`;
227
227
  (0, external_vitest_namespaceObject.expect)(Array.isArray(sub.tools)).toBe(true);
228
228
  (0, external_vitest_namespaceObject.expect)(sub.tools?.[0]).toHaveProperty("name", "think");
229
229
  });
230
+ (0, external_vitest_namespaceObject.it)("should append prompt refinement instructions when enabled", async ()=>{
231
+ const agentDir = (0, external_node_path_namespaceObject.join)(TEST_CONFIG_DIR, "agents", "refiner-agent");
232
+ (0, external_node_fs_namespaceObject.mkdirSync)(agentDir, {
233
+ recursive: true
234
+ });
235
+ const config = {
236
+ name: "refiner-agent",
237
+ description: "Agent that refines its prompt",
238
+ systemPrompt: "You are a refiner agent",
239
+ promptRefinement: true
240
+ };
241
+ (0, external_node_fs_namespaceObject.writeFileSync)((0, external_node_path_namespaceObject.join)(agentDir, "agent.json"), JSON.stringify(config));
242
+ const loader = new agentLoader_cjs_namespaceObject.AgentLoader(TEST_CONFIG_DIR);
243
+ const agent = await loader.loadAgent("refiner-agent");
244
+ (0, external_vitest_namespaceObject.expect)(agent).toBeDefined();
245
+ (0, external_vitest_namespaceObject.expect)(agent?.systemPrompt).toContain("[[wingman:prompt-refinement]]");
246
+ (0, external_vitest_namespaceObject.expect)(agent?.systemPrompt).toContain("/memories/agents/refiner-agent/instructions.md");
247
+ });
230
248
  });
231
249
  });
232
250
  for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
@@ -225,5 +225,23 @@ Markdown agent`;
225
225
  expect(Array.isArray(sub.tools)).toBe(true);
226
226
  expect(sub.tools?.[0]).toHaveProperty("name", "think");
227
227
  });
228
+ it("should append prompt refinement instructions when enabled", async ()=>{
229
+ const agentDir = join(TEST_CONFIG_DIR, "agents", "refiner-agent");
230
+ mkdirSync(agentDir, {
231
+ recursive: true
232
+ });
233
+ const config = {
234
+ name: "refiner-agent",
235
+ description: "Agent that refines its prompt",
236
+ systemPrompt: "You are a refiner agent",
237
+ promptRefinement: true
238
+ };
239
+ writeFileSync(join(agentDir, "agent.json"), JSON.stringify(config));
240
+ const loader = new AgentLoader(TEST_CONFIG_DIR);
241
+ const agent = await loader.loadAgent("refiner-agent");
242
+ expect(agent).toBeDefined();
243
+ expect(agent?.systemPrompt).toContain("[[wingman:prompt-refinement]]");
244
+ expect(agent?.systemPrompt).toContain("/memories/agents/refiner-agent/instructions.md");
245
+ });
228
246
  });
229
247
  });