@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
|
@@ -4,6 +4,14 @@ const external_vitest_namespaceObject = require("vitest");
|
|
|
4
4
|
const modelFactory_cjs_namespaceObject = require("../config/modelFactory.cjs");
|
|
5
5
|
const anthropic_namespaceObject = require("@langchain/anthropic");
|
|
6
6
|
const openai_namespaceObject = require("@langchain/openai");
|
|
7
|
+
const originalAnthropicApiKey = process.env.ANTHROPIC_API_KEY;
|
|
8
|
+
(0, external_vitest_namespaceObject.beforeEach)(()=>{
|
|
9
|
+
process.env.ANTHROPIC_API_KEY = "test-anthropic-api-key";
|
|
10
|
+
});
|
|
11
|
+
(0, external_vitest_namespaceObject.afterEach)(()=>{
|
|
12
|
+
if (void 0 === originalAnthropicApiKey) return void delete process.env.ANTHROPIC_API_KEY;
|
|
13
|
+
process.env.ANTHROPIC_API_KEY = originalAnthropicApiKey;
|
|
14
|
+
});
|
|
7
15
|
(0, external_vitest_namespaceObject.describe)("ModelFactory", ()=>{
|
|
8
16
|
(0, external_vitest_namespaceObject.describe)("createModel", ()=>{
|
|
9
17
|
(0, external_vitest_namespaceObject.it)("should create an Anthropic model", ()=>{
|
|
@@ -14,6 +22,11 @@ const openai_namespaceObject = require("@langchain/openai");
|
|
|
14
22
|
const model = modelFactory_cjs_namespaceObject.ModelFactory.createModel("openai:gpt-4o");
|
|
15
23
|
(0, external_vitest_namespaceObject.expect)(model).toBeInstanceOf(openai_namespaceObject.ChatOpenAI);
|
|
16
24
|
});
|
|
25
|
+
(0, external_vitest_namespaceObject.it)("should force OpenAI models onto the responses API", ()=>{
|
|
26
|
+
const model = modelFactory_cjs_namespaceObject.ModelFactory.createModel("openai:gpt-5.2-codex");
|
|
27
|
+
(0, external_vitest_namespaceObject.expect)(model).toBeInstanceOf(openai_namespaceObject.ChatOpenAI);
|
|
28
|
+
(0, external_vitest_namespaceObject.expect)(model.useResponsesApi).toBe(true);
|
|
29
|
+
});
|
|
17
30
|
(0, external_vitest_namespaceObject.it)("should create an OpenRouter model", ()=>{
|
|
18
31
|
const model = modelFactory_cjs_namespaceObject.ModelFactory.createModel("openrouter:openai/gpt-4o");
|
|
19
32
|
(0, external_vitest_namespaceObject.expect)(model).toBeInstanceOf(openai_namespaceObject.ChatOpenAI);
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
2
2
|
import { ModelFactory } from "../config/modelFactory.js";
|
|
3
3
|
import { ChatAnthropic } from "@langchain/anthropic";
|
|
4
4
|
import { ChatOpenAI } from "@langchain/openai";
|
|
5
|
+
const originalAnthropicApiKey = process.env.ANTHROPIC_API_KEY;
|
|
6
|
+
beforeEach(()=>{
|
|
7
|
+
process.env.ANTHROPIC_API_KEY = "test-anthropic-api-key";
|
|
8
|
+
});
|
|
9
|
+
afterEach(()=>{
|
|
10
|
+
if (void 0 === originalAnthropicApiKey) return void delete process.env.ANTHROPIC_API_KEY;
|
|
11
|
+
process.env.ANTHROPIC_API_KEY = originalAnthropicApiKey;
|
|
12
|
+
});
|
|
5
13
|
describe("ModelFactory", ()=>{
|
|
6
14
|
describe("createModel", ()=>{
|
|
7
15
|
it("should create an Anthropic model", ()=>{
|
|
@@ -12,6 +20,11 @@ describe("ModelFactory", ()=>{
|
|
|
12
20
|
const model = ModelFactory.createModel("openai:gpt-4o");
|
|
13
21
|
expect(model).toBeInstanceOf(ChatOpenAI);
|
|
14
22
|
});
|
|
23
|
+
it("should force OpenAI models onto the responses API", ()=>{
|
|
24
|
+
const model = ModelFactory.createModel("openai:gpt-5.2-codex");
|
|
25
|
+
expect(model).toBeInstanceOf(ChatOpenAI);
|
|
26
|
+
expect(model.useResponsesApi).toBe(true);
|
|
27
|
+
});
|
|
15
28
|
it("should create an OpenRouter model", ()=>{
|
|
16
29
|
const model = ModelFactory.createModel("openrouter:openai/gpt-4o");
|
|
17
30
|
expect(model).toBeInstanceOf(ChatOpenAI);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __webpack_exports__ = {};
|
|
3
3
|
const external_vitest_namespaceObject = require("vitest");
|
|
4
|
+
const external_node_fs_namespaceObject = require("node:fs");
|
|
5
|
+
const external_node_path_namespaceObject = require("node:path");
|
|
6
|
+
const external_node_os_namespaceObject = require("node:os");
|
|
4
7
|
const toolRegistry_cjs_namespaceObject = require("../config/toolRegistry.cjs");
|
|
5
8
|
(0, external_vitest_namespaceObject.describe)("Tool Registry", ()=>{
|
|
6
9
|
(0, external_vitest_namespaceObject.describe)("createTool", ()=>{
|
|
@@ -32,6 +35,18 @@ const toolRegistry_cjs_namespaceObject = require("../config/toolRegistry.cjs");
|
|
|
32
35
|
(0, external_vitest_namespaceObject.expect)(tool).not.toBeNull();
|
|
33
36
|
(0, external_vitest_namespaceObject.expect)(tool?.name).toBe("command_execute");
|
|
34
37
|
});
|
|
38
|
+
(0, external_vitest_namespaceObject.it)("should execute command tools in executionWorkspace when provided", async ()=>{
|
|
39
|
+
const executionWorkspace = (0, external_node_fs_namespaceObject.mkdtempSync)((0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.tmpdir)(), "wingman-tool-workspace-"));
|
|
40
|
+
const tool = (0, toolRegistry_cjs_namespaceObject.createTool)("command_execute", {
|
|
41
|
+
workspace: "/custom/path",
|
|
42
|
+
executionWorkspace
|
|
43
|
+
});
|
|
44
|
+
(0, external_vitest_namespaceObject.expect)(tool).not.toBeNull();
|
|
45
|
+
const result = await tool.invoke({
|
|
46
|
+
command: 'node -e "process.stdout.write(process.cwd())"'
|
|
47
|
+
});
|
|
48
|
+
(0, external_vitest_namespaceObject.expect)(String(result)).toContain(executionWorkspace);
|
|
49
|
+
});
|
|
35
50
|
(0, external_vitest_namespaceObject.it)("should create think tool", ()=>{
|
|
36
51
|
const tool = (0, toolRegistry_cjs_namespaceObject.createTool)("think");
|
|
37
52
|
(0, external_vitest_namespaceObject.expect)(tool).not.toBeNull();
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { mkdtempSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
2
5
|
import { createTool, createTools, getAvailableTools } from "../config/toolRegistry.js";
|
|
3
6
|
describe("Tool Registry", ()=>{
|
|
4
7
|
describe("createTool", ()=>{
|
|
@@ -30,6 +33,18 @@ describe("Tool Registry", ()=>{
|
|
|
30
33
|
expect(tool).not.toBeNull();
|
|
31
34
|
expect(tool?.name).toBe("command_execute");
|
|
32
35
|
});
|
|
36
|
+
it("should execute command tools in executionWorkspace when provided", async ()=>{
|
|
37
|
+
const executionWorkspace = mkdtempSync(join(tmpdir(), "wingman-tool-workspace-"));
|
|
38
|
+
const tool = createTool("command_execute", {
|
|
39
|
+
workspace: "/custom/path",
|
|
40
|
+
executionWorkspace
|
|
41
|
+
});
|
|
42
|
+
expect(tool).not.toBeNull();
|
|
43
|
+
const result = await tool.invoke({
|
|
44
|
+
command: 'node -e "process.stdout.write(process.cwd())"'
|
|
45
|
+
});
|
|
46
|
+
expect(String(result)).toContain(executionWorkspace);
|
|
47
|
+
});
|
|
33
48
|
it("should create think tool", ()=>{
|
|
34
49
|
const tool = createTool("think");
|
|
35
50
|
expect(tool).not.toBeNull();
|
|
@@ -55,6 +55,21 @@ const skillsDir = "skills";
|
|
|
55
55
|
(0, external_vitest_namespaceObject.expect)(result.propsSchema).toBeTruthy();
|
|
56
56
|
}
|
|
57
57
|
});
|
|
58
|
+
(0, external_vitest_namespaceObject.it)("exposes axis scale options for chart components", async ()=>{
|
|
59
|
+
const tool = (0, ui_registry_cjs_namespaceObject.createUiRegistryGetTool)(workspace, skillsDir);
|
|
60
|
+
for (const componentId of [
|
|
61
|
+
"line_chart",
|
|
62
|
+
"area_chart",
|
|
63
|
+
"bar_chart"
|
|
64
|
+
]){
|
|
65
|
+
const result = await tool.invoke({
|
|
66
|
+
componentId
|
|
67
|
+
});
|
|
68
|
+
const props = result.propsSchema?.properties ?? {};
|
|
69
|
+
(0, external_vitest_namespaceObject.expect)(props.xScale).toBeTruthy();
|
|
70
|
+
(0, external_vitest_namespaceObject.expect)(props.yScale).toBeTruthy();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
58
73
|
(0, external_vitest_namespaceObject.it)("renders UI payload when enabled", async ()=>{
|
|
59
74
|
const tool = (0, ui_registry_cjs_namespaceObject.createUiPresentTool)(workspace, skillsDir, true);
|
|
60
75
|
const result = await tool.invoke({
|
|
@@ -50,6 +50,21 @@ describe("UI Registry Tools", ()=>{
|
|
|
50
50
|
expect(result.propsSchema).toBeTruthy();
|
|
51
51
|
}
|
|
52
52
|
});
|
|
53
|
+
it("exposes axis scale options for chart components", async ()=>{
|
|
54
|
+
const tool = createUiRegistryGetTool(workspace, skillsDir);
|
|
55
|
+
for (const componentId of [
|
|
56
|
+
"line_chart",
|
|
57
|
+
"area_chart",
|
|
58
|
+
"bar_chart"
|
|
59
|
+
]){
|
|
60
|
+
const result = await tool.invoke({
|
|
61
|
+
componentId
|
|
62
|
+
});
|
|
63
|
+
const props = result.propsSchema?.properties ?? {};
|
|
64
|
+
expect(props.xScale).toBeTruthy();
|
|
65
|
+
expect(props.yScale).toBeTruthy();
|
|
66
|
+
}
|
|
67
|
+
});
|
|
53
68
|
it("renders UI payload when enabled", async ()=>{
|
|
54
69
|
const tool = createUiPresentTool(workspace, skillsDir, true);
|
|
55
70
|
const result = await tool.invoke({
|
|
@@ -92,7 +92,7 @@ const createCodeSearchTool = (workspace)=>{
|
|
|
92
92
|
description: "Search code patterns across the codebase using ripgrep (or grep as fallback). Fast and efficient for finding function definitions, variables, imports, or any text pattern. Returns file paths with line numbers and context.",
|
|
93
93
|
schema: external_zod_namespaceObject.z.object({
|
|
94
94
|
pattern: external_zod_namespaceObject.z.string().describe("The pattern to search for (regex or literal string). Examples: 'function.*processData', 'import.*React', 'TODO'"),
|
|
95
|
-
path: external_zod_namespaceObject.z.string().optional().describe("Optional: Directory or file to search in (e.g., 'src/', 'src/utils.ts'). Defaults to
|
|
95
|
+
path: external_zod_namespaceObject.z.string().optional().describe("Optional: Directory or file to search in (e.g., 'src/', 'src/utils.ts'). Defaults to the active execution workspace."),
|
|
96
96
|
type: external_zod_namespaceObject.z.string().optional().describe("Optional: File type filter (e.g., 'ts', 'js', 'py', 'go'). Searches only files of this type."),
|
|
97
97
|
context: external_zod_namespaceObject.z.number().optional().default(2).describe("Optional: Number of context lines to show around matches. Default is 2."),
|
|
98
98
|
caseSensitive: external_zod_namespaceObject.z.boolean().optional().default(false).describe("Optional: Whether search should be case-sensitive.")
|
|
@@ -64,7 +64,7 @@ const createCodeSearchTool = (workspace)=>{
|
|
|
64
64
|
description: "Search code patterns across the codebase using ripgrep (or grep as fallback). Fast and efficient for finding function definitions, variables, imports, or any text pattern. Returns file paths with line numbers and context.",
|
|
65
65
|
schema: z.object({
|
|
66
66
|
pattern: z.string().describe("The pattern to search for (regex or literal string). Examples: 'function.*processData', 'import.*React', 'TODO'"),
|
|
67
|
-
path: z.string().optional().describe("Optional: Directory or file to search in (e.g., 'src/', 'src/utils.ts'). Defaults to
|
|
67
|
+
path: z.string().optional().describe("Optional: Directory or file to search in (e.g., 'src/', 'src/utils.ts'). Defaults to the active execution workspace."),
|
|
68
68
|
type: z.string().optional().describe("Optional: File type filter (e.g., 'ts', 'js', 'py', 'go'). Searches only files of this type."),
|
|
69
69
|
context: z.number().optional().default(2).describe("Optional: Number of context lines to show around matches. Default is 2."),
|
|
70
70
|
caseSensitive: z.boolean().optional().default(false).describe("Optional: Whether search should be case-sensitive.")
|
|
@@ -120,7 +120,7 @@ const createCommandExecuteTool = (workspace, envVariables, blockedCommands = DEF
|
|
|
120
120
|
}
|
|
121
121
|
}), {
|
|
122
122
|
name: "command_execute",
|
|
123
|
-
description: "Executes a command in a terminal and reports the output. Cannot execute potentially destructive commands like rm, mv, chmod, sudo, etc. Use for safe operations like running tests, builds, or other validation commands. Do not run long-running commands like dev servers. Commands run with a timeout in the workspace context.",
|
|
123
|
+
description: "Executes a command in a terminal and reports the output. Cannot execute potentially destructive commands like rm, mv, chmod, sudo, etc. Use for safe operations like running tests, builds, or other validation commands. Do not run long-running commands like dev servers. Commands run with a timeout in the active execution workspace context.",
|
|
124
124
|
schema: external_zod_namespaceObject.z.object({
|
|
125
125
|
command: external_zod_namespaceObject.z.string().describe("The command to execute in the terminal")
|
|
126
126
|
})
|
|
@@ -91,7 +91,7 @@ const createCommandExecuteTool = (workspace, envVariables, blockedCommands = DEF
|
|
|
91
91
|
}
|
|
92
92
|
}), {
|
|
93
93
|
name: "command_execute",
|
|
94
|
-
description: "Executes a command in a terminal and reports the output. Cannot execute potentially destructive commands like rm, mv, chmod, sudo, etc. Use for safe operations like running tests, builds, or other validation commands. Do not run long-running commands like dev servers. Commands run with a timeout in the workspace context.",
|
|
94
|
+
description: "Executes a command in a terminal and reports the output. Cannot execute potentially destructive commands like rm, mv, chmod, sudo, etc. Use for safe operations like running tests, builds, or other validation commands. Do not run long-running commands like dev servers. Commands run with a timeout in the active execution workspace context.",
|
|
95
95
|
schema: z.object({
|
|
96
96
|
command: z.string().describe("The command to execute in the terminal")
|
|
97
97
|
})
|
|
@@ -37,9 +37,9 @@ export declare const createUiPresentTool: (workspace: string, skillsDirectory: s
|
|
|
37
37
|
gap: z.ZodOptional<z.ZodNumber>;
|
|
38
38
|
columns: z.ZodOptional<z.ZodNumber>;
|
|
39
39
|
align: z.ZodOptional<z.ZodEnum<{
|
|
40
|
-
end: "end";
|
|
41
40
|
start: "start";
|
|
42
41
|
center: "center";
|
|
42
|
+
end: "end";
|
|
43
43
|
stretch: "stretch";
|
|
44
44
|
}>>;
|
|
45
45
|
}, z.core.$strip>>;
|
|
@@ -54,7 +54,7 @@ export declare const createUiPresentTool: (workspace: string, skillsDirectory: s
|
|
|
54
54
|
type: "stack" | "row" | "grid";
|
|
55
55
|
gap?: number | undefined;
|
|
56
56
|
columns?: number | undefined;
|
|
57
|
-
align?: "
|
|
57
|
+
align?: "start" | "center" | "end" | "stretch" | undefined;
|
|
58
58
|
} | undefined;
|
|
59
59
|
}, {
|
|
60
60
|
componentId: string;
|
|
@@ -64,7 +64,7 @@ export declare const createUiPresentTool: (workspace: string, skillsDirectory: s
|
|
|
64
64
|
type: "stack" | "row" | "grid";
|
|
65
65
|
gap?: number | undefined;
|
|
66
66
|
columns?: number | undefined;
|
|
67
|
-
align?: "
|
|
67
|
+
align?: "start" | "center" | "end" | "stretch" | undefined;
|
|
68
68
|
} | undefined;
|
|
69
69
|
uiOnly?: boolean | undefined;
|
|
70
70
|
}, Record<string, any>, "ui_present">;
|
|
@@ -24,8 +24,13 @@ var __webpack_require__ = {};
|
|
|
24
24
|
var __webpack_exports__ = {};
|
|
25
25
|
__webpack_require__.r(__webpack_exports__);
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
resolveExternalOutputMount: ()=>resolveExternalOutputMount,
|
|
28
|
+
toWorkspaceAliasVirtualPath: ()=>toWorkspaceAliasVirtualPath,
|
|
29
|
+
OUTPUT_VIRTUAL_PATH: ()=>OUTPUT_VIRTUAL_PATH,
|
|
30
|
+
WORKDIR_VIRTUAL_PATH: ()=>WORKDIR_VIRTUAL_PATH,
|
|
31
|
+
AgentInvoker: ()=>AgentInvoker,
|
|
27
32
|
buildUserContent: ()=>buildUserContent,
|
|
28
|
-
|
|
33
|
+
resolveExecutionWorkspace: ()=>resolveExecutionWorkspace
|
|
29
34
|
});
|
|
30
35
|
const external_deepagents_namespaceObject = require("deepagents");
|
|
31
36
|
const external_node_fs_namespaceObject = require("node:fs");
|
|
@@ -49,6 +54,40 @@ function _define_property(obj, key, value) {
|
|
|
49
54
|
else obj[key] = value;
|
|
50
55
|
return obj;
|
|
51
56
|
}
|
|
57
|
+
const WORKDIR_VIRTUAL_PATH = "/workdir/";
|
|
58
|
+
const OUTPUT_VIRTUAL_PATH = "/output/";
|
|
59
|
+
const isPathWithinRoot = (targetPath, rootPath)=>{
|
|
60
|
+
const normalizedTarget = (0, external_node_path_namespaceObject.normalize)(targetPath);
|
|
61
|
+
const normalizedRoot = (0, external_node_path_namespaceObject.normalize)(rootPath);
|
|
62
|
+
return normalizedTarget === normalizedRoot || normalizedTarget.startsWith(normalizedRoot + external_node_path_namespaceObject.sep);
|
|
63
|
+
};
|
|
64
|
+
const resolveExecutionWorkspace = (workspace, workdir)=>{
|
|
65
|
+
if (!workdir) return (0, external_node_path_namespaceObject.normalize)(workspace);
|
|
66
|
+
if ((0, external_node_path_namespaceObject.isAbsolute)(workdir)) return (0, external_node_path_namespaceObject.normalize)(workdir);
|
|
67
|
+
return (0, external_node_path_namespaceObject.normalize)((0, external_node_path_namespaceObject.join)(workspace, workdir));
|
|
68
|
+
};
|
|
69
|
+
const toWorkspaceAliasVirtualPath = (absolutePath)=>{
|
|
70
|
+
const normalized = (0, external_node_path_namespaceObject.normalize)(absolutePath);
|
|
71
|
+
if (!(0, external_node_path_namespaceObject.isAbsolute)(normalized)) return null;
|
|
72
|
+
const posixPath = normalized.replace(/\\/g, "/");
|
|
73
|
+
const trimmed = posixPath.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
74
|
+
if (!trimmed) return null;
|
|
75
|
+
return `/${trimmed}/`;
|
|
76
|
+
};
|
|
77
|
+
const resolveExternalOutputMount = (workspace, workdir, defaultOutputDir)=>{
|
|
78
|
+
if (workdir && !isPathWithinRoot(workdir, workspace)) return {
|
|
79
|
+
virtualPath: WORKDIR_VIRTUAL_PATH,
|
|
80
|
+
absolutePath: workdir
|
|
81
|
+
};
|
|
82
|
+
if (!workdir && defaultOutputDir && !isPathWithinRoot(defaultOutputDir, workspace)) return {
|
|
83
|
+
virtualPath: OUTPUT_VIRTUAL_PATH,
|
|
84
|
+
absolutePath: defaultOutputDir
|
|
85
|
+
};
|
|
86
|
+
return {
|
|
87
|
+
virtualPath: null,
|
|
88
|
+
absolutePath: null
|
|
89
|
+
};
|
|
90
|
+
};
|
|
52
91
|
class AgentInvoker {
|
|
53
92
|
findAllAgents() {
|
|
54
93
|
const agentConfigs = this.loader.loadAllAgentConfigs();
|
|
@@ -59,7 +98,10 @@ class AgentInvoker {
|
|
|
59
98
|
}
|
|
60
99
|
async invokeAgent(agentName, prompt, sessionId, attachments) {
|
|
61
100
|
try {
|
|
62
|
-
const
|
|
101
|
+
const executionWorkspace = resolveExecutionWorkspace(this.workspace, this.workdir);
|
|
102
|
+
const effectiveWorkdir = this.workdir ? executionWorkspace : null;
|
|
103
|
+
const loader = (0, external_node_path_namespaceObject.normalize)(executionWorkspace) === (0, external_node_path_namespaceObject.normalize)(this.workspace) ? this.loader : new agentLoader_cjs_namespaceObject.AgentLoader(this.configDir, this.workspace, this.wingmanConfig, executionWorkspace);
|
|
104
|
+
const targetAgent = await loader.loadAgent(agentName);
|
|
63
105
|
if (!targetAgent) throw new Error(`Agent "${agentName}" not found`);
|
|
64
106
|
this.logger.info(`Invoking agent: ${agentName}`);
|
|
65
107
|
const preview = prompt.trim() || (attachments && attachments.length > 0 ? buildAttachmentPreview(attachments) : "");
|
|
@@ -87,43 +129,65 @@ class AgentInvoker {
|
|
|
87
129
|
}
|
|
88
130
|
}
|
|
89
131
|
const skillsDirectory = this.wingmanConfig?.skills?.skillsDirectory || "skills";
|
|
132
|
+
const normalizedSkillsDirectory = skillsDirectory.replace(/^\/+|\/+$/g, "");
|
|
133
|
+
const skillsVirtualPath = `/${normalizedSkillsDirectory}/`;
|
|
134
|
+
const outputMount = resolveExternalOutputMount(executionWorkspace, effectiveWorkdir, this.defaultOutputDir);
|
|
90
135
|
const middleware = [
|
|
91
136
|
(0, media_compat_cjs_namespaceObject.mediaCompatibilityMiddleware)({
|
|
92
137
|
model: targetAgent.model
|
|
93
138
|
}),
|
|
94
139
|
(0, additional_messages_cjs_namespaceObject.additionalMessageMiddleware)({
|
|
95
|
-
workspaceRoot:
|
|
96
|
-
workdir:
|
|
140
|
+
workspaceRoot: executionWorkspace,
|
|
141
|
+
workdir: effectiveWorkdir,
|
|
97
142
|
defaultOutputDir: this.defaultOutputDir,
|
|
143
|
+
outputVirtualPath: outputMount.virtualPath,
|
|
98
144
|
dynamicUiEnabled: this.wingmanConfig?.gateway?.dynamicUiEnabled !== false,
|
|
99
145
|
skillsDirectory
|
|
100
146
|
})
|
|
101
147
|
];
|
|
102
148
|
if (mergedHooks) {
|
|
103
149
|
this.logger.debug(`Adding hooks middleware with ${mergedHooks.PreToolUse?.length || 0} PreToolUse hooks, ${mergedHooks.PostToolUse?.length || 0} PostToolUse hooks, and ${mergedHooks.Stop?.length || 0} Stop hooks`);
|
|
104
|
-
middleware.push((0, hooks_cjs_namespaceObject.createHooksMiddleware)(mergedHooks,
|
|
150
|
+
middleware.push((0, hooks_cjs_namespaceObject.createHooksMiddleware)(mergedHooks, executionWorkspace, hookSessionId, this.logger));
|
|
105
151
|
}
|
|
106
152
|
const checkpointer = this.sessionManager?.getCheckpointer();
|
|
107
153
|
const bundledSkillsPath = (0, uiRegistry_cjs_namespaceObject.getBundledSkillsPath)();
|
|
108
154
|
const skillsSources = [];
|
|
109
155
|
if ((0, external_node_fs_namespaceObject.existsSync)(bundledSkillsPath)) skillsSources.push("/skills-bundled/");
|
|
110
|
-
skillsSources.push(
|
|
156
|
+
skillsSources.push(skillsVirtualPath);
|
|
111
157
|
const backendOverrides = {
|
|
112
158
|
"/memories/": new external_deepagents_namespaceObject.FilesystemBackend({
|
|
113
159
|
rootDir: (0, external_node_path_namespaceObject.join)(this.workspace, this.configDir, "memories"),
|
|
114
160
|
virtualMode: true
|
|
115
161
|
})
|
|
116
162
|
};
|
|
163
|
+
const executionWorkspaceAlias = toWorkspaceAliasVirtualPath(executionWorkspace);
|
|
164
|
+
if (executionWorkspaceAlias) backendOverrides[executionWorkspaceAlias] = new external_deepagents_namespaceObject.FilesystemBackend({
|
|
165
|
+
rootDir: executionWorkspace,
|
|
166
|
+
virtualMode: true
|
|
167
|
+
});
|
|
168
|
+
if (effectiveWorkdir) backendOverrides[WORKDIR_VIRTUAL_PATH] = new external_deepagents_namespaceObject.FilesystemBackend({
|
|
169
|
+
rootDir: executionWorkspace,
|
|
170
|
+
virtualMode: true
|
|
171
|
+
});
|
|
172
|
+
const workspaceSkillsPath = (0, external_node_path_namespaceObject.join)(this.workspace, normalizedSkillsDirectory);
|
|
173
|
+
if ((0, external_node_fs_namespaceObject.existsSync)(workspaceSkillsPath)) backendOverrides[skillsVirtualPath] = new external_deepagents_namespaceObject.FilesystemBackend({
|
|
174
|
+
rootDir: workspaceSkillsPath,
|
|
175
|
+
virtualMode: true
|
|
176
|
+
});
|
|
117
177
|
if ((0, external_node_fs_namespaceObject.existsSync)(bundledSkillsPath)) backendOverrides["/skills-bundled/"] = new external_deepagents_namespaceObject.FilesystemBackend({
|
|
118
178
|
rootDir: bundledSkillsPath,
|
|
119
179
|
virtualMode: true
|
|
120
180
|
});
|
|
181
|
+
if (outputMount.virtualPath && outputMount.absolutePath) backendOverrides[outputMount.virtualPath] = new external_deepagents_namespaceObject.FilesystemBackend({
|
|
182
|
+
rootDir: outputMount.absolutePath,
|
|
183
|
+
virtualMode: true
|
|
184
|
+
});
|
|
121
185
|
const standaloneAgent = (0, external_deepagents_namespaceObject.createDeepAgent)({
|
|
122
186
|
systemPrompt: targetAgent.systemPrompt,
|
|
123
187
|
tools: targetAgent.tools,
|
|
124
188
|
model: targetAgent.model,
|
|
125
189
|
backend: ()=>new external_deepagents_namespaceObject.CompositeBackend(new external_deepagents_namespaceObject.FilesystemBackend({
|
|
126
|
-
rootDir:
|
|
190
|
+
rootDir: executionWorkspace,
|
|
127
191
|
virtualMode: true
|
|
128
192
|
}), backendOverrides),
|
|
129
193
|
middleware: middleware,
|
|
@@ -132,7 +196,7 @@ class AgentInvoker {
|
|
|
132
196
|
checkpointer: checkpointer
|
|
133
197
|
});
|
|
134
198
|
this.logger.debug("Agent created, sending message");
|
|
135
|
-
const userContent = buildUserContent(prompt, attachments);
|
|
199
|
+
const userContent = buildUserContent(prompt, attachments, targetAgent.model);
|
|
136
200
|
if (this.sessionManager && sessionId) {
|
|
137
201
|
this.logger.debug(`Using streaming with session: ${sessionId}`);
|
|
138
202
|
const stream = await standaloneAgent.streamEvents({
|
|
@@ -216,7 +280,7 @@ class AgentInvoker {
|
|
|
216
280
|
this.loader = new agentLoader_cjs_namespaceObject.AgentLoader(this.configDir, this.workspace, this.wingmanConfig);
|
|
217
281
|
}
|
|
218
282
|
}
|
|
219
|
-
function buildUserContent(prompt, attachments) {
|
|
283
|
+
function buildUserContent(prompt, attachments, model) {
|
|
220
284
|
const text = prompt?.trim() ?? "";
|
|
221
285
|
if (!attachments || 0 === attachments.length) return text;
|
|
222
286
|
const parts = [];
|
|
@@ -224,18 +288,32 @@ function buildUserContent(prompt, attachments) {
|
|
|
224
288
|
type: "text",
|
|
225
289
|
text
|
|
226
290
|
});
|
|
227
|
-
for (const attachment of attachments)if (attachment
|
|
228
|
-
if (
|
|
229
|
-
const
|
|
230
|
-
if (
|
|
291
|
+
for (const attachment of attachments)if (attachment) {
|
|
292
|
+
if (isFileAttachment(attachment)) {
|
|
293
|
+
const nativePdfPart = buildNativePdfPart(attachment, model);
|
|
294
|
+
if (nativePdfPart) {
|
|
295
|
+
parts.push(nativePdfPart);
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
parts.push({
|
|
299
|
+
type: "text",
|
|
300
|
+
text: buildFileAttachmentText(attachment)
|
|
301
|
+
});
|
|
231
302
|
continue;
|
|
232
303
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
304
|
+
if (attachment.dataUrl) {
|
|
305
|
+
if (isAudioAttachment(attachment)) {
|
|
306
|
+
const audioPart = buildAudioPart(attachment);
|
|
307
|
+
if (audioPart) parts.push(audioPart);
|
|
308
|
+
continue;
|
|
237
309
|
}
|
|
238
|
-
|
|
310
|
+
parts.push({
|
|
311
|
+
type: "image_url",
|
|
312
|
+
image_url: {
|
|
313
|
+
url: attachment.dataUrl
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
}
|
|
239
317
|
}
|
|
240
318
|
if (0 === parts.length) {
|
|
241
319
|
if (!text) throw new Error("Attachment payload is empty or invalid.");
|
|
@@ -243,12 +321,93 @@ function buildUserContent(prompt, attachments) {
|
|
|
243
321
|
}
|
|
244
322
|
return parts;
|
|
245
323
|
}
|
|
324
|
+
function supportsNativePdfInputs(model) {
|
|
325
|
+
if (!model || "object" != typeof model) return false;
|
|
326
|
+
try {
|
|
327
|
+
const profile = model.profile;
|
|
328
|
+
if (!profile || "object" != typeof profile) return false;
|
|
329
|
+
return true === profile.pdfInputs;
|
|
330
|
+
} catch {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function isPdfName(name) {
|
|
335
|
+
return (name || "").trim().toLowerCase().endsWith(".pdf");
|
|
336
|
+
}
|
|
337
|
+
function resolveFileMimeType(attachment) {
|
|
338
|
+
const direct = attachment.mimeType?.trim().toLowerCase();
|
|
339
|
+
if (direct) return direct.split(";")[0] || "";
|
|
340
|
+
const parsed = parseDataUrl(attachment.dataUrl);
|
|
341
|
+
return (parsed.mimeType || "").trim().toLowerCase().split(";")[0] || "";
|
|
342
|
+
}
|
|
343
|
+
function buildFileMetadata(attachment, defaultName) {
|
|
344
|
+
const filename = attachment.name?.trim() || defaultName;
|
|
345
|
+
return {
|
|
346
|
+
filename,
|
|
347
|
+
name: filename,
|
|
348
|
+
title: filename
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
function buildNativePdfPart(attachment, model) {
|
|
352
|
+
if (!supportsNativePdfInputs(model)) return null;
|
|
353
|
+
const mimeType = resolveFileMimeType(attachment);
|
|
354
|
+
const isPdf = "application/pdf" === mimeType || isPdfName(attachment.name);
|
|
355
|
+
if (!isPdf) return null;
|
|
356
|
+
const metadata = buildFileMetadata(attachment, "document.pdf");
|
|
357
|
+
const parsed = parseDataUrl(attachment.dataUrl);
|
|
358
|
+
const useResponsesInputFile = shouldUseResponsesInputFile(model);
|
|
359
|
+
if (useResponsesInputFile) {
|
|
360
|
+
if (parsed.data) {
|
|
361
|
+
const fileDataMime = parsed.mimeType || mimeType || "application/pdf";
|
|
362
|
+
return {
|
|
363
|
+
type: "input_file",
|
|
364
|
+
file_data: `data:${fileDataMime};base64,${parsed.data}`,
|
|
365
|
+
filename: metadata.filename
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
const fileDataUrl = attachment.dataUrl?.trim();
|
|
369
|
+
if (!fileDataUrl || !fileDataUrl.startsWith("data:")) return null;
|
|
370
|
+
return {
|
|
371
|
+
type: "input_file",
|
|
372
|
+
file_data: fileDataUrl,
|
|
373
|
+
filename: metadata.filename
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
if (parsed.data) return {
|
|
377
|
+
type: "file",
|
|
378
|
+
source_type: "base64",
|
|
379
|
+
mime_type: parsed.mimeType || mimeType || "application/pdf",
|
|
380
|
+
data: parsed.data,
|
|
381
|
+
metadata
|
|
382
|
+
};
|
|
383
|
+
const url = attachment.dataUrl?.trim();
|
|
384
|
+
if (!url || !url.startsWith("data:")) return null;
|
|
385
|
+
return {
|
|
386
|
+
type: "file",
|
|
387
|
+
source_type: "url",
|
|
388
|
+
mime_type: mimeType || "application/pdf",
|
|
389
|
+
url,
|
|
390
|
+
metadata
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
function shouldUseResponsesInputFile(model) {
|
|
394
|
+
if (!model || "object" != typeof model) return false;
|
|
395
|
+
try {
|
|
396
|
+
const flag = model.useResponsesApi;
|
|
397
|
+
if ("boolean" == typeof flag) return flag;
|
|
398
|
+
} catch {}
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
246
401
|
function isAudioAttachment(attachment) {
|
|
247
402
|
if ("audio" === attachment.kind) return true;
|
|
248
403
|
if (attachment.mimeType?.startsWith("audio/")) return true;
|
|
249
404
|
if (attachment.dataUrl?.startsWith("data:audio/")) return true;
|
|
250
405
|
return false;
|
|
251
406
|
}
|
|
407
|
+
function isFileAttachment(attachment) {
|
|
408
|
+
if ("file" === attachment.kind) return true;
|
|
409
|
+
return "string" == typeof attachment.textContent;
|
|
410
|
+
}
|
|
252
411
|
function buildAudioPart(attachment) {
|
|
253
412
|
const parsed = parseDataUrl(attachment.dataUrl);
|
|
254
413
|
const mimeType = attachment.mimeType || parsed.mimeType;
|
|
@@ -274,21 +433,53 @@ function parseDataUrl(dataUrl) {
|
|
|
274
433
|
data: match[2]
|
|
275
434
|
};
|
|
276
435
|
}
|
|
436
|
+
function buildFileAttachmentText(attachment) {
|
|
437
|
+
const name = attachment.name?.trim() || "file";
|
|
438
|
+
const mime = attachment.mimeType?.trim();
|
|
439
|
+
const sizeLabel = "number" == typeof attachment.size && attachment.size >= 0 ? `, ${attachment.size} bytes` : "";
|
|
440
|
+
const meta = mime || sizeLabel ? ` (${[
|
|
441
|
+
mime,
|
|
442
|
+
sizeLabel.replace(/^, /, "")
|
|
443
|
+
].filter(Boolean).join(", ")})` : "";
|
|
444
|
+
const header = `[Attached file: ${name}${meta}]`;
|
|
445
|
+
const text = attachment.textContent?.trim();
|
|
446
|
+
if (!text) return `${header}\n[No extractable text content provided.]`;
|
|
447
|
+
return `${header}\n${text}`;
|
|
448
|
+
}
|
|
277
449
|
function buildAttachmentPreview(attachments) {
|
|
450
|
+
let hasFile = false;
|
|
278
451
|
let hasAudio = false;
|
|
279
452
|
let hasImage = false;
|
|
280
|
-
for (const attachment of attachments)
|
|
281
|
-
|
|
453
|
+
for (const attachment of attachments){
|
|
454
|
+
if (isFileAttachment(attachment)) {
|
|
455
|
+
hasFile = true;
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (isAudioAttachment(attachment)) hasAudio = true;
|
|
459
|
+
else hasImage = true;
|
|
460
|
+
}
|
|
461
|
+
if (hasFile && (hasAudio || hasImage)) return "[files and media]";
|
|
282
462
|
if (hasAudio && hasImage) return "[attachments]";
|
|
463
|
+
if (hasFile) return "[file]";
|
|
283
464
|
if (hasAudio) return "[audio]";
|
|
284
465
|
if (hasImage) return "[image]";
|
|
285
466
|
return "";
|
|
286
467
|
}
|
|
287
468
|
exports.AgentInvoker = __webpack_exports__.AgentInvoker;
|
|
469
|
+
exports.OUTPUT_VIRTUAL_PATH = __webpack_exports__.OUTPUT_VIRTUAL_PATH;
|
|
470
|
+
exports.WORKDIR_VIRTUAL_PATH = __webpack_exports__.WORKDIR_VIRTUAL_PATH;
|
|
288
471
|
exports.buildUserContent = __webpack_exports__.buildUserContent;
|
|
472
|
+
exports.resolveExecutionWorkspace = __webpack_exports__.resolveExecutionWorkspace;
|
|
473
|
+
exports.resolveExternalOutputMount = __webpack_exports__.resolveExternalOutputMount;
|
|
474
|
+
exports.toWorkspaceAliasVirtualPath = __webpack_exports__.toWorkspaceAliasVirtualPath;
|
|
289
475
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
290
476
|
"AgentInvoker",
|
|
291
|
-
"
|
|
477
|
+
"OUTPUT_VIRTUAL_PATH",
|
|
478
|
+
"WORKDIR_VIRTUAL_PATH",
|
|
479
|
+
"buildUserContent",
|
|
480
|
+
"resolveExecutionWorkspace",
|
|
481
|
+
"resolveExternalOutputMount",
|
|
482
|
+
"toWorkspaceAliasVirtualPath"
|
|
292
483
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
293
484
|
Object.defineProperty(exports, '__esModule', {
|
|
294
485
|
value: true
|