@wingman-ai/gateway 0.2.2 → 0.2.4

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/.wingman/agents/README.md +7 -1
  2. package/.wingman/agents/coding/agent.md +299 -201
  3. package/.wingman/agents/coding-v2/agent.md +127 -0
  4. package/.wingman/agents/coding-v2/implementor.md +89 -0
  5. package/.wingman/agents/main/agent.md +4 -0
  6. package/README.md +1 -0
  7. package/dist/agent/config/agentConfig.cjs +31 -17
  8. package/dist/agent/config/agentConfig.d.ts +23 -1
  9. package/dist/agent/config/agentConfig.js +30 -19
  10. package/dist/agent/config/agentLoader.cjs +26 -8
  11. package/dist/agent/config/agentLoader.d.ts +4 -2
  12. package/dist/agent/config/agentLoader.js +26 -8
  13. package/dist/agent/config/modelFactory.cjs +95 -25
  14. package/dist/agent/config/modelFactory.d.ts +13 -1
  15. package/dist/agent/config/modelFactory.js +95 -25
  16. package/dist/agent/config/toolRegistry.cjs +19 -6
  17. package/dist/agent/config/toolRegistry.d.ts +5 -2
  18. package/dist/agent/config/toolRegistry.js +19 -6
  19. package/dist/agent/middleware/hooks/types.cjs +13 -13
  20. package/dist/agent/middleware/hooks/types.d.ts +1 -1
  21. package/dist/agent/middleware/hooks/types.js +14 -14
  22. package/dist/agent/tests/agentConfig.test.cjs +22 -2
  23. package/dist/agent/tests/agentConfig.test.js +22 -2
  24. package/dist/agent/tests/agentLoader.test.cjs +38 -1
  25. package/dist/agent/tests/agentLoader.test.js +38 -1
  26. package/dist/agent/tests/backgroundTerminal.test.cjs +70 -0
  27. package/dist/agent/tests/backgroundTerminal.test.d.ts +1 -0
  28. package/dist/agent/tests/backgroundTerminal.test.js +64 -0
  29. package/dist/agent/tests/commandExecuteTool.test.cjs +29 -0
  30. package/dist/agent/tests/commandExecuteTool.test.d.ts +1 -0
  31. package/dist/agent/tests/commandExecuteTool.test.js +23 -0
  32. package/dist/agent/tests/modelFactory.test.cjs +47 -5
  33. package/dist/agent/tests/modelFactory.test.js +47 -5
  34. package/dist/agent/tests/terminalSessionManager.test.cjs +121 -0
  35. package/dist/agent/tests/terminalSessionManager.test.d.ts +1 -0
  36. package/dist/agent/tests/terminalSessionManager.test.js +115 -0
  37. package/dist/agent/tests/toolRegistry.test.cjs +14 -2
  38. package/dist/agent/tests/toolRegistry.test.js +14 -2
  39. package/dist/agent/tools/background_terminal.cjs +128 -0
  40. package/dist/agent/tools/background_terminal.d.ts +41 -0
  41. package/dist/agent/tools/background_terminal.js +94 -0
  42. package/dist/agent/tools/code_search.cjs +6 -6
  43. package/dist/agent/tools/code_search.d.ts +1 -1
  44. package/dist/agent/tools/code_search.js +7 -7
  45. package/dist/agent/tools/command_execute.cjs +22 -7
  46. package/dist/agent/tools/command_execute.d.ts +3 -2
  47. package/dist/agent/tools/command_execute.js +23 -8
  48. package/dist/agent/tools/git_status.cjs +3 -3
  49. package/dist/agent/tools/git_status.d.ts +1 -1
  50. package/dist/agent/tools/git_status.js +4 -4
  51. package/dist/agent/tools/internet_search.cjs +6 -6
  52. package/dist/agent/tools/internet_search.d.ts +1 -1
  53. package/dist/agent/tools/internet_search.js +7 -7
  54. package/dist/agent/tools/terminal_session_manager.cjs +321 -0
  55. package/dist/agent/tools/terminal_session_manager.d.ts +77 -0
  56. package/dist/agent/tools/terminal_session_manager.js +284 -0
  57. package/dist/agent/tools/think.cjs +4 -4
  58. package/dist/agent/tools/think.d.ts +1 -1
  59. package/dist/agent/tools/think.js +5 -5
  60. package/dist/agent/tools/ui_registry.cjs +13 -13
  61. package/dist/agent/tools/ui_registry.d.ts +4 -4
  62. package/dist/agent/tools/ui_registry.js +14 -14
  63. package/dist/agent/tools/web_crawler.cjs +4 -4
  64. package/dist/agent/tools/web_crawler.d.ts +1 -1
  65. package/dist/agent/tools/web_crawler.js +5 -5
  66. package/dist/agent/utils.cjs +2 -1
  67. package/dist/agent/utils.js +2 -1
  68. package/dist/cli/commands/init.cjs +7 -6
  69. package/dist/cli/commands/init.js +7 -6
  70. package/dist/cli/commands/provider.cjs +17 -3
  71. package/dist/cli/commands/provider.js +17 -3
  72. package/dist/cli/config/loader.cjs +27 -0
  73. package/dist/cli/config/loader.js +27 -0
  74. package/dist/cli/config/schema.cjs +146 -68
  75. package/dist/cli/config/schema.d.ts +89 -1
  76. package/dist/cli/config/schema.js +134 -68
  77. package/dist/cli/core/agentInvoker.cjs +344 -17
  78. package/dist/cli/core/agentInvoker.d.ts +63 -3
  79. package/dist/cli/core/agentInvoker.js +303 -12
  80. package/dist/cli/core/sessionManager.cjs +32 -5
  81. package/dist/cli/core/sessionManager.js +32 -5
  82. package/dist/cli/core/streamParser.cjs +15 -0
  83. package/dist/cli/core/streamParser.js +15 -0
  84. package/dist/cli/index.cjs +6 -5
  85. package/dist/cli/index.js +6 -5
  86. package/dist/cli/types.d.ts +32 -0
  87. package/dist/cli/ui/toolDisplayHelpers.cjs +2 -0
  88. package/dist/cli/ui/toolDisplayHelpers.js +2 -0
  89. package/dist/gateway/hooks/registry.cjs +2 -1
  90. package/dist/gateway/hooks/registry.d.ts +1 -1
  91. package/dist/gateway/hooks/registry.js +2 -1
  92. package/dist/gateway/hooks/types.cjs +11 -11
  93. package/dist/gateway/hooks/types.d.ts +1 -1
  94. package/dist/gateway/hooks/types.js +12 -12
  95. package/dist/gateway/http/agents.cjs +67 -4
  96. package/dist/gateway/http/agents.js +67 -4
  97. package/dist/gateway/http/sessions.cjs +7 -7
  98. package/dist/gateway/http/sessions.js +7 -7
  99. package/dist/gateway/http/types.d.ts +5 -3
  100. package/dist/gateway/http/webhooks.cjs +6 -5
  101. package/dist/gateway/http/webhooks.js +6 -5
  102. package/dist/gateway/server.cjs +198 -41
  103. package/dist/gateway/server.d.ts +9 -1
  104. package/dist/gateway/server.js +198 -41
  105. package/dist/gateway/types.d.ts +1 -0
  106. package/dist/gateway/validation.cjs +39 -39
  107. package/dist/gateway/validation.d.ts +1 -1
  108. package/dist/gateway/validation.js +40 -40
  109. package/dist/providers/codex.cjs +167 -0
  110. package/dist/providers/codex.d.ts +15 -0
  111. package/dist/providers/codex.js +127 -0
  112. package/dist/providers/credentials.cjs +8 -0
  113. package/dist/providers/credentials.js +8 -0
  114. package/dist/providers/registry.cjs +11 -0
  115. package/dist/providers/registry.d.ts +1 -1
  116. package/dist/providers/registry.js +11 -0
  117. package/dist/tests/additionalMessageMiddleware.test.cjs +3 -0
  118. package/dist/tests/additionalMessageMiddleware.test.js +3 -0
  119. package/dist/tests/agentInvokerSummarization.test.cjs +455 -0
  120. package/dist/tests/agentInvokerSummarization.test.d.ts +1 -0
  121. package/dist/tests/agentInvokerSummarization.test.js +449 -0
  122. package/dist/tests/agents-api.test.cjs +45 -5
  123. package/dist/tests/agents-api.test.js +45 -5
  124. package/dist/tests/cli-config-loader.test.cjs +88 -0
  125. package/dist/tests/cli-config-loader.test.js +88 -0
  126. package/dist/tests/cli-init.test.cjs +27 -3
  127. package/dist/tests/cli-init.test.js +27 -3
  128. package/dist/tests/codex-credentials-precedence.test.cjs +94 -0
  129. package/dist/tests/codex-credentials-precedence.test.d.ts +1 -0
  130. package/dist/tests/codex-credentials-precedence.test.js +88 -0
  131. package/dist/tests/codex-provider.test.cjs +210 -0
  132. package/dist/tests/codex-provider.test.d.ts +1 -0
  133. package/dist/tests/codex-provider.test.js +204 -0
  134. package/dist/tests/gateway.test.cjs +115 -8
  135. package/dist/tests/gateway.test.js +115 -8
  136. package/dist/tests/provider-command-codex.test.cjs +57 -0
  137. package/dist/tests/provider-command-codex.test.d.ts +1 -0
  138. package/dist/tests/provider-command-codex.test.js +51 -0
  139. package/dist/tests/sessionStateMessages.test.cjs +38 -0
  140. package/dist/tests/sessionStateMessages.test.js +38 -0
  141. package/dist/tests/toolDisplayHelpers.test.cjs +3 -0
  142. package/dist/tests/toolDisplayHelpers.test.js +3 -0
  143. package/dist/tools/mcp-finance.cjs +48 -48
  144. package/dist/tools/mcp-finance.js +48 -48
  145. package/dist/types/mcp.cjs +15 -15
  146. package/dist/types/mcp.d.ts +1 -1
  147. package/dist/types/mcp.js +16 -16
  148. package/dist/types/voice.cjs +21 -21
  149. package/dist/types/voice.d.ts +1 -1
  150. package/dist/types/voice.js +22 -22
  151. package/dist/webui/assets/index-DVWQluit.css +11 -0
  152. package/dist/webui/assets/index-Dlyzwalc.js +270 -0
  153. package/dist/webui/favicon-32x32.png +0 -0
  154. package/dist/webui/favicon-64x64.png +0 -0
  155. package/dist/webui/favicon.webp +0 -0
  156. package/dist/webui/index.html +4 -2
  157. package/package.json +13 -12
  158. package/.wingman/agents/coding/implementor.md +0 -79
  159. package/dist/webui/assets/index-CPhfGPHc.js +0 -182
  160. package/dist/webui/assets/index-DDsMIOTX.css +0 -11
@@ -1,9 +1,9 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
- import { handleAgentsApi } from "../gateway/http/agents.js";
3
1
  import { mkdtempSync, readFileSync, rmSync } from "node:fs";
4
- import { join } from "node:path";
5
2
  import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
6
4
  import { load } from "js-yaml";
5
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
6
+ import { handleAgentsApi } from "../gateway/http/agents.js";
7
7
  const isBunRuntime = void 0 !== globalThis.Bun;
8
8
  const describeIfBun = isBunRuntime ? describe : describe.skip;
9
9
  const parseMarkdownAgent = (raw)=>{
@@ -63,6 +63,7 @@ describeIfBun("agents API", ()=>{
63
63
  "not_real_tool"
64
64
  ],
65
65
  prompt: "You are the orchestrator.",
66
+ reasoningEffort: "high",
66
67
  promptTraining: {
67
68
  enabled: true,
68
69
  instructionsPath: "/memories/agents/orchestrator/instructions.md"
@@ -76,6 +77,7 @@ describeIfBun("agents API", ()=>{
76
77
  "not_real_tool"
77
78
  ],
78
79
  prompt: "Plan tasks in detail.",
80
+ reasoningEffort: "low",
79
81
  promptTraining: true
80
82
  },
81
83
  {
@@ -84,7 +86,8 @@ describeIfBun("agents API", ()=>{
84
86
  tools: [
85
87
  "command_execute"
86
88
  ],
87
- prompt: "Execute planned tasks."
89
+ prompt: "Execute planned tasks.",
90
+ thinkingEffort: "minimal"
88
91
  }
89
92
  ]
90
93
  })
@@ -97,12 +100,15 @@ describeIfBun("agents API", ()=>{
97
100
  enabled: true,
98
101
  instructionsPath: "/memories/agents/orchestrator/instructions.md"
99
102
  });
103
+ expect(created.reasoningEffort).toBe("high");
100
104
  expect(created.promptRefinement).toEqual(created.promptTraining);
101
105
  expect(created.subAgents).toHaveLength(2);
102
106
  expect(created.subAgents[0].promptTraining).toBe(true);
103
107
  expect(created.subAgents[0].tools).toEqual([
104
108
  "think"
105
109
  ]);
110
+ expect(created.subAgents[0].reasoningEffort).toBe("low");
111
+ expect(created.subAgents[1].reasoningEffort).toBe("minimal");
106
112
  const agentPath = join(tempDir, "agents", "orchestrator", "agent.md");
107
113
  const parsed = parseMarkdownAgent(readFileSync(agentPath, "utf-8"));
108
114
  expect(parsed.prompt).toBe("You are the orchestrator.");
@@ -110,10 +116,13 @@ describeIfBun("agents API", ()=>{
110
116
  enabled: true,
111
117
  instructionsPath: "/memories/agents/orchestrator/instructions.md"
112
118
  });
119
+ expect(parsed.metadata.reasoningEffort).toBe("high");
113
120
  expect(parsed.metadata.subAgents).toHaveLength(2);
114
121
  expect(parsed.metadata.subAgents[0].name).toBe("planner");
122
+ expect(parsed.metadata.subAgents[0].reasoningEffort).toBe("low");
115
123
  expect(parsed.metadata.subAgents[0].promptRefinement).toBe(true);
116
124
  expect(parsed.metadata.subAgents[0].systemPrompt).toBe("Plan tasks in detail.");
125
+ expect(parsed.metadata.subAgents[1].reasoningEffort).toBe("minimal");
117
126
  const detailReq = new Request("http://localhost/api/agents/orchestrator", {
118
127
  method: "GET"
119
128
  });
@@ -123,9 +132,11 @@ describeIfBun("agents API", ()=>{
123
132
  const detail = await detailRes.json();
124
133
  expect(detail.promptTraining).toEqual(created.promptTraining);
125
134
  expect(detail.promptRefinement).toEqual(created.promptTraining);
135
+ expect(detail.reasoningEffort).toBe("high");
126
136
  expect(detail.subAgents).toHaveLength(2);
127
137
  expect(detail.subAgents[0].id).toBe("planner");
128
138
  expect(detail.subAgents[0].prompt).toBe("Plan tasks in detail.");
139
+ expect(detail.subAgents[0].reasoningEffort).toBe("low");
129
140
  });
130
141
  it("updates promptTraining and subAgents in markdown", async ()=>{
131
142
  const createReq = new Request("http://localhost/api/agents", {
@@ -139,6 +150,7 @@ describeIfBun("agents API", ()=>{
139
150
  "think"
140
151
  ],
141
152
  prompt: "Original prompt",
153
+ reasoningEffort: "low",
142
154
  promptTraining: true,
143
155
  subAgents: [
144
156
  {
@@ -147,7 +159,8 @@ describeIfBun("agents API", ()=>{
147
159
  tools: [
148
160
  "think"
149
161
  ],
150
- prompt: "Original planner prompt"
162
+ prompt: "Original planner prompt",
163
+ reasoningEffort: "minimal"
151
164
  }
152
165
  ]
153
166
  })
@@ -160,6 +173,7 @@ describeIfBun("agents API", ()=>{
160
173
  "Content-Type": "application/json"
161
174
  },
162
175
  body: JSON.stringify({
176
+ reasoningEffort: "high",
163
177
  promptTraining: false,
164
178
  subAgents: [
165
179
  {
@@ -169,6 +183,7 @@ describeIfBun("agents API", ()=>{
169
183
  "git_status"
170
184
  ],
171
185
  prompt: "Review carefully.",
186
+ thinkingEffort: "medium",
172
187
  promptTraining: {
173
188
  enabled: true,
174
189
  instructionsPath: "/memories/reviewer.md"
@@ -184,8 +199,10 @@ describeIfBun("agents API", ()=>{
184
199
  const updated = await updateRes.json();
185
200
  expect(updated.promptTraining).toBe(false);
186
201
  expect(updated.promptRefinement).toBe(false);
202
+ expect(updated.reasoningEffort).toBe("high");
187
203
  expect(updated.subAgents).toHaveLength(1);
188
204
  expect(updated.subAgents[0].id).toBe("reviewer");
205
+ expect(updated.subAgents[0].reasoningEffort).toBe("medium");
189
206
  expect(updated.subAgents[0].promptTraining).toEqual({
190
207
  enabled: true,
191
208
  instructionsPath: "/memories/reviewer.md"
@@ -193,8 +210,10 @@ describeIfBun("agents API", ()=>{
193
210
  const agentPath = join(tempDir, "agents", "editor-agent", "agent.md");
194
211
  const parsed = parseMarkdownAgent(readFileSync(agentPath, "utf-8"));
195
212
  expect(parsed.metadata.promptRefinement).toBe(false);
213
+ expect(parsed.metadata.reasoningEffort).toBe("high");
196
214
  expect(parsed.metadata.subAgents).toHaveLength(1);
197
215
  expect(parsed.metadata.subAgents[0].name).toBe("reviewer");
216
+ expect(parsed.metadata.subAgents[0].reasoningEffort).toBe("medium");
198
217
  expect(parsed.metadata.subAgents[0].promptRefinement).toEqual({
199
218
  enabled: true,
200
219
  instructionsPath: "/memories/reviewer.md"
@@ -223,4 +242,25 @@ describeIfBun("agents API", ()=>{
223
242
  expect(created.promptTraining).toBe(true);
224
243
  expect(created.promptRefinement).toBe(true);
225
244
  });
245
+ it("accepts legacy thinkingEffort input as reasoningEffort", async ()=>{
246
+ const createReq = new Request("http://localhost/api/agents", {
247
+ method: "POST",
248
+ headers: {
249
+ "Content-Type": "application/json"
250
+ },
251
+ body: JSON.stringify({
252
+ id: "legacy-thinking-agent",
253
+ tools: [
254
+ "think"
255
+ ],
256
+ prompt: "Legacy thinking prompt",
257
+ thinkingEffort: "medium"
258
+ })
259
+ });
260
+ const createRes = await handleAgentsApi(ctx, createReq, new URL(createReq.url));
261
+ expect(createRes).not.toBeNull();
262
+ expect(createRes?.ok).toBe(true);
263
+ const created = await createRes.json();
264
+ expect(created.reasoningEffort).toBe("medium");
265
+ });
226
266
  });
@@ -33,6 +33,33 @@ const external_os_namespaceObject = require("os");
33
33
  (0, external_vitest_namespaceObject.expect)(config).toEqual({
34
34
  logLevel: "info",
35
35
  recursionLimit: 5000,
36
+ summarization: {
37
+ enabled: true,
38
+ maxTokensBeforeSummary: 12000,
39
+ messagesToKeep: 8
40
+ },
41
+ modelRetry: {
42
+ enabled: true,
43
+ maxRetries: 2,
44
+ backoffFactor: 2,
45
+ initialDelayMs: 1000,
46
+ maxDelayMs: 60000,
47
+ jitter: true,
48
+ onFailure: "continue"
49
+ },
50
+ toolRetry: {
51
+ enabled: false,
52
+ maxRetries: 2,
53
+ backoffFactor: 2,
54
+ initialDelayMs: 1000,
55
+ maxDelayMs: 60000,
56
+ jitter: true,
57
+ onFailure: "continue"
58
+ },
59
+ humanInTheLoop: {
60
+ enabled: false,
61
+ interruptOn: {}
62
+ },
36
63
  search: {
37
64
  provider: "duckduckgo",
38
65
  maxResults: 5
@@ -111,6 +138,11 @@ const external_os_namespaceObject = require("os");
111
138
  logLevel: "debug",
112
139
  defaultAgent: "coder",
113
140
  recursionLimit: 5,
141
+ summarization: {
142
+ enabled: true,
143
+ maxTokensBeforeSummary: 18000,
144
+ messagesToKeep: 10
145
+ },
114
146
  search: {
115
147
  provider: "perplexity",
116
148
  maxResults: 10
@@ -130,6 +162,62 @@ const external_os_namespaceObject = require("os");
130
162
  const config = loader.loadConfig();
131
163
  (0, external_vitest_namespaceObject.expect)(config).toMatchObject(configData);
132
164
  });
165
+ (0, external_vitest_namespaceObject.it)("should allow disabling summarization middleware", ()=>{
166
+ const configData = {
167
+ summarization: {
168
+ enabled: false
169
+ }
170
+ };
171
+ (0, external_fs_namespaceObject.writeFileSync)((0, external_path_namespaceObject.join)(configDir, "wingman.config.json"), JSON.stringify(configData));
172
+ const loader = new loader_cjs_namespaceObject.WingmanConfigLoader(".wingman", testDir);
173
+ const config = loader.loadConfig();
174
+ (0, external_vitest_namespaceObject.expect)(config.summarization).toEqual({
175
+ enabled: false,
176
+ maxTokensBeforeSummary: 12000,
177
+ messagesToKeep: 8
178
+ });
179
+ });
180
+ (0, external_vitest_namespaceObject.it)("should load retry and HITL middleware config", ()=>{
181
+ const configData = {
182
+ modelRetry: {
183
+ enabled: true,
184
+ maxRetries: 3,
185
+ backoffFactor: 1.5,
186
+ initialDelayMs: 250,
187
+ maxDelayMs: 4000,
188
+ jitter: false,
189
+ onFailure: "error"
190
+ },
191
+ toolRetry: {
192
+ enabled: true,
193
+ maxRetries: 4,
194
+ backoffFactor: 2,
195
+ initialDelayMs: 500,
196
+ maxDelayMs: 8000,
197
+ jitter: true,
198
+ onFailure: "continue",
199
+ tools: [
200
+ "internet_search",
201
+ "web_crawler"
202
+ ]
203
+ },
204
+ humanInTheLoop: {
205
+ enabled: true,
206
+ interruptOn: {
207
+ command_execute: {
208
+ allowedDecisions: [
209
+ "approve",
210
+ "reject"
211
+ ]
212
+ }
213
+ }
214
+ }
215
+ };
216
+ (0, external_fs_namespaceObject.writeFileSync)((0, external_path_namespaceObject.join)(configDir, "wingman.config.json"), JSON.stringify(configData));
217
+ const loader = new loader_cjs_namespaceObject.WingmanConfigLoader(".wingman", testDir);
218
+ const config = loader.loadConfig();
219
+ (0, external_vitest_namespaceObject.expect)(config).toMatchObject(configData);
220
+ });
133
221
  (0, external_vitest_namespaceObject.it)("should handle all valid log levels", ()=>{
134
222
  const levels = [
135
223
  "debug",
@@ -31,6 +31,33 @@ describe("CLI Config Loader", ()=>{
31
31
  expect(config).toEqual({
32
32
  logLevel: "info",
33
33
  recursionLimit: 5000,
34
+ summarization: {
35
+ enabled: true,
36
+ maxTokensBeforeSummary: 12000,
37
+ messagesToKeep: 8
38
+ },
39
+ modelRetry: {
40
+ enabled: true,
41
+ maxRetries: 2,
42
+ backoffFactor: 2,
43
+ initialDelayMs: 1000,
44
+ maxDelayMs: 60000,
45
+ jitter: true,
46
+ onFailure: "continue"
47
+ },
48
+ toolRetry: {
49
+ enabled: false,
50
+ maxRetries: 2,
51
+ backoffFactor: 2,
52
+ initialDelayMs: 1000,
53
+ maxDelayMs: 60000,
54
+ jitter: true,
55
+ onFailure: "continue"
56
+ },
57
+ humanInTheLoop: {
58
+ enabled: false,
59
+ interruptOn: {}
60
+ },
34
61
  search: {
35
62
  provider: "duckduckgo",
36
63
  maxResults: 5
@@ -109,6 +136,11 @@ describe("CLI Config Loader", ()=>{
109
136
  logLevel: "debug",
110
137
  defaultAgent: "coder",
111
138
  recursionLimit: 5,
139
+ summarization: {
140
+ enabled: true,
141
+ maxTokensBeforeSummary: 18000,
142
+ messagesToKeep: 10
143
+ },
112
144
  search: {
113
145
  provider: "perplexity",
114
146
  maxResults: 10
@@ -128,6 +160,62 @@ describe("CLI Config Loader", ()=>{
128
160
  const config = loader.loadConfig();
129
161
  expect(config).toMatchObject(configData);
130
162
  });
163
+ it("should allow disabling summarization middleware", ()=>{
164
+ const configData = {
165
+ summarization: {
166
+ enabled: false
167
+ }
168
+ };
169
+ writeFileSync(join(configDir, "wingman.config.json"), JSON.stringify(configData));
170
+ const loader = new WingmanConfigLoader(".wingman", testDir);
171
+ const config = loader.loadConfig();
172
+ expect(config.summarization).toEqual({
173
+ enabled: false,
174
+ maxTokensBeforeSummary: 12000,
175
+ messagesToKeep: 8
176
+ });
177
+ });
178
+ it("should load retry and HITL middleware config", ()=>{
179
+ const configData = {
180
+ modelRetry: {
181
+ enabled: true,
182
+ maxRetries: 3,
183
+ backoffFactor: 1.5,
184
+ initialDelayMs: 250,
185
+ maxDelayMs: 4000,
186
+ jitter: false,
187
+ onFailure: "error"
188
+ },
189
+ toolRetry: {
190
+ enabled: true,
191
+ maxRetries: 4,
192
+ backoffFactor: 2,
193
+ initialDelayMs: 500,
194
+ maxDelayMs: 8000,
195
+ jitter: true,
196
+ onFailure: "continue",
197
+ tools: [
198
+ "internet_search",
199
+ "web_crawler"
200
+ ]
201
+ },
202
+ humanInTheLoop: {
203
+ enabled: true,
204
+ interruptOn: {
205
+ command_execute: {
206
+ allowedDecisions: [
207
+ "approve",
208
+ "reject"
209
+ ]
210
+ }
211
+ }
212
+ }
213
+ };
214
+ writeFileSync(join(configDir, "wingman.config.json"), JSON.stringify(configData));
215
+ const loader = new WingmanConfigLoader(".wingman", testDir);
216
+ const config = loader.loadConfig();
217
+ expect(config).toMatchObject(configData);
218
+ });
131
219
  it("should handle all valid log levels", ()=>{
132
220
  const levels = [
133
221
  "debug",
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
  var __webpack_exports__ = {};
3
- const external_vitest_namespaceObject = require("vitest");
4
- const init_cjs_namespaceObject = require("../cli/commands/init.cjs");
5
3
  const external_node_fs_namespaceObject = require("node:fs");
6
- const external_node_path_namespaceObject = require("node:path");
7
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 init_cjs_namespaceObject = require("../cli/commands/init.cjs");
8
8
  (0, external_vitest_namespaceObject.describe)("CLI init", ()=>{
9
9
  let workspace;
10
10
  (0, external_vitest_namespaceObject.beforeEach)(()=>{
@@ -39,6 +39,30 @@ const external_node_os_namespaceObject = require("node:os");
39
39
  (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(agentPath)).toBe(true);
40
40
  const agent = JSON.parse((0, external_node_fs_namespaceObject.readFileSync)(agentPath, "utf-8"));
41
41
  (0, external_vitest_namespaceObject.expect)(agent.name).toBe("wingman");
42
+ const codingAgentPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "coding", "agent.md");
43
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(codingAgentPath)).toBe(true);
44
+ const codingPrompt = (0, external_node_fs_namespaceObject.readFileSync)(codingAgentPath, "utf-8");
45
+ (0, external_vitest_namespaceObject.expect)(codingPrompt).toContain("write_todos");
46
+ (0, external_vitest_namespaceObject.expect)(codingPrompt).not.toContain("update_plan");
47
+ (0, external_vitest_namespaceObject.expect)(codingPrompt).not.toContain("subAgents:");
48
+ (0, external_vitest_namespaceObject.expect)(codingPrompt).toContain("Do not delegate coding work to subagents");
49
+ const codingV2AgentPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "coding-v2", "agent.md");
50
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(codingV2AgentPath)).toBe(true);
51
+ const codingV2Prompt = (0, external_node_fs_namespaceObject.readFileSync)(codingV2AgentPath, "utf-8");
52
+ (0, external_vitest_namespaceObject.expect)(codingV2Prompt).toContain("name: coding-v2");
53
+ (0, external_vitest_namespaceObject.expect)(codingV2Prompt).toContain("subAgents:");
54
+ (0, external_vitest_namespaceObject.expect)(codingV2Prompt).toContain("name: coding-worker");
55
+ (0, external_vitest_namespaceObject.expect)(codingV2Prompt).toContain("promptFile: ./implementor.md");
56
+ (0, external_vitest_namespaceObject.expect)(codingV2Prompt).toContain("`task` tool");
57
+ (0, external_vitest_namespaceObject.expect)(codingV2Prompt).toContain("write_todos");
58
+ const codingV2ImplementorPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "coding-v2", "implementor.md");
59
+ const codingV2PlannerPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "coding-v2", "planner.md");
60
+ const codingV2ReviewerPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "coding-v2", "reviewer.md");
61
+ const codingV2ResearcherPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "coding-v2", "researcher.md");
62
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(codingV2ImplementorPath)).toBe(true);
63
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(codingV2PlannerPath)).toBe(false);
64
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(codingV2ReviewerPath)).toBe(false);
65
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(codingV2ResearcherPath)).toBe(false);
42
66
  });
43
67
  (0, external_vitest_namespaceObject.it)("merges existing config when --merge is set", async ()=>{
44
68
  const configDir = (0, external_node_path_namespaceObject.join)(workspace, ".wingman");
@@ -1,8 +1,8 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
- import { executeInitCommand } from "../cli/commands/init.js";
3
1
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
- import { join } from "node:path";
5
2
  import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+ import { executeInitCommand } from "../cli/commands/init.js";
6
6
  describe("CLI init", ()=>{
7
7
  let workspace;
8
8
  beforeEach(()=>{
@@ -37,6 +37,30 @@ describe("CLI init", ()=>{
37
37
  expect(existsSync(agentPath)).toBe(true);
38
38
  const agent = JSON.parse(readFileSync(agentPath, "utf-8"));
39
39
  expect(agent.name).toBe("wingman");
40
+ const codingAgentPath = join(workspace, ".wingman", "agents", "coding", "agent.md");
41
+ expect(existsSync(codingAgentPath)).toBe(true);
42
+ const codingPrompt = readFileSync(codingAgentPath, "utf-8");
43
+ expect(codingPrompt).toContain("write_todos");
44
+ expect(codingPrompt).not.toContain("update_plan");
45
+ expect(codingPrompt).not.toContain("subAgents:");
46
+ expect(codingPrompt).toContain("Do not delegate coding work to subagents");
47
+ const codingV2AgentPath = join(workspace, ".wingman", "agents", "coding-v2", "agent.md");
48
+ expect(existsSync(codingV2AgentPath)).toBe(true);
49
+ const codingV2Prompt = readFileSync(codingV2AgentPath, "utf-8");
50
+ expect(codingV2Prompt).toContain("name: coding-v2");
51
+ expect(codingV2Prompt).toContain("subAgents:");
52
+ expect(codingV2Prompt).toContain("name: coding-worker");
53
+ expect(codingV2Prompt).toContain("promptFile: ./implementor.md");
54
+ expect(codingV2Prompt).toContain("`task` tool");
55
+ expect(codingV2Prompt).toContain("write_todos");
56
+ const codingV2ImplementorPath = join(workspace, ".wingman", "agents", "coding-v2", "implementor.md");
57
+ const codingV2PlannerPath = join(workspace, ".wingman", "agents", "coding-v2", "planner.md");
58
+ const codingV2ReviewerPath = join(workspace, ".wingman", "agents", "coding-v2", "reviewer.md");
59
+ const codingV2ResearcherPath = join(workspace, ".wingman", "agents", "coding-v2", "researcher.md");
60
+ expect(existsSync(codingV2ImplementorPath)).toBe(true);
61
+ expect(existsSync(codingV2PlannerPath)).toBe(false);
62
+ expect(existsSync(codingV2ReviewerPath)).toBe(false);
63
+ expect(existsSync(codingV2ResearcherPath)).toBe(false);
40
64
  });
41
65
  it("merges existing config when --merge is set", async ()=>{
42
66
  const configDir = join(workspace, ".wingman");
@@ -0,0 +1,94 @@
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
+ (0, external_vitest_namespaceObject.describe)("codex credential precedence", ()=>{
8
+ let homeDir;
9
+ let codexHome;
10
+ const originalHome = process.env.HOME;
11
+ const originalCodexHome = process.env.CODEX_HOME;
12
+ const originalCodexAccessToken = process.env.CODEX_ACCESS_TOKEN;
13
+ const originalChatGptAccessToken = process.env.CHATGPT_ACCESS_TOKEN;
14
+ (0, external_vitest_namespaceObject.beforeEach)(()=>{
15
+ homeDir = (0, external_node_fs_namespaceObject.mkdtempSync)((0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.tmpdir)(), "wingman-home-"));
16
+ codexHome = (0, external_node_fs_namespaceObject.mkdtempSync)((0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.tmpdir)(), "wingman-codex-home-"));
17
+ process.env.HOME = homeDir;
18
+ process.env.CODEX_HOME = codexHome;
19
+ delete process.env.CODEX_ACCESS_TOKEN;
20
+ delete process.env.CHATGPT_ACCESS_TOKEN;
21
+ });
22
+ (0, external_vitest_namespaceObject.afterEach)(()=>{
23
+ if (void 0 === originalHome) delete process.env.HOME;
24
+ else process.env.HOME = originalHome;
25
+ if (void 0 === originalCodexHome) delete process.env.CODEX_HOME;
26
+ else process.env.CODEX_HOME = originalCodexHome;
27
+ if (void 0 === originalCodexAccessToken) delete process.env.CODEX_ACCESS_TOKEN;
28
+ else process.env.CODEX_ACCESS_TOKEN = originalCodexAccessToken;
29
+ if (void 0 === originalChatGptAccessToken) delete process.env.CHATGPT_ACCESS_TOKEN;
30
+ else process.env.CHATGPT_ACCESS_TOKEN = originalChatGptAccessToken;
31
+ if ((0, external_node_fs_namespaceObject.existsSync)(homeDir)) (0, external_node_fs_namespaceObject.rmSync)(homeDir, {
32
+ recursive: true,
33
+ force: true
34
+ });
35
+ if ((0, external_node_fs_namespaceObject.existsSync)(codexHome)) (0, external_node_fs_namespaceObject.rmSync)(codexHome, {
36
+ recursive: true,
37
+ force: true
38
+ });
39
+ });
40
+ (0, external_vitest_namespaceObject.it)("prefers Codex auth file over stored Wingman credentials for codex", async ()=>{
41
+ external_vitest_namespaceObject.vi.resetModules();
42
+ const credentials = await import("../providers/credentials.cjs");
43
+ const codex = await import("../providers/codex.cjs");
44
+ const credsPath = credentials.getCredentialsPath();
45
+ (0, external_node_fs_namespaceObject.mkdirSync)((0, external_node_path_namespaceObject.dirname)(credsPath), {
46
+ recursive: true
47
+ });
48
+ (0, external_node_fs_namespaceObject.writeFileSync)(credsPath, JSON.stringify({
49
+ version: 1,
50
+ updatedAt: new Date().toISOString(),
51
+ providers: {
52
+ codex: {
53
+ apiKey: "stale-wingman-token"
54
+ }
55
+ }
56
+ }, null, 2));
57
+ const authPath = codex.getCodexAuthPath();
58
+ (0, external_node_fs_namespaceObject.mkdirSync)((0, external_node_path_namespaceObject.dirname)(authPath), {
59
+ recursive: true
60
+ });
61
+ (0, external_node_fs_namespaceObject.writeFileSync)(authPath, JSON.stringify({
62
+ tokens: {
63
+ access_token: "codex-file-token",
64
+ account_id: "acct_123"
65
+ }
66
+ }, null, 2));
67
+ const resolved = credentials.resolveProviderToken("codex");
68
+ (0, external_vitest_namespaceObject.expect)(resolved.token).toBe("codex-file-token");
69
+ (0, external_vitest_namespaceObject.expect)(resolved.source).toBe("credentials");
70
+ });
71
+ (0, external_vitest_namespaceObject.it)("still prefers env vars over Codex auth file", async ()=>{
72
+ process.env.CODEX_ACCESS_TOKEN = "env-token";
73
+ external_vitest_namespaceObject.vi.resetModules();
74
+ const credentials = await import("../providers/credentials.cjs");
75
+ const codex = await import("../providers/codex.cjs");
76
+ const authPath = codex.getCodexAuthPath();
77
+ (0, external_node_fs_namespaceObject.mkdirSync)((0, external_node_path_namespaceObject.dirname)(authPath), {
78
+ recursive: true
79
+ });
80
+ (0, external_node_fs_namespaceObject.writeFileSync)(authPath, JSON.stringify({
81
+ tokens: {
82
+ access_token: "codex-file-token"
83
+ }
84
+ }, null, 2));
85
+ const resolved = credentials.resolveProviderToken("codex");
86
+ (0, external_vitest_namespaceObject.expect)(resolved.token).toBe("env-token");
87
+ (0, external_vitest_namespaceObject.expect)(resolved.source).toBe("env");
88
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.readFileSync)(authPath, "utf-8")).toContain("codex-file-token");
89
+ });
90
+ });
91
+ for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
92
+ Object.defineProperty(exports, '__esModule', {
93
+ value: true
94
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,88 @@
1
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5
+ describe("codex credential precedence", ()=>{
6
+ let homeDir;
7
+ let codexHome;
8
+ const originalHome = process.env.HOME;
9
+ const originalCodexHome = process.env.CODEX_HOME;
10
+ const originalCodexAccessToken = process.env.CODEX_ACCESS_TOKEN;
11
+ const originalChatGptAccessToken = process.env.CHATGPT_ACCESS_TOKEN;
12
+ beforeEach(()=>{
13
+ homeDir = mkdtempSync(join(tmpdir(), "wingman-home-"));
14
+ codexHome = mkdtempSync(join(tmpdir(), "wingman-codex-home-"));
15
+ process.env.HOME = homeDir;
16
+ process.env.CODEX_HOME = codexHome;
17
+ delete process.env.CODEX_ACCESS_TOKEN;
18
+ delete process.env.CHATGPT_ACCESS_TOKEN;
19
+ });
20
+ afterEach(()=>{
21
+ if (void 0 === originalHome) delete process.env.HOME;
22
+ else process.env.HOME = originalHome;
23
+ if (void 0 === originalCodexHome) delete process.env.CODEX_HOME;
24
+ else process.env.CODEX_HOME = originalCodexHome;
25
+ if (void 0 === originalCodexAccessToken) delete process.env.CODEX_ACCESS_TOKEN;
26
+ else process.env.CODEX_ACCESS_TOKEN = originalCodexAccessToken;
27
+ if (void 0 === originalChatGptAccessToken) delete process.env.CHATGPT_ACCESS_TOKEN;
28
+ else process.env.CHATGPT_ACCESS_TOKEN = originalChatGptAccessToken;
29
+ if (existsSync(homeDir)) rmSync(homeDir, {
30
+ recursive: true,
31
+ force: true
32
+ });
33
+ if (existsSync(codexHome)) rmSync(codexHome, {
34
+ recursive: true,
35
+ force: true
36
+ });
37
+ });
38
+ it("prefers Codex auth file over stored Wingman credentials for codex", async ()=>{
39
+ vi.resetModules();
40
+ const credentials = await import("../providers/credentials.js");
41
+ const codex = await import("../providers/codex.js");
42
+ const credsPath = credentials.getCredentialsPath();
43
+ mkdirSync(dirname(credsPath), {
44
+ recursive: true
45
+ });
46
+ writeFileSync(credsPath, JSON.stringify({
47
+ version: 1,
48
+ updatedAt: new Date().toISOString(),
49
+ providers: {
50
+ codex: {
51
+ apiKey: "stale-wingman-token"
52
+ }
53
+ }
54
+ }, null, 2));
55
+ const authPath = codex.getCodexAuthPath();
56
+ mkdirSync(dirname(authPath), {
57
+ recursive: true
58
+ });
59
+ writeFileSync(authPath, JSON.stringify({
60
+ tokens: {
61
+ access_token: "codex-file-token",
62
+ account_id: "acct_123"
63
+ }
64
+ }, null, 2));
65
+ const resolved = credentials.resolveProviderToken("codex");
66
+ expect(resolved.token).toBe("codex-file-token");
67
+ expect(resolved.source).toBe("credentials");
68
+ });
69
+ it("still prefers env vars over Codex auth file", async ()=>{
70
+ process.env.CODEX_ACCESS_TOKEN = "env-token";
71
+ vi.resetModules();
72
+ const credentials = await import("../providers/credentials.js");
73
+ const codex = await import("../providers/codex.js");
74
+ const authPath = codex.getCodexAuthPath();
75
+ mkdirSync(dirname(authPath), {
76
+ recursive: true
77
+ });
78
+ writeFileSync(authPath, JSON.stringify({
79
+ tokens: {
80
+ access_token: "codex-file-token"
81
+ }
82
+ }, null, 2));
83
+ const resolved = credentials.resolveProviderToken("codex");
84
+ expect(resolved.token).toBe("env-token");
85
+ expect(resolved.source).toBe("env");
86
+ expect(readFileSync(authPath, "utf-8")).toContain("codex-file-token");
87
+ });
88
+ });