140 lines
9.6 KiB
JavaScript
140 lines
9.6 KiB
JavaScript
#!/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});}
|