@artinet/agent-relay-mcp 0.0.1

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.
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Copyright 2025 The Artinet Project
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { A2AClient, } from "@artinet/sdk";
6
+ import { AgentManager, getAgentCard } from "./manager.js";
7
+ import { scanAgents, DEFAULT_MAX_THREADS } from "./scan.js";
8
+ import { getAgentRuntimePath } from "./sync.js";
9
+ export const DEFAULT_SYNC_INTERVAL = parseInt(process.env.ARTINET_RELAY_SYNC_INTERVAL || "30000");
10
+ /**
11
+ * @description AgentRelay is a class that manages the agents and their interactions.
12
+ * It scans for agents on the network and registers them.
13
+ * It also provides a way to send messages to the agents and get tasks from them.
14
+ * It also provides a way to get the agent card and search for agents.
15
+ */
16
+ export class AgentRelay extends AgentManager {
17
+ config;
18
+ timeoutId = null;
19
+ constructor(config) {
20
+ super(config.agents);
21
+ this.config = {
22
+ ...config,
23
+ abortSignal: config.abortSignal ?? new AbortController().signal,
24
+ syncInterval: config.syncInterval ?? DEFAULT_SYNC_INTERVAL,
25
+ configPath: config.configPath ?? getAgentRuntimePath(),
26
+ scanConfig: {
27
+ ...(config.scanConfig ?? {}),
28
+ host: config.scanConfig?.host ?? "localhost",
29
+ startPort: config.scanConfig?.startPort ?? 3000,
30
+ endPort: config.scanConfig?.endPort ?? 5000,
31
+ threads: config.scanConfig?.threads ?? DEFAULT_MAX_THREADS,
32
+ },
33
+ agents: config.agents ?? new Map(),
34
+ };
35
+ }
36
+ /**
37
+ * @description Create a new AgentRelay instance and ensure its ready to use
38
+ * @param config - The configuration for the AgentRelay
39
+ * @returns The AgentRelay instance
40
+ */
41
+ static async create(config) {
42
+ const relay = new AgentRelay(config);
43
+ await relay.findAgents(relay.config.scanConfig);
44
+ relay.startSync().catch((error) => {
45
+ console.error("Error running sync: ", error);
46
+ throw error;
47
+ });
48
+ return relay;
49
+ }
50
+ getAgent(agentId) {
51
+ //to avoid recursive calls
52
+ if (agentId === this.config.callerId) {
53
+ return undefined;
54
+ }
55
+ return super.getAgent(agentId);
56
+ }
57
+ async close() {
58
+ if (this.timeoutId) {
59
+ clearTimeout(this.timeoutId);
60
+ }
61
+ await super.close();
62
+ }
63
+ async startSync() {
64
+ this.timeoutId = setInterval(async () => {
65
+ await this.findAgents(this.config.scanConfig);
66
+ }, this.config.syncInterval);
67
+ }
68
+ async findAgents(config) {
69
+ const configs = await scanAgents(config).catch((error) => {
70
+ console.error(`Error scanning agents: ${error}`);
71
+ return [];
72
+ });
73
+ for (const config of configs) {
74
+ await this.registerAgent(config).catch((error) => {
75
+ console.error(`Error registering agent: ${error}`);
76
+ });
77
+ }
78
+ }
79
+ //todo if agent is not a server, serverfy it?
80
+ // I think we'll avoid this for now
81
+ // Because it will allow Relay's to have local environments that are unique to them
82
+ async registerAgent(agent) {
83
+ let agentCard = await getAgentCard(agent);
84
+ if (!agentCard &&
85
+ "url" in agent &&
86
+ "headers" in agent &&
87
+ "fallbackPath" in agent) {
88
+ try {
89
+ agent = new A2AClient(agent.url, agent.headers, agent.fallbackPath);
90
+ agentCard = await agent.agentCard();
91
+ }
92
+ catch (error) {
93
+ console.error("error creating client for agent: ", error);
94
+ throw error;
95
+ }
96
+ }
97
+ else if (!agentCard) {
98
+ throw new Error("Invalid agent type");
99
+ }
100
+ await super.setAgent(agent);
101
+ return agentCard;
102
+ }
103
+ async deregisterAgent(id) {
104
+ super.deleteAgent(id);
105
+ }
106
+ async sendMessage(agentId, messageParams) {
107
+ const agent = this.getAgent(agentId);
108
+ if (!agent) {
109
+ throw new Error(`Agent ${agentId} not found`);
110
+ }
111
+ const sendMessageResult = await agent.sendMessage(messageParams);
112
+ if (!sendMessageResult) {
113
+ throw new Error(`Failed to send message to agent ${agentId}`);
114
+ }
115
+ return sendMessageResult;
116
+ }
117
+ async getTask(agentId, taskQuery) {
118
+ const agent = this.getAgent(agentId);
119
+ if (!agent) {
120
+ throw new Error(`Agent ${agentId} not found`);
121
+ }
122
+ const task = await agent.getTask(taskQuery);
123
+ if (!task) {
124
+ throw new Error(`Task ${agentId} not found`);
125
+ }
126
+ return task;
127
+ }
128
+ async cancelTask(agentId, taskId) {
129
+ const agent = this.getAgent(agentId);
130
+ if (!agent) {
131
+ throw new Error(`Agent ${agentId} not found`);
132
+ }
133
+ const task = await agent.cancelTask(taskId);
134
+ if (!task) {
135
+ throw new Error(`Task ${agentId} not found`);
136
+ }
137
+ return task;
138
+ }
139
+ async getAgentCard(agentId) {
140
+ const agent = this.getAgent(agentId);
141
+ if (!agent) {
142
+ throw new Error(`Agent ${agentId} not found`);
143
+ }
144
+ let agentCard = await getAgentCard(agent);
145
+ if (!agentCard) {
146
+ throw new Error(`Invalid agent type`);
147
+ }
148
+ return agentCard;
149
+ }
150
+ async searchAgents(query) {
151
+ const agents = this.getAgents();
152
+ return (await Promise.all(agents.map(async (agent) => agent instanceof A2AClient ? await agent.agentCard() : agent.agentCard))).filter((agentCard) => agentCard.name.toLowerCase().includes(query.toLowerCase()) ||
153
+ agentCard.description.toLowerCase().includes(query.toLowerCase()) ||
154
+ agentCard.skills.some((skill) => skill.name.toLowerCase().includes(query.toLowerCase()) ||
155
+ skill.description.toLowerCase().includes(query.toLowerCase())));
156
+ }
157
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Copyright 2025 The Artinet Project
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { ClientConfig, ScanConfig } from "./types/index.js";
6
+ export declare const DEFAULT_MAX_THREADS = 250;
7
+ export declare function scanAgents(config: ScanConfig): Promise<ClientConfig[]>;
@@ -0,0 +1,30 @@
1
+ import * as portscanner from "portscanner";
2
+ import pLimit from "p-limit";
3
+ export const DEFAULT_MAX_THREADS = 250;
4
+ export async function scanAgents(config) {
5
+ //avoid overwhelming the port scanner
6
+ const limit = pLimit(config.threads ?? DEFAULT_MAX_THREADS);
7
+ const portChecks = [];
8
+ for (let port = config.startPort; port <= config.endPort; port++) {
9
+ portChecks.push(limit(async () => await portscanner
10
+ .checkPortStatus(port, config.host)
11
+ .then((status) => ({ port, status }))));
12
+ }
13
+ const results = await Promise.all(portChecks);
14
+ const openPorts = results
15
+ .filter((r) => r.status === "open")
16
+ .map((r) => r.port);
17
+ if (!openPorts?.length) {
18
+ // console.log("No open ports found");
19
+ return [];
20
+ }
21
+ const configs = [];
22
+ for (const port of openPorts) {
23
+ configs.push({
24
+ url: `http://${config.host}:${port}`,
25
+ headers: config.headers,
26
+ fallbackPath: config.fallbackPath,
27
+ });
28
+ }
29
+ return configs;
30
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Copyright 2025 The Artinet Project
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { ServerOptions } from "@modelcontextprotocol/sdk/server/index.js";
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import { Implementation } from "@modelcontextprotocol/sdk/types.js";
8
+ import { AgentRelayConfig } from "./types/index.js";
9
+ declare class RelayServer extends McpServer {
10
+ private relay;
11
+ constructor(info?: Implementation, options?: ServerOptions);
12
+ close(): Promise<void>;
13
+ init(config: AgentRelayConfig): Promise<void>;
14
+ }
15
+ export { RelayServer };
@@ -0,0 +1,218 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { AgentRelay } from "./relay.js";
3
+ import { v4 as uuidv4 } from "uuid";
4
+ import { TaskSchema, AgentCardSchema, SendMessageSuccessResultSchema, getContent, } from "@artinet/sdk";
5
+ import { z } from "zod";
6
+ class RelayServer extends McpServer {
7
+ relay = null;
8
+ constructor(info = {
9
+ name: "agent-relay-server",
10
+ version: "0.0.1",
11
+ }, options = {}) {
12
+ super(info, {
13
+ ...options,
14
+ instructions: options?.instructions +
15
+ `The relay server is a tool that allows the assistant to:
16
+ - send messages to other agents
17
+ - get information about a task that is currently running orcancel a task that is currently running
18
+ - get information about an agent in the form of an agent card ( which includes the skills of the agent and its capabilities)
19
+ - view the available agents
20
+ - and search for agents based on a query
21
+
22
+ sendMessage - can be used to send a command to another agent.
23
+ getTask - can be used to get the status of a task.
24
+ cancelTask - can be used to cancel a task that is currently running.
25
+ getAgentCard - can be used to get the agent card of an agent. This is useful to get the skills of an agent and understand its capabilities.
26
+ viewAgents - can be used to view the available agents. This is useful to get the names of the agents and review their information.
27
+ searchAgents - can be used to search for agents based on a query. This is useful to find agents that match a specific skill or name.
28
+
29
+ The assistant should request viewAgents at the start of a conversation to get the names of the agents and review their information inorder to see if they would be useful for the current request.
30
+ The assistant should recall the taskId when sending a follow-up message to the same agent to continue the conversation.
31
+ The assistant should be clear and concise when making a request of another agent and provide all the necessary information to the agent to fulfill the request.
32
+ If the assistant lacks the ability to fulfill a request it should check if there are any other agents that would be able to fulfill the request and if another agent is available for the task, the assistant should send detailed instructions to the other agent to fulfill the request.
33
+ If no other agent is available for the task, the assistant should return a message to the user indicating that no other agent is available for the task.
34
+ If the assistant determines that the request has not been adequately fulfilled by the other agent, the assistant must determine whether to call the same agent again or call a different agent or to ask the user for clarification.
35
+ Upon completion of the request, the assistant should return the result to the user.
36
+ The assistant should always return the result to the user in a clear and concise manner.
37
+ `,
38
+ });
39
+ }
40
+ async close() {
41
+ await this.relay?.close();
42
+ await super.close();
43
+ }
44
+ async init(config) {
45
+ this.relay = await AgentRelay.create(config);
46
+ this.registerTool("sendMessage", {
47
+ title: "Send Message",
48
+ description: "A message will be sent to the target agent and a response object containing the full details of request will be returned to the assistant. Which contains the task id and the message sent to the agent, the conversation history and the artifacts created by the agent.",
49
+ inputSchema: z.object({
50
+ agentId: z
51
+ .string()
52
+ .describe("The id of the agent to send the message to."),
53
+ message: z.string().describe("The message to send to the agent."),
54
+ taskId: z
55
+ .string()
56
+ .describe("The id of the task that the message is related to. If not provided, a new task will be created.")
57
+ .optional(),
58
+ }).shape,
59
+ outputSchema: z.object({
60
+ result: SendMessageSuccessResultSchema,
61
+ }).shape,
62
+ }, async (args) => {
63
+ const result = await this.relay?.sendMessage(args.agentId, {
64
+ message: {
65
+ role: "user",
66
+ messageId: uuidv4(),
67
+ kind: "message",
68
+ parts: [{ text: args.message, kind: "text" }],
69
+ taskId: args.taskId ?? uuidv4(),
70
+ },
71
+ });
72
+ return {
73
+ content: [
74
+ {
75
+ type: "text",
76
+ text: getContent(result) ??
77
+ "No result from the agent. This may be because the agent is not responding or the message was not sent.",
78
+ },
79
+ ],
80
+ structuredContent: {
81
+ result: result,
82
+ },
83
+ };
84
+ });
85
+ this.registerTool("getTask", {
86
+ title: "Get Task",
87
+ description: "Get the status of a running task from an agent",
88
+ inputSchema: z
89
+ .object({
90
+ agentId: z
91
+ .string()
92
+ .describe("The id of the agent to get the task from."),
93
+ taskId: z.string().describe("The id of the task to get."),
94
+ })
95
+ .describe("The agent id and task query to get the status of a running task from the agent.").shape,
96
+ outputSchema: TaskSchema.shape,
97
+ }, async (args) => {
98
+ const result = await this.relay?.getTask(args.agentId, {
99
+ id: args.taskId,
100
+ });
101
+ return {
102
+ content: [
103
+ {
104
+ type: "text",
105
+ text: result?.status?.state ??
106
+ "No task found. This may be because the task is not running or the agent is not responding.",
107
+ },
108
+ ],
109
+ structuredContent: result,
110
+ };
111
+ });
112
+ this.registerTool("cancelTask", {
113
+ title: "Cancel Task",
114
+ description: "Cancel a running task from an agent",
115
+ inputSchema: z
116
+ .object({
117
+ agentId: z
118
+ .string()
119
+ .describe("The id of the agent to cancel the task from."),
120
+ taskId: z.string().describe("The id of the task to cancel."),
121
+ })
122
+ .describe("The agent id and task id to cancel a running task from the agent.").shape,
123
+ outputSchema: TaskSchema.shape,
124
+ }, async (args) => {
125
+ const result = await this.relay?.cancelTask(args.agentId, {
126
+ id: args.taskId,
127
+ });
128
+ return {
129
+ content: [
130
+ {
131
+ type: "text",
132
+ text: result?.status?.state ??
133
+ "No task found. This may be because the task is not running or the agent is not responding.",
134
+ },
135
+ ],
136
+ structuredContent: result,
137
+ };
138
+ });
139
+ this.registerTool("getAgentCard", {
140
+ title: "Get Agent Card",
141
+ description: "Get the agent card of an agent",
142
+ inputSchema: z.object({
143
+ agentId: z
144
+ .string()
145
+ .describe("The id of the agent to get the agent card from."),
146
+ }).shape,
147
+ outputSchema: AgentCardSchema.shape,
148
+ }, async (args) => {
149
+ const result = await this.relay?.getAgentCard(args.agentId);
150
+ const text = result
151
+ ? JSON.stringify(result, null, 2)
152
+ : "No agent card found. This may be because the agent is not registered or the agent is not responding.";
153
+ return {
154
+ content: [
155
+ {
156
+ type: "text",
157
+ text: text,
158
+ },
159
+ ],
160
+ structuredContent: result,
161
+ };
162
+ });
163
+ this.registerTool("viewAgents", {
164
+ title: "View Agents",
165
+ description: "View the agents that are registered with the relay.",
166
+ outputSchema: z.object({
167
+ agents: z
168
+ .array(AgentCardSchema)
169
+ .describe("The agents that are registered with the relay."),
170
+ }).shape,
171
+ }, async (_) => {
172
+ const result = (await this.relay?.getAgentCards()) ?? [];
173
+ const text = result && result.length > 0
174
+ ? JSON.stringify({ agents: result }, null, 2)
175
+ : "No agents registered with the relay.";
176
+ return {
177
+ content: [
178
+ {
179
+ type: "text",
180
+ text: text,
181
+ },
182
+ ],
183
+ structuredContent: {
184
+ agents: result ?? [],
185
+ },
186
+ };
187
+ });
188
+ this.registerTool("searchAgents", {
189
+ title: "Search Agents",
190
+ description: "Search for agents by name, description, or skills. The query is case insensitive and will match against the entire name, description, and skills of the agents.",
191
+ inputSchema: z.object({
192
+ query: z.string().describe("The query to search against."),
193
+ }).shape,
194
+ outputSchema: z.object({
195
+ agents: z
196
+ .array(AgentCardSchema)
197
+ .describe("The agents that match the query."),
198
+ }).shape,
199
+ }, async (args) => {
200
+ const result = (await this.relay?.searchAgents(args.query)) ?? [];
201
+ const text = result && result.length > 0
202
+ ? JSON.stringify({ agents: result }, null, 2)
203
+ : "No agents found. This may be because the query is not valid or the agents are not responding.";
204
+ return {
205
+ content: [
206
+ {
207
+ type: "text",
208
+ text: text,
209
+ },
210
+ ],
211
+ structuredContent: {
212
+ agents: result,
213
+ },
214
+ };
215
+ });
216
+ }
217
+ }
218
+ export { RelayServer };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Copyright 2025 The Artinet Project
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { IRuntimeSync, SyncConfig } from "./types/index.js";
6
+ import { AgentRuntime } from "./types/config.js";
7
+ export declare const DEFAULT_RELAY_CONFIG_PATH: string;
8
+ export declare function getAgentRuntimePath(configPath?: string): string;
9
+ export declare class RuntimeLoader {
10
+ private configPath;
11
+ constructor(configPath?: string);
12
+ loadAgentRuntime(): Promise<AgentRuntime>;
13
+ saveAgentRuntime(agentRuntime: AgentRuntime): Promise<void>;
14
+ }
15
+ export declare class Sync extends RuntimeLoader implements IRuntimeSync {
16
+ private syncInterval;
17
+ private runtime;
18
+ private abortSignal;
19
+ private _isRunning;
20
+ constructor(config: SyncConfig);
21
+ private initializeSync;
22
+ getRuntime(): Promise<AgentRuntime>;
23
+ updateRuntime(data: AgentRuntime): Promise<boolean>;
24
+ isRunning(): boolean;
25
+ close(): void;
26
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Copyright 2025 The Artinet Project
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { AgentRuntimeSchema, } from "./types/index.js";
6
+ import { join } from "path";
7
+ import { homedir } from "os";
8
+ import fs from "fs/promises";
9
+ import { dirname } from "path";
10
+ export const DEFAULT_RELAY_CONFIG_PATH = process.env.ARTINET_RELAY_CONFIG_DIR ||
11
+ (process.env.XDG_CONFIG_HOME
12
+ ? join(process.env.XDG_CONFIG_HOME, "symphony")
13
+ : join(homedir(), ".config", "symphony"));
14
+ export function getAgentRuntimePath(configPath = DEFAULT_RELAY_CONFIG_PATH) {
15
+ return join(configPath, "agent-runtime.json");
16
+ }
17
+ export class RuntimeLoader {
18
+ configPath;
19
+ constructor(configPath = DEFAULT_RELAY_CONFIG_PATH) {
20
+ this.configPath = configPath;
21
+ }
22
+ async loadAgentRuntime() {
23
+ const runtimeFilePath = getAgentRuntimePath(this.configPath);
24
+ const dir = dirname(runtimeFilePath);
25
+ try {
26
+ await fs.access(dir);
27
+ }
28
+ catch {
29
+ await fs.mkdir(dir, { recursive: true });
30
+ }
31
+ try {
32
+ await fs.access(runtimeFilePath);
33
+ }
34
+ catch {
35
+ await fs.writeFile(runtimeFilePath, "{}", "utf8");
36
+ }
37
+ try {
38
+ await fs.access(runtimeFilePath);
39
+ }
40
+ catch {
41
+ throw new Error(`agent runtime file does not exist or is not accessible: ${runtimeFilePath}`);
42
+ }
43
+ const agentRuntime = await fs.readFile(runtimeFilePath, "utf8");
44
+ if (agentRuntime.trim() === "") {
45
+ throw new Error("agent runtime file is empty");
46
+ }
47
+ const parsedRuntime = AgentRuntimeSchema.safeParse(JSON.parse(agentRuntime));
48
+ if (parsedRuntime.error) {
49
+ throw new Error("invalid agent runtime file: " + parsedRuntime.error.message);
50
+ }
51
+ // filter out removed agents
52
+ // we do this here so that all runtimes know not to add the removed agent to their runtime
53
+ const runtime = Object.fromEntries(Object.entries(parsedRuntime.data).filter(([_, value]) => value.removed === undefined ? true : value.removed !== true));
54
+ return runtime;
55
+ }
56
+ async saveAgentRuntime(agentRuntime) {
57
+ const runtimeFilePath = getAgentRuntimePath(this.configPath);
58
+ const currentRuntime = await this.loadAgentRuntime();
59
+ const newRuntime = { ...currentRuntime, ...agentRuntime };
60
+ await fs.writeFile(runtimeFilePath, JSON.stringify(newRuntime, null, 2), "utf8");
61
+ }
62
+ }
63
+ export class Sync extends RuntimeLoader {
64
+ syncInterval;
65
+ runtime = {};
66
+ abortSignal;
67
+ _isRunning = false;
68
+ constructor(config) {
69
+ super(config.configPath);
70
+ this.syncInterval = config.syncInterval;
71
+ this.abortSignal = config.abortSignal;
72
+ this.initializeSync().catch((error) => {
73
+ console.error("Error syncing runtime: ", error);
74
+ });
75
+ }
76
+ async initializeSync() {
77
+ this._isRunning = true;
78
+ while (!this.abortSignal.aborted && this._isRunning) {
79
+ this.runtime = await this.loadAgentRuntime();
80
+ await new Promise((resolve) => setTimeout(resolve, this.syncInterval));
81
+ }
82
+ }
83
+ async getRuntime() {
84
+ this.runtime = await this.loadAgentRuntime();
85
+ return this.runtime;
86
+ }
87
+ async updateRuntime(data) {
88
+ this.runtime = { ...this.runtime, ...data };
89
+ await this.saveAgentRuntime(this.runtime);
90
+ return true;
91
+ }
92
+ isRunning() {
93
+ return this._isRunning;
94
+ }
95
+ close() {
96
+ this._isRunning = false;
97
+ }
98
+ }
@@ -0,0 +1,76 @@
1
+ import { AgentType } from "./manager.js";
2
+ import { z } from "zod";
3
+ export declare const RuntimeServerConfigSchema: z.ZodObject<{
4
+ id: z.ZodOptional<z.ZodString>;
5
+ url: z.ZodString;
6
+ } & {
7
+ blocklist: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
8
+ } & {
9
+ removed: z.ZodOptional<z.ZodBoolean>;
10
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
11
+ fallbackPath: z.ZodOptional<z.ZodString>;
12
+ }, "strip", z.ZodTypeAny, {
13
+ url: string;
14
+ id?: string | undefined;
15
+ headers?: Record<string, string> | undefined;
16
+ blocklist?: string[] | undefined;
17
+ removed?: boolean | undefined;
18
+ fallbackPath?: string | undefined;
19
+ }, {
20
+ url: string;
21
+ id?: string | undefined;
22
+ headers?: Record<string, string> | undefined;
23
+ blocklist?: string[] | undefined;
24
+ removed?: boolean | undefined;
25
+ fallbackPath?: string | undefined;
26
+ }>;
27
+ export type RuntimeServerConfig = z.infer<typeof RuntimeServerConfigSchema>;
28
+ export declare const AgentRuntimeSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
29
+ id: z.ZodOptional<z.ZodString>;
30
+ url: z.ZodString;
31
+ } & {
32
+ blocklist: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
33
+ } & {
34
+ removed: z.ZodOptional<z.ZodBoolean>;
35
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
36
+ fallbackPath: z.ZodOptional<z.ZodString>;
37
+ }, "strip", z.ZodTypeAny, {
38
+ url: string;
39
+ id?: string | undefined;
40
+ headers?: Record<string, string> | undefined;
41
+ blocklist?: string[] | undefined;
42
+ removed?: boolean | undefined;
43
+ fallbackPath?: string | undefined;
44
+ }, {
45
+ url: string;
46
+ id?: string | undefined;
47
+ headers?: Record<string, string> | undefined;
48
+ blocklist?: string[] | undefined;
49
+ removed?: boolean | undefined;
50
+ fallbackPath?: string | undefined;
51
+ }>>;
52
+ export type AgentRuntime = z.infer<typeof AgentRuntimeSchema>;
53
+ export interface IRuntimeSync {
54
+ getRuntime(): Promise<AgentRuntime>;
55
+ updateRuntime(data: AgentRuntime): Promise<boolean>;
56
+ }
57
+ export interface RuntimeLoaderConfig {
58
+ configPath: string;
59
+ }
60
+ export interface SyncConfig extends Partial<RuntimeLoaderConfig> {
61
+ abortSignal: AbortSignal;
62
+ syncInterval: number;
63
+ }
64
+ export interface ScanConfig {
65
+ host: string;
66
+ startPort: number;
67
+ endPort: number;
68
+ headers?: Record<string, string>;
69
+ fallbackPath?: string;
70
+ threads?: number;
71
+ }
72
+ export interface AgentRelayConfig extends Partial<SyncConfig> {
73
+ callerId: string;
74
+ scanConfig?: ScanConfig;
75
+ agents?: Map<string, AgentType>;
76
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Copyright 2025 The Artinet Project
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { RemoteServerConfigSchema } from "@artinet/types";
6
+ import { z } from "zod";
7
+ export const RuntimeServerConfigSchema = RemoteServerConfigSchema.extend({
8
+ removed: z.boolean().optional(),
9
+ headers: z.record(z.string(), z.string()).optional(),
10
+ fallbackPath: z.string().optional(),
11
+ });
12
+ export const AgentRuntimeSchema = z.record(z.string(), RuntimeServerConfigSchema);
@@ -0,0 +1,4 @@
1
+ export * from "./manager.js";
2
+ export * from "./config.js";
3
+ export * from "./relay.js";
4
+ export * from "./schema.js";
@@ -0,0 +1,4 @@
1
+ export * from "./manager.js";
2
+ export * from "./config.js";
3
+ export * from "./relay.js";
4
+ export * from "./schema.js";
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Copyright 2025 The Artinet Project
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { Agent, A2AClient } from "@artinet/sdk";
6
+ export type AgentType = Agent | A2AClient;
7
+ export interface IAgentManager {
8
+ getAgent(id: string): AgentType | undefined;
9
+ setAgent(agent: AgentType): Promise<void>;
10
+ deleteAgent(id: string): void;
11
+ getAgents(): AgentType[];
12
+ getAgentCount(): number;
13
+ getAgentIds(): string[];
14
+ }
@@ -0,0 +1 @@
1
+ export {};