@wingman-ai/gateway 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/.wingman/agents/README.md +1 -0
  2. package/.wingman/agents/coding/agent.md +179 -112
  3. package/.wingman/agents/coding/implementor.md +50 -3
  4. package/.wingman/agents/main/agent.md +4 -0
  5. package/README.md +1 -0
  6. package/dist/agent/config/agentConfig.cjs +30 -1
  7. package/dist/agent/config/agentConfig.js +30 -1
  8. package/dist/agent/config/modelFactory.cjs +22 -2
  9. package/dist/agent/config/modelFactory.d.ts +2 -0
  10. package/dist/agent/config/modelFactory.js +22 -2
  11. package/dist/agent/tests/agentConfig.test.cjs +39 -0
  12. package/dist/agent/tests/agentConfig.test.js +39 -0
  13. package/dist/agent/tests/modelFactory.test.cjs +12 -5
  14. package/dist/agent/tests/modelFactory.test.js +12 -5
  15. package/dist/cli/commands/init.cjs +7 -6
  16. package/dist/cli/commands/init.js +7 -6
  17. package/dist/cli/commands/provider.cjs +17 -3
  18. package/dist/cli/commands/provider.js +17 -3
  19. package/dist/cli/config/loader.cjs +27 -0
  20. package/dist/cli/config/loader.js +27 -0
  21. package/dist/cli/config/schema.cjs +80 -2
  22. package/dist/cli/config/schema.d.ts +88 -0
  23. package/dist/cli/config/schema.js +67 -1
  24. package/dist/cli/core/agentInvoker.cjs +242 -17
  25. package/dist/cli/core/agentInvoker.d.ts +46 -4
  26. package/dist/cli/core/agentInvoker.js +214 -13
  27. package/dist/cli/core/sessionManager.cjs +32 -5
  28. package/dist/cli/core/sessionManager.js +32 -5
  29. package/dist/cli/index.cjs +6 -5
  30. package/dist/cli/index.js +6 -5
  31. package/dist/cli/types.d.ts +32 -0
  32. package/dist/gateway/http/sessions.cjs +7 -7
  33. package/dist/gateway/http/sessions.js +7 -7
  34. package/dist/gateway/server.cjs +230 -28
  35. package/dist/gateway/server.d.ts +11 -1
  36. package/dist/gateway/server.js +230 -28
  37. package/dist/gateway/types.d.ts +5 -1
  38. package/dist/gateway/validation.cjs +1 -0
  39. package/dist/gateway/validation.d.ts +2 -0
  40. package/dist/gateway/validation.js +1 -0
  41. package/dist/providers/codex.cjs +167 -0
  42. package/dist/providers/codex.d.ts +15 -0
  43. package/dist/providers/codex.js +127 -0
  44. package/dist/providers/credentials.cjs +8 -0
  45. package/dist/providers/credentials.js +8 -0
  46. package/dist/providers/registry.cjs +11 -0
  47. package/dist/providers/registry.d.ts +1 -1
  48. package/dist/providers/registry.js +11 -0
  49. package/dist/tests/agentInvokerSummarization.test.cjs +296 -0
  50. package/dist/tests/agentInvokerSummarization.test.d.ts +1 -0
  51. package/dist/tests/agentInvokerSummarization.test.js +290 -0
  52. package/dist/tests/cli-config-loader.test.cjs +88 -0
  53. package/dist/tests/cli-config-loader.test.js +88 -0
  54. package/dist/tests/codex-credentials-precedence.test.cjs +94 -0
  55. package/dist/tests/codex-credentials-precedence.test.d.ts +1 -0
  56. package/dist/tests/codex-credentials-precedence.test.js +88 -0
  57. package/dist/tests/codex-provider.test.cjs +186 -0
  58. package/dist/tests/codex-provider.test.d.ts +1 -0
  59. package/dist/tests/codex-provider.test.js +180 -0
  60. package/dist/tests/gateway.test.cjs +173 -1
  61. package/dist/tests/gateway.test.js +173 -1
  62. package/dist/tests/provider-command-codex.test.cjs +57 -0
  63. package/dist/tests/provider-command-codex.test.d.ts +1 -0
  64. package/dist/tests/provider-command-codex.test.js +51 -0
  65. package/dist/tests/sessionStateMessages.test.cjs +38 -0
  66. package/dist/tests/sessionStateMessages.test.js +38 -0
  67. package/dist/webui/assets/index-BVMavpud.css +11 -0
  68. package/dist/webui/assets/index-DCB2aVVf.js +182 -0
  69. package/dist/webui/index.html +2 -2
  70. package/package.json +3 -1
  71. package/dist/webui/assets/index-BytPznA_.css +0 -1
  72. package/dist/webui/assets/index-u_5qlVip.js +0 -176
@@ -29,31 +29,31 @@ __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");
39
36
  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
37
  const agentInvoker_cjs_namespaceObject = require("../cli/core/agentInvoker.cjs");
38
+ const outputManager_cjs_namespaceObject = require("../cli/core/outputManager.cjs");
43
39
  const sessionManager_cjs_namespaceObject = require("../cli/core/sessionManager.cjs");
40
+ const external_logger_cjs_namespaceObject = require("../logger.cjs");
41
+ const discord_cjs_namespaceObject = require("./adapters/discord.cjs");
42
+ const external_auth_cjs_namespaceObject = require("./auth.cjs");
43
+ const external_broadcast_cjs_namespaceObject = require("./broadcast.cjs");
44
+ const index_cjs_namespaceObject = require("./discovery/index.cjs");
45
+ const external_env_cjs_namespaceObject = require("./env.cjs");
46
+ const registry_cjs_namespaceObject = require("./hooks/registry.cjs");
44
47
  const agents_cjs_namespaceObject = require("./http/agents.cjs");
45
48
  const fs_cjs_namespaceObject = require("./http/fs.cjs");
46
49
  const providers_cjs_namespaceObject = require("./http/providers.cjs");
47
- const voice_cjs_namespaceObject = require("./http/voice.cjs");
50
+ const routines_cjs_namespaceObject = require("./http/routines.cjs");
48
51
  const sessions_cjs_namespaceObject = require("./http/sessions.cjs");
52
+ const voice_cjs_namespaceObject = require("./http/voice.cjs");
49
53
  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");
54
+ const external_node_cjs_namespaceObject = require("./node.cjs");
55
+ const external_router_cjs_namespaceObject = require("./router.cjs");
56
+ const external_validation_cjs_namespaceObject = require("./validation.cjs");
57
57
  function _define_property(obj, key, value) {
58
58
  if (key in obj) Object.defineProperty(obj, key, {
59
59
  value: value,
@@ -249,6 +249,7 @@ class GatewayServer {
249
249
  const nodeId = ws.data.nodeId;
250
250
  if ("connect" === msg.type) return void this.handleConnect(ws, msg);
251
251
  if ("req:agent" === msg.type) return void this.handleAgentRequest(ws, msg);
252
+ if ("req:agent:cancel" === msg.type) return void this.handleAgentCancel(ws, msg);
252
253
  if (nodeId && "register" !== msg.type && "ping" !== msg.type && "pong" !== msg.type) {
253
254
  if (this.nodeManager.isRateLimited(nodeId)) return void this.sendError(ws, "RATE_LIMITED", "Too many messages. Please slow down.");
254
255
  this.nodeManager.recordMessage(nodeId);
@@ -303,6 +304,7 @@ class GatewayServer {
303
304
  }
304
305
  this.connectedClients.delete(ws);
305
306
  this.clearSessionSubscriptions(ws);
307
+ this.cancelSocketAgentRequests(ws);
306
308
  }
307
309
  handleDrain(ws) {
308
310
  this.log("debug", "WebSocket drained");
@@ -343,6 +345,12 @@ class GatewayServer {
343
345
  async handleAgentRequest(ws, msg) {
344
346
  if (!msg.id) return void this.sendError(ws, "INVALID_REQUEST", "Missing request id");
345
347
  if (!ws.data.authenticated) return void this.sendAgentError(ws, msg.id, "Client is not authenticated");
348
+ if (this.activeAgentRequests.has(msg.id)) {
349
+ const existing = this.activeAgentRequests.get(msg.id);
350
+ existing?.abortController.abort();
351
+ this.activeAgentRequests.delete(msg.id);
352
+ }
353
+ this.removeQueuedRequestById(msg.id);
346
354
  const payload = msg.payload;
347
355
  const content = "string" == typeof payload?.content ? payload.content : "";
348
356
  const attachments = Array.isArray(payload?.attachments) ? payload.attachments : [];
@@ -352,6 +360,7 @@ class GatewayServer {
352
360
  const agentId = this.router.selectAgent(payload.agentId, payload.routing);
353
361
  if (!agentId) return void this.sendAgentError(ws, msg.id, "No agent matched the request");
354
362
  const sessionKey = payload.sessionKey || this.router.buildSessionKey(agentId, payload.routing);
363
+ const sessionQueueKey = this.buildSessionQueueKey(agentId, sessionKey);
355
364
  const sessionManager = await this.getSessionManager(agentId);
356
365
  const existingSession = sessionManager.getSession(sessionKey);
357
366
  const session = existingSession || sessionManager.getOrCreateSession(sessionKey, agentId);
@@ -402,9 +411,70 @@ class GatewayServer {
402
411
  ],
403
412
  skipSessionId: sessionKey
404
413
  });
414
+ const request = {
415
+ ws,
416
+ msg,
417
+ payload,
418
+ agentId,
419
+ sessionKey,
420
+ sessionQueueKey,
421
+ content,
422
+ attachments,
423
+ sessionManager,
424
+ workdir,
425
+ defaultOutputDir
426
+ };
427
+ this.requestSessionKeys.set(msg.id, sessionQueueKey);
428
+ const queueIfBusy = false !== payload.queueIfBusy;
429
+ const activeRequestId = this.activeSessionRequests.get(sessionQueueKey);
430
+ if (activeRequestId && queueIfBusy) {
431
+ const queued = this.queuedSessionRequests.get(sessionQueueKey) || [];
432
+ queued.push(request);
433
+ this.queuedSessionRequests.set(sessionQueueKey, queued);
434
+ const position = queued.length;
435
+ this.sendMessage(ws, {
436
+ type: "ack",
437
+ id: msg.id,
438
+ payload: {
439
+ action: "req:agent",
440
+ status: "queued",
441
+ requestId: msg.id,
442
+ sessionId: sessionKey,
443
+ agentId,
444
+ position
445
+ },
446
+ timestamp: Date.now()
447
+ });
448
+ this.sendMessage(ws, {
449
+ type: "event:agent",
450
+ id: msg.id,
451
+ clientId: ws.data.clientId,
452
+ payload: this.attachSessionContext({
453
+ type: "request-queued",
454
+ position
455
+ }, sessionKey, agentId),
456
+ timestamp: Date.now()
457
+ });
458
+ return;
459
+ }
460
+ if (activeRequestId && !queueIfBusy) {
461
+ this.requestSessionKeys.delete(msg.id);
462
+ this.sendAgentError(ws, msg.id, "Session already has an in-flight request. Set queueIfBusy=true to enqueue.", {
463
+ sessionId: sessionKey,
464
+ agentId
465
+ });
466
+ return;
467
+ }
468
+ this.executeAgentRequest(request);
469
+ }
470
+ async executeAgentRequest(request) {
471
+ const { ws, msg, agentId, sessionKey, sessionQueueKey, content, attachments, sessionManager, workdir, defaultOutputDir } = request;
472
+ this.activeSessionRequests.set(sessionQueueKey, msg.id);
405
473
  const outputManager = new outputManager_cjs_namespaceObject.OutputManager("interactive");
474
+ let emittedAgentError = false;
406
475
  const outputHandler = (event)=>{
407
476
  const payloadWithSession = this.attachSessionContext(event, sessionKey, agentId);
477
+ if (payloadWithSession && "object" == typeof payloadWithSession && !Array.isArray(payloadWithSession) && "agent-error" === payloadWithSession.type) emittedAgentError = true;
408
478
  const baseMessage = {
409
479
  type: "event:agent",
410
480
  id: msg.id,
@@ -428,18 +498,126 @@ class GatewayServer {
428
498
  workdir,
429
499
  defaultOutputDir
430
500
  });
501
+ const abortController = new AbortController();
502
+ this.activeAgentRequests.set(msg.id, {
503
+ socket: ws,
504
+ abortController
505
+ });
431
506
  try {
432
- await invoker.invokeAgent(agentId, content, sessionKey, attachments);
507
+ await invoker.invokeAgent(agentId, content, sessionKey, attachments, {
508
+ signal: abortController.signal
509
+ });
433
510
  const updated = sessionManager.getSession(sessionKey);
434
511
  if (updated) sessionManager.updateSession(sessionKey, {
435
512
  messageCount: updated.messageCount + 1
436
513
  });
437
514
  } catch (error) {
438
515
  this.logger.error("Agent invocation failed", error);
516
+ if (!emittedAgentError) {
517
+ const message = error instanceof Error ? error.message : String(error);
518
+ const stack = error instanceof Error ? error.stack : void 0;
519
+ this.sendAgentError(ws, msg.id, message, {
520
+ sessionId: sessionKey,
521
+ agentId,
522
+ stack,
523
+ broadcastToSession: true,
524
+ exclude: ws
525
+ });
526
+ }
439
527
  } finally{
528
+ this.activeAgentRequests.delete(msg.id);
529
+ this.activeSessionRequests.delete(sessionQueueKey);
530
+ this.requestSessionKeys.delete(msg.id);
440
531
  outputManager.off("output-event", outputHandler);
532
+ this.processNextQueuedAgentRequest(sessionQueueKey);
441
533
  }
442
534
  }
535
+ processNextQueuedAgentRequest(sessionQueueKey) {
536
+ if (this.activeSessionRequests.has(sessionQueueKey)) return;
537
+ const queue = this.queuedSessionRequests.get(sessionQueueKey);
538
+ if (!queue || 0 === queue.length) return void this.queuedSessionRequests.delete(sessionQueueKey);
539
+ const next = queue.shift();
540
+ if (!next) return;
541
+ if (0 === queue.length) this.queuedSessionRequests.delete(sessionQueueKey);
542
+ else this.queuedSessionRequests.set(sessionQueueKey, queue);
543
+ this.sendMessage(next.ws, {
544
+ type: "ack",
545
+ id: next.msg.id,
546
+ payload: {
547
+ action: "req:agent",
548
+ status: "dequeued",
549
+ requestId: next.msg.id,
550
+ sessionId: next.sessionKey,
551
+ agentId: next.agentId,
552
+ remaining: queue.length
553
+ },
554
+ timestamp: Date.now()
555
+ });
556
+ this.executeAgentRequest(next);
557
+ }
558
+ handleAgentCancel(ws, msg) {
559
+ if (!ws.data.authenticated) return void this.sendError(ws, "AUTH_FAILED", "Client is not authenticated");
560
+ const payload = msg.payload;
561
+ const requestId = "string" == typeof payload?.requestId && payload.requestId || void 0;
562
+ if (!requestId) return void this.sendError(ws, "INVALID_REQUEST", "Missing requestId for cancellation");
563
+ const active = this.activeAgentRequests.get(requestId);
564
+ if (active) {
565
+ if (active.socket !== ws) return void this.sendError(ws, "FORBIDDEN", "Cannot cancel a request started by another client");
566
+ active.abortController.abort();
567
+ this.activeAgentRequests.delete(requestId);
568
+ this.sendMessage(ws, {
569
+ type: "ack",
570
+ id: msg.id,
571
+ payload: {
572
+ action: "req:agent:cancel",
573
+ requestId,
574
+ status: "cancelled"
575
+ },
576
+ timestamp: Date.now()
577
+ });
578
+ return;
579
+ }
580
+ const queued = this.removeQueuedRequestById(requestId);
581
+ if (queued) {
582
+ if (queued.ws !== ws) return void this.sendError(ws, "FORBIDDEN", "Cannot cancel a request started by another client");
583
+ this.sendMessage(ws, {
584
+ type: "ack",
585
+ id: msg.id,
586
+ payload: {
587
+ action: "req:agent:cancel",
588
+ requestId,
589
+ status: "cancelled_queued"
590
+ },
591
+ timestamp: Date.now()
592
+ });
593
+ return;
594
+ }
595
+ this.sendMessage(ws, {
596
+ type: "ack",
597
+ id: msg.id,
598
+ payload: {
599
+ action: "req:agent:cancel",
600
+ requestId,
601
+ status: "not_found"
602
+ },
603
+ timestamp: Date.now()
604
+ });
605
+ }
606
+ buildSessionQueueKey(agentId, sessionKey) {
607
+ return `${agentId}:${sessionKey}`;
608
+ }
609
+ removeQueuedRequestById(requestId) {
610
+ for (const [queueKey, queue] of this.queuedSessionRequests){
611
+ const index = queue.findIndex((item)=>item.msg.id === requestId);
612
+ if (-1 === index) continue;
613
+ const [removed] = queue.splice(index, 1);
614
+ if (0 === queue.length) this.queuedSessionRequests.delete(queueKey);
615
+ else this.queuedSessionRequests.set(queueKey, queue);
616
+ this.requestSessionKeys.delete(requestId);
617
+ return removed || null;
618
+ }
619
+ return null;
620
+ }
443
621
  handleRegister(ws, msg) {
444
622
  const payload = msg.payload;
445
623
  if (!this.auth.validate({
@@ -602,17 +780,39 @@ class GatewayServer {
602
780
  timestamp: Date.now()
603
781
  });
604
782
  }
605
- sendAgentError(ws, requestId, message) {
606
- this.sendMessage(ws, {
783
+ sendAgentError(ws, requestId, message, options) {
784
+ let payload = {
785
+ type: "agent-error",
786
+ error: message,
787
+ timestamp: new Date().toISOString()
788
+ };
789
+ if (options?.stack) payload.stack = options.stack;
790
+ if (options?.sessionId && options?.agentId) payload = this.attachSessionContext(payload, options.sessionId, options.agentId);
791
+ const baseMessage = {
607
792
  type: "event:agent",
608
793
  id: requestId,
609
- payload: {
610
- type: "agent-error",
611
- error: message,
612
- timestamp: new Date().toISOString()
613
- },
794
+ payload,
614
795
  timestamp: Date.now()
796
+ };
797
+ this.sendMessage(ws, {
798
+ ...baseMessage,
799
+ clientId: ws.data.clientId
615
800
  });
801
+ if (options?.broadcastToSession && options.sessionId) this.broadcastSessionEvent(options.sessionId, baseMessage, options.exclude);
802
+ }
803
+ cancelSocketAgentRequests(ws) {
804
+ for (const [requestId, active] of this.activeAgentRequests)if (active.socket === ws) {
805
+ active.abortController.abort();
806
+ this.activeAgentRequests.delete(requestId);
807
+ }
808
+ for (const [queueKey, queue] of this.queuedSessionRequests){
809
+ const nextQueue = queue.filter((request)=>request.ws !== ws);
810
+ if (nextQueue.length !== queue.length) {
811
+ for (const request of queue)if (request.ws === ws && request.msg.id) this.requestSessionKeys.delete(request.msg.id);
812
+ if (0 === nextQueue.length) this.queuedSessionRequests.delete(queueKey);
813
+ else this.queuedSessionRequests.set(queueKey, nextQueue);
814
+ }
815
+ }
616
816
  }
617
817
  attachSessionContext(event, sessionId, agentId) {
618
818
  if (event && "object" == typeof event && !Array.isArray(event)) return {
@@ -804,9 +1004,7 @@ class GatewayServer {
804
1004
  ];
805
1005
  for (const candidate of candidates)try {
806
1006
  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;
807
- } catch {
808
- continue;
809
- }
1007
+ } catch {}
810
1008
  return null;
811
1009
  }
812
1010
  async getSessionManager(agentId) {
@@ -1129,6 +1327,10 @@ class GatewayServer {
1129
1327
  _define_property(this, "sessionSubscriptions", new Map());
1130
1328
  _define_property(this, "socketSubscriptions", new Map());
1131
1329
  _define_property(this, "connectedClients", new Set());
1330
+ _define_property(this, "activeAgentRequests", new Map());
1331
+ _define_property(this, "activeSessionRequests", new Map());
1332
+ _define_property(this, "queuedSessionRequests", new Map());
1333
+ _define_property(this, "requestSessionKeys", new Map());
1132
1334
  _define_property(this, "bridgeQueues", new Map());
1133
1335
  _define_property(this, "bridgePollWaiters", new Map());
1134
1336
  this.workspace = config.workspace || process.cwd();
@@ -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
@@ -33,6 +33,10 @@ export declare class GatewayServer {
33
33
  private sessionSubscriptions;
34
34
  private socketSubscriptions;
35
35
  private connectedClients;
36
+ private activeAgentRequests;
37
+ private activeSessionRequests;
38
+ private queuedSessionRequests;
39
+ private requestSessionKeys;
36
40
  private bridgeQueues;
37
41
  private bridgePollWaiters;
38
42
  constructor(config?: Partial<GatewayConfig>);
@@ -75,6 +79,11 @@ export declare class GatewayServer {
75
79
  * Handle agent execution request
76
80
  */
77
81
  private handleAgentRequest;
82
+ private executeAgentRequest;
83
+ private processNextQueuedAgentRequest;
84
+ private handleAgentCancel;
85
+ private buildSessionQueueKey;
86
+ private removeQueuedRequestById;
78
87
  /**
79
88
  * Handle node registration
80
89
  */
@@ -124,6 +133,7 @@ export declare class GatewayServer {
124
133
  */
125
134
  private sendError;
126
135
  private sendAgentError;
136
+ private cancelSocketAgentRequests;
127
137
  private attachSessionContext;
128
138
  private broadcastSessionEvent;
129
139
  private broadcastToClients;