@kadi.build/core 0.0.1-alpha.1 → 0.0.1-alpha.10

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 (179) hide show
  1. package/README.md +1387 -214
  2. package/dist/abilities/AbilityCache.d.ts +242 -0
  3. package/dist/abilities/AbilityCache.d.ts.map +1 -0
  4. package/dist/abilities/AbilityCache.js +285 -0
  5. package/dist/abilities/AbilityCache.js.map +1 -0
  6. package/dist/abilities/AbilityContext.d.ts +215 -0
  7. package/dist/abilities/AbilityContext.d.ts.map +1 -0
  8. package/dist/abilities/AbilityContext.js +36 -0
  9. package/dist/abilities/AbilityContext.js.map +1 -0
  10. package/dist/abilities/AbilityLoader.d.ts +177 -0
  11. package/dist/abilities/AbilityLoader.d.ts.map +1 -0
  12. package/dist/abilities/AbilityLoader.js +277 -0
  13. package/dist/abilities/AbilityLoader.js.map +1 -0
  14. package/dist/abilities/AbilityProxy.d.ts +463 -0
  15. package/dist/abilities/AbilityProxy.d.ts.map +1 -0
  16. package/dist/abilities/AbilityProxy.js +511 -0
  17. package/dist/abilities/AbilityProxy.js.map +1 -0
  18. package/dist/abilities/AbilityValidator.d.ts +172 -0
  19. package/dist/abilities/AbilityValidator.d.ts.map +1 -0
  20. package/dist/abilities/AbilityValidator.js +253 -0
  21. package/dist/abilities/AbilityValidator.js.map +1 -0
  22. package/dist/abilities/index.d.ts +26 -0
  23. package/dist/abilities/index.d.ts.map +1 -0
  24. package/dist/abilities/index.js +23 -0
  25. package/dist/abilities/index.js.map +1 -0
  26. package/dist/abilities/types.d.ts +156 -0
  27. package/dist/abilities/types.d.ts.map +1 -0
  28. package/dist/abilities/types.js +10 -0
  29. package/dist/abilities/types.js.map +1 -0
  30. package/dist/api/index.d.ts +92 -0
  31. package/dist/api/index.d.ts.map +1 -0
  32. package/dist/api/index.js +124 -0
  33. package/dist/api/index.js.map +1 -0
  34. package/dist/broker/BrokerConnection.d.ts +253 -0
  35. package/dist/broker/BrokerConnection.d.ts.map +1 -0
  36. package/dist/broker/BrokerConnection.js +434 -0
  37. package/dist/broker/BrokerConnection.js.map +1 -0
  38. package/dist/broker/BrokerConnectionManager.d.ts +216 -0
  39. package/dist/broker/BrokerConnectionManager.d.ts.map +1 -0
  40. package/dist/broker/BrokerConnectionManager.js +305 -0
  41. package/dist/broker/BrokerConnectionManager.js.map +1 -0
  42. package/dist/broker/BrokerProtocol.d.ts +280 -0
  43. package/dist/broker/BrokerProtocol.d.ts.map +1 -0
  44. package/dist/broker/BrokerProtocol.js +466 -0
  45. package/dist/broker/BrokerProtocol.js.map +1 -0
  46. package/dist/broker/index.d.ts +9 -0
  47. package/dist/broker/index.d.ts.map +1 -0
  48. package/dist/broker/index.js +9 -0
  49. package/dist/broker/index.js.map +1 -0
  50. package/dist/client/KadiClient.d.ts +270 -0
  51. package/dist/client/KadiClient.d.ts.map +1 -0
  52. package/dist/client/KadiClient.js +492 -0
  53. package/dist/client/KadiClient.js.map +1 -0
  54. package/dist/client/index.d.ts +7 -0
  55. package/dist/client/index.d.ts.map +1 -0
  56. package/dist/client/index.js +7 -0
  57. package/dist/client/index.js.map +1 -0
  58. package/dist/config/ConfigLoader.d.ts +138 -0
  59. package/dist/config/ConfigLoader.d.ts.map +1 -0
  60. package/dist/config/ConfigLoader.js +226 -0
  61. package/dist/config/ConfigLoader.js.map +1 -0
  62. package/dist/config/ConfigResolver.d.ts +135 -0
  63. package/dist/config/ConfigResolver.d.ts.map +1 -0
  64. package/dist/config/ConfigResolver.js +282 -0
  65. package/dist/config/ConfigResolver.js.map +1 -0
  66. package/dist/config/index.d.ts +8 -0
  67. package/dist/config/index.d.ts.map +1 -0
  68. package/dist/config/index.js +8 -0
  69. package/dist/config/index.js.map +1 -0
  70. package/dist/errors/index.d.ts +9 -0
  71. package/dist/errors/index.d.ts.map +1 -0
  72. package/dist/errors/index.js +8 -0
  73. package/dist/errors/index.js.map +1 -0
  74. package/dist/events/EventHub.d.ts +172 -0
  75. package/dist/events/EventHub.d.ts.map +1 -0
  76. package/dist/events/EventHub.js +333 -0
  77. package/dist/events/EventHub.js.map +1 -0
  78. package/dist/events/index.d.ts +7 -0
  79. package/dist/events/index.d.ts.map +1 -0
  80. package/dist/events/index.js +7 -0
  81. package/dist/events/index.js.map +1 -0
  82. package/dist/index.d.ts +49 -0
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/index.js +63 -0
  85. package/dist/index.js.map +1 -0
  86. package/dist/messages/index.d.ts +33 -0
  87. package/dist/messages/index.d.ts.map +1 -0
  88. package/dist/messages/index.js +33 -0
  89. package/dist/messages/index.js.map +1 -0
  90. package/dist/schemas/index.d.ts +19 -0
  91. package/dist/schemas/index.d.ts.map +1 -0
  92. package/dist/schemas/index.js +25 -0
  93. package/dist/schemas/index.js.map +1 -0
  94. package/dist/schemas/kadi-extensions.d.ts +231 -0
  95. package/dist/schemas/kadi-extensions.d.ts.map +1 -0
  96. package/dist/schemas/kadi-extensions.js +14 -0
  97. package/dist/schemas/kadi-extensions.js.map +1 -0
  98. package/dist/schemas/mcp/schema.d.ts +1399 -0
  99. package/dist/schemas/mcp/schema.d.ts.map +1 -0
  100. package/dist/schemas/mcp/schema.js +53 -0
  101. package/dist/schemas/mcp/schema.js.map +1 -0
  102. package/dist/schemas/mcp/version.d.ts +37 -0
  103. package/dist/schemas/mcp/version.d.ts.map +1 -0
  104. package/dist/schemas/mcp/version.js +39 -0
  105. package/dist/schemas/mcp/version.js.map +1 -0
  106. package/dist/schemas/schema-builders.d.ts +178 -0
  107. package/dist/schemas/schema-builders.d.ts.map +1 -0
  108. package/dist/schemas/schema-builders.js +258 -0
  109. package/dist/schemas/schema-builders.js.map +1 -0
  110. package/dist/tools/ToolRegistry.d.ts +256 -0
  111. package/dist/tools/ToolRegistry.d.ts.map +1 -0
  112. package/dist/tools/ToolRegistry.js +340 -0
  113. package/dist/tools/ToolRegistry.js.map +1 -0
  114. package/dist/tools/index.d.ts +7 -0
  115. package/dist/tools/index.d.ts.map +1 -0
  116. package/dist/tools/index.js +7 -0
  117. package/dist/tools/index.js.map +1 -0
  118. package/dist/transports/BrokerTransport.d.ts +151 -0
  119. package/dist/transports/BrokerTransport.d.ts.map +1 -0
  120. package/dist/transports/BrokerTransport.js +261 -0
  121. package/dist/transports/BrokerTransport.js.map +1 -0
  122. package/dist/transports/NativeTransport.d.ts +149 -0
  123. package/dist/transports/NativeTransport.d.ts.map +1 -0
  124. package/dist/transports/NativeTransport.js +302 -0
  125. package/dist/transports/NativeTransport.js.map +1 -0
  126. package/dist/transports/StdioTransport.d.ts +172 -0
  127. package/dist/transports/StdioTransport.d.ts.map +1 -0
  128. package/dist/transports/StdioTransport.js +410 -0
  129. package/dist/transports/StdioTransport.js.map +1 -0
  130. package/dist/transports/index.d.ts +10 -0
  131. package/dist/transports/index.d.ts.map +1 -0
  132. package/dist/transports/index.js +9 -0
  133. package/dist/transports/index.js.map +1 -0
  134. package/dist/types/broker.d.ts +301 -0
  135. package/dist/types/broker.d.ts.map +1 -0
  136. package/dist/types/broker.js +46 -0
  137. package/dist/types/broker.js.map +1 -0
  138. package/dist/types/config.d.ts +325 -0
  139. package/dist/types/config.d.ts.map +1 -0
  140. package/dist/types/config.js +17 -0
  141. package/dist/types/config.js.map +1 -0
  142. package/dist/types/errors.d.ts +178 -0
  143. package/dist/types/errors.d.ts.map +1 -0
  144. package/dist/types/errors.js +165 -0
  145. package/dist/types/errors.js.map +1 -0
  146. package/dist/types/events.d.ts +210 -0
  147. package/dist/types/events.d.ts.map +1 -0
  148. package/dist/types/events.js +8 -0
  149. package/dist/types/events.js.map +1 -0
  150. package/dist/types/index.d.ts +32 -0
  151. package/dist/types/index.d.ts.map +1 -0
  152. package/dist/types/index.js +21 -0
  153. package/dist/types/index.js.map +1 -0
  154. package/dist/types/protocol.d.ts +48 -0
  155. package/dist/types/protocol.d.ts.map +1 -0
  156. package/dist/types/protocol.js +11 -0
  157. package/dist/types/protocol.js.map +1 -0
  158. package/dist/types/tools.d.ts +67 -0
  159. package/dist/types/tools.d.ts.map +1 -0
  160. package/dist/types/tools.js +16 -0
  161. package/dist/types/tools.js.map +1 -0
  162. package/dist/types/transport.d.ts +250 -0
  163. package/dist/types/transport.d.ts.map +1 -0
  164. package/dist/types/transport.js +18 -0
  165. package/dist/types/transport.js.map +1 -0
  166. package/dist/validation/SchemaValidator.d.ts +208 -0
  167. package/dist/validation/SchemaValidator.d.ts.map +1 -0
  168. package/dist/validation/SchemaValidator.js +411 -0
  169. package/dist/validation/SchemaValidator.js.map +1 -0
  170. package/dist/validation/index.d.ts +11 -0
  171. package/dist/validation/index.d.ts.map +1 -0
  172. package/dist/validation/index.js +10 -0
  173. package/dist/validation/index.js.map +1 -0
  174. package/package.json +69 -5
  175. package/agent.json +0 -18
  176. package/broker.js +0 -214
  177. package/index.js +0 -370
  178. package/ipc.js +0 -220
  179. package/ipcInterfaces/pythonAbilityIPC.py +0 -177
package/README.md CHANGED
@@ -1,306 +1,1479 @@
1
1
  # @kadi.build/core
2
2
 
3
- ---
3
+ > A comprehensive toolkit for building and managing KADI abilities with multiple transport protocols
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@kadi.build/core.svg)](https://www.npmjs.com/package/@kadi.build/core)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## 🎯 Overview
9
+
10
+ `@kadi.build/core` is the foundational library for creating KADI abilities - modular, protocol-agnostic components that can communicate via multiple transport layers. Whether you're building local tools, distributed systems, or high-performance native modules, this toolkit provides a unified, developer-friendly API that abstracts away transport complexity.
11
+
12
+ ## 📚 Table of Contents
13
+
14
+ - [🎯 Overview](#-overview)
15
+ - [📦 Installation](#-installation)
16
+ - [🚀 Quick Start](#-quick-start)
17
+ - [🔌 Transport Protocols](#-transport-protocols)
18
+ - [🛠️ Creating Abilities](#-creating-abilities)
19
+ - [🤖 Creating Agents](#-creating-agents)
20
+ - [🔄 Architecture Deep Dive](#-architecture-deep-dive)
21
+ - [📥 Loading Abilities](#-loading-abilities)
22
+ - [⚙️ Configuration](#-configuration)
23
+ - [🚀 Advanced Usage](#-advanced-usage)
24
+ - [🔧 Development Workflow](#-development-workflow)
25
+ - [📖 API Reference](#-api-reference)
26
+ - [🎮 Running the Examples](#-running-the-examples)
27
+ - [💡 Additional Examples](#-additional-examples)
28
+ - [🐛 Troubleshooting](#-troubleshooting)
29
+ - [🤝 Contributing](#-contributing)
30
+ - [📄 License](#-license)
31
+ - [🔗 Related Projects](#-related-projects)
32
+ - [📚 Resources](#-resources)
33
+
34
+ ## 📦 Installation
35
+
36
+ ```bash
37
+ npm install @kadi.build/core
38
+ ```
39
+
40
+ For global CLI tools:
41
+
42
+ ```bash
43
+ npm install -g @kadi.build/cli
44
+ ```
45
+
46
+ ## 🚀 Quick Start
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
+
53
+ ### Creating Your First Ability
54
+
55
+ ```javascript
56
+ #!/usr/bin/env node
57
+ import { KadiClient } from '@kadi.build/core';
58
+
59
+ // Create a ability instance
60
+ const mathAbility = new KadiClient({
61
+ name: 'math-ability',
62
+ role: 'ability', // 'agent', 'ability'
63
+ protocol: 'stdio' // 'native', 'stdio', or 'broker'
64
+ });
65
+
66
+ // Register a tool
67
+ mathAbility.registerTool('add', async ({ a, b }) => {
68
+ return { result: a + b };
69
+ });
70
+
71
+ // Start serving requests
72
+ mathAbility.serve().catch(console.error);
73
+ ```
74
+
75
+ ### Loading and Using Abilities
4
76
 
5
- The `@kadi.build/core` module is a comprehensive toolkit for developers integrating with the KADI infrastructure. This module simplifies tasks such as managing `agent.json` files, spawning processes, interacting with brokers, and handling interprocess communication (IPC).
77
+ ```javascript
78
+ import { loadAbility } from '@kadi.build/core';
79
+
80
+ async function main() {
81
+ // Load an ability (uses first interface defined in agent.json)
82
+ const math = await loadAbility('math-ability');
83
+
84
+ // Call methods like regular functions
85
+ const result = await math.add({ a: 5, b: 3 });
86
+ console.log(result); // { result: 8 }
87
+ }
88
+
89
+ main().catch(console.error);
90
+ ```
6
91
 
7
- ## Features
92
+ ### Creating a Broker-Connected Agent
8
93
 
9
- - **Agent JSON Management**: Manage configurations for tools, agents, or systems using KADI.
10
- - **Process Management**: Support launching and managing subprocesses.
11
- - **Multi-Broker Support**: Dynamic broker selection and management with environment/runtime configuration.
12
- - **Broker Interaction**: Facilitate communications with WebSocket brokers.
13
- - **IPC Support**: Tools for interprocess communication across various programming languages.
94
+ ```javascript
95
+ import { KadiClient } from '@kadi.build/core';
96
+
97
+ // Create an agent that connects to broker
98
+ const agent = new KadiClient({
99
+ name: 'my-agent',
100
+ role: 'agent',
101
+ protocol: 'broker',
102
+ brokers: {
103
+ prod: 'ws://localhost:8080',
104
+ dev: 'ws://localhost:8081'
105
+ },
106
+ defaultBroker: 'prod',
107
+ networks: ['global']
108
+ });
109
+
110
+ // Register tools that other agents can call
111
+ agent.registerTool(
112
+ 'greet',
113
+ async ({ name }) => {
114
+ return { greeting: `Hello, ${name}!` };
115
+ },
116
+ {
117
+ description: 'Greet someone by name',
118
+ inputSchema: {
119
+ type: 'object',
120
+ properties: {
121
+ name: { type: 'string', description: 'Name to greet' }
122
+ },
123
+ required: ['name']
124
+ }
125
+ }
126
+ );
14
127
 
15
- ## Installation
128
+ // Connect to brokers and start serving
129
+ await agent.connectToBrokers();
130
+
131
+ // Call tools from OTHER agents connected to the same broker
132
+ const result = await agent.callTool('translate', 'text-tool', {
133
+ text: 'Hello world',
134
+ language: 'spanish'
135
+ });
136
+ console.log(result);
137
+ ```
138
+
139
+ ## 🔌 Transport Protocols
140
+
141
+ KADI abilities support three transport protocols, each optimized for different use cases:
142
+
143
+ ### 1. Native Protocol (Fastest)
144
+
145
+ - **Use Case**: High-performance, in-process execution
146
+ - **How it Works**: Loads abilities as ES modules, enabling direct function calls
147
+ - **Performance**: Zero IPC overhead - functions run in the same process
148
+ - **Best For**: Utility libraries, data processing, performance-critical operations
149
+
150
+ ```javascript
151
+ // Loads as native module by default if available
152
+ const ability = await loadAbility('my-ability', 'native');
153
+ ```
16
154
 
17
- Install `@kadi.build/core` using npm:
155
+ ### 2. Stdio Protocol (Balanced)
156
+
157
+ - **Use Case**: Language-agnostic local execution
158
+ - **How it Works**: Spawns abilities as child processes, communicates via LSP-style JSON-RPC over stdin/stdout
159
+ - **Performance**: Minimal overhead, reliable local IPC
160
+ - **Best For**: Python scripts, Go binaries, Node.js abilities, development/testing
161
+
162
+ ```javascript
163
+ // Force stdio protocol
164
+ const ability = await loadAbility('my-ability', 'stdio');
165
+ ```
166
+
167
+ **Environment Variables Passed to Child Process:**
18
168
 
19
169
  ```bash
20
- npm install @kadi.build/core
170
+ KADI_PROTOCOL=stdio # Identifies the protocol being used
171
+ # Plus all parent process.env variables
172
+ ```
173
+
174
+ ### 3. Broker Protocol (Distributed)
175
+
176
+ - **Use Case**: Distributed systems, containerized deployments
177
+ - **How it Works**: Abilities connect to a WebSocket broker for network communication
178
+ - **Performance**: Network overhead, but enables scaling and distribution
179
+ - **Best For**: Distributed abilities, cloud deployments, load-balanced systems
180
+
181
+ ```javascript
182
+ // Use broker for distributed execution
183
+ const ability = await loadAbility('my-ability', 'broker');
21
184
  ```
22
185
 
23
- ## Documentation
186
+ **Environment Variables Passed to Child Process:**
187
+
188
+ ```bash
189
+ KADI_PROTOCOL=broker # Identifies the protocol
190
+ KADI_BROKER_URL=ws://localhost:8080 # Broker connection URL
191
+ KADI_ABILITY_NAME=ability.echo.1_0_0 # Ability identifier
192
+ KADI_AGENT_SCOPE=uuid-here # Agent's scope/namespace
193
+ # Plus all parent process.env variables
194
+ ```
195
+
196
+ **Network/Namespace Visibility:**
197
+
198
+ The `KADI_AGENT_SCOPE` environment variable represents the agent's namespace. For the ability to be visible to its parent agent via the broker, it must register with the same network:
199
+
200
+ ```javascript
201
+ // In your ability code:
202
+ const echoAbility = new KadiClient({
203
+ name: 'echo-js',
204
+ version: '0.0.1',
205
+ description: 'Echo ability with broker support',
206
+ network: process.env.KADI_AGENT_SCOPE // Use agent's network for visibility
207
+ });
208
+
209
+ // Without this, the ability would only be visible in the 'global' network
210
+ // and the parent agent wouldn't be able to communicate with it
211
+ ```
212
+
213
+ ### Protocol Selection Strategy
214
+
215
+ When no protocol is specified, `loadAbility` defaults to the 'native' protocol. You must explicitly specify the protocol if you want to use 'stdio' or 'broker'.
216
+
217
+ ```mermaid
218
+ graph TD
219
+ A[Load Ability] --> B{Protocol Specified?}
220
+ B -->|Yes| C[Use Specified Protocol]
221
+ B -->|No| D[Default to 'native']
222
+ ```
223
+
224
+ ## 🛠️ Creating Abilities with KadiClient
225
+
226
+ ### Basic Ability Structure
227
+
228
+ ```javascript
229
+ import { KadiClient } from '@kadi.build/core';
230
+
231
+ const ability = new KadiClient({
232
+ name: 'echo-ability',
233
+ role: 'ability', // 'agent', 'ability',
234
+ protocol: 'stdio', // 'native', 'stdio', or 'broker'
235
+ brokers: {
236
+ local: 'ws://localhost:8080',
237
+ prod: 'ws://api.example.com:8080'
238
+ },
239
+ defaultBroker: 'local'
240
+ });
241
+
242
+ // Method 1: Simple tool registration
243
+ ability.registerTool('echo', async ({ message }) => ({
244
+ echo: message,
245
+ timestamp: new Date().toISOString()
246
+ }));
247
+
248
+ // Method 2: Tool with inline schema
249
+ ability.registerTool(
250
+ 'format',
251
+ async ({ text, style }) => ({
252
+ formatted: style === 'upper' ? text.toUpperCase() : text.toLowerCase()
253
+ }),
254
+ {
255
+ description: 'Format text with specified style',
256
+ inputSchema: {
257
+ type: 'object',
258
+ properties: {
259
+ text: { type: 'string', description: 'Text to format' },
260
+ style: {
261
+ type: 'string',
262
+ enum: ['upper', 'lower'],
263
+ description: 'Formatting style'
264
+ }
265
+ },
266
+ required: ['text', 'style']
267
+ },
268
+ outputSchema: {
269
+ type: 'object',
270
+ properties: {
271
+ formatted: { type: 'string', description: 'Formatted text' }
272
+ }
273
+ }
274
+ }
275
+ );
276
+
277
+ // Start serving
278
+ ability.serve().catch(console.error);
279
+ ```
280
+
281
+ ### Event Publishing and Subscription
282
+
283
+ **Full Support**: Events work across all protocols - native, stdio, and broker.
284
+
285
+ KadiClient provides a unified event system that works consistently across all transport protocols:
286
+
287
+ ```javascript
288
+ // In your ability (ability.js)
289
+ const ability = new KadiClient({
290
+ name: 'my-ability',
291
+ role: 'ability',
292
+ protocol: 'broker' // Works with all protocols
293
+ });
294
+
295
+ // Publish events from tool handlers
296
+ ability.registerTool('echo', async ({ message }) => {
297
+ // Publish events during processing
298
+ await ability.publishEvent('echo.processing', { status: 'started' });
299
+
300
+ // Do work...
301
+ const result = { echo: message, timestamp: new Date().toISOString() };
24
302
 
25
- ### Agent JSON Functions
303
+ await ability.publishEvent('echo.completed', { result });
304
+ return result;
305
+ });
306
+
307
+ // In your agent (agent.js)
308
+ const agent = new KadiClient({
309
+ name: 'my-client',
310
+ role: 'agent',
311
+ protocol: 'broker'
312
+ });
313
+
314
+ // Subscribe to events using patterns
315
+ agent.subscribeToEvent('echo.*', (data) => {
316
+ console.log('Echo event received:', data);
317
+ });
318
+
319
+ // For loaded abilities with native/stdio protocols
320
+ const ability = await loadAbility('my-ability', 'stdio');
321
+ ability.events.on('echo.completed', (data) => {
322
+ console.log('Completed:', data);
323
+ });
324
+ ```
26
325
 
27
- - **`getAbilityJSON(abilityName, abilityVersion)`**: Retrieves the `agent.json` for a specified ability.
28
- - **`getAbilityJSONPath(abilityName, abilityVersion)`**: Provides the file path to the `agent.json` for a specific ability.
29
- - **`getAbilityVersionFromArray(abilities, name)`**: Searches ability array provided, and returns the version number for name provided.
30
- - **`getAbilitiesDir()`**: Returns the directory path where abilities are stored.
31
- - **`getProjectJSON()`**: Fetches the `agent.json` for the current project.
32
- - **`getProjectJSONPath()`**: Gets the file path for the project's `agent.json`.
33
- - **`getKadiCoreJSON()`**: Retrieves the `agent.json` for the Kadi core.
34
- - **`getKadiCoreJSONPath()`**: Provides the file path to the Kadi core's `agent.json`.
35
- - **`getKadiJSON()`**: Fetches the `agent.json` for the Kadi system.
36
- - **`getKadiJSONPath()`**: Returns the file path for the Kadi system's `agent.json`.
326
+ **Event Patterns**:
37
327
 
38
- ### Broker Management Functions
328
+ - Use wildcards: `math.*` matches `math.operation`, `math.milestone`
329
+ - Protocol-specific delivery:
330
+ - **Native**: Direct EventEmitter (synchronous)
331
+ - **Stdio**: JSON-RPC notifications over stdout
332
+ - **Broker**: RabbitMQ pub/sub via WebSocket
39
333
 
40
- The broker management system allows dynamic selection from multiple configured brokers defined in `agent.json`:
334
+ ### Ability Configuration (agent.json)
335
+
336
+ Every ability should have an `agent.json` file that defines its capabilities:
41
337
 
42
338
  ```json
43
339
  {
340
+ "name": "echo-ability",
341
+ "kind": "ability",
342
+ "version": "1.0.0",
343
+ "license": "MIT",
344
+ "description": "Echo ability with multiple transport support",
345
+ "scripts": {
346
+ "setup": "npm install",
347
+ "start": "node ability.js"
348
+ },
349
+ "exports": [
350
+ {
351
+ "name": "echo",
352
+ "description": "Echo the input message",
353
+ "inputSchema": {
354
+ "type": "object",
355
+ "properties": {
356
+ "message": { "type": "string" }
357
+ },
358
+ "required": ["message"]
359
+ },
360
+ "outputSchema": {
361
+ "type": "object",
362
+ "properties": {
363
+ "echo": { "type": "string" },
364
+ "timestamp": { "type": "string" }
365
+ }
366
+ }
367
+ }
368
+ ],
44
369
  "brokers": {
45
- "local": "ws://127.0.0.1:8080",
46
- "remote": "ws://production.example.com:8080",
47
- "staging": "ws://staging.example.com:8080"
48
- }
370
+ "local": "ws://localhost:8080",
371
+ "remote": "ws://api.example.com:8080"
372
+ },
373
+ "defaultBroker": "local"
49
374
  }
50
375
  ```
51
376
 
52
- Available functions:
377
+ ### Schema Definition Options
378
+
379
+ The ability system provides two ways to define method schemas:
380
+
381
+ 1. **Export Schemas** (defined in `agent.json` exports section)
382
+ 2. **Inline Schemas** (passed directly to `registerTool()`)
383
+
384
+ ```javascript
385
+ // Option 1: Define in agent.json exports section
386
+ // The schema is picked up automatically if the method name matches
387
+ // agent.json:
388
+ {
389
+ "exports": [
390
+ {
391
+ "name": "process",
392
+ "description": "Process data",
393
+ "inputSchema": { /* ... */ },
394
+ "outputSchema": { /* ... */ }
395
+ }
396
+ ]
397
+ }
398
+
399
+ // Option 2: Define inline when registering the method
400
+ ability.registerTool('process', handler, {
401
+ description: 'Process data',
402
+ inputSchema: { /* ... */ },
403
+ outputSchema: { /* ... */ }
404
+ });
405
+
406
+ // Note: If both are defined, inline schemas take precedence over exports
407
+ ```
53
408
 
54
- - **`KADI_BROKERS`**: Object containing all configured brokers with their parsed URLs.
55
- - **`KADI_BROKER_URL`**: Default broker URL (first one defined, for backward compatibility).
56
- - **`getBrokerUrl(brokerName)`**: Get URL for a specific broker by name. Returns `null` if not found.
57
- - **`getBrokerNames()`**: Get array of all available broker names.
58
- - **`setActiveBroker(brokerName)`**: Set the active broker for the session. Returns `true` if successful.
59
- - **`getActiveBrokerName()`**: Get the name of the currently active broker.
60
- - **`getActiveBrokerUrl()`**: Get the URL of the currently active broker.
61
- - **`getDefaultBrokerName()`**: Get the name of the default broker (first one defined).
62
- - **`selectBrokerFromEnv()`**: Set active broker from `KADI_BROKER` environment variable.
409
+ **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.
63
410
 
64
- ### Process Management Functions
411
+ ## 🤖 Working with KadiClient
65
412
 
66
- - **`runExecCommand(name, version, command)`**: Executes a command for initializing abilities.
67
- - **`runSpawnCommand(name, version, command)`**: Uses `spawn` to execute commands for subprocesses.
413
+ 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.
68
414
 
69
- ## Usage Examples
415
+ ### Direct Approach (using loadAbility)
70
416
 
71
417
  ```javascript
72
- import {
73
- getProjectJSON,
74
- runExecCommand,
75
- IPCManager,
76
- Broker
77
- } from '@kadi.build/core';
418
+ import { loadAbility } from '@kadi.build/core';
78
419
 
79
- async function setupProject() {
80
- const projectConfig = getProjectJSON();
81
- console.log(projectConfig);
82
-
83
- await runExecCommand('example', '1.0', 'npm install');
84
-
85
- // Broker management examples
86
- console.log('Available brokers:', getBrokerNames());
87
- console.log('All brokers:', KADI_BROKERS);
88
-
89
- // Set active broker
90
- setActiveBroker('remote');
91
- console.log('Active broker:', getActiveBrokerUrl());
92
-
93
- // IPC setup
94
- const ipc = new IPCManager();
95
- ipc.createInstance('python', 'pythonScript.py', 'pythonInstance');
96
-
97
- // Traditional broker setup
98
- Broker.addBroker('ws://example.com', 'exampleBroker');
99
- let broker = Broker.getBroker('default');
100
- console.log(`Connected to ${broker.url}`);
101
- broker.send(
102
- BrokerMessageBuilder.setup(
103
- 'TestAgent',
104
- 'A Broker Testing Agent',
105
- null,
106
- null
107
- )
108
- );
109
- }
420
+ // Load and call abilities directly
421
+ const echoAbility = await loadAbility('echo-js', 'stdio');
422
+ const result = await echoAbility.echo({ message: 'Hello world' });
423
+
424
+ // Load broker tools directly
425
+ const remoteAbility = await loadAbility('remote-ability', 'broker', {
426
+ brokerUrl: 'ws://localhost:8080',
427
+ networks: ['global']
428
+ });
429
+ const brokerResult = await remoteAbility.process({ data: 'test' });
110
430
  ```
111
431
 
112
- ### Broker Functions
432
+ ### Using KadiClient with Named Brokers
113
433
 
114
- - **`Broker`**: Manages interactions with broker systems.
115
- - **`IBroker`**: Broker instance interface with methods to manage its lifecycle and communications.
116
- - **`BrokerMessageBuilder`**: Assists in constructing messages for broker communication.
434
+ ```javascript
435
+ import { KadiClient } from '@kadi.build/core';
436
+
437
+ // Create a unified client with named brokers
438
+ const client = new KadiClient({
439
+ name: 'my-ability',
440
+ role: 'ability', // Provider role
441
+ protocol: 'broker',
442
+ brokers: {
443
+ dev: 'ws://localhost:8080',
444
+ staging: 'ws://staging.example.com:8080',
445
+ prod: 'ws://prod.example.com:8080'
446
+ },
447
+ defaultBroker: 'dev',
448
+ networks: ['global']
449
+ });
117
450
 
118
- ## Broker Functions
451
+ // Register tools for others to call
452
+ client.registerTool(
453
+ 'greet',
454
+ async ({ name }) => {
455
+ return { greeting: `Hello, ${name}!` };
456
+ },
457
+ {
458
+ description: 'Greet someone by name',
459
+ inputSchema: {
460
+ type: 'object',
461
+ properties: { name: { type: 'string' } },
462
+ required: ['name']
463
+ }
464
+ }
465
+ );
119
466
 
120
- The Broker system facilitates interaction between different agents and services within the Kadi environment.
467
+ // Connect to all configured brokers
468
+ await client.connectToBrokers();
121
469
 
122
- ### `Broker` Interface
470
+ // Call remote tools
471
+ const result = await client.callTool('translator', 'translate', {
472
+ text: 'Hello',
473
+ to: 'es'
474
+ });
123
475
 
124
- - **`addBroker(url, name = 'default')`**: Adds a new broker connection. If a broker with the same name exists, it will be reused.
125
- - **`disconnect(name = 'default')`**: Disconnects the broker specified by the name.
126
- - **`deleteBroker(name = 'default')`**: Deletes the broker specified by the name and closes its connection.
127
- - **`send(message, brokerName = 'default')`**: Sends a message through the broker specified by the name.
128
- - **`addEventListener(event, listener, brokerName = 'default')`**: Adds an event listener to the specified broker.
129
- - **`removeEventListener(event, listener, brokerName = 'default')`**: Removes an event listener from the specified broker.
130
- - **`removeAllListeners(brokerName = 'default')`**: Removes all event listeners from the specified broker.
131
- - **`getBroker(brokerName = 'default')`**: Retrieves a broker instance by name.
476
+ // Load abilities for compatibility
477
+ const ability = await client.loadAbility('echo-js');
478
+ ```
132
479
 
133
- ### `IBroker` Instance
480
+ Use `loadAbility()` for simple consumption, or KadiClient for full-featured ability development with tool registration, event publishing, and remote tool invocation.
481
+
482
+ ## 🔄 Architecture Deep Dive
483
+
484
+ ### Transport Architecture and Loading Sequence
485
+
486
+ The loading process uses modular transports based on the selected protocol:
487
+
488
+ ```mermaid
489
+ sequenceDiagram
490
+ participant Client
491
+ participant loadAbility
492
+ participant Transport
493
+ participant FileSystem
494
+ participant ChildProcess
495
+ participant Ability
496
+ participant Broker
497
+
498
+ Client->>loadAbility: loadAbility('echo-js', protocol?)
499
+ loadAbility->>FileSystem: Read agent.json manifests
500
+ FileSystem-->>loadAbility: Return config & version
501
+
502
+ alt protocol === 'native'
503
+ loadAbility->>Transport: new NativeTransport(config)
504
+ Transport->>FileSystem: import(entry point)
505
+ FileSystem-->>Transport: Module with KadiClient instance
506
+ Transport->>Ability: Extract tool handlers via getToolNames()
507
+ Ability-->>Transport: Return registered tools
508
+ Transport->>Transport: Store handlers in Map
509
+ else protocol === 'stdio'
510
+ loadAbility->>Transport: new StdioTransport(config)
511
+ Transport->>ChildProcess: spawn(startCmd, env: {KADI_PROTOCOL})
512
+ ChildProcess->>Ability: KadiClient detects stdio mode
513
+ Ability->>Ability: Setup FrameReader/Writer
514
+ Transport->>Ability: Send discovery request
515
+ Ability-->>Transport: Return tool list
516
+ Transport->>Transport: Setup JSON-RPC proxy
517
+ else protocol === 'broker'
518
+ loadAbility->>Transport: new BrokerTransport(config)
519
+ Transport->>Transport: Reuse existing KadiClient
520
+ Transport->>Broker: WebSocket connect
521
+ Transport->>Broker: kadi.session.hello
522
+ Broker-->>Transport: { nonce, heartbeatIntervalSec }
523
+ Transport->>Broker: kadi.session.authenticate
524
+ Broker-->>Transport: { sessionId }
525
+ Transport->>Broker: kadi.ability.list
526
+ Broker-->>Transport: Available tools
527
+ Transport->>Transport: Start heartbeat timer
528
+ end
529
+
530
+ loadAbility->>loadAbility: Create proxy with tool methods
531
+ loadAbility-->>Client: Return ability proxy
532
+
533
+ Note over Client,Ability: Tool invocation
534
+ Client->>Transport: ability.echo({ message })
535
+
536
+ alt native
537
+ Transport->>Ability: Direct handler call
538
+ else stdio
539
+ Transport->>Ability: JSON-RPC over frames
540
+ else broker
541
+ Transport->>Broker: kadi.ability.invoke
542
+ Broker->>Ability: Route to target
543
+ Ability->>Broker: kadi.ability.result
544
+ Broker-->>Transport: Forward result
545
+ end
546
+
547
+ Transport-->>Client: Return result
548
+ ```
134
549
 
135
- This class extends `EventEmitter` and manages individual broker connections:
550
+ ### KadiClient Architecture: Tool Registration & Serving
551
+
552
+ How KadiClient registers tools and starts serving:
553
+
554
+ ```mermaid
555
+ sequenceDiagram
556
+ participant Dev as Developer
557
+ participant KC as KadiClient
558
+ participant Transport
559
+ participant Network as Network Layer
560
+ participant RemoteClient
561
+
562
+ Note over Dev,KC: Tool Registration Phase
563
+ Dev->>KC: new KadiClient({ name, role, protocol })
564
+ KC->>KC: Initialize based on role & protocol
565
+
566
+ Dev->>KC: registerTool('echo', handler, schema?)
567
+ KC->>KC: Store in toolHandlers Map<name, {handler, schema}>
568
+
569
+ Dev->>KC: registerTool('process', handler, schema)
570
+ KC->>KC: Add to toolHandlers Map
571
+
572
+ Note over KC,Network: Connection & Serving Phase
573
+ Dev->>KC: serve() or connectToBrokers()
574
+
575
+ alt protocol === 'native'
576
+ KC->>KC: Tools available via direct reference
577
+ KC->>KC: Emit 'start' event
578
+ else protocol === 'stdio'
579
+ KC->>Transport: Setup StdioTransport
580
+ Transport->>Transport: new FrameReader(stdin)
581
+ Transport->>Transport: new FrameWriter(stdout)
582
+ Transport->>KC: on('message', handleStdioMessage)
583
+ KC->>KC: Process JSON-RPC requests
584
+ else protocol === 'broker'
585
+ KC->>Network: WebSocket.connect(brokerUrl)
586
+ KC->>Network: kadi.session.hello({ role })
587
+ Network-->>KC: { nonce, heartbeatIntervalSec: 30 }
588
+ KC->>KC: Sign nonce with Ed25519
589
+ KC->>Network: kadi.session.authenticate({ signature })
590
+ Network-->>KC: { sessionId }
591
+ KC->>Network: kadi.agent.register({ tools, networks })
592
+ KC->>KC: Start heartbeat timer (30s interval)
593
+ end
594
+
595
+ Note over RemoteClient,KC: Tool Invocation Flow
596
+ RemoteClient->>Network: Request tool invocation
597
+
598
+ alt native
599
+ RemoteClient->>KC: Direct method call
600
+ KC->>KC: toolHandlers.get(name).handler(params)
601
+ else stdio
602
+ Network->>Transport: JSON-RPC frame
603
+ Transport->>KC: Parsed request
604
+ KC->>KC: toolHandlers.get(name).handler(params)
605
+ KC->>Transport: JSON-RPC response
606
+ Transport->>Network: Framed response
607
+ else broker
608
+ Network->>KC: kadi.ability.invoke message
609
+ KC->>KC: toolHandlers.get(toolName).handler(params)
610
+ KC->>Network: kadi.ability.result message
611
+ end
612
+
613
+ Network-->>RemoteClient: Tool result
614
+ ```
136
615
 
137
- - **`constructor(url, name)`**: Initializes a new broker connection.
138
- - **`send(message)`**: Sends a message through the WebSocket connection.
139
- - **`getConnectedAgents()`**: Retrieves a list of currently connected agents.
616
+ ### Event System Architecture
617
+
618
+ KadiClient provides a unified event API that works across all transport protocols:
619
+
620
+ ```mermaid
621
+ sequenceDiagram
622
+ participant Publisher as Ability (Publisher)
623
+ participant Transport as Transport Layer
624
+ participant EventBus as Event Bus
625
+ participant Subscriber as Client (Subscriber)
626
+
627
+ Note over Subscriber: Setup subscription
628
+ Subscriber->>Subscriber: subscribeToEvent('math.*', callback)
629
+
630
+ alt protocol === 'broker'
631
+ Subscriber->>EventBus: kadi.event.subscribe({channels: ['math.*']})
632
+ EventBus-->>Subscriber: {subscribed: ['math.*'], queueName}
633
+ else protocol === 'stdio'
634
+ Subscriber->>Transport: Setup event listener
635
+ Transport->>Subscriber: on('transport:event', dispatcher)
636
+ else protocol === 'native'
637
+ Subscriber->>Publisher: Direct EventEmitter reference
638
+ Publisher->>Subscriber: on('ability:event', dispatcher)
639
+ end
640
+
641
+ Note over Publisher: Publish event
642
+ Publisher->>Publisher: publishEvent('math.operation', data)
643
+
644
+ alt protocol === 'native'
645
+ Publisher->>Publisher: emit('ability:event', {eventName, data})
646
+ Publisher-->>Subscriber: Direct EventEmitter delivery
647
+ Subscriber->>Subscriber: Pattern match & invoke callback
648
+ else protocol === 'stdio'
649
+ Publisher->>Transport: writer.write(__kadi_event notification)
650
+ Transport->>Transport: Frame with LSP headers
651
+ Transport-->>Subscriber: Framed event over stdout
652
+ Subscriber->>Transport: FrameReader parses
653
+ Transport->>Subscriber: emit('transport:event', {name, data})
654
+ Subscriber->>Subscriber: Pattern match & invoke callback
655
+ else protocol === 'broker'
656
+ Publisher->>EventBus: kadi.event.publish({channel, data})
657
+ EventBus->>EventBus: Route via RabbitMQ
658
+ EventBus-->>Subscriber: kadi.event.delivery
659
+ Subscriber->>Subscriber: Pattern match & invoke callback
660
+ end
661
+
662
+ Note over Subscriber: Cleanup
663
+ Subscriber->>Subscriber: unsubscribe()
664
+ alt protocol === 'broker'
665
+ Subscriber->>EventBus: kadi.event.unsubscribe
666
+ else
667
+ Subscriber->>Subscriber: Remove local listener
668
+ end
669
+ ```
140
670
 
141
- ### `BrokerMessageBuilder`
671
+ ### Broker Protocol Communication Flow
672
+
673
+ Detailed view of broker-based communication with heartbeat:
674
+
675
+ ```mermaid
676
+ sequenceDiagram
677
+ participant Client as KadiClient
678
+ participant Broker as KADI Broker
679
+ participant Target as Target Ability
680
+
681
+ Note over Client,Broker: Connection & Authentication
682
+ Client->>Broker: WebSocket connect
683
+ Client->>Broker: kadi.session.hello({ role })
684
+ Broker-->>Client: { nonce, heartbeatIntervalSec: 30 }
685
+ Client->>Client: Generate Ed25519 keypair
686
+ Client->>Client: Sign nonce
687
+ Client->>Broker: kadi.session.authenticate({ signature })
688
+ Broker-->>Client: { sessionId }
689
+
690
+ Note over Client,Broker: Heartbeat Management
691
+ Client->>Client: Start heartbeat timer (30s)
692
+ loop Every 30 seconds
693
+ Client->>Broker: kadi.session.ping
694
+ Note over Broker: Reset connection timeout (90s)
695
+ end
696
+
697
+ Note over Client,Broker: Tool Registration
698
+ Client->>Broker: kadi.agent.register({ tools, networks })
699
+ Broker->>Broker: Store in registry
700
+ Broker-->>Client: { registered: true }
701
+
702
+ Note over Client,Target: Remote Tool Invocation
703
+ Client->>Client: callTool('ability-b', 'process', params)
704
+ Client->>Broker: kadi.ability.invoke({
705
+ targetAgent: 'ability-b',
706
+ toolName: 'process',
707
+ toolInput: params
708
+ })
709
+ Broker->>Broker: Lookup ability-b
710
+ Broker->>Target: kadi.ability.invoke
711
+ Target->>Target: Execute tool handler
712
+ Target->>Broker: JSON-RPC response
713
+ Broker-->>Client: Forward response
714
+ Client->>Client: Resolve promise
715
+
716
+ Note over Client,Broker: Event Publishing
717
+ Client->>Broker: kadi.event.publish({
718
+ channel: 'system.status',
719
+ data: { status: 'healthy' }
720
+ })
721
+ Broker->>Broker: Publish to RabbitMQ exchange
722
+
723
+ Note over Client,Broker: Graceful Shutdown
724
+ Client->>Client: Stop heartbeat timer
725
+ Client->>Broker: kadi.session.goodbye
726
+ Client->>Client: Close WebSocket
727
+ ```
142
728
 
143
- Utility class for constructing broker messages:
729
+ ### Stdio Protocol: LSP-Style Frame Processing
730
+
731
+ How KadiClient handles stdio communication with LSP-style framing:
732
+
733
+ ```mermaid
734
+ sequenceDiagram
735
+ participant Parent as Parent Process
736
+ participant SR as StdioFrameReader
737
+ participant SW as StdioFrameWriter
738
+ participant Server as StdioRpcServer
739
+ participant Ability as KadiClient
740
+
741
+ Note over Parent,SR: Incoming Request
742
+ Parent->>SR: Write to stdin: "Content-Length: 52\r\n\r\n{...}"
743
+ SR->>SR: Buffer incoming data
744
+ SR->>SR: Look for Content-Length header
745
+ SR->>SR: Parse header, extract body length
746
+ SR->>SR: Wait for complete body
747
+ SR->>SR: Parse JSON from body
748
+ SR->>Server: onMessage({ success: true, data: request })
749
+
750
+ Server->>Server: Validate JSON-RPC format
751
+ Server->>Ability: getMethodHandler(request.method)
752
+ Ability-->>Server: Return handler
753
+ Server->>Server: Execute handler(params) with timeout
754
+
755
+ Note over Server,Parent: Response
756
+ Server->>SW: write(response)
757
+ SW->>SW: JSON.stringify(response)
758
+ SW->>SW: Calculate byte length
759
+ SW->>SW: Create header: "Content-Length: N\r\n\r\n"
760
+ SW->>Parent: Write header + body to stdout
761
+
762
+ Note over SR: Error Recovery
763
+ alt Frame Corruption
764
+ SR->>SR: Detect invalid frame
765
+ SR->>SR: Search for next Content-Length marker
766
+ SR->>SR: Skip corrupted bytes
767
+ SR->>Server: onMessage({ success: false, error: 'FRAME_CORRUPTION' })
768
+ Server->>Server: Log error, continue processing
769
+ end
770
+ ```
144
771
 
145
- - **`create_message(type, data)`**: Creates a JSON string with the specified type and data.
146
- - **`message(to, content)`**: Constructs a message directed to a specific peer.
147
- - **`setup(name, description, limit, uuid)`**: Creates a setup message for registering the broker.
148
- - **`suspend()`**: Constructs a suspend message.
149
- - **`finish()`**: Constructs a finish message.
150
- - **`list()`**: Constructs a list message to request a list of connected agents.
772
+ ## 📥 Loading Abilities
151
773
 
152
- ### IPC Functions
774
+ ### Basic Loading
153
775
 
154
- - **`IPCMessageBuilder`**: Constructs messages for IPC interactions.
155
- - **`IPCManager`**: Manages setup and lifecycle of IPC connections.
156
- - **`IAbilityIPC`**: Represents an IPC ability instance with methods to manage its lifecycle and communications.
776
+ ```javascript
777
+ import { loadAbility } from '@kadi.build/core';
157
778
 
158
- ### `IPCManager`
779
+ // Auto-detect protocol from agent.json
780
+ const ability = await loadAbility('my-ability');
159
781
 
160
- Manages the lifecycle and setup of interprocess communications.
782
+ // Explicitly specify protocol
783
+ const stdioAbility = await loadAbility('my-ability', 'stdio');
784
+ const brokerAbility = await loadAbility('my-ability', 'broker');
785
+ const nativeAbility = await loadAbility('my-ability', 'native');
786
+ ```
787
+
788
+ ### Working with Loaded Abilities
789
+
790
+ ```javascript
791
+ // Call methods directly - no need to discover them first
792
+ const result = await ability.echo({ message: 'hello' });
161
793
 
162
- - **`createInstance(language, commandString, name = 'default')`**: Creates and manages a new IPC instance for the specified language.
163
- - **`getInstance(name)`**: Retrieves an existing IPC instance by name.
164
- - **`shutdownInstance(name)`**: Shuts down an existing IPC instance by name.
794
+ // Call methods
795
+ const result = await ability.someMethod({ param: 'value' });
165
796
 
166
- ### `IAbilityIPC` Instance
797
+ // Direct RPC call (bypasses method validation)
798
+ const response = await ability.__call('someMethod', { param: 'value' });
167
799
 
168
- Manages an individual interprocess communication instance:
800
+ // Subscribe to events
801
+ ability.events.on('custom:event', (data) => {
802
+ console.log('Event received:', data);
803
+ });
169
804
 
170
- - **`start()`**: Starts the child process associated with this IPC instance.
171
- - **`launch()`**: Sends the launch command to the associated process.
172
- - **`sendMessage(message)`**: Sends a JSON-formatted message to the child process.
173
- - **`shutdown()`**: Sends a shutdown command to the child process and terminates it.
805
+ // Note: Events work for all protocols (native, stdio, and broker)
806
+ // Subscribe before calling methods that emit events
807
+ ```
174
808
 
175
- ### `IPCMessageBuilder`
809
+ ### KadiClient transport vs loadAbility transport
176
810
 
177
- Utility class for constructing IPC messages:
811
+ KADI uses “transport” in two places and they serve different purposes:
178
812
 
179
- - **`createMessage(type, contents)`**: Creates a JSON string with the specified type and contents.
180
- - **`launch(commandString)`**: Constructs a launch message with the specified command string.
181
- - **`shutdown()`**: Constructs a shutdown message.
182
- - **`message(content)`**: Constructs a general message with the specified content.
813
+ - KadiClient transport how this client exposes its own tools after `registerTool(...)` and `serve()`.
814
+ - `transport: 'native'` — in‑process only. There is no network or stdio listener. Code that loads you natively (via `loadAbility('you','native')`) can call your tools directly. `serve()` simply keeps the process alive if you want a long‑running ability.
815
+ - `transport: 'stdio'` — the client runs a stdio server (JSON‑RPC over stdin/stdout) and answers requests with your registered handlers. `serve()` wires up the stdio loop.
816
+ - `transport: 'broker'` the client connects to the broker, registers your tools, and receives remote invocations over WebSocket. `serve()` (or `connectToBrokers()`) handles connection and registration.
183
817
 
184
- ## Usage Examples
818
+ - loadAbility transport — how this client connects to a specific ability it wants to use.
819
+ - `'native'` loads a module in‑process
820
+ - `'stdio'` talks to a spawned child process (uses the ability’s `agent.json` `scripts.start`)
821
+ - `'broker'` talks to a remote ability over the broker
185
822
 
186
- Here's how you might use the IPC and Broker functionalities:
823
+ These two choices are independent: setting the client’s `transport` does not change how you load another ability, and the transport you choose in `loadAbility(...)` does not change how your client serves its own tools.
187
824
 
188
- ### Interacting with IPC
825
+ Examples
826
+
827
+ Provide tools over broker
828
+
829
+ ```ts
830
+ import { KadiClient } from '@kadi.build/core';
831
+
832
+ const svc = new KadiClient({
833
+ name: 'math',
834
+ role: 'ability',
835
+ transport: 'broker',
836
+ brokers: { local: 'ws://localhost:8080' },
837
+ defaultBroker: 'local'
838
+ });
839
+
840
+ svc.registerTool('add', async ({ a, b }) => ({ result: a + b }));
841
+ await svc.serve(); // registers with the broker so others can call math.add
842
+ ```
843
+
844
+ Consume a stdio ability
845
+
846
+ ```ts
847
+ const agent = new KadiClient({
848
+ name: 'client',
849
+ role: 'agent',
850
+ transport: 'native'
851
+ });
852
+ const math = await agent.loadAbility('simple-math', 'stdio');
853
+ const res = await math.add({ a: 2, b: 3 });
854
+ ```
855
+
856
+ Consume a broker ability
857
+
858
+ ```ts
859
+ const agent = new KadiClient({
860
+ name: 'client',
861
+ role: 'agent',
862
+ transport: 'broker',
863
+ brokers: { local: 'ws://localhost:8080' },
864
+ defaultBroker: 'local'
865
+ });
866
+
867
+ const echo = await agent.loadAbility('echo-js', 'broker', {
868
+ networks: ['global']
869
+ });
870
+ const r = await echo.say_message({ message: 'hi' });
871
+ ```
872
+
873
+ ### Loading Context
874
+
875
+ The loader automatically resolves ability versions from:
876
+
877
+ 1. **Project context**: Reads from project's `agent.json`
878
+ 2. **Nested context**: When loading from within another ability
879
+ 3. **Explicit version**: Can be specified in ability name
880
+
881
+ ## ⚙️ Configuration
882
+
883
+ ### Environment Variables
884
+
885
+ ```bash
886
+ # Set default protocol
887
+ export KADI_PROTOCOL=stdio
888
+
889
+ # Configure broker
890
+ export KADI_BROKER_URL=ws://localhost:8080
891
+ export KADI_ABILITY_NAME=my-ability
892
+ export KADI_AGENT_SCOPE=project-123
893
+ ```
894
+
895
+ ### Environment Variables for Child Processes
896
+
897
+ When abilities are spawned as child processes (stdio and broker protocols), the parent process passes down important environment variables:
189
898
 
190
899
  ```javascript
191
- console.log('Starting IPC Ability Interface test...');
900
+ // In your ability code, you can access these variables:
901
+ const protocol = process.env.KADI_PROTOCOL; // 'stdio' or 'broker'
902
+ const brokerUrl = process.env.KADI_BROKER_URL;
903
+ const abilityName = process.env.KADI_ABILITY_NAME;
904
+ const agentScope = process.env.KADI_AGENT_SCOPE;
905
+
906
+ // Use them to configure your ability behavior
907
+ const ability = new KadiClient({
908
+ name: 'my-ability',
909
+ network: process.env.KADI_AGENT_SCOPE || 'global'
910
+ // Ability automatically uses KADI_PROTOCOL to determine transport
911
+ });
912
+ ```
192
913
 
193
- // Create Python IPC instance
194
- const pythonIPC = IPCManager.createInstance(
195
- 'python',
196
- 'echo "Hello, World!"',
197
- 'pythonTest'
198
- );
914
+ ### Example Project Structure
915
+
916
+ ```
917
+ my-project/
918
+ ├── agent.json # Project configuration
919
+ ├── abilities/ # Installed abilities
920
+ │ └── echo-ability/
921
+ │ └── 1.0.0/
922
+ │ ├── agent.json # Ability configuration
923
+ │ ├── ability.js # Ability implementation
924
+ │ └── package.json
925
+ ├── modules/ # Source modules
926
+ │ └── echo-ability/ # Development version
927
+ └── index.js # Main entry point
928
+ ```
929
+
930
+ ## 🚀 Advanced Usage
931
+
932
+ ### Event System
199
933
 
200
- pythonIPC.on('error', (error) => {
201
- console.error('Handled Python Error:', error);
202
- // Consider appropriate error handling or recovery actions here
934
+ KadiClient provides a comprehensive event system that works across all protocols:
935
+
936
+ #### 1. Lifecycle Events
937
+
938
+ Monitor the ability lifecycle:
939
+
940
+ ```javascript
941
+ const client = new KadiClient({ name: 'my-ability' });
942
+
943
+ // Connection events
944
+ client.on('connected', ({ broker }) => {
945
+ console.log(`Connected to broker: ${broker}`);
946
+ });
947
+
948
+ client.on('disconnected', () => {
949
+ console.log('Disconnected from broker');
950
+ });
951
+
952
+ // Tool invocation events
953
+ client.on('tool:invoked', ({ toolName, params }) => {
954
+ console.log(`Tool ${toolName} invoked with:`, params);
955
+ });
956
+
957
+ client.on('tool:completed', ({ toolName, result }) => {
958
+ console.log(`Tool ${toolName} completed:`, result);
959
+ });
960
+
961
+ client.on('error', (error) => {
962
+ console.error('Ability error:', error);
963
+ });
964
+ ```
965
+
966
+ #### 2. Custom Events (All Protocols)
967
+
968
+ Publish and subscribe to custom events across all transport protocols:
969
+
970
+ ```javascript
971
+ // Publishing events
972
+ const publisher = new KadiClient({
973
+ name: 'event-publisher',
974
+ role: 'ability',
975
+ protocol: 'broker' // Works with all protocols
976
+ });
977
+
978
+ publisher.registerTool('process', async ({ data }) => {
979
+ // Publish events during processing
980
+ await publisher.publishEvent('process.started', {
981
+ timestamp: Date.now(),
982
+ data
983
+ });
984
+
985
+ // Do work...
986
+ const result = await processData(data);
987
+
988
+ await publisher.publishEvent('process.completed', {
989
+ timestamp: Date.now(),
990
+ result
991
+ });
992
+
993
+ return result;
203
994
  });
204
995
 
205
- pythonIPC.on('critical-error', (error) => {
206
- console.error('Critical Error from Python:', error);
207
- // Handle critical errors, potentially restarting the process or alerting administrators
996
+ // Subscribing to events
997
+ const subscriber = new KadiClient({
998
+ name: 'event-subscriber',
999
+ role: 'agent',
1000
+ protocol: 'broker'
208
1001
  });
209
1002
 
210
- pythonIPC.on('message', (msg) => {
211
- console.log('Message from Python:', msg);
1003
+ // Subscribe with wildcards
1004
+ subscriber.subscribeToEvent('process.*', (data) => {
1005
+ console.log('Process event:', data);
212
1006
  });
213
1007
 
214
- pythonIPC.on('broker', (msg) => {
215
- console.log('Broker message from Python:', msg);
1008
+ // One-time subscription
1009
+ subscriber.onceEvent('process.completed', (data) => {
1010
+ console.log('First completion:', data);
216
1011
  });
217
1012
 
218
- pythonIPC.on('shutdown', () => {
219
- console.log('Python IPC has shut down.');
1013
+ // Multiple pattern subscription
1014
+ subscriber.subscribeToEvents(['process.*', 'system.*'], (pattern, data) => {
1015
+ console.log(`Event from ${pattern}:`, data);
1016
+ });
1017
+ ```
1018
+
1019
+ **Protocol-Specific Behavior:**
1020
+
1021
+ - **Native**: Direct EventEmitter, synchronous delivery
1022
+ - **Stdio**: JSON-RPC notifications with LSP framing
1023
+ - **Broker**: RabbitMQ pub/sub via WebSocket, persistent queues available
1024
+
1025
+ ### Multi-Broker Configuration
1026
+
1027
+ KadiClient supports connecting to multiple brokers simultaneously for redundancy and load distribution:
1028
+
1029
+ ```javascript
1030
+ const client = new KadiClient({
1031
+ name: 'multi-broker-ability',
1032
+ role: 'ability',
1033
+ protocol: 'broker',
1034
+ brokers: {
1035
+ primary: 'ws://broker1.example.com:8080',
1036
+ secondary: 'ws://broker2.example.com:8080',
1037
+ backup: 'ws://broker3.example.com:8080'
1038
+ },
1039
+ defaultBroker: 'primary', // Used for sending if no specific broker targeted
1040
+ networks: ['production']
220
1041
  });
221
1042
 
222
- Broker.addBroker('http://your.broker.com', 'default');
223
- let brk = Broker.getBroker('default');
224
- broker = brk;
1043
+ // Connect to all configured brokers
1044
+ await client.connectToBrokers();
1045
+
1046
+ // The client will now:
1047
+ // 1. Maintain connections to all three brokers
1048
+ // 2. Receive messages from any broker
1049
+ // 3. Send messages to the default broker unless specified
1050
+ // 4. Automatically handle broker failover
1051
+ ```
1052
+
1053
+ ### Cross-Language Ability Support
1054
+
1055
+ KADI now fully supports abilities written in any language through the stdio protocol:
1056
+
1057
+ ```javascript
1058
+ // Go ability with agent.json:
1059
+ {
1060
+ "name": "hash-go",
1061
+ "scripts": {
1062
+ "setup": "go build -o bin/hash_ability",
1063
+ "start": "./bin/hash_ability" // Binary executable
1064
+ }
1065
+ }
1066
+
1067
+ // Python ability:
1068
+ {
1069
+ "name": "ml-processor",
1070
+ "scripts": {
1071
+ "start": "python3 main.py"
1072
+ }
1073
+ }
1074
+
1075
+ // Rust ability:
1076
+ {
1077
+ "name": "crypto-rust",
1078
+ "scripts": {
1079
+ "setup": "cargo build --release",
1080
+ "start": "./target/release/crypto_ability"
1081
+ }
1082
+ }
1083
+
1084
+ // Load and use regardless of implementation language:
1085
+ const hashAbility = await loadAbility('hash-go', 'stdio');
1086
+ const result = await hashAbility.sha256({ data: 'hello' });
1087
+ ```
1088
+
1089
+ ## 🔧 Development Workflow
1090
+
1091
+ ### Testing Local Changes
1092
+
1093
+ When developing new features or testing changes to `@kadi.build/core` before publishing to NPM:
1094
+
1095
+ 0. **Clone the repository**
1096
+
1097
+ ```bash
1098
+ git clone https://gitlab.com/humin-game-lab/kadi/kadi-core.git
1099
+ ```
1100
+
1101
+ 1. **Pack the local version** (in the kadi-core directory):
1102
+
1103
+ ```bash
1104
+ cd /path/to/kadi-core
1105
+ npm pack
1106
+ # This creates: kadi.build-core-X.Y.Z.tgz
1107
+ ```
1108
+
1109
+ 2. **Install in your project**:
1110
+
1111
+ ```bash
1112
+ cd /path/to/your-project
1113
+ npm install /path/to/kadi-core/kadi.build-core-X.Y.Z.tgz
1114
+ ```
1115
+
1116
+ 3. **For ability development**, update the ability's preflight script:
1117
+
1118
+ ```json
1119
+ {
1120
+ "scripts": {
1121
+ "preflight": "npm uninstall @kadi.build/core && npm install /path/to/kadi.build-core-X.Y.Z.tgz"
1122
+ }
1123
+ }
1124
+ ```
1125
+
1126
+ ## 📖 API Reference
1127
+
1128
+ ### Core Classes
1129
+
1130
+ #### `KadiClient`
1131
+
1132
+ The unified class for all KADI operations - serving tools, calling remote abilities, and managing events.
1133
+
1134
+ ```javascript
1135
+ import { KadiClient } from '@kadi.build/core';
1136
+
1137
+ const client = new KadiClient({
1138
+ name: 'my-ability',
1139
+ role: 'ability', // 'agent' or 'ability'
1140
+ protocol: 'broker', // 'native', 'stdio', or 'broker'
1141
+ brokers: {
1142
+ dev: 'ws://localhost:8080',
1143
+ prod: 'ws://prod.example.com:8080'
1144
+ },
1145
+ defaultBroker: 'dev',
1146
+ network: 'global', // Primary network
1147
+ networks: ['global', 'custom-network'] // All networks
1148
+ });
1149
+ ```
1150
+
1151
+ **Configuration Options:**
1152
+
1153
+ - `name` - Ability/agent name
1154
+ - `role` - Operating role: 'agent' (consumer) or 'ability' (provider)
1155
+ - `protocol` - Transport protocol to use
1156
+ - `brokers` - Named broker configurations (object mapping names to URLs)
1157
+ - `defaultBroker` - Default broker name for sending messages
1158
+ - `network` - Primary network segment for message routing
1159
+ - `networks` - All network namespaces for tool discovery
1160
+ - `heartbeatIntervalSec` - Custom heartbeat interval (default: from broker)
225
1161
 
226
- pythonIPC.attachBroker(brk);
1162
+ **Tool Registration Methods:**
227
1163
 
228
- brk.on('open', function open() {
229
- console.log(`Connected to ${brk.url}`);
230
- const jsonObject = BrokerMessageBuilder.setup(
231
- 'TestAgent',
232
- 'A Broker Testing Agent',
233
- null,
234
- null
235
- );
236
- brk.send(jsonObject);
1164
+ - `registerTool(name, handler, schema?)` - Register a single tool
1165
+ - `getTools()` - Get list of registered tool names
1166
+ - `getToolNames()` - Get filtered list of tool names
1167
+ - `getToolHandler(name)` - Get a specific tool's handler
1168
+ - `getToolSchema(name)` - Get a specific tool's schema
1169
+ - `hasTool(name)` - Check if a tool is registered
1170
+
1171
+ **Remote Tool Invocation:**
1172
+
1173
+ - `callTool(targetAgent, toolName, params)` - Call a remote tool via broker
1174
+ - `discoverRemoteTools(targetAgent)` - List tools from a remote agent
1175
+ - `loadAbility(name, protocol?, options?)` - Load an ability (compatibility)
1176
+
1177
+ **Event System:**
1178
+
1179
+ - `publishEvent(eventName, data)` - Publish an event
1180
+ - `subscribeToEvent(pattern, callback)` - Subscribe with wildcards
1181
+ - `subscribeToEvents(patterns[], callback)` - Multiple subscriptions
1182
+ - `unsubscribeFromEvent(pattern, callback)` - Remove subscription
1183
+ - `onceEvent(pattern, callback)` - One-time subscription
1184
+
1185
+ **Connection Management:**
1186
+
1187
+ - `serve()` - Start serving (stdio/native modes)
1188
+ - `connectToBrokers()` - Connect to all configured brokers
1189
+ - `disconnect()` - Clean disconnect
1190
+ - `isConnected` - Check connection status
1191
+
1192
+ **Properties:**
1193
+
1194
+ - `agentId` - Unique agent identifier
1195
+ - `name` - Ability name
1196
+ - `role` - Current operating role
1197
+ - `protocol` - Active protocol
1198
+ - `brokers` - Configured broker map
1199
+ - `defaultBroker` - Default broker name
1200
+ - `currentBroker` - Currently active broker for sending
1201
+
1202
+ **Complete Usage Example:**
1203
+
1204
+ ```javascript
1205
+ import { KadiClient } from '@kadi.build/core';
1206
+
1207
+ // Create an ability that can serve tools
1208
+ const ability = new KadiClient({
1209
+ name: 'math-ability',
1210
+ role: 'ability',
1211
+ protocol: 'broker',
1212
+ brokers: {
1213
+ local: 'ws://localhost:8080',
1214
+ cloud: 'wss://api.example.com'
1215
+ },
1216
+ defaultBroker: 'local',
1217
+ networks: ['global']
237
1218
  });
238
1219
 
239
- brk.on('message', function incoming(data) {
240
- let msg = JSON.parse(data);
241
- if (msg.type == 'setup') {
242
- (async () => {
243
- try {
244
- await GetLLMProxy();
245
- } catch (error) {
246
- console.error('Error during GetLLMProxy:', error.message);
1220
+ // Register tools with schemas
1221
+ ability.registerTool(
1222
+ 'add',
1223
+ async ({ a, b }) => {
1224
+ // Publish event before processing
1225
+ await ability.publishEvent('math.operation', {
1226
+ operation: 'add',
1227
+ inputs: { a, b }
1228
+ });
1229
+
1230
+ const result = a + b;
1231
+
1232
+ // Publish completion event
1233
+ await ability.publishEvent('math.completed', {
1234
+ operation: 'add',
1235
+ result
1236
+ });
1237
+
1238
+ return { result };
1239
+ },
1240
+ {
1241
+ description: 'Add two numbers',
1242
+ inputSchema: {
1243
+ type: 'object',
1244
+ properties: {
1245
+ a: { type: 'number', description: 'First number' },
1246
+ b: { type: 'number', description: 'Second number' }
1247
+ },
1248
+ required: ['a', 'b']
1249
+ },
1250
+ outputSchema: {
1251
+ type: 'object',
1252
+ properties: {
1253
+ result: { type: 'number', description: 'Sum of a and b' }
247
1254
  }
248
- })();
1255
+ }
249
1256
  }
250
- // console.log('Received:', formatJSON(data));
1257
+ );
1258
+
1259
+ // Subscribe to events from other abilities
1260
+ ability.subscribeToEvent('system.*', (data) => {
1261
+ console.log('System event:', data);
1262
+ });
1263
+
1264
+ // Connect to brokers
1265
+ await ability.connectToBrokers();
1266
+
1267
+ // Call remote tools
1268
+ const result = await ability.callTool('translator-ability', 'translate', {
1269
+ text: 'Hello',
1270
+ to: 'es'
251
1271
  });
252
1272
 
253
- brk.on('error', function error(error) {
254
- console.error('WebSocket error:', error);
1273
+ // Graceful shutdown
1274
+ process.on('SIGTERM', async () => {
1275
+ await ability.disconnect();
1276
+ process.exit(0);
255
1277
  });
1278
+ ```
1279
+
1280
+ #### `loadAbility`
1281
+
1282
+ Load an ability by name and protocol.
1283
+
1284
+ ```javascript
1285
+ import { loadAbility } from '@kadi.build/core';
256
1286
 
257
- brk.on('close', function close() {
258
- console.log('Disconnected from the server');
259
- process.exit(0); // Exit the process when the WebSocket connection is closed
1287
+ const ability = await loadAbility('ability-name', 'protocol', {
1288
+ brokerUrl: 'ws://localhost:8080', // For broker protocol
1289
+ brokerName: 'dev', // Named broker to use
1290
+ networks: ['global'], // Network segments
1291
+ existingClient: client // Reuse existing KadiClient for broker
260
1292
  });
1293
+ ```
1294
+
1295
+ **Returns:** A proxy object with:
261
1296
 
262
- // Send launch command
1297
+ - Direct method calls (e.g., `ability.echo({ message: 'hello' })`)
1298
+ - `ability.events` - EventEmitter for subscribing to events (all protocols)
1299
+ - `ability.__call(method, params)` - Direct RPC call
1300
+ - `ability.__disconnect()` - Clean up resources
263
1301
 
264
- //Utilize log files
265
- // pythonIPC.sendMessage(IPCMessageBuilder.launch(pythonIPC.commandString, process.cwd() + "/logs"));
1302
+ ### Utility Functions
266
1303
 
267
- //No log files created
268
- pythonIPC.sendMessage(IPCMessageBuilder.launch(pythonIPC.commandString));
1304
+ ```javascript
1305
+ import {
1306
+ createLogger,
1307
+ getProjectJSON,
1308
+ getAbilityJSON,
1309
+ getAgentJSON,
1310
+ getBrokerUrl,
1311
+ runExecCommand
1312
+ } from '@kadi.build/core';
1313
+ ```
1314
+
1315
+ ### Debug Mode
1316
+
1317
+ Enable detailed logging:
1318
+
1319
+ ```bash
1320
+ DEBUG=kadi:* node index.js
269
1321
  ```
270
1322
 
271
- ### Handling IPC
1323
+ Check ability logs:
1324
+
1325
+ ```bash
1326
+ tail -f abilities/my-ability/1.0.0/my-ability.log
1327
+ ```
1328
+
1329
+ ## 💡 Additional Examples
1330
+
1331
+ ### Error Handling Pattern
272
1332
 
273
1333
  ```javascript
274
- const ipcManager = new IPCManager();
275
- const ipcInstance = ipcManager.createInstance('node', 'app.js', 'nodeApp');
276
- ipcInstance.on('message', (message) => console.log(message));
1334
+ const client = new KadiClient({
1335
+ name: 'robust-ability',
1336
+ role: 'ability'
1337
+ });
1338
+
1339
+ client.registerTool('riskyOperation', async (params) => {
1340
+ try {
1341
+ const result = await performOperation(params);
1342
+ await client.publishEvent('operation.success', { result });
1343
+ return { success: true, result };
1344
+ } catch (error) {
1345
+ await client.publishEvent('operation.error', {
1346
+ error: error.message,
1347
+ params
1348
+ });
1349
+ // Return error in structured format
1350
+ return {
1351
+ success: false,
1352
+ error: error.message
1353
+ };
1354
+ }
1355
+ });
277
1356
  ```
278
1357
 
1358
+ ### Health Check Pattern
1359
+
1360
+ ```javascript
1361
+ // Register a standard health check tool
1362
+ client.registerTool('health', async () => {
1363
+ const checks = {
1364
+ memory: process.memoryUsage(),
1365
+ uptime: process.uptime(),
1366
+ brokers: client.isConnected ? 'connected' : 'disconnected',
1367
+ timestamp: Date.now()
1368
+ };
1369
+
1370
+ return {
1371
+ status: 'healthy',
1372
+ checks
1373
+ };
1374
+ });
1375
+ ```
1376
+
1377
+ ## 🐛 Troubleshooting
1378
+
1379
+ ### Common Issues and Solutions
1380
+
1381
+ #### Broker Connection Failures
1382
+
1383
+ **Problem**: "Failed to connect to any brokers"
1384
+
1385
+ **Solutions**:
1386
+
1387
+ - Verify broker URLs are correct
1388
+ - Check network connectivity
1389
+ - Ensure broker is running
1390
+ - Try connecting with lower security (ws:// before wss://)
1391
+
1392
+ #### Events Not Arriving
1393
+
1394
+ **Problem**: Subscribed to events but callbacks not triggered
1395
+
1396
+ **Solutions**:
1397
+
1398
+ - Subscribe BEFORE triggering events
1399
+ - Check pattern matching (use exact match first)
1400
+ - Verify publisher and subscriber are on same network
1401
+ - Enable debug logging to trace event flow
1402
+
1403
+ #### Cross-Language Abilities Not Loading
1404
+
1405
+ **Problem**: "Cannot find module 'ability.js'" when loading Go/Python abilities
1406
+
1407
+ **Solutions**:
1408
+
1409
+ - Ensure `scripts.start` field exists in agent.json
1410
+ - Verify the binary/script is executable
1411
+ - Check that setup script has been run
1412
+ - Use stdio protocol, not native
1413
+
1414
+ #### Memory Leaks
1415
+
1416
+ **Problem**: Memory usage grows over time
1417
+
1418
+ **Solutions**:
1419
+
1420
+ - Always call unsubscribe functions
1421
+ - Use `onceEvent` for single-use subscriptions
1422
+ - Disconnect abilities when done
1423
+ - Clear event listeners on cleanup
1424
+
1425
+ ## 🤝 Contributing
1426
+
1427
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
1428
+
1429
+ 1. Fork the repository
1430
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
1431
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
1432
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
1433
+ 5. Open a Pull Request
1434
+
1435
+ ## 📄 License
1436
+
1437
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
1438
+
1439
+ ## 🔗 Related Projects
1440
+
1441
+ - [@kadi.build/cli](https://gitlab.com/humin-game-lab/kadi/kadi) - Command-line interface
1442
+ - [@kadi.build/broker](https://gitlab.com/humin-game-lab/kadi/kadi-broker) - The KADI broker
1443
+
1444
+ ## 📚 Resources
1445
+
1446
+ ## ⚠️ Error Codes
1447
+
1448
+ KADI core exposes a unified, typed error catalog to keep errors consistent across core and the broker.
1449
+
1450
+ - CoreErrorCodes: CORE\_\* errors for library/transport concerns
1451
+ - BrokerErrorCodes: BROKER\_\* errors mirrored for broker interactions
1452
+ - ErrorCodes: merged view of both
1453
+
1454
+ Usage:
1455
+
1456
+ ```ts
1457
+ import { KadiError, CoreErrorCodes } from '@kadi.build/core';
1458
+
1459
+ throw new KadiError(
1460
+ CoreErrorCodes.CORE_CONFIG_MISSING.code,
1461
+ 'scripts.start is required for stdio',
1462
+ { transport: 'stdio', abilityName: 'echo' }
1463
+ );
1464
+ ```
1465
+
1466
+ Guidelines:
1467
+
1468
+ - Do not use raw string codes like 'CONFIG_MISSING'; always use the exported constants.
1469
+ - Prefer KadiError helper factories where available (e.g., KadiError.abilityNotFound()).
1470
+ - KadiError automatically tags domain from the code prefix (core/broker) and preserves the original stack.
1471
+ - Broker-side codes are fully prefixed: BROKER*SESSION*_, BROKER*AUTH*_, BROKER*AGENT*_, BROKER*NETWORK*_, etc.
1472
+
1473
+ - [Event System Deep Dive](docs/event-system.md) - Complete guide to the event architecture
1474
+ - [API Documentation](https://docs.kadi.build) - Full API reference
1475
+ - [Examples Repository](https://github.com/kadi-examples) - More examples and patterns
1476
+
279
1477
  ---
280
1478
 
281
- ## TODO:
282
-
283
- - Once @kadi.build/core and dependencies are done, @kadi.build/cli (and all commands), need to be rewritten, using the @kadi.build/core module. this will be v1.0 release
284
-
285
- * [ ] add getConnectedAgents to the Broker, it should call the IBroker method
286
-
287
- * [x] add key to IBroker object
288
- * [ ] IBroker on 'message' should listen for setup event, and update teh uuid and key for the IBroker object
289
-
290
- * [ ] Extract Broker and IPC to their own node module and host on npm
291
- * [x] Publish @kadi.build/core to npm
292
- * [x] Setup of python environment needs to happen once an IPC Inteface is created
293
- This include setting up the virtual environment and installing the required packages
294
- * [x] Add method to IPCManger to set IPCInterfaces from agent.json. If IPCManger has empty list, it thorws an error that no IAbilityIPC's can be created. IPCManger passes the proper interface command to IAbilityIPC on creation from this list, IAbilityIPC does not need access directly to agent.json. Controlling app can update this list anytime a new IAbilityInterface is created, allowing the agent.json to be updated during runtime.
295
- * [x] Need to adapt ipc.js to read from the @kadi.build/core folder when trying to open IPC instances, instead of project folder. @kadi.build/core should be in the npm_modules folder (when deployed)
296
- * [x] The test code needs to fully test the IPCManger interface, and not just the IPCAbility
297
- * [x] If node fails, we need to make sure python IPC interface and child processes also close out.
298
- * [x] Add buffer to error handeling in IAbilityIPC, mimic same process used in std input
299
- * [x] Verify if Python IAbilityIPC needs to utilize buffering, and node passing '\n' at end of jso
300
- * [x] Setup passthrough for broker messages to/from Language IPC interface
301
- * [x] Add broker message builder/parser in python IAbilityIPC
302
- * [x] Update BrokerMessageBuilder to use same format at IPCMessageBuilder
303
- * [x] Update Broker so it extends EventEmitter, rather than have a property of event emitter (similar to IAbilityIPC)
304
- * [x] update python IAbility so that it creates a timestamped command_output.log file, new file for each run, in the project root directory... not the @kadi.build/core directory
305
- * [x] Comment out debug messages
306
- * [x] Add ability to allow for launched process to be piped to event handler (launch command should default to process, but allow user to set flag that will save all outputs to file in the event handler)
1479
+ Built with ❤️ by the KADI team