@kadi.build/core 0.7.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -107,6 +107,59 @@ client.registerTool({
107
107
  client.registerTool(definition, handler, { brokers: ['internal'] });
108
108
  ```
109
109
 
110
+ ### Targeting Specific Networks
111
+
112
+ By default, a tool is visible on **all networks** the agent has joined. You can restrict a tool to specific networks using the `networks` option — the tool will only be discoverable and invocable by agents on those networks.
113
+
114
+ ```typescript
115
+ const client = new KadiClient({
116
+ name: 'multi-network-agent',
117
+ brokers: { default: 'ws://localhost:8080/kadi' },
118
+ networks: ['global', 'internal', 'gpu-cluster'],
119
+ });
120
+
121
+ // Visible on ALL agent networks (global, internal, gpu-cluster)
122
+ client.registerTool({
123
+ name: 'echo',
124
+ description: 'Echo back the input',
125
+ input: z.object({ message: z.string() }),
126
+ }, async ({ message }) => ({ echo: message }));
127
+
128
+ // Visible ONLY on gpu-cluster
129
+ client.registerTool({
130
+ name: 'gpu-inference',
131
+ description: 'Run ML inference on GPU',
132
+ input: z.object({ model: z.string(), prompt: z.string() }),
133
+ }, async ({ model, prompt }) => {
134
+ return { result: await runInference(model, prompt) };
135
+ }, { networks: ['gpu-cluster'] });
136
+
137
+ // Visible on internal AND global only (not gpu-cluster)
138
+ client.registerTool({
139
+ name: 'audit-log',
140
+ description: 'Retrieve audit logs',
141
+ input: z.object({ limit: z.number() }),
142
+ }, async ({ limit }) => {
143
+ return { logs: await getAuditLogs(limit) };
144
+ }, { networks: ['internal', 'global'] });
145
+ ```
146
+
147
+ Per-tool networks must be a **subset** of the client's `networks`. Attempting to scope a tool to a network the agent hasn't joined throws an `INVALID_CONFIG` error:
148
+
149
+ ```typescript
150
+ // Throws KadiError — 'finance' is not in client networks
151
+ client.registerTool(definition, handler, { networks: ['finance'] });
152
+ ```
153
+
154
+ You can combine `brokers` and `networks` to control both which brokers a tool registers with and which networks it's visible on:
155
+
156
+ ```typescript
157
+ client.registerTool(definition, handler, {
158
+ brokers: ['production'],
159
+ networks: ['internal'],
160
+ });
161
+ ```
162
+
110
163
  ---
111
164
 
112
165
  ## Connecting to Brokers
package/dist/client.d.ts CHANGED
@@ -15,7 +15,7 @@
15
15
  * - Readable, traceable code flow
16
16
  */
17
17
  import { type EncryptionKeyPair } from './crypto.js';
18
- import type { ClientConfig, ClientIdentity, BrokerState, ToolDefinition, ZodToolDefinition, ToolHandler, LoadedAbility, ToolExecutionBridge, RegisterToolOptions, LoadNativeOptions, LoadStdioOptions, LoadBrokerOptions, InvokeRemoteOptions, BrokerEventHandler, PublishOptions, SubscribeOptions, EmitOptions } from './types.js';
18
+ import type { ClientConfig, ClientIdentity, BrokerState, BrokerToolDefinition, ZodToolDefinition, ToolHandler, LoadedAbility, ToolExecutionBridge, RegisterToolOptions, LoadNativeOptions, LoadStdioOptions, LoadBrokerOptions, InvokeRemoteOptions, BrokerEventHandler, PublishOptions, SubscribeOptions, EmitOptions } from './types.js';
19
19
  /**
20
20
  * The main client for building KADI agents.
21
21
  *
@@ -25,8 +25,8 @@ import type { ClientConfig, ClientIdentity, BrokerState, ToolDefinition, ZodTool
25
25
  * const client = new KadiClient({
26
26
  * name: 'my-agent',
27
27
  * brokers: {
28
- * production: 'ws://broker-prod:8080',
29
- * internal: 'ws://broker-internal:8080',
28
+ * production: { url: 'ws://broker-prod:8080/kadi' },
29
+ * internal: { url: 'ws://broker-internal:8080/kadi', networks: ['private'] },
30
30
  * },
31
31
  * defaultBroker: 'production',
32
32
  * });
@@ -404,6 +404,11 @@ export declare class KadiClient {
404
404
  * Handle response to a pending request.
405
405
  */
406
406
  private handleBrokerResponse;
407
+ /**
408
+ * Finalize a broker connection: start heartbeat and mark as connected.
409
+ * Shared by initial connect and reconnect paths.
410
+ */
411
+ private finalizeConnection;
407
412
  /**
408
413
  * Send heartbeat to keep connection alive.
409
414
  */
@@ -521,8 +526,12 @@ export declare class KadiClient {
521
526
  * output: z.object({ result: z.number() }),
522
527
  * }, async ({ a, b }) => ({ result: a + b }));
523
528
  *
524
- * // Register only on specific broker
525
- * client.registerTool(def, handler, { brokers: ['internal'] });
529
+ * // Register on specific brokers with per-broker networks
530
+ * client.registerTool(def, handler, {
531
+ * brokers: {
532
+ * internal: { networks: ['private'] },
533
+ * },
534
+ * });
526
535
  * ```
527
536
  */
528
537
  registerTool<TInput, TOutput>(definition: ZodToolDefinition<TInput, TOutput>, handler: ToolHandler<TInput, TOutput>, options?: RegisterToolOptions): void;
@@ -530,7 +539,7 @@ export declare class KadiClient {
530
539
  * Get tool definitions, optionally filtered for a specific broker.
531
540
  *
532
541
  * @param forBroker - If provided, only return tools targeted for this broker.
533
- * Tools with empty targetBrokers are included for all brokers.
542
+ * Tools with empty brokerNetworks are included for all brokers.
534
543
  */
535
544
  private getToolDefinitions;
536
545
  /**
@@ -697,7 +706,7 @@ export declare class KadiClient {
697
706
  readAgentJson(): {
698
707
  name: string;
699
708
  version: string;
700
- tools: ToolDefinition[];
709
+ tools: BrokerToolDefinition[];
701
710
  };
702
711
  /**
703
712
  * Get broker connection state (for broker transport).
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js CHANGED
@@ -20,6 +20,7 @@ import { convertToEncryptionKey, convertToEncryptionKeyPair, } from './crypto.js
20
20
  // @ts-expect-error - tweetnacl-sealedbox-js doesn't have type definitions
21
21
  import sealedbox from 'tweetnacl-sealedbox-js';
22
22
  import { KadiError } from './errors.js';
23
+ import * as protocol from './protocol.js';
23
24
  import { zodToJsonSchema, isZodSchema } from './zod.js';
24
25
  import { resolveAbilityEntry, resolveAbilityScript } from './lockfile.js';
25
26
  import { loadNativeTransport } from './transports/native.js';
@@ -112,8 +113,8 @@ function isMcpCallToolResult(result) {
112
113
  * const client = new KadiClient({
113
114
  * name: 'my-agent',
114
115
  * brokers: {
115
- * production: 'ws://broker-prod:8080',
116
- * internal: 'ws://broker-internal:8080',
116
+ * production: { url: 'ws://broker-prod:8080/kadi' },
117
+ * internal: { url: 'ws://broker-internal:8080/kadi', networks: ['private'] },
117
118
  * },
118
119
  * defaultBroker: 'production',
119
120
  * });
@@ -182,16 +183,21 @@ export class KadiClient {
182
183
  // Derive agentId using same algorithm as broker: SHA256(publicKey).hex().substring(0, 16)
183
184
  this._agentId = crypto.createHash('sha256').update(this._publicKeyBase64).digest('hex').substring(0, 16);
184
185
  // Resolve configuration with defaults
185
- // Auto-select first broker as default if not specified (matches Python behavior)
186
- const brokers = config.brokers ?? {};
187
- const firstBrokerName = Object.keys(brokers)[0];
186
+ const rawBrokers = config.brokers ?? {};
187
+ const normalizedBrokers = {};
188
+ for (const [name, entry] of Object.entries(rawBrokers)) {
189
+ normalizedBrokers[name] = {
190
+ url: entry.url,
191
+ networks: entry.networks ?? ['global'],
192
+ };
193
+ }
194
+ const firstBrokerName = Object.keys(normalizedBrokers)[0];
188
195
  this.config = {
189
196
  name: config.name,
190
197
  version: config.version ?? '1.0.0',
191
198
  description: config.description ?? '',
192
- brokers,
199
+ brokers: normalizedBrokers,
193
200
  defaultBroker: config.defaultBroker ?? firstBrokerName,
194
- networks: config.networks ?? ['global'],
195
201
  heartbeatInterval: config.heartbeatInterval ?? DEFAULT_HEARTBEAT_INTERVAL,
196
202
  requestTimeout: config.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT,
197
203
  autoReconnect: config.autoReconnect ?? DEFAULT_AUTO_RECONNECT,
@@ -364,8 +370,6 @@ export class KadiClient {
364
370
  for (const [i, result] of results.entries()) {
365
371
  if (result.status === 'rejected') {
366
372
  const failedBroker = brokerNames[i];
367
- if (failedBroker === undefined)
368
- continue;
369
373
  const reason = result.reason instanceof Error ? result.reason.message : String(result.reason);
370
374
  console.error(`[KADI] Failed to connect to broker "${failedBroker}": ${reason}`);
371
375
  failures.push(failedBroker);
@@ -397,14 +401,15 @@ export class KadiClient {
397
401
  * across all broker connections, ensuring consistent identity.
398
402
  */
399
403
  async connectToBroker(brokerName) {
400
- const url = this.config.brokers[brokerName];
401
- if (!url) {
404
+ const brokerConfig = this.config.brokers[brokerName];
405
+ if (!brokerConfig) {
402
406
  throw new KadiError(`Broker "${brokerName}" not found in configuration`, 'UNKNOWN_BROKER', {
403
407
  broker: brokerName,
404
408
  available: Object.keys(this.config.brokers),
405
409
  hint: 'Check your brokers configuration',
406
410
  });
407
411
  }
412
+ const url = brokerConfig.url;
408
413
  // Check if already connected
409
414
  if (this.brokers.has(brokerName)) {
410
415
  const existing = this.brokers.get(brokerName);
@@ -416,6 +421,7 @@ export class KadiClient {
416
421
  const state = {
417
422
  name: brokerName,
418
423
  url,
424
+ networks: brokerConfig.networks,
419
425
  ws: null,
420
426
  heartbeatTimer: null,
421
427
  pendingRequests: new Map(),
@@ -436,22 +442,14 @@ export class KadiClient {
436
442
  await this.performHandshake(state);
437
443
  // Register with broker (transitions to "ready" state)
438
444
  await this.registerWithBroker(state);
439
- // Start heartbeat
440
- state.heartbeatTimer = setInterval(() => {
441
- this.sendHeartbeat(state);
442
- }, this.config.heartbeatInterval);
443
- state.status = 'connected';
445
+ // Finalize connection (heartbeat + status)
446
+ this.finalizeConnection(state);
444
447
  }
445
448
  // ─────────────────────────────────────────────────────────────
446
449
  // MESSAGE BUILDERS
447
450
  // ─────────────────────────────────────────────────────────────
448
451
  buildHelloMessage() {
449
- return {
450
- jsonrpc: '2.0',
451
- id: this.nextRequestId++,
452
- method: 'kadi.session.hello',
453
- params: { role: 'agent' },
454
- };
452
+ return protocol.hello(this.nextRequestId++);
455
453
  }
456
454
  buildAuthMessage(nonce) {
457
455
  // Sign the nonce using the client's private key
@@ -461,36 +459,13 @@ export class KadiClient {
461
459
  type: 'pkcs8',
462
460
  });
463
461
  const signature = crypto.sign(null, Buffer.from(nonce, 'utf8'), privateKey);
464
- return {
465
- jsonrpc: '2.0',
466
- id: this.nextRequestId++,
467
- method: 'kadi.session.authenticate',
468
- params: {
469
- publicKey: this._publicKeyBase64,
470
- signature: signature.toString('base64'),
471
- nonce,
472
- },
473
- };
462
+ return protocol.authenticate(this.nextRequestId++, this._publicKeyBase64, signature.toString('base64'), nonce);
474
463
  }
475
464
  buildRegisterMessage(tools, networks) {
476
- return {
477
- jsonrpc: '2.0',
478
- id: this.nextRequestId++,
479
- method: 'kadi.agent.register',
480
- params: {
481
- tools,
482
- networks,
483
- displayName: this.config.name,
484
- },
485
- };
465
+ return protocol.register(this.nextRequestId++, tools, networks, this.config.name);
486
466
  }
487
467
  buildHeartbeatMessage() {
488
- return {
489
- jsonrpc: '2.0',
490
- id: this.nextRequestId++,
491
- method: 'kadi.session.heartbeat',
492
- params: { timestamp: Date.now() },
493
- };
468
+ return protocol.heartbeat(this.nextRequestId++);
494
469
  }
495
470
  // ─────────────────────────────────────────────────────────────
496
471
  // CONNECTION HELPERS
@@ -593,7 +568,11 @@ export class KadiClient {
593
568
  // Note: If registration fails, sendRequest will reject with the error.
594
569
  // We don't need to check response.error here - errors are already
595
570
  // handled via Promise rejection in handleBrokerResponse.
596
- await this.sendRequest(state, this.buildRegisterMessage(tools, this.config.networks));
571
+ const { droppedNetworks } = await this.sendRequest(state, this.buildRegisterMessage(tools, state.networks));
572
+ if (droppedNetworks && droppedNetworks.length > 0) {
573
+ console.warn(`[KADI] Broker "${state.name}" dropped networks ${JSON.stringify(droppedNetworks)} ` +
574
+ `during registration. Tools scoped to those networks will not be discoverable.`);
575
+ }
597
576
  }
598
577
  /**
599
578
  * Send a JSON-RPC request and wait for the response.
@@ -862,12 +841,7 @@ export class KadiClient {
862
841
  handlers.add(handler);
863
842
  // If this is the first handler for this pattern, subscribe on broker
864
843
  if (!state.subscribedPatterns.has(pattern)) {
865
- await this.sendRequest(state, {
866
- jsonrpc: '2.0',
867
- id: this.nextRequestId++,
868
- method: 'kadi.event.subscribe',
869
- params: { pattern },
870
- });
844
+ await this.sendRequest(state, protocol.eventSubscribe(this.nextRequestId++, pattern));
871
845
  state.subscribedPatterns.add(pattern);
872
846
  }
873
847
  }
@@ -913,12 +887,7 @@ export class KadiClient {
913
887
  // Unsubscribe from broker if we were subscribed
914
888
  if (state.subscribedPatterns.has(pattern) && state.status === 'connected') {
915
889
  try {
916
- await this.sendRequest(state, {
917
- jsonrpc: '2.0',
918
- id: this.nextRequestId++,
919
- method: 'kadi.event.unsubscribe',
920
- params: { pattern },
921
- });
890
+ await this.sendRequest(state, protocol.eventUnsubscribe(this.nextRequestId++, pattern));
922
891
  }
923
892
  catch {
924
893
  // Ignore errors during unsubscribe (broker might be disconnecting)
@@ -949,19 +918,10 @@ export class KadiClient {
949
918
  */
950
919
  async publish(channel, data, options = {}) {
951
920
  const { state } = this.getConnectedBrokerState(options.broker);
952
- // Resolve which network to publish to
953
- const networkId = options.network ?? this.config.networks[0] ?? 'global';
921
+ // Resolve which network to publish to (prefer broker-specific, fallback to global)
922
+ const networkId = options.network ?? state.networks[0] ?? '';
954
923
  // Send publish request to broker
955
- await this.sendRequest(state, {
956
- jsonrpc: '2.0',
957
- id: this.nextRequestId++,
958
- method: 'kadi.event.publish',
959
- params: {
960
- channel,
961
- data,
962
- networkId,
963
- },
964
- });
924
+ await this.sendRequest(state, protocol.eventPublish(this.nextRequestId++, channel, data, networkId));
965
925
  }
966
926
  // ─────────────────────────────────────────────────────────────
967
927
  // DEPLOYMENT SECRETS (Trusted Introducer Pattern)
@@ -1191,35 +1151,18 @@ export class KadiClient {
1191
1151
  // KADI clients receive raw structured data
1192
1152
  responseResult = result;
1193
1153
  }
1194
- const response = {
1195
- jsonrpc: '2.0',
1196
- id: requestId,
1197
- result: responseResult,
1198
- };
1199
- state.ws?.send(JSON.stringify(response));
1154
+ state.ws?.send(JSON.stringify(protocol.resultResponse(requestId, responseResult)));
1200
1155
  }
1201
1156
  catch (error) {
1202
1157
  const errorMessage = error instanceof Error ? error.message : String(error);
1203
1158
  // For MCP clients, wrap errors in CallToolResult format too
1204
1159
  if (context.callerProtocol === 'mcp') {
1205
- const response = {
1206
- jsonrpc: '2.0',
1207
- id: requestId,
1208
- result: { content: [{ type: 'text', text: errorMessage }], isError: true },
1209
- };
1210
- state.ws?.send(JSON.stringify(response));
1160
+ const mcpResult = { content: [{ type: 'text', text: errorMessage }], isError: true };
1161
+ state.ws?.send(JSON.stringify(protocol.resultResponse(requestId, mcpResult)));
1211
1162
  }
1212
1163
  else {
1213
1164
  // KADI clients receive JSON-RPC error
1214
- const response = {
1215
- jsonrpc: '2.0',
1216
- id: requestId,
1217
- error: {
1218
- code: -32000,
1219
- message: errorMessage,
1220
- },
1221
- };
1222
- state.ws?.send(JSON.stringify(response));
1165
+ state.ws?.send(JSON.stringify(protocol.errorResponse(requestId, -32000, errorMessage)));
1223
1166
  }
1224
1167
  }
1225
1168
  }
@@ -1244,6 +1187,16 @@ export class KadiClient {
1244
1187
  pending.resolve(response.result);
1245
1188
  }
1246
1189
  }
1190
+ /**
1191
+ * Finalize a broker connection: start heartbeat and mark as connected.
1192
+ * Shared by initial connect and reconnect paths.
1193
+ */
1194
+ finalizeConnection(state) {
1195
+ state.heartbeatTimer = setInterval(() => {
1196
+ this.sendHeartbeat(state);
1197
+ }, this.config.heartbeatInterval);
1198
+ state.status = 'connected';
1199
+ }
1247
1200
  /**
1248
1201
  * Send heartbeat to keep connection alive.
1249
1202
  */
@@ -1283,6 +1236,9 @@ export class KadiClient {
1283
1236
  }));
1284
1237
  }
1285
1238
  state.pendingInvocations.clear();
1239
+ // Clear broker-side subscription tracking — subscriptions are lost when
1240
+ // the connection drops and must be re-established after reconnection.
1241
+ state.subscribedPatterns.clear();
1286
1242
  }
1287
1243
  // ─────────────────────────────────────────────────────────────
1288
1244
  // RECONNECTION LOGIC
@@ -1395,14 +1351,11 @@ export class KadiClient {
1395
1351
  await this.performHandshake(state);
1396
1352
  // Re-register tools
1397
1353
  await this.registerWithBroker(state);
1398
- // Restart heartbeat
1399
- state.heartbeatTimer = setInterval(() => {
1400
- this.sendHeartbeat(state);
1401
- }, this.config.heartbeatInterval);
1354
+ // Finalize connection (heartbeat + status)
1355
+ this.finalizeConnection(state);
1402
1356
  // Success!
1403
1357
  console.error(`[KADI] Reconnected to broker "${state.name}" after ${state.reconnectAttempts} attempts`);
1404
1358
  state.reconnectAttempts = 0;
1405
- state.status = 'connected';
1406
1359
  }
1407
1360
  catch (error) {
1408
1361
  // Log the error and try again
@@ -1509,12 +1462,7 @@ export class KadiClient {
1509
1462
  }
1510
1463
  else if (this.isServingStdio) {
1511
1464
  // Stdio transport: write notification to stdout
1512
- const notification = {
1513
- jsonrpc: '2.0',
1514
- method: 'event',
1515
- params: { name: event, data },
1516
- };
1517
- const json = JSON.stringify(notification);
1465
+ const json = JSON.stringify(protocol.eventNotification(event, data));
1518
1466
  process.stdout.write(`Content-Length: ${Buffer.byteLength(json)}\r\n\r\n${json}`);
1519
1467
  }
1520
1468
  // Broadcast: also publish to broker (if specified and connected)
@@ -1548,8 +1496,12 @@ export class KadiClient {
1548
1496
  * output: z.object({ result: z.number() }),
1549
1497
  * }, async ({ a, b }) => ({ result: a + b }));
1550
1498
  *
1551
- * // Register only on specific broker
1552
- * client.registerTool(def, handler, { brokers: ['internal'] });
1499
+ * // Register on specific brokers with per-broker networks
1500
+ * client.registerTool(def, handler, {
1501
+ * brokers: {
1502
+ * internal: { networks: ['private'] },
1503
+ * },
1504
+ * });
1553
1505
  * ```
1554
1506
  */
1555
1507
  registerTool(definition, handler, options = {}) {
@@ -1569,12 +1521,35 @@ export class KadiClient {
1569
1521
  ? zodToJsonSchema(definition.output)
1570
1522
  : undefined,
1571
1523
  };
1524
+ // Validate per-broker network scoping (skip when no brokers configured)
1525
+ const brokerScopes = options.brokers ?? {};
1526
+ if (Object.keys(brokerScopes).length > 0 && Object.keys(this.config.brokers).length > 0) {
1527
+ for (const [brokerName, scope] of Object.entries(brokerScopes)) {
1528
+ const brokerConfig = this.config.brokers[brokerName];
1529
+ if (!brokerConfig) {
1530
+ throw new KadiError(`Tool "${definition.name}" references broker "${brokerName}" which is not configured. Available brokers: ${JSON.stringify(Object.keys(this.config.brokers))}`, 'INVALID_CONFIG', {
1531
+ toolName: definition.name,
1532
+ broker: brokerName,
1533
+ availableBrokers: Object.keys(this.config.brokers),
1534
+ });
1535
+ }
1536
+ const brokerNetworks = brokerConfig.networks;
1537
+ const invalid = scope.networks.filter((n) => !brokerNetworks.includes(n));
1538
+ if (invalid.length > 0) {
1539
+ throw new KadiError(`Tool "${definition.name}" has networks ${JSON.stringify(invalid)} not present on broker "${brokerName}" networks ${JSON.stringify(brokerNetworks)}. Per-tool networks must be a subset of the broker's networks — a tool can only be visible on networks the agent has joined.`, 'INVALID_CONFIG', {
1540
+ toolName: definition.name,
1541
+ broker: brokerName,
1542
+ invalidNetworks: invalid,
1543
+ brokerNetworks,
1544
+ });
1545
+ }
1546
+ }
1547
+ }
1572
1548
  // Store registration
1573
1549
  const registered = {
1574
1550
  definition: jsonDefinition,
1575
1551
  handler: handler,
1576
- registeredAt: new Date(),
1577
- targetBrokers: options.brokers ?? [],
1552
+ brokerNetworks: brokerScopes,
1578
1553
  };
1579
1554
  this.tools.set(definition.name, registered);
1580
1555
  }
@@ -1582,21 +1557,31 @@ export class KadiClient {
1582
1557
  * Get tool definitions, optionally filtered for a specific broker.
1583
1558
  *
1584
1559
  * @param forBroker - If provided, only return tools targeted for this broker.
1585
- * Tools with empty targetBrokers are included for all brokers.
1560
+ * Tools with empty brokerNetworks are included for all brokers.
1586
1561
  */
1587
1562
  getToolDefinitions(forBroker) {
1588
- return Array.from(this.tools.values())
1589
- .filter((t) => {
1590
- // If no broker specified, return all tools (e.g., for readAgentJson)
1591
- if (!forBroker)
1592
- return true;
1593
- // Empty targetBrokers means "register with all brokers"
1594
- if (t.targetBrokers.length === 0)
1595
- return true;
1596
- // Otherwise, only include if this broker is in the target list
1597
- return t.targetBrokers.includes(forBroker);
1598
- })
1599
- .map((t) => t.definition);
1563
+ const results = [];
1564
+ for (const t of this.tools.values()) {
1565
+ const hasBrokerNetworks = Object.keys(t.brokerNetworks).length > 0;
1566
+ if (!forBroker) {
1567
+ // No broker filter — return all tools (e.g., for readAgentJson)
1568
+ results.push({ ...t.definition });
1569
+ continue;
1570
+ }
1571
+ if (!hasBrokerNetworks) {
1572
+ // Empty brokerNetworks = register with all brokers, no network field
1573
+ results.push({ ...t.definition });
1574
+ continue;
1575
+ }
1576
+ const scope = t.brokerNetworks[forBroker];
1577
+ if (!scope)
1578
+ continue; // Tool not registered on this broker
1579
+ results.push({
1580
+ ...t.definition,
1581
+ ...(scope.networks.length > 0 && { networks: scope.networks }),
1582
+ });
1583
+ }
1584
+ return results;
1600
1585
  }
1601
1586
  /**
1602
1587
  * Execute a tool registered on this client.
@@ -1752,7 +1737,7 @@ export class KadiClient {
1752
1737
  const ability = await loadBrokerTransport(name, {
1753
1738
  broker: state,
1754
1739
  requestTimeout: options.timeout ?? this.config.requestTimeout,
1755
- networks: options.networks,
1740
+ networks: options.networks ?? state.networks,
1756
1741
  // Provide subscribe/unsubscribe for ability.on()/off() support
1757
1742
  subscribe: (pattern, handler) => this.subscribe(pattern, handler, { broker: brokerName }),
1758
1743
  unsubscribe: (pattern, handler) => this.unsubscribe(pattern, handler, { broker: brokerName }),
@@ -1831,13 +1816,9 @@ export class KadiClient {
1831
1816
  };
1832
1817
  // NOW send the request (listener already exists, no race possible)
1833
1818
  try {
1834
- const pendingResult = await this.sendRequest(state, {
1835
- jsonrpc: '2.0',
1836
- id: ++this.nextRequestId,
1837
- method: 'kadi.ability.request',
1838
- // timeout sent so the broker can enforce a matching server-side deadline
1839
- params: { toolName, toolInput: params, requestId, timeout },
1840
- });
1819
+ const pendingResult = await this.sendRequest(state,
1820
+ // timeout sent so the broker can enforce a matching server-side deadline
1821
+ protocol.abilityRequest(++this.nextRequestId, toolName, params, requestId, timeout));
1841
1822
  // Validate the broker accepted the request
1842
1823
  if (pendingResult.status !== 'pending') {
1843
1824
  cleanupPendingInvocation();
@@ -1990,24 +1971,14 @@ export class KadiClient {
1990
1971
  * Send a response via stdio.
1991
1972
  */
1992
1973
  sendStdioResponse(id, result) {
1993
- const response = {
1994
- jsonrpc: '2.0',
1995
- id,
1996
- result,
1997
- };
1998
- const json = JSON.stringify(response);
1974
+ const json = JSON.stringify(protocol.resultResponse(id, result));
1999
1975
  process.stdout.write(`Content-Length: ${Buffer.byteLength(json)}\r\n\r\n${json}`);
2000
1976
  }
2001
1977
  /**
2002
1978
  * Send an error response via stdio.
2003
1979
  */
2004
1980
  sendStdioError(id, error) {
2005
- const response = {
2006
- jsonrpc: '2.0',
2007
- id,
2008
- error,
2009
- };
2010
- const json = JSON.stringify(response);
1981
+ const json = JSON.stringify(protocol.errorResponse(id, error.code, error.message));
2011
1982
  process.stdout.write(`Content-Length: ${Buffer.byteLength(json)}\r\n\r\n${json}`);
2012
1983
  }
2013
1984
  /**