@wingman-ai/gateway 0.2.2 → 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.
- package/.wingman/agents/coding/agent.md +174 -169
- package/.wingman/agents/coding/implementor.md +25 -1
- package/.wingman/agents/main/agent.md +4 -0
- package/README.md +1 -0
- package/dist/agent/config/agentConfig.cjs +1 -1
- package/dist/agent/config/agentConfig.js +1 -1
- package/dist/agent/config/modelFactory.cjs +22 -2
- package/dist/agent/config/modelFactory.d.ts +2 -0
- package/dist/agent/config/modelFactory.js +22 -2
- package/dist/agent/tests/modelFactory.test.cjs +12 -5
- package/dist/agent/tests/modelFactory.test.js +12 -5
- package/dist/cli/commands/init.cjs +7 -6
- package/dist/cli/commands/init.js +7 -6
- package/dist/cli/commands/provider.cjs +17 -3
- package/dist/cli/commands/provider.js +17 -3
- package/dist/cli/config/loader.cjs +27 -0
- package/dist/cli/config/loader.js +27 -0
- package/dist/cli/config/schema.cjs +80 -2
- package/dist/cli/config/schema.d.ts +88 -0
- package/dist/cli/config/schema.js +67 -1
- package/dist/cli/core/agentInvoker.cjs +191 -13
- package/dist/cli/core/agentInvoker.d.ts +42 -3
- package/dist/cli/core/agentInvoker.js +163 -9
- package/dist/cli/core/sessionManager.cjs +32 -5
- package/dist/cli/core/sessionManager.js +32 -5
- package/dist/cli/index.cjs +6 -5
- package/dist/cli/index.js +6 -5
- package/dist/cli/types.d.ts +32 -0
- package/dist/gateway/http/sessions.cjs +7 -7
- package/dist/gateway/http/sessions.js +7 -7
- package/dist/gateway/server.cjs +191 -41
- package/dist/gateway/server.d.ts +8 -1
- package/dist/gateway/server.js +191 -41
- package/dist/gateway/types.d.ts +1 -0
- package/dist/providers/codex.cjs +167 -0
- package/dist/providers/codex.d.ts +15 -0
- package/dist/providers/codex.js +127 -0
- package/dist/providers/credentials.cjs +8 -0
- package/dist/providers/credentials.js +8 -0
- package/dist/providers/registry.cjs +11 -0
- package/dist/providers/registry.d.ts +1 -1
- package/dist/providers/registry.js +11 -0
- package/dist/tests/agentInvokerSummarization.test.cjs +296 -0
- package/dist/tests/agentInvokerSummarization.test.d.ts +1 -0
- package/dist/tests/agentInvokerSummarization.test.js +290 -0
- package/dist/tests/cli-config-loader.test.cjs +88 -0
- package/dist/tests/cli-config-loader.test.js +88 -0
- package/dist/tests/codex-credentials-precedence.test.cjs +94 -0
- package/dist/tests/codex-credentials-precedence.test.d.ts +1 -0
- package/dist/tests/codex-credentials-precedence.test.js +88 -0
- package/dist/tests/codex-provider.test.cjs +186 -0
- package/dist/tests/codex-provider.test.d.ts +1 -0
- package/dist/tests/codex-provider.test.js +180 -0
- package/dist/tests/gateway.test.cjs +108 -1
- package/dist/tests/gateway.test.js +108 -1
- package/dist/tests/provider-command-codex.test.cjs +57 -0
- package/dist/tests/provider-command-codex.test.d.ts +1 -0
- package/dist/tests/provider-command-codex.test.js +51 -0
- package/dist/tests/sessionStateMessages.test.cjs +38 -0
- package/dist/tests/sessionStateMessages.test.js +38 -0
- package/dist/webui/assets/{index-DDsMIOTX.css → index-BVMavpud.css} +1 -1
- package/dist/webui/assets/index-DCB2aVVf.js +182 -0
- package/dist/webui/index.html +2 -2
- package/package.json +1 -1
- package/dist/webui/assets/index-CPhfGPHc.js +0 -182
|
@@ -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
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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;
|
package/dist/cli/index.cjs
CHANGED
|
@@ -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
|
|
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 {
|
|
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"
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -3,6 +3,38 @@ export type OutputMode = "interactive" | "json";
|
|
|
3
3
|
export interface WingmanConfig {
|
|
4
4
|
logLevel?: LogLevel;
|
|
5
5
|
defaultAgent?: string;
|
|
6
|
+
summarization?: {
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
maxTokensBeforeSummary?: number;
|
|
9
|
+
messagesToKeep?: number;
|
|
10
|
+
};
|
|
11
|
+
modelRetry?: {
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
maxRetries?: number;
|
|
14
|
+
backoffFactor?: number;
|
|
15
|
+
initialDelayMs?: number;
|
|
16
|
+
maxDelayMs?: number;
|
|
17
|
+
jitter?: boolean;
|
|
18
|
+
onFailure?: "continue" | "error";
|
|
19
|
+
};
|
|
20
|
+
toolRetry?: {
|
|
21
|
+
enabled?: boolean;
|
|
22
|
+
maxRetries?: number;
|
|
23
|
+
backoffFactor?: number;
|
|
24
|
+
initialDelayMs?: number;
|
|
25
|
+
maxDelayMs?: number;
|
|
26
|
+
jitter?: boolean;
|
|
27
|
+
onFailure?: "continue" | "error";
|
|
28
|
+
tools?: string[];
|
|
29
|
+
};
|
|
30
|
+
humanInTheLoop?: {
|
|
31
|
+
enabled?: boolean;
|
|
32
|
+
interruptOn?: Record<string, boolean | {
|
|
33
|
+
allowedDecisions: Array<"approve" | "edit" | "reject">;
|
|
34
|
+
description?: string;
|
|
35
|
+
argsSchema?: Record<string, any>;
|
|
36
|
+
}>;
|
|
37
|
+
};
|
|
6
38
|
gateway?: {
|
|
7
39
|
host?: string;
|
|
8
40
|
port?: number;
|
|
@@ -63,7 +63,7 @@ const handleSessionsApi = async (ctx, req, url)=>{
|
|
|
63
63
|
const bUpdated = "number" == typeof b.updatedAt ? b.updatedAt : 0;
|
|
64
64
|
return bUpdated - aUpdated;
|
|
65
65
|
});
|
|
66
|
-
return new Response(JSON.stringify(sorted.slice(0, limit)
|
|
66
|
+
return new Response(JSON.stringify(sorted.slice(0, limit)), {
|
|
67
67
|
headers: {
|
|
68
68
|
"Content-Type": "application/json"
|
|
69
69
|
}
|
|
@@ -87,7 +87,7 @@ const handleSessionsApi = async (ctx, req, url)=>{
|
|
|
87
87
|
messageCount: session.messageCount,
|
|
88
88
|
lastMessagePreview: session.lastMessagePreview,
|
|
89
89
|
workdir: session.metadata?.workdir ?? null
|
|
90
|
-
}
|
|
90
|
+
}), {
|
|
91
91
|
headers: {
|
|
92
92
|
"Content-Type": "application/json"
|
|
93
93
|
}
|
|
@@ -107,7 +107,7 @@ const handleSessionsApi = async (ctx, req, url)=>{
|
|
|
107
107
|
const manager = await ctx.getSessionManager(agentId);
|
|
108
108
|
if ("GET" === req.method) {
|
|
109
109
|
const messages = await manager.listMessages(sessionId);
|
|
110
|
-
return new Response(JSON.stringify(messages
|
|
110
|
+
return new Response(JSON.stringify(messages), {
|
|
111
111
|
headers: {
|
|
112
112
|
"Content-Type": "application/json"
|
|
113
113
|
}
|
|
@@ -124,7 +124,7 @@ const handleSessionsApi = async (ctx, req, url)=>{
|
|
|
124
124
|
id: sessionId,
|
|
125
125
|
messageCount: updated?.messageCount ?? 0,
|
|
126
126
|
lastMessagePreview: updated?.lastMessagePreview ?? null
|
|
127
|
-
}
|
|
127
|
+
}), {
|
|
128
128
|
headers: {
|
|
129
129
|
"Content-Type": "application/json"
|
|
130
130
|
}
|
|
@@ -152,7 +152,7 @@ const handleSessionsApi = async (ctx, req, url)=>{
|
|
|
152
152
|
return new Response(JSON.stringify({
|
|
153
153
|
id: session.id,
|
|
154
154
|
workdir: null
|
|
155
|
-
}
|
|
155
|
+
}), {
|
|
156
156
|
headers: {
|
|
157
157
|
"Content-Type": "application/json"
|
|
158
158
|
}
|
|
@@ -172,7 +172,7 @@ const handleSessionsApi = async (ctx, req, url)=>{
|
|
|
172
172
|
return new Response(JSON.stringify({
|
|
173
173
|
id: session.id,
|
|
174
174
|
workdir: resolved
|
|
175
|
-
}
|
|
175
|
+
}), {
|
|
176
176
|
headers: {
|
|
177
177
|
"Content-Type": "application/json"
|
|
178
178
|
}
|
|
@@ -208,7 +208,7 @@ const handleSessionsApi = async (ctx, req, url)=>{
|
|
|
208
208
|
messageCount: updated?.messageCount ?? session.messageCount,
|
|
209
209
|
lastMessagePreview: updated?.lastMessagePreview ?? session.lastMessagePreview,
|
|
210
210
|
workdir: updated?.metadata?.workdir ?? session.metadata?.workdir ?? null
|
|
211
|
-
}
|
|
211
|
+
}), {
|
|
212
212
|
headers: {
|
|
213
213
|
"Content-Type": "application/json"
|
|
214
214
|
}
|
|
@@ -35,7 +35,7 @@ const handleSessionsApi = async (ctx, req, url)=>{
|
|
|
35
35
|
const bUpdated = "number" == typeof b.updatedAt ? b.updatedAt : 0;
|
|
36
36
|
return bUpdated - aUpdated;
|
|
37
37
|
});
|
|
38
|
-
return new Response(JSON.stringify(sorted.slice(0, limit)
|
|
38
|
+
return new Response(JSON.stringify(sorted.slice(0, limit)), {
|
|
39
39
|
headers: {
|
|
40
40
|
"Content-Type": "application/json"
|
|
41
41
|
}
|
|
@@ -59,7 +59,7 @@ const handleSessionsApi = async (ctx, req, url)=>{
|
|
|
59
59
|
messageCount: session.messageCount,
|
|
60
60
|
lastMessagePreview: session.lastMessagePreview,
|
|
61
61
|
workdir: session.metadata?.workdir ?? null
|
|
62
|
-
}
|
|
62
|
+
}), {
|
|
63
63
|
headers: {
|
|
64
64
|
"Content-Type": "application/json"
|
|
65
65
|
}
|
|
@@ -79,7 +79,7 @@ const handleSessionsApi = async (ctx, req, url)=>{
|
|
|
79
79
|
const manager = await ctx.getSessionManager(agentId);
|
|
80
80
|
if ("GET" === req.method) {
|
|
81
81
|
const messages = await manager.listMessages(sessionId);
|
|
82
|
-
return new Response(JSON.stringify(messages
|
|
82
|
+
return new Response(JSON.stringify(messages), {
|
|
83
83
|
headers: {
|
|
84
84
|
"Content-Type": "application/json"
|
|
85
85
|
}
|
|
@@ -96,7 +96,7 @@ const handleSessionsApi = async (ctx, req, url)=>{
|
|
|
96
96
|
id: sessionId,
|
|
97
97
|
messageCount: updated?.messageCount ?? 0,
|
|
98
98
|
lastMessagePreview: updated?.lastMessagePreview ?? null
|
|
99
|
-
}
|
|
99
|
+
}), {
|
|
100
100
|
headers: {
|
|
101
101
|
"Content-Type": "application/json"
|
|
102
102
|
}
|
|
@@ -124,7 +124,7 @@ const handleSessionsApi = async (ctx, req, url)=>{
|
|
|
124
124
|
return new Response(JSON.stringify({
|
|
125
125
|
id: session.id,
|
|
126
126
|
workdir: null
|
|
127
|
-
}
|
|
127
|
+
}), {
|
|
128
128
|
headers: {
|
|
129
129
|
"Content-Type": "application/json"
|
|
130
130
|
}
|
|
@@ -144,7 +144,7 @@ const handleSessionsApi = async (ctx, req, url)=>{
|
|
|
144
144
|
return new Response(JSON.stringify({
|
|
145
145
|
id: session.id,
|
|
146
146
|
workdir: resolved
|
|
147
|
-
}
|
|
147
|
+
}), {
|
|
148
148
|
headers: {
|
|
149
149
|
"Content-Type": "application/json"
|
|
150
150
|
}
|
|
@@ -180,7 +180,7 @@ const handleSessionsApi = async (ctx, req, url)=>{
|
|
|
180
180
|
messageCount: updated?.messageCount ?? session.messageCount,
|
|
181
181
|
lastMessagePreview: updated?.lastMessagePreview ?? session.lastMessagePreview,
|
|
182
182
|
workdir: updated?.metadata?.workdir ?? session.metadata?.workdir ?? null
|
|
183
|
-
}
|
|
183
|
+
}), {
|
|
184
184
|
headers: {
|
|
185
185
|
"Content-Type": "application/json"
|
|
186
186
|
}
|
package/dist/gateway/server.cjs
CHANGED
|
@@ -29,31 +29,31 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
29
29
|
__webpack_require__.d(__webpack_exports__, {
|
|
30
30
|
GatewayServer: ()=>GatewayServer
|
|
31
31
|
});
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
const
|
|
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
|
|
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
|
|
51
|
-
const
|
|
52
|
-
const
|
|
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,
|
|
@@ -350,6 +350,7 @@ class GatewayServer {
|
|
|
350
350
|
existing?.abortController.abort();
|
|
351
351
|
this.activeAgentRequests.delete(msg.id);
|
|
352
352
|
}
|
|
353
|
+
this.removeQueuedRequestById(msg.id);
|
|
353
354
|
const payload = msg.payload;
|
|
354
355
|
const content = "string" == typeof payload?.content ? payload.content : "";
|
|
355
356
|
const attachments = Array.isArray(payload?.attachments) ? payload.attachments : [];
|
|
@@ -359,6 +360,7 @@ class GatewayServer {
|
|
|
359
360
|
const agentId = this.router.selectAgent(payload.agentId, payload.routing);
|
|
360
361
|
if (!agentId) return void this.sendAgentError(ws, msg.id, "No agent matched the request");
|
|
361
362
|
const sessionKey = payload.sessionKey || this.router.buildSessionKey(agentId, payload.routing);
|
|
363
|
+
const sessionQueueKey = this.buildSessionQueueKey(agentId, sessionKey);
|
|
362
364
|
const sessionManager = await this.getSessionManager(agentId);
|
|
363
365
|
const existingSession = sessionManager.getSession(sessionKey);
|
|
364
366
|
const session = existingSession || sessionManager.getOrCreateSession(sessionKey, agentId);
|
|
@@ -409,9 +411,70 @@ class GatewayServer {
|
|
|
409
411
|
],
|
|
410
412
|
skipSessionId: sessionKey
|
|
411
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);
|
|
412
473
|
const outputManager = new outputManager_cjs_namespaceObject.OutputManager("interactive");
|
|
474
|
+
let emittedAgentError = false;
|
|
413
475
|
const outputHandler = (event)=>{
|
|
414
476
|
const payloadWithSession = this.attachSessionContext(event, sessionKey, agentId);
|
|
477
|
+
if (payloadWithSession && "object" == typeof payloadWithSession && !Array.isArray(payloadWithSession) && "agent-error" === payloadWithSession.type) emittedAgentError = true;
|
|
415
478
|
const baseMessage = {
|
|
416
479
|
type: "event:agent",
|
|
417
480
|
id: msg.id,
|
|
@@ -450,41 +513,111 @@ class GatewayServer {
|
|
|
450
513
|
});
|
|
451
514
|
} catch (error) {
|
|
452
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
|
+
}
|
|
453
527
|
} finally{
|
|
454
528
|
this.activeAgentRequests.delete(msg.id);
|
|
529
|
+
this.activeSessionRequests.delete(sessionQueueKey);
|
|
530
|
+
this.requestSessionKeys.delete(msg.id);
|
|
455
531
|
outputManager.off("output-event", outputHandler);
|
|
532
|
+
this.processNextQueuedAgentRequest(sessionQueueKey);
|
|
456
533
|
}
|
|
457
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
|
+
}
|
|
458
558
|
handleAgentCancel(ws, msg) {
|
|
459
559
|
if (!ws.data.authenticated) return void this.sendError(ws, "AUTH_FAILED", "Client is not authenticated");
|
|
460
560
|
const payload = msg.payload;
|
|
461
561
|
const requestId = "string" == typeof payload?.requestId && payload.requestId || void 0;
|
|
462
562
|
if (!requestId) return void this.sendError(ws, "INVALID_REQUEST", "Missing requestId for cancellation");
|
|
463
563
|
const active = this.activeAgentRequests.get(requestId);
|
|
464
|
-
if (
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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
|
+
}
|
|
477
595
|
this.sendMessage(ws, {
|
|
478
596
|
type: "ack",
|
|
479
597
|
id: msg.id,
|
|
480
598
|
payload: {
|
|
481
599
|
action: "req:agent:cancel",
|
|
482
600
|
requestId,
|
|
483
|
-
status: "
|
|
601
|
+
status: "not_found"
|
|
484
602
|
},
|
|
485
603
|
timestamp: Date.now()
|
|
486
604
|
});
|
|
487
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
|
+
}
|
|
488
621
|
handleRegister(ws, msg) {
|
|
489
622
|
const payload = msg.payload;
|
|
490
623
|
if (!this.auth.validate({
|
|
@@ -647,23 +780,39 @@ class GatewayServer {
|
|
|
647
780
|
timestamp: Date.now()
|
|
648
781
|
});
|
|
649
782
|
}
|
|
650
|
-
sendAgentError(ws, requestId, message) {
|
|
651
|
-
|
|
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 = {
|
|
652
792
|
type: "event:agent",
|
|
653
793
|
id: requestId,
|
|
654
|
-
payload
|
|
655
|
-
type: "agent-error",
|
|
656
|
-
error: message,
|
|
657
|
-
timestamp: new Date().toISOString()
|
|
658
|
-
},
|
|
794
|
+
payload,
|
|
659
795
|
timestamp: Date.now()
|
|
796
|
+
};
|
|
797
|
+
this.sendMessage(ws, {
|
|
798
|
+
...baseMessage,
|
|
799
|
+
clientId: ws.data.clientId
|
|
660
800
|
});
|
|
801
|
+
if (options?.broadcastToSession && options.sessionId) this.broadcastSessionEvent(options.sessionId, baseMessage, options.exclude);
|
|
661
802
|
}
|
|
662
803
|
cancelSocketAgentRequests(ws) {
|
|
663
804
|
for (const [requestId, active] of this.activeAgentRequests)if (active.socket === ws) {
|
|
664
805
|
active.abortController.abort();
|
|
665
806
|
this.activeAgentRequests.delete(requestId);
|
|
666
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
|
+
}
|
|
667
816
|
}
|
|
668
817
|
attachSessionContext(event, sessionId, agentId) {
|
|
669
818
|
if (event && "object" == typeof event && !Array.isArray(event)) return {
|
|
@@ -855,9 +1004,7 @@ class GatewayServer {
|
|
|
855
1004
|
];
|
|
856
1005
|
for (const candidate of candidates)try {
|
|
857
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;
|
|
858
|
-
} catch {
|
|
859
|
-
continue;
|
|
860
|
-
}
|
|
1007
|
+
} catch {}
|
|
861
1008
|
return null;
|
|
862
1009
|
}
|
|
863
1010
|
async getSessionManager(agentId) {
|
|
@@ -1181,6 +1328,9 @@ class GatewayServer {
|
|
|
1181
1328
|
_define_property(this, "socketSubscriptions", new Map());
|
|
1182
1329
|
_define_property(this, "connectedClients", new Set());
|
|
1183
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());
|
|
1184
1334
|
_define_property(this, "bridgeQueues", new Map());
|
|
1185
1335
|
_define_property(this, "bridgePollWaiters", new Map());
|
|
1186
1336
|
this.workspace = config.workspace || process.cwd();
|
package/dist/gateway/server.d.ts
CHANGED
|
@@ -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,9 @@ 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;
|
|
37
40
|
private bridgeQueues;
|
|
38
41
|
private bridgePollWaiters;
|
|
39
42
|
constructor(config?: Partial<GatewayConfig>);
|
|
@@ -76,7 +79,11 @@ export declare class GatewayServer {
|
|
|
76
79
|
* Handle agent execution request
|
|
77
80
|
*/
|
|
78
81
|
private handleAgentRequest;
|
|
82
|
+
private executeAgentRequest;
|
|
83
|
+
private processNextQueuedAgentRequest;
|
|
79
84
|
private handleAgentCancel;
|
|
85
|
+
private buildSessionQueueKey;
|
|
86
|
+
private removeQueuedRequestById;
|
|
80
87
|
/**
|
|
81
88
|
* Handle node registration
|
|
82
89
|
*/
|