@masons/agent-network 0.5.13 → 0.5.15

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.
Files changed (66) hide show
  1. package/dist/channel.d.ts +0 -7
  2. package/dist/channel.d.ts.map +1 -1
  3. package/dist/channel.js +3 -174
  4. package/dist/cli-setup.d.ts +0 -93
  5. package/dist/cli-setup.d.ts.map +1 -1
  6. package/dist/cli-setup.js +124 -741
  7. package/dist/config-fs.d.ts +4 -0
  8. package/dist/config-fs.d.ts.map +1 -0
  9. package/dist/config-fs.js +23 -0
  10. package/dist/config-schema.js +2 -2
  11. package/dist/config.d.ts +2 -210
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/config.js +14 -334
  14. package/dist/connector-client.d.ts +0 -32
  15. package/dist/connector-client.d.ts.map +1 -1
  16. package/dist/connector-client.js +1 -89
  17. package/dist/constants.d.ts +0 -1
  18. package/dist/constants.d.ts.map +1 -1
  19. package/dist/constants.js +2 -3
  20. package/dist/conversation-manager.d.ts +0 -106
  21. package/dist/conversation-manager.d.ts.map +1 -1
  22. package/dist/conversation-manager.js +2 -131
  23. package/dist/environment-context.d.ts +0 -24
  24. package/dist/environment-context.d.ts.map +1 -1
  25. package/dist/environment-context.js +0 -42
  26. package/dist/handle-utils.d.ts +0 -14
  27. package/dist/handle-utils.d.ts.map +1 -1
  28. package/dist/handle-utils.js +0 -14
  29. package/dist/index.js +0 -9
  30. package/dist/owner-notes.d.ts +0 -33
  31. package/dist/owner-notes.d.ts.map +1 -1
  32. package/dist/owner-notes.js +2 -41
  33. package/dist/owner-session-state.d.ts +0 -26
  34. package/dist/owner-session-state.d.ts.map +1 -1
  35. package/dist/owner-session-state.js +0 -37
  36. package/dist/platform-client.d.ts +13 -202
  37. package/dist/platform-client.d.ts.map +1 -1
  38. package/dist/platform-client.js +22 -171
  39. package/dist/plugin.d.ts +5 -0
  40. package/dist/plugin.d.ts.map +1 -1
  41. package/dist/plugin.js +3 -167
  42. package/dist/sent-message-buffer.d.ts +0 -36
  43. package/dist/sent-message-buffer.d.ts.map +1 -1
  44. package/dist/sent-message-buffer.js +1 -45
  45. package/dist/tools.d.ts +0 -28
  46. package/dist/tools.d.ts.map +1 -1
  47. package/dist/tools.js +36 -240
  48. package/dist/turn-context.d.ts +0 -45
  49. package/dist/turn-context.d.ts.map +1 -1
  50. package/dist/turn-context.js +0 -57
  51. package/dist/types.d.ts +0 -67
  52. package/dist/types.d.ts.map +1 -1
  53. package/dist/types.js +0 -7
  54. package/dist/update-cache.d.ts +0 -17
  55. package/dist/update-cache.d.ts.map +1 -1
  56. package/dist/update-cache.js +1 -21
  57. package/dist/update-check.d.ts +1 -40
  58. package/dist/update-check.d.ts.map +1 -1
  59. package/dist/update-check.js +7 -66
  60. package/dist/version.d.ts +1 -2
  61. package/dist/version.d.ts.map +1 -1
  62. package/dist/version.js +1 -2
  63. package/openclaw.plugin.json +57 -3
  64. package/package.json +11 -10
  65. package/skills/agent-network/SKILL.md +21 -47
  66. 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
- // Stable system context interoperation awareness + routing table.
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 opens a browser to authorize the device, then prompts in the " +
274
- "terminal to pick or create an agent. After it completes, OpenClaw will " +
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":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,MAAM,WAAW,WAAW;IAC1B,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAoC;IAEnD,yCAAyC;IACzC,MAAM,CAAC,QAAQ,CAAC,eAAe,KAAK;IACpC,4CAA4C;IAC5C,MAAM,CAAC,QAAQ,CAAC,MAAM,SAAsB;IAC5C,yFAAyF;IACzF,MAAM,CAAC,QAAQ,CAAC,kBAAkB,QAAQ;IAE1C;;;OAGG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IA2B9C;;;;;OAKG;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE;IAoBzC;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAMD,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
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
- /** Messages older than this are evicted. */
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
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AA6CH,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;AAwCD,uDAAuD;AACvD,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AA4ED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAm2BhD"}
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"}