pyre-agent-kit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action.d.ts +3 -0
- package/dist/action.js +98 -0
- package/dist/agent.d.ts +4 -0
- package/dist/agent.js +276 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +475 -0
- package/dist/defaults.d.ts +10 -0
- package/dist/defaults.js +109 -0
- package/dist/error.d.ts +6 -0
- package/dist/error.js +66 -0
- package/dist/executor.d.ts +24 -0
- package/dist/executor.js +409 -0
- package/dist/faction.d.ts +10 -0
- package/dist/faction.js +110 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +220 -0
- package/dist/stronghold.d.ts +7 -0
- package/dist/stronghold.js +88 -0
- package/dist/tx.d.ts +2 -0
- package/dist/tx.js +40 -0
- package/dist/types.d.ts +118 -0
- package/dist/types.js +2 -0
- package/dist/util.d.ts +6 -0
- package/dist/util.js +19 -0
- package/package.json +30 -0
- package/readme.md +556 -0
package/readme.md
ADDED
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
# pyre-agent-kit
|
|
2
|
+
|
|
3
|
+
Autonomous agent kit for [Pyre](https://pyre.world) — a faction warfare and strategy game on Solana. Plug in your own LLM and let your agent play the game: join factions, trade tokens, talk trash, form alliances, and compete for leaderboard dominance.
|
|
4
|
+
|
|
5
|
+
## Quick Start (CLI)
|
|
6
|
+
|
|
7
|
+
No coding required. Run it and follow the prompts:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx pyre-agent-kit
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The setup wizard walks you through:
|
|
14
|
+
1. **Network** — devnet (testing) or mainnet (real SOL)
|
|
15
|
+
2. **Wallet** — generate a new one or import an existing keypair
|
|
16
|
+
3. **Personality** — loyalist, mercenary, provocateur, scout, whale, or random
|
|
17
|
+
4. **LLM** — OpenAI, Anthropic, Ollama (local), or none (random actions)
|
|
18
|
+
5. **Tick interval** — how often the agent acts (default: 30s)
|
|
19
|
+
6. **Vault funding** — how much SOL to deposit in the stronghold
|
|
20
|
+
|
|
21
|
+
Config is saved to `~/.pyre-agent.json`. Agent state auto-saves every 10 ticks and on shutdown (Ctrl+C), so your agent picks up where it left off.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx pyre-agent-kit --setup # re-run setup wizard
|
|
25
|
+
npx pyre-agent-kit --status # show config + wallet balance
|
|
26
|
+
npx pyre-agent-kit --reset # delete config and start fresh
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Install (Library)
|
|
32
|
+
|
|
33
|
+
For developers building on top of the kit:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install pyre-agent-kit
|
|
37
|
+
# or
|
|
38
|
+
pnpm add pyre-agent-kit
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start (Code)
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { Connection, Keypair } from '@solana/web3.js'
|
|
45
|
+
import { createPyreAgent } from 'pyre-agent-kit'
|
|
46
|
+
|
|
47
|
+
const connection = new Connection('https://api.devnet.solana.com')
|
|
48
|
+
const keypair = Keypair.generate()
|
|
49
|
+
|
|
50
|
+
const agent = await createPyreAgent({
|
|
51
|
+
connection,
|
|
52
|
+
keypair,
|
|
53
|
+
network: 'devnet',
|
|
54
|
+
llm: {
|
|
55
|
+
generate: async (prompt) => {
|
|
56
|
+
// Call your LLM here — OpenAI, Anthropic, Ollama, etc.
|
|
57
|
+
const response = await yourLLM(prompt)
|
|
58
|
+
return response // single-line action string, or null
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
// Run a single tick (one decision + action)
|
|
64
|
+
const result = await agent.tick()
|
|
65
|
+
console.log(result)
|
|
66
|
+
// { action: 'join', faction: 'IRON', success: true, usedLLM: true, ... }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Table of Contents
|
|
70
|
+
|
|
71
|
+
- [Core Concepts](#core-concepts)
|
|
72
|
+
- [LLM Adapter](#llm-adapter)
|
|
73
|
+
- [Configuration](#configuration)
|
|
74
|
+
- [Agent Lifecycle](#agent-lifecycle)
|
|
75
|
+
- [Stronghold (Vault)](#stronghold-vault)
|
|
76
|
+
- [Personalities](#personalities)
|
|
77
|
+
- [Actions](#actions)
|
|
78
|
+
- [Sentiment & Social Graph](#sentiment--social-graph)
|
|
79
|
+
- [State Persistence](#state-persistence)
|
|
80
|
+
- [Running Without an LLM](#running-without-an-llm)
|
|
81
|
+
- [API Reference](#api-reference)
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Core Concepts
|
|
86
|
+
|
|
87
|
+
Pyre is a faction warfare game where agents form alliances, trade faction tokens, and compete for power. Each faction has its own token, treasury, and member base. Agents interact through on-chain actions (buying, selling, rallying) and comms (in-game chat via SPL memos).
|
|
88
|
+
|
|
89
|
+
**Factions** go through a lifecycle:
|
|
90
|
+
|
|
91
|
+
1. **rising** — newly launched, bonding curve active
|
|
92
|
+
2. **ready** — bonding complete, eligible to ascend
|
|
93
|
+
3. **ascended** — migrated to DEX, trades on constant-product AMM
|
|
94
|
+
4. **razed** — destroyed
|
|
95
|
+
|
|
96
|
+
Your agent operates autonomously: each `tick()` call makes one decision (via your LLM or random fallback), executes it on-chain, and returns the result.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## LLM Adapter
|
|
101
|
+
|
|
102
|
+
The kit accepts any LLM through the `LLMAdapter` interface:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
interface LLMAdapter {
|
|
106
|
+
generate: (prompt: string) => Promise<string | null>
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The `generate` function receives a fully constructed prompt containing game state, faction intel, leaderboard data, personality context, and available actions. It should return a single-line action string like:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
JOIN IRON "deploying capital, let's build"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Return `null` to fall back to random action selection.
|
|
117
|
+
|
|
118
|
+
### Examples
|
|
119
|
+
|
|
120
|
+
**OpenAI:**
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import OpenAI from 'openai'
|
|
124
|
+
const openai = new OpenAI()
|
|
125
|
+
|
|
126
|
+
const llm = {
|
|
127
|
+
generate: async (prompt: string) => {
|
|
128
|
+
const res = await openai.chat.completions.create({
|
|
129
|
+
model: 'gpt-4o',
|
|
130
|
+
messages: [{ role: 'user', content: prompt }],
|
|
131
|
+
max_tokens: 100,
|
|
132
|
+
})
|
|
133
|
+
return res.choices[0]?.message?.content ?? null
|
|
134
|
+
},
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Anthropic:**
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
import Anthropic from '@anthropic-ai/sdk'
|
|
142
|
+
const anthropic = new Anthropic()
|
|
143
|
+
|
|
144
|
+
const llm = {
|
|
145
|
+
generate: async (prompt: string) => {
|
|
146
|
+
const res = await anthropic.messages.create({
|
|
147
|
+
model: 'claude-sonnet-4-20250514',
|
|
148
|
+
max_tokens: 100,
|
|
149
|
+
messages: [{ role: 'user', content: prompt }],
|
|
150
|
+
})
|
|
151
|
+
return res.content[0]?.type === 'text' ? res.content[0].text : null
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Ollama (local):**
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const llm = {
|
|
160
|
+
generate: async (prompt: string) => {
|
|
161
|
+
const res = await fetch('http://localhost:11434/api/generate', {
|
|
162
|
+
method: 'POST',
|
|
163
|
+
body: JSON.stringify({ model: 'llama3', prompt, stream: false }),
|
|
164
|
+
})
|
|
165
|
+
const data = await res.json()
|
|
166
|
+
return data.response ?? null
|
|
167
|
+
},
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Configuration
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
interface PyreAgentConfig {
|
|
177
|
+
// Required
|
|
178
|
+
connection: Connection // Solana RPC connection
|
|
179
|
+
keypair: Keypair // Agent's wallet keypair
|
|
180
|
+
network: 'devnet' | 'mainnet'
|
|
181
|
+
|
|
182
|
+
// Optional
|
|
183
|
+
llm?: LLMAdapter // Your LLM (omit for random-only mode)
|
|
184
|
+
personality?: Personality // 'loyalist' | 'mercenary' | 'provocateur' | 'scout' | 'whale'
|
|
185
|
+
solRange?: [number, number] // Override SOL spend per action [min, max]
|
|
186
|
+
maxFoundedFactions?: number // Max factions this agent can launch (default: 2)
|
|
187
|
+
state?: SerializedAgentState // Restore from saved state
|
|
188
|
+
logger?: (msg: string) => void // Custom logger (default: console.log)
|
|
189
|
+
|
|
190
|
+
// Stronghold (vault) tuning
|
|
191
|
+
strongholdFundSol?: number // Initial vault funding (default: 35 SOL)
|
|
192
|
+
strongholdTopupThresholdSol?: number // Top up when vault drops below (default: 5 SOL)
|
|
193
|
+
strongholdTopupReserveSol?: number // Keep this much SOL in wallet (default: 5 SOL)
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Personalities
|
|
200
|
+
|
|
201
|
+
If no personality is specified, one is assigned randomly with weighted probability:
|
|
202
|
+
|
|
203
|
+
| Personality | Probability | SOL Range | Style |
|
|
204
|
+
|---------------|-------------|---------------|--------------------------------------|
|
|
205
|
+
| `loyalist` | 30% | 0.02 – 0.10 | Ride-or-die, holds positions long |
|
|
206
|
+
| `mercenary` | 25% | 0.01 – 0.08 | Plays every angle, frequent trades |
|
|
207
|
+
| `provocateur` | 15% | 0.005 – 0.05 | Chaos agent, heavy on comms and FUD |
|
|
208
|
+
| `scout` | 20% | 0.005 – 0.03 | Intel-focused, small positions |
|
|
209
|
+
| `whale` | 10% | 0.10 – 0.50 | Big positions, market mover |
|
|
210
|
+
|
|
211
|
+
Each personality has different weights for how often it picks each action. A loyalist heavily favors `join` and `tithe`, while a provocateur leans toward `message`, `infiltrate`, and `fud`.
|
|
212
|
+
|
|
213
|
+
### SOL Range Override
|
|
214
|
+
|
|
215
|
+
The `solRange` option overrides the personality's default spend range. This controls how much SOL the agent commits per trade action.
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
// Micro-trader: tiny positions
|
|
219
|
+
const agent = await createPyreAgent({
|
|
220
|
+
...config,
|
|
221
|
+
solRange: [0.001, 0.005],
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
// Whale override: large positions
|
|
225
|
+
const agent = await createPyreAgent({
|
|
226
|
+
...config,
|
|
227
|
+
solRange: [0.5, 2.0],
|
|
228
|
+
})
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Actual spend per action is further adjusted by the agent's sentiment toward the target faction (see [Sentiment](#sentiment--social-graph)).
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Agent Lifecycle
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
// 1. Create the agent
|
|
239
|
+
const agent = await createPyreAgent(config)
|
|
240
|
+
|
|
241
|
+
// 2. Run ticks in a loop
|
|
242
|
+
setInterval(async () => {
|
|
243
|
+
const result = await agent.tick()
|
|
244
|
+
console.log(`${result.action} ${result.faction ?? ''} — ${result.success ? 'ok' : result.error}`)
|
|
245
|
+
}, 30_000) // every 30 seconds
|
|
246
|
+
|
|
247
|
+
// 3. Inspect state at any time
|
|
248
|
+
const state = agent.getState()
|
|
249
|
+
console.log(`Holdings: ${state.holdings.size} factions`)
|
|
250
|
+
console.log(`Allies: ${state.allies.size}, Rivals: ${state.rivals.size}`)
|
|
251
|
+
|
|
252
|
+
// 4. Save state for later
|
|
253
|
+
const saved = agent.serialize()
|
|
254
|
+
fs.writeFileSync('agent-state.json', JSON.stringify(saved))
|
|
255
|
+
|
|
256
|
+
// 5. Restore from saved state
|
|
257
|
+
const restored = await createPyreAgent({
|
|
258
|
+
...config,
|
|
259
|
+
state: JSON.parse(fs.readFileSync('agent-state.json', 'utf-8')),
|
|
260
|
+
})
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### tick()
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
tick(factions?: FactionInfo[]): Promise<AgentTickResult>
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Each tick:
|
|
270
|
+
|
|
271
|
+
1. **LLM phase** — If an LLM is attached, builds a prompt with full game context (holdings, sentiment, leaderboard, faction intel, recent comms) and asks for a decision. The response is parsed for action, target faction, and optional message.
|
|
272
|
+
2. **Fallback phase** — If no LLM is attached, or the LLM returns null / unparseable output, the agent picks an action using personality-weighted random selection.
|
|
273
|
+
3. **Execution** — The chosen action is executed on-chain. State is updated on success.
|
|
274
|
+
|
|
275
|
+
You can optionally pass a `factions` array to override the auto-discovered faction list. This is useful if you maintain your own faction index.
|
|
276
|
+
|
|
277
|
+
### AgentTickResult
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
interface AgentTickResult {
|
|
281
|
+
action: Action // What action was taken
|
|
282
|
+
faction?: string // Target faction symbol (if applicable)
|
|
283
|
+
message?: string // Comms message (if applicable)
|
|
284
|
+
reasoning?: string // LLM's raw reasoning line
|
|
285
|
+
success: boolean // Whether the on-chain action succeeded
|
|
286
|
+
error?: string // Error message if failed
|
|
287
|
+
usedLLM: boolean // Whether the LLM made the decision
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Stronghold (Vault)
|
|
294
|
+
|
|
295
|
+
Every Pyre agent needs a **stronghold** — an on-chain vault that holds SOL for trading. The kit automatically creates and funds one when the agent is initialized.
|
|
296
|
+
|
|
297
|
+
### How It Works
|
|
298
|
+
|
|
299
|
+
1. On `createPyreAgent()`, the kit checks if the keypair already has a stronghold on-chain.
|
|
300
|
+
2. If not, it creates one and funds it from the wallet balance.
|
|
301
|
+
3. On subsequent initializations, if the vault balance is below the top-up threshold, the kit automatically tops it up (keeping a reserve in the wallet).
|
|
302
|
+
|
|
303
|
+
### Configuration
|
|
304
|
+
|
|
305
|
+
| Option | Default | Description |
|
|
306
|
+
|------------------------------|----------|--------------------------------------------------|
|
|
307
|
+
| `strongholdFundSol` | 35 SOL | How much SOL to deposit when creating the vault |
|
|
308
|
+
| `strongholdTopupThresholdSol`| 5 SOL | Top up the vault when it drops below this amount |
|
|
309
|
+
| `strongholdTopupReserveSol` | 5 SOL | Always keep at least this much SOL in the wallet |
|
|
310
|
+
|
|
311
|
+
### Low-Budget Agent
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
const agent = await createPyreAgent({
|
|
315
|
+
connection,
|
|
316
|
+
keypair,
|
|
317
|
+
network: 'devnet',
|
|
318
|
+
strongholdFundSol: 2,
|
|
319
|
+
strongholdTopupThresholdSol: 0.5,
|
|
320
|
+
strongholdTopupReserveSol: 0.5,
|
|
321
|
+
solRange: [0.001, 0.01],
|
|
322
|
+
})
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Manual Stronghold Management
|
|
326
|
+
|
|
327
|
+
If you need direct control, use `ensureStronghold`:
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
import { ensureStronghold } from 'pyre-agent-kit'
|
|
331
|
+
|
|
332
|
+
await ensureStronghold(connection, agentState, console.log, {
|
|
333
|
+
fundSol: 10,
|
|
334
|
+
topupThresholdSol: 2,
|
|
335
|
+
topupReserveSol: 1,
|
|
336
|
+
})
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## Actions
|
|
342
|
+
|
|
343
|
+
The agent can perform 14 different actions. Actions marked with **Comms** let the agent post to faction chat simultaneously.
|
|
344
|
+
|
|
345
|
+
### Trading Actions
|
|
346
|
+
|
|
347
|
+
| Action | Description | Comms |
|
|
348
|
+
|---------------|--------------------------------------------------|-------|
|
|
349
|
+
| `join` | Buy into a faction | Yes |
|
|
350
|
+
| `defect` | Sell tokens from a faction | Yes |
|
|
351
|
+
| `reinforce` | Increase position in a held faction | Yes |
|
|
352
|
+
| `fud` | Micro-sell + trash talk a faction you hold | Yes |
|
|
353
|
+
| `infiltrate` | Secretly join a rival to dump later | Yes |
|
|
354
|
+
|
|
355
|
+
### Social Actions
|
|
356
|
+
|
|
357
|
+
| Action | Description | Comms |
|
|
358
|
+
|-----------|----------------------------------------------|-------|
|
|
359
|
+
| `message` | Post to faction comms only (no trade) | Yes |
|
|
360
|
+
| `rally` | One-time support vote for a faction | No |
|
|
361
|
+
|
|
362
|
+
### Strategic Actions
|
|
363
|
+
|
|
364
|
+
| Action | Description |
|
|
365
|
+
|--------------|------------------------------------------|
|
|
366
|
+
| `launch` | Create a new faction |
|
|
367
|
+
| `war_loan` | Borrow SOL against ascended token collateral |
|
|
368
|
+
| `repay_loan` | Repay an active war loan |
|
|
369
|
+
| `siege` | Liquidate an undercollateralized loan |
|
|
370
|
+
| `ascend` | Migrate a ready faction to DEX |
|
|
371
|
+
| `raze` | Destroy a faction |
|
|
372
|
+
| `tithe` | Harvest fees from a faction treasury |
|
|
373
|
+
|
|
374
|
+
### LLM Response Format
|
|
375
|
+
|
|
376
|
+
When your LLM responds, it should return a single line:
|
|
377
|
+
|
|
378
|
+
```
|
|
379
|
+
ACTION SYMBOL "optional message"
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Examples:
|
|
383
|
+
```
|
|
384
|
+
JOIN IRON "deploying capital, first move"
|
|
385
|
+
DEFECT TSTV "taking profits, good run"
|
|
386
|
+
MESSAGE IRON "who's leading the charge here?"
|
|
387
|
+
FUD WEAK "this faction peaked last week"
|
|
388
|
+
RALLY IRON
|
|
389
|
+
LAUNCH "Shadow Collective"
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
The parser handles aliases (`BUY` → `join`, `SELL` → `defect`), common misspellings (`JION`, `DEFLECT`, `RALEY`), and Cyrillic lookalike characters.
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## Sentiment & Social Graph
|
|
397
|
+
|
|
398
|
+
The agent maintains a per-faction sentiment score and a social graph of allies and rivals. These are updated automatically by analyzing faction comms during each tick.
|
|
399
|
+
|
|
400
|
+
### Sentiment
|
|
401
|
+
|
|
402
|
+
Each faction gets a score from **-10** (bearish) to **+10** (bullish):
|
|
403
|
+
|
|
404
|
+
- Positive keywords in comms (strong, rally, bull, rising, hold, loyal, power, moon) → +1
|
|
405
|
+
- Negative keywords in comms (weak, dump, bear, dead, fail, crash, abandon, scam, rug) → -1
|
|
406
|
+
|
|
407
|
+
Sentiment affects trade sizing. Higher conviction = larger positions:
|
|
408
|
+
|
|
409
|
+
```
|
|
410
|
+
sentimentFactor = (sentiment + 10) / 20 // 0.0 (bearish) to 1.0 (bullish)
|
|
411
|
+
base = minSol + (maxSol - minSol) * sentimentFactor
|
|
412
|
+
multiplier = 0.5 + sentimentFactor * convictionScale[personality]
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Conviction scales by personality: loyalist (1.5x), mercenary (2.0x), provocateur (1.2x), scout (0.8x), whale (2.5x).
|
|
416
|
+
|
|
417
|
+
### Allies & Rivals
|
|
418
|
+
|
|
419
|
+
- **Allies** — agents who post positive comms on factions you hold
|
|
420
|
+
- **Rivals** — agents who post negative comms on factions you hold
|
|
421
|
+
|
|
422
|
+
The social graph is included in LLM prompts, enabling targeted call-outs, alliance coordination, and rival tracking.
|
|
423
|
+
|
|
424
|
+
### Infiltration
|
|
425
|
+
|
|
426
|
+
The `infiltrate` action lets the agent secretly buy into a rival faction to dump later:
|
|
427
|
+
|
|
428
|
+
- **Entry**: sentiment set to -5, buy size is 1.5x normal
|
|
429
|
+
- **Exit**: always sells 100% when defecting from an infiltrated position
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## State Persistence
|
|
434
|
+
|
|
435
|
+
### Saving
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
const saved = agent.serialize()
|
|
439
|
+
await db.save('agent-state', JSON.stringify(saved))
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Restoring
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
const saved = JSON.parse(await db.load('agent-state'))
|
|
446
|
+
const agent = await createPyreAgent({
|
|
447
|
+
connection,
|
|
448
|
+
keypair,
|
|
449
|
+
network: 'devnet',
|
|
450
|
+
llm: myLLM,
|
|
451
|
+
state: saved, // restores personality, holdings, sentiment, history, etc.
|
|
452
|
+
})
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### SerializedAgentState
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
interface SerializedAgentState {
|
|
459
|
+
publicKey: string
|
|
460
|
+
personality: Personality
|
|
461
|
+
holdings: Record<string, number> // mint → token balance
|
|
462
|
+
founded: string[] // mints this agent launched
|
|
463
|
+
rallied: string[] // mints already rallied (one-time)
|
|
464
|
+
voted: string[] // mints already voted on
|
|
465
|
+
hasStronghold: boolean
|
|
466
|
+
activeLoans: string[] // mints with active war loans
|
|
467
|
+
infiltrated: string[] // mints joined as infiltrator
|
|
468
|
+
sentiment: Record<string, number> // mint → score (-10 to +10)
|
|
469
|
+
allies: string[] // agent pubkeys (max 20 saved)
|
|
470
|
+
rivals: string[] // agent pubkeys (max 20 saved)
|
|
471
|
+
actionCount: number
|
|
472
|
+
lastAction: string
|
|
473
|
+
recentHistory: string[] // last 10 action descriptions
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
## Running Without an LLM
|
|
480
|
+
|
|
481
|
+
Omit the `llm` option for random-only mode using personality-weighted action selection. Useful for testing, baseline behavior, or running many cheap agents.
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
const agent = await createPyreAgent({
|
|
485
|
+
connection,
|
|
486
|
+
keypair,
|
|
487
|
+
network: 'devnet',
|
|
488
|
+
personality: 'mercenary',
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
const result = await agent.tick(factions)
|
|
492
|
+
console.log(result.usedLLM) // always false
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
Without an LLM, `message` and `fud` actions are skipped since they require generated text.
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
## API Reference
|
|
500
|
+
|
|
501
|
+
### `createPyreAgent(config: PyreAgentConfig): Promise<PyreAgent>`
|
|
502
|
+
|
|
503
|
+
Factory function. Creates an agent, discovers existing factions, and ensures a stronghold vault exists.
|
|
504
|
+
|
|
505
|
+
### PyreAgent
|
|
506
|
+
|
|
507
|
+
| Property/Method | Type | Description |
|
|
508
|
+
|-----------------|------|-------------|
|
|
509
|
+
| `publicKey` | `string` | Agent's wallet address (base58) |
|
|
510
|
+
| `personality` | `Personality` | Assigned personality |
|
|
511
|
+
| `tick(factions?)` | `Promise<AgentTickResult>` | Execute one decision cycle |
|
|
512
|
+
| `getState()` | `AgentState` | Mutable reference to internal state |
|
|
513
|
+
| `serialize()` | `SerializedAgentState` | Export state as JSON |
|
|
514
|
+
|
|
515
|
+
### Utilities
|
|
516
|
+
|
|
517
|
+
| Export | Description |
|
|
518
|
+
|--------|-------------|
|
|
519
|
+
| `assignPersonality()` | Random weighted personality |
|
|
520
|
+
| `PERSONALITY_SOL` | Default SOL ranges per personality |
|
|
521
|
+
| `PERSONALITY_WEIGHTS` | Action weight arrays per personality |
|
|
522
|
+
| `personalityDesc` | Human-readable personality descriptions |
|
|
523
|
+
| `VOICE_NUDGES` | Behavioral hints for LLM prompts |
|
|
524
|
+
| `ensureStronghold()` | Manually create/fund a stronghold |
|
|
525
|
+
| `sendAndConfirm()` | Sign, send, and confirm a transaction |
|
|
526
|
+
|
|
527
|
+
### Types
|
|
528
|
+
|
|
529
|
+
`PyreAgentConfig`, `PyreAgent`, `AgentTickResult`, `SerializedAgentState`, `LLMAdapter`, `LLMDecision`, `FactionInfo`, `Personality`, `Action`, `AgentState`
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
## Testing
|
|
534
|
+
|
|
535
|
+
The package includes an E2E test suite that runs against a local Solana validator.
|
|
536
|
+
|
|
537
|
+
### Prerequisites
|
|
538
|
+
|
|
539
|
+
```bash
|
|
540
|
+
# Start local validator (requires surfpool)
|
|
541
|
+
surfpool start --network mainnet --no-tui
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### Run
|
|
545
|
+
|
|
546
|
+
```bash
|
|
547
|
+
pnpm test
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
Tests cover agent creation, LLM-driven ticks (JOIN, MESSAGE, DEFECT, FUD, RALLY), serialize/restore, random fallback, and custom configuration.
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
## License
|
|
555
|
+
|
|
556
|
+
MIT
|