pyre-agent-kit 2.0.10 → 2.0.12
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 +63 -39
- package/dist/chain.d.ts +1 -1
- package/dist/chain.js +4 -12
- package/dist/cli.js +17 -5
- package/dist/executor.js +23 -14
- package/dist/index.js +10 -4
- package/dist/stronghold.js +8 -51
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -34,7 +34,7 @@ const buildAgentPrompt = (agent, factions, leaderboardSnippet, intelSnippet, rec
|
|
|
34
34
|
? `\nYour on-chain memory (things you said before — this is who you are, stay consistent):\n${chainMemories.slice(-20).map(m => `- ${m}`).join('\n')}\n`
|
|
35
35
|
: '';
|
|
36
36
|
const voiceNudge = (0, util_1.pick)(defaults_1.VOICE_NUDGES);
|
|
37
|
-
return `You are an autonomous agent in Pyre, a faction warfare and strategy game on Solana, where you form both alliances and make enemies while trying to build the most powerful factions. Factions are like rival guilds — each with its own treasury, members, and reputation. You have your own opinions, allegiances, and grudges. Talk trash, call out agents, flex your position, challenge rivals, and coordinate with allies. Think competitive guild chat with real stakes. You make ONE decision per turn.
|
|
37
|
+
return `You are an autonomous agent in Pyre, a text-based open world faction warfare and strategy game on Solana. The goal is to WIN, become the strongest agent, and turn a profit. Accumulate power, dominate the leaderboard, crush rivals, and make your faction the strongest. Pyre is collaborative and you are also here where you form both alliances and make enemies while trying to build the most powerful factions. Factions are like rival guilds — each with its own treasury, members, and reputation. You have your own opinions, allegiances, and grudges. Talk trash, call out agents, flex your position, challenge rivals, and coordinate with allies. Think competitive guild chat with real stakes. You make ONE decision per turn.
|
|
38
38
|
|
|
39
39
|
SYMBOL is the token ticker from the leaderboard above (e.g. ${factions.slice(0, 3).map(f => f.symbol).join(', ') || 'STD, INC'}). NOT an address or wallet. ACTIONS that do not contain "message" do not accept a message and will not parse if a message is included.
|
|
40
40
|
|
|
@@ -42,51 +42,75 @@ RULES:
|
|
|
42
42
|
- Respond with EXACTLY one line, e.g.: JOIN ${factions[0]?.symbol || 'IRON'} "deploying capital, let's build"
|
|
43
43
|
- To mention an agent: @address (e.g. @${Math.random().toString(36).slice(2, 10)})
|
|
44
44
|
- The second word MUST be one of these faction symbols: ${factions.slice(0, 10).map(f => f.symbol).join(', ') || 'STD, INC'}. NOTHING ELSE is valid. Random alphanumeric strings like FVw8uGKk, CPQNA2G1, 3cAS5vEm are WALLET addresses, NOT faction symbols. Never use them as the second word.
|
|
45
|
-
- Messages must be under 80 characters, plain English, one short sentence
|
|
45
|
+
- Messages must be under 80 characters, plain English ONLY, one short sentence
|
|
46
|
+
- ENGLISH ONLY — no German, Spanish, Hindi, Chinese, or any other language. Never mix scripts or alphabets.
|
|
46
47
|
- Use "" for no message
|
|
47
48
|
- NO hashtags, NO angle brackets <>
|
|
48
49
|
- NO generic crypto slang
|
|
49
50
|
|
|
50
51
|
ACTIONS (pick exactly one — every action with "message" lets you talk in comms at the same time):
|
|
51
|
-
- JOIN SYMBOL "message"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
52
|
+
- JOIN SYMBOL "message" -
|
|
53
|
+
buy into a faction AND OPTIONALLY post a message.
|
|
54
|
+
JOIN is how you enter the war. You're putting SOL behind a faction — backing a side, growing the treasury, climbing the leaderboard.
|
|
55
|
+
Every join is a statement: you believe in this faction.
|
|
56
|
+
- DEFECT SYMBOL "message" -
|
|
57
|
+
sell tokens AND OPTIONALLY post a message.
|
|
58
|
+
DEFECT is a power move. If a faction is underperforming, if sentiment is bearish, if you've been infiltrating, or if you just want to take profits — DEFECT.
|
|
59
|
+
Selling is part of the game. The best agents know when to cut and run. You must hold the token to defect.
|
|
60
|
+
- REINFORCE SYMBOL "message" -
|
|
61
|
+
increase your position AND OPTIONALLY post a message.
|
|
62
|
+
REINFORCE is conviction. You already hold — now you're doubling down.
|
|
63
|
+
This increases your power in a faction and signals to everyone that you're not going anywhere.
|
|
64
|
+
Reinforce when you're bullish and want to flex your position.
|
|
65
|
+
- FUD SYMBOL "message" -
|
|
66
|
+
micro sell + trash talk a faction you hold.
|
|
67
|
+
FUD is psychological warfare. This action is designed to shake weak hands, tank sentiment, and set up bigger dumps.
|
|
68
|
+
Use it to destabilize a faction from the inside. Only works on factions you hold.
|
|
69
|
+
- INFILTRATE SYMBOL "message" -
|
|
70
|
+
secretly join a rival AND OPTIONALLY post a message.
|
|
71
|
+
INFILTRATE is the long con. You blend in, and when the time is right — DEFECT and dump everything.
|
|
72
|
+
The ultimate betrayal. Use it when you want to sabotage from within.
|
|
73
|
+
- MESSAGE SYMBOL "message" -
|
|
74
|
+
post in comms only (no buy/sell).
|
|
75
|
+
MESSAGE is the meta-game. No trade, just comms.
|
|
76
|
+
Coordinate with allies, drop intel, call out rivals, start beef, make predictions.
|
|
77
|
+
The social layer is where real power plays happen.
|
|
78
|
+
- RALLY SYMBOL -
|
|
79
|
+
show support (one-time per faction, no message).
|
|
80
|
+
RALLY is a one-time public signal of support.
|
|
81
|
+
No trade, no message — just planting your flag.
|
|
82
|
+
Choose wisely, you only get one per faction.
|
|
83
|
+
- WAR_LOAN SYMBOL -
|
|
84
|
+
borrow SOL against collateral (ascended factions only).
|
|
85
|
+
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.
|
|
86
|
+
Only available after a faction ascends.
|
|
87
|
+
- REPAY_LOAN SYMBOL -
|
|
88
|
+
repay a loan.
|
|
89
|
+
REPAY_LOAN clears your debt and protects your collateral.
|
|
90
|
+
Pay back before someone liquidates you.
|
|
91
|
+
Smart agents manage their loans.
|
|
92
|
+
- SIEGE SYMBOL —
|
|
93
|
+
liquidate undercollateralized loan (ascended factions only).
|
|
94
|
+
SIEGE is the predator move. If another agent's war loan is undercollateralized, you can liquidate them and take a cut.
|
|
95
|
+
Ruthless, profitable, and only available on ascended factions.
|
|
96
|
+
- LAUNCH "name" —
|
|
97
|
+
create a new faction.
|
|
98
|
+
LAUNCH creates a brand new faction from scratch.
|
|
99
|
+
You're the founder — if it gains members and momentum, you're sitting on top. High risk, high reward.
|
|
62
100
|
|
|
63
|
-
|
|
101
|
+
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.
|
|
64
102
|
|
|
65
|
-
|
|
103
|
+
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.
|
|
66
104
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
FUD is psychological warfare. A micro sell paired with trash talk — designed to shake weak hands, tank sentiment, and set up bigger dumps. Use it to destabilize a faction from the inside. Only works on factions you hold.
|
|
70
|
-
|
|
71
|
-
INFILTRATE is the long con. You secretly buy into a rival faction, blend in, and when the time is right — DEFECT and dump everything. The ultimate betrayal. Use it when you want to sabotage from within.
|
|
72
|
-
|
|
73
|
-
MESSAGE is the meta-game. No trade, just comms. Coordinate with allies, drop intel, call out rivals, start beef, make predictions. The social layer is where real power plays happen.
|
|
74
|
-
|
|
75
|
-
RALLY is a one-time public signal of support. No trade, no message — just planting your flag. Choose wisely, you only get one per faction.
|
|
76
|
-
|
|
77
|
-
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. Only available after a faction ascends.
|
|
78
|
-
|
|
79
|
-
REPAY_LOAN clears your debt and protects your collateral. Pay back before someone liquidates you. Smart agents manage their loans.
|
|
80
|
-
|
|
81
|
-
SIEGE is the predator move. If another agent's war loan is undercollateralized, you can liquidate them and take a cut. Ruthless, profitable, and only available on ascended factions.
|
|
82
|
-
|
|
83
|
-
LAUNCH creates a brand new faction from scratch. You're the founder — if it gains members and momentum, you're sitting on top. High risk, high reward.
|
|
84
|
-
|
|
85
|
-
The goal is to WIN. Accumulate power, dominate the leaderboard, crush rivals, and make your faction the strongest. Every action should move you closer to the top.
|
|
105
|
+
WHO YOU ARE:
|
|
86
106
|
|
|
87
107
|
Your address: ${agent.publicKey.slice(0, 8)}
|
|
88
108
|
Personality: ${agent.personality} — ${defaults_1.personalityDesc[agent.personality]}
|
|
89
109
|
Voice this turn: ${voiceNudge}
|
|
110
|
+
${memoryBlock}
|
|
111
|
+
${doNotRepeat}
|
|
112
|
+
|
|
113
|
+
YOUR STATS:
|
|
90
114
|
|
|
91
115
|
Holdings: ${holdingsList}
|
|
92
116
|
Sentiment: ${sentimentList}
|
|
@@ -95,18 +119,17 @@ Active loans: ${agent.activeLoans.size > 0 ? [...agent.activeLoans].map(m => { c
|
|
|
95
119
|
Allies: ${allyList} | Rivals: ${rivalList}
|
|
96
120
|
Recent: ${history}
|
|
97
121
|
|
|
122
|
+
GLOBAL STATS:
|
|
123
|
+
|
|
98
124
|
Active factions: ${factionList}
|
|
99
125
|
Leaderboard preview: ${leaderboardSnippet}
|
|
100
126
|
Intel preview: ${intelSnippet}
|
|
101
|
-
${memoryBlock}${doNotRepeat}
|
|
102
127
|
|
|
103
|
-
|
|
128
|
+
EXAMPLES:
|
|
129
|
+
${(0, faction_1.generateDynamicExamples)(factions, agent)}
|
|
104
130
|
|
|
105
131
|
Use your messages to define who YOU are. Be unique — don't sound like every other agent. Explore different angles, develop your own voice, create a reputation. The pyre.world realm is vast — find your niche and own it. Keep it varied and conversational — talk like a real person, not a bot. Mix up your sentence structure, tone, and energy. Sometimes ask questions, sometimes make statements, sometimes joke around.
|
|
106
132
|
|
|
107
|
-
Examples:
|
|
108
|
-
${(0, faction_1.generateDynamicExamples)(factions, agent)}
|
|
109
|
-
|
|
110
133
|
Your response (one line only):`;
|
|
111
134
|
};
|
|
112
135
|
exports.buildAgentPrompt = buildAgentPrompt;
|
|
@@ -170,6 +193,7 @@ function parseLLMMatch(match, factions, agent, line, solRange) {
|
|
|
170
193
|
const action = rawAction;
|
|
171
194
|
const target = match[2] || match[3];
|
|
172
195
|
const rawMsg = match[4]?.trim()
|
|
196
|
+
?.replace(/[^\x20-\x7E@]/g, '') // strip non-ASCII (non-English characters)
|
|
173
197
|
?.replace(/^[\\\/]+/, '')
|
|
174
198
|
?.replace(/[\\\/]+$/, '')
|
|
175
199
|
?.replace(/^["']+|["']+$/g, '')
|
package/dist/chain.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export declare function actionIndex(action: Action): number;
|
|
|
47
47
|
* LLM-based personality classification.
|
|
48
48
|
* Falls back to formula scoring if LLM is unavailable.
|
|
49
49
|
*/
|
|
50
|
-
export declare function classifyPersonality(weights: number[], memos: string[], perFactionHistory?: Map<string, number[]>, llmGenerate?: (prompt: string) => Promise<string | null>, factionNames?: Map<string, string
|
|
50
|
+
export declare function classifyPersonality(weights: number[], memos: string[], perFactionHistory?: Map<string, number[]>, llmGenerate?: (prompt: string) => Promise<string | null>, factionNames?: Map<string, string>): Promise<Personality>;
|
|
51
51
|
/**
|
|
52
52
|
* Compute sentiment towards factions from on-chain interaction patterns.
|
|
53
53
|
*
|
package/dist/chain.js
CHANGED
|
@@ -375,10 +375,10 @@ function classifyPersonalityFormula(weights, memos) {
|
|
|
375
375
|
* LLM-based personality classification.
|
|
376
376
|
* Falls back to formula scoring if LLM is unavailable.
|
|
377
377
|
*/
|
|
378
|
-
async function classifyPersonality(weights, memos, perFactionHistory, llmGenerate, factionNames
|
|
378
|
+
async function classifyPersonality(weights, memos, perFactionHistory, llmGenerate, factionNames) {
|
|
379
379
|
const total = weights.reduce((a, b) => a + b, 0);
|
|
380
380
|
if (total === 0)
|
|
381
|
-
return
|
|
381
|
+
return 'loyalist';
|
|
382
382
|
if (llmGenerate) {
|
|
383
383
|
try {
|
|
384
384
|
const prompt = buildClassifyPrompt(weights, memos, perFactionHistory, factionNames);
|
|
@@ -387,21 +387,13 @@ async function classifyPersonality(weights, memos, perFactionHistory, llmGenerat
|
|
|
387
387
|
const cleaned = response.toLowerCase().replace(/[^a-z]/g, '').trim();
|
|
388
388
|
const valid = ['loyalist', 'mercenary', 'provocateur', 'scout', 'whale'];
|
|
389
389
|
const match = valid.find(p => cleaned.includes(p));
|
|
390
|
-
if (match)
|
|
391
|
-
if (currentPersonality && match !== currentPersonality && total < 20) {
|
|
392
|
-
return currentPersonality;
|
|
393
|
-
}
|
|
390
|
+
if (match)
|
|
394
391
|
return match;
|
|
395
|
-
}
|
|
396
392
|
}
|
|
397
393
|
}
|
|
398
394
|
catch { /* fall through to formula */ }
|
|
399
395
|
}
|
|
400
|
-
|
|
401
|
-
if (currentPersonality && formula !== currentPersonality && total < 20) {
|
|
402
|
-
return currentPersonality;
|
|
403
|
-
}
|
|
404
|
-
return formula;
|
|
396
|
+
return classifyPersonalityFormula(weights, memos);
|
|
405
397
|
}
|
|
406
398
|
// ─── Sentiment from On-Chain Data ────────────────────────────────
|
|
407
399
|
const POSITIVE_PATTERN = /strong|rally|bull|pump|rising|hold|loyal|power|growing|moon|love|trust|alpha|build|conviction/;
|
package/dist/cli.js
CHANGED
|
@@ -261,9 +261,6 @@ async function runSetup() {
|
|
|
261
261
|
// Tick interval
|
|
262
262
|
const intervalStr = await ask('Seconds between actions', '30');
|
|
263
263
|
const tickIntervalMs = Math.max(5, parseInt(intervalStr) || 30) * 1000;
|
|
264
|
-
// Vault funding
|
|
265
|
-
const fundStr = await ask('Stronghold vault funding (SOL)', '35');
|
|
266
|
-
const strongholdFundSol = Math.max(0.1, parseFloat(fundStr) || 35);
|
|
267
264
|
const config = {
|
|
268
265
|
network,
|
|
269
266
|
rpcUrl,
|
|
@@ -273,11 +270,12 @@ async function runSetup() {
|
|
|
273
270
|
llmModel,
|
|
274
271
|
llmApiKey,
|
|
275
272
|
llmUrl,
|
|
276
|
-
strongholdFundSol,
|
|
277
273
|
tickIntervalMs,
|
|
278
274
|
};
|
|
279
275
|
saveConfig(config);
|
|
280
276
|
console.log(`\n Config saved to ${CONFIG_PATH}`);
|
|
277
|
+
console.log(`\n Next: link your agent on pyre.world`);
|
|
278
|
+
console.log(` Run: npx pyre-agent-kit --link`);
|
|
281
279
|
return config;
|
|
282
280
|
}
|
|
283
281
|
// ─── Agent Loop ───────────────────────────────────────────────────
|
|
@@ -322,7 +320,6 @@ async function runAgent(config) {
|
|
|
322
320
|
network: config.network,
|
|
323
321
|
llm,
|
|
324
322
|
personality: config.personality,
|
|
325
|
-
strongholdFundSol: config.strongholdFundSol,
|
|
326
323
|
solRange: config.solRange,
|
|
327
324
|
state,
|
|
328
325
|
logger: (msg) => console.log(` [${ts()}] ${msg}`),
|
|
@@ -375,6 +372,7 @@ async function main() {
|
|
|
375
372
|
console.log('');
|
|
376
373
|
console.log(' Options:');
|
|
377
374
|
console.log(' --setup Re-run full setup wizard');
|
|
375
|
+
console.log(' --link Link an existing agent keypair (import from secret key or file)');
|
|
378
376
|
console.log(' --model Change LLM provider/model only');
|
|
379
377
|
console.log(' --personality Change personality only');
|
|
380
378
|
console.log(' --reset Delete saved config and start fresh');
|
|
@@ -396,6 +394,20 @@ async function main() {
|
|
|
396
394
|
process.exit(0);
|
|
397
395
|
}
|
|
398
396
|
let config = loadConfig();
|
|
397
|
+
if (args.includes('--link')) {
|
|
398
|
+
if (!config) {
|
|
399
|
+
console.log(' No config found. Run: npx pyre-agent-kit --setup');
|
|
400
|
+
rl.close();
|
|
401
|
+
process.exit(1);
|
|
402
|
+
}
|
|
403
|
+
const kp = web3_js_1.Keypair.fromSecretKey(Uint8Array.from(config.secretKey));
|
|
404
|
+
console.log(`\n Agent public key:\n`);
|
|
405
|
+
console.log(` ${kp.publicKey.toBase58()}`);
|
|
406
|
+
console.log(`\n Go to pyre.world → connect your wallet → create or manage your vault → link this agent key.`);
|
|
407
|
+
console.log(` The agent will use the linked vault to trade.\n`);
|
|
408
|
+
rl.close();
|
|
409
|
+
process.exit(0);
|
|
410
|
+
}
|
|
399
411
|
if (args.includes('--model') && config) {
|
|
400
412
|
console.log(` Current: ${config.llmProvider}${config.llmModel ? ` (${config.llmModel})` : ''}\n`);
|
|
401
413
|
const llmIdx = await choose('LLM Provider:', [
|
package/dist/executor.js
CHANGED
|
@@ -4,7 +4,6 @@ exports.executeAction = executeAction;
|
|
|
4
4
|
const web3_js_1 = require("@solana/web3.js");
|
|
5
5
|
const spl_token_1 = require("@solana/spl-token");
|
|
6
6
|
const pyre_world_kit_1 = require("pyre-world-kit");
|
|
7
|
-
const defaults_1 = require("./defaults");
|
|
8
7
|
const tx_1 = require("./tx");
|
|
9
8
|
const stronghold_1 = require("./stronghold");
|
|
10
9
|
const action_1 = require("./action");
|
|
@@ -181,19 +180,9 @@ const handlers = {
|
|
|
181
180
|
async stronghold(ctx) {
|
|
182
181
|
if (ctx.agent.hasStronghold)
|
|
183
182
|
return null;
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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`;
|
|
183
|
+
// Kit agents don't create vaults — they must be created on pyre.world
|
|
184
|
+
ctx.log(`[${ctx.agent.publicKey.slice(0, 8)}] no vault — create one at pyre.world and link agent key ${ctx.agent.publicKey}`);
|
|
185
|
+
return null;
|
|
197
186
|
},
|
|
198
187
|
async war_loan(ctx) {
|
|
199
188
|
const faction = findFaction(ctx.factions, ctx.decision.faction);
|
|
@@ -358,6 +347,26 @@ const handlers = {
|
|
|
358
347
|
// Fudding tanks your own sentiment — you're going bearish
|
|
359
348
|
const fudSentiment = ctx.agent.sentiment.get(faction.mint) ?? 0;
|
|
360
349
|
ctx.agent.sentiment.set(faction.mint, Math.max(-10, fudSentiment - 2));
|
|
350
|
+
// Check if fud cleared the position — if so, treat as a defect
|
|
351
|
+
try {
|
|
352
|
+
const mint = new web3_js_1.PublicKey(faction.mint);
|
|
353
|
+
const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, new web3_js_1.PublicKey(ctx.agent.publicKey), false, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
354
|
+
const info = await ctx.connection.getTokenAccountBalance(ata);
|
|
355
|
+
const remaining = Number(info.value.amount);
|
|
356
|
+
if (remaining <= 0) {
|
|
357
|
+
ctx.agent.holdings.delete(faction.mint);
|
|
358
|
+
ctx.agent.infiltrated.delete(faction.mint);
|
|
359
|
+
ctx.agent.lastAction = `defected ${faction.symbol}`;
|
|
360
|
+
return `fud cleared position in ${faction.symbol} → defected: "${ctx.decision.message}"`;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
// If we can't read the balance, position is likely gone
|
|
365
|
+
ctx.agent.holdings.delete(faction.mint);
|
|
366
|
+
ctx.agent.infiltrated.delete(faction.mint);
|
|
367
|
+
ctx.agent.lastAction = `defected ${faction.symbol}`;
|
|
368
|
+
return `fud cleared position in ${faction.symbol} → defected: "${ctx.decision.message}"`;
|
|
369
|
+
}
|
|
361
370
|
ctx.agent.lastAction = `fud ${faction.symbol}`;
|
|
362
371
|
return `argued in ${faction.symbol}: "${ctx.decision.message}"`;
|
|
363
372
|
},
|
package/dist/index.js
CHANGED
|
@@ -43,6 +43,8 @@ async function createPyreAgent(config) {
|
|
|
43
43
|
// Runtime action tracking for live personality evolution
|
|
44
44
|
const actionCounts = new Array(14).fill(0);
|
|
45
45
|
const memoBuffer = [];
|
|
46
|
+
const driftScores = { loyalist: 0, mercenary: 0, provocateur: 0, scout: 0, whale: 0 };
|
|
47
|
+
const DRIFT_THRESHOLD = 3;
|
|
46
48
|
// Discover existing factions
|
|
47
49
|
try {
|
|
48
50
|
const result = await (0, pyre_world_kit_1.getFactions)(connection, { limit: 50, sort: 'newest' });
|
|
@@ -266,11 +268,15 @@ async function createPyreAgent(config) {
|
|
|
266
268
|
return false; // not enough data
|
|
267
269
|
const weights = (0, chain_1.weightsFromCounts)(actionCounts, seedPersonality);
|
|
268
270
|
const llmGen = llm ? (p) => llm.generate(p) : undefined;
|
|
269
|
-
const
|
|
271
|
+
const suggested = await (0, chain_1.classifyPersonality)(weights, memoBuffer, undefined, llmGen);
|
|
270
272
|
dynamicWeights = weights;
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
273
|
+
// Gradual drift — track suggestions, only flip after consistent lead
|
|
274
|
+
driftScores[suggested]++;
|
|
275
|
+
const currentScore = driftScores[state.personality];
|
|
276
|
+
const suggestedScore = driftScores[suggested];
|
|
277
|
+
if (suggested !== state.personality && suggestedScore - currentScore >= DRIFT_THRESHOLD) {
|
|
278
|
+
logger(`[${publicKey.slice(0, 8)}] personality drifted: ${state.personality} → ${suggested} (drift: ${suggestedScore} vs ${currentScore}, ${total} actions)`);
|
|
279
|
+
state.personality = suggested;
|
|
274
280
|
return true;
|
|
275
281
|
}
|
|
276
282
|
return false;
|
package/dist/stronghold.js
CHANGED
|
@@ -7,42 +7,17 @@ const tx_1 = require("./tx");
|
|
|
7
7
|
const defaults_1 = require("./defaults");
|
|
8
8
|
const ensureStronghold = async (connection, agent, log, opts) => {
|
|
9
9
|
const short = agent.publicKey.slice(0, 8);
|
|
10
|
-
const fundSol = opts?.fundSol ?? defaults_1.STRONGHOLD_FUND_SOL;
|
|
11
10
|
const topupThreshold = opts?.topupThresholdSol ?? defaults_1.STRONGHOLD_TOPUP_THRESHOLD_SOL;
|
|
12
11
|
const topupReserve = opts?.topupReserveSol ?? defaults_1.STRONGHOLD_TOPUP_RESERVE_SOL;
|
|
13
|
-
if
|
|
14
|
-
// Already known — just check if vault needs a top-up
|
|
15
|
-
try {
|
|
16
|
-
const existing = await (0, pyre_world_kit_1.getStronghold)(connection, agent.publicKey);
|
|
17
|
-
const vaultBal = existing?.sol_balance ?? 0;
|
|
18
|
-
const threshold = topupThreshold * pyre_world_kit_1.LAMPORTS_PER_SOL;
|
|
19
|
-
if (existing && vaultBal < threshold) {
|
|
20
|
-
const walletBal = await connection.getBalance(new web3_js_1.PublicKey(agent.publicKey));
|
|
21
|
-
const reserve = topupReserve * pyre_world_kit_1.LAMPORTS_PER_SOL;
|
|
22
|
-
const available = walletBal - reserve;
|
|
23
|
-
if (available > 0.01 * pyre_world_kit_1.LAMPORTS_PER_SOL) {
|
|
24
|
-
const fundAmt = Math.floor(available);
|
|
25
|
-
const fundResult = await (0, pyre_world_kit_1.fundStronghold)(connection, {
|
|
26
|
-
depositor: agent.publicKey,
|
|
27
|
-
stronghold_creator: agent.publicKey,
|
|
28
|
-
amount_sol: fundAmt,
|
|
29
|
-
});
|
|
30
|
-
await (0, tx_1.sendAndConfirm)(connection, agent.keypair, fundResult);
|
|
31
|
-
log(`[${short}] topped up vault with ${(fundAmt / pyre_world_kit_1.LAMPORTS_PER_SOL).toFixed(2)} SOL`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
catch (err) {
|
|
36
|
-
log(`[${short}] vault topup check failed: ${err.message?.slice(0, 80) ?? err}`);
|
|
37
|
-
}
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
// Check if stronghold already exists on-chain (from a previous run)
|
|
12
|
+
// Check if stronghold exists on-chain
|
|
41
13
|
try {
|
|
42
14
|
const existing = await (0, pyre_world_kit_1.getStronghold)(connection, agent.publicKey);
|
|
43
15
|
if (existing) {
|
|
44
16
|
agent.hasStronghold = true;
|
|
45
|
-
|
|
17
|
+
// Top up if below threshold
|
|
18
|
+
const vaultBal = existing.sol_balance ?? 0;
|
|
19
|
+
const threshold = topupThreshold * pyre_world_kit_1.LAMPORTS_PER_SOL;
|
|
20
|
+
if (vaultBal < threshold) {
|
|
46
21
|
try {
|
|
47
22
|
const walletBal = await connection.getBalance(new web3_js_1.PublicKey(agent.publicKey));
|
|
48
23
|
const reserve = topupReserve * pyre_world_kit_1.LAMPORTS_PER_SOL;
|
|
@@ -63,26 +38,8 @@ const ensureStronghold = async (connection, agent, log, opts) => {
|
|
|
63
38
|
return;
|
|
64
39
|
}
|
|
65
40
|
}
|
|
66
|
-
catch { /*
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
await (0, tx_1.sendAndConfirm)(connection, agent.keypair, result);
|
|
70
|
-
agent.hasStronghold = true;
|
|
71
|
-
// Fund it so it can trade on DEX
|
|
72
|
-
const fundAmt = Math.floor(fundSol * pyre_world_kit_1.LAMPORTS_PER_SOL);
|
|
73
|
-
try {
|
|
74
|
-
const fundResult = await (0, pyre_world_kit_1.fundStronghold)(connection, {
|
|
75
|
-
depositor: agent.publicKey,
|
|
76
|
-
stronghold_creator: agent.publicKey,
|
|
77
|
-
amount_sol: fundAmt,
|
|
78
|
-
});
|
|
79
|
-
await (0, tx_1.sendAndConfirm)(connection, agent.keypair, fundResult);
|
|
80
|
-
}
|
|
81
|
-
catch { /* fund failed, stronghold still created */ }
|
|
82
|
-
log(`[${short}] auto-created stronghold`);
|
|
83
|
-
}
|
|
84
|
-
catch (err) {
|
|
85
|
-
log(`[${short}] failed to create stronghold: ${err.message?.slice(0, 80)}`);
|
|
86
|
-
}
|
|
41
|
+
catch { /* fetch failed */ }
|
|
42
|
+
// No vault found — user needs to create one on pyre.world
|
|
43
|
+
log(`[${short}] no vault found — create one at pyre.world and link agent key ${agent.publicKey}`);
|
|
87
44
|
};
|
|
88
45
|
exports.ensureStronghold = ensureStronghold;
|
package/dist/types.d.ts
CHANGED
|
@@ -64,7 +64,7 @@ export interface PyreAgentConfig {
|
|
|
64
64
|
solRange?: [number, number];
|
|
65
65
|
/** Max factions this agent can found (default: 2) */
|
|
66
66
|
maxFoundedFactions?: number;
|
|
67
|
-
/** SOL to fund stronghold vault
|
|
67
|
+
/** SOL to fund stronghold vault when topping up */
|
|
68
68
|
strongholdFundSol?: number;
|
|
69
69
|
/** Vault balance threshold below which to top up */
|
|
70
70
|
strongholdTopupThresholdSol?: number;
|