@wingman-ai/gateway 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/.wingman/agents/README.md +7 -0
  2. package/.wingman/agents/coding/agent.md +1 -0
  3. package/.wingman/agents/main/agent.md +1 -0
  4. package/.wingman/agents/researcher/agent.md +1 -0
  5. package/.wingman/agents/stock-trader/agent.md +8 -2
  6. package/dist/agent/config/agentConfig.cjs +12 -1
  7. package/dist/agent/config/agentConfig.d.ts +14 -0
  8. package/dist/agent/config/agentConfig.js +12 -1
  9. package/dist/agent/config/agentLoader.cjs +37 -1
  10. package/dist/agent/config/agentLoader.d.ts +2 -1
  11. package/dist/agent/config/agentLoader.js +37 -1
  12. package/dist/agent/config/modelFactory.cjs +2 -1
  13. package/dist/agent/config/modelFactory.js +2 -1
  14. package/dist/agent/config/toolRegistry.cjs +6 -4
  15. package/dist/agent/config/toolRegistry.d.ts +1 -0
  16. package/dist/agent/config/toolRegistry.js +6 -4
  17. package/dist/agent/middleware/additional-messages.cjs +8 -1
  18. package/dist/agent/middleware/additional-messages.d.ts +1 -0
  19. package/dist/agent/middleware/additional-messages.js +8 -1
  20. package/dist/agent/tests/agentConfig.test.cjs +25 -0
  21. package/dist/agent/tests/agentConfig.test.js +25 -0
  22. package/dist/agent/tests/agentLoader.test.cjs +18 -0
  23. package/dist/agent/tests/agentLoader.test.js +18 -0
  24. package/dist/agent/tests/modelFactory.test.cjs +13 -0
  25. package/dist/agent/tests/modelFactory.test.js +14 -1
  26. package/dist/agent/tests/toolRegistry.test.cjs +15 -0
  27. package/dist/agent/tests/toolRegistry.test.js +15 -0
  28. package/dist/agent/tests/uiRegistryTools.test.cjs +15 -0
  29. package/dist/agent/tests/uiRegistryTools.test.js +15 -0
  30. package/dist/agent/tools/code_search.cjs +1 -1
  31. package/dist/agent/tools/code_search.js +1 -1
  32. package/dist/agent/tools/command_execute.cjs +1 -1
  33. package/dist/agent/tools/command_execute.js +1 -1
  34. package/dist/agent/tools/ui_registry.d.ts +3 -3
  35. package/dist/cli/core/agentInvoker.cjs +212 -21
  36. package/dist/cli/core/agentInvoker.d.ts +55 -20
  37. package/dist/cli/core/agentInvoker.js +197 -21
  38. package/dist/cli/core/sessionManager.cjs +93 -4
  39. package/dist/cli/core/sessionManager.d.ts +1 -1
  40. package/dist/cli/core/sessionManager.js +93 -4
  41. package/dist/gateway/http/agents.cjs +121 -10
  42. package/dist/gateway/http/agents.js +121 -10
  43. package/dist/gateway/server.cjs +55 -17
  44. package/dist/gateway/server.js +55 -17
  45. package/dist/gateway/types.d.ts +9 -1
  46. package/dist/tests/additionalMessageMiddleware.test.cjs +26 -0
  47. package/dist/tests/additionalMessageMiddleware.test.js +26 -0
  48. package/dist/tests/agentInvokerAttachments.test.cjs +123 -0
  49. package/dist/tests/agentInvokerAttachments.test.js +123 -0
  50. package/dist/tests/agentInvokerWorkdir.test.cjs +100 -0
  51. package/dist/tests/agentInvokerWorkdir.test.d.ts +1 -0
  52. package/dist/tests/agentInvokerWorkdir.test.js +72 -0
  53. package/dist/tests/agents-api.test.cjs +232 -0
  54. package/dist/tests/agents-api.test.d.ts +1 -0
  55. package/dist/tests/agents-api.test.js +226 -0
  56. package/dist/tests/gateway.test.cjs +24 -0
  57. package/dist/tests/gateway.test.js +24 -0
  58. package/dist/tests/sessionMessageAttachments.test.cjs +59 -0
  59. package/dist/tests/sessionMessageAttachments.test.js +59 -0
  60. package/dist/types/agents.d.ts +5 -0
  61. package/dist/webui/assets/{index-DTK8ignm.css → index-BytPznA_.css} +1 -1
  62. package/dist/webui/assets/index-u_5qlVip.js +176 -0
  63. package/dist/webui/index.html +2 -2
  64. package/package.json +7 -7
  65. package/skills/ui-registry/registry.json +90 -0
  66. package/.wingman/agents/wingman/agent.json +0 -12
  67. package/dist/webui/assets/index-CZt-NLM_.js +0 -178
@@ -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 entire workspace."),
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 entire workspace."),
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?: "end" | "start" | "center" | "stretch" | undefined;
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?: "end" | "start" | "center" | "stretch" | undefined;
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
- AgentInvoker: ()=>AgentInvoker
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 targetAgent = await this.findAgent(agentName);
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: this.workspace,
96
- workdir: this.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, this.workspace, hookSessionId, this.logger));
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(`/${skillsDirectory.replace(/^\/+|\/+$/g, "")}/`);
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: this.workspace,
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?.dataUrl) {
228
- if (isAudioAttachment(attachment)) {
229
- const audioPart = buildAudioPart(attachment);
230
- if (audioPart) parts.push(audioPart);
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
- parts.push({
234
- type: "image_url",
235
- image_url: {
236
- url: attachment.dataUrl
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)if (isAudioAttachment(attachment)) hasAudio = true;
281
- else hasImage = true;
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
- "buildUserContent"
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