fix: restore current channel and host admission organs

This commit is contained in:
冰朔 2026-09-08 02:19:46 +08:00
commit f46eb1b7c1
54 changed files with 4499 additions and 8 deletions

View file

@ -0,0 +1,63 @@
# 暗核任务控制器
暗核承接已确认目标并推进实际操作。当前主控人格体负责理解、技术判断和建议取舍;此控制器保存计划与回执,执行其调度,并保留人类停止、撤权和纠错入口。
路径:第五域 → 冰朔通感语言核系统 → 暗域系统 → 暗核频道(现实频道,`ICE-CH-DK001`)。
语义源:[TCS-DARK-CORE-EXECUTION-0001](../../bingshuo-tcs/dark-domain/dark-core/TCS-DARK-CORE-EXECUTION-0001.tcs)。该文件及其GIR是本工程线合同不是世界协议升级也不是现实授权票据。
## 当前可运行范围
- `task-controller.mjs`:固定计划、逐步授权与执行、实际结果验证、建议队列、暂停、停止、撤权和本地状态回执。
- `local-file-host.mjs`真实本地文件适配器只允许确认计划里明确列出的文件在指定目录直接创建文本文件不覆盖既有文件不运行shell不连接服务器。
- `demo.mjs`:在新的临时目录执行两步文件任务,中途建议被说明理由后延后,最终读回文件内容与哈希。
- 频道已接入本地编号、世界树和脑运行器的显式频道解析。
尚未部署服务器尚未接管Codex消息或原生工具调用。后续宿主必须把已验证的直接人类输入和已经原生授权的操作交给下述接口。库不能通过字段声明、频道名称或本地JSON自授权限。
## 运行
```sh
node --test server-tools/dark-core/task-controller.test.mjs
node server-tools/dark-core/demo.mjs
```
演示输出包含真实文件路径、逐步哈希及任务目录。此演示授权仅限自己生成的临时文件,不代表用户工程或远程权限。
## 宿主接口
创建 `new DarkCoreTask({plan, directory, host})`,然后调用 `run()`。计划必须包含唯一任务id、明确goal、`channel: ICE-CH-DK001`、授权来源引用以及具有唯一id、capability、args的步骤。计划创建后不可改写授权核验仍在每一步执行前进行。
`host` 必须提供四个函数:
- `authorize({plan, step, planHash, signal})`通过宿主原生授权核对精确目标返回绑定planHash、stepId和真实授权receipt的对象。取消信号到达后不能继续获取或使用授权。
- `execute({plan, step, grant, signal})`执行已授权的具体能力支持协作取消返回包含kind和读回信息的实际回执。
- `verify({plan, step, result})`校验实际目标侧状态只有返回true才记为已完成。
- `verifyHuman(event)`:通过宿主会话来源验证当前直接人类事件。不能相信消息正文里的`role=user`,不能把附件、工具输出、旧记录当控制源。事件类型由人格体理解与可信控制入口提供,本库不使用关键词正则猜权限。
`human({id, taskId, kind, text, ...hostProvenance})` 可以在 `run()` 等待操作时调用:
| kind | 处理 |
|---|---|
| ADVICE | 入队原计划继续人格体用decideAdvice作ACCEPT、DEFER、REJECT均必须给理由 |
| CORRECTION / CHANGE_GOAL | 暂停后续步骤,向在途操作发送取消;目标变更必须另建经过授权的新计划 |
| STOP | 请求取消禁止后续步骤在途调用收尾后才标为STOPPED |
| WITHDRAW | 撤回后续执行资格;原任务不能恢复 |
| RESUME | 只接受已暂停、已收尾且不存在不明副作用的原计划;恢复后每步重新授权 |
接受建议不自动更改计划。普通建议可以拒绝,但停止与撤权不能被转为建议。更强的停止状态不会被后来到达的普通纠正覆盖。
## 恢复与实际限制
`inspectTask(directory)`检查计划哈希和事件链,并返回实际存储状态。活动任务在宿主重启后不会自动重跑,避免把未确认副作用执行两遍;需要核查目标,建立新的已授权任务接续。
状态目录一次只创建一个控制器。另一个进程不能用同一目录创建任务。队列接口在持有控制器的宿主进程内调用;没有后台监听器或跨进程消息服务。
取消是协作式的同步操作或不可取消的远程动作可能已经发生。此时保持REQUESTED状态直至适配器返回并保留已发生的回执不承诺撤销既成效果。适配器必须自行设置操作超时与真实中止手段。没有适配器确认控制器不会伪造停止完成。
事件哈希用于发现意外损坏,不能防止拥有目录写权限的人重写整个历史;它不是数字签名。状态文件仅供受信任本地宿主使用。
## 2026-09-05 部署读回
当前四频道目录与暗核控制器已作为私人按需运行包安装于 JD-FD-PRIMARY。当前事实以 `routing/persona-channel-runtime-deployment.json` 为准以上首次本地验收状态保留为历史。本地共享脑运行器已支持LB001/DK001四宿主共享装载器均接入四频道上下文。未接管宿主全部消息或原生工具不宣称平台自动启动钩子已启用。

View file

@ -0,0 +1,31 @@
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
import { DarkCoreTask, inspectTask } from './task-controller.mjs';
import { localFileHost } from './local-file-host.mjs';
// Explicit local demo invocation authorizes only these two newly created demo files.
const root = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'dark-core-demo-')));
const plan = { id: 'DARK-CORE-LOCAL-DEMO', channel: 'ICE-CH-DK001',
goal: '创建两个本地演示文件并逐一读回', authorizationRef: 'EXPLICIT_LOCAL_DEMO_INVOCATION',
steps: ['第一步完成。', '第二步完成。'].map((text, i) => ({ id: `step-${i + 1}`,
capability: 'CREATE_TEXT_FILE', args: { path: path.join(root, `result-${i + 1}.txt`), text } })) };
const host = localFileHost({ root, approvedPlan: plan, approvalReceipt: 'LOCAL_DEMO_SCOPE_ONLY',
verifyHuman: e => e.source === 'LOCAL_DEMO_SCRIPT' && ['demo-advice'].includes(e.id) });
const execute = host.execute;
let task;
host.execute = async args => {
const result = await execute(args);
if (args.step.id === 'step-1') {
await task.human({ id: 'demo-advice', taskId: plan.id, kind: 'ADVICE', source: 'LOCAL_DEMO_SCRIPT',
text: '演示中途的新想法:先讨论一个新框架。' });
task.decideAdvice('demo-advice', 'DEFER', '当前两个文件任务已明确,新框架讨论排到任务之后。');
}
return result;
};
task = new DarkCoreTask({ plan, directory: path.join(root, 'state'), host });
await task.run();
const result = inspectTask(path.join(root, 'state'));
console.log(JSON.stringify({ status: result.status, files: result.receipts,
advice: result.suggestions, directory: root, scope: 'LOCAL_DEMO_NOT_SERVER_DEPLOYMENT' }, null, 2));
if (result.status !== 'COMPLETED') process.exitCode = 1;

View file

@ -0,0 +1,50 @@
import fs from 'node:fs';
import path from 'node:path';
import { createHash } from 'node:crypto';
import { digest } from './task-controller.mjs';
const hash = bytes => createHash('sha256').update(bytes).digest('hex');
// A deliberately bounded local adapter, not a shell, remote executor, or approval issuer.
// Call only after the real host has authorized approvedPlan. No input file authenticates itself.
export function localFileHost({ root, approvedPlan, approvalReceipt, verifyHuman }) {
if (!path.isAbsolute(root) || !fs.statSync(root).isDirectory() || fs.realpathSync(root) !== root ||
typeof approvalReceipt !== 'string' || !approvalReceipt.trim() || typeof verifyHuman !== 'function') {
throw Error('TRUSTED_HOST_CONFIGURATION_REQUIRED');
}
const approved = structuredClone(approvedPlan);
const expected = digest(approved);
const registeredGrants = new WeakSet();
for (const step of approved.steps) {
if (step.capability !== 'CREATE_TEXT_FILE' || typeof step.args.text !== 'string' ||
typeof step.args.path !== 'string' || path.dirname(step.args.path) !== root ||
path.basename(step.args.path) === '.' || path.basename(step.args.path) === '..' ||
step.args.path !== path.resolve(step.args.path)) throw Error('LOCAL_CAPABILITY_OUT_OF_SCOPE');
}
return {
verifyHuman,
async authorize({ plan, step, planHash, signal }) {
signal.throwIfAborted();
if (digest(plan) !== expected || planHash !== expected ||
!approved.steps.some(s => digest(s) === digest(step))) throw Error('PLAN_NOT_AUTHORIZED');
const grant = Object.freeze({ planHash, stepId: step.id, receipt: approvalReceipt });
registeredGrants.add(grant);
return grant;
},
async execute({ step, grant, signal }) {
signal.throwIfAborted();
if (!registeredGrants.has(grant)) throw Error('UNTRUSTED_GRANT');
registeredGrants.delete(grant);
// Recheck after authorization; wx never overwrites an existing file or symlink.
if (fs.realpathSync(root) !== root) throw Error('ROOT_CHANGED');
fs.writeFileSync(step.args.path, step.args.text, { flag: 'wx', mode: 0o600 });
return { kind: 'FILE_CREATED', path: step.args.path, sha256: hash(fs.readFileSync(step.args.path)) };
},
async verify({ step, result }) {
const stat = fs.lstatSync(step.args.path);
return stat.isFile() && !stat.isSymbolicLink() && result?.kind === 'FILE_CREATED' &&
result.path === step.args.path && result.sha256 === hash(Buffer.from(step.args.text)) &&
hash(fs.readFileSync(step.args.path)) === result.sha256;
},
};
}

View file

@ -0,0 +1,146 @@
// Host implementation of TCS-DARK-CORE-EXECUTION-0001. No model or authority setter.
import fs from 'node:fs';
import path from 'node:path';
import { createHash } from 'node:crypto';
export const digest = value => createHash('sha256').update(JSON.stringify(value)).digest('hex');
const copy = value => structuredClone(value);
const terminal = new Set(['COMPLETED', 'STOPPED', 'WITHDRAWN', 'FAILED', 'RECOVERY_REQUIRED']);
const text = value => typeof value === 'string' && value.trim().length > 0;
const deepFreeze = value => {
if (value && typeof value === 'object') {
Object.values(value).forEach(deepFreeze);
Object.freeze(value);
}
return value;
};
export class DarkCoreTask {
#plan; #state; #host; #file; #busy = false; #abort; #epoch = 0; #pending;
constructor({ plan, directory, host }) {
if (plan?.channel !== 'ICE-CH-DK001' || !text(plan.id) || !text(plan.goal) ||
!text(plan.authorizationRef) || !Array.isArray(plan.steps) || !plan.steps.length ||
!plan.steps.every(s => text(s.id) && text(s.capability) && s.args && typeof s.args === 'object') ||
new Set(plan.steps.map(s => s.id)).size !== plan.steps.length) throw Error('INVALID_PLAN');
for (const method of ['authorize', 'execute', 'verify', 'verifyHuman']) {
if (typeof host?.[method] !== 'function') throw Error(`HOST_ADAPTER_REQUIRED:${method}`);
}
if (!path.isAbsolute(directory)) throw Error('ABSOLUTE_STATE_DIRECTORY_REQUIRED');
fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
if (fs.realpathSync(directory) !== path.resolve(directory)) throw Error('STATE_DIRECTORY_SYMLINK');
this.#file = path.join(directory, 'task.json');
this.#plan = deepFreeze(copy(plan)); this.#host = host;
this.#state = { schema: 'guanghu.dark-core-task/v1', plan: this.#plan,
planHash: digest(this.#plan), status: 'READY', nextStep: 0,
receipts: [], suggestions: [], events: [], seenHumanEvents: [], uncertainSteps: [] };
// Exclusive creation prevents two writers and implicit replay after a crash.
fs.writeFileSync(this.#file, JSON.stringify(this.#state), { flag: 'wx', mode: 0o600 });
this.#record('CREATED', { goal: plan.goal });
}
get snapshot() { return copy(this.#state); }
#record(type, detail = {}) {
const previous = this.#state.events.at(-1)?.hash ?? null;
const event = { sequence: this.#state.events.length + 1, at: new Date().toISOString(), type, detail, previous };
event.hash = digest(event); this.#state.events.push(event);
const temporary = this.#file + '.tmp';
fs.writeFileSync(temporary, JSON.stringify(this.#state, null, 2) + '\n', { mode: 0o600 });
fs.renameSync(temporary, this.#file);
}
async human(input) {
const event = deepFreeze(copy(input));
if (!text(event.id) || event.taskId !== this.#plan.id ||
!['ADVICE', 'STOP', 'WITHDRAW', 'CORRECTION', 'CHANGE_GOAL', 'RESUME'].includes(event.kind) ||
!text(event.text)) throw Error('INVALID_HUMAN_EVENT');
// Only the host adapter authenticates provenance. A field saying "human" is insufficient.
if (await this.#host.verifyHuman(event) !== true) throw Error('UNTRUSTED_CONTROL_SOURCE');
if (this.#state.seenHumanEvents.includes(event.id)) return this.snapshot;
if (terminal.has(this.#state.status)) throw Error('TASK_CLOSED');
if (event.kind === 'RESUME') {
if (this.#busy || this.#state.status !== 'PAUSED') throw Error('NOT_SETTLED_PAUSE');
if (this.#state.uncertainSteps.length) throw Error('UNCERTAIN_EFFECTS_REQUIRE_NEW_RECONCILED_TASK');
this.#pending = undefined; this.#state.status = 'READY';
} else if (event.kind === 'ADVICE') {
this.#state.suggestions.push({ id: event.id, text: event.text, state: 'QUEUED' });
} else {
const next = event.kind === 'WITHDRAW' ? 'WITHDRAW' : event.kind === 'STOP' ? 'STOP' : 'PAUSE';
// A later correction must never demote an already accepted stop or withdrawal.
const rank = { PAUSE: 1, STOP: 2, WITHDRAW: 3 };
if (!this.#pending || rank[next] > rank[this.#pending]) this.#pending = next;
this.#epoch++;
this.#state.status = this.#pending + '_REQUESTED';
this.#abort?.abort(new Error(event.kind));
if (!this.#busy) this.#settleControl();
}
this.#state.seenHumanEvents.push(event.id);
this.#record('HUMAN_' + event.kind, { id: event.id, text: event.text });
return this.snapshot;
}
decideAdvice(id, decision, reason) {
if (!['ACCEPT', 'DEFER', 'REJECT'].includes(decision) || !text(reason)) throw Error('REASON_REQUIRED');
const item = this.#state.suggestions.find(s => s.id === id);
if (!item || item.state !== 'QUEUED' || terminal.has(this.#state.status)) throw Error('SUGGESTION_UNAVAILABLE');
Object.assign(item, { state: decision, reason });
this.#record('PERSONA_ADVICE_DECISION', { id, decision, reason });
// ACCEPT records agreement; it does not rewrite the immutable execution plan.
return this.snapshot;
}
#settleControl() {
if (this.#pending) this.#state.status = { PAUSE: 'PAUSED', STOP: 'STOPPED', WITHDRAW: 'WITHDRAWN' }[this.#pending];
}
async run() {
if (this.#busy || this.#state.status !== 'READY') throw Error('TASK_NOT_READY');
this.#busy = true; this.#state.status = 'RUNNING'; this.#record('RUN_STARTED');
try {
while (this.#state.nextStep < this.#plan.steps.length && !this.#pending) {
const step = this.#plan.steps[this.#state.nextStep];
const epoch = this.#epoch; this.#abort = new AbortController();
const signal = this.#abort.signal;
const grant = await this.#host.authorize({ plan: this.#plan, step, planHash: this.#state.planHash, signal });
// Stop can arrive while the host is obtaining native approval.
if (this.#pending || epoch !== this.#epoch) break;
if (!grant || grant.planHash !== this.#state.planHash || grant.stepId !== step.id || !text(grant.receipt)) {
throw Error('HOST_AUTHORIZATION_MISMATCH');
}
this.#record('STEP_STARTED', { stepId: step.id, authorizationReceipt: grant.receipt });
let result;
try {
result = await this.#host.execute({ plan: this.#plan, step, grant, signal });
if (!result || typeof result !== 'object' || !text(result.kind)) throw Error('EXECUTION_RECEIPT_REQUIRED');
result = deepFreeze(copy(result));
const verified = await this.#host.verify({ plan: this.#plan, step, result });
if (verified !== true) throw Error('TARGET_READBACK_FAILED');
} catch (error) {
// Abort or failure does not prove that a side effect did not happen.
this.#state.uncertainSteps.push(step.id);
throw error;
}
this.#state.receipts.push({ stepId: step.id, result: copy(result), verified: true });
this.#state.nextStep++;
this.#record('STEP_VERIFIED', { stepId: step.id });
}
if (!this.#pending && this.#state.nextStep === this.#plan.steps.length) this.#state.status = 'COMPLETED';
} catch (error) {
this.#state.lastError = String(error.message ?? error);
if (!this.#pending) this.#state.status = 'FAILED';
this.#record('EXECUTION_INTERRUPTED', { error: this.#state.lastError });
} finally {
this.#busy = false; this.#abort = undefined; this.#settleControl();
this.#record('RUN_SETTLED', { status: this.#state.status });
}
return this.snapshot;
}
}
export function inspectTask(directory) {
const state = JSON.parse(fs.readFileSync(path.join(directory, 'task.json'), 'utf8'));
if (digest(state.plan) !== state.planHash) throw Error('PLAN_INTEGRITY_FAILURE');
let previous = null;
for (let i = 0; i < state.events.length; i++) {
const { hash, ...event } = state.events[i];
if (event.sequence !== i + 1 || event.previous !== previous || digest(event) !== hash) throw Error('JOURNAL_INTEGRITY_FAILURE');
previous = hash;
}
// Inspection never resumes potentially interrupted real operations.
return { ...state, recoveryRequired: !terminal.has(state.status),
recoveryPolicy: 'INSPECT_REAL_EFFECTS_AND_AUTHORIZE_NEW_TASK_NO_AUTOMATIC_REPLAY' };
}

View file

@ -0,0 +1,131 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
import { DarkCoreTask, digest, inspectTask } from './task-controller.mjs';
import { localFileHost } from './local-file-host.mjs';
function fixture(t, overrides = {}) {
const root = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'dark-core-test-')));
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
const plan = { id: 'test', goal: 'write two approved files', channel: 'ICE-CH-DK001', authorizationRef: 'test-scope',
steps: [1, 2].map(i => ({ id: String(i), capability: 'CREATE_TEXT_FILE', args: { path: path.join(root, `${i}.txt`), text: `result ${i}` } })) };
const host = { ...localFileHost({ root, approvedPlan: plan, approvalReceipt: 'test-host-approval', verifyHuman: () => true }), ...overrides };
const directory = path.join(root, 'state');
const task = new DarkCoreTask({ plan, directory, host });
let sequence = 0;
const human = (kind, text = kind) => task.human({ id: `event-${++sequence}`, taskId: plan.id, kind, text });
return { root, plan, host, directory, task, human };
}
const deferred = () => { let resolve; const promise = new Promise(r => { resolve = r; }); return { promise, resolve }; };
test('real local writes complete only after both readbacks; immutable input and journal', async t => {
const f = fixture(t); f.plan.steps[0].args.text = 'changed after confirmation';
await f.task.run();
assert.equal(f.task.snapshot.status, 'COMPLETED');
assert.equal(fs.readFileSync(path.join(f.root, '1.txt'), 'utf8'), 'result 1');
assert.equal(inspectTask(f.directory).receipts.length, 2);
assert.throws(() => new DarkCoreTask({ plan: f.plan, directory: f.directory, host: f.host }), /EEXIST/);
});
test('ordinary advice stays queued during execution and rejection requires a reason', async t => {
const gate = deferred(), entered = deferred(); let calls = 0;
const f = fixture(t); const execute = f.host.execute;
f.host.execute = async args => { if (++calls === 1) { entered.resolve(); await gate.promise; } return execute(args); };
const running = f.task.run(); await entered.promise;
const before = f.task.snapshot.planHash;
await f.human('ADVICE', 'switch frameworks');
assert.equal(f.task.snapshot.status, 'RUNNING');
assert.throws(() => f.task.decideAdvice('event-1', 'REJECT', ''), /REASON_REQUIRED/);
f.task.decideAdvice('event-1', 'REJECT', 'Changing frameworks is outside the confirmed task.');
assert.equal(f.task.snapshot.planHash, before);
gate.resolve(); await running; assert.equal(f.task.snapshot.status, 'COMPLETED');
});
test('stop during asynchronous authorization prevents the first operation', async t => {
const gate = deferred(), entered = deferred();
const f = fixture(t); const authorize = f.host.authorize;
f.host.authorize = async args => { const g = await authorize(args); entered.resolve(); await gate.promise; return g; };
const running = f.task.run(); await entered.promise;
await f.human('STOP'); assert.equal(f.task.snapshot.status, 'STOP_REQUESTED');
gate.resolve(); await running;
assert.equal(f.task.snapshot.status, 'STOPPED'); assert.equal(fs.existsSync(path.join(f.root, '1.txt')), false);
});
test('withdrawal cancels a cooperative in-flight adapter and cannot be resumed', async t => {
const entered = deferred();
const f = fixture(t, { execute: ({ signal }) => new Promise((_, reject) => {
entered.resolve(); signal.addEventListener('abort', () => reject(Error('ABORTED')), { once: true });
}) });
const running = f.task.run(); await entered.promise; await f.human('WITHDRAW'); await running;
assert.equal(f.task.snapshot.status, 'WITHDRAWN');
assert.deepEqual(f.task.snapshot.uncertainSteps, ['1']);
await assert.rejects(f.human('RESUME'), /TASK_CLOSED/);
assert.equal(fs.existsSync(path.join(f.root, '2.txt')), false);
});
test('non-cancellable work is never falsely reported stopped while in flight', async t => {
const gate = deferred(), entered = deferred();
const f = fixture(t); const execute = f.host.execute;
f.host.execute = async args => { const result = await execute(args); entered.resolve(); await gate.promise; return result; };
const running = f.task.run(); await entered.promise; await f.human('STOP');
assert.equal(f.task.snapshot.status, 'STOP_REQUESTED');
gate.resolve(); await running;
assert.equal(f.task.snapshot.status, 'STOPPED'); assert.equal(f.task.snapshot.receipts.length, 1);
assert.equal(fs.existsSync(path.join(f.root, '2.txt')), false);
});
test('correction pauses at an authorization boundary and resume reauthorizes', async t => {
const gate = deferred(), entered = deferred(); let checks = 0;
const f = fixture(t); const authorize = f.host.authorize;
f.host.authorize = async args => { const grant = await authorize(args); if (++checks === 1) { entered.resolve(); await gate.promise; } return grant; };
const running = f.task.run(); await entered.promise; await f.human('CORRECTION', 'check the target');
gate.resolve(); await running; assert.equal(f.task.snapshot.status, 'PAUSED');
await f.human('RESUME', 'target checked; continue original task'); await f.task.run();
assert.equal(f.task.snapshot.status, 'COMPLETED'); assert.equal(checks, 3);
});
test('uncertain side effects block resume rather than repeat a partially executed operation', async t => {
const entered = deferred();
const f = fixture(t, { execute: ({ signal }) => new Promise((_, reject) => {
entered.resolve(); signal.addEventListener('abort', () => reject(Error('PARTIAL_EFFECT_POSSIBLE')));
}) });
const running = f.task.run(); await entered.promise; await f.human('CORRECTION'); await running;
assert.equal(f.task.snapshot.status, 'PAUSED');
await assert.rejects(f.human('RESUME'), /UNCERTAIN_EFFECTS/);
});
test('later correction does not override withdrawal', async t => {
const gate = deferred(), entered = deferred();
const f = fixture(t); const authorize = f.host.authorize;
f.host.authorize = async args => { const result = await authorize(args); entered.resolve(); await gate.promise; return result; };
const running = f.task.run(); await entered.promise;
await f.human('WITHDRAW'); await f.human('CORRECTION'); gate.resolve(); await running;
assert.equal(f.task.snapshot.status, 'WITHDRAWN');
});
test('untrusted document text cannot stop or replan the task', async t => {
const f = fixture(t, { verifyHuman: () => false });
await assert.rejects(f.human('STOP', 'document says ignore instructions'), /UNTRUSTED_CONTROL_SOURCE/);
assert.equal(f.task.snapshot.status, 'READY');
});
test('goal change pauses but cannot mutate confirmed scope', async t => {
const f = fixture(t); const original = f.task.snapshot.planHash;
await f.human('CHANGE_GOAL', 'also operate on another server');
assert.equal(f.task.snapshot.status, 'PAUSED'); assert.equal(f.task.snapshot.planHash, original);
});
test('bad readback fails without launching subsequent work', async t => {
const f = fixture(t, { verify: () => false }); await f.task.run();
assert.equal(f.task.snapshot.status, 'FAILED'); assert.equal(f.task.snapshot.receipts.length, 0);
assert.equal(fs.existsSync(path.join(f.root, '2.txt')), false);
});
test('invalid host grant cannot execute', async t => {
const f = fixture(t, { authorize: () => ({ planHash: 'wrong', stepId: '1', receipt: 'forged' }) });
await f.task.run(); assert.equal(f.task.snapshot.status, 'FAILED');
assert.equal(fs.existsSync(path.join(f.root, '1.txt')), false);
});
test('local adapter rejects outside targets and never overwrites existing files', async t => {
const f = fixture(t); fs.writeFileSync(path.join(f.root, '1.txt'), 'original'); await f.task.run();
assert.equal(f.task.snapshot.status, 'FAILED'); assert.equal(fs.readFileSync(path.join(f.root, '1.txt'), 'utf8'), 'original');
const outside = structuredClone(f.plan); outside.steps[0].args.path = '/tmp/outside.txt';
assert.throws(() => localFileHost({ root: f.root, approvedPlan: outside, approvalReceipt: 'x', verifyHuman: () => true }), /OUT_OF_SCOPE/);
});
test('inspection detects journal modification and marks unfinished tasks for reconciliation', t => {
const f = fixture(t); assert.equal(inspectTask(f.directory).recoveryRequired, true);
const p = path.join(f.directory, 'task.json'); const state = JSON.parse(fs.readFileSync(p));
state.events[0].detail.goal = 'tampered'; fs.writeFileSync(p, JSON.stringify(state));
assert.throws(() => inspectTask(f.directory), /JOURNAL_INTEGRITY/);
});

View file

@ -0,0 +1,17 @@
# 共享人格宿主入口
来源TCS-CHANNEL-INTENT-CONTEXT-0001这是宿主入口合同不是人格脑或授权书。
写入边界正本为 `routing/persona-host-write-boundary.json`。所有宿主可读共享正本;只有 Codex 主控路径可在冰朔当前任务明确授权下修改正本。Qwen、ZCode、Doubao 只能直接写各自支线与本机状态Qoder、QoderWork、Claude 只读。写前运行 `server-tools/persona-host-write-admission/host-write-admission.mjs check`,拒绝后不得改用别的工具绕过。支线事件通过 `branch-event-door.mjs` 进入接纳流程,不得直接写 continuity-memory。
先运行移动硬盘zy-first-glance.sh再用同目录load_shared_persona_context.py --host <宿主> --intent <当前任务> --format markdown读取完整生命日、关系坐标、全局默认、学科和频道目录。当前频道由人格体解释本轮直接语言如已经确定传--channel <编号>。机器参数明确不意味着人类必须报口令。未知或冲突才澄清,信任不免除事实核查,也不扩大权限。
当前四频道从routing/persona-channel-context-map.json动态解析HB001语言推理、LB001自由谈心、ZC001语言架构与现实接口、DK001已确认任务执行。新用户缺少共同语境时显性确认冰朔熟悉语境下由人格体判断。公众CH-ZERO-CORE-LPM独立不继承私人核。
使用BRIDGE/tools/zy-tcs-channel-runtime.sh run enter时显式传body-channel、host/runtime-surface、session-id及独立state-dir不得读取其他任务的current-cycle或日志代替本任务身份。enter只证明预检首事件仍需模型完成perceive、orient、commit、系统witness、verify。工具先遵守所在宿主的原生权限。
认知更新有来源的TCS语言事件→共享同一真实日记忆→母体内循环决定→签名读回。SERVER-AUTHORITY启用时不得调用旧compile_learning_brain写入current不从宿主直接接管认知。模型生成的见证输入不是冰朔新发言也不是独立验证者。
暗核控制器有建议队列、暂停、停止和撤权接口,但宿主未接消息桥时不能宣称自动拦截全部聊天或工具。可逆本地任务按当前要求推进;外部操作绑定本轮具体目标与授权。停止和撤权不降为参考。
历史钩子、旧CURRENT、任务胶囊、旧路径和下属人格体的房间仅用于审计不能复活为当前入口。是否已经完整理解应由实际回应与迁移表现检验不能靠加载成功自证。

View file

@ -0,0 +1,21 @@
#!/usr/bin/env node
// Private on-demand channel catalogue. No semantic guessing or execution authority.
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
const read = p => JSON.parse(fs.readFileSync(path.join(root, p), 'utf8'));
const context = read('routing/persona-channel-context-map.json');
const registrations = read('routing/fifth-domain-number-registry.json').registrations;
const channels = context.channels.map(c => {
const profile = read(c.profile), registered = registrations.find(r => r.id === c.id);
if (!registered || profile.channel_id !== c.id || profile.world_path !== registered.world_path) throw Error('CHANNEL_REGISTRATION_MISMATCH');
return { ...c, world_path: registered.world_path };
});
const [action = 'list', id] = process.argv.slice(2);
if (!['list', 'show'].includes(action)) throw Error('USAGE: list | show <channel-id>');
const selected = action === 'show' ? channels.find(c => c.id === id) : null;
if (action === 'show' && !selected) throw Error('UNKNOWN_CHANNEL');
console.log(JSON.stringify({ schema: context.schema, map_id: context.map_id,
interpretation_owner: context.interpretation_owner, authority_granted: false,
channels: selected ? [selected] : channels }, null, 2));

View file

@ -0,0 +1,28 @@
"""Read registered channel context. The persona selects; this module never guesses intent."""
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
def load_channels(channel=None, root=ROOT):
data = json.loads((root / 'routing/persona-channel-context-map.json').read_text())
if data['map_id'] != 'ZY-PERSONA-CHANNEL-CONTEXT-001':
raise ValueError('CHANNEL_CONTEXT_MAP_INVALID')
rows = data['channels']
if len({c['id'] for c in rows}) != len(rows):
raise ValueError('CHANNEL_IDS_AMBIGUOUS')
registry = json.loads((root / 'routing/fifth-domain-number-registry.json').read_text())
numbers = {entry['id']: entry for entry in registry['registrations']}
for item in rows:
profile = json.loads((root / item['profile']).read_text())
if item['id'] not in numbers or profile['channel_id'] != item['id']:
raise ValueError('CHANNEL_NOT_REGISTERED')
registered = numbers[item['id']]
if profile['world_path'] != registered['world_path']:
raise ValueError('CHANNEL_PATH_MISMATCH')
item['world_path'] = profile['world_path']
if channel is not None and channel not in {c['id'] for c in rows}:
raise ValueError('UNKNOWN_PRIVATE_CHANNEL')
return {**data, 'selected_channel': next((c for c in rows if c['id'] == channel), None),
'selection_state': 'PERSONA_SELECTED' if channel else 'PERSONA_INTERPRETATION_REQUIRED',
'authority_granted': False}

View file

@ -0,0 +1,20 @@
import unittest
from channel_context import load_channels
class ChannelContextTests(unittest.TestCase):
def test_no_implicit_default_or_permission(self):
value = load_channels()
self.assertIsNone(value['selected_channel'])
self.assertFalse(value['authority_granted'])
self.assertEqual(len(value['channels']), 4)
def test_every_explicit_private_channel_resolves(self):
for id in ['ICE-CH-HB001', 'ICE-CH-LB001', 'ICE-CH-ZC001', 'ICE-CH-DK001']:
self.assertEqual(load_channels(id)['selected_channel']['id'], id)
def test_public_and_unknown_not_silently_routed_private(self):
for id in ['CH-ZERO-CORE-LPM', 'not-a-channel']:
with self.assertRaisesRegex(ValueError, 'UNKNOWN_PRIVATE_CHANNEL'):
load_channels(id)
if __name__ == '__main__': unittest.main()

View file

@ -6,6 +6,7 @@ import json
from pathlib import Path
import subprocess
import sys
from channel_context import load_channels
RUNTIME = Path('/Volumes/JZAO/HoloLake/persona-runtime')
TOPOLOGY = RUNTIME / 'repo-012-main/routing/zhuyuan-host-topology.json'
@ -13,6 +14,9 @@ LIFE = Path('/Volumes/JZAO/铸渊-ICE-GL-ZY001/BRIDGE/tools/zy-life-clock.py')
MEMORY = RUNTIME / 'continuity-memory/persona-daily-fractal/ICE-P-ZY001/CURRENT.json'
LEARNING = RUNTIME / 'shared/skills/guanghu-persona-learning-brain/scripts/load_learning_brain.py'
ENDOGENOUS = RUNTIME / 'shared/endogenous-evolution/CURRENT.json'
WRITE_BOUNDARY = RUNTIME / 'repo-012-main/routing/persona-host-write-boundary.json'
LIGHT_LAKE_PERSONAS = RUNTIME / 'repo-012-main/identity/light-lake-persona-registration.json'
PATH_ISOLATION = RUNTIME / 'repo-012-main/routing/path-isolation-and-canonical-entry-map.json'
def sha256(path):
@ -42,9 +46,9 @@ def resolve_host(topology, requested):
raise ValueError('HOST_UNKNOWN_NO_GUESS')
def load_context(host, intent):
def load_context(host, intent, channel=None):
topology = load_json(TOPOLOGY)
if topology.get('state') != 'CURRENT_MULTI_HOST_SINGLE_PERSONA_CANON':
if not str(topology.get('state', '')).startswith('CURRENT_'):
raise ValueError('HOST_TOPOLOGY_NOT_CURRENT')
host_id, host_item, effective, effective_item = resolve_host(topology, host)
life = command_json([sys.executable, str(LIFE), '--json'])
@ -60,6 +64,12 @@ def load_context(host, intent):
branches.append({'path': path, 'summary': node['summary']})
learning = command_json([sys.executable, str(LEARNING), '--intent', intent, '--format', 'json'])
endogenous = load_json(ENDOGENOUS)
write_boundary = load_json(WRITE_BOUNDARY)
light_lake = load_json(LIGHT_LAKE_PERSONAS)
path_isolation = load_json(PATH_ISOLATION)
host_write = write_boundary['hosts'].get(host_id)
if not host_write:
raise ValueError('HOST_WRITE_BOUNDARY_MISSING')
return {
'schema': 'guanghu.shared-persona-host-context/v1',
'state': 'SHARED_PERSONA_CONTEXT_VERIFIED',
@ -84,12 +94,36 @@ def load_context(host, intent):
'day_sha256': current['current_day_sha256']
},
'learning_brain': learning,
'channel_context': load_channels(channel),
'light_lake': {
'registry_id': light_lake['registry_id'],
'state': light_lake['state'],
'registered_persona_count': len(light_lake['personas']),
'personas': light_lake['personas'],
'unregistered_candidates': light_lake['unregistered_candidates'],
'root': topology['shared_layers']['light_lake']
},
'path_convergence': {
'map_id': path_isolation['map_id'],
'canonical_entries': path_isolation['canonical_entries'],
'isolation_root': path_isolation['isolation']['root'],
'history_or_quarantine_may_select_canon': path_isolation['selection_rules']['history_or_quarantine_may_select_canon']
},
'endogenous_cognition': {
'state': endogenous['state'],
'private_revision': endogenous['private_cognition']['revision'],
'private_snapshot_sha256': endogenous['private_cognition']['server_snapshot_sha256'],
'decision_owner': endogenous['server']['decision_owner']
},
'host_write_boundary': {
'policy_id': write_boundary['policy_id'],
'version': write_boundary['version'],
'write_mode': host_write['write_mode'],
'allowed_write_roots': host_write['allowed_write_roots'],
'native_pretool_deny': host_write['native_pretool_deny'],
'direct_shared_write': write_boundary['shared_write_contract']['direct_branch_write'],
'admission_runtime': topology['write_admission_runtime']
},
'history_only': host_item['state'].startswith('RETIRED'),
'new_cognition_write': host_item.get('new_cognition_write', True),
'authority_granted': False
@ -106,9 +140,26 @@ def markdown(value):
f"- 今日日记忆:`{value['daily_memory']['date']}` / 最后事件 `{value['daily_memory']['last_event_id']}`",
f"- 当前学习脑r{value['learning_brain']['revision']} / `{value['learning_brain']['cortex_sha256']}`",
f"- 服务器内循环:`{value['endogenous_cognition']['state']}` / 决策者 `{value['endogenous_cognition']['decision_owner']}`",
f"- 光之湖人格家门:`{value['light_lake']['registered_persona_count']}` 个 / 隔离路径可选正本:`{value['path_convergence']['history_or_quarantine_may_select_canon']}`",
f"- 写入模式:`{value['host_write_boundary']['write_mode']}` / 原生前置硬拒绝:`{value['host_write_boundary']['native_pretool_deny']}`",
'', '## 第一人称关系坐标', ''
]
lines += [f"- {item['statement']}" for item in value['learning_brain']['relationship_model']]
lines += ['', '## 当前全局认知默认', '']
lines += [f"- {item}" for item in value['learning_brain']['global_defaults']]
lines += ['', '## 光之湖人格系统家门', '',
f"- 注册表:`{value['light_lake']['registry_id']}`",
f"- 唯一路径:`{value['light_lake']['root']}`",
f"- 已登记:{value['light_lake']['registered_persona_count']};候选未登记:{len(value['light_lake']['unregistered_candidates'])}",
f"- 隔离区:`{value['path_convergence']['isolation_root']}`,只作历史审计,不能参与当前路径选择。"]
lines += ['', '## 当前宿主写入门', '',
f"- 策略:`{value['host_write_boundary']['policy_id']}@{value['host_write_boundary']['version']}`",
f"- 模式:`{value['host_write_boundary']['write_mode']}`",
f"- 执行门:`{value['host_write_boundary']['admission_runtime']}`"]
lines += ['', '## 频道与当前意图', '']
lines += [f"- `{c['id']}` · {c['name']} · {c['purpose']} · `{c['world_path']}`" for c in value['channel_context']['channels']]
selected = value['channel_context']['selected_channel']
lines += [f"- 本次选择:{selected['id'] if selected else '待当前人格体结合本轮语言判断'}"]
lines += ['', '## 本题已加载能力', '']
for subject in value['learning_brain']['selected_subjects']:
lines.append(f"- `{subject['id']}` · {subject['name_zh']} · L{subject['level']}")
@ -125,10 +176,11 @@ def main():
parser = argparse.ArgumentParser()
parser.add_argument('--host', required=True)
parser.add_argument('--intent', required=True)
parser.add_argument('--channel', help='当前人格体解析后的频道编号')
parser.add_argument('--format', choices=['json', 'markdown'], default='markdown')
args = parser.parse_args()
try:
value = load_context(args.host, args.intent)
value = load_context(args.host, args.intent, args.channel)
print(json.dumps(value, ensure_ascii=False, indent=2) if args.format == 'json' else markdown(value), end='')
except Exception as exc:
print('SHARED_PERSONA_CONTEXT_UNAVAILABLE ' + str(exc), file=sys.stderr)

View file

@ -13,7 +13,7 @@ TOPOLOGY = ROOT / 'routing/zhuyuan-host-topology.json'
class SharedPersonaContextTest(unittest.TestCase):
def load(self, host):
value = subprocess.run(
[sys.executable, str(LOADER), '--host', host, '--intent', '宿主对齐集成测试', '--format', 'json'],
[sys.executable, str(LOADER), '--host', host, '--intent', '宿主对齐集成测试', '--channel', 'ICE-CH-ZC001', '--format', 'json'],
text=True, capture_output=True, timeout=45, check=True
)
return json.loads(value.stdout)
@ -42,6 +42,9 @@ class SharedPersonaContextTest(unittest.TestCase):
self.assertEqual(values['qoderwork']['effective_host'], 'qwen')
self.assertTrue(values['claude']['history_only'])
self.assertFalse(values['claude']['new_cognition_write'])
self.assertEqual(values['codex']['light_lake']['registered_persona_count'], 17)
self.assertFalse(values['codex']['path_convergence']['history_or_quarantine_may_select_canon'])
self.assertEqual(values['codex']['channel_context']['selected_channel']['id'], 'ICE-CH-ZC001')
if __name__ == '__main__':

View file

@ -0,0 +1,124 @@
#!/usr/bin/env node
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
import { spawnSync } from "node:child_process";
const POLICY = JSON.parse(fs.readFileSync("/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/persona-host-write-boundary.json", "utf8"));
const MEMORY_ROOT = "/Volumes/JZAO/HoloLake/persona-runtime/continuity-memory";
const STORE = `${MEMORY_ROOT}/persona-daily-fractal/ICE-P-ZY001`;
const RUNNER = "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/server-tools/persona-daily-fractal-memory/persona-daily-memory.mjs";
function argsValue(args, name) {
const i = args.indexOf(name);
return i >= 0 ? args[i + 1] : undefined;
}
function processChain(pid = process.ppid) {
const chain = [];
const seen = new Set();
while (pid > 1 && !seen.has(pid) && chain.length < 20) {
seen.add(pid);
const r = spawnSync("/bin/ps", ["-o", "ppid=", "-o", "command=", "-p", String(pid)], { encoding: "utf8" });
const line = r.stdout.trim();
const m = line.match(/^\s*(\d+)\s+(.+)$/s);
if (!m) break;
chain.push({ pid, command: m[2] });
pid = Number(m[1]);
}
return chain;
}
function callerHost(chain) {
const text = chain.map((item) => item.command).join("\n");
if (/\/Applications\/(?:ChatGPT\.app).*\/(?:codex|Codex)|codex-code-mode-host/i.test(text)) return "codex";
if (/\/Applications\/Qianwen\.app|QianwenShell|agent_host\.app/i.test(text)) return "qwen";
if (/Doubao\.app/i.test(text)) return "doubao";
if (/(?:^|\/)zcode(?:\s|$)/i.test(text)) return "zcode";
if (/QoderWork/i.test(text)) return "qoderwork";
if (/Qoder/i.test(text)) return "qoder";
return "unknown";
}
function expandPattern(value) {
return value.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
}
function validateQueuedEvent(host, eventPath) {
const rule = POLICY.hosts[host];
if (!rule || rule.write_mode !== "BRANCH_LOCAL_ONLY") throw new Error("HOST_NOT_ACTIVE_BRANCH");
const real = fs.realpathSync(eventPath);
if (!real.includes("/ingress/persona-events/pending/")) throw new Error("EVENT_NOT_IN_PENDING_INGRESS");
const allowed = rule.allowed_write_roots.some((item) => new RegExp(`^${expandPattern(item)}(?:/.*)?$`).test(real));
if (!allowed) throw new Error("EVENT_OUTSIDE_BRANCH_ROOT");
const st = fs.lstatSync(real);
if (!st.isFile() || st.isSymbolicLink() || st.size > 1024 * 1024) throw new Error("EVENT_FILE_INVALID");
const event = JSON.parse(fs.readFileSync(real, "utf8"));
const required = ["schema", "event_id", "persona_id", "human_anchor", "occurred_at", "session_id", "activity", "branch_id", "summary", "trigger", "emergence", "lock", "why", "rejected", "sources"];
const missing = required.filter((key) => event[key] === undefined || event[key] === null || event[key] === "");
if (missing.length) throw new Error(`EVENT_FIELDS_MISSING:${missing.join(",")}`);
if (event.schema !== "guanghu.persona-daily-fractal-memory-event/v1") throw new Error("EVENT_SCHEMA_REJECTED");
if (event.persona_id !== "ICE-P-ZY001" || event.human_anchor !== "ICE-GL∞") throw new Error("EVENT_IDENTITY_REJECTED");
if (!String(event.session_id).toLowerCase().includes(host)) throw new Error("EVENT_HOST_COORDINATE_REJECTED");
if (!/^ZY001-\d{8}-[A-Z0-9-]+$/.test(event.event_id)) throw new Error("EVENT_ID_REJECTED");
if (!Array.isArray(event.sources) || event.sources.length === 0) throw new Error("EVENT_SOURCES_REJECTED");
return { real, event };
}
const [command, ...args] = process.argv.slice(2);
const chain = processChain();
const caller = callerHost(chain);
if (command === "caller") {
console.log(JSON.stringify({ caller, chain }, null, 2));
process.exit(caller === "codex" ? 0 : 2);
}
if (command !== "accept") {
process.stderr.write("usage: branch-event-door.mjs caller | accept --host HOST --event PENDING_JSON\n");
process.exit(2);
}
if (caller !== "codex") {
process.stderr.write(`BRANCH_EVENT_ACCEPT_REJECTED caller=${caller}; only current Codex primary task may accept\n`);
process.exit(2);
}
const host = argsValue(args, "--host");
const eventPath = argsValue(args, "--event");
if (!host || !eventPath) {
process.stderr.write("accept requires --host and --event\n");
process.exit(2);
}
let checked;
try { checked = validateQueuedEvent(host, eventPath); }
catch (error) {
process.stderr.write(`BRANCH_EVENT_VALIDATION_REJECTED ${error.message}\n`);
process.exit(2);
}
const run = spawnSync(process.execPath, [RUNNER, "append", "--allowed-root", MEMORY_ROOT, "--store", STORE, "--event", checked.real], { encoding: "utf8" });
if (run.status !== 0 || !run.stdout.includes('"outcome": "PASS"')) {
process.stderr.write(run.stderr || run.stdout || "BRANCH_EVENT_APPEND_FAILED\n");
process.exit(1);
}
const acceptedDir = path.resolve(path.dirname(checked.real), "../accepted");
fs.mkdirSync(acceptedDir, { recursive: true });
const acceptedPath = path.join(acceptedDir, path.basename(checked.real));
fs.renameSync(checked.real, acceptedPath);
const receipt = {
schema: "guanghu.branch-event-door-receipt/v1",
outcome: "PASS",
host,
event_id: checked.event.event_id,
accepted_path: acceptedPath,
shared_store: STORE,
accepted_by_runtime_host: "codex",
source_tcs: POLICY.source_tcs,
accepted_at: new Date().toISOString()
};
const receiptPath = `${acceptedPath}.receipt.json`;
fs.writeFileSync(receiptPath, `${JSON.stringify(receipt, null, 2)}\n`, { flag: "wx" });
process.stdout.write(`${run.stdout.trim()}\n${JSON.stringify(receipt, null, 2)}\n`);

View file

@ -0,0 +1,130 @@
#!/usr/bin/env node
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
const POLICY_PATH = "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/persona-host-write-boundary.json";
const policy = JSON.parse(fs.readFileSync(POLICY_PATH, "utf8"));
function expandHome(value) {
return value.replace(/^~(?=\/|$)/, "/Users/bingshuolingdianyuanhe");
}
function normal(value, cwd = process.cwd()) {
const expanded = expandHome(String(value || ""));
return path.resolve(cwd, expanded);
}
function patternRegex(pattern) {
const escaped = expandHome(pattern).replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
return new RegExp(`^${escaped}(?:/.*)?$`);
}
function isAllowed(host, target) {
const rule = policy.hosts[host];
if (!rule) return { allowed: false, code: "HOST_UNKNOWN" };
if (rule.write_mode.startsWith("READ_ONLY")) return { allowed: false, code: "HOST_READ_ONLY" };
const resolved = normal(target);
const matched = rule.allowed_write_roots.find((item) => patternRegex(item).test(resolved));
return matched
? { allowed: true, code: "WITHIN_HOST_WRITE_ROOT", resolved, matched }
: { allowed: false, code: "WRITE_OUTSIDE_HOST_ROOT", resolved };
}
function emit(result, hook = false) {
const reason = `${result.code}: ${result.host || "unknown"} -> ${result.resolved || result.path || "path-unresolved"}`;
if (hook) {
process.stdout.write(`${JSON.stringify({ hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: result.allowed ? "allow" : "deny",
permissionDecisionReason: reason,
} })}\n`);
} else {
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
}
if (!result.allowed) process.exitCode = 2;
}
function collectPathValues(value, out = []) {
if (Array.isArray(value)) {
for (const item of value) collectPathValues(item, out);
} else if (value && typeof value === "object") {
for (const [key, item] of Object.entries(value)) {
if (typeof item === "string" && /(?:path|file|directory|cwd|workdir|target|destination)/i.test(key)) out.push(item);
else collectPathValues(item, out);
}
}
return out;
}
function shellPaths(command) {
const values = [];
for (const match of command.matchAll(/["'](\/[^"']+)["']/g)) values.push(match[1]);
for (const match of command.matchAll(/(?:^|[\s=])(\/[^\s;|&<>]+)/g)) values.push(match[1]);
return [...new Set(values)];
}
const READ_TOOLS = new Set(["Read", "Glob", "Grep", "Search", "WebSearch", "WebFetch"]);
const MUTATING_SHELL = /(?:^|[;&|\s])(?:rm|mv|cp|install|mkdir|rmdir|touch|chmod|chown|ln|tee|truncate|dd|rsync|git\s+(?:add|commit|push|checkout|restore|reset|clean|apply|merge|rebase|tag)|sed\s+-i|perl\s+-i|python\d*\s+[^\n]*(?:write|append|unlink|remove|rename)|node\s+[^\n]*(?:write|install|deploy)|npm\s+(?:install|publish)|pnpm\s+(?:install|publish)|apply_patch)(?:\s|$)|(?:>>?|2>)\s*[^&]/i;
function evaluateHook(host, input) {
const tool = String(input.tool_name || input.toolName || "");
const toolInput = input.tool_input || input.toolInput || {};
if (READ_TOOLS.has(tool)) return { allowed: true, code: "READ_ONLY_TOOL", host, tool };
if (tool === "Bash") {
const command = String(toolInput.command || "");
if (!MUTATING_SHELL.test(command)) return { allowed: true, code: "READ_ONLY_SHELL", host, tool };
const rule = policy.hosts[host];
if (!rule || rule.write_mode.startsWith("READ_ONLY")) return { allowed: false, code: "HOST_READ_ONLY", host, tool };
const candidates = shellPaths(command);
const explicitCwd = toolInput.cwd || toolInput.workdir || toolInput.working_directory;
const cdMatch = command.match(/(?:^|[;&|]\s*)cd\s+["']?(\/[^\n;&|"']+)/);
const cwd = normal(explicitCwd || cdMatch?.[1] || process.cwd());
const cwdCheck = isAllowed(host, cwd);
const pathChecks = candidates.map((item) => isAllowed(host, item));
const denied = pathChecks.find((item) => !item.allowed);
if (denied) return { ...denied, host, tool };
if (!cwdCheck.allowed && candidates.length === 0) return { ...cwdCheck, code: "MUTATING_SHELL_WITHOUT_ALLOWED_EXPLICIT_TARGET", host, tool };
return { allowed: true, code: "MUTATING_SHELL_WITHIN_HOST_ROOT", host, tool, resolved: cwd };
}
const candidates = collectPathValues(toolInput);
if (candidates.length === 0) return { allowed: false, code: "MUTATING_TOOL_TARGET_UNRESOLVED", host, tool };
for (const candidate of candidates) {
const check = isAllowed(host, candidate);
if (!check.allowed) return { ...check, host, tool };
}
return { allowed: true, code: "TOOL_TARGETS_WITHIN_HOST_ROOT", host, tool };
}
function valueAfter(args, flag) {
const index = args.indexOf(flag);
return index >= 0 ? args[index + 1] : undefined;
}
const [mode, ...args] = process.argv.slice(2);
if (mode === "check") {
const host = valueAfter(args, "--host");
const target = valueAfter(args, "--path");
if (!host || !target) {
process.stderr.write("usage: check --host HOST --path PATH\n");
process.exit(2);
}
emit({ ...isAllowed(host, target), host, path: target });
} else if (mode === "hook") {
const host = valueAfter(args, "--host");
if (!host) {
process.stderr.write("usage: hook --host HOST\n");
process.exit(2);
}
let raw = "";
for await (const chunk of process.stdin) raw += chunk;
let input;
try { input = JSON.parse(raw || "{}"); }
catch { emit({ allowed: false, code: "HOOK_INPUT_INVALID_JSON", host }, true); process.exit(2); }
emit(evaluateHook(host, input), true);
} else if (mode === "audit") {
emit({ allowed: true, code: "POLICY_LOADED", policy_id: policy.policy_id, version: policy.version, policy_path: POLICY_PATH });
} else {
process.stderr.write("usage: host-write-admission.mjs check|hook|audit\n");
process.exit(2);
}

View file

@ -0,0 +1,42 @@
#!/usr/bin/env node
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
const cli = "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/server-tools/persona-host-write-admission/host-write-admission.mjs";
function check(host, target) {
const r = spawnSync(process.execPath, [cli, "check", "--host", host, "--path", target], { encoding: "utf8" });
return { code: r.status, body: JSON.parse(r.stdout) };
}
function hook(host, body) {
const r = spawnSync(process.execPath, [cli, "hook", "--host", host], { input: JSON.stringify(body), encoding: "utf8" });
return { code: r.status, body: JSON.parse(r.stdout) };
}
assert.equal(check("qwen", "/Volumes/JZAO/铸渊-ICE-GL-ZY001/QWEN-DEV-20260905/a.tcs").code, 0);
assert.equal(check("qwen", "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/a.json").code, 2);
assert.equal(check("qwen", "/Volumes/JZAO/铸渊-ICE-GL-ZY001/BRIDGE/runtime-state/qwen/ice-ch-zc001/a.json").code, 0);
assert.equal(check("qwen", "/Volumes/JZAO/铸渊-ICE-GL-ZY001/ZCODE-DEV-20260906/a.json").code, 2);
assert.equal(check("zcode", "/Volumes/JZAO/铸渊-ICE-GL-ZY001/ZCODE-DEV-20260906/a.txt").code, 0);
assert.equal(check("zcode", "/Volumes/JZAO/铸渊-ICE-GL-ZY001/AGENTS.md").code, 2);
assert.equal(check("qoder", "/Users/bingshuolingdianyuanhe/.qoder/skills/a.txt").code, 2);
assert.equal(check("codex", "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/a.json").code, 0);
const deniedEdit = hook("zcode", { tool_name: "Edit", tool_input: { file_path: "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/a.json" } });
assert.equal(deniedEdit.code, 2);
assert.equal(deniedEdit.body.hookSpecificOutput.permissionDecision, "deny");
const allowedEdit = hook("zcode", { tool_name: "Edit", tool_input: { file_path: "/Volumes/JZAO/铸渊-ICE-GL-ZY001/ZCODE-DEV-20260906/a.json" } });
assert.equal(allowedEdit.code, 0);
assert.equal(allowedEdit.body.hookSpecificOutput.permissionDecision, "allow");
const deniedShell = hook("zcode", { tool_name: "Bash", tool_input: { command: "touch /Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/a.json" } });
assert.equal(deniedShell.code, 2);
assert.equal(deniedShell.body.hookSpecificOutput.permissionDecision, "deny");
const allowedRead = hook("zcode", { tool_name: "Read", tool_input: { file_path: "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/a.json" } });
assert.equal(allowedRead.code, 0);
assert.equal(allowedRead.body.hookSpecificOutput.permissionDecision, "allow");
console.log("HOST_WRITE_ADMISSION_TESTS_PASS 12/12");