@kadi.build/core 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,6 +6,7 @@ kadi-core lets you:
6
6
  - **Expose functions** as callable tools over the network
7
7
  - **Discover and invoke** remote services without hardcoding URLs
8
8
  - **Communicate via events** across your service mesh
9
+ - **Load configuration** from `config.yml` with project → global fallback and env var overrides
9
10
  - **Manage agent.json** configuration across projects, abilities, and global scope
10
11
  - **Run background processes** with headless, piped, and JSON-RPC bridge modes
11
12
  - **Install as an ability** — use kadi-core itself as a dependency in any agent
@@ -59,7 +60,9 @@ Now another agent can load and use it:
59
60
  - [Serving as an Ability](#serving-as-an-ability)
60
61
  - [Agent.json Management](#agentjson-management)
61
62
  - [Process Manager](#process-manager)
63
+ - [Encryption (Crypto)](#encryption-crypto)
62
64
  - [Installing as an Ability](#installing-as-an-ability)
65
+ - [Configuration (loadConfig)](#configuration-loadconfig)
63
66
  - [Error Handling](#error-handling)
64
67
  - [API Reference](#api-reference)
65
68
  - [Troubleshooting](#troubleshooting)
@@ -210,6 +213,8 @@ const client = new KadiClient({
210
213
 
211
214
  This means abilities that use the native-mode pattern (connect first, then register tools locally) will correctly announce zero tools to the broker on reconnection, just as they did on the initial connect.
212
215
 
216
+ **Event subscriptions on reconnection:** All broker event subscriptions (registered via `subscribe()`) are automatically re-established on reconnect. Your event handlers continue working transparently — no manual resubscription needed.
217
+
213
218
  If you dynamically register new tools after the initial connection and want the broker to know about them, call `refreshBrokerTools()`:
214
219
 
215
220
  ```typescript
@@ -227,6 +232,25 @@ await client.refreshBrokerTools();
227
232
  await client.refreshBrokerTools('production');
228
233
  ```
229
234
 
235
+ ### Lifecycle Hooks
236
+
237
+ Register callbacks for broker connection lifecycle events:
238
+
239
+ ```typescript
240
+ // Run cleanup logic when a broker disconnects
241
+ client.onDisconnect((brokerName) => {
242
+ console.log(`Lost connection to ${brokerName}`);
243
+ });
244
+
245
+ // Run logic after a broker successfully reconnects
246
+ client.onReconnect((brokerName) => {
247
+ console.log(`Reconnected to ${brokerName}`);
248
+ // Good place to refresh state, re-sync data, etc.
249
+ });
250
+ ```
251
+
252
+ Both hooks fire with the broker name and support async callbacks. Multiple hooks can be registered and will run in order. Errors in hooks are caught and logged — they won't break the reconnection flow.
253
+
230
254
  ---
231
255
 
232
256
  ## Loading Abilities
@@ -664,6 +688,55 @@ await pm.shutdown();
664
688
 
665
689
  ---
666
690
 
691
+ ## Encryption (Crypto)
692
+
693
+ `client.crypto` provides NaCl sealed-box encryption for secure inter-agent communication. Sealed boxes let anyone encrypt a message using the recipient's public key — only the recipient can decrypt it. The sender remains anonymous (use API tokens or signatures for authentication).
694
+
695
+ Keys are derived automatically from the agent's Ed25519 identity — no extra key management required.
696
+
697
+ ### Encrypting for Another Agent
698
+
699
+ ```typescript
700
+ // Agent A encrypts secrets for Agent B
701
+ const encrypted = agentA.crypto.encryptFor(
702
+ JSON.stringify({ API_KEY: 'sk-123', DB_PASS: 'hunter2' }),
703
+ agentB.publicKey // Ed25519 identity key (auto-converted to X25519)
704
+ );
705
+ ```
706
+
707
+ ### Decrypting
708
+
709
+ ```typescript
710
+ // Agent B decrypts
711
+ const plaintext = agentB.crypto.decrypt(encrypted);
712
+ const secrets = JSON.parse(plaintext);
713
+ ```
714
+
715
+ ### Getting Your Encryption Public Key
716
+
717
+ ```typescript
718
+ // X25519 encryption key (base64) — share this with senders
719
+ const encryptionKey = client.crypto.publicKey;
720
+
721
+ // Ed25519 identity key — the standard client.publicKey
722
+ const identityKey = client.crypto.identityPublicKey;
723
+ ```
724
+
725
+ ### Key Format Auto-Detection
726
+
727
+ `encryptFor()` accepts both key formats:
728
+
729
+ - **Ed25519 SPKI DER base64** (44 bytes decoded) — the standard `client.publicKey` format, auto-converted to X25519
730
+ - **Raw X25519 base64** (32 bytes decoded) — used directly, no conversion needed
731
+
732
+ ```typescript
733
+ // Both work:
734
+ client.crypto.encryptFor('secret', otherAgent.publicKey); // Ed25519 → auto-converted
735
+ client.crypto.encryptFor('secret', otherAgent.crypto.publicKey); // X25519 → used directly
736
+ ```
737
+
738
+ ---
739
+
667
740
  ## Installing as an Ability
668
741
 
669
742
  kadi-core v0.10.0 ships its own `agent.json` with `type: "ability"`, so it can be installed into any agent project using the standard `kadi install` workflow:
@@ -685,6 +758,102 @@ This is particularly useful for:
685
758
 
686
759
  ---
687
760
 
761
+ ## Configuration (loadConfig)
762
+
763
+ Load settings from `config.yml` with a standardized 3-tier resolution:
764
+
765
+ 1. **Environment variables** — `PREFIX_KEY` (highest priority)
766
+ 2. **Project config.yml** — walk up from CWD to find the nearest `config.yml`
767
+ 3. **Global `~/.kadi/config.yml`** — shared KADI infrastructure settings
768
+ 4. **Built-in defaults** — hardcoded fallbacks (lowest priority)
769
+
770
+ Secrets (API keys, tokens) should **never** go in `config.yml`. Use `kadi secret` for credentials.
771
+
772
+ ### Basic Usage
773
+
774
+ ```typescript
775
+ import { loadConfig } from '@kadi.build/core';
776
+
777
+ const { config, configPath, source } = loadConfig({
778
+ section: 'tunnel',
779
+ envPrefix: 'KADI_TUNNEL',
780
+ defaults: {
781
+ server_addr: 'kadi.build:7835',
782
+ tunnel_domain: 'tunnels.kadi.build',
783
+ },
784
+ });
785
+
786
+ console.log(config.server_addr); // from config.yml or default
787
+ console.log(configPath); // '/path/to/config.yml' or null
788
+ console.log(source); // 'project' | 'global' | 'default'
789
+ ```
790
+
791
+ With a `config.yml` like:
792
+
793
+ ```yaml
794
+ tunnel:
795
+ server_addr: kadi.build:7835
796
+ tunnel_domain: tunnels.kadi.build
797
+
798
+ memory:
799
+ database: kadi_memory
800
+ embedding_model: text-embedding-3-small
801
+ ```
802
+
803
+ ### Options
804
+
805
+ | Option | Type | Default | Description |
806
+ |--------|------|---------|-------------|
807
+ | `section` | `string` | *(required)* | Top-level YAML key to extract |
808
+ | `startDir` | `string` | `process.cwd()` | Directory to start the upward walk |
809
+ | `filename` | `string` | `'config.yml'` | Config filename to search for |
810
+ | `envPrefix` | `string` | — | Env var prefix for automatic overrides |
811
+ | `defaults` | `Record<string, unknown>` | `{}` | Built-in defaults (lowest priority) |
812
+
813
+ ### Environment Variable Overrides
814
+
815
+ When `envPrefix` is set, loadConfig scans `process.env` for matching keys:
816
+
817
+ ```
818
+ envPrefix: 'MEMORY'
819
+
820
+ MEMORY_DATABASE=my_db → config.database = 'my_db'
821
+ MEMORY_EMBEDDING_MODEL=ada → config.embedding_model = 'ada'
822
+ ```
823
+
824
+ Values are coerced automatically: `"true"` → `true`, `"42"` → `42`, `"3.14"` → `3.14`.
825
+
826
+ ### File Discovery Helpers
827
+
828
+ ```typescript
829
+ import { findConfigFile, findGlobalConfigFile } from '@kadi.build/core';
830
+
831
+ // Walk up from CWD to find config.yml
832
+ const projectConfig = findConfigFile();
833
+
834
+ // Walk up from a specific directory
835
+ const agentConfig = findConfigFile('config.yml', '/path/to/agent');
836
+
837
+ // Check ~/.kadi/config.yml
838
+ const globalConfig = findGlobalConfigFile();
839
+ ```
840
+
841
+ ### Result Type
842
+
843
+ ```typescript
844
+ import type { ConfigResult, ConfigSource, LoadConfigOptions } from '@kadi.build/core';
845
+
846
+ interface ConfigResult<T = Record<string, unknown>> {
847
+ config: T; // Merged config object
848
+ configPath: string | null; // Path to config.yml used, or null
849
+ source: ConfigSource; // 'project' | 'global' | 'default'
850
+ }
851
+ ```
852
+
853
+ > **Note:** `loadConfig` requires `js-yaml` at runtime. It is lazy-imported — if your project doesn't use `loadConfig`, you don't need `js-yaml` installed.
854
+
855
+ ---
856
+
688
857
  ## Error Handling
689
858
 
690
859
  All errors are `KadiError` with structured codes and context:
@@ -767,6 +936,8 @@ new KadiClient(config: ClientConfig)
767
936
  | `emit(event, data)` | Emit event to consumer (when serving as ability) |
768
937
  | `serve(mode)` | Serve as ability (`'stdio'` or `'broker'`) |
769
938
  | `refreshBrokerTools(broker?)` | Recapture tool snapshot and re-announce to broker. See [Reconnection](#reconnection) |
939
+ | `onDisconnect(hook)` | Register callback for broker disconnect. See [Lifecycle Hooks](#lifecycle-hooks) |
940
+ | `onReconnect(hook)` | Register callback for broker reconnect. See [Lifecycle Hooks](#lifecycle-hooks) |
770
941
  | `getConnectedBrokers()` | List connected broker names |
771
942
 
772
943
  **Properties:**
@@ -775,6 +946,7 @@ new KadiClient(config: ClientConfig)
775
946
  |----------|------|-------------|
776
947
  | `agentJson` | `AgentJsonManager` | Lazy-initialized agent.json manager. See [Agent.json Management](#agentjson-management) |
777
948
  | `processes` | `ProcessManager` | Lazy-initialized process manager. See [Process Manager](#process-manager) |
949
+ | `crypto` | `CryptoService` | Lazy-initialized encryption service. See [Encryption (Crypto)](#encryption-crypto) |
778
950
 
779
951
  ### LoadedAbility
780
952
 
@@ -895,6 +1067,24 @@ Returned by `ProcessManager.spawn()`. Extends `EventEmitter`.
895
1067
  | `error` | `Error` | all |
896
1068
  | `notification` | `{ method, params }` | bridge |
897
1069
 
1070
+ ### CryptoService
1071
+
1072
+ Accessed via `client.crypto`. Lazy-initialized from the agent's Ed25519 identity keys.
1073
+
1074
+ **Properties:**
1075
+
1076
+ | Property | Type | Description |
1077
+ |----------|------|-------------|
1078
+ | `publicKey` | `string` | X25519 encryption public key (base64). Share with senders. Cached after first access |
1079
+ | `identityPublicKey` | `string` | Ed25519 identity public key (SPKI DER base64). Same as `client.publicKey` |
1080
+
1081
+ **Methods:**
1082
+
1083
+ | Method | Returns | Description |
1084
+ |--------|---------|-------------|
1085
+ | `encryptFor(plaintext, recipientPublicKey)` | `string` | Encrypt string for recipient. Returns base64 sealed box. Accepts Ed25519 or X25519 keys |
1086
+ | `decrypt(ciphertext)` | `string` | Decrypt base64 sealed box. Throws on failure |
1087
+
898
1088
  ---
899
1089
 
900
1090
  ## Troubleshooting
package/agent.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "kadi-core",
3
- "version": "0.12.0",
3
+ "version": "0.15.0",
4
4
  "type": "ability",
5
5
  "license": "MIT",
6
6
  "description": "Core SDK for building KADI agents - provides client, transports, agent.json management, and process management",
7
7
  "entrypoint": "dist/index.js",
8
8
  "repo": "https://gitlab.com/humin-game-lab/agent-abilities/kadi-core.git",
9
- "lib": "https://gitlab.com/humin-game-lab/agent-abilities/kadi-core/-/archive/v0.11.0/kadi-core-v0.11.0.zip",
9
+ "lib": "https://gitlab.com/humin-game-lab/agent-abilities/kadi-core/-/archive/v0.15.0/kadi-core-v0.15.0.zip",
10
10
  "abilities": {},
11
11
 
12
12
  "scripts": {
13
- "preflight": "echo 'kadi-core v0.11.0 checking environment...'",
13
+ "preflight": "echo 'kadi-core v0.15.0 checking environment...'",
14
14
  "setup": "npm install && npm run build",
15
15
  "postinstall": "node scripts/symlink.mjs",
16
16
  "start": "echo 'kadi-core is a library - import it in your code'",
package/dist/client.d.ts CHANGED
@@ -18,6 +18,7 @@ import { type EncryptionKeyPair } from './crypto.js';
18
18
  import type { ClientConfig, ClientIdentity, BrokerState, BrokerToolDefinition, ZodToolDefinition, ToolHandler, LoadedAbility, ToolExecutionBridge, RegisterToolOptions, LoadNativeOptions, LoadStdioOptions, LoadBrokerOptions, InvokeRemoteOptions, BrokerEventHandler, PublishOptions, SubscribeOptions, EmitOptions } from './types.js';
19
19
  import { AgentJsonManager } from './agent-json.js';
20
20
  import { ProcessManager } from './process-manager.js';
21
+ import { CryptoService } from './crypto-service.js';
21
22
  /**
22
23
  * The main client for building KADI agents.
23
24
  *
@@ -65,10 +66,14 @@ export declare class KadiClient {
65
66
  private readonly loadedAbilities;
66
67
  /** Registered disconnect hooks */
67
68
  private readonly disconnectHooks;
69
+ /** Registered reconnect hooks */
70
+ private readonly reconnectHooks;
68
71
  /** Lazy-initialized AgentJsonManager */
69
72
  private _agentJson;
70
73
  /** Lazy-initialized ProcessManager */
71
74
  private _processes;
75
+ /** Lazy-initialized CryptoService */
76
+ private _crypto;
72
77
  /** Ed25519 private key (DER format) - used for signing */
73
78
  private readonly _privateKey;
74
79
  /** Base64-encoded Ed25519 public key (SPKI DER format) */
@@ -482,6 +487,23 @@ export declare class KadiClient {
482
487
  * Register a cleanup function to run on disconnect.
483
488
  */
484
489
  onDisconnect(hook: () => Promise<void>): void;
490
+ /**
491
+ * Register a hook to run after a successful broker reconnection.
492
+ *
493
+ * Event subscriptions are automatically re-established on reconnect,
494
+ * so most agents won't need this. Use it for additional recovery logic:
495
+ * re-fetching state, logging, sending a "I'm back" message, etc.
496
+ *
497
+ * @param hook - Called with the broker name after each successful reconnect.
498
+ *
499
+ * @example
500
+ * ```typescript
501
+ * client.onReconnect(async (brokerName) => {
502
+ * console.log(`Reconnected to ${brokerName} — re-initializing state`);
503
+ * });
504
+ * ```
505
+ */
506
+ onReconnect(hook: (brokerName: string) => void | Promise<void>): void;
485
507
  /**
486
508
  * Disconnect from broker(s) and run registered cleanup hooks.
487
509
  *
@@ -743,6 +765,25 @@ export declare class KadiClient {
743
765
  * ```
744
766
  */
745
767
  get processes(): ProcessManager;
768
+ /**
769
+ * Access the CryptoService for encrypting/decrypting messages.
770
+ *
771
+ * Lazily created on first access. Uses NaCl sealed boxes (X25519 +
772
+ * XSalsa20-Poly1305) derived from this agent's Ed25519 identity keys.
773
+ *
774
+ * @example
775
+ * ```typescript
776
+ * // Encrypt a message for another agent
777
+ * const encrypted = client.crypto.encryptFor('secret', otherAgent.publicKey);
778
+ *
779
+ * // Decrypt a message sent to this agent
780
+ * const plaintext = client.crypto.decrypt(encrypted);
781
+ *
782
+ * // Get this agent's encryption public key (to share with others)
783
+ * const pubKey = client.crypto.publicKey;
784
+ * ```
785
+ */
786
+ get crypto(): CryptoService;
746
787
  /**
747
788
  * Get agent information (for readAgentJson protocol).
748
789
  */
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js CHANGED
@@ -28,6 +28,7 @@ import { loadStdioTransport } from './transports/stdio.js';
28
28
  import { loadBrokerTransport } from './transports/broker.js';
29
29
  import { AgentJsonManager } from './agent-json.js';
30
30
  import { ProcessManager } from './process-manager.js';
31
+ import { CryptoService } from './crypto-service.js';
31
32
  // ═══════════════════════════════════════════════════════════════
32
33
  // CONSTANTS
33
34
  // ═══════════════════════════════════════════════════════════════
@@ -153,10 +154,14 @@ export class KadiClient {
153
154
  loadedAbilities = new Map();
154
155
  /** Registered disconnect hooks */
155
156
  disconnectHooks = [];
157
+ /** Registered reconnect hooks */
158
+ reconnectHooks = [];
156
159
  /** Lazy-initialized AgentJsonManager */
157
160
  _agentJson = null;
158
161
  /** Lazy-initialized ProcessManager */
159
162
  _processes = null;
163
+ /** Lazy-initialized CryptoService */
164
+ _crypto = null;
160
165
  // ─────────────────────────────────────────────────────────────
161
166
  // IDENTITY (single keypair shared across all broker connections)
162
167
  // ─────────────────────────────────────────────────────────────
@@ -1374,9 +1379,36 @@ export class KadiClient {
1374
1379
  await this.registerWithBroker(state);
1375
1380
  // Finalize connection (heartbeat + status)
1376
1381
  this.finalizeConnection(state);
1382
+ // Re-subscribe event patterns that were active before disconnect.
1383
+ // cleanupBroker() cleared subscribedPatterns but preserved eventHandlers,
1384
+ // so we replay broker-side subscriptions for every pattern that still has handlers.
1385
+ if (state.eventHandlers.size > 0) {
1386
+ const patterns = [...state.eventHandlers.keys()];
1387
+ console.error(`[KADI] Re-subscribing ${patterns.length} event pattern(s) on broker "${state.name}"`);
1388
+ for (const pattern of patterns) {
1389
+ try {
1390
+ await this.sendRequest(state, protocol.eventSubscribe(this.nextRequestId++, pattern));
1391
+ state.subscribedPatterns.add(pattern);
1392
+ }
1393
+ catch (err) {
1394
+ const msg = err instanceof Error ? err.message : String(err);
1395
+ console.error(`[KADI] Failed to re-subscribe "${pattern}" on broker "${state.name}": ${msg}`);
1396
+ }
1397
+ }
1398
+ }
1377
1399
  // Success!
1378
1400
  console.error(`[KADI] Reconnected to broker "${state.name}" after ${state.reconnectAttempts} attempts`);
1379
1401
  state.reconnectAttempts = 0;
1402
+ // Fire reconnect hooks (best-effort, don't let a failing hook break reconnect)
1403
+ for (const hook of this.reconnectHooks) {
1404
+ try {
1405
+ await hook(state.name);
1406
+ }
1407
+ catch (err) {
1408
+ const msg = err instanceof Error ? err.message : String(err);
1409
+ console.error(`[KADI] Reconnect hook error for broker "${state.name}": ${msg}`);
1410
+ }
1411
+ }
1380
1412
  }
1381
1413
  catch (error) {
1382
1414
  // Log the error and try again
@@ -1396,6 +1428,25 @@ export class KadiClient {
1396
1428
  onDisconnect(hook) {
1397
1429
  this.disconnectHooks.push(hook);
1398
1430
  }
1431
+ /**
1432
+ * Register a hook to run after a successful broker reconnection.
1433
+ *
1434
+ * Event subscriptions are automatically re-established on reconnect,
1435
+ * so most agents won't need this. Use it for additional recovery logic:
1436
+ * re-fetching state, logging, sending a "I'm back" message, etc.
1437
+ *
1438
+ * @param hook - Called with the broker name after each successful reconnect.
1439
+ *
1440
+ * @example
1441
+ * ```typescript
1442
+ * client.onReconnect(async (brokerName) => {
1443
+ * console.log(`Reconnected to ${brokerName} — re-initializing state`);
1444
+ * });
1445
+ * ```
1446
+ */
1447
+ onReconnect(hook) {
1448
+ this.reconnectHooks.push(hook);
1449
+ }
1399
1450
  /**
1400
1451
  * Disconnect from broker(s) and run registered cleanup hooks.
1401
1452
  *
@@ -2067,6 +2118,33 @@ export class KadiClient {
2067
2118
  return this._processes;
2068
2119
  }
2069
2120
  // ─────────────────────────────────────────────────────────────
2121
+ // CRYPTO SERVICE
2122
+ // ─────────────────────────────────────────────────────────────
2123
+ /**
2124
+ * Access the CryptoService for encrypting/decrypting messages.
2125
+ *
2126
+ * Lazily created on first access. Uses NaCl sealed boxes (X25519 +
2127
+ * XSalsa20-Poly1305) derived from this agent's Ed25519 identity keys.
2128
+ *
2129
+ * @example
2130
+ * ```typescript
2131
+ * // Encrypt a message for another agent
2132
+ * const encrypted = client.crypto.encryptFor('secret', otherAgent.publicKey);
2133
+ *
2134
+ * // Decrypt a message sent to this agent
2135
+ * const plaintext = client.crypto.decrypt(encrypted);
2136
+ *
2137
+ * // Get this agent's encryption public key (to share with others)
2138
+ * const pubKey = client.crypto.publicKey;
2139
+ * ```
2140
+ */
2141
+ get crypto() {
2142
+ if (!this._crypto) {
2143
+ this._crypto = new CryptoService(this._privateKey, this._publicKeyBase64);
2144
+ }
2145
+ return this._crypto;
2146
+ }
2147
+ // ─────────────────────────────────────────────────────────────
2070
2148
  // UTILITY
2071
2149
  // ─────────────────────────────────────────────────────────────
2072
2150
  /**