@masons/agent-network 0.5.13 → 0.5.15
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.
- package/dist/channel.d.ts +0 -7
- package/dist/channel.d.ts.map +1 -1
- package/dist/channel.js +3 -174
- package/dist/cli-setup.d.ts +0 -93
- package/dist/cli-setup.d.ts.map +1 -1
- package/dist/cli-setup.js +124 -741
- package/dist/config-fs.d.ts +4 -0
- package/dist/config-fs.d.ts.map +1 -0
- package/dist/config-fs.js +23 -0
- package/dist/config-schema.js +2 -2
- package/dist/config.d.ts +2 -210
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +14 -334
- package/dist/connector-client.d.ts +0 -32
- package/dist/connector-client.d.ts.map +1 -1
- package/dist/connector-client.js +1 -89
- package/dist/constants.d.ts +0 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -3
- package/dist/conversation-manager.d.ts +0 -106
- package/dist/conversation-manager.d.ts.map +1 -1
- package/dist/conversation-manager.js +2 -131
- package/dist/environment-context.d.ts +0 -24
- package/dist/environment-context.d.ts.map +1 -1
- package/dist/environment-context.js +0 -42
- package/dist/handle-utils.d.ts +0 -14
- package/dist/handle-utils.d.ts.map +1 -1
- package/dist/handle-utils.js +0 -14
- package/dist/index.js +0 -9
- package/dist/owner-notes.d.ts +0 -33
- package/dist/owner-notes.d.ts.map +1 -1
- package/dist/owner-notes.js +2 -41
- package/dist/owner-session-state.d.ts +0 -26
- package/dist/owner-session-state.d.ts.map +1 -1
- package/dist/owner-session-state.js +0 -37
- package/dist/platform-client.d.ts +13 -202
- package/dist/platform-client.d.ts.map +1 -1
- package/dist/platform-client.js +22 -171
- package/dist/plugin.d.ts +5 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +3 -167
- package/dist/sent-message-buffer.d.ts +0 -36
- package/dist/sent-message-buffer.d.ts.map +1 -1
- package/dist/sent-message-buffer.js +1 -45
- package/dist/tools.d.ts +0 -28
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +36 -240
- package/dist/turn-context.d.ts +0 -45
- package/dist/turn-context.d.ts.map +1 -1
- package/dist/turn-context.js +0 -57
- package/dist/types.d.ts +0 -67
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -7
- package/dist/update-cache.d.ts +0 -17
- package/dist/update-cache.d.ts.map +1 -1
- package/dist/update-cache.js +1 -21
- package/dist/update-check.d.ts +1 -40
- package/dist/update-check.d.ts.map +1 -1
- package/dist/update-check.js +7 -66
- package/dist/version.d.ts +1 -2
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -2
- package/openclaw.plugin.json +57 -3
- package/package.json +11 -10
- package/skills/agent-network/SKILL.md +21 -47
- package/skills/agent-network/references/troubleshooting.md +5 -5
package/dist/channel.d.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import type { AgentNetworkAccount } from "./config-schema.js";
|
|
2
|
-
/** Get the stored owner Passport address (first is_owner visit). */
|
|
3
2
|
export declare function getOwnerPassportAddress(): string | null;
|
|
4
|
-
/**
|
|
5
|
-
* Consume the identity linking nudge flag. Destructive read — returns true
|
|
6
|
-
* at most once per detection cycle. Called by before_prompt_build on
|
|
7
|
-
* non-Passport owner turns to deliver the nudge where the action is relevant.
|
|
8
|
-
*/
|
|
9
3
|
export declare function consumeIdentityLinkingNudge(): boolean;
|
|
10
|
-
/** Test-only: set the nudge flag without going through the detection path. */
|
|
11
4
|
export declare function _setNudgePendingForTesting(value: boolean): void;
|
|
12
5
|
interface ChannelMeta {
|
|
13
6
|
name: string;
|
package/dist/channel.d.ts.map
CHANGED
|
@@ -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;
|
|
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;AAuDD,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;AA4WD,eAAO,MAAM,mBAAmB,EAAE,yBAyNjC,CAAC"}
|
package/dist/channel.js
CHANGED
|
@@ -9,79 +9,25 @@ import { clearOwnerState, isOwnerAddress, markOwnerAddress, setCurrentTurnIsOwne
|
|
|
9
9
|
import { setCurrentTurnSender } from "./turn-context.js";
|
|
10
10
|
import { checkForUpdate } from "./update-check.js";
|
|
11
11
|
const dbg = createDebug("agent-network:channel");
|
|
12
|
-
// Cross-channel identity notification: once per startAccount lifecycle.
|
|
13
|
-
// Reset on startAccount(). isIdentityLinksConfigured() provides the
|
|
14
|
-
// cross-restart idempotency (checks config file).
|
|
15
12
|
let notificationAttempted = false;
|
|
16
|
-
// Identity linking nudge — set when the owner first visits via Passport and
|
|
17
|
-
// cross-channel linking is available. Delivered on the owner's next
|
|
18
|
-
// non-Passport turn (Telegram/Feishu) where the linking action is relevant.
|
|
19
|
-
// Separate from ownerNotesQueue to avoid the Passport drain deadlock (#1158):
|
|
20
|
-
// the queue drains on ALL owner turns (including Passport), but this nudge
|
|
21
|
-
// only fires on non-Passport owner turns.
|
|
22
13
|
let identityLinkingNudgePending = false;
|
|
23
|
-
// Owner's Passport address: recorded on first is_owner MESSAGE_RECEIVED.
|
|
24
|
-
// Used by masons_link_identity tool to auto-include the Passport entry.
|
|
25
14
|
let storedOwnerPassportAddress = null;
|
|
26
|
-
/** Get the stored owner Passport address (first is_owner visit). */
|
|
27
15
|
export function getOwnerPassportAddress() {
|
|
28
16
|
return storedOwnerPassportAddress;
|
|
29
17
|
}
|
|
30
|
-
/**
|
|
31
|
-
* Consume the identity linking nudge flag. Destructive read — returns true
|
|
32
|
-
* at most once per detection cycle. Called by before_prompt_build on
|
|
33
|
-
* non-Passport owner turns to deliver the nudge where the action is relevant.
|
|
34
|
-
*/
|
|
35
18
|
export function consumeIdentityLinkingNudge() {
|
|
36
19
|
if (!identityLinkingNudgePending)
|
|
37
20
|
return false;
|
|
38
21
|
identityLinkingNudgePending = false;
|
|
39
22
|
return true;
|
|
40
23
|
}
|
|
41
|
-
/** Test-only: set the nudge flag without going through the detection path. */
|
|
42
24
|
export function _setNudgePendingForTesting(value) {
|
|
43
25
|
identityLinkingNudgePending = value;
|
|
44
26
|
}
|
|
45
|
-
// --- Sender identity ---
|
|
46
|
-
/**
|
|
47
|
-
* Project a NodeRef view from `MESSAGE_RECEIVED.metadata.sender` when the
|
|
48
|
-
* Connector has projected a NodeRef onto the inbound envelope (Full Node
|
|
49
|
-
* sender path per `docs/concepts/human-node-capability-obligations.md § 2`
|
|
50
|
-
* + `apps/connector/src/routes/root.ts` § Piece 2 projection).
|
|
51
|
-
*
|
|
52
|
-
* Wire shape (the Connector's `metadata.sender = senderNodeRef` projection):
|
|
53
|
-
* ```
|
|
54
|
-
* metadata: {
|
|
55
|
-
* visitor_name?: string; // legacy back-compat
|
|
56
|
-
* is_owner?: boolean; // legacy back-compat
|
|
57
|
-
* client_type?: string; // legacy back-compat
|
|
58
|
-
* sender?: { // NEW under (b) Full Node
|
|
59
|
-
* kind: string;
|
|
60
|
-
* mstpAddress: string;
|
|
61
|
-
* handle?: string;
|
|
62
|
-
* displayName?: string;
|
|
63
|
-
* avatarUrl?: string;
|
|
64
|
-
* };
|
|
65
|
-
* }
|
|
66
|
-
* ```
|
|
67
|
-
*
|
|
68
|
-
* Returns the NodeRef view when `metadata.sender` is an object with
|
|
69
|
-
* `kind` and `mstpAddress` both string. Returns null otherwise — the
|
|
70
|
-
* caller falls back to the Client-side metadata shape (`visitor_name`,
|
|
71
|
-
* `is_owner`, `client_type`).
|
|
72
|
-
*
|
|
73
|
-
* Per `human-node-capability-obligations.md § 5`, consumers MUST accept
|
|
74
|
-
* future `kind` values (e.g., `"team"`); this projector treats `kind` as
|
|
75
|
-
* opaque and surfaces the value as-is.
|
|
76
|
-
*/
|
|
77
27
|
function tryProjectNodeRef(metadata) {
|
|
78
28
|
if (!metadata)
|
|
79
29
|
return null;
|
|
80
30
|
const sender = metadata.sender;
|
|
81
|
-
// Defensive: reject non-objects, null, and arrays. `typeof [] === "object"`
|
|
82
|
-
// and `[] !== null`, so an array would otherwise pass the next guard and
|
|
83
|
-
// produce undefined when indexed by `kind` / `mstpAddress`. Explicit
|
|
84
|
-
// rejection makes the contract self-documenting.
|
|
85
31
|
if (typeof sender !== "object" || sender === null || Array.isArray(sender))
|
|
86
32
|
return null;
|
|
87
33
|
const senderObj = sender;
|
|
@@ -96,36 +42,13 @@ function tryProjectNodeRef(metadata) {
|
|
|
96
42
|
const avatarUrl = typeof senderObj.avatarUrl === "string" ? senderObj.avatarUrl : undefined;
|
|
97
43
|
return { kind, mstpAddress, handle, displayName, avatarUrl };
|
|
98
44
|
}
|
|
99
|
-
/**
|
|
100
|
-
* Derive the sender display name for the visitor / Identified-Client /
|
|
101
|
-
* anonymous path (no NodeRef projection on metadata).
|
|
102
|
-
*
|
|
103
|
-
* Resolution order:
|
|
104
|
-
* 1. `metadata.visitor_name` — display name for Passport browser visitors
|
|
105
|
-
* 2. `event.from` — extract handle from routable address
|
|
106
|
-
*/
|
|
107
45
|
function deriveSenderName(event) {
|
|
108
|
-
// Account-level identity: Passport visitor with resolved display name
|
|
109
46
|
const visitorName = event.metadata?.visitor_name;
|
|
110
47
|
if (visitorName && typeof visitorName === "string") {
|
|
111
48
|
return visitorName;
|
|
112
49
|
}
|
|
113
|
-
// Extract handle from `from` address
|
|
114
50
|
return extractHandleFromAddress(event.from);
|
|
115
51
|
}
|
|
116
|
-
/**
|
|
117
|
-
* Derive the sender display name for the Full Node sender path. Walks the
|
|
118
|
-
* fallback chain `displayName → handle → mstpAddress`-derived handle per
|
|
119
|
-
* `docs/concepts/human-node-capability-obligations.md § 2`.
|
|
120
|
-
*
|
|
121
|
-
* Note: the spec's terminal fallback is `mstpAddress` itself. This
|
|
122
|
-
* implementation uses `extractHandleFromAddress(fromAddress)` instead —
|
|
123
|
-
* the `from` field is already a routable address (the same one
|
|
124
|
-
* `mstpAddress` would parse to a handle from), and a parsed handle is
|
|
125
|
-
* higher-fidelity for display than the full URI. When `nodeRef.handle`
|
|
126
|
-
* is absent and `from` does not parse to a handle, both forms degrade
|
|
127
|
-
* equivalently to "unknown"-style strings.
|
|
128
|
-
*/
|
|
129
52
|
function deriveNodeName(nodeRef, fromAddress) {
|
|
130
53
|
if (nodeRef.displayName)
|
|
131
54
|
return nodeRef.displayName;
|
|
@@ -133,28 +56,20 @@ function deriveNodeName(nodeRef, fromAddress) {
|
|
|
133
56
|
return nodeRef.handle;
|
|
134
57
|
return extractHandleFromAddress(fromAddress);
|
|
135
58
|
}
|
|
136
|
-
/**
|
|
137
|
-
* Extract the Connector host from a connectorUrl.
|
|
138
|
-
* `wss://preview.masons.ai/gw` -> `preview.masons.ai`
|
|
139
|
-
*/
|
|
140
59
|
function extractConnectorHost(connectorUrl) {
|
|
141
60
|
try {
|
|
142
61
|
const url = new URL(connectorUrl);
|
|
143
62
|
return url.hostname;
|
|
144
63
|
}
|
|
145
64
|
catch {
|
|
146
|
-
// Fallback: strip protocol and path
|
|
147
65
|
return connectorUrl.replace(/^wss?:\/\//, "").replace(/\/.*$/, "");
|
|
148
66
|
}
|
|
149
67
|
}
|
|
150
|
-
// --- Duplicate message protection for at-least-once delivery ---
|
|
151
|
-
// Short-window dedup: stored messages re-delivered on reconnect are
|
|
152
|
-
// suppressed if the same (from, content, sentAt) was seen within 60 seconds.
|
|
153
68
|
const DEDUP_WINDOW_MS = 60_000;
|
|
154
|
-
const recentMessages = new Map();
|
|
69
|
+
const recentMessages = new Map();
|
|
155
70
|
function isDuplicate(from, content, sentAt) {
|
|
156
71
|
if (!sentAt)
|
|
157
|
-
return false;
|
|
72
|
+
return false;
|
|
158
73
|
const key = `${from}\0${content}\0${sentAt}`;
|
|
159
74
|
const now = Date.now();
|
|
160
75
|
const seen = recentMessages.get(key);
|
|
@@ -162,7 +77,6 @@ function isDuplicate(from, content, sentAt) {
|
|
|
162
77
|
return true;
|
|
163
78
|
}
|
|
164
79
|
recentMessages.set(key, now);
|
|
165
|
-
// Lazy cleanup: remove expired entries when map grows large
|
|
166
80
|
if (recentMessages.size > 200) {
|
|
167
81
|
for (const [k, ts] of recentMessages) {
|
|
168
82
|
if (now - ts > DEDUP_WINDOW_MS)
|
|
@@ -171,58 +85,25 @@ function isDuplicate(from, content, sentAt) {
|
|
|
171
85
|
}
|
|
172
86
|
return false;
|
|
173
87
|
}
|
|
174
|
-
// --- Message dispatch helpers ---
|
|
175
|
-
/**
|
|
176
|
-
* Handle an address-based MESSAGE_RECEIVED { from, content, metadata }.
|
|
177
|
-
* This is the primary path for the address-based protocol.
|
|
178
|
-
*/
|
|
179
88
|
async function handleAddressedMessage(event, conversationManager, client, ctx) {
|
|
180
89
|
const fromAddress = event.from;
|
|
181
90
|
dbg("routing inbound message from=%s contentLength=%d", fromAddress, event.content.length);
|
|
182
|
-
// Duplicate protection for at-least-once re-delivery (stored messages).
|
|
183
91
|
if (isDuplicate(fromAddress, event.content, event.sentAt)) {
|
|
184
92
|
dbg("duplicate message suppressed from=%s sentAt=%s", fromAddress, event.sentAt);
|
|
185
93
|
return;
|
|
186
94
|
}
|
|
187
|
-
// Sender shape disambiguation per `docs/openclaw/program-demo-runbook.md § 4.2`:
|
|
188
|
-
// - Full Node sender (Node ↔ Node): `metadata.sender` is a NodeRef
|
|
189
|
-
// (`kind`, `mstpAddress`, optional `handle` / `displayName` /
|
|
190
|
-
// `avatarUrl`) per `docs/concepts/human-node-capability-obligations.md
|
|
191
|
-
// § 2` + Connector Piece 2 projection at
|
|
192
|
-
// `apps/connector/src/routes/root.ts`.
|
|
193
|
-
// Right-Fix lock per #1455 issuecomment-4354081096: route via standard
|
|
194
|
-
// Node-peer path; do NOT consult `metadata.is_owner`. `is_owner` is an
|
|
195
|
-
// ownership signal, not a routing signal — owner-self-chat walks the
|
|
196
|
-
// same wire as stranger-chat.
|
|
197
|
-
// - Visitor / Identified Client / Anonymous (Client → Node):
|
|
198
|
-
// `metadata.sender` is absent; only `visitor_name`, `is_owner`,
|
|
199
|
-
// `client_type` per
|
|
200
|
-
// `docs/concepts/client-to-node-capability-obligations.md § 2`.
|
|
201
|
-
// Existing visitor-mode owner-routing applies.
|
|
202
95
|
const nodeRef = tryProjectNodeRef(event.metadata);
|
|
203
96
|
if (nodeRef) {
|
|
204
|
-
// Full Node sender — standard Node-peer path. Skip is_owner consultation,
|
|
205
|
-
// owner-address marking, and the identity-linking nudge (those all key off
|
|
206
|
-
// the visitor-mode owner concept).
|
|
207
97
|
setCurrentTurnIsOwner(false);
|
|
208
98
|
dbg("Full Node sender: kind=%s mstpAddress=%s handle=%s", nodeRef.kind, nodeRef.mstpAddress, nodeRef.handle ?? "(absent)");
|
|
209
99
|
}
|
|
210
100
|
else {
|
|
211
|
-
// Visitor-mode path — preserved behavior.
|
|
212
|
-
// Owner detection: first-message-sets, subsequent-inherits.
|
|
213
|
-
// Connector includes is_owner in metadata for Passport visitors.
|
|
214
101
|
if (event.metadata?.is_owner === true) {
|
|
215
102
|
markOwnerAddress(fromAddress);
|
|
216
|
-
// Record Passport address for tool auto-include (first visit only)
|
|
217
103
|
if (!storedOwnerPassportAddress) {
|
|
218
104
|
storedOwnerPassportAddress = fromAddress;
|
|
219
105
|
dbg("recorded owner Passport address: %s", fromAddress);
|
|
220
106
|
}
|
|
221
|
-
// --- dmScope-aware identity linking nudge (#986 redesign, #1158 fix) ---
|
|
222
|
-
// Detect that identity linking is available but not configured.
|
|
223
|
-
// Sets a flag (NOT enqueued into ownerNotesQueue) for delivery on the
|
|
224
|
-
// owner's next non-Passport turn, where the linking action is relevant.
|
|
225
|
-
// Separated from the queue to avoid the Passport drain deadlock (#1158).
|
|
226
107
|
if (getDmScope() !== "main" &&
|
|
227
108
|
!isIdentityLinksConfigured() &&
|
|
228
109
|
!notificationAttempted) {
|
|
@@ -233,7 +114,6 @@ async function handleAddressedMessage(event, conversationManager, client, ctx) {
|
|
|
233
114
|
}
|
|
234
115
|
setCurrentTurnIsOwner(isOwnerAddress(fromAddress));
|
|
235
116
|
}
|
|
236
|
-
// Resolve contact from address. Register inbound conversation if new.
|
|
237
117
|
const contact = nodeRef
|
|
238
118
|
? deriveNodeName(nodeRef, fromAddress)
|
|
239
119
|
: deriveSenderName(event);
|
|
@@ -241,7 +121,6 @@ async function handleAddressedMessage(event, conversationManager, client, ctx) {
|
|
|
241
121
|
conversationManager.registerInbound(contact, fromAddress);
|
|
242
122
|
}
|
|
243
123
|
setCurrentTurnSender(conversationManager.getContactByAddress(fromAddress) ?? null);
|
|
244
|
-
// Runtime guard
|
|
245
124
|
let runtime;
|
|
246
125
|
try {
|
|
247
126
|
runtime = requirePluginRuntime();
|
|
@@ -254,7 +133,6 @@ async function handleAddressedMessage(event, conversationManager, client, ctx) {
|
|
|
254
133
|
dbg("channelRuntime not available, dropping message from=%s", fromAddress);
|
|
255
134
|
return;
|
|
256
135
|
}
|
|
257
|
-
// senderId for OpenClaw routing: use the routable address
|
|
258
136
|
const senderId = fromAddress;
|
|
259
137
|
const senderName = contact;
|
|
260
138
|
try {
|
|
@@ -297,7 +175,6 @@ async function handleAddressedMessage(event, conversationManager, client, ctx) {
|
|
|
297
175
|
console.error(`[agent-network:${ctx.accountId}] session record error:`, err);
|
|
298
176
|
},
|
|
299
177
|
});
|
|
300
|
-
// Typing: address-based
|
|
301
178
|
client.sendTyping(fromAddress, true);
|
|
302
179
|
try {
|
|
303
180
|
await runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
@@ -323,9 +200,6 @@ async function handleAddressedMessage(event, conversationManager, client, ctx) {
|
|
|
323
200
|
}
|
|
324
201
|
finally {
|
|
325
202
|
client.sendTyping(fromAddress, false);
|
|
326
|
-
// Commit deferred deletions from end_conversation calls made during
|
|
327
|
-
// this dispatch cycle. Must run after text buffer flush so that
|
|
328
|
-
// hasConversation() returns true for the deliver callback (#999).
|
|
329
203
|
conversationManager.commitEndedConversations();
|
|
330
204
|
}
|
|
331
205
|
}
|
|
@@ -334,7 +208,6 @@ async function handleAddressedMessage(event, conversationManager, client, ctx) {
|
|
|
334
208
|
}
|
|
335
209
|
}
|
|
336
210
|
const accounts = new Map();
|
|
337
|
-
// --- Channel Plugin ---
|
|
338
211
|
export const agentNetworkChannel = {
|
|
339
212
|
id: "agent-network",
|
|
340
213
|
meta: {
|
|
@@ -349,9 +222,6 @@ export const agentNetworkChannel = {
|
|
|
349
222
|
attachments: false,
|
|
350
223
|
},
|
|
351
224
|
config: {
|
|
352
|
-
// Always return ["default"] — matches community convention (Telegram,
|
|
353
|
-
// Slack, Feishu all do this). Gateway uses isConfigured() to decide
|
|
354
|
-
// whether to start the account, not the presence of account IDs.
|
|
355
225
|
listAccountIds() {
|
|
356
226
|
return ["default"];
|
|
357
227
|
},
|
|
@@ -378,9 +248,6 @@ export const agentNetworkChannel = {
|
|
|
378
248
|
token: account.token,
|
|
379
249
|
};
|
|
380
250
|
},
|
|
381
|
-
// Gateway calls this after resolveAccount(). Return false when
|
|
382
|
-
// credentials are missing — Gateway marks the channel as "unmanaged"
|
|
383
|
-
// and skips health checks + startAccount().
|
|
384
251
|
isConfigured(account) {
|
|
385
252
|
return account.connectorUrl !== "" && account.token !== "";
|
|
386
253
|
},
|
|
@@ -392,8 +259,6 @@ export const agentNetworkChannel = {
|
|
|
392
259
|
outbound: {
|
|
393
260
|
deliveryMode: "direct",
|
|
394
261
|
async sendText(ctx) {
|
|
395
|
-
// ctx.to is the routable address (senderId from inbound dispatch).
|
|
396
|
-
// ConversationManager.send() handles address resolution transparently.
|
|
397
262
|
const state = accounts.get(ctx.accountId);
|
|
398
263
|
if (!state)
|
|
399
264
|
return { channel: "agent-network", messageId: "" };
|
|
@@ -408,14 +273,9 @@ export const agentNetworkChannel = {
|
|
|
408
273
|
gateway: {
|
|
409
274
|
async startAccount(ctx) {
|
|
410
275
|
dbg("starting account %s", ctx.accountId);
|
|
411
|
-
// Reset per-session state
|
|
412
276
|
notificationAttempted = false;
|
|
413
277
|
identityLinkingNudgePending = false;
|
|
414
278
|
storedOwnerPassportAddress = null;
|
|
415
|
-
// Defensive: clear stale singletons from a previous cycle.
|
|
416
|
-
// The abort handler intentionally leaves them alive for in-flight tool
|
|
417
|
-
// calls (#935). Clean up here at cycle start so initConnectorClient()
|
|
418
|
-
// doesn't throw its double-init guard.
|
|
419
279
|
clearConversationManager();
|
|
420
280
|
clearConnectorClient();
|
|
421
281
|
const { connectorUrl, token } = ctx.account;
|
|
@@ -427,64 +287,33 @@ export const agentNetworkChannel = {
|
|
|
427
287
|
conversationManager,
|
|
428
288
|
};
|
|
429
289
|
accounts.set(ctx.accountId, state);
|
|
430
|
-
// --- Inbound message routing (4-step channelRuntime dispatch) ---
|
|
431
|
-
// Replaces the non-existent routeMessage() API. Uses PluginRuntime
|
|
432
|
-
// captured in register() via config.ts module-level singleton.
|
|
433
|
-
// Reference: DingTalk plugin (github.com/soimy/openclaw-channel-dingtalk).
|
|
434
|
-
//
|
|
435
|
-
// Concurrency model: this handler is async but EventEmitter fires it
|
|
436
|
-
// synchronously per event. Multiple MESSAGE_RECEIVED on the same session
|
|
437
|
-
// produce concurrent dispatches — replies may arrive out of order.
|
|
438
|
-
// Acceptable for unstructured text conversation (Phase 1). If MSTP adds
|
|
439
|
-
// structured multi-step interactions, add a per-session dispatch queue.
|
|
440
290
|
client.on("message_received", async (event) => {
|
|
441
291
|
if (!("from" in event))
|
|
442
|
-
return;
|
|
292
|
+
return;
|
|
443
293
|
await handleAddressedMessage(event, conversationManager, client, ctx);
|
|
444
294
|
});
|
|
445
|
-
// --- Delivery ACK ---
|
|
446
|
-
// ACK immediately on DELIVERY_PENDING — tells Connector it's safe
|
|
447
|
-
// to delete delivered stored messages.
|
|
448
295
|
client.on("delivery_pending", (event) => {
|
|
449
296
|
client.ackDelivery(event.upTo);
|
|
450
297
|
dbg("delivery_pending → ACK sent count=%d upTo=%s", event.count, event.upTo);
|
|
451
298
|
});
|
|
452
|
-
// --- Disconnect ---
|
|
453
|
-
// On disconnect, all session state is invalid — clear owner + environment.
|
|
454
299
|
client.on("disconnected", () => {
|
|
455
300
|
clearOwnerState();
|
|
456
301
|
clearEnvironmentContext();
|
|
457
302
|
});
|
|
458
|
-
// --- Error handling ---
|
|
459
|
-
// Must register before connect() — unhandled "error" events crash the process.
|
|
460
303
|
client.on("error", (err) => {
|
|
461
304
|
console.error(`[agent-network:${ctx.accountId}]`, err.message);
|
|
462
305
|
});
|
|
463
|
-
// --- Abort signal ---
|
|
464
|
-
// Do NOT clear ConversationManager or ConnectorClient here.
|
|
465
|
-
// In-flight LLM dispatches may still be running — tools that call
|
|
466
|
-
// requireConversationManager() would throw "Not connected" and trigger
|
|
467
|
-
// LLM confidence collapse (#935). Instead, let the stale CM survive:
|
|
468
|
-
// the disconnected WebSocket causes tools to graceful-fail ("Failed to
|
|
469
|
-
// send message") rather than throw. stopAccount() handles final cleanup.
|
|
470
306
|
ctx.abortSignal.addEventListener("abort", () => {
|
|
471
307
|
client.disconnect();
|
|
472
308
|
}, { once: true });
|
|
473
|
-
// --- Inject config for LLM tools ---
|
|
474
309
|
initToolConfig(ctx.cfg);
|
|
475
|
-
// --- Check for plugin updates (non-blocking) ---
|
|
476
310
|
const networkCfg = extractNetworkConfig(ctx.cfg);
|
|
477
311
|
const updateCheckEnabled = networkCfg?.updateCheck !== false;
|
|
478
312
|
checkForUpdate(updateCheckEnabled).catch(() => { });
|
|
479
|
-
// --- Connect ---
|
|
480
313
|
await client.connect();
|
|
481
314
|
dbg("connected account=%s", ctx.accountId);
|
|
482
|
-
// --- Expose client and conversation manager to tools (only after WS is connected) ---
|
|
483
315
|
initConnectorClient(client);
|
|
484
316
|
initConversationManager(conversationManager);
|
|
485
|
-
// Park the Promise — DO NOT resolve/return.
|
|
486
|
-
// Resolution signals "account stopped" to OpenClaw's orchestrator,
|
|
487
|
-
// triggering auto-restart with exponential backoff (crash-loop).
|
|
488
317
|
await new Promise((resolve) => {
|
|
489
318
|
if (ctx.abortSignal.aborted) {
|
|
490
319
|
resolve();
|
package/dist/cli-setup.d.ts
CHANGED
|
@@ -1,65 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI Login Path — Stripe-style browser-mediated loopback handoff.
|
|
3
|
-
*
|
|
4
|
-
* Implements `login()` hook for OpenClaw's 2026.4.x channel-plugin contract:
|
|
5
|
-
* api.registerChannel({ plugin: { ..., auth: { login } } })
|
|
6
|
-
*
|
|
7
|
-
* Invoked by `openclaw channels login --channel agent-network`. CLI dispatch
|
|
8
|
-
* at `dist/channels-cli-Cc40S0aS.js:85` reads `plugin.auth?.login` and throws
|
|
9
|
-
* `"Channel ... does not support login"` if absent. See WhatsApp + Feishu
|
|
10
|
-
* bundled extensions for reference implementations of the same contract.
|
|
11
|
-
*
|
|
12
|
-
* Flow (W4 PR 3 — `loopbackHandoffFlow`):
|
|
13
|
-
*
|
|
14
|
-
* 1. Bind a one-shot HTTP listener on `127.0.0.1:0` (OS-assigned ephemeral
|
|
15
|
-
* port). The listener serves a single `POST /handoff` plus the matching
|
|
16
|
-
* CORS preflight, then closes.
|
|
17
|
-
* 2. Generate a 24-byte (32-char base64url) URL-safe random nonce. The
|
|
18
|
-
* nonce travels in the URL fragment (`#nonce=…`), never the query
|
|
19
|
-
* string — fragments do not reach `apps/web` access logs nor referer
|
|
20
|
-
* headers (Alt W4-B2 lock).
|
|
21
|
-
* 3. Open the user's browser to the handoff URL (no-handle path uses
|
|
22
|
-
* `${idpBaseUrl}/console/handoff?port=<port>#nonce=<nonce>`; with
|
|
23
|
-
* `--handle` the per-handle deep-link variant is the entry).
|
|
24
|
-
* 4. The user authenticates against Better Auth on `apps/web`, picks (or
|
|
25
|
-
* creates) an agent, and the page POSTs `{token, agent, key, nonce}`
|
|
26
|
-
* to the listener. The listener verifies the body's `nonce` matches
|
|
27
|
-
* the value it placed in the fragment, accepts the token, and closes.
|
|
28
|
-
* 5. Persist `{connectorUrl, token}` to `openclaw.json` via
|
|
29
|
-
* `writeCredentials()`. `connectorUrl` is derived from `apiHost`
|
|
30
|
-
* (`wss://<apiHost>/gateway`) for cloud / preview deployments; self-
|
|
31
|
-
* hosted users keep an explicit `connectorUrl` in their `openclaw.json`
|
|
32
|
-
* and the legacy value is preserved across the handoff (see
|
|
33
|
-
* `loadExistingConnectorUrl()`).
|
|
34
|
-
*
|
|
35
|
-
* Why move off device flow:
|
|
36
|
-
*
|
|
37
|
-
* - One step shorter (no `user_code` to type into the browser).
|
|
38
|
-
* - No periodic IdP polling (was 5s default, 5+s on `slow_down`).
|
|
39
|
-
* - Browser remains fully authenticated across login and key issuance —
|
|
40
|
-
* same Better Auth session is reused, so the user never re-types their
|
|
41
|
-
* password mid-flow.
|
|
42
|
-
* - The runtime API key carrier (`masons_rt_…`) replaces the legacy
|
|
43
|
-
* connector-minted `sk-…` carrier; W4 PR 1 substrate plus the connector
|
|
44
|
-
* mirror wiring earlier in this PR make both carriers verifiable through
|
|
45
|
-
* plugin v0.5.13's life until the W6 cleanup retires the legacy path.
|
|
46
|
-
*
|
|
47
|
-
* The legacy device-flow helpers (`deviceCodeFlow`, `pollUntilAuthorized`,
|
|
48
|
-
* `agentSetup`, `createAgentLoop`) remain in this file as
|
|
49
|
-
* `@deprecated` references for the W6 cleanup window. They have no call
|
|
50
|
-
* site after the migration and should not be invoked by any new code.
|
|
51
|
-
*
|
|
52
|
-
* See #1264 for original design rationale (single-driver Node semantic —
|
|
53
|
-
* re-running this flow rotates the runtime key and evicts any previously-
|
|
54
|
-
* connected Runtime); see the W4 strategic-pass at
|
|
55
|
-
* `#1466 issuecomment-4362892782` for the loopback handoff lock.
|
|
56
|
-
*/
|
|
57
|
-
/**
|
|
58
|
-
* Minimal OpenClaw runtime surface passed to `auth.login`. Exposed as
|
|
59
|
-
* `defaultRuntime` in `openclaw/dist/runtime-Dx7oeLYq.js` — plugins get
|
|
60
|
-
* only the output/exit channel, not a prompter (plugins build their own
|
|
61
|
-
* via @clack/prompts, which is what OpenClaw CLI uses internally too).
|
|
62
|
-
*/
|
|
63
1
|
interface OpenClawRuntime {
|
|
64
2
|
log(...args: unknown[]): void;
|
|
65
3
|
error(...args: unknown[]): void;
|
|
@@ -67,11 +5,6 @@ interface OpenClawRuntime {
|
|
|
67
5
|
writeJson(value: unknown, space?: number): void;
|
|
68
6
|
exit(code: number): void;
|
|
69
7
|
}
|
|
70
|
-
/**
|
|
71
|
-
* Context passed by OpenClaw CLI to `plugin.auth.login`. Matches the call
|
|
72
|
-
* site at `dist/channels-cli-Cc40S0aS.js:89`:
|
|
73
|
-
* await login({ cfg, accountId, runtime, verbose, channelInput });
|
|
74
|
-
*/
|
|
75
8
|
interface AuthLoginContext {
|
|
76
9
|
cfg: Record<string, unknown>;
|
|
77
10
|
accountId?: string;
|
|
@@ -79,32 +12,6 @@ interface AuthLoginContext {
|
|
|
79
12
|
verbose?: boolean;
|
|
80
13
|
channelInput?: string;
|
|
81
14
|
}
|
|
82
|
-
/**
|
|
83
|
-
* Channel login hook — invoked by `openclaw channels login --channel agent-network`.
|
|
84
|
-
*
|
|
85
|
-
* Unlike the legacy `configureInteractive` contract (which returned a config
|
|
86
|
-
* object for the CLI to persist), the 2026.4.x `auth.login` contract returns
|
|
87
|
-
* void and expects the plugin to persist any config changes itself. We use
|
|
88
|
-
* `writeCredentials()` in config.ts, which writes `openclaw.json` atomically
|
|
89
|
-
* and bumps the state-cache generation so Layer B's dynamic context re-reads
|
|
90
|
-
* on the next turn.
|
|
91
|
-
*
|
|
92
|
-
* Flow (W4 PR 3):
|
|
93
|
-
* 1. Run `loopbackHandoffFlow` against `apps/web` — a Stripe-style
|
|
94
|
-
* browser-mediated runtime-key issuance that delivers the plaintext
|
|
95
|
-
* `masons_rt_…` token to a one-shot loopback listener bound at
|
|
96
|
-
* `127.0.0.1:0` with a fragment-nonce cross-origin discipline.
|
|
97
|
-
* 2. Resolve the connector URL — preserve any user-set
|
|
98
|
-
* `accounts.default.connectorUrl` from `openclaw.json` (for self-
|
|
99
|
-
* hosted topologies); fall back to the cloud derivation
|
|
100
|
-
* `wss://<apiHost>/gateway` matching the connector's
|
|
101
|
-
* `CONNECTOR_PLUGIN_URL` env default.
|
|
102
|
-
* 3. Persist `{connectorUrl, token}` atomically.
|
|
103
|
-
*
|
|
104
|
-
* Errors during the handoff are surfaced via `ctx.runtime.error` (not
|
|
105
|
-
* thrown) so OpenClaw CLI doesn't display a stack trace — matches the
|
|
106
|
-
* WhatsApp/Feishu pattern.
|
|
107
|
-
*/
|
|
108
15
|
export declare function login(ctx: AuthLoginContext): Promise<void>;
|
|
109
16
|
export {};
|
|
110
17
|
//# sourceMappingURL=cli-setup.d.ts.map
|
package/dist/cli-setup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-setup.d.ts","sourceRoot":"","sources":["../src/cli-setup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli-setup.d.ts","sourceRoot":"","sources":["../src/cli-setup.ts"],"names":[],"mappings":"AAqFA,UAAU,eAAe;IACvB,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC9B,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAOD,UAAU,gBAAgB;IACxB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,eAAe,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAyWD,wBAAsB,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqDhE"}
|