@masons/agent-network 0.3.6 → 0.3.8

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/channel.d.ts CHANGED
@@ -32,22 +32,9 @@ interface ChannelGatewayContext<T = unknown> {
32
32
  cfg: Record<string, unknown>;
33
33
  accountId: string;
34
34
  account: T;
35
- runtime: GatewayRuntime;
35
+ runtime: unknown;
36
36
  abortSignal: AbortSignal;
37
37
  }
38
- interface GatewayRuntime {
39
- routeMessage(envelope: MessageEnvelope): void;
40
- }
41
- interface MessageEnvelope {
42
- channelId: string;
43
- messageId: string;
44
- senderId: string;
45
- senderName: string;
46
- text: string;
47
- timestamp: number;
48
- isGroupMessage: boolean;
49
- metadata?: Record<string, unknown>;
50
- }
51
38
  interface ChannelGatewayAdapter<T = unknown> {
52
39
  startAccount?(ctx: ChannelGatewayContext<T>): Promise<unknown>;
53
40
  stopAccount?(ctx: ChannelGatewayContext<T>): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAa9D,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,UAAU,oBAAoB,CAAC,CAAC;IAC9B,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAAC;IACvD,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC;IACpE,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;IACjE,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;CAC/D;AAED,UAAU,sBAAsB;IAC9B,EAAE,EAAE,OAAO,CAAC;CACb;AAED,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,sBAAsB;IAC9B,YAAY,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC9C,QAAQ,CAAC,CAAC,GAAG,EAAE,sBAAsB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACzE;AAED,UAAU,qBAAqB,CAAC,CAAC,GAAG,OAAO;IACzC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,cAAc,CAAC;IACxB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,UAAU,cAAc;IACtB,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;CAC/C;AAED,UAAU,eAAe;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,UAAU,qBAAqB,CAAC,CAAC,GAAG,OAAO;IACzC,YAAY,CAAC,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/D,WAAW,CAAC,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED,UAAU,yBAAyB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,YAAY,EAAE,mBAAmB,CAAC;IAClC,MAAM,EAAE,oBAAoB,CAAC,mBAAmB,CAAC,CAAC;IAClD,QAAQ,EAAE,sBAAsB,CAAC;IACjC,OAAO,EAAE,qBAAqB,CAAC,mBAAmB,CAAC,CAAC;CACrD;AAwDD,eAAO,MAAM,mBAAmB,EAAE,yBA4NjC,CAAC"}
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAa9D,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,UAAU,oBAAoB,CAAC,CAAC;IAC9B,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAAC;IACvD,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC;IACpE,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;IACjE,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;CAC/D;AAED,UAAU,sBAAsB;IAC9B,EAAE,EAAE,OAAO,CAAC;CACb;AAED,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,sBAAsB;IAC9B,YAAY,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC9C,QAAQ,CAAC,CAAC,GAAG,EAAE,sBAAsB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACzE;AAED,UAAU,qBAAqB,CAAC,CAAC,GAAG,OAAO;IACzC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAsDD,UAAU,qBAAqB,CAAC,CAAC,GAAG,OAAO;IACzC,YAAY,CAAC,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/D,WAAW,CAAC,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED,UAAU,yBAAyB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,YAAY,EAAE,mBAAmB,CAAC;IAClC,MAAM,EAAE,oBAAoB,CAAC,mBAAmB,CAAC,CAAC;IAClD,QAAQ,EAAE,sBAAsB,CAAC;IACjC,OAAO,EAAE,qBAAqB,CAAC,mBAAmB,CAAC,CAAC;CACrD;AAiED,eAAO,MAAM,mBAAmB,EAAE,yBAqWjC,CAAC"}
package/dist/channel.js CHANGED
@@ -1,24 +1,31 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import createDebug from "debug";
3
- import { clearConnectorClient, extractNetworkConfig, initConnectorClient, initToolConfig, } from "./config.js";
3
+ import { clearConnectorClient, extractNetworkConfig, initConnectorClient, initToolConfig, requirePluginRuntime, } from "./config.js";
4
4
  import { ConnectorClient } from "./connector-client.js";
5
5
  import { checkForUpdate } from "./update-check.js";
6
6
  const dbg = createDebug("agent-network:channel");
7
7
  // --- Sender identity ---
8
8
  /**
9
- * Use sessionId as the sender identifier for OpenClaw routing.
9
+ * Derive a persistent sender identifier from the MSTP address.
10
10
  *
11
- * OpenClaw Gateway sets `ctx.recipient = senderId` when the LLM replies.
12
- * Our `sendText()` uses `ctx.recipient` as a sessionId to look up the
13
- * MSTP session. So senderId MUST equal sessionId for reply routing to work.
11
+ * Uses `metadata.from` (the remote agent's MSTP address, e.g.
12
+ * `mstps://masons.ai/alice`) as the conversation key. This ensures
13
+ * all messages from the same agent land in the same OpenClaw conversation,
14
+ * regardless of how many MSTP sessions are created.
14
15
  *
15
- * Each MSTP session maps to a separate OpenClaw conversation correct
16
- * semantics since sessions are independent, ephemeral exchanges.
16
+ * Falls back to `sessionId` for direct MSTP clients that connect without
17
+ * a Connector (no `metadata.from`). These get ephemeral conversations.
17
18
  *
18
- * The remote Agent's MSTP address (from `metadata.from`) is preserved in
19
- * `senderName` and the envelope's `metadata` field identity is not lost.
19
+ * The `activeSession` reverse index in AccountState maps the MSTP address
20
+ * back to the current sessionId for outbound routing (sendText, deliver).
21
+ *
22
+ * See: docs/openclaw/research/session-identity-mapping.md §5
20
23
  */
21
24
  function deriveSenderId(event) {
25
+ const from = event.metadata?.from;
26
+ if (typeof from === "string" && from.length > 0) {
27
+ return from;
28
+ }
22
29
  return event.sessionId;
23
30
  }
24
31
  /**
@@ -89,7 +96,7 @@ export const agentNetworkChannel = {
89
96
  isConfigured(account) {
90
97
  return account.connectorUrl !== "" && account.token !== "";
91
98
  },
92
- isEnabled(account, cfg) {
99
+ isEnabled(_account, cfg) {
93
100
  const networkCfg = extractNetworkConfig(cfg);
94
101
  return networkCfg?.enabled !== false;
95
102
  },
@@ -97,68 +104,176 @@ export const agentNetworkChannel = {
97
104
  outbound: {
98
105
  deliveryMode: "direct",
99
106
  async sendText(ctx) {
100
- // ctx.recipient = sessionId Gateway echoes back the senderId from routeMessage
107
+ // ctx.recipient is now an MSTP address (senderId = address from deriveSenderId).
108
+ // Resolve to the active sessionId via the reverse index.
101
109
  const state = accounts.get(ctx.accountId);
102
- if (!state?.sessions.has(ctx.recipient)) {
110
+ if (!state)
103
111
  return { ok: false };
112
+ let sessionId = state.activeSession.get(ctx.recipient);
113
+ // Fallback: ctx.recipient might be a raw sessionId (direct MSTP client
114
+ // without metadata.from — deriveSenderId fell back to sessionId).
115
+ if (!sessionId && state.sessions.has(ctx.recipient)) {
116
+ sessionId = ctx.recipient;
104
117
  }
105
- const sent = state.client.sendMessage(ctx.recipient, ctx.text, {
118
+ if (!sessionId || !state.sessions.has(sessionId)) {
119
+ return { ok: false };
120
+ }
121
+ const sent = state.client.sendMessage(sessionId, ctx.text, {
106
122
  contentType: "text",
107
123
  });
108
- dbg("sendText sessionId=%s contentLength=%d sent=%s", ctx.recipient, ctx.text.length, sent);
124
+ dbg("sendText recipient=%s sessionId=%s contentLength=%d sent=%s", ctx.recipient, sessionId, ctx.text.length, sent);
109
125
  return { ok: sent };
110
126
  },
111
127
  },
112
128
  gateway: {
113
129
  async startAccount(ctx) {
114
130
  dbg("starting account %s", ctx.accountId);
115
- // Capability check: routeMessage is required for inbound message delivery.
116
- // Older OpenClaw versions may not provide it on the gateway runtime.
117
- if (typeof ctx.runtime.routeMessage !== "function") {
118
- console.error("[agent-network] OpenClaw version too old — ctx.runtime.routeMessage is not available. " +
119
- "Inbound messages will not be delivered. Please upgrade OpenClaw.");
120
- }
121
131
  const { connectorUrl, token } = ctx.account;
122
132
  const client = new ConnectorClient(connectorUrl, token);
123
133
  const state = {
124
134
  client,
125
135
  sessions: new Map(),
136
+ activeSession: new Map(),
126
137
  };
127
138
  accounts.set(ctx.accountId, state);
128
- // --- Inbound message routing ---
129
- client.on("message_received", (event) => {
139
+ // --- Inbound message routing (4-step channelRuntime dispatch) ---
140
+ // Replaces the non-existent routeMessage() API. Uses PluginRuntime
141
+ // captured in register() via config.ts module-level singleton.
142
+ // Reference: DingTalk plugin (github.com/soimy/openclaw-channel-dingtalk).
143
+ //
144
+ // Concurrency model: this handler is async but EventEmitter fires it
145
+ // synchronously per event. Multiple MESSAGE_RECEIVED on the same session
146
+ // produce concurrent dispatches — replies may arrive out of order.
147
+ // Acceptable for unstructured text conversation (Phase 1). If MSTP adds
148
+ // structured multi-step interactions, add a per-session dispatch queue.
149
+ client.on("message_received", async (event) => {
130
150
  dbg("routing inbound message sessionId=%s contentLength=%d", event.sessionId, event.content.length);
131
- const senderId = deriveSenderId(event);
132
- const envelope = {
133
- channelId: "agent-network",
134
- messageId: `mstp:${event.sessionId}:${randomUUID()}`,
135
- senderId,
136
- senderName: deriveSenderName(event),
137
- text: event.content,
138
- timestamp: Date.now(),
139
- isGroupMessage: false,
140
- metadata: {
141
- ...event.metadata,
142
- contentType: event.contentType,
143
- },
144
- };
145
- if (typeof ctx.runtime.routeMessage !== "function") {
146
- dbg("routeMessage not available, dropping inbound message sessionId=%s", event.sessionId);
151
+ // --- Runtime guard ---
152
+ let runtime;
153
+ try {
154
+ runtime = requirePluginRuntime();
155
+ }
156
+ catch {
157
+ dbg("plugin runtime not available, dropping message sessionId=%s", event.sessionId);
147
158
  return;
148
159
  }
149
- ctx.runtime.routeMessage(envelope);
160
+ // channel is required in our PluginRuntime type but may be absent
161
+ // at runtime on older OpenClaw versions (pre-2026.2.19).
162
+ if (!runtime.channel) {
163
+ dbg("channelRuntime not available, dropping message sessionId=%s", event.sessionId);
164
+ return;
165
+ }
166
+ // Resolve sender identity from session state, NOT from per-message metadata.
167
+ // metadata.from is reliably present on SESSION_CREATED (Connector sets it
168
+ // server-side) but NOT on MESSAGE_RECEIVED (per-message metadata is
169
+ // pass-through from the remote plugin, which doesn't include `from`).
170
+ // The session_created handler already stored the remoteAddress.
171
+ const sessionMeta = state.sessions.get(event.sessionId);
172
+ const senderId = sessionMeta?.remoteAddress ?? event.sessionId;
173
+ // Derive sender name from the resolved address, not per-message metadata.
174
+ // Build a synthetic event with the session's address for deriveSenderName.
175
+ const senderName = sessionMeta?.remoteAddress
176
+ ? deriveSenderName({
177
+ ...event,
178
+ metadata: { ...event.metadata, from: sessionMeta.remoteAddress },
179
+ })
180
+ : deriveSenderName(event);
181
+ try {
182
+ // Step 1 — Resolve agent route (await defensively — may be sync or async)
183
+ const route = await runtime.channel.routing.resolveAgentRoute({
184
+ cfg: ctx.cfg,
185
+ channel: "agent-network",
186
+ accountId: ctx.accountId,
187
+ peer: { kind: "direct", id: senderId },
188
+ });
189
+ // Step 2 — Finalize inbound context (builds MsgContext for LLM pipeline)
190
+ const msgCtx = runtime.channel.reply.finalizeInboundContext({
191
+ Body: event.content,
192
+ RawBody: event.content,
193
+ CommandBody: event.content,
194
+ From: senderId,
195
+ To: senderId,
196
+ SessionKey: route.sessionKey,
197
+ AccountId: ctx.accountId,
198
+ ChatType: "direct",
199
+ SenderName: senderName,
200
+ SenderId: senderId,
201
+ Timestamp: Date.now(),
202
+ Provider: "agent-network",
203
+ Surface: "agent-network",
204
+ MessageSid: `mstp:${event.sessionId}:${randomUUID()}`,
205
+ CommandAuthorized: true,
206
+ OriginatingChannel: "agent-network",
207
+ OriginatingTo: senderId,
208
+ });
209
+ // Step 3 — Record inbound session
210
+ const storePath = runtime.channel.session.resolveStorePath(undefined, { agentId: route.agentId });
211
+ const sessionKey = typeof msgCtx.SessionKey === "string"
212
+ ? msgCtx.SessionKey
213
+ : route.sessionKey;
214
+ await runtime.channel.session.recordInboundSession({
215
+ storePath,
216
+ sessionKey,
217
+ ctx: msgCtx,
218
+ });
219
+ // Step 4 — Dispatch to LLM pipeline + deliver reply via callback
220
+ await runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
221
+ ctx: msgCtx,
222
+ cfg: ctx.cfg,
223
+ dispatcherOptions: {
224
+ deliver: async (payload) => {
225
+ const text = payload.text;
226
+ if (!text)
227
+ return;
228
+ // senderId is now an MSTP address. Resolve to sessionId.
229
+ let deliverSessionId = state.activeSession.get(senderId);
230
+ // Fallback: senderId might be a raw sessionId (direct client).
231
+ if (!deliverSessionId && state.sessions.has(senderId)) {
232
+ deliverSessionId = senderId;
233
+ }
234
+ // Session may have ended while LLM was processing.
235
+ if (!deliverSessionId || !state.sessions.has(deliverSessionId))
236
+ return;
237
+ const sent = state.client.sendMessage(deliverSessionId, text, {
238
+ contentType: "text",
239
+ });
240
+ dbg("deliver reply address=%s sessionId=%s contentLength=%d sent=%s", senderId, deliverSessionId, text.length, sent);
241
+ },
242
+ onError: (err) => {
243
+ console.error(`[agent-network:${ctx.accountId}] dispatch error:`, err);
244
+ },
245
+ },
246
+ });
247
+ }
248
+ catch (err) {
249
+ console.error(`[agent-network:${ctx.accountId}] inbound dispatch failed sessionId=${event.sessionId}:`, err);
250
+ }
150
251
  });
151
252
  // --- Session lifecycle ---
152
253
  client.on("session_created", (event) => {
153
254
  dbg("session created sessionId=%s direction=%s", event.sessionId, event.direction);
154
255
  const from = event.metadata?.from;
155
- state.sessions.set(event.sessionId, {
156
- remoteAddress: typeof from === "string" && from.length > 0 ? from : null,
157
- });
256
+ const remoteAddress = typeof from === "string" && from.length > 0 ? from : null;
257
+ state.sessions.set(event.sessionId, { remoteAddress });
258
+ // Update reverse index: this address is now reachable via this session.
259
+ // Last-write-wins: if a new session arrives from the same address,
260
+ // replies go to the newest session (same as XMPP resource binding).
261
+ if (remoteAddress) {
262
+ state.activeSession.set(remoteAddress, event.sessionId);
263
+ }
158
264
  });
159
265
  client.on("session_ended", (event) => {
160
266
  dbg("session ended sessionId=%s", event.sessionId);
267
+ const sessionMeta = state.sessions.get(event.sessionId);
161
268
  state.sessions.delete(event.sessionId);
269
+ // Only clear reverse index if this session was the active one.
270
+ // If a newer session replaced it, the index already points elsewhere.
271
+ if (sessionMeta?.remoteAddress) {
272
+ const currentActive = state.activeSession.get(sessionMeta.remoteAddress);
273
+ if (currentActive === event.sessionId) {
274
+ state.activeSession.delete(sessionMeta.remoteAddress);
275
+ }
276
+ }
162
277
  });
163
278
  // --- Error handling ---
164
279
  // Must register before connect() — unhandled "error" events crash the process.
@@ -169,6 +284,7 @@ export const agentNetworkChannel = {
169
284
  client.on("disconnected", () => {
170
285
  dbg("disconnected, cleared %d sessions", state.sessions.size);
171
286
  state.sessions.clear();
287
+ state.activeSession.clear();
172
288
  });
173
289
  // --- Abort signal ---
174
290
  ctx.abortSignal.addEventListener("abort", () => {
@@ -207,6 +323,7 @@ export const agentNetworkChannel = {
207
323
  state.client.removeAllListeners();
208
324
  state.client.disconnect();
209
325
  state.sessions.clear();
326
+ state.activeSession.clear();
210
327
  accounts.delete(ctx.accountId);
211
328
  }
212
329
  },
package/dist/config.d.ts CHANGED
@@ -49,6 +49,28 @@ export declare function initConnectorClient(client: ConnectorClient): void;
49
49
  * operating on a disconnected client.
50
50
  */
51
51
  export declare function clearConnectorClient(): void;
52
+ /**
53
+ * Store the OpenClaw PluginRuntime for inbound message dispatch.
54
+ *
55
+ * Called by `register(api)` in plugin.ts — captures `api.runtime` which
56
+ * provides `runtime.channel.*` (routing, reply, session) used by the
57
+ * 4-step inbound dispatch flow in channel.ts.
58
+ *
59
+ * Idempotent: overwrites on re-call. Gateway may hot-reload plugins,
60
+ * calling `register()` again without a teardown counterpart.
61
+ */
62
+ export declare function initPluginRuntime(runtime: unknown): void;
63
+ /**
64
+ * Clear the stored PluginRuntime reference.
65
+ */
66
+ export declare function clearPluginRuntime(): void;
67
+ /**
68
+ * Get the stored PluginRuntime. Throws if not initialized.
69
+ *
70
+ * Callers cast the result to `PluginRuntime` (defined in channel.ts)
71
+ * at the usage site. Typed as `unknown` here to avoid circular imports.
72
+ */
73
+ export declare function requirePluginRuntime(): unknown;
52
74
  /**
53
75
  * Get platform config. Always returns a valid config — defaults are set
54
76
  * at module load time, and overwritten by `initToolConfig()` when
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,sBAAsB,CAAC;AAuB9B;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAMhC;AAMD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAsBjE;AAMD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAOjE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAMD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,IAAI,oBAAoB,CAE5D;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAKtC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAEhD;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI,eAAe,CAOxD;AAMD,+EAA+E;AAC/E,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAgDD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,kBAAkB,EACzB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CA2Bf;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKrE;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAQvD;AAMD;;;;;GAKG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAKvD;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAKzD;AAMD,6EAA6E;AAC7E,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,cAAc,EAAE,OAAO,CAAC;IACxB,wDAAwD;IACxD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,4EAA4E;IAC5E,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,YAAY,CAAC,CA2BhE;AAMD,uDAAuD;AACvD,wBAAgB,gBAAgB,IAAI,IAAI,CAKvC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,sBAAsB,CAAC;AA2B9B;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAMhC;AAMD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAsBjE;AAMD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAOjE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAMD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAExD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAO9C;AAMD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,IAAI,oBAAoB,CAE5D;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAKtC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAEhD;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI,eAAe,CAOxD;AAMD,+EAA+E;AAC/E,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAgDD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,kBAAkB,EACzB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CA2Bf;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKrE;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAQvD;AAMD;;;;;GAKG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAKvD;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAKzD;AAMD,6EAA6E;AAC7E,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,cAAc,EAAE,OAAO,CAAC;IACxB,wDAAwD;IACxD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,4EAA4E;IAC5E,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,YAAY,CAAC,CA2BhE;AAMD,uDAAuD;AACvD,wBAAgB,gBAAgB,IAAI,IAAI,CAMvC"}
package/dist/config.js CHANGED
@@ -32,6 +32,9 @@ let storedApiKey = null;
32
32
  let storedPendingTarget = null;
33
33
  // Runtime state (live connection — set by startAccount, cleared by stopAccount)
34
34
  let storedConnectorClient = null;
35
+ // PluginRuntime state (set by register() in plugin.ts, used by channel.ts)
36
+ // Typed as unknown to avoid circular imports — channel.ts casts to PluginRuntime.
37
+ let storedPluginRuntime = null;
35
38
  // ---------------------------------------------------------------------------
36
39
  // Config navigation — shared between channel.ts and config.ts
37
40
  // ---------------------------------------------------------------------------
@@ -106,6 +109,40 @@ export function clearConnectorClient() {
106
109
  storedConnectorClient = null;
107
110
  }
108
111
  // ---------------------------------------------------------------------------
112
+ // PluginRuntime injection (called by register() in plugin.ts)
113
+ // ---------------------------------------------------------------------------
114
+ /**
115
+ * Store the OpenClaw PluginRuntime for inbound message dispatch.
116
+ *
117
+ * Called by `register(api)` in plugin.ts — captures `api.runtime` which
118
+ * provides `runtime.channel.*` (routing, reply, session) used by the
119
+ * 4-step inbound dispatch flow in channel.ts.
120
+ *
121
+ * Idempotent: overwrites on re-call. Gateway may hot-reload plugins,
122
+ * calling `register()` again without a teardown counterpart.
123
+ */
124
+ export function initPluginRuntime(runtime) {
125
+ storedPluginRuntime = runtime;
126
+ }
127
+ /**
128
+ * Clear the stored PluginRuntime reference.
129
+ */
130
+ export function clearPluginRuntime() {
131
+ storedPluginRuntime = null;
132
+ }
133
+ /**
134
+ * Get the stored PluginRuntime. Throws if not initialized.
135
+ *
136
+ * Callers cast the result to `PluginRuntime` (defined in channel.ts)
137
+ * at the usage site. Typed as `unknown` here to avoid circular imports.
138
+ */
139
+ export function requirePluginRuntime() {
140
+ if (storedPluginRuntime == null) {
141
+ throw new Error("Plugin runtime not initialized. register() must be called first.");
142
+ }
143
+ return storedPluginRuntime;
144
+ }
145
+ // ---------------------------------------------------------------------------
109
146
  // Guard functions (used by tools — fail-fast if not initialized)
110
147
  // ---------------------------------------------------------------------------
111
148
  /**
@@ -312,4 +349,5 @@ export function _resetForTesting() {
312
349
  storedApiKey = null;
313
350
  storedPendingTarget = null;
314
351
  storedConnectorClient = null;
352
+ storedPluginRuntime = null;
315
353
  }
@@ -120,6 +120,10 @@ export interface ListConnectionsResponse {
120
120
  address: string;
121
121
  }>;
122
122
  }
123
+ export interface ConnectionStatusResponse {
124
+ status: "connected" | "pending" | "not_connected";
125
+ canCreateSession: boolean;
126
+ }
123
127
  /**
124
128
  * `POST /setup/init` — Start Device Code Flow.
125
129
  *
@@ -207,4 +211,13 @@ export declare function updateProfile(cfg: PlatformClientConfig, apiKey: string,
207
211
  * Returns name + MSTP address for each connected agent.
208
212
  */
209
213
  export declare function listConnections(cfg: PlatformClientConfig, apiKey: string): Promise<ListConnectionsResponse>;
214
+ /**
215
+ * `GET /connections/status?target={handle}` — Check connection status.
216
+ *
217
+ * Requires API Key in Authorization header.
218
+ * Returns connection status and whether session creation is possible.
219
+ *
220
+ * Returns null if the request fails (advisory — caller should degrade gracefully).
221
+ */
222
+ export declare function getConnectionStatus(cfg: PlatformClientConfig, apiKey: string, targetHandle: string): Promise<ConnectionStatusResponse | null>;
210
223
  //# sourceMappingURL=platform-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"platform-client.d.ts","sourceRoot":"","sources":["../src/platform-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"platform-client.d.ts","sourceRoot":"","sources":["../src/platform-client.ts"],"names":[],"mappings":"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"}
@@ -247,3 +247,28 @@ export async function listConnections(cfg, apiKey) {
247
247
  return handleError(res);
248
248
  return (await res.json());
249
249
  }
250
+ /**
251
+ * `GET /connections/status?target={handle}` — Check connection status.
252
+ *
253
+ * Requires API Key in Authorization header.
254
+ * Returns connection status and whether session creation is possible.
255
+ *
256
+ * Returns null if the request fails (advisory — caller should degrade gracefully).
257
+ */
258
+ export async function getConnectionStatus(cfg, apiKey, targetHandle) {
259
+ try {
260
+ const controller = new AbortController();
261
+ const timeout = setTimeout(() => controller.abort(), 3000);
262
+ const res = await fetch(`${baseUrl(cfg)}/connections/status?target=${encodeURIComponent(targetHandle)}`, {
263
+ headers: { Authorization: `Bearer ${apiKey}` },
264
+ signal: controller.signal,
265
+ });
266
+ clearTimeout(timeout);
267
+ if (!res.ok)
268
+ return null; // Advisory — degrade gracefully
269
+ return (await res.json());
270
+ }
271
+ catch {
272
+ return null; // Network error or timeout — degrade gracefully
273
+ }
274
+ }
package/dist/plugin.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  interface OpenClawPluginApi {
2
+ runtime: unknown;
2
3
  registerChannel(opts: {
3
4
  plugin: unknown;
4
5
  }): void;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAWA,UAAU,iBAAiB;IACzB,eAAe,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC;CACnE;AAED,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;kBA4BI,iBAAiB;CAuGhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAWA,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC;CACnE;AAED,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;kBA4BI,iBAAiB;CA4GhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/plugin.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // NOT imported by index.ts to avoid pulling ws/typebox into Next.js app bundles.
4
4
  import { agentNetworkChannel } from "./channel.js";
5
5
  import { configureInteractive } from "./cli-setup.js";
6
- import { detectPendingState } from "./config.js";
6
+ import { detectPendingState, initPluginRuntime } from "./config.js";
7
7
  import { registerTools } from "./tools.js";
8
8
  import { PLUGIN_VERSION } from "./version.js";
9
9
  const plugin = {
@@ -32,6 +32,10 @@ const plugin = {
32
32
  },
33
33
  },
34
34
  register(api) {
35
+ // Capture runtime for inbound message dispatch (Pathway 1: module-level singleton).
36
+ // api.runtime is the full PluginRuntime (includes .channel for routing/reply/session).
37
+ // Idempotent — Gateway may hot-reload, calling register() again.
38
+ initPluginRuntime(api.runtime);
35
39
  // Register tools FIRST — they must be available even when the channel
36
40
  // has no credentials yet (setup flow needs these tools to GET credentials).
37
41
  registerTools(api);
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAoCH,UAAU,WAAW;IACnB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,CACP,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,WAAW,CAAC,CAAC;CAC3B;AAED,UAAU,OAAO;IACf,YAAY,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACzE;AAmBD,uDAAuD;AACvD,wBAAgB,qBAAqB,IAAI,IAAI,CAI5C;AAED,6DAA6D;AAC7D,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE7D;AAuID;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CA6pBhD"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAqCH,UAAU,WAAW;IACnB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,CACP,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,WAAW,CAAC,CAAC;CAC3B;AAED,UAAU,OAAO;IACf,YAAY,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACzE;AAmBD,uDAAuD;AACvD,wBAAgB,qBAAqB,IAAI,IAAI,CAI5C;AAED,6DAA6D;AAC7D,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE7D;AAuJD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAurBhD"}
package/dist/tools.js CHANGED
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import { Type } from "@sinclair/typebox";
17
17
  import { clearTargetHandle, getPendingTarget, markProfileComplete, markProfileNeeded, requireApiKey, requireConnectorClient, requirePlatformConfig, writeCredentials, } from "./config.js";
18
- import { acceptRequest, declineRequest, initSetup, listConnections, listRequests, onboard, PlatformApiError, pollSetup, reconnect, requestConnection, SetupExpiredError, SetupPendingError, updateProfile, } from "./platform-client.js";
18
+ import { acceptRequest, declineRequest, getConnectionStatus, initSetup, listConnections, listRequests, onboard, PlatformApiError, pollSetup, reconnect, requestConnection, SetupExpiredError, SetupPendingError, updateProfile, } from "./platform-client.js";
19
19
  import { getUpdateInfo } from "./update-check.js";
20
20
  // ---------------------------------------------------------------------------
21
21
  // Constants
@@ -131,19 +131,33 @@ function formatSessionCreated(sessionId, target) {
131
131
  function waitForSessionCreated(client, requestId) {
132
132
  return new Promise((resolve) => {
133
133
  const timer = setTimeout(() => {
134
- client.off("session_created", onSessionCreated);
134
+ cleanup();
135
135
  resolve({
136
136
  error: "Session creation timed out. The remote agent may be unavailable.",
137
137
  });
138
138
  }, sessionTimeoutMs);
139
+ function cleanup() {
140
+ clearTimeout(timer);
141
+ client.off("session_created", onSessionCreated);
142
+ client.off("error", onError);
143
+ }
139
144
  function onSessionCreated(event) {
140
145
  if (event.requestId === requestId && event.direction === "outbound") {
141
- clearTimeout(timer);
142
- client.off("session_created", onSessionCreated);
146
+ cleanup();
143
147
  resolve({ sessionId: event.sessionId });
144
148
  }
145
149
  }
150
+ // Listen for ERROR events from the Connector (e.g., access control rejection).
151
+ // The Connector sends { event: "ERROR", requestId, message } as a global error
152
+ // when CREATE_SESSION is rejected.
153
+ function onError(err) {
154
+ // ConnectorClient emits Error objects with the server message as err.message.
155
+ // Match by checking if the error message is actionable (not a generic WS error).
156
+ cleanup();
157
+ resolve({ error: err.message });
158
+ }
146
159
  client.on("session_created", onSessionCreated);
160
+ client.on("error", onError);
147
161
  });
148
162
  }
149
163
  // ---------------------------------------------------------------------------
@@ -603,6 +617,28 @@ export function registerTools(api) {
603
617
  execute: withUpdateNotice(async (_id, params) => {
604
618
  const client = requireConnectorClient();
605
619
  const target = params.target;
620
+ // --- Pre-flight: check connection status (advisory, not gate) ---
621
+ // If the check fails (network error, API down), proceed anyway —
622
+ // the Connector gate (Layer 2) is the authoritative enforcement.
623
+ try {
624
+ const handle = target
625
+ .replace(/^mstps?:\/\/[^/]+\//, "")
626
+ .replace(/\/+$/, "");
627
+ if (handle) {
628
+ const cfg = requirePlatformConfig();
629
+ const apiKey = requireApiKey();
630
+ const status = await getConnectionStatus(cfg, apiKey, handle);
631
+ if (status && !status.canCreateSession) {
632
+ if (status.status === "pending") {
633
+ return textResult(`Connection request to @${handle} is pending. Wait for the other agent to accept your request.`);
634
+ }
635
+ return textResult(`You are not connected to @${handle}. Send a connection request first using masons_send_connection_request.`);
636
+ }
637
+ }
638
+ }
639
+ catch {
640
+ // Advisory check failed — proceed to Connector gate
641
+ }
606
642
  const { requestId, sent } = client.createSession(target);
607
643
  if (!sent) {
608
644
  return textResult("Failed to create session. The network connection may be temporarily unavailable. Try again in a moment.");
package/dist/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  /** Plugin version — must match package.json. Validated by prepublishOnly. */
2
- export declare const PLUGIN_VERSION = "0.3.6";
2
+ export declare const PLUGIN_VERSION = "0.3.8";
3
3
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Plugin version — must match package.json. Validated by prepublishOnly. */
2
- export const PLUGIN_VERSION = "0.3.6";
2
+ export const PLUGIN_VERSION = "0.3.8";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@masons/agent-network",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "MASONS plugin for OpenClaw — connect your agent to the agent network",
5
5
  "license": "MIT",
6
6
  "author": "MASONS.ai <hello@masons.ai> (https://masons.ai)",