@masons/agent-network 0.5.22 → 0.5.24
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-setup.d.ts +1 -0
- package/dist/channel-setup.d.ts.map +1 -1
- package/dist/channel-setup.js +2 -1
- package/dist/channel.d.ts +2 -0
- package/dist/channel.d.ts.map +1 -1
- package/dist/channel.js +45 -2
- package/dist/config-schema.d.ts +2 -2
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -1
- package/dist/owner-session-state.d.ts +3 -0
- package/dist/owner-session-state.d.ts.map +1 -1
- package/dist/owner-session-state.js +10 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +4 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +55 -23
- package/dist/turn-context.d.ts +3 -0
- package/dist/turn-context.d.ts.map +1 -1
- package/dist/turn-context.js +7 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +10 -11
- package/dist/session-lifecycle.d.ts +0 -89
- package/dist/session-lifecycle.d.ts.map +0 -1
- package/dist/session-lifecycle.js +0 -348
package/dist/channel-setup.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/channel-setup.js
CHANGED
|
@@ -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,
|
package/dist/channel.d.ts
CHANGED
|
@@ -39,6 +39,8 @@ interface ChannelGatewayContext<T = unknown> {
|
|
|
39
39
|
runtime: unknown;
|
|
40
40
|
abortSignal: AbortSignal;
|
|
41
41
|
}
|
|
42
|
+
declare function withAgentNetworkSilentReplyPolicy(cfg: Record<string, unknown>): Record<string, unknown>;
|
|
43
|
+
export declare const _withAgentNetworkSilentReplyPolicyForTesting: typeof withAgentNetworkSilentReplyPolicy;
|
|
42
44
|
interface ChannelGatewayAdapter<T = unknown> {
|
|
43
45
|
startAccount?(ctx: ChannelGatewayContext<T>): Promise<unknown>;
|
|
44
46
|
stopAccount?(ctx: ChannelGatewayContext<T>): Promise<void>;
|
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;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");
|
|
@@ -24,6 +24,39 @@ export function consumeIdentityLinkingNudge() {
|
|
|
24
24
|
export function _setNudgePendingForTesting(value) {
|
|
25
25
|
identityLinkingNudgePending = value;
|
|
26
26
|
}
|
|
27
|
+
function isRecord(value) {
|
|
28
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
29
|
+
}
|
|
30
|
+
function withAgentNetworkSilentReplyPolicy(cfg) {
|
|
31
|
+
const surfaces = isRecord(cfg.surfaces) ? cfg.surfaces : {};
|
|
32
|
+
const existingSurface = isRecord(surfaces["agent-network"])
|
|
33
|
+
? surfaces["agent-network"]
|
|
34
|
+
: {};
|
|
35
|
+
const silentReply = isRecord(existingSurface.silentReply)
|
|
36
|
+
? existingSurface.silentReply
|
|
37
|
+
: {};
|
|
38
|
+
const silentReplyRewrite = isRecord(existingSurface.silentReplyRewrite)
|
|
39
|
+
? existingSurface.silentReplyRewrite
|
|
40
|
+
: {};
|
|
41
|
+
return {
|
|
42
|
+
...cfg,
|
|
43
|
+
surfaces: {
|
|
44
|
+
...surfaces,
|
|
45
|
+
"agent-network": {
|
|
46
|
+
...existingSurface,
|
|
47
|
+
silentReply: {
|
|
48
|
+
...silentReply,
|
|
49
|
+
direct: "allow",
|
|
50
|
+
},
|
|
51
|
+
silentReplyRewrite: {
|
|
52
|
+
...silentReplyRewrite,
|
|
53
|
+
direct: false,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export const _withAgentNetworkSilentReplyPolicyForTesting = withAgentNetworkSilentReplyPolicy;
|
|
27
60
|
function tryProjectNodeRef(metadata) {
|
|
28
61
|
if (!metadata)
|
|
29
62
|
return null;
|
|
@@ -95,6 +128,7 @@ async function handleAddressedMessage(event, conversationManager, client, ctx) {
|
|
|
95
128
|
const nodeRef = tryProjectNodeRef(event.metadata);
|
|
96
129
|
if (nodeRef) {
|
|
97
130
|
setCurrentTurnIsOwner(false);
|
|
131
|
+
setCurrentTurnOwnerSignal(null);
|
|
98
132
|
dbg("Full Node sender: kind=%s mstpAddress=%s handle=%s", nodeRef.kind, nodeRef.mstpAddress, nodeRef.handle ?? "(absent)");
|
|
99
133
|
}
|
|
100
134
|
else {
|
|
@@ -113,6 +147,15 @@ async function handleAddressedMessage(event, conversationManager, client, ctx) {
|
|
|
113
147
|
}
|
|
114
148
|
}
|
|
115
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
|
+
}
|
|
116
159
|
}
|
|
117
160
|
const contact = nodeRef
|
|
118
161
|
? deriveNodeName(nodeRef, fromAddress)
|
|
@@ -179,7 +222,7 @@ async function handleAddressedMessage(event, conversationManager, client, ctx) {
|
|
|
179
222
|
try {
|
|
180
223
|
await runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
181
224
|
ctx: msgCtx,
|
|
182
|
-
cfg: ctx.cfg,
|
|
225
|
+
cfg: withAgentNetworkSilentReplyPolicy(ctx.cfg),
|
|
183
226
|
dispatcherOptions: {
|
|
184
227
|
deliver: async (payload) => {
|
|
185
228
|
const text = payload.text;
|
package/dist/config-schema.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Static } from "@sinclair/typebox";
|
|
2
2
|
export declare const AgentNetworkAccountSchema: import("@sinclair/typebox").TObject<{
|
|
3
|
-
connectorUrl: import("@sinclair/typebox").TString
|
|
4
|
-
token: import("@sinclair/typebox").TString
|
|
3
|
+
connectorUrl: import("@sinclair/typebox").TString<string>;
|
|
4
|
+
token: import("@sinclair/typebox").TString<string>;
|
|
5
5
|
}>;
|
|
6
6
|
export type AgentNetworkAccount = Static<typeof AgentNetworkAccountSchema>;
|
|
7
7
|
//# sourceMappingURL=config-schema.d.ts.map
|
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>;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -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;
|
|
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":"
|
|
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
|
+
}
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"
|
|
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) {
|
|
@@ -133,9 +133,11 @@ const plugin = {
|
|
|
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 {
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"
|
|
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(
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
function resolveSetupAuthority() {
|
|
60
|
+
const channelId = getCurrentTurnChannelId();
|
|
61
|
+
if (channelId !== "agent-network") {
|
|
62
|
+
return { allowed: true };
|
|
62
63
|
}
|
|
63
|
-
|
|
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
|
|
124
|
+
function ownerRefusalText(reason) {
|
|
120
125
|
if (reason === "known-non-owner") {
|
|
121
126
|
return [
|
|
122
|
-
"
|
|
123
|
-
"",
|
|
124
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
|
|
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'
|
|
179
|
-
"
|
|
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
|
-
"
|
|
182
|
-
"
|
|
183
|
-
"
|
|
184
|
-
"
|
|
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
|
-
|
|
188
|
-
|
|
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(
|
|
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(
|
|
224
|
+
: await startOrGetChannelSetup({
|
|
225
|
+
...getSetupOptionsFromToolContext(ctx),
|
|
226
|
+
invitedBy,
|
|
227
|
+
});
|
|
196
228
|
return textResult(formatChannelSetupResult(result));
|
|
197
229
|
},
|
|
198
230
|
}), { name: "masons_setup" });
|
package/dist/turn-context.d.ts
CHANGED
|
@@ -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":"
|
|
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"}
|
package/dist/turn-context.js
CHANGED
|
@@ -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.
|
|
1
|
+
export declare const PLUGIN_VERSION = "0.5.24";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const PLUGIN_VERSION = "0.5.
|
|
1
|
+
export const PLUGIN_VERSION = "0.5.24";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@masons/agent-network",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.24",
|
|
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)",
|
|
@@ -19,15 +19,6 @@
|
|
|
19
19
|
"publishConfig": {
|
|
20
20
|
"access": "public"
|
|
21
21
|
},
|
|
22
|
-
"scripts": {
|
|
23
|
-
"build": "tsc",
|
|
24
|
-
"dev": "tsc --watch",
|
|
25
|
-
"test": "tsc -p test/tsconfig.json && node --test --loader ts-node/esm test/**/*.test.ts",
|
|
26
|
-
"lint": "biome check",
|
|
27
|
-
"format": "biome format --write",
|
|
28
|
-
"prepublishOnly": "bash scripts/check-version.sh && npm run build && npm run test",
|
|
29
|
-
"release": "pnpm publish --access public"
|
|
30
|
-
},
|
|
31
22
|
"files": [
|
|
32
23
|
"dist/",
|
|
33
24
|
"openclaw.plugin.json",
|
|
@@ -78,5 +69,13 @@
|
|
|
78
69
|
"@types/ws": "^8",
|
|
79
70
|
"ts-node": "^10",
|
|
80
71
|
"typescript": "^5"
|
|
72
|
+
},
|
|
73
|
+
"scripts": {
|
|
74
|
+
"build": "tsc",
|
|
75
|
+
"dev": "tsc --watch",
|
|
76
|
+
"test": "tsc -p test/tsconfig.json && node --test --loader ts-node/esm test/**/*.test.ts",
|
|
77
|
+
"lint": "biome check",
|
|
78
|
+
"format": "biome format --write",
|
|
79
|
+
"release": "pnpm publish --access public"
|
|
81
80
|
}
|
|
82
|
-
}
|
|
81
|
+
}
|
|
@@ -1,89 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,348 +0,0 @@
|
|
|
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
|
-
}
|