@rk0429/agentic-relay 0.11.0 → 0.12.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.
Files changed (2) hide show
  1. package/dist/relay.mjs +174 -90
  2. package/package.json +1 -1
package/dist/relay.mjs CHANGED
@@ -1047,6 +1047,24 @@ var init_backend_selector = __esm({
1047
1047
  }
1048
1048
  });
1049
1049
 
1050
+ // src/types/mcp.ts
1051
+ var DEFAULT_TASK_TTL, DEFAULT_POLL_INTERVAL;
1052
+ var init_mcp = __esm({
1053
+ "src/types/mcp.ts"() {
1054
+ "use strict";
1055
+ DEFAULT_TASK_TTL = 5 * 60 * 1e3;
1056
+ DEFAULT_POLL_INTERVAL = 3e3;
1057
+ }
1058
+ });
1059
+
1060
+ // src/types/index.ts
1061
+ var init_types = __esm({
1062
+ "src/types/index.ts"() {
1063
+ "use strict";
1064
+ init_mcp();
1065
+ }
1066
+ });
1067
+
1050
1068
  // src/mcp-server/response-formatter.ts
1051
1069
  function formatSpawnAgentResponse(result) {
1052
1070
  const isError = result.exitCode !== 0;
@@ -1119,9 +1137,20 @@ __export(server_exports, {
1119
1137
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
1120
1138
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
1121
1139
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
1140
+ import { InMemoryTaskStore } from "@modelcontextprotocol/sdk/experimental/tasks/stores/in-memory.js";
1141
+ import { InMemoryTaskMessageQueue } from "@modelcontextprotocol/sdk/experimental/tasks/stores/in-memory.js";
1122
1142
  import { createServer } from "http";
1123
1143
  import { randomUUID } from "crypto";
1124
1144
  import { z as z5 } from "zod";
1145
+ function createMcpServerOptions() {
1146
+ const taskStore = new InMemoryTaskStore();
1147
+ return {
1148
+ capabilities: { tasks: { requests: { tools: { call: {} } } } },
1149
+ taskStore,
1150
+ taskMessageQueue: new InMemoryTaskMessageQueue(),
1151
+ defaultTaskPollInterval: DEFAULT_POLL_INTERVAL
1152
+ };
1153
+ }
1125
1154
  var spawnAgentsParallelInputShape, MAX_CHILD_HTTP_SESSIONS, RelayMCPServer;
1126
1155
  var init_server = __esm({
1127
1156
  "src/mcp-server/server.ts"() {
@@ -1134,6 +1163,7 @@ var init_server = __esm({
1134
1163
  init_list_available_backends();
1135
1164
  init_backend_selector();
1136
1165
  init_logger();
1166
+ init_types();
1137
1167
  init_response_formatter();
1138
1168
  spawnAgentsParallelInputShape = {
1139
1169
  agents: z5.array(spawnAgentInputSchema).min(1).max(10).describe(
@@ -1149,10 +1179,10 @@ var init_server = __esm({
1149
1179
  this.contextMonitor = contextMonitor2;
1150
1180
  this.guard = new RecursionGuard(guardConfig);
1151
1181
  this.backendSelector = new BackendSelector();
1152
- this.server = new McpServer({
1153
- name: "agentic-relay",
1154
- version: "0.11.0"
1155
- });
1182
+ this.server = new McpServer(
1183
+ { name: "agentic-relay", version: "0.12.0" },
1184
+ createMcpServerOptions()
1185
+ );
1156
1186
  this.registerTools(this.server);
1157
1187
  }
1158
1188
  server;
@@ -1168,94 +1198,148 @@ var init_server = __esm({
1168
1198
  get childHttpServer() {
1169
1199
  return this._childHttpServer;
1170
1200
  }
1171
- registerTools(server) {
1172
- server.tool(
1173
- "spawn_agent",
1174
- "Spawn a sub-agent on the specified backend CLI (Claude Code, Codex CLI, or Gemini CLI). The agent executes the given prompt in non-interactive mode and returns the result. Use 'agent' for named agent configurations (Claude only), 'systemPrompt' for custom role instructions (all backends), 'skillContext' to inject a skill definition (SKILL.md/SUBSKILL.md), 'agentDefinition' to inject an agent definition file into the sub-agent's system prompt, 'preferredBackend' to override automatic backend selection, or 'taskType' to hint at the task nature for backend selection.",
1175
- spawnAgentInputSchema.shape,
1176
- async (params, extra) => {
1201
+ /**
1202
+ * Run spawn_agent in the background and store the result in taskStore.
1203
+ * Fire-and-forget: errors are caught and stored as failed task results.
1204
+ */
1205
+ runSpawnAgentInBackground(taskId, params, extra) {
1206
+ const run = async () => {
1207
+ try {
1208
+ const result = await executeSpawnAgent(
1209
+ params,
1210
+ this.registry,
1211
+ this.sessionManager,
1212
+ this.guard,
1213
+ this.hooksEngine,
1214
+ this.contextMonitor,
1215
+ this.backendSelector,
1216
+ this._childHttpUrl
1217
+ );
1218
+ const { text, isError } = formatSpawnAgentResponse(result);
1219
+ const callToolResult = {
1220
+ content: [{ type: "text", text }],
1221
+ isError
1222
+ };
1223
+ await extra.taskStore.storeTaskResult(
1224
+ taskId,
1225
+ "completed",
1226
+ callToolResult
1227
+ );
1228
+ } catch (outerError) {
1177
1229
  try {
1178
- const onProgress = (progress) => {
1179
- const progressToken = params._meta ? params._meta.progressToken : void 0;
1180
- if (progressToken !== void 0) {
1181
- void extra.sendNotification({
1182
- method: "notifications/progress",
1183
- params: {
1184
- progressToken,
1185
- progress: progress.percent ?? 0,
1186
- total: 100,
1187
- message: progress.stage
1188
- }
1189
- });
1230
+ const message = outerError instanceof Error ? outerError.message : String(outerError);
1231
+ await extra.taskStore.storeTaskResult(
1232
+ taskId,
1233
+ "failed",
1234
+ {
1235
+ content: [{ type: "text", text: `Error: ${message}` }],
1236
+ isError: true
1190
1237
  }
1191
- };
1192
- const result = await executeSpawnAgent(
1193
- params,
1194
- this.registry,
1195
- this.sessionManager,
1196
- this.guard,
1197
- this.hooksEngine,
1198
- this.contextMonitor,
1199
- this.backendSelector,
1200
- this._childHttpUrl,
1201
- onProgress
1202
1238
  );
1203
- const { text, isError } = formatSpawnAgentResponse(result);
1204
- return {
1205
- content: [{ type: "text", text }],
1206
- isError
1207
- };
1208
- } catch (error) {
1209
- const message = error instanceof Error ? error.message : String(error);
1210
- return {
1211
- content: [{ type: "text", text: `Error: ${message}` }],
1212
- isError: true
1213
- };
1239
+ } catch (storeError) {
1240
+ logger.error(
1241
+ `Failed to store task result for ${taskId}: ${storeError instanceof Error ? storeError.message : String(storeError)}`
1242
+ );
1214
1243
  }
1215
1244
  }
1216
- );
1217
- server.tool(
1218
- "spawn_agents_parallel",
1219
- "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. Failed results include 'originalInput' for retry via retry_failed_agents.",
1220
- spawnAgentsParallelInputShape,
1221
- async (params, extra) => {
1245
+ };
1246
+ void run();
1247
+ }
1248
+ /**
1249
+ * Run spawn_agents_parallel in the background and store the result in taskStore.
1250
+ * Fire-and-forget: errors are caught and stored as failed task results.
1251
+ */
1252
+ runSpawnAgentsParallelInBackground(taskId, agents, extra) {
1253
+ const run = async () => {
1254
+ try {
1255
+ const result = await executeSpawnAgentsParallel(
1256
+ agents,
1257
+ this.registry,
1258
+ this.sessionManager,
1259
+ this.guard,
1260
+ this.hooksEngine,
1261
+ this.contextMonitor,
1262
+ this.backendSelector,
1263
+ this._childHttpUrl
1264
+ );
1265
+ const { text, isError } = formatParallelResponse(result);
1266
+ const callToolResult = {
1267
+ content: [{ type: "text", text }],
1268
+ isError
1269
+ };
1270
+ await extra.taskStore.storeTaskResult(
1271
+ taskId,
1272
+ "completed",
1273
+ callToolResult
1274
+ );
1275
+ } catch (outerError) {
1222
1276
  try {
1223
- const onProgress = (progress) => {
1224
- const progressToken = params._meta ? params._meta.progressToken : void 0;
1225
- if (progressToken !== void 0) {
1226
- void extra.sendNotification({
1227
- method: "notifications/progress",
1228
- params: {
1229
- progressToken,
1230
- progress: progress.percent ?? 0,
1231
- total: 100,
1232
- message: progress.stage
1233
- }
1234
- });
1277
+ const message = outerError instanceof Error ? outerError.message : String(outerError);
1278
+ await extra.taskStore.storeTaskResult(
1279
+ taskId,
1280
+ "failed",
1281
+ {
1282
+ content: [{ type: "text", text: `Error: ${message}` }],
1283
+ isError: true
1235
1284
  }
1236
- };
1237
- const result = await executeSpawnAgentsParallel(
1238
- params.agents,
1239
- this.registry,
1240
- this.sessionManager,
1241
- this.guard,
1242
- this.hooksEngine,
1243
- this.contextMonitor,
1244
- this.backendSelector,
1245
- this._childHttpUrl,
1246
- onProgress
1247
1285
  );
1248
- const { text, isError } = formatParallelResponse(result);
1249
- return {
1250
- content: [{ type: "text", text }],
1251
- isError
1252
- };
1253
- } catch (error) {
1254
- const message = error instanceof Error ? error.message : String(error);
1255
- return {
1256
- content: [{ type: "text", text: `Error: ${message}` }],
1257
- isError: true
1258
- };
1286
+ } catch (storeError) {
1287
+ logger.error(
1288
+ `Failed to store parallel task result for ${taskId}: ${storeError instanceof Error ? storeError.message : String(storeError)}`
1289
+ );
1290
+ }
1291
+ }
1292
+ };
1293
+ void run();
1294
+ }
1295
+ registerTools(server) {
1296
+ server.experimental.tasks.registerToolTask(
1297
+ "spawn_agent",
1298
+ {
1299
+ description: "Spawn a sub-agent on the specified backend CLI (Claude Code, Codex CLI, or Gemini CLI). The agent executes the given prompt in non-interactive mode and returns the result. Use 'agent' for named agent configurations (Claude only), 'systemPrompt' for custom role instructions (all backends), 'skillContext' to inject a skill definition (SKILL.md/SUBSKILL.md), 'agentDefinition' to inject an agent definition file into the sub-agent's system prompt, 'preferredBackend' to override automatic backend selection, or 'taskType' to hint at the task nature for backend selection.",
1300
+ inputSchema: spawnAgentInputSchema.shape,
1301
+ execution: { taskSupport: "optional" }
1302
+ },
1303
+ {
1304
+ createTask: async (params, extra) => {
1305
+ const task = await extra.taskStore.createTask({
1306
+ ttl: DEFAULT_TASK_TTL,
1307
+ pollInterval: DEFAULT_POLL_INTERVAL
1308
+ });
1309
+ this.runSpawnAgentInBackground(task.taskId, params, extra);
1310
+ return { task };
1311
+ },
1312
+ getTask: async (_params, extra) => {
1313
+ const task = await extra.taskStore.getTask(extra.taskId);
1314
+ return { task: task ?? void 0 };
1315
+ },
1316
+ getTaskResult: async (_params, extra) => {
1317
+ return await extra.taskStore.getTaskResult(extra.taskId);
1318
+ }
1319
+ }
1320
+ );
1321
+ server.experimental.tasks.registerToolTask(
1322
+ "spawn_agents_parallel",
1323
+ {
1324
+ description: "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. Failed results include 'originalInput' for retry via retry_failed_agents.",
1325
+ inputSchema: spawnAgentsParallelInputShape,
1326
+ execution: { taskSupport: "optional" }
1327
+ },
1328
+ {
1329
+ createTask: async (params, extra) => {
1330
+ const task = await extra.taskStore.createTask({
1331
+ ttl: DEFAULT_TASK_TTL,
1332
+ pollInterval: DEFAULT_POLL_INTERVAL
1333
+ });
1334
+ this.runSpawnAgentsParallelInBackground(task.taskId, params.agents, extra);
1335
+ return { task };
1336
+ },
1337
+ getTask: async (_params, extra) => {
1338
+ const task = await extra.taskStore.getTask(extra.taskId);
1339
+ return { task: task ?? void 0 };
1340
+ },
1341
+ getTaskResult: async (_params, extra) => {
1342
+ return await extra.taskStore.getTaskResult(extra.taskId);
1259
1343
  }
1260
1344
  }
1261
1345
  );
@@ -1457,10 +1541,10 @@ var init_server = __esm({
1457
1541
  const transport = new StreamableHTTPServerTransport({
1458
1542
  sessionIdGenerator: () => randomUUID()
1459
1543
  });
1460
- const server = new McpServer({
1461
- name: "agentic-relay",
1462
- version: "0.11.0"
1463
- });
1544
+ const server = new McpServer(
1545
+ { name: "agentic-relay", version: "0.12.0" },
1546
+ createMcpServerOptions()
1547
+ );
1464
1548
  this.registerTools(server);
1465
1549
  transport.onclose = () => {
1466
1550
  const sid2 = transport.sessionId;
@@ -4411,7 +4495,7 @@ function createVersionCommand(registry2) {
4411
4495
  description: "Show relay and backend versions"
4412
4496
  },
4413
4497
  async run() {
4414
- const relayVersion = "0.11.0";
4498
+ const relayVersion = "0.12.0";
4415
4499
  console.log(`agentic-relay v${relayVersion}`);
4416
4500
  console.log("");
4417
4501
  console.log("Backends:");
@@ -4761,7 +4845,7 @@ void configManager.getConfig().then((config) => {
4761
4845
  var main = defineCommand10({
4762
4846
  meta: {
4763
4847
  name: "relay",
4764
- version: "0.11.0",
4848
+ version: "0.12.0",
4765
4849
  description: "Unified CLI proxy for Claude Code, Codex CLI, and Gemini CLI"
4766
4850
  },
4767
4851
  subCommands: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rk0429/agentic-relay",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Unified CLI proxy for Claude Code, Codex CLI, and Gemini CLI with MCP-based multi-layer sub-agent orchestration",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",