@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.
- package/README.md +571 -594
- package/dist/KadiClient.d.ts +303 -0
- package/dist/KadiClient.d.ts.map +1 -0
- package/dist/KadiClient.js +1162 -0
- package/dist/KadiClient.js.map +1 -0
- package/dist/errors/error-codes.d.ts +215 -0
- package/dist/errors/error-codes.d.ts.map +1 -0
- package/dist/errors/error-codes.js +295 -0
- package/dist/errors/error-codes.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/loadAbility.d.ts +65 -0
- package/dist/loadAbility.d.ts.map +1 -0
- package/dist/loadAbility.js +335 -0
- package/dist/loadAbility.js.map +1 -0
- package/dist/messages/BrokerMessages.d.ts +84 -0
- package/dist/messages/BrokerMessages.d.ts.map +1 -0
- package/dist/messages/BrokerMessages.js +127 -0
- package/dist/messages/BrokerMessages.js.map +1 -0
- package/dist/messages/MessageBuilder.d.ts +83 -0
- package/dist/messages/MessageBuilder.d.ts.map +1 -0
- package/dist/messages/MessageBuilder.js +144 -0
- package/dist/messages/MessageBuilder.js.map +1 -0
- package/dist/schemas/events.schemas.d.ts +177 -0
- package/dist/schemas/events.schemas.d.ts.map +1 -0
- package/dist/schemas/events.schemas.js +265 -0
- package/dist/schemas/events.schemas.js.map +1 -0
- package/dist/schemas/index.d.ts +3 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +4 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/kadi.schemas.d.ts +70 -0
- package/dist/schemas/kadi.schemas.d.ts.map +1 -0
- package/dist/schemas/kadi.schemas.js +120 -0
- package/dist/schemas/kadi.schemas.js.map +1 -0
- package/dist/transports/BrokerTransport.d.ts +96 -0
- package/dist/transports/BrokerTransport.d.ts.map +1 -0
- package/dist/transports/BrokerTransport.js +145 -0
- package/dist/transports/BrokerTransport.js.map +1 -0
- package/dist/transports/NativeTransport.d.ts +92 -0
- package/dist/transports/NativeTransport.d.ts.map +1 -0
- package/dist/transports/NativeTransport.js +221 -0
- package/dist/transports/NativeTransport.js.map +1 -0
- package/dist/transports/StdioTransport.d.ts +112 -0
- package/dist/transports/StdioTransport.d.ts.map +1 -0
- package/dist/transports/StdioTransport.js +440 -0
- package/dist/transports/StdioTransport.js.map +1 -0
- package/dist/transports/Transport.d.ts +93 -0
- package/dist/transports/Transport.d.ts.map +1 -0
- package/dist/transports/Transport.js +13 -0
- package/dist/transports/Transport.js.map +1 -0
- package/dist/types/broker.d.ts +31 -0
- package/dist/types/broker.d.ts.map +1 -0
- package/dist/types/broker.js +6 -0
- package/dist/types/broker.js.map +1 -0
- package/dist/types/core.d.ts +138 -0
- package/dist/types/core.d.ts.map +1 -0
- package/dist/types/core.js +26 -0
- package/dist/types/core.js.map +1 -0
- package/dist/types/events.d.ts +186 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +16 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +13 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/protocol.d.ts +160 -0
- package/dist/types/protocol.d.ts.map +1 -0
- package/dist/types/protocol.js +5 -0
- package/dist/types/protocol.js.map +1 -0
- package/dist/utils/agentUtils.d.ts +102 -0
- package/dist/utils/agentUtils.d.ts.map +1 -0
- package/dist/utils/agentUtils.js +166 -0
- package/dist/utils/agentUtils.js.map +1 -0
- package/dist/utils/commandUtils.d.ts +45 -0
- package/dist/utils/commandUtils.d.ts.map +1 -0
- package/dist/utils/commandUtils.js +145 -0
- package/dist/utils/commandUtils.js.map +1 -0
- package/dist/utils/configUtils.d.ts +55 -0
- package/dist/utils/configUtils.d.ts.map +1 -0
- package/dist/utils/configUtils.js +100 -0
- package/dist/utils/configUtils.js.map +1 -0
- package/dist/utils/logger.d.ts +59 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +122 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/pathUtils.d.ts +48 -0
- package/dist/utils/pathUtils.d.ts.map +1 -0
- package/dist/utils/pathUtils.js +128 -0
- package/dist/utils/pathUtils.js.map +1 -0
- package/package.json +56 -5
- package/agent.json +0 -18
- package/examples/example-abilities/echo-js/README.md +0 -131
- package/examples/example-abilities/echo-js/agent.json +0 -63
- package/examples/example-abilities/echo-js/package.json +0 -24
- package/examples/example-abilities/echo-js/service.js +0 -43
- package/examples/example-abilities/hash-go/agent.json +0 -53
- package/examples/example-abilities/hash-go/cmd/hash_ability/main.go +0 -340
- package/examples/example-abilities/hash-go/go.mod +0 -3
- package/examples/example-agent/abilities/echo-js/0.0.1/README.md +0 -131
- package/examples/example-agent/abilities/echo-js/0.0.1/agent.json +0 -63
- package/examples/example-agent/abilities/echo-js/0.0.1/package-lock.json +0 -93
- package/examples/example-agent/abilities/echo-js/0.0.1/package.json +0 -24
- package/examples/example-agent/abilities/echo-js/0.0.1/service.js +0 -41
- package/examples/example-agent/abilities/hash-go/0.0.1/agent.json +0 -53
- package/examples/example-agent/abilities/hash-go/0.0.1/bin/hash_ability +0 -0
- package/examples/example-agent/abilities/hash-go/0.0.1/cmd/hash_ability/main.go +0 -340
- package/examples/example-agent/abilities/hash-go/0.0.1/go.mod +0 -3
- package/examples/example-agent/agent.json +0 -39
- package/examples/example-agent/index.js +0 -102
- package/examples/example-agent/package-lock.json +0 -93
- package/examples/example-agent/package.json +0 -17
- package/src/KadiAbility.js +0 -478
- package/src/index.js +0 -65
- package/src/loadAbility.js +0 -1086
- package/src/servers/BaseRpcServer.js +0 -404
- package/src/servers/BrokerRpcServer.js +0 -776
- package/src/servers/StdioRpcServer.js +0 -360
- package/src/transport/BrokerMessageBuilder.js +0 -377
- package/src/transport/IpcMessageBuilder.js +0 -1229
- package/src/utils/agentUtils.js +0 -137
- package/src/utils/commandUtils.js +0 -64
- package/src/utils/configUtils.js +0 -72
- package/src/utils/logger.js +0 -161
- 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
|
-
|
|
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 {
|
|
58
|
+
import { KadiClient } from '@kadi.build/core';
|
|
52
59
|
|
|
53
|
-
// Create
|
|
54
|
-
const
|
|
55
|
-
name: 'math-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
|
61
|
-
|
|
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
|
-
|
|
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
|
|
235
|
+
## 🛠️ Creating Services with KadiClient
|
|
189
236
|
|
|
190
|
-
### Basic
|
|
237
|
+
### Basic Service Structure
|
|
191
238
|
|
|
192
239
|
```javascript
|
|
193
|
-
import {
|
|
240
|
+
import { KadiClient } from '@kadi.build/core';
|
|
194
241
|
|
|
195
|
-
const
|
|
196
|
-
name: 'echo-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
|
203
|
-
|
|
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:
|
|
209
|
-
|
|
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
|
-
|
|
285
|
+
service.serve().catch(console.error);
|
|
239
286
|
```
|
|
240
287
|
|
|
241
288
|
### Event Publishing and Subscription
|
|
242
289
|
|
|
243
|
-
**
|
|
290
|
+
**Full Support**: Events work across all protocols - native, stdio, and broker.
|
|
244
291
|
|
|
245
|
-
|
|
292
|
+
KadiClient provides a unified event system that works consistently across all transport protocols:
|
|
246
293
|
|
|
247
294
|
```javascript
|
|
248
|
-
// In your
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
|
|
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
|
|
261
|
-
const
|
|
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
|
|
264
|
-
|
|
265
|
-
console.log(
|
|
321
|
+
// Subscribe to events using patterns
|
|
322
|
+
client.subscribeToEvent('echo.*', (data) => {
|
|
323
|
+
console.log('Echo event received:', data);
|
|
266
324
|
});
|
|
267
325
|
|
|
268
|
-
//
|
|
269
|
-
const
|
|
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
|
-
**
|
|
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
|
-
###
|
|
498
|
+
### Transport Architecture and Loading Sequence
|
|
367
499
|
|
|
368
|
-
The loading process
|
|
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
|
|
509
|
+
participant Service
|
|
377
510
|
participant Broker
|
|
378
511
|
|
|
379
512
|
Client->>loadAbility: loadAbility('echo-js', protocol?)
|
|
380
|
-
loadAbility->>FileSystem: Read
|
|
381
|
-
FileSystem-->>loadAbility: Return
|
|
382
|
-
|
|
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->>
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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->>
|
|
393
|
-
ChildProcess
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
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->>
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
Broker
|
|
414
|
-
|
|
415
|
-
|
|
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->>
|
|
432
|
-
Client
|
|
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
|
-
###
|
|
564
|
+
### KadiClient Architecture: Tool Registration & Serving
|
|
436
565
|
|
|
437
|
-
How
|
|
566
|
+
How KadiClient registers tools and starts serving:
|
|
438
567
|
|
|
439
568
|
```mermaid
|
|
440
569
|
sequenceDiagram
|
|
441
570
|
participant Dev as Developer
|
|
442
|
-
participant
|
|
443
|
-
participant Server as RpcServer
|
|
571
|
+
participant KC as KadiClient
|
|
444
572
|
participant Transport
|
|
445
|
-
participant
|
|
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
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
-
|
|
453
|
-
KA->>KA: Store handler in methodHandlers
|
|
454
|
-
KA->>KA: Store schema in methodMCPSchemas
|
|
630
|
+
### Event System Architecture
|
|
455
631
|
|
|
456
|
-
|
|
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
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
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
|
|
493
|
-
participant Ability as Ability (Child)
|
|
691
|
+
participant Client as KadiClient
|
|
494
692
|
participant Broker as KADI Broker
|
|
495
|
-
participant
|
|
496
|
-
|
|
497
|
-
Note over
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
Broker-->>
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
Broker
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
Broker
|
|
527
|
-
|
|
528
|
-
Broker
|
|
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
|
|
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
|
-
//
|
|
594
|
-
const
|
|
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;
|
|
642
|
-
const serviceName = process.env.KADI_SERVICE_NAME;
|
|
643
|
-
const agentScope = process.env.KADI_AGENT_SCOPE;
|
|
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
|
-
|
|
884
|
+
KadiClient provides a comprehensive event system that works across all protocols:
|
|
674
885
|
|
|
675
|
-
#### 1. Lifecycle Events
|
|
886
|
+
#### 1. Lifecycle Events
|
|
676
887
|
|
|
677
|
-
|
|
888
|
+
Monitor the service lifecycle:
|
|
678
889
|
|
|
679
890
|
```javascript
|
|
680
|
-
const
|
|
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
|
-
|
|
683
|
-
|
|
684
|
-
console.log(`Started with ${protocol}, methods: ${methods.join(', ')}`);
|
|
898
|
+
client.on('disconnected', () => {
|
|
899
|
+
console.log('Disconnected from broker');
|
|
685
900
|
});
|
|
686
901
|
|
|
687
|
-
|
|
688
|
-
|
|
902
|
+
// Tool invocation events
|
|
903
|
+
client.on('tool:invoked', ({ toolName, params }) => {
|
|
904
|
+
console.log(`Tool ${toolName} invoked with:`, params);
|
|
689
905
|
});
|
|
690
906
|
|
|
691
|
-
|
|
692
|
-
|
|
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
|
-
|
|
697
|
-
console.error('
|
|
911
|
+
client.on('error', (error) => {
|
|
912
|
+
console.error('Service error:', error);
|
|
698
913
|
});
|
|
699
914
|
```
|
|
700
915
|
|
|
701
|
-
#### 2. Custom Events (
|
|
916
|
+
#### 2. Custom Events (All Protocols)
|
|
702
917
|
|
|
703
|
-
|
|
918
|
+
Publish and subscribe to custom events across all transport protocols:
|
|
704
919
|
|
|
705
920
|
```javascript
|
|
706
|
-
//
|
|
707
|
-
const
|
|
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
|
-
|
|
928
|
+
publisher.registerTool('process', async ({ data }) => {
|
|
710
929
|
// Publish events during processing
|
|
711
|
-
|
|
712
|
-
timestamp:
|
|
930
|
+
await publisher.publishEvent('process.started', {
|
|
931
|
+
timestamp: Date.now(),
|
|
932
|
+
data
|
|
713
933
|
});
|
|
714
934
|
|
|
715
|
-
//
|
|
935
|
+
// Do work...
|
|
936
|
+
const result = await processData(data);
|
|
716
937
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
938
|
+
await publisher.publishEvent('process.completed', {
|
|
939
|
+
timestamp: Date.now(),
|
|
940
|
+
result
|
|
720
941
|
});
|
|
721
942
|
|
|
722
|
-
return
|
|
943
|
+
return result;
|
|
723
944
|
});
|
|
724
945
|
|
|
725
|
-
//
|
|
726
|
-
const
|
|
946
|
+
// Subscribing to events
|
|
947
|
+
const subscriber = new KadiClient({
|
|
948
|
+
name: 'event-subscriber',
|
|
949
|
+
role: 'agent',
|
|
950
|
+
protocol: 'broker'
|
|
951
|
+
});
|
|
727
952
|
|
|
728
|
-
// Subscribe
|
|
729
|
-
|
|
730
|
-
console.log(
|
|
953
|
+
// Subscribe with wildcards
|
|
954
|
+
subscriber.subscribeToEvent('process.*', (data) => {
|
|
955
|
+
console.log('Process event:', data);
|
|
731
956
|
});
|
|
732
957
|
|
|
733
|
-
|
|
734
|
-
|
|
958
|
+
// One-time subscription
|
|
959
|
+
subscriber.onceEvent('process.completed', (data) => {
|
|
960
|
+
console.log('First completion:', data);
|
|
735
961
|
});
|
|
736
962
|
|
|
737
|
-
//
|
|
738
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
#### `
|
|
1020
|
+
#### `KadiClient`
|
|
811
1021
|
|
|
812
|
-
The
|
|
1022
|
+
The unified class for all KADI operations - serving tools, calling remote services, and managing events.
|
|
813
1023
|
|
|
814
1024
|
```javascript
|
|
815
|
-
import {
|
|
816
|
-
|
|
817
|
-
const
|
|
818
|
-
name: 'my-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
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
|
-
**
|
|
827
|
-
|
|
828
|
-
- `
|
|
829
|
-
- `
|
|
830
|
-
- `
|
|
831
|
-
- `
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
- `
|
|
849
|
-
|
|
850
|
-
|
|
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
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
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
|
-
//
|
|
1014
|
-
|
|
1015
|
-
'
|
|
1016
|
-
async ({
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
return {
|
|
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: '
|
|
1111
|
+
description: 'Add two numbers',
|
|
1035
1112
|
inputSchema: {
|
|
1036
1113
|
type: 'object',
|
|
1037
1114
|
properties: {
|
|
1038
|
-
|
|
1039
|
-
|
|
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: ['
|
|
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
|
-
|
|
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
|
-
|
|
1105
|
-
|
|
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
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
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
|
-
//
|
|
1128
|
-
|
|
1129
|
-
|
|
1144
|
+
// Graceful shutdown
|
|
1145
|
+
process.on('SIGTERM', async () => {
|
|
1146
|
+
await service.disconnect();
|
|
1147
|
+
process.exit(0);
|
|
1130
1148
|
});
|
|
1131
1149
|
```
|
|
1132
1150
|
|
|
1133
|
-
|
|
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
|
-
|
|
1153
|
+
Load an ability by name and protocol.
|
|
1168
1154
|
|
|
1169
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
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
|