@rk0429/agentic-relay 0.6.3 → 0.7.0
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/dist/relay.mjs +181 -18
- package/package.json +1 -1
package/dist/relay.mjs
CHANGED
|
@@ -542,6 +542,97 @@ var init_spawn_agent = __esm({
|
|
|
542
542
|
}
|
|
543
543
|
});
|
|
544
544
|
|
|
545
|
+
// src/mcp-server/tools/spawn-agents-parallel.ts
|
|
546
|
+
async function executeSpawnAgentsParallel(agents, registry2, sessionManager2, guard, hooksEngine2, contextMonitor2, backendSelector, childHttpUrl) {
|
|
547
|
+
const envContext = buildContextFromEnv();
|
|
548
|
+
if (envContext.depth >= guard.getConfig().maxDepth) {
|
|
549
|
+
const reason = `Max depth exceeded: ${envContext.depth} >= ${guard.getConfig().maxDepth}`;
|
|
550
|
+
logger.warn(`Batch spawn blocked by RecursionGuard: ${reason}`);
|
|
551
|
+
return {
|
|
552
|
+
results: agents.map((_, index) => ({
|
|
553
|
+
index,
|
|
554
|
+
sessionId: "",
|
|
555
|
+
exitCode: 1,
|
|
556
|
+
stdout: "",
|
|
557
|
+
stderr: `Batch spawn blocked: ${reason}`,
|
|
558
|
+
error: reason
|
|
559
|
+
})),
|
|
560
|
+
totalCount: agents.length,
|
|
561
|
+
successCount: 0,
|
|
562
|
+
failureCount: agents.length
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
const currentCount = guard.getCallCount(envContext.traceId);
|
|
566
|
+
const maxCalls = guard.getConfig().maxCallsPerSession;
|
|
567
|
+
if (currentCount + agents.length > maxCalls) {
|
|
568
|
+
const reason = `Batch would exceed max calls per session: ${currentCount} + ${agents.length} > ${maxCalls}`;
|
|
569
|
+
logger.warn(`Batch spawn blocked by RecursionGuard: ${reason}`);
|
|
570
|
+
return {
|
|
571
|
+
results: agents.map((_, index) => ({
|
|
572
|
+
index,
|
|
573
|
+
sessionId: "",
|
|
574
|
+
exitCode: 1,
|
|
575
|
+
stdout: "",
|
|
576
|
+
stderr: `Batch spawn blocked: ${reason}`,
|
|
577
|
+
error: reason
|
|
578
|
+
})),
|
|
579
|
+
totalCount: agents.length,
|
|
580
|
+
successCount: 0,
|
|
581
|
+
failureCount: agents.length
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
const settled = await Promise.allSettled(
|
|
585
|
+
agents.map(
|
|
586
|
+
(agent) => executeSpawnAgent(
|
|
587
|
+
agent,
|
|
588
|
+
registry2,
|
|
589
|
+
sessionManager2,
|
|
590
|
+
guard,
|
|
591
|
+
hooksEngine2,
|
|
592
|
+
contextMonitor2,
|
|
593
|
+
backendSelector,
|
|
594
|
+
childHttpUrl
|
|
595
|
+
)
|
|
596
|
+
)
|
|
597
|
+
);
|
|
598
|
+
const results = settled.map((outcome, index) => {
|
|
599
|
+
if (outcome.status === "fulfilled") {
|
|
600
|
+
const r = outcome.value;
|
|
601
|
+
return {
|
|
602
|
+
index,
|
|
603
|
+
sessionId: r.sessionId,
|
|
604
|
+
exitCode: r.exitCode,
|
|
605
|
+
stdout: r.stdout,
|
|
606
|
+
stderr: r.stderr,
|
|
607
|
+
...r.nativeSessionId ? { nativeSessionId: r.nativeSessionId } : {}
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
const errorMessage = outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason);
|
|
611
|
+
return {
|
|
612
|
+
index,
|
|
613
|
+
sessionId: "",
|
|
614
|
+
exitCode: 1,
|
|
615
|
+
stdout: "",
|
|
616
|
+
stderr: errorMessage,
|
|
617
|
+
error: errorMessage
|
|
618
|
+
};
|
|
619
|
+
});
|
|
620
|
+
const successCount = results.filter((r) => r.exitCode === 0).length;
|
|
621
|
+
return {
|
|
622
|
+
results,
|
|
623
|
+
totalCount: agents.length,
|
|
624
|
+
successCount,
|
|
625
|
+
failureCount: agents.length - successCount
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
var init_spawn_agents_parallel = __esm({
|
|
629
|
+
"src/mcp-server/tools/spawn-agents-parallel.ts"() {
|
|
630
|
+
"use strict";
|
|
631
|
+
init_spawn_agent();
|
|
632
|
+
init_logger();
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
|
|
545
636
|
// src/mcp-server/tools/list-sessions.ts
|
|
546
637
|
import { z as z3 } from "zod";
|
|
547
638
|
async function executeListSessions(input, sessionManager2) {
|
|
@@ -702,6 +793,7 @@ var init_server = __esm({
|
|
|
702
793
|
"use strict";
|
|
703
794
|
init_recursion_guard();
|
|
704
795
|
init_spawn_agent();
|
|
796
|
+
init_spawn_agents_parallel();
|
|
705
797
|
init_list_sessions();
|
|
706
798
|
init_get_context_status();
|
|
707
799
|
init_list_available_backends();
|
|
@@ -717,12 +809,11 @@ var init_server = __esm({
|
|
|
717
809
|
this.backendSelector = new BackendSelector();
|
|
718
810
|
this.server = new McpServer({
|
|
719
811
|
name: "agentic-relay",
|
|
720
|
-
version: "0.
|
|
812
|
+
version: "0.7.0"
|
|
721
813
|
});
|
|
722
814
|
this.registerTools(this.server);
|
|
723
815
|
}
|
|
724
816
|
server;
|
|
725
|
-
childServer;
|
|
726
817
|
guard;
|
|
727
818
|
backendSelector;
|
|
728
819
|
_childHttpServer;
|
|
@@ -788,6 +879,56 @@ ${result.stdout}`;
|
|
|
788
879
|
}
|
|
789
880
|
}
|
|
790
881
|
);
|
|
882
|
+
server.tool(
|
|
883
|
+
"spawn_agents_parallel",
|
|
884
|
+
"Spawn multiple sub-agents in parallel across available backends. Each agent entry accepts the same parameters as spawn_agent. All agents are executed concurrently via Promise.allSettled, and results are returned as an array with per-agent status. RecursionGuard batch pre-validation ensures the entire batch fits within call limits before execution begins.",
|
|
885
|
+
{
|
|
886
|
+
agents: z5.array(z5.object({
|
|
887
|
+
backend: z5.enum(["claude", "codex", "gemini"]),
|
|
888
|
+
prompt: z5.string(),
|
|
889
|
+
agent: z5.string().optional().describe("Named agent configuration (Claude only)"),
|
|
890
|
+
systemPrompt: z5.string().optional().describe("System prompt / role instructions for the sub-agent (all backends)"),
|
|
891
|
+
resumeSessionId: z5.string().optional(),
|
|
892
|
+
model: z5.string().optional(),
|
|
893
|
+
maxTurns: z5.number().optional(),
|
|
894
|
+
skillContext: z5.object({
|
|
895
|
+
skillPath: z5.string().describe("Path to the skill directory"),
|
|
896
|
+
subskill: z5.string().optional().describe("Specific subskill to activate")
|
|
897
|
+
}).optional().describe("Skill context to inject into the sub-agent's system prompt"),
|
|
898
|
+
agentDefinition: z5.object({
|
|
899
|
+
definitionPath: z5.string().describe("Path to the agent definition file")
|
|
900
|
+
}).optional().describe("Agent definition file to inject into the sub-agent's system prompt"),
|
|
901
|
+
preferredBackend: z5.enum(["claude", "codex", "gemini"]).optional().describe("Preferred backend override."),
|
|
902
|
+
taskType: z5.enum(["code", "document", "analysis", "mixed"]).optional().describe("Task type hint for automatic backend selection.")
|
|
903
|
+
})).min(1).max(10).describe("Array of agent configurations to execute in parallel (1-10 agents)")
|
|
904
|
+
},
|
|
905
|
+
async (params) => {
|
|
906
|
+
try {
|
|
907
|
+
const result = await executeSpawnAgentsParallel(
|
|
908
|
+
params.agents,
|
|
909
|
+
this.registry,
|
|
910
|
+
this.sessionManager,
|
|
911
|
+
this.guard,
|
|
912
|
+
this.hooksEngine,
|
|
913
|
+
this.contextMonitor,
|
|
914
|
+
this.backendSelector,
|
|
915
|
+
this._childHttpUrl
|
|
916
|
+
);
|
|
917
|
+
const isError = result.failureCount === result.totalCount;
|
|
918
|
+
const text = JSON.stringify(result, null, 2);
|
|
919
|
+
return {
|
|
920
|
+
content: [{ type: "text", text }],
|
|
921
|
+
isError
|
|
922
|
+
};
|
|
923
|
+
} catch (error) {
|
|
924
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
925
|
+
return {
|
|
926
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
927
|
+
isError: true
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
);
|
|
791
932
|
server.tool(
|
|
792
933
|
"list_sessions",
|
|
793
934
|
"List relay sessions, optionally filtered by backend.",
|
|
@@ -912,29 +1053,51 @@ ${result.stdout}`;
|
|
|
912
1053
|
}
|
|
913
1054
|
/**
|
|
914
1055
|
* Start an HTTP server for child agents.
|
|
915
|
-
*
|
|
916
|
-
*
|
|
1056
|
+
* Each client connection gets its own McpServer + StreamableHTTPServerTransport pair
|
|
1057
|
+
* because StreamableHTTPServerTransport only supports a single session per instance.
|
|
1058
|
+
* Routes requests to the correct session based on the Mcp-Session-Id header.
|
|
917
1059
|
*/
|
|
918
1060
|
async startChildHttpServer() {
|
|
919
|
-
|
|
920
|
-
name: "agentic-relay",
|
|
921
|
-
version: "0.6.3"
|
|
922
|
-
});
|
|
923
|
-
this.registerTools(this.childServer);
|
|
924
|
-
const childTransport = new StreamableHTTPServerTransport({
|
|
925
|
-
sessionIdGenerator: () => randomUUID()
|
|
926
|
-
});
|
|
1061
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
927
1062
|
const httpServer = createServer(async (req, res) => {
|
|
928
1063
|
const url = req.url ?? "";
|
|
929
|
-
if (url
|
|
930
|
-
await childTransport.handleRequest(req, res);
|
|
931
|
-
} else {
|
|
1064
|
+
if (url !== "/mcp" && !url.startsWith("/mcp?")) {
|
|
932
1065
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
933
1066
|
res.end("Not found");
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
1070
|
+
if (sessionId) {
|
|
1071
|
+
const session = sessions.get(sessionId);
|
|
1072
|
+
if (session) {
|
|
1073
|
+
await session.transport.handleRequest(req, res);
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
const transport = new StreamableHTTPServerTransport({
|
|
1078
|
+
sessionIdGenerator: () => randomUUID()
|
|
1079
|
+
});
|
|
1080
|
+
const server = new McpServer({
|
|
1081
|
+
name: "agentic-relay",
|
|
1082
|
+
version: "0.7.0"
|
|
1083
|
+
});
|
|
1084
|
+
this.registerTools(server);
|
|
1085
|
+
transport.onclose = () => {
|
|
1086
|
+
const sid2 = transport.sessionId;
|
|
1087
|
+
if (sid2) {
|
|
1088
|
+
sessions.delete(sid2);
|
|
1089
|
+
logger.debug(`Child MCP session closed: ${sid2}`);
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
await server.connect(transport);
|
|
1093
|
+
await transport.handleRequest(req, res);
|
|
1094
|
+
const sid = transport.sessionId;
|
|
1095
|
+
if (sid) {
|
|
1096
|
+
sessions.set(sid, { transport, server });
|
|
1097
|
+
logger.debug(`Child MCP session created: ${sid}`);
|
|
934
1098
|
}
|
|
935
1099
|
});
|
|
936
1100
|
this._childHttpServer = httpServer;
|
|
937
|
-
await this.childServer.connect(childTransport);
|
|
938
1101
|
await new Promise((resolve) => {
|
|
939
1102
|
httpServer.listen(0, "127.0.0.1", () => {
|
|
940
1103
|
const addr = httpServer.address();
|
|
@@ -3898,7 +4061,7 @@ function createVersionCommand(registry2) {
|
|
|
3898
4061
|
description: "Show relay and backend versions"
|
|
3899
4062
|
},
|
|
3900
4063
|
async run() {
|
|
3901
|
-
const relayVersion = "0.
|
|
4064
|
+
const relayVersion = "0.7.0";
|
|
3902
4065
|
console.log(`agentic-relay v${relayVersion}`);
|
|
3903
4066
|
console.log("");
|
|
3904
4067
|
console.log("Backends:");
|
|
@@ -4248,7 +4411,7 @@ void configManager.getConfig().then((config) => {
|
|
|
4248
4411
|
var main = defineCommand10({
|
|
4249
4412
|
meta: {
|
|
4250
4413
|
name: "relay",
|
|
4251
|
-
version: "0.
|
|
4414
|
+
version: "0.7.0",
|
|
4252
4415
|
description: "Unified CLI proxy for Claude Code, Codex CLI, and Gemini CLI"
|
|
4253
4416
|
},
|
|
4254
4417
|
subCommands: {
|
package/package.json
CHANGED