@wingman-ai/gateway 0.4.2 → 0.4.3

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 (160) hide show
  1. package/README.md +14 -0
  2. package/dist/agent/config/mcpClientManager.cjs +104 -1
  3. package/dist/agent/config/mcpClientManager.d.ts +30 -0
  4. package/dist/agent/config/mcpClientManager.js +104 -1
  5. package/dist/agent/config/modelFactory.cjs +10 -0
  6. package/dist/agent/config/modelFactory.js +10 -0
  7. package/dist/agent/config/xaiImageModel.cjs +242 -0
  8. package/dist/agent/config/xaiImageModel.d.ts +33 -0
  9. package/dist/agent/config/xaiImageModel.js +202 -0
  10. package/dist/agent/tests/mcpClientManager.test.cjs +116 -0
  11. package/dist/agent/tests/mcpClientManager.test.js +117 -1
  12. package/dist/agent/tests/mcpResourceTools.test.cjs +101 -0
  13. package/dist/agent/tests/mcpResourceTools.test.d.ts +1 -0
  14. package/dist/agent/tests/mcpResourceTools.test.js +95 -0
  15. package/dist/agent/tests/modelFactory.test.cjs +16 -2
  16. package/dist/agent/tests/modelFactory.test.js +16 -2
  17. package/dist/agent/tests/xaiImageModel.test.cjs +194 -0
  18. package/dist/agent/tests/xaiImageModel.test.d.ts +1 -0
  19. package/dist/agent/tests/xaiImageModel.test.js +188 -0
  20. package/dist/agent/tools/mcp_resources.cjs +111 -0
  21. package/dist/agent/tools/mcp_resources.d.ts +3 -0
  22. package/dist/agent/tools/mcp_resources.js +77 -0
  23. package/dist/bench/adapters/commandAdapter.cjs +93 -0
  24. package/dist/bench/adapters/commandAdapter.d.ts +6 -0
  25. package/dist/bench/adapters/commandAdapter.js +59 -0
  26. package/dist/bench/adapters/helpers.cjs +170 -0
  27. package/dist/bench/adapters/helpers.d.ts +7 -0
  28. package/dist/bench/adapters/helpers.js +133 -0
  29. package/dist/bench/adapters/index.cjs +41 -0
  30. package/dist/bench/adapters/index.d.ts +2 -0
  31. package/dist/bench/adapters/index.js +7 -0
  32. package/dist/bench/adapters/wingmanCliAdapter.cjs +100 -0
  33. package/dist/bench/adapters/wingmanCliAdapter.d.ts +6 -0
  34. package/dist/bench/adapters/wingmanCliAdapter.js +66 -0
  35. package/dist/bench/cleanup.cjs +122 -0
  36. package/dist/bench/cleanup.d.ts +9 -0
  37. package/dist/bench/cleanup.js +85 -0
  38. package/dist/bench/config.cjs +190 -0
  39. package/dist/bench/config.d.ts +2 -0
  40. package/dist/bench/config.js +156 -0
  41. package/dist/bench/index.cjs +43 -0
  42. package/dist/bench/index.d.ts +3 -0
  43. package/dist/bench/index.js +3 -0
  44. package/dist/bench/official.cjs +616 -0
  45. package/dist/bench/official.d.ts +80 -0
  46. package/dist/bench/official.js +546 -0
  47. package/dist/bench/officialCli.cjs +204 -0
  48. package/dist/bench/officialCli.d.ts +5 -0
  49. package/dist/bench/officialCli.js +170 -0
  50. package/dist/bench/process.cjs +78 -0
  51. package/dist/bench/process.d.ts +14 -0
  52. package/dist/bench/process.js +44 -0
  53. package/dist/bench/runner.cjs +237 -0
  54. package/dist/bench/runner.d.ts +7 -0
  55. package/dist/bench/runner.js +197 -0
  56. package/dist/bench/scoring.cjs +171 -0
  57. package/dist/bench/scoring.d.ts +9 -0
  58. package/dist/bench/scoring.js +137 -0
  59. package/dist/bench/types.cjs +18 -0
  60. package/dist/bench/types.d.ts +200 -0
  61. package/dist/bench/types.js +0 -0
  62. package/dist/bench/validator.cjs +92 -0
  63. package/dist/bench/validator.d.ts +2 -0
  64. package/dist/bench/validator.js +58 -0
  65. package/dist/cli/config/schema.cjs +36 -1
  66. package/dist/cli/config/schema.d.ts +46 -0
  67. package/dist/cli/config/schema.js +36 -1
  68. package/dist/cli/config/warnings.cjs +119 -51
  69. package/dist/cli/config/warnings.js +119 -51
  70. package/dist/cli/core/agentInvoker.cjs +9 -2
  71. package/dist/cli/core/agentInvoker.d.ts +1 -0
  72. package/dist/cli/core/agentInvoker.js +9 -2
  73. package/dist/cli/core/imagePersistence.cjs +17 -1
  74. package/dist/cli/core/imagePersistence.d.ts +2 -0
  75. package/dist/cli/core/imagePersistence.js +13 -3
  76. package/dist/cli/core/sessionManager.cjs +2 -0
  77. package/dist/cli/core/sessionManager.js +3 -1
  78. package/dist/cli/types.d.ts +18 -0
  79. package/dist/gateway/adapters/teams.cjs +419 -0
  80. package/dist/gateway/adapters/teams.d.ts +47 -0
  81. package/dist/gateway/adapters/teams.js +361 -0
  82. package/dist/gateway/http/sms.cjs +286 -0
  83. package/dist/gateway/http/sms.d.ts +4 -0
  84. package/dist/gateway/http/sms.js +249 -0
  85. package/dist/gateway/server.cjs +54 -3
  86. package/dist/gateway/server.d.ts +2 -0
  87. package/dist/gateway/server.js +54 -3
  88. package/dist/gateway/sms/commands.cjs +116 -0
  89. package/dist/gateway/sms/commands.d.ts +15 -0
  90. package/dist/gateway/sms/commands.js +79 -0
  91. package/dist/gateway/sms/control.cjs +118 -0
  92. package/dist/gateway/sms/control.d.ts +18 -0
  93. package/dist/gateway/sms/control.js +84 -0
  94. package/dist/gateway/sms/policyStore.cjs +198 -0
  95. package/dist/gateway/sms/policyStore.d.ts +37 -0
  96. package/dist/gateway/sms/policyStore.js +161 -0
  97. package/dist/providers/registry.cjs +1 -0
  98. package/dist/providers/registry.js +1 -0
  99. package/dist/tests/cli-config-warnings.test.cjs +41 -0
  100. package/dist/tests/cli-config-warnings.test.js +41 -0
  101. package/dist/tests/cli-init.test.cjs +32 -26
  102. package/dist/tests/cli-init.test.js +32 -26
  103. package/dist/tests/gateway-http-security.test.cjs +21 -0
  104. package/dist/tests/gateway-http-security.test.js +21 -0
  105. package/dist/tests/gateway-origin-policy.test.cjs +22 -0
  106. package/dist/tests/gateway-origin-policy.test.js +22 -0
  107. package/dist/tests/gateway.test.cjs +57 -0
  108. package/dist/tests/gateway.test.js +57 -0
  109. package/dist/tests/imagePersistence.test.cjs +26 -0
  110. package/dist/tests/imagePersistence.test.js +27 -1
  111. package/dist/tests/run-terminal-bench-official-script.test.cjs +61 -0
  112. package/dist/tests/run-terminal-bench-official-script.test.d.ts +1 -0
  113. package/dist/tests/run-terminal-bench-official-script.test.js +55 -0
  114. package/dist/tests/sessions-api.test.cjs +69 -1
  115. package/dist/tests/sessions-api.test.js +70 -2
  116. package/dist/tests/sms-api.test.cjs +183 -0
  117. package/dist/tests/sms-api.test.d.ts +1 -0
  118. package/dist/tests/sms-api.test.js +177 -0
  119. package/dist/tests/sms-commands.test.cjs +90 -0
  120. package/dist/tests/sms-commands.test.d.ts +1 -0
  121. package/dist/tests/sms-commands.test.js +84 -0
  122. package/dist/tests/sms-policy-store.test.cjs +69 -0
  123. package/dist/tests/sms-policy-store.test.d.ts +1 -0
  124. package/dist/tests/sms-policy-store.test.js +63 -0
  125. package/dist/tests/teams-adapter.test.cjs +58 -0
  126. package/dist/tests/teams-adapter.test.d.ts +1 -0
  127. package/dist/tests/teams-adapter.test.js +52 -0
  128. package/dist/tests/terminal-bench-adapters-helpers.test.cjs +64 -0
  129. package/dist/tests/terminal-bench-adapters-helpers.test.d.ts +1 -0
  130. package/dist/tests/terminal-bench-adapters-helpers.test.js +58 -0
  131. package/dist/tests/terminal-bench-cleanup.test.cjs +93 -0
  132. package/dist/tests/terminal-bench-cleanup.test.d.ts +1 -0
  133. package/dist/tests/terminal-bench-cleanup.test.js +87 -0
  134. package/dist/tests/terminal-bench-config.test.cjs +62 -0
  135. package/dist/tests/terminal-bench-config.test.d.ts +1 -0
  136. package/dist/tests/terminal-bench-config.test.js +56 -0
  137. package/dist/tests/terminal-bench-official.test.cjs +194 -0
  138. package/dist/tests/terminal-bench-official.test.d.ts +1 -0
  139. package/dist/tests/terminal-bench-official.test.js +188 -0
  140. package/dist/tests/terminal-bench-runner.test.cjs +82 -0
  141. package/dist/tests/terminal-bench-runner.test.d.ts +1 -0
  142. package/dist/tests/terminal-bench-runner.test.js +76 -0
  143. package/dist/tests/terminal-bench-scoring.test.cjs +128 -0
  144. package/dist/tests/terminal-bench-scoring.test.d.ts +1 -0
  145. package/dist/tests/terminal-bench-scoring.test.js +122 -0
  146. package/dist/tools/mcp-fal-ai.cjs +1 -1
  147. package/dist/tools/mcp-fal-ai.js +1 -1
  148. package/dist/webui/assets/index-Cyg_Hs57.css +11 -0
  149. package/dist/webui/assets/{index-BMekSELC.js → index-DZXLLjaA.js} +109 -109
  150. package/dist/webui/index.html +2 -2
  151. package/package.json +11 -2
  152. package/templates/agents/game-dev/agent.md +122 -63
  153. package/templates/agents/game-dev/art-director.md +106 -0
  154. package/templates/agents/game-dev/game-designer.md +87 -0
  155. package/templates/agents/game-dev/scene-engineer.md +474 -0
  156. package/dist/webui/assets/index-Cwkg4DKj.css +0 -11
  157. package/templates/agents/game-dev/art-generation.md +0 -38
  158. package/templates/agents/game-dev/asset-refinement.md +0 -17
  159. package/templates/agents/game-dev/planning-idea.md +0 -17
  160. package/templates/agents/game-dev/ui-specialist.md +0 -17
@@ -20,6 +20,40 @@ vi.mock("@/cli/core/agentInvoker.js", ()=>({
20
20
  AgentInvoker: class {
21
21
  async invokeAgent(_agentId, content, _sessionId, _attachments, options) {
22
22
  if ("throw-no-event" === content) throw new Error("Synthetic invocation failure");
23
+ if ("stream-model-failure-no-error" === content) {
24
+ this.outputManager?.emitAgentStream?.({
25
+ event: "on_chain_end",
26
+ run_id: "model-failure-run-1",
27
+ name: "ChannelWrite<branch:to:todoListMiddleware.after_model>",
28
+ data: {
29
+ output: [
30
+ {
31
+ lg_name: "Command",
32
+ update: {
33
+ messages: [
34
+ {
35
+ type: "constructor",
36
+ id: [
37
+ "langchain_core",
38
+ "messages",
39
+ "AIMessage"
40
+ ],
41
+ kwargs: {
42
+ id: "model-failure-msg-1",
43
+ content: "Model call failed after 3 attempts with Error: xAI image generation failed: Prompt len is larger than the maximum allowed length which is 8000"
44
+ }
45
+ }
46
+ ]
47
+ }
48
+ }
49
+ ],
50
+ input: {}
51
+ }
52
+ });
53
+ return {
54
+ streaming: true
55
+ };
56
+ }
23
57
  const signal = options?.signal;
24
58
  await new Promise((resolve)=>{
25
59
  const timer = setTimeout(resolve, 75);
@@ -410,6 +444,29 @@ describeIfBun("Gateway", ()=>{
410
444
  });
411
445
  requester.close();
412
446
  });
447
+ it("does not auto-convert streamed model failure text into agent-error", async ()=>{
448
+ const requester = await connectClient("session-streamed-failure-requester");
449
+ const requestId = "req-streamed-failure-no-error";
450
+ const sessionId = "session-streamed-failure-no-error";
451
+ requester.send(JSON.stringify({
452
+ type: "req:agent",
453
+ id: requestId,
454
+ payload: {
455
+ agentId: "main",
456
+ sessionKey: sessionId,
457
+ content: "stream-model-failure-no-error"
458
+ },
459
+ timestamp: Date.now()
460
+ }));
461
+ const streamMsg = await waitForMessage(requester, (msg)=>"event:agent" === msg.type && msg.id === requestId && msg.payload?.type === "agent-stream" && msg.payload?.chunk?.event === "on_chain_end", 10000);
462
+ expect(JSON.stringify(streamMsg.payload?.chunk?.data || {})).toContain("Model call failed after 3 attempts");
463
+ const completeMsg = await waitForMessage(requester, (msg)=>"event:agent" === msg.type && msg.id === requestId && msg.payload?.type === "agent-complete", 10000);
464
+ expect(completeMsg.payload?.sessionId).toBe(sessionId);
465
+ expect(completeMsg.payload?.agentId).toBe("main");
466
+ const terminalErrorEvents = await collectMessages(requester, (msg)=>"event:agent" === msg.type && msg.id === requestId && msg.payload?.type === "agent-error", 400);
467
+ expect(terminalErrorEvents).toHaveLength(0);
468
+ requester.close();
469
+ });
413
470
  it("should emit a single agent-complete terminal event per request", async ()=>{
414
471
  const requester = await connectClient("session-single-complete-requester");
415
472
  const requestId = "req-single-complete";
@@ -128,6 +128,32 @@ const PNG_DATA_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQA
128
128
  (0, external_vitest_namespaceObject.expect)(firstPath).toBeTruthy();
129
129
  (0, external_vitest_namespaceObject.expect)(secondPath).toBe(firstPath);
130
130
  });
131
+ (0, external_vitest_namespaceObject.it)("removes persisted session media directory", ()=>{
132
+ const tempDir = (0, external_node_fs_namespaceObject.mkdtempSync)((0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.tmpdir)(), "wingman-image-store-"));
133
+ tempDirs.push(tempDir);
134
+ const dbPath = (0, external_node_path_namespaceObject.join)(tempDir, "wingman.db");
135
+ const sessionId = "session-cleanup";
136
+ const messages = [
137
+ {
138
+ role: "assistant",
139
+ attachments: [
140
+ {
141
+ kind: "image",
142
+ dataUrl: PNG_DATA_URL
143
+ }
144
+ ]
145
+ }
146
+ ];
147
+ (0, imagePersistence_cjs_namespaceObject.persistAssistantImagesToDisk)({
148
+ dbPath,
149
+ sessionId,
150
+ messages
151
+ });
152
+ const mediaDir = (0, imagePersistence_cjs_namespaceObject.getSessionMediaDirectory)(dbPath, sessionId);
153
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(mediaDir)).toBe(true);
154
+ (0, imagePersistence_cjs_namespaceObject.removeSessionMediaDirectory)(dbPath, sessionId);
155
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(mediaDir)).toBe(false);
156
+ });
131
157
  (0, external_vitest_namespaceObject.it)("parses base64 data URLs and resolves extensions", ()=>{
132
158
  const parsed = (0, imagePersistence_cjs_namespaceObject.parseBase64DataUrl)(PNG_DATA_URL);
133
159
  (0, external_vitest_namespaceObject.expect)(parsed?.mimeType).toBe("image/png");
@@ -2,7 +2,7 @@ import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { afterEach, describe, expect, it } from "vitest";
5
- import { parseBase64DataUrl, persistAssistantImagesToDisk, resolveImageExtension } from "../cli/core/imagePersistence.js";
5
+ import { getSessionMediaDirectory, parseBase64DataUrl, persistAssistantImagesToDisk, removeSessionMediaDirectory, resolveImageExtension } from "../cli/core/imagePersistence.js";
6
6
  const PNG_DATA_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO3X6S0AAAAASUVORK5CYII=";
7
7
  describe("imagePersistence", ()=>{
8
8
  const tempDirs = [];
@@ -126,6 +126,32 @@ describe("imagePersistence", ()=>{
126
126
  expect(firstPath).toBeTruthy();
127
127
  expect(secondPath).toBe(firstPath);
128
128
  });
129
+ it("removes persisted session media directory", ()=>{
130
+ const tempDir = mkdtempSync(join(tmpdir(), "wingman-image-store-"));
131
+ tempDirs.push(tempDir);
132
+ const dbPath = join(tempDir, "wingman.db");
133
+ const sessionId = "session-cleanup";
134
+ const messages = [
135
+ {
136
+ role: "assistant",
137
+ attachments: [
138
+ {
139
+ kind: "image",
140
+ dataUrl: PNG_DATA_URL
141
+ }
142
+ ]
143
+ }
144
+ ];
145
+ persistAssistantImagesToDisk({
146
+ dbPath,
147
+ sessionId,
148
+ messages
149
+ });
150
+ const mediaDir = getSessionMediaDirectory(dbPath, sessionId);
151
+ expect(existsSync(mediaDir)).toBe(true);
152
+ removeSessionMediaDirectory(dbPath, sessionId);
153
+ expect(existsSync(mediaDir)).toBe(false);
154
+ });
129
155
  it("parses base64 data URLs and resolves extensions", ()=>{
130
156
  const parsed = parseBase64DataUrl(PNG_DATA_URL);
131
157
  expect(parsed?.mimeType).toBe("image/png");
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __webpack_exports__ = {};
3
+ const external_node_path_namespaceObject = require("node:path");
4
+ const external_vitest_namespaceObject = require("vitest");
5
+ const officialCli_cjs_namespaceObject = require("../bench/officialCli.cjs");
6
+ (0, external_vitest_namespaceObject.describe)("run-terminal-bench-official cli args", ()=>{
7
+ (0, external_vitest_namespaceObject.it)("parses wingman bridge options", ()=>{
8
+ const parsed = (0, officialCli_cjs_namespaceObject.parseOfficialBenchArgs)([
9
+ "bun",
10
+ "run-terminal-bench-official.ts",
11
+ "--config",
12
+ "bench/config.tb2-wingman.json",
13
+ "--task-id",
14
+ "heterogeneous-dates",
15
+ "--n-attempts",
16
+ "3",
17
+ "--n-tasks",
18
+ "2",
19
+ "--wingman-agent",
20
+ "coding",
21
+ "--wingman-model",
22
+ "openai/gpt-5"
23
+ ]);
24
+ (0, external_vitest_namespaceObject.expect)(parsed.configPath).toBe((0, external_node_path_namespaceObject.join)(process.cwd(), "bench/config.tb2-wingman.json"));
25
+ (0, external_vitest_namespaceObject.expect)(parsed.overrides.taskNames).toEqual([
26
+ "heterogeneous-dates"
27
+ ]);
28
+ (0, external_vitest_namespaceObject.expect)(parsed.overrides.nAttempts).toBe(3);
29
+ (0, external_vitest_namespaceObject.expect)(parsed.overrides.nTasks).toBe(2);
30
+ (0, external_vitest_namespaceObject.expect)(parsed.overrides.agentKwargs).toEqual({
31
+ wingman_agent: "coding",
32
+ wingman_model: "openai:gpt-5"
33
+ });
34
+ });
35
+ (0, external_vitest_namespaceObject.it)("uses all dataset tasks when --all-tasks is set", ()=>{
36
+ const parsed = (0, officialCli_cjs_namespaceObject.parseOfficialBenchArgs)([
37
+ "bun",
38
+ "run-terminal-bench-official.ts",
39
+ "--task-name",
40
+ "heterogeneous-dates",
41
+ "--all-tasks"
42
+ ]);
43
+ (0, external_vitest_namespaceObject.expect)(parsed.overrides.taskNames).toEqual([]);
44
+ });
45
+ (0, external_vitest_namespaceObject.it)("parses explicit registry options", ()=>{
46
+ const parsed = (0, officialCli_cjs_namespaceObject.parseOfficialBenchArgs)([
47
+ "bun",
48
+ "run-terminal-bench-official.ts",
49
+ "--registry-url",
50
+ "https://example.com/registry.json",
51
+ "--registry-path",
52
+ "/tmp/registry.json"
53
+ ]);
54
+ (0, external_vitest_namespaceObject.expect)(parsed.overrides.registryUrl).toBe("https://example.com/registry.json");
55
+ (0, external_vitest_namespaceObject.expect)(parsed.overrides.registryPath).toBe("/tmp/registry.json");
56
+ });
57
+ });
58
+ for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
59
+ Object.defineProperty(exports, '__esModule', {
60
+ value: true
61
+ });
@@ -0,0 +1,55 @@
1
+ import { join } from "node:path";
2
+ import { describe, expect, it } from "vitest";
3
+ import { parseOfficialBenchArgs } from "../bench/officialCli.js";
4
+ describe("run-terminal-bench-official cli args", ()=>{
5
+ it("parses wingman bridge options", ()=>{
6
+ const parsed = parseOfficialBenchArgs([
7
+ "bun",
8
+ "run-terminal-bench-official.ts",
9
+ "--config",
10
+ "bench/config.tb2-wingman.json",
11
+ "--task-id",
12
+ "heterogeneous-dates",
13
+ "--n-attempts",
14
+ "3",
15
+ "--n-tasks",
16
+ "2",
17
+ "--wingman-agent",
18
+ "coding",
19
+ "--wingman-model",
20
+ "openai/gpt-5"
21
+ ]);
22
+ expect(parsed.configPath).toBe(join(process.cwd(), "bench/config.tb2-wingman.json"));
23
+ expect(parsed.overrides.taskNames).toEqual([
24
+ "heterogeneous-dates"
25
+ ]);
26
+ expect(parsed.overrides.nAttempts).toBe(3);
27
+ expect(parsed.overrides.nTasks).toBe(2);
28
+ expect(parsed.overrides.agentKwargs).toEqual({
29
+ wingman_agent: "coding",
30
+ wingman_model: "openai:gpt-5"
31
+ });
32
+ });
33
+ it("uses all dataset tasks when --all-tasks is set", ()=>{
34
+ const parsed = parseOfficialBenchArgs([
35
+ "bun",
36
+ "run-terminal-bench-official.ts",
37
+ "--task-name",
38
+ "heterogeneous-dates",
39
+ "--all-tasks"
40
+ ]);
41
+ expect(parsed.overrides.taskNames).toEqual([]);
42
+ });
43
+ it("parses explicit registry options", ()=>{
44
+ const parsed = parseOfficialBenchArgs([
45
+ "bun",
46
+ "run-terminal-bench-official.ts",
47
+ "--registry-url",
48
+ "https://example.com/registry.json",
49
+ "--registry-path",
50
+ "/tmp/registry.json"
51
+ ]);
52
+ expect(parsed.overrides.registryUrl).toBe("https://example.com/registry.json");
53
+ expect(parsed.overrides.registryPath).toBe("/tmp/registry.json");
54
+ });
55
+ });
@@ -4,6 +4,7 @@ const external_node_fs_namespaceObject = require("node:fs");
4
4
  const external_node_os_namespaceObject = require("node:os");
5
5
  const external_node_path_namespaceObject = require("node:path");
6
6
  const external_vitest_namespaceObject = require("vitest");
7
+ const imagePersistence_cjs_namespaceObject = require("../cli/core/imagePersistence.cjs");
7
8
  const sessionManager_cjs_namespaceObject = require("../cli/core/sessionManager.cjs");
8
9
  const sessions_cjs_namespaceObject = require("../gateway/http/sessions.cjs");
9
10
  const isBunRuntime = void 0 !== globalThis.Bun;
@@ -11,9 +12,11 @@ const describeIfBun = isBunRuntime ? external_vitest_namespaceObject.describe :
11
12
  describeIfBun("sessions API", ()=>{
12
13
  let manager;
13
14
  let tempDir;
15
+ let dbPath;
16
+ const PNG_DATA_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO3X6S0AAAAASUVORK5CYII=";
14
17
  (0, external_vitest_namespaceObject.beforeEach)(async ()=>{
15
18
  tempDir = (0, external_node_fs_namespaceObject.mkdtempSync)((0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.tmpdir)(), "wingman-sessions-"));
16
- const dbPath = (0, external_node_path_namespaceObject.join)(tempDir, "sessions.db");
19
+ dbPath = (0, external_node_path_namespaceObject.join)(tempDir, "sessions.db");
17
20
  manager = new sessionManager_cjs_namespaceObject.SessionManager(dbPath);
18
21
  await manager.initialize();
19
22
  });
@@ -45,6 +48,23 @@ describeIfBun("sessions API", ()=>{
45
48
  (0, external_vitest_namespaceObject.expect)(createRes).not.toBeNull();
46
49
  (0, external_vitest_namespaceObject.expect)(createRes?.ok).toBe(true);
47
50
  const created = await createRes.json();
51
+ const mediaDir = (0, imagePersistence_cjs_namespaceObject.getSessionMediaDirectory)(dbPath, created.id);
52
+ (0, imagePersistence_cjs_namespaceObject.persistAssistantImagesToDisk)({
53
+ dbPath,
54
+ sessionId: created.id,
55
+ messages: [
56
+ {
57
+ role: "assistant",
58
+ attachments: [
59
+ {
60
+ kind: "image",
61
+ dataUrl: PNG_DATA_URL
62
+ }
63
+ ]
64
+ }
65
+ ]
66
+ });
67
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(mediaDir)).toBe(true);
48
68
  manager.updateSession(created.id, {
49
69
  messageCount: 2,
50
70
  lastMessagePreview: "Hello"
@@ -57,10 +77,58 @@ describeIfBun("sessions API", ()=>{
57
77
  (0, external_vitest_namespaceObject.expect)(deleteRes?.ok).toBe(true);
58
78
  const payload = await deleteRes.json();
59
79
  (0, external_vitest_namespaceObject.expect)(payload.messageCount).toBe(0);
80
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(mediaDir)).toBe(false);
60
81
  const updated = manager.getSession(created.id);
61
82
  (0, external_vitest_namespaceObject.expect)(updated?.messageCount).toBe(0);
62
83
  (0, external_vitest_namespaceObject.expect)(updated?.lastMessagePreview).toBeNull();
63
84
  });
85
+ (0, external_vitest_namespaceObject.it)("removes persisted session media when deleting a session", async ()=>{
86
+ const ctx = {
87
+ getSessionManager: async ()=>manager,
88
+ router: {
89
+ selectAgent: (agentId)=>agentId || "main"
90
+ }
91
+ };
92
+ const createReq = new Request("http://localhost/api/sessions", {
93
+ method: "POST",
94
+ headers: {
95
+ "Content-Type": "application/json"
96
+ },
97
+ body: JSON.stringify({
98
+ agentId: "main",
99
+ name: "Delete Session Media"
100
+ })
101
+ });
102
+ const createRes = await (0, sessions_cjs_namespaceObject.handleSessionsApi)(ctx, createReq, new URL(createReq.url));
103
+ (0, external_vitest_namespaceObject.expect)(createRes).not.toBeNull();
104
+ (0, external_vitest_namespaceObject.expect)(createRes?.ok).toBe(true);
105
+ const created = await createRes.json();
106
+ const mediaDir = (0, imagePersistence_cjs_namespaceObject.getSessionMediaDirectory)(dbPath, created.id);
107
+ (0, imagePersistence_cjs_namespaceObject.persistAssistantImagesToDisk)({
108
+ dbPath,
109
+ sessionId: created.id,
110
+ messages: [
111
+ {
112
+ role: "assistant",
113
+ attachments: [
114
+ {
115
+ kind: "image",
116
+ dataUrl: PNG_DATA_URL
117
+ }
118
+ ]
119
+ }
120
+ ]
121
+ });
122
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(mediaDir)).toBe(true);
123
+ const deleteReq = new Request(`http://localhost/api/sessions/${encodeURIComponent(created.id)}?agentId=main`, {
124
+ method: "DELETE"
125
+ });
126
+ const deleteRes = await (0, sessions_cjs_namespaceObject.handleSessionsApi)(ctx, deleteReq, new URL(deleteReq.url));
127
+ (0, external_vitest_namespaceObject.expect)(deleteRes).not.toBeNull();
128
+ (0, external_vitest_namespaceObject.expect)(deleteRes?.ok).toBe(true);
129
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(mediaDir)).toBe(false);
130
+ (0, external_vitest_namespaceObject.expect)(manager.getSession(created.id)).toBeNull();
131
+ });
64
132
  (0, external_vitest_namespaceObject.it)("returns pending messages and clears them via DELETE", async ()=>{
65
133
  const ctx = {
66
134
  getSessionManager: async ()=>manager,
@@ -1,7 +1,8 @@
1
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+ import { getSessionMediaDirectory, persistAssistantImagesToDisk } from "../cli/core/imagePersistence.js";
5
6
  import { SessionManager } from "../cli/core/sessionManager.js";
6
7
  import { handleSessionsApi } from "../gateway/http/sessions.js";
7
8
  const isBunRuntime = void 0 !== globalThis.Bun;
@@ -9,9 +10,11 @@ const describeIfBun = isBunRuntime ? describe : describe.skip;
9
10
  describeIfBun("sessions API", ()=>{
10
11
  let manager;
11
12
  let tempDir;
13
+ let dbPath;
14
+ const PNG_DATA_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO3X6S0AAAAASUVORK5CYII=";
12
15
  beforeEach(async ()=>{
13
16
  tempDir = mkdtempSync(join(tmpdir(), "wingman-sessions-"));
14
- const dbPath = join(tempDir, "sessions.db");
17
+ dbPath = join(tempDir, "sessions.db");
15
18
  manager = new SessionManager(dbPath);
16
19
  await manager.initialize();
17
20
  });
@@ -43,6 +46,23 @@ describeIfBun("sessions API", ()=>{
43
46
  expect(createRes).not.toBeNull();
44
47
  expect(createRes?.ok).toBe(true);
45
48
  const created = await createRes.json();
49
+ const mediaDir = getSessionMediaDirectory(dbPath, created.id);
50
+ persistAssistantImagesToDisk({
51
+ dbPath,
52
+ sessionId: created.id,
53
+ messages: [
54
+ {
55
+ role: "assistant",
56
+ attachments: [
57
+ {
58
+ kind: "image",
59
+ dataUrl: PNG_DATA_URL
60
+ }
61
+ ]
62
+ }
63
+ ]
64
+ });
65
+ expect(existsSync(mediaDir)).toBe(true);
46
66
  manager.updateSession(created.id, {
47
67
  messageCount: 2,
48
68
  lastMessagePreview: "Hello"
@@ -55,10 +75,58 @@ describeIfBun("sessions API", ()=>{
55
75
  expect(deleteRes?.ok).toBe(true);
56
76
  const payload = await deleteRes.json();
57
77
  expect(payload.messageCount).toBe(0);
78
+ expect(existsSync(mediaDir)).toBe(false);
58
79
  const updated = manager.getSession(created.id);
59
80
  expect(updated?.messageCount).toBe(0);
60
81
  expect(updated?.lastMessagePreview).toBeNull();
61
82
  });
83
+ it("removes persisted session media when deleting a session", async ()=>{
84
+ const ctx = {
85
+ getSessionManager: async ()=>manager,
86
+ router: {
87
+ selectAgent: (agentId)=>agentId || "main"
88
+ }
89
+ };
90
+ const createReq = new Request("http://localhost/api/sessions", {
91
+ method: "POST",
92
+ headers: {
93
+ "Content-Type": "application/json"
94
+ },
95
+ body: JSON.stringify({
96
+ agentId: "main",
97
+ name: "Delete Session Media"
98
+ })
99
+ });
100
+ const createRes = await handleSessionsApi(ctx, createReq, new URL(createReq.url));
101
+ expect(createRes).not.toBeNull();
102
+ expect(createRes?.ok).toBe(true);
103
+ const created = await createRes.json();
104
+ const mediaDir = getSessionMediaDirectory(dbPath, created.id);
105
+ persistAssistantImagesToDisk({
106
+ dbPath,
107
+ sessionId: created.id,
108
+ messages: [
109
+ {
110
+ role: "assistant",
111
+ attachments: [
112
+ {
113
+ kind: "image",
114
+ dataUrl: PNG_DATA_URL
115
+ }
116
+ ]
117
+ }
118
+ ]
119
+ });
120
+ expect(existsSync(mediaDir)).toBe(true);
121
+ const deleteReq = new Request(`http://localhost/api/sessions/${encodeURIComponent(created.id)}?agentId=main`, {
122
+ method: "DELETE"
123
+ });
124
+ const deleteRes = await handleSessionsApi(ctx, deleteReq, new URL(deleteReq.url));
125
+ expect(deleteRes).not.toBeNull();
126
+ expect(deleteRes?.ok).toBe(true);
127
+ expect(existsSync(mediaDir)).toBe(false);
128
+ expect(manager.getSession(created.id)).toBeNull();
129
+ });
62
130
  it("returns pending messages and clears them via DELETE", async ()=>{
63
131
  const ctx = {
64
132
  getSessionManager: async ()=>manager,
@@ -0,0 +1,183 @@
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 sms_cjs_namespaceObject = require("../gateway/http/sms.cjs");
8
+ const requireTempDir = (value)=>{
9
+ if (!value) throw new Error("temp dir not initialized");
10
+ return value;
11
+ };
12
+ (0, external_vitest_namespaceObject.describe)("sms policy api", ()=>{
13
+ let tempDir = null;
14
+ (0, external_vitest_namespaceObject.afterEach)(()=>{
15
+ if (!tempDir) return;
16
+ (0, external_node_fs_namespaceObject.rmSync)(tempDir, {
17
+ recursive: true,
18
+ force: true
19
+ });
20
+ tempDir = null;
21
+ });
22
+ (0, external_vitest_namespaceObject.it)("updates, fetches, and resets policy records", async ()=>{
23
+ tempDir = (0, external_node_fs_namespaceObject.mkdtempSync)((0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.tmpdir)(), "wingman-sms-api-"));
24
+ const store = (0, sms_cjs_namespaceObject.createSmsPolicyStateStore)(()=>requireTempDir(tempDir));
25
+ const target = encodeURIComponent("sms-macos:+15555550000");
26
+ const ctx = {};
27
+ const updateReq = new Request(`http://localhost/api/sms/policies/${target}`, {
28
+ method: "PUT",
29
+ headers: {
30
+ "Content-Type": "application/json"
31
+ },
32
+ body: JSON.stringify({
33
+ stopEnabled: true,
34
+ pauseForMs: 120000,
35
+ alertMode: "all"
36
+ })
37
+ });
38
+ const updateRes = await (0, sms_cjs_namespaceObject.handleSmsApi)(ctx, store, updateReq, new URL(updateReq.url));
39
+ (0, external_vitest_namespaceObject.expect)(updateRes?.status).toBe(200);
40
+ const updated = await updateRes?.json();
41
+ (0, external_vitest_namespaceObject.expect)(updated.paused).toBe(true);
42
+ (0, external_vitest_namespaceObject.expect)(updated.pausedUntil).toBeTypeOf("number");
43
+ (0, external_vitest_namespaceObject.expect)(updated.stopEnabled).toBe(true);
44
+ (0, external_vitest_namespaceObject.expect)(updated.alertMode).toBe("all");
45
+ const getRes = await (0, sms_cjs_namespaceObject.handleSmsApi)(ctx, store, new Request(`http://localhost/api/sms/policies/${target}`, {
46
+ method: "GET"
47
+ }), new URL(`http://localhost/api/sms/policies/${target}`));
48
+ (0, external_vitest_namespaceObject.expect)(getRes?.status).toBe(200);
49
+ const listRes = await (0, sms_cjs_namespaceObject.handleSmsApi)(ctx, store, new Request("http://localhost/api/sms/policies", {
50
+ method: "GET"
51
+ }), new URL("http://localhost/api/sms/policies"));
52
+ (0, external_vitest_namespaceObject.expect)(listRes?.status).toBe(200);
53
+ const listPayload = await listRes?.json();
54
+ (0, external_vitest_namespaceObject.expect)(listPayload.policies).toHaveLength(1);
55
+ const deleteRes = await (0, sms_cjs_namespaceObject.handleSmsApi)(ctx, store, new Request(`http://localhost/api/sms/policies/${target}`, {
56
+ method: "DELETE"
57
+ }), new URL(`http://localhost/api/sms/policies/${target}`));
58
+ (0, external_vitest_namespaceObject.expect)(deleteRes?.status).toBe(200);
59
+ const reset = await deleteRes?.json();
60
+ (0, external_vitest_namespaceObject.expect)(reset.paused).toBe(false);
61
+ (0, external_vitest_namespaceObject.expect)(reset.stopEnabled).toBe(false);
62
+ });
63
+ (0, external_vitest_namespaceObject.it)("applies control commands and leaves non-commands as pass-through text", async ()=>{
64
+ tempDir = (0, external_node_fs_namespaceObject.mkdtempSync)((0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.tmpdir)(), "wingman-sms-api-"));
65
+ const store = (0, sms_cjs_namespaceObject.createSmsPolicyStateStore)(()=>requireTempDir(tempDir));
66
+ const target = encodeURIComponent("sms-macos:+15555550000");
67
+ const ctx = {
68
+ router: {
69
+ selectAgent: ()=>"main",
70
+ buildSessionKey: ()=>"agent:main:sms-macos:dm:+15555550000"
71
+ }
72
+ };
73
+ const pauseReq = new Request(`http://localhost/api/sms/policies/${target}/command`, {
74
+ method: "POST",
75
+ headers: {
76
+ "Content-Type": "application/json"
77
+ },
78
+ body: JSON.stringify({
79
+ text: "PAUSE 2h",
80
+ nowMs: 10000
81
+ })
82
+ });
83
+ const pauseRes = await (0, sms_cjs_namespaceObject.handleSmsApi)(ctx, store, pauseReq, new URL(pauseReq.url));
84
+ (0, external_vitest_namespaceObject.expect)(pauseRes?.status).toBe(200);
85
+ const pausedPayload = await pauseRes?.json();
86
+ (0, external_vitest_namespaceObject.expect)(pausedPayload.handled).toBe(true);
87
+ (0, external_vitest_namespaceObject.expect)(pausedPayload.command?.name).toBe("pause");
88
+ (0, external_vitest_namespaceObject.expect)(pausedPayload.policy.paused).toBe(true);
89
+ const textReq = new Request(`http://localhost/api/sms/policies/${target}/command`, {
90
+ method: "POST",
91
+ headers: {
92
+ "Content-Type": "application/json"
93
+ },
94
+ body: JSON.stringify({
95
+ text: "deploy this now"
96
+ })
97
+ });
98
+ const textRes = await (0, sms_cjs_namespaceObject.handleSmsApi)(ctx, store, textReq, new URL(textReq.url));
99
+ (0, external_vitest_namespaceObject.expect)(textRes?.status).toBe(200);
100
+ const textPayload = await textRes?.json();
101
+ (0, external_vitest_namespaceObject.expect)(textPayload.handled).toBe(false);
102
+ (0, external_vitest_namespaceObject.expect)(textPayload.passThroughText).toBe("deploy this now");
103
+ });
104
+ (0, external_vitest_namespaceObject.it)("normalizes inbound messages into command, stopped, or agent results", async ()=>{
105
+ tempDir = (0, external_node_fs_namespaceObject.mkdtempSync)((0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.tmpdir)(), "wingman-sms-api-"));
106
+ const store = (0, sms_cjs_namespaceObject.createSmsPolicyStateStore)(()=>requireTempDir(tempDir));
107
+ const ctx = {
108
+ router: {
109
+ selectAgent: ()=>"main",
110
+ buildSessionKey: ()=>"agent:main:sms-macos:dm:+15555550000"
111
+ }
112
+ };
113
+ const commandReq = new Request("http://localhost/api/sms/messages", {
114
+ method: "POST",
115
+ headers: {
116
+ "Content-Type": "application/json"
117
+ },
118
+ body: JSON.stringify({
119
+ target: "sms-macos:+15555550000",
120
+ text: "HELP"
121
+ })
122
+ });
123
+ const commandRes = await (0, sms_cjs_namespaceObject.handleSmsApi)(ctx, store, commandReq, new URL(commandReq.url));
124
+ (0, external_vitest_namespaceObject.expect)(commandRes?.status).toBe(200);
125
+ const commandPayload = await commandRes?.json();
126
+ (0, external_vitest_namespaceObject.expect)(commandPayload.kind).toBe("command");
127
+ (0, external_vitest_namespaceObject.expect)(commandPayload.handled).toBe(true);
128
+ const stopReq = new Request("http://localhost/api/sms/messages", {
129
+ method: "POST",
130
+ headers: {
131
+ "Content-Type": "application/json"
132
+ },
133
+ body: JSON.stringify({
134
+ target: "sms-macos:+15555550000",
135
+ text: "STOP"
136
+ })
137
+ });
138
+ const stopRes = await (0, sms_cjs_namespaceObject.handleSmsApi)(ctx, store, stopReq, new URL(stopReq.url));
139
+ (0, external_vitest_namespaceObject.expect)(stopRes?.status).toBe(200);
140
+ const blockedReq = new Request("http://localhost/api/sms/messages", {
141
+ method: "POST",
142
+ headers: {
143
+ "Content-Type": "application/json"
144
+ },
145
+ body: JSON.stringify({
146
+ target: "sms-macos:+15555550000",
147
+ text: "summarize my build failures"
148
+ })
149
+ });
150
+ const blockedRes = await (0, sms_cjs_namespaceObject.handleSmsApi)(ctx, store, blockedReq, new URL(blockedReq.url));
151
+ (0, external_vitest_namespaceObject.expect)(blockedRes?.status).toBe(200);
152
+ const blockedPayload = await blockedRes?.json();
153
+ (0, external_vitest_namespaceObject.expect)(blockedPayload.kind).toBe("stopped");
154
+ (0, external_vitest_namespaceObject.expect)(blockedPayload.responseText).toContain("stopped");
155
+ store.upsert("sms-macos:+15555550000", {
156
+ stopEnabled: false
157
+ });
158
+ const agentReq = new Request("http://localhost/api/sms/messages", {
159
+ method: "POST",
160
+ headers: {
161
+ "Content-Type": "application/json"
162
+ },
163
+ body: JSON.stringify({
164
+ target: "sms-macos:+15555550000",
165
+ text: "summarize my build failures",
166
+ queueIfBusy: true
167
+ })
168
+ });
169
+ const agentRes = await (0, sms_cjs_namespaceObject.handleSmsApi)(ctx, store, agentReq, new URL(agentReq.url));
170
+ (0, external_vitest_namespaceObject.expect)(agentRes?.status).toBe(200);
171
+ const agentPayload = await agentRes?.json();
172
+ (0, external_vitest_namespaceObject.expect)(agentPayload.kind).toBe("agent");
173
+ (0, external_vitest_namespaceObject.expect)(agentPayload.request.agentId).toBe("main");
174
+ (0, external_vitest_namespaceObject.expect)(agentPayload.request.content).toBe("summarize my build failures");
175
+ (0, external_vitest_namespaceObject.expect)(agentPayload.request.sessionKey).toBe("agent:main:sms-macos:dm:+15555550000");
176
+ (0, external_vitest_namespaceObject.expect)(agentPayload.request.routing?.channel).toBe("sms-macos");
177
+ (0, external_vitest_namespaceObject.expect)(agentPayload.request.queueIfBusy).toBe(true);
178
+ });
179
+ });
180
+ for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
181
+ Object.defineProperty(exports, '__esModule', {
182
+ value: true
183
+ });
@@ -0,0 +1 @@
1
+ export {};