@wingman-ai/gateway 0.4.0 → 0.4.2
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/README.md +29 -111
- package/dist/agent/config/agentConfig.cjs +2 -0
- package/dist/agent/config/agentConfig.d.ts +6 -0
- package/dist/agent/config/agentConfig.js +2 -0
- package/dist/agent/config/agentLoader.cjs +21 -18
- package/dist/agent/config/agentLoader.js +22 -19
- package/dist/agent/config/toolRegistry.cjs +19 -0
- package/dist/agent/config/toolRegistry.d.ts +4 -0
- package/dist/agent/config/toolRegistry.js +17 -1
- package/dist/agent/middleware/additional-messages.cjs +115 -11
- package/dist/agent/middleware/additional-messages.d.ts +9 -0
- package/dist/agent/middleware/additional-messages.js +115 -11
- package/dist/agent/tests/agentLoader.test.cjs +45 -0
- package/dist/agent/tests/agentLoader.test.js +45 -0
- package/dist/agent/tests/toolRegistry.test.cjs +2 -0
- package/dist/agent/tests/toolRegistry.test.js +2 -0
- package/dist/agent/tools/node_invoke.cjs +146 -0
- package/dist/agent/tools/node_invoke.d.ts +86 -0
- package/dist/agent/tools/node_invoke.js +109 -0
- package/dist/cli/commands/gateway.cjs +1 -1
- package/dist/cli/commands/gateway.js +1 -1
- package/dist/cli/commands/init.cjs +135 -1
- package/dist/cli/commands/init.js +136 -2
- package/dist/cli/commands/skill.cjs +7 -3
- package/dist/cli/commands/skill.js +7 -3
- package/dist/cli/config/loader.cjs +7 -3
- package/dist/cli/config/loader.js +7 -3
- package/dist/cli/config/schema.cjs +27 -9
- package/dist/cli/config/schema.d.ts +18 -4
- package/dist/cli/config/schema.js +23 -8
- package/dist/cli/core/agentInvoker.cjs +70 -14
- package/dist/cli/core/agentInvoker.d.ts +10 -0
- package/dist/cli/core/agentInvoker.js +70 -14
- package/dist/cli/services/skillRepository.cjs +155 -69
- package/dist/cli/services/skillRepository.d.ts +7 -2
- package/dist/cli/services/skillRepository.js +155 -69
- package/dist/cli/services/skillService.cjs +93 -26
- package/dist/cli/services/skillService.d.ts +7 -0
- package/dist/cli/services/skillService.js +96 -29
- package/dist/cli/types/skill.d.ts +8 -3
- package/dist/gateway/http/nodes.cjs +247 -0
- package/dist/gateway/http/nodes.d.ts +20 -0
- package/dist/gateway/http/nodes.js +210 -0
- package/dist/gateway/node.cjs +10 -1
- package/dist/gateway/node.d.ts +10 -1
- package/dist/gateway/node.js +10 -1
- package/dist/gateway/server.cjs +414 -27
- package/dist/gateway/server.d.ts +34 -0
- package/dist/gateway/server.js +408 -27
- package/dist/gateway/types.d.ts +6 -1
- package/dist/gateway/validation.cjs +2 -0
- package/dist/gateway/validation.d.ts +4 -0
- package/dist/gateway/validation.js +2 -0
- package/dist/skills/activation.cjs +92 -0
- package/dist/skills/activation.d.ts +12 -0
- package/dist/skills/activation.js +58 -0
- package/dist/skills/bin-requirements.cjs +63 -0
- package/dist/skills/bin-requirements.d.ts +3 -0
- package/dist/skills/bin-requirements.js +26 -0
- package/dist/skills/metadata.cjs +141 -0
- package/dist/skills/metadata.d.ts +29 -0
- package/dist/skills/metadata.js +104 -0
- package/dist/skills/overlay.cjs +75 -0
- package/dist/skills/overlay.d.ts +2 -0
- package/dist/skills/overlay.js +38 -0
- package/dist/tests/additionalMessageMiddleware.test.cjs +92 -0
- package/dist/tests/additionalMessageMiddleware.test.js +92 -0
- package/dist/tests/cli-config-loader.test.cjs +7 -3
- package/dist/tests/cli-config-loader.test.js +7 -3
- package/dist/tests/cli-init.test.cjs +54 -0
- package/dist/tests/cli-init.test.js +54 -0
- package/dist/tests/config-json-schema.test.cjs +12 -0
- package/dist/tests/config-json-schema.test.js +12 -0
- package/dist/tests/gateway-http-security.test.cjs +277 -0
- package/dist/tests/gateway-http-security.test.d.ts +1 -0
- package/dist/tests/gateway-http-security.test.js +271 -0
- package/dist/tests/gateway-node-mode.test.cjs +174 -0
- package/dist/tests/gateway-node-mode.test.d.ts +1 -0
- package/dist/tests/gateway-node-mode.test.js +168 -0
- package/dist/tests/gateway-origin-policy.test.cjs +60 -0
- package/dist/tests/gateway-origin-policy.test.d.ts +1 -0
- package/dist/tests/gateway-origin-policy.test.js +54 -0
- package/dist/tests/gateway.test.cjs +1 -0
- package/dist/tests/gateway.test.js +1 -0
- package/dist/tests/node-tools.test.cjs +77 -0
- package/dist/tests/node-tools.test.d.ts +1 -0
- package/dist/tests/node-tools.test.js +71 -0
- package/dist/tests/nodes-api.test.cjs +86 -0
- package/dist/tests/nodes-api.test.d.ts +1 -0
- package/dist/tests/nodes-api.test.js +80 -0
- package/dist/tests/skill-activation.test.cjs +86 -0
- package/dist/tests/skill-activation.test.d.ts +1 -0
- package/dist/tests/skill-activation.test.js +80 -0
- package/dist/tests/skill-metadata.test.cjs +119 -0
- package/dist/tests/skill-metadata.test.d.ts +1 -0
- package/dist/tests/skill-metadata.test.js +113 -0
- package/dist/tests/skill-repository.test.cjs +363 -0
- package/dist/tests/skill-repository.test.js +363 -0
- package/dist/webui/assets/{index-DHbfLOUR.js → index-BMekSELC.js} +106 -106
- package/dist/webui/index.html +1 -1
- package/package.json +4 -4
- package/skills/gog/SKILL.md +1 -1
- package/skills/weather/SKILL.md +1 -1
- package/skills/ui-registry/SKILL.md +0 -35
package/README.md
CHANGED
|
@@ -123,128 +123,46 @@ wingman provider login ollama # Optional
|
|
|
123
123
|
wingman agent --local --agent <id> "prompt"
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
##
|
|
126
|
+
## Secure Skills + MCP Proxy (TL;DR)
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
Main point: skill scanning and MCP proxy are separate toggles, both explicit, and `uv` checks only happen when the feature is enabled.
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
Key CLI commands:
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
- `gateway.auth.allowTailscale` - trust Tailscale identity headers so Tailnet users can access without tokens.
|
|
137
|
-
- `gateway.controlUi.enabled` / `gateway.controlUi.port` - enable/disable Control UI and choose its port.
|
|
138
|
-
- `gateway.controlUi.pairingRequired` - require pairing for Control UI clients (recommended).
|
|
139
|
-
- `gateway.controlUi.allowInsecureAuth` - only for local dev when testing auth flows.
|
|
140
|
-
- `gateway.adapters.discord.*` - Discord output adapter:
|
|
141
|
-
- `enabled`, `token`, `mentionOnly`, `allowBots`, `allowedGuilds`, `allowedChannels`
|
|
142
|
-
- `channelSessions` to pin channels to a session (or `agent:<id>:` to force routing)
|
|
143
|
-
- `sessionCommand` for ad-hoc session overrides
|
|
144
|
-
- `responseChunkSize` to fit Discord message limits
|
|
145
|
-
- Optional `gatewayUrl`, `gatewayToken`, `gatewayPassword` to point the adapter at a remote gateway
|
|
146
|
-
|
|
147
|
-
### 2) Runtime flags (`wingman gateway start` / `run`)
|
|
148
|
-
|
|
149
|
-
- `--host`, `--port` - override bind address + port for this run.
|
|
150
|
-
- `--auth`, `--auth-mode`, `--token`, `--password` - enable auth without editing config.
|
|
151
|
-
- `--discovery mdns|tailscale`, `--name` - advertise your gateway for LAN or Tailnet discovery.
|
|
152
|
-
- `--max-nodes`, `--ping-interval`, `--ping-timeout` - tune scale and heartbeat behavior.
|
|
153
|
-
- `--log-level` - dial verbosity for debugging or production.
|
|
154
|
-
|
|
155
|
-
### 3) Environment overrides
|
|
156
|
-
|
|
157
|
-
- `WINGMAN_GATEWAY_TOKEN` - supply a token at runtime so you don't store secrets in config.
|
|
158
|
-
|
|
159
|
-
### Related gateway behavior (configured elsewhere)
|
|
160
|
-
|
|
161
|
-
- `agents.bindings` - deterministic routing rules used by the gateway to select an agent per inbound channel/message.
|
|
162
|
-
- `voice` - gateway TTS defaults (provider + settings), with optional per-agent overrides for voice-enabled UIs.
|
|
163
|
-
|
|
164
|
-
### Example configs (common setups)
|
|
132
|
+
```bash
|
|
133
|
+
# gateway auth token
|
|
134
|
+
wingman gateway token --generate
|
|
135
|
+
export WINGMAN_GATEWAY_TOKEN="<token>"
|
|
165
136
|
|
|
166
|
-
|
|
137
|
+
# gateway runtime
|
|
138
|
+
wingman gateway start
|
|
167
139
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
"auth": { "mode": "none" },
|
|
174
|
-
"controlUi": { "enabled": true, "port": 18790 }
|
|
175
|
-
}
|
|
176
|
-
}
|
|
140
|
+
# skills
|
|
141
|
+
wingman skill browse
|
|
142
|
+
wingman skill install <skill-name>
|
|
143
|
+
wingman skill list
|
|
144
|
+
wingman skill remove <skill-name>
|
|
177
145
|
```
|
|
178
146
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
"gateway": {
|
|
184
|
-
"host": "0.0.0.0",
|
|
185
|
-
"port": 18789,
|
|
186
|
-
"fsRoots": ["~/Projects", "~/.wingman/outputs"],
|
|
187
|
-
"auth": { "mode": "token" },
|
|
188
|
-
"controlUi": { "enabled": true, "port": 18790, "pairingRequired": true }
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
```
|
|
147
|
+
- Skill scan runs on each `wingman skill install` only when `skills.security.scanOnInstall` is enabled.
|
|
148
|
+
- MCP proxy runs at agent runtime only when `gateway.mcpProxy.enabled` is enabled.
|
|
149
|
+
- If `uv` is missing for an enabled feature, Wingman fails with an error (no interactive prompt).
|
|
150
|
+
- Full config examples: `../docs-website/docs/configuration/skills.mdx`, `../docs-website/docs/configuration/gateway.mdx`, `../docs-website/docs/configuration/wingman-config.mdx`.
|
|
192
151
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
#### 3) Headless gateway + Discord output adapter
|
|
196
|
-
|
|
197
|
-
```json
|
|
198
|
-
{
|
|
199
|
-
"gateway": {
|
|
200
|
-
"host": "0.0.0.0",
|
|
201
|
-
"port": 18789,
|
|
202
|
-
"auth": { "mode": "token" },
|
|
203
|
-
"controlUi": { "enabled": false },
|
|
204
|
-
"adapters": {
|
|
205
|
-
"discord": {
|
|
206
|
-
"enabled": true,
|
|
207
|
-
"token": "DISCORD_BOT_TOKEN",
|
|
208
|
-
"mentionOnly": true,
|
|
209
|
-
"allowedGuilds": ["123456789012345678"],
|
|
210
|
-
"allowedChannels": ["987654321098765432"],
|
|
211
|
-
"channelSessions": {
|
|
212
|
-
"987654321098765432": "agent:support:discord:channel:987654321098765432"
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
```
|
|
152
|
+
## Configuration
|
|
219
153
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
```json
|
|
223
|
-
{
|
|
224
|
-
"gateway": {
|
|
225
|
-
"host": "0.0.0.0",
|
|
226
|
-
"port": 18789,
|
|
227
|
-
"auth": { "mode": "token", "allowTailscale": true },
|
|
228
|
-
"controlUi": { "enabled": true, "port": 18790, "pairingRequired": true }
|
|
229
|
-
},
|
|
230
|
-
"voice": {
|
|
231
|
-
"provider": "elevenlabs",
|
|
232
|
-
"defaultPolicy": "off",
|
|
233
|
-
"elevenlabs": {
|
|
234
|
-
"voiceId": "VOICE_ID",
|
|
235
|
-
"modelId": "eleven_multilingual_v2",
|
|
236
|
-
"stability": 0.4,
|
|
237
|
-
"similarityBoost": 0.7
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
```
|
|
154
|
+
Where to configure:
|
|
242
155
|
|
|
243
|
-
|
|
156
|
+
- Runtime flags: `wingman gateway start --help`
|
|
157
|
+
- Environment secret: `WINGMAN_GATEWAY_TOKEN`
|
|
158
|
+
- Persistent config: `.wingman/wingman.config.json`
|
|
159
|
+
- JSON schema: `https://getwingmanai.com/schemas/wingman.config.schema.json`
|
|
244
160
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
161
|
+
Docs (full examples):
|
|
162
|
+
|
|
163
|
+
- Gateway: `../docs-website/docs/configuration/gateway.mdx`
|
|
164
|
+
- Skills: `../docs-website/docs/configuration/skills.mdx`
|
|
165
|
+
- Full config: `../docs-website/docs/configuration/wingman-config.mdx`
|
|
248
166
|
|
|
249
167
|
## Core Concepts
|
|
250
168
|
|
|
@@ -9,6 +9,8 @@ export declare const AvailableToolNames: z.ZodEnum<{
|
|
|
9
9
|
browser_control: "browser_control";
|
|
10
10
|
command_execute: "command_execute";
|
|
11
11
|
background_terminal: "background_terminal";
|
|
12
|
+
node_notify: "node_notify";
|
|
13
|
+
node_run: "node_run";
|
|
12
14
|
think: "think";
|
|
13
15
|
code_search: "code_search";
|
|
14
16
|
git_status: "git_status";
|
|
@@ -44,6 +46,8 @@ export declare const AgentConfigSchema: z.ZodObject<{
|
|
|
44
46
|
browser_control: "browser_control";
|
|
45
47
|
command_execute: "command_execute";
|
|
46
48
|
background_terminal: "background_terminal";
|
|
49
|
+
node_notify: "node_notify";
|
|
50
|
+
node_run: "node_run";
|
|
47
51
|
think: "think";
|
|
48
52
|
code_search: "code_search";
|
|
49
53
|
git_status: "git_status";
|
|
@@ -148,6 +152,8 @@ export declare const AgentConfigSchema: z.ZodObject<{
|
|
|
148
152
|
browser_control: "browser_control";
|
|
149
153
|
command_execute: "command_execute";
|
|
150
154
|
background_terminal: "background_terminal";
|
|
155
|
+
node_notify: "node_notify";
|
|
156
|
+
node_run: "node_run";
|
|
151
157
|
think: "think";
|
|
152
158
|
code_search: "code_search";
|
|
153
159
|
git_status: "git_status";
|
|
@@ -45,6 +45,10 @@ function _define_property(obj, key, value) {
|
|
|
45
45
|
}
|
|
46
46
|
const logger = (0, external_logger_cjs_namespaceObject.createLogger)();
|
|
47
47
|
const PROMPT_REFINEMENT_MARKER = "[[wingman:prompt-refinement]]";
|
|
48
|
+
const ALWAYS_ON_TOOL_NAMES = [
|
|
49
|
+
...external_toolRegistry_cjs_namespaceObject.UI_TOOL_NAMES,
|
|
50
|
+
...external_toolRegistry_cjs_namespaceObject.NODE_TOOL_NAMES
|
|
51
|
+
];
|
|
48
52
|
const normalizePromptRefinementPath = (agentName, rawPath)=>{
|
|
49
53
|
const fallback = `/memories/agents/${agentName}/instructions.md`;
|
|
50
54
|
if (!rawPath) return fallback;
|
|
@@ -244,16 +248,23 @@ class AgentLoader {
|
|
|
244
248
|
...this.runtimeToolOptions
|
|
245
249
|
};
|
|
246
250
|
};
|
|
251
|
+
const addAlwaysOnTools = async (existingTools, source)=>{
|
|
252
|
+
const alwaysOnTools = await (0, external_toolRegistry_cjs_namespaceObject.createTools)([
|
|
253
|
+
...ALWAYS_ON_TOOL_NAMES
|
|
254
|
+
], buildToolOptions(source));
|
|
255
|
+
if (0 === alwaysOnTools.length) return existingTools || [];
|
|
256
|
+
if (existingTools && existingTools.length > 0) {
|
|
257
|
+
const existing = new Set(existingTools.map((tool)=>tool.name));
|
|
258
|
+
const uniqueAlwaysOnTools = alwaysOnTools.filter((tool)=>!existing.has(tool.name));
|
|
259
|
+
return [
|
|
260
|
+
...existingTools,
|
|
261
|
+
...uniqueAlwaysOnTools
|
|
262
|
+
];
|
|
263
|
+
}
|
|
264
|
+
return alwaysOnTools;
|
|
265
|
+
};
|
|
247
266
|
if (config.tools && config.tools.length > 0) agent.tools = await (0, external_toolRegistry_cjs_namespaceObject.createTools)(config.tools, buildToolOptions(config));
|
|
248
|
-
|
|
249
|
-
if (uiTools.length > 0) if (agent.tools && agent.tools.length > 0) {
|
|
250
|
-
const existing = new Set(agent.tools.map((tool)=>tool.name));
|
|
251
|
-
const uniqueUiTools = uiTools.filter((tool)=>!existing.has(tool.name));
|
|
252
|
-
agent.tools = [
|
|
253
|
-
...agent.tools,
|
|
254
|
-
...uniqueUiTools
|
|
255
|
-
];
|
|
256
|
-
} else agent.tools = uiTools;
|
|
267
|
+
agent.tools = await addAlwaysOnTools(agent.tools, config);
|
|
257
268
|
if (config.mcp) agent.mcpConfig = config.mcp;
|
|
258
269
|
if (config.mcpUseGlobal) agent.mcpUseGlobal = true;
|
|
259
270
|
if (config.model) try {
|
|
@@ -280,15 +291,7 @@ class AgentLoader {
|
|
|
280
291
|
sub.systemPrompt = applyPromptRefinement(sub.systemPrompt, subagent.name, subagent.promptRefinement);
|
|
281
292
|
}
|
|
282
293
|
if (subagent.tools && subagent.tools.length > 0) sub.tools = await (0, external_toolRegistry_cjs_namespaceObject.createTools)(subagent.tools, buildToolOptions(subagent));
|
|
283
|
-
|
|
284
|
-
if (subUiTools.length > 0) if (sub.tools && sub.tools.length > 0) {
|
|
285
|
-
const existing = new Set(sub.tools.map((tool)=>tool.name));
|
|
286
|
-
const uniqueUiTools = subUiTools.filter((tool)=>!existing.has(tool.name));
|
|
287
|
-
sub.tools = [
|
|
288
|
-
...sub.tools,
|
|
289
|
-
...uniqueUiTools
|
|
290
|
-
];
|
|
291
|
-
} else sub.tools = subUiTools;
|
|
294
|
+
sub.tools = await addAlwaysOnTools(sub.tools, subagent);
|
|
292
295
|
if (subagent.model) try {
|
|
293
296
|
sub.model = external_modelFactory_cjs_namespaceObject.ModelFactory.createModel(subagent.model, {
|
|
294
297
|
reasoningEffort: subagent.reasoningEffort,
|
|
@@ -4,7 +4,7 @@ import { load } from "js-yaml";
|
|
|
4
4
|
import { createLogger } from "../../logger.js";
|
|
5
5
|
import { WingmanDirectory, validateAgentConfig } from "./agentConfig.js";
|
|
6
6
|
import { ModelFactory } from "./modelFactory.js";
|
|
7
|
-
import { UI_TOOL_NAMES, createTools } from "./toolRegistry.js";
|
|
7
|
+
import { NODE_TOOL_NAMES, UI_TOOL_NAMES, createTools } from "./toolRegistry.js";
|
|
8
8
|
function _define_property(obj, key, value) {
|
|
9
9
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
10
10
|
value: value,
|
|
@@ -17,6 +17,10 @@ function _define_property(obj, key, value) {
|
|
|
17
17
|
}
|
|
18
18
|
const logger = createLogger();
|
|
19
19
|
const PROMPT_REFINEMENT_MARKER = "[[wingman:prompt-refinement]]";
|
|
20
|
+
const ALWAYS_ON_TOOL_NAMES = [
|
|
21
|
+
...UI_TOOL_NAMES,
|
|
22
|
+
...NODE_TOOL_NAMES
|
|
23
|
+
];
|
|
20
24
|
const normalizePromptRefinementPath = (agentName, rawPath)=>{
|
|
21
25
|
const fallback = `/memories/agents/${agentName}/instructions.md`;
|
|
22
26
|
if (!rawPath) return fallback;
|
|
@@ -216,16 +220,23 @@ class AgentLoader {
|
|
|
216
220
|
...this.runtimeToolOptions
|
|
217
221
|
};
|
|
218
222
|
};
|
|
223
|
+
const addAlwaysOnTools = async (existingTools, source)=>{
|
|
224
|
+
const alwaysOnTools = await createTools([
|
|
225
|
+
...ALWAYS_ON_TOOL_NAMES
|
|
226
|
+
], buildToolOptions(source));
|
|
227
|
+
if (0 === alwaysOnTools.length) return existingTools || [];
|
|
228
|
+
if (existingTools && existingTools.length > 0) {
|
|
229
|
+
const existing = new Set(existingTools.map((tool)=>tool.name));
|
|
230
|
+
const uniqueAlwaysOnTools = alwaysOnTools.filter((tool)=>!existing.has(tool.name));
|
|
231
|
+
return [
|
|
232
|
+
...existingTools,
|
|
233
|
+
...uniqueAlwaysOnTools
|
|
234
|
+
];
|
|
235
|
+
}
|
|
236
|
+
return alwaysOnTools;
|
|
237
|
+
};
|
|
219
238
|
if (config.tools && config.tools.length > 0) agent.tools = await createTools(config.tools, buildToolOptions(config));
|
|
220
|
-
|
|
221
|
-
if (uiTools.length > 0) if (agent.tools && agent.tools.length > 0) {
|
|
222
|
-
const existing = new Set(agent.tools.map((tool)=>tool.name));
|
|
223
|
-
const uniqueUiTools = uiTools.filter((tool)=>!existing.has(tool.name));
|
|
224
|
-
agent.tools = [
|
|
225
|
-
...agent.tools,
|
|
226
|
-
...uniqueUiTools
|
|
227
|
-
];
|
|
228
|
-
} else agent.tools = uiTools;
|
|
239
|
+
agent.tools = await addAlwaysOnTools(agent.tools, config);
|
|
229
240
|
if (config.mcp) agent.mcpConfig = config.mcp;
|
|
230
241
|
if (config.mcpUseGlobal) agent.mcpUseGlobal = true;
|
|
231
242
|
if (config.model) try {
|
|
@@ -252,15 +263,7 @@ class AgentLoader {
|
|
|
252
263
|
sub.systemPrompt = applyPromptRefinement(sub.systemPrompt, subagent.name, subagent.promptRefinement);
|
|
253
264
|
}
|
|
254
265
|
if (subagent.tools && subagent.tools.length > 0) sub.tools = await createTools(subagent.tools, buildToolOptions(subagent));
|
|
255
|
-
|
|
256
|
-
if (subUiTools.length > 0) if (sub.tools && sub.tools.length > 0) {
|
|
257
|
-
const existing = new Set(sub.tools.map((tool)=>tool.name));
|
|
258
|
-
const uniqueUiTools = subUiTools.filter((tool)=>!existing.has(tool.name));
|
|
259
|
-
sub.tools = [
|
|
260
|
-
...sub.tools,
|
|
261
|
-
...uniqueUiTools
|
|
262
|
-
];
|
|
263
|
-
} else sub.tools = subUiTools;
|
|
266
|
+
sub.tools = await addAlwaysOnTools(sub.tools, subagent);
|
|
264
267
|
if (subagent.model) try {
|
|
265
268
|
sub.model = ModelFactory.createModel(subagent.model, {
|
|
266
269
|
reasoningEffort: subagent.reasoningEffort,
|
|
@@ -27,6 +27,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
27
27
|
createTools: ()=>createTools,
|
|
28
28
|
UI_TOOL_NAMES: ()=>UI_TOOL_NAMES,
|
|
29
29
|
getAvailableTools: ()=>getAvailableTools,
|
|
30
|
+
NODE_TOOL_NAMES: ()=>NODE_TOOL_NAMES,
|
|
30
31
|
createTool: ()=>createTool
|
|
31
32
|
});
|
|
32
33
|
const external_logger_cjs_namespaceObject = require("../../logger.cjs");
|
|
@@ -36,6 +37,7 @@ const code_search_cjs_namespaceObject = require("../tools/code_search.cjs");
|
|
|
36
37
|
const command_execute_cjs_namespaceObject = require("../tools/command_execute.cjs");
|
|
37
38
|
const git_status_cjs_namespaceObject = require("../tools/git_status.cjs");
|
|
38
39
|
const internet_search_cjs_namespaceObject = require("../tools/internet_search.cjs");
|
|
40
|
+
const node_invoke_cjs_namespaceObject = require("../tools/node_invoke.cjs");
|
|
39
41
|
const terminal_session_manager_cjs_namespaceObject = require("../tools/terminal_session_manager.cjs");
|
|
40
42
|
const think_cjs_namespaceObject = require("../tools/think.cjs");
|
|
41
43
|
const ui_registry_cjs_namespaceObject = require("../tools/ui_registry.cjs");
|
|
@@ -47,6 +49,10 @@ const UI_TOOL_NAMES = [
|
|
|
47
49
|
"ui_registry_get",
|
|
48
50
|
"ui_present"
|
|
49
51
|
];
|
|
52
|
+
const NODE_TOOL_NAMES = [
|
|
53
|
+
"node_notify",
|
|
54
|
+
"node_run"
|
|
55
|
+
];
|
|
50
56
|
function createTool(name, options = {}) {
|
|
51
57
|
const { workspace = process.cwd(), executionWorkspace, blockedCommands, allowScriptExecution = true, timeout = 300000, terminalOwnerId = "default", terminalSessionManager = (0, terminal_session_manager_cjs_namespaceObject.getSharedTerminalSessionManager)(), searchConfig = {
|
|
52
58
|
provider: "duckduckgo",
|
|
@@ -93,6 +99,16 @@ function createTool(name, options = {}) {
|
|
|
93
99
|
allowScriptExecution,
|
|
94
100
|
commandTimeout: timeout
|
|
95
101
|
});
|
|
102
|
+
case "node_notify":
|
|
103
|
+
return (0, node_invoke_cjs_namespaceObject.createNodeNotifyTool)({
|
|
104
|
+
nodeInvoker: options.nodeInvoker,
|
|
105
|
+
defaultTargetClientId: options.nodeDefaultTargetClientId
|
|
106
|
+
});
|
|
107
|
+
case "node_run":
|
|
108
|
+
return (0, node_invoke_cjs_namespaceObject.createNodeRunTool)({
|
|
109
|
+
nodeInvoker: options.nodeInvoker,
|
|
110
|
+
defaultTargetClientId: options.nodeDefaultTargetClientId
|
|
111
|
+
});
|
|
96
112
|
case "think":
|
|
97
113
|
return (0, think_cjs_namespaceObject.createThinkingTool)();
|
|
98
114
|
case "code_search":
|
|
@@ -140,17 +156,20 @@ function getAvailableTools() {
|
|
|
140
156
|
"browser_control",
|
|
141
157
|
"command_execute",
|
|
142
158
|
"background_terminal",
|
|
159
|
+
...NODE_TOOL_NAMES,
|
|
143
160
|
"think",
|
|
144
161
|
"code_search",
|
|
145
162
|
"git_status",
|
|
146
163
|
...UI_TOOL_NAMES
|
|
147
164
|
];
|
|
148
165
|
}
|
|
166
|
+
exports.NODE_TOOL_NAMES = __webpack_exports__.NODE_TOOL_NAMES;
|
|
149
167
|
exports.UI_TOOL_NAMES = __webpack_exports__.UI_TOOL_NAMES;
|
|
150
168
|
exports.createTool = __webpack_exports__.createTool;
|
|
151
169
|
exports.createTools = __webpack_exports__.createTools;
|
|
152
170
|
exports.getAvailableTools = __webpack_exports__.getAvailableTools;
|
|
153
171
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
172
|
+
"NODE_TOOL_NAMES",
|
|
154
173
|
"UI_TOOL_NAMES",
|
|
155
174
|
"createTool",
|
|
156
175
|
"createTools",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { StructuredTool } from "@langchain/core/tools";
|
|
2
2
|
import type { MCPServersConfig } from "@/types/mcp.js";
|
|
3
3
|
import type { SearchConfig } from "../../cli/config/schema.js";
|
|
4
|
+
import { type NodeInvokeRequest, type NodeInvokeResult } from "../tools/node_invoke.js";
|
|
4
5
|
import { type TerminalSessionManager } from "../tools/terminal_session_manager.js";
|
|
5
6
|
import type { AvailableToolName } from "./agentConfig.js";
|
|
6
7
|
export interface ToolOptions {
|
|
@@ -30,8 +31,11 @@ export interface ToolOptions {
|
|
|
30
31
|
mcpConfigs?: MCPServersConfig[];
|
|
31
32
|
skillsDirectory?: string;
|
|
32
33
|
dynamicUiEnabled?: boolean;
|
|
34
|
+
nodeInvoker?: (request: NodeInvokeRequest) => Promise<NodeInvokeResult>;
|
|
35
|
+
nodeDefaultTargetClientId?: string;
|
|
33
36
|
}
|
|
34
37
|
export declare const UI_TOOL_NAMES: AvailableToolName[];
|
|
38
|
+
export declare const NODE_TOOL_NAMES: AvailableToolName[];
|
|
35
39
|
/**
|
|
36
40
|
* Create a tool by name with optional configuration
|
|
37
41
|
*/
|
|
@@ -5,6 +5,7 @@ import { createCodeSearchTool } from "../tools/code_search.js";
|
|
|
5
5
|
import { createCommandExecuteTool } from "../tools/command_execute.js";
|
|
6
6
|
import { createGitStatusTool } from "../tools/git_status.js";
|
|
7
7
|
import { createInternetSearchTool } from "../tools/internet_search.js";
|
|
8
|
+
import { createNodeNotifyTool, createNodeRunTool } from "../tools/node_invoke.js";
|
|
8
9
|
import { getSharedTerminalSessionManager } from "../tools/terminal_session_manager.js";
|
|
9
10
|
import { createThinkingTool } from "../tools/think.js";
|
|
10
11
|
import { createUiPresentTool, createUiRegistryGetTool, createUiRegistryListTool } from "../tools/ui_registry.js";
|
|
@@ -16,6 +17,10 @@ const UI_TOOL_NAMES = [
|
|
|
16
17
|
"ui_registry_get",
|
|
17
18
|
"ui_present"
|
|
18
19
|
];
|
|
20
|
+
const NODE_TOOL_NAMES = [
|
|
21
|
+
"node_notify",
|
|
22
|
+
"node_run"
|
|
23
|
+
];
|
|
19
24
|
function createTool(name, options = {}) {
|
|
20
25
|
const { workspace = process.cwd(), executionWorkspace, blockedCommands, allowScriptExecution = true, timeout = 300000, terminalOwnerId = "default", terminalSessionManager = getSharedTerminalSessionManager(), searchConfig = {
|
|
21
26
|
provider: "duckduckgo",
|
|
@@ -62,6 +67,16 @@ function createTool(name, options = {}) {
|
|
|
62
67
|
allowScriptExecution,
|
|
63
68
|
commandTimeout: timeout
|
|
64
69
|
});
|
|
70
|
+
case "node_notify":
|
|
71
|
+
return createNodeNotifyTool({
|
|
72
|
+
nodeInvoker: options.nodeInvoker,
|
|
73
|
+
defaultTargetClientId: options.nodeDefaultTargetClientId
|
|
74
|
+
});
|
|
75
|
+
case "node_run":
|
|
76
|
+
return createNodeRunTool({
|
|
77
|
+
nodeInvoker: options.nodeInvoker,
|
|
78
|
+
defaultTargetClientId: options.nodeDefaultTargetClientId
|
|
79
|
+
});
|
|
65
80
|
case "think":
|
|
66
81
|
return createThinkingTool();
|
|
67
82
|
case "code_search":
|
|
@@ -109,10 +124,11 @@ function getAvailableTools() {
|
|
|
109
124
|
"browser_control",
|
|
110
125
|
"command_execute",
|
|
111
126
|
"background_terminal",
|
|
127
|
+
...NODE_TOOL_NAMES,
|
|
112
128
|
"think",
|
|
113
129
|
"code_search",
|
|
114
130
|
"git_status",
|
|
115
131
|
...UI_TOOL_NAMES
|
|
116
132
|
];
|
|
117
133
|
}
|
|
118
|
-
export { UI_TOOL_NAMES, createTool, createTools, getAvailableTools };
|
|
134
|
+
export { NODE_TOOL_NAMES, UI_TOOL_NAMES, createTool, createTools, getAvailableTools };
|
|
@@ -30,6 +30,7 @@ const external_node_path_namespaceObject = require("node:path");
|
|
|
30
30
|
const external_langchain_namespaceObject = require("langchain");
|
|
31
31
|
const external_utils_cjs_namespaceObject = require("../utils.cjs");
|
|
32
32
|
const external_uiRegistry_cjs_namespaceObject = require("../uiRegistry.cjs");
|
|
33
|
+
const INJECTION_SOURCE = "additional-message-middleware";
|
|
33
34
|
const normalizeRelativePath = (value)=>value.replace(/\\/g, "/");
|
|
34
35
|
const toSafeRelativePath = (workspaceRoot, targetPath)=>{
|
|
35
36
|
if (!workspaceRoot) return null;
|
|
@@ -56,12 +57,108 @@ const buildWorkingDirectoryMessage = (context)=>{
|
|
|
56
57
|
if (!context.workspaceRoot) return null;
|
|
57
58
|
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
59
|
};
|
|
60
|
+
const resolveConnectedNodeIds = async (context)=>{
|
|
61
|
+
if (context.nodeConnectedTargetsProvider) try {
|
|
62
|
+
const rawTargets = await context.nodeConnectedTargetsProvider();
|
|
63
|
+
if (!Array.isArray(rawTargets)) return [];
|
|
64
|
+
const seen = new Set();
|
|
65
|
+
const normalized = [];
|
|
66
|
+
for (const target of rawTargets){
|
|
67
|
+
if (!target || "object" != typeof target) continue;
|
|
68
|
+
const nodeId = target.nodeId?.trim();
|
|
69
|
+
if (!(!nodeId || seen.has(nodeId))) {
|
|
70
|
+
seen.add(nodeId);
|
|
71
|
+
normalized.push(nodeId);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return normalized;
|
|
75
|
+
} catch {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
if (!context.nodeConnectedIdsProvider) return [];
|
|
79
|
+
try {
|
|
80
|
+
const rawIds = await context.nodeConnectedIdsProvider();
|
|
81
|
+
if (!Array.isArray(rawIds)) return [];
|
|
82
|
+
const seen = new Set();
|
|
83
|
+
const normalized = [];
|
|
84
|
+
for (const value of rawIds){
|
|
85
|
+
if ("string" != typeof value) continue;
|
|
86
|
+
const trimmed = value.trim();
|
|
87
|
+
if (!(!trimmed || seen.has(trimmed))) {
|
|
88
|
+
seen.add(trimmed);
|
|
89
|
+
normalized.push(trimmed);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return normalized;
|
|
93
|
+
} catch {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const resolveConnectedNodeTargets = async (context)=>{
|
|
98
|
+
if (context.nodeConnectedTargetsProvider) try {
|
|
99
|
+
const rawTargets = await context.nodeConnectedTargetsProvider();
|
|
100
|
+
if (!Array.isArray(rawTargets)) return [];
|
|
101
|
+
const seen = new Set();
|
|
102
|
+
const normalized = [];
|
|
103
|
+
for (const target of rawTargets){
|
|
104
|
+
if (!target || "object" != typeof target) continue;
|
|
105
|
+
const typedTarget = target;
|
|
106
|
+
const nodeId = typedTarget.nodeId?.trim();
|
|
107
|
+
if (!nodeId || seen.has(nodeId)) continue;
|
|
108
|
+
seen.add(nodeId);
|
|
109
|
+
const rawCapabilities = typedTarget.capabilities;
|
|
110
|
+
const capabilities = Array.isArray(rawCapabilities) ? rawCapabilities.filter((value)=>"string" == typeof value).map((value)=>value.trim()).filter(Boolean) : [];
|
|
111
|
+
normalized.push({
|
|
112
|
+
nodeId,
|
|
113
|
+
clientId: typedTarget.clientId?.trim() || void 0,
|
|
114
|
+
name: typedTarget.name?.trim() || void 0,
|
|
115
|
+
capabilities: capabilities.length > 0 ? capabilities : void 0
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return normalized;
|
|
119
|
+
} catch {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
const nodeIds = await resolveConnectedNodeIds(context);
|
|
123
|
+
return nodeIds.map((nodeId)=>({
|
|
124
|
+
nodeId
|
|
125
|
+
}));
|
|
126
|
+
};
|
|
127
|
+
const buildNodeTargetsMessage = async (context)=>{
|
|
128
|
+
const connectedNodeTargets = await resolveConnectedNodeTargets(context);
|
|
129
|
+
const connectedNodeIds = connectedNodeTargets.map((target)=>target.nodeId);
|
|
130
|
+
const defaultClientId = context.defaultNodeTargetClientId?.trim();
|
|
131
|
+
if (0 === connectedNodeIds.length && !defaultClientId) return null;
|
|
132
|
+
const lines = [
|
|
133
|
+
"** Connected Node Targets **"
|
|
134
|
+
];
|
|
135
|
+
if (connectedNodeIds.length > 0) lines.push(`- Connected node IDs: ${connectedNodeIds.join(", ")}`);
|
|
136
|
+
else lines.push("- Connected node IDs: (none currently connected)");
|
|
137
|
+
const withMetadata = connectedNodeTargets.filter((target)=>Boolean(target.clientId) || Boolean(target.name) || target.capabilities && target.capabilities.length > 0);
|
|
138
|
+
if (withMetadata.length > 0) {
|
|
139
|
+
lines.push("- Connected node metadata:");
|
|
140
|
+
for (const target of withMetadata.slice(0, 8)){
|
|
141
|
+
const details = [];
|
|
142
|
+
if (target.clientId) details.push(`clientId: ${target.clientId}`);
|
|
143
|
+
if (target.name) details.push(`name: ${target.name}`);
|
|
144
|
+
if (target.capabilities && target.capabilities.length > 0) {
|
|
145
|
+
const preview = target.capabilities.slice(0, 6).join(", ");
|
|
146
|
+
const remaining = target.capabilities.length - 6;
|
|
147
|
+
details.push(remaining > 0 ? `capabilities: ${preview} (+${remaining} more)` : `capabilities: ${preview}`);
|
|
148
|
+
}
|
|
149
|
+
if (details.length > 0) lines.push(` - ${target.nodeId} (${details.join("; ")})`);
|
|
150
|
+
}
|
|
151
|
+
if (withMetadata.length > 8) lines.push(` - ... ${withMetadata.length - 8} more connected node(s)`);
|
|
152
|
+
}
|
|
153
|
+
if (defaultClientId) lines.push(`- Default node target clientId for this request: ${defaultClientId}`);
|
|
154
|
+
lines.push("- For node_notify/node_run, set target.nodeId or target.clientId when the user specifies a device.");
|
|
155
|
+
return lines.join("\n");
|
|
156
|
+
};
|
|
59
157
|
const additionalMessageMiddleware = (context = {})=>({
|
|
60
|
-
name:
|
|
158
|
+
name: INJECTION_SOURCE,
|
|
61
159
|
[external_langchain_namespaceObject.MIDDLEWARE_BRAND]: true,
|
|
62
160
|
beforeAgent: async (input)=>{
|
|
63
|
-
const
|
|
64
|
-
if (alreadyInjected) return input;
|
|
161
|
+
const messagesWithoutInjected = input.messages.filter((message)=>message?.additional_kwargs?.source !== INJECTION_SOURCE);
|
|
65
162
|
const lines = [
|
|
66
163
|
(0, external_utils_cjs_namespaceObject.getConfidentialityNotice)(),
|
|
67
164
|
`** Current Date Time (UTC): ${new Date().toISOString()} **`
|
|
@@ -70,6 +167,8 @@ const additionalMessageMiddleware = (context = {})=>({
|
|
|
70
167
|
if (outputLocation) lines.push(outputLocation);
|
|
71
168
|
const workingDirectory = buildWorkingDirectoryMessage(context);
|
|
72
169
|
if (workingDirectory) lines.push(workingDirectory);
|
|
170
|
+
const nodeTargets = await buildNodeTargetsMessage(context);
|
|
171
|
+
if (nodeTargets) lines.push(nodeTargets);
|
|
73
172
|
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");
|
|
74
173
|
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.");
|
|
75
174
|
else {
|
|
@@ -82,14 +181,19 @@ const additionalMessageMiddleware = (context = {})=>({
|
|
|
82
181
|
lines.push("** Dynamic UI Registry **\n" + summaryLines + "\n- Use ui_registry_get for schema details, then ui_present with textFallback.");
|
|
83
182
|
}
|
|
84
183
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
184
|
+
return {
|
|
185
|
+
...input,
|
|
186
|
+
messages: [
|
|
187
|
+
new external_langchain_namespaceObject.HumanMessage({
|
|
188
|
+
content: lines.join("\n\n"),
|
|
189
|
+
additional_kwargs: {
|
|
190
|
+
ui_hidden: true,
|
|
191
|
+
source: INJECTION_SOURCE
|
|
192
|
+
}
|
|
193
|
+
}),
|
|
194
|
+
...messagesWithoutInjected
|
|
195
|
+
]
|
|
196
|
+
};
|
|
93
197
|
}
|
|
94
198
|
});
|
|
95
199
|
exports.additionalMessageMiddleware = __webpack_exports__.additionalMessageMiddleware;
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { type AgentMiddleware } from "langchain";
|
|
2
|
+
export type ConnectedNodeTarget = {
|
|
3
|
+
nodeId: string;
|
|
4
|
+
clientId?: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
capabilities?: string[];
|
|
7
|
+
};
|
|
2
8
|
type AdditionalMessageContext = {
|
|
3
9
|
workspaceRoot?: string | null;
|
|
4
10
|
workdir?: string | null;
|
|
@@ -6,6 +12,9 @@ type AdditionalMessageContext = {
|
|
|
6
12
|
outputVirtualPath?: string | null;
|
|
7
13
|
dynamicUiEnabled?: boolean;
|
|
8
14
|
skillsDirectory?: string;
|
|
15
|
+
nodeConnectedIdsProvider?: () => string[] | Promise<string[]>;
|
|
16
|
+
nodeConnectedTargetsProvider?: () => ConnectedNodeTarget[] | Promise<ConnectedNodeTarget[]>;
|
|
17
|
+
defaultNodeTargetClientId?: string;
|
|
9
18
|
};
|
|
10
19
|
export declare const additionalMessageMiddleware: (context?: AdditionalMessageContext) => AgentMiddleware;
|
|
11
20
|
export {};
|