@wingman-ai/gateway 0.4.1 → 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 (200) 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/commands/init.cjs +135 -1
  66. package/dist/cli/commands/init.js +136 -2
  67. package/dist/cli/commands/skill.cjs +7 -3
  68. package/dist/cli/commands/skill.js +7 -3
  69. package/dist/cli/config/loader.cjs +7 -3
  70. package/dist/cli/config/loader.js +7 -3
  71. package/dist/cli/config/schema.cjs +63 -10
  72. package/dist/cli/config/schema.d.ts +64 -4
  73. package/dist/cli/config/schema.js +59 -9
  74. package/dist/cli/config/warnings.cjs +119 -51
  75. package/dist/cli/config/warnings.js +119 -51
  76. package/dist/cli/core/agentInvoker.cjs +58 -13
  77. package/dist/cli/core/agentInvoker.d.ts +1 -0
  78. package/dist/cli/core/agentInvoker.js +58 -13
  79. package/dist/cli/core/imagePersistence.cjs +17 -1
  80. package/dist/cli/core/imagePersistence.d.ts +2 -0
  81. package/dist/cli/core/imagePersistence.js +13 -3
  82. package/dist/cli/core/sessionManager.cjs +2 -0
  83. package/dist/cli/core/sessionManager.js +3 -1
  84. package/dist/cli/services/skillRepository.cjs +155 -69
  85. package/dist/cli/services/skillRepository.d.ts +7 -2
  86. package/dist/cli/services/skillRepository.js +155 -69
  87. package/dist/cli/services/skillService.cjs +93 -26
  88. package/dist/cli/services/skillService.d.ts +7 -0
  89. package/dist/cli/services/skillService.js +96 -29
  90. package/dist/cli/types/skill.d.ts +8 -3
  91. package/dist/cli/types.d.ts +18 -0
  92. package/dist/gateway/adapters/teams.cjs +419 -0
  93. package/dist/gateway/adapters/teams.d.ts +47 -0
  94. package/dist/gateway/adapters/teams.js +361 -0
  95. package/dist/gateway/http/sms.cjs +286 -0
  96. package/dist/gateway/http/sms.d.ts +4 -0
  97. package/dist/gateway/http/sms.js +249 -0
  98. package/dist/gateway/server.cjs +54 -3
  99. package/dist/gateway/server.d.ts +2 -0
  100. package/dist/gateway/server.js +54 -3
  101. package/dist/gateway/sms/commands.cjs +116 -0
  102. package/dist/gateway/sms/commands.d.ts +15 -0
  103. package/dist/gateway/sms/commands.js +79 -0
  104. package/dist/gateway/sms/control.cjs +118 -0
  105. package/dist/gateway/sms/control.d.ts +18 -0
  106. package/dist/gateway/sms/control.js +84 -0
  107. package/dist/gateway/sms/policyStore.cjs +198 -0
  108. package/dist/gateway/sms/policyStore.d.ts +37 -0
  109. package/dist/gateway/sms/policyStore.js +161 -0
  110. package/dist/providers/registry.cjs +1 -0
  111. package/dist/providers/registry.js +1 -0
  112. package/dist/skills/activation.cjs +92 -0
  113. package/dist/skills/activation.d.ts +12 -0
  114. package/dist/skills/activation.js +58 -0
  115. package/dist/skills/bin-requirements.cjs +63 -0
  116. package/dist/skills/bin-requirements.d.ts +3 -0
  117. package/dist/skills/bin-requirements.js +26 -0
  118. package/dist/skills/metadata.cjs +141 -0
  119. package/dist/skills/metadata.d.ts +29 -0
  120. package/dist/skills/metadata.js +104 -0
  121. package/dist/skills/overlay.cjs +75 -0
  122. package/dist/skills/overlay.d.ts +2 -0
  123. package/dist/skills/overlay.js +38 -0
  124. package/dist/tests/cli-config-loader.test.cjs +7 -3
  125. package/dist/tests/cli-config-loader.test.js +7 -3
  126. package/dist/tests/cli-config-warnings.test.cjs +41 -0
  127. package/dist/tests/cli-config-warnings.test.js +41 -0
  128. package/dist/tests/cli-init.test.cjs +86 -26
  129. package/dist/tests/cli-init.test.js +86 -26
  130. package/dist/tests/config-json-schema.test.cjs +12 -0
  131. package/dist/tests/config-json-schema.test.js +12 -0
  132. package/dist/tests/gateway-http-security.test.cjs +21 -0
  133. package/dist/tests/gateway-http-security.test.js +21 -0
  134. package/dist/tests/gateway-origin-policy.test.cjs +22 -0
  135. package/dist/tests/gateway-origin-policy.test.js +22 -0
  136. package/dist/tests/gateway.test.cjs +57 -0
  137. package/dist/tests/gateway.test.js +57 -0
  138. package/dist/tests/imagePersistence.test.cjs +26 -0
  139. package/dist/tests/imagePersistence.test.js +27 -1
  140. package/dist/tests/run-terminal-bench-official-script.test.cjs +61 -0
  141. package/dist/tests/run-terminal-bench-official-script.test.d.ts +1 -0
  142. package/dist/tests/run-terminal-bench-official-script.test.js +55 -0
  143. package/dist/tests/sessions-api.test.cjs +69 -1
  144. package/dist/tests/sessions-api.test.js +70 -2
  145. package/dist/tests/skill-activation.test.cjs +86 -0
  146. package/dist/tests/skill-activation.test.d.ts +1 -0
  147. package/dist/tests/skill-activation.test.js +80 -0
  148. package/dist/tests/skill-metadata.test.cjs +119 -0
  149. package/dist/tests/skill-metadata.test.d.ts +1 -0
  150. package/dist/tests/skill-metadata.test.js +113 -0
  151. package/dist/tests/skill-repository.test.cjs +363 -0
  152. package/dist/tests/skill-repository.test.js +363 -0
  153. package/dist/tests/sms-api.test.cjs +183 -0
  154. package/dist/tests/sms-api.test.d.ts +1 -0
  155. package/dist/tests/sms-api.test.js +177 -0
  156. package/dist/tests/sms-commands.test.cjs +90 -0
  157. package/dist/tests/sms-commands.test.d.ts +1 -0
  158. package/dist/tests/sms-commands.test.js +84 -0
  159. package/dist/tests/sms-policy-store.test.cjs +69 -0
  160. package/dist/tests/sms-policy-store.test.d.ts +1 -0
  161. package/dist/tests/sms-policy-store.test.js +63 -0
  162. package/dist/tests/teams-adapter.test.cjs +58 -0
  163. package/dist/tests/teams-adapter.test.d.ts +1 -0
  164. package/dist/tests/teams-adapter.test.js +52 -0
  165. package/dist/tests/terminal-bench-adapters-helpers.test.cjs +64 -0
  166. package/dist/tests/terminal-bench-adapters-helpers.test.d.ts +1 -0
  167. package/dist/tests/terminal-bench-adapters-helpers.test.js +58 -0
  168. package/dist/tests/terminal-bench-cleanup.test.cjs +93 -0
  169. package/dist/tests/terminal-bench-cleanup.test.d.ts +1 -0
  170. package/dist/tests/terminal-bench-cleanup.test.js +87 -0
  171. package/dist/tests/terminal-bench-config.test.cjs +62 -0
  172. package/dist/tests/terminal-bench-config.test.d.ts +1 -0
  173. package/dist/tests/terminal-bench-config.test.js +56 -0
  174. package/dist/tests/terminal-bench-official.test.cjs +194 -0
  175. package/dist/tests/terminal-bench-official.test.d.ts +1 -0
  176. package/dist/tests/terminal-bench-official.test.js +188 -0
  177. package/dist/tests/terminal-bench-runner.test.cjs +82 -0
  178. package/dist/tests/terminal-bench-runner.test.d.ts +1 -0
  179. package/dist/tests/terminal-bench-runner.test.js +76 -0
  180. package/dist/tests/terminal-bench-scoring.test.cjs +128 -0
  181. package/dist/tests/terminal-bench-scoring.test.d.ts +1 -0
  182. package/dist/tests/terminal-bench-scoring.test.js +122 -0
  183. package/dist/tools/mcp-fal-ai.cjs +1 -1
  184. package/dist/tools/mcp-fal-ai.js +1 -1
  185. package/dist/webui/assets/index-Cyg_Hs57.css +11 -0
  186. package/dist/webui/assets/{index-BMekSELC.js → index-DZXLLjaA.js} +109 -109
  187. package/dist/webui/index.html +2 -2
  188. package/package.json +14 -5
  189. package/skills/gog/SKILL.md +1 -1
  190. package/skills/weather/SKILL.md +1 -1
  191. package/templates/agents/game-dev/agent.md +122 -63
  192. package/templates/agents/game-dev/art-director.md +106 -0
  193. package/templates/agents/game-dev/game-designer.md +87 -0
  194. package/templates/agents/game-dev/scene-engineer.md +474 -0
  195. package/dist/webui/assets/index-Cwkg4DKj.css +0 -11
  196. package/skills/ui-registry/SKILL.md +0 -35
  197. package/templates/agents/game-dev/art-generation.md +0 -38
  198. package/templates/agents/game-dev/asset-refinement.md +0 -17
  199. package/templates/agents/game-dev/planning-idea.md +0 -17
  200. package/templates/agents/game-dev/ui-specialist.md +0 -17
@@ -18,6 +18,18 @@ const jsonSchema_cjs_namespaceObject = require("../cli/config/jsonSchema.cjs");
18
18
  const scannerArgsDefault = schema.properties.skills.properties.security.properties.scannerArgs.default;
19
19
  (0, external_vitest_namespaceObject.expect)(scannerArgsDefault).toContain("mcp-scan>=0.4,<0.5");
20
20
  });
21
+ (0, external_vitest_namespaceObject.it)("exposes hybrid defaults for skill sources", ()=>{
22
+ const schema = (0, jsonSchema_cjs_namespaceObject.buildWingmanConfigJsonSchema)();
23
+ const repositoriesDefault = schema.properties.skills.properties.repositories.default;
24
+ const providerDefault = schema.properties.skills.properties.provider.default;
25
+ (0, external_vitest_namespaceObject.expect)(providerDefault).toBe("hybrid");
26
+ (0, external_vitest_namespaceObject.expect)(repositoriesDefault).toEqual([
27
+ {
28
+ owner: "RussellCanfield",
29
+ name: "wingman-ai"
30
+ }
31
+ ]);
32
+ });
21
33
  });
22
34
  for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
23
35
  Object.defineProperty(exports, '__esModule', {
@@ -16,4 +16,16 @@ describe("wingman config json schema", ()=>{
16
16
  const scannerArgsDefault = schema.properties.skills.properties.security.properties.scannerArgs.default;
17
17
  expect(scannerArgsDefault).toContain("mcp-scan>=0.4,<0.5");
18
18
  });
19
+ it("exposes hybrid defaults for skill sources", ()=>{
20
+ const schema = buildWingmanConfigJsonSchema();
21
+ const repositoriesDefault = schema.properties.skills.properties.repositories.default;
22
+ const providerDefault = schema.properties.skills.properties.provider.default;
23
+ expect(providerDefault).toBe("hybrid");
24
+ expect(repositoriesDefault).toEqual([
25
+ {
26
+ owner: "RussellCanfield",
27
+ name: "wingman-ai"
28
+ }
29
+ ]);
30
+ });
19
31
  });
@@ -93,6 +93,27 @@ function createTestSocket(initialData) {
93
93
  (0, external_vitest_namespaceObject.expect)(allowed.status).toBe(204);
94
94
  (0, external_vitest_namespaceObject.expect)(allowed.headers.get("access-control-allow-origin")).toBe("http://localhost:5173");
95
95
  });
96
+ (0, external_vitest_namespaceObject.it)("allows tauri loopback origin preflight for desktop clients", async ()=>{
97
+ const server = createGateway({
98
+ host: "127.0.0.1",
99
+ port: 18789,
100
+ auth: {
101
+ mode: "token",
102
+ token: "test-token"
103
+ },
104
+ requireAuth: true
105
+ });
106
+ const internals = getGatewayInternals(server);
107
+ const allowed = await internals.handleUiRequest(new Request("http://127.0.0.1:18789/api/providers", {
108
+ method: "OPTIONS",
109
+ headers: {
110
+ Origin: "tauri://localhost",
111
+ "Access-Control-Request-Method": "GET"
112
+ }
113
+ }));
114
+ (0, external_vitest_namespaceObject.expect)(allowed.status).toBe(204);
115
+ (0, external_vitest_namespaceObject.expect)(allowed.headers.get("access-control-allow-origin")).toBe("tauri://localhost");
116
+ });
96
117
  (0, external_vitest_namespaceObject.it)("does not trust tailscale identity headers on non-loopback hosts", ()=>{
97
118
  const server = createGateway({
98
119
  host: "0.0.0.0",
@@ -91,6 +91,27 @@ describe("gateway HTTP security", ()=>{
91
91
  expect(allowed.status).toBe(204);
92
92
  expect(allowed.headers.get("access-control-allow-origin")).toBe("http://localhost:5173");
93
93
  });
94
+ it("allows tauri loopback origin preflight for desktop clients", async ()=>{
95
+ const server = createGateway({
96
+ host: "127.0.0.1",
97
+ port: 18789,
98
+ auth: {
99
+ mode: "token",
100
+ token: "test-token"
101
+ },
102
+ requireAuth: true
103
+ });
104
+ const internals = getGatewayInternals(server);
105
+ const allowed = await internals.handleUiRequest(new Request("http://127.0.0.1:18789/api/providers", {
106
+ method: "OPTIONS",
107
+ headers: {
108
+ Origin: "tauri://localhost",
109
+ "Access-Control-Request-Method": "GET"
110
+ }
111
+ }));
112
+ expect(allowed.status).toBe(204);
113
+ expect(allowed.headers.get("access-control-allow-origin")).toBe("tauri://localhost");
114
+ });
94
115
  it("does not trust tailscale identity headers on non-loopback hosts", ()=>{
95
116
  const server = createGateway({
96
117
  host: "0.0.0.0",
@@ -20,6 +20,17 @@ const server_cjs_namespaceObject = require("../gateway/server.cjs");
20
20
  });
21
21
  (0, external_vitest_namespaceObject.expect)(allowed).toBe(true);
22
22
  });
23
+ (0, external_vitest_namespaceObject.it)("allows tauri loopback origins for desktop clients", ()=>{
24
+ const allowed = (0, server_cjs_namespaceObject.isGatewayOriginAllowed)({
25
+ origin: "tauri://localhost",
26
+ requestUrl: "http://127.0.0.1:18789/api/sessions",
27
+ gatewayHost: "127.0.0.1",
28
+ gatewayPort: 18789,
29
+ controlUiEnabled: true,
30
+ controlUiPort: 18790
31
+ });
32
+ (0, external_vitest_namespaceObject.expect)(allowed).toBe(true);
33
+ });
23
34
  (0, external_vitest_namespaceObject.it)("rejects unrelated internet origins", ()=>{
24
35
  const allowed = (0, server_cjs_namespaceObject.isGatewayOriginAllowed)({
25
36
  origin: "https://evil.example",
@@ -53,6 +64,17 @@ const server_cjs_namespaceObject = require("../gateway/server.cjs");
53
64
  });
54
65
  (0, external_vitest_namespaceObject.expect)(allowed).toBe(false);
55
66
  });
67
+ (0, external_vitest_namespaceObject.it)("rejects tauri origins for non-loopback gateways", ()=>{
68
+ const allowed = (0, server_cjs_namespaceObject.isGatewayOriginAllowed)({
69
+ origin: "tauri://localhost",
70
+ requestUrl: "http://192.168.1.50:18789/api/sessions",
71
+ gatewayHost: "0.0.0.0",
72
+ gatewayPort: 18789,
73
+ controlUiEnabled: true,
74
+ controlUiPort: 18790
75
+ });
76
+ (0, external_vitest_namespaceObject.expect)(allowed).toBe(false);
77
+ });
56
78
  });
57
79
  for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
58
80
  Object.defineProperty(exports, '__esModule', {
@@ -18,6 +18,17 @@ describe("gateway origin policy", ()=>{
18
18
  });
19
19
  expect(allowed).toBe(true);
20
20
  });
21
+ it("allows tauri loopback origins for desktop clients", ()=>{
22
+ const allowed = isGatewayOriginAllowed({
23
+ origin: "tauri://localhost",
24
+ requestUrl: "http://127.0.0.1:18789/api/sessions",
25
+ gatewayHost: "127.0.0.1",
26
+ gatewayPort: 18789,
27
+ controlUiEnabled: true,
28
+ controlUiPort: 18790
29
+ });
30
+ expect(allowed).toBe(true);
31
+ });
21
32
  it("rejects unrelated internet origins", ()=>{
22
33
  const allowed = isGatewayOriginAllowed({
23
34
  origin: "https://evil.example",
@@ -51,4 +62,15 @@ describe("gateway origin policy", ()=>{
51
62
  });
52
63
  expect(allowed).toBe(false);
53
64
  });
65
+ it("rejects tauri origins for non-loopback gateways", ()=>{
66
+ const allowed = isGatewayOriginAllowed({
67
+ origin: "tauri://localhost",
68
+ requestUrl: "http://192.168.1.50:18789/api/sessions",
69
+ gatewayHost: "0.0.0.0",
70
+ gatewayPort: 18789,
71
+ controlUiEnabled: true,
72
+ controlUiPort: 18790
73
+ });
74
+ expect(allowed).toBe(false);
75
+ });
54
76
  });
@@ -22,6 +22,40 @@ external_vitest_namespaceObject.vi.mock("@/cli/core/agentInvoker.js", ()=>({
22
22
  AgentInvoker: class {
23
23
  async invokeAgent(_agentId, content, _sessionId, _attachments, options) {
24
24
  if ("throw-no-event" === content) throw new Error("Synthetic invocation failure");
25
+ if ("stream-model-failure-no-error" === content) {
26
+ this.outputManager?.emitAgentStream?.({
27
+ event: "on_chain_end",
28
+ run_id: "model-failure-run-1",
29
+ name: "ChannelWrite<branch:to:todoListMiddleware.after_model>",
30
+ data: {
31
+ output: [
32
+ {
33
+ lg_name: "Command",
34
+ update: {
35
+ messages: [
36
+ {
37
+ type: "constructor",
38
+ id: [
39
+ "langchain_core",
40
+ "messages",
41
+ "AIMessage"
42
+ ],
43
+ kwargs: {
44
+ id: "model-failure-msg-1",
45
+ 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"
46
+ }
47
+ }
48
+ ]
49
+ }
50
+ }
51
+ ],
52
+ input: {}
53
+ }
54
+ });
55
+ return {
56
+ streaming: true
57
+ };
58
+ }
25
59
  const signal = options?.signal;
26
60
  await new Promise((resolve)=>{
27
61
  const timer = setTimeout(resolve, 75);
@@ -412,6 +446,29 @@ describeIfBun("Gateway", ()=>{
412
446
  });
413
447
  requester.close();
414
448
  });
449
+ (0, external_vitest_namespaceObject.it)("does not auto-convert streamed model failure text into agent-error", async ()=>{
450
+ const requester = await connectClient("session-streamed-failure-requester");
451
+ const requestId = "req-streamed-failure-no-error";
452
+ const sessionId = "session-streamed-failure-no-error";
453
+ requester.send(JSON.stringify({
454
+ type: "req:agent",
455
+ id: requestId,
456
+ payload: {
457
+ agentId: "main",
458
+ sessionKey: sessionId,
459
+ content: "stream-model-failure-no-error"
460
+ },
461
+ timestamp: Date.now()
462
+ }));
463
+ 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);
464
+ (0, external_vitest_namespaceObject.expect)(JSON.stringify(streamMsg.payload?.chunk?.data || {})).toContain("Model call failed after 3 attempts");
465
+ const completeMsg = await waitForMessage(requester, (msg)=>"event:agent" === msg.type && msg.id === requestId && msg.payload?.type === "agent-complete", 10000);
466
+ (0, external_vitest_namespaceObject.expect)(completeMsg.payload?.sessionId).toBe(sessionId);
467
+ (0, external_vitest_namespaceObject.expect)(completeMsg.payload?.agentId).toBe("main");
468
+ const terminalErrorEvents = await collectMessages(requester, (msg)=>"event:agent" === msg.type && msg.id === requestId && msg.payload?.type === "agent-error", 400);
469
+ (0, external_vitest_namespaceObject.expect)(terminalErrorEvents).toHaveLength(0);
470
+ requester.close();
471
+ });
415
472
  (0, external_vitest_namespaceObject.it)("should emit a single agent-complete terminal event per request", async ()=>{
416
473
  const requester = await connectClient("session-single-complete-requester");
417
474
  const requestId = "req-single-complete";
@@ -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,