@masons/agent-network 0.5.14 → 0.5.16
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 +0 -7
- package/dist/channel.d.ts.map +1 -1
- package/dist/channel.js +3 -174
- package/dist/cli-setup.d.ts +0 -109
- package/dist/cli-setup.d.ts.map +1 -1
- package/dist/cli-setup.js +16 -570
- package/dist/config-fs.d.ts +4 -0
- package/dist/config-fs.d.ts.map +1 -0
- package/dist/config-fs.js +23 -0
- package/dist/config-schema.js +2 -2
- package/dist/config.d.ts +2 -210
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +14 -334
- package/dist/connector-client.d.ts +0 -32
- package/dist/connector-client.d.ts.map +1 -1
- package/dist/connector-client.js +1 -89
- package/dist/constants.d.ts +0 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -3
- package/dist/conversation-manager.d.ts +0 -106
- package/dist/conversation-manager.d.ts.map +1 -1
- package/dist/conversation-manager.js +2 -131
- package/dist/environment-context.d.ts +0 -24
- package/dist/environment-context.d.ts.map +1 -1
- package/dist/environment-context.js +0 -42
- package/dist/handle-utils.d.ts +0 -14
- package/dist/handle-utils.d.ts.map +1 -1
- package/dist/handle-utils.js +0 -14
- package/dist/index.js +0 -9
- package/dist/owner-notes.d.ts +0 -33
- package/dist/owner-notes.d.ts.map +1 -1
- package/dist/owner-notes.js +2 -41
- package/dist/owner-session-state.d.ts +0 -26
- package/dist/owner-session-state.d.ts.map +1 -1
- package/dist/owner-session-state.js +0 -37
- package/dist/platform-client.d.ts +13 -202
- package/dist/platform-client.d.ts.map +1 -1
- package/dist/platform-client.js +22 -171
- package/dist/plugin.d.ts +5 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +3 -167
- package/dist/sent-message-buffer.d.ts +0 -36
- package/dist/sent-message-buffer.d.ts.map +1 -1
- package/dist/sent-message-buffer.js +1 -45
- package/dist/tools.d.ts +0 -28
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +36 -240
- package/dist/turn-context.d.ts +0 -45
- package/dist/turn-context.d.ts.map +1 -1
- package/dist/turn-context.js +0 -57
- package/dist/types.d.ts +0 -67
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -7
- package/dist/update-cache.d.ts +0 -17
- package/dist/update-cache.d.ts.map +1 -1
- package/dist/update-cache.js +1 -21
- package/dist/update-check.d.ts +1 -40
- package/dist/update-check.d.ts.map +1 -1
- package/dist/update-check.js +7 -66
- package/dist/version.d.ts +1 -2
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -2
- package/openclaw.plugin.json +94 -3
- package/package.json +11 -10
- package/skills/agent-network/SKILL.md +21 -47
- package/skills/agent-network/references/troubleshooting.md +5 -5
package/dist/tools.js
CHANGED
|
@@ -1,81 +1,28 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LLM tools — setup, connection, profile, and conversation tools.
|
|
3
|
-
*
|
|
4
|
-
* Registers tools with OpenClaw's Plugin API so the LLM can
|
|
5
|
-
* drive setup, profile completion, connection listing, connection
|
|
6
|
-
* requests, request management, and real-time conversations,
|
|
7
|
-
* guided by SKILL.md.
|
|
8
|
-
*
|
|
9
|
-
* Two access patterns:
|
|
10
|
-
* - **HTTP tools** (setup, connection): read config via `requirePlatformConfig()`,
|
|
11
|
-
* call Platform API via `platform-client.ts`.
|
|
12
|
-
* - **WebSocket tools** (conversation): use `requireConversationManager()` for
|
|
13
|
-
* identity-based messaging. Session management is fully transparent.
|
|
14
|
-
*
|
|
15
|
-
* Session Abstraction (#741):
|
|
16
|
-
* - `masons_send_message(to, content)` — sends via ConversationManager
|
|
17
|
-
* - `masons_end_conversation(contact)` — ends via ConversationManager
|
|
18
|
-
* (Deprecated shims masons_create_session and masons_end_session have been removed.)
|
|
19
|
-
*/
|
|
20
|
-
import { tmpdir } from "node:os";
|
|
21
1
|
import { Type } from "@sinclair/typebox";
|
|
22
2
|
import { getOwnerPassportAddress } from "./channel.js";
|
|
23
|
-
import { clearTargetHandle, getDmScope,
|
|
3
|
+
import { clearTargetHandle, getDmScope, getPendingTarget, isProfileNeeded, markProfileComplete, removeIdentityLinks, requireApiKey, requireConversationManager, requirePlatformConfig, writeIdentityLinks, } from "./config.js";
|
|
24
4
|
import { getOwnerHandle } from "./environment-context.js";
|
|
25
5
|
import { ownerNotesQueue } from "./owner-notes.js";
|
|
26
|
-
import { acceptRequest, declineRequest,
|
|
6
|
+
import { acceptRequest, declineRequest, listConnections, listRequests, PlatformApiError, requestConnection, updateProfile, } from "./platform-client.js";
|
|
27
7
|
import { sentMessageBuffer } from "./sent-message-buffer.js";
|
|
28
8
|
import { getCurrentTurnChannelId, getCurrentTurnIsOwnerNonConsuming, } from "./turn-context.js";
|
|
29
|
-
import {
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
|
-
// Constants
|
|
32
|
-
// ---------------------------------------------------------------------------
|
|
33
|
-
/** Accepted field names for masons_update_profile — reject anything else. */
|
|
9
|
+
import { getLatestPublishedVersion, getPluginVersion, getUpdateInfo, } from "./update-check.js";
|
|
34
10
|
const PROFILE_FIELDS = new Set(["name", "scope", "about", "audience"]);
|
|
35
|
-
/** CLI command to update the plugin via OpenClaw. */
|
|
36
11
|
const UPGRADE_CMD = "openclaw plugins update agent-network";
|
|
37
|
-
/** Fallback: manual update via npm pack + in-place extract. Used when
|
|
38
|
-
* `openclaw plugins update` fails (integrity conflict, npm cache issue, etc.)
|
|
39
|
-
* while preserving credentials and node_modules.
|
|
40
|
-
* Placeholders `{version}`, `{home}`, and `{tmpdir}` are replaced at runtime. */
|
|
41
|
-
const FALLBACK_UPGRADE_CMD = "npm pack @masons/agent-network@{version} --pack-destination {tmpdir} && " +
|
|
42
|
-
"tar xzf {tmpdir}/masons-agent-network-{version}.tgz --strip-components=1 -C {home}/extensions/agent-network/ && " +
|
|
43
|
-
"cd {home}/extensions/agent-network && npm install --omit=dev && " +
|
|
44
|
-
"rm -f {tmpdir}/masons-agent-network-{version}.tgz";
|
|
45
|
-
/** CLI command to restart the gateway. */
|
|
46
12
|
const GATEWAY_RESTART_CMD = "openclaw gateway restart";
|
|
47
|
-
|
|
13
|
+
function reinstallCmd(version) {
|
|
14
|
+
return `openclaw plugins install @masons/agent-network@${version} --force`;
|
|
15
|
+
}
|
|
48
16
|
const SEMVER_RE = /^\d+\.\d+\.\d+$/;
|
|
49
|
-
// ---------------------------------------------------------------------------
|
|
50
|
-
// Module-level state (not persisted across process restarts)
|
|
51
|
-
// ---------------------------------------------------------------------------
|
|
52
|
-
/**
|
|
53
|
-
* Tracks the version we already tried to upgrade to this session.
|
|
54
|
-
* null = no attempt yet. Set to the target version string on the first
|
|
55
|
-
* normal-path attempt. If the tool is called again while current version
|
|
56
|
-
* still differs, the fallback path is returned instead.
|
|
57
|
-
*/
|
|
58
17
|
let upgradeAttemptedVersion = null;
|
|
59
|
-
/** @internal Reset module state for test isolation. */
|
|
60
18
|
export function _resetToolsForTesting() {
|
|
61
19
|
updateNoticeShown = false;
|
|
62
20
|
upgradeAttemptedVersion = null;
|
|
63
21
|
}
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
65
|
-
// Format helpers
|
|
66
|
-
// ---------------------------------------------------------------------------
|
|
67
22
|
function textResult(text) {
|
|
68
23
|
return { content: [{ type: "text", text }] };
|
|
69
24
|
}
|
|
70
|
-
// Show update notice only once per gateway session to avoid being annoying.
|
|
71
25
|
let updateNoticeShown = false;
|
|
72
|
-
/**
|
|
73
|
-
* Append an update notice to tool output if a newer version is available.
|
|
74
|
-
*
|
|
75
|
-
* Called on every tool result. Shows the notice at most once per session —
|
|
76
|
-
* after the first tool call surfaces it, the Agent has the information
|
|
77
|
-
* and can guide the user through upgrading.
|
|
78
|
-
*/
|
|
79
26
|
function maybeAppendUpdateNotice(result) {
|
|
80
27
|
const info = getUpdateInfo();
|
|
81
28
|
if (!info?.updateAvailable || updateNoticeShown)
|
|
@@ -89,28 +36,9 @@ function maybeAppendUpdateNotice(result) {
|
|
|
89
36
|
}
|
|
90
37
|
return result;
|
|
91
38
|
}
|
|
92
|
-
/**
|
|
93
|
-
* Wrap a tool's execute function to append update notice to its output.
|
|
94
|
-
*
|
|
95
|
-
* Applied at registration time so every tool automatically surfaces
|
|
96
|
-
* update information without modifying individual return sites.
|
|
97
|
-
*/
|
|
98
39
|
function withUpdateNotice(fn) {
|
|
99
40
|
return async (id, params) => maybeAppendUpdateNotice(await fn(id, params));
|
|
100
41
|
}
|
|
101
|
-
/**
|
|
102
|
-
* Format a Node kind discriminator as a prefix label for list rendering.
|
|
103
|
-
*
|
|
104
|
-
* Returns:
|
|
105
|
-
* - "[Human Node] " when kind === "user"
|
|
106
|
-
* - "[Agent Node] " when kind === "agent"
|
|
107
|
-
* - "[Node:<kind>] " for future / unknown kinds (forward-compat per
|
|
108
|
-
* `docs/concepts/human-node-capability-obligations.md § 5` — never
|
|
109
|
-
* suppress, never crash)
|
|
110
|
-
* - "" (empty) when kind is absent — preserves backward-compatible
|
|
111
|
-
* rendering against Connector versions that have not adopted NodeRef
|
|
112
|
-
* projection.
|
|
113
|
-
*/
|
|
114
42
|
function formatKindLabel(kind) {
|
|
115
43
|
if (!kind)
|
|
116
44
|
return "";
|
|
@@ -126,42 +54,18 @@ function formatConnectionResult(requestIds, status) {
|
|
|
126
54
|
}
|
|
127
55
|
return `Connection request sent. Status: ${status}. The other agent's owner will be notified.`;
|
|
128
56
|
}
|
|
129
|
-
// ---------------------------------------------------------------------------
|
|
130
|
-
// Tool registration
|
|
131
|
-
// ---------------------------------------------------------------------------
|
|
132
|
-
/**
|
|
133
|
-
* Register agent network tools with the OpenClaw Plugin API.
|
|
134
|
-
*
|
|
135
|
-
* Called from `plugin.ts` during plugin registration. Tools become available
|
|
136
|
-
* to the LLM after the plugin loads. Tools that require config will fail-fast
|
|
137
|
-
* with a clear error if `initToolConfig()` hasn't been called yet (i.e.,
|
|
138
|
-
* `startAccount()` hasn't run).
|
|
139
|
-
*/
|
|
140
57
|
export function registerTools(api) {
|
|
141
|
-
// --- masons_setup ---------------------------------------------------------
|
|
142
|
-
// Lightweight nudge for the no-credentials state (#1264). The legacy
|
|
143
|
-
// `masons_setup_init / check / complete` trio (removed in 0.5.0) ran the
|
|
144
|
-
// device flow inside the LLM; that's not viable for an OAuth Device
|
|
145
|
-
// Authorization Grant flow which requires terminal I/O (the user enters a
|
|
146
|
-
// user_code in their browser, returns to the terminal to pick or create
|
|
147
|
-
// an agent). This tool's only job is to surface the CLI command and let
|
|
148
|
-
// the user run it.
|
|
149
|
-
//
|
|
150
|
-
// Layer B's `before_prompt_build` hook in plugin.ts already injects this
|
|
151
|
-
// guidance for the no-credentials state — this tool is here so the LLM
|
|
152
|
-
// can reach it explicitly when the user asks "how do I set this up?"
|
|
153
|
-
// (e.g., as a fallback after a failed tool call surfaces "no credentials").
|
|
154
58
|
api.registerTool({
|
|
155
59
|
name: "masons_setup",
|
|
156
60
|
description: [
|
|
157
61
|
"Returns instructions for connecting OpenClaw to the agent network.",
|
|
158
62
|
"Use when the user asks how to set up Agent Network, when other Agent",
|
|
159
|
-
"Network tools fail with a 'no credentials' / 'no
|
|
63
|
+
"Network tools fail with a 'no credentials' / 'no runtime key' error, or",
|
|
160
64
|
"when the user wants to switch which agent OpenClaw is driving.",
|
|
161
65
|
"",
|
|
162
66
|
"This tool does NOT perform setup itself — it surfaces the terminal",
|
|
163
|
-
"command. The user must run it from their shell because
|
|
164
|
-
"
|
|
67
|
+
"command. The user must run it from their shell because login requires",
|
|
68
|
+
"a browser handoff.",
|
|
165
69
|
].join("\n"),
|
|
166
70
|
parameters: Type.Object({}),
|
|
167
71
|
execute: async () => ({
|
|
@@ -174,21 +78,21 @@ export function registerTools(api) {
|
|
|
174
78
|
" openclaw channels login --channel agent-network",
|
|
175
79
|
"",
|
|
176
80
|
"It will:",
|
|
177
|
-
" 1. Display a
|
|
178
|
-
" 2. Open
|
|
179
|
-
" 3.
|
|
180
|
-
" 4.
|
|
81
|
+
" 1. Display a MASONS handoff URL.",
|
|
82
|
+
" 2. Open it in your browser and sign in.",
|
|
83
|
+
" 3. Pick an existing agent or create a new one.",
|
|
84
|
+
" 4. Return to the terminal after the encrypted handoff completes.",
|
|
85
|
+
" 5. Persist credentials to openclaw.json. OpenClaw reloads the channel.",
|
|
181
86
|
"",
|
|
182
|
-
"Note
|
|
183
|
-
"agent on
|
|
184
|
-
"
|
|
185
|
-
"
|
|
87
|
+
"Note: if you re-run this with another",
|
|
88
|
+
"agent on this machine, this OpenClaw install receives",
|
|
89
|
+
"its own runtime key. Re-running login here",
|
|
90
|
+
"updates this OpenClaw install's credentials.",
|
|
186
91
|
].join("\n"),
|
|
187
92
|
},
|
|
188
93
|
],
|
|
189
94
|
}),
|
|
190
95
|
});
|
|
191
|
-
// --- masons_update_profile ------------------------------------------------
|
|
192
96
|
api.registerTool({
|
|
193
97
|
name: "masons_update_profile",
|
|
194
98
|
description: [
|
|
@@ -217,8 +121,6 @@ export function registerTools(api) {
|
|
|
217
121
|
})),
|
|
218
122
|
}),
|
|
219
123
|
execute: withUpdateNotice(async (_id, params) => {
|
|
220
|
-
// Guard: reject unknown field names immediately so the LLM
|
|
221
|
-
// sees an actionable error and can self-correct.
|
|
222
124
|
const unknownFields = Object.keys(params).filter((k) => !PROFILE_FIELDS.has(k));
|
|
223
125
|
if (unknownFields.length > 0) {
|
|
224
126
|
return textResult(`Unknown field(s): ${unknownFields.join(", ")}. ` +
|
|
@@ -246,7 +148,7 @@ export function registerTools(api) {
|
|
|
246
148
|
catch (err) {
|
|
247
149
|
if (err instanceof PlatformApiError) {
|
|
248
150
|
if (err.status === 401) {
|
|
249
|
-
return textResult("Authentication failed. The
|
|
151
|
+
return textResult("Authentication failed. The runtime key may be invalid. Ask the user to run `openclaw channels login --channel agent-network`.");
|
|
250
152
|
}
|
|
251
153
|
if (err.status === 422) {
|
|
252
154
|
return textResult(`Validation error: ${err.message}`);
|
|
@@ -254,15 +156,12 @@ export function registerTools(api) {
|
|
|
254
156
|
}
|
|
255
157
|
throw err;
|
|
256
158
|
}
|
|
257
|
-
// Completion gate: scope + about + audience must ALL be non-empty.
|
|
258
|
-
// name is set during onboard — not a completion gate.
|
|
259
159
|
if (result.profile) {
|
|
260
160
|
const { scope, about, audience } = result.profile;
|
|
261
161
|
const profileComplete = scope.length > 0 && about.length > 0 && audience.length > 0;
|
|
262
162
|
if (profileComplete) {
|
|
263
163
|
await markProfileComplete();
|
|
264
164
|
}
|
|
265
|
-
// Echo saved values for LLM verification
|
|
266
165
|
const lines = [
|
|
267
166
|
"Profile updated successfully. Current profile:",
|
|
268
167
|
`• name: ${result.profile.name}`,
|
|
@@ -285,14 +184,12 @@ export function registerTools(api) {
|
|
|
285
184
|
}
|
|
286
185
|
return textResult(lines.join("\n"));
|
|
287
186
|
}
|
|
288
|
-
// Backward compat: old Platform without profile in response
|
|
289
187
|
if (profileFields.scope && profileFields.scope.length > 0) {
|
|
290
188
|
await markProfileComplete();
|
|
291
189
|
}
|
|
292
190
|
return textResult("Profile updated successfully. The agent's identity is now visible on the network.");
|
|
293
191
|
}),
|
|
294
192
|
});
|
|
295
|
-
// --- masons_send_connection_request --------------------------------------
|
|
296
193
|
api.registerTool({
|
|
297
194
|
name: "masons_send_connection_request",
|
|
298
195
|
description: "Send a connection request to another Agent on the agent network.",
|
|
@@ -304,9 +201,6 @@ export function registerTools(api) {
|
|
|
304
201
|
execute: withUpdateNotice(async (_id, params) => {
|
|
305
202
|
const cfg = requirePlatformConfig();
|
|
306
203
|
const apiKey = requireApiKey();
|
|
307
|
-
// Profile gate: block connection requests until profile is complete.
|
|
308
|
-
// Without a profile the other agent sees an anonymous stranger,
|
|
309
|
-
// making acceptance unlikely.
|
|
310
204
|
if (isProfileNeeded()) {
|
|
311
205
|
return textResult("Your agent's profile is not complete yet. Without a profile, " +
|
|
312
206
|
"the other agent can't see who you are — connection requests from " +
|
|
@@ -327,20 +221,18 @@ export function registerTools(api) {
|
|
|
327
221
|
return textResult(`Agent @${targetHandle} not found. Check the handle and try again.`);
|
|
328
222
|
}
|
|
329
223
|
if (err.status === 401) {
|
|
330
|
-
return textResult("Authentication failed. The
|
|
224
|
+
return textResult("Authentication failed. The runtime key may be invalid. Ask the user to run `openclaw channels login --channel agent-network`.");
|
|
331
225
|
}
|
|
332
226
|
return textResult(`Connection request failed: ${err.message}`);
|
|
333
227
|
}
|
|
334
228
|
throw err;
|
|
335
229
|
}
|
|
336
|
-
// Clear pending target if one was set
|
|
337
230
|
if (getPendingTarget()) {
|
|
338
231
|
await clearTargetHandle();
|
|
339
232
|
}
|
|
340
233
|
return textResult(formatConnectionResult(result.requestIds, result.status));
|
|
341
234
|
}),
|
|
342
235
|
});
|
|
343
|
-
// --- masons_list_requests --------------------------------------------------
|
|
344
236
|
api.registerTool({
|
|
345
237
|
name: "masons_list_requests",
|
|
346
238
|
description: "List connection requests — incoming from other agents, outgoing ones you sent, or all.",
|
|
@@ -368,7 +260,7 @@ export function registerTools(api) {
|
|
|
368
260
|
catch (err) {
|
|
369
261
|
if (err instanceof PlatformApiError) {
|
|
370
262
|
if (err.status === 401) {
|
|
371
|
-
return textResult("Authentication failed. The
|
|
263
|
+
return textResult("Authentication failed. The runtime key may be invalid. Ask the user to run `openclaw channels login --channel agent-network`.");
|
|
372
264
|
}
|
|
373
265
|
return textResult(`Failed to list requests: ${err.message}`);
|
|
374
266
|
}
|
|
@@ -384,24 +276,9 @@ export function registerTools(api) {
|
|
|
384
276
|
}
|
|
385
277
|
const lines = result.items.map((item) => {
|
|
386
278
|
const dir = item.direction ?? direction;
|
|
387
|
-
// For incoming: sender NodeRef is in `fromNode` (since #1450 Phase 1).
|
|
388
|
-
// For outgoing: target NodeRef is in `toNode`. Both may be null when
|
|
389
|
-
// the Connector ran NodeRef enrichment but the entity record was
|
|
390
|
-
// missing. Back-compat fallback: `fromAgent` / `toAgent` carry just
|
|
391
|
-
// `{ handle, name }` from older Connector versions or the back-compat
|
|
392
|
-
// shape #1450 still emits.
|
|
393
279
|
const node = dir === "outgoing" ? item.toNode : item.fromNode;
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
// NodeRef.displayName → NodeRef.handle → back-compat name → handle → "unknown".
|
|
397
|
-
const otherName = node?.displayName ||
|
|
398
|
-
node?.handle ||
|
|
399
|
-
compat?.name ||
|
|
400
|
-
compat?.handle ||
|
|
401
|
-
"unknown";
|
|
402
|
-
const otherHandle = node?.handle || compat?.handle || "unknown";
|
|
403
|
-
// Kind discriminator: surface "Human Node" / "Agent Node" / future kinds
|
|
404
|
-
// when the Connector projects `kind` per the same doc § 5 forward-compat.
|
|
280
|
+
const otherName = node?.displayName || node?.handle || "unknown";
|
|
281
|
+
const otherHandle = node?.handle || "unknown";
|
|
405
282
|
const kindLabel = formatKindLabel(node?.kind);
|
|
406
283
|
const dirArrow = dir === "outgoing" ? "→" : "←";
|
|
407
284
|
const parts = [
|
|
@@ -430,7 +307,6 @@ export function registerTools(api) {
|
|
|
430
307
|
return textResult(`${result.total} ${dirLabel}${status === "all" ? "" : `${status} `}request(s):\n\n${lines.join("\n\n")}${suffix}`);
|
|
431
308
|
}),
|
|
432
309
|
});
|
|
433
|
-
// --- masons_accept_request ------------------------------------------------
|
|
434
310
|
api.registerTool({
|
|
435
311
|
name: "masons_accept_request",
|
|
436
312
|
description: "Accept an incoming connection request from another agent.",
|
|
@@ -442,9 +318,6 @@ export function registerTools(api) {
|
|
|
442
318
|
execute: withUpdateNotice(async (_id, params) => {
|
|
443
319
|
const cfg = requirePlatformConfig();
|
|
444
320
|
const apiKey = requireApiKey();
|
|
445
|
-
// Profile gate: block accepting requests until profile is complete.
|
|
446
|
-
// When a connection is established, the other agent sees your profile —
|
|
447
|
-
// an empty profile means they won't know who you are.
|
|
448
321
|
if (isProfileNeeded()) {
|
|
449
322
|
return textResult("Your agent's profile is not complete yet. Without a profile, " +
|
|
450
323
|
"the other agent won't know who you are when the connection is established. " +
|
|
@@ -453,17 +326,11 @@ export function registerTools(api) {
|
|
|
453
326
|
const requestId = params.requestId;
|
|
454
327
|
try {
|
|
455
328
|
const result = await acceptRequest(cfg, apiKey, requestId);
|
|
456
|
-
// Prefer NodeRef on `counterparty` (since #1450 Phase 1); fall
|
|
457
|
-
// back to legacy `connection.{name, handle}` for older Connector
|
|
458
|
-
// versions or when enrichment returned null.
|
|
459
329
|
const node = result.counterparty;
|
|
460
|
-
const who = node?.displayName ||
|
|
461
|
-
|
|
462
|
-
result.connection.name ||
|
|
463
|
-
result.connection.handle;
|
|
464
|
-
const handle = node?.handle || result.connection.handle;
|
|
330
|
+
const who = node?.displayName || node?.handle || "that Node";
|
|
331
|
+
const handle = node?.handle ? ` (@${node.handle})` : "";
|
|
465
332
|
const kindLabel = formatKindLabel(node?.kind);
|
|
466
|
-
return textResult(`Connected with ${kindLabel}${who}
|
|
333
|
+
return textResult(`Connected with ${kindLabel}${who}${handle}! You can now send messages using masons_send_message.`);
|
|
467
334
|
}
|
|
468
335
|
catch (err) {
|
|
469
336
|
if (err instanceof PlatformApiError && err.status === 404) {
|
|
@@ -473,7 +340,6 @@ export function registerTools(api) {
|
|
|
473
340
|
}
|
|
474
341
|
}),
|
|
475
342
|
});
|
|
476
|
-
// --- masons_decline_request -----------------------------------------------
|
|
477
343
|
api.registerTool({
|
|
478
344
|
name: "masons_decline_request",
|
|
479
345
|
description: "Decline an incoming connection request from another agent.",
|
|
@@ -498,7 +364,6 @@ export function registerTools(api) {
|
|
|
498
364
|
}
|
|
499
365
|
}),
|
|
500
366
|
});
|
|
501
|
-
// --- masons_list_connections -----------------------------------------------
|
|
502
367
|
api.registerTool({
|
|
503
368
|
name: "masons_list_connections",
|
|
504
369
|
description: "List your established connections on the agent network. Returns the name and address of each connected agent.",
|
|
@@ -513,7 +378,7 @@ export function registerTools(api) {
|
|
|
513
378
|
catch (err) {
|
|
514
379
|
if (err instanceof PlatformApiError) {
|
|
515
380
|
if (err.status === 401) {
|
|
516
|
-
return textResult("Authentication failed. The
|
|
381
|
+
return textResult("Authentication failed. The runtime key may be invalid. Ask the user to run `openclaw channels login --channel agent-network`.");
|
|
517
382
|
}
|
|
518
383
|
return textResult(`Failed to list connections: ${err.message}`);
|
|
519
384
|
}
|
|
@@ -522,22 +387,16 @@ export function registerTools(api) {
|
|
|
522
387
|
if (result.total === 0) {
|
|
523
388
|
return textResult("No connections yet. Use masons_send_connection_request to connect with other agents.");
|
|
524
389
|
}
|
|
525
|
-
const lines = result.
|
|
526
|
-
// NodeRef on `counterparty` (since #1450 Phase 1); falls back to
|
|
527
|
-
// legacy `name + address` for older Connector versions.
|
|
390
|
+
const lines = result.connections.map((item, i) => {
|
|
528
391
|
const node = item.counterparty;
|
|
529
|
-
const displayName = node
|
|
530
|
-
const address = node
|
|
392
|
+
const displayName = node.displayName || node.handle || "(unnamed)";
|
|
393
|
+
const address = node.mstpAddress;
|
|
531
394
|
const kindLabel = formatKindLabel(node?.kind);
|
|
532
395
|
return `${i + 1}. ${kindLabel}${displayName} (${address})`;
|
|
533
396
|
});
|
|
534
397
|
return textResult(`${result.total} connection(s):\n\n${lines.join("\n")}\n\nUse masons_send_message with a handle or address to send a message.`);
|
|
535
398
|
}),
|
|
536
399
|
});
|
|
537
|
-
// =========================================================================
|
|
538
|
-
// Conversation tools — identity-based via ConversationManager
|
|
539
|
-
// =========================================================================
|
|
540
|
-
// --- masons_send_message --------------------------------------------------
|
|
541
400
|
api.registerTool({
|
|
542
401
|
name: "masons_send_message",
|
|
543
402
|
description: "Send a message to a remote agent on the agent network. This is agent-to-agent communication — separate from your owner's conversation. Sessions are managed automatically — just provide the contact handle or address.",
|
|
@@ -553,33 +412,11 @@ export function registerTools(api) {
|
|
|
553
412
|
const cm = requireConversationManager();
|
|
554
413
|
const to = params.to;
|
|
555
414
|
const content = params.content;
|
|
556
|
-
// Extract handle once — shared by pre-flight check and sent message buffer.
|
|
557
415
|
const handle = to.startsWith("mstps://") || to.startsWith("mstp://")
|
|
558
416
|
? to.replace(/^mstps?:\/\/[^/]+\//, "").replace(/\/+$/, "")
|
|
559
417
|
: to;
|
|
560
|
-
// --- Pre-flight: check connection status (advisory, not gate) ---
|
|
561
|
-
// If the check fails (network error, API down), proceed anyway —
|
|
562
|
-
// the Connector gate (Layer 2) is the authoritative enforcement.
|
|
563
|
-
try {
|
|
564
|
-
if (handle) {
|
|
565
|
-
const cfg = requirePlatformConfig();
|
|
566
|
-
const apiKey = requireApiKey();
|
|
567
|
-
const status = await getConnectionStatus(cfg, apiKey, handle);
|
|
568
|
-
if (status && !status.canCreateSession) {
|
|
569
|
-
if (status.status === "pending") {
|
|
570
|
-
return textResult(`Connection request to @${handle} is pending. Wait for the other agent to accept your request.`);
|
|
571
|
-
}
|
|
572
|
-
return textResult(`You are not connected to @${handle}. Send a connection request first using masons_send_connection_request.`);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
catch {
|
|
577
|
-
// Advisory check failed — proceed to Connector gate
|
|
578
|
-
}
|
|
579
418
|
const result = await cm.send(to, content);
|
|
580
419
|
if (result.status === "sent") {
|
|
581
|
-
// Record in sent message buffer so the agent-network session has
|
|
582
|
-
// context when the remote agent replies (cross-session bridge, #918).
|
|
583
420
|
sentMessageBuffer.record(handle, content);
|
|
584
421
|
return textResult("Message sent.");
|
|
585
422
|
}
|
|
@@ -587,7 +424,6 @@ export function registerTools(api) {
|
|
|
587
424
|
"Failed to send message. The network connection may be temporarily unavailable. Try again in a moment.");
|
|
588
425
|
}),
|
|
589
426
|
});
|
|
590
|
-
// --- masons_end_conversation -----------------------------------------------
|
|
591
427
|
api.registerTool({
|
|
592
428
|
name: "masons_end_conversation",
|
|
593
429
|
description: "End the conversation with a connected agent. This immediately closes the session — do not send any text reply after calling this tool.",
|
|
@@ -603,7 +439,6 @@ export function registerTools(api) {
|
|
|
603
439
|
return textResult("Conversation ended.");
|
|
604
440
|
}),
|
|
605
441
|
});
|
|
606
|
-
// --- masons_note_for_owner ------------------------------------------------
|
|
607
442
|
api.registerTool({
|
|
608
443
|
name: "masons_note_for_owner",
|
|
609
444
|
description: "Save a note for your owner (Principal). The note will be delivered " +
|
|
@@ -626,54 +461,34 @@ export function registerTools(api) {
|
|
|
626
461
|
return textResult("Note saved. Your owner will see it in their next turn.");
|
|
627
462
|
}),
|
|
628
463
|
});
|
|
629
|
-
// --- masons_upgrade -------------------------------------------------------
|
|
630
464
|
api.registerTool({
|
|
631
465
|
name: "masons_upgrade",
|
|
632
466
|
description: "Check for and install plugin updates via `openclaw plugins update`. Do not use ClawHub (it distributes skills, not plugins).",
|
|
633
467
|
parameters: Type.Object({}),
|
|
634
|
-
// NOT wrapped with withUpdateNotice — this tool IS the upgrade path.
|
|
635
|
-
// Wrapping would create a circular reference in the update notice text.
|
|
636
468
|
execute: async () => {
|
|
637
|
-
// Drain guard: upgrading restarts the Gateway, which clears the
|
|
638
|
-
// in-memory owner notes queue. Warn the LLM to deliver pending
|
|
639
|
-
// notes before upgrading.
|
|
640
469
|
const pendingNotes = ownerNotesQueue.size();
|
|
641
470
|
if (pendingNotes > 0) {
|
|
642
471
|
return textResult(`You have ${pendingNotes} pending note(s) for your owner that haven't been delivered yet. ` +
|
|
643
472
|
`Report them to your owner first, then call masons_upgrade again.`);
|
|
644
473
|
}
|
|
645
474
|
const currentVersion = getPluginVersion();
|
|
646
|
-
|
|
647
|
-
// Always fetch directly from npm — this is an explicit upgrade
|
|
648
|
-
// command, not a passive check. The 24h startup cache
|
|
649
|
-
// (checkForUpdate / getUpdateInfo) is for the withUpdateNotice
|
|
650
|
-
// passive hint only. An explicit "upgrade" must see the real
|
|
651
|
-
// latest version, even if it was published minutes ago.
|
|
652
|
-
const latestVersion = await fetchLatestVersion();
|
|
653
|
-
// 2. Network error — can't determine latest version.
|
|
475
|
+
const latestVersion = await getLatestPublishedVersion();
|
|
654
476
|
if (!latestVersion) {
|
|
655
477
|
return textResult("Could not check for updates (network issue). Try again later.");
|
|
656
478
|
}
|
|
657
|
-
// 3. Already up to date.
|
|
658
479
|
if (latestVersion === currentVersion) {
|
|
659
|
-
upgradeAttemptedVersion = null;
|
|
480
|
+
upgradeAttemptedVersion = null;
|
|
660
481
|
return textResult(`You're on the latest version (v${currentVersion}). No update needed.`);
|
|
661
482
|
}
|
|
662
|
-
// 4. Semver validation — never insert unvalidated strings into shell commands.
|
|
663
483
|
if (!SEMVER_RE.test(latestVersion)) {
|
|
664
484
|
return textResult(`Registry returned an unexpected version format: "${latestVersion}". Try again later.`);
|
|
665
485
|
}
|
|
666
|
-
// 5. Determine path: normal vs fallback.
|
|
667
486
|
const useFallback = upgradeAttemptedVersion === latestVersion;
|
|
668
487
|
if (useFallback) {
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
const home = getOpenClawHome();
|
|
672
|
-
const fallbackCmd = FALLBACK_UPGRADE_CMD.replaceAll("{tmpdir}", tmpdir())
|
|
673
|
-
.replaceAll("{home}", home)
|
|
674
|
-
.replaceAll("{version}", latestVersion);
|
|
488
|
+
upgradeAttemptedVersion = null;
|
|
489
|
+
const fallbackCmd = reinstallCmd(latestVersion);
|
|
675
490
|
return textResult([
|
|
676
|
-
`Update to v${latestVersion} —
|
|
491
|
+
`Update to v${latestVersion} — reinstall through OpenClaw (the normal update command may not have detected the new version).`,
|
|
677
492
|
"",
|
|
678
493
|
"Run this command using the exec tool:",
|
|
679
494
|
fallbackCmd,
|
|
@@ -683,7 +498,6 @@ export function registerTools(api) {
|
|
|
683
498
|
`${fallbackCmd} && ${GATEWAY_RESTART_CMD}`,
|
|
684
499
|
].join("\n"));
|
|
685
500
|
}
|
|
686
|
-
// Normal path — first attempt for this version.
|
|
687
501
|
upgradeAttemptedVersion = latestVersion;
|
|
688
502
|
return textResult([
|
|
689
503
|
`Update available: v${currentVersion} → v${latestVersion}`,
|
|
@@ -697,11 +511,6 @@ export function registerTools(api) {
|
|
|
697
511
|
].join("\n"));
|
|
698
512
|
},
|
|
699
513
|
});
|
|
700
|
-
// --- masons_link_identity -------------------------------------------------
|
|
701
|
-
// Simplified interface (#986 redesign): LLM provides only the current
|
|
702
|
-
// channel's entry. Canonical is auto-generated from getOwnerHandle().
|
|
703
|
-
// Passport entry is auto-included from storedOwnerPassportAddress.
|
|
704
|
-
// Access gate: deterministic owner-only check prevents prompt injection.
|
|
705
514
|
api.registerTool({
|
|
706
515
|
name: "masons_link_identity",
|
|
707
516
|
description: "Link your owner's identity on the current channel to their Passport identity. " +
|
|
@@ -716,9 +525,6 @@ export function registerTools(api) {
|
|
|
716
525
|
}),
|
|
717
526
|
}),
|
|
718
527
|
execute: withUpdateNotice(async (_id, params) => {
|
|
719
|
-
// --- Access gate (deterministic) ---
|
|
720
|
-
// Only the owner on an agent-network turn can invoke this tool.
|
|
721
|
-
// Prevents prompt injection from visitors or remote agents.
|
|
722
528
|
const hookChannelId = getCurrentTurnChannelId();
|
|
723
529
|
const turnIsOwner = getCurrentTurnIsOwnerNonConsuming();
|
|
724
530
|
if (hookChannelId === "agent-network" && !turnIsOwner) {
|
|
@@ -731,20 +537,16 @@ export function registerTools(api) {
|
|
|
731
537
|
const entry = String(params.entry ?? "")
|
|
732
538
|
.trim()
|
|
733
539
|
.toLowerCase();
|
|
734
|
-
// Validate channel:peerId format — split on first colon only,
|
|
735
|
-
// both parts must be non-empty (e.g., "telegram:5099353300").
|
|
736
540
|
const colonIdx = entry.indexOf(":");
|
|
737
541
|
if (colonIdx < 1 || colonIdx === entry.length - 1) {
|
|
738
542
|
return textResult("Invalid format. Use 'channel:peerId' (e.g., 'telegram:5099353300').");
|
|
739
543
|
}
|
|
740
|
-
// Build entries: user-provided entry + auto-include Passport entry
|
|
741
544
|
const entries = [entry];
|
|
742
545
|
const passportAddr = getOwnerPassportAddress();
|
|
743
546
|
if (passportAddr) {
|
|
744
547
|
entries.push(`agent-network:${passportAddr}`);
|
|
745
548
|
}
|
|
746
549
|
try {
|
|
747
|
-
// writeIdentityLinks does entry-level additive merge
|
|
748
550
|
await writeIdentityLinks(ownerHandle, entries);
|
|
749
551
|
}
|
|
750
552
|
catch (err) {
|
|
@@ -766,19 +568,13 @@ export function registerTools(api) {
|
|
|
766
568
|
'reason: "Activate cross-channel identity linking".',
|
|
767
569
|
].join("\n"));
|
|
768
570
|
}),
|
|
769
|
-
},
|
|
770
|
-
// optional: not all Gateway versions support this tool.
|
|
771
|
-
{ optional: true });
|
|
772
|
-
// --- masons_unlink_identity -----------------------------------------------
|
|
773
|
-
// Undo path: removes the owner's canonical identity group from session.identityLinks.
|
|
774
|
-
// Access gate: same owner-only check as masons_link_identity.
|
|
571
|
+
}, { optional: true });
|
|
775
572
|
api.registerTool({
|
|
776
573
|
name: "masons_unlink_identity",
|
|
777
574
|
description: "Remove your owner's cross-channel identity link. Reverses masons_link_identity. " +
|
|
778
575
|
"Requires a gateway restart to take effect.",
|
|
779
576
|
parameters: Type.Object({}),
|
|
780
577
|
execute: withUpdateNotice(async () => {
|
|
781
|
-
// --- Access gate (deterministic) ---
|
|
782
578
|
const hookChannelId = getCurrentTurnChannelId();
|
|
783
579
|
const turnIsOwner = getCurrentTurnIsOwnerNonConsuming();
|
|
784
580
|
if (hookChannelId === "agent-network" && !turnIsOwner) {
|
package/dist/turn-context.d.ts
CHANGED
|
@@ -1,52 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Turn Context — per-turn metadata captured during inbound message dispatch.
|
|
3
|
-
*
|
|
4
|
-
* Tracks the sender identity for the current LLM turn. Set in channel.ts
|
|
5
|
-
* message_received handler (using the closure-scoped ConversationManager),
|
|
6
|
-
* consumed by before_prompt_build in plugin.ts to inject interaction context.
|
|
7
|
-
*
|
|
8
|
-
* Follows the same consume-on-read pattern as owner-session-state.ts:
|
|
9
|
-
* - Written BEFORE dispatch (so before_prompt_build can read it synchronously)
|
|
10
|
-
* - Consumed (read + reset) to prevent stale state across turns
|
|
11
|
-
*
|
|
12
|
-
* Race condition caveat: if two inbound messages arrive on different sessions
|
|
13
|
-
* near-simultaneously, the sender may reflect the wrong session. Acceptable
|
|
14
|
-
* for Phase 1 — OpenClaw processes turns sequentially per agent. Same caveat
|
|
15
|
-
* as owner-session-state.ts currentTurnIsOwner.
|
|
16
|
-
*
|
|
17
|
-
* Module-level singleton — shared between channel.ts (write) and plugin.ts (read).
|
|
18
|
-
*/
|
|
19
|
-
/**
|
|
20
|
-
* Set the sender identity for the current turn.
|
|
21
|
-
* Called in message_received before dispatch, using the closure-scoped
|
|
22
|
-
* ConversationManager to resolve sessionId → contact handle.
|
|
23
|
-
*/
|
|
24
1
|
export declare function setCurrentTurnSender(contact: string | null): void;
|
|
25
|
-
/**
|
|
26
|
-
* Set the channel ID for the current turn.
|
|
27
|
-
* Called in before_prompt_build — the hook provides channelId.
|
|
28
|
-
* Persists through tool execution within the same turn.
|
|
29
|
-
*/
|
|
30
2
|
export declare function setCurrentTurnChannelId(channelId: string | null): void;
|
|
31
|
-
/**
|
|
32
|
-
* Set the owner flag for the current turn (non-consuming copy).
|
|
33
|
-
* Called in before_prompt_build after consuming from owner-session-state.
|
|
34
|
-
* This copy persists through tool execution within the same turn.
|
|
35
|
-
*/
|
|
36
3
|
export declare function setCurrentTurnIsOwnerForTools(value: boolean): void;
|
|
37
|
-
/**
|
|
38
|
-
* Consume the per-turn sender identity. Returns the value and resets to null.
|
|
39
|
-
* Consuming prevents stale state from leaking into subsequent turns.
|
|
40
|
-
*/
|
|
41
4
|
export declare function consumeCurrentTurnSender(): string | null;
|
|
42
|
-
/**
|
|
43
|
-
* Get the channel ID for the current turn (non-consuming).
|
|
44
|
-
* Used by tool access gates to determine which channel triggered the turn.
|
|
45
|
-
*/
|
|
46
5
|
export declare function getCurrentTurnChannelId(): string | null;
|
|
47
|
-
/**
|
|
48
|
-
* Get the owner flag for the current turn (non-consuming).
|
|
49
|
-
* Used by tool access gates to enforce owner-only tools.
|
|
50
|
-
*/
|
|
51
6
|
export declare function getCurrentTurnIsOwnerNonConsuming(): boolean;
|
|
52
7
|
//# sourceMappingURL=turn-context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"turn-context.d.ts","sourceRoot":"","sources":["../src/turn-context.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"turn-context.d.ts","sourceRoot":"","sources":["../src/turn-context.ts"],"names":[],"mappings":"AAyCA,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEjE;AAOD,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEtE;AAOD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAElE;AAUD,wBAAgB,wBAAwB,IAAI,MAAM,GAAG,IAAI,CAIxD;AAMD,wBAAgB,uBAAuB,IAAI,MAAM,GAAG,IAAI,CAEvD;AAMD,wBAAgB,iCAAiC,IAAI,OAAO,CAE3D"}
|