@kadi.build/core 0.0.1-alpha.4 → 0.0.1-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +359 -188
- package/dist/KadiClient.d.ts +153 -16
- package/dist/KadiClient.d.ts.map +1 -1
- package/dist/KadiClient.js +395 -39
- package/dist/KadiClient.js.map +1 -1
- package/dist/loadAbility.d.ts +64 -23
- package/dist/loadAbility.d.ts.map +1 -1
- package/dist/loadAbility.js +81 -40
- package/dist/loadAbility.js.map +1 -1
- package/dist/messages/BrokerMessages.d.ts.map +1 -1
- package/dist/messages/BrokerMessages.js +0 -2
- package/dist/messages/BrokerMessages.js.map +1 -1
- package/dist/transports/BrokerTransport.d.ts +14 -4
- package/dist/transports/BrokerTransport.d.ts.map +1 -1
- package/dist/transports/BrokerTransport.js +61 -29
- package/dist/transports/BrokerTransport.js.map +1 -1
- package/dist/transports/NativeTransport.d.ts +2 -12
- package/dist/transports/NativeTransport.d.ts.map +1 -1
- package/dist/transports/NativeTransport.js +66 -24
- package/dist/transports/NativeTransport.js.map +1 -1
- package/dist/transports/StdioTransport.d.ts.map +1 -1
- package/dist/transports/StdioTransport.js +8 -3
- package/dist/transports/StdioTransport.js.map +1 -1
- package/dist/types/core.d.ts +1 -0
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/core.js.map +1 -1
- package/dist/utils/agentUtils.d.ts +86 -1
- package/dist/utils/agentUtils.d.ts.map +1 -1
- package/dist/utils/agentUtils.js +20 -1
- package/dist/utils/agentUtils.js.map +1 -1
- package/package.json +3 -5
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
## 🎯 Overview
|
|
9
9
|
|
|
10
|
-
`@kadi.build/core` is the foundational library for creating KADI abilities - modular, protocol-agnostic
|
|
10
|
+
`@kadi.build/core` is the foundational library for creating KADI abilities - modular, protocol-agnostic components that can communicate via multiple transport layers. Whether you're building local tools, distributed systems, or high-performance native modules, this toolkit provides a unified, developer-friendly API that abstracts away transport complexity.
|
|
11
11
|
|
|
12
12
|
## 📚 Table of Contents
|
|
13
13
|
|
|
@@ -49,28 +49,27 @@ The library provides a unified way to work with KADI through the **KadiClient**
|
|
|
49
49
|
|
|
50
50
|
- **As an Ability**: Serving tools/methods that others can call
|
|
51
51
|
- **As an Agent**: Calling remote tools via broker protocol
|
|
52
|
-
- **As a Service**: Combining both roles - serving and consuming tools
|
|
53
52
|
|
|
54
|
-
### Creating Your First
|
|
53
|
+
### Creating Your First Ability
|
|
55
54
|
|
|
56
55
|
```javascript
|
|
57
56
|
#!/usr/bin/env node
|
|
58
57
|
import { KadiClient } from '@kadi.build/core';
|
|
59
58
|
|
|
60
|
-
// Create a
|
|
61
|
-
const
|
|
62
|
-
name: 'math-
|
|
63
|
-
role: 'ability', // 'agent', 'ability'
|
|
59
|
+
// Create a ability instance
|
|
60
|
+
const mathAbility = new KadiClient({
|
|
61
|
+
name: 'math-ability',
|
|
62
|
+
role: 'ability', // 'agent', 'ability'
|
|
64
63
|
protocol: 'stdio' // 'native', 'stdio', or 'broker'
|
|
65
64
|
});
|
|
66
65
|
|
|
67
66
|
// Register a tool
|
|
68
|
-
|
|
67
|
+
mathAbility.registerTool('add', async ({ a, b }) => {
|
|
69
68
|
return { result: a + b };
|
|
70
69
|
});
|
|
71
70
|
|
|
72
71
|
// Start serving requests
|
|
73
|
-
|
|
72
|
+
mathAbility.serve().catch(console.error);
|
|
74
73
|
```
|
|
75
74
|
|
|
76
75
|
### Loading and Using Abilities
|
|
@@ -100,7 +99,11 @@ const agent = new KadiClient({
|
|
|
100
99
|
name: 'my-agent',
|
|
101
100
|
role: 'agent',
|
|
102
101
|
protocol: 'broker',
|
|
103
|
-
|
|
102
|
+
brokers: {
|
|
103
|
+
'prod': 'ws://localhost:8080',
|
|
104
|
+
'dev': 'ws://localhost:8081'
|
|
105
|
+
},
|
|
106
|
+
defaultBroker: 'prod',
|
|
104
107
|
networks: ['global']
|
|
105
108
|
});
|
|
106
109
|
|
|
@@ -122,7 +125,7 @@ agent.registerTool(
|
|
|
122
125
|
}
|
|
123
126
|
);
|
|
124
127
|
|
|
125
|
-
// Connect to
|
|
128
|
+
// Connect to brokers and start serving
|
|
126
129
|
await agent.connectToBrokers();
|
|
127
130
|
|
|
128
131
|
// Call tools from OTHER agents connected to the same broker
|
|
@@ -154,7 +157,7 @@ const ability = await loadAbility('my-ability', 'native');
|
|
|
154
157
|
- **Use Case**: Language-agnostic local execution
|
|
155
158
|
- **How it Works**: Spawns abilities as child processes, communicates via LSP-style JSON-RPC over stdin/stdout
|
|
156
159
|
- **Performance**: Minimal overhead, reliable local IPC
|
|
157
|
-
- **Best For**: Python scripts, Go binaries, Node.js
|
|
160
|
+
- **Best For**: Python scripts, Go binaries, Node.js abilities, development/testing
|
|
158
161
|
|
|
159
162
|
```javascript
|
|
160
163
|
// Force stdio protocol
|
|
@@ -170,10 +173,10 @@ KADI_PROTOCOL=stdio # Identifies the protocol being used
|
|
|
170
173
|
|
|
171
174
|
### 3. Broker Protocol (Distributed)
|
|
172
175
|
|
|
173
|
-
- **Use Case**: Distributed
|
|
176
|
+
- **Use Case**: Distributed systems, containerized deployments
|
|
174
177
|
- **How it Works**: Abilities connect to a WebSocket broker for network communication
|
|
175
178
|
- **Performance**: Network overhead, but enables scaling and distribution
|
|
176
|
-
- **Best For**:
|
|
179
|
+
- **Best For**: Distributed abilities, cloud deployments, load-balanced systems
|
|
177
180
|
|
|
178
181
|
```javascript
|
|
179
182
|
// Use broker for distributed execution
|
|
@@ -183,77 +186,67 @@ const ability = await loadAbility('my-ability', 'broker');
|
|
|
183
186
|
**Environment Variables Passed to Child Process:**
|
|
184
187
|
|
|
185
188
|
```bash
|
|
186
|
-
KADI_PROTOCOL=broker
|
|
189
|
+
KADI_PROTOCOL=broker # Identifies the protocol
|
|
187
190
|
KADI_BROKER_URL=ws://localhost:8080 # Broker connection URL
|
|
188
|
-
|
|
191
|
+
KADI_ABILITY_NAME=ability.echo.1_0_0 # Ability identifier
|
|
189
192
|
KADI_AGENT_SCOPE=uuid-here # Agent's scope/namespace
|
|
190
193
|
# Plus all parent process.env variables
|
|
191
194
|
```
|
|
192
195
|
|
|
193
|
-
**
|
|
196
|
+
**Network/Namespace Visibility:**
|
|
194
197
|
|
|
195
|
-
The `KADI_AGENT_SCOPE` environment variable represents the agent's namespace. For the ability to be visible to its parent agent via the broker, it must register with the same
|
|
198
|
+
The `KADI_AGENT_SCOPE` environment variable represents the agent's namespace. For the ability to be visible to its parent agent via the broker, it must register with the same network:
|
|
196
199
|
|
|
197
200
|
```javascript
|
|
198
201
|
// In your ability code:
|
|
199
|
-
const echoAbility = new
|
|
202
|
+
const echoAbility = new KadiClient({
|
|
200
203
|
name: 'echo-js',
|
|
201
204
|
version: '0.0.1',
|
|
202
205
|
description: 'Echo ability with broker support',
|
|
203
|
-
|
|
206
|
+
network: process.env.KADI_AGENT_SCOPE // Use agent's network for visibility
|
|
204
207
|
});
|
|
205
208
|
|
|
206
|
-
// Without this, the ability would only be visible in the 'global'
|
|
209
|
+
// Without this, the ability would only be visible in the 'global' network
|
|
207
210
|
// and the parent agent wouldn't be able to communicate with it
|
|
208
211
|
```
|
|
209
212
|
|
|
210
213
|
### Protocol Selection Strategy
|
|
211
214
|
|
|
212
|
-
When no protocol is specified, `loadAbility`
|
|
215
|
+
When no protocol is specified, `loadAbility` defaults to the 'native' protocol. You must explicitly specify the protocol if you want to use 'stdio' or 'broker'.
|
|
213
216
|
|
|
214
217
|
```mermaid
|
|
215
218
|
graph TD
|
|
216
219
|
A[Load Ability] --> B{Protocol Specified?}
|
|
217
220
|
B -->|Yes| C[Use Specified Protocol]
|
|
218
|
-
B -->|No| D
|
|
219
|
-
D --> E[Use First Interface Listed]
|
|
220
|
-
E --> F[native/stdio/broker]
|
|
221
|
+
B -->|No| D[Default to 'native']
|
|
221
222
|
```
|
|
222
223
|
|
|
223
|
-
|
|
224
|
+
## 🛠️ Creating Abilities with KadiClient
|
|
224
225
|
|
|
225
|
-
|
|
226
|
-
{
|
|
227
|
-
"interfaces": {
|
|
228
|
-
"native": { "entry": "service.js" }, // 1st choice: Fastest
|
|
229
|
-
"stdio": { "discover": true }, // 2nd choice: Balanced
|
|
230
|
-
"broker": { "discover": true } // 3rd choice: Distributed
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
## 🛠️ Creating Services with KadiClient
|
|
236
|
-
|
|
237
|
-
### Basic Service Structure
|
|
226
|
+
### Basic Ability Structure
|
|
238
227
|
|
|
239
228
|
```javascript
|
|
240
229
|
import { KadiClient } from '@kadi.build/core';
|
|
241
230
|
|
|
242
|
-
const
|
|
243
|
-
name: 'echo-
|
|
244
|
-
role: 'ability',
|
|
231
|
+
const ability = new KadiClient({
|
|
232
|
+
name: 'echo-ability',
|
|
233
|
+
role: 'ability', // 'agent', 'ability',
|
|
245
234
|
protocol: 'stdio', // 'native', 'stdio', or 'broker'
|
|
246
|
-
|
|
235
|
+
brokers: {
|
|
236
|
+
'local': 'ws://localhost:8080',
|
|
237
|
+
'prod': 'ws://api.example.com:8080'
|
|
238
|
+
},
|
|
239
|
+
defaultBroker: 'local'
|
|
247
240
|
});
|
|
248
241
|
|
|
249
242
|
// Method 1: Simple tool registration
|
|
250
|
-
|
|
243
|
+
ability.registerTool('echo', async ({ message }) => ({
|
|
251
244
|
echo: message,
|
|
252
245
|
timestamp: new Date().toISOString()
|
|
253
246
|
}));
|
|
254
247
|
|
|
255
248
|
// Method 2: Tool with inline schema
|
|
256
|
-
|
|
249
|
+
ability.registerTool(
|
|
257
250
|
'format',
|
|
258
251
|
async ({ text, style }) => ({
|
|
259
252
|
formatted: style === 'upper' ? text.toUpperCase() : text.toLowerCase()
|
|
@@ -282,7 +275,7 @@ service.registerTool(
|
|
|
282
275
|
);
|
|
283
276
|
|
|
284
277
|
// Start serving
|
|
285
|
-
|
|
278
|
+
ability.serve().catch(console.error);
|
|
286
279
|
```
|
|
287
280
|
|
|
288
281
|
### Event Publishing and Subscription
|
|
@@ -292,45 +285,46 @@ service.serve().catch(console.error);
|
|
|
292
285
|
KadiClient provides a unified event system that works consistently across all transport protocols:
|
|
293
286
|
|
|
294
287
|
```javascript
|
|
295
|
-
// In your
|
|
296
|
-
const
|
|
297
|
-
name: 'my-
|
|
288
|
+
// In your ability (ability.js)
|
|
289
|
+
const ability = new KadiClient({
|
|
290
|
+
name: 'my-ability',
|
|
298
291
|
role: 'ability',
|
|
299
292
|
protocol: 'broker' // Works with all protocols
|
|
300
293
|
});
|
|
301
294
|
|
|
302
295
|
// Publish events from tool handlers
|
|
303
|
-
|
|
296
|
+
ability.registerTool('echo', async ({ message }) => {
|
|
304
297
|
// Publish events during processing
|
|
305
|
-
await
|
|
306
|
-
|
|
298
|
+
await ability.publishEvent('echo.processing', { status: 'started' });
|
|
299
|
+
|
|
307
300
|
// Do work...
|
|
308
301
|
const result = { echo: message, timestamp: new Date().toISOString() };
|
|
309
|
-
|
|
310
|
-
await
|
|
302
|
+
|
|
303
|
+
await ability.publishEvent('echo.completed', { result });
|
|
311
304
|
return result;
|
|
312
305
|
});
|
|
313
306
|
|
|
314
|
-
// In your
|
|
315
|
-
const
|
|
307
|
+
// In your agent (agent.js)
|
|
308
|
+
const agent = new KadiClient({
|
|
316
309
|
name: 'my-client',
|
|
317
310
|
role: 'agent',
|
|
318
311
|
protocol: 'broker'
|
|
319
312
|
});
|
|
320
313
|
|
|
321
314
|
// Subscribe to events using patterns
|
|
322
|
-
|
|
315
|
+
agent.subscribeToEvent('echo.*', (data) => {
|
|
323
316
|
console.log('Echo event received:', data);
|
|
324
317
|
});
|
|
325
318
|
|
|
326
319
|
// For loaded abilities with native/stdio protocols
|
|
327
|
-
const ability = await loadAbility('my-
|
|
320
|
+
const ability = await loadAbility('my-ability', 'stdio');
|
|
328
321
|
ability.events.on('echo.completed', (data) => {
|
|
329
322
|
console.log('Completed:', data);
|
|
330
323
|
});
|
|
331
324
|
```
|
|
332
325
|
|
|
333
|
-
**Event Patterns**:
|
|
326
|
+
**Event Patterns**:
|
|
327
|
+
|
|
334
328
|
- Use wildcards: `math.*` matches `math.operation`, `math.milestone`
|
|
335
329
|
- Protocol-specific delivery:
|
|
336
330
|
- **Native**: Direct EventEmitter (synchronous)
|
|
@@ -339,7 +333,7 @@ ability.events.on('echo.completed', (data) => {
|
|
|
339
333
|
|
|
340
334
|
### Ability Configuration (agent.json)
|
|
341
335
|
|
|
342
|
-
Every ability should have an `agent.json` file that defines its capabilities
|
|
336
|
+
Every ability should have an `agent.json` file that defines its capabilities:
|
|
343
337
|
|
|
344
338
|
```json
|
|
345
339
|
{
|
|
@@ -347,24 +341,10 @@ Every ability should have an `agent.json` file that defines its capabilities and
|
|
|
347
341
|
"kind": "ability",
|
|
348
342
|
"version": "1.0.0",
|
|
349
343
|
"license": "MIT",
|
|
350
|
-
"description": "Echo
|
|
344
|
+
"description": "Echo ability with multiple transport support",
|
|
351
345
|
"scripts": {
|
|
352
346
|
"setup": "npm install",
|
|
353
|
-
"start": "node
|
|
354
|
-
},
|
|
355
|
-
"interfaces": {
|
|
356
|
-
"native": {
|
|
357
|
-
"entry": "service.js"
|
|
358
|
-
},
|
|
359
|
-
"stdio": {
|
|
360
|
-
"discover": true,
|
|
361
|
-
"timeoutMs": 10000
|
|
362
|
-
},
|
|
363
|
-
"broker": {
|
|
364
|
-
"discover": true,
|
|
365
|
-
"timeoutMs": 15000,
|
|
366
|
-
"serviceName": "echo-service"
|
|
367
|
-
}
|
|
347
|
+
"start": "node ability.js"
|
|
368
348
|
},
|
|
369
349
|
"exports": [
|
|
370
350
|
{
|
|
@@ -389,7 +369,8 @@ Every ability should have an `agent.json` file that defines its capabilities and
|
|
|
389
369
|
"brokers": {
|
|
390
370
|
"local": "ws://localhost:8080",
|
|
391
371
|
"remote": "ws://api.example.com:8080"
|
|
392
|
-
}
|
|
372
|
+
},
|
|
373
|
+
"defaultBroker": "local"
|
|
393
374
|
}
|
|
394
375
|
```
|
|
395
376
|
|
|
@@ -398,7 +379,7 @@ Every ability should have an `agent.json` file that defines its capabilities and
|
|
|
398
379
|
The ability system provides two ways to define method schemas:
|
|
399
380
|
|
|
400
381
|
1. **Export Schemas** (defined in `agent.json` exports section)
|
|
401
|
-
2. **Inline Schemas** (passed directly to
|
|
382
|
+
2. **Inline Schemas** (passed directly to `registerTool()`)
|
|
402
383
|
|
|
403
384
|
```javascript
|
|
404
385
|
// Option 1: Define in agent.json exports section
|
|
@@ -416,7 +397,7 @@ The ability system provides two ways to define method schemas:
|
|
|
416
397
|
}
|
|
417
398
|
|
|
418
399
|
// Option 2: Define inline when registering the method
|
|
419
|
-
ability.
|
|
400
|
+
ability.registerTool('process', handler, {
|
|
420
401
|
description: 'Process data',
|
|
421
402
|
inputSchema: { /* ... */ },
|
|
422
403
|
outputSchema: { /* ... */ }
|
|
@@ -441,24 +422,29 @@ const echoAbility = await loadAbility('echo-js', 'stdio');
|
|
|
441
422
|
const result = await echoAbility.echo({ message: 'Hello world' });
|
|
442
423
|
|
|
443
424
|
// Load broker tools directly
|
|
444
|
-
const
|
|
425
|
+
const remoteAbility = await loadAbility('remote-ability', 'broker', {
|
|
445
426
|
brokerUrl: 'ws://localhost:8080',
|
|
446
427
|
networks: ['global']
|
|
447
428
|
});
|
|
448
|
-
const brokerResult = await
|
|
429
|
+
const brokerResult = await remoteAbility.process({ data: 'test' });
|
|
449
430
|
```
|
|
450
431
|
|
|
451
|
-
### Using KadiClient
|
|
432
|
+
### Using KadiClient with Named Brokers
|
|
452
433
|
|
|
453
434
|
```javascript
|
|
454
435
|
import { KadiClient } from '@kadi.build/core';
|
|
455
436
|
|
|
456
|
-
// Create a unified client
|
|
437
|
+
// Create a unified client with named brokers
|
|
457
438
|
const client = new KadiClient({
|
|
458
|
-
name: 'my-
|
|
459
|
-
role: '
|
|
439
|
+
name: 'my-ability',
|
|
440
|
+
role: 'ability', // Provider role
|
|
460
441
|
protocol: 'broker',
|
|
461
|
-
|
|
442
|
+
brokers: {
|
|
443
|
+
'dev': 'ws://localhost:8080',
|
|
444
|
+
'staging': 'ws://staging.example.com:8080',
|
|
445
|
+
'prod': 'ws://prod.example.com:8080'
|
|
446
|
+
},
|
|
447
|
+
defaultBroker: 'dev',
|
|
462
448
|
networks: ['global']
|
|
463
449
|
});
|
|
464
450
|
|
|
@@ -478,7 +464,7 @@ client.registerTool(
|
|
|
478
464
|
}
|
|
479
465
|
);
|
|
480
466
|
|
|
481
|
-
// Connect
|
|
467
|
+
// Connect to all configured brokers
|
|
482
468
|
await client.connectToBrokers();
|
|
483
469
|
|
|
484
470
|
// Call remote tools
|
|
@@ -491,7 +477,7 @@ const result = await client.callTool('translator', 'translate', {
|
|
|
491
477
|
const ability = await client.loadAbility('echo-js');
|
|
492
478
|
```
|
|
493
479
|
|
|
494
|
-
Use `loadAbility()` for simple consumption, or KadiClient for full-featured
|
|
480
|
+
Use `loadAbility()` for simple consumption, or KadiClient for full-featured ability development with tool registration, event publishing, and remote tool invocation.
|
|
495
481
|
|
|
496
482
|
## 🔄 Architecture Deep Dive
|
|
497
483
|
|
|
@@ -506,31 +492,31 @@ sequenceDiagram
|
|
|
506
492
|
participant Transport
|
|
507
493
|
participant FileSystem
|
|
508
494
|
participant ChildProcess
|
|
509
|
-
participant
|
|
495
|
+
participant Ability
|
|
510
496
|
participant Broker
|
|
511
497
|
|
|
512
498
|
Client->>loadAbility: loadAbility('echo-js', protocol?)
|
|
513
499
|
loadAbility->>FileSystem: Read agent.json manifests
|
|
514
500
|
FileSystem-->>loadAbility: Return config & version
|
|
515
|
-
|
|
501
|
+
|
|
516
502
|
alt protocol === 'native'
|
|
517
503
|
loadAbility->>Transport: new NativeTransport(config)
|
|
518
504
|
Transport->>FileSystem: import(entry point)
|
|
519
505
|
FileSystem-->>Transport: Module with KadiClient instance
|
|
520
|
-
Transport->>
|
|
521
|
-
|
|
506
|
+
Transport->>Ability: Extract tool handlers via getToolNames()
|
|
507
|
+
Ability-->>Transport: Return registered tools
|
|
522
508
|
Transport->>Transport: Store handlers in Map
|
|
523
509
|
else protocol === 'stdio'
|
|
524
510
|
loadAbility->>Transport: new StdioTransport(config)
|
|
525
511
|
Transport->>ChildProcess: spawn(startCmd, env: {KADI_PROTOCOL})
|
|
526
|
-
ChildProcess->>
|
|
527
|
-
|
|
528
|
-
Transport->>
|
|
529
|
-
|
|
512
|
+
ChildProcess->>Ability: KadiClient detects stdio mode
|
|
513
|
+
Ability->>Ability: Setup FrameReader/Writer
|
|
514
|
+
Transport->>Ability: Send discovery request
|
|
515
|
+
Ability-->>Transport: Return tool list
|
|
530
516
|
Transport->>Transport: Setup JSON-RPC proxy
|
|
531
517
|
else protocol === 'broker'
|
|
532
518
|
loadAbility->>Transport: new BrokerTransport(config)
|
|
533
|
-
Transport->>Transport:
|
|
519
|
+
Transport->>Transport: Reuse existing KadiClient
|
|
534
520
|
Transport->>Broker: WebSocket connect
|
|
535
521
|
Transport->>Broker: kadi.session.hello
|
|
536
522
|
Broker-->>Transport: { nonce, heartbeatIntervalSec }
|
|
@@ -543,21 +529,21 @@ sequenceDiagram
|
|
|
543
529
|
|
|
544
530
|
loadAbility->>loadAbility: Create proxy with tool methods
|
|
545
531
|
loadAbility-->>Client: Return ability proxy
|
|
546
|
-
|
|
547
|
-
Note over Client,
|
|
532
|
+
|
|
533
|
+
Note over Client,Ability: Tool invocation
|
|
548
534
|
Client->>Transport: ability.echo({ message })
|
|
549
|
-
|
|
535
|
+
|
|
550
536
|
alt native
|
|
551
|
-
Transport->>
|
|
552
|
-
else stdio
|
|
553
|
-
Transport->>
|
|
537
|
+
Transport->>Ability: Direct handler call
|
|
538
|
+
else stdio
|
|
539
|
+
Transport->>Ability: JSON-RPC over frames
|
|
554
540
|
else broker
|
|
555
541
|
Transport->>Broker: kadi.ability.invoke
|
|
556
|
-
Broker->>
|
|
557
|
-
|
|
542
|
+
Broker->>Ability: Route to target
|
|
543
|
+
Ability->>Broker: kadi.ability.result
|
|
558
544
|
Broker-->>Transport: Forward result
|
|
559
545
|
end
|
|
560
|
-
|
|
546
|
+
|
|
561
547
|
Transport-->>Client: Return result
|
|
562
548
|
```
|
|
563
549
|
|
|
@@ -576,16 +562,16 @@ sequenceDiagram
|
|
|
576
562
|
Note over Dev,KC: Tool Registration Phase
|
|
577
563
|
Dev->>KC: new KadiClient({ name, role, protocol })
|
|
578
564
|
KC->>KC: Initialize based on role & protocol
|
|
579
|
-
|
|
565
|
+
|
|
580
566
|
Dev->>KC: registerTool('echo', handler, schema?)
|
|
581
567
|
KC->>KC: Store in toolHandlers Map<name, {handler, schema}>
|
|
582
|
-
|
|
568
|
+
|
|
583
569
|
Dev->>KC: registerTool('process', handler, schema)
|
|
584
570
|
KC->>KC: Add to toolHandlers Map
|
|
585
571
|
|
|
586
572
|
Note over KC,Network: Connection & Serving Phase
|
|
587
573
|
Dev->>KC: serve() or connectToBrokers()
|
|
588
|
-
|
|
574
|
+
|
|
589
575
|
alt protocol === 'native'
|
|
590
576
|
KC->>KC: Tools available via direct reference
|
|
591
577
|
KC->>KC: Emit 'start' event
|
|
@@ -608,7 +594,7 @@ sequenceDiagram
|
|
|
608
594
|
|
|
609
595
|
Note over RemoteClient,KC: Tool Invocation Flow
|
|
610
596
|
RemoteClient->>Network: Request tool invocation
|
|
611
|
-
|
|
597
|
+
|
|
612
598
|
alt native
|
|
613
599
|
RemoteClient->>KC: Direct method call
|
|
614
600
|
KC->>KC: toolHandlers.get(name).handler(params)
|
|
@@ -623,7 +609,7 @@ sequenceDiagram
|
|
|
623
609
|
KC->>KC: toolHandlers.get(toolName).handler(params)
|
|
624
610
|
KC->>Network: kadi.ability.result message
|
|
625
611
|
end
|
|
626
|
-
|
|
612
|
+
|
|
627
613
|
Network-->>RemoteClient: Tool result
|
|
628
614
|
```
|
|
629
615
|
|
|
@@ -633,14 +619,14 @@ KadiClient provides a unified event API that works across all transport protocol
|
|
|
633
619
|
|
|
634
620
|
```mermaid
|
|
635
621
|
sequenceDiagram
|
|
636
|
-
participant Publisher as
|
|
622
|
+
participant Publisher as Ability (Publisher)
|
|
637
623
|
participant Transport as Transport Layer
|
|
638
624
|
participant EventBus as Event Bus
|
|
639
625
|
participant Subscriber as Client (Subscriber)
|
|
640
|
-
|
|
626
|
+
|
|
641
627
|
Note over Subscriber: Setup subscription
|
|
642
628
|
Subscriber->>Subscriber: subscribeToEvent('math.*', callback)
|
|
643
|
-
|
|
629
|
+
|
|
644
630
|
alt protocol === 'broker'
|
|
645
631
|
Subscriber->>EventBus: kadi.event.subscribe({channels: ['math.*']})
|
|
646
632
|
EventBus-->>Subscriber: {subscribed: ['math.*'], queueName}
|
|
@@ -651,10 +637,10 @@ sequenceDiagram
|
|
|
651
637
|
Subscriber->>Publisher: Direct EventEmitter reference
|
|
652
638
|
Publisher->>Subscriber: on('ability:event', dispatcher)
|
|
653
639
|
end
|
|
654
|
-
|
|
640
|
+
|
|
655
641
|
Note over Publisher: Publish event
|
|
656
642
|
Publisher->>Publisher: publishEvent('math.operation', data)
|
|
657
|
-
|
|
643
|
+
|
|
658
644
|
alt protocol === 'native'
|
|
659
645
|
Publisher->>Publisher: emit('ability:event', {eventName, data})
|
|
660
646
|
Publisher-->>Subscriber: Direct EventEmitter delivery
|
|
@@ -665,14 +651,14 @@ sequenceDiagram
|
|
|
665
651
|
Transport-->>Subscriber: Framed event over stdout
|
|
666
652
|
Subscriber->>Transport: FrameReader parses
|
|
667
653
|
Transport->>Subscriber: emit('transport:event', {name, data})
|
|
668
|
-
Subscriber->>Subscriber: Pattern match & invoke callback
|
|
654
|
+
Subscriber->>Subscriber: Pattern match & invoke callback
|
|
669
655
|
else protocol === 'broker'
|
|
670
656
|
Publisher->>EventBus: kadi.event.publish({channel, data})
|
|
671
657
|
EventBus->>EventBus: Route via RabbitMQ
|
|
672
658
|
EventBus-->>Subscriber: kadi.event.delivery
|
|
673
659
|
Subscriber->>Subscriber: Pattern match & invoke callback
|
|
674
660
|
end
|
|
675
|
-
|
|
661
|
+
|
|
676
662
|
Note over Subscriber: Cleanup
|
|
677
663
|
Subscriber->>Subscriber: unsubscribe()
|
|
678
664
|
alt protocol === 'broker'
|
|
@@ -690,8 +676,8 @@ Detailed view of broker-based communication with heartbeat:
|
|
|
690
676
|
sequenceDiagram
|
|
691
677
|
participant Client as KadiClient
|
|
692
678
|
participant Broker as KADI Broker
|
|
693
|
-
participant Target as Target
|
|
694
|
-
|
|
679
|
+
participant Target as Target Ability
|
|
680
|
+
|
|
695
681
|
Note over Client,Broker: Connection & Authentication
|
|
696
682
|
Client->>Broker: WebSocket connect
|
|
697
683
|
Client->>Broker: kadi.session.hello({ role })
|
|
@@ -700,40 +686,40 @@ sequenceDiagram
|
|
|
700
686
|
Client->>Client: Sign nonce
|
|
701
687
|
Client->>Broker: kadi.session.authenticate({ signature })
|
|
702
688
|
Broker-->>Client: { sessionId }
|
|
703
|
-
|
|
689
|
+
|
|
704
690
|
Note over Client,Broker: Heartbeat Management
|
|
705
691
|
Client->>Client: Start heartbeat timer (30s)
|
|
706
692
|
loop Every 30 seconds
|
|
707
693
|
Client->>Broker: kadi.session.ping
|
|
708
694
|
Note over Broker: Reset connection timeout (90s)
|
|
709
695
|
end
|
|
710
|
-
|
|
696
|
+
|
|
711
697
|
Note over Client,Broker: Tool Registration
|
|
712
698
|
Client->>Broker: kadi.agent.register({ tools, networks })
|
|
713
699
|
Broker->>Broker: Store in registry
|
|
714
700
|
Broker-->>Client: { registered: true }
|
|
715
|
-
|
|
701
|
+
|
|
716
702
|
Note over Client,Target: Remote Tool Invocation
|
|
717
|
-
Client->>Client: callTool('
|
|
718
|
-
Client->>Broker: kadi.ability.invoke({
|
|
719
|
-
targetAgent: '
|
|
703
|
+
Client->>Client: callTool('ability-b', 'process', params)
|
|
704
|
+
Client->>Broker: kadi.ability.invoke({
|
|
705
|
+
targetAgent: 'ability-b',
|
|
720
706
|
toolName: 'process',
|
|
721
707
|
toolInput: params
|
|
722
708
|
})
|
|
723
|
-
Broker->>Broker: Lookup
|
|
709
|
+
Broker->>Broker: Lookup ability-b
|
|
724
710
|
Broker->>Target: kadi.ability.invoke
|
|
725
711
|
Target->>Target: Execute tool handler
|
|
726
712
|
Target->>Broker: JSON-RPC response
|
|
727
713
|
Broker-->>Client: Forward response
|
|
728
714
|
Client->>Client: Resolve promise
|
|
729
|
-
|
|
715
|
+
|
|
730
716
|
Note over Client,Broker: Event Publishing
|
|
731
717
|
Client->>Broker: kadi.event.publish({
|
|
732
718
|
channel: 'system.status',
|
|
733
719
|
data: { status: 'healthy' }
|
|
734
720
|
})
|
|
735
721
|
Broker->>Broker: Publish to RabbitMQ exchange
|
|
736
|
-
|
|
722
|
+
|
|
737
723
|
Note over Client,Broker: Graceful Shutdown
|
|
738
724
|
Client->>Client: Stop heartbeat timer
|
|
739
725
|
Client->>Broker: kadi.session.goodbye
|
|
@@ -750,10 +736,10 @@ sequenceDiagram
|
|
|
750
736
|
participant SR as StdioFrameReader
|
|
751
737
|
participant SW as StdioFrameWriter
|
|
752
738
|
participant Server as StdioRpcServer
|
|
753
|
-
participant Ability as
|
|
739
|
+
participant Ability as KadiClient
|
|
754
740
|
|
|
755
741
|
Note over Parent,SR: Incoming Request
|
|
756
|
-
Parent->>SR: Write to stdin: "
|
|
742
|
+
Parent->>SR: Write to stdin: "Content-Length: 52\r\n\r\n{...}"
|
|
757
743
|
SR->>SR: Buffer incoming data
|
|
758
744
|
SR->>SR: Look for Content-Length header
|
|
759
745
|
SR->>SR: Parse header, extract body length
|
|
@@ -770,7 +756,7 @@ sequenceDiagram
|
|
|
770
756
|
Server->>SW: write(response)
|
|
771
757
|
SW->>SW: JSON.stringify(response)
|
|
772
758
|
SW->>SW: Calculate byte length
|
|
773
|
-
SW->>SW: Create header: "
|
|
759
|
+
SW->>SW: Create header: "Content-Length: N\r\n\r\n"
|
|
774
760
|
SW->>Parent: Write header + body to stdout
|
|
775
761
|
|
|
776
762
|
Note over SR: Error Recovery
|
|
@@ -809,14 +795,14 @@ const result = await ability.echo({ message: 'hello' });
|
|
|
809
795
|
const result = await ability.someMethod({ param: 'value' });
|
|
810
796
|
|
|
811
797
|
// Direct RPC call (bypasses method validation)
|
|
812
|
-
const response = await ability.
|
|
798
|
+
const response = await ability.__call('someMethod', { param: 'value' });
|
|
813
799
|
|
|
814
800
|
// Subscribe to events
|
|
815
801
|
ability.events.on('custom:event', (data) => {
|
|
816
802
|
console.log('Event received:', data);
|
|
817
803
|
});
|
|
818
804
|
|
|
819
|
-
// Note: Events work for native and
|
|
805
|
+
// Note: Events work for all protocols (native, stdio, and broker)
|
|
820
806
|
// Subscribe before calling methods that emit events
|
|
821
807
|
```
|
|
822
808
|
|
|
@@ -838,7 +824,7 @@ export KADI_PROTOCOL=stdio
|
|
|
838
824
|
|
|
839
825
|
# Configure broker
|
|
840
826
|
export KADI_BROKER_URL=ws://localhost:8080
|
|
841
|
-
export
|
|
827
|
+
export KADI_ABILITY_NAME=my-ability
|
|
842
828
|
export KADI_AGENT_SCOPE=project-123
|
|
843
829
|
```
|
|
844
830
|
|
|
@@ -849,14 +835,14 @@ When abilities are spawned as child processes (stdio and broker protocols), the
|
|
|
849
835
|
```javascript
|
|
850
836
|
// In your ability code, you can access these variables:
|
|
851
837
|
const protocol = process.env.KADI_PROTOCOL; // 'stdio' or 'broker'
|
|
852
|
-
const brokerUrl = process.env.KADI_BROKER_URL;
|
|
853
|
-
const
|
|
838
|
+
const brokerUrl = process.env.KADI_BROKER_URL;
|
|
839
|
+
const abilityName = process.env.KADI_ABILITY_NAME;
|
|
854
840
|
const agentScope = process.env.KADI_AGENT_SCOPE;
|
|
855
841
|
|
|
856
842
|
// Use them to configure your ability behavior
|
|
857
|
-
const ability = new
|
|
843
|
+
const ability = new KadiClient({
|
|
858
844
|
name: 'my-ability',
|
|
859
|
-
|
|
845
|
+
network: process.env.KADI_AGENT_SCOPE || 'global'
|
|
860
846
|
// Ability automatically uses KADI_PROTOCOL to determine transport
|
|
861
847
|
});
|
|
862
848
|
```
|
|
@@ -870,7 +856,7 @@ my-project/
|
|
|
870
856
|
│ └── echo-ability/
|
|
871
857
|
│ └── 1.0.0/
|
|
872
858
|
│ ├── agent.json # Ability configuration
|
|
873
|
-
│ ├──
|
|
859
|
+
│ ├── ability.js # Ability implementation
|
|
874
860
|
│ └── package.json
|
|
875
861
|
├── modules/ # Source modules
|
|
876
862
|
│ └── echo-ability/ # Development version
|
|
@@ -885,10 +871,10 @@ KadiClient provides a comprehensive event system that works across all protocols
|
|
|
885
871
|
|
|
886
872
|
#### 1. Lifecycle Events
|
|
887
873
|
|
|
888
|
-
Monitor the
|
|
874
|
+
Monitor the ability lifecycle:
|
|
889
875
|
|
|
890
876
|
```javascript
|
|
891
|
-
const client = new KadiClient({ name: 'my-
|
|
877
|
+
const client = new KadiClient({ name: 'my-ability' });
|
|
892
878
|
|
|
893
879
|
// Connection events
|
|
894
880
|
client.on('connected', ({ broker }) => {
|
|
@@ -909,7 +895,7 @@ client.on('tool:completed', ({ toolName, result }) => {
|
|
|
909
895
|
});
|
|
910
896
|
|
|
911
897
|
client.on('error', (error) => {
|
|
912
|
-
console.error('
|
|
898
|
+
console.error('Ability error:', error);
|
|
913
899
|
});
|
|
914
900
|
```
|
|
915
901
|
|
|
@@ -921,7 +907,7 @@ Publish and subscribe to custom events across all transport protocols:
|
|
|
921
907
|
// Publishing events
|
|
922
908
|
const publisher = new KadiClient({
|
|
923
909
|
name: 'event-publisher',
|
|
924
|
-
role: '
|
|
910
|
+
role: 'ability',
|
|
925
911
|
protocol: 'broker' // Works with all protocols
|
|
926
912
|
});
|
|
927
913
|
|
|
@@ -961,12 +947,9 @@ subscriber.onceEvent('process.completed', (data) => {
|
|
|
961
947
|
});
|
|
962
948
|
|
|
963
949
|
// Multiple pattern subscription
|
|
964
|
-
subscriber.subscribeToEvents(
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
console.log(`Event from ${pattern}:`, data);
|
|
968
|
-
}
|
|
969
|
-
);
|
|
950
|
+
subscriber.subscribeToEvents(['process.*', 'system.*'], (pattern, data) => {
|
|
951
|
+
console.log(`Event from ${pattern}:`, data);
|
|
952
|
+
});
|
|
970
953
|
```
|
|
971
954
|
|
|
972
955
|
**Protocol-Specific Behavior:**
|
|
@@ -975,6 +958,69 @@ subscriber.subscribeToEvents(
|
|
|
975
958
|
- **Stdio**: JSON-RPC notifications with LSP framing
|
|
976
959
|
- **Broker**: RabbitMQ pub/sub via WebSocket, persistent queues available
|
|
977
960
|
|
|
961
|
+
### Multi-Broker Configuration
|
|
962
|
+
|
|
963
|
+
KadiClient supports connecting to multiple brokers simultaneously for redundancy and load distribution:
|
|
964
|
+
|
|
965
|
+
```javascript
|
|
966
|
+
const client = new KadiClient({
|
|
967
|
+
name: 'multi-broker-ability',
|
|
968
|
+
role: 'ability',
|
|
969
|
+
protocol: 'broker',
|
|
970
|
+
brokers: {
|
|
971
|
+
'primary': 'ws://broker1.example.com:8080',
|
|
972
|
+
'secondary': 'ws://broker2.example.com:8080',
|
|
973
|
+
'backup': 'ws://broker3.example.com:8080'
|
|
974
|
+
},
|
|
975
|
+
defaultBroker: 'primary', // Used for sending if no specific broker targeted
|
|
976
|
+
networks: ['production']
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
// Connect to all configured brokers
|
|
980
|
+
await client.connectToBrokers();
|
|
981
|
+
|
|
982
|
+
// The client will now:
|
|
983
|
+
// 1. Maintain connections to all three brokers
|
|
984
|
+
// 2. Receive messages from any broker
|
|
985
|
+
// 3. Send messages to the default broker unless specified
|
|
986
|
+
// 4. Automatically handle broker failover
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
### Cross-Language Ability Support
|
|
990
|
+
|
|
991
|
+
KADI now fully supports abilities written in any language through the stdio protocol:
|
|
992
|
+
|
|
993
|
+
```javascript
|
|
994
|
+
// Go ability with agent.json:
|
|
995
|
+
{
|
|
996
|
+
"name": "hash-go",
|
|
997
|
+
"scripts": {
|
|
998
|
+
"setup": "go build -o bin/hash_ability",
|
|
999
|
+
"start": "./bin/hash_ability" // Binary executable
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// Python ability:
|
|
1004
|
+
{
|
|
1005
|
+
"name": "ml-processor",
|
|
1006
|
+
"scripts": {
|
|
1007
|
+
"start": "python3 main.py"
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// Rust ability:
|
|
1012
|
+
{
|
|
1013
|
+
"name": "crypto-rust",
|
|
1014
|
+
"scripts": {
|
|
1015
|
+
"setup": "cargo build --release",
|
|
1016
|
+
"start": "./target/release/crypto_ability"
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// Load and use regardless of implementation language:
|
|
1021
|
+
const hashAbility = await loadAbility('hash-go', 'stdio');
|
|
1022
|
+
const result = await hashAbility.sha256({ data: 'hello' });
|
|
1023
|
+
```
|
|
978
1024
|
|
|
979
1025
|
## 🔧 Development Workflow
|
|
980
1026
|
|
|
@@ -982,7 +1028,7 @@ subscriber.subscribeToEvents(
|
|
|
982
1028
|
|
|
983
1029
|
When developing new features or testing changes to `@kadi.build/core` before publishing to NPM:
|
|
984
1030
|
|
|
985
|
-
0. **
|
|
1031
|
+
0. **Clone the repository**
|
|
986
1032
|
|
|
987
1033
|
```bash
|
|
988
1034
|
git clone https://gitlab.com/humin-game-lab/kadi/kadi-core.git
|
|
@@ -996,14 +1042,14 @@ npm pack
|
|
|
996
1042
|
# This creates: kadi.build-core-X.Y.Z.tgz
|
|
997
1043
|
```
|
|
998
1044
|
|
|
999
|
-
|
|
1045
|
+
2. **Install in your project**:
|
|
1000
1046
|
|
|
1001
1047
|
```bash
|
|
1002
1048
|
cd /path/to/your-project
|
|
1003
1049
|
npm install /path/to/kadi-core/kadi.build-core-X.Y.Z.tgz
|
|
1004
1050
|
```
|
|
1005
1051
|
|
|
1006
|
-
|
|
1052
|
+
3. **For ability development**, update the ability's preflight script:
|
|
1007
1053
|
|
|
1008
1054
|
```json
|
|
1009
1055
|
{
|
|
@@ -1019,29 +1065,38 @@ npm install /path/to/kadi-core/kadi.build-core-X.Y.Z.tgz
|
|
|
1019
1065
|
|
|
1020
1066
|
#### `KadiClient`
|
|
1021
1067
|
|
|
1022
|
-
The unified class for all KADI operations - serving tools, calling remote
|
|
1068
|
+
The unified class for all KADI operations - serving tools, calling remote abilities, and managing events.
|
|
1023
1069
|
|
|
1024
1070
|
```javascript
|
|
1025
1071
|
import { KadiClient } from '@kadi.build/core';
|
|
1026
1072
|
|
|
1027
1073
|
const client = new KadiClient({
|
|
1028
|
-
name: 'my-
|
|
1029
|
-
role: '
|
|
1074
|
+
name: 'my-ability',
|
|
1075
|
+
role: 'ability', // 'agent' or 'ability'
|
|
1030
1076
|
protocol: 'broker', // 'native', 'stdio', or 'broker'
|
|
1031
|
-
|
|
1032
|
-
|
|
1077
|
+
brokers: {
|
|
1078
|
+
'dev': 'ws://localhost:8080',
|
|
1079
|
+
'prod': 'ws://prod.example.com:8080'
|
|
1080
|
+
},
|
|
1081
|
+
defaultBroker: 'dev',
|
|
1082
|
+
network: 'global', // Primary network
|
|
1083
|
+
networks: ['global', 'custom-network'] // All networks
|
|
1033
1084
|
});
|
|
1034
1085
|
```
|
|
1035
1086
|
|
|
1036
1087
|
**Configuration Options:**
|
|
1037
|
-
|
|
1038
|
-
- `
|
|
1088
|
+
|
|
1089
|
+
- `name` - Ability/agent name
|
|
1090
|
+
- `role` - Operating role: 'agent' (consumer) or 'ability' (provider)
|
|
1039
1091
|
- `protocol` - Transport protocol to use
|
|
1040
|
-
- `
|
|
1041
|
-
- `
|
|
1092
|
+
- `brokers` - Named broker configurations (object mapping names to URLs)
|
|
1093
|
+
- `defaultBroker` - Default broker name for sending messages
|
|
1094
|
+
- `network` - Primary network segment for message routing
|
|
1095
|
+
- `networks` - All network namespaces for tool discovery
|
|
1042
1096
|
- `heartbeatIntervalSec` - Custom heartbeat interval (default: from broker)
|
|
1043
1097
|
|
|
1044
1098
|
**Tool Registration Methods:**
|
|
1099
|
+
|
|
1045
1100
|
- `registerTool(name, handler, schema?)` - Register a single tool
|
|
1046
1101
|
- `getTools()` - Get list of registered tool names
|
|
1047
1102
|
- `getToolNames()` - Get filtered list of tool names
|
|
@@ -1050,11 +1105,13 @@ const client = new KadiClient({
|
|
|
1050
1105
|
- `hasTool(name)` - Check if a tool is registered
|
|
1051
1106
|
|
|
1052
1107
|
**Remote Tool Invocation:**
|
|
1108
|
+
|
|
1053
1109
|
- `callTool(targetAgent, toolName, params)` - Call a remote tool via broker
|
|
1054
1110
|
- `discoverRemoteTools(targetAgent)` - List tools from a remote agent
|
|
1055
1111
|
- `loadAbility(name, protocol?, options?)` - Load an ability (compatibility)
|
|
1056
1112
|
|
|
1057
1113
|
**Event System:**
|
|
1114
|
+
|
|
1058
1115
|
- `publishEvent(eventName, data)` - Publish an event
|
|
1059
1116
|
- `subscribeToEvent(pattern, callback)` - Subscribe with wildcards
|
|
1060
1117
|
- `subscribeToEvents(patterns[], callback)` - Multiple subscriptions
|
|
@@ -1062,49 +1119,58 @@ const client = new KadiClient({
|
|
|
1062
1119
|
- `onceEvent(pattern, callback)` - One-time subscription
|
|
1063
1120
|
|
|
1064
1121
|
**Connection Management:**
|
|
1122
|
+
|
|
1065
1123
|
- `serve()` - Start serving (stdio/native modes)
|
|
1066
|
-
- `connectToBrokers(
|
|
1124
|
+
- `connectToBrokers()` - Connect to all configured brokers
|
|
1067
1125
|
- `disconnect()` - Clean disconnect
|
|
1068
1126
|
- `isConnected` - Check connection status
|
|
1069
1127
|
|
|
1070
1128
|
**Properties:**
|
|
1129
|
+
|
|
1071
1130
|
- `agentId` - Unique agent identifier
|
|
1072
|
-
- `name` -
|
|
1131
|
+
- `name` - Ability name
|
|
1073
1132
|
- `role` - Current operating role
|
|
1074
1133
|
- `protocol` - Active protocol
|
|
1134
|
+
- `brokers` - Configured broker map
|
|
1135
|
+
- `defaultBroker` - Default broker name
|
|
1136
|
+
- `currentBroker` - Currently active broker for sending
|
|
1075
1137
|
|
|
1076
1138
|
**Complete Usage Example:**
|
|
1077
1139
|
|
|
1078
1140
|
```javascript
|
|
1079
1141
|
import { KadiClient } from '@kadi.build/core';
|
|
1080
1142
|
|
|
1081
|
-
// Create
|
|
1082
|
-
const
|
|
1083
|
-
name: 'math-
|
|
1084
|
-
role: '
|
|
1143
|
+
// Create an ability that can serve tools
|
|
1144
|
+
const ability = new KadiClient({
|
|
1145
|
+
name: 'math-ability',
|
|
1146
|
+
role: 'ability',
|
|
1085
1147
|
protocol: 'broker',
|
|
1086
|
-
|
|
1148
|
+
brokers: {
|
|
1149
|
+
'local': 'ws://localhost:8080',
|
|
1150
|
+
'cloud': 'wss://api.example.com'
|
|
1151
|
+
},
|
|
1152
|
+
defaultBroker: 'local',
|
|
1087
1153
|
networks: ['global']
|
|
1088
1154
|
});
|
|
1089
1155
|
|
|
1090
1156
|
// Register tools with schemas
|
|
1091
|
-
|
|
1157
|
+
ability.registerTool(
|
|
1092
1158
|
'add',
|
|
1093
1159
|
async ({ a, b }) => {
|
|
1094
1160
|
// Publish event before processing
|
|
1095
|
-
await
|
|
1161
|
+
await ability.publishEvent('math.operation', {
|
|
1096
1162
|
operation: 'add',
|
|
1097
1163
|
inputs: { a, b }
|
|
1098
1164
|
});
|
|
1099
|
-
|
|
1165
|
+
|
|
1100
1166
|
const result = a + b;
|
|
1101
|
-
|
|
1167
|
+
|
|
1102
1168
|
// Publish completion event
|
|
1103
|
-
await
|
|
1169
|
+
await ability.publishEvent('math.completed', {
|
|
1104
1170
|
operation: 'add',
|
|
1105
1171
|
result
|
|
1106
1172
|
});
|
|
1107
|
-
|
|
1173
|
+
|
|
1108
1174
|
return { result };
|
|
1109
1175
|
},
|
|
1110
1176
|
{
|
|
@@ -1126,24 +1192,23 @@ service.registerTool(
|
|
|
1126
1192
|
}
|
|
1127
1193
|
);
|
|
1128
1194
|
|
|
1129
|
-
// Subscribe to events from other
|
|
1130
|
-
|
|
1195
|
+
// Subscribe to events from other abilities
|
|
1196
|
+
ability.subscribeToEvent('system.*', (data) => {
|
|
1131
1197
|
console.log('System event:', data);
|
|
1132
1198
|
});
|
|
1133
1199
|
|
|
1134
|
-
// Connect to
|
|
1135
|
-
await
|
|
1200
|
+
// Connect to brokers
|
|
1201
|
+
await ability.connectToBrokers();
|
|
1136
1202
|
|
|
1137
1203
|
// Call remote tools
|
|
1138
|
-
const result = await
|
|
1139
|
-
'
|
|
1140
|
-
'
|
|
1141
|
-
|
|
1142
|
-
);
|
|
1204
|
+
const result = await ability.callTool('translator-ability', 'translate', {
|
|
1205
|
+
text: 'Hello',
|
|
1206
|
+
to: 'es'
|
|
1207
|
+
});
|
|
1143
1208
|
|
|
1144
1209
|
// Graceful shutdown
|
|
1145
1210
|
process.on('SIGTERM', async () => {
|
|
1146
|
-
await
|
|
1211
|
+
await ability.disconnect();
|
|
1147
1212
|
process.exit(0);
|
|
1148
1213
|
});
|
|
1149
1214
|
```
|
|
@@ -1155,14 +1220,20 @@ Load an ability by name and protocol.
|
|
|
1155
1220
|
```javascript
|
|
1156
1221
|
import { loadAbility } from '@kadi.build/core';
|
|
1157
1222
|
|
|
1158
|
-
const ability = await loadAbility('ability-name', 'protocol'
|
|
1223
|
+
const ability = await loadAbility('ability-name', 'protocol', {
|
|
1224
|
+
brokerUrl: 'ws://localhost:8080', // For broker protocol
|
|
1225
|
+
brokerName: 'dev', // Named broker to use
|
|
1226
|
+
networks: ['global'], // Network segments
|
|
1227
|
+
existingClient: client // Reuse existing KadiClient for broker
|
|
1228
|
+
});
|
|
1159
1229
|
```
|
|
1160
1230
|
|
|
1161
1231
|
**Returns:** A proxy object with:
|
|
1162
1232
|
|
|
1163
1233
|
- Direct method calls (e.g., `ability.echo({ message: 'hello' })`)
|
|
1164
|
-
- `ability.events` - EventEmitter for subscribing to events (
|
|
1234
|
+
- `ability.events` - EventEmitter for subscribing to events (all protocols)
|
|
1165
1235
|
- `ability.__call(method, params)` - Direct RPC call
|
|
1236
|
+
- `ability.__disconnect()` - Clean up resources
|
|
1166
1237
|
|
|
1167
1238
|
### Utility Functions
|
|
1168
1239
|
|
|
@@ -1171,6 +1242,7 @@ import {
|
|
|
1171
1242
|
createLogger,
|
|
1172
1243
|
getProjectJSON,
|
|
1173
1244
|
getAbilityJSON,
|
|
1245
|
+
getAgentJSON,
|
|
1174
1246
|
getBrokerUrl,
|
|
1175
1247
|
runExecCommand
|
|
1176
1248
|
} from '@kadi.build/core';
|
|
@@ -1189,6 +1261,97 @@ Check ability logs:
|
|
|
1189
1261
|
```bash
|
|
1190
1262
|
tail -f abilities/my-ability/1.0.0/my-ability.log
|
|
1191
1263
|
```
|
|
1264
|
+
## 💡 Additional Examples
|
|
1265
|
+
|
|
1266
|
+
### Error Handling Pattern
|
|
1267
|
+
|
|
1268
|
+
```javascript
|
|
1269
|
+
const client = new KadiClient({
|
|
1270
|
+
name: 'robust-ability',
|
|
1271
|
+
role: 'ability'
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
client.registerTool('riskyOperation', async (params) => {
|
|
1275
|
+
try {
|
|
1276
|
+
const result = await performOperation(params);
|
|
1277
|
+
await client.publishEvent('operation.success', { result });
|
|
1278
|
+
return { success: true, result };
|
|
1279
|
+
} catch (error) {
|
|
1280
|
+
await client.publishEvent('operation.error', {
|
|
1281
|
+
error: error.message,
|
|
1282
|
+
params
|
|
1283
|
+
});
|
|
1284
|
+
// Return error in structured format
|
|
1285
|
+
return {
|
|
1286
|
+
success: false,
|
|
1287
|
+
error: error.message
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
});
|
|
1291
|
+
```
|
|
1292
|
+
|
|
1293
|
+
### Health Check Pattern
|
|
1294
|
+
|
|
1295
|
+
```javascript
|
|
1296
|
+
// Register a standard health check tool
|
|
1297
|
+
client.registerTool('health', async () => {
|
|
1298
|
+
const checks = {
|
|
1299
|
+
memory: process.memoryUsage(),
|
|
1300
|
+
uptime: process.uptime(),
|
|
1301
|
+
brokers: client.isConnected ? 'connected' : 'disconnected',
|
|
1302
|
+
timestamp: Date.now()
|
|
1303
|
+
};
|
|
1304
|
+
|
|
1305
|
+
return {
|
|
1306
|
+
status: 'healthy',
|
|
1307
|
+
checks
|
|
1308
|
+
};
|
|
1309
|
+
});
|
|
1310
|
+
```
|
|
1311
|
+
|
|
1312
|
+
## 🐛 Troubleshooting
|
|
1313
|
+
|
|
1314
|
+
### Common Issues and Solutions
|
|
1315
|
+
|
|
1316
|
+
#### Broker Connection Failures
|
|
1317
|
+
|
|
1318
|
+
**Problem**: "Failed to connect to any brokers"
|
|
1319
|
+
|
|
1320
|
+
**Solutions**:
|
|
1321
|
+
- Verify broker URLs are correct
|
|
1322
|
+
- Check network connectivity
|
|
1323
|
+
- Ensure broker is running
|
|
1324
|
+
- Try connecting with lower security (ws:// before wss://)
|
|
1325
|
+
|
|
1326
|
+
#### Events Not Arriving
|
|
1327
|
+
|
|
1328
|
+
**Problem**: Subscribed to events but callbacks not triggered
|
|
1329
|
+
|
|
1330
|
+
**Solutions**:
|
|
1331
|
+
- Subscribe BEFORE triggering events
|
|
1332
|
+
- Check pattern matching (use exact match first)
|
|
1333
|
+
- Verify publisher and subscriber are on same network
|
|
1334
|
+
- Enable debug logging to trace event flow
|
|
1335
|
+
|
|
1336
|
+
#### Cross-Language Abilities Not Loading
|
|
1337
|
+
|
|
1338
|
+
**Problem**: "Cannot find module 'ability.js'" when loading Go/Python abilities
|
|
1339
|
+
|
|
1340
|
+
**Solutions**:
|
|
1341
|
+
- Ensure `scripts.start` field exists in agent.json
|
|
1342
|
+
- Verify the binary/script is executable
|
|
1343
|
+
- Check that setup script has been run
|
|
1344
|
+
- Use stdio protocol, not native
|
|
1345
|
+
|
|
1346
|
+
#### Memory Leaks
|
|
1347
|
+
|
|
1348
|
+
**Problem**: Memory usage grows over time
|
|
1349
|
+
|
|
1350
|
+
**Solutions**:
|
|
1351
|
+
- Always call unsubscribe functions
|
|
1352
|
+
- Use `onceEvent` for single-use subscriptions
|
|
1353
|
+
- Disconnect abilities when done
|
|
1354
|
+
- Clear event listeners on cleanup
|
|
1192
1355
|
|
|
1193
1356
|
## 🤝 Contributing
|
|
1194
1357
|
|
|
@@ -1200,13 +1363,21 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
|
|
|
1200
1363
|
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
1201
1364
|
5. Open a Pull Request
|
|
1202
1365
|
|
|
1366
|
+
## 📄 License
|
|
1203
1367
|
|
|
1368
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
1204
1369
|
|
|
1205
1370
|
## 🔗 Related Projects
|
|
1206
1371
|
|
|
1207
1372
|
- [@kadi.build/cli](https://gitlab.com/humin-game-lab/kadi/kadi) - Command-line interface
|
|
1208
1373
|
- [@kadi.build/broker](https://gitlab.com/humin-game-lab/kadi/kadi-broker) - The KADI broker
|
|
1209
1374
|
|
|
1375
|
+
## 📚 Resources
|
|
1376
|
+
|
|
1377
|
+
- [Event System Deep Dive](docs/event-system.md) - Complete guide to the event architecture
|
|
1378
|
+
- [API Documentation](https://docs.kadi.build) - Full API reference
|
|
1379
|
+
- [Examples Repository](https://github.com/kadi-examples) - More examples and patterns
|
|
1380
|
+
|
|
1210
1381
|
---
|
|
1211
1382
|
|
|
1212
|
-
Built with ❤️ by the KADI team
|
|
1383
|
+
Built with ❤️ by the KADI team
|