@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 +190 -0
- package/agent.json +3 -3
- package/dist/client.d.ts +41 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +78 -0
- package/dist/client.js.map +1 -1
- package/dist/crypto-service.d.ts +100 -0
- package/dist/crypto-service.d.ts.map +1 -0
- package/dist/crypto-service.js +144 -0
- package/dist/crypto-service.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +83 -0
- package/src/crypto-service.ts +169 -0
- package/src/index.ts +3 -0
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.
|
|
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.
|
|
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.
|
|
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
|
*/
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EACV,YAAY,EAEZ,cAAc,EACd,WAAW,EAGX,oBAAoB,EACpB,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EAOnB,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,WAAW,EACZ,MAAM,YAAY,CAAC;AAQpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
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
|
/**
|