pyre-agent-kit 2.0.23 → 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.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import { AgentState, FactionInfo, LLMAdapter, LLMDecision } from './types';
2
2
  import { Connection } from '@solana/web3.js';
3
+ export declare const pendingScoutResults: Map<string, string[]>;
4
+ /** Execute a SCOUT action — look up an agent's pyre_world registry profile */
5
+ export declare function executeScout(connection: Connection, targetAddress: string): Promise<string>;
3
6
  export declare const buildAgentPrompt: (agent: AgentState, factions: FactionInfo[], leaderboardSnippet: string, intelSnippet: string, recentMessages: string[], solRange?: [number, number], chainMemories?: string[]) => string;
4
7
  export declare function llmDecide(agent: AgentState, factions: FactionInfo[], connection: Connection, recentMessages: string[], llm: LLMAdapter, log: (msg: string) => void, solRange?: [number, number], chainMemories?: string[]): Promise<LLMDecision | null>;
package/dist/agent.js CHANGED
@@ -1,11 +1,47 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildAgentPrompt = void 0;
3
+ exports.buildAgentPrompt = exports.pendingScoutResults = void 0;
4
+ exports.executeScout = executeScout;
4
5
  exports.llmDecide = llmDecide;
5
6
  const pyre_world_kit_1 = require("pyre-world-kit");
6
7
  const defaults_1 = require("./defaults");
7
8
  const util_1 = require("./util");
9
+ const web3_js_1 = require("@solana/web3.js");
10
+ const spl_token_1 = require("@solana/spl-token");
8
11
  const faction_1 = require("./faction");
12
+ // Store scout results to show on the next turn
13
+ exports.pendingScoutResults = new Map();
14
+ /** Execute a SCOUT action — look up an agent's pyre_world registry profile */
15
+ async function executeScout(connection, targetAddress) {
16
+ try {
17
+ const p = await (0, pyre_world_kit_1.getRegistryProfile)(connection, targetAddress);
18
+ if (!p)
19
+ return ` @${targetAddress.slice(0, 8)}: no pyre identity found`;
20
+ const total = p.joins + p.defects + p.rallies + p.launches + p.messages +
21
+ p.fuds + p.infiltrates + p.reinforces + p.war_loans + p.repay_loans +
22
+ p.sieges + p.ascends + p.razes + p.tithes;
23
+ const topActions = [
24
+ { n: 'joins', v: p.joins }, { n: 'defects', v: p.defects },
25
+ { n: 'rallies', v: p.rallies }, { n: 'messages', v: p.messages },
26
+ { n: 'fuds', v: p.fuds }, { n: 'infiltrates', v: p.infiltrates },
27
+ { n: 'reinforces', v: p.reinforces }, { n: 'war_loans', v: p.war_loans },
28
+ { n: 'sieges', v: p.sieges },
29
+ ].sort((a, b) => b.v - a.v).filter(a => a.v > 0).slice(0, 4)
30
+ .map(a => `${a.n}:${a.v}`).join(', ');
31
+ const personality = p.personality_summary || 'unknown';
32
+ const checkpoint = p.last_checkpoint > 0
33
+ ? new Date(p.last_checkpoint * 1000).toISOString().slice(0, 10)
34
+ : 'never';
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}`;
40
+ }
41
+ catch {
42
+ return ` @${targetAddress.slice(0, 8)}: lookup failed`;
43
+ }
44
+ }
9
45
  const buildAgentPrompt = (agent, factions, leaderboardSnippet, intelSnippet, recentMessages, solRange, chainMemories) => {
10
46
  const [minSol, maxSol] = solRange ?? defaults_1.PERSONALITY_SOL[agent.personality];
11
47
  const holdingsList = [...agent.holdings.entries()]
@@ -83,29 +119,33 @@ MESSAGE is the meta-game. No trade, just comms.
83
119
  Coordinate with allies, drop intel, call out rivals, start beef, make predictions.
84
120
  The social layer is where real power plays happen.
85
121
  - RALLY SYMBOL -
86
- show support (one-time per faction, no message).
122
+ show support (one-time per faction). messages not availale.
87
123
  RALLY is a one-time public signal of support.
88
124
  No trade, no message — just planting your flag.
89
125
  Choose wisely, you only get one per faction.
90
126
  - WAR_LOAN SYMBOL -
91
- borrow SOL against collateral (ascended factions only).
127
+ borrow SOL against collateral (ascended factions only). messages not availale.
92
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.
93
129
  Only available after a faction ascends.
94
130
  - REPAY_LOAN SYMBOL -
95
- repay a loan.
131
+ repay a loan. messages not availale.
96
132
  REPAY_LOAN clears your debt and protects your collateral.
97
133
  Pay back before someone liquidates you.
98
134
  Smart agents manage their loans.
99
135
  - SIEGE SYMBOL —
100
- liquidate undercollateralized loan (ascended factions only).
136
+ liquidate undercollateralized loan (ascended factions only). messages not availale.
101
137
  SIEGE is the predator move. If another agent's war loan is undercollateralized, you can liquidate them and take a cut.
102
138
  Ruthless, profitable, and only available on ascended factions.
103
139
  - LAUNCH "name" —
104
- create a new faction.
140
+ create a new faction. messages not availale.
105
141
  LAUNCH creates a brand new faction from scratch.
106
142
  You're the founder — if it gains members and momentum, you're sitting on top. High risk, high reward.
143
+ - SCOUT @address —
144
+ look up an agent's on-chain identity from the pyre_world registry (no trade). messages not availale.
145
+ SCOUT reveals their personality, total actions, and what they do most (joins, defects, infiltrates, etc).
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.
107
147
 
108
- 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.
109
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.
110
150
 
111
151
  WHO YOU ARE:
@@ -144,6 +184,11 @@ function parseLLMDecision(raw, factions, agent, solRange) {
144
184
  return null;
145
185
  for (const candidate of lines) {
146
186
  const line = candidate.trim();
187
+ // Handle SCOUT @address
188
+ const scoutMatch = line.match(/^SCOUT\s+@?([A-Za-z0-9]{6,44})/i);
189
+ if (scoutMatch) {
190
+ return { action: 'scout', faction: scoutMatch[1], reasoning: line };
191
+ }
147
192
  const cleaned = line
148
193
  .replace(/\*+/g, '') // strip all bold/italic markdown (e.g. **DEFECT SBP "msg"**)
149
194
  .replace(/^[-•>#\d.)\s]+/, '').replace(/^(?:WARNING|NOTE|RESPONSE|OUTPUT|ANSWER|RESULT|SCPRT|SCRIPT)\s*:?\s*/i, '').replace(/^ACTION\s+/i, '')
@@ -260,6 +305,22 @@ function parseLLMMatch(match, factions, agent, line, solRange) {
260
305
  };
261
306
  }
262
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
+ }
263
324
  let leaderboardSnippet = '';
264
325
  try {
265
326
  const lb = await (0, pyre_world_kit_1.getFactionLeaderboard)(connection, { limit: 5 });
@@ -323,7 +384,14 @@ async function llmDecide(agent, factions, connection, recentMessages, llm, log,
323
384
  catch {
324
385
  // intel fetch failed, proceed without it
325
386
  }
326
- const prompt = (0, exports.buildAgentPrompt)(agent, factions, leaderboardSnippet, intelSnippet, recentMessages, solRange, chainMemories);
387
+ // Include results from previous SCOUT actions
388
+ const scoutResults = exports.pendingScoutResults.get(agent.publicKey);
389
+ let scoutSnippet = '';
390
+ if (scoutResults && scoutResults.length > 0) {
391
+ scoutSnippet = '\nSCOUT RESULTS (from your previous SCOUT actions):\n' + scoutResults.join('\n');
392
+ exports.pendingScoutResults.delete(agent.publicKey);
393
+ }
394
+ const prompt = (0, exports.buildAgentPrompt)(agent, factions, leaderboardSnippet, intelSnippet + scoutSnippet, recentMessages, solRange, chainMemories);
327
395
  const raw = await llm.generate(prompt);
328
396
  if (!raw) {
329
397
  log(`[${agent.publicKey.slice(0, 8)}] LLM returned null`);
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/defaults.js CHANGED
@@ -72,7 +72,8 @@ exports.ACTION_MAP = {
72
72
  'SEND': 'MESSAGE', 'SAY': 'MESSAGE', 'CHAT': 'MESSAGE', 'MSG': 'MESSAGE', 'MESSAGING': 'MESSAGE',
73
73
  'CREATE': 'LAUNCH', 'FOUND': 'LAUNCH', 'HARVEST': 'TITHE',
74
74
  'MIGRATE': 'ASCEND', 'RECLAIM': 'RAZE', 'SPY': 'INFILTRATE',
75
- 'INVESTIGATION': 'INFILTRATE', 'INVESTIGATE': 'INFILTRATE', 'SCOUT': 'INFILTRATE', 'RECON': 'INFILTRATE',
75
+ 'INVESTIGATION': 'INFILTRATE', 'INVESTIGATE': 'INFILTRATE', 'RECON': 'INFILTRATE',
76
+ 'SCOUT': 'SCOUT',
76
77
  'PLEDGE': 'JOIN', 'ALLY': 'JOIN', 'BACK': 'JOIN', 'FUND': 'JOIN',
77
78
  'WITHDRAW': 'DEFECT', 'RETREAT': 'DEFECT', 'ABANDON': 'DEFECT', 'BAIL': 'DEFECT',
78
79
  'ANNOUNCE': 'MESSAGE', 'BROADCAST': 'MESSAGE', 'COMM': 'MESSAGE', 'COMMS': 'MESSAGE', 'REPORT': 'MESSAGE',
package/dist/executor.js CHANGED
@@ -9,7 +9,20 @@ const stronghold_1 = require("./stronghold");
9
9
  const action_1 = require("./action");
10
10
  const error_1 = require("./error");
11
11
  const faction_1 = require("./faction");
12
+ const agent_1 = require("./agent");
12
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
+ }
13
26
  /** Vault creator key — may differ from agent key for linked vaults */
14
27
  const vault = (agent) => agent.vaultCreator ?? agent.publicKey;
15
28
  const handlers = {
@@ -40,8 +53,8 @@ const handlers = {
40
53
  const result = await (0, pyre_world_kit_1.joinFaction)(ctx.connection, params);
41
54
  await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
42
55
  }
43
- const prev = ctx.agent.holdings.get(faction.mint) ?? 0;
44
- 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);
45
58
  ctx.agent.voted.add(faction.mint);
46
59
  ctx.agent.lastAction = `joined ${faction.symbol}`;
47
60
  return `joined ${faction.symbol} for ${sol.toFixed(4)} SOL${ctx.decision.message ? ` — "${ctx.decision.message}"` : ''}`;
@@ -50,17 +63,7 @@ const handlers = {
50
63
  const faction = findFaction(ctx.factions, ctx.decision.faction);
51
64
  if (!faction)
52
65
  return null;
53
- let balance;
54
- try {
55
- const mint = new web3_js_1.PublicKey(faction.mint);
56
- const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, new web3_js_1.PublicKey(ctx.agent.publicKey), false, spl_token_1.TOKEN_2022_PROGRAM_ID);
57
- const info = await ctx.connection.getTokenAccountBalance(ata);
58
- balance = Number(info.value.amount);
59
- }
60
- catch {
61
- ctx.agent.holdings.delete(faction.mint);
62
- return null;
63
- }
66
+ const balance = await getOnChainBalance(ctx.connection, faction.mint, ctx.agent.publicKey);
64
67
  if (balance <= 0) {
65
68
  ctx.agent.holdings.delete(faction.mint);
66
69
  return null;
@@ -87,7 +90,7 @@ const handlers = {
87
90
  });
88
91
  await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
89
92
  }
90
- const remaining = balance - sellAmount;
93
+ const remaining = await getOnChainBalance(ctx.connection, faction.mint, ctx.agent.publicKey);
91
94
  if (remaining <= 0) {
92
95
  ctx.agent.holdings.delete(faction.mint);
93
96
  ctx.agent.infiltrated.delete(faction.mint);
@@ -173,8 +176,8 @@ const handlers = {
173
176
  throw retryErr;
174
177
  }
175
178
  }
176
- const prev = ctx.agent.holdings.get(faction.mint) ?? 0;
177
- 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);
178
181
  ctx.agent.voted.add(faction.mint);
179
182
  ctx.agent.lastAction = `messaged ${faction.symbol}`;
180
183
  return `said in ${faction.symbol}: "${ctx.decision.message}"`;
@@ -190,9 +193,11 @@ const handlers = {
190
193
  const faction = findFaction(ctx.factions, ctx.decision.faction);
191
194
  if (!faction)
192
195
  return null;
193
- const balance = ctx.agent.holdings.get(faction.mint) ?? 0;
194
- 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);
195
199
  return null;
200
+ }
196
201
  const collateral = Math.max(1, Math.floor(balance * (0.90 + Math.random() * 0.09)));
197
202
  let borrowLamports;
198
203
  try {
@@ -326,8 +331,8 @@ const handlers = {
326
331
  const result = await (0, pyre_world_kit_1.joinFaction)(ctx.connection, params);
327
332
  await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
328
333
  }
329
- const prev = ctx.agent.holdings.get(faction.mint) ?? 0;
330
- 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);
331
336
  ctx.agent.infiltrated.add(faction.mint);
332
337
  ctx.agent.voted.add(faction.mint);
333
338
  ctx.agent.sentiment.set(faction.mint, -5);
@@ -336,8 +341,14 @@ const handlers = {
336
341
  },
337
342
  async fud(ctx) {
338
343
  const faction = findFaction(ctx.factions, ctx.decision.faction);
339
- 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);
340
350
  return null;
351
+ }
341
352
  await (0, stronghold_1.ensureStronghold)(ctx.connection, ctx.agent, ctx.log, ctx.strongholdOpts);
342
353
  if (!ctx.agent.hasStronghold)
343
354
  return null;
@@ -349,21 +360,9 @@ const handlers = {
349
360
  // Fudding tanks your own sentiment — you're going bearish
350
361
  const fudSentiment = ctx.agent.sentiment.get(faction.mint) ?? 0;
351
362
  ctx.agent.sentiment.set(faction.mint, Math.max(-10, fudSentiment - 2));
352
- // Check if fud cleared the position — if so, treat as a defect
353
- try {
354
- const mint = new web3_js_1.PublicKey(faction.mint);
355
- const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, new web3_js_1.PublicKey(ctx.agent.publicKey), false, spl_token_1.TOKEN_2022_PROGRAM_ID);
356
- const info = await ctx.connection.getTokenAccountBalance(ata);
357
- const remaining = Number(info.value.amount);
358
- if (remaining <= 0) {
359
- ctx.agent.holdings.delete(faction.mint);
360
- ctx.agent.infiltrated.delete(faction.mint);
361
- ctx.agent.lastAction = `defected ${faction.symbol}`;
362
- return `fud cleared position in ${faction.symbol} → defected: "${ctx.decision.message}"`;
363
- }
364
- }
365
- catch {
366
- // 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) {
367
366
  ctx.agent.holdings.delete(faction.mint);
368
367
  ctx.agent.infiltrated.delete(faction.mint);
369
368
  ctx.agent.lastAction = `defected ${faction.symbol}`;
@@ -372,6 +371,20 @@ const handlers = {
372
371
  ctx.agent.lastAction = `fud ${faction.symbol}`;
373
372
  return `argued in ${faction.symbol}: "${ctx.decision.message}"`;
374
373
  },
374
+ scout: async (ctx) => {
375
+ const target = ctx.decision.faction; // holds the address for scout
376
+ if (!target)
377
+ return null;
378
+ const result = await (0, agent_1.executeScout)(ctx.connection, target);
379
+ // Store result to show in next turn's prompt
380
+ const existing = agent_1.pendingScoutResults.get(ctx.agent.publicKey) ?? [];
381
+ existing.push(result);
382
+ if (existing.length > 5)
383
+ existing.shift();
384
+ agent_1.pendingScoutResults.set(ctx.agent.publicKey, existing);
385
+ ctx.agent.lastAction = `scouted @${target.slice(0, 8)}`;
386
+ return `scouted @${target.slice(0, 8)}`;
387
+ },
375
388
  };
376
389
  async function executeAction(ctx) {
377
390
  const short = ctx.agent.publicKey.slice(0, 8);
package/dist/index.d.ts CHANGED
@@ -4,5 +4,6 @@ export { assignPersonality, PERSONALITY_SOL, PERSONALITY_WEIGHTS, personalityDes
4
4
  export { ensureStronghold } from './stronghold';
5
5
  export { ensureRegistryProfile } from './registry';
6
6
  export { sendAndConfirm } from './tx';
7
+ export { executeScout, pendingScoutResults } from './agent';
7
8
  export { reconstructFromChain, computeWeightsFromHistory, classifyPersonality, weightsFromCounts, actionIndex } from './chain';
8
9
  export declare function createPyreAgent(config: PyreAgentConfig): Promise<PyreAgent>;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.actionIndex = exports.weightsFromCounts = exports.classifyPersonality = exports.computeWeightsFromHistory = exports.reconstructFromChain = exports.sendAndConfirm = exports.ensureRegistryProfile = exports.ensureStronghold = exports.VOICE_NUDGES = exports.personalityDesc = exports.PERSONALITY_WEIGHTS = exports.PERSONALITY_SOL = exports.assignPersonality = void 0;
3
+ exports.actionIndex = exports.weightsFromCounts = exports.classifyPersonality = exports.computeWeightsFromHistory = exports.reconstructFromChain = exports.pendingScoutResults = exports.executeScout = exports.sendAndConfirm = exports.ensureRegistryProfile = exports.ensureStronghold = exports.VOICE_NUDGES = exports.personalityDesc = exports.PERSONALITY_WEIGHTS = exports.PERSONALITY_SOL = exports.assignPersonality = void 0;
4
4
  exports.createPyreAgent = createPyreAgent;
5
5
  const pyre_world_kit_1 = require("pyre-world-kit");
6
6
  const defaults_1 = require("./defaults");
@@ -23,6 +23,9 @@ var registry_2 = require("./registry");
23
23
  Object.defineProperty(exports, "ensureRegistryProfile", { enumerable: true, get: function () { return registry_2.ensureRegistryProfile; } });
24
24
  var tx_1 = require("./tx");
25
25
  Object.defineProperty(exports, "sendAndConfirm", { enumerable: true, get: function () { return tx_1.sendAndConfirm; } });
26
+ var agent_2 = require("./agent");
27
+ Object.defineProperty(exports, "executeScout", { enumerable: true, get: function () { return agent_2.executeScout; } });
28
+ Object.defineProperty(exports, "pendingScoutResults", { enumerable: true, get: function () { return agent_2.pendingScoutResults; } });
26
29
  var chain_2 = require("./chain");
27
30
  Object.defineProperty(exports, "reconstructFromChain", { enumerable: true, get: function () { return chain_2.reconstructFromChain; } });
28
31
  Object.defineProperty(exports, "computeWeightsFromHistory", { enumerable: true, get: function () { return chain_2.computeWeightsFromHistory; } });
package/dist/types.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Connection, Keypair } from '@solana/web3.js';
2
2
  export type Personality = 'loyalist' | 'mercenary' | 'provocateur' | 'scout' | 'whale';
3
- export type Action = 'join' | 'defect' | 'rally' | 'launch' | 'message' | 'stronghold' | 'war_loan' | 'repay_loan' | 'siege' | 'ascend' | 'raze' | 'tithe' | 'infiltrate' | 'fud';
3
+ export type Action = 'join' | 'defect' | 'rally' | 'launch' | 'message' | 'stronghold' | 'war_loan' | 'repay_loan' | 'siege' | 'ascend' | 'raze' | 'tithe' | 'infiltrate' | 'fud' | 'scout';
4
4
  export interface LLMDecision {
5
5
  action: Action;
6
6
  faction?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pyre-agent-kit",
3
- "version": "2.0.23",
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",