@masons/agent-network 0.4.13 → 0.4.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.
- package/dist/channel.d.ts.map +1 -1
- package/dist/channel.js +10 -3
- package/dist/conversation-manager.d.ts +8 -0
- package/dist/conversation-manager.d.ts.map +1 -1
- package/dist/conversation-manager.js +11 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +22 -2
- package/dist/sent-message-buffer.d.ts +51 -0
- package/dist/sent-message-buffer.d.ts.map +1 -0
- package/dist/sent-message-buffer.js +90 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +9 -5
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +11 -10
package/dist/channel.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAsB9D,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,UAAU,oBAAoB,CAAC,CAAC;IAC9B,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAAC;IACvD,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC;IACpE,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;IACjE,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;CAC/D;AAED,UAAU,sBAAsB;IAC9B,EAAE,EAAE,OAAO,CAAC;CACb;AAED,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,sBAAsB;IAC9B,YAAY,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC9C,QAAQ,CAAC,CAAC,GAAG,EAAE,sBAAsB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACzE;AAED,UAAU,qBAAqB,CAAC,CAAC,GAAG,OAAO;IACzC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAsDD,UAAU,qBAAqB,CAAC,CAAC,GAAG,OAAO;IACzC,YAAY,CAAC,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/D,WAAW,CAAC,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED,UAAU,yBAAyB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,YAAY,EAAE,mBAAmB,CAAC;IAClC,MAAM,EAAE,oBAAoB,CAAC,mBAAmB,CAAC,CAAC;IAClD,QAAQ,EAAE,sBAAsB,CAAC;IACjC,OAAO,EAAE,qBAAqB,CAAC,mBAAmB,CAAC,CAAC;CACrD;AAkED,eAAO,MAAM,mBAAmB,EAAE,
|
|
1
|
+
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAsB9D,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,UAAU,oBAAoB,CAAC,CAAC;IAC9B,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAAC;IACvD,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC;IACpE,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;IACjE,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;CAC/D;AAED,UAAU,sBAAsB;IAC9B,EAAE,EAAE,OAAO,CAAC;CACb;AAED,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,sBAAsB;IAC9B,YAAY,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC9C,QAAQ,CAAC,CAAC,GAAG,EAAE,sBAAsB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACzE;AAED,UAAU,qBAAqB,CAAC,CAAC,GAAG,OAAO;IACzC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAsDD,UAAU,qBAAqB,CAAC,CAAC,GAAG,OAAO;IACzC,YAAY,CAAC,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/D,WAAW,CAAC,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED,UAAU,yBAAyB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,YAAY,EAAE,mBAAmB,CAAC;IAClC,MAAM,EAAE,oBAAoB,CAAC,mBAAmB,CAAC,CAAC;IAClD,QAAQ,EAAE,sBAAsB,CAAC;IACjC,OAAO,EAAE,qBAAqB,CAAC,mBAAmB,CAAC,CAAC;CACrD;AAkED,eAAO,MAAM,mBAAmB,EAAE,yBAkajC,CAAC"}
|
package/dist/channel.js
CHANGED
|
@@ -244,9 +244,16 @@ export const agentNetworkChannel = {
|
|
|
244
244
|
const text = payload.text;
|
|
245
245
|
if (!text)
|
|
246
246
|
return;
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
//
|
|
247
|
+
// Guard: if conversation was ended during this dispatch
|
|
248
|
+
// (LLM called masons_end_conversation mid-turn), suppress
|
|
249
|
+
// straggler text to prevent auto-creating a new session.
|
|
250
|
+
// endConversation() deletes the conversation entry — its
|
|
251
|
+
// absence is the signal. Normal inbound replies always
|
|
252
|
+
// have an entry (registerInbound creates it before dispatch).
|
|
253
|
+
if (!conversationManager.hasConversation(senderId)) {
|
|
254
|
+
dbg("deliver suppressed: conversation ended senderId=%s len=%d", senderId, text.length);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
250
257
|
const deliverResult = await conversationManager.send(senderId, text);
|
|
251
258
|
dbg("deliver reply senderId=%s status=%s contentLength=%d", senderId, deliverResult.status, text.length);
|
|
252
259
|
},
|
|
@@ -64,6 +64,14 @@ export declare class ConversationManager {
|
|
|
64
64
|
* List all conversations with metadata.
|
|
65
65
|
*/
|
|
66
66
|
listConversations(): ConversationSummary[];
|
|
67
|
+
/**
|
|
68
|
+
* Check if a conversation entry exists for a contact.
|
|
69
|
+
*
|
|
70
|
+
* Used by the deliver callback to detect post-end straggler text:
|
|
71
|
+
* endConversation() deletes the entry, so its absence signals that
|
|
72
|
+
* the LLM called masons_end_conversation during this dispatch.
|
|
73
|
+
*/
|
|
74
|
+
hasConversation(contact: string): boolean;
|
|
67
75
|
/**
|
|
68
76
|
* End the conversation with a contact.
|
|
69
77
|
* Closes the active session if one exists.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-manager.d.ts","sourceRoot":"","sources":["../src/conversation-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAQ1D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,GAAG,QAAQ,CAAC;CACjC;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,GAAG,QAAQ,CAAC;CACjC;AAMD,qBAAa,mBAAmB;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC,0CAA0C;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwC;gBAE1D,MAAM,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM;IAU1D;;;;;;OAMG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAgDjE;;;OAGG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAyB1E;;;OAGG;IACH,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAQ5D;;;OAGG;IACH,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI5D;;OAEG;IACH,iBAAiB,IAAI,mBAAmB,EAAE;IAe1C;;;OAGG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAkBtC;;;;;;;;;OASG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAYvC;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAUtC,0CAA0C;IAC1C,gBAAgB,IAAI,IAAI;CAIzB"}
|
|
1
|
+
{"version":3,"file":"conversation-manager.d.ts","sourceRoot":"","sources":["../src/conversation-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAQ1D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,GAAG,QAAQ,CAAC;CACjC;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,GAAG,QAAQ,CAAC;CACjC;AAMD,qBAAa,mBAAmB;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC,0CAA0C;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwC;gBAE1D,MAAM,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM;IAU1D;;;;;;OAMG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAgDjE;;;OAGG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAyB1E;;;OAGG;IACH,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAQ5D;;;OAGG;IACH,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI5D;;OAEG;IACH,iBAAiB,IAAI,mBAAmB,EAAE;IAe1C;;;;;;OAMG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAKzC;;;OAGG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAkBtC;;;;;;;;;OASG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAYvC;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAUtC,0CAA0C;IAC1C,gBAAgB,IAAI,IAAI;CAIzB"}
|
|
@@ -131,6 +131,17 @@ export class ConversationManager {
|
|
|
131
131
|
}
|
|
132
132
|
return result;
|
|
133
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Check if a conversation entry exists for a contact.
|
|
136
|
+
*
|
|
137
|
+
* Used by the deliver callback to detect post-end straggler text:
|
|
138
|
+
* endConversation() deletes the entry, so its absence signals that
|
|
139
|
+
* the LLM called masons_end_conversation during this dispatch.
|
|
140
|
+
*/
|
|
141
|
+
hasConversation(contact) {
|
|
142
|
+
const address = this.resolveAddress(contact);
|
|
143
|
+
return this.conversations.has(address);
|
|
144
|
+
}
|
|
134
145
|
/**
|
|
135
146
|
* End the conversation with a contact.
|
|
136
147
|
* Closes the active session if one exists.
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAoHA,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC;CACnE;AAED,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;kBA4BI,iBAAiB;CAgThC,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/dist/plugin.js
CHANGED
|
@@ -6,6 +6,7 @@ import { configureInteractive } from "./cli-setup.js";
|
|
|
6
6
|
import { detectPendingState, getConversationManager, getStateCacheGeneration, initPluginRuntime, } from "./config.js";
|
|
7
7
|
import { ownerNotesQueue } from "./owner-notes.js";
|
|
8
8
|
import { consumeCurrentTurnIsOwner } from "./owner-session-state.js";
|
|
9
|
+
import { sentMessageBuffer } from "./sent-message-buffer.js";
|
|
9
10
|
import { registerTools } from "./tools.js";
|
|
10
11
|
import { consumeCurrentTurnSender } from "./turn-context.js";
|
|
11
12
|
import { getUpdateInfo } from "./update-check.js";
|
|
@@ -192,8 +193,9 @@ const plugin = {
|
|
|
192
193
|
"request action, not open-ended conversation. " +
|
|
193
194
|
"After responding on the network, check: " +
|
|
194
195
|
"should you report to your owner via masons_note_for_owner? " +
|
|
195
|
-
"Should you
|
|
196
|
-
"
|
|
196
|
+
"Should you follow up with another agent via masons_send_message? " +
|
|
197
|
+
"When the interaction is complete, call masons_end_conversation as your FINAL action — " +
|
|
198
|
+
"do not generate any text reply after ending.";
|
|
197
199
|
api.on("before_prompt_build", async (_event, ctx) => {
|
|
198
200
|
const hookCtx = ctx;
|
|
199
201
|
const channelId = hookCtx?.channelId;
|
|
@@ -300,6 +302,24 @@ const plugin = {
|
|
|
300
302
|
if (interactionCtx) {
|
|
301
303
|
dynamicContext += `\n\n${interactionCtx}`;
|
|
302
304
|
}
|
|
305
|
+
// Append sent message context — cross-session bridge (#918).
|
|
306
|
+
// When the LLM sent a message from the owner session via
|
|
307
|
+
// masons_send_message, inject the content here so the LLM
|
|
308
|
+
// knows what it previously said to this contact.
|
|
309
|
+
if (turnSender) {
|
|
310
|
+
const sentMsgs = sentMessageBuffer.getRecent(turnSender);
|
|
311
|
+
if (sentMsgs.length > 0) {
|
|
312
|
+
const lines = sentMsgs.map((m) => {
|
|
313
|
+
const preview = m.content.length > 100
|
|
314
|
+
? `${m.content.slice(0, 100)}...`
|
|
315
|
+
: m.content;
|
|
316
|
+
return `• (${formatTimeAgo(m.timestamp)}): "${preview}"`;
|
|
317
|
+
});
|
|
318
|
+
dynamicContext +=
|
|
319
|
+
`\n\n[Your recent messages to @${turnSender}]\n` +
|
|
320
|
+
lines.join("\n");
|
|
321
|
+
}
|
|
322
|
+
}
|
|
303
323
|
}
|
|
304
324
|
}
|
|
305
325
|
else {
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
export interface SentMessage {
|
|
20
|
+
/** The message content that was sent. */
|
|
21
|
+
content: string;
|
|
22
|
+
/** When the message was sent (Date.now()). */
|
|
23
|
+
timestamp: number;
|
|
24
|
+
}
|
|
25
|
+
export declare class SentMessageBuffer {
|
|
26
|
+
private entries;
|
|
27
|
+
/** Max messages retained per contact. */
|
|
28
|
+
static readonly MAX_PER_CONTACT = 5;
|
|
29
|
+
/** Messages older than this are evicted. */
|
|
30
|
+
static readonly TTL_MS: number;
|
|
31
|
+
/** Max content length stored per message. Longer content is truncated at record time. */
|
|
32
|
+
static readonly MAX_CONTENT_LENGTH = 500;
|
|
33
|
+
/**
|
|
34
|
+
* Record an outgoing message to a contact.
|
|
35
|
+
* Called by `masons_send_message` after successful send.
|
|
36
|
+
*/
|
|
37
|
+
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
|
+
getRecent(contact: string): SentMessage[];
|
|
45
|
+
/**
|
|
46
|
+
* Clear all entries. For testing only.
|
|
47
|
+
*/
|
|
48
|
+
clear(): void;
|
|
49
|
+
}
|
|
50
|
+
export declare const sentMessageBuffer: SentMessageBuffer;
|
|
51
|
+
//# sourceMappingURL=sent-message-buffer.d.ts.map
|
|
@@ -0,0 +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,SAAkB;IACxC,yFAAyF;IACzF,MAAM,CAAC,QAAQ,CAAC,kBAAkB,OAAO;IAEzC;;;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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
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
|
+
export class SentMessageBuffer {
|
|
23
|
+
entries = new Map();
|
|
24
|
+
/** Max messages retained per contact. */
|
|
25
|
+
static MAX_PER_CONTACT = 5;
|
|
26
|
+
/** Messages older than this are evicted. */
|
|
27
|
+
static TTL_MS = 30 * 60 * 1000; // 30 minutes
|
|
28
|
+
/** Max content length stored per message. Longer content is truncated at record time. */
|
|
29
|
+
static MAX_CONTENT_LENGTH = 500;
|
|
30
|
+
/**
|
|
31
|
+
* Record an outgoing message to a contact.
|
|
32
|
+
* Called by `masons_send_message` after successful send.
|
|
33
|
+
*/
|
|
34
|
+
record(contact, content) {
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
const key = contact.toLowerCase();
|
|
37
|
+
let list = this.entries.get(key);
|
|
38
|
+
if (!list) {
|
|
39
|
+
list = [];
|
|
40
|
+
this.entries.set(key, list);
|
|
41
|
+
}
|
|
42
|
+
// Evict stale entries
|
|
43
|
+
const cutoff = now - SentMessageBuffer.TTL_MS;
|
|
44
|
+
const fresh = list.filter((m) => m.timestamp > cutoff);
|
|
45
|
+
// Cap at MAX_PER_CONTACT (drop oldest)
|
|
46
|
+
if (fresh.length >= SentMessageBuffer.MAX_PER_CONTACT) {
|
|
47
|
+
fresh.shift();
|
|
48
|
+
}
|
|
49
|
+
// Truncate content to prevent unbounded memory growth
|
|
50
|
+
const stored = content.length > SentMessageBuffer.MAX_CONTENT_LENGTH
|
|
51
|
+
? `${content.slice(0, SentMessageBuffer.MAX_CONTENT_LENGTH)}...`
|
|
52
|
+
: content;
|
|
53
|
+
fresh.push({ content: stored, timestamp: now });
|
|
54
|
+
this.entries.set(key, fresh);
|
|
55
|
+
}
|
|
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
|
+
getRecent(contact) {
|
|
63
|
+
const key = contact.toLowerCase();
|
|
64
|
+
const list = this.entries.get(key);
|
|
65
|
+
if (!list)
|
|
66
|
+
return [];
|
|
67
|
+
const cutoff = Date.now() - SentMessageBuffer.TTL_MS;
|
|
68
|
+
const fresh = list.filter((m) => m.timestamp > cutoff);
|
|
69
|
+
if (fresh.length !== list.length) {
|
|
70
|
+
// Evict stale in place
|
|
71
|
+
if (fresh.length === 0) {
|
|
72
|
+
this.entries.delete(key);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
this.entries.set(key, fresh);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return fresh;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Clear all entries. For testing only.
|
|
82
|
+
*/
|
|
83
|
+
clear() {
|
|
84
|
+
this.entries.clear();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Singleton
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
export const sentMessageBuffer = new SentMessageBuffer();
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA4CH,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;AA0CD,uDAAuD;AACvD,wBAAgB,qBAAqB,IAAI,IAAI,CAI5C;AAsFD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAs2BhD"}
|
package/dist/tools.js
CHANGED
|
@@ -22,6 +22,7 @@ import { tmpdir } from "node:os";
|
|
|
22
22
|
import { Type } from "@sinclair/typebox";
|
|
23
23
|
import { clearTargetHandle, getOpenClawHome, getPendingTarget, isProfileNeeded, markProfileComplete, markProfileNeeded, requireApiKey, requireConversationManager, requirePlatformConfig, writeCredentials, } from "./config.js";
|
|
24
24
|
import { ownerNotesQueue } from "./owner-notes.js";
|
|
25
|
+
import { sentMessageBuffer } from "./sent-message-buffer.js";
|
|
25
26
|
import { acceptRequest, declineRequest, getConnectionStatus, initSetup, listConnections, listRequests, onboard, PlatformApiError, pollSetup, reconnect, requestConnection, SetupExpiredError, SetupPendingError, updateProfile, } from "./platform-client.js";
|
|
26
27
|
import { fetchLatestVersion, getPluginVersion, getUpdateInfo, } from "./update-check.js";
|
|
27
28
|
// ---------------------------------------------------------------------------
|
|
@@ -605,14 +606,14 @@ export function registerTools(api) {
|
|
|
605
606
|
const cm = requireConversationManager();
|
|
606
607
|
const to = params.to;
|
|
607
608
|
const content = params.content;
|
|
609
|
+
// Extract handle once — shared by pre-flight check and sent message buffer.
|
|
610
|
+
const handle = to.startsWith("mstps://") || to.startsWith("mstp://")
|
|
611
|
+
? to.replace(/^mstps?:\/\/[^/]+\//, "").replace(/\/+$/, "")
|
|
612
|
+
: to;
|
|
608
613
|
// --- Pre-flight: check connection status (advisory, not gate) ---
|
|
609
614
|
// If the check fails (network error, API down), proceed anyway —
|
|
610
615
|
// the Connector gate (Layer 2) is the authoritative enforcement.
|
|
611
616
|
try {
|
|
612
|
-
// Extract handle for connection check
|
|
613
|
-
const handle = to.startsWith("mstps://") || to.startsWith("mstp://")
|
|
614
|
-
? to.replace(/^mstps?:\/\/[^/]+\//, "").replace(/\/+$/, "")
|
|
615
|
-
: to;
|
|
616
617
|
if (handle) {
|
|
617
618
|
const cfg = requirePlatformConfig();
|
|
618
619
|
const apiKey = requireApiKey();
|
|
@@ -630,6 +631,9 @@ export function registerTools(api) {
|
|
|
630
631
|
}
|
|
631
632
|
const result = await cm.send(to, content);
|
|
632
633
|
if (result.status === "sent") {
|
|
634
|
+
// Record in sent message buffer so the agent-network session has
|
|
635
|
+
// context when the remote agent replies (cross-session bridge, #918).
|
|
636
|
+
sentMessageBuffer.record(handle, content);
|
|
633
637
|
return textResult("Message sent.");
|
|
634
638
|
}
|
|
635
639
|
return textResult(result.error ??
|
|
@@ -639,7 +643,7 @@ export function registerTools(api) {
|
|
|
639
643
|
// --- masons_end_conversation -----------------------------------------------
|
|
640
644
|
api.registerTool({
|
|
641
645
|
name: "masons_end_conversation",
|
|
642
|
-
description: "End the conversation with a connected agent.",
|
|
646
|
+
description: "End the conversation with a connected agent. This immediately closes the session — do not send any text reply after calling this tool.",
|
|
643
647
|
parameters: Type.Object({
|
|
644
648
|
contact: Type.String({
|
|
645
649
|
description: "Handle (e.g. alice) or address of the agent to end the conversation with",
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Plugin version — must match package.json. Validated by prepublishOnly. */
|
|
2
|
-
export const PLUGIN_VERSION = "0.4.
|
|
2
|
+
export const PLUGIN_VERSION = "0.4.15";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@masons/agent-network",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.15",
|
|
4
4
|
"description": "MASONS plugin for OpenClaw — connect your agent to the agent network",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "MASONS.ai <hello@masons.ai> (https://masons.ai)",
|
|
@@ -19,6 +19,15 @@
|
|
|
19
19
|
"publishConfig": {
|
|
20
20
|
"access": "public"
|
|
21
21
|
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc",
|
|
24
|
+
"dev": "tsc --watch",
|
|
25
|
+
"test": "tsc -p test/tsconfig.json && node --test --loader ts-node/esm test/**/*.test.ts",
|
|
26
|
+
"lint": "biome check",
|
|
27
|
+
"format": "biome format --write",
|
|
28
|
+
"prepublishOnly": "bash scripts/check-version.sh && npm run build && npm run test",
|
|
29
|
+
"release": "pnpm publish --access public"
|
|
30
|
+
},
|
|
22
31
|
"files": [
|
|
23
32
|
"dist/",
|
|
24
33
|
"openclaw.plugin.json",
|
|
@@ -68,13 +77,5 @@
|
|
|
68
77
|
"@types/ws": "^8",
|
|
69
78
|
"ts-node": "^10",
|
|
70
79
|
"typescript": "^5"
|
|
71
|
-
},
|
|
72
|
-
"scripts": {
|
|
73
|
-
"build": "tsc",
|
|
74
|
-
"dev": "tsc --watch",
|
|
75
|
-
"test": "tsc -p test/tsconfig.json && node --test --loader ts-node/esm test/**/*.test.ts",
|
|
76
|
-
"lint": "biome check",
|
|
77
|
-
"format": "biome format --write",
|
|
78
|
-
"release": "pnpm publish --access public"
|
|
79
80
|
}
|
|
80
|
-
}
|
|
81
|
+
}
|