@circuitorg/agent-sdk 1.1.7 โ†’ 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +358 -751
  2. package/index.d.ts +1294 -448
  3. package/index.js +1 -1
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -1,20 +1,45 @@
1
- # Circuit Agent SDK - Typescript
1
+ # Circuit Agent SDK - TypeScript
2
2
 
3
- > **Clean, type-safe TypeScript SDK for building cross-chain agents on the circuit platform**
3
+ > **Clean, unified, type-safe TypeScript SDK for building cross-chain agents on Circuit**
4
4
 
5
- A TypeScript SDK for building automated agents to deploy on Circuit. Features a simple API surface with just **2 core methods** plus **platform integrations** with full type safety.
5
+ A simplified TypeScript SDK for building automated agents to deploy on Circuit. Agents receive a single `AgentContext` object containing everything they need - request data, SDK methods, and unified logging. No boilerplate, no return values to manage, just write your logic.
6
6
 
7
7
  > **๐Ÿ’ก Best used with [Circuit Agents CLI](https://github.com/circuitorg/agents-cli)** - Deploy, manage, and test your agents with ease
8
8
 
9
- ## โœจ Features
10
-
11
- - **๐ŸŽฏ Simple API**: 2 core methods (`sendLog()`, `signAndSend()`) + platform integrations
12
- - **๐Ÿ”’ Type Safety**: Network parameter determines valid request shapes automatically
13
- - **๐Ÿš€ Cross-Chain**: Unified interface for EVM and Solana networks
14
- - **๐ŸŒ‰ Cross-Chain Swaps**: Built-in Swidge integration for seamless token swaps and bridges
15
- - **๐Ÿ“ˆ Polymarket Integration**: Trade prediction markets with `sdk.polymarket.*` methods
9
+ ## ๐Ÿ“‘ Table of Contents
10
+
11
+ - [Circuit Agent SDK - TypeScript](#circuit-agent-sdk---typescript)
12
+ - [๐Ÿ“‘ Table of Contents](#-table-of-contents)
13
+ - [๐Ÿš€ Quick Start](#-quick-start)
14
+ - [Install the SDK](#install-the-sdk)
15
+ - [Create Your First Agent](#create-your-first-agent)
16
+ - [๐ŸŽฏ Core Concepts](#-core-concepts)
17
+ - [The AgentContext Object](#the-agentcontext-object)
18
+ - [Run/Stop Function Requirements](#runstop-function-requirements)
19
+ - [๐Ÿ“ Unified Logging with agent.log()](#-unified-logging-with-agentlog)
20
+ - [๐Ÿ’พ Session Memory Storage](#-session-memory-storage)
21
+ - [๐ŸŒ‰ Cross-Chain Swaps with Swidge](#-cross-chain-swaps-with-swidge)
22
+ - [Get and execute a quote](#get-and-execute-a-quote)
23
+ - [๐Ÿ“ˆ Polymarket Prediction Markets](#-polymarket-prediction-markets)
24
+ - [Place Market Orders](#place-market-orders)
25
+ - [Redeem Positions](#redeem-positions)
26
+ - [๐Ÿš€ Sign \& Send Transactions](#-sign--send-transactions)
27
+ - [Ethereum (any EVM chain)](#ethereum-any-evm-chain)
28
+ - [Solana](#solana)
29
+ - [โšก Error Handling](#-error-handling)
30
+ - [๐Ÿ› ๏ธ Deployment](#๏ธ-deployment)
31
+ - [What You Write](#what-you-write)
32
+ - [Deploy to Circuit](#deploy-to-circuit)
33
+ - [Test Locally](#test-locally)
34
+ - [๐Ÿงช Manual Instantiation (Testing)](#-manual-instantiation-testing)
35
+ - [๐Ÿ“š Working Examples](#-working-examples)
36
+ - [`demo-agent.ts`](#demo-agentts)
37
+ - [`examples/kitchen-sink.ts`](#exampleskitchen-sinkts)
38
+ - [๐ŸŽฏ Key Takeaways](#-key-takeaways)
39
+ - [๐Ÿ“– Additional Resources](#-additional-resources)
16
40
 
17
41
  ## ๐Ÿš€ Quick Start
42
+
18
43
  ### Install the SDK
19
44
  ```bash
20
45
  npm install @circuitorg/agent-sdk
@@ -24,867 +49,449 @@ yarn add @circuitorg/agent-sdk
24
49
  bun add @circuitorg/agent-sdk
25
50
  ```
26
51
 
27
- ### Sample SDK Usage
28
- >**NOTE:** The fastest, and recommended, way to get started is to setup an agent via the circuit [CLI](https://github.com/circuitorg/agents-cli)'s 'circuit agent init' command. This will setup a sample agent directory with the necessary agent wireframe, and configure the cli to allow you for easy testing and deployment. You just simply need to add in your secret formula to the execution and stop functions.
52
+ ### Create Your First Agent
29
53
 
30
- ```typescript
31
- import { AgentSdk } from "@circuitorg/agent-sdk";
54
+ Every agent receives a single `AgentContext` object that provides:
32
55
 
33
- // Initialize the sdk
34
- const sdk = new AgentSdk({
35
- sessionId: 123,
36
- });
37
- ```
56
+ **Session Data:**
57
+ - `agent.sessionId` - Unique session identifier
58
+ - `agent.sessionWalletAddress` - The wallet address for this session
59
+ - `agent.currentPositions` - Assets allocated to the agent at the start of this execution
38
60
 
39
- ## ๐ŸŽฏ Core SDK API (Only 2 Methods!)
61
+ **Available Methods:**
62
+ - `agent.log()` - Send messages to users and log locally
63
+ - `agent.memory` - Persist data across executions (`.set()`, `.get()`, `.list()`, `.delete()`)
64
+ - `agent.platforms.polymarket` - Trade prediction markets (`.marketOrder()`, `.redeemPositions()`)
65
+ - `agent.swidge` - Cross-chain swaps and bridges (`.quote()`, `.execute()`)
66
+ - `agent.signAndSend()` - Execute custom built transactions on any supported chain
67
+ - `agent.signMessage()` - Sign messages (EVM only)
40
68
 
41
- ### 1. Add Logs to Timeline
69
+ **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.
42
70
 
43
71
  ```typescript
44
- await sdk.sendLog({
45
- type: "observe",
46
- shortMessage: "Starting swap operation"
47
- });
48
- ```
49
-
50
- ### 2. Sign & Send Transactions
51
-
52
- #### Ethereum (any EVM chain)
72
+ import { Agent, AgentContext } from "@circuitorg/agent-sdk";
73
+
74
+ async function run(agent: AgentContext): Promise<void> {
75
+ /**
76
+ * Main agent logic - receives AgentContext with everything needed.
77
+ * No return value - errors are caught automatically.
78
+ */
79
+ // Access session data
80
+ await agent.log(`Starting execution for session ${agent.sessionId}`);
81
+ await agent.log(`Wallet: ${agent.sessionWalletAddress}`);
82
+ await agent.log(`Managing ${agent.currentPositions.length} allocated positions`);
83
+
84
+ // Use SDK methods
85
+ await agent.memory.set("last_run", Date.now().toString());
86
+
87
+ // Your agent logic here - track position changes within this execution
88
+ // Circuit will provide updated positions on the next run
89
+ }
53
90
 
54
- ```typescript
55
- // Native ETH transfer
56
- await sdk.signAndSend({
57
- network: "ethereum:1", // Chain ID in network string
58
- request: {
59
- toAddress: "0x742d35cc6634C0532925a3b8D65e95f32B6b5582" as `0x${string}`,
60
- data: "0x" as `0x${string}`,
61
- value: "1000000000000000000" // 1 ETH in wei
62
- },
63
- message: "Sending 1 ETH"
64
- });
91
+ async function stop(agent: AgentContext): Promise<void> {
92
+ /**Cleanup when agent is stopped.*/
93
+ await agent.log("Cleaning up resources");
94
+ await agent.memory.delete("temp_data");
95
+ }
65
96
 
66
- // Contract interaction
67
- await sdk.signAndSend({
68
- network: "ethereum:42161", // Arbitrum
69
- request: {
70
- toAddress: "0xTokenContract..." as `0x${string}`,
71
- data: "0xa9059cbb..." as `0x${string}`, // encoded transfer()
72
- value: "0"
73
- }
97
+ // Boilerplate - This should never change unless you want to change the names of your run and stop functions
98
+ const agent = new Agent({
99
+ runFunction: run,
100
+ stopFunction: stop
74
101
  });
75
- ```
76
-
77
- #### Solana
78
102
 
79
- ```typescript
80
- await sdk.signAndSend({
81
- network: "solana",
82
- request: {
83
- hexTransaction: "010001030a0b..." // serialized VersionedTransaction
84
- }
85
- });
103
+ export default agent.getExport();
86
104
  ```
87
105
 
106
+ ## ๐ŸŽฏ Core Concepts
88
107
 
89
- ## ๐ŸŒ‰ Cross-Chain Swaps with Swidge
108
+ ### The AgentContext Object
90
109
 
91
- The SDK includes built-in Swidge integration for seamless cross-chain token swaps and bridges.
110
+ Every agent function receives a single `AgentContext` object that contains:
92
111
 
93
- Swidge provides a unified interface that handles both **swapping** (exchanging tokens within the same network) and **bridging** (moving tokens across different networks) through a single, easy-to-use API. Whether you're doing a simple token swap on Ethereum or bridging assets across chains, the same quote-and-execute pattern works for everything.
112
+ **Request Data:**
113
+ - `agent.sessionId` - Unique session identifier
114
+ - `agent.sessionWalletAddress` - Wallet address for this session
115
+ - `agent.currentPositions` - Current positions allocated to this agent
94
116
 
95
- ### 3. Cross-Chain Swaps & Bridges
117
+ **SDK Methods:**
118
+ - `agent.log()` - Unified logging (console + backend)
119
+ - `agent.memory` - Session-scoped key-value storage
120
+ - `agent.platforms.polymarket` - Prediction market operations
121
+ - `agent.swidge` - Cross-chain swap operations
122
+ - `agent.signAndSend()` - Sign and broadcast transactions
123
+ - `agent.signMessage()` - Sign messages on EVM
96
124
 
97
- #### Get a Quote
125
+ ### Run/Stop Function Requirements
98
126
 
99
- ```typescript
100
- // ๐ŸŒ‰ Bridge USDC: Polygon โ†’ Arbitrum
101
- const bridgeQuote = await sdk.swidge.quote({
102
- from: { network: "ethereum:137", address: request.sessionWalletAddress },
103
- to: { network: "ethereum:42161", address: request.sessionWalletAddress },
104
- amount: "50000000", // $50 USDC (6 decimals)
105
- fromToken: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", // USDC on Polygon
106
- toToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC on Arbitrum
107
- slippage: "2.0", // 2% slippage for cross-chain (default: 0.5%)
108
- priceImpact: "1.0" // 1% max price impact (default: 0.5%)
109
- });
127
+ 1. **Signature**: `async function myFunction(agent: AgentContext): Promise<void>`
128
+ 2. **Return**: Always return `void` (or no return statement)
129
+ 3. **Errors**: You should surface any relevant errors via `agent.log('error message', { error: true })`. All errors from built-in SDK functions will be caught gracefully and provided in the return data for you to handle as necessary.
110
130
 
111
- // ๐Ÿ”„ Swap USDC โ†’ ETH on same chain (using defaults)
112
- const swapQuote = await sdk.swidge.quote({
113
- from: { network: "ethereum:42161", address: request.sessionWalletAddress },
114
- to: { network: "ethereum:42161", address: request.sessionWalletAddress },
115
- amount: "100000000", // $100 USDC (6 decimals)
116
- fromToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC
117
- // toToken omitted = native ETH (default behavior)
118
- // slippage defaults to "0.5", priceImpact defaults to "0.5"
119
- });
120
-
121
- if (quote.success) {
122
- console.log(`๐Ÿ’ฐ You'll receive: ${quote.data.assetReceive.amountFormatted}`);
123
- console.log(`๐Ÿ’ธ Total fees: ${quote.data.fees.map(f => f.name).join(", ")}`);
124
- } else if (quote.error) {
125
- // Check for specific error types
126
- if (quote.error === QUOTE_RESULT.WALLET_NOT_FOUND) {
127
- console.log("๐Ÿ‘› Wallet not found");
128
- } else if (quote.error === QUOTE_RESULT.WALLET_MISMATCH) {
129
- console.log("๐Ÿ” Wallet address doesn't match session");
130
- } else {
131
- console.log("โ“ Quote not available for this swap");
132
- }
133
- }
134
- ```
131
+ ## ๐Ÿ“ Unified Logging with agent.log()
135
132
 
136
- #### Execute a Swap
133
+ Use `agent.log()` to communicate with your users and debug your agent. Every message appears in your terminal, and by default also shows up in the Circuit UI for your users to see. Simply pass `debug: true` to skip sending the message to the user.
137
134
 
138
135
  ```typescript
139
- // 1๏ธโƒฃ Get a quote first
140
- let quoteRequest = {
141
- from: { network: "ethereum:42161", address: request.sessionWalletAddress },
142
- to: { network: "ethereum:1", address: request.sessionWalletAddress },
143
- amount: "100000000", // $100 USDC
144
- fromToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC on Arbitrum
145
- priceImpact: "0.1", // Conservative price impact setting
146
- slippage: "5.0"
147
- };
136
+ async function run(agent: AgentContext): Promise<void> {
137
+ // Standard log: Shows to user in Circuit UI
138
+ await agent.log("Processing transaction");
139
+
140
+ // Error log: Shows to user in Circuit UI (as an error)
141
+ const result = await agent.memory.get("key");
142
+ if (!result.success) {
143
+ await agent.log(result.error_message || result.error, { error: true });
144
+ }
148
145
 
149
- let quote = await sdk.swidge.quote(quoteRequest);
146
+ // Debug log: Only you see this in your terminal
147
+ await agent.log("Internal state: processing...", { debug: true });
150
148
 
151
- // 2๏ธโƒฃ Handle quote failures with retry logic
152
- if (quote.error === QUOTE_RESULT.NO_QUOTE_PROVIDED) {
153
- console.log(`โ“ Quote not available, increasing price impact and retrying...`)
154
- // Retry with more permissive parameters
155
- quoteRequest.priceImpact = "10.0";
156
- quoteRequest.slippage = "10.0";
157
- quote = await sdk.swidge.quote(quoteRequest);
158
- }
149
+ // Logging objects
150
+ // Objects are pretty-printed to console and serialized/truncated for backend
151
+ await agent.log({ wallet: agent.sessionWalletAddress, status: "active" });
159
152
 
160
- // 3๏ธโƒฃ Execute the swap if quote succeeded
161
- if (quote.success && quote.data) {
162
- console.log(`๐Ÿ’ฐ Expected to receive: ${quote.data.assetReceive.amountFormatted}`);
163
- console.log(`๐Ÿ’ธ Fees: ${quote.data.fees.map(f => `${f.name}: ${f.amountFormatted}`).join(", ")}`);
164
-
165
- const result = await sdk.swidge.execute(quote.data);
166
-
167
- if (result.success && result.data) {
168
- console.log(`๐ŸŽ‰ Status: ${result.data.status}`);
169
-
170
- if (result.data.status === "success") {
171
- console.log(`๐Ÿ“ค Sent: ${result.data.in.txs[0]}`);
172
- console.log(`๐Ÿ“ฅ Received: ${result.data.out.txs[0]}`);
173
- console.log("โœ… Cross-chain swap completed!");
174
- } else if (result.data.status === "failure") {
175
- console.log("โŒ Transaction failed");
176
- } else if (result.data.status === "refund") {
177
- console.log("โ†ฉ๏ธ Transaction was refunded");
178
- } else if (result.data.status === "delayed") {
179
- console.log("โฐ Transaction is delayed");
180
- }
181
- } else {
182
- console.log(`โŒ Execute failed: ${result.error}`);
153
+ // Check if message reached the user
154
+ const logResult = await agent.log("Important message");
155
+ if (!logResult.success) {
156
+ // Rare - usually means Circuit UI is unreachable
183
157
  }
184
- } else {
185
- console.log(`โŒ Quote failed after retry: ${quote.error}`);
186
- return { success: false };
187
158
  }
188
159
  ```
189
160
 
190
- ### Swidge API Reference
161
+ **What Your Users See:**
191
162
 
192
- #### `sdk.swidge.quote(request: SwidgeQuoteRequest): Promise<SwidgeQuoteResponse>`
163
+ | Code | You See (Terminal) | User Sees (Circuit UI) |
164
+ |------|-------------------|----------------------|
165
+ | `agent.log("msg")` | โœ… In your terminal | โœ… In Circuit UI |
166
+ | `agent.log("msg", { error: true })` | โœ… As error in terminal | โœ… As error in Circuit UI |
167
+ | `agent.log("msg", { debug: true })` | โœ… In terminal | โŒ Hidden from user |
168
+ | `agent.log("msg", { error: true, debug: true })` | โœ… As error in terminal | โŒ Hidden from user |
193
169
 
194
- Get pricing and routing information for token swaps between networks or within the same network.
170
+ ## ๐Ÿ’พ Session Memory Storage
195
171
 
196
- **Parameters:**
197
- ```typescript
198
- {
199
- from: { network: SwidgeNetwork, address: string },
200
- to: { network: SwidgeNetwork, address: string },
201
- amount: string, // Amount in token's smallest unit
202
- fromToken?: string, // Source token contract (omit for native tokens)
203
- toToken?: string, // Destination token contract (omit for native tokens)
204
- slippage?: string, // Slippage tolerance % (default: "0.5")
205
- priceImpact?: string // Max price impact % (default: "0.5")
206
- }
207
- ```
172
+ Store and retrieve data for your agent's session with simple operations. Memory is **automatically scoped to your session ID**, and for now is simple string storage. You will need to handle serialization of whatever data you want to store here.
208
173
 
209
- **Returns:**
210
174
  ```typescript
211
- {
212
- success: boolean,
213
- data?: {
214
- engine: "relay",
215
- assetSend: {
216
- network: SwidgeNetwork,
217
- address: string,
218
- token: string | null,
219
- amount: string,
220
- amountFormatted: string,
221
- amountUsd: string,
222
- // ... additional fields
223
- },
224
- assetReceive: {
225
- network: SwidgeNetwork,
226
- address: string,
227
- token: string | null,
228
- amount: string,
229
- amountFormatted: string,
230
- amountUsd: string,
231
- // ... additional fields
232
- },
233
- priceImpact: {
234
- usd?: string,
235
- percentage?: string
236
- },
237
- fees: Array<{
238
- name: string,
239
- amount?: string,
240
- amountFormatted?: string,
241
- amountUsd?: string
242
- }>,
243
- steps: Array<SwidgeUnsignedStep> // Transaction steps to execute
244
- },
245
- error?: string,
246
- errorDetails?: {
247
- message: string,
248
- status?: number,
249
- statusText?: string
175
+ async function run(agent: AgentContext): Promise<void> {
176
+ // Set a value
177
+ const result = await agent.memory.set("lastSwapNetwork", "ethereum:42161");
178
+ if (!result.success) {
179
+ await agent.log(result.error_message || result.error, { error: true });
250
180
  }
181
+
182
+ // Get a value
183
+ const getResult = await agent.memory.get("lastSwapNetwork");
184
+ if (getResult.success && getResult.data) {
185
+ const network = getResult.data.value;
186
+ await agent.log(`Using network: ${network}`);
187
+ } else {
188
+ await agent.log("No saved network found");
189
+ }
190
+
191
+ // List all keys
192
+ const listResult = await agent.memory.list();
193
+ if (listResult.success && listResult.data) {
194
+ await agent.log(`Found ${listResult.data.count} keys: ${listResult.data.keys}`);
195
+ }
196
+
197
+ // Delete a key
198
+ await agent.memory.delete("tempData");
251
199
  }
252
200
  ```
253
201
 
254
- **Error Handling with QUOTE_RESULT:**
255
-
256
- The SDK exports a `QUOTE_RESULT` constant that provides type-safe error checking with full IntelliSense support:
202
+ **All memory operations return responses with `.success` and `.error_message`:**
257
203
 
258
204
  ```typescript
259
- import { QUOTE_RESULT } from "@circuitorg/agent-sdk";
260
-
261
- // Use QUOTE_RESULT constants instead of hardcoded strings
262
- if (quote.error === QUOTE_RESULT.WALLET_NOT_FOUND) {
263
- // Handle wallet not found
264
- } else if (quote.error === QUOTE_RESULT.WALLET_MISMATCH) {
265
- // Handle wallet mismatch
205
+ const result = await agent.memory.set("key", "value");
206
+ if (!result.success) {
207
+ await agent.log(`Failed to save: ${result.error_message}`, { error: true });
266
208
  }
267
209
  ```
268
210
 
269
- **Possible Error Responses:**
270
- - `QUOTE_RESULT.FOUND` - Success case (not an error)
271
- - `QUOTE_RESULT.NO_QUOTE_PROVIDED` - Generic API error
272
- - `QUOTE_RESULT.WALLET_NOT_FOUND` - Wallet address not found
273
- - `QUOTE_RESULT.WALLET_MISMATCH` - From wallet does not match session wallet
274
-
275
- #### `sdk.swidge.execute(quoteData: SwidgeExecuteRequest): Promise<SwidgeExecuteResponse>`
211
+ ## ๐ŸŒ‰ Cross-Chain Swaps with Swidge
276
212
 
277
- Execute a cross-chain swap or bridge using a complete quote from `sdk.swidge.quote()`.
213
+ Built-in Swidge integration for seamless cross-chain token swaps and bridges.
278
214
 
279
- Pass the entire `quote.data` object returned from `quote()` - the SDK handles all transaction signing, broadcasting, and cross-chain coordination.
215
+ ### Get and execute a quote
216
+ > 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
280
217
 
281
- **Parameters:**
282
218
  ```typescript
283
- // The complete quote.data object from sdk.swidge.quote()
284
- {
285
- engine: "relay",
286
- assetSend: SwidgeQuoteAsset,
287
- assetReceive: SwidgeQuoteAsset,
288
- priceImpact: SwidgePriceImpact,
289
- fees: SwidgeFee[],
290
- steps: SwidgeUnsignedStep[] // Contains transaction details for each step
291
- }
292
- ```
219
+ async function run(agent: AgentContext): Promise<void> {
220
+ // 1. Get quote
221
+ const quote = await agent.swidge.quote({
222
+ from: { network: "ethereum:8453", address: agent.sessionWalletAddress },
223
+ to: { network: "ethereum:137", address: agent.sessionWalletAddress },
224
+ amount: "1000000000000000", // 0.001 ETH
225
+ toToken: "0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
226
+ slippage: "2.0",
227
+ priceImpact: "100.0"
228
+ });
293
229
 
294
- **Returns:**
295
- ```typescript
296
- {
297
- success: boolean,
298
- data?: {
299
- status: "success" | "failure" | "refund" | "delayed",
300
- in: {
301
- network: string,
302
- txs: string[] // Transaction hashes for sending side
303
- },
304
- out: {
305
- network: string,
306
- txs: string[] // Transaction hashes for receiving side
307
- },
308
- lastUpdated: number // Timestamp of last status update
309
- },
310
- error?: string,
311
- errorDetails?: {
312
- message: string,
313
- status?: number,
314
- statusText?: string
230
+ if (!quote.success) {
231
+ await agent.log(`Quote failed: ${quote.error_message}`, { error: true });
232
+ return;
233
+ }
234
+
235
+ if (quote.data && Math.abs(parseFloat(quote.data.priceImpact.percentage)) > 10) {
236
+ await agent.log(`Warning: Price impact is too high: ${quote.data.priceImpact.percentage}%`, { error: true, debug: true });
237
+ } else if (quote.data) {
238
+ await agent.log(`You'll receive: ${quote.data.assetReceive.amountFormatted}`);
239
+ await agent.log(`Fees: ${quote.data.fees.map(f => f.name).join(', ')}`);
240
+
241
+ // 2. Execute the swap
242
+ const result = await agent.swidge.execute(quote.data);
243
+
244
+ if (result.success && result.data) {
245
+ await agent.log(`Swap status: ${result.data.status}`);
246
+ if (result.data.status === "success") {
247
+ await agent.log("โœ… Swap completed!");
248
+ await agent.log(`In tx: ${result.data.in.txs}`);
249
+ await agent.log(`Out tx: ${result.data.out.txs}`);
250
+ } else if (result.data.status === "failure") {
251
+ await agent.log("โŒ Swap failed", { error: true });
252
+ }
253
+ } else {
254
+ await agent.log(result.error_message || result.error, { error: true });
255
+ }
315
256
  }
316
257
  }
317
258
  ```
318
259
 
319
- **Execution Flow:**
320
- 1. SDK signs and broadcasts transactions from the `steps` array
321
- 2. Monitors cross-chain execution progress
322
- 3. Returns final status once complete (never returns "pending" or "waiting")
323
-
324
- **Status Meanings:**
325
- - `"success"` - All transactions completed successfully
326
- - `"failure"` - One or more transactions failed
327
- - `"refund"` - Transaction was refunded (e.g., due to timeout or failure)
328
- - `"delayed"` - Transaction is taking longer than expected but still processing
329
-
330
-
331
260
  ## ๐Ÿ“ˆ Polymarket Prediction Markets
332
261
 
333
- The SDK includes built-in Polymarket integration for trading prediction markets on Polygon.
334
-
335
- ### 4. Polymarket Trading
262
+ Trade prediction markets on Polygon.
336
263
 
337
- #### Get Positions
338
-
339
- Fetch all current positions for the session wallet:
264
+ ### Place Market Orders
265
+ > Note: Right now, polymarket's API accepts different decimal precision for buys and sells, this will result in dust positions if you are selling out of a position before expiry. Once expired, dust can be cleaned up during the redeem step.
340
266
 
341
267
  ```typescript
342
- const positions = await sdk.polymarket.positions();
343
-
344
- if (positions.success && positions.data) {
345
- console.log(`Total value: $${positions.data.totalValue}`);
346
-
347
- for (const position of positions.data.positions) {
348
- console.log(`${position.question} (${position.outcome})`);
349
- console.log(` Shares: ${position.formattedShares}`);
350
- console.log(` Value: $${position.valueUsd}`);
351
- console.log(` P&L: $${position.pnlUsd} (${position.pnlPercent}%)`);
268
+ async function run(agent: AgentContext): Promise<void> {
269
+ // Buy order - size is USD amount to spend
270
+ const buyOrder = await agent.platforms.polymarket.marketOrder({
271
+ tokenId: "86192057611122246511563653509192966169513312957180910360241289053249649036697",
272
+ size: 3, // Spend $3
273
+ side: "BUY"
274
+ });
275
+
276
+ if (buyOrder.success && buyOrder.data) {
277
+ await agent.log(`Order ID: ${buyOrder.data.orderInfo.orderId}`);
278
+ await agent.log(`Price: $${buyOrder.data.orderInfo.priceUsd}`);
279
+ await agent.log(`Total: $${buyOrder.data.orderInfo.totalPriceUsd}`);
280
+ } else {
281
+ await agent.log(buyOrder.error_message || buyOrder.error, { error: true });
352
282
  }
283
+
284
+ // Sell order - size is number of shares to sell
285
+ const sellOrder = await agent.platforms.polymarket.marketOrder({
286
+ tokenId: "86192057611122246511563653509192966169513312957180910360241289053249649036697",
287
+ size: 5.5, // Sell 5.5 shares
288
+ side: "SELL"
289
+ });
353
290
  }
354
291
  ```
355
292
 
356
- #### Place Market Orders
357
-
358
- Execute buy or sell market orders:
293
+ ### Redeem Positions
359
294
 
360
295
  ```typescript
361
- // Buy order - size is USD amount to spend
362
- const buyOrder = await sdk.polymarket.marketOrder({
363
- tokenId: "123456789...", // Market token ID
364
- size: 10, // Spend $10 to buy shares
365
- side: "BUY"
366
- });
296
+ async function stop(agent: AgentContext): Promise<void> {
297
+ /**Redeem all settled positions.*/
298
+ const redemption = await agent.platforms.polymarket.redeemPositions();
367
299
 
368
- // Sell order - size is number of shares to sell
369
- const sellOrder = await sdk.polymarket.marketOrder({
370
- tokenId: "123456789...",
371
- size: 5.5, // Sell 5.5 shares
372
- side: "SELL"
373
- });
374
-
375
- if (buyOrder.success && buyOrder.data) {
376
- console.log(`Order Success: ${buyOrder.data.success}`);
377
- console.log(`Order ID: ${buyOrder.data.orderInfo.orderId}`);
378
- console.log(`Filled at $${buyOrder.data.orderInfo.priceUsd} per share`);
379
- console.log(`Total cost: $${buyOrder.data.orderInfo.totalPriceUsd}`);
300
+ if (redemption.success && redemption.data) {
301
+ const successful = redemption.data.filter(r => r.success);
302
+ await agent.log(`Redeemed ${successful.length} positions`);
303
+ } else {
304
+ await agent.log(redemption.error_message || redemption.error, { error: true });
305
+ }
380
306
  }
381
307
  ```
382
308
 
383
- #### Redeem Positions
309
+ ## ๐Ÿš€ Sign & Send Transactions
384
310
 
385
- Claim winnings from settled positions:
311
+ ### Ethereum (any EVM chain)
386
312
 
387
313
  ```typescript
388
- // Redeem all redeemable positions
389
- const redemption = await sdk.polymarket.redeemPositions();
390
-
391
- if (redemption.success && redemption.data) {
392
- for (const result of redemption.data) {
393
- if (result.success) {
394
- console.log(`โœ… Redeemed: ${result.position.question}`);
395
- console.log(` TX: ${result.transactionHash}`);
314
+ async function run(agent: AgentContext): Promise<void> {
315
+ // Self-send demo - send a small amount to yourself
316
+ const response = await agent.signAndSend({
317
+ network: "ethereum:1",
318
+ request: {
319
+ toAddress: agent.sessionWalletAddress, // Send to self
320
+ data: "0x",
321
+ value: "100000000000000" // 0.0001 ETH
322
+ },
323
+ message: "Self-send demo"
324
+ });
325
+
326
+ if (response.success) {
327
+ await agent.log(`Transaction sent: ${response.txHash}`);
328
+ if (response.transactionUrl) {
329
+ await agent.log(`View: ${response.transactionUrl}`);
396
330
  }
331
+ } else {
332
+ await agent.log(response.error_message || response.error, { error: true });
397
333
  }
398
334
  }
399
-
400
- // Redeem specific positions by token IDs
401
- const specificRedemption = await sdk.polymarket.redeemPositions({
402
- tokenIds: ["123456", "789012"]
403
- });
404
335
  ```
405
336
 
406
- ### Polymarket API Reference
407
-
408
- #### `sdk.polymarket.positions(): Promise<PolymarketPositionsResponse>`
409
-
410
- Get all current positions for the session wallet.
337
+ ### Solana
411
338
 
412
- **Returns:**
413
339
  ```typescript
414
- {
415
- success: boolean,
416
- data?: {
417
- totalValue: number,
418
- positions: Array<{
419
- contractAddress: string,
420
- tokenId: string,
421
- question: string,
422
- outcome: string,
423
- formattedShares: string,
424
- shares: string,
425
- valueUsd: string,
426
- priceUsd: string,
427
- averagePriceUsd: string,
428
- pnlUsd: string,
429
- pnlPercent: string,
430
- isRedeemable: boolean,
431
- endDate: string,
432
- // ... additional fields
433
- }>
434
- },
435
- error?: string,
436
- errorDetails?: { message: string, status?: number, statusText?: string }
340
+ async function run(agent: AgentContext): Promise<void> {
341
+ const response = await agent.signAndSend({
342
+ network: "solana",
343
+ request: {
344
+ hexTransaction: "010001030a0b..." // serialized VersionedTransaction
345
+ }
346
+ });
347
+
348
+ if (response.success) {
349
+ await agent.log(`Transaction: ${response.txHash}`);
350
+ } else {
351
+ await agent.log(response.error_message || response.error, { error: true });
352
+ }
437
353
  }
438
354
  ```
439
355
 
440
- #### `sdk.polymarket.marketOrder(request): Promise<PolymarketMarketOrderResponse>`
441
-
442
- Place a buy or sell market order.
356
+ ## โšก Error Handling
443
357
 
444
- โš ๏ธ **Important**: The `size` parameter meaning differs by order side:
445
- - **BUY**: `size` is the USD amount to spend (e.g., 10 = $10 worth of shares)
446
- - **SELL**: `size` is the number of shares/tokens to sell (e.g., 10 = 10 shares)
358
+ **All SDK methods return response objects with `.success` and `.error_message`:**
447
359
 
448
- **Parameters:**
449
- ```typescript
450
- {
451
- tokenId: string, // Market token ID for the position
452
- size: number, // For BUY: USD amount. For SELL: Number of shares
453
- side: "BUY" | "SELL"
454
- }
455
- ```
456
360
 
457
- **Returns:**
458
- ```typescript
459
- {
460
- success: boolean,
461
- data?: {
462
- success: boolean, // Whether the order was successfully submitted
463
- orderInfo: {
464
- orderId: string,
465
- side: string,
466
- size: string,
467
- priceUsd: string,
468
- totalPriceUsd: string,
469
- txHashes: string[]
470
- }
471
- },
472
- error?: string,
473
- errorDetails?: { message: string, status?: number, statusText?: string }
474
- }
475
- ```
361
+ **Uncaught Exceptions:**
476
362
 
477
- #### `sdk.polymarket.redeemPositions(request?): Promise<PolymarketRedeemPositionsResponse>`
363
+ If your function throws an uncaught exception, the Agent SDK automatically:
364
+ 1. Logs the error to console (visible in local dev and cloud logs)
365
+ 2. Updates the job in the circuit backend with status='failed' and logs the error.
478
366
 
479
- Redeem settled positions to claim winnings.
480
-
481
- **Parameters (optional):**
482
367
  ```typescript
483
- {
484
- tokenIds?: string[] // Specific token IDs to redeem (default: all redeemable positions)
485
- }
486
- ```
368
+ async function run(agent: AgentContext): Promise<void> {
369
+ // This typo will be caught and logged automatically
370
+ await agent.memmory.set("key", "value"); // TypeError
487
371
 
488
- **Returns:**
489
- ```typescript
490
- {
491
- success: boolean,
492
- data?: Array<{
493
- success: boolean,
494
- position: { /* Full position details */ },
495
- transactionHash: string | null
496
- }>,
497
- error?: string,
498
- errorDetails?: { message: string, status?: number, statusText?: string }
372
+ // No need for try/catch around everything!
499
373
  }
500
374
  ```
501
375
 
502
- ### Complete Polymarket Example
376
+ ## ๐Ÿ› ๏ธ Deployment
503
377
 
504
- ```typescript
505
- const executionFunction: ExecutionFunctionContract = async (request) => {
506
- const sdk = new AgentSdk({ sessionId: request.sessionId });
507
-
508
- try {
509
- // Get current positions
510
- const positions = await sdk.polymarket.positions();
511
-
512
- if (!positions.success || !positions.data) {
513
- throw new Error(`Failed to get positions: ${positions.error}`);
514
- }
378
+ ### What You Write
515
379
 
516
- // Find specific position
517
- const targetPosition = positions.data.positions.find(
518
- p => p.tokenId === "YOUR_TOKEN_ID"
519
- );
520
-
521
- if (targetPosition && Number(targetPosition.formattedShares) > 0) {
522
- // Sell position
523
- const sellOrder = await sdk.polymarket.marketOrder({
524
- tokenId: targetPosition.tokenId,
525
- size: Number(targetPosition.formattedShares),
526
- side: "SELL"
527
- });
528
-
529
- if (sellOrder.success && sellOrder.data?.orderInfo) {
530
- await sdk.sendLog({
531
- type: "observe",
532
- shortMessage: `Sold ${targetPosition.outcome} for $${sellOrder.data.orderInfo.totalPriceUsd}`
533
- });
534
- }
535
- }
380
+ Your agent code should look like this - just define your functions and add the boilerplate at the bottom:
536
381
 
537
- return { success: true };
538
- } catch (error) {
539
- await sdk.sendLog({
540
- type: "error",
541
- shortMessage: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`
542
- });
543
- return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
544
- }
545
- };
382
+ ```typescript
383
+ import { Agent, AgentContext } from "@circuitorg/agent-sdk";
546
384
 
547
- const stopFunction: StopFunctionContract = async (request) => {
548
- const sdk = new AgentSdk({ sessionId: request.sessionId });
385
+ async function run(agent: AgentContext): Promise<void> {
386
+ await agent.log("Hello from my agent!");
387
+ // Your agent logic here
388
+ }
549
389
 
550
- try {
551
- // Redeem all settled positions on stop
552
- const redemption = await sdk.polymarket.redeemPositions();
390
+ async function stop(agent: AgentContext): Promise<void> {
391
+ await agent.log("Cleaning up...");
392
+ // Optional cleanup logic
393
+ }
553
394
 
554
- if (redemption.success && redemption.data) {
555
- const successful = redemption.data.filter(r => r.success);
556
- await sdk.sendLog({
557
- type: "observe",
558
- shortMessage: `โœ… Redeemed ${successful.length} positions`
559
- });
560
- }
395
+ // ============================================================================
396
+ // BOILERPLATE - Don't modify, this handles local testing AND Circuit deployment
397
+ // ============================================================================
398
+ const agent = new Agent({
399
+ runFunction: run,
400
+ stopFunction: stop
401
+ });
561
402
 
562
- return { success: true };
563
- } catch (error) {
564
- return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
565
- }
566
- };
403
+ export default agent.getExport();
567
404
  ```
568
405
 
406
+ That's it! The boilerplate code automatically handles the rest
569
407
 
570
- ## ๐Ÿ”ง Examples
571
-
572
- ### Barebones Agent
573
- >This is the wireframe structure all agents need to have (executionFunction, stopFunction, and export default agent.getWorkerExport()). The main thing an agent dev needs to focus on is the execution logic, and any cleanup logic that would need to be ran whenever a user decides to stop an agent session.
574
-
575
- ```typescript
576
- import { Agent, AgentSdk, ExecutionFunctionContract, StopFunctionContract } from "@circuitorg/agent-sdk";
577
-
578
- // Agent execution function using the new AgentSdk v1.0 API
579
- const executionFunction: ExecutionFunctionContract = async (request) => {
580
- console.log(`๐Ÿš€ Agent execution called for session ${request.sessionId}`);
581
-
582
- const sdk = new AgentSdk({
583
- sessionId: request.sessionId,
584
- });
408
+ ### Deploy to Circuit
585
409
 
586
- try {
587
- // Get user's wallet address (use vitalik.eth for demo if no address provided)
588
- const userAddress = request.sessionWalletAddress
589
-
590
- await sdk.sendLog({
591
- type: "observe",
592
- shortMessage: `๐Ÿš€ Agent SDK v1.0 demo - Hello ${userAddress}`
593
- });
594
-
595
- return { success: true};
596
- } catch (error) {
597
- await sdk.sendLog({
598
- type: "error",
599
- shortMessage: `Agent execution error: ${error instanceof Error ? error.message : 'Unknown error'}`
600
- });
601
- return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
602
- }
603
- };
410
+ ```bash
411
+ circuit publish
412
+ ```
413
+ > See the circuit cli docs for more details
604
414
 
605
- const stopFunction: StopFunctionContract = async (request) => {
606
- const sdk = new AgentSdk({
607
- sessionId: request.sessionId,
608
- testing: true
609
- });
610
-
611
- try {
612
- await sdk.sendLog({
613
- type: "observe",
614
- shortMessage: "๐Ÿ›‘ Agent stopping with new SDK v1.0"
615
- });
616
- return { success: true };
617
- } catch (error) {
618
- return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
619
- }
620
- };
621
415
 
622
- // DONT MODIFY BELOW THIS
623
- const agent = new Agent({ executionFunction, stopFunction });
416
+ ### Test Locally
624
417
 
625
- // Export the agent for Cloudflare Workers
626
- export default agent.getWorkerExport();
418
+ ## ๐Ÿงช Manual Instantiation (Testing)
627
419
 
628
- ```
420
+ For testing in scripts or Node.js REPL, you can manually create an `AgentContext`:
629
421
 
630
-
631
- ### Sample self sender
632
- >This demonstrates a very basic structure of fetching balances and sending 1% of that amount to yourself. It highlights what stage in the transaction process you will "hand off" the transaction to the circuit infrastructure for signing and sending (broadcasting).
633
422
  ```typescript
634
- import {
635
- Agent,
636
- AgentSdk,
637
- ExecutionFunctionContract,
638
- StopFunctionContract,
639
- } from "@circuitorg/agent-sdk";
640
- import { encodeFunctionData, parseAbi } from "viem";
641
-
642
- export type BalanceResponse = {
643
- /** Raw amount as string (wei, lamports, or token units) */
644
- amount: string;
645
- /** Number of decimals for the asset */
646
- decimals: number;
647
- /** Whether this is a token (true) or native asset (false) */
648
- isToken: boolean;
423
+ import { AgentContext } from "@circuitorg/agent-sdk";
424
+
425
+ // Create agent context with test data
426
+ // Tip: Get a real session ID and wallet from running 'circuit run -m local -x execute'
427
+ const SESSION_ID = 123; // <your-session-id>
428
+ const WALLET_ADDRESS = "your-wallet-address";
429
+
430
+ // Create sample position data - helpful for testing your agents treatment of different scenarios
431
+ // for example if you want to test some re-balancing logic, you can build a sample set of positions here
432
+ // In production, or when using the cli, these will be live values for the session
433
+ const SAMPLE_POSITION = {
434
+ network: "ethereum:137",
435
+ assetAddress: "0x4d97dcd97ec945f40cf65f87097ace5ea0476045",
436
+ tokenId: "86192057611122246511563653509192966169513312957180910360241289053249649036697",
437
+ avgUnitCost: "0.779812797920499100",
438
+ currentQty: "41282044"
649
439
  };
650
440
 
651
- export async function getEvmTokenBalance(params: {
652
- address: string;
653
- token: string;
654
- rpcUrl: string;
655
- }): Promise<BalanceResponse> {
656
- // balanceOf(address) - ERC-20 standard
657
- const data = `0x70a08231${params.address
658
- .replace(/^0x/, "")
659
- .padStart(64, "0")}`;
660
-
661
- const balanceCall = {
662
- jsonrpc: "2.0",
663
- id: 1,
664
- method: "eth_call",
665
- params: [{ to: params.token, data }, "latest"],
666
- } as const;
667
-
668
- const balanceRes = await fetch(params.rpcUrl, {
669
- method: "POST",
670
- headers: { "content-type": "application/json" },
671
- body: JSON.stringify(balanceCall),
672
- });
673
-
674
- const balanceJson = (await balanceRes.json()) as { result?: string };
675
- const balHex = balanceJson.result || "0x0";
676
- const amount = BigInt(balHex);
677
-
678
- // decimals() - ERC-20 standard
679
- const decimalsCall = {
680
- jsonrpc: "2.0",
681
- id: 2,
682
- method: "eth_call",
683
- params: [{ to: params.token, data: "0x313ce567" }, "latest"],
684
- } as const;
685
-
686
- const decimalsRes = await fetch(params.rpcUrl, {
687
- method: "POST",
688
- headers: { "content-type": "application/json" },
689
- body: JSON.stringify(decimalsCall),
690
- });
441
+ const agent = new AgentContext({
442
+ sessionId: SESSION_ID,
443
+ sessionWalletAddress: WALLET_ADDRESS,
444
+ currentPositions: [SAMPLE_POSITION]
445
+ });
691
446
 
692
- const decimalsJson = (await decimalsRes.json()) as { result?: string };
693
- const decimals = decimalsJson.result
694
- ? Number(BigInt(decimalsJson.result))
695
- : 18;
447
+ // Use it just like in production!
448
+ await agent.log("Testing agent functionality!");
449
+ await agent.memory.set("test_key", "test_value");
696
450
 
697
- return {
698
- amount: amount.toString(),
699
- decimals,
700
- isToken: true,
701
- };
451
+ const result = await agent.memory.get("test_key");
452
+ if (result.success && result.data) {
453
+ console.log(`Value: ${result.data.value}`);
702
454
  }
455
+ ```
703
456
 
704
- const toSubscript = (num: number): string => {
705
- const map: Record<string, string> = {
706
- "0": "โ‚€",
707
- "1": "โ‚",
708
- "2": "โ‚‚",
709
- "3": "โ‚ƒ",
710
- "4": "โ‚„",
711
- "5": "โ‚…",
712
- "6": "โ‚†",
713
- "7": "โ‚‡",
714
- "8": "โ‚ˆ",
715
- "9": "โ‚‰",
716
- };
717
- return String(num)
718
- .split("")
719
- .map((c) => map[c] ?? c)
720
- .join("");
721
- };
457
+ **Key Points:**
458
+ - Use real session IDs from the CLI for proper testing
459
+ - All SDK methods work the same way as in production
722
460
 
723
- const formatTokenAmount = (
724
- amount: bigint,
725
- decimals: number,
726
- subscriptDecimals: number = 4,
727
- standardDecimals: number = 6
728
- ): string => {
729
- const sign = amount < 0n ? "-" : "";
730
- const s = (amount < 0n ? -amount : amount).toString();
731
-
732
- const hasFraction = decimals > 0;
733
- const integerPart =
734
- s.length > decimals ? s.slice(0, s.length - decimals) : "0";
735
- const fractional = hasFraction
736
- ? s.length > decimals
737
- ? s.slice(s.length - decimals)
738
- : s.padStart(decimals, "0")
739
- : "";
740
-
741
- const groupedInt = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
742
-
743
- if (!hasFraction) return sign + groupedInt;
744
- if (/^0+$/.test(fractional)) return sign + groupedInt;
745
-
746
- const leadingZeros = fractional.match(/^0+/)?.[0].length ?? 0;
747
-
748
- if (leadingZeros >= 3) {
749
- // Use subscript notation for numbers with 3+ leading zeros
750
- const rest = fractional.slice(leadingZeros);
751
- const truncatedRest = rest.slice(0, subscriptDecimals);
752
- const compressedFrac = `0${toSubscript(leadingZeros)}${truncatedRest}`;
753
- return `${sign}${groupedInt}.${compressedFrac}`;
754
- }
461
+ ## ๐Ÿ“š Working Examples
755
462
 
756
- // For 0-2 leading zeros, use standard decimal formatting
757
- const truncatedFrac = fractional.slice(0, standardDecimals);
758
- const trimmedFrac = truncatedFrac.replace(/0+$/, ""); // Remove trailing zeros
463
+ The SDK includes complete working examples to help you get started:
759
464
 
760
- if (trimmedFrac === "") return sign + groupedInt;
761
- return `${sign}${groupedInt}.${trimmedFrac}`;
762
- };
465
+ ### [`demo-agent.ts`](./examples/demo-agent.ts)
466
+ An agent demonstrating all main features of the sdk:
467
+ - Memory operations (set, get, list, delete)
468
+ - Polymarket integration (market orders, position redemption)
469
+ - Swidge
470
+ - Self sending using sign and send
471
+ - Unified logging patterns
763
472
 
764
- // Agent execution function using the new AgentSdk v1.0 API
765
- const executionFunction: ExecutionFunctionContract = async (request) => {
766
- console.log(`๐Ÿš€ Agent execution called for session ${request.sessionId}`);
473
+ This is a great starting point for building your own agent.
767
474
 
768
- const sdk = new AgentSdk({
769
- sessionId: request.sessionId,
770
- });
771
-
772
- try {
773
- await sdk.sendLog({
774
- type: "observe",
775
- shortMessage:
776
- "๐Ÿš€ Starting Agent SDK v1.0 comprehensive demo -- live test",
777
- });
778
-
779
- const PEPE_ARB = "0x25d887Ce7a35172C62FeBFD67a1856F20FaEbB00" as `0x${string}`;
780
- const pepeBalance = await getEvmTokenBalance({
781
- rpcUrl: "https://arb-mainnet.g.alchemy.com/v2/<YOUR_API_KEY>",
782
- address: request.sessionWalletAddress,
783
- token: PEPE_ARB,
784
- });
785
-
786
- const pepeBalanceFormatted = formatTokenAmount(
787
- BigInt(pepeBalance.amount),
788
- pepeBalance.decimals
789
- );
790
-
791
- await sdk.sendLog({
792
- type: "observe",
793
- shortMessage: `PEPE Balance (Arbitrum): ${pepeBalanceFormatted} PEPE`,
794
- });
795
-
796
-
797
- const abi = parseAbi([
798
- "function transfer(address to, uint256 value) returns (bool)",
799
- ]);
800
- const demoAmount = BigInt(pepeBalance.amount) / 100n;
801
-
802
- const data = encodeFunctionData({
803
- abi,
804
- functionName: "transfer",
805
- args: [request.sessionWalletAddress as `0x${string}`, demoAmount],
806
- });
807
-
808
- // Demo custom contract call
809
- await sdk.signAndSend({
810
- network: "ethereum:42161",
811
- request: {
812
- toAddress: PEPE_ARB,
813
- data: data,
814
- value: "0", // No ETH value for token transfers
815
- },
816
- message: "Custom PEPE transfer demo",
817
- });
818
-
819
-
820
- await sdk.sendLog({
821
- type: "observe",
822
- shortMessage:
823
- "โœ… Agent SDK v1.0 comprehensive demo completed successfully!",
824
- });
825
-
826
- return { success: true };
827
- } catch (error) {
828
- console.error("๐Ÿ’ฅ Agent execution error:", error);
829
-
830
- await sdk.sendLog({
831
- type: "error",
832
- shortMessage: `Agent execution error: ${
833
- error instanceof Error ? error.message : "Unknown error"
834
- }`,
835
- });
836
-
837
- // Optional - Return detailed error information for the CLI to display
838
- return {
839
- success: false,
840
- error: error instanceof Error ? error.message : "Unknown error",
841
- errorDetails: {
842
- name: error instanceof Error ? error.name : "Unknown",
843
- message: error instanceof Error ? error.message : String(error),
844
- stack: error instanceof Error ? error.stack : undefined,
845
- timestamp: new Date().toISOString(),
846
- sessionId: request.sessionId,
847
- agentSlug: (globalThis as any).CIRCUIT_AGENT_SLUG || "unknown",
848
- errorType: "AGENT_EXECUTION_ERROR",
849
- },
850
- };
851
- }
852
- };
475
+ ### [`examples/kitchen-sink.ts`](./examples/kitchen-sink.ts)
476
+ A comprehensive script showing all SDK features:
477
+ - Manual AgentContext instantiation for testing
478
+ - All memory operations with examples
479
+ - Cross-chain swaps with Swidge (quotes, execution, price impact checks)
480
+ - Polymarket operations (buy/sell orders, redemptions)
481
+ - Custom transactions with signAndSend
482
+ - Logging patterns (standard, error, debug)
853
483
 
854
- const stopFunction: StopFunctionContract = async (request) => {
855
- const sdk = new AgentSdk({
856
- sessionId: request.sessionId,
857
- testing: true,
858
- });
484
+ Run this script to experiment with the SDK before deploying your agent.
859
485
 
860
- try {
861
- await sdk.sendLog({
862
- type: "observe",
863
- shortMessage: "๐Ÿ›‘ Agent stopping with new SDK v1.0",
864
- });
865
- return { success: true };
866
- } catch (error) {
867
- console.error("๐Ÿ’ฅ Agent stop error:", error);
868
-
869
- return {
870
- success: false,
871
- error: error instanceof Error ? error.message : "Unknown error",
872
- errorDetails: {
873
- name: error instanceof Error ? error.name : "Unknown",
874
- message: error instanceof Error ? error.message : String(error),
875
- stack: error instanceof Error ? error.stack : undefined,
876
- timestamp: new Date().toISOString(),
877
- sessionId: request.sessionId,
878
- agentSlug: (globalThis as any).CIRCUIT_AGENT_SLUG || "unknown",
879
- },
880
- };
881
- }
882
- };
486
+ ## ๐ŸŽฏ Key Takeaways
883
487
 
884
- // Create the agent
885
- const agent = new Agent({ executionFunction, stopFunction });
488
+ 1. **Single Interface**: Everything you need is in the `AgentContext` object
489
+ 2. **No Return Values**: Functions return `void` - uncaught errors are handled automatically by circuit
490
+ 3. **Unified Logging**: `agent.log()` with simple `debug` and `error` flags
491
+ 4. **Check `.success`**: All SDK methods return response objects with `.success` and `.error_message`
886
492
 
887
- // Export the agent for both Bun local development and Cloudflare Workers
888
- export default agent.getWorkerExport();
493
+ ## ๐Ÿ“– Additional Resources
889
494
 
890
- ```
495
+ - [Circuit CLI](https://github.com/circuitorg/agents-cli) - Deploy and manage agents
496
+ - [`demo-agent.ts`](./examples/demo-agent.ts) - Production-ready agent template
497
+ - [`examples/kitchen-sink.ts`](./examples/kitchen-sink.ts) - Comprehensive SDK demo