@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.
- package/.wingman/agents/README.md +7 -0
- package/.wingman/agents/coding/agent.md +1 -0
- package/.wingman/agents/main/agent.md +1 -0
- package/.wingman/agents/researcher/agent.md +1 -0
- package/.wingman/agents/stock-trader/agent.md +8 -2
- package/dist/agent/config/agentConfig.cjs +12 -1
- package/dist/agent/config/agentConfig.d.ts +14 -0
- package/dist/agent/config/agentConfig.js +12 -1
- package/dist/agent/config/agentLoader.cjs +37 -1
- package/dist/agent/config/agentLoader.d.ts +2 -1
- package/dist/agent/config/agentLoader.js +37 -1
- package/dist/agent/config/modelFactory.cjs +2 -1
- package/dist/agent/config/modelFactory.js +2 -1
- package/dist/agent/config/toolRegistry.cjs +6 -4
- package/dist/agent/config/toolRegistry.d.ts +1 -0
- package/dist/agent/config/toolRegistry.js +6 -4
- package/dist/agent/middleware/additional-messages.cjs +8 -1
- package/dist/agent/middleware/additional-messages.d.ts +1 -0
- package/dist/agent/middleware/additional-messages.js +8 -1
- package/dist/agent/tests/agentConfig.test.cjs +25 -0
- package/dist/agent/tests/agentConfig.test.js +25 -0
- package/dist/agent/tests/agentLoader.test.cjs +18 -0
- package/dist/agent/tests/agentLoader.test.js +18 -0
- package/dist/agent/tests/modelFactory.test.cjs +13 -0
- package/dist/agent/tests/modelFactory.test.js +14 -1
- package/dist/agent/tests/toolRegistry.test.cjs +15 -0
- package/dist/agent/tests/toolRegistry.test.js +15 -0
- package/dist/agent/tests/uiRegistryTools.test.cjs +15 -0
- package/dist/agent/tests/uiRegistryTools.test.js +15 -0
- package/dist/agent/tools/code_search.cjs +1 -1
- package/dist/agent/tools/code_search.js +1 -1
- package/dist/agent/tools/command_execute.cjs +1 -1
- package/dist/agent/tools/command_execute.js +1 -1
- package/dist/agent/tools/ui_registry.d.ts +3 -3
- package/dist/cli/core/agentInvoker.cjs +212 -21
- package/dist/cli/core/agentInvoker.d.ts +55 -20
- package/dist/cli/core/agentInvoker.js +197 -21
- package/dist/cli/core/sessionManager.cjs +93 -4
- package/dist/cli/core/sessionManager.d.ts +1 -1
- package/dist/cli/core/sessionManager.js +93 -4
- package/dist/gateway/http/agents.cjs +121 -10
- package/dist/gateway/http/agents.js +121 -10
- package/dist/gateway/server.cjs +55 -17
- package/dist/gateway/server.js +55 -17
- package/dist/gateway/types.d.ts +9 -1
- package/dist/tests/additionalMessageMiddleware.test.cjs +26 -0
- package/dist/tests/additionalMessageMiddleware.test.js +26 -0
- package/dist/tests/agentInvokerAttachments.test.cjs +123 -0
- package/dist/tests/agentInvokerAttachments.test.js +123 -0
- package/dist/tests/agentInvokerWorkdir.test.cjs +100 -0
- package/dist/tests/agentInvokerWorkdir.test.d.ts +1 -0
- package/dist/tests/agentInvokerWorkdir.test.js +72 -0
- package/dist/tests/agents-api.test.cjs +232 -0
- package/dist/tests/agents-api.test.d.ts +1 -0
- package/dist/tests/agents-api.test.js +226 -0
- package/dist/tests/gateway.test.cjs +24 -0
- package/dist/tests/gateway.test.js +24 -0
- package/dist/tests/sessionMessageAttachments.test.cjs +59 -0
- package/dist/tests/sessionMessageAttachments.test.js +59 -0
- package/dist/types/agents.d.ts +5 -0
- package/dist/webui/assets/{index-DTK8ignm.css → index-BytPznA_.css} +1 -1
- package/dist/webui/assets/index-u_5qlVip.js +176 -0
- package/dist/webui/index.html +2 -2
- package/package.json +7 -7
- package/skills/ui-registry/registry.json +90 -0
- package/.wingman/agents/wingman/agent.json +0 -12
- 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
|
|
|
@@ -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
|
-
-
|
|
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
|
-
|
|
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);
|
|
@@ -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)(
|
|
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)(
|
|
71
|
+
return (0, code_search_cjs_namespaceObject.createCodeSearchTool)(runtimeWorkspace);
|
|
70
72
|
case "git_status":
|
|
71
|
-
return (0, git_status_cjs_namespaceObject.createGitStatusTool)(
|
|
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(
|
|
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(
|
|
40
|
+
return createCodeSearchTool(runtimeWorkspace);
|
|
39
41
|
case "git_status":
|
|
40
|
-
return createGitStatusTool(
|
|
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
|
|
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 {
|
|
@@ -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
|
|
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
|
});
|