@masons/agent-network 0.4.6 → 0.4.8

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;AAc9D,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;AAqDD,eAAO,MAAM,mBAAmB,EAAE,yBAwWjC,CAAC"}
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,yBAsYjC,CAAC"}
package/dist/channel.js CHANGED
@@ -3,6 +3,7 @@ import createDebug from "debug";
3
3
  import { clearConnectorClient, clearConversationManager, extractNetworkConfig, initConnectorClient, initConversationManager, initToolConfig, requirePluginRuntime, } from "./config.js";
4
4
  import { ConnectorClient } from "./connector-client.js";
5
5
  import { ConversationManager } from "./conversation-manager.js";
6
+ import { clearOwnerSessions, isOwnerSession, markOwnerSession, removeOwnerSession, setCurrentTurnIsOwner, } from "./owner-session-state.js";
6
7
  import { checkForUpdate } from "./update-check.js";
7
8
  const dbg = createDebug("agent-network:channel");
8
9
  // --- Sender identity ---
@@ -10,21 +11,32 @@ const dbg = createDebug("agent-network:channel");
10
11
  // not per-message metadata. See the message_received handler for details.
11
12
  // See: docs/openclaw/research/session-identity-mapping.md §5
12
13
  /**
13
- * Extract a human-readable name from the remote Agent's MSTP address.
14
+ * Extract a human-readable name from the sender's metadata.
14
15
  *
15
- * `metadata.from` contains the address (e.g. `mstps://preview.masons.ai/alice`)
16
- * when the remote Agent connects through a Connector. Falls back to
17
- * "Unknown Agent" for direct MSTP clients without address metadata.
16
+ * Resolution order (account-level identity first, then address):
17
+ * 1. `metadata.visitor_name` display name for Passport browser visitors
18
+ * (resolved by Connector from user_info.displayName)
19
+ * 2. `metadata.from` — MSTP address for agent-to-agent connections
20
+ * (e.g. `mstps://preview.masons.ai/alice` → "alice")
21
+ * 3. Fallback: "Unknown" for direct MSTP clients without metadata
22
+ *
23
+ * Design: Channel determines routing, Account determines identity.
24
+ * See: interop-routing-system-design.md §3 "Channel vs Account".
18
25
  */
19
26
  function deriveSenderName(event) {
27
+ // Account-level identity: Passport visitor with resolved display name
28
+ const visitorName = event.metadata?.visitor_name;
29
+ if (visitorName && typeof visitorName === "string") {
30
+ return visitorName;
31
+ }
32
+ // MSTP address: extract handle from agent address
20
33
  const from = event.metadata?.from;
21
- if (!from || typeof from !== "string") {
22
- return "Unknown Agent";
34
+ if (from && typeof from === "string") {
35
+ const trimmed = from.replace(/\/+$/, "");
36
+ const parts = trimmed.split("/");
37
+ return parts[parts.length - 1] || from;
23
38
  }
24
- // Extract handle from MSTP address: mstps://preview.masons.ai/alice → alice
25
- const trimmed = from.replace(/\/+$/, "");
26
- const parts = trimmed.split("/");
27
- return parts[parts.length - 1] || from;
39
+ return "Unknown";
28
40
  }
29
41
  /**
30
42
  * Extract the Connector host from a connectorUrl.
@@ -133,6 +145,9 @@ export const agentNetworkChannel = {
133
145
  // structured multi-step interactions, add a per-session dispatch queue.
134
146
  client.on("message_received", async (event) => {
135
147
  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.
150
+ setCurrentTurnIsOwner(isOwnerSession(event.sessionId));
136
151
  // --- Runtime guard ---
137
152
  let runtime;
138
153
  try {
@@ -159,13 +174,20 @@ export const agentNetworkChannel = {
159
174
  // senderId for OpenClaw routing: use MSTP address if available,
160
175
  // fall back to sessionId for unknown sessions / direct MSTP clients.
161
176
  const senderId = isRealAddress ? senderAddress : event.sessionId;
162
- // Derive sender name from the MSTP address, or "Unknown Agent" for fallback.
177
+ // Derive sender name.
178
+ // - MSTP agents: extract handle from address.
179
+ // - Passport visitors / direct clients: use the contact name stored
180
+ // in ConversationManager at session_created time (which read
181
+ // visitor_name from session metadata). Per-message metadata does
182
+ // NOT carry session-level fields like visitor_name, so we must
183
+ // use the stored value. Falls back to deriveSenderName → "Unknown".
163
184
  const senderName = isRealAddress
164
185
  ? deriveSenderName({
165
186
  ...event,
166
187
  metadata: { ...event.metadata, from: senderAddress },
167
188
  })
168
- : deriveSenderName(event);
189
+ : (conversationManager.getContactBySessionId(event.sessionId) ??
190
+ deriveSenderName(event));
169
191
  try {
170
192
  // Step 1 — Resolve agent route (await defensively — may be sync or async)
171
193
  const route = await runtime.channel.routing.resolveAgentRoute({
@@ -235,6 +257,13 @@ export const agentNetworkChannel = {
235
257
  client.on("session_created", (event) => {
236
258
  dbg("session created sessionId=%s direction=%s", event.sessionId, event.direction);
237
259
  if (event.direction === "inbound") {
260
+ // --- Owner identity binding (#836) ---
261
+ // Connector sets `is_owner: true` when visitor_user_id === agent.userId.
262
+ // Track this session so message_received can set the per-turn flag.
263
+ if (event.metadata?.is_owner === true) {
264
+ markOwnerSession(event.sessionId);
265
+ dbg("owner session detected sessionId=%s", event.sessionId);
266
+ }
238
267
  const from = event.metadata?.from;
239
268
  const remoteAddress = typeof from === "string" && from.length > 0 ? from : null;
240
269
  if (remoteAddress) {
@@ -242,8 +271,11 @@ export const agentNetworkChannel = {
242
271
  conversationManager.registerInbound(event.sessionId, contact, remoteAddress);
243
272
  }
244
273
  else {
245
- // No address metadata register with sessionId as fallback
246
- conversationManager.registerInbound(event.sessionId, event.sessionId, event.sessionId);
274
+ // No MSTP address — Passport visitor or direct MSTP client.
275
+ // Use visitor_name from session metadata (account-level identity
276
+ // resolved by Connector) if available, else sessionId fallback.
277
+ const contact = deriveSenderName(event);
278
+ conversationManager.registerInbound(event.sessionId, contact, event.sessionId);
247
279
  }
248
280
  }
249
281
  // Outbound sessions are handled by SessionLifecycle internally
@@ -260,6 +292,14 @@ export const agentNetworkChannel = {
260
292
  // --- Session ended + disconnect ---
261
293
  // SessionLifecycle handles session state transitions (session_ended, disconnected)
262
294
  // via its own event subscriptions. No CM calls needed here.
295
+ // Owner session cleanup (#836): remove from ownerSessionIds on session_ended.
296
+ client.on("session_ended", (event) => {
297
+ removeOwnerSession(event.sessionId);
298
+ });
299
+ // On disconnect, all sessions are invalid — clear owner state.
300
+ client.on("disconnected", () => {
301
+ clearOwnerSessions();
302
+ });
263
303
  // --- Error handling ---
264
304
  // Must register before connect() — unhandled "error" events crash the process.
265
305
  client.on("error", (err) => {
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Owner Session State — tracks which MSTP sessions belong to the agent's owner.
3
+ *
4
+ * When the Connector detects that a Passport visitor is the agent's owner
5
+ * (deterministic userId comparison, #836), it sets `is_owner: true` in the
6
+ * SESSION_CREATED metadata. This module maintains the Plugin-side state:
7
+ *
8
+ * - `ownerSessionIds`: Set of session IDs where the visitor is the owner.
9
+ * Populated on session_created, cleaned up on session_ended / disconnected.
10
+ *
11
+ * - `currentTurnIsOwner`: Per-turn flag set in the message_received handler
12
+ * and read in before_prompt_build. Reset after each read to avoid stale state.
13
+ * Race condition note (from design review): if two messages arrive on
14
+ * different sessions concurrently, the flag may reflect the wrong session.
15
+ * Fail-safe: owner gets the routing warning (same as today's behavior).
16
+ * Acceptable for Phase 1 — OpenClaw processes turns sequentially per agent.
17
+ *
18
+ * Module-level singleton — shared between channel.ts (write) and plugin.ts (read).
19
+ * Same pattern as owner-notes.ts.
20
+ */
21
+ /** Mark a session as belonging to the owner. */
22
+ export declare function markOwnerSession(sessionId: string): void;
23
+ /** Remove a session from owner tracking (session ended or disconnected). */
24
+ export declare function removeOwnerSession(sessionId: string): void;
25
+ /** Clear all owner sessions (connection lost — all sessions are invalid). */
26
+ export declare function clearOwnerSessions(): void;
27
+ /** Check if a session belongs to the owner. */
28
+ export declare function isOwnerSession(sessionId: string): boolean;
29
+ /**
30
+ * Set the per-turn owner flag. Called in message_received before dispatch.
31
+ * The flag is consumed (read + reset) by before_prompt_build.
32
+ */
33
+ export declare function setCurrentTurnIsOwner(value: boolean): void;
34
+ /**
35
+ * Consume the per-turn owner flag. Returns the value and resets to false.
36
+ * Consuming prevents stale state from leaking into subsequent turns.
37
+ */
38
+ export declare function consumeCurrentTurnIsOwner(): boolean;
39
+ //# sourceMappingURL=owner-session-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owner-session-state.d.ts","sourceRoot":"","sources":["../src/owner-session-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAgBH,gDAAgD;AAChD,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAExD;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAE1D;AAED,6EAA6E;AAC7E,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC;AAED,+CAA+C;AAC/C,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE1D;AAMD;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,OAAO,CAInD"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Owner Session State — tracks which MSTP sessions belong to the agent's owner.
3
+ *
4
+ * When the Connector detects that a Passport visitor is the agent's owner
5
+ * (deterministic userId comparison, #836), it sets `is_owner: true` in the
6
+ * SESSION_CREATED metadata. This module maintains the Plugin-side state:
7
+ *
8
+ * - `ownerSessionIds`: Set of session IDs where the visitor is the owner.
9
+ * Populated on session_created, cleaned up on session_ended / disconnected.
10
+ *
11
+ * - `currentTurnIsOwner`: Per-turn flag set in the message_received handler
12
+ * and read in before_prompt_build. Reset after each read to avoid stale state.
13
+ * Race condition note (from design review): if two messages arrive on
14
+ * different sessions concurrently, the flag may reflect the wrong session.
15
+ * Fail-safe: owner gets the routing warning (same as today's behavior).
16
+ * Acceptable for Phase 1 — OpenClaw processes turns sequentially per agent.
17
+ *
18
+ * Module-level singleton — shared between channel.ts (write) and plugin.ts (read).
19
+ * Same pattern as owner-notes.ts.
20
+ */
21
+ // ---------------------------------------------------------------------------
22
+ // State
23
+ // ---------------------------------------------------------------------------
24
+ /** Session IDs where the visitor has been identified as the agent's owner. */
25
+ const ownerSessionIds = new Set();
26
+ /** Whether the current LLM turn was triggered by an owner session. */
27
+ let currentTurnIsOwner = false;
28
+ // ---------------------------------------------------------------------------
29
+ // Write API (called from channel.ts)
30
+ // ---------------------------------------------------------------------------
31
+ /** Mark a session as belonging to the owner. */
32
+ export function markOwnerSession(sessionId) {
33
+ ownerSessionIds.add(sessionId);
34
+ }
35
+ /** Remove a session from owner tracking (session ended or disconnected). */
36
+ export function removeOwnerSession(sessionId) {
37
+ ownerSessionIds.delete(sessionId);
38
+ }
39
+ /** Clear all owner sessions (connection lost — all sessions are invalid). */
40
+ export function clearOwnerSessions() {
41
+ ownerSessionIds.clear();
42
+ currentTurnIsOwner = false;
43
+ }
44
+ /** Check if a session belongs to the owner. */
45
+ export function isOwnerSession(sessionId) {
46
+ return ownerSessionIds.has(sessionId);
47
+ }
48
+ /**
49
+ * Set the per-turn owner flag. Called in message_received before dispatch.
50
+ * The flag is consumed (read + reset) by before_prompt_build.
51
+ */
52
+ export function setCurrentTurnIsOwner(value) {
53
+ currentTurnIsOwner = value;
54
+ }
55
+ // ---------------------------------------------------------------------------
56
+ // Read API (called from plugin.ts)
57
+ // ---------------------------------------------------------------------------
58
+ /**
59
+ * Consume the per-turn owner flag. Returns the value and resets to false.
60
+ * Consuming prevents stale state from leaking into subsequent turns.
61
+ */
62
+ export function consumeCurrentTurnIsOwner() {
63
+ const value = currentTurnIsOwner;
64
+ currentTurnIsOwner = false;
65
+ return value;
66
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAkCA,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;CAiNhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
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"}
package/dist/plugin.js CHANGED
@@ -5,6 +5,7 @@ import { agentNetworkChannel } from "./channel.js";
5
5
  import { configureInteractive } from "./cli-setup.js";
6
6
  import { detectPendingState, getStateCacheGeneration, initPluginRuntime, } from "./config.js";
7
7
  import { ownerNotesQueue } from "./owner-notes.js";
8
+ import { consumeCurrentTurnIsOwner } from "./owner-session-state.js";
8
9
  import { registerTools } from "./tools.js";
9
10
  import { getUpdateInfo } from "./update-check.js";
10
11
  import { PLUGIN_VERSION } from "./version.js";
@@ -173,13 +174,46 @@ const plugin = {
173
174
  !state.needsProfile &&
174
175
  !state.pendingTarget) {
175
176
  if (channelId === "agent-network") {
176
- // Turn triggered by a remote agent — warn about routing.
177
- // The LLM's text reply will go to that agent via deliver callback.
178
- dynamicContext =
179
- "[Agent Network Routing] This turn was triggered by a message " +
180
- "on the agent network. Your text reply will be sent to that sender — " +
181
- "your owner (Principal) will NOT see it. To report something to your owner, " +
182
- "call masons_note_for_owner.";
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
+ if (turnIsOwner) {
182
+ // Owner visiting via Passport no routing warning needed.
183
+ // The LLM's text reply goes to the owner (the visitor IS the owner).
184
+ // Drain pending notes: owner is here, deliver them directly.
185
+ const notes = ownerNotesQueue.drain();
186
+ const notesBlock = notes.length > 0
187
+ ? "\n\nPending updates from the agent network:\n" +
188
+ notes
189
+ .map((n) => {
190
+ const fromStr = n.from ? `@${n.from}` : "agent";
191
+ const timeStr = formatTimeAgo(n.timestamp);
192
+ const preview = n.content.length > 100
193
+ ? `${n.content.slice(0, 100)}...`
194
+ : n.content;
195
+ return `• ${fromStr} (${timeStr}): "${preview}"`;
196
+ })
197
+ .join("\n")
198
+ : "";
199
+ dynamicContext =
200
+ "[Agent Network — Owner via Passport] Your owner (Principal) is talking to you " +
201
+ "via Passport (your public web chat page where visitors can reach you). " +
202
+ "This IS your owner — respond naturally, no need to ask who they are. " +
203
+ "Your text reply goes directly to your owner. " +
204
+ "You can share information freely — no information boundary applies. " +
205
+ "Do NOT use masons_note_for_owner — your owner is right here." +
206
+ notesBlock;
207
+ }
208
+ 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.
211
+ dynamicContext =
212
+ "[Agent Network — Routing] This turn was triggered by a message " +
213
+ "on the agent network. Your text reply will be sent to that sender — " +
214
+ "your owner (Principal) will NOT see it. To report something to your owner, " +
215
+ "call masons_note_for_owner.";
216
+ }
183
217
  }
184
218
  else {
185
219
  // Owner turn (or other channel) — drain pending notes if any.
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.6";
2
+ export declare const PLUGIN_VERSION = "0.4.8";
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.6";
2
+ export const PLUGIN_VERSION = "0.4.8";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@masons/agent-network",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
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)",