@masons/agent-network 0.5.23 → 0.5.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.map +1 -1
- package/dist/channel.js +11 -1
- 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 +6 -4
- package/dist/runtime-endpoint-client.d.ts +57 -0
- package/dist/runtime-endpoint-client.d.ts.map +1 -0
- package/dist/runtime-endpoint-client.js +71 -0
- package/dist/session-lifecycle.d.ts +89 -0
- package/dist/session-lifecycle.d.ts.map +1 -0
- package/dist/session-lifecycle.js +348 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +56 -24
- 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 +5 -1
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.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");
|
|
@@ -128,6 +128,7 @@ async function handleAddressedMessage(event, conversationManager, client, ctx) {
|
|
|
128
128
|
const nodeRef = tryProjectNodeRef(event.metadata);
|
|
129
129
|
if (nodeRef) {
|
|
130
130
|
setCurrentTurnIsOwner(false);
|
|
131
|
+
setCurrentTurnOwnerSignal(null);
|
|
131
132
|
dbg("Full Node sender: kind=%s mstpAddress=%s handle=%s", nodeRef.kind, nodeRef.mstpAddress, nodeRef.handle ?? "(absent)");
|
|
132
133
|
}
|
|
133
134
|
else {
|
|
@@ -146,6 +147,15 @@ async function handleAddressedMessage(event, conversationManager, client, ctx) {
|
|
|
146
147
|
}
|
|
147
148
|
}
|
|
148
149
|
setCurrentTurnIsOwner(isOwnerAddress(fromAddress));
|
|
150
|
+
if (event.metadata?.is_owner === true) {
|
|
151
|
+
setCurrentTurnOwnerSignal("owner");
|
|
152
|
+
}
|
|
153
|
+
else if (event.metadata?.is_owner === false) {
|
|
154
|
+
setCurrentTurnOwnerSignal("non-owner");
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
setCurrentTurnOwnerSignal(null);
|
|
158
|
+
}
|
|
149
159
|
}
|
|
150
160
|
const contact = nodeRef
|
|
151
161
|
? deriveNodeName(nodeRef, fromAddress)
|
package/dist/config.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export interface NetworkCredentials {
|
|
|
26
26
|
token: string;
|
|
27
27
|
}
|
|
28
28
|
export declare function readExistingConnectorUrl(): Promise<string | undefined>;
|
|
29
|
-
export declare function writeCredentials(creds: NetworkCredentials, apiHost?: string, idpBaseUrl?: string): Promise<void>;
|
|
29
|
+
export declare function writeCredentials(creds: NetworkCredentials, apiHost?: string, idpBaseUrl?: string, pendingTarget?: string): Promise<void>;
|
|
30
30
|
export declare function writeTargetHandle(handle: string): Promise<void>;
|
|
31
31
|
export declare function clearTargetHandle(): Promise<void>;
|
|
32
32
|
export declare function markProfileNeeded(): Promise<void>;
|
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) {
|
|
@@ -126,16 +126,18 @@ const plugin = {
|
|
|
126
126
|
"After responding on the network, check: " +
|
|
127
127
|
"should you report to your owner via masons_note_for_owner? " +
|
|
128
128
|
"Should you follow up with another agent via masons_send_message? " +
|
|
129
|
-
"When the interaction is complete, call masons_end_conversation as your FINAL
|
|
130
|
-
"
|
|
129
|
+
"When the interaction is complete, call masons_end_conversation as your FINAL action — " +
|
|
130
|
+
"do not generate any text reply after ending. " +
|
|
131
131
|
"";
|
|
132
132
|
api.on("before_prompt_build", async (_event, ctx) => {
|
|
133
133
|
const hookCtx = ctx;
|
|
134
134
|
const channelId = hookCtx?.channelId;
|
|
135
135
|
const turnIsOwner = consumeCurrentTurnIsOwner();
|
|
136
|
+
const turnOwnerSignal = consumeCurrentTurnOwnerSignal();
|
|
136
137
|
const turnSender = consumeCurrentTurnSender();
|
|
137
138
|
setCurrentTurnChannelId(channelId ?? null);
|
|
138
139
|
setCurrentTurnIsOwnerForTools(turnIsOwner);
|
|
140
|
+
setCurrentTurnOwnerSignalForTools(turnOwnerSignal);
|
|
139
141
|
const currentGeneration = getStateCacheGeneration();
|
|
140
142
|
if (!cachedState || cachedGeneration !== currentGeneration) {
|
|
141
143
|
try {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { PlatformApiError, type PlatformClientConfig } from "./platform-client.js";
|
|
2
|
+
export { PlatformApiError, type PlatformClientConfig };
|
|
3
|
+
export type RuntimeKind = "claude-code" | "openclaw" | "codex" | "passport" | "custom" | (string & {});
|
|
4
|
+
export interface WorkspaceMetadata {
|
|
5
|
+
root: string;
|
|
6
|
+
cwd: string;
|
|
7
|
+
remote?: string;
|
|
8
|
+
branch?: string;
|
|
9
|
+
}
|
|
10
|
+
export type ShareWithPeerField = "workspace" | "tracking_ref" | "runtime_session_id" | "fingerprint" | (string & {});
|
|
11
|
+
export interface RegisterRuntimeEndpointParams {
|
|
12
|
+
as: string;
|
|
13
|
+
runtime_kind: RuntimeKind;
|
|
14
|
+
runtime_session_id?: string;
|
|
15
|
+
endpoint_nonce: string;
|
|
16
|
+
display_label: string;
|
|
17
|
+
workspace?: WorkspaceMetadata;
|
|
18
|
+
tracking_ref?: string;
|
|
19
|
+
share_with_peer?: ShareWithPeerField[];
|
|
20
|
+
}
|
|
21
|
+
export interface RegisterRuntimeEndpointResponse {
|
|
22
|
+
endpoint_id: string;
|
|
23
|
+
node_id: string;
|
|
24
|
+
display_label: string;
|
|
25
|
+
heartbeat_after_seconds: number;
|
|
26
|
+
lease_ttl_seconds: number;
|
|
27
|
+
soft_retention_seconds: number;
|
|
28
|
+
registered_at: string;
|
|
29
|
+
}
|
|
30
|
+
export interface OwnedRuntimeEndpointView {
|
|
31
|
+
endpoint_id: string;
|
|
32
|
+
node_id: string;
|
|
33
|
+
node_handle?: string;
|
|
34
|
+
runtime_kind: RuntimeKind;
|
|
35
|
+
runtime_session_id?: string;
|
|
36
|
+
display_label: string;
|
|
37
|
+
workspace?: WorkspaceMetadata;
|
|
38
|
+
tracking_ref?: string;
|
|
39
|
+
fingerprint?: {
|
|
40
|
+
workspace_hash_per_peer?: Record<string, string>;
|
|
41
|
+
};
|
|
42
|
+
registered_at: string;
|
|
43
|
+
retired_at?: string;
|
|
44
|
+
is_self: boolean;
|
|
45
|
+
}
|
|
46
|
+
export interface ListOwnedRuntimeEndpointsResponse {
|
|
47
|
+
total: number;
|
|
48
|
+
items: OwnedRuntimeEndpointView[];
|
|
49
|
+
}
|
|
50
|
+
export type HeartbeatRuntimeEndpointResponse = Record<string, unknown>;
|
|
51
|
+
export type UnregisterRuntimeEndpointResponse = Record<string, unknown>;
|
|
52
|
+
export declare function registerRuntimeEndpoint(cfg: PlatformClientConfig, runtimeKey: string, params: RegisterRuntimeEndpointParams): Promise<RegisterRuntimeEndpointResponse>;
|
|
53
|
+
export declare function heartbeatRuntimeEndpoint(cfg: PlatformClientConfig, runtimeKey: string, endpointId: string): Promise<HeartbeatRuntimeEndpointResponse>;
|
|
54
|
+
export declare function unregisterRuntimeEndpoint(cfg: PlatformClientConfig, runtimeKey: string, endpointId: string, asNodeId: string): Promise<UnregisterRuntimeEndpointResponse>;
|
|
55
|
+
export declare function listOwnedRuntimeEndpoints(cfg: PlatformClientConfig, runtimeKey: string, asNodeId: string): Promise<ListOwnedRuntimeEndpointsResponse>;
|
|
56
|
+
export declare function getOwnedRuntimeEndpoint(cfg: PlatformClientConfig, runtimeKey: string, endpointId: string, asNodeId: string): Promise<OwnedRuntimeEndpointView>;
|
|
57
|
+
//# sourceMappingURL=runtime-endpoint-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-endpoint-client.d.ts","sourceRoot":"","sources":["../src/runtime-endpoint-client.ts"],"names":[],"mappings":"AAuBA,OAAO,EACL,gBAAgB,EAChB,KAAK,oBAAoB,EAC1B,MAAM,sBAAsB,CAAC;AAM9B,OAAO,EAAE,gBAAgB,EAAE,KAAK,oBAAoB,EAAE,CAAC;AAmBvD,MAAM,MAAM,WAAW,GACnB,aAAa,GACb,UAAU,GACV,OAAO,GACP,UAAU,GACV,QAAQ,GACR,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAalB,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAeD,MAAM,MAAM,kBAAkB,GAC1B,WAAW,GACX,cAAc,GACd,oBAAoB,GACpB,aAAa,GACb,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAUlB,MAAM,WAAW,6BAA6B;IAE5C,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,WAAW,CAAC;IAO1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B,cAAc,EAAE,MAAM,CAAC;IAEvB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAE9B,YAAY,CAAC,EAAE,MAAM,CAAC;IAKtB,eAAe,CAAC,EAAE,kBAAkB,EAAE,CAAC;CACxC;AAcD,MAAM,WAAW,+BAA+B;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,MAAM,CAAC;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;CACvB;AAeD,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IAKpB,OAAO,EAAE,MAAM,CAAC;IAUhB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,WAAW,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAQ5B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IAYtB,WAAW,CAAC,EAAE;QACZ,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAClD,CAAC;IACF,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IAOpB,OAAO,EAAE,OAAO,CAAC;CAClB;AASD,MAAM,WAAW,iCAAiC;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,wBAAwB,EAAE,CAAC;CACnC;AAUD,MAAM,MAAM,gCAAgC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAQvE,MAAM,MAAM,iCAAiC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA4DxE,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,oBAAoB,EACzB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,+BAA+B,CAAC,CAQ1C;AAiBD,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,oBAAoB,EACzB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,gCAAgC,CAAC,CAY3C;AAcD,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,oBAAoB,EACzB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,iCAAiC,CAAC,CAY5C;AAgBD,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,oBAAoB,EACzB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,iCAAiC,CAAC,CAO5C;AAWD,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,oBAAoB,EACzB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,wBAAwB,CAAC,CAUnC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { PlatformApiError, } from "./platform-client.js";
|
|
2
|
+
export { PlatformApiError };
|
|
3
|
+
function baseUrl(cfg) {
|
|
4
|
+
const trimmed = cfg.apiHost.replace(/\/+$/, "");
|
|
5
|
+
const origin = /^https?:\/\//.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
6
|
+
return `${origin}/v1`;
|
|
7
|
+
}
|
|
8
|
+
async function handleError(res) {
|
|
9
|
+
const body = (await res.json().catch(() => ({})));
|
|
10
|
+
const code = typeof body.error === "string" ? body.error : "unknown";
|
|
11
|
+
const message = typeof body.message === "string" ? body.message : res.statusText;
|
|
12
|
+
throw new PlatformApiError(res.status, code, message);
|
|
13
|
+
}
|
|
14
|
+
function authHeaders(runtimeKey) {
|
|
15
|
+
return { Authorization: `Bearer ${runtimeKey}` };
|
|
16
|
+
}
|
|
17
|
+
function jsonHeaders(runtimeKey) {
|
|
18
|
+
return {
|
|
19
|
+
...authHeaders(runtimeKey),
|
|
20
|
+
"Content-Type": "application/json",
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export async function registerRuntimeEndpoint(cfg, runtimeKey, params) {
|
|
24
|
+
const res = await fetch(`${baseUrl(cfg)}/runtime-endpoints`, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: jsonHeaders(runtimeKey),
|
|
27
|
+
body: JSON.stringify(params),
|
|
28
|
+
});
|
|
29
|
+
if (!res.ok)
|
|
30
|
+
return handleError(res);
|
|
31
|
+
return (await res.json());
|
|
32
|
+
}
|
|
33
|
+
export async function heartbeatRuntimeEndpoint(cfg, runtimeKey, endpointId) {
|
|
34
|
+
const res = await fetch(`${baseUrl(cfg)}/runtime-endpoints/${encodeURIComponent(endpointId)}/heartbeat`, {
|
|
35
|
+
method: "POST",
|
|
36
|
+
headers: authHeaders(runtimeKey),
|
|
37
|
+
});
|
|
38
|
+
if (!res.ok)
|
|
39
|
+
return handleError(res);
|
|
40
|
+
return ((await res.json().catch(() => ({}))) ??
|
|
41
|
+
{});
|
|
42
|
+
}
|
|
43
|
+
export async function unregisterRuntimeEndpoint(cfg, runtimeKey, endpointId, asNodeId) {
|
|
44
|
+
const query = new URLSearchParams({ as: asNodeId }).toString();
|
|
45
|
+
const res = await fetch(`${baseUrl(cfg)}/runtime-endpoints/${encodeURIComponent(endpointId)}?${query}`, {
|
|
46
|
+
method: "DELETE",
|
|
47
|
+
headers: authHeaders(runtimeKey),
|
|
48
|
+
});
|
|
49
|
+
if (!res.ok)
|
|
50
|
+
return handleError(res);
|
|
51
|
+
return ((await res.json().catch(() => ({}))) ??
|
|
52
|
+
{});
|
|
53
|
+
}
|
|
54
|
+
export async function listOwnedRuntimeEndpoints(cfg, runtimeKey, asNodeId) {
|
|
55
|
+
const query = new URLSearchParams({ as: asNodeId }).toString();
|
|
56
|
+
const res = await fetch(`${baseUrl(cfg)}/runtime-endpoints?${query}`, {
|
|
57
|
+
headers: authHeaders(runtimeKey),
|
|
58
|
+
});
|
|
59
|
+
if (!res.ok)
|
|
60
|
+
return handleError(res);
|
|
61
|
+
return (await res.json());
|
|
62
|
+
}
|
|
63
|
+
export async function getOwnedRuntimeEndpoint(cfg, runtimeKey, endpointId, asNodeId) {
|
|
64
|
+
const query = new URLSearchParams({ as: asNodeId }).toString();
|
|
65
|
+
const res = await fetch(`${baseUrl(cfg)}/runtime-endpoints/${encodeURIComponent(endpointId)}?${query}`, {
|
|
66
|
+
headers: authHeaders(runtimeKey),
|
|
67
|
+
});
|
|
68
|
+
if (!res.ok)
|
|
69
|
+
return handleError(res);
|
|
70
|
+
return (await res.json());
|
|
71
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Lifecycle — manages MSTP session state per contact.
|
|
3
|
+
*
|
|
4
|
+
* State machine: idle -> creating -> active -> recreating -> failed.
|
|
5
|
+
* Internal key: MSTP address (not handle) — supports same handle on different Connectors.
|
|
6
|
+
*
|
|
7
|
+
* Responsibilities:
|
|
8
|
+
* - Maps MSTP address to session state (sessionId, status, pending promise)
|
|
9
|
+
* - Auto-creates sessions on demand via ensureSession()
|
|
10
|
+
* - Deduplicates concurrent creates (second caller awaits first's promise)
|
|
11
|
+
* - Registers inbound sessions from remote agents
|
|
12
|
+
* - Invalidates all sessions on transport disconnect
|
|
13
|
+
* - Subscribes to ConnectorClient events: session_created, session_error, session_ended, disconnected
|
|
14
|
+
*
|
|
15
|
+
* @see docs/openclaw/session-abstraction-system-design.md §6.1
|
|
16
|
+
*/
|
|
17
|
+
import type { ConnectorClient } from "./connector-client.js";
|
|
18
|
+
export type SessionStatus = "idle" | "creating" | "active" | "recreating" | "failed";
|
|
19
|
+
export interface SessionState {
|
|
20
|
+
status: SessionStatus;
|
|
21
|
+
sessionId: string | null;
|
|
22
|
+
pendingRequestId: string | null;
|
|
23
|
+
retryCount: number;
|
|
24
|
+
/** Promise that resolves when session creation completes. Used for dedup. */
|
|
25
|
+
createPromise: Promise<string> | null;
|
|
26
|
+
/** Resolve function for the create promise. */
|
|
27
|
+
createResolve: ((sessionId: string) => void) | null;
|
|
28
|
+
/** Reject function for the create promise. */
|
|
29
|
+
createReject: ((err: Error) => void) | null;
|
|
30
|
+
}
|
|
31
|
+
export declare class SessionLifecycle {
|
|
32
|
+
private readonly client;
|
|
33
|
+
/** Primary map: MSTP address -> session state */
|
|
34
|
+
private readonly sessions;
|
|
35
|
+
/** Reverse index: sessionId -> MSTP address */
|
|
36
|
+
private readonly sessionToAddress;
|
|
37
|
+
/** Configurable timeout for tests */
|
|
38
|
+
private timeoutMs;
|
|
39
|
+
constructor(client: ConnectorClient);
|
|
40
|
+
/**
|
|
41
|
+
* Ensure an active session exists for the given address.
|
|
42
|
+
* Returns the sessionId. Creates a new session if none exists.
|
|
43
|
+
* Concurrent callers get the same promise (dedup).
|
|
44
|
+
*/
|
|
45
|
+
ensureSession(address: string): Promise<string>;
|
|
46
|
+
/**
|
|
47
|
+
* Get the active sessionId for an address, or null.
|
|
48
|
+
*/
|
|
49
|
+
getSession(address: string): string | null;
|
|
50
|
+
/**
|
|
51
|
+
* Close the session for an address.
|
|
52
|
+
*/
|
|
53
|
+
closeSession(address: string, reason?: string): void;
|
|
54
|
+
/**
|
|
55
|
+
* Register an inbound session (remote initiated).
|
|
56
|
+
*/
|
|
57
|
+
registerInbound(sessionId: string, address: string): void;
|
|
58
|
+
/**
|
|
59
|
+
* Invalidate all sessions. Called on transport disconnect.
|
|
60
|
+
* Conversations are preserved but sessions are cleared.
|
|
61
|
+
*/
|
|
62
|
+
invalidateAll(): void;
|
|
63
|
+
/**
|
|
64
|
+
* Reverse lookup: sessionId -> MSTP address.
|
|
65
|
+
*/
|
|
66
|
+
getAddressBySessionId(sessionId: string): string | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Get all session entries (for listing conversations).
|
|
69
|
+
*/
|
|
70
|
+
listSessions(): Array<{
|
|
71
|
+
address: string;
|
|
72
|
+
status: SessionStatus;
|
|
73
|
+
sessionId: string | null;
|
|
74
|
+
}>;
|
|
75
|
+
/**
|
|
76
|
+
* Remove a session entry entirely (for endConversation).
|
|
77
|
+
*/
|
|
78
|
+
removeSession(address: string): void;
|
|
79
|
+
/** @internal Override timeout for tests. */
|
|
80
|
+
_setTimeoutForTesting(ms: number): void;
|
|
81
|
+
/** @internal Reset for test isolation. */
|
|
82
|
+
_resetForTesting(): void;
|
|
83
|
+
private subscribe;
|
|
84
|
+
private createNewSession;
|
|
85
|
+
private handleSessionCreated;
|
|
86
|
+
private handleSessionEnded;
|
|
87
|
+
private handleSessionError;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=session-lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-lifecycle.d.ts","sourceRoot":"","sources":["../src/session-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAQ7D,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,UAAU,GACV,QAAQ,GACR,YAAY,GACZ,QAAQ,CAAC;AAEb,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IACtC,+CAA+C;IAC/C,aAAa,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACpD,8CAA8C;IAC9C,YAAY,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;CAC7C;AAaD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,iDAAiD;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;IAC5D,+CAA+C;IAC/C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;IAE9D,qCAAqC;IACrC,OAAO,CAAC,SAAS,CAA6B;gBAElC,MAAM,EAAE,eAAe;IASnC;;;;OAIG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAoB/C;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAQ1C;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAqBpD;;OAEG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAqBzD;;;OAGG;IACH,aAAa,IAAI,IAAI;IAqBrB;;OAEG;IACH,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI5D;;OAEG;IACH,YAAY,IAAI,KAAK,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,aAAa,CAAC;QACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,CAAC;IAgBF;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAcpC,4CAA4C;IAC5C,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIvC,0CAA0C;IAC1C,gBAAgB,IAAI,IAAI;IAmBxB,OAAO,CAAC,SAAS;IAsBjB,OAAO,CAAC,gBAAgB;IA+GxB,OAAO,CAAC,oBAAoB;IAuC5B,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,kBAAkB;CA0B3B"}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Lifecycle — manages MSTP session state per contact.
|
|
3
|
+
*
|
|
4
|
+
* State machine: idle -> creating -> active -> recreating -> failed.
|
|
5
|
+
* Internal key: MSTP address (not handle) — supports same handle on different Connectors.
|
|
6
|
+
*
|
|
7
|
+
* Responsibilities:
|
|
8
|
+
* - Maps MSTP address to session state (sessionId, status, pending promise)
|
|
9
|
+
* - Auto-creates sessions on demand via ensureSession()
|
|
10
|
+
* - Deduplicates concurrent creates (second caller awaits first's promise)
|
|
11
|
+
* - Registers inbound sessions from remote agents
|
|
12
|
+
* - Invalidates all sessions on transport disconnect
|
|
13
|
+
* - Subscribes to ConnectorClient events: session_created, session_error, session_ended, disconnected
|
|
14
|
+
*
|
|
15
|
+
* @see docs/openclaw/session-abstraction-system-design.md §6.1
|
|
16
|
+
*/
|
|
17
|
+
import createDebug from "debug";
|
|
18
|
+
const dbg = createDebug("agent-network:session-lifecycle");
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Constants
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
const SESSION_CREATE_TIMEOUT_MS = 15_000;
|
|
23
|
+
const MAX_RETRY_COUNT = 1;
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// SessionLifecycle
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
export class SessionLifecycle {
|
|
28
|
+
client;
|
|
29
|
+
/** Primary map: MSTP address -> session state */
|
|
30
|
+
sessions = new Map();
|
|
31
|
+
/** Reverse index: sessionId -> MSTP address */
|
|
32
|
+
sessionToAddress = new Map();
|
|
33
|
+
/** Configurable timeout for tests */
|
|
34
|
+
timeoutMs = SESSION_CREATE_TIMEOUT_MS;
|
|
35
|
+
constructor(client) {
|
|
36
|
+
this.client = client;
|
|
37
|
+
this.subscribe();
|
|
38
|
+
}
|
|
39
|
+
// -------------------------------------------------------------------------
|
|
40
|
+
// Public API
|
|
41
|
+
// -------------------------------------------------------------------------
|
|
42
|
+
/**
|
|
43
|
+
* Ensure an active session exists for the given address.
|
|
44
|
+
* Returns the sessionId. Creates a new session if none exists.
|
|
45
|
+
* Concurrent callers get the same promise (dedup).
|
|
46
|
+
*/
|
|
47
|
+
ensureSession(address) {
|
|
48
|
+
const state = this.sessions.get(address);
|
|
49
|
+
// Active session — return immediately
|
|
50
|
+
if (state?.status === "active" && state.sessionId) {
|
|
51
|
+
return Promise.resolve(state.sessionId);
|
|
52
|
+
}
|
|
53
|
+
// Creating/recreating — dedup: return existing promise
|
|
54
|
+
if ((state?.status === "creating" || state?.status === "recreating") &&
|
|
55
|
+
state.createPromise) {
|
|
56
|
+
return state.createPromise;
|
|
57
|
+
}
|
|
58
|
+
// Idle, failed, or no state — create new session
|
|
59
|
+
return this.createNewSession(address, state?.retryCount ?? 0);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get the active sessionId for an address, or null.
|
|
63
|
+
*/
|
|
64
|
+
getSession(address) {
|
|
65
|
+
const state = this.sessions.get(address);
|
|
66
|
+
if (state?.status === "active" && state.sessionId) {
|
|
67
|
+
return state.sessionId;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Close the session for an address.
|
|
73
|
+
*/
|
|
74
|
+
closeSession(address, reason) {
|
|
75
|
+
const state = this.sessions.get(address);
|
|
76
|
+
if (!state)
|
|
77
|
+
return;
|
|
78
|
+
if (state.sessionId) {
|
|
79
|
+
this.client.endSession(state.sessionId, reason);
|
|
80
|
+
this.sessionToAddress.delete(state.sessionId);
|
|
81
|
+
}
|
|
82
|
+
// Reject any pending create promise
|
|
83
|
+
if (state.createReject) {
|
|
84
|
+
state.createReject(new Error("Session closed by caller"));
|
|
85
|
+
state.createResolve = null;
|
|
86
|
+
state.createReject = null;
|
|
87
|
+
state.createPromise = null;
|
|
88
|
+
}
|
|
89
|
+
this.sessions.delete(address);
|
|
90
|
+
dbg("closeSession address=%s", address);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Register an inbound session (remote initiated).
|
|
94
|
+
*/
|
|
95
|
+
registerInbound(sessionId, address) {
|
|
96
|
+
// If there's an existing session for this address, clean up
|
|
97
|
+
const existing = this.sessions.get(address);
|
|
98
|
+
if (existing?.sessionId && existing.sessionId !== sessionId) {
|
|
99
|
+
this.sessionToAddress.delete(existing.sessionId);
|
|
100
|
+
}
|
|
101
|
+
const state = {
|
|
102
|
+
status: "active",
|
|
103
|
+
sessionId,
|
|
104
|
+
pendingRequestId: null,
|
|
105
|
+
retryCount: 0,
|
|
106
|
+
createPromise: null,
|
|
107
|
+
createResolve: null,
|
|
108
|
+
createReject: null,
|
|
109
|
+
};
|
|
110
|
+
this.sessions.set(address, state);
|
|
111
|
+
this.sessionToAddress.set(sessionId, address);
|
|
112
|
+
dbg("registerInbound sessionId=%s address=%s", sessionId, address);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Invalidate all sessions. Called on transport disconnect.
|
|
116
|
+
* Conversations are preserved but sessions are cleared.
|
|
117
|
+
*/
|
|
118
|
+
invalidateAll() {
|
|
119
|
+
const count = this.sessions.size;
|
|
120
|
+
for (const [address, state] of this.sessions) {
|
|
121
|
+
// Reject pending creates
|
|
122
|
+
if (state.createReject) {
|
|
123
|
+
state.createReject(new Error("Transport disconnected"));
|
|
124
|
+
state.createResolve = null;
|
|
125
|
+
state.createReject = null;
|
|
126
|
+
state.createPromise = null;
|
|
127
|
+
}
|
|
128
|
+
// Reset to idle — next send() will auto-create
|
|
129
|
+
state.status = "idle";
|
|
130
|
+
state.sessionId = null;
|
|
131
|
+
state.pendingRequestId = null;
|
|
132
|
+
state.retryCount = 0;
|
|
133
|
+
this.sessions.set(address, state);
|
|
134
|
+
}
|
|
135
|
+
this.sessionToAddress.clear();
|
|
136
|
+
dbg("invalidateAll cleared %d sessions", count);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Reverse lookup: sessionId -> MSTP address.
|
|
140
|
+
*/
|
|
141
|
+
getAddressBySessionId(sessionId) {
|
|
142
|
+
return this.sessionToAddress.get(sessionId);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get all session entries (for listing conversations).
|
|
146
|
+
*/
|
|
147
|
+
listSessions() {
|
|
148
|
+
const entries = [];
|
|
149
|
+
for (const [address, state] of this.sessions) {
|
|
150
|
+
entries.push({
|
|
151
|
+
address,
|
|
152
|
+
status: state.status,
|
|
153
|
+
sessionId: state.sessionId,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return entries;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Remove a session entry entirely (for endConversation).
|
|
160
|
+
*/
|
|
161
|
+
removeSession(address) {
|
|
162
|
+
const state = this.sessions.get(address);
|
|
163
|
+
if (state?.sessionId) {
|
|
164
|
+
this.sessionToAddress.delete(state.sessionId);
|
|
165
|
+
}
|
|
166
|
+
if (state?.createReject) {
|
|
167
|
+
state.createReject(new Error("Session removed"));
|
|
168
|
+
state.createResolve = null;
|
|
169
|
+
state.createReject = null;
|
|
170
|
+
state.createPromise = null;
|
|
171
|
+
}
|
|
172
|
+
this.sessions.delete(address);
|
|
173
|
+
}
|
|
174
|
+
/** @internal Override timeout for tests. */
|
|
175
|
+
_setTimeoutForTesting(ms) {
|
|
176
|
+
this.timeoutMs = ms;
|
|
177
|
+
}
|
|
178
|
+
/** @internal Reset for test isolation. */
|
|
179
|
+
_resetForTesting() {
|
|
180
|
+
// Reject pending creates silently
|
|
181
|
+
for (const state of this.sessions.values()) {
|
|
182
|
+
if (state.createReject) {
|
|
183
|
+
state.createReject(new Error("Reset for testing"));
|
|
184
|
+
state.createResolve = null;
|
|
185
|
+
state.createReject = null;
|
|
186
|
+
state.createPromise = null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
this.sessions.clear();
|
|
190
|
+
this.sessionToAddress.clear();
|
|
191
|
+
this.timeoutMs = SESSION_CREATE_TIMEOUT_MS;
|
|
192
|
+
}
|
|
193
|
+
// -------------------------------------------------------------------------
|
|
194
|
+
// Private
|
|
195
|
+
// -------------------------------------------------------------------------
|
|
196
|
+
subscribe() {
|
|
197
|
+
this.client.on("session_created", (event) => {
|
|
198
|
+
this.handleSessionCreated(event.sessionId, event.requestId, event.direction);
|
|
199
|
+
});
|
|
200
|
+
this.client.on("session_error", (sessionId, message) => {
|
|
201
|
+
this.handleSessionError(sessionId, message);
|
|
202
|
+
});
|
|
203
|
+
this.client.on("session_ended", (event) => {
|
|
204
|
+
this.handleSessionEnded(event.sessionId);
|
|
205
|
+
});
|
|
206
|
+
this.client.on("disconnected", () => {
|
|
207
|
+
this.invalidateAll();
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
createNewSession(address, previousRetryCount) {
|
|
211
|
+
const status = previousRetryCount > 0 ? "recreating" : "creating";
|
|
212
|
+
let createResolve = null;
|
|
213
|
+
let createReject = null;
|
|
214
|
+
const createPromise = new Promise((resolve, reject) => {
|
|
215
|
+
createResolve = resolve;
|
|
216
|
+
createReject = reject;
|
|
217
|
+
});
|
|
218
|
+
const { requestId, sent } = this.client.createSession(address);
|
|
219
|
+
if (!sent) {
|
|
220
|
+
const state = {
|
|
221
|
+
status: "failed",
|
|
222
|
+
sessionId: null,
|
|
223
|
+
pendingRequestId: null,
|
|
224
|
+
retryCount: previousRetryCount,
|
|
225
|
+
createPromise: null,
|
|
226
|
+
createResolve: null,
|
|
227
|
+
createReject: null,
|
|
228
|
+
};
|
|
229
|
+
this.sessions.set(address, state);
|
|
230
|
+
return Promise.reject(new Error("Failed to create session: transport unavailable"));
|
|
231
|
+
}
|
|
232
|
+
const state = {
|
|
233
|
+
status,
|
|
234
|
+
sessionId: null,
|
|
235
|
+
pendingRequestId: requestId,
|
|
236
|
+
retryCount: previousRetryCount,
|
|
237
|
+
createPromise,
|
|
238
|
+
createResolve,
|
|
239
|
+
createReject,
|
|
240
|
+
};
|
|
241
|
+
this.sessions.set(address, state);
|
|
242
|
+
// Timeout
|
|
243
|
+
const timer = setTimeout(() => {
|
|
244
|
+
const current = this.sessions.get(address);
|
|
245
|
+
if (current &&
|
|
246
|
+
current.pendingRequestId === requestId &&
|
|
247
|
+
(current.status === "creating" || current.status === "recreating")) {
|
|
248
|
+
dbg("session create timeout address=%s requestId=%s", address, requestId);
|
|
249
|
+
// Can we retry?
|
|
250
|
+
if (current.retryCount < MAX_RETRY_COUNT) {
|
|
251
|
+
// Move to failed, then retry
|
|
252
|
+
current.status = "failed";
|
|
253
|
+
current.retryCount++;
|
|
254
|
+
current.pendingRequestId = null;
|
|
255
|
+
const retryResolve = current.createResolve;
|
|
256
|
+
const retryReject = current.createReject;
|
|
257
|
+
current.createPromise = null;
|
|
258
|
+
current.createResolve = null;
|
|
259
|
+
current.createReject = null;
|
|
260
|
+
this.sessions.set(address, current);
|
|
261
|
+
// Retry: create a new session, pipe result to original promise
|
|
262
|
+
this.createNewSession(address, current.retryCount)
|
|
263
|
+
.then((sessionId) => retryResolve?.(sessionId))
|
|
264
|
+
.catch((err) => retryReject?.(err));
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
// Max retries exhausted
|
|
268
|
+
current.status = "idle";
|
|
269
|
+
current.pendingRequestId = null;
|
|
270
|
+
current.retryCount = 0;
|
|
271
|
+
if (current.createReject) {
|
|
272
|
+
current.createReject(new Error("Session creation timed out. The remote agent may be unavailable."));
|
|
273
|
+
}
|
|
274
|
+
current.createPromise = null;
|
|
275
|
+
current.createResolve = null;
|
|
276
|
+
current.createReject = null;
|
|
277
|
+
this.sessions.set(address, current);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}, this.timeoutMs);
|
|
281
|
+
// Attach cleanup to the promise to clear the timer if resolved early
|
|
282
|
+
createPromise
|
|
283
|
+
.then(() => clearTimeout(timer))
|
|
284
|
+
.catch(() => clearTimeout(timer));
|
|
285
|
+
dbg("createNewSession address=%s requestId=%s status=%s retry=%d", address, requestId, status, previousRetryCount);
|
|
286
|
+
return createPromise;
|
|
287
|
+
}
|
|
288
|
+
handleSessionCreated(sessionId, requestId, direction) {
|
|
289
|
+
// Outbound: match by requestId
|
|
290
|
+
if (direction === "outbound" && requestId) {
|
|
291
|
+
for (const [address, state] of this.sessions) {
|
|
292
|
+
if (state.pendingRequestId === requestId &&
|
|
293
|
+
(state.status === "creating" || state.status === "recreating")) {
|
|
294
|
+
state.status = "active";
|
|
295
|
+
state.sessionId = sessionId;
|
|
296
|
+
state.pendingRequestId = null;
|
|
297
|
+
this.sessionToAddress.set(sessionId, address);
|
|
298
|
+
if (state.createResolve) {
|
|
299
|
+
state.createResolve(sessionId);
|
|
300
|
+
}
|
|
301
|
+
state.createPromise = null;
|
|
302
|
+
state.createResolve = null;
|
|
303
|
+
state.createReject = null;
|
|
304
|
+
dbg("session created (outbound) address=%s sessionId=%s", address, sessionId);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Inbound sessions are registered externally via registerInbound()
|
|
310
|
+
// from the channel adapter (which has the metadata to derive the address).
|
|
311
|
+
// We don't handle inbound SESSION_CREATED here.
|
|
312
|
+
}
|
|
313
|
+
handleSessionEnded(sessionId) {
|
|
314
|
+
const address = this.sessionToAddress.get(sessionId);
|
|
315
|
+
if (!address)
|
|
316
|
+
return;
|
|
317
|
+
const state = this.sessions.get(address);
|
|
318
|
+
if (!state)
|
|
319
|
+
return;
|
|
320
|
+
// Only clear if this session is the current one
|
|
321
|
+
if (state.sessionId === sessionId) {
|
|
322
|
+
state.status = "idle";
|
|
323
|
+
state.sessionId = null;
|
|
324
|
+
dbg("session ended address=%s sessionId=%s", address, sessionId);
|
|
325
|
+
}
|
|
326
|
+
this.sessionToAddress.delete(sessionId);
|
|
327
|
+
}
|
|
328
|
+
handleSessionError(sessionId, message) {
|
|
329
|
+
const address = this.sessionToAddress.get(sessionId);
|
|
330
|
+
if (!address)
|
|
331
|
+
return;
|
|
332
|
+
const state = this.sessions.get(address);
|
|
333
|
+
if (!state)
|
|
334
|
+
return;
|
|
335
|
+
dbg("session error address=%s sessionId=%s message=%s", address, sessionId, message);
|
|
336
|
+
// Session error on active session — mark for auto-recreate on next send().
|
|
337
|
+
// Reset retryCount to 0: this is a new failure context (transport error),
|
|
338
|
+
// not a continuation of a creation timeout. The next ensureSession() gets
|
|
339
|
+
// a fresh retry budget.
|
|
340
|
+
if (state.status === "active") {
|
|
341
|
+
this.sessionToAddress.delete(sessionId);
|
|
342
|
+
state.status = "idle";
|
|
343
|
+
state.sessionId = null;
|
|
344
|
+
state.retryCount = 0;
|
|
345
|
+
dbg("auto-recreate queued address=%s", address);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
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" });
|
|
@@ -529,7 +561,7 @@ export function registerTools(api) {
|
|
|
529
561
|
});
|
|
530
562
|
api.registerTool({
|
|
531
563
|
name: "masons_end_conversation",
|
|
532
|
-
description: "End the
|
|
564
|
+
description: "End the conversation with a connected agent. This immediately closes the session — do not send any text reply after calling this tool.",
|
|
533
565
|
parameters: Type.Object({
|
|
534
566
|
contact: Type.String({
|
|
535
567
|
description: "Handle (e.g. alice) or address of the agent to end the conversation with",
|
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.25";
|
|
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.25";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@masons/agent-network",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.25",
|
|
4
4
|
"description": "MASONS plugin for OpenClaw — connect your agent to the agent network",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "MASONS.ai <hello@masons.ai> (https://masons.ai)",
|
|
@@ -55,6 +55,10 @@
|
|
|
55
55
|
"./platform-client": {
|
|
56
56
|
"types": "./dist/platform-client.d.ts",
|
|
57
57
|
"default": "./dist/platform-client.js"
|
|
58
|
+
},
|
|
59
|
+
"./runtime-endpoint": {
|
|
60
|
+
"types": "./dist/runtime-endpoint-client.d.ts",
|
|
61
|
+
"default": "./dist/runtime-endpoint-client.js"
|
|
58
62
|
}
|
|
59
63
|
},
|
|
60
64
|
"dependencies": {
|