@circuitorg/agent-sdk 1.2.1 → 1.2.3

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 (4) hide show
  1. package/README.md +83 -0
  2. package/index.d.ts +299 -10
  3. package/index.js +1 -1
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -13,6 +13,7 @@ A simplified TypeScript SDK for building automated agents to deploy on Circuit.
13
13
  - [🚀 Quick Start](#-quick-start)
14
14
  - [Install the SDK](#install-the-sdk)
15
15
  - [Create Your First Agent](#create-your-first-agent)
16
+ - [Required Asset setup](#required-asset-setup)
16
17
  - [🎯 Core Concepts](#-core-concepts)
17
18
  - [The AgentContext Object](#the-agentcontext-object)
18
19
  - [Run/Stop Function Requirements](#runstop-function-requirements)
@@ -23,6 +24,7 @@ A simplified TypeScript SDK for building automated agents to deploy on Circuit.
23
24
  - [📈 Polymarket Prediction Markets](#-polymarket-prediction-markets)
24
25
  - [Place Market Orders](#place-market-orders)
25
26
  - [Redeem Positions](#redeem-positions)
27
+ - [📊 Transaction History](#-transaction-history)
26
28
  - [🚀 Sign \& Send Transactions](#-sign--send-transactions)
27
29
  - [Ethereum (any EVM chain)](#ethereum-any-evm-chain)
28
30
  - [Solana](#solana)
@@ -65,9 +67,27 @@ Every agent receives a single `AgentContext` object that provides:
65
67
  - `agent.swidge` - Cross-chain swaps and bridges (`.quote()`, `.execute()`)
66
68
  - `agent.signAndSend()` - Execute custom built transactions on any supported chain
67
69
  - `agent.signMessage()` - Sign messages (EVM only)
70
+ - `agent.transactions()` - Get transaction history with asset changes
68
71
 
69
72
  **Important:** `currentPositions` reflects your allocated assets at the **start** of each execution. To avoid failed transactions, you should be tracking intra-run position changes based on transactions the agent makes during the execution. Circuit will provide updated positions on the next execution request.
70
73
 
74
+
75
+ #### Required Asset setup
76
+ > Note: For native tokens, use the following null addresses for solana/ethereum
77
+ ```toml
78
+ // Requiring 1 SOL
79
+ [[requiredAssets]]
80
+ network = "solana"
81
+ address = "11111111111111111111111111111111"
82
+ minimumAmount = "1000000000"
83
+
84
+ // Requiring 1 ETH
85
+ [[requiredAssets]]
86
+ network = "ethereum:<chainId>"
87
+ address = "0x0000000000000000000000000000000000000000"
88
+ minimumAmount = "1000000000000000000"
89
+ ```
90
+
71
91
  ```typescript
72
92
  import { Agent, AgentContext } from "@circuitorg/agent-sdk";
73
93
 
@@ -121,6 +141,7 @@ Every agent function receives a single `AgentContext` object that contains:
121
141
  - `agent.swidge` - Cross-chain swap operations
122
142
  - `agent.signAndSend()` - Sign and broadcast transactions
123
143
  - `agent.signMessage()` - Sign messages on EVM
144
+ - `agent.transactions()` - Get transaction history with asset changes
124
145
 
125
146
  ### Run/Stop Function Requirements
126
147
 
@@ -215,6 +236,8 @@ Built-in Swidge integration for seamless cross-chain token swaps and bridges.
215
236
  ### Get and execute a quote
216
237
  > Note: It is important to always validate quotes before executing. Circuit will always do its best to return a quote, and as of now, will only filter out quotes with price impacts exceeding 100% to ensure maximum flexibility. It is on the agent to makes sure a quote is valid, given its own parameters
217
238
 
239
+ > **Bulk Execution:** `execute()` accepts both single quotes and arrays. Pass an array to execute multiple swaps in parallel: `await agent.swidge.execute([quote1.data, quote2.data])` returns an array of results.
240
+
218
241
  ```typescript
219
242
  async function run(agent: AgentContext): Promise<void> {
220
243
  // 1. Get quote
@@ -305,6 +328,66 @@ async function stop(agent: AgentContext): Promise<void> {
305
328
  }
306
329
  ```
307
330
 
331
+ ## 📊 Transaction History
332
+
333
+ Get a list of asset changes for all confirmed transactions during your session. This is useful for tracking what assets have moved in and out of the agent's wallet.
334
+
335
+ > **Note:** The system needs to index new transactions, so there may be a slight delay between when you execute a transaction and when the resulting asset changes are returned in this method. Make sure you are taking that into consideration if dealing with assets the agent just transacted with.
336
+
337
+ ```typescript
338
+ async function run(agent: AgentContext): Promise<void> {
339
+ // Get all transaction history for this session
340
+ const result = await agent.transactions();
341
+
342
+ if (result.success && result.data) {
343
+ await agent.log(`Found ${result.data.length} asset changes`);
344
+
345
+ // Filter for outgoing transfers
346
+ const outgoing = result.data.filter(
347
+ (change) => change.from === agent.sessionWalletAddress
348
+ );
349
+ await agent.log(`Outgoing transfers: ${outgoing.length}`);
350
+
351
+ // Calculate total USD value (where price data is available)
352
+ const totalUsd = result.data
353
+ .filter((c) => c.tokenUsdPrice)
354
+ .reduce((sum, c) => {
355
+ const amount = parseFloat(c.amount);
356
+ const price = parseFloat(c.tokenUsdPrice!);
357
+ return sum + amount * price;
358
+ }, 0);
359
+ await agent.log(`Total USD value: $${totalUsd.toFixed(2)}`);
360
+
361
+ // View specific transaction details
362
+ for (const change of result.data) {
363
+ await agent.log(`${change.network}: ${change.from} → ${change.to}`);
364
+ await agent.log(` Amount: ${change.amount} ${change.tokenType}`);
365
+ if (change.token) {
366
+ await agent.log(` Token: ${change.token}`);
367
+ }
368
+ await agent.log(` Tx: ${change.transactionHash}`);
369
+ await agent.log(` Time: ${change.timestamp}`);
370
+ }
371
+ } else {
372
+ await agent.log(result.error || "Failed to fetch transactions", { error: true });
373
+ }
374
+ }
375
+ ```
376
+
377
+ **AssetChange Structure:**
378
+
379
+ Each asset change in the response contains:
380
+ - `network` - Network identifier (e.g., `"ethereum:1"`, `"solana"`)
381
+ - `transactionHash` - Transaction hash
382
+ - `from` - Sender address
383
+ - `to` - Recipient address
384
+ - `amount` - Amount transferred (as string to preserve precision)
385
+ - `token` - Token contract address (`null` for native tokens)
386
+ - `tokenId` - Token ID for NFTs (`null` for fungible tokens)
387
+ - `tokenType` - Token type (e.g., `"native"`, `"ERC20"`, `"ERC721"`)
388
+ - `tokenUsdPrice` - Token price in USD at time of transaction (`null` if unavailable)
389
+ - `timestamp` - Transaction timestamp
390
+
308
391
  ## 🚀 Sign & Send Transactions
309
392
 
310
393
  ### Ethereum (any EVM chain)
package/index.d.ts CHANGED
@@ -182,6 +182,94 @@ type TransactionsResponse = {
182
182
  status?: number;
183
183
  };
184
184
  };
185
+ /**
186
+ * Current position with optional Polymarket metadata enrichment
187
+ */
188
+ type EnrichedPosition = {
189
+ /** Network identifier (e.g., "ethereum:1", "solana") */
190
+ network: string;
191
+ /** Asset contract address */
192
+ assetAddress: string;
193
+ /** Token ID for NFTs/ERC1155 (null for fungible tokens) */
194
+ tokenId: string | null;
195
+ /** Average unit cost in USD */
196
+ avgUnitCost: string;
197
+ /** Current quantity held (raw amount) */
198
+ currentQty: string;
199
+ /** Optional Polymarket metadata (only present for ERC1155 positions) */
200
+ polymarketMetadata?: {
201
+ /** ERC1155 contract address for the market */
202
+ contractAddress: string;
203
+ /** Token ID for the specific outcome */
204
+ tokenId: string | null;
205
+ /** Token decimals (typically 6) */
206
+ decimals: number;
207
+ /** Unique condition identifier */
208
+ conditionId: string;
209
+ /** Human-readable share count */
210
+ formattedShares: string;
211
+ /** Raw share count in smallest unit */
212
+ shares: string;
213
+ /** Current position value in USD */
214
+ valueUsd: string;
215
+ /** Market question text */
216
+ question: string;
217
+ /** Outcome name (e.g., "Yes", "No") */
218
+ outcome: string;
219
+ /** Current price per share in USD */
220
+ priceUsd: string;
221
+ /** Average purchase price per share in USD */
222
+ averagePriceUsd: string;
223
+ /** Whether position can be redeemed */
224
+ isRedeemable: boolean;
225
+ /** Whether position uses negative risk collateral */
226
+ isNegativeRisk: boolean;
227
+ /** Market image URL */
228
+ imageUrl: string;
229
+ /** Initial position value in USD */
230
+ initialValue: string;
231
+ /** Unrealized profit/loss in USD */
232
+ pnlUsd: string;
233
+ /** Unrealized profit/loss percentage */
234
+ pnlPercent: string;
235
+ /** Realized profit/loss in USD */
236
+ pnlRealizedUsd: string;
237
+ /** Realized profit/loss percentage */
238
+ pnlRealizedPercent: string;
239
+ /** Market end date (ISO 8601 string) */
240
+ endDate: string;
241
+ };
242
+ };
243
+ /**
244
+ * Data returned from getCurrentPositions() method
245
+ */
246
+ type CurrentPositionsData = {
247
+ /** Array of current positions with optional Polymarket metadata */
248
+ positions: EnrichedPosition[];
249
+ /** Whether there are pending transactions that may affect position balances */
250
+ hasPendingTxs: boolean;
251
+ };
252
+ /**
253
+ * Response from getCurrentPositions() method
254
+ *
255
+ * Returns the current live positions for the session with optional Polymarket
256
+ * metadata enrichment for ERC1155 positions.
257
+ */
258
+ type CurrentPositionsResponse = {
259
+ /** Whether the operation was successful */
260
+ success: boolean;
261
+ /** Current positions data (only present on success) */
262
+ data?: CurrentPositionsData;
263
+ /** Error message (only present on failure) */
264
+ error?: string;
265
+ /** Detailed error message (only present on failure) */
266
+ errorMessage?: string;
267
+ /** Detailed error information (only present on failure) */
268
+ errorDetails?: {
269
+ message?: string;
270
+ status?: number;
271
+ };
272
+ };
185
273
 
186
274
  /**
187
275
  * Agent log type definitions for agent communication
@@ -522,6 +610,7 @@ declare const SwidgeExecuteResponseWrapperSchema: z.ZodObject<{
522
610
  txs: z.ZodArray<z.ZodString>;
523
611
  }, z.core.$strip>;
524
612
  lastUpdated: z.ZodNumber;
613
+ error: z.ZodOptional<z.ZodString>;
525
614
  }, z.core.$strip>>;
526
615
  error: z.ZodOptional<z.ZodString>;
527
616
  errorMessage: z.ZodOptional<z.ZodString>;
@@ -939,6 +1028,10 @@ declare class AgentSdk {
939
1028
  * `sdk.swidge.quote()` and let the magic happen. Signs transactions, broadcasts them,
940
1029
  * and waits for completion.
941
1030
  *
1031
+ * Supports both single and bulk execution:
1032
+ * - Pass a single quote → get a single response
1033
+ * - Pass an array of quotes → get an array of responses
1034
+ *
942
1035
  * ⚠️ **What happens:**
943
1036
  * - Signs transactions using your wallet's policy engine
944
1037
  * - Broadcasts to the blockchain(s)
@@ -947,8 +1040,8 @@ declare class AgentSdk {
947
1040
  *
948
1041
  * 💡 **Pro tip**: The backend handles all the complexity - you just pass the quote!
949
1042
  *
950
- * @param quote Complete quote object from `sdk.swidge.quote()` (the entire `data` field from the quote response)
951
- * @returns SwidgeExecuteResponse with transaction status and details
1043
+ * @param quote Complete quote object(s) from `sdk.swidge.quote()` (the entire `data` field from the quote response)
1044
+ * @returns SwidgeExecuteResponse or array of responses (matching input type) with transaction status and details
952
1045
  * - `success` (boolean): Whether the execution was successful
953
1046
  * - `data` (SwidgeExecuteData | undefined): Execution result containing:
954
1047
  * - `status`: "success" | "failure" | "refund" | "delayed" - Final transaction status
@@ -969,7 +1062,7 @@ declare class AgentSdk {
969
1062
  * toToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
970
1063
  * });
971
1064
  *
972
- * // 2️⃣ Execute the swap
1065
+ * // 2️⃣ Execute the swap (single)
973
1066
  * if (quote.success && quote.data) {
974
1067
  * const result = await sdk.swidge.execute(quote.data);
975
1068
  *
@@ -991,9 +1084,18 @@ declare class AgentSdk {
991
1084
  * console.log(`❌ Execute failed: ${result.error}`);
992
1085
  * }
993
1086
  * }
1087
+ *
1088
+ * // 3️⃣ Bulk execution (multiple quotes)
1089
+ * const results = await sdk.swidge.execute([quote.data, quote.data]);
1090
+ * results.forEach((result, index) => {
1091
+ * console.log(`Swap ${index + 1}: ${result.success ? result.data?.status : result.error}`);
1092
+ * });
994
1093
  * ```
995
1094
  */
996
- execute: (executeQuote: SwidgeExecuteRequest) => Promise<SwidgeExecuteResponse>;
1095
+ execute: {
1096
+ (executeQuote: SwidgeExecuteRequest): Promise<SwidgeExecuteResponse>;
1097
+ (executeQuotes: SwidgeExecuteRequest[]): Promise<SwidgeExecuteResponse[]>;
1098
+ };
997
1099
  };
998
1100
  /**
999
1101
  * 💾 Memory: Session-scoped key-value storage
@@ -1356,7 +1458,7 @@ declare class AgentSdk {
1356
1458
  */
1357
1459
  private handleSwidgeQuote;
1358
1460
  /**
1359
- * Handle swidge execute requests
1461
+ * Handle swidge execute requests (single or bulk)
1360
1462
  */
1361
1463
  private handleSwidgeExecute;
1362
1464
  private handlePolymarketMarketOrder;
@@ -1416,6 +1518,107 @@ declare class AgentSdk {
1416
1518
  * ```
1417
1519
  */
1418
1520
  transactions(): Promise<TransactionsResponse>;
1521
+ /**
1522
+ * 💼 Get current live positions for the session
1523
+ *
1524
+ * Retrieves all current positions held by the session wallet with live balance data.
1525
+ * For ERC1155 positions (e.g., Polymarket), optionally enriches the response with
1526
+ * detailed market metadata including PNL, current prices, and redeemability status.
1527
+ *
1528
+ * **Key Features**:
1529
+ * - Live balance data from the analytics service
1530
+ * - Automatic Polymarket metadata enrichment for ERC1155 positions
1531
+ * - Pending transaction detection to warn of potentially stale balances
1532
+ *
1533
+ * **Returns**: `CurrentPositionsResponse`
1534
+ * - `success` (boolean): Whether the operation succeeded
1535
+ * - `data` (CurrentPositionsData | undefined): Current positions data on success
1536
+ * - `positions` (EnrichedPosition[]): Array of current positions
1537
+ * - `network` (string): Network identifier (e.g., "ethereum:137")
1538
+ * - `assetAddress` (string): Token/asset contract address
1539
+ * - `tokenId` (string | null): Token ID for NFTs/ERC1155 (null for fungible)
1540
+ * - `avgUnitCost` (string): Average unit cost in USD
1541
+ * - `currentQty` (string): Current quantity held (raw amount)
1542
+ * - `polymarketMetadata` (optional): Detailed Polymarket position data
1543
+ * - `question` (string): Market question text
1544
+ * - `outcome` (string): Outcome name (e.g., "Yes", "No")
1545
+ * - `formattedShares` (string): Human-readable share count
1546
+ * - `valueUsd` (string): Current position value in USD
1547
+ * - `priceUsd` (string): Current price per share
1548
+ * - `averagePriceUsd` (string): Average purchase price
1549
+ * - `pnlUsd` (string): Unrealized profit/loss in USD
1550
+ * - `pnlPercent` (string): Unrealized profit/loss percentage
1551
+ * - `isRedeemable` (boolean): Whether position can be redeemed
1552
+ * - `endDate` (string): Market end date
1553
+ * - Plus additional market details
1554
+ * - `hasPendingTxs` (boolean): Whether there are pending transactions
1555
+ * - `error` (string | undefined): Error message on failure
1556
+ * - `errorMessage` (string | undefined): Detailed error message on failure
1557
+ *
1558
+ * @returns Promise resolving to CurrentPositionsResponse with enriched positions
1559
+ *
1560
+ * @example
1561
+ * ```ts
1562
+ * const result = await sdk.getCurrentPositions();
1563
+ *
1564
+ * if (result.success && result.data) {
1565
+ * console.log(`Found ${result.data.positions.length} positions`);
1566
+ *
1567
+ * if (result.data.hasPendingTxs) {
1568
+ * console.log('⚠️ Warning: Pending transactions may affect balances');
1569
+ * }
1570
+ *
1571
+ * result.data.positions.forEach(position => {
1572
+ * console.log(`${position.assetAddress}: ${position.currentQty} units`);
1573
+ * console.log(` Average cost: $${position.avgUnitCost}`);
1574
+ *
1575
+ * // Check for Polymarket enrichment
1576
+ * if (position.polymarketMetadata) {
1577
+ * const pm = position.polymarketMetadata;
1578
+ * console.log(` 📈 ${pm.question}`);
1579
+ * console.log(` Outcome: ${pm.outcome}`);
1580
+ * console.log(` Shares: ${pm.formattedShares}`);
1581
+ * console.log(` Value: $${pm.valueUsd}`);
1582
+ * console.log(` PNL: $${pm.pnlUsd} (${pm.pnlPercent}%)`);
1583
+ * console.log(` Redeemable: ${pm.isRedeemable ? 'Yes' : 'No'}`);
1584
+ * }
1585
+ * });
1586
+ * } else {
1587
+ * console.error(`Failed to get positions: ${result.error}`);
1588
+ * }
1589
+ * ```
1590
+ *
1591
+ * **Success Response Example**:
1592
+ * ```json
1593
+ * {
1594
+ * "success": true,
1595
+ * "data": {
1596
+ * "positions": [
1597
+ * {
1598
+ * "network": "ethereum:137",
1599
+ * "assetAddress": "0x4d97dcd97eC945f40cF65F87097ACe5EA0476045",
1600
+ * "tokenId": "123456",
1601
+ * "avgUnitCost": "0.52",
1602
+ * "currentQty": "10.5",
1603
+ * "polymarketMetadata": {
1604
+ * "question": "Will event X happen?",
1605
+ * "outcome": "Yes",
1606
+ * "formattedShares": "10.5",
1607
+ * "valueUsd": "5.46",
1608
+ * "priceUsd": "0.52",
1609
+ * "averagePriceUsd": "0.50",
1610
+ * "pnlUsd": "0.21",
1611
+ * "pnlPercent": "4.00",
1612
+ * "isRedeemable": false
1613
+ * }
1614
+ * }
1615
+ * ],
1616
+ * "hasPendingTxs": false
1617
+ * }
1618
+ * }
1619
+ * ```
1620
+ */
1621
+ getCurrentPositions(): Promise<CurrentPositionsResponse>;
1419
1622
  }
1420
1623
 
1421
1624
  /**
@@ -2050,18 +2253,23 @@ declare class AgentContext {
2050
2253
  *
2051
2254
  * Takes your quote and signs/broadcasts transactions automatically.
2052
2255
  *
2053
- * **Input**: `SwidgeQuoteData` - Complete quote object from agent.swidge.quote()
2256
+ * Supports both single and bulk execution:
2257
+ * - Pass a single quote → get a single response
2258
+ * - Pass an array of quotes → get an array of responses
2054
2259
  *
2055
- * **Output**: `SwidgeExecuteResponse`
2260
+ * **Input**: `SwidgeQuoteData | SwidgeQuoteData[]` - Complete quote object(s) from agent.swidge.quote()
2261
+ *
2262
+ * **Output**: `SwidgeExecuteResponse | SwidgeExecuteResponse[]` (matching input type)
2056
2263
  * - `success`: Whether the execution was successful
2057
2264
  * - `data`: Execution result with status ("success", "failure", "refund", "delayed") and transaction hashes
2058
2265
  * - `error`: Error message if execution failed
2059
2266
  *
2060
- * @param quoteData - Complete quote object from agent.swidge.quote()
2267
+ * @param quoteData - Complete quote object(s) from agent.swidge.quote()
2061
2268
  * @returns Promise resolving to SwidgeExecuteResponse with transaction status
2062
2269
  *
2063
2270
  * @example
2064
2271
  * ```typescript
2272
+ * // Single execution
2065
2273
  * const quote = await agent.swidge.quote({...});
2066
2274
  * if (quote.success && quote.data) {
2067
2275
  * const result = await agent.swidge.execute(quote.data);
@@ -2072,9 +2280,20 @@ declare class AgentContext {
2072
2280
  * }
2073
2281
  * }
2074
2282
  * }
2283
+ *
2284
+ * // Bulk execution
2285
+ * const results = await agent.swidge.execute([quote.data, quote2.data]);
2286
+ * results.forEach((result, index) => {
2287
+ * if (result.success && result.data) {
2288
+ * console.log(`Swap ${index + 1} status: ${result.data.status}`);
2289
+ * }
2290
+ * });
2075
2291
  * ```
2076
2292
  */
2077
- execute: (quoteData: SwidgeQuoteData) => Promise<SwidgeExecuteResponse>;
2293
+ execute: {
2294
+ (quoteData: SwidgeQuoteData): Promise<SwidgeExecuteResponse>;
2295
+ (quoteData: SwidgeQuoteData[]): Promise<SwidgeExecuteResponse[]>;
2296
+ };
2078
2297
  };
2079
2298
  /**
2080
2299
  * Get transaction ledger with asset changes.
@@ -2125,6 +2344,76 @@ declare class AgentContext {
2125
2344
  * ```
2126
2345
  */
2127
2346
  transactions(): Promise<TransactionsResponse>;
2347
+ /**
2348
+ * Get current live positions for the session.
2349
+ *
2350
+ * Retrieves all current positions held by the session wallet with live balance data.
2351
+ * For ERC1155 positions (e.g., Polymarket), automatically enriches the response with
2352
+ * detailed market metadata including PNL, current prices, and redeemability status.
2353
+ *
2354
+ * **Output**: `CurrentPositionsResponse`
2355
+ * - `success` (boolean): Whether the operation succeeded
2356
+ * - `data` (CurrentPositionsData | undefined): Current positions data on success
2357
+ * - `hasPendingTxs` (boolean): Whether there are pending transactions
2358
+ * - `positions` (EnrichedPosition[]): Array of current positions
2359
+ * - `network` (string): Network identifier (e.g., "ethereum:137")
2360
+ * - `assetAddress` (string): Token/asset contract address
2361
+ * - `tokenId` (string | null): Token ID for NFTs/ERC1155 (null for fungible)
2362
+ * - `avgUnitCost` (string): Average unit cost in USD
2363
+ * - `currentQty` (string): Current quantity held (raw amount)
2364
+ * - `polymarketMetadata` (optional): Detailed Polymarket position data
2365
+ * - `question` (string): Market question text
2366
+ * - `outcome` (string): Outcome name (e.g., "Yes", "No")
2367
+ * - `formattedShares` (string): Human-readable share count
2368
+ * - `valueUsd` (string): Current position value in USD
2369
+ * - `priceUsd` (string): Current price per share
2370
+ * - `averagePriceUsd` (string): Average purchase price
2371
+ * - `pnlUsd` (string): Unrealized profit/loss in USD
2372
+ * - `pnlPercent` (string): Unrealized profit/loss percentage
2373
+ * - `isRedeemable` (boolean): Whether position can be redeemed
2374
+ * - `endDate` (string): Market end date
2375
+ * - Plus additional market details
2376
+ * - `error` (string | undefined): Error message on failure
2377
+ * - `errorMessage` (string | undefined): Detailed error message on failure
2378
+ *
2379
+ * @returns Promise resolving to CurrentPositionsResponse with enriched positions
2380
+ *
2381
+ * @example
2382
+ * ```typescript
2383
+ * const result = await agent.getCurrentPositions();
2384
+ *
2385
+ * if (result.success && result.data) {
2386
+ * await agent.log(`Managing ${result.data.positions.length} positions`);
2387
+ *
2388
+ * if (result.data.hasPendingTxs) {
2389
+ * await agent.log('⚠️ Warning: Pending transactions may affect balances');
2390
+ * }
2391
+ *
2392
+ * // Iterate through positions
2393
+ * for (const position of result.data.positions) {
2394
+ * await agent.log(`Position: ${position.assetAddress}`);
2395
+ * await agent.log(` Quantity: ${position.currentQty}`);
2396
+ * await agent.log(` Avg Cost: $${position.avgUnitCost}`);
2397
+ *
2398
+ * // Check for Polymarket enrichment
2399
+ * if (position.polymarketMetadata) {
2400
+ * const pm = position.polymarketMetadata;
2401
+ * await agent.log(` Market: ${pm.question}`);
2402
+ * await agent.log(` Outcome: ${pm.outcome}`);
2403
+ * await agent.log(` Value: $${pm.valueUsd}`);
2404
+ * await agent.log(` PNL: $${pm.pnlUsd} (${pm.pnlPercent}%)`);
2405
+ *
2406
+ * if (pm.isRedeemable) {
2407
+ * await agent.log(' ✅ This position is redeemable!');
2408
+ * }
2409
+ * }
2410
+ * }
2411
+ * } else {
2412
+ * await agent.log(result.error || 'Failed to fetch positions', { error: true });
2413
+ * }
2414
+ * ```
2415
+ */
2416
+ getCurrentPositions(): Promise<CurrentPositionsResponse>;
2128
2417
  }
2129
2418
 
2130
2419
  /**
@@ -2271,4 +2560,4 @@ declare function isSolanaNetwork(network: Network): network is "solana";
2271
2560
  */
2272
2561
  declare function getChainIdFromNetwork(network: `ethereum:${number}`): number;
2273
2562
 
2274
- export { APIClient, Agent, AgentContext, AgentSdk, type AssetChange, type CurrentPosition, type LogResponse, type MemoryDeleteResponse, type MemoryGetResponse, type MemoryListResponse, type MemorySetResponse, type Network, type PolymarketMarketOrderRequest, type PolymarketMarketOrderResponse, type PolymarketRedeemPositionsRequest, type PolymarketRedeemPositionsResponse, QUOTE_RESULT, type SDKConfig, type SignAndSendRequest, type SignAndSendResponse, type SignMessageRequest, type SignMessageResponse, type StopFunctionContract, type SwidgeExecuteResponse, type SwidgeQuoteRequest, type SwidgeQuoteResponse, type SwidgeQuoteResult, type TransactionsResponse, getChainIdFromNetwork, isEthereumNetwork, isSolanaNetwork, type runFunctionContract };
2563
+ export { APIClient, Agent, AgentContext, AgentSdk, type AssetChange, type CurrentPosition, type CurrentPositionsData, type CurrentPositionsResponse, type EnrichedPosition, type LogResponse, type MemoryDeleteResponse, type MemoryGetResponse, type MemoryListResponse, type MemorySetResponse, type Network, type PolymarketMarketOrderRequest, type PolymarketMarketOrderResponse, type PolymarketRedeemPositionsRequest, type PolymarketRedeemPositionsResponse, QUOTE_RESULT, type SDKConfig, type SignAndSendRequest, type SignAndSendResponse, type SignMessageRequest, type SignMessageResponse, type StopFunctionContract, type SwidgeExecuteResponse, type SwidgeQuoteRequest, type SwidgeQuoteResponse, type SwidgeQuoteResult, type TransactionsResponse, getChainIdFromNetwork, isEthereumNetwork, isSolanaNetwork, type runFunctionContract };
package/index.js CHANGED
@@ -1 +1 @@
1
- import{loadAuthFromFileSystem as e}from"./chunk-4I3A6QAK.js";var r=class{config;baseUrl;authorizationHeader;isCloudflareWorker(){return"undefined"!=typeof globalThis&&void 0!==globalThis.Cloudflare}hasServiceBinding(){if(this.isCloudflareWorker()){if(void 0!==globalThis.AGENTS_TO_API_PROXY)return!0;if(void 0!==globalThis.__AGENT_ENV__&&globalThis.__AGENT_ENV__?.AGENTS_TO_API_PROXY)return!0}return!1}constructor(e){this.config=e,this.baseUrl=e.baseUrl||"https://agents.circuit.org",this.authorizationHeader=e.authorizationHeader}getAgentSlug(){return"undefined"!=typeof process&&process.env?.CIRCUIT_AGENT_SLUG?process.env.CIRCUIT_AGENT_SLUG:void 0!==globalThis.CIRCUIT_AGENT_SLUG?globalThis.CIRCUIT_AGENT_SLUG:void 0}getAuthHeaders(){const e={};e["X-Session-Id"]=this.config.sessionId.toString();const r=this.getAgentSlug();if(r&&(e["X-Agent-Slug"]=r),this.isCloudflareWorker())return e;if(this.authorizationHeader)return e.Authorization=this.authorizationHeader,e;try{const r=this.loadAuthConfig();r?.sessionToken&&(e.Authorization=`Bearer ${r.sessionToken}`)}catch{}return e}loadAuthConfig(){try{return e()}catch{}}async makeRequest(e,r={}){const t={...{"Content-Type":"application/json",...this.getAuthHeaders()},...r.headers};let s;if(this.hasServiceBinding()){let o;if(void 0!==globalThis.AGENTS_TO_API_PROXY)o=globalThis.AGENTS_TO_API_PROXY;else{if(void 0===globalThis.__AGENT_ENV__||!globalThis.__AGENT_ENV__?.AGENTS_TO_API_PROXY)throw new Error("Service binding detected but not accessible");o=globalThis.__AGENT_ENV__.AGENTS_TO_API_PROXY}const a={...r,headers:t},n=`https://agents-to-api-proxy.circuit-0bc.workers.dev${e}`;s=await o.fetch(n,a)}else{const o=`${this.baseUrl}${e}`,a={...r,headers:t};s=await fetch(o,a)}if(!s.ok){const e=await s.json().catch(()=>({})),r=e.message||e.error||`HTTP ${s.status}: ${s.statusText}`,t=new Error(r);throw t.error=e.error,t.errorMessage=e.message,t.errorDetails=e,t.statusCode=s.status,t}return await s.json()}async get(e){return this.makeRequest(e,{method:"GET"})}async post(e,r){return this.makeRequest(e,{method:"POST",body:r?JSON.stringify(r):void 0})}async delete(e){return this.makeRequest(e,{method:"DELETE"})}};function t(e){return e.startsWith("ethereum:")}function s(e){return"solana"===e}function o(e){return Number(e.split(":")[1])}var a=class{client;config;constructor(e){this.config=e,this.client=new r(e)}async _sendLog(e){await this.client.post("/v1/logs",e)}async signAndSend(e){try{if(t(e.network)){const r=o(e.network);if("toAddress"in e.request)return await this.handleEvmTransaction({chainId:r,toAddress:e.request.toAddress,data:e.request.data,valueWei:e.request.value,message:e.message})}if(s(e.network)&&"hexTransaction"in e.request)return await this.handleSolanaTransaction({hexTransaction:e.request.hexTransaction,message:e.message});const r=`Unsupported network: ${e.network}`;return{success:!1,error:"Unsupported Network",errorMessage:r,errorDetails:{message:r}}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{};return r||t?{success:!1,error:r,errorMessage:t,errorDetails:s}:{success:!1,error:"SDK Error",errorMessage:e instanceof Error?e.message:"Unknown error",errorDetails:{}}}}async signMessage(e){try{if(t(e.network))return await this.handleEvmSignMessage(e);const r=`Unsupported network: ${e.network}`;return{success:!1,error:"Unsupported Network",errorMessage:r,errorDetails:{message:r}}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{};return r||t?{success:!1,error:r,errorMessage:t,errorDetails:s}:{success:!1,error:"SDK Error",errorMessage:e instanceof Error?e.message:"Unknown error",errorDetails:{}}}}swidge={quote:async e=>this.handleSwidgeQuote(e),execute:async e=>this.handleSwidgeExecute(e)};memory={set:async(e,r)=>this.handleMemorySet(e,r),get:async e=>this.handleMemoryGet(e),delete:async e=>this.handleMemoryDelete(e),list:async()=>this.handleMemoryList()};platforms={polymarket:{marketOrder:async e=>this.handlePolymarketMarketOrder(e),redeemPositions:async e=>this.handlePolymarketRedeemPositions(e||{tokenIds:[]})}};async handleEvmTransaction(e){try{const r=await this.client.post("/v1/transactions/evm",e),t=await this.client.post(`/v1/transactions/evm/${r.internalTransactionId}/broadcast`);return{success:!0,data:{internalTransactionId:r.internalTransactionId,txHash:t.txHash,transactionUrl:t.transactionUrl}}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{};return r||t?{success:!1,error:r,errorMessage:t,errorDetails:s}:{success:!1,error:"SDK Error",errorMessage:e instanceof Error?e.message:"Unknown error",errorDetails:{}}}}async handleSolanaTransaction(e){try{const r=await this.client.post("/v1/transactions/solana",e),t=await this.client.post(`/v1/transactions/solana/${r.internalTransactionId}/broadcast`);return{success:!0,data:{internalTransactionId:r.internalTransactionId,txHash:t.txHash,transactionUrl:t.transactionUrl}}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{};return r||t?{success:!1,error:r,errorMessage:t,errorDetails:s}:{success:!1,error:"SDK Error",errorMessage:e instanceof Error?e.message:"Unknown error",errorDetails:{}}}}async handleEvmSignMessage(e){try{return{success:!0,data:await this.client.post("/v1/messages/evm",{messageType:e.request.messageType,data:e.request.data,chainId:e.request.chainId})}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{};return r||t?{success:!1,error:r,errorMessage:t,errorDetails:s}:{success:!1,error:"SDK Error",errorMessage:e instanceof Error?e.message:"Unknown error",errorDetails:{}}}}async _updateJobStatus(e){try{return await this.client.post(`/v1/jobs/${e.jobId}/status`,e)}catch(e){return{status:400,message:`Failed to update job status: ${e instanceof Error?e.message:"Unknown error"}`}}}async handleSwidgeQuote(e){try{return{success:!0,data:await this.client.post("/v1/swidge/quote",e)}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to get swidge quote";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}async handleSwidgeExecute(e){try{return{success:!0,data:await this.client.post("/v1/swidge/execute",e)}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to execute swidge swap";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}async handlePolymarketMarketOrder(e){try{return{success:!0,data:await this.client.post("/v1/platforms/polymarket/market-order",e)}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to execute polymarket market order";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}async handlePolymarketRedeemPositions(e){try{return{success:!0,data:await this.client.post("/v1/platforms/polymarket/redeem-positions",e)}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to redeem polymarket positions";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}async handleMemorySet(e,r){try{return{success:!0,data:await this.client.post(`/v1/memory/${e}`,{value:r})}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to set memory";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}async handleMemoryGet(e){try{return{success:!0,data:await this.client.get(`/v1/memory/${e}`)}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to get memory";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}async handleMemoryDelete(e){try{return{success:!0,data:await this.client.delete(`/v1/memory/${e}`)}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to delete memory";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}async handleMemoryList(){try{return{success:!0,data:await this.client.get("/v1/memory/list")}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to list memory keys";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}async transactions(){try{return{success:!0,data:await this.client.get("/v1/transactions/ledger")}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to fetch transactions";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}};import{existsSync as n,readFileSync as i}from"fs";import{join as c}from"path";import{zValidator as u}from"@hono/zod-validator";import{Hono as l}from"hono";import{cors as d}from"hono/cors";import*as h from"zod";var m=class{sessionId;sessionWalletAddress;currentPositions;t;constructor(e){this.sessionId=e.sessionId,this.sessionWalletAddress=e.sessionWalletAddress,this.currentPositions=e.currentPositions,this.t=new a({sessionId:e.sessionId,baseUrl:e.baseUrl,authorizationHeader:e.authorizationHeader})}async log(e,r){const{error:t=!1,debug:s=!1}=r||{};let o,a;const n=(e,r)=>{if("bigint"==typeof r)return r.toString();if("function"==typeof r)return`[Function: ${r.name||"anonymous"}]`;if(void 0===r)return"[undefined]";if(null===r)return null;if("object"==typeof r&&!Array.isArray(r)&&r.toString!==Object.prototype.toString)try{const e=r.toString();if("[object Object]"!==e)return e}catch(e){}return r};if("object"==typeof e&&null!==e?(o=JSON.stringify(e,n,2),a=JSON.stringify(e,n)):(o=String(e),a=String(e)),t?console.error(o):console.log(o),s)return{success:!0};const i=t?"error":"observe";try{const e=a.length>250?a.slice(0,250):a;return await this.t._sendLog([{type:i,shortMessage:e}]),{success:!0}}catch(e){const r=e instanceof Error?e.message:"Failed to send log";return console.error(`Failed to send log to backend: ${r}`),{success:!1,error:"Log Error",errorMessage:r,errorDetails:{message:r,type:e instanceof Error?e.constructor.name:"UnknownError"}}}}async signAndSend(e){return this.t.signAndSend(e)}async signMessage(e){return this.t.signMessage(e)}memory={set:async(e,r)=>this.t.memory.set(e,r),get:async e=>this.t.memory.get(e),delete:async e=>this.t.memory.delete(e),list:async()=>this.t.memory.list()};platforms={polymarket:{marketOrder:async e=>this.t.platforms.polymarket.marketOrder(e),redeemPositions:async e=>this.t.platforms.polymarket.redeemPositions(e)}};swidge={quote:async e=>this.t.swidge.quote(e),execute:async e=>this.t.swidge.execute(e)};async transactions(){return this.t.transactions()}},g=h.object({network:h.string(),assetAddress:h.string(),tokenId:h.string().nullable(),avgUnitCost:h.string(),currentQty:h.string()}),y=h.object({sessionId:h.number(),sessionWalletAddress:h.string(),jobId:h.string().optional(),currentPositions:h.array(g)}),p=(h.object({status:h.string()}),class{app;runFunction;stopFunction;healthCheckFunction=async()=>({status:"healthy",timestamp:(new Date).toISOString()});constructor(e){this.app=new l,this.runFunction=e.runFunction,this.stopFunction=e.stopFunction,this.app.use("*",d()),this.setupRoutes()}defaultStopFunction=async e=>{await e.log(`Agent stopped for session ${e.sessionId}`)};async executeWithJobTracking(e,r,t){let s,o=!1;try{const s=new m({sessionId:e.sessionId,sessionWalletAddress:e.sessionWalletAddress,currentPositions:e.currentPositions,authorizationHeader:t});await r(s),o=!0}catch(e){s=this.getErrorMessage(e),o=!1,console.error("Agent function error:",s)}finally{e.jobId&&await this.updateJobStatus(e.sessionId,e.jobId,o?"success":"failed",s,t)}}getErrorMessage(e){if(null==e)return"Unknown error";try{const r=e?.constructor?.name||"Error";let t="";t=e instanceof Error&&e.message||String(e),t=t.replace(/[^\x20-\x7E\n\t]/g,"");const s=`${r}: ${t}`;return s.length>1e3?`${s.substring(0,997)}...`:s}catch{return"Unknown error (message extraction failed)"}}async updateJobStatus(e,r,t,s,o){const n=new a({sessionId:e,authorizationHeader:o});for(let e=1;e<=3;e++)try{return void await n._updateJobStatus({jobId:r,status:t,errorMessage:s})}catch(r){console.error(`Status update attempt ${e}/3 failed:`,r),e<3&&await new Promise(r=>setTimeout(r,100*2**(e-1)))}if("failed"===t)try{return console.warn(`Issue updating job status to '${t}' with error message, attempting to update status without error message`),void await n._updateJobStatus({jobId:r,status:t,errorMessage:void 0})}catch(e){console.error(`CRITICAL: Failed to update job ${r} status. Likely API connectivity issue:`,e)}else console.error(`CRITICAL: Failed to update job ${r} status to success after 3 attempts`)}setupRoutes(){this.app.post("/run",u("json",y),async e=>{const r=e.req.valid("json"),t=e.req.header("Authorization");return await this.executeWithJobTracking(r,this.runFunction,t),e.json({success:!0,message:"Execution completed"})}),this.app.post("/execute",u("json",y),async e=>{const r=e.req.valid("json"),t=e.req.header("Authorization");return await this.executeWithJobTracking(r,this.runFunction,t),e.json({success:!0,message:"Execution completed"})}),this.app.post("/stop",u("json",y),async e=>{const r=e.req.valid("json"),t=e.req.header("Authorization"),s=this.stopFunction||this.defaultStopFunction;return await this.executeWithJobTracking(r,s,t),e.json({success:!0,message:"Stop completed"})}),this.app.get("/health",async e=>{try{const r=await this.healthCheckFunction();return e.json(r)}catch(r){return console.error("Agent health check error:",r),e.json({status:"unhealthy",error:r instanceof Error?r.message:"Unknown error",timestamp:(new Date).toISOString()},500)}})}getPortFromPackageJson(){try{const e=c(process.cwd(),"package.json");if(n(e)){const r=JSON.parse(i(e,"utf-8"));if(r.circuit?.port)return console.log("⚠️ Warning: circuit.port in package.json is deprecated. Use AGENT_PORT environment variable instead."),Number.parseInt(r.circuit.port,10)}}catch(e){console.log("Could not read package.json for port configuration")}return null}async run(e){if("undefined"!=typeof globalThis&&void 0!==globalThis.Cloudflare)return this.getExport();const r=globalThis.Bun?.env,t=process.env.AGENT_PORT||r?.AGENT_PORT,s=this.getPortFromPackageJson();let o=e;!o&&t&&(o=Number.parseInt(t,10)),!o&&s&&(o=s),o||(o=3e3),console.log("🔧 Agent configuration:"),console.log(` Explicit port parameter: ${e||"not set"}`),console.log(` process.env.AGENT_PORT: ${process.env.AGENT_PORT||"not set"}`),console.log(` Bun.env.AGENT_PORT: ${r?.AGENT_PORT||"not set"}`),console.log(` package.json circuit.port: ${s||"not set"} (deprecated)`),console.log(` Final port: ${o}`);try{const{serve:e}=await import("@hono/node-server");console.log(`🚀 Server is running on port ${o}`),console.log("📍 Available endpoints: GET /health, POST /run, POST /execute (backward compat), POST /stop"),e({fetch:this.app.fetch,port:o})}catch(e){console.error("Failed to start local server. @hono/node-server is not available."),console.error("For local development, install @hono/node-server: npm install @hono/node-server"),process.exit(1)}}getExport(){return{fetch:async(e,r,t)=>(r&&"undefined"!=typeof globalThis&&(globalThis.__AGENT_ENV__=r),this.app.fetch(e,r,t))}}});import{z as f}from"zod";var w=f.templateLiteral(["ethereum:",f.coerce.number().int().nonnegative()]),v=f.union([f.literal("solana"),w]),E=f.object({address:f.string(),network:v}),T=(f.object({from:E,to:E,fromToken:f.string().optional(),toToken:f.string().optional(),amount:f.string(),slippage:f.string().optional()}),f.object({network:v,address:f.string(),token:f.string().nullable(),name:f.string().optional(),symbol:f.string().optional(),decimals:f.number().optional(),amount:f.string().optional(),minimumAmount:f.string().optional(),amountFormatted:f.string().optional(),amountUsd:f.string().optional()})),b=f.object({usd:f.string().optional(),percentage:f.string().optional()}),k=f.object({name:f.string(),amount:f.string().optional(),amountFormatted:f.string().optional(),amountUsd:f.string().optional()}),D=f.object({programId:f.string(),keys:f.array(f.object({pubkey:f.string(),isSigner:f.boolean(),isWritable:f.boolean()})),data:f.union([f.string(),f.instanceof(Buffer)])}),S=f.object({type:f.literal("evm"),from:f.string().regex(/^0x[a-fA-F0-9]{40}$/),to:f.string().regex(/^0x[a-fA-F0-9]{40}$/),chainId:f.number(),value:f.number(),data:f.string().regex(/^0x[a-fA-F0-9]*$/),gas:f.number().nullish(),maxFeePerGas:f.number().nullish(),maxPriorityFeePerGas:f.number().nullish()}),A=f.object({type:f.literal("solana"),instructions:f.array(D),addressLookupTableAddresses:f.array(f.string())}),M=f.object({type:f.literal("transaction"),description:f.string(),transactionDetails:f.union([S,A]),metadata:f.record(f.string(),f.string())}),F=f.object({type:f.literal("signature"),description:f.string(),signatureData:f.string(),metadata:f.record(f.string(),f.string())}),$=f.discriminatedUnion("type",[M,F]),O=f.object({engine:f.literal("relay"),assetSend:T,assetReceive:T,priceImpact:b,fees:f.array(k),steps:f.array($)}),U=f.object({network:f.string(),txs:f.array(f.string())}),x=f.object({status:f.union([f.literal("success"),f.literal("failure"),f.literal("refund"),f.literal("delayed")]),in:U,out:U,lastUpdated:f.number()}),I={FOUND:"QUOTE_FOUND",NO_QUOTE_PROVIDED:"No quote provided",WALLET_NOT_FOUND:"Wallet not found",WALLET_MISMATCH:"From wallet does not match session wallet",PRICE_IMPACT_TOO_HIGH:"Failed to get quote. Error: Price impact is too high",NO_ROUTES_FOUND:"Failed to get quote. Error: no routes found",AMOUNT_TOO_SMALL:"Failed to get quote. APIError: Swap output amount is too small to cover fees required to execute swap"},P=e=>f.object({success:f.boolean(),data:e.optional(),error:f.string().optional(),errorMessage:f.string().optional(),errorDetails:f.object({message:f.string().optional(),error:f.string().optional(),status:f.number().optional(),statusText:f.string().optional()}).optional()});P(O),P(x);export{r as APIClient,p as Agent,m as AgentContext,a as AgentSdk,I as QUOTE_RESULT,o as getChainIdFromNetwork,t as isEthereumNetwork,s as isSolanaNetwork};
1
+ import{loadAuthFromFileSystem as e}from"./chunk-4I3A6QAK.js";var r=class{config;baseUrl;authorizationHeader;isCloudflareWorker(){return"undefined"!=typeof globalThis&&void 0!==globalThis.Cloudflare}hasServiceBinding(){if(this.isCloudflareWorker()){if(void 0!==globalThis.AGENTS_TO_API_PROXY)return!0;if(void 0!==globalThis.__AGENT_ENV__&&globalThis.__AGENT_ENV__?.AGENTS_TO_API_PROXY)return!0}return!1}constructor(e){this.config=e,this.baseUrl=e.baseUrl||"https://agents.circuit.org",this.authorizationHeader=e.authorizationHeader}getAgentSlug(){return"undefined"!=typeof process&&process.env?.CIRCUIT_AGENT_SLUG?process.env.CIRCUIT_AGENT_SLUG:void 0!==globalThis.CIRCUIT_AGENT_SLUG?globalThis.CIRCUIT_AGENT_SLUG:void 0}getAuthHeaders(){const e={};e["X-Session-Id"]=this.config.sessionId.toString();const r=this.getAgentSlug();if(r&&(e["X-Agent-Slug"]=r),this.isCloudflareWorker())return e;if(this.authorizationHeader)return e.Authorization=this.authorizationHeader,e;try{const r=this.loadAuthConfig();r?.sessionToken&&(e.Authorization=`Bearer ${r.sessionToken}`)}catch{}return e}loadAuthConfig(){try{return e()}catch{}}async makeRequest(e,r={}){const t={...{"Content-Type":"application/json",...this.getAuthHeaders()},...r.headers};let s;if(this.hasServiceBinding()){let o;if(void 0!==globalThis.AGENTS_TO_API_PROXY)o=globalThis.AGENTS_TO_API_PROXY;else{if(void 0===globalThis.__AGENT_ENV__||!globalThis.__AGENT_ENV__?.AGENTS_TO_API_PROXY)throw new Error("Service binding detected but not accessible");o=globalThis.__AGENT_ENV__.AGENTS_TO_API_PROXY}const a={...r,headers:t},n=`https://agents-to-api-proxy.circuit-0bc.workers.dev${e}`;s=await o.fetch(n,a)}else{const o=`${this.baseUrl}${e}`,a={...r,headers:t};s=await fetch(o,a)}if(!s.ok){const e=await s.json().catch(()=>({})),r=e.message||e.error||`HTTP ${s.status}: ${s.statusText}`,t=new Error(r);throw t.error=e.error,t.errorMessage=e.message,t.errorDetails=e,t.statusCode=s.status,t}return await s.json()}async get(e){return this.makeRequest(e,{method:"GET"})}async post(e,r){return this.makeRequest(e,{method:"POST",body:r?JSON.stringify(r):void 0})}async delete(e){return this.makeRequest(e,{method:"DELETE"})}};function t(e){return e.startsWith("ethereum:")}function s(e){return"solana"===e}function o(e){return Number(e.split(":")[1])}var a=class{client;config;constructor(e){this.config=e,this.client=new r(e)}async _sendLog(e){await this.client.post("/v1/logs",e)}async signAndSend(e){try{if(t(e.network)){const r=o(e.network);if("toAddress"in e.request)return await this.handleEvmTransaction({chainId:r,toAddress:e.request.toAddress,data:e.request.data,valueWei:e.request.value,message:e.message})}if(s(e.network)&&"hexTransaction"in e.request)return await this.handleSolanaTransaction({hexTransaction:e.request.hexTransaction,message:e.message});const r=`Unsupported network: ${e.network}`;return{success:!1,error:"Unsupported Network",errorMessage:r,errorDetails:{message:r}}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{};return r||t?{success:!1,error:r,errorMessage:t,errorDetails:s}:{success:!1,error:"SDK Error",errorMessage:e instanceof Error?e.message:"Unknown error",errorDetails:{}}}}async signMessage(e){try{if(t(e.network))return await this.handleEvmSignMessage(e);const r=`Unsupported network: ${e.network}`;return{success:!1,error:"Unsupported Network",errorMessage:r,errorDetails:{message:r}}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{};return r||t?{success:!1,error:r,errorMessage:t,errorDetails:s}:{success:!1,error:"SDK Error",errorMessage:e instanceof Error?e.message:"Unknown error",errorDetails:{}}}}swidge={quote:async e=>this.handleSwidgeQuote(e),execute:function(e){return this.handleSwidgeExecute(e)}.bind(this)};memory={set:async(e,r)=>this.handleMemorySet(e,r),get:async e=>this.handleMemoryGet(e),delete:async e=>this.handleMemoryDelete(e),list:async()=>this.handleMemoryList()};platforms={polymarket:{marketOrder:async e=>this.handlePolymarketMarketOrder(e),redeemPositions:async e=>this.handlePolymarketRedeemPositions(e||{tokenIds:[]})}};async handleEvmTransaction(e){try{const r=await this.client.post("/v1/transactions/evm",e),t=await this.client.post(`/v1/transactions/evm/${r.internalTransactionId}/broadcast`);return{success:!0,data:{internalTransactionId:r.internalTransactionId,txHash:t.txHash,transactionUrl:t.transactionUrl}}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{};return r||t?{success:!1,error:r,errorMessage:t,errorDetails:s}:{success:!1,error:"SDK Error",errorMessage:e instanceof Error?e.message:"Unknown error",errorDetails:{}}}}async handleSolanaTransaction(e){try{const r=await this.client.post("/v1/transactions/solana",e),t=await this.client.post(`/v1/transactions/solana/${r.internalTransactionId}/broadcast`);return{success:!0,data:{internalTransactionId:r.internalTransactionId,txHash:t.txHash,transactionUrl:t.transactionUrl}}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{};return r||t?{success:!1,error:r,errorMessage:t,errorDetails:s}:{success:!1,error:"SDK Error",errorMessage:e instanceof Error?e.message:"Unknown error",errorDetails:{}}}}async handleEvmSignMessage(e){try{return{success:!0,data:await this.client.post("/v1/messages/evm",{messageType:e.request.messageType,data:e.request.data,chainId:e.request.chainId})}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{};return r||t?{success:!1,error:r,errorMessage:t,errorDetails:s}:{success:!1,error:"SDK Error",errorMessage:e instanceof Error?e.message:"Unknown error",errorDetails:{}}}}async _updateJobStatus(e){try{return await this.client.post(`/v1/jobs/${e.jobId}/status`,e)}catch(e){return{status:400,message:`Failed to update job status: ${e instanceof Error?e.message:"Unknown error"}`}}}async handleSwidgeQuote(e){try{return{success:!0,data:await this.client.post("/v1/swidge/quote",e)}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to get swidge quote";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}async handleSwidgeExecute(e){try{const r=await this.client.post("/v1/swidge/execute",e);if(Array.isArray(r))return r.map(e=>{const r="success"===e.status;return{success:r,data:e,error:r?void 0:e.error}});const t="success"===r.status;return{success:t,data:r,error:t?void 0:r.error}}catch(r){const t=r.error,s=r.errorMessage,o=r.errorDetails||{},a=r instanceof Error?r.message:"Failed to execute swidge swap";return Array.isArray(e)?[{success:!1,error:t||"SDK Error",errorMessage:s||a,errorDetails:o}]:{success:!1,error:t||"SDK Error",errorMessage:s||a,errorDetails:o}}}async handlePolymarketMarketOrder(e){try{return{success:!0,data:await this.client.post("/v1/platforms/polymarket/market-order",e)}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to execute polymarket market order";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}async handlePolymarketRedeemPositions(e){try{return{success:!0,data:await this.client.post("/v1/platforms/polymarket/redeem-positions",e)}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to redeem polymarket positions";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}async handleMemorySet(e,r){try{return{success:!0,data:await this.client.post(`/v1/memory/${e}`,{value:r})}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to set memory";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}async handleMemoryGet(e){try{return{success:!0,data:await this.client.get(`/v1/memory/${e}`)}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to get memory";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}async handleMemoryDelete(e){try{return{success:!0,data:await this.client.delete(`/v1/memory/${e}`)}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to delete memory";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}async handleMemoryList(){try{return{success:!0,data:await this.client.get("/v1/memory/list")}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to list memory keys";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}async transactions(){try{return{success:!0,data:await this.client.get("/v1/transactions/ledger")}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to fetch transactions";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}async getCurrentPositions(){try{return{success:!0,data:await this.client.get("/v1/positions/current")}}catch(e){const r=e.error,t=e.errorMessage,s=e.errorDetails||{},o=e instanceof Error?e.message:"Failed to fetch current positions";return{success:!1,error:r||"SDK Error",errorMessage:t||o,errorDetails:s}}}};import{existsSync as n,readFileSync as i}from"fs";import{join as c}from"path";import{zValidator as u}from"@hono/zod-validator";import{Hono as l}from"hono";import{cors as d}from"hono/cors";import*as h from"zod";var m=class{sessionId;sessionWalletAddress;currentPositions;t;constructor(e){this.sessionId=e.sessionId,this.sessionWalletAddress=e.sessionWalletAddress,this.currentPositions=e.currentPositions,this.t=new a({sessionId:e.sessionId,baseUrl:e.baseUrl,authorizationHeader:e.authorizationHeader})}async log(e,r){const{error:t=!1,debug:s=!1}=r||{};let o,a;const n=(e,r)=>{if("bigint"==typeof r)return r.toString();if("function"==typeof r)return`[Function: ${r.name||"anonymous"}]`;if(void 0===r)return"[undefined]";if(null===r)return null;if("object"==typeof r&&!Array.isArray(r)&&r.toString!==Object.prototype.toString)try{const e=r.toString();if("[object Object]"!==e)return e}catch(e){}return r};if("object"==typeof e&&null!==e?(o=JSON.stringify(e,n,2),a=JSON.stringify(e,n)):(o=String(e),a=String(e)),t?console.error(o):console.log(o),s)return{success:!0};const i=t?"error":"observe";try{const e=a.length>250?a.slice(0,250):a;return await this.t._sendLog([{type:i,shortMessage:e}]),{success:!0}}catch(e){const r=e instanceof Error?e.message:"Failed to send log";return console.error(`Failed to send log to backend: ${r}`),{success:!1,error:"Log Error",errorMessage:r,errorDetails:{message:r,type:e instanceof Error?e.constructor.name:"UnknownError"}}}}async signAndSend(e){return this.t.signAndSend(e)}async signMessage(e){return this.t.signMessage(e)}memory={set:async(e,r)=>this.t.memory.set(e,r),get:async e=>this.t.memory.get(e),delete:async e=>this.t.memory.delete(e),list:async()=>this.t.memory.list()};platforms={polymarket:{marketOrder:async e=>this.t.platforms.polymarket.marketOrder(e),redeemPositions:async e=>this.t.platforms.polymarket.redeemPositions(e)}};swidge={quote:async e=>this.t.swidge.quote(e),execute:function(e){return this.t.swidge.execute(e)}.bind(this)};async transactions(){return this.t.transactions()}async getCurrentPositions(){return this.t.getCurrentPositions()}},g=h.object({network:h.string(),assetAddress:h.string(),tokenId:h.string().nullable(),avgUnitCost:h.string(),currentQty:h.string()}),y=h.object({sessionId:h.number(),sessionWalletAddress:h.string(),jobId:h.string().optional(),currentPositions:h.array(g)}),p=(h.object({status:h.string()}),class{app;runFunction;stopFunction;healthCheckFunction=async()=>({status:"healthy",timestamp:(new Date).toISOString()});constructor(e){this.app=new l,this.runFunction=e.runFunction,this.stopFunction=e.stopFunction,this.app.use("*",d()),this.setupRoutes()}defaultStopFunction=async e=>{await e.log(`Agent stopped for session ${e.sessionId}`)};async executeWithJobTracking(e,r,t){let s,o=!1;try{const s=new m({sessionId:e.sessionId,sessionWalletAddress:e.sessionWalletAddress,currentPositions:e.currentPositions,authorizationHeader:t});await r(s),o=!0}catch(e){s=this.getErrorMessage(e),o=!1,console.error("Agent function error:",s)}finally{e.jobId&&await this.updateJobStatus(e.sessionId,e.jobId,o?"success":"failed",s,t)}}getErrorMessage(e){if(null==e)return"Unknown error";try{const r=e?.constructor?.name||"Error";let t="";t=e instanceof Error&&e.message||String(e),t=t.replace(/[^\x20-\x7E\n\t]/g,"");const s=`${r}: ${t}`;return s.length>1e3?`${s.substring(0,997)}...`:s}catch{return"Unknown error (message extraction failed)"}}async updateJobStatus(e,r,t,s,o){const n=new a({sessionId:e,authorizationHeader:o});for(let e=1;e<=3;e++)try{return void await n._updateJobStatus({jobId:r,status:t,errorMessage:s})}catch(r){console.error(`Status update attempt ${e}/3 failed:`,r),e<3&&await new Promise(r=>setTimeout(r,100*2**(e-1)))}if("failed"===t)try{return console.warn(`Issue updating job status to '${t}' with error message, attempting to update status without error message`),void await n._updateJobStatus({jobId:r,status:t,errorMessage:void 0})}catch(e){console.error(`CRITICAL: Failed to update job ${r} status. Likely API connectivity issue:`,e)}else console.error(`CRITICAL: Failed to update job ${r} status to success after 3 attempts`)}setupRoutes(){this.app.post("/run",u("json",y),async e=>{const r=e.req.valid("json"),t=e.req.header("Authorization");return await this.executeWithJobTracking(r,this.runFunction,t),e.json({success:!0,message:"Execution completed"})}),this.app.post("/execute",u("json",y),async e=>{const r=e.req.valid("json"),t=e.req.header("Authorization");return await this.executeWithJobTracking(r,this.runFunction,t),e.json({success:!0,message:"Execution completed"})}),this.app.post("/stop",u("json",y),async e=>{const r=e.req.valid("json"),t=e.req.header("Authorization"),s=this.stopFunction||this.defaultStopFunction;return await this.executeWithJobTracking(r,s,t),e.json({success:!0,message:"Stop completed"})}),this.app.get("/health",async e=>{try{const r=await this.healthCheckFunction();return e.json(r)}catch(r){return console.error("Agent health check error:",r),e.json({status:"unhealthy",error:r instanceof Error?r.message:"Unknown error",timestamp:(new Date).toISOString()},500)}})}getPortFromPackageJson(){try{const e=c(process.cwd(),"package.json");if(n(e)){const r=JSON.parse(i(e,"utf-8"));if(r.circuit?.port)return console.log("⚠️ Warning: circuit.port in package.json is deprecated. Use AGENT_PORT environment variable instead."),Number.parseInt(r.circuit.port,10)}}catch(e){console.log("Could not read package.json for port configuration")}return null}async run(e){if("undefined"!=typeof globalThis&&void 0!==globalThis.Cloudflare)return this.getExport();const r=globalThis.Bun?.env,t=process.env.AGENT_PORT||r?.AGENT_PORT,s=this.getPortFromPackageJson();let o=e;!o&&t&&(o=Number.parseInt(t,10)),!o&&s&&(o=s),o||(o=3e3),console.log("🔧 Agent configuration:"),console.log(` Explicit port parameter: ${e||"not set"}`),console.log(` process.env.AGENT_PORT: ${process.env.AGENT_PORT||"not set"}`),console.log(` Bun.env.AGENT_PORT: ${r?.AGENT_PORT||"not set"}`),console.log(` package.json circuit.port: ${s||"not set"} (deprecated)`),console.log(` Final port: ${o}`);try{const{serve:e}=await import("@hono/node-server");console.log(`🚀 Server is running on port ${o}`),console.log("📍 Available endpoints: GET /health, POST /run, POST /execute (backward compat), POST /stop"),e({fetch:this.app.fetch,port:o})}catch(e){console.error("Failed to start local server. @hono/node-server is not available."),console.error("For local development, install @hono/node-server: npm install @hono/node-server"),process.exit(1)}}getExport(){return{fetch:async(e,r,t)=>(r&&"undefined"!=typeof globalThis&&(globalThis.__AGENT_ENV__=r),this.app.fetch(e,r,t))}}});import{z as f}from"zod";var w=f.templateLiteral(["ethereum:",f.coerce.number().int().nonnegative()]),v=f.union([f.literal("solana"),w]),E=f.object({address:f.string(),network:v}),T=(f.object({from:E,to:E,fromToken:f.string().optional(),toToken:f.string().optional(),amount:f.string(),slippage:f.string().optional()}),f.object({network:v,address:f.string(),token:f.string().nullable(),name:f.string().optional(),symbol:f.string().optional(),decimals:f.number().optional(),amount:f.string().optional(),minimumAmount:f.string().optional(),amountFormatted:f.string().optional(),amountUsd:f.string().optional()})),b=f.object({usd:f.string().optional(),percentage:f.string().optional()}),k=f.object({name:f.string(),amount:f.string().optional(),amountFormatted:f.string().optional(),amountUsd:f.string().optional()}),D=f.object({programId:f.string(),keys:f.array(f.object({pubkey:f.string(),isSigner:f.boolean(),isWritable:f.boolean()})),data:f.union([f.string(),f.instanceof(Buffer)])}),S=f.object({type:f.literal("evm"),from:f.string().regex(/^0x[a-fA-F0-9]{40}$/),to:f.string().regex(/^0x[a-fA-F0-9]{40}$/),chainId:f.number(),value:f.number(),data:f.string().regex(/^0x[a-fA-F0-9]*$/),gas:f.number().nullish(),maxFeePerGas:f.number().nullish(),maxPriorityFeePerGas:f.number().nullish()}),A=f.object({type:f.literal("solana"),instructions:f.array(D),addressLookupTableAddresses:f.array(f.string())}),M=f.object({type:f.literal("transaction"),description:f.string(),transactionDetails:f.union([S,A]),metadata:f.record(f.string(),f.string())}),F=f.object({type:f.literal("signature"),description:f.string(),signatureData:f.string(),metadata:f.record(f.string(),f.string())}),$=f.discriminatedUnion("type",[M,F]),O=f.object({engine:f.literal("relay"),assetSend:T,assetReceive:T,priceImpact:b,fees:f.array(k),steps:f.array($)}),U=f.object({network:f.string(),txs:f.array(f.string())}),x=f.object({status:f.union([f.literal("success"),f.literal("failure"),f.literal("refund"),f.literal("delayed")]),in:U,out:U,lastUpdated:f.number(),error:f.string().optional()}),I={FOUND:"QUOTE_FOUND",NO_QUOTE_PROVIDED:"No quote provided",WALLET_NOT_FOUND:"Wallet not found",WALLET_MISMATCH:"From wallet does not match session wallet",PRICE_IMPACT_TOO_HIGH:"Failed to get quote. Error: Price impact is too high",NO_ROUTES_FOUND:"Failed to get quote. Error: no routes found",AMOUNT_TOO_SMALL:"Failed to get quote. APIError: Swap output amount is too small to cover fees required to execute swap"},P=e=>f.object({success:f.boolean(),data:e.optional(),error:f.string().optional(),errorMessage:f.string().optional(),errorDetails:f.object({message:f.string().optional(),error:f.string().optional(),status:f.number().optional(),statusText:f.string().optional()}).optional()});P(O),P(x);export{r as APIClient,p as Agent,m as AgentContext,a as AgentSdk,I as QUOTE_RESULT,o as getChainIdFromNetwork,t as isEthereumNetwork,s as isSolanaNetwork};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@circuitorg/agent-sdk",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "typescript sdk for the Agent Toolset Service",
5
5
  "type": "module",
6
6
  "main": "index.js",