@circuitorg/agent-sdk 1.1.8 β†’ 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 +360 -667
  2. package/index.d.ts +1100 -516
  3. package/index.js +1 -1
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -1,30 +1,45 @@
1
- # Circuit Agent SDK - Typescript
1
+ # Circuit Agent SDK - TypeScript
2
2
 
3
+ > **Clean, unified, type-safe TypeScript SDK for building cross-chain agents on Circuit**
3
4
 
4
- A TypeScript SDK for building automated agents to deploy on Circuit. Features **2 low-level core methods** for maximum flexibility, plus **built-in platform integrations** for common on-chain operationsβ€”all 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.
5
6
 
6
7
  > **πŸ’‘ Best used with [Circuit Agents CLI](https://github.com/circuitorg/agents-cli)** - Deploy, manage, and test your agents with ease
7
8
 
8
9
  ## πŸ“‘ Table of Contents
9
10
 
10
- - [Features](#-features)
11
- - [Quick Start](#-quick-start)
12
- - [Core SDK API](#-core-sdk-api-only-2-methods) - Low-level transaction & event logging methods
13
- - [Cross-Chain Swaps with Swidge](#-cross-chain-swaps-with-swidge) - Token swaps & bridges
14
- - [Polymarket Prediction Markets](#-polymarket-prediction-markets) - Trading integration
15
- - [Session Memory Storage](#-session-memory-storage) - State management
16
- - [Examples](#-examples)
17
-
18
- ## ✨ Features
19
-
20
- - **🎯 Low-Level Core API**: 2 foundational methods (`sendLog()`, `signAndSend()`) for sending custom transactions
21
- - **πŸ”’ Type Safety**: Network parameter determines valid request shapes automatically
22
- - **πŸš€ Cross-Chain**: Unified interface for EVM and Solana networks
23
- - **πŸŒ‰ Cross-Chain Swaps**: Built-in Swidge integration for seamless token swaps and bridges
24
- - **πŸ“ˆ Polymarket Integration**: Trade prediction markets with `sdk.polymarket.*` methods
25
- - **πŸ’Ύ Session Memory**: Key-value storage with `sdk.memory.*` methods for maintaining state
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)
26
40
 
27
41
  ## πŸš€ Quick Start
42
+
28
43
  ### Install the SDK
29
44
  ```bash
30
45
  npm install @circuitorg/agent-sdk
@@ -34,771 +49,449 @@ yarn add @circuitorg/agent-sdk
34
49
  bun add @circuitorg/agent-sdk
35
50
  ```
36
51
 
37
- ### Sample SDK Usage
38
- >**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
39
53
 
40
- ```typescript
41
- import { AgentSdk } from "@circuitorg/agent-sdk";
54
+ Every agent receives a single `AgentContext` object that provides:
42
55
 
43
- // Initialize the sdk
44
- const sdk = new AgentSdk({
45
- sessionId: 123,
46
- });
47
- ```
48
-
49
- ## 🎯 Core SDK API (Only 2 Methods!)
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
50
60
 
51
- > **Low-level building blocks** for direct blockchain interaction. Use these for maximum flexibility, or leverage our higher-level integrations (Swidge, Polymarket, Memory) for common operations.
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)
52
68
 
53
- ### 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.
54
70
 
55
71
  ```typescript
56
- await sdk.sendLog({
57
- type: "observe",
58
- shortMessage: "Starting swap operation"
59
- });
60
- ```
61
-
62
- ### 2. Sign & Send Transactions
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
+ }
63
90
 
64
- #### Ethereum (any EVM chain)
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
- ```typescript
67
- // Native ETH transfer
68
- await sdk.signAndSend({
69
- network: "ethereum:1", // Chain ID in network string
70
- request: {
71
- toAddress: "0x742d35cc6634C0532925a3b8D65e95f32B6b5582" as `0x${string}`,
72
- data: "0x" as `0x${string}`,
73
- value: "1000000000000000000" // 1 ETH in wei
74
- },
75
- message: "Sending 1 ETH"
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
76
101
  });
77
102
 
78
- // Contract interaction
79
- await sdk.signAndSend({
80
- network: "ethereum:42161", // Arbitrum
81
- request: {
82
- toAddress: "0xTokenContract..." as `0x${string}`,
83
- data: "0xa9059cbb..." as `0x${string}`, // encoded transfer()
84
- value: "0"
85
- }
86
- });
103
+ export default agent.getExport();
87
104
  ```
88
105
 
89
- #### Solana
106
+ ## 🎯 Core Concepts
90
107
 
91
- ```typescript
92
- await sdk.signAndSend({
93
- network: "solana",
94
- request: {
95
- hexTransaction: "010001030a0b..." // serialized VersionedTransaction
96
- }
97
- });
98
- ```
108
+ ### The AgentContext Object
109
+
110
+ Every agent function receives a single `AgentContext` object that contains:
99
111
 
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
100
116
 
101
- ## πŸŒ‰ Cross-Chain Swaps and Bridging with Swidge
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
102
124
 
103
- > **High-level abstraction** built on core SDK methods. Simplifies cross-chain token operations with a quote-and-execute pattern.
125
+ ### Run/Stop Function Requirements
104
126
 
105
- The SDK includes built-in Swidge integration for seamless cross-chain token swaps and bridges. 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 API. Whether you're doing a simple token swap on Ethereum or bridging assets across chains, the same pattern works for everything.
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.
106
130
 
107
- ### Cross-Chain Swaps & Bridges
131
+ ## πŸ“ Unified Logging with agent.log()
108
132
 
109
- #### Get a Quote
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.
110
134
 
111
135
  ```typescript
112
- // πŸŒ‰ Bridge USDC: Polygon β†’ Arbitrum
113
- const bridgeQuote = await sdk.swidge.quote({
114
- from: { network: "ethereum:137", address: request.sessionWalletAddress },
115
- to: { network: "ethereum:42161", address: request.sessionWalletAddress },
116
- amount: "50000000", // $50 USDC (6 decimals)
117
- fromToken: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", // USDC on Polygon
118
- toToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC on Arbitrum
119
- slippage: "2.0", // 2% slippage for cross-chain (default: 0.5%)
120
- priceImpact: "1.0" // 1% max price impact (default: 0.5%)
121
- });
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
+ }
122
145
 
123
- // πŸ”„ Swap USDC β†’ ETH on same chain (using defaults)
124
- const swapQuote = await sdk.swidge.quote({
125
- from: { network: "ethereum:42161", address: request.sessionWalletAddress },
126
- to: { network: "ethereum:42161", address: request.sessionWalletAddress },
127
- amount: "100000000", // $100 USDC (6 decimals)
128
- fromToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC
129
- // toToken omitted = native ETH (default behavior)
130
- // slippage defaults to "0.5", priceImpact defaults to "0.5"
131
- });
146
+ // Debug log: Only you see this in your terminal
147
+ await agent.log("Internal state: processing...", { debug: true });
132
148
 
133
- if (quote.success) {
134
- console.log(`πŸ’° You'll receive: ${quote.data.assetReceive.amountFormatted}`);
135
- console.log(`πŸ’Έ Total fees: ${quote.data.fees.map(f => f.name).join(", ")}`);
136
- } else if (quote.error) {
137
- // Check for specific error types
138
- if (quote.error === QUOTE_RESULT.WALLET_NOT_FOUND) {
139
- console.log("πŸ‘› Wallet not found");
140
- } else if (quote.error === QUOTE_RESULT.WALLET_MISMATCH) {
141
- console.log("πŸ” Wallet address doesn't match session");
142
- } else {
143
- console.log("❓ Quote not available for this swap");
149
+ // Logging objects
150
+ // Objects are pretty-printed to console and serialized/truncated for backend
151
+ await agent.log({ wallet: agent.sessionWalletAddress, status: "active" });
152
+
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
144
157
  }
145
158
  }
146
159
  ```
147
160
 
148
- #### Execute a Swap
161
+ **What Your Users See:**
149
162
 
150
- ```typescript
151
- // 1️⃣ Get a quote first
152
- let quoteRequest = {
153
- from: { network: "ethereum:42161", address: request.sessionWalletAddress },
154
- to: { network: "ethereum:1", address: request.sessionWalletAddress },
155
- amount: "100000000", // $100 USDC
156
- fromToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC on Arbitrum
157
- priceImpact: "0.1", // Conservative price impact setting
158
- slippage: "5.0"
159
- };
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 |
160
169
 
161
- let quote = await sdk.swidge.quote(quoteRequest);
170
+ ## πŸ’Ύ Session Memory Storage
162
171
 
163
- // 2️⃣ Handle quote failures with retry logic
164
- if (quote.error === QUOTE_RESULT.NO_QUOTE_PROVIDED) {
165
- console.log(`❓ Quote not available, increasing price impact and retrying...`)
166
- // Retry with more permissive parameters
167
- quoteRequest.priceImpact = "10.0";
168
- quoteRequest.slippage = "10.0";
169
- quote = await sdk.swidge.quote(quoteRequest);
170
- }
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.
171
173
 
172
- // 3️⃣ Execute the swap if quote succeeded
173
- if (quote.success && quote.data) {
174
- console.log(`πŸ’° Expected to receive: ${quote.data.assetReceive.amountFormatted}`);
175
- console.log(`πŸ’Έ Fees: ${quote.data.fees.map(f => `${f.name}: ${f.amountFormatted}`).join(", ")}`);
176
-
177
- const result = await sdk.swidge.execute(quote.data);
178
-
179
- if (result.success && result.data) {
180
- console.log(`πŸŽ‰ Status: ${result.data.status}`);
181
-
182
- if (result.data.status === "success") {
183
- console.log(`πŸ“€ Sent: ${result.data.in.txs[0]}`);
184
- console.log(`πŸ“₯ Received: ${result.data.out.txs[0]}`);
185
- console.log("βœ… Cross-chain swap completed!");
186
- } else if (result.data.status === "failure") {
187
- console.log("❌ Transaction failed");
188
- } else if (result.data.status === "refund") {
189
- console.log("↩️ Transaction was refunded");
190
- } else if (result.data.status === "delayed") {
191
- console.log("⏰ Transaction is delayed");
192
- }
174
+ ```typescript
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 });
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}`);
193
187
  } else {
194
- console.log(`❌ Execute failed: ${result.error}`);
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
195
  }
196
- } else {
197
- console.log(`❌ Quote failed after retry: ${quote.error}`);
198
- return { success: false };
196
+
197
+ // Delete a key
198
+ await agent.memory.delete("tempData");
199
199
  }
200
200
  ```
201
201
 
202
- ## πŸ“ˆ Polymarket Prediction Markets
203
-
204
- > **High-level abstraction** built on core SDK methods. Simplifies prediction market trading on Polygon.
202
+ **All memory operations return responses with `.success` and `.error_message`:**
205
203
 
206
- > **Note:** Due to polymarket accepting buys and sells with different decimal precision, you may end up with dust positions when trying to sell to close out a position.
204
+ ```typescript
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 });
208
+ }
209
+ ```
207
210
 
211
+ ## πŸŒ‰ Cross-Chain Swaps with Swidge
208
212
 
209
- The SDK includes built-in Polymarket integration for trading prediction markets on Polygon.
213
+ Built-in Swidge integration for seamless cross-chain token swaps and bridges.
210
214
 
211
- ### Polymarket Trading
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
212
217
 
213
- #### Get Positions
218
+ ```typescript
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
+ });
214
229
 
215
- Fetch all current positions for the session wallet:
230
+ if (!quote.success) {
231
+ await agent.log(`Quote failed: ${quote.error_message}`, { error: true });
232
+ return;
233
+ }
216
234
 
217
- ```typescript
218
- const positions = await sdk.polymarket.positions();
219
-
220
- if (positions.success && positions.data) {
221
- console.log(`Total value: $${positions.data.totalValue}`);
222
-
223
- for (const position of positions.data.positions) {
224
- console.log(`${position.question} (${position.outcome})`);
225
- console.log(` Shares: ${position.formattedShares}`);
226
- console.log(` Value: $${position.valueUsd}`);
227
- console.log(` P&L: $${position.pnlUsd} (${position.pnlPercent}%)`);
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
+ }
228
256
  }
229
257
  }
230
258
  ```
231
259
 
232
- #### Place Market Orders
260
+ ## πŸ“ˆ Polymarket Prediction Markets
261
+
262
+ Trade prediction markets on Polygon.
233
263
 
234
- Execute buy or sell market orders:
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.
235
266
 
236
267
  ```typescript
237
- // Buy order - size is USD amount to spend
238
- const buyOrder = await sdk.polymarket.marketOrder({
239
- tokenId: "123456789...", // Market token ID
240
- size: 10, // Spend $10 to buy shares
241
- side: "BUY"
242
- });
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
+ });
243
275
 
244
- // Sell order - size is number of shares to sell
245
- const sellOrder = await sdk.polymarket.marketOrder({
246
- tokenId: "123456789...",
247
- size: 5.5, // Sell 5.5 shares
248
- side: "SELL"
249
- });
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 });
282
+ }
250
283
 
251
- if (buyOrder.success && buyOrder.data) {
252
- console.log(`Order Success: ${buyOrder.data.success}`);
253
- console.log(`Order ID: ${buyOrder.data.orderInfo.orderId}`);
254
- console.log(`Filled at $${buyOrder.data.orderInfo.priceUsd} per share`);
255
- console.log(`Total cost: $${buyOrder.data.orderInfo.totalPriceUsd}`);
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
+ });
256
290
  }
257
291
  ```
258
292
 
259
- #### Redeem Positions
260
-
261
- Claim winnings from settled positions:
293
+ ### Redeem Positions
262
294
 
263
295
  ```typescript
264
- // Redeem all redeemable positions
265
- const redemption = await sdk.polymarket.redeemPositions();
266
-
267
- if (redemption.success && redemption.data) {
268
- for (const result of redemption.data) {
269
- if (result.success) {
270
- if (result.position) {
271
- console.log(`βœ… Redeemed: ${result.position.question}`);
272
- } else {
273
- console.log(`βœ… Unwrapped collateral`);
274
- }
275
- console.log(` TX: ${result.transactionHash}`);
276
- }
296
+ async function stop(agent: AgentContext): Promise<void> {
297
+ /**Redeem all settled positions.*/
298
+ const redemption = await agent.platforms.polymarket.redeemPositions();
299
+
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 });
277
305
  }
278
306
  }
279
-
280
- // Redeem specific positions by token IDs
281
- const specificRedemption = await sdk.polymarket.redeemPositions({
282
- tokenIds: ["123456", "789012"]
283
- });
284
307
  ```
285
308
 
286
- ### Complete Polymarket Example
309
+ ## πŸš€ Sign & Send Transactions
310
+
311
+ ### Ethereum (any EVM chain)
287
312
 
288
313
  ```typescript
289
- const executionFunction: ExecutionFunctionContract = async (request) => {
290
- const sdk = new AgentSdk({ sessionId: request.sessionId });
291
-
292
- try {
293
- // Get current positions
294
- const positions = await sdk.polymarket.positions();
295
-
296
- if (!positions.success || !positions.data) {
297
- throw new Error(`Failed to get positions: ${positions.error}`);
298
- }
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
+ });
299
325
 
300
- // Find specific position
301
- const targetPosition = positions.data.positions.find(
302
- p => p.tokenId === "YOUR_TOKEN_ID"
303
- );
304
-
305
- if (targetPosition && Number(targetPosition.formattedShares) > 0) {
306
- // Sell position
307
- const sellOrder = await sdk.polymarket.marketOrder({
308
- tokenId: targetPosition.tokenId,
309
- size: Number(targetPosition.formattedShares),
310
- side: "SELL"
311
- });
312
-
313
- if (sellOrder.success && sellOrder.data?.orderInfo) {
314
- await sdk.sendLog({
315
- type: "observe",
316
- shortMessage: `Sold ${targetPosition.outcome} for $${sellOrder.data.orderInfo.totalPriceUsd}`
317
- });
318
- }
326
+ if (response.success) {
327
+ await agent.log(`Transaction sent: ${response.txHash}`);
328
+ if (response.transactionUrl) {
329
+ await agent.log(`View: ${response.transactionUrl}`);
319
330
  }
320
-
321
- return { success: true };
322
- } catch (error) {
323
- await sdk.sendLog({
324
- type: "error",
325
- shortMessage: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`
326
- });
327
- return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
331
+ } else {
332
+ await agent.log(response.error_message || response.error, { error: true });
328
333
  }
329
- };
330
-
331
- const stopFunction: StopFunctionContract = async (request) => {
332
- const sdk = new AgentSdk({ sessionId: request.sessionId });
334
+ }
335
+ ```
333
336
 
334
- try {
335
- // Redeem all settled positions on stop
336
- const redemption = await sdk.polymarket.redeemPositions();
337
+ ### Solana
337
338
 
338
- if (redemption.success && redemption.data) {
339
- const successful = redemption.data.filter(r => r.success);
340
- await sdk.sendLog({
341
- type: "observe",
342
- shortMessage: `βœ… Redeemed ${successful.length} positions`
343
- });
339
+ ```typescript
340
+ async function run(agent: AgentContext): Promise<void> {
341
+ const response = await agent.signAndSend({
342
+ network: "solana",
343
+ request: {
344
+ hexTransaction: "010001030a0b..." // serialized VersionedTransaction
344
345
  }
346
+ });
345
347
 
346
- return { success: true };
347
- } catch (error) {
348
- return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
348
+ if (response.success) {
349
+ await agent.log(`Transaction: ${response.txHash}`);
350
+ } else {
351
+ await agent.log(response.error_message || response.error, { error: true });
349
352
  }
350
- };
353
+ }
351
354
  ```
352
355
 
353
- ## πŸ’Ύ Session Memory Storage
356
+ ## ⚑ Error Handling
354
357
 
355
- > **High-level abstraction** built on core SDK methods. Provides key-value storage for maintaining state across execution cycles without external databases.
358
+ **All SDK methods return response objects with `.success` and `.error_message`:**
356
359
 
357
- Store and retrieve data for your agent's session with simple get/set/delete/list operations. Memory is **automatically scoped to your session ID** - each session has isolated storage that persists across execution cycles.
358
360
 
359
- ### Memory Operations
361
+ **Uncaught Exceptions:**
360
362
 
361
- #### Set a Value
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.
362
366
 
363
367
  ```typescript
364
- // Store user preferences
365
- const result = await sdk.memory.set("lastSwapNetwork", "ethereum:42161");
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
366
371
 
367
- if (result.success && result.data) {
368
- console.log(`Stored key: ${result.data.key}`);
369
- } else {
370
- console.error(`Failed to store: ${result.error}`);
372
+ // No need for try/catch around everything!
371
373
  }
372
374
  ```
373
375
 
374
- #### Get a Value
375
-
376
- ```typescript
377
- // Retrieve stored preferences
378
- const result = await sdk.memory.get("lastSwapNetwork");
376
+ ## πŸ› οΈ Deployment
379
377
 
380
- if (result.success && result.data) {
381
- console.log(`Network: ${result.data.value}`);
382
- } else {
383
- console.log(`Key not found: ${result.error}`);
384
- }
385
- ```
378
+ ### What You Write
386
379
 
387
- #### Delete a Value
380
+ Your agent code should look like this - just define your functions and add the boilerplate at the bottom:
388
381
 
389
382
  ```typescript
390
- // Clean up temporary data
391
- const result = await sdk.memory.delete("tempSwapQuote");
383
+ import { Agent, AgentContext } from "@circuitorg/agent-sdk";
392
384
 
393
- if (result.success && result.data) {
394
- console.log(`Deleted key: ${result.data.key}`);
385
+ async function run(agent: AgentContext): Promise<void> {
386
+ await agent.log("Hello from my agent!");
387
+ // Your agent logic here
395
388
  }
396
- ```
397
-
398
- #### List All Keys
399
389
 
400
- ```typescript
401
- // List all stored keys
402
- const result = await sdk.memory.list();
403
-
404
- if (result.success && result.data) {
405
- console.log(`Found ${result.data.count} keys:`);
406
- result.data.keys.forEach(key => console.log(` - ${key}`));
390
+ async function stop(agent: AgentContext): Promise<void> {
391
+ await agent.log("Cleaning up...");
392
+ // Optional cleanup logic
407
393
  }
408
- ```
409
-
410
- ### Complete Memory Example
411
394
 
412
- ```typescript
413
- const executionFunction: ExecutionFunctionContract = async (request) => {
414
- const sdk = new AgentSdk({ sessionId: request.sessionId });
415
-
416
- try {
417
- // Check if this is a first run by looking for a stored counter
418
- const counterResult = await sdk.memory.get("executionCount");
419
-
420
- let count = 0;
421
- if (counterResult.success && counterResult.data) {
422
- count = parseInt(counterResult.data.value);
423
- await sdk.sendLog({
424
- type: "observe",
425
- shortMessage: `Execution #${count + 1} - Welcome back!`
426
- });
427
- } else {
428
- await sdk.sendLog({
429
- type: "observe",
430
- shortMessage: "First execution - initializing..."
431
- });
432
- }
433
-
434
- // Store some session data
435
- await sdk.memory.set("executionCount", String(count + 1));
436
- await sdk.memory.set("lastRunTimestamp", Date.now().toString());
437
- await sdk.memory.set("userPreferences", JSON.stringify({
438
- network: "ethereum:42161",
439
- slippage: "2.0"
440
- }));
441
-
442
- // List all stored keys
443
- const listResult = await sdk.memory.list();
444
- if (listResult.success && listResult.data) {
445
- await sdk.sendLog({
446
- type: "observe",
447
- shortMessage: `Stored ${listResult.data.count} keys: ${listResult.data.keys.join(", ")}`
448
- });
449
- }
450
-
451
- return { success: true };
452
- } catch (error) {
453
- await sdk.sendLog({
454
- type: "error",
455
- shortMessage: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`
456
- });
457
- return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
458
- }
459
- };
460
-
461
- const stopFunction: StopFunctionContract = async (request) => {
462
- const sdk = new AgentSdk({ sessionId: request.sessionId });
463
-
464
- try {
465
- // Clean up temporary data on stop
466
- const listResult = await sdk.memory.list();
467
- if (listResult.success && listResult.data) {
468
- // Delete all temp keys
469
- for (const key of listResult.data.keys) {
470
- if (key.startsWith("temp_")) {
471
- await sdk.memory.delete(key);
472
- }
473
- }
474
- }
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
+ });
475
402
 
476
- return { success: true };
477
- } catch (error) {
478
- return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
479
- }
480
- };
403
+ export default agent.getExport();
481
404
  ```
482
405
 
406
+ That's it! The boilerplate code automatically handles the rest
483
407
 
484
- ## πŸ”§ Examples
485
-
486
- ### Barebones Agent
487
- >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.
408
+ ### Deploy to Circuit
488
409
 
489
- ```typescript
490
- import { Agent, AgentSdk, ExecutionFunctionContract, StopFunctionContract } from "@circuitorg/agent-sdk";
491
-
492
- // Agent execution function using the new AgentSdk v1.0 API
493
- const executionFunction: ExecutionFunctionContract = async (request) => {
494
- console.log(`πŸš€ Agent execution called for session ${request.sessionId}`);
495
-
496
- const sdk = new AgentSdk({
497
- sessionId: request.sessionId,
498
- });
499
-
500
- try {
501
- // Get user's wallet address (use vitalik.eth for demo if no address provided)
502
- const userAddress = request.sessionWalletAddress
503
-
504
- await sdk.sendLog({
505
- type: "observe",
506
- shortMessage: `πŸš€ Agent SDK v1.0 demo - Hello ${userAddress}`
507
- });
508
-
509
- return { success: true};
510
- } catch (error) {
511
- await sdk.sendLog({
512
- type: "error",
513
- shortMessage: `Agent execution error: ${error instanceof Error ? error.message : 'Unknown error'}`
514
- });
515
- return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
516
- }
517
- };
518
-
519
- const stopFunction: StopFunctionContract = async (request) => {
520
- const sdk = new AgentSdk({
521
- sessionId: request.sessionId,
522
- testing: true
523
- });
524
-
525
- try {
526
- await sdk.sendLog({
527
- type: "observe",
528
- shortMessage: "πŸ›‘ Agent stopping with new SDK v1.0"
529
- });
530
- return { success: true };
531
- } catch (error) {
532
- return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
533
- }
534
- };
410
+ ```bash
411
+ circuit publish
412
+ ```
413
+ > See the circuit cli docs for more details
535
414
 
536
- // DONT MODIFY BELOW THIS
537
- const agent = new Agent({ executionFunction, stopFunction });
538
415
 
539
- // Export the agent for Cloudflare Workers
540
- export default agent.getWorkerExport();
416
+ ### Test Locally
541
417
 
542
- ```
418
+ ## πŸ§ͺ Manual Instantiation (Testing)
543
419
 
420
+ For testing in scripts or Node.js REPL, you can manually create an `AgentContext`:
544
421
 
545
- ### Sample self sender
546
- >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).
547
422
  ```typescript
548
- import {
549
- Agent,
550
- AgentSdk,
551
- ExecutionFunctionContract,
552
- StopFunctionContract,
553
- } from "@circuitorg/agent-sdk";
554
- import { encodeFunctionData, parseAbi } from "viem";
555
-
556
- export type BalanceResponse = {
557
- /** Raw amount as string (wei, lamports, or token units) */
558
- amount: string;
559
- /** Number of decimals for the asset */
560
- decimals: number;
561
- /** Whether this is a token (true) or native asset (false) */
562
- 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"
563
439
  };
564
440
 
565
- export async function getEvmTokenBalance(params: {
566
- address: string;
567
- token: string;
568
- rpcUrl: string;
569
- }): Promise<BalanceResponse> {
570
- // balanceOf(address) - ERC-20 standard
571
- const data = `0x70a08231${params.address
572
- .replace(/^0x/, "")
573
- .padStart(64, "0")}`;
574
-
575
- const balanceCall = {
576
- jsonrpc: "2.0",
577
- id: 1,
578
- method: "eth_call",
579
- params: [{ to: params.token, data }, "latest"],
580
- } as const;
581
-
582
- const balanceRes = await fetch(params.rpcUrl, {
583
- method: "POST",
584
- headers: { "content-type": "application/json" },
585
- body: JSON.stringify(balanceCall),
586
- });
587
-
588
- const balanceJson = (await balanceRes.json()) as { result?: string };
589
- const balHex = balanceJson.result || "0x0";
590
- const amount = BigInt(balHex);
591
-
592
- // decimals() - ERC-20 standard
593
- const decimalsCall = {
594
- jsonrpc: "2.0",
595
- id: 2,
596
- method: "eth_call",
597
- params: [{ to: params.token, data: "0x313ce567" }, "latest"],
598
- } as const;
599
-
600
- const decimalsRes = await fetch(params.rpcUrl, {
601
- method: "POST",
602
- headers: { "content-type": "application/json" },
603
- body: JSON.stringify(decimalsCall),
604
- });
441
+ const agent = new AgentContext({
442
+ sessionId: SESSION_ID,
443
+ sessionWalletAddress: WALLET_ADDRESS,
444
+ currentPositions: [SAMPLE_POSITION]
445
+ });
605
446
 
606
- const decimalsJson = (await decimalsRes.json()) as { result?: string };
607
- const decimals = decimalsJson.result
608
- ? Number(BigInt(decimalsJson.result))
609
- : 18;
447
+ // Use it just like in production!
448
+ await agent.log("Testing agent functionality!");
449
+ await agent.memory.set("test_key", "test_value");
610
450
 
611
- return {
612
- amount: amount.toString(),
613
- decimals,
614
- isToken: true,
615
- };
451
+ const result = await agent.memory.get("test_key");
452
+ if (result.success && result.data) {
453
+ console.log(`Value: ${result.data.value}`);
616
454
  }
455
+ ```
617
456
 
618
- const toSubscript = (num: number): string => {
619
- const map: Record<string, string> = {
620
- "0": "β‚€",
621
- "1": "₁",
622
- "2": "β‚‚",
623
- "3": "₃",
624
- "4": "β‚„",
625
- "5": "β‚…",
626
- "6": "₆",
627
- "7": "₇",
628
- "8": "β‚ˆ",
629
- "9": "₉",
630
- };
631
- return String(num)
632
- .split("")
633
- .map((c) => map[c] ?? c)
634
- .join("");
635
- };
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
636
460
 
637
- const formatTokenAmount = (
638
- amount: bigint,
639
- decimals: number,
640
- subscriptDecimals: number = 4,
641
- standardDecimals: number = 6
642
- ): string => {
643
- const sign = amount < 0n ? "-" : "";
644
- const s = (amount < 0n ? -amount : amount).toString();
645
-
646
- const hasFraction = decimals > 0;
647
- const integerPart =
648
- s.length > decimals ? s.slice(0, s.length - decimals) : "0";
649
- const fractional = hasFraction
650
- ? s.length > decimals
651
- ? s.slice(s.length - decimals)
652
- : s.padStart(decimals, "0")
653
- : "";
654
-
655
- const groupedInt = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
656
-
657
- if (!hasFraction) return sign + groupedInt;
658
- if (/^0+$/.test(fractional)) return sign + groupedInt;
659
-
660
- const leadingZeros = fractional.match(/^0+/)?.[0].length ?? 0;
661
-
662
- if (leadingZeros >= 3) {
663
- // Use subscript notation for numbers with 3+ leading zeros
664
- const rest = fractional.slice(leadingZeros);
665
- const truncatedRest = rest.slice(0, subscriptDecimals);
666
- const compressedFrac = `0${toSubscript(leadingZeros)}${truncatedRest}`;
667
- return `${sign}${groupedInt}.${compressedFrac}`;
668
- }
461
+ ## πŸ“š Working Examples
669
462
 
670
- // For 0-2 leading zeros, use standard decimal formatting
671
- const truncatedFrac = fractional.slice(0, standardDecimals);
672
- const trimmedFrac = truncatedFrac.replace(/0+$/, ""); // Remove trailing zeros
463
+ The SDK includes complete working examples to help you get started:
673
464
 
674
- if (trimmedFrac === "") return sign + groupedInt;
675
- return `${sign}${groupedInt}.${trimmedFrac}`;
676
- };
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
677
472
 
678
- // Agent execution function using the new AgentSdk v1.0 API
679
- const executionFunction: ExecutionFunctionContract = async (request) => {
680
- console.log(`πŸš€ Agent execution called for session ${request.sessionId}`);
473
+ This is a great starting point for building your own agent.
681
474
 
682
- const sdk = new AgentSdk({
683
- sessionId: request.sessionId,
684
- });
685
-
686
- try {
687
- await sdk.sendLog({
688
- type: "observe",
689
- shortMessage:
690
- "πŸš€ Starting Agent SDK v1.0 comprehensive demo -- live test",
691
- });
692
-
693
- const PEPE_ARB = "0x25d887Ce7a35172C62FeBFD67a1856F20FaEbB00" as `0x${string}`;
694
- const pepeBalance = await getEvmTokenBalance({
695
- rpcUrl: "https://arb-mainnet.g.alchemy.com/v2/<YOUR_API_KEY>",
696
- address: request.sessionWalletAddress,
697
- token: PEPE_ARB,
698
- });
699
-
700
- const pepeBalanceFormatted = formatTokenAmount(
701
- BigInt(pepeBalance.amount),
702
- pepeBalance.decimals
703
- );
704
-
705
- await sdk.sendLog({
706
- type: "observe",
707
- shortMessage: `PEPE Balance (Arbitrum): ${pepeBalanceFormatted} PEPE`,
708
- });
709
-
710
-
711
- const abi = parseAbi([
712
- "function transfer(address to, uint256 value) returns (bool)",
713
- ]);
714
- const demoAmount = BigInt(pepeBalance.amount) / 100n;
715
-
716
- const data = encodeFunctionData({
717
- abi,
718
- functionName: "transfer",
719
- args: [request.sessionWalletAddress as `0x${string}`, demoAmount],
720
- });
721
-
722
- // Demo custom contract call
723
- await sdk.signAndSend({
724
- network: "ethereum:42161",
725
- request: {
726
- toAddress: PEPE_ARB,
727
- data: data,
728
- value: "0", // No ETH value for token transfers
729
- },
730
- message: "Custom PEPE transfer demo",
731
- });
732
-
733
-
734
- await sdk.sendLog({
735
- type: "observe",
736
- shortMessage:
737
- "βœ… Agent SDK v1.0 comprehensive demo completed successfully!",
738
- });
739
-
740
- return { success: true };
741
- } catch (error) {
742
- console.error("πŸ’₯ Agent execution error:", error);
743
-
744
- await sdk.sendLog({
745
- type: "error",
746
- shortMessage: `Agent execution error: ${
747
- error instanceof Error ? error.message : "Unknown error"
748
- }`,
749
- });
750
-
751
- // Optional - Return detailed error information for the CLI to display
752
- return {
753
- success: false,
754
- error: error instanceof Error ? error.message : "Unknown error",
755
- errorDetails: {
756
- name: error instanceof Error ? error.name : "Unknown",
757
- message: error instanceof Error ? error.message : String(error),
758
- stack: error instanceof Error ? error.stack : undefined,
759
- timestamp: new Date().toISOString(),
760
- sessionId: request.sessionId,
761
- agentSlug: (globalThis as any).CIRCUIT_AGENT_SLUG || "unknown",
762
- errorType: "AGENT_EXECUTION_ERROR",
763
- },
764
- };
765
- }
766
- };
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)
767
483
 
768
- const stopFunction: StopFunctionContract = async (request) => {
769
- const sdk = new AgentSdk({
770
- sessionId: request.sessionId,
771
- testing: true,
772
- });
484
+ Run this script to experiment with the SDK before deploying your agent.
773
485
 
774
- try {
775
- await sdk.sendLog({
776
- type: "observe",
777
- shortMessage: "πŸ›‘ Agent stopping with new SDK v1.0",
778
- });
779
- return { success: true };
780
- } catch (error) {
781
- console.error("πŸ’₯ Agent stop error:", error);
782
-
783
- return {
784
- success: false,
785
- error: error instanceof Error ? error.message : "Unknown error",
786
- errorDetails: {
787
- name: error instanceof Error ? error.name : "Unknown",
788
- message: error instanceof Error ? error.message : String(error),
789
- stack: error instanceof Error ? error.stack : undefined,
790
- timestamp: new Date().toISOString(),
791
- sessionId: request.sessionId,
792
- agentSlug: (globalThis as any).CIRCUIT_AGENT_SLUG || "unknown",
793
- },
794
- };
795
- }
796
- };
486
+ ## 🎯 Key Takeaways
797
487
 
798
- // Create the agent
799
- 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`
800
492
 
801
- // Export the agent for both Bun local development and Cloudflare Workers
802
- export default agent.getWorkerExport();
493
+ ## πŸ“– Additional Resources
803
494
 
804
- ```
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