@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,8 +1,9 @@
1
- import type { GatewayRouter } from "../router.js";
2
- import type { GatewayAuth } from "../auth.js";
3
- import type { Logger } from "@/logger.js";
1
+ import type { TerminalSessionManager } from "@/agent/tools/terminal_session_manager.js";
4
2
  import type { WingmanConfigType } from "@/cli/config/schema.js";
5
3
  import type { SessionManager } from "@/cli/core/sessionManager.js";
4
+ import type { Logger } from "@/logger.js";
5
+ import type { GatewayAuth } from "../auth.js";
6
+ import type { GatewayRouter } from "../router.js";
6
7
  export type GatewayHttpContext = {
7
8
  workspace: string;
8
9
  configDir: string;
@@ -21,5 +22,6 @@ export type GatewayHttpContext = {
21
22
  resolveFsRoots: () => string[];
22
23
  resolveFsPath: (path: string) => string;
23
24
  isPathWithinRoots: (path: string, roots: string[]) => boolean;
25
+ getTerminalSessionManager: () => TerminalSessionManager;
24
26
  getBuiltInTools: () => string[];
25
27
  };
@@ -28,12 +28,12 @@ __webpack_require__.d(__webpack_exports__, {
28
28
  createWebhookStore: ()=>createWebhookStore,
29
29
  handleWebhookInvoke: ()=>handleWebhookInvoke
30
30
  });
31
- const agentInvoker_cjs_namespaceObject = require("../../cli/core/agentInvoker.cjs");
32
- const outputManager_cjs_namespaceObject = require("../../cli/core/outputManager.cjs");
33
- const agentLoader_cjs_namespaceObject = require("../../agent/config/agentLoader.cjs");
34
31
  const external_node_crypto_namespaceObject = require("node:crypto");
35
32
  const external_node_fs_namespaceObject = require("node:fs");
36
33
  const external_node_path_namespaceObject = require("node:path");
34
+ const agentLoader_cjs_namespaceObject = require("../../agent/config/agentLoader.cjs");
35
+ const agentInvoker_cjs_namespaceObject = require("../../cli/core/agentInvoker.cjs");
36
+ const outputManager_cjs_namespaceObject = require("../../cli/core/outputManager.cjs");
37
37
  const WEBHOOK_PRESETS = new Set([
38
38
  "gog-gmail"
39
39
  ]);
@@ -192,12 +192,12 @@ const handleWebhooksApi = async (ctx, store, req, url)=>{
192
192
  if (!agentExists(ctx, nextAgentId)) return new Response("Invalid agentId", {
193
193
  status: 400
194
194
  });
195
- const hasPreset = Object.prototype.hasOwnProperty.call(body ?? {}, "preset");
195
+ const hasPreset = Object.hasOwn(body ?? {}, "preset");
196
196
  const presetValue = hasPreset ? normalizePreset(body?.preset) : webhook.preset;
197
197
  if (hasPreset && body?.preset && !presetValue && "custom" !== body.preset.trim()) return new Response("Invalid preset", {
198
198
  status: 400
199
199
  });
200
- const hasSessionId = Object.prototype.hasOwnProperty.call(body ?? {}, "sessionId");
200
+ const hasSessionId = Object.hasOwn(body ?? {}, "sessionId");
201
201
  let nextSessionId = webhook.sessionId;
202
202
  if (hasSessionId) {
203
203
  const trimmed = body?.sessionId?.trim();
@@ -316,6 +316,7 @@ const handleWebhookInvoke = async (ctx, store, req, url)=>{
316
316
  outputManager,
317
317
  logger: ctx.logger,
318
318
  sessionManager,
319
+ terminalSessionManager: ctx.getTerminalSessionManager(),
319
320
  workdir,
320
321
  defaultOutputDir
321
322
  });
@@ -1,9 +1,9 @@
1
- import { AgentInvoker } from "../../cli/core/agentInvoker.js";
2
- import { OutputManager } from "../../cli/core/outputManager.js";
3
- import { AgentLoader } from "../../agent/config/agentLoader.js";
4
1
  import { randomUUID } from "node:crypto";
5
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
3
  import { join } from "node:path";
4
+ import { AgentLoader } from "../../agent/config/agentLoader.js";
5
+ import { AgentInvoker } from "../../cli/core/agentInvoker.js";
6
+ import { OutputManager } from "../../cli/core/outputManager.js";
7
7
  const WEBHOOK_PRESETS = new Set([
8
8
  "gog-gmail"
9
9
  ]);
@@ -162,12 +162,12 @@ const handleWebhooksApi = async (ctx, store, req, url)=>{
162
162
  if (!agentExists(ctx, nextAgentId)) return new Response("Invalid agentId", {
163
163
  status: 400
164
164
  });
165
- const hasPreset = Object.prototype.hasOwnProperty.call(body ?? {}, "preset");
165
+ const hasPreset = Object.hasOwn(body ?? {}, "preset");
166
166
  const presetValue = hasPreset ? normalizePreset(body?.preset) : webhook.preset;
167
167
  if (hasPreset && body?.preset && !presetValue && "custom" !== body.preset.trim()) return new Response("Invalid preset", {
168
168
  status: 400
169
169
  });
170
- const hasSessionId = Object.prototype.hasOwnProperty.call(body ?? {}, "sessionId");
170
+ const hasSessionId = Object.hasOwn(body ?? {}, "sessionId");
171
171
  let nextSessionId = webhook.sessionId;
172
172
  if (hasSessionId) {
173
173
  const trimmed = body?.sessionId?.trim();
@@ -286,6 +286,7 @@ const handleWebhookInvoke = async (ctx, store, req, url)=>{
286
286
  outputManager,
287
287
  logger: ctx.logger,
288
288
  sessionManager,
289
+ terminalSessionManager: ctx.getTerminalSessionManager(),
289
290
  workdir,
290
291
  defaultOutputDir
291
292
  });
@@ -29,31 +29,32 @@ __webpack_require__.r(__webpack_exports__);
29
29
  __webpack_require__.d(__webpack_exports__, {
30
30
  GatewayServer: ()=>GatewayServer
31
31
  });
32
- const external_node_cjs_namespaceObject = require("./node.cjs");
33
- const external_broadcast_cjs_namespaceObject = require("./broadcast.cjs");
34
- const external_auth_cjs_namespaceObject = require("./auth.cjs");
35
- const external_validation_cjs_namespaceObject = require("./validation.cjs");
36
- const external_env_cjs_namespaceObject = require("./env.cjs");
37
- const index_cjs_namespaceObject = require("./discovery/index.cjs");
38
- const external_logger_cjs_namespaceObject = require("../logger.cjs");
32
+ const external_node_fs_namespaceObject = require("node:fs");
33
+ const external_node_os_namespaceObject = require("node:os");
34
+ const external_node_path_namespaceObject = require("node:path");
35
+ const external_node_url_namespaceObject = require("node:url");
36
+ const terminal_session_manager_cjs_namespaceObject = require("../agent/tools/terminal_session_manager.cjs");
39
37
  const loader_cjs_namespaceObject = require("../cli/config/loader.cjs");
40
- const external_router_cjs_namespaceObject = require("./router.cjs");
41
- const outputManager_cjs_namespaceObject = require("../cli/core/outputManager.cjs");
42
38
  const agentInvoker_cjs_namespaceObject = require("../cli/core/agentInvoker.cjs");
39
+ const outputManager_cjs_namespaceObject = require("../cli/core/outputManager.cjs");
43
40
  const sessionManager_cjs_namespaceObject = require("../cli/core/sessionManager.cjs");
41
+ const external_logger_cjs_namespaceObject = require("../logger.cjs");
42
+ const discord_cjs_namespaceObject = require("./adapters/discord.cjs");
43
+ const external_auth_cjs_namespaceObject = require("./auth.cjs");
44
+ const external_broadcast_cjs_namespaceObject = require("./broadcast.cjs");
45
+ const index_cjs_namespaceObject = require("./discovery/index.cjs");
46
+ const external_env_cjs_namespaceObject = require("./env.cjs");
47
+ const registry_cjs_namespaceObject = require("./hooks/registry.cjs");
44
48
  const agents_cjs_namespaceObject = require("./http/agents.cjs");
45
49
  const fs_cjs_namespaceObject = require("./http/fs.cjs");
46
50
  const providers_cjs_namespaceObject = require("./http/providers.cjs");
47
- const voice_cjs_namespaceObject = require("./http/voice.cjs");
51
+ const routines_cjs_namespaceObject = require("./http/routines.cjs");
48
52
  const sessions_cjs_namespaceObject = require("./http/sessions.cjs");
53
+ const voice_cjs_namespaceObject = require("./http/voice.cjs");
49
54
  const webhooks_cjs_namespaceObject = require("./http/webhooks.cjs");
50
- const routines_cjs_namespaceObject = require("./http/routines.cjs");
51
- const discord_cjs_namespaceObject = require("./adapters/discord.cjs");
52
- const registry_cjs_namespaceObject = require("./hooks/registry.cjs");
53
- const external_node_os_namespaceObject = require("node:os");
54
- const external_node_path_namespaceObject = require("node:path");
55
- const external_node_fs_namespaceObject = require("node:fs");
56
- const external_node_url_namespaceObject = require("node:url");
55
+ const external_node_cjs_namespaceObject = require("./node.cjs");
56
+ const external_router_cjs_namespaceObject = require("./router.cjs");
57
+ const external_validation_cjs_namespaceObject = require("./validation.cjs");
57
58
  function _define_property(obj, key, value) {
58
59
  if (key in obj) Object.defineProperty(obj, key, {
59
60
  value: value,
@@ -107,6 +108,7 @@ class GatewayServer {
107
108
  });
108
109
  return;
109
110
  }
111
+ if (url.pathname.startsWith("/api/")) return this.handleUiRequest(req);
110
112
  const webhookResponse = await (0, webhooks_cjs_namespaceObject.handleWebhookInvoke)(this.getHttpContext(), this.webhookStore, req, url);
111
113
  if (webhookResponse) return webhookResponse;
112
114
  if (this.controlUiSamePort) return this.handleUiRequest(req);
@@ -163,6 +165,7 @@ class GatewayServer {
163
165
  this.uiServer.stop();
164
166
  this.uiServer = null;
165
167
  }
168
+ this.terminalSessionManager.dispose();
166
169
  this.log("info", "Gateway stopped");
167
170
  }
168
171
  getPort() {
@@ -350,6 +353,7 @@ class GatewayServer {
350
353
  existing?.abortController.abort();
351
354
  this.activeAgentRequests.delete(msg.id);
352
355
  }
356
+ this.removeQueuedRequestById(msg.id);
353
357
  const payload = msg.payload;
354
358
  const content = "string" == typeof payload?.content ? payload.content : "";
355
359
  const attachments = Array.isArray(payload?.attachments) ? payload.attachments : [];
@@ -359,6 +363,7 @@ class GatewayServer {
359
363
  const agentId = this.router.selectAgent(payload.agentId, payload.routing);
360
364
  if (!agentId) return void this.sendAgentError(ws, msg.id, "No agent matched the request");
361
365
  const sessionKey = payload.sessionKey || this.router.buildSessionKey(agentId, payload.routing);
366
+ const sessionQueueKey = this.buildSessionQueueKey(agentId, sessionKey);
362
367
  const sessionManager = await this.getSessionManager(agentId);
363
368
  const existingSession = sessionManager.getSession(sessionKey);
364
369
  const session = existingSession || sessionManager.getOrCreateSession(sessionKey, agentId);
@@ -409,9 +414,70 @@ class GatewayServer {
409
414
  ],
410
415
  skipSessionId: sessionKey
411
416
  });
417
+ const request = {
418
+ ws,
419
+ msg,
420
+ payload,
421
+ agentId,
422
+ sessionKey,
423
+ sessionQueueKey,
424
+ content,
425
+ attachments,
426
+ sessionManager,
427
+ workdir,
428
+ defaultOutputDir
429
+ };
430
+ this.requestSessionKeys.set(msg.id, sessionQueueKey);
431
+ const queueIfBusy = false !== payload.queueIfBusy;
432
+ const activeRequestId = this.activeSessionRequests.get(sessionQueueKey);
433
+ if (activeRequestId && queueIfBusy) {
434
+ const queued = this.queuedSessionRequests.get(sessionQueueKey) || [];
435
+ queued.push(request);
436
+ this.queuedSessionRequests.set(sessionQueueKey, queued);
437
+ const position = queued.length;
438
+ this.sendMessage(ws, {
439
+ type: "ack",
440
+ id: msg.id,
441
+ payload: {
442
+ action: "req:agent",
443
+ status: "queued",
444
+ requestId: msg.id,
445
+ sessionId: sessionKey,
446
+ agentId,
447
+ position
448
+ },
449
+ timestamp: Date.now()
450
+ });
451
+ this.sendMessage(ws, {
452
+ type: "event:agent",
453
+ id: msg.id,
454
+ clientId: ws.data.clientId,
455
+ payload: this.attachSessionContext({
456
+ type: "request-queued",
457
+ position
458
+ }, sessionKey, agentId),
459
+ timestamp: Date.now()
460
+ });
461
+ return;
462
+ }
463
+ if (activeRequestId && !queueIfBusy) {
464
+ this.requestSessionKeys.delete(msg.id);
465
+ this.sendAgentError(ws, msg.id, "Session already has an in-flight request. Set queueIfBusy=true to enqueue.", {
466
+ sessionId: sessionKey,
467
+ agentId
468
+ });
469
+ return;
470
+ }
471
+ this.executeAgentRequest(request);
472
+ }
473
+ async executeAgentRequest(request) {
474
+ const { ws, msg, agentId, sessionKey, sessionQueueKey, content, attachments, sessionManager, workdir, defaultOutputDir } = request;
475
+ this.activeSessionRequests.set(sessionQueueKey, msg.id);
412
476
  const outputManager = new outputManager_cjs_namespaceObject.OutputManager("interactive");
477
+ let emittedAgentError = false;
413
478
  const outputHandler = (event)=>{
414
479
  const payloadWithSession = this.attachSessionContext(event, sessionKey, agentId);
480
+ if (payloadWithSession && "object" == typeof payloadWithSession && !Array.isArray(payloadWithSession) && "agent-error" === payloadWithSession.type) emittedAgentError = true;
415
481
  const baseMessage = {
416
482
  type: "event:agent",
417
483
  id: msg.id,
@@ -432,6 +498,7 @@ class GatewayServer {
432
498
  outputManager,
433
499
  logger: this.logger,
434
500
  sessionManager,
501
+ terminalSessionManager: this.terminalSessionManager,
435
502
  workdir,
436
503
  defaultOutputDir
437
504
  });
@@ -450,41 +517,111 @@ class GatewayServer {
450
517
  });
451
518
  } catch (error) {
452
519
  this.logger.error("Agent invocation failed", error);
520
+ if (!emittedAgentError) {
521
+ const message = error instanceof Error ? error.message : String(error);
522
+ const stack = error instanceof Error ? error.stack : void 0;
523
+ this.sendAgentError(ws, msg.id, message, {
524
+ sessionId: sessionKey,
525
+ agentId,
526
+ stack,
527
+ broadcastToSession: true,
528
+ exclude: ws
529
+ });
530
+ }
453
531
  } finally{
454
532
  this.activeAgentRequests.delete(msg.id);
533
+ this.activeSessionRequests.delete(sessionQueueKey);
534
+ this.requestSessionKeys.delete(msg.id);
455
535
  outputManager.off("output-event", outputHandler);
536
+ this.processNextQueuedAgentRequest(sessionQueueKey);
456
537
  }
457
538
  }
539
+ processNextQueuedAgentRequest(sessionQueueKey) {
540
+ if (this.activeSessionRequests.has(sessionQueueKey)) return;
541
+ const queue = this.queuedSessionRequests.get(sessionQueueKey);
542
+ if (!queue || 0 === queue.length) return void this.queuedSessionRequests.delete(sessionQueueKey);
543
+ const next = queue.shift();
544
+ if (!next) return;
545
+ if (0 === queue.length) this.queuedSessionRequests.delete(sessionQueueKey);
546
+ else this.queuedSessionRequests.set(sessionQueueKey, queue);
547
+ this.sendMessage(next.ws, {
548
+ type: "ack",
549
+ id: next.msg.id,
550
+ payload: {
551
+ action: "req:agent",
552
+ status: "dequeued",
553
+ requestId: next.msg.id,
554
+ sessionId: next.sessionKey,
555
+ agentId: next.agentId,
556
+ remaining: queue.length
557
+ },
558
+ timestamp: Date.now()
559
+ });
560
+ this.executeAgentRequest(next);
561
+ }
458
562
  handleAgentCancel(ws, msg) {
459
563
  if (!ws.data.authenticated) return void this.sendError(ws, "AUTH_FAILED", "Client is not authenticated");
460
564
  const payload = msg.payload;
461
565
  const requestId = "string" == typeof payload?.requestId && payload.requestId || void 0;
462
566
  if (!requestId) return void this.sendError(ws, "INVALID_REQUEST", "Missing requestId for cancellation");
463
567
  const active = this.activeAgentRequests.get(requestId);
464
- if (!active) return void this.sendMessage(ws, {
465
- type: "ack",
466
- id: msg.id,
467
- payload: {
468
- action: "req:agent:cancel",
469
- requestId,
470
- status: "not_found"
471
- },
472
- timestamp: Date.now()
473
- });
474
- if (active.socket !== ws) return void this.sendError(ws, "FORBIDDEN", "Cannot cancel a request started by another client");
475
- active.abortController.abort();
476
- this.activeAgentRequests.delete(requestId);
568
+ if (active) {
569
+ if (active.socket !== ws) return void this.sendError(ws, "FORBIDDEN", "Cannot cancel a request started by another client");
570
+ active.abortController.abort();
571
+ this.activeAgentRequests.delete(requestId);
572
+ this.sendMessage(ws, {
573
+ type: "ack",
574
+ id: msg.id,
575
+ payload: {
576
+ action: "req:agent:cancel",
577
+ requestId,
578
+ status: "cancelled"
579
+ },
580
+ timestamp: Date.now()
581
+ });
582
+ return;
583
+ }
584
+ const queued = this.removeQueuedRequestById(requestId);
585
+ if (queued) {
586
+ if (queued.ws !== ws) return void this.sendError(ws, "FORBIDDEN", "Cannot cancel a request started by another client");
587
+ this.sendMessage(ws, {
588
+ type: "ack",
589
+ id: msg.id,
590
+ payload: {
591
+ action: "req:agent:cancel",
592
+ requestId,
593
+ status: "cancelled_queued"
594
+ },
595
+ timestamp: Date.now()
596
+ });
597
+ return;
598
+ }
477
599
  this.sendMessage(ws, {
478
600
  type: "ack",
479
601
  id: msg.id,
480
602
  payload: {
481
603
  action: "req:agent:cancel",
482
604
  requestId,
483
- status: "cancelled"
605
+ status: "not_found"
484
606
  },
485
607
  timestamp: Date.now()
486
608
  });
487
609
  }
610
+ buildSessionQueueKey(agentId, sessionKey) {
611
+ return `${agentId}:${sessionKey}`;
612
+ }
613
+ removeQueuedRequestById(requestId) {
614
+ for (const [queueKey, queue] of this.queuedSessionRequests){
615
+ const index = queue.findIndex((item)=>item.msg.id === requestId);
616
+ if (-1 === index) continue;
617
+ const [removed] = queue.splice(index, 1);
618
+ if (0 === queue.length) this.queuedSessionRequests.delete(queueKey);
619
+ else this.queuedSessionRequests.set(queueKey, queue);
620
+ this.requestSessionKeys.delete(requestId);
621
+ return removed || null;
622
+ }
623
+ return null;
624
+ }
488
625
  handleRegister(ws, msg) {
489
626
  const payload = msg.payload;
490
627
  if (!this.auth.validate({
@@ -647,23 +784,39 @@ class GatewayServer {
647
784
  timestamp: Date.now()
648
785
  });
649
786
  }
650
- sendAgentError(ws, requestId, message) {
651
- this.sendMessage(ws, {
787
+ sendAgentError(ws, requestId, message, options) {
788
+ let payload = {
789
+ type: "agent-error",
790
+ error: message,
791
+ timestamp: new Date().toISOString()
792
+ };
793
+ if (options?.stack) payload.stack = options.stack;
794
+ if (options?.sessionId && options?.agentId) payload = this.attachSessionContext(payload, options.sessionId, options.agentId);
795
+ const baseMessage = {
652
796
  type: "event:agent",
653
797
  id: requestId,
654
- payload: {
655
- type: "agent-error",
656
- error: message,
657
- timestamp: new Date().toISOString()
658
- },
798
+ payload,
659
799
  timestamp: Date.now()
800
+ };
801
+ this.sendMessage(ws, {
802
+ ...baseMessage,
803
+ clientId: ws.data.clientId
660
804
  });
805
+ if (options?.broadcastToSession && options.sessionId) this.broadcastSessionEvent(options.sessionId, baseMessage, options.exclude);
661
806
  }
662
807
  cancelSocketAgentRequests(ws) {
663
808
  for (const [requestId, active] of this.activeAgentRequests)if (active.socket === ws) {
664
809
  active.abortController.abort();
665
810
  this.activeAgentRequests.delete(requestId);
666
811
  }
812
+ for (const [queueKey, queue] of this.queuedSessionRequests){
813
+ const nextQueue = queue.filter((request)=>request.ws !== ws);
814
+ if (nextQueue.length !== queue.length) {
815
+ for (const request of queue)if (request.ws === ws && request.msg.id) this.requestSessionKeys.delete(request.msg.id);
816
+ if (0 === nextQueue.length) this.queuedSessionRequests.delete(queueKey);
817
+ else this.queuedSessionRequests.set(queueKey, nextQueue);
818
+ }
819
+ }
667
820
  }
668
821
  attachSessionContext(event, sessionId, agentId) {
669
822
  if (event && "object" == typeof event && !Array.isArray(event)) return {
@@ -783,6 +936,7 @@ class GatewayServer {
783
936
  resolveFsRoots: ()=>this.resolveFsRoots(),
784
937
  resolveFsPath: (path)=>this.resolveFsPath(path),
785
938
  isPathWithinRoots: (path, roots)=>this.isPathWithinRoots(path, roots),
939
+ getTerminalSessionManager: ()=>this.terminalSessionManager,
786
940
  getBuiltInTools: ()=>this.getBuiltInTools()
787
941
  };
788
942
  }
@@ -855,9 +1009,7 @@ class GatewayServer {
855
1009
  ];
856
1010
  for (const candidate of candidates)try {
857
1011
  if ((0, external_node_fs_namespaceObject.existsSync)(candidate) && (0, external_node_fs_namespaceObject.statSync)(candidate).isDirectory() && (0, external_node_fs_namespaceObject.existsSync)((0, external_node_path_namespaceObject.join)(candidate, "index.html"))) return candidate;
858
- } catch {
859
- continue;
860
- }
1012
+ } catch {}
861
1013
  return null;
862
1014
  }
863
1015
  async getSessionManager(agentId) {
@@ -1181,6 +1333,10 @@ class GatewayServer {
1181
1333
  _define_property(this, "socketSubscriptions", new Map());
1182
1334
  _define_property(this, "connectedClients", new Set());
1183
1335
  _define_property(this, "activeAgentRequests", new Map());
1336
+ _define_property(this, "activeSessionRequests", new Map());
1337
+ _define_property(this, "queuedSessionRequests", new Map());
1338
+ _define_property(this, "requestSessionKeys", new Map());
1339
+ _define_property(this, "terminalSessionManager", void 0);
1184
1340
  _define_property(this, "bridgeQueues", new Map());
1185
1341
  _define_property(this, "bridgePollWaiters", new Map());
1186
1342
  this.workspace = config.workspace || process.cwd();
@@ -1231,6 +1387,7 @@ class GatewayServer {
1231
1387
  this.controlUiPort = controlUi?.port || 18790;
1232
1388
  this.controlUiSamePort = this.controlUiEnabled && this.controlUiPort === this.config.port;
1233
1389
  this.uiDistDir = this.controlUiEnabled ? this.resolveControlUiDir() : null;
1390
+ this.terminalSessionManager = new terminal_session_manager_cjs_namespaceObject.TerminalSessionManager();
1234
1391
  }
1235
1392
  }
1236
1393
  function buildAttachmentPreview(attachments) {
@@ -1,5 +1,5 @@
1
- import type { GatewayConfig } from "./types.js";
2
1
  import { GatewayAuth } from "./auth.js";
2
+ import type { GatewayConfig } from "./types.js";
3
3
  /**
4
4
  * Wingman Gateway Server
5
5
  * Manages WebSocket connections for AI agent swarming
@@ -34,6 +34,10 @@ export declare class GatewayServer {
34
34
  private socketSubscriptions;
35
35
  private connectedClients;
36
36
  private activeAgentRequests;
37
+ private activeSessionRequests;
38
+ private queuedSessionRequests;
39
+ private requestSessionKeys;
40
+ private terminalSessionManager;
37
41
  private bridgeQueues;
38
42
  private bridgePollWaiters;
39
43
  constructor(config?: Partial<GatewayConfig>);
@@ -76,7 +80,11 @@ export declare class GatewayServer {
76
80
  * Handle agent execution request
77
81
  */
78
82
  private handleAgentRequest;
83
+ private executeAgentRequest;
84
+ private processNextQueuedAgentRequest;
79
85
  private handleAgentCancel;
86
+ private buildSessionQueueKey;
87
+ private removeQueuedRequestById;
80
88
  /**
81
89
  * Handle node registration
82
90
  */