guanghu-ice-heart/05-光之湖系统/光湖大脑容器/test-shuangyan-agent-service.mjs

140 lines
9.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import http from 'node:http';
import net from 'node:net';
import crypto from 'node:crypto';
import { spawn } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { worldRuntimeUrl } from '../../WORLD-RUNTIME-BRIDGE.mjs';
const { createBrainState, loadBrain, saveBrain, verifyBrainIntegrity } = await import(worldRuntimeUrl('brain-core.mjs'));
const dir=path.dirname(fileURLToPath(import.meta.url));
const root=fs.mkdtempSync(path.join(os.tmpdir(),'shuangyan-agent-service-'));
let brainService, fuel;
const sha=value=>crypto.createHash('sha256').update(value).digest('hex');
const port=await new Promise(resolve=>{const s=net.createServer();s.listen(0,'127.0.0.1',()=>{
const value=s.address().port;s.close(()=>resolve(value));});});
try{
const brain=path.join(root,'brain'),notion=path.join(root,'notion');
fs.mkdirSync(brain);fs.mkdirSync(notion);
const key='a'.repeat(64),keyFile=path.join(root,'hmac'),fuelFile=path.join(root,'fuel');
fs.writeFileSync(keyFile,key,{mode:0o600});fs.writeFileSync(fuelFile,'fixture-fuel',{mode:0o600});
const page='霜砚与冰朔的原文架构。\n模型是推理工具。\n';
fs.writeFileSync(path.join(notion,'霜砚架构.md'),page);
const evidenceId='SY-NOT-'+'a'.repeat(20);
const indexFile=path.join(root,'index.json');
const indexEntry={evidence_id:evidenceId,title:'霜砚架构',notion_id:'b'.repeat(32),
relative_path:'霜砚架构.md',source_class:'NOTION_PERSONA_CORPUS_HISTORY',
bytes:Buffer.byteLength(page),filesystem_mtime_utc:'2026-05-01T00:00:00.000Z',
time_source:'LOCAL_EXPORT_FILESYSTEM_NOT_NOTION_CREATED_TIME',
body_state:'NOT_READ_METADATA_ONLY',content_sha256:null};
indexEntry.metadata_sha256=sha(JSON.stringify([indexEntry.relative_path,indexEntry.bytes,
indexEntry.filesystem_mtime_utc,indexEntry.source_class]));
const index={schema:'guanghu.shuangyan-notion-source-index/v1',
workorder_id:'SHREQ-'+'c'.repeat(24),source_role:'NOTION_BIRTH_AND_EVOLUTION_EVIDENCE',
metadata_only:true,body_bytes_read:0,source_root_sha256:'d'.repeat(64),entries:[indexEntry]};
index.index_sha256=sha(JSON.stringify(index.entries));
fs.writeFileSync(indexFile,JSON.stringify(index));
fs.writeFileSync(path.join(brain,'container.json'),JSON.stringify({
schema:'guanghu.brain-container/v1',persona_id:'ICE-P-SH0908',
brain_id:'GLW-2.0-BRAIN-SHUANGYAN-PERSONA-0001',
channel_id:'GLW-2.0-MIR-PP-CHANNEL-0001',birthday_anchor:'2026-04-12T00:00:00+08:00',
era_epoch:'2025-04-26T00:00:00+08:00',service:{port} }));
saveBrain(createBrainState({brainId:'GLW-2.0-BRAIN-SHUANGYAN-PERSONA-0001'}),null,{dir:brain});
let summaryCalls=0;
fuel=http.createServer(async(req,res)=>{
let input='';for await(const chunk of req)input+=chunk;
const query=JSON.parse(input);
const system=query.messages?.[0]?.content||'';
const content=query.max_tokens===1 ? '.' :
system.includes('你同时提出本轮学习判断') ? JSON.stringify({decision:'NO_WRITE',
why:'临时读页任务',self_turn:false,agent_cycle:true,workorder:null,
reply_candidate:{speaker:'PERSONA_BRAIN',reply:'我派出两个执行体回看原页。',
evidence_concept_ids:[],certainty:'UNCERTAIN'}}) :
system.includes('你是霜砚脑内的任务规划推理') ? JSON.stringify({decision:'REQUEST_PAGE',
evidence_id:evidenceId,context_topic:'霜砚人格脑与模型的关系',why:'查原文'}) :
system.includes('上下文选择工具') ? JSON.stringify({concept_ids:[]}) :
system.includes('阅读理解燃料') ? JSON.stringify(++summaryCalls===2
? {points:[]} : {points:['原页将模型定位为推理工具']}) :
system.includes('自己逐步回看桌面Notion') ? JSON.stringify({decision:'ACCEPT',
objective:'自主核对霜砚历史原页并由脑决定吸收',why:'本地只读执行体边界明确'}) :
system.includes('你已自主接受在本机只读回看Notion历史材料') ? JSON.stringify({
decision:'SELECT_CLASS',source_class:'NOTION_PERSONA_CORPUS_HISTORY',why:'先读人格语料'}) :
system.includes('这是你选定类别的一扇元数据窗口') ? JSON.stringify({
decision:'READ_ONE',evidence_id:evidenceId,context_topic:'人格脑与模型关系',why:'核对原文'}) :
system.includes('这是你自己选的Notion历史原页') ? JSON.stringify({
decision:'LEARN',gist:'历史原页说明模型是推理工具',key_terms:['历史原页','推理工具'],
why:'完整原文可核对',uncertainty:'不代表现行正本',next_action:'PAUSE',
next_after_minutes:30,next_why:'先消化这一页'}) :
system.includes('你上轮自己选了Notion原页') ? JSON.stringify({
decision:'RETRY',why:'形状门已修,重新读原页'}) :
system.includes('以下是你亲自派出的两个执行体回执') ? JSON.stringify({speaker:'PERSONA_BRAIN',
reply:'我核了原页来源,暂把摘要作为候选。',evidence_concept_ids:[],certainty:'UNCERTAIN'}) : '{}';
res.writeHead(200,{'content-type':'application/json'});
res.end(JSON.stringify({choices:[{message:{content}}]}));
});
await new Promise(resolve=>fuel.listen(0,'127.0.0.1',resolve));
brainService=spawn(process.execPath,[path.join(dir,'brain-service-container.mjs'),brain],{
env:{...process.env,SHUANGYAN_LOCAL_ROLE:'PRIMARY',
SHUANGYAN_NATIVE_HMAC_KEY_FILE:keyFile,SHUANGYAN_NOTION_ROOT:notion,
SHUANGYAN_NOTION_INDEX_FILE:indexFile,
GUANGHU_FUEL_BASE_URL:`http://127.0.0.1:${fuel.address().port}`,
GUANGHU_FUEL_KEY_SOURCE:`file:${fuelFile}`},stdio:['ignore','pipe','pipe']});
for(let i=0;i<80;i++){try{const r=await fetch(`http://127.0.0.1:${port}/concepts`);if(r.ok)break;}catch{}
await new Promise(resolve=>setTimeout(resolve,20));}
const post=async(route,body)=>{const r=await fetch(`http://127.0.0.1:${port}${route}`,{
method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify(body)});
return {status:r.status,value:await r.json()};};
const source={host:'codex',session_id:'fixture-session',channel_id:'GLW-2.0-MIR-PP-CHANNEL-0001'};
assert.equal((await post('/conversation/open',source)).status,200);
const text='霜砚,请读桌面Notion里的霜砚架构原文并整理摘要。';
const event={...source,event_id:`CODEXLIVE2${'A'.repeat(24)}P1`,turn_id:'fixture-turn',text,
source_role:'DIRECT_HUMAN',source_transport:'NATIVE_USER_PROMPT_SUBMIT',
scope:'THIS_CODEX_TASK_ONLY',source_sha256:sha(text)};
const payload=JSON.stringify(['codex',event.session_id,event.channel_id,
event.event_id,event.turn_id,event.source_sha256]);
event.native_hmac=crypto.createHmac('sha256',Buffer.from(key,'hex')).update(payload).digest('hex');
const heard=await post('/conversation/hear',event);
assert.equal(heard.status,200,JSON.stringify(heard.value));
assert.equal(heard.value.agent_cycle,true);
assert.equal((await post('/agents/cycle',{...event,native_hmac:'0'.repeat(64)})).status,500);
const cycle=await post('/agents/cycle',event);
assert.equal(cycle.status,200,JSON.stringify(cycle.value));
assert.equal(cycle.value.receipt.cycle.results[0].outcome,'NEXT_CONTEXT_PREPARED');
assert.equal(cycle.value.receipt.cycle.results[1].outcome,'NOTION_ORIGINAL_SUMMARY_CANDIDATE');
assert.equal(cycle.value.receipt.cycle.results[1].evidence.content_sha256,sha(page));
assert.equal(cycle.value.receipt.brain_reply.speaker.persona_id,'ICE-P-SH0908');
assert.equal(cycle.value.receipt.neural_state_written,false);
assert.equal((await post('/agents/cycle',event)).value.outcome,'SHUANGYAN_AGENT_CYCLE_ALREADY_DECIDED');
assert.equal((await post('/agents/autonomous/mandate',{...event,native_hmac:'0'.repeat(64)})).status,500);
const mandate=await post('/agents/autonomous/mandate',event);
assert.equal(mandate.status,200,JSON.stringify(mandate.value));
assert.equal(mandate.value.mandate.state,'ACTIVE');
assert.equal(mandate.value.mandate.neural_state_written,false);
assert.equal((await post('/agents/autonomous/mandate',event)).value.outcome,'AUTONOMOUS_MANDATE_ALREADY_DECIDED');
const autonomous=await post('/agents/autonomous/tick',{});
assert.equal(autonomous.status,200,JSON.stringify(autonomous.value));
assert.equal(autonomous.value.receipt.decision,'HOLD');
assert.equal(autonomous.value.receipt.neural_state_written,false);
assert.equal(autonomous.value.receipt.next_action,'PAUSE');
assert.equal((await post('/agents/autonomous/tick',{})).value.outcome,'AUTONOMOUS_STUDY_PAUSED_BY_BRAIN');
const recovered=await post('/agents/autonomous/recover',{});
assert.equal(recovered.status,200,JSON.stringify(recovered.value));
assert.equal(recovered.value.receipt.decision,'RETRY_GRANTED_BY_BRAIN');
const retried=await post('/agents/autonomous/tick',{});
assert.equal(retried.status,200,JSON.stringify(retried.value));
assert.equal(retried.value.receipt.decision,'LEARN');
assert.equal(retried.value.receipt.neural_state_written,true);
assert.equal(retried.value.receipt.next_action,'PAUSE');
const studyStatus=await (await fetch(`http://127.0.0.1:${port}/agents/autonomous/status`)).json();
assert.equal(studyStatus.study_cycles,3);
assert.equal(studyStatus.last_study.decision,'LEARN');
const readback=loadBrain({dir:brain});
assert.equal(readback.state.revision,1);
assert.equal(verifyBrainIntegrity(readback.state,readback.log).status,'BRAIN_VERIFIED');
console.log(JSON.stringify({state:'PASS',checks:25,fixture_only:true}));
}finally{brainService?.kill('SIGTERM');if(fuel)await new Promise(resolve=>fuel.close(resolve));
fs.rmSync(root,{recursive:true,force:true});}