@multi-agent-protocol/sdk 0.0.4 → 0.0.6
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/README.md +164 -5
- package/dist/{index-Z76qC_Us.d.cts → index-BQXp4_rd.d.cts} +1898 -142
- package/dist/{index-Z76qC_Us.d.ts → index-BQXp4_rd.d.ts} +1898 -142
- package/dist/index.cjs +2906 -1104
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +641 -24
- package/dist/index.d.ts +641 -24
- package/dist/index.js +2889 -1102
- package/dist/index.js.map +1 -1
- package/dist/testing.cjs +1092 -8
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +2 -1
- package/dist/testing.d.ts +2 -1
- package/dist/testing.js +1092 -8
- package/dist/testing.js.map +1 -1
- package/package.json +8 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,87 @@
|
|
|
1
|
+
import { TunnelStream, createMeshPeer } from 'agentic-mesh';
|
|
1
2
|
export { monotonicFactory, ulid } from 'ulid';
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
2
4
|
import { z } from 'zod';
|
|
3
5
|
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// src/stream/agentic-mesh.ts
|
|
17
|
+
var agentic_mesh_exports = {};
|
|
18
|
+
__export(agentic_mesh_exports, {
|
|
19
|
+
agenticMeshStream: () => agenticMeshStream
|
|
20
|
+
});
|
|
21
|
+
async function agenticMeshStream(config) {
|
|
22
|
+
if (!config.transport.active) {
|
|
23
|
+
await config.transport.start();
|
|
24
|
+
}
|
|
25
|
+
const connected = await config.transport.connect(config.peer);
|
|
26
|
+
if (!connected) {
|
|
27
|
+
throw new Error(`Failed to connect to peer: ${config.peer.peerId}`);
|
|
28
|
+
}
|
|
29
|
+
const streamId = `map-${config.localPeerId}-${Date.now()}`;
|
|
30
|
+
const tunnelStream = new TunnelStream({
|
|
31
|
+
transport: config.transport,
|
|
32
|
+
peerId: config.peer.peerId,
|
|
33
|
+
streamId
|
|
34
|
+
});
|
|
35
|
+
await tunnelStream.open();
|
|
36
|
+
return tunnelStreamToMapStream(tunnelStream);
|
|
37
|
+
}
|
|
38
|
+
function tunnelStreamToMapStream(tunnel) {
|
|
39
|
+
let readingAborted = false;
|
|
40
|
+
const readable = new ReadableStream({
|
|
41
|
+
async start(controller) {
|
|
42
|
+
try {
|
|
43
|
+
for await (const frame of tunnel) {
|
|
44
|
+
if (readingAborted) break;
|
|
45
|
+
controller.enqueue(frame);
|
|
46
|
+
}
|
|
47
|
+
if (!readingAborted) {
|
|
48
|
+
controller.close();
|
|
49
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
if (!readingAborted) {
|
|
52
|
+
controller.error(error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
cancel() {
|
|
57
|
+
readingAborted = true;
|
|
58
|
+
tunnel.close().catch(() => {
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
const writable = new WritableStream({
|
|
63
|
+
async write(message) {
|
|
64
|
+
if (!tunnel.isOpen) {
|
|
65
|
+
throw new Error("Stream is not open");
|
|
66
|
+
}
|
|
67
|
+
await tunnel.write(message);
|
|
68
|
+
},
|
|
69
|
+
async close() {
|
|
70
|
+
await tunnel.close();
|
|
71
|
+
},
|
|
72
|
+
abort() {
|
|
73
|
+
readingAborted = true;
|
|
74
|
+
tunnel.close().catch(() => {
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return { readable, writable };
|
|
79
|
+
}
|
|
80
|
+
var init_agentic_mesh = __esm({
|
|
81
|
+
"src/stream/agentic-mesh.ts"() {
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
4
85
|
// src/types/index.ts
|
|
5
86
|
function isOrphanedAgent(agent) {
|
|
6
87
|
return agent.ownerId === null;
|
|
@@ -86,6 +167,7 @@ var SESSION_METHODS = {
|
|
|
86
167
|
SESSION_CLOSE: "map/session/close"
|
|
87
168
|
};
|
|
88
169
|
var AUTH_METHODS = {
|
|
170
|
+
AUTHENTICATE: "map/authenticate",
|
|
89
171
|
AUTH_REFRESH: "map/auth/refresh"
|
|
90
172
|
};
|
|
91
173
|
var PERMISSION_METHODS = {
|
|
@@ -99,7 +181,9 @@ var NOTIFICATION_METHODS = {
|
|
|
99
181
|
EVENT: "map/event",
|
|
100
182
|
MESSAGE: "map/message",
|
|
101
183
|
/** Client acknowledges received events (for backpressure) */
|
|
102
|
-
SUBSCRIBE_ACK: "map/subscribe.ack"
|
|
184
|
+
SUBSCRIBE_ACK: "map/subscribe.ack",
|
|
185
|
+
/** Server notifies client that auth is about to expire */
|
|
186
|
+
AUTH_EXPIRING: "map/auth/expiring"
|
|
103
187
|
};
|
|
104
188
|
var MAP_METHODS = {
|
|
105
189
|
...CORE_METHODS,
|
|
@@ -134,7 +218,10 @@ var AUTH_ERROR_CODES = {
|
|
|
134
218
|
AUTH_REQUIRED: 1e3,
|
|
135
219
|
AUTH_FAILED: 1001,
|
|
136
220
|
TOKEN_EXPIRED: 1002,
|
|
137
|
-
PERMISSION_DENIED: 1003
|
|
221
|
+
PERMISSION_DENIED: 1003,
|
|
222
|
+
INSUFFICIENT_SCOPE: 1004,
|
|
223
|
+
METHOD_NOT_SUPPORTED: 1005,
|
|
224
|
+
INVALID_CREDENTIALS: 1006
|
|
138
225
|
};
|
|
139
226
|
var ROUTING_ERROR_CODES = {
|
|
140
227
|
ADDRESS_NOT_FOUND: 2e3,
|
|
@@ -208,7 +295,8 @@ var CAPABILITY_REQUIREMENTS = {
|
|
|
208
295
|
[SESSION_METHODS.SESSION_LIST]: [],
|
|
209
296
|
[SESSION_METHODS.SESSION_LOAD]: [],
|
|
210
297
|
[SESSION_METHODS.SESSION_CLOSE]: [],
|
|
211
|
-
// Auth
|
|
298
|
+
// Auth (no capability required - anyone can authenticate)
|
|
299
|
+
[AUTH_METHODS.AUTHENTICATE]: [],
|
|
212
300
|
[AUTH_METHODS.AUTH_REFRESH]: [],
|
|
213
301
|
// Permissions (system-only, no capability check - enforced by participant type)
|
|
214
302
|
[PERMISSION_METHODS.PERMISSIONS_UPDATE]: [],
|
|
@@ -379,6 +467,27 @@ var MAPRequestError = class _MAPRequestError extends Error {
|
|
|
379
467
|
{ category: "auth" }
|
|
380
468
|
);
|
|
381
469
|
}
|
|
470
|
+
static insufficientScope(required) {
|
|
471
|
+
return new _MAPRequestError(
|
|
472
|
+
AUTH_ERROR_CODES.INSUFFICIENT_SCOPE,
|
|
473
|
+
required ? `Insufficient scope: ${required}` : "Insufficient scope",
|
|
474
|
+
{ category: "auth" }
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
static methodNotSupported(method) {
|
|
478
|
+
return new _MAPRequestError(
|
|
479
|
+
AUTH_ERROR_CODES.METHOD_NOT_SUPPORTED,
|
|
480
|
+
`Authentication method not supported: ${method}`,
|
|
481
|
+
{ category: "auth" }
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
static invalidCredentials(details) {
|
|
485
|
+
return new _MAPRequestError(
|
|
486
|
+
AUTH_ERROR_CODES.INVALID_CREDENTIALS,
|
|
487
|
+
details ?? "Invalid credentials",
|
|
488
|
+
{ category: "auth" }
|
|
489
|
+
);
|
|
490
|
+
}
|
|
382
491
|
// ==========================================================================
|
|
383
492
|
// Routing Errors (2xxx)
|
|
384
493
|
// ==========================================================================
|
|
@@ -560,6 +669,7 @@ var MAPTimeoutError = class extends Error {
|
|
|
560
669
|
};
|
|
561
670
|
|
|
562
671
|
// src/stream/index.ts
|
|
672
|
+
init_agentic_mesh();
|
|
563
673
|
function ndJsonStream(readable, writable) {
|
|
564
674
|
const encoder = new TextEncoder();
|
|
565
675
|
const decoder = new TextDecoder();
|
|
@@ -1799,1076 +1909,1355 @@ function sortCausalOrder(events) {
|
|
|
1799
1909
|
return result;
|
|
1800
1910
|
}
|
|
1801
1911
|
|
|
1802
|
-
// src/
|
|
1803
|
-
var
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1912
|
+
// src/acp/types.ts
|
|
1913
|
+
var ACP_ERROR_CODES = {
|
|
1914
|
+
PARSE_ERROR: -32700,
|
|
1915
|
+
INVALID_REQUEST: -32600,
|
|
1916
|
+
METHOD_NOT_FOUND: -32601,
|
|
1917
|
+
INVALID_PARAMS: -32602,
|
|
1918
|
+
INTERNAL_ERROR: -32603,
|
|
1919
|
+
REQUEST_CANCELLED: -32800,
|
|
1920
|
+
AUTH_REQUIRED: -32e3,
|
|
1921
|
+
SESSION_NOT_FOUND: -32002
|
|
1922
|
+
};
|
|
1923
|
+
var ACPError = class _ACPError extends Error {
|
|
1924
|
+
code;
|
|
1925
|
+
data;
|
|
1926
|
+
constructor(code, message, data) {
|
|
1927
|
+
super(message);
|
|
1928
|
+
this.name = "ACPError";
|
|
1929
|
+
this.code = code;
|
|
1930
|
+
this.data = data;
|
|
1931
|
+
}
|
|
1932
|
+
/**
|
|
1933
|
+
* Create an ACPError from an error response.
|
|
1934
|
+
*/
|
|
1935
|
+
static fromResponse(error) {
|
|
1936
|
+
return new _ACPError(error.code, error.message, error.data);
|
|
1937
|
+
}
|
|
1938
|
+
/**
|
|
1939
|
+
* Convert to JSON-RPC error object.
|
|
1940
|
+
*/
|
|
1941
|
+
toErrorObject() {
|
|
1942
|
+
return {
|
|
1943
|
+
code: this.code,
|
|
1944
|
+
message: this.message,
|
|
1945
|
+
...this.data !== void 0 && { data: this.data }
|
|
1946
|
+
};
|
|
1947
|
+
}
|
|
1948
|
+
};
|
|
1949
|
+
var ACP_PROTOCOL_VERSION = 20241007;
|
|
1950
|
+
var ACP_METHODS = {
|
|
1951
|
+
// Lifecycle
|
|
1952
|
+
INITIALIZE: "initialize",
|
|
1953
|
+
AUTHENTICATE: "authenticate",
|
|
1954
|
+
// Session management
|
|
1955
|
+
SESSION_NEW: "session/new",
|
|
1956
|
+
SESSION_LOAD: "session/load",
|
|
1957
|
+
SESSION_SET_MODE: "session/set_mode",
|
|
1958
|
+
// Prompt
|
|
1959
|
+
SESSION_PROMPT: "session/prompt",
|
|
1960
|
+
SESSION_CANCEL: "session/cancel",
|
|
1961
|
+
// Notifications
|
|
1962
|
+
SESSION_UPDATE: "session/update",
|
|
1963
|
+
// Agent→Client requests
|
|
1964
|
+
REQUEST_PERMISSION: "request_permission",
|
|
1965
|
+
FS_READ_TEXT_FILE: "fs/read_text_file",
|
|
1966
|
+
FS_WRITE_TEXT_FILE: "fs/write_text_file",
|
|
1967
|
+
TERMINAL_CREATE: "terminal/create",
|
|
1968
|
+
TERMINAL_OUTPUT: "terminal/output",
|
|
1969
|
+
TERMINAL_RELEASE: "terminal/release",
|
|
1970
|
+
TERMINAL_WAIT_FOR_EXIT: "terminal/wait_for_exit",
|
|
1971
|
+
TERMINAL_KILL: "terminal/kill"
|
|
1972
|
+
};
|
|
1973
|
+
function isACPRequest(msg) {
|
|
1974
|
+
if (typeof msg !== "object" || msg === null) {
|
|
1975
|
+
return false;
|
|
1976
|
+
}
|
|
1977
|
+
return "method" in msg && "id" in msg && msg.id !== void 0;
|
|
1978
|
+
}
|
|
1979
|
+
function isACPNotification(msg) {
|
|
1980
|
+
if (typeof msg !== "object" || msg === null) {
|
|
1981
|
+
return false;
|
|
1982
|
+
}
|
|
1983
|
+
return "method" in msg && !("id" in msg);
|
|
1984
|
+
}
|
|
1985
|
+
function isACPResponse(msg) {
|
|
1986
|
+
if (typeof msg !== "object" || msg === null) {
|
|
1987
|
+
return false;
|
|
1988
|
+
}
|
|
1989
|
+
return "id" in msg && !("method" in msg);
|
|
1990
|
+
}
|
|
1991
|
+
function isACPErrorResponse(response) {
|
|
1992
|
+
if (typeof response !== "object" || response === null) {
|
|
1993
|
+
return false;
|
|
1994
|
+
}
|
|
1995
|
+
return "error" in response;
|
|
1996
|
+
}
|
|
1997
|
+
function isACPSuccessResponse(response) {
|
|
1998
|
+
if (typeof response !== "object" || response === null) {
|
|
1999
|
+
return false;
|
|
2000
|
+
}
|
|
2001
|
+
return "result" in response;
|
|
2002
|
+
}
|
|
2003
|
+
function isACPEnvelope(payload) {
|
|
2004
|
+
if (typeof payload !== "object" || payload === null) {
|
|
2005
|
+
return false;
|
|
2006
|
+
}
|
|
2007
|
+
const envelope = payload;
|
|
2008
|
+
if (typeof envelope.acp !== "object" || envelope.acp === null || typeof envelope.acpContext !== "object" || envelope.acpContext === null) {
|
|
2009
|
+
return false;
|
|
2010
|
+
}
|
|
2011
|
+
const acpContext = envelope.acpContext;
|
|
2012
|
+
return typeof acpContext.streamId === "string";
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
// src/acp/stream.ts
|
|
2016
|
+
var ACPStreamConnection = class extends EventEmitter {
|
|
2017
|
+
#mapClient;
|
|
1808
2018
|
#options;
|
|
2019
|
+
#streamId;
|
|
2020
|
+
#pendingRequests = /* @__PURE__ */ new Map();
|
|
2021
|
+
#subscription = null;
|
|
1809
2022
|
#sessionId = null;
|
|
1810
|
-
#
|
|
1811
|
-
#
|
|
1812
|
-
#
|
|
2023
|
+
#initialized = false;
|
|
2024
|
+
#capabilities = null;
|
|
2025
|
+
#closed = false;
|
|
2026
|
+
#lastEventId = null;
|
|
1813
2027
|
#isReconnecting = false;
|
|
1814
|
-
|
|
1815
|
-
this.#connection = new BaseConnection(stream, options);
|
|
1816
|
-
this.#options = options;
|
|
1817
|
-
this.#connection.setNotificationHandler(this.#handleNotification.bind(this));
|
|
1818
|
-
if (options.reconnection?.enabled && options.createStream) {
|
|
1819
|
-
this.#connection.onStateChange((newState) => {
|
|
1820
|
-
if (newState === "closed" && this.#connected && !this.#isReconnecting) {
|
|
1821
|
-
void this.#handleDisconnect();
|
|
1822
|
-
}
|
|
1823
|
-
});
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
// ===========================================================================
|
|
1827
|
-
// Static Factory Methods
|
|
1828
|
-
// ===========================================================================
|
|
2028
|
+
#unsubscribeReconnection = null;
|
|
1829
2029
|
/**
|
|
1830
|
-
*
|
|
1831
|
-
*
|
|
1832
|
-
* Handles:
|
|
1833
|
-
* - WebSocket creation and connection
|
|
1834
|
-
* - Stream wrapping
|
|
1835
|
-
* - Auto-configuration of createStream for reconnection
|
|
1836
|
-
* - Initial MAP protocol connect handshake
|
|
1837
|
-
*
|
|
1838
|
-
* @param url - WebSocket URL (ws:// or wss://)
|
|
1839
|
-
* @param options - Connection options
|
|
1840
|
-
* @returns Connected ClientConnection instance
|
|
1841
|
-
*
|
|
1842
|
-
* @example
|
|
1843
|
-
* ```typescript
|
|
1844
|
-
* const client = await ClientConnection.connect('ws://localhost:8080', {
|
|
1845
|
-
* name: 'MyClient',
|
|
1846
|
-
* reconnection: true
|
|
1847
|
-
* });
|
|
2030
|
+
* Create a new ACP stream connection.
|
|
1848
2031
|
*
|
|
1849
|
-
*
|
|
1850
|
-
*
|
|
1851
|
-
* ```
|
|
2032
|
+
* @param mapClient - The underlying MAP client connection
|
|
2033
|
+
* @param options - Stream configuration options
|
|
1852
2034
|
*/
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
const timeout = options?.connectTimeout ?? 1e4;
|
|
1861
|
-
const ws = new WebSocket(url);
|
|
1862
|
-
await waitForOpen(ws, timeout);
|
|
1863
|
-
const stream = websocketStream(ws);
|
|
1864
|
-
const createStream = async () => {
|
|
1865
|
-
const newWs = new WebSocket(url);
|
|
1866
|
-
await waitForOpen(newWs, timeout);
|
|
1867
|
-
return websocketStream(newWs);
|
|
1868
|
-
};
|
|
1869
|
-
const reconnection = options?.reconnection === true ? { enabled: true } : typeof options?.reconnection === "object" ? options.reconnection : void 0;
|
|
1870
|
-
const client = new _ClientConnection(stream, {
|
|
1871
|
-
name: options?.name,
|
|
1872
|
-
capabilities: options?.capabilities,
|
|
1873
|
-
createStream,
|
|
1874
|
-
reconnection
|
|
2035
|
+
constructor(mapClient, options) {
|
|
2036
|
+
super();
|
|
2037
|
+
this.#mapClient = mapClient;
|
|
2038
|
+
this.#options = options;
|
|
2039
|
+
this.#streamId = `acp-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
2040
|
+
this.#unsubscribeReconnection = mapClient.onReconnection((event) => {
|
|
2041
|
+
void this.#handleReconnectionEvent(event);
|
|
1875
2042
|
});
|
|
1876
|
-
await client.connect({ auth: options?.auth });
|
|
1877
|
-
return client;
|
|
1878
2043
|
}
|
|
1879
2044
|
// ===========================================================================
|
|
1880
|
-
//
|
|
2045
|
+
// Public Properties
|
|
2046
|
+
// ===========================================================================
|
|
2047
|
+
/** Unique identifier for this ACP stream */
|
|
2048
|
+
get streamId() {
|
|
2049
|
+
return this.#streamId;
|
|
2050
|
+
}
|
|
2051
|
+
/** Target agent this stream connects to */
|
|
2052
|
+
get targetAgent() {
|
|
2053
|
+
return this.#options.targetAgent;
|
|
2054
|
+
}
|
|
2055
|
+
/** Current ACP session ID (null until newSession called) */
|
|
2056
|
+
get sessionId() {
|
|
2057
|
+
return this.#sessionId;
|
|
2058
|
+
}
|
|
2059
|
+
/** Whether initialize() has been called */
|
|
2060
|
+
get initialized() {
|
|
2061
|
+
return this.#initialized;
|
|
2062
|
+
}
|
|
2063
|
+
/** Agent capabilities from initialize response */
|
|
2064
|
+
get capabilities() {
|
|
2065
|
+
return this.#capabilities;
|
|
2066
|
+
}
|
|
2067
|
+
/** Whether the stream is closed */
|
|
2068
|
+
get isClosed() {
|
|
2069
|
+
return this.#closed;
|
|
2070
|
+
}
|
|
2071
|
+
/** Last processed event ID (for reconnection support) */
|
|
2072
|
+
get lastEventId() {
|
|
2073
|
+
return this.#lastEventId;
|
|
2074
|
+
}
|
|
2075
|
+
/** Whether the stream is currently reconnecting */
|
|
2076
|
+
get isReconnecting() {
|
|
2077
|
+
return this.#isReconnecting;
|
|
2078
|
+
}
|
|
2079
|
+
// ===========================================================================
|
|
2080
|
+
// Reconnection Handling
|
|
1881
2081
|
// ===========================================================================
|
|
1882
2082
|
/**
|
|
1883
|
-
*
|
|
2083
|
+
* Handle MAP reconnection events.
|
|
1884
2084
|
*/
|
|
1885
|
-
async
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
2085
|
+
async #handleReconnectionEvent(event) {
|
|
2086
|
+
if (this.#closed) return;
|
|
2087
|
+
switch (event.type) {
|
|
2088
|
+
case "disconnected":
|
|
2089
|
+
this.#isReconnecting = true;
|
|
2090
|
+
this.emit("reconnecting");
|
|
2091
|
+
for (const [id, pending] of this.#pendingRequests) {
|
|
2092
|
+
clearTimeout(pending.timeout);
|
|
2093
|
+
pending.reject(new Error("Connection lost during ACP request"));
|
|
2094
|
+
this.#pendingRequests.delete(id);
|
|
2095
|
+
}
|
|
2096
|
+
break;
|
|
2097
|
+
case "reconnected":
|
|
2098
|
+
await this.#handleReconnected();
|
|
2099
|
+
break;
|
|
2100
|
+
case "reconnectFailed":
|
|
2101
|
+
this.#isReconnecting = false;
|
|
2102
|
+
this.emit("error", event.error ?? new Error("MAP reconnection failed"));
|
|
2103
|
+
break;
|
|
2104
|
+
}
|
|
1902
2105
|
}
|
|
1903
2106
|
/**
|
|
1904
|
-
*
|
|
1905
|
-
* @param reason - Optional reason for disconnecting
|
|
1906
|
-
* @returns Resume token that can be used to resume this session later
|
|
2107
|
+
* Handle successful MAP reconnection.
|
|
1907
2108
|
*/
|
|
1908
|
-
async
|
|
1909
|
-
|
|
1910
|
-
let resumeToken;
|
|
2109
|
+
async #handleReconnected() {
|
|
2110
|
+
this.#subscription = null;
|
|
1911
2111
|
try {
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
2112
|
+
await this.#setupSubscription();
|
|
2113
|
+
if (this.#sessionId) {
|
|
2114
|
+
const sessionValid = await this.#verifySessionValid();
|
|
2115
|
+
if (!sessionValid) {
|
|
2116
|
+
const lostSessionId = this.#sessionId;
|
|
2117
|
+
this.#sessionId = null;
|
|
2118
|
+
this.emit("sessionLost", {
|
|
2119
|
+
sessionId: lostSessionId,
|
|
2120
|
+
reason: "Session no longer valid after reconnection"
|
|
2121
|
+
});
|
|
2122
|
+
}
|
|
1920
2123
|
}
|
|
1921
|
-
this.#
|
|
1922
|
-
|
|
1923
|
-
|
|
2124
|
+
this.#isReconnecting = false;
|
|
2125
|
+
this.emit("reconnected");
|
|
2126
|
+
} catch (error) {
|
|
2127
|
+
this.#isReconnecting = false;
|
|
2128
|
+
this.emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
1924
2129
|
}
|
|
1925
|
-
return resumeToken;
|
|
1926
2130
|
}
|
|
1927
2131
|
/**
|
|
1928
|
-
*
|
|
2132
|
+
* Verify that the current ACP session is still valid.
|
|
2133
|
+
*
|
|
2134
|
+
* Since ACP doesn't have a dedicated status check method, we attempt
|
|
2135
|
+
* a lightweight operation (cancel with no effect) to verify the session.
|
|
1929
2136
|
*/
|
|
1930
|
-
|
|
1931
|
-
|
|
2137
|
+
async #verifySessionValid() {
|
|
2138
|
+
if (!this.#sessionId) return false;
|
|
2139
|
+
try {
|
|
2140
|
+
await this.#sendNotification(ACP_METHODS.SESSION_CANCEL, {
|
|
2141
|
+
sessionId: this.#sessionId,
|
|
2142
|
+
reason: "session_verification"
|
|
2143
|
+
});
|
|
2144
|
+
return true;
|
|
2145
|
+
} catch {
|
|
2146
|
+
return false;
|
|
2147
|
+
}
|
|
1932
2148
|
}
|
|
2149
|
+
// ===========================================================================
|
|
2150
|
+
// Internal Methods
|
|
2151
|
+
// ===========================================================================
|
|
1933
2152
|
/**
|
|
1934
|
-
*
|
|
2153
|
+
* Set up the subscription for receiving messages from the target agent.
|
|
1935
2154
|
*/
|
|
1936
|
-
|
|
1937
|
-
|
|
2155
|
+
async #setupSubscription() {
|
|
2156
|
+
if (this.#subscription) return;
|
|
2157
|
+
this.#subscription = await this.#mapClient.subscribe({
|
|
2158
|
+
fromAgents: [this.#options.targetAgent]
|
|
2159
|
+
});
|
|
2160
|
+
void this.#processEvents();
|
|
1938
2161
|
}
|
|
1939
2162
|
/**
|
|
1940
|
-
*
|
|
2163
|
+
* Process incoming events from the subscription.
|
|
1941
2164
|
*/
|
|
1942
|
-
|
|
1943
|
-
|
|
2165
|
+
async #processEvents() {
|
|
2166
|
+
if (!this.#subscription) return;
|
|
2167
|
+
try {
|
|
2168
|
+
for await (const event of this.#subscription) {
|
|
2169
|
+
if (this.#closed) break;
|
|
2170
|
+
if (event.id) {
|
|
2171
|
+
this.#lastEventId = event.id;
|
|
2172
|
+
}
|
|
2173
|
+
if (event.type === "message_delivered" && event.data) {
|
|
2174
|
+
const message = event.data.message;
|
|
2175
|
+
if (message?.payload) {
|
|
2176
|
+
await this.#handleIncomingMessage(message);
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
} catch (error) {
|
|
2181
|
+
if (!this.#closed) {
|
|
2182
|
+
this.emit("error", error);
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
1944
2185
|
}
|
|
1945
2186
|
/**
|
|
1946
|
-
*
|
|
1947
|
-
*/
|
|
1948
|
-
|
|
1949
|
-
|
|
2187
|
+
* Handle an incoming message from the target agent.
|
|
2188
|
+
*/
|
|
2189
|
+
async #handleIncomingMessage(message) {
|
|
2190
|
+
const payload = message.payload;
|
|
2191
|
+
if (!isACPEnvelope(payload)) return;
|
|
2192
|
+
const envelope = payload;
|
|
2193
|
+
const { acp, acpContext } = envelope;
|
|
2194
|
+
if (acpContext.streamId !== this.#streamId) return;
|
|
2195
|
+
if (acp.id !== void 0 && !acp.method) {
|
|
2196
|
+
const requestId = String(acp.id);
|
|
2197
|
+
const pending = this.#pendingRequests.get(requestId);
|
|
2198
|
+
if (pending) {
|
|
2199
|
+
clearTimeout(pending.timeout);
|
|
2200
|
+
this.#pendingRequests.delete(requestId);
|
|
2201
|
+
if (acp.error) {
|
|
2202
|
+
pending.reject(ACPError.fromResponse(acp.error));
|
|
2203
|
+
} else {
|
|
2204
|
+
pending.resolve(acp.result);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
return;
|
|
2208
|
+
}
|
|
2209
|
+
if (acp.method && acp.id === void 0) {
|
|
2210
|
+
await this.#handleNotification(acp.method, acp.params, acpContext);
|
|
2211
|
+
return;
|
|
2212
|
+
}
|
|
2213
|
+
if (acp.method && acp.id !== void 0) {
|
|
2214
|
+
await this.#handleAgentRequest(acp.id, acp.method, acp.params, acpContext, message);
|
|
2215
|
+
}
|
|
1950
2216
|
}
|
|
1951
2217
|
/**
|
|
1952
|
-
*
|
|
2218
|
+
* Handle an ACP notification from the agent.
|
|
1953
2219
|
*/
|
|
1954
|
-
|
|
1955
|
-
|
|
2220
|
+
async #handleNotification(method, params, _acpContext) {
|
|
2221
|
+
if (method === ACP_METHODS.SESSION_UPDATE) {
|
|
2222
|
+
await this.#options.client.sessionUpdate(params);
|
|
2223
|
+
}
|
|
1956
2224
|
}
|
|
1957
|
-
// ===========================================================================
|
|
1958
|
-
// Session Management
|
|
1959
|
-
// ===========================================================================
|
|
1960
2225
|
/**
|
|
1961
|
-
*
|
|
2226
|
+
* Handle an agent→client request.
|
|
1962
2227
|
*/
|
|
1963
|
-
async
|
|
1964
|
-
|
|
2228
|
+
async #handleAgentRequest(requestId, method, params, _ctx, originalMessage) {
|
|
2229
|
+
let result;
|
|
2230
|
+
let error;
|
|
2231
|
+
try {
|
|
2232
|
+
switch (method) {
|
|
2233
|
+
case ACP_METHODS.REQUEST_PERMISSION:
|
|
2234
|
+
result = await this.#options.client.requestPermission(
|
|
2235
|
+
params
|
|
2236
|
+
);
|
|
2237
|
+
break;
|
|
2238
|
+
case ACP_METHODS.FS_READ_TEXT_FILE:
|
|
2239
|
+
if (!this.#options.client.readTextFile) {
|
|
2240
|
+
throw new ACPError(-32601, "Method not supported: fs/read_text_file");
|
|
2241
|
+
}
|
|
2242
|
+
result = await this.#options.client.readTextFile(
|
|
2243
|
+
params
|
|
2244
|
+
);
|
|
2245
|
+
break;
|
|
2246
|
+
case ACP_METHODS.FS_WRITE_TEXT_FILE:
|
|
2247
|
+
if (!this.#options.client.writeTextFile) {
|
|
2248
|
+
throw new ACPError(-32601, "Method not supported: fs/write_text_file");
|
|
2249
|
+
}
|
|
2250
|
+
result = await this.#options.client.writeTextFile(
|
|
2251
|
+
params
|
|
2252
|
+
);
|
|
2253
|
+
break;
|
|
2254
|
+
case ACP_METHODS.TERMINAL_CREATE:
|
|
2255
|
+
if (!this.#options.client.createTerminal) {
|
|
2256
|
+
throw new ACPError(-32601, "Method not supported: terminal/create");
|
|
2257
|
+
}
|
|
2258
|
+
result = await this.#options.client.createTerminal(
|
|
2259
|
+
params
|
|
2260
|
+
);
|
|
2261
|
+
break;
|
|
2262
|
+
case ACP_METHODS.TERMINAL_OUTPUT:
|
|
2263
|
+
if (!this.#options.client.terminalOutput) {
|
|
2264
|
+
throw new ACPError(-32601, "Method not supported: terminal/output");
|
|
2265
|
+
}
|
|
2266
|
+
result = await this.#options.client.terminalOutput(
|
|
2267
|
+
params
|
|
2268
|
+
);
|
|
2269
|
+
break;
|
|
2270
|
+
case ACP_METHODS.TERMINAL_RELEASE:
|
|
2271
|
+
if (!this.#options.client.releaseTerminal) {
|
|
2272
|
+
throw new ACPError(-32601, "Method not supported: terminal/release");
|
|
2273
|
+
}
|
|
2274
|
+
result = await this.#options.client.releaseTerminal(
|
|
2275
|
+
params
|
|
2276
|
+
);
|
|
2277
|
+
break;
|
|
2278
|
+
case ACP_METHODS.TERMINAL_WAIT_FOR_EXIT:
|
|
2279
|
+
if (!this.#options.client.waitForTerminalExit) {
|
|
2280
|
+
throw new ACPError(-32601, "Method not supported: terminal/wait_for_exit");
|
|
2281
|
+
}
|
|
2282
|
+
result = await this.#options.client.waitForTerminalExit(
|
|
2283
|
+
params
|
|
2284
|
+
);
|
|
2285
|
+
break;
|
|
2286
|
+
case ACP_METHODS.TERMINAL_KILL:
|
|
2287
|
+
if (!this.#options.client.killTerminal) {
|
|
2288
|
+
throw new ACPError(-32601, "Method not supported: terminal/kill");
|
|
2289
|
+
}
|
|
2290
|
+
result = await this.#options.client.killTerminal(
|
|
2291
|
+
params
|
|
2292
|
+
);
|
|
2293
|
+
break;
|
|
2294
|
+
default:
|
|
2295
|
+
throw new ACPError(-32601, `Unknown method: ${method}`);
|
|
2296
|
+
}
|
|
2297
|
+
} catch (e) {
|
|
2298
|
+
if (e instanceof ACPError) {
|
|
2299
|
+
error = e;
|
|
2300
|
+
} else {
|
|
2301
|
+
error = new ACPError(-32603, e.message);
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
const responseEnvelope = {
|
|
2305
|
+
acp: {
|
|
2306
|
+
jsonrpc: "2.0",
|
|
2307
|
+
id: requestId,
|
|
2308
|
+
...error ? { error: error.toErrorObject() } : { result }
|
|
2309
|
+
},
|
|
2310
|
+
acpContext: {
|
|
2311
|
+
streamId: this.#streamId,
|
|
2312
|
+
sessionId: this.#sessionId,
|
|
2313
|
+
direction: "client-to-agent"
|
|
2314
|
+
}
|
|
2315
|
+
};
|
|
2316
|
+
await this.#mapClient.send(
|
|
2317
|
+
{ agent: this.#options.targetAgent },
|
|
2318
|
+
responseEnvelope,
|
|
2319
|
+
{
|
|
2320
|
+
protocol: "acp",
|
|
2321
|
+
correlationId: originalMessage.id
|
|
2322
|
+
}
|
|
2323
|
+
);
|
|
1965
2324
|
}
|
|
1966
2325
|
/**
|
|
1967
|
-
*
|
|
2326
|
+
* Send an ACP request and wait for response.
|
|
1968
2327
|
*/
|
|
1969
|
-
async
|
|
1970
|
-
|
|
2328
|
+
async #sendRequest(method, params) {
|
|
2329
|
+
if (this.#closed) {
|
|
2330
|
+
throw new Error("ACP stream is closed");
|
|
2331
|
+
}
|
|
2332
|
+
await this.#setupSubscription();
|
|
2333
|
+
if (this.#closed) {
|
|
2334
|
+
throw new Error("ACP stream closed");
|
|
2335
|
+
}
|
|
2336
|
+
const correlationId = `${this.#streamId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
2337
|
+
const timeout = this.#options.timeout ?? 3e4;
|
|
2338
|
+
const envelope = {
|
|
2339
|
+
acp: {
|
|
2340
|
+
jsonrpc: "2.0",
|
|
2341
|
+
id: correlationId,
|
|
2342
|
+
method,
|
|
2343
|
+
...params !== void 0 && { params }
|
|
2344
|
+
},
|
|
2345
|
+
acpContext: {
|
|
2346
|
+
streamId: this.#streamId,
|
|
2347
|
+
sessionId: this.#sessionId,
|
|
2348
|
+
direction: "client-to-agent"
|
|
2349
|
+
}
|
|
2350
|
+
};
|
|
2351
|
+
const resultPromise = new Promise((resolve, reject) => {
|
|
2352
|
+
const timeoutHandle = setTimeout(() => {
|
|
2353
|
+
this.#pendingRequests.delete(correlationId);
|
|
2354
|
+
reject(new Error(`ACP request timed out after ${timeout}ms: ${method}`));
|
|
2355
|
+
}, timeout);
|
|
2356
|
+
this.#pendingRequests.set(correlationId, {
|
|
2357
|
+
resolve,
|
|
2358
|
+
reject,
|
|
2359
|
+
timeout: timeoutHandle,
|
|
2360
|
+
method
|
|
2361
|
+
});
|
|
2362
|
+
});
|
|
2363
|
+
try {
|
|
2364
|
+
await this.#mapClient.send({ agent: this.#options.targetAgent }, envelope, {
|
|
2365
|
+
protocol: "acp",
|
|
2366
|
+
correlationId
|
|
2367
|
+
});
|
|
2368
|
+
} catch (err) {
|
|
2369
|
+
const pending = this.#pendingRequests.get(correlationId);
|
|
2370
|
+
if (pending) {
|
|
2371
|
+
clearTimeout(pending.timeout);
|
|
2372
|
+
this.#pendingRequests.delete(correlationId);
|
|
2373
|
+
}
|
|
2374
|
+
throw err;
|
|
2375
|
+
}
|
|
2376
|
+
if (this.#closed && !this.#pendingRequests.has(correlationId)) {
|
|
2377
|
+
throw new Error("ACP stream closed");
|
|
2378
|
+
}
|
|
2379
|
+
return resultPromise;
|
|
1971
2380
|
}
|
|
1972
2381
|
/**
|
|
1973
|
-
*
|
|
2382
|
+
* Send an ACP notification (no response expected).
|
|
1974
2383
|
*/
|
|
1975
|
-
async
|
|
1976
|
-
|
|
2384
|
+
async #sendNotification(method, params) {
|
|
2385
|
+
if (this.#closed) {
|
|
2386
|
+
throw new Error("ACP stream is closed");
|
|
2387
|
+
}
|
|
2388
|
+
await this.#setupSubscription();
|
|
2389
|
+
const envelope = {
|
|
2390
|
+
acp: {
|
|
2391
|
+
jsonrpc: "2.0",
|
|
2392
|
+
method,
|
|
2393
|
+
...params !== void 0 && { params }
|
|
2394
|
+
},
|
|
2395
|
+
acpContext: {
|
|
2396
|
+
streamId: this.#streamId,
|
|
2397
|
+
sessionId: this.#sessionId,
|
|
2398
|
+
direction: "client-to-agent"
|
|
2399
|
+
}
|
|
2400
|
+
};
|
|
2401
|
+
await this.#mapClient.send({ agent: this.#options.targetAgent }, envelope, {
|
|
2402
|
+
protocol: "acp"
|
|
2403
|
+
});
|
|
1977
2404
|
}
|
|
1978
2405
|
// ===========================================================================
|
|
1979
|
-
//
|
|
2406
|
+
// ACP Lifecycle Methods
|
|
1980
2407
|
// ===========================================================================
|
|
1981
2408
|
/**
|
|
1982
|
-
*
|
|
1983
|
-
*/
|
|
1984
|
-
async listAgents(options) {
|
|
1985
|
-
return this.#connection.sendRequest(OBSERVATION_METHODS.AGENTS_LIST, options);
|
|
1986
|
-
}
|
|
1987
|
-
/**
|
|
1988
|
-
* Get a single agent by ID
|
|
2409
|
+
* Initialize the ACP connection with the target agent.
|
|
1989
2410
|
*/
|
|
1990
|
-
async
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
2411
|
+
async initialize(params) {
|
|
2412
|
+
if (this.#initialized) {
|
|
2413
|
+
throw new Error("ACP stream already initialized");
|
|
2414
|
+
}
|
|
2415
|
+
const result = await this.#sendRequest(
|
|
2416
|
+
ACP_METHODS.INITIALIZE,
|
|
1994
2417
|
params
|
|
1995
2418
|
);
|
|
2419
|
+
this.#initialized = true;
|
|
2420
|
+
this.#capabilities = result.agentCapabilities ?? null;
|
|
2421
|
+
return result;
|
|
1996
2422
|
}
|
|
1997
2423
|
/**
|
|
1998
|
-
*
|
|
2424
|
+
* Authenticate with the agent.
|
|
1999
2425
|
*/
|
|
2000
|
-
async
|
|
2001
|
-
|
|
2426
|
+
async authenticate(params) {
|
|
2427
|
+
if (!this.#initialized) {
|
|
2428
|
+
throw new Error("Must call initialize() before authenticate()");
|
|
2429
|
+
}
|
|
2430
|
+
return this.#sendRequest(
|
|
2431
|
+
ACP_METHODS.AUTHENTICATE,
|
|
2432
|
+
params
|
|
2433
|
+
);
|
|
2002
2434
|
}
|
|
2003
2435
|
// ===========================================================================
|
|
2004
|
-
//
|
|
2436
|
+
// ACP Session Methods
|
|
2005
2437
|
// ===========================================================================
|
|
2006
2438
|
/**
|
|
2007
|
-
*
|
|
2439
|
+
* Create a new ACP session.
|
|
2008
2440
|
*/
|
|
2009
|
-
async
|
|
2010
|
-
|
|
2441
|
+
async newSession(params) {
|
|
2442
|
+
if (!this.#initialized) {
|
|
2443
|
+
throw new Error("Must call initialize() before newSession()");
|
|
2444
|
+
}
|
|
2445
|
+
const result = await this.#sendRequest(
|
|
2446
|
+
ACP_METHODS.SESSION_NEW,
|
|
2447
|
+
params
|
|
2448
|
+
);
|
|
2449
|
+
this.#sessionId = result.sessionId;
|
|
2450
|
+
return result;
|
|
2011
2451
|
}
|
|
2012
2452
|
/**
|
|
2013
|
-
*
|
|
2453
|
+
* Load an existing ACP session.
|
|
2014
2454
|
*/
|
|
2015
|
-
async
|
|
2016
|
-
|
|
2017
|
-
|
|
2455
|
+
async loadSession(params) {
|
|
2456
|
+
if (!this.#initialized) {
|
|
2457
|
+
throw new Error("Must call initialize() before loadSession()");
|
|
2458
|
+
}
|
|
2459
|
+
const result = await this.#sendRequest(
|
|
2460
|
+
ACP_METHODS.SESSION_LOAD,
|
|
2461
|
+
params
|
|
2462
|
+
);
|
|
2463
|
+
this.#sessionId = params.sessionId;
|
|
2464
|
+
return result;
|
|
2018
2465
|
}
|
|
2019
2466
|
/**
|
|
2020
|
-
*
|
|
2467
|
+
* Set the session mode.
|
|
2021
2468
|
*/
|
|
2022
|
-
async
|
|
2023
|
-
return this.#
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2469
|
+
async setSessionMode(params) {
|
|
2470
|
+
return this.#sendRequest(
|
|
2471
|
+
ACP_METHODS.SESSION_SET_MODE,
|
|
2472
|
+
params
|
|
2473
|
+
);
|
|
2027
2474
|
}
|
|
2028
2475
|
// ===========================================================================
|
|
2029
|
-
//
|
|
2476
|
+
// ACP Prompt Methods
|
|
2030
2477
|
// ===========================================================================
|
|
2031
2478
|
/**
|
|
2032
|
-
* Send a
|
|
2033
|
-
|
|
2034
|
-
async send(to, payload, meta) {
|
|
2035
|
-
const params = { to };
|
|
2036
|
-
if (payload !== void 0) params.payload = payload;
|
|
2037
|
-
if (meta) params.meta = meta;
|
|
2038
|
-
return this.#connection.sendRequest(CORE_METHODS.SEND, params);
|
|
2039
|
-
}
|
|
2040
|
-
/**
|
|
2041
|
-
* Send a message to a specific agent
|
|
2042
|
-
*/
|
|
2043
|
-
async sendToAgent(agentId, payload, meta) {
|
|
2044
|
-
return this.send({ agent: agentId }, payload, meta);
|
|
2045
|
-
}
|
|
2046
|
-
/**
|
|
2047
|
-
* Send a message to all agents in a scope
|
|
2048
|
-
*/
|
|
2049
|
-
async sendToScope(scopeId, payload, meta) {
|
|
2050
|
-
return this.send({ scope: scopeId }, payload, meta);
|
|
2051
|
-
}
|
|
2052
|
-
/**
|
|
2053
|
-
* Send a message to agents with a specific role
|
|
2479
|
+
* Send a prompt to the agent.
|
|
2480
|
+
* Updates are received via the sessionUpdate handler.
|
|
2054
2481
|
*/
|
|
2055
|
-
async
|
|
2056
|
-
|
|
2482
|
+
async prompt(params) {
|
|
2483
|
+
if (!this.#sessionId) {
|
|
2484
|
+
throw new Error("Must call newSession() or loadSession() before prompt()");
|
|
2485
|
+
}
|
|
2486
|
+
return this.#sendRequest(
|
|
2487
|
+
ACP_METHODS.SESSION_PROMPT,
|
|
2488
|
+
params
|
|
2489
|
+
);
|
|
2057
2490
|
}
|
|
2058
2491
|
/**
|
|
2059
|
-
*
|
|
2492
|
+
* Cancel ongoing operations for the current session.
|
|
2060
2493
|
*/
|
|
2061
|
-
async
|
|
2062
|
-
|
|
2494
|
+
async cancel(params) {
|
|
2495
|
+
if (!this.#sessionId) {
|
|
2496
|
+
throw new Error("No active session to cancel");
|
|
2497
|
+
}
|
|
2498
|
+
await this.#sendNotification(ACP_METHODS.SESSION_CANCEL, {
|
|
2499
|
+
sessionId: this.#sessionId,
|
|
2500
|
+
...params
|
|
2501
|
+
});
|
|
2063
2502
|
}
|
|
2503
|
+
// ===========================================================================
|
|
2504
|
+
// Extension Methods
|
|
2505
|
+
// ===========================================================================
|
|
2064
2506
|
/**
|
|
2065
|
-
*
|
|
2507
|
+
* Call an ACP extension method on the target agent.
|
|
2066
2508
|
*
|
|
2067
|
-
*
|
|
2068
|
-
*
|
|
2509
|
+
* Extension methods are prefixed with "_" (e.g., "_macro/spawnAgent").
|
|
2510
|
+
* The agent must support the extension for this to succeed.
|
|
2511
|
+
*
|
|
2512
|
+
* @param method - The extension method name (e.g., "_macro/spawnAgent")
|
|
2513
|
+
* @param params - Parameters to pass to the extension method
|
|
2514
|
+
* @returns The result from the extension method
|
|
2515
|
+
*
|
|
2516
|
+
* @example
|
|
2517
|
+
* ```typescript
|
|
2518
|
+
* const result = await acp.callExtension("_macro/spawnAgent", {
|
|
2519
|
+
* task: "Implement feature X",
|
|
2520
|
+
* cwd: "/project"
|
|
2521
|
+
* });
|
|
2522
|
+
* ```
|
|
2069
2523
|
*/
|
|
2070
|
-
async
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
// We'll filter in the handler since subscription filters don't support correlationId
|
|
2074
|
-
});
|
|
2075
|
-
try {
|
|
2076
|
-
await this.send(to, payload, {
|
|
2077
|
-
...options?.meta,
|
|
2078
|
-
expectsResponse: true,
|
|
2079
|
-
correlationId
|
|
2080
|
-
});
|
|
2081
|
-
const timeout = options?.timeout ?? 3e4;
|
|
2082
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
2083
|
-
setTimeout(() => reject(new Error(`Request timed out after ${timeout}ms`)), timeout);
|
|
2084
|
-
});
|
|
2085
|
-
const responsePromise = (async () => {
|
|
2086
|
-
for await (const event of responseSub) {
|
|
2087
|
-
if (event.type === "message_delivered" && event.data && event.data.correlationId === correlationId) {
|
|
2088
|
-
return event.data.message;
|
|
2089
|
-
}
|
|
2090
|
-
}
|
|
2091
|
-
throw new Error("Subscription closed before response received");
|
|
2092
|
-
})();
|
|
2093
|
-
return await Promise.race([responsePromise, timeoutPromise]);
|
|
2094
|
-
} finally {
|
|
2095
|
-
await responseSub.unsubscribe();
|
|
2524
|
+
async callExtension(method, params) {
|
|
2525
|
+
if (!this.#initialized) {
|
|
2526
|
+
throw new Error("Must call initialize() before callExtension()");
|
|
2096
2527
|
}
|
|
2528
|
+
return this.#sendRequest(method, params);
|
|
2097
2529
|
}
|
|
2098
2530
|
// ===========================================================================
|
|
2099
|
-
//
|
|
2531
|
+
// Lifecycle
|
|
2100
2532
|
// ===========================================================================
|
|
2101
2533
|
/**
|
|
2102
|
-
*
|
|
2534
|
+
* Close this ACP stream and clean up resources.
|
|
2103
2535
|
*/
|
|
2104
|
-
async
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
this.#connection.sendNotification(NOTIFICATION_METHODS.SUBSCRIBE_ACK, ackParams);
|
|
2111
|
-
} : void 0;
|
|
2112
|
-
const subscription = createSubscription(
|
|
2113
|
-
result.subscriptionId,
|
|
2114
|
-
() => this.unsubscribe(result.subscriptionId),
|
|
2115
|
-
{ filter },
|
|
2116
|
-
sendAck
|
|
2117
|
-
);
|
|
2118
|
-
if (serverSupportsAck) {
|
|
2119
|
-
subscription._setServerSupportsAck(true);
|
|
2536
|
+
async close() {
|
|
2537
|
+
if (this.#closed) return;
|
|
2538
|
+
this.#closed = true;
|
|
2539
|
+
if (this.#unsubscribeReconnection) {
|
|
2540
|
+
this.#unsubscribeReconnection();
|
|
2541
|
+
this.#unsubscribeReconnection = null;
|
|
2120
2542
|
}
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
subscription
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2543
|
+
for (const [id, pending] of this.#pendingRequests) {
|
|
2544
|
+
clearTimeout(pending.timeout);
|
|
2545
|
+
pending.reject(new Error("ACP stream closed"));
|
|
2546
|
+
this.#pendingRequests.delete(id);
|
|
2547
|
+
}
|
|
2548
|
+
if (this.#subscription) {
|
|
2549
|
+
await this.#subscription.unsubscribe();
|
|
2550
|
+
this.#subscription = null;
|
|
2551
|
+
}
|
|
2552
|
+
this.emit("close");
|
|
2553
|
+
}
|
|
2554
|
+
};
|
|
2555
|
+
function createACPStream(mapClient, options) {
|
|
2556
|
+
return new ACPStreamConnection(mapClient, options);
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
// src/connection/client.ts
|
|
2560
|
+
var ClientConnection = class _ClientConnection {
|
|
2561
|
+
#connection;
|
|
2562
|
+
#subscriptions = /* @__PURE__ */ new Map();
|
|
2563
|
+
#subscriptionStates = /* @__PURE__ */ new Map();
|
|
2564
|
+
#reconnectionHandlers = /* @__PURE__ */ new Set();
|
|
2565
|
+
#acpStreams = /* @__PURE__ */ new Map();
|
|
2566
|
+
#options;
|
|
2567
|
+
#sessionId = null;
|
|
2568
|
+
#serverCapabilities = null;
|
|
2569
|
+
#connected = false;
|
|
2570
|
+
#lastConnectOptions;
|
|
2571
|
+
#isReconnecting = false;
|
|
2572
|
+
#lastResumeToken;
|
|
2573
|
+
#onTokenExpiring;
|
|
2574
|
+
constructor(stream, options = {}) {
|
|
2575
|
+
this.#connection = new BaseConnection(stream, options);
|
|
2576
|
+
this.#options = options;
|
|
2577
|
+
this.#connection.setNotificationHandler(this.#handleNotification.bind(this));
|
|
2578
|
+
if (options.reconnection?.enabled && options.createStream) {
|
|
2579
|
+
this.#connection.onStateChange((newState) => {
|
|
2580
|
+
if (newState === "closed" && this.#connected && !this.#isReconnecting) {
|
|
2581
|
+
void this.#handleDisconnect();
|
|
2132
2582
|
}
|
|
2133
|
-
|
|
2134
|
-
};
|
|
2583
|
+
});
|
|
2135
2584
|
}
|
|
2136
|
-
return subscription;
|
|
2137
2585
|
}
|
|
2586
|
+
// ===========================================================================
|
|
2587
|
+
// Static Factory Methods
|
|
2588
|
+
// ===========================================================================
|
|
2138
2589
|
/**
|
|
2139
|
-
*
|
|
2140
|
-
*/
|
|
2141
|
-
async unsubscribe(subscriptionId) {
|
|
2142
|
-
const subscription = this.#subscriptions.get(subscriptionId);
|
|
2143
|
-
if (subscription) {
|
|
2144
|
-
subscription._close();
|
|
2145
|
-
this.#subscriptions.delete(subscriptionId);
|
|
2146
|
-
}
|
|
2147
|
-
this.#subscriptionStates.delete(subscriptionId);
|
|
2148
|
-
await this.#connection.sendRequest(CORE_METHODS.UNSUBSCRIBE, { subscriptionId });
|
|
2149
|
-
}
|
|
2150
|
-
// ===========================================================================
|
|
2151
|
-
// Event Replay
|
|
2152
|
-
// ===========================================================================
|
|
2153
|
-
/**
|
|
2154
|
-
* Replay historical events.
|
|
2590
|
+
* Connect to a MAP server via WebSocket URL.
|
|
2155
2591
|
*
|
|
2156
|
-
*
|
|
2157
|
-
*
|
|
2592
|
+
* Handles:
|
|
2593
|
+
* - WebSocket creation and connection
|
|
2594
|
+
* - Stream wrapping
|
|
2595
|
+
* - Auto-configuration of createStream for reconnection
|
|
2596
|
+
* - Initial MAP protocol connect handshake
|
|
2597
|
+
*
|
|
2598
|
+
* @param url - WebSocket URL (ws:// or wss://)
|
|
2599
|
+
* @param options - Connection options
|
|
2600
|
+
* @returns Connected ClientConnection instance
|
|
2158
2601
|
*
|
|
2159
2602
|
* @example
|
|
2160
2603
|
* ```typescript
|
|
2161
|
-
*
|
|
2162
|
-
*
|
|
2163
|
-
*
|
|
2164
|
-
* filter: { eventTypes: ['agent.registered'] },
|
|
2165
|
-
* limit: 100
|
|
2604
|
+
* const client = await ClientConnection.connect('ws://localhost:8080', {
|
|
2605
|
+
* name: 'MyClient',
|
|
2606
|
+
* reconnection: true
|
|
2166
2607
|
* });
|
|
2167
2608
|
*
|
|
2168
|
-
* //
|
|
2169
|
-
*
|
|
2170
|
-
* do {
|
|
2171
|
-
* const page = await client.replay({ afterEventId, limit: 100 });
|
|
2172
|
-
* for (const item of page.events) {
|
|
2173
|
-
* console.log(item.eventId, item.event);
|
|
2174
|
-
* }
|
|
2175
|
-
* afterEventId = page.events.at(-1)?.eventId;
|
|
2176
|
-
* } while (page.hasMore);
|
|
2609
|
+
* // Already connected, ready to use
|
|
2610
|
+
* const agents = await client.listAgents();
|
|
2177
2611
|
* ```
|
|
2178
2612
|
*/
|
|
2179
|
-
async
|
|
2180
|
-
const
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2613
|
+
static async connect(url, options) {
|
|
2614
|
+
const parsedUrl = new URL(url);
|
|
2615
|
+
if (!["ws:", "wss:"].includes(parsedUrl.protocol)) {
|
|
2616
|
+
throw new Error(
|
|
2617
|
+
`Unsupported protocol: ${parsedUrl.protocol}. Use ws: or wss:`
|
|
2618
|
+
);
|
|
2619
|
+
}
|
|
2620
|
+
const timeout = options?.connectTimeout ?? 1e4;
|
|
2621
|
+
const ws = new WebSocket(url);
|
|
2622
|
+
await waitForOpen(ws, timeout);
|
|
2623
|
+
const stream = websocketStream(ws);
|
|
2624
|
+
const createStream = async () => {
|
|
2625
|
+
const newWs = new WebSocket(url);
|
|
2626
|
+
await waitForOpen(newWs, timeout);
|
|
2627
|
+
return websocketStream(newWs);
|
|
2628
|
+
};
|
|
2629
|
+
const reconnection = options?.reconnection === true ? { enabled: true } : typeof options?.reconnection === "object" ? options.reconnection : void 0;
|
|
2630
|
+
const client = new _ClientConnection(stream, {
|
|
2631
|
+
name: options?.name,
|
|
2632
|
+
capabilities: options?.capabilities,
|
|
2633
|
+
createStream,
|
|
2634
|
+
reconnection
|
|
2635
|
+
});
|
|
2636
|
+
await client.connect({ auth: options?.auth });
|
|
2637
|
+
return client;
|
|
2185
2638
|
}
|
|
2186
2639
|
/**
|
|
2187
|
-
*
|
|
2640
|
+
* Connect to a MAP server via agentic-mesh transport.
|
|
2188
2641
|
*
|
|
2189
|
-
*
|
|
2642
|
+
* Handles:
|
|
2643
|
+
* - Dynamic import of agentic-mesh (optional peer dependency)
|
|
2644
|
+
* - Stream creation over encrypted mesh tunnel
|
|
2645
|
+
* - Auto-configuration of createStream for reconnection
|
|
2646
|
+
* - Initial MAP protocol connect handshake
|
|
2647
|
+
*
|
|
2648
|
+
* Requires `agentic-mesh` to be installed as a peer dependency.
|
|
2649
|
+
*
|
|
2650
|
+
* @param options - Mesh connection options
|
|
2651
|
+
* @returns Connected ClientConnection instance
|
|
2190
2652
|
*
|
|
2191
2653
|
* @example
|
|
2192
2654
|
* ```typescript
|
|
2193
|
-
*
|
|
2194
|
-
*
|
|
2195
|
-
*
|
|
2196
|
-
*
|
|
2197
|
-
* }
|
|
2655
|
+
* import { createNebulaTransport } from 'agentic-mesh';
|
|
2656
|
+
*
|
|
2657
|
+
* const transport = createNebulaTransport({
|
|
2658
|
+
* configPath: '/etc/nebula/config.yml',
|
|
2659
|
+
* });
|
|
2660
|
+
*
|
|
2661
|
+
* const client = await ClientConnection.connectMesh({
|
|
2662
|
+
* transport,
|
|
2663
|
+
* peer: { peerId: 'server', address: '10.0.0.1', port: 4242 },
|
|
2664
|
+
* localPeerId: 'my-client',
|
|
2665
|
+
* name: 'MeshClient',
|
|
2666
|
+
* reconnection: true
|
|
2667
|
+
* });
|
|
2668
|
+
*
|
|
2669
|
+
* const agents = await client.listAgents();
|
|
2198
2670
|
* ```
|
|
2199
2671
|
*/
|
|
2200
|
-
async
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2672
|
+
static async connectMesh(options) {
|
|
2673
|
+
const { agenticMeshStream: agenticMeshStream2 } = await Promise.resolve().then(() => (init_agentic_mesh(), agentic_mesh_exports));
|
|
2674
|
+
const streamConfig = {
|
|
2675
|
+
transport: options.transport,
|
|
2676
|
+
peer: options.peer,
|
|
2677
|
+
localPeerId: options.localPeerId,
|
|
2678
|
+
timeout: options.timeout
|
|
2679
|
+
};
|
|
2680
|
+
const stream = await agenticMeshStream2(streamConfig);
|
|
2681
|
+
const createStream = async () => agenticMeshStream2(streamConfig);
|
|
2682
|
+
const reconnection = options.reconnection === true ? { enabled: true } : typeof options.reconnection === "object" ? options.reconnection : void 0;
|
|
2683
|
+
const client = new _ClientConnection(stream, {
|
|
2684
|
+
name: options.name,
|
|
2685
|
+
capabilities: options.capabilities,
|
|
2686
|
+
createStream,
|
|
2687
|
+
reconnection
|
|
2688
|
+
});
|
|
2689
|
+
await client.connect({ auth: options.auth });
|
|
2690
|
+
return client;
|
|
2214
2691
|
}
|
|
2215
2692
|
// ===========================================================================
|
|
2216
|
-
//
|
|
2693
|
+
// Connection Lifecycle
|
|
2217
2694
|
// ===========================================================================
|
|
2218
2695
|
/**
|
|
2219
|
-
*
|
|
2696
|
+
* Connect to the MAP system
|
|
2697
|
+
*
|
|
2698
|
+
* @param options - Connection options
|
|
2699
|
+
* @param options.sessionId - Specific session ID to use
|
|
2700
|
+
* @param options.resumeToken - Token to resume a previously disconnected session
|
|
2701
|
+
* @param options.auth - Authentication credentials
|
|
2702
|
+
* @param options.onTokenExpiring - Callback invoked before token expires for proactive refresh
|
|
2220
2703
|
*/
|
|
2221
|
-
async
|
|
2222
|
-
const params = {
|
|
2223
|
-
|
|
2224
|
-
|
|
2704
|
+
async connect(options) {
|
|
2705
|
+
const params = {
|
|
2706
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
2707
|
+
participantType: "client",
|
|
2708
|
+
name: this.#options.name,
|
|
2709
|
+
capabilities: this.#options.capabilities,
|
|
2710
|
+
sessionId: options?.sessionId,
|
|
2711
|
+
resumeToken: options?.resumeToken,
|
|
2712
|
+
auth: options?.auth
|
|
2713
|
+
};
|
|
2714
|
+
const result = await this.#connection.sendRequest(CORE_METHODS.CONNECT, params);
|
|
2715
|
+
this.#sessionId = result.sessionId;
|
|
2716
|
+
this.#serverCapabilities = result.capabilities;
|
|
2717
|
+
this.#connected = true;
|
|
2718
|
+
if (result.resumeToken) {
|
|
2719
|
+
this.#lastResumeToken = result.resumeToken;
|
|
2720
|
+
}
|
|
2721
|
+
if (options?.onTokenExpiring) {
|
|
2722
|
+
this.#onTokenExpiring = options.onTokenExpiring;
|
|
2723
|
+
this.#setupTokenExpiryMonitoring(result);
|
|
2724
|
+
}
|
|
2725
|
+
this.#connection._transitionTo("connected");
|
|
2726
|
+
this.#lastConnectOptions = options;
|
|
2727
|
+
return result;
|
|
2225
2728
|
}
|
|
2226
|
-
// ===========================================================================
|
|
2227
|
-
// Lifecycle Control (requires canStop capability)
|
|
2228
|
-
// ===========================================================================
|
|
2229
2729
|
/**
|
|
2230
|
-
*
|
|
2730
|
+
* Get the resume token for this session.
|
|
2731
|
+
* Can be used to reconnect and restore session state after disconnection.
|
|
2732
|
+
*
|
|
2733
|
+
* @returns The resume token, or undefined if not available
|
|
2231
2734
|
*/
|
|
2232
|
-
|
|
2233
|
-
return this.#
|
|
2234
|
-
agentId,
|
|
2235
|
-
...options
|
|
2236
|
-
});
|
|
2735
|
+
getResumeToken() {
|
|
2736
|
+
return this.#lastResumeToken;
|
|
2237
2737
|
}
|
|
2238
2738
|
/**
|
|
2239
|
-
*
|
|
2739
|
+
* Reconnect to the server, optionally using a resume token to restore session.
|
|
2740
|
+
*
|
|
2741
|
+
* @param resumeToken - Token to resume previous session. If not provided, uses the last known token.
|
|
2742
|
+
* @returns Connect response result
|
|
2743
|
+
*
|
|
2744
|
+
* @example
|
|
2745
|
+
* ```typescript
|
|
2746
|
+
* // Save token before disconnect
|
|
2747
|
+
* const token = await client.disconnect();
|
|
2748
|
+
*
|
|
2749
|
+
* // Later, reconnect with the token
|
|
2750
|
+
* const result = await client.reconnect(token);
|
|
2751
|
+
* console.log('Reconnected:', result.reconnected);
|
|
2752
|
+
* ```
|
|
2240
2753
|
*/
|
|
2241
|
-
async
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2754
|
+
async reconnect(resumeToken) {
|
|
2755
|
+
const tokenToUse = resumeToken ?? this.#lastResumeToken;
|
|
2756
|
+
return this.connect({
|
|
2757
|
+
...this.#lastConnectOptions,
|
|
2758
|
+
resumeToken: tokenToUse
|
|
2245
2759
|
});
|
|
2246
2760
|
}
|
|
2247
2761
|
/**
|
|
2248
|
-
*
|
|
2249
|
-
*/
|
|
2250
|
-
async resumeAgent(agentId) {
|
|
2251
|
-
return this.#connection.sendRequest(STATE_METHODS.AGENTS_RESUME, { agentId });
|
|
2252
|
-
}
|
|
2253
|
-
// ===========================================================================
|
|
2254
|
-
// Reconnection
|
|
2255
|
-
// ===========================================================================
|
|
2256
|
-
/**
|
|
2257
|
-
* Current connection state
|
|
2258
|
-
*/
|
|
2259
|
-
get state() {
|
|
2260
|
-
return this.#connection.state;
|
|
2261
|
-
}
|
|
2262
|
-
/**
|
|
2263
|
-
* Whether the connection is currently reconnecting
|
|
2762
|
+
* Set up monitoring for token expiration
|
|
2264
2763
|
*/
|
|
2265
|
-
|
|
2266
|
-
|
|
2764
|
+
#setupTokenExpiryMonitoring(connectResult) {
|
|
2765
|
+
const principal = connectResult.principal;
|
|
2766
|
+
if (!principal?.expiresAt || !this.#onTokenExpiring) {
|
|
2767
|
+
return;
|
|
2768
|
+
}
|
|
2769
|
+
const expiresAt = principal.expiresAt;
|
|
2770
|
+
const now = Date.now();
|
|
2771
|
+
const warningTime = expiresAt - 6e4;
|
|
2772
|
+
const delay = warningTime - now;
|
|
2773
|
+
if (delay > 0) {
|
|
2774
|
+
setTimeout(async () => {
|
|
2775
|
+
if (!this.#connected || !this.#onTokenExpiring) return;
|
|
2776
|
+
try {
|
|
2777
|
+
const newCredentials = await this.#onTokenExpiring(expiresAt);
|
|
2778
|
+
if (newCredentials) {
|
|
2779
|
+
const refreshResult = await this.refreshAuth({
|
|
2780
|
+
method: newCredentials.method,
|
|
2781
|
+
credential: newCredentials.credential
|
|
2782
|
+
});
|
|
2783
|
+
if (refreshResult.success && refreshResult.principal?.expiresAt) {
|
|
2784
|
+
this.#setupTokenExpiryMonitoring({
|
|
2785
|
+
...connectResult,
|
|
2786
|
+
principal: refreshResult.principal
|
|
2787
|
+
});
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
} catch {
|
|
2791
|
+
}
|
|
2792
|
+
}, delay);
|
|
2793
|
+
}
|
|
2267
2794
|
}
|
|
2268
2795
|
/**
|
|
2269
|
-
*
|
|
2796
|
+
* Authenticate with the server after connection.
|
|
2270
2797
|
*
|
|
2271
|
-
*
|
|
2272
|
-
*
|
|
2798
|
+
* Use this when the server returns `authRequired` in the connect response,
|
|
2799
|
+
* indicating that authentication is needed before accessing protected resources.
|
|
2800
|
+
*
|
|
2801
|
+
* @param auth - Authentication credentials
|
|
2802
|
+
* @returns Authentication result with principal if successful
|
|
2273
2803
|
*
|
|
2274
2804
|
* @example
|
|
2275
2805
|
* ```typescript
|
|
2276
|
-
* const
|
|
2277
|
-
*
|
|
2278
|
-
*
|
|
2279
|
-
*
|
|
2280
|
-
*
|
|
2281
|
-
*
|
|
2282
|
-
*
|
|
2283
|
-
*
|
|
2284
|
-
*
|
|
2285
|
-
*
|
|
2286
|
-
* break;
|
|
2287
|
-
* case 'reconnectFailed':
|
|
2288
|
-
* console.log('Failed to reconnect:', event.error);
|
|
2289
|
-
* break;
|
|
2806
|
+
* const connectResult = await client.connect();
|
|
2807
|
+
*
|
|
2808
|
+
* if (connectResult.authRequired) {
|
|
2809
|
+
* const authResult = await client.authenticate({
|
|
2810
|
+
* method: 'api-key',
|
|
2811
|
+
* credential: process.env.API_KEY,
|
|
2812
|
+
* });
|
|
2813
|
+
*
|
|
2814
|
+
* if (authResult.success) {
|
|
2815
|
+
* console.log('Authenticated as:', authResult.principal?.id);
|
|
2290
2816
|
* }
|
|
2291
|
-
* }
|
|
2817
|
+
* }
|
|
2292
2818
|
* ```
|
|
2293
2819
|
*/
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2820
|
+
async authenticate(auth) {
|
|
2821
|
+
const params = {
|
|
2822
|
+
method: auth.method,
|
|
2823
|
+
credential: auth.credential
|
|
2824
|
+
};
|
|
2825
|
+
const result = await this.#connection.sendRequest(AUTH_METHODS.AUTHENTICATE, params);
|
|
2826
|
+
if (result.success && result.sessionId) {
|
|
2827
|
+
this.#sessionId = result.sessionId;
|
|
2828
|
+
}
|
|
2829
|
+
return result;
|
|
2297
2830
|
}
|
|
2298
2831
|
/**
|
|
2299
|
-
*
|
|
2832
|
+
* Refresh authentication credentials.
|
|
2300
2833
|
*
|
|
2301
|
-
*
|
|
2302
|
-
*
|
|
2834
|
+
* Use this to update credentials before they expire for long-lived connections.
|
|
2835
|
+
*
|
|
2836
|
+
* @param auth - New authentication credentials
|
|
2837
|
+
* @returns Updated principal information
|
|
2303
2838
|
*/
|
|
2304
|
-
|
|
2305
|
-
|
|
2839
|
+
async refreshAuth(auth) {
|
|
2840
|
+
const params = {
|
|
2841
|
+
method: auth.method,
|
|
2842
|
+
credential: auth.credential
|
|
2843
|
+
};
|
|
2844
|
+
return this.#connection.sendRequest(AUTH_METHODS.AUTH_REFRESH, params);
|
|
2306
2845
|
}
|
|
2307
|
-
// ===========================================================================
|
|
2308
|
-
// Internal
|
|
2309
|
-
// ===========================================================================
|
|
2310
2846
|
/**
|
|
2311
|
-
*
|
|
2847
|
+
* Disconnect from the MAP system
|
|
2848
|
+
* @param reason - Optional reason for disconnecting
|
|
2849
|
+
* @returns Resume token that can be used to resume this session later
|
|
2312
2850
|
*/
|
|
2313
|
-
async
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
}
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2851
|
+
async disconnect(reason) {
|
|
2852
|
+
if (!this.#connected) return void 0;
|
|
2853
|
+
let resumeToken;
|
|
2854
|
+
try {
|
|
2855
|
+
const result = await this.#connection.sendRequest(
|
|
2856
|
+
CORE_METHODS.DISCONNECT,
|
|
2857
|
+
reason ? { reason } : void 0
|
|
2858
|
+
);
|
|
2859
|
+
resumeToken = result.resumeToken;
|
|
2860
|
+
if (resumeToken) {
|
|
2861
|
+
this.#lastResumeToken = resumeToken;
|
|
2862
|
+
}
|
|
2863
|
+
} finally {
|
|
2864
|
+
for (const stream of this.#acpStreams.values()) {
|
|
2865
|
+
await stream.close();
|
|
2866
|
+
}
|
|
2867
|
+
this.#acpStreams.clear();
|
|
2868
|
+
for (const subscription of this.#subscriptions.values()) {
|
|
2869
|
+
subscription._close();
|
|
2870
|
+
}
|
|
2871
|
+
this.#subscriptions.clear();
|
|
2872
|
+
await this.#connection.close();
|
|
2873
|
+
this.#connected = false;
|
|
2330
2874
|
}
|
|
2875
|
+
return resumeToken;
|
|
2331
2876
|
}
|
|
2332
2877
|
/**
|
|
2333
|
-
*
|
|
2878
|
+
* Whether the client is connected
|
|
2334
2879
|
*/
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
try {
|
|
2338
|
-
handler(event);
|
|
2339
|
-
} catch (error) {
|
|
2340
|
-
console.error("MAP: Reconnection event handler error:", error);
|
|
2341
|
-
}
|
|
2342
|
-
}
|
|
2880
|
+
get isConnected() {
|
|
2881
|
+
return this.#connected && !this.#connection.isClosed;
|
|
2343
2882
|
}
|
|
2344
2883
|
/**
|
|
2345
|
-
*
|
|
2884
|
+
* Current session ID
|
|
2346
2885
|
*/
|
|
2347
|
-
|
|
2348
|
-
this.#
|
|
2349
|
-
this.#connected = false;
|
|
2350
|
-
this.#emitReconnectionEvent({ type: "disconnected" });
|
|
2351
|
-
try {
|
|
2352
|
-
await this.#attemptReconnect();
|
|
2353
|
-
} catch (error) {
|
|
2354
|
-
this.#isReconnecting = false;
|
|
2355
|
-
this.#emitReconnectionEvent({
|
|
2356
|
-
type: "reconnectFailed",
|
|
2357
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
2358
|
-
});
|
|
2359
|
-
}
|
|
2886
|
+
get sessionId() {
|
|
2887
|
+
return this.#sessionId;
|
|
2360
2888
|
}
|
|
2361
2889
|
/**
|
|
2362
|
-
*
|
|
2890
|
+
* Server capabilities
|
|
2363
2891
|
*/
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
const createStream = this.#options.createStream;
|
|
2367
|
-
const retryPolicy = {
|
|
2368
|
-
maxRetries: options.maxRetries ?? DEFAULT_RETRY_POLICY.maxRetries,
|
|
2369
|
-
baseDelayMs: options.baseDelayMs ?? DEFAULT_RETRY_POLICY.baseDelayMs,
|
|
2370
|
-
maxDelayMs: options.maxDelayMs ?? DEFAULT_RETRY_POLICY.maxDelayMs,
|
|
2371
|
-
jitter: options.jitter ?? DEFAULT_RETRY_POLICY.jitter
|
|
2372
|
-
};
|
|
2373
|
-
await withRetry(
|
|
2374
|
-
async () => {
|
|
2375
|
-
const newStream = await createStream();
|
|
2376
|
-
await this.#connection.reconnect(newStream);
|
|
2377
|
-
const connectResult = await this.connect(this.#lastConnectOptions);
|
|
2378
|
-
this.#sessionId = connectResult.sessionId;
|
|
2379
|
-
this.#serverCapabilities = connectResult.capabilities;
|
|
2380
|
-
},
|
|
2381
|
-
retryPolicy,
|
|
2382
|
-
{
|
|
2383
|
-
onRetry: (state) => {
|
|
2384
|
-
this.#emitReconnectionEvent({
|
|
2385
|
-
type: "reconnecting",
|
|
2386
|
-
attempt: state.attempt,
|
|
2387
|
-
delay: state.nextDelayMs,
|
|
2388
|
-
error: state.lastError
|
|
2389
|
-
});
|
|
2390
|
-
}
|
|
2391
|
-
}
|
|
2392
|
-
);
|
|
2393
|
-
this.#isReconnecting = false;
|
|
2394
|
-
this.#emitReconnectionEvent({ type: "reconnected" });
|
|
2395
|
-
if (options.restoreSubscriptions !== false) {
|
|
2396
|
-
await this.#restoreSubscriptions();
|
|
2397
|
-
}
|
|
2892
|
+
get serverCapabilities() {
|
|
2893
|
+
return this.#serverCapabilities;
|
|
2398
2894
|
}
|
|
2399
2895
|
/**
|
|
2400
|
-
*
|
|
2896
|
+
* AbortSignal that triggers when the connection closes
|
|
2401
2897
|
*/
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
const subscriptionEntries = Array.from(this.#subscriptionStates.entries());
|
|
2405
|
-
this.#subscriptions.clear();
|
|
2406
|
-
this.#subscriptionStates.clear();
|
|
2407
|
-
for (const [oldId, state] of subscriptionEntries) {
|
|
2408
|
-
try {
|
|
2409
|
-
const newSubscription = await this.subscribe(state.filter);
|
|
2410
|
-
const newId = newSubscription.id;
|
|
2411
|
-
if (options.replayOnRestore !== false && state.lastEventId) {
|
|
2412
|
-
const maxEvents = options.maxReplayEventsPerSubscription ?? 1e3;
|
|
2413
|
-
try {
|
|
2414
|
-
let replayedCount = 0;
|
|
2415
|
-
let afterEventId = state.lastEventId;
|
|
2416
|
-
let hasMore = true;
|
|
2417
|
-
while (hasMore && replayedCount < maxEvents) {
|
|
2418
|
-
const result = await this.replay({
|
|
2419
|
-
afterEventId,
|
|
2420
|
-
filter: state.filter,
|
|
2421
|
-
limit: Math.min(100, maxEvents - replayedCount)
|
|
2422
|
-
});
|
|
2423
|
-
for (const replayedEvent of result.events) {
|
|
2424
|
-
if (replayedCount >= maxEvents) break;
|
|
2425
|
-
newSubscription._pushEvent({
|
|
2426
|
-
subscriptionId: newId,
|
|
2427
|
-
sequenceNumber: replayedCount + 1,
|
|
2428
|
-
eventId: replayedEvent.eventId,
|
|
2429
|
-
timestamp: replayedEvent.timestamp,
|
|
2430
|
-
event: replayedEvent.event
|
|
2431
|
-
});
|
|
2432
|
-
replayedCount++;
|
|
2433
|
-
}
|
|
2434
|
-
hasMore = result.hasMore;
|
|
2435
|
-
afterEventId = result.events.at(-1)?.eventId;
|
|
2436
|
-
if (result.events.length === 0) {
|
|
2437
|
-
break;
|
|
2438
|
-
}
|
|
2439
|
-
}
|
|
2440
|
-
} catch (replayError) {
|
|
2441
|
-
console.warn("MAP: Failed to replay events for subscription:", oldId, replayError);
|
|
2442
|
-
}
|
|
2443
|
-
}
|
|
2444
|
-
this.#emitReconnectionEvent({
|
|
2445
|
-
type: "subscriptionRestored",
|
|
2446
|
-
subscriptionId: oldId,
|
|
2447
|
-
newSubscriptionId: newId
|
|
2448
|
-
});
|
|
2449
|
-
} catch (error) {
|
|
2450
|
-
this.#emitReconnectionEvent({
|
|
2451
|
-
type: "subscriptionRestoreFailed",
|
|
2452
|
-
subscriptionId: oldId,
|
|
2453
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
2454
|
-
});
|
|
2455
|
-
}
|
|
2456
|
-
}
|
|
2898
|
+
get signal() {
|
|
2899
|
+
return this.#connection.signal;
|
|
2457
2900
|
}
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
#subscriptions = /* @__PURE__ */ new Map();
|
|
2464
|
-
#options;
|
|
2465
|
-
#messageHandlers = /* @__PURE__ */ new Set();
|
|
2466
|
-
#reconnectionHandlers = /* @__PURE__ */ new Set();
|
|
2467
|
-
#scopeMemberships = /* @__PURE__ */ new Set();
|
|
2468
|
-
#agentId = null;
|
|
2469
|
-
#sessionId = null;
|
|
2470
|
-
#serverCapabilities = null;
|
|
2471
|
-
#currentState = "registered";
|
|
2472
|
-
#connected = false;
|
|
2473
|
-
#lastConnectOptions;
|
|
2474
|
-
#isReconnecting = false;
|
|
2475
|
-
constructor(stream, options = {}) {
|
|
2476
|
-
this.#connection = new BaseConnection(stream, options);
|
|
2477
|
-
this.#options = options;
|
|
2478
|
-
this.#connection.setNotificationHandler(this.#handleNotification.bind(this));
|
|
2479
|
-
if (options.reconnection?.enabled && options.createStream) {
|
|
2480
|
-
this.#connection.onStateChange((newState) => {
|
|
2481
|
-
if (newState === "closed" && this.#connected && !this.#isReconnecting) {
|
|
2482
|
-
void this.#handleDisconnect();
|
|
2483
|
-
}
|
|
2484
|
-
});
|
|
2485
|
-
}
|
|
2901
|
+
/**
|
|
2902
|
+
* Promise that resolves when the connection closes
|
|
2903
|
+
*/
|
|
2904
|
+
get closed() {
|
|
2905
|
+
return this.#connection.closed;
|
|
2486
2906
|
}
|
|
2487
2907
|
// ===========================================================================
|
|
2488
|
-
//
|
|
2908
|
+
// Session Management
|
|
2489
2909
|
// ===========================================================================
|
|
2490
2910
|
/**
|
|
2491
|
-
*
|
|
2492
|
-
*
|
|
2493
|
-
* Handles:
|
|
2494
|
-
* - WebSocket creation and connection
|
|
2495
|
-
* - Stream wrapping
|
|
2496
|
-
* - Auto-configuration of createStream for reconnection
|
|
2497
|
-
* - Initial MAP protocol connect handshake
|
|
2498
|
-
* - Agent registration
|
|
2499
|
-
*
|
|
2500
|
-
* @param url - WebSocket URL (ws:// or wss://)
|
|
2501
|
-
* @param options - Connection and agent options
|
|
2502
|
-
* @returns Connected and registered AgentConnection instance
|
|
2503
|
-
*
|
|
2504
|
-
* @example
|
|
2505
|
-
* ```typescript
|
|
2506
|
-
* const agent = await AgentConnection.connect('ws://localhost:8080', {
|
|
2507
|
-
* name: 'Worker',
|
|
2508
|
-
* role: 'processor',
|
|
2509
|
-
* reconnection: true
|
|
2510
|
-
* });
|
|
2511
|
-
*
|
|
2512
|
-
* // Already registered, ready to work
|
|
2513
|
-
* agent.onMessage(handleMessage);
|
|
2514
|
-
* await agent.busy();
|
|
2515
|
-
* ```
|
|
2911
|
+
* List available sessions
|
|
2516
2912
|
*/
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
return websocketStream(newWs);
|
|
2532
|
-
};
|
|
2533
|
-
const reconnection = options?.reconnection === true ? { enabled: true } : typeof options?.reconnection === "object" ? options.reconnection : void 0;
|
|
2534
|
-
const agent = new _AgentConnection(stream, {
|
|
2535
|
-
name: options?.name,
|
|
2536
|
-
role: options?.role,
|
|
2537
|
-
capabilities: options?.capabilities,
|
|
2538
|
-
visibility: options?.visibility,
|
|
2539
|
-
parent: options?.parent,
|
|
2540
|
-
scopes: options?.scopes,
|
|
2541
|
-
createStream,
|
|
2542
|
-
reconnection
|
|
2543
|
-
});
|
|
2544
|
-
await agent.connect({ auth: options?.auth });
|
|
2545
|
-
return agent;
|
|
2913
|
+
async listSessions() {
|
|
2914
|
+
return this.#connection.sendRequest(SESSION_METHODS.SESSION_LIST);
|
|
2915
|
+
}
|
|
2916
|
+
/**
|
|
2917
|
+
* Load an existing session
|
|
2918
|
+
*/
|
|
2919
|
+
async loadSession(sessionId) {
|
|
2920
|
+
return this.#connection.sendRequest(SESSION_METHODS.SESSION_LOAD, { sessionId });
|
|
2921
|
+
}
|
|
2922
|
+
/**
|
|
2923
|
+
* Close the current session
|
|
2924
|
+
*/
|
|
2925
|
+
async closeSession(sessionId) {
|
|
2926
|
+
return this.#connection.sendRequest(SESSION_METHODS.SESSION_CLOSE, { sessionId });
|
|
2546
2927
|
}
|
|
2547
2928
|
// ===========================================================================
|
|
2548
|
-
//
|
|
2929
|
+
// Agent Queries
|
|
2549
2930
|
// ===========================================================================
|
|
2550
2931
|
/**
|
|
2551
|
-
*
|
|
2932
|
+
* List agents with optional filters
|
|
2552
2933
|
*/
|
|
2553
|
-
async
|
|
2554
|
-
|
|
2555
|
-
protocolVersion: PROTOCOL_VERSION,
|
|
2556
|
-
participantType: "agent",
|
|
2557
|
-
participantId: options?.agentId,
|
|
2558
|
-
name: this.#options.name,
|
|
2559
|
-
capabilities: this.#options.capabilities,
|
|
2560
|
-
resumeToken: options?.resumeToken,
|
|
2561
|
-
auth: options?.auth
|
|
2562
|
-
};
|
|
2563
|
-
const connectResult = await this.#connection.sendRequest(CORE_METHODS.CONNECT, connectParams);
|
|
2564
|
-
this.#sessionId = connectResult.sessionId;
|
|
2565
|
-
this.#serverCapabilities = connectResult.capabilities;
|
|
2566
|
-
this.#connected = true;
|
|
2567
|
-
this.#lastConnectOptions = options;
|
|
2568
|
-
const registerParams = {
|
|
2569
|
-
agentId: options?.agentId,
|
|
2570
|
-
name: this.#options.name,
|
|
2571
|
-
role: this.#options.role,
|
|
2572
|
-
parent: this.#options.parent,
|
|
2573
|
-
scopes: this.#options.scopes,
|
|
2574
|
-
visibility: this.#options.visibility,
|
|
2575
|
-
capabilities: this.#options.capabilities
|
|
2576
|
-
};
|
|
2577
|
-
const registerResult = await this.#connection.sendRequest(LIFECYCLE_METHODS.AGENTS_REGISTER, registerParams);
|
|
2578
|
-
this.#agentId = registerResult.agent.id;
|
|
2579
|
-
this.#currentState = registerResult.agent.state;
|
|
2580
|
-
this.#connection._transitionTo("connected");
|
|
2581
|
-
return { connection: connectResult, agent: registerResult.agent };
|
|
2934
|
+
async listAgents(options) {
|
|
2935
|
+
return this.#connection.sendRequest(OBSERVATION_METHODS.AGENTS_LIST, options);
|
|
2582
2936
|
}
|
|
2583
2937
|
/**
|
|
2584
|
-
*
|
|
2585
|
-
* @param reason - Optional reason for disconnecting
|
|
2586
|
-
* @returns Resume token that can be used to resume this session later
|
|
2938
|
+
* Get a single agent by ID
|
|
2587
2939
|
*/
|
|
2588
|
-
async
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
agentId: this.#agentId,
|
|
2595
|
-
reason
|
|
2596
|
-
});
|
|
2597
|
-
}
|
|
2598
|
-
const result = await this.#connection.sendRequest(
|
|
2599
|
-
CORE_METHODS.DISCONNECT,
|
|
2600
|
-
reason ? { reason } : void 0
|
|
2601
|
-
);
|
|
2602
|
-
resumeToken = result.resumeToken;
|
|
2603
|
-
} finally {
|
|
2604
|
-
for (const subscription of this.#subscriptions.values()) {
|
|
2605
|
-
subscription._close();
|
|
2606
|
-
}
|
|
2607
|
-
this.#subscriptions.clear();
|
|
2608
|
-
await this.#connection.close();
|
|
2609
|
-
this.#connected = false;
|
|
2610
|
-
}
|
|
2611
|
-
return resumeToken;
|
|
2940
|
+
async getAgent(agentId, options) {
|
|
2941
|
+
const params = { agentId, ...options };
|
|
2942
|
+
return this.#connection.sendRequest(
|
|
2943
|
+
OBSERVATION_METHODS.AGENTS_GET,
|
|
2944
|
+
params
|
|
2945
|
+
);
|
|
2612
2946
|
}
|
|
2613
2947
|
/**
|
|
2614
|
-
*
|
|
2948
|
+
* Get the agent structure/hierarchy graph
|
|
2615
2949
|
*/
|
|
2616
|
-
|
|
2617
|
-
return this.#
|
|
2950
|
+
async getStructureGraph(options) {
|
|
2951
|
+
return this.#connection.sendRequest(OBSERVATION_METHODS.STRUCTURE_GRAPH, options);
|
|
2618
2952
|
}
|
|
2953
|
+
// ===========================================================================
|
|
2954
|
+
// Scope Queries
|
|
2955
|
+
// ===========================================================================
|
|
2619
2956
|
/**
|
|
2620
|
-
*
|
|
2957
|
+
* List scopes
|
|
2621
2958
|
*/
|
|
2622
|
-
|
|
2623
|
-
return this.#
|
|
2959
|
+
async listScopes(options) {
|
|
2960
|
+
return this.#connection.sendRequest(OBSERVATION_METHODS.SCOPES_LIST, options);
|
|
2624
2961
|
}
|
|
2625
2962
|
/**
|
|
2626
|
-
*
|
|
2963
|
+
* Get a single scope by ID
|
|
2627
2964
|
*/
|
|
2628
|
-
|
|
2629
|
-
|
|
2965
|
+
async getScope(scopeId) {
|
|
2966
|
+
const result = await this.#connection.sendRequest(OBSERVATION_METHODS.SCOPES_GET, { scopeId });
|
|
2967
|
+
return result.scope;
|
|
2630
2968
|
}
|
|
2631
2969
|
/**
|
|
2632
|
-
*
|
|
2970
|
+
* List members of a scope
|
|
2633
2971
|
*/
|
|
2634
|
-
|
|
2635
|
-
return this.#
|
|
2972
|
+
async getScopeMembers(scopeId, options) {
|
|
2973
|
+
return this.#connection.sendRequest(OBSERVATION_METHODS.SCOPES_MEMBERS, {
|
|
2974
|
+
scopeId,
|
|
2975
|
+
...options
|
|
2976
|
+
});
|
|
2636
2977
|
}
|
|
2978
|
+
// ===========================================================================
|
|
2979
|
+
// Messaging
|
|
2980
|
+
// ===========================================================================
|
|
2637
2981
|
/**
|
|
2638
|
-
*
|
|
2982
|
+
* Send a message to an address
|
|
2639
2983
|
*/
|
|
2640
|
-
|
|
2641
|
-
|
|
2984
|
+
async send(to, payload, meta) {
|
|
2985
|
+
const params = { to };
|
|
2986
|
+
if (payload !== void 0) params.payload = payload;
|
|
2987
|
+
if (meta) params.meta = meta;
|
|
2988
|
+
return this.#connection.sendRequest(CORE_METHODS.SEND, params);
|
|
2642
2989
|
}
|
|
2643
2990
|
/**
|
|
2644
|
-
*
|
|
2991
|
+
* Send a message to a specific agent
|
|
2645
2992
|
*/
|
|
2646
|
-
|
|
2647
|
-
return this
|
|
2993
|
+
async sendToAgent(agentId, payload, meta) {
|
|
2994
|
+
return this.send({ agent: agentId }, payload, meta);
|
|
2648
2995
|
}
|
|
2649
2996
|
/**
|
|
2650
|
-
*
|
|
2997
|
+
* Send a message to all agents in a scope
|
|
2651
2998
|
*/
|
|
2652
|
-
|
|
2653
|
-
return this
|
|
2999
|
+
async sendToScope(scopeId, payload, meta) {
|
|
3000
|
+
return this.send({ scope: scopeId }, payload, meta);
|
|
2654
3001
|
}
|
|
2655
|
-
// ===========================================================================
|
|
2656
|
-
// Message Handling
|
|
2657
|
-
// ===========================================================================
|
|
2658
3002
|
/**
|
|
2659
|
-
*
|
|
3003
|
+
* Send a message to agents with a specific role
|
|
2660
3004
|
*/
|
|
2661
|
-
|
|
2662
|
-
this
|
|
2663
|
-
return this;
|
|
3005
|
+
async sendToRole(role, payload, meta, withinScope) {
|
|
3006
|
+
return this.send({ role, within: withinScope }, payload, meta);
|
|
2664
3007
|
}
|
|
2665
3008
|
/**
|
|
2666
|
-
*
|
|
3009
|
+
* Broadcast a message to all agents
|
|
2667
3010
|
*/
|
|
2668
|
-
|
|
2669
|
-
this
|
|
2670
|
-
return this;
|
|
3011
|
+
async broadcast(payload, meta) {
|
|
3012
|
+
return this.send({ broadcast: true }, payload, meta);
|
|
2671
3013
|
}
|
|
2672
|
-
// ===========================================================================
|
|
2673
|
-
// State Management
|
|
2674
|
-
// ===========================================================================
|
|
2675
3014
|
/**
|
|
2676
|
-
*
|
|
3015
|
+
* Send a request and wait for a correlated response
|
|
3016
|
+
*
|
|
3017
|
+
* This is a higher-level pattern for request/response messaging.
|
|
3018
|
+
* A correlationId is automatically generated.
|
|
2677
3019
|
*/
|
|
2678
|
-
async
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
const result = await this.#connection.sendRequest(STATE_METHODS.AGENTS_UPDATE, {
|
|
2683
|
-
agentId: this.#agentId,
|
|
2684
|
-
state
|
|
3020
|
+
async request(to, payload, options) {
|
|
3021
|
+
const correlationId = `req-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
3022
|
+
const responseSub = await this.subscribe({
|
|
3023
|
+
// We'll filter in the handler since subscription filters don't support correlationId
|
|
2685
3024
|
});
|
|
2686
|
-
|
|
2687
|
-
|
|
3025
|
+
try {
|
|
3026
|
+
await this.send(to, payload, {
|
|
3027
|
+
...options?.meta,
|
|
3028
|
+
expectsResponse: true,
|
|
3029
|
+
correlationId
|
|
3030
|
+
});
|
|
3031
|
+
const timeout = options?.timeout ?? 3e4;
|
|
3032
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
3033
|
+
setTimeout(() => reject(new Error(`Request timed out after ${timeout}ms`)), timeout);
|
|
3034
|
+
});
|
|
3035
|
+
const responsePromise = (async () => {
|
|
3036
|
+
for await (const event of responseSub) {
|
|
3037
|
+
if (event.type === "message_delivered" && event.data && event.data.correlationId === correlationId) {
|
|
3038
|
+
return event.data.message;
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
throw new Error("Subscription closed before response received");
|
|
3042
|
+
})();
|
|
3043
|
+
return await Promise.race([responsePromise, timeoutPromise]);
|
|
3044
|
+
} finally {
|
|
3045
|
+
await responseSub.unsubscribe();
|
|
3046
|
+
}
|
|
2688
3047
|
}
|
|
3048
|
+
// ===========================================================================
|
|
3049
|
+
// ACP Streams
|
|
3050
|
+
// ===========================================================================
|
|
2689
3051
|
/**
|
|
2690
|
-
*
|
|
3052
|
+
* Create a virtual ACP stream connection to an agent.
|
|
3053
|
+
*
|
|
3054
|
+
* This allows clients to interact with ACP-compatible agents using the
|
|
3055
|
+
* familiar ACP interface while routing all messages through MAP.
|
|
3056
|
+
*
|
|
3057
|
+
* @param options - Stream configuration options
|
|
3058
|
+
* @returns ACPStreamConnection instance ready for initialize()
|
|
3059
|
+
*
|
|
3060
|
+
* @example
|
|
3061
|
+
* ```typescript
|
|
3062
|
+
* const acp = client.createACPStream({
|
|
3063
|
+
* targetAgent: 'coding-agent-1',
|
|
3064
|
+
* client: {
|
|
3065
|
+
* requestPermission: async (req) => ({
|
|
3066
|
+
* outcome: { outcome: 'selected', optionId: 'allow' }
|
|
3067
|
+
* }),
|
|
3068
|
+
* sessionUpdate: async (update) => {
|
|
3069
|
+
* console.log('Agent update:', update);
|
|
3070
|
+
* }
|
|
3071
|
+
* }
|
|
3072
|
+
* });
|
|
3073
|
+
*
|
|
3074
|
+
* await acp.initialize({
|
|
3075
|
+
* protocolVersion: 20241007,
|
|
3076
|
+
* clientInfo: { name: 'IDE', version: '1.0' }
|
|
3077
|
+
* });
|
|
3078
|
+
* const { sessionId } = await acp.newSession({ cwd: '/project', mcpServers: [] });
|
|
3079
|
+
* const result = await acp.prompt({
|
|
3080
|
+
* sessionId,
|
|
3081
|
+
* prompt: [{ type: 'text', text: 'Hello' }]
|
|
3082
|
+
* });
|
|
3083
|
+
*
|
|
3084
|
+
* await acp.close();
|
|
3085
|
+
* ```
|
|
2691
3086
|
*/
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
agentId: this.#agentId,
|
|
2698
|
-
metadata
|
|
3087
|
+
createACPStream(options) {
|
|
3088
|
+
const stream = new ACPStreamConnection(this, options);
|
|
3089
|
+
this.#acpStreams.set(stream.streamId, stream);
|
|
3090
|
+
stream.on("close", () => {
|
|
3091
|
+
this.#acpStreams.delete(stream.streamId);
|
|
2699
3092
|
});
|
|
2700
|
-
return
|
|
3093
|
+
return stream;
|
|
2701
3094
|
}
|
|
2702
3095
|
/**
|
|
2703
|
-
*
|
|
3096
|
+
* Get an active ACP stream by ID.
|
|
2704
3097
|
*/
|
|
2705
|
-
|
|
2706
|
-
return this.
|
|
3098
|
+
getACPStream(streamId) {
|
|
3099
|
+
return this.#acpStreams.get(streamId);
|
|
2707
3100
|
}
|
|
2708
3101
|
/**
|
|
2709
|
-
*
|
|
3102
|
+
* Get all active ACP streams.
|
|
2710
3103
|
*/
|
|
2711
|
-
|
|
2712
|
-
return this
|
|
3104
|
+
get acpStreams() {
|
|
3105
|
+
return this.#acpStreams;
|
|
2713
3106
|
}
|
|
3107
|
+
// ===========================================================================
|
|
3108
|
+
// Subscriptions
|
|
3109
|
+
// ===========================================================================
|
|
2714
3110
|
/**
|
|
2715
|
-
*
|
|
3111
|
+
* Subscribe to events
|
|
2716
3112
|
*/
|
|
2717
|
-
async
|
|
2718
|
-
|
|
2719
|
-
|
|
3113
|
+
async subscribe(filter) {
|
|
3114
|
+
const params = {};
|
|
3115
|
+
if (filter) params.filter = filter;
|
|
3116
|
+
const result = await this.#connection.sendRequest(CORE_METHODS.SUBSCRIBE, params);
|
|
3117
|
+
const serverSupportsAck = this.#serverCapabilities?.streaming?.supportsAck === true;
|
|
3118
|
+
const sendAck = serverSupportsAck ? (ackParams) => {
|
|
3119
|
+
this.#connection.sendNotification(NOTIFICATION_METHODS.SUBSCRIBE_ACK, ackParams);
|
|
3120
|
+
} : void 0;
|
|
3121
|
+
const subscription = createSubscription(
|
|
3122
|
+
result.subscriptionId,
|
|
3123
|
+
() => this.unsubscribe(result.subscriptionId),
|
|
3124
|
+
{ filter },
|
|
3125
|
+
sendAck
|
|
3126
|
+
);
|
|
3127
|
+
if (serverSupportsAck) {
|
|
3128
|
+
subscription._setServerSupportsAck(true);
|
|
2720
3129
|
}
|
|
2721
|
-
|
|
2722
|
-
if (
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
3130
|
+
this.#subscriptions.set(result.subscriptionId, subscription);
|
|
3131
|
+
if (this.#options.reconnection?.restoreSubscriptions !== false) {
|
|
3132
|
+
this.#subscriptionStates.set(result.subscriptionId, {
|
|
3133
|
+
filter,
|
|
3134
|
+
handlers: /* @__PURE__ */ new Set()
|
|
2726
3135
|
});
|
|
3136
|
+
const originalPushEvent = subscription._pushEvent.bind(subscription);
|
|
3137
|
+
subscription._pushEvent = (event) => {
|
|
3138
|
+
const state = this.#subscriptionStates.get(result.subscriptionId);
|
|
3139
|
+
if (state && event.eventId) {
|
|
3140
|
+
state.lastEventId = event.eventId;
|
|
3141
|
+
}
|
|
3142
|
+
originalPushEvent(event);
|
|
3143
|
+
};
|
|
2727
3144
|
}
|
|
3145
|
+
return subscription;
|
|
2728
3146
|
}
|
|
2729
|
-
// ===========================================================================
|
|
2730
|
-
// Child Agent Management
|
|
2731
|
-
// ===========================================================================
|
|
2732
3147
|
/**
|
|
2733
|
-
*
|
|
3148
|
+
* Unsubscribe from events
|
|
2734
3149
|
*/
|
|
2735
|
-
async
|
|
2736
|
-
|
|
2737
|
-
|
|
3150
|
+
async unsubscribe(subscriptionId) {
|
|
3151
|
+
const subscription = this.#subscriptions.get(subscriptionId);
|
|
3152
|
+
if (subscription) {
|
|
3153
|
+
subscription._close();
|
|
3154
|
+
this.#subscriptions.delete(subscriptionId);
|
|
2738
3155
|
}
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
parent: this.#agentId
|
|
2742
|
-
};
|
|
2743
|
-
return this.#connection.sendRequest(LIFECYCLE_METHODS.AGENTS_SPAWN, params);
|
|
3156
|
+
this.#subscriptionStates.delete(subscriptionId);
|
|
3157
|
+
await this.#connection.sendRequest(CORE_METHODS.UNSUBSCRIBE, { subscriptionId });
|
|
2744
3158
|
}
|
|
2745
3159
|
// ===========================================================================
|
|
2746
|
-
//
|
|
3160
|
+
// Event Replay
|
|
2747
3161
|
// ===========================================================================
|
|
2748
3162
|
/**
|
|
2749
|
-
*
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
*
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
*
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
});
|
|
2774
|
-
}
|
|
2775
|
-
/**
|
|
2776
|
-
* Send a message to a specific agent
|
|
2777
|
-
*/
|
|
2778
|
-
async sendToAgent(agentId, payload, meta) {
|
|
2779
|
-
return this.send({ agent: agentId }, payload, meta);
|
|
2780
|
-
}
|
|
2781
|
-
/**
|
|
2782
|
-
* Send a message to all agents in a scope
|
|
2783
|
-
*/
|
|
2784
|
-
async sendToScope(scopeId, payload, meta) {
|
|
2785
|
-
return this.send({ scope: scopeId }, payload, meta);
|
|
2786
|
-
}
|
|
2787
|
-
/**
|
|
2788
|
-
* Send a message to sibling agents
|
|
3163
|
+
* Replay historical events.
|
|
3164
|
+
*
|
|
3165
|
+
* Uses keyset pagination - pass the last eventId from the previous
|
|
3166
|
+
* response to get the next page.
|
|
3167
|
+
*
|
|
3168
|
+
* @example
|
|
3169
|
+
* ```typescript
|
|
3170
|
+
* // Replay all events from the last hour
|
|
3171
|
+
* const result = await client.replay({
|
|
3172
|
+
* fromTimestamp: Date.now() - 3600000,
|
|
3173
|
+
* filter: { eventTypes: ['agent.registered'] },
|
|
3174
|
+
* limit: 100
|
|
3175
|
+
* });
|
|
3176
|
+
*
|
|
3177
|
+
* // Paginate through results
|
|
3178
|
+
* let afterEventId: string | undefined;
|
|
3179
|
+
* do {
|
|
3180
|
+
* const page = await client.replay({ afterEventId, limit: 100 });
|
|
3181
|
+
* for (const item of page.events) {
|
|
3182
|
+
* console.log(item.eventId, item.event);
|
|
3183
|
+
* }
|
|
3184
|
+
* afterEventId = page.events.at(-1)?.eventId;
|
|
3185
|
+
* } while (page.hasMore);
|
|
3186
|
+
* ```
|
|
2789
3187
|
*/
|
|
2790
|
-
async
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
3188
|
+
async replay(params = {}) {
|
|
3189
|
+
const limit = Math.min(params.limit ?? 100, 1e3);
|
|
3190
|
+
return this.#connection.sendRequest(
|
|
3191
|
+
CORE_METHODS.REPLAY,
|
|
3192
|
+
{ ...params, limit }
|
|
3193
|
+
);
|
|
2795
3194
|
}
|
|
2796
3195
|
/**
|
|
2797
|
-
*
|
|
3196
|
+
* Replay all events matching filter, handling pagination automatically.
|
|
3197
|
+
*
|
|
3198
|
+
* Returns an async generator for streaming through all results.
|
|
3199
|
+
*
|
|
3200
|
+
* @example
|
|
3201
|
+
* ```typescript
|
|
3202
|
+
* for await (const item of client.replayAll({
|
|
3203
|
+
* filter: { eventTypes: ['agent.registered'] }
|
|
3204
|
+
* })) {
|
|
3205
|
+
* console.log(item.eventId, item.event);
|
|
3206
|
+
* }
|
|
3207
|
+
* ```
|
|
2798
3208
|
*/
|
|
2799
|
-
async
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
3209
|
+
async *replayAll(params = {}) {
|
|
3210
|
+
let afterEventId;
|
|
3211
|
+
let hasMore = true;
|
|
3212
|
+
while (hasMore) {
|
|
3213
|
+
const result = await this.replay({ ...params, afterEventId });
|
|
3214
|
+
for (const item of result.events) {
|
|
3215
|
+
yield item;
|
|
3216
|
+
}
|
|
3217
|
+
hasMore = result.hasMore;
|
|
3218
|
+
afterEventId = result.events.at(-1)?.eventId;
|
|
3219
|
+
if (result.events.length === 0) {
|
|
3220
|
+
break;
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
2805
3223
|
}
|
|
2806
3224
|
// ===========================================================================
|
|
2807
|
-
//
|
|
3225
|
+
// Steering (requires canSteer capability)
|
|
2808
3226
|
// ===========================================================================
|
|
2809
3227
|
/**
|
|
2810
|
-
*
|
|
3228
|
+
* Inject context into a running agent
|
|
2811
3229
|
*/
|
|
2812
|
-
async
|
|
2813
|
-
const
|
|
2814
|
-
|
|
3230
|
+
async inject(agentId, content, delivery) {
|
|
3231
|
+
const params = { agentId, content };
|
|
3232
|
+
if (delivery) params.delivery = delivery;
|
|
3233
|
+
return this.#connection.sendRequest(STEERING_METHODS.INJECT, params);
|
|
2815
3234
|
}
|
|
3235
|
+
// ===========================================================================
|
|
3236
|
+
// Lifecycle Control (requires canStop capability)
|
|
3237
|
+
// ===========================================================================
|
|
2816
3238
|
/**
|
|
2817
|
-
*
|
|
3239
|
+
* Request an agent to stop
|
|
2818
3240
|
*/
|
|
2819
|
-
async
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
const result = await this.#connection.sendRequest(SCOPE_METHODS.SCOPES_JOIN, {
|
|
2824
|
-
scopeId,
|
|
2825
|
-
agentId: this.#agentId
|
|
3241
|
+
async stopAgent(agentId, options) {
|
|
3242
|
+
return this.#connection.sendRequest(STATE_METHODS.AGENTS_STOP, {
|
|
3243
|
+
agentId,
|
|
3244
|
+
...options
|
|
2826
3245
|
});
|
|
2827
|
-
this.#scopeMemberships.add(scopeId);
|
|
2828
|
-
return result;
|
|
2829
3246
|
}
|
|
2830
3247
|
/**
|
|
2831
|
-
*
|
|
3248
|
+
* Suspend an agent
|
|
2832
3249
|
*/
|
|
2833
|
-
async
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
const result = await this.#connection.sendRequest(SCOPE_METHODS.SCOPES_LEAVE, {
|
|
2838
|
-
scopeId,
|
|
2839
|
-
agentId: this.#agentId
|
|
3250
|
+
async suspendAgent(agentId, reason) {
|
|
3251
|
+
return this.#connection.sendRequest(STATE_METHODS.AGENTS_SUSPEND, {
|
|
3252
|
+
agentId,
|
|
3253
|
+
reason
|
|
2840
3254
|
});
|
|
2841
|
-
this.#scopeMemberships.delete(scopeId);
|
|
2842
|
-
return result;
|
|
2843
|
-
}
|
|
2844
|
-
// ===========================================================================
|
|
2845
|
-
// Subscriptions
|
|
2846
|
-
// ===========================================================================
|
|
2847
|
-
/**
|
|
2848
|
-
* Subscribe to events
|
|
2849
|
-
*/
|
|
2850
|
-
async subscribe(filter) {
|
|
2851
|
-
const params = {};
|
|
2852
|
-
if (filter) params.filter = filter;
|
|
2853
|
-
const result = await this.#connection.sendRequest(CORE_METHODS.SUBSCRIBE, params);
|
|
2854
|
-
const subscription = createSubscription(
|
|
2855
|
-
result.subscriptionId,
|
|
2856
|
-
() => this.unsubscribe(result.subscriptionId),
|
|
2857
|
-
{ filter }
|
|
2858
|
-
);
|
|
2859
|
-
this.#subscriptions.set(result.subscriptionId, subscription);
|
|
2860
|
-
return subscription;
|
|
2861
3255
|
}
|
|
2862
3256
|
/**
|
|
2863
|
-
*
|
|
3257
|
+
* Resume a suspended agent
|
|
2864
3258
|
*/
|
|
2865
|
-
async
|
|
2866
|
-
|
|
2867
|
-
if (subscription) {
|
|
2868
|
-
subscription._close();
|
|
2869
|
-
this.#subscriptions.delete(subscriptionId);
|
|
2870
|
-
}
|
|
2871
|
-
await this.#connection.sendRequest(CORE_METHODS.UNSUBSCRIBE, { subscriptionId });
|
|
3259
|
+
async resumeAgent(agentId) {
|
|
3260
|
+
return this.#connection.sendRequest(STATE_METHODS.AGENTS_RESUME, { agentId });
|
|
2872
3261
|
}
|
|
2873
3262
|
// ===========================================================================
|
|
2874
3263
|
// Reconnection
|
|
@@ -2876,7 +3265,7 @@ var AgentConnection = class _AgentConnection {
|
|
|
2876
3265
|
/**
|
|
2877
3266
|
* Current connection state
|
|
2878
3267
|
*/
|
|
2879
|
-
get
|
|
3268
|
+
get state() {
|
|
2880
3269
|
return this.#connection.state;
|
|
2881
3270
|
}
|
|
2882
3271
|
/**
|
|
@@ -2890,6 +3279,26 @@ var AgentConnection = class _AgentConnection {
|
|
|
2890
3279
|
*
|
|
2891
3280
|
* @param handler - Function called when reconnection events occur
|
|
2892
3281
|
* @returns Unsubscribe function to remove the handler
|
|
3282
|
+
*
|
|
3283
|
+
* @example
|
|
3284
|
+
* ```typescript
|
|
3285
|
+
* const unsubscribe = client.onReconnection((event) => {
|
|
3286
|
+
* switch (event.type) {
|
|
3287
|
+
* case 'disconnected':
|
|
3288
|
+
* console.log('Connection lost');
|
|
3289
|
+
* break;
|
|
3290
|
+
* case 'reconnecting':
|
|
3291
|
+
* console.log(`Reconnecting, attempt ${event.attempt}`);
|
|
3292
|
+
* break;
|
|
3293
|
+
* case 'reconnected':
|
|
3294
|
+
* console.log('Reconnected successfully');
|
|
3295
|
+
* break;
|
|
3296
|
+
* case 'reconnectFailed':
|
|
3297
|
+
* console.log('Failed to reconnect:', event.error);
|
|
3298
|
+
* break;
|
|
3299
|
+
* }
|
|
3300
|
+
* });
|
|
3301
|
+
* ```
|
|
2893
3302
|
*/
|
|
2894
3303
|
onReconnection(handler) {
|
|
2895
3304
|
this.#reconnectionHandlers.add(handler);
|
|
@@ -2917,18 +3326,12 @@ var AgentConnection = class _AgentConnection {
|
|
|
2917
3326
|
const subscription = this.#subscriptions.get(eventParams.subscriptionId);
|
|
2918
3327
|
if (subscription) {
|
|
2919
3328
|
subscription._pushEvent(eventParams);
|
|
3329
|
+
} else {
|
|
3330
|
+
console.warn("MAP: Event for unknown subscription:", eventParams.subscriptionId);
|
|
2920
3331
|
}
|
|
2921
3332
|
break;
|
|
2922
3333
|
}
|
|
2923
3334
|
case NOTIFICATION_METHODS.MESSAGE: {
|
|
2924
|
-
const messageParams = params;
|
|
2925
|
-
for (const handler of this.#messageHandlers) {
|
|
2926
|
-
try {
|
|
2927
|
-
await handler(messageParams.message);
|
|
2928
|
-
} catch (error) {
|
|
2929
|
-
console.error("MAP: Message handler error:", error);
|
|
2930
|
-
}
|
|
2931
|
-
}
|
|
2932
3335
|
break;
|
|
2933
3336
|
}
|
|
2934
3337
|
default:
|
|
@@ -2976,19 +3379,13 @@ var AgentConnection = class _AgentConnection {
|
|
|
2976
3379
|
maxDelayMs: options.maxDelayMs ?? DEFAULT_RETRY_POLICY.maxDelayMs,
|
|
2977
3380
|
jitter: options.jitter ?? DEFAULT_RETRY_POLICY.jitter
|
|
2978
3381
|
};
|
|
2979
|
-
const scopesToRestore = Array.from(this.#scopeMemberships);
|
|
2980
3382
|
await withRetry(
|
|
2981
3383
|
async () => {
|
|
2982
3384
|
const newStream = await createStream();
|
|
2983
3385
|
await this.#connection.reconnect(newStream);
|
|
2984
|
-
const
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
});
|
|
2988
|
-
this.#agentId = result.agent.id;
|
|
2989
|
-
this.#sessionId = result.connection.sessionId;
|
|
2990
|
-
this.#serverCapabilities = result.connection.capabilities;
|
|
2991
|
-
this.#currentState = result.agent.state;
|
|
3386
|
+
const connectResult = await this.connect(this.#lastConnectOptions);
|
|
3387
|
+
this.#sessionId = connectResult.sessionId;
|
|
3388
|
+
this.#serverCapabilities = connectResult.capabilities;
|
|
2992
3389
|
},
|
|
2993
3390
|
retryPolicy,
|
|
2994
3391
|
{
|
|
@@ -3004,18 +3401,746 @@ var AgentConnection = class _AgentConnection {
|
|
|
3004
3401
|
);
|
|
3005
3402
|
this.#isReconnecting = false;
|
|
3006
3403
|
this.#emitReconnectionEvent({ type: "reconnected" });
|
|
3007
|
-
if (options.
|
|
3008
|
-
await this.#
|
|
3404
|
+
if (options.restoreSubscriptions !== false) {
|
|
3405
|
+
await this.#restoreSubscriptions();
|
|
3009
3406
|
}
|
|
3010
3407
|
}
|
|
3011
3408
|
/**
|
|
3012
|
-
* Restore
|
|
3409
|
+
* Restore subscriptions after reconnection
|
|
3013
3410
|
*/
|
|
3014
|
-
async #
|
|
3015
|
-
this.#
|
|
3016
|
-
|
|
3411
|
+
async #restoreSubscriptions() {
|
|
3412
|
+
const options = this.#options.reconnection;
|
|
3413
|
+
const subscriptionEntries = Array.from(this.#subscriptionStates.entries());
|
|
3414
|
+
this.#subscriptions.clear();
|
|
3415
|
+
this.#subscriptionStates.clear();
|
|
3416
|
+
for (const [oldId, state] of subscriptionEntries) {
|
|
3017
3417
|
try {
|
|
3018
|
-
await this.
|
|
3418
|
+
const newSubscription = await this.subscribe(state.filter);
|
|
3419
|
+
const newId = newSubscription.id;
|
|
3420
|
+
if (options.replayOnRestore !== false && state.lastEventId) {
|
|
3421
|
+
const maxEvents = options.maxReplayEventsPerSubscription ?? 1e3;
|
|
3422
|
+
try {
|
|
3423
|
+
let replayedCount = 0;
|
|
3424
|
+
let afterEventId = state.lastEventId;
|
|
3425
|
+
let hasMore = true;
|
|
3426
|
+
while (hasMore && replayedCount < maxEvents) {
|
|
3427
|
+
const result = await this.replay({
|
|
3428
|
+
afterEventId,
|
|
3429
|
+
filter: state.filter,
|
|
3430
|
+
limit: Math.min(100, maxEvents - replayedCount)
|
|
3431
|
+
});
|
|
3432
|
+
for (const replayedEvent of result.events) {
|
|
3433
|
+
if (replayedCount >= maxEvents) break;
|
|
3434
|
+
newSubscription._pushEvent({
|
|
3435
|
+
subscriptionId: newId,
|
|
3436
|
+
sequenceNumber: replayedCount + 1,
|
|
3437
|
+
eventId: replayedEvent.eventId,
|
|
3438
|
+
timestamp: replayedEvent.timestamp,
|
|
3439
|
+
event: replayedEvent.event
|
|
3440
|
+
});
|
|
3441
|
+
replayedCount++;
|
|
3442
|
+
}
|
|
3443
|
+
hasMore = result.hasMore;
|
|
3444
|
+
afterEventId = result.events.at(-1)?.eventId;
|
|
3445
|
+
if (result.events.length === 0) {
|
|
3446
|
+
break;
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
} catch (replayError) {
|
|
3450
|
+
console.warn("MAP: Failed to replay events for subscription:", oldId, replayError);
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
this.#emitReconnectionEvent({
|
|
3454
|
+
type: "subscriptionRestored",
|
|
3455
|
+
subscriptionId: oldId,
|
|
3456
|
+
newSubscriptionId: newId
|
|
3457
|
+
});
|
|
3458
|
+
} catch (error) {
|
|
3459
|
+
this.#emitReconnectionEvent({
|
|
3460
|
+
type: "subscriptionRestoreFailed",
|
|
3461
|
+
subscriptionId: oldId,
|
|
3462
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
3463
|
+
});
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
};
|
|
3468
|
+
|
|
3469
|
+
// src/connection/agent.ts
|
|
3470
|
+
var AgentConnection = class _AgentConnection {
|
|
3471
|
+
#connection;
|
|
3472
|
+
#subscriptions = /* @__PURE__ */ new Map();
|
|
3473
|
+
#options;
|
|
3474
|
+
#messageHandlers = /* @__PURE__ */ new Set();
|
|
3475
|
+
#reconnectionHandlers = /* @__PURE__ */ new Set();
|
|
3476
|
+
#scopeMemberships = /* @__PURE__ */ new Set();
|
|
3477
|
+
#agentId = null;
|
|
3478
|
+
#sessionId = null;
|
|
3479
|
+
#serverCapabilities = null;
|
|
3480
|
+
#currentState = "registered";
|
|
3481
|
+
#connected = false;
|
|
3482
|
+
#lastConnectOptions;
|
|
3483
|
+
#isReconnecting = false;
|
|
3484
|
+
constructor(stream, options = {}) {
|
|
3485
|
+
this.#connection = new BaseConnection(stream, options);
|
|
3486
|
+
this.#options = options;
|
|
3487
|
+
this.#connection.setNotificationHandler(this.#handleNotification.bind(this));
|
|
3488
|
+
if (options.reconnection?.enabled && options.createStream) {
|
|
3489
|
+
this.#connection.onStateChange((newState) => {
|
|
3490
|
+
if (newState === "closed" && this.#connected && !this.#isReconnecting) {
|
|
3491
|
+
void this.#handleDisconnect();
|
|
3492
|
+
}
|
|
3493
|
+
});
|
|
3494
|
+
}
|
|
3495
|
+
}
|
|
3496
|
+
// ===========================================================================
|
|
3497
|
+
// Static Factory Methods
|
|
3498
|
+
// ===========================================================================
|
|
3499
|
+
/**
|
|
3500
|
+
* Connect and register an agent via WebSocket URL.
|
|
3501
|
+
*
|
|
3502
|
+
* Handles:
|
|
3503
|
+
* - WebSocket creation and connection
|
|
3504
|
+
* - Stream wrapping
|
|
3505
|
+
* - Auto-configuration of createStream for reconnection
|
|
3506
|
+
* - Initial MAP protocol connect handshake
|
|
3507
|
+
* - Agent registration
|
|
3508
|
+
*
|
|
3509
|
+
* @param url - WebSocket URL (ws:// or wss://)
|
|
3510
|
+
* @param options - Connection and agent options
|
|
3511
|
+
* @returns Connected and registered AgentConnection instance
|
|
3512
|
+
*
|
|
3513
|
+
* @example
|
|
3514
|
+
* ```typescript
|
|
3515
|
+
* const agent = await AgentConnection.connect('ws://localhost:8080', {
|
|
3516
|
+
* name: 'Worker',
|
|
3517
|
+
* role: 'processor',
|
|
3518
|
+
* reconnection: true
|
|
3519
|
+
* });
|
|
3520
|
+
*
|
|
3521
|
+
* // Already registered, ready to work
|
|
3522
|
+
* agent.onMessage(handleMessage);
|
|
3523
|
+
* await agent.busy();
|
|
3524
|
+
* ```
|
|
3525
|
+
*/
|
|
3526
|
+
static async connect(url, options) {
|
|
3527
|
+
const parsedUrl = new URL(url);
|
|
3528
|
+
if (!["ws:", "wss:"].includes(parsedUrl.protocol)) {
|
|
3529
|
+
throw new Error(
|
|
3530
|
+
`Unsupported protocol: ${parsedUrl.protocol}. Use ws: or wss:`
|
|
3531
|
+
);
|
|
3532
|
+
}
|
|
3533
|
+
const timeout = options?.connectTimeout ?? 1e4;
|
|
3534
|
+
const ws = new WebSocket(url);
|
|
3535
|
+
await waitForOpen(ws, timeout);
|
|
3536
|
+
const stream = websocketStream(ws);
|
|
3537
|
+
const createStream = async () => {
|
|
3538
|
+
const newWs = new WebSocket(url);
|
|
3539
|
+
await waitForOpen(newWs, timeout);
|
|
3540
|
+
return websocketStream(newWs);
|
|
3541
|
+
};
|
|
3542
|
+
const reconnection = options?.reconnection === true ? { enabled: true } : typeof options?.reconnection === "object" ? options.reconnection : void 0;
|
|
3543
|
+
const agent = new _AgentConnection(stream, {
|
|
3544
|
+
name: options?.name,
|
|
3545
|
+
role: options?.role,
|
|
3546
|
+
capabilities: options?.capabilities,
|
|
3547
|
+
visibility: options?.visibility,
|
|
3548
|
+
parent: options?.parent,
|
|
3549
|
+
scopes: options?.scopes,
|
|
3550
|
+
createStream,
|
|
3551
|
+
reconnection
|
|
3552
|
+
});
|
|
3553
|
+
await agent.connect({ auth: options?.auth });
|
|
3554
|
+
return agent;
|
|
3555
|
+
}
|
|
3556
|
+
/**
|
|
3557
|
+
* Connect and register an agent via agentic-mesh transport.
|
|
3558
|
+
*
|
|
3559
|
+
* Handles:
|
|
3560
|
+
* - Dynamic import of agentic-mesh (optional peer dependency)
|
|
3561
|
+
* - Stream creation over encrypted mesh tunnel
|
|
3562
|
+
* - Auto-configuration of createStream for reconnection
|
|
3563
|
+
* - Initial MAP protocol connect handshake
|
|
3564
|
+
* - Agent registration
|
|
3565
|
+
*
|
|
3566
|
+
* Requires `agentic-mesh` to be installed as a peer dependency.
|
|
3567
|
+
*
|
|
3568
|
+
* @param options - Mesh connection and agent options
|
|
3569
|
+
* @returns Connected and registered AgentConnection instance
|
|
3570
|
+
*
|
|
3571
|
+
* @example
|
|
3572
|
+
* ```typescript
|
|
3573
|
+
* import { createNebulaTransport } from 'agentic-mesh';
|
|
3574
|
+
*
|
|
3575
|
+
* const transport = createNebulaTransport({
|
|
3576
|
+
* configPath: '/etc/nebula/config.yml',
|
|
3577
|
+
* });
|
|
3578
|
+
*
|
|
3579
|
+
* const agent = await AgentConnection.connectMesh({
|
|
3580
|
+
* transport,
|
|
3581
|
+
* peer: { peerId: 'server', address: '10.0.0.1', port: 4242 },
|
|
3582
|
+
* localPeerId: 'my-agent',
|
|
3583
|
+
* name: 'MeshWorker',
|
|
3584
|
+
* role: 'processor',
|
|
3585
|
+
* reconnection: true
|
|
3586
|
+
* });
|
|
3587
|
+
*
|
|
3588
|
+
* agent.onMessage(handleMessage);
|
|
3589
|
+
* await agent.busy();
|
|
3590
|
+
* ```
|
|
3591
|
+
*/
|
|
3592
|
+
static async connectMesh(options) {
|
|
3593
|
+
const { agenticMeshStream: agenticMeshStream2 } = await Promise.resolve().then(() => (init_agentic_mesh(), agentic_mesh_exports));
|
|
3594
|
+
const streamConfig = {
|
|
3595
|
+
transport: options.transport,
|
|
3596
|
+
peer: options.peer,
|
|
3597
|
+
localPeerId: options.localPeerId,
|
|
3598
|
+
timeout: options.timeout
|
|
3599
|
+
};
|
|
3600
|
+
const stream = await agenticMeshStream2(streamConfig);
|
|
3601
|
+
const createStream = async () => agenticMeshStream2(streamConfig);
|
|
3602
|
+
const reconnection = options.reconnection === true ? { enabled: true } : typeof options.reconnection === "object" ? options.reconnection : void 0;
|
|
3603
|
+
const agent = new _AgentConnection(stream, {
|
|
3604
|
+
name: options.name,
|
|
3605
|
+
role: options.role,
|
|
3606
|
+
capabilities: options.capabilities,
|
|
3607
|
+
visibility: options.visibility,
|
|
3608
|
+
parent: options.parent,
|
|
3609
|
+
scopes: options.scopes,
|
|
3610
|
+
createStream,
|
|
3611
|
+
reconnection
|
|
3612
|
+
});
|
|
3613
|
+
await agent.connect({ auth: options.auth });
|
|
3614
|
+
return agent;
|
|
3615
|
+
}
|
|
3616
|
+
// ===========================================================================
|
|
3617
|
+
// Connection Lifecycle
|
|
3618
|
+
// ===========================================================================
|
|
3619
|
+
/**
|
|
3620
|
+
* Connect and register with the MAP system
|
|
3621
|
+
*/
|
|
3622
|
+
async connect(options) {
|
|
3623
|
+
const connectParams = {
|
|
3624
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
3625
|
+
participantType: "agent",
|
|
3626
|
+
participantId: options?.agentId,
|
|
3627
|
+
name: this.#options.name,
|
|
3628
|
+
capabilities: this.#options.capabilities,
|
|
3629
|
+
resumeToken: options?.resumeToken,
|
|
3630
|
+
auth: options?.auth
|
|
3631
|
+
};
|
|
3632
|
+
const connectResult = await this.#connection.sendRequest(CORE_METHODS.CONNECT, connectParams);
|
|
3633
|
+
this.#sessionId = connectResult.sessionId;
|
|
3634
|
+
this.#serverCapabilities = connectResult.capabilities;
|
|
3635
|
+
this.#connected = true;
|
|
3636
|
+
this.#lastConnectOptions = options;
|
|
3637
|
+
const registerParams = {
|
|
3638
|
+
agentId: options?.agentId,
|
|
3639
|
+
name: this.#options.name,
|
|
3640
|
+
role: this.#options.role,
|
|
3641
|
+
parent: this.#options.parent,
|
|
3642
|
+
scopes: this.#options.scopes,
|
|
3643
|
+
visibility: this.#options.visibility,
|
|
3644
|
+
capabilities: this.#options.capabilities
|
|
3645
|
+
};
|
|
3646
|
+
const registerResult = await this.#connection.sendRequest(LIFECYCLE_METHODS.AGENTS_REGISTER, registerParams);
|
|
3647
|
+
this.#agentId = registerResult.agent.id;
|
|
3648
|
+
this.#currentState = registerResult.agent.state;
|
|
3649
|
+
this.#connection._transitionTo("connected");
|
|
3650
|
+
return { connection: connectResult, agent: registerResult.agent };
|
|
3651
|
+
}
|
|
3652
|
+
/**
|
|
3653
|
+
* Authenticate with the server after connection.
|
|
3654
|
+
*
|
|
3655
|
+
* Use this when the server returns `authRequired` in the connect response,
|
|
3656
|
+
* indicating that authentication is needed before registering or accessing
|
|
3657
|
+
* protected resources.
|
|
3658
|
+
*
|
|
3659
|
+
* @param auth - Authentication credentials
|
|
3660
|
+
* @returns Authentication result with principal if successful
|
|
3661
|
+
*
|
|
3662
|
+
* @example
|
|
3663
|
+
* ```typescript
|
|
3664
|
+
* const agent = new AgentConnection(stream, { name: 'MyAgent' });
|
|
3665
|
+
*
|
|
3666
|
+
* // First connect to get auth requirements
|
|
3667
|
+
* const connectResult = await agent.connectOnly();
|
|
3668
|
+
*
|
|
3669
|
+
* if (connectResult.authRequired) {
|
|
3670
|
+
* const authResult = await agent.authenticate({
|
|
3671
|
+
* method: 'api-key',
|
|
3672
|
+
* token: process.env.AGENT_API_KEY,
|
|
3673
|
+
* });
|
|
3674
|
+
*
|
|
3675
|
+
* if (authResult.success) {
|
|
3676
|
+
* // Now register the agent
|
|
3677
|
+
* await agent.register({ name: 'MyAgent', role: 'worker' });
|
|
3678
|
+
* }
|
|
3679
|
+
* }
|
|
3680
|
+
* ```
|
|
3681
|
+
*/
|
|
3682
|
+
async authenticate(auth) {
|
|
3683
|
+
const params = {
|
|
3684
|
+
method: auth.method,
|
|
3685
|
+
credential: auth.token
|
|
3686
|
+
};
|
|
3687
|
+
const result = await this.#connection.sendRequest(AUTH_METHODS.AUTHENTICATE, params);
|
|
3688
|
+
if (result.success && result.sessionId) {
|
|
3689
|
+
this.#sessionId = result.sessionId;
|
|
3690
|
+
}
|
|
3691
|
+
return result;
|
|
3692
|
+
}
|
|
3693
|
+
/**
|
|
3694
|
+
* Refresh authentication credentials.
|
|
3695
|
+
*
|
|
3696
|
+
* Use this to update credentials before they expire for long-lived connections.
|
|
3697
|
+
*
|
|
3698
|
+
* @param auth - New authentication credentials
|
|
3699
|
+
* @returns Updated principal information
|
|
3700
|
+
*/
|
|
3701
|
+
async refreshAuth(auth) {
|
|
3702
|
+
const params = {
|
|
3703
|
+
method: auth.method,
|
|
3704
|
+
credential: auth.token
|
|
3705
|
+
};
|
|
3706
|
+
return this.#connection.sendRequest(AUTH_METHODS.AUTH_REFRESH, params);
|
|
3707
|
+
}
|
|
3708
|
+
/**
|
|
3709
|
+
* Disconnect from the MAP system
|
|
3710
|
+
* @param reason - Optional reason for disconnecting
|
|
3711
|
+
* @returns Resume token that can be used to resume this session later
|
|
3712
|
+
*/
|
|
3713
|
+
async disconnect(reason) {
|
|
3714
|
+
if (!this.#connected) return void 0;
|
|
3715
|
+
let resumeToken;
|
|
3716
|
+
try {
|
|
3717
|
+
if (this.#agentId) {
|
|
3718
|
+
await this.#connection.sendRequest(LIFECYCLE_METHODS.AGENTS_UNREGISTER, {
|
|
3719
|
+
agentId: this.#agentId,
|
|
3720
|
+
reason
|
|
3721
|
+
});
|
|
3722
|
+
}
|
|
3723
|
+
const result = await this.#connection.sendRequest(
|
|
3724
|
+
CORE_METHODS.DISCONNECT,
|
|
3725
|
+
reason ? { reason } : void 0
|
|
3726
|
+
);
|
|
3727
|
+
resumeToken = result.resumeToken;
|
|
3728
|
+
} finally {
|
|
3729
|
+
for (const subscription of this.#subscriptions.values()) {
|
|
3730
|
+
subscription._close();
|
|
3731
|
+
}
|
|
3732
|
+
this.#subscriptions.clear();
|
|
3733
|
+
await this.#connection.close();
|
|
3734
|
+
this.#connected = false;
|
|
3735
|
+
}
|
|
3736
|
+
return resumeToken;
|
|
3737
|
+
}
|
|
3738
|
+
/**
|
|
3739
|
+
* Whether the agent is connected
|
|
3740
|
+
*/
|
|
3741
|
+
get isConnected() {
|
|
3742
|
+
return this.#connected && !this.#connection.isClosed;
|
|
3743
|
+
}
|
|
3744
|
+
/**
|
|
3745
|
+
* This agent's ID
|
|
3746
|
+
*/
|
|
3747
|
+
get agentId() {
|
|
3748
|
+
return this.#agentId;
|
|
3749
|
+
}
|
|
3750
|
+
/**
|
|
3751
|
+
* Current session ID
|
|
3752
|
+
*/
|
|
3753
|
+
get sessionId() {
|
|
3754
|
+
return this.#sessionId;
|
|
3755
|
+
}
|
|
3756
|
+
/**
|
|
3757
|
+
* Server capabilities
|
|
3758
|
+
*/
|
|
3759
|
+
get serverCapabilities() {
|
|
3760
|
+
return this.#serverCapabilities;
|
|
3761
|
+
}
|
|
3762
|
+
/**
|
|
3763
|
+
* Current agent state
|
|
3764
|
+
*/
|
|
3765
|
+
get state() {
|
|
3766
|
+
return this.#currentState;
|
|
3767
|
+
}
|
|
3768
|
+
/**
|
|
3769
|
+
* AbortSignal that triggers when the connection closes
|
|
3770
|
+
*/
|
|
3771
|
+
get signal() {
|
|
3772
|
+
return this.#connection.signal;
|
|
3773
|
+
}
|
|
3774
|
+
/**
|
|
3775
|
+
* Promise that resolves when the connection closes
|
|
3776
|
+
*/
|
|
3777
|
+
get closed() {
|
|
3778
|
+
return this.#connection.closed;
|
|
3779
|
+
}
|
|
3780
|
+
// ===========================================================================
|
|
3781
|
+
// Message Handling
|
|
3782
|
+
// ===========================================================================
|
|
3783
|
+
/**
|
|
3784
|
+
* Register a handler for incoming messages
|
|
3785
|
+
*/
|
|
3786
|
+
onMessage(handler) {
|
|
3787
|
+
this.#messageHandlers.add(handler);
|
|
3788
|
+
return this;
|
|
3789
|
+
}
|
|
3790
|
+
/**
|
|
3791
|
+
* Remove a message handler
|
|
3792
|
+
*/
|
|
3793
|
+
offMessage(handler) {
|
|
3794
|
+
this.#messageHandlers.delete(handler);
|
|
3795
|
+
return this;
|
|
3796
|
+
}
|
|
3797
|
+
// ===========================================================================
|
|
3798
|
+
// State Management
|
|
3799
|
+
// ===========================================================================
|
|
3800
|
+
/**
|
|
3801
|
+
* Update this agent's state
|
|
3802
|
+
*/
|
|
3803
|
+
async updateState(state) {
|
|
3804
|
+
if (!this.#agentId) {
|
|
3805
|
+
throw new Error("Agent not registered");
|
|
3806
|
+
}
|
|
3807
|
+
const result = await this.#connection.sendRequest(STATE_METHODS.AGENTS_UPDATE, {
|
|
3808
|
+
agentId: this.#agentId,
|
|
3809
|
+
state
|
|
3810
|
+
});
|
|
3811
|
+
this.#currentState = result.agent.state;
|
|
3812
|
+
return result.agent;
|
|
3813
|
+
}
|
|
3814
|
+
/**
|
|
3815
|
+
* Update this agent's metadata
|
|
3816
|
+
*/
|
|
3817
|
+
async updateMetadata(metadata) {
|
|
3818
|
+
if (!this.#agentId) {
|
|
3819
|
+
throw new Error("Agent not registered");
|
|
3820
|
+
}
|
|
3821
|
+
const result = await this.#connection.sendRequest(STATE_METHODS.AGENTS_UPDATE, {
|
|
3822
|
+
agentId: this.#agentId,
|
|
3823
|
+
metadata
|
|
3824
|
+
});
|
|
3825
|
+
return result.agent;
|
|
3826
|
+
}
|
|
3827
|
+
/**
|
|
3828
|
+
* Mark this agent as busy
|
|
3829
|
+
*/
|
|
3830
|
+
async busy() {
|
|
3831
|
+
return this.updateState("busy");
|
|
3832
|
+
}
|
|
3833
|
+
/**
|
|
3834
|
+
* Mark this agent as idle
|
|
3835
|
+
*/
|
|
3836
|
+
async idle() {
|
|
3837
|
+
return this.updateState("idle");
|
|
3838
|
+
}
|
|
3839
|
+
/**
|
|
3840
|
+
* Mark this agent as done/stopped
|
|
3841
|
+
*/
|
|
3842
|
+
async done(result) {
|
|
3843
|
+
if (!this.#agentId) {
|
|
3844
|
+
throw new Error("Agent not registered");
|
|
3845
|
+
}
|
|
3846
|
+
await this.updateState("stopped");
|
|
3847
|
+
if (result) {
|
|
3848
|
+
await this.updateMetadata({
|
|
3849
|
+
exitCode: result.exitCode,
|
|
3850
|
+
exitReason: result.exitReason
|
|
3851
|
+
});
|
|
3852
|
+
}
|
|
3853
|
+
}
|
|
3854
|
+
// ===========================================================================
|
|
3855
|
+
// Child Agent Management
|
|
3856
|
+
// ===========================================================================
|
|
3857
|
+
/**
|
|
3858
|
+
* Spawn a child agent
|
|
3859
|
+
*/
|
|
3860
|
+
async spawn(options) {
|
|
3861
|
+
if (!this.#agentId) {
|
|
3862
|
+
throw new Error("Agent not registered");
|
|
3863
|
+
}
|
|
3864
|
+
const params = {
|
|
3865
|
+
...options,
|
|
3866
|
+
parent: this.#agentId
|
|
3867
|
+
};
|
|
3868
|
+
return this.#connection.sendRequest(LIFECYCLE_METHODS.AGENTS_SPAWN, params);
|
|
3869
|
+
}
|
|
3870
|
+
// ===========================================================================
|
|
3871
|
+
// Messaging
|
|
3872
|
+
// ===========================================================================
|
|
3873
|
+
/**
|
|
3874
|
+
* Send a message to an address
|
|
3875
|
+
*/
|
|
3876
|
+
async send(to, payload, meta) {
|
|
3877
|
+
const params = { to };
|
|
3878
|
+
if (payload !== void 0) params.payload = payload;
|
|
3879
|
+
if (meta) params.meta = meta;
|
|
3880
|
+
return this.#connection.sendRequest(CORE_METHODS.SEND, params);
|
|
3881
|
+
}
|
|
3882
|
+
/**
|
|
3883
|
+
* Send a message to the parent agent
|
|
3884
|
+
*/
|
|
3885
|
+
async sendToParent(payload, meta) {
|
|
3886
|
+
return this.send({ parent: true }, payload, {
|
|
3887
|
+
...meta,
|
|
3888
|
+
relationship: "child-to-parent"
|
|
3889
|
+
});
|
|
3890
|
+
}
|
|
3891
|
+
/**
|
|
3892
|
+
* Send a message to child agents
|
|
3893
|
+
*/
|
|
3894
|
+
async sendToChildren(payload, meta) {
|
|
3895
|
+
return this.send({ children: true }, payload, {
|
|
3896
|
+
...meta,
|
|
3897
|
+
relationship: "parent-to-child"
|
|
3898
|
+
});
|
|
3899
|
+
}
|
|
3900
|
+
/**
|
|
3901
|
+
* Send a message to a specific agent
|
|
3902
|
+
*/
|
|
3903
|
+
async sendToAgent(agentId, payload, meta) {
|
|
3904
|
+
return this.send({ agent: agentId }, payload, meta);
|
|
3905
|
+
}
|
|
3906
|
+
/**
|
|
3907
|
+
* Send a message to all agents in a scope
|
|
3908
|
+
*/
|
|
3909
|
+
async sendToScope(scopeId, payload, meta) {
|
|
3910
|
+
return this.send({ scope: scopeId }, payload, meta);
|
|
3911
|
+
}
|
|
3912
|
+
/**
|
|
3913
|
+
* Send a message to sibling agents
|
|
3914
|
+
*/
|
|
3915
|
+
async sendToSiblings(payload, meta) {
|
|
3916
|
+
return this.send({ siblings: true }, payload, {
|
|
3917
|
+
...meta,
|
|
3918
|
+
relationship: "peer"
|
|
3919
|
+
});
|
|
3920
|
+
}
|
|
3921
|
+
/**
|
|
3922
|
+
* Reply to a message (uses correlationId from original)
|
|
3923
|
+
*/
|
|
3924
|
+
async reply(originalMessage, payload, meta) {
|
|
3925
|
+
return this.send({ agent: originalMessage.from }, payload, {
|
|
3926
|
+
...meta,
|
|
3927
|
+
correlationId: originalMessage.meta?.correlationId ?? originalMessage.id,
|
|
3928
|
+
isResult: true
|
|
3929
|
+
});
|
|
3930
|
+
}
|
|
3931
|
+
// ===========================================================================
|
|
3932
|
+
// Scope Management
|
|
3933
|
+
// ===========================================================================
|
|
3934
|
+
/**
|
|
3935
|
+
* Create a new scope
|
|
3936
|
+
*/
|
|
3937
|
+
async createScope(options) {
|
|
3938
|
+
const result = await this.#connection.sendRequest(SCOPE_METHODS.SCOPES_CREATE, options);
|
|
3939
|
+
return result.scope;
|
|
3940
|
+
}
|
|
3941
|
+
/**
|
|
3942
|
+
* Join a scope
|
|
3943
|
+
*/
|
|
3944
|
+
async joinScope(scopeId) {
|
|
3945
|
+
if (!this.#agentId) {
|
|
3946
|
+
throw new Error("Agent not registered");
|
|
3947
|
+
}
|
|
3948
|
+
const result = await this.#connection.sendRequest(SCOPE_METHODS.SCOPES_JOIN, {
|
|
3949
|
+
scopeId,
|
|
3950
|
+
agentId: this.#agentId
|
|
3951
|
+
});
|
|
3952
|
+
this.#scopeMemberships.add(scopeId);
|
|
3953
|
+
return result;
|
|
3954
|
+
}
|
|
3955
|
+
/**
|
|
3956
|
+
* Leave a scope
|
|
3957
|
+
*/
|
|
3958
|
+
async leaveScope(scopeId) {
|
|
3959
|
+
if (!this.#agentId) {
|
|
3960
|
+
throw new Error("Agent not registered");
|
|
3961
|
+
}
|
|
3962
|
+
const result = await this.#connection.sendRequest(SCOPE_METHODS.SCOPES_LEAVE, {
|
|
3963
|
+
scopeId,
|
|
3964
|
+
agentId: this.#agentId
|
|
3965
|
+
});
|
|
3966
|
+
this.#scopeMemberships.delete(scopeId);
|
|
3967
|
+
return result;
|
|
3968
|
+
}
|
|
3969
|
+
// ===========================================================================
|
|
3970
|
+
// Subscriptions
|
|
3971
|
+
// ===========================================================================
|
|
3972
|
+
/**
|
|
3973
|
+
* Subscribe to events
|
|
3974
|
+
*/
|
|
3975
|
+
async subscribe(filter) {
|
|
3976
|
+
const params = {};
|
|
3977
|
+
if (filter) params.filter = filter;
|
|
3978
|
+
const result = await this.#connection.sendRequest(CORE_METHODS.SUBSCRIBE, params);
|
|
3979
|
+
const subscription = createSubscription(
|
|
3980
|
+
result.subscriptionId,
|
|
3981
|
+
() => this.unsubscribe(result.subscriptionId),
|
|
3982
|
+
{ filter }
|
|
3983
|
+
);
|
|
3984
|
+
this.#subscriptions.set(result.subscriptionId, subscription);
|
|
3985
|
+
return subscription;
|
|
3986
|
+
}
|
|
3987
|
+
/**
|
|
3988
|
+
* Unsubscribe from events
|
|
3989
|
+
*/
|
|
3990
|
+
async unsubscribe(subscriptionId) {
|
|
3991
|
+
const subscription = this.#subscriptions.get(subscriptionId);
|
|
3992
|
+
if (subscription) {
|
|
3993
|
+
subscription._close();
|
|
3994
|
+
this.#subscriptions.delete(subscriptionId);
|
|
3995
|
+
}
|
|
3996
|
+
await this.#connection.sendRequest(CORE_METHODS.UNSUBSCRIBE, { subscriptionId });
|
|
3997
|
+
}
|
|
3998
|
+
// ===========================================================================
|
|
3999
|
+
// Reconnection
|
|
4000
|
+
// ===========================================================================
|
|
4001
|
+
/**
|
|
4002
|
+
* Current connection state
|
|
4003
|
+
*/
|
|
4004
|
+
get connectionState() {
|
|
4005
|
+
return this.#connection.state;
|
|
4006
|
+
}
|
|
4007
|
+
/**
|
|
4008
|
+
* Whether the connection is currently reconnecting
|
|
4009
|
+
*/
|
|
4010
|
+
get isReconnecting() {
|
|
4011
|
+
return this.#isReconnecting;
|
|
4012
|
+
}
|
|
4013
|
+
/**
|
|
4014
|
+
* Register a handler for reconnection events.
|
|
4015
|
+
*
|
|
4016
|
+
* @param handler - Function called when reconnection events occur
|
|
4017
|
+
* @returns Unsubscribe function to remove the handler
|
|
4018
|
+
*/
|
|
4019
|
+
onReconnection(handler) {
|
|
4020
|
+
this.#reconnectionHandlers.add(handler);
|
|
4021
|
+
return () => this.#reconnectionHandlers.delete(handler);
|
|
4022
|
+
}
|
|
4023
|
+
/**
|
|
4024
|
+
* Register a handler for connection state changes.
|
|
4025
|
+
*
|
|
4026
|
+
* @param handler - Function called when state changes
|
|
4027
|
+
* @returns Unsubscribe function to remove the handler
|
|
4028
|
+
*/
|
|
4029
|
+
onStateChange(handler) {
|
|
4030
|
+
return this.#connection.onStateChange(handler);
|
|
4031
|
+
}
|
|
4032
|
+
// ===========================================================================
|
|
4033
|
+
// Internal
|
|
4034
|
+
// ===========================================================================
|
|
4035
|
+
/**
|
|
4036
|
+
* Handle incoming notifications
|
|
4037
|
+
*/
|
|
4038
|
+
async #handleNotification(method, params) {
|
|
4039
|
+
switch (method) {
|
|
4040
|
+
case NOTIFICATION_METHODS.EVENT: {
|
|
4041
|
+
const eventParams = params;
|
|
4042
|
+
const subscription = this.#subscriptions.get(eventParams.subscriptionId);
|
|
4043
|
+
if (subscription) {
|
|
4044
|
+
subscription._pushEvent(eventParams);
|
|
4045
|
+
}
|
|
4046
|
+
break;
|
|
4047
|
+
}
|
|
4048
|
+
case NOTIFICATION_METHODS.MESSAGE: {
|
|
4049
|
+
const messageParams = params;
|
|
4050
|
+
for (const handler of this.#messageHandlers) {
|
|
4051
|
+
try {
|
|
4052
|
+
await handler(messageParams.message);
|
|
4053
|
+
} catch (error) {
|
|
4054
|
+
console.error("MAP: Message handler error:", error);
|
|
4055
|
+
}
|
|
4056
|
+
}
|
|
4057
|
+
break;
|
|
4058
|
+
}
|
|
4059
|
+
default:
|
|
4060
|
+
console.warn("MAP: Unknown notification:", method);
|
|
4061
|
+
}
|
|
4062
|
+
}
|
|
4063
|
+
/**
|
|
4064
|
+
* Emit a reconnection event to all registered handlers
|
|
4065
|
+
*/
|
|
4066
|
+
#emitReconnectionEvent(event) {
|
|
4067
|
+
for (const handler of this.#reconnectionHandlers) {
|
|
4068
|
+
try {
|
|
4069
|
+
handler(event);
|
|
4070
|
+
} catch (error) {
|
|
4071
|
+
console.error("MAP: Reconnection event handler error:", error);
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
}
|
|
4075
|
+
/**
|
|
4076
|
+
* Handle disconnect when auto-reconnect is enabled
|
|
4077
|
+
*/
|
|
4078
|
+
async #handleDisconnect() {
|
|
4079
|
+
this.#isReconnecting = true;
|
|
4080
|
+
this.#connected = false;
|
|
4081
|
+
this.#emitReconnectionEvent({ type: "disconnected" });
|
|
4082
|
+
try {
|
|
4083
|
+
await this.#attemptReconnect();
|
|
4084
|
+
} catch (error) {
|
|
4085
|
+
this.#isReconnecting = false;
|
|
4086
|
+
this.#emitReconnectionEvent({
|
|
4087
|
+
type: "reconnectFailed",
|
|
4088
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
4089
|
+
});
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
/**
|
|
4093
|
+
* Attempt to reconnect with retry logic
|
|
4094
|
+
*/
|
|
4095
|
+
async #attemptReconnect() {
|
|
4096
|
+
const options = this.#options.reconnection;
|
|
4097
|
+
const createStream = this.#options.createStream;
|
|
4098
|
+
const retryPolicy = {
|
|
4099
|
+
maxRetries: options.maxRetries ?? DEFAULT_RETRY_POLICY.maxRetries,
|
|
4100
|
+
baseDelayMs: options.baseDelayMs ?? DEFAULT_RETRY_POLICY.baseDelayMs,
|
|
4101
|
+
maxDelayMs: options.maxDelayMs ?? DEFAULT_RETRY_POLICY.maxDelayMs,
|
|
4102
|
+
jitter: options.jitter ?? DEFAULT_RETRY_POLICY.jitter
|
|
4103
|
+
};
|
|
4104
|
+
const scopesToRestore = Array.from(this.#scopeMemberships);
|
|
4105
|
+
await withRetry(
|
|
4106
|
+
async () => {
|
|
4107
|
+
const newStream = await createStream();
|
|
4108
|
+
await this.#connection.reconnect(newStream);
|
|
4109
|
+
const result = await this.connect({
|
|
4110
|
+
agentId: this.#agentId ?? this.#lastConnectOptions?.agentId,
|
|
4111
|
+
auth: this.#lastConnectOptions?.auth
|
|
4112
|
+
});
|
|
4113
|
+
this.#agentId = result.agent.id;
|
|
4114
|
+
this.#sessionId = result.connection.sessionId;
|
|
4115
|
+
this.#serverCapabilities = result.connection.capabilities;
|
|
4116
|
+
this.#currentState = result.agent.state;
|
|
4117
|
+
},
|
|
4118
|
+
retryPolicy,
|
|
4119
|
+
{
|
|
4120
|
+
onRetry: (state) => {
|
|
4121
|
+
this.#emitReconnectionEvent({
|
|
4122
|
+
type: "reconnecting",
|
|
4123
|
+
attempt: state.attempt,
|
|
4124
|
+
delay: state.nextDelayMs,
|
|
4125
|
+
error: state.lastError
|
|
4126
|
+
});
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
);
|
|
4130
|
+
this.#isReconnecting = false;
|
|
4131
|
+
this.#emitReconnectionEvent({ type: "reconnected" });
|
|
4132
|
+
if (options.restoreScopeMemberships !== false) {
|
|
4133
|
+
await this.#restoreScopeMemberships(scopesToRestore);
|
|
4134
|
+
}
|
|
4135
|
+
}
|
|
4136
|
+
/**
|
|
4137
|
+
* Restore scope memberships after reconnection
|
|
4138
|
+
*/
|
|
4139
|
+
async #restoreScopeMemberships(scopes) {
|
|
4140
|
+
this.#scopeMemberships.clear();
|
|
4141
|
+
for (const scopeId of scopes) {
|
|
4142
|
+
try {
|
|
4143
|
+
await this.joinScope(scopeId);
|
|
3019
4144
|
} catch (error) {
|
|
3020
4145
|
console.warn("MAP: Failed to restore scope membership:", scopeId, error);
|
|
3021
4146
|
}
|
|
@@ -3871,7 +4996,7 @@ var EventSchema = z.object({
|
|
|
3871
4996
|
var SubscriptionFilterSchema = z.object({
|
|
3872
4997
|
eventTypes: z.array(EventTypeSchema).optional(),
|
|
3873
4998
|
scopes: z.array(ScopeIdSchema).optional(),
|
|
3874
|
-
|
|
4999
|
+
fromAgents: z.array(AgentIdSchema).optional(),
|
|
3875
5000
|
includeChildren: z.boolean().optional(),
|
|
3876
5001
|
_meta: MetaSchema
|
|
3877
5002
|
}).strict();
|
|
@@ -4333,284 +5458,946 @@ function canPerformAction(context, action) {
|
|
|
4333
5458
|
}
|
|
4334
5459
|
}
|
|
4335
5460
|
}
|
|
4336
|
-
const requiredCaps = getRequiredCapabilities(action.method);
|
|
4337
|
-
for (const cap of requiredCaps) {
|
|
4338
|
-
if (!hasCapability(context.participant.capabilities, cap)) {
|
|
4339
|
-
return {
|
|
4340
|
-
allowed: false,
|
|
4341
|
-
reason: `Missing required capability: ${cap}`,
|
|
4342
|
-
layer: 2
|
|
4343
|
-
};
|
|
4344
|
-
}
|
|
5461
|
+
const requiredCaps = getRequiredCapabilities(action.method);
|
|
5462
|
+
for (const cap of requiredCaps) {
|
|
5463
|
+
if (!hasCapability(context.participant.capabilities, cap)) {
|
|
5464
|
+
return {
|
|
5465
|
+
allowed: false,
|
|
5466
|
+
reason: `Missing required capability: ${cap}`,
|
|
5467
|
+
layer: 2
|
|
5468
|
+
};
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
return { allowed: true };
|
|
5472
|
+
}
|
|
5473
|
+
function filterVisibleAgents(agents, context) {
|
|
5474
|
+
const ownedAgentIds = context.ownedAgentIds ?? [];
|
|
5475
|
+
return agents.filter((agent) => {
|
|
5476
|
+
if (!isAgentExposed(context.system.exposure, agent.id)) {
|
|
5477
|
+
return false;
|
|
5478
|
+
}
|
|
5479
|
+
if (!canSeeAgent(agent, context.participant, ownedAgentIds)) {
|
|
5480
|
+
return false;
|
|
5481
|
+
}
|
|
5482
|
+
return true;
|
|
5483
|
+
});
|
|
5484
|
+
}
|
|
5485
|
+
function filterVisibleScopes(scopes, context) {
|
|
5486
|
+
const scopeMembership = context.scopeMembership ?? /* @__PURE__ */ new Map();
|
|
5487
|
+
return scopes.filter((scope) => {
|
|
5488
|
+
if (!isScopeExposed(context.system.exposure, scope.id)) {
|
|
5489
|
+
return false;
|
|
5490
|
+
}
|
|
5491
|
+
const memberAgentIds = scopeMembership.get(scope.id) ?? [];
|
|
5492
|
+
if (!canSeeScope(scope, context.participant, memberAgentIds)) {
|
|
5493
|
+
return false;
|
|
5494
|
+
}
|
|
5495
|
+
return true;
|
|
5496
|
+
});
|
|
5497
|
+
}
|
|
5498
|
+
function filterVisibleEvents(events, context) {
|
|
5499
|
+
return events.filter((event) => {
|
|
5500
|
+
if (!isEventTypeExposed(context.system.exposure, event.type)) {
|
|
5501
|
+
return false;
|
|
5502
|
+
}
|
|
5503
|
+
return true;
|
|
5504
|
+
});
|
|
5505
|
+
}
|
|
5506
|
+
function matchesPatterns(value, patterns) {
|
|
5507
|
+
return patterns.some((pattern) => matchGlob(value, pattern));
|
|
5508
|
+
}
|
|
5509
|
+
function matchGlob(value, pattern) {
|
|
5510
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
5511
|
+
const regex = new RegExp(`^${escaped}$`);
|
|
5512
|
+
return regex.test(value);
|
|
5513
|
+
}
|
|
5514
|
+
function deepClone(obj) {
|
|
5515
|
+
return JSON.parse(JSON.stringify(obj));
|
|
5516
|
+
}
|
|
5517
|
+
function deepMergePermissions(base, override) {
|
|
5518
|
+
const result = { ...base };
|
|
5519
|
+
if (override.canSee) {
|
|
5520
|
+
result.canSee = { ...base.canSee, ...override.canSee };
|
|
5521
|
+
}
|
|
5522
|
+
if (override.canMessage) {
|
|
5523
|
+
result.canMessage = { ...base.canMessage, ...override.canMessage };
|
|
5524
|
+
}
|
|
5525
|
+
if (override.acceptsFrom) {
|
|
5526
|
+
result.acceptsFrom = { ...base.acceptsFrom, ...override.acceptsFrom };
|
|
5527
|
+
}
|
|
5528
|
+
return result;
|
|
5529
|
+
}
|
|
5530
|
+
function mapVisibilityToRule(visibility) {
|
|
5531
|
+
switch (visibility) {
|
|
5532
|
+
case "public":
|
|
5533
|
+
return "all";
|
|
5534
|
+
case "parent-only":
|
|
5535
|
+
return "hierarchy";
|
|
5536
|
+
case "scope":
|
|
5537
|
+
return "scoped";
|
|
5538
|
+
case "system":
|
|
5539
|
+
return "direct";
|
|
5540
|
+
default:
|
|
5541
|
+
return "all";
|
|
5542
|
+
}
|
|
5543
|
+
}
|
|
5544
|
+
var DEFAULT_AGENT_PERMISSION_CONFIG = {
|
|
5545
|
+
defaultPermissions: {
|
|
5546
|
+
canSee: {
|
|
5547
|
+
agents: "all",
|
|
5548
|
+
scopes: "all",
|
|
5549
|
+
structure: "full"
|
|
5550
|
+
},
|
|
5551
|
+
canMessage: {
|
|
5552
|
+
agents: "all",
|
|
5553
|
+
scopes: "all"
|
|
5554
|
+
},
|
|
5555
|
+
acceptsFrom: {
|
|
5556
|
+
agents: "all",
|
|
5557
|
+
clients: "all",
|
|
5558
|
+
systems: "all"
|
|
5559
|
+
}
|
|
5560
|
+
},
|
|
5561
|
+
rolePermissions: {}
|
|
5562
|
+
};
|
|
5563
|
+
function resolveAgentPermissions(agent, config = DEFAULT_AGENT_PERMISSION_CONFIG) {
|
|
5564
|
+
let permissions = deepClone(config.defaultPermissions);
|
|
5565
|
+
if (agent.role && config.rolePermissions[agent.role]) {
|
|
5566
|
+
permissions = deepMergePermissions(permissions, config.rolePermissions[agent.role]);
|
|
5567
|
+
}
|
|
5568
|
+
if (agent.permissionOverrides) {
|
|
5569
|
+
permissions = deepMergePermissions(permissions, agent.permissionOverrides);
|
|
5570
|
+
}
|
|
5571
|
+
if (agent.visibility && !agent.permissionOverrides?.canSee?.agents) {
|
|
5572
|
+
permissions.canSee = permissions.canSee ?? {};
|
|
5573
|
+
permissions.canSee.agents = mapVisibilityToRule(agent.visibility);
|
|
5574
|
+
}
|
|
5575
|
+
return permissions;
|
|
5576
|
+
}
|
|
5577
|
+
function checkAgentAcceptance(rule, context) {
|
|
5578
|
+
if (!rule || rule === "all") return true;
|
|
5579
|
+
if (rule === "hierarchy") {
|
|
5580
|
+
return context.isParent === true || context.isChild === true || context.isAncestor === true || context.isDescendant === true;
|
|
5581
|
+
}
|
|
5582
|
+
if (rule === "scoped") {
|
|
5583
|
+
return (context.sharedScopes?.length ?? 0) > 0;
|
|
5584
|
+
}
|
|
5585
|
+
if (typeof rule === "object" && "include" in rule) {
|
|
5586
|
+
return context.senderAgentId !== void 0 && rule.include.includes(context.senderAgentId);
|
|
5587
|
+
}
|
|
5588
|
+
return false;
|
|
5589
|
+
}
|
|
5590
|
+
function checkClientAcceptance(rule, senderId) {
|
|
5591
|
+
if (!rule || rule === "all") return true;
|
|
5592
|
+
if (rule === "none") return false;
|
|
5593
|
+
if (typeof rule === "object" && "include" in rule) {
|
|
5594
|
+
return rule.include.includes(senderId);
|
|
5595
|
+
}
|
|
5596
|
+
return false;
|
|
5597
|
+
}
|
|
5598
|
+
function checkSystemAcceptance(rule, senderSystemId) {
|
|
5599
|
+
if (!rule || rule === "all") return true;
|
|
5600
|
+
if (rule === "none") return false;
|
|
5601
|
+
if (typeof rule === "object" && "include" in rule) {
|
|
5602
|
+
return senderSystemId !== void 0 && rule.include.includes(senderSystemId);
|
|
5603
|
+
}
|
|
5604
|
+
return false;
|
|
5605
|
+
}
|
|
5606
|
+
function canAgentAcceptMessage(targetAgent, context, config = DEFAULT_AGENT_PERMISSION_CONFIG) {
|
|
5607
|
+
const permissions = resolveAgentPermissions(targetAgent, config);
|
|
5608
|
+
const acceptsFrom = permissions.acceptsFrom;
|
|
5609
|
+
if (!acceptsFrom) return true;
|
|
5610
|
+
switch (context.senderType) {
|
|
5611
|
+
case "agent":
|
|
5612
|
+
return checkAgentAcceptance(acceptsFrom.agents, context);
|
|
5613
|
+
case "client":
|
|
5614
|
+
return checkClientAcceptance(acceptsFrom.clients, context.senderId);
|
|
5615
|
+
case "system":
|
|
5616
|
+
case "gateway":
|
|
5617
|
+
return checkSystemAcceptance(acceptsFrom.systems, context.senderSystemId);
|
|
5618
|
+
default:
|
|
5619
|
+
return false;
|
|
5620
|
+
}
|
|
5621
|
+
}
|
|
5622
|
+
function canAgentSeeAgent(viewerAgent, targetAgentId, context, config = DEFAULT_AGENT_PERMISSION_CONFIG) {
|
|
5623
|
+
const permissions = resolveAgentPermissions(viewerAgent, config);
|
|
5624
|
+
const canSee = permissions.canSee?.agents;
|
|
5625
|
+
if (!canSee || canSee === "all") return true;
|
|
5626
|
+
if (canSee === "hierarchy") {
|
|
5627
|
+
return context.isParent === true || context.isChild === true || context.isAncestor === true || context.isDescendant === true;
|
|
5628
|
+
}
|
|
5629
|
+
if (canSee === "scoped") {
|
|
5630
|
+
return (context.sharedScopes?.length ?? 0) > 0;
|
|
5631
|
+
}
|
|
5632
|
+
if (canSee === "direct") {
|
|
5633
|
+
return false;
|
|
5634
|
+
}
|
|
5635
|
+
if (typeof canSee === "object" && "include" in canSee) {
|
|
5636
|
+
return canSee.include.includes(targetAgentId);
|
|
5637
|
+
}
|
|
5638
|
+
return false;
|
|
5639
|
+
}
|
|
5640
|
+
function canAgentMessageAgent(senderAgent, targetAgentId, context, config = DEFAULT_AGENT_PERMISSION_CONFIG) {
|
|
5641
|
+
const permissions = resolveAgentPermissions(senderAgent, config);
|
|
5642
|
+
const canMessage = permissions.canMessage?.agents;
|
|
5643
|
+
if (!canMessage || canMessage === "all") return true;
|
|
5644
|
+
if (canMessage === "hierarchy") {
|
|
5645
|
+
return context.isParent === true || context.isChild === true || context.isAncestor === true || context.isDescendant === true;
|
|
5646
|
+
}
|
|
5647
|
+
if (canMessage === "scoped") {
|
|
5648
|
+
return (context.sharedScopes?.length ?? 0) > 0;
|
|
5649
|
+
}
|
|
5650
|
+
if (typeof canMessage === "object" && "include" in canMessage) {
|
|
5651
|
+
return canMessage.include.includes(targetAgentId);
|
|
5652
|
+
}
|
|
5653
|
+
return false;
|
|
5654
|
+
}
|
|
5655
|
+
|
|
5656
|
+
// src/server/messages/address.ts
|
|
5657
|
+
var SEPARATOR = ":";
|
|
5658
|
+
var VALID_ADDRESS_TYPES = ["agent", "scope"];
|
|
5659
|
+
var InvalidAddressError = class extends Error {
|
|
5660
|
+
constructor(address, reason) {
|
|
5661
|
+
super(`Invalid address "${address}": ${reason}`);
|
|
5662
|
+
this.name = "InvalidAddressError";
|
|
5663
|
+
}
|
|
5664
|
+
};
|
|
5665
|
+
function formatAddress(type, id) {
|
|
5666
|
+
if (!id) {
|
|
5667
|
+
throw new InvalidAddressError("", "ID cannot be empty");
|
|
5668
|
+
}
|
|
5669
|
+
if (id.includes(SEPARATOR)) {
|
|
5670
|
+
throw new InvalidAddressError(id, "ID cannot contain colon separator");
|
|
5671
|
+
}
|
|
5672
|
+
return `${type}${SEPARATOR}${id}`;
|
|
5673
|
+
}
|
|
5674
|
+
function parseAddress(address) {
|
|
5675
|
+
const separatorIndex = address.indexOf(SEPARATOR);
|
|
5676
|
+
if (separatorIndex === -1) {
|
|
5677
|
+
throw new InvalidAddressError(address, "missing type prefix");
|
|
5678
|
+
}
|
|
5679
|
+
const type = address.slice(0, separatorIndex);
|
|
5680
|
+
const id = address.slice(separatorIndex + 1);
|
|
5681
|
+
if (!VALID_ADDRESS_TYPES.includes(type)) {
|
|
5682
|
+
throw new InvalidAddressError(address, `invalid type "${type}", must be agent or scope`);
|
|
5683
|
+
}
|
|
5684
|
+
if (!id) {
|
|
5685
|
+
throw new InvalidAddressError(address, "ID cannot be empty");
|
|
4345
5686
|
}
|
|
4346
|
-
return {
|
|
5687
|
+
return {
|
|
5688
|
+
type,
|
|
5689
|
+
id
|
|
5690
|
+
};
|
|
4347
5691
|
}
|
|
4348
|
-
function
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
if (!isAgentExposed(context.system.exposure, agent.id)) {
|
|
4352
|
-
return false;
|
|
4353
|
-
}
|
|
4354
|
-
if (!canSeeAgent(agent, context.participant, ownedAgentIds)) {
|
|
4355
|
-
return false;
|
|
4356
|
-
}
|
|
5692
|
+
function isAddress(address) {
|
|
5693
|
+
try {
|
|
5694
|
+
parseAddress(address);
|
|
4357
5695
|
return true;
|
|
4358
|
-
}
|
|
5696
|
+
} catch {
|
|
5697
|
+
return false;
|
|
5698
|
+
}
|
|
4359
5699
|
}
|
|
4360
|
-
function
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
if (!canSeeScope(scope, context.participant, memberAgentIds)) {
|
|
4368
|
-
return false;
|
|
4369
|
-
}
|
|
4370
|
-
return true;
|
|
4371
|
-
});
|
|
5700
|
+
function isAgentAddress(address) {
|
|
5701
|
+
try {
|
|
5702
|
+
const parsed = parseAddress(address);
|
|
5703
|
+
return parsed.type === "agent";
|
|
5704
|
+
} catch {
|
|
5705
|
+
return false;
|
|
5706
|
+
}
|
|
4372
5707
|
}
|
|
4373
|
-
function
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
return
|
|
4379
|
-
}
|
|
5708
|
+
function isScopeAddress(address) {
|
|
5709
|
+
try {
|
|
5710
|
+
const parsed = parseAddress(address);
|
|
5711
|
+
return parsed.type === "scope";
|
|
5712
|
+
} catch {
|
|
5713
|
+
return false;
|
|
5714
|
+
}
|
|
4380
5715
|
}
|
|
4381
|
-
function
|
|
4382
|
-
|
|
5716
|
+
function extractId(address) {
|
|
5717
|
+
try {
|
|
5718
|
+
const parsed = parseAddress(address);
|
|
5719
|
+
return parsed.id;
|
|
5720
|
+
} catch {
|
|
5721
|
+
return address;
|
|
5722
|
+
}
|
|
4383
5723
|
}
|
|
4384
|
-
function
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
5724
|
+
function extractType(address) {
|
|
5725
|
+
try {
|
|
5726
|
+
const parsed = parseAddress(address);
|
|
5727
|
+
return parsed.type;
|
|
5728
|
+
} catch {
|
|
5729
|
+
return void 0;
|
|
5730
|
+
}
|
|
4388
5731
|
}
|
|
4389
|
-
function
|
|
4390
|
-
return
|
|
5732
|
+
function toAgent(agentId) {
|
|
5733
|
+
return formatAddress("agent", agentId);
|
|
4391
5734
|
}
|
|
4392
|
-
function
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
5735
|
+
function toScope(scopeId) {
|
|
5736
|
+
return formatAddress("scope", scopeId);
|
|
5737
|
+
}
|
|
5738
|
+
var MAPMeshPeer = class _MAPMeshPeer {
|
|
5739
|
+
#meshPeer;
|
|
5740
|
+
#config;
|
|
5741
|
+
#gitService;
|
|
5742
|
+
constructor(meshPeer, config) {
|
|
5743
|
+
this.#meshPeer = meshPeer;
|
|
5744
|
+
this.#config = config;
|
|
5745
|
+
this.#gitService = meshPeer.git ? new GitSyncServiceImpl(meshPeer.git, config.git?.repoPath ?? process.cwd()) : null;
|
|
4396
5746
|
}
|
|
4397
|
-
|
|
4398
|
-
|
|
5747
|
+
// ===========================================================================
|
|
5748
|
+
// Static Factory
|
|
5749
|
+
// ===========================================================================
|
|
5750
|
+
/**
|
|
5751
|
+
* Create a new MAPMeshPeer.
|
|
5752
|
+
*
|
|
5753
|
+
* Requires `agentic-mesh` to be installed as a peer dependency.
|
|
5754
|
+
*
|
|
5755
|
+
* @param config - Peer configuration
|
|
5756
|
+
* @returns Promise resolving to the created peer (not yet started)
|
|
5757
|
+
*/
|
|
5758
|
+
static async create(config) {
|
|
5759
|
+
const meshPeer = createMeshPeer({
|
|
5760
|
+
peerId: config.peerId,
|
|
5761
|
+
peerName: config.peerName,
|
|
5762
|
+
transport: config.transport,
|
|
5763
|
+
git: config.git,
|
|
5764
|
+
peers: config.peers,
|
|
5765
|
+
map: config.map
|
|
5766
|
+
});
|
|
5767
|
+
return new _MAPMeshPeer(meshPeer, config);
|
|
4399
5768
|
}
|
|
4400
|
-
|
|
4401
|
-
|
|
5769
|
+
// ===========================================================================
|
|
5770
|
+
// Properties
|
|
5771
|
+
// ===========================================================================
|
|
5772
|
+
/** Unique peer identifier */
|
|
5773
|
+
get peerId() {
|
|
5774
|
+
return this.#meshPeer.peerId;
|
|
4402
5775
|
}
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
5776
|
+
/** Display name for this peer */
|
|
5777
|
+
get peerName() {
|
|
5778
|
+
return this.#meshPeer.peerName;
|
|
5779
|
+
}
|
|
5780
|
+
/** Whether the peer is currently running */
|
|
5781
|
+
get isRunning() {
|
|
5782
|
+
return this.#meshPeer.isRunning;
|
|
5783
|
+
}
|
|
5784
|
+
/** List of connected peer IDs */
|
|
5785
|
+
get connectedPeers() {
|
|
5786
|
+
return this.#meshPeer.connectedPeers;
|
|
5787
|
+
}
|
|
5788
|
+
/** Git sync service (null if git not enabled) */
|
|
5789
|
+
get git() {
|
|
5790
|
+
return this.#gitService;
|
|
5791
|
+
}
|
|
5792
|
+
// ===========================================================================
|
|
5793
|
+
// Lifecycle
|
|
5794
|
+
// ===========================================================================
|
|
5795
|
+
/**
|
|
5796
|
+
* Start the mesh peer.
|
|
5797
|
+
*
|
|
5798
|
+
* This starts the transport, MAP server, and git service (if enabled),
|
|
5799
|
+
* then connects to any initial peers specified in the config.
|
|
5800
|
+
*/
|
|
5801
|
+
async start() {
|
|
5802
|
+
await this.#meshPeer.start(this.#config.transport);
|
|
5803
|
+
}
|
|
5804
|
+
/**
|
|
5805
|
+
* Stop the mesh peer.
|
|
5806
|
+
*
|
|
5807
|
+
* This disconnects from all peers, unregisters all agents,
|
|
5808
|
+
* stops the git service, MAP server, and transport.
|
|
5809
|
+
*/
|
|
5810
|
+
async stop() {
|
|
5811
|
+
await this.#meshPeer.stop();
|
|
5812
|
+
}
|
|
5813
|
+
// ===========================================================================
|
|
5814
|
+
// Peer Connections
|
|
5815
|
+
// ===========================================================================
|
|
5816
|
+
/**
|
|
5817
|
+
* Connect to a remote peer.
|
|
5818
|
+
*
|
|
5819
|
+
* After connecting, agents on both peers will be discoverable
|
|
5820
|
+
* and messages can be routed between them.
|
|
5821
|
+
*
|
|
5822
|
+
* @param endpoint - Peer endpoint to connect to
|
|
5823
|
+
*/
|
|
5824
|
+
async connectToPeer(endpoint) {
|
|
5825
|
+
await this.#meshPeer.connectToPeer(endpoint);
|
|
5826
|
+
}
|
|
5827
|
+
/**
|
|
5828
|
+
* Disconnect from a peer.
|
|
5829
|
+
*
|
|
5830
|
+
* @param peerId - ID of peer to disconnect from
|
|
5831
|
+
* @param reason - Optional reason for disconnecting
|
|
5832
|
+
*/
|
|
5833
|
+
async disconnectFromPeer(peerId, reason) {
|
|
5834
|
+
await this.#meshPeer.disconnectFromPeer(peerId, reason);
|
|
5835
|
+
}
|
|
5836
|
+
/**
|
|
5837
|
+
* Check if connected to a specific peer.
|
|
5838
|
+
*
|
|
5839
|
+
* @param peerId - Peer ID to check
|
|
5840
|
+
* @returns true if connected
|
|
5841
|
+
*/
|
|
5842
|
+
isConnectedTo(peerId) {
|
|
5843
|
+
return this.connectedPeers.includes(peerId);
|
|
5844
|
+
}
|
|
5845
|
+
// ===========================================================================
|
|
5846
|
+
// Agent Management
|
|
5847
|
+
// ===========================================================================
|
|
5848
|
+
/**
|
|
5849
|
+
* Create and register a local agent on this peer's MapServer.
|
|
5850
|
+
*
|
|
5851
|
+
* @param config - Agent configuration
|
|
5852
|
+
* @returns The created agent
|
|
5853
|
+
*/
|
|
5854
|
+
async createAgent(config) {
|
|
5855
|
+
const agentConn = await this.#meshPeer.createAgent(config);
|
|
5856
|
+
return new LocalAgentImpl(agentConn);
|
|
5857
|
+
}
|
|
5858
|
+
/**
|
|
5859
|
+
* Get a local agent by ID.
|
|
5860
|
+
*
|
|
5861
|
+
* @param agentId - Agent ID to look up
|
|
5862
|
+
* @returns The agent, or undefined if not found
|
|
5863
|
+
*/
|
|
5864
|
+
getAgent(agentId) {
|
|
5865
|
+
const conn = this.#meshPeer.getAgentConnection(agentId);
|
|
5866
|
+
return conn ? new LocalAgentImpl(conn) : void 0;
|
|
5867
|
+
}
|
|
5868
|
+
/**
|
|
5869
|
+
* Get all local agents on this peer.
|
|
5870
|
+
*/
|
|
5871
|
+
getLocalAgents() {
|
|
5872
|
+
return this.#meshPeer.getLocalAgents();
|
|
5873
|
+
}
|
|
5874
|
+
/**
|
|
5875
|
+
* Get all known agents (local and discovered from connected peers).
|
|
5876
|
+
*/
|
|
5877
|
+
getAllAgents() {
|
|
5878
|
+
return this.#meshPeer.getAllAgents();
|
|
5879
|
+
}
|
|
5880
|
+
// ===========================================================================
|
|
5881
|
+
// Scope Management
|
|
5882
|
+
// ===========================================================================
|
|
5883
|
+
/**
|
|
5884
|
+
* Create a scope on this peer's MapServer.
|
|
5885
|
+
*
|
|
5886
|
+
* @param config - Scope configuration
|
|
5887
|
+
* @returns The created scope
|
|
5888
|
+
*/
|
|
5889
|
+
createScope(config) {
|
|
5890
|
+
return this.#meshPeer.createScope(config);
|
|
5891
|
+
}
|
|
5892
|
+
/**
|
|
5893
|
+
* Get a scope by ID.
|
|
5894
|
+
*
|
|
5895
|
+
* @param scopeId - Scope ID to look up
|
|
5896
|
+
* @returns The scope, or undefined if not found
|
|
5897
|
+
*/
|
|
5898
|
+
getScope(scopeId) {
|
|
5899
|
+
return this.#meshPeer.getScope(scopeId);
|
|
5900
|
+
}
|
|
5901
|
+
/**
|
|
5902
|
+
* List all scopes on this peer.
|
|
5903
|
+
*/
|
|
5904
|
+
listScopes() {
|
|
5905
|
+
return this.#meshPeer.listScopes();
|
|
5906
|
+
}
|
|
5907
|
+
// ===========================================================================
|
|
5908
|
+
// Messaging
|
|
5909
|
+
// ===========================================================================
|
|
5910
|
+
/**
|
|
5911
|
+
* Send a message from an agent to an address.
|
|
5912
|
+
*
|
|
5913
|
+
* Messages are routed to local agents or forwarded to connected peers.
|
|
5914
|
+
*
|
|
5915
|
+
* @param from - Sending agent ID
|
|
5916
|
+
* @param to - Destination address
|
|
5917
|
+
* @param payload - Message payload
|
|
5918
|
+
* @param meta - Optional message metadata
|
|
5919
|
+
* @returns Send result with delivery status
|
|
5920
|
+
*/
|
|
5921
|
+
async send(from, to, payload, meta) {
|
|
5922
|
+
return this.#meshPeer.send(from, to, payload, meta);
|
|
5923
|
+
}
|
|
5924
|
+
// ===========================================================================
|
|
5925
|
+
// Events
|
|
5926
|
+
// ===========================================================================
|
|
5927
|
+
/**
|
|
5928
|
+
* Subscribe to events from this peer's MapServer.
|
|
5929
|
+
*
|
|
5930
|
+
* @param filter - Optional filter for event types
|
|
5931
|
+
* @returns Event subscription
|
|
5932
|
+
*/
|
|
5933
|
+
subscribe(filter) {
|
|
5934
|
+
return this.#meshPeer.subscribe(this.peerId, filter);
|
|
5935
|
+
}
|
|
5936
|
+
/**
|
|
5937
|
+
* Register a handler for peer connection events.
|
|
5938
|
+
*
|
|
5939
|
+
* @param handler - Function called when a peer connects
|
|
5940
|
+
* @returns Unsubscribe function
|
|
5941
|
+
*/
|
|
5942
|
+
onPeerConnected(handler) {
|
|
5943
|
+
this.#meshPeer.on("peer:connected", handler);
|
|
5944
|
+
return () => this.#meshPeer.off("peer:connected", handler);
|
|
5945
|
+
}
|
|
5946
|
+
/**
|
|
5947
|
+
* Register a handler for peer disconnection events.
|
|
5948
|
+
*
|
|
5949
|
+
* @param handler - Function called when a peer disconnects
|
|
5950
|
+
* @returns Unsubscribe function
|
|
5951
|
+
*/
|
|
5952
|
+
onPeerDisconnected(handler) {
|
|
5953
|
+
this.#meshPeer.on("peer:disconnected", handler);
|
|
5954
|
+
return () => this.#meshPeer.off("peer:disconnected", handler);
|
|
5955
|
+
}
|
|
5956
|
+
/**
|
|
5957
|
+
* Register a handler for agent registration events.
|
|
5958
|
+
*
|
|
5959
|
+
* @param handler - Function called when an agent registers
|
|
5960
|
+
* @returns Unsubscribe function
|
|
5961
|
+
*/
|
|
5962
|
+
onAgentRegistered(handler) {
|
|
5963
|
+
this.#meshPeer.on("agent:registered", handler);
|
|
5964
|
+
return () => this.#meshPeer.off("agent:registered", handler);
|
|
5965
|
+
}
|
|
5966
|
+
/**
|
|
5967
|
+
* Register a handler for agent unregistration events.
|
|
5968
|
+
*
|
|
5969
|
+
* @param handler - Function called when an agent unregisters
|
|
5970
|
+
* @returns Unsubscribe function
|
|
5971
|
+
*/
|
|
5972
|
+
onAgentUnregistered(handler) {
|
|
5973
|
+
this.#meshPeer.on("agent:unregistered", handler);
|
|
5974
|
+
return () => this.#meshPeer.off("agent:unregistered", handler);
|
|
5975
|
+
}
|
|
5976
|
+
/**
|
|
5977
|
+
* Register a handler for error events.
|
|
5978
|
+
*
|
|
5979
|
+
* @param handler - Function called when an error occurs
|
|
5980
|
+
* @returns Unsubscribe function
|
|
5981
|
+
*/
|
|
5982
|
+
onError(handler) {
|
|
5983
|
+
this.#meshPeer.on("error", handler);
|
|
5984
|
+
return () => this.#meshPeer.off("error", handler);
|
|
4417
5985
|
}
|
|
4418
|
-
}
|
|
4419
|
-
var DEFAULT_AGENT_PERMISSION_CONFIG = {
|
|
4420
|
-
defaultPermissions: {
|
|
4421
|
-
canSee: {
|
|
4422
|
-
agents: "all",
|
|
4423
|
-
scopes: "all",
|
|
4424
|
-
structure: "full"
|
|
4425
|
-
},
|
|
4426
|
-
canMessage: {
|
|
4427
|
-
agents: "all",
|
|
4428
|
-
scopes: "all"
|
|
4429
|
-
},
|
|
4430
|
-
acceptsFrom: {
|
|
4431
|
-
agents: "all",
|
|
4432
|
-
clients: "all",
|
|
4433
|
-
systems: "all"
|
|
4434
|
-
}
|
|
4435
|
-
},
|
|
4436
|
-
rolePermissions: {}
|
|
4437
5986
|
};
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
5987
|
+
var LocalAgentImpl = class {
|
|
5988
|
+
#conn;
|
|
5989
|
+
constructor(conn) {
|
|
5990
|
+
this.#conn = conn;
|
|
5991
|
+
}
|
|
5992
|
+
get agentId() {
|
|
5993
|
+
return this.#conn.agentId;
|
|
5994
|
+
}
|
|
5995
|
+
get name() {
|
|
5996
|
+
return this.#conn.name;
|
|
5997
|
+
}
|
|
5998
|
+
get role() {
|
|
5999
|
+
return this.#conn.role;
|
|
6000
|
+
}
|
|
6001
|
+
get state() {
|
|
6002
|
+
return this.#conn.state;
|
|
6003
|
+
}
|
|
6004
|
+
async busy() {
|
|
6005
|
+
await this.#conn.updateState("busy");
|
|
6006
|
+
}
|
|
6007
|
+
async idle() {
|
|
6008
|
+
await this.#conn.updateState("idle");
|
|
6009
|
+
}
|
|
6010
|
+
async updateState(state) {
|
|
6011
|
+
await this.#conn.updateState(state);
|
|
4442
6012
|
}
|
|
4443
|
-
|
|
4444
|
-
|
|
6013
|
+
async updateMetadata(metadata) {
|
|
6014
|
+
await this.#conn.updateMetadata(metadata);
|
|
4445
6015
|
}
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
permissions.canSee.agents = mapVisibilityToRule(agent.visibility);
|
|
6016
|
+
async send(to, payload, meta) {
|
|
6017
|
+
return this.#conn.send(to, payload, meta);
|
|
4449
6018
|
}
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
6019
|
+
onMessage(handler) {
|
|
6020
|
+
const wrappedHandler = (message) => handler(message);
|
|
6021
|
+
this.#conn.on("message", wrappedHandler);
|
|
6022
|
+
return () => {
|
|
6023
|
+
this.#conn.off("message", wrappedHandler);
|
|
6024
|
+
};
|
|
4456
6025
|
}
|
|
4457
|
-
|
|
4458
|
-
|
|
6026
|
+
async unregister(reason) {
|
|
6027
|
+
await this.#conn.unregister(reason);
|
|
4459
6028
|
}
|
|
4460
|
-
|
|
4461
|
-
|
|
6029
|
+
};
|
|
6030
|
+
var GitSyncServiceImpl = class {
|
|
6031
|
+
#service;
|
|
6032
|
+
#defaultRepoPath;
|
|
6033
|
+
#defaultClient = null;
|
|
6034
|
+
constructor(service, defaultRepoPath) {
|
|
6035
|
+
this.#service = service;
|
|
6036
|
+
this.#defaultRepoPath = defaultRepoPath;
|
|
6037
|
+
}
|
|
6038
|
+
get isRunning() {
|
|
6039
|
+
return true;
|
|
4462
6040
|
}
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
function checkClientAcceptance(rule, senderId) {
|
|
4466
|
-
if (!rule || rule === "all") return true;
|
|
4467
|
-
if (rule === "none") return false;
|
|
4468
|
-
if (typeof rule === "object" && "include" in rule) {
|
|
4469
|
-
return rule.include.includes(senderId);
|
|
6041
|
+
get httpPort() {
|
|
6042
|
+
return this.#service.httpPort;
|
|
4470
6043
|
}
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
function checkSystemAcceptance(rule, senderSystemId) {
|
|
4474
|
-
if (!rule || rule === "all") return true;
|
|
4475
|
-
if (rule === "none") return false;
|
|
4476
|
-
if (typeof rule === "object" && "include" in rule) {
|
|
4477
|
-
return senderSystemId !== void 0 && rule.include.includes(senderSystemId);
|
|
6044
|
+
createSyncClient(repoPath) {
|
|
6045
|
+
return this.#service.createSyncClient(repoPath);
|
|
4478
6046
|
}
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
if (!acceptsFrom) return true;
|
|
4485
|
-
switch (context.senderType) {
|
|
4486
|
-
case "agent":
|
|
4487
|
-
return checkAgentAcceptance(acceptsFrom.agents, context);
|
|
4488
|
-
case "client":
|
|
4489
|
-
return checkClientAcceptance(acceptsFrom.clients, context.senderId);
|
|
4490
|
-
case "system":
|
|
4491
|
-
case "gateway":
|
|
4492
|
-
return checkSystemAcceptance(acceptsFrom.systems, context.senderSystemId);
|
|
4493
|
-
default:
|
|
4494
|
-
return false;
|
|
6047
|
+
#getDefaultClient() {
|
|
6048
|
+
if (!this.#defaultClient) {
|
|
6049
|
+
this.#defaultClient = this.#service.createSyncClient(this.#defaultRepoPath);
|
|
6050
|
+
}
|
|
6051
|
+
return this.#defaultClient;
|
|
4495
6052
|
}
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
const permissions = resolveAgentPermissions(viewerAgent, config);
|
|
4499
|
-
const canSee = permissions.canSee?.agents;
|
|
4500
|
-
if (!canSee || canSee === "all") return true;
|
|
4501
|
-
if (canSee === "hierarchy") {
|
|
4502
|
-
return context.isParent === true || context.isChild === true || context.isAncestor === true || context.isDescendant === true;
|
|
6053
|
+
async sync(peerId, options) {
|
|
6054
|
+
return this.#getDefaultClient().sync(peerId, options);
|
|
4503
6055
|
}
|
|
4504
|
-
|
|
4505
|
-
return (
|
|
6056
|
+
async pull(peerId, branch, options) {
|
|
6057
|
+
return this.#getDefaultClient().pull(peerId, branch, options);
|
|
4506
6058
|
}
|
|
4507
|
-
|
|
4508
|
-
return
|
|
6059
|
+
async push(peerId, branch, options) {
|
|
6060
|
+
return this.#getDefaultClient().push(peerId, branch, options);
|
|
4509
6061
|
}
|
|
4510
|
-
|
|
4511
|
-
return
|
|
6062
|
+
async clone(peerId, destPath, options) {
|
|
6063
|
+
return this.#getDefaultClient().clone(peerId, destPath, options);
|
|
4512
6064
|
}
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
6065
|
+
};
|
|
6066
|
+
|
|
6067
|
+
// src/acp/adapter.ts
|
|
6068
|
+
var ACPAgentAdapter = class {
|
|
6069
|
+
#mapAgent;
|
|
6070
|
+
#handler;
|
|
6071
|
+
#streamContexts = /* @__PURE__ */ new Map();
|
|
6072
|
+
#pendingClientRequests = /* @__PURE__ */ new Map();
|
|
6073
|
+
#clientRequestTimeout;
|
|
6074
|
+
/**
|
|
6075
|
+
* Create a new ACP agent adapter.
|
|
6076
|
+
*
|
|
6077
|
+
* @param mapAgent - The underlying MAP agent connection
|
|
6078
|
+
* @param handler - Handler implementing ACP agent methods
|
|
6079
|
+
* @param options - Optional configuration
|
|
6080
|
+
*/
|
|
6081
|
+
constructor(mapAgent, handler, options) {
|
|
6082
|
+
this.#mapAgent = mapAgent;
|
|
6083
|
+
this.#handler = handler;
|
|
6084
|
+
this.#clientRequestTimeout = options?.clientRequestTimeout ?? 3e4;
|
|
6085
|
+
mapAgent.onMessage((message) => {
|
|
6086
|
+
if (!message.payload || !isACPEnvelope(message.payload)) {
|
|
6087
|
+
return;
|
|
6088
|
+
}
|
|
6089
|
+
const envelope = message.payload;
|
|
6090
|
+
const { acp, acpContext } = envelope;
|
|
6091
|
+
if (acp.id !== void 0 && !acp.method) {
|
|
6092
|
+
void this.#handleMessage(message);
|
|
6093
|
+
return;
|
|
6094
|
+
}
|
|
6095
|
+
if (acp.method) {
|
|
6096
|
+
this.#trackStreamContext(acpContext, message.from);
|
|
6097
|
+
}
|
|
6098
|
+
queueMicrotask(() => void this.#handleMessage(message));
|
|
6099
|
+
});
|
|
4521
6100
|
}
|
|
4522
|
-
|
|
4523
|
-
|
|
6101
|
+
/**
|
|
6102
|
+
* Track stream context for a client request.
|
|
6103
|
+
* This is called synchronously so that hasStream() works immediately.
|
|
6104
|
+
*/
|
|
6105
|
+
#trackStreamContext(acpCtx, clientParticipantId) {
|
|
6106
|
+
if (!this.#streamContexts.has(acpCtx.streamId)) {
|
|
6107
|
+
this.#streamContexts.set(acpCtx.streamId, {
|
|
6108
|
+
clientParticipantId,
|
|
6109
|
+
sessionId: acpCtx.sessionId
|
|
6110
|
+
});
|
|
6111
|
+
} else if (acpCtx.sessionId) {
|
|
6112
|
+
const streamCtx = this.#streamContexts.get(acpCtx.streamId);
|
|
6113
|
+
streamCtx.sessionId = acpCtx.sessionId;
|
|
6114
|
+
}
|
|
4524
6115
|
}
|
|
4525
|
-
|
|
4526
|
-
|
|
6116
|
+
// ===========================================================================
|
|
6117
|
+
// Message Handling
|
|
6118
|
+
// ===========================================================================
|
|
6119
|
+
/**
|
|
6120
|
+
* Handle incoming messages from MAP.
|
|
6121
|
+
*/
|
|
6122
|
+
async #handleMessage(message) {
|
|
6123
|
+
if (!message.payload || !isACPEnvelope(message.payload)) {
|
|
6124
|
+
return;
|
|
6125
|
+
}
|
|
6126
|
+
const envelope = message.payload;
|
|
6127
|
+
const { acp, acpContext } = envelope;
|
|
6128
|
+
if (acp.id !== void 0 && !acp.method) {
|
|
6129
|
+
const requestId = String(acp.id);
|
|
6130
|
+
const pending = this.#pendingClientRequests.get(requestId);
|
|
6131
|
+
if (pending) {
|
|
6132
|
+
clearTimeout(pending.timeout);
|
|
6133
|
+
this.#pendingClientRequests.delete(requestId);
|
|
6134
|
+
if (acp.error) {
|
|
6135
|
+
pending.reject(ACPError.fromResponse(acp.error));
|
|
6136
|
+
} else {
|
|
6137
|
+
pending.resolve(acp.result);
|
|
6138
|
+
}
|
|
6139
|
+
}
|
|
6140
|
+
return;
|
|
6141
|
+
}
|
|
6142
|
+
if (acp.method) {
|
|
6143
|
+
await this.#handleClientRequest(
|
|
6144
|
+
acp.id,
|
|
6145
|
+
acp.method,
|
|
6146
|
+
acp.params,
|
|
6147
|
+
acpContext,
|
|
6148
|
+
message
|
|
6149
|
+
);
|
|
6150
|
+
}
|
|
4527
6151
|
}
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
6152
|
+
/**
|
|
6153
|
+
* Handle a client→agent request.
|
|
6154
|
+
*/
|
|
6155
|
+
async #handleClientRequest(requestId, method, params, acpCtx, originalMessage) {
|
|
6156
|
+
const ctx = {
|
|
6157
|
+
streamId: acpCtx.streamId,
|
|
6158
|
+
sessionId: acpCtx.sessionId,
|
|
6159
|
+
clientParticipantId: originalMessage.from
|
|
6160
|
+
};
|
|
6161
|
+
let result;
|
|
6162
|
+
let error;
|
|
6163
|
+
try {
|
|
6164
|
+
switch (method) {
|
|
6165
|
+
case ACP_METHODS.INITIALIZE:
|
|
6166
|
+
result = await this.#handler.initialize(
|
|
6167
|
+
params,
|
|
6168
|
+
ctx
|
|
6169
|
+
);
|
|
6170
|
+
break;
|
|
6171
|
+
case ACP_METHODS.AUTHENTICATE:
|
|
6172
|
+
if (!this.#handler.authenticate) {
|
|
6173
|
+
throw new ACPError(-32601, "Method not implemented: authenticate");
|
|
6174
|
+
}
|
|
6175
|
+
result = await this.#handler.authenticate(
|
|
6176
|
+
params,
|
|
6177
|
+
ctx
|
|
6178
|
+
);
|
|
6179
|
+
break;
|
|
6180
|
+
case ACP_METHODS.SESSION_NEW:
|
|
6181
|
+
result = await this.#handler.newSession(
|
|
6182
|
+
params,
|
|
6183
|
+
ctx
|
|
6184
|
+
);
|
|
6185
|
+
const newSessionResult = result;
|
|
6186
|
+
const streamContext = this.#streamContexts.get(acpCtx.streamId);
|
|
6187
|
+
if (streamContext) {
|
|
6188
|
+
streamContext.sessionId = newSessionResult.sessionId;
|
|
6189
|
+
}
|
|
6190
|
+
break;
|
|
6191
|
+
case ACP_METHODS.SESSION_LOAD:
|
|
6192
|
+
if (!this.#handler.loadSession) {
|
|
6193
|
+
throw new ACPError(-32601, "Method not implemented: session/load");
|
|
6194
|
+
}
|
|
6195
|
+
result = await this.#handler.loadSession(
|
|
6196
|
+
params,
|
|
6197
|
+
ctx
|
|
6198
|
+
);
|
|
6199
|
+
break;
|
|
6200
|
+
case ACP_METHODS.SESSION_SET_MODE:
|
|
6201
|
+
if (!this.#handler.setSessionMode) {
|
|
6202
|
+
throw new ACPError(-32601, "Method not implemented: session/set_mode");
|
|
6203
|
+
}
|
|
6204
|
+
result = await this.#handler.setSessionMode(
|
|
6205
|
+
params,
|
|
6206
|
+
ctx
|
|
6207
|
+
);
|
|
6208
|
+
break;
|
|
6209
|
+
case ACP_METHODS.SESSION_PROMPT:
|
|
6210
|
+
result = await this.#handler.prompt(params, ctx);
|
|
6211
|
+
break;
|
|
6212
|
+
case ACP_METHODS.SESSION_CANCEL:
|
|
6213
|
+
await this.#handler.cancel(params, ctx);
|
|
6214
|
+
return;
|
|
6215
|
+
default:
|
|
6216
|
+
throw new ACPError(-32601, `Unknown method: ${method}`);
|
|
6217
|
+
}
|
|
6218
|
+
} catch (e) {
|
|
6219
|
+
if (e instanceof ACPError) {
|
|
6220
|
+
error = e;
|
|
6221
|
+
} else {
|
|
6222
|
+
error = new ACPError(-32603, e.message);
|
|
6223
|
+
}
|
|
6224
|
+
}
|
|
6225
|
+
if (requestId !== void 0) {
|
|
6226
|
+
const responseEnvelope = {
|
|
6227
|
+
acp: {
|
|
6228
|
+
jsonrpc: "2.0",
|
|
6229
|
+
id: requestId,
|
|
6230
|
+
...error ? { error: error.toErrorObject() } : { result }
|
|
6231
|
+
},
|
|
6232
|
+
acpContext: {
|
|
6233
|
+
streamId: acpCtx.streamId,
|
|
6234
|
+
sessionId: this.#streamContexts.get(acpCtx.streamId)?.sessionId ?? null,
|
|
6235
|
+
direction: "agent-to-client"
|
|
6236
|
+
}
|
|
6237
|
+
};
|
|
6238
|
+
this.#mapAgent.send(
|
|
6239
|
+
{ participant: originalMessage.from },
|
|
6240
|
+
responseEnvelope,
|
|
6241
|
+
{
|
|
6242
|
+
protocol: "acp",
|
|
6243
|
+
correlationId: originalMessage.id
|
|
6244
|
+
}
|
|
6245
|
+
).catch((err) => {
|
|
6246
|
+
console.error("ACP: Failed to send response:", err);
|
|
6247
|
+
});
|
|
6248
|
+
}
|
|
4538
6249
|
}
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
6250
|
+
// ===========================================================================
|
|
6251
|
+
// Agent→Client Communication
|
|
6252
|
+
// ===========================================================================
|
|
6253
|
+
/**
|
|
6254
|
+
* Send a session update notification to the client.
|
|
6255
|
+
*/
|
|
6256
|
+
async sendSessionUpdate(streamId, notification) {
|
|
6257
|
+
const streamCtx = this.#streamContexts.get(streamId);
|
|
6258
|
+
if (!streamCtx) {
|
|
6259
|
+
throw new Error(`Unknown stream: ${streamId}`);
|
|
6260
|
+
}
|
|
6261
|
+
const envelope = {
|
|
6262
|
+
acp: {
|
|
6263
|
+
jsonrpc: "2.0",
|
|
6264
|
+
method: ACP_METHODS.SESSION_UPDATE,
|
|
6265
|
+
params: notification
|
|
6266
|
+
},
|
|
6267
|
+
acpContext: {
|
|
6268
|
+
streamId,
|
|
6269
|
+
sessionId: streamCtx.sessionId,
|
|
6270
|
+
direction: "agent-to-client"
|
|
6271
|
+
}
|
|
6272
|
+
};
|
|
6273
|
+
await this.#mapAgent.send(
|
|
6274
|
+
{ participant: streamCtx.clientParticipantId },
|
|
6275
|
+
envelope,
|
|
6276
|
+
{ protocol: "acp" }
|
|
6277
|
+
);
|
|
4543
6278
|
}
|
|
4544
|
-
|
|
4545
|
-
|
|
6279
|
+
/**
|
|
6280
|
+
* Send an agent→client request and wait for response.
|
|
6281
|
+
*/
|
|
6282
|
+
async #sendClientRequest(streamId, method, params) {
|
|
6283
|
+
const streamCtx = this.#streamContexts.get(streamId);
|
|
6284
|
+
if (!streamCtx) {
|
|
6285
|
+
throw new Error(`Unknown stream: ${streamId}`);
|
|
6286
|
+
}
|
|
6287
|
+
const correlationId = `agent-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
6288
|
+
const envelope = {
|
|
6289
|
+
acp: {
|
|
6290
|
+
jsonrpc: "2.0",
|
|
6291
|
+
id: correlationId,
|
|
6292
|
+
method,
|
|
6293
|
+
params
|
|
6294
|
+
},
|
|
6295
|
+
acpContext: {
|
|
6296
|
+
streamId,
|
|
6297
|
+
sessionId: streamCtx.sessionId,
|
|
6298
|
+
direction: "agent-to-client",
|
|
6299
|
+
pendingClientRequest: {
|
|
6300
|
+
requestId: correlationId,
|
|
6301
|
+
method,
|
|
6302
|
+
timeout: this.#clientRequestTimeout
|
|
6303
|
+
}
|
|
6304
|
+
}
|
|
6305
|
+
};
|
|
6306
|
+
await this.#mapAgent.send(
|
|
6307
|
+
{ participant: streamCtx.clientParticipantId },
|
|
6308
|
+
envelope,
|
|
6309
|
+
{ protocol: "acp", correlationId }
|
|
6310
|
+
);
|
|
6311
|
+
return new Promise((resolve, reject) => {
|
|
6312
|
+
const timeoutHandle = setTimeout(() => {
|
|
6313
|
+
this.#pendingClientRequests.delete(correlationId);
|
|
6314
|
+
reject(new Error(`Client request timed out: ${method}`));
|
|
6315
|
+
}, this.#clientRequestTimeout);
|
|
6316
|
+
this.#pendingClientRequests.set(correlationId, {
|
|
6317
|
+
resolve,
|
|
6318
|
+
reject,
|
|
6319
|
+
timeout: timeoutHandle,
|
|
6320
|
+
method
|
|
6321
|
+
});
|
|
6322
|
+
});
|
|
4546
6323
|
}
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
throw new InvalidAddressError(address, "missing type prefix");
|
|
6324
|
+
/**
|
|
6325
|
+
* Request permission from the client.
|
|
6326
|
+
*/
|
|
6327
|
+
async requestPermission(streamId, request) {
|
|
6328
|
+
return this.#sendClientRequest(streamId, ACP_METHODS.REQUEST_PERMISSION, request);
|
|
4553
6329
|
}
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
6330
|
+
/**
|
|
6331
|
+
* Read a text file from the client.
|
|
6332
|
+
*/
|
|
6333
|
+
async readTextFile(streamId, request) {
|
|
6334
|
+
return this.#sendClientRequest(streamId, ACP_METHODS.FS_READ_TEXT_FILE, request);
|
|
4558
6335
|
}
|
|
4559
|
-
|
|
4560
|
-
|
|
6336
|
+
/**
|
|
6337
|
+
* Write a text file on the client.
|
|
6338
|
+
*/
|
|
6339
|
+
async writeTextFile(streamId, request) {
|
|
6340
|
+
return this.#sendClientRequest(streamId, ACP_METHODS.FS_WRITE_TEXT_FILE, request);
|
|
4561
6341
|
}
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
function isAddress(address) {
|
|
4568
|
-
try {
|
|
4569
|
-
parseAddress(address);
|
|
4570
|
-
return true;
|
|
4571
|
-
} catch {
|
|
4572
|
-
return false;
|
|
6342
|
+
/**
|
|
6343
|
+
* Create a terminal on the client.
|
|
6344
|
+
*/
|
|
6345
|
+
async createTerminal(streamId, request) {
|
|
6346
|
+
return this.#sendClientRequest(streamId, ACP_METHODS.TERMINAL_CREATE, request);
|
|
4573
6347
|
}
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
return
|
|
4579
|
-
} catch {
|
|
4580
|
-
return false;
|
|
6348
|
+
/**
|
|
6349
|
+
* Get terminal output from the client.
|
|
6350
|
+
*/
|
|
6351
|
+
async terminalOutput(streamId, request) {
|
|
6352
|
+
return this.#sendClientRequest(streamId, ACP_METHODS.TERMINAL_OUTPUT, request);
|
|
4581
6353
|
}
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
return
|
|
4587
|
-
} catch {
|
|
4588
|
-
return false;
|
|
6354
|
+
/**
|
|
6355
|
+
* Release a terminal on the client.
|
|
6356
|
+
*/
|
|
6357
|
+
async releaseTerminal(streamId, request) {
|
|
6358
|
+
return this.#sendClientRequest(streamId, ACP_METHODS.TERMINAL_RELEASE, request);
|
|
4589
6359
|
}
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
return
|
|
4595
|
-
} catch {
|
|
4596
|
-
return address;
|
|
6360
|
+
/**
|
|
6361
|
+
* Wait for a terminal to exit on the client.
|
|
6362
|
+
*/
|
|
6363
|
+
async waitForTerminalExit(streamId, request) {
|
|
6364
|
+
return this.#sendClientRequest(streamId, ACP_METHODS.TERMINAL_WAIT_FOR_EXIT, request);
|
|
4597
6365
|
}
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
return
|
|
4603
|
-
} catch {
|
|
4604
|
-
return void 0;
|
|
6366
|
+
/**
|
|
6367
|
+
* Kill a terminal command on the client.
|
|
6368
|
+
*/
|
|
6369
|
+
async killTerminal(streamId, request) {
|
|
6370
|
+
return this.#sendClientRequest(streamId, ACP_METHODS.TERMINAL_KILL, request);
|
|
4605
6371
|
}
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
6372
|
+
// ===========================================================================
|
|
6373
|
+
// Utility Methods
|
|
6374
|
+
// ===========================================================================
|
|
6375
|
+
/**
|
|
6376
|
+
* Get the current session ID for a stream.
|
|
6377
|
+
*/
|
|
6378
|
+
getSessionId(streamId) {
|
|
6379
|
+
return this.#streamContexts.get(streamId)?.sessionId ?? null;
|
|
6380
|
+
}
|
|
6381
|
+
/**
|
|
6382
|
+
* Get the client participant ID for a stream.
|
|
6383
|
+
*/
|
|
6384
|
+
getClientParticipantId(streamId) {
|
|
6385
|
+
return this.#streamContexts.get(streamId)?.clientParticipantId;
|
|
6386
|
+
}
|
|
6387
|
+
/**
|
|
6388
|
+
* Check if a stream is active.
|
|
6389
|
+
*/
|
|
6390
|
+
hasStream(streamId) {
|
|
6391
|
+
return this.#streamContexts.has(streamId);
|
|
6392
|
+
}
|
|
6393
|
+
/**
|
|
6394
|
+
* Remove a stream context (e.g., on disconnect).
|
|
6395
|
+
*/
|
|
6396
|
+
removeStream(streamId) {
|
|
6397
|
+
return this.#streamContexts.delete(streamId);
|
|
6398
|
+
}
|
|
6399
|
+
};
|
|
4613
6400
|
|
|
4614
|
-
export { AGENT_ERROR_CODES, AUTH_ERROR_CODES, AUTH_METHODS, AddressSchema, AgentConnection, AgentIdSchema, AgentLifecycleSchema, AgentRelationshipSchema, AgentSchema, AgentStateSchema, AgentVisibilitySchema, BaseConnection, BroadcastAddressSchema, CAPABILITY_REQUIREMENTS, CORE_METHODS, CausalEventBuffer, ClientConnection, CorrelationIdSchema, DEFAULT_AGENT_PERMISSION_CONFIG, DEFAULT_RETRY_POLICY, DeliverySemanticsSchema, DirectAddressSchema, ERROR_CODES, EVENT_TYPES, EXTENSION_METHODS, ErrorCategorySchema, EventSchema, EventTypeSchema, FEDERATION_ERROR_CODES, FEDERATION_METHODS, FederatedAddressSchema, FederationOutageBuffer, GatewayConnection, HierarchicalAddressSchema, InvalidAddressError, JSONRPC_VERSION, JsonRpcVersionSchema, LIFECYCLE_METHODS, MAPConnectionError, MAPErrorDataSchema, MAPErrorSchema, MAPNotificationSchema, MAPRequestError, MAPRequestSchema, MAPResponseErrorSchema, MAPResponseSchema, MAPResponseSuccessSchema, MAPTimeoutError, MAP_METHODS, METHOD_REGISTRY, MessageIdSchema, MessageMetaSchema, MessagePrioritySchema, MessageRelationshipSchema, MessageSchema, MessageVisibilitySchema, MetaSchema, MultiAddressSchema, NOTIFICATION_METHODS, OBSERVATION_METHODS, PERMISSION_METHODS, PROTOCOL_ERROR_CODES, PROTOCOL_VERSION, ParticipantAddressSchema, ParticipantCapabilitiesSchema, ParticipantIdSchema, ParticipantTypeSchema, ProtocolVersionSchema, RESOURCE_ERROR_CODES, ROUTING_ERROR_CODES, RequestIdSchema, RoleAddressSchema, SCOPE_METHODS, SESSION_METHODS, STATE_METHODS, STEERING_METHODS, STRUCTURE_METHODS, ScopeAddressSchema, ScopeIdSchema, ScopeJoinPolicySchema, ScopeSchema, ScopeSendPolicySchema, ScopeVisibilitySchema, SessionIdSchema, Subscription, SubscriptionFilterSchema, SubscriptionIdSchema, SystemAddressSchema, TimestampSchema, TransportTypeSchema, buildAgentsGetResponse, buildAgentsListResponse, buildAgentsRegisterResponse, buildAgentsSpawnResponse, buildAgentsUnregisterResponse, buildAgentsUpdateResponse, buildConnectResponse, buildDisconnectResponse, buildScopesCreateResponse, buildScopesJoinResponse, buildScopesLeaveResponse, buildScopesListResponse, buildSendResponse, buildSubscribeResponse, buildUnsubscribeResponse, calculateDelay, canAgentAcceptMessage, canAgentMessageAgent, canAgentSeeAgent, canControlAgent, canJoinScope, canMessageAgent, canPerformAction, canPerformMethod, canSeeAgent, canSeeScope, canSendToScope, compareUlid, createErrorResponse, createEvent, createFederationEnvelope, createNotification, createRequest, createRetryPolicy, createStreamPair, createSubscription, createSuccessResponse, deepMergePermissions, extractId, extractType, filterVisibleAgents, filterVisibleEvents, filterVisibleScopes, formatAddress, getEnvelopeRoutingInfo, getMethodInfo, getMethodsByCategory, getRequiredCapabilities, hasCapability, hasRequiredCapabilities, isAddress, isAgentAddress, isAgentExposed, isBroadcastAddress, isDirectAddress, isEnvelopeAtDestination, isErrorResponse, isEventTypeExposed, isFederatedAddress, isHierarchicalAddress, isNotification, isOrphanedAgent, isRequest, isResponse, isScopeAddress, isScopeExposed, isSuccessResponse, isValidEnvelope, isValidUlid, mapVisibilityToRule, ndJsonStream, parseAddress, processFederationEnvelope, resolveAgentPermissions, retryable, sleep, sortCausalOrder, toAgent, toScope, ulidTimestamp, unwrapEnvelope, validateCausalOrder, waitForOpen, websocketStream, withPayload, withRetry };
|
|
6401
|
+
export { ACPAgentAdapter, ACPError, ACPStreamConnection, ACP_ERROR_CODES, ACP_METHODS, ACP_PROTOCOL_VERSION, AGENT_ERROR_CODES, AUTH_ERROR_CODES, AUTH_METHODS, AddressSchema, AgentConnection, AgentIdSchema, AgentLifecycleSchema, AgentRelationshipSchema, AgentSchema, AgentStateSchema, AgentVisibilitySchema, BaseConnection, BroadcastAddressSchema, CAPABILITY_REQUIREMENTS, CORE_METHODS, CausalEventBuffer, ClientConnection, CorrelationIdSchema, DEFAULT_AGENT_PERMISSION_CONFIG, DEFAULT_RETRY_POLICY, DeliverySemanticsSchema, DirectAddressSchema, ERROR_CODES, EVENT_TYPES, EXTENSION_METHODS, ErrorCategorySchema, EventSchema, EventTypeSchema, FEDERATION_ERROR_CODES, FEDERATION_METHODS, FederatedAddressSchema, FederationOutageBuffer, GatewayConnection, HierarchicalAddressSchema, InvalidAddressError, JSONRPC_VERSION, JsonRpcVersionSchema, LIFECYCLE_METHODS, MAPConnectionError, MAPErrorDataSchema, MAPErrorSchema, MAPMeshPeer, MAPNotificationSchema, MAPRequestError, MAPRequestSchema, MAPResponseErrorSchema, MAPResponseSchema, MAPResponseSuccessSchema, MAPTimeoutError, MAP_METHODS, METHOD_REGISTRY, MessageIdSchema, MessageMetaSchema, MessagePrioritySchema, MessageRelationshipSchema, MessageSchema, MessageVisibilitySchema, MetaSchema, MultiAddressSchema, NOTIFICATION_METHODS, OBSERVATION_METHODS, PERMISSION_METHODS, PROTOCOL_ERROR_CODES, PROTOCOL_VERSION, ParticipantAddressSchema, ParticipantCapabilitiesSchema, ParticipantIdSchema, ParticipantTypeSchema, ProtocolVersionSchema, RESOURCE_ERROR_CODES, ROUTING_ERROR_CODES, RequestIdSchema, RoleAddressSchema, SCOPE_METHODS, SESSION_METHODS, STATE_METHODS, STEERING_METHODS, STRUCTURE_METHODS, ScopeAddressSchema, ScopeIdSchema, ScopeJoinPolicySchema, ScopeSchema, ScopeSendPolicySchema, ScopeVisibilitySchema, SessionIdSchema, Subscription, SubscriptionFilterSchema, SubscriptionIdSchema, SystemAddressSchema, TimestampSchema, TransportTypeSchema, agenticMeshStream, buildAgentsGetResponse, buildAgentsListResponse, buildAgentsRegisterResponse, buildAgentsSpawnResponse, buildAgentsUnregisterResponse, buildAgentsUpdateResponse, buildConnectResponse, buildDisconnectResponse, buildScopesCreateResponse, buildScopesJoinResponse, buildScopesLeaveResponse, buildScopesListResponse, buildSendResponse, buildSubscribeResponse, buildUnsubscribeResponse, calculateDelay, canAgentAcceptMessage, canAgentMessageAgent, canAgentSeeAgent, canControlAgent, canJoinScope, canMessageAgent, canPerformAction, canPerformMethod, canSeeAgent, canSeeScope, canSendToScope, compareUlid, createACPStream, createErrorResponse, createEvent, createFederationEnvelope, createNotification, createRequest, createRetryPolicy, createStreamPair, createSubscription, createSuccessResponse, deepMergePermissions, extractId, extractType, filterVisibleAgents, filterVisibleEvents, filterVisibleScopes, formatAddress, getEnvelopeRoutingInfo, getMethodInfo, getMethodsByCategory, getRequiredCapabilities, hasCapability, hasRequiredCapabilities, isACPEnvelope, isACPErrorResponse, isACPNotification, isACPRequest, isACPResponse, isACPSuccessResponse, isAddress, isAgentAddress, isAgentExposed, isBroadcastAddress, isDirectAddress, isEnvelopeAtDestination, isErrorResponse, isEventTypeExposed, isFederatedAddress, isHierarchicalAddress, isNotification, isOrphanedAgent, isRequest, isResponse, isScopeAddress, isScopeExposed, isSuccessResponse, isValidEnvelope, isValidUlid, mapVisibilityToRule, ndJsonStream, parseAddress, processFederationEnvelope, resolveAgentPermissions, retryable, sleep, sortCausalOrder, toAgent, toScope, ulidTimestamp, unwrapEnvelope, validateCausalOrder, waitForOpen, websocketStream, withPayload, withRetry };
|
|
4615
6402
|
//# sourceMappingURL=index.js.map
|
|
4616
6403
|
//# sourceMappingURL=index.js.map
|