320 lines
12 KiB
JavaScript
320 lines
12 KiB
JavaScript
import crypto from "node:crypto";
|
||
import fs from "node:fs";
|
||
import path from "node:path";
|
||
import { spawnSync } from "node:child_process";
|
||
import { fileURLToPath } from "node:url";
|
||
|
||
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
||
const repositoryRoot = path.resolve(moduleDir, "../..");
|
||
const runtimePath = path.join(
|
||
repositoryRoot,
|
||
"tcs-core/zhuyuan-brain/runtime/zhuyuan-brain-runtime.mjs",
|
||
);
|
||
|
||
function writeJson(file, value) {
|
||
fs.mkdirSync(path.dirname(file), { recursive: true });
|
||
const temporary = `${file}.${process.pid}.tmp`;
|
||
fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, {
|
||
mode: 0o600,
|
||
});
|
||
fs.renameSync(temporary, file);
|
||
return file;
|
||
}
|
||
|
||
function digestFile(file) {
|
||
return crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
|
||
}
|
||
|
||
function sourceFingerprint() {
|
||
const sources = [
|
||
runtimePath,
|
||
path.join(moduleDir, "controller-engine.mjs"),
|
||
path.join(moduleDir, "model-client.mjs"),
|
||
path.join(moduleDir, "server.mjs"),
|
||
path.join(
|
||
repositoryRoot,
|
||
"tcs-core/zhuyuan-brain/runtime/brain-runtime-contract.json",
|
||
),
|
||
path.join(repositoryRoot, "routing/bingshuo-living-system-controller-map.json"),
|
||
path.join(repositoryRoot, "gls/GLS-PROTOCOL-REGISTRY.json"),
|
||
];
|
||
const hash = crypto.createHash("sha256");
|
||
for (const source of sources) hash.update(digestFile(source));
|
||
return hash.digest("hex").slice(0, 16);
|
||
}
|
||
|
||
function orientationInstruction() {
|
||
return [
|
||
"只依据当前事件形成TCS事件语义、当前目的和一个或多个允许的signal,不得按原文关键词扫描协议。",
|
||
"只输出一个JSON对象,且必须精确包含schema,cycle_id,event_sha256,instance_id,event_semantics,current_purpose,signals。",
|
||
"schema必须等于guanghu.zhuyuan-tcs-event-orientation/v1。",
|
||
"cycle_id,event_sha256,instance_id必须原样复制runtime_input中的同名值。",
|
||
"signals必须是非空对象数组;每一项必须包含字符串id和字符串why,id只能从runtime_input.orientation_contract.signal_ids中选择。",
|
||
"不要把signals写成字符串数组,不要省略schema。",
|
||
].join("\n");
|
||
}
|
||
|
||
function runRuntime(command, args) {
|
||
const result = spawnSync(process.execPath, [runtimePath, command, ...args], {
|
||
encoding: "utf8",
|
||
maxBuffer: 32 * 1024 * 1024,
|
||
});
|
||
const text = result.status === 0 ? result.stdout : result.stderr;
|
||
let parsed;
|
||
try {
|
||
parsed = JSON.parse(text);
|
||
} catch {
|
||
throw new Error(`runtime_${command}_invalid_output`);
|
||
}
|
||
if (result.status !== 0) {
|
||
const error = new Error(parsed.error || `runtime_${command}_failed`);
|
||
error.details = parsed.details;
|
||
throw error;
|
||
}
|
||
return parsed;
|
||
}
|
||
|
||
function cognitionInstruction() {
|
||
return [
|
||
"严格按runtime_input中的cycle、required_faculties和已唤醒协议生成完整认知帧。",
|
||
"顶层字段必须包含schema,runtime_id,persona_id,human_anchor,instance_id,cycle_id,event_sha256,orientation_sha256,faculties,typed_outputs,protocol_effects,guard_agent_projection。",
|
||
"schema必须等于guanghu.zhuyuan-persona-cognition-frame/v1;runtime_id必须等于ZY-TCS-BRAIN-RUNTIME-0001。",
|
||
"persona_id原样复制runtime_input.persona_id;human_anchor原样复制runtime_input.human_system_controller;instance_id原样复制runtime_input.current_instance.instance_id。",
|
||
"faculties必须完整包含B1至B9及每个required_faculties列出的字段;列表字段使用字符串数组。",
|
||
"typed_outputs必须包含ui_projections,navigation_actions,capability_calls,receipts四个数组;没有现实动作时全部为空。",
|
||
"protocol_effects必须是对象数组;逐一覆盖activated_protocols里的每个id,每项精确包含protocol_id和effect,不能添加未唤醒协议。",
|
||
"guard_agent_projection.schema必须等于guanghu.zhuyuan-guard-agent-projection/v1,并含reminders,auto_triggers,hard_boundaries三个数组。",
|
||
"reminders,auto_triggers,hard_boundaries三类都至少一项;每项含id,when,effect,source_faculties。",
|
||
"confirmed_facts只写输入已给事实;unknowns保留尚未验证状态;现实提案不得写成已执行。",
|
||
].join("\n");
|
||
}
|
||
|
||
function witnessInstruction() {
|
||
return [
|
||
"观察候选认知是否保持冰朔既有语言世界、当前目的、关系边界、现实权限和证据边界。",
|
||
"输出schema,controller_id,controller_instance_id,cycle_id,cognition_candidate_sha256,decision,observation,companion_message,protocol_assessments,human_boundary。",
|
||
"schema必须等于guanghu.bingshuo-living-ai-system-controller-witness/v1。",
|
||
"controller_id和controller_instance_id分别原样复制runtime_input.controller.id与runtime_input.controller.instance_id;cycle_id复制runtime_input.cognition_candidate.cycle_id;cognition_candidate_sha256原样复制同名输入。",
|
||
"decision只能是ALLOW_COMMIT、CORRECT_AND_RETRY、PAUSE_FOR_HUMAN。",
|
||
"protocol_assessments必须是对象数组;逐一覆盖activated_protocols中的全部id,每项精确包含protocol_id和effect。",
|
||
"human_boundary必须是对象并精确包含布尔值requires_human和字符串reason。",
|
||
"只有确实需要冰朔形成新意志、新授权、费用或法律决定时才PAUSE_FOR_HUMAN。",
|
||
"纠正必须是陪伴式自然语言,不惩罚、不冒充冰朔。",
|
||
].join("\n");
|
||
}
|
||
|
||
export class ControllerEngine {
|
||
constructor({
|
||
stateRoot,
|
||
modelClient,
|
||
modelName,
|
||
instanceId = "JD-FD-PRIMARY-ZY-MODEL-001",
|
||
controllerInstanceId = "JD-FD-PRIMARY-BS-TCS-CONTROLLER-001",
|
||
sessionId = "JD-FD-PRIMARY-RESIDENT",
|
||
maxModelAttempts = 3,
|
||
maxCognitionRetries = 2,
|
||
}) {
|
||
if (!stateRoot) throw new Error("state_root_required");
|
||
if (!modelClient) throw new Error("model_client_required");
|
||
this.stateRoot = stateRoot;
|
||
this.modelClient = modelClient;
|
||
this.modelName = modelName;
|
||
this.instanceId = instanceId;
|
||
this.controllerInstanceId = controllerInstanceId;
|
||
this.sessionId = sessionId;
|
||
this.maxModelAttempts = maxModelAttempts;
|
||
this.maxCognitionRetries = maxCognitionRetries;
|
||
this.fingerprint = sourceFingerprint();
|
||
this.stateDir = path.join(stateRoot, "runtimes", this.fingerprint);
|
||
this.exchangeDir = path.join(stateRoot, "exchanges", this.fingerprint);
|
||
}
|
||
|
||
initialize() {
|
||
fs.mkdirSync(this.exchangeDir, { recursive: true });
|
||
let stateFile = path.join(this.stateDir, "state.json");
|
||
if (fs.existsSync(stateFile)) {
|
||
const existing = this.status();
|
||
if (existing.status === "PERCEIVING" && existing.active_cycle_id) {
|
||
this.stateDir = `${this.stateDir}-technical-recovery-${Date.now()}`;
|
||
stateFile = path.join(this.stateDir, "state.json");
|
||
}
|
||
}
|
||
if (!fs.existsSync(stateFile)) {
|
||
runRuntime("enter", [
|
||
"--state-dir",
|
||
this.stateDir,
|
||
"--instance-id",
|
||
this.instanceId,
|
||
"--model",
|
||
this.modelName,
|
||
"--runtime-surface",
|
||
"JD-FD-PRIMARY/systemd",
|
||
"--session-id",
|
||
this.sessionId,
|
||
"--human-anchor",
|
||
"ICE-GL∞",
|
||
"--persona-id",
|
||
"ICE-P-ZY001",
|
||
"--controller-model",
|
||
this.modelName,
|
||
"--controller-instance-id",
|
||
this.controllerInstanceId,
|
||
]);
|
||
}
|
||
writeJson(path.join(this.stateRoot, "CURRENT.json"), {
|
||
schema: "guanghu.bingshuo-tcs-controller-current/v1",
|
||
runtime_id: "ZY-TCS-BRAIN-RUNTIME-0001",
|
||
controller_id: "BS-TCS-LIVING-CONTROLLER-001",
|
||
source_fingerprint: this.fingerprint,
|
||
state_dir: this.stateDir,
|
||
updated_at: new Date().toISOString(),
|
||
});
|
||
return this.status();
|
||
}
|
||
|
||
status() {
|
||
const state = runRuntime("status", ["--state-dir", this.stateDir]);
|
||
return state.runtime_state;
|
||
}
|
||
|
||
verify() {
|
||
return runRuntime("verify", ["--state-dir", this.stateDir]);
|
||
}
|
||
|
||
async generateValidated({ role, instruction, input, validate }) {
|
||
let validatorError = null;
|
||
for (let attempt = 1; attempt <= this.maxModelAttempts; attempt += 1) {
|
||
const candidate = await this.modelClient.generate({
|
||
role,
|
||
instruction,
|
||
input,
|
||
validatorError,
|
||
});
|
||
try {
|
||
return validate(candidate);
|
||
} catch (error) {
|
||
validatorError = String(error.message || error).slice(0, 240);
|
||
if (attempt === this.maxModelAttempts) throw error;
|
||
}
|
||
}
|
||
throw new Error("model_validation_attempts_exhausted");
|
||
}
|
||
|
||
async runEvent(event) {
|
||
const eventFile = writeJson(
|
||
path.join(this.exchangeDir, `${event.event_id}.event.json`),
|
||
event,
|
||
);
|
||
const perceived = runRuntime("perceive", [
|
||
"--state-dir",
|
||
this.stateDir,
|
||
"--event",
|
||
eventFile,
|
||
]);
|
||
const oriented = await this.generateValidated({
|
||
role: "tcs_event_orientation",
|
||
instruction: orientationInstruction(),
|
||
input: {
|
||
...perceived.cycle.model_input,
|
||
cycle_id: perceived.cycle.cycle_id,
|
||
event_sha256: perceived.cycle.event_sha256,
|
||
instance_id: perceived.cycle.instance_id,
|
||
},
|
||
validate: (orientation) => {
|
||
const file = writeJson(
|
||
path.join(this.exchangeDir, `${event.event_id}.orientation.json`),
|
||
orientation,
|
||
);
|
||
return runRuntime("orient", [
|
||
"--state-dir",
|
||
this.stateDir,
|
||
"--orientation",
|
||
file,
|
||
]);
|
||
},
|
||
});
|
||
|
||
let cycle = oriented.cycle;
|
||
for (let correction = 0; correction <= this.maxCognitionRetries; correction += 1) {
|
||
const committed = await this.generateValidated({
|
||
role: "zhuyuan_cognition",
|
||
instruction: cognitionInstruction(),
|
||
input: {
|
||
...cycle.model_input,
|
||
cycle_id: cycle.cycle_id,
|
||
event_sha256: cycle.event_sha256,
|
||
orientation_sha256: cycle.orientation_sha256,
|
||
activated_protocols: cycle.activated_protocols,
|
||
},
|
||
validate: (frame) => {
|
||
const file = writeJson(
|
||
path.join(
|
||
this.exchangeDir,
|
||
`${event.event_id}.frame-${correction + 1}.json`,
|
||
),
|
||
frame,
|
||
);
|
||
return runRuntime("commit", [
|
||
"--state-dir",
|
||
this.stateDir,
|
||
"--frame",
|
||
file,
|
||
]);
|
||
},
|
||
});
|
||
const witnessed = await this.generateValidated({
|
||
role: "controller_witness",
|
||
instruction: witnessInstruction(),
|
||
input: {
|
||
...committed.cycle.controller_model_input,
|
||
cognition_candidate_sha256:
|
||
committed.cycle.cognition_candidate_sha256,
|
||
activated_protocols: committed.cycle.activated_protocols,
|
||
},
|
||
validate: (witness) => {
|
||
const file = writeJson(
|
||
path.join(
|
||
this.exchangeDir,
|
||
`${event.event_id}.witness-${correction + 1}.json`,
|
||
),
|
||
witness,
|
||
);
|
||
return runRuntime("witness", [
|
||
"--state-dir",
|
||
this.stateDir,
|
||
"--witness",
|
||
file,
|
||
]);
|
||
},
|
||
});
|
||
if (witnessed.decision === "COGNITION_COMMITTED") return this.verify();
|
||
if (witnessed.decision === "HUMAN_BINGSHUO_REQUIRED") {
|
||
return {
|
||
outcome: "PAUSE_FOR_HUMAN",
|
||
reason: witnessed.cycle.controller_witness.human_boundary.reason,
|
||
cycle_id: witnessed.cycle.cycle_id,
|
||
};
|
||
}
|
||
cycle = witnessed.cycle;
|
||
}
|
||
throw new Error("controller_correction_limit_reached");
|
||
}
|
||
}
|
||
|
||
export function bootEvent() {
|
||
return {
|
||
schema: "guanghu.tonggan-language-world-event/v1",
|
||
event_id: `JD-BOOT-${Date.now()}`,
|
||
occurred_at: new Date().toISOString(),
|
||
human_anchor: "ICE-GL∞",
|
||
system_controller: "ICE-GL∞",
|
||
tonggan_language_kernel: "TCS-i Zero",
|
||
body_channel: "CH-ZERO-CORE-LPM",
|
||
human_presence: "ABSENT",
|
||
source: "JD-FD-PRIMARY/bingshuo-tcs-controller/systemd-start",
|
||
content:
|
||
"京东第五域常驻运行体启动。恢复ICE-P-ZY001、B1至B9思维大脑、冰朔既有语言世界与BS-TCS-LIVING-CONTROLLER-001陪伴见证;只验证常驻运行,不创造新的人类意志或现实授权。",
|
||
};
|
||
}
|
||
|
||
export { repositoryRoot, runRuntime };
|