@minasoft/mina-ai-router 0.1.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 (34) hide show
  1. package/README.md +53 -0
  2. package/dist/apps/cli/src/index.js +574 -0
  3. package/dist/apps/http-server/src/index.js +755 -0
  4. package/dist/apps/mcp-server/src/index.js +308 -0
  5. package/dist/packages/core/src/file-state.js +35 -0
  6. package/dist/packages/core/src/ids.js +8 -0
  7. package/dist/packages/core/src/index.js +24 -0
  8. package/dist/packages/core/src/prompt-envelope.js +27 -0
  9. package/dist/packages/core/src/registry.js +34 -0
  10. package/dist/packages/core/src/request-store.js +50 -0
  11. package/dist/packages/core/src/response-parser.js +33 -0
  12. package/dist/packages/core/src/router.js +100 -0
  13. package/dist/packages/core/src/types.js +2 -0
  14. package/dist/packages/mcp/src/provider.js +177 -0
  15. package/dist/packages/transports/src/headless/headless-transport.js +31 -0
  16. package/dist/packages/transports/src/index.js +22 -0
  17. package/dist/packages/transports/src/tmux/tmux-client.js +126 -0
  18. package/dist/packages/transports/src/tmux/tmux-transport.js +54 -0
  19. package/dist/packages/transports/src/transport-registry.js +20 -0
  20. package/dist/packages/transports/src/zmux/zmux-client.js +18 -0
  21. package/dist/packages/transports/src/zmux/zmux-transport.js +36 -0
  22. package/docs/DEVELOPER-START-GUIDE.md +111 -0
  23. package/docs/GETTING-STARTED.md +32 -0
  24. package/docs/HTTP-UI-MCP.md +142 -0
  25. package/docs/MCP-CLIENT-SETUP.md +71 -0
  26. package/docs/SKILL-INSTALL-GUIDE.md +96 -0
  27. package/docs/TROUBLESHOOTING.md +75 -0
  28. package/docs/USER-START-GUIDE.md +187 -0
  29. package/docs/assets/mair-agent-details.jpg +0 -0
  30. package/docs/assets/mair-live-flow.jpg +0 -0
  31. package/docs/assets/mair-terminal-preview.jpg +0 -0
  32. package/package.json +47 -0
  33. package/skills/mina-ai-router-agent/SKILL.md +64 -0
  34. package/skills/mina-ai-router-agent/agents/openai.yaml +4 -0
@@ -0,0 +1,308 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const node_path_1 = require("node:path");
5
+ const node_url_1 = require("node:url");
6
+ const node_fs_1 = require("node:fs");
7
+ const src_1 = require("../../../packages/core/src");
8
+ const src_2 = require("../../../packages/transports/src");
9
+ const importEsm = new Function("specifier", "return import(specifier)");
10
+ const runtimeModuleUrl = (0, node_url_1.pathToFileURL)((0, node_path_1.join)(__dirname, "../../../../node_modules/@minasoft/mcp-runtime/dist/index.js")).href;
11
+ const statePath = process.env.MINA_ROUTER_STATE ?? (0, node_path_1.join)(process.cwd(), "data", "router-state.json");
12
+ const logPath = process.env.MINA_MCP_LOG;
13
+ const context = createContext();
14
+ let inputBuffer = Buffer.alloc(0);
15
+ let runtimePromise;
16
+ let providerPromise;
17
+ process.stdin.on("data", (chunk) => {
18
+ debugLog(`stdin data bytes=${chunk.length}`);
19
+ debugLog(`stdin raw=${JSON.stringify(chunk.toString("utf8"))}`);
20
+ inputBuffer = Buffer.concat([inputBuffer, chunk]);
21
+ readFrames().catch((error) => {
22
+ debugLog(`readFrames error=${error instanceof Error ? error.stack ?? error.message : String(error)}`);
23
+ sendFallbackError(undefined, error instanceof Error ? error.message : String(error));
24
+ });
25
+ });
26
+ debugLog(`startup cwd=${process.cwd()} statePath=${statePath} runtime=${runtimeModuleUrl}`);
27
+ function createContext() {
28
+ const fileState = new src_1.FileState(statePath);
29
+ const state = fileState.load();
30
+ const registry = new src_1.AgentRegistry(state.agents);
31
+ const requestStore = new src_1.RequestStore(state.requests);
32
+ const transports = new src_2.DefaultTransportRegistry()
33
+ .register("mock", new src_2.HeadlessTransport())
34
+ .register("headless", new src_2.HeadlessTransport())
35
+ .register("tmux", new src_2.TmuxTransport())
36
+ .register("zmux", new src_2.ZmuxTransport());
37
+ const baseContext = {
38
+ fileState,
39
+ registry,
40
+ requestStore,
41
+ save: () => fileState.save({
42
+ agents: registry.list(),
43
+ requests: requestStore.list(),
44
+ }),
45
+ };
46
+ const router = new src_1.AgentRouter({
47
+ registry,
48
+ requestStore,
49
+ transports,
50
+ onStateChanged: baseContext.save,
51
+ });
52
+ return {
53
+ ...baseContext,
54
+ transports,
55
+ router,
56
+ };
57
+ }
58
+ async function readFrames() {
59
+ while (true) {
60
+ const headerEnd = inputBuffer.indexOf("\r\n\r\n");
61
+ if (headerEnd === -1) {
62
+ return;
63
+ }
64
+ const header = inputBuffer.slice(0, headerEnd).toString("utf8");
65
+ const lengthMatch = /Content-Length:\s*(\d+)/i.exec(header);
66
+ if (!lengthMatch) {
67
+ throw new Error("Missing Content-Length header.");
68
+ }
69
+ const contentLength = Number(lengthMatch[1]);
70
+ const frameStart = headerEnd + 4;
71
+ const frameEnd = frameStart + contentLength;
72
+ if (inputBuffer.length < frameEnd) {
73
+ return;
74
+ }
75
+ const body = inputBuffer.slice(frameStart, frameEnd).toString("utf8");
76
+ inputBuffer = inputBuffer.slice(frameEnd);
77
+ debugLog(`frame body=${body}`);
78
+ await handleMessage(JSON.parse(body));
79
+ }
80
+ }
81
+ async function handleMessage(message) {
82
+ const runtime = await loadRuntime();
83
+ const provider = await createProvider();
84
+ debugLog(`handle method=${message.method} id=${String(message.id ?? "")}`);
85
+ const result = await runtime.handleMcpJsonRpcMessage(message, provider, {
86
+ context: { requestId: String(message.id ?? "") },
87
+ });
88
+ debugLog(`result kind=${result.kind}`);
89
+ sendRuntimeResult(result);
90
+ }
91
+ async function loadRuntime() {
92
+ runtimePromise ?? (runtimePromise = importEsm(runtimeModuleUrl));
93
+ return runtimePromise;
94
+ }
95
+ async function createProvider() {
96
+ if (providerPromise) {
97
+ return providerPromise;
98
+ }
99
+ const tools = [
100
+ {
101
+ name: "list_agents",
102
+ description: "List registered Mina helper agents.",
103
+ inputSchema: {
104
+ type: "object",
105
+ properties: {},
106
+ additionalProperties: false,
107
+ },
108
+ },
109
+ {
110
+ name: "register_agent",
111
+ description: "Register or update an agent in Mina AI Router. Use this from a visible project Codex session to connect itself to the router.",
112
+ inputSchema: {
113
+ type: "object",
114
+ properties: {
115
+ id: { type: "string" },
116
+ name: { type: "string" },
117
+ agentType: { type: "string" },
118
+ transport: { type: "string" },
119
+ sessionId: { type: "string" },
120
+ projectRoot: { type: "string" },
121
+ tmuxTarget: { type: "string" },
122
+ startupCommand: { type: "string" },
123
+ capabilitySummary: { type: "string" },
124
+ capabilitySources: { type: "string" },
125
+ },
126
+ required: ["id", "agentType", "transport", "sessionId", "projectRoot"],
127
+ additionalProperties: false,
128
+ },
129
+ },
130
+ {
131
+ name: "call_agent",
132
+ description: "Send a task to a registered helper agent and wait for a marker-wrapped response.",
133
+ inputSchema: {
134
+ type: "object",
135
+ properties: {
136
+ target: { type: "string" },
137
+ task: { type: "string" },
138
+ timeoutMs: { type: "number" },
139
+ },
140
+ required: ["target", "task"],
141
+ additionalProperties: false,
142
+ },
143
+ },
144
+ {
145
+ name: "get_request_status",
146
+ description: "Return status for a Mina AI Router request.",
147
+ inputSchema: {
148
+ type: "object",
149
+ properties: {
150
+ requestId: { type: "string" },
151
+ },
152
+ required: ["requestId"],
153
+ additionalProperties: false,
154
+ },
155
+ },
156
+ ];
157
+ providerPromise = Promise.resolve({
158
+ serverInfo: {
159
+ name: "mina-ai-router",
160
+ version: "0.1.0",
161
+ },
162
+ tools: {
163
+ async list() {
164
+ return { items: tools };
165
+ },
166
+ async call({ name, arguments: args }) {
167
+ return callTool(name, args ?? {});
168
+ },
169
+ },
170
+ });
171
+ return providerPromise;
172
+ }
173
+ async function callTool(name, args) {
174
+ switch (name) {
175
+ case "list_agents": {
176
+ const agents = await context.router.listAgentStatuses();
177
+ return jsonToolResult({ agents });
178
+ }
179
+ case "register_agent": {
180
+ try {
181
+ const agent = agentFromArgs(args);
182
+ context.registry.register(agent);
183
+ context.save();
184
+ const agents = await context.router.listAgentStatuses();
185
+ return jsonToolResult({ agent, agents });
186
+ }
187
+ catch (error) {
188
+ return {
189
+ kind: "invalid_params",
190
+ message: error instanceof Error ? error.message : String(error),
191
+ };
192
+ }
193
+ }
194
+ case "call_agent": {
195
+ const target = typeof args.target === "string" ? args.target : "";
196
+ const task = typeof args.task === "string" ? args.task : "";
197
+ const timeoutMs = typeof args.timeoutMs === "number" ? args.timeoutMs : undefined;
198
+ if (!target || !task) {
199
+ return { kind: "invalid_params", message: "call_agent requires string target and task." };
200
+ }
201
+ try {
202
+ const response = await context.router.callAgent({ target, task, timeoutMs });
203
+ return jsonToolResult(response);
204
+ }
205
+ catch (error) {
206
+ const message = error instanceof Error ? error.message : String(error);
207
+ return {
208
+ kind: "tool_error",
209
+ content: [{ type: "text", text: message }],
210
+ structuredContent: { error: message },
211
+ };
212
+ }
213
+ finally {
214
+ context.save();
215
+ }
216
+ }
217
+ case "get_request_status": {
218
+ const requestId = typeof args.requestId === "string" ? args.requestId : "";
219
+ if (!requestId) {
220
+ return { kind: "invalid_params", message: "get_request_status requires string requestId." };
221
+ }
222
+ try {
223
+ const found = context.router.getRequest(requestId);
224
+ return jsonToolResult({
225
+ requestId: found.id,
226
+ status: found.status,
227
+ error: found.error,
228
+ });
229
+ }
230
+ catch {
231
+ return { kind: "not_found", message: `Request "${requestId}" was not found.` };
232
+ }
233
+ }
234
+ default:
235
+ return { kind: "not_found", message: `Unknown tool "${name}".` };
236
+ }
237
+ }
238
+ function agentFromArgs(args) {
239
+ const id = requiredString(args.id, "id");
240
+ return {
241
+ id,
242
+ name: stringValue(args.name) ?? id,
243
+ agentType: requiredString(args.agentType, "agentType"),
244
+ transport: requiredString(args.transport, "transport"),
245
+ sessionId: requiredString(args.sessionId, "sessionId"),
246
+ projectRoot: requiredString(args.projectRoot, "projectRoot"),
247
+ tmuxTarget: stringValue(args.tmuxTarget),
248
+ startupCommand: stringValue(args.startupCommand),
249
+ capabilitySummary: stringValue(args.capabilitySummary),
250
+ capabilitySources: stringValue(args.capabilitySources),
251
+ };
252
+ }
253
+ function requiredString(value, field) {
254
+ if (typeof value !== "string" || !value.trim()) {
255
+ throw new Error(`register_agent requires string ${field}.`);
256
+ }
257
+ return value.trim();
258
+ }
259
+ function stringValue(value) {
260
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
261
+ }
262
+ function jsonToolResult(value) {
263
+ const text = JSON.stringify(value, null, 2);
264
+ return {
265
+ kind: "success",
266
+ content: [{ type: "text", text }],
267
+ structuredContent: value,
268
+ };
269
+ }
270
+ function sendRuntimeResult(result) {
271
+ if (result.kind === "accepted") {
272
+ debugLog("send accepted/no-response");
273
+ return;
274
+ }
275
+ if (result.kind === "raw") {
276
+ debugLog(`send raw bytes=${Buffer.byteLength(result.body)}`);
277
+ process.stdout.write(`Content-Length: ${Buffer.byteLength(result.body)}\r\n\r\n`);
278
+ process.stdout.write(result.body);
279
+ return;
280
+ }
281
+ const body = JSON.stringify(result.body);
282
+ debugLog(`send json body=${body}`);
283
+ process.stdout.write(`Content-Length: ${Buffer.byteLength(body)}\r\n\r\n`);
284
+ process.stdout.write(body);
285
+ }
286
+ function sendFallbackError(id, message) {
287
+ if (id === undefined) {
288
+ return;
289
+ }
290
+ const body = JSON.stringify({
291
+ jsonrpc: "2.0",
292
+ id,
293
+ error: { code: -32603, message },
294
+ });
295
+ process.stdout.write(`Content-Length: ${Buffer.byteLength(body)}\r\n\r\n`);
296
+ process.stdout.write(body);
297
+ }
298
+ function debugLog(message) {
299
+ if (!logPath) {
300
+ return;
301
+ }
302
+ try {
303
+ (0, node_fs_1.appendFileSync)(logPath, `${new Date().toISOString()} ${message}\n`);
304
+ }
305
+ catch {
306
+ // Never break MCP startup because logging failed.
307
+ }
308
+ }
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FileState = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const emptyState = {
7
+ agents: [],
8
+ requests: [],
9
+ };
10
+ class FileState {
11
+ constructor(filePath) {
12
+ this.filePath = filePath;
13
+ }
14
+ load() {
15
+ if (!(0, node_fs_1.existsSync)(this.filePath)) {
16
+ return { ...emptyState };
17
+ }
18
+ const raw = (0, node_fs_1.readFileSync)(this.filePath, "utf8").trim();
19
+ if (!raw) {
20
+ return { ...emptyState };
21
+ }
22
+ const parsed = JSON.parse(raw);
23
+ return {
24
+ agents: parsed.agents ?? [],
25
+ requests: parsed.requests ?? [],
26
+ };
27
+ }
28
+ save(state) {
29
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(this.filePath), { recursive: true });
30
+ const tempPath = `${this.filePath}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
31
+ (0, node_fs_1.writeFileSync)(tempPath, `${JSON.stringify(state, null, 2)}\n`);
32
+ (0, node_fs_1.renameSync)(tempPath, this.filePath);
33
+ }
34
+ }
35
+ exports.FileState = FileState;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRequestId = createRequestId;
4
+ let sequence = 0;
5
+ function createRequestId() {
6
+ sequence += 1;
7
+ return `mar-${Date.now().toString(36)}-${sequence.toString(36)}`;
8
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./ids"), exports);
18
+ __exportStar(require("./file-state"), exports);
19
+ __exportStar(require("./prompt-envelope"), exports);
20
+ __exportStar(require("./registry"), exports);
21
+ __exportStar(require("./request-store"), exports);
22
+ __exportStar(require("./response-parser"), exports);
23
+ __exportStar(require("./router"), exports);
24
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildPromptEnvelope = buildPromptEnvelope;
4
+ function buildPromptEnvelope(request, target) {
5
+ return [
6
+ "[Mina AI Router Request]",
7
+ `Request ID: ${request.id}`,
8
+ `From: ${request.sourceAgent}`,
9
+ `To: ${target.id} / ${target.agentType}`,
10
+ "",
11
+ "The source agent is asking you to answer the following request:",
12
+ `"${request.task}"`,
13
+ "",
14
+ "Please answer based on your current project context.",
15
+ "Include:",
16
+ "- relevant project flow",
17
+ "- API/status constraints",
18
+ "- UI or integration states the source project should support",
19
+ "- recommended behavior",
20
+ "- risks or assumptions",
21
+ "",
22
+ "Wrap your final answer with:",
23
+ `<<<MINA_AGENT_RESPONSE_START ${request.id}>>>`,
24
+ "[your answer]",
25
+ `<<<MINA_AGENT_RESPONSE_END ${request.id}>>>`,
26
+ ].join("\n");
27
+ }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AgentRegistry = void 0;
4
+ class AgentRegistry {
5
+ constructor(agents = []) {
6
+ this.agents = new Map();
7
+ for (const agent of agents) {
8
+ this.register(agent);
9
+ }
10
+ }
11
+ register(agent) {
12
+ this.agents.set(agent.id, agent);
13
+ return agent;
14
+ }
15
+ list() {
16
+ return Array.from(this.agents.values()).sort((a, b) => a.id.localeCompare(b.id));
17
+ }
18
+ get(id) {
19
+ return this.agents.get(id);
20
+ }
21
+ unregister(id) {
22
+ const agent = this.require(id);
23
+ this.agents.delete(id);
24
+ return agent;
25
+ }
26
+ require(id) {
27
+ const agent = this.get(id);
28
+ if (!agent) {
29
+ throw new Error(`Agent "${id}" is not registered.`);
30
+ }
31
+ return agent;
32
+ }
33
+ }
34
+ exports.AgentRegistry = AgentRegistry;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RequestStore = void 0;
4
+ class RequestStore {
5
+ constructor(requests = []) {
6
+ this.requests = new Map();
7
+ for (const request of requests) {
8
+ this.requests.set(request.id, request);
9
+ }
10
+ }
11
+ create(request) {
12
+ this.requests.set(request.id, request);
13
+ return request;
14
+ }
15
+ list() {
16
+ return Array.from(this.requests.values()).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
17
+ }
18
+ get(id) {
19
+ return this.requests.get(id);
20
+ }
21
+ require(id) {
22
+ const request = this.get(id);
23
+ if (!request) {
24
+ throw new Error(`Request "${id}" was not found.`);
25
+ }
26
+ return request;
27
+ }
28
+ updateStatus(id, status, patch = {}) {
29
+ const current = this.require(id);
30
+ const updated = {
31
+ ...current,
32
+ ...patch,
33
+ status,
34
+ updatedAt: new Date().toISOString(),
35
+ };
36
+ this.requests.set(id, updated);
37
+ return updated;
38
+ }
39
+ patch(id, patch) {
40
+ const current = this.require(id);
41
+ const updated = {
42
+ ...current,
43
+ ...patch,
44
+ updatedAt: new Date().toISOString(),
45
+ };
46
+ this.requests.set(id, updated);
47
+ return updated;
48
+ }
49
+ }
50
+ exports.RequestStore = RequestStore;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ResponseMarkerNotFoundError = void 0;
4
+ exports.parseMarkedResponse = parseMarkedResponse;
5
+ class ResponseMarkerNotFoundError extends Error {
6
+ constructor(requestId) {
7
+ super(`Response markers were not found for request ${requestId}.`);
8
+ this.name = "ResponseMarkerNotFoundError";
9
+ }
10
+ }
11
+ exports.ResponseMarkerNotFoundError = ResponseMarkerNotFoundError;
12
+ function parseMarkedResponse(output, requestId) {
13
+ const startMarker = `<<<MINA_AGENT_RESPONSE_START ${requestId}>>>`;
14
+ const endMarker = `<<<MINA_AGENT_RESPONSE_END ${requestId}>>>`;
15
+ let end = output.lastIndexOf(endMarker);
16
+ while (end !== -1) {
17
+ const start = output.lastIndexOf(startMarker, end);
18
+ if (start === -1) {
19
+ throw new ResponseMarkerNotFoundError(requestId);
20
+ }
21
+ const contentStart = start + startMarker.length;
22
+ const answer = output.slice(contentStart, end).trim();
23
+ if (!isPlaceholderAnswer(answer)) {
24
+ return answer;
25
+ }
26
+ end = output.lastIndexOf(endMarker, start - 1);
27
+ }
28
+ throw new ResponseMarkerNotFoundError(requestId);
29
+ }
30
+ function isPlaceholderAnswer(answer) {
31
+ const normalized = answer.replace(/[|\s]/g, "");
32
+ return normalized === "..." || normalized === "[youranswer]";
33
+ }
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AgentRouter = void 0;
4
+ const ids_1 = require("./ids");
5
+ const prompt_envelope_1 = require("./prompt-envelope");
6
+ const response_parser_1 = require("./response-parser");
7
+ class AgentRouter {
8
+ constructor(options) {
9
+ this.busyAgents = new Set();
10
+ this.registry = options.registry;
11
+ this.requestStore = options.requestStore;
12
+ this.transports = options.transports;
13
+ this.defaultSourceAgent = options.defaultSourceAgent ?? "main";
14
+ this.defaultTimeoutMs = options.defaultTimeoutMs ?? 300000;
15
+ this.onStateChanged = options.onStateChanged;
16
+ }
17
+ listAgents() {
18
+ return this.registry.list();
19
+ }
20
+ listRequests() {
21
+ return this.requestStore.list();
22
+ }
23
+ async listAgentStatuses() {
24
+ const requests = this.requestStore.list();
25
+ return Promise.all(this.registry.list().map(async (agent) => {
26
+ const agentRequests = requests.filter((request) => request.targetAgent === agent.id);
27
+ const lastRequest = agentRequests[agentRequests.length - 1];
28
+ const transport = this.transports.get(agent.transport);
29
+ const status = transport.status
30
+ ? await transport.status(agent)
31
+ : { status: "unknown" };
32
+ return {
33
+ id: agent.id,
34
+ name: agent.name,
35
+ agentType: agent.agentType,
36
+ transport: agent.transport,
37
+ sessionId: agent.sessionId,
38
+ projectRoot: agent.projectRoot,
39
+ capabilitySummary: agent.capabilitySummary,
40
+ capabilitySources: agent.capabilitySources,
41
+ status: this.busyAgents.has(agent.id) ? "busy" : status.status,
42
+ detail: status.detail,
43
+ lastRequestStatus: lastRequest?.status,
44
+ };
45
+ }));
46
+ }
47
+ getRequest(requestId) {
48
+ return this.requestStore.require(requestId);
49
+ }
50
+ async callAgent(input) {
51
+ const target = this.registry.require(input.target);
52
+ if (this.busyAgents.has(target.id)) {
53
+ throw new Error(`Agent "${target.id}" is busy with another request.`);
54
+ }
55
+ this.busyAgents.add(target.id);
56
+ const now = new Date().toISOString();
57
+ const request = this.requestStore.create({
58
+ id: (0, ids_1.createRequestId)(),
59
+ sourceAgent: input.sourceAgent ?? this.defaultSourceAgent,
60
+ targetAgent: target.id,
61
+ task: input.task,
62
+ status: "created",
63
+ createdAt: now,
64
+ updatedAt: now,
65
+ });
66
+ this.persistState();
67
+ const transport = this.transports.get(target.transport);
68
+ const prompt = (0, prompt_envelope_1.buildPromptEnvelope)(request, target);
69
+ try {
70
+ await transport.send(target, prompt, request.id);
71
+ this.requestStore.updateStatus(request.id, "sent");
72
+ this.persistState();
73
+ this.requestStore.updateStatus(request.id, "waiting");
74
+ this.persistState();
75
+ const output = await transport.waitForResponse(target, request.id, input.timeoutMs ?? this.defaultTimeoutMs);
76
+ const answer = (0, response_parser_1.parseMarkedResponse)(output, request.id);
77
+ this.requestStore.updateStatus(request.id, "answered", { answer });
78
+ this.persistState();
79
+ return {
80
+ requestId: request.id,
81
+ target: target.id,
82
+ answer,
83
+ };
84
+ }
85
+ catch (error) {
86
+ const message = error instanceof Error ? error.message : String(error);
87
+ const status = /time(?:d)?\s*out|timeout/i.test(message) ? "timeout" : "failed";
88
+ this.requestStore.updateStatus(request.id, status, { error: message });
89
+ this.persistState();
90
+ throw error;
91
+ }
92
+ finally {
93
+ this.busyAgents.delete(target.id);
94
+ }
95
+ }
96
+ persistState() {
97
+ this.onStateChanged?.();
98
+ }
99
+ }
100
+ exports.AgentRouter = AgentRouter;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });