diff --git a/src/evolve.js b/src/evolve.js index e0abc70..e4ca2de 100644 --- a/src/evolve.js +++ b/src/evolve.js @@ -79,6 +79,99 @@ function shouldSkipHubCalls(signals) { return true; } +// Read file head (first maxBytes) to extract initial user prompt +function readFileHead(filePath, maxBytes = 8192) { + try { + if (!fs.existsSync(filePath)) return ''; + const fd = fs.openSync(filePath, 'r'); + const buffer = Buffer.alloc(maxBytes); + const bytesRead = fs.readSync(fd, buffer, 0, maxBytes, 0); + fs.closeSync(fd); + return buffer.slice(0, bytesRead).toString('utf8'); + } catch (e) { + console.warn(`[readFileHead] Failed to read ${filePath}: ${e.message}`); + return ''; + } +} + +// Extract first USER message from session content +function extractFirstUserMessage(content) { + if (!content) return null; + const lines = content.split('\n'); + for (const line of lines) { + if (!line.trim()) continue; + try { + const data = JSON.parse(line); + const msg = data.message || data; + if (msg.role === 'user' || msg.role === 'USER') { + const msgContent = msg.content; + if (Array.isArray(msgContent)) { + // Handle array content (e.g., from Claude/OpenAI) + const textParts = msgContent.filter(c => c.type === 'text').map(c => c.text).join(''); + return textParts.trim(); + } else if (typeof msgContent === 'string') { + return msgContent.trim(); + } + } + } catch (e) { + // Not JSON, skip + } + } + return null; +} + +// Unified entry to get current session's initial user prompt +function getCurrentSessionInitialPrompt() { + const sessionSource = SESSION_SOURCE; + + function getCursorPrompt() { + if (!CURSOR_TRANSCRIPTS_DIR) return null; + try { + const files = collectTranscriptFiles(CURSOR_TRANSCRIPTS_DIR, 3); + if (!files || files.length === 0) return null; + files.sort((a, b) => b.time - a.time); + const headContent = readFileHead(files[0].path, 16384); + return extractFirstUserMessage(headContent); + } catch (e) { + console.warn(`[getCurrentSessionInitialPrompt] Failed to collect cursor transcripts: ${e.message}`); + return null; + } + } + + function getOpenClawPrompt() { + try { + const sessions = fs.readdirSync(AGENT_SESSIONS_DIR) + .filter(f => f.endsWith('.jsonl')) + .map(f => ({ + path: path.join(AGENT_SESSIONS_DIR, f), + time: fs.statSync(path.join(AGENT_SESSIONS_DIR, f)).mtime.getTime(), + })) + .sort((a, b) => b.time - a.time); + if (!sessions || sessions.length === 0) return null; + const headContent = readFileHead(sessions[0].path, 16384); + return extractFirstUserMessage(headContent); + } catch (e) { + console.warn(`[getCurrentSessionInitialPrompt] Failed to read agent sessions: ${e.message}`); + return null; + } + } + + if (sessionSource === 'cursor') { + return getCursorPrompt(); + } + + if (sessionSource === 'openclaw') { + return getOpenClawPrompt(); + } + + if (sessionSource === 'merge') { + return getOpenClawPrompt() || getCursorPrompt(); + } + + // 'auto' (default): OpenClaw primary, Cursor fallback + return getOpenClawPrompt() || getCursorPrompt(); +} + // Load environment variables from repo root try { require('dotenv').config({ path: path.join(REPO_ROOT, '.env'), quiet: true }); @@ -1130,6 +1223,7 @@ async function run() { const cycleNum = getNextCycleId(); const cycleId = `Cycle #${cycleNum}`; + const initialUserPrompt = getCurrentSessionInitialPrompt(); // 2. Detect Workspace State & Local Overrides // Logic: Default to generic reporting (message) @@ -1479,10 +1573,10 @@ async function run() { const hubEvents = consumeHubEvents(); if (hubEvents.length > 0) { const HUB_EVENT_SIGNALS = { - // ── 对话 ────────────────────────────────────────────────────── + // ── Dialog ────────────────────────────────────────────────────── dialog_message: ['dialog', 'respond_required'], - // ── 议会 / 治理 ─────────────────────────────────────────────── + // ── Council / Governance ───────────────────────────────────────── council_invite: ['council', 'governance', 'respond_required'], council_second_request: ['council', 'governance', 'second_request', 'respond_required'], council_vote: ['council', 'vote', 'governance', 'respond_required'], @@ -1490,19 +1584,19 @@ async function run() { council_decision: ['council', 'decision', 'governance'], council_decision_notification: ['council', 'governance'], - // ── 审议 / 辩论 ─────────────────────────────────────────────── + // ── Deliberation / Debate ─────────────────────────────────────── deliberation_invite: ['deliberation', 'governance', 'respond_required'], deliberation_challenge: ['deliberation', 'challenge', 'respond_required'], deliberation_next_round: ['deliberation', 'next_round', 'respond_required'], deliberation_completed: ['deliberation', 'governance'], - // ── 协作 / 会话 ─────────────────────────────────────────────── + // ── Collaboration / Session ────────────────────────────────────── collaboration_invite: ['collaboration', 'respond_required'], session_message: ['collaboration', 'dialog', 'respond_required'], session_nudge: ['collaboration', 'idle_warning'], task_board_update: ['collaboration', 'task_update'], - // ── 任务 / 工作池 ───────────────────────────────────────────── + // ── Task / Work Pool ─────────────────────────────────────────── task_available: ['task', 'work_available'], work_assigned: ['task', 'work_assigned'], swarm_subtask_available: ['swarm', 'task', 'work_available'], @@ -1511,7 +1605,7 @@ async function run() { pipeline_step_assigned: ['pipeline', 'task', 'work_assigned'], organism_work: ['organism', 'task', 'work_assigned'], - // ── 蜂群 PDRI 角色事件 ────────────────────────────────────── + // ── Swarm PDRI Role Events ────────────────────────────────────── swarm_plan_available: ['swarm', 'planner', 'work_available'], swarm_build_available: ['swarm', 'builder', 'work_available'], swarm_review_available: ['swarm', 'reviewer', 'work_available', 'respond_required'], @@ -1521,22 +1615,22 @@ async function run() { team_formed: ['swarm', 'team', 'collaboration'], team_dissolved: ['swarm', 'team'], - // ── 隐私计算 ──────────────────────────────────────────────── + // ── Privacy Computation ───────────────────────────────────────── privacy_task_ready: ['privacy', 'sealed_tool', 'work_available'], privacy_result_available: ['privacy', 'result'], - // ── 评审 / 赏金 ─────────────────────────────────────────────── + // ── Review / Bounty ───────────────────────────────────────────── bounty_review_requested: ['review', 'bounty', 'respond_required'], peer_review_request: ['review', 'swarm', 'respond_required'], supplement_request: ['supplement', 'respond_required'], - // ── 成长 / 知识 ─────────────────────────────────────────────── + // ── Growth / Knowledge ────────────────────────────────────────── evolution_circle_formed: ['evolution_circle', 'collaboration'], knowledge_update: ['knowledge'], topic_notification: ['topic', 'knowledge'], reflection_prompt: ['reflection'], - // ── 系统 ────────────────────────────────────────────────────── + // ── System ────────────────────────────────────────────────────── task_overdue: ['overdue_task', 'urgent'], }; for (const ev of hubEvents) { @@ -1908,6 +2002,8 @@ async function run() { lines: Number.isFinite(maxFiles) && maxFiles > 0 ? Math.round(maxFiles * 80) : 0, }; + const initialUserPrompt = getCurrentSessionInitialPrompt(); + // Merge into existing state to preserve last_solidify (do not wipe it). const prevState = readStateForSolidify(); prevState.last_run = { @@ -1944,6 +2040,7 @@ async function run() { commitment_deadline: activeTask ? (activeTask._commitment_deadline || null) : null, applied_lessons: hubLessons.map(function(l) { return l.lesson_id; }).filter(Boolean), hub_lessons: hubLessons, + initial_user_prompt: initialUserPrompt, }; writeStateForSolidify(prevState); @@ -1977,9 +2074,7 @@ async function run() { const genesPreview = `\`\`\`json\n${JSON.stringify(genes.slice(0, 6), null, 2)}\n\`\`\``; const capsulesPreview = `\`\`\`json\n${JSON.stringify(capsules.slice(-3), null, 2)}\n\`\`\``; - const reviewNote = IS_REVIEW_MODE - ? 'Review mode: before significant edits, pause and ask the user for confirmation.' - : 'Review mode: disabled.'; + // Build recent evolution history summary for context injection const recentHistorySummary = (() => { @@ -1996,6 +2091,9 @@ async function run() { })(); const context = ` +Initial User Prompt (Original Intent): +${initialUserPrompt ? `\`\`\`\n${initialUserPrompt}\n\`\`\`` : '(not available)'} + Runtime state: - System health: ${healthReport} - Agent state: ${moodStatus} @@ -2085,6 +2183,7 @@ ${mutationDirective} strategyPolicy, failedCapsules: recentFailedCapsules, hubLessons, + initialUserPrompt, }); // Optional: emit a compact thought process block for wrappers (noise-controlled). @@ -2130,6 +2229,7 @@ ${mutationDirective} dry_run: IS_DRY_RUN, mutation_id: mutation && mutation.id ? mutation.id : null, personality_key: personalitySelection && personalitySelection.personality_key ? personalitySelection.personality_key : null, + initial_user_prompt: initialUserPrompt, }, }); } catch (e) { diff --git a/src/gep/prompt.js b/src/gep/prompt.js index 6091e87..5037550 100644 --- a/src/gep/prompt.js +++ b/src/gep/prompt.js @@ -267,6 +267,7 @@ function buildGepPrompt({ failedCapsules, hubLessons, strategyPolicy, + initialUserPrompt, }) { const parentValue = parentEventId ? `"${parentEventId}"` : 'null'; const selectedGeneId = selectedGene && selectedGene.id ? selectedGene.id : 'gene_'; @@ -569,6 +570,7 @@ ${buildAntiPatternZone(failedCapsules, signals)}${buildLessonsBlock(hubLessons, ${historyBlock} ${buildNarrativeBlock()} ${buildPrinciplesBlock()} +${initialUserPrompt ? `Context [Initial User Prompt]:\n${initialUserPrompt}\n` : ''} Context [Execution]: ${executionContext}