@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/plugin.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// OpenClaw Plugin entry point.
|
|
2
|
-
// Loaded by OpenClaw Gateway via the "openclaw.extensions" field in package.json.
|
|
3
|
-
// NOT imported by index.ts to avoid pulling ws/typebox into Next.js app bundles.
|
|
4
1
|
import pluginManifest from "../openclaw.plugin.json" with { type: "json" };
|
|
5
2
|
import { agentNetworkChannel, consumeIdentityLinkingNudge } from "./channel.js";
|
|
6
3
|
import { login } from "./cli-setup.js";
|
|
@@ -13,10 +10,6 @@ import { registerTools } from "./tools.js";
|
|
|
13
10
|
import { consumeCurrentTurnSender, setCurrentTurnChannelId, setCurrentTurnIsOwnerForTools, } from "./turn-context.js";
|
|
14
11
|
import { getUpdateInfo } from "./update-check.js";
|
|
15
12
|
import { PLUGIN_VERSION } from "./version.js";
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
// Helpers
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
/** Format a timestamp as a human-readable relative time string. */
|
|
20
13
|
function formatTimeAgo(timestamp) {
|
|
21
14
|
const diff = Date.now() - timestamp;
|
|
22
15
|
const minutes = Math.floor(diff / 60_000);
|
|
@@ -30,10 +23,6 @@ function formatTimeAgo(timestamp) {
|
|
|
30
23
|
const days = Math.floor(hours / 24);
|
|
31
24
|
return `${days}d ago`;
|
|
32
25
|
}
|
|
33
|
-
/**
|
|
34
|
-
* Drain ownerNotesQueue and format into an "[Agent Network — Updates]" block.
|
|
35
|
-
* Returns null if the queue was empty.
|
|
36
|
-
*/
|
|
37
26
|
function drainAndFormatNotes(reportInstruction) {
|
|
38
27
|
const notes = ownerNotesQueue.drain();
|
|
39
28
|
if (notes.length === 0)
|
|
@@ -48,21 +37,7 @@ function drainAndFormatNotes(reportInstruction) {
|
|
|
48
37
|
`${lines.join("\n")}\n` +
|
|
49
38
|
reportInstruction);
|
|
50
39
|
}
|
|
51
|
-
/** Max active interactions to inject into context to avoid noise. */
|
|
52
40
|
const MAX_INTERACTIONS = 5;
|
|
53
|
-
/**
|
|
54
|
-
* Build an interaction space snapshot for the current turn.
|
|
55
|
-
*
|
|
56
|
-
* Reads ConversationManager to list active interactions, then formats
|
|
57
|
-
* a context block that tells the LLM who it's currently interacting with.
|
|
58
|
-
*
|
|
59
|
-
* On network turns, includes turn-specific framing (who sent this message,
|
|
60
|
-
* whether this agent initiated the interaction).
|
|
61
|
-
*
|
|
62
|
-
* On owner turns, shows the interaction space as background context.
|
|
63
|
-
*
|
|
64
|
-
* Returns undefined if no interactions exist or CM is not available.
|
|
65
|
-
*/
|
|
66
41
|
function buildInteractionContext(channelId, turnSender) {
|
|
67
42
|
const cm = getConversationManager();
|
|
68
43
|
if (!cm)
|
|
@@ -70,7 +45,6 @@ function buildInteractionContext(channelId, turnSender) {
|
|
|
70
45
|
const conversations = cm.listConversations();
|
|
71
46
|
if (conversations.length === 0)
|
|
72
47
|
return undefined;
|
|
73
|
-
// Sort by most recent activity, cap at MAX_INTERACTIONS for display.
|
|
74
48
|
const sorted = [...conversations]
|
|
75
49
|
.sort((a, b) => b.lastMessageAt - a.lastMessageAt)
|
|
76
50
|
.slice(0, MAX_INTERACTIONS);
|
|
@@ -86,7 +60,6 @@ function buildInteractionContext(channelId, turnSender) {
|
|
|
86
60
|
});
|
|
87
61
|
const isNetworkTurn = channelId === "agent-network";
|
|
88
62
|
if (isNetworkTurn && turnSender) {
|
|
89
|
-
// Find sender in FULL list (not sorted/capped) for accurate initiatedBy.
|
|
90
63
|
const senderConvo = conversations.find((c) => c.contact === turnSender);
|
|
91
64
|
const initiated = senderConvo?.initiatedBy === "local"
|
|
92
65
|
? "You initiated this interaction."
|
|
@@ -97,12 +70,10 @@ function buildInteractionContext(channelId, turnSender) {
|
|
|
97
70
|
"To report to your owner: masons_note_for_owner.");
|
|
98
71
|
}
|
|
99
72
|
if (isNetworkTurn) {
|
|
100
|
-
// Network turn but sender unknown — still show interaction space.
|
|
101
73
|
return (`${header}\n${lines.join("\n")}\n` +
|
|
102
74
|
"Your text reply goes to the sender of this message. " +
|
|
103
75
|
"To report to your owner: masons_note_for_owner.");
|
|
104
76
|
}
|
|
105
|
-
// Owner turn — show interaction space as background context.
|
|
106
77
|
const ownerHeader = truncated
|
|
107
78
|
? `[Active interactions on the network (showing ${sorted.length} of ${conversations.length})]`
|
|
108
79
|
: "[Active interactions on the network]";
|
|
@@ -113,35 +84,10 @@ const plugin = {
|
|
|
113
84
|
version: PLUGIN_VERSION,
|
|
114
85
|
name: "Agent Network",
|
|
115
86
|
description: "Connect your Agent to the agent network for real-time communication",
|
|
116
|
-
// configSchema MUST be on the plugin export object (not just openclaw.plugin.json).
|
|
117
|
-
// Gateway validates user config via AJV against this schema BEFORE calling register().
|
|
118
|
-
// Missing configSchema → register() is never called (silent skip, no error logged).
|
|
119
|
-
// Imported from openclaw.plugin.json — single source of truth.
|
|
120
87
|
configSchema: pluginManifest.configSchema,
|
|
121
88
|
register(api) {
|
|
122
|
-
// Capture runtime for inbound message dispatch (Pathway 1: module-level singleton).
|
|
123
|
-
// api.runtime is the full PluginRuntime (includes .channel for routing/reply/session).
|
|
124
|
-
// Idempotent — Gateway may hot-reload, calling register() again.
|
|
125
89
|
initPluginRuntime(api.runtime);
|
|
126
|
-
// Register tools FIRST — they must be available even when the channel
|
|
127
|
-
// has no credentials yet.
|
|
128
90
|
registerTools(api);
|
|
129
|
-
// Register the agent-network channel. The 2026.4.x contract reads
|
|
130
|
-
// `plugin.auth?.login` for `openclaw channels login --channel agent-network`
|
|
131
|
-
// (CLI dispatch in `dist/channels-cli-Cc40S0aS.js:85`). Reference plugins
|
|
132
|
-
// (WhatsApp, Feishu in the OpenClaw bundle) use the same shape.
|
|
133
|
-
//
|
|
134
|
-
// History: 0.5.0 mounted this hook at `plugin.setup.configureInteractive`
|
|
135
|
-
// — a path no OpenClaw version reads, so `channels login` never worked.
|
|
136
|
-
// 0.5.1 corrected the mount point. 0.5.2 surfaces register errors
|
|
137
|
-
// instead of silently catching them — the previous "cheap guard"
|
|
138
|
-
// (`catch {}`) hid every kind of register-time failure including the
|
|
139
|
-
// ones we care about (schema validation, missing modules, IO errors).
|
|
140
|
-
//
|
|
141
|
-
// The new contract: log the error visibly AND re-throw, so OpenClaw
|
|
142
|
-
// surfaces the failure to the user rather than running with an
|
|
143
|
-
// un-registered channel that mysteriously "doesn't support login".
|
|
144
|
-
// Per Mingke's review of the 0.5.1 T1 failure (#1264, 2026-04-19).
|
|
145
91
|
try {
|
|
146
92
|
api.registerChannel({
|
|
147
93
|
plugin: {
|
|
@@ -154,41 +100,9 @@ const plugin = {
|
|
|
154
100
|
console.error("[@masons/agent-network] api.registerChannel failed — channel will be unavailable:", err);
|
|
155
101
|
throw err;
|
|
156
102
|
}
|
|
157
|
-
// --- Layer B: Per-turn context injection ---
|
|
158
|
-
// Injects agent network awareness into every LLM turn via before_prompt_build.
|
|
159
|
-
//
|
|
160
|
-
// Two injection channels:
|
|
161
|
-
// - prependSystemContext: stable content (tool list), KV-cacheable by OpenClaw
|
|
162
|
-
// - prependContext: dynamic content (setup state, pending target), changes across turns
|
|
163
|
-
//
|
|
164
|
-
// State detection reads config from disk. To avoid disk I/O every turn, the
|
|
165
|
-
// result is cached and only re-read when the state could have changed (on first
|
|
166
|
-
// turn, and when tools modify config — e.g. writeCredentials, clearTargetHandle).
|
|
167
|
-
//
|
|
168
|
-
// Replaces the previous one-shot before_agent_start hook. That hook only fired
|
|
169
|
-
// once per restart; this fires every turn, providing continuous awareness.
|
|
170
|
-
// History: before_prompt_build was discovered via Mkclaw plugin investigation
|
|
171
|
-
// (2026-03-18). The original Layer B (2026-03-11) used before_agent_start
|
|
172
|
-
// because before_prompt_build was not known at the time.
|
|
173
|
-
/** Cached state from last successful disk read. null = not yet read. */
|
|
174
103
|
let cachedState = null;
|
|
175
|
-
/** Generation at time of last cache fill. Mismatch → re-read. */
|
|
176
104
|
let cachedGeneration = -1;
|
|
177
|
-
|
|
178
|
-
// Encodes semantic concepts for Day 1 competency:
|
|
179
|
-
// 1. Two channels (owner's channel + agent network)
|
|
180
|
-
// 2. Different audiences (never mix content between them)
|
|
181
|
-
// 3. One audience per turn (text reply → turn trigger)
|
|
182
|
-
// 4. Information asymmetry (owner instructions are private)
|
|
183
|
-
// 5. Multi-object interaction space (#873)
|
|
184
|
-
// 6. Action = state change, not conversation (#873)
|
|
185
|
-
// 7. Post-action check (#873)
|
|
186
|
-
// Single continuous string (no bullets/newlines) for KV-cache stability.
|
|
187
|
-
// The first ~900 chars are the original prefix (pre-#873). New content is
|
|
188
|
-
// appended so existing KV-cache prefixes remain valid.
|
|
189
|
-
const TOOL_CONTEXT =
|
|
190
|
-
// --- Original prefix (pre-#873, preserve for KV-cache) ---
|
|
191
|
-
"[Context: Agent Network] You are connected to the agent network. " +
|
|
105
|
+
const TOOL_CONTEXT = "[Context: Agent Network] You are connected to the agent network. " +
|
|
192
106
|
"You operate on TWO channels simultaneously: " +
|
|
193
107
|
"your owner's (Principal's) channel (where your owner talks to you) and " +
|
|
194
108
|
"the agent network (where other agents and visitors reach you). " +
|
|
@@ -205,7 +119,6 @@ const plugin = {
|
|
|
205
119
|
"masons_end_conversation. " +
|
|
206
120
|
"Always try these tools first for network operations. " +
|
|
207
121
|
"If a tool call fails, report the error to your user — do not silently work around it. " +
|
|
208
|
-
// --- Interaction space + goal-driven behavior (#873) ---
|
|
209
122
|
"You interact with multiple entities simultaneously — your owner and agents on the network. " +
|
|
210
123
|
"Each interaction is an Action aimed at changing the other party's state, not just exchanging messages. " +
|
|
211
124
|
"When you send to an agent via masons_send_message, be specific about what you need — " +
|
|
@@ -215,30 +128,14 @@ const plugin = {
|
|
|
215
128
|
"Should you follow up with another agent via masons_send_message? " +
|
|
216
129
|
"When the interaction is complete, call masons_end_conversation as your FINAL action — " +
|
|
217
130
|
"do not generate any text reply after ending. " +
|
|
218
|
-
// --- Cross-channel identity linking knowledge removed from static TOOL_CONTEXT ---
|
|
219
|
-
// Moved to dynamicContext in before_prompt_build (#986 redesign).
|
|
220
|
-
// Reason: TOOL_CONTEXT is built in register() before startAccount() runs,
|
|
221
|
-
// so getDmScope() is not available. Linking knowledge is dmScope-conditional.
|
|
222
131
|
"";
|
|
223
132
|
api.on("before_prompt_build", async (_event, ctx) => {
|
|
224
133
|
const hookCtx = ctx;
|
|
225
134
|
const channelId = hookCtx?.channelId;
|
|
226
|
-
// --- Consume per-turn flags unconditionally (#836, #873) ---
|
|
227
|
-
// MUST happen BEFORE any early return (config read timeout, etc.)
|
|
228
|
-
// to prevent stale state from leaking across turns. If not consumed,
|
|
229
|
-
// the value persists and is read on the NEXT turn, producing incorrect
|
|
230
|
-
// context or — critically — bypassing the access gate on identity tools.
|
|
231
135
|
const turnIsOwner = consumeCurrentTurnIsOwner();
|
|
232
136
|
const turnSender = consumeCurrentTurnSender();
|
|
233
|
-
// --- Store turn context for tool access gates (#986) ---
|
|
234
|
-
// Tools run AFTER before_prompt_build. The consumed flags above are gone
|
|
235
|
-
// by tool execution time. Store non-consuming copies so tools can check
|
|
236
|
-
// channelId and isOwner for access control (e.g., identity linking gate).
|
|
237
|
-
// These persist until the next before_prompt_build overwrites them.
|
|
238
|
-
// Assumption: OpenClaw processes turns sequentially per agent.
|
|
239
137
|
setCurrentTurnChannelId(channelId ?? null);
|
|
240
138
|
setCurrentTurnIsOwnerForTools(turnIsOwner);
|
|
241
|
-
// --- Detect setup state (cached, re-read when config changes) ---
|
|
242
139
|
const currentGeneration = getStateCacheGeneration();
|
|
243
140
|
if (!cachedState || cachedGeneration !== currentGeneration) {
|
|
244
141
|
try {
|
|
@@ -250,35 +147,24 @@ const plugin = {
|
|
|
250
147
|
cachedGeneration = currentGeneration;
|
|
251
148
|
}
|
|
252
149
|
catch {
|
|
253
|
-
// Config read failure — proceed without state-specific context.
|
|
254
|
-
// Will retry on next turn (cachedState stays null).
|
|
255
|
-
// Per-turn flags were already consumed above — no stale state leak.
|
|
256
150
|
return {};
|
|
257
151
|
}
|
|
258
152
|
}
|
|
259
153
|
const state = cachedState;
|
|
260
|
-
// --- Build dynamic context based on setup state ---
|
|
261
154
|
let dynamicContext;
|
|
262
155
|
if (!state.hasCredentials) {
|
|
263
|
-
// Just installed, no credentials yet — guide to CLI setup.
|
|
264
|
-
// The previous `masons_setup_init` LLM tool was removed in 0.5.0
|
|
265
|
-
// (#1253) and replaced by the OAuth device flow in cli-setup.ts.
|
|
266
|
-
// The LLM cannot run the device flow itself (it requires terminal
|
|
267
|
-
// I/O via OpenClaw's prompter), so its job here is to surface the
|
|
268
|
-
// command and wait for the user.
|
|
269
156
|
dynamicContext =
|
|
270
157
|
"[Context: Agent Network] You recently installed the agent network plugin " +
|
|
271
158
|
"but it is not yet authorized. Tell your user to run this in their terminal: " +
|
|
272
159
|
"`openclaw channels login --channel agent-network`. " +
|
|
273
|
-
"The command
|
|
274
|
-
"
|
|
160
|
+
"The command prints a MASONS handoff URL; the browser sign-in handles " +
|
|
161
|
+
"agent selection or creation. After it completes, OpenClaw will " +
|
|
275
162
|
"reload and the agent network tools will become available.";
|
|
276
163
|
if (state.pendingTarget) {
|
|
277
164
|
dynamicContext += ` After setup, send a connection request to ${state.pendingTarget} — they invited your user to join.`;
|
|
278
165
|
}
|
|
279
166
|
}
|
|
280
167
|
else if (state.needsProfile) {
|
|
281
|
-
// Agent created but profile is empty — guide to profile completion
|
|
282
168
|
dynamicContext =
|
|
283
169
|
"[Context: Agent Network] You are set up on the agent network, but your agent's profile is empty. " +
|
|
284
170
|
"Generate a profile based on what you know about the user and their agent, then show it to the user for confirmation. " +
|
|
@@ -290,30 +176,14 @@ const plugin = {
|
|
|
290
176
|
}
|
|
291
177
|
}
|
|
292
178
|
else if (state.pendingTarget) {
|
|
293
|
-
// Setup complete but pending connection — guide to connect
|
|
294
179
|
dynamicContext = `[Context: Agent Network] You are set up on the agent network. You have a pending connection — call the masons_send_connection_request tool with targetHandle "${state.pendingTarget}" to send a connection request.`;
|
|
295
180
|
}
|
|
296
|
-
// --- Routing + interaction context (connected and ready state only) ---
|
|
297
|
-
// When the agent has credentials, no pending setup, and no pending target,
|
|
298
|
-
// inject routing awareness and interaction space based on which channel
|
|
299
|
-
// triggered this turn.
|
|
300
|
-
//
|
|
301
|
-
// Priority: setup/profile/pendingTarget state takes precedence over routing
|
|
302
|
-
// context. An agent in setup flow doesn't need routing warnings — it needs
|
|
303
|
-
// to complete setup first. Routing context only applies to the "ready" state.
|
|
304
181
|
if (!dynamicContext &&
|
|
305
182
|
state.hasCredentials &&
|
|
306
183
|
!state.needsProfile &&
|
|
307
184
|
!state.pendingTarget) {
|
|
308
185
|
if (channelId === "agent-network") {
|
|
309
186
|
if (turnIsOwner) {
|
|
310
|
-
// Owner visiting via Passport — no routing warning needed.
|
|
311
|
-
// The LLM's text reply goes to the owner (the visitor IS the owner).
|
|
312
|
-
//
|
|
313
|
-
// Drain ownerNotesQueue (#1158). General notes from network turns
|
|
314
|
-
// should reach the owner wherever they are — including Passport.
|
|
315
|
-
// The identity linking nudge is handled separately (boolean flag
|
|
316
|
-
// in channel.ts) and delivered only on non-Passport owner turns.
|
|
317
187
|
dynamicContext =
|
|
318
188
|
"[Agent Network — Owner via Passport] Your owner (Principal) is talking to you " +
|
|
319
189
|
"via Passport (your public web chat page where visitors can reach you). " +
|
|
@@ -327,22 +197,15 @@ const plugin = {
|
|
|
327
197
|
}
|
|
328
198
|
}
|
|
329
199
|
else {
|
|
330
|
-
// Turn triggered by a remote agent/stranger — routing warning +
|
|
331
|
-
// interaction space snapshot (#873).
|
|
332
200
|
dynamicContext =
|
|
333
201
|
"[Agent Network — Routing] This turn was triggered by a message " +
|
|
334
202
|
"on the agent network. Your text reply will be sent to that sender — " +
|
|
335
203
|
"your owner (Principal) will NOT see it. To report something to your owner, " +
|
|
336
204
|
"call masons_note_for_owner.";
|
|
337
|
-
// Append interaction space snapshot if active interactions exist.
|
|
338
205
|
const interactionCtx = buildInteractionContext(channelId, turnSender);
|
|
339
206
|
if (interactionCtx) {
|
|
340
207
|
dynamicContext += `\n\n${interactionCtx}`;
|
|
341
208
|
}
|
|
342
|
-
// Append sent message context — cross-session bridge (#918).
|
|
343
|
-
// When the LLM sent a message from the owner session via
|
|
344
|
-
// masons_send_message, inject the content here so the LLM
|
|
345
|
-
// knows what it previously said to this contact.
|
|
346
209
|
if (turnSender) {
|
|
347
210
|
const sentMsgs = sentMessageBuffer.getRecent(turnSender);
|
|
348
211
|
if (sentMsgs.length > 0) {
|
|
@@ -360,13 +223,10 @@ const plugin = {
|
|
|
360
223
|
}
|
|
361
224
|
}
|
|
362
225
|
else {
|
|
363
|
-
// Owner turn (or other channel) — drain pending notes if any.
|
|
364
226
|
const notesBlock = drainAndFormatNotes("Report these to your owner.");
|
|
365
227
|
if (notesBlock) {
|
|
366
228
|
dynamicContext = notesBlock;
|
|
367
229
|
}
|
|
368
|
-
// Identity linking nudge — delivered here (non-Passport owner turn)
|
|
369
|
-
// where the linking action is relevant (#1158).
|
|
370
230
|
if (consumeIdentityLinkingNudge()) {
|
|
371
231
|
const nudge = "[Agent Network — Identity Linking Available]\n" +
|
|
372
232
|
"Your owner visited via Passport (verified). " +
|
|
@@ -377,8 +237,6 @@ const plugin = {
|
|
|
377
237
|
? `${dynamicContext}\n\n${nudge}`
|
|
378
238
|
: nudge;
|
|
379
239
|
}
|
|
380
|
-
// Append interaction space on owner turns too (#873).
|
|
381
|
-
// Gives the agent awareness of active network interactions.
|
|
382
240
|
const interactionCtx = buildInteractionContext(channelId, null);
|
|
383
241
|
if (interactionCtx) {
|
|
384
242
|
dynamicContext = dynamicContext
|
|
@@ -387,10 +245,6 @@ const plugin = {
|
|
|
387
245
|
}
|
|
388
246
|
}
|
|
389
247
|
}
|
|
390
|
-
// --- dmScope-conditional linking knowledge (#986 redesign) ---
|
|
391
|
-
// Placed in dynamicContext (not static TOOL_CONTEXT) because getDmScope()
|
|
392
|
-
// is only available after startAccount() runs (initToolConfig reads cfg).
|
|
393
|
-
// TOOL_CONTEXT is built in register() before startAccount().
|
|
394
248
|
if (state.hasCredentials && !state.needsProfile && !state.pendingTarget) {
|
|
395
249
|
const dmScope = getDmScope();
|
|
396
250
|
if (dmScope !== "main") {
|
|
@@ -406,11 +260,6 @@ const plugin = {
|
|
|
406
260
|
: linkingKnowledge;
|
|
407
261
|
}
|
|
408
262
|
}
|
|
409
|
-
// --- Self-identity preamble (#969) ---
|
|
410
|
-
// Inject agent self-identity so the LLM knows who it is on the network.
|
|
411
|
-
// Owner identity is NOT injected here — agent rejected it as untrusted
|
|
412
|
-
// (0.4.18/0.4.19 case study). Owner recognition is handled via
|
|
413
|
-
// session.identityLinks at the Gateway config layer instead.
|
|
414
263
|
if (state.hasCredentials) {
|
|
415
264
|
const envAgent = getAgentIdentity();
|
|
416
265
|
if (envAgent) {
|
|
@@ -423,8 +272,6 @@ const plugin = {
|
|
|
423
272
|
: identityLine;
|
|
424
273
|
}
|
|
425
274
|
}
|
|
426
|
-
// Post-upgrade verification: if an update is still available after
|
|
427
|
-
// a gateway restart, the previous upgrade attempt may have failed.
|
|
428
275
|
const updateInfo = getUpdateInfo();
|
|
429
276
|
if (updateInfo?.updateAvailable) {
|
|
430
277
|
const upgradeHint = ` Plugin update to v${updateInfo.latestVersion} is available.` +
|
|
@@ -432,17 +279,6 @@ const plugin = {
|
|
|
432
279
|
` Do NOT search ClawHub or run manual npm/openclaw commands.`;
|
|
433
280
|
dynamicContext = (dynamicContext ?? "") + upgradeHint;
|
|
434
281
|
}
|
|
435
|
-
// --- Determine what to inject ---
|
|
436
|
-
// Use prependSystemContext for stable tool awareness (KV-cacheable).
|
|
437
|
-
// Use prependContext for dynamic setup state.
|
|
438
|
-
//
|
|
439
|
-
// Workaround for OpenClaw issue #59546: prependSystemContext is silently
|
|
440
|
-
// ignored in the gateway reply path (confirmed in v2026.4.1, still open).
|
|
441
|
-
// To ensure tool awareness is never lost for gateway-mode users, we
|
|
442
|
-
// include TOOL_CONTEXT in both prependSystemContext (for KV-cache benefit
|
|
443
|
-
// on desktop) and prependContext (as fallback for gateway mode).
|
|
444
|
-
// Cost: ~434 tokens duplicated per turn on desktop OpenClaw.
|
|
445
|
-
// Remove this duplication once #59546 is fixed upstream.
|
|
446
282
|
const systemContext = state.hasCredentials ? TOOL_CONTEXT : undefined;
|
|
447
283
|
if (systemContext && dynamicContext) {
|
|
448
284
|
return {
|
|
@@ -1,50 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sent Message Buffer — per-contact ring buffer of outgoing messages.
|
|
3
|
-
*
|
|
4
|
-
* When the LLM calls `masons_send_message`, the outgoing content is recorded
|
|
5
|
-
* here. On the next agent-network turn from that contact, `before_prompt_build`
|
|
6
|
-
* reads the buffer and injects the sent messages into `prependContext` so the
|
|
7
|
-
* LLM knows what it previously said.
|
|
8
|
-
*
|
|
9
|
-
* This is the reverse of `OwnerNotesQueue`:
|
|
10
|
-
* - OwnerNotesQueue: agent-network session → owner session
|
|
11
|
-
* - SentMessageBuffer: owner session → agent-network session
|
|
12
|
-
*
|
|
13
|
-
* Module-level singleton — shared between plugin.ts (read) and tools.ts (write).
|
|
14
|
-
*
|
|
15
|
-
* Lifecycle: in-memory only (D3). Lost on Gateway restart.
|
|
16
|
-
*
|
|
17
|
-
* @see docs/openclaw/interop-routing-system-design.md §7.1
|
|
18
|
-
*/
|
|
19
1
|
export interface SentMessage {
|
|
20
|
-
/** The message content that was sent. */
|
|
21
2
|
content: string;
|
|
22
|
-
/** When the message was sent (Date.now()). */
|
|
23
3
|
timestamp: number;
|
|
24
4
|
}
|
|
25
5
|
export declare class SentMessageBuffer {
|
|
26
6
|
private entries;
|
|
27
|
-
/** Max messages retained per contact. */
|
|
28
7
|
static readonly MAX_PER_CONTACT = 5;
|
|
29
|
-
/** Messages older than this are evicted. */
|
|
30
8
|
static readonly TTL_MS: number;
|
|
31
|
-
/** Max content length stored per message. Longer content is truncated at record time. */
|
|
32
9
|
static readonly MAX_CONTENT_LENGTH = 1000;
|
|
33
|
-
/**
|
|
34
|
-
* Record an outgoing message to a contact.
|
|
35
|
-
* Called by `masons_send_message` after successful send.
|
|
36
|
-
*/
|
|
37
10
|
record(contact: string, content: string): void;
|
|
38
|
-
/**
|
|
39
|
-
* Get recent sent messages for a contact. Non-destructive — the same
|
|
40
|
-
* messages may be relevant across multiple turns with the same contact.
|
|
41
|
-
* Returns messages in chronological order (oldest first). Returns empty
|
|
42
|
-
* array if no messages exist or all are stale.
|
|
43
|
-
*/
|
|
44
11
|
getRecent(contact: string): SentMessage[];
|
|
45
|
-
/**
|
|
46
|
-
* Clear all entries. For testing only.
|
|
47
|
-
*/
|
|
48
12
|
clear(): void;
|
|
49
13
|
}
|
|
50
14
|
export declare const sentMessageBuffer: SentMessageBuffer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sent-message-buffer.d.ts","sourceRoot":"","sources":["../src/sent-message-buffer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sent-message-buffer.d.ts","sourceRoot":"","sources":["../src/sent-message-buffer.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,WAAW;IAE1B,OAAO,EAAE,MAAM,CAAC;IAEhB,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAoC;IAGnD,MAAM,CAAC,QAAQ,CAAC,eAAe,KAAK;IAEpC,MAAM,CAAC,QAAQ,CAAC,MAAM,SAAsB;IAE5C,MAAM,CAAC,QAAQ,CAAC,kBAAkB,QAAQ;IAM1C,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAiC9C,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE;IAuBzC,KAAK,IAAI,IAAI;CAGd;AAMD,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
|
|
@@ -1,36 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sent Message Buffer — per-contact ring buffer of outgoing messages.
|
|
3
|
-
*
|
|
4
|
-
* When the LLM calls `masons_send_message`, the outgoing content is recorded
|
|
5
|
-
* here. On the next agent-network turn from that contact, `before_prompt_build`
|
|
6
|
-
* reads the buffer and injects the sent messages into `prependContext` so the
|
|
7
|
-
* LLM knows what it previously said.
|
|
8
|
-
*
|
|
9
|
-
* This is the reverse of `OwnerNotesQueue`:
|
|
10
|
-
* - OwnerNotesQueue: agent-network session → owner session
|
|
11
|
-
* - SentMessageBuffer: owner session → agent-network session
|
|
12
|
-
*
|
|
13
|
-
* Module-level singleton — shared between plugin.ts (read) and tools.ts (write).
|
|
14
|
-
*
|
|
15
|
-
* Lifecycle: in-memory only (D3). Lost on Gateway restart.
|
|
16
|
-
*
|
|
17
|
-
* @see docs/openclaw/interop-routing-system-design.md §7.1
|
|
18
|
-
*/
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
// Buffer
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
1
|
export class SentMessageBuffer {
|
|
23
2
|
entries = new Map();
|
|
24
|
-
/** Max messages retained per contact. */
|
|
25
3
|
static MAX_PER_CONTACT = 5;
|
|
26
|
-
|
|
27
|
-
static TTL_MS = 2 * 60 * 60 * 1000; // 2 hours
|
|
28
|
-
/** Max content length stored per message. Longer content is truncated at record time. */
|
|
4
|
+
static TTL_MS = 2 * 60 * 60 * 1000;
|
|
29
5
|
static MAX_CONTENT_LENGTH = 1000;
|
|
30
|
-
/**
|
|
31
|
-
* Record an outgoing message to a contact.
|
|
32
|
-
* Called by `masons_send_message` after successful send.
|
|
33
|
-
*/
|
|
34
6
|
record(contact, content) {
|
|
35
7
|
const now = Date.now();
|
|
36
8
|
const key = contact.toLowerCase();
|
|
@@ -39,26 +11,17 @@ export class SentMessageBuffer {
|
|
|
39
11
|
list = [];
|
|
40
12
|
this.entries.set(key, list);
|
|
41
13
|
}
|
|
42
|
-
// Evict stale entries
|
|
43
14
|
const cutoff = now - SentMessageBuffer.TTL_MS;
|
|
44
15
|
const fresh = list.filter((m) => m.timestamp > cutoff);
|
|
45
|
-
// Cap at MAX_PER_CONTACT (drop oldest)
|
|
46
16
|
if (fresh.length >= SentMessageBuffer.MAX_PER_CONTACT) {
|
|
47
17
|
fresh.shift();
|
|
48
18
|
}
|
|
49
|
-
// Truncate content to prevent unbounded memory growth
|
|
50
19
|
const stored = content.length > SentMessageBuffer.MAX_CONTENT_LENGTH
|
|
51
20
|
? `${content.slice(0, SentMessageBuffer.MAX_CONTENT_LENGTH)}...`
|
|
52
21
|
: content;
|
|
53
22
|
fresh.push({ content: stored, timestamp: now });
|
|
54
23
|
this.entries.set(key, fresh);
|
|
55
24
|
}
|
|
56
|
-
/**
|
|
57
|
-
* Get recent sent messages for a contact. Non-destructive — the same
|
|
58
|
-
* messages may be relevant across multiple turns with the same contact.
|
|
59
|
-
* Returns messages in chronological order (oldest first). Returns empty
|
|
60
|
-
* array if no messages exist or all are stale.
|
|
61
|
-
*/
|
|
62
25
|
getRecent(contact) {
|
|
63
26
|
const key = contact.toLowerCase();
|
|
64
27
|
const list = this.entries.get(key);
|
|
@@ -67,7 +30,6 @@ export class SentMessageBuffer {
|
|
|
67
30
|
const cutoff = Date.now() - SentMessageBuffer.TTL_MS;
|
|
68
31
|
const fresh = list.filter((m) => m.timestamp > cutoff);
|
|
69
32
|
if (fresh.length !== list.length) {
|
|
70
|
-
// Evict stale in place
|
|
71
33
|
if (fresh.length === 0) {
|
|
72
34
|
this.entries.delete(key);
|
|
73
35
|
}
|
|
@@ -77,14 +39,8 @@ export class SentMessageBuffer {
|
|
|
77
39
|
}
|
|
78
40
|
return fresh;
|
|
79
41
|
}
|
|
80
|
-
/**
|
|
81
|
-
* Clear all entries. For testing only.
|
|
82
|
-
*/
|
|
83
42
|
clear() {
|
|
84
43
|
this.entries.clear();
|
|
85
44
|
}
|
|
86
45
|
}
|
|
87
|
-
// ---------------------------------------------------------------------------
|
|
88
|
-
// Singleton
|
|
89
|
-
// ---------------------------------------------------------------------------
|
|
90
46
|
export const sentMessageBuffer = new SentMessageBuffer();
|
package/dist/tools.d.ts
CHANGED
|
@@ -1,22 +1,3 @@
|
|
|
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
1
|
interface ToolContent {
|
|
21
2
|
content: Array<{
|
|
22
3
|
type: "text";
|
|
@@ -34,16 +15,7 @@ interface ToolApi {
|
|
|
34
15
|
optional?: boolean;
|
|
35
16
|
}): void;
|
|
36
17
|
}
|
|
37
|
-
/** @internal Reset module state for test isolation. */
|
|
38
18
|
export declare function _resetToolsForTesting(): void;
|
|
39
|
-
/**
|
|
40
|
-
* Register agent network tools with the OpenClaw Plugin API.
|
|
41
|
-
*
|
|
42
|
-
* Called from `plugin.ts` during plugin registration. Tools become available
|
|
43
|
-
* to the LLM after the plugin loads. Tools that require config will fail-fast
|
|
44
|
-
* with a clear error if `initToolConfig()` hasn't been called yet (i.e.,
|
|
45
|
-
* `startAccount()` hasn't run).
|
|
46
|
-
*/
|
|
47
19
|
export declare function registerTools(api: ToolApi): void;
|
|
48
20
|
export {};
|
|
49
21
|
//# sourceMappingURL=tools.d.ts.map
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AA4DA,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;AAmCD,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAoFD,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAizBhD"}
|