@kadi.build/core 0.0.1-alpha.3 → 0.0.1-alpha.4

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.
Files changed (128) hide show
  1. package/README.md +571 -594
  2. package/dist/KadiClient.d.ts +303 -0
  3. package/dist/KadiClient.d.ts.map +1 -0
  4. package/dist/KadiClient.js +1162 -0
  5. package/dist/KadiClient.js.map +1 -0
  6. package/dist/errors/error-codes.d.ts +215 -0
  7. package/dist/errors/error-codes.d.ts.map +1 -0
  8. package/dist/errors/error-codes.js +295 -0
  9. package/dist/errors/error-codes.js.map +1 -0
  10. package/dist/index.d.ts +15 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +24 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/loadAbility.d.ts +65 -0
  15. package/dist/loadAbility.d.ts.map +1 -0
  16. package/dist/loadAbility.js +335 -0
  17. package/dist/loadAbility.js.map +1 -0
  18. package/dist/messages/BrokerMessages.d.ts +84 -0
  19. package/dist/messages/BrokerMessages.d.ts.map +1 -0
  20. package/dist/messages/BrokerMessages.js +127 -0
  21. package/dist/messages/BrokerMessages.js.map +1 -0
  22. package/dist/messages/MessageBuilder.d.ts +83 -0
  23. package/dist/messages/MessageBuilder.d.ts.map +1 -0
  24. package/dist/messages/MessageBuilder.js +144 -0
  25. package/dist/messages/MessageBuilder.js.map +1 -0
  26. package/dist/schemas/events.schemas.d.ts +177 -0
  27. package/dist/schemas/events.schemas.d.ts.map +1 -0
  28. package/dist/schemas/events.schemas.js +265 -0
  29. package/dist/schemas/events.schemas.js.map +1 -0
  30. package/dist/schemas/index.d.ts +3 -0
  31. package/dist/schemas/index.d.ts.map +1 -0
  32. package/dist/schemas/index.js +4 -0
  33. package/dist/schemas/index.js.map +1 -0
  34. package/dist/schemas/kadi.schemas.d.ts +70 -0
  35. package/dist/schemas/kadi.schemas.d.ts.map +1 -0
  36. package/dist/schemas/kadi.schemas.js +120 -0
  37. package/dist/schemas/kadi.schemas.js.map +1 -0
  38. package/dist/transports/BrokerTransport.d.ts +96 -0
  39. package/dist/transports/BrokerTransport.d.ts.map +1 -0
  40. package/dist/transports/BrokerTransport.js +145 -0
  41. package/dist/transports/BrokerTransport.js.map +1 -0
  42. package/dist/transports/NativeTransport.d.ts +92 -0
  43. package/dist/transports/NativeTransport.d.ts.map +1 -0
  44. package/dist/transports/NativeTransport.js +221 -0
  45. package/dist/transports/NativeTransport.js.map +1 -0
  46. package/dist/transports/StdioTransport.d.ts +112 -0
  47. package/dist/transports/StdioTransport.d.ts.map +1 -0
  48. package/dist/transports/StdioTransport.js +440 -0
  49. package/dist/transports/StdioTransport.js.map +1 -0
  50. package/dist/transports/Transport.d.ts +93 -0
  51. package/dist/transports/Transport.d.ts.map +1 -0
  52. package/dist/transports/Transport.js +13 -0
  53. package/dist/transports/Transport.js.map +1 -0
  54. package/dist/types/broker.d.ts +31 -0
  55. package/dist/types/broker.d.ts.map +1 -0
  56. package/dist/types/broker.js +6 -0
  57. package/dist/types/broker.js.map +1 -0
  58. package/dist/types/core.d.ts +138 -0
  59. package/dist/types/core.d.ts.map +1 -0
  60. package/dist/types/core.js +26 -0
  61. package/dist/types/core.js.map +1 -0
  62. package/dist/types/events.d.ts +186 -0
  63. package/dist/types/events.d.ts.map +1 -0
  64. package/dist/types/events.js +16 -0
  65. package/dist/types/events.js.map +1 -0
  66. package/dist/types/index.d.ts +9 -0
  67. package/dist/types/index.d.ts.map +1 -0
  68. package/dist/types/index.js +13 -0
  69. package/dist/types/index.js.map +1 -0
  70. package/dist/types/protocol.d.ts +160 -0
  71. package/dist/types/protocol.d.ts.map +1 -0
  72. package/dist/types/protocol.js +5 -0
  73. package/dist/types/protocol.js.map +1 -0
  74. package/dist/utils/agentUtils.d.ts +102 -0
  75. package/dist/utils/agentUtils.d.ts.map +1 -0
  76. package/dist/utils/agentUtils.js +166 -0
  77. package/dist/utils/agentUtils.js.map +1 -0
  78. package/dist/utils/commandUtils.d.ts +45 -0
  79. package/dist/utils/commandUtils.d.ts.map +1 -0
  80. package/dist/utils/commandUtils.js +145 -0
  81. package/dist/utils/commandUtils.js.map +1 -0
  82. package/dist/utils/configUtils.d.ts +55 -0
  83. package/dist/utils/configUtils.d.ts.map +1 -0
  84. package/dist/utils/configUtils.js +100 -0
  85. package/dist/utils/configUtils.js.map +1 -0
  86. package/dist/utils/logger.d.ts +59 -0
  87. package/dist/utils/logger.d.ts.map +1 -0
  88. package/dist/utils/logger.js +122 -0
  89. package/dist/utils/logger.js.map +1 -0
  90. package/dist/utils/pathUtils.d.ts +48 -0
  91. package/dist/utils/pathUtils.d.ts.map +1 -0
  92. package/dist/utils/pathUtils.js +128 -0
  93. package/dist/utils/pathUtils.js.map +1 -0
  94. package/package.json +56 -5
  95. package/agent.json +0 -18
  96. package/examples/example-abilities/echo-js/README.md +0 -131
  97. package/examples/example-abilities/echo-js/agent.json +0 -63
  98. package/examples/example-abilities/echo-js/package.json +0 -24
  99. package/examples/example-abilities/echo-js/service.js +0 -43
  100. package/examples/example-abilities/hash-go/agent.json +0 -53
  101. package/examples/example-abilities/hash-go/cmd/hash_ability/main.go +0 -340
  102. package/examples/example-abilities/hash-go/go.mod +0 -3
  103. package/examples/example-agent/abilities/echo-js/0.0.1/README.md +0 -131
  104. package/examples/example-agent/abilities/echo-js/0.0.1/agent.json +0 -63
  105. package/examples/example-agent/abilities/echo-js/0.0.1/package-lock.json +0 -93
  106. package/examples/example-agent/abilities/echo-js/0.0.1/package.json +0 -24
  107. package/examples/example-agent/abilities/echo-js/0.0.1/service.js +0 -41
  108. package/examples/example-agent/abilities/hash-go/0.0.1/agent.json +0 -53
  109. package/examples/example-agent/abilities/hash-go/0.0.1/bin/hash_ability +0 -0
  110. package/examples/example-agent/abilities/hash-go/0.0.1/cmd/hash_ability/main.go +0 -340
  111. package/examples/example-agent/abilities/hash-go/0.0.1/go.mod +0 -3
  112. package/examples/example-agent/agent.json +0 -39
  113. package/examples/example-agent/index.js +0 -102
  114. package/examples/example-agent/package-lock.json +0 -93
  115. package/examples/example-agent/package.json +0 -17
  116. package/src/KadiAbility.js +0 -478
  117. package/src/index.js +0 -65
  118. package/src/loadAbility.js +0 -1086
  119. package/src/servers/BaseRpcServer.js +0 -404
  120. package/src/servers/BrokerRpcServer.js +0 -776
  121. package/src/servers/StdioRpcServer.js +0 -360
  122. package/src/transport/BrokerMessageBuilder.js +0 -377
  123. package/src/transport/IpcMessageBuilder.js +0 -1229
  124. package/src/utils/agentUtils.js +0 -137
  125. package/src/utils/commandUtils.js +0 -64
  126. package/src/utils/configUtils.js +0 -72
  127. package/src/utils/logger.js +0 -161
  128. package/src/utils/pathUtils.js +0 -86
package/README.md CHANGED
@@ -16,6 +16,7 @@
16
16
  - [🚀 Quick Start](#-quick-start)
17
17
  - [🔌 Transport Protocols](#-transport-protocols)
18
18
  - [🛠️ Creating Abilities](#-creating-abilities)
19
+ - [🤖 Creating Agents](#-creating-agents)
19
20
  - [🔄 Architecture Deep Dive](#-architecture-deep-dive)
20
21
  - [📥 Loading Abilities](#-loading-abilities)
21
22
  - [⚙️ Configuration](#-configuration)
@@ -44,26 +45,32 @@ npm install -g @kadi.build/cli
44
45
 
45
46
  ## 🚀 Quick Start
46
47
 
47
- ### Creating Your First Ability
48
+ The library provides a unified way to work with KADI through the **KadiClient** class, which can operate in multiple roles:
49
+
50
+ - **As an Ability**: Serving tools/methods that others can call
51
+ - **As an Agent**: Calling remote tools via broker protocol
52
+ - **As a Service**: Combining both roles - serving and consuming tools
53
+
54
+ ### Creating Your First Service
48
55
 
49
56
  ```javascript
50
57
  #!/usr/bin/env node
51
- import { KadiAbility } from '@kadi.build/core';
58
+ import { KadiClient } from '@kadi.build/core';
52
59
 
53
- // Create an ability instance
54
- const mathAbility = new KadiAbility({
55
- name: 'math-ability',
56
- version: '1.0.0',
57
- description: 'Simple math operations'
60
+ // Create a service instance
61
+ const mathService = new KadiClient({
62
+ name: 'math-service',
63
+ role: 'ability', // 'agent', 'ability', or 'service'
64
+ protocol: 'stdio' // 'native', 'stdio', or 'broker'
58
65
  });
59
66
 
60
- // Register a method
61
- mathAbility.method('add', async ({ a, b }) => {
67
+ // Register a tool
68
+ mathService.registerTool('add', async ({ a, b }) => {
62
69
  return { result: a + b };
63
70
  });
64
71
 
65
72
  // Start serving requests
66
- mathAbility.serve().catch(console.error);
73
+ mathService.serve().catch(console.error);
67
74
  ```
68
75
 
69
76
  ### Loading and Using Abilities
@@ -78,14 +85,54 @@ async function main() {
78
85
  // Call methods like regular functions
79
86
  const result = await math.add({ a: 5, b: 3 });
80
87
  console.log(result); // { result: 8 }
81
-
82
- // List available methods
83
- console.log(math.__list()); // ['add']
84
88
  }
85
89
 
86
90
  main().catch(console.error);
87
91
  ```
88
92
 
93
+ ### Creating a Broker-Connected Agent
94
+
95
+ ```javascript
96
+ import { KadiClient } from '@kadi.build/core';
97
+
98
+ // Create an agent that connects to broker
99
+ const agent = new KadiClient({
100
+ name: 'my-agent',
101
+ role: 'agent',
102
+ protocol: 'broker',
103
+ brokerUrls: ['ws://localhost:8080'],
104
+ networks: ['global']
105
+ });
106
+
107
+ // Register tools that other agents can call
108
+ agent.registerTool(
109
+ 'greet',
110
+ async ({ name }) => {
111
+ return { greeting: `Hello, ${name}!` };
112
+ },
113
+ {
114
+ description: 'Greet someone by name',
115
+ inputSchema: {
116
+ type: 'object',
117
+ properties: {
118
+ name: { type: 'string', description: 'Name to greet' }
119
+ },
120
+ required: ['name']
121
+ }
122
+ }
123
+ );
124
+
125
+ // Connect to broker and start serving
126
+ await agent.connectToBrokers();
127
+
128
+ // Call tools from OTHER agents connected to the same broker
129
+ const result = await agent.callTool('translate', 'text-tool', {
130
+ text: 'Hello world',
131
+ language: 'spanish'
132
+ });
133
+ console.log(result);
134
+ ```
135
+
89
136
  ## 🔌 Transport Protocols
90
137
 
91
138
  KADI abilities support three transport protocols, each optimized for different use cases:
@@ -185,28 +232,28 @@ graph TD
185
232
  }
186
233
  ```
187
234
 
188
- ## 🛠️ Creating Abilities
235
+ ## 🛠️ Creating Services with KadiClient
189
236
 
190
- ### Basic Ability Structure
237
+ ### Basic Service Structure
191
238
 
192
239
  ```javascript
193
- import { KadiAbility } from '@kadi.build/core';
240
+ import { KadiClient } from '@kadi.build/core';
194
241
 
195
- const ability = new KadiAbility({
196
- name: 'echo-ability',
197
- version: '1.0.0',
198
- description: 'Echo service with metadata',
199
- scope: process.env.KADI_AGENT_SCOPE || 'global'
242
+ const service = new KadiClient({
243
+ name: 'echo-service',
244
+ role: 'ability', // 'agent', 'ability', or 'service'
245
+ protocol: 'stdio', // 'native', 'stdio', or 'broker'
246
+ brokerUrls: ['ws://localhost:8080']
200
247
  });
201
248
 
202
- // Method 1: Simple handler
203
- ability.method('echo', async ({ message }) => ({
249
+ // Method 1: Simple tool registration
250
+ service.registerTool('echo', async ({ message }) => ({
204
251
  echo: message,
205
252
  timestamp: new Date().toISOString()
206
253
  }));
207
254
 
208
- // Method 2: Handler with inline schema
209
- ability.method(
255
+ // Method 2: Tool with inline schema
256
+ service.registerTool(
210
257
  'format',
211
258
  async ({ text, style }) => ({
212
259
  formatted: style === 'upper' ? text.toUpperCase() : text.toLowerCase()
@@ -235,41 +282,60 @@ ability.method(
235
282
  );
236
283
 
237
284
  // Start serving
238
- ability.serve().catch(console.error);
285
+ service.serve().catch(console.error);
239
286
  ```
240
287
 
241
288
  ### Event Publishing and Subscription
242
289
 
243
- **Note**: Events currently work for `native` and `stdio` protocols. Broker protocol event support is planned for future releases.
290
+ **Full Support**: Events work across all protocols - native, stdio, and broker.
244
291
 
245
- Abilities can publish events that agents can subscribe to:
292
+ KadiClient provides a unified event system that works consistently across all transport protocols:
246
293
 
247
294
  ```javascript
248
- // In your ability (service.js)
249
- const ability = new KadiAbility({ name: 'my-ability' });
250
-
251
- // Publish events from method handlers
252
- ability.method('echo', async ({ message }) => {
253
- // Publish an event before returning the result
254
- ability.publishEvent('echo:test-event', { from: 'message echo' });
255
- ability.publishEvent('echo:test-event', { from: 'message echo 2' });
295
+ // In your service (service.js)
296
+ const service = new KadiClient({
297
+ name: 'my-service',
298
+ role: 'ability',
299
+ protocol: 'broker' // Works with all protocols
300
+ });
256
301
 
257
- return { echo: message, timestamp: new Date().toISOString() };
302
+ // Publish events from tool handlers
303
+ service.registerTool('echo', async ({ message }) => {
304
+ // Publish events during processing
305
+ await service.publishEvent('echo.processing', { status: 'started' });
306
+
307
+ // Do work...
308
+ const result = { echo: message, timestamp: new Date().toISOString() };
309
+
310
+ await service.publishEvent('echo.completed', { result });
311
+ return result;
258
312
  });
259
313
 
260
- // In your agent (index.js)
261
- const ability = await loadAbility('my-ability');
314
+ // In your client (index.js)
315
+ const client = new KadiClient({
316
+ name: 'my-client',
317
+ role: 'agent',
318
+ protocol: 'broker'
319
+ });
262
320
 
263
- // Subscribe to events BEFORE calling methods that emit them
264
- ability.events.on('echo:test-event', (data) => {
265
- console.log(`[NATIVE] echo:test-event received:`, data);
321
+ // Subscribe to events using patterns
322
+ client.subscribeToEvent('echo.*', (data) => {
323
+ console.log('Echo event received:', data);
266
324
  });
267
325
 
268
- // Now call the method that emits events
269
- const result = await ability.echo({ message: 'Hello world' });
326
+ // For loaded abilities with native/stdio protocols
327
+ const ability = await loadAbility('my-service', 'stdio');
328
+ ability.events.on('echo.completed', (data) => {
329
+ console.log('Completed:', data);
330
+ });
270
331
  ```
271
332
 
272
- **Important**: Always subscribe to events before calling methods that emit them, as events are emitted immediately when `publishEvent()` is called.
333
+ **Event Patterns**:
334
+ - Use wildcards: `math.*` matches `math.operation`, `math.milestone`
335
+ - Protocol-specific delivery:
336
+ - **Native**: Direct EventEmitter (synchronous)
337
+ - **Stdio**: JSON-RPC notifications over stdout
338
+ - **Broker**: RabbitMQ pub/sub via WebSocket
273
339
 
274
340
  ### Ability Configuration (agent.json)
275
341
 
@@ -361,176 +427,322 @@ ability.method('process', handler, {
361
427
 
362
428
  **Important**: Specifying MCP schemas for ability handlers is particularly important for `broker` communication. If you only need `native` and `stdio` protocols, schemas can be omitted.
363
429
 
430
+ ## 🤖 Working with KadiClient
431
+
432
+ KadiClient is the unified API for all KADI operations. It replaces the previous separate KadiAbility and KadiAgent classes with a single, powerful interface that can operate in multiple roles.
433
+
434
+ ### Direct Approach (using loadAbility)
435
+
436
+ ```javascript
437
+ import { loadAbility } from '@kadi.build/core';
438
+
439
+ // Load and call abilities directly
440
+ const echoAbility = await loadAbility('echo-js', 'stdio');
441
+ const result = await echoAbility.echo({ message: 'Hello world' });
442
+
443
+ // Load broker tools directly
444
+ const remoteService = await loadAbility('remote-service', 'broker', {
445
+ brokerUrl: 'ws://localhost:8080',
446
+ networks: ['global']
447
+ });
448
+ const brokerResult = await remoteService.process({ data: 'test' });
449
+ ```
450
+
451
+ ### Using KadiClient
452
+
453
+ ```javascript
454
+ import { KadiClient } from '@kadi.build/core';
455
+
456
+ // Create a unified client
457
+ const client = new KadiClient({
458
+ name: 'my-service',
459
+ role: 'service', // Can both serve and consume
460
+ protocol: 'broker',
461
+ brokerUrls: ['ws://localhost:8080'],
462
+ networks: ['global']
463
+ });
464
+
465
+ // Register tools for others to call
466
+ client.registerTool(
467
+ 'greet',
468
+ async ({ name }) => {
469
+ return { greeting: `Hello, ${name}!` };
470
+ },
471
+ {
472
+ description: 'Greet someone by name',
473
+ inputSchema: {
474
+ type: 'object',
475
+ properties: { name: { type: 'string' } },
476
+ required: ['name']
477
+ }
478
+ }
479
+ );
480
+
481
+ // Connect and serve
482
+ await client.connectToBrokers();
483
+
484
+ // Call remote tools
485
+ const result = await client.callTool('translator', 'translate', {
486
+ text: 'Hello',
487
+ to: 'es'
488
+ });
489
+
490
+ // Load abilities for compatibility
491
+ const ability = await client.loadAbility('echo-js');
492
+ ```
493
+
494
+ Use `loadAbility()` for simple consumption, or KadiClient for full-featured service development with tool registration, event publishing, and remote tool invocation.
495
+
364
496
  ## 🔄 Architecture Deep Dive
365
497
 
366
- ### Ability Loading Sequence
498
+ ### Transport Architecture and Loading Sequence
367
499
 
368
- The loading process adapts based on the selected protocol:
500
+ The loading process uses modular transports based on the selected protocol:
369
501
 
370
502
  ```mermaid
371
503
  sequenceDiagram
372
504
  participant Client
373
505
  participant loadAbility
506
+ participant Transport
374
507
  participant FileSystem
375
508
  participant ChildProcess
376
- participant Ability
509
+ participant Service
377
510
  participant Broker
378
511
 
379
512
  Client->>loadAbility: loadAbility('echo-js', protocol?)
380
- loadAbility->>FileSystem: Read project/ability agent.json
381
- FileSystem-->>loadAbility: Return ability version & config
382
- loadAbility->>FileSystem: Check ability directory exists
383
- loadAbility->>FileSystem: Read ability's agent.json manifest
384
-
513
+ loadAbility->>FileSystem: Read agent.json manifests
514
+ FileSystem-->>loadAbility: Return config & version
515
+
385
516
  alt protocol === 'native'
386
- loadAbility->>FileSystem: Read entry point from manifest
387
- loadAbility->>Ability: import(modulePath)
388
- Ability-->>loadAbility: Return module exports
389
- loadAbility->>loadAbility: Extract functions from module
390
- loadAbility->>loadAbility: Build proxy with direct function calls
517
+ loadAbility->>Transport: new NativeTransport(config)
518
+ Transport->>FileSystem: import(entry point)
519
+ FileSystem-->>Transport: Module with KadiClient instance
520
+ Transport->>Service: Extract tool handlers via getToolNames()
521
+ Service-->>Transport: Return registered tools
522
+ Transport->>Transport: Store handlers in Map
391
523
  else protocol === 'stdio'
392
- loadAbility->>ChildProcess: spawn(startCmd, { env: { KADI_PROTOCOL: 'stdio' }})
393
- ChildProcess->>Ability: Start ability process
394
- Ability->>Ability: Create StdioRpcServer
395
- loadAbility->>Ability: Send __kadi_init via LSP frames
396
- Ability-->>loadAbility: Return { name, version, functions }
397
-
398
- opt if discover enabled
399
- loadAbility->>Ability: Send __kadi_discover
400
- Ability-->>loadAbility: Return { functions: {...} }
401
- end
402
-
403
- loadAbility->>loadAbility: Merge static exports + discovered functions
404
- loadAbility->>loadAbility: Build proxy with RPC calls
524
+ loadAbility->>Transport: new StdioTransport(config)
525
+ 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
530
+ Transport->>Transport: Setup JSON-RPC proxy
405
531
  else protocol === 'broker'
406
- loadAbility->>loadAbility: Generate scope UUID
407
- loadAbility->>ChildProcess: spawn(startCmd, { env: { KADI_PROTOCOL, KADI_BROKER_URL, KADI_AGENT_SCOPE, KADI_SERVICE_NAME }})
408
- ChildProcess->>Ability: Start ability process
409
-
410
- Note over Ability,Broker: Ability connects to broker
411
- Ability->>Broker: WebSocket connect
412
- Ability->>Broker: hello({ role: 'agent' })
413
- Broker-->>Ability: { nonce }
414
- Ability->>Broker: authenticate({ publicKey, signature })
415
- Broker-->>Ability: { agentId }
416
- Ability->>Broker: registerCapabilities({ tools, scopes: [KADI_AGENT_SCOPE] })
417
-
418
- Note over loadAbility,Broker: Parent connects to broker
419
- loadAbility->>Broker: WebSocket connect
420
- loadAbility->>Broker: hello/auth/register sequence
421
-
422
- opt if discover enabled
423
- loadAbility->>Broker: listTools({ scopes: [scope] })
424
- Broker-->>loadAbility: Return available tools
425
- end
426
-
427
- loadAbility->>loadAbility: Merge static exports + broker tools
428
- loadAbility->>loadAbility: Build proxy with broker RPC
532
+ loadAbility->>Transport: new BrokerTransport(config)
533
+ Transport->>Transport: Create KadiClient wrapper
534
+ Transport->>Broker: WebSocket connect
535
+ Transport->>Broker: kadi.session.hello
536
+ Broker-->>Transport: { nonce, heartbeatIntervalSec }
537
+ Transport->>Broker: kadi.session.authenticate
538
+ Broker-->>Transport: { sessionId }
539
+ Transport->>Broker: kadi.ability.list
540
+ Broker-->>Transport: Available tools
541
+ Transport->>Transport: Start heartbeat timer
429
542
  end
430
543
 
431
- loadAbility->>Client: Return ability proxy
432
- Client->>Client: ability.method({ params })
544
+ loadAbility->>loadAbility: Create proxy with tool methods
545
+ loadAbility-->>Client: Return ability proxy
546
+
547
+ Note over Client,Service: Tool invocation
548
+ Client->>Transport: ability.echo({ message })
549
+
550
+ alt native
551
+ Transport->>Service: Direct handler call
552
+ else stdio
553
+ Transport->>Service: JSON-RPC over frames
554
+ else broker
555
+ Transport->>Broker: kadi.ability.invoke
556
+ Broker->>Service: Route to target
557
+ Service->>Broker: kadi.ability.result
558
+ Broker-->>Transport: Forward result
559
+ end
560
+
561
+ Transport-->>Client: Return result
433
562
  ```
434
563
 
435
- ### Method Registration & Serving
564
+ ### KadiClient Architecture: Tool Registration & Serving
436
565
 
437
- How abilities register methods and start serving:
566
+ How KadiClient registers tools and starts serving:
438
567
 
439
568
  ```mermaid
440
569
  sequenceDiagram
441
570
  participant Dev as Developer
442
- participant KA as KadiAbility
443
- participant Server as RpcServer
571
+ participant KC as KadiClient
444
572
  participant Transport
445
- participant Client
573
+ participant Network as Network Layer
574
+ participant RemoteClient
575
+
576
+ Note over Dev,KC: Tool Registration Phase
577
+ Dev->>KC: new KadiClient({ name, role, protocol })
578
+ KC->>KC: Initialize based on role & protocol
579
+
580
+ Dev->>KC: registerTool('echo', handler, schema?)
581
+ KC->>KC: Store in toolHandlers Map<name, {handler, schema}>
582
+
583
+ Dev->>KC: registerTool('process', handler, schema)
584
+ KC->>KC: Add to toolHandlers Map
585
+
586
+ Note over KC,Network: Connection & Serving Phase
587
+ Dev->>KC: serve() or connectToBrokers()
588
+
589
+ alt protocol === 'native'
590
+ KC->>KC: Tools available via direct reference
591
+ KC->>KC: Emit 'start' event
592
+ else protocol === 'stdio'
593
+ KC->>Transport: Setup StdioTransport
594
+ Transport->>Transport: new FrameReader(stdin)
595
+ Transport->>Transport: new FrameWriter(stdout)
596
+ Transport->>KC: on('message', handleStdioMessage)
597
+ KC->>KC: Process JSON-RPC requests
598
+ else protocol === 'broker'
599
+ KC->>Network: WebSocket.connect(brokerUrl)
600
+ KC->>Network: kadi.session.hello({ role })
601
+ Network-->>KC: { nonce, heartbeatIntervalSec: 30 }
602
+ KC->>KC: Sign nonce with Ed25519
603
+ KC->>Network: kadi.session.authenticate({ signature })
604
+ Network-->>KC: { sessionId }
605
+ KC->>Network: kadi.agent.register({ tools, networks })
606
+ KC->>KC: Start heartbeat timer (30s interval)
607
+ end
446
608
 
447
- Note over Dev,KA: Registration Phase
448
- Dev->>KA: new KadiAbility({ name, scope: process.env.KADI_AGENT_SCOPE })
449
- Dev->>KA: ability.method('echo', handler)
450
- KA->>KA: Store in methodHandlers Map
609
+ Note over RemoteClient,KC: Tool Invocation Flow
610
+ RemoteClient->>Network: Request tool invocation
611
+
612
+ alt native
613
+ RemoteClient->>KC: Direct method call
614
+ KC->>KC: toolHandlers.get(name).handler(params)
615
+ else stdio
616
+ Network->>Transport: JSON-RPC frame
617
+ Transport->>KC: Parsed request
618
+ KC->>KC: toolHandlers.get(name).handler(params)
619
+ KC->>Transport: JSON-RPC response
620
+ Transport->>Network: Framed response
621
+ else broker
622
+ Network->>KC: kadi.ability.invoke message
623
+ KC->>KC: toolHandlers.get(toolName).handler(params)
624
+ KC->>Network: kadi.ability.result message
625
+ end
626
+
627
+ Network-->>RemoteClient: Tool result
628
+ ```
451
629
 
452
- Dev->>KA: ability.method('transform', handler, schema)
453
- KA->>KA: Store handler in methodHandlers
454
- KA->>KA: Store schema in methodMCPSchemas
630
+ ### Event System Architecture
455
631
 
456
- Note over KA,Transport: Serving Phase
457
- Dev->>KA: ability.serve()
458
- KA->>KA: Detect protocol from env/options
632
+ KadiClient provides a unified event API that works across all transport protocols:
459
633
 
460
- alt protocol === 'stdio'
461
- KA->>Server: new StdioRpcServer()
462
- Server->>Transport: new StdioFrameReader(stdin)
463
- Server->>Transport: new StdioFrameWriter(stdout)
464
- Transport->>Transport: Listen for LSP frames on stdin
634
+ ```mermaid
635
+ sequenceDiagram
636
+ participant Publisher as Service (Publisher)
637
+ participant Transport as Transport Layer
638
+ participant EventBus as Event Bus
639
+ participant Subscriber as Client (Subscriber)
640
+
641
+ Note over Subscriber: Setup subscription
642
+ Subscriber->>Subscriber: subscribeToEvent('math.*', callback)
643
+
644
+ alt protocol === 'broker'
645
+ Subscriber->>EventBus: kadi.event.subscribe({channels: ['math.*']})
646
+ EventBus-->>Subscriber: {subscribed: ['math.*'], queueName}
647
+ else protocol === 'stdio'
648
+ Subscriber->>Transport: Setup event listener
649
+ Transport->>Subscriber: on('transport:event', dispatcher)
650
+ else protocol === 'native'
651
+ Subscriber->>Publisher: Direct EventEmitter reference
652
+ Publisher->>Subscriber: on('ability:event', dispatcher)
653
+ end
654
+
655
+ Note over Publisher: Publish event
656
+ Publisher->>Publisher: publishEvent('math.operation', data)
657
+
658
+ alt protocol === 'native'
659
+ Publisher->>Publisher: emit('ability:event', {eventName, data})
660
+ Publisher-->>Subscriber: Direct EventEmitter delivery
661
+ Subscriber->>Subscriber: Pattern match & invoke callback
662
+ else protocol === 'stdio'
663
+ Publisher->>Transport: writer.write(__kadi_event notification)
664
+ Transport->>Transport: Frame with LSP headers
665
+ Transport-->>Subscriber: Framed event over stdout
666
+ Subscriber->>Transport: FrameReader parses
667
+ Transport->>Subscriber: emit('transport:event', {name, data})
668
+ Subscriber->>Subscriber: Pattern match & invoke callback
465
669
  else protocol === 'broker'
466
- KA->>Server: new BrokerRpcServer()
467
- Server->>Transport: WebSocket connect to broker
468
- Server->>Transport: Handshake (hello/auth)
469
- Server->>KA: ability.extractToolsForBroker()
470
- KA-->>Server: Return tools from exports + inline schemas
471
- Server->>Transport: registerCapabilities({ tools, scopes })
670
+ Publisher->>EventBus: kadi.event.publish({channel, data})
671
+ EventBus->>EventBus: Route via RabbitMQ
672
+ EventBus-->>Subscriber: kadi.event.delivery
673
+ Subscriber->>Subscriber: Pattern match & invoke callback
674
+ end
675
+
676
+ Note over Subscriber: Cleanup
677
+ Subscriber->>Subscriber: unsubscribe()
678
+ alt protocol === 'broker'
679
+ Subscriber->>EventBus: kadi.event.unsubscribe
680
+ else
681
+ Subscriber->>Subscriber: Remove local listener
472
682
  end
473
-
474
- Server->>KA: Forward events (start, request, response, error)
475
-
476
- Note over Transport,Client: Request Handling
477
- Client->>Transport: Incoming request
478
- Transport->>Server: Parse & validate request
479
- Server->>KA: getMethodHandler(method)
480
- KA-->>Server: Return handler function
481
- Server->>Server: Execute handler with timeout
482
- Server->>Transport: Send response
483
- Transport->>Client: Return result
484
683
  ```
485
684
 
486
685
  ### Broker Protocol Communication Flow
487
686
 
488
- Detailed view of broker-based communication:
687
+ Detailed view of broker-based communication with heartbeat:
489
688
 
490
689
  ```mermaid
491
690
  sequenceDiagram
492
- participant Agent as Agent (Parent)
493
- participant Ability as Ability (Child)
691
+ participant Client as KadiClient
494
692
  participant Broker as KADI Broker
495
- participant Consumer as Other Agent/Client
496
-
497
- Note over Agent,Ability: Initialization
498
- Agent->>Agent: Generate scope UUID
499
- Agent->>Ability: spawn(env: { KADI_AGENT_SCOPE: uuid })
500
-
501
- Note over Ability,Broker: Ability Registration
502
- Ability->>Broker: Connect WebSocket
503
- Ability->>Broker: hello({ role: 'agent' })
504
- Broker-->>Ability: { nonce: 'abc123' }
505
- Ability->>Ability: Generate ephemeral Ed25519 keypair
506
- Ability->>Broker: authenticate({ publicKey, signature(nonce) })
507
- Broker-->>Ability: { agentId: 'ability-123' }
508
- Ability->>Broker: registerCapabilities({ tools: [...], scopes: [uuid] })
509
- Broker->>Broker: Store capabilities in scope
510
-
511
- Note over Agent,Broker: Agent Connection
512
- Agent->>Broker: Connect WebSocket
513
- Agent->>Broker: hello/authenticate sequence
514
- Broker-->>Agent: { agentId: 'agent-456' }
515
-
516
- Note over Agent,Broker: Method Call via Broker
517
- Agent->>Broker: callAbility({ toolName: 'echo', args: { message: 'test' }, requestId })
518
- Broker-->>Agent: { requestId }
519
- Broker->>Ability: agent.message({ toolName, args, requestId, from: 'agent-456' })
520
- Ability->>Ability: Execute handler
521
- Ability->>Broker: ability.result({ requestId, toSessionId: 'agent-456', result })
522
- Broker->>Agent: ability.result({ requestId, result })
523
-
524
- Note over Broker,Consumer: Scope Isolation
525
- Consumer->>Broker: listTools({ scopes: ['global'] })
526
- Broker-->>Consumer: { tools: [] } // Ability not visible (wrong scope)
527
- Consumer->>Broker: listTools({ scopes: [uuid] })
528
- Broker-->>Consumer: { tools: ['echo', 'transform'] } // Now visible
693
+ participant Target as Target Service
694
+
695
+ Note over Client,Broker: Connection & Authentication
696
+ Client->>Broker: WebSocket connect
697
+ Client->>Broker: kadi.session.hello({ role })
698
+ Broker-->>Client: { nonce, heartbeatIntervalSec: 30 }
699
+ Client->>Client: Generate Ed25519 keypair
700
+ Client->>Client: Sign nonce
701
+ Client->>Broker: kadi.session.authenticate({ signature })
702
+ Broker-->>Client: { sessionId }
703
+
704
+ Note over Client,Broker: Heartbeat Management
705
+ Client->>Client: Start heartbeat timer (30s)
706
+ loop Every 30 seconds
707
+ Client->>Broker: kadi.session.ping
708
+ Note over Broker: Reset connection timeout (90s)
709
+ end
710
+
711
+ Note over Client,Broker: Tool Registration
712
+ Client->>Broker: kadi.agent.register({ tools, networks })
713
+ Broker->>Broker: Store in registry
714
+ Broker-->>Client: { registered: true }
715
+
716
+ 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',
720
+ toolName: 'process',
721
+ toolInput: params
722
+ })
723
+ Broker->>Broker: Lookup service-b
724
+ Broker->>Target: kadi.ability.invoke
725
+ Target->>Target: Execute tool handler
726
+ Target->>Broker: JSON-RPC response
727
+ Broker-->>Client: Forward response
728
+ Client->>Client: Resolve promise
729
+
730
+ Note over Client,Broker: Event Publishing
731
+ Client->>Broker: kadi.event.publish({
732
+ channel: 'system.status',
733
+ data: { status: 'healthy' }
734
+ })
735
+ Broker->>Broker: Publish to RabbitMQ exchange
736
+
737
+ Note over Client,Broker: Graceful Shutdown
738
+ Client->>Client: Stop heartbeat timer
739
+ Client->>Broker: kadi.session.goodbye
740
+ Client->>Client: Close WebSocket
529
741
  ```
530
742
 
531
- ### Stdio Protocol Frame Processing
743
+ ### Stdio Protocol: LSP-Style Frame Processing
532
744
 
533
- How LSP-style frames are processed:
745
+ How KadiClient handles stdio communication with LSP-style framing:
534
746
 
535
747
  ```mermaid
536
748
  sequenceDiagram
@@ -590,9 +802,8 @@ const nativeAbility = await loadAbility('my-ability', 'native');
590
802
  ### Working with Loaded Abilities
591
803
 
592
804
  ```javascript
593
- // List available methods
594
- const methods = ability.__list();
595
- console.log('Available methods:', methods);
805
+ // Call methods directly - no need to discover them first
806
+ const result = await ability.echo({ message: 'hello' });
596
807
 
597
808
  // Call methods
598
809
  const result = await ability.someMethod({ param: 'value' });
@@ -638,9 +849,9 @@ When abilities are spawned as child processes (stdio and broker protocols), the
638
849
  ```javascript
639
850
  // In your ability code, you can access these variables:
640
851
  const protocol = process.env.KADI_PROTOCOL; // 'stdio' or 'broker'
641
- const brokerUrl = process.env.KADI_BROKER_URL; // For broker protocol
642
- const serviceName = process.env.KADI_SERVICE_NAME; // For broker protocol
643
- const agentScope = process.env.KADI_AGENT_SCOPE; // For broker protocol
852
+ const brokerUrl = process.env.KADI_BROKER_URL;
853
+ const serviceName = process.env.KADI_SERVICE_NAME;
854
+ const agentScope = process.env.KADI_AGENT_SCOPE;
644
855
 
645
856
  // Use them to configure your ability behavior
646
857
  const ability = new KadiAbility({
@@ -650,7 +861,7 @@ const ability = new KadiAbility({
650
861
  });
651
862
  ```
652
863
 
653
- ### Project Structure
864
+ ### Example Project Structure
654
865
 
655
866
  ```
656
867
  my-project/
@@ -670,101 +881,100 @@ my-project/
670
881
 
671
882
  ### Event System
672
883
 
673
- KADI abilities support two types of events:
884
+ KadiClient provides a comprehensive event system that works across all protocols:
674
885
 
675
- #### 1. Lifecycle Events (All Protocols)
886
+ #### 1. Lifecycle Events
676
887
 
677
- These are internal ability lifecycle events that work across all protocols:
888
+ Monitor the service lifecycle:
678
889
 
679
890
  ```javascript
680
- const ability = new KadiAbility({ name: 'events-ability' });
891
+ const client = new KadiClient({ name: 'my-service' });
892
+
893
+ // Connection events
894
+ client.on('connected', ({ broker }) => {
895
+ console.log(`Connected to broker: ${broker}`);
896
+ });
681
897
 
682
- // Listen to lifecycle events
683
- ability.on('start', ({ protocol, methods }) => {
684
- console.log(`Started with ${protocol}, methods: ${methods.join(', ')}`);
898
+ client.on('disconnected', () => {
899
+ console.log('Disconnected from broker');
685
900
  });
686
901
 
687
- ability.on('request', ({ id, method, params }) => {
688
- console.log(`Request ${id}: ${method}`);
902
+ // Tool invocation events
903
+ client.on('tool:invoked', ({ toolName, params }) => {
904
+ console.log(`Tool ${toolName} invoked with:`, params);
689
905
  });
690
906
 
691
- ability.on('response', ({ id, result, error }) => {
692
- if (error) console.error(`Error in ${id}:`, error);
693
- else console.log(`Response ${id}:`, result);
907
+ client.on('tool:completed', ({ toolName, result }) => {
908
+ console.log(`Tool ${toolName} completed:`, result);
694
909
  });
695
910
 
696
- ability.on('error', (error) => {
697
- console.error('Ability error:', error);
911
+ client.on('error', (error) => {
912
+ console.error('Service error:', error);
698
913
  });
699
914
  ```
700
915
 
701
- #### 2. Custom Events (Native and Stdio Protocols Only - for now)
916
+ #### 2. Custom Events (All Protocols)
702
917
 
703
- Abilities can publish custom events that agents can subscribe to:
918
+ Publish and subscribe to custom events across all transport protocols:
704
919
 
705
920
  ```javascript
706
- // In your ability
707
- const ability = new KadiAbility({ name: 'my-ability' });
921
+ // Publishing events
922
+ const publisher = new KadiClient({
923
+ name: 'event-publisher',
924
+ role: 'service',
925
+ protocol: 'broker' // Works with all protocols
926
+ });
708
927
 
709
- ability.method('process', async ({ data }) => {
928
+ publisher.registerTool('process', async ({ data }) => {
710
929
  // Publish events during processing
711
- ability.publishEvent('process:started', {
712
- timestamp: new Date().toISOString()
930
+ await publisher.publishEvent('process.started', {
931
+ timestamp: Date.now(),
932
+ data
713
933
  });
714
934
 
715
- // ... do work ...
935
+ // Do work...
936
+ const result = await processData(data);
716
937
 
717
- ability.publishEvent('process:completed', {
718
- result: 'success',
719
- timestamp: new Date().toISOString()
938
+ await publisher.publishEvent('process.completed', {
939
+ timestamp: Date.now(),
940
+ result
720
941
  });
721
942
 
722
- return { processed: data };
943
+ return result;
723
944
  });
724
945
 
725
- // In your agent (index.js)
726
- const ability = await loadAbility('my-ability');
946
+ // Subscribing to events
947
+ const subscriber = new KadiClient({
948
+ name: 'event-subscriber',
949
+ role: 'agent',
950
+ protocol: 'broker'
951
+ });
727
952
 
728
- // Subscribe to custom events BEFORE calling methods
729
- ability.events.on('process:started', (data) => {
730
- console.log(`Process started at: ${data.timestamp}`);
953
+ // Subscribe with wildcards
954
+ subscriber.subscribeToEvent('process.*', (data) => {
955
+ console.log('Process event:', data);
731
956
  });
732
957
 
733
- ability.events.on('process:completed', (data) => {
734
- console.log(`Process completed: ${data.result} at ${data.timestamp}`);
958
+ // One-time subscription
959
+ subscriber.onceEvent('process.completed', (data) => {
960
+ console.log('First completion:', data);
735
961
  });
736
962
 
737
- // Now call the method that emits events
738
- await ability.process({ data: 'test' });
963
+ // Multiple pattern subscription
964
+ subscriber.subscribeToEvents(
965
+ ['process.*', 'system.*'],
966
+ (pattern, data) => {
967
+ console.log(`Event from ${pattern}:`, data);
968
+ }
969
+ );
739
970
  ```
740
971
 
741
- **Important Notes:**
742
-
743
- - Custom events only work with `native` and `stdio` protocols for now
744
- - Always subscribe to events before calling methods that emit them
745
- - Events are emitted immediately when `publishEvent()` is called
746
- - Broker protocol event support is in progress
972
+ **Protocol-Specific Behavior:**
747
973
 
748
- ### Error Handling
974
+ - **Native**: Direct EventEmitter, synchronous delivery
975
+ - **Stdio**: JSON-RPC notifications with LSP framing
976
+ - **Broker**: RabbitMQ pub/sub via WebSocket, persistent queues available
749
977
 
750
- ```javascript
751
- try {
752
- const ability = await loadAbility('my-ability', 'stdio');
753
- const result = await ability.riskyMethod({ data: 'test' });
754
- } catch (error) {
755
- if (error.message.includes('not found')) {
756
- console.error('Ability not installed. Run: kadi install');
757
- } else if (error.message.includes('timeout')) {
758
- console.error('Method timed out. Check ability health.');
759
- } else if (error.message.includes('not exposed by this ability')) {
760
- console.error(
761
- 'Method not available. Use ability.__list() to see available methods.'
762
- );
763
- } else {
764
- console.error('Unexpected error:', error);
765
- }
766
- }
767
- ```
768
978
 
769
979
  ## 🔧 Development Workflow
770
980
 
@@ -807,388 +1017,164 @@ npm install /path/to/kadi-core/kadi.build-core-X.Y.Z.tgz
807
1017
 
808
1018
  ### Core Classes
809
1019
 
810
- #### `KadiAbility`
1020
+ #### `KadiClient`
811
1021
 
812
- The main class for creating abilities.
1022
+ The unified class for all KADI operations - serving tools, calling remote services, and managing events.
813
1023
 
814
1024
  ```javascript
815
- import { KadiAbility } from '@kadi.build/core';
816
-
817
- const ability = new KadiAbility({
818
- name: 'my-ability',
819
- version: '1.0.0',
820
- description: 'My ability description',
821
- scope: 'global', // or process.env.KADI_AGENT_SCOPE
822
- protocol: 'stdio' // 'native', 'stdio', or 'broker'
1025
+ import { KadiClient } from '@kadi.build/core';
1026
+
1027
+ const client = new KadiClient({
1028
+ name: 'my-service',
1029
+ role: 'service', // 'agent', 'ability', or 'service'
1030
+ protocol: 'broker', // 'native', 'stdio', or 'broker'
1031
+ brokerUrls: ['ws://localhost:8080'],
1032
+ networks: ['global', 'custom-network']
823
1033
  });
824
1034
  ```
825
1035
 
826
- **Methods:**
827
-
828
- - `ability.method(name, handler, schema?)` - Register a method
829
- - `ability.serve()` - Start serving requests
830
- - `ability.publishEvent(eventName, data)` - Publish an event
831
- - `ability.on(event, handler)` - Listen to lifecycle events
832
-
833
- #### `loadAbility`
834
-
835
- Load an ability by name and protocol.
836
-
837
- ```javascript
838
- import { loadAbility } from '@kadi.build/core';
839
-
840
- const ability = await loadAbility('ability-name', 'protocol');
841
- ```
842
-
843
- **Returns:** A proxy object with:
844
-
845
- - Direct method calls (e.g., `ability.echo({ message: 'hello' })`)
846
- - `ability.__list()` - List available methods
847
- - `ability.events` - EventEmitter for subscribing to events (native/stdio protocols only)
848
- - `ability.call(method, params)` - Direct RPC call
849
-
850
- ### Utility Functions
1036
+ **Configuration Options:**
1037
+ - `name` - Service/agent name
1038
+ - `role` - Operating role: 'agent' (consumer), 'ability' (provider), 'service' (both)
1039
+ - `protocol` - Transport protocol to use
1040
+ - `brokerUrls` - Array of broker WebSocket URLs (for broker protocol)
1041
+ - `networks` - Network namespaces for tool discovery
1042
+ - `heartbeatIntervalSec` - Custom heartbeat interval (default: from broker)
1043
+
1044
+ **Tool Registration Methods:**
1045
+ - `registerTool(name, handler, schema?)` - Register a single tool
1046
+ - `getTools()` - Get list of registered tool names
1047
+ - `getToolNames()` - Get filtered list of tool names
1048
+ - `getToolHandler(name)` - Get a specific tool's handler
1049
+ - `getToolSchema(name)` - Get a specific tool's schema
1050
+ - `hasTool(name)` - Check if a tool is registered
1051
+
1052
+ **Remote Tool Invocation:**
1053
+ - `callTool(targetAgent, toolName, params)` - Call a remote tool via broker
1054
+ - `discoverRemoteTools(targetAgent)` - List tools from a remote agent
1055
+ - `loadAbility(name, protocol?, options?)` - Load an ability (compatibility)
1056
+
1057
+ **Event System:**
1058
+ - `publishEvent(eventName, data)` - Publish an event
1059
+ - `subscribeToEvent(pattern, callback)` - Subscribe with wildcards
1060
+ - `subscribeToEvents(patterns[], callback)` - Multiple subscriptions
1061
+ - `unsubscribeFromEvent(pattern, callback)` - Remove subscription
1062
+ - `onceEvent(pattern, callback)` - One-time subscription
1063
+
1064
+ **Connection Management:**
1065
+ - `serve()` - Start serving (stdio/native modes)
1066
+ - `connectToBrokers(urls?)` - Connect to broker(s)
1067
+ - `disconnect()` - Clean disconnect
1068
+ - `isConnected` - Check connection status
1069
+
1070
+ **Properties:**
1071
+ - `agentId` - Unique agent identifier
1072
+ - `name` - Service name
1073
+ - `role` - Current operating role
1074
+ - `protocol` - Active protocol
1075
+
1076
+ **Complete Usage Example:**
851
1077
 
852
1078
  ```javascript
853
- import {
854
- createLogger,
855
- getProjectJSON,
856
- getAbilityJSON,
857
- getBrokerUrl,
858
- runExecCommand
859
- } from '@kadi.build/core';
860
- ```
861
-
862
- ## 🎮 Running the Examples
863
-
864
- ### Quick Start with Example Agent
865
-
866
- The `@kadi.build/core` package includes a complete example demonstrating multi-language abilities (JavaScript and Go) with all three transport protocols.
867
-
868
- The `examples` folder contains:
869
-
870
- ```
871
- examples/
872
- ├── example-abilities/ # Sample abilities in different languages
873
- │ ├── echo-js/ # JavaScript ability using KadiAbility
874
- │ │ ├── agent.json # Ability configuration
875
- │ │ ├── service.js # Implementation
876
- │ │ └── package.json
877
- │ └── hash-go/ # Go ability demonstrating polyglot support
878
- │ ├── agent.json
879
- │ ├── go.mod
880
- │ └── cmd/hash_ability/main.go
881
- └── example-agent/ # Sample agent using the abilities
882
- ├── agent.json # Declares dependencies on abilities
883
- ├── index.js # Demonstrates all loading patterns
884
- └── package.json
885
- ```
886
-
887
- #### Step 0: Have the KADI broker running locally
888
-
889
- ```bash
890
- kadi broker up
891
- ```
892
-
893
- #### Step 1: Navigate to the example agent
894
-
895
- ```bash
896
- cd /path/to/kadi-core/examples/example-agent
897
- ```
898
-
899
- #### Step 2: Install abilities
900
-
901
- ```bash
902
- # Inside 'example-agent' folder
903
- kadi install
904
- ```
905
-
906
- This installs the `echo-js` (JavaScript) and `hash-go` (Go) abilities defined in the agent.json.
907
-
908
- #### Step 3: Run the agent
909
-
910
- ```bash
911
- kadi run
912
- ```
913
-
914
- #### Expected Output
915
-
916
- You should see the agent testing various abilities and protocols:
917
-
918
- ```
919
- === WORKING ABILITIES ===
920
-
921
- 🔧 Loading echo-js ability using native (Javascript)...
922
- Available methods: [ 'echo', 'say_message' ]
923
- Echo-js echo result: {
924
- echo: 'I am calling echo-js echo method from Javascript',
925
- timestamp: '2025-08-15T00:20:23.276Z',
926
- length: 48
927
- }
928
-
929
- 🔧 Loading echo-js ability using stdio (Javascript)...
930
- Available methods: [ 'echo', 'say_message' ]
931
- Echo-js echo result: {
932
- echo: 'I am calling echo-js echo method from Javascript',
933
- timestamp: '2025-08-15T00:20:23.339Z',
934
- length: 48
935
- }
936
- ```
937
-
938
- #### Step 4: Stop and Clean Up
939
-
940
- ```bash
941
- # Stop the agent
942
- Ctrl + C
943
-
944
- # Clean up installed abilities and node_modules
945
- kadi run clean
946
- ```
947
-
948
- ### What the Example Demonstrates
949
-
950
- 1. **Polyglot Abilities**: Go (`hash-go`) and JavaScript (`echo-js`) abilities working together
951
- 2. **Protocol Flexibility**: Same ability (`echo-js`) loaded via native, stdio, and broker
952
- 3. **Error Handling**: Graceful handling when calling non-existent methods
953
- 4. **Method Discovery**: Using `__list()` to discover available methods
954
- 5. **Schema Support**: Both inline and export-based schema definitions
955
- 6. **Event System**: Publishing and subscribing to events across protocols
956
- 7. **Event Timing**: Demonstrates the importance of subscribing to events before calling methods
957
-
958
- ### Exploring the Code
959
-
960
- **echo-js ability** (`examples/example-abilities/echo-js/service.js`):
961
-
962
- - Shows method registration with and without schemas
963
- - Demonstrates scope configuration for broker visibility
964
- - Uses environment variables for protocol adaptation
965
- - Includes event publishing examples using `publishEvent()`
966
- - Demonstrates how to emit events from method handlers
967
-
968
- **hash-go ability** (`examples/example-abilities/hash-go/`):
969
-
970
- - Demonstrates Go SDK integration
971
- - Shows stdio protocol with a compiled language
972
- - Implements SHA256 hashing as a service
973
-
974
- **example-agent** (`examples/example-agent/index.js`):
975
-
976
- - Shows all three loading patterns
977
- - Demonstrates error handling
978
- - Tests cross-language ability communication
979
- - Includes event subscription examples using `ability.events.on()`
980
- - Shows how to subscribe to events before calling methods
981
- - Demonstrates event handling across different protocols (native/stdio)
982
-
983
- ## 💡 Additional Examples
984
-
985
- ### Complete Echo Service Example
986
-
987
- **service.js**:
988
-
989
- ```javascript
990
- #!/usr/bin/env node
991
- import { KadiAbility } from '@kadi.build/core';
992
-
993
- const echoAbility = new KadiAbility({
994
- name: 'echo-service',
995
- version: '1.0.0',
996
- description: 'Multi-protocol echo service',
997
- scope: process.env.KADI_AGENT_SCOPE // Important for broker visibility
998
- });
999
-
1000
- // Simple echo with metadata
1001
- echoAbility.method('echo', async ({ message }) => {
1002
- // Publish events (works for native and stdio protocols)
1003
- echoAbility.publishEvent('echo:test-event', { from: 'message echo' });
1004
- echoAbility.publishEvent('echo:test-event', { from: 'message echo 2' });
1005
-
1006
- return {
1007
- echo: message,
1008
- timestamp: new Date().toISOString(),
1009
- length: message?.length || 0
1010
- };
1079
+ import { KadiClient } from '@kadi.build/core';
1080
+
1081
+ // Create a service that can both serve and consume
1082
+ const service = new KadiClient({
1083
+ name: 'math-service',
1084
+ role: 'service',
1085
+ protocol: 'broker',
1086
+ brokerUrls: ['ws://localhost:8080'],
1087
+ networks: ['global']
1011
1088
  });
1012
1089
 
1013
- // Transform text
1014
- echoAbility.method(
1015
- 'transform',
1016
- async ({ text, operations }) => {
1017
- let result = text;
1018
- for (const op of operations) {
1019
- switch (op) {
1020
- case 'upper':
1021
- result = result.toUpperCase();
1022
- break;
1023
- case 'lower':
1024
- result = result.toLowerCase();
1025
- break;
1026
- case 'reverse':
1027
- result = result.split('').reverse().join('');
1028
- break;
1029
- }
1030
- }
1031
- return { transformed: result };
1090
+ // Register tools with schemas
1091
+ service.registerTool(
1092
+ 'add',
1093
+ async ({ a, b }) => {
1094
+ // Publish event before processing
1095
+ await service.publishEvent('math.operation', {
1096
+ operation: 'add',
1097
+ inputs: { a, b }
1098
+ });
1099
+
1100
+ const result = a + b;
1101
+
1102
+ // Publish completion event
1103
+ await service.publishEvent('math.completed', {
1104
+ operation: 'add',
1105
+ result
1106
+ });
1107
+
1108
+ return { result };
1032
1109
  },
1033
1110
  {
1034
- description: 'Transform text with multiple operations',
1111
+ description: 'Add two numbers',
1035
1112
  inputSchema: {
1036
1113
  type: 'object',
1037
1114
  properties: {
1038
- text: { type: 'string' },
1039
- operations: {
1040
- type: 'array',
1041
- items: { enum: ['upper', 'lower', 'reverse'] }
1042
- }
1115
+ a: { type: 'number', description: 'First number' },
1116
+ b: { type: 'number', description: 'Second number' }
1043
1117
  },
1044
- required: ['text', 'operations']
1118
+ required: ['a', 'b']
1119
+ },
1120
+ outputSchema: {
1121
+ type: 'object',
1122
+ properties: {
1123
+ result: { type: 'number', description: 'Sum of a and b' }
1124
+ }
1045
1125
  }
1046
1126
  }
1047
1127
  );
1048
1128
 
1049
- echoAbility.serve().catch(console.error);
1050
- ```
1051
-
1052
- **Using the service**:
1053
-
1054
- ```javascript
1055
- import { loadAbility } from '@kadi.build/core';
1056
-
1057
- async function demo() {
1058
- // Try all protocols
1059
- for (const protocol of ['native', 'stdio', 'broker']) {
1060
- console.log(`\nTesting ${protocol} protocol:`);
1061
-
1062
- try {
1063
- const echo = await loadAbility('echo-service', protocol);
1064
-
1065
- // Subscribe to events (works for native and stdio protocols)
1066
- echo.events.on('echo:test-event', (data) => {
1067
- console.log(`Echo event received: ${data.from}`);
1068
- });
1069
-
1070
- // Simple echo
1071
- const result1 = await echo.echo({
1072
- message: `Hello from ${protocol}!`
1073
- });
1074
- console.log('Echo:', result1);
1075
-
1076
- // Transform
1077
- const result2 = await echo.transform({
1078
- text: 'Hello World',
1079
- operations: ['lower', 'reverse']
1080
- });
1081
- console.log('Transform:', result2);
1082
- } catch (error) {
1083
- console.error(`${protocol} failed:`, error.message);
1084
- }
1085
- }
1086
- }
1087
-
1088
- demo();
1089
- ```
1090
-
1091
- ## 🎯 Real-World Event Example
1092
-
1093
- Here's the exact event pattern used in the example code:
1094
-
1095
- **In your ability (service.js):**
1096
-
1097
- ```javascript
1098
- const echoAbility = new KadiAbility({
1099
- name: 'echo-js',
1100
- version: '0.0.1',
1101
- scope: process.env.KADI_AGENT_SCOPE
1129
+ // Subscribe to events from other services
1130
+ service.subscribeToEvent('system.*', (data) => {
1131
+ console.log('System event:', data);
1102
1132
  });
1103
1133
 
1104
- async function echo(message) {
1105
- const timestamp = new Date().toISOString();
1106
- const messageText = message?.message ?? message ?? '';
1107
- const length = messageText.length;
1108
-
1109
- // Publish events (works for native and stdio protocols)
1110
- echoAbility.publishEvent('echo:test-event', { from: 'message echo' });
1111
- echoAbility.publishEvent('echo:test-event', { from: 'message echo 2' });
1112
-
1113
- return { echo: messageText, timestamp, length };
1114
- }
1115
-
1116
- echoAbility.method('echo', echo);
1117
- ```
1118
-
1119
- **In your agent (index.js):**
1134
+ // Connect to broker
1135
+ await service.connectToBrokers();
1120
1136
 
1121
- ```javascript
1122
- // Subscribe first
1123
- echoJsAbility.events.on('echo:test-event', (data) => {
1124
- console.log(`[NATIVE] echo:test-event received:`, data);
1125
- });
1137
+ // Call remote tools
1138
+ const result = await service.callTool(
1139
+ 'translator-service',
1140
+ 'translate',
1141
+ { text: 'Hello', to: 'es' }
1142
+ );
1126
1143
 
1127
- // Now trigger the method that emits the event
1128
- const echoJsResult = await echoJsAbility.echo({
1129
- message: 'I am calling echo-js echo method from Javascript'
1144
+ // Graceful shutdown
1145
+ process.on('SIGTERM', async () => {
1146
+ await service.disconnect();
1147
+ process.exit(0);
1130
1148
  });
1131
1149
  ```
1132
1150
 
1133
- **Key Points:**
1134
-
1135
- - Events work for `native` and `stdio` protocols only
1136
- - Always subscribe to events BEFORE calling methods that emit them
1137
- - Events are emitted immediately when `publishEvent()` is called
1138
- - Use `ability.events.on()` to subscribe to custom events
1139
- - Use `ability.on()` to listen to lifecycle events
1140
-
1141
- ## 🐛 Troubleshooting
1142
-
1143
- ### Common Issues
1144
-
1145
- **Ability Not Found**
1146
-
1147
- ```
1148
- Error: Ability 'my-ability' version '1.0.0' is not installed
1149
- ```
1150
-
1151
- **Solution**: Run `kadi install` to install project dependencies
1152
-
1153
- **Broker Connection Failed**
1154
-
1155
- ```
1156
- Error: Failed to connect to KADI Broker at ws://localhost:8080
1157
- ```
1158
-
1159
- **Solution**: Ensure the broker is running: `kadi broker start`
1160
-
1161
- **Method Not Found**
1162
-
1163
- ```
1164
- Error: Method 'someMethod' is not exposed by this ability
1165
- ```
1151
+ #### `loadAbility`
1166
1152
 
1167
- **Solution**: Check ability exports or use `__list()` to see available methods
1153
+ Load an ability by name and protocol.
1168
1154
 
1169
- **Protocol Not Supported**
1155
+ ```javascript
1156
+ import { loadAbility } from '@kadi.build/core';
1170
1157
 
1171
- ```
1172
- Error: Unsupported ability interface protocol: custom
1158
+ const ability = await loadAbility('ability-name', 'protocol');
1173
1159
  ```
1174
1160
 
1175
- **Solution**: Ability must define the protocol in its `agent.json` interfaces
1176
-
1177
- **Event Subscription Issues**
1178
-
1179
- ```
1180
- TypeError: Cannot read property 'on' of undefined
1181
- ```
1161
+ **Returns:** A proxy object with:
1182
1162
 
1183
- **Solution**: Make sure you're accessing `ability.events.on()` not `ability.on()` for custom events
1163
+ - Direct method calls (e.g., `ability.echo({ message: 'hello' })`)
1164
+ - `ability.events` - EventEmitter for subscribing to events (native/stdio protocols only)
1165
+ - `ability.__call(method, params)` - Direct RPC call
1184
1166
 
1185
- **Event Protocol Support**
1167
+ ### Utility Functions
1186
1168
 
1169
+ ```javascript
1170
+ import {
1171
+ createLogger,
1172
+ getProjectJSON,
1173
+ getAbilityJSON,
1174
+ getBrokerUrl,
1175
+ runExecCommand
1176
+ } from '@kadi.build/core';
1187
1177
  ```
1188
- Events are only supported for 'native' and 'stdio' protocols
1189
- ```
1190
-
1191
- **Solution**: Events currently work for `native` and `stdio` protocols only. Broker protocol event support is planned for future releases.
1192
1178
 
1193
1179
  ### Debug Mode
1194
1180
 
@@ -1214,22 +1200,13 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
1214
1200
  4. Push to the branch (`git push origin feature/amazing-feature`)
1215
1201
  5. Open a Pull Request
1216
1202
 
1217
- ## 📄 License
1218
1203
 
1219
- MIT License - see [LICENSE](LICENSE) file for details
1220
1204
 
1221
1205
  ## 🔗 Related Projects
1222
1206
 
1223
1207
  - [@kadi.build/cli](https://gitlab.com/humin-game-lab/kadi/kadi) - Command-line interface
1224
1208
  - [@kadi.build/broker](https://gitlab.com/humin-game-lab/kadi/kadi-broker) - The KADI broker
1225
1209
 
1226
- ## 📚 Resources
1227
-
1228
- - [Official Documentation](https://docs.kadi.build)
1229
- - [API Reference](https://docs.kadi.build/api)
1230
- - [Tutorial: Building Your First Ability](https://docs.kadi.build/tutorial)
1231
- - [Architecture Deep Dive](https://docs.kadi.build/architecture)
1232
-
1233
1210
  ---
1234
1211
 
1235
1212
  Built with ❤️ by the KADI team