@masons/agent-network 0.6.20 → 0.6.21

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 (41) hide show
  1. package/dist/channel.d.ts.map +1 -1
  2. package/dist/channel.js +54 -44
  3. package/dist/config.d.ts +2 -0
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +6 -0
  6. package/dist/connector-client.d.ts +35 -4
  7. package/dist/connector-client.d.ts.map +1 -1
  8. package/dist/connector-client.js +126 -19
  9. package/dist/conversation-manager-context.d.ts +4 -0
  10. package/dist/conversation-manager-context.d.ts.map +1 -0
  11. package/dist/conversation-manager-context.js +8 -0
  12. package/dist/conversation-manager.d.ts +29 -4
  13. package/dist/conversation-manager.d.ts.map +1 -1
  14. package/dist/conversation-manager.js +163 -14
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/platform-client.d.ts +1 -0
  18. package/dist/platform-client.d.ts.map +1 -1
  19. package/dist/platform-client.js +12 -0
  20. package/dist/plugin.d.ts +2 -1
  21. package/dist/plugin.d.ts.map +1 -1
  22. package/dist/plugin.js +49 -30
  23. package/dist/sent-message-buffer.d.ts +2 -3
  24. package/dist/sent-message-buffer.d.ts.map +1 -1
  25. package/dist/sent-message-buffer.js +8 -11
  26. package/dist/tools.d.ts.map +1 -1
  27. package/dist/tools.js +101 -7
  28. package/dist/turn-context.d.ts +0 -4
  29. package/dist/turn-context.d.ts.map +1 -1
  30. package/dist/turn-context.js +0 -18
  31. package/dist/types.d.ts +10 -1
  32. package/dist/types.d.ts.map +1 -1
  33. package/dist/types.js +3 -1
  34. package/dist/version.d.ts +1 -1
  35. package/dist/version.js +1 -1
  36. package/dist/ws-heartbeat.d.ts +23 -0
  37. package/dist/ws-heartbeat.d.ts.map +1 -0
  38. package/dist/ws-heartbeat.js +63 -0
  39. package/openclaw.plugin.json +2 -1
  40. package/package.json +1 -1
  41. package/skills/agent-network/SKILL.md +9 -0
@@ -0,0 +1,8 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ const conversationManagerContext = new AsyncLocalStorage();
3
+ export function runWithConversationManagerContext(manager, callback) {
4
+ return conversationManagerContext.run(manager, callback);
5
+ }
6
+ export function getInvocationConversationManager() {
7
+ return conversationManagerContext.getStore() ?? null;
8
+ }
@@ -1,9 +1,25 @@
1
- import { type ConnectorClient } from "./connector-client.js";
2
- export interface SendResult {
3
- status: "sent" | "failed";
1
+ import { type ConnectorClient, type SendOutcomeUnknownReason } from "./connector-client.js";
2
+ import { type SentMessage } from "./sent-message-buffer.js";
3
+ import type { SendAckEvent } from "./types.js";
4
+ export type SendResult = {
5
+ status: "sent";
6
+ messageId: string;
7
+ acknowledgementStatus: string;
8
+ messageRef?: string;
9
+ addressedTarget?: NonNullable<SendAckEvent["addressedTarget"]>;
10
+ } | {
11
+ status: "unknown";
12
+ messageId: string;
13
+ attempts: 1;
14
+ retryable: false;
15
+ reason: SendOutcomeUnknownReason;
16
+ error: string;
17
+ errorCode: "SEND_OUTCOME_UNKNOWN";
18
+ } | {
19
+ status: "failed";
4
20
  error?: string;
5
21
  errorCode?: string;
6
- }
22
+ };
7
23
  export interface ConversationEntry {
8
24
  contact: string;
9
25
  address: string;
@@ -28,11 +44,16 @@ export interface InboundEnvelope {
28
44
  export declare const REPLY_CORRELATION_MAX_AGE_MS: number;
29
45
  export declare class ConversationManager {
30
46
  private readonly client;
47
+ private readonly sentMessageBuffer;
31
48
  private readonly conversations;
49
+ private readonly conversationAliases;
32
50
  private readonly pendingOutreach;
33
51
  constructor(client: ConnectorClient);
52
+ recordOutboundContext(peerAddress: string, content: string): void;
53
+ getRecentOutboundContext(peerAddress: string): SentMessage[];
34
54
  send(contact: string, content: string, metadata?: Record<string, unknown>): Promise<SendResult>;
35
55
  registerInbound(contact: string, address: string): void;
56
+ refreshInboundPortrayal(contact: string, address: string): boolean;
36
57
  recordInboundEnvelope(address: string, envelope: Omit<InboundEnvelope, "receivedAt">): void;
37
58
  getContactByAddress(address: string): string | undefined;
38
59
  listConversations(): ConversationSummary[];
@@ -42,6 +63,10 @@ export declare class ConversationManager {
42
63
  resolveAddress(contact: string): string;
43
64
  private resolveContactForSend;
44
65
  extractHandle(address: string): string;
66
+ private resolveConversationAlias;
67
+ private recordConversationAlias;
68
+ private deleteConversationAliases;
69
+ private fileResolvedConversation;
45
70
  private ensureConversationEntry;
46
71
  _resetForTesting(): void;
47
72
  }
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-manager.d.ts","sourceRoot":"","sources":["../src/conversation-manager.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,KAAK,eAAe,EAAkB,MAAM,uBAAuB,CAAC;AAa7E,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;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;IAchC,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;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;AAuBD,MAAM,WAAW,eAAe;IAE9B,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AASD,eAAO,MAAM,4BAA4B,QAAc,CAAC;AA0CxD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IAGzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwC;IAiBtE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;gBAEzC,MAAM,EAAE,eAAe;IAsB7B,IAAI,CACR,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,UAAU,CAAC;IA0HtB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAYvD,qBAAqB,CACnB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC,GAC5C,IAAI;IAmBP,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAkBxD,iBAAiB,IAAI,mBAAmB,EAAE;IAwB1C,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAiBzC,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAoCzC,wBAAwB,IAAI,IAAI;IAuChC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAwBvC,OAAO,CAAC,qBAAqB;IAuC7B,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAQtC,OAAO,CAAC,uBAAuB;IAiC/B,gBAAgB,IAAI,IAAI;CAGzB"}
1
+ {"version":3,"file":"conversation-manager.d.ts","sourceRoot":"","sources":["../src/conversation-manager.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import createDebug from "debug";
2
- import { ConnectorError } from "./connector-client.js";
2
+ import { ConnectorError, SendOutcomeUnknownError, } from "./connector-client.js";
3
3
  import { extractHandleFromAddress } from "./handle-utils.js";
4
+ import { SentMessageBuffer } from "./sent-message-buffer.js";
4
5
  import { createReplyTraceMetadata, ensureTraceMetadata, } from "./trace-metadata.js";
5
6
  const dbg = createDebug("agent-network:conversation-manager");
6
7
  export const REPLY_CORRELATION_MAX_AGE_MS = 10 * 60_000;
@@ -19,17 +20,68 @@ function normalizeHandleContact(contact) {
19
20
  return contact.startsWith("@") ? contact.slice(1) : contact;
20
21
  }
21
22
  function isRoutableAddress(value) {
22
- return (value.startsWith("mstps://") ||
23
- value.startsWith("mstp://") ||
24
- value.startsWith("passport:"));
23
+ return /^mstps?:\/\//i.test(value) || value.startsWith("passport:");
24
+ }
25
+ function isAddressedTargetAddress(value) {
26
+ for (let index = 0; index < value.length; index++) {
27
+ const codeUnit = value.charCodeAt(index);
28
+ if (codeUnit <= 0x1f || codeUnit === 0x7f)
29
+ return false;
30
+ }
31
+ if (!/^mstps?:\/\//i.test(value)) {
32
+ return false;
33
+ }
34
+ try {
35
+ const parsed = new URL(value);
36
+ return ((parsed.protocol === "mstp:" || parsed.protocol === "mstps:") &&
37
+ parsed.hostname.length > 0);
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ }
43
+ function isCanonicalResolvedNodeAddress(value) {
44
+ return /^mstps?:\/\/[a-z0-9.-]+(?::[0-9]+)?\/[a-z][a-z0-9_-]{2,14}$/.test(value);
45
+ }
46
+ function normalizedAddressedTarget(value) {
47
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
48
+ return undefined;
49
+ }
50
+ const candidate = value;
51
+ if (typeof candidate.address !== "string" ||
52
+ candidate.address.length === 0 ||
53
+ (candidate.basis !== "resolved" && candidate.basis !== "forwarded")) {
54
+ return undefined;
55
+ }
56
+ const validAddress = candidate.basis === "resolved"
57
+ ? isCanonicalResolvedNodeAddress(candidate.address)
58
+ : isAddressedTargetAddress(candidate.address);
59
+ if (!validAddress) {
60
+ return undefined;
61
+ }
62
+ return {
63
+ address: candidate.address,
64
+ basis: candidate.basis,
65
+ };
66
+ }
67
+ function normalizedMessageRef(value) {
68
+ return typeof value === "string" && value.length > 0 ? value : undefined;
25
69
  }
26
70
  export class ConversationManager {
27
71
  client;
72
+ sentMessageBuffer = new SentMessageBuffer();
28
73
  conversations = new Map();
74
+ conversationAliases = new Map();
29
75
  pendingOutreach = new Set();
30
76
  constructor(client) {
31
77
  this.client = client;
32
78
  }
79
+ recordOutboundContext(peerAddress, content) {
80
+ this.sentMessageBuffer.record(peerAddress, content);
81
+ }
82
+ getRecentOutboundContext(peerAddress) {
83
+ return this.sentMessageBuffer.getRecent(peerAddress);
84
+ }
33
85
  async send(contact, content, metadata) {
34
86
  const resolution = this.resolveContactForSend(contact);
35
87
  if (resolution.ambiguous) {
@@ -74,16 +126,45 @@ export class ConversationManager {
74
126
  }
75
127
  try {
76
128
  const ack = await this.client.send(address, content, "text", wireMetadata);
77
- if (isRoutableAddress(address)) {
129
+ const addressedTarget = normalizedAddressedTarget(ack.addressedTarget);
130
+ const messageRef = normalizedMessageRef(ack.messageRef);
131
+ const resolvedAddress = addressedTarget?.basis === "resolved"
132
+ ? addressedTarget.address
133
+ : undefined;
134
+ if (resolvedAddress !== undefined) {
135
+ if (!isRoutableAddress(address)) {
136
+ this.pendingOutreach.delete(normalizeHandleContact(address).toLowerCase());
137
+ }
138
+ this.fileResolvedConversation(address, resolvedAddress);
139
+ }
140
+ else if (isRoutableAddress(address)) {
78
141
  this.ensureConversationEntry(this.extractHandle(address), address, "local");
79
142
  }
80
143
  else {
81
144
  this.pendingOutreach.add(address.toLowerCase());
82
145
  }
83
146
  dbg("send contact=%s address=%s status=%s traceId=%s spanName=send", contact, address, ack.status, sendTraceId);
84
- return { status: "sent" };
147
+ return {
148
+ status: "sent",
149
+ messageId: ack.messageId,
150
+ acknowledgementStatus: ack.status,
151
+ ...(messageRef !== undefined && { messageRef }),
152
+ ...(addressedTarget !== undefined && { addressedTarget }),
153
+ };
85
154
  }
86
155
  catch (err) {
156
+ if (err instanceof SendOutcomeUnknownError) {
157
+ dbg("send outcome unknown contact=%s messageId=%s reason=%s", contact, err.messageId, err.reason);
158
+ return {
159
+ status: "unknown",
160
+ messageId: err.messageId,
161
+ attempts: err.attempts,
162
+ retryable: err.retryable,
163
+ reason: err.reason,
164
+ error: err.message,
165
+ errorCode: err.code,
166
+ };
167
+ }
87
168
  if (err instanceof ConnectorError) {
88
169
  const message = formatErrorMessage(err.code, err.to ?? contact);
89
170
  dbg("send failed contact=%s code=%s message=%s", contact, err.code, message);
@@ -98,8 +179,16 @@ export class ConversationManager {
98
179
  this.ensureConversationEntry(contact, address, "remote");
99
180
  dbg("registerInbound contact=%s address=%s", contact, address);
100
181
  }
182
+ refreshInboundPortrayal(contact, address) {
183
+ const entry = this.conversations.get(this.resolveConversationAlias(address));
184
+ if (!entry)
185
+ return false;
186
+ entry.contact = contact;
187
+ dbg("refreshInboundPortrayal contact=%s address=%s", contact, address);
188
+ return true;
189
+ }
101
190
  recordInboundEnvelope(address, envelope) {
102
- const entry = this.conversations.get(address);
191
+ const entry = this.conversations.get(this.resolveConversationAlias(address));
103
192
  if (!entry) {
104
193
  dbg("recordInboundEnvelope no entry, skipped address=%s", address);
105
194
  return;
@@ -108,7 +197,7 @@ export class ConversationManager {
108
197
  dbg("recordInboundEnvelope address=%s messageId=%s correlationId=%s", address, envelope.messageId ?? "(absent)", envelope.correlationId ?? "(absent)");
109
198
  }
110
199
  getContactByAddress(address) {
111
- const entry = this.conversations.get(address);
200
+ const entry = this.conversations.get(this.resolveConversationAlias(address));
112
201
  if (entry)
113
202
  return entry.contact;
114
203
  for (const [, e] of this.conversations) {
@@ -155,6 +244,7 @@ export class ConversationManager {
155
244
  for (const [address, entry] of this.conversations) {
156
245
  if (entry.ended) {
157
246
  this.conversations.delete(address);
247
+ this.deleteConversationAliases(address);
158
248
  count++;
159
249
  }
160
250
  }
@@ -167,7 +257,10 @@ export class ConversationManager {
167
257
  }
168
258
  resolveContactForSend(contact) {
169
259
  if (isRoutableAddress(contact)) {
170
- return { address: contact, ambiguous: false };
260
+ return {
261
+ address: this.resolveConversationAlias(contact),
262
+ ambiguous: false,
263
+ };
171
264
  }
172
265
  const normalizedContact = normalizeHandleContact(contact);
173
266
  if (this.conversations.has(normalizedContact)) {
@@ -194,22 +287,77 @@ export class ConversationManager {
194
287
  extractHandle(address) {
195
288
  return extractHandleFromAddress(address);
196
289
  }
290
+ resolveConversationAlias(address) {
291
+ return this.conversationAliases.get(address) ?? address;
292
+ }
293
+ recordConversationAlias(aliasAddress, canonicalAddress) {
294
+ if (aliasAddress === canonicalAddress)
295
+ return;
296
+ this.conversationAliases.delete(canonicalAddress);
297
+ for (const [alias, target] of this.conversationAliases) {
298
+ if (target === aliasAddress) {
299
+ this.conversationAliases.set(alias, canonicalAddress);
300
+ }
301
+ }
302
+ this.conversationAliases.set(aliasAddress, canonicalAddress);
303
+ }
304
+ deleteConversationAliases(canonicalAddress) {
305
+ for (const [alias, target] of this.conversationAliases) {
306
+ if (target === canonicalAddress)
307
+ this.conversationAliases.delete(alias);
308
+ }
309
+ }
310
+ fileResolvedConversation(sentAddress, resolvedAddress) {
311
+ const prior = sentAddress !== resolvedAddress && isRoutableAddress(sentAddress)
312
+ ? this.conversations.get(sentAddress)
313
+ : undefined;
314
+ const canonical = this.conversations.get(resolvedAddress);
315
+ if (prior)
316
+ this.conversations.delete(sentAddress);
317
+ if (sentAddress !== resolvedAddress && isRoutableAddress(sentAddress)) {
318
+ this.recordConversationAlias(sentAddress, resolvedAddress);
319
+ }
320
+ if (!canonical && prior) {
321
+ this.conversations.set(resolvedAddress, {
322
+ ...prior,
323
+ address: resolvedAddress,
324
+ lastMessageAt: Date.now(),
325
+ ended: false,
326
+ });
327
+ return;
328
+ }
329
+ if (canonical) {
330
+ canonical.lastMessageAt = Date.now();
331
+ canonical.ended = false;
332
+ if (prior?.initiatedBy === "local")
333
+ canonical.initiatedBy = "local";
334
+ if (prior?.inboundEnvelope &&
335
+ (!canonical.inboundEnvelope ||
336
+ prior.inboundEnvelope.receivedAt >
337
+ canonical.inboundEnvelope.receivedAt)) {
338
+ canonical.inboundEnvelope = prior.inboundEnvelope;
339
+ }
340
+ return;
341
+ }
342
+ this.ensureConversationEntry(this.extractHandle(resolvedAddress), resolvedAddress, "local");
343
+ }
197
344
  ensureConversationEntry(contact, address, initiatedBy) {
198
- if (!this.conversations.has(address)) {
199
- const outreachKey = extractHandleFromAddress(address).toLowerCase();
345
+ const canonicalAddress = this.resolveConversationAlias(address);
346
+ if (!this.conversations.has(canonicalAddress)) {
347
+ const outreachKey = extractHandleFromAddress(canonicalAddress).toLowerCase();
200
348
  let origin = initiatedBy;
201
349
  if (origin === "remote" && this.pendingOutreach.delete(outreachKey)) {
202
350
  origin = "local";
203
351
  }
204
- this.conversations.set(address, {
352
+ this.conversations.set(canonicalAddress, {
205
353
  contact,
206
- address,
354
+ address: canonicalAddress,
207
355
  lastMessageAt: Date.now(),
208
356
  initiatedBy: origin,
209
357
  });
210
358
  }
211
359
  else {
212
- const entry = this.conversations.get(address);
360
+ const entry = this.conversations.get(canonicalAddress);
213
361
  if (entry) {
214
362
  entry.lastMessageAt = Date.now();
215
363
  entry.ended = false;
@@ -218,5 +366,6 @@ export class ConversationManager {
218
366
  }
219
367
  _resetForTesting() {
220
368
  this.conversations.clear();
369
+ this.conversationAliases.clear();
221
370
  }
222
371
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { AUTHORIZED_TOKEN_TTL_MS, SETUP_CODE_CHARSET, SETUP_CODE_LENGTH, SETUP_CODE_TTL_MS, } from "./constants.js";
2
2
  export type { AgentNetworkAccount } from "./config-schema.js";
3
- export type { AddressedMessageEvent, DeliveryPendingEvent, DeliverySkipEvent, DeliveryStatusEvent, RegisterAckEvent, SendAckEvent, StructuredErrorEvent, } from "./types.js";
3
+ export type { AddressedMessageEvent, DeliveryPendingEvent, DeliverySkipEvent, DeliveryStatusEvent, ObservedSendAckEvent, RegisterAckEvent, SendAckEvent, StructuredErrorEvent, } from "./types.js";
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AAIxB,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,EACZ,oBAAoB,GACrB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AAIxB,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,EACZ,oBAAoB,GACrB,MAAM,YAAY,CAAC"}
@@ -87,4 +87,5 @@ export declare function acceptRequest(cfg: PlatformClientConfig, runtimeKey: str
87
87
  export declare function declineRequest(cfg: PlatformClientConfig, runtimeKey: string, requestId: string): Promise<DeclineRequestResponse>;
88
88
  export declare function updateProfile(cfg: PlatformClientConfig, runtimeKey: string, params: UpdateProfileParams): Promise<UpdateProfileResponse>;
89
89
  export declare function listConnections(cfg: PlatformClientConfig, runtimeKey: string): Promise<ListConnectionsResponse>;
90
+ export declare function getIdentity(cfg: PlatformClientConfig, runtimeKey: string): Promise<ConnectionNodeRef | null>;
90
91
  //# sourceMappingURL=platform-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"platform-client.d.ts","sourceRoot":"","sources":["../src/platform-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"platform-client.d.ts","sourceRoot":"","sources":["../src/platform-client.ts"],"names":[],"mappings":"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"}
@@ -144,3 +144,15 @@ export async function listConnections(cfg, runtimeKey) {
144
144
  return handleError(res);
145
145
  return (await res.json());
146
146
  }
147
+ export async function getIdentity(cfg, runtimeKey) {
148
+ const res = await apiFetch(`${baseUrl(cfg)}/me/identity`, {
149
+ headers: { Authorization: `Bearer ${runtimeKey}` },
150
+ });
151
+ if (!res.ok)
152
+ return handleError(res);
153
+ const body = (await res.json().catch(() => null));
154
+ if (!body || body.node === undefined) {
155
+ throw new PlatformApiError(res.status, "malformed_response", "identity response carried no `node` field");
156
+ }
157
+ return body.node;
158
+ }
package/dist/plugin.d.ts CHANGED
@@ -1,5 +1,6 @@
1
+ import type { ConversationManager } from "./conversation-manager.js";
1
2
  import { evaluateServicesRetainedToolPolicy } from "./services-retained-tool-policy.js";
2
- export declare function buildInteractionContext(channelId: string | undefined, turnSender: string | null, turnSenderAddress: string | null, servicesRetained?: boolean): string | undefined;
3
+ export declare function buildInteractionContext(messageProvider: string | undefined, turnSenderAddress: string | null, conversationManager?: ConversationManager | null): string | undefined;
3
4
  interface OpenClawPluginApi {
4
5
  runtime: unknown;
5
6
  registerChannel(opts: {
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAuBA,OAAO,EACL,kCAAkC,EAEnC,MAAM,oCAAoC,CAAC;AAgE5C,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,gBAAgB,UAAQ,GACvB,MAAM,GAAG,SAAS,CA+EpB;AAKD,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,YAAY,CACV,IAAI,EAAE,OAAO,EACb,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3C,IAAI,CAAC;IACR,yBAAyB,CAAC,MAAM,EAAE;QAChC,EAAE,EAAE,MAAM,CAAC;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,OAAO,kCAAkC,CAAC;KACrD,GAAG,IAAI,CAAC;IACT,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC;CACnE;AAED,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAaI,iBAAiB;CAiZhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAUrE,OAAO,EACL,kCAAkC,EAEnC,MAAM,oCAAoC,CAAC;AA8D5C,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,mBAAmB,GAAE,mBAAmB,GAAG,IAA+B,GACzE,MAAM,GAAG,SAAS,CA6EpB;AAKD,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,YAAY,CACV,IAAI,EAAE,OAAO,EACb,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3C,IAAI,CAAC;IACR,yBAAyB,CAAC,MAAM,EAAE;QAChC,EAAE,EAAE,MAAM,CAAC;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,OAAO,kCAAkC,CAAC;KACrD,GAAG,IAAI,CAAC;IACT,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC;CACnE;AAqCD,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAaI,iBAAiB;CA8ZhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/plugin.js CHANGED
@@ -2,14 +2,14 @@ import pluginManifest from "../openclaw.plugin.json" with { type: "json" };
2
2
  import { agentNetworkChannel, consumeIdentityLinkingNudge } from "./channel.js";
3
3
  import { login } from "./cli-setup.js";
4
4
  import { detectPendingState, getConversationManager, getDmScope, getStateCacheGeneration, initPluginRuntime, } from "./config.js";
5
+ import { getInvocationConversationManager } from "./conversation-manager-context.js";
5
6
  import { getAgentIdentity } from "./environment-context.js";
6
7
  import { ownerNotesQueue } from "./owner-notes.js";
7
8
  import { consumeCurrentTurnIsOwner, consumeCurrentTurnOwnerSignal, } from "./owner-session-state.js";
8
- import { sentMessageBuffer } from "./sent-message-buffer.js";
9
9
  import { isServicesRetainedReplyContext } from "./services-retained-reply-context.js";
10
10
  import { evaluateServicesRetainedToolPolicy, SERVICES_RETAINED_TOOL_POLICY_ID, } from "./services-retained-tool-policy.js";
11
11
  import { registerTools } from "./tools.js";
12
- import { consumeCurrentTurnSender, consumeCurrentTurnSenderAddress, setCurrentTurnChannelId, setCurrentTurnIsOwnerForTools, setCurrentTurnOwnerSignalForTools, } from "./turn-context.js";
12
+ import { setCurrentTurnChannelId, setCurrentTurnIsOwnerForTools, setCurrentTurnOwnerSignalForTools, } from "./turn-context.js";
13
13
  import { getUpdateInfo } from "./update-check.js";
14
14
  import { PLUGIN_VERSION } from "./version.js";
15
15
  function formatTimeAgo(timestamp) {
@@ -40,8 +40,8 @@ function drainAndFormatNotes(reportInstruction) {
40
40
  reportInstruction);
41
41
  }
42
42
  const MAX_INTERACTIONS = 5;
43
- export function buildInteractionContext(channelId, turnSender, turnSenderAddress, servicesRetained = false) {
44
- const cm = getConversationManager();
43
+ export function buildInteractionContext(messageProvider, turnSenderAddress, conversationManager = getConversationManager()) {
44
+ const cm = conversationManager;
45
45
  if (!cm)
46
46
  return undefined;
47
47
  const conversations = cm.listConversations();
@@ -60,22 +60,18 @@ export function buildInteractionContext(channelId, turnSender, turnSenderAddress
60
60
  const when = formatTimeAgo(c.lastMessageAt);
61
61
  return `• ${who} — ${initiated} — last activity ${when}`;
62
62
  });
63
- const isNetworkTurn = channelId === "agent-network";
64
- if (isNetworkTurn && turnSender) {
65
- const senderConvo = turnSenderAddress
66
- ? conversations.find((c) => c.address === turnSenderAddress)
67
- : undefined;
63
+ const isNetworkTurn = messageProvider === "agent-network";
64
+ if (isNetworkTurn && turnSenderAddress) {
65
+ const senderConvo = conversations.find((c) => c.address === turnSenderAddress);
68
66
  const initiated = senderConvo?.initiatedBy === "local"
69
67
  ? "You initiated this interaction."
70
68
  : "They initiated this interaction.";
71
- const from = turnSenderAddress && turnSenderAddress !== turnSender
72
- ? `${turnSender} (${turnSenderAddress})`
73
- : turnSender;
69
+ const from = senderConvo && senderConvo.contact !== turnSenderAddress
70
+ ? `${senderConvo.contact} (${turnSenderAddress})`
71
+ : turnSenderAddress;
74
72
  return (`${header}\n${lines.join("\n")}\n` +
75
73
  `[This turn] From ${from}. ${initiated}\n` +
76
- (servicesRetained
77
- ? "Your text reply uses the Services-retained reply authority for this message. "
78
- : "Your text reply goes to this sender. ") +
74
+ "Your text reply goes to this sender. " +
79
75
  "To report to your owner: masons_note_for_owner.");
80
76
  }
81
77
  if (isNetworkTurn) {
@@ -88,6 +84,24 @@ export function buildInteractionContext(channelId, turnSender, turnSenderAddress
88
84
  : "[Active interactions on the network]";
89
85
  return `${ownerHeader}\n${lines.join("\n")}`;
90
86
  }
87
+ function resolveHookProvider(ctx) {
88
+ return ctx?.messageProvider ?? ctx?.channel;
89
+ }
90
+ function resolveHookSender(ctx, servicesRetained) {
91
+ if (servicesRetained || resolveHookProvider(ctx) !== "agent-network") {
92
+ return null;
93
+ }
94
+ const senderId = ctx?.senderId;
95
+ return typeof senderId === "string" && senderId.length > 0 ? senderId : null;
96
+ }
97
+ function renderPeer(address, conversationManager) {
98
+ const conversation = conversationManager
99
+ .listConversations()
100
+ .find((entry) => entry.address === address);
101
+ return conversation && conversation.contact !== address
102
+ ? `${conversation.contact} (${address})`
103
+ : address;
104
+ }
91
105
  const plugin = {
92
106
  id: "agent-network",
93
107
  version: PLUGIN_VERSION,
@@ -130,7 +144,8 @@ const plugin = {
130
144
  "masons_note_for_owner(content, from?) — save a note for your owner's next turn. " +
131
145
  "Other network tools: masons_list_requests, masons_send_connection_request, " +
132
146
  "masons_accept_request, masons_decline_request, masons_list_connections, " +
133
- "masons_end_conversation (rare one-shot close signal only). " +
147
+ "masons_end_conversation (rare one-shot close signal only), " +
148
+ "masons_status (your canonical identity and participation status). " +
134
149
  "Always try these tools first for network operations. " +
135
150
  "If a tool call fails, report the error to your user — do not silently work around it. " +
136
151
  "You interact with multiple entities simultaneously — your owner and agents on the network. " +
@@ -147,13 +162,15 @@ const plugin = {
147
162
  "";
148
163
  api.on("before_prompt_build", async (_event, ctx) => {
149
164
  const hookCtx = ctx;
150
- const channelId = hookCtx?.channelId;
165
+ const messageProvider = resolveHookProvider(hookCtx);
166
+ const invocationConversationManager = getInvocationConversationManager();
151
167
  const turnIsOwner = consumeCurrentTurnIsOwner();
152
168
  const turnOwnerSignal = consumeCurrentTurnOwnerSignal();
153
- const turnSender = consumeCurrentTurnSender();
154
- const turnSenderAddress = consumeCurrentTurnSenderAddress();
155
169
  const servicesRetainedReply = isServicesRetainedReplyContext();
156
- setCurrentTurnChannelId(channelId ?? null);
170
+ const turnSenderAddress = invocationConversationManager
171
+ ? resolveHookSender(hookCtx, servicesRetainedReply)
172
+ : null;
173
+ setCurrentTurnChannelId(messageProvider ?? null);
157
174
  setCurrentTurnIsOwnerForTools(turnIsOwner);
158
175
  setCurrentTurnOwnerSignalForTools(turnOwnerSignal);
159
176
  const currentGeneration = getStateCacheGeneration();
@@ -203,7 +220,7 @@ const plugin = {
203
220
  state.hasCredentials &&
204
221
  !state.needsProfile &&
205
222
  !state.pendingTarget) {
206
- if (channelId === "agent-network") {
223
+ if (messageProvider === "agent-network") {
207
224
  if (turnIsOwner) {
208
225
  dynamicContext =
209
226
  "[Agent Network — Owner via Passport] Your owner (Principal) is talking to you " +
@@ -226,12 +243,12 @@ const plugin = {
226
243
  "on the agent network. Your text reply will be sent to that sender — " +
227
244
  "your owner (Principal) will NOT see it. To report something to your owner, " +
228
245
  "call masons_note_for_owner.";
229
- const interactionCtx = buildInteractionContext(channelId, turnSender, turnSenderAddress, servicesRetainedReply);
246
+ const interactionCtx = buildInteractionContext(messageProvider, turnSenderAddress, invocationConversationManager);
230
247
  if (interactionCtx) {
231
248
  dynamicContext += `\n\n${interactionCtx}`;
232
249
  }
233
- if (turnSender) {
234
- const sentMsgs = sentMessageBuffer.getRecent(turnSender);
250
+ if (turnSenderAddress && invocationConversationManager) {
251
+ const sentMsgs = invocationConversationManager?.getRecentOutboundContext(turnSenderAddress) ?? [];
235
252
  if (sentMsgs.length > 0) {
236
253
  const lines = sentMsgs.map((m) => {
237
254
  const preview = m.content.length > 500
@@ -240,7 +257,7 @@ const plugin = {
240
257
  return `• (${formatTimeAgo(m.timestamp)}): "${preview}"`;
241
258
  });
242
259
  dynamicContext +=
243
- `\n\n[Your recent messages to ${turnSender}]\n` +
260
+ `\n\n[Your recent messages to ${renderPeer(turnSenderAddress, invocationConversationManager)}]\n` +
244
261
  lines.join("\n");
245
262
  }
246
263
  }
@@ -261,7 +278,7 @@ const plugin = {
261
278
  ? `${dynamicContext}\n\n${nudge}`
262
279
  : nudge;
263
280
  }
264
- const interactionCtx = buildInteractionContext(channelId, null, null);
281
+ const interactionCtx = buildInteractionContext(messageProvider, null);
265
282
  if (interactionCtx) {
266
283
  dynamicContext = dynamicContext
267
284
  ? `${dynamicContext}\n\n${interactionCtx}`
@@ -287,10 +304,12 @@ const plugin = {
287
304
  if (state.hasCredentials) {
288
305
  const envAgent = getAgentIdentity();
289
306
  if (envAgent) {
290
- let identityLine = `[Identity] You are @${envAgent.handle}`;
291
- if (envAgent.name)
292
- identityLine += ` (${envAgent.name})`;
293
- identityLine += " on the agent network.";
307
+ const registered = envAgent.name
308
+ ? `@${envAgent.handle}(${envAgent.name})`
309
+ : `@${envAgent.handle}`;
310
+ const identityLine = `[Identity] The Gateway registered this connection as ${registered}. ` +
311
+ "This is connection-level context, not your canonical network identity — " +
312
+ "for the canonical answer, call masons_status.";
294
313
  dynamicContext = dynamicContext
295
314
  ? `${identityLine}\n\n${dynamicContext}`
296
315
  : identityLine;
@@ -7,9 +7,8 @@ export declare class SentMessageBuffer {
7
7
  static readonly MAX_PER_CONTACT = 5;
8
8
  static readonly TTL_MS: number;
9
9
  static readonly MAX_CONTENT_LENGTH = 1000;
10
- record(contact: string, content: string): void;
11
- getRecent(contact: string): SentMessage[];
10
+ record(peerAddress: string, content: string): void;
11
+ getRecent(peerAddress: string): SentMessage[];
12
12
  clear(): void;
13
13
  }
14
- export declare const sentMessageBuffer: SentMessageBuffer;
15
14
  //# sourceMappingURL=sent-message-buffer.d.ts.map
@@ -1 +1 @@
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
+ {"version":3,"file":"sent-message-buffer.d.ts","sourceRoot":"","sources":["../src/sent-message-buffer.ts"],"names":[],"mappings":"AAsBA,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,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAgClD,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,EAAE;IAsB7C,KAAK,IAAI,IAAI;CAGd"}