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