@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
@@ -0,0 +1,161 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ const MAX_TARGET_LENGTH = 256;
4
+ const MINUTE_LIMIT = 1439;
5
+ function isAlertMode(value) {
6
+ return "off" === value || "important-only" === value || "all" === value;
7
+ }
8
+ function normalizeQuietHours(value) {
9
+ if (!value || "object" != typeof value || Array.isArray(value)) return null;
10
+ const candidate = value;
11
+ const startMinute = "number" == typeof candidate.startMinute ? candidate.startMinute : NaN;
12
+ const endMinute = "number" == typeof candidate.endMinute ? candidate.endMinute : NaN;
13
+ if (!Number.isInteger(startMinute) || startMinute < 0 || startMinute > MINUTE_LIMIT || !Number.isInteger(endMinute) || endMinute < 0 || endMinute > MINUTE_LIMIT) return null;
14
+ return {
15
+ enabled: false !== candidate.enabled,
16
+ startMinute,
17
+ endMinute,
18
+ timezone: "string" == typeof candidate.timezone && candidate.timezone.trim() ? candidate.timezone.trim() : void 0
19
+ };
20
+ }
21
+ function normalizeTarget(raw) {
22
+ const trimmed = raw.trim();
23
+ if (!trimmed || trimmed.length > MAX_TARGET_LENGTH) return null;
24
+ return trimmed;
25
+ }
26
+ function normalizeSmsPolicyTarget(raw) {
27
+ return normalizeTarget(raw);
28
+ }
29
+ function createDefaultPolicy(target, nowMs) {
30
+ return {
31
+ target,
32
+ paused: false,
33
+ pausedUntil: null,
34
+ stopEnabled: false,
35
+ alertMode: "important-only",
36
+ quietHours: null,
37
+ createdAt: nowMs,
38
+ updatedAt: nowMs
39
+ };
40
+ }
41
+ function parsePolicyRecord(entry) {
42
+ if (!entry || "object" != typeof entry || Array.isArray(entry)) return null;
43
+ const typed = entry;
44
+ const target = "string" == typeof typed.target ? normalizeTarget(typed.target) : null;
45
+ if (!target) return null;
46
+ const createdAt = "number" == typeof typed.createdAt && Number.isFinite(typed.createdAt) ? typed.createdAt : Date.now();
47
+ const updatedAt = "number" == typeof typed.updatedAt && Number.isFinite(typed.updatedAt) ? typed.updatedAt : createdAt;
48
+ const paused = true === typed.paused;
49
+ const pausedUntil = "number" == typeof typed.pausedUntil && Number.isFinite(typed.pausedUntil) ? Math.trunc(typed.pausedUntil) : null;
50
+ return {
51
+ target,
52
+ paused,
53
+ pausedUntil: paused ? pausedUntil : null,
54
+ stopEnabled: true === typed.stopEnabled,
55
+ alertMode: isAlertMode(typed.alertMode) ? typed.alertMode : "important-only",
56
+ quietHours: normalizeQuietHours(typed.quietHours),
57
+ createdAt,
58
+ updatedAt
59
+ };
60
+ }
61
+ function resolveExpiredPause(record, nowMs) {
62
+ if (!record.paused || null === record.pausedUntil || record.pausedUntil > nowMs) return record;
63
+ return {
64
+ ...record,
65
+ paused: false,
66
+ pausedUntil: null,
67
+ updatedAt: nowMs
68
+ };
69
+ }
70
+ const createSmsPolicyStore = (resolveConfigDirPath)=>{
71
+ const resolvePath = ()=>{
72
+ const configDir = resolveConfigDirPath();
73
+ mkdirSync(configDir, {
74
+ recursive: true
75
+ });
76
+ return join(configDir, "sms-policies.json");
77
+ };
78
+ const readRecords = ()=>{
79
+ const path = resolvePath();
80
+ if (!existsSync(path)) return [];
81
+ try {
82
+ const raw = readFileSync(path, "utf-8");
83
+ const parsed = JSON.parse(raw);
84
+ if (!Array.isArray(parsed)) return [];
85
+ const records = [];
86
+ for (const entry of parsed){
87
+ const record = parsePolicyRecord(entry);
88
+ if (record) records.push(record);
89
+ }
90
+ return records;
91
+ } catch {
92
+ return [];
93
+ }
94
+ };
95
+ const writeRecords = (records)=>{
96
+ const path = resolvePath();
97
+ writeFileSync(path, JSON.stringify(records, null, 2));
98
+ };
99
+ const replaceRecord = (records, nextRecord)=>{
100
+ const index = records.findIndex((record)=>record.target === nextRecord.target);
101
+ if (index >= 0) records[index] = nextRecord;
102
+ else records.unshift(nextRecord);
103
+ return records;
104
+ };
105
+ const resolveRecord = (target, nowMs = Date.now())=>{
106
+ const normalizedTarget = normalizeTarget(target);
107
+ if (!normalizedTarget) return createDefaultPolicy("unknown", nowMs);
108
+ const records = readRecords();
109
+ const existing = records.find((record)=>record.target === normalizedTarget);
110
+ if (!existing) return createDefaultPolicy(normalizedTarget, nowMs);
111
+ const resolved = resolveExpiredPause(existing, nowMs);
112
+ if (resolved !== existing) writeRecords(replaceRecord(records, resolved));
113
+ return resolved;
114
+ };
115
+ return {
116
+ load: ()=>readRecords(),
117
+ save: (records)=>writeRecords(records),
118
+ list: ()=>readRecords(),
119
+ get: (target)=>{
120
+ const normalizedTarget = normalizeTarget(target);
121
+ if (!normalizedTarget) return null;
122
+ return readRecords().find((record)=>record.target === normalizedTarget) || null;
123
+ },
124
+ resolve: (target, nowMs = Date.now())=>resolveRecord(target, nowMs),
125
+ upsert: (target, patch, nowMs = Date.now())=>{
126
+ const normalizedTarget = normalizeTarget(target);
127
+ if (!normalizedTarget) throw new Error("Invalid SMS policy target");
128
+ const records = readRecords();
129
+ const existing = records.find((record)=>record.target === normalizedTarget);
130
+ const base = existing || createDefaultPolicy(normalizedTarget, nowMs);
131
+ const next = {
132
+ ...base,
133
+ updatedAt: nowMs
134
+ };
135
+ if ("boolean" == typeof patch.paused) next.paused = patch.paused;
136
+ if (Object.hasOwn(patch, "pausedUntil")) {
137
+ const value = patch.pausedUntil;
138
+ if ("number" == typeof value && Number.isFinite(value)) next.pausedUntil = Math.trunc(value);
139
+ else next.pausedUntil = null;
140
+ }
141
+ if ("boolean" == typeof patch.stopEnabled) next.stopEnabled = patch.stopEnabled;
142
+ if (isAlertMode(patch.alertMode)) next.alertMode = patch.alertMode;
143
+ if (Object.hasOwn(patch, "quietHours")) next.quietHours = patch.quietHours ? normalizeQuietHours(patch.quietHours) : null;
144
+ if (!next.paused) next.pausedUntil = null;
145
+ const resolved = resolveExpiredPause(next, nowMs);
146
+ writeRecords(replaceRecord(records, resolved));
147
+ return resolved;
148
+ },
149
+ reset: (target)=>{
150
+ const normalizedTarget = normalizeTarget(target);
151
+ if (!normalizedTarget) return;
152
+ const records = readRecords();
153
+ const filtered = records.filter((record)=>record.target !== normalizedTarget);
154
+ if (filtered.length === records.length) return;
155
+ writeRecords(filtered);
156
+ },
157
+ isPaused: (target, nowMs = Date.now())=>resolveRecord(target, nowMs).paused,
158
+ isStopped: (target, nowMs = Date.now())=>resolveRecord(target, nowMs).stopEnabled
159
+ };
160
+ };
161
+ export { createSmsPolicyStore, normalizeSmsPolicyTarget };
@@ -87,6 +87,7 @@ const PROVIDERS = {
87
87
  envVars: [
88
88
  "XAI_API_KEY"
89
89
  ],
90
+ baseURL: "https://api.x.ai/v1",
90
91
  category: "model"
91
92
  },
92
93
  lmstudio: {
@@ -57,6 +57,7 @@ const PROVIDERS = {
57
57
  envVars: [
58
58
  "XAI_API_KEY"
59
59
  ],
60
+ baseURL: "https://api.x.ai/v1",
60
61
  category: "model"
61
62
  },
62
63
  lmstudio: {
@@ -42,6 +42,47 @@ const warnings_cjs_namespaceObject = require("../cli/config/warnings.cjs");
42
42
  (0, external_vitest_namespaceObject.expect)(warnings.some((msg)=>msg.includes("allowedChannels"))).toBe(true);
43
43
  (0, external_vitest_namespaceObject.expect)(warnings.some((msg)=>msg.includes("allowedGuilds"))).toBe(true);
44
44
  });
45
+ (0, external_vitest_namespaceObject.it)("returns warnings for common Teams config issues", ()=>{
46
+ const base = (0, schema_cjs_namespaceObject.validateConfig)({}).data;
47
+ const config = (0, schema_cjs_namespaceObject.validateConfig)({
48
+ ...base,
49
+ gateway: {
50
+ ...base.gateway,
51
+ adapters: {
52
+ ...base.gateway.adapters,
53
+ teams: {
54
+ enabled: true,
55
+ appId: "",
56
+ appPassword: " ",
57
+ appType: "MultiTenant",
58
+ endpointPath: " ",
59
+ mentionOnly: true,
60
+ allowBots: false,
61
+ allowedTeamIds: [
62
+ " "
63
+ ],
64
+ allowedChannelIds: [
65
+ "",
66
+ " 19:abc "
67
+ ],
68
+ channelSessions: {
69
+ " 19:channel ": " session-plain "
70
+ },
71
+ sessionCommand: " ",
72
+ responseChunkSize: 3500
73
+ }
74
+ }
75
+ }
76
+ }).data;
77
+ const warnings = (0, warnings_cjs_namespaceObject.collectConfigWarnings)(config).map((warning)=>warning.message);
78
+ (0, external_vitest_namespaceObject.expect)(warnings.some((msg)=>msg.includes("appId/appPassword"))).toBe(true);
79
+ (0, external_vitest_namespaceObject.expect)(warnings.some((msg)=>msg.includes("sessionCommand"))).toBe(true);
80
+ (0, external_vitest_namespaceObject.expect)(warnings.some((msg)=>msg.includes("endpointPath"))).toBe(true);
81
+ (0, external_vitest_namespaceObject.expect)(warnings.some((msg)=>msg.includes("channelSessions"))).toBe(true);
82
+ (0, external_vitest_namespaceObject.expect)(warnings.some((msg)=>msg.includes("agent prefix"))).toBe(true);
83
+ (0, external_vitest_namespaceObject.expect)(warnings.some((msg)=>msg.includes("allowedChannelIds"))).toBe(true);
84
+ (0, external_vitest_namespaceObject.expect)(warnings.some((msg)=>msg.includes("allowedTeamIds"))).toBe(true);
85
+ });
45
86
  (0, external_vitest_namespaceObject.it)("returns no warnings when Discord adapter is disabled", ()=>{
46
87
  const base = (0, schema_cjs_namespaceObject.validateConfig)({}).data;
47
88
  (0, external_vitest_namespaceObject.expect)((0, warnings_cjs_namespaceObject.collectConfigWarnings)(base)).toEqual([]);
@@ -40,6 +40,47 @@ describe("Config warnings", ()=>{
40
40
  expect(warnings.some((msg)=>msg.includes("allowedChannels"))).toBe(true);
41
41
  expect(warnings.some((msg)=>msg.includes("allowedGuilds"))).toBe(true);
42
42
  });
43
+ it("returns warnings for common Teams config issues", ()=>{
44
+ const base = validateConfig({}).data;
45
+ const config = validateConfig({
46
+ ...base,
47
+ gateway: {
48
+ ...base.gateway,
49
+ adapters: {
50
+ ...base.gateway.adapters,
51
+ teams: {
52
+ enabled: true,
53
+ appId: "",
54
+ appPassword: " ",
55
+ appType: "MultiTenant",
56
+ endpointPath: " ",
57
+ mentionOnly: true,
58
+ allowBots: false,
59
+ allowedTeamIds: [
60
+ " "
61
+ ],
62
+ allowedChannelIds: [
63
+ "",
64
+ " 19:abc "
65
+ ],
66
+ channelSessions: {
67
+ " 19:channel ": " session-plain "
68
+ },
69
+ sessionCommand: " ",
70
+ responseChunkSize: 3500
71
+ }
72
+ }
73
+ }
74
+ }).data;
75
+ const warnings = collectConfigWarnings(config).map((warning)=>warning.message);
76
+ expect(warnings.some((msg)=>msg.includes("appId/appPassword"))).toBe(true);
77
+ expect(warnings.some((msg)=>msg.includes("sessionCommand"))).toBe(true);
78
+ expect(warnings.some((msg)=>msg.includes("endpointPath"))).toBe(true);
79
+ expect(warnings.some((msg)=>msg.includes("channelSessions"))).toBe(true);
80
+ expect(warnings.some((msg)=>msg.includes("agent prefix"))).toBe(true);
81
+ expect(warnings.some((msg)=>msg.includes("allowedChannelIds"))).toBe(true);
82
+ expect(warnings.some((msg)=>msg.includes("allowedTeamIds"))).toBe(true);
83
+ });
43
84
  it("returns no warnings when Discord adapter is disabled", ()=>{
44
85
  const base = validateConfig({}).data;
45
86
  expect(collectConfigWarnings(base)).toEqual([]);
@@ -77,35 +77,41 @@ const init_cjs_namespaceObject = require("../cli/commands/init.cjs");
77
77
  const gameDevPrompt = (0, external_node_fs_namespaceObject.readFileSync)(gameDevAgentPath, "utf-8");
78
78
  (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("name: game-dev");
79
79
  (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("subAgents:");
80
- (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("name: art-generation");
81
- (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("promptFile: ./art-generation.md");
82
- (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("name: asset-refinement");
83
- (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("promptFile: ./asset-refinement.md");
84
- (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("name: planning-idea");
85
- (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("promptFile: ./planning-idea.md");
86
- (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("name: ui-specialist");
87
- (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("promptFile: ./ui-specialist.md");
80
+ (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("name: art-director");
81
+ (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("promptFile: ./art-director.md");
82
+ (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("name: scene-engineer");
83
+ (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("promptFile: ./scene-engineer.md");
84
+ (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("name: game-designer");
85
+ (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("promptFile: ./game-designer.md");
88
86
  (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("write_todos");
89
87
  (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("read_todos");
90
- (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("UV-aware texture planning");
88
+ (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("Delegation routing");
91
89
  (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("MeshStandardMaterial");
92
- (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("uv`/`uv2");
93
- const gameDevArtGenerationPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "game-dev", "art-generation.md");
94
- const gameDevAssetRefinementPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "game-dev", "asset-refinement.md");
95
- const gameDevPlanningIdeaPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "game-dev", "planning-idea.md");
96
- const gameDevUiSpecialistPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "game-dev", "ui-specialist.md");
97
- (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(gameDevArtGenerationPath)).toBe(true);
98
- (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(gameDevAssetRefinementPath)).toBe(true);
99
- (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(gameDevPlanningIdeaPath)).toBe(true);
100
- (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(gameDevUiSpecialistPath)).toBe(true);
101
- const gameDevArtGenerationPrompt = (0, external_node_fs_namespaceObject.readFileSync)(gameDevArtGenerationPath, "utf-8");
102
- (0, external_vitest_namespaceObject.expect)(gameDevArtGenerationPrompt).toContain("UV set(s) or UDIM tiles");
103
- (0, external_vitest_namespaceObject.expect)(gameDevArtGenerationPrompt).toContain("texel density targets");
104
- (0, external_vitest_namespaceObject.expect)(gameDevArtGenerationPrompt).toContain("Texture-to-geometry mapping notes");
105
- (0, external_vitest_namespaceObject.expect)(gameDevArtGenerationPrompt).toContain("material slot");
106
- (0, external_vitest_namespaceObject.expect)(gameDevArtGenerationPrompt).toContain("need `uv`, and `aoMap`");
107
- (0, external_vitest_namespaceObject.expect)(gameDevArtGenerationPrompt).toContain("flipY = false");
108
- (0, external_vitest_namespaceObject.expect)(gameDevArtGenerationPrompt).toContain("RepeatWrapping");
90
+ (0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("three-mesh-bvh");
91
+ const gameDevArtDirectorPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "game-dev", "art-director.md");
92
+ const gameDevSceneEngineerPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "game-dev", "scene-engineer.md");
93
+ const gameDevDesignerPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "game-dev", "game-designer.md");
94
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(gameDevArtDirectorPath)).toBe(true);
95
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(gameDevSceneEngineerPath)).toBe(true);
96
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(gameDevDesignerPath)).toBe(true);
97
+ const gameDevArtDirectorPrompt = (0, external_node_fs_namespaceObject.readFileSync)(gameDevArtDirectorPath, "utf-8");
98
+ (0, external_vitest_namespaceObject.expect)(gameDevArtDirectorPrompt).toContain("You are `art-director`");
99
+ (0, external_vitest_namespaceObject.expect)(gameDevArtDirectorPrompt).toContain("Texture-to-geometry mapping: mesh, material slot, UV set");
100
+ (0, external_vitest_namespaceObject.expect)(gameDevArtDirectorPrompt).toContain("flipY = false");
101
+ (0, external_vitest_namespaceObject.expect)(gameDevArtDirectorPrompt).toContain("RepeatWrapping");
102
+ const gameDevSceneEngineerPrompt = (0, external_node_fs_namespaceObject.readFileSync)(gameDevSceneEngineerPath, "utf-8");
103
+ (0, external_vitest_namespaceObject.expect)(gameDevSceneEngineerPrompt).toContain("You are `scene-engineer`");
104
+ (0, external_vitest_namespaceObject.expect)(gameDevSceneEngineerPrompt).toContain("Rapier is the default physics engine");
105
+ (0, external_vitest_namespaceObject.expect)(gameDevSceneEngineerPrompt).toContain("Prefer `postprocessing` over Three.js built-in `EffectComposer`");
106
+ const gameDevDesignerPrompt = (0, external_node_fs_namespaceObject.readFileSync)(gameDevDesignerPath, "utf-8");
107
+ (0, external_vitest_namespaceObject.expect)(gameDevDesignerPrompt).toContain("You are `game-designer`");
108
+ (0, external_vitest_namespaceObject.expect)(gameDevDesignerPrompt).toContain("Always present 2-3 mechanic variants with tradeoffs");
109
+ (0, external_vitest_namespaceObject.expect)(gameDevDesignerPrompt).toContain("UI Conventions");
110
+ const mainAgentTemplatePath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "main", "agent.md");
111
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(mainAgentTemplatePath)).toBe(true);
112
+ const mainAgentTemplatePrompt = (0, external_node_fs_namespaceObject.readFileSync)(mainAgentTemplatePath, "utf-8");
113
+ (0, external_vitest_namespaceObject.expect)(mainAgentTemplatePrompt).toContain("name: main");
114
+ (0, external_vitest_namespaceObject.expect)(mainAgentTemplatePrompt).toContain("Primary Wingman agent");
109
115
  });
110
116
  (0, external_vitest_namespaceObject.it)("merges existing config when --merge is set", async ()=>{
111
117
  const configDir = (0, external_node_path_namespaceObject.join)(workspace, ".wingman");
@@ -75,35 +75,41 @@ describe("CLI init", ()=>{
75
75
  const gameDevPrompt = readFileSync(gameDevAgentPath, "utf-8");
76
76
  expect(gameDevPrompt).toContain("name: game-dev");
77
77
  expect(gameDevPrompt).toContain("subAgents:");
78
- expect(gameDevPrompt).toContain("name: art-generation");
79
- expect(gameDevPrompt).toContain("promptFile: ./art-generation.md");
80
- expect(gameDevPrompt).toContain("name: asset-refinement");
81
- expect(gameDevPrompt).toContain("promptFile: ./asset-refinement.md");
82
- expect(gameDevPrompt).toContain("name: planning-idea");
83
- expect(gameDevPrompt).toContain("promptFile: ./planning-idea.md");
84
- expect(gameDevPrompt).toContain("name: ui-specialist");
85
- expect(gameDevPrompt).toContain("promptFile: ./ui-specialist.md");
78
+ expect(gameDevPrompt).toContain("name: art-director");
79
+ expect(gameDevPrompt).toContain("promptFile: ./art-director.md");
80
+ expect(gameDevPrompt).toContain("name: scene-engineer");
81
+ expect(gameDevPrompt).toContain("promptFile: ./scene-engineer.md");
82
+ expect(gameDevPrompt).toContain("name: game-designer");
83
+ expect(gameDevPrompt).toContain("promptFile: ./game-designer.md");
86
84
  expect(gameDevPrompt).toContain("write_todos");
87
85
  expect(gameDevPrompt).toContain("read_todos");
88
- expect(gameDevPrompt).toContain("UV-aware texture planning");
86
+ expect(gameDevPrompt).toContain("Delegation routing");
89
87
  expect(gameDevPrompt).toContain("MeshStandardMaterial");
90
- expect(gameDevPrompt).toContain("uv`/`uv2");
91
- const gameDevArtGenerationPath = join(workspace, ".wingman", "agents", "game-dev", "art-generation.md");
92
- const gameDevAssetRefinementPath = join(workspace, ".wingman", "agents", "game-dev", "asset-refinement.md");
93
- const gameDevPlanningIdeaPath = join(workspace, ".wingman", "agents", "game-dev", "planning-idea.md");
94
- const gameDevUiSpecialistPath = join(workspace, ".wingman", "agents", "game-dev", "ui-specialist.md");
95
- expect(existsSync(gameDevArtGenerationPath)).toBe(true);
96
- expect(existsSync(gameDevAssetRefinementPath)).toBe(true);
97
- expect(existsSync(gameDevPlanningIdeaPath)).toBe(true);
98
- expect(existsSync(gameDevUiSpecialistPath)).toBe(true);
99
- const gameDevArtGenerationPrompt = readFileSync(gameDevArtGenerationPath, "utf-8");
100
- expect(gameDevArtGenerationPrompt).toContain("UV set(s) or UDIM tiles");
101
- expect(gameDevArtGenerationPrompt).toContain("texel density targets");
102
- expect(gameDevArtGenerationPrompt).toContain("Texture-to-geometry mapping notes");
103
- expect(gameDevArtGenerationPrompt).toContain("material slot");
104
- expect(gameDevArtGenerationPrompt).toContain("need `uv`, and `aoMap`");
105
- expect(gameDevArtGenerationPrompt).toContain("flipY = false");
106
- expect(gameDevArtGenerationPrompt).toContain("RepeatWrapping");
88
+ expect(gameDevPrompt).toContain("three-mesh-bvh");
89
+ const gameDevArtDirectorPath = join(workspace, ".wingman", "agents", "game-dev", "art-director.md");
90
+ const gameDevSceneEngineerPath = join(workspace, ".wingman", "agents", "game-dev", "scene-engineer.md");
91
+ const gameDevDesignerPath = join(workspace, ".wingman", "agents", "game-dev", "game-designer.md");
92
+ expect(existsSync(gameDevArtDirectorPath)).toBe(true);
93
+ expect(existsSync(gameDevSceneEngineerPath)).toBe(true);
94
+ expect(existsSync(gameDevDesignerPath)).toBe(true);
95
+ const gameDevArtDirectorPrompt = readFileSync(gameDevArtDirectorPath, "utf-8");
96
+ expect(gameDevArtDirectorPrompt).toContain("You are `art-director`");
97
+ expect(gameDevArtDirectorPrompt).toContain("Texture-to-geometry mapping: mesh, material slot, UV set");
98
+ expect(gameDevArtDirectorPrompt).toContain("flipY = false");
99
+ expect(gameDevArtDirectorPrompt).toContain("RepeatWrapping");
100
+ const gameDevSceneEngineerPrompt = readFileSync(gameDevSceneEngineerPath, "utf-8");
101
+ expect(gameDevSceneEngineerPrompt).toContain("You are `scene-engineer`");
102
+ expect(gameDevSceneEngineerPrompt).toContain("Rapier is the default physics engine");
103
+ expect(gameDevSceneEngineerPrompt).toContain("Prefer `postprocessing` over Three.js built-in `EffectComposer`");
104
+ const gameDevDesignerPrompt = readFileSync(gameDevDesignerPath, "utf-8");
105
+ expect(gameDevDesignerPrompt).toContain("You are `game-designer`");
106
+ expect(gameDevDesignerPrompt).toContain("Always present 2-3 mechanic variants with tradeoffs");
107
+ expect(gameDevDesignerPrompt).toContain("UI Conventions");
108
+ const mainAgentTemplatePath = join(workspace, ".wingman", "agents", "main", "agent.md");
109
+ expect(existsSync(mainAgentTemplatePath)).toBe(true);
110
+ const mainAgentTemplatePrompt = readFileSync(mainAgentTemplatePath, "utf-8");
111
+ expect(mainAgentTemplatePrompt).toContain("name: main");
112
+ expect(mainAgentTemplatePrompt).toContain("Primary Wingman agent");
107
113
  });
108
114
  it("merges existing config when --merge is set", async ()=>{
109
115
  const configDir = join(workspace, ".wingman");
@@ -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";