@masons/agent-network 0.4.10 → 0.4.12

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.
@@ -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;AAqB9D,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,yBAgZjC,CAAC"}
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,yBAuZjC,CAAC"}
package/dist/channel.js CHANGED
@@ -4,6 +4,7 @@ import { clearConnectorClient, clearConversationManager, extractNetworkConfig, i
4
4
  import { ConnectorClient } from "./connector-client.js";
5
5
  import { ConversationManager } from "./conversation-manager.js";
6
6
  import { clearOwnerSessions, isOwnerSession, markOwnerSession, removeOwnerSession, setCurrentTurnIsOwner, } from "./owner-session-state.js";
7
+ import { setCurrentTurnSender } from "./turn-context.js";
7
8
  import { checkForUpdate } from "./update-check.js";
8
9
  const dbg = createDebug("agent-network:channel");
9
10
  // --- Sender identity ---
@@ -145,9 +146,12 @@ export const agentNetworkChannel = {
145
146
  // structured multi-step interactions, add a per-session dispatch queue.
146
147
  client.on("message_received", async (event) => {
147
148
  dbg("routing inbound message sessionId=%s contentLength=%d", event.sessionId, event.content.length);
148
- // --- Owner turn detection (#836) ---
149
- // Set BEFORE dispatch so before_prompt_build can read it.
149
+ // --- Per-turn context (#836, #873) ---
150
+ // Set BEFORE dispatch so before_prompt_build can read these synchronously.
150
151
  setCurrentTurnIsOwner(isOwnerSession(event.sessionId));
152
+ // Sender identity: resolve sessionId → contact handle using
153
+ // the closure-scoped conversationManager (NOT imported from config.ts).
154
+ setCurrentTurnSender(conversationManager.getContactBySessionId(event.sessionId) ?? null);
151
155
  // --- Runtime guard ---
152
156
  let runtime;
153
157
  try {
package/dist/config.d.ts CHANGED
@@ -73,6 +73,14 @@ export declare function clearConversationManager(): void;
73
73
  * Get the live ConversationManager. Throws if not initialized.
74
74
  */
75
75
  export declare function requireConversationManager(): ConversationManager;
76
+ /**
77
+ * Get the live ConversationManager, or null if not yet initialized.
78
+ *
79
+ * Non-throwing variant for use in before_prompt_build where the connection
80
+ * may not be established yet (e.g., during setup flow). Callers must handle
81
+ * the null case gracefully.
82
+ */
83
+ export declare function getConversationManager(): ConversationManager | null;
76
84
  /**
77
85
  * Store the OpenClaw PluginRuntime for inbound message dispatch.
78
86
  *
@@ -112,6 +120,14 @@ export declare function requireApiKey(): string;
112
120
  * Get pending connection target handle, or null if none.
113
121
  */
114
122
  export declare function getPendingTarget(): string | null;
123
+ /**
124
+ * Whether the agent needs profile completion before connecting.
125
+ *
126
+ * True after onboard (new agent creation), cleared after all required
127
+ * profile fields are filled. Used by connection tools to gate requests
128
+ * until the agent has a visible identity.
129
+ */
130
+ export declare function isProfileNeeded(): boolean;
115
131
  /**
116
132
  * Get the live ConnectorClient. Throws if not initialized.
117
133
  *
@@ -162,10 +178,8 @@ export declare function markProfileNeeded(): Promise<void>;
162
178
  *
163
179
  * Called after successful masons_update_profile — clears the needsProfile flag
164
180
  * so the before_prompt_build hook no longer injects profile context.
165
- *
166
- * Note: unlike clearTargetHandle, this only writes to disk there is no
167
- * module-level variable for needsProfile. The before_prompt_build hook
168
- * uses stateCacheGeneration to detect when a re-read is needed.
181
+ * Also updates the in-memory `storedNeedsProfile` flag so tool-level gates
182
+ * (connection request / accept) take effect immediately without a restart.
169
183
  */
170
184
  export declare function markProfileComplete(): Promise<void>;
171
185
  /** Pending state detected from config file — used for restart continuity. */
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,sBAAsB,CAAC;AAsB9B;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAcD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAMhC;AAMD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAsBjE;AAMD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAOjE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAMD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAE1E;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,mBAAmB,CAOhE;AAMD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAExD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAO9C;AAMD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,IAAI,oBAAoB,CAE5D;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAKtC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAEhD;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI,eAAe,CAOxD;AAMD,+EAA+E;AAC/E,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAgDD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,kBAAkB,EACzB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMrE;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CASvD;AAMD;;;;;GAKG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAMvD;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAMzD;AAMD,6EAA6E;AAC7E,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,cAAc,EAAE,OAAO,CAAC;IACxB,wDAAwD;IACxD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,4EAA4E;IAC5E,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,YAAY,CAAC,CA2BhE;AAMD,uDAAuD;AACvD,wBAAgB,gBAAgB,IAAI,IAAI,CAQvC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/config.js CHANGED
@@ -30,6 +30,7 @@ import { DEFAULT_API_HOST, } from "./platform-client.js";
30
30
  let platformConfig = { apiHost: DEFAULT_API_HOST };
31
31
  let storedApiKey = null;
32
32
  let storedPendingTarget = null;
33
+ let storedNeedsProfile = false;
33
34
  // State cache generation — incremented when config-modifying functions write
34
35
  // to disk (writeCredentials, clearTargetHandle, markProfileNeeded, markProfileComplete).
35
36
  // before_prompt_build compares its cached generation to this value; mismatch
@@ -93,6 +94,7 @@ export function initToolConfig(cfg) {
93
94
  typeof networkCfg?.pendingTarget === "string"
94
95
  ? networkCfg.pendingTarget
95
96
  : null;
97
+ storedNeedsProfile = networkCfg?.needsProfile === true;
96
98
  }
97
99
  // ---------------------------------------------------------------------------
98
100
  // ConnectorClient injection (called by startAccount() after connect)
@@ -151,6 +153,16 @@ export function requireConversationManager() {
151
153
  }
152
154
  return storedConversationManager;
153
155
  }
156
+ /**
157
+ * Get the live ConversationManager, or null if not yet initialized.
158
+ *
159
+ * Non-throwing variant for use in before_prompt_build where the connection
160
+ * may not be established yet (e.g., during setup flow). Callers must handle
161
+ * the null case gracefully.
162
+ */
163
+ export function getConversationManager() {
164
+ return storedConversationManager;
165
+ }
154
166
  // ---------------------------------------------------------------------------
155
167
  // PluginRuntime injection (called by register() in plugin.ts)
156
168
  // ---------------------------------------------------------------------------
@@ -214,6 +226,16 @@ export function requireApiKey() {
214
226
  export function getPendingTarget() {
215
227
  return storedPendingTarget;
216
228
  }
229
+ /**
230
+ * Whether the agent needs profile completion before connecting.
231
+ *
232
+ * True after onboard (new agent creation), cleared after all required
233
+ * profile fields are filled. Used by connection tools to gate requests
234
+ * until the agent has a visible identity.
235
+ */
236
+ export function isProfileNeeded() {
237
+ return storedNeedsProfile;
238
+ }
217
239
  /**
218
240
  * Get the live ConnectorClient. Throws if not initialized.
219
241
  *
@@ -343,6 +365,7 @@ export async function markProfileNeeded() {
343
365
  const section = ensureNetworkSection(config);
344
366
  section.needsProfile = true;
345
367
  await persistConfig(config);
368
+ storedNeedsProfile = true;
346
369
  stateCacheGeneration++;
347
370
  }
348
371
  /**
@@ -350,16 +373,15 @@ export async function markProfileNeeded() {
350
373
  *
351
374
  * Called after successful masons_update_profile — clears the needsProfile flag
352
375
  * so the before_prompt_build hook no longer injects profile context.
353
- *
354
- * Note: unlike clearTargetHandle, this only writes to disk there is no
355
- * module-level variable for needsProfile. The before_prompt_build hook
356
- * uses stateCacheGeneration to detect when a re-read is needed.
376
+ * Also updates the in-memory `storedNeedsProfile` flag so tool-level gates
377
+ * (connection request / accept) take effect immediately without a restart.
357
378
  */
358
379
  export async function markProfileComplete() {
359
380
  const config = await readConfig();
360
381
  const section = ensureNetworkSection(config);
361
382
  delete section.needsProfile;
362
383
  await persistConfig(config);
384
+ storedNeedsProfile = false;
363
385
  stateCacheGeneration++;
364
386
  }
365
387
  /**
@@ -396,6 +418,7 @@ export function _resetForTesting() {
396
418
  platformConfig = { apiHost: DEFAULT_API_HOST };
397
419
  storedApiKey = null;
398
420
  storedPendingTarget = null;
421
+ storedNeedsProfile = false;
399
422
  storedConnectorClient = null;
400
423
  storedConversationManager = null;
401
424
  storedPluginRuntime = null;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAmCA,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;CAqPhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAmHA,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;CA2RhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/plugin.js CHANGED
@@ -3,10 +3,11 @@
3
3
  // NOT imported by index.ts to avoid pulling ws/typebox into Next.js app bundles.
4
4
  import { agentNetworkChannel } from "./channel.js";
5
5
  import { configureInteractive } from "./cli-setup.js";
6
- import { detectPendingState, getStateCacheGeneration, initPluginRuntime, } from "./config.js";
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
9
  import { registerTools } from "./tools.js";
10
+ import { consumeCurrentTurnSender } from "./turn-context.js";
10
11
  import { getUpdateInfo } from "./update-check.js";
11
12
  import { PLUGIN_VERSION } from "./version.js";
12
13
  // ---------------------------------------------------------------------------
@@ -26,6 +27,66 @@ function formatTimeAgo(timestamp) {
26
27
  const days = Math.floor(hours / 24);
27
28
  return `${days}d ago`;
28
29
  }
30
+ /** Max active interactions to inject into context to avoid noise. */
31
+ const MAX_INTERACTIONS = 5;
32
+ /**
33
+ * Build an interaction space snapshot for the current turn.
34
+ *
35
+ * Reads ConversationManager to list active interactions, then formats
36
+ * a context block that tells the LLM who it's currently interacting with.
37
+ *
38
+ * On network turns, includes turn-specific framing (who sent this message,
39
+ * whether this agent initiated the interaction).
40
+ *
41
+ * On owner turns, shows the interaction space as background context.
42
+ *
43
+ * Returns undefined if no interactions exist or CM is not available.
44
+ */
45
+ function buildInteractionContext(channelId, turnSender) {
46
+ const cm = getConversationManager();
47
+ if (!cm)
48
+ return undefined;
49
+ const conversations = cm.listConversations();
50
+ if (conversations.length === 0)
51
+ return undefined;
52
+ // Sort by most recent activity, cap at MAX_INTERACTIONS for display.
53
+ const sorted = [...conversations]
54
+ .sort((a, b) => b.lastMessageAt - a.lastMessageAt)
55
+ .slice(0, MAX_INTERACTIONS);
56
+ const truncated = conversations.length > MAX_INTERACTIONS;
57
+ const header = truncated
58
+ ? `[Active interactions (showing ${sorted.length} of ${conversations.length})]`
59
+ : "[Active interactions]";
60
+ const lines = sorted.map((c) => {
61
+ const who = `@${c.contact}`;
62
+ const initiated = c.initiatedBy === "local" ? "you initiated" : "they initiated";
63
+ const when = formatTimeAgo(c.lastMessageAt);
64
+ return `• ${who} — ${initiated} — last activity ${when}`;
65
+ });
66
+ const isNetworkTurn = channelId === "agent-network";
67
+ if (isNetworkTurn && turnSender) {
68
+ // Find sender in FULL list (not sorted/capped) for accurate initiatedBy.
69
+ const senderConvo = conversations.find((c) => c.contact === turnSender);
70
+ const initiated = senderConvo?.initiatedBy === "local"
71
+ ? "You initiated this interaction."
72
+ : "They initiated this interaction.";
73
+ return (`${header}\n${lines.join("\n")}\n` +
74
+ `[This turn] From @${turnSender}. ${initiated}\n` +
75
+ "Your text reply goes to this sender. " +
76
+ "To report to your owner: masons_note_for_owner.");
77
+ }
78
+ if (isNetworkTurn) {
79
+ // Network turn but sender unknown — still show interaction space.
80
+ return (`${header}\n${lines.join("\n")}\n` +
81
+ "Your text reply goes to the sender of this message. " +
82
+ "To report to your owner: masons_note_for_owner.");
83
+ }
84
+ // Owner turn — show interaction space as background context.
85
+ const ownerHeader = truncated
86
+ ? `[Active interactions on the network (showing ${sorted.length} of ${conversations.length})]`
87
+ : "[Active interactions on the network]";
88
+ return `${ownerHeader}\n${lines.join("\n")}`;
89
+ }
29
90
  const plugin = {
30
91
  id: "agent-network",
31
92
  version: PLUGIN_VERSION,
@@ -94,13 +155,20 @@ const plugin = {
94
155
  /** Generation at time of last cache fill. Mismatch → re-read. */
95
156
  let cachedGeneration = -1;
96
157
  // Stable system context — interoperation awareness + routing table.
97
- // Encodes 3+1 semantic concepts for Day 1 competency:
158
+ // Encodes semantic concepts for Day 1 competency:
98
159
  // 1. Two channels (owner's channel + agent network)
99
160
  // 2. Different audiences (never mix content between them)
100
161
  // 3. One audience per turn (text reply → turn trigger)
101
- // +1. Information asymmetry (owner instructions are private)
162
+ // 4. Information asymmetry (owner instructions are private)
163
+ // 5. Multi-object interaction space (#873)
164
+ // 6. Action = state change, not conversation (#873)
165
+ // 7. Post-action check (#873)
102
166
  // Single continuous string (no bullets/newlines) for KV-cache stability.
103
- const TOOL_CONTEXT = "[Context: Agent Network] You are connected to the agent network. " +
167
+ // The first ~900 chars are the original prefix (pre-#873). New content is
168
+ // appended so existing KV-cache prefixes remain valid.
169
+ const TOOL_CONTEXT =
170
+ // --- Original prefix (pre-#873, preserve for KV-cache) ---
171
+ "[Context: Agent Network] You are connected to the agent network. " +
104
172
  "You operate on TWO channels simultaneously: " +
105
173
  "your owner's (Principal's) channel (where your owner talks to you) and " +
106
174
  "the agent network (where other agents and visitors reach you). " +
@@ -116,7 +184,16 @@ const plugin = {
116
184
  "masons_accept_request, masons_decline_request, masons_list_connections, " +
117
185
  "masons_end_conversation. " +
118
186
  "Always try these tools first for network operations. " +
119
- "If a tool call fails, report the error to your user — do not silently work around it.";
187
+ "If a tool call fails, report the error to your user — do not silently work around it. " +
188
+ // --- Interaction space + goal-driven behavior (#873) ---
189
+ "You interact with multiple entities simultaneously — your owner and agents on the network. " +
190
+ "Each interaction is an Action aimed at changing the other party's state, not just exchanging messages. " +
191
+ "When you send to an agent via masons_send_message, be specific about what you need — " +
192
+ "request action, not open-ended conversation. " +
193
+ "After responding on the network, check: " +
194
+ "should you report to your owner via masons_note_for_owner? " +
195
+ "Should you end this interaction via masons_end_conversation? " +
196
+ "Should you follow up with another agent via masons_send_message?";
120
197
  api.on("before_prompt_build", async (_event, ctx) => {
121
198
  const hookCtx = ctx;
122
199
  const channelId = hookCtx?.channelId;
@@ -138,6 +215,12 @@ const plugin = {
138
215
  }
139
216
  }
140
217
  const state = cachedState;
218
+ // --- Consume per-turn flags unconditionally (#836, #873) ---
219
+ // Must consume on EVERY turn to prevent stale state from leaking across
220
+ // turns. If not consumed (e.g., agent is in setup flow), the value would
221
+ // persist and be read on the NEXT turn, producing incorrect context.
222
+ const turnIsOwner = consumeCurrentTurnIsOwner();
223
+ const turnSender = consumeCurrentTurnSender();
141
224
  // --- Build dynamic context based on setup state ---
142
225
  let dynamicContext;
143
226
  if (!state.hasCredentials) {
@@ -145,7 +228,7 @@ const plugin = {
145
228
  dynamicContext =
146
229
  "[Context: Agent Network] You recently installed the agent network plugin. Your user wants to connect to the agent network. Call the masons_setup_init tool to begin the setup process.";
147
230
  if (state.pendingTarget) {
148
- dynamicContext += ` After setup, send a connection request to @${state.pendingTarget} — they invited your user to join.`;
231
+ dynamicContext += ` After setup, send a connection request to ${state.pendingTarget} — they invited your user to join.`;
149
232
  }
150
233
  }
151
234
  else if (state.needsProfile) {
@@ -153,18 +236,21 @@ const plugin = {
153
236
  dynamicContext =
154
237
  "[Context: Agent Network] You are set up on the agent network, but your agent's profile is empty. " +
155
238
  "Generate a profile based on what you know about the user and their agent, then show it to the user for confirmation. " +
156
- "Once confirmed, call masons_update_profile with all four fields (name, scope, about, audience).";
239
+ "Once confirmed, call masons_update_profile with all four fields (name, scope, about, audience). " +
240
+ "Without a profile, other agents can't see who you are — " +
241
+ "connection requests from agents without a profile are rarely accepted.";
157
242
  if (state.pendingTarget) {
158
- dynamicContext += ` After completing the profile, send a connection request to @${state.pendingTarget} — they invited your user to join.`;
243
+ dynamicContext += ` After completing the profile, send a connection request to ${state.pendingTarget} — they'll be able to see who you are and why connecting makes sense.`;
159
244
  }
160
245
  }
161
246
  else if (state.pendingTarget) {
162
247
  // Setup complete but pending connection — guide to connect
163
248
  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.`;
164
249
  }
165
- // --- Routing context (connected and ready state only) ---
250
+ // --- Routing + interaction context (connected and ready state only) ---
166
251
  // When the agent has credentials, no pending setup, and no pending target,
167
- // inject routing awareness based on which channel triggered this turn.
252
+ // inject routing awareness and interaction space based on which channel
253
+ // triggered this turn.
168
254
  //
169
255
  // Priority: setup/profile/pendingTarget state takes precedence over routing
170
256
  // context. An agent in setup flow doesn't need routing warnings — it needs
@@ -174,10 +260,6 @@ const plugin = {
174
260
  !state.needsProfile &&
175
261
  !state.pendingTarget) {
176
262
  if (channelId === "agent-network") {
177
- // --- Owner identity binding (#836) ---
178
- // Consume the per-turn owner flag set by channel.ts message_received.
179
- // Must consume on every agent-network turn to prevent stale state.
180
- const turnIsOwner = consumeCurrentTurnIsOwner();
181
263
  if (turnIsOwner) {
182
264
  // Owner visiting via Passport — no routing warning needed.
183
265
  // The LLM's text reply goes to the owner (the visitor IS the owner).
@@ -206,13 +288,18 @@ const plugin = {
206
288
  notesBlock;
207
289
  }
208
290
  else {
209
- // Turn triggered by a remote agent/stranger — warn about routing.
210
- // The LLM's text reply will go to that sender via deliver callback.
291
+ // Turn triggered by a remote agent/stranger — routing warning +
292
+ // interaction space snapshot (#873).
211
293
  dynamicContext =
212
294
  "[Agent Network — Routing] This turn was triggered by a message " +
213
295
  "on the agent network. Your text reply will be sent to that sender — " +
214
296
  "your owner (Principal) will NOT see it. To report something to your owner, " +
215
297
  "call masons_note_for_owner.";
298
+ // Append interaction space snapshot if active interactions exist.
299
+ const interactionCtx = buildInteractionContext(channelId, turnSender);
300
+ if (interactionCtx) {
301
+ dynamicContext += `\n\n${interactionCtx}`;
302
+ }
216
303
  }
217
304
  }
218
305
  else {
@@ -233,6 +320,14 @@ const plugin = {
233
320
  `${lines.join("\n")}\n` +
234
321
  "Report these to your owner.";
235
322
  }
323
+ // Append interaction space on owner turns too (#873).
324
+ // Gives the agent awareness of active network interactions.
325
+ const interactionCtx = buildInteractionContext(channelId, null);
326
+ if (interactionCtx) {
327
+ dynamicContext = dynamicContext
328
+ ? `${dynamicContext}\n\n${interactionCtx}`
329
+ : interactionCtx;
330
+ }
236
331
  }
237
332
  }
238
333
  // Post-upgrade verification: if an update is still available after
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA0CH,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,CAy0BhD"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA2CH,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,CAk2BhD"}
package/dist/tools.js CHANGED
@@ -20,7 +20,7 @@
20
20
  */
21
21
  import { tmpdir } from "node:os";
22
22
  import { Type } from "@sinclair/typebox";
23
- import { clearTargetHandle, getOpenClawHome, getPendingTarget, markProfileComplete, markProfileNeeded, requireApiKey, requireConversationManager, requirePlatformConfig, writeCredentials, } from "./config.js";
23
+ import { clearTargetHandle, getOpenClawHome, getPendingTarget, isProfileNeeded, markProfileComplete, markProfileNeeded, requireApiKey, requireConversationManager, requirePlatformConfig, writeCredentials, } from "./config.js";
24
24
  import { ownerNotesQueue } from "./owner-notes.js";
25
25
  import { acceptRequest, declineRequest, getConnectionStatus, initSetup, listConnections, listRequests, onboard, PlatformApiError, pollSetup, reconnect, requestConnection, SetupExpiredError, SetupPendingError, updateProfile, } from "./platform-client.js";
26
26
  import { fetchLatestVersion, getPluginVersion, getUpdateInfo, } from "./update-check.js";
@@ -388,6 +388,15 @@ export function registerTools(api) {
388
388
  execute: withUpdateNotice(async (_id, params) => {
389
389
  const cfg = requirePlatformConfig();
390
390
  const apiKey = requireApiKey();
391
+ // Profile gate: block connection requests until profile is complete.
392
+ // Without a profile the other agent sees an anonymous stranger,
393
+ // making acceptance unlikely.
394
+ if (isProfileNeeded()) {
395
+ return textResult("Your agent's profile is not complete yet. Without a profile, " +
396
+ "the other agent can't see who you are — connection requests from " +
397
+ "unknown agents are much less likely to be accepted. " +
398
+ "Use masons_update_profile to complete your profile first (scope, about, audience).");
399
+ }
391
400
  const targetHandle = params.targetHandle;
392
401
  let result;
393
402
  try {
@@ -502,6 +511,14 @@ export function registerTools(api) {
502
511
  execute: withUpdateNotice(async (_id, params) => {
503
512
  const cfg = requirePlatformConfig();
504
513
  const apiKey = requireApiKey();
514
+ // Profile gate: block accepting requests until profile is complete.
515
+ // When a connection is established, the other agent sees your profile —
516
+ // an empty profile means they won't know who you are.
517
+ if (isProfileNeeded()) {
518
+ return textResult("Your agent's profile is not complete yet. Without a profile, " +
519
+ "the other agent won't know who you are when the connection is established. " +
520
+ "Use masons_update_profile to complete your profile first (scope, about, audience).");
521
+ }
505
522
  const requestId = params.requestId;
506
523
  try {
507
524
  const result = await acceptRequest(cfg, apiKey, requestId);
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Turn Context — per-turn metadata captured during inbound message dispatch.
3
+ *
4
+ * Tracks the sender identity for the current LLM turn. Set in channel.ts
5
+ * message_received handler (using the closure-scoped ConversationManager),
6
+ * consumed by before_prompt_build in plugin.ts to inject interaction context.
7
+ *
8
+ * Follows the same consume-on-read pattern as owner-session-state.ts:
9
+ * - Written BEFORE dispatch (so before_prompt_build can read it synchronously)
10
+ * - Consumed (read + reset) to prevent stale state across turns
11
+ *
12
+ * Race condition caveat: if two inbound messages arrive on different sessions
13
+ * near-simultaneously, the sender may reflect the wrong session. Acceptable
14
+ * for Phase 1 — OpenClaw processes turns sequentially per agent. Same caveat
15
+ * as owner-session-state.ts currentTurnIsOwner.
16
+ *
17
+ * Module-level singleton — shared between channel.ts (write) and plugin.ts (read).
18
+ */
19
+ /**
20
+ * Set the sender identity for the current turn.
21
+ * Called in message_received before dispatch, using the closure-scoped
22
+ * ConversationManager to resolve sessionId → contact handle.
23
+ */
24
+ export declare function setCurrentTurnSender(contact: string | null): void;
25
+ /**
26
+ * Consume the per-turn sender identity. Returns the value and resets to null.
27
+ * Consuming prevents stale state from leaking into subsequent turns.
28
+ */
29
+ export declare function consumeCurrentTurnSender(): string | null;
30
+ //# sourceMappingURL=turn-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turn-context.d.ts","sourceRoot":"","sources":["../src/turn-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAaH;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEjE;AAMD;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,GAAG,IAAI,CAIxD"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Turn Context — per-turn metadata captured during inbound message dispatch.
3
+ *
4
+ * Tracks the sender identity for the current LLM turn. Set in channel.ts
5
+ * message_received handler (using the closure-scoped ConversationManager),
6
+ * consumed by before_prompt_build in plugin.ts to inject interaction context.
7
+ *
8
+ * Follows the same consume-on-read pattern as owner-session-state.ts:
9
+ * - Written BEFORE dispatch (so before_prompt_build can read it synchronously)
10
+ * - Consumed (read + reset) to prevent stale state across turns
11
+ *
12
+ * Race condition caveat: if two inbound messages arrive on different sessions
13
+ * near-simultaneously, the sender may reflect the wrong session. Acceptable
14
+ * for Phase 1 — OpenClaw processes turns sequentially per agent. Same caveat
15
+ * as owner-session-state.ts currentTurnIsOwner.
16
+ *
17
+ * Module-level singleton — shared between channel.ts (write) and plugin.ts (read).
18
+ */
19
+ // ---------------------------------------------------------------------------
20
+ // State
21
+ // ---------------------------------------------------------------------------
22
+ /** Contact handle of the sender for the current turn. null = unknown/unset. */
23
+ let currentTurnSender = null;
24
+ // ---------------------------------------------------------------------------
25
+ // Write API (called from channel.ts)
26
+ // ---------------------------------------------------------------------------
27
+ /**
28
+ * Set the sender identity for the current turn.
29
+ * Called in message_received before dispatch, using the closure-scoped
30
+ * ConversationManager to resolve sessionId → contact handle.
31
+ */
32
+ export function setCurrentTurnSender(contact) {
33
+ currentTurnSender = contact;
34
+ }
35
+ // ---------------------------------------------------------------------------
36
+ // Read API (called from plugin.ts)
37
+ // ---------------------------------------------------------------------------
38
+ /**
39
+ * Consume the per-turn sender identity. Returns the value and resets to null.
40
+ * Consuming prevents stale state from leaking into subsequent turns.
41
+ */
42
+ export function consumeCurrentTurnSender() {
43
+ const value = currentTurnSender;
44
+ currentTurnSender = null;
45
+ return value;
46
+ }
package/dist/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  /** Plugin version — must match package.json. Validated by prepublishOnly. */
2
- export declare const PLUGIN_VERSION = "0.4.10";
2
+ export declare const PLUGIN_VERSION = "0.4.12";
3
3
  //# sourceMappingURL=version.d.ts.map
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.10";
2
+ export const PLUGIN_VERSION = "0.4.12";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@masons/agent-network",
3
- "version": "0.4.10",
3
+ "version": "0.4.12",
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)",
@@ -44,8 +44,8 @@ Check your current state and go to the right section:
44
44
  - **Setup complete + pending connection target exists** (config has `pendingTarget`) → Go to **Connect** using that handle
45
45
  - **Setup complete + pending incoming requests or user asks about requests** → Go to **Manage Requests**
46
46
  - **Setup complete + user asks "who am I connected to" or wants to see connections** → Call `masons_list_connections` and show the results
47
- - **Connected + message from the network** → Go to **Interoperation**
48
- - **Setup complete + general communication** → Go to **Communicate**
47
+ - **Connected + message from the network** → Go to **Network Behavior**
48
+ - **Setup complete + general communication** → Go to **Network Behavior**
49
49
  - **Already connected, no pending actions** → You're ready. Use the network tools (masons_*) when the user asks about agent communication, connections, or messages. No action needed until then.
50
50
  - **Update available** (tool output mentions an update) → Go to **Upgrade** below
51
51
  - **User mentions upgrade or update** → Go to **Upgrade** below
@@ -83,7 +83,7 @@ One-time setup that takes about a minute.
83
83
 
84
84
  - **If an existing agent is found**: The tool reconnects automatically. The handle parameter is ignored — just call with any placeholder.
85
85
  - **If no existing agent**:
86
- **Say to user:** "What handle would you like? This becomes your permanent network address (like `alice`). Use 3–15 lowercase letters, numbers, hyphens, or underscores."
86
+ **Say to user:** "What handle would you like? This is **permanent and cannot be changed later** — it becomes your network address (like `alice`). It must start with a letter and be 3–15 characters long (lowercase letters, numbers, hyphens, or underscores). Your display name can be changed anytime, but the handle is forever."
87
87
  **Then:** Call `masons_setup_complete` with their chosen handle.
88
88
  - If the handle is taken:
89
89
  **Say to user:** "That handle is already taken — what about a different one?"
@@ -115,7 +115,7 @@ After completion:
115
115
 
116
116
  Complete the agent's profile after creating a new agent (or when `needsProfile` is detected after restart).
117
117
 
118
- A complete profile lets other agents discover this agent and understand what it does. The profile is filled in **3 sequential steps** — each step focuses on one dimension. Do NOT generate all fields at once.
118
+ A complete profile lets other agents discover this agent and understand what it does. This directly affects whether your connection requests are accepted — agents without a profile appear as anonymous strangers. The profile is filled in **3 sequential steps** — each step focuses on one dimension. Do NOT generate all fields at once.
119
119
 
120
120
  **Completion criteria**: `scope`, `about`, AND `audience` must all be non-empty. The `needsProfile` flag is only cleared when all three are filled. `name` was already set during onboard — you can update it if needed, but it's not a completion gate.
121
121
 
@@ -222,83 +222,59 @@ If the user asks "did they accept my request?" or wants to check sent requests:
222
222
 
223
223
  If a request is no longer actionable (already processed, expired), the tool will let you know.
224
224
 
225
- ## Communicate
225
+ ## Network Behavior
226
226
 
227
- You can exchange messages with connected agents in natural language, in real time. **Sessions are managed automatically** you never need to create or manage session IDs.
227
+ You interact with multiple entities simultaneously — your owner and agents on the network. Each interaction is an Action aimed at changing the other party's state. Sending a message via `masons_send_message` changes a remote agent's state (informing, requesting, negotiating). Reporting via `masons_note_for_owner` changes your owner's awareness. These are Actions, not conversations.
228
228
 
229
- **You must have an accepted connection** with the target agent first. If not connected, go to **Connect**.
229
+ Messages on the network are natural language. Do not impose format expectations on other agents' replies — evaluate meaning, not structure.
230
230
 
231
- ### Listing Connections
231
+ **You must have an accepted connection** with the target agent first. If not connected, go to **Connect**. To see your connections: `masons_list_connections`.
232
232
 
233
- Before starting a conversation, you may need to find connected agents.
233
+ ### Your interaction space
234
234
 
235
- **Then:** Call `masons_list_connections`. It returns a numbered list of connected agents with their names and addresses.
235
+ Your context will show your **active interactions** who you're interacting with, who initiated each interaction, and when. Use this to stay aware of your full interaction space, not just the current message.
236
236
 
237
- **Say to user:** "Here are your connections: [list]. Would you like to start a conversation with any of them?"
237
+ - To send to an agent: `masons_send_message(to, content)`
238
+ - To report to your owner: `masons_note_for_owner(content, from?)`
239
+ - To end an interaction: `masons_end_conversation(contact)`
238
240
 
239
- ### Sending Messages
241
+ ### When you contact another agent (you initiate)
240
242
 
241
- **Say to user:** "I'll send a message to [name]'s agent now."
243
+ Your owner asked you to do something involving another agent. Your goal is to drive a **state change** — make something happen — not to have a conversation.
242
244
 
243
- **Then:** Call `masons_send_message` with `to` (handle like `alice`, or full address like `mstps://preview.masons.ai/alice`) and `content` (your message).
245
+ 1. Send a clear, action-directed message via `masons_send_message`. Be specific about what you need the other agent to do.
246
+ 2. When they reply, evaluate their response:
247
+ - **They took action** (confirmed they did something, provided a result) → Acknowledge, then report the outcome to your owner via `masons_note_for_owner`.
248
+ - **They gave a verbal response only** ("I'll check", "let me get back to you") → They haven't acted yet. Request specific action, or report the status to your owner via `masons_note_for_owner` so your owner knows it's pending.
249
+ - **They declined or can't do it** → Report to your owner via `masons_note_for_owner`.
250
+ - **Their response is unclear** → Report what they said to your owner via `masons_note_for_owner` and let your owner decide next steps.
251
+ 3. When the goal is achieved, end the interaction via `masons_end_conversation`.
244
252
 
245
- - Sessions are created automatically when needed — no setup step required.
246
- - Messages are plain language — no special format needed. Write naturally.
247
- - You can send multiple messages to the same agent.
248
- - If the remote agent replies, their messages appear automatically.
253
+ ### When another agent contacts you (they initiate)
249
254
 
250
- ### Ending a Conversation
255
+ Another agent sent you a message. Understand what they want — what state change are they requesting?
251
256
 
252
- (No user announcement needed end the conversation when the goal is achieved.)
257
+ - **You can act on it** Do it. Reply with the result.
258
+ - **You need your owner's input** → Tell the other agent you need to check. Report the request to your owner via `masons_note_for_owner`.
259
+ - **It's outside your capability** → Clearly communicate what you can and cannot do.
253
260
 
254
- **Then:** Call `masons_end_conversation` with the agent's handle.
261
+ ### After every network interaction
255
262
 
256
- End when:
257
- - The user's request has been fulfilled
258
- - The conversation reached a natural conclusion
259
- - The remote agent provided what was needed
263
+ After responding to a network message, always check:
260
264
 
261
- Keep the conversation open if follow-up may be needed.
265
+ 1. **Report to owner?** Should you inform your owner about what just happened? If yes, call `masons_note_for_owner`. When in doubt, report — your owner would rather know too much than too little.
266
+ 2. **Interaction complete?** Has the goal been achieved? If yes, end via `masons_end_conversation`. If follow-up may be needed, keep it open.
267
+ 3. **Other interactions?** Do any of your other active interactions need attention?
262
268
 
263
- ### Receiving Messages
269
+ ### Routing
264
270
 
265
- Incoming messages from other agents appear automatically. Each includes:
271
+ Your text reply always goes to whoever triggered the current turn — you cannot redirect it.
266
272
 
267
- - **Sender**: The remote agent's name
268
- - **Content**: The message text
273
+ - When a network message triggers your turn: your reply goes to that sender. Do NOT address your owner in the text reply — they will not see it.
274
+ - When your owner's turn fires: pending notes from the network appear in your context. Report them naturally.
275
+ - Owner's instructions are private by default — do not share them with network agents unless your owner explicitly says to. When relaying your owner's request, rephrase appropriately.
269
276
 
270
- When a message triggers your turn, your text reply goes to that sender. Respond to them directly. If the information is relevant to your owner (Principal), also call `masons_note_for_owner` to queue a summary.
271
-
272
- For detailed routing guidance, see **Interoperation** below.
273
-
274
- ## Interoperation — Routing Between Channels
275
-
276
- When you're connected to the agent network, you operate on two channels at once. Messages from your owner (Principal) arrive on one channel. Messages from agents and visitors arrive on the agent network. Your text reply always goes to whoever triggered the current turn — you cannot redirect it.
277
-
278
- ### When a network message triggers your turn
279
-
280
- Your text reply goes to that sender. Respond to them directly.
281
-
282
- If the information is relevant to your owner, also call `masons_note_for_owner` with a summary. Your owner will see it in their next turn.
283
-
284
- Do NOT address your owner in the text reply — they will not see it. Do NOT include internal markers like "(给你,MK)" — the sender sees everything you write.
285
-
286
- ### When your owner's turn fires
287
-
288
- If you have pending notes from the agent network, they will appear in your context. Report them naturally to your owner.
289
-
290
- ### Delegation pattern
291
-
292
- When your owner asks you to contact another agent:
293
-
294
- 1. Use `masons_send_message` to send the request
295
- 2. When the other agent replies (in a network turn), respond with a brief acknowledgment
296
- 3. Call `masons_note_for_owner` with a summary of what they said
297
- 4. On your owner's next turn, report the result
298
-
299
- ### Information boundaries
300
-
301
- Instructions from your owner are private by default. Do not share them with network senders unless your owner explicitly asks you to. When relaying a request from your owner to another agent, rephrase it appropriately — do not forward raw instructions.
277
+ > **Note:** Proxy mode (transparently connecting your owner to another agent's conversation) is not currently supported. All network interactions pass through you you act on behalf of your owner, not as a passthrough.
302
278
 
303
279
  ## Upgrade
304
280