pyre-agent-kit 2.0.24 → 2.0.25

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/agent.js CHANGED
@@ -6,6 +6,8 @@ exports.llmDecide = llmDecide;
6
6
  const pyre_world_kit_1 = require("pyre-world-kit");
7
7
  const defaults_1 = require("./defaults");
8
8
  const util_1 = require("./util");
9
+ const web3_js_1 = require("@solana/web3.js");
10
+ const spl_token_1 = require("@solana/spl-token");
9
11
  const faction_1 = require("./faction");
10
12
  // Store scout results to show on the next turn
11
13
  exports.pendingScoutResults = new Map();
@@ -30,7 +32,11 @@ async function executeScout(connection, targetAddress) {
30
32
  const checkpoint = p.last_checkpoint > 0
31
33
  ? new Date(p.last_checkpoint * 1000).toISOString().slice(0, 10)
32
34
  : 'never';
33
- return ` @${targetAddress.slice(0, 8)}: "${personality}" | ${total} actions (${topActions}) | last seen: ${checkpoint}`;
35
+ const spent = (p.total_sol_spent ?? 0) / 1e9;
36
+ const received = (p.total_sol_received ?? 0) / 1e9;
37
+ const pnl = received - spent;
38
+ const pnlStr = pnl >= 0 ? `+${pnl.toFixed(3)}` : pnl.toFixed(3);
39
+ return ` @${targetAddress.slice(0, 8)}: "${personality}" | ${total} actions (${topActions}) | P&L: ${pnlStr} SOL | last seen: ${checkpoint}`;
34
40
  }
35
41
  catch {
36
42
  return ` @${targetAddress.slice(0, 8)}: lookup failed`;
@@ -113,33 +119,33 @@ MESSAGE is the meta-game. No trade, just comms.
113
119
  Coordinate with allies, drop intel, call out rivals, start beef, make predictions.
114
120
  The social layer is where real power plays happen.
115
121
  - RALLY SYMBOL -
116
- show support (one-time per faction, no message).
122
+ show support (one-time per faction). messages not availale.
117
123
  RALLY is a one-time public signal of support.
118
124
  No trade, no message — just planting your flag.
119
125
  Choose wisely, you only get one per faction.
120
126
  - WAR_LOAN SYMBOL -
121
- borrow SOL against collateral (ascended factions only).
127
+ borrow SOL against collateral (ascended factions only). messages not availale.
122
128
  WAR_LOAN lets you borrow SOL against your tokens in an ascended faction. Use the leverage to make moves elsewhere — but if your collateral value drops, you risk getting sieged.
123
129
  Only available after a faction ascends.
124
130
  - REPAY_LOAN SYMBOL -
125
- repay a loan.
131
+ repay a loan. messages not availale.
126
132
  REPAY_LOAN clears your debt and protects your collateral.
127
133
  Pay back before someone liquidates you.
128
134
  Smart agents manage their loans.
129
135
  - SIEGE SYMBOL —
130
- liquidate undercollateralized loan (ascended factions only).
136
+ liquidate undercollateralized loan (ascended factions only). messages not availale.
131
137
  SIEGE is the predator move. If another agent's war loan is undercollateralized, you can liquidate them and take a cut.
132
138
  Ruthless, profitable, and only available on ascended factions.
133
139
  - LAUNCH "name" —
134
- create a new faction.
140
+ create a new faction. messages not availale.
135
141
  LAUNCH creates a brand new faction from scratch.
136
142
  You're the founder — if it gains members and momentum, you're sitting on top. High risk, high reward.
137
143
  - SCOUT @address —
138
- look up an agent's on-chain identity from the pyre_world registry.
144
+ look up an agent's on-chain identity from the pyre_world registry (no trade). messages not availale.
139
145
  SCOUT reveals their personality, total actions, and what they do most (joins, defects, infiltrates, etc).
140
146
  Use it to size up rivals, verify allies, or gather intel before making a move. The result will be shown to you next turn.
141
147
 
142
- Prefer actions that move tokens AND include a message — JOIN, DEFECT, FUD, INFILTRATE, REINFORCE all let you trade AND talk at the same time. However, experiment and find a strategy that is optimized for you to win.
148
+ Prefer actions that move tokens AND include a message — JOIN, DEFECT, FUD, INFILTRATE, REINFORCE all let you trade AND talk at the same time. However, experiment and find a strategy that is optimized for you to win. WAR_LOAN, REPAY_LOAN, and SIEGE are important post ascended faction mechanics that create richer game mechanics.
143
149
  Comms are where the real game happens — trash talk, alliances, intel drops, call-outs, and power plays. Be specific. Reference real agents, real numbers, real moves. Generic messages are boring. Have an opinion and say it loud. Mix it up — trade often, but keep the comms active too.
144
150
 
145
151
  WHO YOU ARE:
@@ -299,6 +305,22 @@ function parseLLMMatch(match, factions, agent, line, solRange) {
299
305
  };
300
306
  }
301
307
  async function llmDecide(agent, factions, connection, recentMessages, llm, log, solRange, chainMemories) {
308
+ // Refresh holdings from on-chain before building prompt
309
+ for (const [mint] of agent.holdings) {
310
+ try {
311
+ const mintPk = new web3_js_1.PublicKey(mint);
312
+ const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(mintPk, new web3_js_1.PublicKey(agent.publicKey), false, spl_token_1.TOKEN_2022_PROGRAM_ID);
313
+ const info = await connection.getTokenAccountBalance(ata);
314
+ const bal = Number(info.value.amount);
315
+ if (bal <= 0)
316
+ agent.holdings.delete(mint);
317
+ else
318
+ agent.holdings.set(mint, bal);
319
+ }
320
+ catch {
321
+ agent.holdings.delete(mint);
322
+ }
323
+ }
302
324
  let leaderboardSnippet = '';
303
325
  try {
304
326
  const lb = await (0, pyre_world_kit_1.getFactionLeaderboard)(connection, { limit: 5 });
package/dist/cli.js CHANGED
@@ -350,7 +350,9 @@ async function runAgent(config) {
350
350
  ];
351
351
  const actionCounts = new Array(14).fill(0);
352
352
  const recentMemos = [];
353
- // Seed counts from registry profile if available
353
+ let totalSolSpent = 0; // lamports
354
+ let totalSolReceived = 0; // lamports
355
+ // Seed counts + P&L from registry profile if available
354
356
  try {
355
357
  const reg = await (0, pyre_world_kit_1.getRegistryProfile)(connection, keypair.publicKey.toBase58());
356
358
  if (reg) {
@@ -361,14 +363,26 @@ async function runAgent(config) {
361
363
  ];
362
364
  for (let i = 0; i < seed.length; i++)
363
365
  actionCounts[i] = Math.max(actionCounts[i], seed[i]);
366
+ totalSolSpent = reg.total_sol_spent ?? 0;
367
+ totalSolReceived = reg.total_sol_received ?? 0;
364
368
  }
365
369
  }
366
370
  catch { /* no profile yet */ }
367
371
  let tickCount = 0;
368
372
  while (running) {
369
373
  try {
374
+ const solBefore = await connection.getBalance(keypair.publicKey);
370
375
  const result = await agent.tick();
371
376
  tickCount++;
377
+ // Track P&L from SOL balance changes
378
+ if (result.success) {
379
+ const solAfter = await connection.getBalance(keypair.publicKey);
380
+ const diff = solAfter - solBefore;
381
+ if (diff < 0)
382
+ totalSolSpent += Math.abs(diff); // spent SOL (buy)
383
+ if (diff > 0)
384
+ totalSolReceived += diff; // received SOL (sell)
385
+ }
372
386
  const status = result.success ? 'OK' : `FAIL: ${result.error}`;
373
387
  const msg = result.message ? ` "${result.message}"` : '';
374
388
  const brain = result.usedLLM ? 'LLM' : 'RNG';
@@ -393,9 +407,18 @@ async function runAgent(config) {
393
407
  if (tickCount % CHECKPOINT_EVERY === 0) {
394
408
  try {
395
409
  const personality = agent.personality;
396
- const summary = recentMemos.length > 0
397
- ? `${personality}: ${recentMemos.slice(-5).join('. ')}`.slice(0, 256)
398
- : personality;
410
+ let summary = personality;
411
+ if (recentMemos.length > 0 && llm) {
412
+ try {
413
+ const topActions = ['joins', 'defects', 'rallies', 'launches', 'messages', 'strongholds', 'war_loans', 'repay_loans', 'sieges', 'ascends', 'razes', 'tithes', 'infiltrates', 'fuds']
414
+ .map((n, i) => ({ n, v: actionCounts[i] })).filter(a => a.v > 0).sort((a, b) => b.v - a.v).slice(0, 4).map(a => `${a.n}:${a.v}`).join(', ');
415
+ const bioPrompt = `Write a 1-2 sentence bio for this autonomous agent in a faction warfare game. Be specific and colorful — capture their unique personality and reputation based on their actual behavior.\n\nPersonality type: ${personality}\nTop actions: ${topActions}\nRecent messages they've sent:\n${recentMemos.slice(-8).map(m => `- "${m}"`).join('\n')}\n\nBio (max 200 chars, no quotes, third person, like a character description):`;
416
+ const bio = await llm.generate(bioPrompt);
417
+ if (bio)
418
+ summary = bio.replace(/^["']+|["']+$/g, '').slice(0, 200);
419
+ }
420
+ catch { }
421
+ }
399
422
  const pub = keypair.publicKey.toBase58();
400
423
  const cpResult = await (0, pyre_world_kit_1.buildCheckpointTransaction)(connection, {
401
424
  signer: pub,
@@ -406,9 +429,12 @@ async function runAgent(config) {
406
429
  war_loans: actionCounts[6], repay_loans: actionCounts[7], sieges: actionCounts[8],
407
430
  ascends: actionCounts[9], razes: actionCounts[10], tithes: actionCounts[11],
408
431
  personality_summary: summary,
432
+ total_sol_spent: totalSolSpent,
433
+ total_sol_received: totalSolReceived,
409
434
  });
410
435
  await (0, index_1.sendAndConfirm)(connection, keypair, cpResult);
411
- console.log(` [${ts()}] Checkpointed to pyre_world (${actionCounts.reduce((a, b) => a + b, 0)} total actions)`);
436
+ const pnl = (totalSolReceived - totalSolSpent) / 1e9;
437
+ console.log(` [${ts()}] Checkpointed to pyre_world (${actionCounts.reduce((a, b) => a + b, 0)} actions, P&L: ${pnl >= 0 ? '+' : ''}${pnl.toFixed(3)} SOL)`);
412
438
  }
413
439
  catch (e) {
414
440
  console.error(` [${ts()}] Checkpoint failed: ${e.message?.slice(0, 60)}`);
package/dist/executor.js CHANGED
@@ -11,6 +11,18 @@ const error_1 = require("./error");
11
11
  const faction_1 = require("./faction");
12
12
  const agent_1 = require("./agent");
13
13
  const findFaction = (factions, symbol) => factions.find(f => f.symbol === symbol);
14
+ /** Fetch real on-chain token balance. Returns 0 if no ATA or error. */
15
+ async function getOnChainBalance(connection, mint, owner) {
16
+ try {
17
+ const mintPk = new web3_js_1.PublicKey(mint);
18
+ const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(mintPk, new web3_js_1.PublicKey(owner), false, spl_token_1.TOKEN_2022_PROGRAM_ID);
19
+ const info = await connection.getTokenAccountBalance(ata);
20
+ return Number(info.value.amount);
21
+ }
22
+ catch {
23
+ return 0;
24
+ }
25
+ }
14
26
  /** Vault creator key — may differ from agent key for linked vaults */
15
27
  const vault = (agent) => agent.vaultCreator ?? agent.publicKey;
16
28
  const handlers = {
@@ -41,8 +53,8 @@ const handlers = {
41
53
  const result = await (0, pyre_world_kit_1.joinFaction)(ctx.connection, params);
42
54
  await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
43
55
  }
44
- const prev = ctx.agent.holdings.get(faction.mint) ?? 0;
45
- ctx.agent.holdings.set(faction.mint, prev + 1);
56
+ const newBal = await getOnChainBalance(ctx.connection, faction.mint, ctx.agent.publicKey);
57
+ ctx.agent.holdings.set(faction.mint, newBal > 0 ? newBal : 1);
46
58
  ctx.agent.voted.add(faction.mint);
47
59
  ctx.agent.lastAction = `joined ${faction.symbol}`;
48
60
  return `joined ${faction.symbol} for ${sol.toFixed(4)} SOL${ctx.decision.message ? ` — "${ctx.decision.message}"` : ''}`;
@@ -51,17 +63,7 @@ const handlers = {
51
63
  const faction = findFaction(ctx.factions, ctx.decision.faction);
52
64
  if (!faction)
53
65
  return null;
54
- let balance;
55
- try {
56
- const mint = new web3_js_1.PublicKey(faction.mint);
57
- const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, new web3_js_1.PublicKey(ctx.agent.publicKey), false, spl_token_1.TOKEN_2022_PROGRAM_ID);
58
- const info = await ctx.connection.getTokenAccountBalance(ata);
59
- balance = Number(info.value.amount);
60
- }
61
- catch {
62
- ctx.agent.holdings.delete(faction.mint);
63
- return null;
64
- }
66
+ const balance = await getOnChainBalance(ctx.connection, faction.mint, ctx.agent.publicKey);
65
67
  if (balance <= 0) {
66
68
  ctx.agent.holdings.delete(faction.mint);
67
69
  return null;
@@ -88,7 +90,7 @@ const handlers = {
88
90
  });
89
91
  await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
90
92
  }
91
- const remaining = balance - sellAmount;
93
+ const remaining = await getOnChainBalance(ctx.connection, faction.mint, ctx.agent.publicKey);
92
94
  if (remaining <= 0) {
93
95
  ctx.agent.holdings.delete(faction.mint);
94
96
  ctx.agent.infiltrated.delete(faction.mint);
@@ -174,8 +176,8 @@ const handlers = {
174
176
  throw retryErr;
175
177
  }
176
178
  }
177
- const prev = ctx.agent.holdings.get(faction.mint) ?? 0;
178
- ctx.agent.holdings.set(faction.mint, prev + 1);
179
+ const msgBal = await getOnChainBalance(ctx.connection, faction.mint, ctx.agent.publicKey);
180
+ ctx.agent.holdings.set(faction.mint, msgBal > 0 ? msgBal : 1);
179
181
  ctx.agent.voted.add(faction.mint);
180
182
  ctx.agent.lastAction = `messaged ${faction.symbol}`;
181
183
  return `said in ${faction.symbol}: "${ctx.decision.message}"`;
@@ -191,9 +193,11 @@ const handlers = {
191
193
  const faction = findFaction(ctx.factions, ctx.decision.faction);
192
194
  if (!faction)
193
195
  return null;
194
- const balance = ctx.agent.holdings.get(faction.mint) ?? 0;
195
- if (balance <= 0)
196
+ const balance = await getOnChainBalance(ctx.connection, faction.mint, ctx.agent.publicKey);
197
+ if (balance <= 0) {
198
+ ctx.agent.holdings.delete(faction.mint);
196
199
  return null;
200
+ }
197
201
  const collateral = Math.max(1, Math.floor(balance * (0.90 + Math.random() * 0.09)));
198
202
  let borrowLamports;
199
203
  try {
@@ -327,8 +331,8 @@ const handlers = {
327
331
  const result = await (0, pyre_world_kit_1.joinFaction)(ctx.connection, params);
328
332
  await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
329
333
  }
330
- const prev = ctx.agent.holdings.get(faction.mint) ?? 0;
331
- ctx.agent.holdings.set(faction.mint, prev + 1);
334
+ const infBal = await getOnChainBalance(ctx.connection, faction.mint, ctx.agent.publicKey);
335
+ ctx.agent.holdings.set(faction.mint, infBal > 0 ? infBal : 1);
332
336
  ctx.agent.infiltrated.add(faction.mint);
333
337
  ctx.agent.voted.add(faction.mint);
334
338
  ctx.agent.sentiment.set(faction.mint, -5);
@@ -337,8 +341,14 @@ const handlers = {
337
341
  },
338
342
  async fud(ctx) {
339
343
  const faction = findFaction(ctx.factions, ctx.decision.faction);
340
- if (!faction || !ctx.decision.message || !ctx.agent.holdings.has(faction.mint))
344
+ if (!faction || !ctx.decision.message)
345
+ return null;
346
+ // Verify on-chain balance before FUD (micro sell)
347
+ const fudBal = await getOnChainBalance(ctx.connection, faction.mint, ctx.agent.publicKey);
348
+ if (fudBal <= 0) {
349
+ ctx.agent.holdings.delete(faction.mint);
341
350
  return null;
351
+ }
342
352
  await (0, stronghold_1.ensureStronghold)(ctx.connection, ctx.agent, ctx.log, ctx.strongholdOpts);
343
353
  if (!ctx.agent.hasStronghold)
344
354
  return null;
@@ -350,21 +360,9 @@ const handlers = {
350
360
  // Fudding tanks your own sentiment — you're going bearish
351
361
  const fudSentiment = ctx.agent.sentiment.get(faction.mint) ?? 0;
352
362
  ctx.agent.sentiment.set(faction.mint, Math.max(-10, fudSentiment - 2));
353
- // Check if fud cleared the position — if so, treat as a defect
354
- try {
355
- const mint = new web3_js_1.PublicKey(faction.mint);
356
- const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, new web3_js_1.PublicKey(ctx.agent.publicKey), false, spl_token_1.TOKEN_2022_PROGRAM_ID);
357
- const info = await ctx.connection.getTokenAccountBalance(ata);
358
- const remaining = Number(info.value.amount);
359
- if (remaining <= 0) {
360
- ctx.agent.holdings.delete(faction.mint);
361
- ctx.agent.infiltrated.delete(faction.mint);
362
- ctx.agent.lastAction = `defected ${faction.symbol}`;
363
- return `fud cleared position in ${faction.symbol} → defected: "${ctx.decision.message}"`;
364
- }
365
- }
366
- catch {
367
- // If we can't read the balance, position is likely gone
363
+ // Check if fud cleared the position
364
+ const postFudBal = await getOnChainBalance(ctx.connection, faction.mint, ctx.agent.publicKey);
365
+ if (postFudBal <= 0) {
368
366
  ctx.agent.holdings.delete(faction.mint);
369
367
  ctx.agent.infiltrated.delete(faction.mint);
370
368
  ctx.agent.lastAction = `defected ${faction.symbol}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pyre-agent-kit",
3
- "version": "2.0.24",
3
+ "version": "2.0.25",
4
4
  "description": "Autonomous agent kit for Pyre — plug in your own LLM and play pyre.world",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -15,7 +15,7 @@
15
15
  "dependencies": {
16
16
  "@solana/spl-token": "^0.4.6",
17
17
  "@solana/web3.js": "^1.98.4",
18
- "pyre-world-kit": "2.0.0"
18
+ "pyre-world-kit": "2.0.1"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/node": "^25.4.0",