@wingman-ai/gateway 0.2.5 → 0.3.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 (90) hide show
  1. package/.wingman/agents/coding/agent.md +5 -0
  2. package/.wingman/agents/coding-v2/agent.md +58 -0
  3. package/.wingman/agents/game-dev/agent.md +94 -0
  4. package/.wingman/agents/game-dev/art-generation.md +37 -0
  5. package/.wingman/agents/game-dev/asset-refinement.md +17 -0
  6. package/.wingman/agents/game-dev/planning-idea.md +17 -0
  7. package/.wingman/agents/game-dev/ui-specialist.md +17 -0
  8. package/.wingman/agents/main/agent.md +2 -0
  9. package/README.md +1 -0
  10. package/dist/agent/config/agentConfig.d.ts +4 -0
  11. package/dist/agent/config/mcpClientManager.cjs +44 -10
  12. package/dist/agent/config/mcpClientManager.d.ts +6 -2
  13. package/dist/agent/config/mcpClientManager.js +44 -10
  14. package/dist/agent/config/toolRegistry.cjs +3 -1
  15. package/dist/agent/config/toolRegistry.js +3 -1
  16. package/dist/agent/tests/mcpClientManager.test.cjs +124 -0
  17. package/dist/agent/tests/mcpClientManager.test.d.ts +1 -0
  18. package/dist/agent/tests/mcpClientManager.test.js +118 -0
  19. package/dist/agent/tools/command_execute.cjs +1 -1
  20. package/dist/agent/tools/command_execute.js +1 -1
  21. package/dist/cli/config/schema.d.ts +2 -0
  22. package/dist/cli/core/agentInvoker.cjs +55 -66
  23. package/dist/cli/core/agentInvoker.d.ts +10 -13
  24. package/dist/cli/core/agentInvoker.js +42 -62
  25. package/dist/cli/core/imagePersistence.cjs +125 -0
  26. package/dist/cli/core/imagePersistence.d.ts +24 -0
  27. package/dist/cli/core/imagePersistence.js +85 -0
  28. package/dist/cli/core/sessionManager.cjs +297 -40
  29. package/dist/cli/core/sessionManager.d.ts +9 -0
  30. package/dist/cli/core/sessionManager.js +297 -40
  31. package/dist/debug/terminalProbe.cjs +57 -0
  32. package/dist/debug/terminalProbe.d.ts +10 -0
  33. package/dist/debug/terminalProbe.js +20 -0
  34. package/dist/debug/terminalProbeAuth.cjs +140 -0
  35. package/dist/debug/terminalProbeAuth.d.ts +20 -0
  36. package/dist/debug/terminalProbeAuth.js +97 -0
  37. package/dist/gateway/http/fs.cjs +19 -0
  38. package/dist/gateway/http/fs.js +19 -0
  39. package/dist/gateway/http/sessions.cjs +25 -5
  40. package/dist/gateway/http/sessions.js +25 -5
  41. package/dist/gateway/server.cjs +112 -11
  42. package/dist/gateway/server.d.ts +2 -0
  43. package/dist/gateway/server.js +112 -11
  44. package/dist/tests/agentInvokerSummarization.test.cjs +56 -37
  45. package/dist/tests/agentInvokerSummarization.test.js +58 -39
  46. package/dist/tests/agentInvokerWorkdir.test.cjs +50 -0
  47. package/dist/tests/agentInvokerWorkdir.test.js +52 -2
  48. package/dist/tests/cli-init.test.cjs +36 -0
  49. package/dist/tests/cli-init.test.js +36 -0
  50. package/dist/tests/falRuntime.test.cjs +78 -0
  51. package/dist/tests/falRuntime.test.d.ts +1 -0
  52. package/dist/tests/falRuntime.test.js +72 -0
  53. package/dist/tests/falSummary.test.cjs +51 -0
  54. package/dist/tests/falSummary.test.d.ts +1 -0
  55. package/dist/tests/falSummary.test.js +45 -0
  56. package/dist/tests/gateway.test.cjs +109 -1
  57. package/dist/tests/gateway.test.js +109 -1
  58. package/dist/tests/imagePersistence.test.cjs +143 -0
  59. package/dist/tests/imagePersistence.test.d.ts +1 -0
  60. package/dist/tests/imagePersistence.test.js +137 -0
  61. package/dist/tests/sessionMessageAttachments.test.cjs +30 -0
  62. package/dist/tests/sessionMessageAttachments.test.js +30 -0
  63. package/dist/tests/sessionStateMessages.test.cjs +126 -0
  64. package/dist/tests/sessionStateMessages.test.js +126 -0
  65. package/dist/tests/sessions-api.test.cjs +117 -3
  66. package/dist/tests/sessions-api.test.js +118 -4
  67. package/dist/tests/terminalProbe.test.cjs +45 -0
  68. package/dist/tests/terminalProbe.test.d.ts +1 -0
  69. package/dist/tests/terminalProbe.test.js +39 -0
  70. package/dist/tests/terminalProbeAuth.test.cjs +85 -0
  71. package/dist/tests/terminalProbeAuth.test.d.ts +1 -0
  72. package/dist/tests/terminalProbeAuth.test.js +79 -0
  73. package/dist/tools/fal/runtime.cjs +103 -0
  74. package/dist/tools/fal/runtime.d.ts +10 -0
  75. package/dist/tools/fal/runtime.js +60 -0
  76. package/dist/tools/fal/summary.cjs +78 -0
  77. package/dist/tools/fal/summary.d.ts +22 -0
  78. package/dist/tools/fal/summary.js +41 -0
  79. package/dist/tools/mcp-fal-ai.cjs +1041 -0
  80. package/dist/tools/mcp-fal-ai.d.ts +1 -0
  81. package/dist/tools/mcp-fal-ai.js +1025 -0
  82. package/dist/types/mcp.cjs +2 -0
  83. package/dist/types/mcp.d.ts +8 -0
  84. package/dist/types/mcp.js +3 -1
  85. package/dist/webui/assets/index-0nUBsUUq.js +278 -0
  86. package/dist/webui/assets/index-kk7OrD-G.css +11 -0
  87. package/dist/webui/index.html +2 -2
  88. package/package.json +11 -8
  89. package/dist/webui/assets/index-C7EuTbnE.js +0 -270
  90. package/dist/webui/assets/index-DVWQluit.css +0 -11
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ var __webpack_exports__ = {};
3
+ const external_node_fs_namespaceObject = require("node:fs");
4
+ const external_node_os_namespaceObject = require("node:os");
5
+ const external_node_path_namespaceObject = require("node:path");
6
+ const external_vitest_namespaceObject = require("vitest");
7
+ const imagePersistence_cjs_namespaceObject = require("../cli/core/imagePersistence.cjs");
8
+ const PNG_DATA_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO3X6S0AAAAASUVORK5CYII=";
9
+ (0, external_vitest_namespaceObject.describe)("imagePersistence", ()=>{
10
+ const tempDirs = [];
11
+ (0, external_vitest_namespaceObject.afterEach)(()=>{
12
+ for (const dir of tempDirs)(0, external_node_fs_namespaceObject.rmSync)(dir, {
13
+ recursive: true,
14
+ force: true
15
+ });
16
+ tempDirs.length = 0;
17
+ });
18
+ (0, external_vitest_namespaceObject.it)("persists assistant image data URLs to disk", ()=>{
19
+ const tempDir = (0, external_node_fs_namespaceObject.mkdtempSync)((0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.tmpdir)(), "wingman-image-store-"));
20
+ tempDirs.push(tempDir);
21
+ const dbPath = (0, external_node_path_namespaceObject.join)(tempDir, "wingman.db");
22
+ const messages = [
23
+ {
24
+ role: "assistant",
25
+ attachments: [
26
+ {
27
+ kind: "image",
28
+ dataUrl: PNG_DATA_URL
29
+ }
30
+ ]
31
+ }
32
+ ];
33
+ (0, imagePersistence_cjs_namespaceObject.persistAssistantImagesToDisk)({
34
+ dbPath,
35
+ sessionId: "session-123",
36
+ messages
37
+ });
38
+ const attachment = messages[0].attachments?.[0];
39
+ (0, external_vitest_namespaceObject.expect)(typeof attachment?.path).toBe("string");
40
+ (0, external_vitest_namespaceObject.expect)(attachment?.mimeType).toBe("image/png");
41
+ (0, external_vitest_namespaceObject.expect)(typeof attachment?.size).toBe("number");
42
+ (0, external_vitest_namespaceObject.expect)(attachment?.size).toBeGreaterThan(0);
43
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(attachment.path)).toBe(true);
44
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.readFileSync)(attachment.path).length).toBe(attachment?.size);
45
+ });
46
+ (0, external_vitest_namespaceObject.it)("does not persist non-assistant images", ()=>{
47
+ const tempDir = (0, external_node_fs_namespaceObject.mkdtempSync)((0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.tmpdir)(), "wingman-image-store-"));
48
+ tempDirs.push(tempDir);
49
+ const dbPath = (0, external_node_path_namespaceObject.join)(tempDir, "wingman.db");
50
+ const messages = [
51
+ {
52
+ role: "user",
53
+ attachments: [
54
+ {
55
+ kind: "image",
56
+ dataUrl: PNG_DATA_URL
57
+ }
58
+ ]
59
+ }
60
+ ];
61
+ (0, imagePersistence_cjs_namespaceObject.persistAssistantImagesToDisk)({
62
+ dbPath,
63
+ sessionId: "session-123",
64
+ messages
65
+ });
66
+ (0, external_vitest_namespaceObject.expect)(messages[0].attachments?.[0].path).toBeUndefined();
67
+ });
68
+ (0, external_vitest_namespaceObject.it)("keeps remote image URLs untouched", ()=>{
69
+ const tempDir = (0, external_node_fs_namespaceObject.mkdtempSync)((0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.tmpdir)(), "wingman-image-store-"));
70
+ tempDirs.push(tempDir);
71
+ const dbPath = (0, external_node_path_namespaceObject.join)(tempDir, "wingman.db");
72
+ const messages = [
73
+ {
74
+ role: "assistant",
75
+ attachments: [
76
+ {
77
+ kind: "image",
78
+ dataUrl: "https://example.com/image.png"
79
+ }
80
+ ]
81
+ }
82
+ ];
83
+ (0, imagePersistence_cjs_namespaceObject.persistAssistantImagesToDisk)({
84
+ dbPath,
85
+ sessionId: "session-123",
86
+ messages
87
+ });
88
+ (0, external_vitest_namespaceObject.expect)(messages[0].attachments?.[0].path).toBeUndefined();
89
+ });
90
+ (0, external_vitest_namespaceObject.it)("derives deterministic file paths for repeated image payloads", ()=>{
91
+ const tempDir = (0, external_node_fs_namespaceObject.mkdtempSync)((0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.tmpdir)(), "wingman-image-store-"));
92
+ tempDirs.push(tempDir);
93
+ const dbPath = (0, external_node_path_namespaceObject.join)(tempDir, "wingman.db");
94
+ const messages = [
95
+ {
96
+ role: "assistant",
97
+ attachments: [
98
+ {
99
+ kind: "image",
100
+ dataUrl: PNG_DATA_URL
101
+ }
102
+ ]
103
+ }
104
+ ];
105
+ (0, imagePersistence_cjs_namespaceObject.persistAssistantImagesToDisk)({
106
+ dbPath,
107
+ sessionId: "session-123",
108
+ messages
109
+ });
110
+ const firstPath = messages[0].attachments?.[0].path;
111
+ const nextMessages = [
112
+ {
113
+ role: "assistant",
114
+ attachments: [
115
+ {
116
+ kind: "image",
117
+ dataUrl: PNG_DATA_URL
118
+ }
119
+ ]
120
+ }
121
+ ];
122
+ (0, imagePersistence_cjs_namespaceObject.persistAssistantImagesToDisk)({
123
+ dbPath,
124
+ sessionId: "session-123",
125
+ messages: nextMessages
126
+ });
127
+ const secondPath = nextMessages[0].attachments?.[0].path;
128
+ (0, external_vitest_namespaceObject.expect)(firstPath).toBeTruthy();
129
+ (0, external_vitest_namespaceObject.expect)(secondPath).toBe(firstPath);
130
+ });
131
+ (0, external_vitest_namespaceObject.it)("parses base64 data URLs and resolves extensions", ()=>{
132
+ const parsed = (0, imagePersistence_cjs_namespaceObject.parseBase64DataUrl)(PNG_DATA_URL);
133
+ (0, external_vitest_namespaceObject.expect)(parsed?.mimeType).toBe("image/png");
134
+ (0, external_vitest_namespaceObject.expect)(typeof parsed?.data).toBe("string");
135
+ (0, external_vitest_namespaceObject.expect)((0, imagePersistence_cjs_namespaceObject.resolveImageExtension)("image/jpeg")).toBe("jpg");
136
+ (0, external_vitest_namespaceObject.expect)((0, imagePersistence_cjs_namespaceObject.resolveImageExtension)("image/svg+xml")).toBe("svg");
137
+ (0, external_vitest_namespaceObject.expect)((0, imagePersistence_cjs_namespaceObject.resolveImageExtension)("image/custom+format")).toBe("customformat");
138
+ });
139
+ });
140
+ for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
141
+ Object.defineProperty(exports, '__esModule', {
142
+ value: true
143
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,137 @@
1
+ import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+ import { parseBase64DataUrl, persistAssistantImagesToDisk, resolveImageExtension } from "../cli/core/imagePersistence.js";
6
+ const PNG_DATA_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO3X6S0AAAAASUVORK5CYII=";
7
+ describe("imagePersistence", ()=>{
8
+ const tempDirs = [];
9
+ afterEach(()=>{
10
+ for (const dir of tempDirs)rmSync(dir, {
11
+ recursive: true,
12
+ force: true
13
+ });
14
+ tempDirs.length = 0;
15
+ });
16
+ it("persists assistant image data URLs to disk", ()=>{
17
+ const tempDir = mkdtempSync(join(tmpdir(), "wingman-image-store-"));
18
+ tempDirs.push(tempDir);
19
+ const dbPath = join(tempDir, "wingman.db");
20
+ const messages = [
21
+ {
22
+ role: "assistant",
23
+ attachments: [
24
+ {
25
+ kind: "image",
26
+ dataUrl: PNG_DATA_URL
27
+ }
28
+ ]
29
+ }
30
+ ];
31
+ persistAssistantImagesToDisk({
32
+ dbPath,
33
+ sessionId: "session-123",
34
+ messages
35
+ });
36
+ const attachment = messages[0].attachments?.[0];
37
+ expect(typeof attachment?.path).toBe("string");
38
+ expect(attachment?.mimeType).toBe("image/png");
39
+ expect(typeof attachment?.size).toBe("number");
40
+ expect(attachment?.size).toBeGreaterThan(0);
41
+ expect(existsSync(attachment.path)).toBe(true);
42
+ expect(readFileSync(attachment.path).length).toBe(attachment?.size);
43
+ });
44
+ it("does not persist non-assistant images", ()=>{
45
+ const tempDir = mkdtempSync(join(tmpdir(), "wingman-image-store-"));
46
+ tempDirs.push(tempDir);
47
+ const dbPath = join(tempDir, "wingman.db");
48
+ const messages = [
49
+ {
50
+ role: "user",
51
+ attachments: [
52
+ {
53
+ kind: "image",
54
+ dataUrl: PNG_DATA_URL
55
+ }
56
+ ]
57
+ }
58
+ ];
59
+ persistAssistantImagesToDisk({
60
+ dbPath,
61
+ sessionId: "session-123",
62
+ messages
63
+ });
64
+ expect(messages[0].attachments?.[0].path).toBeUndefined();
65
+ });
66
+ it("keeps remote image URLs untouched", ()=>{
67
+ const tempDir = mkdtempSync(join(tmpdir(), "wingman-image-store-"));
68
+ tempDirs.push(tempDir);
69
+ const dbPath = join(tempDir, "wingman.db");
70
+ const messages = [
71
+ {
72
+ role: "assistant",
73
+ attachments: [
74
+ {
75
+ kind: "image",
76
+ dataUrl: "https://example.com/image.png"
77
+ }
78
+ ]
79
+ }
80
+ ];
81
+ persistAssistantImagesToDisk({
82
+ dbPath,
83
+ sessionId: "session-123",
84
+ messages
85
+ });
86
+ expect(messages[0].attachments?.[0].path).toBeUndefined();
87
+ });
88
+ it("derives deterministic file paths for repeated image payloads", ()=>{
89
+ const tempDir = mkdtempSync(join(tmpdir(), "wingman-image-store-"));
90
+ tempDirs.push(tempDir);
91
+ const dbPath = join(tempDir, "wingman.db");
92
+ const messages = [
93
+ {
94
+ role: "assistant",
95
+ attachments: [
96
+ {
97
+ kind: "image",
98
+ dataUrl: PNG_DATA_URL
99
+ }
100
+ ]
101
+ }
102
+ ];
103
+ persistAssistantImagesToDisk({
104
+ dbPath,
105
+ sessionId: "session-123",
106
+ messages
107
+ });
108
+ const firstPath = messages[0].attachments?.[0].path;
109
+ const nextMessages = [
110
+ {
111
+ role: "assistant",
112
+ attachments: [
113
+ {
114
+ kind: "image",
115
+ dataUrl: PNG_DATA_URL
116
+ }
117
+ ]
118
+ }
119
+ ];
120
+ persistAssistantImagesToDisk({
121
+ dbPath,
122
+ sessionId: "session-123",
123
+ messages: nextMessages
124
+ });
125
+ const secondPath = nextMessages[0].attachments?.[0].path;
126
+ expect(firstPath).toBeTruthy();
127
+ expect(secondPath).toBe(firstPath);
128
+ });
129
+ it("parses base64 data URLs and resolves extensions", ()=>{
130
+ const parsed = parseBase64DataUrl(PNG_DATA_URL);
131
+ expect(parsed?.mimeType).toBe("image/png");
132
+ expect(typeof parsed?.data).toBe("string");
133
+ expect(resolveImageExtension("image/jpeg")).toBe("jpg");
134
+ expect(resolveImageExtension("image/svg+xml")).toBe("svg");
135
+ expect(resolveImageExtension("image/custom+format")).toBe("customformat");
136
+ });
137
+ });
@@ -50,6 +50,36 @@ const sessionManager_cjs_namespaceObject = require("../cli/core/sessionManager.c
50
50
  }
51
51
  ]);
52
52
  });
53
+ (0, external_vitest_namespaceObject.it)("extracts MCP image blocks with inline base64 payload", ()=>{
54
+ const blocks = [
55
+ {
56
+ type: "image",
57
+ mimeType: "image/png",
58
+ data: "abc123"
59
+ }
60
+ ];
61
+ (0, external_vitest_namespaceObject.expect)((0, sessionManager_cjs_namespaceObject.extractImageAttachments)(blocks)).toEqual([
62
+ {
63
+ kind: "image",
64
+ dataUrl: "data:image/png;base64,abc123"
65
+ }
66
+ ]);
67
+ });
68
+ (0, external_vitest_namespaceObject.it)("extracts resource_link image blocks", ()=>{
69
+ const blocks = [
70
+ {
71
+ type: "resource_link",
72
+ uri: "/api/fs/file?path=%2Ftmp%2Fgenerated.png",
73
+ mimeType: "image/png"
74
+ }
75
+ ];
76
+ (0, external_vitest_namespaceObject.expect)((0, sessionManager_cjs_namespaceObject.extractImageAttachments)(blocks)).toEqual([
77
+ {
78
+ kind: "image",
79
+ dataUrl: "/api/fs/file?path=%2Ftmp%2Fgenerated.png"
80
+ }
81
+ ]);
82
+ });
53
83
  });
54
84
  (0, external_vitest_namespaceObject.describe)("extractAttachments", ()=>{
55
85
  (0, external_vitest_namespaceObject.it)("extracts input_audio blocks", ()=>{
@@ -48,6 +48,36 @@ describe("extractImageAttachments", ()=>{
48
48
  }
49
49
  ]);
50
50
  });
51
+ it("extracts MCP image blocks with inline base64 payload", ()=>{
52
+ const blocks = [
53
+ {
54
+ type: "image",
55
+ mimeType: "image/png",
56
+ data: "abc123"
57
+ }
58
+ ];
59
+ expect(extractImageAttachments(blocks)).toEqual([
60
+ {
61
+ kind: "image",
62
+ dataUrl: "data:image/png;base64,abc123"
63
+ }
64
+ ]);
65
+ });
66
+ it("extracts resource_link image blocks", ()=>{
67
+ const blocks = [
68
+ {
69
+ type: "resource_link",
70
+ uri: "/api/fs/file?path=%2Ftmp%2Fgenerated.png",
71
+ mimeType: "image/png"
72
+ }
73
+ ];
74
+ expect(extractImageAttachments(blocks)).toEqual([
75
+ {
76
+ kind: "image",
77
+ dataUrl: "/api/fs/file?path=%2Ftmp%2Fgenerated.png"
78
+ }
79
+ ]);
80
+ });
51
81
  });
52
82
  describe("extractAttachments", ()=>{
53
83
  it("extracts input_audio blocks", ()=>{
@@ -103,6 +103,132 @@ const sessionManager_cjs_namespaceObject = require("../cli/core/sessionManager.c
103
103
  content: "Here is the plan."
104
104
  });
105
105
  });
106
+ (0, external_vitest_namespaceObject.it)("promotes tool image outputs into assistant attachments", ()=>{
107
+ const state = {
108
+ createdAt: 3000,
109
+ values: {
110
+ messages: [
111
+ {
112
+ role: "user",
113
+ content: "Create a puppy image."
114
+ },
115
+ {
116
+ role: "tool",
117
+ content: JSON.stringify({
118
+ content: [
119
+ {
120
+ type: "text",
121
+ text: "Generated image."
122
+ },
123
+ {
124
+ type: "resource_link",
125
+ uri: "/api/fs/file?path=%2Ftmp%2Fpuppy.png",
126
+ mimeType: "image/png"
127
+ }
128
+ ]
129
+ })
130
+ }
131
+ ]
132
+ }
133
+ };
134
+ const result = (0, sessionManager_cjs_namespaceObject.extractMessagesFromState)(state);
135
+ (0, external_vitest_namespaceObject.expect)(result).not.toBeNull();
136
+ (0, external_vitest_namespaceObject.expect)(result).toHaveLength(2);
137
+ (0, external_vitest_namespaceObject.expect)(result?.[1]).toMatchObject({
138
+ role: "assistant",
139
+ content: "Generated image.",
140
+ attachments: [
141
+ {
142
+ kind: "image",
143
+ dataUrl: "/api/fs/file?path=%2Ftmp%2Fpuppy.png"
144
+ }
145
+ ]
146
+ });
147
+ });
148
+ (0, external_vitest_namespaceObject.it)("extracts tool attachments when MCP payload is wrapped in a text block", ()=>{
149
+ const state = {
150
+ createdAt: 4000,
151
+ values: {
152
+ messages: [
153
+ {
154
+ role: "user",
155
+ content: "Generate a landscape image."
156
+ },
157
+ {
158
+ role: "tool",
159
+ content: [
160
+ {
161
+ type: "text",
162
+ text: JSON.stringify({
163
+ content: [
164
+ {
165
+ type: "text",
166
+ text: "Image ready."
167
+ },
168
+ {
169
+ type: "resource_link",
170
+ uri: "/api/fs/file?path=%2Ftmp%2Flandscape.png",
171
+ mimeType: "image/png"
172
+ }
173
+ ]
174
+ })
175
+ }
176
+ ]
177
+ }
178
+ ]
179
+ }
180
+ };
181
+ const result = (0, sessionManager_cjs_namespaceObject.extractMessagesFromState)(state);
182
+ (0, external_vitest_namespaceObject.expect)(result).not.toBeNull();
183
+ (0, external_vitest_namespaceObject.expect)(result).toHaveLength(2);
184
+ (0, external_vitest_namespaceObject.expect)(result?.[1]).toMatchObject({
185
+ role: "assistant",
186
+ content: "Image ready.",
187
+ attachments: [
188
+ {
189
+ kind: "image",
190
+ dataUrl: "/api/fs/file?path=%2Ftmp%2Flandscape.png"
191
+ }
192
+ ]
193
+ });
194
+ });
195
+ (0, external_vitest_namespaceObject.it)("extracts tool image attachments from artifact blocks", ()=>{
196
+ const state = {
197
+ createdAt: 5000,
198
+ values: {
199
+ messages: [
200
+ {
201
+ role: "user",
202
+ content: "Generate another image."
203
+ },
204
+ {
205
+ role: "tool",
206
+ content: "Generated image.",
207
+ artifact: [
208
+ {
209
+ type: "image",
210
+ mimeType: "image/png",
211
+ data: "abc123"
212
+ }
213
+ ]
214
+ }
215
+ ]
216
+ }
217
+ };
218
+ const result = (0, sessionManager_cjs_namespaceObject.extractMessagesFromState)(state);
219
+ (0, external_vitest_namespaceObject.expect)(result).not.toBeNull();
220
+ (0, external_vitest_namespaceObject.expect)(result).toHaveLength(2);
221
+ (0, external_vitest_namespaceObject.expect)(result?.[1]).toMatchObject({
222
+ role: "assistant",
223
+ content: "Generated image.",
224
+ attachments: [
225
+ {
226
+ kind: "image",
227
+ dataUrl: "data:image/png;base64,abc123"
228
+ }
229
+ ]
230
+ });
231
+ });
106
232
  });
107
233
  for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
108
234
  Object.defineProperty(exports, '__esModule', {
@@ -101,4 +101,130 @@ describe("extractMessagesFromState", ()=>{
101
101
  content: "Here is the plan."
102
102
  });
103
103
  });
104
+ it("promotes tool image outputs into assistant attachments", ()=>{
105
+ const state = {
106
+ createdAt: 3000,
107
+ values: {
108
+ messages: [
109
+ {
110
+ role: "user",
111
+ content: "Create a puppy image."
112
+ },
113
+ {
114
+ role: "tool",
115
+ content: JSON.stringify({
116
+ content: [
117
+ {
118
+ type: "text",
119
+ text: "Generated image."
120
+ },
121
+ {
122
+ type: "resource_link",
123
+ uri: "/api/fs/file?path=%2Ftmp%2Fpuppy.png",
124
+ mimeType: "image/png"
125
+ }
126
+ ]
127
+ })
128
+ }
129
+ ]
130
+ }
131
+ };
132
+ const result = extractMessagesFromState(state);
133
+ expect(result).not.toBeNull();
134
+ expect(result).toHaveLength(2);
135
+ expect(result?.[1]).toMatchObject({
136
+ role: "assistant",
137
+ content: "Generated image.",
138
+ attachments: [
139
+ {
140
+ kind: "image",
141
+ dataUrl: "/api/fs/file?path=%2Ftmp%2Fpuppy.png"
142
+ }
143
+ ]
144
+ });
145
+ });
146
+ it("extracts tool attachments when MCP payload is wrapped in a text block", ()=>{
147
+ const state = {
148
+ createdAt: 4000,
149
+ values: {
150
+ messages: [
151
+ {
152
+ role: "user",
153
+ content: "Generate a landscape image."
154
+ },
155
+ {
156
+ role: "tool",
157
+ content: [
158
+ {
159
+ type: "text",
160
+ text: JSON.stringify({
161
+ content: [
162
+ {
163
+ type: "text",
164
+ text: "Image ready."
165
+ },
166
+ {
167
+ type: "resource_link",
168
+ uri: "/api/fs/file?path=%2Ftmp%2Flandscape.png",
169
+ mimeType: "image/png"
170
+ }
171
+ ]
172
+ })
173
+ }
174
+ ]
175
+ }
176
+ ]
177
+ }
178
+ };
179
+ const result = extractMessagesFromState(state);
180
+ expect(result).not.toBeNull();
181
+ expect(result).toHaveLength(2);
182
+ expect(result?.[1]).toMatchObject({
183
+ role: "assistant",
184
+ content: "Image ready.",
185
+ attachments: [
186
+ {
187
+ kind: "image",
188
+ dataUrl: "/api/fs/file?path=%2Ftmp%2Flandscape.png"
189
+ }
190
+ ]
191
+ });
192
+ });
193
+ it("extracts tool image attachments from artifact blocks", ()=>{
194
+ const state = {
195
+ createdAt: 5000,
196
+ values: {
197
+ messages: [
198
+ {
199
+ role: "user",
200
+ content: "Generate another image."
201
+ },
202
+ {
203
+ role: "tool",
204
+ content: "Generated image.",
205
+ artifact: [
206
+ {
207
+ type: "image",
208
+ mimeType: "image/png",
209
+ data: "abc123"
210
+ }
211
+ ]
212
+ }
213
+ ]
214
+ }
215
+ };
216
+ const result = extractMessagesFromState(state);
217
+ expect(result).not.toBeNull();
218
+ expect(result).toHaveLength(2);
219
+ expect(result?.[1]).toMatchObject({
220
+ role: "assistant",
221
+ content: "Generated image.",
222
+ attachments: [
223
+ {
224
+ kind: "image",
225
+ dataUrl: "data:image/png;base64,abc123"
226
+ }
227
+ ]
228
+ });
229
+ });
104
230
  });