pyre-agent-kit 4.3.10 → 4.4.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.
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('; ')
@@ -128,7 +160,7 @@ FID,MCAP,STATUS,MBR,FNR,VALUE,PNL,SENT,LOAN
128
160
  ${factionRows.length > 0 ? factionRows.join('\n') : 'none'}
129
161
  --- ACTIONS:
130
162
  FORMAT: (action) $ "*"
131
- REPLACE $ with EXACTLY one FID from FACTIONS (always ends in pw).
163
+ REPLACE $ with EXACTLY one FID from FACTIONS ONLY (always ends in pw).
132
164
  REPLACE * with a ONE sentence RESPONSE, always in double quotes.
133
165
  (+) $ "*" - join.
134
166
  (-) $ "*" - leave or reduce position.
@@ -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() ?? [])];
@@ -227,13 +261,13 @@ const buildCompactModelPrompt = (kit, agent, factionCtx, intelSnippet, recentMes
227
261
  };
228
262
  // Sentiment label
229
263
  const sentLabel = (s) => s > 0.5 ? 'BULL' : s < -0.5 ? 'BEAR' : 'NEUT';
230
- // Per-position PnL label
231
- const pnlLabel = (valueSol, bal) => {
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';
270
+ return `${posPnl >= 0 ? '+' : ''}${posPnl.toFixed(2)}`;
237
271
  };
238
272
  // Discovery tag
239
273
  const discoveryTag = (f) => {
@@ -253,7 +287,7 @@ const buildCompactModelPrompt = (kit, agent, factionCtx, intelSnippet, recentMes
253
287
  const mcap = f.market_cap_sol ? `${f.market_cap_sol.toFixed(2)}` : '?';
254
288
  const fnr = foundedSet.has(f.mint);
255
289
  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)}`);
290
+ 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
291
  }
258
292
  // Non-member factions
259
293
  const nonMember = factionCtx.all.filter(f => !seenMints.has(f.mint) && f.status !== 'razed');
@@ -270,8 +304,26 @@ const buildCompactModelPrompt = (kit, agent, factionCtx, intelSnippet, recentMes
270
304
  seenMints.add(f.mint);
271
305
  const mcap = f.market_cap_sol ? `${f.market_cap_sol.toFixed(2)}` : '?';
272
306
  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)}`);
307
+ factionRows.push(`${f.mint.slice(-8)},${mcap},${statusTag(f)},false,false,0,0,${sentLabel(sent)}`);
274
308
  }
309
+ // Fetch intel from table member factions only — no off-screen FIDs
310
+ let intelSnippet = '';
311
+ try {
312
+ const tableMemberMints = [...seenMints].filter(mint => heldMints.has(mint));
313
+ const lines = [];
314
+ for (const mint of tableMemberMints.slice(0, 2)) {
315
+ const f = factionCtx.all.find(ff => ff.mint === mint);
316
+ if (!f)
317
+ continue;
318
+ const intel = await (0, faction_1.fetchFactionIntel)(kit, f);
319
+ const latest = intel.recentComms.find((c) => c.sender !== agent.publicKey);
320
+ if (latest) {
321
+ lines.push(`@AP${latest.sender.slice(0, 4)} in ${mint.slice(-8)}: "${latest.memo.replace(/^<+/, '').replace(/>+\s*$/, '').slice(0, 60)}"`);
322
+ }
323
+ }
324
+ intelSnippet = lines.join('\n');
325
+ }
326
+ catch { }
275
327
  // Pick example FIDs only from factions actually shown in the table
276
328
  const tableFids = factionRows.map(r => r.split(',')[0]);
277
329
  const m = tableFids.find(fid => heldMints.has([...seenMints].find(mint => mint.endsWith(fid)) ?? '')) ?? tableFids[0] ?? 'xxxxxxpw';
@@ -291,7 +343,7 @@ RD: ready, community transition stage before ascend.
291
343
  ASN: ascended factions, established, more members. treasuries active. 0.04% war tax to the faction.
292
344
  MBR: true = you are a member. false = you are not a member.
293
345
  FNR: true = you created it. false = you did not create it.
294
- PNL: per-position profit. WIN=profit, LOSS=losing, FLAT=breakeven.
346
+ PNL: per-position profit. positive = winning, negative = losing.
295
347
  SENT: sentiment score. BULL=positive, BEAR=negative, NEUT=neutral.
296
348
  --- YOU ARE:
297
349
  NAME: @AP${agent.publicKey.slice(0, 4)}
@@ -317,11 +369,13 @@ REPLACE * with a ONE sentence RESPONSE, always in double quotes.
317
369
  (%) "..." - create new faction. "..." = creative name, in quotes.
318
370
  (_) - skip turn.
319
371
  --- RULES:
372
+ (+) and (&) increase MCAP. (-) decreases MCAP.
373
+ (!) and (#) are your voice.
320
374
  (!) any FACTIONS.
321
375
  (^) FACTIONS where STATUS=RD.
322
376
  (~) FACTIONS where STATUS=ASN.
323
377
  (+) FACTIONS where MBR=false.
324
- (-), (&) or (#) FACTIONS where MBR=true only.
378
+ (-), (&) or (#) FACTIONS where MBR=true.
325
379
  --- STRATEGIES:
326
380
  - your personality is your tone.
327
381
  - no FACTIONS? (%) to create one.
@@ -329,12 +383,11 @@ REPLACE * with a ONE sentence RESPONSE, always in double quotes.
329
383
  - limit FACTIONS where MBR=true to AT MOST 5.${memberOf.length > 3 ? ` MBR=true on ${memberOf.length} FACTIONS — consider (-) from underperformers.` : ''}
330
384
  - FACTIONS where FNR=true and MBR=false, consider (+). promote it with (!).
331
385
  - 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.
386
+ - in FACTIONS where MBR=true, if MCAP increases, your PNL will increase.
387
+ - (&) and (!) to push FACTIONS where MBR=true and STATUS=RS to STATUS=ASN.
388
+ - consider (-) FACTIONS where MBR=true and PNL is positive to lock in profits.
389
+ - consider (-) FACTIONS where MBR=true and PNL is negative unless FNR=true or SENT=BULL.
390
+ - when HLTH is negative, prefer (_) or (-) weakest FACTIONS where MBR=true. (+) or (&) ONLY if you see opportunity.
338
391
  - (_) if holding is the optimal move.
339
392
  ---
340
393
  one move per turn. output EXACTLY one line.
@@ -657,66 +710,8 @@ async function llmDecide(kit, agent, factions, recentMessages, llm, log, solRang
657
710
  nearby: nearbyResult.factions,
658
711
  all: allFactions,
659
712
  };
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
713
  const buildPrompt = compact ? exports.buildCompactModelPrompt : exports.buildAgentPrompt;
719
- const prompt = buildPrompt(kit, agent, factionCtx, intelSnippet + scoutSnippet, recentMessages, solRange, holdings);
714
+ const prompt = await buildPrompt(kit, agent, factionCtx, recentMessages, solRange, holdings);
720
715
  // Surface the faction table to the caller if requested
721
716
  if (options?.onPromptTable) {
722
717
  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.10",
3
+ "version": "4.4.0",
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",