@masons/agent-network 0.5.23 → 0.5.25

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.
@@ -2,6 +2,7 @@ interface StartChannelSetupOptions {
2
2
  apiHost: string;
3
3
  connectorUrl: string;
4
4
  idpBaseUrl: string;
5
+ invitedBy?: string;
5
6
  }
6
7
  export interface ChannelSetupResult {
7
8
  status: "pending" | "completed" | "expired" | "failed";
@@ -1 +1 @@
1
- {"version":3,"file":"channel-setup.d.ts","sourceRoot":"","sources":["../src/channel-setup.ts"],"names":[],"mappings":"AAeA,UAAU,wBAAwB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAgBD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAKD,wBAAgB,6BAA6B,IAAI,wBAAwB,CAMxE;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC,CA8B7B;AAED,wBAAgB,qBAAqB,IAAI,kBAAkB,GAAG,IAAI,CAejE;AAED,wBAAgB,yBAAyB,IAAI,kBAAkB,GAAG,IAAI,CAMrE;AA8DD,wBAAgB,4BAA4B,IAAI,IAAI,CAGnD;AAED,eAAO,MAAM,oBAAoB,QAAqB,CAAC"}
1
+ {"version":3,"file":"channel-setup.d.ts","sourceRoot":"","sources":["../src/channel-setup.ts"],"names":[],"mappings":"AAeA,UAAU,wBAAwB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IAMnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAuBD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAKD,wBAAgB,6BAA6B,IAAI,wBAAwB,CAMxE;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC,CA+B7B;AAED,wBAAgB,qBAAqB,IAAI,kBAAkB,GAAG,IAAI,CAejE;AAED,wBAAgB,yBAAyB,IAAI,kBAAkB,GAAG,IAAI,CAMrE;AAmED,wBAAgB,4BAA4B,IAAI,IAAI,CAGnD;AAED,eAAO,MAAM,oBAAoB,QAAqB,CAAC"}
@@ -29,6 +29,7 @@ export async function startOrGetChannelSetup(options) {
29
29
  apiHost: options.apiHost,
30
30
  connectorUrl: options.connectorUrl,
31
31
  idpBaseUrl: options.idpBaseUrl,
32
+ invitedBy: options.invitedBy,
32
33
  };
33
34
  pendingSetup = record;
34
35
  void completePendingSetup(record);
@@ -85,7 +86,7 @@ async function completePendingSetup(record) {
85
86
  connectorUrl: existingConnectorUrl ?? record.connectorUrl,
86
87
  token: handoff.token,
87
88
  };
88
- await writeCredentials(creds, record.apiHost, record.idpBaseUrl);
89
+ await writeCredentials(creds, record.apiHost, record.idpBaseUrl, record.invitedBy);
89
90
  lastSetupStatus = {
90
91
  status: "completed",
91
92
  agentHandle: handoff.agent.handle,
@@ -1 +1 @@
1
- {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAmC9D,wBAAgB,uBAAuB,IAAI,MAAM,GAAG,IAAI,CAEvD;AAOD,wBAAgB,2BAA2B,IAAI,OAAO,CAIrD;AAGD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE/D;AAUD,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,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,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;AA2DD,iBAAS,iCAAiC,CACxC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA6BzB;AAED,eAAO,MAAM,4CAA4C,0CACtB,CAAC;AAEpC,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;AA+WD,eAAO,MAAM,mBAAmB,EAAE,yBAyNjC,CAAC"}
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAoC9D,wBAAgB,uBAAuB,IAAI,MAAM,GAAG,IAAI,CAEvD;AAOD,wBAAgB,2BAA2B,IAAI,OAAO,CAIrD;AAGD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE/D;AAUD,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,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,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;AA2DD,iBAAS,iCAAiC,CACxC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA6BzB;AAED,eAAO,MAAM,4CAA4C,0CACtB,CAAC;AAEpC,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;AAyYD,eAAO,MAAM,mBAAmB,EAAE,yBAyNjC,CAAC"}
package/dist/channel.js CHANGED
@@ -5,7 +5,7 @@ import { ConnectorClient } from "./connector-client.js";
5
5
  import { ConversationManager } from "./conversation-manager.js";
6
6
  import { clearEnvironmentContext } from "./environment-context.js";
7
7
  import { extractHandleFromAddress } from "./handle-utils.js";
8
- import { clearOwnerState, isOwnerAddress, markOwnerAddress, setCurrentTurnIsOwner, } from "./owner-session-state.js";
8
+ import { clearOwnerState, isOwnerAddress, markOwnerAddress, setCurrentTurnIsOwner, setCurrentTurnOwnerSignal, } from "./owner-session-state.js";
9
9
  import { setCurrentTurnSender } from "./turn-context.js";
10
10
  import { checkForUpdate } from "./update-check.js";
11
11
  const dbg = createDebug("agent-network:channel");
@@ -128,6 +128,7 @@ async function handleAddressedMessage(event, conversationManager, client, ctx) {
128
128
  const nodeRef = tryProjectNodeRef(event.metadata);
129
129
  if (nodeRef) {
130
130
  setCurrentTurnIsOwner(false);
131
+ setCurrentTurnOwnerSignal(null);
131
132
  dbg("Full Node sender: kind=%s mstpAddress=%s handle=%s", nodeRef.kind, nodeRef.mstpAddress, nodeRef.handle ?? "(absent)");
132
133
  }
133
134
  else {
@@ -146,6 +147,15 @@ async function handleAddressedMessage(event, conversationManager, client, ctx) {
146
147
  }
147
148
  }
148
149
  setCurrentTurnIsOwner(isOwnerAddress(fromAddress));
150
+ if (event.metadata?.is_owner === true) {
151
+ setCurrentTurnOwnerSignal("owner");
152
+ }
153
+ else if (event.metadata?.is_owner === false) {
154
+ setCurrentTurnOwnerSignal("non-owner");
155
+ }
156
+ else {
157
+ setCurrentTurnOwnerSignal(null);
158
+ }
149
159
  }
150
160
  const contact = nodeRef
151
161
  ? deriveNodeName(nodeRef, fromAddress)
package/dist/config.d.ts CHANGED
@@ -26,7 +26,7 @@ export interface NetworkCredentials {
26
26
  token: string;
27
27
  }
28
28
  export declare function readExistingConnectorUrl(): Promise<string | undefined>;
29
- export declare function writeCredentials(creds: NetworkCredentials, apiHost?: string, idpBaseUrl?: string): Promise<void>;
29
+ export declare function writeCredentials(creds: NetworkCredentials, apiHost?: string, idpBaseUrl?: string, pendingTarget?: string): Promise<void>;
30
30
  export declare function writeTargetHandle(handle: string): Promise<void>;
31
31
  export declare function clearTargetHandle(): Promise<void>;
32
32
  export declare function markProfileNeeded(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAkBA,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;AAE9B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAoCjD,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAsBD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAMhC;AAYD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAyCjE;AAuCD,wBAAgB,yBAAyB,IAAI,OAAO,CAEnD;AAWD,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAeD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAOjE;AAQD,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAYD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAE1E;AASD,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAKD,wBAAgB,0BAA0B,IAAI,mBAAmB,CAOhE;AASD,wBAAgB,sBAAsB,IAAI,mBAAmB,GAAG,IAAI,CAEnE;AAgBD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAExD;AAKD,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAQD,wBAAgB,oBAAoB,IAAI,OAAO,CAO9C;AAcD,wBAAgB,qBAAqB,IAAI,oBAAoB,CAE5D;AAKD,wBAAgB,aAAa,IAAI,MAAM,CAKtC;AAKD,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAEhD;AASD,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AASD,wBAAgB,sBAAsB,IAAI,eAAe,CAOxD;AAyBD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAQD,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAW5E;AAYD,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,kBAAkB,EACzB,OAAO,CAAC,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAkCf;AAUD,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMrE;AASD,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CASvD;AAYD,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAOvD;AAUD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAOzD;AAwCD,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,CAsBf;AAQD,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4B1E;AAOD,MAAM,WAAW,YAAY;IAE3B,cAAc,EAAE,OAAO,CAAC;IAExB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B,YAAY,EAAE,OAAO,CAAC;CACvB;AASD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,YAAY,CAAC,CA2BhE;AAOD,wBAAgB,gBAAgB,IAAI,IAAI,CAWvC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAkBA,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;AAE9B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAoCjD,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAsBD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAMhC;AAYD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAyCjE;AAuCD,wBAAgB,yBAAyB,IAAI,OAAO,CAEnD;AAWD,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAeD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAOjE;AAQD,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAYD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAE1E;AASD,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAKD,wBAAgB,0BAA0B,IAAI,mBAAmB,CAOhE;AASD,wBAAgB,sBAAsB,IAAI,mBAAmB,GAAG,IAAI,CAEnE;AAgBD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAExD;AAKD,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAQD,wBAAgB,oBAAoB,IAAI,OAAO,CAO9C;AAcD,wBAAgB,qBAAqB,IAAI,oBAAoB,CAE5D;AAKD,wBAAgB,aAAa,IAAI,MAAM,CAKtC;AAKD,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAEhD;AASD,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AASD,wBAAgB,sBAAsB,IAAI,eAAe,CAOxD;AAyBD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAQD,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAW5E;AAmBD,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,kBAAkB,EACzB,OAAO,CAAC,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,EACnB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CA0Cf;AAUD,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMrE;AASD,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CASvD;AAYD,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAOvD;AAUD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAOzD;AAwCD,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,CAsBf;AAQD,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4B1E;AAOD,MAAM,WAAW,YAAY;IAE3B,cAAc,EAAE,OAAO,CAAC;IAExB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B,YAAY,EAAE,OAAO,CAAC;CACvB;AASD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,YAAY,CAAC,CA2BhE;AAOD,wBAAgB,gBAAgB,IAAI,IAAI,CAWvC"}
package/dist/config.js CHANGED
@@ -141,7 +141,7 @@ export async function readExistingConnectorUrl() {
141
141
  ? connectorUrl
142
142
  : undefined;
143
143
  }
144
- export async function writeCredentials(creds, apiHost, idpBaseUrl) {
144
+ export async function writeCredentials(creds, apiHost, idpBaseUrl, pendingTarget) {
145
145
  const config = await readConfig();
146
146
  const section = ensureNetworkSection(config);
147
147
  section.enabled = true;
@@ -160,8 +160,14 @@ export async function writeCredentials(creds, apiHost, idpBaseUrl) {
160
160
  if (idpBaseUrl) {
161
161
  section.idpBaseUrl = idpBaseUrl;
162
162
  }
163
+ if (pendingTarget) {
164
+ section.pendingTarget = pendingTarget;
165
+ }
163
166
  await persistConfig(config);
164
167
  storedApiKey = creds.token;
168
+ if (pendingTarget) {
169
+ storedPendingTarget = pendingTarget;
170
+ }
165
171
  stateCacheGeneration++;
166
172
  }
167
173
  export async function writeTargetHandle(handle) {
@@ -1,6 +1,9 @@
1
+ export type OwnerSignal = "owner" | "non-owner" | null;
1
2
  export declare function markOwnerAddress(address: string): void;
2
3
  export declare function isOwnerAddress(address: string): boolean;
3
4
  export declare function clearOwnerState(): void;
4
5
  export declare function setCurrentTurnIsOwner(value: boolean): void;
6
+ export declare function setCurrentTurnOwnerSignal(signal: OwnerSignal): void;
5
7
  export declare function consumeCurrentTurnIsOwner(): boolean;
8
+ export declare function consumeCurrentTurnOwnerSignal(): OwnerSignal;
6
9
  //# sourceMappingURL=owner-session-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"owner-session-state.d.ts","sourceRoot":"","sources":["../src/owner-session-state.ts"],"names":[],"mappings":"AA+BA,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEtD;AAGD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvD;AAGD,wBAAgB,eAAe,IAAI,IAAI,CAGtC;AAMD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE1D;AAUD,wBAAgB,yBAAyB,IAAI,OAAO,CAInD"}
1
+ {"version":3,"file":"owner-session-state.d.ts","sourceRoot":"","sources":["../src/owner-session-state.ts"],"names":[],"mappings":"AAmDA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,WAAW,GAAG,IAAI,CAAC;AAQvD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEtD;AAGD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvD;AAGD,wBAAgB,eAAe,IAAI,IAAI,CAItC;AAMD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE1D;AAYD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAEnE;AAUD,wBAAgB,yBAAyB,IAAI,OAAO,CAInD;AASD,wBAAgB,6BAA6B,IAAI,WAAW,CAI3D"}
@@ -1,5 +1,6 @@
1
1
  const ownerAddresses = new Set();
2
2
  let currentTurnIsOwner = false;
3
+ let currentTurnOwnerSignal = null;
3
4
  export function markOwnerAddress(address) {
4
5
  ownerAddresses.add(address);
5
6
  }
@@ -9,12 +10,21 @@ export function isOwnerAddress(address) {
9
10
  export function clearOwnerState() {
10
11
  ownerAddresses.clear();
11
12
  currentTurnIsOwner = false;
13
+ currentTurnOwnerSignal = null;
12
14
  }
13
15
  export function setCurrentTurnIsOwner(value) {
14
16
  currentTurnIsOwner = value;
15
17
  }
18
+ export function setCurrentTurnOwnerSignal(signal) {
19
+ currentTurnOwnerSignal = signal;
20
+ }
16
21
  export function consumeCurrentTurnIsOwner() {
17
22
  const value = currentTurnIsOwner;
18
23
  currentTurnIsOwner = false;
19
24
  return value;
20
25
  }
26
+ export function consumeCurrentTurnOwnerSignal() {
27
+ const value = currentTurnOwnerSignal;
28
+ currentTurnOwnerSignal = null;
29
+ return value;
30
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAiJA,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,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;CA4XhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAqJA,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,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;CA+XhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/plugin.js CHANGED
@@ -4,10 +4,10 @@ import { login } from "./cli-setup.js";
4
4
  import { detectPendingState, getConversationManager, getDmScope, getStateCacheGeneration, initPluginRuntime, } from "./config.js";
5
5
  import { getAgentIdentity } from "./environment-context.js";
6
6
  import { ownerNotesQueue } from "./owner-notes.js";
7
- import { consumeCurrentTurnIsOwner } from "./owner-session-state.js";
7
+ import { consumeCurrentTurnIsOwner, consumeCurrentTurnOwnerSignal, } from "./owner-session-state.js";
8
8
  import { sentMessageBuffer } from "./sent-message-buffer.js";
9
9
  import { registerTools } from "./tools.js";
10
- import { consumeCurrentTurnSender, setCurrentTurnChannelId, setCurrentTurnIsOwnerForTools, } from "./turn-context.js";
10
+ import { consumeCurrentTurnSender, setCurrentTurnChannelId, setCurrentTurnIsOwnerForTools, setCurrentTurnOwnerSignalForTools, } from "./turn-context.js";
11
11
  import { getUpdateInfo } from "./update-check.js";
12
12
  import { PLUGIN_VERSION } from "./version.js";
13
13
  function formatTimeAgo(timestamp) {
@@ -126,16 +126,18 @@ const plugin = {
126
126
  "After responding on the network, check: " +
127
127
  "should you report to your owner via masons_note_for_owner? " +
128
128
  "Should you follow up with another agent via masons_send_message? " +
129
- "When the interaction is complete, call masons_end_conversation as your FINAL local action; " +
130
- "it does not close the Connector transport session, and you must not generate any text reply after ending. " +
129
+ "When the interaction is complete, call masons_end_conversation as your FINAL action " +
130
+ "do not generate any text reply after ending. " +
131
131
  "";
132
132
  api.on("before_prompt_build", async (_event, ctx) => {
133
133
  const hookCtx = ctx;
134
134
  const channelId = hookCtx?.channelId;
135
135
  const turnIsOwner = consumeCurrentTurnIsOwner();
136
+ const turnOwnerSignal = consumeCurrentTurnOwnerSignal();
136
137
  const turnSender = consumeCurrentTurnSender();
137
138
  setCurrentTurnChannelId(channelId ?? null);
138
139
  setCurrentTurnIsOwnerForTools(turnIsOwner);
140
+ setCurrentTurnOwnerSignalForTools(turnOwnerSignal);
139
141
  const currentGeneration = getStateCacheGeneration();
140
142
  if (!cachedState || cachedGeneration !== currentGeneration) {
141
143
  try {
@@ -0,0 +1,57 @@
1
+ import { PlatformApiError, type PlatformClientConfig } from "./platform-client.js";
2
+ export { PlatformApiError, type PlatformClientConfig };
3
+ export type RuntimeKind = "claude-code" | "openclaw" | "codex" | "passport" | "custom" | (string & {});
4
+ export interface WorkspaceMetadata {
5
+ root: string;
6
+ cwd: string;
7
+ remote?: string;
8
+ branch?: string;
9
+ }
10
+ export type ShareWithPeerField = "workspace" | "tracking_ref" | "runtime_session_id" | "fingerprint" | (string & {});
11
+ export interface RegisterRuntimeEndpointParams {
12
+ as: string;
13
+ runtime_kind: RuntimeKind;
14
+ runtime_session_id?: string;
15
+ endpoint_nonce: string;
16
+ display_label: string;
17
+ workspace?: WorkspaceMetadata;
18
+ tracking_ref?: string;
19
+ share_with_peer?: ShareWithPeerField[];
20
+ }
21
+ export interface RegisterRuntimeEndpointResponse {
22
+ endpoint_id: string;
23
+ node_id: string;
24
+ display_label: string;
25
+ heartbeat_after_seconds: number;
26
+ lease_ttl_seconds: number;
27
+ soft_retention_seconds: number;
28
+ registered_at: string;
29
+ }
30
+ export interface OwnedRuntimeEndpointView {
31
+ endpoint_id: string;
32
+ node_id: string;
33
+ node_handle?: string;
34
+ runtime_kind: RuntimeKind;
35
+ runtime_session_id?: string;
36
+ display_label: string;
37
+ workspace?: WorkspaceMetadata;
38
+ tracking_ref?: string;
39
+ fingerprint?: {
40
+ workspace_hash_per_peer?: Record<string, string>;
41
+ };
42
+ registered_at: string;
43
+ retired_at?: string;
44
+ is_self: boolean;
45
+ }
46
+ export interface ListOwnedRuntimeEndpointsResponse {
47
+ total: number;
48
+ items: OwnedRuntimeEndpointView[];
49
+ }
50
+ export type HeartbeatRuntimeEndpointResponse = Record<string, unknown>;
51
+ export type UnregisterRuntimeEndpointResponse = Record<string, unknown>;
52
+ export declare function registerRuntimeEndpoint(cfg: PlatformClientConfig, runtimeKey: string, params: RegisterRuntimeEndpointParams): Promise<RegisterRuntimeEndpointResponse>;
53
+ export declare function heartbeatRuntimeEndpoint(cfg: PlatformClientConfig, runtimeKey: string, endpointId: string): Promise<HeartbeatRuntimeEndpointResponse>;
54
+ export declare function unregisterRuntimeEndpoint(cfg: PlatformClientConfig, runtimeKey: string, endpointId: string, asNodeId: string): Promise<UnregisterRuntimeEndpointResponse>;
55
+ export declare function listOwnedRuntimeEndpoints(cfg: PlatformClientConfig, runtimeKey: string, asNodeId: string): Promise<ListOwnedRuntimeEndpointsResponse>;
56
+ export declare function getOwnedRuntimeEndpoint(cfg: PlatformClientConfig, runtimeKey: string, endpointId: string, asNodeId: string): Promise<OwnedRuntimeEndpointView>;
57
+ //# sourceMappingURL=runtime-endpoint-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-endpoint-client.d.ts","sourceRoot":"","sources":["../src/runtime-endpoint-client.ts"],"names":[],"mappings":"AAuBA,OAAO,EACL,gBAAgB,EAChB,KAAK,oBAAoB,EAC1B,MAAM,sBAAsB,CAAC;AAM9B,OAAO,EAAE,gBAAgB,EAAE,KAAK,oBAAoB,EAAE,CAAC;AAmBvD,MAAM,MAAM,WAAW,GACnB,aAAa,GACb,UAAU,GACV,OAAO,GACP,UAAU,GACV,QAAQ,GACR,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAalB,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAeD,MAAM,MAAM,kBAAkB,GAC1B,WAAW,GACX,cAAc,GACd,oBAAoB,GACpB,aAAa,GACb,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAUlB,MAAM,WAAW,6BAA6B;IAE5C,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,WAAW,CAAC;IAO1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B,cAAc,EAAE,MAAM,CAAC;IAEvB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAE9B,YAAY,CAAC,EAAE,MAAM,CAAC;IAKtB,eAAe,CAAC,EAAE,kBAAkB,EAAE,CAAC;CACxC;AAcD,MAAM,WAAW,+BAA+B;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,MAAM,CAAC;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;CACvB;AAeD,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IAKpB,OAAO,EAAE,MAAM,CAAC;IAUhB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,WAAW,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAQ5B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IAYtB,WAAW,CAAC,EAAE;QACZ,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAClD,CAAC;IACF,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IAOpB,OAAO,EAAE,OAAO,CAAC;CAClB;AASD,MAAM,WAAW,iCAAiC;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,wBAAwB,EAAE,CAAC;CACnC;AAUD,MAAM,MAAM,gCAAgC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAQvE,MAAM,MAAM,iCAAiC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA4DxE,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,oBAAoB,EACzB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,+BAA+B,CAAC,CAQ1C;AAiBD,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,oBAAoB,EACzB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,gCAAgC,CAAC,CAY3C;AAcD,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,oBAAoB,EACzB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,iCAAiC,CAAC,CAY5C;AAgBD,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,oBAAoB,EACzB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,iCAAiC,CAAC,CAO5C;AAWD,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,oBAAoB,EACzB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,wBAAwB,CAAC,CAUnC"}
@@ -0,0 +1,71 @@
1
+ import { PlatformApiError, } from "./platform-client.js";
2
+ export { PlatformApiError };
3
+ function baseUrl(cfg) {
4
+ const trimmed = cfg.apiHost.replace(/\/+$/, "");
5
+ const origin = /^https?:\/\//.test(trimmed) ? trimmed : `https://${trimmed}`;
6
+ return `${origin}/v1`;
7
+ }
8
+ async function handleError(res) {
9
+ const body = (await res.json().catch(() => ({})));
10
+ const code = typeof body.error === "string" ? body.error : "unknown";
11
+ const message = typeof body.message === "string" ? body.message : res.statusText;
12
+ throw new PlatformApiError(res.status, code, message);
13
+ }
14
+ function authHeaders(runtimeKey) {
15
+ return { Authorization: `Bearer ${runtimeKey}` };
16
+ }
17
+ function jsonHeaders(runtimeKey) {
18
+ return {
19
+ ...authHeaders(runtimeKey),
20
+ "Content-Type": "application/json",
21
+ };
22
+ }
23
+ export async function registerRuntimeEndpoint(cfg, runtimeKey, params) {
24
+ const res = await fetch(`${baseUrl(cfg)}/runtime-endpoints`, {
25
+ method: "POST",
26
+ headers: jsonHeaders(runtimeKey),
27
+ body: JSON.stringify(params),
28
+ });
29
+ if (!res.ok)
30
+ return handleError(res);
31
+ return (await res.json());
32
+ }
33
+ export async function heartbeatRuntimeEndpoint(cfg, runtimeKey, endpointId) {
34
+ const res = await fetch(`${baseUrl(cfg)}/runtime-endpoints/${encodeURIComponent(endpointId)}/heartbeat`, {
35
+ method: "POST",
36
+ headers: authHeaders(runtimeKey),
37
+ });
38
+ if (!res.ok)
39
+ return handleError(res);
40
+ return ((await res.json().catch(() => ({}))) ??
41
+ {});
42
+ }
43
+ export async function unregisterRuntimeEndpoint(cfg, runtimeKey, endpointId, asNodeId) {
44
+ const query = new URLSearchParams({ as: asNodeId }).toString();
45
+ const res = await fetch(`${baseUrl(cfg)}/runtime-endpoints/${encodeURIComponent(endpointId)}?${query}`, {
46
+ method: "DELETE",
47
+ headers: authHeaders(runtimeKey),
48
+ });
49
+ if (!res.ok)
50
+ return handleError(res);
51
+ return ((await res.json().catch(() => ({}))) ??
52
+ {});
53
+ }
54
+ export async function listOwnedRuntimeEndpoints(cfg, runtimeKey, asNodeId) {
55
+ const query = new URLSearchParams({ as: asNodeId }).toString();
56
+ const res = await fetch(`${baseUrl(cfg)}/runtime-endpoints?${query}`, {
57
+ headers: authHeaders(runtimeKey),
58
+ });
59
+ if (!res.ok)
60
+ return handleError(res);
61
+ return (await res.json());
62
+ }
63
+ export async function getOwnedRuntimeEndpoint(cfg, runtimeKey, endpointId, asNodeId) {
64
+ const query = new URLSearchParams({ as: asNodeId }).toString();
65
+ const res = await fetch(`${baseUrl(cfg)}/runtime-endpoints/${encodeURIComponent(endpointId)}?${query}`, {
66
+ headers: authHeaders(runtimeKey),
67
+ });
68
+ if (!res.ok)
69
+ return handleError(res);
70
+ return (await res.json());
71
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Session Lifecycle — manages MSTP session state per contact.
3
+ *
4
+ * State machine: idle -> creating -> active -> recreating -> failed.
5
+ * Internal key: MSTP address (not handle) — supports same handle on different Connectors.
6
+ *
7
+ * Responsibilities:
8
+ * - Maps MSTP address to session state (sessionId, status, pending promise)
9
+ * - Auto-creates sessions on demand via ensureSession()
10
+ * - Deduplicates concurrent creates (second caller awaits first's promise)
11
+ * - Registers inbound sessions from remote agents
12
+ * - Invalidates all sessions on transport disconnect
13
+ * - Subscribes to ConnectorClient events: session_created, session_error, session_ended, disconnected
14
+ *
15
+ * @see docs/openclaw/session-abstraction-system-design.md §6.1
16
+ */
17
+ import type { ConnectorClient } from "./connector-client.js";
18
+ export type SessionStatus = "idle" | "creating" | "active" | "recreating" | "failed";
19
+ export interface SessionState {
20
+ status: SessionStatus;
21
+ sessionId: string | null;
22
+ pendingRequestId: string | null;
23
+ retryCount: number;
24
+ /** Promise that resolves when session creation completes. Used for dedup. */
25
+ createPromise: Promise<string> | null;
26
+ /** Resolve function for the create promise. */
27
+ createResolve: ((sessionId: string) => void) | null;
28
+ /** Reject function for the create promise. */
29
+ createReject: ((err: Error) => void) | null;
30
+ }
31
+ export declare class SessionLifecycle {
32
+ private readonly client;
33
+ /** Primary map: MSTP address -> session state */
34
+ private readonly sessions;
35
+ /** Reverse index: sessionId -> MSTP address */
36
+ private readonly sessionToAddress;
37
+ /** Configurable timeout for tests */
38
+ private timeoutMs;
39
+ constructor(client: ConnectorClient);
40
+ /**
41
+ * Ensure an active session exists for the given address.
42
+ * Returns the sessionId. Creates a new session if none exists.
43
+ * Concurrent callers get the same promise (dedup).
44
+ */
45
+ ensureSession(address: string): Promise<string>;
46
+ /**
47
+ * Get the active sessionId for an address, or null.
48
+ */
49
+ getSession(address: string): string | null;
50
+ /**
51
+ * Close the session for an address.
52
+ */
53
+ closeSession(address: string, reason?: string): void;
54
+ /**
55
+ * Register an inbound session (remote initiated).
56
+ */
57
+ registerInbound(sessionId: string, address: string): void;
58
+ /**
59
+ * Invalidate all sessions. Called on transport disconnect.
60
+ * Conversations are preserved but sessions are cleared.
61
+ */
62
+ invalidateAll(): void;
63
+ /**
64
+ * Reverse lookup: sessionId -> MSTP address.
65
+ */
66
+ getAddressBySessionId(sessionId: string): string | undefined;
67
+ /**
68
+ * Get all session entries (for listing conversations).
69
+ */
70
+ listSessions(): Array<{
71
+ address: string;
72
+ status: SessionStatus;
73
+ sessionId: string | null;
74
+ }>;
75
+ /**
76
+ * Remove a session entry entirely (for endConversation).
77
+ */
78
+ removeSession(address: string): void;
79
+ /** @internal Override timeout for tests. */
80
+ _setTimeoutForTesting(ms: number): void;
81
+ /** @internal Reset for test isolation. */
82
+ _resetForTesting(): void;
83
+ private subscribe;
84
+ private createNewSession;
85
+ private handleSessionCreated;
86
+ private handleSessionEnded;
87
+ private handleSessionError;
88
+ }
89
+ //# sourceMappingURL=session-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-lifecycle.d.ts","sourceRoot":"","sources":["../src/session-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAQ7D,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,UAAU,GACV,QAAQ,GACR,YAAY,GACZ,QAAQ,CAAC;AAEb,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IACtC,+CAA+C;IAC/C,aAAa,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACpD,8CAA8C;IAC9C,YAAY,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;CAC7C;AAaD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,iDAAiD;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;IAC5D,+CAA+C;IAC/C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;IAE9D,qCAAqC;IACrC,OAAO,CAAC,SAAS,CAA6B;gBAElC,MAAM,EAAE,eAAe;IASnC;;;;OAIG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAoB/C;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAQ1C;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAqBpD;;OAEG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAqBzD;;;OAGG;IACH,aAAa,IAAI,IAAI;IAqBrB;;OAEG;IACH,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI5D;;OAEG;IACH,YAAY,IAAI,KAAK,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,aAAa,CAAC;QACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,CAAC;IAgBF;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAcpC,4CAA4C;IAC5C,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIvC,0CAA0C;IAC1C,gBAAgB,IAAI,IAAI;IAmBxB,OAAO,CAAC,SAAS;IAsBjB,OAAO,CAAC,gBAAgB;IA+GxB,OAAO,CAAC,oBAAoB;IAuC5B,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,kBAAkB;CA0B3B"}
@@ -0,0 +1,348 @@
1
+ /**
2
+ * Session Lifecycle — manages MSTP session state per contact.
3
+ *
4
+ * State machine: idle -> creating -> active -> recreating -> failed.
5
+ * Internal key: MSTP address (not handle) — supports same handle on different Connectors.
6
+ *
7
+ * Responsibilities:
8
+ * - Maps MSTP address to session state (sessionId, status, pending promise)
9
+ * - Auto-creates sessions on demand via ensureSession()
10
+ * - Deduplicates concurrent creates (second caller awaits first's promise)
11
+ * - Registers inbound sessions from remote agents
12
+ * - Invalidates all sessions on transport disconnect
13
+ * - Subscribes to ConnectorClient events: session_created, session_error, session_ended, disconnected
14
+ *
15
+ * @see docs/openclaw/session-abstraction-system-design.md §6.1
16
+ */
17
+ import createDebug from "debug";
18
+ const dbg = createDebug("agent-network:session-lifecycle");
19
+ // ---------------------------------------------------------------------------
20
+ // Constants
21
+ // ---------------------------------------------------------------------------
22
+ const SESSION_CREATE_TIMEOUT_MS = 15_000;
23
+ const MAX_RETRY_COUNT = 1;
24
+ // ---------------------------------------------------------------------------
25
+ // SessionLifecycle
26
+ // ---------------------------------------------------------------------------
27
+ export class SessionLifecycle {
28
+ client;
29
+ /** Primary map: MSTP address -> session state */
30
+ sessions = new Map();
31
+ /** Reverse index: sessionId -> MSTP address */
32
+ sessionToAddress = new Map();
33
+ /** Configurable timeout for tests */
34
+ timeoutMs = SESSION_CREATE_TIMEOUT_MS;
35
+ constructor(client) {
36
+ this.client = client;
37
+ this.subscribe();
38
+ }
39
+ // -------------------------------------------------------------------------
40
+ // Public API
41
+ // -------------------------------------------------------------------------
42
+ /**
43
+ * Ensure an active session exists for the given address.
44
+ * Returns the sessionId. Creates a new session if none exists.
45
+ * Concurrent callers get the same promise (dedup).
46
+ */
47
+ ensureSession(address) {
48
+ const state = this.sessions.get(address);
49
+ // Active session — return immediately
50
+ if (state?.status === "active" && state.sessionId) {
51
+ return Promise.resolve(state.sessionId);
52
+ }
53
+ // Creating/recreating — dedup: return existing promise
54
+ if ((state?.status === "creating" || state?.status === "recreating") &&
55
+ state.createPromise) {
56
+ return state.createPromise;
57
+ }
58
+ // Idle, failed, or no state — create new session
59
+ return this.createNewSession(address, state?.retryCount ?? 0);
60
+ }
61
+ /**
62
+ * Get the active sessionId for an address, or null.
63
+ */
64
+ getSession(address) {
65
+ const state = this.sessions.get(address);
66
+ if (state?.status === "active" && state.sessionId) {
67
+ return state.sessionId;
68
+ }
69
+ return null;
70
+ }
71
+ /**
72
+ * Close the session for an address.
73
+ */
74
+ closeSession(address, reason) {
75
+ const state = this.sessions.get(address);
76
+ if (!state)
77
+ return;
78
+ if (state.sessionId) {
79
+ this.client.endSession(state.sessionId, reason);
80
+ this.sessionToAddress.delete(state.sessionId);
81
+ }
82
+ // Reject any pending create promise
83
+ if (state.createReject) {
84
+ state.createReject(new Error("Session closed by caller"));
85
+ state.createResolve = null;
86
+ state.createReject = null;
87
+ state.createPromise = null;
88
+ }
89
+ this.sessions.delete(address);
90
+ dbg("closeSession address=%s", address);
91
+ }
92
+ /**
93
+ * Register an inbound session (remote initiated).
94
+ */
95
+ registerInbound(sessionId, address) {
96
+ // If there's an existing session for this address, clean up
97
+ const existing = this.sessions.get(address);
98
+ if (existing?.sessionId && existing.sessionId !== sessionId) {
99
+ this.sessionToAddress.delete(existing.sessionId);
100
+ }
101
+ const state = {
102
+ status: "active",
103
+ sessionId,
104
+ pendingRequestId: null,
105
+ retryCount: 0,
106
+ createPromise: null,
107
+ createResolve: null,
108
+ createReject: null,
109
+ };
110
+ this.sessions.set(address, state);
111
+ this.sessionToAddress.set(sessionId, address);
112
+ dbg("registerInbound sessionId=%s address=%s", sessionId, address);
113
+ }
114
+ /**
115
+ * Invalidate all sessions. Called on transport disconnect.
116
+ * Conversations are preserved but sessions are cleared.
117
+ */
118
+ invalidateAll() {
119
+ const count = this.sessions.size;
120
+ for (const [address, state] of this.sessions) {
121
+ // Reject pending creates
122
+ if (state.createReject) {
123
+ state.createReject(new Error("Transport disconnected"));
124
+ state.createResolve = null;
125
+ state.createReject = null;
126
+ state.createPromise = null;
127
+ }
128
+ // Reset to idle — next send() will auto-create
129
+ state.status = "idle";
130
+ state.sessionId = null;
131
+ state.pendingRequestId = null;
132
+ state.retryCount = 0;
133
+ this.sessions.set(address, state);
134
+ }
135
+ this.sessionToAddress.clear();
136
+ dbg("invalidateAll cleared %d sessions", count);
137
+ }
138
+ /**
139
+ * Reverse lookup: sessionId -> MSTP address.
140
+ */
141
+ getAddressBySessionId(sessionId) {
142
+ return this.sessionToAddress.get(sessionId);
143
+ }
144
+ /**
145
+ * Get all session entries (for listing conversations).
146
+ */
147
+ listSessions() {
148
+ const entries = [];
149
+ for (const [address, state] of this.sessions) {
150
+ entries.push({
151
+ address,
152
+ status: state.status,
153
+ sessionId: state.sessionId,
154
+ });
155
+ }
156
+ return entries;
157
+ }
158
+ /**
159
+ * Remove a session entry entirely (for endConversation).
160
+ */
161
+ removeSession(address) {
162
+ const state = this.sessions.get(address);
163
+ if (state?.sessionId) {
164
+ this.sessionToAddress.delete(state.sessionId);
165
+ }
166
+ if (state?.createReject) {
167
+ state.createReject(new Error("Session removed"));
168
+ state.createResolve = null;
169
+ state.createReject = null;
170
+ state.createPromise = null;
171
+ }
172
+ this.sessions.delete(address);
173
+ }
174
+ /** @internal Override timeout for tests. */
175
+ _setTimeoutForTesting(ms) {
176
+ this.timeoutMs = ms;
177
+ }
178
+ /** @internal Reset for test isolation. */
179
+ _resetForTesting() {
180
+ // Reject pending creates silently
181
+ for (const state of this.sessions.values()) {
182
+ if (state.createReject) {
183
+ state.createReject(new Error("Reset for testing"));
184
+ state.createResolve = null;
185
+ state.createReject = null;
186
+ state.createPromise = null;
187
+ }
188
+ }
189
+ this.sessions.clear();
190
+ this.sessionToAddress.clear();
191
+ this.timeoutMs = SESSION_CREATE_TIMEOUT_MS;
192
+ }
193
+ // -------------------------------------------------------------------------
194
+ // Private
195
+ // -------------------------------------------------------------------------
196
+ subscribe() {
197
+ this.client.on("session_created", (event) => {
198
+ this.handleSessionCreated(event.sessionId, event.requestId, event.direction);
199
+ });
200
+ this.client.on("session_error", (sessionId, message) => {
201
+ this.handleSessionError(sessionId, message);
202
+ });
203
+ this.client.on("session_ended", (event) => {
204
+ this.handleSessionEnded(event.sessionId);
205
+ });
206
+ this.client.on("disconnected", () => {
207
+ this.invalidateAll();
208
+ });
209
+ }
210
+ createNewSession(address, previousRetryCount) {
211
+ const status = previousRetryCount > 0 ? "recreating" : "creating";
212
+ let createResolve = null;
213
+ let createReject = null;
214
+ const createPromise = new Promise((resolve, reject) => {
215
+ createResolve = resolve;
216
+ createReject = reject;
217
+ });
218
+ const { requestId, sent } = this.client.createSession(address);
219
+ if (!sent) {
220
+ const state = {
221
+ status: "failed",
222
+ sessionId: null,
223
+ pendingRequestId: null,
224
+ retryCount: previousRetryCount,
225
+ createPromise: null,
226
+ createResolve: null,
227
+ createReject: null,
228
+ };
229
+ this.sessions.set(address, state);
230
+ return Promise.reject(new Error("Failed to create session: transport unavailable"));
231
+ }
232
+ const state = {
233
+ status,
234
+ sessionId: null,
235
+ pendingRequestId: requestId,
236
+ retryCount: previousRetryCount,
237
+ createPromise,
238
+ createResolve,
239
+ createReject,
240
+ };
241
+ this.sessions.set(address, state);
242
+ // Timeout
243
+ const timer = setTimeout(() => {
244
+ const current = this.sessions.get(address);
245
+ if (current &&
246
+ current.pendingRequestId === requestId &&
247
+ (current.status === "creating" || current.status === "recreating")) {
248
+ dbg("session create timeout address=%s requestId=%s", address, requestId);
249
+ // Can we retry?
250
+ if (current.retryCount < MAX_RETRY_COUNT) {
251
+ // Move to failed, then retry
252
+ current.status = "failed";
253
+ current.retryCount++;
254
+ current.pendingRequestId = null;
255
+ const retryResolve = current.createResolve;
256
+ const retryReject = current.createReject;
257
+ current.createPromise = null;
258
+ current.createResolve = null;
259
+ current.createReject = null;
260
+ this.sessions.set(address, current);
261
+ // Retry: create a new session, pipe result to original promise
262
+ this.createNewSession(address, current.retryCount)
263
+ .then((sessionId) => retryResolve?.(sessionId))
264
+ .catch((err) => retryReject?.(err));
265
+ }
266
+ else {
267
+ // Max retries exhausted
268
+ current.status = "idle";
269
+ current.pendingRequestId = null;
270
+ current.retryCount = 0;
271
+ if (current.createReject) {
272
+ current.createReject(new Error("Session creation timed out. The remote agent may be unavailable."));
273
+ }
274
+ current.createPromise = null;
275
+ current.createResolve = null;
276
+ current.createReject = null;
277
+ this.sessions.set(address, current);
278
+ }
279
+ }
280
+ }, this.timeoutMs);
281
+ // Attach cleanup to the promise to clear the timer if resolved early
282
+ createPromise
283
+ .then(() => clearTimeout(timer))
284
+ .catch(() => clearTimeout(timer));
285
+ dbg("createNewSession address=%s requestId=%s status=%s retry=%d", address, requestId, status, previousRetryCount);
286
+ return createPromise;
287
+ }
288
+ handleSessionCreated(sessionId, requestId, direction) {
289
+ // Outbound: match by requestId
290
+ if (direction === "outbound" && requestId) {
291
+ for (const [address, state] of this.sessions) {
292
+ if (state.pendingRequestId === requestId &&
293
+ (state.status === "creating" || state.status === "recreating")) {
294
+ state.status = "active";
295
+ state.sessionId = sessionId;
296
+ state.pendingRequestId = null;
297
+ this.sessionToAddress.set(sessionId, address);
298
+ if (state.createResolve) {
299
+ state.createResolve(sessionId);
300
+ }
301
+ state.createPromise = null;
302
+ state.createResolve = null;
303
+ state.createReject = null;
304
+ dbg("session created (outbound) address=%s sessionId=%s", address, sessionId);
305
+ return;
306
+ }
307
+ }
308
+ }
309
+ // Inbound sessions are registered externally via registerInbound()
310
+ // from the channel adapter (which has the metadata to derive the address).
311
+ // We don't handle inbound SESSION_CREATED here.
312
+ }
313
+ handleSessionEnded(sessionId) {
314
+ const address = this.sessionToAddress.get(sessionId);
315
+ if (!address)
316
+ return;
317
+ const state = this.sessions.get(address);
318
+ if (!state)
319
+ return;
320
+ // Only clear if this session is the current one
321
+ if (state.sessionId === sessionId) {
322
+ state.status = "idle";
323
+ state.sessionId = null;
324
+ dbg("session ended address=%s sessionId=%s", address, sessionId);
325
+ }
326
+ this.sessionToAddress.delete(sessionId);
327
+ }
328
+ handleSessionError(sessionId, message) {
329
+ const address = this.sessionToAddress.get(sessionId);
330
+ if (!address)
331
+ return;
332
+ const state = this.sessions.get(address);
333
+ if (!state)
334
+ return;
335
+ dbg("session error address=%s sessionId=%s message=%s", address, sessionId, message);
336
+ // Session error on active session — mark for auto-recreate on next send().
337
+ // Reset retryCount to 0: this is a new failure context (transport error),
338
+ // not a continuation of a creation timeout. The next ensureSession() gets
339
+ // a fresh retry budget.
340
+ if (state.status === "active") {
341
+ this.sessionToAddress.delete(sessionId);
342
+ state.status = "idle";
343
+ state.sessionId = null;
344
+ state.retryCount = 0;
345
+ dbg("auto-recreate queued address=%s", address);
346
+ }
347
+ }
348
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAoEA,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,WAAW;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,KAAK,WAAW,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,cAAc,GAAG,IAAI,GAAG,SAAS,CAAC;AAE3E,UAAU,OAAO;IACf,YAAY,CACV,IAAI,EAAE,cAAc,GAAG,WAAW,EAClC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3C,IAAI,CAAC;CACT;AAmCD,wBAAgB,qBAAqB,IAAI,IAAI,CAI5C;AA2ND,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAoyBhD"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAqEA,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,WAAW;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,KAAK,WAAW,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,cAAc,GAAG,IAAI,GAAG,SAAS,CAAC;AAE3E,UAAU,OAAO;IACf,YAAY,CACV,IAAI,EAAE,cAAc,GAAG,WAAW,EAClC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3C,IAAI,CAAC;CACT;AAmCD,wBAAgB,qBAAqB,IAAI,IAAI,CAI5C;AAqRD,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CA80BhD"}
package/dist/tools.js CHANGED
@@ -6,7 +6,7 @@ import { getOwnerHandle } from "./environment-context.js";
6
6
  import { ownerNotesQueue } from "./owner-notes.js";
7
7
  import { acceptRequest, declineRequest, listConnections, listRequests, PlatformApiError, requestConnection, updateProfile, } from "./platform-client.js";
8
8
  import { sentMessageBuffer } from "./sent-message-buffer.js";
9
- import { getCurrentTurnChannelId, getCurrentTurnIsOwnerNonConsuming, } from "./turn-context.js";
9
+ import { getCurrentTurnChannelId, getCurrentTurnIsOwnerNonConsuming, getCurrentTurnOwnerSignal, } from "./turn-context.js";
10
10
  import { getLatestPublishedVersion, getPluginVersion, getUpdateInfo, } from "./update-check.js";
11
11
  const PROFILE_FIELDS = new Set(["name", "scope", "about", "audience"]);
12
12
  const UPGRADE_CMD = "openclaw plugins update agent-network";
@@ -56,12 +56,17 @@ function formatConnectionResult(requestIds, status) {
56
56
  }
57
57
  return `Connection request sent. Status: ${status}. The other agent's owner will be notified.`;
58
58
  }
59
- function resolveSetupAuthority(ctx) {
60
- if (ctx.senderIsOwner === false) {
61
- return { allowed: false, reason: "known-non-owner" };
59
+ function resolveSetupAuthority() {
60
+ const channelId = getCurrentTurnChannelId();
61
+ if (channelId !== "agent-network") {
62
+ return { allowed: true };
62
63
  }
63
- if (ctx.senderIsOwner === true)
64
+ const signal = getCurrentTurnOwnerSignal();
65
+ if (signal === "owner")
64
66
  return { allowed: true };
67
+ if (signal === "non-owner") {
68
+ return { allowed: false, reason: "known-non-owner" };
69
+ }
65
70
  return { allowed: false, reason: "ambiguous" };
66
71
  }
67
72
  function getSetupOptionsFromToolContext(ctx) {
@@ -116,20 +121,25 @@ function terminalSetupInstructions() {
116
121
  "its own runtime key. Re-running login here updates this OpenClaw install's credentials.",
117
122
  ].join("\n");
118
123
  }
119
- function ownerFallbackText(reason) {
124
+ function ownerRefusalText(reason) {
120
125
  if (reason === "known-non-owner") {
121
126
  return [
122
- "Agent Network setup is owner-only. I cannot generate a MASONS handoff URL for this sender.",
123
- "",
124
- terminalSetupInstructions(),
127
+ "Setup is owner-only. You are reaching this agent through the MASONS",
128
+ "network as a visitor or as a peer agent. Setup re-binds the OpenClaw",
129
+ "runtime to a specific agent identity, so it must be initiated by the",
130
+ "agent's runtime owner from a channel their OpenClaw is configured for.",
125
131
  ].join("\n");
126
132
  }
127
133
  return [
128
- "I cannot verify this channel sender as the OpenClaw owner, so I will not generate a MASONS setup link here.",
134
+ "Setup is owner-only on the MASONS network channel. I could not verify",
135
+ "that you are this agent's owner from the message received.",
129
136
  "",
130
- "Use the terminal fallback, or configure OpenClaw owner authority for this channel and ask again:",
137
+ "If you are the agent's runtime owner, please initiate setup from a",
138
+ "channel your OpenClaw runtime is configured for (e.g., Lark, Telegram,",
139
+ "desktop). The terminal command works as a last resort if no other channel",
140
+ "is reachable:",
131
141
  "",
132
- terminalSetupInstructions(),
142
+ " openclaw channels login --channel agent-network",
133
143
  ].join("\n");
134
144
  }
135
145
  function formatChannelSetupResult(result) {
@@ -175,24 +185,46 @@ export function registerTools(api) {
175
185
  description: [
176
186
  "Start or inspect Agent Network setup.",
177
187
  "Use when the user asks to set up Agent Network, when other Agent",
178
- "Network tools fail with a 'no credentials' / 'no runtime key' error, or",
179
- "when the owner wants to switch which MASONS agent OpenClaw is driving.",
188
+ "Network tools fail with a 'no credentials' error, or when the runtime",
189
+ "owner wants to switch which MASONS agent OpenClaw is driving.",
180
190
  "",
181
- "If the current channel sender is verified as the OpenClaw owner, this",
182
- "tool starts an encrypted MASONS browser handoff and returns the URL.",
183
- "If owner authority is missing or false, it returns terminal fallback",
184
- "instructions and never generates a setup URL.",
191
+ "Pass `invitedBy` if the user came from another agent's profile page",
192
+ "(handle extracted from the URL); the plugin will record it and propose",
193
+ "a connection request after setup completes.",
194
+ "",
195
+ "This tool returns a browser handoff URL on the channel where you are",
196
+ "running. The user opens it, signs in, picks an agent, and the encrypted",
197
+ "handoff completes in the background. After completion, restart the",
198
+ "gateway to activate realtime messaging.",
199
+ "",
200
+ "If the user is reaching this agent through the MASONS network as a",
201
+ "visitor (not the agent's owner), this tool refuses with an explanation",
202
+ "— setup is owner-only because it re-binds the OpenClaw runtime to a",
203
+ "specific agent identity.",
185
204
  ].join("\n"),
186
- parameters: Type.Object({}),
187
- execute: async () => {
188
- const authority = resolveSetupAuthority(ctx);
205
+ parameters: Type.Object({
206
+ invitedBy: Type.Optional(Type.String({
207
+ description: "Handle of the agent whose profile page the user came from " +
208
+ "(e.g., 'alice' from 'masons.ai/alice'). When setup completes, " +
209
+ "the plugin records this as a pending connection target so the " +
210
+ "agent can propose a connection request automatically.",
211
+ })),
212
+ }),
213
+ execute: async (_id, params) => {
214
+ const authority = resolveSetupAuthority();
189
215
  if (!authority.allowed) {
190
- return textResult(ownerFallbackText(authority.reason ?? "ambiguous"));
216
+ return textResult(ownerRefusalText(authority.reason ?? "ambiguous"));
191
217
  }
218
+ const invitedBy = typeof params.invitedBy === "string" && params.invitedBy.length > 0
219
+ ? params.invitedBy
220
+ : undefined;
192
221
  const existing = consumeChannelSetupStatus();
193
222
  const result = existing
194
223
  ? existing
195
- : await startOrGetChannelSetup(getSetupOptionsFromToolContext(ctx));
224
+ : await startOrGetChannelSetup({
225
+ ...getSetupOptionsFromToolContext(ctx),
226
+ invitedBy,
227
+ });
196
228
  return textResult(formatChannelSetupResult(result));
197
229
  },
198
230
  }), { name: "masons_setup" });
@@ -529,7 +561,7 @@ export function registerTools(api) {
529
561
  });
530
562
  api.registerTool({
531
563
  name: "masons_end_conversation",
532
- description: "End the local Agent Network interaction with a connected agent. This is a silent terminal action: it does not close the Connector transport session, and no text reply should be sent after calling it.",
564
+ description: "End the conversation with a connected agent. This immediately closes the session do not send any text reply after calling this tool.",
533
565
  parameters: Type.Object({
534
566
  contact: Type.String({
535
567
  description: "Handle (e.g. alice) or address of the agent to end the conversation with",
@@ -1,7 +1,10 @@
1
+ import type { OwnerSignal } from "./owner-session-state.js";
1
2
  export declare function setCurrentTurnSender(contact: string | null): void;
2
3
  export declare function setCurrentTurnChannelId(channelId: string | null): void;
3
4
  export declare function setCurrentTurnIsOwnerForTools(value: boolean): void;
5
+ export declare function setCurrentTurnOwnerSignalForTools(signal: OwnerSignal): void;
4
6
  export declare function consumeCurrentTurnSender(): string | null;
5
7
  export declare function getCurrentTurnChannelId(): string | null;
6
8
  export declare function getCurrentTurnIsOwnerNonConsuming(): boolean;
9
+ export declare function getCurrentTurnOwnerSignal(): OwnerSignal;
7
10
  //# sourceMappingURL=turn-context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"turn-context.d.ts","sourceRoot":"","sources":["../src/turn-context.ts"],"names":[],"mappings":"AAyCA,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEjE;AAOD,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEtE;AAOD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAElE;AAUD,wBAAgB,wBAAwB,IAAI,MAAM,GAAG,IAAI,CAIxD;AAMD,wBAAgB,uBAAuB,IAAI,MAAM,GAAG,IAAI,CAEvD;AAMD,wBAAgB,iCAAiC,IAAI,OAAO,CAE3D"}
1
+ {"version":3,"file":"turn-context.d.ts","sourceRoot":"","sources":["../src/turn-context.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAwC5D,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEjE;AAOD,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEtE;AAOD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAElE;AAUD,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAE3E;AAUD,wBAAgB,wBAAwB,IAAI,MAAM,GAAG,IAAI,CAIxD;AAMD,wBAAgB,uBAAuB,IAAI,MAAM,GAAG,IAAI,CAEvD;AAMD,wBAAgB,iCAAiC,IAAI,OAAO,CAE3D;AAOD,wBAAgB,yBAAyB,IAAI,WAAW,CAEvD"}
@@ -1,6 +1,7 @@
1
1
  let currentTurnSender = null;
2
2
  let currentTurnChannelId = null;
3
3
  let currentTurnIsOwner = false;
4
+ let currentTurnOwnerSignalForTools = null;
4
5
  export function setCurrentTurnSender(contact) {
5
6
  currentTurnSender = contact;
6
7
  }
@@ -10,6 +11,9 @@ export function setCurrentTurnChannelId(channelId) {
10
11
  export function setCurrentTurnIsOwnerForTools(value) {
11
12
  currentTurnIsOwner = value;
12
13
  }
14
+ export function setCurrentTurnOwnerSignalForTools(signal) {
15
+ currentTurnOwnerSignalForTools = signal;
16
+ }
13
17
  export function consumeCurrentTurnSender() {
14
18
  const value = currentTurnSender;
15
19
  currentTurnSender = null;
@@ -21,3 +25,6 @@ export function getCurrentTurnChannelId() {
21
25
  export function getCurrentTurnIsOwnerNonConsuming() {
22
26
  return currentTurnIsOwner;
23
27
  }
28
+ export function getCurrentTurnOwnerSignal() {
29
+ return currentTurnOwnerSignalForTools;
30
+ }
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const PLUGIN_VERSION = "0.5.23";
1
+ export declare const PLUGIN_VERSION = "0.5.25";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const PLUGIN_VERSION = "0.5.23";
1
+ export const PLUGIN_VERSION = "0.5.25";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@masons/agent-network",
3
- "version": "0.5.23",
3
+ "version": "0.5.25",
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)",
@@ -55,6 +55,10 @@
55
55
  "./platform-client": {
56
56
  "types": "./dist/platform-client.d.ts",
57
57
  "default": "./dist/platform-client.js"
58
+ },
59
+ "./runtime-endpoint": {
60
+ "types": "./dist/runtime-endpoint-client.d.ts",
61
+ "default": "./dist/runtime-endpoint-client.js"
58
62
  }
59
63
  },
60
64
  "dependencies": {