@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 +53 -0
- package/dist/client.d.ts +16 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +113 -142
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/protocol.d.ts +49 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +109 -0
- package/dist/protocol.js.map +1 -0
- package/dist/transports/stdio.d.ts.map +1 -1
- package/dist/transports/stdio.js +2 -6
- package/dist/transports/stdio.js.map +1 -1
- package/dist/types.d.ts +61 -23
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +143 -145
- package/src/index.ts +7 -1
- package/src/protocol.ts +161 -0
- package/src/transports/stdio.ts +2 -6
- package/src/types.ts +58 -25
package/src/client.ts
CHANGED
|
@@ -31,6 +31,7 @@ import type {
|
|
|
31
31
|
BrokerState,
|
|
32
32
|
RegisteredTool,
|
|
33
33
|
ToolDefinition,
|
|
34
|
+
BrokerToolDefinition,
|
|
34
35
|
ZodToolDefinition,
|
|
35
36
|
ToolHandler,
|
|
36
37
|
LoadedAbility,
|
|
@@ -52,6 +53,7 @@ import type {
|
|
|
52
53
|
EmitOptions,
|
|
53
54
|
} from './types.js';
|
|
54
55
|
import { KadiError } from './errors.js';
|
|
56
|
+
import * as protocol from './protocol.js';
|
|
55
57
|
import { zodToJsonSchema, isZodSchema } from './zod.js';
|
|
56
58
|
import { resolveAbilityEntry, resolveAbilityScript } from './lockfile.js';
|
|
57
59
|
import { loadNativeTransport } from './transports/native.js';
|
|
@@ -156,8 +158,8 @@ function isMcpCallToolResult(result: unknown): boolean {
|
|
|
156
158
|
* const client = new KadiClient({
|
|
157
159
|
* name: 'my-agent',
|
|
158
160
|
* brokers: {
|
|
159
|
-
* production: 'ws://broker-prod:8080',
|
|
160
|
-
* internal: 'ws://broker-internal:8080',
|
|
161
|
+
* production: { url: 'ws://broker-prod:8080/kadi' },
|
|
162
|
+
* internal: { url: 'ws://broker-internal:8080/kadi', networks: ['private'] },
|
|
161
163
|
* },
|
|
162
164
|
* defaultBroker: 'production',
|
|
163
165
|
* });
|
|
@@ -241,17 +243,24 @@ export class KadiClient {
|
|
|
241
243
|
this._agentId = crypto.createHash('sha256').update(this._publicKeyBase64).digest('hex').substring(0, 16);
|
|
242
244
|
|
|
243
245
|
// Resolve configuration with defaults
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
|
|
246
|
+
const rawBrokers = config.brokers ?? {};
|
|
247
|
+
const normalizedBrokers: Record<string, { url: string; networks: string[] }> = {};
|
|
248
|
+
|
|
249
|
+
for (const [name, entry] of Object.entries(rawBrokers)) {
|
|
250
|
+
normalizedBrokers[name] = {
|
|
251
|
+
url: entry.url,
|
|
252
|
+
networks: entry.networks ?? ['global'],
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const firstBrokerName = Object.keys(normalizedBrokers)[0];
|
|
247
257
|
|
|
248
258
|
this.config = {
|
|
249
259
|
name: config.name,
|
|
250
260
|
version: config.version ?? '1.0.0',
|
|
251
261
|
description: config.description ?? '',
|
|
252
|
-
brokers,
|
|
262
|
+
brokers: normalizedBrokers,
|
|
253
263
|
defaultBroker: config.defaultBroker ?? firstBrokerName,
|
|
254
|
-
networks: config.networks ?? ['global'],
|
|
255
264
|
heartbeatInterval: config.heartbeatInterval ?? DEFAULT_HEARTBEAT_INTERVAL,
|
|
256
265
|
requestTimeout: config.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT,
|
|
257
266
|
autoReconnect: config.autoReconnect ?? DEFAULT_AUTO_RECONNECT,
|
|
@@ -445,8 +454,7 @@ export class KadiClient {
|
|
|
445
454
|
const failures: string[] = [];
|
|
446
455
|
for (const [i, result] of results.entries()) {
|
|
447
456
|
if (result.status === 'rejected') {
|
|
448
|
-
const failedBroker = brokerNames[i]
|
|
449
|
-
if (failedBroker === undefined) continue;
|
|
457
|
+
const failedBroker = brokerNames[i]!;
|
|
450
458
|
const reason = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
451
459
|
console.error(`[KADI] Failed to connect to broker "${failedBroker}": ${reason}`);
|
|
452
460
|
failures.push(failedBroker);
|
|
@@ -485,8 +493,8 @@ export class KadiClient {
|
|
|
485
493
|
* across all broker connections, ensuring consistent identity.
|
|
486
494
|
*/
|
|
487
495
|
private async connectToBroker(brokerName: string): Promise<void> {
|
|
488
|
-
const
|
|
489
|
-
if (!
|
|
496
|
+
const brokerConfig = this.config.brokers[brokerName];
|
|
497
|
+
if (!brokerConfig) {
|
|
490
498
|
throw new KadiError(`Broker "${brokerName}" not found in configuration`, 'UNKNOWN_BROKER', {
|
|
491
499
|
broker: brokerName,
|
|
492
500
|
available: Object.keys(this.config.brokers),
|
|
@@ -494,6 +502,8 @@ export class KadiClient {
|
|
|
494
502
|
});
|
|
495
503
|
}
|
|
496
504
|
|
|
505
|
+
const url = brokerConfig.url;
|
|
506
|
+
|
|
497
507
|
// Check if already connected
|
|
498
508
|
if (this.brokers.has(brokerName)) {
|
|
499
509
|
const existing = this.brokers.get(brokerName)!;
|
|
@@ -506,6 +516,7 @@ export class KadiClient {
|
|
|
506
516
|
const state: BrokerState = {
|
|
507
517
|
name: brokerName,
|
|
508
518
|
url,
|
|
519
|
+
networks: brokerConfig.networks,
|
|
509
520
|
ws: null,
|
|
510
521
|
heartbeatTimer: null,
|
|
511
522
|
pendingRequests: new Map(),
|
|
@@ -530,12 +541,8 @@ export class KadiClient {
|
|
|
530
541
|
// Register with broker (transitions to "ready" state)
|
|
531
542
|
await this.registerWithBroker(state);
|
|
532
543
|
|
|
533
|
-
//
|
|
534
|
-
|
|
535
|
-
this.sendHeartbeat(state);
|
|
536
|
-
}, this.config.heartbeatInterval);
|
|
537
|
-
|
|
538
|
-
state.status = 'connected';
|
|
544
|
+
// Finalize connection (heartbeat + status)
|
|
545
|
+
this.finalizeConnection(state);
|
|
539
546
|
}
|
|
540
547
|
|
|
541
548
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -543,12 +550,7 @@ export class KadiClient {
|
|
|
543
550
|
// ─────────────────────────────────────────────────────────────
|
|
544
551
|
|
|
545
552
|
private buildHelloMessage(): JsonRpcRequest {
|
|
546
|
-
return
|
|
547
|
-
jsonrpc: '2.0',
|
|
548
|
-
id: this.nextRequestId++,
|
|
549
|
-
method: 'kadi.session.hello',
|
|
550
|
-
params: { role: 'agent' },
|
|
551
|
-
};
|
|
553
|
+
return protocol.hello(this.nextRequestId++);
|
|
552
554
|
}
|
|
553
555
|
|
|
554
556
|
private buildAuthMessage(nonce: string): JsonRpcRequest {
|
|
@@ -560,38 +562,20 @@ export class KadiClient {
|
|
|
560
562
|
});
|
|
561
563
|
const signature = crypto.sign(null, Buffer.from(nonce, 'utf8'), privateKey);
|
|
562
564
|
|
|
563
|
-
return
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
signature: signature.toString('base64'),
|
|
570
|
-
nonce,
|
|
571
|
-
},
|
|
572
|
-
};
|
|
565
|
+
return protocol.authenticate(
|
|
566
|
+
this.nextRequestId++,
|
|
567
|
+
this._publicKeyBase64,
|
|
568
|
+
signature.toString('base64'),
|
|
569
|
+
nonce,
|
|
570
|
+
);
|
|
573
571
|
}
|
|
574
572
|
|
|
575
|
-
private buildRegisterMessage(tools:
|
|
576
|
-
return
|
|
577
|
-
jsonrpc: '2.0',
|
|
578
|
-
id: this.nextRequestId++,
|
|
579
|
-
method: 'kadi.agent.register',
|
|
580
|
-
params: {
|
|
581
|
-
tools,
|
|
582
|
-
networks,
|
|
583
|
-
displayName: this.config.name,
|
|
584
|
-
},
|
|
585
|
-
};
|
|
573
|
+
private buildRegisterMessage(tools: BrokerToolDefinition[], networks: string[]): JsonRpcRequest {
|
|
574
|
+
return protocol.register(this.nextRequestId++, tools, networks, this.config.name);
|
|
586
575
|
}
|
|
587
576
|
|
|
588
577
|
private buildHeartbeatMessage(): JsonRpcRequest {
|
|
589
|
-
return
|
|
590
|
-
jsonrpc: '2.0',
|
|
591
|
-
id: this.nextRequestId++,
|
|
592
|
-
method: 'kadi.session.heartbeat',
|
|
593
|
-
params: { timestamp: Date.now() },
|
|
594
|
-
};
|
|
578
|
+
return protocol.heartbeat(this.nextRequestId++);
|
|
595
579
|
}
|
|
596
580
|
|
|
597
581
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -718,10 +702,17 @@ export class KadiClient {
|
|
|
718
702
|
// Note: If registration fails, sendRequest will reject with the error.
|
|
719
703
|
// We don't need to check response.error here - errors are already
|
|
720
704
|
// handled via Promise rejection in handleBrokerResponse.
|
|
721
|
-
await this.sendRequest<{ status: string }>(
|
|
705
|
+
const { droppedNetworks } = await this.sendRequest<{ status: string; droppedNetworks?: string[] }>(
|
|
722
706
|
state,
|
|
723
|
-
this.buildRegisterMessage(tools,
|
|
707
|
+
this.buildRegisterMessage(tools, state.networks)
|
|
724
708
|
);
|
|
709
|
+
|
|
710
|
+
if (droppedNetworks && droppedNetworks.length > 0) {
|
|
711
|
+
console.warn(
|
|
712
|
+
`[KADI] Broker "${state.name}" dropped networks ${JSON.stringify(droppedNetworks)} ` +
|
|
713
|
+
`during registration. Tools scoped to those networks will not be discoverable.`
|
|
714
|
+
);
|
|
715
|
+
}
|
|
725
716
|
}
|
|
726
717
|
|
|
727
718
|
/**
|
|
@@ -1037,12 +1028,7 @@ export class KadiClient {
|
|
|
1037
1028
|
|
|
1038
1029
|
// If this is the first handler for this pattern, subscribe on broker
|
|
1039
1030
|
if (!state.subscribedPatterns.has(pattern)) {
|
|
1040
|
-
await this.sendRequest(state,
|
|
1041
|
-
jsonrpc: '2.0',
|
|
1042
|
-
id: this.nextRequestId++,
|
|
1043
|
-
method: 'kadi.event.subscribe',
|
|
1044
|
-
params: { pattern },
|
|
1045
|
-
});
|
|
1031
|
+
await this.sendRequest(state, protocol.eventSubscribe(this.nextRequestId++, pattern));
|
|
1046
1032
|
state.subscribedPatterns.add(pattern);
|
|
1047
1033
|
}
|
|
1048
1034
|
}
|
|
@@ -1097,12 +1083,7 @@ export class KadiClient {
|
|
|
1097
1083
|
// Unsubscribe from broker if we were subscribed
|
|
1098
1084
|
if (state.subscribedPatterns.has(pattern) && state.status === 'connected') {
|
|
1099
1085
|
try {
|
|
1100
|
-
await this.sendRequest(state,
|
|
1101
|
-
jsonrpc: '2.0',
|
|
1102
|
-
id: this.nextRequestId++,
|
|
1103
|
-
method: 'kadi.event.unsubscribe',
|
|
1104
|
-
params: { pattern },
|
|
1105
|
-
});
|
|
1086
|
+
await this.sendRequest(state, protocol.eventUnsubscribe(this.nextRequestId++, pattern));
|
|
1106
1087
|
} catch {
|
|
1107
1088
|
// Ignore errors during unsubscribe (broker might be disconnecting)
|
|
1108
1089
|
}
|
|
@@ -1134,20 +1115,11 @@ export class KadiClient {
|
|
|
1134
1115
|
async publish(channel: string, data: unknown, options: PublishOptions = {}): Promise<void> {
|
|
1135
1116
|
const { state } = this.getConnectedBrokerState(options.broker);
|
|
1136
1117
|
|
|
1137
|
-
// Resolve which network to publish to
|
|
1138
|
-
const networkId = options.network ??
|
|
1118
|
+
// Resolve which network to publish to (prefer broker-specific, fallback to global)
|
|
1119
|
+
const networkId = options.network ?? state.networks[0] ?? '';
|
|
1139
1120
|
|
|
1140
1121
|
// Send publish request to broker
|
|
1141
|
-
await this.sendRequest(state,
|
|
1142
|
-
jsonrpc: '2.0',
|
|
1143
|
-
id: this.nextRequestId++,
|
|
1144
|
-
method: 'kadi.event.publish',
|
|
1145
|
-
params: {
|
|
1146
|
-
channel,
|
|
1147
|
-
data,
|
|
1148
|
-
networkId,
|
|
1149
|
-
},
|
|
1150
|
-
});
|
|
1122
|
+
await this.sendRequest(state, protocol.eventPublish(this.nextRequestId++, channel, data, networkId));
|
|
1151
1123
|
}
|
|
1152
1124
|
|
|
1153
1125
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -1463,34 +1435,17 @@ export class KadiClient {
|
|
|
1463
1435
|
responseResult = result;
|
|
1464
1436
|
}
|
|
1465
1437
|
|
|
1466
|
-
|
|
1467
|
-
jsonrpc: '2.0',
|
|
1468
|
-
id: requestId,
|
|
1469
|
-
result: responseResult,
|
|
1470
|
-
};
|
|
1471
|
-
state.ws?.send(JSON.stringify(response));
|
|
1438
|
+
state.ws?.send(JSON.stringify(protocol.resultResponse(requestId, responseResult)));
|
|
1472
1439
|
} catch (error) {
|
|
1473
1440
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1474
1441
|
|
|
1475
1442
|
// For MCP clients, wrap errors in CallToolResult format too
|
|
1476
1443
|
if (context.callerProtocol === 'mcp') {
|
|
1477
|
-
const
|
|
1478
|
-
|
|
1479
|
-
id: requestId,
|
|
1480
|
-
result: { content: [{ type: 'text', text: errorMessage }], isError: true },
|
|
1481
|
-
};
|
|
1482
|
-
state.ws?.send(JSON.stringify(response));
|
|
1444
|
+
const mcpResult = { content: [{ type: 'text', text: errorMessage }], isError: true };
|
|
1445
|
+
state.ws?.send(JSON.stringify(protocol.resultResponse(requestId, mcpResult)));
|
|
1483
1446
|
} else {
|
|
1484
1447
|
// KADI clients receive JSON-RPC error
|
|
1485
|
-
|
|
1486
|
-
jsonrpc: '2.0',
|
|
1487
|
-
id: requestId,
|
|
1488
|
-
error: {
|
|
1489
|
-
code: -32000,
|
|
1490
|
-
message: errorMessage,
|
|
1491
|
-
},
|
|
1492
|
-
};
|
|
1493
|
-
state.ws?.send(JSON.stringify(response));
|
|
1448
|
+
state.ws?.send(JSON.stringify(protocol.errorResponse(requestId, -32000, errorMessage)));
|
|
1494
1449
|
}
|
|
1495
1450
|
}
|
|
1496
1451
|
}
|
|
@@ -1520,6 +1475,17 @@ export class KadiClient {
|
|
|
1520
1475
|
}
|
|
1521
1476
|
}
|
|
1522
1477
|
|
|
1478
|
+
/**
|
|
1479
|
+
* Finalize a broker connection: start heartbeat and mark as connected.
|
|
1480
|
+
* Shared by initial connect and reconnect paths.
|
|
1481
|
+
*/
|
|
1482
|
+
private finalizeConnection(state: BrokerState): void {
|
|
1483
|
+
state.heartbeatTimer = setInterval(() => {
|
|
1484
|
+
this.sendHeartbeat(state);
|
|
1485
|
+
}, this.config.heartbeatInterval);
|
|
1486
|
+
state.status = 'connected';
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1523
1489
|
/**
|
|
1524
1490
|
* Send heartbeat to keep connection alive.
|
|
1525
1491
|
*/
|
|
@@ -1565,6 +1531,10 @@ export class KadiClient {
|
|
|
1565
1531
|
);
|
|
1566
1532
|
}
|
|
1567
1533
|
state.pendingInvocations.clear();
|
|
1534
|
+
|
|
1535
|
+
// Clear broker-side subscription tracking — subscriptions are lost when
|
|
1536
|
+
// the connection drops and must be re-established after reconnection.
|
|
1537
|
+
state.subscribedPatterns.clear();
|
|
1568
1538
|
}
|
|
1569
1539
|
|
|
1570
1540
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -1701,15 +1671,12 @@ export class KadiClient {
|
|
|
1701
1671
|
// Re-register tools
|
|
1702
1672
|
await this.registerWithBroker(state);
|
|
1703
1673
|
|
|
1704
|
-
//
|
|
1705
|
-
|
|
1706
|
-
this.sendHeartbeat(state);
|
|
1707
|
-
}, this.config.heartbeatInterval);
|
|
1674
|
+
// Finalize connection (heartbeat + status)
|
|
1675
|
+
this.finalizeConnection(state);
|
|
1708
1676
|
|
|
1709
1677
|
// Success!
|
|
1710
1678
|
console.error(`[KADI] Reconnected to broker "${state.name}" after ${state.reconnectAttempts} attempts`);
|
|
1711
1679
|
state.reconnectAttempts = 0;
|
|
1712
|
-
state.status = 'connected';
|
|
1713
1680
|
} catch (error) {
|
|
1714
1681
|
// Log the error and try again
|
|
1715
1682
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1819,12 +1786,7 @@ export class KadiClient {
|
|
|
1819
1786
|
this.eventHandler(event, data);
|
|
1820
1787
|
} else if (this.isServingStdio) {
|
|
1821
1788
|
// Stdio transport: write notification to stdout
|
|
1822
|
-
const
|
|
1823
|
-
jsonrpc: '2.0',
|
|
1824
|
-
method: 'event',
|
|
1825
|
-
params: { name: event, data },
|
|
1826
|
-
};
|
|
1827
|
-
const json = JSON.stringify(notification);
|
|
1789
|
+
const json = JSON.stringify(protocol.eventNotification(event, data));
|
|
1828
1790
|
process.stdout.write(`Content-Length: ${Buffer.byteLength(json)}\r\n\r\n${json}`);
|
|
1829
1791
|
}
|
|
1830
1792
|
|
|
@@ -1861,8 +1823,12 @@ export class KadiClient {
|
|
|
1861
1823
|
* output: z.object({ result: z.number() }),
|
|
1862
1824
|
* }, async ({ a, b }) => ({ result: a + b }));
|
|
1863
1825
|
*
|
|
1864
|
-
* // Register
|
|
1865
|
-
* client.registerTool(def, handler, {
|
|
1826
|
+
* // Register on specific brokers with per-broker networks
|
|
1827
|
+
* client.registerTool(def, handler, {
|
|
1828
|
+
* brokers: {
|
|
1829
|
+
* internal: { networks: ['private'] },
|
|
1830
|
+
* },
|
|
1831
|
+
* });
|
|
1866
1832
|
* ```
|
|
1867
1833
|
*/
|
|
1868
1834
|
registerTool<TInput, TOutput>(
|
|
@@ -1888,12 +1854,44 @@ export class KadiClient {
|
|
|
1888
1854
|
: undefined,
|
|
1889
1855
|
};
|
|
1890
1856
|
|
|
1857
|
+
// Validate per-broker network scoping (skip when no brokers configured)
|
|
1858
|
+
const brokerScopes = options.brokers ?? {};
|
|
1859
|
+
if (Object.keys(brokerScopes).length > 0 && Object.keys(this.config.brokers).length > 0) {
|
|
1860
|
+
for (const [brokerName, scope] of Object.entries(brokerScopes)) {
|
|
1861
|
+
const brokerConfig = this.config.brokers[brokerName];
|
|
1862
|
+
if (!brokerConfig) {
|
|
1863
|
+
throw new KadiError(
|
|
1864
|
+
`Tool "${definition.name}" references broker "${brokerName}" which is not configured. Available brokers: ${JSON.stringify(Object.keys(this.config.brokers))}`,
|
|
1865
|
+
'INVALID_CONFIG',
|
|
1866
|
+
{
|
|
1867
|
+
toolName: definition.name,
|
|
1868
|
+
broker: brokerName,
|
|
1869
|
+
availableBrokers: Object.keys(this.config.brokers),
|
|
1870
|
+
}
|
|
1871
|
+
);
|
|
1872
|
+
}
|
|
1873
|
+
const brokerNetworks = brokerConfig.networks;
|
|
1874
|
+
const invalid = scope.networks.filter((n) => !brokerNetworks.includes(n));
|
|
1875
|
+
if (invalid.length > 0) {
|
|
1876
|
+
throw new KadiError(
|
|
1877
|
+
`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.`,
|
|
1878
|
+
'INVALID_CONFIG',
|
|
1879
|
+
{
|
|
1880
|
+
toolName: definition.name,
|
|
1881
|
+
broker: brokerName,
|
|
1882
|
+
invalidNetworks: invalid,
|
|
1883
|
+
brokerNetworks,
|
|
1884
|
+
}
|
|
1885
|
+
);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1891
1890
|
// Store registration
|
|
1892
1891
|
const registered: RegisteredTool = {
|
|
1893
1892
|
definition: jsonDefinition,
|
|
1894
1893
|
handler: handler as ToolHandler,
|
|
1895
|
-
|
|
1896
|
-
targetBrokers: options.brokers ?? [],
|
|
1894
|
+
brokerNetworks: brokerScopes,
|
|
1897
1895
|
};
|
|
1898
1896
|
this.tools.set(definition.name, registered);
|
|
1899
1897
|
}
|
|
@@ -1902,21 +1900,36 @@ export class KadiClient {
|
|
|
1902
1900
|
* Get tool definitions, optionally filtered for a specific broker.
|
|
1903
1901
|
*
|
|
1904
1902
|
* @param forBroker - If provided, only return tools targeted for this broker.
|
|
1905
|
-
* Tools with empty
|
|
1903
|
+
* Tools with empty brokerNetworks are included for all brokers.
|
|
1906
1904
|
*/
|
|
1907
|
-
private getToolDefinitions(forBroker?: string):
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1905
|
+
private getToolDefinitions(forBroker?: string): BrokerToolDefinition[] {
|
|
1906
|
+
const results: BrokerToolDefinition[] = [];
|
|
1907
|
+
|
|
1908
|
+
for (const t of this.tools.values()) {
|
|
1909
|
+
const hasBrokerNetworks = Object.keys(t.brokerNetworks).length > 0;
|
|
1910
|
+
|
|
1911
|
+
if (!forBroker) {
|
|
1912
|
+
// No broker filter — return all tools (e.g., for readAgentJson)
|
|
1913
|
+
results.push({ ...t.definition });
|
|
1914
|
+
continue;
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
if (!hasBrokerNetworks) {
|
|
1918
|
+
// Empty brokerNetworks = register with all brokers, no network field
|
|
1919
|
+
results.push({ ...t.definition });
|
|
1920
|
+
continue;
|
|
1921
|
+
}
|
|
1912
1922
|
|
|
1913
|
-
|
|
1914
|
-
|
|
1923
|
+
const scope = t.brokerNetworks[forBroker];
|
|
1924
|
+
if (!scope) continue; // Tool not registered on this broker
|
|
1915
1925
|
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1926
|
+
results.push({
|
|
1927
|
+
...t.definition,
|
|
1928
|
+
...(scope.networks.length > 0 && { networks: scope.networks }),
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
return results;
|
|
1920
1933
|
}
|
|
1921
1934
|
|
|
1922
1935
|
/**
|
|
@@ -2090,7 +2103,7 @@ export class KadiClient {
|
|
|
2090
2103
|
const ability = await loadBrokerTransport(name, {
|
|
2091
2104
|
broker: state,
|
|
2092
2105
|
requestTimeout: options.timeout ?? this.config.requestTimeout,
|
|
2093
|
-
networks: options.networks,
|
|
2106
|
+
networks: options.networks ?? state.networks,
|
|
2094
2107
|
// Provide subscribe/unsubscribe for ability.on()/off() support
|
|
2095
2108
|
subscribe: (pattern, handler) => this.subscribe(pattern, handler, { broker: brokerName }),
|
|
2096
2109
|
unsubscribe: (pattern, handler) => this.unsubscribe(pattern, handler, { broker: brokerName }),
|
|
@@ -2184,13 +2197,8 @@ export class KadiClient {
|
|
|
2184
2197
|
try {
|
|
2185
2198
|
const pendingResult = await this.sendRequest<{ status: string; requestId: string }>(
|
|
2186
2199
|
state,
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
id: ++this.nextRequestId,
|
|
2190
|
-
method: 'kadi.ability.request',
|
|
2191
|
-
// timeout sent so the broker can enforce a matching server-side deadline
|
|
2192
|
-
params: { toolName, toolInput: params, requestId, timeout },
|
|
2193
|
-
}
|
|
2200
|
+
// timeout sent so the broker can enforce a matching server-side deadline
|
|
2201
|
+
protocol.abilityRequest(++this.nextRequestId, toolName, params, requestId, timeout)
|
|
2194
2202
|
);
|
|
2195
2203
|
|
|
2196
2204
|
// Validate the broker accepted the request
|
|
@@ -2365,12 +2373,7 @@ export class KadiClient {
|
|
|
2365
2373
|
* Send a response via stdio.
|
|
2366
2374
|
*/
|
|
2367
2375
|
private sendStdioResponse(id: string | number, result: unknown): void {
|
|
2368
|
-
const
|
|
2369
|
-
jsonrpc: '2.0',
|
|
2370
|
-
id,
|
|
2371
|
-
result,
|
|
2372
|
-
};
|
|
2373
|
-
const json = JSON.stringify(response);
|
|
2376
|
+
const json = JSON.stringify(protocol.resultResponse(id, result));
|
|
2374
2377
|
process.stdout.write(`Content-Length: ${Buffer.byteLength(json)}\r\n\r\n${json}`);
|
|
2375
2378
|
}
|
|
2376
2379
|
|
|
@@ -2378,12 +2381,7 @@ export class KadiClient {
|
|
|
2378
2381
|
* Send an error response via stdio.
|
|
2379
2382
|
*/
|
|
2380
2383
|
private sendStdioError(id: string | number, error: { code: number; message: string }): void {
|
|
2381
|
-
const
|
|
2382
|
-
jsonrpc: '2.0',
|
|
2383
|
-
id,
|
|
2384
|
-
error,
|
|
2385
|
-
};
|
|
2386
|
-
const json = JSON.stringify(response);
|
|
2384
|
+
const json = JSON.stringify(protocol.errorResponse(id, error.code, error.message));
|
|
2387
2385
|
process.stdout.write(`Content-Length: ${Buffer.byteLength(json)}\r\n\r\n${json}`);
|
|
2388
2386
|
}
|
|
2389
2387
|
|
|
@@ -2418,7 +2416,7 @@ export class KadiClient {
|
|
|
2418
2416
|
/**
|
|
2419
2417
|
* Get agent information (for readAgentJson protocol).
|
|
2420
2418
|
*/
|
|
2421
|
-
readAgentJson(): { name: string; version: string; tools:
|
|
2419
|
+
readAgentJson(): { name: string; version: string; tools: BrokerToolDefinition[] } {
|
|
2422
2420
|
return {
|
|
2423
2421
|
name: this.config.name,
|
|
2424
2422
|
version: this.config.version,
|
package/src/index.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* const client = new KadiClient({
|
|
11
11
|
* name: 'my-agent',
|
|
12
12
|
* brokers: {
|
|
13
|
-
* production: 'ws://broker:8080',
|
|
13
|
+
* production: { url: 'ws://broker:8080/kadi' },
|
|
14
14
|
* },
|
|
15
15
|
* defaultBroker: 'production',
|
|
16
16
|
* });
|
|
@@ -39,10 +39,12 @@ export type { ErrorCode, ErrorContext } from './errors.js';
|
|
|
39
39
|
export type {
|
|
40
40
|
// Configuration
|
|
41
41
|
ClientConfig,
|
|
42
|
+
BrokerEntry,
|
|
42
43
|
ResolvedConfig,
|
|
43
44
|
|
|
44
45
|
// Tools
|
|
45
46
|
ToolDefinition,
|
|
47
|
+
BrokerToolDefinition,
|
|
46
48
|
ZodToolDefinition,
|
|
47
49
|
ToolHandler,
|
|
48
50
|
RegisteredTool,
|
|
@@ -67,6 +69,7 @@ export type {
|
|
|
67
69
|
BrokerState,
|
|
68
70
|
BrokerStatus,
|
|
69
71
|
PendingRequest,
|
|
72
|
+
PendingInvocation,
|
|
70
73
|
|
|
71
74
|
// Broker Events (Pub/Sub)
|
|
72
75
|
BrokerEvent,
|
|
@@ -109,6 +112,9 @@ export { loadStdioTransport } from './transports/stdio.js';
|
|
|
109
112
|
export { loadBrokerTransport } from './transports/broker.js';
|
|
110
113
|
export type { BrokerTransportOptions } from './transports/broker.js';
|
|
111
114
|
|
|
115
|
+
// Protocol message builders
|
|
116
|
+
export * as protocol from './protocol.js';
|
|
117
|
+
|
|
112
118
|
// Crypto utilities (Ed25519 to X25519 conversion)
|
|
113
119
|
export { convertToEncryptionKey, convertToEncryptionKeyPair } from './crypto.js';
|
|
114
120
|
export type { EncryptionKeyPair } from './crypto.js';
|