@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,15 +1,17 @@
1
- import { CompositeBackend, FilesystemBackend, createDeepAgent } from "deepagents";
2
1
  import { existsSync } from "node:fs";
3
2
  import { isAbsolute, join, normalize, sep } from "node:path";
3
+ import { CompositeBackend, FilesystemBackend, createDeepAgent } from "deepagents";
4
+ import { modelRetryMiddleware, summarizationMiddleware, toolRetryMiddleware } from "langchain";
4
5
  import { v4 } from "uuid";
5
- import { AgentLoader } from "../../agent/config/agentLoader.js";
6
- import { WingmanConfigLoader } from "../config/loader.js";
6
+ import { MCPClientManager } from "../../agent/config/mcpClientManager.js";
7
7
  import { additionalMessageMiddleware } from "../../agent/middleware/additional-messages.js";
8
- import { createHooksMiddleware } from "../../agent/middleware/hooks.js";
9
8
  import { mergeHooks } from "../../agent/middleware/hooks/merger.js";
9
+ import { createHooksMiddleware } from "../../agent/middleware/hooks.js";
10
10
  import { mediaCompatibilityMiddleware } from "../../agent/middleware/media-compat.js";
11
- import { MCPClientManager } from "../../agent/config/mcpClientManager.js";
11
+ import { getSharedTerminalSessionManager } from "../../agent/tools/terminal_session_manager.js";
12
12
  import { getBundledSkillsPath } from "../../agent/uiRegistry.js";
13
+ import { AgentLoader } from "../../agent/config/agentLoader.js";
14
+ import { WingmanConfigLoader } from "../config/loader.js";
13
15
  function _define_property(obj, key, value) {
14
16
  if (key in obj) Object.defineProperty(obj, key, {
15
17
  value: value,
@@ -22,6 +24,7 @@ function _define_property(obj, key, value) {
22
24
  }
23
25
  const WORKDIR_VIRTUAL_PATH = "/workdir/";
24
26
  const OUTPUT_VIRTUAL_PATH = "/output/";
27
+ const DEFAULT_DEEPAGENT_MODEL = "claude-sonnet-4-5-20250929";
25
28
  const isPathWithinRoot = (targetPath, rootPath)=>{
26
29
  const normalizedTarget = normalize(targetPath);
27
30
  const normalizedRoot = normalize(rootPath);
@@ -54,6 +57,206 @@ const resolveExternalOutputMount = (workspace, workdir, defaultOutputDir)=>{
54
57
  absolutePath: null
55
58
  };
56
59
  };
60
+ const resolveSummarizationMiddlewareSettings = (config)=>{
61
+ if (!config.summarization?.enabled) return null;
62
+ return {
63
+ maxTokensBeforeSummary: config.summarization.maxTokensBeforeSummary,
64
+ messagesToKeep: config.summarization.messagesToKeep
65
+ };
66
+ };
67
+ const resolveModelRetryMiddlewareSettings = (config)=>{
68
+ if (!config.modelRetry?.enabled) return null;
69
+ return {
70
+ maxRetries: config.modelRetry.maxRetries,
71
+ backoffFactor: config.modelRetry.backoffFactor,
72
+ initialDelayMs: config.modelRetry.initialDelayMs,
73
+ maxDelayMs: config.modelRetry.maxDelayMs,
74
+ jitter: config.modelRetry.jitter,
75
+ onFailure: config.modelRetry.onFailure
76
+ };
77
+ };
78
+ const resolveToolRetryMiddlewareSettings = (config)=>{
79
+ if (!config.toolRetry?.enabled) return null;
80
+ return {
81
+ maxRetries: config.toolRetry.maxRetries,
82
+ backoffFactor: config.toolRetry.backoffFactor,
83
+ initialDelayMs: config.toolRetry.initialDelayMs,
84
+ maxDelayMs: config.toolRetry.maxDelayMs,
85
+ jitter: config.toolRetry.jitter,
86
+ onFailure: config.toolRetry.onFailure,
87
+ ...config.toolRetry.tools && config.toolRetry.tools.length > 0 ? {
88
+ tools: config.toolRetry.tools
89
+ } : {}
90
+ };
91
+ };
92
+ const resolveHumanInTheLoopSettings = (config)=>{
93
+ if (!config.humanInTheLoop?.enabled) return null;
94
+ const interruptOn = config.humanInTheLoop.interruptOn || {};
95
+ if (0 === Object.keys(interruptOn).length) return null;
96
+ return {
97
+ interruptOn
98
+ };
99
+ };
100
+ const configureDeepAgentSummarizationMiddleware = (agent, settings, model)=>{
101
+ const middleware = agent?.options?.middleware;
102
+ if (!Array.isArray(middleware)) return;
103
+ const index = middleware.findIndex((entry)=>entry?.name === "SummarizationMiddleware");
104
+ if (index < 0) return;
105
+ if (!settings) return void middleware.splice(index, 1);
106
+ middleware[index] = summarizationMiddleware({
107
+ model: model || DEFAULT_DEEPAGENT_MODEL,
108
+ trigger: {
109
+ tokens: settings.maxTokensBeforeSummary
110
+ },
111
+ keep: {
112
+ messages: settings.messagesToKeep
113
+ }
114
+ });
115
+ };
116
+ const detectToolEventContext = (chunk)=>{
117
+ if (!chunk || "object" != typeof chunk || Array.isArray(chunk)) return null;
118
+ const eventChunk = chunk;
119
+ if ("on_tool_start" !== eventChunk.event && "on_tool_end" !== eventChunk.event && "on_tool_error" !== eventChunk.event) return null;
120
+ const toolName = "string" == typeof eventChunk.name && eventChunk.name.trim() ? eventChunk.name.trim() : "unknown";
121
+ return {
122
+ event: eventChunk.event,
123
+ toolName
124
+ };
125
+ };
126
+ const chunkHasAssistantText = (chunk)=>{
127
+ if (!chunk || "object" != typeof chunk || Array.isArray(chunk)) return false;
128
+ const eventChunk = chunk;
129
+ const eventName = "string" == typeof eventChunk.event ? eventChunk.event : void 0;
130
+ if ("on_chat_model_stream" === eventName) {
131
+ const data = eventChunk.data && "object" == typeof eventChunk.data ? eventChunk.data : null;
132
+ const messageChunk = data?.chunk || data?.message;
133
+ const content = messageChunk?.content;
134
+ if ("string" == typeof content) return content.length > 0;
135
+ if (Array.isArray(content)) return content.some((part)=>part && "object" == typeof part && "text" === part.type && "string" == typeof part.text && part.text.length > 0);
136
+ }
137
+ if ("on_llm_stream" === eventName) {
138
+ const data = eventChunk.data && "object" == typeof eventChunk.data ? eventChunk.data : null;
139
+ const llmChunk = data?.chunk && "object" == typeof data.chunk ? data.chunk : null;
140
+ return "string" == typeof llmChunk?.text && llmChunk.text.length > 0;
141
+ }
142
+ return false;
143
+ };
144
+ const selectStreamingFallbackText = (previousMessages, currentMessages)=>{
145
+ if (0 === currentMessages.length) return;
146
+ const previousAssistantCounts = new Map();
147
+ for (const message of previousMessages){
148
+ if (!message || "object" != typeof message) continue;
149
+ if ("assistant" !== message.role) continue;
150
+ if ("string" != typeof message.content) continue;
151
+ const content = message.content.trim();
152
+ if (content) previousAssistantCounts.set(content, (previousAssistantCounts.get(content) || 0) + 1);
153
+ }
154
+ let fallback;
155
+ for (const message of currentMessages){
156
+ if (!message || "object" != typeof message) continue;
157
+ if ("assistant" !== message.role) continue;
158
+ if ("string" != typeof message.content) continue;
159
+ const content = message.content.trim();
160
+ if (!content) continue;
161
+ const remaining = previousAssistantCounts.get(content) || 0;
162
+ if (remaining > 0) {
163
+ previousAssistantCounts.set(content, remaining - 1);
164
+ continue;
165
+ }
166
+ fallback = content;
167
+ }
168
+ return fallback;
169
+ };
170
+ const detectStreamErrorMessage = (chunk)=>{
171
+ if (!chunk || "object" != typeof chunk || Array.isArray(chunk)) return;
172
+ const eventChunk = chunk;
173
+ const eventName = "string" == typeof eventChunk.event ? eventChunk.event : void 0;
174
+ if (!eventName || !eventName.endsWith("_error")) return;
175
+ if ("on_tool_error" === eventName) return;
176
+ const data = eventChunk.data && "object" == typeof eventChunk.data ? eventChunk.data : null;
177
+ const errorPayload = data?.error || eventChunk.error || data?.output || data?.chunk;
178
+ if ("string" == typeof errorPayload && errorPayload.trim()) return errorPayload.trim();
179
+ if (errorPayload && "object" == typeof errorPayload) {
180
+ const record = errorPayload;
181
+ if ("string" == typeof record.message && record.message.trim()) return record.message.trim();
182
+ if ("string" == typeof record.error && record.error.trim()) return record.error.trim();
183
+ }
184
+ if (null != errorPayload) return String(errorPayload);
185
+ return eventName;
186
+ };
187
+ const extractStreamEventRecord = (chunk)=>{
188
+ if (!chunk || "object" != typeof chunk || Array.isArray(chunk)) return null;
189
+ const record = chunk;
190
+ return "string" == typeof record.event ? record : null;
191
+ };
192
+ const normalizeEventName = (value)=>{
193
+ if ("string" != typeof value) return;
194
+ const normalized = value.trim();
195
+ return normalized.length > 0 ? normalized.toLowerCase() : void 0;
196
+ };
197
+ const normalizeEventParentRunIds = (value)=>{
198
+ if (Array.isArray(value)) return value.filter((item)=>"string" == typeof item).map((item)=>item.trim()).filter(Boolean);
199
+ if ("string" == typeof value && value.trim()) return [
200
+ value.trim()
201
+ ];
202
+ return [];
203
+ };
204
+ const extractEventParentRunIds = (eventRecord)=>{
205
+ const parentCandidates = [
206
+ eventRecord.parent_ids,
207
+ eventRecord.parentIds,
208
+ eventRecord.metadata?.parent_ids,
209
+ eventRecord.metadata?.parentIds,
210
+ eventRecord.data?.parent_ids,
211
+ eventRecord.data?.parentIds
212
+ ];
213
+ for (const candidate of parentCandidates){
214
+ const parentIds = normalizeEventParentRunIds(candidate);
215
+ if (parentIds.length > 0) return parentIds;
216
+ }
217
+ return [];
218
+ };
219
+ const extractEventRunId = (eventRecord)=>{
220
+ const runCandidates = [
221
+ eventRecord.run_id,
222
+ eventRecord.runId,
223
+ eventRecord.data?.run_id,
224
+ eventRecord.data?.runId
225
+ ];
226
+ for (const candidate of runCandidates)if ("string" == typeof candidate && candidate.trim()) return candidate.trim();
227
+ };
228
+ const isRootLangGraphChainEvent = (eventRecord, eventType)=>{
229
+ if (eventRecord.event !== eventType) return false;
230
+ const eventName = normalizeEventName(eventRecord.name);
231
+ if ("langgraph" !== eventName) return false;
232
+ return 0 === extractEventParentRunIds(eventRecord).length;
233
+ };
234
+ const trackRootLangGraphRunId = (currentRootLangGraphRunId, chunk)=>{
235
+ if (currentRootLangGraphRunId) return currentRootLangGraphRunId;
236
+ const eventRecord = extractStreamEventRecord(chunk);
237
+ if (!eventRecord || !isRootLangGraphChainEvent(eventRecord, "on_chain_start")) return currentRootLangGraphRunId;
238
+ return extractEventRunId(eventRecord) || currentRootLangGraphRunId;
239
+ };
240
+ const isRootLangGraphTerminalEvent = (chunk, rootLangGraphRunId)=>{
241
+ if (!rootLangGraphRunId) return false;
242
+ const eventRecord = extractStreamEventRecord(chunk);
243
+ if (!eventRecord || !isRootLangGraphChainEvent(eventRecord, "on_chain_end")) return false;
244
+ const chunkRunId = extractEventRunId(eventRecord);
245
+ return Boolean(chunkRunId && chunkRunId === rootLangGraphRunId);
246
+ };
247
+ const evaluateStreamingCompletion = (input)=>{
248
+ if (!input.sawAssistantText && !input.fallbackText) {
249
+ const message = input.streamErrorMessage ? `Model call failed: ${input.streamErrorMessage}` : "Model completed without a response. Check provider logs for request errors.";
250
+ return {
251
+ status: "blocked",
252
+ reason: input.streamErrorMessage ? "stream_error" : "empty_stream_response",
253
+ message
254
+ };
255
+ }
256
+ return {
257
+ status: "ok"
258
+ };
259
+ };
57
260
  class AgentInvoker {
58
261
  findAllAgents() {
59
262
  const agentConfigs = this.loader.loadAllAgentConfigs();
@@ -64,11 +267,21 @@ class AgentInvoker {
64
267
  }
65
268
  async invokeAgent(agentName, prompt, sessionId, attachments, options) {
66
269
  let cancellationHandled = false;
270
+ let activeToolName = null;
271
+ let lastToolName = null;
272
+ let sawAssistantText = false;
273
+ let streamErrorMessage;
274
+ let rootLangGraphRunId;
275
+ let preInvocationMessages = null;
67
276
  const isCancelled = ()=>options?.signal?.aborted === true;
68
277
  try {
278
+ const hookSessionId = sessionId || v4();
69
279
  const executionWorkspace = resolveExecutionWorkspace(this.workspace, this.workdir);
70
280
  const effectiveWorkdir = this.workdir ? executionWorkspace : null;
71
- const loader = normalize(executionWorkspace) === normalize(this.workspace) ? this.loader : new AgentLoader(this.configDir, this.workspace, this.wingmanConfig, executionWorkspace);
281
+ const loader = new AgentLoader(this.configDir, this.workspace, this.wingmanConfig, executionWorkspace, {
282
+ terminalOwnerId: `${agentName}:${hookSessionId}`,
283
+ terminalSessionManager: this.terminalSessionManager
284
+ });
72
285
  const targetAgent = await loader.loadAgent(agentName);
73
286
  if (!targetAgent) throw new Error(`Agent "${agentName}" not found`);
74
287
  this.logger.info(`Invoking agent: ${agentName}`);
@@ -77,7 +290,6 @@ class AgentInvoker {
77
290
  this.logger.debug(`Found ${this.wingmanConfig.toolHooks ? "global hooks" : "no global hooks"}`);
78
291
  this.logger.debug(`Found ${targetAgent.toolHooks ? "agent-specific hooks" : "no agent-specific hooks"}`);
79
292
  const mergedHooks = mergeHooks(this.wingmanConfig.toolHooks, targetAgent.toolHooks);
80
- const hookSessionId = sessionId || v4();
81
293
  const mcpConfigs = [];
82
294
  if (targetAgent.mcpConfig) mcpConfigs.push(targetAgent.mcpConfig);
83
295
  if (targetAgent.mcpUseGlobal && this.wingmanConfig.mcp) mcpConfigs.push(this.wingmanConfig.mcp);
@@ -113,6 +325,29 @@ class AgentInvoker {
113
325
  skillsDirectory
114
326
  })
115
327
  ];
328
+ const summarizationSettings = resolveSummarizationMiddlewareSettings(this.wingmanConfig);
329
+ const modelRetrySettings = resolveModelRetryMiddlewareSettings(this.wingmanConfig);
330
+ if (modelRetrySettings) middleware.push(modelRetryMiddleware({
331
+ maxRetries: modelRetrySettings.maxRetries,
332
+ backoffFactor: modelRetrySettings.backoffFactor,
333
+ initialDelayMs: modelRetrySettings.initialDelayMs,
334
+ maxDelayMs: modelRetrySettings.maxDelayMs,
335
+ jitter: modelRetrySettings.jitter,
336
+ onFailure: modelRetrySettings.onFailure
337
+ }));
338
+ const toolRetrySettings = resolveToolRetryMiddlewareSettings(this.wingmanConfig);
339
+ if (toolRetrySettings) middleware.push(toolRetryMiddleware({
340
+ maxRetries: toolRetrySettings.maxRetries,
341
+ backoffFactor: toolRetrySettings.backoffFactor,
342
+ initialDelayMs: toolRetrySettings.initialDelayMs,
343
+ maxDelayMs: toolRetrySettings.maxDelayMs,
344
+ jitter: toolRetrySettings.jitter,
345
+ onFailure: toolRetrySettings.onFailure,
346
+ ...toolRetrySettings.tools ? {
347
+ tools: toolRetrySettings.tools
348
+ } : {}
349
+ }));
350
+ const hitlSettings = resolveHumanInTheLoopSettings(this.wingmanConfig);
116
351
  if (mergedHooks) {
117
352
  this.logger.debug(`Adding hooks middleware with ${mergedHooks.PreToolUse?.length || 0} PreToolUse hooks, ${mergedHooks.PostToolUse?.length || 0} PostToolUse hooks, and ${mergedHooks.Stop?.length || 0} Stop hooks`);
118
353
  middleware.push(createHooksMiddleware(mergedHooks, executionWorkspace, hookSessionId, this.logger));
@@ -159,14 +394,25 @@ class AgentInvoker {
159
394
  virtualMode: true
160
395
  }), backendOverrides),
161
396
  middleware: middleware,
397
+ interruptOn: hitlSettings?.interruptOn,
162
398
  skills: skillsSources,
163
399
  subagents: targetAgent.subagents || [],
164
400
  checkpointer: checkpointer
165
401
  });
402
+ configureDeepAgentSummarizationMiddleware(standaloneAgent, summarizationSettings, targetAgent.model);
166
403
  this.logger.debug("Agent created, sending message");
167
404
  const userContent = buildUserContent(prompt, attachments, targetAgent.model);
168
405
  if (this.sessionManager && sessionId) {
169
406
  this.logger.debug(`Using streaming with session: ${sessionId}`);
407
+ try {
408
+ const messages = await this.sessionManager.listMessages(sessionId);
409
+ preInvocationMessages = messages.map((message)=>({
410
+ role: message.role,
411
+ content: message.content
412
+ }));
413
+ } catch (stateError) {
414
+ this.logger.debug("Failed to capture pre-invocation session state", stateError);
415
+ }
170
416
  const stream = await standaloneAgent.streamEvents({
171
417
  messages: [
172
418
  {
@@ -183,6 +429,15 @@ class AgentInvoker {
183
429
  signal: options?.signal
184
430
  });
185
431
  for await (const chunk of stream){
432
+ rootLangGraphRunId = trackRootLangGraphRunId(rootLangGraphRunId, chunk);
433
+ if (!sawAssistantText && chunkHasAssistantText(chunk)) sawAssistantText = true;
434
+ if (!streamErrorMessage) streamErrorMessage = detectStreamErrorMessage(chunk);
435
+ const toolEvent = detectToolEventContext(chunk);
436
+ if (toolEvent) {
437
+ lastToolName = toolEvent.toolName;
438
+ if ("on_tool_start" === toolEvent.event) activeToolName = toolEvent.toolName;
439
+ else if (activeToolName === toolEvent.toolName) activeToolName = null;
440
+ }
186
441
  if (isCancelled()) {
187
442
  cancellationHandled = true;
188
443
  this.logger.info("Agent invocation cancelled");
@@ -193,6 +448,10 @@ class AgentInvoker {
193
448
  };
194
449
  }
195
450
  this.outputManager.emitAgentStream(chunk);
451
+ if (isRootLangGraphTerminalEvent(chunk, rootLangGraphRunId)) {
452
+ this.logger.debug("Detected root LangGraph on_chain_end event; finalizing stream without waiting for iterator shutdown");
453
+ break;
454
+ }
196
455
  }
197
456
  if (isCancelled()) {
198
457
  cancellationHandled = true;
@@ -202,9 +461,37 @@ class AgentInvoker {
202
461
  cancelled: true
203
462
  };
204
463
  }
205
- this.logger.info("Agent streaming completed successfully");
464
+ let fallbackText;
465
+ if (!sawAssistantText && this.sessionManager && sessionId && preInvocationMessages) try {
466
+ const sessionMessages = await this.sessionManager.listMessages(sessionId);
467
+ fallbackText = selectStreamingFallbackText(preInvocationMessages, sessionMessages.map((message)=>({
468
+ role: message.role,
469
+ content: message.content
470
+ })));
471
+ } catch (stateError) {
472
+ this.logger.debug("Failed to derive streaming fallback text from session state", stateError);
473
+ }
474
+ const completionOutcome = evaluateStreamingCompletion({
475
+ sawAssistantText,
476
+ fallbackText,
477
+ streamErrorMessage
478
+ });
479
+ if ("blocked" === completionOutcome.status) {
480
+ this.logger.warn(completionOutcome.message);
481
+ this.outputManager.emitAgentError(completionOutcome.message);
482
+ return {
483
+ blocked: true,
484
+ reason: completionOutcome.reason
485
+ };
486
+ }
487
+ this.logger.info("Agent streaming completed successfully", {
488
+ usedFallbackText: Boolean(fallbackText)
489
+ });
206
490
  this.outputManager.emitAgentComplete({
207
- streaming: true
491
+ streaming: true,
492
+ ...fallbackText ? {
493
+ fallbackText
494
+ } : {}
208
495
  });
209
496
  return {
210
497
  streaming: true
@@ -252,8 +539,10 @@ class AgentInvoker {
252
539
  cancelled: true
253
540
  };
254
541
  }
255
- this.logger.error(`Agent invocation failed: ${error instanceof Error ? error.message : String(error)}`);
256
- this.outputManager.emitAgentError(error);
542
+ this.logger.error(`Agent invocation failed: ${error instanceof Error ? error.message : String(error)}${activeToolName ? ` (while running tool "${activeToolName}")` : lastToolName ? ` (last tool: "${lastToolName}")` : ""}`);
543
+ const errorMessage = error instanceof Error ? error.message : String(error);
544
+ const errorWithToolContext = activeToolName ? `${errorMessage} (while running tool "${activeToolName}")` : lastToolName ? `${errorMessage} (last tool: "${lastToolName}")` : errorMessage;
545
+ this.outputManager.emitAgentError(errorWithToolContext);
257
546
  throw error;
258
547
  } finally{
259
548
  if (this.mcpManager) {
@@ -279,6 +568,7 @@ class AgentInvoker {
279
568
  _define_property(this, "wingmanConfig", void 0);
280
569
  _define_property(this, "mcpManager", null);
281
570
  _define_property(this, "sessionManager", null);
571
+ _define_property(this, "terminalSessionManager", void 0);
282
572
  _define_property(this, "workdir", null);
283
573
  _define_property(this, "defaultOutputDir", null);
284
574
  this.outputManager = options.outputManager;
@@ -286,6 +576,7 @@ class AgentInvoker {
286
576
  this.workspace = options.workspace || process.cwd();
287
577
  this.configDir = options.configDir || ".wingman";
288
578
  this.sessionManager = options.sessionManager || null;
579
+ this.terminalSessionManager = options.terminalSessionManager || getSharedTerminalSessionManager();
289
580
  this.workdir = options.workdir || null;
290
581
  this.defaultOutputDir = options.defaultOutputDir || null;
291
582
  const configLoader = new WingmanConfigLoader(this.configDir, this.workspace);
@@ -478,4 +769,4 @@ function buildAttachmentPreview(attachments) {
478
769
  if (hasImage) return "[image]";
479
770
  return "";
480
771
  }
481
- export { AgentInvoker, OUTPUT_VIRTUAL_PATH, WORKDIR_VIRTUAL_PATH, buildUserContent, resolveExecutionWorkspace, resolveExternalOutputMount, toWorkspaceAliasVirtualPath };
772
+ export { AgentInvoker, OUTPUT_VIRTUAL_PATH, WORKDIR_VIRTUAL_PATH, buildUserContent, chunkHasAssistantText, configureDeepAgentSummarizationMiddleware, detectStreamErrorMessage, detectToolEventContext, evaluateStreamingCompletion, isRootLangGraphTerminalEvent, resolveExecutionWorkspace, resolveExternalOutputMount, resolveHumanInTheLoopSettings, resolveModelRetryMiddlewareSettings, resolveSummarizationMiddlewareSettings, resolveToolRetryMiddlewareSettings, selectStreamingFallbackText, toWorkspaceAliasVirtualPath, trackRootLangGraphRunId };
@@ -68,6 +68,8 @@ class SessionManager {
68
68
 
69
69
  CREATE INDEX IF NOT EXISTS idx_sessions_updated ON sessions(updated_at DESC);
70
70
  CREATE INDEX IF NOT EXISTS idx_sessions_agent ON sessions(agent_name);
71
+ CREATE INDEX IF NOT EXISTS idx_sessions_status_updated ON sessions(status, updated_at DESC);
72
+ CREATE INDEX IF NOT EXISTS idx_sessions_status_agent_updated ON sessions(status, agent_name, updated_at DESC);
71
73
  `);
72
74
  }
73
75
  createSession(agentName, name) {
@@ -407,13 +409,38 @@ function extractContentBlocks(entry) {
407
409
  }
408
410
  function extractMessageContent(entry, blocks = []) {
409
411
  if (!entry || "object" != typeof entry) return "";
410
- if ("string" == typeof entry.content) return entry.content;
411
- if ("string" == typeof entry?.kwargs?.content) return entry.kwargs.content;
412
- if ("string" == typeof entry?.additional_kwargs?.content) return entry.additional_kwargs.content;
413
- if ("string" == typeof entry?.data?.content) return entry.data.content;
414
- if (blocks.length > 0) return blocks.filter((block)=>block && "text" === block.type && block.text).map((block)=>block.text).join("");
412
+ const candidates = [
413
+ entry.content,
414
+ entry?.kwargs?.content,
415
+ entry?.additional_kwargs?.content,
416
+ entry?.data?.content
417
+ ];
418
+ for (const candidate of candidates){
419
+ const extracted = extractTextContent(candidate);
420
+ if (extracted) return extracted;
421
+ }
422
+ if (blocks.length > 0) return extractTextContent(blocks);
423
+ return "";
424
+ }
425
+ function extractTextContent(value, depth = 0) {
426
+ if (depth > 5 || null == value) return "";
427
+ if ("string" == typeof value) return value;
428
+ if (Array.isArray(value)) return value.map((entry)=>extractTextContent(entry, depth + 1)).filter((entry)=>entry.length > 0).join("");
429
+ if ("object" != typeof value) return "";
430
+ const record = value;
431
+ if ("string" == typeof record.text) return record.text;
432
+ if (record.text && "object" == typeof record.text && "string" == typeof record.text.value) return record.text.value;
433
+ if ("string" == typeof record.output_text) return record.output_text;
434
+ if ("string" == typeof record.input_text) return record.input_text;
435
+ if ("string" == typeof record.value && isTextLikeContentType(record.type)) return record.value;
436
+ if ("content" in record) return extractTextContent(record.content, depth + 1);
415
437
  return "";
416
438
  }
439
+ function isTextLikeContentType(type) {
440
+ if ("string" != typeof type) return false;
441
+ const normalized = type.toLowerCase();
442
+ return "text" === normalized || "input_text" === normalized || "output_text" === normalized || "text_delta" === normalized;
443
+ }
417
444
  function isToolMessage(entry) {
418
445
  if (!entry || "object" != typeof entry) return false;
419
446
  const role = entry.role || entry?.kwargs?.role || entry?.additional_kwargs?.role;
@@ -35,6 +35,8 @@ class SessionManager {
35
35
 
36
36
  CREATE INDEX IF NOT EXISTS idx_sessions_updated ON sessions(updated_at DESC);
37
37
  CREATE INDEX IF NOT EXISTS idx_sessions_agent ON sessions(agent_name);
38
+ CREATE INDEX IF NOT EXISTS idx_sessions_status_updated ON sessions(status, updated_at DESC);
39
+ CREATE INDEX IF NOT EXISTS idx_sessions_status_agent_updated ON sessions(status, agent_name, updated_at DESC);
38
40
  `);
39
41
  }
40
42
  createSession(agentName, name) {
@@ -374,13 +376,38 @@ function extractContentBlocks(entry) {
374
376
  }
375
377
  function extractMessageContent(entry, blocks = []) {
376
378
  if (!entry || "object" != typeof entry) return "";
377
- if ("string" == typeof entry.content) return entry.content;
378
- if ("string" == typeof entry?.kwargs?.content) return entry.kwargs.content;
379
- if ("string" == typeof entry?.additional_kwargs?.content) return entry.additional_kwargs.content;
380
- if ("string" == typeof entry?.data?.content) return entry.data.content;
381
- if (blocks.length > 0) return blocks.filter((block)=>block && "text" === block.type && block.text).map((block)=>block.text).join("");
379
+ const candidates = [
380
+ entry.content,
381
+ entry?.kwargs?.content,
382
+ entry?.additional_kwargs?.content,
383
+ entry?.data?.content
384
+ ];
385
+ for (const candidate of candidates){
386
+ const extracted = extractTextContent(candidate);
387
+ if (extracted) return extracted;
388
+ }
389
+ if (blocks.length > 0) return extractTextContent(blocks);
390
+ return "";
391
+ }
392
+ function extractTextContent(value, depth = 0) {
393
+ if (depth > 5 || null == value) return "";
394
+ if ("string" == typeof value) return value;
395
+ if (Array.isArray(value)) return value.map((entry)=>extractTextContent(entry, depth + 1)).filter((entry)=>entry.length > 0).join("");
396
+ if ("object" != typeof value) return "";
397
+ const record = value;
398
+ if ("string" == typeof record.text) return record.text;
399
+ if (record.text && "object" == typeof record.text && "string" == typeof record.text.value) return record.text.value;
400
+ if ("string" == typeof record.output_text) return record.output_text;
401
+ if ("string" == typeof record.input_text) return record.input_text;
402
+ if ("string" == typeof record.value && isTextLikeContentType(record.type)) return record.value;
403
+ if ("content" in record) return extractTextContent(record.content, depth + 1);
382
404
  return "";
383
405
  }
406
+ function isTextLikeContentType(type) {
407
+ if ("string" != typeof type) return false;
408
+ const normalized = type.toLowerCase();
409
+ return "text" === normalized || "input_text" === normalized || "output_text" === normalized || "text_delta" === normalized;
410
+ }
384
411
  function isToolMessage(entry) {
385
412
  if (!entry || "object" != typeof entry) return false;
386
413
  const role = entry.role || entry?.kwargs?.role || entry?.additional_kwargs?.role;
@@ -151,6 +151,21 @@ function parseStreamEventChunk(chunk) {
151
151
  timestamp: Date.now()
152
152
  };
153
153
  }
154
+ if ("on_tool_error" === chunk.event) {
155
+ const toolId = "string" == typeof chunk.run_id ? chunk.run_id : void 0;
156
+ if (!toolId) return null;
157
+ const errorPayload = chunk.data?.error ?? chunk.error;
158
+ const error = "string" == typeof errorPayload ? errorPayload : "string" == typeof errorPayload?.message ? errorPayload.message : errorPayload ? String(errorPayload) : "Tool error";
159
+ return {
160
+ toolResult: {
161
+ id: toolId,
162
+ output: chunk.data?.output ?? "",
163
+ error
164
+ },
165
+ type: "tool-result",
166
+ timestamp: Date.now()
167
+ };
168
+ }
154
169
  return null;
155
170
  }
156
171
  function normalizeMessagesFromChunk(chunk) {
@@ -121,6 +121,21 @@ function parseStreamEventChunk(chunk) {
121
121
  timestamp: Date.now()
122
122
  };
123
123
  }
124
+ if ("on_tool_error" === chunk.event) {
125
+ const toolId = "string" == typeof chunk.run_id ? chunk.run_id : void 0;
126
+ if (!toolId) return null;
127
+ const errorPayload = chunk.data?.error ?? chunk.error;
128
+ const error = "string" == typeof errorPayload ? errorPayload : "string" == typeof errorPayload?.message ? errorPayload.message : errorPayload ? String(errorPayload) : "Tool error";
129
+ return {
130
+ toolResult: {
131
+ id: toolId,
132
+ output: chunk.data?.output ?? "",
133
+ error
134
+ },
135
+ type: "tool-result",
136
+ timestamp: Date.now()
137
+ };
138
+ }
124
139
  return null;
125
140
  }
126
141
  function normalizeMessagesFromChunk(chunk) {
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  var __webpack_exports__ = {};
4
- const external_logger_cjs_namespaceObject = require("../logger.cjs");
5
- const loader_cjs_namespaceObject = require("./config/loader.cjs");
6
4
  const env_cjs_namespaceObject = require("../gateway/env.cjs");
7
- const outputManager_cjs_namespaceObject = require("./core/outputManager.cjs");
5
+ const external_logger_cjs_namespaceObject = require("../logger.cjs");
8
6
  const agent_cjs_namespaceObject = require("./commands/agent.cjs");
9
- const skill_cjs_namespaceObject = require("./commands/skill.cjs");
10
7
  const gateway_cjs_namespaceObject = require("./commands/gateway.cjs");
11
- const provider_cjs_namespaceObject = require("./commands/provider.cjs");
12
8
  const init_cjs_namespaceObject = require("./commands/init.cjs");
9
+ const provider_cjs_namespaceObject = require("./commands/provider.cjs");
10
+ const skill_cjs_namespaceObject = require("./commands/skill.cjs");
11
+ const loader_cjs_namespaceObject = require("./config/loader.cjs");
12
+ const outputManager_cjs_namespaceObject = require("./core/outputManager.cjs");
13
13
  function parseArgs(argv) {
14
14
  const args = argv.slice(2);
15
15
  if (args.includes("--help") || args.includes("-h")) return {
@@ -114,6 +114,7 @@ Examples:
114
114
  wingman skill install pdf
115
115
  wingman skill list
116
116
  wingman provider status
117
+ wingman provider login codex
117
118
  wingman provider login copilot --token="<token>"
118
119
  wingman gateway start
119
120
  wingman gateway join ws://localhost:3000/ws --name="agent-1"
package/dist/cli/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { createLogger, getLogFilePath } from "../logger.js";
3
- import { WingmanConfigLoader } from "./config/loader.js";
4
2
  import { getGatewayTokenFromEnv } from "../gateway/env.js";
5
- import { OutputManager } from "./core/outputManager.js";
3
+ import { createLogger, getLogFilePath } from "../logger.js";
6
4
  import { executeAgentCommand } from "./commands/agent.js";
7
- import { executeSkillCommand } from "./commands/skill.js";
8
5
  import { executeGatewayCommand } from "./commands/gateway.js";
9
- import { executeProviderCommand } from "./commands/provider.js";
10
6
  import { executeInitCommand } from "./commands/init.js";
7
+ import { executeProviderCommand } from "./commands/provider.js";
8
+ import { executeSkillCommand } from "./commands/skill.js";
9
+ import { WingmanConfigLoader } from "./config/loader.js";
10
+ import { OutputManager } from "./core/outputManager.js";
11
11
  function parseArgs(argv) {
12
12
  const args = argv.slice(2);
13
13
  if (args.includes("--help") || args.includes("-h")) return {
@@ -112,6 +112,7 @@ Examples:
112
112
  wingman skill install pdf
113
113
  wingman skill list
114
114
  wingman provider status
115
+ wingman provider login codex
115
116
  wingman provider login copilot --token="<token>"
116
117
  wingman gateway start
117
118
  wingman gateway join ws://localhost:3000/ws --name="agent-1"