pyre-agent-kit 4.3.11 → 4.4.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.d.ts CHANGED
@@ -11,6 +11,6 @@ export interface FactionContext {
11
11
  nearby: FactionInfo[];
12
12
  all: FactionInfo[];
13
13
  }
14
- export declare const buildAgentPrompt: (kit: PyreKit, agent: AgentState, factionCtx: FactionContext, intelSnippet: string, recentMessages: string[], solRange?: [number, number], holdings?: Map<string, number>) => string;
15
- export declare const buildCompactModelPrompt: (kit: PyreKit, agent: AgentState, factionCtx: FactionContext, intelSnippet: string, recentMessages: string[], solRange?: [number, number], holdings?: Map<string, number>) => string;
14
+ export declare const buildAgentPrompt: (kit: PyreKit, agent: AgentState, factionCtx: FactionContext, recentMessages: string[], solRange?: [number, number], holdings?: Map<string, number>) => Promise<string>;
15
+ export declare const buildCompactModelPrompt: (kit: PyreKit, agent: AgentState, factionCtx: FactionContext, recentMessages: string[], solRange?: [number, number], holdings?: Map<string, number>) => Promise<string>;
16
16
  export declare function llmDecide(kit: PyreKit, agent: AgentState, factions: FactionInfo[], recentMessages: string[], llm: LLMAdapter, log: (msg: string) => void, solRange?: [number, number], options?: LLMDecideOptions): Promise<LLMDecision | null>;
package/dist/agent.js CHANGED
@@ -6,7 +6,7 @@ const defaults_1 = require("./defaults");
6
6
  const util_1 = require("./util");
7
7
  const faction_1 = require("./faction");
8
8
  exports.pendingScoutResults = new Map();
9
- const buildAgentPrompt = (kit, agent, factionCtx, intelSnippet, recentMessages, solRange, holdings) => {
9
+ const buildAgentPrompt = async (kit, agent, factionCtx, recentMessages, solRange, holdings) => {
10
10
  const [minSol, maxSol] = solRange ?? defaults_1.PERSONALITY_SOL[agent.personality];
11
11
  const gameState = kit.state.state;
12
12
  const holdingsEntries = [...(holdings?.entries() ?? [])];
@@ -47,8 +47,8 @@ const buildAgentPrompt = (kit, agent, factionCtx, intelSnippet, recentMessages,
47
47
  // Build flat faction table rows
48
48
  const factionRows = [];
49
49
  const seenMints = new Set();
50
- // MBR factions first
51
- for (const pv of positionValues) {
50
+ // MBR factions first (cap at 10)
51
+ for (const pv of positionValues.slice(0, 10)) {
52
52
  const f = factionCtx.all.find(ff => ff.mint === pv.mint);
53
53
  if (!f)
54
54
  continue;
@@ -75,18 +75,50 @@ const buildAgentPrompt = (kit, agent, factionCtx, intelSnippet, recentMessages,
75
75
  const ready = rest.filter(f => f.status === 'ready');
76
76
  const shown = new Set([...nearby, ...rising, ...ascended, ...ready].map(f => f.mint));
77
77
  const unexplored = rest.filter(f => !shown.has(f.mint)).sort(() => Math.random() - 0.5).slice(0, 3);
78
+ let nonMemberCount = 0;
78
79
  for (const f of [...nearby, ...ascended, ...ready, ...rising, ...unexplored]) {
80
+ if (nonMemberCount >= 10)
81
+ break;
79
82
  if (seenMints.has(f.mint))
80
83
  continue;
81
84
  seenMints.add(f.mint);
82
85
  const mcap = f.market_cap_sol ? `${f.market_cap_sol.toFixed(2)}` : '?';
83
86
  const sent = kit.state.sentimentMap.get(f.mint) ?? 0;
84
87
  factionRows.push(`${f.mint.slice(-8)},${mcap},${statusTag(f)},false,false,0,0,${sent > 0 ? '+' : ''}${Math.round(sent * 10) / 10},false`);
88
+ nonMemberCount++;
85
89
  }
86
90
  const validatedFactions = [...ascended, ...ready, ...rising, ...nearby, ...unexplored];
87
- const doNotRepeat = recentMessages.length > 0
88
- ? `\nDO NOT REPEAT OR PARAPHRASE:\n${recentMessages.slice(0, 5).map((m) => `- "${m}"`).join('\n')}\n`
89
- : '';
91
+ // Fetch intel from table factions only — no off-screen FIDs
92
+ let intelSnippet = '';
93
+ try {
94
+ const tableMemberMints = [...seenMints].filter(mint => heldMints.has(mint)).slice(0, 2);
95
+ const tableNonMemberMints = [...seenMints].filter(mint => !heldMints.has(mint));
96
+ const toScout = [
97
+ ...tableMemberMints.map(mint => factionCtx.all.find((f) => f.mint === mint)).filter(Boolean),
98
+ ...(tableNonMemberMints.length > 0 ? [factionCtx.all.find((f) => f.mint === (0, util_1.pick)(tableNonMemberMints))].filter(Boolean) : []),
99
+ ];
100
+ if (toScout.length > 0) {
101
+ const intels = await Promise.all(toScout.map(f => (0, faction_1.fetchFactionIntel)(kit, f)));
102
+ const lines = intels.map((intel, i) => {
103
+ const fid = toScout[i].mint.slice(-8);
104
+ const memberInfo = intel.totalMembers > 0
105
+ ? `${intel.totalMembers} members, top holder: ${intel.members[0]?.percentage.toFixed(1)}%`
106
+ : 'no members';
107
+ const commsInfo = intel.recentComms.length > 0
108
+ ? intel.recentComms.slice(0, 3).map(c => `@AP${c.sender.slice(0, 4)} said: "${c.memo.replace(/^<+/, '').replace(/>+\s*$/, '')}"`).join(', ')
109
+ : 'no recent comms';
110
+ return ` [${fid}] ${memberInfo} | recent comms: ${commsInfo}`;
111
+ });
112
+ intelSnippet = 'FACTION INTEL:\n' + lines.join('\n');
113
+ }
114
+ }
115
+ catch { }
116
+ // Include results from previous SCOUT actions
117
+ const scoutResults = exports.pendingScoutResults.get(agent.publicKey);
118
+ if (scoutResults && scoutResults.length > 0) {
119
+ intelSnippet += '\nSCOUT RESULTS (from your previous SCOUT actions):\n' + scoutResults.join('\n');
120
+ exports.pendingScoutResults.delete(agent.publicKey);
121
+ }
90
122
  const memoryEntries = [...kit.state.history].slice(-20);
91
123
  const memoryBlock = memoryEntries.length > 0
92
124
  ? memoryEntries.slice(0, 7).map((m) => `- ${m}`).join('; ')
@@ -145,6 +177,8 @@ REPLACE * with a ONE sentence RESPONSE, always in double quotes.
145
177
  (%) "..." - create new faction. "..." = creative name, in quotes.
146
178
  (_) - skip turn.
147
179
  --- RULES:
180
+ (+), (&) and (/) increase MCAP. (-) decreases MCAP.
181
+ (!) and (#) are your voice. (!) increases SENT. (#) decreases SENT.
148
182
  (+) or (/) FACTIONS where MBR=false.
149
183
  (-), (&) or (#) FACTIONS where MBR=true.
150
184
  (^) FACTIONS where STATUS=RD.
@@ -166,13 +200,13 @@ REPLACE * with a ONE sentence RESPONSE, always in double quotes.
166
200
  - if MBR=false and FNR=true, consider (+). this is your faction, promote it with (!).
167
201
  - FACTIONS where MBR=true and SENT is positive ARE your identity. promote what you hold.${factionCtx.all.length <= 2 ? '\n- no FACTIONS? (%) to create one.' : ''}
168
202
  - FACTIONS where STATUS=RS and MBR=false and lower MCAP could turn more profit if you (+) the right one.
169
- - (+), (&) and (/) increase MCAP. (-) decreases MCAP.
170
- - (!) and (#) are your voice. (!) increases SENT. (#) decreases SENT. use them to coordinate and talk with other agents.
171
- - (/) to join a faction with intentions of (-) later. take this action when you are profit seeking or want to harm a rival faction.
172
- - (&) and (!) to push FACTIONS where MBR=true and STATUS=RS to STATUS=ASN. as MCAP increases your PNL will also increase.
203
+ - (!) and (#) help you coordinate and talk with other agents.
204
+ - in FACTIONS where MBR=true, if MCAP increases, your PNL will increase.
205
+ - (&) and (!) to push FACTIONS where MBR=true and STATUS=RS to STATUS=ASN.
206
+ - (/) to join a faction with intentions of (-) later. (/) when you are profit seeking or want to harm a rival faction.
173
207
  - consider (-) to lock in profits on FACTIONS where MBR=true and PNL is positive.
174
208
  - consider (-) FACTIONS where MBR=true and PNL is negative unless FNR=true or SENT is positive.
175
- - when HLTH is negative, prefer (-) weakest FACTIONS where MBR=true or (_). (+) or (&) ONLY if you see opportunity.
209
+ - when HLTH is negative, prefer (_) or (-) weakest FACTIONS where MBR=true. (+) or (&) ONLY if you see opportunity.
176
210
  - (_) if holding is the optimal move.
177
211
  ---
178
212
  one move per turn. output EXACTLY one line.
@@ -187,7 +221,7 @@ example format: ${(0, util_1.pick)([
187
221
  >`;
188
222
  };
189
223
  exports.buildAgentPrompt = buildAgentPrompt;
190
- const buildCompactModelPrompt = (kit, agent, factionCtx, intelSnippet, recentMessages, solRange, holdings) => {
224
+ const buildCompactModelPrompt = async (kit, agent, factionCtx, recentMessages, solRange, holdings) => {
191
225
  const gameState = kit.state.state;
192
226
  const [minSol, maxSol] = solRange ?? defaults_1.PERSONALITY_SOL[agent.personality];
193
227
  const holdingsEntries = [...(holdings?.entries() ?? [])];
@@ -226,20 +260,14 @@ const buildCompactModelPrompt = (kit, agent, factionCtx, intelSnippet, recentMes
226
260
  return 'RS';
227
261
  };
228
262
  // Sentiment label
229
- const sentLabel = (s) => s > 0.5 ? 'BULL' : s < -0.5 ? 'BEAR' : 'NEUT';
230
- // Per-position PnL label
231
- const pnlLabel = (valueSol, bal) => {
263
+ const sentLabel = (s) => `${s >= 0 ? '+' : ''}${s.toFixed(2)}`;
264
+ // Per-position PnL (numeric, 2 decimal places)
265
+ const pnlValue = (valueSol, bal) => {
232
266
  if (totalTokens <= 0 || netInvested <= 0)
233
- return 'FLAT';
267
+ return '0';
234
268
  const estCost = netInvested * (bal / totalTokens);
235
269
  const posPnl = valueSol - estCost;
236
- return posPnl > 0.005 ? 'WIN' : posPnl < -0.005 ? 'LOSS' : 'FLAT';
237
- };
238
- // Discovery tag
239
- const discoveryTag = (f) => {
240
- if (nearbyMints.has(f.mint))
241
- return 'NB';
242
- return 'UX';
270
+ return `${posPnl >= 0 ? '+' : ''}${posPnl.toFixed(2)}`;
243
271
  };
244
272
  // Build flat faction rows: FACTION (MCAP) STATUS MBR FNR [NB|UX]
245
273
  const factionRows = [];
@@ -253,7 +281,7 @@ const buildCompactModelPrompt = (kit, agent, factionCtx, intelSnippet, recentMes
253
281
  const mcap = f.market_cap_sol ? `${f.market_cap_sol.toFixed(2)}` : '?';
254
282
  const fnr = foundedSet.has(f.mint);
255
283
  const sent = kit.state.sentimentMap.get(f.mint) ?? 0;
256
- factionRows.push(`${f.mint.slice(-8)},${mcap},${statusTag(f)},true,${fnr},${Math.max(v.valueSol, 0.005).toFixed(2)},${pnlLabel(v.valueSol, v.bal)},${sentLabel(sent)}`);
284
+ factionRows.push(`${f.mint.slice(-8)},${mcap},${statusTag(f)},true,${fnr},${Math.max(v.valueSol, 0.005).toFixed(2)},${pnlValue(v.valueSol, v.bal)},${sentLabel(sent)}`);
257
285
  }
258
286
  // Non-member factions
259
287
  const nonMember = factionCtx.all.filter(f => !seenMints.has(f.mint) && f.status !== 'razed');
@@ -270,15 +298,33 @@ const buildCompactModelPrompt = (kit, agent, factionCtx, intelSnippet, recentMes
270
298
  seenMints.add(f.mint);
271
299
  const mcap = f.market_cap_sol ? `${f.market_cap_sol.toFixed(2)}` : '?';
272
300
  const sent = kit.state.sentimentMap.get(f.mint) ?? 0;
273
- factionRows.push(`${f.mint.slice(-8)},${mcap},${statusTag(f)},false,false,0,FLAT,${sentLabel(sent)}`);
301
+ factionRows.push(`${f.mint.slice(-8)},${mcap},${statusTag(f)},false,false,0,0,${sentLabel(sent)}`);
274
302
  }
303
+ // Fetch intel from table member factions only — no off-screen FIDs
304
+ let intelSnippet = '';
305
+ try {
306
+ const tableMemberMints = [...seenMints].filter(mint => heldMints.has(mint));
307
+ const lines = [];
308
+ for (const mint of tableMemberMints.slice(0, 2)) {
309
+ const f = factionCtx.all.find(ff => ff.mint === mint);
310
+ if (!f)
311
+ continue;
312
+ const intel = await (0, faction_1.fetchFactionIntel)(kit, f);
313
+ const latest = intel.recentComms.find((c) => c.sender !== agent.publicKey);
314
+ if (latest) {
315
+ lines.push(`@AP${latest.sender.slice(0, 4)} in ${mint.slice(-8)}: "${latest.memo.replace(/^<+/, '').replace(/>+\s*$/, '').slice(0, 60)}"`);
316
+ }
317
+ }
318
+ intelSnippet = lines.join('\n');
319
+ }
320
+ catch { }
275
321
  // Pick example FIDs only from factions actually shown in the table
276
322
  const tableFids = factionRows.map(r => r.split(',')[0]);
277
323
  const m = tableFids.find(fid => heldMints.has([...seenMints].find(mint => mint.endsWith(fid)) ?? '')) ?? tableFids[0] ?? 'xxxxxxpw';
278
324
  const nonMemberFids = tableFids.filter(fid => fid !== m);
279
325
  const f1 = nonMemberFids.length > 0 ? (0, util_1.pick)(nonMemberFids) : m;
280
326
  const f2 = nonMemberFids.length > 1 ? (0, util_1.pick)(nonMemberFids.filter(fid => fid !== f1)) : f1;
281
- return `You are an autonomous agent playing in Pyre, a faction warfare game. Think in English only. Think linearly: situation → decision → reason. Do not repeat yourself. Do NOT overthink, chess/strategy mood.
327
+ return `Welcome to Pyre, a faction warfare game. Think in English only. Think linearly: situation → decision → reason. Do not repeat yourself. Do NOT overthink, chess/strategy mood.
282
328
  --- GOAL:
283
329
  Maximize long-term profit and faction dominance.
284
330
  --- LEGEND:
@@ -291,8 +337,8 @@ RD: ready, community transition stage before ascend.
291
337
  ASN: ascended factions, established, more members. treasuries active. 0.04% war tax to the faction.
292
338
  MBR: true = you are a member. false = you are not a member.
293
339
  FNR: true = you created it. false = you did not create it.
294
- PNL: per-position profit. WIN=profit, LOSS=losing, FLAT=breakeven.
295
- SENT: sentiment score. BULL=positive, BEAR=negative, NEUT=neutral.
340
+ PNL: per-position profit. positive = winning, negative = losing.
341
+ SENT: sentiment score. positive = bullish, negative = bearish.
296
342
  --- YOU ARE:
297
343
  NAME: @AP${agent.publicKey.slice(0, 4)}
298
344
  BIO: ${defaults_1.personalityDesc[agent.personality]}
@@ -317,11 +363,13 @@ REPLACE * with a ONE sentence RESPONSE, always in double quotes.
317
363
  (%) "..." - create new faction. "..." = creative name, in quotes.
318
364
  (_) - skip turn.
319
365
  --- RULES:
366
+ (+) and (&) increase MCAP. (-) decreases MCAP.
367
+ (!) and (#) are your voice. (!) increases SENT. (#) decreases SENT.
320
368
  (!) any FACTIONS.
321
369
  (^) FACTIONS where STATUS=RD.
322
370
  (~) FACTIONS where STATUS=ASN.
323
371
  (+) FACTIONS where MBR=false.
324
- (-), (&) or (#) FACTIONS where MBR=true only.
372
+ (-), (&) or (#) FACTIONS where MBR=true.
325
373
  --- STRATEGIES:
326
374
  - your personality is your tone.
327
375
  - no FACTIONS? (%) to create one.
@@ -329,12 +377,11 @@ REPLACE * with a ONE sentence RESPONSE, always in double quotes.
329
377
  - limit FACTIONS where MBR=true to AT MOST 5.${memberOf.length > 3 ? ` MBR=true on ${memberOf.length} FACTIONS — consider (-) from underperformers.` : ''}
330
378
  - FACTIONS where FNR=true and MBR=false, consider (+). promote it with (!).
331
379
  - FACTIONS where STATUS=RS may have higher reward if you (+) the right one.
332
- - (!) and (#) are your voice.
333
- - (+) and (&) increase MCAP. (-) decreases MCAP.
334
- - (&) and (!) to push FACTIONS where MBR=true and STATUS=RS to STATUS=ASN. as MCAP increases your PNL will also increase.
335
- - consider (-) FACTIONS where MBR=true and PNL=WIN to lock in profits.
336
- - consider (-) FACTIONS where MBR=true and PNL=LOSS unless FNR=true or SENT=BULL.
337
- - when HLTH is negative, prefer (-) weakest FACTIONS where MBR=true or (_). (+) or (&) ONLY if you see opportunity.
380
+ - in FACTIONS where MBR=true, if MCAP increases, your PNL will increase.
381
+ - (&) and (!) to push FACTIONS where MBR=true and STATUS=RS to STATUS=ASN.
382
+ - consider (-) FACTIONS where MBR=true and PNL is positive to lock in profits.
383
+ - consider (-) FACTIONS where MBR=true and PNL is negative unless FNR=true or SENT is positive.
384
+ - when HLTH is negative, consider (_) or (-) weakest FACTIONS where MBR=true. (+) or (&) ONLY if you see opportunity.
338
385
  - (_) if holding is the optimal move.
339
386
  ---
340
387
  one move per turn. output EXACTLY one line.
@@ -657,66 +704,8 @@ async function llmDecide(kit, agent, factions, recentMessages, llm, log, solRang
657
704
  nearby: nearbyResult.factions,
658
705
  all: allFactions,
659
706
  };
660
- let intelSnippet = '';
661
- if (compact) {
662
- // Compact: up to 2 intel lines from held factions
663
- try {
664
- const heldMints = [...holdings.keys()];
665
- const heldFactions = allFactions.filter((f) => heldMints.includes(f.mint)).slice(0, 2);
666
- const lines = [];
667
- for (const hf of heldFactions) {
668
- if (lines.length >= 2)
669
- break;
670
- const intel = await (0, faction_1.fetchFactionIntel)(kit, hf);
671
- const latest = intel.recentComms.find((c) => c.sender !== agent.publicKey);
672
- if (latest) {
673
- lines.push(`@AP${latest.sender.slice(0, 4)} in ${hf.mint.slice(-8)}: "${latest.memo.replace(/^<+/, '').replace(/>+\s*$/, '').slice(0, 60)}"`);
674
- }
675
- }
676
- intelSnippet = lines.join('\n');
677
- }
678
- catch { }
679
- }
680
- else {
681
- try {
682
- const heldMints = [...holdings.keys()];
683
- const heldFactions = allFactions.filter((f) => heldMints.includes(f.mint));
684
- const otherFactions = allFactions.filter((f) => !heldMints.includes(f.mint));
685
- const toScout = [
686
- ...heldFactions.slice(0, 2),
687
- ...(otherFactions.length > 0 ? [(0, util_1.pick)(otherFactions)] : []),
688
- ];
689
- if (toScout.length > 0) {
690
- const intels = await Promise.all(toScout.map((f) => (0, faction_1.fetchFactionIntel)(kit, f)));
691
- const lines = intels.map((intel, i) => {
692
- const fid = toScout[i].mint.slice(-8);
693
- const memberInfo = intel.totalMembers > 0
694
- ? `${intel.totalMembers} members, top holder: ${intel.members[0]?.percentage.toFixed(1)}%`
695
- : 'no members';
696
- const commsInfo = intel.recentComms.length > 0
697
- ? intel.recentComms
698
- .slice(0, 3)
699
- .map((c) => `@AP${c.sender.slice(0, 4)} said: "${c.memo.replace(/^<+/, '').replace(/>+\s*$/, '')}"`)
700
- .join(', ')
701
- : 'no recent comms';
702
- return ` [${fid}] ${memberInfo} | recent comms: ${commsInfo}`;
703
- });
704
- intelSnippet = 'FACTION INTEL:\n' + lines.join('\n');
705
- }
706
- }
707
- catch { }
708
- }
709
- // Include results from previous SCOUT actions (skip in compact mode)
710
- let scoutSnippet = '';
711
- if (!compact) {
712
- const scoutResults = exports.pendingScoutResults.get(agent.publicKey);
713
- if (scoutResults && scoutResults.length > 0) {
714
- scoutSnippet = '\nSCOUT RESULTS (from your previous SCOUT actions):\n' + scoutResults.join('\n');
715
- exports.pendingScoutResults.delete(agent.publicKey);
716
- }
717
- }
718
707
  const buildPrompt = compact ? exports.buildCompactModelPrompt : exports.buildAgentPrompt;
719
- const prompt = buildPrompt(kit, agent, factionCtx, intelSnippet + scoutSnippet, recentMessages, solRange, holdings);
708
+ const prompt = await buildPrompt(kit, agent, factionCtx, recentMessages, solRange, holdings);
720
709
  // Surface the faction table to the caller if requested
721
710
  if (options?.onPromptTable) {
722
711
  const tableStart = prompt.indexOf('--- FACTIONS:');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pyre-agent-kit",
3
- "version": "4.3.11",
3
+ "version": "4.4.1",
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",