pyre-agent-kit 2.0.0 → 2.0.1
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 +4 -2
- package/dist/chain.d.ts +6 -1
- package/dist/chain.js +82 -74
- package/dist/index.js +4 -3
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -31,7 +31,7 @@ const buildAgentPrompt = (agent, factions, leaderboardSnippet, intelSnippet, rec
|
|
|
31
31
|
: '';
|
|
32
32
|
// On-chain memory — agent's own past memos as persistent context
|
|
33
33
|
const memoryBlock = chainMemories && chainMemories.length > 0
|
|
34
|
-
? `\nYour on-chain memory (
|
|
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
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.
|
|
@@ -81,7 +81,9 @@ Leaderboard preview: ${leaderboardSnippet}
|
|
|
81
81
|
Intel preview: ${intelSnippet}
|
|
82
82
|
${memoryBlock}${doNotRepeat}
|
|
83
83
|
|
|
84
|
-
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, 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
|
|
84
|
+
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, 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.
|
|
85
|
+
|
|
86
|
+
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.
|
|
85
87
|
|
|
86
88
|
Your response (one line only):`;
|
|
87
89
|
};
|
package/dist/chain.d.ts
CHANGED
|
@@ -43,7 +43,11 @@ export declare function computeWeightsFromHistory(history: OnChainAction[], seed
|
|
|
43
43
|
export declare function weightsFromCounts(counts: number[], seedPersonality: Personality, decay?: number): number[];
|
|
44
44
|
/** Action type to index in the ALL_ACTIONS array */
|
|
45
45
|
export declare function actionIndex(action: Action): number;
|
|
46
|
-
|
|
46
|
+
/**
|
|
47
|
+
* LLM-based personality classification.
|
|
48
|
+
* Falls back to formula scoring if LLM is unavailable.
|
|
49
|
+
*/
|
|
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>;
|
|
47
51
|
/**
|
|
48
52
|
* Compute sentiment towards factions from on-chain interaction patterns.
|
|
49
53
|
*
|
|
@@ -101,4 +105,5 @@ export declare function reconstructFromChain(connection: Connection, agentPubkey
|
|
|
101
105
|
sender: string;
|
|
102
106
|
memo: string;
|
|
103
107
|
}[]>;
|
|
108
|
+
llmGenerate?: (prompt: string) => Promise<string | null>;
|
|
104
109
|
}): Promise<ChainDerivedState>;
|
package/dist/chain.js
CHANGED
|
@@ -292,107 +292,109 @@ function scoreMemoPersonality(memos) {
|
|
|
292
292
|
}
|
|
293
293
|
// ─── Personality Classification ──────────────────────────────────
|
|
294
294
|
/**
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
* Two signal sources, blended:
|
|
298
|
-
* 1. Action ratios — what the agent DOES
|
|
299
|
-
* 2. Memo keywords — what the agent SAYS
|
|
300
|
-
*
|
|
301
|
-
* Action indices: [join=0, defect=1, rally=2, launch=3, message=4,
|
|
302
|
-
* stronghold=5, war_loan=6, repay_loan=7, siege=8, ascend=9,
|
|
303
|
-
* raze=10, tithe=11, infiltrate=12, fud=13]
|
|
304
|
-
*/
|
|
305
|
-
/**
|
|
306
|
-
* Score a single action set (per-faction or global) into personality scores.
|
|
295
|
+
* Build the personality classification prompt for the LLM.
|
|
307
296
|
*/
|
|
308
|
-
function
|
|
297
|
+
function buildClassifyPrompt(weights, memos, perFactionHistory, factionNames) {
|
|
298
|
+
const total = weights.reduce((a, b) => a + b, 0);
|
|
299
|
+
const r = total > 0 ? weights.map(w => ((w / total) * 100).toFixed(1) + '%') : weights.map(() => '0%');
|
|
300
|
+
let actionSummary = `Overall action distribution:\n`;
|
|
301
|
+
actionSummary += ` join: ${r[0]}, defect: ${r[1]}, rally: ${r[2]}, launch: ${r[3]}, message: ${r[4]}\n`;
|
|
302
|
+
actionSummary += ` stronghold: ${r[5]}, war_loan: ${r[6]}, repay_loan: ${r[7]}, siege: ${r[8]}\n`;
|
|
303
|
+
actionSummary += ` ascend: ${r[9]}, raze: ${r[10]}, tithe: ${r[11]}, infiltrate: ${r[12]}, fud: ${r[13]}\n`;
|
|
304
|
+
if (perFactionHistory && perFactionHistory.size > 0) {
|
|
305
|
+
actionSummary += `\nPer-faction breakdown:\n`;
|
|
306
|
+
for (const [mint, counts] of perFactionHistory) {
|
|
307
|
+
const fTotal = counts.reduce((a, b) => a + b, 0);
|
|
308
|
+
if (fTotal < 2)
|
|
309
|
+
continue;
|
|
310
|
+
const name = factionNames?.get(mint) ?? mint.slice(0, 8);
|
|
311
|
+
const fr = counts.map(c => c);
|
|
312
|
+
actionSummary += ` ${name}: join=${fr[0]} defect=${fr[1]} rally=${fr[2]} message=${fr[4]} fud=${fr[13]} (${fTotal} total)\n`;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
const recentMemos = memos.slice(-150);
|
|
316
|
+
const memoBlock = recentMemos.length > 0
|
|
317
|
+
? `\nThis agent's last ${recentMemos.length} messages (oldest first):\n${recentMemos.map((m, i) => ` ${i + 1}. "${m}"`).join('\n')}`
|
|
318
|
+
: '\nNo messages from this agent.';
|
|
319
|
+
return `You are classifying an autonomous agent's personality based on its on-chain behavior and messages.
|
|
320
|
+
|
|
321
|
+
Personalities:
|
|
322
|
+
- loyalist: Ride-or-die for their factions. Buys in and hypes. Messages are positive, supportive, builds community. Rarely defects or fuds.
|
|
323
|
+
- mercenary: Profit-driven lone wolf. Infiltration pattern: joins factions, fuds them, then defects (dumps). Messages are self-serving. High faction churn.
|
|
324
|
+
- provocateur: Lives for chaos and hot takes. High fud rate, starts beef, stirs drama. Messages are inflammatory, challenging, confrontational.
|
|
325
|
+
- scout: Intel operative. Messages are analytical, observational, data-driven. Reports on movements, asks questions. Rarely fuds aggressively.
|
|
326
|
+
- whale: Silent trader. Very few messages relative to trades. Big moves, few words. When they speak, it's brief and authoritative.
|
|
327
|
+
|
|
328
|
+
Agent behavior data:
|
|
329
|
+
${actionSummary}
|
|
330
|
+
${memoBlock}
|
|
331
|
+
|
|
332
|
+
Based on the action patterns AND message content/tone, classify this agent.
|
|
333
|
+
Respond with ONLY a single word: loyalist, mercenary, provocateur, scout, or whale.`;
|
|
334
|
+
}
|
|
335
|
+
/** Formula-based fallback for personality classification */
|
|
336
|
+
function classifyPersonalityFormula(weights, memos) {
|
|
337
|
+
const total = weights.reduce((a, b) => a + b, 0);
|
|
338
|
+
if (total === 0)
|
|
339
|
+
return 'loyalist';
|
|
340
|
+
const r = weights.map(w => w / total);
|
|
309
341
|
const joinRate = r[0], defectRate = r[1], rallyRate = r[2], messageRate = r[4];
|
|
310
342
|
const warLoanRate = r[6], siegeRate = r[8], titheRate = r[11];
|
|
311
343
|
const infiltrateRate = r[12], fudRate = r[13];
|
|
312
344
|
const commsRate = messageRate + fudRate;
|
|
313
345
|
const tradeRate = joinRate + defectRate;
|
|
314
|
-
|
|
315
|
-
const fudRatio = commsRate > 0 ? fudRate / commsRate : 0; // 0 = pure messenger, 1 = pure fudder
|
|
346
|
+
const fudRatio = commsRate > 0 ? fudRate / commsRate : 0;
|
|
316
347
|
const msgRatio = commsRate > 0 ? messageRate / commsRate : 0;
|
|
317
|
-
|
|
318
|
-
// Loyalist: high message ratio, joins, rallies — positive vibes
|
|
348
|
+
const scores = {
|
|
319
349
|
loyalist: msgRatio * 3 + joinRate * 2 + rallyRate * 3 + titheRate * 2
|
|
320
350
|
- fudRatio * 4 - defectRate * 3,
|
|
321
|
-
// Mercenary: defect + fud cycle, infiltration pattern
|
|
322
351
|
mercenary: defectRate * 4 + fudRatio * 2
|
|
323
352
|
+ (defectRate > 0 && fudRate > 0 ? 3 : 0)
|
|
324
353
|
+ infiltrateRate * 3 + warLoanRate * 2 + siegeRate * 2
|
|
325
354
|
- msgRatio * 2 - rallyRate * 2,
|
|
326
|
-
// Provocateur: high fud ratio — more fud than message
|
|
327
355
|
provocateur: fudRatio * 4 + infiltrateRate * 2
|
|
328
356
|
- msgRatio * 2 - rallyRate * 2,
|
|
329
|
-
// Scout: high message ratio, low fud — observes, doesn't attack
|
|
330
357
|
scout: msgRatio * 3 + rallyRate - fudRatio * 3 - defectRate,
|
|
331
|
-
// Whale: trades a lot but talks very little
|
|
332
358
|
whale: (tradeRate > commsRate ? 1 : 0) * 2 + warLoanRate * 3 + defectRate * 2
|
|
333
359
|
- commsRate * 3,
|
|
334
360
|
};
|
|
335
|
-
}
|
|
336
|
-
function classifyPersonality(weights, memos = [], perFactionHistory) {
|
|
337
|
-
const total = weights.reduce((a, b) => a + b, 0);
|
|
338
|
-
if (total === 0)
|
|
339
|
-
return 'loyalist';
|
|
340
|
-
// ── Signal 1: per-faction action scores, averaged ──
|
|
341
|
-
let actionScores;
|
|
342
|
-
if (perFactionHistory && perFactionHistory.size > 0) {
|
|
343
|
-
const accumulated = {
|
|
344
|
-
loyalist: 0, mercenary: 0, provocateur: 0, scout: 0, whale: 0,
|
|
345
|
-
};
|
|
346
|
-
let factionCount = 0;
|
|
347
|
-
for (const [, counts] of perFactionHistory) {
|
|
348
|
-
const fTotal = counts.reduce((a, b) => a + b, 0);
|
|
349
|
-
if (fTotal < 2)
|
|
350
|
-
continue;
|
|
351
|
-
const r = counts.map(c => c / fTotal);
|
|
352
|
-
const scores = scoreActions(r);
|
|
353
|
-
for (const p of Object.keys(accumulated)) {
|
|
354
|
-
accumulated[p] += scores[p];
|
|
355
|
-
}
|
|
356
|
-
factionCount++;
|
|
357
|
-
}
|
|
358
|
-
if (factionCount > 0) {
|
|
359
|
-
for (const p of Object.keys(accumulated)) {
|
|
360
|
-
accumulated[p] /= factionCount;
|
|
361
|
-
}
|
|
362
|
-
actionScores = accumulated;
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
const r = weights.map(w => w / total);
|
|
366
|
-
actionScores = scoreActions(r);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
else {
|
|
370
|
-
const r = weights.map(w => w / total);
|
|
371
|
-
actionScores = scoreActions(r);
|
|
372
|
-
}
|
|
373
|
-
// ── Signal 2: memo content ──
|
|
374
361
|
const memoScores = scoreMemoPersonality(memos);
|
|
375
362
|
const memoTotal = Object.values(memoScores).reduce((a, b) => a + b, 0);
|
|
376
|
-
const memoWeight = 0.4;
|
|
377
|
-
// ── Blend ──
|
|
378
|
-
const finalScores = {
|
|
379
|
-
loyalist: 0, mercenary: 0, provocateur: 0, scout: 0, whale: 0,
|
|
380
|
-
};
|
|
381
|
-
for (const p of Object.keys(finalScores)) {
|
|
382
|
-
const actionSignal = actionScores[p];
|
|
383
|
-
const memoSignal = memoTotal > 0 ? (memoScores[p] / memoTotal) : 0;
|
|
384
|
-
finalScores[p] = actionSignal * (1 - memoWeight) + memoSignal * memoWeight;
|
|
385
|
-
}
|
|
386
363
|
let best = 'loyalist';
|
|
387
364
|
let bestScore = -Infinity;
|
|
388
|
-
for (const
|
|
389
|
-
|
|
390
|
-
|
|
365
|
+
for (const p of Object.keys(scores)) {
|
|
366
|
+
const blended = scores[p] * 0.6 + (memoTotal > 0 ? (memoScores[p] / memoTotal) * 0.4 : 0);
|
|
367
|
+
if (blended > bestScore) {
|
|
368
|
+
bestScore = blended;
|
|
391
369
|
best = p;
|
|
392
370
|
}
|
|
393
371
|
}
|
|
394
372
|
return best;
|
|
395
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* LLM-based personality classification.
|
|
376
|
+
* Falls back to formula scoring if LLM is unavailable.
|
|
377
|
+
*/
|
|
378
|
+
async function classifyPersonality(weights, memos, perFactionHistory, llmGenerate, factionNames) {
|
|
379
|
+
const total = weights.reduce((a, b) => a + b, 0);
|
|
380
|
+
if (total === 0)
|
|
381
|
+
return 'loyalist';
|
|
382
|
+
if (llmGenerate) {
|
|
383
|
+
try {
|
|
384
|
+
const prompt = buildClassifyPrompt(weights, memos, perFactionHistory, factionNames);
|
|
385
|
+
const response = await llmGenerate(prompt);
|
|
386
|
+
if (response) {
|
|
387
|
+
const cleaned = response.toLowerCase().replace(/[^a-z]/g, '').trim();
|
|
388
|
+
const valid = ['loyalist', 'mercenary', 'provocateur', 'scout', 'whale'];
|
|
389
|
+
const match = valid.find(p => cleaned.includes(p));
|
|
390
|
+
if (match)
|
|
391
|
+
return match;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
catch { /* fall through to formula */ }
|
|
395
|
+
}
|
|
396
|
+
return classifyPersonalityFormula(weights, memos);
|
|
397
|
+
}
|
|
396
398
|
// ─── Sentiment from On-Chain Data ────────────────────────────────
|
|
397
399
|
const POSITIVE_PATTERN = /strong|rally|bull|pump|rising|hold|loyal|power|growing|moon|love|trust|alpha|build|conviction/;
|
|
398
400
|
const NEGATIVE_PATTERN = /weak|dump|bear|dead|fail|raze|crash|abandon|scam|rug|sell|exit|trash|hate|fake/;
|
|
@@ -560,7 +562,13 @@ async function reconstructFromChain(connection, agentPubkey, factions, seedPerso
|
|
|
560
562
|
perFaction.set(entry.mint, new Array(ALL_ACTIONS.length).fill(0));
|
|
561
563
|
perFaction.get(entry.mint)[idx]++;
|
|
562
564
|
}
|
|
563
|
-
|
|
565
|
+
// Map mint → symbol for readable LLM prompts
|
|
566
|
+
const factionNames = new Map();
|
|
567
|
+
for (const f of factions)
|
|
568
|
+
factionNames.set(f.mint, f.symbol);
|
|
569
|
+
const personality = history.length > 0
|
|
570
|
+
? await classifyPersonality(weights, memoTexts, perFaction, opts?.llmGenerate, factionNames)
|
|
571
|
+
: seedPersonality;
|
|
564
572
|
// 4. Compute sentiment from on-chain interactions
|
|
565
573
|
const sentiment = computeSentimentFromHistory(history, factions);
|
|
566
574
|
// 5. Derive allies/rivals
|
package/dist/index.js
CHANGED
|
@@ -65,7 +65,7 @@ async function createPyreAgent(config) {
|
|
|
65
65
|
let dynamicWeights;
|
|
66
66
|
let solRange = config.solRange ?? defaults_1.PERSONALITY_SOL[seedPersonality];
|
|
67
67
|
try {
|
|
68
|
-
chainState = await (0, chain_1.reconstructFromChain)(connection, publicKey, knownFactions, seedPersonality, { maxSignatures: 500 });
|
|
68
|
+
chainState = await (0, chain_1.reconstructFromChain)(connection, publicKey, knownFactions, seedPersonality, { maxSignatures: 500, llmGenerate: llm ? (p) => llm.generate(p) : undefined });
|
|
69
69
|
if (chainState.actionCount > 0) {
|
|
70
70
|
personality = chainState.personality;
|
|
71
71
|
dynamicWeights = chainState.weights;
|
|
@@ -260,12 +260,13 @@ async function createPyreAgent(config) {
|
|
|
260
260
|
usedLLM,
|
|
261
261
|
};
|
|
262
262
|
}
|
|
263
|
-
function evolve() {
|
|
263
|
+
async function evolve() {
|
|
264
264
|
const total = actionCounts.reduce((a, b) => a + b, 0);
|
|
265
265
|
if (total < 5)
|
|
266
266
|
return false; // not enough data
|
|
267
267
|
const weights = (0, chain_1.weightsFromCounts)(actionCounts, seedPersonality);
|
|
268
|
-
const
|
|
268
|
+
const llmGen = llm ? (p) => llm.generate(p) : undefined;
|
|
269
|
+
const newPersonality = await (0, chain_1.classifyPersonality)(weights, memoBuffer, undefined, llmGen);
|
|
269
270
|
dynamicWeights = weights;
|
|
270
271
|
if (newPersonality !== state.personality) {
|
|
271
272
|
logger(`[${publicKey.slice(0, 8)}] personality evolved: ${state.personality} → ${newPersonality} (${total} runtime actions)`);
|
package/dist/types.d.ts
CHANGED
|
@@ -112,7 +112,7 @@ export interface PyreAgent {
|
|
|
112
112
|
/** Run one decision+action cycle */
|
|
113
113
|
tick(factions?: FactionInfo[]): Promise<AgentTickResult>;
|
|
114
114
|
/** Recompute personality + weights from accumulated runtime actions. Returns true if personality changed. */
|
|
115
|
-
evolve(): boolean
|
|
115
|
+
evolve(): Promise<boolean>;
|
|
116
116
|
/** Get current mutable state */
|
|
117
117
|
getState(): AgentState;
|
|
118
118
|
/** Serialize state for persistence */
|