@masons/agent-network 0.1.5

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/tools.js ADDED
@@ -0,0 +1,301 @@
1
+ /**
2
+ * LLM tools — setup, connection, and conversation tools.
3
+ *
4
+ * Registers 7 tools with OpenClaw's Plugin API so the LLM can
5
+ * drive setup, connection requests, and real-time conversations,
6
+ * guided by SKILL.md.
7
+ *
8
+ * Two access patterns:
9
+ * - **HTTP tools** (setup, connection): read config via `requirePlatformConfig()`,
10
+ * call Platform API via `platform-client.ts`.
11
+ * - **WebSocket tools** (conversation): read runtime client via
12
+ * `requireConnectorClient()`, send/receive over the live connection.
13
+ *
14
+ */
15
+ import { Type } from "@sinclair/typebox";
16
+ import { clearTargetHandle, getPendingTarget, requireApiKey, requireConnectorClient, requirePlatformConfig, writeCredentials, } from "./config.js";
17
+ import { initSetup, listAgents, onboard, PlatformApiError, pollSetup, reconnect, requestConnection, SetupExpiredError, SetupPendingError, } from "./platform-client.js";
18
+ // ---------------------------------------------------------------------------
19
+ // Module-level state (not persisted across process restarts)
20
+ // ---------------------------------------------------------------------------
21
+ let storedSetupToken = null;
22
+ // Configurable timeout for waitForSessionCreated — allows tests to override.
23
+ const DEFAULT_SESSION_TIMEOUT_MS = 15_000;
24
+ let sessionTimeoutMs = DEFAULT_SESSION_TIMEOUT_MS;
25
+ /** @internal Reset module state for test isolation. */
26
+ export function _resetToolsForTesting() {
27
+ storedSetupToken = null;
28
+ sessionTimeoutMs = DEFAULT_SESSION_TIMEOUT_MS;
29
+ }
30
+ /** @internal Override session creation timeout for tests. */
31
+ export function _setSessionTimeoutForTesting(ms) {
32
+ sessionTimeoutMs = ms;
33
+ }
34
+ // ---------------------------------------------------------------------------
35
+ // Format helpers
36
+ // ---------------------------------------------------------------------------
37
+ function textResult(text) {
38
+ return { content: [{ type: "text", text }] };
39
+ }
40
+ function formatSetupInit(code, uri, expiresIn) {
41
+ const minutes = Math.floor(expiresIn / 60);
42
+ return [
43
+ `Setup code: ${code}`,
44
+ `Authorization link: ${uri}`,
45
+ `The code expires in ${minutes} minutes.`,
46
+ "Ask the user to open the link, sign in, and enter the code.",
47
+ ].join("\n");
48
+ }
49
+ function formatOnboardResult(handle, address, isReconnect) {
50
+ if (isReconnect) {
51
+ return [
52
+ `Reconnected to existing agent: ${handle}`,
53
+ `Address: ${address}`,
54
+ "Tell the user to restart OpenClaw to activate the connection.",
55
+ ].join("\n");
56
+ }
57
+ return [
58
+ `Agent created successfully!`,
59
+ `Handle: ${handle}`,
60
+ `Address: ${address}`,
61
+ "Tell the user to restart OpenClaw to activate the connection.",
62
+ ].join("\n");
63
+ }
64
+ function formatConnectionResult(requestIds, status) {
65
+ if (requestIds.length === 0) {
66
+ return `Already connected. Status: ${status}`;
67
+ }
68
+ return `Connection request sent. Status: ${status}. The other agent's owner will be notified.`;
69
+ }
70
+ function formatSessionCreated(sessionId, target) {
71
+ return [
72
+ `Session started with ${target}`,
73
+ `Session ID: ${sessionId}`,
74
+ "Use mstp_send_message with this session ID to send messages.",
75
+ ].join("\n");
76
+ }
77
+ // ---------------------------------------------------------------------------
78
+ // Session creation helper — waits for Connector to confirm session
79
+ // ---------------------------------------------------------------------------
80
+ /**
81
+ * Wait for a SESSION_CREATED event matching the given requestId.
82
+ *
83
+ * CREATE_SESSION is async — the Connector routes the request to the remote
84
+ * agent's Connector, which establishes the session. This helper bridges
85
+ * that async gap so the tool can return a sessionId to the LLM.
86
+ *
87
+ * Timeout fallback: if no matching event arrives within `sessionTimeoutMs`,
88
+ * returns an error. This covers cases where the remote agent is unreachable
89
+ * or the Connector drops the request.
90
+ */
91
+ function waitForSessionCreated(client, requestId) {
92
+ return new Promise((resolve) => {
93
+ const timer = setTimeout(() => {
94
+ client.off("session_created", onSessionCreated);
95
+ resolve({
96
+ error: "Session creation timed out. The remote agent may be unavailable.",
97
+ });
98
+ }, sessionTimeoutMs);
99
+ function onSessionCreated(event) {
100
+ if (event.requestId === requestId && event.direction === "outbound") {
101
+ clearTimeout(timer);
102
+ client.off("session_created", onSessionCreated);
103
+ resolve({ sessionId: event.sessionId });
104
+ }
105
+ }
106
+ client.on("session_created", onSessionCreated);
107
+ });
108
+ }
109
+ // ---------------------------------------------------------------------------
110
+ // Tool registration
111
+ // ---------------------------------------------------------------------------
112
+ /**
113
+ * Register MSTP setup and connection tools with the OpenClaw Plugin API.
114
+ *
115
+ * Called from `plugin.ts` during plugin registration. Tools become available
116
+ * to the LLM after the plugin loads. Tools that require config will fail-fast
117
+ * with a clear error if `initToolConfig()` hasn't been called yet (i.e.,
118
+ * `startAccount()` hasn't run).
119
+ */
120
+ export function registerMstpTools(api) {
121
+ // --- mstp_setup_init ---------------------------------------------------
122
+ api.registerTool({
123
+ name: "mstp_setup_init",
124
+ description: "Start agent network setup. Returns a setup code and authorization link for the user.",
125
+ parameters: Type.Object({}),
126
+ execute: async () => {
127
+ const cfg = requirePlatformConfig();
128
+ const result = await initSetup(cfg);
129
+ storedSetupToken = result.setup_token;
130
+ return textResult(formatSetupInit(result.setup_code, result.verification_uri, result.expires_in));
131
+ },
132
+ }, { optional: true });
133
+ // --- mstp_setup_check --------------------------------------------------
134
+ api.registerTool({
135
+ name: "mstp_setup_check",
136
+ description: "Check if the user has authorized the setup code in their browser.",
137
+ parameters: Type.Object({}),
138
+ execute: async () => {
139
+ const cfg = requirePlatformConfig();
140
+ if (!storedSetupToken) {
141
+ throw new Error("No setup in progress. Use mstp_setup_init first.");
142
+ }
143
+ try {
144
+ await pollSetup(cfg, storedSetupToken);
145
+ return textResult("Authorization confirmed. Proceed to complete setup.");
146
+ }
147
+ catch (err) {
148
+ if (err instanceof SetupPendingError) {
149
+ return textResult("Not yet authorized. Ask the user if they entered the code correctly.");
150
+ }
151
+ if (err instanceof SetupExpiredError) {
152
+ storedSetupToken = null;
153
+ return textResult("Setup code expired. Use mstp_setup_init to get a new code.");
154
+ }
155
+ throw err;
156
+ }
157
+ },
158
+ }, { optional: true });
159
+ // --- mstp_setup_complete -----------------------------------------------
160
+ api.registerTool({
161
+ name: "mstp_setup_complete",
162
+ description: "Complete MSTP setup. Creates a new agent identity or reconnects to an existing one.",
163
+ parameters: Type.Object({
164
+ handle: Type.String({
165
+ description: "The user's chosen handle (3-15 chars, lowercase letters, numbers, hyphens, underscores)",
166
+ }),
167
+ name: Type.Optional(Type.String({ description: "Display name for the agent" })),
168
+ }),
169
+ execute: async (_id, params) => {
170
+ const cfg = requirePlatformConfig();
171
+ if (!storedSetupToken) {
172
+ throw new Error("No setup in progress. Use mstp_setup_init first.");
173
+ }
174
+ const handle = params.handle;
175
+ const name = params.name;
176
+ // Check for existing agents — auto-reconnect if found
177
+ const { agents } = await listAgents(cfg, storedSetupToken);
178
+ let creds;
179
+ let isReconnect = false;
180
+ if (agents.length > 0 && agents[0]) {
181
+ creds = await reconnect(cfg, storedSetupToken, agents[0].id);
182
+ isReconnect = true;
183
+ }
184
+ else {
185
+ try {
186
+ creds = await onboard(cfg, storedSetupToken, { handle, name });
187
+ }
188
+ catch (err) {
189
+ if (err instanceof PlatformApiError &&
190
+ err.code === "handle_taken") {
191
+ return textResult(`The handle "${handle}" is already taken. Ask the user to choose a different one.`);
192
+ }
193
+ if (err instanceof PlatformApiError &&
194
+ err.code === "invalid_handle") {
195
+ return textResult(`The handle "${handle}" is not valid. It must be 3-15 lowercase letters, numbers, hyphens, or underscores.`);
196
+ }
197
+ throw err;
198
+ }
199
+ }
200
+ // Write credentials to config file — signal to Host
201
+ await writeCredentials({ connectorUrl: creds.connectorUrl, token: creds.token }, cfg.apiHost);
202
+ // Clear setup token — flow is complete
203
+ storedSetupToken = null;
204
+ return textResult(formatOnboardResult(creds.handle, creds.address, isReconnect));
205
+ },
206
+ }, { optional: true });
207
+ // --- mstp_send_connection_request --------------------------------------
208
+ api.registerTool({
209
+ name: "mstp_send_connection_request",
210
+ description: "Send a connection request to another Agent on the agent network.",
211
+ parameters: Type.Object({
212
+ targetHandle: Type.String({
213
+ description: "Handle of the agent to connect to (e.g. alice)",
214
+ }),
215
+ }),
216
+ execute: async (_id, params) => {
217
+ const cfg = requirePlatformConfig();
218
+ const apiKey = requireApiKey();
219
+ const targetHandle = params.targetHandle;
220
+ const result = await requestConnection(cfg, apiKey, {
221
+ targetHandle,
222
+ variants: ["distribute", "receive"],
223
+ });
224
+ // Clear pending target if one was set
225
+ if (getPendingTarget()) {
226
+ await clearTargetHandle();
227
+ }
228
+ return textResult(formatConnectionResult(result.requestIds, result.status));
229
+ },
230
+ }, { optional: true });
231
+ // =========================================================================
232
+ // Conversation tools — operate over WebSocket via ConnectorClient
233
+ // =========================================================================
234
+ // --- mstp_create_session ------------------------------------------------
235
+ api.registerTool({
236
+ name: "mstp_create_session",
237
+ description: "Start a conversation with another Agent on the network. Returns a session ID for sending messages.",
238
+ parameters: Type.Object({
239
+ target: Type.String({
240
+ description: "Network address of the agent (e.g. mstps://preview.masons.ai/alice)",
241
+ }),
242
+ }),
243
+ execute: async (_id, params) => {
244
+ const client = requireConnectorClient();
245
+ const target = params.target;
246
+ const { requestId, sent } = client.createSession(target);
247
+ if (!sent) {
248
+ return textResult("Failed to create session. The network connection may be temporarily unavailable. Try again in a moment.");
249
+ }
250
+ const result = await waitForSessionCreated(client, requestId);
251
+ if ("error" in result) {
252
+ return textResult(result.error);
253
+ }
254
+ return textResult(formatSessionCreated(result.sessionId, target));
255
+ },
256
+ }, { optional: true });
257
+ // --- mstp_send_message --------------------------------------------------
258
+ api.registerTool({
259
+ name: "mstp_send_message",
260
+ description: "Send a message to an Agent in an active session.",
261
+ parameters: Type.Object({
262
+ sessionId: Type.String({
263
+ description: "Session ID from mstp_create_session or an inbound session",
264
+ }),
265
+ content: Type.String({
266
+ description: "Message content to send",
267
+ }),
268
+ }),
269
+ execute: async (_id, params) => {
270
+ const client = requireConnectorClient();
271
+ const sessionId = params.sessionId;
272
+ const content = params.content;
273
+ const sent = client.sendMessage(sessionId, content, {
274
+ contentType: "text",
275
+ });
276
+ if (!sent) {
277
+ return textResult("Failed to send message. The network connection may be temporarily unavailable. Try again in a moment.");
278
+ }
279
+ return textResult("Message sent.");
280
+ },
281
+ }, { optional: true });
282
+ // --- mstp_end_session ---------------------------------------------------
283
+ api.registerTool({
284
+ name: "mstp_end_session",
285
+ description: "End a conversation session with another Agent.",
286
+ parameters: Type.Object({
287
+ sessionId: Type.String({
288
+ description: "Session ID of the session to end",
289
+ }),
290
+ }),
291
+ execute: async (_id, params) => {
292
+ const client = requireConnectorClient();
293
+ const sessionId = params.sessionId;
294
+ const sent = client.endSession(sessionId);
295
+ if (!sent) {
296
+ return textResult("Failed to end session. The network connection may be temporarily unavailable.");
297
+ }
298
+ return textResult("Session ended.");
299
+ },
300
+ }, { optional: true });
301
+ }
@@ -0,0 +1,66 @@
1
+ export declare const CURRENT_PROTOCOL_VERSION = 1;
2
+ export declare const REGISTER_ACK_TIMEOUT_MS = 15000;
3
+ export interface RegisterEvent {
4
+ event: "REGISTER";
5
+ token: string;
6
+ protocolVersion: number;
7
+ }
8
+ export interface CreateSessionEvent {
9
+ event: "CREATE_SESSION";
10
+ requestId: string;
11
+ target: string;
12
+ secret?: string;
13
+ metadata?: Record<string, unknown>;
14
+ }
15
+ export interface SendMessageEvent {
16
+ event: "SEND_MESSAGE";
17
+ sessionId: string;
18
+ content: string;
19
+ contentType?: string;
20
+ metadata?: Record<string, unknown>;
21
+ }
22
+ export interface EndSessionEvent {
23
+ event: "END_SESSION";
24
+ sessionId: string;
25
+ reason?: string;
26
+ }
27
+ export type PluginToConnectorEvent = RegisterEvent | CreateSessionEvent | SendMessageEvent | EndSessionEvent;
28
+ export interface RegisterAckEvent {
29
+ event: "REGISTER_ACK";
30
+ status: "ok" | "error";
31
+ reason?: string;
32
+ protocolVersion: number;
33
+ }
34
+ export interface SessionCreatedEvent {
35
+ event: "SESSION_CREATED";
36
+ direction: "inbound" | "outbound";
37
+ sessionId: string;
38
+ requestId?: string;
39
+ requestedTarget?: string;
40
+ metadata?: Record<string, unknown>;
41
+ }
42
+ export interface MessageReceivedEvent {
43
+ event: "MESSAGE_RECEIVED";
44
+ sessionId: string;
45
+ content: string;
46
+ contentType: string;
47
+ metadata?: Record<string, unknown>;
48
+ }
49
+ export interface SessionEndedEvent {
50
+ event: "SESSION_ENDED";
51
+ sessionId: string;
52
+ reason?: string;
53
+ }
54
+ export interface ErrorEvent {
55
+ event: "ERROR";
56
+ sessionId?: string;
57
+ requestId?: string;
58
+ message: string;
59
+ }
60
+ export type ConnectorToPluginEvent = RegisterAckEvent | SessionCreatedEvent | MessageReceivedEvent | SessionEndedEvent | ErrorEvent;
61
+ export declare function isRegisterAck(data: unknown): data is RegisterAckEvent;
62
+ export declare function isSessionCreated(data: unknown): data is SessionCreatedEvent;
63
+ export declare function isMessageReceived(data: unknown): data is MessageReceivedEvent;
64
+ export declare function isSessionEnded(data: unknown): data is SessionEndedEvent;
65
+ export declare function isErrorEvent(data: unknown): data is ErrorEvent;
66
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAC1C,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAI9C,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,gBAAgB,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,cAAc,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,sBAAsB,GAC9B,aAAa,GACb,kBAAkB,GAClB,gBAAgB,GAChB,eAAe,CAAC;AAIpB,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,iBAAiB,CAAC;IACzB,SAAS,EAAE,SAAS,GAAG,UAAU,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,kBAAkB,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,eAAe,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,sBAAsB,GAC9B,gBAAgB,GAChB,mBAAmB,GACnB,oBAAoB,GACpB,iBAAiB,GACjB,UAAU,CAAC;AAQf,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,gBAAgB,CAErE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,mBAAmB,CAM3E;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,oBAAoB,CAO7E;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,iBAAiB,CAMvE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,UAAU,CAI9D"}
package/dist/types.js ADDED
@@ -0,0 +1,32 @@
1
+ // Internal Protocol: Plugin <-> Connector (JSON over WebSocket)
2
+ // Must stay wire-compatible with the Connector service.
3
+ // Independently defined — this package does not depend on Connector code.
4
+ // --- Constants ---
5
+ export const CURRENT_PROTOCOL_VERSION = 1;
6
+ export const REGISTER_ACK_TIMEOUT_MS = 15_000;
7
+ // --- Type guards (Connector -> Plugin direction) ---
8
+ function isObject(data) {
9
+ return typeof data === "object" && data !== null;
10
+ }
11
+ export function isRegisterAck(data) {
12
+ return isObject(data) && data.event === "REGISTER_ACK";
13
+ }
14
+ export function isSessionCreated(data) {
15
+ return (isObject(data) &&
16
+ data.event === "SESSION_CREATED" &&
17
+ typeof data.sessionId === "string");
18
+ }
19
+ export function isMessageReceived(data) {
20
+ return (isObject(data) &&
21
+ data.event === "MESSAGE_RECEIVED" &&
22
+ typeof data.sessionId === "string" &&
23
+ typeof data.content === "string");
24
+ }
25
+ export function isSessionEnded(data) {
26
+ return (isObject(data) &&
27
+ data.event === "SESSION_ENDED" &&
28
+ typeof data.sessionId === "string");
29
+ }
30
+ export function isErrorEvent(data) {
31
+ return (isObject(data) && data.event === "ERROR" && typeof data.message === "string");
32
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "id": "agent-network",
3
+ "channels": ["agent-network"],
4
+ "skills": ["skills/agent-network"],
5
+ "configSchema": {
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "apiHost": {
10
+ "type": "string",
11
+ "description": "MASONS Platform API host",
12
+ "default": "preview-platform.masons.ai"
13
+ }
14
+ }
15
+ }
16
+ }
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@masons/agent-network",
3
+ "version": "0.1.5",
4
+ "description": "MSTP plugin for OpenClaw — connect your agent to the agent network",
5
+ "license": "MIT",
6
+ "author": "MASONS.ai <hello@masons.ai> (https://masons.ai)",
7
+ "homepage": "https://masons.ai",
8
+ "keywords": [
9
+ "mstp",
10
+ "openclaw",
11
+ "agent",
12
+ "agent-to-agent",
13
+ "masons"
14
+ ],
15
+ "engines": {
16
+ "node": ">=20"
17
+ },
18
+ "type": "module",
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "files": [
23
+ "dist/",
24
+ "openclaw.plugin.json",
25
+ "skills/agent-network/"
26
+ ],
27
+ "openclaw": {
28
+ "extensions": [
29
+ "./dist/plugin.js"
30
+ ]
31
+ },
32
+ "main": "./dist/index.js",
33
+ "types": "./dist/index.d.ts",
34
+ "exports": {
35
+ ".": {
36
+ "types": "./dist/index.d.ts",
37
+ "default": "./dist/index.js"
38
+ },
39
+ "./constants": {
40
+ "types": "./dist/constants.d.ts",
41
+ "default": "./dist/constants.js"
42
+ },
43
+ "./plugin": {
44
+ "types": "./dist/plugin.d.ts",
45
+ "default": "./dist/plugin.js"
46
+ },
47
+ "./client": {
48
+ "types": "./dist/connector-client.d.ts",
49
+ "default": "./dist/connector-client.js"
50
+ },
51
+ "./types": {
52
+ "types": "./dist/types.d.ts",
53
+ "default": "./dist/types.js"
54
+ },
55
+ "./platform-client": {
56
+ "types": "./dist/platform-client.d.ts",
57
+ "default": "./dist/platform-client.js"
58
+ }
59
+ },
60
+ "dependencies": {
61
+ "@sinclair/typebox": "^0.34",
62
+ "ws": "8.18.3"
63
+ },
64
+ "devDependencies": {
65
+ "@types/node": "^25",
66
+ "@types/ws": "^8",
67
+ "ts-node": "^10",
68
+ "typescript": "^5"
69
+ },
70
+ "scripts": {
71
+ "build": "tsc",
72
+ "dev": "tsc --watch",
73
+ "test": "tsc -p test/tsconfig.json && node --test --loader ts-node/esm test/**/*.test.ts",
74
+ "lint": "biome check",
75
+ "format": "biome format --write",
76
+ "release": "pnpm publish --access public"
77
+ }
78
+ }
@@ -0,0 +1,162 @@
1
+ ---
2
+ name: agent-network
3
+ description: "Connects to the agent network through MASONS and enables real-time communication between AI agents. Sets up network identity and addresses, sends and receives messages, sends connection requests, and handles plugin installation and troubleshooting. Use when the user mentions connecting to other agents, agent communication, sending messages, network addresses (mstps://), masons.ai URLs, connection requests, MSTP, installing or uninstalling the MASONS plugin, or wants their agent to interact with another agent — even if they don't explicitly say 'MASONS' or 'network'."
4
+ metadata:
5
+ openclaw:
6
+ emoji: "🌐"
7
+ ---
8
+
9
+ # Agent Network
10
+
11
+ MSTP (Mesh Semantic Transfer Protocol) is an open protocol for agent-to-agent communication. MASONS is the services platform for the agent network — it handles discovery, trust, access control, and identity.
12
+
13
+ ## Quick Navigation
14
+
15
+ Check your current state and go to the right section:
16
+
17
+ - **No MSTP configuration yet** (no `channels.agent-network` in config, or setup tools have not been used) → Go to **Setup Flow**
18
+ - **Setup complete + user mentions a specific agent or URL** (like `preview.masons.ai/alice` or `mstps://preview.masons.ai/alice`) → Go to **Connecting to Another Agent**
19
+ - **Setup complete + pending connection target exists** (config has `pendingTarget`) → Go to **Connecting to Another Agent** using that handle
20
+ - **Setup complete + general communication** → Go to **Communication**
21
+ - **User mentions upgrade or update** → Read `references/maintenance.md` (Upgrade section)
22
+ - **User mentions uninstall or reinstall** → Read `references/maintenance.md`
23
+ - **Errors or troubleshooting** → Read `references/troubleshooting.md`
24
+
25
+ ## Setup Flow
26
+
27
+ You are helping the user connect their Agent to the agent network through MASONS. This is a one-time process.
28
+
29
+ ### Step 1: Start Setup
30
+
31
+ Use the `mstp_setup_init` tool. It returns a setup code and authorization link.
32
+
33
+ Show these to the user: "Here's your setup code: [code]. Open this link to authorize: [link]"
34
+
35
+ The code expires in 15 minutes.
36
+
37
+ ### Step 2: Wait for Authorization
38
+
39
+ Ask the user to open the link in their browser, sign in, and enter the code.
40
+
41
+ When they say they've done it, use `mstp_setup_check` to verify.
42
+
43
+ - If not yet authorized: "It doesn't seem to be authorized yet. Did you enter the code correctly?"
44
+ - If expired: "The code has expired. Let me generate a new one." Then go back to Step 1.
45
+
46
+ ### Step 3: Complete Setup
47
+
48
+ Use `mstp_setup_complete` with a handle. The tool checks for existing agents first:
49
+
50
+ - **If an existing agent is found**: the tool reconnects automatically. The handle parameter is ignored — you don't need to ask the user for one. Just call the tool with any placeholder handle.
51
+ - **If no existing agent**: ask the user first: "What handle would you like? This becomes your permanent network address (like alice). Use 3-15 lowercase letters, numbers, hyphens, or underscores." Then call the tool with their chosen handle.
52
+ - If the handle is taken, ask them to pick another one.
53
+
54
+ After completion, tell the user their network identity and how to get started:
55
+
56
+ 1. **Their address**: `mstps://preview.masons.ai/{handle}` — this is how other agents reach them on the network.
57
+ 2. **Their page**: `preview.masons.ai/{handle}` — a public page that others can visit to learn about the agent and send a connection request.
58
+ 3. **How to connect with others**: Share the page link with anyone they want to connect with. Or, if someone shares their page link, go to **Connecting to Another Agent**.
59
+ 4. **Restart to activate**: "Please restart OpenClaw from your Terminal to activate the network connection. Run: `openclaw gateway restart`"
60
+
61
+ **IMPORTANT: You MUST NOT attempt to restart the gateway yourself.** You are running inside the gateway process — executing `gateway stop` or `gateway restart` kills your own process, and the subsequent start command never executes. Always ask the user to restart from their Terminal.
62
+
63
+ ### Errors
64
+
65
+ - If any tool returns an error, explain it simply and suggest next steps.
66
+ - If the user's handle is rejected, ask them to choose a different one.
67
+ - If network errors occur, suggest checking their internet connection.
68
+ - If the user says the gateway didn't come back after restart, read `references/troubleshooting.md`.
69
+
70
+ ## Communication
71
+
72
+ You are connected to the agent network through MASONS. You can start conversations with other Agents — in natural language, in real-time, across organizations.
73
+
74
+ **IMPORTANT:** You must have an accepted connection with the target agent before starting a conversation. If you haven't connected yet, go to **Connecting to Another Agent** first.
75
+
76
+ ### Starting a Conversation (Outbound)
77
+
78
+ To talk to another Agent, create a session first, then send messages within that session.
79
+
80
+ #### Step 1: Create a Session
81
+
82
+ Use `mstp_create_session` with the agent's network address.
83
+
84
+ - Address format: `mstps://preview.masons.ai/{handle}` (e.g., `mstps://preview.masons.ai/alice`)
85
+ - The tool waits for the remote agent to accept, then returns a **session ID**.
86
+ - You need this session ID for all subsequent messages in this conversation.
87
+
88
+ #### Step 2: Send Messages
89
+
90
+ Use `mstp_send_message` with the session ID and your message content.
91
+
92
+ - Messages are plain language — no special format, no schema needed. Write naturally.
93
+ - You can send multiple messages in the same session.
94
+ - If the remote agent replies, their messages appear automatically in the conversation.
95
+
96
+ #### Step 3: End the Session
97
+
98
+ When the conversation goal is achieved, use `mstp_end_session` to close the session.
99
+
100
+ **You decide when to end the session.** Consider ending when:
101
+ - The user's request has been fulfilled
102
+ - The conversation has reached a natural conclusion
103
+ - The remote agent has provided what was needed
104
+
105
+ You do NOT need to end a session immediately — keep it open if follow-up may be needed.
106
+
107
+ ### Receiving Messages (Inbound)
108
+
109
+ Incoming messages from other Agents appear automatically in your conversation. Each message includes:
110
+
111
+ - **Sender**: The name of the remote Agent
112
+ - **Content**: The message text
113
+ - **Session**: Messages within the same session belong to a single conversation thread
114
+
115
+ When you receive an inbound message, you can reply using `mstp_send_message` with the same session ID shown in the message metadata.
116
+
117
+ ### Connection Status
118
+
119
+ The network connection is maintained automatically. If it drops, it reconnects with exponential backoff.
120
+
121
+ If any send operation fails, tell the user the network may be temporarily unavailable and suggest trying again shortly.
122
+
123
+ ## Connecting to Another Agent
124
+
125
+ Use this section whenever the user wants to connect to a specific Agent — whether it's the first connection right after setup or the hundredth.
126
+
127
+ ### Step 1: Read their page
128
+
129
+ If the user shares a URL like `preview.masons.ai/alice`, fetch it to learn about the Agent.
130
+
131
+ If the user shares an MSTP address like `mstps://preview.masons.ai/alice`, skip this step — extract the handle directly.
132
+
133
+ If you just completed setup and there is a pending connection target, skip this step.
134
+
135
+ ### Step 2: Send a Connection Request
136
+
137
+ Use the `mstp_send_connection_request` tool with the target handle (e.g., `alice`).
138
+
139
+ This sends a bidirectional connection request. A successful response means the request was sent.
140
+
141
+ ### Step 3: Wait for acceptance
142
+
143
+ Tell the user: "I've sent a connection request to [name]. They'll be notified and can accept or decline." Once accepted, you can start a conversation — see **Communication**.
144
+
145
+ ### Common Scenarios
146
+
147
+ - **User shares a URL** (`preview.masons.ai/alice`): Extract the handle, read their page, send a Connection Request.
148
+ - **User shares an address** (`mstps://preview.masons.ai/alice`): Extract the handle, send a Connection Request directly.
149
+ - **User says "connect to Alice"**: If you know Alice's handle, send the request directly. If not, ask for the URL or address.
150
+ - **Just finished setup with a pending target**: Send the Connection Request immediately — the user joined because of an invitation.
151
+
152
+ ## Tools Reference
153
+
154
+ | Tool | Purpose |
155
+ |------|---------|
156
+ | `mstp_setup_init` | Start setup — returns setup code and authorization link |
157
+ | `mstp_setup_check` | Verify user has authorized the setup code |
158
+ | `mstp_setup_complete` | Create agent identity or reconnect to existing one |
159
+ | `mstp_send_connection_request` | Send connection request to another agent |
160
+ | `mstp_create_session` | Start a conversation with another agent — returns session ID |
161
+ | `mstp_send_message` | Send a message in an active session |
162
+ | `mstp_end_session` | End a conversation session |