@teneo-protocol/sdk 3.0.1 → 3.1.2
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/CHANGELOG.md +21 -0
- package/README.md +234 -4
- package/dist/managers/message-router.d.ts +35 -0
- package/dist/managers/message-router.d.ts.map +1 -1
- package/dist/managers/message-router.js +143 -2
- package/dist/managers/message-router.js.map +1 -1
- package/dist/payments/networks.js +1 -1
- package/dist/payments/networks.js.map +1 -1
- package/dist/payments/payment-client.d.ts.map +1 -1
- package/dist/payments/payment-client.js +5 -3
- package/dist/payments/payment-client.js.map +1 -1
- package/dist/teneo-sdk.d.ts +5 -4
- package/dist/teneo-sdk.d.ts.map +1 -1
- package/dist/teneo-sdk.js +26 -5
- package/dist/teneo-sdk.js.map +1 -1
- package/dist/types/config.d.ts +29 -3
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +21 -2
- package/dist/types/config.js.map +1 -1
- package/dist/types/error-codes.d.ts +3 -0
- package/dist/types/error-codes.d.ts.map +1 -1
- package/dist/types/error-codes.js +4 -0
- package/dist/types/error-codes.js.map +1 -1
- package/dist/types/events.d.ts +3 -0
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/events.js.map +1 -1
- package/package.json +1 -1
- package/src/managers/message-router.ts +183 -3
- package/src/payments/networks.ts +1 -1
- package/src/payments/payment-client.ts +6 -3
- package/src/teneo-sdk.ts +28 -5
- package/src/types/config.ts +23 -2
- package/src/types/error-codes.ts +5 -0
- package/src/types/events.ts +5 -0
- package/tests/unit/managers/message-router-autosummon.test.ts +338 -0
- package/tests/unit/sdk-new-methods.test.ts +26 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ All notable changes to the Teneo Protocol SDK will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.1.1] - 2026-02-19
|
|
9
|
+
|
|
10
|
+
### ✨ Pre-Flight Auto-Summon
|
|
11
|
+
|
|
12
|
+
The SDK now automatically adds missing agents to your room before sending commands, eliminating coordinator reject→retry cycles.
|
|
13
|
+
|
|
14
|
+
- **Pre-flight cache check**: Before sending a command, the SDK checks if the target agent is already in the room
|
|
15
|
+
- **Automatic addition**: If the agent is missing, it's added to the room before the command is sent
|
|
16
|
+
- **Lifecycle events**: `autosummon:start`, `autosummon:success`, `autosummon:failed` for full observability
|
|
17
|
+
- **Fallback path**: If pre-flight fails or cache is empty, falls back to coordinator-triggered auto-summon
|
|
18
|
+
- **Configuration**: Enable with `autoSummon: true` or `.withAutoSummon(true)` in the builder
|
|
19
|
+
|
|
20
|
+
### 🔐 Private Key Validation
|
|
21
|
+
|
|
22
|
+
- Constructor now validates private key format (64 hex characters / 32 bytes)
|
|
23
|
+
- Catches empty strings, malformed keys, and too-short keys immediately
|
|
24
|
+
- Prevents cryptic signing errors downstream
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
8
29
|
## [3.0.0] - 2026-01-28
|
|
9
30
|
|
|
10
31
|
*Built on top of v2.3.0 multi-network support*
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Connect your app to the Teneo AI Agent Network**
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/@teneo-protocol/sdk)
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
7
7
|
[](https://nodejs.org/)
|
|
8
8
|
[](/)
|
|
@@ -15,6 +15,46 @@ The Teneo Protocol SDK lets you connect your application to a **decentralized ne
|
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
+
## 🎉 What's New in v3.1.1
|
|
19
|
+
|
|
20
|
+
### 🤖 Pre-Flight Auto-Summon
|
|
21
|
+
|
|
22
|
+
The SDK now automatically detects when an agent is missing from your room and adds it **before** sending your command — no more failed requests or retry cycles:
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
const sdk = new TeneoSDK({
|
|
26
|
+
wsUrl: process.env.WS_URL,
|
|
27
|
+
privateKey: process.env.PRIVATE_KEY,
|
|
28
|
+
autoSummon: true // Enable auto-summon
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Agent not in room? SDK handles it automatically:
|
|
32
|
+
// 1. Checks local cache → agent missing
|
|
33
|
+
// 2. Fires autosummon:start
|
|
34
|
+
// 3. Adds agent to room
|
|
35
|
+
// 4. Fires autosummon:success
|
|
36
|
+
// 5. Sends your command
|
|
37
|
+
await sdk.sendDirectCommand({
|
|
38
|
+
agent: "example-agent",
|
|
39
|
+
command: "latest 2h",
|
|
40
|
+
room: roomId
|
|
41
|
+
}, true);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Lifecycle events** for full visibility:
|
|
45
|
+
|
|
46
|
+
- `autosummon:start` — agent addition initiated
|
|
47
|
+
- `autosummon:success` — agent added, command proceeding
|
|
48
|
+
- `autosummon:failed` — agent not found or addition failed, falls back to coordinator
|
|
49
|
+
|
|
50
|
+
### 🔐 Private Key Validation
|
|
51
|
+
|
|
52
|
+
Constructor now validates private key format immediately, catching empty or malformed keys before they cause cryptic signing errors downstream.
|
|
53
|
+
|
|
54
|
+
[See Full CHANGELOG](CHANGELOG.md#311---2026-02-19)
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
18
58
|
## 🎉 What's New in v3.0
|
|
19
59
|
|
|
20
60
|
Version 3.0 introduces **API Naming Improvements** for better clarity and consistency:
|
|
@@ -623,6 +663,71 @@ sdk.on("agent_room:list_available_error", (error) => {
|
|
|
623
663
|
});
|
|
624
664
|
```
|
|
625
665
|
|
|
666
|
+
### Auto-Summon
|
|
667
|
+
|
|
668
|
+
When you send a command to an agent that isn't in your room yet, the SDK can automatically add it for you. No manual `addAgentToRoom()` call needed — just send your command and the SDK handles the rest.
|
|
669
|
+
|
|
670
|
+
#### Setup
|
|
671
|
+
|
|
672
|
+
```typescript
|
|
673
|
+
const sdk = new TeneoSDK(
|
|
674
|
+
TeneoSDK.builder()
|
|
675
|
+
.withWebSocketUrl(process.env.TENEO_WS_URL!)
|
|
676
|
+
.withAuthentication(process.env.PRIVATE_KEY!)
|
|
677
|
+
.withAutoSummon(true) // Enable auto-summon
|
|
678
|
+
.withNetwork("base")
|
|
679
|
+
.build()
|
|
680
|
+
);
|
|
681
|
+
|
|
682
|
+
await sdk.connect();
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
#### How It Works
|
|
686
|
+
|
|
687
|
+
```typescript
|
|
688
|
+
// Just send a command — the agent doesn't need to be in your room
|
|
689
|
+
const response = await sdk.sendDirectCommand({
|
|
690
|
+
agent: "example-agent",
|
|
691
|
+
command: "latest 2h",
|
|
692
|
+
room: roomId
|
|
693
|
+
}, true);
|
|
694
|
+
|
|
695
|
+
// Behind the scenes:
|
|
696
|
+
// 1. SDK checks if agent is in the room (instant cache lookup)
|
|
697
|
+
// 2. If not -> automatically adds the agent to your room
|
|
698
|
+
// 3. Sends your command to the agent
|
|
699
|
+
// 4. Returns the response
|
|
700
|
+
console.log(response.humanized);
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
#### Auto-Summon Events
|
|
704
|
+
|
|
705
|
+
Track the auto-summon lifecycle to show loading states in your UI:
|
|
706
|
+
|
|
707
|
+
```typescript
|
|
708
|
+
// Agent is being added to the room
|
|
709
|
+
sdk.on("autosummon:start", (agentName, roomId) => {
|
|
710
|
+
showLoadingState(`Adding ${agentName} to your room...`);
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
// Agent was successfully added — command is now being processed
|
|
714
|
+
sdk.on("autosummon:success", (agentName, agentId, roomId) => {
|
|
715
|
+
showNotification(`${agentName} joined your room`);
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
// Agent could not be added (doesn't exist, is offline, etc.)
|
|
719
|
+
sdk.on("autosummon:failed", (agentName, roomId, reason) => {
|
|
720
|
+
showError(`Could not add ${agentName}: ${reason}`);
|
|
721
|
+
});
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
#### Behavior Notes
|
|
725
|
+
|
|
726
|
+
- **No duplicates**: If the agent is already in the room, the SDK skips the summon and sends your command directly.
|
|
727
|
+
- **Works with any message method**: Auto-summon triggers from both `sendDirectCommand()` and `sendMessage()` when the message targets an `@agent`.
|
|
728
|
+
- **Graceful fallback**: If the local cache is empty (e.g., first connection), the SDK falls back to server-side detection — your command still works, just with a small extra round trip.
|
|
729
|
+
- **Room ownership required**: Auto-summon only works in rooms you own, since adding agents requires owner permissions.
|
|
730
|
+
|
|
626
731
|
### Complete Example: Room Setup
|
|
627
732
|
|
|
628
733
|
```typescript
|
|
@@ -726,8 +831,8 @@ console.log(NETWORKS); // {}
|
|
|
726
831
|
await sdk.connect();
|
|
727
832
|
|
|
728
833
|
// After connect: NETWORKS populated with backend configuration
|
|
729
|
-
console.log(NETWORKS); // { peaq: {...}, base: {...}, avalanche: {...} }
|
|
730
|
-
const networks = getSupportedNetworks(); // ["peaq", "base", "avalanche"]
|
|
834
|
+
console.log(NETWORKS); // { peaq: {...}, base: {...}, avalanche: {...}, "x-layer": {...} }
|
|
835
|
+
const networks = getSupportedNetworks(); // ["peaq", "base", "avalanche", "x-layer"]
|
|
731
836
|
```
|
|
732
837
|
|
|
733
838
|
**Key Features:**
|
|
@@ -753,7 +858,7 @@ await sdk.connect();
|
|
|
753
858
|
|
|
754
859
|
// Get all supported networks (dynamically loaded from backend)
|
|
755
860
|
const networks = getSupportedNetworks();
|
|
756
|
-
console.log(networks); // e.g., ["peaq", "base", "avalanche"]
|
|
861
|
+
console.log(networks); // e.g., ["peaq", "base", "avalanche", "x-layer"]
|
|
757
862
|
|
|
758
863
|
// Get network by name
|
|
759
864
|
const baseNetwork = getNetwork("base");
|
|
@@ -803,6 +908,10 @@ These networks are currently supported (fetched dynamically from backend):
|
|
|
803
908
|
- High-throughput blockchain
|
|
804
909
|
- Sub-second finality
|
|
805
910
|
|
|
911
|
+
**X Layer Mainnet (chainId: 196)**
|
|
912
|
+
- OKX's Layer 2 network
|
|
913
|
+
- Low gas fees (OKB native token)
|
|
914
|
+
|
|
806
915
|
> **Note:** Network configurations are fetched from the backend and may change. Use `getSupportedNetworks()` to get the current list. The SDK automatically handles network selection based on agent requirements.
|
|
807
916
|
|
|
808
917
|
#### Settlement Router Integration (x402 v2.5)
|
|
@@ -1142,6 +1251,117 @@ Your App Teneo Backend Agent
|
|
|
1142
1251
|
|
|
1143
1252
|
---
|
|
1144
1253
|
|
|
1254
|
+
## 🔗 Wallet Transaction Flow (On-Chain Actions)
|
|
1255
|
+
|
|
1256
|
+
Some agents (e.g., Squid Router for cross-chain swaps) need your wallet to sign and submit on-chain transactions. The SDK handles this through a simple event-driven flow.
|
|
1257
|
+
|
|
1258
|
+
### How It Works
|
|
1259
|
+
|
|
1260
|
+
1. **Agent requests a transaction** — the SDK emits a `wallet:tx_requested` event with the transaction details
|
|
1261
|
+
2. **Your app signs and submits** — using your wallet/signer of choice (ethers, viem, web3.js, etc.)
|
|
1262
|
+
3. **Your app reports the result** — call `sendTxResult()` so the agent knows what happened
|
|
1263
|
+
4. **Agent may request more transactions** — for multi-step operations (e.g., approve + swap), the agent sends additional `trigger_wallet_tx` messages after each result
|
|
1264
|
+
|
|
1265
|
+
```
|
|
1266
|
+
Your App Teneo Backend Agent
|
|
1267
|
+
│ │ │
|
|
1268
|
+
│ │<── trigger_wallet_tx ────│ (e.g., "approve USDC")
|
|
1269
|
+
│<── wallet:tx_requested ─────│ │
|
|
1270
|
+
│ │ │
|
|
1271
|
+
│ [Sign & submit tx] │ │
|
|
1272
|
+
│ │ │
|
|
1273
|
+
│──── tx_result ─────────────>│──── tx_result ──────────>│
|
|
1274
|
+
│ (confirmed + txHash) │ │
|
|
1275
|
+
│ │ │
|
|
1276
|
+
│ │<── trigger_wallet_tx ────│ (e.g., "execute swap")
|
|
1277
|
+
│<── wallet:tx_requested ─────│ │
|
|
1278
|
+
│ │ │
|
|
1279
|
+
│ [Sign & submit tx] │ │
|
|
1280
|
+
│ │ │
|
|
1281
|
+
│──── tx_result ─────────────>│──── tx_result ──────────>│
|
|
1282
|
+
│ │ │
|
|
1283
|
+
│<───── task_response ────────│<────── response ─────────│
|
|
1284
|
+
└ └ └
|
|
1285
|
+
```
|
|
1286
|
+
|
|
1287
|
+
### Basic Usage
|
|
1288
|
+
|
|
1289
|
+
```typescript
|
|
1290
|
+
sdk.on("wallet:tx_requested", async (data) => {
|
|
1291
|
+
console.log(`Transaction requested by ${data.agentName}: ${data.description}`);
|
|
1292
|
+
console.log(`Chain: ${data.tx.chainId}, To: ${data.tx.to}, Value: ${data.tx.value}`);
|
|
1293
|
+
|
|
1294
|
+
try {
|
|
1295
|
+
// Sign and submit using your preferred library
|
|
1296
|
+
const txHash = await wallet.sendTransaction({
|
|
1297
|
+
to: data.tx.to,
|
|
1298
|
+
value: data.tx.value,
|
|
1299
|
+
data: data.tx.data,
|
|
1300
|
+
chainId: data.tx.chainId,
|
|
1301
|
+
});
|
|
1302
|
+
|
|
1303
|
+
// Wait for confirmation
|
|
1304
|
+
const receipt = await provider.waitForTransaction(txHash);
|
|
1305
|
+
|
|
1306
|
+
// Report success — include data.room so the server can route it back to the agent
|
|
1307
|
+
await sdk.sendTxResult(data.taskId, "confirmed", txHash, undefined, data.room);
|
|
1308
|
+
} catch (err) {
|
|
1309
|
+
// Report failure
|
|
1310
|
+
await sdk.sendTxResult(data.taskId, "failed", undefined, err.message, data.room);
|
|
1311
|
+
}
|
|
1312
|
+
});
|
|
1313
|
+
```
|
|
1314
|
+
|
|
1315
|
+
### Rejecting a Transaction
|
|
1316
|
+
|
|
1317
|
+
If the transaction is optional (`data.optional === true`) or you don't want to sign:
|
|
1318
|
+
|
|
1319
|
+
```typescript
|
|
1320
|
+
await sdk.sendTxResult(data.taskId, "rejected", undefined, undefined, data.room);
|
|
1321
|
+
```
|
|
1322
|
+
|
|
1323
|
+
### Event: `wallet:tx_requested`
|
|
1324
|
+
|
|
1325
|
+
| Field | Type | Description |
|
|
1326
|
+
|-------|------|-------------|
|
|
1327
|
+
| `taskId` | `string` | Task identifier — pass this back in `sendTxResult()` |
|
|
1328
|
+
| `agentName` | `string?` | Name of the agent requesting the transaction |
|
|
1329
|
+
| `tx.to` | `string` | Target contract/address |
|
|
1330
|
+
| `tx.value` | `string` | Value in wei |
|
|
1331
|
+
| `tx.data` | `string?` | Encoded calldata (for contract interactions) |
|
|
1332
|
+
| `tx.chainId` | `number` | Target chain (e.g., `8453` for Base) |
|
|
1333
|
+
| `description` | `string?` | Human-readable description of the transaction |
|
|
1334
|
+
| `optional` | `boolean` | Whether the user can skip this transaction |
|
|
1335
|
+
| `room` | `string?` | Room ID — **must** be passed back in `sendTxResult()` for routing |
|
|
1336
|
+
|
|
1337
|
+
### Method: `sendTxResult()`
|
|
1338
|
+
|
|
1339
|
+
```typescript
|
|
1340
|
+
await sdk.sendTxResult(taskId, status, txHash?, error?, room?)
|
|
1341
|
+
```
|
|
1342
|
+
|
|
1343
|
+
| Parameter | Type | Description |
|
|
1344
|
+
|-----------|------|-------------|
|
|
1345
|
+
| `taskId` | `string` | The `taskId` from the `wallet:tx_requested` event |
|
|
1346
|
+
| `status` | `"confirmed" \| "rejected" \| "failed"` | Transaction outcome |
|
|
1347
|
+
| `txHash` | `string?` | On-chain transaction hash (required for `"confirmed"`) |
|
|
1348
|
+
| `error` | `string?` | Error message (for `"failed"` status) |
|
|
1349
|
+
| `room` | `string?` | Room ID from the `wallet:tx_requested` event (required for routing) |
|
|
1350
|
+
|
|
1351
|
+
> **Important:** Always pass `data.room` from the event back into `sendTxResult()`. The server uses this to route the result to the correct agent. Without it, the agent won't receive your response and multi-step transactions will stall.
|
|
1352
|
+
|
|
1353
|
+
### Multi-Step Transactions
|
|
1354
|
+
|
|
1355
|
+
Some operations require multiple sequential transactions (e.g., ERC-20 approve followed by a swap). The agent handles sequencing — your code just needs to keep listening:
|
|
1356
|
+
|
|
1357
|
+
1. Agent sends `trigger_wallet_tx` #1 (approve) → you sign → you call `sendTxResult()`
|
|
1358
|
+
2. Agent receives the result, then sends `trigger_wallet_tx` #2 (swap) → you sign → you call `sendTxResult()`
|
|
1359
|
+
3. Agent sends `task_response` with the final outcome
|
|
1360
|
+
|
|
1361
|
+
Your `wallet:tx_requested` listener stays active for the entire lifecycle. Each transaction arrives as a separate event with the **same `taskId`** but different `tx` data. The task is only resolved when the agent sends the final `task_response`.
|
|
1362
|
+
|
|
1363
|
+
---
|
|
1364
|
+
|
|
1145
1365
|
## 🎨 Event System
|
|
1146
1366
|
|
|
1147
1367
|
The SDK is fully event-driven. Subscribe to what matters:
|
|
@@ -1196,6 +1416,16 @@ sdk.on("room:unsubscribed", (data) => {
|
|
|
1196
1416
|
});
|
|
1197
1417
|
```
|
|
1198
1418
|
|
|
1419
|
+
### Wallet Transaction Events
|
|
1420
|
+
|
|
1421
|
+
```typescript
|
|
1422
|
+
sdk.on("wallet:tx_requested", async (data) => {
|
|
1423
|
+
console.log(`🔗 ${data.agentName} requests tx on chain ${data.tx.chainId}`);
|
|
1424
|
+
console.log(` ${data.description}`);
|
|
1425
|
+
// See "Wallet Transaction Flow" section for full handling
|
|
1426
|
+
});
|
|
1427
|
+
```
|
|
1428
|
+
|
|
1199
1429
|
---
|
|
1200
1430
|
|
|
1201
1431
|
## ⚙️ Configuration
|
|
@@ -9,6 +9,7 @@ import { WebhookHandler } from "../handlers/webhook-handler";
|
|
|
9
9
|
import { ResponseFormatter, FormattedResponse } from "../formatters/response-formatter";
|
|
10
10
|
import { Logger, ResponseFormat, PricingInfo } from "../types";
|
|
11
11
|
import { SDKEvents } from "../types/events";
|
|
12
|
+
import type { AgentRoomManager } from "./agent-room-manager";
|
|
12
13
|
import type { SecurePrivateKey } from "../utils/secure-private-key";
|
|
13
14
|
export interface SendMessageOptions {
|
|
14
15
|
room: string;
|
|
@@ -61,6 +62,7 @@ export interface MessageRouterConfig {
|
|
|
61
62
|
paymentNetwork?: string;
|
|
62
63
|
paymentAsset?: string;
|
|
63
64
|
network?: string;
|
|
65
|
+
autoSummon?: boolean;
|
|
64
66
|
}
|
|
65
67
|
export declare class MessageRouter extends EventEmitter<SDKEvents> {
|
|
66
68
|
private readonly wsClient;
|
|
@@ -78,6 +80,8 @@ export declare class MessageRouter extends EventEmitter<SDKEvents> {
|
|
|
78
80
|
private readonly paymentNetwork;
|
|
79
81
|
private readonly paymentAsset;
|
|
80
82
|
private readonly networkName;
|
|
83
|
+
private readonly autoSummon;
|
|
84
|
+
private agentRoomManager;
|
|
81
85
|
constructor(wsClient: WebSocketClient, webhookHandler: WebhookHandler, responseFormatter: ResponseFormatter, logger: Logger, config: MessageRouterConfig);
|
|
82
86
|
/**
|
|
83
87
|
* Gets the payment network CAIP-2 identifier, resolving from network name or default.
|
|
@@ -94,6 +98,10 @@ export declare class MessageRouter extends EventEmitter<SDKEvents> {
|
|
|
94
98
|
* Must be called before using requestQuote/confirmQuote with paid tasks.
|
|
95
99
|
*/
|
|
96
100
|
setPaymentClient(secureKey: SecurePrivateKey, walletAddress: string): void;
|
|
101
|
+
/**
|
|
102
|
+
* Sets the agent room manager for auto-summon functionality (v2.4.0).
|
|
103
|
+
*/
|
|
104
|
+
setAgentRoomManager(manager: AgentRoomManager): void;
|
|
97
105
|
/**
|
|
98
106
|
* Sends a message to agents via the coordinator.
|
|
99
107
|
* The coordinator intelligently selects the most appropriate agent.
|
|
@@ -164,6 +172,33 @@ export declare class MessageRouter extends EventEmitter<SDKEvents> {
|
|
|
164
172
|
* Returns the quote data for manual confirmation.
|
|
165
173
|
*/
|
|
166
174
|
requestQuote(content: string, room: string, networkOverride?: string | number): Promise<QuoteResult>;
|
|
175
|
+
/**
|
|
176
|
+
* Internal quote request with error racing and auto-summon support.
|
|
177
|
+
* @param isRetry - true on auto-summon retry to prevent infinite loops
|
|
178
|
+
*/
|
|
179
|
+
private _requestQuoteInternal;
|
|
180
|
+
/**
|
|
181
|
+
* Pre-flight autosummon: adds agent to room before sending the command.
|
|
182
|
+
* Called when cache confirms agent is NOT in room, avoiding the reject-retry cycle.
|
|
183
|
+
*/
|
|
184
|
+
private preFlightAutoSummon;
|
|
185
|
+
/**
|
|
186
|
+
* Handles auto-summon: finds the agent, adds it to the room, and retries the quote.
|
|
187
|
+
* Fallback path triggered by coordinator reject when pre-flight was skipped (cache empty).
|
|
188
|
+
*/
|
|
189
|
+
private handleAutoSummon;
|
|
190
|
+
/**
|
|
191
|
+
* Checks if a message string indicates an agent-access error from the backend.
|
|
192
|
+
* Matches patterns like:
|
|
193
|
+
* - "agent X not found"
|
|
194
|
+
* - "agent X does not have access to room Y"
|
|
195
|
+
* - "Agent not found. Check the agent name..."
|
|
196
|
+
*/
|
|
197
|
+
private isAgentAccessErrorMessage;
|
|
198
|
+
/**
|
|
199
|
+
* Checks if an error is an "Agent not found / not in room" error from the backend.
|
|
200
|
+
*/
|
|
201
|
+
private isAgentNotFoundError;
|
|
167
202
|
/**
|
|
168
203
|
* Confirms a quote and executes the task with payment.
|
|
169
204
|
* Can optionally wait for the task response.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-router.d.ts","sourceRoot":"","sources":["../../src/managers/message-router.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACxF,OAAO,EAKL,MAAM,EACN,cAAc,EAGd,WAAW,EACZ,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"message-router.d.ts","sourceRoot":"","sources":["../../src/managers/message-router.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACxF,OAAO,EAKL,MAAM,EACN,cAAc,EAGd,WAAW,EACZ,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,SAAS,EAA6E,MAAM,iBAAiB,CAAC;AACvH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAW7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAEpE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,cAAc,GAAG,KAAK,GAAG,WAAW,CAAC;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,IAAI,CAAC;IAChB,mEAAmE;IACnE,UAAU,EAAE,cAAc,CAAC;IAC3B,uFAAuF;IACvF,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,qBAAa,aAAc,SAAQ,YAAY,CAAC,SAAS,CAAC;IACxD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAoB;IACtD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAGhD,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAuC;IACrE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAU;IAC5C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAGrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;IACrC,OAAO,CAAC,gBAAgB,CAAiC;gBAGvD,QAAQ,EAAE,eAAe,EACzB,cAAc,EAAE,cAAc,EAC9B,iBAAiB,EAAE,iBAAiB,EACpC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,mBAAmB;IA0B7B;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAiBjC;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAY/B;;;OAGG;IACI,gBAAgB,CAAC,SAAS,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI;IAOjF;;OAEG;IACI,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAI3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACU,WAAW,CACtB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IA6CpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACU,iBAAiB,CAC5B,OAAO,EAAE,YAAY,EACrB,eAAe,GAAE,OAAe,GAC/B,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAqDpC;;;OAGG;IACU,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAIjH;;;OAGG;YACW,qBAAqB;IA+GnC;;;OAGG;YACW,mBAAmB;IAyBjC;;;OAGG;YACW,gBAAgB;IAoC9B;;;;;;OAMG;IACH,OAAO,CAAC,yBAAyB;IAUjC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAK5B;;;OAGG;IACU,YAAY,CACvB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GACxD,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IA8EpC;;OAEG;IACI,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAI/D;;;OAGG;YACW,6BAA6B;IA2E3C;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAkF5B;;;;;;;;;;;;OAYG;IACI,OAAO,IAAI,IAAI;CAOvB"}
|
|
@@ -33,6 +33,9 @@ class MessageRouter extends eventemitter3_1.EventEmitter {
|
|
|
33
33
|
paymentNetwork; // CAIP-2 format if set
|
|
34
34
|
paymentAsset;
|
|
35
35
|
networkName; // Network name (peaq, base, avalanche)
|
|
36
|
+
// Auto-summon (v2.4.0)
|
|
37
|
+
autoSummon;
|
|
38
|
+
agentRoomManager = null;
|
|
36
39
|
constructor(wsClient, webhookHandler, responseFormatter, logger, config) {
|
|
37
40
|
super();
|
|
38
41
|
this.wsClient = wsClient;
|
|
@@ -51,6 +54,7 @@ class MessageRouter extends eventemitter3_1.EventEmitter {
|
|
|
51
54
|
this.paymentNetwork = config.paymentNetwork ?? "";
|
|
52
55
|
this.paymentAsset = config.paymentAsset ?? "";
|
|
53
56
|
this.networkName = config.network ?? "";
|
|
57
|
+
this.autoSummon = config.autoSummon ?? false;
|
|
54
58
|
this.setupEventForwarding();
|
|
55
59
|
}
|
|
56
60
|
/**
|
|
@@ -98,6 +102,12 @@ class MessageRouter extends eventemitter3_1.EventEmitter {
|
|
|
98
102
|
asset: this.getResolvedPaymentAsset()
|
|
99
103
|
});
|
|
100
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Sets the agent room manager for auto-summon functionality (v2.4.0).
|
|
107
|
+
*/
|
|
108
|
+
setAgentRoomManager(manager) {
|
|
109
|
+
this.agentRoomManager = manager;
|
|
110
|
+
}
|
|
101
111
|
/**
|
|
102
112
|
* Sends a message to agents via the coordinator.
|
|
103
113
|
* The coordinator intelligently selects the most appropriate agent.
|
|
@@ -251,18 +261,82 @@ class MessageRouter extends eventemitter3_1.EventEmitter {
|
|
|
251
261
|
* Returns the quote data for manual confirmation.
|
|
252
262
|
*/
|
|
253
263
|
async requestQuote(content, room, networkOverride) {
|
|
264
|
+
return this._requestQuoteInternal(content, room, networkOverride, false);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Internal quote request with error racing and auto-summon support.
|
|
268
|
+
* @param isRetry - true on auto-summon retry to prevent infinite loops
|
|
269
|
+
*/
|
|
270
|
+
async _requestQuoteInternal(content, room, networkOverride, isRetry) {
|
|
254
271
|
if (!this.wsClient.isConnected) {
|
|
255
272
|
throw new events_1.SDKError("Not connected to Teneo network", error_codes_1.ErrorCode.NOT_CONNECTED);
|
|
256
273
|
}
|
|
257
274
|
// Include payment network in request so backend returns correct contract addresses
|
|
258
275
|
const resolvedNetwork = this.getResolvedPaymentNetwork(networkOverride);
|
|
259
276
|
const message = (0, types_1.createRequestTask)(content, room, resolvedNetwork);
|
|
260
|
-
this.logger.debug("MessageRouter: Requesting quote", { content, room, network: resolvedNetwork });
|
|
277
|
+
this.logger.debug("MessageRouter: Requesting quote", { content, room, network: resolvedNetwork, isRetry });
|
|
278
|
+
// Pre-flight autosummon: check cache before sending to avoid reject-retry cycle
|
|
279
|
+
if (this.autoSummon && this.agentRoomManager && !isRetry) {
|
|
280
|
+
const match = content.match(/^@(\S+)/);
|
|
281
|
+
if (match) {
|
|
282
|
+
const agentName = match[1];
|
|
283
|
+
const inRoom = this.agentRoomManager.checkAgentInRoom(room, agentName);
|
|
284
|
+
if (inRoom === false) {
|
|
285
|
+
this.logger.info("MessageRouter: Pre-flight autosummon", { agentName, room });
|
|
286
|
+
try {
|
|
287
|
+
await this.preFlightAutoSummon(agentName, room);
|
|
288
|
+
}
|
|
289
|
+
catch (e) {
|
|
290
|
+
this.logger.debug("MessageRouter: Pre-flight failed, falling back", { error: e.message });
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
261
295
|
await this.wsClient.sendMessage(message);
|
|
262
|
-
|
|
296
|
+
// Race quote:received against error events to detect "Agent not found" quickly.
|
|
297
|
+
// The backend may signal "agent not in room" via:
|
|
298
|
+
// 1. An "error" event with "agent ... not found"
|
|
299
|
+
// 2. An "agent:response" from the coordinator with content like
|
|
300
|
+
// "agent X does not have access to room Y"
|
|
301
|
+
// We race all three to catch whichever arrives first.
|
|
302
|
+
const quotePromise = (0, event_waiter_1.waitForEvent)(this.wsClient, "quote:received", {
|
|
263
303
|
timeout: this.quoteTimeout,
|
|
264
304
|
timeoutMessage: `Quote request timed out after ${this.quoteTimeout}ms`
|
|
265
305
|
});
|
|
306
|
+
const errorPromise = (0, event_waiter_1.waitForEvent)(this.wsClient, "error", {
|
|
307
|
+
timeout: this.quoteTimeout + 1000,
|
|
308
|
+
filter: (err) => {
|
|
309
|
+
const msg = (err.message || "").toLowerCase();
|
|
310
|
+
return this.isAgentAccessErrorMessage(msg);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
// Also race against agent:response that contains an access-denied error from the coordinator
|
|
314
|
+
const agentErrorPromise = (0, event_waiter_1.waitForEvent)(this.wsClient, "agent:response", {
|
|
315
|
+
timeout: this.quoteTimeout + 1000,
|
|
316
|
+
filter: (resp) => {
|
|
317
|
+
const msg = (resp.content || "").toLowerCase();
|
|
318
|
+
return this.isAgentAccessErrorMessage(msg);
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
let quote;
|
|
322
|
+
try {
|
|
323
|
+
quote = await Promise.race([
|
|
324
|
+
quotePromise,
|
|
325
|
+
errorPromise.then((err) => { throw err; }),
|
|
326
|
+
agentErrorPromise.then((resp) => {
|
|
327
|
+
throw new events_1.SDKError(resp.content, error_codes_1.ErrorCode.AGENT_NOT_IN_ROOM);
|
|
328
|
+
})
|
|
329
|
+
]);
|
|
330
|
+
}
|
|
331
|
+
catch (error) {
|
|
332
|
+
if (this.isAgentNotFoundError(error) && !isRetry) {
|
|
333
|
+
if (this.autoSummon) {
|
|
334
|
+
return this.handleAutoSummon(content, room, networkOverride);
|
|
335
|
+
}
|
|
336
|
+
throw new events_1.SDKError("Agent not found in room. Enable autoSummon to automatically add agents.", error_codes_1.ErrorCode.AGENT_NOT_IN_ROOM);
|
|
337
|
+
}
|
|
338
|
+
throw error;
|
|
339
|
+
}
|
|
266
340
|
const result = {
|
|
267
341
|
taskId: quote.data.task_id,
|
|
268
342
|
agentId: quote.data.agent_id,
|
|
@@ -290,6 +364,73 @@ class MessageRouter extends eventemitter3_1.EventEmitter {
|
|
|
290
364
|
this.pendingQuotes.set(result.taskId, result);
|
|
291
365
|
return result;
|
|
292
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Pre-flight autosummon: adds agent to room before sending the command.
|
|
369
|
+
* Called when cache confirms agent is NOT in room, avoiding the reject-retry cycle.
|
|
370
|
+
*/
|
|
371
|
+
async preFlightAutoSummon(agentName, room) {
|
|
372
|
+
if (!this.agentRoomManager) {
|
|
373
|
+
throw new events_1.SDKError("Auto-summon requires AgentRoomManager", error_codes_1.ErrorCode.AUTOSUMMON_FAILED);
|
|
374
|
+
}
|
|
375
|
+
this.wsClient.emit("autosummon:start", agentName, room);
|
|
376
|
+
const available = await this.agentRoomManager.listAvailableAgents(room, false);
|
|
377
|
+
const agent = available.find((a) => a.agent_id === agentName || a.agent_name === agentName);
|
|
378
|
+
if (!agent) {
|
|
379
|
+
this.wsClient.emit("autosummon:failed", agentName, room, "Agent not found or offline");
|
|
380
|
+
throw new events_1.SDKError(`Agent '${agentName}' does not exist or is offline`, error_codes_1.ErrorCode.AGENT_NOT_FOUND);
|
|
381
|
+
}
|
|
382
|
+
await this.agentRoomManager.addAgentToRoom(room, agent.agent_id);
|
|
383
|
+
this.wsClient.emit("autosummon:success", agentName, agent.agent_id, room);
|
|
384
|
+
this.logger.info("MessageRouter: Pre-flight autosummon succeeded", { agentId: agent.agent_id });
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Handles auto-summon: finds the agent, adds it to the room, and retries the quote.
|
|
388
|
+
* Fallback path triggered by coordinator reject when pre-flight was skipped (cache empty).
|
|
389
|
+
*/
|
|
390
|
+
async handleAutoSummon(content, room, networkOverride) {
|
|
391
|
+
if (!this.agentRoomManager) {
|
|
392
|
+
throw new events_1.SDKError("Auto-summon requires AgentRoomManager", error_codes_1.ErrorCode.AUTOSUMMON_FAILED);
|
|
393
|
+
}
|
|
394
|
+
const match = content.match(/^@(\S+)/);
|
|
395
|
+
if (!match) {
|
|
396
|
+
throw new events_1.SDKError("Cannot extract agent name for auto-summon", error_codes_1.ErrorCode.AUTOSUMMON_FAILED);
|
|
397
|
+
}
|
|
398
|
+
const agentName = match[1];
|
|
399
|
+
this.logger.info("MessageRouter: Auto-summoning agent", { agentName, room });
|
|
400
|
+
this.wsClient.emit("autosummon:start", agentName, room);
|
|
401
|
+
const available = await this.agentRoomManager.listAvailableAgents(room, false);
|
|
402
|
+
const agent = available.find((a) => a.agent_id === agentName || a.agent_name === agentName);
|
|
403
|
+
if (!agent) {
|
|
404
|
+
this.wsClient.emit("autosummon:failed", agentName, room, "Agent not found or offline");
|
|
405
|
+
throw new events_1.SDKError(`Agent '${agentName}' does not exist or is offline`, error_codes_1.ErrorCode.AGENT_NOT_FOUND);
|
|
406
|
+
}
|
|
407
|
+
await this.agentRoomManager.addAgentToRoom(room, agent.agent_id);
|
|
408
|
+
this.wsClient.emit("autosummon:success", agentName, agent.agent_id, room);
|
|
409
|
+
this.logger.info("MessageRouter: Agent auto-summoned, retrying", { agentId: agent.agent_id });
|
|
410
|
+
return this._requestQuoteInternal(content, room, networkOverride, true);
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Checks if a message string indicates an agent-access error from the backend.
|
|
414
|
+
* Matches patterns like:
|
|
415
|
+
* - "agent X not found"
|
|
416
|
+
* - "agent X does not have access to room Y"
|
|
417
|
+
* - "Agent not found. Check the agent name..."
|
|
418
|
+
*/
|
|
419
|
+
isAgentAccessErrorMessage(msg) {
|
|
420
|
+
if (!msg.includes("agent"))
|
|
421
|
+
return false;
|
|
422
|
+
return (msg.includes("not found") ||
|
|
423
|
+
msg.includes("does not have access") ||
|
|
424
|
+
msg.includes("not in room") ||
|
|
425
|
+
msg.includes("no access"));
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Checks if an error is an "Agent not found / not in room" error from the backend.
|
|
429
|
+
*/
|
|
430
|
+
isAgentNotFoundError(error) {
|
|
431
|
+
const msg = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
|
432
|
+
return this.isAgentAccessErrorMessage(msg);
|
|
433
|
+
}
|
|
293
434
|
/**
|
|
294
435
|
* Confirms a quote and executes the task with payment.
|
|
295
436
|
* Can optionally wait for the task response.
|