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