@masons/agent-network 0.6.24 → 0.6.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.
@@ -4,6 +4,7 @@ interface StartChannelSetupOptions {
4
4
  idpBaseUrl: string;
5
5
  idpBaseUrlIsDefault?: boolean;
6
6
  invitedBy?: string;
7
+ startOver?: boolean;
7
8
  }
8
9
  export interface ChannelSetupResult {
9
10
  status: "pending" | "completed" | "expired" | "failed";
@@ -14,7 +15,7 @@ export interface ChannelSetupResult {
14
15
  export declare function getDefaultChannelSetupOptions(): StartChannelSetupOptions;
15
16
  export declare function startOrGetChannelSetup(options: StartChannelSetupOptions): Promise<ChannelSetupResult>;
16
17
  export declare function getChannelSetupStatus(): ChannelSetupResult | null;
17
- export declare function consumeChannelSetupStatus(): ChannelSetupResult | null;
18
+ export declare function getReportableSetupStatus(): ChannelSetupResult | null;
18
19
  export declare function _resetChannelSetupForTesting(): void;
19
20
  export declare const CHANNEL_SETUP_TTL_MS: number;
20
21
  export {};
@@ -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,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IAEnB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAM9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA2BD,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,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAOD,wBAAgB,6BAA6B,IAAI,wBAAwB,CAKxE;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC,CAsC7B;AAED,wBAAgB,qBAAqB,IAAI,kBAAkB,GAAG,IAAI,CAcjE;AAED,wBAAgB,yBAAyB,IAAI,kBAAkB,GAAG,IAAI,CAMrE;AAmFD,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":"AAkBA,UAAU,wBAAwB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IAEnB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAM9B,SAAS,CAAC,EAAE,MAAM,CAAC;IAOnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AA4CD,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,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAuBD,wBAAgB,6BAA6B,IAAI,wBAAwB,CAKxE;AAWD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC,CAkB7B;AAOD,wBAAgB,qBAAqB,IAAI,kBAAkB,GAAG,IAAI,CAQjE;AAaD,wBAAgB,wBAAwB,IAAI,kBAAkB,GAAG,IAAI,CAOpE;AAgOD,wBAAgB,4BAA4B,IAAI,IAAI,CAInD;AAED,eAAO,MAAM,oBAAoB,QAAqB,CAAC"}
@@ -1,12 +1,17 @@
1
1
  import createDebug from "debug";
2
2
  import { readExistingConnectorUrl } from "./config.js";
3
3
  import { resolveConnectorUrlForSetup } from "./connector-url-boundary.js";
4
- import { beginBridgeHandoff, completeBridgeHandoff, DEFAULT_IDP_BASE_URL, HANDOFF_TIMEOUT_MS, SetupFlowError, } from "./handoff.js";
4
+ import { beginBridgeHandoff, collectBridgeHandoffOnce, completeBridgeHandoff, DEFAULT_IDP_BASE_URL, HANDOFF_TIMEOUT_MS, SetupFlowError, } from "./handoff.js";
5
5
  import { acceptBridgeHandoff } from "./handoff-acceptance.js";
6
6
  import { DEFAULT_API_HOST } from "./platform-client.js";
7
7
  const dbg = createDebug("agent-network:channel-setup");
8
+ const GENERIC_EXPIRY_MESSAGE = "The previous Link URL expired before the browser handoff completed.";
9
+ const GONE_MESSAGE = "That Link session is no longer on the handoff bridge — it expired, or its runtime key was already delivered — so it cannot complete.";
10
+ const SUPERSEDED_MESSAGE = "A newer Link session replaced this one while it was completing, so the runtime key that session delivered was discarded. Nothing changed — this runtime still uses the credential it had before.";
8
11
  let pendingSetup = null;
9
12
  let lastSetupStatus = null;
13
+ let setupSeq = 0;
14
+ let lastRecordedSeq = 0;
10
15
  export function getDefaultChannelSetupOptions() {
11
16
  return {
12
17
  apiHost: DEFAULT_API_HOST,
@@ -14,50 +19,26 @@ export function getDefaultChannelSetupOptions() {
14
19
  };
15
20
  }
16
21
  export async function startOrGetChannelSetup(options) {
22
+ if (options.startOver)
23
+ return await startNewSession(options);
17
24
  const active = getActivePendingSetup();
18
25
  if (active) {
19
- return {
20
- status: "pending",
21
- handoffUrl: active.session.handoffUrl,
22
- expiresAt: active.session.expiresAt,
23
- };
26
+ const collected = await collectActiveSetup(active);
27
+ if (collected)
28
+ return collected;
24
29
  }
25
- lastSetupStatus = null;
26
- const existingConnectorUrl = await readExistingConnectorUrl();
27
- const connectorUrl = resolveConnectorUrlForSetup({
28
- apiHost: options.apiHost,
29
- existingConnectorUrl,
30
- configuredConnectorUrl: options.connectorUrl,
31
- });
32
- const session = await beginBridgeHandoff({
33
- apiHost: options.apiHost,
34
- idpBaseUrl: options.idpBaseUrl,
35
- idpBaseUrlIsDefault: options.idpBaseUrlIsDefault,
36
- });
37
- const record = {
38
- session,
39
- apiHost: options.apiHost,
40
- connectorUrl,
41
- idpBaseUrl: options.idpBaseUrl,
42
- invitedBy: options.invitedBy,
43
- };
44
- pendingSetup = record;
45
- void completePendingSetup(record);
46
- return {
47
- status: "pending",
48
- handoffUrl: session.handoffUrl,
49
- expiresAt: session.expiresAt,
50
- };
30
+ const reportable = getReportableSetupStatus();
31
+ if (reportable)
32
+ return reportable;
33
+ const live = getActivePendingSetup();
34
+ if (live)
35
+ return pendingResult(live);
36
+ return await startNewSession(options);
51
37
  }
52
38
  export function getChannelSetupStatus() {
53
39
  const active = getActivePendingSetup();
54
- if (active) {
55
- return {
56
- status: "pending",
57
- handoffUrl: active.session.handoffUrl,
58
- expiresAt: active.session.expiresAt,
59
- };
60
- }
40
+ if (active)
41
+ return pendingResult(active);
61
42
  if (!lastSetupStatus)
62
43
  return null;
63
44
  return {
@@ -65,31 +46,76 @@ export function getChannelSetupStatus() {
65
46
  message: lastSetupStatus.message,
66
47
  };
67
48
  }
68
- export function consumeChannelSetupStatus() {
69
- const status = getChannelSetupStatus();
70
- if (status?.status !== "pending") {
71
- lastSetupStatus = null;
72
- }
73
- return status;
49
+ export function getReportableSetupStatus() {
50
+ const recorded = lastSetupStatus;
51
+ if (!recorded)
52
+ return null;
53
+ if (pendingSetup?.seq === recorded.seq)
54
+ return null;
55
+ if (recorded.status === "completed")
56
+ return reportStatus(recorded);
57
+ if (!recorded.reported)
58
+ return reportStatus(recorded);
59
+ return null;
60
+ }
61
+ function pendingResult(record) {
62
+ return {
63
+ status: "pending",
64
+ handoffUrl: record.session.handoffUrl,
65
+ expiresAt: record.session.expiresAt,
66
+ };
67
+ }
68
+ function reportStatus(status) {
69
+ status.reported = true;
70
+ return { status: status.status, message: status.message };
71
+ }
72
+ function storedResult(stored) {
73
+ return stored ? reportStatus(stored) : null;
74
74
  }
75
75
  function getActivePendingSetup() {
76
76
  if (!pendingSetup)
77
77
  return null;
78
78
  if (Date.now() < pendingSetup.session.expiresAt)
79
79
  return pendingSetup;
80
- lastSetupStatus = {
81
- status: "expired",
82
- message: "The previous Link URL expired before the browser handoff completed.",
83
- finishedAt: Date.now(),
84
- };
80
+ const lapsed = pendingSetup;
85
81
  pendingSetup = null;
82
+ recordSetupStatus(lapsed, "expired", GENERIC_EXPIRY_MESSAGE, false);
86
83
  return null;
87
84
  }
88
- async function completePendingSetup(record) {
85
+ async function collectActiveSetup(record) {
86
+ let collection;
87
+ try {
88
+ collection = await collectBridgeHandoffOnce(record.session);
89
+ }
90
+ catch (err) {
91
+ dbg("owner-invoked collection could not reach the bridge: %O", err);
92
+ return null;
93
+ }
94
+ switch (collection.outcome) {
95
+ case "delivered":
96
+ return storedResult(await acceptCollectedHandoff(record, collection.handoff));
97
+ case "spoiled":
98
+ return storedResult(settle(record, isExpiryError(collection.error) ? "expired" : "failed", collection.error.message));
99
+ case "gone":
100
+ return storedResult(collectGoneSession(record));
101
+ default:
102
+ return null;
103
+ }
104
+ }
105
+ function collectGoneSession(record) {
106
+ if (!record.collected)
107
+ return settle(record, "expired", GONE_MESSAGE);
108
+ const settled = lastSetupStatus;
109
+ if (settled?.seq === record.seq && settled.settled)
110
+ return settled;
111
+ return null;
112
+ }
113
+ async function acceptCollectedHandoff(record, handoff) {
114
+ record.collected = true;
115
+ if (pendingSetup !== record) {
116
+ return recordSetupStatus(record, "failed", SUPERSEDED_MESSAGE, true);
117
+ }
89
118
  try {
90
- const handoff = await completeBridgeHandoff(record.session);
91
- if (pendingSetup !== record)
92
- return;
93
119
  const receipt = await acceptBridgeHandoff({
94
120
  handoff,
95
121
  apiHost: record.apiHost,
@@ -98,31 +124,74 @@ async function completePendingSetup(record) {
98
124
  pendingTarget: record.invitedBy,
99
125
  expiresAt: record.session.expiresAt,
100
126
  });
101
- lastSetupStatus = {
102
- status: "completed",
103
- message: receipt,
104
- finishedAt: Date.now(),
105
- };
127
+ return settle(record, "completed", receipt);
106
128
  }
107
129
  catch (err) {
108
- if (pendingSetup !== record && !supersedesGenericExpiry(err))
109
- return;
110
- lastSetupStatus = {
111
- status: isExpiryError(err) ? "expired" : "failed",
112
- message: safeSetupErrorMessage(err),
113
- finishedAt: Date.now(),
114
- };
130
+ return settle(record, isExpiryError(err) ? "expired" : "failed", safeSetupErrorMessage(err));
131
+ }
132
+ }
133
+ function settle(record, status, message) {
134
+ if (pendingSetup === record)
135
+ pendingSetup = null;
136
+ return recordSetupStatus(record, status, message, true);
137
+ }
138
+ async function startNewSession(options) {
139
+ const existingConnectorUrl = await readExistingConnectorUrl();
140
+ const connectorUrl = resolveConnectorUrlForSetup({
141
+ apiHost: options.apiHost,
142
+ existingConnectorUrl,
143
+ configuredConnectorUrl: options.connectorUrl,
144
+ });
145
+ const session = await beginBridgeHandoff({
146
+ apiHost: options.apiHost,
147
+ idpBaseUrl: options.idpBaseUrl,
148
+ idpBaseUrlIsDefault: options.idpBaseUrlIsDefault,
149
+ });
150
+ const record = {
151
+ seq: ++setupSeq,
152
+ session,
153
+ apiHost: options.apiHost,
154
+ connectorUrl,
155
+ idpBaseUrl: options.idpBaseUrl,
156
+ invitedBy: options.invitedBy,
157
+ collected: false,
158
+ };
159
+ pendingSetup = record;
160
+ lastSetupStatus = null;
161
+ void completePendingSetup(record);
162
+ return pendingResult(record);
163
+ }
164
+ async function completePendingSetup(record) {
165
+ let handoff;
166
+ try {
167
+ handoff = await completeBridgeHandoff(record.session);
115
168
  }
116
- finally {
117
- if (pendingSetup === record) {
118
- pendingSetup = null;
119
- }
169
+ catch (err) {
170
+ recordSetupStatus(record, isExpiryError(err) ? "expired" : "failed", safeSetupErrorMessage(err), false);
171
+ return;
120
172
  }
173
+ await acceptCollectedHandoff(record, handoff);
121
174
  }
122
- function supersedesGenericExpiry(err) {
123
- return (err instanceof SetupFlowError &&
124
- pendingSetup === null &&
125
- lastSetupStatus?.status === "expired");
175
+ function recordSetupStatus(record, status, message, settled) {
176
+ const next = {
177
+ seq: record.seq,
178
+ status,
179
+ message,
180
+ reported: false,
181
+ settled,
182
+ };
183
+ if (!canRecord(next))
184
+ return null;
185
+ lastSetupStatus = next;
186
+ lastRecordedSeq = next.seq;
187
+ return next;
188
+ }
189
+ function canRecord(next) {
190
+ if (next.seq !== lastRecordedSeq)
191
+ return next.seq > lastRecordedSeq;
192
+ if (!lastSetupStatus)
193
+ return false;
194
+ return next.settled && !lastSetupStatus.settled;
126
195
  }
127
196
  function isExpiryError(err) {
128
197
  if (!(err instanceof SetupFlowError))
@@ -138,5 +207,6 @@ function safeSetupErrorMessage(err) {
138
207
  export function _resetChannelSetupForTesting() {
139
208
  pendingSetup = null;
140
209
  lastSetupStatus = null;
210
+ lastRecordedSeq = 0;
141
211
  }
142
212
  export const CHANNEL_SETUP_TTL_MS = HANDOFF_TIMEOUT_MS;
@@ -1 +1 @@
1
- {"version":3,"file":"connector-client.d.ts","sourceRoot":"","sources":["../src/connector-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connector-client.d.ts","sourceRoot":"","sources":["../src/connector-client.ts"],"names":[],"mappings":"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"}
@@ -476,8 +476,10 @@ export class ConnectorClient extends EventEmitter {
476
476
  this.registration = { state: "replaced" };
477
477
  const msg = "This agent was claimed by another Runtime " +
478
478
  "(this Runtime disconnected). " +
479
- "Re-run `openclaw channels login --channel agent-network` " +
480
- "to drive this agent from here again.";
479
+ "To drive this agent from here again, ask the owner to call " +
480
+ "masons_link with start_over: true in this conversation; as a last " +
481
+ "resort, `openclaw channels login --channel agent-network` in a " +
482
+ "terminal does the same.";
481
483
  this.emit("error", new Error(msg));
482
484
  if (wasRegistering) {
483
485
  this.rejectRegister(new Error(msg));
package/dist/handoff.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type KeyObject } from "node:crypto";
2
+ import { SetupFlowError } from "./handoff-deadline.js";
2
3
  export { SetupFlowError } from "./handoff-deadline.js";
3
4
  export declare const DEFAULT_IDP_BASE_URL = "https://preview.masons.ai";
4
5
  export declare const HANDOFF_TIMEOUT_MS: number;
@@ -24,4 +25,16 @@ export interface BeginBridgeHandoffOptions {
24
25
  }
25
26
  export declare function beginBridgeHandoff(options: BeginBridgeHandoffOptions): Promise<BridgeHandoffSession>;
26
27
  export declare function completeBridgeHandoff(session: BridgeHandoffSession): Promise<BridgeHandoffResult>;
28
+ export type BridgeCollection = {
29
+ outcome: "pending";
30
+ } | {
31
+ outcome: "gone";
32
+ } | {
33
+ outcome: "delivered";
34
+ handoff: BridgeHandoffResult;
35
+ } | {
36
+ outcome: "spoiled";
37
+ error: SetupFlowError;
38
+ };
39
+ export declare function collectBridgeHandoffOnce(session: BridgeHandoffSession): Promise<BridgeCollection>;
27
40
  //# sourceMappingURL=handoff.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handoff.d.ts","sourceRoot":"","sources":["../src/handoff.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,SAAS,EAEf,MAAM,aAAa,CAAC;AAQrB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAavD,eAAO,MAAM,oBAAoB,8BAA8B,CAAC;AAWhE,eAAO,MAAM,kBAAkB,QAAiB,CAAC;AA0BjD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1D;AAQD,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAElB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAOhB,UAAU,EAAE,SAAS,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IAMnB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAMD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,oBAAoB,CAAC,CAiE/B;AAKD,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CA0C9B"}
1
+ {"version":3,"file":"handoff.d.ts","sourceRoot":"","sources":["../src/handoff.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,SAAS,EAEf,MAAM,aAAa,CAAC;AAErB,OAAO,EAEL,cAAc,EACf,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAavD,eAAO,MAAM,oBAAoB,8BAA8B,CAAC;AAWhE,eAAO,MAAM,kBAAkB,QAAiB,CAAC;AA0BjD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1D;AAQD,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAElB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAUhB,UAAU,EAAE,SAAS,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IAMnB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAMD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,oBAAoB,CAAC,CAiE/B;AAKD,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAO9B;AAUD,MAAM,MAAM,gBAAgB,GACxB;IAAE,OAAO,EAAE,SAAS,CAAA;CAAE,GACtB;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GACnB;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,mBAAmB,CAAA;CAAE,GACtD;IAAE,OAAO,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,CAAC;AASlD,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CA8B3B"}
package/dist/handoff.js CHANGED
@@ -55,6 +55,44 @@ export async function beginBridgeHandoff(options) {
55
55
  }
56
56
  export async function completeBridgeHandoff(session) {
57
57
  const envelope = await pollForCompletion(session.apiBase, session.sessionId, session.expiresAt);
58
+ return readBridgeEnvelope(session, envelope);
59
+ }
60
+ export async function collectBridgeHandoffOnce(session) {
61
+ assertHandoffDeadlineActive(session.expiresAt);
62
+ const res = await apiFetch(pollUrl(session.apiBase, session.sessionId), {
63
+ method: "GET",
64
+ });
65
+ if (res.status === 404)
66
+ return { outcome: "gone" };
67
+ if (!res.ok)
68
+ return { outcome: "pending" };
69
+ let body;
70
+ try {
71
+ body = (await res.json());
72
+ }
73
+ catch (err) {
74
+ return { outcome: "spoiled", error: unreadableDelivery(err) };
75
+ }
76
+ if (body.status !== "completed" || !body.payload)
77
+ return { outcome: "pending" };
78
+ try {
79
+ return {
80
+ outcome: "delivered",
81
+ handoff: readBridgeEnvelope(session, body.payload),
82
+ };
83
+ }
84
+ catch (err) {
85
+ return {
86
+ outcome: "spoiled",
87
+ error: err instanceof SetupFlowError ? err : unreadableDelivery(err),
88
+ };
89
+ }
90
+ }
91
+ function unreadableDelivery(err) {
92
+ const detail = err instanceof Error ? err.message : "unknown error";
93
+ return new SetupFlowError(`The handoff bridge released this Link's runtime key but the response could not be read (${detail}), so the key was lost with it.`);
94
+ }
95
+ function readBridgeEnvelope(session, envelope) {
58
96
  let payload;
59
97
  try {
60
98
  payload = decryptBridgeEnvelope(session.privateKey, envelope);
@@ -81,12 +119,15 @@ export async function completeBridgeHandoff(session) {
81
119
  },
82
120
  };
83
121
  }
122
+ function pollUrl(apiBase, sessionId) {
123
+ return `${apiBase}/v1/cli-handoff/${encodeURIComponent(sessionId)}/poll`;
124
+ }
84
125
  async function pollForCompletion(apiBase, sessionId, expiresAt) {
85
126
  let lastNetworkError;
86
127
  while (Date.now() < expiresAt) {
87
128
  let res;
88
129
  try {
89
- res = await apiFetch(`${apiBase}/v1/cli-handoff/${encodeURIComponent(sessionId)}/poll`, { method: "GET" });
130
+ res = await apiFetch(pollUrl(apiBase, sessionId), { method: "GET" });
90
131
  }
91
132
  catch (err) {
92
133
  if (err instanceof PlatformNetworkError) {
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAUrE,OAAO,EACL,kCAAkC,EAEnC,MAAM,oCAAoC,CAAC;AA8D5C,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,mBAAmB,GAAE,mBAAmB,GAAG,IAA+B,GACzE,MAAM,GAAG,SAAS,CA6EpB;AAKD,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,YAAY,CACV,IAAI,EAAE,OAAO,EACb,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3C,IAAI,CAAC;IACR,yBAAyB,CAAC,MAAM,EAAE;QAChC,EAAE,EAAE,MAAM,CAAC;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,OAAO,kCAAkC,CAAC;KACrD,GAAG,IAAI,CAAC;IACT,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC;CACnE;AAqCD,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAaI,iBAAiB;CAoahC,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAUrE,OAAO,EACL,kCAAkC,EAEnC,MAAM,oCAAoC,CAAC;AA8D5C,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,mBAAmB,GAAE,mBAAmB,GAAG,IAA+B,GACzE,MAAM,GAAG,SAAS,CA6EpB;AAKD,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,YAAY,CACV,IAAI,EAAE,OAAO,EACb,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3C,IAAI,CAAC;IACR,yBAAyB,CAAC,MAAM,EAAE;QAChC,EAAE,EAAE,MAAM,CAAC;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,OAAO,kCAAkC,CAAC;KACrD,GAAG,IAAI,CAAC;IACT,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC;CACnE;AAqCD,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAaI,iBAAiB;CAsahC,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/plugin.js CHANGED
@@ -194,10 +194,12 @@ const plugin = {
194
194
  if (!state.hasCredentials) {
195
195
  dynamicContext =
196
196
  "[Context: Agent Network] You recently installed the agent network plugin " +
197
- "but it is not yet authorized. If your current channel sender is the verified " +
198
- "OpenClaw owner, call masons_link to generate a MASONS handoff URL here. " +
199
- "If owner authority is unavailable, tell your user to run this terminal fallback: " +
200
- "`openclaw channels login --channel agent-network`. Browser sign-in handles " +
197
+ "but it is not yet authorized. Link finishes in this conversation: if your " +
198
+ "current channel sender is the verified OpenClaw owner, call masons_link to " +
199
+ "generate a MASONS handoff URL here. If owner authority is unavailable here, " +
200
+ "ask the owner to start Link from a channel their OpenClaw is configured for; " +
201
+ "only as a last resort, `openclaw channels login --channel agent-network` in a " +
202
+ "terminal. Browser sign-in handles " +
201
203
  "agent selection or creation. After the Link completes, OpenClaw will reload and " +
202
204
  "the agent network tools will become available.";
203
205
  if (state.pendingTarget) {
package/dist/tools.d.ts CHANGED
@@ -26,7 +26,6 @@ interface ToolApi {
26
26
  }): void;
27
27
  }
28
28
  export declare function _resetToolsForTesting(): void;
29
- export declare function terminalSetupInstructions(): string;
30
29
  export declare function ownerRefusalText(reason: "known-non-owner" | "ambiguous"): string;
31
30
  export declare function formatChannelSetupResult(result: ChannelSetupResult): string;
32
31
  export declare function registerTools(api: ToolApi): void;
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAuBA,OAAO,EAEL,KAAK,kBAAkB,EAIxB,MAAM,oBAAoB,CAAC;AAuD5B,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,EAWlC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAC7D,IAAI,CAAC;CACT;AAkED,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AA6WD,wBAAgB,yBAAyB,IAAI,MAAM,CAiBlD;AAmBD,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,iBAAiB,GAAG,WAAW,GACtC,MAAM,CAqBR;AAGD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAoC3E;AAcD,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAmnChD"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAuBA,OAAO,EAEL,KAAK,kBAAkB,EAGxB,MAAM,oBAAoB,CAAC;AAuD5B,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,EAWlC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAC7D,IAAI,CAAC;CACT;AAkED,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AA6XD,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,iBAAiB,GAAG,WAAW,GACtC,MAAM,CAqBR;AAGD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAoC3E;AAyBD,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAmoChD"}
package/dist/tools.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import pluginManifest from "../openclaw.plugin.json" with { type: "json" };
3
3
  import { getOwnerPassportAddress } from "./channel.js";
4
- import { _resetChannelSetupForTesting, consumeChannelSetupStatus, getDefaultChannelSetupOptions, startOrGetChannelSetup, } from "./channel-setup.js";
4
+ import { _resetChannelSetupForTesting, getDefaultChannelSetupOptions, startOrGetChannelSetup, } from "./channel-setup.js";
5
5
  import { clearTargetHandle, extractNetworkConfig, getConnectorClient, getDmScope, getPendingTarget, hasApiKey, isProfileNeeded, markProfileComplete, removeIdentityLinks, requireApiKey, requireConversationManager, requirePlatformConfig, writeIdentityLinks, } from "./config.js";
6
6
  import { getOwnerHandle } from "./environment-context.js";
7
7
  import { classifyIdentityError, formatCanonicalNode, formatKindLabel, } from "./identity-format.js";
@@ -222,24 +222,6 @@ function toRecord(value) {
222
222
  function stringValue(value) {
223
223
  return typeof value === "string" && value.length > 0 ? value : undefined;
224
224
  }
225
- export function terminalSetupInstructions() {
226
- return [
227
- "Run this in the terminal where OpenClaw is installed:",
228
- "",
229
- " openclaw channels login --channel agent-network",
230
- "",
231
- "It will:",
232
- " 1. Display a MASONS handoff URL.",
233
- " 2. Open it in your browser and sign in.",
234
- " 3. Pick an existing agent or create a new one.",
235
- " 4. Return to the terminal after the encrypted handoff completes.",
236
- " 5. Persist credentials to openclaw.json. OpenClaw reloads the channel.",
237
- "",
238
- "Note: on an already-linked install this is a Relink — choosing another Node here changes",
239
- "which Node this OpenClaw install drives. Each install holds its own runtime key, so",
240
- "relinking updates this OpenClaw install's credentials.",
241
- ].join("\n");
242
- }
243
225
  export function ownerRefusalText(reason) {
244
226
  if (reason === "known-non-owner") {
245
227
  return [
@@ -269,6 +251,7 @@ export function formatChannelSetupResult(result) {
269
251
  result.handoffUrl,
270
252
  "",
271
253
  "Open this link in your browser, sign in, choose an agent, then come back here.",
254
+ "This Link is still open, so asking me again returns this same URL, not a new one — open the one above.",
272
255
  "After browser sign-in and agent selection, OpenClaw will receive the encrypted handoff, persist the runtime key locally, and reload the channel.",
273
256
  "This URL is sensitive Link material; only the OpenClaw owner should open it.",
274
257
  ].join("\n");
@@ -279,23 +262,26 @@ export function formatChannelSetupResult(result) {
279
262
  "",
280
263
  "HTTP tools may work before the Gateway WebSocket is connected.",
281
264
  'If the gateway tool is available, call it with action "restart" and reason "Activate Agent Network after Link". If not, ask the user to run `openclaw gateway restart`.',
265
+ "This result stands until the owner asks to start over: to re-link or change Node, call masons_link with start_over: true.",
282
266
  ].join("\n");
283
267
  }
284
268
  if (result.status === "expired") {
285
269
  return [
286
270
  result.message ?? "The previous Link URL expired.",
287
- "Ask again to generate a fresh owner-only Link URL, or use the terminal fallback:",
288
271
  "",
289
- terminalSetupInstructions(),
272
+ LINK_RETRY_GUIDANCE,
290
273
  ].join("\n");
291
274
  }
292
275
  return [
293
276
  result.message ?? "Agent Network Link failed.",
294
- "Ask again to retry, or use the terminal fallback:",
295
277
  "",
296
- terminalSetupInstructions(),
278
+ LINK_RETRY_GUIDANCE,
297
279
  ].join("\n");
298
280
  }
281
+ const LINK_RETRY_GUIDANCE = [
282
+ "Call masons_link again in this conversation and I will issue a fresh Link URL — the whole flow finishes here.",
283
+ "Last resort, only if that keeps failing: the owner can run `openclaw channels login --channel agent-network` in a terminal.",
284
+ ].join("\n");
299
285
  export function registerTools(api) {
300
286
  api.registerTool((ctx = {}) => ({
301
287
  name: "masons_link",
@@ -306,7 +292,12 @@ export function registerTools(api) {
306
292
  "Agent Network tools fail with a 'no credentials' error, or when the",
307
293
  "runtime owner wants a different MASONS agent driven by OpenClaw.",
308
294
  "On a Runtime that is already linked, present this to the user as",
309
- "Relink / Change Node — the same capability, not a second one.",
295
+ "Relink / Change Node — the same capability, not a second one. After a",
296
+ "Link has completed in this process, pass start_over: true to re-link",
297
+ "or link a different Node; a plain call re-reports the completed one.",
298
+ "To check on a Link in progress (after the owner opens the URL), call",
299
+ "with NO arguments — start_over would discard the pending Link and",
300
+ "issue a fresh URL instead of confirming the one the owner completed.",
310
301
  "",
311
302
  "Not to be confused with masons_link_identity: this tool binds a",
312
303
  "Runtime to a Node. masons_link_identity aliases your owner's identity",
@@ -333,6 +324,13 @@ export function registerTools(api) {
333
324
  "the plugin records this as a pending connection target so the " +
334
325
  "agent can propose a connection request automatically.",
335
326
  })),
327
+ start_over: Type.Optional(Type.Boolean({
328
+ description: "Discard whatever this Runtime is holding — a Link still in " +
329
+ "progress, or a Link that already completed — and issue a fresh " +
330
+ "Link URL. After a completed Link, pass start_over: true to " +
331
+ "re-link or link a different Node; without it a repeat call " +
332
+ "re-reports the completed result instead of starting again.",
333
+ })),
336
334
  }),
337
335
  execute: async (_id, params) => {
338
336
  const authority = resolveSetupAuthority();
@@ -342,15 +340,13 @@ export function registerTools(api) {
342
340
  const invitedBy = typeof params.invitedBy === "string" && params.invitedBy.length > 0
343
341
  ? params.invitedBy
344
342
  : undefined;
345
- const existing = consumeChannelSetupStatus();
346
343
  let result;
347
344
  try {
348
- result = existing
349
- ? existing
350
- : await startOrGetChannelSetup({
351
- ...getSetupOptionsFromToolContext(ctx),
352
- invitedBy,
353
- });
345
+ result = await startOrGetChannelSetup({
346
+ ...getSetupOptionsFromToolContext(ctx),
347
+ invitedBy,
348
+ startOver: params.start_over === true,
349
+ });
354
350
  }
355
351
  catch (err) {
356
352
  result = {
@@ -416,7 +412,7 @@ export function registerTools(api) {
416
412
  catch (err) {
417
413
  if (err instanceof PlatformApiError) {
418
414
  if (err.status === 401) {
419
- return textResult("Authentication failed. The runtime key may be invalid. Ask the user to run `openclaw channels login --channel agent-network`.");
415
+ return textResult("Authentication failed. The runtime key may be invalid. Ask the owner to re-link here: call masons_link with start_over: true.");
420
416
  }
421
417
  if (err.status === 422) {
422
418
  return textResult(`Validation error: ${err.message}`);
@@ -497,7 +493,7 @@ export function registerTools(api) {
497
493
  : "This Services deployment does not serve the connection-request endpoint. Nothing was sent.");
498
494
  }
499
495
  if (err.status === 401) {
500
- return textResult("Authentication failed. The runtime key may be invalid. Ask the user to run `openclaw channels login --channel agent-network`.");
496
+ return textResult("Authentication failed. The runtime key may be invalid. Ask the owner to re-link here: call masons_link with start_over: true.");
501
497
  }
502
498
  return textResult(`Connection request failed: ${err.message}`);
503
499
  }
@@ -581,7 +577,7 @@ export function registerTools(api) {
581
577
  catch (err) {
582
578
  if (err instanceof PlatformApiError) {
583
579
  if (err.status === 401) {
584
- return textResult("Authentication failed. The runtime key may be invalid. Ask the user to run `openclaw channels login --channel agent-network`.");
580
+ return textResult("Authentication failed. The runtime key may be invalid. Ask the owner to re-link here: call masons_link with start_over: true.");
585
581
  }
586
582
  if (err.status === 404) {
587
583
  return textResult("This Services deployment does not serve the packaged connection-request endpoint. Nothing was read.");
@@ -769,7 +765,7 @@ export function registerTools(api) {
769
765
  catch (err) {
770
766
  if (err instanceof PlatformApiError) {
771
767
  if (err.status === 401) {
772
- return textResult("Authentication failed. The runtime key may be invalid. Ask the user to run `openclaw channels login --channel agent-network`.");
768
+ return textResult("Authentication failed. The runtime key may be invalid. Ask the owner to re-link here: call masons_link with start_over: true.");
773
769
  }
774
770
  return textResult(`Failed to list connections: ${err.message}`);
775
771
  }
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const PLUGIN_VERSION = "0.6.24";
1
+ export declare const PLUGIN_VERSION = "0.6.25";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const PLUGIN_VERSION = "0.6.24";
1
+ export const PLUGIN_VERSION = "0.6.25";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@masons/agent-network",
3
- "version": "0.6.24",
3
+ "version": "0.6.25",
4
4
  "description": "MASONS Agent Network — OpenClaw channel plugin for connecting agent runtimes to the Agent Network over MSTP.",
5
5
  "license": "MIT",
6
6
  "author": "MASONS.ai <hello@masons.ai> (https://masons.ai)",
@@ -71,7 +71,7 @@ Check your current state and go to the right section:
71
71
 
72
72
  ## Link
73
73
 
74
- Binding this runtime to an agent Node takes about a minute. Link can run from an owner-verified channel through an encrypted browser handoff. Terminal login remains the fallback when owner authority is unavailable.
74
+ Binding this runtime to an agent Node takes about a minute. Link runs from an owner-verified channel through an encrypted browser handoff, and it finishes **in this conversation** — never send the owner to a terminal as your first move. Terminal login is the last resort, for when owner authority is unavailable here or Link keeps failing here.
75
75
 
76
76
  On a runtime that is already linked, this same flow is a **Relink / Change Node** — tell the user that, and use the same tool. There is no second capability.
77
77
 
@@ -83,11 +83,7 @@ On a runtime that is already linked, this same flow is a **Relink / Change Node*
83
83
 
84
84
  If the current sender is verified as the OpenClaw owner, the tool returns a MASONS handoff URL. Share the URL as ordinary chat text, on its own line, without wrapping it in backticks or a code block. Never ask for or display runtime keys, ciphertext payloads, decrypted token metadata, private key material, or debug output that looks credential-like.
85
85
 
86
- If owner authority is missing, false, or ambiguous, the tool returns the terminal fallback. Tell the user to run:
87
-
88
- ```sh
89
- openclaw channels login --channel agent-network
90
- ```
86
+ If owner authority is missing, false, or ambiguous, the tool refuses and explains why — relay that text. Where it names `openclaw channels login --channel agent-network`, that is the last resort for an owner with no channel their OpenClaw is configured for; try such a channel first.
91
87
 
92
88
  ### Step 2: User Completes Browser Handoff
93
89
 
@@ -97,7 +93,7 @@ The browser handoff page lets the owner sign in, select or create an agent, and
97
93
 
98
94
  After completion:
99
95
 
100
- 1. Call `masons_link` again to check whether the encrypted handoff completed.
96
+ 1. Call `masons_link` again to check whether the encrypted handoff completed. This call collects the owner's browser selection itself, so a step they already finished is confirmed here rather than costing a second browser walk. While the Link is still open the same call returns the SAME URL — pass it on, do not describe it as a new one.
101
97
  2. If `masons_link` reports the Link completed, restart the Gateway to activate realtime messaging:
102
98
  - Prefer the host `gateway` tool with `action`: `"restart"` and `reason`: `"Activate Agent Network after Link"`.
103
99
  - If the `gateway` tool is unavailable, tell the user to run `openclaw gateway restart`.
@@ -110,10 +106,13 @@ After completion:
110
106
 
111
107
  ### Errors
112
108
 
113
- - If any tool returns an error, explain it simply and suggest next steps.
114
- - If the CLI says a handle is rejected or taken, ask the user to retry the login command and choose a different handle.
109
+ - If `masons_link` reports the Link expired or failed, that session is over: call `masons_link` again in this conversation and it issues a fresh Link URL. Do not offer the terminal on a first failure.
110
+ - If `masons_link` reports the Link completed and the user wants to re-link or change Node, call it with `start_over: true` a plain call re-reports the completed result.
111
+ - Only after Link has failed here repeatedly, name `openclaw channels login --channel agent-network` as the last resort, and say so.
112
+ - If any other tool returns an error, explain it simply and suggest next steps.
113
+ - If the CLI says a handle is rejected or taken, ask the user to retry and choose a different handle.
115
114
  - If network errors, suggest checking internet connection.
116
- - If the tools still report no runtime key after login, read `references/troubleshooting.md`.
115
+ - If the tools still report no runtime key after a completed Link, read `references/troubleshooting.md`.
117
116
 
118
117
  ## Profile
119
118