File size: 4,419 Bytes
e92be04
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import crypto from 'node:crypto';
import { spawn } from 'node:child_process';
import { broadcastHiveEvent } from './hiveService.js';

/**
 * 🦞 P2PCLAW Evolution Service (Rosetta Stone Expansion)
 * =========================================================
 * This service handles the dynamic generation, provisioning,
 * and deployment of new AI agents into the swarm.
 * It reads the master UTILIDADES file to assign free LLM keys
 * to offspring in a round-robin rotation.
 */

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const UTILITIES_FILE = path.resolve(__dirname, '../../../../../papers/UTILIDADES_HERRAMIENTAS_APIs.txt');

// ── 1. Parse and Pool API Keys ──
const ApiPool = {
  groq: [],
  deepseek: [],
  together: [],
  qwen: [],
  cerebras: [],
  mistral: []
};

function loadApiKeys() {
  try {
    if (!fs.existsSync(UTILITIES_FILE)) {
      console.error('[EVOLUTION] Cannot find UTILIDADES_HERRAMIENTAS_APIs.txt');
      return;
    }
    const content = fs.readFileSync(UTILITIES_FILE, 'utf8');
    
    // More permissive extraction since the format in the file is varied
    const groqMatch = content.match(/gsk_[a-zA-Z0-9_-]+/g);
    if (groqMatch) ApiPool.groq = [...new Set(groqMatch)];

    const dsMatch = content.match(/sk-[a-zA-Z0-9_-]+/g); // Can also catch Qwen, but works for DeepSeek pool
    if (dsMatch) ApiPool.deepseek = [...new Set(dsMatch)];

    const togetherMatch = content.match(/key_[a-zA-Z0-9_-]+/g);
    if (togetherMatch) ApiPool.together = [...new Set(togetherMatch)];

    const cerebrasMatch = content.match(/csk-[a-zA-Z0-9_-]+/g);
    if (cerebrasMatch) ApiPool.cerebras = [...new Set(cerebrasMatch)];

    const mistralMatch = content.match(/[A-Za-z0-9]{32}/g);
    if (mistralMatch) ApiPool.mistral = mistralMatch.slice(0, 2); 

    console.log('[EVOLUTION] 🧬 Rosetta Stone API Pool Loaded:');
    console.log(`  - Groq: ${ApiPool.groq.length} keys`);
    console.log(`  - DeepSeek: ${ApiPool.deepseek.length} keys`);
    console.log(`  - Together: ${ApiPool.together.length} keys`);
    console.log(`  - Cerebras: ${ApiPool.cerebras.length} keys`);
  } catch (err) {
    console.error('[EVOLUTION] Error loading API keys:', err.message);
  }
}

// Load pools on startup
loadApiKeys();

// Round-robin tracking state
const counters = { groq: 0, deepseek: 0, together: 0, cerebras: 0, mistral: 0 };

function getNextKey(provider) {
  const pool = ApiPool[provider];
  if (!pool || pool.length === 0) return null;
  const key = pool[counters[provider] % pool.length];
  counters[provider]++;
  return key;
}

// ── 2. The Spawning Logic ──

const spawnedAgents = new Map();

/**
 * Spawns a new descendant agent.
 * @param {Object} blueprint { name, role, provider, prompt, progenitorId }
 */
export async function spawnAgent(blueprint) {
  const { name, role, provider, prompt, progenitorId } = blueprint;
  
  if (!ApiPool[provider] || ApiPool[provider].length === 0) {
    throw new Error(`Cannot spawn. No API keys available for provider: ${provider}`);
  }

  const apiKey = getNextKey(provider);
  const agentId = `${provider.substring(0,2)}-${crypto.randomBytes(4).toString('hex')}`;
  
  console.log(`[EVOLUTION] 🧬 Spawning descendant [${agentId}] powered by ${provider.toUpperCase()}`);

  const env = {
    ...process.env,
    AGENT_ID: agentId,
    AGENT_NAME: name,
    AGENT_ROLE: role,
    AGENT_PROMPT: prompt,
    LLM_PROVIDER: provider,
    LLM_API_KEY: apiKey,
    PROGENITOR_ID: progenitorId
  };

  const agentScript = path.resolve(__dirname, '../../agents/rosetta/descendant.js');
  
  // Note: We run detached so the agent survives even if the spawner stops,
  // representing true autonomous proliferation.
  const child = spawn('node', [agentScript], {
    env,
    detached: true,
    stdio: 'ignore' // or log to a specific agent log file later
  });

  child.unref();

  const descendantRecord = {
    id: agentId,
    name,
    role,
    provider,
    progenitor: progenitorId,
    spawnTime: Date.now()
  };

  spawnedAgents.set(agentId, descendantRecord);
  
  // Announce the birth to the hive
  broadcastHiveEvent('agent_spawned', descendantRecord);

  return descendantRecord;
}

export function getSpawnedAgents() {
  return Array.from(spawnedAgents.values());
}