@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 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 services that can communicate via multiple transport layers. Whether you're building local tools, distributed microservices, or high-performance native modules, this toolkit provides a unified, developer-friendly API that abstracts away transport complexity.
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 Service
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 service instance
61
- const mathService = new KadiClient({
62
- name: 'math-service',
63
- role: 'ability', // 'agent', 'ability', or 'service'
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
- mathService.registerTool('add', async ({ a, b }) => {
67
+ mathAbility.registerTool('add', async ({ a, b }) => {
69
68
  return { result: a + b };
70
69
  });
71
70
 
72
71
  // Start serving requests
73
- mathService.serve().catch(console.error);
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
- brokerUrls: ['ws://localhost:8080'],
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 broker and start serving
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 services, development/testing
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 services, containerized deployments
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**: Microservices, cloud deployments, load-balanced services
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 # Identifies the protocol
189
+ KADI_PROTOCOL=broker # Identifies the protocol
187
190
  KADI_BROKER_URL=ws://localhost:8080 # Broker connection URL
188
- KADI_SERVICE_NAME=ability.echo.1_0_0 # Service identifier
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
- **Scope/Namespace Visibility:**
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 scope:
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 KadiAbility({
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
- scope: process.env.KADI_AGENT_SCOPE // Use agent's scope for visibility
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' scope
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` uses the **first interface** defined in the ability's `agent.json`. The KADI team recommends ordering interfaces from fastest to slowest (native → stdio broker) for optimal default performance.
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{Check agent.json interfaces}
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
- **Recommended Interface Order in agent.json:**
224
+ ## 🛠️ Creating Abilities with KadiClient
224
225
 
225
- ```json
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 service = new KadiClient({
243
- name: 'echo-service',
244
- role: 'ability', // 'agent', 'ability', or 'service'
231
+ const ability = new KadiClient({
232
+ name: 'echo-ability',
233
+ role: 'ability', // 'agent', 'ability',
245
234
  protocol: 'stdio', // 'native', 'stdio', or 'broker'
246
- brokerUrls: ['ws://localhost:8080']
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
- service.registerTool('echo', async ({ message }) => ({
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
- service.registerTool(
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
- service.serve().catch(console.error);
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 service (service.js)
296
- const service = new KadiClient({
297
- name: 'my-service',
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
- service.registerTool('echo', async ({ message }) => {
296
+ ability.registerTool('echo', async ({ message }) => {
304
297
  // Publish events during processing
305
- await service.publishEvent('echo.processing', { status: 'started' });
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 service.publishEvent('echo.completed', { result });
302
+
303
+ await ability.publishEvent('echo.completed', { result });
311
304
  return result;
312
305
  });
313
306
 
314
- // In your client (index.js)
315
- const client = new KadiClient({
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
- client.subscribeToEvent('echo.*', (data) => {
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-service', 'stdio');
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 and interfaces:
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 service with multiple transport support",
344
+ "description": "Echo ability with multiple transport support",
351
345
  "scripts": {
352
346
  "setup": "npm install",
353
- "start": "node service.js"
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 `.method()`)
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.method('process', handler, {
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 remoteService = await loadAbility('remote-service', 'broker', {
425
+ const remoteAbility = await loadAbility('remote-ability', 'broker', {
445
426
  brokerUrl: 'ws://localhost:8080',
446
427
  networks: ['global']
447
428
  });
448
- const brokerResult = await remoteService.process({ data: 'test' });
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-service',
459
- role: 'service', // Can both serve and consume
439
+ name: 'my-ability',
440
+ role: 'ability', // Provider role
460
441
  protocol: 'broker',
461
- brokerUrls: ['ws://localhost:8080'],
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 and serve
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 service development with tool registration, event publishing, and remote tool invocation.
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 Service
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->>Service: Extract tool handlers via getToolNames()
521
- Service-->>Transport: Return registered tools
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->>Service: KadiClient detects stdio mode
527
- Service->>Service: Setup FrameReader/Writer
528
- Transport->>Service: Send discovery request
529
- Service-->>Transport: Return tool list
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: Create KadiClient wrapper
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,Service: Tool invocation
532
+
533
+ Note over Client,Ability: Tool invocation
548
534
  Client->>Transport: ability.echo({ message })
549
-
535
+
550
536
  alt native
551
- Transport->>Service: Direct handler call
552
- else stdio
553
- Transport->>Service: JSON-RPC over frames
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->>Service: Route to target
557
- Service->>Broker: kadi.ability.result
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 Service (Publisher)
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 Service
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('service-b', 'process', params)
718
- Client->>Broker: kadi.ability.invoke({
719
- targetAgent: 'service-b',
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 service-b
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 KadiAbility
739
+ participant Ability as KadiClient
754
740
 
755
741
  Note over Parent,SR: Incoming Request
756
- Parent->>SR: Write to stdin: "Kadi-Content-Length: 52\r\n\r\n{...}"
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: "Kadi-Content-Length: N\r\n\r\n"
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.call('someMethod', { param: 'value' });
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 stdio protocols
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 KADI_SERVICE_NAME=my-service
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 serviceName = process.env.KADI_SERVICE_NAME;
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 KadiAbility({
843
+ const ability = new KadiClient({
858
844
  name: 'my-ability',
859
- scope: process.env.KADI_AGENT_SCOPE || 'global'
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
- │ ├── service.js # Ability implementation
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 service lifecycle:
874
+ Monitor the ability lifecycle:
889
875
 
890
876
  ```javascript
891
- const client = new KadiClient({ name: 'my-service' });
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('Service error:', 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: 'service',
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
- ['process.*', 'system.*'],
966
- (pattern, data) => {
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. **clone the repository**
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
- 1. **Install in your project**:
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
- 1. **For ability development**, update the ability's preflight script:
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 services, and managing events.
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-service',
1029
- role: 'service', // 'agent', 'ability', or 'service'
1074
+ name: 'my-ability',
1075
+ role: 'ability', // 'agent' or 'ability'
1030
1076
  protocol: 'broker', // 'native', 'stdio', or 'broker'
1031
- brokerUrls: ['ws://localhost:8080'],
1032
- networks: ['global', 'custom-network']
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
- - `name` - Service/agent name
1038
- - `role` - Operating role: 'agent' (consumer), 'ability' (provider), 'service' (both)
1088
+
1089
+ - `name` - Ability/agent name
1090
+ - `role` - Operating role: 'agent' (consumer) or 'ability' (provider)
1039
1091
  - `protocol` - Transport protocol to use
1040
- - `brokerUrls` - Array of broker WebSocket URLs (for broker protocol)
1041
- - `networks` - Network namespaces for tool discovery
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(urls?)` - Connect to broker(s)
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` - Service 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 a service that can both serve and consume
1082
- const service = new KadiClient({
1083
- name: 'math-service',
1084
- role: 'service',
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
- brokerUrls: ['ws://localhost:8080'],
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
- service.registerTool(
1157
+ ability.registerTool(
1092
1158
  'add',
1093
1159
  async ({ a, b }) => {
1094
1160
  // Publish event before processing
1095
- await service.publishEvent('math.operation', {
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 service.publishEvent('math.completed', {
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 services
1130
- service.subscribeToEvent('system.*', (data) => {
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 broker
1135
- await service.connectToBrokers();
1200
+ // Connect to brokers
1201
+ await ability.connectToBrokers();
1136
1202
 
1137
1203
  // Call remote tools
1138
- const result = await service.callTool(
1139
- 'translator-service',
1140
- 'translate',
1141
- { text: 'Hello', to: 'es' }
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 service.disconnect();
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 (native/stdio protocols only)
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