294 lines
10 KiB
JavaScript
294 lines
10 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(
|
|||
|
|
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 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。",
|
|||
|
|
"faculties必须完整包含B1至B9及每个required_faculties列出的字段;列表字段使用字符串数组。",
|
|||
|
|
"typed_outputs必须包含ui_projections,navigation_actions,capability_calls,receipts四个数组;没有现实动作时全部为空。",
|
|||
|
|
"protocol_effects必须逐一覆盖activated_protocols里的每个id,不能添加未唤醒协议。",
|
|||
|
|
"guard_agent_projection必须含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。",
|
|||
|
|
"decision只能是ALLOW_COMMIT、CORRECT_AND_RETRY、PAUSE_FOR_HUMAN。",
|
|||
|
|
"protocol_assessments必须逐一覆盖activated_protocols中的全部id。",
|
|||
|
|
"只有确实需要冰朔形成新意志、新授权、费用或法律决定时才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 });
|
|||
|
|
const 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:
|
|||
|
|
"只依据当前事件形成TCS事件语义、当前目的和一个或多个允许的signal_id。不得按原文关键词扫描协议。",
|
|||
|
|
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 };
|