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.
@@ -0,0 +1,24 @@
1
+ import { Connection } from '@solana/web3.js';
2
+ import { AgentState, FactionInfo, LLMAdapter, LLMDecision } from './types';
3
+ interface ExecutorContext {
4
+ connection: Connection;
5
+ agent: AgentState;
6
+ factions: FactionInfo[];
7
+ decision: LLMDecision;
8
+ brain: string;
9
+ log: (msg: string) => void;
10
+ llm?: LLMAdapter;
11
+ maxFoundedFactions: number;
12
+ usedFactionNames: Set<string>;
13
+ strongholdOpts?: {
14
+ fundSol?: number;
15
+ topupThresholdSol?: number;
16
+ topupReserveSol?: number;
17
+ };
18
+ }
19
+ export declare function executeAction(ctx: ExecutorContext): Promise<{
20
+ success: boolean;
21
+ description?: string;
22
+ error?: string;
23
+ }>;
24
+ export {};
@@ -0,0 +1,409 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.executeAction = executeAction;
4
+ const web3_js_1 = require("@solana/web3.js");
5
+ const spl_token_1 = require("@solana/spl-token");
6
+ const pyre_world_kit_1 = require("pyre-world-kit");
7
+ const defaults_1 = require("./defaults");
8
+ const tx_1 = require("./tx");
9
+ const stronghold_1 = require("./stronghold");
10
+ const action_1 = require("./action");
11
+ const error_1 = require("./error");
12
+ const faction_1 = require("./faction");
13
+ const findFaction = (factions, symbol) => factions.find(f => f.symbol === symbol);
14
+ const handlers = {
15
+ async join(ctx) {
16
+ const faction = findFaction(ctx.factions, ctx.decision.faction);
17
+ if (!faction)
18
+ return null;
19
+ const sol = ctx.decision.sol ?? (0, action_1.sentimentBuySize)(ctx.agent, faction.mint);
20
+ const lamports = Math.floor(sol * web3_js_1.LAMPORTS_PER_SOL);
21
+ await (0, stronghold_1.ensureStronghold)(ctx.connection, ctx.agent, ctx.log, ctx.strongholdOpts);
22
+ if (!ctx.agent.hasStronghold)
23
+ return null;
24
+ if (faction.status === 'ascended') {
25
+ const result = await (0, pyre_world_kit_1.tradeOnDex)(ctx.connection, {
26
+ mint: faction.mint, signer: ctx.agent.publicKey, stronghold_creator: ctx.agent.publicKey,
27
+ amount_in: lamports, minimum_amount_out: 1, is_buy: true, message: ctx.decision.message,
28
+ });
29
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
30
+ }
31
+ else {
32
+ const alreadyVoted = ctx.agent.voted.has(faction.mint);
33
+ const params = {
34
+ mint: faction.mint, agent: ctx.agent.publicKey, amount_sol: lamports,
35
+ message: ctx.decision.message, stronghold: ctx.agent.publicKey,
36
+ };
37
+ if (!alreadyVoted)
38
+ params.strategy = Math.random() > 0.5 ? 'fortify' : 'scorched_earth';
39
+ const result = await (0, pyre_world_kit_1.joinFaction)(ctx.connection, params);
40
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
41
+ }
42
+ const prev = ctx.agent.holdings.get(faction.mint) ?? 0;
43
+ ctx.agent.holdings.set(faction.mint, prev + 1);
44
+ ctx.agent.voted.add(faction.mint);
45
+ ctx.agent.lastAction = `joined ${faction.symbol}`;
46
+ return `joined ${faction.symbol} for ${sol.toFixed(4)} SOL${ctx.decision.message ? ` — "${ctx.decision.message}"` : ''}`;
47
+ },
48
+ async defect(ctx) {
49
+ const faction = findFaction(ctx.factions, ctx.decision.faction);
50
+ if (!faction)
51
+ return null;
52
+ let balance;
53
+ try {
54
+ const mint = new web3_js_1.PublicKey(faction.mint);
55
+ const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, new web3_js_1.PublicKey(ctx.agent.publicKey), false, spl_token_1.TOKEN_2022_PROGRAM_ID);
56
+ const info = await ctx.connection.getTokenAccountBalance(ata);
57
+ balance = Number(info.value.amount);
58
+ }
59
+ catch {
60
+ ctx.agent.holdings.delete(faction.mint);
61
+ return null;
62
+ }
63
+ if (balance <= 0) {
64
+ ctx.agent.holdings.delete(faction.mint);
65
+ return null;
66
+ }
67
+ const isInfiltrated = ctx.agent.infiltrated.has(faction.mint);
68
+ const sellPortion = isInfiltrated ? 1.0
69
+ : ctx.agent.personality === 'mercenary' ? 0.5 + Math.random() * 0.5
70
+ : 0.2 + Math.random() * 0.3;
71
+ const sellAmount = Math.max(1, Math.floor(balance * sellPortion));
72
+ if (faction.status === 'ascended') {
73
+ await (0, stronghold_1.ensureStronghold)(ctx.connection, ctx.agent, ctx.log, ctx.strongholdOpts);
74
+ if (!ctx.agent.hasStronghold)
75
+ return null;
76
+ const result = await (0, pyre_world_kit_1.tradeOnDex)(ctx.connection, {
77
+ mint: faction.mint, signer: ctx.agent.publicKey, stronghold_creator: ctx.agent.publicKey,
78
+ amount_in: sellAmount, minimum_amount_out: 1, is_buy: false, message: ctx.decision.message,
79
+ });
80
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
81
+ }
82
+ else {
83
+ const result = await (0, pyre_world_kit_1.defect)(ctx.connection, {
84
+ mint: faction.mint, agent: ctx.agent.publicKey, amount_tokens: sellAmount,
85
+ message: ctx.decision.message, stronghold: ctx.agent.publicKey,
86
+ });
87
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
88
+ }
89
+ const remaining = balance - sellAmount;
90
+ if (remaining <= 0) {
91
+ ctx.agent.holdings.delete(faction.mint);
92
+ ctx.agent.infiltrated.delete(faction.mint);
93
+ }
94
+ else {
95
+ ctx.agent.holdings.set(faction.mint, remaining);
96
+ }
97
+ const prefix = isInfiltrated ? 'dumped (infiltration complete)' : 'defected from';
98
+ ctx.agent.lastAction = `defected ${faction.symbol}`;
99
+ return `${prefix} ${faction.symbol}${ctx.decision.message ? ` — "${ctx.decision.message}"` : ''}`;
100
+ },
101
+ async rally(ctx) {
102
+ const faction = findFaction(ctx.factions, ctx.decision.faction);
103
+ if (!faction || ctx.agent.rallied.has(faction.mint))
104
+ return null;
105
+ const result = await (0, pyre_world_kit_1.rally)(ctx.connection, {
106
+ mint: faction.mint, agent: ctx.agent.publicKey, stronghold: ctx.agent.publicKey,
107
+ });
108
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
109
+ ctx.agent.rallied.add(faction.mint);
110
+ ctx.agent.lastAction = `rallied ${faction.symbol}`;
111
+ return `rallied ${faction.symbol}`;
112
+ },
113
+ async launch(ctx) {
114
+ if (ctx.agent.founded.length >= ctx.maxFoundedFactions)
115
+ return null;
116
+ let name = null;
117
+ let symbol = null;
118
+ const identity = await (0, faction_1.generateFactionIdentity)(ctx.agent.personality, ctx.usedFactionNames, ctx.llm);
119
+ if (identity) {
120
+ name = identity.name;
121
+ symbol = identity.symbol;
122
+ }
123
+ else {
124
+ for (let i = 0; i < faction_1.FALLBACK_FACTION_NAMES.length; i++) {
125
+ if (!ctx.usedFactionNames.has(faction_1.FALLBACK_FACTION_NAMES[i])) {
126
+ name = faction_1.FALLBACK_FACTION_NAMES[i];
127
+ symbol = faction_1.FALLBACK_FACTION_SYMBOLS[i];
128
+ break;
129
+ }
130
+ }
131
+ }
132
+ if (!name || !symbol)
133
+ return null;
134
+ const metadataUri = `https://pyre.gg/factions/${symbol.toLowerCase()}.json`;
135
+ const result = await (0, pyre_world_kit_1.launchFaction)(ctx.connection, {
136
+ founder: ctx.agent.publicKey, name, symbol, metadata_uri: metadataUri, community_faction: true,
137
+ });
138
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
139
+ const mint = result.mint.toBase58();
140
+ ctx.agent.founded.push(mint);
141
+ ctx.factions.push({ mint, name, symbol, status: 'rising' });
142
+ ctx.usedFactionNames.add(name);
143
+ ctx.agent.lastAction = `launched ${symbol}`;
144
+ return `launched [${symbol}] ${name} (${(0, pyre_world_kit_1.isPyreMint)(mint) ? 'py' : 'no-vanity'})`;
145
+ },
146
+ async message(ctx) {
147
+ const faction = findFaction(ctx.factions, ctx.decision.faction);
148
+ if (!faction || !ctx.decision.message)
149
+ return null;
150
+ await (0, stronghold_1.ensureStronghold)(ctx.connection, ctx.agent, ctx.log, ctx.strongholdOpts);
151
+ if (!ctx.agent.hasStronghold)
152
+ return null;
153
+ let result = await (0, pyre_world_kit_1.messageFaction)(ctx.connection, {
154
+ mint: faction.mint, agent: ctx.agent.publicKey, message: ctx.decision.message,
155
+ stronghold: ctx.agent.publicKey, ascended: faction.status === 'ascended',
156
+ first_buy: !ctx.agent.voted.has(faction.mint),
157
+ });
158
+ try {
159
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
160
+ }
161
+ catch (retryErr) {
162
+ const p = (0, error_1.parseCustomError)(retryErr);
163
+ if (p && p.code === 6008) {
164
+ ctx.agent.voted.add(faction.mint);
165
+ result = await (0, pyre_world_kit_1.messageFaction)(ctx.connection, {
166
+ mint: faction.mint, agent: ctx.agent.publicKey, message: ctx.decision.message,
167
+ stronghold: ctx.agent.publicKey, ascended: faction.status === 'ascended', first_buy: false,
168
+ });
169
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
170
+ }
171
+ else {
172
+ throw retryErr;
173
+ }
174
+ }
175
+ const prev = ctx.agent.holdings.get(faction.mint) ?? 0;
176
+ ctx.agent.holdings.set(faction.mint, prev + 1);
177
+ ctx.agent.voted.add(faction.mint);
178
+ ctx.agent.lastAction = `messaged ${faction.symbol}`;
179
+ return `said in ${faction.symbol}: "${ctx.decision.message}"`;
180
+ },
181
+ async stronghold(ctx) {
182
+ if (ctx.agent.hasStronghold)
183
+ return null;
184
+ const result = await (0, pyre_world_kit_1.createStronghold)(ctx.connection, { creator: ctx.agent.publicKey });
185
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
186
+ ctx.agent.hasStronghold = true;
187
+ const fundAmt = Math.floor((ctx.strongholdOpts?.fundSol ?? defaults_1.STRONGHOLD_FUND_SOL) * web3_js_1.LAMPORTS_PER_SOL);
188
+ try {
189
+ const fundResult = await (0, pyre_world_kit_1.fundStronghold)(ctx.connection, {
190
+ depositor: ctx.agent.publicKey, stronghold_creator: ctx.agent.publicKey, amount_sol: fundAmt,
191
+ });
192
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, fundResult);
193
+ }
194
+ catch { /* fund failed, stronghold still created */ }
195
+ ctx.agent.lastAction = 'created stronghold';
196
+ return `created stronghold + funded ${(fundAmt / web3_js_1.LAMPORTS_PER_SOL).toFixed(1)} SOL`;
197
+ },
198
+ async war_loan(ctx) {
199
+ const faction = findFaction(ctx.factions, ctx.decision.faction);
200
+ if (!faction)
201
+ return null;
202
+ const balance = ctx.agent.holdings.get(faction.mint) ?? 0;
203
+ if (balance <= 0)
204
+ return null;
205
+ const collateral = Math.max(1, Math.floor(balance * (0.90 + Math.random() * 0.09)));
206
+ let borrowLamports;
207
+ try {
208
+ const quote = await (0, pyre_world_kit_1.getMaxWarLoan)(ctx.connection, faction.mint, collateral);
209
+ if (quote.max_borrow_sol < 0.1 * web3_js_1.LAMPORTS_PER_SOL)
210
+ return null;
211
+ borrowLamports = Math.floor(quote.max_borrow_sol * (0.80 + Math.random() * 0.15));
212
+ }
213
+ catch {
214
+ borrowLamports = Math.floor(0.1 * web3_js_1.LAMPORTS_PER_SOL);
215
+ }
216
+ await (0, stronghold_1.ensureStronghold)(ctx.connection, ctx.agent, ctx.log, ctx.strongholdOpts);
217
+ const result = await (0, pyre_world_kit_1.requestWarLoan)(ctx.connection, {
218
+ mint: faction.mint, borrower: ctx.agent.publicKey, collateral_amount: collateral,
219
+ sol_to_borrow: borrowLamports, stronghold: ctx.agent.publicKey,
220
+ });
221
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
222
+ ctx.agent.activeLoans.add(faction.mint);
223
+ ctx.agent.lastAction = `war loan ${faction.symbol}`;
224
+ return `took war loan on ${faction.symbol} (${collateral} tokens, ${(borrowLamports / web3_js_1.LAMPORTS_PER_SOL).toFixed(3)} SOL)`;
225
+ },
226
+ async repay_loan(ctx) {
227
+ const faction = findFaction(ctx.factions, ctx.decision.faction);
228
+ if (!faction || !ctx.agent.activeLoans.has(faction.mint))
229
+ return null;
230
+ let loan;
231
+ try {
232
+ loan = await (0, pyre_world_kit_1.getWarLoan)(ctx.connection, faction.mint, ctx.agent.publicKey);
233
+ }
234
+ catch {
235
+ return null;
236
+ }
237
+ if (loan.total_owed <= 0) {
238
+ ctx.agent.activeLoans.delete(faction.mint);
239
+ return null;
240
+ }
241
+ const result = await (0, pyre_world_kit_1.repayWarLoan)(ctx.connection, {
242
+ mint: faction.mint, borrower: ctx.agent.publicKey,
243
+ sol_amount: Math.ceil(loan.total_owed), stronghold: ctx.agent.publicKey,
244
+ });
245
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
246
+ ctx.agent.activeLoans.delete(faction.mint);
247
+ ctx.agent.lastAction = `repaid loan ${faction.symbol}`;
248
+ return `repaid war loan on ${faction.symbol} (${(loan.total_owed / web3_js_1.LAMPORTS_PER_SOL).toFixed(4)} SOL)`;
249
+ },
250
+ async siege(ctx) {
251
+ const faction = findFaction(ctx.factions, ctx.decision.faction);
252
+ if (!faction)
253
+ return null;
254
+ let targetBorrower = null;
255
+ try {
256
+ const allLoans = await (0, pyre_world_kit_1.getAllWarLoans)(ctx.connection, faction.mint);
257
+ for (const pos of allLoans.positions) {
258
+ if (pos.health === 'liquidatable') {
259
+ targetBorrower = pos.borrower;
260
+ break;
261
+ }
262
+ }
263
+ }
264
+ catch {
265
+ return null;
266
+ }
267
+ if (!targetBorrower)
268
+ return null;
269
+ const result = await (0, pyre_world_kit_1.siege)(ctx.connection, {
270
+ mint: faction.mint, liquidator: ctx.agent.publicKey,
271
+ borrower: targetBorrower, stronghold: ctx.agent.publicKey,
272
+ });
273
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
274
+ ctx.agent.lastAction = `siege ${faction.symbol}`;
275
+ return `sieged ${targetBorrower.slice(0, 8)}... in ${faction.symbol} (liquidation)`;
276
+ },
277
+ async ascend(ctx) {
278
+ const faction = findFaction(ctx.factions, ctx.decision.faction);
279
+ if (!faction || faction.status !== 'ready')
280
+ return null;
281
+ const result = await (0, pyre_world_kit_1.ascend)(ctx.connection, { mint: faction.mint, payer: ctx.agent.publicKey });
282
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
283
+ faction.status = 'ascended';
284
+ ctx.agent.lastAction = `ascended ${faction.symbol}`;
285
+ return `ascended ${faction.symbol} to DEX`;
286
+ },
287
+ async raze(ctx) {
288
+ const faction = findFaction(ctx.factions, ctx.decision.faction);
289
+ if (!faction)
290
+ return null;
291
+ const result = await (0, pyre_world_kit_1.raze)(ctx.connection, { payer: ctx.agent.publicKey, mint: faction.mint });
292
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
293
+ ctx.agent.lastAction = `razed ${faction.symbol}`;
294
+ return `razed ${faction.symbol} (reclaimed)`;
295
+ },
296
+ async tithe(ctx) {
297
+ const faction = findFaction(ctx.factions, ctx.decision.faction);
298
+ if (!faction)
299
+ return null;
300
+ try {
301
+ const result = await (0, pyre_world_kit_1.convertTithe)(ctx.connection, { mint: faction.mint, payer: ctx.agent.publicKey, harvest: true });
302
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
303
+ }
304
+ catch {
305
+ const result = await (0, pyre_world_kit_1.tithe)(ctx.connection, { mint: faction.mint, payer: ctx.agent.publicKey });
306
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
307
+ }
308
+ ctx.agent.lastAction = `tithed ${faction.symbol}`;
309
+ return `tithed ${faction.symbol} (harvested fees)`;
310
+ },
311
+ async infiltrate(ctx) {
312
+ const faction = findFaction(ctx.factions, ctx.decision.faction);
313
+ if (!faction)
314
+ return null;
315
+ const sol = ctx.decision.sol ?? (0, action_1.sentimentBuySize)(ctx.agent, faction.mint) * 1.5;
316
+ const lamports = Math.floor(sol * web3_js_1.LAMPORTS_PER_SOL);
317
+ await (0, stronghold_1.ensureStronghold)(ctx.connection, ctx.agent, ctx.log, ctx.strongholdOpts);
318
+ if (!ctx.agent.hasStronghold)
319
+ return null;
320
+ if (faction.status === 'ascended') {
321
+ const result = await (0, pyre_world_kit_1.tradeOnDex)(ctx.connection, {
322
+ mint: faction.mint, signer: ctx.agent.publicKey, stronghold_creator: ctx.agent.publicKey,
323
+ amount_in: lamports, minimum_amount_out: 1, is_buy: true, message: ctx.decision.message,
324
+ });
325
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
326
+ }
327
+ else {
328
+ const alreadyVoted = ctx.agent.voted.has(faction.mint);
329
+ const params = {
330
+ mint: faction.mint, agent: ctx.agent.publicKey, amount_sol: lamports,
331
+ message: ctx.decision.message, stronghold: ctx.agent.publicKey,
332
+ };
333
+ if (!alreadyVoted)
334
+ params.strategy = 'scorched_earth';
335
+ const result = await (0, pyre_world_kit_1.joinFaction)(ctx.connection, params);
336
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
337
+ }
338
+ const prev = ctx.agent.holdings.get(faction.mint) ?? 0;
339
+ ctx.agent.holdings.set(faction.mint, prev + 1);
340
+ ctx.agent.infiltrated.add(faction.mint);
341
+ ctx.agent.voted.add(faction.mint);
342
+ ctx.agent.sentiment.set(faction.mint, -5);
343
+ ctx.agent.lastAction = `infiltrated ${faction.symbol}`;
344
+ return `infiltrated ${faction.symbol} for ${sol.toFixed(4)} SOL — "${ctx.decision.message}"`;
345
+ },
346
+ async fud(ctx) {
347
+ const faction = findFaction(ctx.factions, ctx.decision.faction);
348
+ if (!faction || !ctx.decision.message || !ctx.agent.holdings.has(faction.mint))
349
+ return null;
350
+ await (0, stronghold_1.ensureStronghold)(ctx.connection, ctx.agent, ctx.log, ctx.strongholdOpts);
351
+ if (!ctx.agent.hasStronghold)
352
+ return null;
353
+ const result = await (0, pyre_world_kit_1.fudFaction)(ctx.connection, {
354
+ mint: faction.mint, agent: ctx.agent.publicKey, message: ctx.decision.message,
355
+ stronghold: ctx.agent.publicKey, ascended: faction.status === 'ascended',
356
+ });
357
+ await (0, tx_1.sendAndConfirm)(ctx.connection, ctx.agent.keypair, result);
358
+ ctx.agent.lastAction = `fud ${faction.symbol}`;
359
+ return `argued in ${faction.symbol}: "${ctx.decision.message}"`;
360
+ },
361
+ };
362
+ async function executeAction(ctx) {
363
+ const short = ctx.agent.publicKey.slice(0, 8);
364
+ try {
365
+ const handler = handlers[ctx.decision.action];
366
+ if (!handler)
367
+ return { success: false, error: `unknown action: ${ctx.decision.action}` };
368
+ const desc = await handler(ctx);
369
+ if (!desc)
370
+ return { success: false, error: 'action precondition not met' };
371
+ ctx.agent.recentHistory.push(desc);
372
+ if (ctx.agent.recentHistory.length > 10)
373
+ ctx.agent.recentHistory = ctx.agent.recentHistory.slice(-10);
374
+ ctx.agent.actionCount++;
375
+ ctx.log(`[${short}] [${ctx.agent.personality}] [${ctx.brain}] ${desc}`);
376
+ return { success: true, description: desc };
377
+ }
378
+ catch (err) {
379
+ const parsed = (0, error_1.parseCustomError)(err);
380
+ if (parsed) {
381
+ const sym = ctx.decision.faction ?? '?';
382
+ ctx.log(`[${short}] [${ctx.agent.personality}] [${ctx.brain}] ERROR (${ctx.decision.action} ${sym}): ${parsed.name} [0x${parsed.code.toString(16)}]`);
383
+ // Adapt behavior based on error
384
+ if (parsed.code === 6002 && ctx.decision.faction) {
385
+ const f = findFaction(ctx.factions, ctx.decision.faction);
386
+ if (f)
387
+ ctx.agent.sentiment.set(f.mint, (ctx.agent.sentiment.get(f.mint) ?? 0) + 1);
388
+ }
389
+ else if (parsed.code === 6055) {
390
+ ctx.agent.recentHistory.push('vault empty — need funds');
391
+ }
392
+ else if (parsed.code === 6051) {
393
+ const f = findFaction(ctx.factions, ctx.decision.faction);
394
+ if (f)
395
+ ctx.agent.sentiment.set(f.mint, (ctx.agent.sentiment.get(f.mint) ?? 0) + 2);
396
+ }
397
+ else if (parsed.code === 6046) {
398
+ ctx.agent.recentHistory.push(`loan rejected on ${ctx.decision.faction} — LTV too high`);
399
+ }
400
+ else if (parsed.code === 6049) {
401
+ ctx.agent.recentHistory.push(`loan too small on ${ctx.decision.faction} — min 0.1 SOL`);
402
+ }
403
+ return { success: false, error: `${parsed.name} [0x${parsed.code.toString(16)}]` };
404
+ }
405
+ const msg = err.message?.slice(0, 120) ?? String(err);
406
+ ctx.log(`[${short}] [${ctx.agent.personality}] [${ctx.brain}] ERROR (${ctx.decision.action}): ${msg}`);
407
+ return { success: false, error: msg };
408
+ }
409
+ }
@@ -0,0 +1,10 @@
1
+ import { Connection } from '@solana/web3.js';
2
+ import { AgentState, FactionInfo, FactionIntel, LLMAdapter, Personality } from './types';
3
+ export declare const FALLBACK_FACTION_NAMES: string[];
4
+ export declare const FALLBACK_FACTION_SYMBOLS: string[];
5
+ export declare const generateFactionIdentity: (personality: Personality, existingNames: Set<string>, llm?: LLMAdapter) => Promise<{
6
+ name: string;
7
+ symbol: string;
8
+ } | null>;
9
+ export declare const fetchFactionIntel: (connection: Connection, faction: FactionInfo) => Promise<FactionIntel>;
10
+ export declare const generateDynamicExamples: (factions: FactionInfo[], _agent: AgentState) => string;
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateDynamicExamples = exports.fetchFactionIntel = exports.generateFactionIdentity = exports.FALLBACK_FACTION_SYMBOLS = exports.FALLBACK_FACTION_NAMES = void 0;
4
+ const pyre_world_kit_1 = require("pyre-world-kit");
5
+ const util_1 = require("./util");
6
+ // Fallback faction names/symbols — only used when LLM is unavailable
7
+ exports.FALLBACK_FACTION_NAMES = [
8
+ 'Iron Vanguard', 'Obsidian Order', 'Crimson Dawn', 'Shadow Covenant',
9
+ 'Ember Collective', 'Void Walkers', 'Solar Reign', 'Frost Legion',
10
+ 'Thunder Pact', 'Ash Republic', 'Neon Syndicate', 'Storm Brigade',
11
+ 'Lunar Assembly', 'Flame Sentinels', 'Dark Meridian', 'Phoenix Accord',
12
+ 'Steel Dominion', 'Crystal Enclave', 'Rogue Alliance', 'Titan Front',
13
+ ];
14
+ exports.FALLBACK_FACTION_SYMBOLS = [
15
+ 'IRON', 'OBSD', 'CRIM', 'SHAD', 'EMBR', 'VOID', 'SOLR', 'FRST',
16
+ 'THDR', 'ASHR', 'NEON', 'STRM', 'LUNR', 'FLMS', 'DARK', 'PHNX',
17
+ 'STEL', 'CRYS', 'ROGU', 'TITN',
18
+ ];
19
+ const generateFactionIdentity = async (personality, existingNames, llm) => {
20
+ if (!llm)
21
+ return null;
22
+ const existing = [...existingNames].slice(0, 15).join(', ');
23
+ const prompt = `You are naming a new faction in Pyre, a faction warfare game on Solana.
24
+
25
+ Your personality: ${personality}
26
+
27
+ Existing factions (DO NOT reuse these): ${existing || 'none yet'}
28
+
29
+ Generate a faction name and ticker symbol. The name should be 2-3 words, evocative, and feel like a militant organization, secret society, or political movement. The ticker should be 3-5 uppercase letters that abbreviate or represent the name.
30
+
31
+ Respond with EXACTLY one line in this format:
32
+ NAME | TICKER
33
+
34
+ Examples:
35
+ Obsidian Vanguard | OBSD
36
+ Neon Syndicate | NEON
37
+ Crimson Dawn | CRIM
38
+ Void Collective | VOID
39
+ Ash Republic | ASHR
40
+
41
+ Your response (one line only):`;
42
+ const raw = await llm.generate(prompt);
43
+ if (!raw)
44
+ return null;
45
+ const line = raw.split('\n').find(l => l.includes('|'));
46
+ if (!line)
47
+ return null;
48
+ const parts = line.split('|').map(s => s.trim());
49
+ if (parts.length !== 2)
50
+ return null;
51
+ const name = parts[0].replace(/^["']|["']$/g, '').trim();
52
+ let symbol = parts[1].replace(/^["']|["']$/g, '').trim().toUpperCase();
53
+ if (!name || name.length < 3 || name.length > 32)
54
+ return null;
55
+ if (!symbol || symbol.length < 3 || symbol.length > 5)
56
+ return null;
57
+ symbol = symbol.replace(/[^A-Z]/g, '').slice(0, 5);
58
+ if (symbol.length < 3)
59
+ return null;
60
+ if (existingNames.has(name))
61
+ return null;
62
+ return { name, symbol };
63
+ };
64
+ exports.generateFactionIdentity = generateFactionIdentity;
65
+ const fetchFactionIntel = async (connection, faction) => {
66
+ const [membersResult, commsResult] = await Promise.all([
67
+ (0, pyre_world_kit_1.getMembers)(connection, faction.mint, 10).catch(() => ({ members: [], total_members: 0 })),
68
+ (0, pyre_world_kit_1.getComms)(connection, faction.mint, 5, faction.status).catch(() => ({ comms: [], total: 0 })),
69
+ ]);
70
+ return {
71
+ symbol: faction.symbol,
72
+ members: membersResult.members.map(m => ({ address: m.address, percentage: m.percentage })),
73
+ totalMembers: membersResult.total_members,
74
+ recentComms: commsResult.comms.map(c => ({ sender: c.sender, memo: c.memo })),
75
+ };
76
+ };
77
+ exports.fetchFactionIntel = fetchFactionIntel;
78
+ const generateDynamicExamples = (factions, _agent) => {
79
+ const syms = factions.map(f => f.symbol);
80
+ const s1 = syms.length > 0 ? (0, util_1.pick)(syms) : 'IRON';
81
+ const s2 = syms.length > 1 ? (0, util_1.pick)(syms.filter(s => s !== s1)) : 'VOID';
82
+ const addr = Math.random().toString(36).slice(2, 10);
83
+ const pct = Math.floor(Math.random() * 45 + 5);
84
+ const members = Math.floor(Math.random() * 30 + 3);
85
+ const messageExamples = [
86
+ `MESSAGE ${s1} "${s2} is gaining power, we need more resources."`,
87
+ `MESSAGE ${s2} "who else noticed @${addr} is gathering resources?"`,
88
+ `MESSAGE ${s1} "top member holds ${pct}%, resources concentrated"`,
89
+ `MESSAGE ${s1} "what's our strategy against ${s2}?"`,
90
+ `FUD ${s2} "only ${members} members, dead faction"`,
91
+ `FUD ${s1} "treasury growing but where's the activity?"`,
92
+ `FUD ${s1} "@${addr} you better watch your back"`,
93
+ ];
94
+ const actionExamples = [
95
+ `JOIN ${s1} "deploying capital, let's build this"`,
96
+ `JOIN ${s2} "following @${addr} into this one"`,
97
+ `JOIN ${s1} "@${addr}, ready to form an alliance if you are"`,
98
+ `DEFECT ${s1} "${members} are losing faith, taking profits"`,
99
+ `DEFECT ${s2} "saw @${addr} dump ${pct}%, I'm out"`,
100
+ `FUD ${s2} "@${addr} has been quiet, what are they planning?"`,
101
+ `INFILTRATE ${s2} "this one's undervalued, sneaking in"`,
102
+ `RALLY ${s1}`,
103
+ `WAR_LOAN ${s1}`,
104
+ ];
105
+ // 3 actions, 2 messages
106
+ const msgShuffled = messageExamples.sort(() => Math.random() - 0.5).slice(0, 2);
107
+ const actShuffled = actionExamples.sort(() => Math.random() - 0.5).slice(0, 3);
108
+ return [...actShuffled, ...msgShuffled].sort(() => Math.random() - 0.5).join('\n');
109
+ };
110
+ exports.generateDynamicExamples = generateDynamicExamples;
@@ -0,0 +1,6 @@
1
+ import { PyreAgentConfig, PyreAgent } from './types';
2
+ export type { PyreAgentConfig, PyreAgent, AgentTickResult, SerializedAgentState, LLMAdapter, LLMDecision, FactionInfo, Personality, Action, AgentState, } from './types';
3
+ export { assignPersonality, PERSONALITY_SOL, PERSONALITY_WEIGHTS, personalityDesc, VOICE_NUDGES } from './defaults';
4
+ export { ensureStronghold } from './stronghold';
5
+ export { sendAndConfirm } from './tx';
6
+ export declare function createPyreAgent(config: PyreAgentConfig): Promise<PyreAgent>;